@compa11y/react 0.1.0 → 0.1.2

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.cjs CHANGED
@@ -229,191 +229,2801 @@ function useRovingTabindexMap(ids, options = {}) {
229
229
  }
230
230
  };
231
231
  }
232
- var warnings = core.createComponentWarnings("Switch");
233
- var Switch = react.forwardRef(
234
- function Switch2({
232
+ var warnings = core.createComponentWarnings("Select");
233
+ var SelectContext = react.createContext(null);
234
+ function useSelectContext() {
235
+ const context = react.useContext(SelectContext);
236
+ if (!context) {
237
+ throw new Error(
238
+ "Select compound components must be used within a Select component"
239
+ );
240
+ }
241
+ return context;
242
+ }
243
+ function findNextEnabledIndex(options, currentIndex, direction) {
244
+ const length = options.length;
245
+ let index = currentIndex + direction;
246
+ if (index < 0) index = length - 1;
247
+ if (index >= length) index = 0;
248
+ const startIndex = index;
249
+ while (options[index]?.disabled) {
250
+ index += direction;
251
+ if (index < 0) index = length - 1;
252
+ if (index >= length) index = 0;
253
+ if (index === startIndex) return -1;
254
+ }
255
+ return index;
256
+ }
257
+ function findFirstEnabledIndex(options) {
258
+ return options.findIndex((o) => !o.disabled);
259
+ }
260
+ function findLastEnabledIndex(options) {
261
+ for (let i = options.length - 1; i >= 0; i--) {
262
+ if (!options[i]?.disabled) return i;
263
+ }
264
+ return -1;
265
+ }
266
+ function Select({
267
+ options,
268
+ value: controlledValue,
269
+ defaultValue,
270
+ onValueChange,
271
+ disabled = false,
272
+ placeholder = "Select an option...",
273
+ "aria-label": ariaLabel,
274
+ "aria-labelledby": ariaLabelledBy,
275
+ children
276
+ }) {
277
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(
278
+ defaultValue ?? null
279
+ );
280
+ const [isOpen, setIsOpen] = react.useState(false);
281
+ const [highlightedIndex, setHighlightedIndex] = react.useState(-1);
282
+ const triggerRef = react.useRef(null);
283
+ const selectedValue = controlledValue !== void 0 ? controlledValue : uncontrolledValue;
284
+ const triggerId = chunk52J4Z3QD_cjs.useId("select-trigger");
285
+ const listboxId = chunk52J4Z3QD_cjs.useId("select-listbox");
286
+ const baseOptionId = chunk52J4Z3QD_cjs.useId("select-option");
287
+ const setSelectedValue = react.useCallback(
288
+ (value) => {
289
+ if (controlledValue === void 0) {
290
+ setUncontrolledValue(value);
291
+ }
292
+ onValueChange?.(value);
293
+ },
294
+ [controlledValue, onValueChange]
295
+ );
296
+ const getOptionId = react.useCallback(
297
+ (index) => `${baseOptionId}-${index}`,
298
+ [baseOptionId]
299
+ );
300
+ const onSelect = react.useCallback(
301
+ (option) => {
302
+ setSelectedValue(option.value);
303
+ setIsOpen(false);
304
+ setHighlightedIndex(-1);
305
+ triggerRef.current?.focus();
306
+ },
307
+ [setSelectedValue]
308
+ );
309
+ react.useEffect(() => {
310
+ if (!ariaLabel && !ariaLabelledBy) {
311
+ warnings.warning(
312
+ "Select has no accessible label.",
313
+ "Add aria-label or aria-labelledby prop."
314
+ );
315
+ }
316
+ }, [ariaLabel, ariaLabelledBy]);
317
+ const contextValue = {
318
+ selectedValue,
319
+ setSelectedValue,
320
+ isOpen,
321
+ setIsOpen,
322
+ highlightedIndex,
323
+ setHighlightedIndex,
324
+ options,
325
+ triggerId,
326
+ listboxId,
327
+ getOptionId,
328
+ onSelect,
329
+ triggerRef,
330
+ placeholder,
331
+ disabled
332
+ };
333
+ return /* @__PURE__ */ jsxRuntime.jsx(SelectContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-compa11y-select": true, "data-disabled": disabled || void 0, children }) });
334
+ }
335
+ var SelectTrigger = react.forwardRef(
336
+ function SelectTrigger2({ onKeyDown, onClick, onBlur, ...props }, forwardedRef) {
337
+ const {
338
+ selectedValue,
339
+ isOpen,
340
+ setIsOpen,
341
+ highlightedIndex,
342
+ setHighlightedIndex,
343
+ options,
344
+ triggerId,
345
+ listboxId,
346
+ getOptionId,
347
+ onSelect,
348
+ triggerRef,
349
+ placeholder,
350
+ disabled
351
+ } = useSelectContext();
352
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
353
+ const typeAheadProps = chunkGDLOJH6K_cjs.useTypeAhead(
354
+ options.map((o) => o.label),
355
+ (match) => {
356
+ const index = options.findIndex(
357
+ (o) => o.label === match && !o.disabled
358
+ );
359
+ if (index >= 0) {
360
+ if (!isOpen) {
361
+ setIsOpen(true);
362
+ }
363
+ setHighlightedIndex(index);
364
+ }
365
+ },
366
+ { disabled }
367
+ );
368
+ const openAndHighlight = react.useCallback(
369
+ (preferLast = false) => {
370
+ setIsOpen(true);
371
+ const selectedIndex = options.findIndex(
372
+ (o) => o.value === selectedValue
373
+ );
374
+ if (selectedIndex >= 0) {
375
+ setHighlightedIndex(selectedIndex);
376
+ } else {
377
+ setHighlightedIndex(
378
+ preferLast ? findLastEnabledIndex(options) : findFirstEnabledIndex(options)
379
+ );
380
+ }
381
+ },
382
+ [options, selectedValue, setIsOpen, setHighlightedIndex]
383
+ );
384
+ const keyboardProps = chunkGDLOJH6K_cjs.useKeyboard(
385
+ {
386
+ ArrowDown: () => {
387
+ if (!isOpen) {
388
+ openAndHighlight();
389
+ } else {
390
+ const next = findNextEnabledIndex(options, highlightedIndex, 1);
391
+ if (next >= 0) setHighlightedIndex(next);
392
+ }
393
+ },
394
+ ArrowUp: () => {
395
+ if (!isOpen) {
396
+ openAndHighlight(true);
397
+ } else {
398
+ const prev = findNextEnabledIndex(options, highlightedIndex, -1);
399
+ if (prev >= 0) setHighlightedIndex(prev);
400
+ }
401
+ },
402
+ Enter: () => {
403
+ if (isOpen && highlightedIndex >= 0) {
404
+ const option = options[highlightedIndex];
405
+ if (option && !option.disabled) {
406
+ onSelect(option);
407
+ announce2(`${option.label} selected`);
408
+ }
409
+ } else if (!isOpen) {
410
+ openAndHighlight();
411
+ }
412
+ },
413
+ " ": () => {
414
+ if (isOpen && highlightedIndex >= 0) {
415
+ const option = options[highlightedIndex];
416
+ if (option && !option.disabled) {
417
+ onSelect(option);
418
+ announce2(`${option.label} selected`);
419
+ }
420
+ } else if (!isOpen) {
421
+ openAndHighlight();
422
+ }
423
+ },
424
+ Escape: () => {
425
+ if (isOpen) {
426
+ setIsOpen(false);
427
+ setHighlightedIndex(-1);
428
+ }
429
+ },
430
+ Home: () => {
431
+ if (isOpen) {
432
+ setHighlightedIndex(findFirstEnabledIndex(options));
433
+ }
434
+ },
435
+ End: () => {
436
+ if (isOpen) {
437
+ setHighlightedIndex(findLastEnabledIndex(options));
438
+ }
439
+ },
440
+ Tab: () => {
441
+ if (isOpen) {
442
+ setIsOpen(false);
443
+ setHighlightedIndex(-1);
444
+ }
445
+ return false;
446
+ }
447
+ },
448
+ { preventDefault: true, stopPropagation: false }
449
+ );
450
+ const handleKeyDown = (event) => {
451
+ onKeyDown?.(event);
452
+ if (!event.defaultPrevented) {
453
+ keyboardProps.onKeyDown(event);
454
+ }
455
+ if (!event.defaultPrevented) {
456
+ typeAheadProps.onKeyDown(event);
457
+ }
458
+ };
459
+ const handleClick = (event) => {
460
+ onClick?.(event);
461
+ if (!event.defaultPrevented && !disabled) {
462
+ if (isOpen) {
463
+ setIsOpen(false);
464
+ setHighlightedIndex(-1);
465
+ } else {
466
+ openAndHighlight();
467
+ }
468
+ }
469
+ };
470
+ const handleBlur = (event) => {
471
+ onBlur?.(event);
472
+ setTimeout(() => {
473
+ setIsOpen(false);
474
+ setHighlightedIndex(-1);
475
+ }, 150);
476
+ };
477
+ const activeDescendant = isOpen && highlightedIndex >= 0 ? getOptionId(highlightedIndex) : void 0;
478
+ const selectedOption = options.find((o) => o.value === selectedValue);
479
+ const displayText = selectedOption?.label ?? placeholder;
480
+ const setRefs = react.useCallback(
481
+ (node) => {
482
+ triggerRef.current = node;
483
+ if (typeof forwardedRef === "function") {
484
+ forwardedRef(node);
485
+ } else if (forwardedRef) {
486
+ forwardedRef.current = node;
487
+ }
488
+ },
489
+ [forwardedRef, triggerRef]
490
+ );
491
+ return /* @__PURE__ */ jsxRuntime.jsxs(
492
+ "button",
493
+ {
494
+ ref: setRefs,
495
+ id: triggerId,
496
+ type: "button",
497
+ role: "combobox",
498
+ "aria-expanded": isOpen,
499
+ "aria-controls": listboxId,
500
+ "aria-haspopup": "listbox",
501
+ "aria-activedescendant": activeDescendant,
502
+ disabled,
503
+ onKeyDown: handleKeyDown,
504
+ onClick: handleClick,
505
+ onBlur: handleBlur,
506
+ "data-compa11y-select-trigger": true,
507
+ "data-placeholder": !selectedOption || void 0,
508
+ ...props,
509
+ children: [
510
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-compa11y-select-value": true, children: displayText }),
511
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", "data-compa11y-select-chevron": true, children: "\u25BC" })
512
+ ]
513
+ }
514
+ );
515
+ }
516
+ );
517
+ var SelectListbox = react.forwardRef(
518
+ function SelectListbox2({ children, style, ...props }, forwardedRef) {
519
+ const { isOpen, options, listboxId, triggerId } = useSelectContext();
520
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
521
+ const internalRef = react.useRef(null);
522
+ const [position, setPosition] = react.useState("bottom");
523
+ react.useEffect(() => {
524
+ if (isOpen) {
525
+ announce2(
526
+ `${options.length} option${options.length === 1 ? "" : "s"} available`
527
+ );
528
+ }
529
+ }, [isOpen, options.length, announce2]);
530
+ react.useLayoutEffect(() => {
531
+ if (isOpen && internalRef.current) {
532
+ const listbox = internalRef.current;
533
+ const rect = listbox.getBoundingClientRect();
534
+ const viewportHeight = window.innerHeight;
535
+ const spaceBelow = viewportHeight - rect.top;
536
+ const spaceAbove = rect.top;
537
+ const listboxHeight = Math.min(rect.height, 200);
538
+ if (spaceBelow < listboxHeight + 50 && spaceAbove > spaceBelow) {
539
+ setPosition("top");
540
+ } else {
541
+ setPosition("bottom");
542
+ }
543
+ }
544
+ }, [isOpen]);
545
+ const handleMouseDown = (event) => {
546
+ event.preventDefault();
547
+ };
548
+ const setRefs = react.useCallback(
549
+ (node) => {
550
+ internalRef.current = node;
551
+ if (typeof forwardedRef === "function") {
552
+ forwardedRef(node);
553
+ } else if (forwardedRef) {
554
+ forwardedRef.current = node;
555
+ }
556
+ },
557
+ [forwardedRef]
558
+ );
559
+ if (!isOpen) {
560
+ return null;
561
+ }
562
+ const positionStyle = position === "top" ? { bottom: "100%", top: "auto", marginBottom: "4px", marginTop: 0 } : {};
563
+ return /* @__PURE__ */ jsxRuntime.jsx(
564
+ "ul",
565
+ {
566
+ ref: setRefs,
567
+ id: listboxId,
568
+ role: "listbox",
569
+ "aria-labelledby": triggerId,
570
+ style: { ...style, ...positionStyle },
571
+ onMouseDown: handleMouseDown,
572
+ "data-compa11y-select-listbox": true,
573
+ "data-position": position,
574
+ ...props,
575
+ children: children ?? options.map((option, index) => /* @__PURE__ */ jsxRuntime.jsx(
576
+ SelectOptionItem,
577
+ {
578
+ option,
579
+ index
580
+ },
581
+ option.value
582
+ ))
583
+ }
584
+ );
585
+ }
586
+ );
587
+ var SelectOptionItem = react.forwardRef(
588
+ function SelectOptionItem2({ option, index, onClick, onMouseEnter, ...props }, forwardedRef) {
589
+ const {
590
+ selectedValue,
591
+ highlightedIndex,
592
+ setHighlightedIndex,
593
+ getOptionId,
594
+ onSelect
595
+ } = useSelectContext();
596
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
597
+ const internalRef = react.useRef(null);
598
+ const isSelected = selectedValue === option.value;
599
+ const isHighlighted = highlightedIndex === index;
600
+ const optionId = getOptionId(index);
601
+ react.useEffect(() => {
602
+ if (isHighlighted && internalRef.current) {
603
+ internalRef.current.scrollIntoView({
604
+ block: "nearest",
605
+ behavior: "smooth"
606
+ });
607
+ }
608
+ }, [isHighlighted]);
609
+ const handleClick = (event) => {
610
+ onClick?.(event);
611
+ if (!event.defaultPrevented && !option.disabled) {
612
+ onSelect(option);
613
+ announce2(`${option.label} selected`);
614
+ }
615
+ };
616
+ const handleMouseEnter = (event) => {
617
+ onMouseEnter?.(event);
618
+ if (!option.disabled) {
619
+ setHighlightedIndex(index);
620
+ }
621
+ };
622
+ const setRefs = react.useCallback(
623
+ (node) => {
624
+ internalRef.current = node;
625
+ if (typeof forwardedRef === "function") {
626
+ forwardedRef(node);
627
+ } else if (forwardedRef) {
628
+ forwardedRef.current = node;
629
+ }
630
+ },
631
+ [forwardedRef]
632
+ );
633
+ return /* @__PURE__ */ jsxRuntime.jsxs(
634
+ "li",
635
+ {
636
+ ref: setRefs,
637
+ id: optionId,
638
+ role: "option",
639
+ "aria-selected": isSelected,
640
+ "aria-disabled": option.disabled,
641
+ "data-highlighted": isHighlighted,
642
+ "data-selected": isSelected,
643
+ "data-disabled": option.disabled,
644
+ onClick: handleClick,
645
+ onMouseEnter: handleMouseEnter,
646
+ "data-compa11y-select-option": true,
647
+ ...props,
648
+ children: [
649
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-compa11y-select-option-text": true, children: option.label }),
650
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", "data-compa11y-select-check": true, children: "\u2713" })
651
+ ]
652
+ }
653
+ );
654
+ }
655
+ );
656
+ var SelectCompound = Object.assign(Select, {
657
+ Trigger: SelectTrigger,
658
+ Listbox: SelectListbox,
659
+ Option: SelectOptionItem
660
+ });
661
+ var warnings2 = core.createComponentWarnings("Checkbox");
662
+ var CheckmarkIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
663
+ "svg",
664
+ {
665
+ width: "12",
666
+ height: "12",
667
+ viewBox: "0 0 12 12",
668
+ fill: "none",
669
+ "aria-hidden": "true",
670
+ style: { display: "block" },
671
+ children: /* @__PURE__ */ jsxRuntime.jsx(
672
+ "path",
673
+ {
674
+ d: "M2.5 6L5 8.5L9.5 3.5",
675
+ stroke: "currentColor",
676
+ strokeWidth: "2",
677
+ strokeLinecap: "round",
678
+ strokeLinejoin: "round"
679
+ }
680
+ )
681
+ }
682
+ );
683
+ var IndeterminateIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
684
+ "svg",
685
+ {
686
+ width: "12",
687
+ height: "12",
688
+ viewBox: "0 0 12 12",
689
+ fill: "none",
690
+ "aria-hidden": "true",
691
+ style: { display: "block" },
692
+ children: /* @__PURE__ */ jsxRuntime.jsx(
693
+ "path",
694
+ {
695
+ d: "M3 6H9",
696
+ stroke: "currentColor",
697
+ strokeWidth: "2",
698
+ strokeLinecap: "round"
699
+ }
700
+ )
701
+ }
702
+ );
703
+ var CheckboxGroupContext = react.createContext(
704
+ null
705
+ );
706
+ function useCheckboxGroupContext() {
707
+ return react.useContext(CheckboxGroupContext);
708
+ }
709
+ var CheckboxGroup = react.forwardRef(
710
+ function CheckboxGroup2({
711
+ value: controlledValue,
712
+ defaultValue = [],
713
+ onValueChange,
714
+ disabled = false,
715
+ legend,
716
+ error,
717
+ orientation = "vertical",
718
+ name: providedName,
719
+ unstyled = false,
720
+ className = "",
721
+ children,
722
+ "aria-label": ariaLabel,
723
+ "aria-labelledby": ariaLabelledBy
724
+ }, ref) {
725
+ const generatedName = chunk52J4Z3QD_cjs.useId("checkbox-group");
726
+ const name = providedName || generatedName;
727
+ const errorId = chunk52J4Z3QD_cjs.useId("checkbox-group-error");
728
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(defaultValue);
729
+ const isControlled = controlledValue !== void 0;
730
+ const value = isControlled ? controlledValue : uncontrolledValue;
731
+ react.useEffect(() => {
732
+ if (!legend && !ariaLabel && !ariaLabelledBy) {
733
+ warnings2.warning(
734
+ "CheckboxGroup has no accessible label. Screen readers need this to identify the group.",
735
+ 'Add legend="...", aria-label="...", or aria-labelledby="..."'
736
+ );
737
+ }
738
+ }, [legend, ariaLabel, ariaLabelledBy]);
739
+ const handleCheckboxChange = react.useCallback(
740
+ (checkboxValue, checked) => {
741
+ const currentValue = isControlled ? controlledValue : uncontrolledValue;
742
+ const newValue = checked ? [...currentValue, checkboxValue] : currentValue.filter((v) => v !== checkboxValue);
743
+ if (!isControlled) {
744
+ setUncontrolledValue(newValue);
745
+ }
746
+ onValueChange?.(newValue);
747
+ },
748
+ [isControlled, controlledValue, uncontrolledValue, onValueChange]
749
+ );
750
+ const contextValue = {
751
+ name,
752
+ value,
753
+ disabled,
754
+ unstyled,
755
+ onCheckboxChange: handleCheckboxChange
756
+ };
757
+ const hasError = Boolean(error);
758
+ const fieldsetStyle = {
759
+ border: "none",
760
+ margin: 0,
761
+ padding: 0,
762
+ minWidth: 0
763
+ };
764
+ const legendStyle = unstyled ? { padding: 0, marginBottom: "0.5rem" } : {
765
+ padding: 0,
766
+ marginBottom: "0.5rem",
767
+ fontWeight: 600
768
+ };
769
+ const itemsStyle = {
770
+ display: "flex",
771
+ flexDirection: orientation === "horizontal" ? "row" : "column",
772
+ flexWrap: orientation === "horizontal" ? "wrap" : void 0,
773
+ gap: "var(--compa11y-checkbox-group-gap, 0.75rem)"
774
+ };
775
+ const errorStyle = unstyled ? { marginTop: "0.25rem" } : {
776
+ color: "var(--compa11y-checkbox-group-error-color, #ef4444)",
777
+ fontSize: "0.8125rem",
778
+ marginTop: "0.25rem"
779
+ };
780
+ return /* @__PURE__ */ jsxRuntime.jsx(CheckboxGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
781
+ "fieldset",
782
+ {
783
+ ref,
784
+ className,
785
+ "aria-label": ariaLabel,
786
+ "aria-labelledby": ariaLabelledBy,
787
+ "aria-describedby": hasError ? errorId : void 0,
788
+ disabled,
789
+ "data-compa11y-checkbox-group": "",
790
+ "data-orientation": orientation,
791
+ "data-disabled": disabled ? "true" : void 0,
792
+ style: fieldsetStyle,
793
+ children: [
794
+ legend && /* @__PURE__ */ jsxRuntime.jsx(
795
+ "legend",
796
+ {
797
+ "data-compa11y-checkbox-group-legend": "",
798
+ style: legendStyle,
799
+ children: legend
800
+ }
801
+ ),
802
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: itemsStyle, children }),
803
+ hasError && /* @__PURE__ */ jsxRuntime.jsx(
804
+ "div",
805
+ {
806
+ id: errorId,
807
+ role: "alert",
808
+ "data-compa11y-checkbox-group-error": "",
809
+ style: errorStyle,
810
+ children: error
811
+ }
812
+ )
813
+ ]
814
+ }
815
+ ) });
816
+ }
817
+ );
818
+ CheckboxGroup.displayName = "CheckboxGroup";
819
+ var Checkbox = react.forwardRef(
820
+ function Checkbox2({
235
821
  checked: controlledChecked,
236
822
  defaultChecked = false,
237
823
  onCheckedChange,
824
+ indeterminate = false,
825
+ label,
826
+ hint,
827
+ error,
828
+ disabled: localDisabled = false,
829
+ value,
830
+ required = false,
831
+ unstyled: localUnstyled,
832
+ className = "",
833
+ size = "md",
834
+ name: localName,
835
+ "aria-label": ariaLabel,
836
+ "aria-labelledby": ariaLabelledBy,
837
+ "aria-describedby": externalDescribedBy,
838
+ onClick,
839
+ onFocus,
840
+ onBlur,
841
+ ...props
842
+ }, ref) {
843
+ const groupContext = useCheckboxGroupContext();
844
+ const id = chunk52J4Z3QD_cjs.useId("checkbox");
845
+ const fieldId = `${id}-input`;
846
+ const labelId = `${id}-label`;
847
+ const hintId = `${id}-hint`;
848
+ const errorId = `${id}-error`;
849
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
850
+ const { isFocusVisible, focusProps } = useFocusVisible();
851
+ const isInGroup = groupContext !== null;
852
+ const disabled = isInGroup ? groupContext.disabled || localDisabled : localDisabled;
853
+ const unstyled = localUnstyled ?? (isInGroup ? groupContext.unstyled : false);
854
+ const name = isInGroup ? groupContext.name : localName;
855
+ const [uncontrolledChecked, setUncontrolledChecked] = react.useState(defaultChecked);
856
+ const checked = isInGroup ? groupContext.value.includes(value || "") : controlledChecked !== void 0 ? controlledChecked : uncontrolledChecked;
857
+ const inputRef = react.useRef(null);
858
+ const mergedRef = react.useCallback(
859
+ (node) => {
860
+ inputRef.current = node;
861
+ if (typeof ref === "function") {
862
+ ref(node);
863
+ } else if (ref) {
864
+ ref.current = node;
865
+ }
866
+ },
867
+ [ref]
868
+ );
869
+ react.useEffect(() => {
870
+ if (inputRef.current) {
871
+ inputRef.current.indeterminate = indeterminate;
872
+ }
873
+ }, [indeterminate]);
874
+ react.useEffect(() => {
875
+ if (!label && !ariaLabel && !ariaLabelledBy) {
876
+ warnings2.warning(
877
+ "Checkbox has no accessible label. Screen readers need this to identify the checkbox.",
878
+ 'Add label="...", aria-label="...", or aria-labelledby="..."'
879
+ );
880
+ }
881
+ }, [label, ariaLabel, ariaLabelledBy]);
882
+ const handleChange = react.useCallback(
883
+ (event) => {
884
+ const newChecked = event.target.checked;
885
+ if (isInGroup) {
886
+ groupContext.onCheckboxChange(value || "", newChecked);
887
+ } else {
888
+ if (controlledChecked === void 0) {
889
+ setUncontrolledChecked(newChecked);
890
+ }
891
+ onCheckedChange?.(newChecked);
892
+ }
893
+ const labelText = typeof label === "string" ? label : ariaLabel || "Checkbox";
894
+ announce2(`${labelText} ${newChecked ? "checked" : "unchecked"}`);
895
+ },
896
+ [
897
+ isInGroup,
898
+ groupContext,
899
+ value,
900
+ controlledChecked,
901
+ onCheckedChange,
902
+ label,
903
+ ariaLabel,
904
+ announce2
905
+ ]
906
+ );
907
+ const handleFocus = react.useCallback(
908
+ (event) => {
909
+ focusProps.onFocus();
910
+ onFocus?.(event);
911
+ },
912
+ [focusProps, onFocus]
913
+ );
914
+ const handleBlur = react.useCallback(
915
+ (event) => {
916
+ focusProps.onBlur();
917
+ onBlur?.(event);
918
+ },
919
+ [focusProps, onBlur]
920
+ );
921
+ const hasLabel = Boolean(label);
922
+ const hasHint = Boolean(hint);
923
+ const hasError = Boolean(error);
924
+ const describedByParts = [];
925
+ if (externalDescribedBy) describedByParts.push(externalDescribedBy);
926
+ if (hasHint) describedByParts.push(hintId);
927
+ if (hasError) describedByParts.push(errorId);
928
+ const describedBy = describedByParts.length ? describedByParts.join(" ") : void 0;
929
+ const sizes = {
930
+ sm: { box: 16 },
931
+ md: { box: 20 },
932
+ lg: { box: 24 }
933
+ };
934
+ const sizeConfig = sizes[size];
935
+ const wrapperStyle = {
936
+ display: "inline-flex",
937
+ alignItems: "flex-start",
938
+ gap: "0.5rem",
939
+ cursor: disabled ? "not-allowed" : "pointer",
940
+ userSelect: "none",
941
+ ...disabled && !unstyled ? { opacity: 0.5 } : {}
942
+ };
943
+ const controlStyle = {
944
+ position: "relative",
945
+ display: "inline-flex",
946
+ alignItems: "center",
947
+ justifyContent: "center",
948
+ flexShrink: 0
949
+ };
950
+ const hiddenInputStyle = {
951
+ position: "absolute",
952
+ opacity: 0,
953
+ width: "100%",
954
+ height: "100%",
955
+ margin: 0,
956
+ cursor: "inherit",
957
+ zIndex: 1
958
+ };
959
+ const indicatorStyle = unstyled ? {} : {
960
+ width: sizeConfig.box,
961
+ height: sizeConfig.box,
962
+ minWidth: 24,
963
+ minHeight: 24,
964
+ border: checked || indeterminate ? "var(--compa11y-checkbox-checked-border, 2px solid #0066cc)" : "var(--compa11y-checkbox-border, 2px solid #666)",
965
+ borderRadius: "var(--compa11y-checkbox-radius, 3px)",
966
+ background: checked || indeterminate ? "var(--compa11y-checkbox-checked-bg, #0066cc)" : "var(--compa11y-checkbox-bg, white)",
967
+ display: "flex",
968
+ alignItems: "center",
969
+ justifyContent: "center",
970
+ flexShrink: 0,
971
+ transition: "all 0.15s ease",
972
+ pointerEvents: "none",
973
+ color: "var(--compa11y-checkbox-check-color, white)",
974
+ ...isFocusVisible ? {
975
+ outline: "2px solid var(--compa11y-focus-color, #0066cc)",
976
+ outlineOffset: "2px"
977
+ } : {}
978
+ };
979
+ const contentStyle = {
980
+ display: "flex",
981
+ flexDirection: "column",
982
+ gap: "0.125rem",
983
+ paddingTop: "0.125rem"
984
+ };
985
+ const labelStyle = unstyled ? { cursor: "inherit" } : {
986
+ cursor: "inherit",
987
+ color: "var(--compa11y-checkbox-label-color, inherit)"
988
+ };
989
+ const hintStyle = unstyled ? {} : {
990
+ color: "var(--compa11y-checkbox-hint-color, #666)",
991
+ fontSize: "0.8125rem"
992
+ };
993
+ const errorStyles = unstyled ? {} : {
994
+ color: "var(--compa11y-checkbox-error-color, #ef4444)",
995
+ fontSize: "0.8125rem"
996
+ };
997
+ return /* @__PURE__ */ jsxRuntime.jsxs(
998
+ "div",
999
+ {
1000
+ className,
1001
+ "data-compa11y-checkbox": "",
1002
+ "data-checked": checked ? "true" : "false",
1003
+ "data-indeterminate": indeterminate ? "true" : void 0,
1004
+ "data-disabled": disabled ? "true" : void 0,
1005
+ "data-size": size,
1006
+ style: wrapperStyle,
1007
+ children: [
1008
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: controlStyle, children: [
1009
+ /* @__PURE__ */ jsxRuntime.jsx(
1010
+ "input",
1011
+ {
1012
+ ref: mergedRef,
1013
+ type: "checkbox",
1014
+ id: fieldId,
1015
+ name,
1016
+ value,
1017
+ checked,
1018
+ onChange: handleChange,
1019
+ onClick,
1020
+ onFocus: handleFocus,
1021
+ onBlur: handleBlur,
1022
+ disabled,
1023
+ required,
1024
+ "aria-required": required || void 0,
1025
+ "aria-invalid": hasError || void 0,
1026
+ "aria-label": !hasLabel && !ariaLabelledBy ? ariaLabel : void 0,
1027
+ "aria-labelledby": ariaLabelledBy || (hasLabel ? labelId : void 0),
1028
+ "aria-describedby": describedBy,
1029
+ "aria-checked": indeterminate ? "mixed" : checked,
1030
+ tabIndex: disabled ? -1 : 0,
1031
+ style: hiddenInputStyle,
1032
+ ...props
1033
+ }
1034
+ ),
1035
+ !unstyled && /* @__PURE__ */ jsxRuntime.jsxs(
1036
+ "div",
1037
+ {
1038
+ "data-compa11y-checkbox-indicator": "",
1039
+ "aria-hidden": "true",
1040
+ style: indicatorStyle,
1041
+ children: [
1042
+ checked && !indeterminate && /* @__PURE__ */ jsxRuntime.jsx(CheckmarkIcon, {}),
1043
+ indeterminate && /* @__PURE__ */ jsxRuntime.jsx(IndeterminateIcon, {})
1044
+ ]
1045
+ }
1046
+ )
1047
+ ] }),
1048
+ (hasLabel || hasHint || hasError) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle, children: [
1049
+ hasLabel && /* @__PURE__ */ jsxRuntime.jsxs(
1050
+ "label",
1051
+ {
1052
+ htmlFor: fieldId,
1053
+ id: labelId,
1054
+ "data-compa11y-checkbox-label": "",
1055
+ style: labelStyle,
1056
+ children: [
1057
+ label,
1058
+ required && /* @__PURE__ */ jsxRuntime.jsx(
1059
+ "span",
1060
+ {
1061
+ "aria-hidden": "true",
1062
+ style: {
1063
+ color: "var(--compa11y-checkbox-required-color, #ef4444)",
1064
+ marginLeft: "0.125rem"
1065
+ },
1066
+ children: "*"
1067
+ }
1068
+ )
1069
+ ]
1070
+ }
1071
+ ),
1072
+ hasHint && /* @__PURE__ */ jsxRuntime.jsx(
1073
+ "div",
1074
+ {
1075
+ id: hintId,
1076
+ "data-compa11y-checkbox-hint": "",
1077
+ style: hintStyle,
1078
+ children: hint
1079
+ }
1080
+ ),
1081
+ hasError && /* @__PURE__ */ jsxRuntime.jsx(
1082
+ "div",
1083
+ {
1084
+ id: errorId,
1085
+ role: "alert",
1086
+ "data-compa11y-checkbox-error": "",
1087
+ style: errorStyles,
1088
+ children: error
1089
+ }
1090
+ )
1091
+ ] })
1092
+ ]
1093
+ }
1094
+ );
1095
+ }
1096
+ );
1097
+ Checkbox.displayName = "Checkbox";
1098
+ var CheckboxIndicator = react.forwardRef(function CheckboxIndicator2({ children, className }, ref) {
1099
+ return /* @__PURE__ */ jsxRuntime.jsx(
1100
+ "div",
1101
+ {
1102
+ ref,
1103
+ className,
1104
+ "data-compa11y-checkbox-indicator": "",
1105
+ "aria-hidden": "true",
1106
+ children
1107
+ }
1108
+ );
1109
+ });
1110
+ CheckboxIndicator.displayName = "CheckboxIndicator";
1111
+ var CheckboxCompound = Object.assign(Checkbox, {
1112
+ Group: CheckboxGroup,
1113
+ Indicator: CheckboxIndicator
1114
+ });
1115
+ var warnings3 = core.createComponentWarnings("RadioGroup");
1116
+ var RadioGroupContext = react.createContext(null);
1117
+ function useRadioGroupContext() {
1118
+ const context = react.useContext(RadioGroupContext);
1119
+ if (!context) {
1120
+ throw new Error(
1121
+ "[Compa11y RadioGroup]: Radio must be used within a RadioGroup."
1122
+ );
1123
+ }
1124
+ return context;
1125
+ }
1126
+ var RadioGroup = react.forwardRef(
1127
+ function RadioGroup2({
1128
+ value: controlledValue,
1129
+ defaultValue,
1130
+ onValueChange,
1131
+ disabled = false,
1132
+ discoverable = true,
1133
+ required = false,
1134
+ orientation = "vertical",
1135
+ name: providedName,
1136
+ legend,
1137
+ hint,
1138
+ error,
1139
+ unstyled = false,
1140
+ className = "",
1141
+ children,
1142
+ "aria-label": ariaLabel,
1143
+ "aria-labelledby": ariaLabelledBy
1144
+ }, ref) {
1145
+ const generatedName = chunk52J4Z3QD_cjs.useId("radiogroup");
1146
+ const name = providedName || generatedName;
1147
+ const hintId = chunk52J4Z3QD_cjs.useId("radiogroup-hint");
1148
+ const errorId = chunk52J4Z3QD_cjs.useId("radiogroup-error");
1149
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(
1150
+ defaultValue ?? null
1151
+ );
1152
+ const isControlled = controlledValue !== void 0;
1153
+ const value = isControlled ? controlledValue : uncontrolledValue;
1154
+ const radiosRef = react.useRef(/* @__PURE__ */ new Map());
1155
+ react.useEffect(() => {
1156
+ if (!legend && !ariaLabel && !ariaLabelledBy) {
1157
+ warnings3.warning(
1158
+ "RadioGroup has no accessible label. Screen readers need this to identify the group.",
1159
+ 'Add legend="...", aria-label="...", or aria-labelledby="..."'
1160
+ );
1161
+ }
1162
+ }, [legend, ariaLabel, ariaLabelledBy]);
1163
+ const handleValueChange = react.useCallback(
1164
+ (newValue) => {
1165
+ if (disabled) return;
1166
+ if (!isControlled) {
1167
+ setUncontrolledValue(newValue);
1168
+ }
1169
+ onValueChange?.(newValue);
1170
+ },
1171
+ [disabled, isControlled, onValueChange]
1172
+ );
1173
+ const registerRadio = react.useCallback(
1174
+ (radioValue, radioDisabled) => {
1175
+ radiosRef.current.set(radioValue, radioDisabled);
1176
+ },
1177
+ []
1178
+ );
1179
+ const unregisterRadio = react.useCallback((radioValue) => {
1180
+ radiosRef.current.delete(radioValue);
1181
+ }, []);
1182
+ const updateRadioDisabled = react.useCallback(
1183
+ (radioValue, radioDisabled) => {
1184
+ radiosRef.current.set(radioValue, radioDisabled);
1185
+ },
1186
+ []
1187
+ );
1188
+ const getEnabledRadioValues = react.useCallback(() => {
1189
+ const values = [];
1190
+ radiosRef.current.forEach((isDisabled, radioValue) => {
1191
+ if (!isDisabled && !disabled) {
1192
+ values.push(radioValue);
1193
+ }
1194
+ });
1195
+ return values;
1196
+ }, [disabled]);
1197
+ const internalRef = react.useRef(null);
1198
+ const groupRef = ref || internalRef;
1199
+ const navigateWithRef = react.useCallback(
1200
+ (direction) => {
1201
+ const enabledValues = getEnabledRadioValues();
1202
+ if (enabledValues.length === 0) return;
1203
+ const currentIndex = value ? enabledValues.indexOf(value) : -1;
1204
+ let nextIndex;
1205
+ switch (direction) {
1206
+ case "next":
1207
+ nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % enabledValues.length;
1208
+ break;
1209
+ case "prev":
1210
+ nextIndex = currentIndex < 0 ? enabledValues.length - 1 : (currentIndex - 1 + enabledValues.length) % enabledValues.length;
1211
+ break;
1212
+ case "first":
1213
+ nextIndex = 0;
1214
+ break;
1215
+ case "last":
1216
+ nextIndex = enabledValues.length - 1;
1217
+ break;
1218
+ }
1219
+ const nextValue = enabledValues[nextIndex];
1220
+ if (nextValue !== void 0) {
1221
+ handleValueChange(nextValue);
1222
+ const groupEl = typeof groupRef === "object" && groupRef ? groupRef.current : null;
1223
+ if (groupEl) {
1224
+ const input = groupEl.querySelector(
1225
+ `input[type="radio"][value="${CSS.escape(nextValue)}"]`
1226
+ );
1227
+ input?.focus();
1228
+ }
1229
+ }
1230
+ },
1231
+ [getEnabledRadioValues, value, handleValueChange, groupRef]
1232
+ );
1233
+ const keyboardProps = chunkGDLOJH6K_cjs.useKeyboard(
1234
+ {
1235
+ ArrowDown: () => navigateWithRef("next"),
1236
+ ArrowRight: () => navigateWithRef("next"),
1237
+ ArrowUp: () => navigateWithRef("prev"),
1238
+ ArrowLeft: () => navigateWithRef("prev"),
1239
+ Home: () => navigateWithRef("first"),
1240
+ End: () => navigateWithRef("last")
1241
+ },
1242
+ { preventDefault: true, stopPropagation: true, disabled }
1243
+ );
1244
+ const contextValue = {
1245
+ name,
1246
+ value: value ?? null,
1247
+ disabled,
1248
+ discoverable,
1249
+ required,
1250
+ unstyled,
1251
+ orientation,
1252
+ onValueChange: handleValueChange,
1253
+ registerRadio,
1254
+ unregisterRadio,
1255
+ updateRadioDisabled
1256
+ };
1257
+ const describedByParts = [];
1258
+ if (hint) describedByParts.push(hintId);
1259
+ if (error) describedByParts.push(errorId);
1260
+ const ariaDescribedBy = describedByParts.length > 0 ? describedByParts.join(" ") : void 0;
1261
+ const groupContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1262
+ /* @__PURE__ */ jsxRuntime.jsx(
1263
+ "div",
1264
+ {
1265
+ style: unstyled ? {
1266
+ display: "flex",
1267
+ flexDirection: orientation === "horizontal" ? "row" : "column"
1268
+ } : {
1269
+ display: "flex",
1270
+ flexDirection: orientation === "horizontal" ? "row" : "column",
1271
+ gap: "var(--compa11y-radio-gap, 0.75rem)"
1272
+ },
1273
+ children
1274
+ }
1275
+ ),
1276
+ hint && /* @__PURE__ */ jsxRuntime.jsx(
1277
+ "div",
1278
+ {
1279
+ id: hintId,
1280
+ style: unstyled ? {} : {
1281
+ color: "var(--compa11y-radio-group-hint-color, #666)",
1282
+ fontSize: "0.8125rem",
1283
+ marginTop: "0.25rem"
1284
+ },
1285
+ children: hint
1286
+ }
1287
+ ),
1288
+ error && /* @__PURE__ */ jsxRuntime.jsx(
1289
+ "div",
1290
+ {
1291
+ id: errorId,
1292
+ role: "alert",
1293
+ style: unstyled ? {} : {
1294
+ color: "var(--compa11y-radio-group-error-color, #ef4444)",
1295
+ fontSize: "0.8125rem",
1296
+ marginTop: "0.25rem"
1297
+ },
1298
+ children: error
1299
+ }
1300
+ )
1301
+ ] });
1302
+ if (legend) {
1303
+ return /* @__PURE__ */ jsxRuntime.jsx(RadioGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
1304
+ "fieldset",
1305
+ {
1306
+ ref: groupRef,
1307
+ role: "radiogroup",
1308
+ "aria-label": ariaLabel,
1309
+ "aria-labelledby": ariaLabelledBy,
1310
+ "aria-orientation": orientation,
1311
+ "aria-required": required || void 0,
1312
+ "aria-disabled": disabled || void 0,
1313
+ "aria-invalid": error ? true : void 0,
1314
+ "aria-describedby": ariaDescribedBy,
1315
+ className: `compa11y-radiogroup ${className}`.trim(),
1316
+ "data-compa11y-radiogroup": "",
1317
+ "data-orientation": orientation,
1318
+ "data-disabled": disabled ? "true" : void 0,
1319
+ "data-error": error ? "true" : void 0,
1320
+ onKeyDown: keyboardProps.onKeyDown,
1321
+ style: unstyled ? { border: "none", margin: 0, padding: 0, minWidth: 0 } : {
1322
+ border: "none",
1323
+ margin: 0,
1324
+ padding: 0,
1325
+ minWidth: 0
1326
+ },
1327
+ children: [
1328
+ /* @__PURE__ */ jsxRuntime.jsxs(
1329
+ "legend",
1330
+ {
1331
+ style: unstyled ? {} : {
1332
+ padding: 0,
1333
+ marginBottom: "var(--compa11y-radio-group-legend-gap, 0.5rem)",
1334
+ fontWeight: "var(--compa11y-radio-group-legend-weight, 600)",
1335
+ color: "var(--compa11y-radio-group-legend-color, inherit)",
1336
+ fontSize: "var(--compa11y-radio-group-legend-size, 1rem)"
1337
+ },
1338
+ children: [
1339
+ legend,
1340
+ required && !unstyled && /* @__PURE__ */ jsxRuntime.jsxs(
1341
+ "span",
1342
+ {
1343
+ "aria-hidden": "true",
1344
+ style: {
1345
+ color: "var(--compa11y-radio-group-required-color, #ef4444)",
1346
+ marginLeft: "0.125rem"
1347
+ },
1348
+ children: [
1349
+ " ",
1350
+ "*"
1351
+ ]
1352
+ }
1353
+ )
1354
+ ]
1355
+ }
1356
+ ),
1357
+ groupContent
1358
+ ]
1359
+ }
1360
+ ) });
1361
+ }
1362
+ return /* @__PURE__ */ jsxRuntime.jsx(RadioGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
1363
+ "div",
1364
+ {
1365
+ ref: groupRef,
1366
+ role: "radiogroup",
1367
+ "aria-label": ariaLabel,
1368
+ "aria-labelledby": ariaLabelledBy,
1369
+ "aria-orientation": orientation,
1370
+ "aria-required": required || void 0,
1371
+ "aria-disabled": disabled || void 0,
1372
+ "aria-invalid": error ? true : void 0,
1373
+ "aria-describedby": ariaDescribedBy,
1374
+ className: `compa11y-radiogroup ${className}`.trim(),
1375
+ "data-compa11y-radiogroup": "",
1376
+ "data-orientation": orientation,
1377
+ "data-disabled": disabled ? "true" : void 0,
1378
+ "data-error": error ? "true" : void 0,
1379
+ onKeyDown: keyboardProps.onKeyDown,
1380
+ children: groupContent
1381
+ }
1382
+ ) });
1383
+ }
1384
+ );
1385
+ RadioGroup.displayName = "RadioGroup";
1386
+ var Radio = react.forwardRef(
1387
+ function Radio2({
1388
+ value,
1389
+ disabled: localDisabled = false,
1390
+ discoverable: localDiscoverable,
238
1391
  label,
1392
+ hint,
1393
+ unstyled: localUnstyled,
1394
+ className = "",
1395
+ children,
1396
+ "aria-label": ariaLabel
1397
+ }, ref) {
1398
+ const context = useRadioGroupContext();
1399
+ const generatedId = chunk52J4Z3QD_cjs.useId("radio");
1400
+ const hintId = chunk52J4Z3QD_cjs.useId("radio-hint");
1401
+ const disabled = context.disabled || localDisabled;
1402
+ const discoverable = localDiscoverable ?? context.discoverable;
1403
+ const unstyled = localUnstyled ?? context.unstyled;
1404
+ const checked = context.value === value;
1405
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
1406
+ const { isFocusVisible, focusProps } = useFocusVisible();
1407
+ const inputRef = react.useRef(null);
1408
+ const mergedRef = react.useCallback(
1409
+ (node) => {
1410
+ inputRef.current = node;
1411
+ if (typeof ref === "function") {
1412
+ ref(node);
1413
+ } else if (ref) {
1414
+ ref.current = node;
1415
+ }
1416
+ },
1417
+ [ref]
1418
+ );
1419
+ react.useEffect(() => {
1420
+ context.registerRadio(value, disabled);
1421
+ return () => context.unregisterRadio(value);
1422
+ }, [value]);
1423
+ react.useEffect(() => {
1424
+ context.updateRadioDisabled(value, disabled);
1425
+ }, [disabled, value]);
1426
+ const handleChange = react.useCallback(
1427
+ (event) => {
1428
+ if (disabled) {
1429
+ event.preventDefault();
1430
+ return;
1431
+ }
1432
+ context.onValueChange(value);
1433
+ const labelText = label || (typeof children === "string" ? children : null) || value;
1434
+ announce2(`${labelText} selected`, { politeness: "polite" });
1435
+ },
1436
+ [disabled, context, value, label, children, announce2]
1437
+ );
1438
+ const handleClick = react.useCallback(
1439
+ (event) => {
1440
+ if (disabled) {
1441
+ event.preventDefault();
1442
+ }
1443
+ },
1444
+ [disabled]
1445
+ );
1446
+ const handleFocus = react.useCallback(
1447
+ (_event) => {
1448
+ focusProps.onFocus();
1449
+ if (!checked && !disabled) {
1450
+ context.onValueChange(value);
1451
+ }
1452
+ },
1453
+ [focusProps, checked, disabled, context, value]
1454
+ );
1455
+ const handleBlur = react.useCallback(() => {
1456
+ focusProps.onBlur();
1457
+ }, [focusProps]);
1458
+ const hasLabel = Boolean(children || label);
1459
+ const labelContent = children || label;
1460
+ const labelId = `${generatedId}-label`;
1461
+ const hasHint = Boolean(hint);
1462
+ const isFirstEnabled = (() => {
1463
+ if (context.value !== null) return false;
1464
+ const entries = Array.from(
1465
+ inputRef.current?.closest('[role="radiogroup"]')?.querySelectorAll('input[type="radio"]') ?? []
1466
+ );
1467
+ for (const entry of entries) {
1468
+ const input = entry;
1469
+ if (!input.disabled && input.getAttribute("aria-disabled") !== "true") {
1470
+ return input.value === value;
1471
+ }
1472
+ }
1473
+ return false;
1474
+ })();
1475
+ const tabIndex = (() => {
1476
+ if (disabled && !discoverable) return -1;
1477
+ if (checked) return 0;
1478
+ if (context.value === null && isFirstEnabled) return 0;
1479
+ return -1;
1480
+ })();
1481
+ const useNativeDisabled = disabled && !discoverable;
1482
+ const ariaDescribedBy = hasHint ? hintId : void 0;
1483
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1484
+ "label",
1485
+ {
1486
+ htmlFor: generatedId,
1487
+ className: `compa11y-radio-wrapper ${className}`.trim(),
1488
+ "data-compa11y-radio": "",
1489
+ "data-value": value,
1490
+ "data-checked": checked ? "true" : "false",
1491
+ "data-disabled": disabled ? "true" : void 0,
1492
+ style: unstyled ? {
1493
+ display: "inline-flex",
1494
+ alignItems: "flex-start",
1495
+ cursor: disabled ? "not-allowed" : "pointer",
1496
+ gap: "0.5rem"
1497
+ } : {
1498
+ display: "inline-flex",
1499
+ alignItems: "flex-start",
1500
+ gap: "var(--compa11y-radio-gap, 0.5rem)",
1501
+ cursor: disabled ? "not-allowed" : "pointer",
1502
+ userSelect: "none",
1503
+ opacity: disabled ? 0.5 : 1
1504
+ },
1505
+ children: [
1506
+ /* @__PURE__ */ jsxRuntime.jsxs(
1507
+ "div",
1508
+ {
1509
+ style: {
1510
+ position: "relative",
1511
+ display: "inline-flex",
1512
+ alignItems: "center",
1513
+ justifyContent: "center",
1514
+ flexShrink: 0
1515
+ },
1516
+ children: [
1517
+ /* @__PURE__ */ jsxRuntime.jsx(
1518
+ "input",
1519
+ {
1520
+ ref: mergedRef,
1521
+ type: "radio",
1522
+ id: generatedId,
1523
+ name: context.name,
1524
+ value,
1525
+ checked,
1526
+ onChange: handleChange,
1527
+ onClick: handleClick,
1528
+ onFocus: handleFocus,
1529
+ onBlur: handleBlur,
1530
+ disabled: useNativeDisabled,
1531
+ tabIndex,
1532
+ "aria-disabled": disabled ? "true" : void 0,
1533
+ "aria-label": !hasLabel ? ariaLabel : void 0,
1534
+ "aria-labelledby": hasLabel ? labelId : void 0,
1535
+ "aria-describedby": ariaDescribedBy,
1536
+ required: context.required,
1537
+ className: "compa11y-radio-input",
1538
+ style: {
1539
+ position: "absolute",
1540
+ opacity: 0,
1541
+ width: "var(--compa11y-radio-size, 1.25rem)",
1542
+ height: "var(--compa11y-radio-size, 1.25rem)",
1543
+ margin: 0,
1544
+ cursor: disabled ? "not-allowed" : "pointer"
1545
+ }
1546
+ }
1547
+ ),
1548
+ !unstyled && /* @__PURE__ */ jsxRuntime.jsx(
1549
+ "div",
1550
+ {
1551
+ className: "compa11y-radio-circle",
1552
+ style: {
1553
+ width: "var(--compa11y-radio-size, 1.25rem)",
1554
+ height: "var(--compa11y-radio-size, 1.25rem)",
1555
+ minWidth: "24px",
1556
+ minHeight: "24px",
1557
+ border: checked ? "var(--compa11y-radio-checked-border, 2px solid #0066cc)" : "var(--compa11y-radio-border, 2px solid #666)",
1558
+ borderRadius: "50%",
1559
+ background: checked ? "var(--compa11y-radio-checked-bg, #0066cc)" : "var(--compa11y-radio-bg, white)",
1560
+ display: "flex",
1561
+ alignItems: "center",
1562
+ justifyContent: "center",
1563
+ flexShrink: 0,
1564
+ transition: "all 0.15s ease",
1565
+ pointerEvents: "none",
1566
+ ...isFocusVisible ? {
1567
+ outline: "2px solid var(--compa11y-focus-color, #0066cc)",
1568
+ outlineOffset: "2px"
1569
+ } : {}
1570
+ },
1571
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1572
+ "div",
1573
+ {
1574
+ "aria-hidden": "true",
1575
+ style: {
1576
+ width: "var(--compa11y-radio-dot-size, 0.5rem)",
1577
+ height: "var(--compa11y-radio-dot-size, 0.5rem)",
1578
+ borderRadius: "50%",
1579
+ background: "var(--compa11y-radio-dot-color, white)",
1580
+ opacity: checked ? 1 : 0,
1581
+ transform: checked ? "scale(1)" : "scale(0)",
1582
+ transition: "all 0.15s ease"
1583
+ }
1584
+ }
1585
+ )
1586
+ }
1587
+ )
1588
+ ]
1589
+ }
1590
+ ),
1591
+ (hasLabel || hasHint) && /* @__PURE__ */ jsxRuntime.jsxs(
1592
+ "div",
1593
+ {
1594
+ style: unstyled ? {} : {
1595
+ display: "flex",
1596
+ flexDirection: "column",
1597
+ gap: "0.125rem",
1598
+ paddingTop: "0.125rem"
1599
+ },
1600
+ children: [
1601
+ hasLabel && /* @__PURE__ */ jsxRuntime.jsx(
1602
+ "span",
1603
+ {
1604
+ id: labelId,
1605
+ className: "compa11y-radio-label",
1606
+ style: unstyled ? {} : {
1607
+ color: "var(--compa11y-radio-label-color, inherit)",
1608
+ fontSize: "var(--compa11y-radio-label-size, 1rem)"
1609
+ },
1610
+ children: labelContent
1611
+ }
1612
+ ),
1613
+ hasHint && /* @__PURE__ */ jsxRuntime.jsx(
1614
+ "span",
1615
+ {
1616
+ id: hintId,
1617
+ className: "compa11y-radio-hint",
1618
+ style: unstyled ? {} : {
1619
+ color: "var(--compa11y-radio-hint-color, #666)",
1620
+ fontSize: "var(--compa11y-radio-hint-size, 0.8125rem)"
1621
+ },
1622
+ children: hint
1623
+ }
1624
+ )
1625
+ ]
1626
+ }
1627
+ )
1628
+ ]
1629
+ }
1630
+ );
1631
+ }
1632
+ );
1633
+ Radio.displayName = "Radio";
1634
+ var RadioGroupCompound = Object.assign(RadioGroup, { Radio });
1635
+ var warnings4 = core.createComponentWarnings("Switch");
1636
+ var Switch = react.forwardRef(
1637
+ function Switch2({
1638
+ checked: controlledChecked,
1639
+ defaultChecked = false,
1640
+ onCheckedChange,
1641
+ label,
1642
+ disabled = false,
1643
+ unstyled = false,
1644
+ className,
1645
+ size = "md",
1646
+ "aria-label": ariaLabel,
1647
+ "aria-labelledby": ariaLabelledby,
1648
+ onClick,
1649
+ ...props
1650
+ }, ref) {
1651
+ const id = chunk52J4Z3QD_cjs.useId("switch");
1652
+ const labelId = `${id}-label`;
1653
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
1654
+ const [uncontrolledChecked, setUncontrolledChecked] = react.useState(defaultChecked);
1655
+ const isControlled = controlledChecked !== void 0;
1656
+ const checked = isControlled ? controlledChecked : uncontrolledChecked;
1657
+ react.useEffect(() => {
1658
+ if (!label && !ariaLabel && !ariaLabelledby) {
1659
+ warnings4.warning(
1660
+ "Switch has no accessible label. Screen readers need this to identify the switch.",
1661
+ 'Add label="Description", aria-label="...", or aria-labelledby="..."'
1662
+ );
1663
+ }
1664
+ }, [label, ariaLabel, ariaLabelledby]);
1665
+ const toggleSwitch = react.useCallback(() => {
1666
+ if (disabled) return;
1667
+ const newChecked = !checked;
1668
+ if (!isControlled) {
1669
+ setUncontrolledChecked(newChecked);
1670
+ }
1671
+ onCheckedChange?.(newChecked);
1672
+ const labelText = label || ariaLabel || "Switch";
1673
+ announce2(`${labelText} ${newChecked ? "on" : "off"}`);
1674
+ }, [
1675
+ checked,
1676
+ disabled,
1677
+ isControlled,
1678
+ onCheckedChange,
1679
+ label,
1680
+ ariaLabel,
1681
+ announce2
1682
+ ]);
1683
+ const handleClick = react.useCallback(
1684
+ (event) => {
1685
+ onClick?.(event);
1686
+ if (!event.defaultPrevented) {
1687
+ toggleSwitch();
1688
+ }
1689
+ },
1690
+ [onClick, toggleSwitch]
1691
+ );
1692
+ const keyboardProps = chunkGDLOJH6K_cjs.useKeyboard(
1693
+ {
1694
+ " ": () => {
1695
+ toggleSwitch();
1696
+ },
1697
+ Enter: () => {
1698
+ toggleSwitch();
1699
+ }
1700
+ },
1701
+ { preventDefault: true }
1702
+ );
1703
+ const handleKeyDown = (event) => {
1704
+ props.onKeyDown?.(event);
1705
+ if (!event.defaultPrevented) {
1706
+ keyboardProps.onKeyDown(event);
1707
+ }
1708
+ };
1709
+ const computedAriaLabel = ariaLabel;
1710
+ const computedAriaLabelledby = ariaLabelledby || (label ? labelId : void 0);
1711
+ const wrapperStructuralStyles = {
1712
+ display: "inline-flex",
1713
+ alignItems: "center"
1714
+ };
1715
+ const sizes = {
1716
+ sm: { width: 32, height: 18, thumb: 14, translate: 14 },
1717
+ md: { width: 44, height: 24, thumb: 20, translate: 20 },
1718
+ lg: { width: 56, height: 30, thumb: 26, translate: 26 }
1719
+ };
1720
+ const sizeConfig = sizes[size];
1721
+ const trackStructuralStyles = {
1722
+ position: "relative",
1723
+ display: "inline-flex",
1724
+ alignItems: "center",
1725
+ flexShrink: 0,
1726
+ width: sizeConfig.width,
1727
+ height: sizeConfig.height,
1728
+ border: "none",
1729
+ padding: 2,
1730
+ cursor: disabled ? "not-allowed" : "pointer"
1731
+ };
1732
+ const trackVisualStyles = unstyled ? {} : {
1733
+ backgroundColor: checked ? "#0066cc" : "#d1d5db",
1734
+ borderRadius: sizeConfig.height / 2,
1735
+ transition: "background-color 0.2s ease",
1736
+ opacity: disabled ? 0.5 : 1
1737
+ };
1738
+ const thumbStructuralStyles = {
1739
+ position: "absolute",
1740
+ left: 2,
1741
+ width: sizeConfig.thumb,
1742
+ height: sizeConfig.thumb,
1743
+ pointerEvents: "none",
1744
+ transform: checked ? `translateX(${sizeConfig.translate}px)` : "translateX(0)"
1745
+ };
1746
+ const thumbVisualStyles = unstyled ? {} : {
1747
+ backgroundColor: "white",
1748
+ borderRadius: "50%",
1749
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)",
1750
+ transition: "transform 0.2s ease"
1751
+ };
1752
+ const labelStyles = unstyled ? {
1753
+ marginLeft: 8,
1754
+ userSelect: "none",
1755
+ cursor: disabled ? "not-allowed" : "pointer"
1756
+ } : {
1757
+ marginLeft: 8,
1758
+ userSelect: "none",
1759
+ cursor: disabled ? "not-allowed" : "pointer",
1760
+ opacity: disabled ? 0.5 : 1
1761
+ };
1762
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1763
+ "div",
1764
+ {
1765
+ style: wrapperStructuralStyles,
1766
+ "data-compa11y-switch-wrapper": true,
1767
+ "data-size": size,
1768
+ children: [
1769
+ /* @__PURE__ */ jsxRuntime.jsx(
1770
+ "button",
1771
+ {
1772
+ ref,
1773
+ type: "button",
1774
+ role: "switch",
1775
+ "aria-checked": checked,
1776
+ "aria-label": computedAriaLabel,
1777
+ "aria-labelledby": computedAriaLabelledby,
1778
+ disabled,
1779
+ onClick: handleClick,
1780
+ onKeyDown: handleKeyDown,
1781
+ className,
1782
+ style: { ...trackStructuralStyles, ...trackVisualStyles },
1783
+ tabIndex: disabled ? -1 : 0,
1784
+ "data-compa11y-switch": true,
1785
+ "data-checked": checked,
1786
+ "data-disabled": disabled || void 0,
1787
+ "data-size": size,
1788
+ ...props,
1789
+ onFocus: (e) => {
1790
+ if (!unstyled) {
1791
+ e.currentTarget.style.outline = "2px solid #0066cc";
1792
+ e.currentTarget.style.outlineOffset = "2px";
1793
+ }
1794
+ props.onFocus?.(e);
1795
+ },
1796
+ onBlur: (e) => {
1797
+ if (!unstyled) {
1798
+ e.currentTarget.style.outline = "none";
1799
+ }
1800
+ props.onBlur?.(e);
1801
+ },
1802
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1803
+ "span",
1804
+ {
1805
+ style: { ...thumbStructuralStyles, ...thumbVisualStyles },
1806
+ "data-compa11y-switch-thumb": true,
1807
+ "aria-hidden": "true"
1808
+ }
1809
+ )
1810
+ }
1811
+ ),
1812
+ label && /* @__PURE__ */ jsxRuntime.jsx(
1813
+ "label",
1814
+ {
1815
+ id: labelId,
1816
+ onClick: disabled ? void 0 : () => toggleSwitch(),
1817
+ style: labelStyles,
1818
+ "data-compa11y-switch-label": true,
1819
+ children: label
1820
+ }
1821
+ )
1822
+ ]
1823
+ }
1824
+ );
1825
+ }
1826
+ );
1827
+ Switch.displayName = "Switch";
1828
+ var warnings5 = core.createComponentWarnings("Listbox");
1829
+ var ListboxContext = react.createContext(null);
1830
+ function useListboxContext() {
1831
+ const context = react.useContext(ListboxContext);
1832
+ if (!context) {
1833
+ throw new Error(
1834
+ "[Compa11y Listbox]: Option/Group must be used within a Listbox."
1835
+ );
1836
+ }
1837
+ return context;
1838
+ }
1839
+ var ListboxGroupContext = react.createContext({
1840
+ groupDisabled: false
1841
+ });
1842
+ var Listbox = react.forwardRef(
1843
+ function Listbox2({
1844
+ value: controlledValue,
1845
+ defaultValue,
1846
+ onValueChange,
1847
+ multiple = false,
1848
+ disabled = false,
1849
+ discoverable = true,
1850
+ orientation = "vertical",
1851
+ unstyled = false,
1852
+ className = "",
1853
+ children,
1854
+ "aria-label": ariaLabel,
1855
+ "aria-labelledby": ariaLabelledBy
1856
+ }, ref) {
1857
+ const listboxId = chunk52J4Z3QD_cjs.useId("listbox");
1858
+ const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
1859
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(() => defaultValue ?? (multiple ? [] : ""));
1860
+ const isControlled = controlledValue !== void 0;
1861
+ const currentValue = isControlled ? controlledValue : uncontrolledValue;
1862
+ const selectedValues = react.useMemo(() => {
1863
+ if (multiple) {
1864
+ return new Set(
1865
+ Array.isArray(currentValue) ? currentValue : []
1866
+ );
1867
+ }
1868
+ return new Set(currentValue ? [String(currentValue)] : []);
1869
+ }, [currentValue, multiple]);
1870
+ const [focusedValue, setFocusedValue] = react.useState(null);
1871
+ const optionsRef = react.useRef(/* @__PURE__ */ new Map());
1872
+ const [optionsVersion, setOptionsVersion] = react.useState(0);
1873
+ const registerOption = react.useCallback(
1874
+ (value, optDisabled, label) => {
1875
+ optionsRef.current.set(value, { disabled: optDisabled, label });
1876
+ setOptionsVersion((v) => v + 1);
1877
+ },
1878
+ []
1879
+ );
1880
+ const unregisterOption = react.useCallback((value) => {
1881
+ optionsRef.current.delete(value);
1882
+ setOptionsVersion((v) => v + 1);
1883
+ }, []);
1884
+ const updateOptionDisabled = react.useCallback(
1885
+ (value, optDisabled) => {
1886
+ const existing = optionsRef.current.get(value);
1887
+ if (existing) {
1888
+ optionsRef.current.set(value, { ...existing, disabled: optDisabled });
1889
+ }
1890
+ },
1891
+ []
1892
+ );
1893
+ const enabledValues = react.useMemo(() => {
1894
+ const result = [];
1895
+ optionsRef.current.forEach((info, value) => {
1896
+ if (!info.disabled && !disabled) {
1897
+ result.push(value);
1898
+ }
1899
+ });
1900
+ return result;
1901
+ }, [optionsVersion, disabled]);
1902
+ react.useEffect(() => {
1903
+ if (!ariaLabel && !ariaLabelledBy) {
1904
+ warnings5.warning(
1905
+ "Listbox has no accessible label. Screen readers need this.",
1906
+ 'Add aria-label="..." or aria-labelledby="..."'
1907
+ );
1908
+ }
1909
+ }, [ariaLabel, ariaLabelledBy]);
1910
+ const isSelected = react.useCallback(
1911
+ (value) => selectedValues.has(value),
1912
+ [selectedValues]
1913
+ );
1914
+ const handleSelect = react.useCallback(
1915
+ (optionValue) => {
1916
+ if (disabled) return;
1917
+ const info = optionsRef.current.get(optionValue);
1918
+ if (info?.disabled) return;
1919
+ let newValue;
1920
+ const label = info?.label || optionValue;
1921
+ if (multiple) {
1922
+ const current = new Set(selectedValues);
1923
+ if (current.has(optionValue)) {
1924
+ current.delete(optionValue);
1925
+ announce2(`${label} deselected`, { politeness: "polite" });
1926
+ } else {
1927
+ current.add(optionValue);
1928
+ announce2(`${label} selected`, { politeness: "polite" });
1929
+ }
1930
+ newValue = Array.from(current);
1931
+ } else {
1932
+ newValue = optionValue;
1933
+ announce2(`${label} selected`, { politeness: "polite" });
1934
+ }
1935
+ if (!isControlled) {
1936
+ setUncontrolledValue(newValue);
1937
+ }
1938
+ onValueChange?.(newValue);
1939
+ },
1940
+ [disabled, multiple, selectedValues, isControlled, onValueChange, announce2]
1941
+ );
1942
+ const navigateOption = react.useCallback(
1943
+ (direction) => {
1944
+ if (enabledValues.length === 0) return;
1945
+ const currentIndex = focusedValue ? enabledValues.indexOf(focusedValue) : -1;
1946
+ let nextIndex;
1947
+ switch (direction) {
1948
+ case "next":
1949
+ nextIndex = currentIndex < 0 ? 0 : Math.min(currentIndex + 1, enabledValues.length - 1);
1950
+ break;
1951
+ case "prev":
1952
+ nextIndex = currentIndex < 0 ? enabledValues.length - 1 : Math.max(currentIndex - 1, 0);
1953
+ break;
1954
+ case "first":
1955
+ nextIndex = 0;
1956
+ break;
1957
+ case "last":
1958
+ nextIndex = enabledValues.length - 1;
1959
+ break;
1960
+ }
1961
+ const nextValue = enabledValues[nextIndex];
1962
+ if (nextValue !== void 0) {
1963
+ setFocusedValue(nextValue);
1964
+ if (!multiple) {
1965
+ const info = optionsRef.current.get(nextValue);
1966
+ const label = info?.label || nextValue;
1967
+ let newVal = nextValue;
1968
+ if (!isControlled) {
1969
+ setUncontrolledValue(newVal);
1970
+ }
1971
+ onValueChange?.(newVal);
1972
+ announce2(`${label} selected`, { politeness: "polite" });
1973
+ } else {
1974
+ const info = optionsRef.current.get(nextValue);
1975
+ const label = info?.label || nextValue;
1976
+ const sel = selectedValues.has(nextValue);
1977
+ announce2(`${label}${sel ? ", selected" : ""}`, {
1978
+ politeness: "polite"
1979
+ });
1980
+ }
1981
+ }
1982
+ },
1983
+ [
1984
+ enabledValues,
1985
+ focusedValue,
1986
+ multiple,
1987
+ isControlled,
1988
+ onValueChange,
1989
+ announce2,
1990
+ selectedValues
1991
+ ]
1992
+ );
1993
+ const toggleFocusedOption = react.useCallback(() => {
1994
+ if (focusedValue && multiple) {
1995
+ handleSelect(focusedValue);
1996
+ }
1997
+ }, [focusedValue, multiple, handleSelect]);
1998
+ const selectRangeToFirst = react.useCallback(() => {
1999
+ if (!multiple || !focusedValue) return;
2000
+ const currentIdx = enabledValues.indexOf(focusedValue);
2001
+ if (currentIdx < 0) return;
2002
+ const toSelect = enabledValues.slice(0, currentIdx + 1);
2003
+ const newSet = new Set(selectedValues);
2004
+ toSelect.forEach((v) => newSet.add(v));
2005
+ const newValue = Array.from(newSet);
2006
+ if (!isControlled) {
2007
+ setUncontrolledValue(newValue);
2008
+ }
2009
+ onValueChange?.(newValue);
2010
+ setFocusedValue(enabledValues[0] ?? null);
2011
+ announce2(`${toSelect.length} items selected`, { politeness: "polite" });
2012
+ }, [
2013
+ multiple,
2014
+ focusedValue,
2015
+ enabledValues,
2016
+ selectedValues,
2017
+ isControlled,
2018
+ onValueChange,
2019
+ announce2
2020
+ ]);
2021
+ const selectRangeToLast = react.useCallback(() => {
2022
+ if (!multiple || !focusedValue) return;
2023
+ const currentIdx = enabledValues.indexOf(focusedValue);
2024
+ if (currentIdx < 0) return;
2025
+ const toSelect = enabledValues.slice(currentIdx);
2026
+ const newSet = new Set(selectedValues);
2027
+ toSelect.forEach((v) => newSet.add(v));
2028
+ const newValue = Array.from(newSet);
2029
+ if (!isControlled) {
2030
+ setUncontrolledValue(newValue);
2031
+ }
2032
+ onValueChange?.(newValue);
2033
+ setFocusedValue(enabledValues[enabledValues.length - 1] ?? null);
2034
+ announce2(`${toSelect.length} items selected`, { politeness: "polite" });
2035
+ }, [
2036
+ multiple,
2037
+ focusedValue,
2038
+ enabledValues,
2039
+ selectedValues,
2040
+ isControlled,
2041
+ onValueChange,
2042
+ announce2
2043
+ ]);
2044
+ const toggleSelectAll = react.useCallback(() => {
2045
+ if (!multiple) return;
2046
+ const allSelected = enabledValues.every((v) => selectedValues.has(v));
2047
+ let newValue;
2048
+ if (allSelected) {
2049
+ newValue = [];
2050
+ announce2("All deselected", { politeness: "polite" });
2051
+ } else {
2052
+ newValue = [...enabledValues];
2053
+ announce2("All selected", { politeness: "polite" });
2054
+ }
2055
+ if (!isControlled) {
2056
+ setUncontrolledValue(newValue);
2057
+ }
2058
+ onValueChange?.(newValue);
2059
+ }, [
2060
+ multiple,
2061
+ enabledValues,
2062
+ selectedValues,
2063
+ isControlled,
2064
+ onValueChange,
2065
+ announce2
2066
+ ]);
2067
+ const keyboardHandlers = react.useMemo(() => {
2068
+ const handlers = {
2069
+ ArrowDown: () => navigateOption("next"),
2070
+ ArrowUp: () => navigateOption("prev"),
2071
+ Home: () => navigateOption("first"),
2072
+ End: () => navigateOption("last")
2073
+ };
2074
+ if (multiple) {
2075
+ handlers[" "] = () => toggleFocusedOption();
2076
+ handlers["Shift+ArrowDown"] = () => {
2077
+ navigateOption("next");
2078
+ setTimeout(() => toggleFocusedOption(), 0);
2079
+ };
2080
+ handlers["Shift+ArrowUp"] = () => {
2081
+ navigateOption("prev");
2082
+ setTimeout(() => toggleFocusedOption(), 0);
2083
+ };
2084
+ handlers["Ctrl+Shift+Home"] = () => selectRangeToFirst();
2085
+ handlers["Ctrl+Shift+End"] = () => selectRangeToLast();
2086
+ handlers["Ctrl+a"] = () => toggleSelectAll();
2087
+ handlers["Meta+a"] = () => toggleSelectAll();
2088
+ }
2089
+ return handlers;
2090
+ }, [
2091
+ multiple,
2092
+ navigateOption,
2093
+ toggleFocusedOption,
2094
+ selectRangeToFirst,
2095
+ selectRangeToLast,
2096
+ toggleSelectAll
2097
+ ]);
2098
+ const keyboardProps = chunkGDLOJH6K_cjs.useKeyboard(keyboardHandlers, {
2099
+ preventDefault: true,
2100
+ stopPropagation: true,
2101
+ disabled
2102
+ });
2103
+ const optionLabels = react.useMemo(() => {
2104
+ return Array.from(optionsRef.current.values()).map((info) => info.label);
2105
+ }, [optionsVersion]);
2106
+ const typeAheadProps = chunkGDLOJH6K_cjs.useTypeAhead(
2107
+ optionLabels,
2108
+ (matchedLabel) => {
2109
+ for (const [value, info] of optionsRef.current.entries()) {
2110
+ if (info.label === matchedLabel && !info.disabled) {
2111
+ setFocusedValue(value);
2112
+ if (!multiple) {
2113
+ let newVal = value;
2114
+ if (!isControlled) {
2115
+ setUncontrolledValue(newVal);
2116
+ }
2117
+ onValueChange?.(newVal);
2118
+ announce2(`${matchedLabel} selected`, { politeness: "polite" });
2119
+ }
2120
+ break;
2121
+ }
2122
+ }
2123
+ },
2124
+ { disabled }
2125
+ );
2126
+ const handleKeyDown = react.useCallback(
2127
+ (event) => {
2128
+ keyboardProps.onKeyDown(event);
2129
+ if (!event.defaultPrevented) {
2130
+ typeAheadProps.onKeyDown(event);
2131
+ }
2132
+ },
2133
+ [keyboardProps, typeAheadProps]
2134
+ );
2135
+ const handleFocus = react.useCallback(() => {
2136
+ if (focusedValue) return;
2137
+ if (!multiple && currentValue && typeof currentValue === "string") {
2138
+ setFocusedValue(currentValue);
2139
+ return;
2140
+ }
2141
+ if (multiple && Array.isArray(currentValue) && currentValue.length > 0) {
2142
+ setFocusedValue(currentValue[0] ?? null);
2143
+ return;
2144
+ }
2145
+ if (enabledValues.length > 0) {
2146
+ setFocusedValue(enabledValues[0] ?? null);
2147
+ }
2148
+ }, [focusedValue, multiple, currentValue, enabledValues]);
2149
+ const handleBlur = react.useCallback(() => {
2150
+ }, []);
2151
+ const listboxRef = react.useRef(null);
2152
+ react.useEffect(() => {
2153
+ if (focusedValue && listboxRef.current) {
2154
+ const el = listboxRef.current.querySelector(
2155
+ `[data-value="${CSS.escape(focusedValue)}"]`
2156
+ );
2157
+ el?.scrollIntoView({ block: "nearest" });
2158
+ }
2159
+ }, [focusedValue]);
2160
+ const contextValue = react.useMemo(
2161
+ () => ({
2162
+ multiple,
2163
+ disabled,
2164
+ discoverable,
2165
+ unstyled,
2166
+ orientation,
2167
+ selectedValues,
2168
+ focusedValue,
2169
+ onSelect: handleSelect,
2170
+ onFocusOption: setFocusedValue,
2171
+ registerOption,
2172
+ unregisterOption,
2173
+ updateOptionDisabled,
2174
+ isSelected,
2175
+ listboxId
2176
+ }),
2177
+ [
2178
+ multiple,
2179
+ disabled,
2180
+ discoverable,
2181
+ unstyled,
2182
+ orientation,
2183
+ selectedValues,
2184
+ focusedValue,
2185
+ handleSelect,
2186
+ registerOption,
2187
+ unregisterOption,
2188
+ updateOptionDisabled,
2189
+ isSelected,
2190
+ listboxId
2191
+ ]
2192
+ );
2193
+ const mergedRef = react.useCallback(
2194
+ (node) => {
2195
+ listboxRef.current = node;
2196
+ if (typeof ref === "function") {
2197
+ ref(node);
2198
+ } else if (ref) {
2199
+ ref.current = node;
2200
+ }
2201
+ },
2202
+ [ref]
2203
+ );
2204
+ const activeDescendantId = focusedValue ? `${listboxId}-option-${focusedValue}` : void 0;
2205
+ return /* @__PURE__ */ jsxRuntime.jsx(ListboxContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
2206
+ "div",
2207
+ {
2208
+ ref: mergedRef,
2209
+ role: "listbox",
2210
+ "aria-label": ariaLabel,
2211
+ "aria-labelledby": ariaLabelledBy,
2212
+ "aria-orientation": orientation,
2213
+ "aria-multiselectable": multiple || void 0,
2214
+ "aria-disabled": disabled || void 0,
2215
+ "aria-activedescendant": activeDescendantId,
2216
+ tabIndex: disabled && !discoverable ? -1 : 0,
2217
+ className: `compa11y-listbox ${className}`.trim(),
2218
+ "data-compa11y-listbox": "",
2219
+ "data-orientation": orientation,
2220
+ "data-disabled": disabled ? "true" : void 0,
2221
+ "data-multiple": multiple ? "true" : void 0,
2222
+ onKeyDown: handleKeyDown,
2223
+ onFocus: handleFocus,
2224
+ onBlur: handleBlur,
2225
+ style: unstyled ? {} : {
2226
+ width: "var(--compa11y-listbox-width, 250px)",
2227
+ maxHeight: "var(--compa11y-listbox-max-height, 300px)",
2228
+ overflowY: "auto",
2229
+ border: "var(--compa11y-listbox-border, 1px solid #e0e0e0)",
2230
+ borderRadius: "var(--compa11y-listbox-radius, 4px)",
2231
+ background: "var(--compa11y-listbox-bg, white)",
2232
+ padding: "var(--compa11y-listbox-padding, 0.25rem 0)",
2233
+ ...disabled ? {
2234
+ opacity: "var(--compa11y-listbox-disabled-opacity, 0.5)",
2235
+ cursor: "not-allowed"
2236
+ } : {}
2237
+ },
2238
+ children
2239
+ }
2240
+ ) });
2241
+ }
2242
+ );
2243
+ Listbox.displayName = "Listbox";
2244
+ var ListboxOption = react.forwardRef(
2245
+ function ListboxOption2({
2246
+ value,
2247
+ disabled: localDisabled = false,
2248
+ discoverable: _,
2249
+ unstyled: localUnstyled,
2250
+ className = "",
2251
+ children,
2252
+ "aria-label": ariaLabel
2253
+ }, ref) {
2254
+ const context = useListboxContext();
2255
+ const { groupDisabled } = react.useContext(ListboxGroupContext);
2256
+ const disabled = context.disabled || groupDisabled || localDisabled;
2257
+ const unstyled = localUnstyled ?? context.unstyled;
2258
+ const selected = context.isSelected(value);
2259
+ const focused = context.focusedValue === value;
2260
+ const optionId = `${context.listboxId}-option-${value}`;
2261
+ const labelRef = react.useRef("");
2262
+ const optionRef = react.useRef(null);
2263
+ const mergedRef = react.useCallback(
2264
+ (node) => {
2265
+ optionRef.current = node;
2266
+ if (typeof ref === "function") {
2267
+ ref(node);
2268
+ } else if (ref) {
2269
+ ref.current = node;
2270
+ }
2271
+ },
2272
+ [ref]
2273
+ );
2274
+ react.useEffect(() => {
2275
+ const label = typeof children === "string" ? children : optionRef.current?.textContent?.trim() || value;
2276
+ labelRef.current = label;
2277
+ context.registerOption(value, disabled, label);
2278
+ return () => context.unregisterOption(value);
2279
+ }, [value]);
2280
+ react.useEffect(() => {
2281
+ context.updateOptionDisabled(value, disabled);
2282
+ }, [disabled, value]);
2283
+ const handleClick = react.useCallback(
2284
+ (event) => {
2285
+ if (disabled) {
2286
+ event.preventDefault();
2287
+ return;
2288
+ }
2289
+ context.onFocusOption(value);
2290
+ context.onSelect(value);
2291
+ },
2292
+ [disabled, context, value]
2293
+ );
2294
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2295
+ "div",
2296
+ {
2297
+ ref: mergedRef,
2298
+ id: optionId,
2299
+ role: "option",
2300
+ "aria-selected": selected,
2301
+ "aria-disabled": disabled || void 0,
2302
+ "aria-label": ariaLabel,
2303
+ className: `compa11y-listbox-option ${className}`.trim(),
2304
+ "data-compa11y-listbox-option": "",
2305
+ "data-value": value,
2306
+ "data-selected": selected ? "true" : "false",
2307
+ "data-focused": focused ? "true" : "false",
2308
+ "data-disabled": disabled ? "true" : void 0,
2309
+ onClick: handleClick,
2310
+ style: unstyled ? {
2311
+ cursor: disabled ? "not-allowed" : "pointer"
2312
+ } : {
2313
+ display: "flex",
2314
+ alignItems: "center",
2315
+ justifyContent: "space-between",
2316
+ padding: "var(--compa11y-listbox-option-padding, 0.5rem 0.75rem)",
2317
+ cursor: disabled ? "not-allowed" : "pointer",
2318
+ userSelect: "none",
2319
+ transition: "background 0.1s ease",
2320
+ background: focused ? selected ? "var(--compa11y-listbox-option-selected-hover-bg, #cce0ff)" : "var(--compa11y-listbox-option-hover-bg, #f5f5f5)" : selected ? "var(--compa11y-listbox-option-selected-bg, #e6f0ff)" : "transparent",
2321
+ fontWeight: selected ? 500 : "normal",
2322
+ opacity: disabled ? 0.5 : 1
2323
+ },
2324
+ children: [
2325
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1 }, children }),
2326
+ selected && !unstyled && /* @__PURE__ */ jsxRuntime.jsx(
2327
+ "span",
2328
+ {
2329
+ "aria-hidden": "true",
2330
+ style: {
2331
+ fontSize: "0.875rem",
2332
+ color: "var(--compa11y-listbox-check-color, #0066cc)",
2333
+ marginLeft: "0.5rem"
2334
+ },
2335
+ children: "\u2713"
2336
+ }
2337
+ )
2338
+ ]
2339
+ }
2340
+ );
2341
+ }
2342
+ );
2343
+ ListboxOption.displayName = "ListboxOption";
2344
+ var ListboxGroup = react.forwardRef(
2345
+ function ListboxGroup2({
2346
+ label,
2347
+ disabled: groupDisabled = false,
2348
+ unstyled: localUnstyled,
2349
+ className = "",
2350
+ children
2351
+ }, ref) {
2352
+ const context = useListboxContext();
2353
+ const unstyled = localUnstyled ?? context.unstyled;
2354
+ const labelId = chunk52J4Z3QD_cjs.useId("listbox-group");
2355
+ const groupContextValue = react.useMemo(
2356
+ () => ({ groupDisabled: context.disabled || groupDisabled }),
2357
+ [context.disabled, groupDisabled]
2358
+ );
2359
+ return /* @__PURE__ */ jsxRuntime.jsx(ListboxGroupContext.Provider, { value: groupContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
2360
+ "div",
2361
+ {
2362
+ ref,
2363
+ role: "group",
2364
+ "aria-labelledby": labelId,
2365
+ "aria-disabled": groupDisabled || void 0,
2366
+ className: `compa11y-listbox-group ${className}`.trim(),
2367
+ "data-compa11y-listbox-group": "",
2368
+ "data-disabled": groupDisabled ? "true" : void 0,
2369
+ style: unstyled ? {} : {
2370
+ opacity: groupDisabled ? 0.5 : 1
2371
+ },
2372
+ children: [
2373
+ /* @__PURE__ */ jsxRuntime.jsx(
2374
+ "div",
2375
+ {
2376
+ id: labelId,
2377
+ role: "presentation",
2378
+ style: unstyled ? {} : {
2379
+ padding: "var(--compa11y-listbox-group-label-padding, 0.5rem 0.75rem 0.25rem)",
2380
+ fontSize: "var(--compa11y-listbox-group-label-size, 0.75rem)",
2381
+ fontWeight: 600,
2382
+ color: "var(--compa11y-listbox-group-label-color, #666)",
2383
+ textTransform: "uppercase",
2384
+ letterSpacing: "0.05em"
2385
+ },
2386
+ children: label
2387
+ }
2388
+ ),
2389
+ children
2390
+ ]
2391
+ }
2392
+ ) });
2393
+ }
2394
+ );
2395
+ ListboxGroup.displayName = "ListboxGroup";
2396
+ var ListboxCompound = Object.assign(Listbox, {
2397
+ Option: ListboxOption,
2398
+ Group: ListboxGroup
2399
+ });
2400
+ var InputContext = react.createContext(null);
2401
+ function useInputContext() {
2402
+ const ctx = react.useContext(InputContext);
2403
+ if (!ctx) {
2404
+ throw new Error(
2405
+ "[Compa11y Input]: Input sub-components (Input.Label, Input.Field, etc.) must be used within <Input>."
2406
+ );
2407
+ }
2408
+ return ctx;
2409
+ }
2410
+ var InputLabel = react.forwardRef(
2411
+ function InputLabel2({ children, className }, ref) {
2412
+ const ctx = useInputContext();
2413
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2414
+ "label",
2415
+ {
2416
+ ref,
2417
+ id: ctx.labelId,
2418
+ htmlFor: ctx.fieldId,
2419
+ "data-compa11y-input-label": "",
2420
+ className,
2421
+ style: ctx.unstyled ? {} : {
2422
+ display: "block",
2423
+ color: ctx.disabled ? "var(--compa11y-input-disabled-color, #999)" : "var(--compa11y-input-label-color, inherit)",
2424
+ fontSize: "var(--compa11y-input-label-size, 0.875rem)",
2425
+ fontWeight: "var(--compa11y-input-label-weight, 500)"
2426
+ },
2427
+ children: [
2428
+ children,
2429
+ ctx.required && /* @__PURE__ */ jsxRuntime.jsx(
2430
+ "span",
2431
+ {
2432
+ "data-compa11y-input-required": "",
2433
+ "aria-hidden": "true",
2434
+ style: ctx.unstyled ? {} : {
2435
+ color: "var(--compa11y-input-required-color, #ef4444)",
2436
+ marginLeft: "0.125rem"
2437
+ },
2438
+ children: "*"
2439
+ }
2440
+ )
2441
+ ]
2442
+ }
2443
+ );
2444
+ }
2445
+ );
2446
+ InputLabel.displayName = "InputLabel";
2447
+ var InputField = react.forwardRef(
2448
+ function InputField2({
2449
+ type = "text",
2450
+ onFocus: providedOnFocus,
2451
+ onBlur: providedOnBlur,
2452
+ className,
2453
+ style,
2454
+ ...rest
2455
+ }, ref) {
2456
+ const ctx = useInputContext();
2457
+ const inputRef = react.useRef(null);
2458
+ const mergedRef = react.useCallback(
2459
+ (node) => {
2460
+ inputRef.current = node;
2461
+ if (typeof ref === "function") {
2462
+ ref(node);
2463
+ } else if (ref) {
2464
+ ref.current = node;
2465
+ }
2466
+ },
2467
+ [ref]
2468
+ );
2469
+ const handleChange = react.useCallback(
2470
+ (event) => {
2471
+ ctx.setValue(event.target.value);
2472
+ },
2473
+ [ctx.setValue]
2474
+ );
2475
+ const handleFocus = react.useCallback(
2476
+ (event) => {
2477
+ ctx.focusProps.onFocus();
2478
+ providedOnFocus?.(event);
2479
+ },
2480
+ [ctx.focusProps, providedOnFocus]
2481
+ );
2482
+ const handleBlur = react.useCallback(
2483
+ (event) => {
2484
+ ctx.focusProps.onBlur();
2485
+ providedOnBlur?.(event);
2486
+ },
2487
+ [ctx.focusProps, providedOnBlur]
2488
+ );
2489
+ const describedByParts = [];
2490
+ describedByParts.push(ctx.hintId);
2491
+ if (ctx.hasError) {
2492
+ describedByParts.push(ctx.errorId);
2493
+ }
2494
+ return /* @__PURE__ */ jsxRuntime.jsx(
2495
+ "input",
2496
+ {
2497
+ ref: mergedRef,
2498
+ id: ctx.fieldId,
2499
+ type,
2500
+ value: ctx.value,
2501
+ onChange: handleChange,
2502
+ onFocus: handleFocus,
2503
+ onBlur: handleBlur,
2504
+ disabled: ctx.disabled,
2505
+ readOnly: ctx.readOnly,
2506
+ "aria-describedby": describedByParts.join(" ") || void 0,
2507
+ "aria-invalid": ctx.hasError ? "true" : void 0,
2508
+ "aria-required": ctx.required ? "true" : void 0,
2509
+ "data-compa11y-input-field": "",
2510
+ className,
2511
+ style: ctx.unstyled ? style : {
2512
+ width: "100%",
2513
+ padding: "var(--compa11y-input-padding, 0.5rem 0.75rem)",
2514
+ border: ctx.hasError ? "1px solid var(--compa11y-input-border-error, #ef4444)" : "var(--compa11y-input-border, 1px solid #ccc)",
2515
+ borderRadius: "var(--compa11y-input-radius, 4px)",
2516
+ fontSize: "var(--compa11y-input-font-size, 0.875rem)",
2517
+ fontFamily: "inherit",
2518
+ background: ctx.disabled ? "var(--compa11y-input-disabled-bg, #f5f5f5)" : ctx.readOnly ? "var(--compa11y-input-readonly-bg, #f9f9f9)" : "var(--compa11y-input-bg, white)",
2519
+ color: "inherit",
2520
+ cursor: ctx.disabled ? "not-allowed" : void 0,
2521
+ opacity: ctx.disabled ? "var(--compa11y-input-disabled-opacity, 0.7)" : void 0,
2522
+ ...ctx.isFocusVisible && !ctx.disabled ? {
2523
+ outline: ctx.hasError ? "2px solid var(--compa11y-input-border-error, #ef4444)" : "2px solid var(--compa11y-focus-color, #0066cc)",
2524
+ outlineOffset: "-1px",
2525
+ borderColor: ctx.hasError ? "var(--compa11y-input-border-error, #ef4444)" : "var(--compa11y-input-border-focus, #0066cc)"
2526
+ } : {},
2527
+ ...style
2528
+ },
2529
+ ...rest
2530
+ }
2531
+ );
2532
+ }
2533
+ );
2534
+ InputField.displayName = "InputField";
2535
+ var InputHint = react.forwardRef(
2536
+ function InputHint2({ children, className }, ref) {
2537
+ const ctx = useInputContext();
2538
+ return /* @__PURE__ */ jsxRuntime.jsx(
2539
+ "div",
2540
+ {
2541
+ ref,
2542
+ id: ctx.hintId,
2543
+ "data-compa11y-input-hint": "",
2544
+ className,
2545
+ style: ctx.unstyled ? {} : {
2546
+ color: "var(--compa11y-input-hint-color, #666)",
2547
+ fontSize: "var(--compa11y-input-hint-size, 0.8125rem)"
2548
+ },
2549
+ children
2550
+ }
2551
+ );
2552
+ }
2553
+ );
2554
+ InputHint.displayName = "InputHint";
2555
+ var InputError = react.forwardRef(
2556
+ function InputError2({ children, className }, ref) {
2557
+ const ctx = useInputContext();
2558
+ if (!children) return null;
2559
+ return /* @__PURE__ */ jsxRuntime.jsx(
2560
+ "div",
2561
+ {
2562
+ ref,
2563
+ id: ctx.errorId,
2564
+ role: "alert",
2565
+ "data-compa11y-input-error": "",
2566
+ className,
2567
+ style: ctx.unstyled ? {} : {
2568
+ color: "var(--compa11y-input-error-color, #ef4444)",
2569
+ fontSize: "var(--compa11y-input-error-size, 0.8125rem)"
2570
+ },
2571
+ children
2572
+ }
2573
+ );
2574
+ }
2575
+ );
2576
+ InputError.displayName = "InputError";
2577
+ var Input = react.forwardRef(
2578
+ function Input2({
2579
+ value: controlledValue,
2580
+ defaultValue = "",
2581
+ onValueChange,
2582
+ type = "text",
2583
+ label,
2584
+ hint,
2585
+ error,
2586
+ required = false,
2587
+ disabled = false,
2588
+ readOnly = false,
2589
+ "aria-label": ariaLabel,
2590
+ "aria-labelledby": ariaLabelledBy,
2591
+ placeholder,
2592
+ name,
2593
+ autoComplete,
2594
+ maxLength,
2595
+ minLength,
2596
+ pattern,
2597
+ inputMode,
2598
+ id: providedId,
2599
+ unstyled = false,
2600
+ className,
2601
+ onFocus: providedOnFocus,
2602
+ onBlur: providedOnBlur,
2603
+ children
2604
+ }, ref) {
2605
+ const generatedId = chunk52J4Z3QD_cjs.useId("input");
2606
+ const baseId = providedId || generatedId;
2607
+ const fieldId = `${baseId}-field`;
2608
+ const labelId = `${baseId}-label`;
2609
+ const hintId = `${baseId}-hint`;
2610
+ const errorId = `${baseId}-error`;
2611
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(defaultValue);
2612
+ const isControlled = controlledValue !== void 0;
2613
+ const currentValue = isControlled ? controlledValue : uncontrolledValue;
2614
+ const setValue = react.useCallback(
2615
+ (newValue) => {
2616
+ if (!isControlled) {
2617
+ setUncontrolledValue(newValue);
2618
+ }
2619
+ onValueChange?.(newValue);
2620
+ },
2621
+ [isControlled, onValueChange]
2622
+ );
2623
+ const { isFocusVisible, focusProps } = useFocusVisible();
2624
+ react.useEffect(() => {
2625
+ if (process.env.NODE_ENV !== "production") {
2626
+ const isCompound2 = react.Children.count(children) > 0;
2627
+ if (!isCompound2 && !label && !ariaLabel && !ariaLabelledBy) {
2628
+ console.warn(
2629
+ "[Compa11y Input]: Input has no accessible label. Screen readers need this to identify the input. Use label prop, aria-label, or aria-labelledby."
2630
+ );
2631
+ }
2632
+ }
2633
+ }, [children, label, ariaLabel, ariaLabelledBy]);
2634
+ const hasError = Boolean(error);
2635
+ const isCompound = react.Children.count(children) > 0;
2636
+ const contextValue = {
2637
+ fieldId,
2638
+ labelId,
2639
+ hintId,
2640
+ errorId,
2641
+ value: currentValue,
2642
+ setValue,
2643
+ hasError,
2644
+ disabled,
2645
+ readOnly,
2646
+ required,
2647
+ isFocusVisible,
2648
+ focusProps,
2649
+ unstyled
2650
+ };
2651
+ const dataAttrs = {
2652
+ "data-compa11y-input": "",
2653
+ "data-error": hasError ? "true" : "false",
2654
+ "data-disabled": disabled ? "true" : "false",
2655
+ "data-required": required ? "true" : "false",
2656
+ "data-readonly": readOnly ? "true" : "false"
2657
+ };
2658
+ const wrapperStyle = unstyled ? {} : {
2659
+ display: "flex",
2660
+ flexDirection: "column",
2661
+ gap: "0.25rem"
2662
+ };
2663
+ if (isCompound) {
2664
+ return /* @__PURE__ */ jsxRuntime.jsx(InputContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ...dataAttrs, className, style: wrapperStyle, children }) });
2665
+ }
2666
+ const inputRef = react.useRef(null);
2667
+ const mergedRef = react.useCallback(
2668
+ (node) => {
2669
+ inputRef.current = node;
2670
+ if (typeof ref === "function") {
2671
+ ref(node);
2672
+ } else if (ref) {
2673
+ ref.current = node;
2674
+ }
2675
+ },
2676
+ [ref]
2677
+ );
2678
+ const handleChange = react.useCallback(
2679
+ (event) => {
2680
+ setValue(event.target.value);
2681
+ },
2682
+ [setValue]
2683
+ );
2684
+ const handleFocus = react.useCallback(
2685
+ (event) => {
2686
+ focusProps.onFocus();
2687
+ providedOnFocus?.(event);
2688
+ },
2689
+ [focusProps, providedOnFocus]
2690
+ );
2691
+ const handleBlur = react.useCallback(
2692
+ (event) => {
2693
+ focusProps.onBlur();
2694
+ providedOnBlur?.(event);
2695
+ },
2696
+ [focusProps, providedOnBlur]
2697
+ );
2698
+ const describedByParts = [];
2699
+ if (hint) describedByParts.push(hintId);
2700
+ if (hasError) describedByParts.push(errorId);
2701
+ const ariaDescribedBy = describedByParts.length > 0 ? describedByParts.join(" ") : void 0;
2702
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ...dataAttrs, className, style: wrapperStyle, children: [
2703
+ label && /* @__PURE__ */ jsxRuntime.jsxs(
2704
+ "label",
2705
+ {
2706
+ id: labelId,
2707
+ htmlFor: fieldId,
2708
+ "data-compa11y-input-label": "",
2709
+ style: unstyled ? {} : {
2710
+ display: "block",
2711
+ color: disabled ? "var(--compa11y-input-disabled-color, #999)" : "var(--compa11y-input-label-color, inherit)",
2712
+ fontSize: "var(--compa11y-input-label-size, 0.875rem)",
2713
+ fontWeight: "var(--compa11y-input-label-weight, 500)"
2714
+ },
2715
+ children: [
2716
+ label,
2717
+ required && /* @__PURE__ */ jsxRuntime.jsx(
2718
+ "span",
2719
+ {
2720
+ "data-compa11y-input-required": "",
2721
+ "aria-hidden": "true",
2722
+ style: unstyled ? {} : {
2723
+ color: "var(--compa11y-input-required-color, #ef4444)",
2724
+ marginLeft: "0.125rem"
2725
+ },
2726
+ children: "*"
2727
+ }
2728
+ )
2729
+ ]
2730
+ }
2731
+ ),
2732
+ /* @__PURE__ */ jsxRuntime.jsx(
2733
+ "input",
2734
+ {
2735
+ ref: mergedRef,
2736
+ id: fieldId,
2737
+ type,
2738
+ value: currentValue,
2739
+ onChange: handleChange,
2740
+ onFocus: handleFocus,
2741
+ onBlur: handleBlur,
2742
+ disabled,
2743
+ readOnly,
2744
+ required,
2745
+ placeholder,
2746
+ name,
2747
+ autoComplete,
2748
+ maxLength,
2749
+ minLength,
2750
+ pattern,
2751
+ inputMode,
2752
+ "aria-label": !label ? ariaLabel : void 0,
2753
+ "aria-labelledby": !label && ariaLabelledBy ? ariaLabelledBy : label ? labelId : void 0,
2754
+ "aria-describedby": ariaDescribedBy,
2755
+ "aria-invalid": hasError ? "true" : void 0,
2756
+ "aria-required": required ? "true" : void 0,
2757
+ "data-compa11y-input-field": "",
2758
+ style: unstyled ? {} : {
2759
+ width: "100%",
2760
+ padding: "var(--compa11y-input-padding, 0.5rem 0.75rem)",
2761
+ border: hasError ? "1px solid var(--compa11y-input-border-error, #ef4444)" : "var(--compa11y-input-border, 1px solid #ccc)",
2762
+ borderRadius: "var(--compa11y-input-radius, 4px)",
2763
+ fontSize: "var(--compa11y-input-font-size, 0.875rem)",
2764
+ fontFamily: "inherit",
2765
+ background: disabled ? "var(--compa11y-input-disabled-bg, #f5f5f5)" : readOnly ? "var(--compa11y-input-readonly-bg, #f9f9f9)" : "var(--compa11y-input-bg, white)",
2766
+ color: "inherit",
2767
+ cursor: disabled ? "not-allowed" : void 0,
2768
+ opacity: disabled ? "var(--compa11y-input-disabled-opacity, 0.7)" : void 0,
2769
+ ...isFocusVisible && !disabled ? {
2770
+ outline: hasError ? "2px solid var(--compa11y-input-border-error, #ef4444)" : "2px solid var(--compa11y-focus-color, #0066cc)",
2771
+ outlineOffset: "-1px",
2772
+ borderColor: hasError ? "var(--compa11y-input-border-error, #ef4444)" : "var(--compa11y-input-border-focus, #0066cc)"
2773
+ } : {}
2774
+ }
2775
+ }
2776
+ ),
2777
+ hint && /* @__PURE__ */ jsxRuntime.jsx(
2778
+ "div",
2779
+ {
2780
+ id: hintId,
2781
+ "data-compa11y-input-hint": "",
2782
+ style: unstyled ? {} : {
2783
+ color: "var(--compa11y-input-hint-color, #666)",
2784
+ fontSize: "var(--compa11y-input-hint-size, 0.8125rem)"
2785
+ },
2786
+ children: hint
2787
+ }
2788
+ ),
2789
+ hasError && /* @__PURE__ */ jsxRuntime.jsx(
2790
+ "div",
2791
+ {
2792
+ id: errorId,
2793
+ role: "alert",
2794
+ "data-compa11y-input-error": "",
2795
+ style: unstyled ? {} : {
2796
+ color: "var(--compa11y-input-error-color, #ef4444)",
2797
+ fontSize: "var(--compa11y-input-error-size, 0.8125rem)"
2798
+ },
2799
+ children: error
2800
+ }
2801
+ )
2802
+ ] });
2803
+ }
2804
+ );
2805
+ Input.displayName = "Input";
2806
+ var InputCompound = Object.assign(Input, {
2807
+ Label: InputLabel,
2808
+ Field: InputField,
2809
+ Hint: InputHint,
2810
+ Error: InputError
2811
+ });
2812
+ var VARIANT_STYLES = {
2813
+ primary: {
2814
+ background: "var(--compa11y-button-primary-bg, #0066cc)",
2815
+ color: "var(--compa11y-button-primary-color, white)",
2816
+ border: "var(--compa11y-button-primary-border, 1px solid #0066cc)"
2817
+ },
2818
+ secondary: {
2819
+ background: "var(--compa11y-button-secondary-bg, white)",
2820
+ color: "var(--compa11y-button-secondary-color, #333)",
2821
+ border: "var(--compa11y-button-secondary-border, 1px solid #ccc)"
2822
+ },
2823
+ danger: {
2824
+ background: "var(--compa11y-button-danger-bg, #ef4444)",
2825
+ color: "var(--compa11y-button-danger-color, white)",
2826
+ border: "var(--compa11y-button-danger-border, 1px solid #ef4444)"
2827
+ },
2828
+ outline: {
2829
+ background: "var(--compa11y-button-outline-bg, transparent)",
2830
+ color: "var(--compa11y-button-outline-color, #0066cc)",
2831
+ border: "var(--compa11y-button-outline-border, 1px solid #0066cc)"
2832
+ },
2833
+ ghost: {
2834
+ background: "var(--compa11y-button-ghost-bg, transparent)",
2835
+ color: "var(--compa11y-button-ghost-color, #333)",
2836
+ border: "var(--compa11y-button-ghost-border, 1px solid transparent)"
2837
+ }
2838
+ };
2839
+ var SIZE_STYLES = {
2840
+ sm: {
2841
+ padding: "var(--compa11y-button-padding-sm, 0.25rem 0.5rem)",
2842
+ fontSize: "var(--compa11y-button-font-size-sm, 0.75rem)"
2843
+ },
2844
+ md: {
2845
+ padding: "var(--compa11y-button-padding-md, 0.5rem 1rem)",
2846
+ fontSize: "var(--compa11y-button-font-size-md, 0.875rem)"
2847
+ },
2848
+ lg: {
2849
+ padding: "var(--compa11y-button-padding-lg, 0.75rem 1.5rem)",
2850
+ fontSize: "var(--compa11y-button-font-size-lg, 1rem)"
2851
+ }
2852
+ };
2853
+ var Button = react.forwardRef(
2854
+ function Button2({
2855
+ variant = "secondary",
2856
+ size = "md",
2857
+ type = "button",
239
2858
  disabled = false,
2859
+ discoverable = false,
2860
+ loading = false,
240
2861
  unstyled = false,
241
- className,
242
- size = "md",
243
2862
  "aria-label": ariaLabel,
244
- "aria-labelledby": ariaLabelledby,
2863
+ className,
2864
+ style,
245
2865
  onClick,
246
- ...props
2866
+ onFocus: providedOnFocus,
2867
+ onBlur: providedOnBlur,
2868
+ children,
2869
+ ...rest
247
2870
  }, ref) {
248
- const id = chunk52J4Z3QD_cjs.useId("switch");
249
- const labelId = `${id}-label`;
250
- const { announce: announce2 } = chunkRBDQCIS7_cjs.useAnnouncer();
251
- const [uncontrolledChecked, setUncontrolledChecked] = react.useState(defaultChecked);
252
- const isControlled = controlledChecked !== void 0;
253
- const checked = isControlled ? controlledChecked : uncontrolledChecked;
2871
+ const generatedId = chunk52J4Z3QD_cjs.useId("button");
2872
+ const buttonRef = react.useRef(null);
2873
+ const mergedRef = react.useCallback(
2874
+ (node) => {
2875
+ buttonRef.current = node;
2876
+ if (typeof ref === "function") {
2877
+ ref(node);
2878
+ } else if (ref) {
2879
+ ref.current = node;
2880
+ }
2881
+ },
2882
+ [ref]
2883
+ );
2884
+ const { isFocusVisible, focusProps } = useFocusVisible();
254
2885
  react.useEffect(() => {
255
- if (!label && !ariaLabel && !ariaLabelledby) {
256
- warnings.warning(
257
- "Switch has no accessible label. Screen readers need this to identify the switch.",
258
- 'Add label="Description", aria-label="...", or aria-labelledby="..."'
259
- );
260
- }
261
- }, [label, ariaLabel, ariaLabelledby]);
262
- const toggleSwitch = react.useCallback(() => {
263
- if (disabled) return;
264
- const newChecked = !checked;
265
- if (!isControlled) {
266
- setUncontrolledChecked(newChecked);
2886
+ if (process.env.NODE_ENV !== "production") {
2887
+ if (!children && !ariaLabel && !rest["aria-labelledby"]) {
2888
+ console.warn(
2889
+ "[Compa11y Button]: Button has no accessible label. Screen readers need this to identify the button. Add text content, aria-label, or aria-labelledby."
2890
+ );
2891
+ }
267
2892
  }
268
- onCheckedChange?.(newChecked);
269
- const labelText = label || ariaLabel || "Switch";
270
- announce2(`${labelText} ${newChecked ? "on" : "off"}`);
271
- }, [
272
- checked,
273
- disabled,
274
- isControlled,
275
- onCheckedChange,
276
- label,
277
- ariaLabel,
278
- announce2
279
- ]);
2893
+ }, [children, ariaLabel, rest["aria-labelledby"]]);
2894
+ const useNativeDisabled = disabled && !discoverable;
2895
+ const isInteractionDisabled = disabled || loading;
280
2896
  const handleClick = react.useCallback(
281
2897
  (event) => {
282
- onClick?.(event);
283
- if (!event.defaultPrevented) {
284
- toggleSwitch();
2898
+ if (isInteractionDisabled) {
2899
+ event.preventDefault();
2900
+ return;
285
2901
  }
2902
+ onClick?.(event);
286
2903
  },
287
- [onClick, toggleSwitch]
2904
+ [isInteractionDisabled, onClick]
288
2905
  );
289
- const keyboardProps = chunkGDLOJH6K_cjs.useKeyboard(
290
- {
291
- " ": () => {
292
- toggleSwitch();
293
- },
294
- Enter: () => {
295
- toggleSwitch();
296
- }
2906
+ const handleFocus = react.useCallback(
2907
+ (event) => {
2908
+ focusProps.onFocus();
2909
+ providedOnFocus?.(event);
297
2910
  },
298
- { preventDefault: true }
2911
+ [focusProps, providedOnFocus]
299
2912
  );
300
- const handleKeyDown = (event) => {
301
- props.onKeyDown?.(event);
302
- if (!event.defaultPrevented) {
303
- keyboardProps.onKeyDown(event);
304
- }
305
- };
306
- const computedAriaLabel = ariaLabel;
307
- const computedAriaLabelledby = ariaLabelledby || (label ? labelId : void 0);
308
- const wrapperStructuralStyles = {
309
- display: "inline-flex",
310
- alignItems: "center"
311
- };
312
- const sizes = {
313
- sm: { width: 32, height: 18, thumb: 14, translate: 14 },
314
- md: { width: 44, height: 24, thumb: 20, translate: 20 },
315
- lg: { width: 56, height: 30, thumb: 26, translate: 26 }
2913
+ const handleBlur = react.useCallback(
2914
+ (event) => {
2915
+ focusProps.onBlur();
2916
+ providedOnBlur?.(event);
2917
+ },
2918
+ [focusProps, providedOnBlur]
2919
+ );
2920
+ const dataAttrs = {
2921
+ "data-compa11y-button": "",
2922
+ "data-variant": variant,
2923
+ "data-size": size,
2924
+ "data-disabled": disabled ? "true" : "false",
2925
+ "data-loading": loading ? "true" : "false"
316
2926
  };
317
- const sizeConfig = sizes[size];
318
- const trackStructuralStyles = {
319
- position: "relative",
2927
+ const baseStyle = unstyled ? {} : {
320
2928
  display: "inline-flex",
321
2929
  alignItems: "center",
322
- flexShrink: 0,
323
- width: sizeConfig.width,
324
- height: sizeConfig.height,
325
- border: "none",
326
- padding: 2,
327
- cursor: disabled ? "not-allowed" : "pointer"
328
- };
329
- const trackVisualStyles = unstyled ? {} : {
330
- backgroundColor: checked ? "#0066cc" : "#d1d5db",
331
- borderRadius: sizeConfig.height / 2,
332
- transition: "background-color 0.2s ease",
333
- opacity: disabled ? 0.5 : 1
334
- };
335
- const thumbStructuralStyles = {
336
- position: "absolute",
337
- left: 2,
338
- width: sizeConfig.thumb,
339
- height: sizeConfig.thumb,
340
- pointerEvents: "none",
341
- transform: checked ? `translateX(${sizeConfig.translate}px)` : "translateX(0)"
342
- };
343
- const thumbVisualStyles = unstyled ? {} : {
344
- backgroundColor: "white",
345
- borderRadius: "50%",
346
- boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)",
347
- transition: "transform 0.2s ease"
348
- };
349
- const labelStyles = unstyled ? {
350
- marginLeft: 8,
351
- userSelect: "none",
352
- cursor: disabled ? "not-allowed" : "pointer"
353
- } : {
354
- marginLeft: 8,
355
- userSelect: "none",
356
- cursor: disabled ? "not-allowed" : "pointer",
357
- opacity: disabled ? 0.5 : 1
2930
+ justifyContent: "center",
2931
+ gap: "0.5rem",
2932
+ borderRadius: "var(--compa11y-button-radius, 4px)",
2933
+ fontFamily: "inherit",
2934
+ fontWeight: "var(--compa11y-button-font-weight, 500)",
2935
+ lineHeight: "1.5",
2936
+ cursor: isInteractionDisabled ? "not-allowed" : "pointer",
2937
+ opacity: disabled ? "var(--compa11y-button-disabled-opacity, 0.5)" : void 0,
2938
+ transition: "background-color 0.15s ease, border-color 0.15s ease",
2939
+ ...VARIANT_STYLES[variant],
2940
+ ...SIZE_STYLES[size],
2941
+ ...isFocusVisible && !useNativeDisabled ? {
2942
+ outline: "2px solid var(--compa11y-focus-color, #0066cc)",
2943
+ outlineOffset: "2px"
2944
+ } : {}
358
2945
  };
359
2946
  return /* @__PURE__ */ jsxRuntime.jsxs(
360
- "div",
2947
+ "button",
361
2948
  {
362
- style: wrapperStructuralStyles,
363
- "data-compa11y-switch-wrapper": true,
364
- "data-size": size,
2949
+ ref: mergedRef,
2950
+ id: generatedId,
2951
+ type,
2952
+ disabled: useNativeDisabled,
2953
+ tabIndex: disabled && !discoverable ? void 0 : 0,
2954
+ "aria-disabled": isInteractionDisabled ? "true" : void 0,
2955
+ "aria-busy": loading ? "true" : void 0,
2956
+ "aria-label": ariaLabel,
2957
+ onClick: handleClick,
2958
+ onFocus: handleFocus,
2959
+ onBlur: handleBlur,
2960
+ className,
2961
+ style: { ...baseStyle, ...style },
2962
+ ...dataAttrs,
2963
+ ...rest,
365
2964
  children: [
366
- /* @__PURE__ */ jsxRuntime.jsx(
367
- "button",
2965
+ loading && /* @__PURE__ */ jsxRuntime.jsx(
2966
+ "span",
368
2967
  {
369
- ref,
370
- type: "button",
371
- role: "switch",
372
- "aria-checked": checked,
373
- "aria-label": computedAriaLabel,
374
- "aria-labelledby": computedAriaLabelledby,
375
- disabled,
376
- onClick: handleClick,
377
- onKeyDown: handleKeyDown,
378
- className,
379
- style: { ...trackStructuralStyles, ...trackVisualStyles },
380
- tabIndex: disabled ? -1 : 0,
381
- "data-compa11y-switch": true,
382
- "data-checked": checked,
383
- "data-disabled": disabled || void 0,
384
- "data-size": size,
385
- ...props,
386
- onFocus: (e) => {
387
- if (!unstyled) {
388
- e.currentTarget.style.outline = "2px solid #0066cc";
389
- e.currentTarget.style.outlineOffset = "2px";
390
- }
391
- props.onFocus?.(e);
392
- },
393
- onBlur: (e) => {
394
- if (!unstyled) {
395
- e.currentTarget.style.outline = "none";
396
- }
397
- props.onBlur?.(e);
398
- },
399
- children: /* @__PURE__ */ jsxRuntime.jsx(
400
- "span",
401
- {
402
- style: { ...thumbStructuralStyles, ...thumbVisualStyles },
403
- "data-compa11y-switch-thumb": true,
404
- "aria-hidden": "true"
405
- }
406
- )
2968
+ "data-compa11y-button-spinner": "",
2969
+ "aria-hidden": "true",
2970
+ style: unstyled ? {} : {
2971
+ display: "inline-block",
2972
+ width: "1em",
2973
+ height: "1em",
2974
+ border: "2px solid currentColor",
2975
+ borderRightColor: "transparent",
2976
+ borderRadius: "50%",
2977
+ animation: "compa11y-spin 0.6s linear infinite"
2978
+ }
407
2979
  }
408
2980
  ),
409
- label && /* @__PURE__ */ jsxRuntime.jsx(
410
- "label",
2981
+ children
2982
+ ]
2983
+ }
2984
+ );
2985
+ }
2986
+ );
2987
+ Button.displayName = "Button";
2988
+ var TextareaContext = react.createContext(null);
2989
+ function useTextareaContext() {
2990
+ const ctx = react.useContext(TextareaContext);
2991
+ if (!ctx) {
2992
+ throw new Error(
2993
+ "[Compa11y Textarea]: Textarea sub-components (Textarea.Label, Textarea.Field, etc.) must be used within <Textarea>."
2994
+ );
2995
+ }
2996
+ return ctx;
2997
+ }
2998
+ var TextareaLabel = react.forwardRef(
2999
+ function TextareaLabel2({ children, className }, ref) {
3000
+ const ctx = useTextareaContext();
3001
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3002
+ "label",
3003
+ {
3004
+ ref,
3005
+ id: ctx.labelId,
3006
+ htmlFor: ctx.fieldId,
3007
+ "data-compa11y-textarea-label": "",
3008
+ className,
3009
+ style: ctx.unstyled ? {} : {
3010
+ display: "block",
3011
+ color: ctx.disabled ? "var(--compa11y-textarea-disabled-color, #999)" : "var(--compa11y-textarea-label-color, inherit)",
3012
+ fontSize: "var(--compa11y-textarea-label-size, 0.875rem)",
3013
+ fontWeight: "var(--compa11y-textarea-label-weight, 500)"
3014
+ },
3015
+ children: [
3016
+ children,
3017
+ ctx.required && /* @__PURE__ */ jsxRuntime.jsx(
3018
+ "span",
411
3019
  {
412
- id: labelId,
413
- onClick: disabled ? void 0 : () => toggleSwitch(),
414
- style: labelStyles,
415
- "data-compa11y-switch-label": true,
416
- children: label
3020
+ "data-compa11y-textarea-required": "",
3021
+ "aria-hidden": "true",
3022
+ style: ctx.unstyled ? {} : {
3023
+ color: "var(--compa11y-textarea-required-color, #ef4444)",
3024
+ marginLeft: "0.125rem"
3025
+ },
3026
+ children: "*"
417
3027
  }
418
3028
  )
419
3029
  ]
@@ -421,7 +3031,375 @@ var Switch = react.forwardRef(
421
3031
  );
422
3032
  }
423
3033
  );
424
- Switch.displayName = "Switch";
3034
+ TextareaLabel.displayName = "TextareaLabel";
3035
+ var TextareaField = react.forwardRef(
3036
+ function TextareaField2({
3037
+ onFocus: providedOnFocus,
3038
+ onBlur: providedOnBlur,
3039
+ className,
3040
+ style,
3041
+ rows: rowsOverride,
3042
+ ...rest
3043
+ }, ref) {
3044
+ const ctx = useTextareaContext();
3045
+ const textareaRef = react.useRef(null);
3046
+ const mergedRef = react.useCallback(
3047
+ (node) => {
3048
+ textareaRef.current = node;
3049
+ if (typeof ref === "function") {
3050
+ ref(node);
3051
+ } else if (ref) {
3052
+ ref.current = node;
3053
+ }
3054
+ },
3055
+ [ref]
3056
+ );
3057
+ const handleChange = react.useCallback(
3058
+ (event) => {
3059
+ ctx.setValue(event.target.value);
3060
+ },
3061
+ [ctx.setValue]
3062
+ );
3063
+ const handleFocus = react.useCallback(
3064
+ (event) => {
3065
+ ctx.focusProps.onFocus();
3066
+ providedOnFocus?.(event);
3067
+ },
3068
+ [ctx.focusProps, providedOnFocus]
3069
+ );
3070
+ const handleBlur = react.useCallback(
3071
+ (event) => {
3072
+ ctx.focusProps.onBlur();
3073
+ providedOnBlur?.(event);
3074
+ },
3075
+ [ctx.focusProps, providedOnBlur]
3076
+ );
3077
+ const describedByParts = [];
3078
+ describedByParts.push(ctx.hintId);
3079
+ if (ctx.hasError) {
3080
+ describedByParts.push(ctx.errorId);
3081
+ }
3082
+ return /* @__PURE__ */ jsxRuntime.jsx(
3083
+ "textarea",
3084
+ {
3085
+ ref: mergedRef,
3086
+ id: ctx.fieldId,
3087
+ rows: rowsOverride ?? ctx.rows,
3088
+ value: ctx.value,
3089
+ onChange: handleChange,
3090
+ onFocus: handleFocus,
3091
+ onBlur: handleBlur,
3092
+ disabled: ctx.disabled,
3093
+ readOnly: ctx.readOnly,
3094
+ "aria-describedby": describedByParts.join(" ") || void 0,
3095
+ "aria-invalid": ctx.hasError ? "true" : void 0,
3096
+ "aria-required": ctx.required ? "true" : void 0,
3097
+ "data-compa11y-textarea-field": "",
3098
+ className,
3099
+ style: ctx.unstyled ? style : {
3100
+ width: "100%",
3101
+ padding: "var(--compa11y-textarea-padding, 0.5rem 0.75rem)",
3102
+ border: ctx.hasError ? "1px solid var(--compa11y-textarea-border-error, #ef4444)" : "var(--compa11y-textarea-border, 1px solid #ccc)",
3103
+ borderRadius: "var(--compa11y-textarea-radius, 4px)",
3104
+ fontSize: "var(--compa11y-textarea-font-size, 0.875rem)",
3105
+ fontFamily: "inherit",
3106
+ lineHeight: "1.5",
3107
+ resize: ctx.resize,
3108
+ background: ctx.disabled ? "var(--compa11y-textarea-disabled-bg, #f5f5f5)" : ctx.readOnly ? "var(--compa11y-textarea-readonly-bg, #f9f9f9)" : "var(--compa11y-textarea-bg, white)",
3109
+ color: "inherit",
3110
+ cursor: ctx.disabled ? "not-allowed" : void 0,
3111
+ opacity: ctx.disabled ? "var(--compa11y-textarea-disabled-opacity, 0.7)" : void 0,
3112
+ ...ctx.isFocusVisible && !ctx.disabled ? {
3113
+ outline: ctx.hasError ? "2px solid var(--compa11y-textarea-border-error, #ef4444)" : "2px solid var(--compa11y-focus-color, #0066cc)",
3114
+ outlineOffset: "-1px",
3115
+ borderColor: ctx.hasError ? "var(--compa11y-textarea-border-error, #ef4444)" : "var(--compa11y-textarea-border-focus, #0066cc)"
3116
+ } : {},
3117
+ ...style
3118
+ },
3119
+ ...rest
3120
+ }
3121
+ );
3122
+ }
3123
+ );
3124
+ TextareaField.displayName = "TextareaField";
3125
+ var TextareaHint = react.forwardRef(
3126
+ function TextareaHint2({ children, className }, ref) {
3127
+ const ctx = useTextareaContext();
3128
+ return /* @__PURE__ */ jsxRuntime.jsx(
3129
+ "div",
3130
+ {
3131
+ ref,
3132
+ id: ctx.hintId,
3133
+ "data-compa11y-textarea-hint": "",
3134
+ className,
3135
+ style: ctx.unstyled ? {} : {
3136
+ color: "var(--compa11y-textarea-hint-color, #666)",
3137
+ fontSize: "var(--compa11y-textarea-hint-size, 0.8125rem)"
3138
+ },
3139
+ children
3140
+ }
3141
+ );
3142
+ }
3143
+ );
3144
+ TextareaHint.displayName = "TextareaHint";
3145
+ var TextareaError = react.forwardRef(
3146
+ function TextareaError2({ children, className }, ref) {
3147
+ const ctx = useTextareaContext();
3148
+ if (!children) return null;
3149
+ return /* @__PURE__ */ jsxRuntime.jsx(
3150
+ "div",
3151
+ {
3152
+ ref,
3153
+ id: ctx.errorId,
3154
+ role: "alert",
3155
+ "data-compa11y-textarea-error": "",
3156
+ className,
3157
+ style: ctx.unstyled ? {} : {
3158
+ color: "var(--compa11y-textarea-error-color, #ef4444)",
3159
+ fontSize: "var(--compa11y-textarea-error-size, 0.8125rem)"
3160
+ },
3161
+ children
3162
+ }
3163
+ );
3164
+ }
3165
+ );
3166
+ TextareaError.displayName = "TextareaError";
3167
+ var Textarea = react.forwardRef(
3168
+ function Textarea2({
3169
+ value: controlledValue,
3170
+ defaultValue = "",
3171
+ onValueChange,
3172
+ rows = 3,
3173
+ resize = "vertical",
3174
+ label,
3175
+ hint,
3176
+ error,
3177
+ required = false,
3178
+ disabled = false,
3179
+ readOnly = false,
3180
+ "aria-label": ariaLabel,
3181
+ "aria-labelledby": ariaLabelledBy,
3182
+ placeholder,
3183
+ name,
3184
+ autoComplete,
3185
+ maxLength,
3186
+ minLength,
3187
+ id: providedId,
3188
+ unstyled = false,
3189
+ className,
3190
+ onFocus: providedOnFocus,
3191
+ onBlur: providedOnBlur,
3192
+ children
3193
+ }, ref) {
3194
+ const generatedId = chunk52J4Z3QD_cjs.useId("textarea");
3195
+ const baseId = providedId || generatedId;
3196
+ const fieldId = `${baseId}-field`;
3197
+ const labelId = `${baseId}-label`;
3198
+ const hintId = `${baseId}-hint`;
3199
+ const errorId = `${baseId}-error`;
3200
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(defaultValue);
3201
+ const isControlled = controlledValue !== void 0;
3202
+ const currentValue = isControlled ? controlledValue : uncontrolledValue;
3203
+ const setValue = react.useCallback(
3204
+ (newValue) => {
3205
+ if (!isControlled) {
3206
+ setUncontrolledValue(newValue);
3207
+ }
3208
+ onValueChange?.(newValue);
3209
+ },
3210
+ [isControlled, onValueChange]
3211
+ );
3212
+ const { isFocusVisible, focusProps } = useFocusVisible();
3213
+ react.useEffect(() => {
3214
+ if (process.env.NODE_ENV !== "production") {
3215
+ const isCompound2 = react.Children.count(children) > 0;
3216
+ if (!isCompound2 && !label && !ariaLabel && !ariaLabelledBy) {
3217
+ console.warn(
3218
+ "[Compa11y Textarea]: Textarea has no accessible label. Screen readers need this to identify the textarea. Use label prop, aria-label, or aria-labelledby."
3219
+ );
3220
+ }
3221
+ }
3222
+ }, [children, label, ariaLabel, ariaLabelledBy]);
3223
+ const hasError = Boolean(error);
3224
+ const isCompound = react.Children.count(children) > 0;
3225
+ const contextValue = {
3226
+ fieldId,
3227
+ labelId,
3228
+ hintId,
3229
+ errorId,
3230
+ value: currentValue,
3231
+ setValue,
3232
+ hasError,
3233
+ disabled,
3234
+ readOnly,
3235
+ required,
3236
+ rows,
3237
+ resize,
3238
+ isFocusVisible,
3239
+ focusProps,
3240
+ unstyled
3241
+ };
3242
+ const dataAttrs = {
3243
+ "data-compa11y-textarea": "",
3244
+ "data-error": hasError ? "true" : "false",
3245
+ "data-disabled": disabled ? "true" : "false",
3246
+ "data-required": required ? "true" : "false",
3247
+ "data-readonly": readOnly ? "true" : "false"
3248
+ };
3249
+ const wrapperStyle = unstyled ? {} : {
3250
+ display: "flex",
3251
+ flexDirection: "column",
3252
+ gap: "0.25rem"
3253
+ };
3254
+ if (isCompound) {
3255
+ return /* @__PURE__ */ jsxRuntime.jsx(TextareaContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ...dataAttrs, className, style: wrapperStyle, children }) });
3256
+ }
3257
+ const textareaRef = react.useRef(null);
3258
+ const mergedRef = react.useCallback(
3259
+ (node) => {
3260
+ textareaRef.current = node;
3261
+ if (typeof ref === "function") {
3262
+ ref(node);
3263
+ } else if (ref) {
3264
+ ref.current = node;
3265
+ }
3266
+ },
3267
+ [ref]
3268
+ );
3269
+ const handleChange = react.useCallback(
3270
+ (event) => {
3271
+ setValue(event.target.value);
3272
+ },
3273
+ [setValue]
3274
+ );
3275
+ const handleFocus = react.useCallback(
3276
+ (event) => {
3277
+ focusProps.onFocus();
3278
+ providedOnFocus?.(event);
3279
+ },
3280
+ [focusProps, providedOnFocus]
3281
+ );
3282
+ const handleBlur = react.useCallback(
3283
+ (event) => {
3284
+ focusProps.onBlur();
3285
+ providedOnBlur?.(event);
3286
+ },
3287
+ [focusProps, providedOnBlur]
3288
+ );
3289
+ const describedByParts = [];
3290
+ if (hint) describedByParts.push(hintId);
3291
+ if (hasError) describedByParts.push(errorId);
3292
+ const ariaDescribedBy = describedByParts.length > 0 ? describedByParts.join(" ") : void 0;
3293
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ...dataAttrs, className, style: wrapperStyle, children: [
3294
+ label && /* @__PURE__ */ jsxRuntime.jsxs(
3295
+ "label",
3296
+ {
3297
+ id: labelId,
3298
+ htmlFor: fieldId,
3299
+ "data-compa11y-textarea-label": "",
3300
+ style: unstyled ? {} : {
3301
+ display: "block",
3302
+ color: disabled ? "var(--compa11y-textarea-disabled-color, #999)" : "var(--compa11y-textarea-label-color, inherit)",
3303
+ fontSize: "var(--compa11y-textarea-label-size, 0.875rem)",
3304
+ fontWeight: "var(--compa11y-textarea-label-weight, 500)"
3305
+ },
3306
+ children: [
3307
+ label,
3308
+ required && /* @__PURE__ */ jsxRuntime.jsx(
3309
+ "span",
3310
+ {
3311
+ "data-compa11y-textarea-required": "",
3312
+ "aria-hidden": "true",
3313
+ style: unstyled ? {} : {
3314
+ color: "var(--compa11y-textarea-required-color, #ef4444)",
3315
+ marginLeft: "0.125rem"
3316
+ },
3317
+ children: "*"
3318
+ }
3319
+ )
3320
+ ]
3321
+ }
3322
+ ),
3323
+ /* @__PURE__ */ jsxRuntime.jsx(
3324
+ "textarea",
3325
+ {
3326
+ ref: mergedRef,
3327
+ id: fieldId,
3328
+ rows,
3329
+ value: currentValue,
3330
+ onChange: handleChange,
3331
+ onFocus: handleFocus,
3332
+ onBlur: handleBlur,
3333
+ disabled,
3334
+ readOnly,
3335
+ required,
3336
+ placeholder,
3337
+ name,
3338
+ autoComplete,
3339
+ maxLength,
3340
+ minLength,
3341
+ "aria-label": !label ? ariaLabel : void 0,
3342
+ "aria-labelledby": !label && ariaLabelledBy ? ariaLabelledBy : label ? labelId : void 0,
3343
+ "aria-describedby": ariaDescribedBy,
3344
+ "aria-invalid": hasError ? "true" : void 0,
3345
+ "aria-required": required ? "true" : void 0,
3346
+ "data-compa11y-textarea-field": "",
3347
+ style: unstyled ? {} : {
3348
+ width: "100%",
3349
+ padding: "var(--compa11y-textarea-padding, 0.5rem 0.75rem)",
3350
+ border: hasError ? "1px solid var(--compa11y-textarea-border-error, #ef4444)" : "var(--compa11y-textarea-border, 1px solid #ccc)",
3351
+ borderRadius: "var(--compa11y-textarea-radius, 4px)",
3352
+ fontSize: "var(--compa11y-textarea-font-size, 0.875rem)",
3353
+ fontFamily: "inherit",
3354
+ lineHeight: "1.5",
3355
+ resize,
3356
+ background: disabled ? "var(--compa11y-textarea-disabled-bg, #f5f5f5)" : readOnly ? "var(--compa11y-textarea-readonly-bg, #f9f9f9)" : "var(--compa11y-textarea-bg, white)",
3357
+ color: "inherit",
3358
+ cursor: disabled ? "not-allowed" : void 0,
3359
+ opacity: disabled ? "var(--compa11y-textarea-disabled-opacity, 0.7)" : void 0,
3360
+ ...isFocusVisible && !disabled ? {
3361
+ outline: hasError ? "2px solid var(--compa11y-textarea-border-error, #ef4444)" : "2px solid var(--compa11y-focus-color, #0066cc)",
3362
+ outlineOffset: "-1px",
3363
+ borderColor: hasError ? "var(--compa11y-textarea-border-error, #ef4444)" : "var(--compa11y-textarea-border-focus, #0066cc)"
3364
+ } : {}
3365
+ }
3366
+ }
3367
+ ),
3368
+ hint && /* @__PURE__ */ jsxRuntime.jsx(
3369
+ "div",
3370
+ {
3371
+ id: hintId,
3372
+ "data-compa11y-textarea-hint": "",
3373
+ style: unstyled ? {} : {
3374
+ color: "var(--compa11y-textarea-hint-color, #666)",
3375
+ fontSize: "var(--compa11y-textarea-hint-size, 0.8125rem)"
3376
+ },
3377
+ children: hint
3378
+ }
3379
+ ),
3380
+ hasError && /* @__PURE__ */ jsxRuntime.jsx(
3381
+ "div",
3382
+ {
3383
+ id: errorId,
3384
+ role: "alert",
3385
+ "data-compa11y-textarea-error": "",
3386
+ style: unstyled ? {} : {
3387
+ color: "var(--compa11y-textarea-error-color, #ef4444)",
3388
+ fontSize: "var(--compa11y-textarea-error-size, 0.8125rem)"
3389
+ },
3390
+ children: error
3391
+ }
3392
+ )
3393
+ ] });
3394
+ }
3395
+ );
3396
+ Textarea.displayName = "Textarea";
3397
+ var TextareaCompound = Object.assign(Textarea, {
3398
+ Label: TextareaLabel,
3399
+ Field: TextareaField,
3400
+ Hint: TextareaHint,
3401
+ Error: TextareaError
3402
+ });
425
3403
 
426
3404
  Object.defineProperty(exports, "Dialog", {
427
3405
  enumerable: true,
@@ -691,12 +3669,46 @@ Object.defineProperty(exports, "warn", {
691
3669
  enumerable: true,
692
3670
  get: function () { return core.warn; }
693
3671
  });
3672
+ exports.Button = Button;
3673
+ exports.Checkbox = CheckboxCompound;
3674
+ exports.CheckboxBase = Checkbox;
3675
+ exports.CheckboxGroup = CheckboxGroup;
3676
+ exports.CheckboxIndicator = CheckboxIndicator;
3677
+ exports.Input = InputCompound;
3678
+ exports.InputBase = Input;
3679
+ exports.InputError = InputError;
3680
+ exports.InputField = InputField;
3681
+ exports.InputHint = InputHint;
3682
+ exports.InputLabel = InputLabel;
3683
+ exports.Listbox = ListboxCompound;
3684
+ exports.ListboxBase = Listbox;
3685
+ exports.ListboxGroup = ListboxGroup;
3686
+ exports.ListboxOption = ListboxOption;
3687
+ exports.Radio = Radio;
3688
+ exports.RadioGroup = RadioGroupCompound;
3689
+ exports.RadioGroupBase = RadioGroup;
3690
+ exports.Select = SelectCompound;
3691
+ exports.SelectBase = Select;
3692
+ exports.SelectListbox = SelectListbox;
3693
+ exports.SelectOptionItem = SelectOptionItem;
3694
+ exports.SelectTrigger = SelectTrigger;
694
3695
  exports.Switch = Switch;
3696
+ exports.Textarea = TextareaCompound;
3697
+ exports.TextareaBase = Textarea;
3698
+ exports.TextareaError = TextareaError;
3699
+ exports.TextareaField = TextareaField;
3700
+ exports.TextareaHint = TextareaHint;
3701
+ exports.TextareaLabel = TextareaLabel;
3702
+ exports.useCheckboxGroupContext = useCheckboxGroupContext;
695
3703
  exports.useFocusControl = useFocusControl;
696
3704
  exports.useFocusManager = useFocusManager;
697
3705
  exports.useFocusVisible = useFocusVisible;
698
3706
  exports.useFocusWithin = useFocusWithin;
3707
+ exports.useInputContext = useInputContext;
3708
+ exports.useListboxContext = useListboxContext;
3709
+ exports.useRadioGroupContext = useRadioGroupContext;
699
3710
  exports.useRovingTabindex = useRovingTabindex;
700
3711
  exports.useRovingTabindexMap = useRovingTabindexMap;
3712
+ exports.useTextareaContext = useTextareaContext;
701
3713
  //# sourceMappingURL=index.cjs.map
702
3714
  //# sourceMappingURL=index.cjs.map