@launchpad-ui/menu 0.10.1 → 0.10.2

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