From 57d87aceb12419cd9a31de8439e76a8fd8edb56c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 4 Aug 2026 14:07:37 +0800 Subject: [PATCH 01/23] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20LayoutName?= =?UTF-8?q?=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewComponentBase.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs index c28150be..71c50435 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs @@ -42,6 +42,14 @@ public abstract class DockViewComponentBase : IdComponentBase, IDisposable [JsonIgnore] public RenderFragment? ChildContent { get; set; } + /// + /// 获得/设置 名称 + /// Gets or sets the name + /// + [JsonIgnore] + [Parameter] + public string? LayoutName { get; set; } + [CascadingParameter] private List? Parent { get; set; } From b4cc51a07d84bd4b758a1f35b9981e521615dd2d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 4 Aug 2026 14:32:16 +0800 Subject: [PATCH 02/23] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20setLayout=20?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index e1818586..069bc5e7 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -60,6 +60,14 @@ export function update(id, options) { } } +export function setLayout(id, name) { + const dock = Data.get(id) + if (dock) { + const { dockview } = dock; + dockview.setLayout(name); + } +} + export function reset(id, options) { const dock = Data.get(id) if (dock) { From 719e34955b02db04d7634f77345a0a578edcb2de Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 4 Aug 2026 14:32:33 +0800 Subject: [PATCH 03/23] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20LayoutName?= =?UTF-8?q?=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewComponentBase.cs | 1 - .../Components/DockViewConfig.cs | 6 ++++++ .../Components/DockViewV2.razor.cs | 10 +++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs index 71c50435..2f27e62f 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs @@ -46,7 +46,6 @@ public abstract class DockViewComponentBase : IdComponentBase, IDisposable /// 获得/设置 名称 /// Gets or sets the name /// - [JsonIgnore] [Parameter] public string? LayoutName { get; set; } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs index 5cef1269..de1f4926 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs @@ -129,4 +129,10 @@ class DockViewConfig /// Gets or sets the layout configuration. Default is null /// public string? LayoutConfig { get; set; } + + /// + /// 获得/设置 布局名称,默认为 null + /// Gets or sets the layout name. Default is null + /// + public string? LayoutName { get; set; } } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 9c9c9c50..4b692c39 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -155,6 +155,13 @@ public partial class DockViewV2 [Parameter] public DockViewTheme Theme { get; set; } = DockViewTheme.Light; + /// + /// 获得/设置 布局名称,默认为 null + /// Gets or sets the layout name. Default is null + /// + [Parameter] + public string? LayoutName { get; set; } + /// /// 嵌套 DockView 时生效防止生成冗余的 DOM 结构 /// @@ -284,7 +291,8 @@ private DockViewConfig GetDockViewConfig() SplitterCallback = nameof(SplitterCallbackAsync), SaveConfigCallback = nameof(SaveConfigCallbackAsync), Contents = _components, - LoadTabs = nameof(LoadTabs) + LoadTabs = nameof(LoadTabs), + LayoutName = LayoutName }; } From fb4195b0fc70ec5364deaa1006fded3c242c50ee Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 4 Aug 2026 14:33:06 +0800 Subject: [PATCH 04/23] chore: bump version 10.0.24 --- .../BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj b/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj index a1f17289..e7360a7f 100644 --- a/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj +++ b/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj @@ -1,7 +1,7 @@  - 10.0.23 + 10.0.24 From b25f7199bb829bb581c14bb1e4429fc57dab59e0 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 21:07:26 +0800 Subject: [PATCH 05/23] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=E5=88=87?= =?UTF-8?q?=E6=8D=A2=20layout=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 069bc5e7..8641bcda 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -17,6 +17,9 @@ export async function init(id, invoke, options) { } } const dockview = cerateDockview(el, options); + if (options.layoutName) { + dockview.layoutName = options.layoutName; + } dockview.params.invisiblePanels?.forEach(invisiblePanel => { invoke.invokeMethodAsync(options.panelVisibleChangedCallback, invisiblePanel.params.key, false); }) @@ -56,7 +59,12 @@ export function update(id, options) { const dock = Data.get(id) if (dock) { const { dockview } = dock; - dockview.update(options); + if (dockview.layoutName !== options.layoutName) { + console.log(`DockViewV2: layoutName changed from ${dockview.layoutName} to ${options.layoutName}`); + } + else { + dockview.update(options); + } } } From 2b4317900a4d57903f44e47c2897cb668143c514 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 21:10:07 +0800 Subject: [PATCH 06/23] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=20js=20?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 8641bcda..0a68bfff 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -3,6 +3,19 @@ import { cerateDockview } from '../js/dockview-utils.js' import Data from '../../BootstrapBlazor/modules/data.js' import EventHandler from "../../BootstrapBlazor/modules/event-handler.js" +const getLayoutOptions = options => { + const contents = options?.content; + if (!Array.isArray(contents) || contents.length === 0) { + return options; + } + + const content = options.layoutName + ? contents.find(item => item.layoutName === options.layoutName) + : contents[0]; + + return content ? { ...options, content: [content] } : options; +} + export async function init(id, invoke, options) { await addLink("./_content/BootstrapBlazor.DockView/css/dockview-bb.css") const el = document.getElementById(id); @@ -16,6 +29,7 @@ export async function init(id, invoke, options) { options.theme = `dockview-theme-dark`; } } + options = getLayoutOptions(options); const dockview = cerateDockview(el, options); if (options.layoutName) { dockview.layoutName = options.layoutName; @@ -80,7 +94,7 @@ export function reset(id, options) { const dock = Data.get(id) if (dock) { const { dockview } = dock; - dockview.reset(options); + dockview.reset(getLayoutOptions(options)); } } From da4d60107f8db9ed7500f180bdd78b1c1997dca1 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 21:56:20 +0800 Subject: [PATCH 07/23] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 4b692c39..b2be6b99 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -290,12 +290,27 @@ private DockViewConfig GetDockViewConfig() LockChangedCallback = nameof(LockChangedCallbackAsync), SplitterCallback = nameof(SplitterCallbackAsync), SaveConfigCallback = nameof(SaveConfigCallbackAsync), - Contents = _components, + Contents = GetLayoutContents(), LoadTabs = nameof(LoadTabs), LayoutName = LayoutName }; } + /// + /// 根据 在服务器端过滤出匹配的布局,避免将所有布局序列化到客户端 + /// Filters the matching layout on the server side by to avoid serializing all layouts to the client + /// + private List GetLayoutContents() + { + var content = _components[0]; + if (string.IsNullOrEmpty(LayoutName)) + { + return [content]; + } + + return [_components.Find(item => item.LayoutName == LayoutName) ?? content]; + } + private bool IsEnableLocalStorage => EnableLocalStorage ?? _options.EnableLocalStorage ?? false; private string? LocalStorageKey => IsEnableLocalStorage ? $"{GetPrefixKey()}-{Name}-{GetVersion()}" : null; From e9f8931116af28d5c78d5e72869115fa504585e8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 21:56:29 +0800 Subject: [PATCH 08/23] =?UTF-8?q?Revert=20"refactor:=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20js=20=E9=80=BB=E8=BE=91"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2b4317900a4d57903f44e47c2897cb668143c514. --- .../Components/DockViewV2.razor.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 0a68bfff..8641bcda 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -3,19 +3,6 @@ import { cerateDockview } from '../js/dockview-utils.js' import Data from '../../BootstrapBlazor/modules/data.js' import EventHandler from "../../BootstrapBlazor/modules/event-handler.js" -const getLayoutOptions = options => { - const contents = options?.content; - if (!Array.isArray(contents) || contents.length === 0) { - return options; - } - - const content = options.layoutName - ? contents.find(item => item.layoutName === options.layoutName) - : contents[0]; - - return content ? { ...options, content: [content] } : options; -} - export async function init(id, invoke, options) { await addLink("./_content/BootstrapBlazor.DockView/css/dockview-bb.css") const el = document.getElementById(id); @@ -29,7 +16,6 @@ export async function init(id, invoke, options) { options.theme = `dockview-theme-dark`; } } - options = getLayoutOptions(options); const dockview = cerateDockview(el, options); if (options.layoutName) { dockview.layoutName = options.layoutName; @@ -94,7 +80,7 @@ export function reset(id, options) { const dock = Data.get(id) if (dock) { const { dockview } = dock; - dockview.reset(getLayoutOptions(options)); + dockview.reset(options); } } From ca24607ec38131c191d4cf59da30d427a5898ace Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 7 Aug 2026 09:48:23 +0800 Subject: [PATCH 09/23] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 8641bcda..4a9e5971 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -61,6 +61,7 @@ export function update(id, options) { const { dockview } = dock; if (dockview.layoutName !== options.layoutName) { console.log(`DockViewV2: layoutName changed from ${dockview.layoutName} to ${options.layoutName}`); + //dockview.setLayout(options); } else { dockview.update(options); @@ -68,14 +69,6 @@ export function update(id, options) { } } -export function setLayout(id, name) { - const dock = Data.get(id) - if (dock) { - const { dockview } = dock; - dockview.setLayout(name); - } -} - export function reset(id, options) { const dock = Data.get(id) if (dock) { @@ -94,14 +87,6 @@ export function save(id) { return ret; } -export function switchLayout(id, options) { - const dock = Data.get(id) - if (dock) { - const { dockview } = dock; - dockview.switchLayout(options); - } -} - export function dispose(id) { const dock = Data.get(id) Data.remove(id); From cc524e91c9f9909af8104dd3f05b33da8eaaf596 Mon Sep 17 00:00:00 2001 From: zhaijunlei <276318515@qq.com> Date: Sat, 8 Aug 2026 12:50:43 +0800 Subject: [PATCH 10/23] =?UTF-8?q?refactor=EF=BC=9Adockview=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=96=B0=E7=9A=84=E5=88=87=E6=8D=A2=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewComponent.razor | 36 ++++++++++--------- .../Components/DockViewV2.razor.cs | 11 +++++- .../Components/DockViewV2.razor.js | 8 +---- .../wwwroot/js/dockview-config.js | 24 +++++++++++-- .../wwwroot/js/dockview-content.js | 1 + .../wwwroot/js/dockview-panel.js | 4 ++- .../wwwroot/js/dockview-utils.js | 34 ++++++++++++++---- 7 files changed, 84 insertions(+), 34 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor index 573cc056..a3ebfcf9 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor @@ -1,18 +1,22 @@ @inherits DockViewComponentBase -
- @if (TitleTemplate != null) - { -
- @TitleTemplate -
- } - else if (ShowTitleBar) - { - - } - @if (IsRender()) - { - @ChildContent - } -
+@* 多布局下同一组件会在各布局重复声明(仅一处有内容);无条件渲染外壳会产生同 key 空 div,导致 querySelector 命中空壳 *@ +@if (ChildContent != null) +{ +
+ @if (TitleTemplate != null) + { +
+ @TitleTemplate +
+ } + else if (ShowTitleBar) + { + + } + @if (IsRender()) + { + @ChildContent + } +
+} diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index b2be6b99..40f61cab 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -313,7 +313,16 @@ private List GetLayoutContents() private bool IsEnableLocalStorage => EnableLocalStorage ?? _options.EnableLocalStorage ?? false; - private string? LocalStorageKey => IsEnableLocalStorage ? $"{GetPrefixKey()}-{Name}-{GetVersion()}" : null; + private string? LocalStorageKey + { + get + { + if (!IsEnableLocalStorage) return null; + // 各布局独立持久化;LayoutName 为空时保持原格式以兼容旧存档 + var layoutSegment = string.IsNullOrEmpty(LayoutName) ? "" : $"-{LayoutName}"; + return $"{GetPrefixKey()}-{Name}{layoutSegment}-{GetVersion()}"; + } + } private string GetVersion() => Version ?? _options.Version ?? "v1"; diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 4a9e5971..bb6aeb3d 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -59,13 +59,7 @@ export function update(id, options) { const dock = Data.get(id) if (dock) { const { dockview } = dock; - if (dockview.layoutName !== options.layoutName) { - console.log(`DockViewV2: layoutName changed from ${dockview.layoutName} to ${options.layoutName}`); - //dockview.setLayout(options); - } - else { - dockview.update(options); - } + dockview.update(options); } } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js index b81b9fd7..a2c51e9c 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js @@ -1,4 +1,4 @@ -import { getPanelsFromOptions } from "./dockview-panel.js" +import { getPanelsFromOptions, getRootContent } from "./dockview-panel.js" const initDockviewFromConfig = (dockview, options) => { const { layoutConfig, enableLocalStorage } = options; @@ -58,17 +58,35 @@ const initDockviewFromConfig = (dockview, options) => { else { dockview.fromJSON(getConfigFromContent(options)); dockview.params.invisiblePanels = []; + dockview.params.floatingGroups = []; } + // fromJSON 用 dockview.width/height 定稿;该值为 0 时整网塌缩(group 被 clamp 到 100)。 + // 刷新有 ResizeObserver 兜底,切换布局时容器尺寸未变不会触发,故主动按实测尺寸校正一次。 + syncLayoutToContainer(dockview); } +const syncLayoutToContainer = dockview => { + const el = dockview.gridview?.element?.parentElement ?? dockview.element; + if (!el) return; + const width = el.clientWidth; + const height = el.clientHeight; + // 尺寸不可用(隐藏 Tab/未挂载)时不动,交回 ResizeObserver 按原机制处理 + if (!width || !height) return; + if (width === dockview.width && height === dockview.height) return; + dockview.layout(width, height, true); +} + + const getConfigFromContent = options => { const { width, height } = { width: 800, height: 600 }; const getGroupId = getGroupIdFunc() options = filterEmptyContent(options) - const panels = {}, rootType = options.content[0].type + const rootContent = getRootContent(options) + const rootParent = { content: [rootContent] } + const panels = {}, rootType = rootContent.type const orientation = rootType === 'column' ? 'VERTICAL' : 'HORIZONTAL'; - const root = getTree(options.content[0], { width, height, orientation }, options, panels, getGroupId, options) + const root = getTree(rootContent, { width, height, orientation }, rootParent, panels, getGroupId, options) return { activeGroup: '1', grid: { width, height, orientation, root }, diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js index 1da93a8e..0433310e 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js @@ -9,6 +9,7 @@ export class DockviewPanelContent { const { tab, content } = panel.view if (template) { + // DockViewComponent.razor 仅在有 ChildContent 时渲染外壳,故每个 key 在 template 下唯一 this._element = key ? template.querySelector(`[data-bb-key="${key}"]`) : (template.querySelector(`#${this.option.id}`) ?? template.querySelector(`[data-bb-title="${title}"]`)) diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js index cc3a755a..78a36fb0 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js @@ -120,8 +120,10 @@ const updateTitle = panel => { } } +// 按 layoutName 选取布局根 content。唯一定义在此:config 建 grid、本文件取 panels,两者须选同一套布局 +export const getRootContent = options => options.content.find(c => c.layoutName === options.layoutName) || options.content[0] const getPanelsFromOptions = options => { - return getPanels(options.content[0], options) + return getPanels(getRootContent(options), options) } const getPanels = (contentItem, options, parent = {}, panels = []) => { diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index de4bad1a..6243314a 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -63,7 +63,7 @@ const guardCollapsedSaveProportions = dockview => { } const initDockview = (dockview, options, template) => { - dockview.params = { panels: [], options, template, observer: null }; + dockview.params = { panels: [], options, template, observer: null, layoutSeq: 0 }; dockview.init = function (options) { initDockviewFromConfig(this, options); } @@ -87,8 +87,11 @@ const initDockview = (dockview, options, template) => { dockview.updateTheme(); } - if (options.layoutConfig) { - dockview.reset(options); + // layoutName 变更即切换布局:各布局存档结构不同,须整体重建而非增量调整。 + // 比对放在 JS 侧:这里本就持有旧值 params.options,C# 无需再跟踪 _layoutName。 + const layoutNameChanged = oldOptions.layoutName !== options.layoutName; + if (options.layoutConfig || layoutNameChanged) { + dockview.reset(dockview.params.options); // 传已规范化的 options(含 renderer 兜底) } else { toggleComponent(dockview, options); @@ -98,8 +101,15 @@ const initDockview = (dockview, options, template) => { dockview.reset = options => { dockview.params.inited = false; dockview.params.reset = true; - dockview.init(options); - dockview.params.reset = false; + // 递增布局序号,作废上一次 reset 遗留的异步尾巴(见 onDidLayoutFromJSON 内的校验) + dockview.params.layoutSeq++; + try { + dockview.init(options); + } + finally { + // 放 finally:init 抛错时若标记不复位,会影响后续正常的面板关闭流程 + dockview.params.reset = false; + } } dockview.onDidRemovePanel(onRemovePanel); @@ -123,6 +133,8 @@ const initDockview = (dockview, options, template) => { }) dockview.onDidLayoutFromJSON(() => { + // 捕获本次布局序号,用于在异步回调里识别自己是否已被后续切换作废 + const layoutToken = dockview.params.layoutSeq; dockview.groups.forEach(group => { markFirstVisibleElement(group); }) @@ -132,9 +144,15 @@ const initDockview = (dockview, options, template) => { dockview = null; return; } + // 已被后续 reset 作废:此时 panels/floatingGroups 属于新布局,继续执行会关错面板、 + // 且 floatingGroups 匹配不到而抛错中断,导致 inited 永不置 true → saveConfig 静默失效 + if (dockview.params.layoutSeq !== layoutToken) { + return; + } const panels = dockview.panels; const groups = dockview.groups; + panels.forEach(panel => { const visible = panel.params.visible if (visible) { @@ -145,13 +163,17 @@ const initDockview = (dockview, options, template) => { } }) + if (options.renderer === 'onlyWhenVisible') { const visiblePanels = groups.filter(g => g.isVisible).map(g => g.panels.find(p => p.params.isActive) || g.panels.find(p => p.api.isVisible)) dockview._loadTabs?.fire(visiblePanels.filter(p => p.params.key).map(p => p.params.key)); } const { floatingGroups } = dockview.params dockview.floatingGroups.forEach(fg => { - const { top, right, bottom, left } = floatingGroups.find(g => g.data.id == fg.group.id).position + // 存档中无对应浮动组时跳过(布局切换/存档陈旧),避免解构 undefined 抛错中断整个回调 + const saved = floatingGroups.find(g => g.data.id == fg.group.id); + if (!saved?.position) return; + const { top, right, bottom, left } = saved.position; fg.group.element.parentElement.style.inset = [top, right, bottom, left] .map(item => typeof item == 'number' ? (item + 'px') : 'auto').join(' ') From 59789e244fd9c361a65f94a243782c644ac18027 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 19 Aug 2026 10:14:43 +0800 Subject: [PATCH 11/23] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20switchLayout?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.cs | 7 +++++++ .../Components/DockViewV2.razor.js | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 40f61cab..725735a4 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -186,6 +186,7 @@ public partial class DockViewV2 private DockViewOptions? _options = null; private ConcurrentDictionary _componentStates = new(); private string? _layoutConfig; + private string? _layoutName; private bool _disposed; /// @@ -231,6 +232,12 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender) { _layoutConfig = LayoutConfig; + _layoutName = LayoutName; + } + else if (_layoutName != LayoutName) + { + _layoutName = LayoutName; + await InvokeVoidAsync("switchLayout", Id, GetDockViewConfig()); } else if (!_triggerLoadTabs) { diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index bb6aeb3d..1836278b 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -63,6 +63,14 @@ export function update(id, options) { } } +export function switchLayout(id, options) { + const dock = Data.get(id) + if (dock) { + const { dockview } = dock; + console.log('switchLayout', options); + } +} + export function reset(id, options) { const dock = Data.get(id) if (dock) { From 03fe213ce38a2f5eb6738183ee1b9eb8ed9ee80c Mon Sep 17 00:00:00 2001 From: zhaijunlei <276318515@qq.com> Date: Wed, 19 Aug 2026 13:05:44 +0800 Subject: [PATCH 12/23] =?UTF-8?q?refactor=EF=BC=9A=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=BD=BF=E7=94=A8switchLayout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.js | 3 +-- .../wwwroot/js/dockview-utils.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 1836278b..f8f7ea6f 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -66,8 +66,7 @@ export function update(id, options) { export function switchLayout(id, options) { const dock = Data.get(id) if (dock) { - const { dockview } = dock; - console.log('switchLayout', options); + dock.dockview.switchLayout(options); } } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index 6243314a..d09d3a6f 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -101,15 +101,21 @@ const initDockview = (dockview, options, template) => { dockview.reset = options => { dockview.params.inited = false; dockview.params.reset = true; - // 递增布局序号,作废上一次 reset 遗留的异步尾巴(见 onDidLayoutFromJSON 内的校验) dockview.params.layoutSeq++; try { dockview.init(options); } finally { - // 放 finally:init 抛错时若标记不复位,会影响后续正常的面板关闭流程 dockview.params.reset = false; } + dockview.params.invisiblePanels?.forEach(p => { + dockview._panelVisibleChanged?.fire({ key: p.params.key, status: false }); + }); + } + + dockview.switchLayout = options => { + dockview.params.options = { ...options, renderer: options.renderer || 'onlyWhenVisible' }; + dockview.reset(dockview.params.options); } dockview.onDidRemovePanel(onRemovePanel); From e25409a2ace2b7cc4a759647ef13adc89cfb5a50 Mon Sep 17 00:00:00 2001 From: zhaijunlei <276318515@qq.com> Date: Fri, 28 Aug 2026 16:04:15 +0800 Subject: [PATCH 13/23] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=B8=83=E5=B1=80=E6=97=B6=E6=8A=BD=E5=B1=89=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E7=B4=AF=E7=A7=AF=E4=B8=8E=E5=A4=B1=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - reset 前整体清空抽屉按钮容器(:scope > 限定直接子元素),避免 onDidLayoutFromJSON 重建后累积 - 浮动组位置恢复与抽屉重建解耦:位置缺失跳过防解构抛错,抽屉重建无条件执行 - 布局切换判定收敛到 C# 侧显式调用 switchLayout,update 仅做增量更新 - removeDrawerBtn 空值保护;纯 LayoutConfig 用法的空集合守卫(C#/JS 各处) - GetLayoutContents 空集合守卫,防 _components[0] 越界 --- .../Components/DockViewV2.razor.cs | 6 ++++ .../Components/DockViewV2.razor.js | 3 -- .../wwwroot/js/dockview-config.js | 22 ++++++++++----- .../wwwroot/js/dockview-group.js | 3 +- .../wwwroot/js/dockview-panel.js | 4 ++- .../wwwroot/js/dockview-utils.js | 28 +++++++++++-------- 6 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 725735a4..f747e36c 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -309,6 +309,12 @@ private DockViewConfig GetDockViewConfig() /// private List GetLayoutContents() { + // 纯 LayoutConfig 驱动(未声明子组件)时集合为空,此用法由 GetDockViewConfig 的守卫显式允许 + if (_components.Count == 0) + { + return []; + } + var content = _components[0]; if (string.IsNullOrEmpty(LayoutName)) { diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index f8f7ea6f..214d654d 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -17,9 +17,6 @@ export async function init(id, invoke, options) { } } const dockview = cerateDockview(el, options); - if (options.layoutName) { - dockview.layoutName = options.layoutName; - } dockview.params.invisiblePanels?.forEach(invisiblePanel => { invoke.invokeMethodAsync(options.panelVisibleChangedCallback, invisiblePanel.params.key, false); }) diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js index a2c51e9c..56420e78 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js @@ -83,6 +83,14 @@ const getConfigFromContent = options => { const getGroupId = getGroupIdFunc() options = filterEmptyContent(options) const rootContent = getRootContent(options) + // 无 content(纯 layoutConfig 且存档缺失/损坏)时兜底空网格,防解构抛错 + if (!rootContent) { + return { + activeGroup: '1', + grid: { width, height, orientation: 'HORIZONTAL', root: { type: 'branch', size: 0, data: [] } }, + panels: {} + } + } const rootParent = { content: [rootContent] } const panels = {}, rootType = rootContent.type const orientation = rootType === 'column' ? 'VERTICAL' : 'HORIZONTAL'; @@ -278,9 +286,7 @@ const saveConfig = dockview => { return; } - // Don't persist a collapsed/unmeasured layout: while collapsed toJSON() serializes size:100, which - // makes the even-split sticky across refreshes. Real layouts are measured (width>0), so this only - // drops a degenerate save the next change re-saves. + // 折叠/未测量时 toJSON 会把 size 定格为 100,导致均分比例刷新后粘滞,不落档 if (!dockview.width || !dockview.height) { return; } @@ -289,10 +295,7 @@ const saveConfig = dockview => { panel.params.isActive = panel.api.isActive || panel.group.activePanel === panel }) - // While maximized, toJSON()'s serialize() toggles sibling visibility once; guard it with - // `maximizing` so the onlyWhenVisible handler doesn't move sibling content into the template - // (which would blank it after exit). finally resets the flag even on throw, otherwise a stuck - // `maximizing` would make every later saveConfig bail at the top guard. + // toJSON 在最大化时会切换兄弟面板可见性,以 maximizing 防护避免内容被搬进 template;finally 复位防卡死 let gridJson; dockview.params.maximizing = true; try { @@ -359,6 +362,11 @@ const syncLayoutWithOptions = (layout, options, invisiblePanels = []) => { if (p.params?.key) localIdByKey.set(p.params.key, p.id); }); + // 无面板可同步时存档即全部事实,原样返回(getConfigFromContent 需要非空 content) + if (optionPanels.length === 0) { + return layout; + } + const matchCount = [...optionPanelByKey.keys()].filter(key => localIdByKey.has(key)).length; if (matchCount === 0) { return getConfigFromContent(options); diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js index 465db180..4dc42f66 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js @@ -463,7 +463,8 @@ const createDrawerBtn = (floatingGroup, isRight) => { const removeDrawerBtn = group => { const parentEle = group.api.accessor.element.parentElement.parentElement const btnList = parentEle?.querySelectorAll(`[groupid="${group.api.accessor.id}_${group.id}"]`) - btnList.forEach(btn => btn.remove()) + // parentEle 为空时 btnList 为 undefined + btnList?.forEach(btn => btn.remove()) } const setDrawerTitle = group => { diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js index 78a36fb0..f462fc42 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js @@ -123,7 +123,9 @@ const updateTitle = panel => { // 按 layoutName 选取布局根 content。唯一定义在此:config 建 grid、本文件取 panels,两者须选同一套布局 export const getRootContent = options => options.content.find(c => c.layoutName === options.layoutName) || options.content[0] const getPanelsFromOptions = options => { - return getPanels(getRootContent(options), options) + const rootContent = getRootContent(options) + // 纯 layoutConfig 驱动(未声明子组件)时无 content,返回空面板集 + return rootContent ? getPanels(rootContent, options) : [] } const getPanels = (contentItem, options, parent = {}, panels = []) => { diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index d09d3a6f..b0d8ff31 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -62,6 +62,13 @@ const guardCollapsedSaveProportions = dockview => { }; } +// reset 路径的 removeGroup 提前返回、跳过 removeDrawerBtn,须在此整体清空抽屉按钮容器, +// 否则 onDidLayoutFromJSON 按新存档重建后按钮累积,残留闭包指向已销毁 group。 +// :scope > 限定直接子元素,不波及嵌套 dockview。 +const removeDrawerButtons = dockview => { + dockview.element?.querySelectorAll(':scope > .bb-dockview-aside').forEach(el => el.remove()); +} + const initDockview = (dockview, options, template) => { dockview.params = { panels: [], options, template, observer: null, layoutSeq: 0 }; dockview.init = function (options) { @@ -87,10 +94,8 @@ const initDockview = (dockview, options, template) => { dockview.updateTheme(); } - // layoutName 变更即切换布局:各布局存档结构不同,须整体重建而非增量调整。 - // 比对放在 JS 侧:这里本就持有旧值 params.options,C# 无需再跟踪 _layoutName。 - const layoutNameChanged = oldOptions.layoutName !== options.layoutName; - if (options.layoutConfig || layoutNameChanged) { + // 布局切换由 C# 侧显式调用 switchLayout,update 只做增量更新 + if (options.layoutConfig) { dockview.reset(dockview.params.options); // 传已规范化的 options(含 renderer 兜底) } else { @@ -102,6 +107,7 @@ const initDockview = (dockview, options, template) => { dockview.params.inited = false; dockview.params.reset = true; dockview.params.layoutSeq++; + removeDrawerButtons(dockview); try { dockview.init(options); } @@ -150,8 +156,7 @@ const initDockview = (dockview, options, template) => { dockview = null; return; } - // 已被后续 reset 作废:此时 panels/floatingGroups 属于新布局,继续执行会关错面板、 - // 且 floatingGroups 匹配不到而抛错中断,导致 inited 永不置 true → saveConfig 静默失效 + // 已被后续 reset 作废,继续执行会操作新布局的面板并中断 inited 置位 if (dockview.params.layoutSeq !== layoutToken) { return; } @@ -176,13 +181,14 @@ const initDockview = (dockview, options, template) => { } const { floatingGroups } = dockview.params dockview.floatingGroups.forEach(fg => { - // 存档中无对应浮动组时跳过(布局切换/存档陈旧),避免解构 undefined 抛错中断整个回调 + // 位置缺失时跳过防解构抛错;抽屉重建与位置无关,须无条件执行(reset 已清空按钮容器) const saved = floatingGroups.find(g => g.data.id == fg.group.id); - if (!saved?.position) return; - const { top, right, bottom, left } = saved.position; + if (saved?.position) { + const { top, right, bottom, left } = saved.position; - fg.group.element.parentElement.style.inset = [top, right, bottom, left] - .map(item => typeof item == 'number' ? (item + 'px') : 'auto').join(' ') + fg.group.element.parentElement.style.inset = [top, right, bottom, left] + .map(item => typeof item == 'number' ? (item + 'px') : 'auto').join(' ') + } observeOverlayChange(fg.overlay, fg.group) const { floatType, direction } = fg.group.getParams(); From a5fdc2fbfab60fe6ae0ba5e18d80e1f7016e364a Mon Sep 17 00:00:00 2001 From: zhaijunlei <276318515@qq.com> Date: Fri, 28 Aug 2026 16:13:48 +0800 Subject: [PATCH 14/23] =?UTF-8?q?chore:=20=E7=BB=9F=E4=B8=80=E4=B8=BA?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.cs | 2 +- .../wwwroot/js/dockview-config.js | 9 +++++---- .../wwwroot/js/dockview-group.js | 2 +- .../wwwroot/js/dockview-panel.js | 4 ++-- .../wwwroot/js/dockview-utils.js | 13 +++++++------ 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index f747e36c..90ff8ead 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -309,7 +309,7 @@ private DockViewConfig GetDockViewConfig() /// private List GetLayoutContents() { - // 纯 LayoutConfig 驱动(未声明子组件)时集合为空,此用法由 GetDockViewConfig 的守卫显式允许 + // Pure LayoutConfig mode declares no child components; allowed by GetDockViewConfig's guard if (_components.Count == 0) { return []; diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js index 56420e78..0d22e730 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js @@ -83,7 +83,7 @@ const getConfigFromContent = options => { const getGroupId = getGroupIdFunc() options = filterEmptyContent(options) const rootContent = getRootContent(options) - // 无 content(纯 layoutConfig 且存档缺失/损坏)时兜底空网格,防解构抛错 + // Fallback empty grid when no content (pure layoutConfig with a missing/corrupt archive), to avoid destructuring throws if (!rootContent) { return { activeGroup: '1', @@ -286,7 +286,7 @@ const saveConfig = dockview => { return; } - // 折叠/未测量时 toJSON 会把 size 定格为 100,导致均分比例刷新后粘滞,不落档 + // Don't persist a collapsed/unmeasured layout: toJSON freezes size to 100, making even splits sticky across reloads if (!dockview.width || !dockview.height) { return; } @@ -295,7 +295,8 @@ const saveConfig = dockview => { panel.params.isActive = panel.api.isActive || panel.group.activePanel === panel }) - // toJSON 在最大化时会切换兄弟面板可见性,以 maximizing 防护避免内容被搬进 template;finally 复位防卡死 + // toJSON toggles sibling visibility while maximized; guard with `maximizing` so content isn't moved + // into the template. finally resets the flag even on throw so later saves don't bail out. let gridJson; dockview.params.maximizing = true; try { @@ -362,7 +363,7 @@ const syncLayoutWithOptions = (layout, options, invisiblePanels = []) => { if (p.params?.key) localIdByKey.set(p.params.key, p.id); }); - // 无面板可同步时存档即全部事实,原样返回(getConfigFromContent 需要非空 content) + // Nothing to sync: the archive is the whole truth (getConfigFromContent requires non-empty content) if (optionPanels.length === 0) { return layout; } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js index 4dc42f66..4937bc92 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js @@ -463,7 +463,7 @@ const createDrawerBtn = (floatingGroup, isRight) => { const removeDrawerBtn = group => { const parentEle = group.api.accessor.element.parentElement.parentElement const btnList = parentEle?.querySelectorAll(`[groupid="${group.api.accessor.id}_${group.id}"]`) - // parentEle 为空时 btnList 为 undefined + // btnList is undefined when parentEle is null btnList?.forEach(btn => btn.remove()) } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js index f462fc42..14c2dd9d 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js @@ -120,11 +120,11 @@ const updateTitle = panel => { } } -// 按 layoutName 选取布局根 content。唯一定义在此:config 建 grid、本文件取 panels,两者须选同一套布局 +// Pick the layout root by layoutName. Single definition: config grid-building and panel extraction here must select the same layout export const getRootContent = options => options.content.find(c => c.layoutName === options.layoutName) || options.content[0] const getPanelsFromOptions = options => { const rootContent = getRootContent(options) - // 纯 layoutConfig 驱动(未声明子组件)时无 content,返回空面板集 + // Pure layoutConfig mode declares no content; return an empty panel set return rootContent ? getPanels(rootContent, options) : [] } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index b0d8ff31..8898a03b 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -62,9 +62,9 @@ const guardCollapsedSaveProportions = dockview => { }; } -// reset 路径的 removeGroup 提前返回、跳过 removeDrawerBtn,须在此整体清空抽屉按钮容器, -// 否则 onDidLayoutFromJSON 按新存档重建后按钮累积,残留闭包指向已销毁 group。 -// :scope > 限定直接子元素,不波及嵌套 dockview。 +// The patched removeGroup returns early on the reset path, skipping removeDrawerBtn; clear the +// drawer-button container here or onDidLayoutFromJSON rebuilds on top of stale ones whose closures +// point to destroyed groups. ":scope >" keeps nested dockviews untouched. const removeDrawerButtons = dockview => { dockview.element?.querySelectorAll(':scope > .bb-dockview-aside').forEach(el => el.remove()); } @@ -94,7 +94,7 @@ const initDockview = (dockview, options, template) => { dockview.updateTheme(); } - // 布局切换由 C# 侧显式调用 switchLayout,update 只做增量更新 + // Layout switching is an explicit switchLayout call from C#; update stays incremental if (options.layoutConfig) { dockview.reset(dockview.params.options); // 传已规范化的 options(含 renderer 兜底) } @@ -156,7 +156,7 @@ const initDockview = (dockview, options, template) => { dockview = null; return; } - // 已被后续 reset 作废,继续执行会操作新布局的面板并中断 inited 置位 + // Superseded by a later reset: proceeding would touch the new layout's panels and skip setting inited if (dockview.params.layoutSeq !== layoutToken) { return; } @@ -181,7 +181,8 @@ const initDockview = (dockview, options, template) => { } const { floatingGroups } = dockview.params dockview.floatingGroups.forEach(fg => { - // 位置缺失时跳过防解构抛错;抽屉重建与位置无关,须无条件执行(reset 已清空按钮容器) + // Skip missing positions to avoid destructuring throws; drawer rebuild is + // position-independent and must always run (reset already cleared the container) const saved = floatingGroups.find(g => g.data.id == fg.group.id); if (saved?.position) { const { top, right, bottom, left } = saved.position; From 795fa2e9af97a02639056af315960a12250743b7 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 31 Aug 2026 16:53:41 +0800 Subject: [PATCH 15/23] =?UTF-8?q?doc:=20=E7=A7=BB=E9=99=A4=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BootstrapBlazor.DockView/Components/DockViewV2.razor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 90ff8ead..c0fb25b5 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -309,7 +309,6 @@ private DockViewConfig GetDockViewConfig() /// private List GetLayoutContents() { - // Pure LayoutConfig mode declares no child components; allowed by GetDockViewConfig's guard if (_components.Count == 0) { return []; From 509f55f809a26dd958529660c7d1ea66add65158 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 31 Aug 2026 16:58:11 +0800 Subject: [PATCH 16/23] =?UTF-8?q?revert:=20=E6=92=A4=E9=94=80=E6=9B=B4?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewComponent.razor | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor index a3ebfcf9..573cc056 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor @@ -1,22 +1,18 @@ @inherits DockViewComponentBase -@* 多布局下同一组件会在各布局重复声明(仅一处有内容);无条件渲染外壳会产生同 key 空 div,导致 querySelector 命中空壳 *@ -@if (ChildContent != null) -{ -
- @if (TitleTemplate != null) - { -
- @TitleTemplate -
- } - else if (ShowTitleBar) - { - - } - @if (IsRender()) - { - @ChildContent - } -
-} +
+ @if (TitleTemplate != null) + { +
+ @TitleTemplate +
+ } + else if (ShowTitleBar) + { + + } + @if (IsRender()) + { + @ChildContent + } +
From 6d6eb3f376f81c20dbe2d6be9431197532d095ff Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 31 Aug 2026 17:01:25 +0800 Subject: [PATCH 17/23] =?UTF-8?q?revert:=20=E6=92=A4=E9=94=80=E6=9B=B4?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index 214d654d..e1818586 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -60,13 +60,6 @@ export function update(id, options) { } } -export function switchLayout(id, options) { - const dock = Data.get(id) - if (dock) { - dock.dockview.switchLayout(options); - } -} - export function reset(id, options) { const dock = Data.get(id) if (dock) { @@ -85,6 +78,14 @@ export function save(id) { return ret; } +export function switchLayout(id, options) { + const dock = Data.get(id) + if (dock) { + const { dockview } = dock; + dockview.switchLayout(options); + } +} + export function dispose(id) { const dock = Data.get(id) Data.remove(id); From 553dd3abb15fe0ed0708063e6c92c4d55d26007b Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 31 Aug 2026 17:04:04 +0800 Subject: [PATCH 18/23] chore(DockView): remove JavaScript comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../wwwroot/js/dockview-config.js | 8 -------- .../wwwroot/js/dockview-content.js | 1 - .../wwwroot/js/dockview-group.js | 1 - .../wwwroot/js/dockview-panel.js | 2 -- .../wwwroot/js/dockview-utils.js | 10 +--------- 5 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js index 0d22e730..91dd7d2f 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js @@ -61,8 +61,6 @@ const initDockviewFromConfig = (dockview, options) => { dockview.params.floatingGroups = []; } - // fromJSON 用 dockview.width/height 定稿;该值为 0 时整网塌缩(group 被 clamp 到 100)。 - // 刷新有 ResizeObserver 兜底,切换布局时容器尺寸未变不会触发,故主动按实测尺寸校正一次。 syncLayoutToContainer(dockview); } @@ -71,7 +69,6 @@ const syncLayoutToContainer = dockview => { if (!el) return; const width = el.clientWidth; const height = el.clientHeight; - // 尺寸不可用(隐藏 Tab/未挂载)时不动,交回 ResizeObserver 按原机制处理 if (!width || !height) return; if (width === dockview.width && height === dockview.height) return; dockview.layout(width, height, true); @@ -83,7 +80,6 @@ const getConfigFromContent = options => { const getGroupId = getGroupIdFunc() options = filterEmptyContent(options) const rootContent = getRootContent(options) - // Fallback empty grid when no content (pure layoutConfig with a missing/corrupt archive), to avoid destructuring throws if (!rootContent) { return { activeGroup: '1', @@ -286,7 +282,6 @@ const saveConfig = dockview => { return; } - // Don't persist a collapsed/unmeasured layout: toJSON freezes size to 100, making even splits sticky across reloads if (!dockview.width || !dockview.height) { return; } @@ -295,8 +290,6 @@ const saveConfig = dockview => { panel.params.isActive = panel.api.isActive || panel.group.activePanel === panel }) - // toJSON toggles sibling visibility while maximized; guard with `maximizing` so content isn't moved - // into the template. finally resets the flag even on throw so later saves don't bail out. let gridJson; dockview.params.maximizing = true; try { @@ -363,7 +356,6 @@ const syncLayoutWithOptions = (layout, options, invisiblePanels = []) => { if (p.params?.key) localIdByKey.set(p.params.key, p.id); }); - // Nothing to sync: the archive is the whole truth (getConfigFromContent requires non-empty content) if (optionPanels.length === 0) { return layout; } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js index 0433310e..1da93a8e 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-content.js @@ -9,7 +9,6 @@ export class DockviewPanelContent { const { tab, content } = panel.view if (template) { - // DockViewComponent.razor 仅在有 ChildContent 时渲染外壳,故每个 key 在 template 下唯一 this._element = key ? template.querySelector(`[data-bb-key="${key}"]`) : (template.querySelector(`#${this.option.id}`) ?? template.querySelector(`[data-bb-title="${title}"]`)) diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js index 4937bc92..56fca224 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js @@ -463,7 +463,6 @@ const createDrawerBtn = (floatingGroup, isRight) => { const removeDrawerBtn = group => { const parentEle = group.api.accessor.element.parentElement.parentElement const btnList = parentEle?.querySelectorAll(`[groupid="${group.api.accessor.id}_${group.id}"]`) - // btnList is undefined when parentEle is null btnList?.forEach(btn => btn.remove()) } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js index 14c2dd9d..c9eb0f63 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js @@ -120,11 +120,9 @@ const updateTitle = panel => { } } -// Pick the layout root by layoutName. Single definition: config grid-building and panel extraction here must select the same layout export const getRootContent = options => options.content.find(c => c.layoutName === options.layoutName) || options.content[0] const getPanelsFromOptions = options => { const rootContent = getRootContent(options) - // Pure layoutConfig mode declares no content; return an empty panel set return rootContent ? getPanels(rootContent, options) : [] } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index 8898a03b..57306c19 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -62,9 +62,6 @@ const guardCollapsedSaveProportions = dockview => { }; } -// The patched removeGroup returns early on the reset path, skipping removeDrawerBtn; clear the -// drawer-button container here or onDidLayoutFromJSON rebuilds on top of stale ones whose closures -// point to destroyed groups. ":scope >" keeps nested dockviews untouched. const removeDrawerButtons = dockview => { dockview.element?.querySelectorAll(':scope > .bb-dockview-aside').forEach(el => el.remove()); } @@ -94,9 +91,8 @@ const initDockview = (dockview, options, template) => { dockview.updateTheme(); } - // Layout switching is an explicit switchLayout call from C#; update stays incremental if (options.layoutConfig) { - dockview.reset(dockview.params.options); // 传已规范化的 options(含 renderer 兜底) + dockview.reset(dockview.params.options); } else { toggleComponent(dockview, options); @@ -145,7 +141,6 @@ const initDockview = (dockview, options, template) => { }) dockview.onDidLayoutFromJSON(() => { - // 捕获本次布局序号,用于在异步回调里识别自己是否已被后续切换作废 const layoutToken = dockview.params.layoutSeq; dockview.groups.forEach(group => { markFirstVisibleElement(group); @@ -156,7 +151,6 @@ const initDockview = (dockview, options, template) => { dockview = null; return; } - // Superseded by a later reset: proceeding would touch the new layout's panels and skip setting inited if (dockview.params.layoutSeq !== layoutToken) { return; } @@ -181,8 +175,6 @@ const initDockview = (dockview, options, template) => { } const { floatingGroups } = dockview.params dockview.floatingGroups.forEach(fg => { - // Skip missing positions to avoid destructuring throws; drawer rebuild is - // position-independent and must always run (reset already cleared the container) const saved = floatingGroups.find(g => g.data.id == fg.group.id); if (saved?.position) { const { top, right, bottom, left } = saved.position; From 3d08d6f45372a0ed0b06484e8035cd2ed62136a9 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 31 Aug 2026 17:28:00 +0800 Subject: [PATCH 19/23] =?UTF-8?q?refactor:=20=E6=92=A4=E9=94=80=E6=9B=B4?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewComponent.razor | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor index 573cc056..5b3c1443 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor @@ -1,18 +1,21 @@ @inherits DockViewComponentBase -
- @if (TitleTemplate != null) - { -
- @TitleTemplate -
- } - else if (ShowTitleBar) - { - - } - @if (IsRender()) - { - @ChildContent - } -
+@if (ChildContent != null) +{ +
+ @if (TitleTemplate != null) + { +
+ @TitleTemplate +
+ } + else if (ShowTitleBar) + { + + } + @if (IsRender()) + { + @ChildContent + } +
+} From d63cbc35ab468bf714e5b18572b53316e2b6cb4d Mon Sep 17 00:00:00 2001 From: zhaijunlei <276318515@qq.com> Date: Tue, 1 Sep 2026 16:03:01 +0800 Subject: [PATCH 20/23] =?UTF-8?q?perf:=20=E6=8F=90=E5=8D=87=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=B8=83=E5=B1=80=E6=97=B6=E6=96=B0=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E7=9A=84=E6=98=BE=E7=A4=BA=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - postLayoutInit:浮窗位置恢复/观察器挂载/inited 门控从 setTimeout(0) 提前到 initDockviewFromConfig 返回后同步执行 - 可见性通知/loadTabs/initialized 改为微任务发出(早于绘制,消除队列空等约50ms) - 抽屉外点关闭改为实例级单次绑定,修复重复绑定 - onDidLayoutFromJSON 仅保留 markFirstVisibleElement,重建后处理收敛到 postLayoutInit 单处维护 --- .../Components/DockViewV2.razor.js | 3 - .../wwwroot/js/dockview-utils.js | 136 +++++++++--------- 2 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index e1818586..fc30e41f 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -17,9 +17,6 @@ export async function init(id, invoke, options) { } } const dockview = cerateDockview(el, options); - dockview.params.invisiblePanels?.forEach(invisiblePanel => { - invoke.invokeMethodAsync(options.panelVisibleChangedCallback, invisiblePanel.params.key, false); - }) const updateTheme = e => dockview.switchTheme(e.theme); Data.set(id, { el, dockview, updateTheme }); diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index 57306c19..5e6702bd 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -66,10 +66,81 @@ const removeDrawerButtons = dockview => { dockview.element?.querySelectorAll(':scope > .bb-dockview-aside').forEach(el => el.remove()); } +// Post-rebuild work for the initial load and every reset (both go through dockview.init). +// Notifications run one microtask later so razor.js subscribers — attached after cerateDockview +// returns — exist, replacing the old setTimeout(0) whose queue wait delayed the content render. +const postLayoutInit = (dockview, options) => { + if (dockview._isDisposed) { + return + } + const { floatingGroups } = dockview.params + dockview.floatingGroups.forEach(fg => { + const saved = floatingGroups.find(g => g.data.id == fg.group.id); + if (saved?.position) { + const { top, right, bottom, left } = saved.position; + + fg.group.element.parentElement.style.inset = [top, right, bottom, left] + .map(item => typeof item == 'number' ? (item + 'px') : 'auto').join(' ') + } + + observeOverlayChange(fg.overlay, fg.group) + const { floatType, direction } = fg.group.getParams(); + if (floatType == 'drawer') { + createDrawerHandle(fg.group, direction == 'right') + } + observeFloatingGroupLocationChange(fg.group) + }) + + dockview.groups.forEach(group => { + observeGroup(group) + }) + + // Bind once per instance: re-binding on every rebuild piled duplicate handlers. + if (!dockview.params.drawerHandlerBound) { + dockview.params.drawerHandlerBound = true; + dockview.element.querySelector('&>.dv-dockview>.dv-branch-node')?.addEventListener('click', function (e) { + this.parentElement.querySelectorAll('&>.dv-resize-container-drawer, &>.dv-render-overlay-float-drawer')?.forEach(item => { + item.classList.remove('active') + }) + this.closest('.bb-dockview').querySelectorAll('&>.bb-dockview-aside>.bb-dockview-aside-button')?.forEach(item => { + item.classList.remove('active') + }) + }) + } + + // Must stay the last synchronous step: keeps saveConfig suppressed until post-processing completes. + dockview.params.inited = true; + + queueMicrotask(() => { + if (dockview._isDisposed) { + return + } + // Hidden set first (merged from reset's tail and the razor.js init loop), then visible — original order. + dockview.params.invisiblePanels?.forEach(p => { + dockview._panelVisibleChanged?.fire({ key: p.params.key, status: false }); + }) + dockview.panels.forEach(panel => { + if (panel.params.visible) { + dockview._panelVisibleChanged?.fire({ key: panel.params.key, status: true }); + } + else { + panel.group.model.closePanel(panel) + } + }) + + if (options.renderer === 'onlyWhenVisible') { + const visiblePanels = dockview.groups.filter(g => g.isVisible).map(g => g.panels.find(p => p.params.isActive) || g.panels.find(p => p.api.isVisible)) + dockview._loadTabs?.fire(visiblePanels.filter(p => p.params.key).map(p => p.params.key)); + } + dockview._initialized?.fire(); + }) +} + const initDockview = (dockview, options, template) => { dockview.params = { panels: [], options, template, observer: null, layoutSeq: 0 }; dockview.init = function (options) { initDockviewFromConfig(this, options); + postLayoutInit(this, options); } dockview.switchTheme = theme => { @@ -110,9 +181,6 @@ const initDockview = (dockview, options, template) => { finally { dockview.params.reset = false; } - dockview.params.invisiblePanels?.forEach(p => { - dockview._panelVisibleChanged?.fire({ key: p.params.key, status: false }); - }); } dockview.switchLayout = options => { @@ -141,70 +209,10 @@ const initDockview = (dockview, options, template) => { }) dockview.onDidLayoutFromJSON(() => { - const layoutToken = dockview.params.layoutSeq; + // Rebuild post-processing lives in postLayoutInit (called by the dockview.init wrapper). dockview.groups.forEach(group => { markFirstVisibleElement(group); }) - const handler = setTimeout(() => { - clearTimeout(handler); - if (dockview._isDisposed) { - dockview = null; - return; - } - if (dockview.params.layoutSeq !== layoutToken) { - return; - } - const panels = dockview.panels; - const groups = dockview.groups; - - - panels.forEach(panel => { - const visible = panel.params.visible - if (visible) { - dockview._panelVisibleChanged?.fire({ key: panel.params.key, status: true }); - } - else { - panel.group.model.closePanel(panel) - } - }) - - - if (options.renderer === 'onlyWhenVisible') { - const visiblePanels = groups.filter(g => g.isVisible).map(g => g.panels.find(p => p.params.isActive) || g.panels.find(p => p.api.isVisible)) - dockview._loadTabs?.fire(visiblePanels.filter(p => p.params.key).map(p => p.params.key)); - } - const { floatingGroups } = dockview.params - dockview.floatingGroups.forEach(fg => { - const saved = floatingGroups.find(g => g.data.id == fg.group.id); - if (saved?.position) { - const { top, right, bottom, left } = saved.position; - - fg.group.element.parentElement.style.inset = [top, right, bottom, left] - .map(item => typeof item == 'number' ? (item + 'px') : 'auto').join(' ') - } - - observeOverlayChange(fg.overlay, fg.group) - const { floatType, direction } = fg.group.getParams(); - if (floatType == 'drawer') { - createDrawerHandle(fg.group, direction == 'right') - } - observeFloatingGroupLocationChange(fg.group) - }) - - dockview.groups.forEach(group => { - observeGroup(group) - }) - dockview.element.querySelector('&>.dv-dockview>.dv-branch-node')?.addEventListener('click', function (e) { - this.parentElement.querySelectorAll('&>.dv-resize-container-drawer, &>.dv-render-overlay-float-drawer')?.forEach(item => { - item.classList.remove('active') - }) - this.closest('.bb-dockview').querySelectorAll('&>.bb-dockview-aside>.bb-dockview-aside-button')?.forEach(item => { - item.classList.remove('active') - }) - }) - dockview.params.inited = true; - dockview._initialized?.fire(); - }, 0); }) dockview.gridview.onDidChange(event => { From 1ef2b80535df59383227cc891c3939bcd54aaca7 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 2 Sep 2026 08:10:54 +0800 Subject: [PATCH 21/23] chore: bump version 10.0.5 --- .../BootstrapBlazor.Region/BootstrapBlazor.Region.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj b/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj index d4200eaf..5f59acfd 100644 --- a/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj +++ b/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj @@ -1,7 +1,7 @@  - 10.0.4 + 10.0.5 From 65df8a8677d3513fb2f47a43e4766da1e781d9b3 Mon Sep 17 00:00:00 2001 From: zhaijunlei <276318515@qq.com> Date: Thu, 3 Sep 2026 10:17:48 +0800 Subject: [PATCH 22/23] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DockViewV2.razor.cs | 1 - .../BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js | 7 ------- 2 files changed, 8 deletions(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index c0fb25b5..9454ddec 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -330,7 +330,6 @@ private string? LocalStorageKey get { if (!IsEnableLocalStorage) return null; - // 各布局独立持久化;LayoutName 为空时保持原格式以兼容旧存档 var layoutSegment = string.IsNullOrEmpty(LayoutName) ? "" : $"-{LayoutName}"; return $"{GetPrefixKey()}-{Name}{layoutSegment}-{GetVersion()}"; } diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index 5e6702bd..fb1ed4fc 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -66,9 +66,6 @@ const removeDrawerButtons = dockview => { dockview.element?.querySelectorAll(':scope > .bb-dockview-aside').forEach(el => el.remove()); } -// Post-rebuild work for the initial load and every reset (both go through dockview.init). -// Notifications run one microtask later so razor.js subscribers — attached after cerateDockview -// returns — exist, replacing the old setTimeout(0) whose queue wait delayed the content render. const postLayoutInit = (dockview, options) => { if (dockview._isDisposed) { return @@ -95,7 +92,6 @@ const postLayoutInit = (dockview, options) => { observeGroup(group) }) - // Bind once per instance: re-binding on every rebuild piled duplicate handlers. if (!dockview.params.drawerHandlerBound) { dockview.params.drawerHandlerBound = true; dockview.element.querySelector('&>.dv-dockview>.dv-branch-node')?.addEventListener('click', function (e) { @@ -108,14 +104,12 @@ const postLayoutInit = (dockview, options) => { }) } - // Must stay the last synchronous step: keeps saveConfig suppressed until post-processing completes. dockview.params.inited = true; queueMicrotask(() => { if (dockview._isDisposed) { return } - // Hidden set first (merged from reset's tail and the razor.js init loop), then visible — original order. dockview.params.invisiblePanels?.forEach(p => { dockview._panelVisibleChanged?.fire({ key: p.params.key, status: false }); }) @@ -209,7 +203,6 @@ const initDockview = (dockview, options, template) => { }) dockview.onDidLayoutFromJSON(() => { - // Rebuild post-processing lives in postLayoutInit (called by the dockview.init wrapper). dockview.groups.forEach(group => { markFirstVisibleElement(group); }) From b54f785b488449a4a829ec33ece49e42bd4b96d8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 3 Sep 2026 16:41:57 +0800 Subject: [PATCH 23/23] =?UTF-8?q?refactor:=20=E4=BB=A3=E7=A0=81=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BootstrapBlazor.DockView/Components/DockViewV2.razor.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 9454ddec..9402395f 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -329,7 +329,11 @@ private string? LocalStorageKey { get { - if (!IsEnableLocalStorage) return null; + if (!IsEnableLocalStorage) + { + return null; + } + var layoutSegment = string.IsNullOrEmpty(LayoutName) ? "" : $"-{LayoutName}"; return $"{GetPrefixKey()}-{Name}{layoutSegment}-{GetVersion()}"; }