@idealyst/components 1.0.83 → 1.0.85

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,304 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, Intent, CompoundVariants} from '@idealyst/theme';
3
+
4
+ type AlertType = 'filled' | 'outlined' | 'soft';
5
+ type AlertIntent = Intent | 'info'; // Alert includes 'info' which maps to primary
6
+
7
+ export type AlertVariants = {
8
+ type: AlertType;
9
+ intent: AlertIntent;
10
+ }
11
+
12
+
13
+ /**
14
+ * Create type variants (structure only, colors handled by compound variants)
15
+ */
16
+ const TypeVariants = {
17
+ filled: {
18
+ borderWidth: 1,
19
+ borderStyle: 'solid' as const,
20
+ },
21
+ outlined: {
22
+ backgroundColor: 'transparent',
23
+ borderWidth: 1,
24
+ borderStyle: 'solid' as const,
25
+ },
26
+ soft: {
27
+ borderWidth: 1,
28
+ borderStyle: 'solid' as const,
29
+ }
30
+ } as const;
31
+
32
+ /**
33
+ * Create compound variants for container (type + intent combinations)
34
+ */
35
+ function createContainerCompoundVariants(theme: Theme) {
36
+ const compoundVariants: CompoundVariants<keyof AlertVariants> = [];
37
+
38
+ // Process standard intents from theme
39
+ for (const intent in theme.intents) {
40
+ const intentValue = theme.intents[intent as Intent];
41
+
42
+ // Filled + intent
43
+ compoundVariants.push({
44
+ intent,
45
+ type: 'filled',
46
+ styles: {
47
+ backgroundColor: intentValue.primary,
48
+ borderColor: intentValue.primary,
49
+ },
50
+ });
51
+
52
+ // Outlined + intent
53
+ compoundVariants.push({
54
+ intent,
55
+ type: 'outlined',
56
+ styles: {
57
+ borderColor: intentValue.primary,
58
+ },
59
+ });
60
+
61
+ // Soft + intent
62
+ compoundVariants.push({
63
+ intent,
64
+ type: 'soft',
65
+ styles: {
66
+ backgroundColor: intentValue.light,
67
+ borderColor: intentValue.light,
68
+ },
69
+ });
70
+ }
71
+
72
+ // Add 'info' intent (maps to primary)
73
+ const primaryIntent = theme.intents.primary;
74
+ compoundVariants.push({
75
+ intent: 'info',
76
+ type: 'filled',
77
+ styles: {
78
+ backgroundColor: primaryIntent.primary,
79
+ borderColor: primaryIntent.primary,
80
+ },
81
+ });
82
+ compoundVariants.push({
83
+ intent: 'info',
84
+ type: 'outlined',
85
+ styles: {
86
+ borderColor: primaryIntent.primary,
87
+ },
88
+ });
89
+ compoundVariants.push({
90
+ intent: 'info',
91
+ type: 'soft',
92
+ styles: {
93
+ backgroundColor: primaryIntent.light,
94
+ borderColor: primaryIntent.light,
95
+ },
96
+ });
97
+
98
+ return compoundVariants;
99
+ }
100
+
101
+ /**
102
+ * Create compound variants for icon/title colors (type + intent combinations)
103
+ */
104
+ function createIconTitleCompoundVariants(theme: Theme) {
105
+ const compoundVariants: CompoundVariants<keyof AlertVariants> = [];
106
+
107
+ // Process standard intents from theme
108
+ for (const intent in theme.intents) {
109
+ const intentValue = theme.intents[intent as Intent];
110
+
111
+ // Filled type: use contrast color
112
+ compoundVariants.push({
113
+ intent,
114
+ type: 'filled',
115
+ styles: {
116
+ color: intentValue.contrast,
117
+ },
118
+ });
119
+
120
+ // Outlined type: use primary color
121
+ compoundVariants.push({
122
+ intent,
123
+ type: 'outlined',
124
+ styles: {
125
+ color: intentValue.primary,
126
+ },
127
+ });
128
+
129
+ // Soft type: use primary color
130
+ compoundVariants.push({
131
+ intent,
132
+ type: 'soft',
133
+ styles: {
134
+ color: intentValue.primary,
135
+ },
136
+ });
137
+ }
138
+
139
+ // Add 'info' intent (maps to primary)
140
+ const primaryIntent = theme.intents.primary;
141
+ compoundVariants.push({
142
+ intent: 'info',
143
+ type: 'filled',
144
+ styles: {
145
+ color: primaryIntent.contrast,
146
+ },
147
+ });
148
+ compoundVariants.push({
149
+ intent: 'info',
150
+ type: 'outlined',
151
+ styles: {
152
+ color: primaryIntent.primary,
153
+ },
154
+ });
155
+ compoundVariants.push({
156
+ intent: 'info',
157
+ type: 'soft',
158
+ styles: {
159
+ color: primaryIntent.primary,
160
+ },
161
+ });
162
+
163
+ return compoundVariants;
164
+ }
165
+
166
+ /**
167
+ * Create compound variants for message colors (type + intent combinations)
168
+ */
169
+ function createMessageCompoundVariants(theme: Theme): CompoundVariants<keyof AlertVariants> {
170
+ const compoundVariants: CompoundVariants<keyof AlertVariants> = [];
171
+
172
+ // Process standard intents from theme
173
+ for (const intent in theme.intents) {
174
+ const intentValue = theme.intents[intent as Intent];
175
+
176
+ // Filled type: use contrast color
177
+ compoundVariants.push({
178
+ intent,
179
+ type: 'filled',
180
+ styles: {
181
+ color: intentValue.contrast,
182
+ },
183
+ });
184
+
185
+ // Outlined type: use primary text color
186
+ compoundVariants.push({
187
+ intent,
188
+ type: 'outlined',
189
+ styles: {
190
+ color: theme.colors.text.primary,
191
+ },
192
+ });
193
+
194
+ // Soft type: use primary text color
195
+ compoundVariants.push({
196
+ intent,
197
+ type: 'soft',
198
+ styles: {
199
+ color: theme.colors.text.primary,
200
+ },
201
+ });
202
+ }
203
+
204
+ // Add 'info' intent (maps to primary)
205
+ const primaryIntent = theme.intents.primary;
206
+ compoundVariants.push({
207
+ intent: 'info',
208
+ type: 'filled',
209
+ styles: {
210
+ color: primaryIntent.contrast,
211
+ },
212
+ });
213
+ compoundVariants.push({
214
+ intent: 'info',
215
+ type: 'outlined',
216
+ styles: {
217
+ color: theme.colors.text.primary,
218
+ },
219
+ });
220
+ compoundVariants.push({
221
+ intent: 'info',
222
+ type: 'soft',
223
+ styles: {
224
+ color: theme.colors.text.primary,
225
+ },
226
+ });
227
+
228
+ return compoundVariants;
229
+ }
230
+
231
+ export const alertStyles = StyleSheet.create((theme: Theme) => {
232
+ return {
233
+ container: {
234
+ display: 'flex',
235
+ flexDirection: 'row',
236
+ alignItems: 'flex-start',
237
+ gap: 8,
238
+ padding: 16,
239
+ borderRadius: 8,
240
+ borderWidth: 1,
241
+ borderStyle: 'solid' as const,
242
+ variants: {
243
+ type: TypeVariants,
244
+ } as const,
245
+ compoundVariants: createContainerCompoundVariants(theme),
246
+ } as const,
247
+ iconContainer: {
248
+ display: 'flex',
249
+ alignItems: 'center',
250
+ justifyContent: 'center',
251
+ flexShrink: 0,
252
+ width: 24,
253
+ height: 24,
254
+ compoundVariants: createIconTitleCompoundVariants(theme),
255
+ } as const,
256
+ content: {
257
+ flex: 1,
258
+ display: 'flex',
259
+ flexDirection: 'column',
260
+ gap: 4,
261
+ },
262
+ title: {
263
+ fontSize: 16,
264
+ lineHeight: 24,
265
+ fontWeight: '600',
266
+ compoundVariants: createIconTitleCompoundVariants(theme),
267
+ },
268
+ message: {
269
+ fontSize: 14,
270
+ lineHeight: 20,
271
+ compoundVariants: createMessageCompoundVariants(theme),
272
+ },
273
+ actions: {
274
+ marginTop: 4,
275
+ display: 'flex',
276
+ flexDirection: 'row',
277
+ gap: 8,
278
+ },
279
+ closeButton: {
280
+ padding: 4,
281
+ backgroundColor: 'transparent',
282
+ borderRadius: 4,
283
+ display: 'flex',
284
+ alignItems: 'center',
285
+ justifyContent: 'center',
286
+ flexShrink: 0,
287
+ _web: {
288
+ border: 'none',
289
+ cursor: 'pointer',
290
+ outline: 'none',
291
+ _hover: {
292
+ backgroundColor: 'rgba(0, 0, 0, 0.1)',
293
+ },
294
+ },
295
+ },
296
+ closeIcon: {
297
+ display: 'flex',
298
+ alignItems: 'center',
299
+ justifyContent: 'center',
300
+ width: 16,
301
+ height: 16,
302
+ },
303
+ } as const;
304
+ });
@@ -0,0 +1,123 @@
1
+ import React, { isValidElement, forwardRef } from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { alertStyles } from './Alert.styles';
4
+ import type { AlertProps } from './types';
5
+ import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
6
+ import { resolveIconPath, isIconName } from '../Icon/icon-resolver';
7
+ import useMergeRefs from '../hooks/useMergeRefs';
8
+
9
+ // Default icons for each intent
10
+ const defaultIcons = {
11
+ primary: 'information',
12
+ success: 'check-circle',
13
+ error: 'alert-circle',
14
+ warning: 'alert',
15
+ info: 'information',
16
+ neutral: 'record-circle',
17
+ };
18
+
19
+ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
20
+ title,
21
+ message,
22
+ children,
23
+ intent = 'neutral',
24
+ type = 'soft',
25
+ icon,
26
+ showIcon = true,
27
+ dismissible = false,
28
+ onDismiss,
29
+ actions,
30
+ style,
31
+ testID,
32
+ }, ref) => {
33
+ // Apply variants to stylesheet
34
+ alertStyles.useVariants({
35
+ type,
36
+ intent,
37
+ });
38
+
39
+ const containerProps = getWebProps([alertStyles.container, style as any]);
40
+ const iconContainerProps = getWebProps([alertStyles.iconContainer]);
41
+ const contentProps = getWebProps([alertStyles.content]);
42
+ const titleProps = getWebProps([alertStyles.title]);
43
+ const messageProps = getWebProps([alertStyles.message]);
44
+ const actionsProps = getWebProps([alertStyles.actions]);
45
+ const closeButtonProps = getWebProps([alertStyles.closeButton]);
46
+ const closeIconProps = getWebProps([alertStyles.closeIcon]);
47
+
48
+ const displayIcon = icon !== undefined ? icon : (showIcon ? defaultIcons[intent] : null);
49
+
50
+ // Helper to render icon
51
+ const renderIcon = (iconProp: typeof displayIcon) => {
52
+ if (!iconProp) return null;
53
+
54
+ if (isIconName(iconProp)) {
55
+ const iconPath = resolveIconPath(iconProp);
56
+ return (
57
+ <IconSvg
58
+ path={iconPath}
59
+ {...iconContainerProps}
60
+ aria-label={iconProp}
61
+ />
62
+ );
63
+ } else if (isValidElement(iconProp)) {
64
+ return iconProp;
65
+ }
66
+
67
+ return null;
68
+ };
69
+
70
+ const mergedRef = useMergeRefs(ref, containerProps.ref);
71
+
72
+ return (
73
+ <div
74
+ {...containerProps}
75
+ ref={mergedRef}
76
+ data-testid={testID}
77
+ role="alert"
78
+ >
79
+ {displayIcon && renderIcon(displayIcon)}
80
+
81
+ <div {...contentProps}>
82
+ {title && (
83
+ <div {...titleProps}>
84
+ {title}
85
+ </div>
86
+ )}
87
+
88
+ {message && (
89
+ <div {...messageProps}>
90
+ {message}
91
+ </div>
92
+ )}
93
+
94
+ {children}
95
+
96
+ {actions && (
97
+ <div {...actionsProps}>
98
+ {actions}
99
+ </div>
100
+ )}
101
+ </div>
102
+
103
+ {dismissible && onDismiss && (
104
+ <button
105
+ {...closeButtonProps}
106
+ onClick={onDismiss}
107
+ aria-label="Dismiss alert"
108
+ type="button"
109
+ >
110
+ <IconSvg
111
+ path={resolveIconPath('close')}
112
+ {...closeIconProps}
113
+ aria-label="close"
114
+ />
115
+ </button>
116
+ )}
117
+ </div>
118
+ );
119
+ });
120
+
121
+ Alert.displayName = 'Alert';
122
+
123
+ export default Alert;
@@ -0,0 +1,5 @@
1
+ import AlertComponent from './Alert.native';
2
+
3
+ export default AlertComponent;
4
+ export { AlertComponent as Alert };
5
+ export type { AlertProps, AlertIntentVariant, AlertType} from './types';
@@ -0,0 +1,5 @@
1
+ import AlertComponent from './Alert.web';
2
+
3
+ export default AlertComponent;
4
+ export { AlertComponent as Alert };
5
+ export type { AlertProps, AlertIntentVariant, AlertType } from './types';
@@ -0,0 +1,5 @@
1
+ import AlertComponent from './Alert.web';
2
+
3
+ export default AlertComponent;
4
+ export { AlertComponent as Alert };
5
+ export type { AlertProps, AlertIntentVariant, AlertType } from './types';
@@ -0,0 +1,21 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ import type { Intent } from '@idealyst/theme';
3
+
4
+ // Component-specific type aliases for future extensibility
5
+ export type AlertIntentVariant = Intent;
6
+ export type AlertType= 'filled' | 'outlined' | 'soft';
7
+
8
+ export interface AlertProps {
9
+ title?: string;
10
+ message?: string;
11
+ children?: React.ReactNode;
12
+ intent?: AlertIntentVariant;
13
+ type?: AlertType;
14
+ icon?: React.ReactNode;
15
+ showIcon?: boolean;
16
+ dismissible?: boolean;
17
+ onDismiss?: () => void;
18
+ actions?: React.ReactNode;
19
+ style?: StyleProp<ViewStyle>;
20
+ testID?: string;
21
+ }
@@ -1,17 +1,17 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, forwardRef } from 'react';
2
2
  import { View, Text, Image } from 'react-native';
3
3
  import { AvatarProps } from './types';
4
4
  import { avatarStyles } from './Avatar.styles';
5
5
 
6
- const Avatar: React.FC<AvatarProps> = ({
6
+ const Avatar = forwardRef<View, AvatarProps>(({
7
7
  src,
8
8
  alt,
9
9
  fallback,
10
- size = 'medium',
10
+ size = 'md',
11
11
  shape = 'circle',
12
12
  style,
13
13
  testID,
14
- }) => {
14
+ }, ref) => {
15
15
  const [hasError, setHasError] = useState(false);
16
16
 
17
17
  avatarStyles.useVariants({
@@ -24,7 +24,7 @@ const Avatar: React.FC<AvatarProps> = ({
24
24
  };
25
25
 
26
26
  return (
27
- <View style={[avatarStyles.avatar, style]} testID={testID}>
27
+ <View ref={ref} style={[avatarStyles.avatar, style]} testID={testID}>
28
28
  {src && !hasError ? (
29
29
  <Image
30
30
  source={typeof src === 'string' ? { uri: src } : src}
@@ -39,6 +39,8 @@ const Avatar: React.FC<AvatarProps> = ({
39
39
  )}
40
40
  </View>
41
41
  );
42
- };
42
+ });
43
+
44
+ Avatar.displayName = 'Avatar';
43
45
 
44
46
  export default Avatar;
@@ -1,67 +1,73 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, Size} from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
2
4
 
3
- export const avatarStyles = StyleSheet.create((theme) => ({
4
- avatar: {
5
- display: 'flex',
6
- alignItems: 'center',
7
- justifyContent: 'center',
8
- backgroundColor: theme.colors?.surface?.secondary || '#f3f4f6',
9
- overflow: 'hidden',
10
-
11
- variants: {
12
- size: {
13
- small: {
14
- width: 32,
15
- height: 32,
16
- },
17
- medium: {
18
- width: 40,
19
- height: 40,
20
- },
21
- large: {
22
- width: 48,
23
- height: 48,
24
- },
25
- xlarge: {
26
- width: 64,
27
- height: 64,
28
- },
29
- },
30
- shape: {
5
+ type AvatarSize = Size;
6
+ type AvatarShape = 'circle' | 'square';
7
+
8
+ type AvatarVariants = {
9
+ size: AvatarSize;
10
+ shape: AvatarShape;
11
+ }
12
+
13
+ export type ExpandedAvatarStyles = StylesheetStyles<keyof AvatarVariants>;
14
+
15
+ export type AvatarStylesheet = {
16
+ avatar: ExpandedAvatarStyles;
17
+ image: ExpandedAvatarStyles;
18
+ fallback: ExpandedAvatarStyles;
19
+ }
20
+
21
+ function createAvatarSizeVariants(theme: Theme) {
22
+ return buildSizeVariants(theme, 'avatar', (size) => ({
23
+ width: size.width,
24
+ height: size.height,
25
+ }));
26
+ }
27
+
28
+ function createAvatarShapeVariants(theme: Theme) {
29
+ return {
31
30
  circle: {
32
- borderRadius: 9999,
31
+ borderRadius: 9999,
33
32
  },
34
33
  square: {
35
- borderRadius: theme.borderRadius?.md || 8,
36
- },
37
- },
38
- },
39
- },
40
-
41
- image: {
42
- width: '100%',
43
- height: '100%',
44
- },
45
-
46
- fallback: {
47
- color: theme.colors?.text?.primary || '#374151',
48
- fontWeight: '600',
49
-
50
- variants: {
51
- size: {
52
- small: {
53
- fontSize: 14,
54
- },
55
- medium: {
56
- fontSize: 16,
34
+ borderRadius: 8,
57
35
  },
58
- large: {
59
- fontSize: 18,
36
+ } as const;
37
+ }
38
+
39
+ function createFallbackSizeVariants(theme: Theme) {
40
+ return buildSizeVariants(theme, 'avatar', (size) => ({
41
+ fontSize: size.fontSize,
42
+ }));
43
+ }
44
+
45
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
46
+ // transform on native cannot resolve function calls to extract variant structures.
47
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
48
+ export const avatarStyles = StyleSheet.create((theme: Theme) => {
49
+ return {
50
+ avatar: {
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ justifyContent: 'center',
54
+ backgroundColor: theme.colors.surface.secondary,
55
+ overflow: 'hidden',
56
+ variants: {
57
+ size: createAvatarSizeVariants(theme),
58
+ shape: createAvatarShapeVariants(theme),
60
59
  },
61
- xlarge: {
62
- fontSize: 24,
60
+ },
61
+ image: {
62
+ width: '100%',
63
+ height: '100%',
64
+ },
65
+ fallback: {
66
+ color: theme.colors.text.primary,
67
+ fontWeight: '600',
68
+ variants: {
69
+ size: createFallbackSizeVariants(theme),
63
70
  },
64
- },
65
71
  },
66
- },
67
- }));
72
+ };
73
+ });
@@ -1,17 +1,18 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, forwardRef } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { AvatarProps } from './types';
4
4
  import { avatarStyles } from './Avatar.styles';
5
+ import useMergeRefs from '../hooks/useMergeRefs';
5
6
 
6
- const Avatar: React.FC<AvatarProps> = ({
7
+ const Avatar = forwardRef<HTMLDivElement, AvatarProps>(({
7
8
  src,
8
9
  alt,
9
10
  fallback,
10
- size = 'medium',
11
+ size = 'md',
11
12
  shape = 'circle',
12
13
  style,
13
14
  testID,
14
- }) => {
15
+ }, ref) => {
15
16
  const [hasError, setHasError] = useState(false);
16
17
 
17
18
  avatarStyles.useVariants({
@@ -21,7 +22,7 @@ const Avatar: React.FC<AvatarProps> = ({
21
22
 
22
23
  const avatarStyleArray = [avatarStyles.avatar, style];
23
24
  const avatarProps = getWebProps(avatarStyleArray);
24
-
25
+
25
26
  // Generate fallback text styles with proper theming and size
26
27
  const fallbackStyleArray = [avatarStyles.fallback];
27
28
  const fallbackProps = getWebProps(fallbackStyleArray);
@@ -30,11 +31,13 @@ const Avatar: React.FC<AvatarProps> = ({
30
31
  setHasError(true);
31
32
  };
32
33
 
34
+ const mergedRef = useMergeRefs(ref, avatarProps.ref);
35
+
33
36
  return (
34
- <div {...avatarProps} data-testid={testID}>
37
+ <div {...avatarProps} ref={mergedRef} data-testid={testID}>
35
38
  {src && !hasError ? (
36
39
  <img
37
- src={src}
40
+ src={src as any}
38
41
  alt={alt}
39
42
  onError={handleImageError}
40
43
  style={{ width: '100%', height: '100%', objectFit: 'cover' }}
@@ -46,6 +49,8 @@ const Avatar: React.FC<AvatarProps> = ({
46
49
  )}
47
50
  </div>
48
51
  );
49
- };
52
+ });
53
+
54
+ Avatar.displayName = 'Avatar';
50
55
 
51
56
  export default Avatar;