Skip to content

Commit

Permalink
Pause on hover feature #148 (#203)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cvijo authored Feb 23, 2023
1 parent 99e244f commit eed68ee
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 44 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 over me, I will pause the timeout progress and resume it once you move away", settings => {settings.Timeout = 15; settings.PauseProgressOnHover = true; }))">Pause progress on hover</button>
<button class="btn btn-success" @onclick="@(() => toastService.ShowSuccess("When you hover over me, I will stop the timeout progress and close after an extended timeout of 1 second", settings => {settings.Timeout = 15; settings.PauseProgressOnHover = true; settings.ExtendedTimeout = 1;}))">Pause 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
4 changes: 4 additions & 0 deletions samples/BlazorWebAssembly/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<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 class="mt-2">
<button class="btn btn-info" @onclick="@(() => ToastService.ShowInfo("When you hover over me, I will pause the timeout progress and resume it once you move away", settings => {settings.Timeout = 15; settings.PauseProgressOnHover = true; }))">Pause progress on hover</button>
<button class="btn btn-success" @onclick="@(() => ToastService.ShowSuccess("When you hover over me, I will stop the timeout progress and close after an extended timeout of 1 second", settings => {settings.Timeout = 15; settings.PauseProgressOnHover = true; settings.ExtendedTimeout = 1;}))">Pause 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="TryPauseCountdown" @onmouseout="TryResumeCountdown">
<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="TryPauseCountdown" @onmouseout="TryResumeCountdown">

@if (ShowIconDiv())
{
Expand Down
24 changes: 22 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 Down Expand Up @@ -28,13 +30,13 @@ protected override async Task OnInitializedAsync()

if (Settings.ShowProgressBar)
{
_countdownTimer = new CountdownTimer(Settings.Timeout)
_countdownTimer = new CountdownTimer(Settings.Timeout, Settings.ExtendedTimeout!.Value)
.OnTick(CalculateProgressAsync)
.OnElapsed(Close);
}
else
{
_countdownTimer = new CountdownTimer(Settings.Timeout)
_countdownTimer = new CountdownTimer(Settings.Timeout, Settings.ExtendedTimeout!.Value)
.OnElapsed(Close);
}

Expand All @@ -47,6 +49,24 @@ protected override async Task OnInitializedAsync()
public void Close()
=> ToastsContainer.RemoveToast(ToastId);

private void TryPauseCountdown()
{
if (Settings.PauseProgressOnHover!.Value)
{
Settings.ShowProgressBar= false;
_countdownTimer?.Pause();
}
}

private void TryResumeCountdown()
{
if (Settings.PauseProgressOnHover!.Value )
{
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
27 changes: 19 additions & 8 deletions src/Blazored.Toast/BlazoredToasts.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ public partial class BlazoredToasts
{
[Inject] private IToastService ToastService { get; set; } = default!;
[Inject] private NavigationManager NavigationManager { get; set; } = default!;

[Parameter] public IconType IconType { get; set; } = IconType.Blazored;
[Parameter] public IconType IconType { get; set; } = IconType.Blazored;
[Parameter] public string? InfoClass { get; set; }
[Parameter] public string? InfoIcon { get; set; }
[Parameter] public string? SuccessClass { get; set; }
Expand All @@ -27,7 +26,8 @@ public partial class BlazoredToasts
[Parameter] public RenderFragment? CloseButtonContent { get; set; }
[Parameter] public bool ShowCloseButton { get; set; } = true;
[Parameter] public bool DisableTimeout { get; set; }

[Parameter] public bool PauseProgressOnHover { get; set; } = false;
[Parameter] public int ExtendedTimeout { get; set; }
private string PositionClass { get; set; } = string.Empty;
private List<ToastInstance> ToastList { get; set; } = new();
private Queue<ToastInstance> ToastWaitingQueue { get; set; } = new();
Expand Down Expand Up @@ -63,7 +63,10 @@ private ToastSettings BuildCustomToastSettings(Action<ToastSettings>? settings)
{
var instanceToastSettings = new ToastSettings();
settings?.Invoke(instanceToastSettings);
instanceToastSettings.Timeout = instanceToastSettings.Timeout == 0 ? Timeout : instanceToastSettings.Timeout;
instanceToastSettings.DisableTimeout ??= DisableTimeout;
instanceToastSettings.PauseProgressOnHover ??= PauseProgressOnHover;
instanceToastSettings.ExtendedTimeout ??= ExtendedTimeout;

return instanceToastSettings;
}
Expand All @@ -83,7 +86,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.DisableTimeout ?? DisableTimeout),
toastInstanceSettings.DisableTimeout ?? DisableTimeout,
toastInstanceSettings.PauseProgressOnHover ?? PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout ?? ExtendedTimeout),
ToastLevel.Info => new ToastSettings(
$"blazored-toast-info {toastInstanceSettings.AdditionalClasses}",
toastInstanceSettings.IconType ?? IconType,
Expand All @@ -92,7 +97,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.DisableTimeout ?? DisableTimeout),
toastInstanceSettings.DisableTimeout ?? DisableTimeout,
toastInstanceSettings.PauseProgressOnHover ?? PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout ?? ExtendedTimeout),
ToastLevel.Success => new ToastSettings(
$"blazored-toast-success {toastInstanceSettings.AdditionalClasses}",
toastInstanceSettings.IconType ?? IconType,
Expand All @@ -101,7 +108,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.DisableTimeout ?? DisableTimeout),
toastInstanceSettings.DisableTimeout ?? DisableTimeout,
toastInstanceSettings.PauseProgressOnHover ?? PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout ?? ExtendedTimeout),
ToastLevel.Warning => new ToastSettings(
$"blazored-toast-warning {toastInstanceSettings.AdditionalClasses}",
toastInstanceSettings.IconType ?? IconType,
Expand All @@ -110,7 +119,9 @@ private ToastSettings BuildToastSettings(ToastLevel level, RenderFragment messag
ShowCloseButton,
toastInstanceSettings.OnClick,
toastInstanceSettings.Timeout == 0 ? Timeout : toastInstanceSettings.Timeout,
toastInstanceSettings.DisableTimeout ?? DisableTimeout),
toastInstanceSettings.DisableTimeout ?? DisableTimeout,
toastInstanceSettings.PauseProgressOnHover ?? PauseProgressOnHover,
toastInstanceSettings.ExtendedTimeout ?? ExtendedTimeout),
_ => throw new InvalidOperationException()
};
}
Expand All @@ -125,7 +136,7 @@ private void ShowToast(ToastLevel level, RenderFragment message, Action<ToastSet
if (ToastList.Count < MaxToastCount)
{
ToastList.Add(toast);

StateHasChanged();
}
else
Expand Down
74 changes: 65 additions & 9 deletions src/Blazored.Toast/Configuration/ToastSettings.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,71 @@
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;

namespace Blazored.Toast.Configuration;

namespace Blazored.Toast.Configuration;
public class ToastSettings
{
/// <summary>
/// The <c>AdditionalClasses</c> property is used to specify additional CSS classes that will be applied to the toast component.
/// </summary>
/// <remarks>
/// By setting this property, you can customize the appearance of the toast notification and apply custom styles to it. Note that the value of the <c>AdditionalClasses</c> property should be a string containing one or more CSS class names separated by spaces.
/// </remarks>
public string AdditionalClasses { get; set; }

/// <summary>
/// The possible values for the <c>Icon</c> property are names of icons from the FontAwesome and Material icon libraries. By providing the name of the desired icon, the corresponding icon will be displayed on the notification.
/// </summary>
public string? Icon { get; set; }

/// <summary>
/// The <c>IconType</c> property determines the type of icon that will be displayed on the toast notification. This property is an optional feature that can be used to provide users with additional visual cues about the notification.
/// </summary>
public IconType? IconType { get; set; }

/// <summary>
/// Enabling the <c>ShowProgressBar</c> property provides visual feedback on the remaining time for the toast notification based on the <c>Timeout</c> property.
/// </summary>
public bool ShowProgressBar { get; set; }

/// <summary>
/// When the <c>PauseProgressOnHover</c> property is enabled, the timeout period for the toast notification will be paused when the user hovers the mouse over the toast.
/// </summary>
/// <remarks>
/// This can be useful for providing users with more time to read the contents of the notification. By using the <c>PauseProgressOnHover</c> property in conjunction with the <c>ExtendedTimeout</c> property, you can create a toast notification that is more user-friendly and provides better visual feedback to the user.
/// </remarks>
public bool? PauseProgressOnHover { get; set; }

/// <summary>
/// The ShowCloseButton property determines whether or not the close button is displayed on the toast notification.
/// </summary>
public bool ShowCloseButton { get; set; }

/// <summary>
/// The <c>OnClick</c> property is an optional action that is triggered when the user clicks on the toast notification.
/// </summary>
/// <remarks>
/// This property allows you to define a custom action that will be executed when the user interacts with the notification, such as opening a new window or performing some other action.
/// </remarks>
public Action? OnClick { get; set; }

/// <summary>
/// The <c>Timeout</c> property determines the amount of time, in seconds, that the toast notification will be displayed before it is automatically closed.
/// </summary>
/// <remarks>
/// By setting this property, you can control the duration of the notification and ensure that it is visible to the user for an appropriate amount of time.
/// </remarks>
public int Timeout { get; set; }

/// <summary>
/// When <c>PauseProgressOnHover</c> is enabled, the <c>ExtendedTimeout</c> property determines the amount of time, in seconds, that the toast notification will remain visible after the user moves the mouse away from it.
/// </summary>
/// <remarks>
/// Default value is <c>0</c>, meaning no extended timeout.
/// </remarks>
public int? ExtendedTimeout { get; set; }

/// <summary>
/// Setting the <c>DisableTimeout</c> property to true will prevent the toast notification from automatically closing, ignoring <c>Timeout</c> and <c>ExtendedTimeout</c> properties. A close button will be shown to allow the user to dismiss the notification manually.
/// </summary>
public bool? DisableTimeout { get; set; }

public ToastSettings(
string additionalClasses,
IconType? iconType,
Expand All @@ -22,7 +74,9 @@ public ToastSettings(
bool showCloseButton,
Action? onClick,
int timeout,
bool disableTimeout)
bool disableTimeout,
bool pauseProgressOnHover,
int extendedTimeout)
{
AdditionalClasses = additionalClasses;
IconType = iconType;
Expand All @@ -31,14 +85,16 @@ public ToastSettings(
ShowCloseButton = showCloseButton;
OnClick = onClick;
Timeout = timeout;
DisableTimeout = disableTimeout;
DisableTimeout= disableTimeout;
PauseProgressOnHover = pauseProgressOnHover;
ExtendedTimeout = extendedTimeout;

if (onClick is not null)
{
AdditionalClasses += " blazored-toast-action";
}
}

#pragma warning disable CS8618
internal ToastSettings() { }
#pragma warning restore CS8618
Expand Down
Loading

0 comments on commit eed68ee

Please sign in to comment.