@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
108 lines (105 loc) • 3.4 kB
JavaScript
import { DATA_TOP_LAYER_ATTR } from './ZKYDDHM6.js';
import { access, noop, getDocument, composeEventHandlers, contains, isCtrlKey } 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 };