@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,163 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, Intent, Size} from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+
5
+ type RadioButtonSize = Size;
6
+ type RadioButtonIntent = Intent;
7
+ type RadioGroupOrientation = 'horizontal' | 'vertical';
8
+
9
+ type RadioButtonVariants = {
10
+ size: RadioButtonSize;
11
+ intent: RadioButtonIntent;
12
+ checked: boolean;
13
+ disabled: boolean;
14
+ }
15
+
16
+ type RadioGroupVariants = {
17
+ orientation: RadioGroupOrientation;
18
+ }
19
+
20
+ export type ExpandedRadioButtonStyles = StylesheetStyles<keyof RadioButtonVariants>;
21
+ export type ExpandedRadioGroupStyles = StylesheetStyles<keyof RadioGroupVariants>;
22
+
23
+ export type RadioButtonStylesheet = {
24
+ container: ExpandedRadioButtonStyles;
25
+ radio: ExpandedRadioButtonStyles;
26
+ radioDot: ExpandedRadioButtonStyles;
27
+ label: ExpandedRadioButtonStyles;
28
+ groupContainer: ExpandedRadioGroupStyles;
29
+ }
30
+
31
+ function createRadioSizeVariants(theme: Theme) {
32
+ return buildSizeVariants(theme, 'radioButton', (size) => ({
33
+ width: size.radioSize,
34
+ height: size.radioSize,
35
+ }));
36
+ }
37
+
38
+ function createCheckedVariants(theme: Theme, intent: RadioButtonIntent) {
39
+ const intentValue = theme.intents[intent];
40
+ return {
41
+ true: {
42
+ borderColor: intentValue.primary,
43
+ },
44
+ false: {
45
+ borderColor: theme.colors.border.primary,
46
+ },
47
+ } as const;
48
+ }
49
+
50
+ function createRadioDotSizeVariants(theme: Theme) {
51
+ return buildSizeVariants(theme, 'radioButton', (size) => ({
52
+ width: size.radioDotSize,
53
+ height: size.radioDotSize,
54
+ }));
55
+ }
56
+
57
+ function createRadioDotIntentColor(theme: Theme, intent: RadioButtonIntent) {
58
+ return theme.intents[intent].primary;
59
+ }
60
+
61
+ function createRadioStyles(theme: Theme) {
62
+ return ({ intent }: Partial<RadioButtonVariants>) => {
63
+ return {
64
+ borderRadius: 9999,
65
+ borderWidth: 1.5,
66
+ borderStyle: 'solid',
67
+ alignItems: 'center',
68
+ justifyContent: 'center',
69
+ backgroundColor: theme.colors.surface.primary,
70
+ variants: {
71
+ size: createRadioSizeVariants(theme),
72
+ checked: createCheckedVariants(theme, intent),
73
+ disabled: {
74
+ true: {
75
+ opacity: 0.5,
76
+ backgroundColor: theme.colors.surface.tertiary,
77
+ _web: {
78
+ cursor: 'not-allowed',
79
+ },
80
+ },
81
+ false: {
82
+ opacity: 1,
83
+ backgroundColor: theme.colors.surface.primary,
84
+ _web: {
85
+ cursor: 'pointer',
86
+ _hover: {
87
+ opacity: 0.8,
88
+ },
89
+ _active: {
90
+ opacity: 0.6,
91
+ },
92
+ },
93
+ },
94
+ },
95
+ },
96
+ _web: {
97
+ transition: 'all 0.2s ease',
98
+ },
99
+ } as const;
100
+ }
101
+ }
102
+
103
+ function createRadioDotStyles(theme: Theme) {
104
+ return ({ intent }: Partial<RadioButtonVariants>) => {
105
+ return {
106
+ borderRadius: 9999,
107
+ backgroundColor: createRadioDotIntentColor(theme, intent),
108
+ variants: {
109
+ size: createRadioDotSizeVariants(theme),
110
+ },
111
+ } as const;
112
+ }
113
+ }
114
+
115
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
116
+ // transform on native cannot resolve function calls to extract variant structures.
117
+ export const radioButtonStyles = StyleSheet.create((theme: Theme) => {
118
+ return {
119
+ container: {
120
+ flexDirection: 'row',
121
+ alignItems: 'center',
122
+ paddingVertical: 4,
123
+ variants: {
124
+ size: buildSizeVariants(theme, 'radioButton', (size) => ({
125
+ gap: size.gap,
126
+ })),
127
+ } as const,
128
+ },
129
+ radio: createRadioStyles(theme),
130
+ radioDot: createRadioDotStyles(theme),
131
+ label: {
132
+ color: theme.colors.text.primary,
133
+ variants: {
134
+ size: buildSizeVariants(theme, 'radioButton', (size) => ({
135
+ fontSize: size.fontSize,
136
+ })),
137
+ disabled: {
138
+ true: {
139
+ opacity: 0.5,
140
+ },
141
+ false: {
142
+ opacity: 1,
143
+ },
144
+ },
145
+ },
146
+ },
147
+ groupContainer: {
148
+ gap: 4,
149
+ variants: {
150
+ orientation: {
151
+ horizontal: {
152
+ flexDirection: 'row',
153
+ flexWrap: 'wrap',
154
+ gap: 16,
155
+ },
156
+ vertical: {
157
+ flexDirection: 'column',
158
+ },
159
+ },
160
+ },
161
+ },
162
+ };
163
+ });
@@ -0,0 +1,85 @@
1
+ import React from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { radioButtonStyles } from './RadioButton.styles';
4
+ import type { RadioButtonProps } from './types';
5
+ import { useRadioGroup } from './RadioGroup.web';
6
+
7
+ const RadioButton: React.FC<RadioButtonProps> = ({
8
+ value,
9
+ checked: checkedProp,
10
+ onPress,
11
+ disabled: disabledProp = false,
12
+ label,
13
+ size = 'md',
14
+ intent = 'primary',
15
+ style,
16
+ testID,
17
+ }) => {
18
+ const group = useRadioGroup();
19
+
20
+ const checked = group.value !== undefined ? group.value === value : checkedProp;
21
+ const disabled = group.disabled || disabledProp;
22
+
23
+ const handleClick = () => {
24
+ if (!disabled) {
25
+ if (group.onValueChange) {
26
+ group.onValueChange(value);
27
+ } else if (onPress) {
28
+ onPress();
29
+ }
30
+ }
31
+ };
32
+
33
+ // Apply variants using the correct Unistyles v3 pattern
34
+ radioButtonStyles.useVariants({
35
+ size,
36
+ checked,
37
+ disabled,
38
+ });
39
+
40
+ const containerProps = getWebProps([radioButtonStyles.container, style]);
41
+ const radioProps = getWebProps([radioButtonStyles.radio({ intent })]);
42
+ const dotProps = getWebProps([radioButtonStyles.radioDot({ intent })]);
43
+ const labelProps = getWebProps([radioButtonStyles.label]);
44
+
45
+ return (
46
+ <button
47
+ {...containerProps}
48
+ onClick={handleClick}
49
+ disabled={disabled}
50
+ data-testid={testID}
51
+ role="radio"
52
+ aria-checked={checked}
53
+ aria-disabled={disabled}
54
+ style={{
55
+ background: 'none',
56
+ border: 'none',
57
+ padding: 0,
58
+ cursor: 'pointer',
59
+ display: 'inline-flex',
60
+ alignItems: 'center',
61
+ }}
62
+ {...containerProps}
63
+ >
64
+ <div {...radioProps} style={{
65
+ display: 'flex',
66
+ alignItems: 'center',
67
+ justifyContent: 'center',
68
+ WebkitFontSmoothing: 'antialiased',
69
+ MozOsxFontSmoothing: 'grayscale',
70
+ backfaceVisibility: 'hidden',
71
+ }}>
72
+ {checked && <div {...dotProps} style={{
73
+ WebkitFontSmoothing: 'antialiased',
74
+ MozOsxFontSmoothing: 'grayscale',
75
+ backfaceVisibility: 'hidden',
76
+ }} />}
77
+ </div>
78
+ {label && (
79
+ <span {...labelProps}>{label}</span>
80
+ )}
81
+ </button>
82
+ );
83
+ };
84
+
85
+ export default RadioButton;
@@ -0,0 +1,43 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { View } from 'react-native';
3
+ import { radioButtonStyles } from './RadioButton.styles';
4
+ import type { RadioGroupProps } from './types';
5
+
6
+ const RadioGroupContext = React.createContext<{
7
+ value?: string;
8
+ onValueChange?: (value: string) => void;
9
+ disabled?: boolean;
10
+ }>({});
11
+
12
+ export const useRadioGroup = () => React.useContext(RadioGroupContext);
13
+
14
+ const RadioGroup = forwardRef<View, RadioGroupProps>(({
15
+ value,
16
+ onValueChange,
17
+ disabled = false,
18
+ orientation = 'vertical',
19
+ children,
20
+ style,
21
+ testID,
22
+ }, ref) => {
23
+
24
+ return (
25
+ <RadioGroupContext.Provider value={{ value, onValueChange, disabled }}>
26
+ <View
27
+ ref={ref}
28
+ style={[
29
+ radioButtonStyles.groupContainer,
30
+ style as any,
31
+ ]}
32
+ accessibilityRole="radiogroup"
33
+ testID={testID}
34
+ >
35
+ {children}
36
+ </View>
37
+ </RadioGroupContext.Provider>
38
+ );
39
+ });
40
+
41
+ RadioGroup.displayName = 'RadioGroup';
42
+
43
+ export default RadioGroup;
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { radioButtonStyles } from './RadioButton.styles';
4
+ import type { RadioGroupProps } from './types';
5
+
6
+ const RadioGroupContext = React.createContext<{
7
+ value?: string;
8
+ onValueChange?: (value: string) => void;
9
+ disabled?: boolean;
10
+ }>({});
11
+
12
+ export const useRadioGroup = () => React.useContext(RadioGroupContext);
13
+
14
+ const RadioGroup: React.FC<RadioGroupProps> = ({
15
+ value,
16
+ onValueChange,
17
+ disabled = false,
18
+ orientation = 'vertical',
19
+ children,
20
+ style,
21
+ testID,
22
+ }) => {
23
+ // Apply variants
24
+ radioButtonStyles.useVariants({
25
+ orientation,
26
+ });
27
+
28
+ const groupProps = getWebProps([
29
+ radioButtonStyles.groupContainer,
30
+ style as any,
31
+ ]);
32
+
33
+ return (
34
+ <RadioGroupContext.Provider value={{ value, onValueChange, disabled }}>
35
+ <div
36
+ {...groupProps}
37
+ role="radiogroup"
38
+ data-testid={testID}
39
+ style={{
40
+ display: 'flex',
41
+ }}
42
+ >
43
+ {children}
44
+ </div>
45
+ </RadioGroupContext.Provider>
46
+ );
47
+ };
48
+
49
+ export default RadioGroup;
@@ -0,0 +1,2 @@
1
+ export { default as RadioButton } from './RadioButton.native';
2
+ export { default as RadioGroup } from './RadioGroup.native';
@@ -0,0 +1,2 @@
1
+ export { default as RadioButton } from './RadioButton.web';
2
+ export { default as RadioGroup } from './RadioGroup.web';
@@ -0,0 +1,2 @@
1
+ export { default as RadioButton } from './RadioButton.web';
2
+ export { default as RadioGroup } from './RadioGroup.web';
@@ -0,0 +1,29 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ import type { ReactNode } from 'react';
3
+ import { Intent, Size } from '@idealyst/theme';
4
+
5
+ // Component-specific type aliases for future extensibility
6
+ export type RadioButtonIntentVariant = Intent;
7
+ export type RadioButtonSizeVariant = Size;
8
+
9
+ export interface RadioButtonProps {
10
+ value: string;
11
+ checked?: boolean;
12
+ onPress?: () => void;
13
+ disabled?: boolean;
14
+ label?: string;
15
+ size?: RadioButtonSizeVariant;
16
+ intent?: RadioButtonIntentVariant;
17
+ style?: StyleProp<ViewStyle>;
18
+ testID?: string;
19
+ }
20
+
21
+ export interface RadioGroupProps {
22
+ value?: string;
23
+ onValueChange?: (value: string) => void;
24
+ disabled?: boolean;
25
+ orientation?: 'horizontal' | 'vertical';
26
+ children: ReactNode;
27
+ style?: StyleProp<ViewStyle>;
28
+ testID?: string;
29
+ }
@@ -1,10 +1,10 @@
1
- import React from 'react';
1
+ import React, { forwardRef } from 'react';
2
2
  import { View } from 'react-native';
3
3
  import { SvgUri } from 'react-native-svg';
4
4
  import { SVGImageProps } from './types';
5
5
  import { svgImageStyles } from './SVGImage.styles';
6
6
 
7
- const SVGImage: React.FC<SVGImageProps> = ({
7
+ const SVGImage = forwardRef<View, SVGImageProps>(({
8
8
  source,
9
9
  width,
10
10
  height,
@@ -14,7 +14,7 @@ const SVGImage: React.FC<SVGImageProps> = ({
14
14
  style,
15
15
  testID,
16
16
  ...props
17
- }) => {
17
+ }, ref) => {
18
18
  // Apply variants using Unistyles 3.0 pattern
19
19
  if (intent) {
20
20
  svgImageStyles.useVariants({
@@ -30,7 +30,7 @@ const SVGImage: React.FC<SVGImageProps> = ({
30
30
  if (typeof source === 'function') {
31
31
  const SvgComponent = source;
32
32
  return (
33
- <View style={[svgImageStyles.container, style]} testID={testID} {...props}>
33
+ <View ref={ref} style={[svgImageStyles.container, style]} testID={testID} {...props}>
34
34
  <SvgComponent
35
35
  width={finalWidth}
36
36
  height={finalHeight}
@@ -43,9 +43,9 @@ const SVGImage: React.FC<SVGImageProps> = ({
43
43
 
44
44
  // Mode 2: Handle URI-based SVG loading
45
45
  const sourceUri = typeof source === 'string' ? source : source.uri;
46
-
46
+
47
47
  return (
48
- <View style={[svgImageStyles.container, style]} testID={testID} {...props}>
48
+ <View ref={ref} style={[svgImageStyles.container, style]} testID={testID} {...props}>
49
49
  <SvgUri
50
50
  uri={sourceUri}
51
51
  width={finalWidth}
@@ -55,6 +55,8 @@ const SVGImage: React.FC<SVGImageProps> = ({
55
55
  />
56
56
  </View>
57
57
  );
58
- };
58
+ });
59
+
60
+ SVGImage.displayName = 'SVGImage';
59
61
 
60
62
  export default SVGImage;
@@ -1,70 +1,78 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, Intent} from '@idealyst/theme';
2
3
 
3
- export const svgImageStyles = StyleSheet.create((theme) => ({
4
- container: {
5
- alignItems: 'center',
6
- justifyContent: 'center',
7
-
8
- variants: {
9
- intent: {
4
+ type SVGImageIntent = Intent;
5
+
6
+ type SVGImageVariants = {
7
+ intent: SVGImageIntent;
8
+ }
9
+
10
+ export type ExpandedSVGImageStyles = StylesheetStyles<keyof SVGImageVariants>;
11
+
12
+ export type SVGImageStylesheet = {
13
+ container: ExpandedSVGImageStyles;
14
+ image: ExpandedSVGImageStyles;
15
+ }
16
+
17
+ function createContainerIntentVariants(theme: Theme) {
18
+ return {
10
19
  primary: {
11
- // Use CSS filter for web, tintColor for React Native
12
- filter: `brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%)`,
20
+ filter: `brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%)`,
13
21
  },
14
22
  success: {
15
- filter: `brightness(0) saturate(100%) invert(64%) sepia(88%) saturate(3323%) hue-rotate(84deg) brightness(119%) contrast(119%)`,
23
+ filter: `brightness(0) saturate(100%) invert(64%) sepia(88%) saturate(3323%) hue-rotate(84deg) brightness(119%) contrast(119%)`,
16
24
  },
17
25
  error: {
18
- filter: `brightness(0) saturate(100%) invert(23%) sepia(89%) saturate(7395%) hue-rotate(4deg) brightness(102%) contrast(118%)`,
26
+ filter: `brightness(0) saturate(100%) invert(23%) sepia(89%) saturate(7395%) hue-rotate(4deg) brightness(102%) contrast(118%)`,
19
27
  },
20
28
  warning: {
21
- filter: `brightness(0) saturate(100%) invert(54%) sepia(98%) saturate(4341%) hue-rotate(21deg) brightness(101%) contrast(101%)`,
29
+ filter: `brightness(0) saturate(100%) invert(54%) sepia(98%) saturate(4341%) hue-rotate(21deg) brightness(101%) contrast(101%)`,
22
30
  },
23
31
  neutral: {
24
- filter: `brightness(0) saturate(100%) invert(52%) sepia(23%) saturate(3207%) hue-rotate(314deg) brightness(99%) contrast(96%)`,
32
+ filter: `brightness(0) saturate(100%) invert(52%) sepia(23%) saturate(3207%) hue-rotate(314deg) brightness(99%) contrast(96%)`,
25
33
  },
26
- },
27
- },
28
-
29
- // Web-specific styles
30
- _web: {
31
- userSelect: 'none',
32
- },
33
-
34
- // Native-specific styles
35
- _native: {
36
- variants: {
37
- intent: {
38
- primary: {
39
- tintColor: theme.intents?.primary?.main || '#3b82f6',
40
- },
41
- success: {
42
- tintColor: theme.intents?.success?.main || '#22c55e',
43
- },
44
- error: {
45
- tintColor: theme.intents?.error?.main || '#ef4444',
46
- },
47
- warning: {
48
- tintColor: theme.intents?.warning?.main || '#f59e0b',
49
- },
50
- neutral: {
51
- tintColor: theme.intents?.neutral?.main || '#6b7280',
52
- },
34
+ info: {
35
+ filter: `brightness(0) saturate(100%) invert(58%) sepia(96%) saturate(2582%) hue-rotate(165deg) brightness(99%) contrast(91%)`,
36
+ },
37
+ };
38
+ }
39
+
40
+ function createContainerNativeIntentVariants(theme: Theme) {
41
+ const variants: Record<SVGImageIntent, any> = {} as any;
42
+ for (const intent in theme.intents) {
43
+ variants[intent as SVGImageIntent] = {
44
+ tintColor: theme.intents[intent as SVGImageIntent].primary,
45
+ };
46
+ }
47
+ return variants;
48
+ }
49
+
50
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
51
+ // transform on native cannot resolve function calls to extract variant structures.
52
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
53
+ export const svgImageStyles = StyleSheet.create((theme: Theme) => {
54
+ return {
55
+ container: {
56
+ alignItems: 'center',
57
+ justifyContent: 'center',
58
+ variants: {
59
+ intent: createContainerIntentVariants(theme),
60
+ },
61
+ _web: {
62
+ userSelect: 'none',
63
+ },
64
+ _native: {
65
+ variants: {
66
+ intent: createContainerNativeIntentVariants(theme),
67
+ },
53
68
  },
54
- },
55
- },
56
- },
57
-
58
- image: {
59
- // Base image styles
60
- _web: {
61
- display: 'block',
62
- maxWidth: '100%',
63
- height: 'auto',
64
69
  },
65
-
66
- _native: {
67
- // Native image styles will be applied via Image component
70
+ image: {
71
+ _web: {
72
+ display: 'block',
73
+ maxWidth: '100%',
74
+ height: 'auto',
75
+ },
68
76
  },
69
- },
70
- }));
77
+ };
78
+ });
@@ -1,9 +1,10 @@
1
- import React from 'react';
1
+ import React, { forwardRef } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { SVGImageProps } from './types';
4
4
  import { svgImageStyles } from './SVGImage.styles';
5
+ import useMergeRefs from '../hooks/useMergeRefs';
5
6
 
6
- const SVGImage: React.FC<SVGImageProps> = ({
7
+ const SVGImage = forwardRef<HTMLDivElement, SVGImageProps>(({
7
8
  source,
8
9
  width,
9
10
  height,
@@ -14,11 +15,11 @@ const SVGImage: React.FC<SVGImageProps> = ({
14
15
  style,
15
16
  testID,
16
17
  ...props
17
- }) => {
18
+ }, ref) => {
18
19
  // Apply variants using Unistyles 3.0 pattern
19
20
  if (intent) {
20
21
  svgImageStyles.useVariants({
21
- intent: intent as 'primary' | 'success' | 'error' | 'warning' | 'neutral',
22
+ intent,
22
23
  });
23
24
  }
24
25
 
@@ -29,8 +30,10 @@ const SVGImage: React.FC<SVGImageProps> = ({
29
30
  // Handle React components (imported SVG components)
30
31
  if (typeof source === 'function') {
31
32
  const SvgComponent = source;
33
+ const componentContainerProps = getWebProps([svgImageStyles.container, style as any]);
34
+ const mergedRefForComponent = useMergeRefs(ref, componentContainerProps.ref);
32
35
  return (
33
- <div {...getWebProps([svgImageStyles.container, style])} {...props} data-testid={testID}>
36
+ <div {...componentContainerProps} ref={mergedRefForComponent} {...(props as any)} data-testid={testID}>
34
37
  <SvgComponent
35
38
  width={finalWidth || 24}
36
39
  height={finalHeight || 24}
@@ -47,16 +50,12 @@ const SVGImage: React.FC<SVGImageProps> = ({
47
50
  // Create the style array
48
51
  const containerStyleArray = [
49
52
  svgImageStyles.container,
50
- style,
51
- ];
52
-
53
- const imageStyleArray = [
54
- svgImageStyles.image,
53
+ style as any,
55
54
  ];
56
55
 
57
56
  // Use getWebProps to generate className and ref for web
58
57
  const containerWebProps = getWebProps(containerStyleArray);
59
- const imageWebProps = getWebProps(imageStyleArray);
58
+ const imageWebProps = getWebProps(svgImageStyles.image);
60
59
 
61
60
  // Apply custom color if provided
62
61
  // Convert React Native resize modes to CSS object-fit values
@@ -78,8 +77,10 @@ const SVGImage: React.FC<SVGImageProps> = ({
78
77
  }),
79
78
  };
80
79
 
80
+ const mergedRef = useMergeRefs(ref, containerWebProps.ref);
81
+
81
82
  return (
82
- <div {...containerWebProps} {...props} data-testid={testID}>
83
+ <div {...containerWebProps} ref={mergedRef} {...(props as any)} data-testid={testID}>
83
84
  <img
84
85
  {...imageWebProps}
85
86
  src={sourceUrl}
@@ -88,6 +89,8 @@ const SVGImage: React.FC<SVGImageProps> = ({
88
89
  />
89
90
  </div>
90
91
  );
91
- };
92
+ });
93
+
94
+ SVGImage.displayName = 'SVGImage';
92
95
 
93
96
  export default SVGImage;
@@ -1,5 +1,5 @@
1
- // Platform-agnostic SVGImage export
2
- // Metro will resolve to index.native.ts for React Native
3
- // This file serves as fallback for web environments
4
- export { default } from './SVGImage.web';
5
- export * from './types';
1
+ import SVGImageComponent from './SVGImage.web';
2
+
3
+ export default SVGImageComponent;
4
+ export { SVGImageComponent as SVGImage };
5
+ export * from './types';
@@ -1,2 +1,5 @@
1
- export { default } from './SVGImage.web';
2
- export * from './types';
1
+ import SVGImageComponent from './SVGImage.web';
2
+
3
+ export default SVGImageComponent;
4
+ export { SVGImageComponent as SVGImage };
5
+ export * from './types';