Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
57d87ac
feat: 增加 LayoutName 参数
ArgoZhang Aug 4, 2026
b4cc51a
feat: 增加 setLayout 脚本
ArgoZhang Aug 4, 2026
719e349
feat: 增加 LayoutName 参数
ArgoZhang Aug 4, 2026
fb4195b
chore: bump version 10.0.24
ArgoZhang Aug 4, 2026
b25f719
refactor: 增加切换 layout 逻辑
ArgoZhang Aug 6, 2026
2b43179
refactor: 更新 js 逻辑
ArgoZhang Aug 6, 2026
da4d601
refactor: 更新序列化逻辑
ArgoZhang Aug 6, 2026
e9f8931
Revert "refactor: 更新 js 逻辑"
ArgoZhang Aug 6, 2026
ca24607
refactor: 精简代码
ArgoZhang Aug 7, 2026
cc524e9
refactor:dockview支持新的切换布局逻辑
zhaijunlei955 Aug 8, 2026
59789e2
feat: 增加 switchLayout 方法
ArgoZhang Aug 19, 2026
03fe213
refactor:切换布局使用switchLayout
zhaijunlei955 Aug 19, 2026
e25409a
fix: 修复切换布局时抽屉按钮累积与失联
zhaijunlei955 Aug 28, 2026
a5fdc2f
chore: 统一为英文注释
zhaijunlei955 Aug 28, 2026
803fd6a
Merge branch 'master' into dev-dv
ArgoZhang Aug 31, 2026
795fa2e
doc: 移除注释
ArgoZhang Aug 31, 2026
509f55f
revert: 撤销更改
ArgoZhang Aug 31, 2026
6d6eb3f
revert: 撤销更改
ArgoZhang Aug 31, 2026
553dd3a
chore(DockView): remove JavaScript comments
ArgoZhang Aug 31, 2026
3d08d6f
refactor: 撤销更改
ArgoZhang Aug 31, 2026
d759579
Merge branch 'master' into feat/dockview-layout-switching
ArgoZhang Aug 31, 2026
d63cbc3
perf: 提升切换布局时新面板的显示速度
zhaijunlei955 Sep 1, 2026
1ef2b80
chore: bump version 10.0.5
ArgoZhang Sep 2, 2026
65df8a8
清理注释
zhaijunlei955 Sep 3, 2026
b54f785
refactor: 代码格式化
ArgoZhang Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.0.23</Version>
<Version>10.0.24</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
@inherits DockViewComponentBase

<div id="@Id" class="bb-dockview-panel" data-bb-key="@Key" data-bb-title="@Title">
@if (TitleTemplate != null)
{
<div class="bb-dockview-item-title">
@TitleTemplate
</div>
}
else if (ShowTitleBar)
{
<DockViewTitleBar BarIcon="@TitleBarIcon" BarIconUrl="@TitleBarIconUrl" OnClickBarCallback="OnClickBar"></DockViewTitleBar>
}
@if (IsRender())
{
@ChildContent
}
</div>
@if (ChildContent != null)
{
<div id="@Id" class="bb-dockview-panel" data-bb-key="@Key" data-bb-title="@Title">
@if (TitleTemplate != null)
{
<div class="bb-dockview-item-title">
@TitleTemplate
</div>
}
else if (ShowTitleBar)
{
<DockViewTitleBar BarIcon="@TitleBarIcon" BarIconUrl="@TitleBarIconUrl" OnClickBarCallback="OnClickBar"></DockViewTitleBar>
}
@if (IsRender())
{
@ChildContent
}
</div>
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ public abstract class DockViewComponentBase : IdComponentBase, IDisposable
[JsonIgnore]
public RenderFragment? ChildContent { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 名称</para>
/// <para lang="en">Gets or sets the name</para>
/// </summary>
[Parameter]
public string? LayoutName { get; set; }

[CascadingParameter]
private List<DockViewComponentBase>? Parent { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,10 @@ class DockViewConfig
/// <para lang="en">Gets or sets the layout configuration. Default is null</para>
/// </summary>
public string? LayoutConfig { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 布局名称,默认为 null</para>
/// <para lang="en">Gets or sets the layout name. Default is null</para>
/// </summary>
public string? LayoutName { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ public partial class DockViewV2
[Parameter]
public DockViewTheme Theme { get; set; } = DockViewTheme.Light;

/// <summary>
/// <para lang="zh">获得/设置 布局名称,默认为 null</para>
/// <para lang="en">Gets or sets the layout name. Default is null</para>
/// </summary>
[Parameter]
public string? LayoutName { get; set; }

/// <summary>
/// 嵌套 DockView 时生效防止生成冗余的 DOM 结构
/// </summary>
Expand All @@ -179,6 +186,7 @@ public partial class DockViewV2
private DockViewOptions? _options = null;
private ConcurrentDictionary<string, DockViewComponentState> _componentStates = new();
private string? _layoutConfig;
private string? _layoutName;
private bool _disposed;

/// <summary>
Expand Down Expand Up @@ -224,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)
{
Expand Down Expand Up @@ -283,14 +297,47 @@ private DockViewConfig GetDockViewConfig()
LockChangedCallback = nameof(LockChangedCallbackAsync),
SplitterCallback = nameof(SplitterCallbackAsync),
SaveConfigCallback = nameof(SaveConfigCallbackAsync),
Contents = _components,
LoadTabs = nameof(LoadTabs)
Contents = GetLayoutContents(),
LoadTabs = nameof(LoadTabs),
LayoutName = LayoutName
};
}

/// <summary>
/// <para lang="zh">根据 <see cref="LayoutName"/> 在服务器端过滤出匹配的布局,避免将所有布局序列化到客户端</para>
/// <para lang="en">Filters the matching layout on the server side by <see cref="LayoutName"/> to avoid serializing all layouts to the client</para>
/// </summary>
private List<DockViewComponentBase> GetLayoutContents()
{
if (_components.Count == 0)
{
return [];
}

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;
private string? LocalStorageKey
{
get
{
if (!IsEnableLocalStorage)
{
return null;
}

var layoutSegment = string.IsNullOrEmpty(LayoutName) ? "" : $"-{LayoutName}";
return $"{GetPrefixKey()}-{Name}{layoutSegment}-{GetVersion()}";
}
}

private string GetVersion() => Version ?? _options.Version ?? "v1";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -58,17 +58,39 @@ const initDockviewFromConfig = (dockview, options) => {
else {
dockview.fromJSON(getConfigFromContent(options));
dockview.params.invisiblePanels = [];
dockview.params.floatingGroups = [];
}

syncLayoutToContainer(dockview);
}

const syncLayoutToContainer = dockview => {
const el = dockview.gridview?.element?.parentElement ?? dockview.element;
if (!el) return;
const width = el.clientWidth;
const height = el.clientHeight;
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)
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';
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 },
Expand Down Expand Up @@ -260,9 +282,6 @@ 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.
if (!dockview.width || !dockview.height) {
return;
}
Expand All @@ -271,10 +290,6 @@ 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.
let gridJson;
dockview.params.maximizing = true;
try {
Expand Down Expand Up @@ -341,6 +356,10 @@ const syncLayoutWithOptions = (layout, options, invisiblePanels = []) => {
if (p.params?.key) localIdByKey.set(p.params.key, p.id);
});

if (optionPanels.length === 0) {
return layout;
}

const matchCount = [...optionPanelByKey.keys()].filter(key => localIdByKey.has(key)).length;
if (matchCount === 0) {
return getConfigFromContent(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}"]`)
btnList.forEach(btn => btn.remove())
btnList?.forEach(btn => btn.remove())
}

const setDrawerTitle = group => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,10 @@ const updateTitle = panel => {
}
}

export const getRootContent = options => options.content.find(c => c.layoutName === options.layoutName) || options.content[0]
const getPanelsFromOptions = options => {
return getPanels(options.content[0], options)
const rootContent = getRootContent(options)
return rootContent ? getPanels(rootContent, options) : []
}

const getPanels = (contentItem, options, parent = {}, panels = []) => {
Expand Down
Loading
Loading