@arun-dev/headless 4.6.0 → 4.7.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/chunk-2S4XG4VD.js +33 -0
- package/dist/menu/index.cjs +438 -0
- package/dist/menu/index.d.cts +115 -0
- package/dist/menu/index.d.ts +115 -0
- package/dist/menu/index.js +255 -0
- package/dist/tabs/index.cjs +62 -43
- package/dist/tabs/index.d.cts +15 -4
- package/dist/tabs/index.d.ts +15 -4
- package/dist/tabs/index.js +37 -43
- package/package.json +11 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/core/rovingFocus.ts
|
|
2
|
+
function byDocumentOrder(a, b) {
|
|
3
|
+
const x = a.ref.current;
|
|
4
|
+
const y = b.ref.current;
|
|
5
|
+
if (!x || !y) return 0;
|
|
6
|
+
return x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
7
|
+
}
|
|
8
|
+
function rovingIndex(key, {
|
|
9
|
+
count,
|
|
10
|
+
current,
|
|
11
|
+
orientation,
|
|
12
|
+
rtl
|
|
13
|
+
}) {
|
|
14
|
+
if (count === 0) return null;
|
|
15
|
+
const [prevKey, nextKey] = orientation === "vertical" ? ["ArrowUp", "ArrowDown"] : rtl ? ["ArrowRight", "ArrowLeft"] : ["ArrowLeft", "ArrowRight"];
|
|
16
|
+
switch (key) {
|
|
17
|
+
case nextKey:
|
|
18
|
+
return current < 0 ? 0 : (current + 1) % count;
|
|
19
|
+
case prevKey:
|
|
20
|
+
return current < 0 ? count - 1 : (current - 1 + count) % count;
|
|
21
|
+
case "Home":
|
|
22
|
+
return 0;
|
|
23
|
+
case "End":
|
|
24
|
+
return count - 1;
|
|
25
|
+
default:
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
byDocumentOrder,
|
|
32
|
+
rovingIndex
|
|
33
|
+
};
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/menu/index.ts
|
|
21
|
+
var menu_exports = {};
|
|
22
|
+
__export(menu_exports, {
|
|
23
|
+
Menu: () => index_parts_exports
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(menu_exports);
|
|
26
|
+
|
|
27
|
+
// src/menu/index.parts.ts
|
|
28
|
+
var index_parts_exports = {};
|
|
29
|
+
__export(index_parts_exports, {
|
|
30
|
+
Item: () => MenuItem,
|
|
31
|
+
Popup: () => MenuPopup,
|
|
32
|
+
Root: () => MenuRoot,
|
|
33
|
+
Trigger: () => MenuTrigger
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// src/menu/MenuRoot.tsx
|
|
37
|
+
var import_react3 = require("react");
|
|
38
|
+
|
|
39
|
+
// src/core/anchoring.ts
|
|
40
|
+
function anchorNameFor(id) {
|
|
41
|
+
return `--hl-anchor-${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
42
|
+
}
|
|
43
|
+
var POSITION_AREA = {
|
|
44
|
+
bottom: { start: "bottom span-right", center: "bottom", end: "bottom span-left" },
|
|
45
|
+
top: { start: "top span-right", center: "top", end: "top span-left" },
|
|
46
|
+
right: { start: "right span-bottom", center: "right", end: "right span-top" },
|
|
47
|
+
left: { start: "left span-bottom", center: "left", end: "left span-top" }
|
|
48
|
+
};
|
|
49
|
+
function anchoredPopupStyle(anchorName, side, align) {
|
|
50
|
+
return {
|
|
51
|
+
positionAnchor: anchorName,
|
|
52
|
+
positionArea: POSITION_AREA[side][align],
|
|
53
|
+
// Flip across the axis it opens on; the other axis is left alone.
|
|
54
|
+
positionTryFallbacks: side === "top" || side === "bottom" ? "flip-block" : "flip-inline"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function anchoredDataAttributes(side, align) {
|
|
58
|
+
return { "data-side": side, "data-align": align };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/core/rovingFocus.ts
|
|
62
|
+
function byDocumentOrder(a, b) {
|
|
63
|
+
const x = a.ref.current;
|
|
64
|
+
const y = b.ref.current;
|
|
65
|
+
if (!x || !y) return 0;
|
|
66
|
+
return x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
67
|
+
}
|
|
68
|
+
function rovingIndex(key, {
|
|
69
|
+
count,
|
|
70
|
+
current,
|
|
71
|
+
orientation,
|
|
72
|
+
rtl
|
|
73
|
+
}) {
|
|
74
|
+
if (count === 0) return null;
|
|
75
|
+
const [prevKey, nextKey] = orientation === "vertical" ? ["ArrowUp", "ArrowDown"] : rtl ? ["ArrowRight", "ArrowLeft"] : ["ArrowLeft", "ArrowRight"];
|
|
76
|
+
switch (key) {
|
|
77
|
+
case nextKey:
|
|
78
|
+
return current < 0 ? 0 : (current + 1) % count;
|
|
79
|
+
case prevKey:
|
|
80
|
+
return current < 0 ? count - 1 : (current - 1 + count) % count;
|
|
81
|
+
case "Home":
|
|
82
|
+
return 0;
|
|
83
|
+
case "End":
|
|
84
|
+
return count - 1;
|
|
85
|
+
default:
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/core/useControlled.ts
|
|
91
|
+
var import_react = require("react");
|
|
92
|
+
function useControlled({
|
|
93
|
+
controlled,
|
|
94
|
+
default: defaultValue,
|
|
95
|
+
name = "Component",
|
|
96
|
+
state = "value"
|
|
97
|
+
}) {
|
|
98
|
+
const { current: isControlled } = (0, import_react.useRef)(controlled !== void 0);
|
|
99
|
+
const [uncontrolled, setUncontrolled] = (0, import_react.useState)(defaultValue);
|
|
100
|
+
const defaultRef = (0, import_react.useRef)(defaultValue);
|
|
101
|
+
const value = isControlled ? controlled : uncontrolled;
|
|
102
|
+
if (process.env.NODE_ENV !== "production") {
|
|
103
|
+
if (isControlled !== (controlled !== void 0)) {
|
|
104
|
+
console.error(
|
|
105
|
+
`${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (!isControlled && defaultRef.current !== defaultValue) {
|
|
109
|
+
console.error(
|
|
110
|
+
`${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const setValue = (0, import_react.useCallback)(
|
|
115
|
+
(next) => {
|
|
116
|
+
if (!isControlled) setUncontrolled(next);
|
|
117
|
+
},
|
|
118
|
+
[isControlled]
|
|
119
|
+
);
|
|
120
|
+
return [value, setValue];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/menu/MenuRootContext.ts
|
|
124
|
+
var import_react2 = require("react");
|
|
125
|
+
var MenuRootContext = (0, import_react2.createContext)(null);
|
|
126
|
+
function useMenuRootContext(part) {
|
|
127
|
+
const context = (0, import_react2.useContext)(MenuRootContext);
|
|
128
|
+
if (context === null) {
|
|
129
|
+
throw new Error(`<Menu.${part}> must be rendered inside <Menu.Root>.`);
|
|
130
|
+
}
|
|
131
|
+
return context;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/menu/MenuRoot.tsx
|
|
135
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
136
|
+
function MenuRoot({
|
|
137
|
+
open: openProp,
|
|
138
|
+
defaultOpen,
|
|
139
|
+
onOpenChange,
|
|
140
|
+
focusableWhenDisabled = false,
|
|
141
|
+
children
|
|
142
|
+
}) {
|
|
143
|
+
const [open, setOpenState] = useControlled({
|
|
144
|
+
controlled: openProp,
|
|
145
|
+
default: defaultOpen ?? false,
|
|
146
|
+
name: "Menu.Root",
|
|
147
|
+
state: "open"
|
|
148
|
+
});
|
|
149
|
+
const setOpen = (0, import_react3.useCallback)(
|
|
150
|
+
(next) => {
|
|
151
|
+
if (next === open) return;
|
|
152
|
+
setOpenState(next);
|
|
153
|
+
onOpenChange?.(next);
|
|
154
|
+
},
|
|
155
|
+
[open, setOpenState, onOpenChange]
|
|
156
|
+
);
|
|
157
|
+
const [items, setItems] = (0, import_react3.useState)([]);
|
|
158
|
+
const register = (0, import_react3.useCallback)((entry) => {
|
|
159
|
+
setItems((current) => [...current, entry].sort(byDocumentOrder));
|
|
160
|
+
return () => setItems((current) => current.filter((i) => i !== entry));
|
|
161
|
+
}, []);
|
|
162
|
+
const navigableItems = (0, import_react3.useMemo)(
|
|
163
|
+
() => focusableWhenDisabled ? items : items.filter((i) => !i.disabled),
|
|
164
|
+
[items, focusableWhenDisabled]
|
|
165
|
+
);
|
|
166
|
+
const id = (0, import_react3.useId)();
|
|
167
|
+
const triggerRef = (0, import_react3.useRef)(null);
|
|
168
|
+
const focusOnOpenRef = (0, import_react3.useRef)("first");
|
|
169
|
+
const context = (0, import_react3.useMemo)(
|
|
170
|
+
() => ({
|
|
171
|
+
open,
|
|
172
|
+
setOpen,
|
|
173
|
+
navigableItems,
|
|
174
|
+
focusableWhenDisabled,
|
|
175
|
+
register,
|
|
176
|
+
focusOnOpenRef,
|
|
177
|
+
triggerId: `${id}-trigger`,
|
|
178
|
+
popupId: `${id}-popup`,
|
|
179
|
+
anchorName: anchorNameFor(id),
|
|
180
|
+
triggerRef
|
|
181
|
+
}),
|
|
182
|
+
[open, setOpen, navigableItems, focusableWhenDisabled, register, id]
|
|
183
|
+
);
|
|
184
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuRootContext.Provider, { value: context, children });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/core/useRender.ts
|
|
188
|
+
var import_react4 = require("react");
|
|
189
|
+
|
|
190
|
+
// src/core/mergeProps.ts
|
|
191
|
+
function isEventHandler(key) {
|
|
192
|
+
return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
|
|
193
|
+
}
|
|
194
|
+
function mergeRefs(...refs) {
|
|
195
|
+
return (node) => {
|
|
196
|
+
const cleanups = refs.map((ref) => {
|
|
197
|
+
if (typeof ref === "function") {
|
|
198
|
+
const result = ref(node);
|
|
199
|
+
return typeof result === "function" ? result : () => ref(null);
|
|
200
|
+
}
|
|
201
|
+
if (ref && typeof ref === "object") {
|
|
202
|
+
ref.current = node;
|
|
203
|
+
return () => {
|
|
204
|
+
ref.current = null;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return void 0;
|
|
208
|
+
});
|
|
209
|
+
return () => {
|
|
210
|
+
cleanups.forEach((cleanup) => {
|
|
211
|
+
if (typeof cleanup === "function") cleanup();
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function isSyntheticEvent(event) {
|
|
217
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
218
|
+
}
|
|
219
|
+
function makeEventPreventable(event) {
|
|
220
|
+
if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
|
|
221
|
+
const target = event;
|
|
222
|
+
target.preventComponentHandler = () => {
|
|
223
|
+
target.componentHandlerPrevented = true;
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function isComponentHandlerPrevented(event) {
|
|
227
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
228
|
+
}
|
|
229
|
+
function chainHandlers(earlier, value) {
|
|
230
|
+
return (...args) => {
|
|
231
|
+
const event = args[0];
|
|
232
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
233
|
+
value(...args);
|
|
234
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
235
|
+
earlier(...args);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function mergeProps(...objects) {
|
|
239
|
+
const merged = {};
|
|
240
|
+
for (const props of objects) {
|
|
241
|
+
if (!props) continue;
|
|
242
|
+
for (const key of Object.keys(props)) {
|
|
243
|
+
const value = props[key];
|
|
244
|
+
if (value === void 0) continue;
|
|
245
|
+
if (isEventHandler(key) && typeof value === "function") {
|
|
246
|
+
const earlier = merged[key];
|
|
247
|
+
merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
|
|
248
|
+
} else if (key === "className") {
|
|
249
|
+
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
250
|
+
} else if (key === "style") {
|
|
251
|
+
merged.style = { ...merged.style, ...value };
|
|
252
|
+
} else if (key === "ref") {
|
|
253
|
+
merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
|
|
254
|
+
} else {
|
|
255
|
+
merged[key] = value;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return merged;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/core/useRender.ts
|
|
263
|
+
function useRender({
|
|
264
|
+
render,
|
|
265
|
+
defaultTagName,
|
|
266
|
+
props,
|
|
267
|
+
consumerProps
|
|
268
|
+
}) {
|
|
269
|
+
const merged = mergeProps(props, consumerProps);
|
|
270
|
+
if ((0, import_react4.isValidElement)(render)) {
|
|
271
|
+
return (0, import_react4.cloneElement)(render, mergeProps(merged, render.props));
|
|
272
|
+
}
|
|
273
|
+
return (0, import_react4.createElement)(defaultTagName, merged);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/menu/menuDataAttributes.ts
|
|
277
|
+
function menuDataAttributes({ open }) {
|
|
278
|
+
return {
|
|
279
|
+
"data-open": open ? "" : void 0,
|
|
280
|
+
"data-closed": open ? void 0 : ""
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function menuItemDataAttributes(disabled) {
|
|
284
|
+
return { "data-disabled": disabled ? "" : void 0 };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/menu/MenuTrigger.tsx
|
|
288
|
+
function MenuTrigger({ className, children, render, ...rest }) {
|
|
289
|
+
const { open, setOpen, focusOnOpenRef, triggerId, popupId, anchorName, triggerRef } = useMenuRootContext("Trigger");
|
|
290
|
+
return useRender({
|
|
291
|
+
render,
|
|
292
|
+
defaultTagName: "button",
|
|
293
|
+
props: {
|
|
294
|
+
type: "button",
|
|
295
|
+
id: triggerId,
|
|
296
|
+
"aria-haspopup": "menu",
|
|
297
|
+
"aria-expanded": open,
|
|
298
|
+
"aria-controls": popupId,
|
|
299
|
+
...menuDataAttributes({ open }),
|
|
300
|
+
style: { anchorName },
|
|
301
|
+
className,
|
|
302
|
+
children,
|
|
303
|
+
ref: triggerRef,
|
|
304
|
+
onClick() {
|
|
305
|
+
focusOnOpenRef.current = "first";
|
|
306
|
+
setOpen(!open);
|
|
307
|
+
},
|
|
308
|
+
onKeyDown(event) {
|
|
309
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
focusOnOpenRef.current = event.key === "ArrowUp" ? "last" : "first";
|
|
312
|
+
setOpen(true);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
consumerProps: rest
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/menu/MenuPopup.tsx
|
|
320
|
+
var import_react5 = require("react");
|
|
321
|
+
function MenuPopup({
|
|
322
|
+
side = "bottom",
|
|
323
|
+
align = "start",
|
|
324
|
+
className,
|
|
325
|
+
children,
|
|
326
|
+
render,
|
|
327
|
+
...rest
|
|
328
|
+
}) {
|
|
329
|
+
const {
|
|
330
|
+
open,
|
|
331
|
+
setOpen,
|
|
332
|
+
navigableItems,
|
|
333
|
+
focusOnOpenRef,
|
|
334
|
+
triggerId,
|
|
335
|
+
popupId,
|
|
336
|
+
anchorName,
|
|
337
|
+
triggerRef
|
|
338
|
+
} = useMenuRootContext("Popup");
|
|
339
|
+
const elementRef = (0, import_react5.useRef)(null);
|
|
340
|
+
const focusPendingRef = (0, import_react5.useRef)(false);
|
|
341
|
+
const [, resync] = (0, import_react5.useReducer)((n) => n + 1, 0);
|
|
342
|
+
(0, import_react5.useEffect)(() => {
|
|
343
|
+
const popup = elementRef.current;
|
|
344
|
+
if (!popup || typeof popup.showPopover !== "function") return;
|
|
345
|
+
const shown = popup.matches(":popover-open");
|
|
346
|
+
if (open && !shown) {
|
|
347
|
+
popup.showPopover({ source: triggerRef.current ?? void 0 });
|
|
348
|
+
if (!popup.matches(":popover-open")) setOpen(false);
|
|
349
|
+
else focusPendingRef.current = true;
|
|
350
|
+
} else if (!open && shown) {
|
|
351
|
+
focusPendingRef.current = false;
|
|
352
|
+
popup.hidePopover();
|
|
353
|
+
}
|
|
354
|
+
if (focusPendingRef.current && navigableItems.length > 0) {
|
|
355
|
+
focusPendingRef.current = false;
|
|
356
|
+
const target = focusOnOpenRef.current === "last" ? navigableItems.at(-1) : navigableItems[0];
|
|
357
|
+
target?.ref.current?.focus();
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
return useRender({
|
|
361
|
+
render,
|
|
362
|
+
defaultTagName: "div",
|
|
363
|
+
props: {
|
|
364
|
+
id: popupId,
|
|
365
|
+
popover: "auto",
|
|
366
|
+
role: "menu",
|
|
367
|
+
"aria-labelledby": triggerId,
|
|
368
|
+
...anchoredDataAttributes(side, align),
|
|
369
|
+
...menuDataAttributes({ open }),
|
|
370
|
+
style: anchoredPopupStyle(anchorName, side, align),
|
|
371
|
+
className,
|
|
372
|
+
children,
|
|
373
|
+
ref: elementRef,
|
|
374
|
+
onKeyDown(event) {
|
|
375
|
+
if (event.key === "Tab") {
|
|
376
|
+
setOpen(false);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const current = navigableItems.findIndex((i) => i.ref.current === event.target);
|
|
380
|
+
if (current < 0 && event.target !== event.currentTarget) return;
|
|
381
|
+
const next = rovingIndex(event.key, {
|
|
382
|
+
count: navigableItems.length,
|
|
383
|
+
current,
|
|
384
|
+
orientation: "vertical",
|
|
385
|
+
rtl: false
|
|
386
|
+
});
|
|
387
|
+
if (next === null) return;
|
|
388
|
+
event.preventDefault();
|
|
389
|
+
navigableItems[next]?.ref.current?.focus();
|
|
390
|
+
},
|
|
391
|
+
onToggle(event) {
|
|
392
|
+
if (event.target !== event.currentTarget) return;
|
|
393
|
+
if (event.nativeEvent.newState !== "closed") return;
|
|
394
|
+
setOpen(false);
|
|
395
|
+
resync();
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
consumerProps: rest
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/menu/MenuItem.tsx
|
|
403
|
+
var import_react6 = require("react");
|
|
404
|
+
function MenuItem({
|
|
405
|
+
disabled = false,
|
|
406
|
+
onClick,
|
|
407
|
+
className,
|
|
408
|
+
children,
|
|
409
|
+
render,
|
|
410
|
+
...rest
|
|
411
|
+
}) {
|
|
412
|
+
const { setOpen, focusableWhenDisabled, register } = useMenuRootContext("Item");
|
|
413
|
+
const elementRef = (0, import_react6.useRef)(null);
|
|
414
|
+
(0, import_react6.useLayoutEffect)(() => register({ disabled, ref: elementRef }), [register, disabled]);
|
|
415
|
+
return useRender({
|
|
416
|
+
render,
|
|
417
|
+
defaultTagName: "button",
|
|
418
|
+
props: {
|
|
419
|
+
type: "button",
|
|
420
|
+
role: "menuitem",
|
|
421
|
+
tabIndex: -1,
|
|
422
|
+
disabled: disabled && !focusableWhenDisabled || void 0,
|
|
423
|
+
"aria-disabled": disabled && focusableWhenDisabled || void 0,
|
|
424
|
+
...menuItemDataAttributes(disabled),
|
|
425
|
+
className,
|
|
426
|
+
children,
|
|
427
|
+
ref: elementRef,
|
|
428
|
+
onClick() {
|
|
429
|
+
if (!disabled) setOpen(false);
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
consumerProps: disabled ? rest : { ...rest, onClick }
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
436
|
+
0 && (module.exports = {
|
|
437
|
+
Menu
|
|
438
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
import { A as AnchorSide, a as AnchorAlign } from '../anchoring-Btxi0QwC.cjs';
|
|
4
|
+
|
|
5
|
+
type MenuRootProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Controlled state. Provide `onOpenChange` alongside it.
|
|
8
|
+
*
|
|
9
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
10
|
+
*/
|
|
11
|
+
open?: boolean;
|
|
12
|
+
/** Initial state when uncontrolled. Read once, at mount. */
|
|
13
|
+
defaultOpen?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Called on every request to open or close — Trigger, an Item, Tab, Esc, or a click
|
|
16
|
+
* outside. A controlled menu moves only if the parent accepts it.
|
|
17
|
+
*/
|
|
18
|
+
onOpenChange?: (open: boolean) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Keeps disabled items in the arrow-key sequence, so a screen reader announces them as
|
|
21
|
+
* unavailable. They still cannot be activated. Off by default: the arrows skip them.
|
|
22
|
+
*/
|
|
23
|
+
focusableWhenDisabled?: boolean;
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A menu button, by the WAI-ARIA pattern: a Trigger that opens a list of actions. Holds the
|
|
28
|
+
* state and shares it with its parts. Renders no element of its own.
|
|
29
|
+
*
|
|
30
|
+
* The popup is a native `popover`, anchored like Popover (decision 12): the platform supplies
|
|
31
|
+
* the top layer, light dismiss, Esc and focus return. This component adds state, one setter
|
|
32
|
+
* every close goes through, and the arrow-key focus a menu needs (`core/rovingFocus`).
|
|
33
|
+
*
|
|
34
|
+
* Each Item registers its own `disabled` from its props; the Root never reads it back from
|
|
35
|
+
* the DOM (decision 10). Elements are kept only to move focus to.
|
|
36
|
+
*/
|
|
37
|
+
declare function MenuRoot({ open: openProp, defaultOpen, onOpenChange, focusableWhenDisabled, children, }: MenuRootProps): react.JSX.Element;
|
|
38
|
+
|
|
39
|
+
type MenuTriggerOwnProps = {
|
|
40
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
41
|
+
render?: ReactElement;
|
|
42
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
43
|
+
ref?: Ref<HTMLElement>;
|
|
44
|
+
};
|
|
45
|
+
type MenuTriggerProps = MenuTriggerOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof MenuTriggerOwnProps>;
|
|
46
|
+
/**
|
|
47
|
+
* The menu button. A native `<button>`, so a click, Enter and Space open the menu at its
|
|
48
|
+
* first item with no key handling here; Down does the same, and Up opens it at the last.
|
|
49
|
+
* It is what the popup is anchored to, and names it: the popup is `aria-labelledby` it.
|
|
50
|
+
*/
|
|
51
|
+
declare function MenuTrigger({ className, children, render, ...rest }: MenuTriggerProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
52
|
+
|
|
53
|
+
type MenuSide = AnchorSide;
|
|
54
|
+
type MenuAlign = AnchorAlign;
|
|
55
|
+
type MenuPopupOwnProps = {
|
|
56
|
+
/** Which side of the trigger to open on. Flips to the opposite side when there is no room. */
|
|
57
|
+
side?: MenuSide;
|
|
58
|
+
/** Where along that side: flush with the trigger's start or end edge, or centred on it. */
|
|
59
|
+
align?: MenuAlign;
|
|
60
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
61
|
+
render?: ReactElement;
|
|
62
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
63
|
+
ref?: Ref<HTMLElement>;
|
|
64
|
+
};
|
|
65
|
+
type MenuPopupProps = MenuPopupOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof MenuPopupOwnProps | 'popover'>;
|
|
66
|
+
/**
|
|
67
|
+
* The menu: a native `popover="auto"` with `role="menu"`, anchored to the Trigger as
|
|
68
|
+
* Popover's popup is (decision 12), and labelled by it.
|
|
69
|
+
*
|
|
70
|
+
* Opening moves focus to the first item — the last, when Up on the Trigger opened it. Up
|
|
71
|
+
* and Down then move between items, wrapping, with Home and End; disabled items are skipped
|
|
72
|
+
* unless the Root sets `focusableWhenDisabled`. Tab closes the menu and lets focus move on.
|
|
73
|
+
*
|
|
74
|
+
* Light dismiss and Esc close the element before anyone is asked; the `toggle` event
|
|
75
|
+
* reports it through the Root, and the element reopens if the state stays open. Focus
|
|
76
|
+
* returns to the Trigger, the invoker, whenever the menu hides with focus inside it.
|
|
77
|
+
*/
|
|
78
|
+
declare function MenuPopup({ side, align, className, children, render, ...rest }: MenuPopupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
79
|
+
|
|
80
|
+
type MenuItemOwnProps = {
|
|
81
|
+
/**
|
|
82
|
+
* Cannot be activated. Skipped by the arrow keys, unless the Root sets
|
|
83
|
+
* `focusableWhenDisabled`.
|
|
84
|
+
*/
|
|
85
|
+
disabled?: boolean;
|
|
86
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
87
|
+
render?: ReactElement;
|
|
88
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
89
|
+
ref?: Ref<HTMLElement>;
|
|
90
|
+
};
|
|
91
|
+
type MenuItemProps = MenuItemOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof MenuItemOwnProps>;
|
|
92
|
+
/**
|
|
93
|
+
* One action. A native `<button>`, so Enter, Space and a click activate it with no key
|
|
94
|
+
* handling here: put the action in `onClick`. Activating it closes the menu — call
|
|
95
|
+
* `event.preventComponentHandler()` in your `onClick` to keep it open.
|
|
96
|
+
*
|
|
97
|
+
* `tabIndex={-1}`: the menu moves focus between items itself, and Tab leaves the menu.
|
|
98
|
+
*
|
|
99
|
+
* Disabled, it is a native `disabled` button, out of focus and activation. With the Root's
|
|
100
|
+
* `focusableWhenDisabled` it is `aria-disabled` instead, which keeps it focusable, so the
|
|
101
|
+
* activation a native `disabled` would block is blocked here: your `onClick` is not
|
|
102
|
+
* attached while it is disabled.
|
|
103
|
+
*/
|
|
104
|
+
declare function MenuItem({ disabled, onClick, className, children, render, ...rest }: MenuItemProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
105
|
+
|
|
106
|
+
declare namespace index_parts {
|
|
107
|
+
export { MenuItem as Item, MenuPopup as Popup, MenuRoot as Root, MenuTrigger as Trigger };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The state Menu.Root shares with its parts, and projects as `data-*` attributes. */
|
|
111
|
+
type MenuState = {
|
|
112
|
+
open: boolean;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { index_parts as Menu, type MenuAlign, type MenuItemProps, type MenuPopupProps, type MenuRootProps, type MenuSide, type MenuState, type MenuTriggerProps };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
|
+
import { A as AnchorSide, a as AnchorAlign } from '../anchoring-Btxi0QwC.js';
|
|
4
|
+
|
|
5
|
+
type MenuRootProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Controlled state. Provide `onOpenChange` alongside it.
|
|
8
|
+
*
|
|
9
|
+
* Never `undefined` once mounted: the mode is latched at mount, as for Switch.
|
|
10
|
+
*/
|
|
11
|
+
open?: boolean;
|
|
12
|
+
/** Initial state when uncontrolled. Read once, at mount. */
|
|
13
|
+
defaultOpen?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Called on every request to open or close — Trigger, an Item, Tab, Esc, or a click
|
|
16
|
+
* outside. A controlled menu moves only if the parent accepts it.
|
|
17
|
+
*/
|
|
18
|
+
onOpenChange?: (open: boolean) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Keeps disabled items in the arrow-key sequence, so a screen reader announces them as
|
|
21
|
+
* unavailable. They still cannot be activated. Off by default: the arrows skip them.
|
|
22
|
+
*/
|
|
23
|
+
focusableWhenDisabled?: boolean;
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A menu button, by the WAI-ARIA pattern: a Trigger that opens a list of actions. Holds the
|
|
28
|
+
* state and shares it with its parts. Renders no element of its own.
|
|
29
|
+
*
|
|
30
|
+
* The popup is a native `popover`, anchored like Popover (decision 12): the platform supplies
|
|
31
|
+
* the top layer, light dismiss, Esc and focus return. This component adds state, one setter
|
|
32
|
+
* every close goes through, and the arrow-key focus a menu needs (`core/rovingFocus`).
|
|
33
|
+
*
|
|
34
|
+
* Each Item registers its own `disabled` from its props; the Root never reads it back from
|
|
35
|
+
* the DOM (decision 10). Elements are kept only to move focus to.
|
|
36
|
+
*/
|
|
37
|
+
declare function MenuRoot({ open: openProp, defaultOpen, onOpenChange, focusableWhenDisabled, children, }: MenuRootProps): react.JSX.Element;
|
|
38
|
+
|
|
39
|
+
type MenuTriggerOwnProps = {
|
|
40
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
41
|
+
render?: ReactElement;
|
|
42
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
43
|
+
ref?: Ref<HTMLElement>;
|
|
44
|
+
};
|
|
45
|
+
type MenuTriggerProps = MenuTriggerOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof MenuTriggerOwnProps>;
|
|
46
|
+
/**
|
|
47
|
+
* The menu button. A native `<button>`, so a click, Enter and Space open the menu at its
|
|
48
|
+
* first item with no key handling here; Down does the same, and Up opens it at the last.
|
|
49
|
+
* It is what the popup is anchored to, and names it: the popup is `aria-labelledby` it.
|
|
50
|
+
*/
|
|
51
|
+
declare function MenuTrigger({ className, children, render, ...rest }: MenuTriggerProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
52
|
+
|
|
53
|
+
type MenuSide = AnchorSide;
|
|
54
|
+
type MenuAlign = AnchorAlign;
|
|
55
|
+
type MenuPopupOwnProps = {
|
|
56
|
+
/** Which side of the trigger to open on. Flips to the opposite side when there is no room. */
|
|
57
|
+
side?: MenuSide;
|
|
58
|
+
/** Where along that side: flush with the trigger's start or end edge, or centred on it. */
|
|
59
|
+
align?: MenuAlign;
|
|
60
|
+
/** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
|
|
61
|
+
render?: ReactElement;
|
|
62
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
63
|
+
ref?: Ref<HTMLElement>;
|
|
64
|
+
};
|
|
65
|
+
type MenuPopupProps = MenuPopupOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof MenuPopupOwnProps | 'popover'>;
|
|
66
|
+
/**
|
|
67
|
+
* The menu: a native `popover="auto"` with `role="menu"`, anchored to the Trigger as
|
|
68
|
+
* Popover's popup is (decision 12), and labelled by it.
|
|
69
|
+
*
|
|
70
|
+
* Opening moves focus to the first item — the last, when Up on the Trigger opened it. Up
|
|
71
|
+
* and Down then move between items, wrapping, with Home and End; disabled items are skipped
|
|
72
|
+
* unless the Root sets `focusableWhenDisabled`. Tab closes the menu and lets focus move on.
|
|
73
|
+
*
|
|
74
|
+
* Light dismiss and Esc close the element before anyone is asked; the `toggle` event
|
|
75
|
+
* reports it through the Root, and the element reopens if the state stays open. Focus
|
|
76
|
+
* returns to the Trigger, the invoker, whenever the menu hides with focus inside it.
|
|
77
|
+
*/
|
|
78
|
+
declare function MenuPopup({ side, align, className, children, render, ...rest }: MenuPopupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
79
|
+
|
|
80
|
+
type MenuItemOwnProps = {
|
|
81
|
+
/**
|
|
82
|
+
* Cannot be activated. Skipped by the arrow keys, unless the Root sets
|
|
83
|
+
* `focusableWhenDisabled`.
|
|
84
|
+
*/
|
|
85
|
+
disabled?: boolean;
|
|
86
|
+
/** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
|
|
87
|
+
render?: ReactElement;
|
|
88
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
89
|
+
ref?: Ref<HTMLElement>;
|
|
90
|
+
};
|
|
91
|
+
type MenuItemProps = MenuItemOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof MenuItemOwnProps>;
|
|
92
|
+
/**
|
|
93
|
+
* One action. A native `<button>`, so Enter, Space and a click activate it with no key
|
|
94
|
+
* handling here: put the action in `onClick`. Activating it closes the menu — call
|
|
95
|
+
* `event.preventComponentHandler()` in your `onClick` to keep it open.
|
|
96
|
+
*
|
|
97
|
+
* `tabIndex={-1}`: the menu moves focus between items itself, and Tab leaves the menu.
|
|
98
|
+
*
|
|
99
|
+
* Disabled, it is a native `disabled` button, out of focus and activation. With the Root's
|
|
100
|
+
* `focusableWhenDisabled` it is `aria-disabled` instead, which keeps it focusable, so the
|
|
101
|
+
* activation a native `disabled` would block is blocked here: your `onClick` is not
|
|
102
|
+
* attached while it is disabled.
|
|
103
|
+
*/
|
|
104
|
+
declare function MenuItem({ disabled, onClick, className, children, render, ...rest }: MenuItemProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
105
|
+
|
|
106
|
+
declare namespace index_parts {
|
|
107
|
+
export { MenuItem as Item, MenuPopup as Popup, MenuRoot as Root, MenuTrigger as Trigger };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The state Menu.Root shares with its parts, and projects as `data-*` attributes. */
|
|
111
|
+
type MenuState = {
|
|
112
|
+
open: boolean;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { index_parts as Menu, type MenuAlign, type MenuItemProps, type MenuPopupProps, type MenuRootProps, type MenuSide, type MenuState, type MenuTriggerProps };
|