@launchpad-ui/menu 0.16.12 → 0.16.13
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/Menu.d.ts +48 -0
- package/dist/Menu.d.ts.map +1 -0
- package/dist/MenuBase.d.ts +10 -0
- package/dist/MenuBase.d.ts.map +1 -0
- package/dist/MenuDivider.d.ts +10 -0
- package/dist/MenuDivider.d.ts.map +1 -0
- package/dist/MenuItem.d.ts +31 -0
- package/dist/MenuItem.d.ts.map +1 -0
- package/dist/MenuItemList.d.ts +6 -0
- package/dist/MenuItemList.d.ts.map +1 -0
- package/dist/MenuSearch.d.ts +13 -0
- package/dist/MenuSearch.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +383 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +408 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +2 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/Menu.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FocusManager } from '@react-aria/focus';
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
3
|
+
type ControlledMenuProps<T> = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
onSelect?: (item: T) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Menus items are rendered using react-virtual for
|
|
8
|
+
* additional rendering performance.
|
|
9
|
+
*/
|
|
10
|
+
enableVirtualization?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Class name to be applied to all MenuItem components
|
|
13
|
+
* in the menu.
|
|
14
|
+
*/
|
|
15
|
+
menuItemClassName?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Sets the width of the menu. This is especially useful when using virtual items
|
|
18
|
+
* since the width cannot be automatically set by the widest element.
|
|
19
|
+
*/
|
|
20
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
21
|
+
/**
|
|
22
|
+
* Sets the number out of elements rendered outside of the view window
|
|
23
|
+
* when using virtualization
|
|
24
|
+
*/
|
|
25
|
+
overscan?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Sets the height for each menu item when using virtualization.
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
itemHeight?: number;
|
|
31
|
+
'data-test-id'?: string;
|
|
32
|
+
};
|
|
33
|
+
type MenuProps<T extends number | string> = ControlledMenuProps<T>;
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated use `Menu` or `ListBox` from `@launchpad-ui/components` instead
|
|
36
|
+
*
|
|
37
|
+
* https://launchpad.launchdarkly.com/?path=/docs/components-collections-menu--docs
|
|
38
|
+
*/
|
|
39
|
+
declare const Menu: <T extends number | string>(props: MenuProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
type ItemVirtualizerProps<T> = Omit<ControlledMenuProps<T>, 'children'> & {
|
|
41
|
+
items: ReactElement[] | null;
|
|
42
|
+
searchElement?: ReactElement | null;
|
|
43
|
+
focusManager?: FocusManager;
|
|
44
|
+
};
|
|
45
|
+
declare const ItemVirtualizer: <T extends number | string>(props: ItemVirtualizerProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
export { Menu, ItemVirtualizer };
|
|
47
|
+
export type { MenuProps, ItemVirtualizerProps };
|
|
48
|
+
//# sourceMappingURL=Menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../src/Menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA8BpE,KAAK,mBAAmB,CAAC,CAAC,IAAI;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAEnE;;;;GAIG;AACH,QAAA,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,SAAS,CAAC,CAAC,CAAC,4CAyH3D,CAAC;AAEF,KAAK,oBAAoB,CAAC,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG;IACzE,KAAK,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC5B,CAAC;AAEF,QAAA,MAAM,eAAe,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,oBAAoB,CAAC,CAAC,CAAC,4CAiNjF,CAAC;AAEF,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef } from 'react';
|
|
2
|
+
import type { MenuProps } from './Menu';
|
|
3
|
+
type MenuBaseProps = ComponentPropsWithRef<'div'> & {
|
|
4
|
+
isVirtual?: boolean;
|
|
5
|
+
size?: MenuProps<string>['size'];
|
|
6
|
+
};
|
|
7
|
+
declare const MenuBase: import("react").ForwardRefExoticComponent<Omit<MenuBaseProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export { MenuBase };
|
|
9
|
+
export type { MenuBaseProps };
|
|
10
|
+
//# sourceMappingURL=MenuBase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuBase.d.ts","sourceRoot":"","sources":["../src/MenuBase.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAOxC,KAAK,aAAa,GAAG,qBAAqB,CAAC,KAAK,CAAC,GAAG;IACnD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,QAAA,MAAM,QAAQ,uHAcb,CAAC;AAIF,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SeparatorProps } from '@react-aria/separator';
|
|
2
|
+
import type { RefObject } from 'react';
|
|
3
|
+
type MenuDividerProps = SeparatorProps & {
|
|
4
|
+
innerRef?: RefObject<HTMLDivElement>;
|
|
5
|
+
'data-test-id'?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const MenuDivider: ({ elementType, orientation, innerRef, "data-test-id": testId, }: MenuDividerProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export { MenuDivider };
|
|
9
|
+
export type { MenuDividerProps };
|
|
10
|
+
//# sourceMappingURL=MenuDivider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuDivider.d.ts","sourceRoot":"","sources":["../src/MenuDivider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,KAAK,gBAAgB,GAAG,cAAc,GAAG;IACxC,QAAQ,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,WAAW,GAAI,iEAKlB,gBAAgB,4CAclB,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IconProps } from '@launchpad-ui/icons';
|
|
2
|
+
import type { PopoverPlacement } from '@launchpad-ui/popover';
|
|
3
|
+
import type { ComponentPropsWithRef, ElementType, JSX, PropsWithRef, ReactElement } from 'react';
|
|
4
|
+
import { Tooltip } from '@launchpad-ui/tooltip';
|
|
5
|
+
type Merge<T, U> = Omit<T, keyof U> & U;
|
|
6
|
+
type PropsWithComponent<P, T extends ElementType> = P & {
|
|
7
|
+
component?: T;
|
|
8
|
+
};
|
|
9
|
+
type PolymorphicPropsWithRef<P, T extends ElementType> = Merge<T extends keyof JSX.IntrinsicElements ? PropsWithRef<JSX.IntrinsicElements[T]> : ComponentPropsWithRef<T>, PropsWithComponent<P, T>>;
|
|
10
|
+
type MenuItemOwnProps = {
|
|
11
|
+
isHighlighted?: boolean;
|
|
12
|
+
icon?: ReactElement<IconProps>;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
nested?: boolean;
|
|
15
|
+
groupHeader?: boolean;
|
|
16
|
+
tooltip?: string | ReactElement;
|
|
17
|
+
tooltipOptions?: ComponentPropsWithRef<typeof Tooltip>;
|
|
18
|
+
tooltipPlacement?: PopoverPlacement;
|
|
19
|
+
asChild?: boolean;
|
|
20
|
+
'data-test-id'?: string;
|
|
21
|
+
};
|
|
22
|
+
declare const defaultElement = "button";
|
|
23
|
+
type MenuItemProps<P, T extends ElementType = typeof defaultElement> = PolymorphicPropsWithRef<(MenuItemOwnProps & {
|
|
24
|
+
item: P;
|
|
25
|
+
}) | (MenuItemOwnProps & {
|
|
26
|
+
item?: undefined;
|
|
27
|
+
}), T>;
|
|
28
|
+
declare const MenuItem: <P, T extends ElementType = typeof defaultElement>({ ...props }: MenuItemProps<P, T>) => import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export { MenuItem };
|
|
30
|
+
export type { MenuItemProps };
|
|
31
|
+
//# sourceMappingURL=MenuItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuItem.d.ts","sourceRoot":"","sources":["../src/MenuItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAEjG,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAShD,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;AAExC,KAAK,kBAAkB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AAE1E,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,KAAK,CAC7D,CAAC,SAAS,MAAM,GAAG,CAAC,iBAAiB,GAClC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,GACtC,qBAAqB,CAAC,CAAC,CAAC,EAC3B,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CACxB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAChC,cAAc,CAAC,EAAE,qBAAqB,CAAC,OAAO,OAAO,CAAC,CAAC;IACvD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,cAAc,WAAW,CAAC;AAEhC,KAAK,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,GAAG,OAAO,cAAc,IAAI,uBAAuB,CAC3F,CAAC,gBAAgB,GAAG;IACpB,IAAI,EAAE,CAAC,CAAC;CACP,CAAC,GACF,CAAC,gBAAgB,GAAG;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;CAChB,CAAC,EACJ,CAAC,CACD,CAAC;AAEF,QAAA,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,SAAS,WAAW,GAAG,OAAO,cAAc,EAAE,cAEjE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,4CAuErB,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef } from 'react';
|
|
2
|
+
type MenuItemListProps = Omit<ComponentPropsWithRef<'div'>, 'className'>;
|
|
3
|
+
declare const MenuItemList: import("react").ForwardRefExoticComponent<Omit<MenuItemListProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
4
|
+
export { MenuItemList };
|
|
5
|
+
export type { MenuItemListProps };
|
|
6
|
+
//# sourceMappingURL=MenuItemList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuItemList.d.ts","sourceRoot":"","sources":["../src/MenuItemList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAC;AAMnD,KAAK,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC;AAEzE,QAAA,MAAM,YAAY,2HAIhB,CAAC;AAIH,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChangeEvent } from 'react';
|
|
2
|
+
type MenuSearchProps = {
|
|
3
|
+
ariaLabel?: string;
|
|
4
|
+
value?: string;
|
|
5
|
+
id?: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
onChange?(event: ChangeEvent<HTMLInputElement>): void;
|
|
8
|
+
'data-test-id'?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const MenuSearch: import("react").ForwardRefExoticComponent<MenuSearchProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
11
|
+
export { MenuSearch };
|
|
12
|
+
export type { MenuSearchProps };
|
|
13
|
+
//# sourceMappingURL=MenuSearch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuSearch.d.ts","sourceRoot":"","sources":["../src/MenuSearch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAOzC,KAAK,eAAe,GAAG;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,UAAU,8GAyBd,CAAC;AAIH,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type { MenuProps } from './Menu';
|
|
2
|
+
export type { MenuBaseProps } from './MenuBase';
|
|
3
|
+
export type { MenuDividerProps } from './MenuDivider';
|
|
4
|
+
export type { MenuItemProps } from './MenuItem';
|
|
5
|
+
export type { MenuItemListProps } from './MenuItemList';
|
|
6
|
+
export type { MenuSearchProps } from './MenuSearch';
|
|
7
|
+
export { Menu } from './Menu';
|
|
8
|
+
export { MenuBase } from './MenuBase';
|
|
9
|
+
export { MenuDivider } from './MenuDivider';
|
|
10
|
+
export { MenuItem } from './MenuItem';
|
|
11
|
+
export { MenuItemList } from './MenuItemList';
|
|
12
|
+
export { MenuSearch } from './MenuSearch';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import './style.css';
|
|
2
|
+
import { FocusRing, useFocusManager } from "@react-aria/focus";
|
|
3
|
+
import { cx } from "classix";
|
|
4
|
+
import { Children, cloneElement, forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { useVirtual } from "react-virtual";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useSeparator } from "@react-aria/separator";
|
|
8
|
+
import { Tooltip } from "@launchpad-ui/tooltip";
|
|
9
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
10
|
+
import { TextField } from "@launchpad-ui/form";
|
|
11
|
+
const Menu$1 = "C73R2W_Menu";
|
|
12
|
+
var Menu_module_default = {
|
|
13
|
+
"has-focus": "C73R2W_has-focus",
|
|
14
|
+
"is-highlighted": "C73R2W_is-highlighted",
|
|
15
|
+
Menu: Menu$1,
|
|
16
|
+
"Menu--isVirtual": "C73R2W_Menu--isVirtual",
|
|
17
|
+
"Menu-divider": "C73R2W_Menu-divider",
|
|
18
|
+
"Menu-item": "C73R2W_Menu-item",
|
|
19
|
+
"Menu-item--header": "C73R2W_Menu-item--header",
|
|
20
|
+
"Menu-item--nested": "C73R2W_Menu-item--nested",
|
|
21
|
+
"Menu-item-icon": "C73R2W_Menu-item-icon",
|
|
22
|
+
"Menu-item-list": "C73R2W_Menu-item-list",
|
|
23
|
+
"Menu-search": "C73R2W_Menu-search",
|
|
24
|
+
"Menu-search-hidden-placeholder": "C73R2W_Menu-search-hidden-placeholder",
|
|
25
|
+
"MenuSize--lg": "C73R2W_MenuSize--lg",
|
|
26
|
+
"MenuSize--md": "C73R2W_MenuSize--md",
|
|
27
|
+
"MenuSize--sm": "C73R2W_MenuSize--sm",
|
|
28
|
+
"MenuSize--xl": "C73R2W_MenuSize--xl",
|
|
29
|
+
"VirtualMenu-item": "C73R2W_VirtualMenu-item",
|
|
30
|
+
"VirtualMenu-item-list": "C73R2W_VirtualMenu-item-list"
|
|
31
|
+
};
|
|
32
|
+
const MenuBase = /* @__PURE__ */ forwardRef(({ children, size, isVirtual,...props }, ref) => {
|
|
33
|
+
const classes = cx(Menu_module_default.Menu, isVirtual && Menu_module_default["Menu--isVirtual"], size && Menu_module_default[`MenuSize--${size}`]);
|
|
34
|
+
return /* @__PURE__ */ jsx("div", {
|
|
35
|
+
...props,
|
|
36
|
+
role: "menu",
|
|
37
|
+
className: classes,
|
|
38
|
+
ref,
|
|
39
|
+
children
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
MenuBase.displayName = "MenuBase";
|
|
43
|
+
const MenuDivider = ({ elementType = "div", orientation, innerRef, "data-test-id": testId = "menu-divider" }) => {
|
|
44
|
+
const { separatorProps } = useSeparator({
|
|
45
|
+
orientation,
|
|
46
|
+
elementType
|
|
47
|
+
});
|
|
48
|
+
return /* @__PURE__ */ jsx("div", {
|
|
49
|
+
...separatorProps,
|
|
50
|
+
"data-test-id": testId,
|
|
51
|
+
ref: innerRef,
|
|
52
|
+
className: Menu_module_default["Menu-divider"]
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
const defaultElement = "button";
|
|
56
|
+
const MenuItem = ({ ...props }) => {
|
|
57
|
+
const { component, children, isHighlighted, icon, nested, groupHeader, item, disabled, className, tooltip, role = "menuitem", tooltipPlacement, onKeyDown, tooltipOptions, asChild, "data-test-id": testId = "menu-item",...rest } = props;
|
|
58
|
+
const Component = component || (asChild ? Slot : defaultElement);
|
|
59
|
+
const renderIcon = icon && /* @__PURE__ */ cloneElement(icon, { size: "small" });
|
|
60
|
+
const renderedItem = /* @__PURE__ */ jsx(FocusRing, {
|
|
61
|
+
focusRingClass: Menu_module_default["has-focus"],
|
|
62
|
+
children: /* @__PURE__ */ jsx(Component, {
|
|
63
|
+
...rest,
|
|
64
|
+
disabled,
|
|
65
|
+
"aria-disabled": disabled ? disabled : void 0,
|
|
66
|
+
className: cx(Menu_module_default["Menu-item"], className, isHighlighted && Menu_module_default["is-highlighted"], nested && Menu_module_default["Menu-item--nested"], groupHeader && Menu_module_default["Menu-item--header"]),
|
|
67
|
+
"data-test-id": testId,
|
|
68
|
+
role,
|
|
69
|
+
onKeyDown,
|
|
70
|
+
children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [icon && /* @__PURE__ */ jsx("span", {
|
|
71
|
+
className: Menu_module_default["Menu-item-icon"],
|
|
72
|
+
children: renderIcon
|
|
73
|
+
}), children] })
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
if (tooltip) return /* @__PURE__ */ jsx(Tooltip, {
|
|
77
|
+
content: tooltip,
|
|
78
|
+
rootElementStyle: { display: "block" },
|
|
79
|
+
allowBoundaryElementOverflow: true,
|
|
80
|
+
placement: tooltipPlacement ? tooltipPlacement : "bottom",
|
|
81
|
+
...tooltipOptions || {},
|
|
82
|
+
children: renderedItem
|
|
83
|
+
});
|
|
84
|
+
return renderedItem;
|
|
85
|
+
};
|
|
86
|
+
const MenuItemList = /* @__PURE__ */ forwardRef(({ children,...rest }, ref) => /* @__PURE__ */ jsx("div", {
|
|
87
|
+
...rest,
|
|
88
|
+
ref,
|
|
89
|
+
"data-test-id": "menu-item-list",
|
|
90
|
+
className: Menu_module_default["Menu-item-list"],
|
|
91
|
+
children
|
|
92
|
+
}));
|
|
93
|
+
MenuItemList.displayName = "MenuItemList";
|
|
94
|
+
const MenuSearch = /* @__PURE__ */ forwardRef((props, ref) => {
|
|
95
|
+
const { ariaLabel, placeholder, id, "data-test-id": testId = "menu-search",...finalProps } = props;
|
|
96
|
+
return /* @__PURE__ */ jsx("div", {
|
|
97
|
+
className: Menu_module_default["Menu-search"],
|
|
98
|
+
children: /* @__PURE__ */ jsx(TextField, {
|
|
99
|
+
...finalProps,
|
|
100
|
+
ref,
|
|
101
|
+
className: Menu_module_default["Menu-search-input"],
|
|
102
|
+
tiny: true,
|
|
103
|
+
id,
|
|
104
|
+
type: "search",
|
|
105
|
+
"data-test-id": testId,
|
|
106
|
+
autoComplete: "off",
|
|
107
|
+
placeholder,
|
|
108
|
+
"aria-label": ariaLabel || "Search"
|
|
109
|
+
})
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
MenuSearch.displayName = "MenuSearch";
|
|
113
|
+
const createItemId = (index, id) => `${id}-item-${index}`;
|
|
114
|
+
const getNodeForIndex = (index, menuId) => index === null ? index : document.getElementById(createItemId(index, menuId));
|
|
115
|
+
const handleKeyboardInteractions = (event, keyHandlers) => {
|
|
116
|
+
const ops = {
|
|
117
|
+
ArrowUp: keyHandlers.handleUp,
|
|
118
|
+
ArrowDown: keyHandlers.handleDown,
|
|
119
|
+
Enter: keyHandlers.handleEnter
|
|
120
|
+
};
|
|
121
|
+
if (ops[event.key]) {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
ops[event.key]?.call(globalThis, event);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const chainEventHandlers = (...handlers) => (event) => {
|
|
127
|
+
for (const h of handlers) typeof h === "function" && h(event);
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* @deprecated use `Menu` or `ListBox` from `@launchpad-ui/components` instead
|
|
131
|
+
*
|
|
132
|
+
* https://launchpad.launchdarkly.com/?path=/docs/components-collections-menu--docs
|
|
133
|
+
*/
|
|
134
|
+
const Menu = (props) => {
|
|
135
|
+
const { children, menuItemClassName, onSelect, enableVirtualization, itemHeight, size, overscan = 1, "data-test-id": testId = "menu" } = props;
|
|
136
|
+
const focusManager = useFocusManager();
|
|
137
|
+
const handleArrowDown = useCallback(() => {
|
|
138
|
+
focusManager?.focusNext({ wrap: true });
|
|
139
|
+
}, [focusManager]);
|
|
140
|
+
const handleArrowUp = useCallback(() => {
|
|
141
|
+
focusManager?.focusPrevious({ wrap: true });
|
|
142
|
+
}, [focusManager]);
|
|
143
|
+
const reduceItems = useMemo(() => {
|
|
144
|
+
const childrenProps = Children.toArray(children);
|
|
145
|
+
if (enableVirtualization) {
|
|
146
|
+
let searchElem = null;
|
|
147
|
+
let elements = [];
|
|
148
|
+
for (const child of childrenProps) switch (child.type) {
|
|
149
|
+
case MenuSearch:
|
|
150
|
+
searchElem = child;
|
|
151
|
+
break;
|
|
152
|
+
case MenuItem:
|
|
153
|
+
case MenuDivider:
|
|
154
|
+
elements = elements.concat(child);
|
|
155
|
+
break;
|
|
156
|
+
default: break;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
items: elements,
|
|
160
|
+
searchElement: searchElem
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return childrenProps.reduce(({ items, searchElement }, child) => {
|
|
164
|
+
switch (child.type) {
|
|
165
|
+
case MenuSearch: return {
|
|
166
|
+
items,
|
|
167
|
+
searchElement: /* @__PURE__ */ cloneElement(child, { onKeyDown: (e) => handleKeyboardInteractions(e, {
|
|
168
|
+
handleDown: handleArrowDown,
|
|
169
|
+
handleUp: handleArrowUp
|
|
170
|
+
}) })
|
|
171
|
+
};
|
|
172
|
+
case MenuItem: return {
|
|
173
|
+
items: items.concat(child.props.disabled ? /* @__PURE__ */ cloneElement(child, {
|
|
174
|
+
className: cx(child.props.className, menuItemClassName),
|
|
175
|
+
onClick: () => void 0,
|
|
176
|
+
onKeyDown: () => void 0,
|
|
177
|
+
tabIndex: -1,
|
|
178
|
+
disabled: true
|
|
179
|
+
}) : /* @__PURE__ */ cloneElement(child, {
|
|
180
|
+
className: cx(child.props.className, menuItemClassName),
|
|
181
|
+
item: child.props.item ?? items.length,
|
|
182
|
+
onClick: chainEventHandlers(child.props.onClick, () => {
|
|
183
|
+
onSelect?.(child.props.item ?? items.length);
|
|
184
|
+
}),
|
|
185
|
+
onKeyDown: (e) => handleKeyboardInteractions(e, {
|
|
186
|
+
handleDown: handleArrowDown,
|
|
187
|
+
handleUp: handleArrowUp
|
|
188
|
+
})
|
|
189
|
+
})),
|
|
190
|
+
searchElement
|
|
191
|
+
};
|
|
192
|
+
case MenuDivider: return {
|
|
193
|
+
items: items.concat(child),
|
|
194
|
+
searchElement
|
|
195
|
+
};
|
|
196
|
+
default: return {
|
|
197
|
+
items,
|
|
198
|
+
searchElement
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}, {
|
|
202
|
+
items: [],
|
|
203
|
+
searchElement: null
|
|
204
|
+
});
|
|
205
|
+
}, [
|
|
206
|
+
children,
|
|
207
|
+
enableVirtualization,
|
|
208
|
+
menuItemClassName,
|
|
209
|
+
handleArrowDown,
|
|
210
|
+
handleArrowUp,
|
|
211
|
+
onSelect
|
|
212
|
+
]);
|
|
213
|
+
if (enableVirtualization) return /* @__PURE__ */ jsx(MenuBase, {
|
|
214
|
+
"data-test-id": testId,
|
|
215
|
+
isVirtual: true,
|
|
216
|
+
size,
|
|
217
|
+
children: /* @__PURE__ */ jsx(ItemVirtualizer, {
|
|
218
|
+
items: Children.toArray(reduceItems.items),
|
|
219
|
+
searchElement: reduceItems.searchElement,
|
|
220
|
+
overscan,
|
|
221
|
+
menuItemClassName,
|
|
222
|
+
onSelect,
|
|
223
|
+
itemHeight,
|
|
224
|
+
focusManager
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
return /* @__PURE__ */ jsxs(MenuBase, {
|
|
228
|
+
"data-test-id": testId,
|
|
229
|
+
size,
|
|
230
|
+
children: [reduceItems.searchElement, /* @__PURE__ */ jsx(MenuItemList, {
|
|
231
|
+
role: "presentation",
|
|
232
|
+
children: reduceItems.items
|
|
233
|
+
})]
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
const ItemVirtualizer = (props) => {
|
|
237
|
+
const { overscan, searchElement, itemHeight = 31.5, menuItemClassName, items, focusManager, onSelect } = props;
|
|
238
|
+
const menuId = useRef(`menu-ctrl-${useId()}`);
|
|
239
|
+
const focusedItemIndex = useRef(null);
|
|
240
|
+
const parentRef = useRef(null);
|
|
241
|
+
const searchRef = useRef(null);
|
|
242
|
+
const [nextFocusValue, setNextFocusValue] = useState(null);
|
|
243
|
+
const hasSearch = !!searchElement;
|
|
244
|
+
const lastVirtualItemIndex = items ? items.length - 1 : 0;
|
|
245
|
+
const rowVirtualizer = useVirtual({
|
|
246
|
+
size: items !== null ? items.length : 0,
|
|
247
|
+
parentRef,
|
|
248
|
+
estimateSize: useCallback(() => itemHeight, [itemHeight]),
|
|
249
|
+
overscan
|
|
250
|
+
});
|
|
251
|
+
const focusSearchBar = useCallback(() => {
|
|
252
|
+
rowVirtualizer.scrollToIndex(0);
|
|
253
|
+
searchRef.current?.focus?.();
|
|
254
|
+
}, [rowVirtualizer]);
|
|
255
|
+
/**
|
|
256
|
+
* Scrolls to the menu item with the index provided and
|
|
257
|
+
* then manually focuses it using a side effect in useEffect
|
|
258
|
+
*/
|
|
259
|
+
const focusMenuItem = useCallback((index) => {
|
|
260
|
+
rowVirtualizer.scrollToIndex(index);
|
|
261
|
+
setNextFocusValue(index);
|
|
262
|
+
}, [rowVirtualizer]);
|
|
263
|
+
const handleKeyboardFocusInteraction = useCallback((direction) => {
|
|
264
|
+
if (focusedItemIndex.current === null || focusedItemIndex.current === void 0) return;
|
|
265
|
+
const nextIndex = direction === "next" ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;
|
|
266
|
+
const shouldWrap = direction === "next" && focusedItemIndex.current === lastVirtualItemIndex || direction === "previous" && focusedItemIndex.current === 0;
|
|
267
|
+
if (shouldWrap) {
|
|
268
|
+
if (hasSearch) focusSearchBar();
|
|
269
|
+
else focusMenuItem(direction === "next" ? 0 : lastVirtualItemIndex);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
switch (direction) {
|
|
273
|
+
case "next":
|
|
274
|
+
rowVirtualizer.scrollToIndex(nextIndex);
|
|
275
|
+
focusManager?.focusNext();
|
|
276
|
+
break;
|
|
277
|
+
case "previous":
|
|
278
|
+
rowVirtualizer.scrollToIndex(nextIndex);
|
|
279
|
+
focusManager?.focusPrevious();
|
|
280
|
+
break;
|
|
281
|
+
default: break;
|
|
282
|
+
}
|
|
283
|
+
}, [
|
|
284
|
+
focusManager,
|
|
285
|
+
focusMenuItem,
|
|
286
|
+
focusSearchBar,
|
|
287
|
+
hasSearch,
|
|
288
|
+
lastVirtualItemIndex,
|
|
289
|
+
rowVirtualizer
|
|
290
|
+
]);
|
|
291
|
+
const getItemProps = useCallback((itemElem, index) => {
|
|
292
|
+
const childProps = itemElem.props;
|
|
293
|
+
switch (itemElem.type) {
|
|
294
|
+
case MenuItem: return {
|
|
295
|
+
className: cx(childProps.className, menuItemClassName),
|
|
296
|
+
onKeyDown: childProps.disabled ? () => void 0 : (e) => handleKeyboardFocusKeydown(e, {
|
|
297
|
+
handleFocusBackward: handleKeyboardFocusInteraction,
|
|
298
|
+
handleFocusForward: handleKeyboardFocusInteraction
|
|
299
|
+
}),
|
|
300
|
+
onFocus: chainEventHandlers(childProps.onFocus, () => {
|
|
301
|
+
focusedItemIndex.current = index;
|
|
302
|
+
}),
|
|
303
|
+
id: createItemId(index, menuId.current),
|
|
304
|
+
onBlur: chainEventHandlers(childProps.onBlur, () => {
|
|
305
|
+
focusedItemIndex.current = null;
|
|
306
|
+
}),
|
|
307
|
+
onClick: childProps.disabled ? () => void 0 : chainEventHandlers(childProps.onClick, () => {
|
|
308
|
+
onSelect?.(childProps.item);
|
|
309
|
+
})
|
|
310
|
+
};
|
|
311
|
+
default: return {};
|
|
312
|
+
}
|
|
313
|
+
}, [
|
|
314
|
+
handleKeyboardFocusInteraction,
|
|
315
|
+
menuItemClassName,
|
|
316
|
+
onSelect
|
|
317
|
+
]);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
if (nextFocusValue !== null) {
|
|
320
|
+
requestAnimationFrame(() => {
|
|
321
|
+
const element = getNodeForIndex(nextFocusValue, menuId.current);
|
|
322
|
+
element?.focus();
|
|
323
|
+
});
|
|
324
|
+
setNextFocusValue(null);
|
|
325
|
+
}
|
|
326
|
+
}, [nextFocusValue]);
|
|
327
|
+
/**
|
|
328
|
+
* Calls handleFocusForward when the user is attempting to focus forward using
|
|
329
|
+
* tab or arrow keys. Calls handleFocusBackward when the users wants to move backward.
|
|
330
|
+
*/
|
|
331
|
+
const handleKeyboardFocusKeydown = (e, callbacks) => {
|
|
332
|
+
const keyOps = [
|
|
333
|
+
"Tab",
|
|
334
|
+
"ArrowUp",
|
|
335
|
+
"ArrowDown"
|
|
336
|
+
];
|
|
337
|
+
if (keyOps.includes(e.key)) {
|
|
338
|
+
e.preventDefault();
|
|
339
|
+
e.stopPropagation();
|
|
340
|
+
if (e.key === "Tab" && e.shiftKey || e.key === "ArrowUp") callbacks.handleFocusBackward?.("previous");
|
|
341
|
+
else if (e.key === "ArrowDown" || e.key === "Tab") callbacks.handleFocusForward?.("next");
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
const renderSearch = useMemo(() => searchElement ? /* @__PURE__ */ cloneElement(searchElement, {
|
|
345
|
+
onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
|
|
346
|
+
handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
|
|
347
|
+
handleFocusForward: () => focusMenuItem(0)
|
|
348
|
+
}),
|
|
349
|
+
ref: searchRef
|
|
350
|
+
}) : null, [
|
|
351
|
+
searchElement,
|
|
352
|
+
lastVirtualItemIndex,
|
|
353
|
+
focusMenuItem
|
|
354
|
+
]);
|
|
355
|
+
const renderItems = useMemo(() => rowVirtualizer.virtualItems.map((virtualRow) => {
|
|
356
|
+
if (!items) return null;
|
|
357
|
+
const elem = items[virtualRow.index];
|
|
358
|
+
return /* @__PURE__ */ jsx("div", {
|
|
359
|
+
ref: virtualRow.measureRef,
|
|
360
|
+
role: "presentation",
|
|
361
|
+
className: Menu_module_default["VirtualMenu-item"],
|
|
362
|
+
style: { transform: `translateY(${virtualRow.start}px)` },
|
|
363
|
+
children: /* @__PURE__ */ cloneElement(elem, getItemProps(elem, virtualRow.index))
|
|
364
|
+
}, virtualRow.index);
|
|
365
|
+
}), [
|
|
366
|
+
rowVirtualizer.virtualItems,
|
|
367
|
+
items,
|
|
368
|
+
getItemProps
|
|
369
|
+
]);
|
|
370
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [renderSearch, /* @__PURE__ */ jsx(MenuItemList, {
|
|
371
|
+
ref: parentRef,
|
|
372
|
+
role: "presentation",
|
|
373
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
374
|
+
role: "presentation",
|
|
375
|
+
className: Menu_module_default["VirtualMenu-item-list"],
|
|
376
|
+
style: { height: `${rowVirtualizer.totalSize}px` },
|
|
377
|
+
children: renderItems
|
|
378
|
+
})
|
|
379
|
+
})] });
|
|
380
|
+
};
|
|
381
|
+
export { Menu, MenuBase, MenuDivider, MenuItem, MenuItemList, MenuSearch };
|
|
382
|
+
|
|
383
|
+
//# sourceMappingURL=index.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.es.js","names":["Component: ElementType","index: number","id: string","index: number | null","menuId: string","event: KeyboardEvent","keyHandlers: Partial<\n\t\tRecord<'handleUp' | 'handleDown' | 'handleEnter', (e: KeyboardEvent) => void>\n\t>","event: SyntheticEvent","props: MenuProps<T>","elements: ReactElement[]","child: ReactElement<any>","e: KeyboardEvent","props: ItemVirtualizerProps<T>","index: number","direction: 'next' | 'previous'","itemElem: ReactElement","callbacks: Record<\n\t\t\t'handleFocusForward' | 'handleFocusBackward',\n\t\t\t(direction: 'next' | 'previous') => void\n\t\t>"],"sources":["../src/styles/Menu.module.css","../src/MenuBase.tsx","../src/MenuDivider.tsx","../src/MenuItem.tsx","../src/MenuItemList.tsx","../src/MenuSearch.tsx","../src/utils.ts","../src/Menu.tsx"],"sourcesContent":[".Menu-item {\n\tbackground-color: transparent;\n\tborder-radius: var(--lp-border-radius-regular);\n\tborder-width: var(--lp-border-width-100);\n\tcolor: var(--lp-color-text-ui-primary-base);\n\tcursor: pointer;\n\tdisplay: block;\n\tfont: var(--lp-text-label-1-medium);\n\toutline: none;\n\toverflow: hidden;\n\tpadding-inline: var(--lp-spacing-300);\n\tpadding-block: var(--lp-spacing-200);\n\tposition: relative;\n\ttext-align: left;\n\ttext-decoration: none;\n\ttext-overflow: ellipsis;\n\tuser-select: none;\n\twhite-space: nowrap;\n\twidth: 100%;\n}\n\n.Menu-item-list {\n\tmax-height: 55vh;\n\tmin-width: 7.5rem;\n\toverflow: auto;\n\tpadding: var(--lp-spacing-200);\n}\n\n.Menu .Menu-item:not([disabled]):not([aria-disabled]):not(.Menu-item--header) {\n\t&:active {\n\t\ttext-decoration: none;\n\t\tbackground-color: var(--lp-color-bg-interactive-secondary-active);\n\t}\n\n\t&:hover:not(:active):not(.has-focus) {\n\t\tbackground-color: var(--lp-color-bg-interactive-secondary-hover);\n\t}\n\n\t&.has-focus {\n\t\tbackground-color: var(--lp-color-bg-interactive-secondary-hover);\n\t\tbox-shadow: inset 0 0 0 2px var(--lp-color-shadow-interactive-focus);\n\t}\n}\n\n.Menu {\n\tbackground: var(--lp-color-bg-overlay-secondary);\n\tborder-radius: var(--lp-border-radius-medium);\n\tfont-family: var(--lp-font-family-base);\n\n\t&:focus {\n\t\toutline: none;\n\t}\n\n\t/* Override our link styles for link component */\n\t& a.Menu-item {\n\t\t&:focus:not(:hover):not(.has-focus) {\n\t\t\ttext-decoration: none;\n\t\t\tbox-shadow: none;\n\t\t}\n\n\t\t/* Ensures that links that are disabled don't show active styles */\n\t\t&:active[disabled],\n\t\t&:active[aria-disabled] {\n\t\t\ttext-decoration: none;\n\t\t\tcolor: var(--lp-color-text-interactive-disabled);\n\t\t}\n\t}\n}\n\n.Menu-item--nested {\n\tpadding-left: 2.5rem;\n}\n\n.Menu-item--header {\n\tfont: var(--lp-text-small-1-regular);\n\tcolor: var(--lp-color-text-ui-tertiary);\n}\n\n.Menu-item-icon {\n\tmargin-right: var(--lp-spacing-300);\n}\n\n.Menu-item :global(.Gravatar) {\n\tmargin-right: 0.3125rem;\n}\n\n.Menu-item.is-highlighted {\n\tbackground-color: var(--lp-color-bg-interactive-secondary-hover);\n}\n\n.Menu-item[aria-disabled],\n.Menu-item[disabled] {\n\tcolor: var(--lp-color-text-interactive-disabled);\n\tcursor: not-allowed;\n}\n\n.Menu-divider {\n\tborder-top: 1px solid var(--lp-color-border-ui-secondary);\n\tmargin: var(--lp-spacing-200) 0;\n}\n\n.Menu-search {\n\tborder-bottom: 1px solid var(--lp-color-border-ui-secondary);\n\tpadding: var(--lp-spacing-300);\n}\n\n[class*='_Popover-content'] .Menu-search {\n\twidth: 100%;\n\n\t/* Removing anything that could give it some height */\n\t& .Menu-search-hidden-placeholder {\n\t\tpadding-top: 0;\n\t\tpadding-bottom: 0;\n\t\theight: 0;\n\t\tborder: none;\n\t\tvisibility: hidden;\n\t}\n}\n\n.Menu--isVirtual {\n\tdisplay: flex;\n\talign-items: stretch;\n\tflex-direction: column;\n}\n\n.MenuSize--xl {\n\twidth: 20rem;\n}\n\n.MenuSize--lg {\n\twidth: 15rem;\n}\n\n.MenuSize--md {\n\twidth: 10rem;\n}\n\n.MenuSize--sm {\n\twidth: 7.5rem;\n}\n\n.VirtualMenu-item-list {\n\twidth: 100%;\n\tposition: relative;\n}\n\n.VirtualMenu-item {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n}\n","import type { ComponentPropsWithRef } from 'react';\nimport type { MenuProps } from './Menu';\n\nimport { cx } from 'classix';\nimport { forwardRef } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuBaseProps = ComponentPropsWithRef<'div'> & {\n\tisVirtual?: boolean;\n\tsize?: MenuProps<string>['size'];\n};\n\nconst MenuBase = forwardRef<HTMLDivElement, MenuBaseProps>(\n\t({ children, size, isVirtual, ...props }, ref) => {\n\t\tconst classes = cx(\n\t\t\tstyles.Menu,\n\t\t\tisVirtual && styles['Menu--isVirtual'],\n\t\t\tsize && styles[`MenuSize--${size}`],\n\t\t);\n\n\t\treturn (\n\t\t\t<div {...props} role=\"menu\" className={classes} ref={ref}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t);\n\t},\n);\n\nMenuBase.displayName = 'MenuBase';\n\nexport { MenuBase };\nexport type { MenuBaseProps };\n","import type { SeparatorProps } from '@react-aria/separator';\nimport type { RefObject } from 'react';\n\nimport { useSeparator } from '@react-aria/separator';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuDividerProps = SeparatorProps & {\n\tinnerRef?: RefObject<HTMLDivElement>;\n\t'data-test-id'?: string;\n};\n\nconst MenuDivider = ({\n\telementType = 'div',\n\torientation,\n\tinnerRef,\n\t'data-test-id': testId = 'menu-divider',\n}: MenuDividerProps) => {\n\tconst { separatorProps } = useSeparator({\n\t\torientation,\n\t\telementType,\n\t});\n\n\treturn (\n\t\t<div\n\t\t\t{...separatorProps}\n\t\t\tdata-test-id={testId}\n\t\t\tref={innerRef}\n\t\t\tclassName={styles['Menu-divider']}\n\t\t/>\n\t);\n};\n\nexport { MenuDivider };\nexport type { MenuDividerProps };\n","import type { IconProps } from '@launchpad-ui/icons';\nimport type { PopoverPlacement } from '@launchpad-ui/popover';\nimport type { ComponentPropsWithRef, ElementType, JSX, PropsWithRef, ReactElement } from 'react';\n\nimport { Tooltip } from '@launchpad-ui/tooltip';\nimport { Slot } from '@radix-ui/react-slot';\nimport { FocusRing } from '@react-aria/focus';\nimport { cx } from 'classix';\nimport { cloneElement } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\n// Merge two types and get rid of overlapping definitions\ntype Merge<T, U> = Omit<T, keyof U> & U;\n\ntype PropsWithComponent<P, T extends ElementType> = P & { component?: T };\n\ntype PolymorphicPropsWithRef<P, T extends ElementType> = Merge<\n\tT extends keyof JSX.IntrinsicElements\n\t\t? PropsWithRef<JSX.IntrinsicElements[T]>\n\t\t: ComponentPropsWithRef<T>,\n\tPropsWithComponent<P, T>\n>;\n\ntype MenuItemOwnProps = {\n\tisHighlighted?: boolean;\n\ticon?: ReactElement<IconProps>;\n\tdisabled?: boolean;\n\tnested?: boolean;\n\tgroupHeader?: boolean;\n\ttooltip?: string | ReactElement;\n\ttooltipOptions?: ComponentPropsWithRef<typeof Tooltip>;\n\ttooltipPlacement?: PopoverPlacement;\n\tasChild?: boolean;\n\t'data-test-id'?: string;\n};\n\nconst defaultElement = 'button';\n\ntype MenuItemProps<P, T extends ElementType = typeof defaultElement> = PolymorphicPropsWithRef<\n\t| (MenuItemOwnProps & {\n\t\t\titem: P; // Infer the type if it is included\n\t })\n\t| (MenuItemOwnProps & {\n\t\t\titem?: undefined;\n\t }),\n\tT\n>;\n\nconst MenuItem = <P, T extends ElementType = typeof defaultElement>({\n\t...props\n}: MenuItemProps<P, T>) => {\n\tconst {\n\t\t// TODO: remove component prop once we migrate over to asChild format\n\t\tcomponent,\n\t\tchildren,\n\t\tisHighlighted,\n\t\ticon,\n\t\tnested,\n\t\tgroupHeader,\n\t\t// biome-ignore lint/correctness/noUnusedVariables: ignore\n\t\titem,\n\t\tdisabled,\n\t\tclassName,\n\t\ttooltip,\n\t\trole = 'menuitem',\n\t\ttooltipPlacement,\n\t\tonKeyDown,\n\t\ttooltipOptions,\n\t\tasChild,\n\t\t'data-test-id': testId = 'menu-item',\n\t\t...rest\n\t} = props;\n\n\tconst Component: ElementType = component || (asChild ? Slot : defaultElement);\n\n\tconst renderIcon = icon && cloneElement(icon, { size: 'small' });\n\n\tconst renderedItem = (\n\t\t<FocusRing focusRingClass={styles['has-focus']}>\n\t\t\t<Component\n\t\t\t\t{...rest}\n\t\t\t\tdisabled={disabled}\n\t\t\t\taria-disabled={disabled ? disabled : undefined}\n\t\t\t\tclassName={cx(\n\t\t\t\t\tstyles['Menu-item'],\n\t\t\t\t\tclassName,\n\t\t\t\t\tisHighlighted && styles['is-highlighted'],\n\t\t\t\t\tnested && styles['Menu-item--nested'],\n\t\t\t\t\tgroupHeader && styles['Menu-item--header'],\n\t\t\t\t)}\n\t\t\t\tdata-test-id={testId}\n\t\t\t\trole={role}\n\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t>\n\t\t\t\t{asChild ? (\n\t\t\t\t\tchildren\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{icon && <span className={styles['Menu-item-icon']}>{renderIcon}</span>}\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</Component>\n\t\t</FocusRing>\n\t);\n\n\tif (tooltip) {\n\t\treturn (\n\t\t\t<Tooltip\n\t\t\t\tcontent={tooltip}\n\t\t\t\trootElementStyle={{ display: 'block' }}\n\t\t\t\tallowBoundaryElementOverflow\n\t\t\t\tplacement={tooltipPlacement ? tooltipPlacement : 'bottom'}\n\t\t\t\t{...(tooltipOptions || {})}\n\t\t\t>\n\t\t\t\t{renderedItem}\n\t\t\t</Tooltip>\n\t\t);\n\t}\n\n\treturn renderedItem;\n};\n\nexport { MenuItem };\nexport type { MenuItemProps };\n","import type { ComponentPropsWithRef } from 'react';\n\nimport { forwardRef } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuItemListProps = Omit<ComponentPropsWithRef<'div'>, 'className'>;\n\nconst MenuItemList = forwardRef<HTMLDivElement, MenuItemListProps>(({ children, ...rest }, ref) => (\n\t<div {...rest} ref={ref} data-test-id=\"menu-item-list\" className={styles['Menu-item-list']}>\n\t\t{children}\n\t</div>\n));\n\nMenuItemList.displayName = 'MenuItemList';\n\nexport { MenuItemList };\nexport type { MenuItemListProps };\n","import type { ChangeEvent } from 'react';\n\nimport { TextField } from '@launchpad-ui/form';\nimport { forwardRef } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuSearchProps = {\n\tariaLabel?: string;\n\tvalue?: string;\n\tid?: string;\n\tplaceholder?: string;\n\tonChange?(event: ChangeEvent<HTMLInputElement>): void;\n\t'data-test-id'?: string;\n};\n\nconst MenuSearch = forwardRef<HTMLInputElement, MenuSearchProps>((props, ref) => {\n\tconst {\n\t\tariaLabel,\n\t\tplaceholder,\n\t\tid,\n\t\t'data-test-id': testId = 'menu-search',\n\t\t...finalProps\n\t} = props;\n\n\treturn (\n\t\t<div className={styles['Menu-search']}>\n\t\t\t<TextField\n\t\t\t\t{...finalProps}\n\t\t\t\tref={ref}\n\t\t\t\tclassName={styles['Menu-search-input']}\n\t\t\t\ttiny\n\t\t\t\tid={id}\n\t\t\t\ttype=\"search\"\n\t\t\t\tdata-test-id={testId}\n\t\t\t\tautoComplete=\"off\"\n\t\t\t\tplaceholder={placeholder}\n\t\t\t\taria-label={ariaLabel || 'Search'}\n\t\t\t/>\n\t\t</div>\n\t);\n});\n\nMenuSearch.displayName = 'MenuSearch';\n\nexport { MenuSearch };\nexport type { MenuSearchProps };\n","import type { EventHandler, KeyboardEvent, SyntheticEvent } from 'react';\n\nconst createItemId = (index: number, id: string) => `${id}-item-${index}`;\n\nconst getNodeForIndex = (index: number | null, menuId: string) =>\n\tindex === null ? index : document.getElementById(createItemId(index, menuId));\n\nconst handleKeyboardInteractions = (\n\tevent: KeyboardEvent,\n\tkeyHandlers: Partial<\n\t\tRecord<'handleUp' | 'handleDown' | 'handleEnter', (e: KeyboardEvent) => void>\n\t>,\n) => {\n\tconst ops = {\n\t\tArrowUp: keyHandlers.handleUp,\n\t\tArrowDown: keyHandlers.handleDown,\n\t\tEnter: keyHandlers.handleEnter,\n\t\t// biome-ignore lint/suspicious/noConfusingVoidType: ignore\n\t} as Record<string, (e: KeyboardEvent) => void | undefined>;\n\n\tif (ops[event.key]) {\n\t\tevent.preventDefault();\n\t\tops[event.key]?.call(globalThis, event);\n\t}\n};\n\nconst chainEventHandlers =\n\t(...handlers: (EventHandler<SyntheticEvent> | undefined)[]) =>\n\t(event: SyntheticEvent) => {\n\t\tfor (const h of handlers) {\n\t\t\ttypeof h === 'function' && h(event);\n\t\t}\n\t};\n\nexport { createItemId, getNodeForIndex, handleKeyboardInteractions, chainEventHandlers };\n","import type { FocusManager } from '@react-aria/focus';\nimport type { KeyboardEvent, ReactElement, ReactNode } from 'react';\nimport type { MenuItemProps } from './MenuItem';\n\nimport { useFocusManager } from '@react-aria/focus';\nimport { cx } from 'classix';\nimport {\n\tChildren,\n\tcloneElement,\n\tuseCallback,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useVirtual } from 'react-virtual';\n\nimport { MenuBase } from './MenuBase';\nimport { MenuDivider } from './MenuDivider';\nimport { MenuItem } from './MenuItem';\nimport { MenuItemList } from './MenuItemList';\nimport { MenuSearch } from './MenuSearch';\nimport styles from './styles/Menu.module.css';\nimport {\n\tchainEventHandlers,\n\tcreateItemId,\n\tgetNodeForIndex,\n\thandleKeyboardInteractions,\n} from './utils';\n\ntype ControlledMenuProps<T> = {\n\tchildren: ReactNode;\n\tonSelect?: (item: T) => void;\n\t/**\n\t * Menus items are rendered using react-virtual for\n\t * additional rendering performance.\n\t */\n\tenableVirtualization?: boolean;\n\t/**\n\t * Class name to be applied to all MenuItem components\n\t * in the menu.\n\t */\n\tmenuItemClassName?: string;\n\t/**\n\t * Sets the width of the menu. This is especially useful when using virtual items\n\t * since the width cannot be automatically set by the widest element.\n\t */\n\tsize?: 'sm' | 'md' | 'lg' | 'xl';\n\t/**\n\t * Sets the number out of elements rendered outside of the view window\n\t * when using virtualization\n\t */\n\toverscan?: number;\n\t/**\n\t * Sets the height for each menu item when using virtualization.\n\t *\n\t */\n\titemHeight?: number;\n\t'data-test-id'?: string;\n};\n\ntype MenuProps<T extends number | string> = ControlledMenuProps<T>;\n\n/**\n * @deprecated use `Menu` or `ListBox` from `@launchpad-ui/components` instead\n *\n * https://launchpad.launchdarkly.com/?path=/docs/components-collections-menu--docs\n */\nconst Menu = <T extends number | string>(props: MenuProps<T>) => {\n\tconst {\n\t\tchildren,\n\t\tmenuItemClassName,\n\t\tonSelect,\n\t\tenableVirtualization,\n\t\titemHeight,\n\t\tsize,\n\t\toverscan = 1,\n\t\t'data-test-id': testId = 'menu',\n\t} = props;\n\n\tconst focusManager = useFocusManager();\n\n\tconst handleArrowDown = useCallback(() => {\n\t\tfocusManager?.focusNext({ wrap: true });\n\t}, [focusManager]);\n\n\tconst handleArrowUp = useCallback(() => {\n\t\tfocusManager?.focusPrevious({ wrap: true });\n\t}, [focusManager]);\n\n\tconst reduceItems = useMemo(() => {\n\t\tconst childrenProps = Children.toArray(children);\n\t\tif (enableVirtualization) {\n\t\t\t// the virtualized menu has its own handlers and props\n\t\t\tlet searchElem = null;\n\t\t\tlet elements: ReactElement[] = [];\n\t\t\tfor (const child of childrenProps as ReactElement[]) {\n\t\t\t\tswitch (child.type) {\n\t\t\t\t\tcase MenuSearch:\n\t\t\t\t\t\tsearchElem = child;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MenuItem:\n\t\t\t\t\tcase MenuDivider:\n\t\t\t\t\t\telements = elements.concat(child);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { items: elements, searchElement: searchElem };\n\t\t}\n\n\t\treturn (childrenProps as ReactElement[]).reduce(\n\t\t\t(\n\t\t\t\t{ items, searchElement }: { items: ReactElement[]; searchElement: null | ReactElement },\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: ignore\n\t\t\t\tchild: ReactElement<any>,\n\t\t\t) => {\n\t\t\t\tswitch (child.type) {\n\t\t\t\t\tcase MenuSearch:\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\titems,\n\t\t\t\t\t\t\tsearchElement: cloneElement(child, {\n\t\t\t\t\t\t\t\tonKeyDown: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\t\t\thandleKeyboardInteractions(e, {\n\t\t\t\t\t\t\t\t\t\thandleDown: handleArrowDown,\n\t\t\t\t\t\t\t\t\t\thandleUp: handleArrowUp,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t};\n\t\t\t\t\tcase MenuItem:\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\titems: items.concat(\n\t\t\t\t\t\t\t\tchild.props.disabled\n\t\t\t\t\t\t\t\t\t? cloneElement(child, {\n\t\t\t\t\t\t\t\t\t\t\tclassName: cx(child.props.className, menuItemClassName),\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => undefined,\n\t\t\t\t\t\t\t\t\t\t\tonKeyDown: () => undefined,\n\t\t\t\t\t\t\t\t\t\t\ttabIndex: -1,\n\t\t\t\t\t\t\t\t\t\t\tdisabled: true,\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t: cloneElement(child, {\n\t\t\t\t\t\t\t\t\t\t\tclassName: cx(child.props.className, menuItemClassName),\n\t\t\t\t\t\t\t\t\t\t\titem: child.props.item ?? items.length,\n\t\t\t\t\t\t\t\t\t\t\t// set focus on the first menu item if there is no search input, and set in the tab order\n\t\t\t\t\t\t\t\t\t\t\tonClick: chainEventHandlers(child.props.onClick, () => {\n\t\t\t\t\t\t\t\t\t\t\t\tonSelect?.(child.props.item ?? items.length);\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\tonKeyDown: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\t\t\t\t\t\thandleKeyboardInteractions(e, {\n\t\t\t\t\t\t\t\t\t\t\t\t\thandleDown: handleArrowDown,\n\t\t\t\t\t\t\t\t\t\t\t\t\thandleUp: handleArrowUp,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tsearchElement,\n\t\t\t\t\t\t};\n\t\t\t\t\tcase MenuDivider:\n\t\t\t\t\t\treturn { items: items.concat(child), searchElement };\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn { items, searchElement };\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ items: [], searchElement: null },\n\t\t);\n\t}, [children, enableVirtualization, menuItemClassName, handleArrowDown, handleArrowUp, onSelect]);\n\n\tif (enableVirtualization) {\n\t\treturn (\n\t\t\t<MenuBase data-test-id={testId} isVirtual size={size}>\n\t\t\t\t<ItemVirtualizer<T>\n\t\t\t\t\titems={Children.toArray(reduceItems.items) as ReactElement[]}\n\t\t\t\t\tsearchElement={reduceItems.searchElement}\n\t\t\t\t\toverscan={overscan}\n\t\t\t\t\tmenuItemClassName={menuItemClassName}\n\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\titemHeight={itemHeight}\n\t\t\t\t\tfocusManager={focusManager}\n\t\t\t\t/>\n\t\t\t</MenuBase>\n\t\t);\n\t}\n\n\treturn (\n\t\t<MenuBase data-test-id={testId} size={size}>\n\t\t\t{reduceItems.searchElement}\n\t\t\t<MenuItemList role=\"presentation\">{reduceItems.items}</MenuItemList>\n\t\t</MenuBase>\n\t);\n};\n\ntype ItemVirtualizerProps<T> = Omit<ControlledMenuProps<T>, 'children'> & {\n\titems: ReactElement[] | null;\n\tsearchElement?: ReactElement | null;\n\tfocusManager?: FocusManager;\n};\n\nconst ItemVirtualizer = <T extends number | string>(props: ItemVirtualizerProps<T>) => {\n\tconst {\n\t\toverscan,\n\t\tsearchElement,\n\t\titemHeight = 31.5,\n\t\tmenuItemClassName,\n\t\titems,\n\t\tfocusManager,\n\t\tonSelect,\n\t} = props;\n\n\tconst menuId = useRef(`menu-ctrl-${useId()}`);\n\n\tconst focusedItemIndex = useRef<number | null>(null);\n\tconst parentRef = useRef<HTMLDivElement | null>(null);\n\tconst searchRef = useRef<HTMLInputElement | null>(null);\n\n\tconst [nextFocusValue, setNextFocusValue] = useState<number | null>(null);\n\n\tconst hasSearch = !!searchElement;\n\n\tconst lastVirtualItemIndex = items ? items.length - 1 : 0;\n\n\tconst rowVirtualizer = useVirtual({\n\t\tsize: items !== null ? items.length : 0,\n\t\tparentRef,\n\t\testimateSize: useCallback(() => itemHeight, [itemHeight]),\n\t\toverscan,\n\t});\n\n\tconst focusSearchBar = useCallback(() => {\n\t\trowVirtualizer.scrollToIndex(0);\n\t\tsearchRef.current?.focus?.();\n\t}, [rowVirtualizer]);\n\n\t/**\n\t * Scrolls to the menu item with the index provided and\n\t * then manually focuses it using a side effect in useEffect\n\t */\n\tconst focusMenuItem = useCallback(\n\t\t(index: number) => {\n\t\t\trowVirtualizer.scrollToIndex(index);\n\t\t\tsetNextFocusValue(index);\n\t\t},\n\t\t[rowVirtualizer],\n\t);\n\n\tconst handleKeyboardFocusInteraction = useCallback(\n\t\t(direction: 'next' | 'previous') => {\n\t\t\tif (focusedItemIndex.current === null || focusedItemIndex.current === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst nextIndex =\n\t\t\t\tdirection === 'next' ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;\n\t\t\tconst shouldWrap =\n\t\t\t\t(direction === 'next' && focusedItemIndex.current === lastVirtualItemIndex) ||\n\t\t\t\t(direction === 'previous' && focusedItemIndex.current === 0);\n\t\t\tif (shouldWrap) {\n\t\t\t\t// we are at the end of the list so we will\n\t\t\t\t// scroll back to the beginning of the list\n\t\t\t\tif (hasSearch) {\n\t\t\t\t\tfocusSearchBar();\n\t\t\t\t} else {\n\t\t\t\t\t// if at end, wrap to beginning, else focus last item\n\t\t\t\t\tfocusMenuItem(direction === 'next' ? 0 : lastVirtualItemIndex);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tswitch (direction) {\n\t\t\t\tcase 'next':\n\t\t\t\t\trowVirtualizer.scrollToIndex(nextIndex);\n\t\t\t\t\tfocusManager?.focusNext();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'previous':\n\t\t\t\t\trowVirtualizer.scrollToIndex(nextIndex);\n\t\t\t\t\tfocusManager?.focusPrevious();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t[focusManager, focusMenuItem, focusSearchBar, hasSearch, lastVirtualItemIndex, rowVirtualizer],\n\t);\n\n\tconst getItemProps = useCallback(\n\t\t(itemElem: ReactElement, index: number) => {\n\t\t\tconst childProps = itemElem.props as MenuItemProps<T>;\n\t\t\tswitch (itemElem.type) {\n\t\t\t\tcase MenuItem:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tclassName: cx(childProps.className, menuItemClassName),\n\t\t\t\t\t\t// set focus on the first menu item if there is no search input, and set in the tab order\n\t\t\t\t\t\tonKeyDown: childProps.disabled\n\t\t\t\t\t\t\t? () => undefined\n\t\t\t\t\t\t\t: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\t\t\thandleKeyboardFocusKeydown(e, {\n\t\t\t\t\t\t\t\t\t\thandleFocusBackward: handleKeyboardFocusInteraction,\n\t\t\t\t\t\t\t\t\t\thandleFocusForward: handleKeyboardFocusInteraction,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tonFocus: chainEventHandlers(childProps.onFocus, () => {\n\t\t\t\t\t\t\tfocusedItemIndex.current = index;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tid: createItemId(index, menuId.current),\n\t\t\t\t\t\tonBlur: chainEventHandlers(childProps.onBlur, () => {\n\t\t\t\t\t\t\tfocusedItemIndex.current = null;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tonClick: childProps.disabled\n\t\t\t\t\t\t\t? () => undefined\n\t\t\t\t\t\t\t: chainEventHandlers(childProps.onClick, () => {\n\t\t\t\t\t\t\t\t\tonSelect?.(childProps.item as T);\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t} as MenuItemProps<T>;\n\t\t\t\tdefault:\n\t\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\t\t[handleKeyboardFocusInteraction, menuItemClassName, onSelect],\n\t);\n\n\tuseEffect(() => {\n\t\tif (nextFocusValue !== null) {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tconst element = getNodeForIndex(nextFocusValue, menuId.current);\n\t\t\t\telement?.focus();\n\t\t\t});\n\t\t\tsetNextFocusValue(null);\n\t\t}\n\t}, [nextFocusValue]);\n\n\t/**\n\t * Calls handleFocusForward when the user is attempting to focus forward using\n\t * tab or arrow keys. Calls handleFocusBackward when the users wants to move backward.\n\t */\n\tconst handleKeyboardFocusKeydown = (\n\t\te: KeyboardEvent,\n\t\tcallbacks: Record<\n\t\t\t'handleFocusForward' | 'handleFocusBackward',\n\t\t\t(direction: 'next' | 'previous') => void\n\t\t>,\n\t) => {\n\t\tconst keyOps = ['Tab', 'ArrowUp', 'ArrowDown'];\n\t\tif (keyOps.includes(e.key)) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tif ((e.key === 'Tab' && e.shiftKey) || e.key === 'ArrowUp') {\n\t\t\t\tcallbacks.handleFocusBackward?.('previous');\n\t\t\t} else if (e.key === 'ArrowDown' || e.key === 'Tab') {\n\t\t\t\tcallbacks.handleFocusForward?.('next');\n\t\t\t}\n\t\t}\n\t};\n\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: ignore\n\tconst renderSearch = useMemo(\n\t\t() =>\n\t\t\tsearchElement\n\t\t\t\t? // biome-ignore lint/suspicious/noExplicitAny: ignore\n\t\t\t\t\tcloneElement(searchElement as ReactElement<any>, {\n\t\t\t\t\t\tonKeyDown: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\thandleKeyboardFocusKeydown(e, {\n\t\t\t\t\t\t\t\thandleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),\n\t\t\t\t\t\t\t\thandleFocusForward: () => focusMenuItem(0),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tref: searchRef,\n\t\t\t\t\t})\n\t\t\t\t: null,\n\t\t[searchElement, lastVirtualItemIndex, focusMenuItem],\n\t);\n\n\tconst renderItems = useMemo(\n\t\t() =>\n\t\t\trowVirtualizer.virtualItems.map((virtualRow) => {\n\t\t\t\tif (!items) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst elem = items[virtualRow.index];\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={virtualRow.index}\n\t\t\t\t\t\tref={virtualRow.measureRef}\n\t\t\t\t\t\trole=\"presentation\"\n\t\t\t\t\t\tclassName={styles['VirtualMenu-item']}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\ttransform: `translateY(${virtualRow.start}px)`,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{cloneElement(elem, getItemProps(elem, virtualRow.index))}\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t}),\n\t\t[rowVirtualizer.virtualItems, items, getItemProps],\n\t);\n\n\treturn (\n\t\t<>\n\t\t\t{renderSearch}\n\t\t\t<MenuItemList ref={parentRef} role=\"presentation\">\n\t\t\t\t<div\n\t\t\t\t\trole=\"presentation\"\n\t\t\t\t\tclassName={styles['VirtualMenu-item-list']}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\theight: `${rowVirtualizer.totalSize}px`,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{renderItems}\n\t\t\t\t</div>\n\t\t\t</MenuItemList>\n\t\t</>\n\t);\n};\n\nexport { Menu, ItemVirtualizer };\nexport type { MenuProps, ItemVirtualizerProps };\n"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
require('./style.css');
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
const __react_aria_focus = __toESM(require("@react-aria/focus"));
|
|
23
|
+
const classix = __toESM(require("classix"));
|
|
24
|
+
const react = __toESM(require("react"));
|
|
25
|
+
const react_virtual = __toESM(require("react-virtual"));
|
|
26
|
+
const react_jsx_runtime = __toESM(require("react/jsx-runtime"));
|
|
27
|
+
const __react_aria_separator = __toESM(require("@react-aria/separator"));
|
|
28
|
+
const __launchpad_ui_tooltip = __toESM(require("@launchpad-ui/tooltip"));
|
|
29
|
+
const __radix_ui_react_slot = __toESM(require("@radix-ui/react-slot"));
|
|
30
|
+
const __launchpad_ui_form = __toESM(require("@launchpad-ui/form"));
|
|
31
|
+
const Menu$1 = "C73R2W_Menu";
|
|
32
|
+
var Menu_module_default = {
|
|
33
|
+
"has-focus": "C73R2W_has-focus",
|
|
34
|
+
"is-highlighted": "C73R2W_is-highlighted",
|
|
35
|
+
Menu: Menu$1,
|
|
36
|
+
"Menu--isVirtual": "C73R2W_Menu--isVirtual",
|
|
37
|
+
"Menu-divider": "C73R2W_Menu-divider",
|
|
38
|
+
"Menu-item": "C73R2W_Menu-item",
|
|
39
|
+
"Menu-item--header": "C73R2W_Menu-item--header",
|
|
40
|
+
"Menu-item--nested": "C73R2W_Menu-item--nested",
|
|
41
|
+
"Menu-item-icon": "C73R2W_Menu-item-icon",
|
|
42
|
+
"Menu-item-list": "C73R2W_Menu-item-list",
|
|
43
|
+
"Menu-search": "C73R2W_Menu-search",
|
|
44
|
+
"Menu-search-hidden-placeholder": "C73R2W_Menu-search-hidden-placeholder",
|
|
45
|
+
"MenuSize--lg": "C73R2W_MenuSize--lg",
|
|
46
|
+
"MenuSize--md": "C73R2W_MenuSize--md",
|
|
47
|
+
"MenuSize--sm": "C73R2W_MenuSize--sm",
|
|
48
|
+
"MenuSize--xl": "C73R2W_MenuSize--xl",
|
|
49
|
+
"VirtualMenu-item": "C73R2W_VirtualMenu-item",
|
|
50
|
+
"VirtualMenu-item-list": "C73R2W_VirtualMenu-item-list"
|
|
51
|
+
};
|
|
52
|
+
const MenuBase = /* @__PURE__ */ (0, react.forwardRef)(({ children, size, isVirtual,...props }, ref) => {
|
|
53
|
+
const classes = (0, classix.cx)(Menu_module_default.Menu, isVirtual && Menu_module_default["Menu--isVirtual"], size && Menu_module_default[`MenuSize--${size}`]);
|
|
54
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55
|
+
...props,
|
|
56
|
+
role: "menu",
|
|
57
|
+
className: classes,
|
|
58
|
+
ref,
|
|
59
|
+
children
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
MenuBase.displayName = "MenuBase";
|
|
63
|
+
const MenuDivider = ({ elementType = "div", orientation, innerRef, "data-test-id": testId = "menu-divider" }) => {
|
|
64
|
+
const { separatorProps } = (0, __react_aria_separator.useSeparator)({
|
|
65
|
+
orientation,
|
|
66
|
+
elementType
|
|
67
|
+
});
|
|
68
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
69
|
+
...separatorProps,
|
|
70
|
+
"data-test-id": testId,
|
|
71
|
+
ref: innerRef,
|
|
72
|
+
className: Menu_module_default["Menu-divider"]
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
const defaultElement = "button";
|
|
76
|
+
const MenuItem = ({ ...props }) => {
|
|
77
|
+
const { component, children, isHighlighted, icon, nested, groupHeader, item, disabled, className, tooltip, role = "menuitem", tooltipPlacement, onKeyDown, tooltipOptions, asChild, "data-test-id": testId = "menu-item",...rest } = props;
|
|
78
|
+
const Component = component || (asChild ? __radix_ui_react_slot.Slot : defaultElement);
|
|
79
|
+
const renderIcon = icon && /* @__PURE__ */ (0, react.cloneElement)(icon, { size: "small" });
|
|
80
|
+
const renderedItem = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_aria_focus.FocusRing, {
|
|
81
|
+
focusRingClass: Menu_module_default["has-focus"],
|
|
82
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
83
|
+
...rest,
|
|
84
|
+
disabled,
|
|
85
|
+
"aria-disabled": disabled ? disabled : void 0,
|
|
86
|
+
className: (0, classix.cx)(Menu_module_default["Menu-item"], className, isHighlighted && Menu_module_default["is-highlighted"], nested && Menu_module_default["Menu-item--nested"], groupHeader && Menu_module_default["Menu-item--header"]),
|
|
87
|
+
"data-test-id": testId,
|
|
88
|
+
role,
|
|
89
|
+
onKeyDown,
|
|
90
|
+
children: asChild ? children : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
91
|
+
className: Menu_module_default["Menu-item-icon"],
|
|
92
|
+
children: renderIcon
|
|
93
|
+
}), children] })
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
if (tooltip) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__launchpad_ui_tooltip.Tooltip, {
|
|
97
|
+
content: tooltip,
|
|
98
|
+
rootElementStyle: { display: "block" },
|
|
99
|
+
allowBoundaryElementOverflow: true,
|
|
100
|
+
placement: tooltipPlacement ? tooltipPlacement : "bottom",
|
|
101
|
+
...tooltipOptions || {},
|
|
102
|
+
children: renderedItem
|
|
103
|
+
});
|
|
104
|
+
return renderedItem;
|
|
105
|
+
};
|
|
106
|
+
const MenuItemList = /* @__PURE__ */ (0, react.forwardRef)(({ children,...rest }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
107
|
+
...rest,
|
|
108
|
+
ref,
|
|
109
|
+
"data-test-id": "menu-item-list",
|
|
110
|
+
className: Menu_module_default["Menu-item-list"],
|
|
111
|
+
children
|
|
112
|
+
}));
|
|
113
|
+
MenuItemList.displayName = "MenuItemList";
|
|
114
|
+
const MenuSearch = /* @__PURE__ */ (0, react.forwardRef)((props, ref) => {
|
|
115
|
+
const { ariaLabel, placeholder, id, "data-test-id": testId = "menu-search",...finalProps } = props;
|
|
116
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
117
|
+
className: Menu_module_default["Menu-search"],
|
|
118
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__launchpad_ui_form.TextField, {
|
|
119
|
+
...finalProps,
|
|
120
|
+
ref,
|
|
121
|
+
className: Menu_module_default["Menu-search-input"],
|
|
122
|
+
tiny: true,
|
|
123
|
+
id,
|
|
124
|
+
type: "search",
|
|
125
|
+
"data-test-id": testId,
|
|
126
|
+
autoComplete: "off",
|
|
127
|
+
placeholder,
|
|
128
|
+
"aria-label": ariaLabel || "Search"
|
|
129
|
+
})
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
MenuSearch.displayName = "MenuSearch";
|
|
133
|
+
const createItemId = (index, id) => `${id}-item-${index}`;
|
|
134
|
+
const getNodeForIndex = (index, menuId) => index === null ? index : document.getElementById(createItemId(index, menuId));
|
|
135
|
+
const handleKeyboardInteractions = (event, keyHandlers) => {
|
|
136
|
+
const ops = {
|
|
137
|
+
ArrowUp: keyHandlers.handleUp,
|
|
138
|
+
ArrowDown: keyHandlers.handleDown,
|
|
139
|
+
Enter: keyHandlers.handleEnter
|
|
140
|
+
};
|
|
141
|
+
if (ops[event.key]) {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
ops[event.key]?.call(globalThis, event);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const chainEventHandlers = (...handlers) => (event) => {
|
|
147
|
+
for (const h of handlers) typeof h === "function" && h(event);
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* @deprecated use `Menu` or `ListBox` from `@launchpad-ui/components` instead
|
|
151
|
+
*
|
|
152
|
+
* https://launchpad.launchdarkly.com/?path=/docs/components-collections-menu--docs
|
|
153
|
+
*/
|
|
154
|
+
const Menu = (props) => {
|
|
155
|
+
const { children, menuItemClassName, onSelect, enableVirtualization, itemHeight, size, overscan = 1, "data-test-id": testId = "menu" } = props;
|
|
156
|
+
const focusManager = (0, __react_aria_focus.useFocusManager)();
|
|
157
|
+
const handleArrowDown = (0, react.useCallback)(() => {
|
|
158
|
+
focusManager?.focusNext({ wrap: true });
|
|
159
|
+
}, [focusManager]);
|
|
160
|
+
const handleArrowUp = (0, react.useCallback)(() => {
|
|
161
|
+
focusManager?.focusPrevious({ wrap: true });
|
|
162
|
+
}, [focusManager]);
|
|
163
|
+
const reduceItems = (0, react.useMemo)(() => {
|
|
164
|
+
const childrenProps = react.Children.toArray(children);
|
|
165
|
+
if (enableVirtualization) {
|
|
166
|
+
let searchElem = null;
|
|
167
|
+
let elements = [];
|
|
168
|
+
for (const child of childrenProps) switch (child.type) {
|
|
169
|
+
case MenuSearch:
|
|
170
|
+
searchElem = child;
|
|
171
|
+
break;
|
|
172
|
+
case MenuItem:
|
|
173
|
+
case MenuDivider:
|
|
174
|
+
elements = elements.concat(child);
|
|
175
|
+
break;
|
|
176
|
+
default: break;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
items: elements,
|
|
180
|
+
searchElement: searchElem
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return childrenProps.reduce(({ items, searchElement }, child) => {
|
|
184
|
+
switch (child.type) {
|
|
185
|
+
case MenuSearch: return {
|
|
186
|
+
items,
|
|
187
|
+
searchElement: /* @__PURE__ */ (0, react.cloneElement)(child, { onKeyDown: (e) => handleKeyboardInteractions(e, {
|
|
188
|
+
handleDown: handleArrowDown,
|
|
189
|
+
handleUp: handleArrowUp
|
|
190
|
+
}) })
|
|
191
|
+
};
|
|
192
|
+
case MenuItem: return {
|
|
193
|
+
items: items.concat(child.props.disabled ? /* @__PURE__ */ (0, react.cloneElement)(child, {
|
|
194
|
+
className: (0, classix.cx)(child.props.className, menuItemClassName),
|
|
195
|
+
onClick: () => void 0,
|
|
196
|
+
onKeyDown: () => void 0,
|
|
197
|
+
tabIndex: -1,
|
|
198
|
+
disabled: true
|
|
199
|
+
}) : /* @__PURE__ */ (0, react.cloneElement)(child, {
|
|
200
|
+
className: (0, classix.cx)(child.props.className, menuItemClassName),
|
|
201
|
+
item: child.props.item ?? items.length,
|
|
202
|
+
onClick: chainEventHandlers(child.props.onClick, () => {
|
|
203
|
+
onSelect?.(child.props.item ?? items.length);
|
|
204
|
+
}),
|
|
205
|
+
onKeyDown: (e) => handleKeyboardInteractions(e, {
|
|
206
|
+
handleDown: handleArrowDown,
|
|
207
|
+
handleUp: handleArrowUp
|
|
208
|
+
})
|
|
209
|
+
})),
|
|
210
|
+
searchElement
|
|
211
|
+
};
|
|
212
|
+
case MenuDivider: return {
|
|
213
|
+
items: items.concat(child),
|
|
214
|
+
searchElement
|
|
215
|
+
};
|
|
216
|
+
default: return {
|
|
217
|
+
items,
|
|
218
|
+
searchElement
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}, {
|
|
222
|
+
items: [],
|
|
223
|
+
searchElement: null
|
|
224
|
+
});
|
|
225
|
+
}, [
|
|
226
|
+
children,
|
|
227
|
+
enableVirtualization,
|
|
228
|
+
menuItemClassName,
|
|
229
|
+
handleArrowDown,
|
|
230
|
+
handleArrowUp,
|
|
231
|
+
onSelect
|
|
232
|
+
]);
|
|
233
|
+
if (enableVirtualization) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuBase, {
|
|
234
|
+
"data-test-id": testId,
|
|
235
|
+
isVirtual: true,
|
|
236
|
+
size,
|
|
237
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ItemVirtualizer, {
|
|
238
|
+
items: react.Children.toArray(reduceItems.items),
|
|
239
|
+
searchElement: reduceItems.searchElement,
|
|
240
|
+
overscan,
|
|
241
|
+
menuItemClassName,
|
|
242
|
+
onSelect,
|
|
243
|
+
itemHeight,
|
|
244
|
+
focusManager
|
|
245
|
+
})
|
|
246
|
+
});
|
|
247
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(MenuBase, {
|
|
248
|
+
"data-test-id": testId,
|
|
249
|
+
size,
|
|
250
|
+
children: [reduceItems.searchElement, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuItemList, {
|
|
251
|
+
role: "presentation",
|
|
252
|
+
children: reduceItems.items
|
|
253
|
+
})]
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
const ItemVirtualizer = (props) => {
|
|
257
|
+
const { overscan, searchElement, itemHeight = 31.5, menuItemClassName, items, focusManager, onSelect } = props;
|
|
258
|
+
const menuId = (0, react.useRef)(`menu-ctrl-${(0, react.useId)()}`);
|
|
259
|
+
const focusedItemIndex = (0, react.useRef)(null);
|
|
260
|
+
const parentRef = (0, react.useRef)(null);
|
|
261
|
+
const searchRef = (0, react.useRef)(null);
|
|
262
|
+
const [nextFocusValue, setNextFocusValue] = (0, react.useState)(null);
|
|
263
|
+
const hasSearch = !!searchElement;
|
|
264
|
+
const lastVirtualItemIndex = items ? items.length - 1 : 0;
|
|
265
|
+
const rowVirtualizer = (0, react_virtual.useVirtual)({
|
|
266
|
+
size: items !== null ? items.length : 0,
|
|
267
|
+
parentRef,
|
|
268
|
+
estimateSize: (0, react.useCallback)(() => itemHeight, [itemHeight]),
|
|
269
|
+
overscan
|
|
270
|
+
});
|
|
271
|
+
const focusSearchBar = (0, react.useCallback)(() => {
|
|
272
|
+
rowVirtualizer.scrollToIndex(0);
|
|
273
|
+
searchRef.current?.focus?.();
|
|
274
|
+
}, [rowVirtualizer]);
|
|
275
|
+
/**
|
|
276
|
+
* Scrolls to the menu item with the index provided and
|
|
277
|
+
* then manually focuses it using a side effect in useEffect
|
|
278
|
+
*/
|
|
279
|
+
const focusMenuItem = (0, react.useCallback)((index) => {
|
|
280
|
+
rowVirtualizer.scrollToIndex(index);
|
|
281
|
+
setNextFocusValue(index);
|
|
282
|
+
}, [rowVirtualizer]);
|
|
283
|
+
const handleKeyboardFocusInteraction = (0, react.useCallback)((direction) => {
|
|
284
|
+
if (focusedItemIndex.current === null || focusedItemIndex.current === void 0) return;
|
|
285
|
+
const nextIndex = direction === "next" ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;
|
|
286
|
+
const shouldWrap = direction === "next" && focusedItemIndex.current === lastVirtualItemIndex || direction === "previous" && focusedItemIndex.current === 0;
|
|
287
|
+
if (shouldWrap) {
|
|
288
|
+
if (hasSearch) focusSearchBar();
|
|
289
|
+
else focusMenuItem(direction === "next" ? 0 : lastVirtualItemIndex);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
switch (direction) {
|
|
293
|
+
case "next":
|
|
294
|
+
rowVirtualizer.scrollToIndex(nextIndex);
|
|
295
|
+
focusManager?.focusNext();
|
|
296
|
+
break;
|
|
297
|
+
case "previous":
|
|
298
|
+
rowVirtualizer.scrollToIndex(nextIndex);
|
|
299
|
+
focusManager?.focusPrevious();
|
|
300
|
+
break;
|
|
301
|
+
default: break;
|
|
302
|
+
}
|
|
303
|
+
}, [
|
|
304
|
+
focusManager,
|
|
305
|
+
focusMenuItem,
|
|
306
|
+
focusSearchBar,
|
|
307
|
+
hasSearch,
|
|
308
|
+
lastVirtualItemIndex,
|
|
309
|
+
rowVirtualizer
|
|
310
|
+
]);
|
|
311
|
+
const getItemProps = (0, react.useCallback)((itemElem, index) => {
|
|
312
|
+
const childProps = itemElem.props;
|
|
313
|
+
switch (itemElem.type) {
|
|
314
|
+
case MenuItem: return {
|
|
315
|
+
className: (0, classix.cx)(childProps.className, menuItemClassName),
|
|
316
|
+
onKeyDown: childProps.disabled ? () => void 0 : (e) => handleKeyboardFocusKeydown(e, {
|
|
317
|
+
handleFocusBackward: handleKeyboardFocusInteraction,
|
|
318
|
+
handleFocusForward: handleKeyboardFocusInteraction
|
|
319
|
+
}),
|
|
320
|
+
onFocus: chainEventHandlers(childProps.onFocus, () => {
|
|
321
|
+
focusedItemIndex.current = index;
|
|
322
|
+
}),
|
|
323
|
+
id: createItemId(index, menuId.current),
|
|
324
|
+
onBlur: chainEventHandlers(childProps.onBlur, () => {
|
|
325
|
+
focusedItemIndex.current = null;
|
|
326
|
+
}),
|
|
327
|
+
onClick: childProps.disabled ? () => void 0 : chainEventHandlers(childProps.onClick, () => {
|
|
328
|
+
onSelect?.(childProps.item);
|
|
329
|
+
})
|
|
330
|
+
};
|
|
331
|
+
default: return {};
|
|
332
|
+
}
|
|
333
|
+
}, [
|
|
334
|
+
handleKeyboardFocusInteraction,
|
|
335
|
+
menuItemClassName,
|
|
336
|
+
onSelect
|
|
337
|
+
]);
|
|
338
|
+
(0, react.useEffect)(() => {
|
|
339
|
+
if (nextFocusValue !== null) {
|
|
340
|
+
requestAnimationFrame(() => {
|
|
341
|
+
const element = getNodeForIndex(nextFocusValue, menuId.current);
|
|
342
|
+
element?.focus();
|
|
343
|
+
});
|
|
344
|
+
setNextFocusValue(null);
|
|
345
|
+
}
|
|
346
|
+
}, [nextFocusValue]);
|
|
347
|
+
/**
|
|
348
|
+
* Calls handleFocusForward when the user is attempting to focus forward using
|
|
349
|
+
* tab or arrow keys. Calls handleFocusBackward when the users wants to move backward.
|
|
350
|
+
*/
|
|
351
|
+
const handleKeyboardFocusKeydown = (e, callbacks) => {
|
|
352
|
+
const keyOps = [
|
|
353
|
+
"Tab",
|
|
354
|
+
"ArrowUp",
|
|
355
|
+
"ArrowDown"
|
|
356
|
+
];
|
|
357
|
+
if (keyOps.includes(e.key)) {
|
|
358
|
+
e.preventDefault();
|
|
359
|
+
e.stopPropagation();
|
|
360
|
+
if (e.key === "Tab" && e.shiftKey || e.key === "ArrowUp") callbacks.handleFocusBackward?.("previous");
|
|
361
|
+
else if (e.key === "ArrowDown" || e.key === "Tab") callbacks.handleFocusForward?.("next");
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const renderSearch = (0, react.useMemo)(() => searchElement ? /* @__PURE__ */ (0, react.cloneElement)(searchElement, {
|
|
365
|
+
onKeyDown: (e) => handleKeyboardFocusKeydown(e, {
|
|
366
|
+
handleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),
|
|
367
|
+
handleFocusForward: () => focusMenuItem(0)
|
|
368
|
+
}),
|
|
369
|
+
ref: searchRef
|
|
370
|
+
}) : null, [
|
|
371
|
+
searchElement,
|
|
372
|
+
lastVirtualItemIndex,
|
|
373
|
+
focusMenuItem
|
|
374
|
+
]);
|
|
375
|
+
const renderItems = (0, react.useMemo)(() => rowVirtualizer.virtualItems.map((virtualRow) => {
|
|
376
|
+
if (!items) return null;
|
|
377
|
+
const elem = items[virtualRow.index];
|
|
378
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
379
|
+
ref: virtualRow.measureRef,
|
|
380
|
+
role: "presentation",
|
|
381
|
+
className: Menu_module_default["VirtualMenu-item"],
|
|
382
|
+
style: { transform: `translateY(${virtualRow.start}px)` },
|
|
383
|
+
children: /* @__PURE__ */ (0, react.cloneElement)(elem, getItemProps(elem, virtualRow.index))
|
|
384
|
+
}, virtualRow.index);
|
|
385
|
+
}), [
|
|
386
|
+
rowVirtualizer.virtualItems,
|
|
387
|
+
items,
|
|
388
|
+
getItemProps
|
|
389
|
+
]);
|
|
390
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderSearch, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuItemList, {
|
|
391
|
+
ref: parentRef,
|
|
392
|
+
role: "presentation",
|
|
393
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
394
|
+
role: "presentation",
|
|
395
|
+
className: Menu_module_default["VirtualMenu-item-list"],
|
|
396
|
+
style: { height: `${rowVirtualizer.totalSize}px` },
|
|
397
|
+
children: renderItems
|
|
398
|
+
})
|
|
399
|
+
})] });
|
|
400
|
+
};
|
|
401
|
+
exports.Menu = Menu;
|
|
402
|
+
exports.MenuBase = MenuBase;
|
|
403
|
+
exports.MenuDivider = MenuDivider;
|
|
404
|
+
exports.MenuItem = MenuItem;
|
|
405
|
+
exports.MenuItemList = MenuItemList;
|
|
406
|
+
exports.MenuSearch = MenuSearch;
|
|
407
|
+
|
|
408
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["Component: ElementType","index: number","id: string","index: number | null","menuId: string","event: KeyboardEvent","keyHandlers: Partial<\n\t\tRecord<'handleUp' | 'handleDown' | 'handleEnter', (e: KeyboardEvent) => void>\n\t>","event: SyntheticEvent","props: MenuProps<T>","elements: ReactElement[]","child: ReactElement<any>","e: KeyboardEvent","props: ItemVirtualizerProps<T>","index: number","direction: 'next' | 'previous'","itemElem: ReactElement","callbacks: Record<\n\t\t\t'handleFocusForward' | 'handleFocusBackward',\n\t\t\t(direction: 'next' | 'previous') => void\n\t\t>"],"sources":["../src/styles/Menu.module.css","../src/MenuBase.tsx","../src/MenuDivider.tsx","../src/MenuItem.tsx","../src/MenuItemList.tsx","../src/MenuSearch.tsx","../src/utils.ts","../src/Menu.tsx"],"sourcesContent":[".Menu-item {\n\tbackground-color: transparent;\n\tborder-radius: var(--lp-border-radius-regular);\n\tborder-width: var(--lp-border-width-100);\n\tcolor: var(--lp-color-text-ui-primary-base);\n\tcursor: pointer;\n\tdisplay: block;\n\tfont: var(--lp-text-label-1-medium);\n\toutline: none;\n\toverflow: hidden;\n\tpadding-inline: var(--lp-spacing-300);\n\tpadding-block: var(--lp-spacing-200);\n\tposition: relative;\n\ttext-align: left;\n\ttext-decoration: none;\n\ttext-overflow: ellipsis;\n\tuser-select: none;\n\twhite-space: nowrap;\n\twidth: 100%;\n}\n\n.Menu-item-list {\n\tmax-height: 55vh;\n\tmin-width: 7.5rem;\n\toverflow: auto;\n\tpadding: var(--lp-spacing-200);\n}\n\n.Menu .Menu-item:not([disabled]):not([aria-disabled]):not(.Menu-item--header) {\n\t&:active {\n\t\ttext-decoration: none;\n\t\tbackground-color: var(--lp-color-bg-interactive-secondary-active);\n\t}\n\n\t&:hover:not(:active):not(.has-focus) {\n\t\tbackground-color: var(--lp-color-bg-interactive-secondary-hover);\n\t}\n\n\t&.has-focus {\n\t\tbackground-color: var(--lp-color-bg-interactive-secondary-hover);\n\t\tbox-shadow: inset 0 0 0 2px var(--lp-color-shadow-interactive-focus);\n\t}\n}\n\n.Menu {\n\tbackground: var(--lp-color-bg-overlay-secondary);\n\tborder-radius: var(--lp-border-radius-medium);\n\tfont-family: var(--lp-font-family-base);\n\n\t&:focus {\n\t\toutline: none;\n\t}\n\n\t/* Override our link styles for link component */\n\t& a.Menu-item {\n\t\t&:focus:not(:hover):not(.has-focus) {\n\t\t\ttext-decoration: none;\n\t\t\tbox-shadow: none;\n\t\t}\n\n\t\t/* Ensures that links that are disabled don't show active styles */\n\t\t&:active[disabled],\n\t\t&:active[aria-disabled] {\n\t\t\ttext-decoration: none;\n\t\t\tcolor: var(--lp-color-text-interactive-disabled);\n\t\t}\n\t}\n}\n\n.Menu-item--nested {\n\tpadding-left: 2.5rem;\n}\n\n.Menu-item--header {\n\tfont: var(--lp-text-small-1-regular);\n\tcolor: var(--lp-color-text-ui-tertiary);\n}\n\n.Menu-item-icon {\n\tmargin-right: var(--lp-spacing-300);\n}\n\n.Menu-item :global(.Gravatar) {\n\tmargin-right: 0.3125rem;\n}\n\n.Menu-item.is-highlighted {\n\tbackground-color: var(--lp-color-bg-interactive-secondary-hover);\n}\n\n.Menu-item[aria-disabled],\n.Menu-item[disabled] {\n\tcolor: var(--lp-color-text-interactive-disabled);\n\tcursor: not-allowed;\n}\n\n.Menu-divider {\n\tborder-top: 1px solid var(--lp-color-border-ui-secondary);\n\tmargin: var(--lp-spacing-200) 0;\n}\n\n.Menu-search {\n\tborder-bottom: 1px solid var(--lp-color-border-ui-secondary);\n\tpadding: var(--lp-spacing-300);\n}\n\n[class*='_Popover-content'] .Menu-search {\n\twidth: 100%;\n\n\t/* Removing anything that could give it some height */\n\t& .Menu-search-hidden-placeholder {\n\t\tpadding-top: 0;\n\t\tpadding-bottom: 0;\n\t\theight: 0;\n\t\tborder: none;\n\t\tvisibility: hidden;\n\t}\n}\n\n.Menu--isVirtual {\n\tdisplay: flex;\n\talign-items: stretch;\n\tflex-direction: column;\n}\n\n.MenuSize--xl {\n\twidth: 20rem;\n}\n\n.MenuSize--lg {\n\twidth: 15rem;\n}\n\n.MenuSize--md {\n\twidth: 10rem;\n}\n\n.MenuSize--sm {\n\twidth: 7.5rem;\n}\n\n.VirtualMenu-item-list {\n\twidth: 100%;\n\tposition: relative;\n}\n\n.VirtualMenu-item {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n}\n","import type { ComponentPropsWithRef } from 'react';\nimport type { MenuProps } from './Menu';\n\nimport { cx } from 'classix';\nimport { forwardRef } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuBaseProps = ComponentPropsWithRef<'div'> & {\n\tisVirtual?: boolean;\n\tsize?: MenuProps<string>['size'];\n};\n\nconst MenuBase = forwardRef<HTMLDivElement, MenuBaseProps>(\n\t({ children, size, isVirtual, ...props }, ref) => {\n\t\tconst classes = cx(\n\t\t\tstyles.Menu,\n\t\t\tisVirtual && styles['Menu--isVirtual'],\n\t\t\tsize && styles[`MenuSize--${size}`],\n\t\t);\n\n\t\treturn (\n\t\t\t<div {...props} role=\"menu\" className={classes} ref={ref}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t);\n\t},\n);\n\nMenuBase.displayName = 'MenuBase';\n\nexport { MenuBase };\nexport type { MenuBaseProps };\n","import type { SeparatorProps } from '@react-aria/separator';\nimport type { RefObject } from 'react';\n\nimport { useSeparator } from '@react-aria/separator';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuDividerProps = SeparatorProps & {\n\tinnerRef?: RefObject<HTMLDivElement>;\n\t'data-test-id'?: string;\n};\n\nconst MenuDivider = ({\n\telementType = 'div',\n\torientation,\n\tinnerRef,\n\t'data-test-id': testId = 'menu-divider',\n}: MenuDividerProps) => {\n\tconst { separatorProps } = useSeparator({\n\t\torientation,\n\t\telementType,\n\t});\n\n\treturn (\n\t\t<div\n\t\t\t{...separatorProps}\n\t\t\tdata-test-id={testId}\n\t\t\tref={innerRef}\n\t\t\tclassName={styles['Menu-divider']}\n\t\t/>\n\t);\n};\n\nexport { MenuDivider };\nexport type { MenuDividerProps };\n","import type { IconProps } from '@launchpad-ui/icons';\nimport type { PopoverPlacement } from '@launchpad-ui/popover';\nimport type { ComponentPropsWithRef, ElementType, JSX, PropsWithRef, ReactElement } from 'react';\n\nimport { Tooltip } from '@launchpad-ui/tooltip';\nimport { Slot } from '@radix-ui/react-slot';\nimport { FocusRing } from '@react-aria/focus';\nimport { cx } from 'classix';\nimport { cloneElement } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\n// Merge two types and get rid of overlapping definitions\ntype Merge<T, U> = Omit<T, keyof U> & U;\n\ntype PropsWithComponent<P, T extends ElementType> = P & { component?: T };\n\ntype PolymorphicPropsWithRef<P, T extends ElementType> = Merge<\n\tT extends keyof JSX.IntrinsicElements\n\t\t? PropsWithRef<JSX.IntrinsicElements[T]>\n\t\t: ComponentPropsWithRef<T>,\n\tPropsWithComponent<P, T>\n>;\n\ntype MenuItemOwnProps = {\n\tisHighlighted?: boolean;\n\ticon?: ReactElement<IconProps>;\n\tdisabled?: boolean;\n\tnested?: boolean;\n\tgroupHeader?: boolean;\n\ttooltip?: string | ReactElement;\n\ttooltipOptions?: ComponentPropsWithRef<typeof Tooltip>;\n\ttooltipPlacement?: PopoverPlacement;\n\tasChild?: boolean;\n\t'data-test-id'?: string;\n};\n\nconst defaultElement = 'button';\n\ntype MenuItemProps<P, T extends ElementType = typeof defaultElement> = PolymorphicPropsWithRef<\n\t| (MenuItemOwnProps & {\n\t\t\titem: P; // Infer the type if it is included\n\t })\n\t| (MenuItemOwnProps & {\n\t\t\titem?: undefined;\n\t }),\n\tT\n>;\n\nconst MenuItem = <P, T extends ElementType = typeof defaultElement>({\n\t...props\n}: MenuItemProps<P, T>) => {\n\tconst {\n\t\t// TODO: remove component prop once we migrate over to asChild format\n\t\tcomponent,\n\t\tchildren,\n\t\tisHighlighted,\n\t\ticon,\n\t\tnested,\n\t\tgroupHeader,\n\t\t// biome-ignore lint/correctness/noUnusedVariables: ignore\n\t\titem,\n\t\tdisabled,\n\t\tclassName,\n\t\ttooltip,\n\t\trole = 'menuitem',\n\t\ttooltipPlacement,\n\t\tonKeyDown,\n\t\ttooltipOptions,\n\t\tasChild,\n\t\t'data-test-id': testId = 'menu-item',\n\t\t...rest\n\t} = props;\n\n\tconst Component: ElementType = component || (asChild ? Slot : defaultElement);\n\n\tconst renderIcon = icon && cloneElement(icon, { size: 'small' });\n\n\tconst renderedItem = (\n\t\t<FocusRing focusRingClass={styles['has-focus']}>\n\t\t\t<Component\n\t\t\t\t{...rest}\n\t\t\t\tdisabled={disabled}\n\t\t\t\taria-disabled={disabled ? disabled : undefined}\n\t\t\t\tclassName={cx(\n\t\t\t\t\tstyles['Menu-item'],\n\t\t\t\t\tclassName,\n\t\t\t\t\tisHighlighted && styles['is-highlighted'],\n\t\t\t\t\tnested && styles['Menu-item--nested'],\n\t\t\t\t\tgroupHeader && styles['Menu-item--header'],\n\t\t\t\t)}\n\t\t\t\tdata-test-id={testId}\n\t\t\t\trole={role}\n\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t>\n\t\t\t\t{asChild ? (\n\t\t\t\t\tchildren\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{icon && <span className={styles['Menu-item-icon']}>{renderIcon}</span>}\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</Component>\n\t\t</FocusRing>\n\t);\n\n\tif (tooltip) {\n\t\treturn (\n\t\t\t<Tooltip\n\t\t\t\tcontent={tooltip}\n\t\t\t\trootElementStyle={{ display: 'block' }}\n\t\t\t\tallowBoundaryElementOverflow\n\t\t\t\tplacement={tooltipPlacement ? tooltipPlacement : 'bottom'}\n\t\t\t\t{...(tooltipOptions || {})}\n\t\t\t>\n\t\t\t\t{renderedItem}\n\t\t\t</Tooltip>\n\t\t);\n\t}\n\n\treturn renderedItem;\n};\n\nexport { MenuItem };\nexport type { MenuItemProps };\n","import type { ComponentPropsWithRef } from 'react';\n\nimport { forwardRef } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuItemListProps = Omit<ComponentPropsWithRef<'div'>, 'className'>;\n\nconst MenuItemList = forwardRef<HTMLDivElement, MenuItemListProps>(({ children, ...rest }, ref) => (\n\t<div {...rest} ref={ref} data-test-id=\"menu-item-list\" className={styles['Menu-item-list']}>\n\t\t{children}\n\t</div>\n));\n\nMenuItemList.displayName = 'MenuItemList';\n\nexport { MenuItemList };\nexport type { MenuItemListProps };\n","import type { ChangeEvent } from 'react';\n\nimport { TextField } from '@launchpad-ui/form';\nimport { forwardRef } from 'react';\n\nimport styles from './styles/Menu.module.css';\n\ntype MenuSearchProps = {\n\tariaLabel?: string;\n\tvalue?: string;\n\tid?: string;\n\tplaceholder?: string;\n\tonChange?(event: ChangeEvent<HTMLInputElement>): void;\n\t'data-test-id'?: string;\n};\n\nconst MenuSearch = forwardRef<HTMLInputElement, MenuSearchProps>((props, ref) => {\n\tconst {\n\t\tariaLabel,\n\t\tplaceholder,\n\t\tid,\n\t\t'data-test-id': testId = 'menu-search',\n\t\t...finalProps\n\t} = props;\n\n\treturn (\n\t\t<div className={styles['Menu-search']}>\n\t\t\t<TextField\n\t\t\t\t{...finalProps}\n\t\t\t\tref={ref}\n\t\t\t\tclassName={styles['Menu-search-input']}\n\t\t\t\ttiny\n\t\t\t\tid={id}\n\t\t\t\ttype=\"search\"\n\t\t\t\tdata-test-id={testId}\n\t\t\t\tautoComplete=\"off\"\n\t\t\t\tplaceholder={placeholder}\n\t\t\t\taria-label={ariaLabel || 'Search'}\n\t\t\t/>\n\t\t</div>\n\t);\n});\n\nMenuSearch.displayName = 'MenuSearch';\n\nexport { MenuSearch };\nexport type { MenuSearchProps };\n","import type { EventHandler, KeyboardEvent, SyntheticEvent } from 'react';\n\nconst createItemId = (index: number, id: string) => `${id}-item-${index}`;\n\nconst getNodeForIndex = (index: number | null, menuId: string) =>\n\tindex === null ? index : document.getElementById(createItemId(index, menuId));\n\nconst handleKeyboardInteractions = (\n\tevent: KeyboardEvent,\n\tkeyHandlers: Partial<\n\t\tRecord<'handleUp' | 'handleDown' | 'handleEnter', (e: KeyboardEvent) => void>\n\t>,\n) => {\n\tconst ops = {\n\t\tArrowUp: keyHandlers.handleUp,\n\t\tArrowDown: keyHandlers.handleDown,\n\t\tEnter: keyHandlers.handleEnter,\n\t\t// biome-ignore lint/suspicious/noConfusingVoidType: ignore\n\t} as Record<string, (e: KeyboardEvent) => void | undefined>;\n\n\tif (ops[event.key]) {\n\t\tevent.preventDefault();\n\t\tops[event.key]?.call(globalThis, event);\n\t}\n};\n\nconst chainEventHandlers =\n\t(...handlers: (EventHandler<SyntheticEvent> | undefined)[]) =>\n\t(event: SyntheticEvent) => {\n\t\tfor (const h of handlers) {\n\t\t\ttypeof h === 'function' && h(event);\n\t\t}\n\t};\n\nexport { createItemId, getNodeForIndex, handleKeyboardInteractions, chainEventHandlers };\n","import type { FocusManager } from '@react-aria/focus';\nimport type { KeyboardEvent, ReactElement, ReactNode } from 'react';\nimport type { MenuItemProps } from './MenuItem';\n\nimport { useFocusManager } from '@react-aria/focus';\nimport { cx } from 'classix';\nimport {\n\tChildren,\n\tcloneElement,\n\tuseCallback,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useVirtual } from 'react-virtual';\n\nimport { MenuBase } from './MenuBase';\nimport { MenuDivider } from './MenuDivider';\nimport { MenuItem } from './MenuItem';\nimport { MenuItemList } from './MenuItemList';\nimport { MenuSearch } from './MenuSearch';\nimport styles from './styles/Menu.module.css';\nimport {\n\tchainEventHandlers,\n\tcreateItemId,\n\tgetNodeForIndex,\n\thandleKeyboardInteractions,\n} from './utils';\n\ntype ControlledMenuProps<T> = {\n\tchildren: ReactNode;\n\tonSelect?: (item: T) => void;\n\t/**\n\t * Menus items are rendered using react-virtual for\n\t * additional rendering performance.\n\t */\n\tenableVirtualization?: boolean;\n\t/**\n\t * Class name to be applied to all MenuItem components\n\t * in the menu.\n\t */\n\tmenuItemClassName?: string;\n\t/**\n\t * Sets the width of the menu. This is especially useful when using virtual items\n\t * since the width cannot be automatically set by the widest element.\n\t */\n\tsize?: 'sm' | 'md' | 'lg' | 'xl';\n\t/**\n\t * Sets the number out of elements rendered outside of the view window\n\t * when using virtualization\n\t */\n\toverscan?: number;\n\t/**\n\t * Sets the height for each menu item when using virtualization.\n\t *\n\t */\n\titemHeight?: number;\n\t'data-test-id'?: string;\n};\n\ntype MenuProps<T extends number | string> = ControlledMenuProps<T>;\n\n/**\n * @deprecated use `Menu` or `ListBox` from `@launchpad-ui/components` instead\n *\n * https://launchpad.launchdarkly.com/?path=/docs/components-collections-menu--docs\n */\nconst Menu = <T extends number | string>(props: MenuProps<T>) => {\n\tconst {\n\t\tchildren,\n\t\tmenuItemClassName,\n\t\tonSelect,\n\t\tenableVirtualization,\n\t\titemHeight,\n\t\tsize,\n\t\toverscan = 1,\n\t\t'data-test-id': testId = 'menu',\n\t} = props;\n\n\tconst focusManager = useFocusManager();\n\n\tconst handleArrowDown = useCallback(() => {\n\t\tfocusManager?.focusNext({ wrap: true });\n\t}, [focusManager]);\n\n\tconst handleArrowUp = useCallback(() => {\n\t\tfocusManager?.focusPrevious({ wrap: true });\n\t}, [focusManager]);\n\n\tconst reduceItems = useMemo(() => {\n\t\tconst childrenProps = Children.toArray(children);\n\t\tif (enableVirtualization) {\n\t\t\t// the virtualized menu has its own handlers and props\n\t\t\tlet searchElem = null;\n\t\t\tlet elements: ReactElement[] = [];\n\t\t\tfor (const child of childrenProps as ReactElement[]) {\n\t\t\t\tswitch (child.type) {\n\t\t\t\t\tcase MenuSearch:\n\t\t\t\t\t\tsearchElem = child;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MenuItem:\n\t\t\t\t\tcase MenuDivider:\n\t\t\t\t\t\telements = elements.concat(child);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { items: elements, searchElement: searchElem };\n\t\t}\n\n\t\treturn (childrenProps as ReactElement[]).reduce(\n\t\t\t(\n\t\t\t\t{ items, searchElement }: { items: ReactElement[]; searchElement: null | ReactElement },\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: ignore\n\t\t\t\tchild: ReactElement<any>,\n\t\t\t) => {\n\t\t\t\tswitch (child.type) {\n\t\t\t\t\tcase MenuSearch:\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\titems,\n\t\t\t\t\t\t\tsearchElement: cloneElement(child, {\n\t\t\t\t\t\t\t\tonKeyDown: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\t\t\thandleKeyboardInteractions(e, {\n\t\t\t\t\t\t\t\t\t\thandleDown: handleArrowDown,\n\t\t\t\t\t\t\t\t\t\thandleUp: handleArrowUp,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t};\n\t\t\t\t\tcase MenuItem:\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\titems: items.concat(\n\t\t\t\t\t\t\t\tchild.props.disabled\n\t\t\t\t\t\t\t\t\t? cloneElement(child, {\n\t\t\t\t\t\t\t\t\t\t\tclassName: cx(child.props.className, menuItemClassName),\n\t\t\t\t\t\t\t\t\t\t\tonClick: () => undefined,\n\t\t\t\t\t\t\t\t\t\t\tonKeyDown: () => undefined,\n\t\t\t\t\t\t\t\t\t\t\ttabIndex: -1,\n\t\t\t\t\t\t\t\t\t\t\tdisabled: true,\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t: cloneElement(child, {\n\t\t\t\t\t\t\t\t\t\t\tclassName: cx(child.props.className, menuItemClassName),\n\t\t\t\t\t\t\t\t\t\t\titem: child.props.item ?? items.length,\n\t\t\t\t\t\t\t\t\t\t\t// set focus on the first menu item if there is no search input, and set in the tab order\n\t\t\t\t\t\t\t\t\t\t\tonClick: chainEventHandlers(child.props.onClick, () => {\n\t\t\t\t\t\t\t\t\t\t\t\tonSelect?.(child.props.item ?? items.length);\n\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\tonKeyDown: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\t\t\t\t\t\thandleKeyboardInteractions(e, {\n\t\t\t\t\t\t\t\t\t\t\t\t\thandleDown: handleArrowDown,\n\t\t\t\t\t\t\t\t\t\t\t\t\thandleUp: handleArrowUp,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tsearchElement,\n\t\t\t\t\t\t};\n\t\t\t\t\tcase MenuDivider:\n\t\t\t\t\t\treturn { items: items.concat(child), searchElement };\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn { items, searchElement };\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ items: [], searchElement: null },\n\t\t);\n\t}, [children, enableVirtualization, menuItemClassName, handleArrowDown, handleArrowUp, onSelect]);\n\n\tif (enableVirtualization) {\n\t\treturn (\n\t\t\t<MenuBase data-test-id={testId} isVirtual size={size}>\n\t\t\t\t<ItemVirtualizer<T>\n\t\t\t\t\titems={Children.toArray(reduceItems.items) as ReactElement[]}\n\t\t\t\t\tsearchElement={reduceItems.searchElement}\n\t\t\t\t\toverscan={overscan}\n\t\t\t\t\tmenuItemClassName={menuItemClassName}\n\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\titemHeight={itemHeight}\n\t\t\t\t\tfocusManager={focusManager}\n\t\t\t\t/>\n\t\t\t</MenuBase>\n\t\t);\n\t}\n\n\treturn (\n\t\t<MenuBase data-test-id={testId} size={size}>\n\t\t\t{reduceItems.searchElement}\n\t\t\t<MenuItemList role=\"presentation\">{reduceItems.items}</MenuItemList>\n\t\t</MenuBase>\n\t);\n};\n\ntype ItemVirtualizerProps<T> = Omit<ControlledMenuProps<T>, 'children'> & {\n\titems: ReactElement[] | null;\n\tsearchElement?: ReactElement | null;\n\tfocusManager?: FocusManager;\n};\n\nconst ItemVirtualizer = <T extends number | string>(props: ItemVirtualizerProps<T>) => {\n\tconst {\n\t\toverscan,\n\t\tsearchElement,\n\t\titemHeight = 31.5,\n\t\tmenuItemClassName,\n\t\titems,\n\t\tfocusManager,\n\t\tonSelect,\n\t} = props;\n\n\tconst menuId = useRef(`menu-ctrl-${useId()}`);\n\n\tconst focusedItemIndex = useRef<number | null>(null);\n\tconst parentRef = useRef<HTMLDivElement | null>(null);\n\tconst searchRef = useRef<HTMLInputElement | null>(null);\n\n\tconst [nextFocusValue, setNextFocusValue] = useState<number | null>(null);\n\n\tconst hasSearch = !!searchElement;\n\n\tconst lastVirtualItemIndex = items ? items.length - 1 : 0;\n\n\tconst rowVirtualizer = useVirtual({\n\t\tsize: items !== null ? items.length : 0,\n\t\tparentRef,\n\t\testimateSize: useCallback(() => itemHeight, [itemHeight]),\n\t\toverscan,\n\t});\n\n\tconst focusSearchBar = useCallback(() => {\n\t\trowVirtualizer.scrollToIndex(0);\n\t\tsearchRef.current?.focus?.();\n\t}, [rowVirtualizer]);\n\n\t/**\n\t * Scrolls to the menu item with the index provided and\n\t * then manually focuses it using a side effect in useEffect\n\t */\n\tconst focusMenuItem = useCallback(\n\t\t(index: number) => {\n\t\t\trowVirtualizer.scrollToIndex(index);\n\t\t\tsetNextFocusValue(index);\n\t\t},\n\t\t[rowVirtualizer],\n\t);\n\n\tconst handleKeyboardFocusInteraction = useCallback(\n\t\t(direction: 'next' | 'previous') => {\n\t\t\tif (focusedItemIndex.current === null || focusedItemIndex.current === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst nextIndex =\n\t\t\t\tdirection === 'next' ? focusedItemIndex.current + 1 : focusedItemIndex.current - 1;\n\t\t\tconst shouldWrap =\n\t\t\t\t(direction === 'next' && focusedItemIndex.current === lastVirtualItemIndex) ||\n\t\t\t\t(direction === 'previous' && focusedItemIndex.current === 0);\n\t\t\tif (shouldWrap) {\n\t\t\t\t// we are at the end of the list so we will\n\t\t\t\t// scroll back to the beginning of the list\n\t\t\t\tif (hasSearch) {\n\t\t\t\t\tfocusSearchBar();\n\t\t\t\t} else {\n\t\t\t\t\t// if at end, wrap to beginning, else focus last item\n\t\t\t\t\tfocusMenuItem(direction === 'next' ? 0 : lastVirtualItemIndex);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tswitch (direction) {\n\t\t\t\tcase 'next':\n\t\t\t\t\trowVirtualizer.scrollToIndex(nextIndex);\n\t\t\t\t\tfocusManager?.focusNext();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'previous':\n\t\t\t\t\trowVirtualizer.scrollToIndex(nextIndex);\n\t\t\t\t\tfocusManager?.focusPrevious();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t\t[focusManager, focusMenuItem, focusSearchBar, hasSearch, lastVirtualItemIndex, rowVirtualizer],\n\t);\n\n\tconst getItemProps = useCallback(\n\t\t(itemElem: ReactElement, index: number) => {\n\t\t\tconst childProps = itemElem.props as MenuItemProps<T>;\n\t\t\tswitch (itemElem.type) {\n\t\t\t\tcase MenuItem:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tclassName: cx(childProps.className, menuItemClassName),\n\t\t\t\t\t\t// set focus on the first menu item if there is no search input, and set in the tab order\n\t\t\t\t\t\tonKeyDown: childProps.disabled\n\t\t\t\t\t\t\t? () => undefined\n\t\t\t\t\t\t\t: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\t\t\thandleKeyboardFocusKeydown(e, {\n\t\t\t\t\t\t\t\t\t\thandleFocusBackward: handleKeyboardFocusInteraction,\n\t\t\t\t\t\t\t\t\t\thandleFocusForward: handleKeyboardFocusInteraction,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tonFocus: chainEventHandlers(childProps.onFocus, () => {\n\t\t\t\t\t\t\tfocusedItemIndex.current = index;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tid: createItemId(index, menuId.current),\n\t\t\t\t\t\tonBlur: chainEventHandlers(childProps.onBlur, () => {\n\t\t\t\t\t\t\tfocusedItemIndex.current = null;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tonClick: childProps.disabled\n\t\t\t\t\t\t\t? () => undefined\n\t\t\t\t\t\t\t: chainEventHandlers(childProps.onClick, () => {\n\t\t\t\t\t\t\t\t\tonSelect?.(childProps.item as T);\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t} as MenuItemProps<T>;\n\t\t\t\tdefault:\n\t\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\t\t[handleKeyboardFocusInteraction, menuItemClassName, onSelect],\n\t);\n\n\tuseEffect(() => {\n\t\tif (nextFocusValue !== null) {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tconst element = getNodeForIndex(nextFocusValue, menuId.current);\n\t\t\t\telement?.focus();\n\t\t\t});\n\t\t\tsetNextFocusValue(null);\n\t\t}\n\t}, [nextFocusValue]);\n\n\t/**\n\t * Calls handleFocusForward when the user is attempting to focus forward using\n\t * tab or arrow keys. Calls handleFocusBackward when the users wants to move backward.\n\t */\n\tconst handleKeyboardFocusKeydown = (\n\t\te: KeyboardEvent,\n\t\tcallbacks: Record<\n\t\t\t'handleFocusForward' | 'handleFocusBackward',\n\t\t\t(direction: 'next' | 'previous') => void\n\t\t>,\n\t) => {\n\t\tconst keyOps = ['Tab', 'ArrowUp', 'ArrowDown'];\n\t\tif (keyOps.includes(e.key)) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tif ((e.key === 'Tab' && e.shiftKey) || e.key === 'ArrowUp') {\n\t\t\t\tcallbacks.handleFocusBackward?.('previous');\n\t\t\t} else if (e.key === 'ArrowDown' || e.key === 'Tab') {\n\t\t\t\tcallbacks.handleFocusForward?.('next');\n\t\t\t}\n\t\t}\n\t};\n\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: ignore\n\tconst renderSearch = useMemo(\n\t\t() =>\n\t\t\tsearchElement\n\t\t\t\t? // biome-ignore lint/suspicious/noExplicitAny: ignore\n\t\t\t\t\tcloneElement(searchElement as ReactElement<any>, {\n\t\t\t\t\t\tonKeyDown: (e: KeyboardEvent) =>\n\t\t\t\t\t\t\thandleKeyboardFocusKeydown(e, {\n\t\t\t\t\t\t\t\thandleFocusBackward: () => focusMenuItem(lastVirtualItemIndex),\n\t\t\t\t\t\t\t\thandleFocusForward: () => focusMenuItem(0),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\tref: searchRef,\n\t\t\t\t\t})\n\t\t\t\t: null,\n\t\t[searchElement, lastVirtualItemIndex, focusMenuItem],\n\t);\n\n\tconst renderItems = useMemo(\n\t\t() =>\n\t\t\trowVirtualizer.virtualItems.map((virtualRow) => {\n\t\t\t\tif (!items) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst elem = items[virtualRow.index];\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={virtualRow.index}\n\t\t\t\t\t\tref={virtualRow.measureRef}\n\t\t\t\t\t\trole=\"presentation\"\n\t\t\t\t\t\tclassName={styles['VirtualMenu-item']}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\ttransform: `translateY(${virtualRow.start}px)`,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{cloneElement(elem, getItemProps(elem, virtualRow.index))}\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t}),\n\t\t[rowVirtualizer.virtualItems, items, getItemProps],\n\t);\n\n\treturn (\n\t\t<>\n\t\t\t{renderSearch}\n\t\t\t<MenuItemList ref={parentRef} role=\"presentation\">\n\t\t\t\t<div\n\t\t\t\t\trole=\"presentation\"\n\t\t\t\t\tclassName={styles['VirtualMenu-item-list']}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\theight: `${rowVirtualizer.totalSize}px`,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{renderItems}\n\t\t\t\t</div>\n\t\t\t</MenuItemList>\n\t\t</>\n\t);\n};\n\nexport { Menu, ItemVirtualizer };\nexport type { MenuProps, ItemVirtualizerProps };\n"],"mappings":"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"}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.C73R2W_Menu-item{border-radius:var(--lp-border-radius-regular);border-width:var(--lp-border-width-100);color:var(--lp-color-text-ui-primary-base);cursor:pointer;font:var(--lp-text-label-1-medium);padding-inline:var(--lp-spacing-300);padding-block:var(--lp-spacing-200);text-align:left;text-overflow:ellipsis;-webkit-user-select:none;user-select:none;white-space:nowrap;background-color:#0000;outline:none;width:100%;text-decoration:none;display:block;position:relative;overflow:hidden}.C73R2W_Menu-item-list{min-width:7.5rem;max-height:55vh;padding:var(--lp-spacing-200);overflow:auto}.C73R2W_Menu .C73R2W_Menu-item:not([disabled]):not([aria-disabled]):not(.C73R2W_Menu-item--header):active{background-color:var(--lp-color-bg-interactive-secondary-active);text-decoration:none}.C73R2W_Menu .C73R2W_Menu-item:not([disabled]):not([aria-disabled]):not(.C73R2W_Menu-item--header):hover:not(:active):not(.C73R2W_has-focus){background-color:var(--lp-color-bg-interactive-secondary-hover)}.C73R2W_Menu .C73R2W_Menu-item:not([disabled]):not([aria-disabled]):not(.C73R2W_Menu-item--header).C73R2W_has-focus{background-color:var(--lp-color-bg-interactive-secondary-hover);box-shadow:inset 0 0 0 2px var(--lp-color-shadow-interactive-focus)}.C73R2W_Menu{background:var(--lp-color-bg-overlay-secondary);border-radius:var(--lp-border-radius-medium);font-family:var(--lp-font-family-base)}.C73R2W_Menu:focus{outline:none}.C73R2W_Menu a.C73R2W_Menu-item:focus:not(:hover):not(.C73R2W_has-focus){box-shadow:none;text-decoration:none}.C73R2W_Menu a.C73R2W_Menu-item:active[disabled],.C73R2W_Menu a.C73R2W_Menu-item:active[aria-disabled]{color:var(--lp-color-text-interactive-disabled);text-decoration:none}.C73R2W_Menu-item--nested{padding-left:2.5rem}.C73R2W_Menu-item--header{font:var(--lp-text-small-1-regular);color:var(--lp-color-text-ui-tertiary)}.C73R2W_Menu-item-icon{margin-right:var(--lp-spacing-300)}.C73R2W_Menu-item .Gravatar{margin-right:.3125rem}.C73R2W_Menu-item.C73R2W_is-highlighted{background-color:var(--lp-color-bg-interactive-secondary-hover)}.C73R2W_Menu-item[aria-disabled],.C73R2W_Menu-item[disabled]{color:var(--lp-color-text-interactive-disabled);cursor:not-allowed}.C73R2W_Menu-divider{border-top:1px solid var(--lp-color-border-ui-secondary);margin:var(--lp-spacing-200)0}.C73R2W_Menu-search{border-bottom:1px solid var(--lp-color-border-ui-secondary);padding:var(--lp-spacing-300)}[class*=_Popover-content] .C73R2W_Menu-search{width:100%}[class*=_Popover-content] .C73R2W_Menu-search .C73R2W_Menu-search-hidden-placeholder{visibility:hidden;border:none;height:0;padding-top:0;padding-bottom:0}.C73R2W_Menu--isVirtual{flex-direction:column;align-items:stretch;display:flex}.C73R2W_MenuSize--xl{width:20rem}.C73R2W_MenuSize--lg{width:15rem}.C73R2W_MenuSize--md{width:10rem}.C73R2W_MenuSize--sm{width:7.5rem}.C73R2W_VirtualMenu-item-list{width:100%;position:relative}.C73R2W_VirtualMenu-item{width:100%;position:absolute;top:0;left:0}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EventHandler, KeyboardEvent, SyntheticEvent } from 'react';
|
|
2
|
+
declare const createItemId: (index: number, id: string) => string;
|
|
3
|
+
declare const getNodeForIndex: (index: number | null, menuId: string) => HTMLElement | null;
|
|
4
|
+
declare const handleKeyboardInteractions: (event: KeyboardEvent, keyHandlers: Partial<Record<"handleUp" | "handleDown" | "handleEnter", (e: KeyboardEvent) => void>>) => void;
|
|
5
|
+
declare const chainEventHandlers: (...handlers: (EventHandler<SyntheticEvent> | undefined)[]) => (event: SyntheticEvent) => void;
|
|
6
|
+
export { createItemId, getNodeForIndex, handleKeyboardInteractions, chainEventHandlers };
|
|
7
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAEzE,QAAA,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,IAAI,MAAM,WAA0B,CAAC;AAE1E,QAAA,MAAM,eAAe,GAAI,OAAO,MAAM,GAAG,IAAI,EAAE,QAAQ,MAAM,uBACiB,CAAC;AAE/E,QAAA,MAAM,0BAA0B,GAC/B,OAAO,aAAa,EACpB,aAAa,OAAO,CACnB,MAAM,CAAC,UAAU,GAAG,YAAY,GAAG,aAAa,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC,CAC7E,SAaD,CAAC;AAEF,QAAA,MAAM,kBAAkB,GACtB,GAAG,UAAU,CAAC,YAAY,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC,EAAE,MACzD,OAAO,cAAc,SAIrB,CAAC;AAEH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@launchpad-ui/menu",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.13",
|
|
4
4
|
"description": "An element that presents a list of items a user can choose from.",
|
|
5
5
|
"repository": "launchdarkly/launchpad-ui",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"@react-aria/separator": "3.4.13",
|
|
25
25
|
"classix": "2.2.0",
|
|
26
26
|
"react-virtual": "2.10.4",
|
|
27
|
-
"@launchpad-ui/form": "~0.15.
|
|
28
|
-
"@launchpad-ui/icons": "~0.25.
|
|
27
|
+
"@launchpad-ui/form": "~0.15.13",
|
|
28
|
+
"@launchpad-ui/icons": "~0.25.4",
|
|
29
29
|
"@launchpad-ui/popover": "~0.14.2",
|
|
30
30
|
"@launchpad-ui/tokens": "~0.15.1",
|
|
31
31
|
"@launchpad-ui/tooltip": "~0.12.2"
|