diff --git a/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor b/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor index 06a2d4f34a8..a8f4dc6ffcc 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor @@ -1,7 +1,6 @@ @page "/toast" @inject IStringLocalizer Localizer @inject ToastService ToastService -@inject IJSRuntime JSRuntime

@Localizer["ToastsTitle"]

@@ -51,6 +50,10 @@ private ToastService? ToastService { get; set; } + + + + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor.cs index 58cf516cdaf..73914f3e9fd 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Toasts.razor.cs @@ -37,7 +37,9 @@ public sealed partial class Toasts [Inject, NotNull] private IOptions? Options { get; set; } - private int _delayTs => Options.Value.ToastDelay / 1000; + private int DelayTs => Options.Value.ToastDelay / 1000; + + private readonly ToastOption _option = new(); /// /// OnInitialized @@ -46,10 +48,10 @@ protected override void OnInitialized() { base.OnInitialized(); - Options1 = new ToastOption { Title = "Save data", IsAutoHide = false, Content = $"Save data successfully, automatically close after {_delayTs} seconds" }; - Options2 = new ToastOption { Category = ToastCategory.Error, Title = "Save data", IsAutoHide = false, Content = $"Save data successfully, automatically close after {_delayTs} seconds" }; - Options3 = new ToastOption { Category = ToastCategory.Information, Title = "Prompt information", IsAutoHide = false, Content = $"Information prompt pop-up window, automatically closes after {_delayTs} seconds" }; - Options4 = new ToastOption { Category = ToastCategory.Warning, Title = "Warning message", IsAutoHide = false, Content = $"Information prompt pop-up window, automatically closes after {_delayTs} seconds" }; + Options1 = new ToastOption { Title = "Save data", IsAutoHide = false, Content = $"Save data successfully, automatically close after {DelayTs} seconds" }; + Options2 = new ToastOption { Category = ToastCategory.Error, Title = "Save data", IsAutoHide = false, Content = $"Save data successfully, automatically close after {DelayTs} seconds" }; + Options3 = new ToastOption { Category = ToastCategory.Information, Title = "Prompt information", IsAutoHide = false, Content = $"Information prompt pop-up window, automatically closes after {DelayTs} seconds" }; + Options4 = new ToastOption { Category = ToastCategory.Warning, Title = "Warning message", IsAutoHide = false, Content = $"Information prompt pop-up window, automatically closes after {DelayTs} seconds" }; ToastContainer = Root.ToastContainer; } @@ -62,10 +64,33 @@ await ToastService.Show(new ToastOption() PreventDuplicates = true, Category = ToastCategory.Success, Title = "Successfully saved", - Content = $"Save data successfully, automatically close after {_delayTs} seconds" + Content = $"Save data successfully, automatically close after {DelayTs} seconds" }); } + private async Task OnAsyncClick() + { + _option.Title = Localizer["ToastsAsyncDemoTitle"]; + _option.ForceDelay = true; + _option.IsAutoHide = false; + _option.Delay = 3000; + _option.Content = Localizer["ToastsAsyncDemoStep1Text"]; + _option.Category = ToastCategory.Information; + await ToastService.Show(_option); + + await Task.Delay(3000); + _option.Content = Localizer["ToastsAsyncDemoStep2Text"]; + _option.IsAutoHide = true; + _option.Category = ToastCategory.Information; + await ToastService.Show(_option); + + await Task.Delay(2000); + _option.Content = Localizer["ToastsAsyncDemoStep3Text"]; + _option.Category = ToastCategory.Success; + + await ToastService.Show(_option); + } + private async Task OnSuccessClick() { ToastContainer.SetPlacement(Placement.BottomEnd); @@ -73,7 +98,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Success, Title = "Successfully saved", - Content = $"Save data successfully, automatically close after {_delayTs} seconds" + Content = $"Save data successfully, automatically close after {DelayTs} seconds" }); } @@ -84,7 +109,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Error, Title = "Failed to save", - Content = $"Failed to save data, automatically closes after {_delayTs} seconds" + Content = $"Failed to save data, automatically closes after {DelayTs} seconds" }); } @@ -95,7 +120,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Information, Title = "Notification", - Content = $"The system adds new components, it will automatically shut down after {_delayTs} seconds" + Content = $"The system adds new components, it will automatically shut down after {DelayTs} seconds" }); } @@ -106,7 +131,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Warning, Title = "Warning", - Content = $"If the system finds abnormality, please deal with it in time, and it will automatically shut down after {_delayTs} seconds" + Content = $"If the system finds abnormality, please deal with it in time, and it will automatically shut down after {DelayTs} seconds" }); } @@ -135,7 +160,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Warning, ShowHeader = false, - Content = $"The system adds new components, it will automatically shut down after {_delayTs} seconds" + Content = $"The system adds new components, it will automatically shut down after {DelayTs} seconds" }); } @@ -146,7 +171,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Information, HeaderTemplate = RenderHeader, - Content = $"The system adds new components, it will automatically shut down after {_delayTs} seconds" + Content = $"The system adds new components, it will automatically shut down after {DelayTs} seconds" }); } @@ -157,7 +182,7 @@ await ToastService.Show(new ToastOption() { Category = ToastCategory.Information, Title = "Notification", - Content = $"Toast The component has changed position, it will automatically shut down after {_delayTs} seconds" + Content = $"Toast The component has changed position, it will automatically shut down after {DelayTs} seconds" }); } @@ -176,7 +201,7 @@ private AttributeItem[] GetAttributes() => Name = "Title", Description = Localizer["ToastsAttrTitle"], Type = "string", - ValueList = "—", + ValueList = " — ", DefaultValue = "" }, new() @@ -184,7 +209,7 @@ private AttributeItem[] GetAttributes() => Name = "Content", Description = Localizer["ToastsAttrContent"], Type = "string", - ValueList = "—", + ValueList = " — ", DefaultValue = "" }, new() @@ -192,7 +217,7 @@ private AttributeItem[] GetAttributes() => Name = "Delay", Description = Localizer["ToastsAttrDelay"], Type = "int", - ValueList = "—", + ValueList = " — ", DefaultValue = "4000" }, new() @@ -200,7 +225,7 @@ private AttributeItem[] GetAttributes() => Name = "IsAutoHide", Description = Localizer["ToastsAttrIsAutoHide"], Type = "boolean", - ValueList = "", + ValueList = " — ", DefaultValue = "true" }, new() diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 04a5dd1a7cc..e7556b4a69a 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -124,7 +124,14 @@ "ToastsPreventTitle": "Prevent Duplicates", "ToastsPreventIntro": "By setting PreventDuplicates=\"true\" to repeatedly click the button below, only one pop-up window will appear", "ToastsPreventText": "Prevent Duplicates", + "ToastsAsyncTitle": "Async notification", + "ToastsAsyncIntro": "By setting IsAsync, the pop-up window is displayed, the thread is blocked, and after clicking the close button, the subsequent code continues to execute", + "ToastsAsyncDemoTitle": "Async notification", + "ToastsAsyncDemoStep1Text": "Packing documents, please wait...", + "ToastsAsyncDemoStep2Text": "Packaging completed, downloading...", + "ToastsAsyncDemoStep3Text": "Download completed, close the window automatically", "ToastsWarning": "Warning notice", + "ToastsAsyncText": "AsyncNotify", "ToastsCloseTitle": "Toast is closed manually", "ToastsCloseIntro": "It will not close automatically, you need to manually click the close button. You can customize the event after closing the pop-up window by settingOnCloseAsynccallback delegate", "ToastsCloseNotificationText": "success notification", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index c5478c69f93..800e289d3c3 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -124,7 +124,14 @@ "ToastsPreventTitle": "阻止重复", "ToastsPreventIntro": "通过设置 PreventDuplicates=\"true\" 重复点击下方按钮时,仅弹窗一次", "ToastsPreventText": "阻止重复", + "ToastsAsyncTitle": "线程阻塞通知", + "ToastsAsyncIntro": "通过设置 IsAsync 弹窗显示后,线程阻塞,点击关闭按钮后,继续执行后续代码", + "ToastsAsyncDemoTitle": "异步通知", + "ToastsAsyncDemoStep1Text": "正在打包文档,请稍等...", + "ToastsAsyncDemoStep2Text": "打包完成,正在下载...", + "ToastsAsyncDemoStep3Text": "下载完成,自动关窗", "ToastsWarning": "警告通知", + "ToastsAsyncText": "线程阻塞通知示例", "ToastsCloseTitle": "Toast 手动关闭", "ToastsCloseIntro": "不会自动关闭,需要人工点击关闭按钮,可通过设置 OnCloseAsync 回调委托自定义关闭弹窗后事件", "ToastsCloseNotificationText": "成功通知", diff --git a/src/BootstrapBlazor/Components/Toast/Toast.razor.cs b/src/BootstrapBlazor/Components/Toast/Toast.razor.cs index 353d02ca2d8..ff6f9fed31b 100644 --- a/src/BootstrapBlazor/Components/Toast/Toast.razor.cs +++ b/src/BootstrapBlazor/Components/Toast/Toast.razor.cs @@ -49,21 +49,19 @@ public partial class Toast /// /// 获得/设置 弹出框自动关闭时长 /// - protected string? DelayString => Options.IsAutoHide ? Convert.ToString(Options.Delay) : null; + private string? DelayString => Options.IsAutoHide ? Options.Delay.ToString() : null; /// - /// 获得/设置 是否开启动画效果 + /// 获得/设置 是否开启动画效果 /// - protected string? AnimationString => Options.Animation ? null : "false"; + private string? AnimationString => Options.Animation ? null : "false"; /// /// 获得/设置 ToastOption 实例 /// [Parameter] [NotNull] -#if NET6_0_OR_GREATER [EditorRequired] -#endif public ToastOption? Options { get; set; } /// @@ -71,7 +69,7 @@ public partial class Toast /// /// [CascadingParameter] - protected ToastContainer? ToastContainer { get; set; } + private ToastContainer? ToastContainer { get; set; } [Inject] [NotNull] @@ -100,6 +98,19 @@ protected override void OnParametersSet() Options.ErrorIcon ??= IconTheme.GetIconByKey(ComponentIcons.ToastErrorIcon); } + /// + /// + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (!firstRender) + { + await InvokeVoidAsync("update", Id); + } + } + /// /// /// diff --git a/src/BootstrapBlazor/Components/Toast/Toast.razor.js b/src/BootstrapBlazor/Components/Toast/Toast.razor.js index 3bbe14cf3e9..4640ff84110 100644 --- a/src/BootstrapBlazor/Components/Toast/Toast.razor.js +++ b/src/BootstrapBlazor/Components/Toast/Toast.razor.js @@ -12,7 +12,7 @@ export function init(id, invoke, callback) { return toast.toast._config.autohide } } - Data.set(id, toast) + Data.set(id, toast); if (toast.showProgress()) { toast.progressElement = toast.element.querySelector('.toast-progress') @@ -31,6 +31,25 @@ export function init(id, invoke, callback) { toast.toast.show() } +export function update(id) { + const t = Data.get(id); + const { element, toast } = t; + const autoHide = element.getAttribute('data-bs-autohide') !== 'false'; + if(autoHide) { + const delay = parseInt(element.getAttribute('data-bs-delay')); + const progressElement = element.querySelector('.toast-progress'); + + toast._config.autohide = autoHide; + toast._config.delay = delay; + + progressElement.style.width = '100%'; + progressElement.style.transition = `width linear ${delay / 1000}s`; + EventHandler.on(progressElement, 'transitionend', e => { + toast.hide(); + }); + } +} + export function dispose(id) { const toast = Data.get(id) Data.remove(id) diff --git a/src/BootstrapBlazor/Components/Toast/ToastContainer.razor.cs b/src/BootstrapBlazor/Components/Toast/ToastContainer.razor.cs index d8abd0d6c77..42a84d7042a 100644 --- a/src/BootstrapBlazor/Components/Toast/ToastContainer.razor.cs +++ b/src/BootstrapBlazor/Components/Toast/ToastContainer.razor.cs @@ -75,7 +75,12 @@ private async Task Show(ToastOption option) return; } } - Toasts.Add(option); + + // support update content + if (!Toasts.Contains(option)) + { + Toasts.Add(option); + } await InvokeAsync(StateHasChanged); }