diff --git a/apps/progress-bar/components/App.tsx b/apps/progress-bar/components/App.tsx new file mode 100644 index 0000000..1d2d9e9 --- /dev/null +++ b/apps/progress-bar/components/App.tsx @@ -0,0 +1,49 @@ +import { Component } from '@geastack/core' +import { ProgressBar, progressFraction } from '@geastack/elements' +import { demo } from '../stores/DemoStore' + +export class App extends Component { + // The buttons drive #demo-bar. The .demo-cases bars below them never change; + // they cover default props, a fractional value with an empty label, a + // negative range, an inverted range and a NaN value for core's pipeline test. + template() { + return ( +
+ ProgressBar + +
+ + + + +
+
+ + + + +
+
+ +
+
+ + + + + + +
+
+ ) + } +} diff --git a/apps/progress-bar/env.d.ts b/apps/progress-bar/env.d.ts new file mode 100644 index 0000000..dc50b2b --- /dev/null +++ b/apps/progress-bar/env.d.ts @@ -0,0 +1,2 @@ +/// +declare module '*.css' diff --git a/apps/progress-bar/index.tsx b/apps/progress-bar/index.tsx new file mode 100644 index 0000000..46c9b1f --- /dev/null +++ b/apps/progress-bar/index.tsx @@ -0,0 +1,5 @@ +import { mount } from '@geastack/core' +import './styles.css' +import { App } from './components/App' + +mount(App) diff --git a/apps/progress-bar/package.json b/apps/progress-bar/package.json new file mode 100644 index 0000000..8aaa3e4 --- /dev/null +++ b/apps/progress-bar/package.json @@ -0,0 +1,29 @@ +{ + "name": "@geastack/example-progress-bar", + "type": "module", + "scripts": { + "check": "tsc --noEmit" + }, + "dependencies": { + "@geastack/core": "^0.1.41", + "@geastack/elements": "^0.1.6" + }, + "gea": { + "id": "progress-bar", + "name": "Progress Bar", + "entry": "index.tsx", + "runtime": "gea", + "targets": { + "web": true, + "esp32": false + }, + "launcher": { + "description": "progress bar states", + "order": 999, + "hidden": true + } + }, + "private": true, + "license": "MIT", + "version": "0.1.0" +} diff --git a/apps/progress-bar/stores/DemoStore.ts b/apps/progress-bar/stores/DemoStore.ts new file mode 100644 index 0000000..4e778ac --- /dev/null +++ b/apps/progress-bar/stores/DemoStore.ts @@ -0,0 +1,61 @@ +import { Store } from '@geastack/core' + +// Every button drives one of the cases the ProgressBar has to handle, so the +// app doubles as the fixture for core's progress-bar pipeline test. +export class DemoStore extends Store { + value = 40 + min = 0 + max = 100 + busy = false + still = false + wide = true + + // ProgressBar clamps what it draws, not the app's value, so the app keeps its + // own value in range. Over and Under bypass this on purpose to exercise the + // bar's clamping. + step(delta: number) { + let next = this.value + delta + if (next > this.max) next = this.max + if (next < this.min) next = this.min + this.value = next + } + + setValue(next: number) { + this.value = next + } + + // min == max leaves the bar nothing to divide by; it must draw empty, not NaN. + zeroRange() { + this.min = 50 + this.max = 50 + } + + reset() { + this.value = 40 + this.min = 0 + this.max = 100 + this.busy = false + this.still = false + this.wide = true + } + + toggleBusy() { + this.busy = !this.busy + } + + toggleStill() { + this.still = !this.still + } + + toggleWide() { + this.wide = !this.wide + } + + get label(): string { + if (this.busy) return 'Working' + const range = 'value ' + this.value + ' of ' + this.min + '-' + this.max + return this.max > this.min ? range : range + ' (zero range)' + } +} + +export const demo = new DemoStore() diff --git a/apps/progress-bar/styles.css b/apps/progress-bar/styles.css new file mode 100644 index 0000000..9c906db --- /dev/null +++ b/apps/progress-bar/styles.css @@ -0,0 +1,51 @@ +body { + background-color: #f6f3ea; + color: #171713; + font-family: 'Inter'; +} + +.demo-app { + display: flex; + flex-direction: column; + padding: 24px; + gap: 20px; +} + +.demo-title { + font-size: 28px; + font-weight: 700; +} + +.demo-bar { + width: 300px; +} + +.demo-bar--narrow { + width: 150px; +} + +.demo-buttons { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 8px; +} + +.demo-button { + width: 70px; + height: 44px; + background-color: #171713; + color: #ffffff; + font-size: 14px; + border-radius: 8px; +} + +.demo-cases { + display: flex; + flex-direction: column; + gap: 12px; +} + +.demo-case { + width: 300px; +} diff --git a/apps/progress-bar/tsconfig.json b/apps/progress-bar/tsconfig.json new file mode 100644 index 0000000..2388422 --- /dev/null +++ b/apps/progress-bar/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": [ + "ES2022" + ], + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "jsx": "preserve", + "types": [] + }, + "include": [ + "**/*.tsx", + "**/*.d.ts", + "../../node_modules/@geajs/vite-plugin/gea-env.d.ts" + ], + "exclude": [ + "vite.config.ts" + ] +} diff --git a/docs/EXAMPLE-CATALOG.md b/docs/EXAMPLE-CATALOG.md index f0ee5d2..cb02832 100644 --- a/docs/EXAMPLE-CATALOG.md +++ b/docs/EXAMPLE-CATALOG.md @@ -10,7 +10,7 @@ tests, and marketing proof. | Watch and wearable UI | `watch`, `watch-analog`, `watch-date`, `watch-face` | Compact layouts, clock/date rendering, small-screen app surfaces. | | Basic JSX apps | `counter-jsx`, `todo-jsx`, `static-card`, `stopwatch-jsx` | Component rendering, state updates, text/input, timers. | | Canvas and animation | `bouncing-balls`, `bouncing-balls-jsx`, `canvas-3d`, `canvas-3d-cube`, `css-3d-cube`, `css-animation-showcase` | Canvas drawing, CSS animation, frame loop, transform behavior. | -| UI components | `settings`, `dialer`, `virtual-list`, `typography`, `bubble-grid`, `bubble-grid-jsx` | Higher-level controls, scroll behavior, text layout, launchable apps. | +| UI components | `settings`, `dialer`, `virtual-list`, `progress-bar`, `typography`, `bubble-grid`, `bubble-grid-jsx` | Higher-level controls, scroll behavior, text layout, launchable apps. | | Games | `sky-hop`, `sky-hop-jsx`, `tic-tac-toe`, `tilt-breakout`, `button-tetris` | Interaction, simple game loops, collision/physics, deterministic logic. | | Device features | `camera-showcase`, `camera-studio`, `voice-notes`, `hid-clicker`, `weather`, `maps` | Target capabilities such as camera, audio, HID, network, and map assets. | | Apple/native experiments | `notes-jsx`, `notes-native`, `ios-device-showcase`, `ios-metal-*`, `ios-native-showcase`, `macos-native-showcase` | Apple target and native renderer experiments. | diff --git a/package-lock.json b/package-lock.json index d71e820..f80f8b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1378,6 +1378,15 @@ "vite": "latest" } }, + "apps/progress-bar": { + "name": "@geastack/example-progress-bar", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@geastack/core": "^0.1.41", + "@geastack/elements": "^0.1.6" + } + }, "apps/reactive-child-probe": { "name": "@geastack/example-reactive-child-probe", "version": "0.1.0", @@ -2823,6 +2832,10 @@ "resolved": "apps/notes-windows", "link": true }, + "node_modules/@geastack/example-progress-bar": { + "resolved": "apps/progress-bar", + "link": true + }, "node_modules/@geastack/example-reactive-child-probe": { "resolved": "apps/reactive-child-probe", "link": true