@astryxdesign/core 0.7.6-canary.cebf4a9 → 0.7.6-canary.d29fe0f
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/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +8 -3
- package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.js +1 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +6 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +5 -2
- package/dist/gestures/GestureArena.d.ts +1 -1
- package/dist/gestures/GestureArena.d.ts.map +1 -1
- package/dist/gestures/GestureArena.js +2 -1
- package/dist/gestures/GestureHost.d.ts +2 -3
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +7 -7
- package/package.json +4 -4
- package/src/Chat/ChatDictationButton.spec.md +126 -81
- package/src/Link/Link.spec.md +3 -2
- package/src/Link/Link.tsx +11 -0
- package/src/Link/__tests__/Link.truncation.a11y.chromium.spec.ts +58 -0
- package/src/Markdown/plugins/headingLinks.a11y.chromium.spec.ts +1 -1
- package/src/RadioList/RadioList.spec.md +1 -1
- package/src/RadioList/RadioList.test.tsx +28 -0
- package/src/RadioList/RadioListItem.tsx +6 -0
- package/src/Switch/Switch.spec.md +1 -1
- package/src/Switch/Switch.test.tsx +21 -1
- package/src/Switch/Switch.tsx +5 -1
- package/src/gestures/GestureArena.ts +1 -1
- package/src/gestures/GestureHost.ts +6 -7
package/dist/Link/Link.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../src/Link/Link.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAc5C,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../src/Link/Link.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAc5C,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AA8G/C,MAAM,WAAW,SAAU,SAAQ,SAAS,CAC1C,iBAAiB,GAAG,iBAAiB,CACtC;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,GAAG,iBAAiB,CAAC,CAAC;IACvD;;;;;OAKG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B;;OAEG;IACH,cAAc,CAAC,EAAE,KAAK,CAAC,2BAA2B,CAAC;IACnD;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,GAAG,iBAAiB,CAAC,CAAC;IACzE;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;OAEG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,EACnB,EAAE,EACF,KAAK,EACL,IAAI,EACJ,YAAoB,EACpB,UAAkB,EAClB,cAAsB,EACtB,WAAW,EAAE,oBAAoB,EACjC,MAAM,EAAE,eAAe,EACvB,OAAO,EACP,OAAO,EACP,YAAoB,EACpB,IAAa,EACb,IAAI,EACJ,MAAM,EACN,KAAgB,EAChB,OAAkB,EAClB,QAAY,EACZ,QAAQ,EACR,GAAG,EAAE,YAAY,EACjB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,SAAS,+BAuJX;yBAhLe,IAAI"}
|
package/dist/Link/Link.js
CHANGED
|
@@ -53,6 +53,11 @@ const styles = {
|
|
|
53
53
|
kAMwcw: "xlr8y92",
|
|
54
54
|
$$css: true
|
|
55
55
|
},
|
|
56
|
+
truncating: {
|
|
57
|
+
ks0D6T: "x193iq5w",
|
|
58
|
+
k7Eaqz: "xeuugli",
|
|
59
|
+
$$css: true
|
|
60
|
+
},
|
|
56
61
|
buttonReset: {
|
|
57
62
|
kWkggS: "xjbqb8w",
|
|
58
63
|
ksu8eU: "xng3xce",
|
|
@@ -206,7 +211,7 @@ export function Link({
|
|
|
206
211
|
}), focusOutlineProps.focusVisible(styles.base, styles.buttonReset, linkColorStyles[color],
|
|
207
212
|
// The system's pressed overlay behind the text; the hover stays the
|
|
208
213
|
// colour change above, so a press is the one background it paints.
|
|
209
|
-
!isDisabled && interactionOverlayStyles.pressedBackgroundColor, hasUnderline && styles.hasUnderline, isStandalone && styles.standalone, isDisabled && styles.disabled, xstyle), className, style),
|
|
214
|
+
!isDisabled && interactionOverlayStyles.pressedBackgroundColor, hasUnderline && styles.hasUnderline, isStandalone && styles.standalone, isDisabled && styles.disabled, maxLines > 0 && styles.truncating, xstyle), className, style),
|
|
210
215
|
...props,
|
|
211
216
|
...(isDisabled ? disabledInteractionProps(props.onKeyDown) : null),
|
|
212
217
|
children: sharedContent
|
|
@@ -226,7 +231,7 @@ export function Link({
|
|
|
226
231
|
tabIndex: 0,
|
|
227
232
|
...mergeProps(themeProps('link', {
|
|
228
233
|
color
|
|
229
|
-
}), focusOutlineProps.focusVisible(styles.base, linkColorStyles[color], hasUnderline && styles.hasUnderline, isStandalone && styles.standalone, styles.disabled, xstyle), className, style),
|
|
234
|
+
}), focusOutlineProps.focusVisible(styles.base, linkColorStyles[color], hasUnderline && styles.hasUnderline, isStandalone && styles.standalone, styles.disabled, maxLines > 0 && styles.truncating, xstyle), className, style),
|
|
230
235
|
...props,
|
|
231
236
|
...disabledInteractionProps(props.onKeyDown),
|
|
232
237
|
children: sharedContent
|
|
@@ -244,7 +249,7 @@ export function Link({
|
|
|
244
249
|
tabIndex: isDisabled ? -1 : undefined,
|
|
245
250
|
...mergeProps(themeProps('link', {
|
|
246
251
|
color
|
|
247
|
-
}), focusOutlineProps.focusVisible(styles.base, linkColorStyles[color], !isDisabled && interactionOverlayStyles.pressedBackgroundColor, hasUnderline && styles.hasUnderline, isStandalone && styles.standalone, isDisabled && styles.disabled, xstyle), className, style),
|
|
252
|
+
}), focusOutlineProps.focusVisible(styles.base, linkColorStyles[color], !isDisabled && interactionOverlayStyles.pressedBackgroundColor, hasUnderline && styles.hasUnderline, isStandalone && styles.standalone, isDisabled && styles.disabled, maxLines > 0 && styles.truncating, xstyle), className, style),
|
|
248
253
|
...props,
|
|
249
254
|
children: sharedContent
|
|
250
255
|
});
|
|
@@ -16,7 +16,7 @@ import * as fs from 'node:fs';
|
|
|
16
16
|
import * as path from 'node:path';
|
|
17
17
|
import { expect, test } from '@playwright/test';
|
|
18
18
|
import { DEFAULT_STORYBOOK_DIR, serveStorybook } from '@astryxdesign/a11y-spec/storybook';
|
|
19
|
-
const STORY = 'core-markdown
|
|
19
|
+
const STORY = 'core-markdown--heading-links';
|
|
20
20
|
const OUTPUT = path.resolve('test-results/markdown-heading-links');
|
|
21
21
|
const GEOMETRY_TOLERANCE_PX = 1;
|
|
22
22
|
let storybook;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RadioListItem.d.ts","sourceRoot":"","sources":["../../src/RadioList/RadioListItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,EAAqB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGhE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,OAAO,EAAO,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AA0I7C,MAAM,WAAW,kBAAmB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;;;;;OAUG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,KAAK,EACL,YAAY,EAAE,SAAS,EACvB,KAAK,EACL,WAAW,EACX,UAAU,EAAE,cAAsB,EAClC,YAAY,EACZ,UAAU,EACV,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,GAAG,IAAI,EACR,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"RadioListItem.d.ts","sourceRoot":"","sources":["../../src/RadioList/RadioListItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,EAAqB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGhE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAQ5C,OAAO,EAAO,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AA0I7C,MAAM,WAAW,kBAAmB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;;;;;OAUG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,KAAK,EACL,YAAY,EAAE,SAAS,EACvB,KAAK,EACL,WAAW,EACX,UAAU,EAAE,cAAsB,EAClC,YAAY,EACZ,UAAU,EACV,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,GAAG,IAAI,EACR,EAAE,kBAAkB,qBA2KpB;yBA1Le,aAAa"}
|
|
@@ -198,7 +198,12 @@ export function RadioListItem({
|
|
|
198
198
|
// from submission, matching a natively disabled control.
|
|
199
199
|
,
|
|
200
200
|
form: keepsFocusableForMessage ? '' : undefined,
|
|
201
|
-
required: context.isRequired
|
|
201
|
+
required: context.isRequired
|
|
202
|
+
// Chromium reports an unanswered native-required radio invalid before
|
|
203
|
+
// any interaction; the invalid state follows the group's status
|
|
204
|
+
// instead (RadioList FR6).
|
|
205
|
+
,
|
|
206
|
+
"aria-invalid": context.isRequired ? context.status?.type === 'error' : undefined,
|
|
202
207
|
onChange: () => {
|
|
203
208
|
if (isDisabled) {
|
|
204
209
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../src/Switch/Switch.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../src/Switch/Switch.tsx"],"names":[],"mappings":"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"}
|
package/dist/Switch/Switch.js
CHANGED
|
@@ -401,8 +401,11 @@ export function Switch({
|
|
|
401
401
|
// than the field wrapper via `...rest`.
|
|
402
402
|
,
|
|
403
403
|
"aria-labelledby": ariaLabelledByProp,
|
|
404
|
-
"aria-describedby": ariaDescribedBy
|
|
405
|
-
|
|
404
|
+
"aria-describedby": ariaDescribedBy
|
|
405
|
+
// Chromium reports an unchecked native-required checkbox invalid before
|
|
406
|
+
// any interaction; the invalid state follows status instead.
|
|
407
|
+
,
|
|
408
|
+
"aria-invalid": status?.type === 'error' ? true : isRequired ? false : undefined,
|
|
406
409
|
"aria-busy": isBusy || undefined,
|
|
407
410
|
...stylex.props(styles.input, rtlStyles.centerInline('-50%'), styles.inputCoarse, inputSizeStyles[size], isDisabled && styles.inputDisabled, isBusy && styles.inputBusy, switchFocusInput)
|
|
408
411
|
}), /*#__PURE__*/_jsx("div", {
|
|
@@ -138,7 +138,6 @@ export declare class GestureArena {
|
|
|
138
138
|
setTimer(recognizer: GestureRecognizer, deadline: number, callback: () => void): void;
|
|
139
139
|
/** @internal */
|
|
140
140
|
clearTimer(recognizer: GestureRecognizer): void;
|
|
141
|
-
/** Cancel an active recognizer, or fail a possible one. */
|
|
142
141
|
/**
|
|
143
142
|
* Runs `body`, code outside input that stops recognizers (an Effect that
|
|
144
143
|
* turns one off or detaches it), without resolving what those stops
|
|
@@ -151,6 +150,7 @@ export declare class GestureArena {
|
|
|
151
150
|
deferResolution(body: () => void): void;
|
|
152
151
|
/** The last event's time, or now before any event. */
|
|
153
152
|
private lastEventTime;
|
|
153
|
+
/** Cancel an active recognizer, or fail a possible one. */
|
|
154
154
|
stop(recognizer: GestureRecognizer): void;
|
|
155
155
|
private attemptRecognition;
|
|
156
156
|
/** Whether `winner` being recognized prevents `other`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GestureArena.d.ts","sourceRoot":"","sources":["../../src/gestures/GestureArena.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"GestureArena.d.ts","sourceRoot":"","sources":["../../src/gestures/GestureArena.ts"],"names":[],"mappings":"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"}
|
|
@@ -405,7 +405,6 @@ export class GestureArena {
|
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
/** Cancel an active recognizer, or fail a possible one. */
|
|
409
408
|
/**
|
|
410
409
|
* Runs `body`, code outside input that stops recognizers (an Effect that
|
|
411
410
|
* turns one off or detaches it), without resolving what those stops
|
|
@@ -435,6 +434,8 @@ export class GestureArena {
|
|
|
435
434
|
lastEventTime() {
|
|
436
435
|
return Number.isFinite(this.latestTime) ? this.latestTime : this.clock.now();
|
|
437
436
|
}
|
|
437
|
+
|
|
438
|
+
/** Cancel an active recognizer, or fail a possible one. */
|
|
438
439
|
stop(recognizer) {
|
|
439
440
|
// A cancel from code between events happens at the last event's time:
|
|
440
441
|
// wall time would run deadlines ahead of input still queued (FR16).
|
|
@@ -59,7 +59,6 @@ export declare class GestureHost {
|
|
|
59
59
|
private readonly touchIds;
|
|
60
60
|
/** Touch.identifier → the element the finger landed on. */
|
|
61
61
|
private readonly touchTargets;
|
|
62
|
-
/** Finger targets with listeners of their own, and how many fingers each. */
|
|
63
62
|
/**
|
|
64
63
|
* Finger targets with listeners of their own: how many fingers each, and
|
|
65
64
|
* how many of those a recognizer could claim while they move (only those
|
|
@@ -102,19 +101,19 @@ export declare class GestureHost {
|
|
|
102
101
|
private readonly claimedLifts;
|
|
103
102
|
/** Fingers still waiting for their pointerdown, by arena id. */
|
|
104
103
|
private readonly unpairedTouches;
|
|
105
|
-
/** The last position fed per contact, so one channel's echo is dropped. */
|
|
106
104
|
/** The elements each held contact turned selection off on. */
|
|
107
105
|
private readonly heldSelectionBy;
|
|
108
106
|
private readonly isPrimaryPointerCoarse;
|
|
109
107
|
private readonly coarseQuery;
|
|
110
108
|
private isWatchingPointer;
|
|
111
109
|
private readonly onPointerKindChange;
|
|
110
|
+
/** The last position fed per contact, so one channel's echo is dropped. */
|
|
112
111
|
private readonly lastFed;
|
|
113
112
|
/** When a finger was last down or lifted, and where it landed. */
|
|
114
113
|
private lastTouch;
|
|
115
|
-
/** The last scroll a finger drove, and when. */
|
|
116
114
|
/** Scrollers a recognizer is scrolling itself this frame (ownScroll). */
|
|
117
115
|
private readonly ownedScrolls;
|
|
116
|
+
/** The last scroll a finger drove, and when. */
|
|
118
117
|
private lastScroll;
|
|
119
118
|
private nextId;
|
|
120
119
|
private isInstalled;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GestureHost.d.ts","sourceRoot":"","sources":["../../src/gestures/GestureHost.ts"],"names":[],"mappings":"AA8FA,OAAO,EAEL,YAAY,EAGZ,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAgB3D,OAAO,KAAK,EAEV,YAAY,EACZ,WAAW,EAEX,aAAa,EAGd,MAAM,SAAS,CAAC;AAyBjB,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACtC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAC;IACnD;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,GAAG,aAAa,GAAG,IAAI,CAAC;CAC1E;AAwHD,qBAAa,WAAW;IACtB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAW;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAC9D,qDAAqD;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D
|
|
1
|
+
{"version":3,"file":"GestureHost.d.ts","sourceRoot":"","sources":["../../src/gestures/GestureHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -120,10 +120,6 @@ function claimsWhileMoving(recognizer) {
|
|
|
120
120
|
return recognizer.acceptsTouchInput && recognizer.kind !== 'tap';
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
/**
|
|
124
|
-
* Run `task` in the next frame, before it paints: a read there styles what
|
|
125
|
-
* the frame must style anyway, and its writes are what the frame shows.
|
|
126
|
-
*/
|
|
127
123
|
/**
|
|
128
124
|
* A CSS-wide keyword, which names no touch-action of its own: the value in
|
|
129
125
|
* force resolves it (`initial` and `unset` to `auto`, `inherit` to the
|
|
@@ -133,6 +129,11 @@ function isCssWideKeyword(value) {
|
|
|
133
129
|
return CSS_WIDE_KEYWORDS.has(value.trim().toLowerCase());
|
|
134
130
|
}
|
|
135
131
|
const CSS_WIDE_KEYWORDS = new Set(['initial', 'inherit', 'unset', 'revert', 'revert-layer']);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Run `task` in the next frame, before it paints: a read there styles what
|
|
135
|
+
* the frame must style anyway, and its writes are what the frame shows.
|
|
136
|
+
*/
|
|
136
137
|
function inNextFrame(view, task) {
|
|
137
138
|
if (view != null && typeof view.requestAnimationFrame === 'function') {
|
|
138
139
|
view.requestAnimationFrame(task);
|
|
@@ -193,7 +194,6 @@ export class GestureHost {
|
|
|
193
194
|
touchIds = new Map();
|
|
194
195
|
/** Touch.identifier → the element the finger landed on. */
|
|
195
196
|
touchTargets = new Map();
|
|
196
|
-
/** Finger targets with listeners of their own, and how many fingers each. */
|
|
197
197
|
/**
|
|
198
198
|
* Finger targets with listeners of their own: how many fingers each, and
|
|
199
199
|
* how many of those a recognizer could claim while they move (only those
|
|
@@ -236,17 +236,17 @@ export class GestureHost {
|
|
|
236
236
|
claimedLifts = [];
|
|
237
237
|
/** Fingers still waiting for their pointerdown, by arena id. */
|
|
238
238
|
unpairedTouches = new Map();
|
|
239
|
-
/** The last position fed per contact, so one channel's echo is dropped. */
|
|
240
239
|
/** The elements each held contact turned selection off on. */
|
|
241
240
|
heldSelectionBy = new Map();
|
|
242
241
|
isWatchingPointer = false;
|
|
243
242
|
onPointerKindChange = () => this.refreshAll();
|
|
243
|
+
/** The last position fed per contact, so one channel's echo is dropped. */
|
|
244
244
|
lastFed = new Map();
|
|
245
245
|
/** When a finger was last down or lifted, and where it landed. */
|
|
246
246
|
lastTouch = null;
|
|
247
|
-
/** The last scroll a finger drove, and when. */
|
|
248
247
|
/** Scrollers a recognizer is scrolling itself this frame (ownScroll). */
|
|
249
248
|
ownedScrolls = new Set();
|
|
249
|
+
/** The last scroll a finger drove, and when. */
|
|
250
250
|
lastScroll = null;
|
|
251
251
|
nextId = 1;
|
|
252
252
|
isInstalled = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.6-canary.
|
|
3
|
+
"version": "0.7.6-canary.d29fe0f",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -847,9 +847,9 @@
|
|
|
847
847
|
"react-dom": ">=19.0.0"
|
|
848
848
|
},
|
|
849
849
|
"devDependencies": {
|
|
850
|
-
"@astryxdesign/a11y-spec": "0.7.6-canary.
|
|
851
|
-
"@astryxdesign/cli": "0.7.6-canary.
|
|
852
|
-
"@astryxdesign/interaction-harness": "0.7.6-canary.
|
|
850
|
+
"@astryxdesign/a11y-spec": "0.7.6-canary.d29fe0f",
|
|
851
|
+
"@astryxdesign/cli": "0.7.6-canary.d29fe0f",
|
|
852
|
+
"@astryxdesign/interaction-harness": "0.7.6-canary.d29fe0f",
|
|
853
853
|
"@babel/cli": "^7.29.7",
|
|
854
854
|
"@babel/core": "^7.29.7",
|
|
855
855
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
schema_version: 3
|
|
3
|
-
template_version:
|
|
3
|
+
template_version: 7
|
|
4
4
|
kind: component
|
|
5
5
|
id: component:ChatDictationButton
|
|
6
|
-
authority:
|
|
6
|
+
authority: current
|
|
7
7
|
archive_reason: null
|
|
8
8
|
superseded_by: null
|
|
9
|
-
approved_by:
|
|
10
|
-
approved_at:
|
|
9
|
+
approved_by: cixzhang
|
|
10
|
+
approved_at: 2026-10-31
|
|
11
11
|
owners: [cixzhang]
|
|
12
12
|
review_triggers: [public-api, behavior, accessibility, theming, testing]
|
|
13
13
|
verified_by:
|
|
@@ -18,7 +18,7 @@ verified_by:
|
|
|
18
18
|
]
|
|
19
19
|
modules: []
|
|
20
20
|
families: []
|
|
21
|
-
design_specs: []
|
|
21
|
+
design_specs: [design:reduced-motion]
|
|
22
22
|
architecture:
|
|
23
23
|
[
|
|
24
24
|
architecture:public-component-api,
|
|
@@ -28,133 +28,178 @@ architecture:
|
|
|
28
28
|
architecture:knowledge-contracts,
|
|
29
29
|
]
|
|
30
30
|
contributing: []
|
|
31
|
-
system_specs: [spec:AST-029]
|
|
31
|
+
system_specs: [spec:AST-029, spec:AST-027]
|
|
32
32
|
---
|
|
33
33
|
|
|
34
34
|
# ChatDictationButton component contract
|
|
35
35
|
|
|
36
36
|
## Contract at a glance
|
|
37
37
|
|
|
38
|
-
| Area | Contract
|
|
39
|
-
| ----------------------- |
|
|
40
|
-
| Public contract | `dictation`
|
|
41
|
-
| Behavior |
|
|
42
|
-
| End-user impact | Voice input
|
|
43
|
-
| Builder impact |
|
|
44
|
-
| Compatibility/readiness |
|
|
45
|
-
| Review checks | Reject an enabled unsupported action, a missing
|
|
46
|
-
| Governing rules | `architecture:component-
|
|
38
|
+
| Area | Contract |
|
|
39
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| Public contract | `ChatDictationButton` with a required `dictation` state (`isSupported`, `isListening`, `volume`, `bands`, `toggle`), `size` (`sm`, `md`; default `md`), `isHiddenWhenUnsupported` (default true), `label`, `ref`, `xstyle`, `className`, `style`, and span props. |
|
|
41
|
+
| Behavior | An icon-only microphone Button that toggles dictation. While listening it shows five volume-reactive bars instead of the microphone; unsupported dictation renders nothing, or a disabled Button when kept visible. |
|
|
42
|
+
| End-user impact | Voice input is one labelled button that says whether it starts or stops dictation; browsers without speech recognition see no action that cannot run. |
|
|
43
|
+
| Builder impact | None. |
|
|
44
|
+
| Compatibility/readiness | Records shipped behavior; no public change. Two implementation gaps, listed under Current-state impact. |
|
|
45
|
+
| Review checks | Reject an enabled unsupported action, a missing or stale start/stop name, bars exposed to assistive technology, or clipping color that a theme cannot retint. |
|
|
46
|
+
| Governing rules | `architecture:public-component-api`; `architecture:theme-tokens` INV3; `architecture:component-style-authoring` INV7; `design:reduced-motion`. |
|
|
47
47
|
|
|
48
48
|
This table is a review projection; the body below is authoritative.
|
|
49
49
|
|
|
50
50
|
## Intent
|
|
51
51
|
|
|
52
|
-
ChatDictationButton
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
ChatDictationButton is the visible control for a speech-recognition state
|
|
53
|
+
owner. It turns that owner's support and listening state into one icon-only
|
|
54
|
+
Button and a five-bar volume display. Consumer usage lives in
|
|
55
|
+
`ChatDictationButton.doc.mjs`.
|
|
55
56
|
|
|
56
57
|
## Compatibility and migration
|
|
57
58
|
|
|
58
|
-
- Released default preserved: yes
|
|
59
|
-
- Compatibility class:
|
|
60
|
-
-
|
|
59
|
+
- Released default preserved: `yes`
|
|
60
|
+
- Compatibility class: records shipped behavior; no public change
|
|
61
|
+
- Controlled/uncontrolled behavior: listening state is owned by the supplied
|
|
62
|
+
`dictation` value
|
|
61
63
|
- Migration decision: none
|
|
62
64
|
|
|
65
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
66
|
+
|
|
67
|
+
## Current-state impact
|
|
68
|
+
|
|
69
|
+
- **The volume bars are extra elements.** The listening display is a container
|
|
70
|
+
and five bar `span` elements that no theme target paints.
|
|
71
|
+
`architecture:component-style-authoring` INV7 renders one element per
|
|
72
|
+
semantic role or themable part.
|
|
73
|
+
|
|
74
|
+
- **The bars set a z-index without isolation.** The bar container carries
|
|
75
|
+
`z-index: 1` and the wrapper does not isolate it, so the bars take part in
|
|
76
|
+
the page's stacking order. `spec:AST-027` FR1 keeps z-index local and lists
|
|
77
|
+
this case as a containment gap.
|
|
78
|
+
|
|
63
79
|
## Ownership boundary
|
|
64
80
|
|
|
65
81
|
**Owns**
|
|
66
82
|
|
|
67
|
-
- The start
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
83
|
+
- The start and stop name, and the caller's `label` override (FR1, FR2).
|
|
84
|
+
- Hiding or disabling the control when speech recognition is unsupported
|
|
85
|
+
(FR3).
|
|
86
|
+
- The five-bar display of the supplied bands and its clipping color (FR2, FR4).
|
|
87
|
+
- The wrapper span's ref, props, and styling (FR5).
|
|
72
88
|
|
|
73
89
|
**Does not own / non-goals**
|
|
74
90
|
|
|
75
|
-
- Speech recognition support detection, lifecycle, transcripts,
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
|
|
91
|
+
- Speech recognition support detection, lifecycle, transcripts, errors, and
|
|
92
|
+
microphone permission — owned by the `dictation` state owner.
|
|
93
|
+
- Activation, focus, disabled semantics, and icon-only presentation — owned by
|
|
94
|
+
`component:Button`.
|
|
95
|
+
- Placement in ChatComposer — the caller's composition.
|
|
96
|
+
- A public color, threshold, or bar-shape option.
|
|
79
97
|
|
|
80
98
|
## Public concepts
|
|
81
99
|
|
|
82
|
-
| Concept
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
| label | translated default; caller override | Names the action for assistive technology. | state-dependent translation | component/caller | stable |
|
|
88
|
-
| root surface | ref; DOM/data/ARIA props; className/style/xstyle | Extends the positioning wrapper. | omitted | public component API | stable |
|
|
100
|
+
| Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
|
|
101
|
+
| --------- | -------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------- | ----------------------- | ------------------------------- | --------- | -------------------------------- |
|
|
102
|
+
| Support | supported; unsupported and hidden; unsupported and shown | Whether the control renders and whether it can operate. | Every control. | hidden when unsupported | `component:ChatDictationButton` | Stable | TypeScript rejects other values. |
|
|
103
|
+
| Listening | idle; listening | Selects the start or stop name and the microphone or bars. | Supported controls. | idle | `dictation` owner | Stable | Not applicable. |
|
|
104
|
+
| Size | `sm`, `md` | Sizes the Button and bars together. | Every control. | `md` | `component:ChatDictationButton` | Stable | TypeScript rejects other values. |
|
|
89
105
|
|
|
90
106
|
## Behavioral and layout contract
|
|
91
107
|
|
|
92
|
-
| ID |
|
|
93
|
-
| --- |
|
|
94
|
-
| FR1 |
|
|
95
|
-
| FR2 |
|
|
96
|
-
| FR3 |
|
|
97
|
-
| FR4 |
|
|
98
|
-
| FR5 |
|
|
108
|
+
| ID | Invariant | Basis |
|
|
109
|
+
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
110
|
+
| FR1 | When supported and idle, the control is an enabled, icon-only ghost Button with the microphone icon, named by `label` or the translated start label, that calls `dictation.toggle` when activated. | Current source and tests |
|
|
111
|
+
| FR2 | While listening, the Button is named by `label` or the translated stop label and shows no icon, and five bars, hidden from assistive technology, scale with the supplied `bands`. | Current source and tests |
|
|
112
|
+
| FR3 | When dictation is unsupported, the control renders nothing; with `isHiddenWhenUnsupported={false}` it renders a disabled Button. | Current source and tests |
|
|
113
|
+
| FR4 | The bars use `--color-accent`, blending toward `--color-error` as `volume` passes the clipping threshold, so a theme retints both ends (`architecture:theme-tokens` INV3). | Current source and tests |
|
|
114
|
+
| FR5 | `ref`, DOM, data, and ARIA props, `className`, `style`, and `xstyle` reach the wrapper span, which carries the `chat-dictation-button` target; `size` sizes the Button and the bars together. | Current source and tests |
|
|
115
|
+
| FR6 | Under reduced motion the bars change scale and color at once, with no transition. | `design:reduced-motion`; current source |
|
|
99
116
|
|
|
100
117
|
### Allowed variation
|
|
101
118
|
|
|
102
|
-
- Callers may replace the translated start
|
|
103
|
-
-
|
|
104
|
-
|
|
119
|
+
- **AV1 — Label.** Callers may replace the translated start and stop name.
|
|
120
|
+
- **AV2 — Bars.** Bar heights and clipping strength follow the supplied
|
|
121
|
+
`bands` and `volume`.
|
|
122
|
+
- **AV3 — Theme colors.** Themes may change the accent and error colors.
|
|
123
|
+
|
|
124
|
+
### Representative states
|
|
125
|
+
|
|
126
|
+
| State | Required invariant | Allowed variation |
|
|
127
|
+
| ------------------------- | ---------------------------------------------- | ----------------- |
|
|
128
|
+
| Supported, idle | Enabled microphone Button with the start name. | Label, size. |
|
|
129
|
+
| Listening | Stop name; five hidden bars. | Bar heights. |
|
|
130
|
+
| Clipping | Bars blend from accent toward error. | Theme colors. |
|
|
131
|
+
| Unsupported, default | Nothing renders. | — |
|
|
132
|
+
| Unsupported, kept visible | A disabled Button. | Label. |
|
|
133
|
+
|
|
134
|
+
### Transformation and precedence order
|
|
135
|
+
|
|
136
|
+
- **ORD1 — Name.** The caller's `label`, then the translated start or stop
|
|
137
|
+
label for the listening state.
|
|
138
|
+
- **ORD2 — Support.** Unsupported and hidden renders nothing before any other
|
|
139
|
+
state applies.
|
|
140
|
+
|
|
141
|
+
### Performance and resources
|
|
142
|
+
|
|
143
|
+
- No resource rule.
|
|
105
144
|
|
|
106
145
|
## Accessibility contract
|
|
107
146
|
|
|
108
|
-
- The
|
|
109
|
-
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
-
|
|
147
|
+
- **AR1 — Name.** The control always has a name that says whether it starts or
|
|
148
|
+
stops dictation, unless the caller overrides it (FR1, FR2).
|
|
149
|
+
- **AR2 — Decorative bars.** The bars are hidden from assistive technology
|
|
150
|
+
(FR2).
|
|
151
|
+
- **AR3 — Unavailable action.** A visible unsupported control is disabled
|
|
152
|
+
(FR3); Button owns how disabled is conveyed.
|
|
113
153
|
|
|
114
154
|
## Design relationships
|
|
115
155
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
## Responsive and bidirectional behavior
|
|
156
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
157
|
+
| ---------------- | -------------------------------------------------- | ------------------------ | -------------- | ------------------ |
|
|
158
|
+
| Button | Icon-only microphone action. | `component:Button` | Prominent | FR1, FR3 |
|
|
159
|
+
| Volume bars | Five bars that show live input level and clipping. | Current source | Supporting | FR2, FR4, FR6 |
|
|
122
160
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
verified-N/A ledger records this with source evidence.
|
|
161
|
+
No anatomy-theming map is recorded yet; the block is optional during
|
|
162
|
+
migration, so the map is deferred.
|
|
126
163
|
|
|
127
164
|
## Family and system relationships
|
|
128
165
|
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
- `
|
|
133
|
-
-
|
|
166
|
+
- `component:Button` owns activation, focus, disabled semantics, and icon-only
|
|
167
|
+
presentation.
|
|
168
|
+
- `architecture:theme-tokens` owns the accent and error roles the bars use.
|
|
169
|
+
- `design:reduced-motion` owns the reduced-motion rule FR6 follows.
|
|
170
|
+
- The glyph and centered bars are direction-neutral; the RTL audit records
|
|
171
|
+
this as not applicable.
|
|
134
172
|
|
|
135
173
|
## Verification map
|
|
136
174
|
|
|
137
|
-
| Contract
|
|
138
|
-
|
|
|
139
|
-
| FR1,
|
|
140
|
-
| FR2
|
|
141
|
-
| FR3
|
|
142
|
-
| FR4
|
|
143
|
-
| FR5
|
|
144
|
-
|
|
|
145
|
-
|
|
175
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
176
|
+
| -------- | ----------------------------------------------------------------------------------------------------- | ----------------------------- | ----------------------------------------------------------------------- | ----------------------------------------- |
|
|
177
|
+
| FR1, AR1 | `ChatDictationButton.test.tsx` "labels and toggles the idle button" | Idle | A missing name, or activation that does not toggle, fails. | `audit:ChatDictationButton/behavior` |
|
|
178
|
+
| FR2, AR2 | `ChatDictationButton.test.tsx` "shows five hidden visualizer bars and the stop label while listening" | Listening | A wrong bar count, exposed bars, or a start name while listening fails. | `audit:ChatDictationButton/behavior` |
|
|
179
|
+
| FR3, AR3 | `ChatDictationButton.test.tsx` unsupported tests | Unsupported, hidden and shown | A rendered or enabled unsupported control fails. | `audit:ChatDictationButton/accessibility` |
|
|
180
|
+
| FR4 | `ChatDictationButton.test.tsx` "uses theme-owned colors for clipping feedback" | Clipping | A raw color in place of the theme roles fails. | `audit:ChatDictationButton/theming` |
|
|
181
|
+
| FR5 | `ChatDictationButton.test.tsx` "forwards the root ref and accepted span props" | Ref and span props | A dropped ref or prop fails. | `audit:ChatDictationButton/api` |
|
|
182
|
+
| RTL N/A | `apps/storybook/rtl-audit/verified-not-applicable.json` | — | — | `audit:ChatDictationButton/rtl` |
|
|
183
|
+
|
|
184
|
+
Read from source, with no focused test yet:
|
|
185
|
+
|
|
186
|
+
- FR1: the ghost variant and the microphone icon.
|
|
187
|
+
- FR2: no icon while listening, the bars scaling with `bands`, and the
|
|
188
|
+
caller's `label` while listening.
|
|
189
|
+
- FR4: the plain accent below the clipping threshold.
|
|
190
|
+
- FR5: ARIA props reaching the wrapper, `className`, `style`, and `xstyle` composing on the wrapper, the
|
|
191
|
+
`chat-dictation-button` target, and `sm` sizing.
|
|
192
|
+
- FR6: the reduced-motion transition removal.
|
|
146
193
|
|
|
147
194
|
## Decision log
|
|
148
195
|
|
|
149
|
-
|
|
150
|
-
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------ |
|
|
151
|
-
| Keep unsupported dictation hidden by default and disabled when explicitly visible. | An unavailable action must not present as operable. | objective accessibility/operability repair |
|
|
152
|
-
| Blend portable accent and error tokens for clipping. | Preserves the existing progressive clipping cue while keeping both endpoints theme-owned. | `architecture:theme-tokens/INV3` |
|
|
196
|
+
No component-local decision is recorded.
|
|
153
197
|
|
|
154
198
|
## Open questions
|
|
155
199
|
|
|
156
|
-
None
|
|
200
|
+
None.
|
|
157
201
|
|
|
158
202
|
## Content boundary
|
|
159
203
|
|
|
160
|
-
|
|
204
|
+
This file does not duplicate consumer prop tables, speech-recognition
|
|
205
|
+
behavior, or Button's rules. It links to their owners.
|
package/src/Link/Link.spec.md
CHANGED
|
@@ -16,6 +16,7 @@ verified_by:
|
|
|
16
16
|
packages/core/src/Link/__tests__/Link.a11y.test.tsx,
|
|
17
17
|
packages/core/src/Link/__tests__/Link.a11y.chromium.spec.ts,
|
|
18
18
|
packages/core/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts,
|
|
19
|
+
packages/core/src/Link/__tests__/Link.truncation.a11y.chromium.spec.ts,
|
|
19
20
|
]
|
|
20
21
|
modules: []
|
|
21
22
|
families: [family:navigation-destinations]
|
|
@@ -107,7 +108,7 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
107
108
|
| FR3 | An external Link that is not rendered as a button shows a trailing extra-small external-link icon that inherits the root's color, followed by visually hidden new-tab text: `newTabLabel`, else the localized default. | Current source and tests |
|
|
108
109
|
| FR4 | A destination reaches the link component only through the shared navigation rule; a rejected destination renders an inert anchor and never invokes a router. | `family:navigation-destinations` FR1–FR6; `spec:AST-005` |
|
|
109
110
|
| FR5 | A disabled Link stays focusable with `aria-disabled="true"`, never navigates, and never runs `onClick`. The disabled anchor form omits `href`, `target`, and `rel`, takes a Tab stop, and skips the custom link component. In every form, clicks are cancelled and stopped, Enter and Space are suppressed, other keys reach the caller's `onKeyDown`, and the Link paints at half opacity with no pointer events and no press paint. A focused Link that becomes disabled MUST keep focus and expose the change on that element (`spec:AST-085` FR4, FR5), and a caller `aria-disabled`, `role`, or `tabIndex` MUST NOT replace the disabled exposure (`architecture:public-component-api` INV5). Implementation gap: caller props spread after Link's own, so a caller `aria-disabled`, `role`, or `tabIndex` overrides them. Implementation gap: an enabled link renders through the safe link component and a disabled one as a plain anchor (or a `<button>` when it has `onClick`), so a focused link that becomes disabled (or re-enabled) is replaced and focus falls to the page. | `spec:AST-085` FR1, FR2, FR4, FR5; `family:navigation-destinations` FR7; current tests |
|
|
110
|
-
| FR6 | Children render inside Text with `type` (default `body`), `size`, `weight`, `color` (default `accent`), `display` (default `inline`), and `maxLines` (default `0`). The root inherits font family, size, weight, and line height and lays content out with a small gap; `isStandalone` sets the body size and leading on the root. A Link inside a Text with `maxLines` MUST truncate with that text. Implementation gap: the root is `inline-flex`, so an ancestor Text clamp does not truncate it.
|
|
111
|
+
| FR6 | Children render inside Text with `type` (default `body`), `size`, `weight`, `color` (default `accent`), `display` (default `inline`), and `maxLines` (default `0`). The root inherits font family, size, weight, and line height and lays content out with a small gap; with `maxLines` above 0 it stops at its container, so its Text truncates inside a constrained line; `isStandalone` sets the body size and leading on the root. A Link inside a Text with `maxLines` MUST truncate with that text. Implementation gap: the root is `inline-flex`, so an ancestor Text clamp does not truncate it. | Current source and tests |
|
|
111
112
|
| FR7 | The root paints the chosen color for the six built-in colors, which the underline and the external icon take; the label's color comes from Text (FR6). On hover-capable pointers an enabled Link underlines on hover, and for `primary`, `secondary`, and `accent` the root color mixes in the hover tint. `hasUnderline` underlines at rest. | Current source and tests |
|
|
112
113
|
| FR8 | Press feedback follows `design:user-states` DR1 and DR2 for Link's text surface: an enabled link or button form paints the pressed overlay, and a disabled Link does not. | `design:user-states` DR1, DR2; current tests |
|
|
113
114
|
| FR9 | `label`, when non-empty, becomes `aria-label` and replaces the content as the accessible name; a caller `aria-label` overrides it; otherwise the content is the name. | Current tests |
|
|
@@ -191,7 +192,7 @@ carries no anatomy-theming map.
|
|
|
191
192
|
| FR2, FR3, AR3 | `Link.test.tsx` external, target, rel, and new-tab cases; `Link.a11y.chromium.spec.ts` external state | External; explicit `_blank`; custom label | A missing safety token, icon, or new-tab text fails. Evidence gap: no test checks the accessible name of an external link with `label`. Evidence gap: no test sets `referrerPolicy` or asserts the external icon's size or color. | `audit:Link/accessibility` |
|
|
192
193
|
| FR4 | `Link.navigation.a11y.chromium.spec.ts` | Rejected and ordinary schemes; native, router, downloads | A rejected destination that navigates or reaches a router fails. | `audit:Link/behavior` |
|
|
193
194
|
| FR5, AR2 | `Link.test.tsx` disabled cases | Disabled anchor; disabled button form; with `onClick` | Navigation, a consumer `onClick`, a lost Tab stop, or a custom component on a disabled link fails. Evidence gap: no test disables a focused link or passes a caller `aria-disabled`, `role`, or `tabIndex`. Evidence gap: no test asserts that other keys reach the caller's `onKeyDown`, or the half-opacity, no-pointer-events paint. | `audit:Link/accessibility` |
|
|
194
|
-
| FR6 (type) | `Link.test.tsx` text-type cases | Default `body`; `type="inherit"` | A dropped Text `type` fails. Evidence gap: no test asserts that `size`, `weight`, `color`,
|
|
195
|
+
| FR6 (type) | `Link.test.tsx` text-type cases | Default `body`; `type="inherit"` | A dropped Text `type` fails. `Link.truncation.a11y.chromium.spec.ts`: an anchor and a button Link with `maxLines={1}` in a fixed-width line stop at the line and draw an ellipsis. Evidence gap: no test asserts that `size`, `weight`, `color`, or `display` reach Text, or the `isStandalone` root size. | `audit:Link/visual` |
|
|
195
196
|
| FR6, FR7 (paint) | Evidence gap: the color, underline, and standalone cases assert only that a link renders; hover paint is untested | Each color; `hasUnderline`; `isStandalone` | — | `audit:Link/visual` |
|
|
196
197
|
| FR8 | `Link.test.tsx` pressed-state suite | Link, button form, disabled | No press paint, or press paint on a disabled Link, fails. | `audit:Link/behavior` |
|
|
197
198
|
| FR9, FR11, AR4 | `Link.test.tsx` label, ref, and theme-class cases | With and without `label`; `data-color` | A missing or extra `aria-label`, target, `data-color`, or ref fails. The accessibility suites assert a passed `data-*` attribute and `aria-current` on the root, and the navigation suite asserts `download`. Evidence gap: no test passes both `label` and `aria-label`, or passes `className`, `style`, or `xstyle`. | `audit:Link/theming` |
|
package/src/Link/Link.tsx
CHANGED
|
@@ -76,6 +76,14 @@ const styles = stylex.create({
|
|
|
76
76
|
transitionDuration: durationVars['--duration-fast'],
|
|
77
77
|
transitionTimingFunction: easeVars['--ease-standard'],
|
|
78
78
|
},
|
|
79
|
+
// A truncating label needs a box that stops at its container: an
|
|
80
|
+
// inline-flex root otherwise grows to its whole text, so its Text never
|
|
81
|
+
// overflows and draws no ellipsis (`component:Link` FR6, `component:Text`
|
|
82
|
+
// FR4).
|
|
83
|
+
truncating: {
|
|
84
|
+
maxWidth: '100%',
|
|
85
|
+
minWidth: 0,
|
|
86
|
+
},
|
|
79
87
|
/**
|
|
80
88
|
* Reset styles for rendering as a <button> when href is undefined.
|
|
81
89
|
* Strips all native button chrome so it looks identical to a link.
|
|
@@ -370,6 +378,7 @@ export function Link({
|
|
|
370
378
|
hasUnderline && styles.hasUnderline,
|
|
371
379
|
isStandalone && styles.standalone,
|
|
372
380
|
isDisabled && styles.disabled,
|
|
381
|
+
maxLines > 0 && styles.truncating,
|
|
373
382
|
xstyle,
|
|
374
383
|
),
|
|
375
384
|
className,
|
|
@@ -403,6 +412,7 @@ export function Link({
|
|
|
403
412
|
hasUnderline && styles.hasUnderline,
|
|
404
413
|
isStandalone && styles.standalone,
|
|
405
414
|
styles.disabled,
|
|
415
|
+
maxLines > 0 && styles.truncating,
|
|
406
416
|
xstyle,
|
|
407
417
|
),
|
|
408
418
|
className,
|
|
@@ -434,6 +444,7 @@ export function Link({
|
|
|
434
444
|
hasUnderline && styles.hasUnderline,
|
|
435
445
|
isStandalone && styles.standalone,
|
|
436
446
|
isDisabled && styles.disabled,
|
|
447
|
+
maxLines > 0 && styles.truncating,
|
|
437
448
|
xstyle,
|
|
438
449
|
),
|
|
439
450
|
className,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// @pr-a11y scope ^(packages/core/src/Link/|packages/core/src/Text/|apps/storybook/stories/Link\.stories)
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Real-browser evidence for `component:Link` FR6 with `component:Text` FR4: a
|
|
6
|
+
* Link with `maxLines={1}` in a fixed-width line, as an anchor and as a
|
|
7
|
+
* button, stops at the line and its label overflows with an ellipsis.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {expect, test} from '@playwright/test';
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_STORYBOOK_DIR,
|
|
13
|
+
serveStorybook,
|
|
14
|
+
type StaticServer,
|
|
15
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
16
|
+
|
|
17
|
+
let storybook: StaticServer;
|
|
18
|
+
|
|
19
|
+
test.beforeAll(async () => {
|
|
20
|
+
storybook = await serveStorybook(
|
|
21
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test.afterAll(async () => storybook?.close());
|
|
26
|
+
|
|
27
|
+
for (const form of ['anchor', 'button'] as const) {
|
|
28
|
+
test(`a ${form} Link with maxLines={1} truncates inside a fixed-width line`, async ({
|
|
29
|
+
page,
|
|
30
|
+
}) => {
|
|
31
|
+
await page.goto(
|
|
32
|
+
`${storybook.origin}/iframe.html?id=core-link--truncated-in-constrained-line&viewMode=story`,
|
|
33
|
+
);
|
|
34
|
+
const line = page.locator(`[data-truncation-line="${form}"]`);
|
|
35
|
+
await expect(line).toBeVisible();
|
|
36
|
+
const measured = await line.evaluate(el => {
|
|
37
|
+
const root = el.firstElementChild as HTMLElement;
|
|
38
|
+
const label = root.firstElementChild as HTMLElement;
|
|
39
|
+
const style = getComputedStyle(label);
|
|
40
|
+
return {
|
|
41
|
+
tag: root.tagName,
|
|
42
|
+
line: el.getBoundingClientRect().width,
|
|
43
|
+
root: root.getBoundingClientRect().width,
|
|
44
|
+
labelClient: label.clientWidth,
|
|
45
|
+
labelScroll: label.scrollWidth,
|
|
46
|
+
textOverflow: style.textOverflow,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
expect(measured.tag, 'the Link renders its own element').toBe(
|
|
50
|
+
form === 'anchor' ? 'A' : 'BUTTON',
|
|
51
|
+
);
|
|
52
|
+
// The root stops at the line instead of growing to its whole text.
|
|
53
|
+
expect(measured.root).toBeLessThanOrEqual(measured.line + 0.5);
|
|
54
|
+
// The label itself overflows and draws the ellipsis.
|
|
55
|
+
expect(measured.labelScroll).toBeGreaterThan(measured.labelClient);
|
|
56
|
+
expect(measured.textOverflow).toBe('ellipsis');
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
type StaticServer,
|
|
22
22
|
} from '@astryxdesign/a11y-spec/storybook';
|
|
23
23
|
|
|
24
|
-
const STORY = 'core-markdown
|
|
24
|
+
const STORY = 'core-markdown--heading-links';
|
|
25
25
|
const OUTPUT = path.resolve('test-results/markdown-heading-links');
|
|
26
26
|
const GEOMETRY_TOLERANCE_PX = 1;
|
|
27
27
|
let storybook: StaticServer;
|
|
@@ -120,7 +120,7 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
120
120
|
| FR3 | Selecting an enabled option calls `onChange(value)` once; RadioList holds no selection of its own. | Current tests |
|
|
121
121
|
| FR4 | With an empty `value`, keyboard focus entering the group lands on the first radio that is not natively disabled (in a group disabled with a reason, that is its first `aria-disabled` radio, keeping the enabled focus order under `spec:AST-085` FR1), or stays on the last one when entering backward. Focus moving between radios inside the group is not redirected. With a selection, the selected radio is the Tab stop. Arrow keys follow the browser's native radio behavior, which stops on `aria-disabled` radios. | WAI-ARIA radio group pattern; current tests |
|
|
122
122
|
| FR5 | `isDisabled` MUST disable every item: each radio stays focusable with `aria-disabled="true"`, is detached from the form so it does not submit, and refuses selection (`spec:AST-085` FR1–FR3, FR7). With `disabledMessage`, a reason tooltip also opens on group hover and keyboard focus, and the reason MUST describe each focusable radio (`spec:AST-085` FR5, FR7). Implementation gap: the reason describes only the radiogroup, so a focused radio is not described by it; and without a `disabledMessage`, each radio gets native `disabled` instead, which removes it from the tab order, and a radio focused when the group becomes disabled loses focus (`spec:AST-085` FR4). | `spec:AST-085` FR1–FR5, FR7; current tests |
|
|
123
|
-
| FR6 | `status.type === 'error'` sets `aria-invalid` on the radiogroup. The radiogroup exposes the effective required state (including a form-level default) with `aria-required`, while each radio's native `required` follows only the explicit `isRequired
|
|
123
|
+
| FR6 | `status.type === 'error'` sets `aria-invalid` on the radiogroup. The radiogroup exposes the effective required state (including a form-level default) with `aria-required`, while each radio's native `required` follows only the explicit `isRequired`; a natively required radio's `aria-invalid` follows the same status, so the browser's own invalid state never shows before an error. | Current source and tests |
|
|
124
124
|
| FR7 | The radiogroup lays options in a column with a small gap, or a row with a wider gap when horizontal, and passes `size` to every item. | Current source and tests |
|
|
125
125
|
| FR8 | Base props (`ref`, `id`, `aria-*`, `data-*`, `hidden`, `inert`, `dir`, DOM handlers) reach the radiogroup; a caller `onFocus` runs after FR4's entry handling. `container*` reaches the Field box where one renders (in a `horizontal-labels` FormLayout none renders, and `container*` has no effect and warns), and `control*` the radiogroup; root `xstyle`, `className`, and `style` are not accepted. | `family:input-fields` FR10; current tests |
|
|
126
126
|
| FR9 | The radiogroup carries `astryx-radio-list` and reflects `data-orientation` and `data-size`. | `architecture:component-theming-surface`; current tests |
|
|
@@ -165,6 +165,34 @@ describe('RadioList', () => {
|
|
|
165
165
|
expect(screen.getByText(/Required/)).toBeInTheDocument();
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
+
it('keeps required radios valid until the group shows an error', () => {
|
|
169
|
+
const {rerender} = render(
|
|
170
|
+
<RadioList label="Preference" value="" onChange={() => {}} isRequired>
|
|
171
|
+
<RadioListItem label="Option A" value="a" />
|
|
172
|
+
<RadioListItem label="Option B" value="b" />
|
|
173
|
+
</RadioList>,
|
|
174
|
+
);
|
|
175
|
+
// Native required alone makes Chromium report every radio invalid.
|
|
176
|
+
for (const radio of screen.getAllByRole('radio')) {
|
|
177
|
+
expect(radio).toBeRequired();
|
|
178
|
+
expect(radio).toHaveAttribute('aria-invalid', 'false');
|
|
179
|
+
}
|
|
180
|
+
rerender(
|
|
181
|
+
<RadioList
|
|
182
|
+
label="Preference"
|
|
183
|
+
value=""
|
|
184
|
+
onChange={() => {}}
|
|
185
|
+
isRequired
|
|
186
|
+
status={{type: 'error', message: 'Pick one'}}>
|
|
187
|
+
<RadioListItem label="Option A" value="a" />
|
|
188
|
+
<RadioListItem label="Option B" value="b" />
|
|
189
|
+
</RadioList>,
|
|
190
|
+
);
|
|
191
|
+
for (const radio of screen.getAllByRole('radio')) {
|
|
192
|
+
expect(radio).toHaveAttribute('aria-invalid', 'true');
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
168
196
|
it('shows Optional indicator when isOptional is true', () => {
|
|
169
197
|
render(
|
|
170
198
|
<RadioList label="Preference" value="" onChange={() => {}} isOptional>
|
|
@@ -326,6 +326,12 @@ export function RadioListItem({
|
|
|
326
326
|
// from submission, matching a natively disabled control.
|
|
327
327
|
form={keepsFocusableForMessage ? '' : undefined}
|
|
328
328
|
required={context.isRequired}
|
|
329
|
+
// Chromium reports an unanswered native-required radio invalid before
|
|
330
|
+
// any interaction; the invalid state follows the group's status
|
|
331
|
+
// instead (RadioList FR6).
|
|
332
|
+
aria-invalid={
|
|
333
|
+
context.isRequired ? context.status?.type === 'error' : undefined
|
|
334
|
+
}
|
|
329
335
|
onChange={() => {
|
|
330
336
|
if (isDisabled) {
|
|
331
337
|
return;
|
|
@@ -123,7 +123,7 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
123
123
|
| FR5 | A change calls `onChange(checked, event)`; unless that call prevents default, `changeAction(checked, event)` runs in a transition and the switch shows the proposed state while the Action is pending, and `value` once it settles. | Current tests |
|
|
124
124
|
| FR6 | While `isLoading`, or while the shown state differs from `value`, the input sets `aria-busy`, ignores pointer input, refuses changes, keeps focus, and the switch announces its busy start once per busy period through the shared channel. | `spec:AST-086` FR8; current tests |
|
|
125
125
|
| FR7 | Disabled: `aria-disabled="true"` on a focusable input, no `name`, detached from its form (so it neither submits nor blocks submission when required), no change, no caller `onClick` on the field, no hover or press paint, and a half-opacity track. With `disabledMessage`, a reason tooltip opens on row hover and keyboard focus and describes the input. Implementation gap: caller DOM handlers other than `onClick`, such as `onKeyDown`, still run on the field root while disabled (`spec:AST-085` FR2). | `spec:AST-085` FR1–FR3, FR5; current tests |
|
|
126
|
-
| FR8 | Enabled, the input submits `on` under `htmlName` when on and nothing when off; `isRequired` sets native `required`, while `aria-required` follows the effective required state.
|
|
126
|
+
| FR8 | Enabled, the input submits `on` under `htmlName` when on and nothing when off; `isRequired` sets native `required`, while `aria-required` follows the effective required state; `aria-invalid` follows `status` (`false` on a required switch without an error), so the browser's own invalid state never shows before an error. | Current tests |
|
|
127
127
|
| FR9 | Keyboard focus paints the shared focus outline on the track. Hovering the enabled row tints the track; pressing paints the pressed overlay on both, applying `design:user-states` DR1 and DR2; a touch press fades out from the row. Track color transitions are instant under reduced motion. | `architecture:interaction-modality` INV1; `design:user-states` DR1, DR2 for the pressed overlay; `design:reduced-motion`; current tests |
|
|
128
128
|
| FR10 | The thumb travels toward the inline end when on, mirrored under RTL, and moves without a transition under reduced motion (`design:reduced-motion` DR1). On coarse pointers the input's hit area is at least 24 by 24 CSS pixels (WCAG 2.5.8) and follows the inline direction. Under `spec:AST-094` FR42 a coarse-pointer target MUST reach 44 CSS pixels through an invisible hit area where neighbors allow. Implementation gap: the input's hit area stops at 24 by 24. | WCAG 2.5.8; current tests; `design:reduced-motion` DR1; `spec:AST-094` FR42 |
|
|
129
129
|
| FR11 | Under forced colors, on and off stay distinguishable (Highlight track when on), and the hover tint does not hide the thumb. | WCAG 1.4.11; current tests |
|
|
@@ -429,7 +429,9 @@ describe('Switch', () => {
|
|
|
429
429
|
continue;
|
|
430
430
|
}
|
|
431
431
|
for (const node of record.addedNodes) {
|
|
432
|
-
if (node.textContent) {
|
|
432
|
+
if (node.textContent) {
|
|
433
|
+
written.push(node.textContent);
|
|
434
|
+
}
|
|
433
435
|
}
|
|
434
436
|
}
|
|
435
437
|
});
|
|
@@ -563,6 +565,24 @@ describe('Switch', () => {
|
|
|
563
565
|
expect(screen.getByRole('switch')).toBeRequired();
|
|
564
566
|
});
|
|
565
567
|
|
|
568
|
+
it('keeps a required switch valid until status shows an error', () => {
|
|
569
|
+
const {rerender} = render(
|
|
570
|
+
<Switch label="Accept" value={false} onChange={() => {}} isRequired />,
|
|
571
|
+
);
|
|
572
|
+
// Native required alone makes Chromium report the switch invalid.
|
|
573
|
+
expect(screen.getByRole('switch')).toHaveAttribute('aria-invalid', 'false');
|
|
574
|
+
rerender(
|
|
575
|
+
<Switch
|
|
576
|
+
label="Accept"
|
|
577
|
+
value={false}
|
|
578
|
+
onChange={() => {}}
|
|
579
|
+
isRequired
|
|
580
|
+
status={{type: 'error', message: 'Required'}}
|
|
581
|
+
/>,
|
|
582
|
+
);
|
|
583
|
+
expect(screen.getByRole('switch')).toHaveAttribute('aria-invalid', 'true');
|
|
584
|
+
});
|
|
585
|
+
|
|
566
586
|
describe('disabledMessage', () => {
|
|
567
587
|
const h = {hidden: true} as const;
|
|
568
588
|
|
package/src/Switch/Switch.tsx
CHANGED
|
@@ -679,7 +679,11 @@ export function Switch({
|
|
|
679
679
|
// than the field wrapper via `...rest`.
|
|
680
680
|
aria-labelledby={ariaLabelledByProp}
|
|
681
681
|
aria-describedby={ariaDescribedBy}
|
|
682
|
-
|
|
682
|
+
// Chromium reports an unchecked native-required checkbox invalid before
|
|
683
|
+
// any interaction; the invalid state follows status instead.
|
|
684
|
+
aria-invalid={
|
|
685
|
+
status?.type === 'error' ? true : isRequired ? false : undefined
|
|
686
|
+
}
|
|
683
687
|
aria-busy={isBusy || undefined}
|
|
684
688
|
{...stylex.props(
|
|
685
689
|
styles.input,
|
|
@@ -471,7 +471,6 @@ export class GestureArena {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
/** Cancel an active recognizer, or fail a possible one. */
|
|
475
474
|
/**
|
|
476
475
|
* Runs `body`, code outside input that stops recognizers (an Effect that
|
|
477
476
|
* turns one off or detaches it), without resolving what those stops
|
|
@@ -504,6 +503,7 @@ export class GestureArena {
|
|
|
504
503
|
: this.clock.now();
|
|
505
504
|
}
|
|
506
505
|
|
|
506
|
+
/** Cancel an active recognizer, or fail a possible one. */
|
|
507
507
|
stop(recognizer: GestureRecognizer): void {
|
|
508
508
|
// A cancel from code between events happens at the last event's time:
|
|
509
509
|
// wall time would run deadlines ahead of input still queued (FR16).
|
|
@@ -217,10 +217,6 @@ function claimsWhileMoving(recognizer: GestureRecognizer): boolean {
|
|
|
217
217
|
return recognizer.acceptsTouchInput && recognizer.kind !== 'tap';
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
/**
|
|
221
|
-
* Run `task` in the next frame, before it paints: a read there styles what
|
|
222
|
-
* the frame must style anyway, and its writes are what the frame shows.
|
|
223
|
-
*/
|
|
224
220
|
/**
|
|
225
221
|
* A CSS-wide keyword, which names no touch-action of its own: the value in
|
|
226
222
|
* force resolves it (`initial` and `unset` to `auto`, `inherit` to the
|
|
@@ -238,6 +234,10 @@ const CSS_WIDE_KEYWORDS: ReadonlySet<string> = new Set([
|
|
|
238
234
|
'revert-layer',
|
|
239
235
|
]);
|
|
240
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Run `task` in the next frame, before it paints: a read there styles what
|
|
239
|
+
* the frame must style anyway, and its writes are what the frame shows.
|
|
240
|
+
*/
|
|
241
241
|
function inNextFrame(view: Window | null, task: () => void): void {
|
|
242
242
|
if (view != null && typeof view.requestAnimationFrame === 'function') {
|
|
243
243
|
view.requestAnimationFrame(task);
|
|
@@ -328,7 +328,6 @@ export class GestureHost {
|
|
|
328
328
|
private readonly touchIds = new Map<number, number>();
|
|
329
329
|
/** Touch.identifier → the element the finger landed on. */
|
|
330
330
|
private readonly touchTargets = new Map<number, EventTarget>();
|
|
331
|
-
/** Finger targets with listeners of their own, and how many fingers each. */
|
|
332
331
|
/**
|
|
333
332
|
* Finger targets with listeners of their own: how many fingers each, and
|
|
334
333
|
* how many of those a recognizer could claim while they move (only those
|
|
@@ -374,20 +373,20 @@ export class GestureHost {
|
|
|
374
373
|
private readonly claimedLifts: PlacedContact[] = [];
|
|
375
374
|
/** Fingers still waiting for their pointerdown, by arena id. */
|
|
376
375
|
private readonly unpairedTouches = new Map<number, PlacedContact>();
|
|
377
|
-
/** The last position fed per contact, so one channel's echo is dropped. */
|
|
378
376
|
/** The elements each held contact turned selection off on. */
|
|
379
377
|
private readonly heldSelectionBy = new Map<number, HTMLElement[]>();
|
|
380
378
|
private readonly isPrimaryPointerCoarse: () => boolean;
|
|
381
379
|
private readonly coarseQuery: MediaQueryList | null;
|
|
382
380
|
private isWatchingPointer = false;
|
|
383
381
|
private readonly onPointerKindChange = (): void => this.refreshAll();
|
|
382
|
+
/** The last position fed per contact, so one channel's echo is dropped. */
|
|
384
383
|
private readonly lastFed = new Map<number, TouchSample>();
|
|
385
384
|
/** When a finger was last down or lifted, and where it landed. */
|
|
386
385
|
private lastTouch: {readonly target: Node; readonly time: number} | null =
|
|
387
386
|
null;
|
|
388
|
-
/** The last scroll a finger drove, and when. */
|
|
389
387
|
/** Scrollers a recognizer is scrolling itself this frame (ownScroll). */
|
|
390
388
|
private readonly ownedScrolls = new Set<Node>();
|
|
389
|
+
/** The last scroll a finger drove, and when. */
|
|
391
390
|
private lastScroll: {readonly scroller: Node; readonly time: number} | null =
|
|
392
391
|
null;
|
|
393
392
|
private nextId = 1;
|