@launchpad-ui/menu 0.12.6 → 0.12.8

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