Demos/TarkovInventory/SplitModalBlob.cs
using System;
using Goo;
using Sandbox.UI;
using static Sandbox.TarkovInventory.StashTheme;

namespace Sandbox.TarkovInventory;

// Pure stateless presenter for the stack-split modal (centered window, backdrop dim, peel-off slider); everything arrives as Props. The controller owns the _splitId/_splitAmount state and DoSplit.
internal static class SplitModalBlob
{
    public readonly record struct Props(
        StashItem Item,
        int Amount,
        bool HasRoom,
        Action OnCancel,
        Action<float> OnAmount,
        Action OnSplit );

    public static Container Build( Props p ) => new Container
    {
        Key = "split-slot",
        Position = PositionMode.Absolute,
        Left = 0f, Top = 0f,
        Width = Length.Percent( 100 ),
        Height = Length.Percent( 100 ),
        JustifyContent = Justify.Center,
        AlignItems = Align.Center,
        BackgroundColor = Backdrop,
        PointerEvents = PointerEvents.All,
        OnClick = _ => p.OnCancel(),   // click outside the panel cancels
        Children =
        {
            new Container
            {
                Key = "panel",
                Width = 360f,
                FlexDirection = FlexDirection.Column,
                Gap = 16f,
                Padding = 24f,
                BackgroundColor = CrateTheme.Steel,
                BorderWidth = 1f,
                BorderColor = CrateTheme.Edge,
                BorderRadius = 6f,
                PointerEvents = PointerEvents.All,
                OnClick = e => e.StopPropagation(),
                Children =
                {
                    new Text( $"Split {p.Item.Name}" ) { Key = "title", FontColor = Title, FontFamily = LabelFont, FontSize = 24f, FontWeight = 700 },
                    Goo.Controls.Slider(
                        value: p.Amount, min: 1f, max: p.Item.Count - 1, step: 1f,
                        onChanged: p.OnAmount, key: "slider" ),
                    new Text( $"{p.Amount} / {p.Item.Count}" ) { Key = "amt", FontColor = Title, FontFamily = LabelFont, FontSize = 18f },
                    Buttons( p ),
                },
            },
        },
    };

    static Container Buttons( Props p ) => new Container
    {
        Key = "btns",
        FlexDirection = FlexDirection.Row,
        Gap = 12f,
        JustifyContent = Justify.FlexEnd,
        AlignItems = Align.Center,
        Children =
        {
            p.HasRoom
                ? new Container { Key = "hint", PointerEvents = PointerEvents.None }
                : new Container
                {
                    Key = "hint",
                    PointerEvents = PointerEvents.None,
                    Children = { new Text( "no free space" ) { Key = "t", FontColor = Faint, FontFamily = LabelFont, FontSize = 14f } },
                },
            new Container
            {
                Key = "cancel",
                Padding = 10f,
                BackgroundColor = BtnOff,
                BorderRadius = 6f,
                PointerEvents = PointerEvents.All,
                OnClick = _ => p.OnCancel(),
                Children = { new Text( "Cancel" ) { Key = "t", FontColor = Title, FontFamily = LabelFont, FontSize = 16f } },
            },
            new Container
            {
                Key = "split",
                Padding = 10f,
                BackgroundColor = p.HasRoom ? CrateTheme.Stamp : BtnGrey,
                BorderRadius = 6f,
                PointerEvents = PointerEvents.All,
                OnClick = _ => { if ( p.HasRoom ) p.OnSplit(); },
                Children = { new Text( "Split" ) { Key = "t", FontColor = p.HasRoom ? CrateTheme.StampInk : Title, FontFamily = LabelFont, FontSize = 16f } },
            },
        },
    };
}