@astryxdesign/core 0.7.8-canary.ffd29c5 → 0.7.8
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/CHANGELOG.md +132 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +30 -14
- package/dist/BottomSheet/bottomSheetStack.js +1 -1
- package/dist/BottomSheet/useSheetGestures.d.ts +3 -1
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +4 -0
- package/dist/CheckboxList/CheckboxList.d.ts +13 -2
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +10 -4
- package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListContext.js +2 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +14 -2
- package/dist/CheckboxList/requiredGroup.d.ts +22 -0
- package/dist/CheckboxList/requiredGroup.d.ts.map +1 -0
- package/dist/CheckboxList/requiredGroup.js +52 -0
- package/dist/DateInput/PickerWheel.d.ts +1 -1
- package/dist/DateInput/PickerWheel.d.ts.map +1 -1
- package/dist/DateInput/PickerWheel.js +27 -6
- package/dist/DateInput/Wheel.d.ts +2 -2
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +20 -6
- package/dist/DateInput/datePickerTouch.d.ts +13 -0
- package/dist/DateInput/datePickerTouch.d.ts.map +1 -0
- package/dist/DateInput/datePickerTouch.js +14 -0
- package/dist/DateInput/wheelOptions.d.ts +22 -2
- package/dist/DateInput/wheelOptions.d.ts.map +1 -1
- package/dist/DateInput/wheelOptions.js +14 -2
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +62 -119
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +6 -1
- package/dist/Overlay/Overlay.d.ts.map +1 -1
- package/dist/Overlay/Overlay.js +7 -2
- package/dist/PowerSearch/PowerSearch.d.ts +4 -2
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +24 -1
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchBottomSheet.js +172 -50
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +15 -15
- package/dist/Resizable/createResizable.d.ts +70 -16
- package/dist/Resizable/createResizable.d.ts.map +1 -1
- package/dist/Resizable/createResizable.js +180 -22
- package/dist/Resizable/separatorAttributes.d.ts +45 -0
- package/dist/Resizable/separatorAttributes.d.ts.map +1 -0
- package/dist/Resizable/separatorAttributes.js +35 -0
- package/dist/Resizable/useResizable.js +2 -2
- package/dist/TimeInput/TimeWheels.d.ts +32 -0
- package/dist/TimeInput/TimeWheels.d.ts.map +1 -0
- package/dist/TimeInput/TimeWheels.js +144 -0
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +63 -118
- package/dist/TimeInput/timeWheelRules.d.ts +124 -0
- package/dist/TimeInput/timeWheelRules.d.ts.map +1 -0
- package/dist/TimeInput/timeWheelRules.js +277 -0
- package/dist/Tokenizer/TokenizerBottomSheet.js +8 -12
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +6 -0
- package/dist/astryx.css +3 -1
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +3 -0
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +5 -2
- package/dist/i18n/generated-english/Resizable/createResizable.generated.d.ts +8 -0
- package/dist/i18n/generated-english/Resizable/createResizable.generated.d.ts.map +1 -0
- package/dist/i18n/generated-english/Resizable/createResizable.generated.js +13 -0
- package/dist/i18n/generated-english/TimeInput/TimeWheels.generated.d.ts +8 -0
- package/dist/i18n/generated-english/TimeInput/TimeWheels.generated.d.ts.map +1 -0
- package/dist/i18n/generated-english/TimeInput/TimeWheels.generated.js +17 -0
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +5 -2
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +5 -2
- package/dist/keyboard/index.d.ts +17 -0
- package/dist/keyboard/index.d.ts.map +1 -0
- package/dist/keyboard/index.js +19 -0
- package/dist/keyboard/keyboardHeight.d.ts +149 -0
- package/dist/keyboard/keyboardHeight.d.ts.map +1 -0
- package/dist/keyboard/keyboardHeight.js +432 -0
- package/dist/keyboard/keyboardLayoutGuide.d.ts +59 -0
- package/dist/keyboard/keyboardLayoutGuide.d.ts.map +1 -0
- package/dist/keyboard/keyboardLayoutGuide.js +207 -0
- package/dist/keyboard/keyboardModel.d.ts +93 -0
- package/dist/keyboard/keyboardModel.d.ts.map +1 -0
- package/dist/keyboard/keyboardModel.js +220 -0
- package/dist/keyboard/rideKeyboard.d.ts +42 -0
- package/dist/keyboard/rideKeyboard.d.ts.map +1 -0
- package/dist/keyboard/rideKeyboard.js +77 -0
- package/dist/keyboard/useMobileKeyboard.d.ts +61 -0
- package/dist/keyboard/useMobileKeyboard.d.ts.map +1 -0
- package/dist/keyboard/useMobileKeyboard.js +699 -0
- package/dist/motion/android/index.d.ts +16 -0
- package/dist/motion/android/index.d.ts.map +1 -0
- package/dist/motion/android/index.js +18 -0
- package/dist/motion/android/keyboard.d.ts +83 -0
- package/dist/motion/android/keyboard.d.ts.map +1 -0
- package/dist/motion/android/keyboard.js +96 -0
- package/dist/motion/uikit/datePicker.d.ts +58 -0
- package/dist/motion/uikit/datePicker.d.ts.map +1 -0
- package/dist/motion/uikit/datePicker.js +60 -0
- package/dist/motion/uikit/index.d.ts +4 -3
- package/dist/motion/uikit/index.d.ts.map +1 -1
- package/dist/motion/uikit/index.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/layoutViewport.d.ts +25 -0
- package/dist/utils/layoutViewport.d.ts.map +1 -0
- package/dist/utils/layoutViewport.js +102 -0
- 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/dist/utils/selectionLock.d.ts +10 -1
- package/dist/utils/selectionLock.d.ts.map +1 -1
- package/dist/utils/selectionLock.js +31 -5
- package/dist/utils/sharedMutationObserver.d.ts +2 -1
- package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
- package/dist/utils/sharedMutationObserver.js +2 -1
- package/dist/utils/timeParser.d.ts +2 -1
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +29 -3
- package/locales/en.json +21 -9
- package/locales/pseudo.json +15 -6
- package/package.json +5 -5
- package/src/BottomSheet/BottomSheet.doc.mjs +1 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +41 -13
- package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +5 -0
- package/src/BottomSheet/bottomSheetStack.ts +1 -1
- package/src/BottomSheet/useSheetGestures.ts +6 -0
- package/src/CheckboxList/CheckboxList.doc.mjs +7 -0
- package/src/CheckboxList/CheckboxList.tsx +34 -2
- package/src/CheckboxList/CheckboxListContext.tsx +9 -1
- package/src/CheckboxList/CheckboxListItem.tsx +23 -2
- package/src/CheckboxList/requiredGroup.ts +77 -0
- package/src/DateInput/PickerWheel.tsx +37 -4
- package/src/DateInput/Wheel.tsx +30 -3
- package/src/DateInput/datePickerTouch.ts +15 -0
- package/src/DateInput/wheelOptions.ts +31 -2
- package/src/DateTimeInput/TouchDateTimeField.tsx +94 -178
- package/src/HoverCard/useHoverCard.tsx +6 -1
- package/src/Overlay/Overlay.tsx +7 -2
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearch.tsx +4 -2
- package/src/PowerSearch/PowerSearchBottomSheet.tsx +237 -62
- package/src/Resizable/ResizeHandle.tsx +18 -26
- package/src/Resizable/createResizable.ts +242 -27
- package/src/Resizable/separatorAttributes.ts +73 -0
- package/src/Resizable/useResizable.ts +2 -2
- package/src/TimeInput/TimeWheels.tsx +218 -0
- package/src/TimeInput/TouchTimeField.tsx +93 -157
- package/src/TimeInput/timeWheelRules.ts +352 -0
- package/src/Tokenizer/TokenizerBottomSheet.tsx +8 -12
- package/src/Tooltip/useTooltip.tsx +6 -0
- package/src/gestures/GestureHost.ts +5 -4
- package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +5 -2
- package/src/i18n/generated-english/Resizable/createResizable.generated.ts +14 -0
- package/src/i18n/generated-english/TimeInput/TimeWheels.generated.ts +18 -0
- package/src/i18n/generated-locales/en.generated.ts +6 -4
- package/src/i18n/generated-locales/pseudo.generated.ts +6 -4
- package/src/keyboard/README.md +197 -0
- package/src/keyboard/index.ts +48 -0
- package/src/keyboard/keyboardHeight.ts +543 -0
- package/src/keyboard/keyboardLayoutGuide.ts +266 -0
- package/src/keyboard/keyboardModel.ts +301 -0
- package/src/keyboard/rideKeyboard.ts +100 -0
- package/src/keyboard/useMobileKeyboard.ts +866 -0
- package/src/motion/MEASUREMENTS.md +10 -0
- package/src/motion/README.md +2 -0
- package/src/motion/android/index.ts +18 -0
- package/src/motion/android/keyboard.ts +96 -0
- package/src/motion/uikit/datePicker.ts +60 -0
- package/src/motion/uikit/index.ts +4 -3
- package/src/utils/interactionOverlay.stylex.ts +3 -2
- package/src/utils/layoutViewport.ts +104 -0
- package/src/utils/pressFeedback.ts +99 -21
- package/src/utils/pressGesture.ts +4 -2
- package/src/utils/selectionLock.ts +40 -6
- package/src/utils/sharedMutationObserver.ts +2 -1
- package/src/utils/timeParser.ts +40 -3
- package/dist/BottomSheet/useMobileKeyboard.d.ts +0 -21
- package/dist/BottomSheet/useMobileKeyboard.d.ts.map +0 -1
- package/dist/BottomSheet/useMobileKeyboard.js +0 -413
- package/src/BottomSheet/useMobileKeyboard.ts +0 -542
|
@@ -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
|
|
|
@@ -19,6 +19,15 @@ export interface DragSelectionReleaseOptions {
|
|
|
19
19
|
*/
|
|
20
20
|
isCancel?: boolean;
|
|
21
21
|
}
|
|
22
|
+
/** A drag's selection hold: call it to release the hold. */
|
|
23
|
+
export interface DragSelectionHold {
|
|
24
|
+
(options?: DragSelectionReleaseOptions): void;
|
|
25
|
+
/**
|
|
26
|
+
* Called after the lift's own handlers ran. A selection they made since
|
|
27
|
+
* the release stays: the hold then puts it back, not the press's.
|
|
28
|
+
*/
|
|
29
|
+
keepLiftSelection(): void;
|
|
30
|
+
}
|
|
22
31
|
/**
|
|
23
32
|
* Refuse a new selection while a mouse or pen drag that a recognizer claimed
|
|
24
33
|
* runs, until the returned function runs. `kept` is the page's selection
|
|
@@ -38,7 +47,7 @@ export interface DragSelectionReleaseOptions {
|
|
|
38
47
|
* follows a cancel. `onReleased` runs once the document's hold has ended.
|
|
39
48
|
* The page listeners exist only during a press. Idempotent.
|
|
40
49
|
*/
|
|
41
|
-
export declare function lockDragSelection(elements: Iterable<Element>, view: Window | null | undefined, kept: ReadonlyArray<AbstractRange>, onReleased?: () => void):
|
|
50
|
+
export declare function lockDragSelection(elements: Iterable<Element>, view: Window | null | undefined, kept: ReadonlyArray<AbstractRange>, onReleased?: () => void): DragSelectionHold;
|
|
42
51
|
/**
|
|
43
52
|
* The page's selection now, its ranges copied; none for a collapsed or empty
|
|
44
53
|
* one. A drag's hold keeps what this read when the press began.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectionLock.d.ts","sourceRoot":"","sources":["../../src/utils/selectionLock.ts"],"names":[],"mappings":"AAoDA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAWD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC3B,MAAM,EAAE,WAAW,GAAG,IAAI,GACzB,MAAM,IAAI,CA4BZ;AAED,mEAAmE;AACnE,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,IAAI,GACtB,
|
|
1
|
+
{"version":3,"file":"selectionLock.d.ts","sourceRoot":"","sources":["../../src/utils/selectionLock.ts"],"names":[],"mappings":"AAoDA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAWD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC3B,MAAM,EAAE,WAAW,GAAG,IAAI,GACzB,MAAM,IAAI,CA4BZ;AAED,mEAAmE;AACnE,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,4DAA4D;AAC5D,MAAM,WAAW,iBAAiB;IAChC,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC9C;;;OAGG;IACH,iBAAiB,IAAI,IAAI,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,IAAI,GACtB,iBAAiB,CAuGnB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,aAAa,CAAC,aAAa,CAAC,CAiB9B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,QAAQ,EACb,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,WAAW,GAAG,IAAI,GACzB,OAAO,CA+CT"}
|
|
@@ -84,6 +84,8 @@ export function lockSelection(elements, target) {
|
|
|
84
84
|
|
|
85
85
|
/** How a drag's selection hold ends: at a lift, or at a cancel. */
|
|
86
86
|
|
|
87
|
+
/** A drag's selection hold: call it to release the hold. */
|
|
88
|
+
|
|
87
89
|
/**
|
|
88
90
|
* Refuse a new selection while a mouse or pen drag that a recognizer claimed
|
|
89
91
|
* runs, until the returned function runs. `kept` is the page's selection
|
|
@@ -105,7 +107,11 @@ export function lockSelection(elements, target) {
|
|
|
105
107
|
*/
|
|
106
108
|
export function lockDragSelection(elements, view, kept, onReleased) {
|
|
107
109
|
const doc = view?.document ?? null;
|
|
108
|
-
|
|
110
|
+
let keptRanges = kept;
|
|
111
|
+
let roots = shadowRootsOfRanges(keptRanges);
|
|
112
|
+
// The selection when the lift released the hold, until the lift's
|
|
113
|
+
// handlers ran.
|
|
114
|
+
let atLift = null;
|
|
109
115
|
const restore = () => {
|
|
110
116
|
const selection = view?.getSelection();
|
|
111
117
|
// The focused field keeps its own selection (a drag never starts in one).
|
|
@@ -113,7 +119,7 @@ export function lockDragSelection(elements, view, kept, onReleased) {
|
|
|
113
119
|
return;
|
|
114
120
|
}
|
|
115
121
|
const held = heldRanges(selection, roots);
|
|
116
|
-
if (
|
|
122
|
+
if (keptRanges.length === 0) {
|
|
117
123
|
if (held.some(range => !range.collapsed)) {
|
|
118
124
|
selection.removeAllRanges();
|
|
119
125
|
}
|
|
@@ -121,10 +127,10 @@ export function lockDragSelection(elements, view, kept, onReleased) {
|
|
|
121
127
|
}
|
|
122
128
|
// A render can remove what the kept selection held: then there is
|
|
123
129
|
// nothing to put back.
|
|
124
|
-
if (sameRanges(held,
|
|
130
|
+
if (sameRanges(held, keptRanges) || !keptRanges.every(isValidRange)) {
|
|
125
131
|
return;
|
|
126
132
|
}
|
|
127
|
-
selectRanges(selection,
|
|
133
|
+
selectRanges(selection, keptRanges);
|
|
128
134
|
};
|
|
129
135
|
restore();
|
|
130
136
|
const set = new Set(elements);
|
|
@@ -160,11 +166,12 @@ export function lockDragSelection(elements, view, kept, onReleased) {
|
|
|
160
166
|
onReleased?.();
|
|
161
167
|
}
|
|
162
168
|
};
|
|
163
|
-
|
|
169
|
+
const release = ({
|
|
164
170
|
isCancel = false
|
|
165
171
|
} = {}) => {
|
|
166
172
|
if (stage === 'held') {
|
|
167
173
|
stage = 'lifted';
|
|
174
|
+
atLift = selectionRangesOf(view);
|
|
168
175
|
for (const element of set) {
|
|
169
176
|
element.removeEventListener('selectstart', refuse);
|
|
170
177
|
}
|
|
@@ -180,6 +187,25 @@ export function lockDragSelection(elements, view, kept, onReleased) {
|
|
|
180
187
|
releaseDocument();
|
|
181
188
|
}
|
|
182
189
|
};
|
|
190
|
+
release.keepLiftSelection = () => {
|
|
191
|
+
if (stage !== 'lifted' || atLift == null) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const before = atLift;
|
|
195
|
+
atLift = null;
|
|
196
|
+
const keepIfChanged = () => {
|
|
197
|
+
const now = selectionRangesOf(view);
|
|
198
|
+
if (stage !== 'done' && !sameRanges(now, before)) {
|
|
199
|
+
keptRanges = now;
|
|
200
|
+
roots = shadowRootsOfRanges(now);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
keepIfChanged();
|
|
204
|
+
// A click's state update renders in a microtask, and its layout effect
|
|
205
|
+
// may select then. That still runs before the press's own mouseup.
|
|
206
|
+
queueMicrotask(keepIfChanged);
|
|
207
|
+
};
|
|
208
|
+
return release;
|
|
183
209
|
}
|
|
184
210
|
|
|
185
211
|
/**
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* @output Exports observeMutations (one shared observer per set of options)
|
|
5
5
|
* and observeContent, its text-and-children form
|
|
6
6
|
* @position Internal utility; consumed by useTruncation, hitArea, AppShell's
|
|
7
|
-
* main-overflow observer
|
|
7
|
+
* main-overflow observer, ModalAnnounceProvider's presentation watch and
|
|
8
|
+
* CheckboxList's required group. The mutation counterpart of sharedResizeObserver:
|
|
8
9
|
* one observer for every element observed with the same options, so a
|
|
9
10
|
* repeated cell, row or region does not create one observer per instance.
|
|
10
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharedMutationObserver.d.ts","sourceRoot":"","sources":["../../src/utils/sharedMutationObserver.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"sharedMutationObserver.d.ts","sourceRoot":"","sources":["../../src/utils/sharedMutationObserver.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,KAAK,gBAAgB,GAAG,MAAM,IAAI,CAAC;AA4DnC;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,gBAAgB,GACzB,MAAM,IAAI,CAmCZ;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,gBAAgB,GACzB,MAAM,IAAI,CAEZ"}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* @output Exports observeMutations (one shared observer per set of options)
|
|
9
9
|
* and observeContent, its text-and-children form
|
|
10
10
|
* @position Internal utility; consumed by useTruncation, hitArea, AppShell's
|
|
11
|
-
* main-overflow observer
|
|
11
|
+
* main-overflow observer, ModalAnnounceProvider's presentation watch and
|
|
12
|
+
* CheckboxList's required group. The mutation counterpart of sharedResizeObserver:
|
|
12
13
|
* one observer for every element observed with the same options, so a
|
|
13
14
|
* repeated cell, row or region does not create one observer per instance.
|
|
14
15
|
*/
|
|
@@ -65,7 +65,8 @@ export declare function compareTime(a: ISOTimeString | undefined, b: ISOTimeStri
|
|
|
65
65
|
*/
|
|
66
66
|
export declare function isTimeInRange(time: ISOTimeString, min?: ISOTimeString, max?: ISOTimeString): boolean;
|
|
67
67
|
/**
|
|
68
|
-
* Clamps a time to a min/max range.
|
|
68
|
+
* Clamps a time to a min/max range. Without seconds, a bound that falls
|
|
69
|
+
* between whole minutes gives the nearest whole minute inside the range.
|
|
69
70
|
*/
|
|
70
71
|
export declare function clampTime(time: ISOTimeString, min?: ISOTimeString, max?: ISOTimeString, includeSeconds?: boolean): ISOTimeString;
|
|
71
72
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeParser.d.ts","sourceRoot":"","sources":["../../src/utils/timeParser.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAMvE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA6B5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,cAAc,GAAE,OAAe,GAC9B,aAAa,CAUf;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,cAAc,GAAE,OAAe,GAC9B,MAAM,CAqBR;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,cAAc,GAAE,OAAe,GAC9B,MAAM,CAmBR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,cAAc,GAAE,OAAe,GAC9B,aAAa,GAAG,IAAI,CA6HtB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,aAAa,GAAG,SAAS,EAC5B,CAAC,EAAE,aAAa,GAAG,SAAS,GAC3B,MAAM,CA4BR;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,EACnB,GAAG,CAAC,EAAE,aAAa,EACnB,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAQT;AAED
|
|
1
|
+
{"version":3,"file":"timeParser.d.ts","sourceRoot":"","sources":["../../src/utils/timeParser.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAMvE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA6B5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,cAAc,GAAE,OAAe,GAC9B,aAAa,CAUf;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,cAAc,GAAE,OAAe,GAC9B,MAAM,CAqBR;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,cAAc,GAAE,OAAe,GAC9B,MAAM,CAmBR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,cAAc,GAAE,OAAe,GAC9B,aAAa,GAAG,IAAI,CA6HtB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,aAAa,GAAG,SAAS,EAC5B,CAAC,EAAE,aAAa,GAAG,SAAS,GAC3B,MAAM,CA4BR;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,EACnB,GAAG,CAAC,EAAE,aAAa,EACnB,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAQT;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,aAAa,EACnB,GAAG,CAAC,EAAE,aAAa,EACnB,GAAG,CAAC,EAAE,aAAa,EACnB,cAAc,GAAE,OAAe,GAC9B,aAAa,CA0Bf;AAgCD;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,aAAa,EACnB,YAAY,EAAE,MAAM,EACpB,cAAc,GAAE,OAAe,GAC9B,aAAa,CA2Bf"}
|
package/dist/utils/timeParser.js
CHANGED
|
@@ -289,7 +289,8 @@ export function isTimeInRange(time, min, max) {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
/**
|
|
292
|
-
* Clamps a time to a min/max range.
|
|
292
|
+
* Clamps a time to a min/max range. Without seconds, a bound that falls
|
|
293
|
+
* between whole minutes gives the nearest whole minute inside the range.
|
|
293
294
|
*/
|
|
294
295
|
export function clampTime(time, min, max, includeSeconds = false) {
|
|
295
296
|
const parsed = parseISOTime(time);
|
|
@@ -299,18 +300,43 @@ export function clampTime(time, min, max, includeSeconds = false) {
|
|
|
299
300
|
if (min && compareTime(time, min) < 0) {
|
|
300
301
|
const parsedMin = parseISOTime(min);
|
|
301
302
|
if (parsedMin) {
|
|
302
|
-
return formatISOTime(parsedMin, includeSeconds);
|
|
303
|
+
return formatISOTime(onWholeMinute(parsedMin, 1, includeSeconds, max), includeSeconds);
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
306
|
if (max && compareTime(time, max) > 0) {
|
|
306
307
|
const parsedMax = parseISOTime(max);
|
|
307
308
|
if (parsedMax) {
|
|
308
|
-
return formatISOTime(parsedMax, includeSeconds);
|
|
309
|
+
return formatISOTime(onWholeMinute(parsedMax, -1, includeSeconds, min), includeSeconds);
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
return time;
|
|
312
313
|
}
|
|
313
314
|
|
|
315
|
+
/**
|
|
316
|
+
* A bound as a time without seconds reaches it: the next whole minute up from
|
|
317
|
+
* a minimum, or the whole minute down from a maximum. With no whole minute
|
|
318
|
+
* before the other bound, the bound stays as it is.
|
|
319
|
+
*/
|
|
320
|
+
function onWholeMinute(bound, direction, includeSeconds, other) {
|
|
321
|
+
if (includeSeconds || bound.second === 0) {
|
|
322
|
+
return bound;
|
|
323
|
+
}
|
|
324
|
+
const minutes = bound.hour * 60 + bound.minute + (direction === 1 ? 1 : 0);
|
|
325
|
+
if (minutes > 23 * 60 + 59) {
|
|
326
|
+
return bound;
|
|
327
|
+
}
|
|
328
|
+
const rounded = {
|
|
329
|
+
hour: Math.floor(minutes / 60),
|
|
330
|
+
minute: minutes % 60,
|
|
331
|
+
second: 0
|
|
332
|
+
};
|
|
333
|
+
const order = compareTime(formatISOTime(rounded, true), other);
|
|
334
|
+
if (other && (direction === 1 ? order > 0 : order < 0)) {
|
|
335
|
+
return bound;
|
|
336
|
+
}
|
|
337
|
+
return rounded;
|
|
338
|
+
}
|
|
339
|
+
|
|
314
340
|
/**
|
|
315
341
|
* Adjusts a time by adding/subtracting minutes.
|
|
316
342
|
*
|
package/locales/en.json
CHANGED
|
@@ -267,6 +267,10 @@
|
|
|
267
267
|
"defaultMessage": "No filters applied",
|
|
268
268
|
"description": "Heading of the PowerSearch Filters sheet empty state when no filter is active (experimental bottom-sheet presentation). Neutral tone."
|
|
269
269
|
},
|
|
270
|
+
"@astryx.powersearch.sheet.noFiltersInDraft": {
|
|
271
|
+
"defaultMessage": "No filters",
|
|
272
|
+
"description": "Heading of the PowerSearch Filters sheet empty state while the sheet's draft is empty but the field still has applied filters, which Done replaces. Must not say the filters are applied or removed yet. Neutral tone."
|
|
273
|
+
},
|
|
270
274
|
"@astryx.powersearch.sheet.noFiltersDescription": {
|
|
271
275
|
"defaultMessage": "Add a filter to narrow your results and find what you need faster.",
|
|
272
276
|
"description": "Guidance line under the PowerSearch Filters sheet empty state, above its Add filter action. One short sentence."
|
|
@@ -293,7 +297,23 @@
|
|
|
293
297
|
},
|
|
294
298
|
"@astryx.powersearch.sheet.done": {
|
|
295
299
|
"defaultMessage": "Done",
|
|
296
|
-
"description": "Primary button label in the PowerSearch Filters sheet footer that closes the sheet. Short; marks the end of the filtering task."
|
|
300
|
+
"description": "Primary button label in the PowerSearch Filters sheet footer that commits the sheet's filter draft and closes the sheet. Short; marks the end of the filtering task."
|
|
301
|
+
},
|
|
302
|
+
"@astryx.powersearch.sheet.discardTitle": {
|
|
303
|
+
"defaultMessage": "Discard changes?",
|
|
304
|
+
"description": "Title of the confirmation dialog shown when someone tries to dismiss the PowerSearch filter sheet after changing its filter draft. A question."
|
|
305
|
+
},
|
|
306
|
+
"@astryx.powersearch.sheet.discardDescription": {
|
|
307
|
+
"defaultMessage": "Your changes to the filters haven't been applied.",
|
|
308
|
+
"description": "Body of the dirty-dismiss confirmation inside the PowerSearch filter sheet. Done applies the draft; a dismissal would drop it."
|
|
309
|
+
},
|
|
310
|
+
"@astryx.powersearch.sheet.keepEditing": {
|
|
311
|
+
"defaultMessage": "Keep editing",
|
|
312
|
+
"description": "Label of the safe cancel action in the PowerSearch dirty-dismiss confirmation. It closes the confirmation and returns to the unchanged draft."
|
|
313
|
+
},
|
|
314
|
+
"@astryx.powersearch.sheet.discardConfirm": {
|
|
315
|
+
"defaultMessage": "Discard changes",
|
|
316
|
+
"description": "Label of the destructive action in the PowerSearch dirty-dismiss confirmation. It closes the sheet without applying its filter draft."
|
|
297
317
|
},
|
|
298
318
|
"@astryx.powersearch.sheet.search": {
|
|
299
319
|
"defaultMessage": "Search",
|
|
@@ -367,14 +387,6 @@
|
|
|
367
387
|
"defaultMessage": "{label} {value}",
|
|
368
388
|
"description": "A Filters row's \"field operator\" text joined with its value, as the removal announcement reads it (for example \"Status is Open\"). Translators may reorder the two parts."
|
|
369
389
|
},
|
|
370
|
-
"@astryx.powersearch.sheet.filtersClearedWithCount": {
|
|
371
|
-
"defaultMessage": "All filters removed. {resultCount}",
|
|
372
|
-
"description": "Polite screen-reader announcement after Clear all in the PowerSearch touch sheet when the result count changed with it; resultCount is the field's already formatted result count (for example \"240 results\"). Live-region text, never shown on screen."
|
|
373
|
-
},
|
|
374
|
-
"@astryx.powersearch.sheet.editableFiltersClearedWithCount": {
|
|
375
|
-
"defaultMessage": "Editable filters removed. {resultCount}",
|
|
376
|
-
"description": "Polite screen-reader announcement after Clear all in the PowerSearch touch sheet removes the editable filters, keeps read-only ones, and the result count changed with it; resultCount is the field's already formatted result count (for example \"240 results\"). Live-region text, never shown on screen."
|
|
377
|
-
},
|
|
378
390
|
"@astryx.alertDialog.cancel": {
|
|
379
391
|
"defaultMessage": "Cancel",
|
|
380
392
|
"description": "Button label on the secondary/dismiss button of an AlertDialog (modal confirmation). Imperative verb; consumers usually override with task-specific text."
|
package/locales/pseudo.json
CHANGED
|
@@ -200,6 +200,9 @@
|
|
|
200
200
|
"@astryx.powersearch.sheet.noFilters": {
|
|
201
201
|
"defaultMessage": "⟦Ñó ƒíłţéřš àþþłíéð⟧"
|
|
202
202
|
},
|
|
203
|
+
"@astryx.powersearch.sheet.noFiltersInDraft": {
|
|
204
|
+
"defaultMessage": "⟦Ñó ƒíłţéřš⟧"
|
|
205
|
+
},
|
|
203
206
|
"@astryx.powersearch.sheet.noFiltersDescription": {
|
|
204
207
|
"defaultMessage": "⟦Àðð à ƒíłţéř ţó ñàřřóŵ ýóúř řéšúłţš àñð ƒíñð ŵĥàţ ýóú ñééð ƒàšţéř.⟧"
|
|
205
208
|
},
|
|
@@ -221,6 +224,18 @@
|
|
|
221
224
|
"@astryx.powersearch.sheet.done": {
|
|
222
225
|
"defaultMessage": "⟦Ðóñé⟧"
|
|
223
226
|
},
|
|
227
|
+
"@astryx.powersearch.sheet.discardTitle": {
|
|
228
|
+
"defaultMessage": "⟦Ðíšçàřð çĥàñĝéš?⟧"
|
|
229
|
+
},
|
|
230
|
+
"@astryx.powersearch.sheet.discardDescription": {
|
|
231
|
+
"defaultMessage": "⟦Ýóúř çĥàñĝéš ţó ţĥé ƒíłţéřš ĥàṽéñ'ţ ƀééñ àþþłíéð.⟧"
|
|
232
|
+
},
|
|
233
|
+
"@astryx.powersearch.sheet.keepEditing": {
|
|
234
|
+
"defaultMessage": "⟦Ķééþ éðíţíñĝ⟧"
|
|
235
|
+
},
|
|
236
|
+
"@astryx.powersearch.sheet.discardConfirm": {
|
|
237
|
+
"defaultMessage": "⟦Ðíšçàřð çĥàñĝéš⟧"
|
|
238
|
+
},
|
|
224
239
|
"@astryx.powersearch.sheet.search": {
|
|
225
240
|
"defaultMessage": "⟦Šéàřçĥ⟧"
|
|
226
241
|
},
|
|
@@ -275,12 +290,6 @@
|
|
|
275
290
|
"@astryx.powersearch.sheet.labelValue": {
|
|
276
291
|
"defaultMessage": "⟦{label} {value}⟧"
|
|
277
292
|
},
|
|
278
|
-
"@astryx.powersearch.sheet.filtersClearedWithCount": {
|
|
279
|
-
"defaultMessage": "⟦Àłł ƒíłţéřš řéɱóṽéð. {resultCount}⟧"
|
|
280
|
-
},
|
|
281
|
-
"@astryx.powersearch.sheet.editableFiltersClearedWithCount": {
|
|
282
|
-
"defaultMessage": "⟦Éðíţàƀłé ƒíłţéřš řéɱóṽéð. {resultCount}⟧"
|
|
283
|
-
},
|
|
284
293
|
"@astryx.alertDialog.cancel": {
|
|
285
294
|
"defaultMessage": "⟦Çàñçéł⟧"
|
|
286
295
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.8
|
|
3
|
+
"version": "0.7.8",
|
|
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,6 @@
|
|
|
870
870
|
"react-dom": ">=19.0.0"
|
|
871
871
|
},
|
|
872
872
|
"devDependencies": {
|
|
873
|
-
"@astryxdesign/a11y-spec": "0.7.8-canary.ffd29c5",
|
|
874
|
-
"@astryxdesign/cli": "0.7.8-canary.ffd29c5",
|
|
875
|
-
"@astryxdesign/interaction-harness": "0.7.8-canary.ffd29c5",
|
|
876
873
|
"@babel/cli": "^7.29.7",
|
|
877
874
|
"@babel/core": "^7.29.7",
|
|
878
875
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -883,7 +880,10 @@
|
|
|
883
880
|
"@testing-library/jest-dom": "^6.6.0",
|
|
884
881
|
"@testing-library/react": "^16.3.2",
|
|
885
882
|
"@types/babel__core": "^7.20.5",
|
|
886
|
-
"tailwindcss": "^4.3.2"
|
|
883
|
+
"tailwindcss": "^4.3.2",
|
|
884
|
+
"@astryxdesign/cli": "0.7.8",
|
|
885
|
+
"@astryxdesign/interaction-harness": "0.0.0",
|
|
886
|
+
"@astryxdesign/a11y-spec": "0.0.0"
|
|
887
887
|
},
|
|
888
888
|
"dependencies": {
|
|
889
889
|
"intl-messageformat": "^11.2.9"
|
|
@@ -152,7 +152,7 @@ export const docs = {
|
|
|
152
152
|
name: 'height',
|
|
153
153
|
type: "'hug' | 'capped' | 'tall' | number | string",
|
|
154
154
|
description:
|
|
155
|
-
"How tall the sheet is. Named budgets: 'hug' fits its content up to 92% of the viewport, 'capped' is a scrolling mid-height panel (~62%), and 'tall' is a pinned near-full panel (~92%) for content that streams in. Or pass a number (px) / CSS length for a custom budget. Give snapPoints to let the user drag between heights. On shorter viewports the sheet fills the available height. Only a fully expanded 'tall' sheet provides mobile-keyboard accommodation: it stays put and scrolls each focused control above the keyboard. Hug, Capped, numeric and CSS-length heights never do, and a Tall sheet stops doing it the moment the user drags it to a shorter detent, resuming when they drag it back. Outside that state the sheet neither moves nor adds keyboard scroll space, and the browser's own focus reveal is left in place; on iOS that reveal can shift the whole page.",
|
|
155
|
+
"How tall the sheet is. Named budgets: 'hug' fits its content up to 92% of the viewport, 'capped' is a scrolling mid-height panel (~62%), and 'tall' is a pinned near-full panel (~92%) for content that streams in. Or pass a number (px) / CSS length for a custom budget. Give snapPoints to let the user drag between heights. On shorter viewports the sheet fills the available height. Only a fully expanded 'tall' sheet provides mobile-keyboard accommodation: it stays put and scrolls each focused control above the keyboard. Hug, Capped, numeric and CSS-length heights never do, and a Tall sheet stops doing it the moment the user drags it to a shorter detent, resuming when they drag it back. Outside that state the sheet neither moves nor adds keyboard scroll space, and the browser's own focus reveal is left in place; on iOS that reveal can shift the whole page. This accommodation applies where the visual viewport shrinks for the keyboard, as in Safari on iOS, Firefox for Android and Chrome on Android, where the sheet places the focused control just above the keyboard as it comes up, and the control rides the rest of Gboard\u2019s slide up, and the slide down after it loses focus; a hide Chrome reports late, such as Back, rides down from Chrome\u2019s report (`astryx docs browser-support`).",
|
|
156
156
|
default: "'capped'",
|
|
157
157
|
},
|
|
158
158
|
{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - /packages/core/src/BottomSheet/BottomSheet.tsx
|
|
15
15
|
* - /packages/core/src/BottomSheet/BottomSheetPanel.test.tsx
|
|
16
16
|
* - /packages/core/src/BottomSheet/snapOffsets.ts
|
|
17
|
-
* - /packages/core/src/
|
|
17
|
+
* - /packages/core/src/keyboard/useMobileKeyboard.ts
|
|
18
18
|
* - /packages/core/src/BottomSheet/useSheetGestures.ts
|
|
19
19
|
* - /packages/core/src/Layout/container.stylex.ts (bottom-sheet padding chain)
|
|
20
20
|
*/
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
resolveSnapPoints,
|
|
67
67
|
type BottomSheetSnapPoint,
|
|
68
68
|
} from './snapOffsets';
|
|
69
|
-
import {useMobileKeyboard} from '
|
|
69
|
+
import {useMobileKeyboard} from '../keyboard/useMobileKeyboard';
|
|
70
70
|
import {
|
|
71
71
|
scrollPreservationPadding,
|
|
72
72
|
sheetLayoutHeight,
|
|
@@ -316,13 +316,29 @@ const styles = stylex.create({
|
|
|
316
316
|
},
|
|
317
317
|
tallKeyboardBody: {
|
|
318
318
|
scrollPaddingBlockEnd: MOBILE_KEYBOARD_BOTTOM_CLEARANCE,
|
|
319
|
+
},
|
|
320
|
+
// The inset the keyboard takes at the end of a Tall sheet's content
|
|
321
|
+
// (useMobileKeyboard), so its last control can scroll above the keyboard.
|
|
322
|
+
// It is the content box's last in-flow box, after the content's own
|
|
323
|
+
// children, not a box after the content. Firefox resolves the content's
|
|
324
|
+
// `min-height: min-content` as `auto` in the block axis. The content box
|
|
325
|
+
// then keeps the body's height while its children overflow it. An inset
|
|
326
|
+
// after that box sat inside the overflow and added no scroll range. In
|
|
327
|
+
// Firefox for Android, the body's scrollHeight was 1491 with and without a
|
|
328
|
+
// 357 px inset.
|
|
329
|
+
tallKeyboardContent: {
|
|
319
330
|
'::after': {
|
|
320
331
|
content: '""',
|
|
321
332
|
display: 'block',
|
|
322
|
-
blockSize:
|
|
333
|
+
blockSize: 0,
|
|
323
334
|
pointerEvents: 'none',
|
|
324
335
|
},
|
|
325
336
|
},
|
|
337
|
+
keyboardInset: (inset: number) => ({
|
|
338
|
+
'::after': {
|
|
339
|
+
blockSize: inset,
|
|
340
|
+
},
|
|
341
|
+
}),
|
|
326
342
|
budget: {
|
|
327
343
|
height: `calc(var(--_sheet-budget) + ${OVERSCROLL_PADDING}px)`,
|
|
328
344
|
},
|
|
@@ -564,6 +580,9 @@ export function BottomSheetPanel({
|
|
|
564
580
|
...props
|
|
565
581
|
}: BottomSheetPanelProps) {
|
|
566
582
|
const elementRef = useRef<HTMLDivElement | null>(null);
|
|
583
|
+
const dismissKeyboardRef = useRef<
|
|
584
|
+
((focusTarget?: HTMLElement) => void) | null
|
|
585
|
+
>(null);
|
|
567
586
|
const previousStateRef = useRef(state);
|
|
568
587
|
const reactivatedEntranceRef = useRef(false);
|
|
569
588
|
const onMotionStartRef = useRef(onMotionStart);
|
|
@@ -639,7 +658,6 @@ export function BottomSheetPanel({
|
|
|
639
658
|
dragOffset,
|
|
640
659
|
settledOffset,
|
|
641
660
|
isDragging,
|
|
642
|
-
isTraveling,
|
|
643
661
|
isDismissedByTouchDrag,
|
|
644
662
|
sheetHeight,
|
|
645
663
|
scrollPreservationInset,
|
|
@@ -653,6 +671,10 @@ export function BottomSheetPanel({
|
|
|
653
671
|
offscreenBlockEndInset: OVERSCROLL_PADDING,
|
|
654
672
|
onDismiss,
|
|
655
673
|
snapHeights,
|
|
674
|
+
onTravelStart: () => {
|
|
675
|
+
// Focusing the sheet dismisses the keyboard as the sheet starts to move.
|
|
676
|
+
dismissKeyboardRef.current?.(elementRef.current ?? undefined);
|
|
677
|
+
},
|
|
656
678
|
onScrimOpacity,
|
|
657
679
|
hasFixedHeight: consumerHeight != null,
|
|
658
680
|
});
|
|
@@ -686,17 +708,19 @@ export function BottomSheetPanel({
|
|
|
686
708
|
isPresentedRef.current = isPresented;
|
|
687
709
|
sheetRef(isPresented ? elementRef.current : null);
|
|
688
710
|
}, [isPresented, sheetRef]);
|
|
689
|
-
useMobileKeyboard({
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
isSheetTraveling: isTraveling,
|
|
711
|
+
const keyboard = useMobileKeyboard(elementRef, {
|
|
712
|
+
move: 'ride',
|
|
713
|
+
inset: true,
|
|
714
|
+
offset: MOBILE_KEYBOARD_BOTTOM_CLEARANCE,
|
|
715
|
+
isEnabled: height === 'tall' && isPresented,
|
|
716
|
+
isExpanded: settledOffset === 0,
|
|
696
717
|
isOpen: isInteractive,
|
|
697
|
-
|
|
698
|
-
sheetRef: elementRef,
|
|
718
|
+
isPageScrollLocked,
|
|
699
719
|
});
|
|
720
|
+
const keyboardInset = keyboard.inset;
|
|
721
|
+
// The drag's own callback dismisses the keyboard as the sheet starts to
|
|
722
|
+
// move. It runs in the gesture, so it reads the newest dismiss.
|
|
723
|
+
dismissKeyboardRef.current = keyboard.dismiss;
|
|
700
724
|
// Keep controller registration attached to one stable host ref. React
|
|
701
725
|
// detaches an old callback ref when a consumer supplies a new identity; if
|
|
702
726
|
// that public ref were merged with setElement, an ordinary parent rerender
|
|
@@ -943,6 +967,10 @@ export function BottomSheetPanel({
|
|
|
943
967
|
getContentProps<HTMLDivElement>(
|
|
944
968
|
stylex.props(
|
|
945
969
|
styles.content,
|
|
970
|
+
height === 'tall' && styles.tallKeyboardContent,
|
|
971
|
+
height === 'tall' &&
|
|
972
|
+
keyboardInset > 0 &&
|
|
973
|
+
styles.keyboardInset(keyboardInset),
|
|
946
974
|
...container(
|
|
947
975
|
usesThemePadding
|
|
948
976
|
? {useThemeDefault: 'bottom-sheet'}
|
|
@@ -29,6 +29,11 @@ export const docs = {
|
|
|
29
29
|
group: 'BottomSheet',
|
|
30
30
|
category: 'Overlay',
|
|
31
31
|
keywords: ['bottom sheet', 'switcher', 'multi-step', 'flow', 'wizard'],
|
|
32
|
+
deprecated: {
|
|
33
|
+
replacement: 'BottomSheet',
|
|
34
|
+
description:
|
|
35
|
+
'Write each step as its own BottomSheet with isOpen, controlled by one state value that names the open step; moving on from inside the open step replaces it in place (see BottomSheet, Replacing a sheet). BottomSheetSwitcher keeps working until a future minor release removes it.',
|
|
36
|
+
},
|
|
32
37
|
playground: {
|
|
33
38
|
overlay: true,
|
|
34
39
|
overlayControl: {
|
|
@@ -1881,7 +1881,7 @@ export function getBottomSheetBeneath(
|
|
|
1881
1881
|
|
|
1882
1882
|
// preventScroll on both: presenting a sheet must not scroll the page to reveal
|
|
1883
1883
|
// what it just focused. For an autofocused field that reveal is the mobile
|
|
1884
|
-
// keyboard's, and it moves the document under a fixed sheet
|
|
1884
|
+
// keyboard's, and it moves the document under a fixed sheet. useMobileKeyboard
|
|
1885
1885
|
// brings the field into view within the sheet instead.
|
|
1886
1886
|
/**
|
|
1887
1887
|
* Move focus into a sheet panel. Focus already inside the sheet stays put:
|