UI/PixelDropdown.razor
@using System
@using System.Collections.Generic
@using Sandbox
@using Sandbox.UI
@inherits Panel
@namespace BlockParty
@attribute [StyleSheet( "PixelDropdown.razor.scss" )]

@* A minimal, fully self-styled dropdown. s&box stylesheets do NOT cascade into a child Razor
   component's internals, so the engine DropDown can't be reskinned from a parent stylesheet — and
   we want the blocky/pixel look, not the default. This component owns its own DOM + stylesheet.
   Mouse-driven: click the header to open, click an option to pick it (and close), click anywhere
   else (the full-screen backdrop) to dismiss without changing the value. The menu hangs below the
   header by default; OpenUp stacks it above instead (for dropdowns near the bottom of the screen). *@
<root class="pixel-dropdown @(OpenUp ? "up" : "down") @(_open ? "open" : "")">
	<div class="header" onclick=@Toggle>
		<label class="value">@CurrentLabel</label>
		@* Points toward where the menu appears while closed, and back toward the header while open. *@
		<label class="arrow">@(_open != OpenUp ? "▲" : "▼")</label>
	</div>

	@if ( _open )
	{
		@* Full-screen catch: any click outside the option list closes the menu. Drawn behind the
		   menu (earlier sibling), stretched well past the reference viewport in every direction. *@
		<div class="backdrop" onclick=@Close></div>

		<div class="menu">
			@for ( int i = 0; i < OptionCount; i++ )
			{
				var idx = i;   // capture for the click handler
				<div class="option @(i == Selected ? "sel" : "")" onclick=@(() => Choose( idx ))>
					<label>@Options[idx]</label>
				</div>
			}
		</div>
	}
</root>

@code
{
	/// <summary>The option labels, top-to-bottom.</summary>
	[Parameter] public List<string> Options { get; set; } = new();

	/// <summary>Index of the currently-selected option.</summary>
	[Parameter] public int Selected { get; set; }

	/// <summary>Raised with the new index when the player picks a different option.</summary>
	[Parameter] public Action<int> OnSelected { get; set; }

	/// <summary>Open the option list above the header instead of below it.</summary>
	[Parameter] public bool OpenUp { get; set; }

	private bool _open;

	private int OptionCount => Options?.Count ?? 0;

	private string CurrentLabel =>
		Options is not null && Selected >= 0 && Selected < Options.Count ? Options[Selected] : "";

	private void Toggle()
	{
		_open = !_open;
		StateHasChanged();
	}

	private void Close()
	{
		_open = false;
		StateHasChanged();
	}

	private void Choose( int index )
	{
		_open = false;
		if ( index != Selected )
		{
			Audio.PlaySfx( SfxType.MenuBlip );
			Selected = index;
			OnSelected?.Invoke( index );
		}
		StateHasChanged();
	}

	protected override int BuildHash() => System.HashCode.Combine( Selected, _open, OptionCount, OpenUp );
}