@wordpress/components
Version:
UI components for WordPress.
164 lines (144 loc) • 5.17 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import { createElement } from "@wordpress/element";
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { forwardRef, useEffect, useRef, useState } from '@wordpress/element';
import { useMergeRefs } from '@wordpress/compose';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import Popover from '../popover';
function useObservableState(initialState, onStateChange) {
const [state, setState] = 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) {
deprecated('`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] = useState(null);
const containerRef = useRef();
const [isOpen, setIsOpen] = useObservableState(false, onToggle);
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 createElement("div", {
className: classnames('components-dropdown', className),
ref: 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 && createElement(Popover, _extends({
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: classnames('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> }
* />
* );
* ```
*/
export const Dropdown = forwardRef(UnforwardedDropdown);
export default Dropdown;
//# sourceMappingURL=index.js.map