@idealyst/components 1.0.83 → 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 +20 -2
  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 +140 -63
  170. package/src/Select/Select.styles.tsx +312 -302
  171. package/src/Select/Select.web.tsx +156 -316
  172. package/src/Select/index.ts +5 -2
  173. package/src/Select/index.web.ts +5 -2
  174. package/src/Select/types.ts +13 -7
  175. package/src/Skeleton/Skeleton.native.tsx +139 -0
  176. package/src/Skeleton/Skeleton.styles.tsx +59 -0
  177. package/src/Skeleton/Skeleton.web.tsx +112 -0
  178. package/src/Skeleton/index.native.ts +4 -0
  179. package/src/Skeleton/index.ts +5 -0
  180. package/src/Skeleton/index.web.ts +5 -0
  181. package/src/Skeleton/types.ts +75 -0
  182. package/src/Slider/Slider.native.tsx +248 -0
  183. package/src/Slider/Slider.styles.tsx +241 -0
  184. package/src/Slider/Slider.web.tsx +226 -0
  185. package/src/Slider/index.native.ts +3 -0
  186. package/src/Slider/index.ts +5 -0
  187. package/src/Slider/index.web.ts +5 -0
  188. package/src/Slider/types.ts +31 -0
  189. package/src/Switch/Switch.native.tsx +131 -0
  190. package/src/Switch/Switch.styles.tsx +169 -0
  191. package/src/Switch/Switch.web.tsx +121 -0
  192. package/src/Switch/index.native.ts +3 -0
  193. package/src/Switch/index.ts +5 -0
  194. package/src/Switch/index.web.ts +5 -0
  195. package/src/Switch/types.ts +21 -0
  196. package/src/TabBar/TabBar.native.tsx +142 -0
  197. package/src/TabBar/TabBar.styles.tsx +399 -0
  198. package/src/TabBar/TabBar.web.tsx +205 -0
  199. package/src/TabBar/index.native.tsx +3 -0
  200. package/src/TabBar/index.ts +3 -0
  201. package/src/TabBar/index.web.tsx +3 -0
  202. package/src/TabBar/types.ts +26 -0
  203. package/src/Table/Table.native.tsx +122 -0
  204. package/src/Table/Table.styles.tsx +283 -0
  205. package/src/Table/Table.web.tsx +112 -0
  206. package/src/Table/index.native.tsx +3 -0
  207. package/src/Table/index.ts +3 -0
  208. package/src/Table/index.web.tsx +3 -0
  209. package/src/Table/types.ts +28 -0
  210. package/src/Text/Text.native.tsx +12 -11
  211. package/src/Text/Text.styles.tsx +76 -64
  212. package/src/Text/Text.web.tsx +14 -9
  213. package/src/Text/index.ts +5 -5
  214. package/src/Text/index.web.ts +5 -3
  215. package/src/Text/types.ts +20 -13
  216. package/src/TextArea/TextArea.native.tsx +134 -0
  217. package/src/TextArea/TextArea.styles.tsx +175 -0
  218. package/src/TextArea/TextArea.web.tsx +156 -0
  219. package/src/TextArea/index.native.ts +3 -0
  220. package/src/TextArea/index.ts +3 -0
  221. package/src/TextArea/index.web.ts +3 -0
  222. package/src/TextArea/types.ts +30 -0
  223. package/src/Tooltip/Tooltip.native.tsx +165 -0
  224. package/src/Tooltip/Tooltip.styles.tsx +73 -0
  225. package/src/Tooltip/Tooltip.web.tsx +87 -0
  226. package/src/Tooltip/index.native.ts +3 -0
  227. package/src/Tooltip/index.ts +3 -0
  228. package/src/Tooltip/types.ts +18 -0
  229. package/src/Video/Video.native.tsx +105 -0
  230. package/src/Video/Video.styles.tsx +39 -0
  231. package/src/Video/Video.web.tsx +115 -0
  232. package/src/Video/index.native.ts +5 -0
  233. package/src/Video/index.ts +5 -0
  234. package/src/Video/types.ts +29 -0
  235. package/src/View/View.native.tsx +9 -14
  236. package/src/View/View.styles.tsx +101 -93
  237. package/src/View/View.web.tsx +16 -17
  238. package/src/View/index.ts +5 -5
  239. package/src/View/index.web.ts +5 -3
  240. package/src/View/types.ts +29 -21
  241. package/src/examples/AccordionExamples.tsx +126 -0
  242. package/src/examples/AlertExamples.tsx +280 -0
  243. package/src/examples/AvatarExamples.tsx +23 -23
  244. package/src/examples/BadgeExamples.tsx +109 -41
  245. package/src/examples/BreadcrumbExamples.tsx +312 -0
  246. package/src/examples/ButtonExamples.tsx +160 -33
  247. package/src/examples/CardExamples.tsx +40 -40
  248. package/src/examples/CheckboxExamples.tsx +12 -12
  249. package/src/examples/ChipExamples.tsx +197 -0
  250. package/src/examples/DialogExamples.tsx +22 -22
  251. package/src/examples/DividerExamples.tsx +49 -49
  252. package/src/examples/IconExamples.tsx +270 -54
  253. package/src/examples/ImageExamples.tsx +174 -0
  254. package/src/examples/InputExamples.tsx +75 -17
  255. package/src/examples/ListExamples.tsx +288 -0
  256. package/src/examples/MenuExamples.tsx +144 -0
  257. package/src/examples/PopoverExamples.tsx +69 -73
  258. package/src/examples/ProgressExamples.tsx +137 -0
  259. package/src/examples/RadioButtonExamples.tsx +161 -0
  260. package/src/examples/SVGImageExamples.tsx +19 -17
  261. package/src/examples/ScreenExamples.tsx +31 -31
  262. package/src/examples/SelectExamples.tsx +67 -67
  263. package/src/examples/SkeletonExamples.tsx +206 -0
  264. package/src/examples/SliderExamples.tsx +200 -0
  265. package/src/examples/SwitchExamples.tsx +182 -0
  266. package/src/examples/TabBarExamples.tsx +143 -0
  267. package/src/examples/TableExamples.tsx +280 -0
  268. package/src/examples/TextAreaExamples.tsx +173 -0
  269. package/src/examples/TextExamples.tsx +28 -32
  270. package/src/examples/ThemeExtensionExamples.tsx +10 -10
  271. package/src/examples/TooltipExamples.tsx +126 -0
  272. package/src/examples/VideoExamples.tsx +144 -0
  273. package/src/examples/ViewExamples.tsx +64 -56
  274. package/src/examples/index.ts +17 -3
  275. package/src/hooks/useMergeRefs.ts +16 -0
  276. package/src/hooks/useSmartPosition.native.ts +169 -0
  277. package/src/index.native.ts +80 -9
  278. package/src/index.ts +71 -1
  279. package/src/internal/BoundedModalContent.native.tsx +58 -0
  280. package/src/internal/PositionedPortal.tsx +254 -0
  281. package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
  282. package/src/unistyles.d.ts +6 -0
  283. package/src/utils/buildSizeVariants.ts +16 -0
  284. package/src/utils/deepMerge.ts +43 -0
  285. package/src/utils/positionUtils.native.ts +280 -0
  286. package/src/utils/styleHelpers.ts +48 -0
  287. package/LLM-ACCESS-GUIDE.md +0 -143
  288. package/src/ActivityIndicator/README.md +0 -132
  289. package/src/Avatar/README.md +0 -139
  290. package/src/Badge/README.md +0 -170
  291. package/src/Button/Button.types.ts +0 -12
  292. package/src/Button/README.md +0 -262
  293. package/src/Card/README.md +0 -258
  294. package/src/Checkbox/README.md +0 -102
  295. package/src/Dialog/README.md +0 -210
  296. package/src/Divider/README.md +0 -108
  297. package/src/Icon/README.md +0 -81
  298. package/src/Input/README.md +0 -100
  299. package/src/SVGImage/README.md +0 -209
  300. package/src/Screen/README.md +0 -86
  301. package/src/Select/README.md +0 -166
  302. package/src/Text/README.md +0 -94
  303. package/src/View/README.md +0 -107
  304. package/src/examples/AllExamples.tsx +0 -88
  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';