/** * Solid 2 port of Base UI React's `isOutsideMenuEvent` helper * (`packages/react/src/navigation-menu/utils/isOutsideMenuEvent.ts`). * * Determines whether a focus event's `relatedTarget` is outside the navigation * menu surface. Used by the trigger + link parts to close the popup on * focus-out. */ import type { FloatingTreeStoreType } from "../../internals/floating-ui/types"; import { contains } from "../../internals/floating-ui/utils/element"; import { getNodeChildren } from "../../internals/floating-ui/utils/nodes"; interface Targets { currentTarget: HTMLElement | null; relatedTarget: HTMLElement | null; } interface Params { popupElement: HTMLElement | null; viewportElement?: HTMLElement | null | undefined; rootRef: { current: HTMLElement | null }; tree: FloatingTreeStoreType | null; nodeId: string | undefined; } export function isOutsideMenuEvent( { currentTarget, relatedTarget }: Targets, params: Params, ): boolean { const { popupElement, viewportElement, rootRef, tree, nodeId } = params; const nodeChildrenContains = tree ? getNodeChildren(tree.nodesRef.current, nodeId).some((node) => contains( (node.context?.elements as { floating?: Element | null } | undefined) ?.floating ?? null, relatedTarget, ), ) : false; if (!popupElement) { return !contains(rootRef.current, relatedTarget) && !nodeChildrenContains; } const floatingElement = popupElement || viewportElement; return ( !contains(floatingElement, currentTarget) && !contains(floatingElement, relatedTarget) && !contains(rootRef.current, relatedTarget) && !nodeChildrenContains && !( contains(floatingElement, relatedTarget) && relatedTarget?.hasAttribute("data-quoin-focus-guard") ) ); }