@launchpad-ui/menu 0.6.40 → 0.6.41

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 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
- ({ 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
- );
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 { separatorProps } = useSeparator({
35
+ const {
36
+ separatorProps
37
+ } = useSeparator({
27
38
  orientation,
28
39
  elementType
29
40
  });
30
- return /* @__PURE__ */ jsx("div", { ...separatorProps, "data-test-id": testId, ref: innerRef, className: "Menu-divider" });
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, { focusRingClass: "has-focus", children: /* @__PURE__ */ jsx(
57
- Component,
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, { children: [
73
- Icon && /* @__PURE__ */ jsx("span", { className: "Menu-item-icon", children: /* @__PURE__ */ jsx(Icon, { size: "small" }) }),
74
- children
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
- Tooltip,
81
- {
82
- content: tooltip,
83
- rootElementStyle: { display: "block" },
84
- allowBoundaryElementOverflow: true,
85
- placement: tooltipPlacement ? tooltipPlacement : "bottom",
86
- ...tooltipOptions || {},
87
- children: renderedItem
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, { ...finalProps, children });
122
+ return /* @__PURE__ */ jsx(MenuItem, {
123
+ ...finalProps,
124
+ children
125
+ });
110
126
  };
111
- const MenuItemList = forwardRef(({ children, ...rest }, ref) => /* @__PURE__ */ jsx("div", { ...rest, ref, "data-test-id": "menu-item-list", className: "Menu-item-list", children }));
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 { ariaLabel, placeholder, "data-test-id": testId = "menu-search", ...finalProps } = props;
115
- return /* @__PURE__ */ jsx("div", { className: "Menu-search", children: /* @__PURE__ */ jsx(
116
- TextField,
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({ wrap: true });
191
+ focusManager.focusNext({
192
+ wrap: true
193
+ });
162
194
  }, [focusManager]);
163
195
  const handleArrowUp = useCallback(() => {
164
- focusManager.focusPrevious({ wrap: true });
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 { items: elements, searchElement: searchElem };
217
+ return {
218
+ items: elements,
219
+ searchElement: searchElem
220
+ };
184
221
  }
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
- })
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
- 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
- );
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, { "data-test-id": testId, isVirtual: true, size, children: /* @__PURE__ */ jsx(
232
- ItemVirtualizer,
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, { "data-test-id": testId, size, children: [
245
- reduceItems.searchElement,
246
- /* @__PURE__ */ jsx(MenuItemList, { role: "presentation", children: reduceItems.items })
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
- 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;
288
- }
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 {};
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
- [handleKeyboardFocusInteraction, menuItemClassName, onSelect]
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
- () => 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
- );
406
+ const renderSearch = useMemo(() => searchElement ? cloneElement(searchElement, {
407
+ onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
408
+ handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
409
+ handleFocusForward: () => focusMenuItem(0)
392
410
  }),
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
- {
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,