@xcv-ui/ui 0.0.0-stage → 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/index.js ADDED
@@ -0,0 +1,1559 @@
1
+ // src/layout/Box.tsx
2
+ import { forwardRef } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var XcvBox = forwardRef(function XcvBox2({
5
+ display = "block",
6
+ direction,
7
+ align,
8
+ justify,
9
+ gap,
10
+ wrap,
11
+ className,
12
+ ...props
13
+ }, ref) {
14
+ const isFlex = display === "flex" || display === "inline-flex";
15
+ const supportsLayout = isFlex || display === "grid";
16
+ const classes = [
17
+ "xcv-box",
18
+ `xcv-box--${display}`,
19
+ supportsLayout && align && `xcv-box--align-${align}`,
20
+ supportsLayout && justify && `xcv-box--justify-${justify}`,
21
+ supportsLayout && gap && `xcv-box--gap-${gap}`,
22
+ isFlex && direction && `xcv-box--direction-${direction}`,
23
+ isFlex && wrap !== void 0 && (wrap ? "xcv-box--wrap" : "xcv-box--nowrap"),
24
+ className
25
+ ].filter(Boolean).join(" ");
26
+ return /* @__PURE__ */ jsx("div", { ref, className: classes, ...props });
27
+ });
28
+
29
+ // src/layout/Stack.tsx
30
+ import { forwardRef as forwardRef2 } from "react";
31
+ import { jsx as jsx2 } from "react/jsx-runtime";
32
+ var XcvStack = forwardRef2(function XcvStack2({ gap = "md", align = "stretch", justify = "start", className, ...props }, ref) {
33
+ const classes = ["xcv-stack", `xcv-stack--gap-${gap}`, `xcv-stack--align-${align}`, `xcv-stack--justify-${justify}`, className].filter(Boolean).join(" ");
34
+ return /* @__PURE__ */ jsx2("div", { ref, className: classes, ...props });
35
+ });
36
+
37
+ // src/layout/Inline.tsx
38
+ import { forwardRef as forwardRef3 } from "react";
39
+ import { jsx as jsx3 } from "react/jsx-runtime";
40
+ var XcvInline = forwardRef3(function XcvInline2({ gap = "sm", align = "center", justify = "start", wrap = true, className, ...props }, ref) {
41
+ const classes = ["xcv-inline", `xcv-inline--gap-${gap}`, `xcv-inline--align-${align}`, `xcv-inline--justify-${justify}`, wrap ? "xcv-inline--wrap" : "xcv-inline--nowrap", className].filter(Boolean).join(" ");
42
+ return /* @__PURE__ */ jsx3("div", { ref, className: classes, ...props });
43
+ });
44
+
45
+ // src/layout/Container.tsx
46
+ import { forwardRef as forwardRef4 } from "react";
47
+ import { jsx as jsx4 } from "react/jsx-runtime";
48
+ var XcvContainer = forwardRef4(function XcvContainer2({ size = "lg", centered = true, className, ...props }, ref) {
49
+ const classes = ["xcv-container", `xcv-container--${size}`, centered && "xcv-container--centered", className].filter(Boolean).join(" ");
50
+ return /* @__PURE__ */ jsx4("div", { ref, className: classes, ...props });
51
+ });
52
+
53
+ // src/layout/Divider.tsx
54
+ import { forwardRef as forwardRef5 } from "react";
55
+ import { jsx as jsx5 } from "react/jsx-runtime";
56
+ var XcvDivider = forwardRef5(function XcvDivider2({ orientation = "horizontal", spacing = "md", className, ...props }, ref) {
57
+ const classes = ["xcv-divider", `xcv-divider--${orientation}`, `xcv-divider--spacing-${spacing}`, className].filter(Boolean).join(" ");
58
+ if (orientation === "vertical") {
59
+ return /* @__PURE__ */ jsx5("span", { ...props, ref, role: "separator", "aria-orientation": "vertical", className: classes });
60
+ }
61
+ return /* @__PURE__ */ jsx5("hr", { ...props, ref, role: "separator", "aria-orientation": "horizontal", className: classes });
62
+ });
63
+
64
+ // src/layout/Surface.tsx
65
+ import { forwardRef as forwardRef6 } from "react";
66
+ import { jsx as jsx6 } from "react/jsx-runtime";
67
+ var XcvSurface = forwardRef6(function XcvSurface2({ variant = "default", padding = "md", radius = "subtle", className, children, ...props }, ref) {
68
+ const classes = ["xcv-surface", `xcv-surface--${variant}`, `xcv-surface--padding-${padding}`, `xcv-surface--radius-${radius}`, className].filter(Boolean).join(" ");
69
+ return /* @__PURE__ */ jsx6("div", { ref, className: classes, "data-xcv-radius": radius, ...props, children });
70
+ });
71
+
72
+ // src/layout/Card.tsx
73
+ import { forwardRef as forwardRef7 } from "react";
74
+ import { jsx as jsx7 } from "react/jsx-runtime";
75
+ function getCardClasses(variant, padding, radius, className) {
76
+ return [
77
+ "xcv-card",
78
+ `xcv-card--${variant}`,
79
+ `xcv-card--padding-${padding}`,
80
+ `xcv-card--radius-${radius}`,
81
+ className
82
+ ].filter(Boolean).join(" ");
83
+ }
84
+ var CardRoot = forwardRef7(function CardRoot2({
85
+ variant = "default",
86
+ padding = "md",
87
+ radius = "subtle",
88
+ interactive = false,
89
+ accented = false,
90
+ className,
91
+ children,
92
+ ...props
93
+ }, ref) {
94
+ return /* @__PURE__ */ jsx7(
95
+ "div",
96
+ {
97
+ ref,
98
+ className: getCardClasses(variant, padding, radius, className),
99
+ "data-xcv-radius": radius,
100
+ "data-xcv-interactive": interactive ? "on" : "off",
101
+ "data-xcv-accented": accented ? "on" : "off",
102
+ ...props,
103
+ children
104
+ }
105
+ );
106
+ });
107
+ var CardContent = forwardRef7(function CardContent2({ className, ...props }, ref) {
108
+ return /* @__PURE__ */ jsx7("div", { ref, className: ["xcv-card__content", className].filter(Boolean).join(" "), ...props });
109
+ });
110
+ var CardFooter = forwardRef7(function CardFooter2({ className, ...props }, ref) {
111
+ return /* @__PURE__ */ jsx7("div", { ref, className: ["xcv-card__footer", className].filter(Boolean).join(" "), ...props });
112
+ });
113
+ var XcvCard = Object.assign(CardRoot, { Content: CardContent, Footer: CardFooter });
114
+
115
+ // src/layout/Media.tsx
116
+ import { forwardRef as forwardRef8 } from "react";
117
+ import { jsx as jsx8 } from "react/jsx-runtime";
118
+ var ratios = { square: "1 / 1", video: "16 / 9", portrait: "3 / 4", landscape: "4 / 3", wide: "21 / 9" };
119
+ var XcvMedia = forwardRef8(function XcvMedia2({
120
+ aspectRatio,
121
+ radius = "subtle",
122
+ fit = "cover",
123
+ position = "center",
124
+ className,
125
+ style,
126
+ children,
127
+ ...props
128
+ }, ref) {
129
+ const ratio = aspectRatio ? ratios[aspectRatio] ?? aspectRatio : void 0;
130
+ return /* @__PURE__ */ jsx8(
131
+ "div",
132
+ {
133
+ ref,
134
+ className: ["xcv-media", ratio ? "xcv-media--ratio" : "xcv-media--intrinsic", className].filter(Boolean).join(" "),
135
+ "data-xcv-radius": radius,
136
+ style: { ...style, ...ratio !== void 0 ? { aspectRatio: ratio } : {}, "--xcv-media-fit": fit, "--xcv-media-position": position },
137
+ ...props,
138
+ children
139
+ }
140
+ );
141
+ });
142
+
143
+ // src/layout/List.tsx
144
+ import { forwardRef as forwardRef9 } from "react";
145
+ import { jsx as jsx9, jsxs } from "react/jsx-runtime";
146
+ var XcvList = forwardRef9(function XcvList2({ as = "ul", variant = "plain", gap = "md", marker, className, ...props }, ref) {
147
+ const Component = as;
148
+ const resolvedMarker = marker ?? (as === "ol" ? "decimal" : "none");
149
+ const classes = [
150
+ "xcv-list",
151
+ `xcv-list--${variant}`,
152
+ `xcv-list--gap-${gap}`,
153
+ `xcv-list--marker-${resolvedMarker}`,
154
+ className
155
+ ].filter(Boolean).join(" ");
156
+ return /* @__PURE__ */ jsx9(Component, { ref, className: classes, ...props });
157
+ });
158
+ var XcvListItem = forwardRef9(function XcvListItem2({ leading, trailing, align = "center", className, children, ...props }, ref) {
159
+ const classes = ["xcv-list-item", `xcv-list-item--align-${align}`, className].filter(Boolean).join(" ");
160
+ return /* @__PURE__ */ jsx9("li", { ref, className: classes, ...props, children: /* @__PURE__ */ jsxs("div", { className: "xcv-list-item__layout", children: [
161
+ leading != null ? /* @__PURE__ */ jsx9("div", { className: "xcv-list-item__leading", children: leading }) : null,
162
+ /* @__PURE__ */ jsx9("div", { className: "xcv-list-item__content", children }),
163
+ trailing != null ? /* @__PURE__ */ jsx9("div", { className: "xcv-list-item__trailing", children: trailing }) : null
164
+ ] }) });
165
+ });
166
+
167
+ // src/typography/Text.tsx
168
+ import { forwardRef as forwardRef10 } from "react";
169
+ import { jsx as jsx10 } from "react/jsx-runtime";
170
+ var XcvText = forwardRef10(function XcvText2({ as: Component = "p", variant = "body", weight = "normal", color = "primary", className, ...props }, ref) {
171
+ const classes = ["xcv-text", `xcv-text--${variant}`, `xcv-text--weight-${weight}`, `xcv-text--color-${color}`, className].filter(Boolean).join(" ");
172
+ return /* @__PURE__ */ jsx10(Component, { ref, className: classes, ...props });
173
+ });
174
+
175
+ // src/typography/Title.tsx
176
+ import { forwardRef as forwardRef11 } from "react";
177
+ import { jsx as jsx11 } from "react/jsx-runtime";
178
+ var XcvTitle = forwardRef11(function XcvTitle2({ as: Component = "h2", size = "xl", weight = "semibold", className, ...props }, ref) {
179
+ const classes = ["xcv-title", `xcv-title--${size}`, `xcv-title--weight-${weight}`, className].filter(Boolean).join(" ");
180
+ return /* @__PURE__ */ jsx11(Component, { ref, className: classes, ...props });
181
+ });
182
+
183
+ // src/typography/Label.tsx
184
+ import { forwardRef as forwardRef12 } from "react";
185
+ import { jsx as jsx12, jsxs as jsxs2 } from "react/jsx-runtime";
186
+ var XcvLabel = forwardRef12(function XcvLabel2({ required, children, className, ...props }, ref) {
187
+ return /* @__PURE__ */ jsxs2("label", { ref, className: ["xcv-label", className].filter(Boolean).join(" "), ...props, children: [
188
+ children,
189
+ required && /* @__PURE__ */ jsx12("span", { className: "xcv-label__required", "aria-hidden": "true", children: " *" })
190
+ ] });
191
+ });
192
+
193
+ // src/actions/Button.tsx
194
+ import { forwardRef as forwardRef13 } from "react";
195
+ import { jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
196
+ var XcvButton = forwardRef13(function XcvButton2({
197
+ variant = "primary",
198
+ size = "md",
199
+ radius = "subtle",
200
+ loading = false,
201
+ fullWidth = false,
202
+ iconOnly = false,
203
+ disabled,
204
+ className,
205
+ children,
206
+ type = "button",
207
+ "aria-busy": ariaBusy,
208
+ ...props
209
+ }, ref) {
210
+ const classes = [
211
+ "xcv-button",
212
+ `xcv-button--${variant}`,
213
+ `xcv-button--${size}`,
214
+ fullWidth && "xcv-button--full-width",
215
+ iconOnly && "xcv-button--icon-only",
216
+ loading && "xcv-button--loading",
217
+ className
218
+ ].filter(Boolean).join(" ");
219
+ return /* @__PURE__ */ jsxs3(
220
+ "button",
221
+ {
222
+ ref,
223
+ type,
224
+ className: classes,
225
+ disabled: disabled || loading,
226
+ "data-xcv-radius": radius,
227
+ "aria-busy": loading ? true : ariaBusy,
228
+ ...props,
229
+ children: [
230
+ loading && /* @__PURE__ */ jsx13("span", { className: "xcv-button__spinner", "aria-hidden": "true" }),
231
+ /* @__PURE__ */ jsx13("span", { className: "xcv-button__content", children })
232
+ ]
233
+ }
234
+ );
235
+ });
236
+
237
+ // src/actions/Link.tsx
238
+ import { forwardRef as forwardRef14 } from "react";
239
+ import { jsx as jsx14 } from "react/jsx-runtime";
240
+ var XcvLink = forwardRef14(function XcvLink2({ variant = "default", className, ...props }, ref) {
241
+ const classes = ["xcv-link", `xcv-link--${variant}`, className].filter(Boolean).join(" ");
242
+ return /* @__PURE__ */ jsx14("a", { ref, className: classes, ...props });
243
+ });
244
+
245
+ // src/forms/TextInput.tsx
246
+ import { forwardRef as forwardRef15 } from "react";
247
+
248
+ // src/forms/FieldContext.tsx
249
+ import { createContext, useContext } from "react";
250
+ import { jsx as jsx15 } from "react/jsx-runtime";
251
+ var XcvFieldContext = createContext(null);
252
+ function XcvFieldProvider({ value, children }) {
253
+ return /* @__PURE__ */ jsx15(XcvFieldContext.Provider, { value, children });
254
+ }
255
+ function useXcvFieldControlProps(props) {
256
+ const context = useContext(XcvFieldContext);
257
+ const describedBy = [context?.describedBy, props["aria-describedby"]].filter(Boolean).join(" ") || void 0;
258
+ const result = {};
259
+ const id = context?.controlId ?? props.id;
260
+ const required = context?.required || props.required;
261
+ const invalid = context?.invalid ? true : props["aria-invalid"];
262
+ if (id !== void 0) result.id = id;
263
+ if (required !== void 0) result.required = required;
264
+ if (describedBy !== void 0) result["aria-describedby"] = describedBy;
265
+ if (invalid !== void 0) result["aria-invalid"] = invalid;
266
+ return result;
267
+ }
268
+
269
+ // src/forms/TextInput.tsx
270
+ import { jsx as jsx16 } from "react/jsx-runtime";
271
+ var XcvTextInput = forwardRef15(function XcvTextInput2({ fieldState = "default", radius = "subtle", inputSize = "md", className, id, required, "aria-describedby": describedBy, "aria-invalid": invalid, ...props }, ref) {
272
+ const fieldProps = useXcvFieldControlProps({ id, required, "aria-describedby": describedBy, "aria-invalid": fieldState === "error" ? true : invalid });
273
+ const classes = ["xcv-field-control", "xcv-text-input", `xcv-field-control--${fieldState}`, `xcv-field-control--${inputSize}`, className].filter(Boolean).join(" ");
274
+ return /* @__PURE__ */ jsx16("input", { ref, className: classes, ...fieldProps, ...props, "data-xcv-radius": radius });
275
+ });
276
+
277
+ // src/forms/TextArea.tsx
278
+ import { forwardRef as forwardRef16 } from "react";
279
+ import { jsx as jsx17 } from "react/jsx-runtime";
280
+ var XcvTextArea = forwardRef16(function XcvTextArea2({ fieldState = "default", radius = "subtle", className, id, required, "aria-describedby": describedBy, "aria-invalid": invalid, ...props }, ref) {
281
+ const fieldProps = useXcvFieldControlProps({ id, required, "aria-describedby": describedBy, "aria-invalid": fieldState === "error" ? true : invalid });
282
+ const classes = ["xcv-field-control", "xcv-text-area", `xcv-field-control--${fieldState}`, className].filter(Boolean).join(" ");
283
+ return /* @__PURE__ */ jsx17("textarea", { ref, className: classes, ...fieldProps, ...props, "data-xcv-radius": radius });
284
+ });
285
+
286
+ // src/forms/Select.tsx
287
+ import {
288
+ forwardRef as forwardRef17,
289
+ useEffect,
290
+ useId,
291
+ useRef,
292
+ useState
293
+ } from "react";
294
+ import { createPortal } from "react-dom";
295
+ import { jsx as jsx18, jsxs as jsxs4 } from "react/jsx-runtime";
296
+ var XcvSelect = forwardRef17(function XcvSelect2({
297
+ options,
298
+ fieldState = "default",
299
+ radius = "subtle",
300
+ placeholder,
301
+ selectSize = "md",
302
+ className,
303
+ id,
304
+ required,
305
+ disabled = false,
306
+ value,
307
+ defaultValue,
308
+ name,
309
+ onChange,
310
+ onBlur,
311
+ onFocus,
312
+ onKeyDown,
313
+ onClick,
314
+ autoFocus,
315
+ "aria-label": ariaLabel,
316
+ "aria-labelledby": ariaLabelledBy,
317
+ "aria-describedby": describedBy,
318
+ "aria-invalid": invalid,
319
+ ...nativeProps
320
+ }, forwardedRef) {
321
+ const fieldProps = useXcvFieldControlProps({
322
+ id,
323
+ required,
324
+ "aria-describedby": describedBy,
325
+ "aria-invalid": fieldState === "error" ? true : invalid
326
+ });
327
+ const generatedId = useId();
328
+ const controlId = fieldProps.id ?? id ?? `xcv-select-${generatedId}`;
329
+ const isRequired = fieldProps.required ?? required;
330
+ const listboxId = `${controlId}-listbox`;
331
+ const selectedId = `${controlId}-option`;
332
+ const isControlled = value !== void 0;
333
+ const initialValue = defaultValue !== void 0 ? String(defaultValue) : placeholder !== void 0 ? "" : options[0]?.value ?? "";
334
+ const [internalValue, setInternalValue] = useState(initialValue);
335
+ const selectedValue = isControlled ? String(value) : internalValue;
336
+ const selectedValueRef = useRef(selectedValue);
337
+ selectedValueRef.current = selectedValue;
338
+ const selectedOption = options.find((option) => option.value === selectedValue);
339
+ const [open, setOpen] = useState(false);
340
+ const [activeValue, setActiveValue] = useState(selectedValue);
341
+ const [listboxPosition, setListboxPosition] = useState(null);
342
+ const [portalTheme, setPortalTheme] = useState({});
343
+ const listboxRef = useRef(null);
344
+ const selectRef = useRef(null);
345
+ const wrapperRef = useRef(null);
346
+ const triggerRef = useRef(null);
347
+ const typeaheadRef = useRef("");
348
+ const typeaheadTimerRef = useRef(null);
349
+ useEffect(() => {
350
+ if (disabled) setOpen(false);
351
+ }, [disabled]);
352
+ useEffect(() => {
353
+ if (!open) {
354
+ setPortalTheme({});
355
+ return;
356
+ }
357
+ const syncTheme = () => {
358
+ const wrapper = wrapperRef.current;
359
+ const readScope = (attribute) => wrapper?.closest(`[${attribute}]`)?.getAttribute(attribute) ?? void 0;
360
+ const brand = readScope("data-xcv-brand");
361
+ const mode = readScope("data-xcv-mode");
362
+ const theme = readScope("data-xcv-theme");
363
+ setPortalTheme({
364
+ ...brand === void 0 ? {} : { brand },
365
+ ...mode === void 0 ? {} : { mode },
366
+ ...theme === void 0 ? {} : { theme }
367
+ });
368
+ };
369
+ syncTheme();
370
+ const observer = new MutationObserver(syncTheme);
371
+ observer.observe(document.documentElement, {
372
+ attributes: true,
373
+ subtree: true,
374
+ attributeFilter: ["data-xcv-brand", "data-xcv-mode", "data-xcv-theme"]
375
+ });
376
+ return () => observer.disconnect();
377
+ }, [open]);
378
+ useEffect(() => {
379
+ if (!open) {
380
+ setListboxPosition(null);
381
+ return;
382
+ }
383
+ const updatePosition = () => {
384
+ const trigger = triggerRef.current;
385
+ const panel = listboxRef.current;
386
+ if (!trigger || !panel) return;
387
+ const rect = trigger.getBoundingClientRect();
388
+ const viewport = window.visualViewport;
389
+ const viewportTop = viewport?.offsetTop ?? 0;
390
+ const viewportLeft = viewport?.offsetLeft ?? 0;
391
+ const viewportHeight = viewport?.height ?? window.innerHeight;
392
+ const viewportWidth = viewport?.width ?? window.innerWidth;
393
+ const gap = 8;
394
+ const panelHeight = Math.min(panel.scrollHeight, 288);
395
+ const viewportBottom = viewportTop + viewportHeight;
396
+ const viewportRight = viewportLeft + viewportWidth;
397
+ const below = viewportBottom - rect.bottom - gap;
398
+ const above = rect.top - viewportTop - gap;
399
+ const placeAbove = below < Math.min(panelHeight, 180) && above > below;
400
+ const available = Math.max(0, placeAbove ? above : below);
401
+ const width = Math.min(rect.width, viewportWidth - 16);
402
+ const left = Math.min(Math.max(viewportLeft + 8, rect.left), viewportRight - width - 8);
403
+ setListboxPosition({ position: "fixed", left, width, top: placeAbove ? void 0 : rect.bottom + gap, bottom: placeAbove ? viewportBottom - rect.top + gap : void 0, maxHeight: Math.min(288, available), visibility: "visible" });
404
+ };
405
+ setListboxPosition({ position: "fixed", visibility: "hidden" });
406
+ updatePosition();
407
+ const visualViewport = window.visualViewport;
408
+ window.addEventListener("resize", updatePosition);
409
+ window.addEventListener("scroll", updatePosition, true);
410
+ visualViewport?.addEventListener("resize", updatePosition);
411
+ visualViewport?.addEventListener("scroll", updatePosition);
412
+ return () => {
413
+ window.removeEventListener("resize", updatePosition);
414
+ window.removeEventListener("scroll", updatePosition, true);
415
+ visualViewport?.removeEventListener("resize", updatePosition);
416
+ visualViewport?.removeEventListener("scroll", updatePosition);
417
+ };
418
+ }, [open, options.length]);
419
+ useEffect(() => {
420
+ if (!open) return;
421
+ function closeOnOutsidePointerDown(event) {
422
+ if (event.target instanceof Node && !wrapperRef.current?.contains(event.target) && !listboxRef.current?.contains(event.target)) {
423
+ setOpen(false);
424
+ }
425
+ }
426
+ document.addEventListener("pointerdown", closeOnOutsidePointerDown);
427
+ return () => document.removeEventListener("pointerdown", closeOnOutsidePointerDown);
428
+ }, [open]);
429
+ useEffect(() => {
430
+ if (selectRef.current && isControlled) selectRef.current.value = selectedValue;
431
+ }, [isControlled, selectedValue]);
432
+ useEffect(() => {
433
+ const form = selectRef.current?.form;
434
+ if (!form || isControlled) return;
435
+ function resetValue() {
436
+ setInternalValue(initialValue);
437
+ }
438
+ form.addEventListener("reset", resetValue);
439
+ return () => form.removeEventListener("reset", resetValue);
440
+ }, [initialValue, isControlled]);
441
+ useEffect(() => () => {
442
+ if (typeaheadTimerRef.current !== null) clearTimeout(typeaheadTimerRef.current);
443
+ }, []);
444
+ function enabledOptions() {
445
+ return options.filter((option) => !option.disabled);
446
+ }
447
+ function openAt(valueToActivate) {
448
+ const enabled = enabledOptions();
449
+ const current = enabled.find((option) => option.value === (valueToActivate ?? selectedValue));
450
+ setActiveValue(current?.value ?? enabled[0]?.value ?? "");
451
+ setOpen(true);
452
+ }
453
+ function choose(option) {
454
+ if (disabled || option.disabled) return;
455
+ const nativeSelect = selectRef.current;
456
+ if (!nativeSelect) return;
457
+ const previousValue = selectedValue;
458
+ if (option.value !== previousValue) {
459
+ if (!isControlled) setInternalValue(option.value);
460
+ nativeSelect.value = option.value;
461
+ const EventConstructor = nativeSelect.ownerDocument.defaultView?.Event;
462
+ if (EventConstructor) nativeSelect.dispatchEvent(new EventConstructor("change", { bubbles: true }));
463
+ if (isControlled) {
464
+ queueMicrotask(() => {
465
+ if (selectRef.current && selectRef.current.value !== selectedValueRef.current) {
466
+ selectRef.current.value = selectedValueRef.current;
467
+ }
468
+ });
469
+ }
470
+ }
471
+ setOpen(false);
472
+ triggerRef.current?.focus();
473
+ }
474
+ function moveActive(direction) {
475
+ const enabled = enabledOptions();
476
+ if (!enabled.length) return;
477
+ const index = enabled.findIndex((option) => option.value === activeValue);
478
+ const next = index < 0 ? direction > 0 ? 0 : enabled.length - 1 : (index + direction + enabled.length) % enabled.length;
479
+ setActiveValue(enabled[next].value);
480
+ }
481
+ function handleKeyDown(event) {
482
+ onKeyDown?.(event);
483
+ if (event.defaultPrevented) return;
484
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
485
+ event.preventDefault();
486
+ if (!open) openAt(event.key === "ArrowDown" ? selectedValue : enabledOptions()[enabledOptions().length - 1]?.value);
487
+ else moveActive(event.key === "ArrowDown" ? 1 : -1);
488
+ return;
489
+ }
490
+ if (event.key === "Home" || event.key === "End") {
491
+ if (!open) return;
492
+ event.preventDefault();
493
+ const enabled = enabledOptions();
494
+ setActiveValue(event.key === "Home" ? enabled[0]?.value ?? "" : enabled[enabled.length - 1]?.value ?? "");
495
+ return;
496
+ }
497
+ if (event.key === "Enter" || event.key === " ") {
498
+ event.preventDefault();
499
+ if (!open) openAt();
500
+ else {
501
+ const option = options.find((item) => item.value === activeValue);
502
+ if (option) choose(option);
503
+ }
504
+ return;
505
+ }
506
+ if (event.key === "Escape" && open) {
507
+ event.preventDefault();
508
+ setOpen(false);
509
+ triggerRef.current?.focus();
510
+ return;
511
+ }
512
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
513
+ const nextQuery = typeaheadRef.current + event.key.toLocaleLowerCase();
514
+ typeaheadRef.current = nextQuery;
515
+ if (typeaheadTimerRef.current !== null) clearTimeout(typeaheadTimerRef.current);
516
+ typeaheadTimerRef.current = setTimeout(() => {
517
+ typeaheadRef.current = "";
518
+ }, 600);
519
+ const start = enabledOptions();
520
+ const match = start.find((option) => option.label.toLocaleLowerCase().startsWith(nextQuery));
521
+ if (match) {
522
+ event.preventDefault();
523
+ if (open) setActiveValue(match.value);
524
+ else choose(match);
525
+ }
526
+ }
527
+ }
528
+ return /* @__PURE__ */ jsxs4("span", { ref: wrapperRef, id: `${controlId}-root`, className: "xcv-select-wrapper", "data-xcv-radius": radius, "data-open": open ? "true" : void 0, children: [
529
+ /* @__PURE__ */ jsxs4(
530
+ "select",
531
+ {
532
+ ...nativeProps,
533
+ ref: (node) => {
534
+ selectRef.current = node;
535
+ if (typeof forwardedRef === "function") forwardedRef(node);
536
+ else if (forwardedRef) forwardedRef.current = node;
537
+ },
538
+ className: "xcv-select__native",
539
+ name,
540
+ value: selectedValue,
541
+ disabled,
542
+ required: isRequired,
543
+ tabIndex: -1,
544
+ "aria-hidden": "true",
545
+ onChange,
546
+ children: [
547
+ placeholder !== void 0 && /* @__PURE__ */ jsx18("option", { value: "", disabled: true, children: placeholder }),
548
+ options.map((option) => /* @__PURE__ */ jsx18("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
549
+ ]
550
+ }
551
+ ),
552
+ /* @__PURE__ */ jsxs4(
553
+ "button",
554
+ {
555
+ ...fieldProps,
556
+ ref: triggerRef,
557
+ id: controlId,
558
+ type: "button",
559
+ role: "combobox",
560
+ className: ["xcv-field-control", "xcv-select__trigger", `xcv-field-control--${fieldState}`, `xcv-field-control--${selectSize}`, className].filter(Boolean).join(" "),
561
+ "data-xcv-radius": radius,
562
+ disabled,
563
+ "aria-label": ariaLabel,
564
+ "aria-labelledby": ariaLabelledBy,
565
+ "aria-describedby": fieldProps["aria-describedby"],
566
+ "aria-invalid": fieldProps["aria-invalid"],
567
+ "aria-required": isRequired || void 0,
568
+ autoFocus,
569
+ "aria-haspopup": "listbox",
570
+ "aria-autocomplete": "none",
571
+ "aria-expanded": open,
572
+ "aria-controls": open ? listboxId : void 0,
573
+ "aria-activedescendant": open && activeValue ? `${selectedId}-${options.findIndex((option) => option.value === activeValue)}` : void 0,
574
+ onFocus,
575
+ onBlur: (event) => {
576
+ onBlur?.(event);
577
+ if (!event.currentTarget.parentElement?.contains(event.relatedTarget) && !listboxRef.current?.contains(event.relatedTarget)) setOpen(false);
578
+ },
579
+ onKeyDown: handleKeyDown,
580
+ onClick: (event) => {
581
+ onClick?.(event);
582
+ if (event.defaultPrevented) return;
583
+ if (open) setOpen(false);
584
+ else openAt();
585
+ },
586
+ children: [
587
+ /* @__PURE__ */ jsx18("span", { className: selectedOption ? "xcv-select__value" : "xcv-select__value xcv-select__value--placeholder", children: selectedOption?.label ?? placeholder ?? "" }),
588
+ /* @__PURE__ */ jsx18("span", { className: "xcv-select__chevron", "aria-hidden": "true" })
589
+ ]
590
+ }
591
+ ),
592
+ open && typeof document !== "undefined" && createPortal(
593
+ /* @__PURE__ */ jsx18("div", { ref: listboxRef, id: listboxId, role: "listbox", "aria-labelledby": ariaLabelledBy ?? controlId, "aria-describedby": fieldProps["aria-describedby"], className: "xcv-select__listbox", "data-xcv-radius": radius, "data-xcv-brand": portalTheme.brand, "data-xcv-mode": portalTheme.mode, "data-xcv-theme": portalTheme.theme, style: listboxPosition ?? { position: "fixed", visibility: "hidden" }, children: options.length === 0 ? /* @__PURE__ */ jsx18("div", { className: "xcv-select__empty", role: "presentation", children: "No options available" }) : options.map((option, index) => /* @__PURE__ */ jsxs4("div", { id: selectedId + "-" + index, role: "option", "aria-selected": option.value === selectedValue, "aria-disabled": option.disabled || void 0, "data-active": option.value === activeValue ? "true" : void 0, className: "xcv-select__option", onPointerMove: () => {
594
+ if (!option.disabled) setActiveValue(option.value);
595
+ }, onClick: () => choose(option), children: [
596
+ /* @__PURE__ */ jsx18("span", { children: option.label }),
597
+ option.value === selectedValue && /* @__PURE__ */ jsx18("span", { className: "xcv-select__check", "aria-hidden": "true", children: "\u2713" })
598
+ ] }, option.value)) }),
599
+ document.body
600
+ )
601
+ ] });
602
+ });
603
+
604
+ // src/forms/Checkbox.tsx
605
+ import { forwardRef as forwardRef18 } from "react";
606
+ import { jsx as jsx19 } from "react/jsx-runtime";
607
+ var XcvCheckbox = forwardRef18(function XcvCheckbox2({ fieldState = "default", className, id, required, "aria-describedby": describedBy, "aria-invalid": invalid, ...props }, ref) {
608
+ const fieldProps = useXcvFieldControlProps({ id, required, "aria-describedby": describedBy, "aria-invalid": fieldState === "error" ? true : invalid });
609
+ const classes = ["xcv-checkable", "xcv-checkbox", `xcv-field-control--${fieldState}`, className].filter(Boolean).join(" ");
610
+ return /* @__PURE__ */ jsx19("input", { ref, type: "checkbox", className: classes, ...fieldProps, ...props });
611
+ });
612
+
613
+ // src/forms/Radio.tsx
614
+ import { forwardRef as forwardRef19 } from "react";
615
+ import { jsx as jsx20 } from "react/jsx-runtime";
616
+ var XcvRadio = forwardRef19(function XcvRadio2({ fieldState = "default", className, id, required, "aria-describedby": describedBy, "aria-invalid": invalid, ...props }, ref) {
617
+ const fieldProps = useXcvFieldControlProps({ id, required, "aria-describedby": describedBy, "aria-invalid": fieldState === "error" ? true : invalid });
618
+ const classes = ["xcv-checkable", "xcv-radio", `xcv-field-control--${fieldState}`, className].filter(Boolean).join(" ");
619
+ return /* @__PURE__ */ jsx20("input", { ref, type: "radio", className: classes, ...fieldProps, ...props });
620
+ });
621
+
622
+ // src/forms/Toggle.tsx
623
+ import { forwardRef as forwardRef20 } from "react";
624
+ import { jsx as jsx21 } from "react/jsx-runtime";
625
+ var XcvToggle = forwardRef20(function XcvToggle2({ fieldState = "default", className, id, required, "aria-describedby": describedBy, "aria-invalid": invalid, ...props }, ref) {
626
+ const fieldProps = useXcvFieldControlProps({ id, required, "aria-describedby": describedBy, "aria-invalid": fieldState === "error" ? true : invalid });
627
+ const classes = ["xcv-checkable", "xcv-toggle", `xcv-field-control--${fieldState}`, className].filter(Boolean).join(" ");
628
+ return /* @__PURE__ */ jsx21("input", { ref, type: "checkbox", role: "switch", className: classes, ...fieldProps, ...props });
629
+ });
630
+
631
+ // src/forms/FormField.tsx
632
+ import { forwardRef as forwardRef21, useId as useId2 } from "react";
633
+ import { jsx as jsx22, jsxs as jsxs5 } from "react/jsx-runtime";
634
+ var XcvFormField = forwardRef21(function XcvFormField2({
635
+ label,
636
+ children,
637
+ id,
638
+ description,
639
+ error,
640
+ required = false,
641
+ className,
642
+ ...props
643
+ }, ref) {
644
+ const generatedId = useId2();
645
+ const controlId = id ?? `xcv-field-${generatedId}`;
646
+ const hasDescription = description !== void 0 && description !== null && description !== false && description !== true;
647
+ const hasError = error !== void 0 && error !== null && error !== false && error !== true;
648
+ const descriptionId = hasDescription ? `${controlId}-description` : void 0;
649
+ const errorId = hasError ? `${controlId}-error` : void 0;
650
+ const describedBy = [descriptionId, errorId].filter(Boolean).join(" ") || void 0;
651
+ const value = { controlId, invalid: hasError, required };
652
+ if (describedBy) value.describedBy = describedBy;
653
+ return /* @__PURE__ */ jsxs5("div", { ref, className: ["xcv-form-field", className].filter(Boolean).join(" "), ...props, children: [
654
+ /* @__PURE__ */ jsxs5("label", { className: "xcv-form-field__label", htmlFor: controlId, children: [
655
+ label,
656
+ required && /* @__PURE__ */ jsx22("span", { className: "xcv-label__required", "aria-hidden": "true", children: " *" })
657
+ ] }),
658
+ /* @__PURE__ */ jsx22(XcvFieldProvider, { value, children }),
659
+ hasDescription && /* @__PURE__ */ jsx22("div", { className: "xcv-form-field__description", id: descriptionId, children: description }),
660
+ hasError && /* @__PURE__ */ jsx22("div", { className: "xcv-form-field__error", id: errorId, "aria-live": "polite", children: error })
661
+ ] });
662
+ });
663
+
664
+ // src/feedback/Notice.tsx
665
+ import { forwardRef as forwardRef22 } from "react";
666
+ import { jsx as jsx23, jsxs as jsxs6 } from "react/jsx-runtime";
667
+ var XcvNotice = forwardRef22(function XcvNotice2({
668
+ tone = "info",
669
+ radius = "subtle",
670
+ title,
671
+ icon,
672
+ announce = false,
673
+ className,
674
+ children,
675
+ ...props
676
+ }, ref) {
677
+ const classes = ["xcv-notice", `xcv-notice--${tone}`, className].filter(Boolean).join(" ");
678
+ return /* @__PURE__ */ jsxs6("div", { ref, ...props, className: classes, "data-xcv-radius": radius, "aria-live": announce || void 0, children: [
679
+ icon != null && /* @__PURE__ */ jsx23("span", { className: "xcv-notice__icon", "aria-hidden": "true", children: icon }),
680
+ /* @__PURE__ */ jsxs6("div", { className: "xcv-notice__body", children: [
681
+ title != null && /* @__PURE__ */ jsx23("div", { className: "xcv-notice__title", children: title }),
682
+ children != null && /* @__PURE__ */ jsx23("div", { className: "xcv-notice__content", children })
683
+ ] })
684
+ ] });
685
+ });
686
+
687
+ // src/feedback/Badge.tsx
688
+ import { forwardRef as forwardRef23 } from "react";
689
+ import { jsx as jsx24 } from "react/jsx-runtime";
690
+ var XcvBadge = forwardRef23(function XcvBadge2({ tone = "neutral", radius = "subtle", className, ...props }, ref) {
691
+ const classes = ["xcv-badge", `xcv-badge--${tone}`, className].filter(Boolean).join(" ");
692
+ return /* @__PURE__ */ jsx24("span", { ref, className: classes, "data-xcv-radius": radius, ...props });
693
+ });
694
+
695
+ // src/feedback/Loader.tsx
696
+ import { forwardRef as forwardRef24 } from "react";
697
+ import { jsx as jsx25, jsxs as jsxs7 } from "react/jsx-runtime";
698
+ var XcvLoader = forwardRef24(function XcvLoader2({ variant = "orbit", size = "md", label = "Loading", showLabel = false, className, ...props }, ref) {
699
+ const classes = ["xcv-loader", `xcv-loader--${size}`, className].filter(Boolean).join(" ");
700
+ return /* @__PURE__ */ jsxs7("span", { ref, ...props, className: classes, "data-variant": variant, role: "status", "aria-live": "polite", "aria-label": label, children: [
701
+ /* @__PURE__ */ jsx25("span", { className: "xcv-loader__visual", "aria-hidden": "true", children: Array.from({ length: 5 }, (_, index) => /* @__PURE__ */ jsx25("span", { className: "xcv-loader__shape" }, index)) }),
702
+ showLabel && /* @__PURE__ */ jsx25("span", { className: "xcv-loader__label", "aria-hidden": "true", children: label })
703
+ ] });
704
+ });
705
+
706
+ // src/navigation/Accordion.tsx
707
+ import {
708
+ Children,
709
+ Fragment,
710
+ createContext as createContext2,
711
+ forwardRef as forwardRef25,
712
+ isValidElement,
713
+ useContext as useContext2,
714
+ useId as useId3,
715
+ useState as useState2
716
+ } from "react";
717
+ import { jsx as jsx26, jsxs as jsxs8 } from "react/jsx-runtime";
718
+ var AccordionContext = createContext2(null);
719
+ var AccordionItemContext = createContext2(null);
720
+ function normalizeValue(value, mode) {
721
+ if (value === void 0) return [];
722
+ const values = typeof value === "string" ? [value] : [...new Set(value)];
723
+ return mode === "single" ? values.slice(0, 1) : values;
724
+ }
725
+ function useAccordionContext() {
726
+ const context = useContext2(AccordionContext);
727
+ if (!context) throw new Error("XcvAccordion compound components must be used inside XcvAccordion.");
728
+ return context;
729
+ }
730
+ function useAccordionItemContext() {
731
+ const context = useContext2(AccordionItemContext);
732
+ if (!context) throw new Error("XcvAccordion.Trigger and XcvAccordion.Content must be used inside XcvAccordion.Item.");
733
+ return context;
734
+ }
735
+ var XcvAccordionRoot = forwardRef25(function XcvAccordionRoot2({
736
+ mode = "single",
737
+ value,
738
+ defaultValue,
739
+ onValueChange,
740
+ radius = "subtle",
741
+ className,
742
+ children,
743
+ ...props
744
+ }, ref) {
745
+ const [internalValue, setInternalValue] = useState2(() => normalizeValue(defaultValue, mode));
746
+ const openValues = normalizeValue(value === void 0 ? internalValue : value, mode);
747
+ const itemValues = collectAccordionItemValues(children);
748
+ if (new Set(itemValues).size !== itemValues.length) throw new Error("XcvAccordion item values must be unique.");
749
+ function toggle(itemValue) {
750
+ let next;
751
+ if (mode === "multiple") next = openValues.includes(itemValue) ? openValues.filter((current) => current !== itemValue) : [...openValues, itemValue];
752
+ else next = openValues.includes(itemValue) ? [] : [itemValue];
753
+ if (value === void 0) setInternalValue(next);
754
+ onValueChange?.(mode === "multiple" ? next : next[0]);
755
+ }
756
+ return /* @__PURE__ */ jsx26(AccordionContext.Provider, { value: { openValues, toggle }, children: /* @__PURE__ */ jsx26("div", { ref, "data-xcv-radius": radius, className: ["xcv-accordion", className].filter(Boolean).join(" "), ...props, children }) });
757
+ });
758
+ function XcvAccordionItem({ value, disabled = false, className, children, ...props }) {
759
+ const { openValues } = useAccordionContext();
760
+ const triggerCount = countAccordionSlots(children, XcvAccordionTrigger);
761
+ const contentCount = countAccordionSlots(children, XcvAccordionContent);
762
+ if (triggerCount !== 1 || contentCount !== 1) {
763
+ throw new Error("Each XcvAccordion.Item must contain exactly one XcvAccordion.Trigger and one XcvAccordion.Content.");
764
+ }
765
+ const id = useId3();
766
+ const context = { itemValue: value, triggerId: `xcv-accordion-trigger-${id}`, panelId: `xcv-accordion-panel-${id}`, open: openValues.includes(value), disabled };
767
+ return /* @__PURE__ */ jsx26(AccordionItemContext.Provider, { value: context, children: /* @__PURE__ */ jsx26("div", { className: ["xcv-accordion__item", context.open && "xcv-accordion__item--open", disabled && "xcv-accordion__item--disabled", className].filter(Boolean).join(" "), ...props, children }) });
768
+ }
769
+ function XcvAccordionTrigger({ className, children, type = "button", disabled, ...props }) {
770
+ const { toggle } = useAccordionContext();
771
+ const item = useAccordionItemContext();
772
+ return /* @__PURE__ */ jsxs8(
773
+ "button",
774
+ {
775
+ ...props,
776
+ id: item.triggerId,
777
+ type,
778
+ role: "button",
779
+ className: ["xcv-accordion__trigger", className].filter(Boolean).join(" "),
780
+ "aria-expanded": item.open,
781
+ "aria-controls": item.panelId,
782
+ disabled: disabled || item.disabled,
783
+ onClick: (event) => {
784
+ props.onClick?.(event);
785
+ if (!event.defaultPrevented && !disabled && !item.disabled) toggle(item.itemValue);
786
+ },
787
+ children: [
788
+ /* @__PURE__ */ jsx26("span", { className: "xcv-accordion__trigger-content", children }),
789
+ /* @__PURE__ */ jsx26("span", { className: "xcv-accordion__indicator xcv-chevron", "aria-hidden": "true" })
790
+ ]
791
+ }
792
+ );
793
+ }
794
+ function XcvAccordionContent({ className, children, ...props }) {
795
+ const item = useAccordionItemContext();
796
+ return /* @__PURE__ */ jsx26(
797
+ "div",
798
+ {
799
+ ...props,
800
+ id: item.panelId,
801
+ role: "region",
802
+ "aria-labelledby": item.triggerId,
803
+ hidden: !item.open,
804
+ className: ["xcv-accordion__content", className].filter(Boolean).join(" "),
805
+ children
806
+ }
807
+ );
808
+ }
809
+ function countAccordionSlots(children, slotType) {
810
+ let count = 0;
811
+ Children.forEach(children, (child) => {
812
+ if (!isValidElement(child)) return;
813
+ if (child.type === slotType) {
814
+ count += 1;
815
+ return;
816
+ }
817
+ if (child.type === Fragment) count += countAccordionSlots(child.props.children, slotType);
818
+ });
819
+ return count;
820
+ }
821
+ function collectAccordionItemValues(children) {
822
+ const values = [];
823
+ Children.forEach(children, (child) => {
824
+ if (!isValidElement(child)) return;
825
+ if (child.type === XcvAccordionItem) {
826
+ if (typeof child.props.value === "string") values.push(child.props.value);
827
+ return;
828
+ }
829
+ if (child.type === Fragment) values.push(...collectAccordionItemValues(child.props.children));
830
+ });
831
+ return values;
832
+ }
833
+ var XcvAccordion = Object.assign(XcvAccordionRoot, { Item: XcvAccordionItem, Trigger: XcvAccordionTrigger, Content: XcvAccordionContent });
834
+
835
+ // src/navigation/ActionsMenu.tsx
836
+ import { Children as Children2, Fragment as Fragment2, createContext as createContext3, forwardRef as forwardRef26, isValidElement as isValidElement2, useContext as useContext3, useId as useId4, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
837
+ import { createPortal as createPortal2 } from "react-dom";
838
+ import { jsx as jsx27 } from "react/jsx-runtime";
839
+ var MenuContext = createContext3(null);
840
+ function useMenu() {
841
+ const value = useContext3(MenuContext);
842
+ if (!value) throw new Error("XcvActionsMenu parts must be used inside XcvActionsMenu.");
843
+ return value;
844
+ }
845
+ var XcvActionsMenuRoot = forwardRef26(function XcvActionsMenuRoot2({ radius = "subtle", defaultOpen = false, open, onOpenChange, className, children, onBlur, ...props }, ref) {
846
+ const [internalOpen, setInternalOpen] = useState3(defaultOpen);
847
+ const isOpen = open ?? internalOpen;
848
+ const id = useId4();
849
+ const triggerRef = useRef2(null);
850
+ const contentRef = useRef2(null);
851
+ const rootRef = useRef2(null);
852
+ const focusLast = useRef2(false);
853
+ const restoreFocus = useRef2(false);
854
+ useEffect2(() => {
855
+ if (!isOpen) return;
856
+ function closeOnOutsidePointerDown(event) {
857
+ if (event.target instanceof Node && !rootRef.current?.contains(event.target) && !contentRef.current?.contains(event.target)) {
858
+ restoreFocus.current = false;
859
+ if (open === void 0) setInternalOpen(false);
860
+ onOpenChange?.(false);
861
+ }
862
+ }
863
+ document.addEventListener("pointerdown", closeOnOutsidePointerDown);
864
+ return () => document.removeEventListener("pointerdown", closeOnOutsidePointerDown);
865
+ }, [isOpen, open, onOpenChange]);
866
+ const triggerCount = countMenuSlots(children, XcvActionsMenuTrigger);
867
+ const contentCount = countMenuSlots(children, XcvActionsMenuContent);
868
+ if (triggerCount !== 1 || contentCount !== 1) {
869
+ throw new Error("XcvActionsMenu requires exactly one Trigger and one Content.");
870
+ }
871
+ if (hasMenuPartsOutsideContent(children)) {
872
+ throw new Error("XcvActionsMenu Item and Separator slots must be inside Content.");
873
+ }
874
+ const setOpen = (next) => {
875
+ if (open === void 0) setInternalOpen(next);
876
+ onOpenChange?.(next);
877
+ };
878
+ return /* @__PURE__ */ jsx27(MenuContext.Provider, { value: { open: isOpen, setOpen, menuId: `xcv-actions-menu-${id}`, radius, triggerRef, contentRef, focusLast, restoreFocus }, children: /* @__PURE__ */ jsx27("div", { ref: (node) => {
879
+ rootRef.current = node;
880
+ if (typeof ref === "function") ref(node);
881
+ else if (ref) ref.current = node;
882
+ }, ...props, "data-xcv-radius": radius, className: ["xcv-actions-menu", className].filter(Boolean).join(" "), onBlur: (event) => {
883
+ onBlur?.(event);
884
+ if (!event.defaultPrevented && !event.currentTarget.contains(event.relatedTarget) && !contentRef.current?.contains(event.relatedTarget)) {
885
+ restoreFocus.current = false;
886
+ setOpen(false);
887
+ }
888
+ }, children }) });
889
+ });
890
+ function XcvActionsMenuTrigger({ className, children, type = "button", onClick, onKeyDown, ...props }) {
891
+ const menu = useMenu();
892
+ return /* @__PURE__ */ jsx27(
893
+ "button",
894
+ {
895
+ ...props,
896
+ ref: menu.triggerRef,
897
+ type,
898
+ className: ["xcv-actions-menu__trigger", className].filter(Boolean).join(" "),
899
+ "aria-haspopup": "menu",
900
+ "aria-expanded": menu.open,
901
+ "aria-controls": menu.open ? menu.menuId : void 0,
902
+ onClick: (event) => {
903
+ onClick?.(event);
904
+ if (!event.defaultPrevented) {
905
+ if (!menu.open) {
906
+ menu.focusLast.current = false;
907
+ menu.restoreFocus.current = false;
908
+ }
909
+ menu.setOpen(!menu.open);
910
+ }
911
+ },
912
+ onKeyDown: (event) => {
913
+ onKeyDown?.(event);
914
+ if (event.defaultPrevented) return;
915
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
916
+ event.preventDefault();
917
+ menu.focusLast.current = event.key === "ArrowUp";
918
+ menu.restoreFocus.current = false;
919
+ menu.setOpen(true);
920
+ }
921
+ },
922
+ children
923
+ }
924
+ );
925
+ }
926
+ function XcvActionsMenuContent({ label, className, children, onKeyDown, ...props }) {
927
+ const menu = useMenu();
928
+ const [position, setPosition] = useState3(null);
929
+ const wasOpen = useRef2(menu.open);
930
+ const typeahead = useRef2("");
931
+ const typeaheadTimer = useRef2(null);
932
+ useEffect2(() => () => {
933
+ if (typeaheadTimer.current !== null) clearTimeout(typeaheadTimer.current);
934
+ }, []);
935
+ useEffect2(() => {
936
+ if (!menu.open) {
937
+ setPosition(null);
938
+ menu.focusLast.current = false;
939
+ if (wasOpen.current && menu.restoreFocus.current) {
940
+ menu.restoreFocus.current = false;
941
+ menu.triggerRef.current?.focus();
942
+ }
943
+ wasOpen.current = false;
944
+ return;
945
+ }
946
+ wasOpen.current = true;
947
+ const updatePosition = () => {
948
+ const trigger = menu.triggerRef.current;
949
+ const panel = menu.contentRef.current;
950
+ if (!trigger || !panel) return;
951
+ const rect = trigger.getBoundingClientRect();
952
+ const panelRect = panel.getBoundingClientRect();
953
+ const viewport = window.visualViewport;
954
+ const viewportTop = viewport?.offsetTop ?? 0;
955
+ const viewportLeft = viewport?.offsetLeft ?? 0;
956
+ const viewportHeight = viewport?.height ?? window.innerHeight;
957
+ const viewportWidth = viewport?.width ?? window.innerWidth;
958
+ const viewportBottom = viewportTop + viewportHeight;
959
+ const viewportRight = viewportLeft + viewportWidth;
960
+ const gap = 8;
961
+ const below = viewportBottom - rect.bottom - gap;
962
+ const above = rect.top - viewportTop - gap;
963
+ const placeAbove = below < Math.min(panelRect.height, 180) && above > below;
964
+ const width = Math.min(Math.max(panelRect.width, rect.width), viewportWidth - 16);
965
+ const left = Math.min(Math.max(viewportLeft + 8, rect.left), viewportRight - width - 8);
966
+ setPosition({ position: "fixed", left, width, minWidth: Math.min(rect.width, viewportWidth - 16), maxWidth: "calc(100vw - 16px)", top: placeAbove ? void 0 : rect.bottom + gap, bottom: placeAbove ? viewportBottom - rect.top + gap : void 0, maxHeight: Math.max(0, placeAbove ? above : below), visibility: "visible" });
967
+ };
968
+ setPosition({ position: "fixed", visibility: "hidden" });
969
+ updatePosition();
970
+ const visualViewport = window.visualViewport;
971
+ window.addEventListener("resize", updatePosition);
972
+ window.addEventListener("scroll", updatePosition, true);
973
+ visualViewport?.addEventListener("resize", updatePosition);
974
+ visualViewport?.addEventListener("scroll", updatePosition);
975
+ const items = menu.contentRef.current?.querySelectorAll('[role="menuitem"]');
976
+ const target = menu.focusLast.current ? items?.[items.length - 1] : items?.[0];
977
+ target?.focus();
978
+ return () => {
979
+ window.removeEventListener("resize", updatePosition);
980
+ window.removeEventListener("scroll", updatePosition, true);
981
+ visualViewport?.removeEventListener("resize", updatePosition);
982
+ visualViewport?.removeEventListener("scroll", updatePosition);
983
+ };
984
+ }, [menu.open]);
985
+ function moveFocus(current, direction) {
986
+ const items = Array.from(menu.contentRef.current?.querySelectorAll('[role="menuitem"]') ?? []);
987
+ if (!items.length) return;
988
+ const currentItem = current.closest('[role="menuitem"]');
989
+ const index = currentItem ? items.indexOf(currentItem) : -1;
990
+ if (index < 0) return;
991
+ items[(index + direction + items.length) % items.length]?.focus();
992
+ }
993
+ return menu.open && typeof document !== "undefined" ? createPortal2(
994
+ /* @__PURE__ */ jsx27(
995
+ "div",
996
+ {
997
+ ...props,
998
+ ref: menu.contentRef,
999
+ id: menu.menuId,
1000
+ role: "menu",
1001
+ "aria-label": label,
1002
+ "data-xcv-radius": menu.radius,
1003
+ style: position ?? { position: "fixed", visibility: "hidden" },
1004
+ className: ["xcv-actions-menu__content", className].filter(Boolean).join(" "),
1005
+ onKeyDown: (event) => {
1006
+ onKeyDown?.(event);
1007
+ if (event.defaultPrevented) return;
1008
+ const target = event.target;
1009
+ if (event.key === "ArrowDown") {
1010
+ event.preventDefault();
1011
+ moveFocus(target, 1);
1012
+ } else if (event.key === "ArrowUp") {
1013
+ event.preventDefault();
1014
+ moveFocus(target, -1);
1015
+ } else if (event.key === "Home" || event.key === "End") {
1016
+ event.preventDefault();
1017
+ const items = menu.contentRef.current?.querySelectorAll('[role="menuitem"]');
1018
+ (event.key === "Home" ? items?.[0] : items?.[items.length - 1])?.focus();
1019
+ } else if (event.key === "Escape") {
1020
+ event.preventDefault();
1021
+ menu.restoreFocus.current = true;
1022
+ menu.setOpen(false);
1023
+ } else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
1024
+ const character = event.key.toLocaleLowerCase();
1025
+ const previous = typeahead.current;
1026
+ const query = previous && [...previous].every((letter) => letter === character) ? character : previous + character;
1027
+ typeahead.current = query;
1028
+ if (typeaheadTimer.current !== null) clearTimeout(typeaheadTimer.current);
1029
+ typeaheadTimer.current = setTimeout(() => {
1030
+ typeahead.current = "";
1031
+ }, 600);
1032
+ const items = Array.from(menu.contentRef.current?.querySelectorAll('[role="menuitem"]') ?? []);
1033
+ const active = items.indexOf(document.activeElement);
1034
+ for (let offset = 1; offset <= items.length; offset += 1) {
1035
+ const candidate = items[(active + offset + items.length) % items.length];
1036
+ if (candidate?.textContent?.trim().toLocaleLowerCase().startsWith(query)) {
1037
+ event.preventDefault();
1038
+ candidate.focus();
1039
+ break;
1040
+ }
1041
+ }
1042
+ }
1043
+ },
1044
+ children
1045
+ }
1046
+ ),
1047
+ document.body
1048
+ ) : null;
1049
+ }
1050
+ function XcvActionsMenuItem({ className, disabled = false, onSelect, onClick, children, ...props }) {
1051
+ const menu = useMenu();
1052
+ return /* @__PURE__ */ jsx27(
1053
+ "button",
1054
+ {
1055
+ ...props,
1056
+ type: "button",
1057
+ role: "menuitem",
1058
+ tabIndex: -1,
1059
+ "aria-disabled": disabled || void 0,
1060
+ className: ["xcv-actions-menu__item", className].filter(Boolean).join(" "),
1061
+ onClick: (event) => {
1062
+ if (disabled) {
1063
+ event.preventDefault();
1064
+ return;
1065
+ }
1066
+ onClick?.(event);
1067
+ if (event.defaultPrevented) return;
1068
+ onSelect?.();
1069
+ menu.restoreFocus.current = true;
1070
+ menu.setOpen(false);
1071
+ },
1072
+ children
1073
+ }
1074
+ );
1075
+ }
1076
+ function XcvActionsMenuSeparator() {
1077
+ return /* @__PURE__ */ jsx27("div", { className: "xcv-actions-menu__separator", role: "separator" });
1078
+ }
1079
+ function hasMenuPartsOutsideContent(children, insideContent = false) {
1080
+ let invalid = false;
1081
+ Children2.forEach(children, (child) => {
1082
+ if (invalid || !isValidElement2(child)) return;
1083
+ if (child.type === Fragment2) {
1084
+ invalid = hasMenuPartsOutsideContent(child.props.children, insideContent);
1085
+ return;
1086
+ }
1087
+ if (child.type === XcvActionsMenuContent) {
1088
+ invalid = insideContent || hasMenuPartsOutsideContent(child.props.children, true);
1089
+ return;
1090
+ }
1091
+ if (child.type === XcvActionsMenuItem || child.type === XcvActionsMenuSeparator) {
1092
+ invalid = !insideContent;
1093
+ }
1094
+ });
1095
+ return invalid;
1096
+ }
1097
+ function countMenuSlots(children, slotType) {
1098
+ let count = 0;
1099
+ Children2.forEach(children, (child) => {
1100
+ if (!isValidElement2(child)) return;
1101
+ if (child.type === slotType) {
1102
+ count += 1;
1103
+ return;
1104
+ }
1105
+ if (child.type === Fragment2) count += countMenuSlots(child.props.children, slotType);
1106
+ });
1107
+ return count;
1108
+ }
1109
+ var XcvActionsMenu = Object.assign(XcvActionsMenuRoot, { Trigger: XcvActionsMenuTrigger, Content: XcvActionsMenuContent, Item: XcvActionsMenuItem, Separator: XcvActionsMenuSeparator });
1110
+
1111
+ // src/navigation/Tabs.tsx
1112
+ import { Children as Children3, Fragment as Fragment3, createContext as createContext4, forwardRef as forwardRef27, isValidElement as isValidElement3, useContext as useContext4, useId as useId5, useState as useState4 } from "react";
1113
+ import { jsx as jsx28 } from "react/jsx-runtime";
1114
+ function getTabId(baseId, kind, value) {
1115
+ return `${baseId}-${kind}-${encodeURIComponent(value)}`;
1116
+ }
1117
+ var TabsContext = createContext4(null);
1118
+ function useTabs() {
1119
+ const value = useContext4(TabsContext);
1120
+ if (!value) throw new Error("XcvTabs parts must be used inside XcvTabs.");
1121
+ return value;
1122
+ }
1123
+ var XcvTabsRoot = forwardRef27(function XcvTabsRoot2({ value, defaultValue, onValueChange, radius = "subtle", orientation = "horizontal", className, children, ...props }, ref) {
1124
+ const [internalValue, setInternalValue] = useState4(defaultValue);
1125
+ const selected = value ?? internalValue;
1126
+ if (selected === void 0) throw new Error("XcvTabs requires a valid value or defaultValue so one tab is selected initially.");
1127
+ const tabListCount = countCompoundComponents(children, XcvTabsList);
1128
+ if (tabListCount !== 1) throw new Error("XcvTabs requires exactly one XcvTabs.List.");
1129
+ const tabValues = collectCompoundValues(children, XcvTabsTab, [XcvTabsList]);
1130
+ const panelValues = collectCompoundValues(children, XcvTabsPanel, []);
1131
+ if (new Set(tabValues).size !== tabValues.length) throw new Error("XcvTabs tab values must be unique.");
1132
+ if (new Set(panelValues).size !== panelValues.length) throw new Error("XcvTabs panel values must be unique.");
1133
+ if (tabValues.length !== panelValues.length || tabValues.some((tabValue) => !panelValues.includes(tabValue))) {
1134
+ throw new Error("Every XcvTabs.Tab must have exactly one XcvTabs.Panel with the same value.");
1135
+ }
1136
+ if (!tabValues.includes(selected)) throw new Error("XcvTabs selected value must match an available tab.");
1137
+ if (isCompoundTabDisabled(children, selected, [XcvTabsList])) throw new Error("XcvTabs selected tab must not be disabled.");
1138
+ const baseId = useId5();
1139
+ const select = (next) => {
1140
+ if (value === void 0) setInternalValue(next);
1141
+ onValueChange?.(next);
1142
+ };
1143
+ return /* @__PURE__ */ jsx28(TabsContext.Provider, { value: { value: selected, select, baseId, orientation }, children: /* @__PURE__ */ jsx28("div", { ref, ...props, "data-xcv-radius": radius, className: ["xcv-tabs", `xcv-tabs--${orientation}`, className].filter(Boolean).join(" "), children }) });
1144
+ });
1145
+ function XcvTabsList({ label, className, children, onKeyDown, ...props }) {
1146
+ const tabs = useTabs();
1147
+ function handleKeyDown(event) {
1148
+ onKeyDown?.(event);
1149
+ if (event.defaultPrevented) return;
1150
+ const directionElement = event.currentTarget.closest("[dir]");
1151
+ const inheritedDirection = directionElement?.getAttribute("dir")?.toLowerCase();
1152
+ const computedDirection = getComputedStyle(event.currentTarget).direction;
1153
+ const isRtl = tabs.orientation === "horizontal" && (inheritedDirection === "rtl" || inheritedDirection !== "ltr" && computedDirection === "rtl");
1154
+ const forward = tabs.orientation === "horizontal" ? isRtl ? "ArrowLeft" : "ArrowRight" : "ArrowDown";
1155
+ const backward = tabs.orientation === "horizontal" ? isRtl ? "ArrowRight" : "ArrowLeft" : "ArrowUp";
1156
+ if (![forward, backward, "Home", "End"].includes(event.key)) return;
1157
+ const buttons = Array.from(event.currentTarget.querySelectorAll('[role="tab"]:not(:disabled)'));
1158
+ if (!buttons.length) return;
1159
+ event.preventDefault();
1160
+ const currentTab = event.target.closest('[role="tab"]');
1161
+ const index = currentTab ? buttons.indexOf(currentTab) : -1;
1162
+ if (index < 0) return;
1163
+ const next = event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : (index + (event.key === forward ? 1 : -1) + buttons.length) % buttons.length;
1164
+ const nextButton = buttons[next];
1165
+ if (!nextButton) return;
1166
+ nextButton.focus();
1167
+ const nextValue = nextButton.dataset.value;
1168
+ if (nextValue !== void 0) tabs.select(nextValue);
1169
+ }
1170
+ return /* @__PURE__ */ jsx28("div", { ...props, role: "tablist", "aria-label": label, "aria-orientation": tabs.orientation, className: ["xcv-tabs__list", className].filter(Boolean).join(" "), onKeyDown: handleKeyDown, children });
1171
+ }
1172
+ function XcvTabsTab({ value, className, children, disabled, ...props }) {
1173
+ const tabs = useTabs();
1174
+ const selected = tabs.value === value;
1175
+ const id = getTabId(tabs.baseId, "tab", value);
1176
+ const panelId = getTabId(tabs.baseId, "panel", value);
1177
+ return /* @__PURE__ */ jsx28(
1178
+ "button",
1179
+ {
1180
+ ...props,
1181
+ type: "button",
1182
+ role: "tab",
1183
+ id,
1184
+ "data-value": value,
1185
+ "aria-selected": selected,
1186
+ "aria-controls": panelId,
1187
+ tabIndex: selected ? 0 : -1,
1188
+ disabled,
1189
+ className: ["xcv-tabs__tab", selected && "xcv-tabs__tab--selected", className].filter(Boolean).join(" "),
1190
+ onClick: (event) => {
1191
+ props.onClick?.(event);
1192
+ if (!event.defaultPrevented && !disabled) tabs.select(value);
1193
+ },
1194
+ children
1195
+ }
1196
+ );
1197
+ }
1198
+ function XcvTabsPanel({ value, className, children, ...props }) {
1199
+ const tabs = useTabs();
1200
+ const selected = tabs.value === value;
1201
+ return /* @__PURE__ */ jsx28(
1202
+ "div",
1203
+ {
1204
+ ...props,
1205
+ id: getTabId(tabs.baseId, "panel", value),
1206
+ role: "tabpanel",
1207
+ "aria-labelledby": getTabId(tabs.baseId, "tab", value),
1208
+ hidden: !selected,
1209
+ tabIndex: 0,
1210
+ className: ["xcv-tabs__panel", className].filter(Boolean).join(" "),
1211
+ children
1212
+ }
1213
+ );
1214
+ }
1215
+ function countCompoundComponents(children, targetType) {
1216
+ let count = 0;
1217
+ Children3.forEach(children, (child) => {
1218
+ if (!isValidElement3(child)) return;
1219
+ if (child.type === targetType) {
1220
+ count += 1;
1221
+ return;
1222
+ }
1223
+ if (child.type === Fragment3) count += countCompoundComponents(child.props.children, targetType);
1224
+ });
1225
+ return count;
1226
+ }
1227
+ function collectCompoundValues(children, targetType, traversableTypes) {
1228
+ const values = [];
1229
+ Children3.forEach(children, (child) => {
1230
+ if (!isValidElement3(child)) return;
1231
+ if (child.type === targetType) {
1232
+ if (typeof child.props.value === "string") values.push(child.props.value);
1233
+ return;
1234
+ }
1235
+ if (child.type === Fragment3 || traversableTypes.includes(child.type)) {
1236
+ values.push(...collectCompoundValues(child.props.children, targetType, traversableTypes));
1237
+ }
1238
+ });
1239
+ return values;
1240
+ }
1241
+ function isCompoundTabDisabled(children, selectedValue, traversableTypes) {
1242
+ let disabled = false;
1243
+ Children3.forEach(children, (child) => {
1244
+ if (!isValidElement3(child)) return;
1245
+ if (child.type === XcvTabsTab) {
1246
+ if (child.props.value === selectedValue && child.props.disabled === true) disabled = true;
1247
+ return;
1248
+ }
1249
+ if (child.type === Fragment3 || traversableTypes.includes(child.type)) {
1250
+ if (isCompoundTabDisabled(child.props.children, selectedValue, traversableTypes)) disabled = true;
1251
+ }
1252
+ });
1253
+ return disabled;
1254
+ }
1255
+ var XcvTabs = Object.assign(XcvTabsRoot, { List: XcvTabsList, Tab: XcvTabsTab, Panel: XcvTabsPanel });
1256
+
1257
+ // src/navigation/Stepper.tsx
1258
+ import { forwardRef as forwardRef28 } from "react";
1259
+ import { jsx as jsx29, jsxs as jsxs9 } from "react/jsx-runtime";
1260
+ var XcvStepper = forwardRef28(function XcvStepper2({ steps, columns = 4, label = "Workflow steps", radius = "subtle", className, ...props }, ref) {
1261
+ const stepIds = /* @__PURE__ */ new Set();
1262
+ for (const step of steps) {
1263
+ if (stepIds.has(step.id)) {
1264
+ throw new Error(`XcvStepper step ids must be unique. Duplicate id: "${step.id}".`);
1265
+ }
1266
+ stepIds.add(step.id);
1267
+ }
1268
+ return /* @__PURE__ */ jsx29(
1269
+ "nav",
1270
+ {
1271
+ ref,
1272
+ ...props,
1273
+ "aria-label": label,
1274
+ "data-xcv-radius": radius,
1275
+ className: ["xcv-stepper", `xcv-stepper--columns-${columns}`, className].filter(Boolean).join(" "),
1276
+ children: /* @__PURE__ */ jsx29("ol", { className: "xcv-stepper__list", children: steps.map((step, index) => /* @__PURE__ */ jsx29("li", { className: "xcv-stepper__item", children: /* @__PURE__ */ jsxs9("a", { className: "xcv-stepper__link", href: step.href, children: [
1277
+ /* @__PURE__ */ jsx29("span", { className: "xcv-stepper__number", "aria-hidden": "true", children: String(index + 1).padStart(2, "0") }),
1278
+ /* @__PURE__ */ jsx29("span", { className: "xcv-stepper__label", children: step.label })
1279
+ ] }) }, step.id)) })
1280
+ }
1281
+ );
1282
+ });
1283
+
1284
+ // src/navigation/Pagination.tsx
1285
+ import { forwardRef as forwardRef29 } from "react";
1286
+ import { jsx as jsx30, jsxs as jsxs10 } from "react/jsx-runtime";
1287
+ var XcvPagination = forwardRef29(function XcvPagination2({
1288
+ page,
1289
+ pageCount,
1290
+ onPageChange,
1291
+ siblingCount: _siblingCount,
1292
+ label = "Pagination",
1293
+ firstLabel = "First page",
1294
+ previousLabel = "Previous page",
1295
+ nextLabel = "Next page",
1296
+ lastLabel = "Last page",
1297
+ pageLabel = (number) => `Page ${number}`,
1298
+ radius = "subtle",
1299
+ className,
1300
+ ...props
1301
+ }, ref) {
1302
+ if (!Number.isSafeInteger(page) || !Number.isSafeInteger(pageCount) || pageCount < 0 || page < 1 || (pageCount === 0 ? page !== 1 : page > pageCount)) {
1303
+ throw new RangeError("XcvPagination expects a valid 1-based page within pageCount.");
1304
+ }
1305
+ void _siblingCount;
1306
+ if (pageCount <= 1) return null;
1307
+ const firstVisiblePage = pageCount >= 3 ? Math.min(Math.max(1, page - 1), pageCount - 2) : 1;
1308
+ const visiblePages = Array.from({ length: 3 }, (_, index) => {
1309
+ const number = firstVisiblePage + index;
1310
+ return number <= pageCount ? number : null;
1311
+ });
1312
+ return /* @__PURE__ */ jsxs10(
1313
+ "nav",
1314
+ {
1315
+ ref,
1316
+ ...props,
1317
+ "data-xcv-radius": radius,
1318
+ "aria-label": label,
1319
+ className: ["xcv-pagination", className].filter(Boolean).join(" "),
1320
+ children: [
1321
+ /* @__PURE__ */ jsx30(
1322
+ "button",
1323
+ {
1324
+ type: "button",
1325
+ className: "xcv-pagination__control",
1326
+ "aria-label": firstLabel,
1327
+ disabled: page <= 1,
1328
+ onClick: () => onPageChange(1),
1329
+ children: "\xAB"
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ jsx30(
1333
+ "button",
1334
+ {
1335
+ type: "button",
1336
+ className: "xcv-pagination__control",
1337
+ "aria-label": previousLabel,
1338
+ disabled: page <= 1,
1339
+ onClick: () => onPageChange(page - 1),
1340
+ children: "\u2039"
1341
+ }
1342
+ ),
1343
+ visiblePages.map((number, index) => number === null ? /* @__PURE__ */ jsx30("span", { className: "xcv-pagination__page-slot xcv-pagination__page-slot--empty", "aria-hidden": "true" }, `placeholder-${index}`) : /* @__PURE__ */ jsx30(
1344
+ "button",
1345
+ {
1346
+ type: "button",
1347
+ className: ["xcv-pagination__page-slot", "xcv-pagination__page", number === page && "xcv-pagination__page--current"].filter(Boolean).join(" "),
1348
+ "aria-label": pageLabel(number),
1349
+ "aria-current": number === page ? "page" : void 0,
1350
+ onClick: () => onPageChange(number),
1351
+ children: number
1352
+ },
1353
+ number
1354
+ )),
1355
+ /* @__PURE__ */ jsx30(
1356
+ "button",
1357
+ {
1358
+ type: "button",
1359
+ className: "xcv-pagination__control",
1360
+ "aria-label": nextLabel,
1361
+ disabled: page >= pageCount,
1362
+ onClick: () => onPageChange(page + 1),
1363
+ children: "\u203A"
1364
+ }
1365
+ ),
1366
+ /* @__PURE__ */ jsx30(
1367
+ "button",
1368
+ {
1369
+ type: "button",
1370
+ className: "xcv-pagination__control",
1371
+ "aria-label": lastLabel,
1372
+ disabled: page >= pageCount,
1373
+ onClick: () => onPageChange(pageCount),
1374
+ children: "\xBB"
1375
+ }
1376
+ )
1377
+ ]
1378
+ }
1379
+ );
1380
+ });
1381
+
1382
+ // src/navigation/FilterGroup.tsx
1383
+ import { forwardRef as forwardRef30, useState as useState5 } from "react";
1384
+ import { jsx as jsx31, jsxs as jsxs11 } from "react/jsx-runtime";
1385
+ var XcvFilterGroup = forwardRef30(function XcvFilterGroup2({
1386
+ label,
1387
+ name,
1388
+ options,
1389
+ values,
1390
+ defaultValues = [],
1391
+ onValuesChange,
1392
+ orientation = "vertical",
1393
+ className,
1394
+ ...props
1395
+ }, ref) {
1396
+ const [internalValues, setInternalValues] = useState5([...defaultValues]);
1397
+ const selected = [...new Set(values ?? internalValues)];
1398
+ const optionValues = /* @__PURE__ */ new Set();
1399
+ for (const option of options) {
1400
+ if (optionValues.has(option.value)) throw new Error(`XcvFilterGroup option values must be unique. Duplicate value: "${option.value}".`);
1401
+ optionValues.add(option.value);
1402
+ }
1403
+ function toggle(value, checked) {
1404
+ const option = options.find((item) => item.value === value);
1405
+ if (!option || option.disabled) return;
1406
+ const next = checked ? [...selected, value] : selected.filter((item) => item !== value);
1407
+ if (values === void 0) setInternalValues(next);
1408
+ onValuesChange?.(next);
1409
+ }
1410
+ return /* @__PURE__ */ jsxs11("fieldset", { ref, ...props, className: ["xcv-filter-group", `xcv-filter-group--${orientation}`, className].filter(Boolean).join(" "), children: [
1411
+ /* @__PURE__ */ jsx31("legend", { className: "xcv-filter-group__legend", children: label }),
1412
+ /* @__PURE__ */ jsx31("div", { className: "xcv-filter-group__options", children: options.map((option) => /* @__PURE__ */ jsxs11("label", { className: `xcv-filter-group__option${option.disabled ? " xcv-filter-group__option--disabled" : ""}`, children: [
1413
+ /* @__PURE__ */ jsx31("input", { type: "checkbox", className: "xcv-checkable xcv-checkbox", name, value: option.value, checked: selected.includes(option.value), disabled: option.disabled, onChange: (event) => toggle(option.value, event.currentTarget.checked) }),
1414
+ /* @__PURE__ */ jsx31("span", { children: option.label })
1415
+ ] }, option.value)) })
1416
+ ] });
1417
+ });
1418
+
1419
+ // src/data/StatCard.tsx
1420
+ import { forwardRef as forwardRef31 } from "react";
1421
+ import { jsx as jsx32, jsxs as jsxs12 } from "react/jsx-runtime";
1422
+ var XcvStatCard = forwardRef31(function XcvStatCard2({ label, value, description, detail, detailTone = "neutral", icon, radius = "subtle", className, ...props }, ref) {
1423
+ return /* @__PURE__ */ jsxs12("div", { ref, ...props, className: ["xcv-stat-card", "xcv-card", className].filter(Boolean).join(" "), "data-xcv-radius": radius, children: [
1424
+ /* @__PURE__ */ jsxs12("div", { className: "xcv-stat-card__header", children: [
1425
+ /* @__PURE__ */ jsx32("div", { className: "xcv-stat-card__label", children: label }),
1426
+ icon != null && /* @__PURE__ */ jsx32("span", { className: "xcv-stat-card__icon", "aria-hidden": "true", children: icon })
1427
+ ] }),
1428
+ /* @__PURE__ */ jsx32("div", { className: "xcv-stat-card__value", children: value }),
1429
+ description != null && /* @__PURE__ */ jsx32("div", { className: "xcv-stat-card__description", children: description }),
1430
+ detail != null && /* @__PURE__ */ jsx32("div", { className: ["xcv-stat-card__detail", `xcv-stat-card__detail--${detailTone}`].join(" "), children: detail })
1431
+ ] });
1432
+ });
1433
+
1434
+ // src/data/Table.tsx
1435
+ import { forwardRef as forwardRef32 } from "react";
1436
+ import { jsx as jsx33 } from "react/jsx-runtime";
1437
+ var XcvTableRoot = forwardRef32(function XcvTable({ density = "comfortable", radius = "large", wrapperClassName, wrapperLabel = "Scrollable table", className, children, ...props }, ref) {
1438
+ return /* @__PURE__ */ jsx33("div", { className: ["xcv-table__viewport", wrapperClassName].filter(Boolean).join(" "), "data-xcv-radius": radius, role: "region", "aria-label": wrapperLabel, tabIndex: 0, children: /* @__PURE__ */ jsx33("table", { ...props, ref, className: ["xcv-table", `xcv-table--${density}`, className].filter(Boolean).join(" "), children }) });
1439
+ });
1440
+ function XcvTableCaption({ className, ...props }) {
1441
+ return /* @__PURE__ */ jsx33("caption", { ...props, className: ["xcv-table__caption", className].filter(Boolean).join(" ") });
1442
+ }
1443
+ function XcvTableHead({ className, ...props }) {
1444
+ return /* @__PURE__ */ jsx33("thead", { ...props, className: ["xcv-table__head", className].filter(Boolean).join(" ") });
1445
+ }
1446
+ function XcvTableBody({ className, ...props }) {
1447
+ return /* @__PURE__ */ jsx33("tbody", { ...props, className: ["xcv-table__body", className].filter(Boolean).join(" ") });
1448
+ }
1449
+ function XcvTableRow({ className, ...props }) {
1450
+ return /* @__PURE__ */ jsx33("tr", { ...props, className: ["xcv-table__row", className].filter(Boolean).join(" ") });
1451
+ }
1452
+ function XcvTableHeaderCell({ scope = "col", className, ...props }) {
1453
+ return /* @__PURE__ */ jsx33("th", { ...props, scope, className: ["xcv-table__header-cell", className].filter(Boolean).join(" ") });
1454
+ }
1455
+ function XcvTableCell({ className, ...props }) {
1456
+ return /* @__PURE__ */ jsx33("td", { ...props, className: ["xcv-table__cell", className].filter(Boolean).join(" ") });
1457
+ }
1458
+ var XcvTable2 = Object.assign(XcvTableRoot, {
1459
+ Caption: XcvTableCaption,
1460
+ Head: XcvTableHead,
1461
+ Body: XcvTableBody,
1462
+ Row: XcvTableRow,
1463
+ HeaderCell: XcvTableHeaderCell,
1464
+ Cell: XcvTableCell
1465
+ });
1466
+
1467
+ // src/data/SearchBox.tsx
1468
+ import { forwardRef as forwardRef33, useEffect as useEffect3, useRef as useRef3, useState as useState6 } from "react";
1469
+ import { jsx as jsx34, jsxs as jsxs13 } from "react/jsx-runtime";
1470
+ var XcvSearchBox = forwardRef33(function XcvSearchBox2({ label, radius = "subtle", value, defaultValue = "", onValueChange, onSearch, debounceMs, loading = false, clearable = true, clearLabel = "Clear search", submitLabel = "Search", disabled, className, ...inputProps }, ref) {
1471
+ const [internalValue, setInternalValue] = useState6(defaultValue);
1472
+ const currentValue = value ?? internalValue;
1473
+ const onSearchRef = useRef3(onSearch);
1474
+ const debounceTimer = useRef3(void 0);
1475
+ const previousValue = useRef3(currentValue);
1476
+ onSearchRef.current = onSearch;
1477
+ useEffect3(() => {
1478
+ if (previousValue.current === currentValue) return;
1479
+ previousValue.current = currentValue;
1480
+ if (disabled || loading || debounceMs === void 0 || !Number.isFinite(debounceMs) || debounceMs < 0) return;
1481
+ debounceTimer.current = setTimeout(() => {
1482
+ debounceTimer.current = void 0;
1483
+ onSearchRef.current(currentValue);
1484
+ }, debounceMs);
1485
+ return () => {
1486
+ if (debounceTimer.current !== void 0) clearTimeout(debounceTimer.current);
1487
+ debounceTimer.current = void 0;
1488
+ };
1489
+ }, [currentValue, debounceMs, disabled, loading]);
1490
+ function updateValue(next) {
1491
+ if (value === void 0) setInternalValue(next);
1492
+ onValueChange?.(next);
1493
+ }
1494
+ function submit(event) {
1495
+ event.preventDefault();
1496
+ if (disabled || loading || currentValue.trim().length === 0) return;
1497
+ if (debounceTimer.current !== void 0) clearTimeout(debounceTimer.current);
1498
+ debounceTimer.current = void 0;
1499
+ onSearch(currentValue);
1500
+ }
1501
+ return /* @__PURE__ */ jsxs13("form", { "data-xcv-radius": radius, className: ["xcv-search-box", className].filter(Boolean).join(" "), role: "search", "aria-label": label, "aria-busy": loading || void 0, onSubmit: submit, children: [
1502
+ /* @__PURE__ */ jsxs13("label", { className: "xcv-search-box__label", children: [
1503
+ label,
1504
+ /* @__PURE__ */ jsx34(XcvTextInput, { ...inputProps, ref, type: "search", className: "xcv-search-box__input", inputSize: "md", radius, value: currentValue, disabled: disabled || loading, onChange: (event) => updateValue(event.currentTarget.value) })
1505
+ ] }),
1506
+ clearable && currentValue.length > 0 && /* @__PURE__ */ jsx34(
1507
+ XcvButton,
1508
+ {
1509
+ type: "button",
1510
+ variant: "secondary",
1511
+ size: "md",
1512
+ radius,
1513
+ iconOnly: true,
1514
+ "aria-label": clearLabel,
1515
+ className: "xcv-search-box__clear",
1516
+ disabled: disabled || loading,
1517
+ onClick: () => updateValue(""),
1518
+ children: /* @__PURE__ */ jsx34("span", { "aria-hidden": "true", children: "\xD7" })
1519
+ }
1520
+ ),
1521
+ /* @__PURE__ */ jsx34(XcvButton, { type: "submit", size: "md", radius, loading, disabled: disabled || currentValue.trim().length === 0, children: submitLabel })
1522
+ ] });
1523
+ });
1524
+ export {
1525
+ XcvAccordion,
1526
+ XcvActionsMenu,
1527
+ XcvBadge,
1528
+ XcvBox,
1529
+ XcvButton,
1530
+ XcvCard,
1531
+ XcvCheckbox,
1532
+ XcvContainer,
1533
+ XcvDivider,
1534
+ XcvFilterGroup,
1535
+ XcvFormField,
1536
+ XcvInline,
1537
+ XcvLabel,
1538
+ XcvLink,
1539
+ XcvList,
1540
+ XcvListItem,
1541
+ XcvLoader,
1542
+ XcvMedia,
1543
+ XcvNotice,
1544
+ XcvPagination,
1545
+ XcvRadio,
1546
+ XcvSearchBox,
1547
+ XcvSelect,
1548
+ XcvStack,
1549
+ XcvStatCard,
1550
+ XcvStepper,
1551
+ XcvSurface,
1552
+ XcvTable2 as XcvTable,
1553
+ XcvTabs,
1554
+ XcvText,
1555
+ XcvTextArea,
1556
+ XcvTextInput,
1557
+ XcvTitle,
1558
+ XcvToggle
1559
+ };