@atlaskit/dropdown-menu
Version:
A dropdown menu displays a list of actions or options to a user.
111 lines (107 loc) • 4.68 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { KEY_DOWN, KEY_END, KEY_HOME, KEY_TAB, KEY_UP } from '@atlaskit/ds-lib/keycodes';
import { fg } from '@atlaskit/platform-feature-flags';
import { PREFIX } from './use-generated-id';
var actionMap = _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, KEY_DOWN, 'next'), KEY_UP, 'prev'), KEY_HOME, 'first'), KEY_END, 'last');
/**
* `currentFocusedIdx + 1` will not work if the next focusable element
* is disabled. So, we need to iterate through the following menu items
* to find one that isn't disabled. If all following elements are disabled,
* return undefined.
*/
var getNextFocusableElement = function getNextFocusableElement(refs, currentFocusedIdx) {
for (var i = 0; i < refs.length - 1; i++) {
if (currentFocusedIdx + 1 === refs.length) {
currentFocusedIdx = 0;
} else {
currentFocusedIdx++;
}
var element = refs[currentFocusedIdx].current;
var isValid = !!element && !element.hasAttribute('disabled');
if (isValid) {
return element;
}
}
};
/**
* `currentFocusedIdx - 1` will not work if the prev focusable element
* is disabled. So, we need to iterate through the previous menu items
* to find one that isn't disabled. If all previous elements are disabled,
* return undefined.
*/
var getPrevFocusableElement = function getPrevFocusableElement(refs, currentFocusedIdx) {
for (var i = 0; i < refs.length - 1; i++) {
if (currentFocusedIdx === 0) {
currentFocusedIdx = refs.length - 1;
} else {
currentFocusedIdx--;
}
var element = refs[currentFocusedIdx].current;
var isValid = !!element && !element.hasAttribute('disabled');
if (isValid) {
return element;
}
}
};
export default function handleFocus(refs, isLayerDisabled, onClose) {
return function (e) {
var _refs$current;
var currentRefs = (_refs$current = refs.current) !== null && _refs$current !== void 0 ? _refs$current : [];
var currentFocusedIdx = currentRefs.findIndex(function (_ref) {
var _document$activeEleme;
var el = _ref.current;
return el && ((_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.isSameNode(el));
});
if (fg('platform_dst_popup-disable-focuslock')) {
var _document$activeEleme2;
// if we use a popup as a nested dropdown, we must prevent the dropdown from closing.
var isNestedDropdown = !!((_document$activeEleme2 = document.activeElement) !== null && _document$activeEleme2 !== void 0 && _document$activeEleme2.closest("[id^=".concat(PREFIX, "]")));
if (isLayerDisabled() && isNestedDropdown) {
if (e.key === KEY_TAB && !e.shiftKey) {
onClose(e);
}
// if it is a nested dropdown and the level of the given dropdown is not the current level,
// we don't need to have focus on it
return;
}
} else {
if (isLayerDisabled()) {
if (e.key === KEY_TAB && !e.shiftKey) {
onClose(e);
}
// if it is a nested dropdown and the level of the given dropdown is not the current level,
// we don't need to have focus on it
return;
}
}
var action = actionMap[e.key];
switch (action) {
case 'next':
// Always cancelling the event to prevent scrolling
e.preventDefault();
var nextFocusableElement = getNextFocusableElement(currentRefs, currentFocusedIdx);
nextFocusableElement === null || nextFocusableElement === void 0 || nextFocusableElement.focus();
break;
case 'prev':
// Always cancelling the event to prevent scrolling
e.preventDefault();
var prevFocusableElement = getPrevFocusableElement(currentRefs, currentFocusedIdx);
prevFocusableElement === null || prevFocusableElement === void 0 || prevFocusableElement.focus();
break;
case 'first':
e.preventDefault();
// Search for first non-disabled element if first element is disabled
var firstFocusableElement = getNextFocusableElement(currentRefs, -1);
firstFocusableElement === null || firstFocusableElement === void 0 || firstFocusableElement.focus();
break;
case 'last':
e.preventDefault();
// Search for last non-disabled element if last element is disabled
var lastFocusableElement = getPrevFocusableElement(currentRefs, currentRefs.length);
lastFocusableElement === null || lastFocusableElement === void 0 || lastFocusableElement.focus();
break;
default:
return;
}
};
}