UI/Controls/StringQueryPopup.razor
@using Sandbox;
@using Sandbox.UI;
@inherits Panel
@namespace Sandbox

<root class="popup prompt-popup">

    <div class="inner popup-window" onclick:preventDefault=@true>
        <div class="popup-header">
            <h2>@Title</h2>
            <Button Icon="close" class="popup-close" @onclick=@(() => Delete()) />
        </div>

        <div class="popup-body">
            @if ( !string.IsNullOrEmpty( Prompt ) )
            {
                <h3>@Prompt</h3>
            }

            @if ( ShowInput )
            {
                <TextEntry class="menu-input" Placeholder=@Placeholder Value:bind=@Value />
            }
        </div>

        <div class="popup-footer">
            <Button Text="#spawnmenu.common.cancel" class="menu-action soft" @onclick=@(() => Delete()) />
            <Button Text=@ConfirmLabel Icon="check" class="menu-action primary popup-confirm" Disabled=@(ShowInput && string.IsNullOrWhiteSpace( Value )) @onclick=@OnConfirmClick />
        </div>
    </div>

</root>

@code
{
    /// <summary>Title shown at the top of the popup.</summary>
    public string Title { get; set; } = "Enter Value";

    /// <summary>Optional subtitle/prompt below the title.</summary>
    public string Prompt { get; set; }

    /// <summary>Placeholder text for the text entry.</summary>
    public string Placeholder { get; set; } = "Enter a value...";

    /// <summary>Label for the confirm button.</summary>
    public string ConfirmLabel { get; set; } = "Confirm";

    /// <summary>Pre-filled value in the text entry.</summary>
    public string InitialValue { get; set; } = "";

    /// <summary>When false, hides the text entry — acts as a simple confirm/deny dialog.</summary>
    public bool ShowInput { get; set; } = true;

    /// <summary>Called with the trimmed string when the user confirms.</summary>
    public Action<string> OnConfirm { get; set; }

    string Value { get; set; }

    protected override void OnAfterTreeRender( bool firstTime )
    {
        if ( firstTime )
            Value = InitialValue;
    }

    void OnConfirmClick()
    {
        if ( ShowInput && string.IsNullOrWhiteSpace( Value ) ) return;
        OnConfirm?.Invoke( Value?.Trim() ?? "" );
        Delete();
    }

    protected override void OnMouseDown( MousePanelEvent e )
    {
        base.OnMouseDown( e );

        // Click on the backdrop (not the inner panel) dismisses the popup
        if ( e.Target == this )
            Delete();
    }
}