@fremtind/jkl-react-hooks
Version:
Jøkul react button components
146 lines (145 loc) • 3.19 kB
JavaScript
import { useEffect } from "react";
function useListNavigation({
ref
}) {
useEffect(() => {
let searchResetTimer;
const search = { keys: "" };
const list = ref.current;
const handler = (event) => {
if (list) {
handleListKeyNav({ list, event, search, searchResetTimer });
}
};
if (list) {
list.addEventListener("keydown", handler);
}
return () => {
if (list) {
list.removeEventListener("keydown", handler);
}
};
}, [ref]);
}
function handleMoveTo(direction, { event, list, currentFocus }) {
event.preventDefault();
moveFocusTo(direction, list, currentFocus);
}
function handleListKeyNav({
list,
event,
search,
searchResetTimer
}) {
const { key, target } = event;
const currentFocus = target;
const moveDetails = {
event,
list,
currentFocus
};
switch (key) {
case "ArrowUp":
case "PageUp":
handleMoveTo("prev", moveDetails);
break;
case "ArrowDown":
case "PageDown":
handleMoveTo("next", moveDetails);
break;
case "Home":
handleMoveTo("first", moveDetails);
break;
case "End":
handleMoveTo("last", moveDetails);
break;
case "Tab":
event.preventDefault();
break;
case "Enter":
case " ":
break;
default:
if (search !== void 0) {
const searchResult = findItem({
list,
key,
search,
searchResetTimer
});
if (searchResult) {
searchResult.focus();
}
}
break;
}
}
function moveFocusTo(direction, list, current) {
const thisOption = current;
switch (direction) {
case "prev":
const prevOption = thisOption && thisOption.previousElementSibling;
if (prevOption) {
prevOption.focus();
}
break;
case "next":
const nextOption = thisOption && thisOption.nextElementSibling;
if (nextOption) {
nextOption.focus();
}
break;
case "first":
const firstItem = list.querySelector('[role="option"]');
if (firstItem) {
firstItem.focus();
}
break;
case "last":
const listItems = list.querySelectorAll('[role="option"]');
if (listItems.length) {
listItems[listItems.length - 1].focus();
}
break;
}
}
function findItem({
list,
key,
search,
searchResetTimer
}) {
const listItems = list.querySelectorAll('[role="option"]');
if (!listItems.length)
return null;
if (search) {
search.keys = search.keys.concat(key);
resetWhenIdle(search, searchResetTimer);
for (let n = 0; n < listItems.length; n++) {
const label = listItems[n].innerText;
if (label && label.toLowerCase().indexOf(search.keys) === 0) {
return listItems[n];
}
}
}
return null;
}
function resetWhenIdle(search, timer) {
if (timer) {
clearTimeout(timer);
timer = void 0;
}
timer = setTimeout(
() => {
search ? search.keys = "" : search = { keys: "" };
timer = void 0;
},
500,
search,
timer
);
}
export {
useListNavigation
};
//# sourceMappingURL=useListNavigation.js.map