diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index ec7d975f848..076d95a7ca0 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 9.5.11-beta03 + 9.5.11-beta05 diff --git a/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor b/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor index 19572d4546f..be939936c7a 100644 --- a/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor +++ b/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor @@ -16,6 +16,7 @@ else RenderFragment RenderComponent => @ @if(IsAsyncLoading) { diff --git a/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.cs b/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.cs index b2eac4c8548..aac36be9c63 100644 --- a/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.cs +++ b/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.cs @@ -69,6 +69,8 @@ protected override void OnParametersSet() private string? ConfirmString => OnBeforeClick != null ? "true" : null; + private string? TriggerCloseString => OnClose != null ? "true" : null; + /// /// 显示确认弹窗方法 /// @@ -97,7 +99,11 @@ private async Task OnClickConfirm() IsDisabled = true; IsAsyncLoading = true; StateHasChanged(); - await Task.Run(() => InvokeAsync(OnConfirm)); + + if (OnConfirm != null) + { + await Task.Run(() => InvokeAsync(OnConfirm)); + } if (ButtonType == ButtonType.Submit) { @@ -112,7 +118,10 @@ private async Task OnClickConfirm() } else { - await OnConfirm(); + if (OnConfirm != null) + { + await OnConfirm(); + } if (ButtonType == ButtonType.Submit) { await TrySubmit(); diff --git a/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.js b/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.js index a361d3a9ac3..5444af61044 100644 --- a/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.js +++ b/src/BootstrapBlazor/Components/Button/PopConfirmButton.razor.js @@ -8,7 +8,7 @@ const config = { popoverSelector: '.popover-confirm.show' } -export function init(id) { +export function init(id, invoke, closeCallback) { const el = document.getElementById(id) if (el == null) { return @@ -18,7 +18,9 @@ export function init(id) { const confirm = { el, - container: el.querySelector('[data-bb-toggle="confirm"]') + container: el.querySelector('[data-bb-toggle="confirm"]'), + invoke, + closeCallback } Data.set(id, confirm) @@ -79,7 +81,17 @@ export function init(id) { if (element) { const popover = bootstrap.Popover.getInstance(element); if (popover) { - popover.hide() + popover.hide(); + + const id = element.getAttribute('id'); + if (id) { + const com = Data.get(id); + const { invoke, closeCallback } = com; + const trigger = element.getAttribute('data-bb-close') === 'true'; + if (invoke && trigger) { + invoke.invokeMethodAsync(closeCallback); + } + } } } }) @@ -135,11 +147,17 @@ export function dispose(id) { const confirm = Data.get(id) Data.remove(id) - const { popover } = confirm ?? {}; + const { popover, el } = confirm ?? {}; if (popover) { popover.dispose(); } + if (el) { + EventHandler.off(el, 'show.bs.popover') + EventHandler.off(el, 'inserted.bs.popover') + EventHandler.off(el, 'hide.bs.popover') + } + const { PopConfirmButton } = window.BootstrapBlazor; PopConfirmButton.dispose(id, () => { EventHandler.off(document, 'click', confirm.closeConfirm) diff --git a/src/BootstrapBlazor/Components/Button/PopConfirmButtonBase.cs b/src/BootstrapBlazor/Components/Button/PopConfirmButtonBase.cs index db9af328978..d646af29f78 100644 --- a/src/BootstrapBlazor/Components/Button/PopConfirmButtonBase.cs +++ b/src/BootstrapBlazor/Components/Button/PopConfirmButtonBase.cs @@ -8,7 +8,7 @@ namespace BootstrapBlazor.Components; /// /// 确认弹窗按钮组件 /// -[BootstrapModuleAutoLoader("Button/PopConfirmButton.razor.js")] +[BootstrapModuleAutoLoader("Button/PopConfirmButton.razor.js", JSObjectReference = true)] public abstract class PopConfirmButtonBase : ButtonBase { /// @@ -57,7 +57,6 @@ public abstract class PopConfirmButtonBase : ButtonBase /// 获得/设置 点击确认时回调方法 /// [Parameter] - [NotNull] public Func? OnConfirm { get; set; } /// @@ -70,7 +69,6 @@ public abstract class PopConfirmButtonBase : ButtonBase /// 获得/设置 点击关闭时回调方法 /// [Parameter] - [NotNull] public Func? OnClose { get; set; } /// @@ -160,12 +158,28 @@ protected override void OnParametersSet() ConfirmIcon ??= IconTheme.GetIconByKey(ComponentIcons.PopConfirmButtonConfirmIcon); Trigger ??= "click"; - OnClose ??= () => Task.CompletedTask; - OnConfirm ??= () => Task.CompletedTask; - if (Placement != Placement.Top && Placement != Placement.Right && Placement != Placement.Bottom && Placement != Placement.Left) { Placement = Placement.Auto; } } + + /// + /// + /// + /// + protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, nameof(TriggerCloseCallback)); + + /// + /// Trigger OnClose event callback. + /// + /// + [JSInvokable] + public async Task TriggerCloseCallback() + { + if (OnClose != null) + { + await OnClose(); + } + } } diff --git a/src/BootstrapBlazor/Components/Table/TableExtensionButton.razor.cs b/src/BootstrapBlazor/Components/Table/TableExtensionButton.razor.cs index edaea66007c..abd09ce06a2 100644 --- a/src/BootstrapBlazor/Components/Table/TableExtensionButton.razor.cs +++ b/src/BootstrapBlazor/Components/Table/TableExtensionButton.razor.cs @@ -61,7 +61,10 @@ await OnClickButton(new TableCellButtonArgs() private async Task OnClickConfirm(TableCellPopConfirmButton b) { - await b.OnConfirm(); + if (b.OnConfirm != null) + { + await b.OnConfirm(); + } if (OnClickButton != null) { diff --git a/src/BootstrapBlazor/Components/Table/TableToolbar.razor.cs b/src/BootstrapBlazor/Components/Table/TableToolbar.razor.cs index 737b72b0f4d..51c630176ec 100644 --- a/src/BootstrapBlazor/Components/Table/TableToolbar.razor.cs +++ b/src/BootstrapBlazor/Components/Table/TableToolbar.razor.cs @@ -91,7 +91,10 @@ private async Task OnConfirm(TableToolbarPopConfirmButton button) await button.OnClick.InvokeAsync(); } - await button.OnConfirm(); + if (button.OnConfirm != null) + { + await button.OnConfirm(); + } // 传递当前选中行给回调委托方法 if (button.OnConfirmCallback != null) diff --git a/test/UnitTest/Components/PopConfirmButtonTest.cs b/test/UnitTest/Components/PopConfirmButtonTest.cs index da5b9d908b2..37ca60e1138 100644 --- a/test/UnitTest/Components/PopConfirmButtonTest.cs +++ b/test/UnitTest/Components/PopConfirmButtonTest.cs @@ -29,6 +29,7 @@ public async Task Show_Ok() cut.Contains("fa-solid fa-xmark"); cut.Contains("fa-solid fa-check"); + cut.DoesNotContain("data-bb-close=\"true\""); // Show var button = cut.Find("div"); @@ -84,9 +85,14 @@ await cut.InvokeAsync(() => return Task.FromResult(true); }); }); + cut.Contains("data-bb-close=\"true\""); // 默认设置增加 shadow 样式 Assert.Contains("data-bs-custom-class=\"test-custom-class shadow\"", cut.Markup); + close = false; + await cut.InvokeAsync(() => popButton.Instance.TriggerCloseCallback()); + Assert.True(close); + // 移除 shadow 样式 popButton.SetParametersAndRender(pb => { @@ -102,6 +108,7 @@ await cut.InvokeAsync(() => }); // Close + close = false; buttons = cut.FindAll(".popover-confirm-buttons button"); await cut.InvokeAsync(() => { diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 6cbde42b495..5f379e286f1 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -2360,6 +2360,7 @@ public async Task CustomerToolbarPopConfirmButton_Ok() { var clicked = false; var clickCallback = false; + var confirmCallback = false; var localizer = Context.Services.GetRequiredService>(); var cut = Context.RenderComponent(pb => { @@ -2390,15 +2391,21 @@ public async Task CustomerToolbarPopConfirmButton_Ok() clickCallback = true; return Task.CompletedTask; })); + builder.AddAttribute(4, nameof(TableToolbarPopConfirmButton.OnConfirm), new Func(() => + { + confirmCallback = true; + return Task.CompletedTask; + })); builder.CloseComponent(); }); }); }); var button = cut.FindComponent(); - await cut.InvokeAsync(() => button.Instance.OnConfirm.Invoke()); + await cut.InvokeAsync(() => button.Instance.OnConfirm!.Invoke()); Assert.True(clickCallback); Assert.True(clicked); + Assert.True(confirmCallback); } [Fact] @@ -5370,7 +5377,7 @@ public async Task Delete_Ok() await cut.InvokeAsync(input.Instance.OnToggleClick); var button = cut.FindComponent>(); - await cut.InvokeAsync(() => button.Instance.OnConfirm.Invoke()); + await cut.InvokeAsync(() => button.Instance.OnConfirm!.Invoke()); Assert.Single(items); } @@ -5420,7 +5427,7 @@ public async Task OnDeleteAsync_Ok() await cut.InvokeAsync(input.Instance.OnToggleClick); var button = cut.FindComponent>(); - await cut.InvokeAsync(() => button.Instance.OnConfirm.Invoke()); + await cut.InvokeAsync(() => button.Instance.OnConfirm!.Invoke()); var row = cut.FindAll("tbody tr"); Assert.Single(row); @@ -6199,7 +6206,7 @@ public async Task DynamicContext_Add() await cut.InvokeAsync(() => table.Instance.AddAsync()); var delete = cut.FindComponent>(); - await cut.InvokeAsync(() => delete.Instance.OnConfirm()); + await cut.InvokeAsync(() => delete.Instance.OnConfirm!.Invoke()); } [Fact] @@ -6953,13 +6960,13 @@ public void OnConfirm_Ok() // 选一个 var input = cut.FindComponents>()[1]; cut.InvokeAsync(input.Instance.OnToggleClick); - cut.InvokeAsync(() => deleteButton.Instance.OnConfirm()); + cut.InvokeAsync(() => deleteButton.Instance.OnConfirm!.Invoke()); table.SetParametersAndRender(pb => { pb.Add(a => a.PageItemsSource, [1, 2, 4, 8]); }); - cut.InvokeAsync(() => deleteButton.Instance.OnConfirm()); + cut.InvokeAsync(() => deleteButton.Instance.OnConfirm!.Invoke()); } [Fact] @@ -7084,7 +7091,7 @@ public async Task IsExcel_Ok() await cut.InvokeAsync(() => table.Instance.AddAsync()); var delete = cut.FindComponent>(); - await cut.InvokeAsync(() => delete.Instance.OnConfirm()); + await cut.InvokeAsync(() => delete.Instance.OnConfirm!.Invoke()); } [Fact]