even-toolkit 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Design system & component library for **Even Realities G2** smart glasses apps.
4
4
 
5
- 55+ web components, 191 pixel-art icons, glasses SDK bridge, light/dark themes, and design tokens — all following the Even Realities 2025 UIUX Design Guidelines.
5
+ 55+ web components, 191 pixel-art icons, glasses SDK bridge with per-screen architecture, speech-to-text module, light/dark themes, and design tokens — all following the Even Realities 2025 UIUX Design Guidelines.
6
6
 
7
7
  **[Live Demo → even-demo.vercel.app](https://even-demo.vercel.app)**
8
8
 
@@ -12,6 +12,12 @@ Design system & component library for **Even Realities G2** smart glasses apps.
12
12
  npm install even-toolkit
13
13
  ```
14
14
 
15
+ Scaffold a new app instantly:
16
+
17
+ ```bash
18
+ npx create-even-app my-app
19
+ ```
20
+
15
21
  ## What's Inside
16
22
 
17
23
  ### `/web` — Web Component Library
@@ -24,7 +30,7 @@ import { Button, Card, NavBar, ListItem, Toggle, AppShell } from 'even-toolkit/w
24
30
 
25
31
  **Primitives:** Button, Card, Badge, Input, Textarea, Select, Checkbox, RadioGroup, Slider, InputGroup, Skeleton, Progress, StatusDot, Pill, Toggle, SegmentedControl, Table, Kbd, Divider
26
32
 
27
- **Layout:** AppShell, Page, NavBar, NavHeader, ScreenHeader, SectionHeader, SettingsGroup, CategoryFilter, ListItem (swipe-to-delete), SearchBar, Tag, TagCarousel, TagCard, SliderIndicator, PageIndicator, StepIndicator, Timeline, StatGrid, StatusProgress
33
+ **Layout:** AppShell, Page, NavBar, NavHeader, SideDrawer, DrawerShell, DrawerTrigger, ScreenHeader, SectionHeader, SettingsGroup, CategoryFilter, ListItem (swipe-to-delete), SearchBar, Tag, TagCarousel, TagCard, SliderIndicator, PageIndicator, StepIndicator, Timeline, StatGrid, StatusProgress
28
34
 
29
35
  **Feedback:** TimerRing, Dialog, ConfirmDialog, Toast, EmptyState, Loading, BottomSheet, CTAGroup, ScrollPicker, DatePicker, TimePicker, SelectionPicker
30
36
 
@@ -44,38 +50,173 @@ import { IcChevronBack, IcTrash, IcSettings } from 'even-toolkit/web/icons/svg-i
44
50
 
45
51
  **Categories:** Edit & Settings (32), Feature & Function (50), Guide System (20), Menu Bar (8), Navigate (23), Status (54), Health (12)
46
52
 
47
- ### `/glasses` — G2 Glasses SDK Bridge
53
+ ---
48
54
 
49
- Everything needed to render content on the G2 glasses display.
55
+ ## Glasses SDK
56
+
57
+ Everything needed to build G2 glasses apps with a clean, per-screen architecture.
58
+
59
+ ### Per-Screen Architecture (v1.4)
60
+
61
+ Each glasses screen lives in its own file with co-located display + action logic:
50
62
 
51
- ```tsx
52
- import { useGlasses } from 'even-toolkit/useGlasses';
53
- import { line, separator } from 'even-toolkit/types';
54
- import { buildActionBar } from 'even-toolkit/action-bar';
63
+ ```
64
+ src/glass/
65
+ shared.ts — Snapshot type + actions interface
66
+ selectors.ts — Screen router (3 lines of wiring)
67
+ splash.ts — Splash image + loading text
68
+ AppGlasses.tsx — useGlasses hook setup
69
+ screens/
70
+ home.ts — { display, action }
71
+ detail.ts — { display, action }
72
+ active.ts — { display, action }
55
73
  ```
56
74
 
57
- **Core:** EvenHubBridge, useGlasses hook, useFlashPhase hook
75
+ #### Define a screen
76
+
77
+ ```ts
78
+ import type { GlassScreen } from 'even-toolkit/glass-screen-router';
79
+ import { buildScrollableList } from 'even-toolkit/glass-display-builders';
80
+ import { moveHighlight } from 'even-toolkit/glass-nav';
81
+
82
+ export const homeScreen: GlassScreen<MySnapshot, MyActions> = {
83
+ display(snapshot, nav) {
84
+ return {
85
+ lines: buildScrollableList({
86
+ items: snapshot.items,
87
+ highlightedIndex: nav.highlightedIndex,
88
+ maxVisible: 5,
89
+ formatter: (item) => item.title,
90
+ }),
91
+ };
92
+ },
93
+
94
+ action(action, nav, snapshot, ctx) {
95
+ if (action.type === 'HIGHLIGHT_MOVE') {
96
+ return { ...nav, highlightedIndex: moveHighlight(nav.highlightedIndex, action.direction, snapshot.items.length - 1) };
97
+ }
98
+ if (action.type === 'SELECT_HIGHLIGHTED') {
99
+ ctx.navigate(`/item/${snapshot.items[nav.highlightedIndex].id}`);
100
+ return nav;
101
+ }
102
+ return nav;
103
+ },
104
+ };
105
+ ```
58
106
 
59
- **Display:** DisplayData, DisplayLine, line(), separator(), text-utils, timer-display, canvas-renderer
107
+ #### Wire screens together
60
108
 
61
- **Input:** action-map, gestures, keyboard bindings
109
+ ```ts
110
+ import { createGlassScreenRouter } from 'even-toolkit/glass-screen-router';
111
+ import { homeScreen } from './screens/home';
112
+ import { detailScreen } from './screens/detail';
62
113
 
63
- **Layout:** 576x288px display, text/columns/chart/home page modes, image tiles
114
+ export const { toDisplayData, onGlassAction } = createGlassScreenRouter({
115
+ 'home': homeScreen,
116
+ 'detail': detailScreen,
117
+ }, 'home');
118
+ ```
64
119
 
65
- **Utilities:** splash screens, PNG encoding, text cleaning, pagination, keep-alive
120
+ ### Navigation Helpers (`glass-nav`)
66
121
 
67
- ### Glasses Display Helpers
122
+ ```ts
123
+ import { moveHighlight, clampIndex, calcMaxScroll, wrapIndex } from 'even-toolkit/glass-nav';
68
124
 
69
- ```tsx
70
- import { glassHeader, line, separator } from 'even-toolkit/types';
125
+ // Clamped movement (0 to max)
126
+ moveHighlight(current, 'up', max) // Math.max(0, Math.min(max, current - 1))
127
+ moveHighlight(current, 'down', max) // Math.max(0, Math.min(max, current + 1))
128
+
129
+ // Clamp index to button count
130
+ clampIndex(index, buttonCount) // Math.min(Math.max(0, index), count - 1)
131
+
132
+ // Max scroll offset
133
+ calcMaxScroll(totalLines, slots) // Math.max(0, totalLines - slots)
134
+
135
+ // Wrapping movement (loops around)
136
+ wrapIndex(current, 'down', count) // (current + 1) % count
137
+ ```
138
+
139
+ ### Display Builders (`glass-display-builders`)
140
+
141
+ ```ts
142
+ import {
143
+ buildScrollableList,
144
+ buildScrollableContent,
145
+ slidingWindowStart,
146
+ G2_TEXT_LINES, // 10
147
+ DEFAULT_CONTENT_SLOTS, // 7 (below glassHeader)
148
+ } from 'even-toolkit/glass-display-builders';
149
+
150
+ // Scrollable highlighted list with scroll indicators
151
+ const lines = buildScrollableList({
152
+ items: recipes,
153
+ highlightedIndex: nav.highlightedIndex,
154
+ maxVisible: 5,
155
+ formatter: (r) => r.title,
156
+ });
157
+
158
+ // Header + scrollable content with indicators
159
+ const display = buildScrollableContent({
160
+ title: 'Recipe Detail',
161
+ actionBar: buildStaticActionBar(['Start'], 0),
162
+ contentLines: ['Line 1', 'Line 2', ...],
163
+ scrollPos: nav.highlightedIndex,
164
+ });
165
+ ```
166
+
167
+ ### Mode Encoding (`glass-mode`)
168
+
169
+ Pack multiple navigation modes into a single `highlightedIndex`:
170
+
171
+ ```ts
172
+ import { createModeEncoder } from 'even-toolkit/glass-mode';
173
+
174
+ const mode = createModeEncoder({
175
+ buttons: 0, // 0-99: button selection
176
+ scroll: 100, // 100+: scroll mode (offset = index - 100)
177
+ links: 200, // 200+: link navigation
178
+ });
179
+
180
+ mode.getMode(150) // 'scroll'
181
+ mode.getOffset(150) // 50
182
+ mode.encode('scroll', 25) // 125
183
+ ```
184
+
185
+ ### Route Mapping (`glass-router`)
186
+
187
+ ```ts
188
+ import { createScreenMapper, createIdExtractor, getHomeTiles } from 'even-toolkit/glass-router';
71
189
 
72
- // Header with separator
73
- lines.push(...glassHeader('MY APP'));
190
+ const deriveScreen = createScreenMapper([
191
+ { pattern: '/', screen: 'home' },
192
+ { pattern: /^\/item\/[^/]+$/, screen: 'detail' },
193
+ ], 'home');
74
194
 
75
- // Header with action bar
76
- lines.push(...glassHeader('STEP 1/4', buildActionBar([...], 0, null, true)));
195
+ const extractId = createIdExtractor(/^\/item\/([^/]+)/);
196
+ const homeTiles = getHomeTiles(appSplash);
77
197
  ```
78
198
 
199
+ ### Core Glasses Modules
200
+
201
+ ```ts
202
+ import { useGlasses } from 'even-toolkit/useGlasses';
203
+ import { useFlashPhase } from 'even-toolkit/useFlashPhase';
204
+ import { EvenHubBridge } from 'even-toolkit/bridge';
205
+ import { line, separator, glassHeader } from 'even-toolkit/types';
206
+ import { buildActionBar, buildStaticActionBar } from 'even-toolkit/action-bar';
207
+ import { truncate, applyScrollIndicators } from 'even-toolkit/text-utils';
208
+ import { renderTimerLines } from 'even-toolkit/timer-display';
209
+ import { createSplash, TILE_PRESETS } from 'even-toolkit/splash';
210
+ ```
211
+
212
+ **Display:** 576x288px, 10 text lines, text/columns/chart/home page modes, image tiles (max 288x144)
213
+
214
+ **Input:** action-map (tap/double-tap/scroll events), gestures (debounce), keyboard bindings
215
+
216
+ **Utilities:** splash screens, PNG encoding, text cleaning, pagination, keep-alive
217
+
218
+ ---
219
+
79
220
  ## Speech-to-Text (STT)
80
221
 
81
222
  Provider-agnostic speech-to-text module for voice input in G2 glasses apps.
@@ -130,9 +271,10 @@ Automatically detects the best audio source:
130
271
  - **Browser mic** — via `getUserMedia` (desktop)
131
272
  - Custom `AudioSource` — pass your own
132
273
 
274
+ ---
275
+
133
276
  ## SDK 0.0.9 Support
134
277
 
135
- - Container limit increased to 12 (8 text + 4 image)
136
278
  - Max image size: 288x144
137
279
  - IMU control: `bridge.imuEnable()` / `bridge.imuDisable()`
138
280
  - Launch source detection: `LaunchSource` type
@@ -174,10 +316,55 @@ Light theme following Even Realities 2025 guidelines:
174
316
  | Normal Subtitle | 13px | 400 | -0.13px |
175
317
  | Normal Detail | 11px | 400 | -0.11px |
176
318
 
177
- ## Quick Start
319
+ ## Navigation Patterns
320
+
321
+ ### DrawerShell (recommended)
322
+
323
+ Side drawer navigation with automatic hamburger/back-button detection, header context for nested screens, and `bottomItems` for pinned items like Settings.
324
+
325
+ ```tsx
326
+ import { DrawerShell, useDrawerHeader } from 'even-toolkit/web';
327
+ import type { SideDrawerItem } from 'even-toolkit/web';
328
+
329
+ // In your shell/layout:
330
+ const MENU_ITEMS: SideDrawerItem[] = [
331
+ { id: '/', label: 'Home', section: 'App' },
332
+ ];
333
+ const BOTTOM_ITEMS: SideDrawerItem[] = [
334
+ { id: '/settings', label: 'Settings', section: 'App' },
335
+ ];
336
+
337
+ function Shell() {
338
+ return (
339
+ <DrawerShell
340
+ items={MENU_ITEMS}
341
+ bottomItems={BOTTOM_ITEMS}
342
+ title="MyApp"
343
+ getPageTitle={(p) => p === '/' ? 'MyApp' : 'Page'}
344
+ deriveActiveId={(p) => p}
345
+ />
346
+ );
347
+ }
348
+
349
+ // In nested screens — customize the header:
350
+ function DetailScreen() {
351
+ useDrawerHeader({
352
+ title: 'Detail',
353
+ backTo: '/', // shows back button instead of hamburger
354
+ right: <Button size="sm">Save</Button>,
355
+ below: <Progress value={50} />, // below header (progress bars)
356
+ footer: <StepIndicator ... />, // fixed bottom area
357
+ hidden: true, // hide header entirely
358
+ });
359
+ return <div>...</div>;
360
+ }
361
+ ```
362
+
363
+ ### NavBar + AppShell (tab bar)
364
+
365
+ Horizontal tab bar for simpler apps.
178
366
 
179
367
  ```tsx
180
- // App.tsx
181
368
  import { AppShell, NavBar, ScreenHeader, Button, Card } from 'even-toolkit/web';
182
369
  import type { NavItem } from 'even-toolkit/web';
183
370
 
@@ -200,7 +387,6 @@ export function App() {
200
387
  ```
201
388
 
202
389
  ```css
203
- /* app.css */
204
390
  @import "tailwindcss";
205
391
  @import "even-toolkit/web/theme-light.css";
206
392
  @import "even-toolkit/web/typography.css";
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { SideDrawerItem } from './side-drawer';
3
+ interface DrawerShellProps {
4
+ items: SideDrawerItem[];
5
+ bottomItems?: SideDrawerItem[];
6
+ title?: string;
7
+ footer?: ReactNode;
8
+ width?: number;
9
+ getPageTitle: (pathname: string) => string;
10
+ deriveActiveId: (pathname: string) => string;
11
+ isNestedRoute?: (pathname: string) => boolean;
12
+ getBackPath?: (pathname: string) => string;
13
+ backIcon?: ReactNode;
14
+ className?: string;
15
+ }
16
+ declare function DrawerShell({ items, bottomItems, title, footer, width, getPageTitle, deriveActiveId, isNestedRoute, getBackPath, backIcon, className, }: DrawerShellProps): import("react/jsx-runtime").JSX.Element;
17
+ export { DrawerShell };
18
+ export type { DrawerShellProps };
19
+ //# sourceMappingURL=drawer-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer-shell.d.ts","sourceRoot":"","sources":["../../../web/components/drawer-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvE,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,eAAe,CAAC;AAIxE,UAAU,gBAAgB;IACxB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD,iBAAS,WAAW,CAAC,EACnB,KAAK,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,WAAW,EACX,QAA4B,EAC5B,SAAS,GACV,EAAE,gBAAgB,2CAgGlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useCallback, useMemo } from 'react';
3
+ import { Outlet, useLocation, useNavigate } from 'react-router';
4
+ import { SideDrawer, DrawerTrigger, DrawerHeaderContext } from './side-drawer';
5
+ import { NavHeader } from './nav-header';
6
+ import { Button } from './button';
7
+ const DEFAULT_BACK_ICON = (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M12.5 15L7.5 10L12.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
8
+ function DrawerShell({ items, bottomItems, title, footer, width, getPageTitle, deriveActiveId, isNestedRoute, getBackPath, backIcon = DEFAULT_BACK_ICON, className, }) {
9
+ const location = useLocation();
10
+ const navigate = useNavigate();
11
+ const [drawerOpen, setDrawerOpen] = useState(false);
12
+ const [headerOverride, setHeaderOverride] = useState(null);
13
+ const handleNavigate = useCallback((id) => {
14
+ navigate(id);
15
+ setDrawerOpen(false);
16
+ }, [navigate]);
17
+ const allItemIds = useMemo(() => {
18
+ const ids = new Set(items.map((i) => i.id));
19
+ if (bottomItems)
20
+ bottomItems.forEach((i) => ids.add(i.id));
21
+ return ids;
22
+ }, [items, bottomItems]);
23
+ const pathname = location.pathname;
24
+ const activeId = deriveActiveId(pathname);
25
+ // Determine if nested: either explicit function or check if path matches any item id
26
+ const nested = isNestedRoute
27
+ ? isNestedRoute(pathname)
28
+ : !allItemIds.has(pathname);
29
+ // Resolve header values (screen overrides > defaults)
30
+ const headerTitle = headerOverride?.title ?? getPageTitle(pathname);
31
+ const headerHidden = headerOverride?.hidden ?? false;
32
+ const defaultLeft = nested
33
+ ? (_jsx(Button, { variant: "ghost", size: "icon", onClick: () => navigate(headerOverride?.backTo ?? getBackPath?.(pathname) ?? '/'), children: backIcon }))
34
+ : _jsx(DrawerTrigger, { onClick: () => setDrawerOpen(true) });
35
+ const headerLeft = headerOverride?.left ?? (headerOverride?.backTo
36
+ ? (_jsx(Button, { variant: "ghost", size: "icon", onClick: () => navigate(headerOverride.backTo), children: backIcon }))
37
+ : defaultLeft);
38
+ const headerRight = headerOverride?.right ?? undefined;
39
+ const headerBelow = headerOverride?.below ?? undefined;
40
+ const headerFooter = headerOverride?.footer ?? undefined;
41
+ // Context value
42
+ const ctxValue = useMemo(() => ({
43
+ setHeader: (config) => setHeaderOverride(config),
44
+ resetHeader: () => setHeaderOverride(null),
45
+ }), []);
46
+ return (_jsx(SideDrawer, { open: drawerOpen, onClose: () => setDrawerOpen(false), onNavigate: handleNavigate, activeId: activeId, items: items, bottomItems: bottomItems, title: title, footer: footer, width: width, className: className, children: _jsx(DrawerHeaderContext.Provider, { value: ctxValue, children: _jsxs("div", { className: "flex flex-col h-full", children: [!headerHidden && (_jsxs("div", { className: "shrink-0", children: [_jsx(NavHeader, { title: headerTitle, left: headerLeft, right: headerRight }), headerBelow] })), _jsx("div", { className: "flex-1 overflow-y-auto min-h-0", children: _jsx(Outlet, {}) }), headerFooter && (_jsx("div", { className: "shrink-0", children: headerFooter }))] }) }) }));
47
+ }
48
+ export { DrawerShell };
49
+ //# sourceMappingURL=drawer-shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer-shell.js","sourceRoot":"","sources":["../../../web/components/drawer-shell.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAgBlC,MAAM,iBAAiB,GAAG,CACxB,cAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,KAAK,EAAC,4BAA4B,YAC5F,eAAM,CAAC,EAAC,wBAAwB,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,GAAG,GACpH,CACP,CAAC;AAEF,SAAS,WAAW,CAAC,EACnB,KAAK,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,WAAW,EACX,QAAQ,GAAG,iBAAiB,EAC5B,SAAS,GACQ;IACjB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAA4B,IAAI,CAAC,CAAC;IAEtF,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,EAAU,EAAE,EAAE;QAChD,QAAQ,CAAC,EAAE,CAAC,CAAC;QACb,aAAa,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5C,IAAI,WAAW;YAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;IAEzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;IACnC,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAE1C,qFAAqF;IACrF,MAAM,MAAM,GAAG,aAAa;QAC1B,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC;QACzB,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE9B,sDAAsD;IACtD,MAAM,WAAW,GAAG,cAAc,EAAE,KAAK,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,cAAc,EAAE,MAAM,IAAI,KAAK,CAAC;IAErD,MAAM,WAAW,GAAG,MAAM;QACxB,CAAC,CAAC,CACE,KAAC,MAAM,IACL,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,MAAM,EACX,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,YAEhF,QAAQ,GACF,CACV;QACH,CAAC,CAAC,KAAC,aAAa,IAAC,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,GAAI,CAAC;IAE1D,MAAM,UAAU,GAAG,cAAc,EAAE,IAAI,IAAI,CAAC,cAAc,EAAE,MAAM;QAChE,CAAC,CAAC,CACE,KAAC,MAAM,IACL,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,MAAM,EACX,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAO,CAAC,YAE9C,QAAQ,GACF,CACV;QACH,CAAC,CAAC,WAAW,CAAC,CAAC;IAEjB,MAAM,WAAW,GAAG,cAAc,EAAE,KAAK,IAAI,SAAS,CAAC;IACvD,MAAM,WAAW,GAAG,cAAc,EAAE,KAAK,IAAI,SAAS,CAAC;IACvD,MAAM,YAAY,GAAG,cAAc,EAAE,MAAM,IAAI,SAAS,CAAC;IAEzD,gBAAgB;IAChB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9B,SAAS,EAAE,CAAC,MAA0B,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;QACpE,WAAW,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC;KAC3C,CAAC,EAAE,EAAE,CAAC,CAAC;IAER,OAAO,CACL,KAAC,UAAU,IACT,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EACnC,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,YAEpB,KAAC,mBAAmB,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,YAC3C,eAAK,SAAS,EAAC,sBAAsB,aAClC,CAAC,YAAY,IAAI,CAChB,eAAK,SAAS,EAAC,UAAU,aACvB,KAAC,SAAS,IAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAI,EACtE,WAAW,IACR,CACP,EACD,cAAK,SAAS,EAAC,gCAAgC,YAC7C,KAAC,MAAM,KAAG,GACN,EACL,YAAY,IAAI,CACf,cAAK,SAAS,EAAC,UAAU,YACtB,YAAY,GACT,CACP,IACG,GACuB,GACpB,CACd,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { type ReactNode } from 'react';
2
+ interface SideDrawerItem {
3
+ id: string;
4
+ label: string;
5
+ icon?: ReactNode;
6
+ section?: string;
7
+ }
8
+ interface SideDrawerProps {
9
+ open: boolean;
10
+ onClose: () => void;
11
+ onNavigate: (id: string) => void;
12
+ activeId: string;
13
+ items: SideDrawerItem[];
14
+ bottomItems?: SideDrawerItem[];
15
+ title?: string;
16
+ footer?: ReactNode;
17
+ children: ReactNode;
18
+ width?: number;
19
+ className?: string;
20
+ }
21
+ declare function SideDrawer({ open, onClose, onNavigate, activeId, items, bottomItems, title, footer, children, width, className, }: SideDrawerProps): import("react/jsx-runtime").JSX.Element;
22
+ declare function DrawerTrigger({ onClick, className }: {
23
+ onClick: () => void;
24
+ className?: string;
25
+ }): import("react/jsx-runtime").JSX.Element;
26
+ interface DrawerHeaderConfig {
27
+ title?: string;
28
+ left?: ReactNode;
29
+ right?: ReactNode;
30
+ below?: ReactNode;
31
+ footer?: ReactNode;
32
+ backTo?: string;
33
+ hidden?: boolean;
34
+ }
35
+ interface DrawerHeaderContextValue {
36
+ setHeader: (config: DrawerHeaderConfig) => void;
37
+ resetHeader: () => void;
38
+ }
39
+ declare const DrawerHeaderContext: import("react").Context<DrawerHeaderContextValue | null>;
40
+ declare function useDrawerHeader(config: DrawerHeaderConfig): void;
41
+ export { SideDrawer, DrawerTrigger, DrawerHeaderContext, useDrawerHeader };
42
+ export type { SideDrawerProps, SideDrawerItem, DrawerHeaderConfig, DrawerHeaderContextValue };
43
+ //# sourceMappingURL=side-drawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"side-drawer.d.ts","sourceRoot":"","sources":["../../../web/components/side-drawer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyF,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK9H,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8DD,iBAAS,UAAU,CAAC,EAClB,IAAI,EACJ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,QAAQ,EACR,KAAqB,EACrB,SAAS,GACV,EAAE,eAAe,2CAsFjB;AAID,iBAAS,aAAa,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAgBzF;AAID,UAAU,kBAAkB;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,wBAAwB;IAChC,SAAS,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAChD,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,QAAA,MAAM,mBAAmB,0DAAuD,CAAC;AAEjF,iBAAS,eAAe,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAiBzD;AAED,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,mBAAmB,EAAE,eAAe,EAAE,CAAC;AAC3E,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext, useEffect, useLayoutEffect, useMemo } from 'react';
3
+ import { cn } from '../utils/cn';
4
+ /* ── Constants ── */
5
+ const DEFAULT_WIDTH = 280;
6
+ const SHIFT_EXTRA = 20;
7
+ const MAIN_SCALE = 0.985;
8
+ const TRANSITION = 'transform 300ms cubic-bezier(0.4, 0, 0.2, 1), border-radius 300ms ease';
9
+ const SIDEBAR_TRANSITION = 'transform 300ms cubic-bezier(0.4, 0, 0.2, 1), opacity 300ms ease';
10
+ /* ── Shared item renderer ── */
11
+ function renderItemSection(sectionName, sectionItems, activeId, onNavigate) {
12
+ return (_jsxs("div", { children: [sectionName && (_jsx("div", { className: "px-3 pt-4 pb-1.5", children: _jsx("span", { className: "text-[11px] tracking-[-0.11px] text-text-dim font-normal uppercase", children: sectionName }) })), sectionItems.map((item) => {
13
+ const isActive = activeId === item.id || (item.id !== '/' && activeId.startsWith(item.id));
14
+ return (_jsxs("button", { onClick: () => onNavigate(item.id), className: cn('w-full flex items-center gap-3 px-3 h-11 rounded-[6px] mb-0.5 text-left cursor-pointer border-none', 'transition-colors', isActive
15
+ ? 'bg-surface-light text-accent'
16
+ : 'text-text hover:bg-surface-light/50'), children: [item.icon && _jsx("span", { className: "shrink-0 w-5 flex items-center justify-center", children: item.icon }), _jsx("span", { className: "text-[15px] tracking-[-0.15px] font-normal", children: item.label })] }, item.id));
17
+ })] }, sectionName || '__default'));
18
+ }
19
+ function groupBySection(items) {
20
+ const sections = new Map();
21
+ for (const item of items) {
22
+ const key = item.section ?? '';
23
+ if (!sections.has(key))
24
+ sections.set(key, []);
25
+ sections.get(key).push(item);
26
+ }
27
+ return sections;
28
+ }
29
+ /* ── SideDrawer ── */
30
+ function SideDrawer({ open, onClose, onNavigate, activeId, items, bottomItems, title, footer, children, width = DEFAULT_WIDTH, className, }) {
31
+ // Close on Escape key
32
+ useEffect(() => {
33
+ if (!open)
34
+ return;
35
+ const handler = (e) => {
36
+ if (e.key === 'Escape')
37
+ onClose();
38
+ };
39
+ document.addEventListener('keydown', handler);
40
+ return () => document.removeEventListener('keydown', handler);
41
+ }, [open, onClose]);
42
+ const shift = width + SHIFT_EXTRA;
43
+ const sections = groupBySection(items);
44
+ const bottomSections = bottomItems ? groupBySection(bottomItems) : null;
45
+ return (_jsxs("div", { className: cn('relative h-dvh w-full overflow-hidden bg-bg', className), children: [_jsxs("div", { className: "absolute top-0 left-0 bottom-0 flex flex-col bg-bg z-0", style: {
46
+ width: `${width}px`,
47
+ transform: open ? 'translateX(0)' : `translateX(-${width * 0.3}px)`,
48
+ opacity: open ? 1 : 0,
49
+ transition: SIDEBAR_TRANSITION,
50
+ pointerEvents: open ? 'auto' : 'none',
51
+ }, children: [title && (_jsx("div", { className: "shrink-0 px-4 pt-4 pb-3", children: _jsx("span", { className: "text-[20px] tracking-[-0.6px] font-normal text-text", children: title }) })), _jsx("div", { className: "flex-1 overflow-y-auto px-2 py-1", children: [...sections.entries()].map(([name, items]) => renderItemSection(name, items, activeId, onNavigate)) }), bottomSections && (_jsx("div", { className: "shrink-0 border-t border-border px-2 py-1", children: [...bottomSections.entries()].map(([name, items]) => renderItemSection(name, items, activeId, onNavigate)) })), footer && (_jsx("div", { className: cn('shrink-0 px-2 py-2', !bottomSections && 'border-t border-border'), children: footer }))] }), _jsxs("div", { className: "relative h-full w-full z-[1] bg-bg overflow-hidden", style: {
52
+ transform: open
53
+ ? `translateX(${shift}px) scale(${MAIN_SCALE})`
54
+ : 'translateX(0) scale(1)',
55
+ borderRadius: open ? '16px' : '0px',
56
+ transition: TRANSITION,
57
+ transformOrigin: 'left center',
58
+ }, children: [children, _jsx("div", { onClick: onClose, className: "absolute inset-0 bg-black cursor-pointer z-[2]", style: {
59
+ opacity: open ? 0.45 : 0,
60
+ pointerEvents: open ? 'auto' : 'none',
61
+ transition: 'opacity 300ms ease',
62
+ } })] })] }));
63
+ }
64
+ /* ── DrawerTrigger (hamburger button) ── */
65
+ function DrawerTrigger({ onClick, className }) {
66
+ return (_jsx("button", { onClick: onClick, className: cn('flex items-center justify-center w-9 h-9 rounded-[6px] cursor-pointer border-none bg-transparent', 'text-text hover:bg-surface-light transition-colors', className), "aria-label": "Open menu", children: _jsx("svg", { width: "18", height: "14", viewBox: "0 0 18 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M1 1h16M1 7h16M1 13h16", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }));
67
+ }
68
+ const DrawerHeaderContext = createContext(null);
69
+ function useDrawerHeader(config) {
70
+ const ctx = useContext(DrawerHeaderContext);
71
+ const configRef = useMemo(() => config, [
72
+ config.title,
73
+ config.left,
74
+ config.right,
75
+ config.below,
76
+ config.footer,
77
+ config.backTo,
78
+ config.hidden,
79
+ ]);
80
+ useLayoutEffect(() => {
81
+ if (!ctx)
82
+ return;
83
+ ctx.setHeader(configRef);
84
+ return () => ctx.resetHeader();
85
+ }, [ctx, configRef]);
86
+ }
87
+ export { SideDrawer, DrawerTrigger, DrawerHeaderContext, useDrawerHeader };
88
+ //# sourceMappingURL=side-drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"side-drawer.js","sourceRoot":"","sources":["../../../web/components/side-drawer.tsx"],"names":[],"mappings":"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"}
@@ -102,6 +102,10 @@ export { AudioPlayer } from './components/audio-player';
102
102
  export type { AudioPlayerProps } from './components/audio-player';
103
103
  export { AppShell } from './components/app-shell';
104
104
  export type { AppShellProps } from './components/app-shell';
105
+ export { SideDrawer, DrawerTrigger, DrawerHeaderContext, useDrawerHeader } from './components/side-drawer';
106
+ export type { SideDrawerProps, SideDrawerItem, DrawerHeaderConfig, DrawerHeaderContextValue } from './components/side-drawer';
107
+ export { DrawerShell } from './components/drawer-shell';
108
+ export type { DrawerShellProps } from './components/drawer-shell';
105
109
  export { ScrollPicker, DatePicker, TimePicker, SelectionPicker } from './components/scroll-picker';
106
110
  export type { ScrollPickerProps, ScrollColumn, DatePickerProps, TimePickerProps, SelectionPickerProps } from './components/scroll-picker';
107
111
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../web/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../web/index.ts"],"names":[],"mappings":"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"}
package/dist/web/index.js CHANGED
@@ -64,6 +64,8 @@ export { ImageGrid, ImageViewer } from './components/image-viewer';
64
64
  export { AudioPlayer } from './components/audio-player';
65
65
  // App Shell
66
66
  export { AppShell } from './components/app-shell';
67
+ export { SideDrawer, DrawerTrigger, DrawerHeaderContext, useDrawerHeader } from './components/side-drawer';
68
+ export { DrawerShell } from './components/drawer-shell';
67
69
  // Scroll Picker
68
70
  export { ScrollPicker, DatePicker, TimePicker, SelectionPicker } from './components/scroll-picker';
69
71
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../web/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,QAAQ;AACR,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAEhC,QAAQ;AACR,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEhF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE7E,aAAa;AACb,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG7D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAGzC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEnG,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGhE,sBAAsB;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAG9C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,qBAAqB;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,8CAA8C;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGxE,oCAAoC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAG5D,+BAA+B;AAC/B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGtJ,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGxF,+BAA+B;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,0BAA0B;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,YAAY;AACZ,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../web/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "even-toolkit",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Design system & component library for Even Realities G2 smart glasses apps — 55+ web components, 191 pixel-art icons, glasses SDK bridge, and design tokens.",
5
5
  "type": "module",
6
6
  "main": "./dist/glasses/index.js",
@@ -0,0 +1,139 @@
1
+ import { useState, useCallback, useMemo, type ReactNode } from 'react';
2
+ import { Outlet, useLocation, useNavigate } from 'react-router';
3
+ import { SideDrawer, DrawerTrigger, DrawerHeaderContext } from './side-drawer';
4
+ import type { SideDrawerItem, DrawerHeaderConfig } from './side-drawer';
5
+ import { NavHeader } from './nav-header';
6
+ import { Button } from './button';
7
+
8
+ interface DrawerShellProps {
9
+ items: SideDrawerItem[];
10
+ bottomItems?: SideDrawerItem[];
11
+ title?: string;
12
+ footer?: ReactNode;
13
+ width?: number;
14
+ getPageTitle: (pathname: string) => string;
15
+ deriveActiveId: (pathname: string) => string;
16
+ isNestedRoute?: (pathname: string) => boolean;
17
+ getBackPath?: (pathname: string) => string;
18
+ backIcon?: ReactNode;
19
+ className?: string;
20
+ }
21
+
22
+ const DEFAULT_BACK_ICON = (
23
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
24
+ <path d="M12.5 15L7.5 10L12.5 5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
25
+ </svg>
26
+ );
27
+
28
+ function DrawerShell({
29
+ items,
30
+ bottomItems,
31
+ title,
32
+ footer,
33
+ width,
34
+ getPageTitle,
35
+ deriveActiveId,
36
+ isNestedRoute,
37
+ getBackPath,
38
+ backIcon = DEFAULT_BACK_ICON,
39
+ className,
40
+ }: DrawerShellProps) {
41
+ const location = useLocation();
42
+ const navigate = useNavigate();
43
+ const [drawerOpen, setDrawerOpen] = useState(false);
44
+ const [headerOverride, setHeaderOverride] = useState<DrawerHeaderConfig | null>(null);
45
+
46
+ const handleNavigate = useCallback((id: string) => {
47
+ navigate(id);
48
+ setDrawerOpen(false);
49
+ }, [navigate]);
50
+
51
+ const allItemIds = useMemo(() => {
52
+ const ids = new Set(items.map((i) => i.id));
53
+ if (bottomItems) bottomItems.forEach((i) => ids.add(i.id));
54
+ return ids;
55
+ }, [items, bottomItems]);
56
+
57
+ const pathname = location.pathname;
58
+ const activeId = deriveActiveId(pathname);
59
+
60
+ // Determine if nested: either explicit function or check if path matches any item id
61
+ const nested = isNestedRoute
62
+ ? isNestedRoute(pathname)
63
+ : !allItemIds.has(pathname);
64
+
65
+ // Resolve header values (screen overrides > defaults)
66
+ const headerTitle = headerOverride?.title ?? getPageTitle(pathname);
67
+ const headerHidden = headerOverride?.hidden ?? false;
68
+
69
+ const defaultLeft = nested
70
+ ? (
71
+ <Button
72
+ variant="ghost"
73
+ size="icon"
74
+ onClick={() => navigate(headerOverride?.backTo ?? getBackPath?.(pathname) ?? '/')}
75
+ >
76
+ {backIcon}
77
+ </Button>
78
+ )
79
+ : <DrawerTrigger onClick={() => setDrawerOpen(true)} />;
80
+
81
+ const headerLeft = headerOverride?.left ?? (headerOverride?.backTo
82
+ ? (
83
+ <Button
84
+ variant="ghost"
85
+ size="icon"
86
+ onClick={() => navigate(headerOverride.backTo!)}
87
+ >
88
+ {backIcon}
89
+ </Button>
90
+ )
91
+ : defaultLeft);
92
+
93
+ const headerRight = headerOverride?.right ?? undefined;
94
+ const headerBelow = headerOverride?.below ?? undefined;
95
+ const headerFooter = headerOverride?.footer ?? undefined;
96
+
97
+ // Context value
98
+ const ctxValue = useMemo(() => ({
99
+ setHeader: (config: DrawerHeaderConfig) => setHeaderOverride(config),
100
+ resetHeader: () => setHeaderOverride(null),
101
+ }), []);
102
+
103
+ return (
104
+ <SideDrawer
105
+ open={drawerOpen}
106
+ onClose={() => setDrawerOpen(false)}
107
+ onNavigate={handleNavigate}
108
+ activeId={activeId}
109
+ items={items}
110
+ bottomItems={bottomItems}
111
+ title={title}
112
+ footer={footer}
113
+ width={width}
114
+ className={className}
115
+ >
116
+ <DrawerHeaderContext.Provider value={ctxValue}>
117
+ <div className="flex flex-col h-full">
118
+ {!headerHidden && (
119
+ <div className="shrink-0">
120
+ <NavHeader title={headerTitle} left={headerLeft} right={headerRight} />
121
+ {headerBelow}
122
+ </div>
123
+ )}
124
+ <div className="flex-1 overflow-y-auto min-h-0">
125
+ <Outlet />
126
+ </div>
127
+ {headerFooter && (
128
+ <div className="shrink-0">
129
+ {headerFooter}
130
+ </div>
131
+ )}
132
+ </div>
133
+ </DrawerHeaderContext.Provider>
134
+ </SideDrawer>
135
+ );
136
+ }
137
+
138
+ export { DrawerShell };
139
+ export type { DrawerShellProps };
@@ -0,0 +1,246 @@
1
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState, type ReactNode } from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ /* ── Types ── */
5
+
6
+ interface SideDrawerItem {
7
+ id: string;
8
+ label: string;
9
+ icon?: ReactNode;
10
+ section?: string;
11
+ }
12
+
13
+ interface SideDrawerProps {
14
+ open: boolean;
15
+ onClose: () => void;
16
+ onNavigate: (id: string) => void;
17
+ activeId: string;
18
+ items: SideDrawerItem[];
19
+ bottomItems?: SideDrawerItem[];
20
+ title?: string;
21
+ footer?: ReactNode;
22
+ children: ReactNode;
23
+ width?: number;
24
+ className?: string;
25
+ }
26
+
27
+ /* ── Constants ── */
28
+
29
+ const DEFAULT_WIDTH = 280;
30
+ const SHIFT_EXTRA = 20;
31
+ const MAIN_SCALE = 0.985;
32
+ const TRANSITION = 'transform 300ms cubic-bezier(0.4, 0, 0.2, 1), border-radius 300ms ease';
33
+ const SIDEBAR_TRANSITION = 'transform 300ms cubic-bezier(0.4, 0, 0.2, 1), opacity 300ms ease';
34
+
35
+ /* ── Shared item renderer ── */
36
+
37
+ function renderItemSection(
38
+ sectionName: string,
39
+ sectionItems: SideDrawerItem[],
40
+ activeId: string,
41
+ onNavigate: (id: string) => void,
42
+ ) {
43
+ return (
44
+ <div key={sectionName || '__default'}>
45
+ {sectionName && (
46
+ <div className="px-3 pt-4 pb-1.5">
47
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim font-normal uppercase">
48
+ {sectionName}
49
+ </span>
50
+ </div>
51
+ )}
52
+ {sectionItems.map((item) => {
53
+ const isActive = activeId === item.id || (item.id !== '/' && activeId.startsWith(item.id));
54
+ return (
55
+ <button
56
+ key={item.id}
57
+ onClick={() => onNavigate(item.id)}
58
+ className={cn(
59
+ 'w-full flex items-center gap-3 px-3 h-11 rounded-[6px] mb-0.5 text-left cursor-pointer border-none',
60
+ 'transition-colors',
61
+ isActive
62
+ ? 'bg-surface-light text-accent'
63
+ : 'text-text hover:bg-surface-light/50',
64
+ )}
65
+ >
66
+ {item.icon && <span className="shrink-0 w-5 flex items-center justify-center">{item.icon}</span>}
67
+ <span className="text-[15px] tracking-[-0.15px] font-normal">{item.label}</span>
68
+ </button>
69
+ );
70
+ })}
71
+ </div>
72
+ );
73
+ }
74
+
75
+ function groupBySection(items: SideDrawerItem[]): Map<string, SideDrawerItem[]> {
76
+ const sections = new Map<string, SideDrawerItem[]>();
77
+ for (const item of items) {
78
+ const key = item.section ?? '';
79
+ if (!sections.has(key)) sections.set(key, []);
80
+ sections.get(key)!.push(item);
81
+ }
82
+ return sections;
83
+ }
84
+
85
+ /* ── SideDrawer ── */
86
+
87
+ function SideDrawer({
88
+ open,
89
+ onClose,
90
+ onNavigate,
91
+ activeId,
92
+ items,
93
+ bottomItems,
94
+ title,
95
+ footer,
96
+ children,
97
+ width = DEFAULT_WIDTH,
98
+ className,
99
+ }: SideDrawerProps) {
100
+ // Close on Escape key
101
+ useEffect(() => {
102
+ if (!open) return;
103
+ const handler = (e: KeyboardEvent) => {
104
+ if (e.key === 'Escape') onClose();
105
+ };
106
+ document.addEventListener('keydown', handler);
107
+ return () => document.removeEventListener('keydown', handler);
108
+ }, [open, onClose]);
109
+
110
+ const shift = width + SHIFT_EXTRA;
111
+ const sections = groupBySection(items);
112
+ const bottomSections = bottomItems ? groupBySection(bottomItems) : null;
113
+
114
+ return (
115
+ <div className={cn('relative h-dvh w-full overflow-hidden bg-bg', className)}>
116
+ {/* Sidebar panel */}
117
+ <div
118
+ className="absolute top-0 left-0 bottom-0 flex flex-col bg-bg z-0"
119
+ style={{
120
+ width: `${width}px`,
121
+ transform: open ? 'translateX(0)' : `translateX(-${width * 0.3}px)`,
122
+ opacity: open ? 1 : 0,
123
+ transition: SIDEBAR_TRANSITION,
124
+ pointerEvents: open ? 'auto' : 'none',
125
+ }}
126
+ >
127
+ {/* Header */}
128
+ {title && (
129
+ <div className="shrink-0 px-4 pt-4 pb-3">
130
+ <span className="text-[20px] tracking-[-0.6px] font-normal text-text">{title}</span>
131
+ </div>
132
+ )}
133
+
134
+ {/* Menu items */}
135
+ <div className="flex-1 overflow-y-auto px-2 py-1">
136
+ {[...sections.entries()].map(([name, items]) =>
137
+ renderItemSection(name, items, activeId, onNavigate),
138
+ )}
139
+ </div>
140
+
141
+ {/* Bottom items */}
142
+ {bottomSections && (
143
+ <div className="shrink-0 border-t border-border px-2 py-1">
144
+ {[...bottomSections.entries()].map(([name, items]) =>
145
+ renderItemSection(name, items, activeId, onNavigate),
146
+ )}
147
+ </div>
148
+ )}
149
+
150
+ {/* Footer */}
151
+ {footer && (
152
+ <div className={cn('shrink-0 px-2 py-2', !bottomSections && 'border-t border-border')}>
153
+ {footer}
154
+ </div>
155
+ )}
156
+ </div>
157
+
158
+ {/* Main content */}
159
+ <div
160
+ className="relative h-full w-full z-[1] bg-bg overflow-hidden"
161
+ style={{
162
+ transform: open
163
+ ? `translateX(${shift}px) scale(${MAIN_SCALE})`
164
+ : 'translateX(0) scale(1)',
165
+ borderRadius: open ? '16px' : '0px',
166
+ transition: TRANSITION,
167
+ transformOrigin: 'left center',
168
+ }}
169
+ >
170
+ {children}
171
+
172
+ {/* Dark overlay */}
173
+ <div
174
+ onClick={onClose}
175
+ className="absolute inset-0 bg-black cursor-pointer z-[2]"
176
+ style={{
177
+ opacity: open ? 0.45 : 0,
178
+ pointerEvents: open ? 'auto' : 'none',
179
+ transition: 'opacity 300ms ease',
180
+ }}
181
+ />
182
+ </div>
183
+ </div>
184
+ );
185
+ }
186
+
187
+ /* ── DrawerTrigger (hamburger button) ── */
188
+
189
+ function DrawerTrigger({ onClick, className }: { onClick: () => void; className?: string }) {
190
+ return (
191
+ <button
192
+ onClick={onClick}
193
+ className={cn(
194
+ 'flex items-center justify-center w-9 h-9 rounded-[6px] cursor-pointer border-none bg-transparent',
195
+ 'text-text hover:bg-surface-light transition-colors',
196
+ className,
197
+ )}
198
+ aria-label="Open menu"
199
+ >
200
+ <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
201
+ <path d="M1 1h16M1 7h16M1 13h16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
202
+ </svg>
203
+ </button>
204
+ );
205
+ }
206
+
207
+ /* ── Drawer Header Context ── */
208
+
209
+ interface DrawerHeaderConfig {
210
+ title?: string;
211
+ left?: ReactNode;
212
+ right?: ReactNode;
213
+ below?: ReactNode;
214
+ footer?: ReactNode;
215
+ backTo?: string;
216
+ hidden?: boolean;
217
+ }
218
+
219
+ interface DrawerHeaderContextValue {
220
+ setHeader: (config: DrawerHeaderConfig) => void;
221
+ resetHeader: () => void;
222
+ }
223
+
224
+ const DrawerHeaderContext = createContext<DrawerHeaderContextValue | null>(null);
225
+
226
+ function useDrawerHeader(config: DrawerHeaderConfig): void {
227
+ const ctx = useContext(DrawerHeaderContext);
228
+ const configRef = useMemo(() => config, [
229
+ config.title,
230
+ config.left,
231
+ config.right,
232
+ config.below,
233
+ config.footer,
234
+ config.backTo,
235
+ config.hidden,
236
+ ]);
237
+
238
+ useLayoutEffect(() => {
239
+ if (!ctx) return;
240
+ ctx.setHeader(configRef);
241
+ return () => ctx.resetHeader();
242
+ }, [ctx, configRef]);
243
+ }
244
+
245
+ export { SideDrawer, DrawerTrigger, DrawerHeaderContext, useDrawerHeader };
246
+ export type { SideDrawerProps, SideDrawerItem, DrawerHeaderConfig, DrawerHeaderContextValue };
package/web/index.ts CHANGED
@@ -167,6 +167,12 @@ export type { AudioPlayerProps } from './components/audio-player';
167
167
  export { AppShell } from './components/app-shell';
168
168
  export type { AppShellProps } from './components/app-shell';
169
169
 
170
+ export { SideDrawer, DrawerTrigger, DrawerHeaderContext, useDrawerHeader } from './components/side-drawer';
171
+ export type { SideDrawerProps, SideDrawerItem, DrawerHeaderConfig, DrawerHeaderContextValue } from './components/side-drawer';
172
+
173
+ export { DrawerShell } from './components/drawer-shell';
174
+ export type { DrawerShellProps } from './components/drawer-shell';
175
+
170
176
  // Scroll Picker
171
177
  export { ScrollPicker, DatePicker, TimePicker, SelectionPicker } from './components/scroll-picker';
172
178
  export type { ScrollPickerProps, ScrollColumn, DatePickerProps, TimePickerProps, SelectionPickerProps } from './components/scroll-picker';