UNPKG

@atlaskit/dropdown-menu

Version:

A dropdown menu displays a list of actions or options to a user.

81 lines (79 loc) 2.59 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import React, { createContext, useCallback, useEffect, useRef, useState } from 'react'; import { bind } from 'bind-event-listener'; import __noop from '@atlaskit/ds-lib/noop'; import { useLayering } from '@atlaskit/layering'; import handleFocus from '../utils/handle-focus'; /** * * * Context provider which maintains the list of focusable elements and a method to * register new menu items. * This list drives the keyboard navigation of the menu. * */ export var FocusManagerContext = /*#__PURE__*/createContext({ menuItemRefs: [], registerRef: __noop }); /** * Focus manager logic. */ var FocusManager = function FocusManager(_ref) { var children = _ref.children, onClose = _ref.onClose; var menuItemRefs = useRef([]); // Used to force a re-render only var _useState = useState(0), _useState2 = _slicedToArray(_useState, 2), refresh = _useState2[0], setRefresh = _useState2[1]; var registerMode = useRef('ordered'); registerMode.current = 'ordered'; var registerRef = useCallback(function (ref) { if (menuItemRefs.current.includes(ref)) { return; } switch (registerMode.current) { case 'ordered': menuItemRefs.current.push(ref); break; case 'unordered': // Reset and force a rerender registerMode.current = 'regenerate'; menuItemRefs.current = []; setRefresh(refresh + 1); break; case 'regenerate': // Ignore registrations until the next render cycle break; default: throw new Error("Unexpected case of ".concat(registerMode.current)); } }, // Updating register ref on force reload will cause `useRegisterItemWithFocusManager` to re-register [refresh]); var _useLayering = useLayering(), isLayerDisabled = _useLayering.isLayerDisabled; // Intentionally rebinding on each render useEffect(function () { if (registerMode.current === 'ordered') { // Use effect is called after rendering is complete and useEffects of the children a called first registerMode.current = 'unordered'; } }); useEffect(function () { return bind(window, { type: 'keydown', listener: handleFocus(menuItemRefs, isLayerDisabled, onClose) }); }, [isLayerDisabled, onClose]); var contextValue = { menuItemRefs: menuItemRefs.current, registerRef: registerRef }; return /*#__PURE__*/React.createElement(FocusManagerContext.Provider, { value: contextValue }, children); }; export default FocusManager;