UNPKG

@1771technologies/lytenyte-pro

Version:

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

53 lines (52 loc) 2.25 kB
import { useEffect, useRef } from "react"; import { useSubmenuContext } from "./submenu/submenu-context.js"; import { getNearestMatching } from "@1771technologies/lytenyte-shared"; import { dispatchActivate, dispatchClose, dispatchDeactivate, getSubmenuRoots } from "./dom.js"; export function useMenu(el) { const sub = useSubmenuContext(); const mouseOutTime = useRef(null); useEffect(() => { if (!el || sub) return; const controller = new AbortController(); document.addEventListener("keydown", (ev) => { if (el.contains(document.activeElement)) return; if (ev.key !== "ArrowDown" && ev.key !== "ArrowUp") return; const menu = Array.from(document.querySelectorAll("[data-ln-menu]")).at(-1); if (!menu) return; const firstItem = menu.querySelector("[data-ln-menu-item"); firstItem.focus?.(); }, { signal: controller.signal }); el.addEventListener("mouseover", (ev) => { const target = ev.target; const item = getNearestMatching(target, (el) => el.getAttribute("data-ln-menu-item") === "true"); if (!item) return; if (item) { const itemRoots = getSubmenuRoots(item); const menus = el.querySelectorAll('[data-ln-submenu-root="true"]'); menus.forEach((m) => { if (!itemRoots.includes(m)) dispatchClose(m); }); } if (mouseOutTime.current) clearTimeout(mouseOutTime.current); dispatchActivate(item); }, { signal: controller.signal }); el.addEventListener("mouseout", (ev) => { const target = ev.target; const item = getNearestMatching(target, (el) => el.getAttribute("data-ln-menu-item") === "true"); if (!item) return; mouseOutTime.current = setTimeout(() => { dispatchDeactivate(item); mouseOutTime.current = null; }); }, { signal: controller.signal }); return () => controller.abort(); }, [el, sub]); }