@atlaskit/dropdown-menu
Version:
A dropdown menu displays a list of actions or options to a user.
81 lines (79 loc) • 2.59 kB
JavaScript
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;