@krizaka/ui 3.46.0 → 3.49.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-P4TTKYC4.js → chunk-4LYKBSOG.js} +4 -1
- package/dist/chunk-4LYKBSOG.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 +1 -1
- package/dist/nav-bar.js +1 -1
- package/dist/settings-layout.d.ts +112 -8
- package/dist/settings-layout.js +274 -28
- package/dist/settings-layout.js.map +1 -1
- package/dist/site-footer.d.ts +1 -1
- package/dist/site-footer.js +1 -1
- package/dist/{sites-inztN_Bv.d.ts → sites-DlR8jicc.d.ts} +1 -1
- package/dist/sites.d.ts +1 -1
- package/dist/sites.js +3 -0
- package/dist/sites.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 +33 -7
- 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/settings-layout/grouped.d.ts +4 -0
- package/registry/examples/settings-layout/grouped.js +64 -0
- package/registry/examples/settings-layout/grouped.tsx +66 -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 +159 -10
- 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 +2 -2
- 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 +79 -13
- package/registry/showcase.json +1 -1
- package/registry/site-footer.json +2 -2
- package/registry/sites.json +2 -2
- 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
- package/dist/chunk-P4TTKYC4.js.map +0 -1
package/dist/settings-layout.js
CHANGED
|
@@ -1,8 +1,182 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
|
+
defineRecipe
|
|
3
|
+
} from "./chunk-2VNYHG4E.js";
|
|
4
|
+
import {
|
|
5
|
+
cn
|
|
6
|
+
} from "./chunk-S6NYGOJT.js";
|
|
7
|
+
|
|
8
|
+
// src/settings-layout/save-status.ts
|
|
2
9
|
import * as React from "react";
|
|
10
|
+
var SaveStatus = { Idle: "idle", Saving: "saving", Saved: "saved", Error: "error" };
|
|
11
|
+
var SAVED_VISIBLE_MS = 4e3;
|
|
12
|
+
function useSaveStatus() {
|
|
13
|
+
const [status, setStatus] = React.useState(SaveStatus.Idle);
|
|
14
|
+
const [error, setError] = React.useState(void 0);
|
|
15
|
+
const timer = React.useRef(void 0);
|
|
16
|
+
const clear = React.useCallback(() => {
|
|
17
|
+
if (timer.current) clearTimeout(timer.current);
|
|
18
|
+
timer.current = void 0;
|
|
19
|
+
}, []);
|
|
20
|
+
React.useEffect(() => clear, [clear]);
|
|
21
|
+
const reset = React.useCallback(() => {
|
|
22
|
+
clear();
|
|
23
|
+
setStatus(SaveStatus.Idle);
|
|
24
|
+
setError(void 0);
|
|
25
|
+
}, [clear]);
|
|
26
|
+
const track = React.useCallback(
|
|
27
|
+
async (save) => {
|
|
28
|
+
clear();
|
|
29
|
+
setStatus(SaveStatus.Saving);
|
|
30
|
+
setError(void 0);
|
|
31
|
+
try {
|
|
32
|
+
await save();
|
|
33
|
+
setStatus(SaveStatus.Saved);
|
|
34
|
+
timer.current = setTimeout(() => setStatus(SaveStatus.Idle), SAVED_VISIBLE_MS);
|
|
35
|
+
return true;
|
|
36
|
+
} catch (failure) {
|
|
37
|
+
setError(failure);
|
|
38
|
+
setStatus(SaveStatus.Error);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[clear]
|
|
43
|
+
);
|
|
44
|
+
return { status, error, track, reset };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/settings-layout/settings-layout.tsx
|
|
48
|
+
import * as React2 from "react";
|
|
49
|
+
import { tv as tv2 } from "tailwind-variants";
|
|
50
|
+
|
|
51
|
+
// src/button/button.tsx
|
|
52
|
+
import { Slot } from "radix-ui";
|
|
3
53
|
import { tv } from "tailwind-variants";
|
|
54
|
+
|
|
55
|
+
// src/spinner/glyph.tsx
|
|
4
56
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
var
|
|
57
|
+
var HEX = "M12 3 19.79 7.5 19.79 16.5 12 21 4.21 16.5 4.21 7.5Z";
|
|
58
|
+
function LoaderGlyph({ state = "loading", value, ...props }) {
|
|
59
|
+
const known = typeof value === "number" && Number.isFinite(value);
|
|
60
|
+
const fill = state !== "loading" ? 100 : known ? Math.round(Math.min(1, Math.max(0, value)) * 100) : null;
|
|
61
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, "data-state": state, "data-progress": fill === null ? void 0 : fill, ...props, children: [
|
|
62
|
+
/* @__PURE__ */ jsx("path", { d: HEX, stroke: "currentColor", strokeOpacity: "0.22", strokeWidth: "2.2", strokeLinejoin: "round" }),
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
"path",
|
|
65
|
+
{
|
|
66
|
+
d: HEX,
|
|
67
|
+
pathLength: 100,
|
|
68
|
+
stroke: "currentColor",
|
|
69
|
+
strokeWidth: "2.2",
|
|
70
|
+
strokeLinejoin: "round",
|
|
71
|
+
strokeLinecap: "round",
|
|
72
|
+
className: "kz-loader-trace",
|
|
73
|
+
strokeDasharray: fill === null ? void 0 : `${fill} ${100 - fill}`
|
|
74
|
+
}
|
|
75
|
+
),
|
|
76
|
+
state === "loading" && /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2.6", fill: "currentColor", className: "kz-loader-node" }),
|
|
77
|
+
state === "success" && /* @__PURE__ */ jsx("path", { d: "M8.3 12.3l2.6 2.6 4.9-5.3", pathLength: 20, stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", className: "kz-loader-mark" }),
|
|
78
|
+
state === "error" && /* @__PURE__ */ jsx("path", { d: "M9.4 9.4l5.2 5.2M14.6 9.4l-5.2 5.2", pathLength: 20, stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", className: "kz-loader-mark" })
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/button/recipe.ts
|
|
83
|
+
var buttonRecipe = defineRecipe({
|
|
84
|
+
base: "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition duration-150 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98] [&[data-loading]]:opacity-100",
|
|
85
|
+
variants: {
|
|
86
|
+
variant: {
|
|
87
|
+
primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover",
|
|
88
|
+
// The brand's signature: accent → accent-2 (on-accent reads on both stops, tested in @krizaka/tokens).
|
|
89
|
+
gradient: "bg-linear-to-r from-accent to-accent-2 text-on-accent shadow-sm hover:brightness-110",
|
|
90
|
+
secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3",
|
|
91
|
+
outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-fg-accent",
|
|
92
|
+
ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg",
|
|
93
|
+
// The border and the tint carry the danger; the label stays the primary text role, legible on the tint at rest
|
|
94
|
+
// and hovered over every surface (`text-fg-danger` is for text set directly on a surface).
|
|
95
|
+
danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20"
|
|
96
|
+
},
|
|
97
|
+
size: { sm: "h-8 px-3 text-xs", md: "h-10 px-4 text-sm", lg: "h-12 px-6 text-sm", icon: "h-10 w-10" },
|
|
98
|
+
// One clean shape (krizaka-ui#393): an 8 px radius (`--kz-radius-sm`, the radius of a field — a button beside an
|
|
99
|
+
// input lines up with it), the pill on demand. `cut` (the retired chamfer) is a deprecated alias of `rounded`.
|
|
100
|
+
shape: { rounded: "rounded-sm", pill: "rounded-full", cut: "rounded-sm" }
|
|
101
|
+
},
|
|
102
|
+
defaultVariants: { variant: "secondary", size: "md", shape: "rounded" }
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// src/button/button.tsx
|
|
106
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
107
|
+
var buttonVariants = tv(buttonRecipe);
|
|
108
|
+
var STATUS_TEXT = { success: "text-fg-success", error: "text-fg-danger" };
|
|
109
|
+
var warnedCut = false;
|
|
110
|
+
function warnCut() {
|
|
111
|
+
if (warnedCut || typeof process === "undefined" || process.env.NODE_ENV === "production") return;
|
|
112
|
+
warnedCut = true;
|
|
113
|
+
console.warn('@krizaka/ui/button: shape="cut" is deprecated (krizaka-ui#393) and now draws the default rounded shape. Remove the prop.');
|
|
114
|
+
}
|
|
115
|
+
function Button({
|
|
116
|
+
asChild,
|
|
117
|
+
loading,
|
|
118
|
+
progress,
|
|
119
|
+
status,
|
|
120
|
+
announce,
|
|
121
|
+
indicator = true,
|
|
122
|
+
shimmer: _shimmer,
|
|
123
|
+
magnetic: _magnetic,
|
|
124
|
+
variant,
|
|
125
|
+
size,
|
|
126
|
+
shape,
|
|
127
|
+
className,
|
|
128
|
+
disabled,
|
|
129
|
+
children,
|
|
130
|
+
...props
|
|
131
|
+
}) {
|
|
132
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
133
|
+
if (shape === "cut") warnCut();
|
|
134
|
+
const known = loading && typeof progress === "number" && Number.isFinite(progress);
|
|
135
|
+
const ratio = known ? Math.min(1, Math.max(0, progress)) : 0;
|
|
136
|
+
const glyph = indicator && !asChild && (loading || status) ? status ?? "loading" : null;
|
|
137
|
+
const filled = variant === "primary" || variant === "gradient";
|
|
138
|
+
const button = /* @__PURE__ */ jsx2(
|
|
139
|
+
Comp,
|
|
140
|
+
{
|
|
141
|
+
type: asChild ? void 0 : "button",
|
|
142
|
+
"aria-busy": loading || void 0,
|
|
143
|
+
"data-loading": loading ? "" : void 0,
|
|
144
|
+
"data-variant": variant ?? "secondary",
|
|
145
|
+
"data-kz-status": status ?? void 0,
|
|
146
|
+
disabled: asChild ? void 0 : disabled || loading,
|
|
147
|
+
"aria-disabled": asChild && (disabled || loading) ? true : void 0,
|
|
148
|
+
className: buttonVariants({
|
|
149
|
+
variant,
|
|
150
|
+
size,
|
|
151
|
+
shape,
|
|
152
|
+
className: cn(known && "relative isolate overflow-hidden", className)
|
|
153
|
+
}),
|
|
154
|
+
...props,
|
|
155
|
+
children: asChild ? children : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
156
|
+
known && /* @__PURE__ */ jsx2("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 -z-10 bg-current opacity-15 transition-[width] duration-500", style: { width: `${ratio * 100}%` } }),
|
|
157
|
+
glyph && /* @__PURE__ */ jsx2(
|
|
158
|
+
"span",
|
|
159
|
+
{
|
|
160
|
+
"aria-hidden": true,
|
|
161
|
+
"data-slot": "indicator",
|
|
162
|
+
className: "-my-1 grid grid-cols-[1fr] transition-[grid-template-columns,margin,opacity] duration-300 starting:-mr-2 starting:grid-cols-[0fr] starting:opacity-0",
|
|
163
|
+
children: /* @__PURE__ */ jsx2("span", { className: "overflow-hidden", children: /* @__PURE__ */ jsx2(LoaderGlyph, { state: glyph, value: known ? ratio : null, className: cn("h-4 w-4", glyph !== "loading" && !filled && STATUS_TEXT[glyph]) }) })
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
children
|
|
167
|
+
] })
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
if (announce === void 0 || asChild) return button;
|
|
171
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
172
|
+
button,
|
|
173
|
+
/* @__PURE__ */ jsx2("span", { role: "status", className: "sr-only", children: announce })
|
|
174
|
+
] });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/settings-layout/settings-layout.tsx
|
|
178
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
179
|
+
var settingsLayout = tv2({
|
|
6
180
|
slots: {
|
|
7
181
|
root: "flex w-full flex-col gap-6",
|
|
8
182
|
head: "flex flex-col gap-1",
|
|
@@ -12,6 +186,19 @@ var settingsLayout = tv({
|
|
|
12
186
|
nav: "-mx-4 overflow-x-auto overflow-y-hidden px-4 py-1 -my-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden lg:mx-0 lg:overflow-visible lg:px-0 lg:py-0 lg:my-0",
|
|
13
187
|
navList: "flex w-max gap-2 lg:sticky lg:top-20 lg:w-auto lg:flex-col lg:gap-0.5",
|
|
14
188
|
navLink: "flex h-9 items-center gap-2.5 whitespace-nowrap rounded-sm border border-border-default bg-surface-1 px-3.5 text-sm font-medium text-fg-secondary no-underline hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current]:border-accent aria-[current]:text-fg lg:rounded-lg lg:border-transparent lg:bg-transparent lg:px-3 lg:hover:bg-surface-2 lg:aria-[current]:bg-accent-soft [&>svg]:h-4 [&>svg]:w-4",
|
|
189
|
+
// Past five sections a strip of chips is a puzzle: the sidebar groups them, a phone gets one menu (a disclosure: no script).
|
|
190
|
+
sidebar: "hidden lg:block",
|
|
191
|
+
sidebarList: "flex flex-col gap-6 lg:sticky lg:top-20",
|
|
192
|
+
group: "flex flex-col gap-1",
|
|
193
|
+
groupLabel: "px-3 text-xs font-semibold text-fg-secondary",
|
|
194
|
+
groupLink: "flex h-9 items-center gap-2.5 whitespace-nowrap rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current]:bg-accent-soft aria-[current]:text-fg [&>svg]:h-4 [&>svg]:w-4",
|
|
195
|
+
groupItems: "m-0 flex list-none flex-col gap-0.5 p-0",
|
|
196
|
+
menu: "group lg:hidden",
|
|
197
|
+
menuSummary: "flex h-11 cursor-pointer list-none items-center justify-between gap-3 rounded-lg border border-border-default bg-surface-1 px-3.5 text-sm font-medium text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden group-open:border-accent",
|
|
198
|
+
menuChevron: "h-4 w-4 shrink-0 text-fg-secondary transition-transform group-open:rotate-180 motion-reduce:transition-none",
|
|
199
|
+
menuPanel: "mt-2 rounded-lg border border-border-default bg-surface-1 p-2",
|
|
200
|
+
navBadge: "ml-auto font-mono text-[11px] text-fg-secondary",
|
|
201
|
+
header: "flex flex-wrap items-center gap-x-4 gap-y-3 border-b border-border-subtle pb-5",
|
|
15
202
|
content: "flex min-w-0 flex-col gap-6",
|
|
16
203
|
section: "rounded-xl border border-border-default bg-surface-1",
|
|
17
204
|
sectionHead: "flex flex-col gap-1 px-5 pt-5",
|
|
@@ -41,50 +228,109 @@ var settingsLayout = tv({
|
|
|
41
228
|
},
|
|
42
229
|
defaultVariants: { variant: "card" }
|
|
43
230
|
});
|
|
44
|
-
|
|
231
|
+
var SETTINGS_CHIPS_MAX = 5;
|
|
232
|
+
var isGrouped = (nav) => nav.length > 0 && "items" in nav[0];
|
|
233
|
+
function NavLink({ item, Link, className }) {
|
|
234
|
+
const s = settingsLayout();
|
|
235
|
+
return /* @__PURE__ */ jsxs3(Link, { href: item.href, className, "aria-current": item.current ? item.href.startsWith("#") ? "location" : "page" : void 0, children: [
|
|
236
|
+
item.icon,
|
|
237
|
+
item.label,
|
|
238
|
+
item.badge !== void 0 && /* @__PURE__ */ jsx3("span", { className: s.navBadge(), children: item.badge })
|
|
239
|
+
] });
|
|
240
|
+
}
|
|
241
|
+
function NavGroups({ groups, Link }) {
|
|
242
|
+
const s = settingsLayout();
|
|
243
|
+
return /* @__PURE__ */ jsx3("div", { className: s.sidebarList(), children: groups.map((group) => /* @__PURE__ */ jsxs3("div", { className: s.group(), children: [
|
|
244
|
+
/* @__PURE__ */ jsx3("h2", { className: s.groupLabel(), children: group.label }),
|
|
245
|
+
/* @__PURE__ */ jsx3("ul", { className: s.groupItems(), children: group.items.map((item) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(NavLink, { item, Link, className: s.groupLink() }) }, item.id)) })
|
|
246
|
+
] }, group.id)) });
|
|
247
|
+
}
|
|
248
|
+
function SettingsLayout({ title, description, header, nav, navLabel, linkAs: Link = "a", variant = "card", className, children, ...props }) {
|
|
45
249
|
const s = settingsLayout({ variant });
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
250
|
+
const groups = isGrouped(nav) ? nav : [{ id: "sections", label: "", items: nav }];
|
|
251
|
+
const items = groups.flatMap((g) => g.items);
|
|
252
|
+
const asMenu = items.length > SETTINGS_CHIPS_MAX;
|
|
253
|
+
const current = items.find((i) => i.current);
|
|
254
|
+
return /* @__PURE__ */ jsxs3("div", { className: s.root({ className }), ...props, children: [
|
|
255
|
+
header ? /* @__PURE__ */ jsx3("div", { className: s.header(), children: header }) : (title || description) && /* @__PURE__ */ jsxs3("div", { className: s.head(), children: [
|
|
256
|
+
title && /* @__PURE__ */ jsx3("h1", { className: s.title(), children: title }),
|
|
257
|
+
description && /* @__PURE__ */ jsx3("p", { className: s.description(), children: description })
|
|
50
258
|
] }),
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
/* @__PURE__ */
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
259
|
+
/* @__PURE__ */ jsxs3("div", { className: s.columns(), children: [
|
|
260
|
+
asMenu ? /* @__PURE__ */ jsxs3("nav", { "aria-label": navLabel, children: [
|
|
261
|
+
/* @__PURE__ */ jsxs3("details", { className: s.menu(), "data-settings-menu": "", children: [
|
|
262
|
+
/* @__PURE__ */ jsxs3("summary", { className: s.menuSummary(), children: [
|
|
263
|
+
/* @__PURE__ */ jsx3("span", { className: "truncate", children: current?.label ?? navLabel }),
|
|
264
|
+
/* @__PURE__ */ jsx3("svg", { "aria-hidden": true, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", className: s.menuChevron(), children: /* @__PURE__ */ jsx3("path", { d: "m4 6 4 4 4-4" }) })
|
|
265
|
+
] }),
|
|
266
|
+
/* @__PURE__ */ jsx3("div", { className: s.menuPanel(), children: /* @__PURE__ */ jsx3(NavGroups, { groups, Link }) })
|
|
267
|
+
] }),
|
|
268
|
+
/* @__PURE__ */ jsx3("div", { className: s.sidebar(), children: /* @__PURE__ */ jsx3(NavGroups, { groups, Link }) })
|
|
269
|
+
] }) : /* @__PURE__ */ jsx3("nav", { "aria-label": navLabel, className: s.nav(), children: /* @__PURE__ */ jsx3("ul", { className: s.navList(), children: items.map((item) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(NavLink, { item, Link, className: s.navLink() }) }, item.id)) }) }),
|
|
270
|
+
/* @__PURE__ */ jsx3("div", { className: s.content(), children })
|
|
57
271
|
] })
|
|
58
272
|
] });
|
|
59
273
|
}
|
|
60
|
-
function SettingsSection({ title, description, footer, variant = "card", id, className, children, ...props }) {
|
|
274
|
+
function SettingsSection({ title, description, footer, save, variant = "card", id, className, children, ...props }) {
|
|
61
275
|
const s = settingsLayout({ variant });
|
|
62
|
-
const auto =
|
|
276
|
+
const auto = React2.useId();
|
|
63
277
|
const titleId = `${id ?? auto}-title`;
|
|
64
|
-
|
|
65
|
-
/* @__PURE__ */
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
278
|
+
const head = /* @__PURE__ */ jsxs3("div", { className: s.sectionHead(), children: [
|
|
279
|
+
/* @__PURE__ */ jsx3("h2", { id: titleId, className: s.sectionTitle(), children: title }),
|
|
280
|
+
description && /* @__PURE__ */ jsx3("p", { className: s.sectionDescription(), children: description })
|
|
281
|
+
] });
|
|
282
|
+
const body = children === void 0 ? null : /* @__PURE__ */ jsx3("div", { className: variant === "flat" ? s.rows() : s.sectionBody(), "data-settings-rows": variant === "flat" ? "" : void 0, children });
|
|
283
|
+
return /* @__PURE__ */ jsx3("section", { id, "aria-labelledby": titleId, "data-dirty": save?.dirty ? "" : void 0, className: s.section({ className }), ...props, children: save ? /* @__PURE__ */ jsxs3(
|
|
284
|
+
"form",
|
|
285
|
+
{
|
|
286
|
+
noValidate: true,
|
|
287
|
+
onSubmit: (event) => {
|
|
288
|
+
event.preventDefault();
|
|
289
|
+
if (save.dirty && save.status !== SaveStatus.Saving) save.onSave();
|
|
290
|
+
},
|
|
291
|
+
children: [
|
|
292
|
+
head,
|
|
293
|
+
body,
|
|
294
|
+
/* @__PURE__ */ jsxs3("div", { className: s.sectionFooter(), children: [
|
|
295
|
+
/* @__PURE__ */ jsx3(SaveFeedback, { save }),
|
|
296
|
+
save.onDiscard && save.dirty ? /* @__PURE__ */ jsx3(Button, { type: "button", variant: "ghost", onClick: save.onDiscard, children: save.labels.discard }) : null,
|
|
297
|
+
/* @__PURE__ */ jsx3(Button, { type: "submit", variant: "primary", disabled: !save.dirty || save.status === SaveStatus.Saving, loading: save.status === SaveStatus.Saving, children: save.labels.save })
|
|
298
|
+
] })
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
302
|
+
head,
|
|
303
|
+
body,
|
|
304
|
+
footer && /* @__PURE__ */ jsx3("div", { className: s.sectionFooter(), children: footer })
|
|
305
|
+
] }) });
|
|
306
|
+
}
|
|
307
|
+
function SaveFeedback({ save }) {
|
|
308
|
+
const failed = save.status === SaveStatus.Error;
|
|
309
|
+
const saved = save.status === SaveStatus.Saved;
|
|
310
|
+
return /* @__PURE__ */ jsxs3("div", { className: "mr-auto flex min-w-0 items-center gap-2 text-sm", children: [
|
|
311
|
+
/* @__PURE__ */ jsx3("span", { role: failed ? "alert" : "status", "aria-live": failed ? "assertive" : "polite", className: failed ? "text-danger-text" : "text-fg-secondary", children: failed ? save.labels.error : saved ? save.labels.saved : save.status === SaveStatus.Saving ? save.labels.saving : "" }),
|
|
312
|
+
saved && save.onUndo ? /* @__PURE__ */ jsx3(Button, { type: "button", variant: "ghost", size: "sm", onClick: save.onUndo, children: save.labels.undo }) : null
|
|
71
313
|
] });
|
|
72
314
|
}
|
|
73
315
|
function SettingsRow({ label, description, htmlFor, className, children, ...props }) {
|
|
74
316
|
const s = settingsLayout();
|
|
75
317
|
const Label = htmlFor ? "label" : "span";
|
|
76
|
-
return /* @__PURE__ */
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
description && /* @__PURE__ */
|
|
318
|
+
return /* @__PURE__ */ jsxs3("div", { className: s.row({ className }), ...props, children: [
|
|
319
|
+
/* @__PURE__ */ jsxs3("div", { className: s.rowText(), children: [
|
|
320
|
+
/* @__PURE__ */ jsx3(Label, { className: s.rowLabel(), ...htmlFor ? { htmlFor } : {}, children: label }),
|
|
321
|
+
description && /* @__PURE__ */ jsx3("span", { className: s.rowDescription(), children: description })
|
|
80
322
|
] }),
|
|
81
|
-
children !== void 0 && /* @__PURE__ */
|
|
323
|
+
children !== void 0 && /* @__PURE__ */ jsx3("div", { className: s.rowControl(), children })
|
|
82
324
|
] });
|
|
83
325
|
}
|
|
84
326
|
export {
|
|
327
|
+
SAVED_VISIBLE_MS,
|
|
328
|
+
SETTINGS_CHIPS_MAX,
|
|
329
|
+
SaveStatus,
|
|
85
330
|
SettingsLayout,
|
|
86
331
|
SettingsRow,
|
|
87
332
|
SettingsSection,
|
|
88
|
-
settingsLayout
|
|
333
|
+
settingsLayout,
|
|
334
|
+
useSaveStatus
|
|
89
335
|
};
|
|
90
336
|
//# sourceMappingURL=settings-layout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/settings-layout/settings-layout.tsx"],"sourcesContent":["// Server-safe. Settings, read from the products' account and administration screens: a list of sections on the left\n// (a row of chips that scrolls on a phone), the current section's forms on the right, each form a titled card with its\n// own save at the foot. The sections are links (one page each) or anchors (one long page).\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const settingsLayout = tv({\n slots: {\n root: \"flex w-full flex-col gap-6\",\n head: \"flex flex-col gap-1\",\n title: \"font-display text-2xl font-extrabold tracking-tight text-fg\",\n description: \"max-w-2xl text-sm text-fg-secondary\",\n columns: \"grid grid-cols-1 gap-6 lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-10\",\n nav: \"-mx-4 overflow-x-auto overflow-y-hidden px-4 py-1 -my-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden lg:mx-0 lg:overflow-visible lg:px-0 lg:py-0 lg:my-0\",\n navList: \"flex w-max gap-2 lg:sticky lg:top-20 lg:w-auto lg:flex-col lg:gap-0.5\",\n navLink:\n \"flex h-9 items-center gap-2.5 whitespace-nowrap rounded-sm border border-border-default bg-surface-1 px-3.5 text-sm font-medium text-fg-secondary no-underline \" +\n \"hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current]:border-accent aria-[current]:text-fg \" +\n \"lg:rounded-lg lg:border-transparent lg:bg-transparent lg:px-3 lg:hover:bg-surface-2 lg:aria-[current]:bg-accent-soft [&>svg]:h-4 [&>svg]:w-4\",\n content: \"flex min-w-0 flex-col gap-6\",\n section: \"rounded-xl border border-border-default bg-surface-1\",\n sectionHead: \"flex flex-col gap-1 px-5 pt-5\",\n sectionTitle: \"text-base font-bold text-fg\",\n sectionDescription: \"text-sm text-fg-secondary\",\n sectionBody: \"flex flex-col gap-4 px-5 py-5\",\n sectionFooter: \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle bg-surface-2/40 px-5 py-3 rounded-b-xl\",\n // The flat look: no card, hairline-divided rows (Linear / Vercel / GitHub settings).\n rows: \"flex flex-col divide-y divide-border-subtle border-y border-border-subtle\",\n row: \"flex flex-col gap-2 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6\",\n rowText: \"flex min-w-0 flex-col gap-0.5\",\n rowLabel: \"text-sm font-medium text-fg\",\n rowDescription: \"text-sm text-fg-secondary\",\n rowControl: \"flex min-w-0 shrink-0 items-center gap-2 text-sm text-fg sm:max-w-[60%] sm:justify-end\",\n },\n variants: {\n variant: {\n card: {},\n flat: {\n content: \"max-w-[45rem] gap-10\",\n section: \"rounded-none border-0 bg-transparent\",\n sectionHead: \"gap-0.5 px-0 pb-3 pt-0\",\n sectionBody: \"gap-0 px-0 py-0\",\n sectionFooter: \"justify-start border-t-0 bg-transparent px-0 pb-0 pt-4\",\n },\n },\n },\n defaultVariants: { variant: \"card\" },\n});\n\nexport interface SettingsNavItem {\n /** Its id (the anchor of a section on one long page). */\n id: string;\n /** Its words. */\n label: string;\n /** Where it leads: a page (`/settings/billing`) or an anchor (`#billing`). */\n href: string;\n /** Its icon (a signature icon). */\n icon?: React.ReactNode;\n /** The section shown now: `aria-current`. */\n current?: boolean;\n}\n\nexport type SettingsLayoutProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The screen's title (`Settings`). */\n title?: React.ReactNode;\n /** One sentence under it. */\n description?: React.ReactNode;\n /** The sections. */\n nav: readonly SettingsNavItem[];\n /** The navigation's accessible name (`Settings sections`). */\n navLabel: string;\n /** The app's link component for the sections (Next's `Link`); an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /**\n * `card` (each form a titled card) or `flat` (one calm column of at most 45rem, rows divided by hairlines).\n * @default \"card\"\n */\n variant?: \"card\" | \"flat\";\n /** The current section's forms (`SettingsSection`). */\n children: React.ReactNode;\n};\n\n/** A settings screen: sections on the left (chips on a phone), forms on the right. */\nexport function SettingsLayout({ title, description, nav, navLabel, linkAs: Link = \"a\", variant = \"card\", className, children, ...props }: SettingsLayoutProps) {\n const s = settingsLayout({ variant });\n return (\n <div className={s.root({ className })} {...props}>\n {(title || description) && (\n <div className={s.head()}>\n {title && <h1 className={s.title()}>{title}</h1>}\n {description && <p className={s.description()}>{description}</p>}\n </div>\n )}\n <div className={s.columns()}>\n <nav aria-label={navLabel} className={s.nav()}>\n <ul className={s.navList()}>\n {nav.map((item) => (\n <li key={item.id}>\n <Link href={item.href} className={s.navLink()} aria-current={item.current ? (item.href.startsWith(\"#\") ? \"location\" : \"page\") : undefined}>\n {item.icon}\n {item.label}\n </Link>\n </li>\n ))}\n </ul>\n </nav>\n <div className={s.content()}>{children}</div>\n </div>\n </div>\n );\n}\n\nexport type SettingsSectionProps = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n /** The section's title: it names the region (and is its anchor's target when `id` is set). */\n title: React.ReactNode;\n /** What these settings change, in one sentence. */\n description?: React.ReactNode;\n /** At the foot: the section's own save (a Button), a note. */\n footer?: React.ReactNode;\n /**\n * `card` (a titled card) or `flat` (no card: a title, then hairline-divided `SettingsRow`s).\n * @default \"card\"\n */\n variant?: \"card\" | \"flat\";\n /** The fields (or, flat, the `SettingsRow`s). */\n children: React.ReactNode;\n};\n\n/** A group of settings: a titled card with its fields and its own save, or flat rows under a title. */\nexport function SettingsSection({ title, description, footer, variant = \"card\", id, className, children, ...props }: SettingsSectionProps) {\n const s = settingsLayout({ variant });\n const auto = React.useId();\n const titleId = `${id ?? auto}-title`;\n return (\n <section id={id} aria-labelledby={titleId} className={s.section({ className })} {...props}>\n <div className={s.sectionHead()}>\n <h2 id={titleId} className={s.sectionTitle()}>\n {title}\n </h2>\n {description && <p className={s.sectionDescription()}>{description}</p>}\n </div>\n <div className={variant === \"flat\" ? s.rows() : s.sectionBody()} data-settings-rows={variant === \"flat\" ? \"\" : undefined}>\n {children}\n </div>\n {footer && <div className={s.sectionFooter()}>{footer}</div>}\n </section>\n );\n}\n\nexport type SettingsRowProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** What the setting is (`Username`). */\n label: React.ReactNode;\n /** The help text under it, one sentence. */\n description?: React.ReactNode;\n /** The id of the control, so the label names it (a `<label htmlFor>`). Leave out for a plain value. */\n htmlFor?: string;\n /** The control or the value, on the right (stacked under the label on a phone). */\n children?: React.ReactNode;\n};\n\n/** One setting: its label and help on the left, its control or value on the right. Put rows in a flat `SettingsSection`. */\nexport function SettingsRow({ label, description, htmlFor, className, children, ...props }: SettingsRowProps) {\n const s = settingsLayout();\n const Label = htmlFor ? \"label\" : \"span\";\n return (\n <div className={s.row({ className })} {...props}>\n <div className={s.rowText()}>\n <Label className={s.rowLabel()} {...(htmlFor ? { htmlFor } : {})}>\n {label}\n </Label>\n {description && <span className={s.rowDescription()}>{description}</span>}\n </div>\n {children !== undefined && <div className={s.rowControl()}>{children}</div>}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/settings-layout/save-status.ts","../src/settings-layout/settings-layout.tsx","../src/button/button.tsx","../src/spinner/glyph.tsx","../src/button/recipe.ts"],"sourcesContent":["import * as React from \"react\";\n\n/** Where an inline save is: nothing to say, in flight, done (for a moment), or failed. */\nexport const SaveStatus = { Idle: \"idle\", Saving: \"saving\", Saved: \"saved\", Error: \"error\" } as const;\nexport type SaveStatus = (typeof SaveStatus)[keyof typeof SaveStatus];\n\n/** How long « Saved » stays before the section goes quiet again. */\n// @fixed: an interface timing (how long a confirmation reads), not a business setting.\nexport const SAVED_VISIBLE_MS = 4000;\n\nexport interface SaveTracker {\n status: SaveStatus;\n /** The failure of the last try, for the message beside the button. */\n error: unknown;\n /** Runs a save: `saving`, then `saved` (back to `idle` after a moment) or `error`. Never rejects. */\n track: (save: () => Promise<unknown>) => Promise<boolean>;\n /** Clears the status (the form was edited again, or the save was undone). */\n reset: () => void;\n}\n\n/** The status of one section's save. Feed it to `SettingsSection`'s `save.status`. */\nexport function useSaveStatus(): SaveTracker {\n const [status, setStatus] = React.useState<SaveStatus>(SaveStatus.Idle);\n const [error, setError] = React.useState<unknown>(undefined);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const clear = React.useCallback(() => {\n if (timer.current) clearTimeout(timer.current);\n timer.current = undefined;\n }, []);\n React.useEffect(() => clear, [clear]);\n const reset = React.useCallback(() => {\n clear();\n setStatus(SaveStatus.Idle);\n setError(undefined);\n }, [clear]);\n const track = React.useCallback(\n async (save: () => Promise<unknown>) => {\n clear();\n setStatus(SaveStatus.Saving);\n setError(undefined);\n try {\n await save();\n setStatus(SaveStatus.Saved);\n timer.current = setTimeout(() => setStatus(SaveStatus.Idle), SAVED_VISIBLE_MS);\n return true;\n } catch (failure) {\n setError(failure);\n setStatus(SaveStatus.Error);\n return false;\n }\n },\n [clear],\n );\n return { status, error, track, reset };\n}\n","// Server-safe. Settings, read from the products' account and administration screens: a list of sections on the left\n// (a row of chips that scrolls on a phone, a disclosure menu past five), the current section's forms on the right, each\n// form a titled card with its own save at the foot. The sections are links (one page each) or anchors (one long page),\n// alone or in titled groups.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { Button } from \"../button/button\";\nimport { SaveStatus } from \"./save-status\";\n\nexport const settingsLayout = tv({\n slots: {\n root: \"flex w-full flex-col gap-6\",\n head: \"flex flex-col gap-1\",\n title: \"font-display text-2xl font-extrabold tracking-tight text-fg\",\n description: \"max-w-2xl text-sm text-fg-secondary\",\n columns: \"grid grid-cols-1 gap-6 lg:grid-cols-[13rem_minmax(0,1fr)] lg:gap-10\",\n nav: \"-mx-4 overflow-x-auto overflow-y-hidden px-4 py-1 -my-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden lg:mx-0 lg:overflow-visible lg:px-0 lg:py-0 lg:my-0\",\n navList: \"flex w-max gap-2 lg:sticky lg:top-20 lg:w-auto lg:flex-col lg:gap-0.5\",\n navLink:\n \"flex h-9 items-center gap-2.5 whitespace-nowrap rounded-sm border border-border-default bg-surface-1 px-3.5 text-sm font-medium text-fg-secondary no-underline \" +\n \"hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current]:border-accent aria-[current]:text-fg \" +\n \"lg:rounded-lg lg:border-transparent lg:bg-transparent lg:px-3 lg:hover:bg-surface-2 lg:aria-[current]:bg-accent-soft [&>svg]:h-4 [&>svg]:w-4\",\n // Past five sections a strip of chips is a puzzle: the sidebar groups them, a phone gets one menu (a disclosure: no script).\n sidebar: \"hidden lg:block\",\n sidebarList: \"flex flex-col gap-6 lg:sticky lg:top-20\",\n group: \"flex flex-col gap-1\",\n groupLabel: \"px-3 text-xs font-semibold text-fg-secondary\",\n groupLink:\n \"flex h-9 items-center gap-2.5 whitespace-nowrap rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline hover:bg-surface-2 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current]:bg-accent-soft aria-[current]:text-fg [&>svg]:h-4 [&>svg]:w-4\",\n groupItems: \"m-0 flex list-none flex-col gap-0.5 p-0\",\n menu: \"group lg:hidden\",\n menuSummary:\n \"flex h-11 cursor-pointer list-none items-center justify-between gap-3 rounded-lg border border-border-default bg-surface-1 px-3.5 text-sm font-medium text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden group-open:border-accent\",\n menuChevron: \"h-4 w-4 shrink-0 text-fg-secondary transition-transform group-open:rotate-180 motion-reduce:transition-none\",\n menuPanel: \"mt-2 rounded-lg border border-border-default bg-surface-1 p-2\",\n navBadge: \"ml-auto font-mono text-[11px] text-fg-secondary\",\n header: \"flex flex-wrap items-center gap-x-4 gap-y-3 border-b border-border-subtle pb-5\",\n content: \"flex min-w-0 flex-col gap-6\",\n section: \"rounded-xl border border-border-default bg-surface-1\",\n sectionHead: \"flex flex-col gap-1 px-5 pt-5\",\n sectionTitle: \"text-base font-bold text-fg\",\n sectionDescription: \"text-sm text-fg-secondary\",\n sectionBody: \"flex flex-col gap-4 px-5 py-5\",\n sectionFooter: \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle bg-surface-2/40 px-5 py-3 rounded-b-xl\",\n // The flat look: no card, hairline-divided rows (Linear / Vercel / GitHub settings).\n rows: \"flex flex-col divide-y divide-border-subtle border-y border-border-subtle\",\n row: \"flex flex-col gap-2 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6\",\n rowText: \"flex min-w-0 flex-col gap-0.5\",\n rowLabel: \"text-sm font-medium text-fg\",\n rowDescription: \"text-sm text-fg-secondary\",\n rowControl: \"flex min-w-0 shrink-0 items-center gap-2 text-sm text-fg sm:max-w-[60%] sm:justify-end\",\n },\n variants: {\n variant: {\n card: {},\n flat: {\n content: \"max-w-[45rem] gap-10\",\n section: \"rounded-none border-0 bg-transparent\",\n sectionHead: \"gap-0.5 px-0 pb-3 pt-0\",\n sectionBody: \"gap-0 px-0 py-0\",\n sectionFooter: \"justify-start border-t-0 bg-transparent px-0 pb-0 pt-4\",\n },\n },\n },\n defaultVariants: { variant: \"card\" },\n});\n\nexport interface SettingsNavItem {\n /** Its id (the anchor of a section on one long page). */\n id: string;\n /** Its words. */\n label: string;\n /** Where it leads: a page (`/settings/billing`) or an anchor (`#billing`). */\n href: string;\n /** Its icon (a signature icon). */\n icon?: React.ReactNode;\n /** The section shown now: `aria-current`. */\n current?: boolean;\n /** A small count or status after the label (`3`, `New`). */\n badge?: React.ReactNode;\n}\n\nexport interface SettingsNavGroup {\n /** Its id. */\n id: string;\n /** The group's title (`Library`, `Money`). */\n label: string;\n items: readonly SettingsNavItem[];\n}\n\n/** Past this many sections a phone gets one menu instead of a strip of chips (« never more than about 5 items in a horizontal tab bar »). */\nexport const SETTINGS_CHIPS_MAX = 5;\n\nconst isGrouped = (nav: readonly SettingsNavItem[] | readonly SettingsNavGroup[]): nav is readonly SettingsNavGroup[] => nav.length > 0 && \"items\" in nav[0]!;\n\nexport type SettingsLayoutProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** The screen's title (`Settings`). */\n title?: React.ReactNode;\n /** One sentence under it. */\n description?: React.ReactNode;\n /** What the screen is about, above the sections: who the account is, with their actions. Replaces `title` and `description`. */\n header?: React.ReactNode;\n /** The sections, alone or in titled groups. */\n nav: readonly SettingsNavItem[] | readonly SettingsNavGroup[];\n /** The navigation's accessible name (`Settings sections`). */\n navLabel: string;\n /** The app's link component for the sections (Next's `Link`); an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /**\n * `card` (each form a titled card) or `flat` (one calm column of at most 45rem, rows divided by hairlines).\n * @default \"card\"\n */\n variant?: \"card\" | \"flat\";\n /** The current section's forms (`SettingsSection`). */\n children: React.ReactNode;\n};\n\nfunction NavLink({ item, Link, className }: { item: SettingsNavItem; Link: React.ElementType; className: string }) {\n const s = settingsLayout();\n return (\n <Link href={item.href} className={className} aria-current={item.current ? (item.href.startsWith(\"#\") ? \"location\" : \"page\") : undefined}>\n {item.icon}\n {item.label}\n {item.badge !== undefined && <span className={s.navBadge()}>{item.badge}</span>}\n </Link>\n );\n}\n\nfunction NavGroups({ groups, Link }: { groups: readonly SettingsNavGroup[]; Link: React.ElementType }) {\n const s = settingsLayout();\n return (\n <div className={s.sidebarList()}>\n {groups.map((group) => (\n <div key={group.id} className={s.group()}>\n <h2 className={s.groupLabel()}>{group.label}</h2>\n <ul className={s.groupItems()}>\n {group.items.map((item) => (\n <li key={item.id}>\n <NavLink item={item} Link={Link} className={s.groupLink()} />\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n );\n}\n\n/** A settings screen: sections on the left (chips on a phone, one menu past five), forms on the right. */\nexport function SettingsLayout({ title, description, header, nav, navLabel, linkAs: Link = \"a\", variant = \"card\", className, children, ...props }: SettingsLayoutProps) {\n const s = settingsLayout({ variant });\n const groups: readonly SettingsNavGroup[] = isGrouped(nav) ? nav : [{ id: \"sections\", label: \"\", items: nav }];\n const items = groups.flatMap((g) => g.items);\n const asMenu = items.length > SETTINGS_CHIPS_MAX;\n const current = items.find((i) => i.current);\n return (\n <div className={s.root({ className })} {...props}>\n {header ? (\n <div className={s.header()}>{header}</div>\n ) : (\n (title || description) && (\n <div className={s.head()}>\n {title && <h1 className={s.title()}>{title}</h1>}\n {description && <p className={s.description()}>{description}</p>}\n </div>\n )\n )}\n <div className={s.columns()}>\n {asMenu ? (\n <nav aria-label={navLabel}>\n <details className={s.menu()} data-settings-menu=\"\">\n <summary className={s.menuSummary()}>\n <span className=\"truncate\">{current?.label ?? navLabel}</span>\n <svg aria-hidden viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={s.menuChevron()}>\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </summary>\n <div className={s.menuPanel()}>\n <NavGroups groups={groups} Link={Link} />\n </div>\n </details>\n <div className={s.sidebar()}>\n <NavGroups groups={groups} Link={Link} />\n </div>\n </nav>\n ) : (\n <nav aria-label={navLabel} className={s.nav()}>\n <ul className={s.navList()}>\n {items.map((item) => (\n <li key={item.id}>\n <NavLink item={item} Link={Link} className={s.navLink()} />\n </li>\n ))}\n </ul>\n </nav>\n )}\n <div className={s.content()}>{children}</div>\n </div>\n </div>\n );\n}\n\nexport interface SectionSaveLabels {\n /** “Save”. */\n save: string;\n /** “Saving…”. */\n saving: string;\n /** “Saved”. */\n saved: string;\n /** What failed, one sentence (“Could not save. Try again.”). */\n error: string;\n /** “Undo” — only shown after a save, when `onUndo` is given. */\n undo: string;\n /** “Discard changes” — only shown while dirty, when `onDiscard` is given. */\n discard: string;\n}\n\n/** The one save of a section: its dirty state, its status, its words. */\nexport interface SectionSave {\n /** Whether a field differs from what is saved: the save is enabled only then. */\n dirty: boolean;\n /** Where the save is (`useSaveStatus().status`). */\n status: SaveStatus;\n /** Saves the section (the form's submit, Enter included). */\n onSave: () => void;\n /** Puts the previous values back after a save, optimistic update included. */\n onUndo?: () => void;\n /** Throws the edits away while dirty. */\n onDiscard?: () => void;\n labels: SectionSaveLabels;\n}\n\nexport type SettingsSectionProps = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n /**\n * One save for the whole section: the section becomes a form, the foot shows the Save (enabled while dirty), the\n * result in a live region (saved, or the error as an alert) and an undo after a save. Replaces `footer`.\n */\n save?: SectionSave;\n /** The section's title: it names the region (and is its anchor's target when `id` is set). */\n title: React.ReactNode;\n /** What these settings change, in one sentence. */\n description?: React.ReactNode;\n /** At the foot: the section's own save (a Button), a note. */\n footer?: React.ReactNode;\n /**\n * `card` (a titled card) or `flat` (no card: a title, then hairline-divided `SettingsRow`s).\n * @default \"card\"\n */\n variant?: \"card\" | \"flat\";\n /** The fields (or, flat, the `SettingsRow`s). A section of a title, a sentence and a foot action has none. */\n children?: React.ReactNode;\n};\n\n/** A group of settings: a titled card with its fields and its own save, or flat rows under a title. */\nexport function SettingsSection({ title, description, footer, save, variant = \"card\", id, className, children, ...props }: SettingsSectionProps) {\n const s = settingsLayout({ variant });\n const auto = React.useId();\n const titleId = `${id ?? auto}-title`;\n const head = (\n <div className={s.sectionHead()}>\n <h2 id={titleId} className={s.sectionTitle()}>\n {title}\n </h2>\n {description && <p className={s.sectionDescription()}>{description}</p>}\n </div>\n );\n const body = children === undefined ? null : (\n <div className={variant === \"flat\" ? s.rows() : s.sectionBody()} data-settings-rows={variant === \"flat\" ? \"\" : undefined}>\n {children}\n </div>\n );\n return (\n <section id={id} aria-labelledby={titleId} data-dirty={save?.dirty ? \"\" : undefined} className={s.section({ className })} {...props}>\n {save ? (\n <form\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n if (save.dirty && save.status !== SaveStatus.Saving) save.onSave();\n }}\n >\n {head}\n {body}\n <div className={s.sectionFooter()}>\n <SaveFeedback save={save} />\n {save.onDiscard && save.dirty ? (\n <Button type=\"button\" variant=\"ghost\" onClick={save.onDiscard}>\n {save.labels.discard}\n </Button>\n ) : null}\n <Button type=\"submit\" variant=\"primary\" disabled={!save.dirty || save.status === SaveStatus.Saving} loading={save.status === SaveStatus.Saving}>\n {save.labels.save}\n </Button>\n </div>\n </form>\n ) : (\n <>\n {head}\n {body}\n {footer && <div className={s.sectionFooter()}>{footer}</div>}\n </>\n )}\n </section>\n );\n}\n\n/** The result of the last save: « Saved » (with Undo) in a polite live region, a failure as an alert. The region stays mounted so the change is announced. */\nfunction SaveFeedback({ save }: { save: SectionSave }) {\n const failed = save.status === SaveStatus.Error;\n const saved = save.status === SaveStatus.Saved;\n return (\n <div className=\"mr-auto flex min-w-0 items-center gap-2 text-sm\">\n <span role={failed ? \"alert\" : \"status\"} aria-live={failed ? \"assertive\" : \"polite\"} className={failed ? \"text-danger-text\" : \"text-fg-secondary\"}>\n {failed ? save.labels.error : saved ? save.labels.saved : save.status === SaveStatus.Saving ? save.labels.saving : \"\"}\n </span>\n {saved && save.onUndo ? (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={save.onUndo}>\n {save.labels.undo}\n </Button>\n ) : null}\n </div>\n );\n}\n\nexport type SettingsRowProps = Omit<React.ComponentProps<\"div\">, \"title\"> & {\n /** What the setting is (`Username`). */\n label: React.ReactNode;\n /** The help text under it, one sentence. */\n description?: React.ReactNode;\n /** The id of the control, so the label names it (a `<label htmlFor>`). Leave out for a plain value. */\n htmlFor?: string;\n /** The control or the value, on the right (stacked under the label on a phone). */\n children?: React.ReactNode;\n};\n\n/** One setting: its label and help on the left, its control or value on the right. Put rows in a flat `SettingsSection`. */\nexport function SettingsRow({ label, description, htmlFor, className, children, ...props }: SettingsRowProps) {\n const s = settingsLayout();\n const Label = htmlFor ? \"label\" : \"span\";\n return (\n <div className={s.row({ className })} {...props}>\n <div className={s.rowText()}>\n <Label className={s.rowLabel()} {...(htmlFor ? { htmlFor } : {})}>\n {label}\n </Label>\n {description && <span className={s.rowDescription()}>{description}</span>}\n </div>\n {children !== undefined && <div className={s.rowControl()}>{children}</div>}\n </div>\n );\n}\n","// Server-safe: no hook, no context. `buttonVariants` styles a <Link> or a Server Component without the component.\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { LoaderGlyph } from \"../spinner/glyph\";\nimport { buttonRecipe } from \"./recipe\";\n\nexport const buttonVariants = tv(buttonRecipe);\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>;\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n ButtonVariants & {\n /**\n * The look: `primary` (the accent), `gradient` (accent → accent-2), `secondary` (a raised surface), `outline`,\n * `ghost`, `danger`.\n * @default \"secondary\"\n */\n variant?: ButtonVariants[\"variant\"];\n /**\n * sm · md · lg, or `icon` (a square).\n * @default \"md\"\n */\n size?: ButtonVariants[\"size\"];\n /**\n * `rounded` (an 8 px radius, the radius of a field) · `pill`. `cut` is deprecated (krizaka-ui#393: the chamfer is\n * retired) — it draws `rounded` and warns in development; it will be removed in 4.0.\n * @default \"rounded\"\n */\n shape?: ButtonVariants[\"shape\"];\n /** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */\n asChild?: boolean;\n /**\n * Disables and signals the wait (aria-busy, data-loading) and shows the Krizaka loader glyph before the label; the\n * button keeps its colour.\n */\n loading?: boolean;\n /**\n * Between 0 and 1, while `loading`: the glyph's trace and a light fill across the button grow to the value (an\n * upload, an export).\n */\n progress?: number | null;\n /**\n * How the action ended: `success` draws a check, `error` a cross (and the button shakes its head once); a ring of\n * the status colour opens around it. Clear it to return to rest.\n */\n status?: \"success\" | \"error\" | null;\n /** A message announced politely by assistive technology when it changes (“Saved”, “Upload failed”) — passed translated. */\n announce?: string;\n /**\n * Shows the glyph while `loading` / with a `status`. `false`: you draw your own.\n * @default true\n */\n indicator?: boolean;\n /** @deprecated Since 3.31 (krizaka-ui#393): does nothing — the hover is quiet. Removed in 4.0. */\n shimmer?: boolean;\n /** @deprecated Since 3.31 (krizaka-ui#393): does nothing — a button never moves under the pointer. Removed in 4.0. */\n magnetic?: boolean;\n };\n\nconst STATUS_TEXT = { success: \"text-fg-success\", error: \"text-fg-danger\" } as const;\n\nlet warnedCut = false;\n/** `shape=\"cut\"` (the chamfer) is retired: one warning per page in development, never in production. */\nfunction warnCut() {\n if (warnedCut || typeof process === \"undefined\" || process.env.NODE_ENV === \"production\") return;\n warnedCut = true;\n console.warn('@krizaka/ui/button: shape=\"cut\" is deprecated (krizaka-ui#393) and now draws the default rounded shape. Remove the prop.');\n}\n\nexport function Button({\n asChild,\n loading,\n progress,\n status,\n announce,\n indicator = true,\n shimmer: _shimmer,\n magnetic: _magnetic,\n variant,\n size,\n shape,\n className,\n disabled,\n children,\n ...props\n}: ButtonProps) {\n const Comp = asChild ? Slot.Root : \"button\";\n if (shape === \"cut\") warnCut();\n const known = loading && typeof progress === \"number\" && Number.isFinite(progress);\n const ratio = known ? Math.min(1, Math.max(0, progress)) : 0;\n const glyph = indicator && !asChild && (loading || status) ? (status ?? \"loading\") : null;\n // On a filled variant the glyph stays in the label's colour; on the others it takes the status colour.\n const filled = variant === \"primary\" || variant === \"gradient\";\n const button = (\n <Comp\n type={asChild ? undefined : \"button\"}\n aria-busy={loading || undefined}\n data-loading={loading ? \"\" : undefined}\n data-variant={variant ?? \"secondary\"}\n data-kz-status={status ?? undefined}\n disabled={asChild ? undefined : disabled || loading}\n aria-disabled={asChild && (disabled || loading) ? true : undefined}\n className={buttonVariants({\n variant,\n size,\n shape,\n className: cn(known && \"relative isolate overflow-hidden\", className),\n })}\n {...props}\n >\n {asChild ? (\n children\n ) : (\n <>\n {known && (\n <span aria-hidden className=\"absolute inset-y-0 left-0 -z-10 bg-current opacity-15 transition-[width] duration-500\" style={{ width: `${ratio * 100}%` }} />\n )}\n {glyph && (\n <span\n aria-hidden\n data-slot=\"indicator\"\n className=\"-my-1 grid grid-cols-[1fr] transition-[grid-template-columns,margin,opacity] duration-300 starting:-mr-2 starting:grid-cols-[0fr] starting:opacity-0\"\n >\n <span className=\"overflow-hidden\">\n <LoaderGlyph state={glyph} value={known ? ratio : null} className={cn(\"h-4 w-4\", glyph !== \"loading\" && !filled && STATUS_TEXT[glyph])} />\n </span>\n </span>\n )}\n {children}\n </>\n )}\n </Comp>\n );\n if (announce === undefined || asChild) return button;\n return (\n <>\n {button}\n <span role=\"status\" className=\"sr-only\">\n {announce}\n </span>\n </>\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"size\"> & {\n /** The accessible name (and tooltip) of the button — required, passed translated. */\n label: string;\n /**\n * sm · md · lg, or `icon` (a square).\n * @default \"icon\"\n */\n size?: ButtonProps[\"size\"];\n};\n\n/** An icon-only button: `label` is required, it is its accessible name. Loading or with a status, the glyph takes the icon's place. */\nexport function IconButton({ label, size = \"icon\", children, ...props }: IconButtonProps) {\n const swap = props.indicator !== false && !props.asChild && (props.loading || props.status);\n return (\n <Button aria-label={label} title={label} size={size} {...props}>\n {swap ? null : children}\n </Button>\n );\n}\n","// The Krizaka loader glyph, server-safe: a hexagon (the marks' shape) traced by a segment of light around a node that\n// breathes; determinate, it fills to its value; done, the trace closes and a check (or a cross) draws itself. Animated by\n// motion.css (`kz-loader-*`); still under prefers-reduced-motion — a segment at rest and the node lit, never blank.\nimport type * as React from \"react\";\n\n/** The hexagon, pointy top, radius 9 around (12, 12). */\nconst HEX = \"M12 3 19.79 7.5 19.79 16.5 12 21 4.21 16.5 4.21 7.5Z\";\n\n/** What the glyph shows: a wait (with an optional value), or how it ended. */\nexport type LoaderGlyphState = \"loading\" | \"success\" | \"error\";\n\nexport interface LoaderGlyphProps extends Omit<React.ComponentProps<\"svg\">, \"children\"> {\n /** loading · success · error. */\n state?: LoaderGlyphState;\n /** Between 0 and 1: a determinate wait, the trace grows to it. Absent: indeterminate. */\n value?: number | null;\n}\n\n/** The glyph alone (decorative): the Spinner, the Loader and the Button's `loading` draw it. */\nexport function LoaderGlyph({ state = \"loading\", value, ...props }: LoaderGlyphProps) {\n const known = typeof value === \"number\" && Number.isFinite(value);\n const fill = state !== \"loading\" ? 100 : known ? Math.round(Math.min(1, Math.max(0, value)) * 100) : null;\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden data-state={state} data-progress={fill === null ? undefined : fill} {...props}>\n <path d={HEX} stroke=\"currentColor\" strokeOpacity=\"0.22\" strokeWidth=\"2.2\" strokeLinejoin=\"round\" />\n <path\n d={HEX}\n pathLength={100}\n stroke=\"currentColor\"\n strokeWidth=\"2.2\"\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n className=\"kz-loader-trace\"\n strokeDasharray={fill === null ? undefined : `${fill} ${100 - fill}`}\n />\n {state === \"loading\" && <circle cx=\"12\" cy=\"12\" r=\"2.6\" fill=\"currentColor\" className=\"kz-loader-node\" />}\n {state === \"success\" && <path d=\"M8.3 12.3l2.6 2.6 4.9-5.3\" pathLength={20} stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"kz-loader-mark\" />}\n {state === \"error\" && <path d=\"M9.4 9.4l5.2 5.2M14.6 9.4l-5.2 5.2\" pathLength={20} stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" className=\"kz-loader-mark\" />}\n </svg>\n );\n}\n","// The button's variants, written once as classes of the Krizaka tokens: the web wears them with `tv(buttonRecipe)`\n// (button.tsx), React Native reads the same classes into styles (native/button.tsx, `nativeRecipe`).\nimport { defineRecipe } from \"../recipes/define\";\n\nexport const buttonRecipe = defineRecipe({\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition duration-150 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 \" +\n \"disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98] \" +\n // Busy is not unavailable: a loading button keeps its colour (it is disabled only so it is not pressed twice).\n \"[&[data-loading]]:opacity-100\",\n variants: {\n variant: {\n primary: \"bg-accent text-on-accent shadow-sm hover:bg-accent-hover\",\n // The brand's signature: accent → accent-2 (on-accent reads on both stops, tested in @krizaka/tokens).\n gradient: \"bg-linear-to-r from-accent to-accent-2 text-on-accent shadow-sm hover:brightness-110\",\n secondary: \"border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3\",\n outline: \"border border-border-default bg-transparent text-fg hover:border-accent hover:text-fg-accent\",\n ghost: \"text-fg-secondary hover:bg-surface-2 hover:text-fg\",\n // The border and the tint carry the danger; the label stays the primary text role, legible on the tint at rest\n // and hovered over every surface (`text-fg-danger` is for text set directly on a surface).\n danger: \"border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20\",\n },\n size: { sm: \"h-8 px-3 text-xs\", md: \"h-10 px-4 text-sm\", lg: \"h-12 px-6 text-sm\", icon: \"h-10 w-10\" },\n // One clean shape (krizaka-ui#393): an 8 px radius (`--kz-radius-sm`, the radius of a field — a button beside an\n // input lines up with it), the pill on demand. `cut` (the retired chamfer) is a deprecated alias of `rounded`.\n shape: { rounded: \"rounded-sm\", pill: \"rounded-full\", cut: \"rounded-sm\" },\n },\n defaultVariants: { variant: \"secondary\", size: \"md\", shape: \"rounded\" },\n});\n"],"mappings":"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","names":["React","tv","jsx","jsxs","Fragment","jsx","jsxs","tv"]}
|
package/dist/site-footer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as tailwind_variants from 'tailwind-variants';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { S as SiteId, F as FamilyLabels } from './sites-
|
|
3
|
+
import { S as SiteId, F as FamilyLabels } from './sites-DlR8jicc.js';
|
|
4
4
|
|
|
5
5
|
declare const siteFooter: tailwind_variants.TVReturnType<{
|
|
6
6
|
variant: {
|
package/dist/site-footer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** A site of the family. */
|
|
2
|
-
type SiteId = "krizaka" | "orazaka" | "orochia" | "yasakia" | "yororis" | "yomizaka" | "kachizaka" | "akinaza" | "ui" | "java" | "motion" | "github";
|
|
2
|
+
type SiteId = "krizaka" | "orazaka" | "orochia" | "yasakia" | "yororis" | "yomizaka" | "kachizaka" | "akinaza" | "ui" | "java" | "ts" | "motion" | "github";
|
|
3
3
|
/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */
|
|
4
4
|
type SiteKind = "company" | "product" | "open-source";
|
|
5
5
|
/**
|
package/dist/sites.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** A site of the family. */
|
|
2
|
-
type SiteId = "krizaka" | "orazaka" | "orochia" | "yasakia" | "yororis" | "yomizaka" | "kachizaka" | "akinaza" | "ui" | "java" | "motion" | "github";
|
|
2
|
+
type SiteId = "krizaka" | "orazaka" | "orochia" | "yasakia" | "yororis" | "yomizaka" | "kachizaka" | "akinaza" | "ui" | "java" | "ts" | "motion" | "github";
|
|
3
3
|
/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */
|
|
4
4
|
type SiteKind = "company" | "product" | "open-source";
|
|
5
5
|
/**
|
package/dist/sites.js
CHANGED
|
@@ -11,6 +11,7 @@ var sites = [
|
|
|
11
11
|
site({ id: "akinaza", name: "Akinaza", url: "https://www.akinaza.com", kind: "product", group: "products", status: "development", mark: "akinaza", repo: "https://github.com/krizaka/akinaza" }),
|
|
12
12
|
site({ id: "ui", name: "Krizaka UI", url: "https://ui.krizaka.com", kind: "open-source", group: "open-source", status: "released", mark: "krizaka", repo: "https://github.com/krizaka/krizaka-ui" }),
|
|
13
13
|
site({ id: "java", name: "Krizaka Java", url: "https://java.krizaka.com", kind: "open-source", group: "open-source", status: "released", mark: "krizaka", repo: "https://github.com/krizaka/krizaka-build" }),
|
|
14
|
+
site({ id: "ts", name: "Krizaka TS", url: "https://ts.krizaka.com", kind: "open-source", group: "open-source", status: "released", mark: "krizaka", repo: "https://github.com/krizaka/krizaka-ts" }),
|
|
14
15
|
// The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.
|
|
15
16
|
site({ id: "motion", name: "Krizaka Motion", url: "https://krizaka.github.io/krizaka-motion", kind: "open-source", group: "open-source", status: "development", mark: "krizaka", repo: "https://github.com/krizaka/krizaka-motion" }),
|
|
16
17
|
site({ id: "github", name: "GitHub", url: "https://github.com/krizaka", kind: "open-source", group: "github", status: "released", mark: "github", repo: "https://github.com/krizaka" })
|
|
@@ -46,6 +47,7 @@ var familyLabels = {
|
|
|
46
47
|
akinaza: "Running a shop in Qu\xE9bec, for less \u2014 Loi 25 by design.",
|
|
47
48
|
ui: "Components, tokens, icons and charts \u2014 web and React Native.",
|
|
48
49
|
java: "Spring Boot building blocks on Maven Central.",
|
|
50
|
+
ts: "TypeScript backend building blocks on npm.",
|
|
49
51
|
motion: "The motion lab: the kit's motion, played live with its knobs.",
|
|
50
52
|
github: "Every repository, in the open."
|
|
51
53
|
},
|
|
@@ -71,6 +73,7 @@ var familyLabels = {
|
|
|
71
73
|
akinaza: "Tenir une boutique au Qu\xE9bec, pour moins cher \u2014 Loi 25 d\xE8s la conception.",
|
|
72
74
|
ui: "Composants, jetons, ic\xF4nes et graphiques \u2014 web et React Native.",
|
|
73
75
|
java: "Des briques Spring Boot sur Maven Central.",
|
|
76
|
+
ts: "Des briques backend TypeScript sur npm.",
|
|
74
77
|
motion: "Le labo du mouvement : le mouvement du kit, jou\xE9 en direct, avec ses r\xE9glages.",
|
|
75
78
|
github: "Tous les d\xE9p\xF4ts, au grand jour."
|
|
76
79
|
},
|