import { createElement, Fragment } from "react"; /** * External dependencies */ // eslint-disable-next-line no-restricted-imports import * as Ariakit from '@ariakit/react'; /** * WordPress dependencies */ import { forwardRef, createContext, useContext, useMemo, cloneElement, isValidElement, useCallback } from '@wordpress/element'; import { isRTL } from '@wordpress/i18n'; import { check, chevronRightSmall } from '@wordpress/icons'; import { SVG, Circle } from '@wordpress/primitives'; /** * Internal dependencies */ import { useContextSystem, contextConnect } from '../context'; import Icon from '../icon'; import * as Styled from './styles'; export const DropdownMenuContext = createContext(undefined); export const DropdownMenuItem = forwardRef(function DropdownMenuItem({ prefix, suffix, children, hideOnClick = true, ...props }, ref) { const dropdownMenuContext = useContext(DropdownMenuContext); return createElement(Styled.DropdownMenuItem, { ref: ref, ...props, accessibleWhenDisabled: true, hideOnClick: hideOnClick, store: dropdownMenuContext?.store }, createElement(Styled.ItemPrefixWrapper, null, prefix), createElement(Styled.DropdownMenuItemContentWrapper, null, createElement(Styled.DropdownMenuItemChildrenWrapper, null, children), suffix && createElement(Styled.ItemSuffixWrapper, null, suffix))); }); export const DropdownMenuCheckboxItem = forwardRef(function DropdownMenuCheckboxItem({ suffix, children, hideOnClick = false, ...props }, ref) { const dropdownMenuContext = useContext(DropdownMenuContext); return createElement(Styled.DropdownMenuCheckboxItem, { ref: ref, ...props, accessibleWhenDisabled: true, hideOnClick: hideOnClick, store: dropdownMenuContext?.store }, createElement(Ariakit.MenuItemCheck, { store: dropdownMenuContext?.store, render: createElement(Styled.ItemPrefixWrapper, null) // Override some ariakit inline styles , style: { width: 'auto', height: 'auto' } }, createElement(Icon, { icon: check, size: 24 })), createElement(Styled.DropdownMenuItemContentWrapper, null, createElement(Styled.DropdownMenuItemChildrenWrapper, null, children), suffix && createElement(Styled.ItemSuffixWrapper, null, suffix))); }); const radioCheck = createElement(SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" }, createElement(Circle, { cx: 12, cy: 12, r: 3 })); export const DropdownMenuRadioItem = forwardRef(function DropdownMenuRadioItem({ suffix, children, hideOnClick = false, ...props }, ref) { const dropdownMenuContext = useContext(DropdownMenuContext); return createElement(Styled.DropdownMenuRadioItem, { ref: ref, ...props, accessibleWhenDisabled: true, hideOnClick: hideOnClick, store: dropdownMenuContext?.store }, createElement(Ariakit.MenuItemCheck, { store: dropdownMenuContext?.store, render: createElement(Styled.ItemPrefixWrapper, null) // Override some ariakit inline styles , style: { width: 'auto', height: 'auto' } }, createElement(Icon, { icon: radioCheck, size: 24 })), createElement(Styled.DropdownMenuItemContentWrapper, null, createElement(Styled.DropdownMenuItemChildrenWrapper, null, children), suffix && createElement(Styled.ItemSuffixWrapper, null, suffix))); }); export const DropdownMenuGroup = forwardRef(function DropdownMenuGroup(props, ref) { const dropdownMenuContext = useContext(DropdownMenuContext); return createElement(Styled.DropdownMenuGroup, { ref: ref, ...props, store: dropdownMenuContext?.store }); }); const UnconnectedDropdownMenu = (props, ref) => { var _props$placement; const { // Store props open, defaultOpen = false, onOpenChange, placement, // Menu trigger props trigger, // Menu props gutter, children, shift, modal = true, // From internal components context variant, // Rest ...otherProps } = useContextSystem(props, 'DropdownMenu'); const parentContext = useContext(DropdownMenuContext); const computedDirection = isRTL() ? 'rtl' : 'ltr'; // If an explicit value for the `placement` prop is not passed, // apply a default placement of `bottom-start` for the root dropdown, // and of `right-start` for nested dropdowns. let computedPlacement = (_props$placement = props.placement) !== null && _props$placement !== void 0 ? _props$placement : parentContext?.store ? 'right-start' : 'bottom-start'; // Swap left/right in case of RTL direction if (computedDirection === 'rtl') { if (/right/.test(computedPlacement)) { computedPlacement = computedPlacement.replace('right', 'left'); } else if (/left/.test(computedPlacement)) { computedPlacement = computedPlacement.replace('left', 'right'); } } const dropdownMenuStore = Ariakit.useMenuStore({ parent: parentContext?.store, open, defaultOpen, placement: computedPlacement, focusLoop: true, setOpen(willBeOpen) { onOpenChange?.(willBeOpen); }, rtl: computedDirection === 'rtl' }); const contextValue = useMemo(() => ({ store: dropdownMenuStore, variant }), [dropdownMenuStore, variant]); // Extract the side from the applied placement — useful for animations. const appliedPlacementSide = dropdownMenuStore.useState('placement').split('-')[0]; if (dropdownMenuStore.parent && !(isValidElement(trigger) && DropdownMenuItem === trigger.type)) { // eslint-disable-next-line no-console console.warn('For nested DropdownMenus, the `trigger` should always be a `DropdownMenuItem`.'); } const hideOnEscape = useCallback(event => { // Pressing Escape can cause unexpected consequences (ie. exiting // full screen mode on MacOs, close parent modals...). event.preventDefault(); // Returning `true` causes the menu to hide. return true; }, []); const wrapperProps = useMemo(() => ({ dir: computedDirection, style: { direction: computedDirection } }), [computedDirection]); return createElement(Fragment, null, createElement(Ariakit.MenuButton, { ref: ref, store: dropdownMenuStore, render: dropdownMenuStore.parent ? cloneElement(trigger, { // Add submenu arrow, unless a `suffix` is explicitly specified suffix: createElement(Fragment, null, trigger.props.suffix, createElement(Styled.SubmenuChevronIcon, { "aria-hidden": "true", icon: chevronRightSmall, size: 24, preserveAspectRatio: "xMidYMid slice" })) }) : trigger }), createElement(Styled.DropdownMenu, { ...otherProps, modal: modal, store: dropdownMenuStore // Root menu has an 8px distance from its trigger, // otherwise 0 (which causes the submenu to slightly overlap) , gutter: gutter !== null && gutter !== void 0 ? gutter : dropdownMenuStore.parent ? 0 : 8 // Align nested menu by the same (but opposite) amount // as the menu container's padding. , shift: shift !== null && shift !== void 0 ? shift : dropdownMenuStore.parent ? -4 : 0, hideOnHoverOutside: false, "data-side": appliedPlacementSide, variant: variant, wrapperProps: wrapperProps, hideOnEscape: hideOnEscape, unmountOnHide: true }, createElement(DropdownMenuContext.Provider, { value: contextValue }, children))); }; export const DropdownMenu = contextConnect(UnconnectedDropdownMenu, 'DropdownMenu'); export const DropdownMenuSeparator = forwardRef(function DropdownMenuSeparator(props, ref) { const dropdownMenuContext = useContext(DropdownMenuContext); return createElement(Styled.DropdownMenuSeparator, { ref: ref, ...props, store: dropdownMenuContext?.store, variant: dropdownMenuContext?.variant }); }); export const DropdownMenuItemLabel = forwardRef(function DropdownMenuItemLabel(props, ref) { return createElement(Styled.DropdownMenuItemLabel, { numberOfLines: 1, ref: ref, ...props }); }); export const DropdownMenuItemHelpText = forwardRef(function DropdownMenuItemHelpText(props, ref) { return createElement(Styled.DropdownMenuItemHelpText, { numberOfLines: 2, ref: ref, ...props }); }); //# sourceMappingURL=index.js.map