Files
TUIkit/Sources/TUIkitExample/ContentView.swift
T
phranck 84fdb7ace2 Feat: Add Table component with column support
- TableColumn: Column definition with title, alignment, width modes
  - Key path and closure-based value extraction
  - Width modes: .fixed(Int), .flexible, .ratio(Double)
  - Alignment: .leading, .center, .trailing
  - Chainable modifiers: .alignment(), .width()

- Table: SwiftUI-compatible table with column headers
  - Single selection: Table(data, selection: Binding<ID?>)
  - Multi-selection: Table(data, selection: Binding<Set<ID>>)
  - Reuses ItemListHandler for navigation/selection
  - ANSI-aware column alignment
  - Header row with column titles
  - Space-only separators (no vertical lines)
  - Scroll indicators when content overflows
  - Empty state placeholder
  - .disabled() modifier support

- TableColumnBuilder: Result builder for column DSL
- TablePage: Example with file browser style demo
- 21 new tests for Table and TableColumn

Completes Phase 2 of List & Table plan.
2026-02-07 19:08:19 +01:00

131 lines
4.5 KiB
Swift

// 🖥️ TUIKit — Terminal UI Kit for Swift
// ContentView.swift
//
// Created by LAYERED.work
// License: MIT
import TUIkit
// MARK: - Demo Page Enum
/// The available demo pages in the example app.
enum DemoPage: Int, CaseIterable {
case menu
case textStyles
case colors
case containers
case overlays
case layout
case buttons
case toggles
case radioButtons
case spinners
case lists
case tables
}
// MARK: - Content View (Page Router)
/// The main content view that switches between pages.
///
/// This view acts as a router, displaying the appropriate demo page
/// based on the current state. It uses `@State` for all reactive
/// properties — exactly like SwiftUI.
struct ContentView: View {
@State var currentPage: DemoPage = .menu
@State var menuSelection: Int = 0
var body: some View {
// Capture bindings for use in closures
let pageSetter = $currentPage
// Show current page based on state
// Note: Background color is set by AppRunner using theme.background
pageContent(for: currentPage, pageSetter: pageSetter)
.onKeyPress { event in
switch event.key {
case .escape:
// ESC goes back to menu (or exits if already on menu)
if currentPage != .menu {
pageSetter.wrappedValue = .menu
return true // Consumed
}
return false // Let default handler exit the app
case .character("8"):
// Quick jump to Radio Buttons
currentPage = .radioButtons
return true
case .character("9"):
// Quick jump to Spinners
currentPage = .spinners
return true
case .character("0"):
// Quick jump to Lists
currentPage = .lists
return true
case .character("-"):
// Quick jump to Tables
currentPage = .tables
return true
default:
return false // Let other handlers process
}
}
}
@ViewBuilder
private func pageContent(for page: DemoPage, pageSetter: Binding<DemoPage>) -> some View {
switch page {
case .menu:
MainMenuPage(currentPage: $currentPage, menuSelection: $menuSelection)
.statusBarItems {
StatusBarItem(shortcut: Shortcut.arrowsUpDown, label: "nav")
StatusBarItem(shortcut: Shortcut.enter, label: "select", key: .enter)
StatusBarItem(shortcut: Shortcut.range("1", "9") + ", 0", label: "jump")
}
case .textStyles:
TextStylesPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .colors:
ColorsPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .containers:
ContainersPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .overlays:
OverlaysPage(onBack: { pageSetter.wrappedValue = .menu })
case .layout:
LayoutPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .buttons:
ButtonsPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .toggles:
TogglePage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .radioButtons:
RadioButtonPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .spinners:
SpinnersPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .lists:
ListPage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
case .tables:
TablePage()
.statusBarItems(subPageItems(pageSetter: pageSetter))
}
}
/// Common status bar items for sub-pages.
private func subPageItems(pageSetter: Binding<DemoPage>) -> [any StatusBarItemProtocol] {
[
StatusBarItem(shortcut: Shortcut.escape, label: "back") { [pageSetter] in
pageSetter.wrappedValue = .menu
},
StatusBarItem(shortcut: Shortcut.arrowsUpDown, label: "scroll"),
]
}
}