@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.
- package/dist/Autocomplete-es.js +1 -1
- package/dist/BottomSheet-cjs.js +109 -77
- package/dist/BottomSheet-es.js +103 -75
- package/dist/Card/index.cjs +2 -3
- package/dist/Card/index.mjs +2 -3
- package/dist/DataDump/index.cjs +2 -3
- package/dist/DataDump/index.mjs +2 -3
- package/dist/InputNumberExperimental-cjs.js +16 -17
- package/dist/InputNumberExperimental-es.js +2 -3
- package/dist/Menu/index.cjs +2 -3
- package/dist/Menu/index.mjs +2 -3
- package/dist/Menu-cjs.js +11 -56
- package/dist/Menu-es.js +4 -49
- package/dist/MenuSubmenuTrigger-cjs.js +1608 -1793
- package/dist/MenuSubmenuTrigger-es.js +1340 -1522
- package/dist/Modal/index.mjs +1 -1
- package/dist/NumberFieldInput-cjs.js +53 -54
- package/dist/NumberFieldInput-es.js +2 -3
- package/dist/Page/index.cjs +2 -3
- package/dist/Page/index.mjs +2 -3
- package/dist/{DrawerRoot-cjs.js → ScrollAreaViewport-cjs.js} +5488 -4449
- package/dist/{DrawerRoot-es.js → ScrollAreaViewport-es.js} +5423 -4393
- package/dist/{useValueChanged-cjs.js → clamp-cjs.js} +573 -321
- package/dist/{useValueChanged-es.js → clamp-es.js} +569 -322
- package/dist/floating-ui.react-es.js +1 -1
- package/dist/index.cjs +2 -3
- package/dist/index.mjs +2 -3
- package/dist/primitives/BottomSheet/BottomSheet.d.ts +15 -16
- package/dist/primitives/BottomSheet/BottomSheet.types.d.ts +27 -0
- package/dist/primitives/BottomSheet/BottomSheet.utils.d.ts +6 -0
- package/dist/primitives/BottomSheet/index.cjs +8 -3
- package/dist/primitives/BottomSheet/index.d.ts +1 -0
- package/dist/primitives/BottomSheet/index.mjs +8 -3
- package/dist/primitives/InputNumberExperimental/index.cjs +2 -3
- package/dist/primitives/InputNumberExperimental/index.mjs +2 -3
- package/dist/primitives/index.cjs +8 -9
- package/dist/primitives/index.mjs +8 -9
- package/dist/styles.css +62 -1
- package/dist/unstyledPrimitives/index.cjs +6373 -5868
- package/dist/unstyledPrimitives/index.d.ts +2 -0
- package/dist/unstyledPrimitives/index.mjs +6325 -5822
- package/dist/useRenderElement-cjs.js +30 -30
- package/dist/useRenderElement-es.js +31 -31
- package/package.json +2 -2
- package/dist/useBaseUiId-cjs.js +0 -275
- package/dist/useBaseUiId-es.js +0 -251
|
@@ -1,35 +1,7 @@
|
|
|
1
|
-
import { b as useRefWithInit, E as EMPTY_OBJECT } from './useRenderElement-es.js';
|
|
2
1
|
import * as React from 'react';
|
|
2
|
+
import { a as useRefWithInit, E as EMPTY_OBJECT, f as formatErrorMessage, m as mergeProps, c as makeEventPreventable } from './useRenderElement-es.js';
|
|
3
3
|
import * as ReactDOM from 'react-dom';
|
|
4
|
-
|
|
5
|
-
let TransitionStatusDataAttributes = /*#__PURE__*/function (TransitionStatusDataAttributes) {
|
|
6
|
-
/**
|
|
7
|
-
* Present when the component is animating in.
|
|
8
|
-
*/
|
|
9
|
-
TransitionStatusDataAttributes["startingStyle"] = "data-starting-style";
|
|
10
|
-
/**
|
|
11
|
-
* Present when the component is animating out.
|
|
12
|
-
*/
|
|
13
|
-
TransitionStatusDataAttributes["endingStyle"] = "data-ending-style";
|
|
14
|
-
return TransitionStatusDataAttributes;
|
|
15
|
-
}({});
|
|
16
|
-
const STARTING_HOOK = {
|
|
17
|
-
[TransitionStatusDataAttributes.startingStyle]: ''
|
|
18
|
-
};
|
|
19
|
-
const ENDING_HOOK = {
|
|
20
|
-
[TransitionStatusDataAttributes.endingStyle]: ''
|
|
21
|
-
};
|
|
22
|
-
const transitionStatusMapping = {
|
|
23
|
-
transitionStatus(value) {
|
|
24
|
-
if (value === 'starting') {
|
|
25
|
-
return STARTING_HOOK;
|
|
26
|
-
}
|
|
27
|
-
if (value === 'ending') {
|
|
28
|
-
return ENDING_HOOK;
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
4
|
+
import { a as isHTMLElement } from './floating-ui.utils.dom-es.js';
|
|
33
5
|
|
|
34
6
|
// https://github.com/mui/material-ui/issues/41190#issuecomment-2040873379
|
|
35
7
|
const useInsertionEffect = React[`useInsertionEffect${Math.random().toFixed(1)}`.slice(0, -3)];
|
|
@@ -71,147 +43,187 @@ function assertNotCalled() {
|
|
|
71
43
|
}
|
|
72
44
|
}
|
|
73
45
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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;
|
|
58
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
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)]);
|
|
73
|
+
}
|
|
74
|
+
const setValueIfUncontrolled = React.useCallback(newValue => {
|
|
75
|
+
if (!isControlled) {
|
|
76
|
+
setValue(newValue);
|
|
77
|
+
}
|
|
78
|
+
}, []);
|
|
79
|
+
return [value, setValueIfUncontrolled];
|
|
80
|
+
}
|
|
78
81
|
|
|
79
82
|
const noop = () => {};
|
|
80
83
|
const useIsoLayoutEffect = typeof document !== 'undefined' ? React.useLayoutEffect : noop;
|
|
81
84
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const keyboard = 'keyboard';
|
|
103
|
-
const scrub = 'scrub';
|
|
104
|
-
const cancelOpen = 'cancel-open';
|
|
105
|
-
const siblingOpen = 'sibling-open';
|
|
106
|
-
const imperativeAction = 'imperative-action';
|
|
107
|
-
const swipe = 'swipe';
|
|
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
|
+
};
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
* Maps a change `reason` string to the corresponding native event type.
|
|
111
|
-
*/
|
|
106
|
+
const EMPTY$2 = [];
|
|
112
107
|
|
|
113
108
|
/**
|
|
114
|
-
*
|
|
109
|
+
* A React.useEffect equivalent that runs once, when the component is mounted.
|
|
115
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 */
|
|
116
|
+
}
|
|
116
117
|
|
|
117
|
-
/**
|
|
118
|
-
*
|
|
119
|
-
|
|
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`. */
|
|
120
132
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
reason,
|
|
131
|
-
event: event ?? new Event('base-ui'),
|
|
132
|
-
cancel() {
|
|
133
|
-
canceled = true;
|
|
134
|
-
},
|
|
135
|
-
allowPropagation() {
|
|
136
|
-
allowPropagation = true;
|
|
137
|
-
},
|
|
138
|
-
get isCanceled() {
|
|
139
|
-
return canceled;
|
|
140
|
-
},
|
|
141
|
-
get isPropagationAllowed() {
|
|
142
|
-
return allowPropagation;
|
|
143
|
-
},
|
|
144
|
-
trigger,
|
|
145
|
-
...custom
|
|
146
|
-
};
|
|
147
|
-
return details;
|
|
148
|
-
}
|
|
149
|
-
function createGenericEventDetails(reason, event, customProperties) {
|
|
150
|
-
const custom = customProperties ?? EMPTY_OBJECT;
|
|
151
|
-
const details = {
|
|
152
|
-
reason,
|
|
153
|
-
event: event ?? new Event('base-ui'),
|
|
154
|
-
...custom
|
|
155
|
-
};
|
|
156
|
-
return details;
|
|
157
|
-
}
|
|
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;
|
|
158
142
|
|
|
159
|
-
|
|
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);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
request(fn) {
|
|
154
|
+
const id = this.nextId;
|
|
155
|
+
this.nextId += 1;
|
|
156
|
+
this.callbacks.push(fn);
|
|
157
|
+
this.callbacksCount += 1;
|
|
160
158
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// Use the incrementing value for client-side rendering only.
|
|
169
|
-
// We can't use it server-side.
|
|
170
|
-
// If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
|
|
171
|
-
globalId += 1;
|
|
172
|
-
setDefaultId(`${prefix}-${globalId}`);
|
|
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;
|
|
173
166
|
}
|
|
174
|
-
|
|
175
|
-
|
|
167
|
+
return id;
|
|
168
|
+
}
|
|
169
|
+
cancel(id) {
|
|
170
|
+
const index = id - this.startId;
|
|
171
|
+
if (index < 0 || index >= this.callbacks.length) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.callbacks[index] = null;
|
|
175
|
+
this.callbacksCount -= 1;
|
|
176
|
+
}
|
|
176
177
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* @example <div id={useId()} />
|
|
182
|
-
* @param idOverride
|
|
183
|
-
* @returns {string}
|
|
184
|
-
*/
|
|
185
|
-
function useId(idOverride, prefix) {
|
|
186
|
-
// React.useId() is only available from React 17.0.0.
|
|
187
|
-
if (maybeReactUseId !== undefined) {
|
|
188
|
-
const reactId = maybeReactUseId();
|
|
189
|
-
return idOverride ?? (prefix ? `${prefix}-${reactId}` : reactId);
|
|
178
|
+
const scheduler = new Scheduler();
|
|
179
|
+
class AnimationFrame {
|
|
180
|
+
static create() {
|
|
181
|
+
return new AnimationFrame();
|
|
190
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;
|
|
191
190
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
+
}
|
|
206
|
+
};
|
|
207
|
+
disposeEffect = () => {
|
|
208
|
+
return this.cancel;
|
|
209
|
+
};
|
|
195
210
|
}
|
|
196
211
|
|
|
197
|
-
const EMPTY$2 = [];
|
|
198
|
-
|
|
199
212
|
/**
|
|
200
|
-
* A
|
|
213
|
+
* A `requestAnimationFrame` with automatic cleanup and guard.
|
|
201
214
|
*/
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
/* eslint-enable react-hooks/exhaustive-deps */
|
|
215
|
+
function useAnimationFrame() {
|
|
216
|
+
const timeout = useRefWithInit(AnimationFrame.create).current;
|
|
217
|
+
useOnMount(timeout.disposeEffect);
|
|
218
|
+
return timeout;
|
|
207
219
|
}
|
|
208
220
|
|
|
209
|
-
const EMPTY
|
|
221
|
+
const EMPTY = 0;
|
|
210
222
|
class Timeout {
|
|
211
223
|
static create() {
|
|
212
224
|
return new Timeout();
|
|
213
225
|
}
|
|
214
|
-
currentId = EMPTY
|
|
226
|
+
currentId = EMPTY;
|
|
215
227
|
|
|
216
228
|
/**
|
|
217
229
|
* Executes `fn` after `delay`, clearing any previously scheduled call.
|
|
@@ -219,17 +231,17 @@ class Timeout {
|
|
|
219
231
|
start(delay, fn) {
|
|
220
232
|
this.clear();
|
|
221
233
|
this.currentId = setTimeout(() => {
|
|
222
|
-
this.currentId = EMPTY
|
|
234
|
+
this.currentId = EMPTY;
|
|
223
235
|
fn();
|
|
224
236
|
}, delay); /* Node.js types are enabled in development */
|
|
225
237
|
}
|
|
226
238
|
isStarted() {
|
|
227
|
-
return this.currentId !== EMPTY
|
|
239
|
+
return this.currentId !== EMPTY;
|
|
228
240
|
}
|
|
229
241
|
clear = () => {
|
|
230
|
-
if (this.currentId !== EMPTY
|
|
242
|
+
if (this.currentId !== EMPTY) {
|
|
231
243
|
clearTimeout(this.currentId);
|
|
232
|
-
this.currentId = EMPTY
|
|
244
|
+
this.currentId = EMPTY;
|
|
233
245
|
}
|
|
234
246
|
};
|
|
235
247
|
disposeEffect = () => {
|
|
@@ -246,6 +258,30 @@ function useTimeout() {
|
|
|
246
258
|
return timeout;
|
|
247
259
|
}
|
|
248
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
|
+
|
|
249
285
|
const hasNavigator = typeof navigator !== 'undefined';
|
|
250
286
|
const nav = getNavigatorData();
|
|
251
287
|
const platform = getPlatform();
|
|
@@ -348,153 +384,126 @@ function ownerDocument(node) {
|
|
|
348
384
|
return node?.ownerDocument || document;
|
|
349
385
|
}
|
|
350
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
|
+
|
|
351
415
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* 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.
|
|
355
417
|
*/
|
|
356
|
-
function useValueAsRef(value) {
|
|
357
|
-
const latest = useRefWithInit(createLatestRef, value).current;
|
|
358
|
-
latest.next = value;
|
|
359
418
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
function createLatestRef(value) {
|
|
365
|
-
const latest = {
|
|
366
|
-
current: value,
|
|
367
|
-
next: value,
|
|
368
|
-
effect: () => {
|
|
369
|
-
latest.current = latest.next;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
return latest;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Unlike `setTimeout`, rAF doesn't guarantee a positive integer return value, so we can't have
|
|
376
|
-
* a monomorphic `uint` type with `0` meaning empty.
|
|
377
|
-
* See warning note at:
|
|
378
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame#return_value */
|
|
379
|
-
const EMPTY = null;
|
|
380
|
-
let LAST_RAF = globalThis.requestAnimationFrame;
|
|
381
|
-
class Scheduler {
|
|
382
|
-
/* This implementation uses an array as a backing data-structure for frame callbacks.
|
|
383
|
-
* It allows `O(1)` callback cancelling by inserting a `null` in the array, though it
|
|
384
|
-
* never calls the native `cancelAnimationFrame` if there are no frames left. This can
|
|
385
|
-
* be much more efficient if there is a call pattern that alterns as
|
|
386
|
-
* "request-cancel-request-cancel-…".
|
|
387
|
-
* But in the case of "request-request-…-cancel-cancel-…", it leaves the final animation
|
|
388
|
-
* frame to run anyway. We turn that frame into a `O(1)` no-op via `callbacksCount`. */
|
|
389
|
-
|
|
390
|
-
callbacks = [];
|
|
391
|
-
callbacksCount = 0;
|
|
392
|
-
nextId = 1;
|
|
393
|
-
startId = 1;
|
|
394
|
-
isScheduled = false;
|
|
395
|
-
tick = timestamp => {
|
|
396
|
-
this.isScheduled = false;
|
|
397
|
-
const currentCallbacks = this.callbacks;
|
|
398
|
-
const currentCallbacksCount = this.callbacksCount;
|
|
419
|
+
/**
|
|
420
|
+
* Details of custom change events emitted by Base UI components.
|
|
421
|
+
*/
|
|
399
422
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.startId = this.nextId;
|
|
404
|
-
if (currentCallbacksCount > 0) {
|
|
405
|
-
for (let i = 0; i < currentCallbacks.length; i += 1) {
|
|
406
|
-
currentCallbacks[i]?.(timestamp);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
request(fn) {
|
|
411
|
-
const id = this.nextId;
|
|
412
|
-
this.nextId += 1;
|
|
413
|
-
this.callbacks.push(fn);
|
|
414
|
-
this.callbacksCount += 1;
|
|
423
|
+
/**
|
|
424
|
+
* Details of custom generic events emitted by Base UI components.
|
|
425
|
+
*/
|
|
415
426
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
|
452
|
+
};
|
|
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;
|
|
434
463
|
}
|
|
435
|
-
const scheduler = new Scheduler();
|
|
436
|
-
class AnimationFrame {
|
|
437
|
-
static create() {
|
|
438
|
-
return new AnimationFrame();
|
|
439
|
-
}
|
|
440
|
-
static request(fn) {
|
|
441
|
-
return scheduler.request(fn);
|
|
442
|
-
}
|
|
443
|
-
static cancel(id) {
|
|
444
|
-
return scheduler.cancel(id);
|
|
445
|
-
}
|
|
446
|
-
currentId = EMPTY;
|
|
447
464
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
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}`);
|
|
462
484
|
}
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
return this.cancel;
|
|
466
|
-
};
|
|
485
|
+
}, [defaultId, prefix]);
|
|
486
|
+
return id;
|
|
467
487
|
}
|
|
488
|
+
const maybeReactUseId = SafeReact.useId;
|
|
468
489
|
|
|
469
490
|
/**
|
|
470
|
-
*
|
|
491
|
+
*
|
|
492
|
+
* @example <div id={useId()} />
|
|
493
|
+
* @param idOverride
|
|
494
|
+
* @returns {string}
|
|
471
495
|
*/
|
|
472
|
-
function
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
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
|
+
}
|
|
477
502
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
border: 0,
|
|
483
|
-
padding: 0,
|
|
484
|
-
width: 1,
|
|
485
|
-
height: 1,
|
|
486
|
-
margin: -1
|
|
487
|
-
};
|
|
488
|
-
const visuallyHidden = {
|
|
489
|
-
...visuallyHiddenBase,
|
|
490
|
-
position: 'fixed',
|
|
491
|
-
top: 0,
|
|
492
|
-
left: 0
|
|
493
|
-
};
|
|
494
|
-
const visuallyHiddenInput = {
|
|
495
|
-
...visuallyHiddenBase,
|
|
496
|
-
position: 'absolute'
|
|
497
|
-
};
|
|
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
|
+
}
|
|
498
507
|
|
|
499
508
|
/**
|
|
500
509
|
* If the provided argument is a ref object, returns its `current` value.
|
|
@@ -570,6 +579,35 @@ function useTransitionStatus(open, enableIdleState = false, deferEndingState = f
|
|
|
570
579
|
}), [mounted, transitionStatus]);
|
|
571
580
|
}
|
|
572
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
|
+
|
|
573
611
|
/**
|
|
574
612
|
* Executes a function once all animations have finished on the provided element.
|
|
575
613
|
* @param elementOrRef - The element to watch for animations.
|
|
@@ -679,44 +717,13 @@ function useOpenChangeComplete(parameters) {
|
|
|
679
717
|
}, [enabled, open, onComplete, runOnceAnimationsFinish]);
|
|
680
718
|
}
|
|
681
719
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
name,
|
|
690
|
-
state = 'value'
|
|
691
|
-
}) {
|
|
692
|
-
// isControlled is ignored in the hook dependency lists as it should never change.
|
|
693
|
-
const {
|
|
694
|
-
current: isControlled
|
|
695
|
-
} = React.useRef(controlled !== undefined);
|
|
696
|
-
const [valueState, setValue] = React.useState(defaultProp);
|
|
697
|
-
const value = isControlled ? controlled : valueState;
|
|
698
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
699
|
-
React.useEffect(() => {
|
|
700
|
-
if (isControlled !== (controlled !== undefined)) {
|
|
701
|
-
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'));
|
|
702
|
-
}
|
|
703
|
-
}, [state, name, controlled]);
|
|
704
|
-
const {
|
|
705
|
-
current: defaultValue
|
|
706
|
-
} = React.useRef(defaultProp);
|
|
707
|
-
React.useEffect(() => {
|
|
708
|
-
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is for more details.
|
|
709
|
-
if (!isControlled && JSON.stringify(defaultValue) !== JSON.stringify(defaultProp)) {
|
|
710
|
-
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'));
|
|
711
|
-
}
|
|
712
|
-
}, [JSON.stringify(defaultProp)]);
|
|
713
|
-
}
|
|
714
|
-
const setValueIfUncontrolled = React.useCallback(newValue => {
|
|
715
|
-
if (!isControlled) {
|
|
716
|
-
setValue(newValue);
|
|
717
|
-
}
|
|
718
|
-
}, []);
|
|
719
|
-
return [value, setValueIfUncontrolled];
|
|
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');
|
|
720
727
|
}
|
|
721
728
|
|
|
722
729
|
function useValueChanged(value, onChange) {
|
|
@@ -733,4 +740,244 @@ function useValueChanged(value, onChange) {
|
|
|
733
740
|
}, [value]);
|
|
734
741
|
}
|
|
735
742
|
|
|
736
|
-
|
|
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;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
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
|
+
};
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function useButton(parameters = {}) {
|
|
809
|
+
const {
|
|
810
|
+
disabled = false,
|
|
811
|
+
focusableWhenDisabled,
|
|
812
|
+
tabIndex = 0,
|
|
813
|
+
native: isNativeButton = true,
|
|
814
|
+
composite: compositeProp
|
|
815
|
+
} = parameters;
|
|
816
|
+
const elementRef = React.useRef(null);
|
|
817
|
+
const compositeRootContext = useCompositeRootContext(true);
|
|
818
|
+
const isCompositeItem = compositeProp ?? compositeRootContext !== undefined;
|
|
819
|
+
const {
|
|
820
|
+
props: focusableWhenDisabledProps
|
|
821
|
+
} = useFocusableWhenDisabled({
|
|
822
|
+
focusableWhenDisabled,
|
|
823
|
+
disabled,
|
|
824
|
+
composite: isCompositeItem,
|
|
825
|
+
tabIndex,
|
|
826
|
+
isNativeButton
|
|
827
|
+
});
|
|
828
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
829
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
830
|
+
React.useEffect(() => {
|
|
831
|
+
if (!elementRef.current) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
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}`);
|
|
845
|
+
}
|
|
846
|
+
}, [isNativeButton]);
|
|
847
|
+
}
|
|
848
|
+
|
|
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)) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
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));
|
|
981
|
+
}
|
|
982
|
+
|
|
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 };
|