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