@1771technologies/lytenyte-pro
Version:
Blazingly fast headless React data grid with 100s of features.
71 lines (70 loc) • 3.26 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { forwardRef, useEffect, useRef, useState } from "react";
import { useSubmenuContext } from "./submenu-context.js";
import { getNearestMatching, getTabbables } from "@1771technologies/lytenyte-shared";
import { handleVerticalNavigation } from "../item/handle-vertical-navigation.js";
import { useCombinedRefs } from "@1771technologies/lytenyte-core/internal";
function SubmenuTriggerImpl({ disabled, ...props }, ref) {
const [active, setActive] = useState(false);
const sub = useSubmenuContext();
const [triggerEl, setTriggerEl] = useState(null);
const combined = useCombinedRefs(setTriggerEl, ref);
const blurRef = useRef(null);
useEffect(() => {
if (!triggerEl || !sub)
return;
const controller = new AbortController();
const signal = controller.signal;
triggerEl.addEventListener("ln-activate-mouse", () => {
triggerEl.focus();
sub.onOpenChange(true);
if (blurRef.current)
clearTimeout(blurRef.current);
}, { signal });
triggerEl.addEventListener("ln-deactivate-mouse", () => {
blurRef.current = setTimeout(() => {
triggerEl.blur();
}, 100);
}, { signal });
return () => controller.abort();
}, [sub, triggerEl]);
if (!sub)
return null;
return (_jsx("div", { ...props, tabIndex: 0, "data-ln-open": sub.open, "data-ln-menu-item": true, "data-ln-subtrigger": true, "data-ln-active": active, "data-ln-disabled": disabled ? true : undefined, inert: disabled ? true : undefined, ref: combined, onFocus: (ev) => {
props.onFocus?.(ev);
if (ev.isPropagationStopped())
return;
setActive(true);
}, onBlur: (ev) => {
props.onBlur?.(ev);
if (ev.isPropagationStopped())
return;
setActive(false);
const triggerRoot = getNearestMatching(triggerEl, (el) => el.getAttribute("data-ln-submenu-root") === "true");
if (!triggerRoot || triggerRoot.contains(ev.relatedTarget))
return;
sub.onOpenChange(false);
}, onKeyDown: (ev) => {
if (ev.key === "ArrowUp" || ev.key === "ArrowDown") {
handleVerticalNavigation(ev);
return;
}
if (ev.key === "ArrowRight") {
ev.stopPropagation();
ev.preventDefault();
sub.onOpenChange(true);
const el = ev.currentTarget;
setTimeout(() => {
const submenu = getNearestMatching(el, (el) => {
return el.getAttribute("data-ln-submenu-root") === "true";
});
const menu = submenu?.querySelector('[data-ln-menu="true"][data-ln-submenu="true"]');
if (!menu)
return;
const first = getTabbables(menu).filter((x) => x.getAttribute("data-ln-menu-item") === "true")[0];
first?.focus();
}, 20);
}
} }));
}
export const SubmenuTrigger = forwardRef(SubmenuTriggerImpl);