diff --git a/samples/BlazorServer/Pages/Index.razor b/samples/BlazorServer/Pages/Index.razor index 69477b4..c7ca0ce 100644 --- a/samples/BlazorServer/Pages/Index.razor +++ b/samples/BlazorServer/Pages/Index.razor @@ -3,23 +3,30 @@ @inject NavigationManager NavigationManager

Blazored Toasts

- - - - - - - +
+ + + + + + +
+
+ + +

+

Blazored Toasts - Custom Component


+

Blazored Toasts - Remove Toasts

diff --git a/samples/BlazorWebAssembly/Pages/Index.razor b/samples/BlazorWebAssembly/Pages/Index.razor index cff4d2b..9c9588a 100644 --- a/samples/BlazorWebAssembly/Pages/Index.razor +++ b/samples/BlazorWebAssembly/Pages/Index.razor @@ -6,15 +6,21 @@ Blazored Toast Samples

Blazored Toasts

- - - - - - - +
+ + + + + + +
+
+ + + +

Blazored Toasts - Custom Component

diff --git a/src/Blazored.Toast/BlazoredToast.razor b/src/Blazored.Toast/BlazoredToast.razor index 3977242..e9b6673 100644 --- a/src/Blazored.Toast/BlazoredToast.razor +++ b/src/Blazored.Toast/BlazoredToast.razor @@ -4,7 +4,7 @@ @if (ChildContent is not null) { -
+
@ChildContent @@ -19,7 +19,7 @@ } else { -
+
@if (ShowIconDiv()) { diff --git a/src/Blazored.Toast/BlazoredToast.razor.cs b/src/Blazored.Toast/BlazoredToast.razor.cs index 430be98..72016e6 100644 --- a/src/Blazored.Toast/BlazoredToast.razor.cs +++ b/src/Blazored.Toast/BlazoredToast.razor.cs @@ -1,6 +1,8 @@ using Blazored.Toast.Configuration; using Blazored.Toast.Services; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using System.Diagnostics; namespace Blazored.Toast; @@ -23,13 +25,13 @@ protected override async Task OnInitializedAsync() { if (Settings.ShowProgressBar) { - _countdownTimer = new CountdownTimer(Settings.Timeout) + _countdownTimer = new CountdownTimer(Settings.Timeout, Settings.ExtendedTimeout) .OnTick(CalculateProgressAsync) .OnElapsed(Close); } else { - _countdownTimer = new CountdownTimer(Settings.Timeout) + _countdownTimer = new CountdownTimer(Settings.Timeout, Settings.ExtendedTimeout) .OnElapsed(Close); } @@ -42,6 +44,25 @@ protected override async Task OnInitializedAsync() public void Close() => ToastsContainer.RemoveToast(ToastId); + private void MouseOver(MouseEventArgs e) + { + if (Settings.PauseProgressOnHover) + { + Settings.ShowProgressBar= false; + _countdownTimer?.Pause(); + } + } + + private void MouseOut() + { + if (Settings.PauseProgressOnHover ) + { + Settings.ShowProgressBar = true; + _countdownTimer?.UnPause(); + } + } + + private async Task CalculateProgressAsync(int percentComplete) { _progress = 100 - percentComplete; diff --git a/src/Blazored.Toast/BlazoredToast.razor.css b/src/Blazored.Toast/BlazoredToast.razor.css index fb16846..701fd95 100644 --- a/src/Blazored.Toast/BlazoredToast.razor.css +++ b/src/Blazored.Toast/BlazoredToast.razor.css @@ -104,7 +104,7 @@ height: 6px; border-bottom-left-radius: .375rem; background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); - transition: all .5s linear; + transition: all .1s linear; } .blazored-toast-action { diff --git a/src/Blazored.Toast/BlazoredToasts.razor.cs b/src/Blazored.Toast/BlazoredToasts.razor.cs index f9d025e..742e3dd 100644 --- a/src/Blazored.Toast/BlazoredToasts.razor.cs +++ b/src/Blazored.Toast/BlazoredToasts.razor.cs @@ -81,6 +81,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag ShowCloseButton, toastInstanceSettings.OnClick, Timeout), + toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout, + toastInstanceSettings.PauseProgressOnHover, + toastInstanceSettings.ExtendedTimeout), ToastLevel.Info => new ToastSettings( "blazored-toast-info", toastInstanceSettings.IconType ?? IconType, @@ -88,6 +91,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag ShowProgressBar, ShowCloseButton, toastInstanceSettings.OnClick, + toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout, + toastInstanceSettings.PauseProgressOnHover, + toastInstanceSettings.ExtendedTimeout), Timeout), ToastLevel.Success => new ToastSettings( "blazored-toast-success", @@ -96,6 +102,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag ShowProgressBar, ShowCloseButton, toastInstanceSettings.OnClick, + toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout, + toastInstanceSettings.PauseProgressOnHover, + toastInstanceSettings.ExtendedTimeout), Timeout), ToastLevel.Warning => new ToastSettings( "blazored-toast-warning", @@ -104,6 +113,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag ShowProgressBar, ShowCloseButton, toastInstanceSettings.OnClick, + toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout, + toastInstanceSettings.PauseProgressOnHover, + toastInstanceSettings.ExtendedTimeout), Timeout), _ => throw new InvalidOperationException() }; diff --git a/src/Blazored.Toast/Configuration/ToastSettings.cs b/src/Blazored.Toast/Configuration/ToastSettings.cs index 23de351..e20f9df 100644 --- a/src/Blazored.Toast/Configuration/ToastSettings.cs +++ b/src/Blazored.Toast/Configuration/ToastSettings.cs @@ -5,14 +5,46 @@ namespace Blazored.Toast.Configuration; public class ToastSettings { + /// + /// Add additional classes that will be applied to toast component + /// public string AdditionalClasses { get; set; } + /// + /// Icon name, currently supporeted names for FontAwesome icons, Material icons + /// public string? Icon { get; set; } + /// + /// + /// public IconType? IconType { get; set; } + /// + /// When enabled progress will be shown until toaster is closed based on value + /// public bool ShowProgressBar { get; set; } + /// + /// When enabled and mouse is over the toast, timeout period will be paused. + /// Use this with + /// + public bool PauseProgressOnHover { get; set; } + /// + /// + /// public bool ShowCloseButton { get; set; } + /// + /// + /// public Action? OnClick { get; set; } + /// + /// Timeout in seconds for toaster to close + /// public int Timeout { get; set; } - + /// + /// When is enabled, set ExtentedTimeout in seconds + /// to close toast after mouse loose focus. + /// Default value is 0, meaning no extended timeout + /// + public int ExtendedTimeout { get; set; } + public ToastSettings( string additionalClasses, IconType? iconType, @@ -20,7 +52,9 @@ public ToastSettings( bool showProgressBar, bool showCloseButton, Action? onClick, - int timeout) + int timeout, + bool pauseProgressOnHover, + int extendedTimeout) { AdditionalClasses = additionalClasses; IconType = iconType; @@ -29,13 +63,16 @@ public ToastSettings( ShowCloseButton = showCloseButton; OnClick = onClick; Timeout = timeout; + PauseProgressOnHover = pauseProgressOnHover; + ExtendedTimeout = extendedTimeout; if (onClick is not null) { AdditionalClasses += " blazored-toast-action"; } + ExtendedTimeout = extendedTimeout; } - + #pragma warning disable CS8618 internal ToastSettings() { } #pragma warning restore CS8618 diff --git a/src/Blazored.Toast/CountdownTimer.cs b/src/Blazored.Toast/CountdownTimer.cs index 81ec3e3..5b6f948 100644 --- a/src/Blazored.Toast/CountdownTimer.cs +++ b/src/Blazored.Toast/CountdownTimer.cs @@ -1,57 +1,72 @@ -namespace Blazored.Toast; +using System.Threading; +namespace Blazored.Toast; internal class CountdownTimer : IDisposable { - private readonly PeriodicTimer _timer; + private PeriodicTimer _timer; private readonly int _ticksToTimeout; private readonly CancellationToken _cancellationToken; + private readonly int _extendedTimeout; private int _percentComplete; - + private bool _isPaused; private Func? _tickDelegate; private Action? _elapsedDelegate; - - internal CountdownTimer(int timeout, CancellationToken cancellationToken = default) + internal CountdownTimer(int timeout, int extendedTimeout = 0, CancellationToken cancellationToken = default) { _ticksToTimeout = 100; _timer = new PeriodicTimer(TimeSpan.FromMilliseconds(timeout * 10)); _cancellationToken = cancellationToken; + _extendedTimeout = extendedTimeout; } - internal CountdownTimer OnTick(Func updateProgressDelegate) { _tickDelegate = updateProgressDelegate; return this; } - internal CountdownTimer OnElapsed(Action elapsedDelegate) { _elapsedDelegate = elapsedDelegate; return this; } - internal async Task StartAsync() { _percentComplete = 0; await DoWorkAsync(); } - + internal void Pause() + { + _isPaused = true; + } + internal async Task UnPause() + { + _isPaused = false; + if (_extendedTimeout > 0) + { + _timer?.Dispose(); + _timer = new PeriodicTimer(TimeSpan.FromMilliseconds(_extendedTimeout * 10)); + await StartAsync(); + } + } private async Task DoWorkAsync() { while (await _timer.WaitForNextTickAsync(_cancellationToken) && !_cancellationToken.IsCancellationRequested) { - _percentComplete++; + + if (!_isPaused) + { + _percentComplete++; + } if (_tickDelegate != null) { await _tickDelegate(_percentComplete); } //await _tickDelegate?.Invoke(_percentComplete)!; - + if (_percentComplete == _ticksToTimeout) { _elapsedDelegate?.Invoke(); } } } - public void Dispose() => _timer.Dispose(); }