@idealyst/components 1.0.82 → 1.0.84

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 (316) hide show
  1. package/CLAUDE.md +199 -232
  2. package/README.md +5 -5
  3. package/package.json +25 -7
  4. package/plugin/README.md +272 -0
  5. package/plugin/test-cases.jsx +112 -0
  6. package/plugin/web-legacy.js +320 -0
  7. package/plugin/web.js +422 -124
  8. package/src/Accordion/Accordion.native.tsx +182 -0
  9. package/src/Accordion/Accordion.styles.tsx +260 -0
  10. package/src/Accordion/Accordion.web.tsx +147 -0
  11. package/src/Accordion/index.native.tsx +3 -0
  12. package/src/Accordion/index.ts +3 -0
  13. package/src/Accordion/index.web.tsx +3 -0
  14. package/src/Accordion/types.ts +23 -0
  15. package/src/ActivityIndicator/ActivityIndicator.native.tsx +17 -12
  16. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +83 -109
  17. package/src/ActivityIndicator/ActivityIndicator.web.tsx +23 -17
  18. package/src/ActivityIndicator/index.ts +5 -2
  19. package/src/ActivityIndicator/index.web.ts +5 -2
  20. package/src/ActivityIndicator/types.ts +15 -10
  21. package/src/Alert/Alert.native.tsx +113 -0
  22. package/src/Alert/Alert.styles.tsx +304 -0
  23. package/src/Alert/Alert.web.tsx +123 -0
  24. package/src/Alert/index.native.ts +5 -0
  25. package/src/Alert/index.ts +5 -0
  26. package/src/Alert/index.web.ts +5 -0
  27. package/src/Alert/types.ts +21 -0
  28. package/src/Avatar/Avatar.native.tsx +8 -6
  29. package/src/Avatar/Avatar.styles.tsx +64 -58
  30. package/src/Avatar/Avatar.web.tsx +13 -8
  31. package/src/Avatar/index.ts +5 -2
  32. package/src/Avatar/index.web.ts +5 -2
  33. package/src/Avatar/types.ts +19 -13
  34. package/src/Badge/Badge.native.tsx +59 -14
  35. package/src/Badge/Badge.styles.tsx +125 -139
  36. package/src/Badge/Badge.web.tsx +72 -16
  37. package/src/Badge/index.ts +5 -2
  38. package/src/Badge/index.web.ts +5 -2
  39. package/src/Badge/types.ts +23 -11
  40. package/src/Breadcrumb/Breadcrumb.native.tsx +225 -0
  41. package/src/Breadcrumb/Breadcrumb.styles.tsx +234 -0
  42. package/src/Breadcrumb/Breadcrumb.web.tsx +268 -0
  43. package/src/Breadcrumb/index.native.ts +5 -0
  44. package/src/Breadcrumb/index.ts +5 -0
  45. package/src/Breadcrumb/index.web.ts +5 -0
  46. package/src/Breadcrumb/types.ts +56 -0
  47. package/src/Button/Button.native.tsx +75 -24
  48. package/src/Button/Button.styles.tsx +248 -205
  49. package/src/Button/Button.web.tsx +82 -25
  50. package/src/Button/index.ts +5 -5
  51. package/src/Button/index.web.ts +5 -3
  52. package/src/Button/types.ts +32 -15
  53. package/src/Card/Card.native.tsx +14 -11
  54. package/src/Card/Card.styles.tsx +146 -220
  55. package/src/Card/Card.web.tsx +20 -21
  56. package/src/Card/index.ts +5 -5
  57. package/src/Card/index.web.ts +5 -3
  58. package/src/Card/types.ts +24 -17
  59. package/src/Checkbox/Checkbox.native.tsx +24 -34
  60. package/src/Checkbox/Checkbox.styles.tsx +223 -275
  61. package/src/Checkbox/Checkbox.web.tsx +30 -37
  62. package/src/Checkbox/index.ts +5 -5
  63. package/src/Checkbox/index.web.ts +5 -3
  64. package/src/Checkbox/types.ts +26 -20
  65. package/src/Chip/Chip.native.tsx +126 -0
  66. package/src/Chip/Chip.styles.tsx +138 -0
  67. package/src/Chip/Chip.web.tsx +154 -0
  68. package/src/Chip/index.native.ts +5 -0
  69. package/src/Chip/index.ts +5 -0
  70. package/src/Chip/index.web.ts +5 -0
  71. package/src/Chip/types.ts +51 -0
  72. package/src/Dialog/Dialog.native.tsx +65 -12
  73. package/src/Dialog/Dialog.styles.tsx +154 -136
  74. package/src/Dialog/Dialog.web.tsx +16 -11
  75. package/src/Dialog/index.ts +5 -2
  76. package/src/Dialog/index.web.ts +5 -2
  77. package/src/Dialog/types.ts +22 -16
  78. package/src/Divider/Divider.native.tsx +19 -14
  79. package/src/Divider/Divider.styles.tsx +273 -595
  80. package/src/Divider/Divider.web.tsx +19 -12
  81. package/src/Divider/index.ts +5 -5
  82. package/src/Divider/index.web.ts +5 -3
  83. package/src/Divider/types.ts +28 -19
  84. package/src/Icon/Icon.native.tsx +17 -24
  85. package/src/Icon/Icon.styles.tsx +64 -48
  86. package/src/Icon/Icon.web.tsx +14 -11
  87. package/src/Icon/IconSvg/IconSvg.native.tsx +42 -0
  88. package/src/Icon/IconSvg/IconSvg.web.tsx +40 -0
  89. package/src/Icon/IconSvg/index.native.ts +1 -0
  90. package/src/Icon/IconSvg/index.ts +1 -0
  91. package/src/Icon/icon-resolver.native.ts +27 -0
  92. package/src/Icon/icon-resolver.ts +70 -0
  93. package/src/Icon/index.ts +5 -5
  94. package/src/Icon/index.web.ts +5 -3
  95. package/src/Icon/types.ts +17 -11
  96. package/src/Image/Image.native.tsx +86 -0
  97. package/src/Image/Image.styles.tsx +57 -0
  98. package/src/Image/Image.web.tsx +92 -0
  99. package/src/Image/index.native.ts +5 -0
  100. package/src/Image/index.ts +5 -0
  101. package/src/Image/types.ts +21 -0
  102. package/src/Input/Input.native.tsx +103 -26
  103. package/src/Input/Input.styles.tsx +240 -177
  104. package/src/Input/Input.web.tsx +141 -38
  105. package/src/Input/index.ts +5 -5
  106. package/src/Input/index.web.ts +5 -3
  107. package/src/Input/types.ts +43 -20
  108. package/src/List/List.native.tsx +56 -0
  109. package/src/List/List.styles.tsx +257 -0
  110. package/src/List/List.web.tsx +43 -0
  111. package/src/List/ListContext.tsx +16 -0
  112. package/src/List/ListItem.native.tsx +111 -0
  113. package/src/List/ListItem.web.tsx +110 -0
  114. package/src/List/ListSection.native.tsx +31 -0
  115. package/src/List/ListSection.web.tsx +33 -0
  116. package/src/List/index.native.tsx +5 -0
  117. package/src/List/index.ts +5 -0
  118. package/src/List/index.web.tsx +5 -0
  119. package/src/List/types.ts +42 -0
  120. package/src/Menu/Menu.native.tsx +150 -0
  121. package/src/Menu/Menu.styles.tsx +185 -0
  122. package/src/Menu/Menu.web.tsx +99 -0
  123. package/src/Menu/MenuItem.native.tsx +66 -0
  124. package/src/Menu/MenuItem.styles.tsx +119 -0
  125. package/src/Menu/MenuItem.web.tsx +67 -0
  126. package/src/Menu/index.native.ts +3 -0
  127. package/src/Menu/index.ts +3 -0
  128. package/src/Menu/index.web.ts +3 -0
  129. package/src/Menu/types.ts +30 -0
  130. package/src/Popover/Popover.native.tsx +102 -32
  131. package/src/Popover/Popover.styles.tsx +100 -67
  132. package/src/Popover/Popover.web.tsx +36 -260
  133. package/src/Popover/index.ts +5 -2
  134. package/src/Popover/index.web.ts +5 -2
  135. package/src/Popover/types.ts +14 -13
  136. package/src/Pressable/Pressable.native.tsx +7 -6
  137. package/src/Pressable/Pressable.web.tsx +8 -6
  138. package/src/Pressable/index.ts +5 -2
  139. package/src/Pressable/index.web.ts +5 -2
  140. package/src/Pressable/types.ts +11 -10
  141. package/src/Progress/Progress.native.tsx +179 -0
  142. package/src/Progress/Progress.styles.tsx +164 -0
  143. package/src/Progress/Progress.web.tsx +144 -0
  144. package/src/Progress/index.native.ts +1 -0
  145. package/src/Progress/index.ts +5 -0
  146. package/src/Progress/index.web.ts +5 -0
  147. package/src/Progress/types.ts +21 -0
  148. package/src/RadioButton/RadioButton.native.tsx +88 -0
  149. package/src/RadioButton/RadioButton.styles.tsx +163 -0
  150. package/src/RadioButton/RadioButton.web.tsx +85 -0
  151. package/src/RadioButton/RadioGroup.native.tsx +43 -0
  152. package/src/RadioButton/RadioGroup.web.tsx +49 -0
  153. package/src/RadioButton/index.native.ts +2 -0
  154. package/src/RadioButton/index.ts +2 -0
  155. package/src/RadioButton/index.web.ts +2 -0
  156. package/src/RadioButton/types.ts +29 -0
  157. package/src/SVGImage/SVGImage.native.tsx +9 -7
  158. package/src/SVGImage/SVGImage.styles.tsx +63 -55
  159. package/src/SVGImage/SVGImage.web.tsx +16 -13
  160. package/src/SVGImage/index.ts +5 -5
  161. package/src/SVGImage/index.web.ts +5 -2
  162. package/src/SVGImage/types.ts +7 -3
  163. package/src/Screen/Screen.native.tsx +43 -17
  164. package/src/Screen/Screen.styles.tsx +58 -54
  165. package/src/Screen/Screen.web.tsx +11 -5
  166. package/src/Screen/index.ts +5 -2
  167. package/src/Screen/index.web.ts +5 -2
  168. package/src/Screen/types.ts +23 -9
  169. package/src/Select/Select.native.tsx +347 -0
  170. package/src/Select/Select.styles.tsx +335 -0
  171. package/src/Select/Select.web.tsx +276 -0
  172. package/src/Select/index.native.ts +2 -0
  173. package/src/Select/index.ts +5 -0
  174. package/src/Select/index.web.ts +5 -0
  175. package/src/Select/types.ts +124 -0
  176. package/src/Skeleton/Skeleton.native.tsx +139 -0
  177. package/src/Skeleton/Skeleton.styles.tsx +59 -0
  178. package/src/Skeleton/Skeleton.web.tsx +112 -0
  179. package/src/Skeleton/index.native.ts +4 -0
  180. package/src/Skeleton/index.ts +5 -0
  181. package/src/Skeleton/index.web.ts +5 -0
  182. package/src/Skeleton/types.ts +75 -0
  183. package/src/Slider/Slider.native.tsx +248 -0
  184. package/src/Slider/Slider.styles.tsx +241 -0
  185. package/src/Slider/Slider.web.tsx +226 -0
  186. package/src/Slider/index.native.ts +3 -0
  187. package/src/Slider/index.ts +5 -0
  188. package/src/Slider/index.web.ts +5 -0
  189. package/src/Slider/types.ts +31 -0
  190. package/src/Switch/Switch.native.tsx +131 -0
  191. package/src/Switch/Switch.styles.tsx +169 -0
  192. package/src/Switch/Switch.web.tsx +121 -0
  193. package/src/Switch/index.native.ts +3 -0
  194. package/src/Switch/index.ts +5 -0
  195. package/src/Switch/index.web.ts +5 -0
  196. package/src/Switch/types.ts +21 -0
  197. package/src/TabBar/TabBar.native.tsx +142 -0
  198. package/src/TabBar/TabBar.styles.tsx +399 -0
  199. package/src/TabBar/TabBar.web.tsx +205 -0
  200. package/src/TabBar/index.native.tsx +3 -0
  201. package/src/TabBar/index.ts +3 -0
  202. package/src/TabBar/index.web.tsx +3 -0
  203. package/src/TabBar/types.ts +26 -0
  204. package/src/Table/Table.native.tsx +122 -0
  205. package/src/Table/Table.styles.tsx +283 -0
  206. package/src/Table/Table.web.tsx +112 -0
  207. package/src/Table/index.native.tsx +3 -0
  208. package/src/Table/index.ts +3 -0
  209. package/src/Table/index.web.tsx +3 -0
  210. package/src/Table/types.ts +28 -0
  211. package/src/Text/Text.native.tsx +12 -11
  212. package/src/Text/Text.styles.tsx +76 -64
  213. package/src/Text/Text.web.tsx +14 -9
  214. package/src/Text/index.ts +5 -5
  215. package/src/Text/index.web.ts +5 -3
  216. package/src/Text/types.ts +20 -13
  217. package/src/TextArea/TextArea.native.tsx +134 -0
  218. package/src/TextArea/TextArea.styles.tsx +175 -0
  219. package/src/TextArea/TextArea.web.tsx +156 -0
  220. package/src/TextArea/index.native.ts +3 -0
  221. package/src/TextArea/index.ts +3 -0
  222. package/src/TextArea/index.web.ts +3 -0
  223. package/src/TextArea/types.ts +30 -0
  224. package/src/Tooltip/Tooltip.native.tsx +165 -0
  225. package/src/Tooltip/Tooltip.styles.tsx +73 -0
  226. package/src/Tooltip/Tooltip.web.tsx +87 -0
  227. package/src/Tooltip/index.native.ts +3 -0
  228. package/src/Tooltip/index.ts +3 -0
  229. package/src/Tooltip/types.ts +18 -0
  230. package/src/Video/Video.native.tsx +105 -0
  231. package/src/Video/Video.styles.tsx +39 -0
  232. package/src/Video/Video.web.tsx +115 -0
  233. package/src/Video/index.native.ts +5 -0
  234. package/src/Video/index.ts +5 -0
  235. package/src/Video/types.ts +29 -0
  236. package/src/View/View.native.tsx +9 -14
  237. package/src/View/View.styles.tsx +101 -93
  238. package/src/View/View.web.tsx +16 -17
  239. package/src/View/index.ts +5 -5
  240. package/src/View/index.web.ts +5 -3
  241. package/src/View/types.ts +29 -21
  242. package/src/examples/AccordionExamples.tsx +126 -0
  243. package/src/examples/AlertExamples.tsx +280 -0
  244. package/src/examples/AvatarExamples.tsx +23 -23
  245. package/src/examples/BadgeExamples.tsx +109 -41
  246. package/src/examples/BreadcrumbExamples.tsx +312 -0
  247. package/src/examples/ButtonExamples.tsx +160 -33
  248. package/src/examples/CardExamples.tsx +40 -40
  249. package/src/examples/CheckboxExamples.tsx +12 -12
  250. package/src/examples/ChipExamples.tsx +197 -0
  251. package/src/examples/DialogExamples.tsx +22 -22
  252. package/src/examples/DividerExamples.tsx +49 -49
  253. package/src/examples/IconExamples.tsx +270 -54
  254. package/src/examples/ImageExamples.tsx +174 -0
  255. package/src/examples/InputExamples.tsx +75 -17
  256. package/src/examples/ListExamples.tsx +288 -0
  257. package/src/examples/MenuExamples.tsx +144 -0
  258. package/src/examples/PopoverExamples.tsx +69 -73
  259. package/src/examples/ProgressExamples.tsx +137 -0
  260. package/src/examples/RadioButtonExamples.tsx +161 -0
  261. package/src/examples/SVGImageExamples.tsx +19 -17
  262. package/src/examples/ScreenExamples.tsx +31 -31
  263. package/src/examples/SelectExamples.tsx +423 -0
  264. package/src/examples/SkeletonExamples.tsx +206 -0
  265. package/src/examples/SliderExamples.tsx +200 -0
  266. package/src/examples/SwitchExamples.tsx +182 -0
  267. package/src/examples/TabBarExamples.tsx +143 -0
  268. package/src/examples/TableExamples.tsx +280 -0
  269. package/src/examples/TextAreaExamples.tsx +173 -0
  270. package/src/examples/TextExamples.tsx +28 -32
  271. package/src/examples/ThemeExtensionExamples.tsx +10 -10
  272. package/src/examples/TooltipExamples.tsx +126 -0
  273. package/src/examples/VideoExamples.tsx +144 -0
  274. package/src/examples/ViewExamples.tsx +64 -56
  275. package/src/examples/index.ts +18 -3
  276. package/src/hooks/useMergeRefs.ts +16 -0
  277. package/src/hooks/useSmartPosition.native.ts +169 -0
  278. package/src/index.native.ts +80 -9
  279. package/src/index.ts +75 -1
  280. package/src/internal/BoundedModalContent.native.tsx +58 -0
  281. package/src/internal/PositionedPortal.tsx +254 -0
  282. package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
  283. package/src/unistyles.d.ts +6 -0
  284. package/src/utils/buildSizeVariants.ts +16 -0
  285. package/src/utils/deepMerge.ts +43 -0
  286. package/src/utils/positionUtils.native.ts +280 -0
  287. package/src/utils/styleHelpers.ts +48 -0
  288. package/LLM-ACCESS-GUIDE.md +0 -143
  289. package/src/ActivityIndicator/README.md +0 -132
  290. package/src/Avatar/README.md +0 -139
  291. package/src/Badge/README.md +0 -170
  292. package/src/Button/Button.types.ts +0 -12
  293. package/src/Button/README.md +0 -262
  294. package/src/Card/README.md +0 -258
  295. package/src/Checkbox/README.md +0 -102
  296. package/src/Dialog/README.md +0 -210
  297. package/src/Divider/README.md +0 -108
  298. package/src/Icon/README.md +0 -81
  299. package/src/Input/README.md +0 -100
  300. package/src/SVGImage/README.md +0 -209
  301. package/src/Screen/README.md +0 -86
  302. package/src/Text/README.md +0 -94
  303. package/src/View/README.md +0 -107
  304. package/src/examples/AllExamples.tsx +0 -84
  305. package/src/examples/README.md +0 -136
  306. package/src/examples/ValidationExamples.tsx +0 -95
  307. package/src/examples/extendedTheme.ts +0 -329
  308. package/src/theme/breakpoints.ts +0 -8
  309. package/src/theme/colorResolver.ts +0 -218
  310. package/src/theme/colors.ts +0 -315
  311. package/src/theme/defaultThemes.ts +0 -326
  312. package/src/theme/index.ts +0 -188
  313. package/src/theme/themeBuilder.ts +0 -602
  314. package/src/theme/unistyles.d.ts +0 -6
  315. package/src/theme/variantHelpers.ts +0 -584
  316. package/src/theme/variants.ts +0 -56
@@ -0,0 +1,179 @@
1
+ import React, { useEffect, forwardRef } from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ useSharedValue,
5
+ useAnimatedStyle,
6
+ useAnimatedProps,
7
+ withTiming,
8
+ withRepeat,
9
+ Easing,
10
+ } from 'react-native-reanimated';
11
+ import { useUnistyles } from 'react-native-unistyles';
12
+ import Svg, { Circle } from 'react-native-svg';
13
+ import Text from '../Text';
14
+ import { progressStyles } from './Progress.styles';
15
+ import type { ProgressProps } from './types';
16
+ import { Theme } from '@idealyst/theme';
17
+
18
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
19
+
20
+ const Progress = forwardRef<View, ProgressProps>(({
21
+ value = 0,
22
+ max = 100,
23
+ variant = 'linear',
24
+ intent = 'primary',
25
+ size = 'md',
26
+ indeterminate = false,
27
+ showLabel = false,
28
+ label,
29
+ rounded = true,
30
+ style,
31
+ testID,
32
+ }, ref) => {
33
+ const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
34
+ const { theme }: { theme: Theme } = useUnistyles();
35
+
36
+ // Apply variants
37
+ progressStyles.useVariants({
38
+ size,
39
+ intent,
40
+ rounded,
41
+ });
42
+
43
+ // Animation values
44
+ const animatedValue = useSharedValue(0);
45
+ const slideAnimation = useSharedValue(0);
46
+ const rotateAnimation = useSharedValue(0);
47
+
48
+ useEffect(() => {
49
+ if (indeterminate) {
50
+ // Indeterminate animation
51
+ slideAnimation.value = withRepeat(
52
+ withTiming(1, { duration: 1500, easing: Easing.inOut(Easing.ease) }),
53
+ -1,
54
+ false
55
+ );
56
+ rotateAnimation.value = withRepeat(
57
+ withTiming(1, { duration: 1400, easing: Easing.linear }),
58
+ -1,
59
+ false
60
+ );
61
+ } else {
62
+ // Determinate animation
63
+ animatedValue.value = withTiming(percentage, {
64
+ duration: 300,
65
+ easing: Easing.out(Easing.ease),
66
+ });
67
+ }
68
+ }, [percentage, indeterminate]);
69
+
70
+ const getCircularSize = () => {
71
+ if (size === 'sm') return 32;
72
+ if (size === 'lg') return 64;
73
+ return 48;
74
+ };
75
+
76
+ if (variant === 'circular') {
77
+ const circularSize = getCircularSize();
78
+ const strokeWidth = size === 'sm' ? 3 : size === 'lg' ? 5 : 4;
79
+ const radius = (circularSize - strokeWidth) / 2;
80
+ const circumference = radius * 2 * Math.PI;
81
+
82
+ // Get colors from theme
83
+ const trackColor = theme.colors.border.secondary;
84
+ const barColor = theme.intents[intent].primary;
85
+
86
+ const circularAnimatedProps = useAnimatedProps(() => {
87
+ const offset = indeterminate
88
+ ? circumference * 0.25
89
+ : circumference - (animatedValue.value / 100) * circumference;
90
+
91
+ return {
92
+ strokeDashoffset: offset,
93
+ };
94
+ });
95
+
96
+ const rotationStyle = useAnimatedStyle(() => {
97
+ return {
98
+ transform: [
99
+ {
100
+ rotate: `${rotateAnimation.value * 360}deg`,
101
+ },
102
+ ],
103
+ };
104
+ });
105
+
106
+ return (
107
+ <View style={[progressStyles.circularContainer, style]} testID={testID}>
108
+ <Animated.View style={indeterminate ? rotationStyle : {}}>
109
+ <Svg width={circularSize} height={circularSize} style={{ transform: [{ rotate: '-90deg' }] }}>
110
+ {/* Track circle (background) */}
111
+ <Circle
112
+ cx={circularSize / 2}
113
+ cy={circularSize / 2}
114
+ r={radius}
115
+ strokeWidth={strokeWidth}
116
+ fill="none"
117
+ stroke={trackColor}
118
+ />
119
+ {/* Progress circle (foreground) */}
120
+ <AnimatedCircle
121
+ cx={circularSize / 2}
122
+ cy={circularSize / 2}
123
+ r={radius}
124
+ strokeWidth={strokeWidth}
125
+ fill="none"
126
+ stroke={barColor}
127
+ strokeDasharray={`${circumference} ${circumference}`}
128
+ animatedProps={circularAnimatedProps}
129
+ strokeLinecap="round"
130
+ />
131
+ </Svg>
132
+ </Animated.View>
133
+ {showLabel && (
134
+ <Text style={progressStyles.circularLabel}>
135
+ {label || `${Math.round(percentage)}%`}
136
+ </Text>
137
+ )}
138
+ </View>
139
+ );
140
+ }
141
+
142
+ // Linear progress
143
+ const barAnimatedStyle = useAnimatedStyle(() => {
144
+ return {
145
+ width: `${animatedValue.value}%`,
146
+ };
147
+ });
148
+
149
+ const indeterminateAnimatedStyle = useAnimatedStyle(() => {
150
+ return {
151
+ transform: [
152
+ {
153
+ translateX: slideAnimation.value * 600 - 200,
154
+ },
155
+ ],
156
+ };
157
+ });
158
+
159
+ return (
160
+ <View ref={ref} style={[progressStyles.container, style]} testID={testID} accessibilityRole="progressbar">
161
+ <View style={progressStyles.linearTrack}>
162
+ {indeterminate ? (
163
+ <Animated.View style={[progressStyles.indeterminateBar, indeterminateAnimatedStyle]} />
164
+ ) : (
165
+ <Animated.View style={[progressStyles.linearBar, barAnimatedStyle]} />
166
+ )}
167
+ </View>
168
+ {showLabel && (
169
+ <Text style={progressStyles.label}>
170
+ {label || `${Math.round(percentage)}%`}
171
+ </Text>
172
+ )}
173
+ </View>
174
+ );
175
+ });
176
+
177
+ Progress.displayName = 'Progress';
178
+
179
+ export default Progress;
@@ -0,0 +1,164 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, Intent, Size, CompoundVariants, StaticStyles } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+
5
+ type ProgressSize = Size;
6
+ type ProgressIntent = Intent;
7
+
8
+ export type ProgressVariants = {
9
+ size: ProgressSize;
10
+ intent: ProgressIntent;
11
+ rounded: boolean;
12
+ }
13
+
14
+ function createLinearTrackSizeVariants(theme: Theme) {
15
+ return buildSizeVariants(theme, 'progress', (size) => ({
16
+ height: size.linearHeight,
17
+ }));
18
+ }
19
+
20
+ function createCircularContainerSizeVariants(theme: Theme) {
21
+ return buildSizeVariants(theme, 'progress', (size) => ({
22
+ width: size.circularSize,
23
+ height: size.circularSize,
24
+ }));
25
+ }
26
+
27
+ function createLabelSizeVariants(theme: Theme) {
28
+ return buildSizeVariants(theme, 'progress', (size) => ({
29
+ fontSize: size.labelFontSize,
30
+ }));
31
+ }
32
+
33
+ function createCircularLabelSizeVariants(theme: Theme) {
34
+ return buildSizeVariants(theme, 'progress', (size) => ({
35
+ fontSize: size.circularLabelFontSize,
36
+ }));
37
+ }
38
+
39
+ function createIntentVariants(theme: Theme) {
40
+ const variants: any = {};
41
+ for (const intent in theme.intents) {
42
+ variants[intent] = {};
43
+ }
44
+ return variants;
45
+ }
46
+
47
+ function createLinearBarCompoundVariants(theme: Theme) {
48
+ const compoundVariants: CompoundVariants<keyof ProgressVariants> = [];
49
+
50
+ for (const intent in theme.intents) {
51
+ const intentValue = theme.intents[intent as Intent];
52
+
53
+ compoundVariants.push({
54
+ intent,
55
+ styles: {
56
+ backgroundColor: intentValue.primary,
57
+ },
58
+ });
59
+ }
60
+
61
+ return compoundVariants;
62
+ }
63
+
64
+ function createCircularBarCompoundVariants(theme: Theme) {
65
+ const compoundVariants: CompoundVariants<keyof ProgressVariants> = [];
66
+
67
+ for (const intent in theme.intents) {
68
+ const intentValue = theme.intents[intent as Intent];
69
+
70
+ compoundVariants.push({
71
+ intent,
72
+ styles: {
73
+ _web: {
74
+ stroke: intentValue.primary,
75
+ },
76
+ },
77
+ });
78
+ }
79
+
80
+ return compoundVariants;
81
+ }
82
+
83
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
84
+ // transform on native cannot resolve function calls to extract variant structures.
85
+ export const progressStyles = StyleSheet.create((theme: Theme) => {
86
+ return {
87
+ container: {
88
+ gap: 4 as const,
89
+ },
90
+ linearTrack: {
91
+ backgroundColor: theme.colors.border.secondary,
92
+ overflow: 'hidden' as const,
93
+ position: 'relative' as const,
94
+ variants: {
95
+ size: createLinearTrackSizeVariants(theme),
96
+ rounded: {
97
+ true: { borderRadius: 9999 },
98
+ false: { borderRadius: 0 },
99
+ },
100
+ },
101
+ },
102
+ linearBar: {
103
+ height: '100%' as const,
104
+ variants: {
105
+ intent: createIntentVariants(theme),
106
+ rounded: {
107
+ true: { borderRadius: 9999 },
108
+ false: { borderRadius: 0 },
109
+ },
110
+ },
111
+ compoundVariants: createLinearBarCompoundVariants(theme),
112
+ _web: {
113
+ transition: 'width 0.3s ease' as const,
114
+ },
115
+ },
116
+ indeterminateBar: {
117
+ position: 'absolute' as const,
118
+ height: '100%' as const,
119
+ width: '40%' as const,
120
+ variants: {
121
+ intent: createIntentVariants(theme),
122
+ rounded: {
123
+ true: { borderRadius: 9999 },
124
+ false: { borderRadius: 0 },
125
+ },
126
+ },
127
+ compoundVariants: createLinearBarCompoundVariants(theme),
128
+ },
129
+ circularContainer: {
130
+ alignItems: 'center' as const,
131
+ justifyContent: 'center' as const,
132
+ position: 'relative' as const,
133
+ variants: {
134
+ size: createCircularContainerSizeVariants(theme),
135
+ } as const,
136
+ } as const,
137
+ circularTrack: {
138
+ _web: {
139
+ stroke: theme.colors.border.secondary,
140
+ }
141
+ },
142
+ circularBar: {
143
+ variants: {
144
+ intent: createIntentVariants(theme),
145
+ },
146
+ compoundVariants: createCircularBarCompoundVariants(theme),
147
+ },
148
+ label: {
149
+ color: theme.colors.text.primary,
150
+ textAlign: 'center' as const,
151
+ variants: {
152
+ size: createLabelSizeVariants(theme),
153
+ },
154
+ },
155
+ circularLabel: {
156
+ position: 'absolute' as const,
157
+ fontWeight: '600' as const,
158
+ color: theme.colors.text.primary,
159
+ variants: {
160
+ size: createCircularLabelSizeVariants(theme),
161
+ },
162
+ },
163
+ };
164
+ });
@@ -0,0 +1,144 @@
1
+ import React from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { progressStyles } from './Progress.styles';
4
+ import type { ProgressProps } from './types';
5
+
6
+ const Progress: React.FC<ProgressProps> = ({
7
+ value = 0,
8
+ max = 100,
9
+ variant = 'linear',
10
+ intent = 'primary',
11
+ size = 'md',
12
+ indeterminate = false,
13
+ showLabel = false,
14
+ label,
15
+ rounded = true,
16
+ style,
17
+ testID,
18
+ }) => {
19
+ const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
20
+
21
+ // Apply variants using the correct Unistyles v3 pattern
22
+ progressStyles.useVariants({
23
+ size,
24
+ intent,
25
+ rounded,
26
+ });
27
+
28
+ // Linear progress
29
+ const containerProps = getWebProps([progressStyles.container, style as any]);
30
+ const trackProps = getWebProps([progressStyles.linearTrack]);
31
+ const barProps = getWebProps([progressStyles.linearBar, { width: `${percentage}%` }]);
32
+ const indeterminateProps = getWebProps([progressStyles.indeterminateBar]);
33
+ const labelProps = getWebProps([progressStyles.label]);
34
+
35
+ const getCircularSize = () => {
36
+ if (size === 'sm') return 32;
37
+ if (size === 'lg') return 64;
38
+ return 48;
39
+ };
40
+
41
+ if (variant === 'circular') {
42
+ const circularSize = getCircularSize();
43
+ const strokeWidth = size === 'sm' ? 3 : size === 'lg' ? 5 : 4;
44
+ const radius = (circularSize - strokeWidth) / 2;
45
+ const circumference = radius * 2 * Math.PI;
46
+ const strokeDashoffset = indeterminate ? circumference * 0.25 : circumference - (percentage / 100) * circumference;
47
+
48
+ const computedContainerProps = getWebProps([
49
+ progressStyles.circularContainer,
50
+ style,
51
+ { display: 'inline-flex' }
52
+ ]);
53
+ const labelProps = getWebProps([progressStyles.circularLabel]);
54
+ const trackColorProps = getWebProps([progressStyles.circularTrack]);
55
+ const barColorProps = getWebProps([progressStyles.circularBar]);
56
+ console.log(trackColorProps)
57
+
58
+ return (
59
+ <div {...computedContainerProps} data-testid={testID}>
60
+ <svg
61
+ width={circularSize}
62
+ height={circularSize}
63
+ style={{ transform: 'rotate(-90deg)' }}
64
+ className={indeterminate ? 'progress-circular-container-indeterminate' : ''}
65
+ >
66
+ {/* Track circle (background) */}
67
+ <circle
68
+ cx={circularSize / 2}
69
+ cy={circularSize / 2}
70
+ r={radius}
71
+ strokeWidth={strokeWidth}
72
+ fill="none"
73
+ className={trackColorProps.className}
74
+ />
75
+ {/* Progress circle (foreground) - apply the className to get intent color */}
76
+ <circle
77
+ cx={circularSize / 2}
78
+ cy={circularSize / 2}
79
+ r={radius}
80
+ strokeWidth={strokeWidth}
81
+ fill="none"
82
+ className={barColorProps.className}
83
+ strokeDasharray={`${circumference} ${circumference}`}
84
+ strokeDashoffset={strokeDashoffset}
85
+ strokeLinecap="round"
86
+ style={{ transition: 'stroke-dashoffset 0.3s ease' }}
87
+ />
88
+ </svg>
89
+ {showLabel && (
90
+ <span {...labelProps}>
91
+ {label || `${Math.round(percentage)}%`}
92
+ </span>
93
+ )}
94
+ <style>{`
95
+ @keyframes circular-rotate {
96
+ from { transform: rotate(-90deg); }
97
+ to { transform: rotate(270deg); }
98
+ }
99
+ .progress-circular-container-indeterminate {
100
+ animation: circular-rotate 1.4s linear infinite;
101
+ }
102
+ `}</style>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ return (
108
+ <>
109
+ <div {...containerProps} data-testid={testID}>
110
+ <div
111
+ {...trackProps}
112
+ role="progressbar"
113
+ aria-valuenow={value}
114
+ aria-valuemin={0}
115
+ aria-valuemax={max}
116
+ >
117
+ {indeterminate ? (
118
+ <div {...indeterminateProps} className={`${indeterminateProps.className} progress-linear-indeterminate`} />
119
+ ) : (
120
+ <div {...barProps} />
121
+ )}
122
+ </div>
123
+ {showLabel && (
124
+ <span {...labelProps}>
125
+ {label || `${Math.round(percentage)}%`}
126
+ </span>
127
+ )}
128
+ </div>
129
+ {indeterminate && (
130
+ <style>{`
131
+ @keyframes progress-slide {
132
+ 0% { transform: translateX(-100%); }
133
+ 100% { transform: translateX(350%); }
134
+ }
135
+ .progress-linear-indeterminate {
136
+ animation: progress-slide 1.5s ease-in-out infinite;
137
+ }
138
+ `}</style>
139
+ )}
140
+ </>
141
+ );
142
+ };
143
+
144
+ export default Progress;
@@ -0,0 +1 @@
1
+ export { default } from './Progress.native';
@@ -0,0 +1,5 @@
1
+ import ProgressComponent from './Progress.web';
2
+
3
+ export default ProgressComponent;
4
+ export { ProgressComponent as Progress };
5
+ export * from './types';
@@ -0,0 +1,5 @@
1
+ import ProgressComponent from './Progress.web';
2
+
3
+ export default ProgressComponent;
4
+ export { ProgressComponent as Progress };
5
+ export * from './types';
@@ -0,0 +1,21 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ import type { Intent, Size } from '@idealyst/theme';
3
+
4
+ // Component-specific type aliases for future extensibility
5
+ export type ProgressIntentVariant = Intent;
6
+ export type ProgressSizeVariant = Size;
7
+ export type ProgressVariant = 'linear' | 'circular';
8
+
9
+ export interface ProgressProps {
10
+ value?: number;
11
+ max?: number;
12
+ variant?: ProgressVariant;
13
+ intent?: ProgressIntentVariant;
14
+ size?: ProgressSizeVariant;
15
+ indeterminate?: boolean;
16
+ showLabel?: boolean;
17
+ label?: string;
18
+ rounded?: boolean;
19
+ style?: StyleProp<ViewStyle>;
20
+ testID?: string;
21
+ }
@@ -0,0 +1,88 @@
1
+ import React, { ComponentRef, forwardRef } from 'react';
2
+ import { View, Pressable, Animated } from 'react-native';
3
+ import Text from '../Text';
4
+ import { radioButtonStyles } from './RadioButton.styles';
5
+ import type { RadioButtonProps } from './types';
6
+ import { useRadioGroup } from './RadioGroup.native';
7
+
8
+ const RadioButton = forwardRef<ComponentRef<typeof Pressable>, RadioButtonProps>(({
9
+ value,
10
+ checked: checkedProp,
11
+ onPress,
12
+ disabled: disabledProp = false,
13
+ label,
14
+ size = 'md',
15
+ intent = 'primary',
16
+ style,
17
+ testID,
18
+ }, ref) => {
19
+ const group = useRadioGroup();
20
+
21
+ const checked = group.value !== undefined ? group.value === value : checkedProp;
22
+ const disabled = group.disabled || disabledProp;
23
+
24
+ const animatedValue = React.useRef(new Animated.Value(checked ? 1 : 0)).current;
25
+
26
+ React.useEffect(() => {
27
+ Animated.spring(animatedValue, {
28
+ toValue: checked ? 1 : 0,
29
+ useNativeDriver: true,
30
+ friction: 8,
31
+ tension: 100,
32
+ }).start();
33
+ }, [checked, animatedValue]);
34
+
35
+ const handlePress = () => {
36
+ if (!disabled) {
37
+ if (group.onValueChange) {
38
+ group.onValueChange(value);
39
+ } else if (onPress) {
40
+ onPress();
41
+ }
42
+ }
43
+ };
44
+
45
+ // Apply variants for radio styles
46
+ radioButtonStyles.useVariants({
47
+ size,
48
+ checked,
49
+ disabled,
50
+ });
51
+
52
+ const dotScale = animatedValue.interpolate({
53
+ inputRange: [0, 1],
54
+ outputRange: [0, 1],
55
+ });
56
+
57
+ return (
58
+ <Pressable
59
+ ref={ref}
60
+ onPress={handlePress}
61
+ disabled={disabled}
62
+ style={[radioButtonStyles.container, style]}
63
+ testID={testID}
64
+ accessibilityRole="radio"
65
+ accessibilityState={{ checked, disabled }}
66
+ >
67
+ <View style={radioButtonStyles.radio({ intent })}>
68
+ <Animated.View
69
+ style={[
70
+ radioButtonStyles.radioDot({ intent }),
71
+ {
72
+ transform: [{ scale: dotScale }],
73
+ },
74
+ ]}
75
+ />
76
+ </View>
77
+ {label && (
78
+ <Text style={radioButtonStyles.label}>
79
+ {label}
80
+ </Text>
81
+ )}
82
+ </Pressable>
83
+ );
84
+ });
85
+
86
+ RadioButton.displayName = 'RadioButton';
87
+
88
+ export default RadioButton;