Ui/Punchboard/PbButton.razor

The component is a custom UI button. It renders a clickable button with optional text and icon, supports disabled and toggled states, and handles input events like mouse clicks and keyboard presses.

@namespace CitySim.Punchboard
@using Sandbox.UI
@inherits Panel

<root class="pb-btn @Kind.Css() @Size.Css() @CssClass @(Disabled ? "disabled" : "") @(Toggled ? "toggled" : "")">
	@if ( !string.IsNullOrEmpty( Icon ) )
	{
		<span class="pb-btn-icon">@Icon</span>
	}
	@if ( !string.IsNullOrEmpty( Text ) )
	{
		<span class="pb-btn-text">@(GameText.Plain(Text))</span>
	}
	@ChildContent
</root>

@code
{
	/// <summary>Content placed inside the component.</summary>
	[Parameter] public new RenderFragment ChildContent { get; set; }

	[Parameter] public string Text { get; set; }
	[Parameter] public string CssClass { get; set; }
	[Parameter] public string Icon { get; set; }
	[Parameter] public string Help { get; set; }

	/// <summary>Primary is the one main action on a screen. Use Ghost for cancel, Danger for destructive.</summary>
	[Parameter] public PbButtonKind Kind { get; set; } = PbButtonKind.Primary;
	[Parameter] public PbSize Size { get; set; } = PbSize.Normal;
	[Parameter] public bool Disabled { get; set; }

	/// <summary>Shows the pressed-in "on" state, for toggles such as a Ready button.</summary>
	[Parameter] public bool Toggled { get; set; }

	/// <summary>Called on click and on Enter or Space while focused.</summary>
	[Parameter] public Action Clicked { get; set; }

	protected override void OnParametersSet()
	{
		base.OnParametersSet();
		AcceptsFocus = !Disabled;
		Tooltip = Help;
		TooltipClass = "city-tooltip";
	}

	protected override void OnClick( MousePanelEvent e )
	{
		base.OnClick( e );
		Activate();
	}

	public override void OnButtonEvent( ButtonEvent e )
	{
		base.OnButtonEvent( e );
		if ( Disabled || !HasFocus || !e.Pressed ) return;
		if ( e.Button == "enter" || e.Button == "space" ) Activate();
	}
	float lastActivation = -100;
	/// <summary>Shared semantic activation path; prevents pointer/key adapters firing twice in one frame.</summary>
	public void Activate() { if (Disabled || lastActivation==Time.Now) return; lastActivation=Time.Now; TabletopAudio.Click(); Clicked?.Invoke(); }

	protected override int BuildHash() => HashCode.Combine(GameText.LanguageCode, HashCode.Combine( Text, Icon, Kind, Size, Disabled, Toggled, CssClass ));
}