# Toggle Component

## Preface

`Toggle` adds interactive on/off controls to TUI apps with SwiftUI API parity: `Binding<Bool>`, label as ViewBuilder or String, two styles (toggle slider `[●○]`/`[○●]` and checkbox `[x]`/`[ ]`). Space/Enter toggles the value. Focused indicators show via pulsing ● dot. Supports `.disabled()` modifier. Works perfectly alongside Button and Menu as part of the core input component set.

## Completed

**2026-02-06**: All implementation tasks completed. Toggle component ready for use with both styles (toggle/checkbox) and full focus management.

## Checklist

- [x] Create ToggleStyle enum with toggle and checkbox cases
- [x] Create Toggle struct with both initializers
- [x] Create ToggleHandler (Focusable) for keyboard events
- [x] Implement Renderable extension with focus indicator
- [x] Add .disabled() modifier
- [x] Write 17 comprehensive tests
- [x] Build & lint verification (571 tests passing)
- [x] Add to example app with TogglePage
- [x] Documentation complete

---

## Context / Problem

Terminal applications need an interactive toggle/checkbox component for boolean state as part of the core input component set alongside Button, TextField, and Menu.

## SwiftUI Reference

```swift
// SwiftUI API
Toggle(isOn: $isEnabled) {
    Text("Enable notifications")
}

Toggle("Enable notifications", isOn: $isEnabled)

// With custom style
Toggle(isOn: $isEnabled) {
    Label("Airplane Mode", systemImage: "airplane")
}
.toggleStyle(.switch)
```

Key SwiftUI patterns:
- `isOn: Binding<Bool>` for two-way binding
- Label as trailing `@ViewBuilder` closure or convenience `String` init
- `.toggleStyle()` modifier for visual variants

## TUIKit Design

### API

```swift
// Primary API (matches SwiftUI)
Toggle(isOn: $isEnabled) {
    Text("Enable notifications")
}

// Convenience initializer
Toggle("Enable notifications", isOn: $isEnabled)

// With style
Toggle("Dark mode", isOn: $darkMode, style: .checkbox)
```

### Visual Styles

```swift
public enum ToggleStyle {
    case toggle   // [●○] / [○●] where dot slides left/right
    case checkbox // [x] / [ ] as classic checkbox
}
```

**Toggle style (default):**

```
Off: [○●]  or  ( ○)
On:  [●○]  or  (● )
```

**Checkbox style:**

```
Off: [ ]
On:  [x] or [✓]
```

Need to test with various terminal fonts:
- SF Mono, Menlo, Monaco (macOS)
- Consolas, Cascadia (Windows)
- Ubuntu Mono, DejaVu Sans Mono (Linux)

### Keyboard Interaction

| Key | Action |
|-----|--------|
| Space | Toggle value |
| Enter | Toggle value |

### Focus Behavior

- Registers with `FocusManager` like Button
- Shows pulsing `●` indicator on the left when focused (consistent with Focus Sections)
- Uses existing `PulseTimer` + `context.pulsePhase` for animation
- Unfocused: no indicator (2 spaces for alignment)

### Component Structure

```swift
public struct Toggle<Label: View>: View {
    let isOn: Binding<Bool>
    let label: Label
    let style: ToggleStyle
    let focusID: String
    let isDisabled: Bool

    public var body: Never  // Renderable primitive
}

final class ToggleHandler: Focusable {
    // Handles Space/Enter → toggle binding
}

extension Toggle: Renderable {
    func renderToBuffer(context: RenderContext) -> FrameBuffer
}
```

### Rendering

Layout: `[focus●] [toggle/checkbox] label`

Example outputs:

```
● [●○] Enable notifications     (focused, on, pulsing dot)
  [○●] Enable notifications     (unfocused, off)
● [ ] Show hidden files         (checkbox, focused, off)
  [x] Show hidden files         (checkbox, unfocused, on)
```

## Implementation Plan

- [ ] Create `ToggleStyle` enum
- [ ] Create `Toggle` struct with both initializers
- [ ] Create `ToggleHandler` (Focusable) for keyboard events
- [ ] Implement `Renderable` extension
- [ ] Add `.disabled()` modifier
- [ ] Write tests
- [ ] Build & lint verification
- [ ] Add to example app for visual testing

## Character Options

### Dot-Style (Slider)

| On | Off | Description |
|----|-----|-------------|
| `●○` | `○●` | Filled/Empty Circle |
| `■□` | `□■` | Square |

### Switch-Style (with Brackets)

| On | Off | Description |
|----|-----|-------------|
| `[●○]` | `[○●]` | Dots in Brackets |
| `(● )` | `( ●)` | Dots in Parens |
| `[■ ]` | `[ ■]` | Square slider |

### Checkbox-Style

| On | Off | Description |
|----|-----|-------------|
| `[x]` | `[ ]` | ASCII x |
| `[✓]` | `[ ]` | Checkmark |
| `[●]` | `[ ]` | Dot |

### Minimal/Inline (no brackets)

| On | Off | Description |
|----|-----|-------------|
| `◉` | `○` | Fisheye / Empty |
| `●` | `○` | Filled / Empty |
| `■` | `□` | Square |
| `◼` | `◻` | Medium Square |

## Open Questions

1. **Toggle characters:** Which combination renders best across terminal fonts?
2. **Checkbox character:** ASCII-safe `[x]` or Unicode `[✓]`?
3. **Label position:** Always right of toggle, or configurable?

## Pre-Requisite: Unified Focus Indicator ✅

Completed 2026-02-06 (PR #81):
- [x] `BorderRenderer.focusIndicatorPrefix()` as shared helper for pulsing `●` indicator
- [x] Button: pulsing accent-colored brackets when focused (no dot for bracketed elements)
- [x] Plain buttons: pulsing dot indicator
- [x] PulseTimer reset on focus change
- [x] Shift+Tab parsing for backward navigation

## Dependencies

- Focus system (existing)
- Binding (existing)
- PulseTimer + pulsePhase (existing)

## Files

- `Sources/TUIkit/Views/Toggle.swift`
- `Tests/TUIkitTests/ToggleTests.swift`
