@primer/react
Version:
An implementation of GitHub's Primer Design System using React
171 lines (170 loc) • 8.05 kB
JavaScript
import { FocusKeys, useFocusZone } from "../hooks/useFocusZone.js";
import { getScrollContainer } from "../utils/scroll.js";
import { c } from "react-compiler-runtime";
//#region src/TreeView/useRovingTabIndex.ts
function useRovingTabIndex(t0, t1) {
const $ = c(12);
const { containerRef, mouseDownRef, preventScroll: t2 } = t0;
const preventScroll = t2 === void 0 ? true : t2;
let t3;
if ($[0] !== t1) {
t3 = t1 === void 0 ? [] : t1;
$[0] = t1;
$[1] = t3;
} else t3 = $[1];
const dependencies = t3;
let t4;
if ($[2] !== containerRef || $[3] !== mouseDownRef) {
t4 = () => {
var _containerRef$current, _containerRef$current2, _containerRef$current3;
if (mouseDownRef !== null && mouseDownRef !== void 0 && mouseDownRef.current) return;
const currentItem = (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.querySelector("[aria-current]");
const firstItem = (_containerRef$current2 = containerRef.current) === null || _containerRef$current2 === void 0 ? void 0 : _containerRef$current2.querySelector("[role=\"treeitem\"]");
if (currentItem instanceof HTMLElement) return currentItem;
if (document.activeElement instanceof HTMLElement && (_containerRef$current3 = containerRef.current) !== null && _containerRef$current3 !== void 0 && _containerRef$current3.contains(document.activeElement) && document.activeElement.getAttribute("role") === "treeitem") return document.activeElement;
return firstItem instanceof HTMLElement ? firstItem : void 0;
};
$[2] = containerRef;
$[3] = mouseDownRef;
$[4] = t4;
} else t4 = $[4];
let t5;
if ($[5] !== containerRef || $[6] !== preventScroll || $[7] !== t4) {
t5 = {
containerRef,
bindKeys: FocusKeys.ArrowVertical | FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd | FocusKeys.Backspace | FocusKeys.PageUpDown,
preventScroll,
getNextFocusable: _temp,
focusInStrategy: t4
};
$[5] = containerRef;
$[6] = preventScroll;
$[7] = t4;
$[8] = t5;
} else t5 = $[8];
let t6;
if ($[9] !== dependencies || $[10] !== preventScroll) {
t6 = [preventScroll, ...dependencies];
$[9] = dependencies;
$[10] = preventScroll;
$[11] = t6;
} else t6 = $[11];
useFocusZone(t5, t6);
}
function _temp(direction, from, event) {
var _getNextFocusableElem;
if (!(from instanceof HTMLElement)) return;
try {
if (from.closest("dialog:modal")) return;
} catch {}
return (_getNextFocusableElem = getNextFocusableElement(from, event)) !== null && _getNextFocusableElem !== void 0 ? _getNextFocusableElem : from;
}
function getNextFocusableElement(activeElement, event) {
switch (`${getElementState(activeElement)} ${event.key}`) {
case "open ArrowRight": return getFirstChildElement(activeElement);
case "open ArrowLeft": return;
case "closed ArrowRight": return;
case "closed ArrowLeft": return getParentElement(activeElement);
case "end ArrowRight": return;
case "end ArrowLeft": return getParentElement(activeElement);
}
switch (event.key) {
case "ArrowUp": return getVisibleElement(activeElement, "previous");
case "ArrowDown": return getVisibleElement(activeElement, "next");
case "Backspace": return getParentElement(activeElement);
case "Home": return getFirstElement(activeElement);
case "End": return getLastElement(activeElement);
case "PageUp": return getPreviousPageElement(activeElement);
case "PageDown": return getNextPageElement(activeElement);
}
}
function getElementState(element) {
if (element.getAttribute("role") !== "treeitem") throw new Error("Element is not a treeitem");
switch (element.getAttribute("aria-expanded")) {
case "true": return "open";
case "false": return "closed";
default: return "end";
}
}
function getVisibleElement(element, direction) {
const root = element.closest("[role=tree]");
if (!root) return;
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, (node) => {
if (!(node instanceof HTMLElement)) return NodeFilter.FILTER_SKIP;
return node.getAttribute("role") === "treeitem" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
});
let current = walker.firstChild();
while (current !== element) current = walker.nextNode();
let next = direction === "next" ? walker.nextNode() : walker.previousNode();
while (next instanceof HTMLElement && (_next$parentElement = next.parentElement) !== null && _next$parentElement !== void 0 && _next$parentElement.closest("[role=treeitem][aria-expanded=false]")) {
var _next$parentElement;
next = direction === "next" ? walker.nextNode() : walker.previousNode();
}
return next instanceof HTMLElement ? next : void 0;
}
function getFirstChildElement(element) {
const firstChild = element.querySelector("[role=treeitem]");
return firstChild instanceof HTMLElement ? firstChild : void 0;
}
function getParentElement(element) {
const group = element.closest("[role=group]");
const parent = group === null || group === void 0 ? void 0 : group.closest("[role=treeitem]");
return parent instanceof HTMLElement ? parent : void 0;
}
function getFirstElement(element) {
const root = element.closest("[role=tree]");
const first = root === null || root === void 0 ? void 0 : root.querySelector("[role=treeitem]");
return first instanceof HTMLElement ? first : void 0;
}
function getLastElement(element) {
const root = element.closest("[role=tree]");
if (!root) return;
const items = Array.from(root.querySelectorAll("[role=treeitem]"));
if (items.length === 0) return;
let index = items.length - 1;
let last = items[index];
while (index > 0 && last instanceof HTMLElement && (_last$parentElement = last.parentElement) !== null && _last$parentElement !== void 0 && _last$parentElement.closest("[role=treeitem][aria-expanded=false]")) {
var _last$parentElement;
index -= 1;
last = items[index];
}
return last instanceof HTMLElement ? last : void 0;
}
const defaultSize = { height: 32 };
/**
* Determine the page size for the given tree based on an item in the tree. We
* estimate this size by trying to see how many items will fit in the given
* tree. If the tree is within a scroll container, we will use the height of
* that container. Otherwise, we'll use the current window height
*/
function getPageSize(root, item) {
var _item$getBoundingClie, _scrollContainer$clie;
const scrollContainer = getScrollContainer(root);
const { height: itemHeight } = (_item$getBoundingClie = item === null || item === void 0 ? void 0 : item.getBoundingClientRect()) !== null && _item$getBoundingClie !== void 0 ? _item$getBoundingClie : defaultSize;
const availableHeight = (_scrollContainer$clie = scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.clientHeight) !== null && _scrollContainer$clie !== void 0 ? _scrollContainer$clie : window.innerHeight;
return Math.floor(availableHeight / itemHeight);
}
function getNextPageElement(element) {
const root = element.closest("[role=\"tree\"]");
if (!root) return;
const items = Array.from(root.querySelectorAll("[role=\"treeitem\"]"));
if (items.length === 0) return;
const itemLabel = items[0].firstElementChild;
const pageSize = getPageSize(root, itemLabel);
const page = Math.floor(items.indexOf(element) / pageSize);
const offset = items.indexOf(element) - pageSize * page;
return items[Math.min(items.length - 1, (page + 1) * pageSize + offset)];
}
function getPreviousPageElement(element) {
const root = element.closest("[role=\"tree\"]");
if (!root) return;
const items = Array.from(root.querySelectorAll("[role=\"treeitem\"]"));
if (items.length === 0) return;
const itemLabel = items[0].firstElementChild;
const pageSize = getPageSize(root, itemLabel);
const page = Math.floor(items.indexOf(element) / pageSize);
const offset = items.indexOf(element) - pageSize * page;
return items[Math.max(0, (page - 1) * pageSize + offset)];
}
//#endregion
export { getElementState, getFirstChildElement, getFirstElement, getLastElement, getNextFocusableElement, getParentElement, getVisibleElement, useRovingTabIndex };