UNPKG

@kobalte/core

Version:

Unstyled components and primitives for building accessible web apps and design systems with SolidJS.

120 lines (117 loc) 3.48 kB
import { DATA_TOP_LAYER_ATTR } from "./3NI6FTA2.jsx"; // src/primitives/create-interact-outside/create-interact-outside.ts import { access, composeEventHandlers, contains, getDocument, isCtrlKey, noop } from "@kobalte/utils"; import { createEffect, onCleanup } from "solid-js"; import { isServer } from "solid-js/web"; var POINTER_DOWN_OUTSIDE_EVENT = "interactOutside.pointerDownOutside"; var FOCUS_OUTSIDE_EVENT = "interactOutside.focusOutside"; function createInteractOutside(props, ref) { let pointerDownTimeoutId; let clickHandler = noop; const ownerDocument = () => getDocument(ref()); const onPointerDownOutside = (e) => props.onPointerDownOutside?.(e); const onFocusOutside = (e) => props.onFocusOutside?.(e); const onInteractOutside = (e) => props.onInteractOutside?.(e); const isEventOutside = (e) => { const target = e.target; if (!(target instanceof Element)) { return false; } if (target.closest(`[${DATA_TOP_LAYER_ATTR}]`)) { return false; } if (!contains(ownerDocument(), target)) { return false; } if (contains(ref(), target)) { return false; } return !props.shouldExcludeElement?.(target); }; const onPointerDown = (e) => { function handler() { const container = ref(); const target = e.target; if (!container || !target || !isEventOutside(e)) { return; } const handler2 = composeEventHandlers([ onPointerDownOutside, onInteractOutside ]); target.addEventListener(POINTER_DOWN_OUTSIDE_EVENT, handler2, { once: true }); const pointerDownOutsideEvent = new CustomEvent( POINTER_DOWN_OUTSIDE_EVENT, { bubbles: false, cancelable: true, detail: { originalEvent: e, isContextMenu: e.button === 2 || isCtrlKey(e) && e.button === 0 } } ); target.dispatchEvent(pointerDownOutsideEvent); } if (e.pointerType === "touch") { ownerDocument().removeEventListener("click", handler); clickHandler = handler; ownerDocument().addEventListener("click", handler, { once: true }); } else { handler(); } }; const onFocusIn = (e) => { const container = ref(); const target = e.target; if (!container || !target || !isEventOutside(e)) { return; } const handler = composeEventHandlers([ onFocusOutside, onInteractOutside ]); target.addEventListener(FOCUS_OUTSIDE_EVENT, handler, { once: true }); const focusOutsideEvent = new CustomEvent(FOCUS_OUTSIDE_EVENT, { bubbles: false, cancelable: true, detail: { originalEvent: e, isContextMenu: false } }); target.dispatchEvent(focusOutsideEvent); }; createEffect(() => { if (isServer) { return; } if (access(props.isDisabled)) { return; } pointerDownTimeoutId = window.setTimeout(() => { ownerDocument().addEventListener("pointerdown", onPointerDown, true); }, 0); ownerDocument().addEventListener("focusin", onFocusIn, true); onCleanup(() => { window.clearTimeout(pointerDownTimeoutId); ownerDocument().removeEventListener("click", clickHandler); ownerDocument().removeEventListener("pointerdown", onPointerDown, true); ownerDocument().removeEventListener("focusin", onFocusIn, true); }); }); } export { createInteractOutside };