@jobber/components 8.14.1 → 8.16.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 (46) hide show
  1. package/dist/Autocomplete-es.js +1 -1
  2. package/dist/BottomSheet-cjs.js +109 -77
  3. package/dist/BottomSheet-es.js +103 -75
  4. package/dist/Card/index.cjs +2 -3
  5. package/dist/Card/index.mjs +2 -3
  6. package/dist/DataDump/index.cjs +2 -3
  7. package/dist/DataDump/index.mjs +2 -3
  8. package/dist/InputNumberExperimental-cjs.js +16 -17
  9. package/dist/InputNumberExperimental-es.js +2 -3
  10. package/dist/Menu/index.cjs +2 -3
  11. package/dist/Menu/index.mjs +2 -3
  12. package/dist/Menu-cjs.js +11 -56
  13. package/dist/Menu-es.js +4 -49
  14. package/dist/MenuSubmenuTrigger-cjs.js +1608 -1793
  15. package/dist/MenuSubmenuTrigger-es.js +1340 -1522
  16. package/dist/Modal/index.mjs +1 -1
  17. package/dist/NumberFieldInput-cjs.js +53 -54
  18. package/dist/NumberFieldInput-es.js +2 -3
  19. package/dist/Page/index.cjs +2 -3
  20. package/dist/Page/index.mjs +2 -3
  21. package/dist/{DrawerRoot-cjs.js → ScrollAreaViewport-cjs.js} +5488 -4449
  22. package/dist/{DrawerRoot-es.js → ScrollAreaViewport-es.js} +5423 -4393
  23. package/dist/{useValueChanged-cjs.js → clamp-cjs.js} +573 -321
  24. package/dist/{useValueChanged-es.js → clamp-es.js} +569 -322
  25. package/dist/floating-ui.react-es.js +1 -1
  26. package/dist/index.cjs +2 -3
  27. package/dist/index.mjs +2 -3
  28. package/dist/primitives/BottomSheet/BottomSheet.d.ts +15 -16
  29. package/dist/primitives/BottomSheet/BottomSheet.types.d.ts +27 -0
  30. package/dist/primitives/BottomSheet/BottomSheet.utils.d.ts +6 -0
  31. package/dist/primitives/BottomSheet/index.cjs +8 -3
  32. package/dist/primitives/BottomSheet/index.d.ts +1 -0
  33. package/dist/primitives/BottomSheet/index.mjs +8 -3
  34. package/dist/primitives/InputNumberExperimental/index.cjs +2 -3
  35. package/dist/primitives/InputNumberExperimental/index.mjs +2 -3
  36. package/dist/primitives/index.cjs +8 -9
  37. package/dist/primitives/index.mjs +8 -9
  38. package/dist/styles.css +62 -1
  39. package/dist/unstyledPrimitives/index.cjs +6373 -5868
  40. package/dist/unstyledPrimitives/index.d.ts +2 -0
  41. package/dist/unstyledPrimitives/index.mjs +6325 -5822
  42. package/dist/useRenderElement-cjs.js +30 -30
  43. package/dist/useRenderElement-es.js +31 -31
  44. package/package.json +2 -2
  45. package/dist/useBaseUiId-cjs.js +0 -275
  46. package/dist/useBaseUiId-es.js +0 -251
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var useRenderElement = require('./useRenderElement-cjs.js');
4
3
  var React = require('react');
4
+ var useRenderElement = require('./useRenderElement-cjs.js');
5
5
  var ReactDOM = require('react-dom');
6
+ var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
6
7
 
7
8
  function _interopNamespaceDefault(e) {
8
9
  var n = Object.create(null);
@@ -24,35 +25,6 @@ function _interopNamespaceDefault(e) {
24
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
25
26
  var ReactDOM__namespace = /*#__PURE__*/_interopNamespaceDefault(ReactDOM);
26
27
 
27
- let TransitionStatusDataAttributes = /*#__PURE__*/function (TransitionStatusDataAttributes) {
28
- /**
29
- * Present when the component is animating in.
30
- */
31
- TransitionStatusDataAttributes["startingStyle"] = "data-starting-style";
32
- /**
33
- * Present when the component is animating out.
34
- */
35
- TransitionStatusDataAttributes["endingStyle"] = "data-ending-style";
36
- return TransitionStatusDataAttributes;
37
- }({});
38
- const STARTING_HOOK = {
39
- [TransitionStatusDataAttributes.startingStyle]: ''
40
- };
41
- const ENDING_HOOK = {
42
- [TransitionStatusDataAttributes.endingStyle]: ''
43
- };
44
- const transitionStatusMapping = {
45
- transitionStatus(value) {
46
- if (value === 'starting') {
47
- return STARTING_HOOK;
48
- }
49
- if (value === 'ending') {
50
- return ENDING_HOOK;
51
- }
52
- return null;
53
- }
54
- };
55
-
56
28
  // https://github.com/mui/material-ui/issues/41190#issuecomment-2040873379
57
29
  const useInsertionEffect = React__namespace[`useInsertionEffect${Math.random().toFixed(1)}`.slice(0, -3)];
58
30
  const useSafeInsertionEffect =
@@ -93,147 +65,187 @@ function assertNotCalled() {
93
65
  }
94
66
  }
95
67
 
96
- // https://github.com/mui/material-ui/issues/41190#issuecomment-2040873379
97
- const SafeReact = {
98
- ...React__namespace
99
- };
68
+ function useControlled({
69
+ controlled,
70
+ default: defaultProp,
71
+ name,
72
+ state = 'value'
73
+ }) {
74
+ // isControlled is ignored in the hook dependency lists as it should never change.
75
+ const {
76
+ current: isControlled
77
+ } = React__namespace.useRef(controlled !== undefined);
78
+ const [valueState, setValue] = React__namespace.useState(defaultProp);
79
+ const value = isControlled ? controlled : valueState;
80
+ if (process.env.NODE_ENV !== 'production') {
81
+ React__namespace.useEffect(() => {
82
+ if (isControlled !== (controlled !== undefined)) {
83
+ console.error([`Base UI: 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'));
84
+ }
85
+ }, [state, name, controlled]);
86
+ const {
87
+ current: defaultValue
88
+ } = React__namespace.useRef(defaultProp);
89
+ React__namespace.useEffect(() => {
90
+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is for more details.
91
+ if (!isControlled && JSON.stringify(defaultValue) !== JSON.stringify(defaultProp)) {
92
+ console.error([`Base UI: 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'));
93
+ }
94
+ }, [JSON.stringify(defaultProp)]);
95
+ }
96
+ const setValueIfUncontrolled = React__namespace.useCallback(newValue => {
97
+ if (!isControlled) {
98
+ setValue(newValue);
99
+ }
100
+ }, []);
101
+ return [value, setValueIfUncontrolled];
102
+ }
100
103
 
101
104
  const noop = () => {};
102
105
  const useIsoLayoutEffect = typeof document !== 'undefined' ? React__namespace.useLayoutEffect : noop;
103
106
 
104
- const none = 'none';
105
- const triggerPress = 'trigger-press';
106
- const triggerHover = 'trigger-hover';
107
- const triggerFocus = 'trigger-focus';
108
- const outsidePress = 'outside-press';
109
- const itemPress = 'item-press';
110
- const closePress = 'close-press';
111
- const clearPress = 'clear-press';
112
- const chipRemovePress = 'chip-remove-press';
113
- const incrementPress = 'increment-press';
114
- const decrementPress = 'decrement-press';
115
- const inputChange = 'input-change';
116
- const inputClear = 'input-clear';
117
- const inputBlur = 'input-blur';
118
- const inputPaste = 'input-paste';
119
- const inputPress = 'input-press';
120
- const focusOut = 'focus-out';
121
- const escapeKey = 'escape-key';
122
- const closeWatcher = 'close-watcher';
123
- const listNavigation = 'list-navigation';
124
- const keyboard = 'keyboard';
125
- const scrub = 'scrub';
126
- const cancelOpen = 'cancel-open';
127
- const siblingOpen = 'sibling-open';
128
- const imperativeAction = 'imperative-action';
129
- const swipe = 'swipe';
107
+ const visuallyHiddenBase = {
108
+ clipPath: 'inset(50%)',
109
+ overflow: 'hidden',
110
+ whiteSpace: 'nowrap',
111
+ border: 0,
112
+ padding: 0,
113
+ width: 1,
114
+ height: 1,
115
+ margin: -1
116
+ };
117
+ const visuallyHidden = {
118
+ ...visuallyHiddenBase,
119
+ position: 'fixed',
120
+ top: 0,
121
+ left: 0
122
+ };
123
+ const visuallyHiddenInput = {
124
+ ...visuallyHiddenBase,
125
+ position: 'absolute'
126
+ };
130
127
 
131
- /**
132
- * Maps a change `reason` string to the corresponding native event type.
133
- */
128
+ const EMPTY$2 = [];
134
129
 
135
130
  /**
136
- * Details of custom change events emitted by Base UI components.
131
+ * A React.useEffect equivalent that runs once, when the component is mounted.
137
132
  */
133
+ function useOnMount(fn) {
134
+ // 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
135
+ /* eslint-disable react-hooks/exhaustive-deps */
136
+ React__namespace.useEffect(fn, EMPTY$2);
137
+ /* eslint-enable react-hooks/exhaustive-deps */
138
+ }
138
139
 
139
- /**
140
- * Details of custom generic events emitted by Base UI components.
141
- */
140
+ /** Unlike `setTimeout`, rAF doesn't guarantee a positive integer return value, so we can't have
141
+ * a monomorphic `uint` type with `0` meaning empty.
142
+ * See warning note at:
143
+ * https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame#return_value */
144
+ const EMPTY$1 = null;
145
+ let LAST_RAF = globalThis.requestAnimationFrame;
146
+ class Scheduler {
147
+ /* This implementation uses an array as a backing data-structure for frame callbacks.
148
+ * It allows `O(1)` callback cancelling by inserting a `null` in the array, though it
149
+ * never calls the native `cancelAnimationFrame` if there are no frames left. This can
150
+ * be much more efficient if there is a call pattern that alterns as
151
+ * "request-cancel-request-cancel-…".
152
+ * But in the case of "request-request-…-cancel-cancel-…", it leaves the final animation
153
+ * frame to run anyway. We turn that frame into a `O(1)` no-op via `callbacksCount`. */
142
154
 
143
- /**
144
- * Creates a Base UI event details object with the given reason and utilities
145
- * for preventing Base UI's internal event handling.
146
- */
147
- function createChangeEventDetails(reason, event, trigger, customProperties) {
148
- let canceled = false;
149
- let allowPropagation = false;
150
- const custom = customProperties ?? useRenderElement.EMPTY_OBJECT;
151
- const details = {
152
- reason,
153
- event: event ?? new Event('base-ui'),
154
- cancel() {
155
- canceled = true;
156
- },
157
- allowPropagation() {
158
- allowPropagation = true;
159
- },
160
- get isCanceled() {
161
- return canceled;
162
- },
163
- get isPropagationAllowed() {
164
- return allowPropagation;
165
- },
166
- trigger,
167
- ...custom
168
- };
169
- return details;
170
- }
171
- function createGenericEventDetails(reason, event, customProperties) {
172
- const custom = customProperties ?? useRenderElement.EMPTY_OBJECT;
173
- const details = {
174
- reason,
175
- event: event ?? new Event('base-ui'),
176
- ...custom
177
- };
178
- return details;
179
- }
155
+ callbacks = [];
156
+ callbacksCount = 0;
157
+ nextId = 1;
158
+ startId = 1;
159
+ isScheduled = false;
160
+ tick = timestamp => {
161
+ this.isScheduled = false;
162
+ const currentCallbacks = this.callbacks;
163
+ const currentCallbacksCount = this.callbacksCount;
180
164
 
181
- let globalId = 0;
165
+ // Update these before iterating, callbacks could call `requestAnimationFrame` again.
166
+ this.callbacks = [];
167
+ this.callbacksCount = 0;
168
+ this.startId = this.nextId;
169
+ if (currentCallbacksCount > 0) {
170
+ for (let i = 0; i < currentCallbacks.length; i += 1) {
171
+ currentCallbacks[i]?.(timestamp);
172
+ }
173
+ }
174
+ };
175
+ request(fn) {
176
+ const id = this.nextId;
177
+ this.nextId += 1;
178
+ this.callbacks.push(fn);
179
+ this.callbacksCount += 1;
182
180
 
183
- // TODO React 17: Remove `useGlobalId` once React 17 support is removed
184
- function useGlobalId(idOverride, prefix = 'mui') {
185
- const [defaultId, setDefaultId] = React__namespace.useState(idOverride);
186
- const id = idOverride || defaultId;
187
- React__namespace.useEffect(() => {
188
- if (defaultId == null) {
189
- // Fallback to this default id when possible.
190
- // Use the incrementing value for client-side rendering only.
191
- // We can't use it server-side.
192
- // If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
193
- globalId += 1;
194
- setDefaultId(`${prefix}-${globalId}`);
181
+ /* In a test environment with fake timers, a fake `requestAnimationFrame` can be called
182
+ * but there's no guarantee that the animation frame will actually run before the fake
183
+ * timers are teared, which leaves `isScheduled` set, but won't run our `tick()`. */
184
+ const didRAFChange = process.env.NODE_ENV !== 'production' && LAST_RAF !== requestAnimationFrame && (LAST_RAF = requestAnimationFrame, true);
185
+ if (!this.isScheduled || didRAFChange) {
186
+ requestAnimationFrame(this.tick);
187
+ this.isScheduled = true;
195
188
  }
196
- }, [defaultId, prefix]);
197
- return id;
189
+ return id;
190
+ }
191
+ cancel(id) {
192
+ const index = id - this.startId;
193
+ if (index < 0 || index >= this.callbacks.length) {
194
+ return;
195
+ }
196
+ this.callbacks[index] = null;
197
+ this.callbacksCount -= 1;
198
+ }
198
199
  }
199
- const maybeReactUseId = SafeReact.useId;
200
-
201
- /**
202
- *
203
- * @example <div id={useId()} />
204
- * @param idOverride
205
- * @returns {string}
206
- */
207
- function useId(idOverride, prefix) {
208
- // React.useId() is only available from React 17.0.0.
209
- if (maybeReactUseId !== undefined) {
210
- const reactId = maybeReactUseId();
211
- return idOverride ?? (prefix ? `${prefix}-${reactId}` : reactId);
200
+ const scheduler = new Scheduler();
201
+ class AnimationFrame {
202
+ static create() {
203
+ return new AnimationFrame();
204
+ }
205
+ static request(fn) {
206
+ return scheduler.request(fn);
207
+ }
208
+ static cancel(id) {
209
+ return scheduler.cancel(id);
212
210
  }
211
+ currentId = EMPTY$1;
213
212
 
214
- // TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler
215
- // eslint-disable-next-line react-hooks/rules-of-hooks -- `React.useId` is invariant at runtime.
216
- return useGlobalId(idOverride, prefix);
213
+ /**
214
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
215
+ */
216
+ request(fn) {
217
+ this.cancel();
218
+ this.currentId = scheduler.request(() => {
219
+ this.currentId = EMPTY$1;
220
+ fn();
221
+ });
222
+ }
223
+ cancel = () => {
224
+ if (this.currentId !== EMPTY$1) {
225
+ scheduler.cancel(this.currentId);
226
+ this.currentId = EMPTY$1;
227
+ }
228
+ };
229
+ disposeEffect = () => {
230
+ return this.cancel;
231
+ };
217
232
  }
218
233
 
219
- const EMPTY$2 = [];
220
-
221
234
  /**
222
- * A React.useEffect equivalent that runs once, when the component is mounted.
235
+ * A `requestAnimationFrame` with automatic cleanup and guard.
223
236
  */
224
- function useOnMount(fn) {
225
- // 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
226
- /* eslint-disable react-hooks/exhaustive-deps */
227
- React__namespace.useEffect(fn, EMPTY$2);
228
- /* eslint-enable react-hooks/exhaustive-deps */
237
+ function useAnimationFrame() {
238
+ const timeout = useRenderElement.useRefWithInit(AnimationFrame.create).current;
239
+ useOnMount(timeout.disposeEffect);
240
+ return timeout;
229
241
  }
230
242
 
231
- const EMPTY$1 = 0;
243
+ const EMPTY = 0;
232
244
  class Timeout {
233
245
  static create() {
234
246
  return new Timeout();
235
247
  }
236
- currentId = EMPTY$1;
248
+ currentId = EMPTY;
237
249
 
238
250
  /**
239
251
  * Executes `fn` after `delay`, clearing any previously scheduled call.
@@ -241,17 +253,17 @@ class Timeout {
241
253
  start(delay, fn) {
242
254
  this.clear();
243
255
  this.currentId = setTimeout(() => {
244
- this.currentId = EMPTY$1;
256
+ this.currentId = EMPTY;
245
257
  fn();
246
258
  }, delay); /* Node.js types are enabled in development */
247
259
  }
248
260
  isStarted() {
249
- return this.currentId !== EMPTY$1;
261
+ return this.currentId !== EMPTY;
250
262
  }
251
263
  clear = () => {
252
- if (this.currentId !== EMPTY$1) {
264
+ if (this.currentId !== EMPTY) {
253
265
  clearTimeout(this.currentId);
254
- this.currentId = EMPTY$1;
266
+ this.currentId = EMPTY;
255
267
  }
256
268
  };
257
269
  disposeEffect = () => {
@@ -268,6 +280,30 @@ function useTimeout() {
268
280
  return timeout;
269
281
  }
270
282
 
283
+ /**
284
+ * Untracks the provided value by turning it into a ref to remove its reactivity.
285
+ *
286
+ * Used to access the passed value inside `React.useEffect` without causing the effect to re-run when the value changes.
287
+ */
288
+ function useValueAsRef(value) {
289
+ const latest = useRenderElement.useRefWithInit(createLatestRef, value).current;
290
+ latest.next = value;
291
+
292
+ // eslint-disable-next-line react-hooks/exhaustive-deps
293
+ useIsoLayoutEffect(latest.effect);
294
+ return latest;
295
+ }
296
+ function createLatestRef(value) {
297
+ const latest = {
298
+ current: value,
299
+ next: value,
300
+ effect: () => {
301
+ latest.current = latest.next;
302
+ }
303
+ };
304
+ return latest;
305
+ }
306
+
271
307
  const hasNavigator = typeof navigator !== 'undefined';
272
308
  const nav = getNavigatorData();
273
309
  const platform = getPlatform();
@@ -370,153 +406,126 @@ function ownerDocument(node) {
370
406
  return node?.ownerDocument || document;
371
407
  }
372
408
 
409
+ const none = 'none';
410
+ const triggerPress = 'trigger-press';
411
+ const triggerHover = 'trigger-hover';
412
+ const triggerFocus = 'trigger-focus';
413
+ const outsidePress = 'outside-press';
414
+ const itemPress = 'item-press';
415
+ const closePress = 'close-press';
416
+ const clearPress = 'clear-press';
417
+ const chipRemovePress = 'chip-remove-press';
418
+ const incrementPress = 'increment-press';
419
+ const decrementPress = 'decrement-press';
420
+ const inputChange = 'input-change';
421
+ const inputClear = 'input-clear';
422
+ const inputBlur = 'input-blur';
423
+ const inputPaste = 'input-paste';
424
+ const inputPress = 'input-press';
425
+ const focusOut = 'focus-out';
426
+ const escapeKey = 'escape-key';
427
+ const closeWatcher = 'close-watcher';
428
+ const listNavigation = 'list-navigation';
429
+ const keyboard = 'keyboard';
430
+ const pointer = 'pointer';
431
+ const scrub = 'scrub';
432
+ const cancelOpen = 'cancel-open';
433
+ const siblingOpen = 'sibling-open';
434
+ const imperativeAction = 'imperative-action';
435
+ const swipe = 'swipe';
436
+
373
437
  /**
374
- * Untracks the provided value by turning it into a ref to remove its reactivity.
375
- *
376
- * Used to access the passed value inside `React.useEffect` without causing the effect to re-run when the value changes.
438
+ * Maps a change `reason` string to the corresponding native event type.
377
439
  */
378
- function useValueAsRef(value) {
379
- const latest = useRenderElement.useRefWithInit(createLatestRef, value).current;
380
- latest.next = value;
381
440
 
382
- // eslint-disable-next-line react-hooks/exhaustive-deps
383
- useIsoLayoutEffect(latest.effect);
384
- return latest;
385
- }
386
- function createLatestRef(value) {
387
- const latest = {
388
- current: value,
389
- next: value,
390
- effect: () => {
391
- latest.current = latest.next;
392
- }
393
- };
394
- return latest;
395
- }
396
-
397
- /** Unlike `setTimeout`, rAF doesn't guarantee a positive integer return value, so we can't have
398
- * a monomorphic `uint` type with `0` meaning empty.
399
- * See warning note at:
400
- * https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame#return_value */
401
- const EMPTY = null;
402
- let LAST_RAF = globalThis.requestAnimationFrame;
403
- class Scheduler {
404
- /* This implementation uses an array as a backing data-structure for frame callbacks.
405
- * It allows `O(1)` callback cancelling by inserting a `null` in the array, though it
406
- * never calls the native `cancelAnimationFrame` if there are no frames left. This can
407
- * be much more efficient if there is a call pattern that alterns as
408
- * "request-cancel-request-cancel-…".
409
- * But in the case of "request-request-…-cancel-cancel-…", it leaves the final animation
410
- * frame to run anyway. We turn that frame into a `O(1)` no-op via `callbacksCount`. */
411
-
412
- callbacks = [];
413
- callbacksCount = 0;
414
- nextId = 1;
415
- startId = 1;
416
- isScheduled = false;
417
- tick = timestamp => {
418
- this.isScheduled = false;
419
- const currentCallbacks = this.callbacks;
420
- const currentCallbacksCount = this.callbacksCount;
441
+ /**
442
+ * Details of custom change events emitted by Base UI components.
443
+ */
421
444
 
422
- // Update these before iterating, callbacks could call `requestAnimationFrame` again.
423
- this.callbacks = [];
424
- this.callbacksCount = 0;
425
- this.startId = this.nextId;
426
- if (currentCallbacksCount > 0) {
427
- for (let i = 0; i < currentCallbacks.length; i += 1) {
428
- currentCallbacks[i]?.(timestamp);
429
- }
430
- }
431
- };
432
- request(fn) {
433
- const id = this.nextId;
434
- this.nextId += 1;
435
- this.callbacks.push(fn);
436
- this.callbacksCount += 1;
445
+ /**
446
+ * Details of custom generic events emitted by Base UI components.
447
+ */
437
448
 
438
- /* In a test environment with fake timers, a fake `requestAnimationFrame` can be called
439
- * but there's no guarantee that the animation frame will actually run before the fake
440
- * timers are teared, which leaves `isScheduled` set, but won't run our `tick()`. */
441
- const didRAFChange = process.env.NODE_ENV !== 'production' && LAST_RAF !== requestAnimationFrame && (LAST_RAF = requestAnimationFrame, true);
442
- if (!this.isScheduled || didRAFChange) {
443
- requestAnimationFrame(this.tick);
444
- this.isScheduled = true;
445
- }
446
- return id;
447
- }
448
- cancel(id) {
449
- const index = id - this.startId;
450
- if (index < 0 || index >= this.callbacks.length) {
451
- return;
452
- }
453
- this.callbacks[index] = null;
454
- this.callbacksCount -= 1;
455
- }
449
+ /**
450
+ * Creates a Base UI event details object with the given reason and utilities
451
+ * for preventing Base UI's internal event handling.
452
+ */
453
+ function createChangeEventDetails(reason, event, trigger, customProperties) {
454
+ let canceled = false;
455
+ let allowPropagation = false;
456
+ const custom = customProperties ?? useRenderElement.EMPTY_OBJECT;
457
+ const details = {
458
+ reason,
459
+ event: event ?? new Event('base-ui'),
460
+ cancel() {
461
+ canceled = true;
462
+ },
463
+ allowPropagation() {
464
+ allowPropagation = true;
465
+ },
466
+ get isCanceled() {
467
+ return canceled;
468
+ },
469
+ get isPropagationAllowed() {
470
+ return allowPropagation;
471
+ },
472
+ trigger,
473
+ ...custom
474
+ };
475
+ return details;
476
+ }
477
+ function createGenericEventDetails(reason, event, customProperties) {
478
+ const custom = customProperties ?? useRenderElement.EMPTY_OBJECT;
479
+ const details = {
480
+ reason,
481
+ event: event ?? new Event('base-ui'),
482
+ ...custom
483
+ };
484
+ return details;
456
485
  }
457
- const scheduler = new Scheduler();
458
- class AnimationFrame {
459
- static create() {
460
- return new AnimationFrame();
461
- }
462
- static request(fn) {
463
- return scheduler.request(fn);
464
- }
465
- static cancel(id) {
466
- return scheduler.cancel(id);
467
- }
468
- currentId = EMPTY;
469
486
 
470
- /**
471
- * Executes `fn` after `delay`, clearing any previously scheduled call.
472
- */
473
- request(fn) {
474
- this.cancel();
475
- this.currentId = scheduler.request(() => {
476
- this.currentId = EMPTY;
477
- fn();
478
- });
479
- }
480
- cancel = () => {
481
- if (this.currentId !== EMPTY) {
482
- scheduler.cancel(this.currentId);
483
- this.currentId = EMPTY;
487
+ // https://github.com/mui/material-ui/issues/41190#issuecomment-2040873379
488
+ const SafeReact = {
489
+ ...React__namespace
490
+ };
491
+
492
+ let globalId = 0;
493
+
494
+ // TODO React 17: Remove `useGlobalId` once React 17 support is removed
495
+ function useGlobalId(idOverride, prefix = 'mui') {
496
+ const [defaultId, setDefaultId] = React__namespace.useState(idOverride);
497
+ const id = idOverride || defaultId;
498
+ React__namespace.useEffect(() => {
499
+ if (defaultId == null) {
500
+ // Fallback to this default id when possible.
501
+ // Use the incrementing value for client-side rendering only.
502
+ // We can't use it server-side.
503
+ // If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
504
+ globalId += 1;
505
+ setDefaultId(`${prefix}-${globalId}`);
484
506
  }
485
- };
486
- disposeEffect = () => {
487
- return this.cancel;
488
- };
507
+ }, [defaultId, prefix]);
508
+ return id;
489
509
  }
510
+ const maybeReactUseId = SafeReact.useId;
490
511
 
491
512
  /**
492
- * A `requestAnimationFrame` with automatic cleanup and guard.
513
+ *
514
+ * @example <div id={useId()} />
515
+ * @param idOverride
516
+ * @returns {string}
493
517
  */
494
- function useAnimationFrame() {
495
- const timeout = useRenderElement.useRefWithInit(AnimationFrame.create).current;
496
- useOnMount(timeout.disposeEffect);
497
- return timeout;
498
- }
518
+ function useId(idOverride, prefix) {
519
+ // React.useId() is only available from React 17.0.0.
520
+ if (maybeReactUseId !== undefined) {
521
+ const reactId = maybeReactUseId();
522
+ return idOverride ?? (prefix ? `${prefix}-${reactId}` : reactId);
523
+ }
499
524
 
500
- const visuallyHiddenBase = {
501
- clipPath: 'inset(50%)',
502
- overflow: 'hidden',
503
- whiteSpace: 'nowrap',
504
- border: 0,
505
- padding: 0,
506
- width: 1,
507
- height: 1,
508
- margin: -1
509
- };
510
- const visuallyHidden = {
511
- ...visuallyHiddenBase,
512
- position: 'fixed',
513
- top: 0,
514
- left: 0
515
- };
516
- const visuallyHiddenInput = {
517
- ...visuallyHiddenBase,
518
- position: 'absolute'
519
- };
525
+ // TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler
526
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- `React.useId` is invariant at runtime.
527
+ return useGlobalId(idOverride, prefix);
528
+ }
520
529
 
521
530
  /**
522
531
  * If the provided argument is a ref object, returns its `current` value.
@@ -592,6 +601,35 @@ function useTransitionStatus(open, enableIdleState = false, deferEndingState = f
592
601
  }), [mounted, transitionStatus]);
593
602
  }
594
603
 
604
+ let TransitionStatusDataAttributes = /*#__PURE__*/function (TransitionStatusDataAttributes) {
605
+ /**
606
+ * Present when the component is animating in.
607
+ */
608
+ TransitionStatusDataAttributes["startingStyle"] = "data-starting-style";
609
+ /**
610
+ * Present when the component is animating out.
611
+ */
612
+ TransitionStatusDataAttributes["endingStyle"] = "data-ending-style";
613
+ return TransitionStatusDataAttributes;
614
+ }({});
615
+ const STARTING_HOOK = {
616
+ [TransitionStatusDataAttributes.startingStyle]: ''
617
+ };
618
+ const ENDING_HOOK = {
619
+ [TransitionStatusDataAttributes.endingStyle]: ''
620
+ };
621
+ const transitionStatusMapping = {
622
+ transitionStatus(value) {
623
+ if (value === 'starting') {
624
+ return STARTING_HOOK;
625
+ }
626
+ if (value === 'ending') {
627
+ return ENDING_HOOK;
628
+ }
629
+ return null;
630
+ }
631
+ };
632
+
595
633
  /**
596
634
  * Executes a function once all animations have finished on the provided element.
597
635
  * @param elementOrRef - The element to watch for animations.
@@ -701,44 +739,13 @@ function useOpenChangeComplete(parameters) {
701
739
  }, [enabled, open, onComplete, runOnceAnimationsFinish]);
702
740
  }
703
741
 
704
- function clamp(val, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
705
- return Math.max(min, Math.min(val, max));
706
- }
707
-
708
- function useControlled({
709
- controlled,
710
- default: defaultProp,
711
- name,
712
- state = 'value'
713
- }) {
714
- // isControlled is ignored in the hook dependency lists as it should never change.
715
- const {
716
- current: isControlled
717
- } = React__namespace.useRef(controlled !== undefined);
718
- const [valueState, setValue] = React__namespace.useState(defaultProp);
719
- const value = isControlled ? controlled : valueState;
720
- if (process.env.NODE_ENV !== 'production') {
721
- React__namespace.useEffect(() => {
722
- if (isControlled !== (controlled !== undefined)) {
723
- console.error([`Base UI: 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'));
724
- }
725
- }, [state, name, controlled]);
726
- const {
727
- current: defaultValue
728
- } = React__namespace.useRef(defaultProp);
729
- React__namespace.useEffect(() => {
730
- // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is for more details.
731
- if (!isControlled && JSON.stringify(defaultValue) !== JSON.stringify(defaultProp)) {
732
- console.error([`Base UI: 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'));
733
- }
734
- }, [JSON.stringify(defaultProp)]);
735
- }
736
- const setValueIfUncontrolled = React__namespace.useCallback(newValue => {
737
- if (!isControlled) {
738
- setValue(newValue);
739
- }
740
- }, []);
741
- return [value, setValueIfUncontrolled];
742
+ /**
743
+ * Wraps `useId` and prefixes generated `id`s with `base-ui-`
744
+ * @param {string | undefined} idOverride overrides the generated id when provided
745
+ * @returns {string | undefined}
746
+ */
747
+ function useBaseUiId(idOverride) {
748
+ return useId(idOverride, 'base-ui');
742
749
  }
743
750
 
744
751
  function useValueChanged(value, onChange) {
@@ -755,6 +762,246 @@ function useValueChanged(value, onChange) {
755
762
  }, [value]);
756
763
  }
757
764
 
765
+ let set;
766
+ if (process.env.NODE_ENV !== 'production') {
767
+ set = new Set();
768
+ }
769
+ function error(...messages) {
770
+ if (process.env.NODE_ENV !== 'production') {
771
+ const messageKey = messages.join(' ');
772
+ if (!set.has(messageKey)) {
773
+ set.add(messageKey);
774
+ console.error(`Base UI: ${messageKey}`);
775
+ }
776
+ }
777
+ }
778
+
779
+ const CompositeRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
780
+ if (process.env.NODE_ENV !== "production") CompositeRootContext.displayName = "CompositeRootContext";
781
+ function useCompositeRootContext(optional = false) {
782
+ const context = React__namespace.useContext(CompositeRootContext);
783
+ if (context === undefined && !optional) {
784
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: CompositeRootContext is missing. Composite parts must be placed within <Composite.Root>.' : useRenderElement.formatErrorMessage(16));
785
+ }
786
+ return context;
787
+ }
788
+
789
+ function useFocusableWhenDisabled(parameters) {
790
+ const {
791
+ focusableWhenDisabled,
792
+ disabled,
793
+ composite = false,
794
+ tabIndex: tabIndexProp = 0,
795
+ isNativeButton
796
+ } = parameters;
797
+ const isFocusableComposite = composite && focusableWhenDisabled !== false;
798
+ const isNonFocusableComposite = composite && focusableWhenDisabled === false;
799
+
800
+ // we can't explicitly assign `undefined` to any of these props because it
801
+ // would otherwise prevent subsequently merged props from setting them
802
+ const props = React__namespace.useMemo(() => {
803
+ const additionalProps = {
804
+ // allow Tabbing away from focusableWhenDisabled elements
805
+ onKeyDown(event) {
806
+ if (disabled && focusableWhenDisabled && event.key !== 'Tab') {
807
+ event.preventDefault();
808
+ }
809
+ }
810
+ };
811
+ if (!composite) {
812
+ additionalProps.tabIndex = tabIndexProp;
813
+ if (!isNativeButton && disabled) {
814
+ additionalProps.tabIndex = focusableWhenDisabled ? tabIndexProp : -1;
815
+ }
816
+ }
817
+ if (isNativeButton && (focusableWhenDisabled || isFocusableComposite) || !isNativeButton && disabled) {
818
+ additionalProps['aria-disabled'] = disabled;
819
+ }
820
+ if (isNativeButton && (!focusableWhenDisabled || isNonFocusableComposite)) {
821
+ additionalProps.disabled = disabled;
822
+ }
823
+ return additionalProps;
824
+ }, [composite, disabled, focusableWhenDisabled, isFocusableComposite, isNonFocusableComposite, isNativeButton, tabIndexProp]);
825
+ return {
826
+ props
827
+ };
828
+ }
829
+
830
+ function useButton(parameters = {}) {
831
+ const {
832
+ disabled = false,
833
+ focusableWhenDisabled,
834
+ tabIndex = 0,
835
+ native: isNativeButton = true,
836
+ composite: compositeProp
837
+ } = parameters;
838
+ const elementRef = React__namespace.useRef(null);
839
+ const compositeRootContext = useCompositeRootContext(true);
840
+ const isCompositeItem = compositeProp ?? compositeRootContext !== undefined;
841
+ const {
842
+ props: focusableWhenDisabledProps
843
+ } = useFocusableWhenDisabled({
844
+ focusableWhenDisabled,
845
+ disabled,
846
+ composite: isCompositeItem,
847
+ tabIndex,
848
+ isNativeButton
849
+ });
850
+ if (process.env.NODE_ENV !== 'production') {
851
+ // eslint-disable-next-line react-hooks/rules-of-hooks
852
+ React__namespace.useEffect(() => {
853
+ if (!elementRef.current) {
854
+ return;
855
+ }
856
+ const isButtonTag = isButtonElement(elementRef.current);
857
+ if (isNativeButton) {
858
+ if (!isButtonTag) {
859
+ const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
860
+ 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`.';
861
+ error(`${message}${ownerStackMessage}`);
862
+ }
863
+ } else if (isButtonTag) {
864
+ const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
865
+ 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`.';
866
+ error(`${message}${ownerStackMessage}`);
867
+ }
868
+ }, [isNativeButton]);
869
+ }
870
+
871
+ // handles a disabled composite button rendering another button, e.g.
872
+ // <Toolbar.Button disabled render={<Menu.Trigger />} />
873
+ // the `disabled` prop needs to pass through 2 `useButton`s then finally
874
+ // delete the `disabled` attribute from DOM
875
+ const updateDisabled = React__namespace.useCallback(() => {
876
+ const element = elementRef.current;
877
+ if (!isButtonElement(element)) {
878
+ return;
879
+ }
880
+ if (isCompositeItem && disabled && focusableWhenDisabledProps.disabled === undefined && element.disabled) {
881
+ element.disabled = false;
882
+ }
883
+ }, [disabled, focusableWhenDisabledProps.disabled, isCompositeItem]);
884
+ useIsoLayoutEffect(updateDisabled, [updateDisabled]);
885
+ const getButtonProps = React__namespace.useCallback((externalProps = {}) => {
886
+ const {
887
+ onClick: externalOnClick,
888
+ onMouseDown: externalOnMouseDown,
889
+ onKeyUp: externalOnKeyUp,
890
+ onKeyDown: externalOnKeyDown,
891
+ onPointerDown: externalOnPointerDown,
892
+ ...otherExternalProps
893
+ } = externalProps;
894
+ const type = isNativeButton ? 'button' : undefined;
895
+ return useRenderElement.mergeProps({
896
+ type,
897
+ onClick(event) {
898
+ if (disabled) {
899
+ event.preventDefault();
900
+ return;
901
+ }
902
+ externalOnClick?.(event);
903
+ },
904
+ onMouseDown(event) {
905
+ if (!disabled) {
906
+ externalOnMouseDown?.(event);
907
+ }
908
+ },
909
+ onKeyDown(event) {
910
+ if (disabled) {
911
+ return;
912
+ }
913
+ useRenderElement.makeEventPreventable(event);
914
+ externalOnKeyDown?.(event);
915
+ if (event.baseUIHandlerPrevented) {
916
+ return;
917
+ }
918
+ const isCurrentTarget = event.target === event.currentTarget;
919
+ const currentTarget = event.currentTarget;
920
+ const isButton = isButtonElement(currentTarget);
921
+ const isLink = !isNativeButton && isValidLinkElement(currentTarget);
922
+ const shouldClick = isCurrentTarget && (isNativeButton ? isButton : !isLink);
923
+ const isEnterKey = event.key === 'Enter';
924
+ const isSpaceKey = event.key === ' ';
925
+ const role = currentTarget.getAttribute('role');
926
+ const isTextNavigationRole = role?.startsWith('menuitem') || role === 'option' || role === 'gridcell';
927
+ if (isCurrentTarget && isCompositeItem && isSpaceKey) {
928
+ if (event.defaultPrevented && isTextNavigationRole) {
929
+ return;
930
+ }
931
+ event.preventDefault();
932
+ if (isLink || isNativeButton && isButton) {
933
+ currentTarget.click();
934
+ event.preventBaseUIHandler();
935
+ } else if (shouldClick) {
936
+ externalOnClick?.(event);
937
+ event.preventBaseUIHandler();
938
+ }
939
+ return;
940
+ }
941
+
942
+ // Keyboard accessibility for native and non-native elements.
943
+ if (shouldClick) {
944
+ if (!isNativeButton && (isSpaceKey || isEnterKey)) {
945
+ event.preventDefault();
946
+ }
947
+ if (!isNativeButton && isEnterKey) {
948
+ externalOnClick?.(event);
949
+ }
950
+ }
951
+ },
952
+ onKeyUp(event) {
953
+ if (disabled) {
954
+ return;
955
+ }
956
+
957
+ // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
958
+ // https://codesandbox.io/p/sandbox/button-keyup-preventdefault-dn7f0
959
+ useRenderElement.makeEventPreventable(event);
960
+ externalOnKeyUp?.(event);
961
+ if (event.target === event.currentTarget && isNativeButton && isCompositeItem && isButtonElement(event.currentTarget) && event.key === ' ') {
962
+ event.preventDefault();
963
+ return;
964
+ }
965
+ if (event.baseUIHandlerPrevented) {
966
+ return;
967
+ }
968
+
969
+ // Keyboard accessibility for non interactive elements
970
+ if (event.target === event.currentTarget && !isNativeButton && !isCompositeItem && event.key === ' ') {
971
+ externalOnClick?.(event);
972
+ }
973
+ },
974
+ onPointerDown(event) {
975
+ if (disabled) {
976
+ event.preventDefault();
977
+ return;
978
+ }
979
+ externalOnPointerDown?.(event);
980
+ }
981
+ }, !isNativeButton ? {
982
+ role: 'button'
983
+ } : undefined, focusableWhenDisabledProps, otherExternalProps);
984
+ }, [disabled, focusableWhenDisabledProps, isCompositeItem, isNativeButton]);
985
+ const buttonRef = useStableCallback(element => {
986
+ elementRef.current = element;
987
+ updateDisabled();
988
+ });
989
+ return {
990
+ getButtonProps,
991
+ buttonRef
992
+ };
993
+ }
994
+ function isButtonElement(elem) {
995
+ return floatingUi_utils_dom.isHTMLElement(elem) && elem.tagName === 'BUTTON';
996
+ }
997
+ function isValidLinkElement(elem) {
998
+ return Boolean(elem?.tagName === 'A' && elem?.href);
999
+ }
1000
+
1001
+ function clamp(val, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
1002
+ return Math.max(min, Math.min(val, max));
1003
+ }
1004
+
758
1005
  exports.AnimationFrame = AnimationFrame;
759
1006
  exports.SafeReact = SafeReact;
760
1007
  exports.Timeout = Timeout;
@@ -768,6 +1015,7 @@ exports.closeWatcher = closeWatcher;
768
1015
  exports.createChangeEventDetails = createChangeEventDetails;
769
1016
  exports.createGenericEventDetails = createGenericEventDetails;
770
1017
  exports.decrementPress = decrementPress;
1018
+ exports.error = error;
771
1019
  exports.escapeKey = escapeKey;
772
1020
  exports.focusOut = focusOut;
773
1021
  exports.imperativeAction = imperativeAction;
@@ -795,6 +1043,7 @@ exports.listNavigation = listNavigation;
795
1043
  exports.none = none;
796
1044
  exports.outsidePress = outsidePress;
797
1045
  exports.ownerDocument = ownerDocument;
1046
+ exports.pointer = pointer;
798
1047
  exports.resolveRef = resolveRef;
799
1048
  exports.scrub = scrub;
800
1049
  exports.siblingOpen = siblingOpen;
@@ -806,6 +1055,9 @@ exports.triggerHover = triggerHover;
806
1055
  exports.triggerPress = triggerPress;
807
1056
  exports.useAnimationFrame = useAnimationFrame;
808
1057
  exports.useAnimationsFinished = useAnimationsFinished;
1058
+ exports.useBaseUiId = useBaseUiId;
1059
+ exports.useButton = useButton;
1060
+ exports.useCompositeRootContext = useCompositeRootContext;
809
1061
  exports.useControlled = useControlled;
810
1062
  exports.useId = useId;
811
1063
  exports.useIsoLayoutEffect = useIsoLayoutEffect;