Examples/Invoke-ComponentDemo.ps1

Import-Module "$PSScriptRoot/../Elm.psd1" -Force

# ---------------------------------------------------------------------------
# Component demo
# Two independent counters, each a self-contained component with its own
# model, update, and view. The parent routes ComponentMsg to each one.
# Demonstrates: New-ElmComponent, New-ElmComponentMsg, message routing
# ---------------------------------------------------------------------------

# ---------------------------------------------------------------------------
# Counter component definition
# A component is a plain PSCustomObject with Init, Update, and View scriptblocks
# ---------------------------------------------------------------------------

$Counter = [PSCustomObject]@{
    Init   = {
        param($Label)
        [PSCustomObject]@{ Label = $Label; Count = 0 }
    }
    Update = {
        param($msg, $model)
        switch ($msg.Type) {
            'Increment' { [PSCustomObject]@{ Label = $model.Label; Count = $model.Count + 1 } }
            'Decrement' { [PSCustomObject]@{ Label = $model.Label; Count = $model.Count - 1 } }
            default     { $model }
        }
    }
    View   = {
        param($model)
        $labelStyle = New-ElmStyle -Foreground 'BrightCyan' -Bold
        $countStyle = New-ElmStyle -Foreground 'BrightWhite'
        $boxStyle   = New-ElmStyle -Border 'Rounded' -Padding @(0, 2) -Width 24
        New-ElmBox -Style $boxStyle -Children @(
            New-ElmText -Content $model.Label -Style $labelStyle
            New-ElmText -Content " $($model.Count)"  -Style $countStyle
        )
    }
}

# ---------------------------------------------------------------------------
# Parent Init
# ---------------------------------------------------------------------------

$init = {
    [PSCustomObject]@{
        Model = [PSCustomObject]@{
            LeftModel  = & $Counter.Init 'Left'
            RightModel = & $Counter.Init 'Right'
            Focus      = 'Left'   # which counter receives key input
        }
        Cmd = $null
    }
}

# ---------------------------------------------------------------------------
# Parent Update
# Routes ComponentMsg to the correct counter; Tab switches focus
# ---------------------------------------------------------------------------

$update = {
    param($msg, $model)

    # Tab key switches focus between counters
    if ($msg.Key -eq 'Tab') {
        $newFocus = if ($model.Focus -eq 'Left') { 'Right' } else { 'Left' }
        return [PSCustomObject]@{
            Model = [PSCustomObject]@{
                LeftModel  = $model.LeftModel
                RightModel = $model.RightModel
                Focus      = $newFocus
            }
            Cmd = $null
        }
    }

    if ($msg.Key -eq 'Q') {
        return [PSCustomObject]@{
            Model = $model
            Cmd   = [PSCustomObject]@{ Type = 'Quit' }
        }
    }

    # Route Up/Down to the focused counter as component messages
    $innerMsg = switch ($msg.Key) {
        'UpArrow'   { [PSCustomObject]@{ Type = 'Increment' } }
        'DownArrow' { [PSCustomObject]@{ Type = 'Decrement' } }
        default     { $null }
    }

    if ($null -eq $innerMsg) {
        return [PSCustomObject]@{ Model = $model; Cmd = $null }
    }

    $newLeft  = $model.LeftModel
    $newRight = $model.RightModel

    if ($model.Focus -eq 'Left') {
        $newLeft  = & $Counter.Update $innerMsg $model.LeftModel
    } else {
        $newRight = & $Counter.Update $innerMsg $model.RightModel
    }

    [PSCustomObject]@{
        Model = [PSCustomObject]@{
            LeftModel  = $newLeft
            RightModel = $newRight
            Focus      = $model.Focus
        }
        Cmd = $null
    }
}

# ---------------------------------------------------------------------------
# Parent View
# Embeds each counter as a New-ElmComponent node
# ---------------------------------------------------------------------------

$view = {
    param($model)

    $hintStyle      = New-ElmStyle -Foreground 'BrightBlack'
    $focusLabelStyle = New-ElmStyle -Foreground 'BrightYellow' -Bold

    $focusText = "Focus: $($model.Focus)"

    $leftComponent  = New-ElmComponent -ComponentId 'left'  -SubModel $model.LeftModel  -ViewFn $Counter.View
    $rightComponent = New-ElmComponent -ComponentId 'right' -SubModel $model.RightModel -ViewFn $Counter.View

    New-ElmBox -Children @(
        New-ElmText -Content 'Component Demo' -Style (New-ElmStyle -Bold -Foreground 'BrightWhite')
        New-ElmText -Content ''
        New-ElmRow -Children @($leftComponent, (New-ElmText -Content ' '), $rightComponent)
        New-ElmText -Content ''
        New-ElmText -Content $focusText -Style $focusLabelStyle
        New-ElmText -Content '[Up] inc [Down] dec [Tab] switch [Q] quit' -Style $hintStyle
    )
}

Start-ElmProgram -InitFn $init -UpdateFn $update -ViewFn $view