Skip to content

Commit 32eb2b1

Browse files
authored
feat(Layout): add ShowTabContextMenu parameter (#5733)
* chore: 更新 header 描述信息 * refactor: Layout 组件增加 Tab 右键菜单 * refactor: 增加图标主题 * test: 更新单元测试
1 parent 7ea1b4c commit 32eb2b1

10 files changed

Lines changed: 199 additions & 34 deletions

File tree

src/BootstrapBlazor/Components/Layout/Layout.razor

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,23 +116,50 @@
116116
@<main class="layout-main">
117117
@if (UseTabSet)
118118
{
119-
<Tab ClickTabToNavigation="ClickTabToNavigation" AdditionalAssemblies="@AdditionalAssemblies"
120-
ShowExtendButtons="ShowTabExtendButtons" ShowClose="ShowTabItemClose" AllowDrag="AllowDragTab"
121-
DefaultUrl="@TabDefaultUrl" ExcludeUrls="@ExcludeUrls" IsOnlyRenderActiveTab="IsOnlyRenderActiveTab"
122-
TabStyle="TabStyle" ShowToolbar="@ShowToolbar" ToolbarTemplate="@ToolbarTemplate"
123-
ShowRefreshToolbarButton="ShowRefreshToolbarButton" ShowFullscreenToolbarButton="ShowFullscreenToolbarButton"
124-
RefreshToolbarButtonIcon="@RefreshToolbarButtonIcon" FullscreenToolbarButtonIcon="@FullscreenToolbarButtonIcon"
125-
RefreshToolbarTooltipText="@RefreshToolbarTooltipText" FullscreenToolbarTooltipText="@FullscreenToolbarTooltipText"
126-
OnToolbarRefreshCallback="OnToolbarRefreshCallback"
127-
Body="@Main" NotAuthorized="NotAuthorized!" NotFound="NotFound!" NotFoundTabText="@NotFoundTabText">
128-
</Tab>
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+
}
129144
}
130145
else
131146
{
132147
@HandlerMain()
133148
}
134149
</main>;
135150

151+
RenderFragment RenderTab =>
152+
@<Tab ClickTabToNavigation="ClickTabToNavigation" AdditionalAssemblies="@AdditionalAssemblies" @ref="_tab"
153+
ShowExtendButtons="ShowTabExtendButtons" ShowClose="ShowTabItemClose" AllowDrag="AllowDragTab"
154+
DefaultUrl="@TabDefaultUrl" ExcludeUrls="@ExcludeUrls" IsOnlyRenderActiveTab="IsOnlyRenderActiveTab"
155+
TabStyle="TabStyle" ShowToolbar="@ShowToolbar" ToolbarTemplate="@ToolbarTemplate"
156+
ShowRefreshToolbarButton="ShowRefreshToolbarButton" ShowFullscreenToolbarButton="ShowFullscreenToolbarButton"
157+
RefreshToolbarButtonIcon="@RefreshToolbarButtonIcon" FullscreenToolbarButtonIcon="@FullscreenToolbarButtonIcon"
158+
RefreshToolbarTooltipText="@RefreshToolbarTooltipText" FullscreenToolbarTooltipText="@FullscreenToolbarTooltipText"
159+
OnToolbarRefreshCallback="OnToolbarRefreshCallback"
160+
Body="@Main" NotAuthorized="NotAuthorized!" NotFound="NotFound!" NotFoundTabText="@NotFoundTabText">
161+
</Tab>;
162+
136163
RenderFragment RenderFooter =>
137164
@<footer class="@FooterClassString">
138165
@Footer

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

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,48 @@ public partial class Layout : IHandlerException
297297
[Parameter]
298298
public string NotAuthorizeUrl { get; set; } = "/Account/Login";
299299

300+
/// <summary>
301+
/// Gets or sets whether enable tab context menu. Default is false.
302+
/// </summary>
303+
[Parameter]
304+
public bool ShowTabContextMenu { get; set; }
305+
306+
/// <summary>
307+
/// Gets or sets the template of before tab context menu. Default is null.
308+
/// </summary>
309+
[Parameter]
310+
public RenderFragment<Tab>? BeforeTabContextMenuTemplate { get; set; }
311+
312+
/// <summary>
313+
/// Gets or sets the template of tab context menu. Default is null.
314+
/// </summary>
315+
[Parameter]
316+
public RenderFragment<Tab>? TabContextMenuTemplate { get; set; }
317+
318+
/// <summary>
319+
/// Gets or sets the icon of tab item context menu refresh button. Default is null.
320+
/// </summary>
321+
[Parameter]
322+
public string? TabContextMenuRefreshIcon { get; set; }
323+
324+
/// <summary>
325+
/// Gets or sets the icon of tab item context menu close button. Default is null.
326+
/// </summary>
327+
[Parameter]
328+
public string? TabContextMenuCloseIcon { get; set; }
329+
330+
/// <summary>
331+
/// Gets or sets the icon of tab item context menu close other button. Default is null.
332+
/// </summary>
333+
[Parameter]
334+
public string? TabContextMenuCloseOtherIcon { get; set; }
335+
336+
/// <summary>
337+
/// Gets or sets the icon of tab item context menu close all button. Default is null.
338+
/// </summary>
339+
[Parameter]
340+
public string? TabContextMenuCloseAllIcon { get; set; }
341+
300342
[Inject]
301343
[NotNull]
302344
private NavigationManager? Navigation { get; set; }
@@ -404,7 +446,8 @@ public partial class Layout : IHandlerException
404446
[NotNull]
405447
private IStringLocalizer<Layout>? Localizer { get; set; }
406448

407-
private bool _init { get; set; }
449+
private bool _init;
450+
private Tab _tab = default!;
408451

409452
/// <summary>
410453
/// <inheritdoc/>
@@ -466,6 +509,10 @@ protected override void OnParametersSet()
466509

467510
TooltipText ??= Localizer[nameof(TooltipText)];
468511
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);
469516
}
470517

471518
/// <summary>
@@ -580,6 +627,38 @@ public virtual Task HandlerException(Exception ex, RenderFragment<Exception> err
580627

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

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+
583662
/// <summary>
584663
/// <inheritdoc/>
585664
/// </summary>

src/BootstrapBlazor/Enums/ComponentIcons.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,26 @@ public enum ComponentIcons
710710
/// </summary>
711711
TabRefreshButtonIcon,
712712

713+
/// <summary>
714+
/// Tab 组件 TabContextMenuRefreshIcon 属性图标
715+
/// </summary>
716+
TabContextMenuRefreshIcon,
717+
718+
/// <summary>
719+
/// Tab 组件 TabContextMenuCloseIcon 属性图标
720+
/// </summary>
721+
TabContextMenuCloseIcon,
722+
723+
/// <summary>
724+
/// Tab 组件 TabContextMenuCloseOtherIcon 属性图标
725+
/// </summary>
726+
TabContextMenuCloseOtherIcon,
727+
728+
/// <summary>
729+
/// Tab 组件 TabContextMenuCloseAllIcon 属性图标
730+
/// </summary>
731+
TabContextMenuCloseAllIcon,
732+
713733
/// <summary>
714734
/// Timer 组件 Icon 属性图标
715735
/// </summary>

src/BootstrapBlazor/Icons/BootstrapIcons.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2-
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3-
// Website: https://www.blazor.zone or https://argozhang.github.io/
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
45

56
namespace BootstrapBlazor.Components;
67

78
internal static class BootstrapIcons
89
{
910
public static Dictionary<ComponentIcons, string> Icons => new()
1011
{
11-
// AnchorLink 组件
1212
{ ComponentIcons.AnchorLinkIcon, "bi bi-link-45deg" },
13-
14-
// Avatar 组件
1513
{ ComponentIcons.AvatarIcon, "bi bi-person-fill" },
1614
{ ComponentIcons.AutoFillIcon, "bi bi-chevron-up" },
1715
{ ComponentIcons.AutoCompleteIcon, "bi bi-chevron-up" },
@@ -23,23 +21,23 @@ internal static class BootstrapIcons
2321
{ ComponentIcons.CameraPlayIcon, "bi bi-circle-play" },
2422
{ ComponentIcons.CameraStopIcon, "bi bi-circle-stop" },
2523
{ ComponentIcons.CameraPhotoIcon, "bi bi-camera" },
24+
2625
{ ComponentIcons.CardCollapseIcon, "bi bi-arrow-right-circle-fill" },
2726
{ ComponentIcons.CarouselPreviousIcon, "bi bi-chevron-left" },
2827
{ ComponentIcons.CarouselNextIcon, "bi bi-chevron-right" },
2928
{ ComponentIcons.CascaderIcon, "bi bi-chevron-up" },
3029
{ ComponentIcons.CascaderSubMenuIcon, "bi bi-chevron-down" },
3130
{ ComponentIcons.ConsoleClearButtonIcon, "bi bi-x" },
3231

33-
// DateTimePicker 组件
3432
{ ComponentIcons.DatePickBodyPreviousYearIcon, "bi bi-chevron-double-left" },
3533
{ ComponentIcons.DatePickBodyPreviousMonthIcon, "bi bi-chevron-left" },
3634
{ ComponentIcons.DatePickBodyNextMonthIcon, "bi bi-chevron-right" },
3735
{ ComponentIcons.DatePickBodyNextYearIcon, "bi bi-chevron-double-right" },
3836
{ ComponentIcons.DateTimePickerIcon, "bi bi-calendar" },
37+
3938
{ ComponentIcons.TimePickerCellUpIcon, "bi bi-chevron-up" },
4039
{ ComponentIcons.TimePickerCellDownIcon, "bi bi-chevron-down" },
4140

42-
// DateTimeRange 组件
4341
{ ComponentIcons.DateTimeRangeIcon, "bi bi-calendar-range" },
4442
{ ComponentIcons.DateTimeRangeClearIcon, "bi bi-x-circle" },
4543

@@ -88,6 +86,11 @@ internal static class BootstrapIcons
8886
{ ComponentIcons.InputNumberPlusIcon, "bi bi-plus-circle" },
8987

9088
{ ComponentIcons.LayoutMenuBarIcon, "bi bi-list" },
89+
{ ComponentIcons.TabContextMenuRefreshIcon, "bi bi-arrow-clockwise" },
90+
{ ComponentIcons.TabContextMenuCloseIcon, "bi bi-x" },
91+
{ ComponentIcons.TabContextMenuCloseOtherIcon, "bi bi-arrow" },
92+
{ ComponentIcons.TabContextMenuCloseAllIcon, "bi bi-arrow-left-right" },
93+
9194
{ ComponentIcons.LogoutLinkIcon, "bi bi-box-arrow-right" },
9295

9396
{ ComponentIcons.LoadingIcon, "bi bi-arrow-clockwise bi-spin" },

src/BootstrapBlazor/Icons/FontAwesomeIcons.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal static class FontAwesomeIcons
2121
{ ComponentIcons.CameraPlayIcon, "fa-solid fa-circle-play" },
2222
{ ComponentIcons.CameraStopIcon, "fa-solid fa-circle-stop" },
2323
{ ComponentIcons.CameraPhotoIcon, "fa-solid fa-camera-retro" },
24+
2425
{ ComponentIcons.CardCollapseIcon, "fa-solid fa-circle-chevron-right" },
2526
{ ComponentIcons.CarouselPreviousIcon, "fa-solid fa-angle-left" },
2627
{ ComponentIcons.CarouselNextIcon, "fa-solid fa-angle-right" },
@@ -32,7 +33,6 @@ internal static class FontAwesomeIcons
3233
{ ComponentIcons.DatePickBodyPreviousMonthIcon, "fa-solid fa-angle-left" },
3334
{ ComponentIcons.DatePickBodyNextMonthIcon, "fa-solid fa-angle-right" },
3435
{ ComponentIcons.DatePickBodyNextYearIcon, "fa-solid fa-angles-right" },
35-
3636
{ ComponentIcons.DateTimePickerIcon, "fa-regular fa-calendar-days" },
3737

3838
{ ComponentIcons.TimePickerCellUpIcon, "fa-solid fa-angle-up" },
@@ -86,6 +86,11 @@ internal static class FontAwesomeIcons
8686
{ ComponentIcons.InputNumberPlusIcon, "fa-solid fa-circle-plus" },
8787

8888
{ ComponentIcons.LayoutMenuBarIcon, "fa-solid fa-bars" },
89+
{ ComponentIcons.TabContextMenuRefreshIcon, "fa-fw fa-solid fa-rotate-right" },
90+
{ ComponentIcons.TabContextMenuCloseIcon, "fa-fw fa-solid fa-xmark" },
91+
{ ComponentIcons.TabContextMenuCloseOtherIcon, "fa-fw fa-solid fa-left-right" },
92+
{ ComponentIcons.TabContextMenuCloseAllIcon, "fa-fw fa-solid fa-arrows-left-right-to-line" },
93+
8994
{ ComponentIcons.LogoutLinkIcon, "fa-solid fa-key" },
9095

9196
{ ComponentIcons.LoadingIcon, "fa-solid fa-fw fa-spin fa-spinner" },

src/BootstrapBlazor/Icons/MaterialDesignIcons.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2-
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3-
// Website: https://www.blazor.zone or https://argozhang.github.io/
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
45

56
namespace BootstrapBlazor.Components;
67

78
internal static class MaterialDesignIcons
89
{
910
public static Dictionary<ComponentIcons, string> Icons => new()
1011
{
11-
// AnchorLink 组件
1212
{ ComponentIcons.AnchorLinkIcon, "mdi mdi-link-variant" },
13-
14-
// Avatar 组件
1513
{ ComponentIcons.AvatarIcon, "mdi mdi-account" },
1614
{ ComponentIcons.AutoFillIcon, "mdi mdi-chevron-up" },
1715
{ ComponentIcons.AutoCompleteIcon, "mdi mdi-chevron-up" },
@@ -23,23 +21,23 @@ internal static class MaterialDesignIcons
2321
{ ComponentIcons.CameraPlayIcon, "mdi mdi-play-circle-outline" },
2422
{ ComponentIcons.CameraStopIcon, "mdi mdi-stop-circle-outline" },
2523
{ ComponentIcons.CameraPhotoIcon, "mdi mdi-camera-outline" },
24+
2625
{ ComponentIcons.CardCollapseIcon, "mdi mdi-chevron-right-circle" },
2726
{ ComponentIcons.CarouselPreviousIcon, "mdi mdi-chevron-left" },
2827
{ ComponentIcons.CarouselNextIcon, "mdi mdi-chevron-right" },
2928
{ ComponentIcons.CascaderIcon, "mdi mdi-chevron-up" },
3029
{ ComponentIcons.CascaderSubMenuIcon, "mdi mdi-chevron-down" },
3130
{ ComponentIcons.ConsoleClearButtonIcon, "mdi mdi-close" },
3231

33-
// DateTimePicker 组件
3432
{ ComponentIcons.DatePickBodyPreviousYearIcon, "mdi mdi-chevron-double-left" },
3533
{ ComponentIcons.DatePickBodyPreviousMonthIcon, "mdi mdi-chevron-left" },
3634
{ ComponentIcons.DatePickBodyNextMonthIcon, "mdi mdi-chevron-right" },
3735
{ ComponentIcons.DatePickBodyNextYearIcon, "mdi mdi-chevron-double-right" },
3836
{ ComponentIcons.DateTimePickerIcon, "mdi mdi-calendar-outline" },
37+
3938
{ ComponentIcons.TimePickerCellUpIcon, "mdi mdi-chevron-up" },
4039
{ ComponentIcons.TimePickerCellDownIcon, "mdi mdi-chevron-down" },
4140

42-
// DateTimeRange 组件
4341
{ ComponentIcons.DateTimeRangeIcon, "mdi mdi-calendar-range-outline" },
4442
{ ComponentIcons.DateTimeRangeClearIcon, "mdi mdi-close-circle-outline" },
4543

@@ -88,6 +86,11 @@ internal static class MaterialDesignIcons
8886
{ ComponentIcons.InputNumberPlusIcon, "mdi mdi-plus-circle-outline" },
8987

9088
{ ComponentIcons.LayoutMenuBarIcon, "mdi mdi-menu" },
89+
{ ComponentIcons.TabContextMenuRefreshIcon, "mdi mdi-refresh" },
90+
{ ComponentIcons.TabContextMenuCloseIcon, "mdi mdi-close" },
91+
{ ComponentIcons.TabContextMenuCloseOtherIcon, "mdi mdi-menu" },
92+
{ ComponentIcons.TabContextMenuCloseAllIcon, "mdi mdi-arrow-left-right-bold" },
93+
9194
{ ComponentIcons.LogoutLinkIcon, "mdi mdi-logout" },
9295

9396
{ ComponentIcons.LoadingIcon, "mdi mdi-loading mdi-spin" },

src/BootstrapBlazor/Locales/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@
105105
"TooltipText": "Go top"
106106
},
107107
"BootstrapBlazor.Components.Layout": {
108-
"TooltipText": "Click to Expand/Collapse sidebar"
108+
"TooltipText": "Click to Expand/Collapse sidebar",
109+
"ContextRefresh": "Refresh",
110+
"ContextClose": "Close",
111+
"ContextCloseOther": "Close Other Tabs",
112+
"ContextCloseAll": "Close All Tabs"
109113
},
110114
"BootstrapBlazor.Components.Logout": {
111115
"PrefixDisplayNameText": "Welcome",

src/BootstrapBlazor/Locales/zh.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@
105105
"TooltipText": "返回顶端"
106106
},
107107
"BootstrapBlazor.Components.Layout": {
108-
"TooltipText": "点击展开收缩左侧菜单"
108+
"TooltipText": "点击展开收缩左侧菜单",
109+
"ContextRefresh": "刷新",
110+
"ContextClose": "关闭",
111+
"ContextCloseOther": "关闭其他",
112+
"ContextCloseAll": "关闭全部"
109113
},
110114
"BootstrapBlazor.Components.Logout": {
111115
"PrefixDisplayNameText": "欢迎",

src/BootstrapBlazor/Services/Bluetooth/BluetoothDeviceInfo.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2-
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3-
// Website: https://www.blazor.zone or https://argozhang.github.io/
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
45

56
namespace BootstrapBlazor.Components;
67

0 commit comments

Comments
 (0)