Skip to content

Commit

Permalink
Fixes Blazored#148
Browse files Browse the repository at this point in the history
  • Loading branch information
Matija Gluhak committed Feb 21, 2023
1 parent f4ec68b commit 56d020b
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 42 deletions.
25 changes: 16 additions & 9 deletions samples/BlazorServer/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,30 @@
@inject NavigationManager NavigationManager

<h1>Blazored Toasts</h1>

<button class="btn btn-info" @onclick="@(() => toastService.ShowInfo("I'm an INFO message"))">Info Toast</button>
<button class="btn btn-success" @onclick="@(() => toastService.ShowSuccess("I'm a SUCCESS message with a custom heading"))">Success Toast</button>
<button class="btn btn-warning" @onclick="@(() => toastService.ShowWarning("I'm a WARNING message"))">Warning Toast</button>
<button class="btn btn-danger" @onclick="@(() => toastService.ShowError("I'm an ERROR message"))">Error Toast</button>
<button class="btn btn-info" @onclick="@OnShowHtml">Info Toast with HTML</button>
<button class="btn btn-info" @onclick="@(() => toastService.ShowInfo("Click to refresh the page", options => options.OnClick = () => NavigationManager.NavigateTo("/", true)))">
Info Toast with custom action on click
</button>
<div>
<button class="btn btn-info" @onclick="@(() => toastService.ShowInfo("I'm an INFO message"))">Info Toast</button>
<button class="btn btn-success" @onclick="@(() => toastService.ShowSuccess("I'm a SUCCESS message with a custom heading"))">Success Toast</button>
<button class="btn btn-warning" @onclick="@(() => toastService.ShowWarning("I'm a WARNING message"))">Warning Toast</button>
<button class="btn btn-danger" @onclick="@(() => toastService.ShowError("I'm an ERROR message"))">Error Toast</button>
<button class="btn btn-info" @onclick="@OnShowHtml">Info Toast with HTML</button>
<button class="btn btn-info" @onclick="@(() => toastService.ShowInfo("Click to refresh the page", options => options.OnClick = () => NavigationManager.NavigateTo("/", true)))">
Info Toast with custom action on click
</button>
</div>
<div class="mt-2">
<button class="btn btn-info" @onclick="@(() => toastService.ShowInfo("When you hover me i will stop timeout progress and will continue after you move out", settings => {settings.Timeout = 15; settings.PauseProgressOnHover = true; }))">Paused progress on hover</button>
<button class="btn btn-success" @onclick="@(() => toastService.ShowSuccess("When you hover me i will stop timeout progress and will close after 1 sec", settings => {settings.Timeout = 15; settings.PauseProgressOnHover = true; settings.ExtendedTimeout = 1;}))">Paused progress on hover with extended timeout</button>
</div>
<hr />


<h1>Blazored Toasts - Custom Component</h1>

<button class="btn btn-primary" @onclick="@(() => toastService.ShowToast<MyToastComponent>(settings => { settings.Timeout = 5; settings.ShowProgressBar = false; }))">Custom Toast</button>
<button class="btn btn-secondary" @onclick="@(() => toastService.ShowToast<MyToastComponent>(_toastParameters, settings => { settings.Timeout = 5; settings.ShowProgressBar = false; }))">Custom Toast with parameters</button>
<hr />


<h1>Blazored Toasts - Remove Toasts</h1>

<button class="btn btn-primary" @onclick="ClearAll">Clear All Toasts</button>
Expand Down
24 changes: 15 additions & 9 deletions samples/BlazorWebAssembly/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@
<PageTitle>Blazored Toast Samples</PageTitle>

<h1>Blazored Toasts</h1>

<button class="btn btn-info" id="InfoButton" @onclick="@(() => ToastService.ShowInfo("I'm an INFO message", settings => settings.IconType = IconType.None))">Info Toast</button>
<button class="btn btn-success" id="SuccessButton" @onclick="@(() => ToastService.ShowSuccess("I'm a SUCCESS message with a lot of text to see what a toast looks like when it's really big."))">Success Toast</button>
<button class="btn btn-warning" @onclick="@(() => ToastService.ShowWarning("I'm a WARNING message"))">Warning Toast</button>
<button class="btn btn-danger" @onclick="@(() => ToastService.ShowError("I'm an ERROR message"))">Error Toast</button>
<button class="btn btn-info" @onclick="@OnShowHtml">Info Toast with HTML</button>
<button class="btn btn-info" @onclick="@(() => ToastService.ShowInfo("Click to refresh the page", settings => settings.OnClick = () => NavigationManager.NavigateTo("/", true)))">
Info Toast with custom action on click
</button>
<div>
<button class="btn btn-info" id="InfoButton" @onclick="@(() => ToastService.ShowInfo("I'm an INFO message", settings => settings.IconType = IconType.None))">Info Toast</button>
<button class="btn btn-success" id="SuccessButton" @onclick="@(() => ToastService.ShowSuccess("I'm a SUCCESS message with a lot of text to see what a toast looks like when it's really big."))">Success Toast</button>
<button class="btn btn-warning" @onclick="@(() => ToastService.ShowWarning("I'm a WARNING message"))">Warning Toast</button>
<button class="btn btn-danger" @onclick="@(() => ToastService.ShowError("I'm an ERROR message"))">Error Toast</button>
<button class="btn btn-info" @onclick="@OnShowHtml">Info Toast with HTML</button>
<button class="btn btn-info" @onclick="@(() => ToastService.ShowInfo("Click to refresh the page", settings => settings.OnClick = () => NavigationManager.NavigateTo("/", true)))">
Info Toast with custom action on click
</button>
</div>
<div class="mt-2">
<button class="btn btn-info" @onclick="@(() => ToastService.ShowInfo("When you hover me i will stop timeout progress and will continue after you move mouse out", settings => {settings.PauseProgressOnHover = true;}))">Paused progress on hover</button>
<button class="btn btn-success" @onclick="@(() => ToastService.ShowSuccess("When you hover me i will stop timeout progress and will close after 1 sec", settings => {settings.PauseProgressOnHover = true; settings.ExtendedTimeout = 1;}))">Paused progress on hover with extended timeout</button>
</div>
<hr />
<h1>Blazored Toasts - Custom Component</h1>
Expand Down
4 changes: 2 additions & 2 deletions src/Blazored.Toast/BlazoredToast.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

@if (ChildContent is not null)
{
<div class="blazored-toast blazored-toast-component @Settings.AdditionalClasses">
<div class="blazored-toast blazored-toast-component @Settings.AdditionalClasses" @onmouseover="MouseOver" @onmouseout="MouseOut">
<CascadingValue Value="this" IsFixed="true">
@ChildContent
</CascadingValue>
Expand All @@ -19,7 +19,7 @@
}
else
{
<div class="blazored-toast @Settings.AdditionalClasses" @onclick="ToastClick">
<div class="blazored-toast @Settings.AdditionalClasses" @onclick="ToastClick" @onmouseover="MouseOver" @onmouseout="MouseOut">

@if (ShowIconDiv())
{
Expand Down
25 changes: 23 additions & 2 deletions src/Blazored.Toast/BlazoredToast.razor.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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);
}

Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/Blazored.Toast/BlazoredToast.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
16 changes: 12 additions & 4 deletions src/Blazored.Toast/BlazoredToasts.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,31 +80,39 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag
ShowProgressBar,
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout),
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout),
ToastLevel.Info => new ToastSettings(
"blazored-toast-info",
toastInstanceSettings.IconType ?? IconType,
toastInstanceSettings.Icon ?? InfoIcon ?? "",
ShowProgressBar,
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout),
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout),
ToastLevel.Success => new ToastSettings(
"blazored-toast-success",
toastInstanceSettings.IconType ?? IconType,
toastInstanceSettings.Icon ?? SuccessIcon ?? "",
ShowProgressBar,
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout),
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout),
ToastLevel.Warning => new ToastSettings(
"blazored-toast-warning",
toastInstanceSettings.IconType ?? IconType,
toastInstanceSettings.Icon ?? WarningIcon ?? "",
ShowProgressBar,
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout),
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout),
_ => throw new InvalidOperationException()
};
}
Expand Down
43 changes: 40 additions & 3 deletions src/Blazored.Toast/Configuration/ToastSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,56 @@ namespace Blazored.Toast.Configuration;

public class ToastSettings
{
/// <summary>
/// Add additional classes that will be applied to toast component
/// </summary>
public string AdditionalClasses { get; set; }
/// <summary>
/// Icon name, currently supporeted names for FontAwesome icons, Material icons
/// </summary>
public string? Icon { get; set; }
/// <summary>
///
/// </summary>
public IconType? IconType { get; set; }
/// <summary>
/// When enabled progress will be shown until toaster is closed based on <see cref="Timeout"/> value
/// </summary>
public bool ShowProgressBar { get; set; }
/// <summary>
/// When enabled and mouse is over the toast, timeout period will be paused.
/// Use this with <see cref="ExtendedTimeout"/>
/// </summary>
public bool PauseProgressOnHover { get; set; }
/// <summary>
///
/// </summary>
public bool ShowCloseButton { get; set; }
/// <summary>
///
/// </summary>
public Action? OnClick { get; set; }
/// <summary>
/// Timeout in seconds for toaster to close
/// </summary>
public int Timeout { get; set; }

/// <summary>
/// When <see cref="PauseProgressOnHover"/> is enabled, set ExtentedTimeout in seconds
/// to close toast after mouse loose focus.
/// Default value is 0, meaning no extended timeout
/// </summary>
public int ExtendedTimeout { get; set; }

public ToastSettings(
string additionalClasses,
IconType? iconType,
string icon,
bool showProgressBar,
bool showCloseButton,
Action? onClick,
int timeout)
int timeout,
bool pauseProgressOnHover,
int extendedTimeout)
{
AdditionalClasses = additionalClasses;
IconType = iconType;
Expand All @@ -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
Expand Down
39 changes: 27 additions & 12 deletions src/Blazored.Toast/CountdownTimer.cs
Original file line number Diff line number Diff line change
@@ -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<int, Task>? _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<int, Task> 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();
}

0 comments on commit 56d020b

Please sign in to comment.