Skip to content

Commit 97d9b30

Browse files
authored
feat(Tab): add OnBeforeShowContextMenu parameter (#5737)
* refactor: 移动 Tab 右键菜单逻辑到内部 * doc: 更新示例 * refactor: 精简代码 * refactor: 代码格式化 * test: 更新单元测试 * refactor: 重构代码 * refactor: 整理代码顺序 * feat: 禁用标签页允许弹出右键菜单 * feat: 增加 OnBeforeShowContextMenu 回调方法 * doc: 更新示例 * doc: 更新说明文字 * test: 更新单元测试 * test: 更新单元测试 * test: 更新单元测试 * refactor: 重构代码 * test: 更新单元测试
1 parent 3531e98 commit 97d9b30

File tree

13 files changed

+246
-208
lines changed

13 files changed

+246
-208
lines changed

src/BootstrapBlazor.Server/Components/Samples/Tabs.razor

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ private void Navigation()
190190
<button class="btn btn-primary" @onclick="e => SetPlacement(Placement.Left)">Left</button>
191191
</div>
192192
</p>
193-
<Tab Placement="@BindPlacement" Height="200">
193+
<Tab Placement="@_bindPlacement" Height="200">
194194
<TabItem Text="@Localizer["TabItem1Text"]">
195195
<div>@Localizer["TabItem1Content"]</div>
196196
</TabItem>
@@ -211,7 +211,7 @@ private void Navigation()
211211
</TabItem>
212212
</Tab>
213213
<Divider Text="@Localizer["DividerText"]"></Divider>
214-
<Tab Placement="@BindPlacement" IsCard="true" Height="200">
214+
<Tab Placement="@_bindPlacement" IsCard="true" Height="200">
215215
<TabItem Text="@Localizer["TabItem1Text"]">
216216
<div>@Localizer["TabItem1Content"]</div>
217217
</TabItem>
@@ -232,7 +232,7 @@ private void Navigation()
232232
</TabItem>
233233
</Tab>
234234
<Divider Text="@Localizer["DividerText"]"></Divider>
235-
<Tab Placement="@BindPlacement" IsBorderCard="true" Height="200">
235+
<Tab Placement="@_bindPlacement" IsBorderCard="true" Height="200">
236236
<TabItem Text="@Localizer["TabItem1Text"]">
237237
<div>@Localizer["TabItem1Content"]</div>
238238
</TabItem>
@@ -339,7 +339,7 @@ private void Navigation()
339339
</button>
340340
</section>
341341

342-
<Tab ShowExtendButtons="ShowButtons" ShowClose="true" @ref="TabSetApp" class="mt-3">
342+
<Tab ShowExtendButtons="true" ShowClose="true" @ref="TabSetApp" class="mt-3">
343343
<TabItem Text="@Localizer["TabItem1Text"]" Closable="false">
344344
<div>@Localizer["TabItem1Content"]</div>
345345
</TabItem>
@@ -497,30 +497,25 @@ private void Navigation()
497497
</DemoBlock>
498498

499499
<DemoBlock Title="@Localizer["TabsContextMenuTitle"]" Introduction="@Localizer["TabsContextMenuIntro"]" Name="ContextMenu">
500-
<ContextMenuZone>
501-
<Tab IsCard="true" ShowClose="true" TabStyle="TabStyle.Chrome" ShowToolbar="true" @ref="_tab">
502-
<TabItem Text="@Localizer["TabItem1Text"]" Icon="fa-solid fa-user">
503-
<div>@Localizer["TabItem1Content"]</div>
504-
<Counter></Counter>
505-
</TabItem>
506-
<TabItem Text="@Localizer["TabItem2Text"]" Icon="fa-solid fa-gauge-high">
507-
<div>@Localizer["TabItem2Content"]</div>
508-
</TabItem>
509-
<TabItem Text="@Localizer["TabItem3Text"]" Icon="fa-solid fa-sitemap">
510-
<div>@Localizer["TabItem3Content"]</div>
511-
</TabItem>
512-
<TabItem Text="@Localizer["TabItem4Text"]" Icon="fa-solid fa-building-columns">
513-
<div>@Localizer["TabItem4Content"]</div>
514-
</TabItem>
515-
</Tab>
516-
<ContextMenu>
517-
<ContextMenuItem Icon="fa-fw fa-solid fa-rotate-right" Text="@Localizer["ContextRefresh"]" OnClick="OnRefrsh"></ContextMenuItem>
518-
<ContextMenuDivider></ContextMenuDivider>
519-
<ContextMenuItem Icon="fa-fw fa-solid fa-xmark" Text="@Localizer["ContextClose"]" OnClick="OnClose"></ContextMenuItem>
520-
<ContextMenuItem Icon="fa-fw fa-solid fa-left-right" Text="@Localizer["ContextCloseOther"]" OnClick="OnCloseOther"></ContextMenuItem>
521-
<ContextMenuItem Icon="fa-fw fa-solid fa-arrows-left-right-to-line" Text="@Localizer["ContextCloseAll"]" OnClick="OnCloseAll"></ContextMenuItem>
522-
</ContextMenu>
523-
</ContextMenuZone>
500+
<section ignore>
501+
<p>@((MarkupString)Localizer["TabsContextMenuDesc"].Value)</p>
502+
</section>
503+
<Tab IsCard="true" ShowClose="true" TabStyle="TabStyle.Chrome" ShowToolbar="true" ShowContextMenu="true"
504+
OnBeforeShowContextMenu="OnBeforeShowContextMenu">
505+
<TabItem Text="@Localizer["TabItem1Text"]" Icon="fa-solid fa-user" IsDisabled="true">
506+
<div>@Localizer["TabItem1Content"]</div>
507+
<Counter></Counter>
508+
</TabItem>
509+
<TabItem Text="@Localizer["TabItem2Text"]" Icon="fa-solid fa-gauge-high">
510+
<div>@Localizer["TabItem2Content"]</div>
511+
</TabItem>
512+
<TabItem Text="@Localizer["TabItem3Text"]" Icon="fa-solid fa-sitemap">
513+
<div>@Localizer["TabItem3Content"]</div>
514+
</TabItem>
515+
<TabItem Text="@Localizer["TabItem4Text"]" Icon="fa-solid fa-building-columns">
516+
<div>@Localizer["TabItem4Content"]</div>
517+
</TabItem>
518+
</Tab>
524519
</DemoBlock>
525520

526521
<AttributeTable Items="@GetAttributes()" Title="@Localizer["AttTitle"]" />

src/BootstrapBlazor.Server/Components/Samples/Tabs.razor.cs

Lines changed: 22 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public sealed partial class Tabs
1313
[NotNull]
1414
private Tab? TabSet { get; set; }
1515

16-
private Placement BindPlacement = Placement.Top;
16+
private Placement _bindPlacement = Placement.Top;
1717

1818
private bool RemoveEnabled => (TabSet?.Items.Count() ?? 4) < 4;
1919

@@ -39,13 +39,13 @@ public sealed partial class Tabs
3939

4040
private void SetPlacement(Placement placement)
4141
{
42-
BindPlacement = placement;
42+
_bindPlacement = placement;
4343
}
4444

45-
private Task AddTab(Tab tabset)
45+
private Task AddTab(Tab tab)
4646
{
47-
var text = $"Tab {tabset.Items.Count() + 1}";
48-
tabset.AddTab(new Dictionary<string, object?>
47+
var text = $"Tab {tab.Items.Count() + 1}";
48+
tab.AddTab(new Dictionary<string, object?>
4949
{
5050
[nameof(TabItem.Text)] = text,
5151
[nameof(TabItem.IsActive)] = true,
@@ -59,44 +59,39 @@ private Task AddTab(Tab tabset)
5959
return Task.CompletedTask;
6060
}
6161

62-
private static Task Active(Tab tabset)
62+
private static Task Active(Tab tab)
6363
{
64-
tabset.ActiveTab(0);
64+
tab.ActiveTab(0);
6565
return Task.CompletedTask;
6666
}
6767

68-
private static async Task RemoveTab(Tab tabset)
68+
private static async Task RemoveTab(Tab tab)
6969
{
70-
if (tabset.Items.Count() > 4)
70+
if (tab.Items.Count() > 4)
7171
{
72-
var item = tabset.Items.Last();
73-
await tabset.RemoveTab(item);
72+
var item = tab.Items.Last();
73+
await tab.RemoveTab(item);
7474
}
7575
}
7676

7777
private void OnToggleDisable()
7878
{
7979
Disabled = !Disabled;
80-
8180
DisableText = Disabled ? "Enable" : "Disable";
8281
}
8382

8483
/// <summary>
85-
/// OnAfterRenderAsync
84+
/// <inheritdoc/>
8685
/// </summary>
8786
/// <param name="firstRender"></param>
88-
/// <returns></returns>
89-
protected override async Task OnAfterRenderAsync(bool firstRender)
87+
protected override void OnAfterRender(bool firstRender)
9088
{
9189
if (firstRender)
9290
{
93-
var menuItem = TabMenu?.Items.FirstOrDefault();
94-
if (menuItem != null)
91+
var menuItem = TabMenu.Items.FirstOrDefault();
92+
if (menuItem != null && TabMenu.OnClick is not null)
9593
{
96-
await InvokeAsync(() =>
97-
{
98-
var _ = TabMenu?.OnClick?.Invoke(menuItem);
99-
});
94+
TabMenu.OnClick(menuItem);
10095
}
10196
}
10297
}
@@ -116,18 +111,19 @@ private Task OnClickMenuItem(MenuItem item)
116111
return Task.CompletedTask;
117112
}
118113

114+
private async Task<bool> OnBeforeShowContextMenu(TabItem item)
115+
{
116+
await Task.Yield();
117+
return item.IsDisabled == false;
118+
}
119+
119120
private void AddTabItem(string text) => TabSetMenu.AddTab(new Dictionary<string, object?>
120121
{
121122
[nameof(TabItem.Text)] = text,
122123
[nameof(TabItem.IsActive)] = true,
123124
[nameof(TabItem.ChildContent)] = text == Localizer["BackText1"] ? BootstrapDynamicComponent.CreateComponent<Counter>().Render() : BootstrapDynamicComponent.CreateComponent<FetchData>().Render()
124125
});
125126

126-
private void OnClick()
127-
{
128-
ShowButtons = !ShowButtons;
129-
}
130-
131127
private async Task RemoveTab()
132128
{
133129
if (TabSetApp.Items.Count() > 4)
@@ -171,42 +167,6 @@ private Task OnSetTitle(string text)
171167
return Task.CompletedTask;
172168
}
173169

174-
[NotNull]
175-
private Tab? _tab = null;
176-
177-
private Task OnRefrsh(ContextMenuItem item, object? context)
178-
{
179-
if (context is TabItem tabItem)
180-
{
181-
_tab.Refresh(tabItem);
182-
}
183-
return Task.CompletedTask;
184-
}
185-
186-
private async Task OnClose(ContextMenuItem item, object? context)
187-
{
188-
if (context is TabItem tabItem)
189-
{
190-
await _tab.RemoveTab(tabItem);
191-
}
192-
}
193-
194-
private Task OnCloseOther(ContextMenuItem item, object? context)
195-
{
196-
if (context is TabItem tabItem)
197-
{
198-
_tab.ActiveTab(tabItem);
199-
}
200-
_tab.CloseOtherTabs();
201-
return Task.CompletedTask;
202-
}
203-
204-
private Task OnCloseAll(ContextMenuItem item, object? context)
205-
{
206-
_tab.CloseAllTabs();
207-
return Task.CompletedTask;
208-
}
209-
210170
/// <summary>
211171
/// 获得属性方法
212172
/// </summary>

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2138,7 +2138,8 @@
21382138
"ContextCloseOther": "Close Other Tabs",
21392139
"ContextCloseAll": "Close All Tabs",
21402140
"TabsContextMenuTitle": "TabItem Context Menu",
2141-
"TabsContextMenuIntro": "Use the built-in <code>ContextMenuZone</code> component to pop up a custom context menu when you right-click a tab item"
2141+
"TabsContextMenuIntro": "Use the built-in <code>ContextMenuZone</code> component to pop up a custom context menu when you right-click a tab item",
2142+
"TabsContextMenuDesc": "The disabled tab can be set by setting the <code>OnBeforeShowContextMenu</code> callback method to control whether the right-click menu is displayed according to the return value. If it is not set, the right-click menu is also displayed for the disabled tab"
21422143
},
21432144
"BootstrapBlazor.Server.Components.Components.DemoTabItem": {
21442145
"Info": "Reset the title of this <code>TabItem</code> by click the button",

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2138,7 +2138,8 @@
21382138
"ContextCloseOther": "关闭其他",
21392139
"ContextCloseAll": "关闭全部",
21402140
"TabsContextMenuTitle": "右键菜单",
2141-
"TabsContextMenuIntro": "通过内置 <code>ContextMenuZone</code> 组件,点击标签页右键时弹窗自定义右键菜单"
2141+
"TabsContextMenuIntro": "通过内置 <code>ContextMenuZone</code> 组件,点击标签页右键时弹窗自定义右键菜单",
2142+
"TabsContextMenuDesc": "被禁用的标签页可以通过设置 <code>OnBeforeShowContextMenu</code> 回调方法根据返回值控制右键菜单是否显示,未设置时禁用标签页也显示右键菜单"
21422143
},
21432144
"BootstrapBlazor.Server.Components.Components.DemoTabItem": {
21442145
"Info": "点击下方按钮,本 <code>TabItem</code> 标题更改为当前分钟与秒",

src/BootstrapBlazor/Components/Layout/Layout.razor

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -116,31 +116,7 @@
116116
@<main class="layout-main">
117117
@if (UseTabSet)
118118
{
119-
@if (ShowTabContextMenu)
120-
{
121-
<ContextMenuZone>
122-
@RenderTab
123-
<ContextMenu>
124-
@if (BeforeTabContextMenuTemplate != null)
125-
{
126-
@BeforeTabContextMenuTemplate(_tab)
127-
}
128-
<ContextMenuItem Icon="@TabContextMenuRefreshIcon" Text="@Localizer["ContextRefresh"]" OnClick="OnRefrsh"></ContextMenuItem>
129-
<ContextMenuDivider></ContextMenuDivider>
130-
<ContextMenuItem Icon="@TabContextMenuCloseIcon" Text="@Localizer["ContextClose"]" OnClick="OnClose"></ContextMenuItem>
131-
<ContextMenuItem Icon="@TabContextMenuCloseOtherIcon" Text="@Localizer["ContextCloseOther"]" OnClick="OnCloseOther"></ContextMenuItem>
132-
<ContextMenuItem Icon="@TabContextMenuCloseAllIcon" Text="@Localizer["ContextCloseAll"]" OnClick="OnCloseAll"></ContextMenuItem>
133-
@if (TabContextMenuTemplate != null)
134-
{
135-
@TabContextMenuTemplate(_tab)
136-
}
137-
</ContextMenu>
138-
</ContextMenuZone>
139-
}
140-
else
141-
{
142-
@RenderTab
143-
}
119+
@RenderTab
144120
}
145121
else
146122
{
@@ -153,6 +129,11 @@
153129
ShowExtendButtons="ShowTabExtendButtons" ShowClose="ShowTabItemClose" AllowDrag="AllowDragTab"
154130
DefaultUrl="@TabDefaultUrl" ExcludeUrls="@ExcludeUrls" IsOnlyRenderActiveTab="IsOnlyRenderActiveTab"
155131
TabStyle="TabStyle" ShowToolbar="@ShowToolbar" ToolbarTemplate="@ToolbarTemplate"
132+
ShowContextMenu="ShowTabContextMenu"
133+
BeforeContextMenuTemplate="@BeforeTabContextMenuTemplate" ContextMenuTemplate="@TabContextMenuTemplate"
134+
ContextMenuRefreshIcon="@TabContextMenuRefreshIcon" ContextMenuCloseIcon="@TabContextMenuCloseIcon"
135+
ContextMenuCloseOtherIcon="@TabContextMenuCloseOtherIcon" ContextMenuCloseAllIcon="@TabContextMenuCloseAllIcon"
136+
OnBeforeShowContextMenu="@OnBeforeShowContextMenu"
156137
ShowRefreshToolbarButton="ShowRefreshToolbarButton" ShowFullscreenToolbarButton="ShowFullscreenToolbarButton"
157138
RefreshToolbarButtonIcon="@RefreshToolbarButtonIcon" FullscreenToolbarButtonIcon="@FullscreenToolbarButtonIcon"
158139
RefreshToolbarTooltipText="@RefreshToolbarTooltipText" FullscreenToolbarTooltipText="@FullscreenToolbarTooltipText"

src/BootstrapBlazor/Components/Layout/Layout.razor.cs

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ public partial class Layout : IHandlerException
339339
[Parameter]
340340
public string? TabContextMenuCloseAllIcon { get; set; }
341341

342+
/// <summary>
343+
/// Gets or sets before popup context menu callback. Default is null.
344+
/// </summary>
345+
[Parameter]
346+
public Func<TabItem, Task<bool>>? OnBeforeShowContextMenu { get; set; }
347+
342348
[Inject]
343349
[NotNull]
344350
private NavigationManager? Navigation { get; set; }
@@ -509,10 +515,6 @@ protected override void OnParametersSet()
509515

510516
TooltipText ??= Localizer[nameof(TooltipText)];
511517
MenuBarIcon ??= IconTheme.GetIconByKey(ComponentIcons.LayoutMenuBarIcon);
512-
TabContextMenuRefreshIcon ??= IconTheme.GetIconByKey(ComponentIcons.TabContextMenuRefreshIcon);
513-
TabContextMenuCloseIcon ??= IconTheme.GetIconByKey(ComponentIcons.TabContextMenuCloseIcon);
514-
TabContextMenuCloseOtherIcon ??= IconTheme.GetIconByKey(ComponentIcons.TabContextMenuCloseOtherIcon);
515-
TabContextMenuCloseAllIcon ??= IconTheme.GetIconByKey(ComponentIcons.TabContextMenuCloseAllIcon);
516518
}
517519

518520
/// <summary>
@@ -627,38 +629,6 @@ public virtual Task HandlerException(Exception ex, RenderFragment<Exception> err
627629

628630
private string? GetTargetString() => IsFixedTabHeader ? ".tabs-body" : null;
629631

630-
private async Task OnRefrsh(ContextMenuItem item, object? context)
631-
{
632-
if (context is TabItem tabItem)
633-
{
634-
await _tab.Refresh(tabItem);
635-
}
636-
}
637-
638-
private async Task OnClose(ContextMenuItem item, object? context)
639-
{
640-
if (context is TabItem tabItem)
641-
{
642-
await _tab.RemoveTab(tabItem);
643-
}
644-
}
645-
646-
private Task OnCloseOther(ContextMenuItem item, object? context)
647-
{
648-
if (context is TabItem tabItem)
649-
{
650-
_tab.ActiveTab(tabItem);
651-
}
652-
_tab.CloseOtherTabs();
653-
return Task.CompletedTask;
654-
}
655-
656-
private Task OnCloseAll(ContextMenuItem item, object? context)
657-
{
658-
_tab.CloseAllTabs();
659-
return Task.CompletedTask;
660-
}
661-
662632
/// <summary>
663633
/// <inheritdoc/>
664634
/// </summary>

0 commit comments

Comments
 (0)