@krizaka/ui 3.8.0 → 3.9.0
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 +10 -8
- package/dist/action-bar.js +2 -1
- package/dist/action-bar.js.map +1 -1
- package/dist/app-shell.d.ts +81 -3
- package/dist/app-shell.js +163 -35
- package/dist/app-shell.js.map +1 -1
- package/dist/audio-player.js +1 -1
- package/dist/badge.d.ts +36 -39
- package/dist/badge.js +11 -2
- package/dist/badge.js.map +1 -1
- package/dist/blocks.css +56 -4
- package/dist/button-BmCrLCq7.d.ts +114 -0
- package/dist/button.d.ts +48 -48
- package/dist/button.js +2 -1
- package/dist/card.d.ts +111 -111
- package/dist/card.js +10 -2
- package/dist/card.js.map +1 -1
- package/dist/chip.d.ts +24 -24
- package/dist/chip.js +10 -2
- package/dist/chip.js.map +1 -1
- package/dist/{chunk-UOMP6VOZ.js → chunk-276PTQMQ.js} +10 -4
- package/dist/chunk-276PTQMQ.js.map +1 -0
- package/dist/{chunk-Z6FUPB3Q.js → chunk-2NB27LM7.js} +11 -3
- package/dist/chunk-2NB27LM7.js.map +1 -0
- package/dist/chunk-2VNYHG4E.js +9 -0
- package/dist/chunk-2VNYHG4E.js.map +1 -0
- package/dist/{chunk-ND3L5VHT.js → chunk-5Z7FWCV2.js} +2 -2
- package/dist/{chunk-YH4LMWRU.js → chunk-7UCPGXKN.js} +11 -3
- package/dist/chunk-7UCPGXKN.js.map +1 -0
- package/dist/{chunk-AZPKXP3O.js → chunk-AFY35UCB.js} +6 -1
- package/dist/{chunk-AZPKXP3O.js.map → chunk-AFY35UCB.js.map} +1 -1
- package/dist/{chunk-LCZXS6AK.js → chunk-CKSHLSDH.js} +5 -5
- package/dist/chunk-CKSHLSDH.js.map +1 -0
- package/dist/chunk-D3T54RSL.js +11 -0
- package/dist/chunk-D3T54RSL.js.map +1 -0
- package/dist/{chunk-ZKVQGXLD.js → chunk-FKBHWS4N.js} +2 -2
- package/dist/{chunk-BRKMQ6JV.js → chunk-FZBWEDQI.js} +1 -23
- package/dist/chunk-FZBWEDQI.js.map +1 -0
- package/dist/{chunk-HCNOOPZE.js → chunk-KVDB4GXV.js} +2 -2
- package/dist/{chunk-RUPNPMP3.js → chunk-OWC2LZD2.js} +11 -3
- package/dist/chunk-OWC2LZD2.js.map +1 -0
- package/dist/{chunk-FDCMBWUU.js → chunk-REPTKUJ6.js} +4 -4
- package/dist/chunk-REPTKUJ6.js.map +1 -0
- package/dist/code-block.d.ts +32 -10
- package/dist/code-block.js +51 -43
- package/dist/code-block.js.map +1 -1
- package/dist/combobox.d.ts +3 -3
- package/dist/combobox.js +4 -3
- package/dist/command.js +3 -2
- package/dist/command.js.map +1 -1
- package/dist/component-page.js.map +1 -1
- package/dist/confirm-button.d.ts +1 -1
- package/dist/confirm-button.js +2 -1
- package/dist/confirm-button.js.map +1 -1
- package/dist/continue-prompt.js +11 -3
- package/dist/continue-prompt.js.map +1 -1
- package/dist/data-table.js +7 -6
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.js +1 -1
- package/dist/dialog.js +3 -2
- package/dist/dialog.js.map +1 -1
- package/dist/file-upload.d.ts +12 -1
- package/dist/file-upload.js +73 -89
- package/dist/file-upload.js.map +1 -1
- package/dist/filter-bar.js +3 -2
- package/dist/image-cropper.js +1 -1
- package/dist/list.js +1 -1
- package/dist/map.js.map +1 -1
- package/dist/markdown.d.ts +38 -0
- package/dist/markdown.js +150 -0
- package/dist/markdown.js.map +1 -0
- package/dist/match-dialog.js +2 -1
- package/dist/match-dialog.js.map +1 -1
- package/dist/media-capture.js +1 -1
- package/dist/native/markdown.d.ts +24 -0
- package/dist/native/markdown.js +200 -0
- package/dist/native/markdown.js.map +1 -0
- package/dist/native.d.ts +1 -7
- package/dist/native.js +357 -95
- package/dist/native.js.map +1 -1
- package/dist/recipes.d.ts +143 -0
- package/dist/recipes.js +180 -0
- package/dist/recipes.js.map +1 -0
- package/dist/search-field.js +2 -2
- package/dist/swipe-deck.js +1 -1
- package/dist/tag-input.js +4 -3
- package/dist/tag-input.js.map +1 -1
- package/dist/tag-selector.js +2 -1
- package/dist/tag-selector.js.map +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +2 -1
- package/dist/theme.js.map +1 -1
- package/dist/tree-view.js +1 -1
- package/dist/vertical-video.js +2 -1
- package/dist/vertical-video.js.map +1 -1
- package/dist/video-player.js +1 -1
- package/dist/video-trimmer.js +1 -1
- package/dist/virtual.js +2 -2
- package/package.json +47 -16
- package/registry/achievement.json +1 -1
- package/registry/action-bar.json +1 -1
- package/registry/activity-feed.json +1 -1
- package/registry/alert.json +1 -1
- package/registry/app-shell.json +135 -12
- package/registry/argument-card.json +1 -1
- package/registry/audio-player.json +2 -2
- package/registry/auth-layout.json +1 -1
- package/registry/avatar.json +1 -1
- package/registry/badge.json +9 -3
- package/registry/button.json +7 -2
- package/registry/card-grid.json +1 -1
- package/registry/card.json +8 -3
- package/registry/catalogue-page.json +1 -1
- package/registry/changelog-page.json +1 -1
- package/registry/chat.json +1 -1
- package/registry/checkbox.json +1 -1
- package/registry/chip-link.json +1 -1
- package/registry/chip.json +9 -3
- package/registry/choice-grid.json +1 -1
- package/registry/code-block.json +65 -21
- package/registry/combobox.json +4 -3
- package/registry/command.json +1 -1
- package/registry/component-page.json +4 -4
- package/registry/confirm-button.json +1 -1
- package/registry/contact-page.json +1 -1
- package/registry/continue-prompt.json +1 -1
- package/registry/countdown.json +1 -1
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +1 -1
- package/registry/data-table.json +2 -2
- package/registry/date-picker.json +3 -3
- package/registry/decision-list.json +1 -1
- package/registry/description-list.json +1 -1
- package/registry/detail-layout.json +1 -1
- package/registry/dialog.json +1 -1
- package/registry/docs-article-page.json +1 -1
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/dropdown-menu.json +1 -1
- package/registry/empty-state.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/app-shell/console.d.ts +4 -0
- package/registry/examples/app-shell/console.js +93 -0
- package/registry/examples/app-shell/console.tsx +103 -0
- package/registry/examples/badge/native/dot.dark.png +0 -0
- package/registry/examples/badge/native/dot.light.png +0 -0
- package/registry/examples/badge/native/scrim.dark.png +0 -0
- package/registry/examples/badge/native/scrim.light.png +0 -0
- package/registry/examples/badge/native/tones.dark.png +0 -0
- package/registry/examples/badge/native/tones.light.png +0 -0
- package/registry/examples/card/native/default.dark.png +0 -0
- package/registry/examples/card/native/default.light.png +0 -0
- package/registry/examples/card/native/elevated.light.png +0 -0
- package/registry/examples/chip/native/multiple.dark.png +0 -0
- package/registry/examples/chip/native/multiple.light.png +0 -0
- package/registry/examples/chip/native/removable.dark.png +0 -0
- package/registry/examples/chip/native/removable.light.png +0 -0
- package/registry/examples/chip/native/single.dark.png +0 -0
- package/registry/examples/chip/native/single.light.png +0 -0
- package/registry/examples/chip/native/toggle.dark.png +0 -0
- package/registry/examples/chip/native/toggle.light.png +0 -0
- package/registry/examples/code-block/annotated.d.ts +4 -0
- package/registry/examples/code-block/annotated.js +31 -0
- package/registry/examples/code-block/annotated.tsx +26 -0
- package/registry/examples/component-page/button.tsx +1 -1
- package/registry/examples/form/native/amount.dark.png +0 -0
- package/registry/examples/form/native/amount.light.png +0 -0
- package/registry/examples/markdown/ai-stream.d.ts +4 -0
- package/registry/examples/markdown/ai-stream.js +31 -0
- package/registry/examples/markdown/ai-stream.tsx +32 -0
- package/registry/examples/markdown/basic.d.ts +4 -0
- package/registry/examples/markdown/basic.js +26 -0
- package/registry/examples/markdown/basic.tsx +22 -0
- package/registry/examples/markdown/native/ai-stream.d.ts +4 -0
- package/registry/examples/markdown/native/ai-stream.dark.png +0 -0
- package/registry/examples/markdown/native/ai-stream.js +17 -0
- package/registry/examples/markdown/native/ai-stream.light.png +0 -0
- package/registry/examples/markdown/native/ai-stream.tsx +17 -0
- package/registry/examples/markdown/native/basic.d.ts +4 -0
- package/registry/examples/markdown/native/basic.dark.png +0 -0
- package/registry/examples/markdown/native/basic.js +28 -0
- package/registry/examples/markdown/native/basic.light.png +0 -0
- package/registry/examples/markdown/native/basic.tsx +27 -0
- package/registry/examples/recipes/tone-chip.d.ts +4 -0
- package/registry/examples/recipes/tone-chip.js +46 -0
- package/registry/examples/recipes/tone-chip.tsx +32 -0
- package/registry/examples/screen/native/profile.dark.png +0 -0
- package/registry/examples/screen/native/profile.light.png +0 -0
- package/registry/examples/tag-selector/native/interests.dark.png +0 -0
- package/registry/examples/tag-selector/native/interests.light.png +0 -0
- package/registry/field.json +1 -1
- package/registry/file-upload.json +8 -3
- package/registry/filter-bar.json +1 -1
- package/registry/form.json +2 -2
- package/registry/gallery-page.json +1 -1
- package/registry/glow-card.json +1 -1
- package/registry/goal-gauge.json +1 -1
- package/registry/how-it-works-page.json +1 -1
- package/registry/image-cropper.json +2 -2
- package/registry/index.json +76 -16
- package/registry/iso.json +1 -1
- package/registry/kanban.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/landing-page.json +1 -1
- package/registry/landscape.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +2 -2
- package/registry/loader.json +1 -1
- package/registry/longform-page.json +1 -1
- package/registry/map.json +3 -3
- package/registry/markdown.json +266 -0
- package/registry/marketing-layout.json +1 -1
- package/registry/marquee.json +1 -1
- package/registry/mascot-quote.json +1 -1
- package/registry/mascot-state.json +1 -1
- package/registry/mascots.json +1 -1
- package/registry/match-dialog.json +1 -1
- package/registry/media-capture.json +2 -2
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +1 -1
- package/registry/number-ticker.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page.json +1 -1
- package/registry/pagination.json +1 -1
- package/registry/photo-grid.json +1 -1
- package/registry/pointer-light.json +1 -1
- package/registry/popover.json +1 -1
- package/registry/pricing-page.json +1 -1
- package/registry/product-page.json +1 -1
- package/registry/profile-card.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/promise-card.json +1 -1
- package/registry/radio-group.json +1 -1
- package/registry/recipes.json +99 -0
- package/registry/scroll-stage.json +1 -1
- package/registry/search-field.json +4 -3
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/settings-layout.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/skip-link.json +1 -1
- package/registry/slider.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/spinner.json +1 -1
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +1 -1
- package/registry/stat.json +1 -1
- package/registry/status-page.json +1 -1
- package/registry/step-progress.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/swipe-deck.json +2 -2
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- package/registry/tag-input.json +2 -2
- package/registry/tag-selector.json +1 -1
- package/registry/terminal-replay.json +1 -1
- package/registry/theme.json +1 -1
- package/registry/toast.json +1 -1
- package/registry/toc.json +1 -1
- package/registry/tooltip.json +1 -1
- package/registry/tree-view.json +2 -2
- package/registry/verified-badge.json +1 -1
- package/registry/vertical-video.json +2 -2
- package/registry/video-player.json +2 -2
- package/registry/video-trimmer.json +2 -2
- package/registry/virtual.json +2 -2
- package/dist/button-BMAO7dKF.d.ts +0 -114
- package/dist/chunk-BRKMQ6JV.js.map +0 -1
- package/dist/chunk-FDCMBWUU.js.map +0 -1
- package/dist/chunk-LCZXS6AK.js.map +0 -1
- package/dist/chunk-RUPNPMP3.js.map +0 -1
- package/dist/chunk-UOMP6VOZ.js.map +0 -1
- package/dist/chunk-YH4LMWRU.js.map +0 -1
- package/dist/chunk-Z6FUPB3Q.js.map +0 -1
- /package/dist/{chunk-ND3L5VHT.js.map → chunk-5Z7FWCV2.js.map} +0 -0
- /package/dist/{chunk-ZKVQGXLD.js.map → chunk-FKBHWS4N.js.map} +0 -0
- /package/dist/{chunk-HCNOOPZE.js.map → chunk-KVDB4GXV.js.map} +0 -0
package/README.md
CHANGED
|
@@ -185,7 +185,7 @@ light or a product's — is a set of tokens.
|
|
|
185
185
|
| `@krizaka/ui/date-picker` | `DatePicker`, `DateRangePicker` (`presets`), `Calendar` — ISO dates, the locale's names through `@krizaka/intl`, `min`/`max`/`isDateDisabled`, the APG grid keyboard | client |
|
|
186
186
|
| `@krizaka/ui/search-field` | `SearchField` (`onSearch` debounced, `onSubmit`, `shortcut` ⌘K, `suggestions`, `clearLabel`, `landmark`) | client |
|
|
187
187
|
| `@krizaka/ui/video-player` | `VideoPlayer` (`src` or a signed-source function, `loadHls={() => import("hls.js")}` (optional peer, loaded only without native HLS), `tracks`, `playbackRates`, `preload`, `pauseWhenHidden`, `overlay` for a gate, `labels`; keyboard Space/K, J/L, arrows, M, F, C) | client |
|
|
188
|
-
| `@krizaka/ui/file-upload` | `Dropzone` (`accept`, `maxSize`, `maxFiles`, refusals with reasons), `UploadList` (preview, progress, speed, pause/resume/retry/cancel), `useUploadQueue` (`upload(file, { signal, onProgress, resumed })`: Tus-ready without depending on it), `acceptsFile`, `formatBytes` | client |
|
|
188
|
+
| `@krizaka/ui/file-upload` | `Dropzone` (`accept`, `maxSize`, `maxFiles`, refusals with reasons), `UploadList` (preview, progress, speed, pause/resume/retry/cancel), `useUploadQueue` (`upload(file, { signal, onProgress, resumed })`: Tus-ready without depending on it; `retries`, `backoff`, `shouldRetry` — a queue of `@krizaka/pacer`), `acceptsFile`, `formatBytes` | client |
|
|
189
189
|
| `@krizaka/ui/media-capture` | `MediaCapture` (`mode` photo·video·both, `facing`, `maxDuration`, permission asked on a gesture, the file picker as the way out, `onCapture({ blob, kind, url })`) | client |
|
|
190
190
|
| `@krizaka/ui/video-trimmer` | `VideoTrimmer` (`duration`, `value` [start, end], `minLength`, `step`/`largeStep`, `frames` or children, `time` + `onSeek`, `classNames` per part, `renderHandle`) | client |
|
|
191
191
|
| `@krizaka/ui/audio-player` | `AudioPlayer` (a seekable waveform: `peaks` or `computePeaks`, `bars`, `playbackRates`, `duration`), `resamplePeaks`, `computeAudioPeaks` | client |
|
|
@@ -198,11 +198,13 @@ light or a product's — is a set of tokens.
|
|
|
198
198
|
| `@krizaka/ui/argument-card` | `ArgumentCard` (compact: `icon`, `title`, `meta`, `facts`, `hook` — the trap in one line —, `more`, `actions`; in a panel: `points`, `rule`, `details`, `panelLabel`) | server |
|
|
199
199
|
| `@krizaka/ui/mascot-quote` | `MascotQuote` (`bird`, `mood`, `label`, the sentence as children, `variant` inline·callout, `animated`) | client |
|
|
200
200
|
| `@krizaka/ui/decision-list` | `DecisionList` (`decisions`: `chose`, `refused`, `because`, `cost`; `labels`, `reveal`) | server |
|
|
201
|
-
| `@krizaka/ui/code-block` | `CodeBlock` (`code`, `language`, `file`, `copyLabel`, `copiedLabel`, `highlight`), `CodeTabs` (`label`, `tabs`), `highlight` | client |
|
|
201
|
+
| `@krizaka/ui/code-block` | `CodeBlock` (`code`, `language`, `file`, `lineNumbers`, `annotations` — highlight · added · removed · focus · dim · error · warning —, `copyLabel`, `copiedLabel`, `highlight`), `CodeTabs` (`label`, `tabs`), `highlight`, `normalizeCodeLanguage` — on TanStack Highlight (TS, TSX, JS, JSON, YAML, Bash, HTML, CSS, Java, Kotlin, diff) | client |
|
|
202
|
+
| `@krizaka/ui/markdown` | `Markdown` (the source as children; `streaming`, `headingIds`, `codeLabels`, `linkAs`), `parseSource`, `healStreaming` — TanStack Markdown on the tokens, raw HTML never rendered; `@krizaka/ui/native/markdown`: the same AST in `Text` and `View` (`onLinkPress`) | server |
|
|
203
|
+
| `@krizaka/ui/recipes` | `defineRecipe` (the variants of a component as classes of the tokens: `tv(recipe)` on the web), `nativeRecipe(recipe).resolve(props, theme)` (the phone's styles from the same classes), `ROLES`, `withAlpha` | server |
|
|
202
204
|
| `@krizaka/ui/action-bar` | `ActionBar` (`actions`: `href`, `label`, `icon`; `variant` buttons·icons — the words in a CSS tooltip —, `primaryFirst`, `size`, `label`, `linkAs`) | server |
|
|
203
205
|
| `@krizaka/ui/cta-panel` | `CtaPanel` (`id`, `title`, `body`, the actions as children, `level`, `reveal`) | server |
|
|
204
206
|
| `@krizaka/ui/toc` | `Toc` (`label`, `items`; the section being read marked), `TocLayout` (`toc`, the sections as children) | client |
|
|
205
|
-
| `@krizaka/ui/app-shell` | `AppShell.Root` (`defaultCollapsed`, `collapsed` + `onCollapsedChange`) `/Sidebar` (`label`, `brand`, `footer`, `collapseLabels`, `closeLabel`; a drawer on a phone) `/Group/Item` (`icon`, `label`, `current`, `badge`, `asChild`) `/Body/Header` (`title`, `menuLabel`, `actions`) `/Main` — see [Layouts and templates](#layouts-and-templates) | client |
|
|
207
|
+
| `@krizaka/ui/app-shell` | `AppShell.Root` (`defaultCollapsed`, `collapsed` + `onCollapsedChange`) `/Sidebar` (`label`, `brand`, `footer`, `collapseLabels`, `closeLabel`; a drawer on a phone) `/Group/Item` (`icon`, `label`, `current`, `badge`, `shortcut`, `asChild`) `/Body/Header` (`title`, `menuLabel`, `actions`) `/Search` (`label`, `onOpen`, `shortcut` ⌘K — opens the app's `CommandDialog`) `/Main`; `collapseShortcut` (⌘B), `useShortcuts` — see [Layouts and templates](#layouts-and-templates) | client |
|
|
206
208
|
| `@krizaka/ui/dashboard-layout` | `DashboardLayout` (`title`, `eyebrow`, `description`, `actions`, `toolbar`, `headingAs`), `Widget` (`title`, `description`, `action`, `span` 1·2·3·full, `level`) | server |
|
|
207
209
|
| `@krizaka/ui/settings-layout` | `SettingsLayout` (`title`, `description`, `nav`, `navLabel`, `linkAs`), `SettingsSection` (`title`, `description`, `footer`) | server |
|
|
208
210
|
| `@krizaka/ui/auth-layout` | `AuthLayout` (`brand`, `title`, `description`, `footer`, `legal`, `promise`, `aside`, `asideFooter`) | server |
|
|
@@ -250,7 +252,7 @@ applies it before the first paint (no flash), the provider reports it. A persist
|
|
|
250
252
|
|
|
251
253
|
## Page blocks
|
|
252
254
|
|
|
253
|
-
> Category **blocks** of krizaka.com
|
|
255
|
+
> Category **blocks** of ui.krizaka.com. The pieces every page of a Krizaka site is made of — one frame, one hero,
|
|
254
256
|
> sections on one rhythm, cards that lead or argue, decisions, code, calls to act — so every page reads the same.
|
|
255
257
|
|
|
256
258
|
```tsx
|
|
@@ -280,7 +282,7 @@ import { LinkCard } from "@krizaka/ui/link-card";
|
|
|
280
282
|
|
|
281
283
|
## Page templates
|
|
282
284
|
|
|
283
|
-
> Category **templates** of krizaka.com
|
|
285
|
+
> Category **templates** of ui.krizaka.com: every page of a Krizaka site has its template.
|
|
284
286
|
|
|
285
287
|
Fifteen templates, each a **distinct composition** of the blocks — not the same card grid everywhere: `LandingPage`
|
|
286
288
|
(split hero with the product alive, a band of proofs, a bento), `ProductPage` (split hero in the product's brand,
|
|
@@ -300,7 +302,7 @@ boundary stays where it is: a template is a Server Component.
|
|
|
300
302
|
|
|
301
303
|
## Layouts and screens
|
|
302
304
|
|
|
303
|
-
> Category **layouts** of krizaka.com
|
|
305
|
+
> Category **layouts** of ui.krizaka.com, and **templates**: whole screens composed from the platform.
|
|
304
306
|
|
|
305
307
|
The shells of the products' screens, read from their real screens (Orochia web, admin and mobile; Orazaka web-client,
|
|
306
308
|
web-admin and mobile; krizaka.com): `AppShell` (sidebar that collapses, header, drawer on a phone), `DashboardLayout`
|
|
@@ -315,7 +317,7 @@ React Native shells are `@krizaka/ui/native`'s (DS4): same names where the conce
|
|
|
315
317
|
|
|
316
318
|
## Registry — the code drives the documentation
|
|
317
319
|
|
|
318
|
-
Every component is also published as data: **[krizaka.com
|
|
320
|
+
Every component is also published as data: **[ui.krizaka.com](https://ui.krizaka.com)** is generated
|
|
319
321
|
from it at every build of the site (one page per component: when to use it, live examples with their code for the web
|
|
320
322
|
and React Native, props, accessibility), and, later, a `npx @krizaka/cli add <name>` may copy a primitive into a
|
|
321
323
|
product that forks it knowingly. Built by `scripts/build-registry.mjs` after tsup, from three sources in the code:
|
|
@@ -497,7 +499,7 @@ classes the components use are generated in your stylesheet.
|
|
|
497
499
|
```
|
|
498
500
|
|
|
499
501
|
Every component, with when to use it, live examples and their code (web and React Native) and its props:
|
|
500
|
-
[krizaka.com
|
|
502
|
+
[ui.krizaka.com](https://ui.krizaka.com).
|
|
501
503
|
|
|
502
504
|
## Develop
|
|
503
505
|
|
package/dist/action-bar.js
CHANGED
package/dist/action-bar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/action-bar/action-bar.tsx"],"sourcesContent":["// Server-safe. A row of actions that leave or move on (blocks.css): discuss · propose · report, read the docs, see the\n// code. `buttons`: buttons with their words — the end of a page or a section. `icons`: icon buttons whose words are the\n// accessible name and a tooltip on hover and focus (CSS only: no script, no portal) — the foot of a card.\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { buttonVariants } from \"../button/button\";\nimport { cn } from \"../cn\";\nimport { ExternalGlyph } from \"../glyphs\";\n\nexport interface Action {\n /** Where it leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** Its words (the accessible name, and the tooltip of an icon action). */\n label: string;\n /** Its icon (a signature icon): decorative. Required by `variant=\"icons\"`. */\n icon?: React.ReactNode;\n}\n\nexport type ActionBarProps = React.HTMLAttributes<HTMLUListElement> & {\n /** The actions, in order of importance. */\n actions: readonly Action[];\n /**\n * `buttons` (with their words) or `icons` (icon buttons, the words in a tooltip).\n * @default \"buttons\"\n */\n variant?: \"buttons\" | \"icons\";\n /**\n * The first action as the primary button (the one action a block is about); the others stay secondary.\n * @default false\n */\n primaryFirst?: boolean;\n /**\n * The buttons' size: `sm` in a card, `md` in a section, `lg` on a hero or a closing call.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** What the actions are about (`Talk about Krizaka Java`): the list's accessible name. */\n label?: string;\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** A row of link actions: buttons with their words, or icon buttons with a tooltip. */\nexport function ActionBar({ actions, variant = \"buttons\", primaryFirst = false, size = \"md\", label, linkAs, className, ...props }: ActionBarProps) {\n const icons = variant === \"icons\";\n return (\n <ul className={cn(\"kz-actions\", className)} data-variant={icons ? \"icons\" : undefined} aria-label={label} {...props}>\n {actions.map((a, i) => {\n const external = isExternal(a.href);\n return (\n <li key={a.href}>\n {icons ? (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n aria-label={a.label}\n data-tip={a.label}\n className={buttonVariants({ variant: \"ghost\", size: \"icon\", shape: \"cut\", className: size === \"sm\" ? \"h-9 w-9\" : undefined })}\n >\n {a.icon}\n </BlockLink>\n ) : (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n className={buttonVariants({ variant: primaryFirst && i === 0 ? \"primary\" : \"secondary\", size, shape: \"cut\", className: \"gap-2\" })}\n >\n {a.icon}\n {a.label}\n {external && <ExternalGlyph className=\"h-3 w-3 opacity-70\" />}\n </BlockLink>\n )}\n </li>\n );\n })}\n </ul>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/action-bar/action-bar.tsx"],"sourcesContent":["// Server-safe. A row of actions that leave or move on (blocks.css): discuss · propose · report, read the docs, see the\n// code. `buttons`: buttons with their words — the end of a page or a section. `icons`: icon buttons whose words are the\n// accessible name and a tooltip on hover and focus (CSS only: no script, no portal) — the foot of a card.\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { buttonVariants } from \"../button/button\";\nimport { cn } from \"../cn\";\nimport { ExternalGlyph } from \"../glyphs\";\n\nexport interface Action {\n /** Where it leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** Its words (the accessible name, and the tooltip of an icon action). */\n label: string;\n /** Its icon (a signature icon): decorative. Required by `variant=\"icons\"`. */\n icon?: React.ReactNode;\n}\n\nexport type ActionBarProps = React.HTMLAttributes<HTMLUListElement> & {\n /** The actions, in order of importance. */\n actions: readonly Action[];\n /**\n * `buttons` (with their words) or `icons` (icon buttons, the words in a tooltip).\n * @default \"buttons\"\n */\n variant?: \"buttons\" | \"icons\";\n /**\n * The first action as the primary button (the one action a block is about); the others stay secondary.\n * @default false\n */\n primaryFirst?: boolean;\n /**\n * The buttons' size: `sm` in a card, `md` in a section, `lg` on a hero or a closing call.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** What the actions are about (`Talk about Krizaka Java`): the list's accessible name. */\n label?: string;\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** A row of link actions: buttons with their words, or icon buttons with a tooltip. */\nexport function ActionBar({ actions, variant = \"buttons\", primaryFirst = false, size = \"md\", label, linkAs, className, ...props }: ActionBarProps) {\n const icons = variant === \"icons\";\n return (\n <ul className={cn(\"kz-actions\", className)} data-variant={icons ? \"icons\" : undefined} aria-label={label} {...props}>\n {actions.map((a, i) => {\n const external = isExternal(a.href);\n return (\n <li key={a.href}>\n {icons ? (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n aria-label={a.label}\n data-tip={a.label}\n className={buttonVariants({ variant: \"ghost\", size: \"icon\", shape: \"cut\", className: size === \"sm\" ? \"h-9 w-9\" : undefined })}\n >\n {a.icon}\n </BlockLink>\n ) : (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n className={buttonVariants({ variant: primaryFirst && i === 0 ? \"primary\" : \"secondary\", size, shape: \"cut\", className: \"gap-2\" })}\n >\n {a.icon}\n {a.label}\n {external && <ExternalGlyph className=\"h-3 w-3 opacity-70\" />}\n </BlockLink>\n )}\n </li>\n );\n })}\n </ul>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqDc,cAWA,YAXA;AATP,SAAS,UAAU,EAAE,SAAS,UAAU,WAAW,eAAe,OAAO,OAAO,MAAM,OAAO,QAAQ,WAAW,GAAG,MAAM,GAAmB;AACjJ,QAAM,QAAQ,YAAY;AAC1B,SACE,oBAAC,QAAG,WAAW,GAAG,cAAc,SAAS,GAAG,gBAAc,QAAQ,UAAU,QAAW,cAAY,OAAQ,GAAG,OAC3G,kBAAQ,IAAI,CAAC,GAAG,MAAM;AACrB,UAAM,WAAW,WAAW,EAAE,IAAI;AAClC,WACE,oBAAC,QACE,kBACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,EAAE;AAAA,QACR;AAAA,QACA;AAAA,QACA,cAAY,EAAE;AAAA,QACd,YAAU,EAAE;AAAA,QACZ,WAAW,eAAe,EAAE,SAAS,SAAS,MAAM,QAAQ,OAAO,OAAO,WAAW,SAAS,OAAO,YAAY,OAAU,CAAC;AAAA,QAE3H,YAAE;AAAA;AAAA,IACL,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,EAAE;AAAA,QACR;AAAA,QACA;AAAA,QACA,WAAW,eAAe,EAAE,SAAS,gBAAgB,MAAM,IAAI,YAAY,aAAa,MAAM,OAAO,OAAO,WAAW,QAAQ,CAAC;AAAA,QAE/H;AAAA,YAAE;AAAA,UACF,EAAE;AAAA,UACF,YAAY,oBAAC,iBAAc,WAAU,sBAAqB;AAAA;AAAA;AAAA,IAC7D,KAtBK,EAAE,IAwBX;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
|
package/dist/app-shell.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
9
9
|
header?: tailwind_variants.ClassValue;
|
|
10
10
|
main?: tailwind_variants.ClassValue;
|
|
11
11
|
nav?: tailwind_variants.ClassValue;
|
|
12
|
+
search?: tailwind_variants.ClassValue;
|
|
12
13
|
title?: tailwind_variants.ClassValue;
|
|
13
14
|
group?: tailwind_variants.ClassValue;
|
|
14
15
|
root?: tailwind_variants.ClassValue;
|
|
@@ -16,6 +17,9 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
16
17
|
overlay?: tailwind_variants.ClassValue;
|
|
17
18
|
brand?: tailwind_variants.ClassValue;
|
|
18
19
|
itemLabel?: tailwind_variants.ClassValue;
|
|
20
|
+
itemHint?: tailwind_variants.ClassValue;
|
|
21
|
+
searchLabel?: tailwind_variants.ClassValue;
|
|
22
|
+
searchKeys?: tailwind_variants.ClassValue;
|
|
19
23
|
actions?: tailwind_variants.ClassValue;
|
|
20
24
|
sidebar?: tailwind_variants.ClassValue;
|
|
21
25
|
item?: tailwind_variants.ClassValue;
|
|
@@ -32,6 +36,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
32
36
|
header?: tailwind_variants.ClassValue;
|
|
33
37
|
main?: tailwind_variants.ClassValue;
|
|
34
38
|
nav?: tailwind_variants.ClassValue;
|
|
39
|
+
search?: tailwind_variants.ClassValue;
|
|
35
40
|
title?: tailwind_variants.ClassValue;
|
|
36
41
|
group?: tailwind_variants.ClassValue;
|
|
37
42
|
root?: tailwind_variants.ClassValue;
|
|
@@ -39,6 +44,9 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
39
44
|
overlay?: tailwind_variants.ClassValue;
|
|
40
45
|
brand?: tailwind_variants.ClassValue;
|
|
41
46
|
itemLabel?: tailwind_variants.ClassValue;
|
|
47
|
+
itemHint?: tailwind_variants.ClassValue;
|
|
48
|
+
searchLabel?: tailwind_variants.ClassValue;
|
|
49
|
+
searchKeys?: tailwind_variants.ClassValue;
|
|
42
50
|
actions?: tailwind_variants.ClassValue;
|
|
43
51
|
sidebar?: tailwind_variants.ClassValue;
|
|
44
52
|
item?: tailwind_variants.ClassValue;
|
|
@@ -57,6 +65,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
57
65
|
group: string;
|
|
58
66
|
item: string;
|
|
59
67
|
itemLabel: string;
|
|
68
|
+
itemHint: string;
|
|
60
69
|
itemBadge: string;
|
|
61
70
|
footer: string;
|
|
62
71
|
collapse: string;
|
|
@@ -64,6 +73,9 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
64
73
|
header: string;
|
|
65
74
|
menuButton: string;
|
|
66
75
|
title: string;
|
|
76
|
+
search: string;
|
|
77
|
+
searchLabel: string;
|
|
78
|
+
searchKeys: string;
|
|
67
79
|
actions: string;
|
|
68
80
|
main: string;
|
|
69
81
|
}, undefined, {
|
|
@@ -74,6 +86,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
74
86
|
header?: tailwind_variants.ClassValue;
|
|
75
87
|
main?: tailwind_variants.ClassValue;
|
|
76
88
|
nav?: tailwind_variants.ClassValue;
|
|
89
|
+
search?: tailwind_variants.ClassValue;
|
|
77
90
|
title?: tailwind_variants.ClassValue;
|
|
78
91
|
group?: tailwind_variants.ClassValue;
|
|
79
92
|
root?: tailwind_variants.ClassValue;
|
|
@@ -81,6 +94,9 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
81
94
|
overlay?: tailwind_variants.ClassValue;
|
|
82
95
|
brand?: tailwind_variants.ClassValue;
|
|
83
96
|
itemLabel?: tailwind_variants.ClassValue;
|
|
97
|
+
itemHint?: tailwind_variants.ClassValue;
|
|
98
|
+
searchLabel?: tailwind_variants.ClassValue;
|
|
99
|
+
searchKeys?: tailwind_variants.ClassValue;
|
|
84
100
|
actions?: tailwind_variants.ClassValue;
|
|
85
101
|
sidebar?: tailwind_variants.ClassValue;
|
|
86
102
|
item?: tailwind_variants.ClassValue;
|
|
@@ -99,6 +115,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
99
115
|
group: string;
|
|
100
116
|
item: string;
|
|
101
117
|
itemLabel: string;
|
|
118
|
+
itemHint: string;
|
|
102
119
|
itemBadge: string;
|
|
103
120
|
footer: string;
|
|
104
121
|
collapse: string;
|
|
@@ -106,6 +123,9 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
106
123
|
header: string;
|
|
107
124
|
menuButton: string;
|
|
108
125
|
title: string;
|
|
126
|
+
search: string;
|
|
127
|
+
searchLabel: string;
|
|
128
|
+
searchKeys: string;
|
|
109
129
|
actions: string;
|
|
110
130
|
main: string;
|
|
111
131
|
}, tailwind_variants.TVReturnTypeLike<unknown, {
|
|
@@ -118,6 +138,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
118
138
|
group: string;
|
|
119
139
|
item: string;
|
|
120
140
|
itemLabel: string;
|
|
141
|
+
itemHint: string;
|
|
121
142
|
itemBadge: string;
|
|
122
143
|
footer: string;
|
|
123
144
|
collapse: string;
|
|
@@ -125,6 +146,9 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
125
146
|
header: string;
|
|
126
147
|
menuButton: string;
|
|
127
148
|
title: string;
|
|
149
|
+
search: string;
|
|
150
|
+
searchLabel: string;
|
|
151
|
+
searchKeys: string;
|
|
128
152
|
actions: string;
|
|
129
153
|
main: string;
|
|
130
154
|
}>>;
|
|
@@ -137,9 +161,15 @@ type AppShellRootProps = React.ComponentProps<"div"> & {
|
|
|
137
161
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
138
162
|
/** The words of the "skip to content" link (`Skip to content`), the first tab stop; it lands on `AppShell.Main`. Without it, no link. */
|
|
139
163
|
skipLabel?: string;
|
|
164
|
+
/**
|
|
165
|
+
* The shortcut that folds and unfolds the sidebar (a combination: `mod+b` is ⌘B on a Mac, Ctrl+B elsewhere), or `false`
|
|
166
|
+
* for none.
|
|
167
|
+
* @default "mod+b"
|
|
168
|
+
*/
|
|
169
|
+
collapseShortcut?: string | false;
|
|
140
170
|
};
|
|
141
171
|
/** The shell: holds the collapse (desktop) and the drawer (phone) state. Children: Sidebar, then Body. */
|
|
142
|
-
declare function AppShellRoot({ defaultCollapsed, collapsed: controlled, onCollapsedChange, skipLabel, className, children, ...props }: AppShellRootProps): React.JSX.Element;
|
|
172
|
+
declare function AppShellRoot({ defaultCollapsed, collapsed: controlled, onCollapsedChange, skipLabel, collapseShortcut, className, children, ...props }: AppShellRootProps): React.JSX.Element;
|
|
143
173
|
type AppShellSidebarProps = Omit<React.ComponentProps<"nav">, "children"> & {
|
|
144
174
|
/** The navigation's accessible name (`Studio`, `Administration`). */
|
|
145
175
|
label: string;
|
|
@@ -171,13 +201,18 @@ type AppShellItemProps = Omit<React.ComponentProps<"a">, "children"> & {
|
|
|
171
201
|
current?: boolean;
|
|
172
202
|
/** A count beside it (unread, pending): a number or a few characters. */
|
|
173
203
|
badge?: React.ReactNode;
|
|
204
|
+
/**
|
|
205
|
+
* A keyboard shortcut that goes to it: a sequence (`g d`: G then D) or a combination (`mod+1`). It clicks the item, so a
|
|
206
|
+
* router link navigates. The keys are shown on hover and focus when the sidebar is open.
|
|
207
|
+
*/
|
|
208
|
+
shortcut?: string;
|
|
174
209
|
/** Renders your link (Next's `Link`) with the item's look: pass it as the only child. */
|
|
175
210
|
asChild?: boolean;
|
|
176
211
|
/** Your link element when `asChild`. */
|
|
177
212
|
children?: React.ReactElement;
|
|
178
213
|
};
|
|
179
214
|
/** A destination of the sidebar: icon, words, a count. */
|
|
180
|
-
declare function AppShellItem({ icon, label, current, badge, asChild, children, className, ...props }: AppShellItemProps): React.JSX.Element;
|
|
215
|
+
declare function AppShellItem({ icon, label, current, badge, shortcut, asChild, children, className, ...props }: AppShellItemProps): React.JSX.Element;
|
|
181
216
|
/** The column right of the sidebar: Header, then Main. */
|
|
182
217
|
declare function AppShellBody({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
183
218
|
type AppShellHeaderProps = Omit<React.ComponentProps<"header">, "title"> & {
|
|
@@ -190,6 +225,19 @@ type AppShellHeaderProps = Omit<React.ComponentProps<"header">, "title"> & {
|
|
|
190
225
|
};
|
|
191
226
|
/** The top of the screen: the drawer's button (phone), the title, the actions. Sticky. */
|
|
192
227
|
declare function AppShellHeader({ title, menuLabel, actions, className, children, ...props }: AppShellHeaderProps): React.JSX.Element;
|
|
228
|
+
type AppShellSearchProps = Omit<React.ComponentProps<"button">, "children" | "onClick"> & {
|
|
229
|
+
/** The words ("Search", "Search creators"): the accessible name, shown beside the keys on a desktop. */
|
|
230
|
+
label: string;
|
|
231
|
+
/** Called when the button is pressed or the shortcut is typed: open your command palette (`CommandDialog`) here. */
|
|
232
|
+
onOpen: () => void;
|
|
233
|
+
/**
|
|
234
|
+
* The shortcut that opens it (a combination); shown on its keys.
|
|
235
|
+
* @default "mod+k"
|
|
236
|
+
*/
|
|
237
|
+
shortcut?: string;
|
|
238
|
+
};
|
|
239
|
+
/** The header's search: a button that looks like a field (a magnifier, its words, ⌘K) and opens the app's command palette. */
|
|
240
|
+
declare function AppShellSearch({ label, onOpen, shortcut, className, ...props }: AppShellSearchProps): React.JSX.Element;
|
|
193
241
|
/** The screen's content. */
|
|
194
242
|
declare function AppShellMain({ className, ...props }: React.ComponentProps<"main">): React.JSX.Element;
|
|
195
243
|
declare const AppShell: {
|
|
@@ -199,7 +247,37 @@ declare const AppShell: {
|
|
|
199
247
|
Item: typeof AppShellItem;
|
|
200
248
|
Body: typeof AppShellBody;
|
|
201
249
|
Header: typeof AppShellHeader;
|
|
250
|
+
Search: typeof AppShellSearch;
|
|
202
251
|
Main: typeof AppShellMain;
|
|
203
252
|
};
|
|
204
253
|
|
|
205
|
-
|
|
254
|
+
type ShortcutMap = Readonly<Record<string, ((event: KeyboardEvent) => void) | undefined>>;
|
|
255
|
+
type Combo = {
|
|
256
|
+
key: string;
|
|
257
|
+
mod: boolean;
|
|
258
|
+
ctrl: boolean;
|
|
259
|
+
alt: boolean;
|
|
260
|
+
shift: boolean;
|
|
261
|
+
meta: boolean;
|
|
262
|
+
};
|
|
263
|
+
/** `"mod+shift+k"` → its parts. A lone `+` key is written `plus`. */
|
|
264
|
+
declare function parseCombo(combo: string): Combo;
|
|
265
|
+
/** Whether a key event is this combination: its modifiers exactly (`mod` is ⌘ or Ctrl by platform), then its key. */
|
|
266
|
+
declare function matchesCombo(event: Pick<KeyboardEvent, "key" | "metaKey" | "ctrlKey" | "altKey" | "shiftKey">, combo: string, apple?: boolean): boolean;
|
|
267
|
+
/** The keys of a shortcut to show on a keycap: `["⌘", "K"]` on a Mac, `["Ctrl", "K"]` elsewhere; a sequence `g d` is `["G", "D"]`. */
|
|
268
|
+
declare function formatShortcut(shortcut: string, apple?: boolean): string[];
|
|
269
|
+
/** Whether the person is on an Apple device — `false` on the server and in the first client render (no mismatch), then right. */
|
|
270
|
+
declare function useApple(): boolean;
|
|
271
|
+
/** The `aria-keyshortcuts` of a combination (`Control+K`, `Meta+K` on a Mac); a sequence has none (it is not a chord). */
|
|
272
|
+
declare function ariaKeyShortcuts(shortcut: string, apple?: boolean): string | undefined;
|
|
273
|
+
/**
|
|
274
|
+
* Binds shortcuts for as long as the component lives (`enabled` turns them off). Each key is a combination or a sequence,
|
|
275
|
+
* each value runs when it is pressed; the handler may change on every render. A shortcut that fires is `preventDefault`ed.
|
|
276
|
+
* A sequence waits `timeout` ms for its next key.
|
|
277
|
+
*/
|
|
278
|
+
declare function useShortcuts(map: ShortcutMap, { enabled, timeout }?: {
|
|
279
|
+
enabled?: boolean;
|
|
280
|
+
timeout?: number;
|
|
281
|
+
}): void;
|
|
282
|
+
|
|
283
|
+
export { AppShell, AppShellBody, AppShellGroup, type AppShellGroupProps, AppShellHeader, type AppShellHeaderProps, AppShellItem, type AppShellItemProps, AppShellMain, AppShellRoot, type AppShellRootProps, AppShellSearch, type AppShellSearchProps, AppShellSidebar, type AppShellSidebarProps, type ShortcutMap, appShell, ariaKeyShortcuts, formatShortcut, matchesCombo, parseCombo, useApple, useShortcuts };
|
package/dist/app-shell.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
ChevronGlyph,
|
|
4
|
-
CloseGlyph
|
|
5
|
-
|
|
4
|
+
CloseGlyph,
|
|
5
|
+
SearchGlyph
|
|
6
|
+
} from "./chunk-AFY35UCB.js";
|
|
6
7
|
import {
|
|
7
8
|
cn
|
|
8
9
|
} from "./chunk-G3ETON2A.js";
|
|
@@ -12,10 +13,111 @@ import {
|
|
|
12
13
|
|
|
13
14
|
// src/app-shell/app-shell.tsx
|
|
14
15
|
import { Dialog as DialogPrimitive, Slot } from "radix-ui";
|
|
15
|
-
import * as
|
|
16
|
+
import * as React3 from "react";
|
|
17
|
+
import { tv as tv2 } from "tailwind-variants";
|
|
18
|
+
|
|
19
|
+
// src/kbd/kbd.tsx
|
|
16
20
|
import { tv } from "tailwind-variants";
|
|
17
|
-
import {
|
|
18
|
-
var
|
|
21
|
+
import { jsx } from "react/jsx-runtime";
|
|
22
|
+
var kbdVariants = tv({
|
|
23
|
+
base: "inline-flex shrink-0 items-center justify-center rounded-md border border-border-default border-b-2 bg-surface-2 font-mono font-semibold text-fg-secondary",
|
|
24
|
+
variants: { size: { sm: "h-5 min-w-5 px-1 text-[10px]", md: "h-6 min-w-6 px-1.5 text-xs" } },
|
|
25
|
+
defaultVariants: { size: "md" }
|
|
26
|
+
});
|
|
27
|
+
function Kbd({ size, className, ...props }) {
|
|
28
|
+
return /* @__PURE__ */ jsx("kbd", { className: kbdVariants({ size, className }), ...props });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/app-shell/shortcuts.ts
|
|
32
|
+
import * as React2 from "react";
|
|
33
|
+
|
|
34
|
+
// src/media.ts
|
|
35
|
+
import * as React from "react";
|
|
36
|
+
function useLatest(value) {
|
|
37
|
+
const ref = React.useRef(value);
|
|
38
|
+
React.useLayoutEffect(() => {
|
|
39
|
+
ref.current = value;
|
|
40
|
+
});
|
|
41
|
+
return ref;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/app-shell/shortcuts.ts
|
|
45
|
+
var isApple = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
|
46
|
+
var ALIASES = { esc: "escape", return: "enter", space: " ", spacebar: " ", up: "arrowup", down: "arrowdown", left: "arrowleft", right: "arrowright", del: "delete", plus: "+" };
|
|
47
|
+
function parseCombo(combo) {
|
|
48
|
+
const parts = combo.toLowerCase().split("+").filter(Boolean);
|
|
49
|
+
const key = ALIASES[parts.at(-1) ?? ""] ?? parts.at(-1) ?? "";
|
|
50
|
+
const has = (name) => parts.slice(0, -1).includes(name);
|
|
51
|
+
return { key, mod: has("mod"), ctrl: has("ctrl"), alt: has("alt"), shift: has("shift"), meta: has("meta") };
|
|
52
|
+
}
|
|
53
|
+
function matchesCombo(event, combo, apple = isApple()) {
|
|
54
|
+
const want = parseCombo(combo);
|
|
55
|
+
const meta = want.meta || want.mod && apple;
|
|
56
|
+
const ctrl = want.ctrl || want.mod && !apple;
|
|
57
|
+
if (event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== want.alt) return false;
|
|
58
|
+
if (want.shift && !event.shiftKey) return false;
|
|
59
|
+
return event.key.toLowerCase() === want.key;
|
|
60
|
+
}
|
|
61
|
+
function formatShortcut(shortcut, apple = isApple()) {
|
|
62
|
+
if (shortcut.includes(" ")) return shortcut.split(/\s+/).map((key2) => key2.toUpperCase());
|
|
63
|
+
const parts = shortcut.split("+").filter(Boolean);
|
|
64
|
+
const key = parts.at(-1) ?? "";
|
|
65
|
+
const names = { mod: ["\u2318", "Ctrl"], meta: ["\u2318", "Win"], ctrl: ["\u2303", "Ctrl"], alt: ["\u2325", "Alt"], shift: ["\u21E7", "Shift"] };
|
|
66
|
+
const shown = parts.slice(0, -1).map((part) => names[part.toLowerCase()]?.[apple ? 0 : 1] ?? part);
|
|
67
|
+
const glyph = { escape: "Esc", enter: "\u21B5", arrowup: "\u2191", arrowdown: "\u2193", arrowleft: "\u2190", arrowright: "\u2192", " ": "Space" };
|
|
68
|
+
const last = glyph[ALIASES[key.toLowerCase()] ?? key.toLowerCase()] ?? key.toUpperCase();
|
|
69
|
+
return [...shown, last];
|
|
70
|
+
}
|
|
71
|
+
var noop = () => () => void 0;
|
|
72
|
+
function useApple() {
|
|
73
|
+
return React2.useSyncExternalStore(noop, isApple, () => false);
|
|
74
|
+
}
|
|
75
|
+
function ariaKeyShortcuts(shortcut, apple = isApple()) {
|
|
76
|
+
if (shortcut.includes(" ")) return void 0;
|
|
77
|
+
const parts = shortcut.split("+").filter(Boolean);
|
|
78
|
+
const names = { mod: apple ? "Meta" : "Control", meta: "Meta", ctrl: "Control", alt: "Alt", shift: "Shift" };
|
|
79
|
+
const key = parts.at(-1) ?? "";
|
|
80
|
+
return [...parts.slice(0, -1).map((part) => names[part.toLowerCase()] ?? part), key.length === 1 ? key.toUpperCase() : key.charAt(0).toUpperCase() + key.slice(1)].join("+");
|
|
81
|
+
}
|
|
82
|
+
function isEditable(target) {
|
|
83
|
+
const element = target instanceof HTMLElement ? target : null;
|
|
84
|
+
if (!element) return { field: false, rich: false };
|
|
85
|
+
const rich = element.isContentEditable;
|
|
86
|
+
return { field: rich || /^(input|textarea|select)$/i.test(element.tagName), rich };
|
|
87
|
+
}
|
|
88
|
+
function useShortcuts(map, { enabled = true, timeout = 900 } = {}) {
|
|
89
|
+
const latest = useLatest(map);
|
|
90
|
+
const names = Object.keys(map).sort().join("\n");
|
|
91
|
+
React2.useEffect(() => {
|
|
92
|
+
if (!enabled) return;
|
|
93
|
+
const bindings = names.split("\n").filter(Boolean);
|
|
94
|
+
let buffer = [];
|
|
95
|
+
const onKeyDown = (event) => {
|
|
96
|
+
if (event.defaultPrevented || event.isComposing || event.repeat || !event.key) return;
|
|
97
|
+
const editing = isEditable(event.target);
|
|
98
|
+
const chord = event.metaKey || event.ctrlKey || event.altKey;
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
buffer = [...buffer.filter((entry) => now - entry.at <= timeout), { key: event.key.toLowerCase(), at: now }].slice(-4);
|
|
101
|
+
for (const binding of bindings) {
|
|
102
|
+
const handler = latest.current[binding];
|
|
103
|
+
if (!handler) continue;
|
|
104
|
+
const keys = binding.toLowerCase().split(/\s+/).map((key) => ALIASES[key] ?? key);
|
|
105
|
+
const fired = keys.length > 1 ? !chord && !editing.field && buffer.length >= keys.length && keys.every((key, i) => buffer[buffer.length - keys.length + i]?.key === key) : matchesCombo(event, binding) && (chord ? !editing.rich : !editing.field);
|
|
106
|
+
if (!fired) continue;
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
buffer = [];
|
|
109
|
+
handler(event);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
document.addEventListener("keydown", onKeyDown);
|
|
114
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
115
|
+
}, [enabled, names, latest, timeout]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/app-shell/app-shell.tsx
|
|
119
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
120
|
+
var appShell = tv2({
|
|
19
121
|
slots: {
|
|
20
122
|
root: "flex min-h-dvh w-full bg-surface-0 text-fg",
|
|
21
123
|
sidebar: "sticky top-0 hidden h-dvh shrink-0 flex-col border-r border-border-subtle bg-surface-1 transition-[width] duration-300 motion-reduce:transition-none md:flex w-64 data-collapsed:w-[4.25rem]",
|
|
@@ -26,6 +128,7 @@ var appShell = tv({
|
|
|
26
128
|
group: "mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold uppercase tracking-[0.14em] text-fg-secondary [[data-collapsed]_&]:sr-only",
|
|
27
129
|
item: "group/item relative flex h-10 items-center gap-3 rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline transition-colors hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg aria-[current=page]:before:absolute aria-[current=page]:before:inset-y-2 aria-[current=page]:before:-left-2 aria-[current=page]:before:w-1 aria-[current=page]:before:rounded-full aria-[current=page]:before:bg-accent [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0",
|
|
28
130
|
itemLabel: "min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only",
|
|
131
|
+
itemHint: "ml-auto hidden items-center gap-0.5 text-fg-secondary group-hover/item:flex group-focus-visible/item:flex [[data-collapsed]_&]:!hidden",
|
|
29
132
|
itemBadge: "ml-auto inline-flex min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-[11px] font-bold text-on-accent [[data-collapsed]_&]:absolute [[data-collapsed]_&]:right-1.5 [[data-collapsed]_&]:top-1 [[data-collapsed]_&]:min-w-4 [[data-collapsed]_&]:px-1 [[data-collapsed]_&]:text-[10px]",
|
|
30
133
|
footer: "shrink-0 border-t border-border-subtle p-2",
|
|
31
134
|
collapse: "hidden h-9 w-full items-center gap-2 rounded-lg px-3 text-xs font-medium text-fg-secondary hover:bg-surface-2 hover:text-fg md:flex focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0",
|
|
@@ -33,31 +136,35 @@ var appShell = tv({
|
|
|
33
136
|
header: "sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0/85 px-4 backdrop-blur-xl md:px-6",
|
|
34
137
|
menuButton: "inline-flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-surface-1 text-fg md:hidden focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
35
138
|
title: "min-w-0 flex-1 truncate font-display text-base font-bold tracking-tight",
|
|
139
|
+
search: "inline-flex h-9 min-w-9 items-center justify-center gap-2 rounded-lg border border-border-subtle bg-surface-1 px-2.5 text-sm text-fg-secondary transition-colors hover:border-border-default hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring md:w-56 md:justify-start [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0",
|
|
140
|
+
searchLabel: "sr-only md:not-sr-only md:flex-1 md:truncate md:text-left",
|
|
141
|
+
searchKeys: "hidden items-center gap-0.5 md:flex",
|
|
36
142
|
actions: "ml-auto flex shrink-0 items-center gap-2",
|
|
37
143
|
main: "min-w-0 flex-1 px-4 py-6 md:px-6 md:py-8"
|
|
38
144
|
}
|
|
39
145
|
});
|
|
40
146
|
var s = appShell();
|
|
41
|
-
var ShellContext =
|
|
147
|
+
var ShellContext = React3.createContext(null);
|
|
42
148
|
var useShell = () => {
|
|
43
|
-
const ctx =
|
|
149
|
+
const ctx = React3.useContext(ShellContext);
|
|
44
150
|
if (!ctx) throw new Error("AppShell parts must be inside <AppShell.Root>");
|
|
45
151
|
return ctx;
|
|
46
152
|
};
|
|
47
|
-
function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, skipLabel, className, children, ...props }) {
|
|
48
|
-
const [own, setOwn] =
|
|
49
|
-
const [open, setOpen] =
|
|
153
|
+
function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, skipLabel, collapseShortcut = "mod+b", className, children, ...props }) {
|
|
154
|
+
const [own, setOwn] = React3.useState(defaultCollapsed);
|
|
155
|
+
const [open, setOpen] = React3.useState(false);
|
|
50
156
|
const collapsed = controlled ?? own;
|
|
51
|
-
const setCollapsed =
|
|
157
|
+
const setCollapsed = React3.useCallback(
|
|
52
158
|
(value2) => {
|
|
53
159
|
if (controlled === void 0) setOwn(value2);
|
|
54
160
|
onCollapsedChange?.(value2);
|
|
55
161
|
},
|
|
56
162
|
[controlled, onCollapsedChange]
|
|
57
163
|
);
|
|
58
|
-
const value =
|
|
59
|
-
|
|
60
|
-
|
|
164
|
+
const value = React3.useMemo(() => ({ collapsed, setCollapsed, open, setOpen, inDrawer: false }), [collapsed, setCollapsed, open]);
|
|
165
|
+
useShortcuts(collapseShortcut ? { [collapseShortcut]: () => setCollapsed(!collapsed) } : {});
|
|
166
|
+
return /* @__PURE__ */ jsx2(ShellContext.Provider, { value, children: /* @__PURE__ */ jsxs("div", { className: s.root({ className }), ...props, children: [
|
|
167
|
+
skipLabel && /* @__PURE__ */ jsx2(SkipLink, { href: "#content", children: skipLabel }),
|
|
61
168
|
children
|
|
62
169
|
] }) });
|
|
63
170
|
}
|
|
@@ -66,14 +173,14 @@ function SidebarContent({ brand, children, footer, collapseLabels, closeLabel })
|
|
|
66
173
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
67
174
|
/* @__PURE__ */ jsxs("div", { className: s.brand(), children: [
|
|
68
175
|
brand,
|
|
69
|
-
inDrawer && /* @__PURE__ */
|
|
176
|
+
inDrawer && /* @__PURE__ */ jsx2(DialogPrimitive.Close, { className: cn(s.menuButton(), "ml-auto flex"), "aria-label": closeLabel, children: /* @__PURE__ */ jsx2(CloseGlyph, { className: "h-4 w-4" }) })
|
|
70
177
|
] }),
|
|
71
|
-
/* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ jsx2("div", { className: s.nav(), children }),
|
|
72
179
|
(footer || collapseLabels && !inDrawer) && /* @__PURE__ */ jsxs("div", { className: s.footer(), children: [
|
|
73
180
|
footer,
|
|
74
181
|
collapseLabels && !inDrawer && /* @__PURE__ */ jsxs("button", { type: "button", className: s.collapse(), "aria-expanded": !collapsed, onClick: () => setCollapsed(!collapsed), children: [
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
-
/* @__PURE__ */
|
|
182
|
+
/* @__PURE__ */ jsx2(ChevronGlyph, { direction: collapsed ? "right" : "left", className: "h-4 w-4" }),
|
|
183
|
+
/* @__PURE__ */ jsx2("span", { className: collapsed ? "sr-only" : void 0, children: collapsed ? collapseLabels[1] : collapseLabels[0] })
|
|
77
184
|
] })
|
|
78
185
|
] })
|
|
79
186
|
] });
|
|
@@ -82,43 +189,56 @@ function AppShellSidebar({ label, brand, children, footer, collapseLabels, close
|
|
|
82
189
|
const shell = useShell();
|
|
83
190
|
const content = { brand, children, footer, collapseLabels, closeLabel };
|
|
84
191
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
-
/* @__PURE__ */
|
|
86
|
-
/* @__PURE__ */
|
|
87
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ jsx2("nav", { "aria-label": label, "data-collapsed": shell.collapsed ? "" : void 0, className: s.sidebar({ className }), ...props, children: /* @__PURE__ */ jsx2(SidebarContent, { ...content }) }),
|
|
193
|
+
/* @__PURE__ */ jsx2(DialogPrimitive.Root, { open: shell.open, onOpenChange: shell.setOpen, children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [
|
|
194
|
+
/* @__PURE__ */ jsx2(DialogPrimitive.Overlay, { className: s.overlay() }),
|
|
88
195
|
/* @__PURE__ */ jsxs(DialogPrimitive.Content, { className: s.drawer(), "aria-describedby": void 0, children: [
|
|
89
|
-
/* @__PURE__ */
|
|
90
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ jsx2(DialogPrimitive.Title, { className: "sr-only", children: label }),
|
|
197
|
+
/* @__PURE__ */ jsx2(ShellContext.Provider, { value: { ...shell, collapsed: false, inDrawer: true }, children: /* @__PURE__ */ jsx2("nav", { "aria-label": label, className: "flex min-h-0 flex-1 flex-col", onClick: (e) => e.target.closest("a") && shell.setOpen(false), children: /* @__PURE__ */ jsx2(SidebarContent, { ...content }) }) })
|
|
91
198
|
] })
|
|
92
199
|
] }) })
|
|
93
200
|
] });
|
|
94
201
|
}
|
|
95
202
|
function AppShellGroup({ className, ...props }) {
|
|
96
|
-
return /* @__PURE__ */
|
|
203
|
+
return /* @__PURE__ */ jsx2("p", { className: s.group({ className }), ...props });
|
|
97
204
|
}
|
|
98
|
-
function AppShellItem({ icon, label, current, badge, asChild, children, className, ...props }) {
|
|
99
|
-
const { collapsed } = useShell();
|
|
205
|
+
function AppShellItem({ icon, label, current, badge, shortcut, asChild, children, className, ...props }) {
|
|
206
|
+
const { collapsed, inDrawer } = useShell();
|
|
207
|
+
const element = React3.useRef(null);
|
|
208
|
+
const apple = useApple();
|
|
209
|
+
useShortcuts(shortcut && !inDrawer ? { [shortcut]: () => element.current?.click() } : {});
|
|
100
210
|
const Comp = asChild ? Slot.Root : "a";
|
|
101
211
|
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
102
212
|
icon,
|
|
103
|
-
/* @__PURE__ */
|
|
104
|
-
badge != null && /* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ jsx2("span", { className: s.itemLabel(), children: label }),
|
|
214
|
+
badge != null && /* @__PURE__ */ jsx2("span", { className: s.itemBadge(), children: badge }),
|
|
215
|
+
shortcut && !inDrawer ? /* @__PURE__ */ jsx2("span", { className: s.itemHint(), "aria-hidden": true, children: formatShortcut(shortcut, apple).map((key) => /* @__PURE__ */ jsx2(Kbd, { size: "sm", children: key }, key)) }) : null
|
|
105
216
|
] });
|
|
106
|
-
return /* @__PURE__ */
|
|
217
|
+
return /* @__PURE__ */ jsx2(Comp, { ref: element, "aria-current": current ? "page" : void 0, "aria-keyshortcuts": shortcut ? ariaKeyShortcuts(shortcut, apple) : void 0, title: collapsed ? label : void 0, className: s.item({ className }), ...props, children: asChild && children ? React3.cloneElement(children, void 0, inner) : inner });
|
|
107
218
|
}
|
|
108
219
|
function AppShellBody({ className, ...props }) {
|
|
109
|
-
return /* @__PURE__ */
|
|
220
|
+
return /* @__PURE__ */ jsx2("div", { className: s.body({ className }), ...props });
|
|
110
221
|
}
|
|
111
222
|
function AppShellHeader({ title, menuLabel, actions, className, children, ...props }) {
|
|
112
223
|
const { open, setOpen } = useShell();
|
|
113
224
|
return /* @__PURE__ */ jsxs("header", { className: s.header({ className }), ...props, children: [
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
-
title && /* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: s.menuButton(), "aria-label": menuLabel, "aria-expanded": open, "aria-haspopup": "dialog", onClick: () => setOpen(true), children: /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "M4 7h16M4 12h16M4 17h10" }) }) }),
|
|
226
|
+
title && /* @__PURE__ */ jsx2("h1", { className: s.title(), children: title }),
|
|
116
227
|
children,
|
|
117
|
-
actions && /* @__PURE__ */
|
|
228
|
+
actions && /* @__PURE__ */ jsx2("div", { className: s.actions(), children: actions })
|
|
229
|
+
] });
|
|
230
|
+
}
|
|
231
|
+
function AppShellSearch({ label, onOpen, shortcut = "mod+k", className, ...props }) {
|
|
232
|
+
const apple = useApple();
|
|
233
|
+
useShortcuts({ [shortcut]: () => onOpen() });
|
|
234
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", className: s.search({ className }), "aria-keyshortcuts": ariaKeyShortcuts(shortcut, apple), onClick: onOpen, ...props, children: [
|
|
235
|
+
/* @__PURE__ */ jsx2(SearchGlyph, {}),
|
|
236
|
+
/* @__PURE__ */ jsx2("span", { className: s.searchLabel(), children: label }),
|
|
237
|
+
/* @__PURE__ */ jsx2("span", { className: s.searchKeys(), "aria-hidden": true, children: formatShortcut(shortcut, apple).map((key) => /* @__PURE__ */ jsx2(Kbd, { size: "sm", children: key }, key)) })
|
|
118
238
|
] });
|
|
119
239
|
}
|
|
120
240
|
function AppShellMain({ className, ...props }) {
|
|
121
|
-
return /* @__PURE__ */
|
|
241
|
+
return /* @__PURE__ */ jsx2("main", { id: "content", tabIndex: -1, className: s.main({ className }), ...props });
|
|
122
242
|
}
|
|
123
243
|
var AppShell = {
|
|
124
244
|
Root: AppShellRoot,
|
|
@@ -127,6 +247,7 @@ var AppShell = {
|
|
|
127
247
|
Item: AppShellItem,
|
|
128
248
|
Body: AppShellBody,
|
|
129
249
|
Header: AppShellHeader,
|
|
250
|
+
Search: AppShellSearch,
|
|
130
251
|
Main: AppShellMain
|
|
131
252
|
};
|
|
132
253
|
export {
|
|
@@ -137,7 +258,14 @@ export {
|
|
|
137
258
|
AppShellItem,
|
|
138
259
|
AppShellMain,
|
|
139
260
|
AppShellRoot,
|
|
261
|
+
AppShellSearch,
|
|
140
262
|
AppShellSidebar,
|
|
141
|
-
appShell
|
|
263
|
+
appShell,
|
|
264
|
+
ariaKeyShortcuts,
|
|
265
|
+
formatShortcut,
|
|
266
|
+
matchesCombo,
|
|
267
|
+
parseCombo,
|
|
268
|
+
useApple,
|
|
269
|
+
useShortcuts
|
|
142
270
|
};
|
|
143
271
|
//# sourceMappingURL=app-shell.js.map
|