@astryxdesign/core 0.7.7-canary.fc68123 → 0.7.7
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 +104 -0
- package/dist/AlertDialog/AlertDialog.d.ts +1 -4
- package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog/AlertDialog.js +6 -36
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +4 -23
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +1 -3
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +1 -17
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +2 -13
- package/dist/ComplexSelector/ComplexSelector.d.ts +1 -4
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +4 -14
- package/dist/ContextMenu/ContextMenu.js +2 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +31 -7
- package/dist/Field/Field.js +2 -2
- package/dist/HoverCard/HoverCard.d.ts +3 -4
- package/dist/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard/HoverCard.js +2 -27
- package/dist/HoverCard/useHoverCard.d.ts +4 -5
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +31 -17
- package/dist/Icon/Icon.d.ts +1 -5
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +2 -5
- package/dist/Layer/usePressPin.d.ts +5 -17
- package/dist/Layer/usePressPin.d.ts.map +1 -1
- package/dist/Layer/usePressPin.js +12 -62
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +0 -18
- package/dist/PowerSearch/PowerSearch.d.ts +3 -36
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +127 -433
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts +1 -32
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +6 -17
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -38
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +61 -170
- package/dist/PowerSearch/formatFilterValue.d.ts +0 -7
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +1 -11
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +2 -3
- package/dist/RadioList/RadioListItem.d.ts +1 -1
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +3 -5
- package/dist/Resizable/ResizeHandle.d.ts +1 -2
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +77 -99
- package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -1
- package/dist/SegmentedControl/useSegmentedSelectionMotion.js +3 -3
- package/dist/Slider/sliderTouchController.js +2 -2
- package/dist/Spinner/Spinner.d.ts +1 -1
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +58 -60
- package/dist/Switch/useSwitchTouch.js +4 -4
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +8 -29
- package/dist/TextInput/TextInput.d.ts +2 -2
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +7 -10
- package/dist/Toast/Toast.d.ts +1 -11
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +15 -25
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +34 -94
- package/dist/Toast/types.d.ts +3 -9
- package/dist/Toast/types.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +1 -19
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +53 -34
- package/dist/Typeahead/TypeaheadItem.d.ts +1 -2
- package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
- package/dist/astryx.css +6 -35
- package/dist/gestures/PressTracker.d.ts.map +1 -1
- package/dist/gestures/PressTracker.js +3 -4
- package/dist/hooks/actionGroupTracker.d.ts +1 -4
- package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
- package/dist/hooks/actionGroupTracker.js +3 -13
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -1
- package/dist/hooks/useAttachment.d.ts +1 -1
- package/dist/hooks/useAttachment.js +1 -1
- package/dist/hooks/useInputStatusIcon.d.ts +1 -1
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +2 -2
- package/dist/hooks/useLongPress.d.ts +2 -6
- package/dist/hooks/useLongPress.d.ts.map +1 -1
- package/dist/hooks/useLongPress.js +15 -95
- package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +0 -6
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +0 -30
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +0 -30
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -4
- package/dist/motion/animatedLayout.d.ts.map +1 -1
- package/dist/motion/animatedLayout.js +6 -5
- package/dist/motion/animator.d.ts +14 -14
- package/dist/motion/animator.d.ts.map +1 -1
- package/dist/motion/animator.js +18 -41
- package/dist/utils/dateParser.d.ts.map +1 -1
- package/dist/utils/dateParser.js +0 -12
- package/dist/utils/focusReturn.d.ts +0 -15
- package/dist/utils/focusReturn.d.ts.map +1 -1
- package/dist/utils/focusReturn.js +0 -28
- package/dist/utils/interactionOverlay.stylex.d.ts +2 -3
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +2 -3
- package/dist/utils/pressFeedback.d.ts.map +1 -1
- package/dist/utils/pressFeedback.js +6 -19
- package/locales/en.json +0 -120
- package/locales/pseudo.json +0 -90
- package/package.json +5 -10
- package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
- package/src/AlertDialog/AlertDialog.tsx +5 -37
- package/src/BottomSheet/BottomSheetPanel.tsx +3 -37
- package/src/BottomSheet/useSheetGestures.ts +1 -3
- package/src/Button/Button.tsx +4 -26
- package/src/CheckboxInput/CheckboxInput.doc.mjs +0 -18
- package/src/CheckboxInput/CheckboxInput.tsx +9 -36
- package/src/ComplexSelector/ComplexSelector.tsx +5 -20
- package/src/ContextMenu/ContextMenu.tsx +2 -2
- package/src/Dialog/Dialog.tsx +31 -10
- package/src/Field/Field.tsx +2 -2
- package/src/HoverCard/HoverCard.doc.mjs +2 -4
- package/src/HoverCard/HoverCard.tsx +4 -35
- package/src/HoverCard/useHoverCard.doc.mjs +36 -178
- package/src/HoverCard/useHoverCard.tsx +52 -46
- package/src/Icon/Icon.doc.mjs +8 -49
- package/src/Icon/Icon.tsx +7 -15
- package/src/Layer/usePressPin.ts +15 -81
- package/src/Lightbox/Lightbox.tsx +0 -18
- package/src/PowerSearch/PowerSearch.doc.mjs +5 -54
- package/src/PowerSearch/PowerSearch.tsx +155 -609
- package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -28
- package/src/PowerSearch/PowerSearchValueEditor.tsx +38 -203
- package/src/PowerSearch/formatFilterValue.ts +1 -18
- package/src/RadioList/RadioList.tsx +2 -3
- package/src/RadioList/RadioListItem.tsx +3 -5
- package/src/Resizable/Resizable.doc.mjs +4 -5
- package/src/Resizable/ResizeHandle.tsx +109 -167
- package/src/SegmentedControl/useSegmentedSelectionMotion.ts +3 -6
- package/src/Slider/sliderTouchController.ts +2 -2
- package/src/Spinner/Spinner.tsx +63 -77
- package/src/Switch/useSwitchTouch.ts +4 -4
- package/src/Table/BaseTable.tsx +12 -46
- package/src/TextInput/TextInput.tsx +10 -11
- package/src/Toast/Toast.doc.mjs +3 -4
- package/src/Toast/Toast.tsx +32 -52
- package/src/Toast/ToastViewport.tsx +35 -121
- package/src/Toast/types.ts +3 -9
- package/src/Tooltip/useTooltip.doc.mjs +0 -24
- package/src/Tooltip/useTooltip.tsx +56 -61
- package/src/Typeahead/TypeaheadItem.doc.mjs +3 -6
- package/src/Typeahead/TypeaheadItem.tsx +1 -2
- package/src/gestures/PressTracker.ts +3 -4
- package/src/gestures/README.md +1 -7
- package/src/hooks/actionGroupTracker.ts +5 -20
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useAttachment.ts +1 -1
- package/src/hooks/useInputStatusIcon.tsx +2 -3
- package/src/hooks/useLongPress.doc.mjs +2 -2
- package/src/hooks/useLongPress.ts +15 -136
- package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +0 -6
- package/src/i18n/generated-locales/en.generated.ts +0 -41
- package/src/i18n/generated-locales/pseudo.generated.ts +0 -42
- package/src/index.ts +0 -13
- package/src/motion/README.md +5 -9
- package/src/motion/animatedLayout.ts +4 -5
- package/src/motion/animator.ts +29 -61
- package/src/utils/dateParser.ts +0 -16
- package/src/utils/focusReturn.ts +0 -30
- package/src/utils/interactionOverlay.stylex.ts +2 -3
- package/src/utils/pressFeedback.ts +6 -19
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +0 -100
- package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +0 -1
- package/dist/PowerSearch/PowerSearchBottomSheet.js +0 -1913
- package/dist/PowerSearch/formatTokenValue.d.ts +0 -28
- package/dist/PowerSearch/formatTokenValue.d.ts.map +0 -1
- package/dist/PowerSearch/formatTokenValue.js +0 -160
- package/dist/PowerSearch/powerSearchSheetUtils.d.ts +0 -65
- package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +0 -1
- package/dist/PowerSearch/powerSearchSheetUtils.js +0 -168
- package/dist/animator/createAnimator.d.ts +0 -98
- package/dist/animator/createAnimator.d.ts.map +0 -1
- package/dist/animator/createAnimator.js +0 -259
- package/dist/hooks/pressGroupContext.d.ts +0 -6
- package/dist/hooks/pressGroupContext.d.ts.map +0 -1
- package/dist/hooks/pressGroupContext.js +0 -20
- package/dist/hooks/useActionGroupTracking.d.ts +0 -53
- package/dist/hooks/useActionGroupTracking.d.ts.map +0 -1
- package/dist/hooks/useActionGroupTracking.js +0 -174
- package/dist/hooks/useAnimator.d.ts +0 -41
- package/dist/hooks/useAnimator.d.ts.map +0 -1
- package/dist/hooks/useAnimator.js +0 -70
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +0 -8
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +0 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +0 -42
- package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +0 -8
- package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +0 -1
- package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +0 -14
- package/dist/internal/indicatorPress.stylex.d.ts +0 -20
- package/dist/internal/indicatorPress.stylex.d.ts.map +0 -1
- package/dist/internal/indicatorPress.stylex.js +0 -26
- package/dist/motion/progress.d.ts +0 -88
- package/dist/motion/progress.d.ts.map +0 -1
- package/dist/motion/progress.js +0 -128
- package/dist/motion/uikit/alert.d.ts +0 -41
- package/dist/motion/uikit/alert.d.ts.map +0 -1
- package/dist/motion/uikit/alert.js +0 -43
- package/dist/privateAttributes/pressGroupItem.stylex.d.ts +0 -15
- package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +0 -1
- package/dist/privateAttributes/pressGroupItem.stylex.js +0 -23
- package/dist/utils/triggerFocus.d.ts +0 -36
- package/dist/utils/triggerFocus.d.ts.map +0 -1
- package/dist/utils/triggerFocus.js +0 -65
- package/src/PowerSearch/PowerSearchBottomSheet.tsx +0 -2515
- package/src/PowerSearch/formatTokenValue.ts +0 -215
- package/src/PowerSearch/powerSearchSheetUtils.ts +0 -224
- package/src/animator/createAnimator.doc.mjs +0 -153
- package/src/animator/createAnimator.ts +0 -324
- package/src/hooks/pressGroupContext.ts +0 -21
- package/src/hooks/useActionGroupTracking.ts +0 -224
- package/src/hooks/useAnimator.doc.mjs +0 -109
- package/src/hooks/useAnimator.ts +0 -67
- package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +0 -43
- package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +0 -15
- package/src/internal/indicatorPress.stylex.ts +0 -24
- package/src/motion/progress.ts +0 -155
- package/src/motion/uikit/alert.ts +0 -43
- package/src/privateAttributes/pressGroupItem.stylex.ts +0 -23
- package/src/utils/triggerFocus.ts +0 -80
package/src/Spinner/Spinner.tsx
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* SYNC: When modified, update these files to stay in sync:
|
|
12
12
|
* - /packages/core/src/Spinner/Spinner.doc.mjs
|
|
13
|
-
* - /packages/core/src/Spinner/Spinner.test.tsx
|
|
13
|
+
* - /packages/core/src/Spinner/Spinner.test.tsx
|
|
14
14
|
* - /packages/core/src/Spinner/index.ts
|
|
15
15
|
* - /apps/storybook/stories/Spinner.stories.tsx
|
|
16
16
|
* - /packages/cli/assets/templates/blocks/components/Spinner/ (showcase blocks)
|
|
@@ -153,57 +153,60 @@ function registerSpinnerVars(): void {
|
|
|
153
153
|
registerSpinnerVars();
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* Pin every ring's
|
|
157
|
-
* own start time, so spinners mounted seconds apart stay in phase.
|
|
156
|
+
* Pin every ring's dash motion to the document timeline's origin instead of
|
|
157
|
+
* its own start time, so spinners mounted seconds apart stay in phase.
|
|
158
158
|
*
|
|
159
|
-
* Setting `startTime` is exact where arithmetic on a clock read is not
|
|
159
|
+
* Setting `startTime` is exact where arithmetic on a clock read is not: a
|
|
160
160
|
* negative `animation-delay` computed at mount is only as good as the gap
|
|
161
|
-
* between the clock
|
|
162
|
-
*
|
|
161
|
+
* between reading the clock and the frame the animation starts in, which at
|
|
162
|
+
* 10x CPU throttling measured 116deg of drift (from when this animated a
|
|
163
|
+
* literal rotation; the same drift risk applies to the dash offset now).
|
|
163
164
|
*
|
|
164
|
-
*
|
|
165
|
-
* style
|
|
166
|
-
* each mount re-force what the previous one invalidated
|
|
167
|
-
* 38 spinners
|
|
165
|
+
* Rings are collected and pinned in one frame because `getAnimations()`
|
|
166
|
+
* resolves style and `startTime` dirties it again, so pinning them one at a
|
|
167
|
+
* time makes each mount re-force what the previous one invalidated — 53 style
|
|
168
|
+
* recalcs for 38 spinners against 19 batched.
|
|
168
169
|
*/
|
|
169
|
-
const
|
|
170
|
+
const pendingRings = new Set<SVGSVGElement>();
|
|
170
171
|
let flushScheduled = false;
|
|
171
172
|
|
|
172
|
-
function
|
|
173
|
+
function pinRingsToTimelineOrigin(): void {
|
|
173
174
|
flushScheduled = false;
|
|
174
175
|
const animations: Animation[] = [];
|
|
175
|
-
for (const
|
|
176
|
-
|
|
176
|
+
for (const svg of pendingRings) {
|
|
177
|
+
// The animation now runs on the arc <circle>, a descendant of the ring
|
|
178
|
+
// this ref sits on (#6253), so it needs subtree:true to be found here.
|
|
179
|
+
animations.push(...svg.getAnimations({subtree: true}));
|
|
177
180
|
}
|
|
178
|
-
|
|
181
|
+
pendingRings.clear();
|
|
179
182
|
for (const animation of animations) {
|
|
180
183
|
animation.startTime = 0;
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
/**
|
|
185
|
-
* Ref callback for the
|
|
188
|
+
* Ref callback for the ring: the one place a mounted ring touches the DOM.
|
|
186
189
|
*
|
|
187
190
|
* It reads nothing back — the geometry is resolved by the cascade, not in JS.
|
|
188
191
|
*/
|
|
189
192
|
function syncRotationPhase(
|
|
190
|
-
|
|
193
|
+
svg: SVGSVGElement | null,
|
|
191
194
|
): (() => void) | undefined {
|
|
192
|
-
if (
|
|
195
|
+
if (svg == null) {
|
|
193
196
|
return undefined;
|
|
194
197
|
}
|
|
195
198
|
// jsdom implements no Web Animations, and this runs in every consumer's
|
|
196
199
|
// component tests.
|
|
197
|
-
if (typeof
|
|
200
|
+
if (typeof svg.getAnimations !== 'function') {
|
|
198
201
|
return undefined;
|
|
199
202
|
}
|
|
200
|
-
|
|
203
|
+
pendingRings.add(svg);
|
|
201
204
|
if (!flushScheduled) {
|
|
202
205
|
flushScheduled = true;
|
|
203
|
-
requestAnimationFrame(
|
|
206
|
+
requestAnimationFrame(pinRingsToTimelineOrigin);
|
|
204
207
|
}
|
|
205
208
|
return () => {
|
|
206
|
-
|
|
209
|
+
pendingRings.delete(svg);
|
|
207
210
|
};
|
|
208
211
|
}
|
|
209
212
|
|
|
@@ -212,13 +215,19 @@ function syncRotationPhase(
|
|
|
212
215
|
// =============================================================================
|
|
213
216
|
|
|
214
217
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
+
* Moves the dash around a stationary circle instead of rotating the whole
|
|
219
|
+
* ring. WebKit re-antialiases a rotating stroked shape's rounded cap
|
|
220
|
+
* (`strokeLinecap: 'round'`, below) slightly differently at each intermediate
|
|
221
|
+
* angle, which reads as a visible wobble on iOS Safari independent of
|
|
222
|
+
* compositing (#6253) — confirmed by a real-device test against this exact
|
|
223
|
+
* fix, since `willChange`/layer promotion alone (the earlier fix for #3617)
|
|
224
|
+
* only smooths the rotation's motion, not the per-frame stroke rasterization.
|
|
225
|
+
* A static circle with an animated `stroke-dashoffset` never rotates, so
|
|
226
|
+
* WebKit never re-rasterizes the cap at an intermediate angle at all.
|
|
218
227
|
*/
|
|
219
|
-
const
|
|
220
|
-
'0%': {
|
|
221
|
-
'100%': {
|
|
228
|
+
const dashMotion = stylex.keyframes({
|
|
229
|
+
'0%': {strokeDashoffset: '0px'},
|
|
230
|
+
'100%': {strokeDashoffset: `calc(var(${RESOLVED_DIAMETER}) * -${PI})`},
|
|
222
231
|
});
|
|
223
232
|
|
|
224
233
|
// =============================================================================
|
|
@@ -274,8 +283,6 @@ const styles = stylex.create({
|
|
|
274
283
|
ring: {
|
|
275
284
|
backfaceVisibility: 'hidden',
|
|
276
285
|
display: 'block',
|
|
277
|
-
// The track and the rotor share the one grid cell, stacked and centred.
|
|
278
|
-
gridArea: '1 / 1',
|
|
279
286
|
// The frame is the box, read from the same composed var the span is sized
|
|
280
287
|
// from — not from the size constant, which a theme cannot reach, and not
|
|
281
288
|
// from a percentage, which needs the grid area to be a definite size in
|
|
@@ -293,27 +300,6 @@ const styles = stylex.create({
|
|
|
293
300
|
// which put the ring 1.5-3.3px off across the four sizes.
|
|
294
301
|
overflow: 'visible',
|
|
295
302
|
},
|
|
296
|
-
// The rotor is the frame's size, so it turns about the ring's centre.
|
|
297
|
-
rotor: {
|
|
298
|
-
display: 'block',
|
|
299
|
-
gridArea: '1 / 1',
|
|
300
|
-
width: `var(${BOX_SIZE})`,
|
|
301
|
-
height: `var(${BOX_SIZE})`,
|
|
302
|
-
// Mobile Safari keeps a re-timed animation off the main thread only on an
|
|
303
|
-
// element with its own layer. So the rotor declares one before the start
|
|
304
|
-
// time is set (see `pinRotorsToTimelineOrigin`).
|
|
305
|
-
willChange: 'transform',
|
|
306
|
-
// Reduced motion slows the turn but never stops it: a still ring reads as
|
|
307
|
-
// broken. The status role and its name still say that work is busy.
|
|
308
|
-
animationDuration: {
|
|
309
|
-
// The loop reads the duration before a theme's tempo scales it.
|
|
310
|
-
default: `var(--_astryx-pre-tempo-duration-slow-min, ${durationVars['--duration-slow-min']})`,
|
|
311
|
-
'@media (prefers-reduced-motion: reduce)': '3s',
|
|
312
|
-
},
|
|
313
|
-
animationIterationCount: 'infinite',
|
|
314
|
-
animationName: rotation,
|
|
315
|
-
animationTimingFunction: 'linear',
|
|
316
|
-
},
|
|
317
303
|
circle: {
|
|
318
304
|
fill: 'none',
|
|
319
305
|
// The ring is centred on the box (cx/cy 50%), so the arc's start offset
|
|
@@ -350,6 +336,18 @@ const styles = stylex.create({
|
|
|
350
336
|
// `0` the way the registered length pair guards against, so it needs no
|
|
351
337
|
// registration.
|
|
352
338
|
arc: {
|
|
339
|
+
// Slow the motion dramatically under reduced-motion rather than freezing
|
|
340
|
+
// it (a frozen spinner reads as broken), matching ProgressBar's approach.
|
|
341
|
+
// The role="status" + "Loading" label still convey busy state (obs-6).
|
|
342
|
+
animationDuration: {
|
|
343
|
+
// The loop reads the duration before a theme's tempo scales it
|
|
344
|
+
// (spec:AST-108 FR4).
|
|
345
|
+
default: `var(--_astryx-pre-tempo-duration-slow-min, ${durationVars['--duration-slow-min']})`,
|
|
346
|
+
'@media (prefers-reduced-motion: reduce)': '3s',
|
|
347
|
+
},
|
|
348
|
+
animationIterationCount: 'infinite',
|
|
349
|
+
animationName: dashMotion,
|
|
350
|
+
animationTimingFunction: 'linear',
|
|
353
351
|
stroke: 'var(--spinner-color)',
|
|
354
352
|
transform: 'rotate(-90deg)',
|
|
355
353
|
strokeDasharray: `calc(var(${RESOLVED_DIAMETER}) * ${PI} * var(--spinner-arc-fraction)) calc(var(${RESOLVED_DIAMETER}) * ${PI} * (1 - var(--spinner-arc-fraction)))`,
|
|
@@ -571,12 +569,10 @@ export function Spinner({
|
|
|
571
569
|
height: `var(${BOX_SIZE}, ${frameSize}px)`,
|
|
572
570
|
},
|
|
573
571
|
)}>
|
|
574
|
-
{/* The track is zero-sized by attribute. With no stylesheet, the
|
|
575
|
-
rotor's arc alone fills the box, instead of two discs side by side.
|
|
576
|
-
`ring` sizes both to the box. */}
|
|
577
572
|
<svg
|
|
578
|
-
|
|
579
|
-
|
|
573
|
+
ref={syncRotationPhase}
|
|
574
|
+
width={frameSize}
|
|
575
|
+
height={frameSize}
|
|
580
576
|
aria-hidden="true"
|
|
581
577
|
{...stylex.props(styles.ring)}>
|
|
582
578
|
<circle
|
|
@@ -590,28 +586,18 @@ export function Spinner({
|
|
|
590
586
|
trackOpacityStyles[shade],
|
|
591
587
|
)}
|
|
592
588
|
/>
|
|
589
|
+
<circle
|
|
590
|
+
cx="50%"
|
|
591
|
+
cy="50%"
|
|
592
|
+
r={diameter / 2}
|
|
593
|
+
strokeWidth={border}
|
|
594
|
+
// The size's own dash, for the render with no stylesheet. The rule
|
|
595
|
+
// above composes the same lengths from the resolved diameter, so a
|
|
596
|
+
// themed ring keeps this fraction of arc rather than this length.
|
|
597
|
+
strokeDasharray={`${arcLength} ${circumference - arcLength}`}
|
|
598
|
+
{...stylex.props(styles.circle, styles.arc)}
|
|
599
|
+
/>
|
|
593
600
|
</svg>
|
|
594
|
-
<span
|
|
595
|
-
ref={syncRotationPhase}
|
|
596
|
-
aria-hidden="true"
|
|
597
|
-
{...stylex.props(styles.rotor)}>
|
|
598
|
-
<svg
|
|
599
|
-
width={frameSize}
|
|
600
|
-
height={frameSize}
|
|
601
|
-
{...stylex.props(styles.ring)}>
|
|
602
|
-
<circle
|
|
603
|
-
cx="50%"
|
|
604
|
-
cy="50%"
|
|
605
|
-
r={diameter / 2}
|
|
606
|
-
strokeWidth={border}
|
|
607
|
-
// The size's own dash, for the render with no stylesheet. The rule
|
|
608
|
-
// above composes the same lengths from the resolved diameter, so a
|
|
609
|
-
// themed ring keeps this fraction of arc rather than this length.
|
|
610
|
-
strokeDasharray={`${arcLength} ${circumference - arcLength}`}
|
|
611
|
-
{...stylex.props(styles.circle, styles.arc)}
|
|
612
|
-
/>
|
|
613
|
-
</svg>
|
|
614
|
-
</span>
|
|
615
601
|
</span>
|
|
616
602
|
);
|
|
617
603
|
|
|
@@ -23,7 +23,7 @@ import type {GestureTouch} from '../gestures/types';
|
|
|
23
23
|
import {isRtlElement} from '../hooks/isRtlElement';
|
|
24
24
|
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
25
25
|
import {
|
|
26
|
-
|
|
26
|
+
createAnimator,
|
|
27
27
|
type Animator,
|
|
28
28
|
type MotionCurve,
|
|
29
29
|
} from '../motion/animator';
|
|
@@ -282,15 +282,15 @@ export class SwitchTouchController {
|
|
|
282
282
|
return;
|
|
283
283
|
}
|
|
284
284
|
registerSwitchTable();
|
|
285
|
-
this.thumb =
|
|
285
|
+
this.thumb = createAnimator(elements.thumb);
|
|
286
286
|
// The layer rests clear (its class's opacity 0) while the visuals show
|
|
287
287
|
// the value; from here its opacity is the animator's.
|
|
288
|
-
this.fade =
|
|
288
|
+
this.fade = createAnimator(elements.track, {
|
|
289
289
|
pseudoElement: '::after',
|
|
290
290
|
initial: {opacity: 0},
|
|
291
291
|
});
|
|
292
292
|
this.fade.set({opacity: 0});
|
|
293
|
-
this.shadow =
|
|
293
|
+
this.shadow = createAnimator(elements.thumb, {
|
|
294
294
|
pseudoElement: '::after',
|
|
295
295
|
initial: {opacity: 0},
|
|
296
296
|
});
|
package/src/Table/BaseTable.tsx
CHANGED
|
@@ -14,14 +14,7 @@
|
|
|
14
14
|
* - /packages/cli/assets/templates/blocks/components/Table/ (showcase blocks)
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
memo,
|
|
19
|
-
useId,
|
|
20
|
-
useRef,
|
|
21
|
-
type ReactElement,
|
|
22
|
-
type ReactNode,
|
|
23
|
-
type Ref,
|
|
24
|
-
} from 'react';
|
|
17
|
+
import {memo, useRef, type ReactElement, type ReactNode, type Ref} from 'react';
|
|
25
18
|
import {isRenderable} from '../utils/isRenderable';
|
|
26
19
|
import * as stylex from '@stylexjs/stylex';
|
|
27
20
|
import {spacingVars} from '../theme/tokens.stylex';
|
|
@@ -430,7 +423,6 @@ function BaseTableInner<T extends Record<string, unknown>>({
|
|
|
430
423
|
} satisfies TableRenderProps);
|
|
431
424
|
|
|
432
425
|
// --- Plugin pipeline: header cells ---
|
|
433
|
-
const headerLabelIdBase = useId();
|
|
434
426
|
const headerCells = resolvedColumns.map((col, columnIndex) => {
|
|
435
427
|
const headerContent = col.header ?? col.key;
|
|
436
428
|
|
|
@@ -485,54 +477,28 @@ function BaseTableInner<T extends Record<string, unknown>>({
|
|
|
485
477
|
const hasSlots =
|
|
486
478
|
before != null || after != null || overlay != null || below != null;
|
|
487
479
|
|
|
488
|
-
const
|
|
489
|
-
<div {...stylex.props(styles.headerLabelRow)}>
|
|
490
|
-
{resolvedContent}
|
|
491
|
-
{after}
|
|
492
|
-
</div>
|
|
493
|
-
) : (
|
|
494
|
-
resolvedContent
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
// The overlay is a control of its own. Keep its name and value out of the
|
|
498
|
-
// header's name.
|
|
499
|
-
const namesOwnHeader =
|
|
500
|
-
isRenderable(overlay) &&
|
|
501
|
-
mergedHtmlProps['aria-label'] == null &&
|
|
502
|
-
mergedHtmlProps['aria-labelledby'] == null;
|
|
503
|
-
const labelId = `${headerLabelIdBase}-label-${columnIndex}`;
|
|
504
|
-
const belowId = `${headerLabelIdBase}-below-${columnIndex}`;
|
|
505
|
-
const hasBelow = isRenderable(below);
|
|
506
|
-
|
|
507
|
-
const headerInner = !hasSlots ? (
|
|
508
|
-
resolvedContent
|
|
509
|
-
) : namesOwnHeader ? (
|
|
510
|
-
<>
|
|
511
|
-
{/* Inline, so the cell's text-overflow still reaches the label. */}
|
|
512
|
-
<span id={labelId}>
|
|
513
|
-
{before}
|
|
514
|
-
{labelRow}
|
|
515
|
-
</span>
|
|
516
|
-
{overlay}
|
|
517
|
-
{hasBelow && <div id={belowId}>{below}</div>}
|
|
518
|
-
</>
|
|
519
|
-
) : (
|
|
480
|
+
const headerInner = hasSlots ? (
|
|
520
481
|
<>
|
|
521
482
|
{before}
|
|
522
|
-
{
|
|
483
|
+
{isRenderable(after) ? (
|
|
484
|
+
<div {...stylex.props(styles.headerLabelRow)}>
|
|
485
|
+
{resolvedContent}
|
|
486
|
+
{after}
|
|
487
|
+
</div>
|
|
488
|
+
) : (
|
|
489
|
+
resolvedContent
|
|
490
|
+
)}
|
|
523
491
|
{overlay}
|
|
524
492
|
{below}
|
|
525
493
|
</>
|
|
494
|
+
) : (
|
|
495
|
+
resolvedContent
|
|
526
496
|
);
|
|
527
|
-
const headerNameProps = namesOwnHeader
|
|
528
|
-
? {'aria-labelledby': hasBelow ? `${labelId} ${belowId}` : labelId}
|
|
529
|
-
: {};
|
|
530
497
|
|
|
531
498
|
return (
|
|
532
499
|
<HeaderCellComponent
|
|
533
500
|
key={col.key}
|
|
534
501
|
{...mergedHtmlProps}
|
|
535
|
-
{...headerNameProps}
|
|
536
502
|
{...headerTitleProp}
|
|
537
503
|
contextMenuActions={cellRenderProps.contextMenuActions}
|
|
538
504
|
xstyle={cellRenderProps.xstyle}>
|
|
@@ -35,22 +35,21 @@ import {
|
|
|
35
35
|
typographyVars,
|
|
36
36
|
typeScaleVars,
|
|
37
37
|
} from '../theme/tokens.stylex';
|
|
38
|
-
import {Field} from '../Field/Field';
|
|
39
|
-
import {InputClearButton} from '../Field/InputClearButton';
|
|
40
38
|
import {
|
|
39
|
+
Field,
|
|
40
|
+
InputClearButton,
|
|
41
|
+
type InputStatus,
|
|
41
42
|
inputWrapperStyles,
|
|
42
43
|
inputStatusBorderStyles,
|
|
43
44
|
inputStatusHoverShadowStyles,
|
|
44
45
|
inputStatusFocusWithinStyles,
|
|
45
|
-
} from '../Field
|
|
46
|
-
import type
|
|
47
|
-
import {renderIconSlot, type IconType} from '../Icon/Icon';
|
|
46
|
+
} from '../Field';
|
|
47
|
+
import {renderIconSlot, type IconType} from '../Icon';
|
|
48
48
|
import {isRenderable} from '../utils/isRenderable';
|
|
49
|
-
import {Spinner} from '../Spinner
|
|
50
|
-
import {useTooltip} from '../Tooltip
|
|
51
|
-
import {VisuallyHidden} from '../VisuallyHidden
|
|
52
|
-
import {getInputARIA} from '../utils
|
|
53
|
-
import {isImeKeyEvent} from '../utils/ime';
|
|
49
|
+
import {Spinner} from '../Spinner';
|
|
50
|
+
import {useTooltip} from '../Tooltip';
|
|
51
|
+
import {VisuallyHidden} from '../VisuallyHidden';
|
|
52
|
+
import {getInputARIA, isImeKeyEvent} from '../utils';
|
|
54
53
|
import {
|
|
55
54
|
textEntryTraitAttributes,
|
|
56
55
|
type TextEntryTraitProps,
|
|
@@ -110,7 +109,7 @@ export type {
|
|
|
110
109
|
InputStatus as TextInputStatus,
|
|
111
110
|
InputStatusType as TextInputStatusType,
|
|
112
111
|
} from '../Field';
|
|
113
|
-
import {mergeProps} from '../utils
|
|
112
|
+
import {mergeProps} from '../utils';
|
|
114
113
|
import {useSize} from '../SizeContext/SizeContext';
|
|
115
114
|
import {useInputContainer} from '../hooks/useInputContainer';
|
|
116
115
|
import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
|
package/src/Toast/Toast.doc.mjs
CHANGED
|
@@ -89,7 +89,7 @@ export const docs = {
|
|
|
89
89
|
name: 'renderContent',
|
|
90
90
|
type: '(toast: ToastContentRenderProps) => ReactNode',
|
|
91
91
|
description:
|
|
92
|
-
"Replaces the content of this toast's card with your own layout. Astryx keeps the card, its astryx-toast theme target and auto-hide behavior
|
|
92
|
+
"Replaces the content of this toast's card with your own layout. Astryx keeps the card, its astryx-toast theme target, the live-region role and auto-hide behavior, then hands the renderer the message, endContent, resolved toast settings and a dismiss callback. The custom renderer owns every control in its layout: compose the control you want and call dismiss from it. Astryx does not inject a fallback close into custom content. Per-toast: an app shares one layout by wrapping useToast and passing it on every call, while a toast raised by library code that never passes it renders as an ordinary Astryx toast. The argument is {body, endContent, type, isAutoHide, autoHideDuration, dismiss}, where type is 'info' | 'error'.",
|
|
93
93
|
},
|
|
94
94
|
],
|
|
95
95
|
theming: {
|
|
@@ -211,7 +211,7 @@ export const docsZh = {
|
|
|
211
211
|
collisionBehavior: '当已存在相同 uniqueID 的 toast 时的行为。',
|
|
212
212
|
onHide: '当 toast 被移除时触发的回调。',
|
|
213
213
|
renderContent:
|
|
214
|
-
"用你自己的布局替换该 toast 卡片内部的内容。Astryx 保留卡片本身、astryx-toast
|
|
214
|
+
"用你自己的布局替换该 toast 卡片内部的内容。Astryx 保留卡片本身、astryx-toast 主题目标、实时区域角色和自动关闭行为,并把消息、endContent、解析后的 toast 设置和 dismiss 回调交给渲染函数。自定义渲染函数完全拥有布局中的控件:请组合所需的控件并通过它调用 dismiss。Astryx 不会向自定义内容注入后备关闭按钮。按 toast 单独设置:应用可通过封装 useToast 并在每次调用时传入来共享同一套布局;而由库代码发起、从不传入该参数的 toast 会渲染为普通的 Astryx toast。传入参数为 {body, endContent, type, isAutoHide, autoHideDuration, dismiss},其中 type 为 'info' | 'error'。",
|
|
215
215
|
},
|
|
216
216
|
usage: {
|
|
217
217
|
description:
|
|
@@ -328,7 +328,6 @@ export const docsDense = {
|
|
|
328
328
|
uniqueID: 'unique id for dedup',
|
|
329
329
|
collisionBehavior: 'behavior when matching uniqueID exists',
|
|
330
330
|
onHide: 'callback when toast removed',
|
|
331
|
-
renderContent:
|
|
332
|
-
'custom inner layout; receives content, resolved settings + dismiss callback; no injected fallback control',
|
|
331
|
+
renderContent: 'custom inner layout; receives content, resolved settings + dismiss callback; no injected fallback control',
|
|
333
332
|
},
|
|
334
333
|
};
|
package/src/Toast/Toast.tsx
CHANGED
|
@@ -154,16 +154,6 @@ export interface ToastProps {
|
|
|
154
154
|
|
|
155
155
|
interface ToastSurfaceProps extends ToastProps {
|
|
156
156
|
gestureDirection: ToastGestureDirection;
|
|
157
|
-
/**
|
|
158
|
-
* Changes when a uniqueID overwrite updates this card in place: the new
|
|
159
|
-
* toast's auto-hide timer starts over.
|
|
160
|
-
*/
|
|
161
|
-
timerKey?: string;
|
|
162
|
-
/**
|
|
163
|
-
* False when the viewport announces the toast: the card then carries no
|
|
164
|
-
* live region of its own, so an update in place is announced once.
|
|
165
|
-
*/
|
|
166
|
-
isLiveRegion?: boolean;
|
|
167
157
|
}
|
|
168
158
|
|
|
169
159
|
/**
|
|
@@ -203,8 +193,6 @@ export function ToastSurface({
|
|
|
203
193
|
onDismiss,
|
|
204
194
|
renderContent,
|
|
205
195
|
gestureDirection,
|
|
206
|
-
timerKey,
|
|
207
|
-
isLiveRegion = true,
|
|
208
196
|
}: ToastSurfaceProps) {
|
|
209
197
|
const t = useComponentTranslator();
|
|
210
198
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -238,28 +226,34 @@ export function ToastSurface({
|
|
|
238
226
|
}, remainingRef.current);
|
|
239
227
|
}, [isAutoHide]);
|
|
240
228
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
229
|
+
const pauseTimer = useCallback(
|
|
230
|
+
(source: PauseSource) => {
|
|
231
|
+
if (!isAutoHide) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const sources = pauseSourcesRef.current;
|
|
235
|
+
const wasRunning = sources.size === 0;
|
|
236
|
+
sources.add(source);
|
|
237
|
+
if (!wasRunning) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (timerRef.current) {
|
|
241
|
+
clearTimeout(timerRef.current);
|
|
242
|
+
timerRef.current = null;
|
|
243
|
+
}
|
|
244
|
+
if (startTimeRef.current != null) {
|
|
245
|
+
const elapsed = Date.now() - startTimeRef.current;
|
|
246
|
+
remainingRef.current = Math.max(remainingRef.current - elapsed, 1000);
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
[isAutoHide],
|
|
250
|
+
);
|
|
260
251
|
|
|
261
252
|
const resumeTimer = useCallback(
|
|
262
253
|
(source: PauseSource) => {
|
|
254
|
+
if (!isAutoHide) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
263
257
|
const sources = pauseSourcesRef.current;
|
|
264
258
|
if (!sources.delete(source)) {
|
|
265
259
|
return;
|
|
@@ -277,31 +271,20 @@ export function ToastSurface({
|
|
|
277
271
|
}
|
|
278
272
|
startTimer();
|
|
279
273
|
},
|
|
280
|
-
[startTimer],
|
|
274
|
+
[isAutoHide, startTimer],
|
|
281
275
|
);
|
|
282
276
|
|
|
283
277
|
useEffect(() => {
|
|
284
278
|
remainingRef.current = autoHideDuration;
|
|
285
|
-
// An update in place can remove the focused control, which fires no blur:
|
|
286
|
-
// a focus hold whose focus has left the card is stale.
|
|
287
|
-
const root = cardRootRef.current?.current;
|
|
288
|
-
if (
|
|
289
|
-
pauseSourcesRef.current.has('focus') &&
|
|
290
|
-
root != null &&
|
|
291
|
-
!root.contains(root.ownerDocument.activeElement)
|
|
292
|
-
) {
|
|
293
|
-
pauseSourcesRef.current.delete('focus');
|
|
294
|
-
}
|
|
295
279
|
startTimer();
|
|
296
280
|
return () => {
|
|
297
281
|
if (timerRef.current) {
|
|
298
282
|
clearTimeout(timerRef.current);
|
|
299
283
|
}
|
|
300
284
|
};
|
|
301
|
-
// startTimer's identity is stable per isAutoHide, so this runs on mount
|
|
302
|
-
// on a genuine duration change
|
|
303
|
-
|
|
304
|
-
}, [autoHideDuration, startTimer, timerKey]);
|
|
285
|
+
// startTimer's identity is stable per isAutoHide, so this runs on mount
|
|
286
|
+
// and on a genuine duration change — not on unrelated viewport renders.
|
|
287
|
+
}, [autoHideDuration, startTimer]);
|
|
305
288
|
|
|
306
289
|
// Pause the auto-hide timer while the window is not focused, so a toast
|
|
307
290
|
// doesn't silently expire while the user is in another window or tab.
|
|
@@ -309,7 +292,6 @@ export function ToastSurface({
|
|
|
309
292
|
if (!isAutoHide) {
|
|
310
293
|
return;
|
|
311
294
|
}
|
|
312
|
-
const pauseSources = pauseSourcesRef.current;
|
|
313
295
|
const pauseForWindow = () => pauseTimer('window');
|
|
314
296
|
const resumeForWindow = () => resumeTimer('window');
|
|
315
297
|
window.addEventListener('blur', pauseForWindow);
|
|
@@ -322,8 +304,6 @@ export function ToastSurface({
|
|
|
322
304
|
return () => {
|
|
323
305
|
window.removeEventListener('blur', pauseForWindow);
|
|
324
306
|
window.removeEventListener('focus', resumeForWindow);
|
|
325
|
-
// With the listeners gone, no focus event would clear the window hold.
|
|
326
|
-
pauseSources.delete('window');
|
|
327
307
|
};
|
|
328
308
|
}, [isAutoHide, pauseTimer, resumeTimer]);
|
|
329
309
|
|
|
@@ -388,9 +368,9 @@ export function ToastSurface({
|
|
|
388
368
|
return (
|
|
389
369
|
<div
|
|
390
370
|
ref={rootRef}
|
|
391
|
-
role={
|
|
392
|
-
aria-live={
|
|
393
|
-
aria-atomic=
|
|
371
|
+
role={isError ? 'alert' : 'status'}
|
|
372
|
+
aria-live={isError ? 'assertive' : 'polite'}
|
|
373
|
+
aria-atomic="true"
|
|
394
374
|
onMouseEnter={pauseForPointer}
|
|
395
375
|
onMouseLeave={resumeForPointer}
|
|
396
376
|
onFocusCapture={pauseForFocus}
|