@comty.io/core 0.1.9 → 0.1.10
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/README.md +28 -1
- package/comty-sdk.js +3866 -1638
- package/package.json +3 -2
- package/src/anchors.js +168 -413
- package/src/browser.js +3306 -1189
- package/src/comments.js +6 -6
- package/src/dev.mjs +317 -112
- package/src/inspect-markup.js +1 -1
- package/src/measurements.js +5 -4
- package/src/resilient-anchor.js +333 -0
- package/src/resilient-anchor.ts +551 -0
- package/src/style.js +60 -33
- package/src/vite.mjs +123 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comty.io/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Comty visual comments, inspector and local agent tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"test:anchors": "node --test tests/anchors.test.mjs tests/anchor-reload.browser.test.mjs",
|
|
40
40
|
"test:auth": "node --test tests/auth-return.browser.test.mjs",
|
|
41
41
|
"test:vite": "node --test tests/vite.test.mjs",
|
|
42
|
-
"prepack": "npm run build"
|
|
42
|
+
"prepack": "npm run build",
|
|
43
|
+
"test:isolation": "node --test tests/style-isolation.browser.test.mjs"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"esbuild": "0.28.2",
|
package/src/anchors.js
CHANGED
|
@@ -1,437 +1,192 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import { createResilientAnchors, readAnchor } from "./resilient-anchor.js";
|
|
2
|
+
|
|
3
|
+
export function createAnchorMatcher({ getPresentationLayer, isComtyNode }) {
|
|
4
|
+
const resilient = createResilientAnchors(isComtyNode, getPresentationLayer);
|
|
5
|
+
|
|
6
|
+
function getStableAnchorId(id) {
|
|
7
|
+
// React useId values change with mount order, including Base UI and Radix IDs.
|
|
8
|
+
return /^(?:(?:radix|base-ui)-)?(?:_r_[\w-]+_|:r[\w-]+:)$/i.test(id || "")
|
|
9
|
+
? ""
|
|
10
|
+
: id || "";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveAnchor(context, exact = null, previous = null) {
|
|
14
|
+
const anchor = readAnchor(context);
|
|
15
|
+
return anchor
|
|
16
|
+
? resilient.resolve(anchor, previous, exact)
|
|
17
|
+
: { element: null, status: "uncertain" };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getViewportBounds() {
|
|
21
|
+
const viewport = window.visualViewport;
|
|
22
|
+
if (!viewport) {
|
|
23
|
+
return {
|
|
24
|
+
left: 0,
|
|
25
|
+
top: 0,
|
|
26
|
+
right: window.innerWidth,
|
|
27
|
+
bottom: window.innerHeight,
|
|
28
|
+
};
|
|
12
29
|
}
|
|
13
30
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
31
|
+
return {
|
|
32
|
+
left: viewport.offsetLeft,
|
|
33
|
+
top: viewport.offsetTop,
|
|
34
|
+
right: viewport.offsetLeft + viewport.width,
|
|
35
|
+
bottom: viewport.offsetTop + viewport.height,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
18
38
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function scoreFingerprint(el, fingerprint) {
|
|
28
|
-
if (!el || !fingerprint) return 0;
|
|
29
|
-
if (
|
|
30
|
-
fingerprint.tag &&
|
|
31
|
-
el.tagName.toLowerCase() !== fingerprint.tag.toLowerCase()
|
|
32
|
-
) {
|
|
33
|
-
return -1000;
|
|
39
|
+
function isElementTreeHidden(el) {
|
|
40
|
+
let current = el;
|
|
41
|
+
while (current instanceof Element) {
|
|
42
|
+
if (!current.isConnected) return true;
|
|
43
|
+
if (current.hasAttribute("hidden")) return true;
|
|
44
|
+
if (current.hasAttribute("inert")) return true;
|
|
45
|
+
if (current.getAttribute("aria-hidden") === "true") {
|
|
46
|
+
return true;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
score += 20;
|
|
41
|
-
const stableId = getStableAnchorId(fingerprint.id);
|
|
42
|
-
if (stableId) {
|
|
43
|
-
score += el.id.slice(0, 160) === stableId ? 70 : -30;
|
|
44
|
-
}
|
|
45
|
-
const exactAttributes = [
|
|
46
|
-
["role", "role", 24],
|
|
47
|
-
["ariaLabel", "aria-label", 34],
|
|
48
|
-
["name", "name", 30],
|
|
49
|
-
["type", "type", 12],
|
|
50
|
-
["placeholder", "placeholder", 24],
|
|
51
|
-
["alt", "alt", 28],
|
|
52
|
-
["href", "href", 30],
|
|
53
|
-
["src", "src", 24],
|
|
54
|
-
["title", "title", 20],
|
|
55
|
-
];
|
|
56
|
-
for (const [key, attribute, weight] of exactAttributes) {
|
|
57
|
-
if (!fingerprint[key]) continue;
|
|
58
|
-
score +=
|
|
59
|
-
getAttributeSignature(el, attribute) === fingerprint[key]
|
|
60
|
-
? weight
|
|
61
|
-
: -Math.ceil(weight / 3);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const testId =
|
|
65
|
-
getAttributeSignature(el, "data-testid") ||
|
|
66
|
-
getAttributeSignature(el, "data-test") ||
|
|
67
|
-
getAttributeSignature(el, "data-cy");
|
|
68
|
-
if (fingerprint.testId) {
|
|
69
|
-
score += testId === fingerprint.testId ? 55 : -20;
|
|
49
|
+
const style = getComputedStyle(current);
|
|
50
|
+
if (style.display === "none") return true;
|
|
51
|
+
if (style.visibility === "hidden" || style.visibility === "collapse") {
|
|
52
|
+
return true;
|
|
70
53
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: [];
|
|
75
|
-
const matchingClasses = classes.filter((className) =>
|
|
76
|
-
el.classList.contains(className),
|
|
77
|
-
);
|
|
78
|
-
score += matchingClasses.length * 7;
|
|
79
|
-
if (classes.length > 0 && matchingClasses.length === 0) score -= 8;
|
|
80
|
-
|
|
81
|
-
if (fingerprint.ownText && ownText === fingerprint.ownText) score += 42;
|
|
82
|
-
else if (
|
|
83
|
-
fingerprint.ownText &&
|
|
84
|
-
includesText(ownText, fingerprint.ownText.slice(0, 80))
|
|
85
|
-
) {
|
|
86
|
-
score += 20;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (fingerprint.text && text === fingerprint.text) score += 32;
|
|
90
|
-
else if (
|
|
91
|
-
fingerprint.text &&
|
|
92
|
-
includesText(text, fingerprint.text.slice(0, 80))
|
|
93
|
-
) {
|
|
94
|
-
score += 15;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (text !== fingerprint.text && similarText(text, fingerprint.text)) score += 24;
|
|
98
|
-
|
|
99
|
-
if (typeof fingerprint.siblingIndex === "number" && el.parentElement) {
|
|
100
|
-
const siblings = [...el.parentElement.children].filter(
|
|
101
|
-
(child) => child.tagName === el.tagName,
|
|
102
|
-
);
|
|
103
|
-
if (siblings.indexOf(el) === fingerprint.siblingIndex) score += 8;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
typeof fingerprint.childCount === "number" &&
|
|
108
|
-
el.children.length === fingerprint.childCount
|
|
109
|
-
) {
|
|
110
|
-
score += 4;
|
|
54
|
+
if (Number.parseFloat(style.opacity) <= 0.01) return true;
|
|
55
|
+
if (style.contentVisibility === "hidden") {
|
|
56
|
+
return true;
|
|
111
57
|
}
|
|
112
58
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
rect.width > 0 &&
|
|
117
|
-
Math.abs(rect.width - fingerprint.width) /
|
|
118
|
-
Math.max(rect.width, fingerprint.width) <
|
|
119
|
-
0.2
|
|
120
|
-
) {
|
|
121
|
-
score += 4;
|
|
122
|
-
}
|
|
123
|
-
if (
|
|
124
|
-
fingerprint.height > 0 &&
|
|
125
|
-
rect.height > 0 &&
|
|
126
|
-
Math.abs(rect.height - fingerprint.height) /
|
|
127
|
-
Math.max(rect.height, fingerprint.height) <
|
|
128
|
-
0.2
|
|
129
|
-
) {
|
|
130
|
-
score += 4;
|
|
59
|
+
if (current.tagName === "DETAILS" && !current.hasAttribute("open")) {
|
|
60
|
+
const summary = current.querySelector(":scope > summary");
|
|
61
|
+
if (!summary || !summary.contains(el)) return true;
|
|
131
62
|
}
|
|
132
63
|
|
|
133
|
-
|
|
64
|
+
current = current.parentElement;
|
|
134
65
|
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
135
68
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!sibling || sibling.tag !== fingerprint.tag) return -4;
|
|
140
|
-
let score = 4;
|
|
141
|
-
if (fingerprint.ariaLabel && sibling.ariaLabel === fingerprint.ariaLabel) {
|
|
142
|
-
score += 6;
|
|
143
|
-
}
|
|
144
|
-
if (fingerprint.role && sibling.role === fingerprint.role) score += 4;
|
|
145
|
-
if (fingerprint.text && sibling.text === fingerprint.text) score += 8;
|
|
146
|
-
return score;
|
|
147
|
-
}
|
|
69
|
+
function pointIsInsideRect(x, y, rect) {
|
|
70
|
+
return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
|
|
71
|
+
}
|
|
148
72
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
73
|
+
function isPointClipped(el, x, y) {
|
|
74
|
+
let ancestor = el.parentElement;
|
|
75
|
+
while (
|
|
76
|
+
ancestor &&
|
|
77
|
+
ancestor !== document.body &&
|
|
78
|
+
ancestor !== document.documentElement
|
|
79
|
+
) {
|
|
80
|
+
const style = getComputedStyle(ancestor);
|
|
81
|
+
const clipsX = ["auto", "scroll", "hidden", "clip"].includes(
|
|
82
|
+
style.overflowX,
|
|
157
83
|
);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
for (
|
|
165
|
-
let index = 0;
|
|
166
|
-
parent && index < ancestors.length;
|
|
167
|
-
index += 1
|
|
168
|
-
) {
|
|
169
|
-
const ancestorScore = scoreFingerprint(
|
|
170
|
-
parent,
|
|
171
|
-
ancestors[index],
|
|
172
|
-
);
|
|
173
|
-
if (ancestorScore > 0) {
|
|
174
|
-
score += Math.min(ancestorScore, 40) * (index === 0 ? 0.5 : 0.2);
|
|
175
|
-
}
|
|
176
|
-
// Distinguish repeated cards by their section, even when dimensions match.
|
|
177
|
-
if (ancestors[index].text && getElementTextSignature(parent, ancestors[index].textMode === "static") === ancestors[index].text) score += 18;
|
|
178
|
-
parent = parent.parentElement;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (context.layer) {
|
|
182
|
-
const layer = getPresentationLayer(el);
|
|
183
|
-
if (!layer) return -1000;
|
|
184
|
-
const layerScore = scoreFingerprint(layer, context.layer);
|
|
185
|
-
if (layerScore < 30) return -1000;
|
|
186
|
-
score += Math.min(layerScore, 60) * 0.5;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return score;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function matchesAnchorIdentity(el, fingerprint, ignoreDescendantText = false, allowTextChanges = true) {
|
|
193
|
-
if (!el || !fingerprint || el.tagName.toLowerCase() !== fingerprint.tag) return false;
|
|
194
|
-
const attributes = { ariaLabel: "aria-label", name: "name", href: "href", src: "src", alt: "alt", title: "title", role: "role" };
|
|
195
|
-
for (const [key, attribute] of Object.entries(attributes)) {
|
|
196
|
-
if (fingerprint[key] && getAttributeSignature(el, attribute) !== fingerprint[key]) return false;
|
|
197
|
-
}
|
|
198
|
-
const stableId = getStableAnchorId(fingerprint.id);
|
|
199
|
-
if (stableId && el.id !== stableId) return false;
|
|
200
|
-
if (fingerprint.testId && !["data-testid", "data-test", "data-cy"].some(name => getAttributeSignature(el, name) === fingerprint.testId)) return false;
|
|
201
|
-
const stableIdentity = (stableId && el.id === stableId) ||
|
|
202
|
-
(fingerprint.testId && ["data-testid", "data-test", "data-cy"].some(name => getAttributeSignature(el, name) === fingerprint.testId));
|
|
203
|
-
const staticText = fingerprint.textMode === "static";
|
|
204
|
-
const text = getElementTextSignature(el, staticText);
|
|
205
|
-
if (fingerprint.ownText && getElementOwnTextSignature(el, staticText) !== fingerprint.ownText && !(allowTextChanges && stableIdentity)) return false;
|
|
206
|
-
// Container text is useful for matching remounted drawers and dialogs.
|
|
207
|
-
if (!ignoreDescendantText && !fingerprint.ownText && fingerprint.text && text !== fingerprint.text && !(allowTextChanges && stableIdentity)) {
|
|
208
|
-
if (!allowTextChanges || !el.children.length || !similarText(text, fingerprint.text)) return false;
|
|
209
|
-
}
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function matchesAnchorContext(el, context) {
|
|
214
|
-
if (!context) return true;
|
|
215
|
-
// A unique page landmark keeps its identity when a child is edited.
|
|
216
|
-
const pageContainer = !context.layer && (context.target?.tag === "main" || context.target?.role === "main") &&
|
|
217
|
-
[...document.querySelectorAll('main, [role="main"]')].filter(node => !isComtyNode(node)).length === 1;
|
|
218
|
-
if (!matchesAnchorIdentity(el, context.target, pageContainer)) return false;
|
|
219
|
-
const parentFingerprint = context.ancestors?.[0];
|
|
220
|
-
if (parentFingerprint?.text && parentFingerprint.text !== context.target.text &&
|
|
221
|
-
!matchesAnchorIdentity(el.parentElement, parentFingerprint, pageContainer)) return false;
|
|
222
|
-
const layer = getPresentationLayer(el);
|
|
223
|
-
if (context.layer && (!layer || !matchesAnchorIdentity(layer, context.layer, false, false))) return false;
|
|
224
|
-
if (context.version >= 2 && !context.layer && layer) return false;
|
|
225
|
-
return scoreAnchorCandidate(el, context) >= 50;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function resolveAnchor(context, exact = null) {
|
|
229
|
-
if (!context?.target) return { element: exact, status: exact ? "matched" : "missing" };
|
|
230
|
-
let candidates;
|
|
231
|
-
try {
|
|
232
|
-
candidates = [...document.body.querySelectorAll(context.target.tag || "*")].filter(el => !isComtyNode(el));
|
|
233
|
-
} catch {
|
|
234
|
-
return { element: null, status: "missing" };
|
|
235
|
-
}
|
|
236
|
-
const matches = candidates
|
|
237
|
-
.filter(el => matchesAnchorContext(el, context))
|
|
238
|
-
.map(el => ({ el, score: scoreAnchorCandidate(el, context) + (context.version === 1 && el === exact ? 16 : 0) }))
|
|
239
|
-
.sort((a, b) => b.score - a.score);
|
|
240
|
-
if (context.candidateCount > 1) {
|
|
241
|
-
// Creation counts full context matches, not every element with the same text.
|
|
242
|
-
const peers = candidates.filter(el => matchesAnchorIdentity(el, context.target));
|
|
243
|
-
if (matches.length !== context.candidateCount && peers.length !== context.candidateCount) {
|
|
244
|
-
// A removed identical sibling must not move its comment to the survivor.
|
|
245
|
-
const distinctive = matches[0] && (getStableAnchorId(context.target.id) || context.target.testId);
|
|
246
|
-
if (!distinctive) return { element: null, status: "uncertain" };
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if (matches[0]?.score >= 58 && (!matches[1] || matches[0].score - matches[1].score >= 12)) {
|
|
250
|
-
return { element: matches[0].el, status: "matched" };
|
|
251
|
-
}
|
|
252
|
-
// A closed/unmounted overlay is not evidence that its contents were deleted.
|
|
253
|
-
if (context.layer && !candidates.some(el => {
|
|
254
|
-
const layer = getPresentationLayer(el);
|
|
255
|
-
return layer && matchesAnchorIdentity(layer, context.layer, false, false);
|
|
256
|
-
})) return { element: null, status: "hidden" };
|
|
257
|
-
const plausible = matches.length || candidates.some(el =>
|
|
258
|
-
(getStableAnchorId(context.target.id) && el.id === context.target.id) ||
|
|
259
|
-
(context.target.text && (getElementTextSignature(el, context.target.textMode === "static") === context.target.text || similarText(getElementTextSignature(el, context.target.textMode === "static"), context.target.text))));
|
|
260
|
-
return { element: null, status: plausible ? "uncertain" : "missing" };
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function resolveByAnchorContext(context) {
|
|
264
|
-
return resolveAnchor(context).element;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function getViewportBounds() {
|
|
268
|
-
const viewport = window.visualViewport;
|
|
269
|
-
if (!viewport) {
|
|
270
|
-
return {
|
|
271
|
-
left: 0,
|
|
272
|
-
top: 0,
|
|
273
|
-
right: window.innerWidth,
|
|
274
|
-
bottom: window.innerHeight,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return {
|
|
279
|
-
left: viewport.offsetLeft,
|
|
280
|
-
top: viewport.offsetTop,
|
|
281
|
-
right: viewport.offsetLeft + viewport.width,
|
|
282
|
-
bottom: viewport.offsetTop + viewport.height,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function isElementTreeHidden(el) {
|
|
287
|
-
let current = el;
|
|
288
|
-
while (current instanceof Element) {
|
|
289
|
-
if (!current.isConnected) return true;
|
|
290
|
-
if (current.hasAttribute("hidden")) return true;
|
|
291
|
-
if (current.hasAttribute("inert")) return true;
|
|
292
|
-
if (current.getAttribute("aria-hidden") === "true") {
|
|
293
|
-
return true;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const style = getComputedStyle(current);
|
|
297
|
-
if (style.display === "none") return true;
|
|
298
|
-
if (style.visibility === "hidden" || style.visibility === "collapse") {
|
|
84
|
+
const clipsY = ["auto", "scroll", "hidden", "clip"].includes(
|
|
85
|
+
style.overflowY,
|
|
86
|
+
);
|
|
87
|
+
if (clipsX || clipsY) {
|
|
88
|
+
const rect = ancestor.getBoundingClientRect();
|
|
89
|
+
if (clipsX && (x < rect.left || x > rect.right)) {
|
|
299
90
|
return true;
|
|
300
91
|
}
|
|
301
|
-
if (
|
|
302
|
-
if (style.contentVisibility === "hidden") {
|
|
92
|
+
if (clipsY && (y < rect.top || y > rect.bottom)) {
|
|
303
93
|
return true;
|
|
304
94
|
}
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
current.tagName === "DETAILS" &&
|
|
308
|
-
!current.hasAttribute("open")
|
|
309
|
-
) {
|
|
310
|
-
const summary = current.querySelector(":scope > summary");
|
|
311
|
-
if (!summary || !summary.contains(el)) return true;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
current = current.parentElement;
|
|
315
95
|
}
|
|
316
|
-
|
|
96
|
+
ancestor = ancestor.parentElement;
|
|
317
97
|
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
318
100
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function isPointClipped(el, x, y) {
|
|
329
|
-
let ancestor = el.parentElement;
|
|
330
|
-
while (
|
|
331
|
-
ancestor &&
|
|
332
|
-
ancestor !== document.body &&
|
|
333
|
-
ancestor !== document.documentElement
|
|
334
|
-
) {
|
|
335
|
-
const style = getComputedStyle(ancestor);
|
|
336
|
-
const clipsX = ["auto", "scroll", "hidden", "clip"].includes(
|
|
337
|
-
style.overflowX,
|
|
338
|
-
);
|
|
339
|
-
const clipsY = ["auto", "scroll", "hidden", "clip"].includes(
|
|
340
|
-
style.overflowY,
|
|
341
|
-
);
|
|
342
|
-
if (clipsX || clipsY) {
|
|
343
|
-
const rect = ancestor.getBoundingClientRect();
|
|
344
|
-
if (clipsX && (x < rect.left || x > rect.right)) {
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
347
|
-
if (clipsY && (y < rect.top || y > rect.bottom)) {
|
|
348
|
-
return true;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
ancestor = ancestor.parentElement;
|
|
352
|
-
}
|
|
353
|
-
return false;
|
|
101
|
+
function getTopActiveModal() {
|
|
102
|
+
const modals = [...document.querySelectorAll('[aria-modal="true"]')];
|
|
103
|
+
try {
|
|
104
|
+
modals.push(...document.querySelectorAll("dialog:modal"));
|
|
105
|
+
} catch {
|
|
106
|
+
modals.push(...document.querySelectorAll("dialog[open]"));
|
|
354
107
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
modals.push(...document.querySelectorAll("dialog:modal"));
|
|
360
|
-
} catch {
|
|
361
|
-
modals.push(...document.querySelectorAll("dialog[open]"));
|
|
362
|
-
}
|
|
363
|
-
const visibleModals = [...new Set(modals)].filter(
|
|
364
|
-
(modal) => {
|
|
365
|
-
if (isComtyNode(modal)) return false;
|
|
366
|
-
const rect = modal.getBoundingClientRect();
|
|
367
|
-
if (rect.width < 0.1 || rect.height < 0.1) return false;
|
|
368
|
-
const viewport = getViewportBounds();
|
|
369
|
-
if (
|
|
370
|
-
rect.right <= viewport.left ||
|
|
371
|
-
rect.left >= viewport.right ||
|
|
372
|
-
rect.bottom <= viewport.top ||
|
|
373
|
-
rect.top >= viewport.bottom
|
|
374
|
-
) {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
return !isElementTreeHidden(modal);
|
|
378
|
-
},
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
return (
|
|
382
|
-
visibleModals.sort((a, b) => {
|
|
383
|
-
const aZIndex = Number.parseFloat(getComputedStyle(a).zIndex) || 0;
|
|
384
|
-
const bZIndex = Number.parseFloat(getComputedStyle(b).zIndex) || 0;
|
|
385
|
-
if (aZIndex !== bZIndex) return aZIndex - bZIndex;
|
|
386
|
-
return a.compareDocumentPosition(b) &
|
|
387
|
-
Node.DOCUMENT_POSITION_FOLLOWING
|
|
388
|
-
? -1
|
|
389
|
-
: 1;
|
|
390
|
-
})[visibleModals.length - 1] || null
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function getOccludingElement(el, x, y) {
|
|
395
|
-
const stack = document
|
|
396
|
-
.elementsFromPoint(x, y)
|
|
397
|
-
.filter((candidate) => !isComtyNode(candidate));
|
|
398
|
-
const top = stack[0];
|
|
399
|
-
if (!top) return null;
|
|
400
|
-
if (top === el || el.contains(top) || top.contains(el)) return null;
|
|
401
|
-
return top;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function getVisibleAnchorPoint(el, relX, relY, activeModal) {
|
|
405
|
-
if (!el || !Number.isFinite(relX) || !Number.isFinite(relY) || isElementTreeHidden(el)) return null;
|
|
406
|
-
|
|
407
|
-
const rect = el.getBoundingClientRect();
|
|
408
|
-
if (rect.width < 0.1 || rect.height < 0.1) {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const clientX = Math.min(
|
|
413
|
-
Math.max(rect.left + relX * rect.width, rect.left + 0.01),
|
|
414
|
-
rect.right - 0.01,
|
|
415
|
-
);
|
|
416
|
-
const clientY = Math.min(
|
|
417
|
-
Math.max(rect.top + relY * rect.height, rect.top + 0.01),
|
|
418
|
-
rect.bottom - 0.01,
|
|
419
|
-
);
|
|
108
|
+
const visibleModals = [...new Set(modals)].filter((modal) => {
|
|
109
|
+
if (isComtyNode(modal)) return false;
|
|
110
|
+
const rect = modal.getBoundingClientRect();
|
|
111
|
+
if (rect.width < 0.1 || rect.height < 0.1) return false;
|
|
420
112
|
const viewport = getViewportBounds();
|
|
421
|
-
if (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
113
|
+
if (
|
|
114
|
+
rect.right <= viewport.left ||
|
|
115
|
+
rect.left >= viewport.right ||
|
|
116
|
+
rect.bottom <= viewport.top ||
|
|
117
|
+
rect.top >= viewport.bottom
|
|
118
|
+
) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return !isElementTreeHidden(modal);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
visibleModals.sort((a, b) => {
|
|
126
|
+
const aZIndex = Number.parseFloat(getComputedStyle(a).zIndex) || 0;
|
|
127
|
+
const bZIndex = Number.parseFloat(getComputedStyle(b).zIndex) || 0;
|
|
128
|
+
if (aZIndex !== bZIndex) return aZIndex - bZIndex;
|
|
129
|
+
return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
130
|
+
? -1
|
|
131
|
+
: 1;
|
|
132
|
+
})[visibleModals.length - 1] || null
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getOccludingElement(el, x, y) {
|
|
137
|
+
const stack = document
|
|
138
|
+
.elementsFromPoint(x, y)
|
|
139
|
+
.filter((candidate) => !isComtyNode(candidate));
|
|
140
|
+
const top = stack[0];
|
|
141
|
+
if (!top) return null;
|
|
142
|
+
if (top === el || el.contains(top) || top.contains(el)) return null;
|
|
143
|
+
return top;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function getVisibleAnchorPoint(el, relX, relY, activeModal) {
|
|
147
|
+
if (
|
|
148
|
+
!el ||
|
|
149
|
+
!Number.isFinite(relX) ||
|
|
150
|
+
!Number.isFinite(relY) ||
|
|
151
|
+
isElementTreeHidden(el)
|
|
152
|
+
)
|
|
153
|
+
return null;
|
|
154
|
+
|
|
155
|
+
const rect = el.getBoundingClientRect();
|
|
156
|
+
if (rect.width < 0.1 || rect.height < 0.1) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const clientX = Math.min(
|
|
161
|
+
Math.max(rect.left + relX * rect.width, rect.left + 0.01),
|
|
162
|
+
rect.right - 0.01,
|
|
163
|
+
);
|
|
164
|
+
const clientY = Math.min(
|
|
165
|
+
Math.max(rect.top + relY * rect.height, rect.top + 0.01),
|
|
166
|
+
rect.bottom - 0.01,
|
|
167
|
+
);
|
|
168
|
+
const viewport = getViewportBounds();
|
|
169
|
+
if (!pointIsInsideRect(clientX, clientY, viewport)) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (isPointClipped(el, clientX, clientY)) return null;
|
|
174
|
+
|
|
175
|
+
if (activeModal && !activeModal.contains(el)) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (getOccludingElement(el, clientX, clientY)) return null;
|
|
180
|
+
|
|
181
|
+
return { clientX, clientY };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
captureResilientAnchor: resilient.capture,
|
|
186
|
+
getStableAnchorId,
|
|
187
|
+
resolveAnchor,
|
|
188
|
+
isElementTreeHidden,
|
|
189
|
+
getTopActiveModal,
|
|
190
|
+
getVisibleAnchorPoint,
|
|
191
|
+
};
|
|
437
192
|
}
|