@reticlehq/browser 3.8.0 → 3.8.1
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/actions/action-report.js +36 -18
- package/dist/actions/actions.js +66 -36
- package/dist/actions/danger-context.js +27 -16
- package/dist/actions/scroll.d.ts +2 -0
- package/dist/actions/scroll.js +10 -2
- package/dist/dom/a11y.d.ts +0 -4
- package/dist/dom/a11y.js +2 -294
- package/dist/dom/dom-ignore.js +23 -10
- package/dist/dom/query-finders.d.ts +6 -0
- package/dist/dom/query-finders.js +216 -0
- package/dist/dom/query.d.ts +0 -1
- package/dist/dom/query.js +108 -244
- package/dist/dom/snapshot.js +2 -1
- package/dist/dom/visibility.d.ts +7 -0
- package/dist/dom/visibility.js +322 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/observers/dom.js +2 -1
- package/dist/observers/net-detail/network-body.js +29 -9
- package/dist/observers/net-detail/network-event-stream.d.ts +5 -0
- package/dist/observers/net-detail/network-event-stream.js +55 -0
- package/dist/observers/net-detail/network-frames.d.ts +2 -0
- package/dist/observers/net-detail/network-frames.js +39 -0
- package/dist/observers/net-detail/network-response-body.d.ts +3 -0
- package/dist/observers/net-detail/network-response-body.js +97 -0
- package/dist/observers/network.js +95 -105
- package/dist/reticle-inject.js +695 -631
- package/package.json +2 -2
|
@@ -10,13 +10,15 @@ export function visibleCanvases(doc) {
|
|
|
10
10
|
const height = doc.defaultView?.innerHeight ?? 0;
|
|
11
11
|
let count = 0;
|
|
12
12
|
for (const canvas of doc.querySelectorAll(CANVAS_SELECTOR)) {
|
|
13
|
-
if (isReticleUi(canvas))
|
|
13
|
+
if (isReticleUi(canvas)) {
|
|
14
14
|
continue;
|
|
15
|
+
}
|
|
15
16
|
const box = canvas.getBoundingClientRect();
|
|
16
17
|
const sized = box.width > 0 && box.height > 0;
|
|
17
18
|
const onScreen = box.bottom > 0 && box.right > 0 && box.top < height && box.left < width;
|
|
18
|
-
if (sized && onScreen)
|
|
19
|
+
if (sized && onScreen) {
|
|
19
20
|
count += 1;
|
|
21
|
+
}
|
|
20
22
|
}
|
|
21
23
|
return count;
|
|
22
24
|
}
|
|
@@ -27,22 +29,28 @@ export function anchorOf(el) {
|
|
|
27
29
|
if (testid !== undefined) {
|
|
28
30
|
out.testid = testid;
|
|
29
31
|
const shared = elementsMatching({ by: QueryBy.TESTID, value: testid }).length;
|
|
30
|
-
if (1 < shared)
|
|
32
|
+
if (1 < shared) {
|
|
31
33
|
out.testidOf = shared;
|
|
34
|
+
}
|
|
32
35
|
}
|
|
33
36
|
const component = info?.componentStack[0];
|
|
34
|
-
if (component !== undefined)
|
|
37
|
+
if (component !== undefined) {
|
|
35
38
|
out.component = component;
|
|
36
|
-
|
|
39
|
+
}
|
|
40
|
+
if (info?.source !== undefined) {
|
|
37
41
|
out.source = info.source;
|
|
42
|
+
}
|
|
38
43
|
const role = getRole(el);
|
|
39
44
|
const name = getAccessibleName(el);
|
|
40
|
-
if (role.length > 0)
|
|
45
|
+
if (role.length > 0) {
|
|
41
46
|
out.role = role;
|
|
42
|
-
|
|
47
|
+
}
|
|
48
|
+
if (name.length > 0) {
|
|
43
49
|
out.name = name;
|
|
44
|
-
|
|
50
|
+
}
|
|
51
|
+
if (isSensitiveField(el)) {
|
|
45
52
|
out.secret = true;
|
|
53
|
+
}
|
|
46
54
|
if (role.length > 0 && name.length > 0) {
|
|
47
55
|
const same = elementsMatching({ by: QueryBy.ROLE, value: role, name });
|
|
48
56
|
const at = same.indexOf(el);
|
|
@@ -63,25 +71,35 @@ export const result = (ref, action, effect, settled, settleReason, anchor, warni
|
|
|
63
71
|
settleReason,
|
|
64
72
|
effect,
|
|
65
73
|
};
|
|
66
|
-
if (anchor.testid !== undefined)
|
|
74
|
+
if (anchor.testid !== undefined) {
|
|
67
75
|
base.testid = anchor.testid;
|
|
68
|
-
|
|
76
|
+
}
|
|
77
|
+
if (anchor.testidOf !== undefined) {
|
|
69
78
|
base.testidOf = anchor.testidOf;
|
|
70
|
-
|
|
79
|
+
}
|
|
80
|
+
if (anchor.component !== undefined) {
|
|
71
81
|
base.component = anchor.component;
|
|
72
|
-
|
|
82
|
+
}
|
|
83
|
+
if (anchor.source !== undefined) {
|
|
73
84
|
base.source = anchor.source;
|
|
74
|
-
|
|
85
|
+
}
|
|
86
|
+
if (anchor.role !== undefined) {
|
|
75
87
|
base.role = anchor.role;
|
|
76
|
-
|
|
88
|
+
}
|
|
89
|
+
if (anchor.name !== undefined) {
|
|
77
90
|
base.name = anchor.name;
|
|
78
|
-
|
|
91
|
+
}
|
|
92
|
+
if (anchor.nth !== undefined) {
|
|
79
93
|
base.nth = anchor.nth;
|
|
80
|
-
|
|
94
|
+
}
|
|
95
|
+
if (anchor.of !== undefined) {
|
|
81
96
|
base.of = anchor.of;
|
|
82
|
-
|
|
97
|
+
}
|
|
98
|
+
if (anchor.secret !== undefined) {
|
|
83
99
|
base.secret = anchor.secret;
|
|
84
|
-
|
|
100
|
+
}
|
|
101
|
+
if (warning !== undefined) {
|
|
85
102
|
base.warning = warning;
|
|
103
|
+
}
|
|
86
104
|
return base;
|
|
87
105
|
};
|
package/dist/actions/actions.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ActionType, ActionWarning, clampHoldMs, DANGEROUS_ACTION_CONFIRM_ARG, ElementState, NATIVE_INPUT_ARG, SettleReason, } from '@reticlehq/core';
|
|
2
|
-
import { isHiddenNativeOption } from '../dom/query.js';
|
|
2
|
+
import { isHiddenNativeOption } from '../dom/query-finders.js';
|
|
3
3
|
import { isReticleUi } from '../dom/dom-ignore.js';
|
|
4
4
|
import { asSyntheticInput } from './synthetic/synthetic-input.js';
|
|
5
5
|
import { echoRef, refs } from '../dom/addressing/refs.js';
|
|
6
6
|
import { requiresDangerousConfirmation, assertOutwardAllowed, assertPaymentEntryAllowed, submitControlFor, } from './danger-context.js';
|
|
7
7
|
import { assertEditable, assertNotRichText, isAutofillSafeTarget, setNativeValue, } from './value-input.js';
|
|
8
|
-
import {
|
|
8
|
+
import { getStates } from '../dom/a11y.js';
|
|
9
|
+
import { isVisible } from '../dom/visibility.js';
|
|
9
10
|
import { elementHasHoverHandlers } from '../registry/stores/adapters.js';
|
|
10
11
|
import { isActionTarget, isForm, isInput, isSelect, isTextArea, } from '../dom/realm.js';
|
|
11
12
|
import { asSettleBudget, nativeSetTimeout, settle } from '../timers/native/native-timers.js';
|
|
@@ -19,10 +20,12 @@ function asString(value, fallback = '') {
|
|
|
19
20
|
}
|
|
20
21
|
function requireElement(ref) {
|
|
21
22
|
const el = refs.resolve(ref);
|
|
22
|
-
if (null === el)
|
|
23
|
+
if (null === el) {
|
|
23
24
|
throw new Error(`ref '${echoRef(ref)}' no longer resolves to an element`);
|
|
24
|
-
|
|
25
|
+
}
|
|
26
|
+
if (!isActionTarget(el)) {
|
|
25
27
|
throw new Error(`ref '${echoRef(ref)}' is not an HTML or SVG element`);
|
|
28
|
+
}
|
|
26
29
|
return el;
|
|
27
30
|
}
|
|
28
31
|
const FILL_LIKE = new Set([
|
|
@@ -51,8 +54,9 @@ const GENERIC_ACTION_ARG_KEYS = new Set([
|
|
|
51
54
|
function assertUploadArgs(args) {
|
|
52
55
|
const keys = Object.keys(args);
|
|
53
56
|
const dropped = keys.filter((k) => !UPLOAD_ARG_KEYS.has(k) && !GENERIC_ACTION_ARG_KEYS.has(k));
|
|
54
|
-
if (0 === dropped.length && keys.some((k) => UPLOAD_ARG_KEYS.has(k)))
|
|
57
|
+
if (0 === dropped.length && keys.some((k) => UPLOAD_ARG_KEYS.has(k))) {
|
|
55
58
|
return;
|
|
59
|
+
}
|
|
56
60
|
const detail = 0 === dropped.length
|
|
57
61
|
? 'no file was described'
|
|
58
62
|
: `upload does not read ${dropped.join(', ')}, so it would be dropped`;
|
|
@@ -70,10 +74,12 @@ function assertActionAllowed(el, action, args) {
|
|
|
70
74
|
(action === ActionType.PRESS && 'Enter' === pressKey(args));
|
|
71
75
|
const dragTarget = action === ActionType.DRAG ? refs.resolve(dragTargetRef(args)) : null;
|
|
72
76
|
const submitter = action === ActionType.PRESS && 'Enter' === pressKey(args) ? submitControlFor(el) : null;
|
|
73
|
-
if (canTrigger)
|
|
77
|
+
if (canTrigger) {
|
|
74
78
|
assertOutwardAllowed([el, submitter, isActionTarget(dragTarget) ? dragTarget : null]);
|
|
75
|
-
|
|
79
|
+
}
|
|
80
|
+
if (action === ActionType.FILL || action === ActionType.TYPE) {
|
|
76
81
|
assertPaymentEntryAllowed(el);
|
|
82
|
+
}
|
|
77
83
|
const sourceDangerous = requiresDangerousConfirmation(el) ||
|
|
78
84
|
(submitter !== null && requiresDangerousConfirmation(submitter));
|
|
79
85
|
const targetDangerous = isActionTarget(dragTarget) && requiresDangerousConfirmation(dragTarget);
|
|
@@ -87,8 +93,9 @@ function assertActionAllowed(el, action, args) {
|
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
function optionValueFor(options, named) {
|
|
90
|
-
if (options.some((option) => option.value === named))
|
|
96
|
+
if (options.some((option) => option.value === named)) {
|
|
91
97
|
return named;
|
|
98
|
+
}
|
|
92
99
|
const byLabel = options.filter((option) => option.label.trim() === named.trim());
|
|
93
100
|
return 1 === byLabel.length ? (byLabel[0]?.value ?? named) : named;
|
|
94
101
|
}
|
|
@@ -106,8 +113,9 @@ function valueOf(el) {
|
|
|
106
113
|
}
|
|
107
114
|
function activeRef(el) {
|
|
108
115
|
const active = el.ownerDocument.activeElement;
|
|
109
|
-
if (null === active || active === el.ownerDocument.body)
|
|
116
|
+
if (null === active || active === el.ownerDocument.body) {
|
|
110
117
|
return null;
|
|
118
|
+
}
|
|
111
119
|
return refs.refFor(active);
|
|
112
120
|
}
|
|
113
121
|
async function dispatchFor(el, action, args) {
|
|
@@ -143,8 +151,9 @@ async function dispatchOther(el, action, args) {
|
|
|
143
151
|
firePointer(el, 'pointermove', from);
|
|
144
152
|
const moved = el.dispatchEvent(mouseEventFor(el, 'mousemove', { bubbles: true, cancelable: true }));
|
|
145
153
|
const holdMs = clampHoldMs(args['holdMs']);
|
|
146
|
-
if (holdMs > 0)
|
|
154
|
+
if (holdMs > 0) {
|
|
147
155
|
await sleep(holdMs);
|
|
156
|
+
}
|
|
148
157
|
return !moved;
|
|
149
158
|
}
|
|
150
159
|
case ActionType.FOCUS:
|
|
@@ -168,8 +177,9 @@ async function dispatchOther(el, action, args) {
|
|
|
168
177
|
'To empty a field on purpose use the `clear` action, or fill with an explicit "".');
|
|
169
178
|
}
|
|
170
179
|
assertEditable(el, 'fill');
|
|
171
|
-
if (!isAutofillSafeTarget(el))
|
|
180
|
+
if (!isAutofillSafeTarget(el)) {
|
|
172
181
|
el.focus();
|
|
182
|
+
}
|
|
173
183
|
return setNativeValue(el, args['value']);
|
|
174
184
|
}
|
|
175
185
|
assertNotRichText(el, 'fill');
|
|
@@ -221,8 +231,9 @@ async function dispatchOther(el, action, args) {
|
|
|
221
231
|
if ('radio' === el.type && action === ActionType.UNCHECK) {
|
|
222
232
|
throw new Error('cannot uncheck a radio button — a real user could not; select another radio in the group');
|
|
223
233
|
}
|
|
224
|
-
if (alreadyAtCheckedState(el, action))
|
|
234
|
+
if (alreadyAtCheckedState(el, action)) {
|
|
225
235
|
return false;
|
|
236
|
+
}
|
|
226
237
|
const event = mouseEventFor(el, 'click', {
|
|
227
238
|
bubbles: true,
|
|
228
239
|
cancelable: true,
|
|
@@ -233,25 +244,29 @@ async function dispatchOther(el, action, args) {
|
|
|
233
244
|
}
|
|
234
245
|
case ActionType.SUBMIT: {
|
|
235
246
|
const form = isForm(el) ? el : el.closest('form');
|
|
236
|
-
if (null === form)
|
|
247
|
+
if (null === form) {
|
|
237
248
|
throw new Error('no form to submit');
|
|
249
|
+
}
|
|
238
250
|
form.requestSubmit();
|
|
239
251
|
return false;
|
|
240
252
|
}
|
|
241
253
|
case ActionType.PRESS: {
|
|
242
254
|
const mods = pressModifiers(args);
|
|
243
255
|
const combo = pressKeys(args);
|
|
244
|
-
if (combo.length > 0)
|
|
256
|
+
if (combo.length > 0) {
|
|
245
257
|
return await pressCombo(el, combo, mods, clampHoldMs(args['holdMs']));
|
|
258
|
+
}
|
|
246
259
|
const key = pressKey(args);
|
|
247
260
|
const code = pressCode(args, key);
|
|
248
261
|
const down = asSyntheticInput(() => el.dispatchEvent(keyboardEvent('keydown', { key, code, bubbles: true, cancelable: true, ...mods })));
|
|
249
262
|
closeModalOnEscape(el, key, down);
|
|
250
|
-
if (down)
|
|
263
|
+
if (down) {
|
|
251
264
|
dispatchKeypress(el, key, code, mods);
|
|
265
|
+
}
|
|
252
266
|
const hold = clampHoldMs(args['holdMs']);
|
|
253
|
-
if (hold > 0)
|
|
267
|
+
if (hold > 0) {
|
|
254
268
|
await holdKey(el, key, code, mods, hold);
|
|
269
|
+
}
|
|
255
270
|
asSyntheticInput(() => el.dispatchEvent(keyboardEvent('keyup', { key, code, bubbles: true, ...mods })));
|
|
256
271
|
return !down;
|
|
257
272
|
}
|
|
@@ -277,8 +292,9 @@ async function dispatchOther(el, action, args) {
|
|
|
277
292
|
if (true === args['__base64']) {
|
|
278
293
|
const bin = atob(rawContent);
|
|
279
294
|
const arr = new Uint8Array(bin.length);
|
|
280
|
-
for (let i = 0; i < bin.length; i++)
|
|
295
|
+
for (let i = 0; i < bin.length; i++) {
|
|
281
296
|
arr[i] = bin.charCodeAt(i);
|
|
297
|
+
}
|
|
282
298
|
fileBody = arr.buffer.slice(0);
|
|
283
299
|
}
|
|
284
300
|
const file = new File([fileBody], asString(args['name'], 'file.txt'), {
|
|
@@ -293,8 +309,9 @@ async function dispatchOther(el, action, args) {
|
|
|
293
309
|
}
|
|
294
310
|
case ActionType.DRAG: {
|
|
295
311
|
const toRef = dragTargetRef(args);
|
|
296
|
-
if ('' === toRef)
|
|
312
|
+
if ('' === toRef) {
|
|
297
313
|
return await dragElement(el, null, args['data']);
|
|
314
|
+
}
|
|
298
315
|
const resolved = refs.resolve(toRef);
|
|
299
316
|
if (!isActionTarget(resolved)) {
|
|
300
317
|
throw new Error(`drag target '${echoRef(toRef)}' did not resolve to an element — pass a ref from ` +
|
|
@@ -309,10 +326,12 @@ async function dispatchOther(el, action, args) {
|
|
|
309
326
|
export async function executeAction(ref, action, args = {}) {
|
|
310
327
|
const named = 0 < ref.length;
|
|
311
328
|
const el = isReflessDocumentPress(ref, action, args) ? focusedOrDocument() : requireElement(ref);
|
|
312
|
-
if (named)
|
|
329
|
+
if (named) {
|
|
313
330
|
assertActionAllowed(el, action, args);
|
|
314
|
-
|
|
331
|
+
}
|
|
332
|
+
if (named) {
|
|
315
333
|
assertNotLeaving(el);
|
|
334
|
+
}
|
|
316
335
|
const anchor = named ? anchorOf(el) : {};
|
|
317
336
|
const visible = isVisible(el);
|
|
318
337
|
const enabled = enabledOf(el);
|
|
@@ -376,15 +395,19 @@ export async function executeAction(ref, action, args = {}) {
|
|
|
376
395
|
...(alreadyAtValue ? { alreadyAtValue: true } : {}),
|
|
377
396
|
...canvasesOnScreen(),
|
|
378
397
|
};
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
398
|
+
let warning;
|
|
399
|
+
if (!named) {
|
|
400
|
+
warning = ActionWarning.GLOBAL_PRESS;
|
|
401
|
+
}
|
|
402
|
+
else if (geometry.occluded) {
|
|
403
|
+
warning = ActionWarning.CLICK_OCCLUDED;
|
|
404
|
+
}
|
|
405
|
+
else if (action === ActionType.HOVER && elementHasHoverHandlers(el)) {
|
|
406
|
+
warning = ActionWarning.HOVER_NATIVE_ENTER_LEAVE;
|
|
407
|
+
}
|
|
408
|
+
else if (hitsNoControl) {
|
|
409
|
+
warning = ActionWarning.CLICK_NOT_A_CONTROL;
|
|
410
|
+
}
|
|
388
411
|
const done = result(ref, action, effect, settled, settleReason, anchor, warning);
|
|
389
412
|
return isReticleUi(el) ? { ...done, reticleUi: true } : done;
|
|
390
413
|
}
|
|
@@ -406,20 +429,27 @@ export async function executeSequence(steps) {
|
|
|
406
429
|
settled: res.settled,
|
|
407
430
|
settleReason: res.settleReason,
|
|
408
431
|
};
|
|
409
|
-
if (res.testid !== undefined)
|
|
432
|
+
if (res.testid !== undefined) {
|
|
410
433
|
stepBase.testid = res.testid;
|
|
411
|
-
|
|
434
|
+
}
|
|
435
|
+
if (res.component !== undefined) {
|
|
412
436
|
stepBase.component = res.component;
|
|
413
|
-
|
|
437
|
+
}
|
|
438
|
+
if (res.role !== undefined) {
|
|
414
439
|
stepBase.role = res.role;
|
|
415
|
-
|
|
440
|
+
}
|
|
441
|
+
if (res.name !== undefined) {
|
|
416
442
|
stepBase.name = res.name;
|
|
417
|
-
|
|
443
|
+
}
|
|
444
|
+
if (res.source !== undefined) {
|
|
418
445
|
stepBase.source = res.source;
|
|
419
|
-
|
|
446
|
+
}
|
|
447
|
+
if (res.secret !== undefined) {
|
|
420
448
|
stepBase.secret = res.secret;
|
|
421
|
-
|
|
449
|
+
}
|
|
450
|
+
if (res.warning !== undefined) {
|
|
422
451
|
stepBase.warning = res.warning;
|
|
452
|
+
}
|
|
423
453
|
stepResults.push(stepBase);
|
|
424
454
|
}
|
|
425
455
|
return { ok: true, count: steps.length, effects, steps: stepResults };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutwardAction, isDangerousActionText,
|
|
1
|
+
import { OutwardAction, isDangerousActionText, isDangerousNavigation, isInertHref, isPaymentField, outwardActionOf, outwardRefusal, withoutFilenameTokens, } from '@reticlehq/core';
|
|
2
2
|
import { getAccessibleName, getRole } from '../dom/a11y.js';
|
|
3
3
|
import { isHtmlElement } from '../dom/realm.js';
|
|
4
4
|
const LABEL_VALUED_INPUT_TYPES = new Set([
|
|
@@ -8,34 +8,39 @@ const LABEL_VALUED_INPUT_TYPES = new Set([
|
|
|
8
8
|
'image',
|
|
9
9
|
]);
|
|
10
10
|
function holdsUserText(el) {
|
|
11
|
-
if (isHtmlElement(el) && el.isContentEditable)
|
|
11
|
+
if (isHtmlElement(el) && el.isContentEditable) {
|
|
12
12
|
return true;
|
|
13
|
-
|
|
13
|
+
}
|
|
14
|
+
if (el instanceof HTMLTextAreaElement) {
|
|
14
15
|
return true;
|
|
15
|
-
|
|
16
|
+
}
|
|
17
|
+
if (el instanceof HTMLInputElement) {
|
|
16
18
|
return !LABEL_VALUED_INPUT_TYPES.has(el.type.toLowerCase());
|
|
19
|
+
}
|
|
17
20
|
return false;
|
|
18
21
|
}
|
|
19
22
|
const ANCHOR_TAG = 'A';
|
|
20
23
|
function navigatingHref(el) {
|
|
21
|
-
if (ANCHOR_TAG !== el.tagName)
|
|
24
|
+
if (ANCHOR_TAG !== el.tagName) {
|
|
22
25
|
return undefined;
|
|
26
|
+
}
|
|
23
27
|
const href = el.getAttribute('href');
|
|
24
28
|
return null === href || isInertHref(href) ? undefined : href;
|
|
25
29
|
}
|
|
26
30
|
export function dangerousActionContext(el) {
|
|
27
31
|
const href = navigatingHref(el);
|
|
28
|
-
if (href !== undefined)
|
|
32
|
+
if (href !== undefined) {
|
|
29
33
|
return href;
|
|
34
|
+
}
|
|
30
35
|
const form = el.closest('form');
|
|
31
36
|
const ownText = holdsUserText(el)
|
|
32
37
|
? ['', '']
|
|
33
38
|
: [el.textContent ?? '', el.getAttribute('value') ?? ''];
|
|
34
39
|
return [
|
|
35
|
-
getAccessibleName(el),
|
|
36
|
-
|
|
37
|
-
el.getAttribute('title') ?? '',
|
|
38
|
-
el.getAttribute('aria-label') ?? '',
|
|
40
|
+
withoutFilenameTokens(getAccessibleName(el)),
|
|
41
|
+
withoutFilenameTokens(ownText.join(' ')),
|
|
42
|
+
withoutFilenameTokens(el.getAttribute('title') ?? ''),
|
|
43
|
+
withoutFilenameTokens(el.getAttribute('aria-label') ?? ''),
|
|
39
44
|
el.getAttribute('href') ?? '',
|
|
40
45
|
form?.getAttribute('action') ?? '',
|
|
41
46
|
].join(' ');
|
|
@@ -47,8 +52,9 @@ export function submitControlFor(el) {
|
|
|
47
52
|
}
|
|
48
53
|
export function requiresDangerousConfirmation(el) {
|
|
49
54
|
const href = navigatingHref(el);
|
|
50
|
-
if (href !== undefined)
|
|
55
|
+
if (href !== undefined) {
|
|
51
56
|
return isDangerousNavigation(href);
|
|
57
|
+
}
|
|
52
58
|
return isDangerousActionText(dangerousActionContext(el), getRole(el));
|
|
53
59
|
}
|
|
54
60
|
let outwardAllowed;
|
|
@@ -56,19 +62,23 @@ export function setOutwardPolicy(allowed) {
|
|
|
56
62
|
outwardAllowed = allowed;
|
|
57
63
|
}
|
|
58
64
|
export function assertOutwardAllowed(targets) {
|
|
59
|
-
if (outwardAllowed === undefined)
|
|
65
|
+
if (outwardAllowed === undefined) {
|
|
60
66
|
return;
|
|
67
|
+
}
|
|
61
68
|
for (const el of targets) {
|
|
62
|
-
if (null === el)
|
|
69
|
+
if (null === el) {
|
|
63
70
|
continue;
|
|
71
|
+
}
|
|
64
72
|
const kind = outwardActionOf(dangerousActionContext(el), getRole(el));
|
|
65
|
-
if (kind !== undefined && !outwardAllowed.includes(kind))
|
|
73
|
+
if (kind !== undefined && !outwardAllowed.includes(kind)) {
|
|
66
74
|
throw new Error(outwardRefusal(kind));
|
|
75
|
+
}
|
|
67
76
|
}
|
|
68
77
|
}
|
|
69
78
|
export function assertPaymentEntryAllowed(el) {
|
|
70
|
-
if (outwardAllowed === undefined || outwardAllowed.includes(OutwardAction.PAY))
|
|
79
|
+
if (outwardAllowed === undefined || outwardAllowed.includes(OutwardAction.PAY)) {
|
|
71
80
|
return;
|
|
81
|
+
}
|
|
72
82
|
const facts = {
|
|
73
83
|
autocomplete: el.getAttribute('autocomplete') ?? undefined,
|
|
74
84
|
name: el.getAttribute('name') ?? undefined,
|
|
@@ -76,6 +86,7 @@ export function assertPaymentEntryAllowed(el) {
|
|
|
76
86
|
inputMode: el.getAttribute('inputmode') ?? undefined,
|
|
77
87
|
label: `${getAccessibleName(el)} ${el.getAttribute('placeholder') ?? ''}`,
|
|
78
88
|
};
|
|
79
|
-
if (isPaymentField(facts))
|
|
89
|
+
if (isPaymentField(facts)) {
|
|
80
90
|
throw new Error(outwardRefusal(OutwardAction.PAY));
|
|
91
|
+
}
|
|
81
92
|
}
|
package/dist/actions/scroll.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ interface ScrollResult {
|
|
|
11
11
|
atLeftEnd: boolean;
|
|
12
12
|
atRightEnd: boolean;
|
|
13
13
|
}
|
|
14
|
+
export declare const SCROLL_FRACTION_RANGE_ERROR_PREFIX = "scroll fraction must be between 0 and 1, got ";
|
|
15
|
+
export declare function fractionOutOfRangeMessage(fraction: number): string;
|
|
14
16
|
export declare function scrollContainer(ref: string | undefined, dy: number | undefined, fraction?: number, dx?: number): ScrollResult;
|
|
15
17
|
export declare function scrollFrom(base: Element | undefined, dy: number | undefined, fraction?: number, dx?: number): ScrollResult;
|
|
16
18
|
export {};
|
package/dist/actions/scroll.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { refs } from '../dom/addressing/refs.js';
|
|
2
2
|
const FALLBACK_STEP_PX = 400;
|
|
3
3
|
const VIEWPORT_FRACTION = 0.8;
|
|
4
|
+
export const SCROLL_FRACTION_RANGE_ERROR_PREFIX = 'scroll fraction must be between 0 and 1, got ';
|
|
5
|
+
export function fractionOutOfRangeMessage(fraction) {
|
|
6
|
+
return `${SCROLL_FRACTION_RANGE_ERROR_PREFIX}${fraction}`;
|
|
7
|
+
}
|
|
4
8
|
function nearestScrollable(el, axis) {
|
|
5
9
|
let cur = el;
|
|
6
10
|
while (cur !== null && cur !== document.body) {
|
|
@@ -18,18 +22,22 @@ export function scrollContainer(ref, dy, fraction, dx) {
|
|
|
18
22
|
return scrollFrom(base instanceof Element ? base : undefined, dy, fraction, dx);
|
|
19
23
|
}
|
|
20
24
|
export function scrollFrom(base, dy, fraction, dx) {
|
|
25
|
+
if (fraction !== undefined && (fraction < 0 || fraction > 1 || Number.isNaN(fraction))) {
|
|
26
|
+
throw new RangeError(fractionOutOfRangeMessage(fraction));
|
|
27
|
+
}
|
|
21
28
|
const axis = dx !== undefined && dx !== 0 && (dy === undefined || 0 === dy) ? 'x' : 'y';
|
|
22
29
|
const target = base !== undefined
|
|
23
30
|
? nearestScrollable(base, axis)
|
|
24
31
|
: (document.scrollingElement ?? document.documentElement);
|
|
25
32
|
const before = target.scrollTop;
|
|
26
33
|
const beforeLeft = target.scrollLeft;
|
|
27
|
-
if (fraction !== undefined
|
|
34
|
+
if (fraction !== undefined) {
|
|
28
35
|
target.scrollTop = Math.round(target.scrollHeight * fraction);
|
|
29
36
|
}
|
|
30
37
|
else {
|
|
31
|
-
if (dx !== undefined && dx !== 0)
|
|
38
|
+
if (dx !== undefined && dx !== 0) {
|
|
32
39
|
target.scrollLeft = beforeLeft + dx;
|
|
40
|
+
}
|
|
33
41
|
if (dx === undefined || (dy !== undefined && 0 !== dy)) {
|
|
34
42
|
const step = dy ?? (Math.round(target.clientHeight * VIEWPORT_FRACTION) || FALLBACK_STEP_PX);
|
|
35
43
|
target.scrollTop = before + step;
|
package/dist/dom/a11y.d.ts
CHANGED
|
@@ -4,8 +4,4 @@ export declare function getAccessibleName(el: Element): string;
|
|
|
4
4
|
export declare function getStates(el: Element, visible?: boolean): ElementState[];
|
|
5
5
|
export declare function isSensitiveField(el: Element): boolean;
|
|
6
6
|
export declare function getValue(el: Element): string | undefined;
|
|
7
|
-
export declare function isHiddenByAriaOnly(el: Element): boolean;
|
|
8
|
-
export declare function isRenderedOutsideAriaHidden(el: Element): boolean;
|
|
9
|
-
export declare function isInViewport(el: Element, memo?: Map<Element, boolean>): boolean;
|
|
10
|
-
export declare function isVisible(el: Element, memo?: Map<Element, boolean>): boolean;
|
|
11
7
|
export declare function describe(el: Element, memo?: Map<Element, boolean>): ElementDescriptor;
|