UNPKG

@1771technologies/lytenyte-pro

Version:

Blazingly fast headless React data grid with 100s of features.

51 lines (50 loc) 2.83 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { forwardRef, useEffect, useState } from "react"; import { useListboxContext } from "./context.js"; import { getTabbables } from "@1771technologies/lytenyte-shared"; import { useCombinedRefs } from "@1771technologies/lytenyte-core/yinternal"; export const Panel = forwardRef(function Panel(props, forwarded) { const [ref, setRef] = useState(); const combinedRef = useCombinedRefs(setRef, forwarded); const ctx = useListboxContext(); useEffect(() => { if (!ref) return; const controller = new AbortController(); const isVert = ctx.orientation === "vertical"; const nextKey = isVert ? "ArrowDown" : ctx.rtl ? "ArrowLeft" : "ArrowRight"; const prevKey = isVert ? "ArrowUp" : ctx.rtl ? "ArrowRight" : "ArrowLeft"; ref.addEventListener("keydown", (ev) => { if (ev.key === "Tab") { if (ref.contains(document.activeElement) || document.activeElement === ref) { ref.inert = true; setTimeout(() => { ref.inert = false; }, 10); } } if (ev.key === nextKey) { const items = getTabbables(ref).filter((c) => c.getAttribute("data-ln-listbox-item") === "true"); const currentIndex = items.findIndex((c) => c.contains(document.activeElement) || c === document.activeElement); const focusItem = currentIndex === -1 ? items[0] : (items[currentIndex + 1] ?? items[0]); if (!focusItem) return; focusItem.focus(); ev.preventDefault(); ev.stopPropagation(); } if (ev.key === prevKey) { const items = getTabbables(ref).filter((c) => c.getAttribute("data-ln-listbox-item") === "true"); const currentIndex = items.findIndex((c) => c.contains(document.activeElement) || c === document.activeElement); const focusItem = currentIndex === -1 ? items.at(-1) : (items[currentIndex - 1] ?? items.at(-1)); if (!focusItem) return; focusItem.focus(); ev.preventDefault(); ev.stopPropagation(); } }, { signal: controller.signal }); return () => controller.abort(); }, [ctx.orientation, ctx.rtl, ref]); return (_jsxs("div", { "aria-label": "Generic listbox with keyboard navigable items", ...props, ref: combinedRef, "data-ln-listbox-panel": true, role: "listbox", tabIndex: 0, children: [props.children, _jsx("div", { "aria-label": "a presentational item that can be ignored", tabIndex: 0, onFocus: () => ref?.focus(), role: "option" })] })); });