@astryxdesign/core 0.7.8-canary.f10bc91 → 0.7.8-canary.f60d2bb
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/PowerSearch/PowerSearchBottomSheet.d.ts +4 -3
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchBottomSheet.js +4 -3
- package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +3 -2
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +74 -9
- package/dist/utils/pressGesture.d.ts +4 -2
- package/dist/utils/pressGesture.d.ts.map +1 -1
- package/dist/utils/pressGesture.js +3 -1
- package/package.json +4 -4
- package/src/PowerSearch/PowerSearchBottomSheet.tsx +7 -5
- package/src/utils/interactionOverlay.stylex.ts +3 -2
- package/src/utils/pressFeedback.ts +99 -21
- package/src/utils/pressGesture.ts +4 -2
|
@@ -76,8 +76,9 @@ export interface PowerSearchBottomSheetProps {
|
|
|
76
76
|
filters: ReadonlyArray<PowerSearchFilter>;
|
|
77
77
|
/**
|
|
78
78
|
* The field's `onChange`. Done replays the draft's edits through it, in
|
|
79
|
-
* order,
|
|
80
|
-
* reported before Done
|
|
79
|
+
* order, onto the field's latest filters, each call carrying that list
|
|
80
|
+
* with the edit applied; nothing is reported before Done, and nothing when
|
|
81
|
+
* the field is read-only or disabled at Done.
|
|
81
82
|
*/
|
|
82
83
|
onChange: (filters: ReadonlyArray<PowerSearchFilter>, changeType: PowerSearchChangeType, index: number) => void;
|
|
83
84
|
/** The field typeahead source; the content search lists its suggestions. */
|
|
@@ -96,7 +97,7 @@ export interface PowerSearchBottomSheetProps {
|
|
|
96
97
|
components?: PowerSearchComponents;
|
|
97
98
|
editors: PowerSearchSheetEditors;
|
|
98
99
|
}
|
|
99
|
-
/** One edit of the draft: the
|
|
100
|
+
/** One edit of the draft: the draft before and after it, and its type and index in the draft; Done replays it onto the field's latest filters. */
|
|
100
101
|
interface DraftEdit {
|
|
101
102
|
before: ReadonlyArray<PowerSearchFilter>;
|
|
102
103
|
after: ReadonlyArray<PowerSearchFilter>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PowerSearchBottomSheet.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/PowerSearchBottomSheet.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,EASZ,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAoBf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAkCrD,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0BAA0B,CAAC;AAe3D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAIV,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EAEjB,iBAAiB,EACjB,eAAe,EAEhB,MAAM,SAAS,CAAC;AAajB;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,wBAAwB,EAAE,OAAO,wBAAwB,CAAC;CAC3D;AAED,MAAM,WAAW,2BAA2B;IAC1C,wEAAwE;IACxE,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,yEAAyE;IACzE,UAAU,EAAE,iBAAiB,CAAC;IAC9B,yEAAyE;IACzE,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC1C
|
|
1
|
+
{"version":3,"file":"PowerSearchBottomSheet.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/PowerSearchBottomSheet.tsx"],"names":[],"mappings":"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"}
|
|
@@ -104,7 +104,7 @@ registerEnglishFallback(english);
|
|
|
104
104
|
// Styles
|
|
105
105
|
// =============================================================================
|
|
106
106
|
|
|
107
|
-
/** One edit of the draft: the
|
|
107
|
+
/** One edit of the draft: the draft before and after it, and its type and index in the draft; Done replays it onto the field's latest filters. */
|
|
108
108
|
|
|
109
109
|
// Whether two filter lists hold the same filters in the same order, by the
|
|
110
110
|
// key-order-insensitive signature the sheet matches filters with.
|
|
@@ -765,12 +765,13 @@ export function PowerSearchBottomSheet({
|
|
|
765
765
|
// consumer gets the calls a popover session makes and keeps a filter it
|
|
766
766
|
// added meanwhile.
|
|
767
767
|
const commitAndClose = useCallback(() => {
|
|
768
|
-
|
|
768
|
+
// A field that became read-only while the flow was open reports nothing.
|
|
769
|
+
if (isDraftDirty && isInteractive) {
|
|
769
770
|
replayDraftEdits(committedFilters, draftEditsRef.current, onCommit);
|
|
770
771
|
}
|
|
771
772
|
draftEditsRef.current = [];
|
|
772
773
|
closeFlow();
|
|
773
|
-
}, [closeFlow, committedFilters, isDraftDirty, onCommit]);
|
|
774
|
+
}, [closeFlow, committedFilters, isDraftDirty, isInteractive, onCommit]);
|
|
774
775
|
const requestDismiss = useCallback(() => {
|
|
775
776
|
if (isDraftDirty) {
|
|
776
777
|
setIsDiscardConfirmationOpen(true);
|
|
@@ -33,8 +33,9 @@ export declare const pressVars: stylex.VarGroup<Readonly<{
|
|
|
33
33
|
*
|
|
34
34
|
* `defineConsts`, so the one number is inlined here at build time and is the
|
|
35
35
|
* controller's constant at run time (`PRESS_FADE_MS` in pressGesture.ts):
|
|
36
|
-
* the release animation and the
|
|
37
|
-
*
|
|
36
|
+
* the release animation and the controller's clock cannot drift apart. When
|
|
37
|
+
* that clock runs out, the controller waits for a release animation that a
|
|
38
|
+
* busy main thread started late, then removes the attribute.
|
|
38
39
|
*/
|
|
39
40
|
export declare const pressConsts: {
|
|
40
41
|
/** How long the release takes, in ms. UIKit's deselect clock. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactionOverlay.stylex.d.ts","sourceRoot":"","sources":["../../src/utils/interactionOverlay.stylex.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AAmCH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAG3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS;;WAEpB,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"interactionOverlay.stylex.d.ts","sourceRoot":"","sources":["../../src/utils/interactionOverlay.stylex.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AAmCH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAG3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS;;WAEpB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW;IACtB,iEAAiE;;CAEjE,CAAC;AA8HH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8HnC,CAAC"}
|
|
@@ -78,8 +78,9 @@ export const pressVars = {
|
|
|
78
78
|
*
|
|
79
79
|
* `defineConsts`, so the one number is inlined here at build time and is the
|
|
80
80
|
* controller's constant at run time (`PRESS_FADE_MS` in pressGesture.ts):
|
|
81
|
-
* the release animation and the
|
|
82
|
-
*
|
|
81
|
+
* the release animation and the controller's clock cannot drift apart. When
|
|
82
|
+
* that clock runs out, the controller waits for a release animation that a
|
|
83
|
+
* busy main thread started late, then removes the attribute.
|
|
83
84
|
*/
|
|
84
85
|
export const pressConsts = {
|
|
85
86
|
releaseMs: 200
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressFeedback.d.ts","sourceRoot":"","sources":["../../src/utils/pressFeedback.ts"],"names":[],"mappings":"AAkDA;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD,qDAAqD;AACrD,eAAO,MAAM,cAAc,EAAE;IAAC,QAAQ,CAAC,CAAC,mBAAmB,CAAC,EAAE,EAAE,CAAA;CAE/D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"pressFeedback.d.ts","sourceRoot":"","sources":["../../src/utils/pressFeedback.ts"],"names":[],"mappings":"AAkDA;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD,qDAAqD;AACrD,eAAO,MAAM,cAAc,EAAE;IAAC,QAAQ,CAAC,CAAC,mBAAmB,CAAC,EAAE,EAAE,CAAA;CAE/D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AA0E5C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAUpD;AAkoBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,IAAI,CA0BjD"}
|
|
@@ -138,6 +138,66 @@ export function pressFadeMs(element) {
|
|
|
138
138
|
const reduced = typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
139
139
|
return reduced ? Math.min(scaled, PRESS_FADE_MS) : scaled;
|
|
140
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The release animations that still run for `element`'s press. Each is a
|
|
144
|
+
* finite CSS animation that has not finished. It runs on the element, on its
|
|
145
|
+
* `::before` or `::after`, or on a descendant whose nearest press owner is
|
|
146
|
+
* the element, because a component can draw its release on a part inside the
|
|
147
|
+
* surface. A press owner nested inside plays the release of its own press,
|
|
148
|
+
* so it never extends this one. The list is empty where the engine has no Web
|
|
149
|
+
* Animations.
|
|
150
|
+
*/
|
|
151
|
+
function releaseAnimationsOf(element) {
|
|
152
|
+
if (typeof element.getAnimations !== 'function') {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return element.getAnimations({
|
|
156
|
+
subtree: true
|
|
157
|
+
}).filter(animation => {
|
|
158
|
+
// The effect of a CSS animation is a KeyframeEffect. Its target is the
|
|
159
|
+
// element, also when the animation runs on a pseudo-element.
|
|
160
|
+
const effect = animation.effect;
|
|
161
|
+
const target = effect?.target ?? null;
|
|
162
|
+
if (effect == null || target == null || !('animationName' in animation) || target !== element && target.closest(`[${PRESSABLE_ATTRIBUTE}]`) !== element) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
if (!Number.isFinite(Number(effect.getComputedTiming().endTime))) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return animation.playState !== 'finished';
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Calls `end` when the release on `element` is over. A busy main thread
|
|
174
|
+
* delays the first frame of the release animation, and with it the end. So a
|
|
175
|
+
* clock that starts at the `fading` write can cut the release short. When
|
|
176
|
+
* `clockMs` runs out, the release waits for each release animation that still
|
|
177
|
+
* runs or still waits for its first frame. It ends when those finish or are
|
|
178
|
+
* cancelled, as when the surface is unmounted or restyled. With no release
|
|
179
|
+
* animation (an instant release, a surface without one, an engine without Web
|
|
180
|
+
* Animations), the release ends at the clock. Returns a cancel.
|
|
181
|
+
*/
|
|
182
|
+
function endAfterRelease(element, clockMs, end) {
|
|
183
|
+
let waiting = true;
|
|
184
|
+
const timer = setTimeout(() => {
|
|
185
|
+
const running = releaseAnimationsOf(element);
|
|
186
|
+
if (running.length === 0) {
|
|
187
|
+
end();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
void Promise.allSettled(running.map(async animation => animation.finished)).then(() => {
|
|
191
|
+
if (waiting) {
|
|
192
|
+
end();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}, clockMs);
|
|
196
|
+
return () => {
|
|
197
|
+
waiting = false;
|
|
198
|
+
clearTimeout(timer);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
141
201
|
const TIMER_EVENT = {
|
|
142
202
|
fade: 'fadeEnd',
|
|
143
203
|
flash: 'flashEnd',
|
|
@@ -152,7 +212,7 @@ function paintElement(element, paint) {
|
|
|
152
212
|
}
|
|
153
213
|
function clearTimer(slot) {
|
|
154
214
|
if (slot.live?.timer != null) {
|
|
155
|
-
|
|
215
|
+
slot.live.timer();
|
|
156
216
|
slot.live.timer = null;
|
|
157
217
|
}
|
|
158
218
|
}
|
|
@@ -188,20 +248,25 @@ function apply(slot, step) {
|
|
|
188
248
|
}
|
|
189
249
|
if (step.timer != null) {
|
|
190
250
|
const kind = step.timer;
|
|
191
|
-
|
|
192
|
-
// while a scroll runs, so a frame-based delay would fire late, after the
|
|
193
|
-
// scroll, and paint a control nobody is pressing any more.
|
|
194
|
-
live.timer = setTimeout(() => {
|
|
251
|
+
const fire = () => {
|
|
195
252
|
if (slot.live != null) {
|
|
196
253
|
slot.live.timer = null;
|
|
197
254
|
}
|
|
198
255
|
apply(slot, pressGestureStep(slot.live?.gesture ?? idlePressGesture(), {
|
|
199
256
|
kind: TIMER_EVENT[kind]
|
|
200
257
|
}));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
258
|
+
};
|
|
259
|
+
if (kind === 'fade') {
|
|
260
|
+
// The release fade follows the theme's tempo (AST-108). It ends when
|
|
261
|
+
// its animation ends, however late a busy main thread let it start.
|
|
262
|
+
live.timer = endAfterRelease(live.element, pressFadeMs(live.element), fire);
|
|
263
|
+
} else {
|
|
264
|
+
// A TIMER, never `requestAnimationFrame`: iOS throttles frame callbacks
|
|
265
|
+
// while a scroll runs, so a frame-based delay would fire late, after the
|
|
266
|
+
// scroll, and paint a control nobody is pressing any more.
|
|
267
|
+
const timer = setTimeout(fire, TIMER_MS[kind]);
|
|
268
|
+
live.timer = () => clearTimeout(timer);
|
|
269
|
+
}
|
|
205
270
|
}
|
|
206
271
|
}
|
|
207
272
|
|
|
@@ -31,7 +31,9 @@ export declare const PRESS_FLASH_MS = 100;
|
|
|
31
31
|
* How long the paint takes to leave after a release: UIKit's deselect
|
|
32
32
|
* crossfade. The one number, shared with the stylesheet: the release
|
|
33
33
|
* animation in interactionOverlay.stylex.ts runs on the same constant, so the
|
|
34
|
-
*
|
|
34
|
+
* controller's release clock and the fade cannot drift apart. When the clock
|
|
35
|
+
* runs out, the controller waits for a release animation that still runs,
|
|
36
|
+
* then removes the attribute.
|
|
35
37
|
*/
|
|
36
38
|
export declare const PRESS_FADE_MS: number;
|
|
37
39
|
/**
|
|
@@ -98,7 +100,7 @@ export type PressEvent = {
|
|
|
98
100
|
| {
|
|
99
101
|
kind: 'flashEnd';
|
|
100
102
|
}
|
|
101
|
-
/**
|
|
103
|
+
/** The release is over: its animation ended, or its clock ran out with none running. */
|
|
102
104
|
| {
|
|
103
105
|
kind: 'fadeEnd';
|
|
104
106
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressGesture.d.ts","sourceRoot":"","sources":["../../src/utils/pressGesture.ts"],"names":[],"mappings":"AA4BA;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC
|
|
1
|
+
{"version":3,"file":"pressGesture.d.ts","sourceRoot":"","sources":["../../src/utils/pressGesture.ts"],"names":[],"mappings":"AA4BA;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,MAA8B,CAAC;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,2EAA2E;AAC3E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAC;AAElD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GACpB,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,UAAU,GAClB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAC;AACtC;;;;GAIG;GACD;IAAC,IAAI,EAAE,YAAY,CAAA;CAAC;AACtB,0EAA0E;GACxE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAC,GACpC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAC;AACd,4FAA4F;GAC1F;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAC;AAClB,iEAAiE;GAC/D;IAAC,IAAI,EAAE,OAAO,CAAA;CAAC;AACjB,yDAAyD;GACvD;IAAC,IAAI,EAAE,UAAU,CAAA;CAAC;AACpB,wFAAwF;GACtF;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,CAAC;AAEtB,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;CACnD;AAkBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,UAAU,GAChB,SAAS,CA6GX;AAED,wBAAgB,gBAAgB,IAAI,YAAY,CAE/C"}
|
|
@@ -62,7 +62,9 @@ export const PRESS_FLASH_MS = 100;
|
|
|
62
62
|
* How long the paint takes to leave after a release: UIKit's deselect
|
|
63
63
|
* crossfade. The one number, shared with the stylesheet: the release
|
|
64
64
|
* animation in interactionOverlay.stylex.ts runs on the same constant, so the
|
|
65
|
-
*
|
|
65
|
+
* controller's release clock and the fade cannot drift apart. When the clock
|
|
66
|
+
* runs out, the controller waits for a release animation that still runs,
|
|
67
|
+
* then removes the attribute.
|
|
66
68
|
*/
|
|
67
69
|
export const PRESS_FADE_MS = pressConsts.releaseMs;
|
|
68
70
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.8-canary.
|
|
3
|
+
"version": "0.7.8-canary.f60d2bb",
|
|
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",
|
|
@@ -870,9 +870,9 @@
|
|
|
870
870
|
"react-dom": ">=19.0.0"
|
|
871
871
|
},
|
|
872
872
|
"devDependencies": {
|
|
873
|
-
"@astryxdesign/a11y-spec": "0.7.8-canary.
|
|
874
|
-
"@astryxdesign/cli": "0.7.8-canary.
|
|
875
|
-
"@astryxdesign/interaction-harness": "0.7.8-canary.
|
|
873
|
+
"@astryxdesign/a11y-spec": "0.7.8-canary.f60d2bb",
|
|
874
|
+
"@astryxdesign/cli": "0.7.8-canary.f60d2bb",
|
|
875
|
+
"@astryxdesign/interaction-harness": "0.7.8-canary.f60d2bb",
|
|
876
876
|
"@babel/cli": "^7.29.7",
|
|
877
877
|
"@babel/core": "^7.29.7",
|
|
878
878
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -185,8 +185,9 @@ export interface PowerSearchBottomSheetProps {
|
|
|
185
185
|
filters: ReadonlyArray<PowerSearchFilter>;
|
|
186
186
|
/**
|
|
187
187
|
* The field's `onChange`. Done replays the draft's edits through it, in
|
|
188
|
-
* order,
|
|
189
|
-
* reported before Done
|
|
188
|
+
* order, onto the field's latest filters, each call carrying that list
|
|
189
|
+
* with the edit applied; nothing is reported before Done, and nothing when
|
|
190
|
+
* the field is read-only or disabled at Done.
|
|
190
191
|
*/
|
|
191
192
|
onChange: (
|
|
192
193
|
filters: ReadonlyArray<PowerSearchFilter>,
|
|
@@ -214,7 +215,7 @@ export interface PowerSearchBottomSheetProps {
|
|
|
214
215
|
// Styles
|
|
215
216
|
// =============================================================================
|
|
216
217
|
|
|
217
|
-
/** One edit of the draft: the
|
|
218
|
+
/** One edit of the draft: the draft before and after it, and its type and index in the draft; Done replays it onto the field's latest filters. */
|
|
218
219
|
interface DraftEdit {
|
|
219
220
|
before: ReadonlyArray<PowerSearchFilter>;
|
|
220
221
|
after: ReadonlyArray<PowerSearchFilter>;
|
|
@@ -1159,12 +1160,13 @@ export function PowerSearchBottomSheet({
|
|
|
1159
1160
|
// consumer gets the calls a popover session makes and keeps a filter it
|
|
1160
1161
|
// added meanwhile.
|
|
1161
1162
|
const commitAndClose = useCallback(() => {
|
|
1162
|
-
|
|
1163
|
+
// A field that became read-only while the flow was open reports nothing.
|
|
1164
|
+
if (isDraftDirty && isInteractive) {
|
|
1163
1165
|
replayDraftEdits(committedFilters, draftEditsRef.current, onCommit);
|
|
1164
1166
|
}
|
|
1165
1167
|
draftEditsRef.current = [];
|
|
1166
1168
|
closeFlow();
|
|
1167
|
-
}, [closeFlow, committedFilters, isDraftDirty, onCommit]);
|
|
1169
|
+
}, [closeFlow, committedFilters, isDraftDirty, isInteractive, onCommit]);
|
|
1168
1170
|
const requestDismiss = useCallback(() => {
|
|
1169
1171
|
if (isDraftDirty) {
|
|
1170
1172
|
setIsDiscardConfirmationOpen(true);
|
|
@@ -76,8 +76,9 @@ export const pressVars = stylex.defineVars({
|
|
|
76
76
|
*
|
|
77
77
|
* `defineConsts`, so the one number is inlined here at build time and is the
|
|
78
78
|
* controller's constant at run time (`PRESS_FADE_MS` in pressGesture.ts):
|
|
79
|
-
* the release animation and the
|
|
80
|
-
*
|
|
79
|
+
* the release animation and the controller's clock cannot drift apart. When
|
|
80
|
+
* that clock runs out, the controller waits for a release animation that a
|
|
81
|
+
* busy main thread started late, then removes the attribute.
|
|
81
82
|
*/
|
|
82
83
|
export const pressConsts = stylex.defineConsts({
|
|
83
84
|
/** How long the release takes, in ms. UIKit's deselect clock. */
|
|
@@ -109,7 +109,8 @@ interface LiveGesture {
|
|
|
109
109
|
* for a press of the Space key.
|
|
110
110
|
*/
|
|
111
111
|
pointerId: number | null;
|
|
112
|
-
|
|
112
|
+
/** Cancels the clock the current phase owes, or the release it waits for. */
|
|
113
|
+
timer: (() => void) | null;
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
/**
|
|
@@ -165,6 +166,76 @@ export function pressFadeMs(element: Element): number {
|
|
|
165
166
|
return reduced ? Math.min(scaled, PRESS_FADE_MS) : scaled;
|
|
166
167
|
}
|
|
167
168
|
|
|
169
|
+
/**
|
|
170
|
+
* The release animations that still run for `element`'s press. Each is a
|
|
171
|
+
* finite CSS animation that has not finished. It runs on the element, on its
|
|
172
|
+
* `::before` or `::after`, or on a descendant whose nearest press owner is
|
|
173
|
+
* the element, because a component can draw its release on a part inside the
|
|
174
|
+
* surface. A press owner nested inside plays the release of its own press,
|
|
175
|
+
* so it never extends this one. The list is empty where the engine has no Web
|
|
176
|
+
* Animations.
|
|
177
|
+
*/
|
|
178
|
+
function releaseAnimationsOf(element: Element): Animation[] {
|
|
179
|
+
if (typeof element.getAnimations !== 'function') {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
return element.getAnimations({subtree: true}).filter(animation => {
|
|
183
|
+
// The effect of a CSS animation is a KeyframeEffect. Its target is the
|
|
184
|
+
// element, also when the animation runs on a pseudo-element.
|
|
185
|
+
const effect = animation.effect as KeyframeEffect | null;
|
|
186
|
+
const target = effect?.target ?? null;
|
|
187
|
+
if (
|
|
188
|
+
effect == null ||
|
|
189
|
+
target == null ||
|
|
190
|
+
!('animationName' in animation) ||
|
|
191
|
+
(target !== element &&
|
|
192
|
+
target.closest(`[${PRESSABLE_ATTRIBUTE}]`) !== element)
|
|
193
|
+
) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (!Number.isFinite(Number(effect.getComputedTiming().endTime))) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return animation.playState !== 'finished';
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Calls `end` when the release on `element` is over. A busy main thread
|
|
205
|
+
* delays the first frame of the release animation, and with it the end. So a
|
|
206
|
+
* clock that starts at the `fading` write can cut the release short. When
|
|
207
|
+
* `clockMs` runs out, the release waits for each release animation that still
|
|
208
|
+
* runs or still waits for its first frame. It ends when those finish or are
|
|
209
|
+
* cancelled, as when the surface is unmounted or restyled. With no release
|
|
210
|
+
* animation (an instant release, a surface without one, an engine without Web
|
|
211
|
+
* Animations), the release ends at the clock. Returns a cancel.
|
|
212
|
+
*/
|
|
213
|
+
function endAfterRelease(
|
|
214
|
+
element: Element,
|
|
215
|
+
clockMs: number,
|
|
216
|
+
end: () => void,
|
|
217
|
+
): () => void {
|
|
218
|
+
let waiting = true;
|
|
219
|
+
const timer = setTimeout(() => {
|
|
220
|
+
const running = releaseAnimationsOf(element);
|
|
221
|
+
if (running.length === 0) {
|
|
222
|
+
end();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
void Promise.allSettled(
|
|
226
|
+
running.map(async animation => animation.finished),
|
|
227
|
+
).then(() => {
|
|
228
|
+
if (waiting) {
|
|
229
|
+
end();
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}, clockMs);
|
|
233
|
+
return () => {
|
|
234
|
+
waiting = false;
|
|
235
|
+
clearTimeout(timer);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
168
239
|
const TIMER_EVENT = {
|
|
169
240
|
fade: 'fadeEnd',
|
|
170
241
|
flash: 'flashEnd',
|
|
@@ -181,7 +252,7 @@ function paintElement(element: HTMLElement, paint: PressPaint): void {
|
|
|
181
252
|
|
|
182
253
|
function clearTimer(slot: Slot): void {
|
|
183
254
|
if (slot.live?.timer != null) {
|
|
184
|
-
|
|
255
|
+
slot.live.timer();
|
|
185
256
|
slot.live.timer = null;
|
|
186
257
|
}
|
|
187
258
|
}
|
|
@@ -217,25 +288,32 @@ function apply(slot: Slot, step: PressStep): void {
|
|
|
217
288
|
}
|
|
218
289
|
if (step.timer != null) {
|
|
219
290
|
const kind = step.timer;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
291
|
+
const fire = () => {
|
|
292
|
+
if (slot.live != null) {
|
|
293
|
+
slot.live.timer = null;
|
|
294
|
+
}
|
|
295
|
+
apply(
|
|
296
|
+
slot,
|
|
297
|
+
pressGestureStep(slot.live?.gesture ?? idlePressGesture(), {
|
|
298
|
+
kind: TIMER_EVENT[kind],
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
};
|
|
302
|
+
if (kind === 'fade') {
|
|
303
|
+
// The release fade follows the theme's tempo (AST-108). It ends when
|
|
304
|
+
// its animation ends, however late a busy main thread let it start.
|
|
305
|
+
live.timer = endAfterRelease(
|
|
306
|
+
live.element,
|
|
307
|
+
pressFadeMs(live.element),
|
|
308
|
+
fire,
|
|
309
|
+
);
|
|
310
|
+
} else {
|
|
311
|
+
// A TIMER, never `requestAnimationFrame`: iOS throttles frame callbacks
|
|
312
|
+
// while a scroll runs, so a frame-based delay would fire late, after the
|
|
313
|
+
// scroll, and paint a control nobody is pressing any more.
|
|
314
|
+
const timer = setTimeout(fire, TIMER_MS[kind]);
|
|
315
|
+
live.timer = () => clearTimeout(timer);
|
|
316
|
+
}
|
|
239
317
|
}
|
|
240
318
|
}
|
|
241
319
|
|
|
@@ -62,7 +62,9 @@ export const PRESS_FLASH_MS = 100;
|
|
|
62
62
|
* How long the paint takes to leave after a release: UIKit's deselect
|
|
63
63
|
* crossfade. The one number, shared with the stylesheet: the release
|
|
64
64
|
* animation in interactionOverlay.stylex.ts runs on the same constant, so the
|
|
65
|
-
*
|
|
65
|
+
* controller's release clock and the fade cannot drift apart. When the clock
|
|
66
|
+
* runs out, the controller waits for a release animation that still runs,
|
|
67
|
+
* then removes the attribute.
|
|
66
68
|
*/
|
|
67
69
|
export const PRESS_FADE_MS: number = pressConsts.releaseMs;
|
|
68
70
|
|
|
@@ -119,7 +121,7 @@ export type PressEvent =
|
|
|
119
121
|
| {kind: 'onset'}
|
|
120
122
|
/** {@link PRESS_FLASH_MS} elapsed on a too-quick tap. */
|
|
121
123
|
| {kind: 'flashEnd'}
|
|
122
|
-
/**
|
|
124
|
+
/** The release is over: its animation ended, or its clock ran out with none running. */
|
|
123
125
|
| {kind: 'fadeEnd'};
|
|
124
126
|
|
|
125
127
|
export interface PressStep {
|