@jobber/components 7.10.0 → 7.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/Autocomplete/index.cjs +1 -0
  2. package/dist/Autocomplete/index.mjs +1 -0
  3. package/dist/Card/index.cjs +3 -0
  4. package/dist/Card/index.mjs +3 -0
  5. package/dist/Chip/index.cjs +1 -0
  6. package/dist/Chip/index.mjs +1 -0
  7. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +1 -0
  8. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +1 -0
  9. package/dist/Chips/InternalChipDismissible/index.cjs +1 -0
  10. package/dist/Chips/InternalChipDismissible/index.mjs +1 -0
  11. package/dist/Chips/index.cjs +1 -0
  12. package/dist/Chips/index.mjs +1 -0
  13. package/dist/Combobox/components/ComboboxActivator/index.cjs +1 -0
  14. package/dist/Combobox/components/ComboboxActivator/index.mjs +1 -0
  15. package/dist/Combobox/components/ComboboxContent/index.cjs +1 -0
  16. package/dist/Combobox/components/ComboboxContent/index.mjs +1 -0
  17. package/dist/Combobox/components/ComboboxTrigger/index.cjs +1 -0
  18. package/dist/Combobox/components/ComboboxTrigger/index.mjs +1 -0
  19. package/dist/Combobox/index.cjs +1 -0
  20. package/dist/Combobox/index.mjs +1 -0
  21. package/dist/ConfirmationModal/index.cjs +1 -0
  22. package/dist/ConfirmationModal/index.mjs +1 -0
  23. package/dist/DataDump/index.cjs +3 -0
  24. package/dist/DataDump/index.mjs +3 -0
  25. package/dist/DataList/components/DataListActions/index.cjs +1 -0
  26. package/dist/DataList/components/DataListActions/index.mjs +1 -0
  27. package/dist/DataList/components/DataListBulkActions/index.cjs +1 -0
  28. package/dist/DataList/components/DataListBulkActions/index.mjs +1 -0
  29. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +1 -0
  30. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +1 -0
  31. package/dist/DataList/components/DataListFilters/index.cjs +1 -0
  32. package/dist/DataList/components/DataListFilters/index.mjs +1 -0
  33. package/dist/DataList/components/DataListHeader/index.cjs +1 -0
  34. package/dist/DataList/components/DataListHeader/index.mjs +1 -0
  35. package/dist/DataList/components/DataListItem/index.cjs +1 -0
  36. package/dist/DataList/components/DataListItem/index.mjs +1 -0
  37. package/dist/DataList/components/DataListItemActions/index.cjs +1 -0
  38. package/dist/DataList/components/DataListItemActions/index.mjs +1 -0
  39. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +1 -0
  40. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +1 -0
  41. package/dist/DataList/components/DataListItems/index.cjs +1 -0
  42. package/dist/DataList/components/DataListItems/index.mjs +1 -0
  43. package/dist/DataList/components/DataListLayout/index.cjs +1 -0
  44. package/dist/DataList/components/DataListLayout/index.mjs +1 -0
  45. package/dist/DataList/components/DataListLayoutActions/index.cjs +1 -0
  46. package/dist/DataList/components/DataListLayoutActions/index.mjs +1 -0
  47. package/dist/DataList/index.cjs +1 -0
  48. package/dist/DataList/index.mjs +1 -0
  49. package/dist/DatePicker/index.cjs +1 -0
  50. package/dist/DatePicker/index.mjs +1 -0
  51. package/dist/DrawerRoot-cjs.js +181 -968
  52. package/dist/DrawerRoot-es.js +5 -734
  53. package/dist/FormatFile/index.cjs +1 -0
  54. package/dist/FormatFile/index.mjs +1 -0
  55. package/dist/Gallery/index.cjs +1 -0
  56. package/dist/Gallery/index.mjs +1 -0
  57. package/dist/InputDate/index.cjs +1 -0
  58. package/dist/InputDate/index.mjs +1 -0
  59. package/dist/InputNumberExperimental-cjs.js +783 -0
  60. package/dist/InputNumberExperimental-es.js +763 -0
  61. package/dist/LightBox/index.cjs +1 -0
  62. package/dist/LightBox/index.mjs +1 -0
  63. package/dist/Menu/index.cjs +3 -0
  64. package/dist/Menu/index.mjs +3 -0
  65. package/dist/MenuSubmenuTrigger-cjs.js +202 -447
  66. package/dist/MenuSubmenuTrigger-es.js +7 -249
  67. package/dist/Modal/index.cjs +1 -0
  68. package/dist/Modal/index.mjs +1 -0
  69. package/dist/NumberFieldInput-cjs.js +1828 -0
  70. package/dist/NumberFieldInput-es.js +1788 -0
  71. package/dist/Page/index.cjs +3 -0
  72. package/dist/Page/index.mjs +3 -0
  73. package/dist/Popover/index.cjs +1 -0
  74. package/dist/Popover/index.mjs +1 -0
  75. package/dist/Tooltip/index.cjs +1 -0
  76. package/dist/Tooltip/index.mjs +1 -0
  77. package/dist/docs/Menu/Menu.md +197 -37
  78. package/dist/floating-ui.react-cjs.js +35 -34
  79. package/dist/floating-ui.react-dom-cjs.js +65 -64
  80. package/dist/floating-ui.react-dom-es.js +2 -1
  81. package/dist/floating-ui.react-es.js +2 -1
  82. package/dist/floating-ui.utils.dom-cjs.js +185 -0
  83. package/dist/floating-ui.utils.dom-es.js +165 -0
  84. package/dist/index.cjs +3 -0
  85. package/dist/index.esm-cjs.js +0 -183
  86. package/dist/index.esm-es.js +1 -165
  87. package/dist/index.mjs +3 -0
  88. package/dist/primitives/BottomSheet/index.cjs +3 -1
  89. package/dist/primitives/BottomSheet/index.mjs +3 -1
  90. package/dist/primitives/InputNumberExperimental/InputNumberExperimental.d.ts +20 -0
  91. package/dist/primitives/InputNumberExperimental/index.cjs +22 -0
  92. package/dist/primitives/InputNumberExperimental/index.d.ts +2 -0
  93. package/dist/primitives/InputNumberExperimental/index.mjs +16 -0
  94. package/dist/primitives/InputNumberExperimental/types.d.ts +147 -0
  95. package/dist/primitives/index.cjs +9 -1
  96. package/dist/primitives/index.d.ts +2 -0
  97. package/dist/primitives/index.mjs +8 -1
  98. package/dist/styles.css +499 -0
  99. package/dist/unstyledPrimitives/index.cjs +264 -2039
  100. package/dist/unstyledPrimitives/index.mjs +72 -1847
  101. package/dist/useBaseUiId-cjs.js +275 -0
  102. package/dist/useBaseUiId-es.js +251 -0
  103. package/dist/useValueChanged-cjs.js +820 -0
  104. package/dist/useValueChanged-es.js +736 -0
  105. package/package.json +2 -2
  106. package/rollup.config.mjs +13 -2
@@ -0,0 +1,275 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
5
+ var useValueChanged = require('./useValueChanged-cjs.js');
6
+ var useRenderElement = require('./useRenderElement-cjs.js');
7
+
8
+ function _interopNamespaceDefault(e) {
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
26
+
27
+ let set;
28
+ if (process.env.NODE_ENV !== 'production') {
29
+ set = new Set();
30
+ }
31
+ function error(...messages) {
32
+ if (process.env.NODE_ENV !== 'production') {
33
+ const messageKey = messages.join(' ');
34
+ if (!set.has(messageKey)) {
35
+ set.add(messageKey);
36
+ console.error(`Base UI: ${messageKey}`);
37
+ }
38
+ }
39
+ }
40
+
41
+ const CompositeRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
42
+ if (process.env.NODE_ENV !== "production") CompositeRootContext.displayName = "CompositeRootContext";
43
+ function useCompositeRootContext(optional = false) {
44
+ const context = React__namespace.useContext(CompositeRootContext);
45
+ if (context === undefined && !optional) {
46
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: CompositeRootContext is missing. Composite parts must be placed within <Composite.Root>.' : useRenderElement.formatErrorMessage(16));
47
+ }
48
+ return context;
49
+ }
50
+
51
+ function useFocusableWhenDisabled(parameters) {
52
+ const {
53
+ focusableWhenDisabled,
54
+ disabled,
55
+ composite = false,
56
+ tabIndex: tabIndexProp = 0,
57
+ isNativeButton
58
+ } = parameters;
59
+ const isFocusableComposite = composite && focusableWhenDisabled !== false;
60
+ const isNonFocusableComposite = composite && focusableWhenDisabled === false;
61
+
62
+ // we can't explicitly assign `undefined` to any of these props because it
63
+ // would otherwise prevent subsequently merged props from setting them
64
+ const props = React__namespace.useMemo(() => {
65
+ const additionalProps = {
66
+ // allow Tabbing away from focusableWhenDisabled elements
67
+ onKeyDown(event) {
68
+ if (disabled && focusableWhenDisabled && event.key !== 'Tab') {
69
+ event.preventDefault();
70
+ }
71
+ }
72
+ };
73
+ if (!composite) {
74
+ additionalProps.tabIndex = tabIndexProp;
75
+ if (!isNativeButton && disabled) {
76
+ additionalProps.tabIndex = focusableWhenDisabled ? tabIndexProp : -1;
77
+ }
78
+ }
79
+ if (isNativeButton && (focusableWhenDisabled || isFocusableComposite) || !isNativeButton && disabled) {
80
+ additionalProps['aria-disabled'] = disabled;
81
+ }
82
+ if (isNativeButton && (!focusableWhenDisabled || isNonFocusableComposite)) {
83
+ additionalProps.disabled = disabled;
84
+ }
85
+ return additionalProps;
86
+ }, [composite, disabled, focusableWhenDisabled, isFocusableComposite, isNonFocusableComposite, isNativeButton, tabIndexProp]);
87
+ return {
88
+ props
89
+ };
90
+ }
91
+
92
+ function useButton(parameters = {}) {
93
+ const {
94
+ disabled = false,
95
+ focusableWhenDisabled,
96
+ tabIndex = 0,
97
+ native: isNativeButton = true,
98
+ composite: compositeProp
99
+ } = parameters;
100
+ const elementRef = React__namespace.useRef(null);
101
+ const compositeRootContext = useCompositeRootContext(true);
102
+ const isCompositeItem = compositeProp ?? compositeRootContext !== undefined;
103
+ const {
104
+ props: focusableWhenDisabledProps
105
+ } = useFocusableWhenDisabled({
106
+ focusableWhenDisabled,
107
+ disabled,
108
+ composite: isCompositeItem,
109
+ tabIndex,
110
+ isNativeButton
111
+ });
112
+ if (process.env.NODE_ENV !== 'production') {
113
+ // eslint-disable-next-line react-hooks/rules-of-hooks
114
+ React__namespace.useEffect(() => {
115
+ if (!elementRef.current) {
116
+ return;
117
+ }
118
+ const isButtonTag = isButtonElement(elementRef.current);
119
+ if (isNativeButton) {
120
+ if (!isButtonTag) {
121
+ const ownerStackMessage = useValueChanged.SafeReact.captureOwnerStack?.() || '';
122
+ 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`.';
123
+ error(`${message}${ownerStackMessage}`);
124
+ }
125
+ } else if (isButtonTag) {
126
+ const ownerStackMessage = useValueChanged.SafeReact.captureOwnerStack?.() || '';
127
+ 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`.';
128
+ error(`${message}${ownerStackMessage}`);
129
+ }
130
+ }, [isNativeButton]);
131
+ }
132
+
133
+ // handles a disabled composite button rendering another button, e.g.
134
+ // <Toolbar.Button disabled render={<Menu.Trigger />} />
135
+ // the `disabled` prop needs to pass through 2 `useButton`s then finally
136
+ // delete the `disabled` attribute from DOM
137
+ const updateDisabled = React__namespace.useCallback(() => {
138
+ const element = elementRef.current;
139
+ if (!isButtonElement(element)) {
140
+ return;
141
+ }
142
+ if (isCompositeItem && disabled && focusableWhenDisabledProps.disabled === undefined && element.disabled) {
143
+ element.disabled = false;
144
+ }
145
+ }, [disabled, focusableWhenDisabledProps.disabled, isCompositeItem]);
146
+ useValueChanged.useIsoLayoutEffect(updateDisabled, [updateDisabled]);
147
+ const getButtonProps = React__namespace.useCallback((externalProps = {}) => {
148
+ const {
149
+ onClick: externalOnClick,
150
+ onMouseDown: externalOnMouseDown,
151
+ onKeyUp: externalOnKeyUp,
152
+ onKeyDown: externalOnKeyDown,
153
+ onPointerDown: externalOnPointerDown,
154
+ ...otherExternalProps
155
+ } = externalProps;
156
+ const type = isNativeButton ? 'button' : undefined;
157
+ return useRenderElement.mergeProps({
158
+ type,
159
+ onClick(event) {
160
+ if (disabled) {
161
+ event.preventDefault();
162
+ return;
163
+ }
164
+ externalOnClick?.(event);
165
+ },
166
+ onMouseDown(event) {
167
+ if (!disabled) {
168
+ externalOnMouseDown?.(event);
169
+ }
170
+ },
171
+ onKeyDown(event) {
172
+ if (disabled) {
173
+ return;
174
+ }
175
+ useRenderElement.makeEventPreventable(event);
176
+ externalOnKeyDown?.(event);
177
+ if (event.baseUIHandlerPrevented) {
178
+ return;
179
+ }
180
+ const isCurrentTarget = event.target === event.currentTarget;
181
+ const currentTarget = event.currentTarget;
182
+ const isButton = isButtonElement(currentTarget);
183
+ const isLink = !isNativeButton && isValidLinkElement(currentTarget);
184
+ const shouldClick = isCurrentTarget && (isNativeButton ? isButton : !isLink);
185
+ const isEnterKey = event.key === 'Enter';
186
+ const isSpaceKey = event.key === ' ';
187
+ const role = currentTarget.getAttribute('role');
188
+ const isTextNavigationRole = role?.startsWith('menuitem') || role === 'option' || role === 'gridcell';
189
+ if (isCurrentTarget && isCompositeItem && isSpaceKey) {
190
+ if (event.defaultPrevented && isTextNavigationRole) {
191
+ return;
192
+ }
193
+ event.preventDefault();
194
+ if (isLink || isNativeButton && isButton) {
195
+ currentTarget.click();
196
+ event.preventBaseUIHandler();
197
+ } else if (shouldClick) {
198
+ externalOnClick?.(event);
199
+ event.preventBaseUIHandler();
200
+ }
201
+ return;
202
+ }
203
+
204
+ // Keyboard accessibility for native and non-native elements.
205
+ if (shouldClick) {
206
+ if (!isNativeButton && (isSpaceKey || isEnterKey)) {
207
+ event.preventDefault();
208
+ }
209
+ if (!isNativeButton && isEnterKey) {
210
+ externalOnClick?.(event);
211
+ }
212
+ }
213
+ },
214
+ onKeyUp(event) {
215
+ if (disabled) {
216
+ return;
217
+ }
218
+
219
+ // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
220
+ // https://codesandbox.io/p/sandbox/button-keyup-preventdefault-dn7f0
221
+ useRenderElement.makeEventPreventable(event);
222
+ externalOnKeyUp?.(event);
223
+ if (event.target === event.currentTarget && isNativeButton && isCompositeItem && isButtonElement(event.currentTarget) && event.key === ' ') {
224
+ event.preventDefault();
225
+ return;
226
+ }
227
+ if (event.baseUIHandlerPrevented) {
228
+ return;
229
+ }
230
+
231
+ // Keyboard accessibility for non interactive elements
232
+ if (event.target === event.currentTarget && !isNativeButton && !isCompositeItem && event.key === ' ') {
233
+ externalOnClick?.(event);
234
+ }
235
+ },
236
+ onPointerDown(event) {
237
+ if (disabled) {
238
+ event.preventDefault();
239
+ return;
240
+ }
241
+ externalOnPointerDown?.(event);
242
+ }
243
+ }, !isNativeButton ? {
244
+ role: 'button'
245
+ } : undefined, focusableWhenDisabledProps, otherExternalProps);
246
+ }, [disabled, focusableWhenDisabledProps, isCompositeItem, isNativeButton]);
247
+ const buttonRef = useValueChanged.useStableCallback(element => {
248
+ elementRef.current = element;
249
+ updateDisabled();
250
+ });
251
+ return {
252
+ getButtonProps,
253
+ buttonRef
254
+ };
255
+ }
256
+ function isButtonElement(elem) {
257
+ return floatingUi_utils_dom.isHTMLElement(elem) && elem.tagName === 'BUTTON';
258
+ }
259
+ function isValidLinkElement(elem) {
260
+ return Boolean(elem?.tagName === 'A' && elem?.href);
261
+ }
262
+
263
+ /**
264
+ * Wraps `useId` and prefixes generated `id`s with `base-ui-`
265
+ * @param {string | undefined} idOverride overrides the generated id when provided
266
+ * @returns {string | undefined}
267
+ */
268
+ function useBaseUiId(idOverride) {
269
+ return useValueChanged.useId(idOverride, 'base-ui');
270
+ }
271
+
272
+ exports.error = error;
273
+ exports.useBaseUiId = useBaseUiId;
274
+ exports.useButton = useButton;
275
+ exports.useCompositeRootContext = useCompositeRootContext;
@@ -0,0 +1,251 @@
1
+ import * as React from 'react';
2
+ import { a as isHTMLElement } from './floating-ui.utils.dom-es.js';
3
+ import { S as SafeReact, u as useIsoLayoutEffect, a as useStableCallback, b as useId } from './useValueChanged-es.js';
4
+ import { f as formatErrorMessage, m as mergeProps, a as makeEventPreventable } from './useRenderElement-es.js';
5
+
6
+ let set;
7
+ if (process.env.NODE_ENV !== 'production') {
8
+ set = new Set();
9
+ }
10
+ function error(...messages) {
11
+ if (process.env.NODE_ENV !== 'production') {
12
+ const messageKey = messages.join(' ');
13
+ if (!set.has(messageKey)) {
14
+ set.add(messageKey);
15
+ console.error(`Base UI: ${messageKey}`);
16
+ }
17
+ }
18
+ }
19
+
20
+ const CompositeRootContext = /*#__PURE__*/React.createContext(undefined);
21
+ if (process.env.NODE_ENV !== "production") CompositeRootContext.displayName = "CompositeRootContext";
22
+ function useCompositeRootContext(optional = false) {
23
+ const context = React.useContext(CompositeRootContext);
24
+ if (context === undefined && !optional) {
25
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: CompositeRootContext is missing. Composite parts must be placed within <Composite.Root>.' : formatErrorMessage(16));
26
+ }
27
+ return context;
28
+ }
29
+
30
+ function useFocusableWhenDisabled(parameters) {
31
+ const {
32
+ focusableWhenDisabled,
33
+ disabled,
34
+ composite = false,
35
+ tabIndex: tabIndexProp = 0,
36
+ isNativeButton
37
+ } = parameters;
38
+ const isFocusableComposite = composite && focusableWhenDisabled !== false;
39
+ const isNonFocusableComposite = composite && focusableWhenDisabled === false;
40
+
41
+ // we can't explicitly assign `undefined` to any of these props because it
42
+ // would otherwise prevent subsequently merged props from setting them
43
+ const props = React.useMemo(() => {
44
+ const additionalProps = {
45
+ // allow Tabbing away from focusableWhenDisabled elements
46
+ onKeyDown(event) {
47
+ if (disabled && focusableWhenDisabled && event.key !== 'Tab') {
48
+ event.preventDefault();
49
+ }
50
+ }
51
+ };
52
+ if (!composite) {
53
+ additionalProps.tabIndex = tabIndexProp;
54
+ if (!isNativeButton && disabled) {
55
+ additionalProps.tabIndex = focusableWhenDisabled ? tabIndexProp : -1;
56
+ }
57
+ }
58
+ if (isNativeButton && (focusableWhenDisabled || isFocusableComposite) || !isNativeButton && disabled) {
59
+ additionalProps['aria-disabled'] = disabled;
60
+ }
61
+ if (isNativeButton && (!focusableWhenDisabled || isNonFocusableComposite)) {
62
+ additionalProps.disabled = disabled;
63
+ }
64
+ return additionalProps;
65
+ }, [composite, disabled, focusableWhenDisabled, isFocusableComposite, isNonFocusableComposite, isNativeButton, tabIndexProp]);
66
+ return {
67
+ props
68
+ };
69
+ }
70
+
71
+ function useButton(parameters = {}) {
72
+ const {
73
+ disabled = false,
74
+ focusableWhenDisabled,
75
+ tabIndex = 0,
76
+ native: isNativeButton = true,
77
+ composite: compositeProp
78
+ } = parameters;
79
+ const elementRef = React.useRef(null);
80
+ const compositeRootContext = useCompositeRootContext(true);
81
+ const isCompositeItem = compositeProp ?? compositeRootContext !== undefined;
82
+ const {
83
+ props: focusableWhenDisabledProps
84
+ } = useFocusableWhenDisabled({
85
+ focusableWhenDisabled,
86
+ disabled,
87
+ composite: isCompositeItem,
88
+ tabIndex,
89
+ isNativeButton
90
+ });
91
+ if (process.env.NODE_ENV !== 'production') {
92
+ // eslint-disable-next-line react-hooks/rules-of-hooks
93
+ React.useEffect(() => {
94
+ if (!elementRef.current) {
95
+ return;
96
+ }
97
+ const isButtonTag = isButtonElement(elementRef.current);
98
+ if (isNativeButton) {
99
+ if (!isButtonTag) {
100
+ const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
101
+ 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`.';
102
+ error(`${message}${ownerStackMessage}`);
103
+ }
104
+ } else if (isButtonTag) {
105
+ const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
106
+ 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`.';
107
+ error(`${message}${ownerStackMessage}`);
108
+ }
109
+ }, [isNativeButton]);
110
+ }
111
+
112
+ // handles a disabled composite button rendering another button, e.g.
113
+ // <Toolbar.Button disabled render={<Menu.Trigger />} />
114
+ // the `disabled` prop needs to pass through 2 `useButton`s then finally
115
+ // delete the `disabled` attribute from DOM
116
+ const updateDisabled = React.useCallback(() => {
117
+ const element = elementRef.current;
118
+ if (!isButtonElement(element)) {
119
+ return;
120
+ }
121
+ if (isCompositeItem && disabled && focusableWhenDisabledProps.disabled === undefined && element.disabled) {
122
+ element.disabled = false;
123
+ }
124
+ }, [disabled, focusableWhenDisabledProps.disabled, isCompositeItem]);
125
+ useIsoLayoutEffect(updateDisabled, [updateDisabled]);
126
+ const getButtonProps = React.useCallback((externalProps = {}) => {
127
+ const {
128
+ onClick: externalOnClick,
129
+ onMouseDown: externalOnMouseDown,
130
+ onKeyUp: externalOnKeyUp,
131
+ onKeyDown: externalOnKeyDown,
132
+ onPointerDown: externalOnPointerDown,
133
+ ...otherExternalProps
134
+ } = externalProps;
135
+ const type = isNativeButton ? 'button' : undefined;
136
+ return mergeProps({
137
+ type,
138
+ onClick(event) {
139
+ if (disabled) {
140
+ event.preventDefault();
141
+ return;
142
+ }
143
+ externalOnClick?.(event);
144
+ },
145
+ onMouseDown(event) {
146
+ if (!disabled) {
147
+ externalOnMouseDown?.(event);
148
+ }
149
+ },
150
+ onKeyDown(event) {
151
+ if (disabled) {
152
+ return;
153
+ }
154
+ makeEventPreventable(event);
155
+ externalOnKeyDown?.(event);
156
+ if (event.baseUIHandlerPrevented) {
157
+ return;
158
+ }
159
+ const isCurrentTarget = event.target === event.currentTarget;
160
+ const currentTarget = event.currentTarget;
161
+ const isButton = isButtonElement(currentTarget);
162
+ const isLink = !isNativeButton && isValidLinkElement(currentTarget);
163
+ const shouldClick = isCurrentTarget && (isNativeButton ? isButton : !isLink);
164
+ const isEnterKey = event.key === 'Enter';
165
+ const isSpaceKey = event.key === ' ';
166
+ const role = currentTarget.getAttribute('role');
167
+ const isTextNavigationRole = role?.startsWith('menuitem') || role === 'option' || role === 'gridcell';
168
+ if (isCurrentTarget && isCompositeItem && isSpaceKey) {
169
+ if (event.defaultPrevented && isTextNavigationRole) {
170
+ return;
171
+ }
172
+ event.preventDefault();
173
+ if (isLink || isNativeButton && isButton) {
174
+ currentTarget.click();
175
+ event.preventBaseUIHandler();
176
+ } else if (shouldClick) {
177
+ externalOnClick?.(event);
178
+ event.preventBaseUIHandler();
179
+ }
180
+ return;
181
+ }
182
+
183
+ // Keyboard accessibility for native and non-native elements.
184
+ if (shouldClick) {
185
+ if (!isNativeButton && (isSpaceKey || isEnterKey)) {
186
+ event.preventDefault();
187
+ }
188
+ if (!isNativeButton && isEnterKey) {
189
+ externalOnClick?.(event);
190
+ }
191
+ }
192
+ },
193
+ onKeyUp(event) {
194
+ if (disabled) {
195
+ return;
196
+ }
197
+
198
+ // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
199
+ // https://codesandbox.io/p/sandbox/button-keyup-preventdefault-dn7f0
200
+ makeEventPreventable(event);
201
+ externalOnKeyUp?.(event);
202
+ if (event.target === event.currentTarget && isNativeButton && isCompositeItem && isButtonElement(event.currentTarget) && event.key === ' ') {
203
+ event.preventDefault();
204
+ return;
205
+ }
206
+ if (event.baseUIHandlerPrevented) {
207
+ return;
208
+ }
209
+
210
+ // Keyboard accessibility for non interactive elements
211
+ if (event.target === event.currentTarget && !isNativeButton && !isCompositeItem && event.key === ' ') {
212
+ externalOnClick?.(event);
213
+ }
214
+ },
215
+ onPointerDown(event) {
216
+ if (disabled) {
217
+ event.preventDefault();
218
+ return;
219
+ }
220
+ externalOnPointerDown?.(event);
221
+ }
222
+ }, !isNativeButton ? {
223
+ role: 'button'
224
+ } : undefined, focusableWhenDisabledProps, otherExternalProps);
225
+ }, [disabled, focusableWhenDisabledProps, isCompositeItem, isNativeButton]);
226
+ const buttonRef = useStableCallback(element => {
227
+ elementRef.current = element;
228
+ updateDisabled();
229
+ });
230
+ return {
231
+ getButtonProps,
232
+ buttonRef
233
+ };
234
+ }
235
+ function isButtonElement(elem) {
236
+ return isHTMLElement(elem) && elem.tagName === 'BUTTON';
237
+ }
238
+ function isValidLinkElement(elem) {
239
+ return Boolean(elem?.tagName === 'A' && elem?.href);
240
+ }
241
+
242
+ /**
243
+ * Wraps `useId` and prefixes generated `id`s with `base-ui-`
244
+ * @param {string | undefined} idOverride overrides the generated id when provided
245
+ * @returns {string | undefined}
246
+ */
247
+ function useBaseUiId(idOverride) {
248
+ return useId(idOverride, 'base-ui');
249
+ }
250
+
251
+ export { useButton as a, useCompositeRootContext as b, error as e, useBaseUiId as u };