@zag-js/interact-outside 0.0.0-dev-20220627213111 → 0.0.0-dev-20220703123907
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.d.ts +14 -3
- package/dist/index.js +79 -12
- package/dist/index.mjs +79 -12
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
export declare type
|
|
1
|
+
export declare type InteractOutsideHandlers = {
|
|
2
|
+
onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;
|
|
3
|
+
onFocusOutside?: (event: FocusOutsideEvent) => void;
|
|
4
|
+
};
|
|
5
|
+
export declare type InteractOutsideOptions = InteractOutsideHandlers & {
|
|
2
6
|
exclude?: (target: HTMLElement) => boolean;
|
|
3
|
-
onPointerDownOutside?: (event: Event) => void;
|
|
4
|
-
onFocusOutside?: (event: Event) => void;
|
|
5
7
|
};
|
|
8
|
+
declare type EventDetails<T> = {
|
|
9
|
+
originalEvent: T;
|
|
10
|
+
contextmenu: boolean;
|
|
11
|
+
focusable: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare type PointerDownOutsideEvent = CustomEvent<EventDetails<PointerEvent>>;
|
|
14
|
+
export declare type FocusOutsideEvent = CustomEvent<EventDetails<FocusEvent>>;
|
|
15
|
+
export declare type InteractOutsideEvent = PointerDownOutsideEvent | FocusOutsideEvent;
|
|
6
16
|
export declare function trackInteractOutside(node: HTMLElement | null, options: InteractOutsideOptions): () => void;
|
|
17
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -41,13 +41,13 @@ function addDomEvent(target, eventName, handler, options) {
|
|
|
41
41
|
function isWindow(value) {
|
|
42
42
|
return (value == null ? void 0 : value.toString()) === "[object Window]";
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function getDocument(el) {
|
|
45
45
|
var _a;
|
|
46
46
|
if (isWindow(el))
|
|
47
47
|
return el.document;
|
|
48
48
|
return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
|
|
49
49
|
}
|
|
50
|
-
function
|
|
50
|
+
function getWindow(el) {
|
|
51
51
|
var _a;
|
|
52
52
|
return (_a = el == null ? void 0 : el.ownerDocument.defaultView) != null ? _a : window;
|
|
53
53
|
}
|
|
@@ -63,21 +63,54 @@ function contains(parent, child) {
|
|
|
63
63
|
function isHTMLElement(v) {
|
|
64
64
|
return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
|
|
65
65
|
}
|
|
66
|
+
function isVisible(el) {
|
|
67
|
+
if (!isHTMLElement(el))
|
|
68
|
+
return false;
|
|
69
|
+
return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0;
|
|
70
|
+
}
|
|
71
|
+
function fireCustomEvent(el, type, init) {
|
|
72
|
+
if (!el)
|
|
73
|
+
return;
|
|
74
|
+
const event = new CustomEvent(type, init);
|
|
75
|
+
return el.dispatchEvent(event);
|
|
76
|
+
}
|
|
77
|
+
var focusableSelector = "input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], button:not([disabled]), [tabindex], iframe, object, embed, area[href], audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), details > summary:first-of-type";
|
|
78
|
+
function isFocusable(element) {
|
|
79
|
+
if (!element)
|
|
80
|
+
return false;
|
|
81
|
+
return element.matches(focusableSelector) && isVisible(element);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ../core/dist/index.mjs
|
|
85
|
+
var isDom = () => typeof window !== "undefined";
|
|
86
|
+
function getPlatform() {
|
|
87
|
+
var _a;
|
|
88
|
+
const agent = navigator.userAgentData;
|
|
89
|
+
return (_a = agent == null ? void 0 : agent.platform) != null ? _a : navigator.platform;
|
|
90
|
+
}
|
|
91
|
+
var pt = (v) => isDom() && v.test(getPlatform());
|
|
92
|
+
var isTouchDevice = () => isDom() && !!navigator.maxTouchPoints;
|
|
93
|
+
var isMac = () => pt(/^Mac/) && !isTouchDevice;
|
|
94
|
+
var isContextMenuEvent = (e) => {
|
|
95
|
+
return e.button === 2 || isCtrlKey(e) && e.button === 0;
|
|
96
|
+
};
|
|
97
|
+
var isCtrlKey = (v) => isMac() ? v.metaKey && !v.ctrlKey : v.ctrlKey && !v.metaKey;
|
|
66
98
|
|
|
67
99
|
// src/index.ts
|
|
100
|
+
var POINTER_OUTSIDE_EVENT = "pointerdown.outside";
|
|
101
|
+
var FOCUS_OUTSIDE_EVENT = "focus.outside";
|
|
68
102
|
function trackInteractOutside(node, options) {
|
|
69
|
-
const { exclude,
|
|
103
|
+
const { exclude, onFocusOutside, onPointerDownOutside } = options;
|
|
70
104
|
if (!node)
|
|
71
105
|
return;
|
|
72
|
-
const doc =
|
|
73
|
-
const win =
|
|
106
|
+
const doc = getDocument(node);
|
|
107
|
+
const win = getWindow(node);
|
|
74
108
|
function isEventOutside(event) {
|
|
75
109
|
const target = getEventTarget(event);
|
|
76
110
|
if (!(target instanceof win.HTMLElement)) {
|
|
77
111
|
return false;
|
|
78
112
|
}
|
|
79
|
-
|
|
80
|
-
if (!contains(doc2.documentElement, target)) {
|
|
113
|
+
if (!contains(doc.documentElement, target)) {
|
|
81
114
|
return false;
|
|
82
115
|
}
|
|
83
116
|
if (contains(node, target)) {
|
|
@@ -85,23 +118,57 @@ function trackInteractOutside(node, options) {
|
|
|
85
118
|
}
|
|
86
119
|
return !(exclude == null ? void 0 : exclude(target));
|
|
87
120
|
}
|
|
121
|
+
let clickHandler;
|
|
88
122
|
function onPointerDown(event) {
|
|
89
|
-
|
|
90
|
-
|
|
123
|
+
function handler() {
|
|
124
|
+
if (!node || !isEventOutside(event))
|
|
125
|
+
return;
|
|
126
|
+
if (onPointerDownOutside) {
|
|
127
|
+
node.addEventListener(POINTER_OUTSIDE_EVENT, onPointerDownOutside, { once: true });
|
|
128
|
+
}
|
|
129
|
+
fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {
|
|
130
|
+
bubbles: false,
|
|
131
|
+
cancelable: true,
|
|
132
|
+
detail: {
|
|
133
|
+
originalEvent: event,
|
|
134
|
+
contextmenu: isContextMenuEvent(event),
|
|
135
|
+
focusable: isFocusable(getEventTarget(event))
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (event.pointerType === "touch") {
|
|
140
|
+
doc.removeEventListener("click", handler);
|
|
141
|
+
clickHandler = handler;
|
|
142
|
+
doc.addEventListener("click", handler, { once: true });
|
|
143
|
+
} else {
|
|
144
|
+
handler();
|
|
91
145
|
}
|
|
92
146
|
}
|
|
93
147
|
const cleanups = /* @__PURE__ */ new Set();
|
|
94
148
|
const timer = setTimeout(() => {
|
|
95
149
|
cleanups.add(addDomEvent(doc, "pointerdown", onPointerDown, true));
|
|
96
|
-
});
|
|
150
|
+
}, 0);
|
|
97
151
|
function onFocusin(event) {
|
|
98
|
-
if (isEventOutside(event))
|
|
99
|
-
|
|
152
|
+
if (!node || !isEventOutside(event))
|
|
153
|
+
return;
|
|
154
|
+
if (onFocusOutside) {
|
|
155
|
+
node.addEventListener(FOCUS_OUTSIDE_EVENT, onFocusOutside, { once: true });
|
|
100
156
|
}
|
|
157
|
+
fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {
|
|
158
|
+
bubbles: false,
|
|
159
|
+
cancelable: true,
|
|
160
|
+
detail: {
|
|
161
|
+
originalEvent: event,
|
|
162
|
+
contextmenu: false,
|
|
163
|
+
focusable: isFocusable(getEventTarget(event))
|
|
164
|
+
}
|
|
165
|
+
});
|
|
101
166
|
}
|
|
102
167
|
cleanups.add(addDomEvent(doc, "focusin", onFocusin, true));
|
|
103
168
|
return () => {
|
|
104
169
|
clearTimeout(timer);
|
|
170
|
+
if (clickHandler)
|
|
171
|
+
doc.removeEventListener("click", clickHandler);
|
|
105
172
|
cleanups.forEach((fn) => fn());
|
|
106
173
|
};
|
|
107
174
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -17,13 +17,13 @@ function addDomEvent(target, eventName, handler, options) {
|
|
|
17
17
|
function isWindow(value) {
|
|
18
18
|
return (value == null ? void 0 : value.toString()) === "[object Window]";
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function getDocument(el) {
|
|
21
21
|
var _a;
|
|
22
22
|
if (isWindow(el))
|
|
23
23
|
return el.document;
|
|
24
24
|
return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
|
|
25
25
|
}
|
|
26
|
-
function
|
|
26
|
+
function getWindow(el) {
|
|
27
27
|
var _a;
|
|
28
28
|
return (_a = el == null ? void 0 : el.ownerDocument.defaultView) != null ? _a : window;
|
|
29
29
|
}
|
|
@@ -39,21 +39,54 @@ function contains(parent, child) {
|
|
|
39
39
|
function isHTMLElement(v) {
|
|
40
40
|
return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
|
|
41
41
|
}
|
|
42
|
+
function isVisible(el) {
|
|
43
|
+
if (!isHTMLElement(el))
|
|
44
|
+
return false;
|
|
45
|
+
return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0;
|
|
46
|
+
}
|
|
47
|
+
function fireCustomEvent(el, type, init) {
|
|
48
|
+
if (!el)
|
|
49
|
+
return;
|
|
50
|
+
const event = new CustomEvent(type, init);
|
|
51
|
+
return el.dispatchEvent(event);
|
|
52
|
+
}
|
|
53
|
+
var focusableSelector = "input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], button:not([disabled]), [tabindex], iframe, object, embed, area[href], audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), details > summary:first-of-type";
|
|
54
|
+
function isFocusable(element) {
|
|
55
|
+
if (!element)
|
|
56
|
+
return false;
|
|
57
|
+
return element.matches(focusableSelector) && isVisible(element);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ../core/dist/index.mjs
|
|
61
|
+
var isDom = () => typeof window !== "undefined";
|
|
62
|
+
function getPlatform() {
|
|
63
|
+
var _a;
|
|
64
|
+
const agent = navigator.userAgentData;
|
|
65
|
+
return (_a = agent == null ? void 0 : agent.platform) != null ? _a : navigator.platform;
|
|
66
|
+
}
|
|
67
|
+
var pt = (v) => isDom() && v.test(getPlatform());
|
|
68
|
+
var isTouchDevice = () => isDom() && !!navigator.maxTouchPoints;
|
|
69
|
+
var isMac = () => pt(/^Mac/) && !isTouchDevice;
|
|
70
|
+
var isContextMenuEvent = (e) => {
|
|
71
|
+
return e.button === 2 || isCtrlKey(e) && e.button === 0;
|
|
72
|
+
};
|
|
73
|
+
var isCtrlKey = (v) => isMac() ? v.metaKey && !v.ctrlKey : v.ctrlKey && !v.metaKey;
|
|
42
74
|
|
|
43
75
|
// src/index.ts
|
|
76
|
+
var POINTER_OUTSIDE_EVENT = "pointerdown.outside";
|
|
77
|
+
var FOCUS_OUTSIDE_EVENT = "focus.outside";
|
|
44
78
|
function trackInteractOutside(node, options) {
|
|
45
|
-
const { exclude,
|
|
79
|
+
const { exclude, onFocusOutside, onPointerDownOutside } = options;
|
|
46
80
|
if (!node)
|
|
47
81
|
return;
|
|
48
|
-
const doc =
|
|
49
|
-
const win =
|
|
82
|
+
const doc = getDocument(node);
|
|
83
|
+
const win = getWindow(node);
|
|
50
84
|
function isEventOutside(event) {
|
|
51
85
|
const target = getEventTarget(event);
|
|
52
86
|
if (!(target instanceof win.HTMLElement)) {
|
|
53
87
|
return false;
|
|
54
88
|
}
|
|
55
|
-
|
|
56
|
-
if (!contains(doc2.documentElement, target)) {
|
|
89
|
+
if (!contains(doc.documentElement, target)) {
|
|
57
90
|
return false;
|
|
58
91
|
}
|
|
59
92
|
if (contains(node, target)) {
|
|
@@ -61,23 +94,57 @@ function trackInteractOutside(node, options) {
|
|
|
61
94
|
}
|
|
62
95
|
return !(exclude == null ? void 0 : exclude(target));
|
|
63
96
|
}
|
|
97
|
+
let clickHandler;
|
|
64
98
|
function onPointerDown(event) {
|
|
65
|
-
|
|
66
|
-
|
|
99
|
+
function handler() {
|
|
100
|
+
if (!node || !isEventOutside(event))
|
|
101
|
+
return;
|
|
102
|
+
if (onPointerDownOutside) {
|
|
103
|
+
node.addEventListener(POINTER_OUTSIDE_EVENT, onPointerDownOutside, { once: true });
|
|
104
|
+
}
|
|
105
|
+
fireCustomEvent(node, POINTER_OUTSIDE_EVENT, {
|
|
106
|
+
bubbles: false,
|
|
107
|
+
cancelable: true,
|
|
108
|
+
detail: {
|
|
109
|
+
originalEvent: event,
|
|
110
|
+
contextmenu: isContextMenuEvent(event),
|
|
111
|
+
focusable: isFocusable(getEventTarget(event))
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (event.pointerType === "touch") {
|
|
116
|
+
doc.removeEventListener("click", handler);
|
|
117
|
+
clickHandler = handler;
|
|
118
|
+
doc.addEventListener("click", handler, { once: true });
|
|
119
|
+
} else {
|
|
120
|
+
handler();
|
|
67
121
|
}
|
|
68
122
|
}
|
|
69
123
|
const cleanups = /* @__PURE__ */ new Set();
|
|
70
124
|
const timer = setTimeout(() => {
|
|
71
125
|
cleanups.add(addDomEvent(doc, "pointerdown", onPointerDown, true));
|
|
72
|
-
});
|
|
126
|
+
}, 0);
|
|
73
127
|
function onFocusin(event) {
|
|
74
|
-
if (isEventOutside(event))
|
|
75
|
-
|
|
128
|
+
if (!node || !isEventOutside(event))
|
|
129
|
+
return;
|
|
130
|
+
if (onFocusOutside) {
|
|
131
|
+
node.addEventListener(FOCUS_OUTSIDE_EVENT, onFocusOutside, { once: true });
|
|
76
132
|
}
|
|
133
|
+
fireCustomEvent(node, FOCUS_OUTSIDE_EVENT, {
|
|
134
|
+
bubbles: false,
|
|
135
|
+
cancelable: true,
|
|
136
|
+
detail: {
|
|
137
|
+
originalEvent: event,
|
|
138
|
+
contextmenu: false,
|
|
139
|
+
focusable: isFocusable(getEventTarget(event))
|
|
140
|
+
}
|
|
141
|
+
});
|
|
77
142
|
}
|
|
78
143
|
cleanups.add(addDomEvent(doc, "focusin", onFocusin, true));
|
|
79
144
|
return () => {
|
|
80
145
|
clearTimeout(timer);
|
|
146
|
+
if (clickHandler)
|
|
147
|
+
doc.removeEventListener("click", clickHandler);
|
|
81
148
|
cleanups.forEach((fn) => fn());
|
|
82
149
|
};
|
|
83
150
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zag-js/interact-outside",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20220703123907",
|
|
4
4
|
"description": "Track interations or focus outside an element",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test:watch": "yarn test --watchAll"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@zag-js/dom-utils": "0.0.0-dev-
|
|
31
|
+
"@zag-js/dom-utils": "0.0.0-dev-20220703123907"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|