UI/Components/Toast/Toast.razor

A Blazor-like/sandbox UI component for a toast notification. Renders title, message, optional icon and copy button, computes a tone CSS class, supports dismiss and clipboard copy with callbacks.

File Access
@namespace Sunless.Libraries.UI
@using Sandbox
@using Sandbox.UI
@attribute [StyleSheet]
@inherits Panel

<root>
	@if ( !string.IsNullOrEmpty( Message ) )
	{
		<div class="toast toast-@ToneClass">
			@if ( !string.IsNullOrEmpty( Icon ) )
			{
				<Icon Name=@Icon Size=@(IconSize.Large) ExtraClass="toast-icon" />
			}
			<div class="toast-body" @onclick=@(() => OnDismiss?.Invoke())>
				@if ( !string.IsNullOrEmpty( Title ) )
				{
					<div class="toast-title">@Title</div>
				}
				<div class="toast-message">@Message</div>
			</div>
			@if ( ShowCopy )
			{
				<div class="toast-copy" @onclick=@CopyValueToClipboard>
					<Icon Name="content_copy" Size=@(IconSize.Medium) />
				</div>
			}
		</div>
	}
</root>

@code
{
	public string Title { get; set; } = "";
	public string Message { get; set; }
	public string Icon { get; set; }
	public ToastTone Tone { get; set; } = ToastTone.Default;
	public Action OnDismiss { get; set; }
	public bool ShowCopy { get; set; }
	public string CopyValue { get; set; }
	public Action OnCopied { get; set; }
	public Action OnCopyFailed { get; set; }

	string ToneClass => Tone switch
	{
		ToastTone.Ok     => "ok",
		ToastTone.Warn   => "warn",
		ToastTone.Danger => "danger",
		ToastTone.Info   => "info",
		_                => "default",
	};

	void CopyValueToClipboard()
	{
		var s = string.IsNullOrEmpty( CopyValue ) ? Message : CopyValue;
		if ( string.IsNullOrEmpty( s ) ) return;

		try
		{
			Sandbox.UI.Clipboard.SetText( s );
			OnCopied?.Invoke();
		}
		catch ( Exception e )
		{
			Log.Warning( $"[Toast] clipboard copy failed: {e.Message}" );
			OnCopyFailed?.Invoke();
		}
	}

	protected override int BuildHash() => HashCode.Combine( Title, Message, Icon, Tone, ShowCopy, CopyValue );
}