@launchpad-ui/menu 0.6.38 → 0.6.40
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/index.es.js +201 -234
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +202 -235
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.es.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import './style.css';
|
2
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
2
3
|
import { cx } from "classix";
|
3
4
|
import { forwardRef, useCallback, useMemo, Children, cloneElement, useRef, useId, useState, useEffect } from "react";
|
4
|
-
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
5
5
|
import { useSeparator } from "@react-aria/separator";
|
6
6
|
import { Tooltip } from "@launchpad-ui/tooltip";
|
7
7
|
import { Slot } from "@radix-ui/react-slot";
|
@@ -10,21 +10,12 @@ import { Link } from "react-router-dom";
|
|
10
10
|
import { TextField } from "@launchpad-ui/form";
|
11
11
|
import { useVirtual } from "react-virtual";
|
12
12
|
const Menu$1 = "";
|
13
|
-
const MenuBase = forwardRef(
|
14
|
-
children,
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
const classes = cx("Menu", isVirtual && "Menu--isVirtual", size && `MenuSize--${size}`);
|
20
|
-
return /* @__PURE__ */ jsx("div", {
|
21
|
-
...props,
|
22
|
-
role: "menu",
|
23
|
-
className: classes,
|
24
|
-
ref,
|
25
|
-
children
|
26
|
-
});
|
27
|
-
});
|
13
|
+
const MenuBase = forwardRef(
|
14
|
+
({ children, size, isVirtual, ...props }, ref) => {
|
15
|
+
const classes = cx("Menu", isVirtual && "Menu--isVirtual", size && `MenuSize--${size}`);
|
16
|
+
return /* @__PURE__ */ jsx("div", { ...props, role: "menu", className: classes, ref, children });
|
17
|
+
}
|
18
|
+
);
|
28
19
|
MenuBase.displayName = "MenuBase";
|
29
20
|
const MenuDivider = ({
|
30
21
|
elementType = "div",
|
@@ -32,18 +23,11 @@ const MenuDivider = ({
|
|
32
23
|
innerRef,
|
33
24
|
"data-test-id": testId = "menu-divider"
|
34
25
|
}) => {
|
35
|
-
const {
|
36
|
-
separatorProps
|
37
|
-
} = useSeparator({
|
26
|
+
const { separatorProps } = useSeparator({
|
38
27
|
orientation,
|
39
28
|
elementType
|
40
29
|
});
|
41
|
-
return /* @__PURE__ */ jsx("div", {
|
42
|
-
...separatorProps,
|
43
|
-
"data-test-id": testId,
|
44
|
-
ref: innerRef,
|
45
|
-
className: "Menu-divider"
|
46
|
-
});
|
30
|
+
return /* @__PURE__ */ jsx("div", { ...separatorProps, "data-test-id": testId, ref: innerRef, className: "Menu-divider" });
|
47
31
|
};
|
48
32
|
const defaultElement = "button";
|
49
33
|
const MenuItem = ({
|
@@ -69,37 +53,40 @@ const MenuItem = ({
|
|
69
53
|
...rest
|
70
54
|
} = props;
|
71
55
|
const Component = component || (asChild ? Slot : defaultElement);
|
72
|
-
const renderedItem = /* @__PURE__ */ jsx(FocusRing, {
|
73
|
-
|
74
|
-
|
56
|
+
const renderedItem = /* @__PURE__ */ jsx(FocusRing, { focusRingClass: "has-focus", children: /* @__PURE__ */ jsx(
|
57
|
+
Component,
|
58
|
+
{
|
75
59
|
...rest,
|
76
60
|
disabled,
|
77
61
|
"aria-disabled": disabled ? disabled : void 0,
|
78
|
-
className: cx(
|
62
|
+
className: cx(
|
63
|
+
"Menu-item",
|
64
|
+
className,
|
65
|
+
isHighlighted && "is-highlighted",
|
66
|
+
nested && "Menu-item--nested",
|
67
|
+
groupHeader && "Menu-item--header"
|
68
|
+
),
|
79
69
|
"data-test-id": testId,
|
80
70
|
role,
|
81
71
|
onKeyDown,
|
82
|
-
children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, {
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
}), children]
|
89
|
-
})
|
90
|
-
})
|
91
|
-
});
|
72
|
+
children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
73
|
+
Icon && /* @__PURE__ */ jsx("span", { className: "Menu-item-icon", children: /* @__PURE__ */ jsx(Icon, { size: "small" }) }),
|
74
|
+
children
|
75
|
+
] })
|
76
|
+
}
|
77
|
+
) });
|
92
78
|
if (tooltip) {
|
93
|
-
return /* @__PURE__ */ jsx(
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
79
|
+
return /* @__PURE__ */ jsx(
|
80
|
+
Tooltip,
|
81
|
+
{
|
82
|
+
content: tooltip,
|
83
|
+
rootElementStyle: { display: "block" },
|
84
|
+
allowBoundaryElementOverflow: true,
|
85
|
+
placement: tooltipPlacement ? tooltipPlacement : "bottom",
|
86
|
+
...tooltipOptions || {},
|
87
|
+
children: renderedItem
|
88
|
+
}
|
89
|
+
);
|
103
90
|
}
|
104
91
|
return renderedItem;
|
105
92
|
};
|
@@ -119,32 +106,15 @@ const MenuItemLink = ({
|
|
119
106
|
rel: newTab ? "noopener noreferrer" : void 0,
|
120
107
|
target: newTab ? "_blank" : void 0
|
121
108
|
};
|
122
|
-
return /* @__PURE__ */ jsx(MenuItem, {
|
123
|
-
...finalProps,
|
124
|
-
children
|
125
|
-
});
|
109
|
+
return /* @__PURE__ */ jsx(MenuItem, { ...finalProps, children });
|
126
110
|
};
|
127
|
-
const MenuItemList = forwardRef(({
|
128
|
-
children,
|
129
|
-
...rest
|
130
|
-
}, ref) => /* @__PURE__ */ jsx("div", {
|
131
|
-
...rest,
|
132
|
-
ref,
|
133
|
-
"data-test-id": "menu-item-list",
|
134
|
-
className: "Menu-item-list",
|
135
|
-
children
|
136
|
-
}));
|
111
|
+
const MenuItemList = forwardRef(({ children, ...rest }, ref) => /* @__PURE__ */ jsx("div", { ...rest, ref, "data-test-id": "menu-item-list", className: "Menu-item-list", children }));
|
137
112
|
MenuItemList.displayName = "MenuItemList";
|
138
113
|
const MenuSearch = forwardRef((props, ref) => {
|
139
|
-
const {
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
...finalProps
|
144
|
-
} = props;
|
145
|
-
return /* @__PURE__ */ jsx("div", {
|
146
|
-
className: "Menu-search",
|
147
|
-
children: /* @__PURE__ */ jsx(TextField, {
|
114
|
+
const { ariaLabel, placeholder, "data-test-id": testId = "menu-search", ...finalProps } = props;
|
115
|
+
return /* @__PURE__ */ jsx("div", { className: "Menu-search", children: /* @__PURE__ */ jsx(
|
116
|
+
TextField,
|
117
|
+
{
|
148
118
|
...finalProps,
|
149
119
|
ref,
|
150
120
|
className: "Menu-search-input",
|
@@ -154,8 +124,8 @@ const MenuSearch = forwardRef((props, ref) => {
|
|
154
124
|
autoComplete: "off",
|
155
125
|
placeholder,
|
156
126
|
"aria-label": ariaLabel || "Search"
|
157
|
-
}
|
158
|
-
});
|
127
|
+
}
|
128
|
+
) });
|
159
129
|
});
|
160
130
|
MenuSearch.displayName = "MenuSearch";
|
161
131
|
const createItemId = (index, id) => `${id}-item-${index}`;
|
@@ -188,14 +158,10 @@ const Menu = (props) => {
|
|
188
158
|
} = props;
|
189
159
|
const focusManager = useFocusManager();
|
190
160
|
const handleArrowDown = useCallback(() => {
|
191
|
-
focusManager.focusNext({
|
192
|
-
wrap: true
|
193
|
-
});
|
161
|
+
focusManager.focusNext({ wrap: true });
|
194
162
|
}, [focusManager]);
|
195
163
|
const handleArrowUp = useCallback(() => {
|
196
|
-
focusManager.focusPrevious({
|
197
|
-
wrap: true
|
198
|
-
});
|
164
|
+
focusManager.focusPrevious({ wrap: true });
|
199
165
|
}, [focusManager]);
|
200
166
|
const reduceItems = useMemo(() => {
|
201
167
|
const childrenProps = Children.toArray(children);
|
@@ -214,71 +180,57 @@ const Menu = (props) => {
|
|
214
180
|
break;
|
215
181
|
}
|
216
182
|
});
|
217
|
-
return {
|
218
|
-
items: elements,
|
219
|
-
searchElement: searchElem
|
220
|
-
};
|
183
|
+
return { items: elements, searchElement: searchElem };
|
221
184
|
}
|
222
|
-
return childrenProps.reduce(
|
223
|
-
items,
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
handleDown: handleArrowDown,
|
234
|
-
handleUp: handleArrowUp
|
235
|
-
})
|
236
|
-
})
|
237
|
-
};
|
238
|
-
case MenuItem:
|
239
|
-
case MenuItemLink:
|
240
|
-
return {
|
241
|
-
items: items.concat(child.props.disabled ? cloneElement(child, {
|
242
|
-
onClick: () => void 0,
|
243
|
-
onKeyDown: () => void 0,
|
244
|
-
tabIndex: -1,
|
245
|
-
disabled: true
|
246
|
-
}) : cloneElement(child, {
|
247
|
-
className: cx(child.props.className, menuItemClassName),
|
248
|
-
item: (_a = child.props.item) != null ? _a : items.length,
|
249
|
-
onClick: chainEventHandlers(child.props.onClick, () => {
|
250
|
-
var _a2;
|
251
|
-
onSelect == null ? void 0 : onSelect((_a2 = child.props.item) != null ? _a2 : items.length);
|
252
|
-
}),
|
253
|
-
onKeyDown: (e) => handleKeyboardInteractions(e, {
|
254
|
-
handleDown: handleArrowDown,
|
255
|
-
handleUp: handleArrowUp
|
185
|
+
return childrenProps.reduce(
|
186
|
+
({ items, searchElement }, child) => {
|
187
|
+
switch (child.type) {
|
188
|
+
case MenuSearch:
|
189
|
+
return {
|
190
|
+
items,
|
191
|
+
searchElement: cloneElement(child, {
|
192
|
+
onKeyDown: (e) => handleKeyboardInteractions(e, {
|
193
|
+
handleDown: handleArrowDown,
|
194
|
+
handleUp: handleArrowUp
|
195
|
+
})
|
256
196
|
})
|
257
|
-
}
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
197
|
+
};
|
198
|
+
case MenuItem:
|
199
|
+
case MenuItemLink:
|
200
|
+
return {
|
201
|
+
items: items.concat(
|
202
|
+
child.props.disabled ? cloneElement(child, {
|
203
|
+
onClick: () => void 0,
|
204
|
+
onKeyDown: () => void 0,
|
205
|
+
tabIndex: -1,
|
206
|
+
disabled: true
|
207
|
+
}) : cloneElement(child, {
|
208
|
+
className: cx(child.props.className, menuItemClassName),
|
209
|
+
item: child.props.item ?? items.length,
|
210
|
+
onClick: chainEventHandlers(child.props.onClick, () => {
|
211
|
+
onSelect == null ? void 0 : onSelect(child.props.item ?? items.length);
|
212
|
+
}),
|
213
|
+
onKeyDown: (e) => handleKeyboardInteractions(e, {
|
214
|
+
handleDown: handleArrowDown,
|
215
|
+
handleUp: handleArrowUp
|
216
|
+
})
|
217
|
+
})
|
218
|
+
),
|
219
|
+
searchElement
|
220
|
+
};
|
221
|
+
case MenuDivider:
|
222
|
+
return { items: items.concat(child), searchElement };
|
223
|
+
default:
|
224
|
+
return { items, searchElement };
|
225
|
+
}
|
226
|
+
},
|
227
|
+
{ items: [], searchElement: null }
|
228
|
+
);
|
275
229
|
}, [children, enableVirtualization, menuItemClassName, handleArrowDown, handleArrowUp, onSelect]);
|
276
230
|
if (enableVirtualization) {
|
277
|
-
return /* @__PURE__ */ jsx(MenuBase, {
|
278
|
-
|
279
|
-
|
280
|
-
size,
|
281
|
-
children: /* @__PURE__ */ jsx(ItemVirtualizer, {
|
231
|
+
return /* @__PURE__ */ jsx(MenuBase, { "data-test-id": testId, isVirtual: true, size, children: /* @__PURE__ */ jsx(
|
232
|
+
ItemVirtualizer,
|
233
|
+
{
|
282
234
|
items: Children.toArray(reduceItems.items),
|
283
235
|
searchElement: reduceItems.searchElement,
|
284
236
|
overscan,
|
@@ -286,17 +238,13 @@ const Menu = (props) => {
|
|
286
238
|
onSelect,
|
287
239
|
itemHeight,
|
288
240
|
focusManager
|
289
|
-
}
|
290
|
-
});
|
241
|
+
}
|
242
|
+
) });
|
291
243
|
}
|
292
|
-
return /* @__PURE__ */ jsxs(MenuBase, {
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
role: "presentation",
|
297
|
-
children: reduceItems.items
|
298
|
-
})]
|
299
|
-
});
|
244
|
+
return /* @__PURE__ */ jsxs(MenuBase, { "data-test-id": testId, size, children: [
|
245
|
+
reduceItems.searchElement,
|
246
|
+
/* @__PURE__ */ jsx(MenuItemList, { role: "presentation", children: reduceItems.items })
|
247
|
+
] });
|
300
248
|
};
|
301
249
|
const ItemVirtualizer = (props) => {
|
302
250
|
const {
|
@@ -326,61 +274,70 @@ const ItemVirtualizer = (props) => {
|
|
326
274
|
rowVirtualizer.scrollToIndex(0);
|
327
275
|
(_b = (_a = searchRef.current) == null ? void 0 : _a.focus) == null ? void 0 : _b.call(_a);
|
328
276
|
}, [rowVirtualizer]);
|
329
|
-
const focusMenuItem = useCallback(
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
if (hasSearch) {
|
341
|
-
focusSearchBar();
|
342
|
-
} else {
|
343
|
-
focusMenuItem(direction === "next" ? 0 : lastVirtualItemIndex);
|
277
|
+
const focusMenuItem = useCallback(
|
278
|
+
(index) => {
|
279
|
+
rowVirtualizer.scrollToIndex(index);
|
280
|
+
setNextFocusValue(index);
|
281
|
+
},
|
282
|
+
[rowVirtualizer]
|
283
|
+
);
|
284
|
+
const handleKeyboardFocusInteraction = useCallback(
|
285
|
+
(direction) => {
|
286
|
+
if (focusedItemIndex.current === null || focusedItemIndex.current === void 0) {
|
287
|
+
return;
|
344
288
|
}
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
289
|
+
const nextIndex = direction === "next" ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;
|
290
|
+
const shouldWrap = direction === "next" && focusedItemIndex.current === lastVirtualItemIndex || direction === "previous" && focusedItemIndex.current === 0;
|
291
|
+
if (shouldWrap) {
|
292
|
+
if (hasSearch) {
|
293
|
+
focusSearchBar();
|
294
|
+
} else {
|
295
|
+
focusMenuItem(direction === "next" ? 0 : lastVirtualItemIndex);
|
296
|
+
}
|
297
|
+
return;
|
298
|
+
}
|
299
|
+
switch (direction) {
|
300
|
+
case "next":
|
301
|
+
rowVirtualizer.scrollToIndex(nextIndex);
|
302
|
+
focusManager.focusNext();
|
303
|
+
break;
|
304
|
+
case "previous":
|
305
|
+
rowVirtualizer.scrollToIndex(nextIndex);
|
306
|
+
focusManager.focusPrevious();
|
307
|
+
break;
|
308
|
+
}
|
309
|
+
},
|
310
|
+
[focusManager, focusMenuItem, focusSearchBar, hasSearch, lastVirtualItemIndex, rowVirtualizer]
|
311
|
+
);
|
312
|
+
const getItemProps = useCallback(
|
313
|
+
(itemElem, index) => {
|
314
|
+
const childProps = itemElem.props;
|
315
|
+
switch (itemElem.type) {
|
316
|
+
case MenuItem:
|
317
|
+
case MenuItemLink:
|
318
|
+
return {
|
319
|
+
className: cx(childProps.className, menuItemClassName),
|
320
|
+
onKeyDown: childProps.disabled ? () => void 0 : (e) => handleKeyboardFocusKeydown(e, {
|
321
|
+
handleFocusBackward: handleKeyboardFocusInteraction,
|
322
|
+
handleFocusForward: handleKeyboardFocusInteraction
|
323
|
+
}),
|
324
|
+
onFocus: chainEventHandlers(childProps.onFocus, () => {
|
325
|
+
focusedItemIndex.current = index;
|
326
|
+
}),
|
327
|
+
id: createItemId(index, menuId.current),
|
328
|
+
onBlur: chainEventHandlers(childProps.onBlur, () => {
|
329
|
+
focusedItemIndex.current = null;
|
330
|
+
}),
|
331
|
+
onClick: childProps.disabled ? () => void 0 : chainEventHandlers(childProps.onClick, () => {
|
332
|
+
onSelect == null ? void 0 : onSelect(childProps.item);
|
333
|
+
})
|
334
|
+
};
|
335
|
+
default:
|
336
|
+
return {};
|
337
|
+
}
|
338
|
+
},
|
339
|
+
[handleKeyboardFocusInteraction, menuItemClassName, onSelect]
|
340
|
+
);
|
384
341
|
useEffect(() => {
|
385
342
|
if (nextFocusValue !== null) {
|
386
343
|
requestAnimationFrame(() => {
|
@@ -403,42 +360,52 @@ const ItemVirtualizer = (props) => {
|
|
403
360
|
}
|
404
361
|
}
|
405
362
|
};
|
406
|
-
const renderSearch = useMemo(
|
407
|
-
|
408
|
-
|
409
|
-
|
363
|
+
const renderSearch = useMemo(
|
364
|
+
() => searchElement ? cloneElement(searchElement, {
|
365
|
+
onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
|
366
|
+
handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
|
367
|
+
handleFocusForward: () => focusMenuItem(0)
|
368
|
+
}),
|
369
|
+
ref: searchRef
|
370
|
+
}) : null,
|
371
|
+
[searchElement, lastVirtualItemIndex, focusMenuItem]
|
372
|
+
);
|
373
|
+
const renderItems = useMemo(
|
374
|
+
() => rowVirtualizer.virtualItems.map((virtualRow) => {
|
375
|
+
if (!items) {
|
376
|
+
return null;
|
377
|
+
}
|
378
|
+
const elem = items[virtualRow.index];
|
379
|
+
return /* @__PURE__ */ jsx(
|
380
|
+
"div",
|
381
|
+
{
|
382
|
+
ref: elem.type !== MenuItem || elem.type !== MenuItemLink ? virtualRow.measureRef : void 0,
|
383
|
+
role: "presentation",
|
384
|
+
className: cx("VirtualMenu-item"),
|
385
|
+
style: {
|
386
|
+
transform: `translateY(${virtualRow.start}px)`
|
387
|
+
},
|
388
|
+
children: cloneElement(elem, getItemProps(elem, virtualRow.index))
|
389
|
+
},
|
390
|
+
virtualRow.index
|
391
|
+
);
|
410
392
|
}),
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
return /* @__PURE__ */ jsx("div", {
|
419
|
-
ref: elem.type !== MenuItem || elem.type !== MenuItemLink ? virtualRow.measureRef : void 0,
|
420
|
-
role: "presentation",
|
421
|
-
className: cx("VirtualMenu-item"),
|
422
|
-
style: {
|
423
|
-
transform: `translateY(${virtualRow.start}px)`
|
424
|
-
},
|
425
|
-
children: cloneElement(elem, getItemProps(elem, virtualRow.index))
|
426
|
-
}, virtualRow.index);
|
427
|
-
}), [rowVirtualizer.virtualItems, items, getItemProps]);
|
428
|
-
return /* @__PURE__ */ jsxs(Fragment, {
|
429
|
-
children: [renderSearch, /* @__PURE__ */ jsx(MenuItemList, {
|
430
|
-
ref: parentRef,
|
431
|
-
role: "presentation",
|
432
|
-
children: /* @__PURE__ */ jsx("div", {
|
393
|
+
[rowVirtualizer.virtualItems, items, getItemProps]
|
394
|
+
);
|
395
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
396
|
+
renderSearch,
|
397
|
+
/* @__PURE__ */ jsx(MenuItemList, { ref: parentRef, role: "presentation", children: /* @__PURE__ */ jsx(
|
398
|
+
"div",
|
399
|
+
{
|
433
400
|
role: "presentation",
|
434
401
|
className: "VirtualMenu-item-list",
|
435
402
|
style: {
|
436
403
|
height: `${rowVirtualizer.totalSize}px`
|
437
404
|
},
|
438
405
|
children: renderItems
|
439
|
-
}
|
440
|
-
})
|
441
|
-
});
|
406
|
+
}
|
407
|
+
) })
|
408
|
+
] });
|
442
409
|
};
|
443
410
|
export {
|
444
411
|
Menu,
|