@seed-design/lynx-react 0.0.0-alpha-20260622094810

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 (196) hide show
  1. package/lib/_virtual/_rolldown/runtime.js +13 -0
  2. package/lib/components/ActionButton/ActionButton.d.ts +49 -0
  3. package/lib/components/ActionButton/ActionButton.d.ts.map +1 -0
  4. package/lib/components/ActionButton/ActionButton.jsx +158 -0
  5. package/lib/components/ActionButton/index.d.ts +2 -0
  6. package/lib/components/ActionButton/index.d.ts.map +1 -0
  7. package/lib/components/AppBar/AppBar.d.ts +35 -0
  8. package/lib/components/AppBar/AppBar.d.ts.map +1 -0
  9. package/lib/components/AppBar/AppBar.jsx +132 -0
  10. package/lib/components/AppBar/AppBar.namespace.d.ts +2 -0
  11. package/lib/components/AppBar/AppBar.namespace.d.ts.map +1 -0
  12. package/lib/components/AppBar/AppBar.namespace.js +15 -0
  13. package/lib/components/AppBar/context.d.ts +13 -0
  14. package/lib/components/AppBar/context.d.ts.map +1 -0
  15. package/lib/components/AppBar/context.js +12 -0
  16. package/lib/components/AppBar/index.d.ts +3 -0
  17. package/lib/components/AppBar/index.d.ts.map +1 -0
  18. package/lib/components/AppBar/useAppBar.d.ts +15 -0
  19. package/lib/components/AppBar/useAppBar.d.ts.map +1 -0
  20. package/lib/components/AppBar/useAppBar.js +72 -0
  21. package/lib/components/Badge/Badge.d.ts +14 -0
  22. package/lib/components/Badge/Badge.d.ts.map +1 -0
  23. package/lib/components/Badge/Badge.jsx +15 -0
  24. package/lib/components/Badge/index.d.ts +2 -0
  25. package/lib/components/Badge/index.d.ts.map +1 -0
  26. package/lib/components/BottomSheet/BottomSheet.d.ts +87 -0
  27. package/lib/components/BottomSheet/BottomSheet.d.ts.map +1 -0
  28. package/lib/components/BottomSheet/BottomSheet.jsx +163 -0
  29. package/lib/components/BottomSheet/BottomSheet.namespace.d.ts +2 -0
  30. package/lib/components/BottomSheet/BottomSheet.namespace.d.ts.map +1 -0
  31. package/lib/components/BottomSheet/BottomSheet.namespace.js +18 -0
  32. package/lib/components/BottomSheet/index.d.ts +3 -0
  33. package/lib/components/BottomSheet/index.d.ts.map +1 -0
  34. package/lib/components/Box/Box.d.ts +20 -0
  35. package/lib/components/Box/Box.d.ts.map +1 -0
  36. package/lib/components/Box/Box.jsx +14 -0
  37. package/lib/components/Box/index.d.ts +2 -0
  38. package/lib/components/Box/index.d.ts.map +1 -0
  39. package/lib/components/Checkbox/Checkbox.d.ts +35 -0
  40. package/lib/components/Checkbox/Checkbox.d.ts.map +1 -0
  41. package/lib/components/Checkbox/Checkbox.jsx +130 -0
  42. package/lib/components/Checkbox/Checkbox.namespace.d.ts +2 -0
  43. package/lib/components/Checkbox/Checkbox.namespace.d.ts.map +1 -0
  44. package/lib/components/Checkbox/Checkbox.namespace.js +12 -0
  45. package/lib/components/Checkbox/index.d.ts +3 -0
  46. package/lib/components/Checkbox/index.d.ts.map +1 -0
  47. package/lib/components/Icon/Icon.d.ts +37 -0
  48. package/lib/components/Icon/Icon.d.ts.map +1 -0
  49. package/lib/components/Icon/Icon.jsx +143 -0
  50. package/lib/components/Icon/index.d.ts +2 -0
  51. package/lib/components/Icon/index.d.ts.map +1 -0
  52. package/lib/components/ProgressCircle/ProgressCircle.d.ts +25 -0
  53. package/lib/components/ProgressCircle/ProgressCircle.d.ts.map +1 -0
  54. package/lib/components/ProgressCircle/ProgressCircle.jsx +281 -0
  55. package/lib/components/ProgressCircle/ProgressCircle.namespace.d.ts +2 -0
  56. package/lib/components/ProgressCircle/ProgressCircle.namespace.d.ts.map +1 -0
  57. package/lib/components/ProgressCircle/ProgressCircle.namespace.js +9 -0
  58. package/lib/components/ProgressCircle/index.d.ts +3 -0
  59. package/lib/components/ProgressCircle/index.d.ts.map +1 -0
  60. package/lib/components/RadioGroup/RadioGroup.d.ts +34 -0
  61. package/lib/components/RadioGroup/RadioGroup.d.ts.map +1 -0
  62. package/lib/components/RadioGroup/RadioGroup.jsx +160 -0
  63. package/lib/components/RadioGroup/RadioGroup.namespace.d.ts +2 -0
  64. package/lib/components/RadioGroup/RadioGroup.namespace.d.ts.map +1 -0
  65. package/lib/components/RadioGroup/RadioGroup.namespace.js +12 -0
  66. package/lib/components/RadioGroup/index.d.ts +3 -0
  67. package/lib/components/RadioGroup/index.d.ts.map +1 -0
  68. package/lib/components/Stack/Stack.d.ts +33 -0
  69. package/lib/components/Stack/Stack.d.ts.map +1 -0
  70. package/lib/components/Stack/Stack.jsx +39 -0
  71. package/lib/components/Stack/index.d.ts +2 -0
  72. package/lib/components/Stack/index.d.ts.map +1 -0
  73. package/lib/components/Switch/Switch.d.ts +21 -0
  74. package/lib/components/Switch/Switch.d.ts.map +1 -0
  75. package/lib/components/Switch/Switch.jsx +101 -0
  76. package/lib/components/Switch/Switch.namespace.d.ts +2 -0
  77. package/lib/components/Switch/Switch.namespace.d.ts.map +1 -0
  78. package/lib/components/Switch/Switch.namespace.js +11 -0
  79. package/lib/components/Switch/index.d.ts +3 -0
  80. package/lib/components/Switch/index.d.ts.map +1 -0
  81. package/lib/components/TagGroup/TagGroup.d.ts +34 -0
  82. package/lib/components/TagGroup/TagGroup.d.ts.map +1 -0
  83. package/lib/components/TagGroup/TagGroup.jsx +57 -0
  84. package/lib/components/TagGroup/TagGroup.namespace.d.ts +2 -0
  85. package/lib/components/TagGroup/TagGroup.namespace.d.ts.map +1 -0
  86. package/lib/components/TagGroup/TagGroup.namespace.js +10 -0
  87. package/lib/components/TagGroup/index.d.ts +3 -0
  88. package/lib/components/TagGroup/index.d.ts.map +1 -0
  89. package/lib/components/Text/Text.d.ts +7 -0
  90. package/lib/components/Text/Text.d.ts.map +1 -0
  91. package/lib/components/Text/Text.jsx +28 -0
  92. package/lib/components/Text/index.d.ts +2 -0
  93. package/lib/components/Text/index.d.ts.map +1 -0
  94. package/lib/components/index.d.ts +14 -0
  95. package/lib/components/index.d.ts.map +1 -0
  96. package/lib/hooks/index.d.ts +5 -0
  97. package/lib/hooks/index.d.ts.map +1 -0
  98. package/lib/hooks/useControllableState.d.ts +15 -0
  99. package/lib/hooks/useControllableState.d.ts.map +1 -0
  100. package/lib/hooks/useControllableState.js +27 -0
  101. package/lib/hooks/useIconColor.d.ts +19 -0
  102. package/lib/hooks/useIconColor.d.ts.map +1 -0
  103. package/lib/hooks/useIconColor.js +38 -0
  104. package/lib/hooks/usePressTap.d.ts +27 -0
  105. package/lib/hooks/usePressTap.d.ts.map +1 -0
  106. package/lib/hooks/usePressTap.js +35 -0
  107. package/lib/hooks/useSafeArea.d.ts +19 -0
  108. package/lib/hooks/useSafeArea.d.ts.map +1 -0
  109. package/lib/hooks/useSafeArea.js +33 -0
  110. package/lib/index.d.ts +5 -0
  111. package/lib/index.d.ts.map +1 -0
  112. package/lib/index.js +29 -0
  113. package/lib/types.d.ts +51 -0
  114. package/lib/types.d.ts.map +1 -0
  115. package/lib/utils/children.d.ts +3 -0
  116. package/lib/utils/children.d.ts.map +1 -0
  117. package/lib/utils/children.js +8 -0
  118. package/lib/utils/create-slot-recipe-context.d.ts +27 -0
  119. package/lib/utils/create-slot-recipe-context.d.ts.map +1 -0
  120. package/lib/utils/create-slot-recipe-context.jsx +94 -0
  121. package/lib/utils/get-seed-class-name.d.ts +22 -0
  122. package/lib/utils/get-seed-class-name.d.ts.map +1 -0
  123. package/lib/utils/get-seed-class-name.js +28 -0
  124. package/lib/utils/index.d.ts +5 -0
  125. package/lib/utils/index.d.ts.map +1 -0
  126. package/lib/utils/resolve-recipe-token.d.ts +38 -0
  127. package/lib/utils/resolve-recipe-token.d.ts.map +1 -0
  128. package/lib/utils/resolve-recipe-token.js +49 -0
  129. package/lib/utils/split-multiple-variants-props.d.ts +29 -0
  130. package/lib/utils/split-multiple-variants-props.d.ts.map +1 -0
  131. package/lib/utils/split-multiple-variants-props.js +34 -0
  132. package/lib/utils/styled.d.ts +83 -0
  133. package/lib/utils/styled.d.ts.map +1 -0
  134. package/lib/utils/styled.js +144 -0
  135. package/package.json +54 -0
  136. package/src/components/ActionButton/ActionButton.test.tsx +167 -0
  137. package/src/components/ActionButton/ActionButton.tsx +363 -0
  138. package/src/components/ActionButton/index.ts +1 -0
  139. package/src/components/AppBar/AppBar.namespace.ts +18 -0
  140. package/src/components/AppBar/AppBar.test.tsx +270 -0
  141. package/src/components/AppBar/AppBar.tsx +288 -0
  142. package/src/components/AppBar/context.ts +27 -0
  143. package/src/components/AppBar/index.ts +21 -0
  144. package/src/components/AppBar/useAppBar.ts +100 -0
  145. package/src/components/Badge/Badge.test.tsx +54 -0
  146. package/src/components/Badge/Badge.tsx +32 -0
  147. package/src/components/Badge/index.ts +1 -0
  148. package/src/components/BottomSheet/BottomSheet.namespace.ts +24 -0
  149. package/src/components/BottomSheet/BottomSheet.test.tsx +219 -0
  150. package/src/components/BottomSheet/BottomSheet.tsx +423 -0
  151. package/src/components/BottomSheet/index.ts +27 -0
  152. package/src/components/Box/Box.test.tsx +85 -0
  153. package/src/components/Box/Box.tsx +39 -0
  154. package/src/components/Box/index.ts +1 -0
  155. package/src/components/Checkbox/Checkbox.namespace.ts +12 -0
  156. package/src/components/Checkbox/Checkbox.tsx +282 -0
  157. package/src/components/Checkbox/index.ts +14 -0
  158. package/src/components/Icon/Icon.tsx +265 -0
  159. package/src/components/Icon/index.ts +8 -0
  160. package/src/components/ProgressCircle/ProgressCircle.namespace.ts +5 -0
  161. package/src/components/ProgressCircle/ProgressCircle.tsx +424 -0
  162. package/src/components/ProgressCircle/index.ts +9 -0
  163. package/src/components/RadioGroup/RadioGroup.namespace.ts +12 -0
  164. package/src/components/RadioGroup/RadioGroup.tsx +320 -0
  165. package/src/components/RadioGroup/index.ts +14 -0
  166. package/src/components/Stack/Stack.tsx +97 -0
  167. package/src/components/Stack/index.ts +1 -0
  168. package/src/components/Switch/Switch.namespace.ts +10 -0
  169. package/src/components/Switch/Switch.tsx +200 -0
  170. package/src/components/Switch/index.ts +12 -0
  171. package/src/components/TagGroup/TagGroup.namespace.ts +8 -0
  172. package/src/components/TagGroup/TagGroup.tsx +127 -0
  173. package/src/components/TagGroup/index.ts +10 -0
  174. package/src/components/Text/Text.tsx +64 -0
  175. package/src/components/Text/index.ts +1 -0
  176. package/src/components/index.ts +13 -0
  177. package/src/hooks/index.ts +4 -0
  178. package/src/hooks/useControllableState.test.ts +120 -0
  179. package/src/hooks/useControllableState.ts +38 -0
  180. package/src/hooks/useIconColor.test.ts +13 -0
  181. package/src/hooks/useIconColor.ts +67 -0
  182. package/src/hooks/usePressTap.test.ts +129 -0
  183. package/src/hooks/usePressTap.ts +58 -0
  184. package/src/hooks/useSafeArea.test.ts +83 -0
  185. package/src/hooks/useSafeArea.ts +56 -0
  186. package/src/index.ts +4 -0
  187. package/src/types.ts +76 -0
  188. package/src/utils/children.ts +13 -0
  189. package/src/utils/create-slot-recipe-context.test.tsx +96 -0
  190. package/src/utils/create-slot-recipe-context.tsx +187 -0
  191. package/src/utils/get-seed-class-name.ts +47 -0
  192. package/src/utils/index.ts +4 -0
  193. package/src/utils/resolve-recipe-token.ts +47 -0
  194. package/src/utils/split-multiple-variants-props.test.ts +61 -0
  195. package/src/utils/split-multiple-variants-props.ts +59 -0
  196. package/src/utils/styled.ts +442 -0
@@ -0,0 +1,424 @@
1
+ import type * as React from "@lynx-js/react";
2
+ import {
3
+ createContext,
4
+ forwardRef,
5
+ runOnMainThread,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useMainThreadRef,
10
+ } from "@lynx-js/react";
11
+ import type { MainThread } from "@lynx-js/types";
12
+ import clsx from "clsx";
13
+ import { progressCircle } from "@seed-design/lynx-css/recipes/progress-circle";
14
+ import type { ProgressCircleVariantProps } from "@seed-design/lynx-css/recipes/progress-circle";
15
+ import type { LynxStyledElementProps, LynxViewRef } from "../../types";
16
+
17
+ ////////////////////////////////////////////////////////////////////////////////////
18
+
19
+ interface ProgressCircleContextValue {
20
+ numSize: number;
21
+ isDeterminate: boolean;
22
+ progress: number;
23
+ classes: ReturnType<typeof progressCircle>;
24
+ }
25
+
26
+ const ProgressCircleContext = createContext<ProgressCircleContextValue | null>(null);
27
+
28
+ function useProgressCircleContext() {
29
+ const ctx = useContext(ProgressCircleContext);
30
+ if (!ctx) {
31
+ throw new Error("ProgressCircle compound components must be used within ProgressCircle.Root");
32
+ }
33
+ return ctx;
34
+ }
35
+
36
+ ////////////////////////////////////////////////////////////////////////////////////
37
+
38
+ // --- Background-thread-only utilities (for initial render) ---
39
+
40
+ function computeRingGeometry(numSize: number) {
41
+ const halfSize = numSize / 2;
42
+ const innerR = 0.53 * Math.SQRT2 * halfSize;
43
+ const ringCenterR = (halfSize + innerR) / 2;
44
+ const capSize = halfSize - innerR;
45
+ return { halfSize, innerR, ringCenterR, capSize };
46
+ }
47
+
48
+ function bgPieClipPath(size: number, angleDeg: number): string | undefined {
49
+ if (angleDeg >= 360) return undefined;
50
+ if (angleDeg <= 0) return 'path("M 0 0 Z")';
51
+ const c = size / 2;
52
+ const r = c + 1;
53
+ const rad = (angleDeg * Math.PI) / 180;
54
+ const endX = c + r * Math.sin(rad);
55
+ const endY = c - r * Math.cos(rad);
56
+ const largeArc = angleDeg > 180 ? 1 : 0;
57
+ return `path("M ${c} ${c} L ${c} ${c - r} A ${r} ${r} 0 ${largeArc} 1 ${endX} ${endY} Z")`;
58
+ }
59
+
60
+ ////////////////////////////////////////////////////////////////////////////////////
61
+
62
+ // --- Main-thread-only utilities (for animations) ---
63
+ // Compiled into the main thread bundle. Must NOT be called from render code.
64
+
65
+ function cubicBezier(rawT: number, x1: number, y1: number, x2: number, y2: number): number {
66
+ "main thread";
67
+ const t = Math.max(0, Math.min(1, rawT));
68
+ const cx = 3 * x1;
69
+ const bx = 3 * (x2 - x1) - cx;
70
+ const ax = 1 - cx - bx;
71
+ let currentT = t;
72
+ for (let i = 0; i < 8; i++) {
73
+ const currentX = ((ax * currentT + bx) * currentT + cx) * currentT - t;
74
+ const currentDx = (3 * ax * currentT + 2 * bx) * currentT + cx;
75
+ if (Math.abs(currentDx) < 1e-6) break;
76
+ currentT = currentT - currentX / currentDx;
77
+ }
78
+ const cy = 3 * y1;
79
+ const by = 3 * (y2 - y1) - cy;
80
+ const ay = 1 - cy - by;
81
+ return ((ay * currentT + by) * currentT + cy) * currentT;
82
+ }
83
+
84
+ function pieClipPath(size: number, angleDeg: number): string | undefined {
85
+ "main thread";
86
+ if (angleDeg >= 360) return undefined;
87
+ if (angleDeg <= 0) return 'path("M 0 0 Z")';
88
+ const c = size / 2;
89
+ const r = c + 1;
90
+ const rad = (angleDeg * Math.PI) / 180;
91
+ const endX = c + r * Math.sin(rad);
92
+ const endY = c - r * Math.cos(rad);
93
+ const largeArc = angleDeg > 180 ? 1 : 0;
94
+ return `path("M ${c} ${c} L ${c} ${c - r} A ${r} ${r} 0 ${largeArc} 1 ${endX} ${endY} Z")`;
95
+ }
96
+
97
+ function sampleIndeterminate(t: number) {
98
+ "main thread";
99
+ const containerEased = cubicBezier(t, 0.35, 0.25, 0.65, 0.75);
100
+ const headEased = cubicBezier(t, 0.35, 0, 0.65, 1);
101
+ const tailEased = cubicBezier(t, 0.35, 0, 0.65, 0.6);
102
+
103
+ const headLength = headEased <= 0.75 ? (headEased / 0.75) * 360 : 360;
104
+ const tailOffset = tailEased <= 1 / 3 ? 0 : ((tailEased - 1 / 3) / (2 / 3)) * 360;
105
+ const arcLength = Math.max(0, headLength - tailOffset);
106
+ const containerDeg = containerEased * 360 + tailOffset;
107
+
108
+ return { containerDeg, arcLength };
109
+ }
110
+
111
+ ////////////////////////////////////////////////////////////////////////////////////
112
+
113
+ // --- Animation constants ---
114
+
115
+ const INDETERMINATE_DURATION = 1200;
116
+ const TRANSITION_DURATION = 300;
117
+
118
+ ////////////////////////////////////////////////////////////////////////////////////
119
+
120
+ // --- Components ---
121
+
122
+ export interface ProgressCircleRootProps
123
+ extends ProgressCircleVariantProps,
124
+ LynxStyledElementProps {
125
+ minValue?: number;
126
+ maxValue?: number;
127
+ value?: number;
128
+ }
129
+
130
+ export type RootProps = ProgressCircleRootProps;
131
+
132
+ ////////////////////////////////////////////////////////////////////////////////////
133
+
134
+ /**
135
+ * Lynx에서 SVG를 사용할 수 없어 CSS clip-path 기반 pie sector로 구현.
136
+ *
137
+ * **Known Issues:**
138
+ * - clip-path가 Lynx에서 animatable이 아니라 JS RAF로 매 프레임 SVG path 생성
139
+ * - Lynx main thread에서 모듈 레벨 Map 미지원으로 인스턴스 간 RAF 공유 불가
140
+ * - 다수 인스턴스 동시 렌더링 시 성능 저하 가능
141
+ *
142
+ * Lynx SVG + stroke-dasharray 지원 시 CSS-only 애니메이션으로 전환 예정.
143
+ */
144
+ export const ProgressCircleRoot = forwardRef<unknown, ProgressCircleRootProps>((props, ref) => {
145
+ const [variantProps, otherProps] = progressCircle.splitVariantProps(props);
146
+ const { children, className, style, minValue, maxValue, value } = otherProps;
147
+ const size = variantProps.size ?? "40";
148
+ const tone = variantProps.tone ?? "neutral";
149
+ const numSize = Number(size);
150
+
151
+ const isDeterminate = minValue !== undefined && maxValue !== undefined && value !== undefined;
152
+
153
+ const range = (maxValue ?? 1) - (minValue ?? 0);
154
+ const progress = isDeterminate ? (range === 0 ? 0 : ((value ?? 0) - (minValue ?? 0)) / range) : 0;
155
+
156
+ const classes = progressCircle({ tone, size });
157
+
158
+ const ctx = useMemo(
159
+ () => ({ numSize, isDeterminate, progress, classes }),
160
+ [numSize, isDeterminate, progress, classes],
161
+ );
162
+
163
+ return (
164
+ <ProgressCircleContext.Provider value={ctx}>
165
+ <view
166
+ {...(ref ? { ref: ref as LynxViewRef } : {})}
167
+ className={clsx(classes.root, className)}
168
+ style={{ ...style, width: `${numSize}px`, height: `${numSize}px` }}
169
+ >
170
+ {children}
171
+ </view>
172
+ </ProgressCircleContext.Provider>
173
+ );
174
+ });
175
+
176
+ ////////////////////////////////////////////////////////////////////////////////////
177
+
178
+ export const ProgressCircleRange = () => {
179
+ const { numSize, isDeterminate, progress, classes } = useProgressCircleContext();
180
+
181
+ if (!isDeterminate) {
182
+ return <IndeterminateRange numSize={numSize} classes={classes} />;
183
+ }
184
+
185
+ return <DeterminateRange numSize={numSize} progress={progress} classes={classes} />;
186
+ };
187
+
188
+ type Classes = ReturnType<typeof progressCircle>;
189
+
190
+ ////////////////////////////////////////////////////////////////////////////////////
191
+
192
+ function DeterminateRange({
193
+ numSize,
194
+ progress,
195
+ classes,
196
+ }: {
197
+ numSize: number;
198
+ progress: number;
199
+ classes: Classes;
200
+ }) {
201
+ const rangeRef = useMainThreadRef<MainThread.Element>(null);
202
+ const startCapRef = useMainThreadRef<MainThread.Element>(null);
203
+ const endCapRef = useMainThreadRef<MainThread.Element>(null);
204
+ const prevProgressRef = useMainThreadRef<number>(progress);
205
+ const cancelRef = useMainThreadRef<number>(0);
206
+
207
+ const { halfSize, ringCenterR, capSize } = computeRingGeometry(numSize);
208
+
209
+ const initialAngle = progress * 360;
210
+ const initialClipPath = bgPieClipPath(numSize, initialAngle);
211
+ const initialEndRad = (initialAngle * Math.PI) / 180;
212
+
213
+ function startAnimation(newProgress: number) {
214
+ "main thread";
215
+
216
+ if (cancelRef.current) {
217
+ cancelAnimationFrame(cancelRef.current);
218
+ cancelRef.current = 0;
219
+ }
220
+
221
+ const from = prevProgressRef.current ?? 0;
222
+ prevProgressRef.current = newProgress;
223
+ if (from === newProgress) return;
224
+
225
+ const rangeEl = rangeRef.current;
226
+ const endCapEl = endCapRef.current;
227
+ let startTs = 0;
228
+
229
+ function applyProgress(p: number): void {
230
+ const angleDeg = p * 360;
231
+ const cp = pieClipPath(numSize, angleDeg);
232
+ if (cp) {
233
+ rangeEl?.setStyleProperty("clip-path", cp);
234
+ } else {
235
+ rangeEl?.setStyleProperty("clip-path", "none");
236
+ }
237
+
238
+ const rad = (angleDeg * Math.PI) / 180;
239
+ endCapEl?.setStyleProperties({
240
+ left: `${halfSize + ringCenterR * Math.sin(rad) - capSize / 2}px`,
241
+ top: `${halfSize - ringCenterR * Math.cos(rad) - capSize / 2}px`,
242
+ });
243
+ }
244
+
245
+ function step(ts: number): void {
246
+ if (!startTs) startTs = Number(ts);
247
+ const elapsed = ts - startTs;
248
+ if (elapsed >= TRANSITION_DURATION) {
249
+ applyProgress(newProgress);
250
+ cancelRef.current = 0;
251
+ return;
252
+ }
253
+ const t = elapsed / TRANSITION_DURATION;
254
+ const eased = cubicBezier(t, 0, 0, 0.15, 1);
255
+ applyProgress(from + (newProgress - from) * eased);
256
+ cancelRef.current = requestAnimationFrame(step);
257
+ }
258
+
259
+ applyProgress(from);
260
+ cancelRef.current = requestAnimationFrame(step);
261
+ }
262
+
263
+ function cancelAnimation() {
264
+ "main thread";
265
+ if (cancelRef.current) {
266
+ cancelAnimationFrame(cancelRef.current);
267
+ cancelRef.current = 0;
268
+ }
269
+ }
270
+
271
+ useEffect(() => {
272
+ runOnMainThread(startAnimation)(progress);
273
+ return () => {
274
+ runOnMainThread(cancelAnimation)();
275
+ };
276
+ }, [progress, numSize]);
277
+
278
+ return (
279
+ <>
280
+ <view
281
+ main-thread:ref={rangeRef}
282
+ className={classes.range}
283
+ style={{
284
+ position: "absolute",
285
+ width: `${numSize}px`,
286
+ height: `${numSize}px`,
287
+ borderRadius: "50%",
288
+ clipPath: initialClipPath,
289
+ }}
290
+ />
291
+ <view
292
+ main-thread:ref={startCapRef}
293
+ className={classes.cap}
294
+ style={{
295
+ width: `${capSize}px`,
296
+ height: `${capSize}px`,
297
+ left: `${halfSize - capSize / 2}px`,
298
+ top: `${halfSize - ringCenterR - capSize / 2}px`,
299
+ }}
300
+ />
301
+ <view
302
+ main-thread:ref={endCapRef}
303
+ className={classes.cap}
304
+ style={{
305
+ width: `${capSize}px`,
306
+ height: `${capSize}px`,
307
+ left: `${halfSize + ringCenterR * Math.sin(initialEndRad) - capSize / 2}px`,
308
+ top: `${halfSize - ringCenterR * Math.cos(initialEndRad) - capSize / 2}px`,
309
+ }}
310
+ />
311
+ </>
312
+ );
313
+ }
314
+
315
+ ////////////////////////////////////////////////////////////////////////////////////
316
+
317
+ function IndeterminateRange({ numSize, classes }: { numSize: number; classes: Classes }) {
318
+ const containerRef = useMainThreadRef<MainThread.Element>(null);
319
+ const rangeRef = useMainThreadRef<MainThread.Element>(null);
320
+ const headCapRef = useMainThreadRef<MainThread.Element>(null);
321
+ const tailCapRef = useMainThreadRef<MainThread.Element>(null);
322
+ const rafIdRef = useMainThreadRef<number>(0);
323
+
324
+ const { halfSize, ringCenterR, capSize } = computeRingGeometry(numSize);
325
+
326
+ function startLoop() {
327
+ "main thread";
328
+
329
+ const containerEl = containerRef.current;
330
+ const rangeEl = rangeRef.current;
331
+ const headCapEl = headCapRef.current;
332
+ let startTs = 0;
333
+
334
+ function tick(ts: number): void {
335
+ if (!startTs) startTs = Number(ts);
336
+
337
+ const elapsed = ts - startTs;
338
+ const t = (elapsed % INDETERMINATE_DURATION) / INDETERMINATE_DURATION;
339
+ const state = sampleIndeterminate(t);
340
+
341
+ containerEl?.setStyleProperty("transform", `rotate(${state.containerDeg}deg)`);
342
+
343
+ const clipPath = pieClipPath(numSize, state.arcLength);
344
+ if (clipPath) {
345
+ rangeEl?.setStyleProperty("clip-path", clipPath);
346
+ } else {
347
+ rangeEl?.setStyleProperty("clip-path", "none");
348
+ }
349
+
350
+ const headRad = (state.arcLength * Math.PI) / 180;
351
+ headCapEl?.setStyleProperties({
352
+ left: `${halfSize + ringCenterR * Math.sin(headRad) - capSize / 2}px`,
353
+ top: `${halfSize - ringCenterR * Math.cos(headRad) - capSize / 2}px`,
354
+ });
355
+ rafIdRef.current = requestAnimationFrame(tick);
356
+ }
357
+
358
+ rafIdRef.current = requestAnimationFrame(tick);
359
+ }
360
+
361
+ function stopLoop() {
362
+ "main thread";
363
+ if (rafIdRef.current) {
364
+ cancelAnimationFrame(rafIdRef.current);
365
+ rafIdRef.current = 0;
366
+ }
367
+ }
368
+
369
+ useEffect(() => {
370
+ runOnMainThread(startLoop)();
371
+ return () => {
372
+ runOnMainThread(stopLoop)();
373
+ };
374
+ }, [numSize]);
375
+
376
+ const initialClipPath = 'path("M 0 0 Z")';
377
+
378
+ return (
379
+ <view
380
+ main-thread:ref={containerRef}
381
+ style={{
382
+ position: "absolute",
383
+ width: `${numSize}px`,
384
+ height: `${numSize}px`,
385
+ transform: "rotate(0deg)",
386
+ }}
387
+ >
388
+ <view
389
+ main-thread:ref={rangeRef}
390
+ className={classes.range}
391
+ style={{
392
+ position: "absolute",
393
+ width: `${numSize}px`,
394
+ height: `${numSize}px`,
395
+ borderRadius: "50%",
396
+ clipPath: initialClipPath,
397
+ }}
398
+ />
399
+ <view
400
+ main-thread:ref={tailCapRef}
401
+ className={classes.cap}
402
+ style={{
403
+ width: `${capSize}px`,
404
+ height: `${capSize}px`,
405
+ left: `${halfSize - capSize / 2}px`,
406
+ top: `${halfSize - ringCenterR - capSize / 2}px`,
407
+ }}
408
+ />
409
+ <view
410
+ main-thread:ref={headCapRef}
411
+ className={classes.cap}
412
+ style={{
413
+ width: `${capSize}px`,
414
+ height: `${capSize}px`,
415
+ left: `${halfSize - capSize / 2}px`,
416
+ top: `${halfSize - ringCenterR - capSize / 2}px`,
417
+ }}
418
+ />
419
+ </view>
420
+ );
421
+ }
422
+
423
+ ProgressCircleRoot.displayName = "ProgressCircleRoot";
424
+ ProgressCircleRange.displayName = "ProgressCircleRange";
@@ -0,0 +1,9 @@
1
+ export {
2
+ ProgressCircleRange,
3
+ ProgressCircleRoot,
4
+ type ProgressCircleRootProps,
5
+ type ProgressCircleRootProps as ProgressCircleProps,
6
+ type RootProps,
7
+ } from "./ProgressCircle";
8
+
9
+ export * as ProgressCircle from "./ProgressCircle.namespace";
@@ -0,0 +1,12 @@
1
+ export {
2
+ RadioGroupItem as Item,
3
+ RadioGroupItemControl as ItemControl,
4
+ RadioGroupItemIndicator as ItemIndicator,
5
+ RadioGroupItemLabel as ItemLabel,
6
+ RadioGroupRoot as Root,
7
+ type RadioGroupItemControlProps as ItemControlProps,
8
+ type RadioGroupItemIndicatorProps as ItemIndicatorProps,
9
+ type RadioGroupItemLabelProps as ItemLabelProps,
10
+ type RadioGroupItemProps as ItemProps,
11
+ type RadioGroupRootProps as RootProps,
12
+ } from "./RadioGroup";