@astryxdesign/lab 0.7.9-canary.2ea8f68 → 0.7.9-canary.2f035fe
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/blocks/CommentThreadShowcase.tsx +1 -1
- package/dist/Chart/formatters.d.ts +9 -6
- package/dist/Chart/formatters.d.ts.map +1 -1
- package/dist/Chart/formatters.js +16 -11
- package/dist/CircularProgress/CircularProgress.d.ts +4 -2
- package/dist/CircularProgress/CircularProgress.d.ts.map +1 -1
- package/dist/CircularProgress/CircularProgress.js +19 -23
- package/dist/RefreshControl/RefreshPullRecognizer.d.ts +40 -0
- package/dist/RefreshControl/RefreshPullRecognizer.d.ts.map +1 -0
- package/dist/RefreshControl/RefreshPullRecognizer.js +90 -0
- package/dist/RefreshControl/index.d.ts +11 -0
- package/dist/RefreshControl/index.d.ts.map +1 -0
- package/dist/RefreshControl/index.js +13 -0
- package/dist/RefreshControl/refreshIndicator.d.ts +10 -0
- package/dist/RefreshControl/refreshIndicator.d.ts.map +1 -0
- package/dist/RefreshControl/refreshIndicator.js +12 -0
- package/dist/RefreshControl/refreshModel.d.ts +61 -0
- package/dist/RefreshControl/refreshModel.d.ts.map +1 -0
- package/dist/RefreshControl/refreshModel.js +116 -0
- package/dist/RefreshControl/refreshPull.d.ts +69 -0
- package/dist/RefreshControl/refreshPull.d.ts.map +1 -0
- package/dist/RefreshControl/refreshPull.js +159 -0
- package/dist/RefreshControl/refreshTouchController.d.ts +45 -0
- package/dist/RefreshControl/refreshTouchController.d.ts.map +1 -0
- package/dist/RefreshControl/refreshTouchController.js +701 -0
- package/dist/RefreshControl/refreshValues.d.ts +76 -0
- package/dist/RefreshControl/refreshValues.d.ts.map +1 -0
- package/dist/RefreshControl/refreshValues.js +80 -0
- package/dist/RefreshControl/useMotionAnimator.d.ts +8 -0
- package/dist/RefreshControl/useMotionAnimator.d.ts.map +1 -0
- package/dist/RefreshControl/useMotionAnimator.js +25 -0
- package/dist/RefreshControl/useRefreshControl.d.ts +84 -0
- package/dist/RefreshControl/useRefreshControl.d.ts.map +1 -0
- package/dist/RefreshControl/useRefreshControl.js +207 -0
- package/dist/Schedule/DayView.d.ts.map +1 -1
- package/dist/Schedule/DayView.js +7 -4
- package/dist/Schedule/ListView.d.ts.map +1 -1
- package/dist/Schedule/ListView.js +8 -9
- package/dist/Schedule/MonthlyView.d.ts.map +1 -1
- package/dist/Schedule/MonthlyView.js +25 -18
- package/dist/Schedule/TimeGridView.d.ts.map +1 -1
- package/dist/Schedule/TimeGridView.js +22 -16
- package/dist/Schedule/WeeklyView.d.ts.map +1 -1
- package/dist/Schedule/WeeklyView.js +4 -4
- package/dist/Schedule/shared.d.ts +19 -13
- package/dist/Schedule/shared.d.ts.map +1 -1
- package/dist/Schedule/shared.js +80 -62
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/lab.css +0 -8
- package/package.json +7 -6
- package/src/Chart/formatters.test.ts +36 -0
- package/src/Chart/formatters.ts +28 -16
- package/src/CircularProgress/CircularProgress.test.tsx +23 -0
- package/src/CircularProgress/CircularProgress.tsx +15 -42
- package/src/RefreshControl/RefreshPullRecognizer.ts +104 -0
- package/src/RefreshControl/__tests__/RefreshControl.interaction.browser.spec.ts +1454 -0
- package/src/RefreshControl/index.ts +18 -0
- package/src/RefreshControl/refreshIndicator.ts +12 -0
- package/src/RefreshControl/refreshModel.test.ts +174 -0
- package/src/RefreshControl/refreshModel.ts +117 -0
- package/src/RefreshControl/refreshPull.test.ts +270 -0
- package/src/RefreshControl/refreshPull.ts +186 -0
- package/src/RefreshControl/refreshTouchController.ts +838 -0
- package/src/RefreshControl/refreshValues.ts +80 -0
- package/src/RefreshControl/useMotionAnimator.ts +35 -0
- package/src/RefreshControl/useRefreshControl.test.tsx +1315 -0
- package/src/RefreshControl/useRefreshControl.ts +255 -0
- package/src/Schedule/DayView.tsx +4 -3
- package/src/Schedule/ListView.tsx +8 -14
- package/src/Schedule/MonthlyView.tsx +27 -18
- package/src/Schedule/Schedule.calendarSystems.test.tsx +317 -0
- package/src/Schedule/Schedule.doc.mjs +1 -1
- package/src/Schedule/Schedule.test.tsx +2 -2
- package/src/Schedule/TimeGridView.tsx +31 -9
- package/src/Schedule/WeeklyView.tsx +4 -6
- package/src/Schedule/shared.tsx +112 -68
- package/src/index.ts +9 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file formatters.ts
|
|
3
|
-
* @input Tick values
|
|
3
|
+
* @input Tick values, optional BCP 47 locale tags, and for dates an optional
|
|
4
|
+
* calendar
|
|
4
5
|
* @output Built-in tick format utilities for common data types
|
|
5
6
|
* @position Utility; consumed by ChartAxis via tickFormat prop
|
|
6
7
|
*/
|
|
7
|
-
import type { Locale } from '@astryxdesign/core/i18n';
|
|
8
|
+
import type { CalendarSystem, Locale } from '@astryxdesign/core/i18n';
|
|
8
9
|
/**
|
|
9
10
|
* Compact number formatter (e.g. 1200 → "1.2K", 1500000 → "1.5M")
|
|
10
11
|
*/
|
|
@@ -25,11 +26,13 @@ export declare function currency(symbol?: string, locale?: Locale): (value: unkn
|
|
|
25
26
|
*/
|
|
26
27
|
export declare function percent(value: unknown): string;
|
|
27
28
|
/**
|
|
28
|
-
* Date formatter — short date (e.g. "Jan 5", "Mar 12")
|
|
29
|
+
* Date formatter — short date (e.g. "Jan 5", "Mar 12"), in `calendar`
|
|
30
|
+
* (Gregorian by default)
|
|
29
31
|
*/
|
|
30
|
-
export declare function shortDate(value: unknown, locale?: Locale): string;
|
|
32
|
+
export declare function shortDate(value: unknown, locale?: Locale, calendar?: CalendarSystem): string;
|
|
31
33
|
/**
|
|
32
|
-
* Date formatter — month/year (e.g. "Jan 2024", "Mar 2025")
|
|
34
|
+
* Date formatter — month/year (e.g. "Jan 2024", "Mar 2025"), in `calendar`
|
|
35
|
+
* (Gregorian by default)
|
|
33
36
|
*/
|
|
34
|
-
export declare function monthYear(value: unknown, locale?: Locale): string;
|
|
37
|
+
export declare function monthYear(value: unknown, locale?: Locale, calendar?: CalendarSystem): string;
|
|
35
38
|
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/Chart/formatters.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/Chart/formatters.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAE,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpE;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAepD;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,MAAM,SAAM,EACZ,MAAM,GAAE,MAAa,GACpB,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAiB5B;AAQD;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM9C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,MAAM,GAAE,MAAa,EACrB,QAAQ,GAAE,cAA0B,GACnC,MAAM,CAUR;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EACd,MAAM,GAAE,MAAa,EACrB,QAAQ,GAAE,cAA0B,GACnC,MAAM,CAUR"}
|
package/dist/Chart/formatters.js
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @file formatters.ts
|
|
5
|
-
* @input Tick values
|
|
5
|
+
* @input Tick values, optional BCP 47 locale tags, and for dates an optional
|
|
6
|
+
* calendar
|
|
6
7
|
* @output Built-in tick format utilities for common data types
|
|
7
8
|
* @position Utility; consumed by ChartAxis via tickFormat prop
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
import { plainDateFormat, plainDateFromDate } from '@astryxdesign/core/utils';
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* Compact number formatter (e.g. 1200 → "1.2K", 1500000 → "1.5M")
|
|
12
15
|
*/
|
|
@@ -74,31 +77,33 @@ export function percent(value) {
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
/**
|
|
77
|
-
* Date formatter — short date (e.g. "Jan 5", "Mar 12")
|
|
80
|
+
* Date formatter — short date (e.g. "Jan 5", "Mar 12"), in `calendar`
|
|
81
|
+
* (Gregorian by default)
|
|
78
82
|
*/
|
|
79
|
-
export function shortDate(value, locale = 'en') {
|
|
83
|
+
export function shortDate(value, locale = 'en', calendar = 'gregory') {
|
|
80
84
|
const d = value instanceof Date ? value : new Date(Number(value));
|
|
81
85
|
if (isNaN(d.getTime())) {
|
|
82
86
|
return String(value);
|
|
83
87
|
}
|
|
84
|
-
return
|
|
88
|
+
return plainDateFormat(plainDateFromDate(d), {
|
|
85
89
|
month: 'short',
|
|
86
90
|
day: 'numeric',
|
|
87
|
-
calendar
|
|
88
|
-
})
|
|
91
|
+
calendar
|
|
92
|
+
}, locale);
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
/**
|
|
92
|
-
* Date formatter — month/year (e.g. "Jan 2024", "Mar 2025")
|
|
96
|
+
* Date formatter — month/year (e.g. "Jan 2024", "Mar 2025"), in `calendar`
|
|
97
|
+
* (Gregorian by default)
|
|
93
98
|
*/
|
|
94
|
-
export function monthYear(value, locale = 'en') {
|
|
99
|
+
export function monthYear(value, locale = 'en', calendar = 'gregory') {
|
|
95
100
|
const d = value instanceof Date ? value : new Date(Number(value));
|
|
96
101
|
if (isNaN(d.getTime())) {
|
|
97
102
|
return String(value);
|
|
98
103
|
}
|
|
99
|
-
return
|
|
104
|
+
return plainDateFormat(plainDateFromDate(d), {
|
|
100
105
|
month: 'short',
|
|
101
106
|
year: 'numeric',
|
|
102
|
-
calendar
|
|
103
|
-
})
|
|
107
|
+
calendar
|
|
108
|
+
}, locale);
|
|
104
109
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file CircularProgress.tsx
|
|
3
3
|
* @input Uses React (useId, refs, layout effects), stylex, SVG, Web
|
|
4
|
-
* Animations, color/spacing/duration/ease tokens
|
|
4
|
+
* Animations, color/spacing/duration/ease tokens, and Spinner's rotor
|
|
5
|
+
* (core internal/spinner-rotor) for the indeterminate ring
|
|
5
6
|
* @output Exports CircularProgress component, CircularProgressProps, CircularProgressSize, CircularProgressVariant types
|
|
6
7
|
* @position Core implementation; consumed by index.ts
|
|
7
8
|
*
|
|
@@ -89,7 +90,8 @@ export interface CircularProgressProps extends BaseProps<HTMLDivElement> {
|
|
|
89
90
|
*/
|
|
90
91
|
variant?: CircularProgressVariant;
|
|
91
92
|
/**
|
|
92
|
-
* When true, renders an animated indeterminate progress indicator
|
|
93
|
+
* When true, renders an animated indeterminate progress indicator: an arc
|
|
94
|
+
* that turns as Spinner's does.
|
|
93
95
|
* Use when the progress amount is unknown (e.g. loading, processing).
|
|
94
96
|
* The `value` and `hasValueLabel` props are ignored in this mode.
|
|
95
97
|
* Respects `prefers-reduced-motion` by slowing the animation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CircularProgress.d.ts","sourceRoot":"","sources":["../../src/CircularProgress/CircularProgress.tsx"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"CircularProgress.d.ts","sourceRoot":"","sources":["../../src/CircularProgress/CircularProgress.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAiBrE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAGlD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,IAAI,CAAC;CACf;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,0BAA0B,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAWtD,MAAM,WAAW,qBAAsB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACtE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAClC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA2TD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAS,EACT,GAAS,EACT,KAAK,EACL,aAAoB,EACpB,aAAqB,EACrB,gBAA0C,EAC1C,QAAQ,EACR,IAAW,EACX,OAAkB,EAClB,eAAuB,EACvB,UAAkB,EAClB,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,UAAU,EACzB,GAAG,EACH,GAAG,IAAI,EACR,EAAE,qBAAqB,+BAqRvB;yBAvSe,gBAAgB"}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file CircularProgress.tsx
|
|
7
7
|
* @input Uses React (useId, refs, layout effects), stylex, SVG, Web
|
|
8
|
-
* Animations, color/spacing/duration/ease tokens
|
|
8
|
+
* Animations, color/spacing/duration/ease tokens, and Spinner's rotor
|
|
9
|
+
* (core internal/spinner-rotor) for the indeterminate ring
|
|
9
10
|
* @output Exports CircularProgress component, CircularProgressProps, CircularProgressSize, CircularProgressVariant types
|
|
10
11
|
* @position Core implementation; consumed by index.ts
|
|
11
12
|
*
|
|
@@ -19,6 +20,7 @@ import { useId, useLayoutEffect, useRef } from 'react';
|
|
|
19
20
|
import * as stylex from '@stylexjs/stylex';
|
|
20
21
|
import '@astryxdesign/core/theme/tokens.stylex';
|
|
21
22
|
import { colorVars, durationVars, easeVars, fontWeightVars, spacingVars, typeScaleVars } from '@astryxdesign/core/theme/tokens.stylex';
|
|
23
|
+
import { SPINNER_ARC_FRACTION, spinnerRotorStyles, syncRotationPhase } from '@astryxdesign/core/internal/spinner-rotor';
|
|
22
24
|
import { mergeProps } from '@astryxdesign/core/utils';
|
|
23
25
|
import { themeProps } from '@astryxdesign/core/utils';
|
|
24
26
|
|
|
@@ -52,16 +54,6 @@ const SIZE_CONFIG = {
|
|
|
52
54
|
strokeWidth: 5
|
|
53
55
|
}
|
|
54
56
|
};
|
|
55
|
-
// =============================================================================
|
|
56
|
-
// Indeterminate animation
|
|
57
|
-
// =============================================================================
|
|
58
|
-
|
|
59
|
-
const indeterminateRotation = "xqng64z-B";
|
|
60
|
-
|
|
61
|
-
// SVG dash percentages scale with the square viewport. A 300% gap exceeds every
|
|
62
|
-
// ring circumference; shifting the 2% + 300% pattern by 302% closes the loop.
|
|
63
|
-
const indeterminateDash = "x9ppb3p-B";
|
|
64
|
-
|
|
65
57
|
// =============================================================================
|
|
66
58
|
// Styles
|
|
67
59
|
// =============================================================================
|
|
@@ -80,6 +72,15 @@ const styles = {
|
|
|
80
72
|
kOIVth: "xzye2dw",
|
|
81
73
|
$$css: true
|
|
82
74
|
},
|
|
75
|
+
svg: {
|
|
76
|
+
k1xSpc: "x1lliihq",
|
|
77
|
+
k3aq6I: "x9tu13d",
|
|
78
|
+
$$css: true
|
|
79
|
+
},
|
|
80
|
+
svgIndeterminate: {
|
|
81
|
+
k1xSpc: "x1lliihq",
|
|
82
|
+
$$css: true
|
|
83
|
+
},
|
|
83
84
|
track: {
|
|
84
85
|
kDwRjp: "xbh8q5q",
|
|
85
86
|
kjVXCG: "xpi25hw",
|
|
@@ -98,10 +99,6 @@ const styles = {
|
|
|
98
99
|
fillIndeterminate: {
|
|
99
100
|
kDwRjp: "xbh8q5q",
|
|
100
101
|
kU5bRw: "x1owpc8m",
|
|
101
|
-
kKVMdj: "x16zax3f",
|
|
102
|
-
k44tkh: "xmg6eyc xnh0sag",
|
|
103
|
-
kyAemX: "x4hg4is",
|
|
104
|
-
ko0y90: "xa4qsjk",
|
|
105
102
|
$$css: true
|
|
106
103
|
}
|
|
107
104
|
};
|
|
@@ -462,14 +459,8 @@ export function CircularProgress({
|
|
|
462
459
|
width: diameter,
|
|
463
460
|
height: diameter,
|
|
464
461
|
viewBox: `0 0 ${diameter} ${diameter}`,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
className: "x1lliihq x9tu13d"
|
|
468
|
-
},
|
|
469
|
-
1: {
|
|
470
|
-
className: "x1lliihq x1ka1v4i x1c74tu6 xy02sl2 x1esw782 xa4qsjk"
|
|
471
|
-
}
|
|
472
|
-
}[!!isIndeterminate << 0],
|
|
462
|
+
ref: isIndeterminate ? syncRotationPhase : undefined,
|
|
463
|
+
...stylex.props(isIndeterminate ? styles.svgIndeterminate : styles.svg, isIndeterminate && spinnerRotorStyles.turn),
|
|
473
464
|
children: [/*#__PURE__*/_jsx("circle", {
|
|
474
465
|
...mergeProps(themeProps('circular-progress-track'), stylex.props(styles.track, trackVariantStyles[trackVariant])),
|
|
475
466
|
cx: center,
|
|
@@ -484,6 +475,11 @@ export function CircularProgress({
|
|
|
484
475
|
cy: center,
|
|
485
476
|
r: radius,
|
|
486
477
|
strokeWidth: strokeWidth
|
|
478
|
+
// Spinner's arc: the same fraction of the ring, starting at 12
|
|
479
|
+
// o'clock.
|
|
480
|
+
,
|
|
481
|
+
strokeDasharray: `${circumference * SPINNER_ARC_FRACTION} ${circumference * (1 - SPINNER_ARC_FRACTION)}`,
|
|
482
|
+
transform: `rotate(-90 ${center} ${center})`
|
|
487
483
|
}) : null]
|
|
488
484
|
}), isIndeterminate ? null :
|
|
489
485
|
/*#__PURE__*/
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RefreshPullRecognizer.ts
|
|
3
|
+
* @input Touches from the gesture arena (Core's internal touch entry)
|
|
4
|
+
* @output Exports RefreshPullRecognizer, the vertical pan that reads a pull
|
|
5
|
+
* to refresh without taking the touch, and RefreshScrollPullRecognizer
|
|
6
|
+
* @position The gesture of useRefreshControl, on the foundation's
|
|
7
|
+
* PanGestureRecognizer
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When modified, update:
|
|
10
|
+
* - /packages/lab/src/RefreshControl/useRefreshControl.ts
|
|
11
|
+
*/
|
|
12
|
+
import { PanGestureRecognizer, type PanGestureRecognizerOptions } from '@astryxdesign/core/internal/touch';
|
|
13
|
+
import type { GestureEventContext, GestureTouch, PointerKind } from '@astryxdesign/core/internal/touch';
|
|
14
|
+
export declare class RefreshPullRecognizer extends PanGestureRecognizer {
|
|
15
|
+
/** Called when the first contact of a gesture lands, with its timestamp. */
|
|
16
|
+
onTouchDown: ((time: number) => void) | null;
|
|
17
|
+
/** The pointer of the gesture's first contact. */
|
|
18
|
+
pointerType: PointerKind;
|
|
19
|
+
/** Set when the gesture is a trackpad's scroll rather than a contact. */
|
|
20
|
+
scrollType: GestureTouch['scrollType'] | null;
|
|
21
|
+
constructor(options?: PanGestureRecognizerOptions);
|
|
22
|
+
/** Native scrolling keeps the finger: the pull only reads it. */
|
|
23
|
+
get claimsTouches(): boolean;
|
|
24
|
+
protected touchesBegan(touches: ReadonlyArray<GestureTouch>, event: GestureEventContext): void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The scroll pull: a trackpad's two-finger scroll at the top. It is a
|
|
28
|
+
* scroll-edge pan that takes a trackpad's scroll and no pointer. So it takes a
|
|
29
|
+
* scroll only where nothing under the pointer could scroll: the owner at its
|
|
30
|
+
* top and the content moving down. The host cancels each wheel event of a
|
|
31
|
+
* gesture that it takes, so its element gets a blocking `wheel` listener. Only
|
|
32
|
+
* a recognizer that claims may register one. It takes no pointer, so it adds
|
|
33
|
+
* no `touchmove` listener.
|
|
34
|
+
*/
|
|
35
|
+
export declare class RefreshScrollPullRecognizer extends RefreshPullRecognizer {
|
|
36
|
+
constructor(options?: PanGestureRecognizerOptions);
|
|
37
|
+
/** The scroll it takes is its own: the host cancels its wheel events. */
|
|
38
|
+
get claimsTouches(): boolean;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=RefreshPullRecognizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RefreshPullRecognizer.d.ts","sourceRoot":"","sources":["../../src/RefreshControl/RefreshPullRecognizer.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAoBH,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EACjC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACZ,MAAM,mCAAmC,CAAC;AAE3C,qBAAa,qBAAsB,SAAQ,oBAAoB;IAC7D,4EAA4E;IAC5E,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACpD,kDAAkD;IAClD,WAAW,EAAE,WAAW,CAAW;IACnC,yEAAyE;IACzE,UAAU,EAAE,YAAY,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEzC,OAAO,GAAE,2BAAgC;IAUrD,iEAAiE;IACjE,IAAa,aAAa,IAAI,OAAO,CAEpC;cAEkB,YAAY,CAC7B,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,EACpC,KAAK,EAAE,mBAAmB,GACzB,IAAI;CASR;AAED;;;;;;;;GAQG;AACH,qBAAa,2BAA4B,SAAQ,qBAAqB;gBACxD,OAAO,GAAE,2BAAgC;IAUrD,yEAAyE;IACzE,IAAa,aAAa,IAAI,OAAO,CAEpC;CACF"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file RefreshPullRecognizer.ts
|
|
5
|
+
* @input Touches from the gesture arena (Core's internal touch entry)
|
|
6
|
+
* @output Exports RefreshPullRecognizer, the vertical pan that reads a pull
|
|
7
|
+
* to refresh without taking the touch, and RefreshScrollPullRecognizer
|
|
8
|
+
* @position The gesture of useRefreshControl, on the foundation's
|
|
9
|
+
* PanGestureRecognizer
|
|
10
|
+
*
|
|
11
|
+
* SYNC: When modified, update:
|
|
12
|
+
* - /packages/lab/src/RefreshControl/useRefreshControl.ts
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// UIKit's refresh pull is the scroll view's own drag, not a second gesture.
|
|
16
|
+
// One touch can scroll, pull past the top, and scroll the same content again.
|
|
17
|
+
// So this pan never claims its touch, and only watches it. The browser keeps
|
|
18
|
+
// the finger for native scrolling, so nothing changes hands midway. The pan
|
|
19
|
+
// reads the finger to draw the overscroll, because the scroll owner's
|
|
20
|
+
// `overscroll-behavior-y` is `none` near the top.
|
|
21
|
+
//
|
|
22
|
+
// The pan begins on vertical travel past its hysteresis. The touch layer
|
|
23
|
+
// starts the pull where the content is at its top and the finger moves down.
|
|
24
|
+
// When the browser takes the touch to scroll, the pan keeps reading it to the
|
|
25
|
+
// lift. A second finger is the browser's, for pinch-zoom.
|
|
26
|
+
//
|
|
27
|
+
// A trackpad's scroll pulls through RefreshScrollPullRecognizer. A pen's or
|
|
28
|
+
// a mouse's drag stays the browser's, for text selection: the pan takes only
|
|
29
|
+
// a finger. `pointerType` and `scrollType` record the first contact's input.
|
|
30
|
+
// `onTouchDown` reports that contact landing, so a settle in flight can stop
|
|
31
|
+
// where it is, as UIScrollView brakes on touch-down.
|
|
32
|
+
|
|
33
|
+
import { PanGestureRecognizer } from '@astryxdesign/core/internal/touch';
|
|
34
|
+
export class RefreshPullRecognizer extends PanGestureRecognizer {
|
|
35
|
+
/** Called when the first contact of a gesture lands, with its timestamp. */
|
|
36
|
+
onTouchDown = null;
|
|
37
|
+
/** The pointer of the gesture's first contact. */
|
|
38
|
+
pointerType = 'touch';
|
|
39
|
+
/** Set when the gesture is a trackpad's scroll rather than a contact. */
|
|
40
|
+
scrollType = null;
|
|
41
|
+
constructor(options = {}) {
|
|
42
|
+
super({
|
|
43
|
+
name: 'RefreshPull',
|
|
44
|
+
direction: 'vertical',
|
|
45
|
+
maximumNumberOfTouches: 1,
|
|
46
|
+
allowedPointerTypes: ['touch'],
|
|
47
|
+
...options
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Native scrolling keeps the finger: the pull only reads it. */
|
|
52
|
+
get claimsTouches() {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
touchesBegan(touches, event) {
|
|
56
|
+
if (this.numberOfTouches === touches.length) {
|
|
57
|
+
const touch = touches[0];
|
|
58
|
+
this.pointerType = touch?.pointerType ?? 'touch';
|
|
59
|
+
this.scrollType = touch?.scrollType ?? null;
|
|
60
|
+
this.onTouchDown?.(event.time);
|
|
61
|
+
}
|
|
62
|
+
super.touchesBegan(touches, event);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The scroll pull: a trackpad's two-finger scroll at the top. It is a
|
|
68
|
+
* scroll-edge pan that takes a trackpad's scroll and no pointer. So it takes a
|
|
69
|
+
* scroll only where nothing under the pointer could scroll: the owner at its
|
|
70
|
+
* top and the content moving down. The host cancels each wheel event of a
|
|
71
|
+
* gesture that it takes, so its element gets a blocking `wheel` listener. Only
|
|
72
|
+
* a recognizer that claims may register one. It takes no pointer, so it adds
|
|
73
|
+
* no `touchmove` listener.
|
|
74
|
+
*/
|
|
75
|
+
export class RefreshScrollPullRecognizer extends RefreshPullRecognizer {
|
|
76
|
+
constructor(options = {}) {
|
|
77
|
+
super({
|
|
78
|
+
name: 'RefreshScrollPull',
|
|
79
|
+
scrollEdge: true,
|
|
80
|
+
allowedPointerTypes: [],
|
|
81
|
+
allowedScrollTypes: ['continuous'],
|
|
82
|
+
...options
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The scroll it takes is its own: the host cancels its wheel events. */
|
|
87
|
+
get claimsTouches() {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* @input Imports from useRefreshControl
|
|
4
|
+
* @output Exports useRefreshControl, its types and the reveal's custom
|
|
5
|
+
* property
|
|
6
|
+
* @position Entry point for Lab's pull to refresh; re-exported by
|
|
7
|
+
* /packages/lab/src/index.ts
|
|
8
|
+
*/
|
|
9
|
+
export { REFRESH_REVEAL_VAR, useRefreshControl } from './useRefreshControl.js';
|
|
10
|
+
export type { UseRefreshControlOptions, UseRefreshControlResult, } from './useRefreshControl.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/RefreshControl/index.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH,OAAO,EAAC,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EACV,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file index.ts
|
|
7
|
+
* @input Imports from useRefreshControl
|
|
8
|
+
* @output Exports useRefreshControl, its types and the reveal's custom
|
|
9
|
+
* property
|
|
10
|
+
* @position Entry point for Lab's pull to refresh; re-exported by
|
|
11
|
+
* /packages/lab/src/index.ts
|
|
12
|
+
*/
|
|
13
|
+
export { REFRESH_REVEAL_VAR, useRefreshControl } from "./useRefreshControl.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file refreshIndicator.ts
|
|
3
|
+
* @output The name of the custom property the touch layer writes and the
|
|
4
|
+
* app's indicator styles may read
|
|
5
|
+
* @position Internal to useRefreshControl; re-exported for the app's
|
|
6
|
+
* indicator styles
|
|
7
|
+
*/
|
|
8
|
+
/** CSS custom property the indicator reads: how much of it is revealed (0–1). */
|
|
9
|
+
export declare const REFRESH_REVEAL_VAR = "--astryx-refresh-reveal";
|
|
10
|
+
//# sourceMappingURL=refreshIndicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshIndicator.d.ts","sourceRoot":"","sources":["../../src/RefreshControl/refreshIndicator.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file refreshIndicator.ts
|
|
5
|
+
* @output The name of the custom property the touch layer writes and the
|
|
6
|
+
* app's indicator styles may read
|
|
7
|
+
* @position Internal to useRefreshControl; re-exported for the app's
|
|
8
|
+
* indicator styles
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** CSS custom property the indicator reads: how much of it is revealed (0–1). */
|
|
12
|
+
export const REFRESH_REVEAL_VAR = '--astryx-refresh-reveal';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file refreshModel.ts
|
|
3
|
+
* @input Pull events from the refresh pull recognizer, and programmatic begin
|
|
4
|
+
* and end requests
|
|
5
|
+
* @output Exports reduceRefresh (UIRefreshControl's state machine as a pure
|
|
6
|
+
* reducer that returns the effects to run), the initial state and types
|
|
7
|
+
* @position Pure logic for useRefreshControl; no DOM, no React
|
|
8
|
+
*
|
|
9
|
+
* SYNC: When modified, update:
|
|
10
|
+
* - /packages/lab/src/RefreshControl/refreshModel.test.ts
|
|
11
|
+
*/
|
|
12
|
+
export interface RefreshState {
|
|
13
|
+
/** A refresh is in progress (`UIRefreshControl.isRefreshing`). */
|
|
14
|
+
readonly isRefreshing: boolean;
|
|
15
|
+
/** A finger is pulling the content. */
|
|
16
|
+
readonly isPulling: boolean;
|
|
17
|
+
/** The current pull has triggered (its mapping turns one to one). */
|
|
18
|
+
readonly hasTriggered: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type RefreshEvent = {
|
|
21
|
+
readonly type: 'pullStart';
|
|
22
|
+
} | {
|
|
23
|
+
readonly type: 'pullMove';
|
|
24
|
+
readonly overscroll: number;
|
|
25
|
+
/** The trigger distance for this scroll owner. */
|
|
26
|
+
readonly trigger: number;
|
|
27
|
+
} | {
|
|
28
|
+
readonly type: 'pullEnd';
|
|
29
|
+
} | {
|
|
30
|
+
readonly type: 'pullCancel';
|
|
31
|
+
} | {
|
|
32
|
+
readonly type: 'begin';
|
|
33
|
+
readonly source: 'user' | 'program';
|
|
34
|
+
} | {
|
|
35
|
+
readonly type: 'end';
|
|
36
|
+
};
|
|
37
|
+
export type RefreshEffect =
|
|
38
|
+
/** The pull reached the trigger: spin, announce, call the handler. */
|
|
39
|
+
{
|
|
40
|
+
readonly type: 'trigger';
|
|
41
|
+
}
|
|
42
|
+
/** A refresh started without a pull; `callHandler` for the user's request. */
|
|
43
|
+
| {
|
|
44
|
+
readonly type: 'begin';
|
|
45
|
+
readonly callHandler: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** The finger let go: settle onto the inset or back to the top. */
|
|
48
|
+
| {
|
|
49
|
+
readonly type: 'settle';
|
|
50
|
+
readonly to: 'inset' | 'top';
|
|
51
|
+
}
|
|
52
|
+
/** The refresh ended: remove the inset and hide the indicator. */
|
|
53
|
+
| {
|
|
54
|
+
readonly type: 'end';
|
|
55
|
+
};
|
|
56
|
+
export declare const INITIAL_REFRESH_STATE: RefreshState;
|
|
57
|
+
export declare function reduceRefresh(state: RefreshState, event: RefreshEvent): {
|
|
58
|
+
state: RefreshState;
|
|
59
|
+
effects: ReadonlyArray<RefreshEffect>;
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=refreshModel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshModel.d.ts","sourceRoot":"","sources":["../../src/RefreshControl/refreshModel.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAeH,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,YAAY,GACpB;IAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAC,GAC5B;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,GACD;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAC,GAC1B;IAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAC,GAC7B;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GAC7D;IAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAC,CAAC;AAE3B,MAAM,MAAM,aAAa;AACvB,sEAAsE;AACpE;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAC;AAC5B,8EAA8E;GAC5E;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAAC;AACzD,mEAAmE;GACjE;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,CAAA;CAAC;AACzD,kEAAkE;GAChE;IAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAC,CAAC;AAE3B,eAAO,MAAM,qBAAqB,EAAE,YAInC,CAAC;AAEF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,YAAY,GAClB;IAAC,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;CAAC,CAgD9D"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file refreshModel.ts
|
|
5
|
+
* @input Pull events from the refresh pull recognizer, and programmatic begin
|
|
6
|
+
* and end requests
|
|
7
|
+
* @output Exports reduceRefresh (UIRefreshControl's state machine as a pure
|
|
8
|
+
* reducer that returns the effects to run), the initial state and types
|
|
9
|
+
* @position Pure logic for useRefreshControl; no DOM, no React
|
|
10
|
+
*
|
|
11
|
+
* SYNC: When modified, update:
|
|
12
|
+
* - /packages/lab/src/RefreshControl/refreshModel.test.ts
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// The rules, as UIKit runs them:
|
|
16
|
+
// - A pull triggers once, with the finger down, when its overscroll reaches
|
|
17
|
+
// the trigger distance. Only overscroll that a finger drives triggers, never
|
|
18
|
+
// a momentum overshoot.
|
|
19
|
+
// - A release settles onto the refreshing inset while refreshing, and back to
|
|
20
|
+
// the top otherwise. A cancelled pull settles the same way.
|
|
21
|
+
// - A programmatic begin puts the control in the refreshing state. It calls
|
|
22
|
+
// the refresh handler only when it stands for the user's request, not when
|
|
23
|
+
// the app reports a refresh that it started.
|
|
24
|
+
// - Ending a refresh that is not running does nothing.
|
|
25
|
+
//
|
|
26
|
+
// One refresh runs at a time: a pull while refreshing only stretches.
|
|
27
|
+
|
|
28
|
+
export const INITIAL_REFRESH_STATE = {
|
|
29
|
+
isRefreshing: false,
|
|
30
|
+
isPulling: false,
|
|
31
|
+
hasTriggered: false
|
|
32
|
+
};
|
|
33
|
+
export function reduceRefresh(state, event) {
|
|
34
|
+
switch (event.type) {
|
|
35
|
+
case 'pullStart':
|
|
36
|
+
return {
|
|
37
|
+
state: {
|
|
38
|
+
...state,
|
|
39
|
+
isPulling: true,
|
|
40
|
+
hasTriggered: false
|
|
41
|
+
},
|
|
42
|
+
effects: []
|
|
43
|
+
};
|
|
44
|
+
case 'pullMove':
|
|
45
|
+
{
|
|
46
|
+
// One refresh at a time: a pull while refreshing only stretches.
|
|
47
|
+
if (!state.isPulling || state.hasTriggered || state.isRefreshing || event.overscroll < event.trigger) {
|
|
48
|
+
return {
|
|
49
|
+
state,
|
|
50
|
+
effects: []
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
state: {
|
|
55
|
+
...state,
|
|
56
|
+
isRefreshing: true,
|
|
57
|
+
hasTriggered: true
|
|
58
|
+
},
|
|
59
|
+
effects: [{
|
|
60
|
+
type: 'trigger'
|
|
61
|
+
}]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
case 'pullEnd':
|
|
65
|
+
case 'pullCancel':
|
|
66
|
+
if (!state.isPulling) {
|
|
67
|
+
return {
|
|
68
|
+
state,
|
|
69
|
+
effects: []
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
state: {
|
|
74
|
+
...state,
|
|
75
|
+
isPulling: false
|
|
76
|
+
},
|
|
77
|
+
effects: [{
|
|
78
|
+
type: 'settle',
|
|
79
|
+
to: state.isRefreshing ? 'inset' : 'top'
|
|
80
|
+
}]
|
|
81
|
+
};
|
|
82
|
+
case 'begin':
|
|
83
|
+
if (state.isRefreshing) {
|
|
84
|
+
return {
|
|
85
|
+
state,
|
|
86
|
+
effects: []
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
state: {
|
|
91
|
+
...state,
|
|
92
|
+
isRefreshing: true
|
|
93
|
+
},
|
|
94
|
+
effects: [{
|
|
95
|
+
type: 'begin',
|
|
96
|
+
callHandler: event.source === 'user'
|
|
97
|
+
}]
|
|
98
|
+
};
|
|
99
|
+
case 'end':
|
|
100
|
+
if (!state.isRefreshing) {
|
|
101
|
+
return {
|
|
102
|
+
state,
|
|
103
|
+
effects: []
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
state: {
|
|
108
|
+
...state,
|
|
109
|
+
isRefreshing: false
|
|
110
|
+
},
|
|
111
|
+
effects: [{
|
|
112
|
+
type: 'end'
|
|
113
|
+
}]
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|