@jobber/components 9.3.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/dist/Autocomplete/index.cjs +0 -1
  2. package/dist/Autocomplete/index.mjs +0 -1
  3. package/dist/Card/index.cjs +7 -7
  4. package/dist/Card/index.mjs +7 -7
  5. package/dist/Chip/index.cjs +0 -1
  6. package/dist/Chip/index.mjs +0 -1
  7. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +0 -1
  8. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +0 -1
  9. package/dist/Chips/InternalChipDismissible/index.cjs +0 -1
  10. package/dist/Chips/InternalChipDismissible/index.mjs +0 -1
  11. package/dist/Chips/index.cjs +0 -1
  12. package/dist/Chips/index.mjs +0 -1
  13. package/dist/ComboboxChipRemove-cjs.js +181 -184
  14. package/dist/ComboboxChipRemove-es.js +4 -7
  15. package/dist/ConfirmationModal/index.cjs +0 -1
  16. package/dist/ConfirmationModal/index.mjs +0 -1
  17. package/dist/DataDump/DataDump.d.ts +1 -1
  18. package/dist/DataDump/DataDump.messages.d.ts +6 -0
  19. package/dist/DataDump/index.cjs +9 -9
  20. package/dist/DataDump/index.mjs +9 -9
  21. package/dist/DataDump-cjs.js +11 -1
  22. package/dist/DataDump-es.js +11 -1
  23. package/dist/DataList/components/DataListActions/index.cjs +0 -1
  24. package/dist/DataList/components/DataListActions/index.mjs +0 -1
  25. package/dist/DataList/components/DataListBulkActions/index.cjs +0 -1
  26. package/dist/DataList/components/DataListBulkActions/index.mjs +0 -1
  27. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +0 -1
  28. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +0 -1
  29. package/dist/DataList/components/DataListFilters/index.cjs +0 -1
  30. package/dist/DataList/components/DataListFilters/index.mjs +0 -1
  31. package/dist/DataList/components/DataListHeader/index.cjs +0 -1
  32. package/dist/DataList/components/DataListHeader/index.mjs +0 -1
  33. package/dist/DataList/components/DataListItem/index.cjs +0 -1
  34. package/dist/DataList/components/DataListItem/index.mjs +0 -1
  35. package/dist/DataList/components/DataListItemActions/index.cjs +0 -1
  36. package/dist/DataList/components/DataListItemActions/index.mjs +0 -1
  37. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +0 -1
  38. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +0 -1
  39. package/dist/DataList/components/DataListItems/index.cjs +0 -1
  40. package/dist/DataList/components/DataListItems/index.mjs +0 -1
  41. package/dist/DataList/components/DataListLayout/index.cjs +0 -1
  42. package/dist/DataList/components/DataListLayout/index.mjs +0 -1
  43. package/dist/DataList/components/DataListLayoutActions/index.cjs +0 -1
  44. package/dist/DataList/components/DataListLayoutActions/index.mjs +0 -1
  45. package/dist/DataList/index.cjs +0 -1
  46. package/dist/DataList/index.mjs +0 -1
  47. package/dist/DataTable/DataTable.messages.d.ts +22 -0
  48. package/dist/DataTable/index.cjs +2 -2
  49. package/dist/DataTable/index.mjs +2 -2
  50. package/dist/DataTable-cjs.js +34 -5
  51. package/dist/DataTable-es.js +34 -5
  52. package/dist/DatePicker/index.cjs +0 -2
  53. package/dist/DatePicker/index.mjs +0 -2
  54. package/dist/DatePicker-cjs.js +19 -18
  55. package/dist/DatePicker-es.js +2 -1
  56. package/dist/Dialog/index.cjs +11 -6
  57. package/dist/Dialog/index.mjs +11 -6
  58. package/dist/DrawerDescription-cjs.js +7 -8
  59. package/dist/DrawerDescription-es.js +1 -2
  60. package/dist/DrawerTitle-cjs.js +4 -4
  61. package/dist/DrawerTitle-es.js +1 -1
  62. package/dist/FeatureSwitch/FeatureSwitch.messages.d.ts +10 -0
  63. package/dist/FeatureSwitch/index.cjs +2 -0
  64. package/dist/FeatureSwitch/index.mjs +2 -0
  65. package/dist/FeatureSwitch-cjs.js +15 -2
  66. package/dist/FeatureSwitch-es.js +15 -2
  67. package/dist/FieldDescription-cjs.js +49 -49
  68. package/dist/FieldDescription-es.js +2 -2
  69. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +0 -1
  70. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +0 -1
  71. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +0 -1
  72. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +0 -1
  73. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +0 -1
  74. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +0 -1
  75. package/dist/FilterPicker/index.cjs +0 -1
  76. package/dist/FilterPicker/index.mjs +0 -1
  77. package/dist/FormatFile/FormatFile.messages.d.ts +18 -0
  78. package/dist/FormatFile/index.cjs +2 -3
  79. package/dist/FormatFile/index.mjs +2 -3
  80. package/dist/FormatFile-cjs.js +24 -2
  81. package/dist/FormatFile-es.js +24 -2
  82. package/dist/Gallery/index.cjs +0 -1
  83. package/dist/Gallery/index.mjs +0 -1
  84. package/dist/HelperText-cjs.js +3 -2
  85. package/dist/HelperText-es.js +3 -2
  86. package/dist/InputDate/index.cjs +0 -2
  87. package/dist/InputDate/index.mjs +0 -2
  88. package/dist/InputNumber/InputNumber.d.ts +75 -5
  89. package/dist/InputNumber/index.cjs +27 -3
  90. package/dist/InputNumber/index.d.ts +2 -2
  91. package/dist/InputNumber/index.mjs +27 -3
  92. package/dist/{primitives/InputNumberExperimental → InputNumber}/types.d.ts +48 -35
  93. package/dist/InputNumber-cjs.js +267 -4986
  94. package/dist/InputNumber-es.js +268 -4987
  95. package/dist/LightBox/index.cjs +0 -1
  96. package/dist/LightBox/index.mjs +0 -1
  97. package/dist/Menu/index.cjs +7 -7
  98. package/dist/Menu/index.mjs +7 -7
  99. package/dist/Menu-cjs.js +5 -1
  100. package/dist/Menu-es.js +5 -2
  101. package/dist/MenuSubmenuTrigger-cjs.js +142 -143
  102. package/dist/MenuSubmenuTrigger-es.js +5 -6
  103. package/dist/Modal/index.cjs +0 -1
  104. package/dist/Modal/index.mjs +0 -1
  105. package/dist/NumberFieldInput-cjs.js +63 -63
  106. package/dist/NumberFieldInput-es.js +5 -5
  107. package/dist/Page/index.cjs +18 -19
  108. package/dist/Page/index.mjs +18 -19
  109. package/dist/Page-cjs.js +6 -3
  110. package/dist/Page-es.js +6 -3
  111. package/dist/Popover/index.cjs +0 -1
  112. package/dist/Popover/index.mjs +0 -1
  113. package/dist/ProgressBar/ProgressBar.messages.d.ts +6 -0
  114. package/dist/ProgressBar/index.cjs +2 -0
  115. package/dist/ProgressBar/index.mjs +2 -0
  116. package/dist/ProgressBar-cjs.js +13 -1
  117. package/dist/ProgressBar-es.js +13 -1
  118. package/dist/ScrollAreaViewport-cjs.js +119 -121
  119. package/dist/ScrollAreaViewport-es.js +3 -5
  120. package/dist/Select/index.cjs +3 -9
  121. package/dist/Select/index.mjs +3 -9
  122. package/dist/SelectGroupLabel-cjs.js +117 -107
  123. package/dist/SelectGroupLabel-es.js +20 -12
  124. package/dist/Toast/Toast.messages.d.ts +6 -0
  125. package/dist/Toast/index.cjs +3 -1
  126. package/dist/Toast/index.mjs +3 -1
  127. package/dist/Toast/showToast.d.ts +12 -1
  128. package/dist/Tooltip/index.cjs +0 -1
  129. package/dist/Tooltip/index.mjs +0 -1
  130. package/dist/dialogReturnFocus-cjs.js +4 -1
  131. package/dist/dialogReturnFocus-es.js +4 -1
  132. package/dist/docs/DataDump/DataDump.md +1 -1
  133. package/dist/docs/InputNumber/InputNumber.md +564 -56
  134. package/dist/docs/Page/Page.md +1 -1
  135. package/dist/docs/index.md +0 -1
  136. package/dist/floating-ui.react-cjs.js +34 -35
  137. package/dist/floating-ui.react-dom-cjs.js +64 -65
  138. package/dist/floating-ui.react-dom-es.js +1 -2
  139. package/dist/floating-ui.react-es.js +1 -2
  140. package/dist/floating-ui.utils-cjs.js +183 -0
  141. package/dist/floating-ui.utils-es.js +165 -1
  142. package/dist/index.cjs +10 -15
  143. package/dist/index.mjs +10 -15
  144. package/dist/primitives/BottomSheet/index.cjs +2 -5
  145. package/dist/primitives/BottomSheet/index.mjs +2 -5
  146. package/dist/primitives/ComboboxPrimitive/index.cjs +3 -7
  147. package/dist/primitives/ComboboxPrimitive/index.mjs +3 -7
  148. package/dist/primitives/HelperText/HelperText.d.ts +4 -1
  149. package/dist/primitives/HelperText/index.cjs +1 -1
  150. package/dist/primitives/HelperText/index.mjs +1 -1
  151. package/dist/primitives/SelectPrimitive/index.cjs +3 -9
  152. package/dist/primitives/SelectPrimitive/index.mjs +3 -9
  153. package/dist/primitives/index.cjs +2 -16
  154. package/dist/primitives/index.d.ts +0 -2
  155. package/dist/primitives/index.mjs +2 -15
  156. package/dist/scrollEdges-cjs.js +6 -2
  157. package/dist/scrollEdges-es.js +5 -2
  158. package/dist/sharedHelpers/i18n/renderWithLocale.d.ts +11 -0
  159. package/dist/showToast-cjs.js +20 -6
  160. package/dist/showToast-es.js +20 -6
  161. package/dist/styles.css +573 -902
  162. package/dist/unstyledPrimitives/index.cjs +105 -111
  163. package/dist/unstyledPrimitives/index.mjs +5 -11
  164. package/dist/useCompositeListItem-cjs.js +516 -90
  165. package/dist/useCompositeListItem-es.js +407 -6
  166. package/dist/useLabel-cjs.js +10 -10
  167. package/dist/useLabel-es.js +3 -3
  168. package/dist/useRenderElement-es.js +1 -1
  169. package/dist/useScrollLock-cjs.js +1332 -203
  170. package/dist/useScrollLock-es.js +1068 -5
  171. package/dist/utils/meta/meta.json +12 -0
  172. package/package.json +2 -3
  173. package/dist/InputNumber/InputNumber.types.d.ts +0 -20
  174. package/dist/InputNumberExperimental-cjs.js +0 -277
  175. package/dist/InputNumberExperimental-es.js +0 -275
  176. package/dist/clamp-cjs.js +0 -7
  177. package/dist/clamp-es.js +0 -5
  178. package/dist/clsx-cjs.js +0 -5
  179. package/dist/clsx-es.js +0 -3
  180. package/dist/docs/InputNumberExperimental/InputNumberExperimental.md +0 -578
  181. package/dist/floating-ui.utils.dom-cjs.js +0 -185
  182. package/dist/floating-ui.utils.dom-es.js +0 -165
  183. package/dist/isElementDisabled-cjs.js +0 -40
  184. package/dist/isElementDisabled-es.js +0 -18
  185. package/dist/primitives/InputNumberExperimental/InputNumberExperimental.d.ts +0 -77
  186. package/dist/primitives/InputNumberExperimental/index.cjs +0 -30
  187. package/dist/primitives/InputNumberExperimental/index.d.ts +0 -2
  188. package/dist/primitives/InputNumberExperimental/index.mjs +0 -24
  189. package/dist/resolveAriaLabelledBy-cjs.js +0 -200
  190. package/dist/resolveAriaLabelledBy-es.js +0 -166
  191. package/dist/useButton-cjs.js +0 -1164
  192. package/dist/useButton-es.js +0 -1071
  193. package/dist/useLabelableId-cjs.js +0 -278
  194. package/dist/useLabelableId-es.js +0 -247
@@ -1,12 +1,88 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { i as isHTMLElement, b as isElement, f as getComputedStyle, g as getNodeName, a as isShadowRoot, c as isNode, o as getWindow, d as isLastTraversableNode, e as getParentNode, m as isOverflowElement } from './floating-ui.utils.dom-es.js';
4
- import { a as useIsoLayoutEffect, b as useStableCallback, aa as lowerUserAgent, ab as apple, q as contains, y as jsdom, E as visuallyHidden, s as stopEvent, o as ownerDocument, p as activeElement, B as addEventListener, d as createChangeEventDetails, v as focusOut, l as useId, m as useValueAsRef, h as useTimeout, n as useAnimationFrame, ac as resolveRef, $ as triggerHover, Q as outsidePress, w as isVirtualClick, x as isVirtualPointerEvent, g as getTarget, W as isMouseLikePointerType, Z as triggerPress, ad as isReactEvent, _ as escapeKey, T as Timeout, ae as isClickLikeEvent, C as none, k as useTransitionStatus, f as useOpenChangeComplete, A as ios, O as useValueChanged, a5 as TransitionStatusDataAttributes, af as AnimationFrame } from './useButton-es.js';
5
- import { f as formatErrorMessage, a as useRefWithInit, i as isReactVersionAtLeast, N as NOOP, u as useRenderElement, E as EMPTY_OBJECT, c as useMergedRefs } from './useRenderElement-es.js';
3
+ import { a as isShadowRoot, i as isHTMLElement, b as isElement, h as getComputedStyle, f as floor, g as getNodeName, c as isNode, K as getWindow, d as isLastTraversableNode, e as getParentNode, I as isOverflowElement } from './floating-ui.utils-es.js';
4
+ import { a as useRefWithInit, f as formatErrorMessage, i as isReactVersionAtLeast, N as NOOP, E as EMPTY_OBJECT, u as useRenderElement, b as useMergedRefs, m as mergeProps, d as makeEventPreventable } from './useRenderElement-es.js';
6
5
  import { jsx, jsxs } from 'react/jsx-runtime';
7
- import { f as floor } from './floating-ui.utils-es.js';
8
6
  import * as ReactDOM from 'react-dom';
9
7
 
8
+ let set;
9
+ if (process.env.NODE_ENV !== 'production') {
10
+ set = new Set();
11
+ }
12
+ function error(...messages) {
13
+ if (process.env.NODE_ENV !== 'production') {
14
+ const messageKey = messages.join(' ');
15
+ if (!set.has(messageKey)) {
16
+ set.add(messageKey);
17
+ console.error(`Base UI: ${messageKey}`);
18
+ }
19
+ }
20
+ }
21
+
22
+ function useControlled({
23
+ controlled,
24
+ default: defaultProp,
25
+ name,
26
+ state = 'value'
27
+ }) {
28
+ // isControlled is ignored in the hook dependency lists as it should never change.
29
+ const {
30
+ current: isControlled
31
+ } = React.useRef(controlled !== undefined);
32
+ const [valueState, setValue] = React.useState(defaultProp);
33
+ const value = isControlled ? controlled : valueState;
34
+ if (process.env.NODE_ENV !== 'production') {
35
+ React.useEffect(() => {
36
+ if (isControlled !== (controlled !== undefined)) {
37
+ error([`A component is changing the ${isControlled ? '' : 'un'}controlled ${state} state of ${name} to be ${isControlled ? 'un' : ''}controlled.`, 'Elements should not switch from uncontrolled to controlled (or vice versa).', `Decide between using a controlled or uncontrolled ${name} ` + 'element for the lifetime of the component.', "The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.", 'More info: https://fb.me/react-controlled-components'].join('\n'));
38
+ }
39
+ }, [state, name, controlled]);
40
+ const {
41
+ current: defaultValue
42
+ } = React.useRef(defaultProp);
43
+ React.useEffect(() => {
44
+ if (!isControlled && serializeToDevModeString(defaultValue) !== serializeToDevModeString(defaultProp)) {
45
+ error([`A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. ` + `To suppress this warning opt to use a controlled ${name}.`].join('\n'));
46
+ }
47
+ }, [defaultProp]);
48
+ }
49
+ const setValueIfUncontrolled = React.useCallback(newValue => {
50
+ if (!isControlled) {
51
+ setValue(newValue);
52
+ }
53
+ }, []);
54
+ return [value, setValueIfUncontrolled];
55
+ }
56
+ function serializeToDevModeString(input) {
57
+ let nextId = 0;
58
+ const seen = new WeakMap();
59
+ try {
60
+ const result = JSON.stringify(input, function replacer(key, value) {
61
+ if (key === '_owner' && this != null && typeof this === 'object' && '$$typeof' in this) {
62
+ return undefined;
63
+ }
64
+ if (typeof value === 'bigint') {
65
+ return `__bigint__:${value}`;
66
+ }
67
+ if (value !== null && typeof value === 'object') {
68
+ const id = seen.get(value);
69
+ if (id !== undefined) {
70
+ return `__object__:${id}`;
71
+ }
72
+ seen.set(value, nextId);
73
+ nextId += 1;
74
+ }
75
+ return value;
76
+ });
77
+ return result ?? `__top__:${typeof input}`;
78
+ } catch {
79
+ return '__unserializable__';
80
+ }
81
+ }
82
+
83
+ const noop = () => {};
84
+ const useIsoLayoutEffect = typeof document !== 'undefined' ? React.useLayoutEffect : noop;
85
+
10
86
  function useOnFirstRender(fn) {
11
87
  const ref = React.useRef(true);
12
88
  if (ref.current) {
@@ -15,6 +91,104 @@ function useOnFirstRender(fn) {
15
91
  }
16
92
  }
17
93
 
94
+ /**
95
+ * A clone of the React namespace for reading APIs that may be missing in older
96
+ * supported React versions. Bundlers can rewrite direct `React.someNewApi`
97
+ * reads into named imports, which breaks React 17. Reading from this cloned
98
+ * object keeps those lookups optional.
99
+ *
100
+ * @see https://github.com/mui/material-ui/issues/41190#issuecomment-2040873379
101
+ */
102
+ const SafeReact = {
103
+ ...React
104
+ };
105
+
106
+ const useInsertionEffect = SafeReact.useInsertionEffect;
107
+ const useSafeInsertionEffect =
108
+ // React 17 doesn't have useInsertionEffect.
109
+ useInsertionEffect &&
110
+ // Preact replaces useInsertionEffect with useLayoutEffect and fires too late.
111
+ useInsertionEffect !== SafeReact.useLayoutEffect ? useInsertionEffect : fn => fn();
112
+ /**
113
+ * Stabilizes the function passed so it's always the same between renders.
114
+ *
115
+ * The function becomes non-reactive to any values it captures.
116
+ * It can safely be passed as a dependency of `React.useMemo` and `React.useEffect` without re-triggering them if its captured values change.
117
+ *
118
+ * The function must only be called inside effects and event handlers, never during render (which throws an error).
119
+ *
120
+ * This hook is a more permissive version of React 19.2's `React.useEffectEvent` in that it can be passed through contexts and called in event handler props, not just effects.
121
+ */
122
+ function useStableCallback(callback) {
123
+ const stable = useRefWithInit(createStableCallback).current;
124
+ stable.next = callback;
125
+ useSafeInsertionEffect(stable.effect);
126
+ return stable.trampoline;
127
+ }
128
+ function createStableCallback() {
129
+ const stable = {
130
+ next: undefined,
131
+ callback: assertNotCalled,
132
+ trampoline: (...args) => stable.callback?.(...args),
133
+ effect: () => {
134
+ stable.callback = stable.next;
135
+ }
136
+ };
137
+ return stable;
138
+ }
139
+ function assertNotCalled() {
140
+ if (process.env.NODE_ENV !== 'production') {
141
+ // TODO: fix mui/no-guarded-throw
142
+ // eslint-disable-next-line mui/no-guarded-throw
143
+ throw /* minify-error-disabled */new Error('Base UI: Cannot call an event handler while rendering.');
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Untracks the provided value by turning it into a ref to remove its reactivity.
149
+ *
150
+ * Used to access the passed value inside `React.useEffect` without causing the effect to re-run when the value changes.
151
+ */
152
+ function useValueAsRef(value) {
153
+ const latest = useRefWithInit(createLatestRef, value).current;
154
+ latest.next = value;
155
+
156
+ // eslint-disable-next-line react-hooks/exhaustive-deps
157
+ useIsoLayoutEffect(latest.effect);
158
+ return latest;
159
+ }
160
+ function createLatestRef(value) {
161
+ const latest = {
162
+ current: value,
163
+ next: value,
164
+ effect: () => {
165
+ latest.current = latest.next;
166
+ }
167
+ };
168
+ return latest;
169
+ }
170
+
171
+ const visuallyHiddenBase = {
172
+ clipPath: 'inset(50%)',
173
+ overflow: 'hidden',
174
+ whiteSpace: 'nowrap',
175
+ border: 0,
176
+ padding: 0,
177
+ width: 1,
178
+ height: 1,
179
+ margin: -1
180
+ };
181
+ const visuallyHidden = {
182
+ ...visuallyHiddenBase,
183
+ position: 'fixed',
184
+ top: 0,
185
+ left: 0
186
+ };
187
+ const visuallyHiddenInput = {
188
+ ...visuallyHiddenBase,
189
+ position: 'absolute'
190
+ };
191
+
18
192
  /**
19
193
  * The NoOptionalParams type is a utility type that checks if a function has optional or default parameters.
20
194
  * If the function has optional or default parameters, it returns a string literal type with an error message.
@@ -954,6 +1128,240 @@ function mergeCleanups(...cleanups) {
954
1128
  };
955
1129
  }
956
1130
 
1131
+ function ownerDocument(node) {
1132
+ return node?.ownerDocument || document;
1133
+ }
1134
+
1135
+ /**
1136
+ * Adds an event listener and returns a cleanup function to remove it.
1137
+ */
1138
+
1139
+ function addEventListener(target, type, listener, options) {
1140
+ target.addEventListener(type, listener, options);
1141
+ return () => {
1142
+ target.removeEventListener(type, listener, options);
1143
+ };
1144
+ }
1145
+
1146
+ const EMPTY$2 = [];
1147
+
1148
+ /**
1149
+ * A React.useEffect equivalent that runs once, when the component is mounted.
1150
+ */
1151
+ function useOnMount(fn) {
1152
+ // TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler -- no need to put `fn` in the dependency array
1153
+ /* eslint-disable react-hooks/exhaustive-deps */
1154
+ React.useEffect(fn, EMPTY$2);
1155
+ /* eslint-enable react-hooks/exhaustive-deps */
1156
+ }
1157
+
1158
+ /** Unlike `setTimeout`, rAF doesn't guarantee a positive integer return value, so we can't have
1159
+ * a monomorphic `uint` type with `0` meaning empty.
1160
+ * See warning note at:
1161
+ * https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame#return_value */
1162
+ const EMPTY$1 = null;
1163
+ let LAST_RAF = globalThis.requestAnimationFrame;
1164
+ class Scheduler {
1165
+ /* This implementation uses an array as a backing data-structure for frame callbacks.
1166
+ * It allows `O(1)` callback cancelling by inserting a `null` in the array, though it
1167
+ * never calls the native `cancelAnimationFrame` if there are no frames left. This can
1168
+ * be much more efficient if there is a call pattern that alterns as
1169
+ * "request-cancel-request-cancel-…".
1170
+ * But in the case of "request-request-…-cancel-cancel-…", it leaves the final animation
1171
+ * frame to run anyway. We turn that frame into a `O(1)` no-op via `callbacksCount`. */
1172
+
1173
+ callbacks = [];
1174
+ callbacksCount = 0;
1175
+ nextId = 1;
1176
+ startId = 1;
1177
+ isScheduled = false;
1178
+ tick = timestamp => {
1179
+ this.isScheduled = false;
1180
+ const currentCallbacks = this.callbacks;
1181
+ const currentCallbacksCount = this.callbacksCount;
1182
+
1183
+ // Update these before iterating, callbacks could call `requestAnimationFrame` again.
1184
+ this.callbacks = [];
1185
+ this.callbacksCount = 0;
1186
+ this.startId = this.nextId;
1187
+ if (currentCallbacksCount > 0) {
1188
+ for (let i = 0; i < currentCallbacks.length; i += 1) {
1189
+ currentCallbacks[i]?.(timestamp);
1190
+ }
1191
+ }
1192
+ };
1193
+ request(fn) {
1194
+ const id = this.nextId;
1195
+ this.nextId += 1;
1196
+ this.callbacks.push(fn);
1197
+ this.callbacksCount += 1;
1198
+
1199
+ /* In a test environment with fake timers, a fake `requestAnimationFrame` can be called
1200
+ * but there's no guarantee that the animation frame will actually run before the fake
1201
+ * timers are teared, which leaves `isScheduled` set, but won't run our `tick()`. */
1202
+ const didRAFChange = process.env.NODE_ENV !== 'production' && LAST_RAF !== requestAnimationFrame && (LAST_RAF = requestAnimationFrame, true);
1203
+ if (!this.isScheduled || didRAFChange) {
1204
+ requestAnimationFrame(this.tick);
1205
+ this.isScheduled = true;
1206
+ }
1207
+ return id;
1208
+ }
1209
+ cancel(id) {
1210
+ const index = id - this.startId;
1211
+ if (index < 0 || index >= this.callbacks.length) {
1212
+ return;
1213
+ }
1214
+ this.callbacks[index] = null;
1215
+ this.callbacksCount -= 1;
1216
+ }
1217
+ }
1218
+ const scheduler = new Scheduler();
1219
+ class AnimationFrame {
1220
+ static create() {
1221
+ return new AnimationFrame();
1222
+ }
1223
+ static request(fn) {
1224
+ return scheduler.request(fn);
1225
+ }
1226
+ static cancel(id) {
1227
+ return scheduler.cancel(id);
1228
+ }
1229
+ currentId = EMPTY$1;
1230
+
1231
+ /**
1232
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
1233
+ */
1234
+ request(fn) {
1235
+ this.cancel();
1236
+ this.currentId = scheduler.request(() => {
1237
+ this.currentId = EMPTY$1;
1238
+ fn();
1239
+ });
1240
+ }
1241
+ cancel = () => {
1242
+ if (this.currentId !== EMPTY$1) {
1243
+ scheduler.cancel(this.currentId);
1244
+ this.currentId = EMPTY$1;
1245
+ }
1246
+ };
1247
+ disposeEffect = () => {
1248
+ return this.cancel;
1249
+ };
1250
+ }
1251
+
1252
+ /**
1253
+ * A `requestAnimationFrame` with automatic cleanup and guard.
1254
+ */
1255
+ function useAnimationFrame() {
1256
+ const timeout = useRefWithInit(AnimationFrame.create).current;
1257
+ useOnMount(timeout.disposeEffect);
1258
+ return timeout;
1259
+ }
1260
+
1261
+ const EMPTY = 0;
1262
+ class Timeout {
1263
+ static create() {
1264
+ return new Timeout();
1265
+ }
1266
+ currentId = EMPTY;
1267
+
1268
+ /**
1269
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
1270
+ */
1271
+ start(delay, fn) {
1272
+ this.clear();
1273
+ this.currentId = setTimeout(() => {
1274
+ this.currentId = EMPTY;
1275
+ fn();
1276
+ }, delay); /* Node.js types are enabled in development */
1277
+ }
1278
+ isStarted() {
1279
+ return this.currentId !== EMPTY;
1280
+ }
1281
+ clear = () => {
1282
+ if (this.currentId !== EMPTY) {
1283
+ clearTimeout(this.currentId);
1284
+ this.currentId = EMPTY;
1285
+ }
1286
+ };
1287
+ disposeEffect = () => {
1288
+ return this.clear;
1289
+ };
1290
+ }
1291
+
1292
+ /**
1293
+ * A `setTimeout` with automatic cleanup and guard.
1294
+ */
1295
+ function useTimeout() {
1296
+ const timeout = useRefWithInit(Timeout.create).current;
1297
+ useOnMount(timeout.disposeEffect);
1298
+ return timeout;
1299
+ }
1300
+
1301
+ /**
1302
+ * Reads `navigator.userAgent` / `navigator.platform` (legacy but universally
1303
+ * supported) into a normalized shape. In development, prefers the modern
1304
+ * `navigator.userAgentData` API on Chromium to avoid DevTools warnings about
1305
+ * the deprecated reads; that branch is dead-code-eliminated in production
1306
+ * builds to keep the bundle small.
1307
+ *
1308
+ * Returns empty/zero values when `navigator` is undefined (SSR), so every
1309
+ * derived flag safely evaluates to `false`.
1310
+ */
1311
+ function readRawData() {
1312
+ if (typeof navigator === 'undefined') {
1313
+ return {
1314
+ userAgent: '',
1315
+ platform: '',
1316
+ maxTouchPoints: 0
1317
+ };
1318
+ }
1319
+ if (process.env.NODE_ENV !== 'production') {
1320
+ const uaData = navigator.userAgentData;
1321
+ if (uaData && Array.isArray(uaData.brands)) {
1322
+ return {
1323
+ userAgent: uaData.brands.map(({
1324
+ brand,
1325
+ version
1326
+ }) => `${brand}/${version}`).join(' '),
1327
+ platform: uaData.platform ?? navigator.platform ?? '',
1328
+ maxTouchPoints: navigator.maxTouchPoints ?? 0
1329
+ };
1330
+ }
1331
+ }
1332
+ return {
1333
+ userAgent: navigator.userAgent,
1334
+ platform: navigator.platform ?? '',
1335
+ maxTouchPoints: navigator.maxTouchPoints ?? 0
1336
+ };
1337
+ }
1338
+ const {
1339
+ userAgent,
1340
+ platform,
1341
+ maxTouchPoints
1342
+ } = readRawData();
1343
+ const lowerUserAgent = userAgent.toLowerCase();
1344
+ const lowerPlatform = platform.toLowerCase();
1345
+
1346
+ // iPadOS 13+ reports `MacIntel` for `navigator.platform`; disambiguated via
1347
+ // `maxTouchPoints` so iPad is classified as iOS, not macOS.
1348
+ // https://github.com/mui/base-ui/issues/1309
1349
+ /** iPhone, iPad (including iPadOS 13+ reporting as macOS), iPod. */
1350
+ const ios = /^i(os$|p)/.test(lowerPlatform) || lowerPlatform === 'macintel' && maxTouchPoints > 1;
1351
+
1352
+ /** Android phones, tablets, and embedded Android browsers. */
1353
+ const ANDROID_STRING = 'android';
1354
+ const android = lowerPlatform === ANDROID_STRING || lowerUserAgent.includes(ANDROID_STRING);
1355
+
1356
+ /** macOS desktop. Excludes iPadOS, which reports as `MacIntel`. */
1357
+ const mac = !ios && lowerPlatform.startsWith('mac');
1358
+
1359
+ /** Windows desktop. */
1360
+ lowerPlatform.startsWith('win');
1361
+
1362
+ /** Any Apple OS (`mac || ios`). */
1363
+ const apple = mac || ios;
1364
+
957
1365
  // WebKit (Safari, all iOS browsers, GNOME Web). Distinguished from Blink by
958
1366
  // the legacy `-webkit-backdrop-filter` name — Blink forked from WebKit in 2013
959
1367
  // and only ships the unprefixed `backdrop-filter`.
@@ -985,6 +1393,49 @@ const gecko = !webkit && lowerUserAgent.includes('firefox');
985
1393
  */
986
1394
  const voiceOver = apple;
987
1395
 
1396
+ /** Running in jsdom or HappyDOM (used by unit tests). */
1397
+ const jsdom = /jsdom|happydom/.test(lowerUserAgent);
1398
+
1399
+ function stopEvent(event) {
1400
+ event.preventDefault();
1401
+ event.stopPropagation();
1402
+ }
1403
+ function isReactEvent(event) {
1404
+ return 'nativeEvent' in event;
1405
+ }
1406
+
1407
+ // License: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/utils/src/isVirtualEvent.ts
1408
+ function isVirtualClick(event) {
1409
+ if (event.pointerType === '' && event.isTrusted) {
1410
+ return true;
1411
+ }
1412
+ if (android && event.pointerType) {
1413
+ return event.type === 'click' && event.buttons === 1;
1414
+ }
1415
+ return event.detail === 0 && !event.pointerType;
1416
+ }
1417
+ function isVirtualPointerEvent(event) {
1418
+ if (jsdom) {
1419
+ return false;
1420
+ }
1421
+ return !android && event.width === 0 && event.height === 0 || android && event.width === 1 && event.height === 1 && event.pressure === 0 && event.detail === 0 && event.pointerType === 'mouse' ||
1422
+ // iOS VoiceOver returns 0.333• for width/height.
1423
+ event.width < 1 && event.height < 1 && event.pressure === 0 && event.detail === 0 && event.pointerType === 'touch';
1424
+ }
1425
+ function isMouseLikePointerType(pointerType, strict) {
1426
+ // On some Linux machines with Chromium, mouse inputs return a `pointerType`
1427
+ // of "pen": https://github.com/floating-ui/floating-ui/issues/2015
1428
+ const values = ['mouse', 'pen'];
1429
+ if (!strict) {
1430
+ values.push('', undefined);
1431
+ }
1432
+ return values.includes(pointerType);
1433
+ }
1434
+ function isClickLikeEvent(event) {
1435
+ const type = event.type;
1436
+ return type === 'click' || type === 'mousedown' || type === 'keydown' || type === 'keyup';
1437
+ }
1438
+
988
1439
  const FOCUSABLE_ATTRIBUTE = 'data-base-ui-focusable';
989
1440
  const TYPEABLE_SELECTOR = "input:not([type='hidden']):not([disabled])," + "[contenteditable]:not([contenteditable='false']),textarea:not([disabled])";
990
1441
  const ARROW_LEFT = 'ArrowLeft';
@@ -992,6 +1443,46 @@ const ARROW_RIGHT = 'ArrowRight';
992
1443
  const ARROW_UP = 'ArrowUp';
993
1444
  const ARROW_DOWN = 'ArrowDown';
994
1445
 
1446
+ function activeElement(doc) {
1447
+ let element = doc.activeElement;
1448
+ while (element?.shadowRoot?.activeElement != null) {
1449
+ element = element.shadowRoot.activeElement;
1450
+ }
1451
+ return element;
1452
+ }
1453
+ function contains(parent, child) {
1454
+ if (!parent || !child) {
1455
+ return false;
1456
+ }
1457
+ const rootNode = child.getRootNode?.();
1458
+
1459
+ // First, attempt with the faster native method.
1460
+ if (parent.contains(child)) {
1461
+ return true;
1462
+ }
1463
+
1464
+ // Then fall back to traversing out of shadow roots when needed.
1465
+ if (rootNode && isShadowRoot(rootNode)) {
1466
+ let next = child;
1467
+ while (next) {
1468
+ if (parent === next) {
1469
+ return true;
1470
+ }
1471
+ next = next.parentNode || next.host;
1472
+ }
1473
+ }
1474
+ return false;
1475
+ }
1476
+ function getTarget(event) {
1477
+ if ('composedPath' in event) {
1478
+ return event.composedPath()[0];
1479
+ }
1480
+
1481
+ // TS assumes `composedPath()` always exists, but older browsers without
1482
+ // shadow DOM support still fall back to `target`.
1483
+ return event.target;
1484
+ }
1485
+
995
1486
  function isTargetInsideEnabledTrigger(target, triggerElements) {
996
1487
  if (!isElement(target)) {
997
1488
  return false;
@@ -1057,6 +1548,86 @@ function getFloatingFocusElement(floatingElement) {
1057
1548
  return floatingElement.hasAttribute(FOCUSABLE_ATTRIBUTE) ? floatingElement : floatingElement.querySelector(`[${FOCUSABLE_ATTRIBUTE}]`) || floatingElement;
1058
1549
  }
1059
1550
 
1551
+ const none = 'none';
1552
+ const triggerPress = 'trigger-press';
1553
+ const triggerHover = 'trigger-hover';
1554
+ const triggerFocus = 'trigger-focus';
1555
+ const outsidePress = 'outside-press';
1556
+ const itemPress = 'item-press';
1557
+ const closePress = 'close-press';
1558
+ const clearPress = 'clear-press';
1559
+ const chipRemovePress = 'chip-remove-press';
1560
+ const incrementPress = 'increment-press';
1561
+ const decrementPress = 'decrement-press';
1562
+ const inputChange = 'input-change';
1563
+ const inputClear = 'input-clear';
1564
+ const inputBlur = 'input-blur';
1565
+ const inputPaste = 'input-paste';
1566
+ const inputPress = 'input-press';
1567
+ const focusOut = 'focus-out';
1568
+ const escapeKey = 'escape-key';
1569
+ const closeWatcher = 'close-watcher';
1570
+ const listNavigation = 'list-navigation';
1571
+ const keyboard = 'keyboard';
1572
+ const pointer = 'pointer';
1573
+ const wheel = 'wheel';
1574
+ const scrub = 'scrub';
1575
+ const cancelOpen = 'cancel-open';
1576
+ const siblingOpen = 'sibling-open';
1577
+ const imperativeAction = 'imperative-action';
1578
+ const swipe = 'swipe';
1579
+ const windowResize = 'window-resize';
1580
+
1581
+ /**
1582
+ * Maps a change `reason` string to the corresponding native event type.
1583
+ */
1584
+
1585
+ /**
1586
+ * Details of custom change events emitted by Base UI components.
1587
+ */
1588
+
1589
+ /**
1590
+ * Details of custom generic events emitted by Base UI components.
1591
+ */
1592
+
1593
+ /**
1594
+ * Creates a Base UI event details object with the given reason and utilities
1595
+ * for preventing Base UI's internal event handling.
1596
+ */
1597
+ function createChangeEventDetails(reason, event, trigger, customProperties) {
1598
+ let canceled = false;
1599
+ let allowPropagation = false;
1600
+ const custom = customProperties ?? EMPTY_OBJECT;
1601
+ const details = {
1602
+ reason,
1603
+ event: event ?? new Event('base-ui'),
1604
+ cancel() {
1605
+ canceled = true;
1606
+ },
1607
+ allowPropagation() {
1608
+ allowPropagation = true;
1609
+ },
1610
+ get isCanceled() {
1611
+ return canceled;
1612
+ },
1613
+ get isPropagationAllowed() {
1614
+ return allowPropagation;
1615
+ },
1616
+ trigger,
1617
+ ...custom
1618
+ };
1619
+ return details;
1620
+ }
1621
+ function createGenericEventDetails(reason, event, customProperties) {
1622
+ const custom = customProperties ?? EMPTY_OBJECT;
1623
+ const details = {
1624
+ reason,
1625
+ event: event ?? new Event('base-ui'),
1626
+ ...custom
1627
+ };
1628
+ return details;
1629
+ }
1630
+
1060
1631
  const FocusGuard = /*#__PURE__*/React.forwardRef(function FocusGuard(props, ref) {
1061
1632
  const [role, setRole] = React.useState();
1062
1633
  useIsoLayoutEffect(() => {
@@ -1796,6 +2367,44 @@ function markOthers(avoidElements, options = {}) {
1796
2367
  });
1797
2368
  }
1798
2369
 
2370
+ let globalId = 0;
2371
+
2372
+ // TODO React 17: Remove `useGlobalId` once React 17 support is removed
2373
+ function useGlobalId(idOverride, prefix = 'mui') {
2374
+ const [defaultId, setDefaultId] = React.useState(idOverride);
2375
+ const id = idOverride || defaultId;
2376
+ React.useEffect(() => {
2377
+ if (defaultId == null) {
2378
+ // Fallback to this default id when possible.
2379
+ // Use the incrementing value for client-side rendering only.
2380
+ // We can't use it server-side.
2381
+ // If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
2382
+ globalId += 1;
2383
+ setDefaultId(`${prefix}-${globalId}`);
2384
+ }
2385
+ }, [defaultId, prefix]);
2386
+ return id;
2387
+ }
2388
+ const maybeReactUseId = SafeReact.useId;
2389
+
2390
+ /**
2391
+ *
2392
+ * @example <div id={useId()} />
2393
+ * @param idOverride
2394
+ * @returns {string}
2395
+ */
2396
+ function useId(idOverride, prefix) {
2397
+ // React.useId() is only available from React 17.0.0.
2398
+ if (maybeReactUseId !== undefined) {
2399
+ const reactId = maybeReactUseId();
2400
+ return idOverride ?? (prefix ? `${prefix}-${reactId}` : reactId);
2401
+ }
2402
+
2403
+ // TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler
2404
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- `React.useId` is invariant at runtime.
2405
+ return useGlobalId(idOverride, prefix);
2406
+ }
2407
+
1799
2408
  const TYPEAHEAD_RESET_MS = 500;
1800
2409
  const PATIENT_CLICK_THRESHOLD = 500;
1801
2410
  const DISABLED_TRANSITIONS_STYLE = {
@@ -2151,6 +2760,17 @@ function FloatingTree(props) {
2151
2760
  });
2152
2761
  }
2153
2762
 
2763
+ /**
2764
+ * If the provided argument is a ref object, returns its `current` value.
2765
+ * Otherwise, returns the argument itself.
2766
+ */
2767
+ function resolveRef(maybeRef) {
2768
+ if (maybeRef == null) {
2769
+ return maybeRef;
2770
+ }
2771
+ return 'current' in maybeRef ? maybeRef.current : maybeRef;
2772
+ }
2773
+
2154
2774
  function getEventType(event, lastInteractionType) {
2155
2775
  const win = getWindow(getTarget(event));
2156
2776
  if (event instanceof win.KeyboardEvent) {
@@ -3386,6 +4006,204 @@ function useSyncedFloatingRootContext(options) {
3386
4006
  return store;
3387
4007
  }
3388
4008
 
4009
+ /**
4010
+ * Provides a status string for CSS animations.
4011
+ * @param open - a boolean that determines if the element is open.
4012
+ * @param enableIdleState - a boolean that enables the `'idle'` state between `'starting'` and `'ending'`
4013
+ */
4014
+ function useTransitionStatus(open, enableIdleState = false, deferEndingState = false) {
4015
+ const [transitionStatus, setTransitionStatus] = React.useState(open && enableIdleState ? 'idle' : undefined);
4016
+ const [mounted, setMounted] = React.useState(open);
4017
+ if (open && !mounted) {
4018
+ setMounted(true);
4019
+ setTransitionStatus('starting');
4020
+ }
4021
+ if (!open && mounted && transitionStatus !== 'ending' && !deferEndingState) {
4022
+ setTransitionStatus('ending');
4023
+ }
4024
+ if (!open && !mounted && transitionStatus === 'ending') {
4025
+ setTransitionStatus(undefined);
4026
+ }
4027
+ useIsoLayoutEffect(() => {
4028
+ if (!open && mounted && transitionStatus !== 'ending' && deferEndingState) {
4029
+ const frame = AnimationFrame.request(() => {
4030
+ setTransitionStatus('ending');
4031
+ });
4032
+ return () => {
4033
+ AnimationFrame.cancel(frame);
4034
+ };
4035
+ }
4036
+ return undefined;
4037
+ }, [open, mounted, transitionStatus, deferEndingState]);
4038
+ useIsoLayoutEffect(() => {
4039
+ if (!open || enableIdleState) {
4040
+ return undefined;
4041
+ }
4042
+ const frame = AnimationFrame.request(() => {
4043
+ // Avoid `flushSync` here due to Firefox.
4044
+ // See https://github.com/mui/base-ui/pull/3424
4045
+ setTransitionStatus(undefined);
4046
+ });
4047
+ return () => {
4048
+ AnimationFrame.cancel(frame);
4049
+ };
4050
+ }, [enableIdleState, open]);
4051
+ useIsoLayoutEffect(() => {
4052
+ if (!open || !enableIdleState) {
4053
+ return undefined;
4054
+ }
4055
+ if (open && mounted && transitionStatus !== 'idle') {
4056
+ setTransitionStatus('starting');
4057
+ }
4058
+ const frame = AnimationFrame.request(() => {
4059
+ setTransitionStatus('idle');
4060
+ });
4061
+ return () => {
4062
+ AnimationFrame.cancel(frame);
4063
+ };
4064
+ }, [enableIdleState, open, mounted, transitionStatus]);
4065
+ return {
4066
+ mounted,
4067
+ setMounted,
4068
+ transitionStatus
4069
+ };
4070
+ }
4071
+
4072
+ let TransitionStatusDataAttributes = /*#__PURE__*/function (TransitionStatusDataAttributes) {
4073
+ /**
4074
+ * Present when the component is animating in.
4075
+ */
4076
+ TransitionStatusDataAttributes["startingStyle"] = "data-starting-style";
4077
+ /**
4078
+ * Present when the component is animating out.
4079
+ */
4080
+ TransitionStatusDataAttributes["endingStyle"] = "data-ending-style";
4081
+ return TransitionStatusDataAttributes;
4082
+ }({});
4083
+ const STARTING_HOOK = {
4084
+ [TransitionStatusDataAttributes.startingStyle]: ''
4085
+ };
4086
+ const ENDING_HOOK = {
4087
+ [TransitionStatusDataAttributes.endingStyle]: ''
4088
+ };
4089
+ const transitionStatusMapping = {
4090
+ transitionStatus(value) {
4091
+ if (value === 'starting') {
4092
+ return STARTING_HOOK;
4093
+ }
4094
+ if (value === 'ending') {
4095
+ return ENDING_HOOK;
4096
+ }
4097
+ return null;
4098
+ }
4099
+ };
4100
+
4101
+ /**
4102
+ * Executes a function once all animations have finished on the provided element.
4103
+ * @param elementOrRef - The element to watch for animations.
4104
+ * @param waitForStartingStyleRemoved - Whether to wait for [data-starting-style] to be removed before checking for animations.
4105
+ * @param treatAbortedAsFinished - Whether to treat aborted animations as finished. If `false`, and there are aborted animations,
4106
+ * the function will check again if any new animations have started and wait for them to finish.
4107
+ * @returns A function that takes a callback to execute once all animations have finished, and an optional AbortSignal to abort the callback
4108
+ */
4109
+ function useAnimationsFinished(elementOrRef, waitForStartingStyleRemoved = false, treatAbortedAsFinished = true) {
4110
+ const frame = useAnimationFrame();
4111
+ return useStableCallback((fnToExecute,
4112
+ /**
4113
+ * An optional [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that
4114
+ * can be used to abort `fnToExecute` before all the animations have finished.
4115
+ * @default null
4116
+ */
4117
+ signal = null) => {
4118
+ frame.cancel();
4119
+ const element = resolveRef(elementOrRef);
4120
+ if (element == null) {
4121
+ return;
4122
+ }
4123
+ const resolvedElement = element;
4124
+ const done = () => {
4125
+ // Synchronously flush the unmounting of the component so that the browser doesn't
4126
+ // paint: https://github.com/mui/base-ui/issues/979
4127
+ ReactDOM.flushSync(fnToExecute);
4128
+ };
4129
+ if (typeof resolvedElement.getAnimations !== 'function' || globalThis.BASE_UI_ANIMATIONS_DISABLED) {
4130
+ fnToExecute();
4131
+ return;
4132
+ }
4133
+ function exec() {
4134
+ Promise.all(resolvedElement.getAnimations().map(animation => animation.finished)).then(() => {
4135
+ if (!signal?.aborted) {
4136
+ done();
4137
+ }
4138
+ }).catch(() => {
4139
+ if (treatAbortedAsFinished) {
4140
+ if (!signal?.aborted) {
4141
+ done();
4142
+ }
4143
+ return;
4144
+ }
4145
+ const currentAnimations = resolvedElement.getAnimations();
4146
+ if (!signal?.aborted && currentAnimations.length > 0 && currentAnimations.some(animation => animation.pending || animation.playState !== 'finished')) {
4147
+ // Sometimes animations can be aborted because a property they depend on changes while the animation plays.
4148
+ // In such cases, we need to re-check if any new animations have started.
4149
+ exec();
4150
+ }
4151
+ });
4152
+ }
4153
+ if (waitForStartingStyleRemoved) {
4154
+ const startingStyleAttribute = TransitionStatusDataAttributes.startingStyle;
4155
+
4156
+ // If `[data-starting-style]` isn't present, fall back to waiting one more frame
4157
+ // to give "open" animations a chance to be registered.
4158
+ if (!resolvedElement.hasAttribute(startingStyleAttribute)) {
4159
+ frame.request(exec);
4160
+ return;
4161
+ }
4162
+
4163
+ // Wait for `[data-starting-style]` to have been removed.
4164
+ const attributeObserver = new MutationObserver(() => {
4165
+ if (!resolvedElement.hasAttribute(startingStyleAttribute)) {
4166
+ attributeObserver.disconnect();
4167
+ exec();
4168
+ }
4169
+ });
4170
+ attributeObserver.observe(resolvedElement, {
4171
+ attributes: true,
4172
+ attributeFilter: [startingStyleAttribute]
4173
+ });
4174
+ signal?.addEventListener('abort', () => attributeObserver.disconnect(), {
4175
+ once: true
4176
+ });
4177
+ return;
4178
+ }
4179
+ frame.request(exec);
4180
+ });
4181
+ }
4182
+
4183
+ /**
4184
+ * Calls the provided function when the CSS open/close animation or transition completes.
4185
+ */
4186
+ function useOpenChangeComplete(parameters) {
4187
+ const {
4188
+ enabled = true,
4189
+ open,
4190
+ ref,
4191
+ onComplete: onCompleteParam
4192
+ } = parameters;
4193
+ const onComplete = useStableCallback(onCompleteParam);
4194
+ const runOnceAnimationsFinish = useAnimationsFinished(ref, open, false);
4195
+ React.useEffect(() => {
4196
+ if (!enabled) {
4197
+ return undefined;
4198
+ }
4199
+ const abortController = new AbortController();
4200
+ runOnceAnimationsFinish(onComplete, abortController.signal);
4201
+ return () => {
4202
+ abortController.abort();
4203
+ };
4204
+ }, [enabled, open, onComplete, runOnceAnimationsFinish]);
4205
+ }
4206
+
3389
4207
  const FOCUSABLE_POPUP_PROPS = {
3390
4208
  tabIndex: -1,
3391
4209
  [FOCUSABLE_ATTRIBUTE]: ''
@@ -3795,6 +4613,15 @@ class PopupTriggerMap {
3795
4613
  }
3796
4614
  }
3797
4615
 
4616
+ /**
4617
+ * Wraps `useId` and prefixes generated `id`s with `base-ui-`
4618
+ * @param {string | undefined} idOverride overrides the generated id when provided
4619
+ * @returns {string | undefined}
4620
+ */
4621
+ function useBaseUiId(idOverride) {
4622
+ return useId(idOverride, 'base-ui');
4623
+ }
4624
+
3798
4625
  /**
3799
4626
  * Provides a cross-browser way to determine the type of the pointer used to click.
3800
4627
  * Safari and Firefox do not provide the PointerEvent to the click handler (they use MouseEvent) yet.
@@ -3831,6 +4658,20 @@ function useEnhancedClickHandler(handler) {
3831
4658
  };
3832
4659
  }
3833
4660
 
4661
+ function useValueChanged(value, onChange) {
4662
+ const valueRef = React.useRef(value);
4663
+ const onChangeCallback = useStableCallback(onChange);
4664
+ useIsoLayoutEffect(() => {
4665
+ if (valueRef.current === value) {
4666
+ return;
4667
+ }
4668
+ onChangeCallback(valueRef.current);
4669
+ }, [value, onChangeCallback]);
4670
+ useIsoLayoutEffect(() => {
4671
+ valueRef.current = value;
4672
+ }, [value]);
4673
+ }
4674
+
3834
4675
  function useOpenMethodTriggerProps(open, setOpenMethod) {
3835
4676
  const handleTriggerClick = useStableCallback((_, interactionType) => {
3836
4677
  const isOpen = typeof open === 'function' ? open() : open;
@@ -3881,6 +4722,228 @@ function useDirection() {
3881
4722
  return context?.direction ?? 'ltr';
3882
4723
  }
3883
4724
 
4725
+ const CompositeRootContext = /*#__PURE__*/React.createContext(undefined);
4726
+ if (process.env.NODE_ENV !== "production") CompositeRootContext.displayName = "CompositeRootContext";
4727
+ function useCompositeRootContext(optional = false) {
4728
+ const context = React.useContext(CompositeRootContext);
4729
+ if (context === undefined && !optional) {
4730
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: CompositeRootContext is missing. Composite parts must be placed within <Composite.Root>.' : formatErrorMessage(16));
4731
+ }
4732
+ return context;
4733
+ }
4734
+
4735
+ function useFocusableWhenDisabled(parameters) {
4736
+ const {
4737
+ focusableWhenDisabled,
4738
+ disabled,
4739
+ composite = false,
4740
+ tabIndex: tabIndexProp = 0,
4741
+ isNativeButton
4742
+ } = parameters;
4743
+ const isFocusableComposite = composite && focusableWhenDisabled !== false;
4744
+ const isNonFocusableComposite = composite && focusableWhenDisabled === false;
4745
+
4746
+ // we can't explicitly assign `undefined` to any of these props because it
4747
+ // would otherwise prevent subsequently merged props from setting them
4748
+ const props = React.useMemo(() => {
4749
+ const additionalProps = {
4750
+ // allow Tabbing away from focusableWhenDisabled elements
4751
+ onKeyDown(event) {
4752
+ if (disabled && focusableWhenDisabled && event.key !== 'Tab') {
4753
+ event.preventDefault();
4754
+ }
4755
+ }
4756
+ };
4757
+ if (!composite) {
4758
+ additionalProps.tabIndex = tabIndexProp;
4759
+ if (!isNativeButton && disabled) {
4760
+ additionalProps.tabIndex = focusableWhenDisabled ? tabIndexProp : -1;
4761
+ }
4762
+ }
4763
+ if (isNativeButton && (focusableWhenDisabled || isFocusableComposite) || !isNativeButton && disabled) {
4764
+ additionalProps['aria-disabled'] = disabled;
4765
+ }
4766
+ if (isNativeButton && (!focusableWhenDisabled || isNonFocusableComposite)) {
4767
+ additionalProps.disabled = disabled;
4768
+ }
4769
+ return additionalProps;
4770
+ }, [composite, disabled, focusableWhenDisabled, isFocusableComposite, isNonFocusableComposite, isNativeButton, tabIndexProp]);
4771
+ return {
4772
+ props
4773
+ };
4774
+ }
4775
+
4776
+ function useButton(parameters = {}) {
4777
+ const {
4778
+ disabled = false,
4779
+ focusableWhenDisabled,
4780
+ tabIndex = 0,
4781
+ native: isNativeButton = true,
4782
+ composite: compositeProp
4783
+ } = parameters;
4784
+ const elementRef = React.useRef(null);
4785
+ const compositeRootContext = useCompositeRootContext(true);
4786
+ const isCompositeItem = compositeProp ?? compositeRootContext !== undefined;
4787
+ const {
4788
+ props: focusableWhenDisabledProps
4789
+ } = useFocusableWhenDisabled({
4790
+ focusableWhenDisabled,
4791
+ disabled,
4792
+ composite: isCompositeItem,
4793
+ tabIndex,
4794
+ isNativeButton
4795
+ });
4796
+ if (process.env.NODE_ENV !== 'production') {
4797
+ // eslint-disable-next-line react-hooks/rules-of-hooks
4798
+ React.useEffect(() => {
4799
+ if (!elementRef.current) {
4800
+ return;
4801
+ }
4802
+ const isButtonTag = isButtonElement(elementRef.current);
4803
+ if (isNativeButton) {
4804
+ if (!isButtonTag) {
4805
+ const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
4806
+ const message = 'A component that acts as a button expected a native <button> because the ' + '`nativeButton` prop is true. Rendering a non-<button> removes native button ' + 'semantics, which can impact forms and accessibility. Use a real <button> in the ' + '`render` prop, or set `nativeButton` to `false`.';
4807
+ error(`${message}${ownerStackMessage}`);
4808
+ }
4809
+ } else if (isButtonTag) {
4810
+ const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
4811
+ const message = 'A component that acts as a button expected a non-<button> because the `nativeButton` ' + 'prop is false. Rendering a <button> keeps native behavior while Base UI applies ' + 'non-native attributes and handlers, which can add unintended extra attributes (such ' + 'as `role` or `aria-disabled`). Use a non-<button> in the `render` prop, or set ' + '`nativeButton` to `true`.';
4812
+ error(`${message}${ownerStackMessage}`);
4813
+ }
4814
+ }, [isNativeButton]);
4815
+ }
4816
+
4817
+ // handles a disabled composite button rendering another button, e.g.
4818
+ // <Toolbar.Button disabled render={<Menu.Trigger />} />
4819
+ // the `disabled` prop needs to pass through 2 `useButton`s then finally
4820
+ // delete the `disabled` attribute from DOM
4821
+ const updateDisabled = React.useCallback(() => {
4822
+ const element = elementRef.current;
4823
+ if (!isButtonElement(element)) {
4824
+ return;
4825
+ }
4826
+ if (isCompositeItem && disabled && focusableWhenDisabledProps.disabled === undefined && element.disabled) {
4827
+ element.disabled = false;
4828
+ }
4829
+ }, [disabled, focusableWhenDisabledProps.disabled, isCompositeItem]);
4830
+ useIsoLayoutEffect(updateDisabled, [updateDisabled]);
4831
+ const getButtonProps = React.useCallback((externalProps = {}) => {
4832
+ const {
4833
+ onClick: externalOnClick,
4834
+ onMouseDown: externalOnMouseDown,
4835
+ onKeyUp: externalOnKeyUp,
4836
+ onKeyDown: externalOnKeyDown,
4837
+ onPointerDown: externalOnPointerDown,
4838
+ ...otherExternalProps
4839
+ } = externalProps;
4840
+ return mergeProps({
4841
+ onClick(event) {
4842
+ if (disabled) {
4843
+ event.preventDefault();
4844
+ return;
4845
+ }
4846
+ externalOnClick?.(event);
4847
+ },
4848
+ onMouseDown(event) {
4849
+ if (!disabled) {
4850
+ externalOnMouseDown?.(event);
4851
+ }
4852
+ },
4853
+ onKeyDown(event) {
4854
+ if (disabled) {
4855
+ return;
4856
+ }
4857
+ makeEventPreventable(event);
4858
+ externalOnKeyDown?.(event);
4859
+ if (event.baseUIHandlerPrevented) {
4860
+ return;
4861
+ }
4862
+ const isCurrentTarget = event.target === event.currentTarget;
4863
+ const currentTarget = event.currentTarget;
4864
+ const isButton = isButtonElement(currentTarget);
4865
+ const isLink = !isNativeButton && isValidLinkElement(currentTarget);
4866
+ const shouldClick = isCurrentTarget && (isNativeButton ? isButton : !isLink);
4867
+ const isEnterKey = event.key === 'Enter';
4868
+ const isSpaceKey = event.key === ' ';
4869
+ const role = currentTarget.getAttribute('role');
4870
+ const isTextNavigationRole = role?.startsWith('menuitem') || role === 'option' || role === 'gridcell';
4871
+ if (isCurrentTarget && isCompositeItem && isSpaceKey) {
4872
+ if (event.defaultPrevented && isTextNavigationRole) {
4873
+ return;
4874
+ }
4875
+ event.preventDefault();
4876
+ if (isLink || isNativeButton && isButton) {
4877
+ currentTarget.click();
4878
+ event.preventBaseUIHandler();
4879
+ } else if (shouldClick) {
4880
+ externalOnClick?.(event);
4881
+ event.preventBaseUIHandler();
4882
+ }
4883
+ return;
4884
+ }
4885
+
4886
+ // Keyboard accessibility for native and non-native elements.
4887
+ if (shouldClick) {
4888
+ if (!isNativeButton && (isSpaceKey || isEnterKey)) {
4889
+ event.preventDefault();
4890
+ }
4891
+ if (!isNativeButton && isEnterKey) {
4892
+ externalOnClick?.(event);
4893
+ }
4894
+ }
4895
+ },
4896
+ onKeyUp(event) {
4897
+ if (disabled) {
4898
+ return;
4899
+ }
4900
+
4901
+ // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
4902
+ // https://codesandbox.io/p/sandbox/button-keyup-preventdefault-dn7f0
4903
+ makeEventPreventable(event);
4904
+ externalOnKeyUp?.(event);
4905
+ if (event.target === event.currentTarget && isNativeButton && isCompositeItem && isButtonElement(event.currentTarget) && event.key === ' ') {
4906
+ event.preventDefault();
4907
+ return;
4908
+ }
4909
+ if (event.baseUIHandlerPrevented) {
4910
+ return;
4911
+ }
4912
+
4913
+ // Keyboard accessibility for non interactive elements
4914
+ if (event.target === event.currentTarget && !isNativeButton && !isCompositeItem && event.key === ' ') {
4915
+ externalOnClick?.(event);
4916
+ }
4917
+ },
4918
+ onPointerDown(event) {
4919
+ if (disabled) {
4920
+ event.preventDefault();
4921
+ return;
4922
+ }
4923
+ externalOnPointerDown?.(event);
4924
+ }
4925
+ }, isNativeButton ? {
4926
+ type: 'button'
4927
+ } : {
4928
+ role: 'button'
4929
+ }, focusableWhenDisabledProps, otherExternalProps);
4930
+ }, [disabled, focusableWhenDisabledProps, isCompositeItem, isNativeButton]);
4931
+ const buttonRef = useStableCallback(element => {
4932
+ elementRef.current = element;
4933
+ updateDisabled();
4934
+ });
4935
+ return {
4936
+ getButtonProps,
4937
+ buttonRef
4938
+ };
4939
+ }
4940
+ function isButtonElement(elem) {
4941
+ return isHTMLElement(elem) && elem.tagName === 'BUTTON';
4942
+ }
4943
+ function isValidLinkElement(elem) {
4944
+ return Boolean(elem?.tagName === 'A' && elem?.href);
4945
+ }
4946
+
3884
4947
  let CommonPopupDataAttributes = function (CommonPopupDataAttributes) {
3885
4948
  /**
3886
4949
  * Present when the popup is open.
@@ -4244,4 +5307,4 @@ function useScrollLock(enabled = true, referenceElement = null) {
4244
5307
  }, [enabled, referenceElement]);
4245
5308
  }
4246
5309
 
4247
- export { useOpenStateTransitions as $, ARROW_UP as A, pressableTriggerOpenStateMapping as B, CommonPopupDataAttributes as C, DISABLED_TRANSITIONS_STYLE as D, FloatingPortal as E, FOCUSABLE_POPUP_PROPS as F, inertValue as G, DROPDOWN_COLLISION_AVOIDANCE as H, InternalBackdrop as I, webkit as J, mergeCleanups as K, isTypeableElement as L, matchesFocusVisible as M, isTargetInsideEnabledTrigger as N, createAttribute as O, PopupTriggerMap as P, isInteractiveElement as Q, getNodeChildren as R, Store as S, POPUP_COLLISION_AVOIDANCE as T, FloatingNode as U, ReactStore as V, FloatingTreeStore as W, fastComponent as X, useInitialOpenSync as Y, useFloatingNodeId as Z, useImplicitActiveTrigger as _, FloatingFocusManager as a, attachPreventUnmountOnClose as a0, setPopupOpenState as a1, useSyncedFloatingRootContext as a2, TYPEAHEAD_RESET_MS as a3, usePopupInteractionProps as a4, FloatingTree as a5, getTabbableBeforeElement as a6, isOutsideEvent as a7, getTabbableAfterElement as a8, getNextTabbable as a9, fastComponentRef as aa, useTriggerDataForwarding as ab, FocusGuard as ac, PATIENT_CLICK_THRESHOLD as ad, useTriggerRegistration as ae, triggerOpenStateMapping as af, usePopupRootSync as ag, usePopupStore as ah, useOpenMethodTriggerProps as ai, CLICK_TRIGGER_IDENTIFIER as aj, BASE_UI_SWIPE_IGNORE_SELECTOR as ak, getGridNavigatedIndex as al, gecko as am, FloatingRootStore as b, createDefaultInitialFocus as c, useFloatingTree as d, isIndexOutOfListBounds as e, getMinListIndex as f, getFloatingFocusElement as g, getMaxListIndex as h, isTypeableCombobox as i, findNonDisabledListIndex as j, enqueueFocus as k, ARROW_DOWN as l, ARROW_LEFT as m, ARROW_RIGHT as n, isListIndexDisabled as o, popupStateMapping as p, isElementVisible as q, useDirection as r, useScrollLock as s, createSelector as t, useFloatingParentNodeId as u, useOpenInteractionType as v, useStore as w, useClick as x, useDismiss as y, useOnFirstRender as z };
5310
+ export { cancelOpen as $, inputBlur as A, inputPaste as B, CommonPopupDataAttributes as C, activeElement as D, createGenericEventDetails as E, FOCUSABLE_POPUP_PROPS as F, wheel as G, useOnMount as H, incrementPress as I, decrementPress as J, useValueChanged as K, stopEvent as L, keyboard as M, createSelector as N, useOpenInteractionType as O, useStore as P, useClick as Q, useDismiss as R, SafeReact as S, Timeout as T, useOnFirstRender as U, Store as V, focusOut as W, outsidePress as X, pressableTriggerOpenStateMapping as Y, getFloatingFocusElement as Z, contains as _, useIsoLayoutEffect as a, getTabbableBeforeElement as a$, FloatingPortal as a0, InternalBackdrop as a1, inertValue as a2, DROPDOWN_COLLISION_AVOIDANCE as a3, useDirection as a4, useAnimationFrame as a5, webkit as a6, windowResize as a7, isVirtualClick as a8, itemPress as a9, isTargetInsideEnabledTrigger as aA, triggerFocus as aB, createAttribute as aC, mac as aD, triggerPress as aE, escapeKey as aF, isInteractiveElement as aG, getNodeChildren as aH, triggerHover as aI, useAnimationsFinished as aJ, POPUP_COLLISION_AVOIDANCE as aK, FloatingNode as aL, siblingOpen as aM, ReactStore as aN, FloatingTreeStore as aO, fastComponent as aP, useInitialOpenSync as aQ, useFloatingNodeId as aR, useImplicitActiveTrigger as aS, useOpenStateTransitions as aT, attachPreventUnmountOnClose as aU, setPopupOpenState as aV, useSyncedFloatingRootContext as aW, TYPEAHEAD_RESET_MS as aX, usePopupInteractionProps as aY, FloatingTree as aZ, useCompositeRootContext as a_, useId as aa, useFloatingParentNodeId as ab, FloatingRootStore as ac, PopupTriggerMap as ad, useFloatingTree as ae, isTypeableCombobox as af, isIndexOutOfListBounds as ag, getMinListIndex as ah, getMaxListIndex as ai, listNavigation as aj, findNonDisabledListIndex as ak, enqueueFocus as al, isVirtualPointerEvent as am, ARROW_UP as an, ARROW_DOWN as ao, ARROW_LEFT as ap, ARROW_RIGHT as aq, isListIndexDisabled as ar, isElementVisible as as, jsdom as at, DISABLED_TRANSITIONS_STYLE as au, useScrollLock as av, isMouseLikePointerType as aw, mergeCleanups as ax, isTypeableElement as ay, matchesFocusVisible as az, useStableCallback as b, isOutsideEvent as b0, getTabbableAfterElement as b1, getNextTabbable as b2, fastComponentRef as b3, useTriggerDataForwarding as b4, FocusGuard as b5, PATIENT_CLICK_THRESHOLD as b6, useTriggerRegistration as b7, triggerOpenStateMapping as b8, usePopupRootSync as b9, usePopupStore as ba, useOpenMethodTriggerProps as bb, CLICK_TRIGGER_IDENTIFIER as bc, android as bd, closeWatcher as be, BASE_UI_SWIPE_IGNORE_SELECTOR as bf, TransitionStatusDataAttributes as bg, swipe as bh, getGridNavigatedIndex as bi, inputPress as bj, gecko as bk, clearPress as bl, chipRemovePress as bm, pointer as bn, scrub as bo, useButton as c, createChangeEventDetails as d, closePress as e, useOpenChangeComplete as f, getTarget as g, createDefaultInitialFocus as h, FloatingFocusManager as i, imperativeAction as j, useTimeout as k, error as l, useTransitionStatus as m, useControlled as n, ownerDocument as o, popupStateMapping as p, useValueAsRef as q, ios as r, addEventListener as s, transitionStatusMapping as t, useBaseUiId as u, none as v, visuallyHiddenInput as w, visuallyHidden as x, inputChange as y, inputClear as z };