Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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.4-beta01</Version>
<Version>10.0.4</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System.Text.Json.Serialization;

namespace BootstrapBlazor.Components;

/// <summary>
/// DockContentItem 配置项子项对标 content 配置项内部 content 配置
/// </summary>
public partial class DockViewComponent
public class DockViewComponent : DockViewComponentBase
{
/// <summary>
/// 获得/设置 组件是否显示 Header 默认 true 显示
Expand Down Expand Up @@ -133,6 +134,10 @@ public partial class DockViewComponent
[JsonIgnore]
public Func<Task>? OnClickTitleBarCallback { get; set; }

[CascadingParameter]
[NotNull]
private DockViewV2? DockView { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand All @@ -143,6 +148,41 @@ protected override void OnInitialized()
Type = DockViewContentType.Component;
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="builder"></param>
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "div");
builder.AddAttribute(10, "id", Id);
builder.AddAttribute(20, "class", "bb-dockview-panel");
builder.AddAttribute(30, "data-bb-key", Key);
builder.AddAttribute(40, "data-bb-title", Title);

if (TitleTemplate != null)
{
builder.OpenElement(50, "div");
builder.AddAttribute(51, "class", "bb-dockview-item-title");
builder.AddContent(53, TitleTemplate);
builder.CloseElement();
}
else if (ShowTitleBar)
{
builder.OpenComponent<DockViewTitleBar>(60);
builder.AddAttribute(61, nameof(DockViewTitleBar.BarIcon), TitleBarIcon);
builder.AddAttribute(62, nameof(DockViewTitleBar.BarIconUrl), TitleBarIconUrl);
builder.AddAttribute(63, nameof(DockViewTitleBar.OnClickBarCallback), OnClickBar);
builder.CloseComponent();
}

if (DockView.ShowTab(Key))
{
builder.AddContent(70, ChildContent);
}
builder.CloseElement();
}

private async Task OnClickBar()
{
if (OnClickTitleBarCallback != null)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

Expand Down Expand Up @@ -77,6 +77,11 @@ class DockViewConfig
/// </summary>
public string? SplitterCallback { get; set; }

/// <summary>
/// 获得/设置 加载当前激活标签页事件回调
/// </summary>
public string? LoadTabs { get; set; }

/// <summary>
/// 获得/设置 客户端缓存键值
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

namespace BootstrapBlazor.Components;

/// <summary>
/// DockViewRenderMode 渲染模式枚举类型
/// <para lang="zh">DockViewRenderMode 渲染模式枚举类型</para>
/// <para lang="en">DockViewRenderMode render mode enumeration type</para>
/// </summary>
[JsonEnumConverter(true)]
public enum DockViewRenderMode
{
/// <summary>
/// 可见时渲染
/// <para lang="zh">可见时渲染</para>
/// <para lang="en">Render when visible</para>
/// </summary>
OnlyWhenVisible,

/// <summary>
/// 始终渲染
/// <para lang="zh">始终渲染</para>
/// <para lang="en">Always render</para>
/// </summary>
Always
Always,

/// <summary>
/// <para lang="zh">部分渲染 可见版面渲染 不可见版面异步渲染</para>
/// <para lang="en"></para>
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DockViewRenderMode.Partial has an empty English <para lang="en"> doc. Please fill it in to match the bilingual XML doc convention used for the rest of the enum values.

Suggested change
/// <para lang="en"></para>
/// <para lang="en">Partial rendering: render visible panels and asynchronously render invisible panels.</para>

Copilot uses AI. Check for mistakes.
/// </summary>
Partial
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@inherits BootstrapModuleComponentBase
@inherits BootstrapModuleComponentBase
@attribute [JSModuleAutoLoader("./_content/BootstrapBlazor.DockView/Components/DockViewV2.razor.js", JSObjectReference = true)]

<div @attributes="@AdditionalAttributes" id="@Id" class="@ClassString">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

Expand Down Expand Up @@ -201,7 +201,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
PanelVisibleChangedCallback = nameof(PanelVisibleChangedCallbackAsync),
LockChangedCallback = nameof(LockChangedCallbackAsync),
SplitterCallback = nameof(SplitterCallbackAsync),
Contents = _components
Contents = _components,
LoadTabs = nameof(LoadTabs)
};

private string GetVersion() => Version ?? _options.Version ?? "v1";
Expand Down Expand Up @@ -258,6 +259,41 @@ public async Task PanelVisibleChangedCallbackAsync(string title, bool status)
}
}

private HashSet<string> _loadTabs = new();

/// <summary>
///
/// </summary>
/// <param name="tabs"></param>
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML doc for LoadTabs is empty and tabs parameter is undocumented. Given this is a JS-invokable public method that affects rendering, please add a clear summary (ideally bilingual if that’s the convention in this component) describing when JS calls it and what tabs contains.

Suggested change
///
/// </summary>
/// <param name="tabs"></param>
/// 标签页加载回调方法,由 JavaScript 在激活标签变化或需重新渲染时调用,用于更新当前需要渲染的标签集合。
/// Tab loading callback invoked from JavaScript when the set of active/visible tabs changes, updating which tabs should be rendered.
/// </summary>
/// <param name="tabs">
/// 需要在服务器端渲染的标签标识列表,由客户端脚本传入。每一项为一个标签的唯一标识。
/// List of tab identifiers that should currently be rendered on the server, provided by client-side script. Each item represents a single tab id.
/// </param>

Copilot uses AI. Check for mistakes.
[JSInvokable]
public Task LoadTabs(List<string> tabs)
{
// 客户端请求渲染当前激活的标签
_loadTabs.Clear();
foreach (var tab in tabs)
{
_loadTabs.Add(tab);
}

StateHasChanged();
return Task.CompletedTask;
}

/// <summary>
/// 检查指定 Key 值 DockviewComponent 是否处于激活状态
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool ShowTab(string? key)
{
if (Renderer == DockViewRenderMode.Always)
{
return true;
}

return _loadTabs.Contains(key ?? string.Empty);
}

/// <summary>
/// 锁定回调方法 由 JavaScript 调用
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addLink, getTheme } from '../../BootstrapBlazor/modules/utility.js'
import { addLink, getTheme } from '../../BootstrapBlazor/modules/utility.js'
import { cerateDockview } from '../js/dockview-utils.js'
import Data from '../../BootstrapBlazor/modules/data.js'
import EventHandler from "../../BootstrapBlazor/modules/event-handler.js"
Expand Down Expand Up @@ -32,6 +32,9 @@ export async function init(id, invoke, options) {
dockview.on('groupSizeChanged', () => {
invoke.invokeMethodAsync(options.splitterCallback);
});
dockview.on('loadTabs', tabs => {
invoke.invokeMethodAsync(options.loadTabs, tabs);
});

EventHandler.on(document, 'changed.bb.theme', updateTheme);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ const getGroupNode = (contentItem, size, boxSize, parent, panels, getGroupId, op
title: item.title,
tabComponent: item.componentName,
contentComponent: item.componentName,
renderer: item.renderer || options.renderer,
// renderer: item.renderer || options.renderer,
params: { ...item, parentId: parent.id }
}
Comment on lines 278 to 283
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as in dockview-panel.js: commenting out renderer: item.renderer || options.renderer means panels created from layout/config won’t carry a renderer value, but the rest of the JS uses panel.renderer to decide how to manage DOM/content. This likely causes inconsistent behavior when reloading layouts. Please restore setting renderer (or set a default elsewhere).

Copilot uses AI. Check for mistakes.
return item.id
Expand All @@ -303,7 +303,7 @@ const getLeafNode = (contentItem, size, boxSize, parent, panels, getGroupId, opt
panels[contentItem.id] = {
id: contentItem.id,
title: contentItem.title,
renderer: contentItem.renderer || options.renderer,
// renderer: contentItem.renderer || options.renderer,
tabComponent: contentItem.componentName,
contentComponent: contentItem.componentName,
params: { ...contentItem, parentId: parent.id }
Comment on lines 303 to 309
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: renderer is commented out for leaf panels, which can leave renderer undefined and break renderer-mode logic. Please ensure renderer is always set (e.g., contentItem.renderer || options.renderer).

Copilot uses AI. Check for mistakes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { saveConfig } from "./dockview-config.js";
import { saveConfig } from "./dockview-config.js";
import { getIcon } from "./dockview-icon.js"
import { setDrawerTitle } from "./dockview-group.js"

Expand All @@ -23,6 +23,13 @@ const observePanelActiveChange = panel => {
moveAlwaysRenderPanel(panel)
}, 0)
})
panel.api.onDidVisibilityChange(({ isVisible }) => {
const dockview = panel.accessor;
if (dockview.params.options.renderer === 'onlyWhenVisible' && dockview._inited && isVisible) {
const visiblePanels = dockview.groups.map(g => g.panels.find(p => p.params.isActive) || g.panels.find(p => p.api.isVisible))
dockview._loadTabs?.fire(visiblePanels.filter(p => Boolean(p)).map(p => p.params.key));
}
})
}

const moveAlwaysRenderPanel = panel => {
Expand Down Expand Up @@ -136,7 +143,7 @@ const getPanels = (contentItem, options, parent = {}, panels = []) => {
id: contentItem.id,
groupId: contentItem.groupId,
title: contentItem.title,
renderer: contentItem.renderer || options.renderer,
// renderer: contentItem.renderer || options.renderer,
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderer is no longer populated when building the panels list from options.content, but other code relies on panel.renderer (e.g., filtering onlyWhenVisible panels and passing renderer into dockview.addPanel). With this commented out, new panels created from options will have renderer === undefined, which can break render-mode behavior. Consider restoring renderer: contentItem.renderer || options.renderer (or otherwise ensuring a default is always set).

Suggested change
// renderer: contentItem.renderer || options.renderer,
renderer: contentItem.renderer || options.renderer,

Copilot uses AI. Check for mistakes.
tabComponent: contentItem.componentName,
contentComponent: contentItem.componentName,
params: { ...contentItem, parentType: parent.type, parentId: parent.id }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DockviewComponent } from "./dockview-core.esm.js"
import { DockviewComponent } from "./dockview-core.esm.js"
import { DockviewPanelContent } from "./dockview-content.js"
import { onAddGroup, addGroupWithPanel, toggleLock, observeFloatingGroupLocationChange, observeOverlayChange, createDrawerHandle } from "./dockview-group.js"
import { onAddPanel, onRemovePanel, getPanelsFromOptions, findContentFromPanels } from "./dockview-panel.js"
Expand All @@ -8,6 +8,7 @@ import './dockview-extensions.js'
const cerateDockview = (el, options) => {
const theme = options.theme || "dockview-theme-light";
const template = el.querySelector('template');
options.renderer ??= 'onlyWhenVisible'; // onlyWhenVisible | partial | always
const dockview = new DockviewComponent(el, {
parentElement: el,
theme: {
Expand Down Expand Up @@ -86,10 +87,22 @@ const initDockview = (dockview, options, template) => {
dockview.onDidLayoutFromJSON(() => {
const handler = setTimeout(() => {
clearTimeout(handler);

const panels = dockview.panels
const delPanelsStr = localStorage.getItem(dockview.params.options.localStorageKey + '-panels')
const delPanels = delPanelsStr && JSON.parse(delPanelsStr) || []
const panels = dockview.panels;
const groups = dockview.groups;
const delPanelsStr = localStorage.getItem(dockview.params.options.localStorageKey + '-panels');
const delPanels = delPanelsStr && JSON.parse(delPanelsStr) || [];
if (options.renderer === 'always') {

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options.renderer === 'always' branch is empty. This conditional can be removed (and let the later branches handle the other modes) or implemented explicitly; leaving an empty branch makes the initialization logic harder to follow and suggests incomplete behavior.

Suggested change
if (dockview.panels.length > 0) {
dockview._loadTabs?.fire(dockview.panels.map(p => p.params.key));
}

Copilot uses AI. Check for mistakes.
}
else if (options.renderer === 'partial' || 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 => Boolean(p)).map(p => p.params.key));
}
if (options.renderer === 'partial') {
if (dockview.panels.length > 0) {
dockview._loadTabs?.fire(dockview.panels.map(p => p.params.key));
}
}
panels.forEach(panel => {
const visible = panel.params.visible
if (!visible) {
Expand Down Expand Up @@ -127,7 +140,7 @@ const initDockview = (dockview, options, template) => {
dockview.groups.forEach(group => {
observeGroup(group)
})
dockview.element.querySelector('&>.dv-dockview>.dv-branch-node').addEventListener('click', function (e) {
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')
})
Expand Down
Loading