@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 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
- 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
- });
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
- focusRingClass: "has-focus",
74
- children: /* @__PURE__ */ jsx(Component, {
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("Menu-item", className, isHighlighted && "is-highlighted", nested && "Menu-item--nested", groupHeader && "Menu-item--header"),
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
- children: [Icon && /* @__PURE__ */ jsx("span", {
84
- className: "Menu-item-icon",
85
- children: /* @__PURE__ */ jsx(Icon, {
86
- size: "small"
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(Tooltip, {
94
- content: tooltip,
95
- rootElementStyle: {
96
- display: "block"
97
- },
98
- allowBoundaryElementOverflow: true,
99
- placement: tooltipPlacement ? tooltipPlacement : "bottom",
100
- ...tooltipOptions || {},
101
- children: renderedItem
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
- 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, {
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
- 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
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
- 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
- });
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
- "data-test-id": testId,
279
- isVirtual: true,
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
- "data-test-id": testId,
294
- size,
295
- children: [reduceItems.searchElement, /* @__PURE__ */ jsx(MenuItemList, {
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((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);
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
- 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]);
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(() => searchElement ? cloneElement(searchElement, {
407
- onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
408
- handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
409
- handleFocusForward: () => focusMenuItem(0)
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
- 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", {
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,