@astryxdesign/core 0.7.7-canary.d111827 → 0.7.7-canary.eadd175
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonthScroller.d.ts","sourceRoot":"","sources":["../../src/DateInput/MonthScroller.tsx"],"names":[],"mappings":"AAqDA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EAOf,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"MonthScroller.d.ts","sourceRoot":"","sources":["../../src/DateInput/MonthScroller.tsx"],"names":[],"mappings":"AAqDA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EAOf,MAAM,UAAU,CAAC;AAiNlB,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACxD,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,oBAAoB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,+BAA+B;IAC/B,YAAY,EAAE,SAAS,GAAG,IAAI,CAAC;IAC/B,4CAA4C;IAC5C,KAAK,EAAE,SAAS,CAAC;IACjB,2DAA2D;IAC3D,cAAc,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC;IAC7C,gCAAgC;IAChC,YAAY,EAAE,SAAS,CAAC;IACxB,kCAAkC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC1C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,EAAE,kBAAkB,+BAmjBpB;yBA9jBe,aAAa"}
|
|
@@ -38,6 +38,7 @@ import { colorVars, radiusVars, fontWeightVars, typeScaleVars, durationVars, bor
|
|
|
38
38
|
import { focusOutlineStyles } from "../utils/index.js";
|
|
39
39
|
import { plainDateFormatStandaloneMonthYear } from "../utils/plainDate.js";
|
|
40
40
|
import { plainDateAddDays, plainDateDayOfWeek, plainDateFormat, plainDateIsEqual, plainDateToISO, DATE_FORMAT_WITH_WEEKDAY } from "../utils/index.js";
|
|
41
|
+
import { reducedMotionFade } from "../motion/reducedMotion.js";
|
|
41
42
|
import "./tokens.stylex.js";
|
|
42
43
|
import { dateInputTouchSizes, dateInputTouchGeometry } from "./tokens.stylex.js";
|
|
43
44
|
import { useOwnScrollGesture } from "./useOwnScrollGesture.js";
|
|
@@ -251,7 +252,25 @@ export function MonthScroller({
|
|
|
251
252
|
hasPositionedRef.current = true;
|
|
252
253
|
scroller.scrollLeft = scrollOffsetForRow(initialMonthIndex - minMonthIndex, paneSize, isRTL);
|
|
253
254
|
}, [paneSize, initialMonthIndex, minMonthIndex, isRTL]);
|
|
255
|
+
|
|
256
|
+
// The Reduced Motion dip of a step on a touch screen, owned here so a
|
|
257
|
+
// second step or the unmount clears it.
|
|
258
|
+
const dipRef = useRef(null);
|
|
259
|
+
const stopDip = useCallback(() => {
|
|
260
|
+
const dip = dipRef.current;
|
|
261
|
+
if (dip != null) {
|
|
262
|
+
window.clearTimeout(dip.timer);
|
|
263
|
+
dip.animation.cancel();
|
|
264
|
+
dipRef.current = null;
|
|
265
|
+
}
|
|
266
|
+
}, []);
|
|
267
|
+
// Synchronizes with the dip's timer and animation: neither outlives the
|
|
268
|
+
// scroller.
|
|
269
|
+
useEffect(() => stopDip, [stopDip]);
|
|
254
270
|
const scrollToMonth = useCallback((monthIndex, behavior = 'smooth') => {
|
|
271
|
+
// Every step replaces a running dip, whichever way it moves. Otherwise
|
|
272
|
+
// the dip's landing would undo a jump or an immediate step made after it.
|
|
273
|
+
stopDip();
|
|
255
274
|
const scroller = scrollerRef.current;
|
|
256
275
|
if (scroller == null || paneSize === 0) {
|
|
257
276
|
return;
|
|
@@ -274,12 +293,63 @@ export function MonthScroller({
|
|
|
274
293
|
recenter(scroller, row);
|
|
275
294
|
return;
|
|
276
295
|
}
|
|
296
|
+
const left = scrollOffsetForRow(row, paneSize, isRTL);
|
|
297
|
+
// Under reduced motion the month changes without travel: at once on a fine
|
|
298
|
+
// primary pointer, under an opacity dip on a coarse one (reducedMotionFade).
|
|
299
|
+
const fade = behavior === 'smooth' ? reducedMotionFade(scroller) : null;
|
|
300
|
+
if (fade != null) {
|
|
301
|
+
// The window mounts around the target first, and the scroll lands in
|
|
302
|
+
// the layout effect below, as a far jump does. So nothing moves the
|
|
303
|
+
// window after the jump. If the window followed the jump, WebKit would
|
|
304
|
+
// re-snap it one pane further on every frame. A scroll that lands on
|
|
305
|
+
// the centre row reports nothing.
|
|
306
|
+
const land = () => {
|
|
307
|
+
// The landing is the newest steer. An earlier steer whose scroll
|
|
308
|
+
// never arrived would otherwise hide every scroll after it, a
|
|
309
|
+
// trackpad's included. Closing the wheels makes such a steer: it
|
|
310
|
+
// re-asserts the month already in view.
|
|
311
|
+
steeredRowRef.current = null;
|
|
312
|
+
if (row === centerRowRef.current) {
|
|
313
|
+
scroller.scrollTo({
|
|
314
|
+
left,
|
|
315
|
+
behavior: 'auto'
|
|
316
|
+
});
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
pendingScrollRef.current = row;
|
|
320
|
+
centerRowRef.current = row;
|
|
321
|
+
setCenterRow(row);
|
|
322
|
+
};
|
|
323
|
+
if (fade.duration === 0) {
|
|
324
|
+
land();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
// A second step within the dip has replaced the first (above): only
|
|
328
|
+
// its scroll lands, at its own dip's middle.
|
|
329
|
+
dipRef.current = {
|
|
330
|
+
animation: scroller.animate([{
|
|
331
|
+
opacity: 1
|
|
332
|
+
}, {
|
|
333
|
+
opacity: 0
|
|
334
|
+
}, {
|
|
335
|
+
opacity: 1
|
|
336
|
+
}], {
|
|
337
|
+
duration: fade.duration,
|
|
338
|
+
easing: fade.easing
|
|
339
|
+
}),
|
|
340
|
+
timer: window.setTimeout(() => {
|
|
341
|
+
dipRef.current = null;
|
|
342
|
+
land();
|
|
343
|
+
}, fade.duration / 2)
|
|
344
|
+
};
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
277
347
|
steeredRowRef.current = row;
|
|
278
348
|
scroller.scrollTo({
|
|
279
|
-
left
|
|
349
|
+
left,
|
|
280
350
|
behavior
|
|
281
351
|
});
|
|
282
|
-
}, [paneSize, minMonthIndex, rowCount, isRTL, recenter]);
|
|
352
|
+
}, [paneSize, minMonthIndex, rowCount, isRTL, recenter, stopDip]);
|
|
283
353
|
|
|
284
354
|
// Deliberately un-keyed: it runs after every render, costs a null check, and
|
|
285
355
|
// must fire on whichever render finally mounts the pending row's pane.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.7-canary.
|
|
3
|
+
"version": "0.7.7-canary.eadd175",
|
|
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",
|
|
@@ -860,9 +860,9 @@
|
|
|
860
860
|
"react-dom": ">=19.0.0"
|
|
861
861
|
},
|
|
862
862
|
"devDependencies": {
|
|
863
|
-
"@astryxdesign/a11y-spec": "0.7.7-canary.
|
|
864
|
-
"@astryxdesign/cli": "0.7.7-canary.
|
|
865
|
-
"@astryxdesign/interaction-harness": "0.7.7-canary.
|
|
863
|
+
"@astryxdesign/a11y-spec": "0.7.7-canary.eadd175",
|
|
864
|
+
"@astryxdesign/cli": "0.7.7-canary.eadd175",
|
|
865
|
+
"@astryxdesign/interaction-harness": "0.7.7-canary.eadd175",
|
|
866
866
|
"@babel/cli": "^7.29.7",
|
|
867
867
|
"@babel/core": "^7.29.7",
|
|
868
868
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
plainDateToISO,
|
|
63
63
|
DATE_FORMAT_WITH_WEEKDAY,
|
|
64
64
|
} from '../utils';
|
|
65
|
+
import {reducedMotionFade} from '../motion/reducedMotion';
|
|
65
66
|
import {dateInputTouchSizes, dateInputTouchGeometry} from './tokens.stylex';
|
|
66
67
|
import {useOwnScrollGesture} from './useOwnScrollGesture';
|
|
67
68
|
import {useScrollSettle} from './useScrollSettle';
|
|
@@ -431,8 +432,26 @@ export function MonthScroller({
|
|
|
431
432
|
);
|
|
432
433
|
}, [paneSize, initialMonthIndex, minMonthIndex, isRTL]);
|
|
433
434
|
|
|
435
|
+
// The Reduced Motion dip of a step on a touch screen, owned here so a
|
|
436
|
+
// second step or the unmount clears it.
|
|
437
|
+
const dipRef = useRef<{animation: Animation; timer: number} | null>(null);
|
|
438
|
+
const stopDip = useCallback(() => {
|
|
439
|
+
const dip = dipRef.current;
|
|
440
|
+
if (dip != null) {
|
|
441
|
+
window.clearTimeout(dip.timer);
|
|
442
|
+
dip.animation.cancel();
|
|
443
|
+
dipRef.current = null;
|
|
444
|
+
}
|
|
445
|
+
}, []);
|
|
446
|
+
// Synchronizes with the dip's timer and animation: neither outlives the
|
|
447
|
+
// scroller.
|
|
448
|
+
useEffect(() => stopDip, [stopDip]);
|
|
449
|
+
|
|
434
450
|
const scrollToMonth = useCallback(
|
|
435
451
|
(monthIndex: number, behavior: ScrollBehavior = 'smooth') => {
|
|
452
|
+
// Every step replaces a running dip, whichever way it moves. Otherwise
|
|
453
|
+
// the dip's landing would undo a jump or an immediate step made after it.
|
|
454
|
+
stopDip();
|
|
436
455
|
const scroller = scrollerRef.current;
|
|
437
456
|
if (scroller == null || paneSize === 0) {
|
|
438
457
|
return;
|
|
@@ -458,13 +477,52 @@ export function MonthScroller({
|
|
|
458
477
|
recenter(scroller, row);
|
|
459
478
|
return;
|
|
460
479
|
}
|
|
480
|
+
const left = scrollOffsetForRow(row, paneSize, isRTL);
|
|
481
|
+
// Under reduced motion the month changes without travel: at once on a fine
|
|
482
|
+
// primary pointer, under an opacity dip on a coarse one (reducedMotionFade).
|
|
483
|
+
const fade = behavior === 'smooth' ? reducedMotionFade(scroller) : null;
|
|
484
|
+
if (fade != null) {
|
|
485
|
+
// The window mounts around the target first, and the scroll lands in
|
|
486
|
+
// the layout effect below, as a far jump does. So nothing moves the
|
|
487
|
+
// window after the jump. If the window followed the jump, WebKit would
|
|
488
|
+
// re-snap it one pane further on every frame. A scroll that lands on
|
|
489
|
+
// the centre row reports nothing.
|
|
490
|
+
const land = () => {
|
|
491
|
+
// The landing is the newest steer. An earlier steer whose scroll
|
|
492
|
+
// never arrived would otherwise hide every scroll after it, a
|
|
493
|
+
// trackpad's included. Closing the wheels makes such a steer: it
|
|
494
|
+
// re-asserts the month already in view.
|
|
495
|
+
steeredRowRef.current = null;
|
|
496
|
+
if (row === centerRowRef.current) {
|
|
497
|
+
scroller.scrollTo({left, behavior: 'auto'});
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
pendingScrollRef.current = row;
|
|
501
|
+
centerRowRef.current = row;
|
|
502
|
+
setCenterRow(row);
|
|
503
|
+
};
|
|
504
|
+
if (fade.duration === 0) {
|
|
505
|
+
land();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
// A second step within the dip has replaced the first (above): only
|
|
509
|
+
// its scroll lands, at its own dip's middle.
|
|
510
|
+
dipRef.current = {
|
|
511
|
+
animation: scroller.animate(
|
|
512
|
+
[{opacity: 1}, {opacity: 0}, {opacity: 1}],
|
|
513
|
+
{duration: fade.duration, easing: fade.easing},
|
|
514
|
+
),
|
|
515
|
+
timer: window.setTimeout(() => {
|
|
516
|
+
dipRef.current = null;
|
|
517
|
+
land();
|
|
518
|
+
}, fade.duration / 2),
|
|
519
|
+
};
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
461
522
|
steeredRowRef.current = row;
|
|
462
|
-
scroller.scrollTo({
|
|
463
|
-
left: scrollOffsetForRow(row, paneSize, isRTL),
|
|
464
|
-
behavior,
|
|
465
|
-
});
|
|
523
|
+
scroller.scrollTo({left, behavior});
|
|
466
524
|
},
|
|
467
|
-
[paneSize, minMonthIndex, rowCount, isRTL, recenter],
|
|
525
|
+
[paneSize, minMonthIndex, rowCount, isRTL, recenter, stopDip],
|
|
468
526
|
);
|
|
469
527
|
|
|
470
528
|
// Deliberately un-keyed: it runs after every render, costs a null check, and
|