@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
@@ -1,51 +1,55 @@
1
- import React from 'react';
1
+ import React, { forwardRef } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { DividerProps } from './types';
4
4
  import { dividerStyles } from './Divider.styles';
5
+ import useMergeRefs from '../hooks/useMergeRefs';
5
6
 
6
- const Divider: React.FC<DividerProps> = ({
7
+ const Divider = forwardRef<HTMLDivElement, DividerProps>(({
7
8
  orientation = 'horizontal',
8
- variant = 'solid',
9
+ type = 'solid',
9
10
  thickness = 'thin',
10
11
  intent = 'neutral',
11
12
  length = 'full',
12
- spacing = 'medium',
13
+ spacing = 'md',
13
14
  children,
14
15
  style,
15
16
  testID,
16
17
  accessibilityLabel,
17
- }) => {
18
+ }, ref) => {
18
19
  // Apply variants for main divider
19
20
  dividerStyles.useVariants({
20
21
  orientation,
21
22
  thickness,
22
- variant: variant as any,
23
+ type,
23
24
  intent,
24
25
  length: typeof length === 'number' ? 'auto' : length,
25
26
  spacing,
26
27
  });
27
28
 
28
29
  // Create style arrays
29
- const dividerStyleArray = [
30
- dividerStyles.divider,
31
- style,
32
- ].filter(Boolean);
33
30
 
34
31
  const containerStyleArray = [dividerStyles.container];
35
32
  const contentStyleArray = [dividerStyles.content];
36
33
  const lineStyleArray = [dividerStyles.line];
37
34
 
38
35
  // Generate web props
39
- const dividerProps = getWebProps(dividerStyleArray);
36
+ const dividerProps = getWebProps([
37
+ dividerStyles.divider,
38
+ style as any,
39
+ ]);
40
40
  const containerProps = getWebProps(containerStyleArray);
41
41
  const contentProps = getWebProps(contentStyleArray);
42
42
  const lineProps = getWebProps(lineStyleArray);
43
43
 
44
+ const mergedDividerRef = useMergeRefs(ref, dividerProps.ref);
45
+ const mergedContainerRef = useMergeRefs(ref, containerProps.ref);
46
+
44
47
  // If no children, render simple divider
45
48
  if (!children) {
46
49
  return (
47
50
  <div
48
51
  {...dividerProps}
52
+ ref={mergedDividerRef}
49
53
  data-testid={testID}
50
54
  aria-label={accessibilityLabel}
51
55
  role="separator"
@@ -57,6 +61,7 @@ const Divider: React.FC<DividerProps> = ({
57
61
  return (
58
62
  <div
59
63
  {...containerProps}
64
+ ref={mergedContainerRef}
60
65
  data-testid={testID}
61
66
  aria-label={accessibilityLabel}
62
67
  role="separator"
@@ -68,6 +73,8 @@ const Divider: React.FC<DividerProps> = ({
68
73
  <div {...lineProps} />
69
74
  </div>
70
75
  );
71
- };
76
+ });
77
+
78
+ Divider.displayName = 'Divider';
72
79
 
73
80
  export default Divider;
@@ -1,5 +1,5 @@
1
- // Platform-agnostic Divider 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 './Divider.web';
5
- export * from './types';
1
+ import DividerComponent from './Divider.web';
2
+
3
+ export default DividerComponent;
4
+ export { DividerComponent as Divider };
5
+ export * from './types';
@@ -1,3 +1,5 @@
1
- // Web-specific Divider export
2
- export { default } from './Divider.web';
3
- export * from './types';
1
+ import DividerComponent from './Divider.web';
2
+
3
+ export default DividerComponent;
4
+ export { DividerComponent as Divider };
5
+ export * from './types';
@@ -1,52 +1,61 @@
1
- import { ReactNode } from 'react';
2
- import type { IntentVariant } from '../theme/variants';
1
+ import { Intent, Size } from '@idealyst/theme';
2
+ import type { ReactNode } from 'react';
3
+ import type { StyleProp, ViewStyle } from 'react-native';
4
+
5
+ // Component-specific type aliases for future extensibility
6
+ export type DividerIntentVariant = Intent;
7
+ export type DividerOrientationVariant = 'horizontal' | 'vertical';
8
+ export type DividerType = 'solid' | 'dashed' | 'dotted';
9
+ export type DividerThicknessVariant = 'thin' | 'md' | 'thick';
10
+ export type DividerLengthVariant = 'full' | 'auto' | number;
11
+ export type DividerSpacingVariant = 'none' | Size;
3
12
 
4
13
  export interface DividerProps {
5
14
  /**
6
15
  * The orientation of the divider
7
16
  */
8
- orientation?: 'horizontal' | 'vertical';
9
-
17
+ orientation?: DividerOrientationVariant;
18
+
10
19
  /**
11
- * The visual style variant of the divider
20
+ * The visual style type of the divider
12
21
  */
13
- variant?: 'solid' | 'dashed' | 'dotted';
14
-
22
+ type?: DividerType;
23
+
15
24
  /**
16
25
  * The thickness of the divider
17
26
  */
18
- thickness?: 'thin' | 'medium' | 'thick';
19
-
27
+ thickness?: DividerThicknessVariant;
28
+
20
29
  /**
21
30
  * The color intent of the divider
22
31
  */
23
- intent?: IntentVariant;
24
-
32
+ intent?: DividerIntentVariant;
33
+
25
34
  /**
26
35
  * The length of the divider (percentage or fixed)
27
36
  */
28
- length?: 'full' | 'auto' | number;
29
-
37
+ length?: DividerLengthVariant;
38
+
30
39
  /**
31
40
  * Spacing around the divider
32
41
  */
33
- spacing?: 'none' | 'small' | 'medium' | 'large';
34
-
42
+ spacing?: DividerSpacingVariant;
43
+
35
44
  /**
36
45
  * Content to display in the center of the divider (for horizontal dividers)
37
46
  */
38
47
  children?: ReactNode;
39
-
48
+
40
49
  /**
41
50
  * Additional styles (platform-specific)
42
51
  */
43
- style?: any;
44
-
52
+ style?: StyleProp<ViewStyle>;
53
+
45
54
  /**
46
55
  * Test ID for testing
47
56
  */
48
57
  testID?: string;
49
-
58
+
50
59
  /**
51
60
  * Accessibility label
52
61
  */
@@ -1,42 +1,35 @@
1
- import React, { useMemo } from 'react';
1
+ import { forwardRef } from 'react';
2
2
  import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
3
3
  import { IconProps } from './types';
4
- import iconStyles from './Icon.styles';
4
+ import { iconStyles } from './Icon.styles';
5
5
 
6
- const Icon: React.FC<IconProps> = ({
6
+ const Icon = forwardRef<any, IconProps>(({
7
7
  name,
8
8
  size = 'md',
9
9
  color,
10
+ intent,
10
11
  style,
11
12
  testID,
12
13
  accessibilityLabel,
13
- }: IconProps) => {
14
-
15
- // Use Unistyles v3 with color and size variants
16
- const styles = iconStyles.useVariants({ color, size });
17
-
18
- // Map size variants to pixel values
19
- const iconSize = useMemo(() => {
20
- if (typeof size === 'number') return size;
21
- const sizeMap = {
22
- xs: 12,
23
- sm: 16,
24
- md: 24,
25
- lg: 32,
26
- xl: 48,
27
- };
28
- return sizeMap[size];
29
- }, [])
30
-
14
+ }: IconProps, ref) => {
15
+
16
+
17
+ // Call dynamic style with variants
18
+ const iconStyle = iconStyles.icon({ color, intent, size });
19
+
20
+ // Get fontSize from styles for numeric size prop
21
+
31
22
  return (
32
23
  <MaterialCommunityIcons
24
+ ref={ref}
33
25
  name={name}
34
- size={iconSize}
35
- style={[styles.icon, style]}
26
+ style={[iconStyle, style]}
36
27
  testID={testID}
37
28
  accessibilityLabel={accessibilityLabel}
38
29
  />
39
30
  );
40
- };
31
+ });
32
+
33
+ Icon.displayName = 'Icon';
41
34
 
42
35
  export default Icon;
@@ -1,51 +1,67 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { generateColorVariants } from '../theme/variantHelpers';
2
+ import { Theme, StylesheetStyles, Intent, Color, getColorFromString } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import { IconSizeVariant } from './types';
3
5
 
4
- const iconStyles = StyleSheet.create((theme) => ({
5
- icon: {
6
- // Default size (medium)
7
- width: 24,
8
- height: 24,
9
-
10
- // Default theme-based color
11
- color: theme.colors?.text || '#000000',
12
-
13
- // Variants for different color schemes and sizes
14
- variants: {
15
- // Dynamically generated color variants using generateColorVariants helper
16
- color: generateColorVariants(theme),
17
- size: {
18
- xs: {
19
- width: 12,
20
- height: 12,
21
- },
22
- sm: {
23
- width: 16,
24
- height: 16,
25
- },
26
- md: {
27
- width: 24,
28
- height: 24,
29
- },
30
- lg: {
31
- width: 32,
32
- height: 32,
33
- },
34
- xl: {
35
- width: 48,
36
- height: 48,
37
- },
38
- },
39
- },
40
-
41
- // Web-specific styles
42
- _web: {
43
- display: 'inline-block',
44
- verticalAlign: 'middle',
45
- flexShrink: 0,
46
- lineHeight: 0, // Remove extra space below the icon
47
- },
48
- },
49
- }));
6
+ type IconVariants = {
7
+ size: IconSizeVariant;
8
+ intent?: Intent;
9
+ color?: Color;
10
+ }
50
11
 
51
- export default iconStyles;
12
+ export type ExpandedIconStyles = StylesheetStyles<keyof IconVariants>;
13
+
14
+ export type IconStylesheet = {
15
+ icon: ExpandedIconStyles;
16
+ }
17
+
18
+ /**
19
+ * Create color variants for icon
20
+ */
21
+ function getIconColor(theme: Theme, color?: Color, intent?: Intent): string {
22
+ if (intent) {
23
+ return theme.intents[intent]?.primary
24
+ } else if (color) {
25
+ return getColorFromString(theme, color);
26
+ }
27
+ return theme.colors.text.primary;
28
+ }
29
+
30
+ function buildIconSize(theme: Theme, size: IconSizeVariant) {
31
+ const iconSize = theme.sizes.icon[size];
32
+ if (typeof iconSize === 'number') {
33
+ return {
34
+ width: iconSize,
35
+ height: iconSize,
36
+ };
37
+ }
38
+ return buildSizeVariants(theme, 'icon', (size) => ({
39
+ width: size.width,
40
+ height: size.height,
41
+ }))[size];
42
+ }
43
+
44
+ function createIconStyles(theme: Theme) {
45
+ return ({ color, intent, size }: Partial<IconVariants>) => {
46
+ const iconSize = buildIconSize(theme, size);
47
+ return {
48
+ width: iconSize.width,
49
+ height: iconSize.height,
50
+ color: getIconColor(theme, color, intent),
51
+ _web: {
52
+ display: 'inline-block',
53
+ verticalAlign: 'middle',
54
+ flexShrink: 0,
55
+ lineHeight: 0,
56
+ },
57
+ } as const;
58
+ }
59
+ }
60
+
61
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
62
+ // transform on native cannot resolve function calls to extract variant structures.
63
+ export const iconStyles = StyleSheet.create((theme: Theme) => {
64
+ return {
65
+ icon: createIconStyles(theme),
66
+ } as const;
67
+ });
@@ -1,36 +1,38 @@
1
- import React from 'react';
1
+ import React, { forwardRef } from 'react';
2
2
  import MdiIcon from '@mdi/react';
3
3
  import { IconProps } from './types';
4
- import iconStyles from './Icon.styles';
4
+ import { iconStyles } from './Icon.styles';
5
5
  import { getWebProps } from 'react-native-unistyles/web';
6
+ import useMergeRefs from '../hooks/useMergeRefs';
6
7
 
7
8
  // Internal props that include the transformed path from Babel plugin
8
9
  interface InternalIconProps extends IconProps {
9
10
  path?: string; // Added by Babel plugin transformation
10
11
  }
11
12
 
12
- const Icon: React.FC<IconProps> = (props: InternalIconProps) => {
13
+ const Icon = forwardRef<HTMLDivElement, IconProps>((props: InternalIconProps, ref) => {
13
14
  const {
14
15
  name,
15
16
  size = 'md',
16
17
  color,
18
+ intent,
17
19
  style,
18
20
  testID,
19
21
  accessibilityLabel,
20
22
  ...restProps
21
23
  } = props;
22
24
 
23
- // Use Unistyles v3 with color and size variants
24
- const styles = iconStyles.useVariants({ color, size });
25
-
26
25
  // Check if we have a path prop (from Babel plugin transformation)
27
26
  const { path } = restProps as { path?: string };
28
- const iconProps = getWebProps(styles.icon);
29
-
27
+ const iconProps = getWebProps(iconStyles.icon({ intent, color, size }));
28
+
29
+ const mergedRef = useMergeRefs(ref, iconProps.ref);
30
+
30
31
  // Use MDI React icon when path is provided (transformed by Babel plugin)
31
32
  return (
32
33
  <div
33
- {...iconProps}>
34
+ {...iconProps}
35
+ ref={mergedRef}>
34
36
  <MdiIcon
35
37
  path={path}
36
38
  size={'100%'}
@@ -40,7 +42,8 @@ const Icon: React.FC<IconProps> = (props: InternalIconProps) => {
40
42
  />
41
43
  </div>
42
44
  );
43
-
44
- };
45
+ });
46
+
47
+ Icon.displayName = 'Icon';
45
48
 
46
49
  export default Icon;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import Svg, { Path } from 'react-native-svg';
4
+
5
+ /**
6
+ * Internal component for rendering SVG icons directly from MDI paths.
7
+ * This is used internally by components like Button, Badge, etc. to render icons
8
+ * without going through the full Icon component.
9
+ *
10
+ * The path prop should be provided by the Babel plugin transformation.
11
+ */
12
+ interface IconSvgProps {
13
+ path?: string; // MDI icon path, provided by Babel plugin
14
+ size?: string | number;
15
+ color?: string;
16
+ style?: any;
17
+ 'aria-label'?: string;
18
+ 'data-testid'?: string;
19
+ }
20
+
21
+ export const IconSvg = React.forwardRef<View, IconSvgProps>(({
22
+ path,
23
+ size = 24,
24
+ color = 'currentColor',
25
+ style,
26
+ 'data-testid': testID,
27
+ }, ref) => {
28
+ // Convert size to number if it's a string
29
+ const sizeNum = typeof size === 'string' ? parseFloat(size) : size;
30
+
31
+ return (
32
+ <View ref={ref} style={[{ width: sizeNum, height: sizeNum }, style]} testID={testID}>
33
+ <Svg viewBox="0 0 24 24" width={sizeNum} height={sizeNum}>
34
+ <Path d={path} fill={color} />
35
+ </Svg>
36
+ </View>
37
+ );
38
+ });
39
+
40
+ IconSvg.displayName = 'IconSvg';
41
+
42
+ export default IconSvg;
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import MdiIcon from '@mdi/react';
3
+
4
+ /**
5
+ * Internal component for rendering SVG icons directly from MDI paths.
6
+ * This is used internally by components like Button, Badge, etc. to render icons
7
+ * without going through the full Icon component.
8
+ *
9
+ * The path prop should be provided by the Babel plugin transformation.
10
+ */
11
+ interface IconSvgProps {
12
+ path?: string; // MDI icon path, provided by Babel plugin
13
+ size?: string | number;
14
+ color?: string;
15
+ style?: React.CSSProperties;
16
+ 'aria-label'?: string;
17
+ 'data-testid'?: string;
18
+ }
19
+
20
+ export const IconSvg: React.FC<IconSvgProps> = ({
21
+ path,
22
+ color = 'currentColor',
23
+ style,
24
+ 'aria-label': ariaLabel,
25
+ 'data-testid': testID,
26
+ ...rest
27
+ }) => {
28
+ return (
29
+ <MdiIcon
30
+ style={style}
31
+ path={path}
32
+ color={color}
33
+ aria-label={ariaLabel}
34
+ data-testid={testID}
35
+ {...rest}
36
+ />
37
+ );
38
+ };
39
+
40
+ export default IconSvg;
@@ -0,0 +1 @@
1
+ export { IconSvg, default } from './IconSvg.native';
@@ -0,0 +1 @@
1
+ export { IconSvg, default } from './IconSvg.web';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Native stub for icon-resolver.
3
+ * This file should not be used on React Native as we use react-native-vector-icons instead.
4
+ * If you're seeing this file being imported, check your import paths.
5
+ */
6
+
7
+ /**
8
+ * Stub implementation for React Native.
9
+ * Always returns undefined as this utility is web-only.
10
+ */
11
+ export function resolveIconPath(iconName: string): string | undefined {
12
+ if (__DEV__) {
13
+ console.warn(
14
+ `[icon-resolver.native] resolveIconPath("${iconName}") was called on React Native. ` +
15
+ `This is a web-only utility. Use react-native-vector-icons directly instead.`
16
+ );
17
+ }
18
+ return undefined;
19
+ }
20
+
21
+ /**
22
+ * Type guard to check if a value is an icon name string.
23
+ * This implementation works cross-platform.
24
+ */
25
+ export function isIconName(icon: any): icon is string {
26
+ return typeof icon === 'string';
27
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Runtime utility for resolving MDI icon names to their SVG paths.
3
+ * This is used when icon names are passed dynamically (e.g., in arrays)
4
+ * and cannot be transformed by the Babel plugin at build time.
5
+ */
6
+
7
+ import * as mdiIcons from '@mdi/js';
8
+
9
+ /**
10
+ * Formats an icon name from kebab-case to the MDI export name format.
11
+ * Examples:
12
+ * "home" -> "mdiHome"
13
+ * "account-circle" -> "mdiAccountCircle"
14
+ * "star-outline" -> "mdiStarOutline"
15
+ */
16
+ function formatIconName(name: string): string {
17
+ if (!name || typeof name !== 'string') {
18
+ return 'mdiHelpCircle';
19
+ }
20
+
21
+ // Remove mdi: prefix if present
22
+ const cleanName = name.startsWith('mdi:') ? name.substring(4) : name;
23
+
24
+ // Check if the name contains only valid characters
25
+ if (!/^[a-zA-Z0-9-_]+$/.test(cleanName)) {
26
+ console.warn(
27
+ `[icon-resolver] Invalid icon name "${name}" (contains special characters), using "help-circle" as fallback`
28
+ );
29
+ return 'mdiHelpCircle';
30
+ }
31
+
32
+ // Convert kebab-case to PascalCase
33
+ const pascalCase = cleanName
34
+ .split('-')
35
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
36
+ .join('');
37
+
38
+ return `mdi${pascalCase}`;
39
+ }
40
+
41
+ /**
42
+ * Resolves an icon name to its SVG path data.
43
+ * Returns undefined if the icon is not found.
44
+ *
45
+ * @param iconName - The icon name in kebab-case (e.g., "home", "account-circle")
46
+ * @returns The SVG path string or undefined if not found
47
+ */
48
+ export function resolveIconPath(iconName: string): string | undefined {
49
+ const mdiIconName = formatIconName(iconName);
50
+ const iconPath = (mdiIcons as any)[mdiIconName];
51
+
52
+ if (!iconPath) {
53
+ console.warn(
54
+ `[icon-resolver] Icon "${iconName}" (${mdiIconName}) not found in @mdi/js, using help-circle as fallback`
55
+ );
56
+ return (mdiIcons as any).mdiHelpCircle;
57
+ }
58
+
59
+ return iconPath;
60
+ }
61
+
62
+ /**
63
+ * Checks if a given value is an icon name (string) or a React component.
64
+ *
65
+ * @param icon - The icon value to check
66
+ * @returns true if the icon is a string (icon name), false otherwise
67
+ */
68
+ export function isIconName(icon: any): icon is string {
69
+ return typeof icon === 'string';
70
+ }
package/src/Icon/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- // Platform-agnostic Icon 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 './Icon.web';
5
- export * from './types';
1
+ import IconComponent from './Icon.web';
2
+
3
+ export default IconComponent;
4
+ export { IconComponent as Icon };
5
+ export * from './types';
@@ -1,3 +1,5 @@
1
- // Web-specific Icon export
2
- export { default } from './Icon.web';
3
- export * from './types';
1
+ import IconComponent from './Icon.web';
2
+
3
+ export default IconComponent;
4
+ export { IconComponent as Icon };
5
+ export * from './types';