From ddc3582a3079f8e7c9366813f13f5729219f57ad Mon Sep 17 00:00:00 2001 From: Ozgur Ersoy Date: Sun, 18 Oct 2026 14:48:21 +0200 Subject: [PATCH] Add an InlineAlert demo app Exercises the @geastack/elements InlineAlert from geastack/core#37: one alert per variant, each with a dismiss button that removes it until Reset, a Retry action on the error alert that counts presses in its message, a narrow warning alert whose message wraps, and a language switch that turns every text, including the severity labels, into Turkish. The app owns which alerts are shown. Each alert is rendered from its own class component that reads the store, with a div as the template's root. In a native build, a component inside a conditional branch kept the props it was first built with: the retry count and the language did not change, whether the alert was written inline in the branch or in a function component that read the store. A class component whose template root was the InlineAlert itself did not update either. With an element as the root, all of them update. The app depends on @geastack/core ^0.1.41, like the other apps, and on @geastack/elements ^0.1.6, which does not export InlineAlert yet; it type-checks only against a core checkout that has the component until an elements release ships it. The manifest enables web only and hides the app from launchers. A native target would need more than icons: with the built-in bitmap font, the Turkish letters draw as "?". docs/EXAMPLE-CATALOG.md lists the app under UI components, and the workspace is added to package-lock.json. Tests: a scratch native test, built with core's run-gea-native-app-pipeline.sh against a core checkout with InlineAlert, renders four alerts, taps Retry, switches to Turkish, dismisses an alert and resets, and checks the texts and the alert count after each step; it passes. It compares text content only, so it does not show how the glyphs are drawn. tsc 5.9.3 on a copy of the app with @geastack/core and @geastack/elements linked to that core checkout: exit 0. npm ci --dry-run --ignore-scripts on a copy of the workspace manifests and lockfile: exit 0; without the lockfile entry it fails with "Missing: @geastack/example-inline-alert@0.1.0 from lock file". In the browser dev server, Retry, the language switch and the dismiss buttons work. --- apps/inline-alert/components/Alerts.tsx | 103 ++++++++++++++++++++++++ apps/inline-alert/components/App.tsx | 22 +++++ apps/inline-alert/env.d.ts | 2 + apps/inline-alert/index.tsx | 5 ++ apps/inline-alert/package.json | 29 +++++++ apps/inline-alert/stores/DemoStore.ts | 38 +++++++++ apps/inline-alert/styles.css | 46 +++++++++++ apps/inline-alert/tsconfig.json | 23 ++++++ docs/EXAMPLE-CATALOG.md | 2 +- package-lock.json | 13 +++ 10 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 apps/inline-alert/components/Alerts.tsx create mode 100644 apps/inline-alert/components/App.tsx create mode 100644 apps/inline-alert/env.d.ts create mode 100644 apps/inline-alert/index.tsx create mode 100644 apps/inline-alert/package.json create mode 100644 apps/inline-alert/stores/DemoStore.ts create mode 100644 apps/inline-alert/styles.css create mode 100644 apps/inline-alert/tsconfig.json diff --git a/apps/inline-alert/components/Alerts.tsx b/apps/inline-alert/components/Alerts.tsx new file mode 100644 index 0000000..94fe87c --- /dev/null +++ b/apps/inline-alert/components/Alerts.tsx @@ -0,0 +1,103 @@ +import { Component } from '@geastack/core' +import { InlineAlert } from '@geastack/elements' +import { demo } from '../stores/DemoStore' + +// Each alert sits in its own class component that reads the store, the way +// e-reader's OpeningModal does, with an element as the template's root. In +// a native build, a component inside a conditional branch keeps the props it +// was first built with, and so does a class whose template root is the +// InlineAlert itself; this shape follows the store. +// The language switch changes every text, including the severity labels, to +// show localized content wrapping in the same layout. With the built-in +// bitmap font, pixel targets draw the Turkish letters as `?`. + +class InfoAlert extends Component { + template() { + return ( +
+ demo.dismiss('info')} + /> +
+ ) + } +} + +class SuccessAlert extends Component { + template() { + return ( +
+ demo.dismiss('success')} + /> +
+ ) + } +} + +class WarningAlert extends Component { + template() { + return ( +
+ demo.dismiss('warning')} + /> +
+ ) + } +} + +class ErrorAlert extends Component { + template() { + return ( +
+ demo.retry()} + dismissible={true} + dismissLabel={demo.turkish ? 'Kapat' : undefined} + onDismiss={() => demo.dismiss('error')} + /> +
+ ) + } +} + +export function Alerts() { + return ( +
+ {demo.infoOpen ? : null} + {demo.successOpen ? : null} + {demo.warningOpen ? : null} + {demo.errorOpen ? : null} +
+ ) +} diff --git a/apps/inline-alert/components/App.tsx b/apps/inline-alert/components/App.tsx new file mode 100644 index 0000000..222aea5 --- /dev/null +++ b/apps/inline-alert/components/App.tsx @@ -0,0 +1,22 @@ +import { Component } from '@geastack/core' +import { demo } from '../stores/DemoStore' +import { Alerts } from './Alerts' + +export class App extends Component { + template() { + return ( +
+ InlineAlert + +
+ + +
+
+ ) + } +} diff --git a/apps/inline-alert/env.d.ts b/apps/inline-alert/env.d.ts new file mode 100644 index 0000000..dc50b2b --- /dev/null +++ b/apps/inline-alert/env.d.ts @@ -0,0 +1,2 @@ +/// +declare module '*.css' diff --git a/apps/inline-alert/index.tsx b/apps/inline-alert/index.tsx new file mode 100644 index 0000000..46c9b1f --- /dev/null +++ b/apps/inline-alert/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/inline-alert/package.json b/apps/inline-alert/package.json new file mode 100644 index 0000000..18872a1 --- /dev/null +++ b/apps/inline-alert/package.json @@ -0,0 +1,29 @@ +{ + "name": "@geastack/example-inline-alert", + "type": "module", + "scripts": { + "check": "tsc --noEmit" + }, + "dependencies": { + "@geastack/core": "^0.1.41", + "@geastack/elements": "^0.1.6" + }, + "gea": { + "id": "inline-alert", + "name": "Inline Alert", + "entry": "index.tsx", + "runtime": "gea", + "targets": { + "web": true, + "esp32": false + }, + "launcher": { + "description": "inline alert variants", + "order": 999, + "hidden": true + } + }, + "private": true, + "license": "MIT", + "version": "0.1.0" +} diff --git a/apps/inline-alert/stores/DemoStore.ts b/apps/inline-alert/stores/DemoStore.ts new file mode 100644 index 0000000..7504dc0 --- /dev/null +++ b/apps/inline-alert/stores/DemoStore.ts @@ -0,0 +1,38 @@ +import { Store } from '@geastack/core' +import type { InlineAlertVariant } from '@geastack/elements' + +// The app owns which alerts are shown. InlineAlert only reports onDismiss, +// so dismissing one here removes it until Reset. +class DemoStore extends Store { + infoOpen = true + successOpen = true + warningOpen = true + errorOpen = true + turkish = false + retries = 0 + + dismiss(variant: InlineAlertVariant): void { + if (variant === 'info') this.infoOpen = false + if (variant === 'success') this.successOpen = false + if (variant === 'warning') this.warningOpen = false + if (variant === 'error') this.errorOpen = false + } + + retry(): void { + this.retries = this.retries + 1 + } + + toggleLanguage(): void { + this.turkish = !this.turkish + } + + reset(): void { + this.infoOpen = true + this.successOpen = true + this.warningOpen = true + this.errorOpen = true + this.retries = 0 + } +} + +export const demo = new DemoStore() diff --git a/apps/inline-alert/styles.css b/apps/inline-alert/styles.css new file mode 100644 index 0000000..3dff5d4 --- /dev/null +++ b/apps/inline-alert/styles.css @@ -0,0 +1,46 @@ +body { + background-color: #f6f3ea; + color: #171713; + font-family: 'Inter'; +} + +.demo-app { + display: flex; + flex-direction: column; + padding: 24px; + gap: 12px; +} + +.demo-title { + font-size: 28px; + font-weight: 700; +} + +.demo-alerts { + display: flex; + flex-direction: column; + gap: 12px; +} + +.demo-alert { + width: 100%; +} + +.demo-alert--narrow { + width: 220px; +} + +.demo-buttons { + display: flex; + flex-direction: row; + gap: 8px; +} + +.demo-button { + width: 96px; + height: 44px; + background-color: #171713; + color: #ffffff; + font-size: 14px; + border-radius: 8px; +} diff --git a/apps/inline-alert/tsconfig.json b/apps/inline-alert/tsconfig.json new file mode 100644 index 0000000..2388422 --- /dev/null +++ b/apps/inline-alert/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..3263611 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`, `inline-alert`, `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..c5eddee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1122,6 +1122,15 @@ "node": ">= 8.0.0" } }, + "apps/inline-alert": { + "name": "@geastack/example-inline-alert", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@geastack/core": "^0.1.41", + "@geastack/elements": "^0.1.6" + } + }, "apps/ios-device-showcase": { "name": "@geastack/example-ios-device-showcase", "version": "0.1.0", @@ -2779,6 +2788,10 @@ "resolved": "apps/image-jsx", "link": true }, + "node_modules/@geastack/example-inline-alert": { + "resolved": "apps/inline-alert", + "link": true + }, "node_modules/@geastack/example-ios-device-showcase": { "resolved": "apps/ios-device-showcase", "link": true