@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.
Files changed (79) hide show
  1. package/blocks/CommentThreadShowcase.tsx +1 -1
  2. package/dist/Chart/formatters.d.ts +9 -6
  3. package/dist/Chart/formatters.d.ts.map +1 -1
  4. package/dist/Chart/formatters.js +16 -11
  5. package/dist/CircularProgress/CircularProgress.d.ts +4 -2
  6. package/dist/CircularProgress/CircularProgress.d.ts.map +1 -1
  7. package/dist/CircularProgress/CircularProgress.js +19 -23
  8. package/dist/RefreshControl/RefreshPullRecognizer.d.ts +40 -0
  9. package/dist/RefreshControl/RefreshPullRecognizer.d.ts.map +1 -0
  10. package/dist/RefreshControl/RefreshPullRecognizer.js +90 -0
  11. package/dist/RefreshControl/index.d.ts +11 -0
  12. package/dist/RefreshControl/index.d.ts.map +1 -0
  13. package/dist/RefreshControl/index.js +13 -0
  14. package/dist/RefreshControl/refreshIndicator.d.ts +10 -0
  15. package/dist/RefreshControl/refreshIndicator.d.ts.map +1 -0
  16. package/dist/RefreshControl/refreshIndicator.js +12 -0
  17. package/dist/RefreshControl/refreshModel.d.ts +61 -0
  18. package/dist/RefreshControl/refreshModel.d.ts.map +1 -0
  19. package/dist/RefreshControl/refreshModel.js +116 -0
  20. package/dist/RefreshControl/refreshPull.d.ts +69 -0
  21. package/dist/RefreshControl/refreshPull.d.ts.map +1 -0
  22. package/dist/RefreshControl/refreshPull.js +159 -0
  23. package/dist/RefreshControl/refreshTouchController.d.ts +45 -0
  24. package/dist/RefreshControl/refreshTouchController.d.ts.map +1 -0
  25. package/dist/RefreshControl/refreshTouchController.js +701 -0
  26. package/dist/RefreshControl/refreshValues.d.ts +76 -0
  27. package/dist/RefreshControl/refreshValues.d.ts.map +1 -0
  28. package/dist/RefreshControl/refreshValues.js +80 -0
  29. package/dist/RefreshControl/useMotionAnimator.d.ts +8 -0
  30. package/dist/RefreshControl/useMotionAnimator.d.ts.map +1 -0
  31. package/dist/RefreshControl/useMotionAnimator.js +25 -0
  32. package/dist/RefreshControl/useRefreshControl.d.ts +84 -0
  33. package/dist/RefreshControl/useRefreshControl.d.ts.map +1 -0
  34. package/dist/RefreshControl/useRefreshControl.js +207 -0
  35. package/dist/Schedule/DayView.d.ts.map +1 -1
  36. package/dist/Schedule/DayView.js +7 -4
  37. package/dist/Schedule/ListView.d.ts.map +1 -1
  38. package/dist/Schedule/ListView.js +8 -9
  39. package/dist/Schedule/MonthlyView.d.ts.map +1 -1
  40. package/dist/Schedule/MonthlyView.js +25 -18
  41. package/dist/Schedule/TimeGridView.d.ts.map +1 -1
  42. package/dist/Schedule/TimeGridView.js +22 -16
  43. package/dist/Schedule/WeeklyView.d.ts.map +1 -1
  44. package/dist/Schedule/WeeklyView.js +4 -4
  45. package/dist/Schedule/shared.d.ts +19 -13
  46. package/dist/Schedule/shared.d.ts.map +1 -1
  47. package/dist/Schedule/shared.js +80 -62
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +5 -1
  51. package/dist/lab.css +0 -8
  52. package/package.json +7 -6
  53. package/src/Chart/formatters.test.ts +36 -0
  54. package/src/Chart/formatters.ts +28 -16
  55. package/src/CircularProgress/CircularProgress.test.tsx +23 -0
  56. package/src/CircularProgress/CircularProgress.tsx +15 -42
  57. package/src/RefreshControl/RefreshPullRecognizer.ts +104 -0
  58. package/src/RefreshControl/__tests__/RefreshControl.interaction.browser.spec.ts +1454 -0
  59. package/src/RefreshControl/index.ts +18 -0
  60. package/src/RefreshControl/refreshIndicator.ts +12 -0
  61. package/src/RefreshControl/refreshModel.test.ts +174 -0
  62. package/src/RefreshControl/refreshModel.ts +117 -0
  63. package/src/RefreshControl/refreshPull.test.ts +270 -0
  64. package/src/RefreshControl/refreshPull.ts +186 -0
  65. package/src/RefreshControl/refreshTouchController.ts +838 -0
  66. package/src/RefreshControl/refreshValues.ts +80 -0
  67. package/src/RefreshControl/useMotionAnimator.ts +35 -0
  68. package/src/RefreshControl/useRefreshControl.test.tsx +1315 -0
  69. package/src/RefreshControl/useRefreshControl.ts +255 -0
  70. package/src/Schedule/DayView.tsx +4 -3
  71. package/src/Schedule/ListView.tsx +8 -14
  72. package/src/Schedule/MonthlyView.tsx +27 -18
  73. package/src/Schedule/Schedule.calendarSystems.test.tsx +317 -0
  74. package/src/Schedule/Schedule.doc.mjs +1 -1
  75. package/src/Schedule/Schedule.test.tsx +2 -2
  76. package/src/Schedule/TimeGridView.tsx +31 -9
  77. package/src/Schedule/WeeklyView.tsx +4 -6
  78. package/src/Schedule/shared.tsx +112 -68
  79. package/src/index.ts +9 -0
@@ -36,7 +36,7 @@ export default function CommentThreadShowcase() {
36
36
  });
37
37
 
38
38
  return (
39
- <Card padding={0} width={340}>
39
+ <Card padding={0} width="100%" maxWidth={340}>
40
40
  <CommentThread
41
41
  comment={thread}
42
42
  currentUser={VIEWER}
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * @file formatters.ts
3
- * @input Tick values and optional BCP 47 locale tags
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;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD;;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;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,MAAa,GAAG,MAAM,CAUvE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,MAAa,GAAG,MAAM,CAUvE"}
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"}
@@ -2,11 +2,14 @@
2
2
 
3
3
  /**
4
4
  * @file formatters.ts
5
- * @input Tick values and optional BCP 47 locale tags
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 new Intl.DateTimeFormat(locale, {
88
+ return plainDateFormat(plainDateFromDate(d), {
85
89
  month: 'short',
86
90
  day: 'numeric',
87
- calendar: 'gregory'
88
- }).format(d);
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 new Intl.DateTimeFormat(locale, {
104
+ return plainDateFormat(plainDateFromDate(d), {
100
105
  month: 'short',
101
106
  year: 'numeric',
102
- calendar: 'gregory'
103
- }).format(d);
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;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAYrE,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;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAmWD;;;;;;;;;;;;;;;;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,+BA+QvB;yBAjSe,gBAAgB"}
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
- 0: {
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
+ }