|
- "use strict";
-
- var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
-
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
-
- exports.__esModule = true;
- exports["default"] = void 0;
-
- var _matches = _interopRequireDefault(require("dom-helpers/matches"));
-
- var _querySelectorAll = _interopRequireDefault(require("dom-helpers/querySelectorAll"));
-
- var _react = _interopRequireWildcard(require("react"));
-
- var _propTypes = _interopRequireDefault(require("prop-types"));
-
- var _uncontrollable = require("uncontrollable");
-
- var _usePrevious = _interopRequireDefault(require("@restart/hooks/usePrevious"));
-
- var _useCallbackRef2 = _interopRequireDefault(require("@restart/hooks/useCallbackRef"));
-
- var _useForceUpdate = _interopRequireDefault(require("@restart/hooks/useForceUpdate"));
-
- var _useEventCallback = _interopRequireDefault(require("@restart/hooks/useEventCallback"));
-
- var _DropdownContext = _interopRequireDefault(require("./DropdownContext"));
-
- var _DropdownMenu = _interopRequireDefault(require("./DropdownMenu"));
-
- var _DropdownToggle = _interopRequireDefault(require("./DropdownToggle"));
-
- var propTypes = {
- /**
- * A render prop that returns the root dropdown element. The `props`
- * argument should spread through to an element containing _both_ the
- * menu and toggle in order to handle keyboard events for focus management.
- *
- * @type {Function ({
- * props: {
- * onKeyDown: (SyntheticEvent) => void,
- * },
- * }) => React.Element}
- */
- children: _propTypes["default"].func.isRequired,
-
- /**
- * Determines the direction and location of the Menu in relation to it's Toggle.
- */
- drop: _propTypes["default"].oneOf(['up', 'left', 'right', 'down']),
-
- /**
- * Controls the focus behavior for when the Dropdown is opened. Set to
- * `true` to always focus the first menu item, `keyboard` to focus only when
- * navigating via the keyboard, or `false` to disable completely
- *
- * The Default behavior is `false` **unless** the Menu has a `role="menu"`
- * where it will default to `keyboard` to match the recommended [ARIA Authoring practices](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton).
- */
- focusFirstItemOnShow: _propTypes["default"].oneOf([false, true, 'keyboard']),
-
- /**
- * A css slector string that will return __focusable__ menu items.
- * Selectors should be relative to the menu component:
- * e.g. ` > li:not('.disabled')`
- */
- itemSelector: _propTypes["default"].string.isRequired,
-
- /**
- * Align the menu to the 'end' side of the placement side of the Dropdown toggle. The default placement is `top-start` or `bottom-start`.
- */
- alignEnd: _propTypes["default"].bool,
-
- /**
- * Whether or not the Dropdown is visible.
- *
- * @controllable onToggle
- */
- show: _propTypes["default"].bool,
-
- /**
- * Sets the initial show position of the Dropdown.
- */
- defaultShow: _propTypes["default"].bool,
-
- /**
- * A callback fired when the Dropdown wishes to change visibility. Called with the requested
- * `show` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`.
- *
- * ```js
- * function(
- * isOpen: boolean,
- * event: SyntheticEvent,
- * ): void
- * ```
- *
- * @controllable show
- */
- onToggle: _propTypes["default"].func
- };
- var defaultProps = {
- itemSelector: '* > *'
- };
- /**
- * `Dropdown` is set of structural components for building, accessible dropdown menus with close-on-click,
- * keyboard navigation, and correct focus handling. As with all the react-overlay's
- * components its BYOS (bring your own styles). Dropdown is primarily
- * built from three base components, you should compose to build your Dropdowns.
- *
- * - `Dropdown`, which wraps the menu and toggle, and handles keyboard navigation
- * - `Dropdown.Toggle` generally a button that triggers the menu opening
- * - `Dropdown.Menu` The overlaid, menu, positioned to the toggle with PopperJs
- */
-
- function Dropdown(_ref) {
- var drop = _ref.drop,
- alignEnd = _ref.alignEnd,
- defaultShow = _ref.defaultShow,
- rawShow = _ref.show,
- rawOnToggle = _ref.onToggle,
- itemSelector = _ref.itemSelector,
- focusFirstItemOnShow = _ref.focusFirstItemOnShow,
- children = _ref.children;
- var forceUpdate = (0, _useForceUpdate["default"])();
-
- var _useUncontrolled = (0, _uncontrollable.useUncontrolled)({
- defaultShow: defaultShow,
- show: rawShow,
- onToggle: rawOnToggle
- }, {
- show: 'onToggle'
- }),
- show = _useUncontrolled.show,
- onToggle = _useUncontrolled.onToggle;
-
- var _useCallbackRef = (0, _useCallbackRef2["default"])(),
- toggleElement = _useCallbackRef[0],
- setToggle = _useCallbackRef[1]; // We use normal refs instead of useCallbackRef in order to populate the
- // the value as quickly as possible, otherwise the effect to focus the element
- // may run before the state value is set
-
-
- var menuRef = (0, _react.useRef)();
- var menuElement = menuRef.current;
- var setMenu = (0, _react.useCallback)(function (ref) {
- menuRef.current = ref; // ensure that a menu set triggers an update for consumers
-
- forceUpdate();
- }, [forceUpdate]);
- var lastShow = (0, _usePrevious["default"])(show);
- var lastSourceEvent = (0, _react.useRef)(null);
- var focusInDropdown = (0, _react.useRef)(false);
- var toggle = (0, _react.useCallback)(function (event) {
- onToggle(!show, event);
- }, [onToggle, show]);
- var context = (0, _react.useMemo)(function () {
- return {
- toggle: toggle,
- drop: drop,
- show: show,
- alignEnd: alignEnd,
- menuElement: menuElement,
- toggleElement: toggleElement,
- setMenu: setMenu,
- setToggle: setToggle
- };
- }, [toggle, drop, show, alignEnd, menuElement, toggleElement, setMenu, setToggle]);
-
- if (menuElement && lastShow && !show) {
- focusInDropdown.current = menuElement.contains(document.activeElement);
- }
-
- var focusToggle = (0, _useEventCallback["default"])(function () {
- if (toggleElement && toggleElement.focus) {
- toggleElement.focus();
- }
- });
- var maybeFocusFirst = (0, _useEventCallback["default"])(function () {
- var type = lastSourceEvent.current;
- var focusType = focusFirstItemOnShow;
-
- if (focusType == null) {
- focusType = menuRef.current && (0, _matches["default"])(menuRef.current, '[role=menu]') ? 'keyboard' : false;
- }
-
- if (focusType === false || focusType === 'keyboard' && !/^key.+$/.test(type)) {
- return;
- }
-
- var first = (0, _querySelectorAll["default"])(menuRef.current, itemSelector)[0];
- if (first && first.focus) first.focus();
- });
- (0, _react.useEffect)(function () {
- if (show) maybeFocusFirst();else if (focusInDropdown.current) {
- focusInDropdown.current = false;
- focusToggle();
- } // only `show` should be changing
- }, [show, focusInDropdown, focusToggle, maybeFocusFirst]);
- (0, _react.useEffect)(function () {
- lastSourceEvent.current = null;
- });
-
- var getNextFocusedChild = function getNextFocusedChild(current, offset) {
- if (!menuRef.current) return null;
- var items = (0, _querySelectorAll["default"])(menuRef.current, itemSelector);
- var index = items.indexOf(current) + offset;
- index = Math.max(0, Math.min(index, items.length));
- return items[index];
- };
-
- var handleKeyDown = function handleKeyDown(event) {
- var key = event.key,
- target = event.target; // Second only to https://github.com/twbs/bootstrap/blob/8cfbf6933b8a0146ac3fbc369f19e520bd1ebdac/js/src/dropdown.js#L400
- // in inscrutability
-
- var isInput = /input|textarea/i.test(target.tagName);
-
- if (isInput && (key === ' ' || key !== 'Escape' && menuRef.current && menuRef.current.contains(target))) {
- return;
- }
-
- lastSourceEvent.current = event.type;
-
- switch (key) {
- case 'ArrowUp':
- {
- var next = getNextFocusedChild(target, -1);
- if (next && next.focus) next.focus();
- event.preventDefault();
- return;
- }
-
- case 'ArrowDown':
- event.preventDefault();
-
- if (!show) {
- toggle(event);
- } else {
- var _next = getNextFocusedChild(target, 1);
-
- if (_next && _next.focus) _next.focus();
- }
-
- return;
-
- case 'Escape':
- case 'Tab':
- onToggle(false, event);
- break;
-
- default:
- }
- };
-
- return _react["default"].createElement(_DropdownContext["default"].Provider, {
- value: context
- }, children({
- props: {
- onKeyDown: handleKeyDown
- }
- }));
- }
-
- Dropdown.displayName = 'ReactOverlaysDropdown';
- Dropdown.propTypes = propTypes;
- Dropdown.defaultProps = defaultProps;
- Dropdown.Menu = _DropdownMenu["default"];
- Dropdown.Toggle = _DropdownToggle["default"];
- var _default = Dropdown;
- exports["default"] = _default;
- module.exports = exports.default;
|