@ceebee/ui 0.6.0 → 1.0.1

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/client.js CHANGED
@@ -1,24 +1,146 @@
1
1
  "use client";
2
- import { createContext, forwardRef, useContext, useMemo, useId, useState, useRef, useEffect, useCallback, Children, isValidElement, Fragment as Fragment$1, cloneElement, useReducer } from 'react';
3
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import { motion } from 'motion/react';
5
- import { Select as Select$1 } from '@base-ui/react/select';
6
- import { ChevronsUpDown, ChevronDown, ChevronRight, AlertTriangle, ChevronLeft, ZoomOut, RotateCcw, ZoomIn, X, XCircle, CheckCircle2, Info, Check, Minus, CalendarDays, Clock, Upload as Upload$1, Plus, Search, Star, PanelLeftOpen, PanelLeftClose, ArrowUp, ArrowDown } from 'lucide-react';
7
- import { Checkbox as Checkbox$1 } from '@base-ui/react/checkbox';
8
- import { Radio } from '@base-ui/react/radio';
9
- import { RadioGroup as RadioGroup$1 } from '@base-ui/react/radio-group';
10
- import { Switch as Switch$1 } from '@base-ui/react/switch';
11
- import { Combobox } from '@base-ui/react/combobox';
12
- import { Popover as Popover$1 } from '@base-ui/react/popover';
13
- import { Slider as Slider$1 } from '@base-ui/react/slider';
2
+ import { theme, ConfigProvider, Progress } from 'antd';
3
+ export * from 'antd';
4
+ import { createContext, useState, useCallback, useEffect, useMemo, useContext, Children } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+ export { default as enUSLocale } from 'antd/locale/en_US.js';
7
+ export { default as enUSDatePickerLocale } from 'antd/lib/date-picker/locale/en_US.js';
8
+ export { default as idIDLocale } from 'antd/locale/id_ID.js';
9
+ export { default as zhCNLocale } from 'antd/locale/zh_CN.js';
14
10
  import { Dialog } from '@base-ui/react/dialog';
15
- import { Tooltip as Tooltip$1 } from '@base-ui/react/tooltip';
11
+ import { Search, Check, ChevronRight, X, PanelLeftOpen, PanelLeftClose, Info, XCircle, AlertTriangle, CheckCircle2 } from 'lucide-react';
16
12
  import { Toast } from '@base-ui/react/toast';
17
- import { Tabs as Tabs$1 } from '@base-ui/react/tabs';
18
- import { Menu as Menu$1 } from '@base-ui/react/menu';
19
- import { Accordion } from '@base-ui/react/accordion';
20
- import useEmblaCarousel from 'embla-carousel-react';
13
+ import { Popover } from '@base-ui/react/popover';
14
+ import { motion } from 'motion/react';
21
15
 
16
+ var SKIN_LINK_ID = "cb-skin";
17
+ function ThemeBridge({ children, mode, theme: theme$1 }) {
18
+ const [skinToken, setSkinToken] = useState({ token: {}, components: {} });
19
+ const refresh = useCallback(() => {
20
+ setSkinToken(readCeebeeThemeToken(document.documentElement));
21
+ }, []);
22
+ useEffect(() => {
23
+ refresh();
24
+ const rootObserver = new MutationObserver(refresh);
25
+ rootObserver.observe(document.documentElement, {
26
+ attributes: true,
27
+ attributeFilter: ["data-theme"]
28
+ });
29
+ const headObserver = new MutationObserver((records) => {
30
+ const skinChanged = records.some((record) => [...record.addedNodes, ...record.removedNodes].some((node) => node instanceof HTMLElement && node.id === SKIN_LINK_ID));
31
+ if (skinChanged) refresh();
32
+ });
33
+ headObserver.observe(document.head, { childList: true });
34
+ const onSkinLoad = (event) => {
35
+ if (event.target instanceof HTMLElement && event.target.id === SKIN_LINK_ID) refresh();
36
+ };
37
+ document.addEventListener("load", onSkinLoad, true);
38
+ return () => {
39
+ rootObserver.disconnect();
40
+ headObserver.disconnect();
41
+ document.removeEventListener("load", onSkinLoad, true);
42
+ };
43
+ }, [refresh]);
44
+ const mergedTheme = useMemo(() => ({
45
+ ...theme$1,
46
+ algorithm: theme$1?.algorithm ?? (mode === "dark" ? theme.darkAlgorithm : theme.defaultAlgorithm),
47
+ cssVar: theme$1?.cssVar ?? { prefix: "cb-ant" },
48
+ token: { ...skinToken.token, ...theme$1?.token },
49
+ components: mergeComponents(skinToken.components, theme$1?.components)
50
+ }), [mode, skinToken, theme$1]);
51
+ useEffect(() => {
52
+ ConfigProvider.config({
53
+ holderRender: (holder) => /* @__PURE__ */ jsx(ConfigProvider, { theme: mergedTheme, children: holder })
54
+ });
55
+ }, [mergedTheme]);
56
+ return /* @__PURE__ */ jsx(ConfigProvider, { theme: mergedTheme, children });
57
+ }
58
+ function mergeComponents(base, override) {
59
+ if (!override) return base;
60
+ const merged = { ...base };
61
+ for (const [name, tokens] of Object.entries(override)) {
62
+ merged[name] = { ...merged[name], ...tokens };
63
+ }
64
+ return merged;
65
+ }
66
+ function readCeebeeThemeToken(root) {
67
+ const probe = document.createElement("span");
68
+ probe.style.position = "fixed";
69
+ probe.style.pointerEvents = "none";
70
+ probe.style.visibility = "hidden";
71
+ root.append(probe);
72
+ const color = (name) => resolveCssColor(probe, name);
73
+ const length = (name) => resolveCssLength(probe, name);
74
+ const tokens = {
75
+ colorPrimary: color("--cb-tone-brand"),
76
+ colorInfo: color("--cb-tone-info"),
77
+ colorSuccess: color("--cb-tone-success"),
78
+ colorWarning: color("--cb-tone-warning"),
79
+ colorError: color("--cb-tone-danger"),
80
+ colorText: color("--cb-fg"),
81
+ colorTextSecondary: color("--cb-fg-muted"),
82
+ colorTextTertiary: color("--cb-fg-subtle"),
83
+ colorTextLightSolid: color("--cb-fg-on-brand"),
84
+ colorBgBase: color("--cb-bg"),
85
+ colorBgContainer: color("--cb-surface"),
86
+ colorBgElevated: color("--cb-surface-raised"),
87
+ colorBorder: color("--cb-border-strong"),
88
+ colorBorderSecondary: color("--cb-border"),
89
+ fontFamily: "var(--cb-font-sans)",
90
+ fontSize: length("--cb-text-sm"),
91
+ fontSizeSM: length("--cb-text-xs"),
92
+ fontSizeLG: length("--cb-text-md"),
93
+ borderRadius: length("--cb-radius-sm"),
94
+ borderRadiusLG: length("--cb-radius-md"),
95
+ controlHeight: length("--cb-control-height-md"),
96
+ controlHeightSM: length("--cb-control-height-sm"),
97
+ controlHeightLG: length("--cb-control-height-lg"),
98
+ lineWidth: length("--cb-border-width"),
99
+ boxShadow: "var(--cb-shadow-md)",
100
+ boxShadowSecondary: "var(--cb-shadow-sm)"
101
+ };
102
+ const components = {};
103
+ const trackBg = color("--cb-brand-300");
104
+ const trackHoverBg = color("--cb-brand-400");
105
+ if (trackBg && trackHoverBg) {
106
+ components.Slider = {
107
+ trackBg,
108
+ trackHoverBg,
109
+ handleColor: trackBg,
110
+ dotActiveBorderColor: trackBg
111
+ };
112
+ }
113
+ probe.remove();
114
+ return {
115
+ token: Object.fromEntries(
116
+ Object.entries(tokens).filter(([, value]) => value !== void 0)
117
+ ),
118
+ components
119
+ };
120
+ }
121
+ function resolveCssLength(probe, name) {
122
+ probe.style.width = `var(${name})`;
123
+ const value = Number.parseFloat(getComputedStyle(probe).width);
124
+ probe.style.removeProperty("width");
125
+ return Number.isFinite(value) ? value : void 0;
126
+ }
127
+ function resolveCssColor(probe, name) {
128
+ probe.style.color = `var(${name})`;
129
+ const value = getComputedStyle(probe).color;
130
+ probe.style.removeProperty("color");
131
+ if (!value) return void 0;
132
+ if (/^(?:#|rgb|hsl|hsv)/i.test(value)) return value;
133
+ const canvas = document.createElement("canvas");
134
+ canvas.width = 1;
135
+ canvas.height = 1;
136
+ const context = canvas.getContext("2d", { willReadFrequently: true });
137
+ if (!context) return void 0;
138
+ context.clearRect(0, 0, 1, 1);
139
+ context.fillStyle = value;
140
+ context.fillRect(0, 0, 1, 1);
141
+ const [red = 0, green = 0, blue = 0, alpha = 255] = context.getImageData(0, 0, 1, 1).data;
142
+ return `rgba(${red}, ${green}, ${blue}, ${alpha / 255})`;
143
+ }
22
144
  var DEFAULT_LABELS = {
23
145
  dismiss: "Dismiss",
24
146
  close: "Close",
@@ -44,11 +166,7 @@ var DEFAULT_LABELS = {
44
166
  decrease: "Decrease",
45
167
  expandNavigation: "Expand navigation",
46
168
  collapseNavigation: "Collapse navigation",
47
- back: "Back",
48
- next: "Next",
49
- done: "Done",
50
- skip: "Skip",
51
- progress: (current, total) => `${current} of ${total}`
169
+ next: "Next"
52
170
  };
53
171
  var LabelsContext = createContext(DEFAULT_LABELS);
54
172
  function LabelsProvider({ children, labels }) {
@@ -102,7 +220,8 @@ var STORAGE_KEY = "cb-theme";
102
220
  function ThemeProvider({
103
221
  children,
104
222
  defaultChoice = "system",
105
- persist = true
223
+ persist = true,
224
+ antdTheme: antdTheme2
106
225
  }) {
107
226
  const [choice, setChoiceState] = useState(defaultChoice);
108
227
  const [systemDark, setSystemDark] = useState(false);
@@ -131,7 +250,7 @@ function ThemeProvider({
131
250
  [persist]
132
251
  );
133
252
  const resolved = choice === "system" ? systemDark ? "dark" : "light" : choice;
134
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { choice, setChoice, resolved }, children });
253
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { choice, setChoice, resolved }, children: /* @__PURE__ */ jsx(ThemeBridge, { mode: resolved, theme: antdTheme2, children }) });
135
254
  }
136
255
  function useTheme() {
137
256
  const value = useContext(ThemeContext);
@@ -143,2737 +262,171 @@ function useTheme() {
143
262
  function cn(...parts) {
144
263
  return parts.filter(Boolean).join(" ");
145
264
  }
146
- var Button = forwardRef(function Button2({
147
- variant = "solid",
148
- tone = "brand",
149
- size = "md",
150
- loading = false,
151
- iconStart,
152
- iconEnd,
153
- motion: motionProp = true,
154
- className,
155
- children,
156
- disabled,
157
- ...rest
158
- }, ref) {
159
- const { enabled, spring } = useMotionSettings();
160
- const animate = enabled && motionProp;
161
- const iconOnly = children === void 0 || children === null || children === false || children === "";
162
- return /* @__PURE__ */ jsxs(
163
- motion.button,
164
- {
165
- ref,
166
- className: cn(
167
- "cb-button",
168
- `cb-button--${variant}`,
169
- `cb-button--${size}`,
170
- iconOnly && "cb-button--icon",
171
- className
172
- ),
173
- "data-tone": tone,
174
- "data-loading": loading || void 0,
175
- disabled: disabled ?? loading,
176
- "aria-busy": loading || void 0,
177
- whileTap: animate ? { scale: 0.97 } : void 0,
178
- whileHover: animate ? { y: -1 } : void 0,
179
- transition: spring("snappy"),
180
- ...rest,
181
- children: [
182
- loading ? /* @__PURE__ */ jsx("span", { className: "cb-button__spinner", "aria-hidden": "true" }) : iconStart,
183
- iconOnly ? null : /* @__PURE__ */ jsx("span", { className: "cb-button__label", children }),
184
- loading || iconOnly ? null : iconEnd
185
- ]
186
- }
187
- );
188
- });
189
- var FloatButton = forwardRef(function FloatButton2({
190
- label,
191
- visibleLabel = false,
192
- icon,
193
- tone = "brand",
194
- size = "md",
195
- placement = "bottom-end",
196
- type = "button",
197
- "aria-label": _ariaLabel,
198
- "aria-labelledby": _ariaLabelledBy,
199
- style: _style,
200
- ...rest
201
- }, ref) {
202
- const {
203
- className: _className,
204
- role: _role,
205
- color: _color,
206
- children: _children,
207
- "data-tone": _dataTone,
208
- ...safeRest
209
- } = rest;
210
- return /* @__PURE__ */ jsxs(
211
- "button",
212
- {
213
- ref,
214
- type,
215
- ...safeRest,
216
- className: cn(
217
- "cb-float-button",
218
- `cb-float-button--${size}`,
219
- `cb-float-button--${placement}`,
220
- visibleLabel && "cb-float-button--label-visible"
221
- ),
222
- "data-tone": tone,
223
- "aria-label": label,
224
- children: [
225
- icon ? /* @__PURE__ */ jsx("span", { className: "cb-float-button__icon", "aria-hidden": "true", children: icon }) : null,
226
- visibleLabel ? /* @__PURE__ */ jsx("span", { className: "cb-float-button__label", children: label }) : null
227
- ]
228
- }
229
- );
230
- });
231
- var FieldContext = createContext(null);
232
- function useFieldWiring() {
233
- return useContext(FieldContext);
234
- }
235
- function Field({ label, hint, error, required = false, labelHidden = false, className, children }) {
236
- const base = useId();
237
- const controlId = `${base}-control`;
238
- const hintId = hint ? `${base}-hint` : void 0;
239
- const errorId = error && error !== true ? `${base}-error` : void 0;
240
- const describedBy = [hintId, errorId].filter(Boolean).join(" ") || void 0;
241
- return /* @__PURE__ */ jsx(FieldContext.Provider, { value: { controlId, describedBy, invalid: Boolean(error), required }, children: /* @__PURE__ */ jsxs("div", { className: cn("cb-field", className), "data-invalid": error ? "" : void 0, children: [
242
- /* @__PURE__ */ jsxs("label", { className: cn("cb-field__label", labelHidden && "cb-visually-hidden"), htmlFor: controlId, children: [
243
- label,
244
- required ? /* @__PURE__ */ jsx("span", { className: "cb-field__required", "aria-hidden": "true", children: "*" }) : null
245
- ] }),
246
- children,
247
- hint ? /* @__PURE__ */ jsx("p", { className: "cb-field__hint", id: hintId, children: hint }) : null,
248
- error && error !== true ? /* @__PURE__ */ jsx("p", { className: "cb-field__error", id: errorId, role: "alert", children: error }) : null
249
- ] }) });
250
- }
251
- var Input = forwardRef(function Input2({ size = "md", invalid, className, ...rest }, ref) {
252
- const field = useFieldWiring();
253
- return /* @__PURE__ */ jsx(
254
- "input",
255
- {
256
- ref,
257
- className: cn("cb-input", `cb-input--${size}`, className),
258
- id: rest.id ?? field?.controlId,
259
- "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
260
- "aria-invalid": invalid ?? field?.invalid ? true : void 0,
261
- required: rest.required ?? field?.required,
262
- ...rest
263
- }
264
- );
265
- });
266
- var Textarea = forwardRef(function Textarea2({ invalid, className, ...rest }, ref) {
267
- const field = useFieldWiring();
268
- return /* @__PURE__ */ jsx(
269
- "textarea",
270
- {
271
- ref,
272
- className: cn("cb-input", "cb-input--textarea", className),
273
- id: rest.id ?? field?.controlId,
274
- "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
275
- "aria-invalid": invalid ?? field?.invalid ? true : void 0,
276
- required: rest.required ?? field?.required,
277
- ...rest
278
- }
279
- );
280
- });
281
- function Select({
282
- items,
283
- value,
284
- defaultValue,
285
- onValueChange,
286
- placeholder = "Select\u2026",
287
- size = "md",
288
- disabled,
289
- invalid,
290
- name,
291
- id,
292
- className
293
- }) {
294
- const field = useFieldWiring();
295
- return /* @__PURE__ */ jsxs(
296
- Select$1.Root,
297
- {
298
- items,
299
- value,
300
- defaultValue,
301
- onValueChange: (next) => onValueChange?.(next),
302
- disabled,
303
- name,
304
- children: [
305
- /* @__PURE__ */ jsxs(
306
- Select$1.Trigger,
307
- {
308
- id: id ?? field?.controlId,
309
- "aria-describedby": field?.describedBy,
310
- "aria-invalid": invalid ?? field?.invalid ? true : void 0,
311
- className: cn("cb-select", `cb-select--${size}`, className),
312
- children: [
313
- /* @__PURE__ */ jsx(Select$1.Value, { placeholder }),
314
- /* @__PURE__ */ jsx(Select$1.Icon, { className: "cb-select__icon", children: /* @__PURE__ */ jsx(ChevronsUpDown, { size: 16 }) })
315
- ]
316
- }
317
- ),
318
- /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsx(
319
- Select$1.Positioner,
320
- {
321
- sideOffset: 6,
322
- alignItemWithTrigger: false,
323
- className: "cb-select__positioner",
324
- children: /* @__PURE__ */ jsx(Select$1.Popup, { className: "cb-select__popup", children: items.map((item) => /* @__PURE__ */ jsxs(Select$1.Item, { value: item.value, disabled: item.disabled, className: "cb-select__item", children: [
325
- /* @__PURE__ */ jsx("span", { className: "cb-select__check", children: /* @__PURE__ */ jsx(Select$1.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { size: 14 }) }) }),
326
- /* @__PURE__ */ jsx(Select$1.ItemText, { children: item.label })
327
- ] }, item.value)) })
328
- }
329
- ) })
330
- ]
331
- }
332
- );
333
- }
334
- function Checkbox({
335
- label,
336
- checked,
337
- defaultChecked,
338
- indeterminate,
339
- onCheckedChange,
340
- disabled,
341
- name,
342
- value,
343
- description,
344
- className
345
- }) {
346
- const generated = useId();
347
- const field = useFieldWiring();
348
- const id = field?.controlId ?? generated;
349
- const descriptionId = description ? `${id}-description` : void 0;
350
- return /* @__PURE__ */ jsxs("div", { className: cn("cb-choice", className), children: [
351
- /* @__PURE__ */ jsx(
352
- Checkbox$1.Root,
353
- {
354
- id,
355
- checked,
356
- defaultChecked,
357
- indeterminate,
358
- onCheckedChange: (next) => onCheckedChange?.(next),
359
- disabled,
360
- name,
361
- value,
362
- "aria-describedby": [descriptionId, field?.describedBy].filter(Boolean).join(" ") || void 0,
363
- className: "cb-checkbox",
364
- children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, { className: "cb-checkbox__indicator", children: indeterminate ? /* @__PURE__ */ jsx(Minus, { size: 12, strokeWidth: 3 }) : /* @__PURE__ */ jsx(Check, { size: 12, strokeWidth: 3 }) })
365
- }
366
- ),
367
- /* @__PURE__ */ jsxs("div", { className: "cb-choice__text", children: [
368
- /* @__PURE__ */ jsx("label", { htmlFor: id, className: "cb-choice__label", children: label }),
369
- description ? /* @__PURE__ */ jsx("p", { className: "cb-choice__description", id: descriptionId, children: description }) : null
370
- ] })
371
- ] });
265
+
266
+ // src/overlay/command.util.ts
267
+ function rankCommand(command, query) {
268
+ const needle = query.trim().toLowerCase();
269
+ if (!needle) return 1;
270
+ const label = command.label.toLowerCase();
271
+ if (label === needle) return 100;
272
+ if (label.startsWith(needle)) return 80;
273
+ if (label.split(/\s+/).some((word) => word.startsWith(needle))) return 60;
274
+ if (label.includes(needle)) return 40;
275
+ if (command.keywords?.some((keyword) => keyword.toLowerCase().includes(needle))) return 20;
276
+ return 0;
372
277
  }
373
- function RadioGroup({
374
- options,
375
- value,
376
- defaultValue,
377
- onValueChange,
378
- name,
379
- label,
380
- direction = "column",
381
- variant = "list",
382
- disabled,
383
- className
384
- }) {
385
- const groupId = useId();
386
- const field = useFieldWiring();
387
- return /* @__PURE__ */ jsx(
388
- RadioGroup$1,
389
- {
390
- value,
391
- defaultValue,
392
- onValueChange: (next) => onValueChange?.(next),
393
- name,
394
- disabled,
395
- "aria-label": label,
396
- "aria-describedby": field?.describedBy,
397
- className: cn(
398
- "cb-radio-group",
399
- `cb-radio-group--${variant === "segmented" ? "row" : direction}`,
400
- variant === "segmented" && "cb-radio-group--segmented",
401
- className
402
- ),
403
- children: options.map((option) => {
404
- const id = `${groupId}-${option.value}`;
405
- const descriptionId = option.description ? `${id}-description` : void 0;
406
- return /* @__PURE__ */ jsxs("div", { className: "cb-choice", children: [
407
- /* @__PURE__ */ jsx(
408
- Radio.Root,
409
- {
410
- id,
411
- value: option.value,
412
- disabled: option.disabled,
413
- "aria-describedby": descriptionId,
414
- className: "cb-radio",
415
- children: /* @__PURE__ */ jsx(Radio.Indicator, { className: "cb-radio__indicator" })
416
- }
417
- ),
418
- /* @__PURE__ */ jsxs("div", { className: "cb-choice__text", children: [
419
- /* @__PURE__ */ jsx("label", { htmlFor: id, className: "cb-choice__label", children: option.label }),
420
- option.description ? /* @__PURE__ */ jsx("p", { className: "cb-choice__description", id: descriptionId, children: option.description }) : null
421
- ] })
422
- ] }, option.value);
423
- })
424
- }
425
- );
278
+ function filterCommands(commands, query) {
279
+ return commands.map((command, index) => ({ command, score: rankCommand(command, query), index })).filter((entry) => entry.score > 0).sort((a, b) => b.score === a.score ? a.index - b.index : b.score - a.score).map((entry) => entry.command);
426
280
  }
427
- function Switch({
428
- label,
429
- checked,
430
- defaultChecked,
431
- onCheckedChange,
432
- disabled,
433
- name,
434
- description,
435
- justified = false,
436
- className
437
- }) {
438
- const generated = useId();
439
- const field = useFieldWiring();
440
- const id = field?.controlId ?? generated;
441
- const descriptionId = description ? `${id}-description` : void 0;
442
- return /* @__PURE__ */ jsxs("div", { className: cn("cb-choice", justified && "cb-choice--justified", className), children: [
443
- justified ? null : /* @__PURE__ */ jsx(
444
- Switch$1.Root,
445
- {
446
- id,
447
- checked,
448
- defaultChecked,
449
- onCheckedChange: (next) => onCheckedChange?.(next),
450
- disabled,
451
- name,
452
- "aria-describedby": [descriptionId, field?.describedBy].filter(Boolean).join(" ") || void 0,
453
- className: "cb-switch",
454
- children: /* @__PURE__ */ jsx(Switch$1.Thumb, { className: "cb-switch__thumb" })
455
- }
456
- ),
457
- /* @__PURE__ */ jsxs("div", { className: "cb-choice__text", children: [
458
- /* @__PURE__ */ jsx("label", { htmlFor: id, className: "cb-choice__label", children: label }),
459
- description ? /* @__PURE__ */ jsx("p", { className: "cb-choice__description", id: descriptionId, children: description }) : null
460
- ] }),
461
- justified ? /* @__PURE__ */ jsx(
462
- Switch$1.Root,
463
- {
464
- id,
465
- checked,
466
- defaultChecked,
467
- onCheckedChange: (next) => onCheckedChange?.(next),
468
- disabled,
469
- name,
470
- "aria-describedby": [descriptionId, field?.describedBy].filter(Boolean).join(" ") || void 0,
471
- className: "cb-switch",
472
- children: /* @__PURE__ */ jsx(Switch$1.Thumb, { className: "cb-switch__thumb" })
473
- }
474
- ) : null
475
- ] });
281
+ function groupCommands(commands) {
282
+ const groups = [];
283
+ for (const command of commands) {
284
+ const existing = groups.find((entry) => entry.group === command.group);
285
+ if (existing) existing.items.push(command);
286
+ else groups.push({ group: command.group, items: [command] });
287
+ }
288
+ return groups;
476
289
  }
477
- function AutoComplete({
478
- items,
479
- loadItems,
480
- loadDelay = 250,
481
- value,
482
- defaultValue,
483
- onValueChange,
484
- placeholder = "Search\u2026",
485
- emptyMessage = "Nothing matched",
486
- loadingMessage = "Searching\u2026",
487
- errorMessage = "Could not search",
488
- size = "md",
489
- disabled,
490
- invalid,
491
- name,
492
- id,
290
+ function CommandPalette({
291
+ open,
292
+ onOpenChange,
293
+ commands,
294
+ placeholder = "Type a command\u2026",
295
+ emptyMessage = "No matching command",
493
296
  className
494
297
  }) {
495
- const field = useFieldWiring();
496
- const labels = useLabels();
497
- const async = Boolean(loadItems);
498
- const shell = useRef(null);
499
- const [loaded, setLoaded] = useState([]);
500
- const [loading, setLoading] = useState(false);
501
- const [failed, setFailed] = useState(false);
502
- const [chosen, setChosen] = useState(null);
503
- const [open, setOpen] = useState(false);
504
- const available = async ? loaded : items ?? [];
505
- const optionFor = (candidate) => {
506
- if (candidate == null) return null;
507
- if (chosen?.value === candidate) return chosen;
508
- return available.find((item) => item.value === candidate) ?? null;
509
- };
510
- const asked = useRef(0);
511
- const inFlight = useRef(null);
512
- const search = useCallback((query2) => {
513
- if (!loadItems) return;
514
- const seq = asked.current + 1;
515
- asked.current = seq;
516
- inFlight.current?.abort();
517
- const controller = new AbortController();
518
- inFlight.current = controller;
519
- setLoading(true);
520
- setFailed(false);
521
- loadItems(query2, controller.signal).then(
522
- (next) => {
523
- if (seq !== asked.current) return;
524
- setLoaded(next);
525
- setLoading(false);
526
- },
527
- () => {
528
- if (seq !== asked.current || controller.signal.aborted) return;
529
- setLoaded([]);
530
- setFailed(true);
531
- setLoading(false);
532
- }
533
- );
534
- }, [loadItems]);
535
298
  const [query, setQuery] = useState("");
299
+ const [active, setActive] = useState(0);
300
+ const results = useMemo(() => filterCommands(commands, query), [commands, query]);
301
+ const groups = useMemo(() => groupCommands(results), [results]);
536
302
  useEffect(() => {
537
- if (!async) return void 0;
538
- const id2 = setTimeout(() => search(query), loadDelay);
539
- return () => clearTimeout(id2);
540
- }, [async, query, loadDelay, search]);
541
- useEffect(() => () => {
542
- asked.current += 1;
543
- inFlight.current?.abort();
544
- }, []);
545
- useEffect(() => {
546
- if (!open) return void 0;
547
- const onScroll = (event) => {
548
- const target = event.target;
549
- if (target === document || target === document.documentElement || target === document.body) {
550
- setOpen(false);
551
- }
552
- };
553
- window.addEventListener("scroll", onScroll, { capture: true, passive: true });
554
- return () => window.removeEventListener("scroll", onScroll, { capture: true });
303
+ if (!open) {
304
+ setQuery("");
305
+ setActive(0);
306
+ }
555
307
  }, [open]);
556
- return /* @__PURE__ */ jsxs(
557
- Combobox.Root,
558
- {
559
- items: available,
560
- filter: async ? null : void 0,
561
- onInputValueChange: async ? (next) => setQuery(next) : void 0,
562
- value: value === void 0 ? void 0 : optionFor(value),
563
- defaultValue: defaultValue === void 0 ? void 0 : optionFor(defaultValue),
564
- onValueChange: (next) => {
565
- const option = next;
566
- setChosen(option);
567
- onValueChange?.(option?.value ?? null);
568
- },
569
- modal: true,
570
- open,
571
- onOpenChange: setOpen,
572
- isItemEqualToValue: (a, b) => a?.value === b?.value,
573
- itemToStringLabel: (item) => typeof item === "string" ? item : item.label,
574
- disabled,
575
- name,
576
- children: [
577
- /* @__PURE__ */ jsxs(
578
- "div",
308
+ useEffect(() => setActive(0), [query]);
309
+ const run = (command) => {
310
+ onOpenChange(false);
311
+ command.onRun();
312
+ };
313
+ return /* @__PURE__ */ jsx(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
314
+ /* @__PURE__ */ jsx(Dialog.Backdrop, { className: "cb-palette__backdrop" }),
315
+ /* @__PURE__ */ jsxs(Dialog.Popup, { className: cn("cb-palette", className), "aria-label": "Command palette", children: [
316
+ /* @__PURE__ */ jsxs("div", { className: "cb-palette__search", children: [
317
+ /* @__PURE__ */ jsx(Search, { size: 16, className: "cb-palette__search-icon" }),
318
+ /* @__PURE__ */ jsx(
319
+ "input",
579
320
  {
580
- ref: shell,
581
- className: cn("cb-autocomplete", `cb-autocomplete--${size}`, className),
582
- "data-disabled": disabled || void 0,
583
- children: [
584
- /* @__PURE__ */ jsx(
585
- Combobox.Input,
586
- {
587
- className: "cb-autocomplete__input",
588
- placeholder,
589
- id: id ?? field?.controlId,
590
- "aria-describedby": field?.describedBy,
591
- "aria-invalid": invalid ?? field?.invalid ? true : void 0
592
- }
593
- ),
594
- /* @__PURE__ */ jsx(Combobox.Clear, { className: "cb-autocomplete__clear", "aria-label": labels.clear, children: /* @__PURE__ */ jsx(X, { size: 14 }) }),
595
- /* @__PURE__ */ jsx(Combobox.Trigger, { className: "cb-autocomplete__trigger", "aria-label": labels.open, children: /* @__PURE__ */ jsx(ChevronsUpDown, { size: 16 }) })
596
- ]
321
+ className: "cb-palette__input",
322
+ value: query,
323
+ placeholder,
324
+ autoFocus: true,
325
+ role: "combobox",
326
+ "aria-expanded": true,
327
+ "aria-controls": "cb-palette-list",
328
+ "aria-activedescendant": results[active] ? `cb-palette-${results[active].id}` : void 0,
329
+ onChange: (event) => setQuery(event.target.value),
330
+ onKeyDown: (event) => {
331
+ if (event.key === "ArrowDown") {
332
+ setActive((index) => Math.min(index + 1, results.length - 1));
333
+ event.preventDefault();
334
+ }
335
+ if (event.key === "ArrowUp") {
336
+ setActive((index) => Math.max(index - 1, 0));
337
+ event.preventDefault();
338
+ }
339
+ if (event.key === "Enter" && results[active]) {
340
+ run(results[active]);
341
+ event.preventDefault();
342
+ }
343
+ }
597
344
  }
598
- ),
599
- /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, { anchor: shell, sideOffset: 6, className: "cb-autocomplete__positioner", children: /* @__PURE__ */ jsxs(Combobox.Popup, { className: "cb-autocomplete__popup", children: [
600
- loading ? /* @__PURE__ */ jsx("p", { className: "cb-autocomplete__status", role: "status", children: loadingMessage }) : failed ? /* @__PURE__ */ jsx("p", { className: "cb-autocomplete__status", role: "alert", children: errorMessage }) : /* @__PURE__ */ jsx(Combobox.Empty, { className: "cb-autocomplete__empty", children: emptyMessage }),
601
- /* @__PURE__ */ jsx(Combobox.List, { children: (item) => /* @__PURE__ */ jsxs(
602
- Combobox.Item,
345
+ )
346
+ ] }),
347
+ /* @__PURE__ */ jsx("div", { className: "cb-palette__list", id: "cb-palette-list", role: "listbox", children: results.length === 0 ? /* @__PURE__ */ jsx("p", { className: "cb-palette__empty", children: emptyMessage }) : groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "cb-palette__group", children: [
348
+ group.group ? /* @__PURE__ */ jsx("p", { className: "cb-palette__group-label", children: group.group }) : null,
349
+ group.items.map((command) => {
350
+ const index = results.indexOf(command);
351
+ return /* @__PURE__ */ jsxs(
352
+ "div",
603
353
  {
604
- value: item,
605
- disabled: item.disabled,
606
- className: "cb-autocomplete__item",
354
+ id: `cb-palette-${command.id}`,
355
+ role: "option",
356
+ "aria-selected": index === active,
357
+ className: "cb-palette__item",
358
+ "data-active": index === active || void 0,
359
+ onMouseMove: () => setActive(index),
360
+ onClick: () => run(command),
607
361
  children: [
608
- /* @__PURE__ */ jsx("span", { className: "cb-autocomplete__check", children: /* @__PURE__ */ jsx(Combobox.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { size: 14 }) }) }),
609
- /* @__PURE__ */ jsxs("span", { className: "cb-autocomplete__text", children: [
610
- /* @__PURE__ */ jsx("span", { className: "cb-autocomplete__label", children: item.label }),
611
- item.description ? /* @__PURE__ */ jsx("span", { className: "cb-autocomplete__description", children: item.description }) : null
612
- ] })
362
+ /* @__PURE__ */ jsx("span", { className: "cb-palette__icon", children: command.icon }),
363
+ /* @__PURE__ */ jsx("span", { className: "cb-palette__label", children: command.label }),
364
+ command.shortcut ? /* @__PURE__ */ jsx("kbd", { className: "cb-palette__shortcut", children: command.shortcut }) : null
613
365
  ]
614
366
  },
615
- item.value
616
- ) })
617
- ] }) }) })
618
- ]
619
- }
620
- );
621
- }
622
-
623
- // src/form/date.util.ts
624
- function isSameDay(a, b) {
625
- if (!a || !b) return false;
626
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
627
- }
628
- function startOfDay(date) {
629
- return new Date(date.getFullYear(), date.getMonth(), date.getDate());
630
- }
631
- function monthMatrix(year, month, weekStartsOn = 1) {
632
- const first = new Date(year, month, 1);
633
- const offset = (first.getDay() - weekStartsOn + 7) % 7;
634
- const start = new Date(year, month, 1 - offset);
635
- const weeks = [];
636
- for (let week = 0; week < 6; week += 1) {
637
- const cells = [];
638
- for (let day = 0; day < 7; day += 1) {
639
- const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + week * 7 + day);
640
- cells.push({ date, inMonth: date.getMonth() === month });
641
- }
642
- weeks.push(cells);
643
- }
644
- return weeks;
645
- }
646
- function parseDateInput(input) {
647
- const text2 = input.trim();
648
- if (!text2) return null;
649
- const iso = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(text2);
650
- if (iso) return build(Number(iso[1]), Number(iso[2]), Number(iso[3]));
651
- const dayFirst = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2,4})$/.exec(text2);
652
- if (dayFirst) {
653
- const year = Number(dayFirst[3]);
654
- return build(year < 100 ? 2e3 + year : year, Number(dayFirst[2]), Number(dayFirst[1]));
655
- }
656
- return null;
657
- }
658
- function build(year, month, day) {
659
- if (month < 1 || month > 12 || day < 1 || day > 31) return null;
660
- const date = new Date(year, month - 1, day);
661
- if (date.getMonth() !== month - 1 || date.getDate() !== day) return null;
662
- return date;
663
- }
664
- function formatISO(date) {
665
- if (!date) return "";
666
- const month = String(date.getMonth() + 1).padStart(2, "0");
667
- const day = String(date.getDate()).padStart(2, "0");
668
- return `${date.getFullYear()}-${month}-${day}`;
669
- }
670
- function isOutOfRange(date, min, max) {
671
- if (min && startOfDay(date) < startOfDay(min)) return true;
672
- if (max && startOfDay(date) > startOfDay(max)) return true;
673
- return false;
367
+ command.id
368
+ );
369
+ })
370
+ ] }, group.group ?? "ungrouped")) })
371
+ ] })
372
+ ] }) });
674
373
  }
675
- var WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
676
- function DatePicker({
677
- value,
678
- defaultValue = null,
679
- onValueChange,
680
- min,
681
- max,
682
- format,
683
- weekStartsOn = 1,
684
- size = "md",
685
- disabled,
686
- invalid,
687
- placeholder = "dd/mm/yyyy",
688
- className
689
- }) {
690
- const field = useFieldWiring();
691
- const labels = useLabels();
692
- const controlled = value !== void 0;
693
- const [internal, setInternal] = useState(defaultValue);
694
- const current = controlled ? value ?? null : internal;
695
- const display = (date) => format ? format(date) : date.toLocaleDateString(void 0, { day: "2-digit", month: "short", year: "numeric" });
696
- const [text2, setText] = useState(() => current ? display(current) : "");
697
- const [open, setOpen] = useState(false);
698
- const [cursor, setCursor] = useState(() => current ?? /* @__PURE__ */ new Date());
699
- const fieldRef = useRef(null);
700
- const commit = (next) => {
701
- if (!controlled) setInternal(next);
702
- setText(next ? display(next) : "");
703
- onValueChange?.(next);
704
- };
705
- const weeks = monthMatrix(cursor.getFullYear(), cursor.getMonth(), weekStartsOn);
706
- const weekdays = weekStartsOn === 1 ? WEEKDAYS : [WEEKDAYS[6], ...WEEKDAYS.slice(0, 6)];
707
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open, onOpenChange: setOpen, children: [
708
- /* @__PURE__ */ jsxs(
709
- "div",
374
+ function Checklist({ title = "Get started", tasks, completeSlot, className }) {
375
+ const done = tasks.filter((task) => task.done).length;
376
+ const complete = tasks.length > 0 && done === tasks.length;
377
+ return /* @__PURE__ */ jsxs("section", { className: cn("cb-checklist", className), "aria-label": typeof title === "string" ? title : "Checklist", children: [
378
+ /* @__PURE__ */ jsxs("header", { className: "cb-checklist__head", children: [
379
+ /* @__PURE__ */ jsx("p", { className: "cb-checklist__title", children: title }),
380
+ /* @__PURE__ */ jsxs("span", { className: "cb-checklist__count", children: [
381
+ done,
382
+ " of ",
383
+ tasks.length
384
+ ] })
385
+ ] }),
386
+ /* @__PURE__ */ jsx(
387
+ Progress,
710
388
  {
711
- ref: fieldRef,
712
- className: cn("cb-date", `cb-date--${size}`, className),
713
- "data-disabled": disabled || void 0,
714
- children: [
715
- /* @__PURE__ */ jsx(
716
- "input",
717
- {
718
- className: "cb-date__input",
719
- id: field?.controlId,
720
- value: controlled ? current ? display(current) : text2 : text2,
721
- placeholder,
722
- disabled,
723
- "aria-describedby": field?.describedBy,
724
- "aria-invalid": invalid ?? field?.invalid ? true : void 0,
725
- onClick: () => !disabled && setOpen(true),
726
- onChange: (event) => setText(event.target.value),
727
- onBlur: () => {
728
- const parsed = parseDateInput(text2);
729
- if (parsed && !isOutOfRange(parsed, min, max)) {
730
- commit(parsed);
731
- setCursor(parsed);
732
- } else {
733
- commit(null);
734
- }
735
- }
736
- }
737
- ),
738
- /* @__PURE__ */ jsx(
739
- Popover$1.Trigger,
740
- {
741
- className: "cb-date__trigger",
742
- "aria-label": labels.chooseDate,
743
- disabled,
744
- render: /* @__PURE__ */ jsx("button", { type: "button" }),
745
- children: /* @__PURE__ */ jsx(CalendarDays, { size: 16 })
746
- }
747
- )
748
- ]
389
+ percent: done / Math.max(tasks.length, 1) * 100,
390
+ size: "small",
391
+ showInfo: false,
392
+ "aria-label": `${done} of ${tasks.length} tasks done`
749
393
  }
750
394
  ),
751
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
752
- Popover$1.Positioner,
395
+ complete && completeSlot ? /* @__PURE__ */ jsx("div", { className: "cb-checklist__complete", children: completeSlot }) : /* @__PURE__ */ jsx("ul", { className: "cb-checklist__list", children: tasks.map((task) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
396
+ "button",
753
397
  {
754
- anchor: fieldRef,
755
- side: "bottom",
756
- align: "start",
757
- sideOffset: 6,
758
- className: "cb-date-picker-calendar__positioner",
759
- children: /* @__PURE__ */ jsxs(Popover$1.Popup, { className: "cb-date-picker-calendar", children: [
760
- /* @__PURE__ */ jsxs("div", { className: "cb-date-picker-calendar__head", children: [
761
- /* @__PURE__ */ jsx(
762
- "button",
763
- {
764
- type: "button",
765
- className: "cb-date-picker-calendar__nav",
766
- "aria-label": labels.previousMonth,
767
- onClick: () => setCursor(new Date(cursor.getFullYear(), cursor.getMonth() - 1, 1)),
768
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
769
- }
770
- ),
771
- /* @__PURE__ */ jsx("p", { className: "cb-date-picker-calendar__month", "aria-live": "polite", children: cursor.toLocaleDateString(void 0, { month: "long", year: "numeric" }) }),
772
- /* @__PURE__ */ jsx(
773
- "button",
774
- {
775
- type: "button",
776
- className: "cb-date-picker-calendar__nav",
777
- "aria-label": labels.nextMonth,
778
- onClick: () => setCursor(new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1)),
779
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 })
780
- }
781
- )
398
+ type: "button",
399
+ className: "cb-checklist__task",
400
+ "data-done": task.done || void 0,
401
+ onClick: task.onSelect,
402
+ disabled: !task.onSelect,
403
+ children: [
404
+ /* @__PURE__ */ jsx("span", { className: "cb-checklist__marker", "aria-hidden": "true", children: task.done ? /* @__PURE__ */ jsx(Check, { size: 12, strokeWidth: 3 }) : null }),
405
+ /* @__PURE__ */ jsxs("span", { className: "cb-checklist__text", children: [
406
+ /* @__PURE__ */ jsxs("span", { className: "cb-checklist__label", children: [
407
+ task.label,
408
+ /* @__PURE__ */ jsx("span", { className: "cb-visually-hidden", children: task.done ? " (done)" : "" })
409
+ ] }),
410
+ task.description ? /* @__PURE__ */ jsx("span", { className: "cb-checklist__description", children: task.description }) : null
782
411
  ] }),
783
- /* @__PURE__ */ jsxs("table", { className: "cb-date-picker-calendar__grid", children: [
784
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: weekdays.map((day) => /* @__PURE__ */ jsx("th", { scope: "col", abbr: day, children: day }, day)) }) }),
785
- /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { children: week.map((cell) => {
786
- const selected = isSameDay(cell.date, current);
787
- const outside = isOutOfRange(cell.date, min, max);
788
- return /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(
789
- "button",
790
- {
791
- type: "button",
792
- className: "cb-date-picker-calendar__day",
793
- "data-outside": !cell.inMonth || void 0,
794
- "data-selected": selected || void 0,
795
- "data-today": isSameDay(cell.date, /* @__PURE__ */ new Date()) || void 0,
796
- disabled: outside,
797
- "aria-pressed": selected,
798
- "aria-label": formatISO(cell.date),
799
- onClick: () => {
800
- commit(cell.date);
801
- setCursor(cell.date);
802
- setOpen(false);
803
- },
804
- children: cell.date.getDate()
805
- }
806
- ) }, cell.date.toISOString());
807
- }) }, weekIndex)) })
808
- ] })
809
- ] })
412
+ task.onSelect && !task.done ? /* @__PURE__ */ jsx(ChevronRight, { size: 16, className: "cb-checklist__chevron" }) : null
413
+ ]
810
414
  }
811
- ) })
415
+ ) }, task.id)) })
812
416
  ] });
813
417
  }
814
-
815
- // src/form/time.util.ts
816
- function parseTime(input) {
817
- const text2 = input.trim().toLowerCase().replace(/\s+/g, "");
818
- if (!text2) return null;
819
- const suffix = text2.endsWith("am") ? "am" : text2.endsWith("pm") ? "pm" : null;
820
- const body = suffix ? text2.slice(0, -2) : text2;
821
- let hours;
822
- let minutes = 0;
823
- const separated = /^(\d{1,2})[:.](\d{1,2})$/.exec(body);
824
- const compact = /^(\d{3,4})$/.exec(body);
825
- const hourOnly = /^(\d{1,2})$/.exec(body);
826
- if (separated) {
827
- hours = Number(separated[1]);
828
- minutes = Number(separated[2]);
829
- } else if (compact) {
830
- const digits = compact[1].padStart(4, "0");
831
- hours = Number(digits.slice(0, 2));
832
- minutes = Number(digits.slice(2));
833
- } else if (hourOnly) {
834
- hours = Number(hourOnly[1]);
835
- } else {
836
- return null;
837
- }
838
- if (suffix === "pm" && hours < 12) hours += 12;
839
- if (suffix === "am" && hours === 12) hours = 0;
840
- if (hours > 23 || minutes > 59) return null;
841
- return { hours, minutes };
842
- }
843
- function formatTime({ hours, minutes }) {
844
- return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
845
- }
846
- function timeToMinutes({ hours, minutes }) {
847
- return hours * 60 + minutes;
848
- }
849
- function timeOptions(step, min, max) {
850
- const safeStep = step > 0 ? step : 30;
851
- const from = min ? timeToMinutes(min) : 0;
852
- const to = max ? timeToMinutes(max) : 24 * 60 - 1;
853
- const options = [];
854
- for (let value = from; value <= to; value += safeStep) {
855
- options.push({ hours: Math.floor(value / 60) % 24, minutes: value % 60 });
856
- }
857
- return options;
858
- }
859
- function isTimeOutOfRange(value, min, max) {
860
- const minutes = timeToMinutes(value);
861
- if (min && minutes < timeToMinutes(min)) return true;
862
- if (max && minutes > timeToMinutes(max)) return true;
863
- return false;
864
- }
865
- function TimePicker({
866
- value,
867
- defaultValue = null,
868
- onValueChange,
869
- min,
870
- max,
871
- step = 30,
872
- size = "md",
873
- disabled,
874
- invalid,
875
- placeholder = "hh:mm",
876
- className
877
- }) {
878
- const field = useFieldWiring();
879
- const labels = useLabels();
880
- const controlled = value !== void 0;
881
- const [internal, setInternal] = useState(defaultValue);
882
- const current = controlled ? value ?? null : internal;
883
- const [text2, setText] = useState(() => current ? formatTime(current) : "");
884
- const [open, setOpen] = useState(false);
885
- const fieldRef = useRef(null);
886
- const commit = (next) => {
887
- if (!controlled) setInternal(next);
888
- setText(next ? formatTime(next) : "");
889
- onValueChange?.(next);
890
- };
891
- const options = timeOptions(step, min, max);
892
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open, onOpenChange: setOpen, children: [
893
- /* @__PURE__ */ jsxs(
894
- "div",
895
- {
896
- ref: fieldRef,
897
- className: cn("cb-date", `cb-date--${size}`, className),
898
- "data-disabled": disabled || void 0,
899
- children: [
900
- /* @__PURE__ */ jsx(
901
- "input",
902
- {
903
- className: "cb-date__input",
904
- id: field?.controlId,
905
- value: controlled ? current ? formatTime(current) : text2 : text2,
906
- placeholder,
907
- disabled,
908
- inputMode: "numeric",
909
- "aria-describedby": field?.describedBy,
910
- "aria-invalid": invalid ?? field?.invalid ? true : void 0,
911
- onClick: () => !disabled && setOpen(true),
912
- onChange: (event) => setText(event.target.value),
913
- onBlur: () => {
914
- const parsed = parseTime(text2);
915
- commit(parsed && !isTimeOutOfRange(parsed, min, max) ? parsed : null);
916
- }
917
- }
918
- ),
919
- /* @__PURE__ */ jsx(
920
- Popover$1.Trigger,
921
- {
922
- className: "cb-date__trigger",
923
- "aria-label": labels.chooseTime,
924
- disabled,
925
- render: /* @__PURE__ */ jsx("button", { type: "button" }),
926
- children: /* @__PURE__ */ jsx(Clock, { size: 16 })
927
- }
928
- )
929
- ]
930
- }
931
- ),
932
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
933
- Popover$1.Positioner,
934
- {
935
- anchor: fieldRef,
936
- side: "bottom",
937
- align: "start",
938
- sideOffset: 6,
939
- className: "cb-times__positioner",
940
- children: /* @__PURE__ */ jsx(Popover$1.Popup, { className: "cb-times", children: /* @__PURE__ */ jsx("ul", { className: "cb-times__list", children: options.map((option) => {
941
- const selected = current ? timeToMinutes(current) === timeToMinutes(option) : false;
942
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
943
- "button",
944
- {
945
- type: "button",
946
- className: "cb-times__option",
947
- "data-selected": selected || void 0,
948
- "aria-pressed": selected,
949
- onClick: () => {
950
- commit(option);
951
- setOpen(false);
952
- },
953
- children: formatTime(option)
954
- }
955
- ) }, formatTime(option));
956
- }) }) })
957
- }
958
- ) })
959
- ] });
960
- }
961
-
962
- // src/form/upload.util.ts
963
- function matchesAccept(file, accept) {
964
- if (!accept || accept.length === 0) return true;
965
- const name = file.name.toLowerCase();
966
- return accept.some((pattern) => {
967
- const rule = pattern.trim().toLowerCase();
968
- if (rule.startsWith(".")) return name.endsWith(rule);
969
- if (rule.endsWith("/*")) return file.type.startsWith(rule.slice(0, -1));
970
- return file.type === rule;
971
- });
972
- }
973
- function partitionFiles(incoming, existing, rules) {
974
- const accepted = [];
975
- const rejected = [];
976
- const limit = rules.multiple === false ? 1 : rules.maxFiles;
977
- for (const file of incoming) {
978
- if (!matchesAccept(file, rules.accept)) {
979
- rejected.push({ file, reason: "type" });
980
- continue;
981
- }
982
- if (typeof rules.maxSize === "number" && file.size > rules.maxSize) {
983
- rejected.push({ file, reason: "size" });
984
- continue;
985
- }
986
- if (typeof limit === "number" && existing.length + accepted.length >= limit) {
987
- rejected.push({ file, reason: "count" });
988
- continue;
989
- }
990
- accepted.push(file);
991
- }
992
- return { accepted, rejected };
993
- }
994
- function describeAccept(rules) {
995
- const parts = [];
996
- if (rules.accept?.length) parts.push(rules.accept.join(", "));
997
- if (typeof rules.maxSize === "number") parts.push(`up to ${Math.round(rules.maxSize / (1024 * 1024))} MB`);
998
- return parts.length > 0 ? ` \u2014 ${parts.join(" ")}` : "";
999
- }
1000
- function Upload({
1001
- onFilesChange,
1002
- files = [],
1003
- onReject,
1004
- accept,
1005
- maxSize,
1006
- maxFiles,
1007
- multiple = true,
1008
- disabled,
1009
- children,
1010
- className
1011
- }) {
1012
- const [over, setOver] = useState(false);
1013
- const field = useFieldWiring();
1014
- const labels = useLabels();
1015
- const rules = { accept, maxSize, maxFiles, multiple };
1016
- const take = (incoming) => {
1017
- if (!incoming) return;
1018
- const { accepted, rejected } = partitionFiles(Array.from(incoming), files, rules);
1019
- if (accepted.length > 0) onFilesChange([...files, ...accepted]);
1020
- if (rejected.length > 0) onReject?.(rejected);
1021
- };
1022
- const onDrop = (event) => {
1023
- event.preventDefault();
1024
- setOver(false);
1025
- if (!disabled) take(event.dataTransfer.files);
1026
- };
1027
- return /* @__PURE__ */ jsxs("div", { className: cn("cb-filedrop", className), children: [
1028
- /* @__PURE__ */ jsxs(
1029
- "label",
1030
- {
1031
- className: "cb-filedrop__zone",
1032
- "data-over": over || void 0,
1033
- "data-disabled": disabled || void 0,
1034
- onDragOver: (event) => {
1035
- event.preventDefault();
1036
- if (!disabled) setOver(true);
1037
- },
1038
- onDragLeave: () => setOver(false),
1039
- onDrop,
1040
- children: [
1041
- /* @__PURE__ */ jsx(
1042
- "input",
1043
- {
1044
- type: "file",
1045
- className: "cb-visually-hidden",
1046
- id: field?.controlId,
1047
- accept: accept?.join(","),
1048
- multiple,
1049
- disabled,
1050
- "aria-describedby": field?.describedBy,
1051
- onChange: (event) => {
1052
- take(event.target.files);
1053
- event.target.value = "";
1054
- }
1055
- }
1056
- ),
1057
- /* @__PURE__ */ jsx("span", { className: "cb-filedrop__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Upload$1, { size: 20 }) }),
1058
- /* @__PURE__ */ jsx("span", { className: "cb-filedrop__button", children: multiple ? labels.chooseFiles : labels.chooseFile }),
1059
- /* @__PURE__ */ jsxs("p", { className: "cb-filedrop__hint", children: [
1060
- multiple ? labels.dropFilesHere : labels.dropFileHere,
1061
- describeAccept(rules)
1062
- ] }),
1063
- children
1064
- ]
1065
- }
1066
- ),
1067
- files.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "cb-filedrop__list", children: files.map((file, index) => /* @__PURE__ */ jsxs("li", { className: "cb-filedrop__file", children: [
1068
- /* @__PURE__ */ jsx("span", { className: "cb-filedrop__name", children: file.name }),
1069
- /* @__PURE__ */ jsx("span", { className: "cb-filedrop__size", children: formatSize(file.size) }),
1070
- /* @__PURE__ */ jsx(
1071
- "button",
1072
- {
1073
- type: "button",
1074
- className: "cb-filedrop__remove",
1075
- "aria-label": labels.removeFile(file.name),
1076
- onClick: () => onFilesChange(files.filter((_, i) => i !== index)),
1077
- children: /* @__PURE__ */ jsx(X, { size: 14 })
1078
- }
1079
- )
1080
- ] }, `${file.name}-${index}`)) }) : null
1081
- ] });
1082
- }
1083
- function formatSize(bytes) {
1084
- if (bytes < 1024) return `${bytes} B`;
1085
- if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1086
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1087
- }
1088
-
1089
- // src/form/number.ts
1090
- function parseNumber(input) {
1091
- const trimmed = input.trim().replace(/\s/g, "").replace(",", ".");
1092
- if (trimmed === "" || trimmed === "-" || trimmed === ".") return null;
1093
- const parsed = Number(trimmed);
1094
- return Number.isFinite(parsed) ? parsed : null;
1095
- }
1096
- function clamp(value, { min, max }) {
1097
- let next = value;
1098
- if (typeof min === "number") next = Math.max(next, min);
1099
- if (typeof max === "number") next = Math.min(next, max);
1100
- return next;
1101
- }
1102
- function stepBy(value, direction, bounds) {
1103
- const step = bounds.step ?? 1;
1104
- const base = value ?? bounds.min ?? 0;
1105
- const next = base + direction * step;
1106
- const decimals = decimalPlaces(step);
1107
- const rounded = Number(next.toFixed(decimals));
1108
- return clamp(rounded, bounds);
1109
- }
1110
- function decimalPlaces(step) {
1111
- const text2 = String(step);
1112
- const dot = text2.indexOf(".");
1113
- return dot === -1 ? 0 : text2.length - dot - 1;
1114
- }
1115
- function canStep(value, direction, bounds) {
1116
- if (value === null) return true;
1117
- if (direction === 1) return typeof bounds.max !== "number" || value < bounds.max;
1118
- return typeof bounds.min !== "number" || value > bounds.min;
1119
- }
1120
- function InputNumber({
1121
- value,
1122
- defaultValue = null,
1123
- onValueChange,
1124
- min,
1125
- max,
1126
- step = 1,
1127
- size = "md",
1128
- disabled,
1129
- invalid,
1130
- name,
1131
- placeholder,
1132
- suffix,
1133
- className
1134
- }) {
1135
- const field = useFieldWiring();
1136
- const labels = useLabels();
1137
- const controlled = value !== void 0;
1138
- const [internal, setInternal] = useState(defaultValue);
1139
- const [text2, setText] = useState(() => formatValue(controlled ? value ?? null : defaultValue));
1140
- const current = controlled ? value ?? null : internal;
1141
- const bounds = { min, max, step };
1142
- const commit = (next) => {
1143
- if (!controlled) setInternal(next);
1144
- setText(formatValue(next));
1145
- onValueChange?.(next);
1146
- };
1147
- const onType = (event) => {
1148
- setText(event.target.value);
1149
- const parsed = parseNumber(event.target.value);
1150
- if (parsed === null) {
1151
- onValueChange?.(null);
1152
- if (!controlled) setInternal(null);
1153
- return;
1154
- }
1155
- if (!controlled) setInternal(parsed);
1156
- onValueChange?.(parsed);
1157
- };
1158
- return /* @__PURE__ */ jsxs("div", { className: cn("cb-number", `cb-number--${size}`, className), "data-disabled": disabled || void 0, children: [
1159
- /* @__PURE__ */ jsx(
1160
- "input",
1161
- {
1162
- className: "cb-number__input",
1163
- inputMode: "decimal",
1164
- id: field?.controlId,
1165
- name,
1166
- value: controlled ? formatValue(value ?? null) : text2,
1167
- placeholder,
1168
- disabled,
1169
- "aria-describedby": field?.describedBy,
1170
- "aria-invalid": invalid ?? field?.invalid ? true : void 0,
1171
- onChange: onType,
1172
- onBlur: () => {
1173
- const parsed = parseNumber(text2);
1174
- commit(parsed === null ? null : clamp(parsed, bounds));
1175
- }
1176
- }
1177
- ),
1178
- suffix ? /* @__PURE__ */ jsx("span", { className: "cb-number__suffix", "aria-hidden": "true", children: suffix }) : null,
1179
- /* @__PURE__ */ jsxs("span", { className: "cb-number__steppers", children: [
1180
- /* @__PURE__ */ jsx(
1181
- "button",
1182
- {
1183
- type: "button",
1184
- className: "cb-number__step",
1185
- "aria-label": labels.decrease,
1186
- disabled: disabled || !canStep(current, -1, bounds),
1187
- onClick: () => commit(stepBy(current, -1, bounds)),
1188
- children: /* @__PURE__ */ jsx(Minus, { size: 14 })
1189
- }
1190
- ),
1191
- /* @__PURE__ */ jsx(
1192
- "button",
1193
- {
1194
- type: "button",
1195
- className: "cb-number__step",
1196
- "aria-label": labels.increase,
1197
- disabled: disabled || !canStep(current, 1, bounds),
1198
- onClick: () => commit(stepBy(current, 1, bounds)),
1199
- children: /* @__PURE__ */ jsx(Plus, { size: 14 })
1200
- }
1201
- )
1202
- ] })
1203
- ] });
1204
- }
1205
- function formatValue(value) {
1206
- return value === null ? "" : String(value);
1207
- }
1208
- function box({ width = "100%", height = "1rem", radius = "md", className }) {
1209
- const style = { "--cb-skeleton-w": width, "--cb-skeleton-h": height };
1210
- return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("cb-skeleton", `cb-radius--${radius}`, className), style });
1211
- }
1212
- function text({ lines = 3, lastLineWidth = "62%", className }) {
1213
- return /* @__PURE__ */ jsx("span", { className: cn("cb-skeleton-text", className), children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx(SkeletonBox, { height: "0.75rem", width: i === lines - 1 ? lastLineWidth : "100%" }, i)) });
1214
- }
1215
- function circle({ size = "2.5rem", className }) {
1216
- return /* @__PURE__ */ jsx(SkeletonBox, { width: size, height: size, radius: "full", className });
1217
- }
1218
- var SkeletonBox = box;
1219
- var Skeleton = Object.assign(box, {
1220
- Text: text,
1221
- Circle: circle,
1222
- Rect: box
1223
- });
1224
- function SliderSkeleton({ orientation = "horizontal", range = false, size = "md" }) {
1225
- return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("cb-slider", "cb-slider--skeleton", `cb-slider--${size}`, `cb-slider--${orientation}`, range && "cb-slider--range"), children: /* @__PURE__ */ jsxs("div", { className: "cb-slider__control", children: [
1226
- /* @__PURE__ */ jsx(Skeleton, { className: "cb-slider__track", radius: "full" }),
1227
- /* @__PURE__ */ jsx(Skeleton.Circle, { className: "cb-slider__thumb", size: "var(--cb-slider-thumb-size)" }),
1228
- range ? /* @__PURE__ */ jsx(Skeleton.Circle, { className: "cb-slider__thumb", size: "var(--cb-slider-thumb-size)" }) : null
1229
- ] }) });
1230
- }
1231
- function SliderComponent(props) {
1232
- const {
1233
- label,
1234
- min = 0,
1235
- max = 100,
1236
- step = 1,
1237
- largeStep,
1238
- minStepsBetweenValues = 0,
1239
- motion: motion3 = true,
1240
- disabled = false,
1241
- orientation = "horizontal",
1242
- tone = "brand",
1243
- size = "md",
1244
- name,
1245
- getAriaValueText
1246
- } = props;
1247
- const field = useFieldWiring();
1248
- const range = props.range === true;
1249
- const value = props.value;
1250
- const defaultValue = props.defaultValue;
1251
- validateSlider({ min, max, step, largeStep, minStepsBetweenValues, value, defaultValue, range });
1252
- const ariaLabel = (index) => {
1253
- if (!range) return label;
1254
- return index === 0 ? `Minimum ${label}` : `Maximum ${label}`;
1255
- };
1256
- const ariaValueText = getAriaValueText ? (_formattedValue, currentValue, index) => getAriaValueText(currentValue, index) : void 0;
1257
- const rootClassName = cn(
1258
- "cb-slider",
1259
- `cb-slider--${size}`,
1260
- `cb-slider--${orientation}`,
1261
- range && "cb-slider--range",
1262
- !motion3 && "cb-slider--motionless"
1263
- );
1264
- const shared = {
1265
- min,
1266
- max,
1267
- step,
1268
- largeStep,
1269
- minStepsBetweenValues,
1270
- disabled,
1271
- orientation,
1272
- name,
1273
- className: rootClassName,
1274
- "data-tone": tone,
1275
- "data-invalid": field?.invalid || void 0
1276
- };
1277
- const thumbWiring = {
1278
- "aria-describedby": field?.describedBy,
1279
- "aria-invalid": field?.invalid || void 0
1280
- };
1281
- const thumbInputRef = (first) => (input) => {
1282
- if (!input) return;
1283
- if (first && field) input.id = field.controlId;
1284
- if (field?.describedBy) input.setAttribute("aria-describedby", field.describedBy);
1285
- else input.removeAttribute("aria-describedby");
1286
- if (field?.invalid) input.setAttribute("aria-invalid", "true");
1287
- else input.removeAttribute("aria-invalid");
1288
- };
1289
- if (range) {
1290
- const rangeDefaultValue = defaultValue ?? [min, max];
1291
- return /* @__PURE__ */ jsxs(
1292
- Slider$1.Root,
1293
- {
1294
- ...shared,
1295
- value,
1296
- defaultValue: rangeDefaultValue,
1297
- onValueChange: (next) => props.onValueChange?.(next),
1298
- onValueCommitted: (next) => props.onValueCommitted?.(next),
1299
- children: [
1300
- /* @__PURE__ */ jsx(SliderTrack, {}),
1301
- /* @__PURE__ */ jsx(Slider$1.Thumb, { index: 0, className: "cb-slider__thumb", inputRef: thumbInputRef(true), getAriaLabel: ariaLabel, getAriaValueText: ariaValueText, ...thumbWiring }),
1302
- /* @__PURE__ */ jsx(Slider$1.Thumb, { index: 1, className: "cb-slider__thumb", inputRef: thumbInputRef(false), getAriaLabel: ariaLabel, getAriaValueText: ariaValueText, ...thumbWiring })
1303
- ]
1304
- },
1305
- "range"
1306
- );
1307
- }
1308
- return /* @__PURE__ */ jsxs(
1309
- Slider$1.Root,
1310
- {
1311
- ...shared,
1312
- value,
1313
- defaultValue,
1314
- onValueChange: (next) => props.onValueChange?.(next),
1315
- onValueCommitted: (next) => props.onValueCommitted?.(next),
1316
- children: [
1317
- /* @__PURE__ */ jsx(SliderTrack, {}),
1318
- /* @__PURE__ */ jsx(Slider$1.Thumb, { className: "cb-slider__thumb", inputRef: thumbInputRef(true), getAriaLabel: ariaLabel, getAriaValueText: ariaValueText, ...thumbWiring })
1319
- ]
1320
- },
1321
- "single"
1322
- );
1323
- }
1324
- function SliderTrack() {
1325
- return /* @__PURE__ */ jsx(Slider$1.Control, { className: "cb-slider__control", children: /* @__PURE__ */ jsx(Slider$1.Track, { className: "cb-slider__track", children: /* @__PURE__ */ jsx(Slider$1.Indicator, { className: "cb-slider__indicator" }) }) });
1326
- }
1327
- function validateSlider({
1328
- min,
1329
- max,
1330
- step,
1331
- largeStep,
1332
- minStepsBetweenValues,
1333
- value,
1334
- defaultValue,
1335
- range
1336
- }) {
1337
- if (!Number.isFinite(min) || !Number.isFinite(max) || min >= max) {
1338
- throw new RangeError("Slider requires finite min and max values where min is less than max.");
1339
- }
1340
- if (!Number.isFinite(step) || step <= 0) {
1341
- throw new RangeError("Slider step must be a finite number greater than zero.");
1342
- }
1343
- if (largeStep !== void 0 && (!Number.isFinite(largeStep) || largeStep <= 0)) {
1344
- throw new RangeError("Slider largeStep must be a finite number greater than zero.");
1345
- }
1346
- if (!Number.isInteger(minStepsBetweenValues) || minStepsBetweenValues < 0) {
1347
- throw new RangeError("Slider minStepsBetweenValues must be a non-negative integer.");
1348
- }
1349
- if (range && step * minStepsBetweenValues > max - min) {
1350
- throw new RangeError("Slider minStepsBetweenValues cannot fit within min and max.");
1351
- }
1352
- const candidate = value ?? defaultValue;
1353
- if (candidate === void 0) return;
1354
- if (range) {
1355
- if (!Array.isArray(candidate) || candidate.length !== 2) {
1356
- throw new RangeError("A range Slider requires exactly two values.");
1357
- }
1358
- validateValue(candidate[0], min, max, step);
1359
- validateValue(candidate[1], min, max, step);
1360
- if (candidate[0] > candidate[1]) {
1361
- throw new RangeError("A range Slider requires values in ascending order.");
1362
- }
1363
- if (candidate[1] - candidate[0] < step * minStepsBetweenValues) {
1364
- throw new RangeError("Range Slider values do not satisfy minStepsBetweenValues.");
1365
- }
1366
- return;
1367
- }
1368
- if (typeof candidate !== "number") {
1369
- throw new RangeError("A single-value Slider requires one number.");
1370
- }
1371
- validateValue(candidate, min, max, step);
1372
- }
1373
- function validateValue(value, min, max, step) {
1374
- if (!Number.isFinite(value) || value < min || value > max) {
1375
- throw new RangeError("Slider values must be finite and within min and max.");
1376
- }
1377
- if (!isStepAligned(value, min, step)) {
1378
- throw new RangeError("Slider values must align to step increments from min.");
1379
- }
1380
- }
1381
- function isStepAligned(value, min, step) {
1382
- const steps = (value - min) / step;
1383
- return Math.abs(steps - Math.round(steps)) <= Number.EPSILON * Math.max(1, Math.abs(steps)) * 32;
1384
- }
1385
- var Slider = Object.assign(SliderComponent, { Skeleton: SliderSkeleton });
1386
- function MentionsSkeleton({ rows = 3 }) {
1387
- return /* @__PURE__ */ jsx("div", { className: "cb-mentions cb-mentions--skeleton", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Skeleton.Text, { lines: rows }) });
1388
- }
1389
- function queryAtCaret(value, caret, trigger) {
1390
- const prefix = value.slice(0, caret);
1391
- const triggerIndex = prefix.lastIndexOf(trigger);
1392
- if (triggerIndex < 0) return null;
1393
- const beforeTrigger = prefix.slice(0, triggerIndex);
1394
- const text2 = prefix.slice(triggerIndex + trigger.length);
1395
- if (beforeTrigger && !/\s$/.test(beforeTrigger) || /\s/.test(text2)) return null;
1396
- return { start: triggerIndex, end: caret, text: text2 };
1397
- }
1398
- function normalizeTrigger(trigger) {
1399
- if (Array.from(trigger).length !== 1) {
1400
- throw new RangeError("Mentions trigger must be exactly one character.");
1401
- }
1402
- return trigger;
1403
- }
1404
- function validateOptions(options) {
1405
- const values = /* @__PURE__ */ new Set();
1406
- for (const option of options) {
1407
- if (values.has(option.value)) throw new RangeError("Mentions options require unique values.");
1408
- values.add(option.value);
1409
- }
1410
- }
1411
- function matchingOptions(options, query) {
1412
- if (!query) return [];
1413
- const needle = query.text.toLocaleLowerCase();
1414
- return options.filter((option) => (option.searchText ?? option.value).toLocaleLowerCase().includes(needle));
1415
- }
1416
- function MentionsRoot({
1417
- options,
1418
- value,
1419
- defaultValue = "",
1420
- onValueChange,
1421
- trigger = "@",
1422
- separator = " ",
1423
- placeholder,
1424
- rows = 3,
1425
- name,
1426
- disabled = false,
1427
- motion: motion3 = true
1428
- }) {
1429
- const field = useFieldWiring();
1430
- const listboxId = useId();
1431
- const normalizedTrigger = normalizeTrigger(trigger);
1432
- validateOptions(options);
1433
- const controlled = value !== void 0;
1434
- const [internalValue, setInternalValue] = useState(defaultValue);
1435
- const text2 = controlled ? value : internalValue;
1436
- const textareaRef = useRef(null);
1437
- const anchorRef = useRef(null);
1438
- const [query, setQuery] = useState(null);
1439
- const [open, setOpen] = useState(false);
1440
- const [activeIndex, setActiveIndex] = useState(0);
1441
- const isComposingRef = useRef(false);
1442
- const matches = useMemo(() => matchingOptions(options, query), [options, query]);
1443
- const enabledMatches = useMemo(() => matches.filter((option) => !option.disabled), [matches]);
1444
- useEffect(() => {
1445
- setActiveIndex(0);
1446
- }, [query?.start, query?.text, matches.length]);
1447
- useEffect(() => {
1448
- if (!disabled) return;
1449
- setOpen(false);
1450
- setQuery(null);
1451
- }, [disabled]);
1452
- const updateText = (next) => {
1453
- if (!controlled) setInternalValue(next);
1454
- onValueChange?.(next);
1455
- };
1456
- const updateQuery = (nextText = text2, caret = textareaRef.current?.selectionStart ?? nextText.length) => {
1457
- if (disabled || isComposingRef.current) {
1458
- setQuery(null);
1459
- setOpen(false);
1460
- return;
1461
- }
1462
- const nextQuery = queryAtCaret(nextText, caret, normalizedTrigger);
1463
- setQuery(nextQuery);
1464
- setOpen(Boolean(matchingOptions(options, nextQuery).some((option) => !option.disabled)));
1465
- };
1466
- const selectOption = (option) => {
1467
- if (disabled || !query || option.disabled) return;
1468
- const inserted = `${normalizedTrigger}${option.value}${separator}`;
1469
- const next = `${text2.slice(0, query.start)}${inserted}${text2.slice(query.end)}`;
1470
- const caret = query.start + inserted.length;
1471
- updateText(next);
1472
- setQuery(null);
1473
- setOpen(false);
1474
- requestAnimationFrame(() => {
1475
- textareaRef.current?.focus();
1476
- textareaRef.current?.setSelectionRange(caret, caret);
1477
- });
1478
- };
1479
- const moveActive = (amount) => {
1480
- if (enabledMatches.length === 0) return;
1481
- setActiveIndex((current) => (current + amount + enabledMatches.length) % enabledMatches.length);
1482
- };
1483
- const handleChange = (event) => {
1484
- const next = event.target.value;
1485
- updateText(next);
1486
- if (disabled || isComposingRef.current) return;
1487
- const nextQuery = queryAtCaret(next, event.target.selectionStart, normalizedTrigger);
1488
- setQuery(nextQuery);
1489
- setOpen(Boolean(nextQuery && options.some((option) => !option.disabled && (option.searchText ?? option.value).toLocaleLowerCase().includes(nextQuery.text.toLocaleLowerCase()))));
1490
- };
1491
- const handleKeyDown = (event) => {
1492
- if (isComposingRef.current || event.nativeEvent.isComposing) return;
1493
- if (!open) return;
1494
- if (event.key === "Tab") {
1495
- setOpen(false);
1496
- } else if (event.key === "ArrowDown") {
1497
- event.preventDefault();
1498
- moveActive(1);
1499
- } else if (event.key === "ArrowUp") {
1500
- event.preventDefault();
1501
- moveActive(-1);
1502
- } else if (event.key === "Enter") {
1503
- const active = enabledMatches[activeIndex];
1504
- if (active) {
1505
- event.preventDefault();
1506
- selectOption(active);
1507
- }
1508
- } else if (event.key === "Escape") {
1509
- event.preventDefault();
1510
- setOpen(false);
1511
- }
1512
- };
1513
- const activeOption = enabledMatches[activeIndex];
1514
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open, onOpenChange: setOpen, children: [
1515
- /* @__PURE__ */ jsx("div", { ref: anchorRef, className: `cb-mentions${!motion3 ? " cb-mentions--motionless" : ""}`, "data-disabled": disabled || void 0, children: /* @__PURE__ */ jsx(
1516
- "textarea",
1517
- {
1518
- ref: textareaRef,
1519
- className: "cb-mentions__textarea",
1520
- id: field?.controlId,
1521
- name,
1522
- value: text2,
1523
- placeholder,
1524
- rows,
1525
- disabled,
1526
- required: field?.required,
1527
- "aria-describedby": field?.describedBy,
1528
- "aria-invalid": field?.invalid || void 0,
1529
- "aria-autocomplete": "list",
1530
- "aria-controls": open ? listboxId : void 0,
1531
- "aria-activedescendant": open && activeOption ? `${listboxId}-option-${matches.indexOf(activeOption)}` : void 0,
1532
- onChange: handleChange,
1533
- onSelect: () => updateQuery(),
1534
- onFocus: () => updateQuery(),
1535
- onCompositionStart: () => {
1536
- isComposingRef.current = true;
1537
- setQuery(null);
1538
- setOpen(false);
1539
- },
1540
- onCompositionEnd: (event) => {
1541
- isComposingRef.current = false;
1542
- updateQuery(event.currentTarget.value, event.currentTarget.selectionStart);
1543
- },
1544
- onKeyDown: handleKeyDown
1545
- }
1546
- ) }),
1547
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { anchor: anchorRef, side: "bottom", align: "start", className: "cb-mentions__positioner", children: /* @__PURE__ */ jsx(Popover$1.Popup, { className: `cb-mentions__popup${!motion3 ? " cb-mentions__popup--motionless" : ""}`, initialFocus: false, finalFocus: false, children: /* @__PURE__ */ jsx("div", { className: "cb-mentions__list", id: listboxId, role: "listbox", "aria-label": "Mention suggestions", children: matches.map((option, matchIndex) => {
1548
- const index = enabledMatches.indexOf(option);
1549
- const active = index === activeIndex;
1550
- return /* @__PURE__ */ jsx(
1551
- "button",
1552
- {
1553
- type: "button",
1554
- role: "option",
1555
- id: `${listboxId}-option-${matchIndex}`,
1556
- className: "cb-mentions__option",
1557
- disabled: option.disabled,
1558
- tabIndex: -1,
1559
- "aria-selected": active,
1560
- "data-active": active || void 0,
1561
- onMouseDown: (event) => event.preventDefault(),
1562
- onClick: () => selectOption(option),
1563
- children: option.label
1564
- },
1565
- `${option.value}-${matchIndex}`
1566
- );
1567
- }) }) }) }) })
1568
- ] });
1569
- }
1570
- var Mentions = Object.assign(MentionsRoot, { Skeleton: MentionsSkeleton });
1571
- function TransferSkeleton({ items = 4 }) {
1572
- return /* @__PURE__ */ jsxs("div", { className: "cb-transfer cb-transfer--skeleton", "aria-hidden": "true", children: [
1573
- /* @__PURE__ */ jsx(TransferSkeletonList, { items }),
1574
- /* @__PURE__ */ jsxs("div", { className: "cb-transfer__actions", children: [
1575
- /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-transfer__skeleton-action" }),
1576
- /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-transfer__skeleton-action" })
1577
- ] }),
1578
- /* @__PURE__ */ jsx(TransferSkeletonList, { items })
1579
- ] });
1580
- }
1581
- function TransferSkeletonList({ items }) {
1582
- return /* @__PURE__ */ jsxs("div", { className: "cb-transfer__list", children: [
1583
- /* @__PURE__ */ jsx(Skeleton, { className: "cb-transfer__skeleton-title" }),
1584
- /* @__PURE__ */ jsx("ul", { className: "cb-transfer__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsxs("li", { className: "cb-transfer__item", children: [
1585
- /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-transfer__skeleton-checkbox" }),
1586
- /* @__PURE__ */ jsx(Skeleton, { className: "cb-transfer__skeleton-label" })
1587
- ] }, index)) })
1588
- ] });
1589
- }
1590
- function normalizeTargetKeys(items, keys) {
1591
- const itemKeys = new Set(items.map((item) => item.key));
1592
- const seen = /* @__PURE__ */ new Set();
1593
- return keys.filter((key) => itemKeys.has(key) && !seen.has(key) && (seen.add(key), true));
1594
- }
1595
- function validateItemKeys(items) {
1596
- const seen = /* @__PURE__ */ new Set();
1597
- for (const item of items) {
1598
- if (seen.has(item.key)) throw new RangeError(`Transfer item keys must be unique: ${item.key}`);
1599
- seen.add(item.key);
1600
- }
1601
- }
1602
- function TransferRoot({
1603
- items,
1604
- targetKeys,
1605
- defaultTargetKeys = [],
1606
- onTargetKeysChange,
1607
- sourceTitle = "Available",
1608
- targetTitle = "Selected",
1609
- disabled = false,
1610
- "aria-label": ariaLabel = "Transfer items"
1611
- }) {
1612
- validateItemKeys(items);
1613
- const generatedId = useId();
1614
- const controlled = targetKeys !== void 0;
1615
- const [internalTargetKeys, setInternalTargetKeys] = useState(() => normalizeTargetKeys(items, defaultTargetKeys));
1616
- const [sourceSelection, setSourceSelection] = useState(() => /* @__PURE__ */ new Set());
1617
- const [targetSelection, setTargetSelection] = useState(() => /* @__PURE__ */ new Set());
1618
- const assignedKeys = normalizeTargetKeys(items, controlled ? targetKeys ?? [] : internalTargetKeys);
1619
- const assigned = useMemo(() => new Set(assignedKeys), [assignedKeys]);
1620
- const sourceItems = items.filter((item) => !assigned.has(item.key));
1621
- const targetItems = items.filter((item) => assigned.has(item.key));
1622
- useEffect(() => {
1623
- const sourceKeys = new Set(sourceItems.map((item) => item.key));
1624
- const targetKeys2 = new Set(targetItems.map((item) => item.key));
1625
- setSourceSelection((previous) => intersection(previous, sourceKeys));
1626
- setTargetSelection((previous) => intersection(previous, targetKeys2));
1627
- }, [sourceItems, targetItems]);
1628
- const updateAssignment = (next) => {
1629
- if (!controlled) setInternalTargetKeys(next);
1630
- onTargetKeysChange?.(next);
1631
- };
1632
- const toggle = (key, side, checked) => {
1633
- const setSelection = side === "source" ? setSourceSelection : setTargetSelection;
1634
- setSelection((previous) => {
1635
- const next = new Set(previous);
1636
- if (checked) next.add(key);
1637
- else next.delete(key);
1638
- return next;
1639
- });
1640
- };
1641
- const movable = (list, selected) => list.filter((item) => selected.has(item.key) && !item.disabled).map((item) => item.key);
1642
- const moveToTarget = () => {
1643
- if (disabled) return;
1644
- const moving = movable(sourceItems, sourceSelection);
1645
- if (moving.length === 0) return;
1646
- updateAssignment([...assignedKeys, ...moving]);
1647
- if (!controlled) setSourceSelection((previous) => without(previous, moving));
1648
- };
1649
- const moveToSource = () => {
1650
- if (disabled) return;
1651
- const moving = movable(targetItems, targetSelection);
1652
- if (moving.length === 0) return;
1653
- const movingSet = new Set(moving);
1654
- updateAssignment(assignedKeys.filter((key) => !movingSet.has(key)));
1655
- if (!controlled) setTargetSelection((previous) => without(previous, moving));
1656
- };
1657
- const movableSource = movable(sourceItems, sourceSelection);
1658
- const movableTarget = movable(targetItems, targetSelection);
1659
- return /* @__PURE__ */ jsxs("section", { className: "cb-transfer", "aria-label": ariaLabel, "data-disabled": disabled || void 0, children: [
1660
- /* @__PURE__ */ jsx(
1661
- TransferList,
1662
- {
1663
- id: `${generatedId}-source`,
1664
- title: sourceTitle,
1665
- items: sourceItems,
1666
- selection: sourceSelection,
1667
- disabled,
1668
- onToggle: (key, checked) => toggle(key, "source", checked)
1669
- }
1670
- ),
1671
- /* @__PURE__ */ jsxs("div", { className: "cb-transfer__actions", "aria-label": "Transfer actions", children: [
1672
- /* @__PURE__ */ jsx("button", { type: "button", className: "cb-transfer__action", onClick: moveToTarget, disabled: disabled || movableSource.length === 0, "aria-label": "Move selected to target", children: "\u2192" }),
1673
- /* @__PURE__ */ jsx("button", { type: "button", className: "cb-transfer__action", onClick: moveToSource, disabled: disabled || movableTarget.length === 0, "aria-label": "Move selected to source", children: "\u2190" })
1674
- ] }),
1675
- /* @__PURE__ */ jsx(
1676
- TransferList,
1677
- {
1678
- id: `${generatedId}-target`,
1679
- title: targetTitle,
1680
- items: targetItems,
1681
- selection: targetSelection,
1682
- disabled,
1683
- onToggle: (key, checked) => toggle(key, "target", checked)
1684
- }
1685
- )
1686
- ] });
1687
- }
1688
- function without(previous, keys) {
1689
- const next = new Set(previous);
1690
- keys.forEach((key) => next.delete(key));
1691
- return next;
1692
- }
1693
- function intersection(previous, valid) {
1694
- const next = new Set([...previous].filter((key) => valid.has(key)));
1695
- return next.size === previous.size ? previous : next;
1696
- }
1697
- function TransferList({
1698
- id,
1699
- title,
1700
- items,
1701
- selection,
1702
- disabled,
1703
- onToggle
1704
- }) {
1705
- return /* @__PURE__ */ jsxs("fieldset", { className: "cb-transfer__list", disabled, children: [
1706
- /* @__PURE__ */ jsx("legend", { className: "cb-transfer__title", children: title }),
1707
- /* @__PURE__ */ jsx("span", { className: "cb-transfer__count", "aria-live": "polite", children: items.length }),
1708
- /* @__PURE__ */ jsx("ul", { className: "cb-transfer__items", children: items.map((item) => {
1709
- const itemId = `${id}-${encodeURIComponent(item.key)}`;
1710
- const descriptionId = item.description ? `${itemId}-description` : void 0;
1711
- return /* @__PURE__ */ jsxs("li", { className: "cb-transfer__item", "data-disabled": item.disabled || void 0, children: [
1712
- /* @__PURE__ */ jsx(
1713
- "input",
1714
- {
1715
- id: itemId,
1716
- className: "cb-transfer__checkbox",
1717
- type: "checkbox",
1718
- checked: selection.has(item.key),
1719
- disabled: item.disabled,
1720
- "aria-describedby": descriptionId,
1721
- onChange: (event) => onToggle(item.key, event.target.checked)
1722
- }
1723
- ),
1724
- /* @__PURE__ */ jsxs("span", { className: "cb-transfer__content", children: [
1725
- /* @__PURE__ */ jsx("label", { className: "cb-transfer__label", htmlFor: itemId, children: /* @__PURE__ */ jsx("span", { children: item.label }) }),
1726
- item.description ? /* @__PURE__ */ jsx("span", { className: "cb-transfer__description", id: descriptionId, children: item.description }) : null
1727
- ] })
1728
- ] }, item.key);
1729
- }) })
1730
- ] });
1731
- }
1732
- var Transfer = Object.assign(TransferRoot, { Skeleton: TransferSkeleton });
1733
- function TreeSkeleton({ items = 5, size = "md", tone = "brand", "aria-label": ariaLabel = "Loading tree" }) {
1734
- return /* @__PURE__ */ jsx("div", { role: "tree", "aria-label": ariaLabel, "aria-hidden": "true", className: cn("cb-tree", `cb-tree--${size}`, `cb-tree--${tone}`, "cb-tree--skeleton"), children: /* @__PURE__ */ jsx("div", { role: "group", className: "cb-tree__group", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsx("div", { className: "cb-tree__node", children: /* @__PURE__ */ jsx(Skeleton, { className: cn("cb-tree__skeleton-line", index % 3 === 0 && "cb-tree__skeleton-line--long") }) }, index)) }) });
1735
- }
1736
- function TreeRoot({
1737
- nodes,
1738
- expandedPaths,
1739
- defaultExpandedPaths = [],
1740
- onExpandedPathsChange,
1741
- selectedPath,
1742
- defaultSelectedPath,
1743
- onSelectedPathChange,
1744
- size = "md",
1745
- tone = "brand",
1746
- motion: motion3 = true,
1747
- "aria-label": ariaLabel = "Tree"
1748
- }) {
1749
- validateTreeNodes(nodes);
1750
- const [uncontrolledExpandedPaths, setUncontrolledExpandedPaths] = useState(defaultExpandedPaths);
1751
- const [uncontrolledSelectedPath, setUncontrolledSelectedPath] = useState(defaultSelectedPath);
1752
- const currentExpandedPaths = expandedPaths ?? uncontrolledExpandedPaths;
1753
- const currentSelectedPath = selectedPath ?? uncontrolledSelectedPath;
1754
- const visibleNodes = useMemo(() => flattenVisible(nodes, currentExpandedPaths), [nodes, currentExpandedPaths]);
1755
- const firstEnabledPath = visibleNodes.find(({ node }) => !node.disabled)?.path;
1756
- const [activePath, setActivePath] = useState(() => selectedPath ?? defaultSelectedPath ?? firstEnabledPath);
1757
- const currentActivePath = visibleNodes.some(({ path, node }) => path === activePath && !node.disabled) ? activePath : firstEnabledPath;
1758
- useEffect(() => {
1759
- if (selectedPath && visibleNodes.some(({ path, node }) => path === selectedPath && !node.disabled)) {
1760
- setActivePath(selectedPath);
1761
- }
1762
- }, [selectedPath]);
1763
- const itemRefs = useRef(/* @__PURE__ */ new Map());
1764
- const setExpanded = (next) => {
1765
- if (expandedPaths === void 0) setUncontrolledExpandedPaths(next);
1766
- onExpandedPathsChange?.(next);
1767
- };
1768
- const expand = (path) => {
1769
- if (!currentExpandedPaths.includes(path)) setExpanded([...currentExpandedPaths, path]);
1770
- };
1771
- const collapse = (path) => {
1772
- if (currentExpandedPaths.includes(path)) setExpanded(currentExpandedPaths.filter((item) => item !== path));
1773
- };
1774
- const select = (path) => {
1775
- if (selectedPath === void 0) setUncontrolledSelectedPath(path);
1776
- onSelectedPathChange?.(path);
1777
- };
1778
- const focusPath = (path) => {
1779
- setActivePath(path);
1780
- itemRefs.current.get(path)?.focus();
1781
- };
1782
- const onKeyDown = (event, current) => {
1783
- const enabled = visibleNodes.filter(({ node }) => !node.disabled);
1784
- const currentIndex = enabled.findIndex(({ path }) => path === current.path);
1785
- const currentExpanded = currentExpandedPaths.includes(current.path);
1786
- const child = enabled.find(({ parentPath }) => parentPath === current.path);
1787
- if (event.key === "ArrowDown" && currentIndex < enabled.length - 1) {
1788
- event.preventDefault();
1789
- focusPath(enabled[currentIndex + 1].path);
1790
- } else if (event.key === "ArrowUp" && currentIndex > 0) {
1791
- event.preventDefault();
1792
- focusPath(enabled[currentIndex - 1].path);
1793
- } else if (event.key === "Home" && enabled[0]) {
1794
- event.preventDefault();
1795
- focusPath(enabled[0].path);
1796
- } else if (event.key === "End" && enabled.at(-1)) {
1797
- event.preventDefault();
1798
- focusPath(enabled.at(-1).path);
1799
- } else if (event.key === "ArrowRight" && current.hasChildren) {
1800
- event.preventDefault();
1801
- if (!currentExpanded) expand(current.path);
1802
- else if (child) focusPath(child.path);
1803
- } else if (event.key === "ArrowLeft") {
1804
- if (current.hasChildren && currentExpanded) {
1805
- event.preventDefault();
1806
- collapse(current.path);
1807
- } else if (current.parentPath) {
1808
- event.preventDefault();
1809
- const parent = nearestEnabledParent(current.parentPath, visibleNodes);
1810
- if (parent) focusPath(parent);
1811
- }
1812
- } else if (event.key === "Enter" || event.key === " ") {
1813
- event.preventDefault();
1814
- select(current.path);
1815
- }
1816
- };
1817
- return /* @__PURE__ */ jsx(
1818
- "div",
1819
- {
1820
- role: "tree",
1821
- "aria-label": ariaLabel,
1822
- className: cn("cb-tree", `cb-tree--${size}`, `cb-tree--${tone}`, !motion3 && "cb-tree--motion-off"),
1823
- children: /* @__PURE__ */ jsx(
1824
- TreeGroup,
1825
- {
1826
- nodes,
1827
- pathParts: [],
1828
- root: true,
1829
- level: 1,
1830
- parentPath: void 0,
1831
- expandedPaths: currentExpandedPaths,
1832
- selectedPath: currentSelectedPath,
1833
- activePath: currentActivePath,
1834
- refs: itemRefs,
1835
- onFocusPath: focusPath,
1836
- onSelect: select,
1837
- onKeyDown
1838
- }
1839
- )
1840
- }
1841
- );
1842
- }
1843
- function TreeGroup({ nodes, pathParts, root = false, level, parentPath, expandedPaths, selectedPath, activePath, refs, onFocusPath, onSelect, onKeyDown }) {
1844
- return /* @__PURE__ */ jsx("div", { role: root ? void 0 : "group", className: "cb-tree__group", children: nodes.map((node) => {
1845
- const parts = [...pathParts, node.key];
1846
- const path = toPath(parts);
1847
- const hasChildren = Boolean(node.children?.length);
1848
- const expanded = expandedPaths.includes(path);
1849
- const current = { node, path, parentPath, level, hasChildren };
1850
- return /* @__PURE__ */ jsxs("div", { className: "cb-tree__node", children: [
1851
- /* @__PURE__ */ jsxs(
1852
- "div",
1853
- {
1854
- ref: (element) => {
1855
- if (element) refs.current.set(path, element);
1856
- else refs.current.delete(path);
1857
- },
1858
- role: "treeitem",
1859
- "aria-level": level,
1860
- "aria-expanded": hasChildren ? expanded : void 0,
1861
- "aria-selected": selectedPath === path || void 0,
1862
- "aria-disabled": node.disabled || void 0,
1863
- tabIndex: !node.disabled && activePath === path ? 0 : -1,
1864
- className: "cb-tree__item",
1865
- "data-selected": selectedPath === path || void 0,
1866
- "data-disabled": node.disabled || void 0,
1867
- onFocus: () => onFocusPath(path),
1868
- onClick: () => {
1869
- if (!node.disabled) onSelect(path);
1870
- },
1871
- onKeyDown: (event) => onKeyDown(event, current),
1872
- children: [
1873
- hasChildren ? /* @__PURE__ */ jsx(TreeChevron, { expanded }) : /* @__PURE__ */ jsx("span", { className: "cb-tree__indent", "aria-hidden": "true" }),
1874
- /* @__PURE__ */ jsx("span", { className: "cb-tree__label", children: node.label }),
1875
- node.content ? /* @__PURE__ */ jsx("span", { className: "cb-tree__content", children: node.content }) : null
1876
- ]
1877
- }
1878
- ),
1879
- hasChildren && expanded ? /* @__PURE__ */ jsx(
1880
- TreeGroup,
1881
- {
1882
- nodes: node.children,
1883
- pathParts: parts,
1884
- level: level + 1,
1885
- parentPath: path,
1886
- expandedPaths,
1887
- selectedPath,
1888
- activePath,
1889
- refs,
1890
- onFocusPath,
1891
- onSelect,
1892
- onKeyDown
1893
- }
1894
- ) : null
1895
- ] }, path);
1896
- }) });
1897
- }
1898
- function TreeChevron({ expanded }) {
1899
- return /* @__PURE__ */ jsxs("span", { className: "cb-tree__chevrons", "aria-hidden": "true", "data-expanded": expanded || void 0, children: [
1900
- /* @__PURE__ */ jsx(ChevronRight, { className: "cb-tree__chevron cb-tree__chevron--collapsed" }),
1901
- /* @__PURE__ */ jsx(ChevronDown, { className: "cb-tree__chevron cb-tree__chevron--expanded" })
1902
- ] });
1903
- }
1904
- function flattenVisible(nodes, expandedPaths, pathParts = [], level = 1, parentPath) {
1905
- return nodes.flatMap((node) => {
1906
- const parts = [...pathParts, node.key];
1907
- const path = toPath(parts);
1908
- const hasChildren = Boolean(node.children?.length);
1909
- const current = { node, path, parentPath, level, hasChildren };
1910
- const children = hasChildren && expandedPaths.includes(path) ? flattenVisible(node.children, expandedPaths, parts, level + 1, path) : [];
1911
- return [current, ...children];
1912
- });
1913
- }
1914
- function toPath(parts) {
1915
- return parts.map(encodeURIComponent).join("/");
1916
- }
1917
- function nearestEnabledParent(path, visibleNodes) {
1918
- let candidate = visibleNodes.find((node) => node.path === path);
1919
- while (candidate && candidate.node.disabled) {
1920
- candidate = candidate.parentPath ? visibleNodes.find((node) => node.path === candidate.parentPath) : void 0;
1921
- }
1922
- return candidate?.path;
1923
- }
1924
- function validateTreeNodes(nodes, path = []) {
1925
- const siblingKeys = /* @__PURE__ */ new Set();
1926
- for (const node of nodes) {
1927
- if (siblingKeys.has(node.key)) {
1928
- const location = [...path, node.key].join(" / ") || node.key;
1929
- throw new Error(`Tree requires unique sibling keys; duplicate key at ${location}.`);
1930
- }
1931
- siblingKeys.add(node.key);
1932
- if (node.children?.length) validateTreeNodes(node.children, [...path, node.key]);
1933
- }
1934
- }
1935
- var Tree = Object.assign(TreeRoot, { Skeleton: TreeSkeleton });
1936
- function TreeSelectSkeleton({ size = "md" }) {
1937
- return /* @__PURE__ */ jsx(Skeleton, { className: `cb-tree-select cb-tree-select--${size} cb-tree-select--skeleton` });
1938
- }
1939
- function TreeSelectRoot({ nodes, value, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, label, placeholder = "Select\u2026", name, disabled = false, motion: motion3 = true }) {
1940
- const field = useFieldWiring();
1941
- const generatedId = useId();
1942
- const [internalValue, setInternalValue] = useState(defaultValue);
1943
- const [internalOpen, setInternalOpen] = useState(defaultOpen);
1944
- const currentValue = value !== void 0 ? value : internalValue;
1945
- const currentOpen = open ?? internalOpen;
1946
- const effectiveOpen = disabled ? false : currentOpen;
1947
- const selectedLabel = currentValue ? labelForPath(nodes, currentValue) : void 0;
1948
- const setOpen = (next) => {
1949
- if (open === void 0) setInternalOpen(next);
1950
- onOpenChange?.(next);
1951
- };
1952
- const select = (path) => {
1953
- if (value === void 0) setInternalValue(path);
1954
- onValueChange?.(path);
1955
- setOpen(false);
1956
- };
1957
- useEffect(() => {
1958
- if (disabled && currentOpen) setOpen(false);
1959
- }, [disabled, currentOpen]);
1960
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open: effectiveOpen, onOpenChange: setOpen, children: [
1961
- /* @__PURE__ */ jsx(
1962
- Popover$1.Trigger,
1963
- {
1964
- disabled,
1965
- render: /* @__PURE__ */ jsxs("button", { type: "button", id: field?.controlId ?? generatedId, className: cn("cb-tree-select", !motion3 && "cb-tree-select--motionless"), "aria-label": label, "aria-describedby": field?.describedBy, "aria-invalid": field?.invalid || void 0, "aria-required": field?.required || void 0, children: [
1966
- /* @__PURE__ */ jsx("span", { className: cn("cb-tree-select__value", !selectedLabel && "cb-tree-select__placeholder"), children: selectedLabel ?? placeholder }),
1967
- /* @__PURE__ */ jsx(ChevronsUpDown, { className: "cb-tree-select__icon", "aria-hidden": "true" })
1968
- ] })
1969
- }
1970
- ),
1971
- name && currentValue ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: currentValue }) : null,
1972
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { side: "bottom", align: "start", className: "cb-tree-select__positioner", children: /* @__PURE__ */ jsx(Popover$1.Popup, { className: cn("cb-tree-select__popup", !motion3 && "cb-tree-select__popup--motionless"), children: /* @__PURE__ */ jsx(Tree, { nodes, selectedPath: currentValue ?? void 0, onSelectedPathChange: select, motion: motion3, "aria-label": `${label} options` }) }) }) })
1973
- ] });
1974
- }
1975
- function labelForPath(nodes, path, parts = []) {
1976
- for (const node of nodes) {
1977
- const next = [...parts, node.key];
1978
- const nodePath = next.map(encodeURIComponent).join("/");
1979
- if (nodePath === path) return node.label;
1980
- const found = node.children ? labelForPath(node.children, path, next) : void 0;
1981
- if (found) return found;
1982
- }
1983
- return void 0;
1984
- }
1985
- var TreeSelect = Object.assign(TreeSelectRoot, { Skeleton: TreeSelectSkeleton });
1986
- function CascaderSkeleton({ rows = 3 }) {
1987
- return /* @__PURE__ */ jsxs("div", { className: "cb-cascader cb-cascader--skeleton", "aria-hidden": "true", children: [
1988
- /* @__PURE__ */ jsx(Skeleton, { className: "cb-cascader__skeleton-label" }),
1989
- /* @__PURE__ */ jsx(ChevronPlaceholder, {}),
1990
- /* @__PURE__ */ jsx("div", { className: "cb-cascader__skeleton-menu", children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { height: "var(--cb-control-height-sm)" }, index)) })
1991
- ] });
1992
- }
1993
- function ChevronPlaceholder() {
1994
- return /* @__PURE__ */ jsx(Skeleton.Circle, { size: "var(--cb-space-4)" });
1995
- }
1996
- function firstEnabledKey(options) {
1997
- return options.find((option) => !option.disabled)?.key;
1998
- }
1999
- function findOption(options, key) {
2000
- return options.find((option) => option.key === key);
2001
- }
2002
- function isLeafPath(options, path) {
2003
- let current = options;
2004
- for (const key of path) {
2005
- const option = findOption(current, key);
2006
- if (!option || option.disabled) return false;
2007
- current = option.children ?? [];
2008
- }
2009
- return path.length > 0 && current.length === 0;
2010
- }
2011
- function labelsForPath(options, path) {
2012
- const labels = [];
2013
- let current = options;
2014
- for (const key of path) {
2015
- const option = findOption(current, key);
2016
- if (!option) return [];
2017
- labels.push(option.label);
2018
- current = option.children ?? [];
2019
- }
2020
- return labels;
2021
- }
2022
- function validateOptions2(options, ancestors = []) {
2023
- const keys = /* @__PURE__ */ new Set();
2024
- for (const option of options) {
2025
- if (keys.has(option.key)) throw new Error(`Cascader requires unique sibling keys; duplicate key at ${[...ancestors, option.key].join(" / ")}.`);
2026
- keys.add(option.key);
2027
- if (option.children?.length) validateOptions2(option.children, [...ancestors, option.key]);
2028
- }
2029
- }
2030
- function columnsForPath(options, path) {
2031
- const columns = [];
2032
- let current = options;
2033
- for (let index = 0; current.length > 0; index += 1) {
2034
- const activeKey = findOption(current, path[index]) && !findOption(current, path[index]).disabled ? path[index] : firstEnabledKey(current);
2035
- if (!activeKey) break;
2036
- columns.push({ options: current, activeKey });
2037
- if (index >= path.length - 1) break;
2038
- const active = findOption(current, activeKey);
2039
- current = active?.children ?? [];
2040
- }
2041
- return columns;
2042
- }
2043
- function optionId(popupId, path) {
2044
- const encodedPath = path.map((key) => encodeURIComponent(key)).join("--");
2045
- return `${popupId}-option-${encodedPath}`;
2046
- }
2047
- function CascaderRoot({
2048
- options,
2049
- label,
2050
- value,
2051
- defaultValue = [],
2052
- onValueChange,
2053
- placeholder = "Select\u2026",
2054
- disabled = false,
2055
- motion: motion3 = true
2056
- }) {
2057
- validateOptions2(options);
2058
- if (label.trim().length === 0) throw new Error("Cascader label must not be empty.");
2059
- const field = useFieldWiring();
2060
- const popupId = useId();
2061
- const triggerRef = useRef(null);
2062
- const controlled = value !== void 0;
2063
- const [internalValue, setInternalValue] = useState(defaultValue);
2064
- const selectedPath = controlled ? value : internalValue;
2065
- const [open, setOpen] = useState(false);
2066
- const [activePath, setActivePath] = useState([]);
2067
- const [activeColumn, setActiveColumn] = useState(0);
2068
- const columns = useMemo(() => columnsForPath(options, activePath), [options, activePath]);
2069
- const selectedLabels = labelsForPath(options, selectedPath);
2070
- useEffect(() => {
2071
- if (disabled) setOpen(false);
2072
- }, [disabled]);
2073
- const initialize = () => {
2074
- const initialPath = isLeafPath(options, selectedPath) ? selectedPath : [firstEnabledKey(options)].filter((key) => Boolean(key));
2075
- setActivePath(initialPath);
2076
- setActiveColumn(Math.max(0, initialPath.length - 1));
2077
- };
2078
- const openPopup = () => {
2079
- if (disabled) return;
2080
- initialize();
2081
- setOpen(true);
2082
- };
2083
- const updatePathAtColumn = (columnIndex, key) => {
2084
- setActivePath((current) => [...current.slice(0, columnIndex), key]);
2085
- setActiveColumn(columnIndex);
2086
- };
2087
- const choose = (path) => {
2088
- if (disabled || !isLeafPath(options, path)) return;
2089
- if (!controlled) setInternalValue(path);
2090
- onValueChange?.(path);
2091
- setOpen(false);
2092
- requestAnimationFrame(() => triggerRef.current?.focus());
2093
- };
2094
- const currentColumn = columns[activeColumn];
2095
- const currentOption = currentColumn ? findOption(currentColumn.options, currentColumn.activeKey) : void 0;
2096
- const activeOptionId = open && currentColumn ? optionId(popupId, [...activePath.slice(0, activeColumn), currentColumn.activeKey]) : void 0;
2097
- const handleKeyDown = (event) => {
2098
- if (disabled) return;
2099
- if (!open && (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ")) {
2100
- event.preventDefault();
2101
- openPopup();
2102
- return;
2103
- }
2104
- if (!open) return;
2105
- if (event.key === "Escape") {
2106
- event.preventDefault();
2107
- setOpen(false);
2108
- } else if ((event.key === "ArrowDown" || event.key === "ArrowUp") && currentColumn) {
2109
- event.preventDefault();
2110
- const enabled = currentColumn.options.filter((option) => !option.disabled);
2111
- const index = enabled.findIndex((option) => option.key === currentColumn.activeKey);
2112
- if (enabled.length > 0) updatePathAtColumn(activeColumn, enabled[(index + (event.key === "ArrowDown" ? 1 : -1) + enabled.length) % enabled.length].key);
2113
- } else if (event.key === "ArrowRight" && currentOption?.children?.length) {
2114
- event.preventDefault();
2115
- const childKey = firstEnabledKey(currentOption.children);
2116
- if (childKey) {
2117
- setActivePath((current) => [...current.slice(0, activeColumn + 1), childKey]);
2118
- setActiveColumn(activeColumn + 1);
2119
- }
2120
- } else if (event.key === "ArrowLeft" && activeColumn > 0) {
2121
- event.preventDefault();
2122
- setActivePath((current) => current.slice(0, activeColumn));
2123
- setActiveColumn(activeColumn - 1);
2124
- } else if (event.key === "Enter" && currentOption && !currentOption.children?.length) {
2125
- event.preventDefault();
2126
- choose(activePath.slice(0, activeColumn + 1));
2127
- }
2128
- };
2129
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open, onOpenChange: setOpen, children: [
2130
- /* @__PURE__ */ jsxs(
2131
- "button",
2132
- {
2133
- ref: triggerRef,
2134
- type: "button",
2135
- role: "combobox",
2136
- className: `cb-cascader${!motion3 ? " cb-cascader--motionless" : ""}`,
2137
- id: field?.controlId,
2138
- disabled,
2139
- "aria-describedby": field?.describedBy,
2140
- "aria-label": label,
2141
- "aria-invalid": field?.invalid || void 0,
2142
- "aria-required": field?.required || void 0,
2143
- "aria-haspopup": "dialog",
2144
- "aria-expanded": open,
2145
- "aria-controls": open ? popupId : void 0,
2146
- "aria-activedescendant": activeOptionId,
2147
- onClick: openPopup,
2148
- onKeyDown: handleKeyDown,
2149
- children: [
2150
- /* @__PURE__ */ jsx("span", { className: "cb-cascader__value", children: selectedLabels.length ? selectedLabels.map((label2, index) => /* @__PURE__ */ jsxs("span", { children: [
2151
- index ? " / " : null,
2152
- label2
2153
- ] }, index)) : placeholder }),
2154
- /* @__PURE__ */ jsx(ChevronDown, { className: "cb-cascader__icon", "aria-hidden": "true" })
2155
- ]
2156
- }
2157
- ),
2158
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { anchor: triggerRef, side: "bottom", align: "start", className: "cb-cascader__positioner", children: /* @__PURE__ */ jsx(Popover$1.Popup, { id: popupId, "aria-label": "Cascader options", className: `cb-cascader__popup${!motion3 ? " cb-cascader__popup--motionless" : ""}`, initialFocus: false, finalFocus: false, children: /* @__PURE__ */ jsx("div", { className: "cb-cascader__columns", "aria-label": "Cascader options", children: columns.map((column, columnIndex) => /* @__PURE__ */ jsx("div", { className: "cb-cascader__column", role: "listbox", "aria-label": `Level ${columnIndex + 1}`, children: column.options.map((option) => {
2159
- const active = option.key === column.activeKey;
2160
- const hasChildren = Boolean(option.children?.length);
2161
- const path = [...activePath.slice(0, columnIndex), option.key];
2162
- return /* @__PURE__ */ jsxs(
2163
- "button",
2164
- {
2165
- type: "button",
2166
- role: "option",
2167
- id: optionId(popupId, path),
2168
- tabIndex: -1,
2169
- className: "cb-cascader__option",
2170
- disabled: option.disabled,
2171
- "aria-selected": active,
2172
- "data-active": active || void 0,
2173
- "data-branch": hasChildren || void 0,
2174
- onMouseDown: (event) => event.preventDefault(),
2175
- onClick: () => {
2176
- if (option.disabled) return;
2177
- if (hasChildren) {
2178
- const child = firstEnabledKey(option.children);
2179
- if (child) {
2180
- setActivePath([...path, child]);
2181
- setActiveColumn(columnIndex + 1);
2182
- }
2183
- } else choose(path);
2184
- },
2185
- children: [
2186
- /* @__PURE__ */ jsx("span", { className: "cb-cascader__option-label", children: option.label }),
2187
- hasChildren ? /* @__PURE__ */ jsx(ChevronRight, { className: "cb-cascader__option-icon", "aria-hidden": "true" }) : null
2188
- ]
2189
- },
2190
- option.key
2191
- );
2192
- }) }, columnIndex)) }) }) }) })
2193
- ] });
2194
- }
2195
- var Cascader = Object.assign(CascaderRoot, { Skeleton: CascaderSkeleton });
2196
- function ColorPickerSkeleton() {
2197
- return /* @__PURE__ */ jsx("div", { className: "cb-color-picker cb-color-picker--skeleton", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Skeleton, { className: "cb-color-picker__skeleton" }) });
2198
- }
2199
- var CORE_OPTIONS = [
2200
- { tone: "neutral", label: "Neutral" },
2201
- { tone: "brand", label: "Brand" },
2202
- { tone: "success", label: "Success" },
2203
- { tone: "warning", label: "Warning" },
2204
- { tone: "danger", label: "Danger" },
2205
- { tone: "info", label: "Info" }
2206
- ];
2207
- function validateOptions3(options) {
2208
- if (options.length === 0) throw new RangeError("ColorPicker options must not be empty.");
2209
- const tones = /* @__PURE__ */ new Set();
2210
- options.forEach((option) => {
2211
- if (tones.has(option.tone)) throw new RangeError(`ColorPicker option tones must be unique: ${option.tone}`);
2212
- tones.add(option.tone);
2213
- });
2214
- if (!options.some((option) => !option.disabled)) throw new RangeError("ColorPicker options must include an enabled tone.");
2215
- }
2216
- function ColorPickerRoot({ label, value, defaultValue = "brand", onValueChange, open, defaultOpen, onOpenChange, options = CORE_OPTIONS, name, disabled = false, motion: motion3 = true }) {
2217
- validateOptions3(options);
2218
- if (label.trim().length === 0) throw new RangeError("ColorPicker label must not be empty.");
2219
- const field = useFieldWiring();
2220
- const controlledValue = value !== void 0;
2221
- const [internalValue, setInternalValue] = useState(defaultValue);
2222
- const selected = controlledValue ? value : internalValue;
2223
- if (!options.some((option) => option.tone === selected && !option.disabled)) throw new RangeError(`ColorPicker value must match an enabled option: ${selected}`);
2224
- const controlledOpen = open !== void 0;
2225
- const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
2226
- const expanded = controlledOpen ? open : internalOpen;
2227
- const effectiveExpanded = disabled ? false : expanded;
2228
- const [activeIndex, setActiveIndex] = useState(() => Math.max(0, options.findIndex((option) => option.tone === selected && !option.disabled)));
2229
- const optionRefs = useRef([]);
2230
- const enabled = useMemo(() => options.map((option, index) => ({ option, index })).filter(({ option }) => !option.disabled), [options]);
2231
- useEffect(() => {
2232
- const selectedIndex = options.findIndex((option) => option.tone === selected && !option.disabled);
2233
- if (selectedIndex >= 0) setActiveIndex(selectedIndex);
2234
- }, [options, selected]);
2235
- useEffect(() => {
2236
- if (disabled && expanded) {
2237
- if (!controlledOpen) setInternalOpen(false);
2238
- onOpenChange?.(false);
2239
- }
2240
- }, [controlledOpen, disabled, expanded, onOpenChange]);
2241
- const changeOpen = (next) => {
2242
- if (!controlledOpen) setInternalOpen(next);
2243
- onOpenChange?.(next);
2244
- };
2245
- const select = (tone) => {
2246
- if (disabled) return;
2247
- if (!controlledValue) setInternalValue(tone);
2248
- onValueChange?.(tone);
2249
- changeOpen(false);
2250
- };
2251
- const move = (amount) => {
2252
- if (enabled.length === 0) return;
2253
- const current = enabled.findIndex(({ index }) => index === activeIndex);
2254
- const next = enabled[(Math.max(0, current) + amount + enabled.length) % enabled.length].index;
2255
- setActiveIndex(next);
2256
- optionRefs.current[next]?.focus();
2257
- };
2258
- const handleGridKey = (event) => {
2259
- if (event.key === "ArrowRight") {
2260
- event.preventDefault();
2261
- move(1);
2262
- } else if (event.key === "ArrowLeft") {
2263
- event.preventDefault();
2264
- move(-1);
2265
- } else if (event.key === "ArrowDown") {
2266
- event.preventDefault();
2267
- move(3);
2268
- } else if (event.key === "ArrowUp") {
2269
- event.preventDefault();
2270
- move(-3);
2271
- } else if (event.key === "Escape") {
2272
- event.preventDefault();
2273
- changeOpen(false);
2274
- }
2275
- };
2276
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open: effectiveExpanded, onOpenChange: changeOpen, children: [
2277
- /* @__PURE__ */ jsxs("span", { className: "cb-color-picker", "data-motion": motion3 ? void 0 : "off", "data-disabled": disabled || void 0, children: [
2278
- name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected }) : null,
2279
- /* @__PURE__ */ jsxs(Popover$1.Trigger, { render: /* @__PURE__ */ jsx("button", { type: "button" }), id: field?.controlId, className: "cb-color-picker__trigger", "aria-label": label, "aria-describedby": field?.describedBy, "aria-invalid": field?.invalid || void 0, "aria-required": field?.required || void 0, disabled, children: [
2280
- /* @__PURE__ */ jsx("span", { className: "cb-color-picker__swatch", "data-tone": selected, "aria-hidden": "true" }),
2281
- /* @__PURE__ */ jsx("span", { children: options.find((option) => option.tone === selected)?.label ?? selected })
2282
- ] })
2283
- ] }),
2284
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { side: "bottom", align: "start", className: "cb-color-picker__positioner", children: /* @__PURE__ */ jsx(Popover$1.Popup, { className: "cb-color-picker__popup", "data-motion": motion3 ? void 0 : "off", initialFocus: () => optionRefs.current[activeIndex] ?? false, children: /* @__PURE__ */ jsx("div", { className: "cb-color-picker__grid", role: "grid", "aria-label": `${label} options`, onKeyDown: handleGridKey, children: Array.from({ length: Math.ceil(options.length / 3) }, (_, rowIndex) => /* @__PURE__ */ jsx("div", { role: "row", className: "cb-color-picker__row", children: options.slice(rowIndex * 3, rowIndex * 3 + 3).map((option, offset) => {
2285
- const index = rowIndex * 3 + offset;
2286
- return /* @__PURE__ */ jsx("button", { ref: (node) => {
2287
- optionRefs.current[index] = node;
2288
- }, type: "button", role: "gridcell", className: "cb-color-picker__option", "data-tone": option.tone, "data-selected": selected === option.tone || void 0, tabIndex: index === activeIndex ? 0 : -1, disabled: option.disabled, "aria-label": option.label, "aria-selected": selected === option.tone, onClick: () => select(option.tone), onKeyDown: (event) => {
2289
- if ((event.key === "Enter" || event.key === " ") && !option.disabled) {
2290
- event.preventDefault();
2291
- select(option.tone);
2292
- }
2293
- }, children: /* @__PURE__ */ jsx("span", { className: "cb-color-picker__swatch", "data-tone": option.tone, "aria-hidden": "true" }) }, option.tone);
2294
- }) }, rowIndex)) }) }) }) })
2295
- ] });
2296
- }
2297
- var ColorPicker = Object.assign(ColorPickerRoot, { Skeleton: ColorPickerSkeleton });
2298
- function ModalSkeleton({
2299
- size = "md",
2300
- lines = 3,
2301
- withActions = true,
2302
- className
2303
- }) {
2304
- return /* @__PURE__ */ jsxs(
2305
- "div",
2306
- {
2307
- className: cn("cb-modal-skeleton", `cb-modal--${size}`, className),
2308
- "aria-hidden": "true",
2309
- children: [
2310
- /* @__PURE__ */ jsx(Skeleton, { width: "48%", height: "1.125rem" }),
2311
- /* @__PURE__ */ jsx("div", { className: "cb-modal-skeleton__body", children: /* @__PURE__ */ jsx(Skeleton.Text, { lines }) }),
2312
- withActions ? /* @__PURE__ */ jsxs("div", { className: "cb-modal__footer", children: [
2313
- /* @__PURE__ */ jsx(Skeleton, { width: "5rem", height: "2.5rem" }),
2314
- /* @__PURE__ */ jsx(Skeleton, { width: "5rem", height: "2.5rem" })
2315
- ] }) : null
2316
- ]
2317
- }
2318
- );
2319
- }
2320
- var CONFIRM_ICONS = {
2321
- neutral: /* @__PURE__ */ jsx(Info, { size: 20 }),
2322
- brand: /* @__PURE__ */ jsx(Info, { size: 20 }),
2323
- info: /* @__PURE__ */ jsx(Info, { size: 20 }),
2324
- success: /* @__PURE__ */ jsx(CheckCircle2, { size: 20 }),
2325
- warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 20 }),
2326
- danger: /* @__PURE__ */ jsx(XCircle, { size: 20 })
2327
- };
2328
- function ModalRoot({
2329
- open,
2330
- defaultOpen,
2331
- onOpenChange,
2332
- title,
2333
- description,
2334
- children,
2335
- footer,
2336
- size = "md",
2337
- placement = "center",
2338
- trigger,
2339
- className
2340
- }) {
2341
- return /* @__PURE__ */ jsxs(Dialog.Root, { open, defaultOpen, onOpenChange, children: [
2342
- trigger ? /* @__PURE__ */ jsx(Dialog.Trigger, { render: trigger }) : null,
2343
- /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
2344
- /* @__PURE__ */ jsx(Dialog.Backdrop, { className: "cb-modal__backdrop" }),
2345
- /* @__PURE__ */ jsxs(
2346
- Dialog.Popup,
2347
- {
2348
- className: cn("cb-modal", `cb-modal--${size}`, `cb-modal--${placement}`, className),
2349
- children: [
2350
- /* @__PURE__ */ jsx(Dialog.Title, { className: "cb-modal__title", children: title }),
2351
- description ? /* @__PURE__ */ jsx(Dialog.Description, { className: "cb-modal__description", children: description }) : null,
2352
- children ? /* @__PURE__ */ jsx("div", { className: "cb-modal__body", children }) : null,
2353
- footer ? /* @__PURE__ */ jsx("div", { className: "cb-modal__footer", children: footer }) : null
2354
- ]
2355
- }
2356
- )
2357
- ] })
2358
- ] });
2359
- }
2360
- function ModalConfirm({
2361
- title,
2362
- description,
2363
- children,
2364
- cancelAction,
2365
- confirmAction,
2366
- tone = "warning",
2367
- icon,
2368
- className,
2369
- ...rootProps
2370
- }) {
2371
- return /* @__PURE__ */ jsx(
2372
- ModalRoot,
2373
- {
2374
- ...rootProps,
2375
- title: /* @__PURE__ */ jsxs("span", { className: "cb-modal-confirm__heading", children: [
2376
- icon === null ? null : /* @__PURE__ */ jsx("span", { className: "cb-modal-confirm__icon", "data-tone": tone, "aria-hidden": "true", children: icon ?? CONFIRM_ICONS[tone] }),
2377
- /* @__PURE__ */ jsx("span", { children: title })
2378
- ] }),
2379
- description,
2380
- footer: /* @__PURE__ */ jsxs(Fragment, { children: [
2381
- cancelAction,
2382
- confirmAction
2383
- ] }),
2384
- size: "sm",
2385
- placement: "center",
2386
- className: cn("cb-modal-confirm", className),
2387
- children
2388
- }
2389
- );
2390
- }
2391
- var Modal = Object.assign(ModalRoot, {
2392
- Confirm: ModalConfirm,
2393
- Skeleton: ModalSkeleton
2394
- });
2395
- var DialogClose = Dialog.Close;
2396
- function Drawer({
2397
- open,
2398
- defaultOpen,
2399
- onOpenChange,
2400
- title,
2401
- description,
2402
- children,
2403
- footer,
2404
- trigger,
2405
- className
2406
- }) {
2407
- return /* @__PURE__ */ jsxs(Dialog.Root, { open, defaultOpen, onOpenChange, children: [
2408
- trigger ? /* @__PURE__ */ jsx(Dialog.Trigger, { render: trigger }) : null,
2409
- /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
2410
- /* @__PURE__ */ jsx(Dialog.Backdrop, { className: "cb-drawer__backdrop" }),
2411
- /* @__PURE__ */ jsxs(Dialog.Popup, { className: cn("cb-drawer", className), children: [
2412
- /* @__PURE__ */ jsx(Dialog.Title, { className: "cb-drawer__title", children: title }),
2413
- description ? /* @__PURE__ */ jsx(Dialog.Description, { className: "cb-drawer__description", children: description }) : null,
2414
- children ? /* @__PURE__ */ jsx("div", { className: "cb-drawer__body", children }) : null,
2415
- footer ? /* @__PURE__ */ jsx("div", { className: "cb-drawer__footer", children: footer }) : null
2416
- ] })
2417
- ] })
2418
- ] });
2419
- }
2420
- var DrawerClose = Dialog.Close;
2421
-
2422
- // src/overlay/command.util.ts
2423
- function rankCommand(command, query) {
2424
- const needle = query.trim().toLowerCase();
2425
- if (!needle) return 1;
2426
- const label = command.label.toLowerCase();
2427
- if (label === needle) return 100;
2428
- if (label.startsWith(needle)) return 80;
2429
- if (label.split(/\s+/).some((word) => word.startsWith(needle))) return 60;
2430
- if (label.includes(needle)) return 40;
2431
- if (command.keywords?.some((keyword) => keyword.toLowerCase().includes(needle))) return 20;
2432
- return 0;
2433
- }
2434
- function filterCommands(commands, query) {
2435
- return commands.map((command, index) => ({ command, score: rankCommand(command, query), index })).filter((entry) => entry.score > 0).sort((a, b) => b.score === a.score ? a.index - b.index : b.score - a.score).map((entry) => entry.command);
2436
- }
2437
- function groupCommands(commands) {
2438
- const groups = [];
2439
- for (const command of commands) {
2440
- const existing = groups.find((entry) => entry.group === command.group);
2441
- if (existing) existing.items.push(command);
2442
- else groups.push({ group: command.group, items: [command] });
2443
- }
2444
- return groups;
2445
- }
2446
- function CommandPalette({
2447
- open,
2448
- onOpenChange,
2449
- commands,
2450
- placeholder = "Type a command\u2026",
2451
- emptyMessage = "No matching command",
2452
- className
2453
- }) {
2454
- const [query, setQuery] = useState("");
2455
- const [active, setActive] = useState(0);
2456
- const results = useMemo(() => filterCommands(commands, query), [commands, query]);
2457
- const groups = useMemo(() => groupCommands(results), [results]);
2458
- useEffect(() => {
2459
- if (!open) {
2460
- setQuery("");
2461
- setActive(0);
2462
- }
2463
- }, [open]);
2464
- useEffect(() => setActive(0), [query]);
2465
- const run = (command) => {
2466
- onOpenChange(false);
2467
- command.onRun();
2468
- };
2469
- return /* @__PURE__ */ jsx(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
2470
- /* @__PURE__ */ jsx(Dialog.Backdrop, { className: "cb-palette__backdrop" }),
2471
- /* @__PURE__ */ jsxs(Dialog.Popup, { className: cn("cb-palette", className), "aria-label": "Command palette", children: [
2472
- /* @__PURE__ */ jsxs("div", { className: "cb-palette__search", children: [
2473
- /* @__PURE__ */ jsx(Search, { size: 16, className: "cb-palette__search-icon" }),
2474
- /* @__PURE__ */ jsx(
2475
- "input",
2476
- {
2477
- className: "cb-palette__input",
2478
- value: query,
2479
- placeholder,
2480
- autoFocus: true,
2481
- role: "combobox",
2482
- "aria-expanded": true,
2483
- "aria-controls": "cb-palette-list",
2484
- "aria-activedescendant": results[active] ? `cb-palette-${results[active].id}` : void 0,
2485
- onChange: (event) => setQuery(event.target.value),
2486
- onKeyDown: (event) => {
2487
- if (event.key === "ArrowDown") {
2488
- setActive((index) => Math.min(index + 1, results.length - 1));
2489
- event.preventDefault();
2490
- }
2491
- if (event.key === "ArrowUp") {
2492
- setActive((index) => Math.max(index - 1, 0));
2493
- event.preventDefault();
2494
- }
2495
- if (event.key === "Enter" && results[active]) {
2496
- run(results[active]);
2497
- event.preventDefault();
2498
- }
2499
- }
2500
- }
2501
- )
2502
- ] }),
2503
- /* @__PURE__ */ jsx("div", { className: "cb-palette__list", id: "cb-palette-list", role: "listbox", children: results.length === 0 ? /* @__PURE__ */ jsx("p", { className: "cb-palette__empty", children: emptyMessage }) : groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "cb-palette__group", children: [
2504
- group.group ? /* @__PURE__ */ jsx("p", { className: "cb-palette__group-label", children: group.group }) : null,
2505
- group.items.map((command) => {
2506
- const index = results.indexOf(command);
2507
- return /* @__PURE__ */ jsxs(
2508
- "div",
2509
- {
2510
- id: `cb-palette-${command.id}`,
2511
- role: "option",
2512
- "aria-selected": index === active,
2513
- className: "cb-palette__item",
2514
- "data-active": index === active || void 0,
2515
- onMouseMove: () => setActive(index),
2516
- onClick: () => run(command),
2517
- children: [
2518
- /* @__PURE__ */ jsx("span", { className: "cb-palette__icon", children: command.icon }),
2519
- /* @__PURE__ */ jsx("span", { className: "cb-palette__label", children: command.label }),
2520
- command.shortcut ? /* @__PURE__ */ jsx("kbd", { className: "cb-palette__shortcut", children: command.shortcut }) : null
2521
- ]
2522
- },
2523
- command.id
2524
- );
2525
- })
2526
- ] }, group.group ?? "ungrouped")) })
2527
- ] })
2528
- ] }) });
2529
- }
2530
- function ProgressBar({ value, max = 100, tone = "brand", size = "md", label, showValue, className }) {
2531
- const indeterminate = value === void 0;
2532
- const clamped = indeterminate ? 0 : Math.min(Math.max(value, 0), max);
2533
- const percent = max > 0 ? Math.round(clamped / max * 100) : 0;
2534
- return /* @__PURE__ */ jsxs("div", { className: cn("cb-progress", className), "data-tone": tone, "data-size": size, children: [
2535
- /* @__PURE__ */ jsx(
2536
- "div",
2537
- {
2538
- className: "cb-progress__track",
2539
- role: "progressbar",
2540
- "aria-label": label,
2541
- "aria-valuemin": indeterminate ? void 0 : 0,
2542
- "aria-valuemax": indeterminate ? void 0 : max,
2543
- "aria-valuenow": indeterminate ? void 0 : clamped,
2544
- children: /* @__PURE__ */ jsx(
2545
- "div",
2546
- {
2547
- className: "cb-progress__fill",
2548
- "data-indeterminate": indeterminate || void 0,
2549
- style: indeterminate ? void 0 : { width: `${percent}%` }
2550
- }
2551
- )
2552
- }
2553
- ),
2554
- showValue && !indeterminate ? /* @__PURE__ */ jsxs("span", { className: "cb-progress__value", children: [
2555
- percent,
2556
- "%"
2557
- ] }) : null
2558
- ] });
2559
- }
2560
- function Checklist({ title = "Get started", tasks, completeSlot, className }) {
2561
- const done = tasks.filter((task) => task.done).length;
2562
- const complete = tasks.length > 0 && done === tasks.length;
2563
- return /* @__PURE__ */ jsxs("section", { className: cn("cb-checklist", className), "aria-label": typeof title === "string" ? title : "Checklist", children: [
2564
- /* @__PURE__ */ jsxs("header", { className: "cb-checklist__head", children: [
2565
- /* @__PURE__ */ jsx("p", { className: "cb-checklist__title", children: title }),
2566
- /* @__PURE__ */ jsxs("span", { className: "cb-checklist__count", children: [
2567
- done,
2568
- " of ",
2569
- tasks.length
2570
- ] })
2571
- ] }),
2572
- /* @__PURE__ */ jsx(ProgressBar, { value: done, max: Math.max(tasks.length, 1), size: "sm", label: `${done} of ${tasks.length} tasks done` }),
2573
- complete && completeSlot ? /* @__PURE__ */ jsx("div", { className: "cb-checklist__complete", children: completeSlot }) : /* @__PURE__ */ jsx("ul", { className: "cb-checklist__list", children: tasks.map((task) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
2574
- "button",
2575
- {
2576
- type: "button",
2577
- className: "cb-checklist__task",
2578
- "data-done": task.done || void 0,
2579
- onClick: task.onSelect,
2580
- disabled: !task.onSelect,
2581
- children: [
2582
- /* @__PURE__ */ jsx("span", { className: "cb-checklist__marker", "aria-hidden": "true", children: task.done ? /* @__PURE__ */ jsx(Check, { size: 12, strokeWidth: 3 }) : null }),
2583
- /* @__PURE__ */ jsxs("span", { className: "cb-checklist__text", children: [
2584
- /* @__PURE__ */ jsxs("span", { className: "cb-checklist__label", children: [
2585
- task.label,
2586
- /* @__PURE__ */ jsx("span", { className: "cb-visually-hidden", children: task.done ? " (done)" : "" })
2587
- ] }),
2588
- task.description ? /* @__PURE__ */ jsx("span", { className: "cb-checklist__description", children: task.description }) : null
2589
- ] }),
2590
- task.onSelect && !task.done ? /* @__PURE__ */ jsx(ChevronRight, { size: 16, className: "cb-checklist__chevron" }) : null
2591
- ]
2592
- }
2593
- ) }, task.id)) })
2594
- ] });
2595
- }
2596
- function Popover({
2597
- trigger,
2598
- children,
2599
- side = "bottom",
2600
- align = "center",
2601
- sideOffset = 8,
2602
- showArrow = true,
2603
- open,
2604
- onOpenChange,
2605
- className
2606
- }) {
2607
- return /* @__PURE__ */ jsxs(Popover$1.Root, { open, onOpenChange, children: [
2608
- /* @__PURE__ */ jsx(Popover$1.Trigger, { render: trigger }),
2609
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
2610
- Popover$1.Positioner,
2611
- {
2612
- side,
2613
- align,
2614
- sideOffset,
2615
- className: "cb-popover-positioner",
2616
- children: /* @__PURE__ */ jsxs(Popover$1.Popup, { className: cn("cb-popover", className), children: [
2617
- showArrow ? /* @__PURE__ */ jsx(Popover$1.Arrow, { className: "cb-popover__arrow", children: /* @__PURE__ */ jsx(ArrowShape, {}) }) : null,
2618
- children
2619
- ] })
2620
- }
2621
- ) })
2622
- ] });
2623
- }
2624
- function Tooltip({
2625
- children,
2626
- label,
2627
- side = "top",
2628
- align = "center",
2629
- sideOffset = 6,
2630
- showArrow = true,
2631
- delay = 400
2632
- }) {
2633
- return /* @__PURE__ */ jsx(Tooltip$1.Provider, { delay, children: /* @__PURE__ */ jsxs(Tooltip$1.Root, { children: [
2634
- /* @__PURE__ */ jsx(Tooltip$1.Trigger, { render: children }),
2635
- /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(
2636
- Tooltip$1.Positioner,
2637
- {
2638
- side,
2639
- align,
2640
- sideOffset,
2641
- className: "cb-tooltip-positioner",
2642
- children: /* @__PURE__ */ jsxs(Tooltip$1.Popup, { className: "cb-tooltip", children: [
2643
- showArrow ? /* @__PURE__ */ jsx(Tooltip$1.Arrow, { className: "cb-tooltip__arrow", children: /* @__PURE__ */ jsx(ArrowShape, {}) }) : null,
2644
- label
2645
- ] })
2646
- }
2647
- ) })
2648
- ] }) });
2649
- }
2650
- function ArrowShape() {
2651
- return /* @__PURE__ */ jsxs(
2652
- "svg",
2653
- {
2654
- className: "cb-arrow",
2655
- viewBox: "0 0 16 8",
2656
- preserveAspectRatio: "none",
2657
- "aria-hidden": "true",
2658
- focusable: "false",
2659
- children: [
2660
- /* @__PURE__ */ jsx("path", { className: "cb-arrow__fill", d: "M0 8 L8 0 L16 8 Z" }),
2661
- /* @__PURE__ */ jsx("path", { className: "cb-arrow__edge", d: "M0 8 L8 0 L16 8" })
2662
- ]
2663
- }
2664
- );
2665
- }
2666
- function PopconfirmSkeleton({
2667
- withDescription = true,
2668
- withCancel = true
2669
- }) {
2670
- return /* @__PURE__ */ jsxs("div", { className: "cb-popconfirm cb-popconfirm--skeleton", "aria-hidden": "true", children: [
2671
- /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-popconfirm__skeleton-icon" }),
2672
- /* @__PURE__ */ jsxs("div", { className: "cb-popconfirm__content", children: [
2673
- /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-popconfirm__skeleton-title" }),
2674
- withDescription ? /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-popconfirm__skeleton-description" }) : null,
2675
- /* @__PURE__ */ jsxs("div", { className: "cb-popconfirm__actions", children: [
2676
- withCancel ? /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-popconfirm__skeleton-action" }) : null,
2677
- /* @__PURE__ */ jsx("span", { className: "cb-skeleton cb-popconfirm__skeleton-action" })
2678
- ] })
2679
- ] })
2680
- ] });
2681
- }
2682
- function PopconfirmRoot({
2683
- trigger,
2684
- title,
2685
- description,
2686
- cancelAction,
2687
- confirmAction,
2688
- tone = "warning",
2689
- icon,
2690
- side = "top",
2691
- align = "center",
2692
- open,
2693
- defaultOpen,
2694
- onOpenChange,
2695
- motion: motion3 = true
2696
- }) {
2697
- const hasIcon = icon !== null;
2698
- return /* @__PURE__ */ jsxs(
2699
- Popover$1.Root,
2700
- {
2701
- open,
2702
- defaultOpen,
2703
- onOpenChange,
2704
- children: [
2705
- /* @__PURE__ */ jsx(Popover$1.Trigger, { render: trigger }),
2706
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
2707
- Popover$1.Positioner,
2708
- {
2709
- side,
2710
- align,
2711
- className: "cb-popconfirm__positioner",
2712
- children: /* @__PURE__ */ jsxs(
2713
- Popover$1.Popup,
2714
- {
2715
- className: "cb-popconfirm",
2716
- "data-tone": tone,
2717
- "data-motion": motion3 ? void 0 : "off",
2718
- "data-icon": hasIcon ? void 0 : "none",
2719
- children: [
2720
- /* @__PURE__ */ jsx(Popover$1.Arrow, { className: "cb-popconfirm__arrow", children: /* @__PURE__ */ jsx(ArrowShape, {}) }),
2721
- hasIcon ? /* @__PURE__ */ jsx("span", { className: "cb-popconfirm__icon", "aria-hidden": "true", children: icon ?? /* @__PURE__ */ jsx(AlertTriangle, { size: 18 }) }) : null,
2722
- /* @__PURE__ */ jsxs("div", { className: "cb-popconfirm__content", children: [
2723
- /* @__PURE__ */ jsx(Popover$1.Title, { className: "cb-popconfirm__title", children: title }),
2724
- description ? /* @__PURE__ */ jsx(Popover$1.Description, { className: "cb-popconfirm__description", children: description }) : null,
2725
- /* @__PURE__ */ jsxs("div", { className: "cb-popconfirm__actions", children: [
2726
- cancelAction ? /* @__PURE__ */ jsx(Popover$1.Close, { render: cancelAction }) : null,
2727
- /* @__PURE__ */ jsx(Popover$1.Close, { render: confirmAction })
2728
- ] })
2729
- ] })
2730
- ]
2731
- }
2732
- )
2733
- }
2734
- ) })
2735
- ]
2736
- }
2737
- );
2738
- }
2739
- var Popconfirm = Object.assign(PopconfirmRoot, {
2740
- Skeleton: PopconfirmSkeleton
2741
- });
2742
- function Tag({
2743
- children,
2744
- tone = "neutral",
2745
- variant = "soft",
2746
- size = "md",
2747
- icon,
2748
- onClick,
2749
- pressed,
2750
- onClose,
2751
- render,
2752
- className
2753
- }) {
2754
- const labels = useLabels();
2755
- const classes = cn("cb-badge", "cb-tag", `cb-badge--${variant}`, `cb-badge--${size}`, className);
2756
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
2757
- icon,
2758
- children
2759
- ] });
2760
- if (render && isValidElement(render)) {
2761
- return cloneElement(render, {
2762
- className: cn(classes, "cb-tag--pressable", render.props.className),
2763
- "data-tone": tone,
2764
- children: body
2765
- });
2766
- }
2767
- if (onClose) {
2768
- return /* @__PURE__ */ jsxs("span", { className: classes, "data-tone": tone, children: [
2769
- body,
2770
- /* @__PURE__ */ jsx("button", { type: "button", className: "cb-tag__close", "aria-label": labels.dismiss, onClick: onClose, children: /* @__PURE__ */ jsx(X, { size: 12 }) })
2771
- ] });
2772
- }
2773
- if (onClick) {
2774
- return /* @__PURE__ */ jsx(
2775
- "button",
2776
- {
2777
- type: "button",
2778
- className: cn(classes, "cb-tag--pressable"),
2779
- "data-tone": tone,
2780
- "aria-pressed": pressed,
2781
- onClick,
2782
- children: body
2783
- }
2784
- );
2785
- }
2786
- return /* @__PURE__ */ jsx("span", { className: classes, "data-tone": tone, children: body });
2787
- }
2788
- function Rate({
2789
- value,
2790
- onValueChange,
2791
- count = 5,
2792
- size = 20,
2793
- readOnly = false,
2794
- label,
2795
- className
2796
- }) {
2797
- const group = useId();
2798
- const field = useFieldWiring();
2799
- const scores = Array.from({ length: count }, (_, i) => i + 1);
2800
- if (readOnly) {
2801
- return /* @__PURE__ */ jsx(
2802
- "span",
2803
- {
2804
- className: cn("cb-rate", "cb-rate--static", className),
2805
- role: "img",
2806
- "aria-label": label ? `${label}: ${value}/${count}` : `${value}/${count}`,
2807
- children: scores.map((n) => /* @__PURE__ */ jsx(Star, { size, strokeWidth: 2.5, fill: value >= n ? "currentColor" : "none" }, n))
2808
- }
2809
- );
2810
- }
2811
- return /* @__PURE__ */ jsx(
2812
- "span",
2813
- {
2814
- className: cn("cb-rate", className),
2815
- role: "radiogroup",
2816
- "aria-label": label,
2817
- "aria-describedby": field?.describedBy,
2818
- children: scores.map((n) => /* @__PURE__ */ jsx(
2819
- "button",
2820
- {
2821
- id: `${group}-${n}`,
2822
- type: "button",
2823
- className: "cb-rate__star",
2824
- role: "radio",
2825
- "aria-checked": value === n,
2826
- "aria-label": `${n}/${count}`,
2827
- onClick: () => onValueChange?.(value === n ? 0 : n),
2828
- children: /* @__PURE__ */ jsx(Star, { size, strokeWidth: 2.5, fill: value >= n ? "currentColor" : "none" })
2829
- },
2830
- n
2831
- ))
2832
- }
2833
- );
418
+ function ToastProvider({
419
+ children,
420
+ timeout = 5e3,
421
+ limit = 4,
422
+ position = "bottom-end"
423
+ }) {
424
+ return /* @__PURE__ */ jsxs(Toast.Provider, { timeout, limit, children: [
425
+ children,
426
+ /* @__PURE__ */ jsx(Toast.Portal, { children: /* @__PURE__ */ jsx(Toast.Viewport, { className: "cb-toast__viewport", "data-position": position, children: /* @__PURE__ */ jsx(ToastList, {}) }) })
427
+ ] });
2834
428
  }
2835
429
  var ICONS = {
2836
- neutral: /* @__PURE__ */ jsx(Info, { size: 18 }),
2837
- brand: /* @__PURE__ */ jsx(Info, { size: 18 }),
2838
- info: /* @__PURE__ */ jsx(Info, { size: 18 }),
2839
- success: /* @__PURE__ */ jsx(CheckCircle2, { size: 18 }),
2840
- warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 18 }),
2841
- danger: /* @__PURE__ */ jsx(XCircle, { size: 18 })
2842
- };
2843
- function Alert({ title, children, tone = "info", icon, actions, onDismiss, className }) {
2844
- const labels = useLabels();
2845
- const assertive = tone === "danger" || tone === "warning";
2846
- return /* @__PURE__ */ jsxs(
2847
- "div",
2848
- {
2849
- className: cn("cb-alert", className),
2850
- "data-tone": tone,
2851
- role: assertive ? "alert" : "status",
2852
- "aria-live": assertive ? "assertive" : "polite",
2853
- children: [
2854
- icon === null ? null : /* @__PURE__ */ jsx("span", { className: "cb-alert__icon", children: icon ?? ICONS[tone] }),
2855
- /* @__PURE__ */ jsxs("div", { className: "cb-alert__body", children: [
2856
- title ? /* @__PURE__ */ jsx("p", { className: "cb-alert__title", children: title }) : null,
2857
- children ? /* @__PURE__ */ jsx("div", { className: "cb-alert__text", children }) : null,
2858
- actions ? /* @__PURE__ */ jsx("div", { className: "cb-alert__actions", children: actions }) : null
2859
- ] }),
2860
- onDismiss ? /* @__PURE__ */ jsx("button", { type: "button", className: "cb-alert__close", "aria-label": labels.dismiss, onClick: onDismiss, children: /* @__PURE__ */ jsx(X, { size: 16 }) }) : null
2861
- ]
2862
- }
2863
- );
2864
- }
2865
- function ToastProvider({
2866
- children,
2867
- timeout = 5e3,
2868
- limit = 4,
2869
- position = "bottom-end"
2870
- }) {
2871
- return /* @__PURE__ */ jsxs(Toast.Provider, { timeout, limit, children: [
2872
- children,
2873
- /* @__PURE__ */ jsx(Toast.Portal, { children: /* @__PURE__ */ jsx(Toast.Viewport, { className: "cb-toast__viewport", "data-position": position, children: /* @__PURE__ */ jsx(ToastList, {}) }) })
2874
- ] });
2875
- }
2876
- var ICONS2 = {
2877
430
  success: /* @__PURE__ */ jsx(CheckCircle2, { size: 16 }),
2878
431
  warning: /* @__PURE__ */ jsx(AlertTriangle, { size: 16 }),
2879
432
  danger: /* @__PURE__ */ jsx(XCircle, { size: 16 }),
@@ -2884,311 +437,30 @@ function ToastList() {
2884
437
  const { toasts } = Toast.useToastManager();
2885
438
  const labels = useLabels();
2886
439
  return toasts.map((toast) => /* @__PURE__ */ jsxs(Toast.Root, { toast, className: cn("cb-toast"), "data-tone": toast.type ?? "neutral", children: [
2887
- /* @__PURE__ */ jsx("span", { className: "cb-toast__icon", children: ICONS2[toast.type ?? "info"] ?? ICONS2.info }),
2888
- /* @__PURE__ */ jsxs(Toast.Content, { className: "cb-toast__content", children: [
2889
- /* @__PURE__ */ jsx(Toast.Title, { className: "cb-toast__title" }),
2890
- /* @__PURE__ */ jsx(Toast.Description, { className: "cb-toast__description" })
2891
- ] }),
2892
- /* @__PURE__ */ jsx(Toast.Action, { className: "cb-toast__action" }),
2893
- /* @__PURE__ */ jsx(Toast.Close, { className: "cb-toast__close", "aria-label": labels.dismiss, children: /* @__PURE__ */ jsx(X, { size: 14 }) })
2894
- ] }, toast.id));
2895
- }
2896
- function useToast() {
2897
- const manager = Toast.useToastManager();
2898
- return {
2899
- show: ({ title, description, tone = "info", timeout, action }) => manager.add({
2900
- title,
2901
- description,
2902
- type: tone,
2903
- // Errors do not time out: something went wrong is not a thing to miss by looking away.
2904
- timeout: timeout ?? (tone === "danger" ? 0 : void 0),
2905
- ...action ? { actionProps: { children: action.label, onClick: action.onClick } } : {}
2906
- }),
2907
- close: (id) => manager.close(id),
2908
- promise: manager.promise
2909
- };
2910
- }
2911
- function WatermarkSkeleton({
2912
- content = "Loading",
2913
- tone = "neutral",
2914
- density = "md",
2915
- direction = "horizontal",
2916
- lines = 3
2917
- }) {
2918
- return /* @__PURE__ */ jsxs(
2919
- "div",
2920
- {
2921
- className: cn(
2922
- "cb-watermark",
2923
- "cb-watermark--skeleton",
2924
- `cb-watermark--${density}`,
2925
- `cb-watermark--${direction}`
2926
- ),
2927
- "data-tone": tone,
2928
- "aria-hidden": "true",
2929
- children: [
2930
- /* @__PURE__ */ jsx("div", { className: "cb-watermark__content", children: /* @__PURE__ */ jsx(Skeleton.Text, { lines }) }),
2931
- /* @__PURE__ */ jsx(WatermarkMarks, { content, density, direction })
2932
- ]
2933
- }
2934
- );
2935
- }
2936
- function normalizeContent(content) {
2937
- return Array.isArray(content) ? content.filter(Boolean) : [content];
2938
- }
2939
- function WatermarkMarks({
2940
- content,
2941
- density = "md",
2942
- direction = "horizontal"
2943
- }) {
2944
- const marks = normalizeContent(content);
2945
- const values = marks.length > 0 ? marks : [""];
2946
- const id = `cb-watermark-pattern-${useId().replaceAll(":", "")}`;
2947
- const vertical = direction === "vertical";
2948
- return /* @__PURE__ */ jsxs("svg", { className: "cb-watermark__marks", "aria-hidden": "true", focusable: "false", children: [
2949
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
2950
- "pattern",
2951
- {
2952
- className: "cb-watermark__pattern",
2953
- id,
2954
- patternUnits: "userSpaceOnUse",
2955
- width: vertical ? "0.5em" : "1em",
2956
- height: vertical ? "1em" : "0.5em",
2957
- children: /* @__PURE__ */ jsx("text", { className: "cb-watermark__mark", x: "0", y: "0", dy: "1em", children: values.join(" \xB7 ") })
2958
- }
2959
- ) }),
2960
- /* @__PURE__ */ jsx("rect", { className: "cb-watermark__fill", width: "100%", height: "100%", fill: `url(#${id})` })
2961
- ] });
2962
- }
2963
- function WatermarkRoot({
2964
- content,
2965
- children,
2966
- tone = "neutral",
2967
- density = "md",
2968
- direction = "horizontal"
2969
- }) {
2970
- return /* @__PURE__ */ jsxs(
2971
- "div",
2972
- {
2973
- className: cn("cb-watermark", `cb-watermark--${density}`, `cb-watermark--${direction}`),
2974
- "data-tone": tone,
2975
- children: [
2976
- /* @__PURE__ */ jsx("div", { className: "cb-watermark__content", children }),
2977
- /* @__PURE__ */ jsx(WatermarkMarks, { content, density, direction })
2978
- ]
2979
- }
2980
- );
2981
- }
2982
- var Watermark = Object.assign(WatermarkRoot, { Skeleton: WatermarkSkeleton });
2983
- function Tabs({ items, value, defaultValue, onValueChange, variant = "underline", className }) {
2984
- return /* @__PURE__ */ jsxs(
2985
- Tabs$1.Root,
2986
- {
2987
- value,
2988
- defaultValue: defaultValue ?? items[0]?.value,
2989
- onValueChange: (next) => onValueChange?.(String(next)),
2990
- className: cn("cb-tabs", `cb-tabs--${variant}`, className),
2991
- children: [
2992
- /* @__PURE__ */ jsxs(Tabs$1.List, { className: "cb-tabs__list", children: [
2993
- items.map((item) => /* @__PURE__ */ jsxs(Tabs$1.Tab, { value: item.value, disabled: item.disabled, className: "cb-tabs__tab", children: [
2994
- item.label,
2995
- item.adornment ? /* @__PURE__ */ jsx("span", { className: "cb-tabs__adornment", children: item.adornment }) : null
2996
- ] }, item.value)),
2997
- /* @__PURE__ */ jsx(Tabs$1.Indicator, { className: "cb-tabs__indicator" })
2998
- ] }),
2999
- items.map((item) => /* @__PURE__ */ jsx(Tabs$1.Panel, { value: item.value, className: "cb-tabs__panel", children: item.content }, item.value))
3000
- ]
3001
- }
3002
- );
3003
- }
3004
- function Dropdown({ trigger, items, sections, align = "end", side = "bottom", className }) {
3005
- const groups = sections ?? [{ items }];
3006
- return /* @__PURE__ */ jsxs(Menu$1.Root, { children: [
3007
- /* @__PURE__ */ jsx(Menu$1.Trigger, { render: trigger }),
3008
- /* @__PURE__ */ jsx(Menu$1.Portal, { children: /* @__PURE__ */ jsx(
3009
- Menu$1.Positioner,
3010
- {
3011
- side,
3012
- align,
3013
- sideOffset: 6,
3014
- className: "cb-menu-positioner",
3015
- children: /* @__PURE__ */ jsx(Menu$1.Popup, { className: cn("cb-menu", className), children: groups.map((section, sectionIndex) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
3016
- sectionIndex > 0 ? /* @__PURE__ */ jsx(Menu$1.Separator, { className: "cb-menu__separator" }) : null,
3017
- /* @__PURE__ */ jsxs(Menu$1.Group, { className: "cb-menu__group", children: [
3018
- section.label ? /* @__PURE__ */ jsx(Menu$1.GroupLabel, { className: "cb-menu__group-label", children: section.label }) : null,
3019
- section.items.map(
3020
- (item, itemIndex) => item.separator ? /* @__PURE__ */ jsx(Menu$1.Separator, { className: "cb-menu__separator" }, `separator-${itemIndex}`) : /* @__PURE__ */ jsxs(
3021
- Menu$1.Item,
3022
- {
3023
- className: "cb-menu__item",
3024
- "data-tone": item.tone,
3025
- disabled: item.disabled,
3026
- onClick: item.onSelect,
3027
- children: [
3028
- /* @__PURE__ */ jsx("span", { className: "cb-menu__icon", children: item.checked ? /* @__PURE__ */ jsx(Check, { size: 14 }) : item.icon }),
3029
- /* @__PURE__ */ jsx("span", { className: "cb-menu__label", children: item.label }),
3030
- item.shortcut ? /* @__PURE__ */ jsx("kbd", { className: "cb-menu__shortcut", children: item.shortcut }) : null
3031
- ]
3032
- },
3033
- `${String(item.label)}-${itemIndex}`
3034
- )
3035
- )
3036
- ] })
3037
- ] }, `section-${sectionIndex}`)) })
3038
- }
3039
- ) })
3040
- ] });
3041
- }
3042
- function MenuSkeleton({
3043
- items = 4,
3044
- size = "md",
3045
- tone = "brand",
3046
- "aria-label": ariaLabel = "Loading navigation"
3047
- }) {
3048
- return /* @__PURE__ */ jsx(
3049
- "nav",
3050
- {
3051
- "aria-label": ariaLabel,
3052
- "aria-hidden": "true",
3053
- className: cn("cb-persistent-menu", `cb-persistent-menu--${size}`, `cb-persistent-menu--${tone}`, "cb-persistent-menu--skeleton"),
3054
- children: /* @__PURE__ */ jsx("ul", { className: "cb-persistent-menu__list", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsx("li", { className: "cb-persistent-menu__item", children: /* @__PURE__ */ jsx(Skeleton, { className: cn("cb-persistent-menu__skeleton-line", index % 3 === 0 && "cb-persistent-menu__skeleton-line--long") }) }, index)) })
3055
- }
3056
- );
3057
- }
3058
- function MenuRoot({
3059
- items,
3060
- selectedPath,
3061
- defaultSelectedPath,
3062
- onSelectedPathChange,
3063
- openKeys,
3064
- defaultOpenKeys = [],
3065
- onOpenKeysChange,
3066
- size = "md",
3067
- tone = "brand",
3068
- motion: motion3 = true,
3069
- "aria-label": ariaLabel = "Navigation"
3070
- }) {
3071
- const [uncontrolledSelectedPath, setUncontrolledSelectedPath] = useState(defaultSelectedPath);
3072
- const [uncontrolledOpenKeys, setUncontrolledOpenKeys] = useState(defaultOpenKeys);
3073
- const currentSelectedPath = selectedPath ?? uncontrolledSelectedPath;
3074
- const currentOpenKeys = openKeys ?? uncontrolledOpenKeys;
3075
- const menuId = useId();
3076
- const select = (path) => {
3077
- if (selectedPath === void 0) setUncontrolledSelectedPath(path);
3078
- onSelectedPathChange?.(path);
3079
- };
3080
- const toggle = (key) => {
3081
- const next = currentOpenKeys.includes(key) ? currentOpenKeys.filter((openKey) => openKey !== key) : [...currentOpenKeys, key];
3082
- if (openKeys === void 0) setUncontrolledOpenKeys(next);
3083
- onOpenKeysChange?.(next);
3084
- };
3085
- return /* @__PURE__ */ jsx(
3086
- "nav",
3087
- {
3088
- "aria-label": ariaLabel,
3089
- className: cn("cb-persistent-menu", `cb-persistent-menu--${size}`, `cb-persistent-menu--${tone}`, !motion3 && "cb-persistent-menu--motion-off"),
3090
- children: /* @__PURE__ */ jsx(
3091
- MenuList,
3092
- {
3093
- items,
3094
- selectedPath: currentSelectedPath,
3095
- openKeys: currentOpenKeys,
3096
- path: [],
3097
- menuId,
3098
- onSelect: select,
3099
- onToggle: toggle
3100
- }
3101
- )
3102
- }
3103
- );
3104
- }
3105
- function MenuList({ items, selectedPath, openKeys, path, menuId, nested = false, onSelect, onToggle }) {
3106
- return /* @__PURE__ */ jsx("ul", { className: cn("cb-persistent-menu__list", nested && "cb-persistent-menu__list--nested"), children: items.map((item) => {
3107
- const itemPath = [...path, item.key];
3108
- const pathKey = pathIdentity(itemPath);
3109
- const isBranch = "children" in item;
3110
- const open = openKeys.includes(pathKey);
3111
- const current = selectedPath === pathKey;
3112
- const branchId = `${menuId}-${pathKey}`;
3113
- return /* @__PURE__ */ jsxs("li", { className: "cb-persistent-menu__item", children: [
3114
- /* @__PURE__ */ jsx("div", { className: "cb-persistent-menu__row", children: isBranch ? /* @__PURE__ */ jsx(MenuBranch, { item, open, branchId, onToggle: () => onToggle(pathKey) }) : /* @__PURE__ */ jsx(MenuEntry, { item, path: pathKey, current, onSelect }) }),
3115
- isBranch && open ? /* @__PURE__ */ jsx("div", { id: branchId, className: "cb-persistent-menu__branch", children: /* @__PURE__ */ jsx(
3116
- MenuList,
3117
- {
3118
- items: item.children,
3119
- selectedPath,
3120
- openKeys,
3121
- path: itemPath,
3122
- menuId,
3123
- nested: true,
3124
- onSelect,
3125
- onToggle
3126
- }
3127
- ) }) : null
3128
- ] }, item.key);
3129
- }) });
3130
- }
3131
- function MenuEntry({
3132
- item,
3133
- path,
3134
- current,
3135
- onSelect
3136
- }) {
3137
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
3138
- item.icon ? /* @__PURE__ */ jsx("span", { className: "cb-persistent-menu__icon", "aria-hidden": "true", children: item.icon }) : null,
3139
- /* @__PURE__ */ jsx("span", { className: "cb-persistent-menu__label", children: item.label })
3140
- ] });
3141
- const props = {
3142
- className: "cb-persistent-menu__entry",
3143
- "aria-current": current ? "page" : void 0,
3144
- "aria-disabled": item.disabled || void 0,
3145
- "data-current": current || void 0,
3146
- "data-disabled": item.disabled || void 0
3147
- };
3148
- if (item.disabled) {
3149
- return /* @__PURE__ */ jsx("span", { ...props, children: content });
3150
- }
3151
- return /* @__PURE__ */ jsx(
3152
- "a",
3153
- {
3154
- href: item.href,
3155
- ...props,
3156
- onClick: () => onSelect(path),
3157
- children: content
3158
- }
3159
- );
3160
- }
3161
- function MenuBranch({
3162
- item,
3163
- open,
3164
- branchId,
3165
- onToggle
3166
- }) {
3167
- return /* @__PURE__ */ jsxs(
3168
- "button",
3169
- {
3170
- type: "button",
3171
- className: "cb-persistent-menu__branch-trigger",
3172
- "aria-label": item.ariaLabel,
3173
- "aria-expanded": open,
3174
- "aria-controls": branchId,
3175
- disabled: item.disabled,
3176
- "data-open": open || void 0,
3177
- "data-disabled": item.disabled || void 0,
3178
- onClick: onToggle,
3179
- children: [
3180
- item.icon ? /* @__PURE__ */ jsx("span", { className: "cb-persistent-menu__icon", "aria-hidden": "true", children: item.icon }) : null,
3181
- /* @__PURE__ */ jsx("span", { className: "cb-persistent-menu__label", children: item.label }),
3182
- /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "cb-persistent-menu__chevron cb-persistent-menu__chevron--collapsed" }),
3183
- /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", className: "cb-persistent-menu__chevron cb-persistent-menu__chevron--expanded" })
3184
- ]
3185
- }
3186
- );
440
+ /* @__PURE__ */ jsx("span", { className: "cb-toast__icon", children: ICONS[toast.type ?? "info"] ?? ICONS.info }),
441
+ /* @__PURE__ */ jsxs(Toast.Content, { className: "cb-toast__content", children: [
442
+ /* @__PURE__ */ jsx(Toast.Title, { className: "cb-toast__title" }),
443
+ /* @__PURE__ */ jsx(Toast.Description, { className: "cb-toast__description" })
444
+ ] }),
445
+ /* @__PURE__ */ jsx(Toast.Action, { className: "cb-toast__action" }),
446
+ /* @__PURE__ */ jsx(Toast.Close, { className: "cb-toast__close", "aria-label": labels.dismiss, children: /* @__PURE__ */ jsx(X, { size: 14 }) })
447
+ ] }, toast.id));
3187
448
  }
3188
- function pathIdentity(path) {
3189
- return path.map(encodeURIComponent).join("/");
449
+ function useToast() {
450
+ const manager = Toast.useToastManager();
451
+ return {
452
+ show: ({ title, description, tone = "info", timeout, action }) => manager.add({
453
+ title,
454
+ description,
455
+ type: tone,
456
+ // Errors do not time out: something went wrong is not a thing to miss by looking away.
457
+ timeout: timeout ?? (tone === "danger" ? 0 : void 0),
458
+ ...action ? { actionProps: { children: action.label, onClick: action.onClick } } : {}
459
+ }),
460
+ close: (id) => manager.close(id),
461
+ promise: manager.promise
462
+ };
3190
463
  }
3191
- var Menu = Object.assign(MenuRoot, { Skeleton: MenuSkeleton });
3192
464
  var hasActiveChild = (item) => Boolean(item.items?.some((child) => child.active));
3193
465
  function Sidebar({ sections, header, footer, collapsed = false, onCollapsedChange, className }) {
3194
466
  const labels = useLabels();
@@ -3264,9 +536,9 @@ function SidebarRow({ item, collapsed, nested }) {
3264
536
  return item.href ? /* @__PURE__ */ jsx("a", { href: item.href, ...props, children: content }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: item.onClick, ...props, children: content });
3265
537
  }
3266
538
  function Flyout({ label, items, children }) {
3267
- return /* @__PURE__ */ jsxs(Popover$1.Root, { children: [
539
+ return /* @__PURE__ */ jsxs(Popover.Root, { children: [
3268
540
  /* @__PURE__ */ jsx(
3269
- Popover$1.Trigger,
541
+ Popover.Trigger,
3270
542
  {
3271
543
  openOnHover: true,
3272
544
  delay: 120,
@@ -3274,7 +546,7 @@ function Flyout({ label, items, children }) {
3274
546
  render: /* @__PURE__ */ jsx("span", { className: "cb-sidebar__flyout-trigger", children })
3275
547
  }
3276
548
  ),
3277
- /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { side: "right", align: "start", sideOffset: 8, className: "cb-sidebar__flyout-positioner", children: /* @__PURE__ */ jsxs(Popover$1.Popup, { className: "cb-sidebar__flyout", children: [
549
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, { side: "right", align: "start", sideOffset: 8, className: "cb-sidebar__flyout-positioner", children: /* @__PURE__ */ jsxs(Popover.Popup, { className: "cb-sidebar__flyout", children: [
3278
550
  /* @__PURE__ */ jsx("p", { className: "cb-sidebar__flyout-title", children: label }),
3279
551
  items?.length ? /* @__PURE__ */ jsx("div", { className: "cb-sidebar__flyout-items", children: items.map((child, index) => /* @__PURE__ */ jsx(SidebarRow, { item: child, collapsed: false }, index)) }) : null
3280
552
  ] }) }) })
@@ -3290,1108 +562,6 @@ function TopBar({ title, subtitle, center, actions, sticky = true, className })
3290
562
  actions ? /* @__PURE__ */ jsx("div", { className: "cb-topbar__actions", children: actions }) : null
3291
563
  ] });
3292
564
  }
3293
-
3294
- // src/data/table-sort.ts
3295
- function nextSort(current, column) {
3296
- if (!current || current.column !== column) return { column, direction: "asc" };
3297
- if (current.direction === "asc") return { column, direction: "desc" };
3298
- return null;
3299
- }
3300
- function ariaSortFor(current, column) {
3301
- if (!current || current.column !== column) return "none";
3302
- return current.direction === "asc" ? "ascending" : "descending";
3303
- }
3304
- function pageRange(page, pageSize, total, siblings = 1) {
3305
- const totalPages = Math.max(Math.ceil(total / Math.max(pageSize, 1)), 1);
3306
- const current = Math.min(Math.max(page, 1), totalPages);
3307
- const from = total === 0 ? 0 : (current - 1) * pageSize + 1;
3308
- const to = Math.min(current * pageSize, total);
3309
- const pages = /* @__PURE__ */ new Set([1, totalPages]);
3310
- for (let offset = -siblings; offset <= siblings; offset += 1) {
3311
- const candidate = current + offset;
3312
- if (candidate >= 1 && candidate <= totalPages) pages.add(candidate);
3313
- }
3314
- const sorted = [...pages].sort((a, b) => a - b);
3315
- const items = [];
3316
- sorted.forEach((value, index) => {
3317
- const previous = sorted[index - 1];
3318
- if (previous !== void 0 && value - previous === 2) items.push(previous + 1);
3319
- else if (previous !== void 0 && value - previous > 2) items.push(null);
3320
- items.push(value);
3321
- });
3322
- return { items, totalPages, from, to };
3323
- }
3324
- function Table({
3325
- columns,
3326
- rows,
3327
- rowKey,
3328
- label,
3329
- sort = null,
3330
- onSortChange,
3331
- onRowClick,
3332
- empty,
3333
- className
3334
- }) {
3335
- if (rows.length === 0 && empty) {
3336
- return /* @__PURE__ */ jsx("div", { className: cn("cb-table__empty", className), children: empty });
3337
- }
3338
- return /* @__PURE__ */ jsx("div", { className: cn("cb-table__scroll", className), children: /* @__PURE__ */ jsxs("table", { className: "cb-table", "aria-label": label, children: [
3339
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx(
3340
- "th",
3341
- {
3342
- scope: "col",
3343
- style: column.width ? { width: column.width } : void 0,
3344
- "data-align": column.align,
3345
- "data-secondary": column.secondary || void 0,
3346
- "aria-sort": column.sortable ? ariaSortFor(sort, column.key) : void 0,
3347
- children: column.sortable && onSortChange ? /* @__PURE__ */ jsxs(
3348
- "button",
3349
- {
3350
- type: "button",
3351
- className: "cb-table__sort",
3352
- onClick: () => onSortChange(nextSort(sort, column.key)),
3353
- children: [
3354
- column.header,
3355
- /* @__PURE__ */ jsx(SortIcon, { state: sort, column: column.key })
3356
- ]
3357
- }
3358
- ) : column.header
3359
- },
3360
- column.key
3361
- )) }) }),
3362
- /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsx(
3363
- "tr",
3364
- {
3365
- "data-clickable": onRowClick ? "" : void 0,
3366
- onClick: onRowClick ? () => onRowClick(row) : void 0,
3367
- children: columns.map((column) => /* @__PURE__ */ jsx(
3368
- "td",
3369
- {
3370
- "data-align": column.align,
3371
- "data-secondary": column.secondary || void 0,
3372
- "data-truncate": column.truncate || void 0,
3373
- children: column.cell(row)
3374
- },
3375
- column.key
3376
- ))
3377
- },
3378
- rowKey(row)
3379
- )) })
3380
- ] }) });
3381
- }
3382
- function SortIcon({ state, column }) {
3383
- if (!state || state.column !== column) return /* @__PURE__ */ jsx(ChevronsUpDown, { size: 13, className: "cb-table__sort-idle" });
3384
- return state.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { size: 13 }) : /* @__PURE__ */ jsx(ArrowDown, { size: 13 });
3385
- }
3386
- function DataTableSkeleton({ columns, rows = 5, className }) {
3387
- return /* @__PURE__ */ jsx("div", { className: cn("cb-table__scroll", className), "aria-hidden": "true", children: /* @__PURE__ */ jsxs("table", { className: "cb-table", children: [
3388
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: Array.from({ length: columns }, (_, index) => /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx(Skeleton, { width: "60%", height: "0.75rem" }) }, index)) }) }),
3389
- /* @__PURE__ */ jsx("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx("tr", { children: Array.from({ length: columns }, (_2, cellIndex) => /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Skeleton, { width: cellIndex === 0 ? "70%" : "45%", height: "0.875rem" }) }, cellIndex)) }, rowIndex)) })
3390
- ] }) });
3391
- }
3392
- Table.Skeleton = DataTableSkeleton;
3393
- function Pagination({
3394
- page,
3395
- pageSize,
3396
- total,
3397
- onPageChange,
3398
- showSummary = true,
3399
- siblings = 1,
3400
- className
3401
- }) {
3402
- const labels = useLabels();
3403
- const { items, totalPages, from, to } = pageRange(page, pageSize, total, siblings);
3404
- const current = Math.min(Math.max(page, 1), totalPages);
3405
- return /* @__PURE__ */ jsxs("nav", { className: cn("cb-pagination", className), "aria-label": "Pagination", children: [
3406
- showSummary ? /* @__PURE__ */ jsx("p", { className: "cb-pagination__summary", children: labels.pageSummary(from, to, total) }) : null,
3407
- /* @__PURE__ */ jsxs("div", { className: "cb-pagination__controls", children: [
3408
- /* @__PURE__ */ jsx(
3409
- "button",
3410
- {
3411
- type: "button",
3412
- className: "cb-pagination__step",
3413
- "aria-label": labels.previousPage,
3414
- disabled: current <= 1,
3415
- onClick: () => onPageChange(current - 1),
3416
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
3417
- }
3418
- ),
3419
- items.map(
3420
- (item, index) => item === null ? /* @__PURE__ */ jsx("span", { className: "cb-pagination__gap", "aria-hidden": "true", children: "\u2026" }, `gap-${index}`) : /* @__PURE__ */ jsx(
3421
- "button",
3422
- {
3423
- type: "button",
3424
- className: "cb-pagination__page",
3425
- "data-active": item === current || void 0,
3426
- "aria-label": labels.page(item),
3427
- "aria-current": item === current ? "page" : void 0,
3428
- onClick: () => onPageChange(item),
3429
- children: item
3430
- },
3431
- item
3432
- )
3433
- ),
3434
- /* @__PURE__ */ jsx(
3435
- "button",
3436
- {
3437
- type: "button",
3438
- className: "cb-pagination__step",
3439
- "aria-label": labels.nextPage,
3440
- disabled: current >= totalPages,
3441
- onClick: () => onPageChange(current + 1),
3442
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 })
3443
- }
3444
- )
3445
- ] })
3446
- ] });
3447
- }
3448
- function CollapseSkeleton({
3449
- items = 3,
3450
- openItems = [0],
3451
- lines = 2,
3452
- size = "md",
3453
- bordered = true,
3454
- expandIconPosition = "start",
3455
- className
3456
- }) {
3457
- const openSet = new Set(openItems);
3458
- return /* @__PURE__ */ jsx(
3459
- "div",
3460
- {
3461
- "aria-hidden": "true",
3462
- className: cn(
3463
- "cb-collapse",
3464
- `cb-collapse--${size}`,
3465
- "cb-collapse--skeleton",
3466
- bordered && "cb-collapse--bordered",
3467
- expandIconPosition === "end" && "cb-collapse--icon-end",
3468
- className
3469
- ),
3470
- children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsxs("div", { className: "cb-collapse__item", children: [
3471
- /* @__PURE__ */ jsxs("div", { className: "cb-collapse__trigger", children: [
3472
- /* @__PURE__ */ jsx(Skeleton.Circle, { size: "1rem", className: "cb-collapse__icon" }),
3473
- /* @__PURE__ */ jsx(Skeleton, { width: "42%", height: "0.875rem", className: "cb-collapse__label" })
3474
- ] }),
3475
- openSet.has(index) ? /* @__PURE__ */ jsx("div", { className: "cb-collapse__content", children: /* @__PURE__ */ jsx(Skeleton.Text, { lines }) }) : null
3476
- ] }, index))
3477
- }
3478
- );
3479
- }
3480
- function normalizeValue(value, multiple) {
3481
- if (value == null) return void 0;
3482
- if (Array.isArray(value)) return multiple ? value : value.slice(0, 1);
3483
- return [value];
3484
- }
3485
- function CollapseRoot({
3486
- items,
3487
- value,
3488
- defaultValue,
3489
- onValueChange,
3490
- multiple = false,
3491
- size = "md",
3492
- bordered = true,
3493
- expandIconPosition = "start",
3494
- headingLevel = 3,
3495
- disabled = false,
3496
- keepMounted = false,
3497
- hiddenUntilFound = false,
3498
- motion: motion3 = true,
3499
- className
3500
- }) {
3501
- const Heading = `h${headingLevel}`;
3502
- return /* @__PURE__ */ jsx(
3503
- Accordion.Root,
3504
- {
3505
- value: normalizeValue(value, multiple),
3506
- defaultValue: normalizeValue(defaultValue, multiple),
3507
- onValueChange: (next) => onValueChange?.(multiple ? next.map(String) : next[0] == null ? void 0 : String(next[0])),
3508
- multiple,
3509
- disabled,
3510
- keepMounted: hiddenUntilFound ? void 0 : keepMounted,
3511
- hiddenUntilFound,
3512
- className: cn(
3513
- "cb-collapse",
3514
- `cb-collapse--${size}`,
3515
- bordered && "cb-collapse--bordered",
3516
- expandIconPosition === "end" && "cb-collapse--icon-end",
3517
- !motion3 && "cb-collapse--motionless",
3518
- className
3519
- ),
3520
- children: items.map((item) => /* @__PURE__ */ jsxs(
3521
- Accordion.Item,
3522
- {
3523
- value: item.key,
3524
- disabled: item.disabled,
3525
- className: "cb-collapse__item",
3526
- children: [
3527
- /* @__PURE__ */ jsx(Accordion.Header, { render: /* @__PURE__ */ jsx(Heading, { className: "cb-collapse__heading" }), children: /* @__PURE__ */ jsxs(Accordion.Trigger, { className: "cb-collapse__trigger", children: [
3528
- /* @__PURE__ */ jsx("span", { className: "cb-collapse__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDown, {}) }),
3529
- /* @__PURE__ */ jsx("span", { className: "cb-collapse__label", children: item.label })
3530
- ] }) }),
3531
- /* @__PURE__ */ jsx(Accordion.Panel, { className: "cb-collapse__panel", children: /* @__PURE__ */ jsx("div", { className: "cb-collapse__content", children: item.children }) })
3532
- ]
3533
- },
3534
- item.key
3535
- ))
3536
- }
3537
- );
3538
- }
3539
- var Collapse = Object.assign(CollapseRoot, {
3540
- Skeleton: CollapseSkeleton
3541
- });
3542
- function CalendarSkeleton({ weekStartsOn = 1 }) {
3543
- return /* @__PURE__ */ jsxs("div", { className: "cb-calendar cb-calendar--skeleton", "aria-hidden": "true", "data-week-starts-on": weekStartsOn, children: [
3544
- /* @__PURE__ */ jsxs("div", { className: "cb-calendar__header", children: [
3545
- /* @__PURE__ */ jsx(Skeleton.Circle, { size: "var(--cb-control-height-sm)" }),
3546
- /* @__PURE__ */ jsx(Skeleton, { width: "42%", height: "var(--cb-text-sm)" }),
3547
- /* @__PURE__ */ jsx(Skeleton.Circle, { size: "var(--cb-control-height-sm)" })
3548
- ] }),
3549
- /* @__PURE__ */ jsx("div", { className: "cb-calendar__weekdays", children: Array.from({ length: 7 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { width: "60%", height: "var(--cb-text-xs)" }, index)) }),
3550
- /* @__PURE__ */ jsx("div", { className: "cb-calendar__grid", children: Array.from({ length: 42 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "cb-calendar__skeleton-day" }, index)) })
3551
- ] });
3552
- }
3553
- function isUsableDate(value) {
3554
- return value instanceof Date && Number.isFinite(value.getTime());
3555
- }
3556
- function calendarDay(value) {
3557
- return new Date(value.getFullYear(), value.getMonth(), value.getDate());
3558
- }
3559
- function sameDay(left, right) {
3560
- return Boolean(left && right && left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate());
3561
- }
3562
- function sameMonth(left, right) {
3563
- return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth();
3564
- }
3565
- function dateKey(date) {
3566
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
3567
- }
3568
- function addDays(date, amount) {
3569
- return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
3570
- }
3571
- function addMonths(date, amount) {
3572
- const targetYear = date.getFullYear();
3573
- const targetMonth = date.getMonth() + amount;
3574
- const lastDay = new Date(targetYear, targetMonth + 1, 0).getDate();
3575
- return new Date(targetYear, targetMonth, Math.min(date.getDate(), lastDay));
3576
- }
3577
- function isDisabled(date, minDate, maxDate, disabledDate) {
3578
- const day = calendarDay(date);
3579
- return isUsableDate(minDate) && day < calendarDay(minDate) || isUsableDate(maxDate) && day > calendarDay(maxDate) || disabledDate?.(day) === true;
3580
- }
3581
- function monthCells(month, weekStartsOn) {
3582
- const first = new Date(month.getFullYear(), month.getMonth(), 1);
3583
- const leadingDays = (first.getDay() - weekStartsOn + 7) % 7;
3584
- const start = addDays(first, -leadingDays);
3585
- return Array.from({ length: 42 }, (_, index) => addDays(start, index));
3586
- }
3587
- function firstEnabledDate(month, weekStartsOn, minDate, maxDate, disabledDate) {
3588
- const cells = monthCells(month, weekStartsOn);
3589
- return cells.find((date) => sameMonth(date, month) && !isDisabled(date, minDate, maxDate, disabledDate)) ?? cells.find((date) => !isDisabled(date, minDate, maxDate, disabledDate));
3590
- }
3591
- function CalendarRoot({
3592
- value,
3593
- defaultValue = null,
3594
- onValueChange,
3595
- month,
3596
- defaultMonth,
3597
- onMonthChange,
3598
- minDate,
3599
- maxDate,
3600
- disabledDate,
3601
- renderDay,
3602
- locale,
3603
- weekStartsOn = 1,
3604
- disabled = false
3605
- }) {
3606
- const labels = useLabels();
3607
- const headingId = useId();
3608
- const rootRef = useRef(null);
3609
- const focusKeyRef = useRef(void 0);
3610
- const today = useMemo(() => calendarDay(/* @__PURE__ */ new Date()), []);
3611
- const controlledValue = value !== void 0;
3612
- const [internalValue, setInternalValue] = useState(() => isUsableDate(defaultValue) && !isDisabled(defaultValue, minDate, maxDate, disabledDate) ? calendarDay(defaultValue) : null);
3613
- const requestedValue = controlledValue ? value : internalValue;
3614
- const selected = isUsableDate(requestedValue) && !isDisabled(requestedValue, minDate, maxDate, disabledDate) ? calendarDay(requestedValue) : null;
3615
- const controlledMonth = month !== void 0;
3616
- const [internalMonth, setInternalMonth] = useState(() => {
3617
- const initial = isUsableDate(defaultMonth) ? defaultMonth : selected ?? today;
3618
- return new Date(initial.getFullYear(), initial.getMonth(), 1);
3619
- });
3620
- const displayedMonth = controlledMonth && isUsableDate(month) ? new Date(month.getFullYear(), month.getMonth(), 1) : internalMonth;
3621
- const [activeDate, setActiveDate] = useState(() => selected && sameMonth(selected, displayedMonth) ? selected : firstEnabledDate(displayedMonth, weekStartsOn, minDate, maxDate, disabledDate) ?? displayedMonth);
3622
- const cells = useMemo(() => monthCells(displayedMonth, weekStartsOn), [displayedMonth, weekStartsOn]);
3623
- const rovingDate = cells.some((date) => sameDay(date, activeDate) && !isDisabled(date, minDate, maxDate, disabledDate)) ? activeDate : firstEnabledDate(displayedMonth, weekStartsOn, minDate, maxDate, disabledDate);
3624
- const weekdayFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { weekday: "short" }), [locale]);
3625
- const fullDateFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { dateStyle: "full" }), [locale]);
3626
- const monthFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }), [locale]);
3627
- const weekdayDates = useMemo(() => Array.from({ length: 7 }, (_, index) => new Date(2023, 0, 1 + (weekStartsOn + index) % 7)), [weekStartsOn]);
3628
- const controlledMonthKey = controlledMonth ? dateKey(displayedMonth) : void 0;
3629
- const controlledValueKey = controlledValue && selected ? dateKey(selected) : void 0;
3630
- const previousControlledMonthKey = useRef(controlledMonthKey);
3631
- const previousControlledValueKey = useRef(controlledValueKey);
3632
- useEffect(() => {
3633
- const monthChanged = controlledMonth && previousControlledMonthKey.current !== controlledMonthKey;
3634
- const valueChanged = controlledValue && previousControlledValueKey.current !== controlledValueKey;
3635
- previousControlledMonthKey.current = controlledMonthKey;
3636
- previousControlledValueKey.current = controlledValueKey;
3637
- if (!monthChanged && !valueChanged) return;
3638
- const next = selected && sameMonth(selected, displayedMonth) ? selected : firstEnabledDate(displayedMonth, weekStartsOn, minDate, maxDate, disabledDate);
3639
- if (next) setActiveDate(next);
3640
- }, [controlledMonth, controlledMonthKey, controlledValue, controlledValueKey, disabledDate, displayedMonth, maxDate, minDate, selected, weekStartsOn]);
3641
- useEffect(() => {
3642
- const key = focusKeyRef.current;
3643
- if (!key) return;
3644
- const target = rootRef.current?.querySelector(`[data-date-key="${key}"]`);
3645
- if (target) target.focus();
3646
- focusKeyRef.current = void 0;
3647
- }, [displayedMonth, activeDate]);
3648
- const changeMonth = (next) => {
3649
- const normalized = new Date(next.getFullYear(), next.getMonth(), 1);
3650
- if (!controlledMonth) setInternalMonth(normalized);
3651
- onMonthChange?.(normalized);
3652
- };
3653
- const setActive = (next, shouldFocus = false) => {
3654
- const normalized = calendarDay(next);
3655
- const enabled = isDisabled(normalized, minDate, maxDate, disabledDate) ? firstEnabledDate(normalized, weekStartsOn, minDate, maxDate, disabledDate) : normalized;
3656
- if (!enabled) return;
3657
- setActiveDate(enabled);
3658
- if (!sameMonth(enabled, displayedMonth)) changeMonth(enabled);
3659
- if (shouldFocus) focusKeyRef.current = dateKey(enabled);
3660
- };
3661
- const select = (next) => {
3662
- if (disabled || isDisabled(next, minDate, maxDate, disabledDate)) return;
3663
- const normalized = calendarDay(next);
3664
- if (!controlledValue) setInternalValue(normalized);
3665
- onValueChange?.(normalized);
3666
- setActive(normalized);
3667
- };
3668
- const handleKeyDown = (event, current) => {
3669
- if (disabled) return;
3670
- const moveFromCurrent = (amount) => {
3671
- const direction = Math.sign(amount);
3672
- let candidate = addDays(current, amount);
3673
- for (let attempts = 0; attempts < 366 && isDisabled(candidate, minDate, maxDate, disabledDate); attempts += 1) {
3674
- candidate = addDays(candidate, direction || 1);
3675
- }
3676
- if (!isDisabled(candidate, minDate, maxDate, disabledDate)) setActive(candidate, true);
3677
- };
3678
- if (event.key === "ArrowLeft") {
3679
- event.preventDefault();
3680
- moveFromCurrent(-1);
3681
- } else if (event.key === "ArrowRight") {
3682
- event.preventDefault();
3683
- moveFromCurrent(1);
3684
- } else if (event.key === "ArrowUp") {
3685
- event.preventDefault();
3686
- moveFromCurrent(-7);
3687
- } else if (event.key === "ArrowDown") {
3688
- event.preventDefault();
3689
- moveFromCurrent(7);
3690
- } else if (event.key === "Home") {
3691
- event.preventDefault();
3692
- moveFromCurrent(-((current.getDay() - weekStartsOn + 7) % 7));
3693
- } else if (event.key === "End") {
3694
- event.preventDefault();
3695
- moveFromCurrent(6 - (current.getDay() - weekStartsOn + 7) % 7);
3696
- } else if (event.key === "PageUp") {
3697
- event.preventDefault();
3698
- setActive(addMonths(current, event.shiftKey ? -12 : -1), true);
3699
- } else if (event.key === "PageDown") {
3700
- event.preventDefault();
3701
- setActive(addMonths(current, event.shiftKey ? 12 : 1), true);
3702
- } else if (event.key === "Enter" || event.key === " ") {
3703
- event.preventDefault();
3704
- select(current);
3705
- }
3706
- };
3707
- return /* @__PURE__ */ jsxs("section", { ref: rootRef, className: "cb-calendar", "aria-label": monthFormatter.format(displayedMonth), "data-disabled": disabled || void 0, children: [
3708
- /* @__PURE__ */ jsxs("div", { className: "cb-calendar__header", children: [
3709
- /* @__PURE__ */ jsx("button", { type: "button", className: "cb-calendar__nav", onClick: () => changeMonth(addMonths(displayedMonth, -1)), disabled, "aria-label": labels.previousMonth, children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" }) }),
3710
- /* @__PURE__ */ jsx("h2", { className: "cb-calendar__month", id: headingId, "aria-live": "polite", children: monthFormatter.format(displayedMonth) }),
3711
- /* @__PURE__ */ jsx("button", { type: "button", className: "cb-calendar__nav", onClick: () => changeMonth(addMonths(displayedMonth, 1)), disabled, "aria-label": labels.nextMonth, children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" }) })
3712
- ] }),
3713
- /* @__PURE__ */ jsx("div", { className: "cb-calendar__weekdays", "aria-hidden": "true", children: weekdayDates.map((date) => /* @__PURE__ */ jsx("span", { children: weekdayFormatter.format(date) }, date.getDay())) }),
3714
- /* @__PURE__ */ jsx("div", { className: "cb-calendar__grid", role: "grid", "aria-labelledby": headingId, children: Array.from({ length: 6 }, (_, weekIndex) => /* @__PURE__ */ jsx("div", { className: "cb-calendar__week", role: "row", children: cells.slice(weekIndex * 7, weekIndex * 7 + 7).map((date) => {
3715
- const unavailable = disabled || isDisabled(date, minDate, maxDate, disabledDate);
3716
- const day = { date, inMonth: sameMonth(date, displayedMonth), selected: sameDay(date, selected), today: sameDay(date, today), disabled: unavailable };
3717
- const active = sameDay(date, rovingDate) && !unavailable;
3718
- return /* @__PURE__ */ jsx("div", { role: "gridcell", className: "cb-calendar__cell", "aria-selected": day.selected || void 0, children: /* @__PURE__ */ jsx(
3719
- "button",
3720
- {
3721
- type: "button",
3722
- className: "cb-calendar__day",
3723
- "data-date-key": dateKey(date),
3724
- "data-outside": !day.inMonth || void 0,
3725
- "data-selected": day.selected || void 0,
3726
- "data-today": day.today || void 0,
3727
- disabled: unavailable,
3728
- tabIndex: active ? 0 : -1,
3729
- "aria-label": fullDateFormatter.format(date),
3730
- "aria-pressed": day.selected,
3731
- onFocus: () => setActiveDate(date),
3732
- onClick: () => select(date),
3733
- onKeyDown: (event) => handleKeyDown(event, date),
3734
- children: renderDay ? renderDay(day) : date.getDate()
3735
- }
3736
- ) }, dateKey(date));
3737
- }) }, weekIndex)) })
3738
- ] });
3739
- }
3740
- var Calendar = Object.assign(CalendarRoot, { Skeleton: CalendarSkeleton });
3741
- function ImageSkeleton({
3742
- aspectRatio,
3743
- radius = "md",
3744
- className
3745
- }) {
3746
- return /* @__PURE__ */ jsx(
3747
- "span",
3748
- {
3749
- className: cn(
3750
- "cb-image cb-image--skeleton",
3751
- `cb-radius--${radius}`,
3752
- className
3753
- ),
3754
- style: aspectRatio ? { aspectRatio: String(aspectRatio) } : void 0,
3755
- "aria-hidden": "true",
3756
- children: /* @__PURE__ */ jsx(
3757
- Skeleton,
3758
- {
3759
- className: "cb-image__skeleton-shape",
3760
- width: "100%",
3761
- height: "100%",
3762
- radius: "md"
3763
- }
3764
- )
3765
- }
3766
- );
3767
- }
3768
- function ImagePreviewGroupSkeleton({
3769
- items = 3,
3770
- aspectRatio,
3771
- radius = "md",
3772
- className
3773
- }) {
3774
- return /* @__PURE__ */ jsx(
3775
- "div",
3776
- {
3777
- className: cn("cb-image-preview-group cb-image-preview-group--skeleton", className),
3778
- "aria-hidden": "true",
3779
- children: Array.from({ length: Math.max(0, items) }, (_, itemIndex) => /* @__PURE__ */ jsx(
3780
- ImageSkeleton,
3781
- {
3782
- aspectRatio,
3783
- radius
3784
- },
3785
- itemIndex
3786
- ))
3787
- }
3788
- );
3789
- }
3790
- function ImageRoot({
3791
- src,
3792
- alt,
3793
- aspectRatio,
3794
- blurDataUrl,
3795
- background,
3796
- fit = "cover",
3797
- radius = "md",
3798
- loading = "lazy",
3799
- motion: motion3 = true,
3800
- className
3801
- }) {
3802
- const [loaded, setLoaded] = useState(false);
3803
- const style = {
3804
- ...aspectRatio ? { aspectRatio: String(aspectRatio) } : {},
3805
- ...background ? { background } : {},
3806
- ...blurDataUrl ? { backgroundImage: `url(${blurDataUrl})` } : {}
3807
- };
3808
- return /* @__PURE__ */ jsx(
3809
- "span",
3810
- {
3811
- className: cn("cb-image", `cb-radius--${radius}`, className),
3812
- "data-loaded": loaded || void 0,
3813
- "data-blurred": blurDataUrl ? "" : void 0,
3814
- "data-motion": motion3 ? void 0 : "off",
3815
- style,
3816
- children: /* @__PURE__ */ jsx(
3817
- "img",
3818
- {
3819
- className: "cb-image__img",
3820
- src,
3821
- alt,
3822
- loading,
3823
- decoding: "async",
3824
- style: { objectFit: fit },
3825
- onLoad: () => setLoaded(true),
3826
- ref: (node) => {
3827
- if (node?.complete) setLoaded(true);
3828
- }
3829
- }
3830
- )
3831
- }
3832
- );
3833
- }
3834
- var DEFAULT_PREVIEW_LABELS = {
3835
- close: "Close preview",
3836
- previous: "Previous image",
3837
- next: "Next image",
3838
- zoomIn: "Zoom in",
3839
- zoomOut: "Zoom out",
3840
- resetZoom: "Reset zoom",
3841
- open: (item) => `Preview ${item.alt || "image"}`,
3842
- position: (current, total) => `Image ${current} of ${total}`
3843
- };
3844
- function clampIndex(index, length) {
3845
- return Math.max(0, Math.min(index, Math.max(length - 1, 0)));
3846
- }
3847
- function ImagePreviewGroupRoot({
3848
- items,
3849
- label,
3850
- open,
3851
- defaultOpen = false,
3852
- onOpenChange,
3853
- index,
3854
- defaultIndex = 0,
3855
- onIndexChange,
3856
- loop = false,
3857
- labels: labelOverrides,
3858
- motion: motion3 = true,
3859
- className
3860
- }) {
3861
- const [internalOpen, setInternalOpen] = useState(defaultOpen);
3862
- const [internalIndex, setInternalIndex] = useState(
3863
- () => clampIndex(defaultIndex, items.length)
3864
- );
3865
- const [scale, setScale] = useState(1);
3866
- const isOpen = open ?? internalOpen;
3867
- const selectedIndex = clampIndex(index ?? internalIndex, items.length);
3868
- const selectedItem = items[selectedIndex];
3869
- const labels = useMemo(
3870
- () => ({ ...DEFAULT_PREVIEW_LABELS, ...labelOverrides }),
3871
- [labelOverrides]
3872
- );
3873
- const changeOpen = (nextOpen) => {
3874
- if (open === void 0) setInternalOpen(nextOpen);
3875
- if (!nextOpen) setScale(1);
3876
- onOpenChange?.(nextOpen);
3877
- };
3878
- const changeIndex = (nextIndex) => {
3879
- if (items.length === 0) return;
3880
- const next = loop ? (nextIndex + items.length) % items.length : clampIndex(nextIndex, items.length);
3881
- if (index === void 0) setInternalIndex(next);
3882
- setScale(1);
3883
- onIndexChange?.(next);
3884
- };
3885
- useEffect(() => {
3886
- if (selectedIndex !== (index ?? internalIndex) && index === void 0) {
3887
- setInternalIndex(selectedIndex);
3888
- }
3889
- }, [index, internalIndex, selectedIndex]);
3890
- const canGoPrevious = loop && items.length > 1 || selectedIndex > 0;
3891
- const canGoNext = loop && items.length > 1 || selectedIndex < items.length - 1;
3892
- const zoomIn = () => setScale((current) => Math.min(3, current + 0.5));
3893
- const zoomOut = () => setScale((current) => Math.max(1, current - 0.5));
3894
- const onPreviewKeyDown = (event) => {
3895
- if (event.key === "ArrowLeft" && canGoPrevious)
3896
- changeIndex(selectedIndex - 1);
3897
- else if (event.key === "ArrowRight" && canGoNext)
3898
- changeIndex(selectedIndex + 1);
3899
- else if (event.key === "+" || event.key === "=") zoomIn();
3900
- else if (event.key === "-") zoomOut();
3901
- else if (event.key === "0") setScale(1);
3902
- else if (event.key === "Home") changeIndex(0);
3903
- else if (event.key === "End") changeIndex(items.length - 1);
3904
- else return;
3905
- event.preventDefault();
3906
- };
3907
- return /* @__PURE__ */ jsxs(Dialog.Root, { open: isOpen, onOpenChange: changeOpen, children: [
3908
- /* @__PURE__ */ jsx(
3909
- "div",
3910
- {
3911
- className: cn("cb-image-preview-group", className),
3912
- role: "group",
3913
- "aria-label": label,
3914
- children: items.map((item, itemIndex) => {
3915
- const {
3916
- id,
3917
- previewSrc: _previewSrc,
3918
- caption: _caption,
3919
- ...imageProps
3920
- } = item;
3921
- return /* @__PURE__ */ jsx(
3922
- Dialog.Trigger,
3923
- {
3924
- className: "cb-image-preview-group__trigger",
3925
- "aria-label": labels.open(item, itemIndex),
3926
- onClick: () => changeIndex(itemIndex),
3927
- children: /* @__PURE__ */ jsx(ImageRoot, { ...imageProps })
3928
- },
3929
- id
3930
- );
3931
- })
3932
- }
3933
- ),
3934
- /* @__PURE__ */ jsx(Dialog.Portal, { children: selectedItem ? /* @__PURE__ */ jsxs(Fragment, { children: [
3935
- /* @__PURE__ */ jsx(Dialog.Backdrop, { className: "cb-image-preview__backdrop" }),
3936
- /* @__PURE__ */ jsxs(
3937
- Dialog.Popup,
3938
- {
3939
- className: "cb-image-preview",
3940
- "data-motion": motion3 ? void 0 : "off",
3941
- onKeyDown: onPreviewKeyDown,
3942
- children: [
3943
- /* @__PURE__ */ jsx(Dialog.Title, { className: "cb-image-preview__sr-title", children: labels.position(selectedIndex + 1, items.length) }),
3944
- /* @__PURE__ */ jsxs("div", { className: "cb-image-preview__toolbar", children: [
3945
- /* @__PURE__ */ jsx("span", { className: "cb-image-preview__position", "aria-live": "polite", children: labels.position(selectedIndex + 1, items.length) }),
3946
- /* @__PURE__ */ jsx(
3947
- "button",
3948
- {
3949
- type: "button",
3950
- className: "cb-image-preview__control",
3951
- "aria-label": labels.zoomOut,
3952
- disabled: scale === 1,
3953
- onClick: zoomOut,
3954
- children: /* @__PURE__ */ jsx(ZoomOut, { "aria-hidden": "true" })
3955
- }
3956
- ),
3957
- /* @__PURE__ */ jsx(
3958
- "button",
3959
- {
3960
- type: "button",
3961
- className: "cb-image-preview__control",
3962
- "aria-label": labels.resetZoom,
3963
- disabled: scale === 1,
3964
- onClick: () => setScale(1),
3965
- children: /* @__PURE__ */ jsx(RotateCcw, { "aria-hidden": "true" })
3966
- }
3967
- ),
3968
- /* @__PURE__ */ jsx(
3969
- "button",
3970
- {
3971
- type: "button",
3972
- className: "cb-image-preview__control",
3973
- "aria-label": labels.zoomIn,
3974
- disabled: scale === 3,
3975
- onClick: zoomIn,
3976
- children: /* @__PURE__ */ jsx(ZoomIn, { "aria-hidden": "true" })
3977
- }
3978
- ),
3979
- /* @__PURE__ */ jsx(
3980
- Dialog.Close,
3981
- {
3982
- className: "cb-image-preview__control",
3983
- "aria-label": labels.close,
3984
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
3985
- }
3986
- )
3987
- ] }),
3988
- /* @__PURE__ */ jsxs("div", { className: "cb-image-preview__stage", children: [
3989
- /* @__PURE__ */ jsx(
3990
- "button",
3991
- {
3992
- type: "button",
3993
- className: "cb-image-preview__navigation cb-image-preview__navigation--previous",
3994
- "aria-label": labels.previous,
3995
- disabled: !canGoPrevious,
3996
- onClick: () => changeIndex(selectedIndex - 1),
3997
- children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" })
3998
- }
3999
- ),
4000
- /* @__PURE__ */ jsx(
4001
- "img",
4002
- {
4003
- className: "cb-image-preview__image",
4004
- src: selectedItem.previewSrc ?? selectedItem.src,
4005
- alt: selectedItem.alt,
4006
- style: { "--cb-image-preview-scale": scale },
4007
- onDoubleClick: () => setScale((current) => current === 1 ? 2 : 1)
4008
- },
4009
- selectedItem.id
4010
- ),
4011
- /* @__PURE__ */ jsx(
4012
- "button",
4013
- {
4014
- type: "button",
4015
- className: "cb-image-preview__navigation cb-image-preview__navigation--next",
4016
- "aria-label": labels.next,
4017
- disabled: !canGoNext,
4018
- onClick: () => changeIndex(selectedIndex + 1),
4019
- children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })
4020
- }
4021
- )
4022
- ] }),
4023
- selectedItem.caption ? /* @__PURE__ */ jsx("div", { className: "cb-image-preview__caption", children: selectedItem.caption }) : null
4024
- ]
4025
- }
4026
- )
4027
- ] }) : null })
4028
- ] });
4029
- }
4030
- var Image = Object.assign(ImageRoot, {
4031
- PreviewGroup: Object.assign(ImagePreviewGroupRoot, {
4032
- Skeleton: ImagePreviewGroupSkeleton
4033
- }),
4034
- Skeleton: ImageSkeleton
4035
- });
4036
-
4037
- // src/media/autoplay.ts
4038
- function shouldAutoplay(conditions) {
4039
- if (!conditions.requested) return false;
4040
- if (conditions.reducedMotion) return false;
4041
- if (conditions.documentHidden) return false;
4042
- if (conditions.pointerInside) return false;
4043
- if (conditions.focusInside) return false;
4044
- return true;
4045
- }
4046
- function CarouselRoot({
4047
- children,
4048
- label,
4049
- slideWidth = "18rem",
4050
- gap = 4,
4051
- loop = false,
4052
- align = "start",
4053
- autoplay,
4054
- showArrows = true,
4055
- showDots = true,
4056
- className
4057
- }) {
4058
- const { enabled: motionEnabled } = useMotionSettings();
4059
- const labels = useLabels();
4060
- const [emblaRef, embla] = useEmblaCarousel({ loop, align, containScroll: "trimSnaps" });
4061
- const [selected, setSelected] = useState(0);
4062
- const [snapCount, setSnapCount] = useState(0);
4063
- const [pointerInside, setPointerInside] = useState(false);
4064
- const [focusInside, setFocusInside] = useState(false);
4065
- const [documentHidden, setDocumentHidden] = useState(false);
4066
- const rootRef = useRef(null);
4067
- useEffect(() => {
4068
- if (!embla) return;
4069
- const sync = () => {
4070
- setSelected(embla.selectedScrollSnap());
4071
- setSnapCount(embla.scrollSnapList().length);
4072
- };
4073
- sync();
4074
- embla.on("select", sync).on("reInit", sync);
4075
- return () => {
4076
- embla.off("select", sync).off("reInit", sync);
4077
- };
4078
- }, [embla]);
4079
- useEffect(() => {
4080
- const onVisibility = () => setDocumentHidden(document.hidden);
4081
- onVisibility();
4082
- document.addEventListener("visibilitychange", onVisibility);
4083
- return () => document.removeEventListener("visibilitychange", onVisibility);
4084
- }, []);
4085
- const running = shouldAutoplay({
4086
- requested: Boolean(autoplay),
4087
- pointerInside,
4088
- focusInside,
4089
- documentHidden,
4090
- reducedMotion: !motionEnabled
4091
- });
4092
- useEffect(() => {
4093
- if (!embla || !running || !autoplay) return;
4094
- const timer = window.setInterval(() => {
4095
- if (embla.canScrollNext()) embla.scrollNext();
4096
- else embla.scrollTo(0);
4097
- }, autoplay);
4098
- return () => window.clearInterval(timer);
4099
- }, [embla, running, autoplay]);
4100
- const onKeyDown = useCallback(
4101
- (event) => {
4102
- if (!embla) return;
4103
- if (event.key === "ArrowRight") {
4104
- embla.scrollNext();
4105
- event.preventDefault();
4106
- }
4107
- if (event.key === "ArrowLeft") {
4108
- embla.scrollPrev();
4109
- event.preventDefault();
4110
- }
4111
- },
4112
- [embla]
4113
- );
4114
- const style = {
4115
- "--cb-carousel-slide": slideWidth,
4116
- "--cb-carousel-gap": `var(--cb-space-${gap})`
4117
- };
4118
- return /* @__PURE__ */ jsxs(
4119
- "div",
4120
- {
4121
- ref: rootRef,
4122
- className: cn("cb-carousel", className),
4123
- style,
4124
- role: "region",
4125
- "aria-roledescription": "carousel",
4126
- "aria-label": label,
4127
- tabIndex: 0,
4128
- onKeyDown,
4129
- onPointerEnter: () => setPointerInside(true),
4130
- onPointerLeave: () => setPointerInside(false),
4131
- onFocus: () => setFocusInside(true),
4132
- onBlur: (event) => {
4133
- if (!rootRef.current?.contains(event.relatedTarget)) setFocusInside(false);
4134
- },
4135
- children: [
4136
- /* @__PURE__ */ jsx("div", { className: "cb-carousel__viewport", ref: emblaRef, children: /* @__PURE__ */ jsx("div", { className: "cb-carousel__track", children }) }),
4137
- showArrows ? /* @__PURE__ */ jsxs("div", { className: "cb-carousel__arrows", children: [
4138
- /* @__PURE__ */ jsx(
4139
- "button",
4140
- {
4141
- type: "button",
4142
- className: "cb-carousel__arrow",
4143
- "aria-label": labels.previousSlide,
4144
- onClick: () => embla?.scrollPrev(),
4145
- disabled: !loop && selected === 0,
4146
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 18 })
4147
- }
4148
- ),
4149
- /* @__PURE__ */ jsx(
4150
- "button",
4151
- {
4152
- type: "button",
4153
- className: "cb-carousel__arrow",
4154
- "aria-label": labels.nextSlide,
4155
- onClick: () => embla?.scrollNext(),
4156
- disabled: !loop && snapCount > 0 && selected === snapCount - 1,
4157
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 18 })
4158
- }
4159
- )
4160
- ] }) : null,
4161
- showDots && snapCount > 1 ? /* @__PURE__ */ jsx("div", { className: "cb-carousel__dots", children: Array.from({ length: snapCount }, (_, index) => /* @__PURE__ */ jsx(
4162
- "button",
4163
- {
4164
- type: "button",
4165
- className: "cb-carousel__dot",
4166
- "data-active": index === selected || void 0,
4167
- "aria-label": labels.goToSlide(index + 1),
4168
- "aria-current": index === selected || void 0,
4169
- onClick: () => embla?.scrollTo(index)
4170
- },
4171
- index
4172
- )) }) : null
4173
- ]
4174
- }
4175
- );
4176
- }
4177
- function CarouselSlide({ children, className }) {
4178
- return /* @__PURE__ */ jsx("div", { className: cn("cb-carousel__slide", className), role: "group", "aria-roledescription": "slide", children });
4179
- }
4180
- function CarouselSkeleton({
4181
- slides = 3,
4182
- slideWidth = "18rem",
4183
- slideHeight = "10rem",
4184
- gap = 4
4185
- }) {
4186
- const style = {
4187
- "--cb-carousel-slide": slideWidth,
4188
- "--cb-carousel-gap": `var(--cb-space-${gap})`
4189
- };
4190
- return /* @__PURE__ */ jsx("div", { className: "cb-carousel", style, "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: "cb-carousel__viewport", children: /* @__PURE__ */ jsx("div", { className: "cb-carousel__track", children: Array.from({ length: slides }, (_, index) => /* @__PURE__ */ jsx("div", { className: "cb-carousel__slide", children: /* @__PURE__ */ jsx(Skeleton, { height: slideHeight, radius: "lg" }) }, index)) }) }) });
4191
- }
4192
- var Carousel = Object.assign(CarouselRoot, {
4193
- Slide: CarouselSlide,
4194
- Skeleton: CarouselSkeleton
4195
- });
4196
- function Coachmark({
4197
- open,
4198
- anchor,
4199
- title,
4200
- children,
4201
- side = "bottom",
4202
- align = "center",
4203
- spotlight = true,
4204
- progress,
4205
- actions,
4206
- onDismiss,
4207
- className
4208
- }) {
4209
- const labels = useLabels();
4210
- const rect = useAnchorRect(anchor, open && spotlight !== false);
4211
- const padding = typeof spotlight === "number" ? spotlight : 8;
4212
- return /* @__PURE__ */ jsxs(Fragment, { children: [
4213
- rect ? /* @__PURE__ */ jsx(
4214
- "div",
4215
- {
4216
- className: "cb-spotlight",
4217
- "aria-hidden": "true",
4218
- onClick: onDismiss,
4219
- style: {
4220
- "--cb-spotlight-x": `${rect.left - padding}px`,
4221
- "--cb-spotlight-y": `${rect.top - padding}px`,
4222
- "--cb-spotlight-w": `${rect.width + padding * 2}px`,
4223
- "--cb-spotlight-h": `${rect.height + padding * 2}px`
4224
- }
4225
- }
4226
- ) : null,
4227
- /* @__PURE__ */ jsx(Popover$1.Root, { open: open && Boolean(anchor), onOpenChange: (next) => !next && onDismiss?.(), children: /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
4228
- Popover$1.Positioner,
4229
- {
4230
- className: "cb-coachmark__positioner",
4231
- anchor,
4232
- side,
4233
- align,
4234
- sideOffset: 12,
4235
- children: /* @__PURE__ */ jsxs(Popover$1.Popup, { className: cn("cb-coachmark", className), children: [
4236
- /* @__PURE__ */ jsx(Popover$1.Arrow, { className: "cb-popover__arrow", children: /* @__PURE__ */ jsx(ArrowShape, {}) }),
4237
- /* @__PURE__ */ jsxs("div", { className: "cb-coachmark__head", children: [
4238
- /* @__PURE__ */ jsx("p", { className: "cb-coachmark__title", children: title }),
4239
- onDismiss ? /* @__PURE__ */ jsx(
4240
- "button",
4241
- {
4242
- type: "button",
4243
- className: "cb-coachmark__close",
4244
- "aria-label": labels.dismiss,
4245
- onClick: onDismiss,
4246
- children: /* @__PURE__ */ jsx(X, { size: 16 })
4247
- }
4248
- ) : null
4249
- ] }),
4250
- children ? /* @__PURE__ */ jsx("div", { className: "cb-coachmark__body", children }) : null,
4251
- progress || actions ? /* @__PURE__ */ jsxs("div", { className: "cb-coachmark__foot", children: [
4252
- progress ? /* @__PURE__ */ jsx("span", { className: "cb-coachmark__progress", children: labels.progress(progress.current, progress.total) }) : /* @__PURE__ */ jsx("span", {}),
4253
- actions ? /* @__PURE__ */ jsx("div", { className: "cb-coachmark__actions", children: actions }) : null
4254
- ] }) : null
4255
- ] })
4256
- }
4257
- ) }) })
4258
- ] });
4259
- }
4260
- function useAnchorRect(anchor, active) {
4261
- const [rect, setRect] = useState(null);
4262
- useEffect(() => {
4263
- if (!anchor || !active) {
4264
- setRect(null);
4265
- return;
4266
- }
4267
- const measure = () => setRect(anchor.getBoundingClientRect());
4268
- measure();
4269
- const observer = new ResizeObserver(measure);
4270
- observer.observe(anchor);
4271
- window.addEventListener("scroll", measure, true);
4272
- window.addEventListener("resize", measure);
4273
- return () => {
4274
- observer.disconnect();
4275
- window.removeEventListener("scroll", measure, true);
4276
- window.removeEventListener("resize", measure);
4277
- };
4278
- }, [anchor, active]);
4279
- return rect;
4280
- }
4281
-
4282
- // src/onboarding/tour-machine.ts
4283
- var initialTourState = { index: 0, status: "idle" };
4284
- function tourReducer(state, action, stepCount) {
4285
- const lastIndex = Math.max(stepCount - 1, 0);
4286
- switch (action.type) {
4287
- case "start":
4288
- return stepCount === 0 ? { index: 0, status: "finished" } : { index: 0, status: "running" };
4289
- case "next":
4290
- if (state.status !== "running") return state;
4291
- return state.index >= lastIndex ? { index: state.index, status: "finished" } : { index: state.index + 1, status: "running" };
4292
- case "prev":
4293
- if (state.status !== "running") return state;
4294
- return { index: Math.max(state.index - 1, 0), status: "running" };
4295
- case "goto":
4296
- if (state.status !== "running") return state;
4297
- return { index: Math.min(Math.max(action.index, 0), lastIndex), status: "running" };
4298
- case "skip":
4299
- return { index: state.index, status: "skipped" };
4300
- case "finish":
4301
- return { index: state.index, status: "finished" };
4302
- default:
4303
- return state;
4304
- }
4305
- }
4306
- function hasEnded(status) {
4307
- return status === "finished" || status === "skipped";
4308
- }
4309
- function resolveTarget(target, root) {
4310
- if (!target) return null;
4311
- if (typeof target === "string") return root.querySelector(target);
4312
- if (typeof target === "function") return target();
4313
- if (target instanceof Element) return target;
4314
- if ("current" in target) return target.current;
4315
- return null;
4316
- }
4317
- function Tour({ id, steps, open = false, onOpenChange, seenStore, onFinish, onSkip, labels }) {
4318
- const [state, rawDispatch] = useReducer(
4319
- (current, action) => tourReducer(current, action, steps.length),
4320
- initialTourState
4321
- );
4322
- const [anchor, setAnchor] = useState(null);
4323
- const { enabled: motionEnabled } = useMotionSettings();
4324
- const provided = useLabels();
4325
- const [allowed, setAllowed] = useState(seenStore ? null : true);
4326
- const marked = useRef(false);
4327
- const text2 = { back: provided.back, next: provided.next, done: provided.done, skip: provided.skip, ...labels };
4328
- useEffect(() => {
4329
- if (!seenStore) return;
4330
- let cancelled = false;
4331
- void Promise.resolve(seenStore.has(id)).then((seen) => {
4332
- if (!cancelled) setAllowed(!seen);
4333
- });
4334
- return () => {
4335
- cancelled = true;
4336
- };
4337
- }, [seenStore, id]);
4338
- useEffect(() => {
4339
- if (open && allowed && state.status === "idle") rawDispatch({ type: "start" });
4340
- }, [open, allowed, state.status]);
4341
- useEffect(() => {
4342
- if (state.status !== "running") {
4343
- setAnchor(null);
4344
- return;
4345
- }
4346
- const step2 = steps[state.index];
4347
- if (!step2) return;
4348
- let frame = 0;
4349
- let raf = 0;
4350
- const look = () => {
4351
- const found = resolveTarget(step2.target, document);
4352
- if (found) {
4353
- setAnchor(found);
4354
- found.scrollIntoView?.({ block: "center", behavior: motionEnabled ? "smooth" : "auto" });
4355
- return;
4356
- }
4357
- if (frame++ < 30) raf = requestAnimationFrame(look);
4358
- else setAnchor(null);
4359
- };
4360
- look();
4361
- return () => cancelAnimationFrame(raf);
4362
- }, [state.status, state.index, steps, motionEnabled]);
4363
- useEffect(() => {
4364
- if (!hasEnded(state.status) || marked.current) return;
4365
- marked.current = true;
4366
- void Promise.resolve(seenStore?.mark(id));
4367
- onOpenChange?.(false);
4368
- if (state.status === "finished") onFinish?.();
4369
- else onSkip?.();
4370
- }, [state.status, seenStore, id, onOpenChange, onFinish, onSkip]);
4371
- const skip = useCallback(() => rawDispatch({ type: "skip" }), []);
4372
- if (state.status !== "running" || !allowed) return null;
4373
- const step = steps[state.index];
4374
- if (!step) return null;
4375
- const isLast = state.index === steps.length - 1;
4376
- return /* @__PURE__ */ jsx(
4377
- Coachmark,
4378
- {
4379
- open: true,
4380
- anchor,
4381
- title: step.title,
4382
- side: step.side,
4383
- align: step.align,
4384
- spotlight: step.spotlight,
4385
- progress: { current: state.index + 1, total: steps.length },
4386
- onDismiss: skip,
4387
- actions: /* @__PURE__ */ jsxs(Fragment, { children: [
4388
- state.index > 0 ? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", tone: "neutral", onClick: () => rawDispatch({ type: "prev" }), children: text2.back }) : /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", tone: "neutral", onClick: skip, children: text2.skip }),
4389
- /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => rawDispatch({ type: "next" }), children: isLast ? text2.done : text2.next })
4390
- ] }),
4391
- children: step.content
4392
- }
4393
- );
4394
- }
4395
565
  var OFFSETS = {
4396
566
  below: (d) => ({ y: d }),
4397
567
  above: (d) => ({ y: -d }),
@@ -4426,167 +596,5 @@ function Reveal({
4426
596
  function Stagger({ children, step = 0.06, from = "below", onView = false, className }) {
4427
597
  return /* @__PURE__ */ jsx("div", { className, children: Children.map(children, (child, index) => /* @__PURE__ */ jsx(Reveal, { from, delay: index * step, onView, children: child })) });
4428
598
  }
4429
- function BorderBeam({ children, tone = "brand", size = "md", motion: motion3 = true }) {
4430
- return /* @__PURE__ */ jsxs("div", { className: cn("cb-border-beam", `cb-border-beam--${size}`), "data-tone": tone, "data-motion": motion3 ? "on" : "off", children: [
4431
- /* @__PURE__ */ jsx("span", { className: "cb-border-beam__overlay", "aria-hidden": "true" }),
4432
- /* @__PURE__ */ jsx("div", { className: "cb-border-beam__content", children })
4433
- ] });
4434
- }
4435
- function SplitterSkeleton({
4436
- orientation = "horizontal",
4437
- size = 50,
4438
- handleSize = "md"
4439
- }) {
4440
- const safeSize = Number.isFinite(size) ? size : 50;
4441
- const boundedSize = Math.min(100, Math.max(0, Math.round(safeSize)));
4442
- const style = {
4443
- "--cb-splitter-first-grow": String(boundedSize),
4444
- "--cb-splitter-second-grow": String(100 - boundedSize)
4445
- };
4446
- return /* @__PURE__ */ jsxs(
4447
- "div",
4448
- {
4449
- className: `cb-splitter cb-splitter--skeleton cb-splitter--${orientation} cb-splitter--handle-${handleSize}`,
4450
- style,
4451
- "aria-hidden": "true",
4452
- children: [
4453
- /* @__PURE__ */ jsx("div", { className: "cb-splitter__pane cb-splitter__pane--first", children: /* @__PURE__ */ jsx(Skeleton, { className: "cb-splitter__skeleton-pane" }) }),
4454
- /* @__PURE__ */ jsx("div", { className: "cb-splitter__handle" }),
4455
- /* @__PURE__ */ jsx("div", { className: "cb-splitter__pane cb-splitter__pane--second", children: /* @__PURE__ */ jsx(Skeleton, { className: "cb-splitter__skeleton-pane" }) })
4456
- ]
4457
- }
4458
- );
4459
- }
4460
- function clampSize(value, minSize, maxSize) {
4461
- return Math.min(maxSize, Math.max(minSize, Math.round(value)));
4462
- }
4463
- function finiteValue(value, fallback) {
4464
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
4465
- }
4466
- function normalizedBounds(minSize, maxSize) {
4467
- const min = Math.min(100, Math.max(0, Math.round(finiteValue(minSize, 0))));
4468
- const max = Math.min(100, Math.max(min, Math.round(finiteValue(maxSize, 100))));
4469
- return { min, max };
4470
- }
4471
- function paneChildren(children) {
4472
- return Children.toArray(children).flatMap(
4473
- (child) => isValidElement(child) && child.type === Fragment$1 ? paneChildren(child.props.children) : [child]
4474
- );
4475
- }
4476
- function SplitterRoot({
4477
- size,
4478
- defaultSize = 50,
4479
- minSize = 0,
4480
- maxSize = 100,
4481
- onSizeChange,
4482
- orientation = "horizontal",
4483
- handleSize = "md",
4484
- disabled = false,
4485
- handleLabel = "Resize panes",
4486
- children
4487
- }) {
4488
- const panes = paneChildren(children);
4489
- if (panes.length !== 2) {
4490
- throw new Error("Splitter requires exactly two pane children.");
4491
- }
4492
- const rootRef = useRef(null);
4493
- const grabOffsetRef = useRef(0);
4494
- const [uncontrolledSize, setUncontrolledSize] = useState(() => finiteValue(defaultSize, 50));
4495
- const { min, max } = normalizedBounds(minSize, maxSize);
4496
- const currentSize = clampSize(size === void 0 ? uncontrolledSize : finiteValue(size, 50), min, max);
4497
- const setSize = (nextSize) => {
4498
- if (disabled) return;
4499
- const next = clampSize(nextSize, min, max);
4500
- if (size === void 0) setUncontrolledSize(next);
4501
- onSizeChange?.(next);
4502
- };
4503
- const sizeFromPointer = (event) => {
4504
- const rootRect = rootRef.current?.getBoundingClientRect();
4505
- const handleRect = event.currentTarget.getBoundingClientRect();
4506
- if (!rootRect) return;
4507
- const rootLength = orientation === "horizontal" ? rootRect.width : rootRect.height;
4508
- const handleLength = orientation === "horizontal" ? handleRect.width : handleRect.height;
4509
- const availableLength = rootLength - handleLength;
4510
- if (availableLength <= 0) return;
4511
- const rootStart = orientation === "horizontal" ? rootRect.left : rootRect.top;
4512
- const pointerPosition = orientation === "horizontal" ? event.clientX : event.clientY;
4513
- setSize((pointerPosition - rootStart - grabOffsetRef.current) / availableLength * 100);
4514
- };
4515
- const handlePointerDown = (event) => {
4516
- if (disabled) return;
4517
- const handleRect = event.currentTarget.getBoundingClientRect();
4518
- const handleStart = orientation === "horizontal" ? handleRect.left : handleRect.top;
4519
- const pointerPosition = orientation === "horizontal" ? event.clientX : event.clientY;
4520
- grabOffsetRef.current = pointerPosition - handleStart;
4521
- event.currentTarget.setPointerCapture(event.pointerId);
4522
- sizeFromPointer(event);
4523
- };
4524
- const handlePointerMove = (event) => {
4525
- if (!disabled && event.currentTarget.hasPointerCapture(event.pointerId)) sizeFromPointer(event);
4526
- };
4527
- const handlePointerUp = (event) => {
4528
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
4529
- sizeFromPointer(event);
4530
- event.currentTarget.releasePointerCapture(event.pointerId);
4531
- }
4532
- };
4533
- const handlePointerCancel = (event) => {
4534
- if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
4535
- };
4536
- const handleKeyDown = (event) => {
4537
- if (disabled) return;
4538
- const decrease = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
4539
- const increase = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
4540
- if (event.key === decrease) {
4541
- event.preventDefault();
4542
- setSize(currentSize - 1);
4543
- } else if (event.key === increase) {
4544
- event.preventDefault();
4545
- setSize(currentSize + 1);
4546
- } else if (event.key === "Home") {
4547
- event.preventDefault();
4548
- setSize(min);
4549
- } else if (event.key === "End") {
4550
- event.preventDefault();
4551
- setSize(max);
4552
- }
4553
- };
4554
- const style = {
4555
- "--cb-splitter-first-grow": String(currentSize),
4556
- "--cb-splitter-second-grow": String(100 - currentSize)
4557
- };
4558
- return /* @__PURE__ */ jsxs(
4559
- "div",
4560
- {
4561
- ref: rootRef,
4562
- className: `cb-splitter cb-splitter--${orientation} cb-splitter--handle-${handleSize}${disabled ? " cb-splitter--disabled" : ""}`,
4563
- style,
4564
- children: [
4565
- /* @__PURE__ */ jsx("div", { className: "cb-splitter__pane cb-splitter__pane--first", children: panes[0] }),
4566
- /* @__PURE__ */ jsx(
4567
- "div",
4568
- {
4569
- className: "cb-splitter__handle",
4570
- role: "separator",
4571
- "aria-orientation": orientation === "horizontal" ? "vertical" : "horizontal",
4572
- "aria-valuenow": currentSize,
4573
- "aria-valuemin": min,
4574
- "aria-valuemax": max,
4575
- "aria-label": handleLabel,
4576
- "aria-disabled": disabled || void 0,
4577
- tabIndex: disabled ? -1 : 0,
4578
- onKeyDown: handleKeyDown,
4579
- onPointerDown: handlePointerDown,
4580
- onPointerMove: handlePointerMove,
4581
- onPointerUp: handlePointerUp,
4582
- onPointerCancel: handlePointerCancel
4583
- }
4584
- ),
4585
- /* @__PURE__ */ jsx("div", { className: "cb-splitter__pane cb-splitter__pane--second", children: panes[1] })
4586
- ]
4587
- }
4588
- );
4589
- }
4590
- var Splitter = Object.assign(SplitterRoot, { Skeleton: SplitterSkeleton });
4591
599
 
4592
- export { Alert, AutoComplete, BorderBeam, Button, Calendar, CalendarSkeleton, Carousel, Cascader, CascaderSkeleton, Checkbox, Checklist, Coachmark, Collapse, CollapseSkeleton, ColorPicker, ColorPickerSkeleton, CommandPalette, DEFAULT_LABELS, DatePicker, DialogClose, Drawer, DrawerClose, Dropdown, Field, FloatButton, Image, Input, InputNumber, LabelsProvider, Mentions, MentionsSkeleton, Menu, MenuSkeleton, Modal, MotionProvider, Pagination, Popconfirm, PopconfirmSkeleton, Popover, RadioGroup, Rate, Reveal, Select, Sidebar, Slider, SliderSkeleton, Splitter, SplitterSkeleton, Stagger, Switch, Table, Tabs, Tag, Textarea, ThemeProvider, TimePicker, ToastProvider, Tooltip, TopBar, Tour, Transfer, TransferSkeleton, Tree, TreeSelect, TreeSelectSkeleton, TreeSkeleton, Upload, Watermark, WatermarkSkeleton, ariaSortFor, canStep, clamp, decimalPlaces, describeAccept, filterCommands, formatISO, formatTime, groupCommands, hasEnded, initialTourState, isOutOfRange, isSameDay, isTimeOutOfRange, matchesAccept, monthMatrix, nextSort, pageRange, parseDateInput, parseNumber, parseTime, partitionFiles, rankCommand, resolveTarget, shouldAutoplay, startOfDay, stepBy, timeOptions, timeToMinutes, tourReducer, useFieldWiring, useLabels, useMotionSettings, useTheme, useToast };
600
+ export { Checklist, CommandPalette, DEFAULT_LABELS, LabelsProvider, MotionProvider, Reveal, Sidebar, Stagger, ThemeBridge, ThemeProvider, ToastProvider, TopBar, filterCommands, groupCommands, rankCommand, useLabels, useMotionSettings, useTheme, useToast };