@krizaka/ui 3.20.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/action-bar.js +4 -4
- package/dist/app-shell.js +1 -1
- package/dist/argument-card.js +4 -4
- package/dist/blocks.css +1 -1
- package/dist/button.js +3 -2
- package/dist/card.js +3 -3
- package/dist/{chunk-5MH64NIL.js → chunk-2L67YTNV.js} +2 -2
- package/dist/{chunk-2NB27LM7.js → chunk-3IN6TOPM.js} +4 -4
- package/dist/{chunk-VDR2EP6K.js → chunk-4QN5YJ3D.js} +17 -5
- package/dist/chunk-4QN5YJ3D.js.map +1 -0
- 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-TDG7ED2W.js +1 -0
- package/dist/chunk-TDG7ED2W.js.map +1 -0
- package/dist/{chunk-LIPK4UCS.js → chunk-YCIBJ6OQ.js} +140 -209
- package/dist/chunk-YCIBJ6OQ.js.map +1 -0
- package/dist/code-block.js +17 -5
- package/dist/code-block.js.map +1 -1
- 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 +5 -5
- package/dist/date-picker.js.map +1 -1
- package/dist/force-update-screen.d.ts +80 -0
- package/dist/force-update-screen.js +38 -0
- package/dist/force-update-screen.js.map +1 -0
- 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/maintenance-screen.d.ts +68 -0
- package/dist/maintenance-screen.js +35 -0
- package/dist/maintenance-screen.js.map +1 -0
- package/dist/marketing-layout.js +1 -1
- package/dist/marketing-layout.js.map +1 -1
- package/dist/media-grid.js +44 -152
- 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 +92 -16
- package/dist/native.js +873 -814
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.d.ts +30 -7
- package/dist/nav-bar.js +71 -17
- package/dist/nav-bar.js.map +1 -1
- package/dist/page-hero.js +5 -5
- package/dist/radio-group.js +1 -1
- package/dist/settings-layout.js +1 -1
- package/dist/settings-layout.js.map +1 -1
- 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/dist/tabs.js +2 -2
- package/dist/tabs.js.map +1 -1
- package/dist/update-prompt.d.ts +86 -0
- package/dist/update-prompt.js +41 -0
- package/dist/update-prompt.js.map +1 -0
- package/dist/video-player.d.ts +1 -1
- package/dist/video-player.js +16 -3
- package/dist/video-player.js.map +1 -1
- package/package.json +108 -9
- 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 +3 -3
- package/registry/decision-list.json +1 -1
- package/registry/description-list.json +1 -1
- package/registry/detail-layout.json +1 -1
- package/registry/dialog.json +1 -1
- package/registry/docs-article-page.json +1 -1
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/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/force-update-screen/default.d.ts +4 -0
- package/registry/examples/force-update-screen/default.js +25 -0
- package/registry/examples/force-update-screen/default.tsx +19 -0
- package/registry/examples/force-update-screen/native/default.d.ts +4 -0
- package/registry/examples/force-update-screen/native/default.dark.png +0 -0
- package/registry/examples/force-update-screen/native/default.js +24 -0
- package/registry/examples/force-update-screen/native/default.light.png +0 -0
- package/registry/examples/force-update-screen/native/default.tsx +18 -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/maintenance-screen/default.d.ts +4 -0
- package/registry/examples/maintenance-screen/default.js +22 -0
- package/registry/examples/maintenance-screen/default.tsx +16 -0
- package/registry/examples/maintenance-screen/native/default.d.ts +4 -0
- package/registry/examples/maintenance-screen/native/default.dark.png +0 -0
- package/registry/examples/maintenance-screen/native/default.js +21 -0
- package/registry/examples/maintenance-screen/native/default.light.png +0 -0
- package/registry/examples/maintenance-screen/native/default.tsx +15 -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/nav-bar/family-product.js +16 -13
- package/registry/examples/nav-bar/family-product.tsx +19 -19
- 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/examples/update-prompt/default.d.ts +4 -0
- package/registry/examples/update-prompt/default.js +24 -0
- package/registry/examples/update-prompt/default.tsx +18 -0
- package/registry/examples/update-prompt/native/default.d.ts +4 -0
- package/registry/examples/update-prompt/native/default.dark.png +0 -0
- package/registry/examples/update-prompt/native/default.js +23 -0
- package/registry/examples/update-prompt/native/default.light.png +0 -0
- package/registry/examples/update-prompt/native/default.tsx +17 -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 +323 -0
- 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 +223 -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 +259 -0
- 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 +2 -2
- package/registry/meta-row.json +215 -0
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +58 -25
- 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 +2 -2
- 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 +2 -2
- 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 +3 -3
- 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 +316 -0
- package/registry/verified-badge.json +1 -1
- package/registry/vertical-video.json +1 -1
- package/registry/video-player.json +4 -4
- 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-VDR2EP6K.js.map +0 -1
- /package/dist/{chunk-5MH64NIL.js.map → chunk-2L67YTNV.js.map} +0 -0
- /package/dist/{chunk-2NB27LM7.js.map → chunk-3IN6TOPM.js.map} +0 -0
|
@@ -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/dist/tabs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
useSlidingIndicator
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4QN5YJ3D.js";
|
|
5
5
|
|
|
6
6
|
// src/tabs/tabs.tsx
|
|
7
7
|
import { Tabs as TabsPrimitive } from "radix-ui";
|
|
@@ -31,7 +31,7 @@ var tabs = tv({
|
|
|
31
31
|
},
|
|
32
32
|
/* Pills that scroll sideways on a phone: a feed's sections. */
|
|
33
33
|
pills: {
|
|
34
|
-
list: "gap-1.5 overflow-x-auto overscroll-x-contain [scrollbar-width:none]
|
|
34
|
+
list: "gap-1.5 overflow-x-auto overflow-y-hidden overscroll-x-contain pt-1 -mt-1 pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden 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-full before:border before:border-accent before:bg-accent-soft",
|
|
35
35
|
trigger: "h-9 rounded-full border border-border-default bg-surface-1 px-4 text-xs text-fg-secondary hover:border-border-strong hover:text-fg data-[state=active]:border-accent data-[state=active]:bg-accent-soft data-[state=active]:text-fg group-data-[indicator=on]/tabs:data-[state=active]:border-transparent group-data-[indicator=on]/tabs:data-[state=active]:bg-transparent"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/dist/tabs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tabs/tabs.tsx"],"sourcesContent":["// 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\",\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-full 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-full 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 overscroll-x-contain [scrollbar-width:none]
|
|
1
|
+
{"version":3,"sources":["../src/tabs/tabs.tsx"],"sourcesContent":["// 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\",\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-full 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-full 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-full before:border before:border-accent before:bg-accent-soft\",\n trigger:\n \"h-9 rounded-full 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"],"mappings":";;;;;;AAEA,SAAS,QAAQ,qBAAqB;AACtC,YAAY,WAAW;AACvB,SAAS,UAA6B;AA2EhC;AAvEC,IAAM,OAAO,GAAG;AAAA,EACrB,OAAO;AAAA,IACL,MAAM;AAAA;AAAA;AAAA,IAGN,MACE;AAAA,IAGF,SACE;AAAA,IAEF,SAAS;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA;AAAA,MAEP,WAAW;AAAA,QACT,MACE;AAAA,QAKF,SACE;AAAA,MAKJ;AAAA;AAAA,MAEA,WAAW;AAAA,QACT,MACE;AAAA,QAEF,SACE;AAAA,MAGJ;AAAA;AAAA,MAEA,OAAO;AAAA,QACL,MACE;AAAA,QAGF,SACE;AAAA,MAGJ;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,SAAS,YAAY;AAC1C,CAAC;AAKD,IAAM,iBAAuB,oBAAuB,WAAW;AAQxD,SAAS,SAAS,EAAE,UAAU,aAAa,WAAW,GAAG,MAAM,GAAkB;AACtF,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,SAC9B,8BAAC,cAAc,MAAd,EAAmB,gBAAc,SAAS,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO,GAC1G;AAEJ;AAGO,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAoD;AACjG,QAAM,UAAgB,iBAAW,cAAc;AAC/C,QAAM,EAAE,IAAI,IAAI,oBAAoC,EAAE,MAAM,cAAc,QAAQ,gCAAgC,CAAC;AACnH,SAAO,oBAAC,cAAc,MAAd,EAAmB,KAAU,gBAAc,SAAS,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAC3H;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AACvG,QAAM,UAAgB,iBAAW,cAAc;AAC/C,SAAO,oBAAC,cAAc,SAAd,EAAsB,WAAW,KAAK,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAChG;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AACvG,SAAO,oBAAC,cAAc,SAAd,EAAsB,WAAW,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AACrF;AAGO,IAAM,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU,SAAS,aAAa,SAAS,YAAY;","names":[]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
declare const updatePrompt: tailwind_variants.TVReturnType<{
|
|
5
|
+
[key: string]: {
|
|
6
|
+
[key: string]: tailwind_variants.ClassValue | {
|
|
7
|
+
mark?: tailwind_variants.ClassValue;
|
|
8
|
+
title?: tailwind_variants.ClassValue;
|
|
9
|
+
version?: tailwind_variants.ClassValue;
|
|
10
|
+
content?: tailwind_variants.ClassValue;
|
|
11
|
+
root?: tailwind_variants.ClassValue;
|
|
12
|
+
message?: tailwind_variants.ClassValue;
|
|
13
|
+
actions?: tailwind_variants.ClassValue;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
} | {
|
|
17
|
+
[x: string]: {
|
|
18
|
+
[x: string]: tailwind_variants.ClassValue | {
|
|
19
|
+
mark?: tailwind_variants.ClassValue;
|
|
20
|
+
title?: tailwind_variants.ClassValue;
|
|
21
|
+
version?: tailwind_variants.ClassValue;
|
|
22
|
+
content?: tailwind_variants.ClassValue;
|
|
23
|
+
root?: tailwind_variants.ClassValue;
|
|
24
|
+
message?: tailwind_variants.ClassValue;
|
|
25
|
+
actions?: tailwind_variants.ClassValue;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
} | {}, {
|
|
29
|
+
root: string;
|
|
30
|
+
mark: string;
|
|
31
|
+
content: string;
|
|
32
|
+
title: string;
|
|
33
|
+
message: string;
|
|
34
|
+
version: string;
|
|
35
|
+
actions: string;
|
|
36
|
+
}, undefined, {
|
|
37
|
+
[key: string]: {
|
|
38
|
+
[key: string]: tailwind_variants.ClassValue | {
|
|
39
|
+
mark?: tailwind_variants.ClassValue;
|
|
40
|
+
title?: tailwind_variants.ClassValue;
|
|
41
|
+
version?: tailwind_variants.ClassValue;
|
|
42
|
+
content?: tailwind_variants.ClassValue;
|
|
43
|
+
root?: tailwind_variants.ClassValue;
|
|
44
|
+
message?: tailwind_variants.ClassValue;
|
|
45
|
+
actions?: tailwind_variants.ClassValue;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
} | {}, {
|
|
49
|
+
root: string;
|
|
50
|
+
mark: string;
|
|
51
|
+
content: string;
|
|
52
|
+
title: string;
|
|
53
|
+
message: string;
|
|
54
|
+
version: string;
|
|
55
|
+
actions: string;
|
|
56
|
+
}, tailwind_variants.TVReturnTypeLike<unknown, {
|
|
57
|
+
root: string;
|
|
58
|
+
mark: string;
|
|
59
|
+
content: string;
|
|
60
|
+
title: string;
|
|
61
|
+
message: string;
|
|
62
|
+
version: string;
|
|
63
|
+
actions: string;
|
|
64
|
+
}>>;
|
|
65
|
+
type UpdatePromptProps = Omit<React.ComponentProps<"section">, "title"> & {
|
|
66
|
+
/** The headline (“A new version is available”). */
|
|
67
|
+
title: React.ReactNode;
|
|
68
|
+
/** Why update, in a sentence — the backend's localized `message`. */
|
|
69
|
+
message?: React.ReactNode;
|
|
70
|
+
/** The versions, in words and mono (“1.2.0 → 1.4.0”). */
|
|
71
|
+
versionLabel?: React.ReactNode;
|
|
72
|
+
/** The product's mark (decorative), e.g. `<OrazakaLogo />`. */
|
|
73
|
+
mark?: React.ReactNode;
|
|
74
|
+
/** The button that updates (“Update”). */
|
|
75
|
+
updateLabel: string;
|
|
76
|
+
/** Opens the store page, or reloads the app on the new version. */
|
|
77
|
+
onUpdate: () => void;
|
|
78
|
+
/** The way out (“Later”). */
|
|
79
|
+
laterLabel: string;
|
|
80
|
+
/** Called by the way out: the hook remembers it for this version. */
|
|
81
|
+
onLater: () => void;
|
|
82
|
+
};
|
|
83
|
+
/** The soft update: an invitation that can be dismissed, announced politely. */
|
|
84
|
+
declare function UpdatePrompt({ title, message, versionLabel, mark, updateLabel, onUpdate, laterLabel, onLater, className, ...props }: UpdatePromptProps): React.JSX.Element;
|
|
85
|
+
|
|
86
|
+
export { UpdatePrompt, type UpdatePromptProps, updatePrompt };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import "./chunk-TDG7ED2W.js";
|
|
2
|
+
import {
|
|
3
|
+
Button
|
|
4
|
+
} from "./chunk-3IN6TOPM.js";
|
|
5
|
+
import "./chunk-43PTCDTL.js";
|
|
6
|
+
import "./chunk-2VNYHG4E.js";
|
|
7
|
+
|
|
8
|
+
// src/update-prompt/update-prompt.tsx
|
|
9
|
+
import { tv } from "tailwind-variants";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var updatePrompt = tv({
|
|
12
|
+
slots: {
|
|
13
|
+
root: "flex w-full flex-wrap items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-4 text-sm text-fg",
|
|
14
|
+
mark: "flex h-10 w-10 shrink-0 items-center justify-center [&>svg]:h-10 [&>svg]:w-10",
|
|
15
|
+
content: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
16
|
+
title: "font-semibold text-fg",
|
|
17
|
+
message: "text-fg-secondary",
|
|
18
|
+
version: "font-mono text-xs text-fg-tertiary",
|
|
19
|
+
actions: "flex shrink-0 items-center gap-2"
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
function UpdatePrompt({ title, message, versionLabel, mark, updateLabel, onUpdate, laterLabel, onLater, className, ...props }) {
|
|
23
|
+
const s = updatePrompt();
|
|
24
|
+
return /* @__PURE__ */ jsxs("section", { role: "region", "aria-label": typeof title === "string" ? title : void 0, "data-kind": "update-soft", className: s.root({ className }), ...props, children: [
|
|
25
|
+
mark && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: s.mark(), children: mark }),
|
|
26
|
+
/* @__PURE__ */ jsxs("div", { className: s.content(), children: [
|
|
27
|
+
/* @__PURE__ */ jsx("p", { className: s.title(), children: title }),
|
|
28
|
+
message && /* @__PURE__ */ jsx("p", { className: s.message(), children: message }),
|
|
29
|
+
versionLabel && /* @__PURE__ */ jsx("p", { className: s.version(), children: versionLabel })
|
|
30
|
+
] }),
|
|
31
|
+
/* @__PURE__ */ jsxs("div", { className: s.actions(), children: [
|
|
32
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onLater, children: laterLabel }),
|
|
33
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", shape: "cut", onClick: onUpdate, children: updateLabel })
|
|
34
|
+
] })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
UpdatePrompt,
|
|
39
|
+
updatePrompt
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=update-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/update-prompt/update-prompt.tsx"],"sourcesContent":["// Server-safe. The soft invitation to update: a dismissible banner, in the flow of the page, never a blocker.\n// Every word is a prop (the app passes them translated); the version and the store URL come from `useAppConfig`.\nimport { Button } from \"@krizaka/ui/button\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const updatePrompt = tv({\n slots: {\n root: \"flex w-full flex-wrap items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-4 text-sm text-fg\",\n mark: \"flex h-10 w-10 shrink-0 items-center justify-center [&>svg]:h-10 [&>svg]:w-10\",\n content: \"flex min-w-0 flex-1 flex-col gap-0.5\",\n title: \"font-semibold text-fg\",\n message: \"text-fg-secondary\",\n version: \"font-mono text-xs text-fg-tertiary\",\n actions: \"flex shrink-0 items-center gap-2\",\n },\n});\n\nexport type UpdatePromptProps = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n /** The headline (“A new version is available”). */\n title: React.ReactNode;\n /** Why update, in a sentence — the backend's localized `message`. */\n message?: React.ReactNode;\n /** The versions, in words and mono (“1.2.0 → 1.4.0”). */\n versionLabel?: React.ReactNode;\n /** The product's mark (decorative), e.g. `<OrazakaLogo />`. */\n mark?: React.ReactNode;\n /** The button that updates (“Update”). */\n updateLabel: string;\n /** Opens the store page, or reloads the app on the new version. */\n onUpdate: () => void;\n /** The way out (“Later”). */\n laterLabel: string;\n /** Called by the way out: the hook remembers it for this version. */\n onLater: () => void;\n};\n\n/** The soft update: an invitation that can be dismissed, announced politely. */\nexport function UpdatePrompt({ title, message, versionLabel, mark, updateLabel, onUpdate, laterLabel, onLater, className, ...props }: UpdatePromptProps) {\n const s = updatePrompt();\n return (\n <section role=\"region\" aria-label={typeof title === \"string\" ? title : undefined} data-kind=\"update-soft\" className={s.root({ className })} {...props}>\n {mark && (\n <div aria-hidden className={s.mark()}>\n {mark}\n </div>\n )}\n <div className={s.content()}>\n <p className={s.title()}>{title}</p>\n {message && <p className={s.message()}>{message}</p>}\n {versionLabel && <p className={s.version()}>{versionLabel}</p>}\n </div>\n <div className={s.actions()}>\n <Button variant=\"ghost\" size=\"sm\" onClick={onLater}>\n {laterLabel}\n </Button>\n <Button variant=\"primary\" size=\"sm\" shape=\"cut\" onClick={onUpdate}>\n {updateLabel}\n </Button>\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;AAIA,SAAS,UAAU;AAuCX,cAIF,YAJE;AArCD,IAAM,eAAe,GAAG;AAAA,EAC7B,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF,CAAC;AAsBM,SAAS,aAAa,EAAE,OAAO,SAAS,cAAc,MAAM,aAAa,UAAU,YAAY,SAAS,WAAW,GAAG,MAAM,GAAsB;AACvJ,QAAM,IAAI,aAAa;AACvB,SACE,qBAAC,aAAQ,MAAK,UAAS,cAAY,OAAO,UAAU,WAAW,QAAQ,QAAW,aAAU,eAAc,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAC7I;AAAA,YACC,oBAAC,SAAI,eAAW,MAAC,WAAW,EAAE,KAAK,GAChC,gBACH;AAAA,IAEF,qBAAC,SAAI,WAAW,EAAE,QAAQ,GACxB;AAAA,0BAAC,OAAE,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,MAC/B,WAAW,oBAAC,OAAE,WAAW,EAAE,QAAQ,GAAI,mBAAQ;AAAA,MAC/C,gBAAgB,oBAAC,OAAE,WAAW,EAAE,QAAQ,GAAI,wBAAa;AAAA,OAC5D;AAAA,IACA,qBAAC,SAAI,WAAW,EAAE,QAAQ,GACxB;AAAA,0BAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,SACxC,sBACH;AAAA,MACA,oBAAC,UAAO,SAAQ,WAAU,MAAK,MAAK,OAAM,OAAM,SAAS,UACtD,uBACH;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
package/dist/video-player.d.ts
CHANGED
|
@@ -163,7 +163,7 @@ type VideoPlayerProps = {
|
|
|
163
163
|
title: string;
|
|
164
164
|
/** The picture shown before playback. */
|
|
165
165
|
poster?: string;
|
|
166
|
-
/** Starts playing, muted (browsers refuse sound without a gesture) — not under reduced motion. */
|
|
166
|
+
/** Starts playing, muted (browsers refuse sound without a gesture), once on screen and again each time it comes back — not under reduced motion. */
|
|
167
167
|
autoPlay?: boolean;
|
|
168
168
|
/** Starts muted. */
|
|
169
169
|
muted?: boolean;
|
package/dist/video-player.js
CHANGED
|
@@ -158,11 +158,17 @@ function VideoPlayer({
|
|
|
158
158
|
hls?.destroy();
|
|
159
159
|
};
|
|
160
160
|
}, [url, type]);
|
|
161
|
+
const [inView, setInView] = React.useState(() => !pauseWhenHidden || typeof IntersectionObserver === "undefined");
|
|
161
162
|
React.useEffect(() => {
|
|
162
163
|
const element = video.current;
|
|
163
|
-
if (!element || !url || !
|
|
164
|
+
if (!element || !url || !wanted) return;
|
|
164
165
|
playSafely(element);
|
|
165
|
-
}, [url, wanted
|
|
166
|
+
}, [url, wanted]);
|
|
167
|
+
React.useEffect(() => {
|
|
168
|
+
const element = video.current;
|
|
169
|
+
if (!element || !url || !autoPlay || reduce || !inView) return;
|
|
170
|
+
playSafely(element);
|
|
171
|
+
}, [url, autoPlay, reduce, inView]);
|
|
166
172
|
React.useEffect(() => {
|
|
167
173
|
if (video.current) showTrack(video.current, caption);
|
|
168
174
|
}, [caption, tracks.length]);
|
|
@@ -172,7 +178,14 @@ function VideoPlayer({
|
|
|
172
178
|
return () => document.removeEventListener("fullscreenchange", change);
|
|
173
179
|
}, []);
|
|
174
180
|
React.useEffect(() => () => clearTimeout(idleTimer.current), []);
|
|
175
|
-
useInView(
|
|
181
|
+
useInView(
|
|
182
|
+
root,
|
|
183
|
+
(visible) => {
|
|
184
|
+
setInView(visible);
|
|
185
|
+
if (!visible && video.current && !video.current.paused) video.current.pause();
|
|
186
|
+
},
|
|
187
|
+
pauseWhenHidden
|
|
188
|
+
);
|
|
176
189
|
const play = () => {
|
|
177
190
|
setWanted(true);
|
|
178
191
|
setFailed(false);
|