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