diff --git a/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj b/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj index 99648844..b3fe2dd0 100644 --- a/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj +++ b/src/components/BootstrapBlazor.DockView/BootstrapBlazor.DockView.csproj @@ -1,7 +1,7 @@  - 10.0.7 + 10.0.8-beta05 diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor index c6500815..573cc056 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor @@ -11,7 +11,7 @@ { } - @if (Visible) + @if (IsRender()) { @ChildContent } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor.cs index bfc7563a..663459a8 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponent.razor.cs @@ -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/ @@ -8,131 +8,152 @@ namespace BootstrapBlazor.Components; /// -/// DockContentItem 配置项子项对标 content 配置项内部 content 配置 +/// DockView 组件配置项,对应 content 配置项中的组件项 +/// DockView component option corresponding to a component item in the content configuration /// public partial class DockViewComponent { /// - /// 获得/设置 组件是否显示 Header 默认 true 显示 + /// 获得/设置 组件是否显示标题栏,默认为 true + /// Gets or sets whether the component header is displayed. Default is true /// [Parameter] public bool ShowHeader { get; set; } = true; /// - /// 获得/设置 组件 Title + /// 获得/设置 组件标题 + /// Gets or sets the component title /// [Parameter] public string? Title { get; set; } /// - /// 获得/设置 组件 Title 宽度 默认 null 未设置 + /// 获得/设置 组件标题宽度,默认为 null + /// Gets or sets the component title width. Default is null /// [Parameter] public int? TitleWidth { get; set; } /// - /// 获得/设置 组件 Title 样式 默认 null 未设置 + /// 获得/设置 组件标题样式类,默认为 null + /// Gets or sets the component title CSS class. Default is null /// [Parameter] public string? TitleClass { get; set; } /// - /// 获得/设置 Title 模板 默认 null 未设置 + /// 获得/设置 标题模板,默认为 null + /// Gets or sets the title template. Default is null /// [Parameter] - [JsonIgnore] public RenderFragment? TitleTemplate { get; set; } /// - /// 获得/设置 组件 Class 默认 null 未设置 + /// 获得/设置 组件样式类,默认为 null + /// Gets or sets the component CSS class. Default is null /// [Parameter] public string? Class { get; set; } /// - /// 获得/设置 组件是否可见 默认 true 可见 + /// 获得/设置 组件是否可见,默认为 true + /// Gets or sets whether the component is visible. Default is true /// [Parameter] public bool Visible { get; set; } = true; /// - /// 获得/设置 组件是否允许关闭 默认 null 使用 DockView 的配置 + /// 获得/设置 组件是否允许关闭,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets whether the component can be closed. Default is null. When not set, the DockView configuration is used /// [Parameter] public bool? ShowClose { get; set; } /// - /// 获得/设置 组件唯一标识值 默认 null 未设置时取 Title 作为唯一标识 + /// 获得/设置 组件唯一标识,默认为 null,未设置时使用 Title 作为唯一标识 + /// Gets or sets the unique component identifier. Default is null. When not set, Title is used as the unique identifier /// [Parameter] public string? Key { get; set; } /// - /// 获得/设置 是否锁定 默认 null 未设置时取 DockView 的配置 + /// 获得/设置 组件是否锁定,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets whether the component is locked. Default is null. When not set, the DockView configuration is used /// - /// 锁定后无法拖动 [Parameter] public bool? IsLock { get; set; } /// - /// 获得/设置 是否显示锁定按钮 默认 null 未设置时取 DockView 的配置 + /// 获得/设置 是否显示锁定按钮,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets whether the lock button is displayed. Default is null. When not set, the DockView configuration is used /// [Parameter] public bool? ShowLock { get; set; } /// - /// 获得/设置 是否悬浮 默认 null 未设置时取 DockView 的配置 + /// 获得/设置 是否悬浮,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets whether the component is floating. Default is null. When not set, the DockView configuration is used /// [Parameter] public bool? IsFloating { get; set; } /// - /// 获得/设置 是否显示可悬浮按钮 默认 null 未设置时取 DockView 的配置 + /// 获得/设置 是否显示悬浮按钮,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets whether the float button is displayed. Default is null. When not set, the DockView configuration is used /// [Parameter] public bool? ShowFloat { get; set; } /// - /// 获得/设置 是否显示最大化按钮 默认 null 未设置时取 DockView 的配置 + /// 获得/设置 是否显示最大化按钮,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets whether the maximize button is displayed. Default is null. When not set, the DockView configuration is used /// [Parameter] public bool? ShowMaximize { get; set; } /// - /// 获得/设置 是否一直显示 默认 null 未设置时取 DockView 的配置 + /// 获得/设置 组件渲染模式,默认为 null,未设置时使用 DockView 的配置 + /// Gets or sets the component render mode. Default is null. When not set, the DockView configuration is used /// [Parameter] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Renderer { get; set; } /// - /// 获得/设置 是否显示标题前置图标 默认 false 不显示 + /// 获得/设置 是否显示标题前置图标,默认为 false + /// Gets or sets whether the leading title icon is displayed. Default is false /// [Parameter] [JsonIgnore] public bool ShowTitleBar { get; set; } /// - /// 获得/设置 标题前置图标 默认 null 未设置使用默认图标 + /// 获得/设置 标题前置图标,默认为 null,未设置时使用默认图标 + /// Gets or sets the leading title icon. Default is null. When not set, the default icon is used /// [Parameter] [JsonIgnore] public string? TitleBarIcon { get; set; } /// - /// 获得/设置 标题前置图标 Url 默认 null 未设置使用默认图标 + /// 获得/设置 标题前置图标地址,默认为 null,未设置时使用默认图标 + /// Gets or sets the leading title icon URL. Default is null. When not set, the default icon is used /// [Parameter] [JsonIgnore] public string? TitleBarIconUrl { get; set; } /// - /// 获得/设置 标题前置图标点击回调方法 默认 null + /// 获得/设置 标题前置图标点击回调方法,默认为 null + /// Gets or sets the click callback for the leading title icon. Default is null /// [Parameter] [JsonIgnore] public Func? OnClickTitleBarCallback { get; set; } + [CascadingParameter] + [NotNull] + private DockViewV2? DockView { get; set; } + /// /// /// @@ -141,6 +162,20 @@ protected override void OnInitialized() base.OnInitialized(); Type = DockViewContentType.Component; + + // 增加组件状态用于序列化 + DockView?.AddComponentState(new DockViewComponentState(this) { Key = Key }); + } + + /// + /// + /// + protected override void OnParametersSet() + { + base.OnParametersSet(); + + // 同步组件状态到缓存 + DockView.UpdateComponentState(Key, Visible, IsLock); } private async Task OnClickBar() @@ -151,12 +186,20 @@ private async Task OnClickBar() } } + private bool IsRender() => DockView.IsRender(Key); + + internal void SetVisible(bool visible) => Visible = visible; + + internal void SetLock(bool isLock) => IsLock = isLock; + /// - /// 设置 Visible 参数方法 + /// /// - /// - public void SetVisible(bool visible) + /// + protected override void Dispose(bool disposing) { - Visible = visible; + base.Dispose(disposing); + + DockView.RemoveComponentState(Key); } } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs index f98d1e9b..c28150be 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewComponentBase.cs @@ -8,44 +8,40 @@ namespace BootstrapBlazor.Components; /// -/// DockComponent 基类 -/// Base class for DockComponent +/// DockView 组件基类 +/// Base class for DockView components /// public abstract class DockViewComponentBase : IdComponentBase, IDisposable { /// - /// 获得/设置 渲染类型 默认 Component - /// Gets or sets the render type. Default is Component. + /// 获得/设置 组件渲染类型,默认为 Component + /// Gets or sets the component render type. Default is Component /// [Parameter] public DockViewContentType Type { get; set; } /// - /// 获得/设置 组件宽度百分比 默认 null 未设置 - /// Gets or sets the component width percentage. Default is null (not set). + /// 获得/设置 组件宽度百分比,默认为 null + /// Gets or sets the component width percentage. Default is null /// [Parameter] public int? Width { get; set; } /// - /// 获得/设置 组件高度百分比 默认 null 未设置 - /// Gets or sets the component height percentage. Default is null (not set). + /// 获得/设置 组件高度百分比,默认为 null + /// Gets or sets the component height percentage. Default is null /// [Parameter] public int? Height { get; set; } /// - /// 获得/设置 子组件 - /// Gets or sets the child content. + /// 获得/设置 子组件内容 + /// Gets or sets the child content /// [Parameter] [JsonIgnore] public RenderFragment? ChildContent { get; set; } - /// - /// 获得/设置 DockContent 实例 - /// Gets or sets the DockContent instance. - /// [CascadingParameter] private List? Parent { get; set; } @@ -60,8 +56,8 @@ protected override void OnInitialized() } /// - /// 资源销毁方法 - /// Resource disposal method + /// 资源释放方法 + /// Releases resources /// /// protected virtual void Dispose(bool disposing) @@ -73,8 +69,8 @@ protected virtual void Dispose(bool disposing) } /// - /// 资源销毁方法 - /// Resource disposal method + /// 资源释放方法 + /// Releases resources /// public void Dispose() { diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs index 963fc419..4605efd7 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewConfig.cs @@ -6,119 +6,121 @@ namespace BootstrapBlazor.Components; +/// +/// DockView 配置项 +/// DockView configuration options +/// class DockViewConfig { /// - /// 获得/设置 是否启用本地布局保持 默认 true - /// Gets or sets whether to enable local layout persistence. Default is true. + /// 获得/设置 是否启用本地布局持久化,默认为 true + /// Gets or sets whether local layout persistence is enabled. Default is true /// public bool EnableLocalStorage { get; set; } = true; /// - /// 获得/设置 是否锁定 默认 false - /// Gets or sets whether the component is locked. Default is false. + /// 获得/设置 是否锁定,默认为 false + /// Gets or sets whether the component is locked. Default is false /// - /// 锁定后无法拖动 [JsonPropertyName("lock")] public bool IsLock { get; set; } /// - /// 获得/设置 是否显示锁定按钮 默认 true 显示 - /// Gets or sets whether the lock button is displayed. Default is true. + /// 获得/设置 是否显示锁定按钮,默认为 true + /// Gets or sets whether the lock button is displayed. Default is true /// public bool ShowLock { get; set; } /// - /// 获得/设置 是否悬浮 默认 false - /// Gets or sets whether the component is floating. Default is false. + /// 获得/设置 是否悬浮,默认为 false + /// Gets or sets whether the component is floating. Default is false /// - /// 锁定后无法拖动 public bool IsFloating { get; set; } /// - /// 获得/设置 是否显示可悬浮按钮 默认 true - /// Gets or sets whether the float button is displayed. Default is true. + /// 获得/设置 是否显示悬浮按钮,默认为 true + /// Gets or sets whether the float button is displayed. Default is true /// public bool ShowFloat { get; set; } = true; /// - /// 获得/设置 是否显示关闭按钮 默认 true 显示 - /// Gets or sets whether the close button is displayed. Default is true. + /// 获得/设置 是否显示关闭按钮,默认为 true + /// Gets or sets whether the close button is displayed. Default is true /// public bool ShowClose { get; set; } /// - /// 获得/设置 是否显示图钉按钮 默认 true - /// Gets or sets whether the pin button is displayed. Default is true. + /// 获得/设置 是否显示图钉按钮,默认为 true + /// Gets or sets whether the pin button is displayed. Default is true /// public bool ShowPin { get; set; } = true; /// - /// 获得/设置 是否显示最大化按钮 默认 true - /// Gets or sets whether the maximize button is displayed. Default is true. + /// 获得/设置 是否显示最大化按钮,默认为 true + /// Gets or sets whether the maximize button is displayed. Default is true /// public bool ShowMaximize { get; set; } = true; /// - /// 获得/设置 客户端渲染模式 默认 null 客户端默认使用 always onlyWhenVisible 值 - /// Gets or sets the client render mode. Default is null, the client will use always onlyWhenVisible value. + /// 获得/设置 客户端渲染模式 + /// Gets or sets the client render mode /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public DockViewRenderMode Renderer { get; set; } /// - /// 获得/设置 标签页可见状态改变事件回调 - /// Gets or sets the callback for when the tab visibility changes. + /// 获得/设置 标签页可见状态变更回调名称 + /// Gets or sets the callback name for tab visibility state changes /// public string? PanelVisibleChangedCallback { get; set; } /// - /// 获得/设置 组件初始化完成事件回调 - /// Gets or sets the callback for when the component is initialized. + /// 获得/设置 组件初始化完成回调名称 + /// Gets or sets the callback name for component initialization completion /// public string? InitializedCallback { get; set; } /// - /// 获得/设置 锁定事件回调 - /// Gets or sets the callback for when the lock state changes. + /// 获得/设置 锁定状态变更回调名称 + /// Gets or sets the callback name for lock state changes /// public string? LockChangedCallback { get; set; } /// - /// 获得/设置 分割栏调整事件回调 - /// Gets or sets the callback for when the splitter is adjusted. + /// 获得/设置 分割器调整回调名称 + /// Gets or sets the callback name for splitter adjustments /// public string? SplitterCallback { get; set; } /// - /// 获得/设置 加载当前激活标签页事件回调 - /// Gets or sets the callback for loading the currently active tab. + /// 获得/设置 加载标签页回调名称 + /// Gets or sets the callback name for loading tabs /// public string? LoadTabs { get; set; } /// - /// 获得/设置 客户端缓存键值 - /// Gets or sets the client-side cache key. + /// 获得/设置 客户端缓存键 + /// Gets or sets the client-side cache key /// public string? LocalStorageKey { get; set; } /// - /// 获得/设置 配置项集合 默认 空集合 - /// Gets or sets the configuration items. Default is an empty collection. + /// 获得/设置 配置项集合,默认为空集合 + /// Gets or sets the configuration items. Default is an empty collection /// [JsonPropertyName("content")] [JsonConverter(typeof(DockViewComponentConverter))] public List Contents { get; set; } = []; /// - /// 获得/设置 组件主题 默认 null 未设置 - /// Gets or sets the component theme. Default is null, not set. + /// 获得/设置 组件主题,默认为 null + /// Gets or sets the component theme. Default is null /// public string? Theme { get; set; } /// - /// 获得/设置 布局配置 默认 null 未设置 - /// Gets or sets the layout configuration. Default is null, not set. + /// 获得/设置 布局配置,默认为 null + /// Gets or sets the layout configuration. Default is null /// public string? LayoutConfig { get; set; } } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewContent.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewContent.cs index a9842c17..dba516f6 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewContent.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewContent.cs @@ -10,13 +10,13 @@ namespace BootstrapBlazor.Components; /// /// DockContent 类对标 content 配置项 -/// DockContent class corresponds to the content configuration item. +/// DockContent class corresponds to the content configuration item /// public class DockViewContent : DockViewComponentBase { /// /// 获得/设置 子项集合 - /// Gets or sets the collection of child items. + /// Gets or sets the collection of child items /// [JsonConverter(typeof(DockViewComponentConverter))] [JsonPropertyName("content")] diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewDropdownIcon.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewDropdownIcon.razor.cs index 25e388ca..3ccbc53d 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewDropdownIcon.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewDropdownIcon.razor.cs @@ -10,10 +10,6 @@ namespace BootstrapBlazor.Components; /// public partial class DockViewDropdownIcon { - /// - /// 获得 样式字符串 - /// Gets the CSS class string. - /// private string? ClassString => CssBuilder.Default("dropdown dropdown-center bb-dockview-control-icon") .AddClass($"bb-dockview-control-icon-{IconName}") .Build(); diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewIcon.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewIcon.razor.cs index 665c651e..c224c227 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewIcon.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewIcon.razor.cs @@ -15,30 +15,25 @@ public partial class DockViewIcon { /// /// 获得/设置 资源文件接口实例 - /// Gets or sets the resource file interface instance. + /// Gets or sets the resource file interface instance /// [Inject, NotNull] protected IStringLocalizer? Localizer { get; set; } /// /// 获得/设置 图标名称 - /// Gets or sets the icon name. + /// Gets or sets the icon name /// [Parameter, NotNull] [EditorRequired] public string? IconName { get; set; } - - /// - /// 获得 样式字符串 - /// Gets the CSS class string. - /// private string? ClassString => CssBuilder.Default("bb-dockview-control-icon") .AddClass($"bb-dockview-control-icon-{IconName}") .Build(); /// /// 获得 图标地址 - /// Gets the icon URL. + /// Gets the icon URL /// protected string Href => $"./_content/BootstrapBlazor.DockView/icon/dockview.svg#{IconName}"; diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewOptions.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewOptions.cs index 191cd049..5046d00d 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewOptions.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewOptions.cs @@ -5,22 +5,26 @@ namespace BootstrapBlazor.Components; /// -/// DockView 组件配置类 +/// DockView 组件配置项 +/// DockView component options /// class DockViewOptions { /// - /// 获得/设置 组件本地化版本信息 + /// 获得/设置 组件版本信息 + /// Gets or sets the component version information /// public string? Version { get; set; } /// - /// 获得/设置 是否开启本地存储 默认 null 未设置 + /// 获得/设置 是否启用本地存储,默认为 null + /// Gets or sets whether local storage is enabled. Default is null /// public bool? EnableLocalStorage { get; set; } /// - /// 获得/设置 本地存储前缀 默认 bb-dock + /// 获得/设置 本地存储前缀,默认为 bb-dock + /// Gets or sets the local storage prefix. Default is bb-dock /// public string? LocalStoragePrefix { get; set; } } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewRenderMode.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewRenderMode.cs index 6469a958..40d25778 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewRenderMode.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewRenderMode.cs @@ -21,11 +21,5 @@ public enum DockViewRenderMode /// 始终渲染 /// Always render /// - Always, - - /// - /// 部分渲染 可见版面渲染 不可见版面异步渲染 - /// - /// - Partial + Always } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewTitleBar.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewTitleBar.razor.cs index d6dab5f6..a0f1c9a0 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewTitleBar.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewTitleBar.razor.cs @@ -7,24 +7,28 @@ namespace BootstrapBlazor.Components; /// -/// DockViewTitle 组件 +/// DockView 标题栏组件 +/// DockView title bar component /// public partial class DockViewTitleBar { /// - /// 获得/设置 标题前置图标点击回调方法 默认 null + /// 获得/设置 标题前置图标点击回调方法,默认为 null + /// Gets or sets the click callback for the leading title icon. Default is null /// [Parameter] public Func? OnClickBarCallback { get; set; } /// - /// 获得/设置 标题前置图标 默认 null 未设置使用默认图标 + /// 获得/设置 标题前置图标,默认为 null,未设置时使用默认图标 + /// Gets or sets the leading title icon. Default is null. When not set, the default icon is used /// [Parameter] public string? BarIcon { get; set; } /// - /// 获得/设置 标题前置图标 Url 默认 null 未设置使用默认图标 + /// 获得/设置 标题前置图标地址,默认为 null,未设置时使用默认图标 + /// Gets or sets the leading title icon URL. Default is null. When not set, the default icon is used /// [Parameter] public string? BarIconUrl { get; set; } diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs index 6130b6e0..84a0bcce 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.cs @@ -4,17 +4,19 @@ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Configuration; +using System.Collections.Concurrent; namespace BootstrapBlazor.Components; /// -/// DockViewV2 组件 +/// DockViewV2 组件 +/// DockViewV2 component /// public partial class DockViewV2 : IDisposable { /// - /// 获得/设置 DockView 名称 默认 null 用于本地存储识别 - /// Gets or sets the DockView name. Default is null, used for local storage identification. + /// 获得/设置 DockView 名称,默认为 null,用于本地存储标识 + /// Gets or sets the DockView name. Default is null and it is used for local storage identification /// [Parameter] [EditorRequired] @@ -23,132 +25,133 @@ public partial class DockViewV2 : IDisposable /// /// 获得/设置 布局配置 - /// Gets or sets the layout configuration. + /// Gets or sets the layout configuration /// [Parameter] public string? LayoutConfig { get; set; } /// - /// 获得/设置 是否显示关闭按钮 默认为 true - /// Gets or sets whether to show the close button. Default is true. + /// 获得/设置 是否显示关闭按钮,默认为 true + /// Gets or sets whether the close button is displayed. Default is true /// [Parameter] public bool ShowClose { get; set; } = true; /// - /// 获得/设置 是否锁定 默认 false - /// Gets or sets whether to lock. Default is false. + /// 获得/设置 是否锁定,默认为 false + /// Gets or sets whether the component is locked. Default is false /// - /// 锁定后无法拖动 [Parameter] public bool IsLock { get; set; } /// - /// 获得/设置 是否显示锁定按钮 默认 true - /// Gets or sets whether to show the lock button. Default is true. + /// 获得/设置 是否显示锁定按钮,默认为 true + /// Gets or sets whether the lock button is displayed. Default is true /// [Parameter] public bool ShowLock { get; set; } = true; /// - /// 获得/设置 是否显示最大化按钮 默认 true - /// Gets or sets whether to show the maximize button. Default is true. + /// 获得/设置 是否显示最大化按钮,默认为 true + /// Gets or sets whether the maximize button is displayed. Default is true /// [Parameter] public bool ShowMaximize { get; set; } = true; /// - /// 获得/设置 是否悬浮 默认 false - /// Gets or sets whether to float. Default is false. + /// 获得/设置 是否悬浮,默认为 false + /// Gets or sets whether the component is floating. Default is false /// [Parameter] public bool IsFloating { get; set; } /// - /// 获得/设置 是否显示可悬浮按钮 默认 true - /// Gets or sets whether to show the float button. Default is true. + /// 获得/设置 是否显示悬浮按钮,默认为 true + /// Gets or sets whether the float button is displayed. Default is true /// [Parameter] public bool ShowFloat { get; set; } = true; /// - /// 获得/设置 是否显示显示图钉按钮 默认 true - /// Gets or sets whether to show the pin button. Default is true. + /// 获得/设置 是否显示图钉按钮,默认为 true + /// Gets or sets whether the pin button is displayed. Default is true /// [Parameter] public bool ShowPin { get; set; } = true; /// - /// 获得/设置 客户端渲染模式 默认 客户端默认使用 always onlyWhenVisible 值 - /// Gets or sets the client render mode. Default is . The client defaults to using always onlyWhenVisible values. + /// 获得/设置 客户端渲染模式,默认为 + /// Gets or sets the client render mode. Default is /// [Parameter] public DockViewRenderMode Renderer { get; set; } /// - /// 获得/设置 锁定状态回调此方法 - /// Gets or sets the callback method for lock state changes. + /// 获得/设置 锁定状态变更回调方法 + /// Gets or sets the callback for lock state changes /// [Parameter] public Func? OnLockChangedCallbackAsync { get; set; } /// - /// 获得/设置 标签关闭时回调此方法 - /// Gets or sets the callback method for when a tab is closed. + /// 获得/设置 标签页可见状态变更回调方法 + /// Gets or sets the callback for tab visibility state changes /// - /// 可用于第三方组件显示标签页状态更新 [Parameter] public Func? OnVisibleStateChangedAsync { get; set; } /// - /// 获得/设置 标签页调整大小完成时回调此方法 - /// Gets or sets the callback method for when a tab is resized. + /// 获得/设置 标签页调整大小完成回调方法 + /// Gets or sets the callback for when tab resizing is completed /// [Parameter] public Func? OnSplitterCallbackAsync { get; set; } /// - /// 获得/设置 客户端组件脚本初始化完成后回调此方法 - /// Gets or sets the callback method for when the client component script initialization is complete. + /// 获得/设置 客户端组件脚本初始化完成回调方法 + /// Gets or sets the callback for when client component script initialization is complete /// [Parameter] public Func? OnInitializedCallbackAsync { get; set; } /// - /// 获得/设置 子组件 - /// Gets or sets the child components. + /// 获得/设置 子组件内容 + /// Gets or sets the child content /// [Parameter] public RenderFragment? ChildContent { get; set; } /// - /// 获得/设置 版本设置 默认 null 未设置 用于本地配置 可通过全局统一配置 - /// Gets or sets the version. Default is null. Used for local configuration and can be configured globally. + /// 获得/设置 版本信息,默认为 null,未设置时可通过全局配置提供 + /// Gets or sets the version information. Default is null. When not set, it can be provided by global configuration /// [Parameter] public string? Version { get; set; } /// - /// 获得/设置 是否启用本地存储布局 默认 null 未设置 - /// Gets or sets whether to enable local storage layout. Default is null. Not set. + /// 获得/设置 是否启用本地存储布局,默认为 null + /// Gets or sets whether local storage layout is enabled. Default is null /// [Parameter] public bool? EnableLocalStorage { get; set; } /// - /// 获得/设置 本地存储前缀 默认 bb-dock - /// Gets or sets the local storage prefix. Default is bb-dock. + /// 获得/设置 本地存储前缀,默认为 bb-dock + /// Gets or sets the local storage prefix. Default is bb-dock /// [Parameter] public string? LocalStoragePrefix { get; set; } /// - /// 获得/设置 DockView 组件主题 默认 Light - /// Gets or sets the DockView component theme. Default is Light. + /// 获得/设置 DockView 组件主题,默认为 Light + /// Gets or sets the DockView component theme. Default is Light /// [Parameter] public DockViewTheme Theme { get; set; } = DockViewTheme.Light; + /// + /// 嵌套 DockView 时生效防止生成冗余的 DOM 结构 + /// [CascadingParameter] private DockViewV2? DockView { get; set; } @@ -168,6 +171,7 @@ public partial class DockViewV2 : IDisposable [NotNull] private DockViewOptions? _options = null; + private ConcurrentDictionary _componentStates = new(); /// /// @@ -186,7 +190,6 @@ protected override void OnInitialized() /// /// /// - /// protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); @@ -230,9 +233,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) /// /// 重置为默认布局 - /// Resets to the default layout. + /// Resets to the default layout /// - /// public async Task Reset(string? layoutConfig = null) { var options = GetOptions(); @@ -244,11 +246,10 @@ public async Task Reset(string? layoutConfig = null) } /// - /// 获得当前布局 Json 字符串 - /// Gets the current layout JSON string. + /// 获得 当前布局 JSON 字符串 + /// Gets the current layout JSON string /// - /// - public Task SaveLayout() => InvokeAsync("save", Id); + public Task SaveLayout() => InvokeAsync("save", Id); private Task OnThemeChangedAsync(string themeName) { @@ -257,8 +258,8 @@ private Task OnThemeChangedAsync(string themeName) } /// - /// 标签页关闭回调方法 由 JavaScript 调用 - /// Tab close callback method called by JavaScript + /// 初始化完成回调方法,由 JavaScript 调用 + /// Initialization callback method called by JavaScript /// [JSInvokable] public async Task InitializedCallbackAsync() @@ -270,79 +271,146 @@ public async Task InitializedCallbackAsync() } /// - /// 标签页关闭回调方法 由 JavaScript 调用 - /// Tab close callback method called by JavaScript + /// 标签页可见状态变更回调方法,由 JavaScript 调用 + /// Tab visibility state change callback method called by JavaScript /// [JSInvokable] - public async Task PanelVisibleChangedCallbackAsync(string title, bool status) + public async Task PanelVisibleChangedCallbackAsync(string key, bool status) { + // 同步更新组件可见状态 + if (_componentStates.TryGetValue(key, out var state)) + { + state.Visible = status; + + // 同步可见状态到组件实例 + state.Component.SetVisible(status); + } + + // 通知订阅者 if (OnVisibleStateChangedAsync != null) { - await OnVisibleStateChangedAsync(title, status); + await OnVisibleStateChangedAsync(key, status); + } + } + + /// + /// 锁定状态变更回调方法,由 JavaScript 调用 + /// Lock state change callback method called by JavaScript + /// + [JSInvokable] + public async Task LockChangedCallbackAsync(string[] panels, bool locked) + { + // 同步更新组件锁定状态 + foreach (var panel in panels) + { + if (_componentStates.TryGetValue(panel, out var state)) + { + state.IsLock = locked; + + // 同步可见状态到组件实例 + state.Component.SetLock(locked); + } + } + + // 通知订阅者 + if (OnLockChangedCallbackAsync != null) + { + await OnLockChangedCallbackAsync(panels, locked); + } + } + + /// + /// 分割器回调方法,由 JavaScript 调用 + /// Splitter callback method called by JavaScript + /// + [JSInvokable] + public async Task SplitterCallbackAsync() + { + if (OnSplitterCallbackAsync != null) + { + await OnSplitterCallbackAsync(); } } private HashSet _loadTabs = new(); /// - /// 加载指定的标签页 由 JavaScript 调用 - /// Loads the specified tabs called by JavaScript + /// 加载指定标签页的方法,由 JavaScript 调用 + /// Method that loads the specified tabs, called by JavaScript /// - /// [JSInvokable] public Task LoadTabs(List tabs) { - // 客户端请求渲染当前激活的标签 - _loadTabs.Clear(); - foreach (var tab in tabs) + // 注意渲染方式 DockViewRenderMode 为 DockViewRenderMode.OnlyWhenVisible 时此逻辑生效 + _loadTabs = tabs.ToHashSet(); + foreach (var componnet in _componentStates) { - _loadTabs.Add(tab); + // 标记是否渲染 + componnet.Value.Render = tabs.Contains(componnet.Key); } - StateHasChanged(); + return Task.CompletedTask; } - /// - /// 检查指定 Key 值 DockviewComponent 是否处于激活状态 - /// Checks whether the DockviewComponent with the specified key is active. - /// - /// - public bool ShowTab(string? key) + internal void AddComponentState(DockViewComponentState state) { - // TODO: Partial 模式下使用临时回滚稍后完善 if (Renderer == DockViewRenderMode.Always) { - return true; + return; } - return _loadTabs.Contains(key ?? string.Empty); + if (!string.IsNullOrEmpty(state.Key)) + { + _componentStates.TryAdd(state.Key, state); + } } - /// - /// 锁定回调方法 由 JavaScript 调用 - /// Lock callback method called by JavaScript - /// - [JSInvokable] - public async Task LockChangedCallbackAsync(string[] panels, bool state) + internal void RemoveComponentState(string? key) { - if (OnLockChangedCallbackAsync != null) + if (Renderer == DockViewRenderMode.Always) + { + return; + } + + if (!string.IsNullOrEmpty(key)) { - await OnLockChangedCallbackAsync(panels, state); + _componentStates.TryRemove(key, out _); } } - /// - /// 分割器回调方法 由 JavaScript 调用 - /// Splitter callback method called by JavaScript - /// - [JSInvokable] - public async Task SplitterCallbackAsync() + internal void UpdateComponentState(string? key, bool visible, bool? isLock) { - if (OnSplitterCallbackAsync != null) + if (Renderer == DockViewRenderMode.Always) { - await OnSplitterCallbackAsync(); + return; + } + + if (!string.IsNullOrEmpty(key) && _componentStates.TryGetValue(key, out var state)) + { + state.Visible = visible; + state.IsLock = isLock; + } + } + + internal bool IsRender(string? key) => Renderer switch + { + DockViewRenderMode.OnlyWhenVisible => GetComponentState(key)?.IsRender() ?? false, + _ => true + }; + + private DockViewComponentState? GetComponentState(string? key) + { + DockViewComponentState? state = null; + if (Renderer == DockViewRenderMode.OnlyWhenVisible) + { + if (!string.IsNullOrEmpty(key) && _componentStates.TryGetValue(key, out var _state)) + { + state = _state; + } } + + return state; } private void Dispose(bool disposing) diff --git a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js index e6d04dab..8df2b141 100644 --- a/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js +++ b/src/components/BootstrapBlazor.DockView/Components/DockViewV2.razor.js @@ -26,14 +26,14 @@ export async function init(id, invoke, options) { dockview.on('lockChanged', ({ title, isLock }) => { invoke.invokeMethodAsync(options.lockChangedCallback, title, isLock); }); - dockview.on('panelVisibleChanged', ({ title, status }) => { - invoke.invokeMethodAsync(options.panelVisibleChangedCallback, title, status); + dockview.on('panelVisibleChanged', ({ key, status }) => { + invoke.invokeMethodAsync(options.panelVisibleChangedCallback, key, status); }); dockview.on('groupSizeChanged', () => { invoke.invokeMethodAsync(options.splitterCallback); }); dockview.on('loadTabs', tabs => { - + invoke.invokeMethodAsync(options.loadTabs, tabs); }); EventHandler.on(document, 'changed.bb.theme', updateTheme); diff --git a/src/components/BootstrapBlazor.DockView/Converters/DockViewComponentConverter.cs b/src/components/BootstrapBlazor.DockView/Converters/DockViewComponentConverter.cs index 202b2a31..9c79847d 100644 --- a/src/components/BootstrapBlazor.DockView/Converters/DockViewComponentConverter.cs +++ b/src/components/BootstrapBlazor.DockView/Converters/DockViewComponentConverter.cs @@ -8,7 +8,7 @@ namespace BootstrapBlazor.Components; /// -/// DockViewComponent 转化器 +/// DockViewComponent 转换器 /// DockViewComponent converter /// class DockViewComponentConverter : JsonConverter> @@ -19,7 +19,6 @@ class DockViewComponentConverter : JsonConverter> /// /// /// - /// /// public override List? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/components/BootstrapBlazor.DockView/Data/DockViewComponentState.cs b/src/components/BootstrapBlazor.DockView/Data/DockViewComponentState.cs new file mode 100644 index 00000000..1fbf0120 --- /dev/null +++ b/src/components/BootstrapBlazor.DockView/Data/DockViewComponentState.cs @@ -0,0 +1,46 @@ +// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). 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 + +namespace BootstrapBlazor.Components; + +/// +/// DockView 组件状态 +/// DockView component state +/// +class DockViewComponentState(DockViewComponent component) +{ + /// + /// 获得/设置 组件唯一标识,默认为 null + /// Gets or sets the unique component identifier. Default is null + /// + public string? Key { get; set; } + + /// + /// 获得/设置 组件是否锁定,默认为 false + /// Gets or sets whether the component is locked. Default is false + /// + public bool? IsLock { get; set; } + + /// + /// 获得/设置 组件是否可见,默认为 true + /// Gets or sets whether the component is visible. Default is true + /// + public bool Visible { get; set; } = true; + + /// + /// 获得/设置 组件内容是否渲染,默认为 false + /// Gets or sets whether the component is rendered. Default is false + /// + /// + /// 组件内容是否渲染,默认为 false,只有当组件可见时并且当前状态为 Active 时才会渲染组件内容 + /// Whether the component content is rendered. Default is false. The content is only rendered when the component is visible and the current state is Active. + /// + public bool Render { get; set; } + + /// + /// 获得/设置 组件实例,默认为 null + /// Gets or sets the component instance. Default is null + /// + public DockViewComponent Component => component; +} diff --git a/src/components/BootstrapBlazor.DockView/Extensions/DockViewComponentStateExtensions.cs b/src/components/BootstrapBlazor.DockView/Extensions/DockViewComponentStateExtensions.cs new file mode 100644 index 00000000..4d0e6f0e --- /dev/null +++ b/src/components/BootstrapBlazor.DockView/Extensions/DockViewComponentStateExtensions.cs @@ -0,0 +1,27 @@ +// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). 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 + +namespace BootstrapBlazor.Components; + +static class DockViewComponentStateExtensions +{ + extension(DockViewComponentState? state) + { + /// + /// 判断组件是否需要渲染 + /// Determine whether the component needs to be rendered + /// + public bool IsRender() + { + // 如果组件 Visible false 表示组件不可见,此时 Render 也不需要渲染 + var render = false; + if (state != null) + { + // 组件必须可见并且 Active 时才需要渲染 + render = state.Render && state.Visible; + } + return render; + } + } +} diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css b/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css index 364742c6..1b2dcc51 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css +++ b/src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css @@ -299,3 +299,9 @@ .bb-dockview .dv-render-overlay-float { z-index: -1 !important; } + +.bb-dockview .dv-split-view-container.dv-vertical > .dv-view-container > .dv-view:not(:first-child):not(.visible):before, +.bb-dockview .dv-split-view-container.dv-vertical > .dv-view-container > .dv-view:first-child:not(.visible) + .dv-view.visible:before, +.bb-dockview .dv-split-view-container.dv-vertical > .dv-view-container > .dv-view.first-visible::before { + height: 0; +} diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js index 367b8cac..411141b0 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js @@ -1,14 +1,15 @@ -import { DockviewComponent, DockviewGroupPanel, getGridLocation, getRelativeLocation, DockviewEmitter } from "./dockview-core.esm.js" +import { DockviewComponent, DockviewGroupPanel, DockviewGroupPanelModel, getGridLocation, getRelativeLocation, DockviewEmitter } from "./dockview-core.esm.js" import { getConfigFromStorage, saveConfig } from "./dockview-config.js" import { disposeGroup, removeDrawerBtn } from "./dockview-group.js" +import { markFirstVisibleElement } from "./dockview-utils.js" -DockviewComponent.prototype.on = function (eventType, callback) { +DockviewComponent.prototype.on = function(eventType, callback) { this['_' + eventType] = new DockviewEmitter(); this['_' + eventType].event(callback) } const dispose = DockviewComponent.prototype.dispose; -DockviewComponent.prototype.dispose = function () { +DockviewComponent.prototype.dispose = function() { this.params.observer?.disconnect(); this.groups.forEach(group => { if (group.mutationObserver) { @@ -20,28 +21,29 @@ DockviewComponent.prototype.dispose = function () { } const groupDispose = DockviewGroupPanel.prototype.dispose -DockviewGroupPanel.prototype.dispose = function () { +DockviewGroupPanel.prototype.dispose = function() { disposeGroup(this) groupDispose.call(this) } -DockviewGroupPanel.prototype.getParams = function () { +DockviewGroupPanel.prototype.getParams = function() { return this.activePanel?.params || {} } -DockviewGroupPanel.prototype.setParams = function (data) { +DockviewGroupPanel.prototype.setParams = function(data) { + console.log('setParameter', data); Object.keys(data).forEach(key => { this.panels.forEach(panel => panel.params[key] = data[key]) }) } -DockviewGroupPanel.prototype.removePropsOfParams = function (keys) { +DockviewGroupPanel.prototype.removePropsOfParams = function(keys) { return (keys instanceof Array) ? keys.map(key => this.panels.forEach(panel => delete panel.params[key])) : typeof keys == 'string' ? this.panels.forEach(panel => delete panel.params[keys]) : false } const removeGroup = DockviewComponent.prototype.removeGroup -DockviewComponent.prototype.removeGroup = function (...args) { +DockviewComponent.prototype.removeGroup = function(...args) { if (this.isClearing) { return removeGroup.apply(this, args) } @@ -53,16 +55,6 @@ DockviewComponent.prototype.removeGroup = function (...args) { panel.api.close() }) this.setVisible(group, false) - - // let delPanelsStr = localStorage.getItem(this.params.options.localStorageKey + '-panels') - // let delPanels = delPanelsStr ? JSON.parse(delPanelsStr) : delPanelsStr - // delPanels = delPanels?.map(panel => { - // if (panel.groupId == group.id) { - // panel.groupInvisible = true - // } - // return panel - // }) - // delPanels && localStorage.setItem(this.params.options.localStorageKey + '-panels', JSON.stringify(delPanels)) } else if (type == 'floating') { removeDrawerBtn(group) @@ -70,20 +62,30 @@ DockviewComponent.prototype.removeGroup = function (...args) { } } -const removePanel = DockviewComponent.prototype.removePanel -DockviewComponent.prototype.removePanel = function (...args) { - const panel = args[0] +const closePanel = DockviewGroupPanelModel.prototype.closePanel; +DockviewGroupPanelModel.prototype.closePanel = function(panel, triggerVisibleChangedCallback = true, moveToTemplate = true) { if (!panel.group.locked) { - removePanel.apply(this, args) - if (!this.isClearing) { - this._panelVisibleChanged?.fire({ title: panel.title, status: false }); + closePanel.call(this, panel); + if (triggerVisibleChangedCallback) { + this.accessor._panelVisibleChanged?.fire({ key: panel.params.key, status: false }); + } + } + + if (moveToTemplate) { + if (panel.view.content.element) { + if (panel.titleMenuEle) { + panel.view.content.element.append(panel.titleMenuEle) + } + if (this.accessor.params.template) { + this.accessor.params.template.append(panel.view.content.element) + } } } } const setVisible = DockviewComponent.prototype.setVisible -DockviewComponent.prototype.setVisible = function (...args) { - setVisible.apply(this, args) +DockviewComponent.prototype.setVisible = function(...args) { + setVisible.apply(this, args); const branch = getBranchByGroup(args[0]) const { orientation, splitview: { sashes } } = branch @@ -109,6 +111,7 @@ DockviewComponent.prototype.setVisible = function (...args) { } }); } + markFirstVisibleElement(args[0]) } function getBranchByGroup(group) { const groupEle = group.element diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js index eb3e1e8b..2f9340e4 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js @@ -1,7 +1,7 @@ import { getIcons, getIcon } from "./dockview-icon.js" import { deletePanel, findContentFromPanels, moveAlwaysRenderPanel } from "./dockview-panel.js" import { saveConfig } from "./dockview-config.js" -import { observeGroup } from "./dockview-utils.js" +import { observeGroup, markFirstVisibleElement } from "./dockview-utils.js" import EventHandler from '../../BootstrapBlazor/modules/event-handler.js' const onAddGroup = group => { @@ -20,20 +20,21 @@ const onAddGroup = group => { saveConfig(dockview) }) group.model.contentContainer.dropTarget.onDrop(() => { - saveConfig(dockview) + saveConfig(dockview); + markFirstVisibleElement(group) }) createGroupActions(group); dockview._inited && observeGroup(group) } -const addGroupWithPanel = (dockview, panel, localPanel, panels, index) => { +const addGroupWithPanel = (dockview, panel, panels, index) => { if (panel.groupId) { addPanelWidthGroupId(dockview, panel, index) } else { addPanelWidthCreatGroup(dockview, panel, panels) } - deletePanel(dockview, localPanel) + deletePanel(dockview, panel) } const addPanelWidthGroupId = (dockview, panel, index) => { @@ -41,18 +42,6 @@ const addPanelWidthGroupId = (dockview, panel, index) => { let { rect = {}, packup, floatType, drawer, direction = 'left' } = panel.params || {} if (!group) { group = dockview.createGroup({ id: panel.groupId }) - // const floatingGroupPosition = isMaximized ? { - // x: 0, y: 0, - // width: dockview.width, - // height: dockview.height - // } : { - // x: currentPosition?.left || 0, - // y: currentPosition?.top || 0, - // width: currentPosition?.width, - // height: currentPosition?.height - // } - // dockview.addFloatingGroup(group, floatingGroupPosition, { skipRemoveGroup: true }) - // createGroupActions(group); const width = dockview.width > 500 ? 500 : (dockview.width - 10) const height = dockview.height > 460 ? 460 : (dockview.height - 10) const left = (dockview.width - width) / 2 @@ -74,23 +63,16 @@ const addPanelWidthGroupId = (dockview, panel, index) => { if (floatType == 'drawer') { setTimeout(() => createDrawerHandle(group, direction == 'right'), 0); } - // const floatingGroup = createFloatingGroup(group, floatingGroupRect) - const autoHideBtn = group.header.rightActionsContainer.querySelector('.bb-dockview-control-icon-autohide') - if (autoHideBtn) { - // autoHideBtn.style.display = 'none' - } - - // saveConfig(dockview) } else { if (group.api.location.type === 'grid') { let isVisible = dockview.isVisible(group) if (isVisible === false) { dockview.setVisible(group, true) - // isMaximized && group.api.maximize(); } } } + dockview.addPanel({ id: panel.id, title: panel.title, @@ -99,11 +81,10 @@ const addPanelWidthGroupId = (dockview, panel, index) => { position: { referenceGroup: group, index: index || 0 }, params: { ...panel.params, rect, packup, visible: true } }) - dockview._panelVisibleChanged?.fire({ title: panel.title, status: true }); } const addPanelWidthCreatGroup = (dockview, panel, panels) => { - let { position = {}, currentPosition, packupHeight, isPackup, isMaximized } = panel.params || {} + let { position = {}, packupHeight, isPackup, isMaximized } = panel.params || {} let brothers = panels.filter(p => p.params.parentId == panel.params.parentId && p.id != panel.id) let group, direction if (brothers.length > 0 && brothers[0].params.parentType == 'group') { @@ -138,7 +119,6 @@ const addPanelWidthCreatGroup = (dockview, panel, panels) => { } if (direction) option.position.direction = direction dockview.addPanel(option); - dockview._panelVisibleChanged?.fire({ title: panel.title, status: true }); } const getOrientation = function (child, group) { @@ -178,37 +158,6 @@ const createGroupActions = (group, groupType) => { }, 0) addActionEvent(group, actionContainer); } -const observeDisplayChange = (icon, group) => { - const dockview = group.api.accessor - const element = icon.querySelector('.dropdown-menu') - const mutationObserver = new MutationObserver((mutations) => { - mutations.forEach(mutation => { - if (mutation.attributeName == 'class') { - if(mutation.target.classList.contains('show')) { - const currentPanelEle = group.activePanel.view.content.element.parentElement - const childEle = currentPanelEle.children[0] - group.element.querySelector('&>.dv-content-container').append(childEle) - currentPanelEle.style.zIndex = -1 - childEle.wrapperEle = currentPanelEle - } - else { - const panelEleList = [...group.element.querySelector('&>.dv-content-container').children].map(item => { - const wrapperEle = item.wrapperEle - delete item.wrapperEle - wrapperEle.append(item) - return wrapperEle - }) - group.element.parentElement.parentElement.append(...panelEleList) - } - } - }) - }); - group.mutationObserver = mutationObserver - mutationObserver.observe(element, { - attributes: true, - attributeFilter: ["class"], - }); -} const disposeGroup = group => { const { observer } = group.api.accessor.params; diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js index ec37daa6..59602cb3 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-panel.js @@ -11,10 +11,11 @@ const observePanelActiveChange = panel => { panel.api.onDidActiveChange(({ isActive }) => { if (isActive && !panel.group.api.isMaximized()) { saveConfig(panel.accessor) - if (panel.group.panels.length < 2) return - panel.group.panels.filter(p => p != panel.group.activePanel && p.renderer == 'onlyWhenVisible').forEach(p => { - appendTemplatePanelEle(p) - }) + if (panel.group.panels.length >= 2){ + panel.group.panels.filter(p => p != panel.group.activePanel && p.renderer == 'onlyWhenVisible').forEach(p => { + appendTemplatePanelEle(p) + }) + } } if (isActive && panel.group.getParams().floatType == 'drawer') { setDrawerTitle(panel.group) @@ -26,8 +27,8 @@ const observePanelActiveChange = panel => { 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)) setTimeout(function() { + 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)); }, 0) } @@ -79,14 +80,14 @@ const onRemovePanel = event => { }) } - if (event.view.content.element) { - if (event.titleMenuEle) { - event.view.content.element.append(event.titleMenuEle) - } - if (dockview.params.template) { - // dockview.params.template.append(event.view.content.element) - } - } + // if (event.view.content.element) { + // if (event.titleMenuEle) { + // event.view.content.element.append(event.titleMenuEle) + // } + // if (dockview.params.template) { + // dockview.params.template.append(event.view.content.element) + // } + // } } const appendTemplatePanelEle = (panel) => { diff --git a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js index ca95a21c..e3fa4216 100644 --- a/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js +++ b/src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js @@ -21,7 +21,6 @@ const cerateDockview = (el, options) => { createComponent: option => new DockviewPanelContent(option) }); initDockview(dockview, options, template); - dockview.init(); return dockview; } @@ -34,7 +33,6 @@ const initDockview = (dockview, options, template) => { const config = getConfig(options); dockview.params.floatingGroups = config.floatingGroups || [] dockview.fromJSON(config); - // window.dockview = dockview; } dockview.switchTheme = theme => { @@ -85,6 +83,9 @@ const initDockview = (dockview, options, template) => { }) dockview.onDidLayoutFromJSON(() => { + dockview.groups.forEach(group => { + markFirstVisibleElement(group); + }) const handler = setTimeout(() => { clearTimeout(handler); const panels = dockview.panels; @@ -92,27 +93,27 @@ const initDockview = (dockview, options, template) => { const delPanelsStr = localStorage.getItem(dockview.params.options.localStorageKey + '-panels'); const delPanels = delPanelsStr && JSON.parse(delPanelsStr) || []; - panels.forEach(panel => { - const visible = panel.params.visible - if (!visible) { - dockview.removePanel(panel) - } - dockview._panelVisibleChanged?.fire({ title: panel.title, status: visible }); - }) - delPanels.forEach(panel => { - dockview._panelVisibleChanged?.fire({ title: panel.title, status: false }); - }) - if (options.renderer === 'always') { - + if (options.enableLocalStorage) { + 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) + } + }) + delPanels.forEach(panel => { + dockview._panelVisibleChanged?.fire({ key: panel.params.key, status: false }); + }) } - else if (options.renderer === 'partial' || options.renderer === 'onlyWhenVisible') { + + 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 => 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)); - } + if (options.renderer === 'always') { + dockview._loadTabs?.fire(dockview.panels.map(p => p.params.key)); } const { floatingGroups } = dockview.params dockview.floatingGroups.forEach(fg => { @@ -121,27 +122,18 @@ const initDockview = (dockview, options, template) => { fg.group.element.parentElement.style.inset = [top, right, bottom, left] .map(item => typeof item == 'number' ? (item + 'px') : 'auto').join(' ') - // fg.overlay.onDidChangeEnd(e => { - // saveConfig(dockview); - // }) observeOverlayChange(fg.overlay, fg.group) const { floatType, direction } = fg.group.getParams(); if (floatType == 'drawer') { createDrawerHandle(fg.group, direction == 'right') } - else { - const autoHideBtn = fg.group.header.rightActionsContainer.querySelector('.bb-dockview-control-icon-autohide') - if (autoHideBtn) { - // autoHideBtn.style.display = 'none' - } - } observeFloatingGroupLocationChange(fg.group) }) 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') }) @@ -233,28 +225,42 @@ const setWidth = (target, dockview) => { } const toggleComponent = (dockview, options) => { - const panels = getPanelsFromOptions(options).filter(p => p.params.visible) - const localPanels = dockview.panels + const optionsPanels = getPanelsFromOptions(options); + const panels = optionsPanels.filter(p => p.params.visible); + const localPanels = dockview.panels; panels.forEach(p => { const pan = findContentFromPanels(localPanels, p); if (pan === void 0) { - const panel = findContentFromPanels(dockview.params.panels, p); - const groupPanels = panels.filter(p1 => p1.params.parentId == p.params.parentId) - let indexOfOptions = groupPanels.findIndex(p => p.params.key == panel?.params.key) - indexOfOptions = indexOfOptions == -1 ? 0 : indexOfOptions - const index = panel && panel.params.index - addGroupWithPanel(dockview, p || panel, panel, panels, index ?? indexOfOptions); + const panel = findContentFromPanels(dockview.params.panels, p) || p; + panel.params = { ...panel.params, ...p.params }; + const groupPanels = panels.filter(p1 => p1.params.parentId == p.params.parentId); + let indexOfOptions = groupPanels.findIndex(p => p.params.key == panel?.params.key); + indexOfOptions = indexOfOptions == -1 ? 0 : indexOfOptions; + addGroupWithPanel(dockview, panel, panels, indexOfOptions); + } + else { + if (pan.title !== p.title) { + pan.setTitle(p.title) + } + pan._params = { + ...pan.params, + ...p.params + } } }) + localPanels.forEach(item => { - const title = panels.find(p => p.params.key == item.params.key)?.title; - if ( title && item.title !== title ) { - item.setTitle(title) - } let pan = findContentFromPanels(panels, item); if (pan === void 0) { - item.group.delPanelIndex = item.group.panels.findIndex(p => p.params.key == item.params.key) - dockview.removePanel(item) + item.group.delPanelIndex = item.group.panels.findIndex(p => p.params.key == item.params.key); + const group = item.group; + + const moveToTemplate = optionsPanels.some(p => p.params.key == item.params.key); + group.model.closePanel(item, false, moveToTemplate); + + if (group.panels.length === 0) { + dockview.setVisible(group, false) + } } }) } @@ -263,5 +269,16 @@ const toggleGroupLock = (dockview, options) => { toggleLock(group, group.header.rightActionsContainer, options.lock) }) } +export const markFirstVisibleElement = group => { + if (!group) return + const viewContainerEle = group.element.parentElement.parentElement; + const className = 'first-visible'; + [...viewContainerEle.children].forEach(ele => { + if (ele.classList.contains(className)) { + ele.classList.remove(className) + } + }) + viewContainerEle.querySelector('.visible')?.classList.add(className); +} export { cerateDockview };