@amsterdam/design-system-react
Version:
All React components from the Amsterdam Design System. Use it to compose pages in your website or application.
57 lines (56 loc) • 2.19 kB
JavaScript
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/
export const KeyboardKeys = {
ArrowDown: 'ArrowDown',
ArrowLeft: 'ArrowLeft',
ArrowRight: 'ArrowRight',
ArrowUp: 'ArrowUp',
End: 'End',
Home: 'Home',
};
const FOCUSABLE_ELEMENTS = [
'a[href]:not([disabled])',
'button:not([disabled])',
'textarea:not([disabled])',
'input[type="text"]:not([disabled])',
'input[type="radio"]:not([disabled])',
'input[type="checkbox"]:not([disabled])',
'select:not([disabled])',
];
export const useKeyboardFocus = (ref, options) => {
const { directChildrenOnly = false, focusableElements = FOCUSABLE_ELEMENTS, horizontally = false, rotating = false, } = options;
const nextKey = horizontally ? KeyboardKeys.ArrowRight : KeyboardKeys.ArrowDown;
const prevKey = horizontally ? KeyboardKeys.ArrowLeft : KeyboardKeys.ArrowUp;
const keyDown = (e) => {
if (!ref.current)
return;
const element = ref.current;
const { activeElement } = document;
const directChildSelector = directChildrenOnly ? ':scope > ' : '';
const focusableEls = Array.from(element.querySelectorAll(`${directChildSelector}${focusableElements.join(`, ${directChildSelector}`)}`));
const getIndex = (el) => (el && focusableEls.includes(el) ? focusableEls.indexOf(el) : -1);
let targetElement;
switch (e.key) {
case KeyboardKeys.End:
targetElement = focusableEls[focusableEls.length - 1];
break;
case KeyboardKeys.Home:
targetElement = focusableEls[0];
break;
case nextKey:
targetElement = focusableEls[getIndex(activeElement) + 1] || (rotating ? focusableEls[0] : undefined);
break;
case prevKey:
targetElement =
focusableEls[getIndex(activeElement) - 1] || (rotating ? focusableEls[focusableEls.length - 1] : undefined);
break;
}
if (targetElement instanceof HTMLElement) {
targetElement.focus();
e.preventDefault();
}
};
return { keyDown };
};