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