@zag-js/interact-outside 0.70.0 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +27 -54
- package/dist/index.mjs +6 -10
- package/package.json +6 -7
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/src/get-window-frames.ts +0 -51
- package/src/index.ts +0 -189
package/dist/index.js
CHANGED
|
@@ -1,34 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
|
+
'use strict';
|
|
19
2
|
|
|
20
|
-
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
trackInteractOutside: () => trackInteractOutside
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(src_exports);
|
|
26
|
-
var import_dom_event2 = require("@zag-js/dom-event");
|
|
27
|
-
var import_dom_query = require("@zag-js/dom-query");
|
|
28
|
-
var import_utils = require("@zag-js/utils");
|
|
3
|
+
var domEvent = require('@zag-js/dom-event');
|
|
4
|
+
var domQuery = require('@zag-js/dom-query');
|
|
5
|
+
var utils = require('@zag-js/utils');
|
|
29
6
|
|
|
30
|
-
// src/
|
|
31
|
-
var import_dom_event = require("@zag-js/dom-event");
|
|
7
|
+
// src/index.ts
|
|
32
8
|
function getWindowFrames(win) {
|
|
33
9
|
const frames = {
|
|
34
10
|
each(cb) {
|
|
@@ -41,7 +17,7 @@ function getWindowFrames(win) {
|
|
|
41
17
|
const cleanup = /* @__PURE__ */ new Set();
|
|
42
18
|
frames.each((frame) => {
|
|
43
19
|
try {
|
|
44
|
-
cleanup.add(
|
|
20
|
+
cleanup.add(domEvent.queueBeforeEvent(frame.document, event, listener));
|
|
45
21
|
} catch {
|
|
46
22
|
}
|
|
47
23
|
});
|
|
@@ -83,7 +59,7 @@ var POINTER_OUTSIDE_EVENT = "pointerdown.outside";
|
|
|
83
59
|
var FOCUS_OUTSIDE_EVENT = "focus.outside";
|
|
84
60
|
function isComposedPathFocusable(composedPath) {
|
|
85
61
|
for (const node of composedPath) {
|
|
86
|
-
if (
|
|
62
|
+
if (domQuery.isHTMLElement(node) && domQuery.isFocusable(node)) return true;
|
|
87
63
|
}
|
|
88
64
|
return false;
|
|
89
65
|
}
|
|
@@ -95,7 +71,7 @@ function isEventPointWithin(node, event) {
|
|
|
95
71
|
return rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width;
|
|
96
72
|
}
|
|
97
73
|
function isEventWithinScrollbar(event) {
|
|
98
|
-
const target =
|
|
74
|
+
const target = domQuery.getEventTarget(event);
|
|
99
75
|
if (!target || !isPointerEvent(event)) return false;
|
|
100
76
|
const isScrollableY = target.scrollHeight > target.clientHeight;
|
|
101
77
|
const onScrollbarY = isScrollableY && event.clientX > target.clientWidth;
|
|
@@ -106,13 +82,13 @@ function isEventWithinScrollbar(event) {
|
|
|
106
82
|
function trackInteractOutsideImpl(node, options) {
|
|
107
83
|
const { exclude, onFocusOutside, onPointerDownOutside, onInteractOutside, defer } = options;
|
|
108
84
|
if (!node) return;
|
|
109
|
-
const doc =
|
|
110
|
-
const win =
|
|
85
|
+
const doc = domQuery.getDocument(node);
|
|
86
|
+
const win = domQuery.getWindow(node);
|
|
111
87
|
const frames = getWindowFrames(win);
|
|
112
88
|
function isEventOutside(event) {
|
|
113
|
-
const target =
|
|
114
|
-
if (!
|
|
115
|
-
if (
|
|
89
|
+
const target = domQuery.getEventTarget(event);
|
|
90
|
+
if (!domQuery.isHTMLElement(target)) return false;
|
|
91
|
+
if (domQuery.contains(node, target)) return false;
|
|
116
92
|
if (isEventPointWithin(node, event)) return false;
|
|
117
93
|
if (isEventWithinScrollbar(event)) return false;
|
|
118
94
|
return !exclude?.(target);
|
|
@@ -120,20 +96,20 @@ function trackInteractOutsideImpl(node, options) {
|
|
|
120
96
|
const pointerdownCleanups = /* @__PURE__ */ new Set();
|
|
121
97
|
function onPointerDown(event) {
|
|
122
98
|
function handler() {
|
|
123
|
-
const func = defer ?
|
|
99
|
+
const func = defer ? domQuery.raf : (v) => v();
|
|
124
100
|
const composedPath = event.composedPath?.() ?? [event.target];
|
|
125
101
|
func(() => {
|
|
126
102
|
if (!node || !isEventOutside(event)) return;
|
|
127
103
|
if (onPointerDownOutside || onInteractOutside) {
|
|
128
|
-
const handler2 =
|
|
104
|
+
const handler2 = utils.callAll(onPointerDownOutside, onInteractOutside);
|
|
129
105
|
node.addEventListener(POINTER_OUTSIDE_EVENT, handler2, { once: true });
|
|
130
106
|
}
|
|
131
|
-
|
|
107
|
+
domEvent.fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {
|
|
132
108
|
bubbles: false,
|
|
133
109
|
cancelable: true,
|
|
134
110
|
detail: {
|
|
135
111
|
originalEvent: event,
|
|
136
|
-
contextmenu:
|
|
112
|
+
contextmenu: domEvent.isContextMenuEvent(event),
|
|
137
113
|
focusable: isComposedPathFocusable(composedPath)
|
|
138
114
|
}
|
|
139
115
|
});
|
|
@@ -141,7 +117,7 @@ function trackInteractOutsideImpl(node, options) {
|
|
|
141
117
|
}
|
|
142
118
|
if (event.pointerType === "touch") {
|
|
143
119
|
pointerdownCleanups.forEach((fn) => fn());
|
|
144
|
-
pointerdownCleanups.add(
|
|
120
|
+
pointerdownCleanups.add(domEvent.addDomEvent(doc, "click", handler, { once: true }));
|
|
145
121
|
pointerdownCleanups.add(frames.addEventListener("click", handler, { once: true }));
|
|
146
122
|
} else {
|
|
147
123
|
handler();
|
|
@@ -150,28 +126,28 @@ function trackInteractOutsideImpl(node, options) {
|
|
|
150
126
|
const cleanups = /* @__PURE__ */ new Set();
|
|
151
127
|
const timer = setTimeout(() => {
|
|
152
128
|
cleanups.add(frames.addEventListener("pointerdown", onPointerDown, true));
|
|
153
|
-
cleanups.add(
|
|
129
|
+
cleanups.add(domEvent.addDomEvent(doc, "pointerdown", onPointerDown, true));
|
|
154
130
|
}, 0);
|
|
155
131
|
function onFocusin(event) {
|
|
156
|
-
const func = defer ?
|
|
132
|
+
const func = defer ? domQuery.raf : (v) => v();
|
|
157
133
|
func(() => {
|
|
158
134
|
if (!node || !isEventOutside(event)) return;
|
|
159
135
|
if (onFocusOutside || onInteractOutside) {
|
|
160
|
-
const handler =
|
|
136
|
+
const handler = utils.callAll(onFocusOutside, onInteractOutside);
|
|
161
137
|
node.addEventListener(FOCUS_OUTSIDE_EVENT, handler, { once: true });
|
|
162
138
|
}
|
|
163
|
-
|
|
139
|
+
domEvent.fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {
|
|
164
140
|
bubbles: false,
|
|
165
141
|
cancelable: true,
|
|
166
142
|
detail: {
|
|
167
143
|
originalEvent: event,
|
|
168
144
|
contextmenu: false,
|
|
169
|
-
focusable:
|
|
145
|
+
focusable: domQuery.isFocusable(domQuery.getEventTarget(event))
|
|
170
146
|
}
|
|
171
147
|
});
|
|
172
148
|
});
|
|
173
149
|
}
|
|
174
|
-
cleanups.add(
|
|
150
|
+
cleanups.add(domEvent.addDomEvent(doc, "focusin", onFocusin, true));
|
|
175
151
|
cleanups.add(frames.addEventListener("focusin", onFocusin, true));
|
|
176
152
|
return () => {
|
|
177
153
|
clearTimeout(timer);
|
|
@@ -181,7 +157,7 @@ function trackInteractOutsideImpl(node, options) {
|
|
|
181
157
|
}
|
|
182
158
|
function trackInteractOutside(nodeOrFn, options) {
|
|
183
159
|
const { defer } = options;
|
|
184
|
-
const func = defer ?
|
|
160
|
+
const func = defer ? domQuery.raf : (v) => v();
|
|
185
161
|
const cleanups = [];
|
|
186
162
|
cleanups.push(
|
|
187
163
|
func(() => {
|
|
@@ -193,8 +169,5 @@ function trackInteractOutside(nodeOrFn, options) {
|
|
|
193
169
|
cleanups.forEach((fn) => fn?.());
|
|
194
170
|
};
|
|
195
171
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
trackInteractOutside
|
|
199
|
-
});
|
|
200
|
-
//# sourceMappingURL=index.js.map
|
|
172
|
+
|
|
173
|
+
exports.trackInteractOutside = trackInteractOutside;
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { callAll } from "@zag-js/utils";
|
|
1
|
+
import { addDomEvent, fireCustomEvent, isContextMenuEvent, queueBeforeEvent } from '@zag-js/dom-event';
|
|
2
|
+
import { getDocument, getWindow, raf, isFocusable, getEventTarget, isHTMLElement, contains } from '@zag-js/dom-query';
|
|
3
|
+
import { callAll } from '@zag-js/utils';
|
|
5
4
|
|
|
6
|
-
// src/
|
|
7
|
-
import { queueBeforeEvent } from "@zag-js/dom-event";
|
|
5
|
+
// src/index.ts
|
|
8
6
|
function getWindowFrames(win) {
|
|
9
7
|
const frames = {
|
|
10
8
|
each(cb) {
|
|
@@ -169,7 +167,5 @@ function trackInteractOutside(nodeOrFn, options) {
|
|
|
169
167
|
cleanups.forEach((fn) => fn?.());
|
|
170
168
|
};
|
|
171
169
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
175
|
-
//# sourceMappingURL=index.mjs.map
|
|
170
|
+
|
|
171
|
+
export { trackInteractOutside };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zag-js/interact-outside",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"description": "Track interations or focus outside an element",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"js",
|
|
@@ -13,13 +13,12 @@
|
|
|
13
13
|
"repository": "https://github.com/chakra-ui/zag/tree/main/packages/utilities/interact-outside",
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
"src"
|
|
16
|
+
"dist"
|
|
18
17
|
],
|
|
19
18
|
"dependencies": {
|
|
20
|
-
"@zag-js/dom-query": "0.
|
|
21
|
-
"@zag-js/dom-event": "0.
|
|
22
|
-
"@zag-js/utils": "0.
|
|
19
|
+
"@zag-js/dom-query": "0.72.0",
|
|
20
|
+
"@zag-js/dom-event": "0.72.0",
|
|
21
|
+
"@zag-js/utils": "0.72.0"
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
25
24
|
"clean-package": "2.2.0"
|
|
@@ -44,7 +43,7 @@
|
|
|
44
43
|
},
|
|
45
44
|
"scripts": {
|
|
46
45
|
"build": "tsup",
|
|
47
|
-
"test": "
|
|
46
|
+
"test": "vitest",
|
|
48
47
|
"lint": "eslint src",
|
|
49
48
|
"test-ci": "pnpm test --ci --runInBand -u",
|
|
50
49
|
"test-watch": "pnpm test --watchAll"
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/get-window-frames.ts"],"sourcesContent":["import { addDomEvent, fireCustomEvent, isContextMenuEvent } from \"@zag-js/dom-event\"\nimport { contains, getDocument, getEventTarget, getWindow, isFocusable, isHTMLElement, raf } from \"@zag-js/dom-query\"\nimport { callAll } from \"@zag-js/utils\"\nimport { getWindowFrames } from \"./get-window-frames\"\n\nexport interface InteractOutsideHandlers {\n /**\n * Function called when the pointer is pressed down outside the component\n */\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void\n /**\n * Function called when the focus is moved outside the component\n */\n onFocusOutside?: (event: FocusOutsideEvent) => void\n /**\n * Function called when an interaction happens outside the component\n */\n onInteractOutside?: (event: InteractOutsideEvent) => void\n}\n\nexport interface InteractOutsideOptions extends InteractOutsideHandlers {\n exclude?: (target: HTMLElement) => boolean\n defer?: boolean\n}\n\nexport interface EventDetails<T> {\n originalEvent: T\n contextmenu: boolean\n focusable: boolean\n}\n\nconst POINTER_OUTSIDE_EVENT = \"pointerdown.outside\"\nconst FOCUS_OUTSIDE_EVENT = \"focus.outside\"\n\nexport type PointerDownOutsideEvent = CustomEvent<EventDetails<PointerEvent>>\n\nexport type FocusOutsideEvent = CustomEvent<EventDetails<FocusEvent>>\n\nexport type InteractOutsideEvent = PointerDownOutsideEvent | FocusOutsideEvent\n\nexport type MaybeElement = HTMLElement | null | undefined\nexport type NodeOrFn = MaybeElement | (() => MaybeElement)\n\nfunction isComposedPathFocusable(composedPath: EventTarget[]) {\n for (const node of composedPath) {\n if (isHTMLElement(node) && isFocusable(node)) return true\n }\n return false\n}\n\nconst isPointerEvent = (event: Event): event is PointerEvent => \"clientY\" in event\n\nfunction isEventPointWithin(node: MaybeElement, event: Event) {\n if (!isPointerEvent(event) || !node) return false\n\n const rect = node.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return false\n\n return (\n rect.top <= event.clientY &&\n event.clientY <= rect.top + rect.height &&\n rect.left <= event.clientX &&\n event.clientX <= rect.left + rect.width\n )\n}\n\nfunction isEventWithinScrollbar(event: Event): boolean {\n const target = getEventTarget<HTMLElement>(event)\n if (!target || !isPointerEvent(event)) return false\n\n const isScrollableY = target.scrollHeight > target.clientHeight\n const onScrollbarY = isScrollableY && event.clientX > target.clientWidth\n\n const isScrollableX = target.scrollWidth > target.clientWidth\n const onScrollbarX = isScrollableX && event.clientY > target.clientHeight\n\n return onScrollbarY || onScrollbarX\n}\n\nfunction trackInteractOutsideImpl(node: MaybeElement, options: InteractOutsideOptions) {\n const { exclude, onFocusOutside, onPointerDownOutside, onInteractOutside, defer } = options\n\n if (!node) return\n\n const doc = getDocument(node)\n const win = getWindow(node)\n const frames = getWindowFrames(win)\n\n function isEventOutside(event: Event): boolean {\n const target = getEventTarget(event)\n if (!isHTMLElement(target)) return false\n if (contains(node, target)) return false\n if (isEventPointWithin(node, event)) return false\n if (isEventWithinScrollbar(event)) return false\n return !exclude?.(target)\n }\n\n const pointerdownCleanups: Set<VoidFunction> = new Set()\n\n function onPointerDown(event: PointerEvent) {\n //\n function handler() {\n const func = defer ? raf : (v: any) => v()\n const composedPath = event.composedPath?.() ?? [event.target]\n func(() => {\n if (!node || !isEventOutside(event)) return\n\n if (onPointerDownOutside || onInteractOutside) {\n const handler = callAll(onPointerDownOutside, onInteractOutside) as EventListener\n node.addEventListener(POINTER_OUTSIDE_EVENT, handler, { once: true })\n }\n\n fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {\n bubbles: false,\n cancelable: true,\n detail: {\n originalEvent: event,\n contextmenu: isContextMenuEvent(event),\n focusable: isComposedPathFocusable(composedPath),\n },\n })\n })\n }\n\n if (event.pointerType === \"touch\") {\n // flush any pending pointerup events\n pointerdownCleanups.forEach((fn) => fn())\n\n // add a pointerup event listener to the document and all frame documents\n pointerdownCleanups.add(addDomEvent(doc, \"click\", handler, { once: true }))\n pointerdownCleanups.add(frames.addEventListener(\"click\", handler, { once: true }))\n } else {\n handler()\n }\n }\n const cleanups = new Set<VoidFunction>()\n\n const timer = setTimeout(() => {\n cleanups.add(frames.addEventListener(\"pointerdown\", onPointerDown, true))\n cleanups.add(addDomEvent(doc, \"pointerdown\", onPointerDown, true))\n }, 0)\n\n function onFocusin(event: FocusEvent) {\n //\n const func = defer ? raf : (v: any) => v()\n func(() => {\n if (!node || !isEventOutside(event)) return\n\n if (onFocusOutside || onInteractOutside) {\n const handler = callAll(onFocusOutside, onInteractOutside) as EventListener\n node.addEventListener(FOCUS_OUTSIDE_EVENT, handler, { once: true })\n }\n\n fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {\n bubbles: false,\n cancelable: true,\n detail: {\n originalEvent: event,\n contextmenu: false,\n focusable: isFocusable(getEventTarget(event)),\n },\n })\n })\n }\n\n cleanups.add(addDomEvent(doc, \"focusin\", onFocusin, true))\n cleanups.add(frames.addEventListener(\"focusin\", onFocusin, true))\n\n return () => {\n clearTimeout(timer)\n pointerdownCleanups.forEach((fn) => fn())\n cleanups.forEach((fn) => fn())\n }\n}\n\nexport function trackInteractOutside(nodeOrFn: NodeOrFn, options: InteractOutsideOptions) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const node = typeof nodeOrFn === \"function\" ? nodeOrFn() : nodeOrFn\n cleanups.push(trackInteractOutsideImpl(node, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n","import { queueBeforeEvent } from \"@zag-js/dom-event\"\n\nexport function getWindowFrames(win: Window) {\n const frames = {\n each(cb: (win: Window) => void) {\n for (let i = 0; i < win.frames?.length; i += 1) {\n const frame = win.frames[i]\n if (frame) cb(frame)\n }\n },\n\n queueBeforeEvent(event: string, listener: any) {\n const cleanup = new Set<VoidFunction>()\n frames.each((frame) => {\n try {\n cleanup.add(queueBeforeEvent(frame.document, event, listener))\n } catch {}\n })\n\n return () => {\n try {\n cleanup.forEach((fn) => fn())\n } catch {}\n }\n },\n\n addEventListener(event: string, listener: any, options?: any) {\n frames.each((frame) => {\n try {\n frame.document.addEventListener(event, listener, options)\n } catch {}\n })\n\n return () => {\n try {\n frames.removeEventListener(event, listener, options)\n } catch {}\n }\n },\n\n removeEventListener(event: string, listener: any, options?: any) {\n frames.each((frame) => {\n try {\n frame.document.removeEventListener(event, listener, options)\n } catch {}\n })\n },\n }\n\n return frames\n}\n"],"mappings":"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","names":["import_dom_event","handler"]}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/get-window-frames.ts"],"sourcesContent":["import { addDomEvent, fireCustomEvent, isContextMenuEvent } from \"@zag-js/dom-event\"\nimport { contains, getDocument, getEventTarget, getWindow, isFocusable, isHTMLElement, raf } from \"@zag-js/dom-query\"\nimport { callAll } from \"@zag-js/utils\"\nimport { getWindowFrames } from \"./get-window-frames\"\n\nexport interface InteractOutsideHandlers {\n /**\n * Function called when the pointer is pressed down outside the component\n */\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void\n /**\n * Function called when the focus is moved outside the component\n */\n onFocusOutside?: (event: FocusOutsideEvent) => void\n /**\n * Function called when an interaction happens outside the component\n */\n onInteractOutside?: (event: InteractOutsideEvent) => void\n}\n\nexport interface InteractOutsideOptions extends InteractOutsideHandlers {\n exclude?: (target: HTMLElement) => boolean\n defer?: boolean\n}\n\nexport interface EventDetails<T> {\n originalEvent: T\n contextmenu: boolean\n focusable: boolean\n}\n\nconst POINTER_OUTSIDE_EVENT = \"pointerdown.outside\"\nconst FOCUS_OUTSIDE_EVENT = \"focus.outside\"\n\nexport type PointerDownOutsideEvent = CustomEvent<EventDetails<PointerEvent>>\n\nexport type FocusOutsideEvent = CustomEvent<EventDetails<FocusEvent>>\n\nexport type InteractOutsideEvent = PointerDownOutsideEvent | FocusOutsideEvent\n\nexport type MaybeElement = HTMLElement | null | undefined\nexport type NodeOrFn = MaybeElement | (() => MaybeElement)\n\nfunction isComposedPathFocusable(composedPath: EventTarget[]) {\n for (const node of composedPath) {\n if (isHTMLElement(node) && isFocusable(node)) return true\n }\n return false\n}\n\nconst isPointerEvent = (event: Event): event is PointerEvent => \"clientY\" in event\n\nfunction isEventPointWithin(node: MaybeElement, event: Event) {\n if (!isPointerEvent(event) || !node) return false\n\n const rect = node.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return false\n\n return (\n rect.top <= event.clientY &&\n event.clientY <= rect.top + rect.height &&\n rect.left <= event.clientX &&\n event.clientX <= rect.left + rect.width\n )\n}\n\nfunction isEventWithinScrollbar(event: Event): boolean {\n const target = getEventTarget<HTMLElement>(event)\n if (!target || !isPointerEvent(event)) return false\n\n const isScrollableY = target.scrollHeight > target.clientHeight\n const onScrollbarY = isScrollableY && event.clientX > target.clientWidth\n\n const isScrollableX = target.scrollWidth > target.clientWidth\n const onScrollbarX = isScrollableX && event.clientY > target.clientHeight\n\n return onScrollbarY || onScrollbarX\n}\n\nfunction trackInteractOutsideImpl(node: MaybeElement, options: InteractOutsideOptions) {\n const { exclude, onFocusOutside, onPointerDownOutside, onInteractOutside, defer } = options\n\n if (!node) return\n\n const doc = getDocument(node)\n const win = getWindow(node)\n const frames = getWindowFrames(win)\n\n function isEventOutside(event: Event): boolean {\n const target = getEventTarget(event)\n if (!isHTMLElement(target)) return false\n if (contains(node, target)) return false\n if (isEventPointWithin(node, event)) return false\n if (isEventWithinScrollbar(event)) return false\n return !exclude?.(target)\n }\n\n const pointerdownCleanups: Set<VoidFunction> = new Set()\n\n function onPointerDown(event: PointerEvent) {\n //\n function handler() {\n const func = defer ? raf : (v: any) => v()\n const composedPath = event.composedPath?.() ?? [event.target]\n func(() => {\n if (!node || !isEventOutside(event)) return\n\n if (onPointerDownOutside || onInteractOutside) {\n const handler = callAll(onPointerDownOutside, onInteractOutside) as EventListener\n node.addEventListener(POINTER_OUTSIDE_EVENT, handler, { once: true })\n }\n\n fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {\n bubbles: false,\n cancelable: true,\n detail: {\n originalEvent: event,\n contextmenu: isContextMenuEvent(event),\n focusable: isComposedPathFocusable(composedPath),\n },\n })\n })\n }\n\n if (event.pointerType === \"touch\") {\n // flush any pending pointerup events\n pointerdownCleanups.forEach((fn) => fn())\n\n // add a pointerup event listener to the document and all frame documents\n pointerdownCleanups.add(addDomEvent(doc, \"click\", handler, { once: true }))\n pointerdownCleanups.add(frames.addEventListener(\"click\", handler, { once: true }))\n } else {\n handler()\n }\n }\n const cleanups = new Set<VoidFunction>()\n\n const timer = setTimeout(() => {\n cleanups.add(frames.addEventListener(\"pointerdown\", onPointerDown, true))\n cleanups.add(addDomEvent(doc, \"pointerdown\", onPointerDown, true))\n }, 0)\n\n function onFocusin(event: FocusEvent) {\n //\n const func = defer ? raf : (v: any) => v()\n func(() => {\n if (!node || !isEventOutside(event)) return\n\n if (onFocusOutside || onInteractOutside) {\n const handler = callAll(onFocusOutside, onInteractOutside) as EventListener\n node.addEventListener(FOCUS_OUTSIDE_EVENT, handler, { once: true })\n }\n\n fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {\n bubbles: false,\n cancelable: true,\n detail: {\n originalEvent: event,\n contextmenu: false,\n focusable: isFocusable(getEventTarget(event)),\n },\n })\n })\n }\n\n cleanups.add(addDomEvent(doc, \"focusin\", onFocusin, true))\n cleanups.add(frames.addEventListener(\"focusin\", onFocusin, true))\n\n return () => {\n clearTimeout(timer)\n pointerdownCleanups.forEach((fn) => fn())\n cleanups.forEach((fn) => fn())\n }\n}\n\nexport function trackInteractOutside(nodeOrFn: NodeOrFn, options: InteractOutsideOptions) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const node = typeof nodeOrFn === \"function\" ? nodeOrFn() : nodeOrFn\n cleanups.push(trackInteractOutsideImpl(node, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n","import { queueBeforeEvent } from \"@zag-js/dom-event\"\n\nexport function getWindowFrames(win: Window) {\n const frames = {\n each(cb: (win: Window) => void) {\n for (let i = 0; i < win.frames?.length; i += 1) {\n const frame = win.frames[i]\n if (frame) cb(frame)\n }\n },\n\n queueBeforeEvent(event: string, listener: any) {\n const cleanup = new Set<VoidFunction>()\n frames.each((frame) => {\n try {\n cleanup.add(queueBeforeEvent(frame.document, event, listener))\n } catch {}\n })\n\n return () => {\n try {\n cleanup.forEach((fn) => fn())\n } catch {}\n }\n },\n\n addEventListener(event: string, listener: any, options?: any) {\n frames.each((frame) => {\n try {\n frame.document.addEventListener(event, listener, options)\n } catch {}\n })\n\n return () => {\n try {\n frames.removeEventListener(event, listener, options)\n } catch {}\n }\n },\n\n removeEventListener(event: string, listener: any, options?: any) {\n frames.each((frame) => {\n try {\n frame.document.removeEventListener(event, listener, options)\n } catch {}\n })\n },\n }\n\n return frames\n}\n"],"mappings":"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","names":["handler"]}
|
package/src/get-window-frames.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { queueBeforeEvent } from "@zag-js/dom-event"
|
|
2
|
-
|
|
3
|
-
export function getWindowFrames(win: Window) {
|
|
4
|
-
const frames = {
|
|
5
|
-
each(cb: (win: Window) => void) {
|
|
6
|
-
for (let i = 0; i < win.frames?.length; i += 1) {
|
|
7
|
-
const frame = win.frames[i]
|
|
8
|
-
if (frame) cb(frame)
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
queueBeforeEvent(event: string, listener: any) {
|
|
13
|
-
const cleanup = new Set<VoidFunction>()
|
|
14
|
-
frames.each((frame) => {
|
|
15
|
-
try {
|
|
16
|
-
cleanup.add(queueBeforeEvent(frame.document, event, listener))
|
|
17
|
-
} catch {}
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
return () => {
|
|
21
|
-
try {
|
|
22
|
-
cleanup.forEach((fn) => fn())
|
|
23
|
-
} catch {}
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
addEventListener(event: string, listener: any, options?: any) {
|
|
28
|
-
frames.each((frame) => {
|
|
29
|
-
try {
|
|
30
|
-
frame.document.addEventListener(event, listener, options)
|
|
31
|
-
} catch {}
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
return () => {
|
|
35
|
-
try {
|
|
36
|
-
frames.removeEventListener(event, listener, options)
|
|
37
|
-
} catch {}
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
removeEventListener(event: string, listener: any, options?: any) {
|
|
42
|
-
frames.each((frame) => {
|
|
43
|
-
try {
|
|
44
|
-
frame.document.removeEventListener(event, listener, options)
|
|
45
|
-
} catch {}
|
|
46
|
-
})
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return frames
|
|
51
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { addDomEvent, fireCustomEvent, isContextMenuEvent } from "@zag-js/dom-event"
|
|
2
|
-
import { contains, getDocument, getEventTarget, getWindow, isFocusable, isHTMLElement, raf } from "@zag-js/dom-query"
|
|
3
|
-
import { callAll } from "@zag-js/utils"
|
|
4
|
-
import { getWindowFrames } from "./get-window-frames"
|
|
5
|
-
|
|
6
|
-
export interface InteractOutsideHandlers {
|
|
7
|
-
/**
|
|
8
|
-
* Function called when the pointer is pressed down outside the component
|
|
9
|
-
*/
|
|
10
|
-
onPointerDownOutside?: (event: PointerDownOutsideEvent) => void
|
|
11
|
-
/**
|
|
12
|
-
* Function called when the focus is moved outside the component
|
|
13
|
-
*/
|
|
14
|
-
onFocusOutside?: (event: FocusOutsideEvent) => void
|
|
15
|
-
/**
|
|
16
|
-
* Function called when an interaction happens outside the component
|
|
17
|
-
*/
|
|
18
|
-
onInteractOutside?: (event: InteractOutsideEvent) => void
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface InteractOutsideOptions extends InteractOutsideHandlers {
|
|
22
|
-
exclude?: (target: HTMLElement) => boolean
|
|
23
|
-
defer?: boolean
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface EventDetails<T> {
|
|
27
|
-
originalEvent: T
|
|
28
|
-
contextmenu: boolean
|
|
29
|
-
focusable: boolean
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const POINTER_OUTSIDE_EVENT = "pointerdown.outside"
|
|
33
|
-
const FOCUS_OUTSIDE_EVENT = "focus.outside"
|
|
34
|
-
|
|
35
|
-
export type PointerDownOutsideEvent = CustomEvent<EventDetails<PointerEvent>>
|
|
36
|
-
|
|
37
|
-
export type FocusOutsideEvent = CustomEvent<EventDetails<FocusEvent>>
|
|
38
|
-
|
|
39
|
-
export type InteractOutsideEvent = PointerDownOutsideEvent | FocusOutsideEvent
|
|
40
|
-
|
|
41
|
-
export type MaybeElement = HTMLElement | null | undefined
|
|
42
|
-
export type NodeOrFn = MaybeElement | (() => MaybeElement)
|
|
43
|
-
|
|
44
|
-
function isComposedPathFocusable(composedPath: EventTarget[]) {
|
|
45
|
-
for (const node of composedPath) {
|
|
46
|
-
if (isHTMLElement(node) && isFocusable(node)) return true
|
|
47
|
-
}
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const isPointerEvent = (event: Event): event is PointerEvent => "clientY" in event
|
|
52
|
-
|
|
53
|
-
function isEventPointWithin(node: MaybeElement, event: Event) {
|
|
54
|
-
if (!isPointerEvent(event) || !node) return false
|
|
55
|
-
|
|
56
|
-
const rect = node.getBoundingClientRect()
|
|
57
|
-
if (rect.width === 0 || rect.height === 0) return false
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
rect.top <= event.clientY &&
|
|
61
|
-
event.clientY <= rect.top + rect.height &&
|
|
62
|
-
rect.left <= event.clientX &&
|
|
63
|
-
event.clientX <= rect.left + rect.width
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function isEventWithinScrollbar(event: Event): boolean {
|
|
68
|
-
const target = getEventTarget<HTMLElement>(event)
|
|
69
|
-
if (!target || !isPointerEvent(event)) return false
|
|
70
|
-
|
|
71
|
-
const isScrollableY = target.scrollHeight > target.clientHeight
|
|
72
|
-
const onScrollbarY = isScrollableY && event.clientX > target.clientWidth
|
|
73
|
-
|
|
74
|
-
const isScrollableX = target.scrollWidth > target.clientWidth
|
|
75
|
-
const onScrollbarX = isScrollableX && event.clientY > target.clientHeight
|
|
76
|
-
|
|
77
|
-
return onScrollbarY || onScrollbarX
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function trackInteractOutsideImpl(node: MaybeElement, options: InteractOutsideOptions) {
|
|
81
|
-
const { exclude, onFocusOutside, onPointerDownOutside, onInteractOutside, defer } = options
|
|
82
|
-
|
|
83
|
-
if (!node) return
|
|
84
|
-
|
|
85
|
-
const doc = getDocument(node)
|
|
86
|
-
const win = getWindow(node)
|
|
87
|
-
const frames = getWindowFrames(win)
|
|
88
|
-
|
|
89
|
-
function isEventOutside(event: Event): boolean {
|
|
90
|
-
const target = getEventTarget(event)
|
|
91
|
-
if (!isHTMLElement(target)) return false
|
|
92
|
-
if (contains(node, target)) return false
|
|
93
|
-
if (isEventPointWithin(node, event)) return false
|
|
94
|
-
if (isEventWithinScrollbar(event)) return false
|
|
95
|
-
return !exclude?.(target)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const pointerdownCleanups: Set<VoidFunction> = new Set()
|
|
99
|
-
|
|
100
|
-
function onPointerDown(event: PointerEvent) {
|
|
101
|
-
//
|
|
102
|
-
function handler() {
|
|
103
|
-
const func = defer ? raf : (v: any) => v()
|
|
104
|
-
const composedPath = event.composedPath?.() ?? [event.target]
|
|
105
|
-
func(() => {
|
|
106
|
-
if (!node || !isEventOutside(event)) return
|
|
107
|
-
|
|
108
|
-
if (onPointerDownOutside || onInteractOutside) {
|
|
109
|
-
const handler = callAll(onPointerDownOutside, onInteractOutside) as EventListener
|
|
110
|
-
node.addEventListener(POINTER_OUTSIDE_EVENT, handler, { once: true })
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {
|
|
114
|
-
bubbles: false,
|
|
115
|
-
cancelable: true,
|
|
116
|
-
detail: {
|
|
117
|
-
originalEvent: event,
|
|
118
|
-
contextmenu: isContextMenuEvent(event),
|
|
119
|
-
focusable: isComposedPathFocusable(composedPath),
|
|
120
|
-
},
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (event.pointerType === "touch") {
|
|
126
|
-
// flush any pending pointerup events
|
|
127
|
-
pointerdownCleanups.forEach((fn) => fn())
|
|
128
|
-
|
|
129
|
-
// add a pointerup event listener to the document and all frame documents
|
|
130
|
-
pointerdownCleanups.add(addDomEvent(doc, "click", handler, { once: true }))
|
|
131
|
-
pointerdownCleanups.add(frames.addEventListener("click", handler, { once: true }))
|
|
132
|
-
} else {
|
|
133
|
-
handler()
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const cleanups = new Set<VoidFunction>()
|
|
137
|
-
|
|
138
|
-
const timer = setTimeout(() => {
|
|
139
|
-
cleanups.add(frames.addEventListener("pointerdown", onPointerDown, true))
|
|
140
|
-
cleanups.add(addDomEvent(doc, "pointerdown", onPointerDown, true))
|
|
141
|
-
}, 0)
|
|
142
|
-
|
|
143
|
-
function onFocusin(event: FocusEvent) {
|
|
144
|
-
//
|
|
145
|
-
const func = defer ? raf : (v: any) => v()
|
|
146
|
-
func(() => {
|
|
147
|
-
if (!node || !isEventOutside(event)) return
|
|
148
|
-
|
|
149
|
-
if (onFocusOutside || onInteractOutside) {
|
|
150
|
-
const handler = callAll(onFocusOutside, onInteractOutside) as EventListener
|
|
151
|
-
node.addEventListener(FOCUS_OUTSIDE_EVENT, handler, { once: true })
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {
|
|
155
|
-
bubbles: false,
|
|
156
|
-
cancelable: true,
|
|
157
|
-
detail: {
|
|
158
|
-
originalEvent: event,
|
|
159
|
-
contextmenu: false,
|
|
160
|
-
focusable: isFocusable(getEventTarget(event)),
|
|
161
|
-
},
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
cleanups.add(addDomEvent(doc, "focusin", onFocusin, true))
|
|
167
|
-
cleanups.add(frames.addEventListener("focusin", onFocusin, true))
|
|
168
|
-
|
|
169
|
-
return () => {
|
|
170
|
-
clearTimeout(timer)
|
|
171
|
-
pointerdownCleanups.forEach((fn) => fn())
|
|
172
|
-
cleanups.forEach((fn) => fn())
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function trackInteractOutside(nodeOrFn: NodeOrFn, options: InteractOutsideOptions) {
|
|
177
|
-
const { defer } = options
|
|
178
|
-
const func = defer ? raf : (v: any) => v()
|
|
179
|
-
const cleanups: (VoidFunction | undefined)[] = []
|
|
180
|
-
cleanups.push(
|
|
181
|
-
func(() => {
|
|
182
|
-
const node = typeof nodeOrFn === "function" ? nodeOrFn() : nodeOrFn
|
|
183
|
-
cleanups.push(trackInteractOutsideImpl(node, options))
|
|
184
|
-
}),
|
|
185
|
-
)
|
|
186
|
-
return () => {
|
|
187
|
-
cleanups.forEach((fn) => fn?.())
|
|
188
|
-
}
|
|
189
|
-
}
|