@krizaka/ui 3.42.0 → 3.43.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/dist/RotatingWord-D9s7FXFD.d.ts +46 -0
- package/dist/app-shell.d.ts +9 -3
- package/dist/app-shell.js +6 -4
- package/dist/app-shell.js.map +1 -1
- package/dist/code-block.js +1 -1
- package/dist/code-block.js.map +1 -1
- package/dist/date-picker.js +1 -1
- package/dist/date-picker.js.map +1 -1
- package/dist/elephant.d.ts +57 -0
- package/dist/elephant.js +61 -0
- package/dist/elephant.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/map.js.map +1 -1
- package/dist/motion.css +44 -0
- package/dist/motion.d.ts +46 -39
- package/dist/motion.js +42 -1
- package/dist/motion.js.map +1 -1
- package/dist/native/map.js.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.d.ts +2 -0
- package/dist/nav-bar.js.map +1 -1
- package/dist/popover.js +1 -1
- package/dist/popover.js.map +1 -1
- package/dist/rail-nav.js +2 -2
- package/dist/rail-nav.js.map +1 -1
- package/dist/site-footer.d.ts +10 -5
- package/dist/site-footer.js +4 -3
- package/dist/site-footer.js.map +1 -1
- package/dist/tabs.js +1 -1
- package/dist/tabs.js.map +1 -1
- package/package.json +19 -5
- package/registry/account-menu.json +1 -1
- 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/anatomy-plate.json +1 -1
- package/registry/app-header.json +1 -1
- package/registry/app-shell.json +11 -3
- package/registry/argument-card.json +1 -1
- package/registry/audio-player.json +1 -1
- package/registry/auth-layout.json +1 -1
- package/registry/avatar.json +1 -1
- package/registry/badge.json +1 -1
- package/registry/bird-plate.json +1 -1
- package/registry/button.json +1 -1
- package/registry/captcha.json +1 -1
- package/registry/card-grid.json +1 -1
- package/registry/card.json +1 -1
- 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-row.json +1 -1
- package/registry/chip.json +1 -1
- package/registry/choice-grid.json +1 -1
- package/registry/code-block.json +1 -1
- package/registry/combobox.json +1 -1
- package/registry/command.json +1 -1
- package/registry/comparison-table.json +1 -1
- package/registry/component-page.json +1 -1
- 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/cover-hero.json +1 -1
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +1 -1
- package/registry/data-table.json +1 -1
- package/registry/date-picker.json +1 -1
- 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/docs-nav.json +1 -1
- package/registry/document-viewer.json +1 -1
- package/registry/dropdown-menu.json +1 -1
- package/registry/elephant.json +192 -0
- package/registry/empty-state.json +1 -1
- package/registry/engine-flow.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/app-shell/collapse-overflow.d.ts +4 -0
- package/registry/examples/app-shell/collapse-overflow.js +35 -0
- package/registry/examples/app-shell/collapse-overflow.tsx +28 -0
- package/registry/examples/elephant/closing.d.ts +4 -0
- package/registry/examples/elephant/closing.js +14 -0
- package/registry/examples/elephant/closing.tsx +11 -0
- package/registry/examples/elephant/poses.d.ts +4 -0
- package/registry/examples/elephant/poses.js +14 -0
- package/registry/examples/elephant/poses.tsx +11 -0
- package/registry/field.json +1 -1
- package/registry/file-upload.json +1 -1
- package/registry/filter-bar.json +1 -1
- package/registry/flock.json +1 -1
- package/registry/footnote.json +1 -1
- package/registry/force-update-screen.json +1 -1
- package/registry/form.json +1 -1
- package/registry/gallery-page.json +1 -1
- package/registry/genui.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 +1 -1
- package/registry/index.json +37 -6
- 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/language-switch.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +1 -1
- package/registry/loader.json +1 -1
- package/registry/longform-page.json +1 -1
- package/registry/maintenance-screen.json +1 -1
- package/registry/map.json +2 -2
- package/registry/markdown.json +1 -1
- 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 +1 -1
- package/registry/media-gallery.json +1 -1
- package/registry/media-grid.json +1 -1
- package/registry/meta-row.json +1 -1
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +5 -5
- package/registry/number-ticker.json +1 -1
- package/registry/opening-hours.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page-index.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 +2 -2
- 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/rail-nav.json +3 -3
- package/registry/recipes.json +1 -1
- package/registry/scroll-stage.json +1 -1
- package/registry/search-field.json +1 -1
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/service-card.json +1 -1
- package/registry/settings-layout.json +1 -1
- package/registry/showcase.json +1 -1
- package/registry/site-footer.json +21 -7
- package/registry/sites.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-picker.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/specimen-tag.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/step-showcase.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/sticky-action.json +1 -1
- package/registry/swipe-deck.json +1 -1
- package/registry/switch.json +1 -1
- package/registry/tabs.json +2 -2
- package/registry/tag-input.json +1 -1
- 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 +1 -1
- package/registry/update-prompt.json +1 -1
- package/registry/verified-badge.json +1 -1
- package/registry/vertical-video.json +1 -1
- package/registry/video-player.json +1 -1
- package/registry/video-trimmer.json +1 -1
- package/registry/virtual.json +1 -1
- package/registry/waveform.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface MotionObserverProps {
|
|
4
|
+
/**
|
|
5
|
+
* Hold the loops of the page (`.kz-loop`, the backdrop's drift) still until the page has loaded and settled
|
|
6
|
+
* (`html[data-motion="settling"]` → `"ready"`, about 1.2 s after `load`): nothing repaints the first screen while it
|
|
7
|
+
* paints.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
settle?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Pause every animation of a scope while it is off screen (`data-offscreen` on `.kz-backdrop`, `.kz-iso`,
|
|
13
|
+
* `[data-motion-scope]` and the page's sections).
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
pauseOffscreen?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The Krizaka motion conductor. Mount it once at the root of the app; it renders nothing and keeps no animation frame
|
|
20
|
+
* running. It reveals every `[data-reveal]` element once as it enters the viewport (including elements added later:
|
|
21
|
+
* client navigation needs nothing more), pauses what is off screen, lets the first screen settle before its loops
|
|
22
|
+
* start. Nothing follows the pointer (krizaka-ui#393: the spotlight and the magnetic buttons are retired). Under
|
|
23
|
+
* prefers-reduced-motion everything is revealed at once.
|
|
24
|
+
*/
|
|
25
|
+
declare function MotionObserver({ settle, pauseOffscreen }?: MotionObserverProps): null;
|
|
26
|
+
|
|
27
|
+
interface RotatingWordProps {
|
|
28
|
+
/** The alternatives, passed translated; the first one is what the server renders. */
|
|
29
|
+
words: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* Milliseconds each word stays.
|
|
32
|
+
* @default 2600
|
|
33
|
+
*/
|
|
34
|
+
interval?: number;
|
|
35
|
+
/** Classes for every word (a colour, gradient text). */
|
|
36
|
+
className?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A word that rolls through alternatives — the Krizaka headline signature. Every word shares one grid cell from the
|
|
40
|
+
* first paint, so the line keeps the width of the longest one (no layout shift); the current one rolls in. The server
|
|
41
|
+
* render shows the first word; it stays put under prefers-reduced-motion and while the tab is hidden. Decorative
|
|
42
|
+
* (`aria-hidden`): give the heading a stable accessible label.
|
|
43
|
+
*/
|
|
44
|
+
declare function RotatingWord({ words, interval, className }: RotatingWordProps): React.JSX.Element;
|
|
45
|
+
|
|
46
|
+
export { MotionObserver as M, RotatingWord as R, type MotionObserverProps as a, type RotatingWordProps as b };
|
package/dist/app-shell.d.ts
CHANGED
|
@@ -16,13 +16,14 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
16
16
|
titlebar?: tailwind_variants.ClassValue;
|
|
17
17
|
drawer?: tailwind_variants.ClassValue;
|
|
18
18
|
overlay?: tailwind_variants.ClassValue;
|
|
19
|
-
brand?: tailwind_variants.ClassValue;
|
|
20
19
|
itemLabel?: tailwind_variants.ClassValue;
|
|
21
20
|
itemHint?: tailwind_variants.ClassValue;
|
|
21
|
+
footerContent?: tailwind_variants.ClassValue;
|
|
22
22
|
searchLabel?: tailwind_variants.ClassValue;
|
|
23
23
|
searchKeys?: tailwind_variants.ClassValue;
|
|
24
24
|
actions?: tailwind_variants.ClassValue;
|
|
25
25
|
sidebar?: tailwind_variants.ClassValue;
|
|
26
|
+
brand?: tailwind_variants.ClassValue;
|
|
26
27
|
item?: tailwind_variants.ClassValue;
|
|
27
28
|
itemBadge?: tailwind_variants.ClassValue;
|
|
28
29
|
collapse?: tailwind_variants.ClassValue;
|
|
@@ -44,13 +45,14 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
44
45
|
titlebar?: tailwind_variants.ClassValue;
|
|
45
46
|
drawer?: tailwind_variants.ClassValue;
|
|
46
47
|
overlay?: tailwind_variants.ClassValue;
|
|
47
|
-
brand?: tailwind_variants.ClassValue;
|
|
48
48
|
itemLabel?: tailwind_variants.ClassValue;
|
|
49
49
|
itemHint?: tailwind_variants.ClassValue;
|
|
50
|
+
footerContent?: tailwind_variants.ClassValue;
|
|
50
51
|
searchLabel?: tailwind_variants.ClassValue;
|
|
51
52
|
searchKeys?: tailwind_variants.ClassValue;
|
|
52
53
|
actions?: tailwind_variants.ClassValue;
|
|
53
54
|
sidebar?: tailwind_variants.ClassValue;
|
|
55
|
+
brand?: tailwind_variants.ClassValue;
|
|
54
56
|
item?: tailwind_variants.ClassValue;
|
|
55
57
|
itemBadge?: tailwind_variants.ClassValue;
|
|
56
58
|
collapse?: tailwind_variants.ClassValue;
|
|
@@ -71,6 +73,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
71
73
|
itemHint: string;
|
|
72
74
|
itemBadge: string;
|
|
73
75
|
footer: string;
|
|
76
|
+
footerContent: string;
|
|
74
77
|
collapse: string;
|
|
75
78
|
body: string;
|
|
76
79
|
header: string;
|
|
@@ -96,13 +99,14 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
96
99
|
titlebar?: tailwind_variants.ClassValue;
|
|
97
100
|
drawer?: tailwind_variants.ClassValue;
|
|
98
101
|
overlay?: tailwind_variants.ClassValue;
|
|
99
|
-
brand?: tailwind_variants.ClassValue;
|
|
100
102
|
itemLabel?: tailwind_variants.ClassValue;
|
|
101
103
|
itemHint?: tailwind_variants.ClassValue;
|
|
104
|
+
footerContent?: tailwind_variants.ClassValue;
|
|
102
105
|
searchLabel?: tailwind_variants.ClassValue;
|
|
103
106
|
searchKeys?: tailwind_variants.ClassValue;
|
|
104
107
|
actions?: tailwind_variants.ClassValue;
|
|
105
108
|
sidebar?: tailwind_variants.ClassValue;
|
|
109
|
+
brand?: tailwind_variants.ClassValue;
|
|
106
110
|
item?: tailwind_variants.ClassValue;
|
|
107
111
|
itemBadge?: tailwind_variants.ClassValue;
|
|
108
112
|
collapse?: tailwind_variants.ClassValue;
|
|
@@ -123,6 +127,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
123
127
|
itemHint: string;
|
|
124
128
|
itemBadge: string;
|
|
125
129
|
footer: string;
|
|
130
|
+
footerContent: string;
|
|
126
131
|
collapse: string;
|
|
127
132
|
body: string;
|
|
128
133
|
header: string;
|
|
@@ -147,6 +152,7 @@ declare const appShell: tailwind_variants.TVReturnType<{
|
|
|
147
152
|
itemHint: string;
|
|
148
153
|
itemBadge: string;
|
|
149
154
|
footer: string;
|
|
155
|
+
footerContent: string;
|
|
150
156
|
collapse: string;
|
|
151
157
|
body: string;
|
|
152
158
|
header: string;
|
package/dist/app-shell.js
CHANGED
|
@@ -130,18 +130,20 @@ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
|
130
130
|
var appShell = tv2({
|
|
131
131
|
slots: {
|
|
132
132
|
root: "flex min-h-dvh w-full bg-surface-0 text-fg data-desktop:h-[var(--kz-window-height,100dvh)] data-desktop:overflow-hidden",
|
|
133
|
-
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] [[data-desktop]_&]:static [[data-desktop]_&]:flex [[data-desktop]_&]:h-auto [[data-desktop]_&]:w-60 [[data-desktop]_&]:select-none [[data-desktop]_&]:data-collapsed:w-[4.75rem]",
|
|
133
|
+
sidebar: "sticky top-0 hidden h-dvh shrink-0 flex-col overflow-x-clip 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] [[data-desktop]_&]:static [[data-desktop]_&]:flex [[data-desktop]_&]:h-auto [[data-desktop]_&]:w-60 [[data-desktop]_&]:select-none [[data-desktop]_&]:data-collapsed:w-[4.75rem]",
|
|
134
134
|
titlebar: "h-[var(--kz-titlebar-inset,1.75rem)] shrink-0",
|
|
135
135
|
drawer: "kz-dialog fixed inset-y-0 left-0 z-50 flex w-[min(20rem,88vw)] flex-col border-r border-border-default bg-surface-1 pl-[env(safe-area-inset-left)] shadow-lg focus:outline-hidden",
|
|
136
136
|
overlay: "kz-overlay fixed inset-0 z-50 bg-overlay",
|
|
137
|
-
brand: "flex h-14 shrink-0 items-center gap-2.5 px-4 font-display text-[15px] font-bold tracking-tight [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [[data-
|
|
137
|
+
brand: "flex h-14 shrink-0 items-center gap-2.5 overflow-hidden whitespace-nowrap px-4 font-display text-[15px] font-bold tracking-tight transition-[font-size,gap,padding] duration-300 motion-reduce:transition-none [[data-desktop]_&]:h-11 [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:gap-0 [[data-collapsed]_&]:px-0 [[data-collapsed]_&]:text-[0px] [[data-collapsed]_&_a]:gap-0 [[data-collapsed]_&_a]:text-[0px]",
|
|
138
138
|
nav: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2",
|
|
139
139
|
group: "mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold text-fg-secondary [[data-collapsed]_&]:sr-only",
|
|
140
140
|
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-sm aria-[current=page]:before:bg-accent [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0 [[data-desktop]_&]:h-8 [[data-desktop]_&]:cursor-default [[data-desktop]_&]:rounded-md [[data-desktop]_&]:text-[13px] [[data-desktop]_&_svg]:h-4 [[data-desktop]_&_svg]:w-4",
|
|
141
141
|
itemLabel: "min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only",
|
|
142
142
|
itemHint: "ml-auto hidden items-center gap-0.5 text-fg-secondary group-hover/item:flex group-focus-visible/item:flex [[data-collapsed]_&]:!hidden",
|
|
143
143
|
itemBadge: "ml-auto inline-flex min-w-5 items-center justify-center rounded-sm 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]",
|
|
144
|
-
footer: "shrink-0 border-t border-border-subtle p-2",
|
|
144
|
+
footer: "shrink-0 overflow-hidden border-t border-border-subtle p-2",
|
|
145
|
+
// The words the app puts in the footer (account, version): a text-only footer fades out first (a footer with controls stays, clipped) and the box narrows with the sidebar, so nothing spills over the content; they stay in the tree for screen readers.
|
|
146
|
+
footerContent: "max-w-full overflow-hidden whitespace-nowrap opacity-100 transition-[max-width,opacity] duration-300 motion-reduce:transition-none [[data-collapsed]_&:not(:has(a,button,input,[role=button]))]:max-w-0 [[data-collapsed]_&:not(:has(a,button,input,[role=button]))]:opacity-0 [[data-collapsed]_&:not(:has(a,button,input,[role=button]))]:duration-150",
|
|
145
147
|
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",
|
|
146
148
|
body: "flex min-w-0 flex-1 flex-col [[data-desktop]_&]:min-h-0 [[data-desktop]_&]:overflow-y-auto",
|
|
147
149
|
header: "sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0 px-4 md:px-6 [[data-desktop]_&]:h-12 [[data-desktop]_&]:select-none [[data-desktop]_&]:px-5",
|
|
@@ -190,7 +192,7 @@ function SidebarContent({ brand, children, footer, collapseLabels, closeLabel })
|
|
|
190
192
|
] }),
|
|
191
193
|
/* @__PURE__ */ jsx2("div", { className: s.nav(), children }),
|
|
192
194
|
(footer || collapseLabels && !inDrawer) && /* @__PURE__ */ jsxs("div", { className: s.footer(), children: [
|
|
193
|
-
footer,
|
|
195
|
+
footer && /* @__PURE__ */ jsx2("div", { className: s.footerContent(), children: footer }),
|
|
194
196
|
collapseLabels && !inDrawer && /* @__PURE__ */ jsxs("button", { type: "button", className: s.collapse(), "aria-expanded": !collapsed, onClick: () => setCollapsed(!collapsed), children: [
|
|
195
197
|
/* @__PURE__ */ jsx2(ChevronGlyph, { direction: collapsed ? "right" : "left", className: "h-4 w-4" }),
|
|
196
198
|
/* @__PURE__ */ jsx2("span", { className: collapsed ? "sr-only" : void 0, children: collapsed ? collapseLabels[1] : collapseLabels[0] })
|
package/dist/app-shell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/app-shell/app-shell.tsx","../src/kbd/kbd.tsx","../src/app-shell/shortcuts.ts","../src/media.ts"],"sourcesContent":["// Client (the collapse, the drawer, the shortcuts). The shell of an application screen, read from the products' consoles\n// (Orochia admin and studio, Orazaka web-admin and web-client): a sidebar of sections that collapses to its icons on a\n// desktop, a header with the screen's title, a search that opens the app's command palette and its actions, the content —\n// and below `md` the sidebar becomes a drawer opened from the header (Radix Dialog: focus trap, Escape, focus return).\n// Keyboard: ⌘/Ctrl+B folds the sidebar, ⌘/Ctrl+K opens the search, `g` then a letter goes to a section (`shortcut` of an\n// item). Words arrive as props; links through `asChild`.\n// `desktop` (ADR-002): the shell is the window of a desktop app (the Orazaka Mac app, Tauri 2): the sidebar is always\n// there (a window has a minimum width, no drawer), its top clears the traffic lights, the header and the sidebar's top are\n// the title bar (`data-tauri-drag-region`: dragging them moves the window), the density is the desktop's (32 px rows,\n// 13 px text, a 48 px header), the chrome is not selectable, and the content scrolls under a fixed header.\nimport { SkipLink } from \"@krizaka/ui/skip-link\";\nimport { Dialog as DialogPrimitive, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { ChevronGlyph, CloseGlyph, SearchGlyph } from \"../glyphs\";\nimport { Kbd } from \"../kbd\";\nimport { ariaKeyShortcuts, formatShortcut, useApple, useShortcuts } from \"./shortcuts\";\n\nexport const appShell = tv({\n slots: {\n root: \"flex min-h-dvh w-full bg-surface-0 text-fg data-desktop:h-[var(--kz-window-height,100dvh)] data-desktop:overflow-hidden\",\n sidebar:\n \"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 \" +\n \"w-64 data-collapsed:w-[4.25rem] \" +\n \"[[data-desktop]_&]:static [[data-desktop]_&]:flex [[data-desktop]_&]:h-auto [[data-desktop]_&]:w-60 [[data-desktop]_&]:select-none [[data-desktop]_&]:data-collapsed:w-[4.75rem]\",\n titlebar: \"h-[var(--kz-titlebar-inset,1.75rem)] shrink-0\",\n drawer:\n \"kz-dialog fixed inset-y-0 left-0 z-50 flex w-[min(20rem,88vw)] flex-col border-r border-border-default bg-surface-1 pl-[env(safe-area-inset-left)] shadow-lg focus:outline-hidden\",\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay\",\n brand: \"flex h-14 shrink-0 items-center gap-2.5 px-4 font-display text-[15px] font-bold tracking-tight [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [[data-desktop]_&]:h-11\",\n nav: \"flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2\",\n group: \"mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold text-fg-secondary [[data-collapsed]_&]:sr-only\",\n item:\n \"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 \" +\n \"hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg \" +\n \"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-sm aria-[current=page]:before:bg-accent \" +\n \"[[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0 \" +\n \"[[data-desktop]_&]:h-8 [[data-desktop]_&]:cursor-default [[data-desktop]_&]:rounded-md [[data-desktop]_&]:text-[13px] [[data-desktop]_&_svg]:h-4 [[data-desktop]_&_svg]:w-4\",\n itemLabel: \"min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only\",\n itemHint: \"ml-auto hidden items-center gap-0.5 text-fg-secondary group-hover/item:flex group-focus-visible/item:flex [[data-collapsed]_&]:!hidden\",\n itemBadge:\n \"ml-auto inline-flex min-w-5 items-center justify-center rounded-sm bg-accent px-1.5 text-[11px] font-bold text-on-accent \" +\n \"[[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]\",\n footer: \"shrink-0 border-t border-border-subtle p-2\",\n collapse:\n \"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 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0\",\n body: \"flex min-w-0 flex-1 flex-col [[data-desktop]_&]:min-h-0 [[data-desktop]_&]:overflow-y-auto\",\n header:\n \"sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0 px-4 md:px-6 \" +\n \"[[data-desktop]_&]:h-12 [[data-desktop]_&]:select-none [[data-desktop]_&]:px-5\",\n menuButton:\n \"inline-flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-surface-1 text-fg md:hidden [[data-desktop]_&]:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n title: \"min-w-0 flex-1 truncate font-display text-base font-bold tracking-tight [[data-desktop]_&]:text-[15px]\",\n search:\n \"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 \" +\n \"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\",\n searchLabel: \"sr-only md:not-sr-only md:flex-1 md:truncate md:text-left\",\n searchKeys: \"hidden items-center gap-0.5 md:flex\",\n actions: \"ml-auto flex shrink-0 items-center gap-2\",\n main: \"min-w-0 flex-1 px-4 py-6 md:px-6 md:py-8 [[data-desktop]_&]:px-6 [[data-desktop]_&]:py-6\",\n },\n});\nconst s = appShell();\n\ninterface ShellState {\n collapsed: boolean;\n setCollapsed: (value: boolean) => void;\n open: boolean;\n setOpen: (value: boolean) => void;\n inDrawer: boolean;\n desktop: boolean;\n}\nconst ShellContext = React.createContext<ShellState | null>(null);\nconst useShell = () => {\n const ctx = React.useContext(ShellContext);\n if (!ctx) throw new Error(\"AppShell parts must be inside <AppShell.Root>\");\n return ctx;\n};\n\nexport type AppShellRootProps = React.ComponentProps<\"div\"> & {\n /** The sidebar starts collapsed to its icons (desktop). */\n defaultCollapsed?: boolean;\n /** Controlled collapse (desktop): pair with `onCollapsedChange` to persist the choice. */\n collapsed?: boolean;\n /** Called when the sidebar collapses or expands. */\n onCollapsedChange?: (collapsed: boolean) => void;\n /** The words of the \"skip to content\" link (`Skip to content`), the first tab stop; it lands on `AppShell.Main`. Without it, no link. */\n skipLabel?: string;\n /**\n * The shortcut that folds and unfolds the sidebar (a combination: `mod+b` is ⌘B on a Mac, Ctrl+B elsewhere), or `false`\n * for none.\n * @default \"mod+b\"\n */\n collapseShortcut?: string | false;\n /**\n * The shell is a desktop app's window (the Orazaka Mac app, Tauri 2 — ADR-002): no drawer, the sidebar's top clears the\n * window's traffic lights (`--kz-titlebar-inset`, 28 px; the window's height is `--kz-window-height`, the viewport by default), the header and that top move the window\n * (`data-tauri-drag-region`), a denser layout, the chrome not selectable. In a browser it is a fixed-height shell.\n */\n desktop?: boolean;\n};\n\n/** The shell: holds the collapse (desktop) and the drawer (phone) state. Children: Sidebar, then Body. */\nexport function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, skipLabel, collapseShortcut = \"mod+b\", desktop = false, className, children, ...props }: AppShellRootProps) {\n const [own, setOwn] = React.useState(defaultCollapsed);\n const [open, setOpen] = React.useState(false);\n const collapsed = controlled ?? own;\n const setCollapsed = React.useCallback(\n (value: boolean) => {\n if (controlled === undefined) setOwn(value);\n onCollapsedChange?.(value);\n },\n [controlled, onCollapsedChange],\n );\n const value = React.useMemo(() => ({ collapsed, setCollapsed, open, setOpen, inDrawer: false, desktop }), [collapsed, setCollapsed, open, desktop]);\n useShortcuts(collapseShortcut ? { [collapseShortcut]: () => setCollapsed(!collapsed) } : {});\n return (\n <ShellContext.Provider value={value}>\n <div data-desktop={desktop ? \"\" : undefined} className={s.root({ className })} {...props}>\n {skipLabel && <SkipLink href=\"#content\">{skipLabel}</SkipLink>}\n {children}\n </div>\n </ShellContext.Provider>\n );\n}\n\nexport type AppShellSidebarProps = Omit<React.ComponentProps<\"nav\">, \"children\"> & {\n /** The navigation's accessible name (`Studio`, `Administration`). */\n label: string;\n /** The brand at the top: the product's mark and name (a link home through `asChild` of your router, or plain). */\n brand?: React.ReactNode;\n /** The sections: `AppShell.Group` and `AppShell.Item`. */\n children: React.ReactNode;\n /** At the bottom, above the collapse button: the account, a plan, the version. */\n footer?: React.ReactNode;\n /** The collapse button's words, `[collapse, expand]` (desktop only). Without it the sidebar cannot collapse. */\n collapseLabels?: readonly [string, string];\n /** The drawer's close button words (phone). */\n closeLabel: string;\n};\n\nfunction SidebarContent({ brand, children, footer, collapseLabels, closeLabel }: Pick<AppShellSidebarProps, \"brand\" | \"children\" | \"footer\" | \"collapseLabels\" | \"closeLabel\">) {\n const { collapsed, setCollapsed, inDrawer, desktop } = useShell();\n const drag = desktop ? { \"data-tauri-drag-region\": \"\" } : {};\n return (\n <>\n {desktop && <div aria-hidden className={s.titlebar()} {...drag} />}\n <div className={s.brand()} {...drag}>\n {brand}\n {inDrawer && (\n <DialogPrimitive.Close className={cn(s.menuButton(), \"ml-auto flex\")} aria-label={closeLabel}>\n <CloseGlyph className=\"h-4 w-4\" />\n </DialogPrimitive.Close>\n )}\n </div>\n <div className={s.nav()}>{children}</div>\n {(footer || (collapseLabels && !inDrawer)) && (\n <div className={s.footer()}>\n {footer}\n {collapseLabels && !inDrawer && (\n <button type=\"button\" className={s.collapse()} aria-expanded={!collapsed} onClick={() => setCollapsed(!collapsed)}>\n <ChevronGlyph direction={collapsed ? \"right\" : \"left\"} className=\"h-4 w-4\" />\n <span className={collapsed ? \"sr-only\" : undefined}>{collapsed ? collapseLabels[1] : collapseLabels[0]}</span>\n </button>\n )}\n </div>\n )}\n </>\n );\n}\n\n/** The sidebar: on a desktop a column that collapses to its icons; on a phone the same content in a drawer. */\nexport function AppShellSidebar({ label, brand, children, footer, collapseLabels, closeLabel, className, ...props }: AppShellSidebarProps) {\n const shell = useShell();\n const content = { brand, children, footer, collapseLabels, closeLabel };\n return (\n <>\n <nav aria-label={label} data-collapsed={shell.collapsed ? \"\" : undefined} className={s.sidebar({ className })} {...props}>\n <SidebarContent {...content} />\n </nav>\n {!shell.desktop && (\n <DialogPrimitive.Root open={shell.open} onOpenChange={shell.setOpen}>\n <DialogPrimitive.Portal>\n <DialogPrimitive.Overlay className={s.overlay()} />\n <DialogPrimitive.Content className={s.drawer()} aria-describedby={undefined}>\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <ShellContext.Provider value={{ ...shell, collapsed: false, inDrawer: true }}>\n <nav aria-label={label} className=\"flex min-h-0 flex-1 flex-col\" onClick={(e) => (e.target as Element).closest(\"a\") && shell.setOpen(false)}>\n <SidebarContent {...content} />\n </nav>\n </ShellContext.Provider>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n )}\n </>\n );\n}\n\nexport type AppShellGroupProps = React.ComponentProps<\"p\"> & {\n /** The group's name, above its items (hidden when the sidebar is collapsed). */\n children: React.ReactNode;\n};\n\n/** A heading between groups of items. */\nexport function AppShellGroup({ className, ...props }: AppShellGroupProps) {\n return <p className={s.group({ className })} {...props} />;\n}\n\nexport type AppShellItemProps = Omit<React.ComponentProps<\"a\">, \"children\"> & {\n /** The item's icon (a signature icon): it stays when the sidebar collapses. */\n icon?: React.ReactNode;\n /** Its words: the accessible name, hidden visually when collapsed (the icon carries a tooltip through `title`). */\n label: string;\n /** The screen it opens is the current one: `aria-current=\"page\"`. */\n current?: boolean;\n /** A count beside it (unread, pending): a number or a few characters. */\n badge?: React.ReactNode;\n /**\n * 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\n * router link navigates. The keys are shown on hover and focus when the sidebar is open.\n */\n shortcut?: string;\n /** Renders your link (Next's `Link`) with the item's look: pass it as the only child. */\n asChild?: boolean;\n /** Your link element when `asChild`. */\n children?: React.ReactElement;\n};\n\n/** A destination of the sidebar: icon, words, a count. */\nexport function AppShellItem({ icon, label, current, badge, shortcut, asChild, children, className, ...props }: AppShellItemProps) {\n const { collapsed, inDrawer } = useShell();\n const element = React.useRef<HTMLAnchorElement>(null);\n const apple = useApple();\n // Only the sidebar's own items listen: the drawer holds a copy of them, open or not.\n useShortcuts(shortcut && !inDrawer ? { [shortcut]: () => element.current?.click() } : {});\n const Comp = asChild ? Slot.Root : \"a\";\n const inner = (\n <>\n {icon}\n <span className={s.itemLabel()}>{label}</span>\n {badge != null && <span className={s.itemBadge()}>{badge}</span>}\n {shortcut && !inDrawer ? (\n <span className={s.itemHint()} aria-hidden>\n {formatShortcut(shortcut, apple).map((key) => (\n <Kbd key={key} size=\"sm\">\n {key}\n </Kbd>\n ))}\n </span>\n ) : null}\n </>\n );\n return (\n <Comp ref={element} aria-current={current ? \"page\" : undefined} aria-keyshortcuts={shortcut ? ariaKeyShortcuts(shortcut, apple) : undefined} title={collapsed ? label : undefined} className={s.item({ className })} {...props}>\n {asChild && children ? React.cloneElement(children, undefined, inner) : inner}\n </Comp>\n );\n}\n\n/** The column right of the sidebar: Header, then Main. */\nexport function AppShellBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport type AppShellHeaderProps = Omit<React.ComponentProps<\"header\">, \"title\"> & {\n /** The screen's title (an `h1`). */\n title?: React.ReactNode;\n /** The button that opens the drawer on a phone: its words. */\n menuLabel: string;\n /** The screen's actions, on the right. */\n actions?: React.ReactNode;\n};\n\n/** The top of the screen: the drawer's button (phone), the title, the actions. Sticky. */\nexport function AppShellHeader({ title, menuLabel, actions, className, children, ...props }: AppShellHeaderProps) {\n const { open, setOpen, desktop } = useShell();\n const drag = desktop ? { \"data-tauri-drag-region\": \"\" } : {};\n return (\n <header className={s.header({ className })} {...drag} {...props}>\n <button type=\"button\" className={s.menuButton()} aria-label={menuLabel} aria-expanded={open} aria-haspopup=\"dialog\" onClick={() => setOpen(true)}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M4 7h16M4 12h16M4 17h10\" />\n </svg>\n </button>\n {title && (\n <h1 className={s.title()} {...drag}>\n {title}\n </h1>\n )}\n {children}\n {actions && <div className={s.actions()}>{actions}</div>}\n </header>\n );\n}\n\nexport type AppShellSearchProps = Omit<React.ComponentProps<\"button\">, \"children\" | \"onClick\"> & {\n /** The words (\"Search\", \"Search creators\"): the accessible name, shown beside the keys on a desktop. */\n label: string;\n /** Called when the button is pressed or the shortcut is typed: open your command palette (`CommandDialog`) here. */\n onOpen: () => void;\n /**\n * The shortcut that opens it (a combination); shown on its keys.\n * @default \"mod+k\"\n */\n shortcut?: string;\n};\n\n/** The header's search: a button that looks like a field (a magnifier, its words, ⌘K) and opens the app's command palette. */\nexport function AppShellSearch({ label, onOpen, shortcut = \"mod+k\", className, ...props }: AppShellSearchProps) {\n const apple = useApple();\n useShortcuts({ [shortcut]: () => onOpen() });\n return (\n <button type=\"button\" className={s.search({ className })} aria-keyshortcuts={ariaKeyShortcuts(shortcut, apple)} onClick={onOpen} {...props}>\n <SearchGlyph />\n <span className={s.searchLabel()}>{label}</span>\n <span className={s.searchKeys()} aria-hidden>\n {formatShortcut(shortcut, apple).map((key) => (\n <Kbd key={key} size=\"sm\">\n {key}\n </Kbd>\n ))}\n </span>\n </button>\n );\n}\n\n/** The screen's content. */\nexport function AppShellMain({ className, ...props }: React.ComponentProps<\"main\">) {\n return <main id=\"content\" tabIndex={-1} className={s.main({ className })} {...props} />;\n}\n\nexport const AppShell = {\n Root: AppShellRoot,\n Sidebar: AppShellSidebar,\n Group: AppShellGroup,\n Item: AppShellItem,\n Body: AppShellBody,\n Header: AppShellHeader,\n Search: AppShellSearch,\n Main: AppShellMain,\n};\n","// Server-safe. A key, or a shortcut of several (`<Kbd>⌘</Kbd><Kbd>K</Kbd>`).\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const kbdVariants = tv({\n base:\n \"inline-flex shrink-0 items-center justify-center rounded-md border border-border-default border-b-2 bg-surface-2 \" +\n \"font-mono font-semibold text-fg-secondary\",\n variants: { size: { sm: \"h-5 min-w-5 px-1 text-[10px]\", md: \"h-6 min-w-6 px-1.5 text-xs\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type KbdProps = React.ComponentProps<\"kbd\"> & {\n /**\n * sm · md.\n * @default \"md\"\n */\n size?: VariantProps<typeof kbdVariants>[\"size\"];\n};\n\nexport function Kbd({ size, className, ...props }: KbdProps) {\n return <kbd className={kbdVariants({ size, className })} {...props} />;\n}\n","// Keyboard shortcuts for an application: a combination (`mod+k`, `shift+/`, `alt+arrowup`) or a sequence (`g d`: press G,\n// then D). `mod` is ⌘ on Apple devices and Ctrl elsewhere. Plain keys and sequences stay out of text fields; a combination\n// with `mod` works in an input (not in a rich-text editor, where ⌘B is bold). Nothing is decided here about the words.\nimport * as React from \"react\";\n\nimport { useLatest } from \"../media\";\n\nexport type ShortcutMap = Readonly<Record<string, ((event: KeyboardEvent) => void) | undefined>>;\n\nconst isApple = () => typeof navigator !== \"undefined\" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n\nconst ALIASES: Record<string, string> = { esc: \"escape\", return: \"enter\", space: \" \", spacebar: \" \", up: \"arrowup\", down: \"arrowdown\", left: \"arrowleft\", right: \"arrowright\", del: \"delete\", plus: \"+\" };\ntype Modifier = \"mod\" | \"ctrl\" | \"alt\" | \"shift\" | \"meta\";\n\ntype Combo = { key: string; mod: boolean; ctrl: boolean; alt: boolean; shift: boolean; meta: boolean };\n\n/** `\"mod+shift+k\"` → its parts. A lone `+` key is written `plus`. */\nexport function parseCombo(combo: string): Combo {\n const parts = combo.toLowerCase().split(\"+\").filter(Boolean);\n const key = ALIASES[parts.at(-1) ?? \"\"] ?? parts.at(-1) ?? \"\";\n const has = (name: Modifier) => parts.slice(0, -1).includes(name);\n return { key, mod: has(\"mod\"), ctrl: has(\"ctrl\"), alt: has(\"alt\"), shift: has(\"shift\"), meta: has(\"meta\") };\n}\n\n/** Whether a key event is this combination: its modifiers exactly (`mod` is ⌘ or Ctrl by platform), then its key. */\nexport function matchesCombo(event: Pick<KeyboardEvent, \"key\" | \"metaKey\" | \"ctrlKey\" | \"altKey\" | \"shiftKey\">, combo: string, apple = isApple()): boolean {\n const want = parseCombo(combo);\n const meta = want.meta || (want.mod && apple);\n const ctrl = want.ctrl || (want.mod && !apple);\n if (event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== want.alt) return false;\n // Shift is only checked when the binding names it: `?` and `/` need it on most layouts.\n if (want.shift && !event.shiftKey) return false;\n return event.key.toLowerCase() === want.key;\n}\n\n/** The keys of a shortcut to show on a keycap: `[\"⌘\", \"K\"]` on a Mac, `[\"Ctrl\", \"K\"]` elsewhere; a sequence `g d` is `[\"G\", \"D\"]`. */\nexport function formatShortcut(shortcut: string, apple = isApple()): string[] {\n if (shortcut.includes(\" \")) return shortcut.split(/\\s+/).map((key) => key.toUpperCase());\n const parts = shortcut.split(\"+\").filter(Boolean);\n const key = parts.at(-1) ?? \"\";\n const names: Record<string, [string, string]> = { mod: [\"⌘\", \"Ctrl\"], meta: [\"⌘\", \"Win\"], ctrl: [\"⌃\", \"Ctrl\"], alt: [\"⌥\", \"Alt\"], shift: [\"⇧\", \"Shift\"] };\n const shown = parts.slice(0, -1).map((part) => names[part.toLowerCase()]?.[apple ? 0 : 1] ?? part);\n const glyph: Record<string, string> = { escape: \"Esc\", enter: \"↵\", arrowup: \"↑\", arrowdown: \"↓\", arrowleft: \"←\", arrowright: \"→\", \" \": \"Space\" };\n const last = glyph[ALIASES[key.toLowerCase()] ?? key.toLowerCase()] ?? key.toUpperCase();\n return [...shown, last];\n}\n\n/**\n * The accelerator of a combination for a desktop app's native menu bar (Tauri's `MenuItem`, Electron's — ADR-002):\n * `mod+k` → `CmdOrCtrl+K`, `shift+mod+/` → `Shift+CmdOrCtrl+/`. The menu and the shell then answer the same keys, and the\n * menu shows them. A sequence (`g d`) has none: a menu item takes one chord.\n */\nexport function acceleratorOf(shortcut: string): string | undefined {\n if (shortcut.trim().includes(\" \")) return undefined;\n const parts = shortcut.split(\"+\").filter(Boolean);\n const names: Record<string, string> = { mod: \"CmdOrCtrl\", meta: \"Super\", ctrl: \"Ctrl\", alt: \"Alt\", shift: \"Shift\" };\n const raw = (parts.at(-1) ?? \"\").toLowerCase();\n const key = ALIASES[raw] ?? raw;\n const keys: Record<string, string> = { escape: \"Escape\", enter: \"Enter\", \" \": \"Space\", arrowup: \"Up\", arrowdown: \"Down\", arrowleft: \"Left\", arrowright: \"Right\", delete: \"Delete\", backspace: \"Backspace\", tab: \"Tab\", \"+\": \"Plus\" };\n const last = keys[key] ?? (key.length === 1 ? key.toUpperCase() : key.charAt(0).toUpperCase() + key.slice(1));\n return [...parts.slice(0, -1).map((part) => names[part.toLowerCase()] ?? part), last].join(\"+\");\n}\n\nconst noop = () => () => undefined;\n\n/** Whether the person is on an Apple device — `false` on the server and in the first client render (no mismatch), then right. */\nexport function useApple(): boolean {\n return React.useSyncExternalStore(noop, isApple, () => false);\n}\n\n/** The `aria-keyshortcuts` of a combination (`Control+K`, `Meta+K` on a Mac); a sequence has none (it is not a chord). */\nexport function ariaKeyShortcuts(shortcut: string, apple = isApple()): string | undefined {\n if (shortcut.includes(\" \")) return undefined;\n const parts = shortcut.split(\"+\").filter(Boolean);\n const names: Record<string, string> = { mod: apple ? \"Meta\" : \"Control\", meta: \"Meta\", ctrl: \"Control\", alt: \"Alt\", shift: \"Shift\" };\n const key = parts.at(-1) ?? \"\";\n return [...parts.slice(0, -1).map((part) => names[part.toLowerCase()] ?? part), key.length === 1 ? key.toUpperCase() : key.charAt(0).toUpperCase() + key.slice(1)].join(\"+\");\n}\n\n/** Whether a key press happens in something you type in. */\nfunction isEditable(target: EventTarget | null): { field: boolean; rich: boolean } {\n const element = target instanceof HTMLElement ? target : null;\n if (!element) return { field: false, rich: false };\n const rich = element.isContentEditable;\n return { field: rich || /^(input|textarea|select)$/i.test(element.tagName), rich };\n}\n\n/**\n * Binds shortcuts for as long as the component lives (`enabled` turns them off). Each key is a combination or a sequence,\n * each value runs when it is pressed; the handler may change on every render. A shortcut that fires is `preventDefault`ed.\n * A sequence waits `timeout` ms for its next key.\n */\nexport function useShortcuts(map: ShortcutMap, { enabled = true, timeout = 900 }: { enabled?: boolean; timeout?: number } = {}) {\n const latest = useLatest(map);\n const names = Object.keys(map).sort().join(\"\\n\");\n React.useEffect(() => {\n if (!enabled) return;\n const bindings = names.split(\"\\n\").filter(Boolean);\n let buffer: { key: string; at: number }[] = [];\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.isComposing || event.repeat || !event.key) return;\n const editing = isEditable(event.target);\n const chord = event.metaKey || event.ctrlKey || event.altKey;\n const now = Date.now();\n buffer = [...buffer.filter((entry) => now - entry.at <= timeout), { key: event.key.toLowerCase(), at: now }].slice(-4);\n for (const binding of bindings) {\n const handler = latest.current[binding];\n if (!handler) continue;\n const keys = binding.toLowerCase().split(/\\s+/).map((key) => ALIASES[key] ?? key);\n const fired =\n keys.length > 1\n ? !chord && !editing.field && buffer.length >= keys.length && keys.every((key, i) => buffer[buffer.length - keys.length + i]?.key === key)\n : matchesCombo(event, binding) && (chord ? !editing.rich : !editing.field);\n if (!fired) continue;\n event.preventDefault();\n buffer = [];\n handler(event);\n return;\n }\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [enabled, names, latest, timeout]);\n}\n","// Shared by the data and media primitives (client): the clock format, the reduced-motion preference, the\n// in-view observer, a debounced callback. Never an entry of its own.\nimport * as React from \"react\";\n\n/** Seconds as a clock: `75` → `1:15`, `3725` → `1:02:05`. Negative or unknown values read `0:00`. */\nexport function formatClock(seconds: number): string {\n const s = Number.isFinite(seconds) && seconds > 0 ? Math.floor(seconds) : 0;\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const rest = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${rest}` : `${m}:${rest}`;\n}\n\n/** Clamps `value` between `min` and `max`. */\nexport const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst REDUCE = \"(prefers-reduced-motion: reduce)\";\nconst subscribeReduce = (notify: () => void) => {\n if (typeof window === \"undefined\" || !window.matchMedia) return () => undefined;\n const query = window.matchMedia(REDUCE);\n query.addEventListener?.(\"change\", notify);\n return () => query.removeEventListener?.(\"change\", notify);\n};\n\n/** `true` when the person asked for less motion (`prefers-reduced-motion: reduce`); follows a change of the setting. */\nexport function usePrefersReducedMotion(): boolean {\n return React.useSyncExternalStore(\n subscribeReduce,\n () => (typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(REDUCE).matches : false),\n () => false,\n );\n}\n\n/** The latest value of a callback, for an effect or an event that must not re-subscribe when it changes. */\nexport function useLatest<T>(value: T) {\n const ref = React.useRef(value);\n React.useLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n\n/** Calls `onChange(visible)` when at least `threshold` of the element enters or leaves the viewport. */\nexport function useInView(ref: React.RefObject<Element | null>, onChange: (visible: boolean) => void, enabled = true, threshold = 0.25) {\n const latest = useLatest(onChange);\n React.useEffect(() => {\n const element = ref.current;\n if (!enabled || !element || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => entry && latest.current(entry.isIntersecting), { threshold });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, enabled, threshold, latest]);\n}\n\n/** A value controlled by a prop when it is given, held inside otherwise; `set` calls `onChange` either way. */\nexport function useControllable<T>(value: T | undefined, defaultValue: T, onChange?: (next: T) => void) {\n const [inner, setInner] = React.useState(defaultValue);\n const controlled = value !== undefined;\n const current = controlled ? value : inner;\n const set = React.useCallback(\n (next: T) => {\n if (!controlled) setInner(next);\n onChange?.(next);\n },\n [controlled, onChange],\n );\n return [current, set] as const;\n}\n\n/** Starts a media element without an unhandled rejection (autoplay refused, the source replaced meanwhile). */\nexport function playSafely(media: HTMLMediaElement | null | undefined) {\n try {\n const started = media?.play() as Promise<void> | undefined;\n started?.catch?.(() => undefined);\n } catch {\n // Not playable here (no media support): the controls stay as they are.\n }\n}\n"],"mappings":"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","names":["React","tv","React","key","jsx","tv","value"]}
|
|
1
|
+
{"version":3,"sources":["../src/app-shell/app-shell.tsx","../src/kbd/kbd.tsx","../src/app-shell/shortcuts.ts","../src/media.ts"],"sourcesContent":["// Client (the collapse, the drawer, the shortcuts). The shell of an application screen, read from the products' consoles\n// (Orochia admin and studio, Orazaka web-admin and web-client): a sidebar of sections that collapses to its icons on a\n// desktop, a header with the screen's title, a search that opens the app's command palette and its actions, the content —\n// and below `md` the sidebar becomes a drawer opened from the header (Radix Dialog: focus trap, Escape, focus return).\n// Keyboard: ⌘/Ctrl+B folds the sidebar, ⌘/Ctrl+K opens the search, `g` then a letter goes to a section (`shortcut` of an\n// item). Words arrive as props; links through `asChild`.\n// `desktop` (ADR-002): the shell is the window of a desktop app (the Orazaka Mac app, Tauri 2): the sidebar is always\n// there (a window has a minimum width, no drawer), its top clears the traffic lights, the header and the sidebar's top are\n// the title bar (`data-tauri-drag-region`: dragging them moves the window), the density is the desktop's (32 px rows,\n// 13 px text, a 48 px header), the chrome is not selectable, and the content scrolls under a fixed header.\nimport { SkipLink } from \"@krizaka/ui/skip-link\";\nimport { Dialog as DialogPrimitive, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { ChevronGlyph, CloseGlyph, SearchGlyph } from \"../glyphs\";\nimport { Kbd } from \"../kbd\";\nimport { ariaKeyShortcuts, formatShortcut, useApple, useShortcuts } from \"./shortcuts\";\n\nexport const appShell = tv({\n slots: {\n root: \"flex min-h-dvh w-full bg-surface-0 text-fg data-desktop:h-[var(--kz-window-height,100dvh)] data-desktop:overflow-hidden\",\n sidebar:\n \"sticky top-0 hidden h-dvh shrink-0 flex-col overflow-x-clip border-r border-border-subtle bg-surface-1 transition-[width] duration-300 motion-reduce:transition-none md:flex \" +\n \"w-64 data-collapsed:w-[4.25rem] \" +\n \"[[data-desktop]_&]:static [[data-desktop]_&]:flex [[data-desktop]_&]:h-auto [[data-desktop]_&]:w-60 [[data-desktop]_&]:select-none [[data-desktop]_&]:data-collapsed:w-[4.75rem]\",\n titlebar: \"h-[var(--kz-titlebar-inset,1.75rem)] shrink-0\",\n drawer:\n \"kz-dialog fixed inset-y-0 left-0 z-50 flex w-[min(20rem,88vw)] flex-col border-r border-border-default bg-surface-1 pl-[env(safe-area-inset-left)] shadow-lg focus:outline-hidden\",\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay\",\n brand:\n \"flex h-14 shrink-0 items-center gap-2.5 overflow-hidden whitespace-nowrap px-4 font-display text-[15px] font-bold tracking-tight transition-[font-size,gap,padding] duration-300 motion-reduce:transition-none [[data-desktop]_&]:h-11 \" +\n \"[[data-collapsed]_&]:justify-center [[data-collapsed]_&]:gap-0 [[data-collapsed]_&]:px-0 [[data-collapsed]_&]:text-[0px] [[data-collapsed]_&_a]:gap-0 [[data-collapsed]_&_a]:text-[0px]\",\n nav: \"flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2\",\n group: \"mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold text-fg-secondary [[data-collapsed]_&]:sr-only\",\n item:\n \"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 \" +\n \"hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg \" +\n \"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-sm aria-[current=page]:before:bg-accent \" +\n \"[[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0 \" +\n \"[[data-desktop]_&]:h-8 [[data-desktop]_&]:cursor-default [[data-desktop]_&]:rounded-md [[data-desktop]_&]:text-[13px] [[data-desktop]_&_svg]:h-4 [[data-desktop]_&_svg]:w-4\",\n itemLabel: \"min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only\",\n itemHint: \"ml-auto hidden items-center gap-0.5 text-fg-secondary group-hover/item:flex group-focus-visible/item:flex [[data-collapsed]_&]:!hidden\",\n itemBadge:\n \"ml-auto inline-flex min-w-5 items-center justify-center rounded-sm bg-accent px-1.5 text-[11px] font-bold text-on-accent \" +\n \"[[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]\",\n footer: \"shrink-0 overflow-hidden border-t border-border-subtle p-2\",\n // The words the app puts in the footer (account, version): a text-only footer fades out first (a footer with controls stays, clipped) and the box narrows with the sidebar, so nothing spills over the content; they stay in the tree for screen readers.\n footerContent: \"max-w-full overflow-hidden whitespace-nowrap opacity-100 transition-[max-width,opacity] duration-300 motion-reduce:transition-none [[data-collapsed]_&:not(:has(a,button,input,[role=button]))]:max-w-0 [[data-collapsed]_&:not(:has(a,button,input,[role=button]))]:opacity-0 [[data-collapsed]_&:not(:has(a,button,input,[role=button]))]:duration-150\",\n collapse:\n \"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 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0\",\n body: \"flex min-w-0 flex-1 flex-col [[data-desktop]_&]:min-h-0 [[data-desktop]_&]:overflow-y-auto\",\n header:\n \"sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0 px-4 md:px-6 \" +\n \"[[data-desktop]_&]:h-12 [[data-desktop]_&]:select-none [[data-desktop]_&]:px-5\",\n menuButton:\n \"inline-flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-surface-1 text-fg md:hidden [[data-desktop]_&]:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n title: \"min-w-0 flex-1 truncate font-display text-base font-bold tracking-tight [[data-desktop]_&]:text-[15px]\",\n search:\n \"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 \" +\n \"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\",\n searchLabel: \"sr-only md:not-sr-only md:flex-1 md:truncate md:text-left\",\n searchKeys: \"hidden items-center gap-0.5 md:flex\",\n actions: \"ml-auto flex shrink-0 items-center gap-2\",\n main: \"min-w-0 flex-1 px-4 py-6 md:px-6 md:py-8 [[data-desktop]_&]:px-6 [[data-desktop]_&]:py-6\",\n },\n});\nconst s = appShell();\n\ninterface ShellState {\n collapsed: boolean;\n setCollapsed: (value: boolean) => void;\n open: boolean;\n setOpen: (value: boolean) => void;\n inDrawer: boolean;\n desktop: boolean;\n}\nconst ShellContext = React.createContext<ShellState | null>(null);\nconst useShell = () => {\n const ctx = React.useContext(ShellContext);\n if (!ctx) throw new Error(\"AppShell parts must be inside <AppShell.Root>\");\n return ctx;\n};\n\nexport type AppShellRootProps = React.ComponentProps<\"div\"> & {\n /** The sidebar starts collapsed to its icons (desktop). */\n defaultCollapsed?: boolean;\n /** Controlled collapse (desktop): pair with `onCollapsedChange` to persist the choice. */\n collapsed?: boolean;\n /** Called when the sidebar collapses or expands. */\n onCollapsedChange?: (collapsed: boolean) => void;\n /** The words of the \"skip to content\" link (`Skip to content`), the first tab stop; it lands on `AppShell.Main`. Without it, no link. */\n skipLabel?: string;\n /**\n * The shortcut that folds and unfolds the sidebar (a combination: `mod+b` is ⌘B on a Mac, Ctrl+B elsewhere), or `false`\n * for none.\n * @default \"mod+b\"\n */\n collapseShortcut?: string | false;\n /**\n * The shell is a desktop app's window (the Orazaka Mac app, Tauri 2 — ADR-002): no drawer, the sidebar's top clears the\n * window's traffic lights (`--kz-titlebar-inset`, 28 px; the window's height is `--kz-window-height`, the viewport by default), the header and that top move the window\n * (`data-tauri-drag-region`), a denser layout, the chrome not selectable. In a browser it is a fixed-height shell.\n */\n desktop?: boolean;\n};\n\n/** The shell: holds the collapse (desktop) and the drawer (phone) state. Children: Sidebar, then Body. */\nexport function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, skipLabel, collapseShortcut = \"mod+b\", desktop = false, className, children, ...props }: AppShellRootProps) {\n const [own, setOwn] = React.useState(defaultCollapsed);\n const [open, setOpen] = React.useState(false);\n const collapsed = controlled ?? own;\n const setCollapsed = React.useCallback(\n (value: boolean) => {\n if (controlled === undefined) setOwn(value);\n onCollapsedChange?.(value);\n },\n [controlled, onCollapsedChange],\n );\n const value = React.useMemo(() => ({ collapsed, setCollapsed, open, setOpen, inDrawer: false, desktop }), [collapsed, setCollapsed, open, desktop]);\n useShortcuts(collapseShortcut ? { [collapseShortcut]: () => setCollapsed(!collapsed) } : {});\n return (\n <ShellContext.Provider value={value}>\n <div data-desktop={desktop ? \"\" : undefined} className={s.root({ className })} {...props}>\n {skipLabel && <SkipLink href=\"#content\">{skipLabel}</SkipLink>}\n {children}\n </div>\n </ShellContext.Provider>\n );\n}\n\nexport type AppShellSidebarProps = Omit<React.ComponentProps<\"nav\">, \"children\"> & {\n /** The navigation's accessible name (`Studio`, `Administration`). */\n label: string;\n /** The brand at the top: the product's mark and name (a link home through `asChild` of your router, or plain). */\n brand?: React.ReactNode;\n /** The sections: `AppShell.Group` and `AppShell.Item`. */\n children: React.ReactNode;\n /** At the bottom, above the collapse button: the account, a plan, the version. */\n footer?: React.ReactNode;\n /** The collapse button's words, `[collapse, expand]` (desktop only). Without it the sidebar cannot collapse. */\n collapseLabels?: readonly [string, string];\n /** The drawer's close button words (phone). */\n closeLabel: string;\n};\n\nfunction SidebarContent({ brand, children, footer, collapseLabels, closeLabel }: Pick<AppShellSidebarProps, \"brand\" | \"children\" | \"footer\" | \"collapseLabels\" | \"closeLabel\">) {\n const { collapsed, setCollapsed, inDrawer, desktop } = useShell();\n const drag = desktop ? { \"data-tauri-drag-region\": \"\" } : {};\n return (\n <>\n {desktop && <div aria-hidden className={s.titlebar()} {...drag} />}\n <div className={s.brand()} {...drag}>\n {brand}\n {inDrawer && (\n <DialogPrimitive.Close className={cn(s.menuButton(), \"ml-auto flex\")} aria-label={closeLabel}>\n <CloseGlyph className=\"h-4 w-4\" />\n </DialogPrimitive.Close>\n )}\n </div>\n <div className={s.nav()}>{children}</div>\n {(footer || (collapseLabels && !inDrawer)) && (\n <div className={s.footer()}>\n {footer && <div className={s.footerContent()}>{footer}</div>}\n {collapseLabels && !inDrawer && (\n <button type=\"button\" className={s.collapse()} aria-expanded={!collapsed} onClick={() => setCollapsed(!collapsed)}>\n <ChevronGlyph direction={collapsed ? \"right\" : \"left\"} className=\"h-4 w-4\" />\n <span className={collapsed ? \"sr-only\" : undefined}>{collapsed ? collapseLabels[1] : collapseLabels[0]}</span>\n </button>\n )}\n </div>\n )}\n </>\n );\n}\n\n/** The sidebar: on a desktop a column that collapses to its icons; on a phone the same content in a drawer. */\nexport function AppShellSidebar({ label, brand, children, footer, collapseLabels, closeLabel, className, ...props }: AppShellSidebarProps) {\n const shell = useShell();\n const content = { brand, children, footer, collapseLabels, closeLabel };\n return (\n <>\n <nav aria-label={label} data-collapsed={shell.collapsed ? \"\" : undefined} className={s.sidebar({ className })} {...props}>\n <SidebarContent {...content} />\n </nav>\n {!shell.desktop && (\n <DialogPrimitive.Root open={shell.open} onOpenChange={shell.setOpen}>\n <DialogPrimitive.Portal>\n <DialogPrimitive.Overlay className={s.overlay()} />\n <DialogPrimitive.Content className={s.drawer()} aria-describedby={undefined}>\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <ShellContext.Provider value={{ ...shell, collapsed: false, inDrawer: true }}>\n <nav aria-label={label} className=\"flex min-h-0 flex-1 flex-col\" onClick={(e) => (e.target as Element).closest(\"a\") && shell.setOpen(false)}>\n <SidebarContent {...content} />\n </nav>\n </ShellContext.Provider>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n )}\n </>\n );\n}\n\nexport type AppShellGroupProps = React.ComponentProps<\"p\"> & {\n /** The group's name, above its items (hidden when the sidebar is collapsed). */\n children: React.ReactNode;\n};\n\n/** A heading between groups of items. */\nexport function AppShellGroup({ className, ...props }: AppShellGroupProps) {\n return <p className={s.group({ className })} {...props} />;\n}\n\nexport type AppShellItemProps = Omit<React.ComponentProps<\"a\">, \"children\"> & {\n /** The item's icon (a signature icon): it stays when the sidebar collapses. */\n icon?: React.ReactNode;\n /** Its words: the accessible name, hidden visually when collapsed (the icon carries a tooltip through `title`). */\n label: string;\n /** The screen it opens is the current one: `aria-current=\"page\"`. */\n current?: boolean;\n /** A count beside it (unread, pending): a number or a few characters. */\n badge?: React.ReactNode;\n /**\n * 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\n * router link navigates. The keys are shown on hover and focus when the sidebar is open.\n */\n shortcut?: string;\n /** Renders your link (Next's `Link`) with the item's look: pass it as the only child. */\n asChild?: boolean;\n /** Your link element when `asChild`. */\n children?: React.ReactElement;\n};\n\n/** A destination of the sidebar: icon, words, a count. */\nexport function AppShellItem({ icon, label, current, badge, shortcut, asChild, children, className, ...props }: AppShellItemProps) {\n const { collapsed, inDrawer } = useShell();\n const element = React.useRef<HTMLAnchorElement>(null);\n const apple = useApple();\n // Only the sidebar's own items listen: the drawer holds a copy of them, open or not.\n useShortcuts(shortcut && !inDrawer ? { [shortcut]: () => element.current?.click() } : {});\n const Comp = asChild ? Slot.Root : \"a\";\n const inner = (\n <>\n {icon}\n <span className={s.itemLabel()}>{label}</span>\n {badge != null && <span className={s.itemBadge()}>{badge}</span>}\n {shortcut && !inDrawer ? (\n <span className={s.itemHint()} aria-hidden>\n {formatShortcut(shortcut, apple).map((key) => (\n <Kbd key={key} size=\"sm\">\n {key}\n </Kbd>\n ))}\n </span>\n ) : null}\n </>\n );\n return (\n <Comp ref={element} aria-current={current ? \"page\" : undefined} aria-keyshortcuts={shortcut ? ariaKeyShortcuts(shortcut, apple) : undefined} title={collapsed ? label : undefined} className={s.item({ className })} {...props}>\n {asChild && children ? React.cloneElement(children, undefined, inner) : inner}\n </Comp>\n );\n}\n\n/** The column right of the sidebar: Header, then Main. */\nexport function AppShellBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport type AppShellHeaderProps = Omit<React.ComponentProps<\"header\">, \"title\"> & {\n /** The screen's title (an `h1`). */\n title?: React.ReactNode;\n /** The button that opens the drawer on a phone: its words. */\n menuLabel: string;\n /** The screen's actions, on the right. */\n actions?: React.ReactNode;\n};\n\n/** The top of the screen: the drawer's button (phone), the title, the actions. Sticky. */\nexport function AppShellHeader({ title, menuLabel, actions, className, children, ...props }: AppShellHeaderProps) {\n const { open, setOpen, desktop } = useShell();\n const drag = desktop ? { \"data-tauri-drag-region\": \"\" } : {};\n return (\n <header className={s.header({ className })} {...drag} {...props}>\n <button type=\"button\" className={s.menuButton()} aria-label={menuLabel} aria-expanded={open} aria-haspopup=\"dialog\" onClick={() => setOpen(true)}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M4 7h16M4 12h16M4 17h10\" />\n </svg>\n </button>\n {title && (\n <h1 className={s.title()} {...drag}>\n {title}\n </h1>\n )}\n {children}\n {actions && <div className={s.actions()}>{actions}</div>}\n </header>\n );\n}\n\nexport type AppShellSearchProps = Omit<React.ComponentProps<\"button\">, \"children\" | \"onClick\"> & {\n /** The words (\"Search\", \"Search creators\"): the accessible name, shown beside the keys on a desktop. */\n label: string;\n /** Called when the button is pressed or the shortcut is typed: open your command palette (`CommandDialog`) here. */\n onOpen: () => void;\n /**\n * The shortcut that opens it (a combination); shown on its keys.\n * @default \"mod+k\"\n */\n shortcut?: string;\n};\n\n/** The header's search: a button that looks like a field (a magnifier, its words, ⌘K) and opens the app's command palette. */\nexport function AppShellSearch({ label, onOpen, shortcut = \"mod+k\", className, ...props }: AppShellSearchProps) {\n const apple = useApple();\n useShortcuts({ [shortcut]: () => onOpen() });\n return (\n <button type=\"button\" className={s.search({ className })} aria-keyshortcuts={ariaKeyShortcuts(shortcut, apple)} onClick={onOpen} {...props}>\n <SearchGlyph />\n <span className={s.searchLabel()}>{label}</span>\n <span className={s.searchKeys()} aria-hidden>\n {formatShortcut(shortcut, apple).map((key) => (\n <Kbd key={key} size=\"sm\">\n {key}\n </Kbd>\n ))}\n </span>\n </button>\n );\n}\n\n/** The screen's content. */\nexport function AppShellMain({ className, ...props }: React.ComponentProps<\"main\">) {\n return <main id=\"content\" tabIndex={-1} className={s.main({ className })} {...props} />;\n}\n\nexport const AppShell = {\n Root: AppShellRoot,\n Sidebar: AppShellSidebar,\n Group: AppShellGroup,\n Item: AppShellItem,\n Body: AppShellBody,\n Header: AppShellHeader,\n Search: AppShellSearch,\n Main: AppShellMain,\n};\n","// Server-safe. A key, or a shortcut of several (`<Kbd>⌘</Kbd><Kbd>K</Kbd>`).\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const kbdVariants = tv({\n base:\n \"inline-flex shrink-0 items-center justify-center rounded-md border border-border-default border-b-2 bg-surface-2 \" +\n \"font-mono font-semibold text-fg-secondary\",\n variants: { size: { sm: \"h-5 min-w-5 px-1 text-[10px]\", md: \"h-6 min-w-6 px-1.5 text-xs\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type KbdProps = React.ComponentProps<\"kbd\"> & {\n /**\n * sm · md.\n * @default \"md\"\n */\n size?: VariantProps<typeof kbdVariants>[\"size\"];\n};\n\nexport function Kbd({ size, className, ...props }: KbdProps) {\n return <kbd className={kbdVariants({ size, className })} {...props} />;\n}\n","// Keyboard shortcuts for an application: a combination (`mod+k`, `shift+/`, `alt+arrowup`) or a sequence (`g d`: press G,\n// then D). `mod` is ⌘ on Apple devices and Ctrl elsewhere. Plain keys and sequences stay out of text fields; a combination\n// with `mod` works in an input (not in a rich-text editor, where ⌘B is bold). Nothing is decided here about the words.\nimport * as React from \"react\";\n\nimport { useLatest } from \"../media\";\n\nexport type ShortcutMap = Readonly<Record<string, ((event: KeyboardEvent) => void) | undefined>>;\n\nconst isApple = () => typeof navigator !== \"undefined\" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n\nconst ALIASES: Record<string, string> = { esc: \"escape\", return: \"enter\", space: \" \", spacebar: \" \", up: \"arrowup\", down: \"arrowdown\", left: \"arrowleft\", right: \"arrowright\", del: \"delete\", plus: \"+\" };\ntype Modifier = \"mod\" | \"ctrl\" | \"alt\" | \"shift\" | \"meta\";\n\ntype Combo = { key: string; mod: boolean; ctrl: boolean; alt: boolean; shift: boolean; meta: boolean };\n\n/** `\"mod+shift+k\"` → its parts. A lone `+` key is written `plus`. */\nexport function parseCombo(combo: string): Combo {\n const parts = combo.toLowerCase().split(\"+\").filter(Boolean);\n const key = ALIASES[parts.at(-1) ?? \"\"] ?? parts.at(-1) ?? \"\";\n const has = (name: Modifier) => parts.slice(0, -1).includes(name);\n return { key, mod: has(\"mod\"), ctrl: has(\"ctrl\"), alt: has(\"alt\"), shift: has(\"shift\"), meta: has(\"meta\") };\n}\n\n/** Whether a key event is this combination: its modifiers exactly (`mod` is ⌘ or Ctrl by platform), then its key. */\nexport function matchesCombo(event: Pick<KeyboardEvent, \"key\" | \"metaKey\" | \"ctrlKey\" | \"altKey\" | \"shiftKey\">, combo: string, apple = isApple()): boolean {\n const want = parseCombo(combo);\n const meta = want.meta || (want.mod && apple);\n const ctrl = want.ctrl || (want.mod && !apple);\n if (event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== want.alt) return false;\n // Shift is only checked when the binding names it: `?` and `/` need it on most layouts.\n if (want.shift && !event.shiftKey) return false;\n return event.key.toLowerCase() === want.key;\n}\n\n/** The keys of a shortcut to show on a keycap: `[\"⌘\", \"K\"]` on a Mac, `[\"Ctrl\", \"K\"]` elsewhere; a sequence `g d` is `[\"G\", \"D\"]`. */\nexport function formatShortcut(shortcut: string, apple = isApple()): string[] {\n if (shortcut.includes(\" \")) return shortcut.split(/\\s+/).map((key) => key.toUpperCase());\n const parts = shortcut.split(\"+\").filter(Boolean);\n const key = parts.at(-1) ?? \"\";\n const names: Record<string, [string, string]> = { mod: [\"⌘\", \"Ctrl\"], meta: [\"⌘\", \"Win\"], ctrl: [\"⌃\", \"Ctrl\"], alt: [\"⌥\", \"Alt\"], shift: [\"⇧\", \"Shift\"] };\n const shown = parts.slice(0, -1).map((part) => names[part.toLowerCase()]?.[apple ? 0 : 1] ?? part);\n const glyph: Record<string, string> = { escape: \"Esc\", enter: \"↵\", arrowup: \"↑\", arrowdown: \"↓\", arrowleft: \"←\", arrowright: \"→\", \" \": \"Space\" };\n const last = glyph[ALIASES[key.toLowerCase()] ?? key.toLowerCase()] ?? key.toUpperCase();\n return [...shown, last];\n}\n\n/**\n * The accelerator of a combination for a desktop app's native menu bar (Tauri's `MenuItem`, Electron's — ADR-002):\n * `mod+k` → `CmdOrCtrl+K`, `shift+mod+/` → `Shift+CmdOrCtrl+/`. The menu and the shell then answer the same keys, and the\n * menu shows them. A sequence (`g d`) has none: a menu item takes one chord.\n */\nexport function acceleratorOf(shortcut: string): string | undefined {\n if (shortcut.trim().includes(\" \")) return undefined;\n const parts = shortcut.split(\"+\").filter(Boolean);\n const names: Record<string, string> = { mod: \"CmdOrCtrl\", meta: \"Super\", ctrl: \"Ctrl\", alt: \"Alt\", shift: \"Shift\" };\n const raw = (parts.at(-1) ?? \"\").toLowerCase();\n const key = ALIASES[raw] ?? raw;\n const keys: Record<string, string> = { escape: \"Escape\", enter: \"Enter\", \" \": \"Space\", arrowup: \"Up\", arrowdown: \"Down\", arrowleft: \"Left\", arrowright: \"Right\", delete: \"Delete\", backspace: \"Backspace\", tab: \"Tab\", \"+\": \"Plus\" };\n const last = keys[key] ?? (key.length === 1 ? key.toUpperCase() : key.charAt(0).toUpperCase() + key.slice(1));\n return [...parts.slice(0, -1).map((part) => names[part.toLowerCase()] ?? part), last].join(\"+\");\n}\n\nconst noop = () => () => undefined;\n\n/** Whether the person is on an Apple device — `false` on the server and in the first client render (no mismatch), then right. */\nexport function useApple(): boolean {\n return React.useSyncExternalStore(noop, isApple, () => false);\n}\n\n/** The `aria-keyshortcuts` of a combination (`Control+K`, `Meta+K` on a Mac); a sequence has none (it is not a chord). */\nexport function ariaKeyShortcuts(shortcut: string, apple = isApple()): string | undefined {\n if (shortcut.includes(\" \")) return undefined;\n const parts = shortcut.split(\"+\").filter(Boolean);\n const names: Record<string, string> = { mod: apple ? \"Meta\" : \"Control\", meta: \"Meta\", ctrl: \"Control\", alt: \"Alt\", shift: \"Shift\" };\n const key = parts.at(-1) ?? \"\";\n return [...parts.slice(0, -1).map((part) => names[part.toLowerCase()] ?? part), key.length === 1 ? key.toUpperCase() : key.charAt(0).toUpperCase() + key.slice(1)].join(\"+\");\n}\n\n/** Whether a key press happens in something you type in. */\nfunction isEditable(target: EventTarget | null): { field: boolean; rich: boolean } {\n const element = target instanceof HTMLElement ? target : null;\n if (!element) return { field: false, rich: false };\n const rich = element.isContentEditable;\n return { field: rich || /^(input|textarea|select)$/i.test(element.tagName), rich };\n}\n\n/**\n * Binds shortcuts for as long as the component lives (`enabled` turns them off). Each key is a combination or a sequence,\n * each value runs when it is pressed; the handler may change on every render. A shortcut that fires is `preventDefault`ed.\n * A sequence waits `timeout` ms for its next key.\n */\nexport function useShortcuts(map: ShortcutMap, { enabled = true, timeout = 900 }: { enabled?: boolean; timeout?: number } = {}) {\n const latest = useLatest(map);\n const names = Object.keys(map).sort().join(\"\\n\");\n React.useEffect(() => {\n if (!enabled) return;\n const bindings = names.split(\"\\n\").filter(Boolean);\n let buffer: { key: string; at: number }[] = [];\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.isComposing || event.repeat || !event.key) return;\n const editing = isEditable(event.target);\n const chord = event.metaKey || event.ctrlKey || event.altKey;\n const now = Date.now();\n buffer = [...buffer.filter((entry) => now - entry.at <= timeout), { key: event.key.toLowerCase(), at: now }].slice(-4);\n for (const binding of bindings) {\n const handler = latest.current[binding];\n if (!handler) continue;\n const keys = binding.toLowerCase().split(/\\s+/).map((key) => ALIASES[key] ?? key);\n const fired =\n keys.length > 1\n ? !chord && !editing.field && buffer.length >= keys.length && keys.every((key, i) => buffer[buffer.length - keys.length + i]?.key === key)\n : matchesCombo(event, binding) && (chord ? !editing.rich : !editing.field);\n if (!fired) continue;\n event.preventDefault();\n buffer = [];\n handler(event);\n return;\n }\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [enabled, names, latest, timeout]);\n}\n","// Shared by the data and media primitives (client): the clock format, the reduced-motion preference, the\n// in-view observer, a debounced callback. Never an entry of its own.\nimport * as React from \"react\";\n\n/** Seconds as a clock: `75` → `1:15`, `3725` → `1:02:05`. Negative or unknown values read `0:00`. */\nexport function formatClock(seconds: number): string {\n const s = Number.isFinite(seconds) && seconds > 0 ? Math.floor(seconds) : 0;\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const rest = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${rest}` : `${m}:${rest}`;\n}\n\n/** Clamps `value` between `min` and `max`. */\nexport const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst REDUCE = \"(prefers-reduced-motion: reduce)\";\nconst subscribeReduce = (notify: () => void) => {\n if (typeof window === \"undefined\" || !window.matchMedia) return () => undefined;\n const query = window.matchMedia(REDUCE);\n query.addEventListener?.(\"change\", notify);\n return () => query.removeEventListener?.(\"change\", notify);\n};\n\n/** `true` when the person asked for less motion (`prefers-reduced-motion: reduce`); follows a change of the setting. */\nexport function usePrefersReducedMotion(): boolean {\n return React.useSyncExternalStore(\n subscribeReduce,\n () => (typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(REDUCE).matches : false),\n () => false,\n );\n}\n\n/** The latest value of a callback, for an effect or an event that must not re-subscribe when it changes. */\nexport function useLatest<T>(value: T) {\n const ref = React.useRef(value);\n React.useLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n\n/** Calls `onChange(visible)` when at least `threshold` of the element enters or leaves the viewport. */\nexport function useInView(ref: React.RefObject<Element | null>, onChange: (visible: boolean) => void, enabled = true, threshold = 0.25) {\n const latest = useLatest(onChange);\n React.useEffect(() => {\n const element = ref.current;\n if (!enabled || !element || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => entry && latest.current(entry.isIntersecting), { threshold });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, enabled, threshold, latest]);\n}\n\n/** A value controlled by a prop when it is given, held inside otherwise; `set` calls `onChange` either way. */\nexport function useControllable<T>(value: T | undefined, defaultValue: T, onChange?: (next: T) => void) {\n const [inner, setInner] = React.useState(defaultValue);\n const controlled = value !== undefined;\n const current = controlled ? value : inner;\n const set = React.useCallback(\n (next: T) => {\n if (!controlled) setInner(next);\n onChange?.(next);\n },\n [controlled, onChange],\n );\n return [current, set] as const;\n}\n\n/** Starts a media element without an unhandled rejection (autoplay refused, the source replaced meanwhile). */\nexport function playSafely(media: HTMLMediaElement | null | undefined) {\n try {\n const started = media?.play() as Promise<void> | undefined;\n started?.catch?.(() => undefined);\n } catch {\n // Not playable here (no media support): the controls stay as they are.\n }\n}\n"],"mappings":"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","names":["React","tv","React","key","jsx","tv","value"]}
|
package/dist/code-block.js
CHANGED
|
@@ -79,7 +79,7 @@ var tabs = tv({
|
|
|
79
79
|
// (server render), each trigger carries its own active style.
|
|
80
80
|
list: "group/tabs relative isolate flex shrink-0 data-[orientation=vertical]:flex-col before:pointer-events-none before:absolute before:-z-10 before:opacity-0 before:transition-[translate,width,height,opacity] before:duration-300 motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100",
|
|
81
81
|
trigger: "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40",
|
|
82
|
-
content: "kz-
|
|
82
|
+
content: "kz-tabs-panel min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=inactive]:hidden"
|
|
83
83
|
},
|
|
84
84
|
variants: {
|
|
85
85
|
variant: {
|
package/dist/code-block.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/code-block/code-block.tsx","../src/tabs/tabs.tsx","../src/sliding-indicator.ts","../src/code-block/tokens.tsx","../src/code-block/highlighter.ts"],"sourcesContent":["// Client (the copy button, the tabs). A sample of code (blocks.css): its language and its file name, the code\n// highlighted by TanStack Highlight (it scrolls sideways, never shrinks, and takes the keyboard's focus to scroll),\n// lines numbered or set apart (highlighted, added, removed, in focus), a copy button that says it copied in a polite\n// live region. CodeTabs puts several ways to write the same thing (Maven · Gradle · npm) under\n// the tabs of @krizaka/ui.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { CheckGlyph, CopyGlyph } from \"../glyphs\";\nimport { TabsContent, TabsList, TabsRoot, TabsTrigger } from \"../tabs/tabs\";\nimport { type CodeAnnotation, highlight } from \"./tokens\";\n\nexport type CodeBlockProps = Omit<React.HTMLAttributes<HTMLElement>, \"children\"> & {\n /** The code, as it is copied. */\n code: string;\n /**\n * Its language, shown as a tag and used by the highlighter: `ts`, `tsx`, `js`, `jsx`, `json`, `yaml`, `bash`, `html`\n * (`xml`), `css`, `java`, `kotlin`, `diff`, `properties`. Another one is shown as plain text.\n */\n language?: string;\n /** The file it belongs in (`pom.xml`, `application.yml`), or what it is (`Terminal`). */\n file?: React.ReactNode;\n /** The copy button's words, passed translated. Without them there is no copy button (a sample inside a text, say). */\n copyLabel?: string;\n /** What the button says once the code is copied (announced politely). */\n copiedLabel?: string;\n /**\n * Colours comments, strings, keywords, types, tags and numbers (the tokens' roles).\n * @default true\n */\n highlight?: boolean;\n /** A number before every line. */\n lineNumbers?: boolean;\n /** Lines set apart: `highlight`, `added`, `removed`, `error`, `warning`, `dim`, or `focus` (all the others are dimmed). */\n annotations?: readonly CodeAnnotation[];\n /** Called after the code is copied. */\n onCopy?: () => void;\n};\n\n/** A code sample: language and file name, highlighted code that scrolls sideways, a copy button. */\nexport function CodeBlock({ code, language = \"text\", file, copyLabel, copiedLabel, highlight: colour = true, lineNumbers, annotations, onCopy, className, ...props }: CodeBlockProps) {\n const [copied, setCopied] = React.useState(false);\n React.useEffect(() => {\n if (!copied) return;\n const t = setTimeout(() => setCopied(false), 1800);\n return () => clearTimeout(t);\n }, [copied]);\n const copy = () =>\n navigator.clipboard?.writeText(code).then(() => {\n setCopied(true);\n onCopy?.();\n });\n return (\n <figure className={cn(\"kz-code\", className)} {...props}>\n {file || language !== \"text\" || copyLabel ? (\n <figcaption className=\"kz-code-bar\">\n <span className=\"kz-code-file\">\n {language !== \"text\" && <span className=\"kz-code-lang\">{language}</span>}\n {file}\n </span>\n {copyLabel ? (\n <button type=\"button\" className=\"kz-code-copy\" onClick={copy} data-copied={copied ? \"\" : undefined}>\n {copied ? <CheckGlyph className=\"h-3.5 w-3.5\" /> : <CopyGlyph className=\"h-3.5 w-3.5\" />}\n <span aria-live=\"polite\">{copied ? (copiedLabel ?? copyLabel) : copyLabel}</span>\n </button>\n ) : null}\n </figcaption>\n ) : null}\n <pre tabIndex={0} className={lineNumbers ? \"th-code--line-numbers\" : undefined} data-focus={annotations?.some((a) => a.kind === \"focus\") ? \"\" : undefined}>\n <code data-language={language}>{colour ? highlight(code, language, { lineNumbers, annotations }) : code}</code>\n </pre>\n </figure>\n );\n}\n\nexport type CodeTabsProps = Omit<React.HTMLAttributes<HTMLDivElement>, \"dir\" | \"defaultValue\"> & {\n /** What the tabs choose between (`Build tool`): the tab list's accessible name. */\n label: string;\n /** The ways to write it: an id, the tab's words, its sample (a `CodeBlock`). */\n tabs: readonly { id: string; label: React.ReactNode; content: React.ReactNode }[];\n /** The tab open at first. Defaults to the first one. */\n defaultValue?: string;\n};\n\n/** Several ways to write the same thing (Maven · Gradle · npm): segmented tabs above code samples. */\nexport function CodeTabs({ label, tabs, defaultValue, className, ...props }: CodeTabsProps) {\n return (\n <TabsRoot defaultValue={defaultValue ?? tabs[0]?.id} variant=\"segmented\" className={cn(\"kz-code-tabs\", className)} {...props}>\n <TabsList aria-label={label}>\n {tabs.map((t) => (\n <TabsTrigger key={t.id} value={t.id}>\n {t.label}\n </TabsTrigger>\n ))}\n </TabsList>\n {tabs.map((t) => (\n <TabsContent key={t.id} value={t.id}>\n {t.content}\n </TabsContent>\n ))}\n </TabsRoot>\n );\n}\n","// Client (a context). Radix Tabs does the roles (tablist, tab, tabpanel), the aria-selected / aria-controls wiring,\n// the roving focus (arrows, Home, End) and automatic activation: this file only holds the three looks.\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { useSlidingIndicator } from \"../sliding-indicator\";\n\nexport const tabs = tv({\n slots: {\n root: \"flex gap-4 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row\",\n // One light glides from the active tab to the next (`before:`, placed by useSlidingIndicator); until it is placed\n // (server render), each trigger carries its own active style.\n list:\n \"group/tabs relative isolate flex shrink-0 data-[orientation=vertical]:flex-col \" +\n \"before:pointer-events-none before:absolute before:-z-10 before:opacity-0 before:transition-[translate,width,height,opacity] before:duration-300 \" +\n \"motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n trigger:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40\",\n content: \"kz-fade min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=inactive]:hidden\",\n },\n variants: {\n variant: {\n /* The section of a page: a line under the list, the active tab underlined in the accent. */\n underline: {\n list:\n \"gap-1 border-border-default data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-r before:rounded-sm before:bg-accent \" +\n \"data-[orientation=horizontal]:before:-bottom-px data-[orientation=horizontal]:before:left-0 data-[orientation=horizontal]:before:h-0.5 \" +\n \"data-[orientation=horizontal]:before:w-(--kz-ind-w) data-[orientation=horizontal]:before:translate-x-(--kz-ind-x) \" +\n \"data-[orientation=vertical]:before:-right-px data-[orientation=vertical]:before:top-0 data-[orientation=vertical]:before:w-0.5 \" +\n \"data-[orientation=vertical]:before:h-(--kz-ind-h) data-[orientation=vertical]:before:translate-y-(--kz-ind-y)\",\n trigger:\n \"relative rounded-md px-3 py-2.5 text-sm text-fg-secondary hover:text-fg data-[state=active]:text-fg \" +\n \"after:absolute after:rounded-sm after:bg-transparent data-[state=active]:after:bg-accent \" +\n \"data-[orientation=horizontal]:after:inset-x-2 data-[orientation=horizontal]:after:-bottom-px data-[orientation=horizontal]:after:h-0.5 \" +\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:after:inset-y-1.5 data-[orientation=vertical]:after:-right-px data-[orientation=vertical]:after:w-0.5 \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:after:bg-transparent\",\n },\n /* A view switch: equal segments in a track, the active one filled. */\n segmented: {\n list:\n \"inline-grid auto-cols-fr grid-flow-col gap-1 self-start rounded-xl border border-border-default bg-surface-1 p-1 data-[orientation=vertical]:grid-flow-row \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:bg-accent before:shadow-sm\",\n trigger:\n \"min-h-8 rounded-lg px-3 py-1.5 text-xs text-fg-secondary hover:bg-surface-2 hover:text-fg \" +\n \"data-[state=active]:bg-accent data-[state=active]:text-on-accent data-[state=active]:shadow-sm data-[state=active]:hover:bg-accent \" +\n \"transition-colors group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent group-data-[indicator=on]/tabs:data-[state=active]:shadow-none\",\n },\n /* Pills that scroll sideways on a phone: a feed's sections. */\n pills: {\n list:\n \"gap-1.5 overflow-x-auto overflow-y-hidden overscroll-x-contain pt-1 -mt-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-sm before:border before:border-accent before:bg-accent-soft\",\n trigger:\n \"h-9 rounded-sm border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:border-transparent group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent\",\n },\n },\n },\n defaultVariants: { variant: \"underline\" },\n});\n\nexport type TabsVariants = VariantProps<typeof tabs>;\ntype Variant = NonNullable<TabsVariants[\"variant\"]>;\n\nconst VariantContext = React.createContext<Variant>(\"underline\");\n\nexport type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root> & {\n /** underline (the sections of a page) · segmented (a view switch) · pills (a feed's sections). */\n variant?: TabsVariants[\"variant\"];\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */\nexport function TabsRoot({ variant = \"underline\", className, ...props }: TabsRootProps) {\n return (\n <VariantContext.Provider value={variant}>\n <TabsPrimitive.Root data-variant={variant} className={tabs({ variant }).root({ className })} {...props} />\n </VariantContext.Provider>\n );\n}\n\n/** The tabs. Name it (`aria-label`) when no heading says what it switches. One light glides to the active tab. */\nexport function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n const variant = React.useContext(VariantContext);\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=tab]\", active: \"[role=tab][data-state=active]\" });\n return <TabsPrimitive.List ref={ref} data-variant={variant} className={tabs({ variant }).list({ className })} {...props} />;\n}\n\nexport function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.Trigger className={tabs({ variant }).trigger({ className })} {...props} />;\n}\n\nexport function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return <TabsPrimitive.Content className={tabs().content({ className })} {...props} />;\n}\n\n/** `Tabs.Root/List/Trigger/Content` — `variant` underline · segmented · pills on the root. */\nexport const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };\n","// The shared sliding indicator (NavBar, Tabs): one element that glides from item to item instead of each item lighting\n// up on its own. It writes the target's box as CSS variables on the list (`--kz-ind-x/y/w/h`) and `data-indicator=\"on\"`\n// — no re-render, no animation frame: the glide is a CSS transition (none under reduced motion). Before hydration the\n// items keep their own active style, so nothing flashes.\nimport * as React from \"react\";\n\nconst useIsoLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\n/**\n * `item`: the selector of an item; `active`: of the current one, where the light rests; `prefer`: an item that, when\n * present, holds the light before the current one (the NavBar's open menu); `follow`: the light follows the pointer\n * and the focus — over anything that is not an item (an open menu's panel) it goes back to rest.\n */\nexport function useSlidingIndicator<T extends HTMLElement>({ item, active, prefer, follow = false }: { item: string; active: string; prefer?: string; follow?: boolean }) {\n const ref = React.useRef<T>(null);\n const place = React.useCallback((target: Element | null) => {\n const list = ref.current;\n if (!list) return;\n if (!(target instanceof HTMLElement) || !list.contains(target)) {\n list.removeAttribute(\"data-indicator\");\n return;\n }\n const a = target.getBoundingClientRect();\n const b = list.getBoundingClientRect();\n list.style.setProperty(\"--kz-ind-x\", `${a.left - b.left + list.scrollLeft}px`);\n list.style.setProperty(\"--kz-ind-y\", `${a.top - b.top + list.scrollTop}px`);\n list.style.setProperty(\"--kz-ind-w\", `${a.width}px`);\n list.style.setProperty(\"--kz-ind-h\", `${a.height}px`);\n list.setAttribute(\"data-indicator\", \"on\");\n }, []);\n const rest = React.useCallback(\n () => {\n const list = ref.current;\n // An item re-rendered under a resting pointer (a menu closing) keeps the light the pointer gave it.\n const hovered = follow ? list?.querySelector(`${item}:hover`) : null;\n place((prefer ? list?.querySelector(prefer) : null) ?? hovered ?? list?.querySelector(active) ?? null);\n },\n [place, active, prefer, item, follow],\n );\n const follow_ = React.useCallback((target: EventTarget) => {\n const hit = target instanceof Element ? target.closest(item) : null;\n if (hit && ref.current?.contains(hit)) place(hit);\n else rest();\n }, [item, place, rest]);\n\n useIsoLayoutEffect(() => {\n const list = ref.current;\n if (!list) return;\n rest();\n const resize = new ResizeObserver(rest);\n resize.observe(list);\n const changes = new MutationObserver(rest);\n changes.observe(list, { subtree: true, attributes: true, attributeFilter: [\"data-state\", \"aria-current\", \"data-active\"] });\n return () => {\n resize.disconnect();\n changes.disconnect();\n };\n }, [rest]);\n\n const handlers = follow\n ? {\n onPointerOver: (e: React.PointerEvent) => follow_(e.target),\n onPointerLeave: rest,\n onFocus: (e: React.FocusEvent) => follow_(e.target),\n onBlur: rest,\n }\n : {};\n return { ref, handlers };\n}\n","// Code → React nodes, from TanStack Highlight's tokens: spans with the `th-*` classes that blocks.css paints with the\n// tokens' text roles (≥ 4.5:1 on the code surface, both themes). No raw HTML: the nodes are elements and text.\nimport { type HighlightDecoration, type HighlightRenderNode, renderTokens } from \"@tanstack/highlight/core\";\nimport * as React from \"react\";\n\nimport { codeHighlighter } from \"./highlighter\";\n\n/** What an annotation says about its lines. */\nexport type AnnotationKind = \"highlight\" | \"added\" | \"removed\" | \"focus\" | \"dim\" | \"error\" | \"warning\";\n\n/** Lines of a sample set apart: one line (`3`) or a range (`[3, 5]`), counted from 1. */\nexport type CodeAnnotation = {\n lines: number | readonly [number, number];\n kind: AnnotationKind;\n};\n\nexport type HighlightOptions = {\n /** A number before every line. */\n lineNumbers?: boolean;\n /** Lines set apart: highlighted, added, removed, in focus (the others are dimmed), dimmed, error, warning. */\n annotations?: readonly CodeAnnotation[];\n};\n\nconst toReact = (node: HighlightRenderNode, key: number): React.ReactNode => {\n if (node.type === \"text\") return node.value;\n const data = Object.fromEntries(Object.entries(node.data ?? {}).map(([name, value]) => [`data-${name}`, value]));\n return React.createElement(\"span\", { key, className: node.classNames.join(\" \"), ...data }, node.children.map(toReact));\n};\n\n/** Splits `code` into text and coloured spans (`th-keyword`, `th-string`…), and into lines when numbered or annotated. */\nexport function highlight(code: string, language = \"text\", { lineNumbers, annotations }: HighlightOptions = {}): React.ReactNode[] {\n const { tokens } = codeHighlighter.tokenize(code, { lang: language });\n const decorations: HighlightDecoration[] = (annotations ?? []).map(({ lines, kind }) => ({ lines, className: `kz-line kz-line-${kind}`, data: { kind } }));\n return renderTokens(tokens, { lineNumbers, decorations }).map(toReact);\n}\n\n/** The language a sample is tokenized in (`bash` → `shell`, `xml` → `html`, unknown → `plaintext`). */\nexport function normalizeCodeLanguage(language?: string): string {\n return codeHighlighter.normalizeLanguage(language);\n}\n","// The highlighter of every code sample: TanStack Highlight (a deterministic, synchronous tokenizer — the same markup on\n// the server and on the client) with the grammars a Krizaka page shows. Each grammar is registered by hand: nothing\n// else is bundled. An unknown language is plain text, never an error.\nimport { createHighlighter } from \"@tanstack/highlight/core\";\nimport { css } from \"@tanstack/highlight/languages/css\";\nimport { diff } from \"@tanstack/highlight/languages/diff\";\nimport { env } from \"@tanstack/highlight/languages/env\";\nimport { html } from \"@tanstack/highlight/languages/html\";\nimport { java } from \"@tanstack/highlight/languages/java\";\nimport { js } from \"@tanstack/highlight/languages/js\";\nimport { json } from \"@tanstack/highlight/languages/json\";\nimport { jsx } from \"@tanstack/highlight/languages/jsx\";\nimport { kotlin } from \"@tanstack/highlight/languages/kotlin\";\nimport { plaintext } from \"@tanstack/highlight/languages/plaintext\";\nimport { shell } from \"@tanstack/highlight/languages/shell\";\nimport { ts } from \"@tanstack/highlight/languages/ts\";\nimport { tsx } from \"@tanstack/highlight/languages/tsx\";\nimport { yaml } from \"@tanstack/highlight/languages/yaml\";\n\n/** The languages a sample can be written in, with the names they answer to (`bash`, `xml`, `yml`, `kt`, `properties`…). */\nexport const codeHighlighter = createHighlighter({\n languages: [css, diff, { ...env, aliases: [...(env.aliases ?? []), \"properties\"] }, html, java, js, json, jsx, kotlin, plaintext, shell, ts, tsx, yaml],\n fallbackLanguage: \"plaintext\",\n});\n"],"mappings":"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","names":["React","React","React","jsx","jsx","tabs"]}
|
|
1
|
+
{"version":3,"sources":["../src/code-block/code-block.tsx","../src/tabs/tabs.tsx","../src/sliding-indicator.ts","../src/code-block/tokens.tsx","../src/code-block/highlighter.ts"],"sourcesContent":["// Client (the copy button, the tabs). A sample of code (blocks.css): its language and its file name, the code\n// highlighted by TanStack Highlight (it scrolls sideways, never shrinks, and takes the keyboard's focus to scroll),\n// lines numbered or set apart (highlighted, added, removed, in focus), a copy button that says it copied in a polite\n// live region. CodeTabs puts several ways to write the same thing (Maven · Gradle · npm) under\n// the tabs of @krizaka/ui.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { CheckGlyph, CopyGlyph } from \"../glyphs\";\nimport { TabsContent, TabsList, TabsRoot, TabsTrigger } from \"../tabs/tabs\";\nimport { type CodeAnnotation, highlight } from \"./tokens\";\n\nexport type CodeBlockProps = Omit<React.HTMLAttributes<HTMLElement>, \"children\"> & {\n /** The code, as it is copied. */\n code: string;\n /**\n * Its language, shown as a tag and used by the highlighter: `ts`, `tsx`, `js`, `jsx`, `json`, `yaml`, `bash`, `html`\n * (`xml`), `css`, `java`, `kotlin`, `diff`, `properties`. Another one is shown as plain text.\n */\n language?: string;\n /** The file it belongs in (`pom.xml`, `application.yml`), or what it is (`Terminal`). */\n file?: React.ReactNode;\n /** The copy button's words, passed translated. Without them there is no copy button (a sample inside a text, say). */\n copyLabel?: string;\n /** What the button says once the code is copied (announced politely). */\n copiedLabel?: string;\n /**\n * Colours comments, strings, keywords, types, tags and numbers (the tokens' roles).\n * @default true\n */\n highlight?: boolean;\n /** A number before every line. */\n lineNumbers?: boolean;\n /** Lines set apart: `highlight`, `added`, `removed`, `error`, `warning`, `dim`, or `focus` (all the others are dimmed). */\n annotations?: readonly CodeAnnotation[];\n /** Called after the code is copied. */\n onCopy?: () => void;\n};\n\n/** A code sample: language and file name, highlighted code that scrolls sideways, a copy button. */\nexport function CodeBlock({ code, language = \"text\", file, copyLabel, copiedLabel, highlight: colour = true, lineNumbers, annotations, onCopy, className, ...props }: CodeBlockProps) {\n const [copied, setCopied] = React.useState(false);\n React.useEffect(() => {\n if (!copied) return;\n const t = setTimeout(() => setCopied(false), 1800);\n return () => clearTimeout(t);\n }, [copied]);\n const copy = () =>\n navigator.clipboard?.writeText(code).then(() => {\n setCopied(true);\n onCopy?.();\n });\n return (\n <figure className={cn(\"kz-code\", className)} {...props}>\n {file || language !== \"text\" || copyLabel ? (\n <figcaption className=\"kz-code-bar\">\n <span className=\"kz-code-file\">\n {language !== \"text\" && <span className=\"kz-code-lang\">{language}</span>}\n {file}\n </span>\n {copyLabel ? (\n <button type=\"button\" className=\"kz-code-copy\" onClick={copy} data-copied={copied ? \"\" : undefined}>\n {copied ? <CheckGlyph className=\"h-3.5 w-3.5\" /> : <CopyGlyph className=\"h-3.5 w-3.5\" />}\n <span aria-live=\"polite\">{copied ? (copiedLabel ?? copyLabel) : copyLabel}</span>\n </button>\n ) : null}\n </figcaption>\n ) : null}\n <pre tabIndex={0} className={lineNumbers ? \"th-code--line-numbers\" : undefined} data-focus={annotations?.some((a) => a.kind === \"focus\") ? \"\" : undefined}>\n <code data-language={language}>{colour ? highlight(code, language, { lineNumbers, annotations }) : code}</code>\n </pre>\n </figure>\n );\n}\n\nexport type CodeTabsProps = Omit<React.HTMLAttributes<HTMLDivElement>, \"dir\" | \"defaultValue\"> & {\n /** What the tabs choose between (`Build tool`): the tab list's accessible name. */\n label: string;\n /** The ways to write it: an id, the tab's words, its sample (a `CodeBlock`). */\n tabs: readonly { id: string; label: React.ReactNode; content: React.ReactNode }[];\n /** The tab open at first. Defaults to the first one. */\n defaultValue?: string;\n};\n\n/** Several ways to write the same thing (Maven · Gradle · npm): segmented tabs above code samples. */\nexport function CodeTabs({ label, tabs, defaultValue, className, ...props }: CodeTabsProps) {\n return (\n <TabsRoot defaultValue={defaultValue ?? tabs[0]?.id} variant=\"segmented\" className={cn(\"kz-code-tabs\", className)} {...props}>\n <TabsList aria-label={label}>\n {tabs.map((t) => (\n <TabsTrigger key={t.id} value={t.id}>\n {t.label}\n </TabsTrigger>\n ))}\n </TabsList>\n {tabs.map((t) => (\n <TabsContent key={t.id} value={t.id}>\n {t.content}\n </TabsContent>\n ))}\n </TabsRoot>\n );\n}\n","// Client (a context). Radix Tabs does the roles (tablist, tab, tabpanel), the aria-selected / aria-controls wiring,\n// the roving focus (arrows, Home, End) and automatic activation: this file only holds the three looks.\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { useSlidingIndicator } from \"../sliding-indicator\";\n\nexport const tabs = tv({\n slots: {\n root: \"flex gap-4 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:flex-row\",\n // One light glides from the active tab to the next (`before:`, placed by useSlidingIndicator); until it is placed\n // (server render), each trigger carries its own active style.\n list:\n \"group/tabs relative isolate flex shrink-0 data-[orientation=vertical]:flex-col \" +\n \"before:pointer-events-none before:absolute before:-z-10 before:opacity-0 before:transition-[translate,width,height,opacity] before:duration-300 \" +\n \"motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n trigger:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40\",\n content: \"kz-tabs-panel min-w-0 flex-1 rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=inactive]:hidden\",\n },\n variants: {\n variant: {\n /* The section of a page: a line under the list, the active tab underlined in the accent. */\n underline: {\n list:\n \"gap-1 border-border-default data-[orientation=horizontal]:border-b data-[orientation=vertical]:border-r before:rounded-sm before:bg-accent \" +\n \"data-[orientation=horizontal]:before:-bottom-px data-[orientation=horizontal]:before:left-0 data-[orientation=horizontal]:before:h-0.5 \" +\n \"data-[orientation=horizontal]:before:w-(--kz-ind-w) data-[orientation=horizontal]:before:translate-x-(--kz-ind-x) \" +\n \"data-[orientation=vertical]:before:-right-px data-[orientation=vertical]:before:top-0 data-[orientation=vertical]:before:w-0.5 \" +\n \"data-[orientation=vertical]:before:h-(--kz-ind-h) data-[orientation=vertical]:before:translate-y-(--kz-ind-y)\",\n trigger:\n \"relative rounded-md px-3 py-2.5 text-sm text-fg-secondary hover:text-fg data-[state=active]:text-fg \" +\n \"after:absolute after:rounded-sm after:bg-transparent data-[state=active]:after:bg-accent \" +\n \"data-[orientation=horizontal]:after:inset-x-2 data-[orientation=horizontal]:after:-bottom-px data-[orientation=horizontal]:after:h-0.5 \" +\n \"data-[orientation=vertical]:justify-start data-[orientation=vertical]:after:inset-y-1.5 data-[orientation=vertical]:after:-right-px data-[orientation=vertical]:after:w-0.5 \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:after:bg-transparent\",\n },\n /* A view switch: equal segments in a track, the active one filled. */\n segmented: {\n list:\n \"inline-grid auto-cols-fr grid-flow-col gap-1 self-start rounded-xl border border-border-default bg-surface-1 p-1 data-[orientation=vertical]:grid-flow-row \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:bg-accent before:shadow-sm\",\n trigger:\n \"min-h-8 rounded-lg px-3 py-1.5 text-xs text-fg-secondary hover:bg-surface-2 hover:text-fg \" +\n \"data-[state=active]:bg-accent data-[state=active]:text-on-accent data-[state=active]:shadow-sm data-[state=active]:hover:bg-accent \" +\n \"transition-colors group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent group-data-[indicator=on]/tabs:data-[state=active]:shadow-none\",\n },\n /* Pills that scroll sideways on a phone: a feed's sections. */\n pills: {\n list:\n \"gap-1.5 overflow-x-auto overflow-y-hidden overscroll-x-contain pt-1 -mt-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden \" +\n \"before:left-0 before:top-0 before:h-(--kz-ind-h) before:w-(--kz-ind-w) before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) \" +\n \"before:rounded-sm before:border before:border-accent before:bg-accent-soft\",\n trigger:\n \"h-9 rounded-sm border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg \" +\n \"group-data-[indicator=on]/tabs:data-[state=active]:border-transparent group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent\",\n },\n },\n },\n defaultVariants: { variant: \"underline\" },\n});\n\nexport type TabsVariants = VariantProps<typeof tabs>;\ntype Variant = NonNullable<TabsVariants[\"variant\"]>;\n\nconst VariantContext = React.createContext<Variant>(\"underline\");\n\nexport type TabsRootProps = React.ComponentProps<typeof TabsPrimitive.Root> & {\n /** underline (the sections of a page) · segmented (a view switch) · pills (a feed's sections). */\n variant?: TabsVariants[\"variant\"];\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation` (horizontal by default), `variant` for the list. */\nexport function TabsRoot({ variant = \"underline\", className, ...props }: TabsRootProps) {\n return (\n <VariantContext.Provider value={variant}>\n <TabsPrimitive.Root data-variant={variant} className={tabs({ variant }).root({ className })} {...props} />\n </VariantContext.Provider>\n );\n}\n\n/** The tabs. Name it (`aria-label`) when no heading says what it switches. One light glides to the active tab. */\nexport function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n const variant = React.useContext(VariantContext);\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=tab]\", active: \"[role=tab][data-state=active]\" });\n return <TabsPrimitive.List ref={ref} data-variant={variant} className={tabs({ variant }).list({ className })} {...props} />;\n}\n\nexport function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n const variant = React.useContext(VariantContext);\n return <TabsPrimitive.Trigger className={tabs({ variant }).trigger({ className })} {...props} />;\n}\n\nexport function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return <TabsPrimitive.Content className={tabs().content({ className })} {...props} />;\n}\n\n/** `Tabs.Root/List/Trigger/Content` — `variant` underline · segmented · pills on the root. */\nexport const Tabs = { Root: TabsRoot, List: TabsList, Trigger: TabsTrigger, Content: TabsContent };\n","// The shared sliding indicator (NavBar, Tabs): one element that glides from item to item instead of each item lighting\n// up on its own. It writes the target's box as CSS variables on the list (`--kz-ind-x/y/w/h`) and `data-indicator=\"on\"`\n// — no re-render, no animation frame: the glide is a CSS transition (none under reduced motion). Before hydration the\n// items keep their own active style, so nothing flashes.\nimport * as React from \"react\";\n\nconst useIsoLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\n/**\n * `item`: the selector of an item; `active`: of the current one, where the light rests; `prefer`: an item that, when\n * present, holds the light before the current one (the NavBar's open menu); `follow`: the light follows the pointer\n * and the focus — over anything that is not an item (an open menu's panel) it goes back to rest.\n */\nexport function useSlidingIndicator<T extends HTMLElement>({ item, active, prefer, follow = false }: { item: string; active: string; prefer?: string; follow?: boolean }) {\n const ref = React.useRef<T>(null);\n const place = React.useCallback((target: Element | null) => {\n const list = ref.current;\n if (!list) return;\n if (!(target instanceof HTMLElement) || !list.contains(target)) {\n list.removeAttribute(\"data-indicator\");\n return;\n }\n const a = target.getBoundingClientRect();\n const b = list.getBoundingClientRect();\n list.style.setProperty(\"--kz-ind-x\", `${a.left - b.left + list.scrollLeft}px`);\n list.style.setProperty(\"--kz-ind-y\", `${a.top - b.top + list.scrollTop}px`);\n list.style.setProperty(\"--kz-ind-w\", `${a.width}px`);\n list.style.setProperty(\"--kz-ind-h\", `${a.height}px`);\n list.setAttribute(\"data-indicator\", \"on\");\n }, []);\n const rest = React.useCallback(\n () => {\n const list = ref.current;\n // An item re-rendered under a resting pointer (a menu closing) keeps the light the pointer gave it.\n const hovered = follow ? list?.querySelector(`${item}:hover`) : null;\n place((prefer ? list?.querySelector(prefer) : null) ?? hovered ?? list?.querySelector(active) ?? null);\n },\n [place, active, prefer, item, follow],\n );\n const follow_ = React.useCallback((target: EventTarget) => {\n const hit = target instanceof Element ? target.closest(item) : null;\n if (hit && ref.current?.contains(hit)) place(hit);\n else rest();\n }, [item, place, rest]);\n\n useIsoLayoutEffect(() => {\n const list = ref.current;\n if (!list) return;\n rest();\n const resize = new ResizeObserver(rest);\n resize.observe(list);\n const changes = new MutationObserver(rest);\n changes.observe(list, { subtree: true, attributes: true, attributeFilter: [\"data-state\", \"aria-current\", \"data-active\"] });\n return () => {\n resize.disconnect();\n changes.disconnect();\n };\n }, [rest]);\n\n const handlers = follow\n ? {\n onPointerOver: (e: React.PointerEvent) => follow_(e.target),\n onPointerLeave: rest,\n onFocus: (e: React.FocusEvent) => follow_(e.target),\n onBlur: rest,\n }\n : {};\n return { ref, handlers };\n}\n","// Code → React nodes, from TanStack Highlight's tokens: spans with the `th-*` classes that blocks.css paints with the\n// tokens' text roles (≥ 4.5:1 on the code surface, both themes). No raw HTML: the nodes are elements and text.\nimport { type HighlightDecoration, type HighlightRenderNode, renderTokens } from \"@tanstack/highlight/core\";\nimport * as React from \"react\";\n\nimport { codeHighlighter } from \"./highlighter\";\n\n/** What an annotation says about its lines. */\nexport type AnnotationKind = \"highlight\" | \"added\" | \"removed\" | \"focus\" | \"dim\" | \"error\" | \"warning\";\n\n/** Lines of a sample set apart: one line (`3`) or a range (`[3, 5]`), counted from 1. */\nexport type CodeAnnotation = {\n lines: number | readonly [number, number];\n kind: AnnotationKind;\n};\n\nexport type HighlightOptions = {\n /** A number before every line. */\n lineNumbers?: boolean;\n /** Lines set apart: highlighted, added, removed, in focus (the others are dimmed), dimmed, error, warning. */\n annotations?: readonly CodeAnnotation[];\n};\n\nconst toReact = (node: HighlightRenderNode, key: number): React.ReactNode => {\n if (node.type === \"text\") return node.value;\n const data = Object.fromEntries(Object.entries(node.data ?? {}).map(([name, value]) => [`data-${name}`, value]));\n return React.createElement(\"span\", { key, className: node.classNames.join(\" \"), ...data }, node.children.map(toReact));\n};\n\n/** Splits `code` into text and coloured spans (`th-keyword`, `th-string`…), and into lines when numbered or annotated. */\nexport function highlight(code: string, language = \"text\", { lineNumbers, annotations }: HighlightOptions = {}): React.ReactNode[] {\n const { tokens } = codeHighlighter.tokenize(code, { lang: language });\n const decorations: HighlightDecoration[] = (annotations ?? []).map(({ lines, kind }) => ({ lines, className: `kz-line kz-line-${kind}`, data: { kind } }));\n return renderTokens(tokens, { lineNumbers, decorations }).map(toReact);\n}\n\n/** The language a sample is tokenized in (`bash` → `shell`, `xml` → `html`, unknown → `plaintext`). */\nexport function normalizeCodeLanguage(language?: string): string {\n return codeHighlighter.normalizeLanguage(language);\n}\n","// The highlighter of every code sample: TanStack Highlight (a deterministic, synchronous tokenizer — the same markup on\n// the server and on the client) with the grammars a Krizaka page shows. Each grammar is registered by hand: nothing\n// else is bundled. An unknown language is plain text, never an error.\nimport { createHighlighter } from \"@tanstack/highlight/core\";\nimport { css } from \"@tanstack/highlight/languages/css\";\nimport { diff } from \"@tanstack/highlight/languages/diff\";\nimport { env } from \"@tanstack/highlight/languages/env\";\nimport { html } from \"@tanstack/highlight/languages/html\";\nimport { java } from \"@tanstack/highlight/languages/java\";\nimport { js } from \"@tanstack/highlight/languages/js\";\nimport { json } from \"@tanstack/highlight/languages/json\";\nimport { jsx } from \"@tanstack/highlight/languages/jsx\";\nimport { kotlin } from \"@tanstack/highlight/languages/kotlin\";\nimport { plaintext } from \"@tanstack/highlight/languages/plaintext\";\nimport { shell } from \"@tanstack/highlight/languages/shell\";\nimport { ts } from \"@tanstack/highlight/languages/ts\";\nimport { tsx } from \"@tanstack/highlight/languages/tsx\";\nimport { yaml } from \"@tanstack/highlight/languages/yaml\";\n\n/** The languages a sample can be written in, with the names they answer to (`bash`, `xml`, `yml`, `kt`, `properties`…). */\nexport const codeHighlighter = createHighlighter({\n languages: [css, diff, { ...env, aliases: [...(env.aliases ?? []), \"properties\"] }, html, java, js, json, jsx, kotlin, plaintext, shell, ts, tsx, yaml],\n fallbackLanguage: \"plaintext\",\n});\n"],"mappings":"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","names":["React","React","React","jsx","jsx","tabs"]}
|
package/dist/date-picker.js
CHANGED
|
@@ -741,7 +741,7 @@ function PopoverContent({ sideOffset = 8, collisionPadding = 8, container, class
|
|
|
741
741
|
{
|
|
742
742
|
sideOffset,
|
|
743
743
|
collisionPadding,
|
|
744
|
-
className: cn(floating, "w-72 max-w-(--radix-popover-content-available-width) p-4 origin-(--radix-popover-content-transform-origin)", className),
|
|
744
|
+
className: cn(floating, "kz-popover w-72 max-w-(--radix-popover-content-available-width) p-4 origin-(--radix-popover-content-transform-origin)", className),
|
|
745
745
|
...props
|
|
746
746
|
}
|
|
747
747
|
) });
|