UNPKG

@stratakit/structures

Version:

Medium-sized component structures for the Strata design system

333 lines (332 loc) 10.1 kB
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 };