@krizaka/ui 3.21.0 → 3.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/app-shell.js +1 -1
- package/dist/argument-card.js +3 -3
- package/dist/{chunk-5MH64NIL.js → chunk-2L67YTNV.js} +2 -2
- package/dist/chunk-AQSZJJAJ.js +126 -0
- package/dist/chunk-AQSZJJAJ.js.map +1 -0
- package/dist/{chunk-3GODX3D6.js → chunk-QDXV544C.js} +2 -2
- package/dist/chunk-QDXV544C.js.map +1 -0
- package/dist/chunk-R6NK6ZNY.js +1080 -0
- package/dist/chunk-R6NK6ZNY.js.map +1 -0
- package/dist/{chunk-LIPK4UCS.js → chunk-YCIBJ6OQ.js} +140 -209
- package/dist/chunk-YCIBJ6OQ.js.map +1 -0
- package/dist/comparison-table.d.ts +44 -0
- package/dist/comparison-table.js +84 -0
- package/dist/comparison-table.js.map +1 -0
- package/dist/data-table.js +19 -1066
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.js +3 -3
- package/dist/genui.d.ts +219 -0
- package/dist/genui.js +506 -0
- package/dist/genui.js.map +1 -0
- package/dist/index.d.ts +17 -24
- package/dist/index.js +1 -1
- package/dist/marketing-layout.js +1 -1
- package/dist/marketing-layout.js.map +1 -1
- package/dist/media-grid.js +43 -151
- package/dist/media-grid.js.map +1 -1
- package/dist/meta-row.d.ts +54 -0
- package/dist/meta-row.js +35 -0
- package/dist/meta-row.js.map +1 -0
- package/dist/native.d.ts +25 -16
- package/dist/native.js +493 -520
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.js +2 -2
- package/dist/page-hero.js +3 -3
- package/dist/site-footer.js +2 -2
- package/dist/skip-link.js +1 -1
- package/dist/step-showcase.d.ts +62 -0
- package/dist/step-showcase.js +227 -0
- package/dist/step-showcase.js.map +1 -0
- package/package.json +61 -4
- package/registry/achievement.json +1 -1
- package/registry/action-bar.json +1 -1
- package/registry/activity-feed.json +1 -1
- package/registry/alert.json +1 -1
- package/registry/app-shell.json +1 -1
- 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/button.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.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 +207 -0
- 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/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/document-viewer.json +1 -1
- package/registry/dropdown-menu.json +1 -1
- package/registry/empty-state.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/comparison-table/versus.d.ts +4 -0
- package/registry/examples/comparison-table/versus.js +28 -0
- package/registry/examples/comparison-table/versus.tsx +20 -0
- package/registry/examples/dialog/native/sheet.dark.png +0 -0
- package/registry/examples/dialog/native/sheet.light.png +0 -0
- package/registry/examples/genui/streaming.d.ts +4 -0
- package/registry/examples/genui/streaming.js +18 -0
- package/registry/examples/genui/streaming.tsx +14 -0
- package/registry/examples/genui/table.d.ts +4 -0
- package/registry/examples/genui/table.js +19 -0
- package/registry/examples/genui/table.tsx +14 -0
- package/registry/examples/meta-row/native/video.d.ts +4 -0
- package/registry/examples/meta-row/native/video.dark.png +0 -0
- package/registry/examples/meta-row/native/video.js +11 -0
- package/registry/examples/meta-row/native/video.light.png +0 -0
- package/registry/examples/meta-row/native/video.tsx +5 -0
- package/registry/examples/meta-row/status.d.ts +4 -0
- package/registry/examples/meta-row/status.js +12 -0
- package/registry/examples/meta-row/status.tsx +7 -0
- package/registry/examples/meta-row/video.d.ts +4 -0
- package/registry/examples/meta-row/video.js +12 -0
- package/registry/examples/meta-row/video.tsx +7 -0
- package/registry/examples/step-showcase/desktop.d.ts +4 -0
- package/registry/examples/step-showcase/desktop.js +24 -0
- package/registry/examples/step-showcase/desktop.tsx +19 -0
- package/registry/examples/step-showcase/phone.d.ts +4 -0
- package/registry/examples/step-showcase/phone.js +24 -0
- package/registry/examples/step-showcase/phone.tsx +19 -0
- package/registry/field.json +1 -1
- package/registry/file-upload.json +1 -1
- package/registry/filter-bar.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 +409 -0
- 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 +130 -4
- package/registry/iso.json +1 -1
- package/registry/kanban.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/landing-page.json +1 -1
- package/registry/landscape.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +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-grid.json +1 -1
- package/registry/meta-row.json +215 -0
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +13 -13
- package/registry/number-ticker.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page.json +1 -1
- package/registry/pagination.json +1 -1
- package/registry/photo-grid.json +1 -1
- package/registry/pointer-light.json +1 -1
- package/registry/popover.json +1 -1
- package/registry/pricing-page.json +1 -1
- package/registry/product-page.json +1 -1
- package/registry/profile-card.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/promise-card.json +1 -1
- package/registry/radio-group.json +1 -1
- package/registry/recipes.json +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/settings-layout.json +1 -1
- package/registry/showcase.json +1 -1
- package/registry/site-footer.json +13 -13
- package/registry/sites.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/skip-link.json +2 -2
- package/registry/slider.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/spinner.json +1 -1
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +1 -1
- package/registry/stat.json +1 -1
- package/registry/status-page.json +1 -1
- package/registry/step-progress.json +1 -1
- package/registry/step-showcase.json +296 -0
- package/registry/stepper.json +1 -1
- package/registry/swipe-deck.json +1 -1
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- 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/dist/chunk-3GODX3D6.js.map +0 -1
- package/dist/chunk-LIPK4UCS.js.map +0 -1
- /package/dist/{chunk-5MH64NIL.js.map → chunk-2L67YTNV.js.map} +0 -0
package/dist/nav-bar.js
CHANGED
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
GitHubGlyph,
|
|
5
5
|
familyLabels,
|
|
6
6
|
sitesByGroup
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2L67YTNV.js";
|
|
8
8
|
import {
|
|
9
9
|
useSlidingIndicator
|
|
10
10
|
} from "./chunk-4QN5YJ3D.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-YCIBJ6OQ.js";
|
|
12
12
|
import {
|
|
13
13
|
cn
|
|
14
14
|
} from "./chunk-2FRWNSRO.js";
|
package/dist/page-hero.js
CHANGED
package/dist/site-footer.js
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
familyLabels,
|
|
5
5
|
siteById,
|
|
6
6
|
sitesByGroup
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2L67YTNV.js";
|
|
8
8
|
import {
|
|
9
9
|
KrizakaLogo
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YCIBJ6OQ.js";
|
|
11
11
|
|
|
12
12
|
// src/site-footer/site-footer.tsx
|
|
13
13
|
import { Slot } from "radix-ui";
|
package/dist/skip-link.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
type DeviceKind = "phone" | "browser" | "desktop";
|
|
4
|
+
interface DeviceFrameProps extends React.ComponentProps<"div"> {
|
|
5
|
+
/** The device: a phone, a browser window or a macOS window. */
|
|
6
|
+
kind: DeviceKind;
|
|
7
|
+
/** The browser's address or the window's title (“dev.orazaka.com”, “Orazaka”). */
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
/** A phone, a browser window or a macOS window around a screen of content. */
|
|
11
|
+
declare function DeviceFrame({ kind, title, className, children, ...props }: DeviceFrameProps): React.JSX.Element;
|
|
12
|
+
|
|
13
|
+
interface ShowcaseStep {
|
|
14
|
+
/** A stable key. */
|
|
15
|
+
id: string;
|
|
16
|
+
/** The step's name (“Make an image”), passed translated. */
|
|
17
|
+
title: string;
|
|
18
|
+
/** One line under it. */
|
|
19
|
+
description: string;
|
|
20
|
+
/** The still shown first, and the only picture under reduced motion. */
|
|
21
|
+
poster: string;
|
|
22
|
+
/** What the poster shows, for assistive technology. */
|
|
23
|
+
alt: string;
|
|
24
|
+
/** The recording: WebM and / or MP4. Without one, the poster stays `duration` ms. */
|
|
25
|
+
video?: {
|
|
26
|
+
webm?: string;
|
|
27
|
+
mp4?: string;
|
|
28
|
+
};
|
|
29
|
+
/** Milliseconds a step without a video stays. @default 6000 */
|
|
30
|
+
duration?: number;
|
|
31
|
+
}
|
|
32
|
+
interface StepShowcaseLabels {
|
|
33
|
+
/** The button's name while paused. */
|
|
34
|
+
play: string;
|
|
35
|
+
/** The button's name while it plays. */
|
|
36
|
+
pause: string;
|
|
37
|
+
/** A step's position in words: `(2, 4) => "Step 2 of 4"`. */
|
|
38
|
+
step: (n: number, total: number) => string;
|
|
39
|
+
}
|
|
40
|
+
declare const stepShowcaseLabels: StepShowcaseLabels;
|
|
41
|
+
interface StepShowcaseProps extends Omit<React.ComponentProps<"section">, "children"> {
|
|
42
|
+
/** The sequence's accessible name (“Orazaka in your pocket”), passed translated. */
|
|
43
|
+
label: string;
|
|
44
|
+
/** The steps, in the order of the journey. */
|
|
45
|
+
steps: readonly ShowcaseStep[];
|
|
46
|
+
/** The device the recordings were made on. */
|
|
47
|
+
device: DeviceKind;
|
|
48
|
+
/** The browser's address or the window's title. */
|
|
49
|
+
deviceTitle?: string;
|
|
50
|
+
/** Moves on by itself (never under reduced motion). @default true */
|
|
51
|
+
autoplay?: boolean;
|
|
52
|
+
/** The controls' words, translated. */
|
|
53
|
+
labels?: StepShowcaseLabels;
|
|
54
|
+
/** The step shown first. @default 0 */
|
|
55
|
+
defaultIndex?: number;
|
|
56
|
+
/** Called when the step changes — `user` is true when the visitor asked for it. */
|
|
57
|
+
onIndexChange?: (index: number, user: boolean) => void;
|
|
58
|
+
}
|
|
59
|
+
/** One device frame, its steps beside it, playing each recording then the next — a product's guided tour. */
|
|
60
|
+
declare function StepShowcase({ label, steps, device, deviceTitle, autoplay, labels, defaultIndex, onIndexChange, className, ...props }: StepShowcaseProps): React.JSX.Element;
|
|
61
|
+
|
|
62
|
+
export { DeviceFrame, type DeviceFrameProps, type DeviceKind, type ShowcaseStep, StepShowcase, type StepShowcaseLabels, type StepShowcaseProps, stepShowcaseLabels };
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
cn
|
|
4
|
+
} from "./chunk-2FRWNSRO.js";
|
|
5
|
+
|
|
6
|
+
// src/step-showcase/device-frame.tsx
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var screen = {
|
|
9
|
+
phone: "aspect-[9/19.5]",
|
|
10
|
+
browser: "aspect-[16/10]",
|
|
11
|
+
desktop: "aspect-[16/10]"
|
|
12
|
+
};
|
|
13
|
+
function DeviceFrame({ kind, title, className, children, ...props }) {
|
|
14
|
+
if (kind === "phone") {
|
|
15
|
+
return /* @__PURE__ */ jsx("div", { "data-device": "phone", className: cn("relative mx-auto w-full max-w-[18rem] rounded-[2.4rem] border border-border-strong bg-surface-3 p-2 shadow-2xl", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: cn("relative overflow-hidden rounded-[1.9rem] bg-surface-0", screen.phone), children: [
|
|
16
|
+
children,
|
|
17
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute left-1/2 top-2 h-5 w-20 -translate-x-1/2 rounded-full bg-surface-3" })
|
|
18
|
+
] }) });
|
|
19
|
+
}
|
|
20
|
+
return /* @__PURE__ */ jsxs("div", { "data-device": kind, className: cn("relative w-full overflow-hidden rounded-xl border border-border-default bg-surface-1 shadow-2xl", className), ...props, children: [
|
|
21
|
+
/* @__PURE__ */ jsxs("div", { className: "flex h-8 items-center gap-1.5 border-b border-border-subtle bg-surface-2 px-3", children: [
|
|
22
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2.5 w-2.5 rounded-full bg-danger/80" }),
|
|
23
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2.5 w-2.5 rounded-full bg-warning/80" }),
|
|
24
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2.5 w-2.5 rounded-full bg-success/80" }),
|
|
25
|
+
title && (kind === "browser" ? /* @__PURE__ */ jsx("span", { className: "mx-auto max-w-[60%] truncate rounded-md bg-surface-1 px-3 py-0.5 font-mono text-[11px] text-fg-secondary", children: title }) : /* @__PURE__ */ jsx("span", { className: "mx-auto truncate text-xs font-semibold text-fg-secondary", children: title }))
|
|
26
|
+
] }),
|
|
27
|
+
/* @__PURE__ */ jsx("div", { className: cn("relative overflow-hidden bg-surface-0", screen[kind]), children })
|
|
28
|
+
] });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/step-showcase/step-showcase.tsx
|
|
32
|
+
import * as React from "react";
|
|
33
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
34
|
+
var FILL_CSS = ".kz-step-fill{transform-origin:left;animation:kz-step-fill var(--kz-step-ms,6000ms) linear forwards}@keyframes kz-step-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}";
|
|
35
|
+
var REDUCED = "(prefers-reduced-motion: reduce)";
|
|
36
|
+
var noop = () => () => void 0;
|
|
37
|
+
var readReduced = () => Boolean(typeof window !== "undefined" && window.matchMedia?.(REDUCED).matches);
|
|
38
|
+
var subscribeReduced = (notify) => {
|
|
39
|
+
const q = window.matchMedia?.(REDUCED);
|
|
40
|
+
q?.addEventListener?.("change", notify);
|
|
41
|
+
return () => q?.removeEventListener?.("change", notify);
|
|
42
|
+
};
|
|
43
|
+
var stepShowcaseLabels = {
|
|
44
|
+
play: "Play the steps",
|
|
45
|
+
pause: "Pause the steps",
|
|
46
|
+
step: (n, total) => `Step ${n} of ${total}`
|
|
47
|
+
};
|
|
48
|
+
function StepShowcase({ label, steps, device, deviceTitle, autoplay = true, labels = stepShowcaseLabels, defaultIndex = 0, onIndexChange, className, ...props }) {
|
|
49
|
+
const total = steps.length;
|
|
50
|
+
const id = React.useId();
|
|
51
|
+
const root = React.useRef(null);
|
|
52
|
+
const video = React.useRef(null);
|
|
53
|
+
const bar = React.useRef(null);
|
|
54
|
+
const swipe = React.useRef(null);
|
|
55
|
+
const [index, setIndex] = React.useState(defaultIndex);
|
|
56
|
+
const [paused, setPaused] = React.useState(false);
|
|
57
|
+
const [hold, setHold] = React.useState({ hover: false, focus: false });
|
|
58
|
+
const [inView, setInView] = React.useState(false);
|
|
59
|
+
const [seen, setSeen] = React.useState(false);
|
|
60
|
+
const reduced = React.useSyncExternalStore(subscribeReduced, readReduced, () => false);
|
|
61
|
+
const mounted = React.useSyncExternalStore(noop, () => true, () => false);
|
|
62
|
+
const current = steps[Math.min(index, total - 1)];
|
|
63
|
+
const auto = autoplay && !reduced && mounted;
|
|
64
|
+
const running = auto && !paused && !hold.hover && !hold.focus && inView;
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
const host = root.current;
|
|
67
|
+
if (!host || typeof IntersectionObserver === "undefined") return;
|
|
68
|
+
const io = new IntersectionObserver((e) => {
|
|
69
|
+
const visible = e.some((x) => x.isIntersecting);
|
|
70
|
+
setInView(visible);
|
|
71
|
+
if (visible) setSeen(true);
|
|
72
|
+
}, { threshold: 0.35 });
|
|
73
|
+
io.observe(host);
|
|
74
|
+
return () => io.disconnect();
|
|
75
|
+
}, []);
|
|
76
|
+
const go = React.useCallback(
|
|
77
|
+
(to, user) => {
|
|
78
|
+
const next = (to + total) % total;
|
|
79
|
+
setIndex(next);
|
|
80
|
+
if (bar.current) bar.current.style.transform = "scaleX(0)";
|
|
81
|
+
onIndexChange?.(next, user);
|
|
82
|
+
},
|
|
83
|
+
[total, onIndexChange]
|
|
84
|
+
);
|
|
85
|
+
React.useEffect(() => {
|
|
86
|
+
const v = video.current;
|
|
87
|
+
if (!v) return;
|
|
88
|
+
if (running) v.play()?.catch?.(() => void 0);
|
|
89
|
+
else v.pause();
|
|
90
|
+
}, [running, index, seen]);
|
|
91
|
+
const playable = Boolean(current?.video) && seen && !reduced;
|
|
92
|
+
const onTime = (e) => {
|
|
93
|
+
const v = e.currentTarget;
|
|
94
|
+
if (bar.current && v.duration) bar.current.style.transform = `scaleX(${v.currentTime / v.duration})`;
|
|
95
|
+
};
|
|
96
|
+
return /* @__PURE__ */ jsxs2(
|
|
97
|
+
"section",
|
|
98
|
+
{
|
|
99
|
+
ref: root,
|
|
100
|
+
"aria-roledescription": "carousel",
|
|
101
|
+
"aria-label": label,
|
|
102
|
+
className: cn("grid items-center gap-6 md:grid-cols-[minmax(0,20rem)_minmax(0,1fr)] md:gap-10", className),
|
|
103
|
+
onPointerEnter: () => setHold((h) => ({ ...h, hover: true })),
|
|
104
|
+
onPointerLeave: () => setHold((h) => ({ ...h, hover: false })),
|
|
105
|
+
onFocus: () => setHold((h) => ({ ...h, focus: true })),
|
|
106
|
+
onBlur: (e) => {
|
|
107
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setHold((h) => ({ ...h, focus: false }));
|
|
108
|
+
},
|
|
109
|
+
...props,
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ jsx2("style", { href: "kz-step-showcase", precedence: "medium", children: FILL_CSS }),
|
|
112
|
+
/* @__PURE__ */ jsxs2("div", { className: "order-2 grid gap-3 md:order-1", children: [
|
|
113
|
+
/* @__PURE__ */ jsx2("div", { role: "tablist", "aria-orientation": "vertical", "aria-label": label, className: "flex justify-center gap-2 md:grid md:gap-1", children: steps.map((s, i) => {
|
|
114
|
+
const active = i === index;
|
|
115
|
+
return /* @__PURE__ */ jsxs2(
|
|
116
|
+
"button",
|
|
117
|
+
{
|
|
118
|
+
type: "button",
|
|
119
|
+
role: "tab",
|
|
120
|
+
id: `${id}-tab-${i}`,
|
|
121
|
+
"aria-selected": active,
|
|
122
|
+
"aria-controls": `${id}-panel`,
|
|
123
|
+
tabIndex: active ? 0 : -1,
|
|
124
|
+
onClick: () => go(i, true),
|
|
125
|
+
onKeyDown: (e) => {
|
|
126
|
+
if (e.key === "ArrowDown" || e.key === "ArrowRight") go(i + 1, true);
|
|
127
|
+
else if (e.key === "ArrowUp" || e.key === "ArrowLeft") go(i - 1, true);
|
|
128
|
+
else return;
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
},
|
|
131
|
+
className: cn(
|
|
132
|
+
"group relative rounded-xl text-left outline-hidden transition-colors focus-visible:ring-2 focus-visible:ring-ring",
|
|
133
|
+
"max-md:h-2.5 max-md:w-2.5 max-md:rounded-full max-md:bg-border-strong max-md:aria-selected:w-6 max-md:aria-selected:bg-accent",
|
|
134
|
+
"md:overflow-hidden md:p-4 md:hover:bg-surface-2 md:aria-selected:bg-surface-2"
|
|
135
|
+
),
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsxs2("span", { className: "max-md:sr-only", children: [
|
|
138
|
+
/* @__PURE__ */ jsxs2("span", { className: "flex items-baseline gap-3", children: [
|
|
139
|
+
/* @__PURE__ */ jsx2("span", { className: cn("font-mono text-xs", active ? "text-fg-accent" : "text-fg-secondary"), children: String(i + 1).padStart(2, "0") }),
|
|
140
|
+
/* @__PURE__ */ jsx2("span", { className: cn("text-sm font-semibold", active ? "text-fg" : "text-fg-secondary"), children: s.title })
|
|
141
|
+
] }),
|
|
142
|
+
/* @__PURE__ */ jsx2("span", { className: "mt-1 block pl-8 text-xs leading-relaxed text-fg-secondary", children: s.description })
|
|
143
|
+
] }),
|
|
144
|
+
active && auto && /* @__PURE__ */ jsx2("span", { "aria-hidden": true, className: "absolute inset-x-4 bottom-0 hidden h-0.5 overflow-hidden rounded-full bg-border-subtle md:block", children: /* @__PURE__ */ jsx2(
|
|
145
|
+
"span",
|
|
146
|
+
{
|
|
147
|
+
ref: bar,
|
|
148
|
+
"data-fill": true,
|
|
149
|
+
className: cn("block h-full bg-accent", !playable && "kz-step-fill"),
|
|
150
|
+
style: { transform: playable ? "scaleX(0)" : void 0, animationPlayState: running ? "running" : "paused", ["--kz-step-ms"]: `${s.duration ?? 6e3}ms` },
|
|
151
|
+
onAnimationEnd: () => go(index + 1, false)
|
|
152
|
+
},
|
|
153
|
+
`${s.id}-${playable}`
|
|
154
|
+
) })
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
s.id
|
|
158
|
+
);
|
|
159
|
+
}) }),
|
|
160
|
+
/* @__PURE__ */ jsxs2("p", { "aria-hidden": true, className: "text-center text-sm md:hidden", children: [
|
|
161
|
+
/* @__PURE__ */ jsx2("span", { className: "font-semibold text-fg", children: current?.title }),
|
|
162
|
+
" ",
|
|
163
|
+
/* @__PURE__ */ jsx2("span", { className: "text-fg-secondary", children: current?.description })
|
|
164
|
+
] }),
|
|
165
|
+
auto && /* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => setPaused((p) => !p), "aria-pressed": paused, className: "mx-auto inline-flex h-8 items-center gap-2 rounded-full border border-border-subtle px-3 text-xs font-medium text-fg-secondary hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring md:mx-0 md:w-fit", children: [
|
|
166
|
+
/* @__PURE__ */ jsx2("svg", { viewBox: "0 0 24 24", className: "h-3.5 w-3.5", fill: "currentColor", "aria-hidden": true, children: paused ? /* @__PURE__ */ jsx2("path", { d: "M8 5v14l11-7z" }) : /* @__PURE__ */ jsx2("path", { d: "M7 5h4v14H7zM13 5h4v14h-4z" }) }),
|
|
167
|
+
paused ? labels.play : labels.pause
|
|
168
|
+
] })
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsxs2(
|
|
171
|
+
"div",
|
|
172
|
+
{
|
|
173
|
+
id: `${id}-panel`,
|
|
174
|
+
role: "tabpanel",
|
|
175
|
+
"aria-roledescription": "slide",
|
|
176
|
+
"aria-labelledby": `${id}-tab-${index}`,
|
|
177
|
+
className: "order-1 touch-pan-y md:order-2",
|
|
178
|
+
onPointerDown: (e) => {
|
|
179
|
+
swipe.current = e.clientX;
|
|
180
|
+
},
|
|
181
|
+
onPointerUp: (e) => {
|
|
182
|
+
if (swipe.current === null) return;
|
|
183
|
+
const dx = e.clientX - swipe.current;
|
|
184
|
+
swipe.current = null;
|
|
185
|
+
if (Math.abs(dx) > 40) go(index + (dx < 0 ? 1 : -1), true);
|
|
186
|
+
},
|
|
187
|
+
children: [
|
|
188
|
+
/* @__PURE__ */ jsx2(DeviceFrame, { kind: device, title: deviceTitle, children: steps.map((s, i) => {
|
|
189
|
+
const active = i === index;
|
|
190
|
+
return /* @__PURE__ */ jsxs2("div", { "data-step": s.id, "aria-hidden": !active, className: cn("absolute inset-0 transition-opacity duration-500 motion-reduce:transition-none", active ? "opacity-100" : "opacity-0"), children: [
|
|
191
|
+
/* @__PURE__ */ jsx2("img", { src: s.poster, alt: active ? s.alt : "", loading: i === 0 ? "eager" : "lazy", decoding: "async", className: "h-full w-full object-cover" }),
|
|
192
|
+
active && playable && s.video && /* @__PURE__ */ jsxs2(
|
|
193
|
+
"video",
|
|
194
|
+
{
|
|
195
|
+
ref: video,
|
|
196
|
+
muted: true,
|
|
197
|
+
playsInline: true,
|
|
198
|
+
preload: "metadata",
|
|
199
|
+
poster: s.poster,
|
|
200
|
+
"aria-hidden": true,
|
|
201
|
+
tabIndex: -1,
|
|
202
|
+
onTimeUpdate: onTime,
|
|
203
|
+
onEnded: () => go(index + 1, false),
|
|
204
|
+
className: "absolute inset-0 h-full w-full object-cover",
|
|
205
|
+
children: [
|
|
206
|
+
s.video.webm && /* @__PURE__ */ jsx2("source", { src: s.video.webm, type: "video/webm" }),
|
|
207
|
+
s.video.mp4 && /* @__PURE__ */ jsx2("source", { src: s.video.mp4, type: "video/mp4" })
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
s.id
|
|
211
|
+
)
|
|
212
|
+
] }, s.id);
|
|
213
|
+
}) }),
|
|
214
|
+
/* @__PURE__ */ jsx2("span", { className: "sr-only", "aria-live": "off", children: labels.step(index + 1, total) })
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
export {
|
|
223
|
+
DeviceFrame,
|
|
224
|
+
StepShowcase,
|
|
225
|
+
stepShowcaseLabels
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=step-showcase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/step-showcase/device-frame.tsx","../src/step-showcase/step-showcase.tsx"],"sourcesContent":["// Server-safe. The frame a product's recording plays in: a phone, a browser window or a macOS window — drawn with the\n// tokens (no image), so it follows dark and light. The content fills the screen area (`object-cover` media).\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\n\nexport type DeviceKind = \"phone\" | \"browser\" | \"desktop\";\n\nexport interface DeviceFrameProps extends React.ComponentProps<\"div\"> {\n /** The device: a phone, a browser window or a macOS window. */\n kind: DeviceKind;\n /** The browser's address or the window's title (“dev.orazaka.com”, “Orazaka”). */\n title?: string;\n}\n\nconst screen: Record<DeviceKind, string> = {\n phone: \"aspect-[9/19.5]\",\n browser: \"aspect-[16/10]\",\n desktop: \"aspect-[16/10]\",\n};\n\n/** A phone, a browser window or a macOS window around a screen of content. */\nexport function DeviceFrame({ kind, title, className, children, ...props }: DeviceFrameProps) {\n if (kind === \"phone\") {\n return (\n <div data-device=\"phone\" className={cn(\"relative mx-auto w-full max-w-[18rem] rounded-[2.4rem] border border-border-strong bg-surface-3 p-2 shadow-2xl\", className)} {...props}>\n <div className={cn(\"relative overflow-hidden rounded-[1.9rem] bg-surface-0\", screen.phone)}>\n {children}\n <span aria-hidden className=\"absolute left-1/2 top-2 h-5 w-20 -translate-x-1/2 rounded-full bg-surface-3\" />\n </div>\n </div>\n );\n }\n return (\n <div data-device={kind} className={cn(\"relative w-full overflow-hidden rounded-xl border border-border-default bg-surface-1 shadow-2xl\", className)} {...props}>\n <div className=\"flex h-8 items-center gap-1.5 border-b border-border-subtle bg-surface-2 px-3\">\n <span aria-hidden className=\"h-2.5 w-2.5 rounded-full bg-danger/80\" />\n <span aria-hidden className=\"h-2.5 w-2.5 rounded-full bg-warning/80\" />\n <span aria-hidden className=\"h-2.5 w-2.5 rounded-full bg-success/80\" />\n {title &&\n (kind === \"browser\" ? (\n <span className=\"mx-auto max-w-[60%] truncate rounded-md bg-surface-1 px-3 py-0.5 font-mono text-[11px] text-fg-secondary\">{title}</span>\n ) : (\n <span className=\"mx-auto truncate text-xs font-semibold text-fg-secondary\">{title}</span>\n ))}\n </div>\n <div className={cn(\"relative overflow-hidden bg-surface-0\", screen[kind])}>{children}</div>\n </div>\n );\n}\n","// Client (timers through the video, observers, swipe). A guided sequence in one device frame: the steps listed beside\n// it (a vertical tab list: number · title · one line, a bar filling under the current one), the frame playing the\n// step's recording muted, then moving on to the next; the recordings crossfade. It plays only while in view, holds on\n// hover and focus, keeps a visible play / pause button, and never shows a video's native controls. Recordings load\n// lazily (posters first, the current step's video once the block has been seen). Reduced motion: posters, manual steps.\n// Below `md` the list becomes dots under the frame, the frame swipes, the current step's words show under it.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { DeviceFrame, type DeviceKind } from \"./device-frame\";\n\nconst FILL_CSS = \".kz-step-fill{transform-origin:left;animation:kz-step-fill var(--kz-step-ms,6000ms) linear forwards}@keyframes kz-step-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}\";\nconst REDUCED = \"(prefers-reduced-motion: reduce)\";\nconst noop = () => () => undefined;\nconst readReduced = () => Boolean(typeof window !== \"undefined\" && window.matchMedia?.(REDUCED).matches);\nconst subscribeReduced = (notify: () => void) => {\n const q = window.matchMedia?.(REDUCED);\n q?.addEventListener?.(\"change\", notify);\n return () => q?.removeEventListener?.(\"change\", notify);\n};\n\nexport interface ShowcaseStep {\n /** A stable key. */\n id: string;\n /** The step's name (“Make an image”), passed translated. */\n title: string;\n /** One line under it. */\n description: string;\n /** The still shown first, and the only picture under reduced motion. */\n poster: string;\n /** What the poster shows, for assistive technology. */\n alt: string;\n /** The recording: WebM and / or MP4. Without one, the poster stays `duration` ms. */\n video?: { webm?: string; mp4?: string };\n /** Milliseconds a step without a video stays. @default 6000 */\n duration?: number;\n}\n\nexport interface StepShowcaseLabels {\n /** The button's name while paused. */\n play: string;\n /** The button's name while it plays. */\n pause: string;\n /** A step's position in words: `(2, 4) => \"Step 2 of 4\"`. */\n step: (n: number, total: number) => string;\n}\n\nexport const stepShowcaseLabels: StepShowcaseLabels = {\n play: \"Play the steps\",\n pause: \"Pause the steps\",\n step: (n, total) => `Step ${n} of ${total}`,\n};\n\nexport interface StepShowcaseProps extends Omit<React.ComponentProps<\"section\">, \"children\"> {\n /** The sequence's accessible name (“Orazaka in your pocket”), passed translated. */\n label: string;\n /** The steps, in the order of the journey. */\n steps: readonly ShowcaseStep[];\n /** The device the recordings were made on. */\n device: DeviceKind;\n /** The browser's address or the window's title. */\n deviceTitle?: string;\n /** Moves on by itself (never under reduced motion). @default true */\n autoplay?: boolean;\n /** The controls' words, translated. */\n labels?: StepShowcaseLabels;\n /** The step shown first. @default 0 */\n defaultIndex?: number;\n /** Called when the step changes — `user` is true when the visitor asked for it. */\n onIndexChange?: (index: number, user: boolean) => void;\n}\n\n/** One device frame, its steps beside it, playing each recording then the next — a product's guided tour. */\nexport function StepShowcase({ label, steps, device, deviceTitle, autoplay = true, labels = stepShowcaseLabels, defaultIndex = 0, onIndexChange, className, ...props }: StepShowcaseProps) {\n const total = steps.length;\n const id = React.useId();\n const root = React.useRef<HTMLElement>(null);\n const video = React.useRef<HTMLVideoElement>(null);\n const bar = React.useRef<HTMLSpanElement>(null);\n const swipe = React.useRef<number | null>(null);\n const [index, setIndex] = React.useState(defaultIndex);\n const [paused, setPaused] = React.useState(false);\n const [hold, setHold] = React.useState({ hover: false, focus: false });\n const [inView, setInView] = React.useState(false);\n const [seen, setSeen] = React.useState(false);\n const reduced = React.useSyncExternalStore(subscribeReduced, readReduced, () => false);\n const mounted = React.useSyncExternalStore(noop, () => true, () => false);\n const current = steps[Math.min(index, total - 1)];\n const auto = autoplay && !reduced && mounted;\n const running = auto && !paused && !hold.hover && !hold.focus && inView;\n\n React.useEffect(() => {\n const host = root.current;\n if (!host || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver((e) => {\n const visible = e.some((x) => x.isIntersecting);\n setInView(visible);\n if (visible) setSeen(true);\n }, { threshold: 0.35 });\n io.observe(host);\n return () => io.disconnect();\n }, []);\n\n const go = React.useCallback(\n (to: number, user: boolean) => {\n const next = (to + total) % total;\n setIndex(next);\n if (bar.current) bar.current.style.transform = \"scaleX(0)\";\n onIndexChange?.(next, user);\n },\n [total, onIndexChange],\n );\n\n // The recording runs only while the sequence does; its time is the bar.\n React.useEffect(() => {\n const v = video.current;\n if (!v) return;\n if (running) v.play()?.catch?.(() => undefined);\n else v.pause();\n }, [running, index, seen]);\n\n const playable = Boolean(current?.video) && seen && !reduced;\n const onTime = (e: React.SyntheticEvent<HTMLVideoElement>) => {\n const v = e.currentTarget;\n if (bar.current && v.duration) bar.current.style.transform = `scaleX(${v.currentTime / v.duration})`;\n };\n\n return (\n <section\n ref={root}\n aria-roledescription=\"carousel\"\n aria-label={label}\n className={cn(\"grid items-center gap-6 md:grid-cols-[minmax(0,20rem)_minmax(0,1fr)] md:gap-10\", className)}\n onPointerEnter={() => setHold((h) => ({ ...h, hover: true }))}\n onPointerLeave={() => setHold((h) => ({ ...h, hover: false }))}\n onFocus={() => setHold((h) => ({ ...h, focus: true }))}\n onBlur={(e) => {\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) setHold((h) => ({ ...h, focus: false }));\n }}\n {...props}\n >\n <style href=\"kz-step-showcase\" precedence=\"medium\">{FILL_CSS}</style>\n <div className=\"order-2 grid gap-3 md:order-1\">\n <div role=\"tablist\" aria-orientation=\"vertical\" aria-label={label} className=\"flex justify-center gap-2 md:grid md:gap-1\">\n {steps.map((s, i) => {\n const active = i === index;\n return (\n <button\n key={s.id}\n type=\"button\"\n role=\"tab\"\n id={`${id}-tab-${i}`}\n aria-selected={active}\n aria-controls={`${id}-panel`}\n tabIndex={active ? 0 : -1}\n onClick={() => go(i, true)}\n onKeyDown={(e) => {\n if (e.key === \"ArrowDown\" || e.key === \"ArrowRight\") go(i + 1, true);\n else if (e.key === \"ArrowUp\" || e.key === \"ArrowLeft\") go(i - 1, true);\n else return;\n e.preventDefault();\n }}\n className={cn(\n \"group relative rounded-xl text-left outline-hidden transition-colors focus-visible:ring-2 focus-visible:ring-ring\",\n \"max-md:h-2.5 max-md:w-2.5 max-md:rounded-full max-md:bg-border-strong max-md:aria-selected:w-6 max-md:aria-selected:bg-accent\",\n \"md:overflow-hidden md:p-4 md:hover:bg-surface-2 md:aria-selected:bg-surface-2\",\n )}\n >\n <span className=\"max-md:sr-only\">\n <span className=\"flex items-baseline gap-3\">\n <span className={cn(\"font-mono text-xs\", active ? \"text-fg-accent\" : \"text-fg-secondary\")}>{String(i + 1).padStart(2, \"0\")}</span>\n <span className={cn(\"text-sm font-semibold\", active ? \"text-fg\" : \"text-fg-secondary\")}>{s.title}</span>\n </span>\n <span className=\"mt-1 block pl-8 text-xs leading-relaxed text-fg-secondary\">{s.description}</span>\n </span>\n {active && auto && (\n <span aria-hidden className=\"absolute inset-x-4 bottom-0 hidden h-0.5 overflow-hidden rounded-full bg-border-subtle md:block\">\n <span\n key={`${s.id}-${playable}`}\n ref={bar}\n data-fill\n className={cn(\"block h-full bg-accent\", !playable && \"kz-step-fill\")}\n style={{ transform: playable ? \"scaleX(0)\" : undefined, animationPlayState: running ? \"running\" : \"paused\", [\"--kz-step-ms\" as string]: `${s.duration ?? 6000}ms` }}\n onAnimationEnd={() => go(index + 1, false)}\n />\n </span>\n )}\n </button>\n );\n })}\n </div>\n <p aria-hidden className=\"text-center text-sm md:hidden\">\n <span className=\"font-semibold text-fg\">{current?.title}</span> <span className=\"text-fg-secondary\">{current?.description}</span>\n </p>\n {auto && (\n <button type=\"button\" onClick={() => setPaused((p) => !p)} aria-pressed={paused} className=\"mx-auto inline-flex h-8 items-center gap-2 rounded-full border border-border-subtle px-3 text-xs font-medium text-fg-secondary hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring md:mx-0 md:w-fit\">\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"currentColor\" aria-hidden>\n {paused ? <path d=\"M8 5v14l11-7z\" /> : <path d=\"M7 5h4v14H7zM13 5h4v14h-4z\" />}\n </svg>\n {paused ? labels.play : labels.pause}\n </button>\n )}\n </div>\n\n <div\n id={`${id}-panel`}\n role=\"tabpanel\"\n aria-roledescription=\"slide\"\n aria-labelledby={`${id}-tab-${index}`}\n className=\"order-1 touch-pan-y md:order-2\"\n onPointerDown={(e) => {\n swipe.current = e.clientX;\n }}\n onPointerUp={(e) => {\n if (swipe.current === null) return;\n const dx = e.clientX - swipe.current;\n swipe.current = null;\n if (Math.abs(dx) > 40) go(index + (dx < 0 ? 1 : -1), true);\n }}\n >\n <DeviceFrame kind={device} title={deviceTitle}>\n {steps.map((s, i) => {\n const active = i === index;\n return (\n <div key={s.id} data-step={s.id} aria-hidden={!active} className={cn(\"absolute inset-0 transition-opacity duration-500 motion-reduce:transition-none\", active ? \"opacity-100\" : \"opacity-0\")}>\n <img src={s.poster} alt={active ? s.alt : \"\"} loading={i === 0 ? \"eager\" : \"lazy\"} decoding=\"async\" className=\"h-full w-full object-cover\" />\n {active && playable && s.video && (\n <video\n ref={video}\n key={s.id}\n muted\n playsInline\n preload=\"metadata\"\n poster={s.poster}\n aria-hidden\n tabIndex={-1}\n onTimeUpdate={onTime}\n onEnded={() => go(index + 1, false)}\n className=\"absolute inset-0 h-full w-full object-cover\"\n >\n {s.video.webm && <source src={s.video.webm} type=\"video/webm\" />}\n {s.video.mp4 && <source src={s.video.mp4} type=\"video/mp4\" />}\n </video>\n )}\n </div>\n );\n })}\n </DeviceFrame>\n <span className=\"sr-only\" aria-live=\"off\">\n {labels.step(index + 1, total)}\n </span>\n </div>\n </section>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.22.0",
|
|
4
4
|
"description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia, Yasakia, Yororis) and the motion signature — web and /native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"krizaka",
|
|
@@ -241,6 +241,14 @@
|
|
|
241
241
|
"types": "./dist/marquee.d.ts",
|
|
242
242
|
"import": "./dist/marquee.js"
|
|
243
243
|
},
|
|
244
|
+
"./comparison-table": {
|
|
245
|
+
"types": "./dist/comparison-table.d.ts",
|
|
246
|
+
"import": "./dist/comparison-table.js"
|
|
247
|
+
},
|
|
248
|
+
"./step-showcase": {
|
|
249
|
+
"types": "./dist/step-showcase.d.ts",
|
|
250
|
+
"import": "./dist/step-showcase.js"
|
|
251
|
+
},
|
|
244
252
|
"./showcase": {
|
|
245
253
|
"types": "./dist/showcase.d.ts",
|
|
246
254
|
"import": "./dist/showcase.js"
|
|
@@ -261,6 +269,10 @@
|
|
|
261
269
|
"types": "./dist/data-table.d.ts",
|
|
262
270
|
"import": "./dist/data-table.js"
|
|
263
271
|
},
|
|
272
|
+
"./genui": {
|
|
273
|
+
"types": "./dist/genui.d.ts",
|
|
274
|
+
"import": "./dist/genui.js"
|
|
275
|
+
},
|
|
264
276
|
"./virtual": {
|
|
265
277
|
"types": "./dist/virtual.d.ts",
|
|
266
278
|
"import": "./dist/virtual.js"
|
|
@@ -345,6 +357,10 @@
|
|
|
345
357
|
"types": "./dist/description-list.d.ts",
|
|
346
358
|
"import": "./dist/description-list.js"
|
|
347
359
|
},
|
|
360
|
+
"./meta-row": {
|
|
361
|
+
"types": "./dist/meta-row.d.ts",
|
|
362
|
+
"import": "./dist/meta-row.js"
|
|
363
|
+
},
|
|
348
364
|
"./activity-feed": {
|
|
349
365
|
"types": "./dist/activity-feed.d.ts",
|
|
350
366
|
"import": "./dist/activity-feed.js"
|
|
@@ -641,7 +657,7 @@
|
|
|
641
657
|
],
|
|
642
658
|
"peerDependencies": {
|
|
643
659
|
"@krizaka/query": ">=0.2.1",
|
|
644
|
-
"@krizaka/tailwind": ">=3.
|
|
660
|
+
"@krizaka/tailwind": ">=3.22.0",
|
|
645
661
|
"@maplibre/maplibre-react-native": ">=11",
|
|
646
662
|
"@shopify/flash-list": ">=2",
|
|
647
663
|
"expo-camera": ">=16",
|
|
@@ -764,7 +780,7 @@
|
|
|
764
780
|
{
|
|
765
781
|
"name": "./native (bundled into the app by Metro, never downloaded: a regression guard, not a budget)",
|
|
766
782
|
"path": "dist/native.js",
|
|
767
|
-
"limit": "
|
|
783
|
+
"limit": "58 kB",
|
|
768
784
|
"gzip": true
|
|
769
785
|
},
|
|
770
786
|
{
|
|
@@ -1205,6 +1221,26 @@
|
|
|
1205
1221
|
"clsx"
|
|
1206
1222
|
]
|
|
1207
1223
|
},
|
|
1224
|
+
{
|
|
1225
|
+
"name": "./comparison-table",
|
|
1226
|
+
"path": "dist/comparison-table.js",
|
|
1227
|
+
"limit": "2 kB",
|
|
1228
|
+
"gzip": true,
|
|
1229
|
+
"ignore": [
|
|
1230
|
+
"tailwind-variants",
|
|
1231
|
+
"clsx"
|
|
1232
|
+
]
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
"name": "./step-showcase",
|
|
1236
|
+
"path": "dist/step-showcase.js",
|
|
1237
|
+
"limit": "3.2 kB",
|
|
1238
|
+
"gzip": true,
|
|
1239
|
+
"ignore": [
|
|
1240
|
+
"tailwind-variants",
|
|
1241
|
+
"clsx"
|
|
1242
|
+
]
|
|
1243
|
+
},
|
|
1208
1244
|
{
|
|
1209
1245
|
"name": "./showcase",
|
|
1210
1246
|
"path": "dist/showcase.js",
|
|
@@ -1457,6 +1493,16 @@
|
|
|
1457
1493
|
"clsx"
|
|
1458
1494
|
]
|
|
1459
1495
|
},
|
|
1496
|
+
{
|
|
1497
|
+
"name": "./meta-row",
|
|
1498
|
+
"path": "dist/meta-row.js",
|
|
1499
|
+
"limit": "1 kB",
|
|
1500
|
+
"gzip": true,
|
|
1501
|
+
"ignore": [
|
|
1502
|
+
"tailwind-variants",
|
|
1503
|
+
"clsx"
|
|
1504
|
+
]
|
|
1505
|
+
},
|
|
1460
1506
|
{
|
|
1461
1507
|
"name": "./activity-feed",
|
|
1462
1508
|
"path": "dist/activity-feed.js",
|
|
@@ -2168,6 +2214,17 @@
|
|
|
2168
2214
|
"tailwind-variants",
|
|
2169
2215
|
"clsx"
|
|
2170
2216
|
]
|
|
2217
|
+
},
|
|
2218
|
+
{
|
|
2219
|
+
"name": "./genui: GenUiStream (A2UI v0.9 validator, store, surface, base catalog with DataTable)",
|
|
2220
|
+
"path": "dist/genui.js",
|
|
2221
|
+
"import": "{ GenUiStream, baseGenUiCatalog }",
|
|
2222
|
+
"limit": "50 kB",
|
|
2223
|
+
"gzip": true,
|
|
2224
|
+
"ignore": [
|
|
2225
|
+
"tailwind-variants",
|
|
2226
|
+
"clsx"
|
|
2227
|
+
]
|
|
2171
2228
|
}
|
|
2172
2229
|
],
|
|
2173
2230
|
"dependencies": {
|
|
@@ -2186,7 +2243,7 @@
|
|
|
2186
2243
|
"tailwind-variants": "^3.3.1",
|
|
2187
2244
|
"@krizaka/intl": "^0.2.1",
|
|
2188
2245
|
"@krizaka/pacer": "^0.1.0",
|
|
2189
|
-
"@krizaka/tokens": "^3.
|
|
2246
|
+
"@krizaka/tokens": "^3.22.0"
|
|
2190
2247
|
},
|
|
2191
2248
|
"scripts": {
|
|
2192
2249
|
"build": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});for(const f of fs.readdirSync('registry/examples',{recursive:true}))if(/\\.(js|d\\.ts)$/.test(f))fs.rmSync('registry/examples/'+f)\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css');require('node:fs').copyFileSync('src/blocks.css','dist/blocks.css');require('node:fs').copyFileSync('src/story.css','dist/story.css')\" && node scripts/build-registry.mjs",
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
},
|
|
209
209
|
{
|
|
210
210
|
"path": "meta.ts",
|
|
211
|
-
"content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"splash-handoff\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"update-prompt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
|
|
211
|
+
"content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which ui.krizaka.com (apps/site) renders as\n// ui.krizaka.com/components/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"action-sheet\"\n | \"activity-feed\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"chat\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"comparison-table\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"description-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"document-viewer\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"fab\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"force-update-screen\"\n | \"form\"\n | \"gallery-page\"\n | \"genui\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"header\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"image-viewer\"\n | \"iso\"\n | \"kanban\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"maintenance-screen\"\n | \"map\"\n | \"markdown\"\n | \"meta-row\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"match-dialog\"\n | \"media-capture\"\n | \"media-grid\"\n | \"media-picker\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"onboarding\"\n | \"otp-input\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"permission-prompt\"\n | \"photo-grid\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"pull-to-refresh\"\n | \"radio-group\"\n | \"recipes\"\n | \"screen\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"showcase\"\n | \"step-showcase\"\n | \"site-footer\"\n | \"sites\"\n | \"skeleton\"\n | \"skip-link\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"splash-handoff\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"step-progress\"\n | \"stepper\"\n | \"swipe-deck\"\n | \"swipeable-row\"\n | \"switch\"\n | \"tab-bar\"\n | \"tabs\"\n | \"tag-input\"\n | \"tag-selector\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"tree-view\"\n | \"txt\"\n | \"update-prompt\"\n | \"verified-badge\"\n | \"vertical-video\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/**\n * Why a component is not on a platform — a decision, never a silence (the parity matrix of ui.krizaka.com/platforms):\n * - `gap` (✗): it belongs there and is not built yet; `issue` is the issue that tracks it (`krizaka/krizaka-ui#275`);\n * - `n/a`: it is not meant for that platform (a site's page block on a phone, a swipe on a desktop); `use` names the\n * component that answers there, when one does.\n */\nexport type Absence =\n | { readonly status: \"gap\"; readonly issue: string; readonly reason: string; readonly use?: undefined }\n | { readonly status: \"n/a\"; readonly reason: string; readonly use?: ComponentName; readonly issue?: undefined };\n\n/**\n * Desktop is the Orazaka Mac app (Tauri 2: the system WebView renders the web components — ADR-002). `web`: the web\n * component runs there as it is; `desktop`: it also has a desktop form (a window's title bar, a denser layout…), said in\n * its documentation; otherwise the absence and why.\n */\nexport type DesktopSupport = \"web\" | \"desktop\" | Absence;\n\n/**\n * The three platforms of Krizaka UI — web · native (React Native, Expo) · desktop — for one component. The side a\n * component has is `platforms`; the side it lacks says why here: `native` is required for (and only for) a web-only\n * component, `web` for a native-only one; `desktop` always. `scripts/registry.test.mjs` fails on a missing decision.\n */\nexport interface Parity {\n readonly web?: Absence;\n readonly native?: Absence;\n readonly desktop: DesktopSupport;\n}\n\n/**\n * `stable`: its API only changes in a major. `beta`: it may still change in a minor. `deprecated`: no product uses it and\n * no work plans to — it still works, says what replaces it, and goes in the major named by `deprecated.removedIn`.\n */\nexport type Status = \"stable\" | \"beta\" | \"deprecated\";\n\n/** What a deprecated component says (required when `status` is `deprecated`, absent otherwise). */\nexport interface Deprecation {\n /** The version that deprecated it (`3.6.0`). */\n readonly since: string;\n /** The major that removes it (`4.0.0`). */\n readonly removedIn: string;\n /** What replaces it, when a component does. */\n readonly use?: ComponentName;\n /** Why it goes: no product uses it, no planned work needs it. */\n readonly reason: string;\n}\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/** Why a reader needs a template: the seven intents the templates are grouped by (sidebar sub-headings, catalogue filter). */\nexport type TemplatePurpose = \"convince\" | \"explain\" | \"show\" | \"act\" | \"operate\" | \"converse\" | \"recover\";\n\n/** The groups of templates, in reading order: the title, and the one line that says when to come to that group. */\nexport const templatePurposes = {\n convince: { title: \"Convince\", useWhen: \"You come here when a visitor must decide: a landing, a product, a price, a comparison, a proof.\" },\n explain: { title: \"Explain\", useWhen: \"You come here when a reader must understand: how it works, a documentation hub or article, a changelog, a FAQ, a policy.\" },\n show: { title: \"Show\", useWhen: \"You come here when the work itself is the argument: a gallery, a story, a case study, an object and its details.\" },\n act: { title: \"Act\", useWhen: \"You come here when a person must do something once: reach the team, sign in or up, set up, pay.\" },\n operate: { title: \"Operate\", useWhen: \"You come here when a person works every day: a dashboard, a console with a table, settings, an app shell.\" },\n converse: { title: \"Converse\", useWhen: \"You come here when people talk: an inbox, a chat, comments, a notifications centre.\" },\n recover: { title: \"Recover\", useWhen: \"You come here when the way is blocked: an empty list, an error, a missing page, maintenance, a product still being built.\" },\n} as const satisfies Record<TemplatePurpose, { readonly title: string; readonly useWhen: string }>;\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\ninterface ExampleBase {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n}\n\n/** A plain example, or a template: a whole screen with its intent (`purpose`) and the user goal its layout serves (`why`). */\nexport type Example =\n | (ExampleBase & { readonly template?: false; readonly purpose?: undefined; readonly why?: undefined })\n | (ExampleBase & {\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its\n * phone version thought out), copied as a starting point — at most 160 lines, listed on ui.krizaka.com/templates.\n */\n readonly template: true;\n /** The intent that groups it: why you need this block. */\n readonly purpose: TemplatePurpose;\n /** Why this layout: the user goal it serves, in one sentence. */\n readonly why: string;\n });\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n /** Required for, and only for, a `deprecated` component. */\n readonly deprecated?: Deprecation;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** The platforms it is not on, and why; and its desktop form (see `Parity`). */\n readonly parity: Parity;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
|
|
212
212
|
},
|
|
213
213
|
{
|
|
214
214
|
"path": "story-variant.ts",
|