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