Files
phranck 5ad97132b8 Feat: Add @Observable support with Observation framework
- Replace custom Observable protocol and @Published with Apple's @Observable macro
- Add withObservationTracking in renderToBuffer for automatic per-property dependency tracking
- Add type-based @Environment(Type.self) and .environment(object) for observable objects
- Add ObjectEnvironmentModifier for injecting observable objects into the environment
- Add needsCacheClear flag to AppState for thread-safe cache invalidation
- Add cross-platform test script (scripts/test-linux.sh) for Docker-based Linux verification
- Add DemoAppHeader with system info display (OS, version, architecture)
- Consolidate Example App: extract ImageDemoHelpers, KeyboardHelpSection, ValueDisplayRow
- Add pre-push verification rule to CLAUDE.md
- Verified on both macOS and Linux (swift:6.0 container), 1155 tests passing
2026-02-15 23:49:34 +01:00

103 lines
3.4 KiB
Swift
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 🖥 TUIKit Terminal UI Kit for Swift
// ListPage.swift
//
// Created by LAYERED.work
// License: MIT
import TUIkit
// MARK: - Demo Item
/// A simple item for list demos.
private struct FileItem: Identifiable {
let id: String
let name: String
let size: String
let icon: String
static let sampleFiles: [Self] = [
Self(id: "1", name: "README.md", size: "4.2 KB", icon: "📄"),
Self(id: "2", name: "Package.swift", size: "1.8 KB", icon: "📦"),
Self(id: "3", name: "Sources", size: "128 KB", icon: "📁"),
Self(id: "4", name: "Tests", size: "64 KB", icon: "📁"),
Self(id: "5", name: ".gitignore", size: "0.5 KB", icon: "📄"),
Self(id: "6", name: "LICENSE", size: "1.1 KB", icon: "📄"),
Self(id: "7", name: "docs", size: "256 KB", icon: "📁"),
Self(id: "8", name: "plans", size: "32 KB", icon: "📁"),
Self(id: "9", name: ".swiftlint.yml", size: "1.2 KB", icon: "⚙️"),
Self(id: "10", name: ".github", size: "8 KB", icon: "📁"),
Self(id: "11", name: "Makefile", size: "0.8 KB", icon: "📄"),
Self(id: "12", name: ".claude", size: "16 KB", icon: "📁"),
]
}
// MARK: - List Page
/// List component demo page.
///
/// Shows interactive list features including:
/// - Single selection with binding
/// - Multi-selection with binding
/// - Keyboard navigation (Up/Down/Home/End/PageUp/PageDown)
/// - Scroll indicators
/// - Empty state placeholder
struct ListPage: View {
@State var singleSelection: String?
@State var multiSelection: Set<String> = []
var body: some View {
VStack(alignment: .leading, spacing: 1) {
HStack(spacing: 2) {
List(
"Single Selection",
selection: $singleSelection
) {
ForEach(FileItem.sampleFiles) { file in
HStack(spacing: 1) {
Text(file.icon)
Text(file.name)
}
}
}
List(
"Multi Selection",
selection: $multiSelection
) {
ForEach(FileItem.sampleFiles) { file in
HStack(spacing: 1) {
Text(file.icon)
Text(file.name)
}
}
}
}
DemoSection("Current Selections") {
VStack(alignment: .leading, spacing: 1) {
ValueDisplayRow("Single:", singleSelection ?? "(none)")
ValueDisplayRow("Multi:", multiSelection.isEmpty ? "(none)" : multiSelection.sorted().joined(separator: ", "))
}
}
List("Empty List", selection: Binding<String?>(get: { nil }, set: { _ in })) {
EmptyView()
}
KeyboardHelpSection("Navigation", shortcuts: [
"Use [↑/↓] to navigate items",
"Use [Home/End] to jump to first/last",
"Use [PageUp/PageDown] for fast scrolling",
"Use [Enter/Space] to select/deselect",
"Use [Tab] to switch between lists",
])
Spacer()
}
.appHeader {
DemoAppHeader("List Demo")
}
}
}