@stratakit/structures
Version:
Medium-sized component structures for the Strata design system
333 lines (332 loc) • 10.1 kB
JavaScript
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { Button as ButtonAk } from "@ariakit/react/button";
import {
Menu,
MenuButton,
MenuGroup,
MenuGroupLabel,
MenuItem,
MenuItemCheckbox,
MenuProvider,
useMenuContext,
useMenuStore
} from "@ariakit/react/menu";
import { useStoreState } from "@ariakit/react/store";
import { Button, Kbd, Text } from "@stratakit/bricks";
import {
DisclosureArrow,
Dot,
predefinedSymbols
} from "@stratakit/bricks/secret-internals";
import { Icon } from "@stratakit/foundations";
import {
forwardRef,
usePopoverApi,
useSafeContext,
useUnreactiveCallback
} from "@stratakit/foundations/secret-internals";
import cx from "classnames";
import { Checkmark, ChevronRight } from "./~utils.icons.js";
import * as ListItem from "./~utils.ListItem.js";
import { useInit } from "./~utils.useInit.js";
function DropdownMenuProvider(props) {
useInit();
const { children, placement, open, setOpen, defaultOpen } = props;
return /* @__PURE__ */ jsx(
MenuProvider,
{
defaultOpen,
open,
placement,
setOpen,
children
}
);
}
DEV: DropdownMenuProvider.displayName = "DropdownMenu.Provider";
const DropdownMenuContent = forwardRef(
(props, forwardedRef) => {
const context = useMenuContext();
const open = useStoreState(context, "open");
const popoverElement = useStoreState(context, "popoverElement");
const setOpen = useUnreactiveCallback(context?.setOpen);
const popoverProps = usePopoverApi({
element: popoverElement,
open,
setOpen
});
return /* @__PURE__ */ jsx(
Menu,
{
portal: true,
unmountOnHide: true,
...props,
gutter: 4,
style: { ...popoverProps.style, ...props.style },
wrapperProps: { popover: popoverProps.popover },
className: cx("\u{1F95D}DropdownMenu", props.className),
ref: forwardedRef
}
);
}
);
DEV: DropdownMenuContent.displayName = "DropdownMenu.Content";
const DropdownMenuButton = forwardRef(
(props, forwardedRef) => {
const { accessibleWhenDisabled = true, children, ...rest } = props;
const open = useStoreState(useMenuContext(), "open");
return /* @__PURE__ */ jsx(
MenuButton,
{
accessibleWhenDisabled: true,
...rest,
render: props.render ?? /* @__PURE__ */ jsxs(Button, { accessibleWhenDisabled, children: [
children,
/* @__PURE__ */ jsx(DisclosureArrow, {})
] }),
className: cx("\u{1F95D}DropdownMenuButton", props.className),
"data-has-popover-open": open || void 0,
ref: forwardedRef
}
);
}
);
DEV: DropdownMenuButton.displayName = "DropdownMenu.Button";
const DropdownMenuItem = forwardRef(
(props, forwardedRef) => {
const { label, shortcuts, icon, unstable_dot, submenu, ...rest } = props;
const dotId = React.useId();
const defaultSubmenuStore = useMenuStore();
const [submenuStore, setSubmenuStore] = React.useState();
const store = submenuStore ?? defaultSubmenuStore;
const open = useStoreState(store, "open");
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs(
MenuItem,
{
accessibleWhenDisabled: true,
render: /* @__PURE__ */ jsx(
ListItem.Root,
{
render: /* @__PURE__ */ jsx(
ButtonAk,
{
accessibleWhenDisabled: true,
"aria-describedby": dotId,
...rest,
render: submenu ? /* @__PURE__ */ jsx(
MenuButton,
{
render: props.render ?? /* @__PURE__ */ jsx("button", {}),
store
}
) : props.render,
className: cx("\u{1F95D}DropdownMenuItem", props.className),
"data-has-popover-open": open || void 0,
ref: forwardedRef
}
)
}
),
children: [
icon ? /* @__PURE__ */ jsx(DropdownMenuIcon, { icon }) : null,
/* @__PURE__ */ jsx(ListItem.Content, { render: /* @__PURE__ */ jsx("span", {}), children: label }),
shortcuts ? /* @__PURE__ */ jsx(DropdownMenuItemShortcuts, { shortcuts }) : null,
unstable_dot ? /* @__PURE__ */ jsx(
ListItem.Decoration,
{
render: /* @__PURE__ */ jsx(Dot, { id: dotId, className: "\u{1F95D}DropdownMenuItemDot", children: unstable_dot })
}
) : null,
submenu ? /* @__PURE__ */ jsx(
ListItem.Decoration,
{
className: "\u{1F95D}DropdownMenuItemChevron",
render: /* @__PURE__ */ jsx(ChevronRight, {})
}
) : null
]
}
),
/* @__PURE__ */ jsx(
DropdownMenuSubmenuContext.Provider,
{
value: React.useMemo(() => ({ setStore: setSubmenuStore }), []),
children: submenu
}
)
] });
}
);
DEV: DropdownMenuItem.displayName = "DropdownMenu.Item";
const DropdownMenuItemShortcuts = forwardRef((props, forwardedRef) => {
const { shortcuts, ...rest } = props;
const shortcutKeys = React.useMemo(() => {
return shortcuts.split("+").map((key) => ({
key: key.trim(),
isSymbol: key in predefinedSymbols
}));
}, [shortcuts]);
return /* @__PURE__ */ jsx(
ListItem.Decoration,
{
render: /* @__PURE__ */ jsx("span", {}),
...rest,
className: cx("\u{1F95D}DropdownMenuItemShortcuts", props.className),
ref: forwardedRef,
children: shortcutKeys.map(({ key, isSymbol }, index) => {
if (isSymbol) {
return /* @__PURE__ */ jsx(
Kbd,
{
variant: "ghost",
symbol: key
},
`${key + index}`
);
}
return (
// biome-ignore lint/suspicious/noArrayIndexKey: The is likely to remain stable.
/* @__PURE__ */ jsx(Kbd, { variant: "ghost", children: key }, `${key + index}`)
);
})
}
);
});
DEV: DropdownMenuItemShortcuts.displayName = "DropdownMenuItemShortcuts";
const DropdownMenuIcon = forwardRef(
(props, forwardedRef) => {
const { icon, ...rest } = props;
return /* @__PURE__ */ jsx(
ListItem.Decoration,
{
render: /* @__PURE__ */ jsx(
Icon,
{
href: typeof icon === "string" ? icon : void 0,
render: React.isValidElement(icon) ? icon : void 0,
...rest,
ref: forwardedRef
}
)
}
);
}
);
DEV: DropdownMenuIcon.displayName = "DropdownMenuIcon";
const DropdownMenuCheckboxItem = forwardRef((props, forwardedRef) => {
const {
label,
icon,
defaultChecked,
checked,
onChange,
name,
value = defaultChecked ? "on" : void 0,
// For defaultChecked to work
...rest
} = props;
return /* @__PURE__ */ jsxs(
MenuItemCheckbox,
{
accessibleWhenDisabled: true,
defaultChecked,
checked,
name,
value,
onChange,
render: /* @__PURE__ */ jsx(
ListItem.Root,
{
render: /* @__PURE__ */ jsx(
ButtonAk,
{
accessibleWhenDisabled: true,
...rest,
className: cx("\u{1F95D}DropdownMenuItem", props.className),
ref: forwardedRef
}
)
}
),
children: [
icon ? /* @__PURE__ */ jsx(DropdownMenuIcon, { icon }) : null,
/* @__PURE__ */ jsx(ListItem.Content, { render: /* @__PURE__ */ jsx("span", {}), children: label }),
/* @__PURE__ */ jsx(
ListItem.Decoration,
{
render: /* @__PURE__ */ jsx(Checkmark, { className: "\u{1F95D}DropdownMenuCheckmark" })
}
)
]
}
);
});
DEV: DropdownMenuCheckboxItem.displayName = "DropdownMenu.CheckboxItem";
const DropdownMenuSubmenuContext = React.createContext(void 0);
const DropdownMenuSubmenu = forwardRef(
(props, forwardedRef) => {
const { setStore } = useSafeContext(DropdownMenuSubmenuContext);
const store = useMenuStore();
React.useEffect(() => {
setStore(store);
return () => setStore(void 0);
}, [store, setStore]);
const parent = useMenuContext();
const popoverElement = useStoreState(parent, "popoverElement");
const portalElement = React.useCallback(() => {
if (!popoverElement?.isConnected) return null;
return popoverElement;
}, [popoverElement]);
return /* @__PURE__ */ jsx(MenuProvider, { store, children: /* @__PURE__ */ jsx(
Menu,
{
store,
portal: true,
portalElement,
preserveTabOrder: false,
...props,
gutter: 2,
shift: -4,
className: cx("\u{1F95D}DropdownMenu", props.className),
ref: forwardedRef
}
) });
}
);
DEV: DropdownMenuSubmenu.displayName = "DropdownMenu.Submenu";
const DropdownMenuGroup = forwardRef(
(props, forwardedRef) => {
const { label, items, ...rest } = props;
return /* @__PURE__ */ jsxs(
MenuGroup,
{
...rest,
className: cx("\u{1F95D}DropdownMenuGroup", props.className),
ref: forwardedRef,
children: [
/* @__PURE__ */ jsx(
MenuGroupLabel,
{
className: "\u{1F95D}DropdownMenuGroupLabel",
render: /* @__PURE__ */ jsx(Text, { variant: "body-sm" }),
children: label
}
),
items
]
}
);
}
);
DEV: DropdownMenuGroup.displayName = "DropdownMenu.Group";
export {
DropdownMenuButton as Button,
DropdownMenuCheckboxItem as CheckboxItem,
DropdownMenuContent as Content,
DropdownMenuGroup as Group,
DropdownMenuItem as Item,
DropdownMenuProvider as Provider,
DropdownMenuSubmenu as Submenu
};