@krizaka/ui 3.45.0 → 3.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-menu.d.ts +2 -111
- package/dist/account-menu.js +39 -118
- package/dist/account-menu.js.map +1 -1
- package/dist/account-settings.d.ts +163 -0
- package/dist/account-settings.js +354 -0
- package/dist/account-settings.js.map +1 -0
- package/dist/agenda.d.ts +136 -0
- package/dist/agenda.js +90 -0
- package/dist/agenda.js.map +1 -0
- package/dist/avatar-iajODANU.d.ts +114 -0
- package/dist/chunk-3D2YI63L.js +27 -0
- package/dist/chunk-3D2YI63L.js.map +1 -0
- package/dist/chunk-3RVFD3UX.js +85 -0
- package/dist/chunk-3RVFD3UX.js.map +1 -0
- package/dist/chunk-S6NYGOJT.js +22 -0
- package/dist/chunk-S6NYGOJT.js.map +1 -0
- package/dist/docs-layout.js +13 -32
- package/dist/docs-layout.js.map +1 -1
- package/dist/docs-nav.js +1 -1
- package/dist/drop-overlay.d.ts +31 -0
- package/dist/drop-overlay.js +86 -0
- package/dist/drop-overlay.js.map +1 -0
- package/dist/motion.css +27 -0
- package/dist/nav-bar.d.ts +41 -1
- package/dist/nav-bar.js +26 -1
- package/dist/nav-bar.js.map +1 -1
- package/dist/settings-layout.d.ts +59 -4
- package/dist/settings-layout.js +229 -24
- package/dist/settings-layout.js.map +1 -1
- package/dist/upload-queue.d.ts +67 -0
- package/dist/upload-queue.js +76 -0
- package/dist/upload-queue.js.map +1 -0
- package/package.json +31 -5
- package/registry/account-menu.json +2 -2
- package/registry/account-settings.json +721 -0
- package/registry/achievement.json +1 -1
- package/registry/action-bar.json +1 -1
- package/registry/activity-feed.json +1 -1
- package/registry/agenda.json +293 -0
- package/registry/alert.json +1 -1
- package/registry/anatomy-plate.json +1 -1
- package/registry/app-header.json +1 -1
- package/registry/app-shell.json +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/bird-plate.json +1 -1
- package/registry/button.json +1 -1
- package/registry/captcha.json +1 -1
- package/registry/card-grid.json +1 -1
- package/registry/card.json +1 -1
- package/registry/catalogue-page.json +1 -1
- package/registry/changelog-page.json +1 -1
- package/registry/chat.json +3 -3
- package/registry/checkbox.json +1 -1
- package/registry/chip-link.json +1 -1
- package/registry/chip-row.json +1 -1
- package/registry/chip.json +1 -1
- package/registry/choice-grid.json +1 -1
- package/registry/code-block.json +1 -1
- package/registry/combobox.json +1 -1
- package/registry/command.json +1 -1
- package/registry/comparison-table.json +1 -1
- package/registry/component-page.json +1 -1
- package/registry/confirm-button.json +1 -1
- package/registry/contact-page.json +1 -1
- package/registry/continue-prompt.json +1 -1
- package/registry/countdown.json +1 -1
- package/registry/cover-hero.json +1 -1
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +1 -1
- package/registry/data-table.json +3 -3
- package/registry/date-picker.json +1 -1
- package/registry/decision-list.json +1 -1
- package/registry/description-list.json +1 -1
- package/registry/detail-layout.json +1 -1
- package/registry/dialog.json +1 -1
- package/registry/docs-article-page.json +1 -1
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/docs-nav.json +1 -1
- package/registry/document-viewer.json +1 -1
- package/registry/drop-overlay.json +182 -0
- package/registry/dropdown-menu.json +1 -1
- package/registry/elephant.json +2 -2
- package/registry/empty-state.json +1 -1
- package/registry/engine-flow.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/account-settings/avatar-picker.d.ts +4 -0
- package/registry/examples/account-settings/avatar-picker.js +15 -0
- package/registry/examples/account-settings/avatar-picker.tsx +15 -0
- package/registry/examples/account-settings/choice-cards.d.ts +4 -0
- package/registry/examples/account-settings/choice-cards.js +24 -0
- package/registry/examples/account-settings/choice-cards.tsx +20 -0
- package/registry/examples/account-settings/language-picker.d.ts +4 -0
- package/registry/examples/account-settings/language-picker.js +25 -0
- package/registry/examples/account-settings/language-picker.tsx +21 -0
- package/registry/examples/account-settings/patterns.d.ts +4 -0
- package/registry/examples/account-settings/patterns.js +29 -0
- package/registry/examples/account-settings/patterns.tsx +39 -0
- package/registry/examples/account-settings/profile.d.ts +4 -0
- package/registry/examples/account-settings/profile.js +70 -0
- package/registry/examples/account-settings/profile.tsx +80 -0
- package/registry/examples/agenda/day.d.ts +4 -0
- package/registry/examples/agenda/day.js +43 -0
- package/registry/examples/agenda/day.tsx +35 -0
- package/registry/examples/drop-overlay/default.d.ts +4 -0
- package/registry/examples/drop-overlay/default.js +20 -0
- package/registry/examples/drop-overlay/default.tsx +18 -0
- package/registry/examples/upload-queue/default.d.ts +4 -0
- package/registry/examples/upload-queue/default.js +22 -0
- package/registry/examples/upload-queue/default.tsx +20 -0
- package/registry/examples/upload-queue/tray.d.ts +4 -0
- package/registry/examples/upload-queue/tray.js +21 -0
- package/registry/examples/upload-queue/tray.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/flock.json +1 -1
- package/registry/footnote.json +1 -1
- package/registry/force-update-screen.json +1 -1
- package/registry/form.json +3 -3
- package/registry/gallery-page.json +1 -1
- package/registry/genui.json +1 -1
- package/registry/glow-card.json +1 -1
- package/registry/goal-gauge.json +1 -1
- package/registry/how-it-works-page.json +1 -1
- package/registry/image-cropper.json +1 -1
- package/registry/index.json +148 -7
- package/registry/iso.json +1 -1
- package/registry/kanban.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/landing-page.json +1 -1
- package/registry/landscape.json +1 -1
- package/registry/language-switch.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +3 -3
- 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 +3 -3
- package/registry/mascot-state.json +3 -3
- package/registry/mascots.json +3 -3
- package/registry/match-dialog.json +1 -1
- package/registry/media-capture.json +1 -1
- package/registry/media-gallery.json +1 -1
- package/registry/media-grid.json +1 -1
- package/registry/meta-row.json +1 -1
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +93 -5
- package/registry/number-ticker.json +1 -1
- package/registry/opening-hours.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page-index.json +1 -1
- package/registry/page.json +1 -1
- package/registry/pagination.json +1 -1
- package/registry/photo-grid.json +1 -1
- package/registry/pointer-light.json +1 -1
- package/registry/popover.json +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 +93 -5
- package/registry/rail-nav.json +2 -2
- package/registry/recipes.json +1 -1
- package/registry/router.json +1 -1
- package/registry/scroll-stage.json +1 -1
- package/registry/search-field.json +1 -1
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/service-card.json +1 -1
- package/registry/settings-layout.json +34 -4
- package/registry/showcase.json +1 -1
- package/registry/site-footer.json +2 -2
- package/registry/sites.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/skip-link.json +1 -1
- package/registry/slider.json +1 -1
- package/registry/slot-picker.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/specimen-tag.json +1 -1
- package/registry/spinner.json +1 -1
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +1 -1
- package/registry/stat.json +1 -1
- package/registry/status-page.json +1 -1
- package/registry/step-progress.json +1 -1
- package/registry/step-showcase.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/sticky-action.json +1 -1
- package/registry/swipe-deck.json +1 -1
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- package/registry/tag-input.json +3 -3
- 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/upload-queue.json +243 -0
- package/registry/verified-badge.json +1 -1
- package/registry/vertical-video.json +1 -1
- package/registry/video-player.json +1 -1
- package/registry/video-trimmer.json +1 -1
- package/registry/virtual.json +1 -1
- package/registry/waveform.json +1 -1
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckGlyph
|
|
3
|
+
} from "./chunk-3D2YI63L.js";
|
|
4
|
+
import {
|
|
5
|
+
defineRecipe
|
|
6
|
+
} from "./chunk-2VNYHG4E.js";
|
|
7
|
+
import {
|
|
8
|
+
Avatar,
|
|
9
|
+
defaultAvatarUrl
|
|
10
|
+
} from "./chunk-3RVFD3UX.js";
|
|
11
|
+
import {
|
|
12
|
+
cn,
|
|
13
|
+
twMergeConfig
|
|
14
|
+
} from "./chunk-S6NYGOJT.js";
|
|
15
|
+
|
|
16
|
+
// src/badge/badge.tsx
|
|
17
|
+
import { tv } from "tailwind-variants";
|
|
18
|
+
|
|
19
|
+
// src/badge/recipe.ts
|
|
20
|
+
var badgeRecipe = defineRecipe({
|
|
21
|
+
base: "inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm font-semibold ",
|
|
22
|
+
variants: {
|
|
23
|
+
tone: {
|
|
24
|
+
neutral: "bg-surface-3 text-fg-secondary",
|
|
25
|
+
accent: "bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent",
|
|
26
|
+
success: "bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success",
|
|
27
|
+
warning: "bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning",
|
|
28
|
+
danger: "bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger",
|
|
29
|
+
/** On a media: veil and text invariant, identical in both themes. */
|
|
30
|
+
scrim: "bg-scrim text-fg-on-media backdrop-blur-md"
|
|
31
|
+
},
|
|
32
|
+
size: { sm: "px-2 py-0.5 text-[10px]", md: "px-2.5 py-1 text-[11px]" },
|
|
33
|
+
pulse: { true: "[&>[data-dot]]:motion-safe:animate-pulse" }
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: { tone: "neutral", size: "sm" }
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// src/badge/badge.tsx
|
|
39
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
40
|
+
var badgeVariants = tv(badgeRecipe);
|
|
41
|
+
function Badge({ tone, size, pulse, dot, className, children, ...props }) {
|
|
42
|
+
return /* @__PURE__ */ jsxs("span", { "data-tone": tone ?? "neutral", className: badgeVariants({ tone, size, pulse, className }), ...props, children: [
|
|
43
|
+
dot && /* @__PURE__ */ jsx("span", { "data-dot": "", "aria-hidden": true, className: "h-1.5 w-1.5 rounded-full bg-current" }),
|
|
44
|
+
children
|
|
45
|
+
] });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/account-settings/account-profile-card.tsx
|
|
49
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
50
|
+
function AccountProfileCard({ name, email, verified, verifiedLabel, unverifiedLabel, memberSince, avatar, action, compact, className, ...props }) {
|
|
51
|
+
return /* @__PURE__ */ jsxs2("section", { "aria-label": name, className: cn("flex items-center gap-3", !compact && "flex-col items-start gap-4 sm:flex-row sm:gap-5", className), ...props, children: [
|
|
52
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 flex-col items-center gap-2", children: [
|
|
53
|
+
/* @__PURE__ */ jsx2(Avatar, { size: compact ? "md" : "xl", alt: "", fallback: name.slice(0, 1), ...avatar }),
|
|
54
|
+
action
|
|
55
|
+
] }),
|
|
56
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
57
|
+
/* @__PURE__ */ jsx2("h2", { className: cn("m-0 truncate font-bold text-fg", compact ? "text-sm font-medium" : "text-lg"), children: name }),
|
|
58
|
+
/* @__PURE__ */ jsxs2("p", { className: "m-0 flex flex-wrap items-center gap-2 text-sm text-fg-secondary", children: [
|
|
59
|
+
/* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: email }),
|
|
60
|
+
/* @__PURE__ */ jsx2(Badge, { tone: verified ? "success" : "warning", dot: true, children: verified ? verifiedLabel : unverifiedLabel })
|
|
61
|
+
] }),
|
|
62
|
+
memberSince ? /* @__PURE__ */ jsx2("p", { className: "text-sm text-fg-secondary", children: memberSince }) : null
|
|
63
|
+
] })
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/account-settings/avatar-picker.tsx
|
|
68
|
+
import { RadioGroup as RadioGroupPrimitive } from "radix-ui";
|
|
69
|
+
import * as React from "react";
|
|
70
|
+
|
|
71
|
+
// src/account-settings/group-head.tsx
|
|
72
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
73
|
+
function GroupHead({ id, label, description }) {
|
|
74
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-0.5", children: [
|
|
75
|
+
/* @__PURE__ */ jsx3("span", { id, className: "text-sm font-medium text-fg", children: label }),
|
|
76
|
+
description ? /* @__PURE__ */ jsx3("span", { className: "text-sm text-fg-secondary", children: description }) : null
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/account-settings/avatar-picker.tsx
|
|
81
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
82
|
+
var pictureOf = (option) => option.src ?? (option.defaultAvatar ? defaultAvatarUrl(option.defaultAvatar, { size: 256 }) : void 0);
|
|
83
|
+
function AvatarPicker({ label, showLabel, description, dense, options, upload, className, ...props }) {
|
|
84
|
+
const id = React.useId();
|
|
85
|
+
const group = /* @__PURE__ */ jsxs4(RadioGroupPrimitive.Root, { "aria-label": label, "aria-labelledby": showLabel ? id : void 0, className: cn(dense ? "flex flex-wrap gap-2.5" : "grid grid-cols-[repeat(auto-fill,minmax(4.25rem,1fr))] gap-3", !showLabel && className), ...props, children: [
|
|
86
|
+
options.map((option) => /* @__PURE__ */ jsxs4(
|
|
87
|
+
RadioGroupPrimitive.Item,
|
|
88
|
+
{
|
|
89
|
+
value: option.id,
|
|
90
|
+
"aria-label": option.label,
|
|
91
|
+
className: cn(
|
|
92
|
+
"group/avatar relative shrink-0 rounded-full",
|
|
93
|
+
dense ? "h-12 w-12" : "aspect-square w-full",
|
|
94
|
+
" outline-none transition-shadow duration-200 motion-reduce:transition-none",
|
|
95
|
+
"ring-1 ring-border-default hover:ring-border-strong focus-visible:focus-ring data-[state=checked]:ring-2 data-[state=checked]:ring-accent"
|
|
96
|
+
),
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ jsx4("img", { src: pictureOf(option), alt: "", draggable: false, className: "h-full w-full rounded-full bg-surface-3 object-cover" }),
|
|
99
|
+
/* @__PURE__ */ jsx4(
|
|
100
|
+
"span",
|
|
101
|
+
{
|
|
102
|
+
"aria-hidden": true,
|
|
103
|
+
className: "absolute -bottom-0.5 -right-0.5 hidden h-5 w-5 items-center justify-center rounded-full border-2 border-surface-1 bg-accent text-on-accent group-data-[state=checked]/avatar:inline-flex",
|
|
104
|
+
children: /* @__PURE__ */ jsx4(CheckGlyph, { className: "h-3 w-3" })
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
option.id
|
|
110
|
+
)),
|
|
111
|
+
upload ? /* @__PURE__ */ jsx4("div", { className: "flex aspect-square w-full items-center justify-center", children: upload }) : null
|
|
112
|
+
] });
|
|
113
|
+
if (!showLabel) return group;
|
|
114
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("flex flex-col gap-3", className), children: [
|
|
115
|
+
/* @__PURE__ */ jsx4(GroupHead, { id, label, description }),
|
|
116
|
+
group
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/account-settings/choice-cards.tsx
|
|
121
|
+
import * as React4 from "react";
|
|
122
|
+
|
|
123
|
+
// src/radio-group/radio-group.tsx
|
|
124
|
+
import { RadioGroup as RadioGroupPrimitive2 } from "radix-ui";
|
|
125
|
+
|
|
126
|
+
// src/router/route-state.tsx
|
|
127
|
+
import * as React2 from "react";
|
|
128
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
129
|
+
var CHANGE = "kz-route-state";
|
|
130
|
+
var browserAdapter = {
|
|
131
|
+
search: () => typeof window === "undefined" ? "" : window.location.search,
|
|
132
|
+
navigate: (search, { replace }) => {
|
|
133
|
+
const url = `${window.location.pathname}${search}${window.location.hash}`;
|
|
134
|
+
window.history[replace ? "replaceState" : "pushState"](window.history.state, "", url);
|
|
135
|
+
window.dispatchEvent(new Event(CHANGE));
|
|
136
|
+
},
|
|
137
|
+
subscribe: (onChange) => {
|
|
138
|
+
window.addEventListener("popstate", onChange);
|
|
139
|
+
window.addEventListener(CHANGE, onChange);
|
|
140
|
+
return () => {
|
|
141
|
+
window.removeEventListener("popstate", onChange);
|
|
142
|
+
window.removeEventListener(CHANGE, onChange);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
var AdapterContext = React2.createContext(browserAdapter);
|
|
147
|
+
function readRouteValue(search, key, { values, default: fallback }) {
|
|
148
|
+
const raw = new URLSearchParams(search).get(key);
|
|
149
|
+
if (raw === null) return fallback;
|
|
150
|
+
return !values || values.includes(raw) ? raw : fallback;
|
|
151
|
+
}
|
|
152
|
+
function writeRouteValue(search, key, value, fallback) {
|
|
153
|
+
const params = new URLSearchParams(search);
|
|
154
|
+
if (value === fallback) params.delete(key);
|
|
155
|
+
else params.set(key, value);
|
|
156
|
+
const text = params.toString();
|
|
157
|
+
return text ? `?${text}` : "";
|
|
158
|
+
}
|
|
159
|
+
function useRouteState(key, { values, default: fallback, history = "push" }) {
|
|
160
|
+
const adapter = React2.useContext(AdapterContext);
|
|
161
|
+
const search = React2.useSyncExternalStore(adapter.subscribe, adapter.search, () => "");
|
|
162
|
+
const value = key === void 0 ? fallback : readRouteValue(search, key, { values, default: fallback });
|
|
163
|
+
const set = React2.useCallback(
|
|
164
|
+
(next) => {
|
|
165
|
+
if (key === void 0) return;
|
|
166
|
+
adapter.navigate(writeRouteValue(adapter.search(), key, next, fallback), { replace: history === "replace" });
|
|
167
|
+
},
|
|
168
|
+
[adapter, key, fallback, history]
|
|
169
|
+
);
|
|
170
|
+
return [value, set];
|
|
171
|
+
}
|
|
172
|
+
function useRouteBinding(routeKey, routeValues, props) {
|
|
173
|
+
const initial = props.defaultValue ?? props.value ?? routeValues?.[0] ?? "";
|
|
174
|
+
const [value, set] = useRouteState(routeKey, { values: routeValues, default: initial });
|
|
175
|
+
if (routeKey === void 0) return props;
|
|
176
|
+
return {
|
|
177
|
+
...props,
|
|
178
|
+
value,
|
|
179
|
+
defaultValue: void 0,
|
|
180
|
+
onValueChange: (next) => {
|
|
181
|
+
set(next);
|
|
182
|
+
props.onValueChange?.(next);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/sliding-indicator.ts
|
|
188
|
+
import * as React3 from "react";
|
|
189
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? React3.useEffect : React3.useLayoutEffect;
|
|
190
|
+
function useSlidingIndicator({ item, active, prefer, follow = false }) {
|
|
191
|
+
const ref = React3.useRef(null);
|
|
192
|
+
const place = React3.useCallback((target) => {
|
|
193
|
+
const list = ref.current;
|
|
194
|
+
if (!list) return;
|
|
195
|
+
if (!(target instanceof HTMLElement) || !list.contains(target)) {
|
|
196
|
+
list.removeAttribute("data-indicator");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const a = target.getBoundingClientRect();
|
|
200
|
+
const b = list.getBoundingClientRect();
|
|
201
|
+
list.style.setProperty("--kz-ind-x", `${a.left - b.left + list.scrollLeft}px`);
|
|
202
|
+
list.style.setProperty("--kz-ind-y", `${a.top - b.top + list.scrollTop}px`);
|
|
203
|
+
list.style.setProperty("--kz-ind-w", `${a.width}px`);
|
|
204
|
+
list.style.setProperty("--kz-ind-h", `${a.height}px`);
|
|
205
|
+
list.setAttribute("data-indicator", "on");
|
|
206
|
+
}, []);
|
|
207
|
+
const rest = React3.useCallback(
|
|
208
|
+
() => {
|
|
209
|
+
const list = ref.current;
|
|
210
|
+
const hovered = follow ? list?.querySelector(`${item}:hover`) : null;
|
|
211
|
+
place((prefer ? list?.querySelector(prefer) : null) ?? hovered ?? list?.querySelector(active) ?? null);
|
|
212
|
+
},
|
|
213
|
+
[place, active, prefer, item, follow]
|
|
214
|
+
);
|
|
215
|
+
const follow_ = React3.useCallback((target) => {
|
|
216
|
+
const hit = target instanceof Element ? target.closest(item) : null;
|
|
217
|
+
if (hit && ref.current?.contains(hit)) place(hit);
|
|
218
|
+
else rest();
|
|
219
|
+
}, [item, place, rest]);
|
|
220
|
+
useIsoLayoutEffect(() => {
|
|
221
|
+
const list = ref.current;
|
|
222
|
+
if (!list) return;
|
|
223
|
+
rest();
|
|
224
|
+
const resize = new ResizeObserver(rest);
|
|
225
|
+
resize.observe(list);
|
|
226
|
+
const changes = new MutationObserver(rest);
|
|
227
|
+
changes.observe(list, { subtree: true, attributes: true, attributeFilter: ["data-state", "aria-current", "data-active"] });
|
|
228
|
+
return () => {
|
|
229
|
+
resize.disconnect();
|
|
230
|
+
changes.disconnect();
|
|
231
|
+
};
|
|
232
|
+
}, [rest]);
|
|
233
|
+
const handlers = follow ? {
|
|
234
|
+
onPointerOver: (e) => follow_(e.target),
|
|
235
|
+
onPointerLeave: rest,
|
|
236
|
+
onFocus: (e) => follow_(e.target),
|
|
237
|
+
onBlur: rest
|
|
238
|
+
} : {};
|
|
239
|
+
return { ref, handlers };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/tv.ts
|
|
243
|
+
import { createTV } from "tailwind-variants";
|
|
244
|
+
var tv2 = createTV({ twMergeConfig });
|
|
245
|
+
|
|
246
|
+
// src/radio-group/radio-group.tsx
|
|
247
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
248
|
+
var radioGroup = tv2({
|
|
249
|
+
slots: {
|
|
250
|
+
root: "grid gap-2 data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-wrap",
|
|
251
|
+
item: "peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-border-strong bg-surface-1 inset-shadow-well transition-colors focus-visible:outline-hidden focus-visible:focus-ring hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:shadow-glow aria-invalid:border-danger",
|
|
252
|
+
dot: "h-2 w-2 rounded-full bg-on-accent",
|
|
253
|
+
label: "flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-70",
|
|
254
|
+
card: "group relative flex min-w-0 flex-col items-start gap-1 rounded-2xl border border-border-default bg-surface-1 bg-sheen p-4 pr-10 text-left text-sm text-fg inset-shadow-edge transition-[border-color,background-color,box-shadow,transform] duration-200 hover:border-border-strong active:scale-[0.99] motion-reduce:transition-none motion-reduce:active:scale-100 focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger data-[state=checked]:border-accent data-[state=checked]:bg-accent-soft data-[state=checked]:shadow-glow",
|
|
255
|
+
// The card's check: an empty ring at rest, the accent filled with its check once chosen.
|
|
256
|
+
cardCheck: "absolute right-3.5 top-3.5 inline-flex h-5 w-5 items-center justify-center rounded-full border border-border-strong bg-surface-1 text-on-accent transition-colors group-data-[state=checked]:border-accent group-data-[state=checked]:bg-accent",
|
|
257
|
+
// The tactile key: on a ledge (its lower edge), its hairline stronger on hover, pressed in while held and once chosen.
|
|
258
|
+
button: "relative inline-flex min-h-12 min-w-12 select-none flex-col items-center justify-center gap-0.5 rounded-xl border border-border-default bg-surface-2 bg-sheen px-4 py-2 text-sm font-semibold text-fg inset-shadow-edge shadow-[0_4px_0_0_var(--kz-border-strong)] transition-[transform,box-shadow,background-color,border-color,color] duration-100 ease-out motion-reduce:transition-none hover:border-border-strong active:translate-y-[3px] active:shadow-[0_1px_0_0_var(--kz-border-strong)] focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger data-[state=checked]:translate-y-[3px] data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:bg-fill-shade data-[state=checked]:text-on-accent data-[state=checked]:inset-shadow-edge-fill data-[state=checked]:shadow-[0_1px_0_0_var(--kz-accent-hover)]",
|
|
259
|
+
buttonHint: "text-[11px] font-medium text-fg-secondary group-data-[state=checked]:text-on-accent",
|
|
260
|
+
segmented: "relative isolate inline-grid auto-cols-fr grid-flow-col gap-1 rounded-xl border border-border-default bg-surface-2 p-1 inset-shadow-well before:pointer-events-none before:absolute before:left-0 before:top-0 before:-z-10 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:border before:border-border-default before:bg-surface-0 before:bg-sheen before:opacity-0 before:shadow-raised before:transition-[translate,width,height,opacity] before:duration-300 motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100",
|
|
261
|
+
segment: "inline-flex min-h-9 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-lg px-3.5 text-sm font-semibold text-fg-secondary transition-colors hover:text-fg focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 data-[state=checked]:border data-[state=checked]:border-border-default data-[state=checked]:bg-surface-0 data-[state=checked]:text-fg data-[state=checked]:shadow-raised group-data-[indicator=on]/seg:data-[state=checked]:border-transparent group-data-[indicator=on]/seg:data-[state=checked]:bg-transparent group-data-[indicator=on]/seg:data-[state=checked]:shadow-none"
|
|
262
|
+
},
|
|
263
|
+
variants: {
|
|
264
|
+
variant: {
|
|
265
|
+
list: {},
|
|
266
|
+
cards: { root: "grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-3" },
|
|
267
|
+
buttons: { root: "flex flex-wrap gap-2.5 pb-1" },
|
|
268
|
+
segmented: {}
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
defaultVariants: { variant: "list" }
|
|
272
|
+
});
|
|
273
|
+
var s = radioGroup();
|
|
274
|
+
function RadioGroupRoot({ label, invalid, variant = "list", className, routeKey, routeValues, ...rest }) {
|
|
275
|
+
const props = useRouteBinding(routeKey, routeValues, rest);
|
|
276
|
+
const { ref } = useSlidingIndicator({ item: "[role=radio]", active: "[role=radio][data-state=checked]" });
|
|
277
|
+
const segmented = variant === "segmented";
|
|
278
|
+
return /* @__PURE__ */ jsx6(
|
|
279
|
+
RadioGroupPrimitive2.Root,
|
|
280
|
+
{
|
|
281
|
+
ref: segmented ? ref : void 0,
|
|
282
|
+
"aria-label": label,
|
|
283
|
+
"aria-invalid": invalid || void 0,
|
|
284
|
+
"data-invalid": invalid ? "" : void 0,
|
|
285
|
+
"data-variant": variant,
|
|
286
|
+
orientation: segmented ? "horizontal" : props.orientation,
|
|
287
|
+
className: segmented ? s.segmented({ className: ["group/seg", className].filter(Boolean).join(" ") }) : radioGroup({ variant }).root({ className }),
|
|
288
|
+
...props
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
function RadioGroupItem({ className, children, ...props }) {
|
|
293
|
+
const item = /* @__PURE__ */ jsx6(RadioGroupPrimitive2.Item, { className: s.item({ className: children ? "mt-0.5" : className }), ...props, children: /* @__PURE__ */ jsx6(RadioGroupPrimitive2.Indicator, { className: s.dot() }) });
|
|
294
|
+
if (!children) return item;
|
|
295
|
+
return /* @__PURE__ */ jsxs5("label", { className: s.label({ className }), children: [
|
|
296
|
+
item,
|
|
297
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0", children })
|
|
298
|
+
] });
|
|
299
|
+
}
|
|
300
|
+
function RadioGroupCard({ className, children, ...props }) {
|
|
301
|
+
return /* @__PURE__ */ jsxs5(RadioGroupPrimitive2.Item, { className: s.card({ className }), ...props, children: [
|
|
302
|
+
children,
|
|
303
|
+
/* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: s.cardCheck(), children: /* @__PURE__ */ jsx6(RadioGroupPrimitive2.Indicator, { forceMount: true, className: "inline-flex data-[state=unchecked]:hidden", children: /* @__PURE__ */ jsx6(CheckGlyph, { className: "h-3 w-3" }) }) })
|
|
304
|
+
] });
|
|
305
|
+
}
|
|
306
|
+
function RadioGroupButton({ className, children, hint, ...props }) {
|
|
307
|
+
return /* @__PURE__ */ jsxs5(RadioGroupPrimitive2.Item, { className: s.button({ className: ["group", className].filter(Boolean).join(" ") }), ...props, children: [
|
|
308
|
+
/* @__PURE__ */ jsx6("span", { children }),
|
|
309
|
+
hint ? /* @__PURE__ */ jsx6("span", { className: s.buttonHint(), children: hint }) : null
|
|
310
|
+
] });
|
|
311
|
+
}
|
|
312
|
+
function RadioGroupSegment({ className, ...props }) {
|
|
313
|
+
return /* @__PURE__ */ jsx6(RadioGroupPrimitive2.Item, { className: s.segment({ className }), ...props });
|
|
314
|
+
}
|
|
315
|
+
var RadioGroup = { Root: RadioGroupRoot, Item: RadioGroupItem, Card: RadioGroupCard, Button: RadioGroupButton, Segment: RadioGroupSegment };
|
|
316
|
+
|
|
317
|
+
// src/account-settings/choice-cards.tsx
|
|
318
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
319
|
+
function ChoiceCards({ label, showLabel, description, options, className, ...props }) {
|
|
320
|
+
const base = React4.useId();
|
|
321
|
+
const group = /* @__PURE__ */ jsx7(RadioGroup.Root, { variant: "cards", label, "aria-labelledby": showLabel ? `${base}-label` : void 0, className: ["grid grid-cols-1 gap-3 sm:grid-cols-2", !showLabel && className].filter(Boolean).join(" "), ...props, children: options.map((option) => {
|
|
322
|
+
const describedBy = option.description ? `${base}-${option.id}` : void 0;
|
|
323
|
+
return /* @__PURE__ */ jsxs6(RadioGroup.Card, { value: option.id, disabled: option.disabled, "aria-describedby": describedBy, children: [
|
|
324
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-2.5 font-semibold text-fg [&>svg]:h-5 [&>svg]:w-5 [&>svg]:text-accent", children: [
|
|
325
|
+
option.icon,
|
|
326
|
+
option.title
|
|
327
|
+
] }),
|
|
328
|
+
option.description ? /* @__PURE__ */ jsx7("span", { id: describedBy, className: "text-sm font-normal text-fg-secondary", children: option.description }) : null
|
|
329
|
+
] }, option.id);
|
|
330
|
+
}) });
|
|
331
|
+
if (!showLabel) return group;
|
|
332
|
+
return /* @__PURE__ */ jsxs6("div", { className: ["flex flex-col gap-3", className].filter(Boolean).join(" "), children: [
|
|
333
|
+
/* @__PURE__ */ jsx7(GroupHead, { id: `${base}-label`, label, description }),
|
|
334
|
+
group
|
|
335
|
+
] });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/account-settings/language-picker.tsx
|
|
339
|
+
import * as React5 from "react";
|
|
340
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
341
|
+
function LanguagePicker({ label, showLabel = true, description, languages, className, ...props }) {
|
|
342
|
+
const id = React5.useId();
|
|
343
|
+
return /* @__PURE__ */ jsxs7("div", { className: ["flex flex-col gap-2", className].filter(Boolean).join(" "), children: [
|
|
344
|
+
showLabel ? /* @__PURE__ */ jsx8(GroupHead, { id: `${id}-label`, label, description }) : null,
|
|
345
|
+
/* @__PURE__ */ jsx8(RadioGroup.Root, { variant: "segmented", "aria-label": showLabel ? void 0 : String(label), "aria-labelledby": showLabel ? `${id}-label` : void 0, ...props, children: languages.map((language) => /* @__PURE__ */ jsx8(RadioGroup.Segment, { value: language.code, lang: language.code, children: language.name }, language.code)) })
|
|
346
|
+
] });
|
|
347
|
+
}
|
|
348
|
+
export {
|
|
349
|
+
AccountProfileCard,
|
|
350
|
+
AvatarPicker,
|
|
351
|
+
ChoiceCards,
|
|
352
|
+
LanguagePicker
|
|
353
|
+
};
|
|
354
|
+
//# sourceMappingURL=account-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/badge/badge.tsx","../src/badge/recipe.ts","../src/account-settings/account-profile-card.tsx","../src/account-settings/avatar-picker.tsx","../src/account-settings/group-head.tsx","../src/account-settings/choice-cards.tsx","../src/radio-group/radio-group.tsx","../src/router/route-state.tsx","../src/sliding-indicator.ts","../src/tv.ts","../src/account-settings/language-picker.tsx"],"sourcesContent":["// Server-safe: no hook, no context.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { badgeRecipe } from \"./recipe\";\n\n// Status tones are soft (a tint, a coloured dot, a text role): the invariant status tokens are too light to carry\n// small text on a light surface, or to sit under white text (WCAG AA).\nexport const badgeVariants = tv(badgeRecipe);\n\nexport type BadgeVariants = VariantProps<typeof badgeVariants>;\n\nexport type BadgeProps = React.ComponentProps<\"span\"> &\n BadgeVariants & {\n /**\n * neutral · accent · success · warning · danger (soft tints, the text stays a text role) · scrim (on a media).\n * @default \"neutral\"\n */\n tone?: BadgeVariants[\"tone\"];\n /**\n * sm · md.\n * @default \"sm\"\n */\n size?: BadgeVariants[\"size\"];\n /** Makes the `dot` pulse (stopped under reduced motion). */\n pulse?: BadgeVariants[\"pulse\"];\n /** A small dot before the text, in the tone's colour (pulsing with `pulse`). */\n dot?: boolean;\n };\n\nexport function Badge({ tone, size, pulse, dot, className, children, ...props }: BadgeProps) {\n return (\n <span data-tone={tone ?? \"neutral\"} className={badgeVariants({ tone, size, pulse, className })} {...props}>\n {dot && <span data-dot=\"\" aria-hidden className=\"h-1.5 w-1.5 rounded-full bg-current\" />}\n {children}\n </span>\n );\n}\n","// The badge's tones and sizes, written once as classes of the Krizaka tokens: the web wears them with `tv(badgeRecipe)`,\n// React Native reads the same classes into styles (native/badge.tsx). Status tones are soft (a tint, a coloured dot, a\n// text role): the invariant status tokens are too light to carry small text on a light surface, or to sit under white\n// text (WCAG AA).\nimport { defineRecipe } from \"../recipes/define\";\n\nexport const badgeRecipe = defineRecipe({\n base: \"inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm font-semibold \",\n variants: {\n tone: {\n neutral: \"bg-surface-3 text-fg-secondary\",\n accent: \"bg-accent-soft text-fg ring-1 ring-inset ring-accent/40 [&>[data-dot]]:bg-accent\",\n success: \"bg-success/15 text-fg ring-1 ring-inset ring-success/40 [&>[data-dot]]:bg-success\",\n warning: \"bg-warning/15 text-fg ring-1 ring-inset ring-warning/40 [&>[data-dot]]:bg-warning\",\n danger: \"bg-danger/15 text-fg ring-1 ring-inset ring-danger/40 [&>[data-dot]]:bg-danger\",\n /** On a media: veil and text invariant, identical in both themes. */\n scrim: \"bg-scrim text-fg-on-media backdrop-blur-md\",\n },\n size: { sm: \"px-2 py-0.5 text-[10px]\", md: \"px-2.5 py-1 text-[11px]\" },\n pulse: { true: \"[&>[data-dot]]:motion-safe:animate-pulse\" },\n },\n defaultVariants: { tone: \"neutral\", size: \"sm\" },\n});\n","// Server-safe. Who the account is, at a glance: the picture with its change action, the name, the e-mail and whether it is\n// verified, since when. The head of a profile section.\nimport type * as React from \"react\";\n\nimport { Avatar, type AvatarProps } from \"../avatar/avatar\";\nimport { Badge } from \"../badge/badge\";\nimport { cn } from \"../cn\";\n\nexport type AccountProfileCardProps = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n /** The name (the card's heading). */\n name: string;\n /** The e-mail address. */\n email: string;\n /** Whether the address is verified. */\n verified: boolean;\n /** “Verified” — passed translated. */\n verifiedLabel: string;\n /** “Not verified” — passed translated. */\n unverifiedLabel: string;\n /** “Member since October 2026” — formatted by the app, in the viewer's language. */\n memberSince?: string;\n /** The picture: the person's photo, or the default avatar of the brand. */\n avatar?: Pick<AvatarProps, \"src\" | \"defaultAvatar\" | \"fallback\">;\n /** Beside the picture: the change action (a `Button`). */\n action?: React.ReactNode;\n /**\n * One calm line for a settings row: a medium picture beside the name and the e-mail, no wrapping.\n * @default false\n */\n compact?: boolean;\n};\n\n/** The account's identity card: picture, name, verified e-mail, member since. */\nexport function AccountProfileCard({ name, email, verified, verifiedLabel, unverifiedLabel, memberSince, avatar, action, compact, className, ...props }: AccountProfileCardProps) {\n return (\n <section aria-label={name} className={cn(\"flex items-center gap-3\", !compact && \"flex-col items-start gap-4 sm:flex-row sm:gap-5\", className)} {...props}>\n <div className=\"flex shrink-0 flex-col items-center gap-2\">\n <Avatar size={compact ? \"md\" : \"xl\"} alt=\"\" fallback={name.slice(0, 1)} {...avatar} />\n {action}\n </div>\n <div className=\"flex min-w-0 flex-col gap-1\">\n <h2 className={cn(\"m-0 truncate font-bold text-fg\", compact ? \"text-sm font-medium\" : \"text-lg\")}>{name}</h2>\n <p className=\"m-0 flex flex-wrap items-center gap-2 text-sm text-fg-secondary\">\n <span className=\"min-w-0 truncate\">{email}</span>\n <Badge tone={verified ? \"success\" : \"warning\"} dot>\n {verified ? verifiedLabel : unverifiedLabel}\n </Badge>\n </p>\n {memberSince ? <p className=\"text-sm text-fg-secondary\">{memberSince}</p> : null}\n </div>\n </section>\n );\n}\n","// Client (a radio group). The pictures of a person to choose from — the family's default avatars, a few photos — as a grid of\n// round tiles: one named radiogroup, one tab stop, the arrows choose, a ring and a check on the chosen one.\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { type DefaultAvatar, defaultAvatarUrl } from \"../avatar/default-avatar\";\nimport { cn } from \"../cn\";\nimport { CheckGlyph } from \"../glyphs\";\nimport { GroupHead } from \"./group-head\";\n\nexport interface AvatarOption {\n /** The value of the choice. */\n id: string;\n /** Its name for assistive technology (“Teinte 3”) — passed translated. */\n label: string;\n /** A picture's address; otherwise `defaultAvatar` draws one of the family's pictures. */\n src?: string;\n /** One of Krizaka's default pictures (`{ brand, tone }`). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport type AvatarPickerProps = Omit<React.ComponentProps<typeof RadioGroupPrimitive.Root>, \"children\" | \"onChange\" | \"dir\"> & {\n /** The group's accessible name (“Profile picture”) — passed translated. */\n label: string;\n /**\n * Smaller tiles, for a settings row.\n * @default false\n */\n dense?: boolean;\n /** Shows the label above the grid, with `description` under it. */\n showLabel?: boolean;\n /** One line under the visible label: what it changes. */\n description?: React.ReactNode;\n /** The pictures. */\n options: readonly AvatarOption[];\n /** The chosen id (controlled). */\n value?: string;\n /** The viewer chooses another picture. */\n onValueChange?: (id: string) => void;\n /** After the pictures, in the grid: the upload action (a `Button`, a file input's label). Not a radio. */\n upload?: React.ReactNode;\n};\n\nconst pictureOf = (option: AvatarOption) => option.src ?? (option.defaultAvatar ? defaultAvatarUrl(option.defaultAvatar, { size: 256 }) : undefined);\n\n/** A grid of pictures to choose from, with an upload beside them: a radio group, never a row of loose radios. */\nexport function AvatarPicker({ label, showLabel, description, dense, options, upload, className, ...props }: AvatarPickerProps) {\n const id = React.useId();\n const group = (\n <RadioGroupPrimitive.Root aria-label={label} aria-labelledby={showLabel ? id : undefined} className={cn(dense ? \"flex flex-wrap gap-2.5\" : \"grid grid-cols-[repeat(auto-fill,minmax(4.25rem,1fr))] gap-3\", !showLabel && className)} {...props}>\n {options.map((option) => (\n <RadioGroupPrimitive.Item\n key={option.id}\n value={option.id}\n aria-label={option.label}\n className={cn(\n \"group/avatar relative shrink-0 rounded-full\", dense ? \"h-12 w-12\" : \"aspect-square w-full\",\n \" outline-none transition-shadow duration-200 motion-reduce:transition-none\",\n \"ring-1 ring-border-default hover:ring-border-strong focus-visible:focus-ring data-[state=checked]:ring-2 data-[state=checked]:ring-accent\",\n )}\n >\n <img src={pictureOf(option)} alt=\"\" draggable={false} className=\"h-full w-full rounded-full bg-surface-3 object-cover\" />\n <span\n aria-hidden\n className=\"absolute -bottom-0.5 -right-0.5 hidden h-5 w-5 items-center justify-center rounded-full border-2 border-surface-1 bg-accent text-on-accent group-data-[state=checked]/avatar:inline-flex\"\n >\n <CheckGlyph className=\"h-3 w-3\" />\n </span>\n </RadioGroupPrimitive.Item>\n ))}\n {upload ? <div className=\"flex aspect-square w-full items-center justify-center\">{upload}</div> : null}\n </RadioGroupPrimitive.Root>\n );\n if (!showLabel) return group;\n return (\n <div className={cn(\"flex flex-col gap-3\", className)}>\n <GroupHead id={id} label={label} description={description} />\n {group}\n </div>\n );\n}\n","import type * as React from \"react\";\n\n/** The visible label (and one line of help) of a radio group: the group's `aria-labelledby` points at it. */\nexport function GroupHead({ id, label, description }: { id: string; label: React.ReactNode; description?: React.ReactNode }) {\n return (\n <div className=\"flex flex-col gap-0.5\">\n <span id={id} className=\"text-sm font-medium text-fg\">\n {label}\n </span>\n {description ? <span className=\"text-sm text-fg-secondary\">{description}</span> : null}\n </div>\n );\n}\n","// Client (a radio group). A single choice as selectable cards — an icon, a title, one line: the side of a lease, a plan, a\n// way to get the answer. Built on `RadioGroup.Card`, so the arrows choose, the card is the radio, the check lights up.\nimport * as React from \"react\";\n\nimport { RadioGroup } from \"../radio-group\";\nimport { GroupHead } from \"./group-head\";\n\nexport interface ChoiceCardOption {\n /** The value of the choice. */\n id: string;\n /** Its name, short. */\n title: React.ReactNode;\n /** One line under the title: what it means. It describes the radio. */\n description?: React.ReactNode;\n /** A glyph before the title (decorative). */\n icon?: React.ReactNode;\n disabled?: boolean;\n}\n\nexport type ChoiceCardsProps = Omit<React.ComponentProps<typeof RadioGroup.Root>, \"children\" | \"variant\" | \"label\"> & {\n /** The question (“Which side of the lease are you?”) — the group's accessible name, passed translated. */\n label: string;\n /** Shows the question above the cards, with `description` under it. */\n showLabel?: boolean;\n /** One line under the visible question. */\n description?: React.ReactNode;\n /** The choices. */\n options: readonly ChoiceCardOption[];\n /** The chosen id (controlled). */\n value?: string;\n /** The viewer chooses another card. */\n onValueChange?: (id: string) => void;\n};\n\n/** Cards to choose one from: a radio group where each card has an icon, a title and a description. */\nexport function ChoiceCards({ label, showLabel, description, options, className, ...props }: ChoiceCardsProps) {\n const base = React.useId();\n const group = (\n <RadioGroup.Root variant=\"cards\" label={label} aria-labelledby={showLabel ? `${base}-label` : undefined} className={[\"grid grid-cols-1 gap-3 sm:grid-cols-2\", !showLabel && className].filter(Boolean).join(\" \")} {...props}>\n {options.map((option) => {\n const describedBy = option.description ? `${base}-${option.id}` : undefined;\n return (\n <RadioGroup.Card key={option.id} value={option.id} disabled={option.disabled} aria-describedby={describedBy}>\n <span className=\"flex items-center gap-2.5 font-semibold text-fg [&>svg]:h-5 [&>svg]:w-5 [&>svg]:text-accent\">\n {option.icon}\n {option.title}\n </span>\n {option.description ? (\n <span id={describedBy} className=\"text-sm font-normal text-fg-secondary\">\n {option.description}\n </span>\n ) : null}\n </RadioGroup.Card>\n );\n })}\n </RadioGroup.Root>\n );\n if (!showLabel) return group;\n return (\n <div className={[\"flex flex-col gap-3\", className].filter(Boolean).join(\" \")}>\n <GroupHead id={`${base}-label`} label={label} description={description} />\n {group}\n </div>\n );\n}\n","// Client (a context). Radix RadioGroup: role=\"radiogroup\" / \"radio\", one tab stop, the arrows move and select, a\n// hidden input inside a form. Four looks, one behaviour:\n// - `RadioGroup.Item`: a dot and its words (a settings list);\n// - `RadioGroup.Card`: a whole card is the radio — an amount, a pack, a plan — its check lit when chosen;\n// - `RadioGroup.Button`: a tactile key that presses in — raised on a ledge, sunk when chosen (a size, a tip, a level);\n// - `RadioGroup.Segment` in `<RadioGroup.Root variant=\"segmented\">`: a segmented control, one light gliding to the choice.\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { CheckGlyph } from \"../glyphs\";\nimport { useRouteBinding } from \"../router\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\nimport { tv } from \"../tv\";\n\nexport const radioGroup = tv({\n slots: {\n root: \"grid gap-2 data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-wrap\",\n item:\n \"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-border-strong bg-surface-1 inset-shadow-well transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:shadow-glow aria-invalid:border-danger\",\n dot: \"h-2 w-2 rounded-full bg-on-accent\",\n label: \"flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-70\",\n card:\n \"group relative flex min-w-0 flex-col items-start gap-1 rounded-2xl border border-border-default bg-surface-1 bg-sheen p-4 pr-10 text-left text-sm text-fg inset-shadow-edge \" +\n \"transition-[border-color,background-color,box-shadow,transform] duration-200 hover:border-border-strong active:scale-[0.99] motion-reduce:transition-none motion-reduce:active:scale-100 \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent-soft data-[state=checked]:shadow-glow\",\n // The card's check: an empty ring at rest, the accent filled with its check once chosen.\n cardCheck:\n \"absolute right-3.5 top-3.5 inline-flex h-5 w-5 items-center justify-center rounded-full border border-border-strong bg-surface-1 text-on-accent transition-colors \" +\n \"group-data-[state=checked]:border-accent group-data-[state=checked]:bg-accent\",\n // The tactile key: on a ledge (its lower edge), its hairline stronger on hover, pressed in while held and once chosen.\n button:\n \"relative inline-flex min-h-12 min-w-12 select-none flex-col items-center justify-center gap-0.5 rounded-xl border border-border-default bg-surface-2 bg-sheen px-4 py-2 \" +\n \"text-sm font-semibold text-fg inset-shadow-edge shadow-[0_4px_0_0_var(--kz-border-strong)] \" +\n \"transition-[transform,box-shadow,background-color,border-color,color] duration-100 ease-out motion-reduce:transition-none \" +\n \"hover:border-border-strong \" +\n \"active:translate-y-[3px] active:shadow-[0_1px_0_0_var(--kz-border-strong)] \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger \" +\n \"data-[state=checked]:translate-y-[3px] data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:bg-fill-shade \" +\n \"data-[state=checked]:text-on-accent data-[state=checked]:inset-shadow-edge-fill data-[state=checked]:shadow-[0_1px_0_0_var(--kz-accent-hover)]\",\n buttonHint: \"text-[11px] font-medium text-fg-secondary group-data-[state=checked]:text-on-accent\",\n segmented:\n \"relative isolate inline-grid auto-cols-fr grid-flow-col gap-1 rounded-xl border border-border-default bg-surface-2 p-1 inset-shadow-well \" +\n \"before:pointer-events-none before:absolute before:left-0 before:top-0 before:-z-10 before:h-(--kz-ind-h) before:w-(--kz-ind-w) \" +\n \"before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:border before:border-border-default before:bg-surface-0 before:bg-sheen \" +\n \"before:opacity-0 before:shadow-raised before:transition-[translate,width,height,opacity] before:duration-300 motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n segment:\n \"inline-flex min-h-9 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-lg px-3.5 text-sm font-semibold text-fg-secondary transition-colors \" +\n \"hover:text-fg focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border data-[state=checked]:border-border-default data-[state=checked]:bg-surface-0 data-[state=checked]:text-fg data-[state=checked]:shadow-raised \" +\n \"group-data-[indicator=on]/seg:data-[state=checked]:border-transparent group-data-[indicator=on]/seg:data-[state=checked]:bg-transparent group-data-[indicator=on]/seg:data-[state=checked]:shadow-none\",\n },\n variants: {\n variant: {\n list: {},\n cards: { root: \"grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-3\" },\n buttons: { root: \"flex flex-wrap gap-2.5 pb-1\" },\n segmented: {},\n },\n },\n defaultVariants: { variant: \"list\" },\n});\nconst s = radioGroup();\n\nexport type RadioGroupRootProps = React.ComponentProps<typeof RadioGroupPrimitive.Root> & {\n /** The group's accessible name — passed translated (or `aria-labelledby` a visible heading). */\n label?: string;\n /** Marks every radio invalid: aria-invalid (pair it with a `Field.Error`). */\n invalid?: boolean;\n /**\n * The layout of the radios: `list` (dots), `cards` (a grid of `Card`s), `buttons` (a row of tactile `Button`s), or\n * `segmented` (a track of `Segment`s, one light gliding to the choice).\n * @default \"list\"\n */\n variant?: \"list\" | \"cards\" | \"buttons\" | \"segmented\";\n /** Binds the choice to this search parameter of the URL (`?kind=…`): the URL is the state — for a view's selection (a segmented switch), not a form field. */\n routeKey?: string;\n /** The values the parameter may hold (the radios' `value`s); the first is the default unless `defaultValue` says otherwise. */\n routeValues?: readonly string[];\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form, `variant` for the layout. */\nexport function RadioGroupRoot({ label, invalid, variant = \"list\", className, routeKey, routeValues, ...rest }: RadioGroupRootProps) {\n const props = useRouteBinding(routeKey, routeValues, rest);\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=radio]\", active: \"[role=radio][data-state=checked]\" });\n const segmented = variant === \"segmented\";\n return (\n <RadioGroupPrimitive.Root\n ref={segmented ? ref : undefined}\n aria-label={label}\n aria-invalid={invalid || undefined}\n data-invalid={invalid ? \"\" : undefined}\n data-variant={variant}\n orientation={segmented ? \"horizontal\" : props.orientation}\n className={segmented ? s.segmented({ className: [\"group/seg\", className].filter(Boolean).join(\" \") }) : radioGroup({ variant }).root({ className })}\n {...props}\n />\n );\n}\n\nexport type RadioGroupItemProps = Omit<React.ComponentProps<typeof RadioGroupPrimitive.Item>, \"children\"> & {\n /** The words next to the dot, clickable. Otherwise name it with `Field.Label htmlFor` or `aria-label`. */\n children?: React.ReactNode;\n};\n\n/** A radio: a dot, and its words when given. */\nexport function RadioGroupItem({ className, children, ...props }: RadioGroupItemProps) {\n const item = (\n <RadioGroupPrimitive.Item className={s.item({ className: children ? \"mt-0.5\" : className })} {...props}>\n <RadioGroupPrimitive.Indicator className={s.dot()} />\n </RadioGroupPrimitive.Item>\n );\n if (!children) return item;\n return (\n <label className={s.label({ className })}>\n {item}\n <span className=\"min-w-0\">{children}</span>\n </label>\n );\n}\n\n/** A whole card as the radio: its content is the radio's name; its check lights up when chosen. */\nexport function RadioGroupCard({ className, children, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return (\n <RadioGroupPrimitive.Item className={s.card({ className })} {...props}>\n {children}\n <span aria-hidden className={s.cardCheck()}>\n <RadioGroupPrimitive.Indicator forceMount className=\"inline-flex data-[state=unchecked]:hidden\">\n <CheckGlyph className=\"h-3 w-3\" />\n </RadioGroupPrimitive.Indicator>\n </span>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport type RadioGroupButtonProps = React.ComponentProps<typeof RadioGroupPrimitive.Item> & {\n /** A second line under the words (\"+15 %\"), part of the radio's name. */\n hint?: React.ReactNode;\n};\n\n/** A tactile key: raised on its ledge, pressed in while held and once chosen — sizes, a tip, a level. */\nexport function RadioGroupButton({ className, children, hint, ...props }: RadioGroupButtonProps) {\n return (\n <RadioGroupPrimitive.Item className={s.button({ className: [\"group\", className].filter(Boolean).join(\" \") })} {...props}>\n <span>{children}</span>\n {hint ? <span className={s.buttonHint()}>{hint}</span> : null}\n </RadioGroupPrimitive.Item>\n );\n}\n\n/** A segment of a `variant=\"segmented\"` group: equal widths, one light gliding to the choice. */\nexport function RadioGroupSegment({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return <RadioGroupPrimitive.Item className={s.segment({ className })} {...props} />;\n}\n\n/** `RadioGroup.Root/Item/Card/Button/Segment`. */\nexport const RadioGroup = { Root: RadioGroupRoot, Item: RadioGroupItem, Card: RadioGroupCard, Button: RadioGroupButton, Segment: RadioGroupSegment };\n","// Client. The URL is the state: a selection the user would want to share, reload or come back to (a tab, a type, a\n// filter) lives in a search parameter, never in `useState` (Oussama, 2026-10-22: « changer les tabs doit changer les routes »).\n// One primitive reads and writes it: `useRouteState`. The selection components take `routeKey` and bind to it.\nimport * as React from \"react\";\n\n/** How a router moves and reads the address: the browser's by default, your framework's through `RouteStateProvider`. */\nexport interface RouteAdapter {\n /** The current query string, with or without `?` (`\"\"` on the server). */\n search: () => string;\n /** Go to `search` (`?type=bail`, or `\"\"`), keeping the path and the hash; `replace` keeps the history as it is. */\n navigate: (search: string, options: { replace: boolean }) => void;\n /** Calls `onChange` when the address changes under you (back, forward, another navigation); returns the unsubscribe. */\n subscribe: (onChange: () => void) => () => void;\n}\n\nconst CHANGE = \"kz-route-state\";\n\n/** The History API: any web app (Vite, a static site). Next.js apps pass their router's adapter once, in the layout. */\nexport const browserAdapter: RouteAdapter = {\n search: () => (typeof window === \"undefined\" ? \"\" : window.location.search),\n navigate: (search, { replace }) => {\n const url = `${window.location.pathname}${search}${window.location.hash}`;\n window.history[replace ? \"replaceState\" : \"pushState\"](window.history.state, \"\", url);\n window.dispatchEvent(new Event(CHANGE));\n },\n subscribe: (onChange) => {\n window.addEventListener(\"popstate\", onChange);\n window.addEventListener(CHANGE, onChange);\n return () => {\n window.removeEventListener(\"popstate\", onChange);\n window.removeEventListener(CHANGE, onChange);\n };\n },\n};\n\nconst AdapterContext = React.createContext<RouteAdapter>(browserAdapter);\n\nexport interface RouteStateProviderProps {\n /** How your router reads the query string, navigates and tells about changes. */\n adapter: RouteAdapter;\n children: React.ReactNode;\n}\n\n/** Gives `useRouteState` your router: `<RouteStateProvider adapter={nextAdapter}>` once, in the root layout. */\nexport function RouteStateProvider({ adapter, children }: RouteStateProviderProps) {\n return <AdapterContext.Provider value={adapter}>{children}</AdapterContext.Provider>;\n}\n\nexport interface RouteStateOptions<V extends string> {\n /** The values the parameter may hold, as a const list: anything else in the address reads as the default. Omit for free text. */\n values?: readonly V[];\n /** The value when the parameter is absent. It is never written to the address (a clean URL). */\n default: V;\n /** `push` (the default) keeps each change in the history, `replace` overwrites it: use `replace` for a filter typed or toggled in passing. */\n history?: \"push\" | \"replace\";\n}\n\n/** The value a query string holds for `key`, checked against `values`. Pure: the hook and the tests share it. */\nexport function readRouteValue<V extends string>(search: string, key: string, { values, default: fallback }: Pick<RouteStateOptions<V>, \"values\" | \"default\">): V {\n const raw = new URLSearchParams(search).get(key);\n if (raw === null) return fallback;\n return !values || (values as readonly string[]).includes(raw) ? (raw as V) : fallback;\n}\n\n/** The query string with `key` set to `value` (removed when it is the default); every other parameter is kept. Pure. */\nexport function writeRouteValue(search: string, key: string, value: string, fallback: string): string {\n const params = new URLSearchParams(search);\n if (value === fallback) params.delete(key);\n else params.set(key, value);\n const text = params.toString();\n return text ? `?${text}` : \"\";\n}\n\n/**\n * A value kept in the URL's search parameter `key`: `const [type, setType] = useRouteState(\"type\", { values: TYPES, default: \"auto\" })`\n * with `const TYPES = [\"auto\", \"lease\", \"contract\", \"invoice\", \"other\"] as const`. The value is typed from the list, an\n * unknown one in the address reads as the default, the default is not written, other parameters are kept.\n * `key` undefined makes the hook inert (it returns the default) so a component can call it unconditionally.\n */\nexport function useRouteState<V extends string>(key: string | undefined, { values, default: fallback, history = \"push\" }: RouteStateOptions<V>): readonly [V, (next: V) => void] {\n const adapter = React.useContext(AdapterContext);\n const search = React.useSyncExternalStore(adapter.subscribe, adapter.search, () => \"\");\n const value = key === undefined ? fallback : readRouteValue(search, key, { values, default: fallback });\n const set = React.useCallback(\n (next: V) => {\n if (key === undefined) return;\n adapter.navigate(writeRouteValue(adapter.search(), key, next, fallback), { replace: history === \"replace\" });\n },\n [adapter, key, fallback, history],\n );\n return [value, set] as const;\n}\n\n/** What a selection component spreads on its root: `value` and `onValueChange` bound to the URL when `routeKey` is set. */\nexport interface RouteBindable {\n value?: string | null;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n}\n\n/** The props of a selection component bound to the URL (`routeKey`), or its own, unchanged, when there is no `routeKey`. */\nexport function useRouteBinding<P extends RouteBindable>(routeKey: string | undefined, routeValues: readonly string[] | undefined, props: P): P {\n const initial = props.defaultValue ?? props.value ?? routeValues?.[0] ?? \"\";\n const [value, set] = useRouteState<string>(routeKey, { values: routeValues, default: initial });\n if (routeKey === undefined) return props;\n return {\n ...props,\n value,\n defaultValue: undefined,\n onValueChange: (next: string) => {\n set(next);\n props.onValueChange?.(next);\n },\n };\n}\n","// The shared sliding indicator (NavBar, Tabs): one element that glides from item to item instead of each item lighting\n// up on its own. It writes the target's box as CSS variables on the list (`--kz-ind-x/y/w/h`) and `data-indicator=\"on\"`\n// — no re-render, no animation frame: the glide is a CSS transition (none under reduced motion). Before hydration the\n// items keep their own active style, so nothing flashes.\nimport * as React from \"react\";\n\nconst useIsoLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\n/**\n * `item`: the selector of an item; `active`: of the current one, where the light rests; `prefer`: an item that, when\n * present, holds the light before the current one (the NavBar's open menu); `follow`: the light follows the pointer\n * and the focus — over anything that is not an item (an open menu's panel) it goes back to rest.\n */\nexport function useSlidingIndicator<T extends HTMLElement>({ item, active, prefer, follow = false }: { item: string; active: string; prefer?: string; follow?: boolean }) {\n const ref = React.useRef<T>(null);\n const place = React.useCallback((target: Element | null) => {\n const list = ref.current;\n if (!list) return;\n if (!(target instanceof HTMLElement) || !list.contains(target)) {\n list.removeAttribute(\"data-indicator\");\n return;\n }\n const a = target.getBoundingClientRect();\n const b = list.getBoundingClientRect();\n list.style.setProperty(\"--kz-ind-x\", `${a.left - b.left + list.scrollLeft}px`);\n list.style.setProperty(\"--kz-ind-y\", `${a.top - b.top + list.scrollTop}px`);\n list.style.setProperty(\"--kz-ind-w\", `${a.width}px`);\n list.style.setProperty(\"--kz-ind-h\", `${a.height}px`);\n list.setAttribute(\"data-indicator\", \"on\");\n }, []);\n const rest = React.useCallback(\n () => {\n const list = ref.current;\n // An item re-rendered under a resting pointer (a menu closing) keeps the light the pointer gave it.\n const hovered = follow ? list?.querySelector(`${item}:hover`) : null;\n place((prefer ? list?.querySelector(prefer) : null) ?? hovered ?? list?.querySelector(active) ?? null);\n },\n [place, active, prefer, item, follow],\n );\n const follow_ = React.useCallback((target: EventTarget) => {\n const hit = target instanceof Element ? target.closest(item) : null;\n if (hit && ref.current?.contains(hit)) place(hit);\n else rest();\n }, [item, place, rest]);\n\n useIsoLayoutEffect(() => {\n const list = ref.current;\n if (!list) return;\n rest();\n const resize = new ResizeObserver(rest);\n resize.observe(list);\n const changes = new MutationObserver(rest);\n changes.observe(list, { subtree: true, attributes: true, attributeFilter: [\"data-state\", \"aria-current\", \"data-active\"] });\n return () => {\n resize.disconnect();\n changes.disconnect();\n };\n }, [rest]);\n\n const handlers = follow\n ? {\n onPointerOver: (e: React.PointerEvent) => follow_(e.target),\n onPointerLeave: rest,\n onFocus: (e: React.FocusEvent) => follow_(e.target),\n onBlur: rest,\n }\n : {};\n return { ref, handlers };\n}\n","import { createTV } from \"tailwind-variants\";\n\nimport { twMergeConfig } from \"./cn\";\n\n/** `tv` with the preset's utilities known to the merge engine (`twMergeConfig`): what a primitive wearing the material is styled with. */\nexport const tv = createTV({ twMergeConfig });\n","// Client (a radio group). The language of the account, labelled, each language named in its own language, saved per user\n// (the caller saves on change or with its section). Segmented: one light gliding to the choice.\nimport * as React from \"react\";\n\nimport { RadioGroup } from \"../radio-group\";\nimport { GroupHead } from \"./group-head\";\n\nexport interface LanguageOption {\n /** The BCP 47 code (`fr`, `en`): the value, and the `lang` of the name. */\n code: string;\n /** Its native name (`Français`, `English`) — never the current language's name for it. */\n name: string;\n}\n\nexport type LanguagePickerProps = Omit<React.ComponentProps<typeof RadioGroup.Root>, \"children\" | \"variant\" | \"label\" | \"aria-labelledby\"> & {\n /** The visible label (“Language”) — passed translated. */\n label: React.ReactNode;\n /**\n * Shows the label above the control; `false` when a `SettingsRow` already says it (the group keeps its accessible name).\n * @default true\n */\n showLabel?: boolean;\n /** One line under it: what it changes. */\n description?: React.ReactNode;\n /** The languages offered. */\n languages: readonly LanguageOption[];\n /** The chosen code (controlled). */\n value?: string;\n /** The viewer chooses another language. */\n onValueChange?: (code: string) => void;\n};\n\n/** The language control of an account page: a labelled segmented choice, native names. */\nexport function LanguagePicker({ label, showLabel = true, description, languages, className, ...props }: LanguagePickerProps) {\n const id = React.useId();\n return (\n <div className={[\"flex flex-col gap-2\", className].filter(Boolean).join(\" \")}>\n {showLabel ? <GroupHead id={`${id}-label`} label={label} description={description} /> : null}\n <RadioGroup.Root variant=\"segmented\" aria-label={showLabel ? undefined : String(label)} aria-labelledby={showLabel ? `${id}-label` : undefined} {...props}>\n {languages.map((language) => (\n <RadioGroup.Segment key={language.code} value={language.code} lang={language.code}>\n {language.name}\n </RadioGroup.Segment>\n ))}\n </RadioGroup.Root>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","jsx","jsxs","React","RadioGroupPrimitive","React","jsx","React","tv","jsx","jsxs","tv","RadioGroupPrimitive","jsx","jsxs","React","jsx","jsxs"]}
|
package/dist/agenda.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
declare const agenda: tailwind_variants.TVReturnType<{
|
|
5
|
+
past: {
|
|
6
|
+
true: {
|
|
7
|
+
times: string;
|
|
8
|
+
title: string;
|
|
9
|
+
marker: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}, {
|
|
13
|
+
root: string;
|
|
14
|
+
item: string;
|
|
15
|
+
main: string;
|
|
16
|
+
times: string;
|
|
17
|
+
start: string;
|
|
18
|
+
end: string;
|
|
19
|
+
marker: string;
|
|
20
|
+
leading: string;
|
|
21
|
+
text: string;
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
status: string;
|
|
25
|
+
action: string;
|
|
26
|
+
now: string;
|
|
27
|
+
nowLine: string;
|
|
28
|
+
nowDot: string;
|
|
29
|
+
empty: string;
|
|
30
|
+
}, undefined, {
|
|
31
|
+
past: {
|
|
32
|
+
true: {
|
|
33
|
+
times: string;
|
|
34
|
+
title: string;
|
|
35
|
+
marker: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}, {
|
|
39
|
+
root: string;
|
|
40
|
+
item: string;
|
|
41
|
+
main: string;
|
|
42
|
+
times: string;
|
|
43
|
+
start: string;
|
|
44
|
+
end: string;
|
|
45
|
+
marker: string;
|
|
46
|
+
leading: string;
|
|
47
|
+
text: string;
|
|
48
|
+
title: string;
|
|
49
|
+
description: string;
|
|
50
|
+
status: string;
|
|
51
|
+
action: string;
|
|
52
|
+
now: string;
|
|
53
|
+
nowLine: string;
|
|
54
|
+
nowDot: string;
|
|
55
|
+
empty: string;
|
|
56
|
+
}, tailwind_variants.TVReturnTypeLike<{
|
|
57
|
+
past: {
|
|
58
|
+
true: {
|
|
59
|
+
times: string;
|
|
60
|
+
title: string;
|
|
61
|
+
marker: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}, {
|
|
65
|
+
root: string;
|
|
66
|
+
item: string;
|
|
67
|
+
main: string;
|
|
68
|
+
times: string;
|
|
69
|
+
start: string;
|
|
70
|
+
end: string;
|
|
71
|
+
marker: string;
|
|
72
|
+
leading: string;
|
|
73
|
+
text: string;
|
|
74
|
+
title: string;
|
|
75
|
+
description: string;
|
|
76
|
+
status: string;
|
|
77
|
+
action: string;
|
|
78
|
+
now: string;
|
|
79
|
+
nowLine: string;
|
|
80
|
+
nowDot: string;
|
|
81
|
+
empty: string;
|
|
82
|
+
}>>;
|
|
83
|
+
type AgendaTone = "neutral" | "accent" | "success" | "warning" | "danger" | "info";
|
|
84
|
+
type AgendaItem = {
|
|
85
|
+
id: string;
|
|
86
|
+
/** ISO instant. */
|
|
87
|
+
start: string;
|
|
88
|
+
/** ISO instant; shown under the start and used to know which one is current. */
|
|
89
|
+
end?: string;
|
|
90
|
+
/** The client, the subject. */
|
|
91
|
+
title: React.ReactNode;
|
|
92
|
+
/** The service, the stylist: one muted line. */
|
|
93
|
+
description?: React.ReactNode;
|
|
94
|
+
/** The colour of the rail's dot; the status chip carries the word. */
|
|
95
|
+
tone?: AgendaTone;
|
|
96
|
+
/** Before the text: an `Avatar`. */
|
|
97
|
+
leading?: React.ReactNode;
|
|
98
|
+
/** After the text: a `Badge` with the status, in words. */
|
|
99
|
+
status?: React.ReactNode;
|
|
100
|
+
/** An address for the row (the app's URL state, `?appointment=…`); rendered with `linkAs`. */
|
|
101
|
+
href?: string;
|
|
102
|
+
};
|
|
103
|
+
type AgendaLabels = {
|
|
104
|
+
/** The « now » line: `(time) => "Now · 10:42"`. */
|
|
105
|
+
now: (time: string) => string;
|
|
106
|
+
};
|
|
107
|
+
declare const agendaLabels: AgendaLabels;
|
|
108
|
+
type AgendaProps<T extends AgendaItem> = {
|
|
109
|
+
/** The day's appointments, in any order: the agenda sorts them by start. */
|
|
110
|
+
items: readonly T[];
|
|
111
|
+
/** The agenda's accessible name (« Appointments of Monday »). */
|
|
112
|
+
"aria-label": string;
|
|
113
|
+
/** The locale of the times. Default `en-US`. */
|
|
114
|
+
locale?: string;
|
|
115
|
+
/** The time zone of the shop, so the times read the same on the server and on the phone. */
|
|
116
|
+
timeZone?: string;
|
|
117
|
+
/** The instant of the « now » line; `null` or absent hides it (a day that is not today). */
|
|
118
|
+
now?: Date | null;
|
|
119
|
+
/** The open item: marked `aria-current`. */
|
|
120
|
+
selectedId?: string | null;
|
|
121
|
+
/** Called with the item pressed, for items with no `href`. */
|
|
122
|
+
onSelect?: (item: T) => void;
|
|
123
|
+
/** The router's link for items with an `href` (`next/link`). Default `a`. */
|
|
124
|
+
linkAs?: React.ElementType;
|
|
125
|
+
/** A small control after the row (Start, End), outside the row's link. */
|
|
126
|
+
action?: (item: T) => React.ReactNode;
|
|
127
|
+
/** The words, passed translated (the « now » line). */
|
|
128
|
+
labels?: Partial<AgendaLabels>;
|
|
129
|
+
/** Shown when there is no item (an `EmptyState`). */
|
|
130
|
+
empty?: React.ReactNode;
|
|
131
|
+
/** Classes merged on the list. */
|
|
132
|
+
className?: string;
|
|
133
|
+
};
|
|
134
|
+
declare function Agenda<T extends AgendaItem>({ items, locale, timeZone, now, selectedId, onSelect, linkAs, action, labels: given, empty, className, "aria-label": label }: AgendaProps<T>): React.JSX.Element | null;
|
|
135
|
+
|
|
136
|
+
export { Agenda, type AgendaItem, type AgendaLabels, type AgendaProps, type AgendaTone, agenda, agendaLabels };
|