frey-ui 1.0.13 → 1.0.14

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.
Files changed (58) hide show
  1. package/dist/cjs/Accordion/accordion.css +13 -0
  2. package/dist/cjs/Accordion/accordion.module.css.cjs +1 -1
  3. package/dist/cjs/Accordion/index.cjs +40 -13
  4. package/dist/cjs/Checkbox/checkbox.css +1 -1
  5. package/dist/cjs/Checkbox/checkbox.module.css.cjs +1 -1
  6. package/dist/cjs/Checkbox/index.cjs +4 -14
  7. package/dist/cjs/Combobox/combobox.css +140 -0
  8. package/dist/cjs/Combobox/combobox.module.css.cjs +10 -0
  9. package/dist/cjs/Combobox/index.cjs +191 -0
  10. package/dist/cjs/Dialog/dialog.css +13 -0
  11. package/dist/cjs/Dialog/index.cjs +2 -9
  12. package/dist/cjs/Popover/index.cjs +1 -1
  13. package/dist/cjs/Popover/popover.css +6 -0
  14. package/dist/cjs/Progress/index.cjs +1 -1
  15. package/dist/cjs/Progress/progress.css +13 -0
  16. package/dist/cjs/RadioGroup/index.cjs +3 -6
  17. package/dist/cjs/Spinner/index.cjs +1 -1
  18. package/dist/cjs/Switch/index.cjs +6 -3
  19. package/dist/cjs/Switch/switch.css +8 -1
  20. package/dist/cjs/Switch/switch.module.css.cjs +1 -1
  21. package/dist/cjs/Tabs/index.cjs +4 -8
  22. package/dist/cjs/TextInput/textinput.css +1 -1
  23. package/dist/cjs/ThemeProvider/index.cjs +10 -1
  24. package/dist/cjs/Toast/toast.css +11 -0
  25. package/dist/cjs/hooks/useControllableState.cjs +10 -2
  26. package/dist/cjs/index.cjs +46 -43
  27. package/dist/esm/Accordion/accordion.css +13 -0
  28. package/dist/esm/Accordion/accordion.module.css.mjs +1 -1
  29. package/dist/esm/Accordion/index.mjs +41 -14
  30. package/dist/esm/Checkbox/checkbox.css +1 -1
  31. package/dist/esm/Checkbox/checkbox.module.css.mjs +1 -1
  32. package/dist/esm/Checkbox/index.mjs +4 -14
  33. package/dist/esm/Combobox/combobox.css +140 -0
  34. package/dist/esm/Combobox/combobox.module.css.mjs +6 -0
  35. package/dist/esm/Combobox/index.mjs +187 -0
  36. package/dist/esm/Dialog/dialog.css +13 -0
  37. package/dist/esm/Dialog/index.mjs +3 -10
  38. package/dist/esm/Popover/index.mjs +1 -1
  39. package/dist/esm/Popover/popover.css +6 -0
  40. package/dist/esm/Progress/index.mjs +1 -1
  41. package/dist/esm/Progress/progress.css +13 -0
  42. package/dist/esm/RadioGroup/index.mjs +3 -6
  43. package/dist/esm/Spinner/index.mjs +1 -1
  44. package/dist/esm/Switch/index.mjs +6 -3
  45. package/dist/esm/Switch/switch.css +8 -1
  46. package/dist/esm/Switch/switch.module.css.mjs +1 -1
  47. package/dist/esm/Tabs/index.mjs +5 -9
  48. package/dist/esm/TextInput/textinput.css +1 -1
  49. package/dist/esm/ThemeProvider/index.mjs +11 -3
  50. package/dist/esm/Toast/toast.css +11 -0
  51. package/dist/esm/hooks/useControllableState.mjs +10 -3
  52. package/dist/esm/index.mjs +2 -1
  53. package/dist/index.d.ts +30 -2
  54. package/dist/types/src/Combobox/index.d.ts +25 -0
  55. package/dist/types/src/ThemeProvider/index.d.ts +1 -0
  56. package/dist/types/src/hooks/useControllableState.d.ts +5 -0
  57. package/dist/types/src/index.d.ts +4 -2
  58. package/package.json +14 -9
@@ -65,6 +65,11 @@
65
65
  grid-template-rows: 1fr;
66
66
  }
67
67
 
68
+ .accordion_content_wrapper_closed_58ee9305 {
69
+ visibility: hidden;
70
+ pointer-events: none;
71
+ }
72
+
68
73
  .accordion_content_945ef079 {
69
74
  overflow: hidden; /* Important for grid animation to hide contents when 0fr */
70
75
  padding-bottom: 1rem;
@@ -78,3 +83,11 @@
78
83
  .accordion_content_945ef079 {
79
84
  padding-bottom: 0;
80
85
  }
86
+
87
+ @media (prefers-reduced-motion: reduce) {
88
+ .accordion_trigger_1c357514,
89
+ .accordion_chevron_c55f991e,
90
+ .accordion_content_wrapper_357a0679 {
91
+ transition: none;
92
+ }
93
+ }
@@ -4,7 +4,7 @@ require('./accordion.css');
4
4
 
5
5
  Object.defineProperty(exports, '__esModule', { value: true });
6
6
 
7
- var styles = {"accordion":"accordion_8f91be6e","accordion_item":"accordion_item_2d6abc63","accordion_header":"accordion_header_c9933178","accordion_trigger":"accordion_trigger_1c357514","accordion_chevron":"accordion_chevron_c55f991e","accordion_chevron_open":"accordion_chevron_open_44560e91","accordion_content_wrapper":"accordion_content_wrapper_357a0679","accordion_content_wrapper_open":"accordion_content_wrapper_open_02841768","accordion_content":"accordion_content_945ef079"};
7
+ var styles = {"accordion":"accordion_8f91be6e","accordion_item":"accordion_item_2d6abc63","accordion_header":"accordion_header_c9933178","accordion_trigger":"accordion_trigger_1c357514","accordion_chevron":"accordion_chevron_c55f991e","accordion_chevron_open":"accordion_chevron_open_44560e91","accordion_content_wrapper":"accordion_content_wrapper_357a0679","accordion_content_wrapper_open":"accordion_content_wrapper_open_02841768","accordion_content_wrapper_closed":"accordion_content_wrapper_closed_58ee9305","accordion_content":"accordion_content_945ef079"};
8
8
 
9
9
  exports.default = styles;
10
10
  //# sourceMappingURL=accordion.module.css.cjs.map
@@ -6,9 +6,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var clsx = require('clsx');
8
8
  var React = require('react');
9
+ var useControllableState = require('../hooks/useControllableState.cjs');
9
10
  var ChevronDownIcon = require('../Icons/ChevronDownIcon.cjs');
11
+ var mergeRefs = require('../utils/mergeRefs.cjs');
10
12
  var accordion_module = require('./accordion.module.css.cjs');
11
13
 
14
+ const ACCORDION_FOCUSABLE_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]';
12
15
  const AccordionContext = React.createContext(null);
13
16
  function useAccordionContext() {
14
17
  const context = React.useContext(AccordionContext);
@@ -27,18 +30,13 @@ function useAccordionItemContext() {
27
30
  }
28
31
  const AccordionRoot = React.forwardRef(function Accordion({ type = 'single', value, defaultValue, onValueChange, className, ...props }, ref) {
29
32
  const defaultVal = defaultValue ?? (type === 'multiple' ? [] : '');
30
- const isControlled = value !== undefined;
31
- const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultVal);
32
- const currentValue = isControlled ? value : uncontrolledValue;
33
+ const [currentValue, setCurrentValue] = useControllableState.useControllableValue(value, defaultVal, onValueChange);
33
34
  const idPrefix = React.useId();
34
35
  const handleValueChange = React.useCallback((nextValue) => {
35
36
  let resolvedValue;
36
37
  if (type === 'single') {
37
38
  const valueAsStr = typeof currentValue === 'string' ? currentValue : '';
38
39
  resolvedValue = valueAsStr === nextValue ? '' : nextValue;
39
- if (!isControlled) {
40
- setUncontrolledValue(resolvedValue);
41
- }
42
40
  }
43
41
  else {
44
42
  const valueAsArray = Array.isArray(currentValue) ? currentValue : [];
@@ -46,13 +44,10 @@ const AccordionRoot = React.forwardRef(function Accordion({ type = 'single', val
46
44
  const nextArrayValue = isSelected
47
45
  ? valueAsArray.filter((v) => v !== nextValue)
48
46
  : [...valueAsArray, nextValue];
49
- if (!isControlled) {
50
- setUncontrolledValue(nextArrayValue);
51
- }
52
47
  resolvedValue = nextArrayValue;
53
48
  }
54
- onValueChange?.(resolvedValue);
55
- }, [type, currentValue, isControlled, onValueChange]);
49
+ setCurrentValue(resolvedValue);
50
+ }, [type, currentValue, setCurrentValue]);
56
51
  const contextValue = React.useMemo(() => ({
57
52
  type,
58
53
  value: currentValue,
@@ -83,8 +78,40 @@ AccordionTrigger.displayName = 'Accordion.Trigger';
83
78
  const AccordionContent = React.forwardRef(function AccordionContent({ className, children, ...props }, ref) {
84
79
  const { idPrefix } = useAccordionContext();
85
80
  const { value, isOpen } = useAccordionItemContext();
86
- return (jsxRuntime.jsx("section", { ref: ref, id: `${idPrefix}-content-${value}`, "aria-labelledby": `${idPrefix}-trigger-${value}`, className: clsx(accordion_module.default.accordion_content_wrapper, {
87
- [accordion_module.default.accordion_content_wrapper_open]: isOpen
81
+ const contentRef = React.useRef(null);
82
+ const mergedContentRef = React.useMemo(() => mergeRefs.mergeRefs(ref, contentRef), [ref]);
83
+ React.useEffect(() => {
84
+ const node = contentRef.current;
85
+ if (!node) {
86
+ return;
87
+ }
88
+ const focusableElements = node.querySelectorAll(ACCORDION_FOCUSABLE_SELECTOR);
89
+ focusableElements.forEach((element) => {
90
+ if (isOpen) {
91
+ const originalTabIndex = element.dataset.freyAccordionTabindex;
92
+ if (originalTabIndex === undefined) {
93
+ return;
94
+ }
95
+ if (originalTabIndex === '') {
96
+ element.removeAttribute('tabindex');
97
+ }
98
+ else {
99
+ element.setAttribute('tabindex', originalTabIndex);
100
+ }
101
+ delete element.dataset.freyAccordionTabindex;
102
+ return;
103
+ }
104
+ if (element.dataset.freyAccordionTabindex !== undefined) {
105
+ return;
106
+ }
107
+ const currentTabIndex = element.getAttribute('tabindex');
108
+ element.dataset.freyAccordionTabindex = currentTabIndex ?? '';
109
+ element.setAttribute('tabindex', '-1');
110
+ });
111
+ }, [isOpen]);
112
+ return (jsxRuntime.jsx("section", { ref: mergedContentRef, id: `${idPrefix}-content-${value}`, "aria-labelledby": `${idPrefix}-trigger-${value}`, "aria-hidden": !isOpen, inert: !isOpen, className: clsx(accordion_module.default.accordion_content_wrapper, {
113
+ [accordion_module.default.accordion_content_wrapper_open]: isOpen,
114
+ [accordion_module.default.accordion_content_wrapper_closed]: !isOpen
88
115
  }), ...props, children: jsxRuntime.jsx("div", { className: clsx(accordion_module.default.accordion_content, className), children: children }) }));
89
116
  });
90
117
  AccordionContent.displayName = 'Accordion.Content';
@@ -122,7 +122,7 @@
122
122
  cursor: not-allowed;
123
123
  }
124
124
 
125
- .visually-hidden_bf54f2cb {
125
+ .visually_hidden_420cf0cd {
126
126
  position: absolute;
127
127
  width: 1px;
128
128
  height: 1px;
@@ -4,7 +4,7 @@ require('./checkbox.css');
4
4
 
5
5
  Object.defineProperty(exports, '__esModule', { value: true });
6
6
 
7
- var styles = {"checkbox-container":"checkbox-container_61f229ee","checkbox":"checkbox_d32c8199","checkbox-sm":"checkbox-sm_a9268e0e","checkbox-md":"checkbox-md_2e4b6d59","checkbox-lg":"checkbox-lg_822bc43d","checkbox-box":"checkbox-box_d429074b","checkbox-disabled":"checkbox-disabled_29936a5d","check-icon":"check-icon_1beba8f1","indeterminate-icon":"indeterminate-icon_d2727941","label":"label_714abdf7","visually-hidden":"visually-hidden_bf54f2cb"};
7
+ var styles = {"checkbox-container":"checkbox-container_61f229ee","checkbox":"checkbox_d32c8199","checkbox-sm":"checkbox-sm_a9268e0e","checkbox-md":"checkbox-md_2e4b6d59","checkbox-lg":"checkbox-lg_822bc43d","checkbox-box":"checkbox-box_d429074b","checkbox-disabled":"checkbox-disabled_29936a5d","check-icon":"check-icon_1beba8f1","indeterminate-icon":"indeterminate-icon_d2727941","label":"label_714abdf7","visually_hidden":"visually_hidden_420cf0cd"};
8
8
 
9
9
  exports.default = styles;
10
10
  //# sourceMappingURL=checkbox.module.css.cjs.map
@@ -8,6 +8,7 @@ var clsx = require('clsx');
8
8
  var React = require('react');
9
9
  var CheckIcon = require('../Icons/CheckIcon.cjs');
10
10
  var MinusIcon = require('../Icons/MinusIcon.cjs');
11
+ var mergeRefs = require('../utils/mergeRefs.cjs');
11
12
  var checkbox_module = require('./checkbox.module.css.cjs');
12
13
 
13
14
  const SizeClassMap = {
@@ -19,27 +20,16 @@ const Checkbox = React.forwardRef(function Checkbox({ label, hideLabel = false,
19
20
  const generatedId = React.useId();
20
21
  const inputId = id ?? generatedId;
21
22
  const internalRef = React.useRef(null);
23
+ const mergedRef = mergeRefs.mergeRefs(internalRef, forwardedRef);
22
24
  React.useLayoutEffect(() => {
23
25
  if (internalRef.current) {
24
26
  internalRef.current.indeterminate = indeterminate;
25
27
  }
26
28
  }, [indeterminate]);
27
- const setRef = (node) => {
28
- internalRef.current = node;
29
- if (node) {
30
- node.indeterminate = indeterminate;
31
- }
32
- if (typeof forwardedRef === 'function') {
33
- forwardedRef(node);
34
- }
35
- else if (forwardedRef) {
36
- forwardedRef.current = node;
37
- }
38
- };
39
29
  return (jsxRuntime.jsxs("div", { className: clsx(checkbox_module.default['checkbox-container'], className), style: style, children: [jsxRuntime.jsxs("span", { className: clsx(checkbox_module.default.checkbox, SizeClassMap[size], {
40
30
  [checkbox_module.default['checkbox-disabled']]: disabled
41
- }), "aria-disabled": disabled || undefined, children: [jsxRuntime.jsx("input", { type: 'checkbox', id: inputId, disabled: disabled, ref: setRef, "aria-checked": indeterminate ? 'mixed' : undefined, ...inputProps }), jsxRuntime.jsxs("span", { className: checkbox_module.default['checkbox-box'], "aria-hidden": 'true', children: [jsxRuntime.jsx(CheckIcon.CheckIcon, { className: checkbox_module.default['check-icon'], strokeWidth: 'bold', size: 'md' }), jsxRuntime.jsx(MinusIcon.MinusIcon, { className: checkbox_module.default['indeterminate-icon'], strokeWidth: 'bold', size: 'md' })] })] }), jsxRuntime.jsx("label", { htmlFor: inputId, className: clsx(checkbox_module.default.label, {
42
- [checkbox_module.default['visually-hidden']]: hideLabel
31
+ }), "aria-disabled": disabled || undefined, children: [jsxRuntime.jsx("input", { type: 'checkbox', id: inputId, disabled: disabled, ref: mergedRef, "aria-checked": indeterminate ? 'mixed' : undefined, ...inputProps }), jsxRuntime.jsxs("span", { className: checkbox_module.default['checkbox-box'], "aria-hidden": 'true', children: [jsxRuntime.jsx(CheckIcon.CheckIcon, { className: checkbox_module.default['check-icon'], strokeWidth: 'bold', size: 'md' }), jsxRuntime.jsx(MinusIcon.MinusIcon, { className: checkbox_module.default['indeterminate-icon'], strokeWidth: 'bold', size: 'md' })] })] }), jsxRuntime.jsx("label", { htmlFor: inputId, className: clsx(checkbox_module.default.label, {
32
+ [checkbox_module.default.visually_hidden]: hideLabel
43
33
  }), children: label })] }));
44
34
  });
45
35
  Checkbox.displayName = 'Checkbox';
@@ -0,0 +1,140 @@
1
+ .combobox_root_09637419 {
2
+ position: relative;
3
+ width: 100%;
4
+ }
5
+
6
+ .combobox_input_wrapper_7fd4a338 {
7
+ position: relative;
8
+ width: 100%;
9
+ }
10
+
11
+ .combobox_input_51f796ee {
12
+ width: 100%;
13
+ color: var(--frey-color-text-primary);
14
+ background-color: var(--frey-color-surface);
15
+ border: 1px solid var(--frey-color-border, #d1d5db);
16
+ border-radius: 0.375rem;
17
+ outline: none;
18
+ transition:
19
+ border-color 0.2s,
20
+ box-shadow 0.2s;
21
+ box-sizing: border-box;
22
+ font-family: inherit;
23
+ padding-right: 2.25rem;
24
+ }
25
+
26
+ .combobox_input_51f796ee:focus-visible {
27
+ border-color: var(--frey-focus-ring);
28
+ box-shadow: 0 0 0 2px var(--frey-focus-ring);
29
+ }
30
+
31
+ .combobox_input_51f796ee:disabled {
32
+ opacity: 0.5;
33
+ cursor: not-allowed;
34
+ background-color: var(--frey-color-surface-subtle, #f1f1f1);
35
+ }
36
+
37
+ .combobox_input_error_c2170046 {
38
+ border-color: var(--frey-color-error, #dc2626);
39
+ }
40
+
41
+ .combobox_input_error_c2170046:focus-visible {
42
+ border-color: var(--frey-color-error, #dc2626);
43
+ box-shadow: 0 0 0 2px var(--frey-color-error, #dc2626);
44
+ }
45
+
46
+ .combobox_input_sm_058a1114 {
47
+ min-height: 2rem;
48
+ padding-block: 0.25rem;
49
+ padding-inline: 0.5rem 2rem;
50
+ font-size: 0.8125rem;
51
+ }
52
+
53
+ .combobox_input_md_8aee06ca {
54
+ min-height: 2.5rem;
55
+ padding-block: 0.5rem;
56
+ padding-inline: 0.75rem 2rem;
57
+ font-size: 0.875rem;
58
+ }
59
+
60
+ .combobox_input_lg_4b0a7fac {
61
+ min-height: 2.875rem;
62
+ padding-block: 0.625rem;
63
+ padding-inline: 0.875rem 2rem;
64
+ font-size: 0.9375rem;
65
+ }
66
+
67
+ .combobox_icon_57742fb0 {
68
+ position: absolute;
69
+ top: 50%;
70
+ right: 0.75rem;
71
+ transform: translateY(-50%);
72
+ pointer-events: none;
73
+ color: var(--frey-color-text-secondary, #6b7280);
74
+ width: 1rem;
75
+ height: 1rem;
76
+ }
77
+
78
+ .combobox_listbox_ec63ee60 {
79
+ position: absolute;
80
+ top: calc(100% + 0.25rem);
81
+ left: 0;
82
+ right: 0;
83
+ margin: 0;
84
+ padding: 0.25rem;
85
+ list-style: none;
86
+ max-height: 14rem;
87
+ overflow-y: auto;
88
+ background: var(--frey-color-surface, #ffffff);
89
+ border: 1px solid var(--frey-color-border, #d1d5db);
90
+ border-radius: 0.5rem;
91
+ box-shadow: var(--frey-shadow-md, 0 10px 30px rgb(0 0 0 / 12%));
92
+ z-index: 20;
93
+ display: grid;
94
+ gap: 0.125rem;
95
+ }
96
+
97
+ .combobox_option_1440ebc4 {
98
+ display: flex;
99
+ align-items: center;
100
+ width: 100%;
101
+ min-height: 2rem;
102
+ padding: 0.375rem 0.5rem;
103
+ border-radius: 0.375rem;
104
+ border: none;
105
+ background-color: transparent;
106
+ color: var(--frey-color-text-primary, #111827);
107
+ text-align: left;
108
+ cursor: pointer;
109
+ transition:
110
+ background-color 0.16s ease,
111
+ color 0.16s ease;
112
+ }
113
+
114
+ .combobox_option_1440ebc4:focus-visible {
115
+ outline: 2px solid var(--frey-focus-ring);
116
+ outline-offset: 1px;
117
+ }
118
+
119
+ .combobox_option_1440ebc4:hover {
120
+ background-color: var(--frey-color-surface-subtle, #f3f4f6);
121
+ }
122
+
123
+ .combobox_option_active_01b209b7 {
124
+ background-color: var(--frey-color-surface-subtle, #f3f4f6);
125
+ }
126
+
127
+ .combobox_option_disabled_36362911 {
128
+ opacity: 0.55;
129
+ cursor: not-allowed;
130
+ }
131
+
132
+ .combobox_empty_state_3311246e {
133
+ margin: 0;
134
+ min-height: 2rem;
135
+ display: flex;
136
+ align-items: center;
137
+ padding: 0.375rem 0.5rem;
138
+ color: var(--frey-color-text-muted, #6b7280);
139
+ font-size: 0.875rem;
140
+ }
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ 'use strict';
3
+ require('./combobox.css');
4
+
5
+ Object.defineProperty(exports, '__esModule', { value: true });
6
+
7
+ var styles = {"combobox_root":"combobox_root_09637419","combobox_input_wrapper":"combobox_input_wrapper_7fd4a338","combobox_input":"combobox_input_51f796ee","combobox_input_error":"combobox_input_error_c2170046","combobox_input_sm":"combobox_input_sm_058a1114","combobox_input_md":"combobox_input_md_8aee06ca","combobox_input_lg":"combobox_input_lg_4b0a7fac","combobox_icon":"combobox_icon_57742fb0","combobox_listbox":"combobox_listbox_ec63ee60","combobox_option":"combobox_option_1440ebc4","combobox_option_active":"combobox_option_active_01b209b7","combobox_option_disabled":"combobox_option_disabled_36362911","combobox_empty_state":"combobox_empty_state_3311246e"};
8
+
9
+ exports.default = styles;
10
+ //# sourceMappingURL=combobox.module.css.cjs.map
@@ -0,0 +1,191 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var clsx = require('clsx');
8
+ var React = require('react');
9
+ var index = require('../Field/index.cjs');
10
+ var useControllableState = require('../hooks/useControllableState.cjs');
11
+ var ChevronDownIcon = require('../Icons/ChevronDownIcon.cjs');
12
+ var aria = require('../utils/aria.cjs');
13
+ var combobox_module = require('./combobox.module.css.cjs');
14
+
15
+ const SizeClassMap = {
16
+ sm: combobox_module.default.combobox_input_sm,
17
+ md: combobox_module.default.combobox_input_md,
18
+ lg: combobox_module.default.combobox_input_lg
19
+ };
20
+ function findNextEnabledOptionIndex(options, startIndex, direction) {
21
+ if (options.length === 0) {
22
+ return -1;
23
+ }
24
+ let currentIndex = startIndex;
25
+ for (const _option of options) {
26
+ currentIndex = (currentIndex + direction + options.length) % options.length;
27
+ if (!options[currentIndex]?.disabled) {
28
+ return currentIndex;
29
+ }
30
+ }
31
+ return -1;
32
+ }
33
+ function dispatchInputChangeEvent(input, nextValue) {
34
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(globalThis.HTMLInputElement.prototype, 'value')?.set;
35
+ const setInputValue = nativeInputValueSetter ??
36
+ function setInputValueFallback(value) {
37
+ this.value = value;
38
+ };
39
+ setInputValue.call(input, nextValue);
40
+ input.dispatchEvent(new Event('input', { bubbles: true }));
41
+ }
42
+ const Combobox = React.forwardRef(function Combobox({ label, options, hideLabel = false, error, helperText, size = 'md', value, defaultValue, onChange, noResultsText = 'No results found', className, style, id, disabled = false, required = false, autoComplete, onFocus, onBlur, onClick, onKeyDown, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, ...inputProps }, ref) {
43
+ const [currentValue, setCurrentValue] = useControllableState.useControllableValue(value, defaultValue ?? '');
44
+ const [open, setOpen] = React.useState(false);
45
+ const [activeIndex, setActiveIndex] = React.useState(-1);
46
+ const rootRef = React.useRef(null);
47
+ const inputRef = React.useRef(null);
48
+ const suppressOpenOnNextInputChangeRef = React.useRef(false);
49
+ const query = currentValue.trim().toLowerCase();
50
+ const filteredOptions = React.useMemo(() => {
51
+ if (!query) {
52
+ return options;
53
+ }
54
+ return options.filter((option) => {
55
+ const candidate = `${option.label} ${option.value}`.toLowerCase();
56
+ return candidate.includes(query);
57
+ });
58
+ }, [options, query]);
59
+ const closeOptions = React.useCallback(() => {
60
+ setOpen(false);
61
+ setActiveIndex(-1);
62
+ }, []);
63
+ const selectOption = React.useCallback((option) => {
64
+ setCurrentValue(option.label);
65
+ closeOptions();
66
+ if (onChange && inputRef.current) {
67
+ suppressOpenOnNextInputChangeRef.current = true;
68
+ dispatchInputChangeEvent(inputRef.current, option.label);
69
+ }
70
+ }, [closeOptions, onChange, setCurrentValue]);
71
+ React.useEffect(() => {
72
+ if (disabled) {
73
+ closeOptions();
74
+ }
75
+ }, [closeOptions, disabled]);
76
+ React.useEffect(() => {
77
+ if (!open) {
78
+ return undefined;
79
+ }
80
+ const handlePointerDown = (event) => {
81
+ if (rootRef.current &&
82
+ event.target instanceof Node &&
83
+ !rootRef.current.contains(event.target)) {
84
+ closeOptions();
85
+ }
86
+ };
87
+ document.addEventListener('mousedown', handlePointerDown);
88
+ return () => {
89
+ document.removeEventListener('mousedown', handlePointerDown);
90
+ };
91
+ }, [closeOptions, open]);
92
+ return (jsxRuntime.jsx(index.default, { label: label, hideLabel: hideLabel, error: error, helperText: helperText, disabled: disabled, required: required, id: id, className: className, style: style, children: ({ inputId, describedBy, hasError }) => {
93
+ const listboxId = `${inputId}-listbox`;
94
+ const activeOptionId = activeIndex >= 0 && activeIndex < filteredOptions.length
95
+ ? `${inputId}-option-${activeIndex}`
96
+ : undefined;
97
+ const isPopupVisible = open && !disabled;
98
+ const isListboxVisible = isPopupVisible && filteredOptions.length > 0;
99
+ const handleInputChange = (event) => {
100
+ const shouldKeepClosed = suppressOpenOnNextInputChangeRef.current;
101
+ suppressOpenOnNextInputChangeRef.current = false;
102
+ setCurrentValue(event.target.value);
103
+ if (!shouldKeepClosed) {
104
+ setOpen(true);
105
+ setActiveIndex(-1);
106
+ }
107
+ onChange?.(event);
108
+ };
109
+ const handleInputFocus = (event) => {
110
+ onFocus?.(event);
111
+ if (!event.defaultPrevented && !disabled) {
112
+ setOpen(true);
113
+ }
114
+ };
115
+ const handleInputBlur = (event) => {
116
+ onBlur?.(event);
117
+ const nextFocused = event.relatedTarget;
118
+ if (!nextFocused ||
119
+ (nextFocused instanceof Node &&
120
+ rootRef.current &&
121
+ !rootRef.current.contains(nextFocused))) {
122
+ closeOptions();
123
+ }
124
+ };
125
+ const handleInputClick = (event) => {
126
+ onClick?.(event);
127
+ if (!event.defaultPrevented && !disabled) {
128
+ setOpen(true);
129
+ }
130
+ };
131
+ const handleInputKeyDown = (event) => {
132
+ onKeyDown?.(event);
133
+ if (event.defaultPrevented || disabled) {
134
+ return;
135
+ }
136
+ if (event.key === 'ArrowDown') {
137
+ event.preventDefault();
138
+ if (!open) {
139
+ setOpen(true);
140
+ }
141
+ setActiveIndex((previousIndex) => findNextEnabledOptionIndex(filteredOptions, previousIndex, 1));
142
+ return;
143
+ }
144
+ if (event.key === 'ArrowUp') {
145
+ event.preventDefault();
146
+ if (!open) {
147
+ setOpen(true);
148
+ }
149
+ setActiveIndex((previousIndex) => findNextEnabledOptionIndex(filteredOptions, previousIndex, -1));
150
+ return;
151
+ }
152
+ if (event.key === 'Enter' && open && activeIndex >= 0) {
153
+ event.preventDefault();
154
+ const selectedOption = filteredOptions[activeIndex];
155
+ if (selectedOption) {
156
+ selectOption(selectedOption);
157
+ }
158
+ return;
159
+ }
160
+ if (event.key === 'Escape') {
161
+ event.preventDefault();
162
+ closeOptions();
163
+ }
164
+ };
165
+ return (jsxRuntime.jsxs("div", { ref: rootRef, className: combobox_module.default.combobox_root, children: [jsxRuntime.jsxs("div", { className: combobox_module.default.combobox_input_wrapper, children: [jsxRuntime.jsx("input", { ref: (node) => {
166
+ inputRef.current = node;
167
+ if (typeof ref === 'function') {
168
+ ref(node);
169
+ return;
170
+ }
171
+ if (ref) {
172
+ ref.current = node;
173
+ }
174
+ }, id: inputId, type: 'text', role: 'combobox', value: currentValue, disabled: disabled, required: required, autoComplete: autoComplete ?? 'off', "aria-autocomplete": 'list', "aria-haspopup": 'listbox', "aria-expanded": isListboxVisible, "aria-controls": isListboxVisible ? listboxId : undefined, "aria-activedescendant": isListboxVisible ? activeOptionId : undefined, className: clsx(combobox_module.default.combobox_input, SizeClassMap[size], hasError && combobox_module.default.combobox_input_error), onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, ...aria.computeAriaProps(hasError, describedBy, ariaDescribedBy, ariaInvalid), ...inputProps }), jsxRuntime.jsx(ChevronDownIcon.ChevronDownIcon, { className: combobox_module.default.combobox_icon, size: 16 })] }), isPopupVisible && !isListboxVisible && (jsxRuntime.jsx("div", { role: 'status', "aria-live": 'polite', className: combobox_module.default.combobox_listbox, children: jsxRuntime.jsx("p", { className: combobox_module.default.combobox_empty_state, children: noResultsText }) })), isListboxVisible && (jsxRuntime.jsx("div", { id: listboxId, role: 'listbox', className: combobox_module.default.combobox_listbox, children: filteredOptions.map((option, index) => {
175
+ const isActive = activeIndex === index;
176
+ const isDisabled = Boolean(option.disabled);
177
+ return (jsxRuntime.jsx("button", { id: `${inputId}-option-${index}`, type: 'button', role: 'option', disabled: isDisabled, tabIndex: -1, "aria-selected": isActive, "aria-disabled": isDisabled || undefined, className: clsx(combobox_module.default.combobox_option, {
178
+ [combobox_module.default.combobox_option_active]: isActive,
179
+ [combobox_module.default.combobox_option_disabled]: isDisabled
180
+ }), onMouseEnter: isDisabled ? undefined : () => setActiveIndex(index), onMouseDown: isDisabled
181
+ ? undefined
182
+ : (event) => {
183
+ event.preventDefault();
184
+ }, onClick: isDisabled ? undefined : () => selectOption(option), children: option.label }, option.value));
185
+ }) }))] }));
186
+ } }));
187
+ });
188
+ Combobox.displayName = 'Combobox';
189
+
190
+ exports.default = Combobox;
191
+ //# sourceMappingURL=index.cjs.map
@@ -142,3 +142,16 @@
142
142
  .dialog_body_39a5839d {
143
143
  margin-top: 0.875rem;
144
144
  }
145
+
146
+ @media (prefers-reduced-motion: reduce) {
147
+ .dialog_root_1c624aa8,
148
+ .dialog_root_1c624aa8::backdrop,
149
+ .dialog_content_ee12e897,
150
+ .dialog_close_e5b07e60 {
151
+ transition: none;
152
+ }
153
+
154
+ .dialog_content_ee12e897 {
155
+ transform: none;
156
+ }
157
+ }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var clsx = require('clsx');
8
8
  var React = require('react');
9
+ var useControllableState = require('../hooks/useControllableState.cjs');
9
10
  var CloseIcon = require('../Icons/CloseIcon.cjs');
10
11
  var mergeRefs = require('../utils/mergeRefs.cjs');
11
12
  var Portal = require('../utils/Portal.cjs');
@@ -22,15 +23,7 @@ function useDialogContext() {
22
23
  }
23
24
  const DialogRoot = function Dialog({ open, defaultOpen = false, onOpenChange, children }) {
24
25
  const idPrefix = React.useId();
25
- const isControlled = open !== undefined;
26
- const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
27
- const currentOpen = isControlled ? open : uncontrolledOpen;
28
- const handleOpenChange = React.useCallback((nextOpen) => {
29
- if (!isControlled) {
30
- setUncontrolledOpen(nextOpen);
31
- }
32
- onOpenChange?.(nextOpen);
33
- }, [isControlled, onOpenChange]);
26
+ const [currentOpen, handleOpenChange] = useControllableState.useControllableState(open, defaultOpen, onOpenChange);
34
27
  const contextValue = React.useMemo(() => ({ open: currentOpen, onOpenChange: handleOpenChange, idPrefix }), [currentOpen, handleOpenChange, idPrefix]);
35
28
  return (jsxRuntime.jsx(DialogContext.Provider, { value: contextValue, children: children }));
36
29
  };
@@ -119,7 +119,7 @@ const PopoverContent = React.forwardRef(function PopoverContent({ className, sty
119
119
  const floatingProps = getFloatingProps(props);
120
120
  if (!open)
121
121
  return null;
122
- return (jsxRuntime.jsx(Portal.default, { children: jsxRuntime.jsx(react.FloatingFocusManager, { context: floatingContext, modal: true, returnFocus: true, outsideElementsInert: false, initialFocus: 0, children: jsxRuntime.jsx("div", { id: `${idPrefix}-content`, ref: mergeRefs.mergeRefs(ref, setFloating), "aria-live": 'polite', className: clsx(popover_module.default.popover_content, className), style: {
122
+ return (jsxRuntime.jsx(Portal.default, { children: jsxRuntime.jsx(react.FloatingFocusManager, { context: floatingContext, modal: true, returnFocus: true, outsideElementsInert: false, initialFocus: 0, children: jsxRuntime.jsx("div", { id: `${idPrefix}-content`, ref: mergeRefs.mergeRefs(ref, setFloating), className: clsx(popover_module.default.popover_content, className), style: {
123
123
  ...floatingStyles,
124
124
  ...style
125
125
  }, ...floatingProps, children: children }) }) }));
@@ -23,3 +23,9 @@
23
23
  transform: translateY(0) scale(1);
24
24
  }
25
25
  }
26
+
27
+ @media (prefers-reduced-motion: reduce) {
28
+ .popover_content_0199c0e3 {
29
+ animation: none;
30
+ }
31
+ }
@@ -21,7 +21,7 @@ const Progress = React.forwardRef(function Progress({ value = 0, max = 100, inde
21
21
  const safeValue = Number.isFinite(value) ? clampValue(value, safeMax) : 0;
22
22
  const percent = (safeValue / safeMax) * 100;
23
23
  const valueText = `${Math.round(percent)}%`;
24
- return (jsxRuntime.jsxs("div", { ref: ref, className: clsx(progress_module.default.progress_root, className), style: style, ...rootProps, children: [(label || showValue) && (jsxRuntime.jsxs("div", { className: progress_module.default.progress_header, children: [label && jsxRuntime.jsx("span", { className: progress_module.default.progress_label, children: label }), showValue && !indeterminate && (jsxRuntime.jsx("span", { className: progress_module.default.progress_value, children: valueText }))] })), jsxRuntime.jsx("div", { className: clsx(progress_module.default.progress_track_wrapper, SizeClassMap[size]), children: jsxRuntime.jsx("progress", { className: clsx(progress_module.default.progress_track, indeterminate && progress_module.default.progress_track_indeterminate, barClassName), "aria-label": label ?? 'Progress', value: indeterminate ? undefined : safeValue, max: safeMax }) })] }));
24
+ return (jsxRuntime.jsxs("div", { ref: ref, className: clsx(progress_module.default.progress_root, className), style: style, ...rootProps, children: [(label || showValue) && (jsxRuntime.jsxs("div", { className: progress_module.default.progress_header, children: [label && jsxRuntime.jsx("span", { className: progress_module.default.progress_label, children: label }), showValue && !indeterminate && (jsxRuntime.jsx("span", { className: progress_module.default.progress_value, children: valueText }))] })), jsxRuntime.jsx("div", { className: clsx(progress_module.default.progress_track_wrapper, SizeClassMap[size]), children: jsxRuntime.jsx("progress", { className: clsx(progress_module.default.progress_track, indeterminate && progress_module.default.progress_track_indeterminate, barClassName), "aria-label": label ?? 'Progress', "aria-busy": indeterminate || undefined, "aria-valuenow": indeterminate ? undefined : safeValue, "aria-valuemax": safeMax, "aria-valuetext": indeterminate ? 'Loading' : valueText, value: indeterminate ? undefined : safeValue, max: safeMax }) })] }));
25
25
  });
26
26
  Progress.displayName = 'Progress';
27
27
 
@@ -84,3 +84,16 @@
84
84
  transform: translateX(320%);
85
85
  }
86
86
  }
87
+
88
+ @media (prefers-reduced-motion: reduce) {
89
+ .progress_track_d33f2771::-webkit-progress-value,
90
+ .progress_track_d33f2771::-moz-progress-bar {
91
+ transition: none;
92
+ }
93
+
94
+ .progress_track_indeterminate_50b31de5::before {
95
+ animation: none;
96
+ width: 60%;
97
+ transform: translateX(0);
98
+ }
99
+ }
@@ -7,6 +7,7 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var clsx = require('clsx');
8
8
  var React = require('react');
9
9
  var index = require('../Field/index.cjs');
10
+ var useControllableState = require('../hooks/useControllableState.cjs');
10
11
  var radiogroup_module = require('./radiogroup.module.css.cjs');
11
12
 
12
13
  const OrientationClassMap = {
@@ -16,13 +17,9 @@ const OrientationClassMap = {
16
17
  const RadioGroup = React.forwardRef(function RadioGroup({ label, options, value, defaultValue, onChange, hideLabel = false, helperText, error, disabled = false, required = false, orientation = 'vertical', name, id, className, style, ...groupProps }, ref) {
17
18
  const generatedName = React.useId();
18
19
  const groupName = name ?? generatedName;
19
- const isControlled = typeof value === 'string';
20
- const [internalValue, setInternalValue] = React.useState(defaultValue ?? '');
21
- const selectedValue = isControlled ? value : internalValue;
20
+ const [selectedValue, setSelectedValue] = useControllableState.useControllableValue(value, defaultValue ?? '');
22
21
  const handleChange = (event) => {
23
- if (!isControlled) {
24
- setInternalValue(event.target.value);
25
- }
22
+ setSelectedValue(event.target.value);
26
23
  onChange?.(event);
27
24
  };
28
25
  return (jsxRuntime.jsx(index.default, { label: label, hideLabel: hideLabel, helperText: helperText, error: error, disabled: disabled, required: required, id: id, className: className, style: style, labelElement: 'span', children: ({ inputId, labelId, describedBy, hasError }) => (jsxRuntime.jsx("div", { ref: ref, id: inputId, role: 'radiogroup', "aria-labelledby": labelId, "aria-describedby": describedBy, "aria-invalid": hasError || undefined, className: clsx(radiogroup_module.default.radio_group, OrientationClassMap[orientation], disabled && radiogroup_module.default.radio_group_disabled), ...groupProps, children: options.map((option, index) => {