UNPKG

@wordpress/components

Version:
178 lines (151 loc) 5.64 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.Dropdown = void 0; var _element = require("@wordpress/element"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _classnames = _interopRequireDefault(require("classnames")); var _compose = require("@wordpress/compose"); var _deprecated = _interopRequireDefault(require("@wordpress/deprecated")); var _popover = _interopRequireDefault(require("../popover")); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ function useObservableState(initialState, onStateChange) { const [state, setState] = (0, _element.useState)(initialState); return [state, value => { setState(value); if (onStateChange) { onStateChange(value); } }]; } function UnforwardedDropdown(_ref, forwardedRef) { let { renderContent, renderToggle, className, contentClassName, expandOnMobile, headerTitle, focusOnMount, popoverProps, onClose, onToggle, style, // Deprecated props position } = _ref; if (position !== undefined) { (0, _deprecated.default)('`position` prop in wp.components.Dropdown', { since: '6.2', alternative: '`popoverProps.placement` prop', hint: 'Note that the `position` prop will override any values passed through the `popoverProps.placement` prop.' }); } // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. const [fallbackPopoverAnchor, setFallbackPopoverAnchor] = (0, _element.useState)(null); const containerRef = (0, _element.useRef)(); const [isOpen, setIsOpen] = useObservableState(false, onToggle); (0, _element.useEffect)(() => () => { if (onToggle && isOpen) { onToggle(false); } }, [onToggle, isOpen]); function toggle() { setIsOpen(!isOpen); } /** * Closes the popover when focus leaves it unless the toggle was pressed or * focus has moved to a separate dialog. The former is to let the toggle * handle closing the popover and the latter is to preserve presence in * case a dialog has opened, allowing focus to return when it's dismissed. */ function closeIfFocusOutside() { var _ownerDocument$active; if (!containerRef.current) { return; } const { ownerDocument } = containerRef.current; const dialog = ownerDocument === null || ownerDocument === void 0 ? void 0 : (_ownerDocument$active = ownerDocument.activeElement) === null || _ownerDocument$active === void 0 ? void 0 : _ownerDocument$active.closest('[role="dialog"]'); if (!containerRef.current.contains(ownerDocument.activeElement) && (!dialog || dialog.contains(containerRef.current))) { close(); } } function close() { if (onClose) { onClose(); } setIsOpen(false); } const args = { isOpen, onToggle: toggle, onClose: close }; const popoverPropsHaveAnchor = !!(popoverProps !== null && popoverProps !== void 0 && popoverProps.anchor) || // Note: `anchorRef`, `getAnchorRect` and `anchorRect` are deprecated and // be removed from `Popover` from WordPress 6.3 !!(popoverProps !== null && popoverProps !== void 0 && popoverProps.anchorRef) || !!(popoverProps !== null && popoverProps !== void 0 && popoverProps.getAnchorRect) || !!(popoverProps !== null && popoverProps !== void 0 && popoverProps.anchorRect); return (0, _element.createElement)("div", { className: (0, _classnames.default)('components-dropdown', className), ref: (0, _compose.useMergeRefs)([containerRef, forwardedRef, setFallbackPopoverAnchor]) // Some UAs focus the closest focusable parent when the toggle is // clicked. Making this div focusable ensures such UAs will focus // it and `closeIfFocusOutside` can tell if the toggle was clicked. , tabIndex: -1, style: style }, renderToggle(args), isOpen && (0, _element.createElement)(_popover.default, (0, _extends2.default)({ position: position, onClose: close, onFocusOutside: closeIfFocusOutside, expandOnMobile: expandOnMobile, headerTitle: headerTitle, focusOnMount: focusOnMount // This value is used to ensure that the dropdowns // align with the editor header by default. , offset: 13, anchor: !popoverPropsHaveAnchor ? fallbackPopoverAnchor : undefined }, popoverProps, { className: (0, _classnames.default)('components-dropdown__content', popoverProps === null || popoverProps === void 0 ? void 0 : popoverProps.className, contentClassName) }), renderContent(args))); } /** * Renders a button that opens a floating content modal when clicked. * * ```jsx * import { Button, Dropdown } from '@wordpress/components'; * * const MyDropdown = () => ( * <Dropdown * className="my-container-class-name" * contentClassName="my-dropdown-content-classname" * popoverProps={ { placement: 'bottom-start' } } * renderToggle={ ( { isOpen, onToggle } ) => ( * <Button * variant="primary" * onClick={ onToggle } * aria-expanded={ isOpen } * > * Toggle Dropdown! * </Button> * ) } * renderContent={ () => <div>This is the content of the dropdown.</div> } * /> * ); * ``` */ const Dropdown = (0, _element.forwardRef)(UnforwardedDropdown); exports.Dropdown = Dropdown; var _default = Dropdown; exports.default = _default; //# sourceMappingURL=index.js.map