@launchpad-ui/menu 0.3.6 → 0.4.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/index.js CHANGED
@@ -17,7 +17,10 @@ var __copyProps = (to, from, except, desc) => {
17
17
  }
18
18
  return to;
19
19
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
21
24
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
25
 
23
26
  // src/index.ts
@@ -41,29 +44,34 @@ var React = __toESM(require("react"));
41
44
  var import_clsx = __toESM(require("clsx"));
42
45
  var import_react = require("react");
43
46
  var import_Menu = require("./styles/Menu.css");
44
- var MenuBase = (0, import_react.forwardRef)(({ children, size, isVirtual, ...props }, ref) => {
45
- const classes = (0, import_clsx.default)("Menu", {
46
- "Menu--isVirtual": isVirtual,
47
- [`MenuSize--${size}`]: size
48
- });
49
- return /* @__PURE__ */ React.createElement("div", {
50
- ...props,
51
- role: "menu",
52
- className: classes,
53
- ref
54
- }, children);
55
- });
47
+ var import_jsx_runtime = require("react/jsx-runtime");
48
+ var MenuBase = (0, import_react.forwardRef)(
49
+ ({ children, size, isVirtual, ...props }, ref) => {
50
+ const classes = (0, import_clsx.default)("Menu", {
51
+ "Menu--isVirtual": isVirtual,
52
+ [`MenuSize--${size}`]: size
53
+ });
54
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
55
+ ...props,
56
+ role: "menu",
57
+ className: classes,
58
+ ref,
59
+ children
60
+ });
61
+ }
62
+ );
56
63
  MenuBase.displayName = "MenuBase";
57
64
 
58
65
  // src/MenuDivider.tsx
59
66
  var import_separator = require("@react-aria/separator");
60
67
  var import_Menu2 = require("./styles/Menu.css");
68
+ var import_jsx_runtime = require("react/jsx-runtime");
61
69
  var MenuDivider = ({ elementType = "div", orientation, innerRef }) => {
62
70
  const { separatorProps } = (0, import_separator.useSeparator)({
63
71
  orientation,
64
72
  elementType
65
73
  });
66
- return /* @__PURE__ */ React.createElement("div", {
74
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
67
75
  ...separatorProps,
68
76
  ref: innerRef,
69
77
  className: "Menu-divider"
@@ -78,6 +86,7 @@ var import_focus = require("@react-aria/focus");
78
86
  var import_clsx2 = __toESM(require("clsx"));
79
87
  var import_react_router_dom = require("react-router-dom");
80
88
  var import_Menu3 = require("./styles/Menu.css");
89
+ var import_jsx_runtime = require("react/jsx-runtime");
81
90
  var defaultElement = "button";
82
91
  var MenuItem = ({
83
92
  ...props
@@ -101,28 +110,43 @@ var MenuItem = ({
101
110
  ...rest
102
111
  } = props;
103
112
  const Component = component || (asChild ? import_react_slot.Slot : defaultElement);
104
- const renderedItem = /* @__PURE__ */ React.createElement(import_focus.FocusRing, {
105
- focusRingClass: "has-focus"
106
- }, /* @__PURE__ */ React.createElement(Component, {
107
- ...rest,
108
- disabled,
109
- "aria-disabled": disabled ? disabled : void 0,
110
- className: (0, import_clsx2.default)("Menu-item", className, { "is-highlighted": isHighlighted }, { "Menu-item--nested": nested }, { "Menu-item--header": groupHeader }),
111
- role,
112
- onKeyDown
113
- }, asChild ? children : /* @__PURE__ */ React.createElement(React.Fragment, null, Icon && /* @__PURE__ */ React.createElement("span", {
114
- className: "Menu-item-icon"
115
- }, /* @__PURE__ */ React.createElement(Icon, {
116
- size: import_icons.IconSize.SMALL
117
- })), children)));
113
+ const renderedItem = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_focus.FocusRing, {
114
+ focusRingClass: "has-focus",
115
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {
116
+ ...rest,
117
+ disabled,
118
+ "aria-disabled": disabled ? disabled : void 0,
119
+ className: (0, import_clsx2.default)(
120
+ "Menu-item",
121
+ className,
122
+ { "is-highlighted": isHighlighted },
123
+ { "Menu-item--nested": nested },
124
+ { "Menu-item--header": groupHeader }
125
+ ),
126
+ role,
127
+ onKeyDown,
128
+ children: asChild ? children : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
129
+ children: [
130
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
131
+ className: "Menu-item-icon",
132
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, {
133
+ size: import_icons.IconSize.SMALL
134
+ })
135
+ }),
136
+ children
137
+ ]
138
+ })
139
+ })
140
+ });
118
141
  if (tooltip) {
119
- return /* @__PURE__ */ React.createElement(import_tooltip.Tooltip, {
142
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltip.Tooltip, {
120
143
  content: tooltip,
121
144
  rootElementStyle: { display: "block" },
122
145
  allowBoundaryElementOverflow: true,
123
146
  placement: tooltipPlacement ? tooltipPlacement : "bottom",
124
- ...tooltipOptions || {}
125
- }, renderedItem);
147
+ ...tooltipOptions || {},
148
+ children: renderedItem
149
+ });
126
150
  }
127
151
  return renderedItem;
128
152
  };
@@ -142,40 +166,45 @@ var MenuItemLink = ({
142
166
  rel: newTab ? "noopener noreferrer" : void 0,
143
167
  target: newTab ? "_blank" : void 0
144
168
  };
145
- return /* @__PURE__ */ React.createElement(MenuItem, {
146
- ...finalProps
147
- }, children);
169
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuItem, {
170
+ ...finalProps,
171
+ children
172
+ });
148
173
  };
149
174
 
150
175
  // src/MenuItemList.tsx
151
176
  var import_react2 = require("react");
152
177
  var import_Menu4 = require("./styles/Menu.css");
153
- var MenuItemList = (0, import_react2.forwardRef)(({ children, ...rest }, ref) => /* @__PURE__ */ React.createElement("div", {
178
+ var import_jsx_runtime = require("react/jsx-runtime");
179
+ var MenuItemList = (0, import_react2.forwardRef)(({ children, ...rest }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
154
180
  ...rest,
155
181
  ref,
156
182
  "data-test-id": "menu-item-list",
157
- className: "Menu-item-list"
158
- }, children));
183
+ className: "Menu-item-list",
184
+ children
185
+ }));
159
186
  MenuItemList.displayName = "MenuItemList";
160
187
 
161
188
  // src/MenuSearch.tsx
162
189
  var import_form = require("@launchpad-ui/form");
163
190
  var import_react3 = require("react");
164
191
  var import_Menu5 = require("./styles/Menu.css");
192
+ var import_jsx_runtime = require("react/jsx-runtime");
165
193
  var MenuSearch = (0, import_react3.forwardRef)((props, ref) => {
166
194
  const { ariaLabel, placeholder, ...finalProps } = props;
167
- return /* @__PURE__ */ React.createElement("div", {
168
- className: "Menu-search"
169
- }, /* @__PURE__ */ React.createElement(import_form.TextField, {
170
- ...finalProps,
171
- ref,
172
- className: "Menu-search-input",
173
- tiny: true,
174
- type: "search",
175
- autoComplete: "off",
176
- placeholder,
177
- "aria-label": ariaLabel || "Search"
178
- }));
195
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
196
+ className: "Menu-search",
197
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form.TextField, {
198
+ ...finalProps,
199
+ ref,
200
+ className: "Menu-search-input",
201
+ tiny: true,
202
+ type: "search",
203
+ autoComplete: "off",
204
+ placeholder,
205
+ "aria-label": ariaLabel || "Search"
206
+ })
207
+ });
179
208
  });
180
209
  MenuSearch.displayName = "MenuSearch";
181
210
 
@@ -205,6 +234,7 @@ var chainEventHandlers = (...handlers) => (event) => {
205
234
  };
206
235
 
207
236
  // src/Menu.tsx
237
+ var import_jsx_runtime = require("react/jsx-runtime");
208
238
  var Menu = (props) => {
209
239
  const {
210
240
  children,
@@ -243,65 +273,76 @@ var Menu = (props) => {
243
273
  });
244
274
  return { items: elements, searchElement: searchElem };
245
275
  }
246
- return childrenProps.reduce(({ items, searchElement }, child) => {
247
- switch (child.type) {
248
- case MenuSearch:
249
- return {
250
- items,
251
- searchElement: (0, import_react4.cloneElement)(child, {
252
- onKeyDown: (e) => handleKeyboardInteractions(e, {
253
- handleDown: handleArrowDown,
254
- handleUp: handleArrowUp
255
- })
256
- })
257
- };
258
- case MenuItem:
259
- case MenuItemLink:
260
- return {
261
- items: items.concat(child.props.disabled ? (0, import_react4.cloneElement)(child, {
262
- onClick: () => void 0,
263
- onKeyDown: () => void 0,
264
- tabIndex: -1,
265
- disabled: true
266
- }) : (0, import_react4.cloneElement)(child, {
267
- className: (0, import_clsx3.default)(child.props.className, menuItemClassName),
268
- item: child.props.item ?? items.length,
269
- onClick: chainEventHandlers(child.props.onClick, () => {
270
- onSelect?.(child.props.item ?? items.length);
271
- }),
272
- onKeyDown: (e) => handleKeyboardInteractions(e, {
273
- handleDown: handleArrowDown,
274
- handleUp: handleArrowUp
276
+ return childrenProps.reduce(
277
+ ({ items, searchElement }, child) => {
278
+ switch (child.type) {
279
+ case MenuSearch:
280
+ return {
281
+ items,
282
+ searchElement: (0, import_react4.cloneElement)(child, {
283
+ onKeyDown: (e) => handleKeyboardInteractions(e, {
284
+ handleDown: handleArrowDown,
285
+ handleUp: handleArrowUp
286
+ })
275
287
  })
276
- })),
277
- searchElement
278
- };
279
- case MenuDivider:
280
- return { items: items.concat(child), searchElement };
281
- default:
282
- return { items, searchElement };
283
- }
284
- }, { items: [], searchElement: null });
288
+ };
289
+ case MenuItem:
290
+ case MenuItemLink:
291
+ return {
292
+ items: items.concat(
293
+ child.props.disabled ? (0, import_react4.cloneElement)(child, {
294
+ onClick: () => void 0,
295
+ onKeyDown: () => void 0,
296
+ tabIndex: -1,
297
+ disabled: true
298
+ }) : (0, import_react4.cloneElement)(child, {
299
+ className: (0, import_clsx3.default)(child.props.className, menuItemClassName),
300
+ item: child.props.item ?? items.length,
301
+ onClick: chainEventHandlers(child.props.onClick, () => {
302
+ onSelect?.(child.props.item ?? items.length);
303
+ }),
304
+ onKeyDown: (e) => handleKeyboardInteractions(e, {
305
+ handleDown: handleArrowDown,
306
+ handleUp: handleArrowUp
307
+ })
308
+ })
309
+ ),
310
+ searchElement
311
+ };
312
+ case MenuDivider:
313
+ return { items: items.concat(child), searchElement };
314
+ default:
315
+ return { items, searchElement };
316
+ }
317
+ },
318
+ { items: [], searchElement: null }
319
+ );
285
320
  }, [children, enableVirtualization, menuItemClassName, handleArrowDown, handleArrowUp, onSelect]);
286
321
  if (enableVirtualization) {
287
- return /* @__PURE__ */ React.createElement(MenuBase, {
322
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuBase, {
288
323
  isVirtual: true,
289
- size
290
- }, /* @__PURE__ */ React.createElement(ItemVirtualizer, {
291
- items: import_react4.Children.toArray(reduceItems.items),
292
- searchElement: reduceItems.searchElement,
293
- overscan,
294
- menuItemClassName,
295
- onSelect,
296
- itemHeight,
297
- focusManager
298
- }));
324
+ size,
325
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemVirtualizer, {
326
+ items: import_react4.Children.toArray(reduceItems.items),
327
+ searchElement: reduceItems.searchElement,
328
+ overscan,
329
+ menuItemClassName,
330
+ onSelect,
331
+ itemHeight,
332
+ focusManager
333
+ })
334
+ });
299
335
  }
300
- return /* @__PURE__ */ React.createElement(MenuBase, {
301
- size
302
- }, reduceItems.searchElement, /* @__PURE__ */ React.createElement(MenuItemList, {
303
- role: "presentation"
304
- }, reduceItems.items));
336
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(MenuBase, {
337
+ size,
338
+ children: [
339
+ reduceItems.searchElement,
340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuItemList, {
341
+ role: "presentation",
342
+ children: reduceItems.items
343
+ })
344
+ ]
345
+ });
305
346
  };
306
347
  var ItemVirtualizer = (props) => {
307
348
  const {
@@ -330,63 +371,72 @@ var ItemVirtualizer = (props) => {
330
371
  rowVirtualizer.scrollToIndex(0);
331
372
  searchRef.current?.focus?.();
332
373
  }, [rowVirtualizer]);
333
- const focusMenuItem = (0, import_react4.useCallback)((index) => {
334
- rowVirtualizer.scrollToIndex(index);
335
- setNextFocusValue(index);
336
- }, [rowVirtualizer]);
337
- const handleKeyboardFocusInteraction = (0, import_react4.useCallback)((direction) => {
338
- if (focusedItemIndex.current === null || focusedItemIndex.current === void 0) {
339
- return;
340
- }
341
- const nextIndex = direction === "next" ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;
342
- const shouldWrap = direction === "next" && focusedItemIndex.current === lastVirtualItemIndex || direction === "previous" && focusedItemIndex.current === 0;
343
- if (shouldWrap) {
344
- if (hasSearch) {
345
- focusSearchBar();
346
- } else {
347
- focusMenuItem(direction === "next" ? 0 : lastVirtualItemIndex);
374
+ const focusMenuItem = (0, import_react4.useCallback)(
375
+ (index) => {
376
+ rowVirtualizer.scrollToIndex(index);
377
+ setNextFocusValue(index);
378
+ },
379
+ [rowVirtualizer]
380
+ );
381
+ const handleKeyboardFocusInteraction = (0, import_react4.useCallback)(
382
+ (direction) => {
383
+ if (focusedItemIndex.current === null || focusedItemIndex.current === void 0) {
384
+ return;
348
385
  }
349
- return;
350
- }
351
- switch (direction) {
352
- case "next":
353
- rowVirtualizer.scrollToIndex(nextIndex);
354
- focusManager.focusNext();
355
- break;
356
- case "previous":
357
- rowVirtualizer.scrollToIndex(nextIndex);
358
- focusManager.focusPrevious();
359
- break;
360
- default:
361
- break;
362
- }
363
- }, [focusManager, focusMenuItem, focusSearchBar, hasSearch, lastVirtualItemIndex, rowVirtualizer]);
364
- const getItemProps = (0, import_react4.useCallback)((itemElem, index) => {
365
- const childProps = itemElem.props;
366
- switch (itemElem.type) {
367
- case MenuItem:
368
- case MenuItemLink:
369
- return {
370
- className: (0, import_clsx3.default)(childProps.className, menuItemClassName),
371
- onKeyDown: childProps.disabled ? () => void 0 : (e) => handleKeyboardFocusKeydown(e, {
372
- handleFocusBackward: handleKeyboardFocusInteraction,
373
- handleFocusForward: handleKeyboardFocusInteraction
374
- }),
375
- onFocus: chainEventHandlers(childProps.onFocus, () => {
376
- focusedItemIndex.current = index;
377
- }),
378
- id: createItemId(index, menuId.current),
379
- onBlur: chainEventHandlers(childProps.onBlur, () => {
380
- focusedItemIndex.current = null;
381
- }),
382
- onClick: childProps.disabled ? () => void 0 : chainEventHandlers(childProps.onClick, () => {
383
- onSelect?.(childProps.item);
384
- })
385
- };
386
- default:
387
- return {};
388
- }
389
- }, [handleKeyboardFocusInteraction, menuItemClassName, onSelect]);
386
+ const nextIndex = direction === "next" ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;
387
+ const shouldWrap = direction === "next" && focusedItemIndex.current === lastVirtualItemIndex || direction === "previous" && focusedItemIndex.current === 0;
388
+ if (shouldWrap) {
389
+ if (hasSearch) {
390
+ focusSearchBar();
391
+ } else {
392
+ focusMenuItem(direction === "next" ? 0 : lastVirtualItemIndex);
393
+ }
394
+ return;
395
+ }
396
+ switch (direction) {
397
+ case "next":
398
+ rowVirtualizer.scrollToIndex(nextIndex);
399
+ focusManager.focusNext();
400
+ break;
401
+ case "previous":
402
+ rowVirtualizer.scrollToIndex(nextIndex);
403
+ focusManager.focusPrevious();
404
+ break;
405
+ default:
406
+ break;
407
+ }
408
+ },
409
+ [focusManager, focusMenuItem, focusSearchBar, hasSearch, lastVirtualItemIndex, rowVirtualizer]
410
+ );
411
+ const getItemProps = (0, import_react4.useCallback)(
412
+ (itemElem, index) => {
413
+ const childProps = itemElem.props;
414
+ switch (itemElem.type) {
415
+ case MenuItem:
416
+ case MenuItemLink:
417
+ return {
418
+ className: (0, import_clsx3.default)(childProps.className, menuItemClassName),
419
+ onKeyDown: childProps.disabled ? () => void 0 : (e) => handleKeyboardFocusKeydown(e, {
420
+ handleFocusBackward: handleKeyboardFocusInteraction,
421
+ handleFocusForward: handleKeyboardFocusInteraction
422
+ }),
423
+ onFocus: chainEventHandlers(childProps.onFocus, () => {
424
+ focusedItemIndex.current = index;
425
+ }),
426
+ id: createItemId(index, menuId.current),
427
+ onBlur: chainEventHandlers(childProps.onBlur, () => {
428
+ focusedItemIndex.current = null;
429
+ }),
430
+ onClick: childProps.disabled ? () => void 0 : chainEventHandlers(childProps.onClick, () => {
431
+ onSelect?.(childProps.item);
432
+ })
433
+ };
434
+ default:
435
+ return {};
436
+ }
437
+ },
438
+ [handleKeyboardFocusInteraction, menuItemClassName, onSelect]
439
+ );
390
440
  (0, import_react4.useEffect)(() => {
391
441
  if (nextFocusValue !== null) {
392
442
  requestAnimationFrame(() => {
@@ -408,38 +458,51 @@ var ItemVirtualizer = (props) => {
408
458
  }
409
459
  }
410
460
  };
411
- const renderSearch = (0, import_react4.useMemo)(() => searchElement ? (0, import_react4.cloneElement)(searchElement, {
412
- onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
413
- handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
414
- handleFocusForward: () => focusMenuItem(0)
415
- }),
416
- ref: searchRef
417
- }) : null, [searchElement, lastVirtualItemIndex, focusMenuItem]);
418
- const renderItems = (0, import_react4.useMemo)(() => rowVirtualizer.virtualItems.map((virtualRow) => {
419
- if (!items) {
420
- return null;
421
- }
422
- const elem = items[virtualRow.index];
423
- return /* @__PURE__ */ React.createElement("div", {
424
- key: virtualRow.index,
425
- ref: elem.type !== MenuItem || elem.type !== MenuItemLink ? virtualRow.measureRef : void 0,
426
- role: "presentation",
427
- className: (0, import_clsx3.default)("VirtualMenu-item"),
428
- style: {
429
- transform: `translateY(${virtualRow.start}px)`
461
+ const renderSearch = (0, import_react4.useMemo)(
462
+ () => searchElement ? (0, import_react4.cloneElement)(searchElement, {
463
+ onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
464
+ handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
465
+ handleFocusForward: () => focusMenuItem(0)
466
+ }),
467
+ ref: searchRef
468
+ }) : null,
469
+ [searchElement, lastVirtualItemIndex, focusMenuItem]
470
+ );
471
+ const renderItems = (0, import_react4.useMemo)(
472
+ () => rowVirtualizer.virtualItems.map((virtualRow) => {
473
+ if (!items) {
474
+ return null;
430
475
  }
431
- }, (0, import_react4.cloneElement)(elem, getItemProps(elem, virtualRow.index)));
432
- }), [rowVirtualizer.virtualItems, items, getItemProps]);
433
- return /* @__PURE__ */ React.createElement(React.Fragment, null, renderSearch, /* @__PURE__ */ React.createElement(MenuItemList, {
434
- ref: parentRef,
435
- role: "presentation"
436
- }, /* @__PURE__ */ React.createElement("div", {
437
- role: "presentation",
438
- className: "VirtualMenu-item-list",
439
- style: {
440
- height: `${rowVirtualizer.totalSize}px`
441
- }
442
- }, renderItems)));
476
+ const elem = items[virtualRow.index];
477
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
478
+ ref: elem.type !== MenuItem || elem.type !== MenuItemLink ? virtualRow.measureRef : void 0,
479
+ role: "presentation",
480
+ className: (0, import_clsx3.default)("VirtualMenu-item"),
481
+ style: {
482
+ transform: `translateY(${virtualRow.start}px)`
483
+ },
484
+ children: (0, import_react4.cloneElement)(elem, getItemProps(elem, virtualRow.index))
485
+ }, virtualRow.index);
486
+ }),
487
+ [rowVirtualizer.virtualItems, items, getItemProps]
488
+ );
489
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
490
+ children: [
491
+ renderSearch,
492
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuItemList, {
493
+ ref: parentRef,
494
+ role: "presentation",
495
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
496
+ role: "presentation",
497
+ className: "VirtualMenu-item-list",
498
+ style: {
499
+ height: `${rowVirtualizer.totalSize}px`
500
+ },
501
+ children: renderItems
502
+ })
503
+ })
504
+ ]
505
+ });
443
506
  };
444
507
 
445
508
  // src/types.ts