Demos/TarkovInventory/CratePanel.cs
using Goo;
using Sandbox;
using Sandbox.UI;

namespace Sandbox.TarkovInventory;

// The shared crate panel: a nine-slice worn-metal frame + orange ink-stamp header + body.
// Every TarkovStash column uses this so the three read as one designed unit. Falls back to
// flat colors when a skin texture is missing, so the layout builds before art lands.
internal static class CratePanel
{
    // width null + grow null -> shrink-wrap the body (used by the stash, which is grid-sized).
    // grow set -> flex-grow into available space (the avatar). Panels never shrink below content
    // (FlexShrink 0) so the stash grid keeps its exact pixel size (the DnD measurement depends on it).
    public static Container Panel( string key, string title, Container body,
        Length? width = null, float? grow = null, string? hint = null )
    {
        var skin = CrateTheme.Panel;
        return new Container
        {
            Key = key,
            Width = width,
            FlexGrow = grow,
            FlexShrink = 0,
            FlexDirection = FlexDirection.Column,
            Overflow = OverflowMode.Hidden,
            BackgroundColor = skin is null ? CrateTheme.Steel : (Color?)null,
            BorderColor = skin is null ? CrateTheme.Edge : (Color?)null,
            BorderImageSource = skin,
            BorderImageWidthLeft = CrateTheme.PanelSlice,
            BorderImageWidthTop = CrateTheme.PanelSlice,
            BorderImageWidthRight = CrateTheme.PanelSlice,
            BorderImageWidthBottom = CrateTheme.PanelSlice,
            BorderWidth = skin is null ? 1f : CrateTheme.PanelBorder,
            BorderImageRepeat = BorderImageRepeat.Stretch,
            BorderImageFill = BorderImageFill.Filled,
            Children =
            {
                Header( title, hint ),
                body,
            },
        };
    }

    static Container Header( string title, string? hint )
    {
        var skin = CrateTheme.Header;
        var bar = new Container
        {
            Key = "crate-hdr",
            Height = CrateTheme.HeaderHeight,
            FlexShrink = 0,
            FlexDirection = FlexDirection.Row,
            AlignItems = Align.Center,
            JustifyContent = hint is null ? Justify.Center : Justify.SpaceBetween,
            PaddingLeft = 14f,
            PaddingRight = 14f,
            BackgroundColor = skin is null ? CrateTheme.Stamp : (Color?)null,
            BorderImageSource = skin,
            BorderImageWidthLeft = CrateTheme.HeaderSliceX,
            BorderImageWidthRight = CrateTheme.HeaderSliceX,
            BorderImageWidthTop = CrateTheme.HeaderSliceY,
            BorderImageWidthBottom = CrateTheme.HeaderSliceY,
            BorderLeftWidth = skin is null ? 0f : CrateTheme.HeaderCap,
            BorderRightWidth = skin is null ? 0f : CrateTheme.HeaderCap,
            BorderTopWidth = skin is null ? 0f : CrateTheme.HeaderEdge,
            BorderBottomWidth = skin is null ? 0f : CrateTheme.HeaderEdge,
            BorderImageRepeat = BorderImageRepeat.Stretch,
            BorderImageFill = BorderImageFill.Filled,
            Children =
            {
                new Text( title )
                {
                    Key = "t",
                    FontColor = CrateTheme.StampInk,
                    FontFamily = CrateTheme.Font,
                    FontSize = 22f,
                    FontWeight = 700,
                    LetterSpacing = CrateTheme.Tracking,
                },
            },
        };
        if ( hint is not null )
            bar.Children.Add( new Text( hint )
            {
                Key = "hint",
                FontColor = CrateTheme.StampInk,
                FontFamily = CrateTheme.Font,
                FontSize = 13f,
            } );
        return bar;
    }
}