From 01813201a651ba5780b7f59d8432cf916681dbf7 Mon Sep 17 00:00:00 2001 From: Chris Sainty Date: Fri, 8 May 2020 15:02:20 +0100 Subject: [PATCH] Adds progress bar option to toasts --- README.md | 12 ++++ samples/BlazorServer/Shared/MainLayout.razor | 3 +- src/Blazored.Toast/BlazoredToast.razor | 9 ++- src/Blazored.Toast/BlazoredToast.razor.cs | 23 ++++--- src/Blazored.Toast/BlazoredToasts.razor.cs | 9 +-- .../Configuration/ToastSettings.cs | 5 +- src/Blazored.Toast/CountdownTimer.cs | 60 +++++++++++++++++++ src/Blazored.Toast/wwwroot/blazored-toast.css | 37 +++++++++++- 8 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 src/Blazored.Toast/CountdownTimer.cs diff --git a/README.md b/README.md index d28280f..f243a12 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,18 @@ To show a toast just click one of the buttons below. ``` Full examples for client and server-side Blazor are included in the [samples](https://github.com/Blazored/Toast/tree/master/samples). +### Show Progress Bar +You can display a progress bar which gives a visual indicator of the time remaining before the toast will disappear. In order to show the progress bar set the `ShowProgressBar` parameter to true. + +```html + +``` + +### Remove Toasts When Navigating +If you wish to clear any visible toasts when the user navigates to a new page you can enable the `RemoveToastsOnNavigation` parameter. Setting this to true will remove any visible toasts whenever the `LocationChanged` event fires. + ## FAQ ### The toasts are not showing - Check the `z-index` of your other `DOM Elements`, make sure that the `.blazored-toast-container` has a higher `z-index` than the other components. diff --git a/samples/BlazorServer/Shared/MainLayout.razor b/samples/BlazorServer/Shared/MainLayout.razor index d39d915..8dc80b9 100644 --- a/samples/BlazorServer/Shared/MainLayout.razor +++ b/samples/BlazorServer/Shared/MainLayout.razor @@ -7,7 +7,8 @@ ErrorIcon="error_outline" InfoIcon="school" SuccessIcon="done_outline" - WarningIcon="warning" /> + WarningIcon="warning" + ShowProgressBar="@true"/> diff --git a/src/Blazored.Toast/BlazoredToast.razor.cs b/src/Blazored.Toast/BlazoredToast.razor.cs index 4bf566f..8ba9231 100644 --- a/src/Blazored.Toast/BlazoredToast.razor.cs +++ b/src/Blazored.Toast/BlazoredToast.razor.cs @@ -1,7 +1,7 @@ using Blazored.Toast.Configuration; using Microsoft.AspNetCore.Components; using System; -using System.Timers; +using System.Threading.Tasks; namespace Blazored.Toast { @@ -13,14 +13,21 @@ public partial class BlazoredToast : IDisposable [Parameter] public ToastSettings ToastSettings { get; set; } [Parameter] public int Timeout { get; set; } - private Timer _timer; + private CountdownTimer _countdownTimer; + private int _progress = 100; protected override void OnInitialized() { - _timer = new Timer(Timeout * 1000); - _timer.Elapsed += (sender, args) => { Close(); }; - _timer.AutoReset = false; - _timer.Start(); + _countdownTimer = new CountdownTimer(Timeout); + _countdownTimer.OnTick += CalculateProgress; + _countdownTimer.OnElapsed += () => { Close(); }; + _countdownTimer.Start(); + } + + private async void CalculateProgress(int percentComplete) + { + _progress = 100 - percentComplete; + await InvokeAsync(StateHasChanged); } private void Close() @@ -30,8 +37,8 @@ private void Close() public void Dispose() { - _timer.Dispose(); - _timer = null; + _countdownTimer.Dispose(); + _countdownTimer = null; } } } diff --git a/src/Blazored.Toast/BlazoredToasts.razor.cs b/src/Blazored.Toast/BlazoredToasts.razor.cs index 5d76c13..c29eae3 100644 --- a/src/Blazored.Toast/BlazoredToasts.razor.cs +++ b/src/Blazored.Toast/BlazoredToasts.razor.cs @@ -28,6 +28,7 @@ public partial class BlazoredToasts [Parameter] public ToastPosition Position { get; set; } = ToastPosition.TopRight; [Parameter] public int Timeout { get; set; } = 5; [Parameter] public bool RemoveToastsOnNavigation { get; set; } + [Parameter] public bool ShowProgressBar { get; set; } private string PositionClass { get; set; } = string.Empty; internal List ToastList { get; set; } = new List(); @@ -77,16 +78,16 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag switch (level) { case ToastLevel.Error: - return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Error" : heading, message, IconType, "blazored-toast-error", ErrorClass, ErrorIcon); + return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Error" : heading, message, IconType, "blazored-toast-error", ErrorClass, ErrorIcon, ShowProgressBar); case ToastLevel.Info: - return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Info" : heading, message, IconType, "blazored-toast-info", InfoClass, InfoIcon); + return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Info" : heading, message, IconType, "blazored-toast-info", InfoClass, InfoIcon, ShowProgressBar); case ToastLevel.Success: - return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Success" : heading, message, IconType, "blazored-toast-success", SuccessClass, SuccessIcon); + return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Success" : heading, message, IconType, "blazored-toast-success", SuccessClass, SuccessIcon, ShowProgressBar); case ToastLevel.Warning: - return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Warning" : heading, message, IconType, "blazored-toast-warning", WarningClass, WarningIcon); + return new ToastSettings(string.IsNullOrWhiteSpace(heading) ? "Warning" : heading, message, IconType, "blazored-toast-warning", WarningClass, WarningIcon, ShowProgressBar); } throw new InvalidOperationException(); diff --git a/src/Blazored.Toast/Configuration/ToastSettings.cs b/src/Blazored.Toast/Configuration/ToastSettings.cs index 624b6c1..adbe70e 100644 --- a/src/Blazored.Toast/Configuration/ToastSettings.cs +++ b/src/Blazored.Toast/Configuration/ToastSettings.cs @@ -10,7 +10,8 @@ public ToastSettings( IconType? iconType, string baseClass, string additionalClasses, - string icon) + string icon, + bool showProgressBar) { Heading = heading; Message = message; @@ -18,6 +19,7 @@ public ToastSettings( BaseClass = baseClass; AdditionalClasses = additionalClasses; Icon = icon; + ShowProgressBar = showProgressBar; } public string Heading { get; set; } @@ -26,5 +28,6 @@ public ToastSettings( public string AdditionalClasses { get; set; } public string Icon { get; set; } public IconType? IconType { get; set; } + public bool ShowProgressBar { get; set; } } } diff --git a/src/Blazored.Toast/CountdownTimer.cs b/src/Blazored.Toast/CountdownTimer.cs new file mode 100644 index 0000000..db007a1 --- /dev/null +++ b/src/Blazored.Toast/CountdownTimer.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using System.Timers; +using Microsoft.AspNetCore.Components; + +namespace Blazored.Toast +{ + internal class CountdownTimer : IDisposable + { + private Timer _timer; + private int _timeout; + private int _countdownTotal; + private int _percentComplete; + + internal Action OnTick; + internal Action OnElapsed; + + internal CountdownTimer(int timeout) + { + _countdownTotal = timeout; + _timeout = (_countdownTotal * 1000) / 100; + _percentComplete = 0; + SetupTimer(); + } + + internal void Start() + { + _timer.Start(); + } + + private void SetupTimer() + { + _timer = new Timer(_timeout); + _timer.Elapsed += HandleTick; + _timer.AutoReset = false; + } + + private void HandleTick(object sender, ElapsedEventArgs args) + { + _percentComplete++; + OnTick?.Invoke(_percentComplete); + + if (_percentComplete == 100) + { + OnElapsed?.Invoke(); + } + else + { + SetupTimer(); + Start(); + } + } + + public void Dispose() + { + _timer.Dispose(); + _timer = null; + } + } +} diff --git a/src/Blazored.Toast/wwwroot/blazored-toast.css b/src/Blazored.Toast/wwwroot/blazored-toast.css index ef0317a..48d97c6 100644 --- a/src/Blazored.Toast/wwwroot/blazored-toast.css +++ b/src/Blazored.Toast/wwwroot/blazored-toast.css @@ -19,6 +19,7 @@ .blazored-toast { display: flex; + position: relative; flex-direction: row; animation: fadein 1.5s; margin-bottom: 1rem; @@ -76,14 +77,48 @@ background-color: transparent; border: 0; -webkit-appearance: none; - color:inherit; + color: inherit; font-size: 1.25rem; } + .blazored-toast-body p { margin-bottom: 0; font-size: 1rem; } +.blazored-toast-progressbar { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 8px; + border-bottom-left-radius: .25rem; + border-bottom-right-radius: .25rem; +} + +.blazored-toast-info .blazored-toast-progressbar > span { + background-color: #34a9ad; +} + +.blazored-toast-success .blazored-toast-progressbar > span { + background-color: #5fba7d; +} + +.blazored-toast-warning .blazored-toast-progressbar > span { + background-color: #c1c13e; +} + +.blazored-toast-error .blazored-toast-progressbar > span { + background-color: #ba5e5e; +} + +.blazored-toast-progressbar > span { + position: absolute; + filter: brightness(75%); + height: 8px; + border-bottom-left-radius: .25rem; +} + @keyframes fadein { from { opacity: 0;