@idealyst/components 1.0.82 → 1.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CLAUDE.md +199 -232
  2. package/README.md +5 -5
  3. package/package.json +25 -7
  4. package/plugin/README.md +272 -0
  5. package/plugin/test-cases.jsx +112 -0
  6. package/plugin/web-legacy.js +320 -0
  7. package/plugin/web.js +422 -124
  8. package/src/Accordion/Accordion.native.tsx +182 -0
  9. package/src/Accordion/Accordion.styles.tsx +260 -0
  10. package/src/Accordion/Accordion.web.tsx +147 -0
  11. package/src/Accordion/index.native.tsx +3 -0
  12. package/src/Accordion/index.ts +3 -0
  13. package/src/Accordion/index.web.tsx +3 -0
  14. package/src/Accordion/types.ts +23 -0
  15. package/src/ActivityIndicator/ActivityIndicator.native.tsx +17 -12
  16. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +83 -109
  17. package/src/ActivityIndicator/ActivityIndicator.web.tsx +23 -17
  18. package/src/ActivityIndicator/index.ts +5 -2
  19. package/src/ActivityIndicator/index.web.ts +5 -2
  20. package/src/ActivityIndicator/types.ts +15 -10
  21. package/src/Alert/Alert.native.tsx +113 -0
  22. package/src/Alert/Alert.styles.tsx +304 -0
  23. package/src/Alert/Alert.web.tsx +123 -0
  24. package/src/Alert/index.native.ts +5 -0
  25. package/src/Alert/index.ts +5 -0
  26. package/src/Alert/index.web.ts +5 -0
  27. package/src/Alert/types.ts +21 -0
  28. package/src/Avatar/Avatar.native.tsx +8 -6
  29. package/src/Avatar/Avatar.styles.tsx +64 -58
  30. package/src/Avatar/Avatar.web.tsx +13 -8
  31. package/src/Avatar/index.ts +5 -2
  32. package/src/Avatar/index.web.ts +5 -2
  33. package/src/Avatar/types.ts +19 -13
  34. package/src/Badge/Badge.native.tsx +59 -14
  35. package/src/Badge/Badge.styles.tsx +125 -139
  36. package/src/Badge/Badge.web.tsx +72 -16
  37. package/src/Badge/index.ts +5 -2
  38. package/src/Badge/index.web.ts +5 -2
  39. package/src/Badge/types.ts +23 -11
  40. package/src/Breadcrumb/Breadcrumb.native.tsx +225 -0
  41. package/src/Breadcrumb/Breadcrumb.styles.tsx +234 -0
  42. package/src/Breadcrumb/Breadcrumb.web.tsx +268 -0
  43. package/src/Breadcrumb/index.native.ts +5 -0
  44. package/src/Breadcrumb/index.ts +5 -0
  45. package/src/Breadcrumb/index.web.ts +5 -0
  46. package/src/Breadcrumb/types.ts +56 -0
  47. package/src/Button/Button.native.tsx +75 -24
  48. package/src/Button/Button.styles.tsx +248 -205
  49. package/src/Button/Button.web.tsx +82 -25
  50. package/src/Button/index.ts +5 -5
  51. package/src/Button/index.web.ts +5 -3
  52. package/src/Button/types.ts +32 -15
  53. package/src/Card/Card.native.tsx +14 -11
  54. package/src/Card/Card.styles.tsx +146 -220
  55. package/src/Card/Card.web.tsx +20 -21
  56. package/src/Card/index.ts +5 -5
  57. package/src/Card/index.web.ts +5 -3
  58. package/src/Card/types.ts +24 -17
  59. package/src/Checkbox/Checkbox.native.tsx +24 -34
  60. package/src/Checkbox/Checkbox.styles.tsx +223 -275
  61. package/src/Checkbox/Checkbox.web.tsx +30 -37
  62. package/src/Checkbox/index.ts +5 -5
  63. package/src/Checkbox/index.web.ts +5 -3
  64. package/src/Checkbox/types.ts +26 -20
  65. package/src/Chip/Chip.native.tsx +126 -0
  66. package/src/Chip/Chip.styles.tsx +138 -0
  67. package/src/Chip/Chip.web.tsx +154 -0
  68. package/src/Chip/index.native.ts +5 -0
  69. package/src/Chip/index.ts +5 -0
  70. package/src/Chip/index.web.ts +5 -0
  71. package/src/Chip/types.ts +51 -0
  72. package/src/Dialog/Dialog.native.tsx +65 -12
  73. package/src/Dialog/Dialog.styles.tsx +154 -136
  74. package/src/Dialog/Dialog.web.tsx +16 -11
  75. package/src/Dialog/index.ts +5 -2
  76. package/src/Dialog/index.web.ts +5 -2
  77. package/src/Dialog/types.ts +22 -16
  78. package/src/Divider/Divider.native.tsx +19 -14
  79. package/src/Divider/Divider.styles.tsx +273 -595
  80. package/src/Divider/Divider.web.tsx +19 -12
  81. package/src/Divider/index.ts +5 -5
  82. package/src/Divider/index.web.ts +5 -3
  83. package/src/Divider/types.ts +28 -19
  84. package/src/Icon/Icon.native.tsx +17 -24
  85. package/src/Icon/Icon.styles.tsx +64 -48
  86. package/src/Icon/Icon.web.tsx +14 -11
  87. package/src/Icon/IconSvg/IconSvg.native.tsx +42 -0
  88. package/src/Icon/IconSvg/IconSvg.web.tsx +40 -0
  89. package/src/Icon/IconSvg/index.native.ts +1 -0
  90. package/src/Icon/IconSvg/index.ts +1 -0
  91. package/src/Icon/icon-resolver.native.ts +27 -0
  92. package/src/Icon/icon-resolver.ts +70 -0
  93. package/src/Icon/index.ts +5 -5
  94. package/src/Icon/index.web.ts +5 -3
  95. package/src/Icon/types.ts +17 -11
  96. package/src/Image/Image.native.tsx +86 -0
  97. package/src/Image/Image.styles.tsx +57 -0
  98. package/src/Image/Image.web.tsx +92 -0
  99. package/src/Image/index.native.ts +5 -0
  100. package/src/Image/index.ts +5 -0
  101. package/src/Image/types.ts +21 -0
  102. package/src/Input/Input.native.tsx +103 -26
  103. package/src/Input/Input.styles.tsx +240 -177
  104. package/src/Input/Input.web.tsx +141 -38
  105. package/src/Input/index.ts +5 -5
  106. package/src/Input/index.web.ts +5 -3
  107. package/src/Input/types.ts +43 -20
  108. package/src/List/List.native.tsx +56 -0
  109. package/src/List/List.styles.tsx +257 -0
  110. package/src/List/List.web.tsx +43 -0
  111. package/src/List/ListContext.tsx +16 -0
  112. package/src/List/ListItem.native.tsx +111 -0
  113. package/src/List/ListItem.web.tsx +110 -0
  114. package/src/List/ListSection.native.tsx +31 -0
  115. package/src/List/ListSection.web.tsx +33 -0
  116. package/src/List/index.native.tsx +5 -0
  117. package/src/List/index.ts +5 -0
  118. package/src/List/index.web.tsx +5 -0
  119. package/src/List/types.ts +42 -0
  120. package/src/Menu/Menu.native.tsx +150 -0
  121. package/src/Menu/Menu.styles.tsx +185 -0
  122. package/src/Menu/Menu.web.tsx +99 -0
  123. package/src/Menu/MenuItem.native.tsx +66 -0
  124. package/src/Menu/MenuItem.styles.tsx +119 -0
  125. package/src/Menu/MenuItem.web.tsx +67 -0
  126. package/src/Menu/index.native.ts +3 -0
  127. package/src/Menu/index.ts +3 -0
  128. package/src/Menu/index.web.ts +3 -0
  129. package/src/Menu/types.ts +30 -0
  130. package/src/Popover/Popover.native.tsx +102 -32
  131. package/src/Popover/Popover.styles.tsx +100 -67
  132. package/src/Popover/Popover.web.tsx +36 -260
  133. package/src/Popover/index.ts +5 -2
  134. package/src/Popover/index.web.ts +5 -2
  135. package/src/Popover/types.ts +14 -13
  136. package/src/Pressable/Pressable.native.tsx +7 -6
  137. package/src/Pressable/Pressable.web.tsx +8 -6
  138. package/src/Pressable/index.ts +5 -2
  139. package/src/Pressable/index.web.ts +5 -2
  140. package/src/Pressable/types.ts +11 -10
  141. package/src/Progress/Progress.native.tsx +179 -0
  142. package/src/Progress/Progress.styles.tsx +164 -0
  143. package/src/Progress/Progress.web.tsx +144 -0
  144. package/src/Progress/index.native.ts +1 -0
  145. package/src/Progress/index.ts +5 -0
  146. package/src/Progress/index.web.ts +5 -0
  147. package/src/Progress/types.ts +21 -0
  148. package/src/RadioButton/RadioButton.native.tsx +88 -0
  149. package/src/RadioButton/RadioButton.styles.tsx +163 -0
  150. package/src/RadioButton/RadioButton.web.tsx +85 -0
  151. package/src/RadioButton/RadioGroup.native.tsx +43 -0
  152. package/src/RadioButton/RadioGroup.web.tsx +49 -0
  153. package/src/RadioButton/index.native.ts +2 -0
  154. package/src/RadioButton/index.ts +2 -0
  155. package/src/RadioButton/index.web.ts +2 -0
  156. package/src/RadioButton/types.ts +29 -0
  157. package/src/SVGImage/SVGImage.native.tsx +9 -7
  158. package/src/SVGImage/SVGImage.styles.tsx +63 -55
  159. package/src/SVGImage/SVGImage.web.tsx +16 -13
  160. package/src/SVGImage/index.ts +5 -5
  161. package/src/SVGImage/index.web.ts +5 -2
  162. package/src/SVGImage/types.ts +7 -3
  163. package/src/Screen/Screen.native.tsx +43 -17
  164. package/src/Screen/Screen.styles.tsx +58 -54
  165. package/src/Screen/Screen.web.tsx +11 -5
  166. package/src/Screen/index.ts +5 -2
  167. package/src/Screen/index.web.ts +5 -2
  168. package/src/Screen/types.ts +23 -9
  169. package/src/Select/Select.native.tsx +347 -0
  170. package/src/Select/Select.styles.tsx +335 -0
  171. package/src/Select/Select.web.tsx +276 -0
  172. package/src/Select/index.native.ts +2 -0
  173. package/src/Select/index.ts +5 -0
  174. package/src/Select/index.web.ts +5 -0
  175. package/src/Select/types.ts +124 -0
  176. package/src/Skeleton/Skeleton.native.tsx +139 -0
  177. package/src/Skeleton/Skeleton.styles.tsx +59 -0
  178. package/src/Skeleton/Skeleton.web.tsx +112 -0
  179. package/src/Skeleton/index.native.ts +4 -0
  180. package/src/Skeleton/index.ts +5 -0
  181. package/src/Skeleton/index.web.ts +5 -0
  182. package/src/Skeleton/types.ts +75 -0
  183. package/src/Slider/Slider.native.tsx +248 -0
  184. package/src/Slider/Slider.styles.tsx +241 -0
  185. package/src/Slider/Slider.web.tsx +226 -0
  186. package/src/Slider/index.native.ts +3 -0
  187. package/src/Slider/index.ts +5 -0
  188. package/src/Slider/index.web.ts +5 -0
  189. package/src/Slider/types.ts +31 -0
  190. package/src/Switch/Switch.native.tsx +131 -0
  191. package/src/Switch/Switch.styles.tsx +169 -0
  192. package/src/Switch/Switch.web.tsx +121 -0
  193. package/src/Switch/index.native.ts +3 -0
  194. package/src/Switch/index.ts +5 -0
  195. package/src/Switch/index.web.ts +5 -0
  196. package/src/Switch/types.ts +21 -0
  197. package/src/TabBar/TabBar.native.tsx +142 -0
  198. package/src/TabBar/TabBar.styles.tsx +399 -0
  199. package/src/TabBar/TabBar.web.tsx +205 -0
  200. package/src/TabBar/index.native.tsx +3 -0
  201. package/src/TabBar/index.ts +3 -0
  202. package/src/TabBar/index.web.tsx +3 -0
  203. package/src/TabBar/types.ts +26 -0
  204. package/src/Table/Table.native.tsx +122 -0
  205. package/src/Table/Table.styles.tsx +283 -0
  206. package/src/Table/Table.web.tsx +112 -0
  207. package/src/Table/index.native.tsx +3 -0
  208. package/src/Table/index.ts +3 -0
  209. package/src/Table/index.web.tsx +3 -0
  210. package/src/Table/types.ts +28 -0
  211. package/src/Text/Text.native.tsx +12 -11
  212. package/src/Text/Text.styles.tsx +76 -64
  213. package/src/Text/Text.web.tsx +14 -9
  214. package/src/Text/index.ts +5 -5
  215. package/src/Text/index.web.ts +5 -3
  216. package/src/Text/types.ts +20 -13
  217. package/src/TextArea/TextArea.native.tsx +134 -0
  218. package/src/TextArea/TextArea.styles.tsx +175 -0
  219. package/src/TextArea/TextArea.web.tsx +156 -0
  220. package/src/TextArea/index.native.ts +3 -0
  221. package/src/TextArea/index.ts +3 -0
  222. package/src/TextArea/index.web.ts +3 -0
  223. package/src/TextArea/types.ts +30 -0
  224. package/src/Tooltip/Tooltip.native.tsx +165 -0
  225. package/src/Tooltip/Tooltip.styles.tsx +73 -0
  226. package/src/Tooltip/Tooltip.web.tsx +87 -0
  227. package/src/Tooltip/index.native.ts +3 -0
  228. package/src/Tooltip/index.ts +3 -0
  229. package/src/Tooltip/types.ts +18 -0
  230. package/src/Video/Video.native.tsx +105 -0
  231. package/src/Video/Video.styles.tsx +39 -0
  232. package/src/Video/Video.web.tsx +115 -0
  233. package/src/Video/index.native.ts +5 -0
  234. package/src/Video/index.ts +5 -0
  235. package/src/Video/types.ts +29 -0
  236. package/src/View/View.native.tsx +9 -14
  237. package/src/View/View.styles.tsx +101 -93
  238. package/src/View/View.web.tsx +16 -17
  239. package/src/View/index.ts +5 -5
  240. package/src/View/index.web.ts +5 -3
  241. package/src/View/types.ts +29 -21
  242. package/src/examples/AccordionExamples.tsx +126 -0
  243. package/src/examples/AlertExamples.tsx +280 -0
  244. package/src/examples/AvatarExamples.tsx +23 -23
  245. package/src/examples/BadgeExamples.tsx +109 -41
  246. package/src/examples/BreadcrumbExamples.tsx +312 -0
  247. package/src/examples/ButtonExamples.tsx +160 -33
  248. package/src/examples/CardExamples.tsx +40 -40
  249. package/src/examples/CheckboxExamples.tsx +12 -12
  250. package/src/examples/ChipExamples.tsx +197 -0
  251. package/src/examples/DialogExamples.tsx +22 -22
  252. package/src/examples/DividerExamples.tsx +49 -49
  253. package/src/examples/IconExamples.tsx +270 -54
  254. package/src/examples/ImageExamples.tsx +174 -0
  255. package/src/examples/InputExamples.tsx +75 -17
  256. package/src/examples/ListExamples.tsx +288 -0
  257. package/src/examples/MenuExamples.tsx +144 -0
  258. package/src/examples/PopoverExamples.tsx +69 -73
  259. package/src/examples/ProgressExamples.tsx +137 -0
  260. package/src/examples/RadioButtonExamples.tsx +161 -0
  261. package/src/examples/SVGImageExamples.tsx +19 -17
  262. package/src/examples/ScreenExamples.tsx +31 -31
  263. package/src/examples/SelectExamples.tsx +423 -0
  264. package/src/examples/SkeletonExamples.tsx +206 -0
  265. package/src/examples/SliderExamples.tsx +200 -0
  266. package/src/examples/SwitchExamples.tsx +182 -0
  267. package/src/examples/TabBarExamples.tsx +143 -0
  268. package/src/examples/TableExamples.tsx +280 -0
  269. package/src/examples/TextAreaExamples.tsx +173 -0
  270. package/src/examples/TextExamples.tsx +28 -32
  271. package/src/examples/ThemeExtensionExamples.tsx +10 -10
  272. package/src/examples/TooltipExamples.tsx +126 -0
  273. package/src/examples/VideoExamples.tsx +144 -0
  274. package/src/examples/ViewExamples.tsx +64 -56
  275. package/src/examples/index.ts +18 -3
  276. package/src/hooks/useMergeRefs.ts +16 -0
  277. package/src/hooks/useSmartPosition.native.ts +169 -0
  278. package/src/index.native.ts +80 -9
  279. package/src/index.ts +75 -1
  280. package/src/internal/BoundedModalContent.native.tsx +58 -0
  281. package/src/internal/PositionedPortal.tsx +254 -0
  282. package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
  283. package/src/unistyles.d.ts +6 -0
  284. package/src/utils/buildSizeVariants.ts +16 -0
  285. package/src/utils/deepMerge.ts +43 -0
  286. package/src/utils/positionUtils.native.ts +280 -0
  287. package/src/utils/styleHelpers.ts +48 -0
  288. package/LLM-ACCESS-GUIDE.md +0 -143
  289. package/src/ActivityIndicator/README.md +0 -132
  290. package/src/Avatar/README.md +0 -139
  291. package/src/Badge/README.md +0 -170
  292. package/src/Button/Button.types.ts +0 -12
  293. package/src/Button/README.md +0 -262
  294. package/src/Card/README.md +0 -258
  295. package/src/Checkbox/README.md +0 -102
  296. package/src/Dialog/README.md +0 -210
  297. package/src/Divider/README.md +0 -108
  298. package/src/Icon/README.md +0 -81
  299. package/src/Input/README.md +0 -100
  300. package/src/SVGImage/README.md +0 -209
  301. package/src/Screen/README.md +0 -86
  302. package/src/Text/README.md +0 -94
  303. package/src/View/README.md +0 -107
  304. package/src/examples/AllExamples.tsx +0 -84
  305. package/src/examples/README.md +0 -136
  306. package/src/examples/ValidationExamples.tsx +0 -95
  307. package/src/examples/extendedTheme.ts +0 -329
  308. package/src/theme/breakpoints.ts +0 -8
  309. package/src/theme/colorResolver.ts +0 -218
  310. package/src/theme/colors.ts +0 -315
  311. package/src/theme/defaultThemes.ts +0 -326
  312. package/src/theme/index.ts +0 -188
  313. package/src/theme/themeBuilder.ts +0 -602
  314. package/src/theme/unistyles.d.ts +0 -6
  315. package/src/theme/variantHelpers.ts +0 -584
  316. package/src/theme/variants.ts +0 -56
@@ -1,2 +1,5 @@
1
- export { default } from './Avatar.web';
2
- export * from './types';
1
+ import AvatarComponent from './Avatar.web';
2
+
3
+ export default AvatarComponent;
4
+ export { AvatarComponent as Avatar };
5
+ export * from './types';
@@ -1,2 +1,5 @@
1
- export { default } from './Avatar.web';
2
- export * from './types';
1
+ import AvatarComponent from './Avatar.web';
2
+
3
+ export default AvatarComponent;
4
+ export { AvatarComponent as Avatar };
5
+ export * from './types';
@@ -1,41 +1,47 @@
1
- import type { ColorVariant } from '../theme/variants';
1
+ import { Color } from '@idealyst/theme';
2
+ import type { StyleProp, ViewStyle, ImageSourcePropType } from 'react-native';
3
+
4
+ // Component-specific type aliases for future extensibility
5
+ export type AvatarColorVariant = Color;
6
+ export type AvatarSizeVariant = 'sm' | 'md' | 'lg' | 'xl';
7
+ export type AvatarShapeVariant = 'circle' | 'square';
2
8
 
3
9
  export interface AvatarProps {
4
10
  /**
5
11
  * Image source (URL or require())
6
12
  */
7
- src?: string | any;
8
-
13
+ src?: string | ImageSourcePropType;
14
+
9
15
  /**
10
16
  * Alt text for the image
11
17
  */
12
18
  alt?: string;
13
-
19
+
14
20
  /**
15
21
  * Fallback text (usually initials)
16
22
  */
17
23
  fallback?: string;
18
-
24
+
19
25
  /**
20
26
  * Size of the avatar
21
27
  */
22
- size?: 'small' | 'medium' | 'large' | 'xlarge';
23
-
28
+ size?: AvatarSizeVariant;
29
+
24
30
  /**
25
31
  * Shape of the avatar
26
32
  */
27
- shape?: 'circle' | 'square';
28
-
33
+ shape?: AvatarShapeVariant;
34
+
29
35
  /**
30
36
  * The color scheme of the avatar (for background when no image)
31
37
  */
32
- color?: ColorVariant;
33
-
38
+ color?: AvatarColorVariant;
39
+
34
40
  /**
35
41
  * Additional styles
36
42
  */
37
- style?: any;
38
-
43
+ style?: StyleProp<ViewStyle>;
44
+
39
45
  /**
40
46
  * Test ID for testing
41
47
  */
@@ -1,43 +1,88 @@
1
- import React from 'react';
1
+ import React, { isValidElement, forwardRef } from 'react';
2
2
  import { View, Text } from 'react-native';
3
+ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
3
4
  import { BadgeProps } from './types';
4
5
  import { badgeStyles } from './Badge.styles';
6
+ import { isIconName } from '../Icon/icon-resolver';
5
7
 
6
- const Badge: React.FC<BadgeProps> = ({
8
+ const Badge = forwardRef<View, BadgeProps>(({
7
9
  children,
8
- size = 'medium',
9
- variant = 'filled',
10
+ icon,
11
+ size = 'md',
12
+ type = 'filled',
10
13
  color = 'blue',
11
14
  style,
12
15
  testID,
13
- }) => {
16
+ }, ref) => {
14
17
  badgeStyles.useVariants({
15
18
  size,
16
- variant: variant as any,
17
- color,
19
+ type,
18
20
  });
19
21
 
20
- if (variant === 'dot') {
22
+ // Call dynamic styles with color variant
23
+ const badgeStyle = badgeStyles.badge({ color });
24
+ const textStyle = badgeStyles.text({ color });
25
+
26
+ // Map badge size to icon size
27
+ const iconSize = size === 'sm' ? 12 : size === 'md' ? 14 : 16;
28
+
29
+ // Helper to render icon
30
+ const renderIcon = () => {
31
+ if (!icon) return null;
32
+
33
+ if (typeof icon === 'string' && isIconName(icon)) {
34
+ return (
35
+ <MaterialCommunityIcons
36
+ name={icon}
37
+ size={iconSize}
38
+ color={textStyle.color}
39
+ />
40
+ );
41
+ } else if (isValidElement(icon)) {
42
+ return icon;
43
+ }
44
+ return null;
45
+ };
46
+
47
+
48
+ if (type === 'dot') {
21
49
  return (
22
50
  <View
23
- style={[badgeStyles.badge, style]}
51
+ style={[badgeStyle, style]}
24
52
  testID={testID}
25
53
  accessibilityLabel="status indicator"
26
54
  />
27
55
  );
28
56
  }
29
57
 
58
+ const hasIcon = Boolean(icon);
59
+ const hasChildren = Boolean(children);
60
+
30
61
  return (
31
62
  <View
32
- style={[badgeStyles.badge, style]}
63
+ ref={ref}
64
+ style={[badgeStyle, style]}
33
65
  testID={testID}
34
66
  accessibilityLabel="badge"
35
67
  >
36
- <Text style={badgeStyles.text}>
37
- {children}
38
- </Text>
68
+ {hasIcon && hasChildren ? (
69
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
70
+ {renderIcon()}
71
+ <Text style={textStyle}>
72
+ {children}
73
+ </Text>
74
+ </View>
75
+ ) : hasIcon ? (
76
+ renderIcon()
77
+ ) : (
78
+ <Text style={textStyle}>
79
+ {children}
80
+ </Text>
81
+ )}
39
82
  </View>
40
83
  );
41
- };
84
+ });
85
+
86
+ Badge.displayName = 'Badge';
42
87
 
43
88
  export default Badge;
@@ -1,154 +1,140 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { generateBadgeCompoundVariants } from '../theme/variantHelpers';
2
+ import { Theme, StylesheetStyles, getColorFromString, Size, Color } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
3
4
 
4
- export const badgeStyles = StyleSheet.create((theme) => ({
5
- badge: {
6
- alignItems: 'center',
7
- justifyContent: 'center',
8
- borderRadius: 9999,
9
-
10
- // Web-specific styles for better text centering
11
- _web: {
12
- display: 'flex',
13
- alignItems: 'center',
14
- justifyContent: 'center',
15
- boxSizing: 'border-box',
16
- // Text styles for web (since text is rendered directly in the container)
17
- fontSize: 12, // Default medium size
18
- fontWeight: '600',
19
- color: '#ffffff',
20
- lineHeight: 1, // Use ratio instead of fixed value for better centering
21
- },
22
-
23
- variants: {
24
- size: {
25
- small: {
26
- minWidth: 16,
27
- height: 16,
28
- paddingHorizontal: 4,
29
- },
30
- medium: {
31
- minWidth: 20,
32
- height: 20,
33
- paddingHorizontal: 6,
34
- },
35
- large: {
36
- minWidth: 24,
37
- height: 24,
38
- paddingHorizontal: 8,
39
- },
40
- },
41
- variant: {
5
+ type BadgeType = 'filled' | 'outlined' | 'dot';
6
+
7
+ type BadgeVariants = {
8
+ size: Size;
9
+ type: BadgeType;
10
+ color: Color;
11
+ }
12
+
13
+ export type ExpandedBadgeStyles = StylesheetStyles<keyof BadgeVariants>;
14
+
15
+ export type BadgeStylesheet = {
16
+ badge: ExpandedBadgeStyles;
17
+ content: ExpandedBadgeStyles;
18
+ icon: ExpandedBadgeStyles;
19
+ text: ExpandedBadgeStyles;
20
+ }
21
+
22
+ /**
23
+ * Create type variants for badge
24
+ */
25
+ function createBadgeTypeVariants(theme: Theme, color: Color) {
26
+ const colorValue = getColorFromString(theme, color);
27
+ return {
42
28
  filled: {
43
- // Colors handled by compound variants
29
+ borderWidth: 0,
30
+ backgroundColor: colorValue,
44
31
  },
45
32
  outlined: {
46
- backgroundColor: 'transparent',
47
- borderWidth: 1,
33
+ backgroundColor: 'transparent',
34
+ borderWidth: 2,
35
+ borderStyle: 'solid',
36
+ borderColor: colorValue,
48
37
  },
49
38
  dot: {
50
- minWidth: 8,
51
- width: 8,
52
- height: 8,
53
- paddingHorizontal: 0, // Override size variant padding
54
- paddingVertical: 0,
55
- },
56
- },
57
- // Dynamically generated color variants (placeholder - actual styling in compound variants)
58
- color: Object.fromEntries([
59
- // All palette colors including shade-specific variants
60
- ...(theme.palettes ? Object.entries(theme.palettes).flatMap(([colorKey, palette]) => [
61
- // Base color
62
- [colorKey, {}],
63
- // All shade variants
64
- ...Object.keys(palette).map((shade) => [`${colorKey}.${shade}`, {}])
65
- ]) : [
66
- // Fallback base colors
67
- ['blue', {}], ['green', {}], ['red', {}], ['amber', {}],
68
- ['gray', {}], ['purple', {}], ['pink', {}], ['cyan', {}]
69
- ]),
70
- // Semantic colors
71
- ['primary', {}],
72
- ['secondary', {}],
73
- ['disabled', {}],
74
- ['inverse', {}],
75
- ['muted', {}],
76
- ]),
77
- },
78
-
79
- // Dynamically generated compound variants plus static size variants
80
- compoundVariants: [
81
- ...generateBadgeCompoundVariants(theme),
82
- // Web-specific text size variants
83
- {
84
- size: 'small',
85
- styles: {
86
- _web: {
87
- fontSize: 10,
88
- },
89
- },
90
- },
91
- {
92
- size: 'medium',
93
- styles: {
94
- _web: {
95
- fontSize: 12,
96
- },
97
- },
98
- },
99
- {
100
- size: 'large',
101
- styles: {
102
- _web: {
103
- fontSize: 14,
104
- },
105
- },
106
- },
107
- ],
108
- },
109
-
110
- text: {
111
- color: '#ffffff',
112
- fontWeight: '600',
113
- textAlign: 'center',
114
-
115
- variants: {
116
- size: {
117
- small: {
118
- fontSize: 10,
119
- lineHeight: 12, // Tight line height for better vertical centering
120
- },
121
- medium: {
122
- fontSize: 12,
123
- lineHeight: 14,
124
- },
125
- large: {
126
- fontSize: 14,
127
- lineHeight: 16,
128
- },
129
- },
130
- variant: {
39
+ minWidth: 8,
40
+ width: 8,
41
+ height: 8,
42
+ paddingHorizontal: 0,
43
+ paddingVertical: 0,
44
+ backgroundColor: colorValue,
45
+ },
46
+ } as const;
47
+ }
48
+
49
+ /**
50
+ * Create type variants for badge text
51
+ */
52
+ function createTextTypeVariants(theme: Theme, color: Color){
53
+ const colorValue = getColorFromString(theme, color);
54
+ return {
131
55
  filled: {
132
- color: '#ffffff',
56
+ color: theme.colors.text.inverse,
133
57
  },
134
58
  outlined: {
135
- // Colors handled by compound variants
59
+ color: colorValue,
136
60
  },
137
61
  dot: {
138
- // No text for dot variant
62
+ display: 'none',
139
63
  },
140
- },
64
+ } as const;
65
+ }
66
+
67
+ /**
68
+ * Generate badge container styles
69
+ */
70
+ function createBadgeStyles(theme: Theme) {
71
+ return ({ color }: Partial<BadgeVariants>) => {
72
+ return {
73
+ alignItems: 'center',
74
+ justifyContent: 'center',
75
+ borderRadius: 9999,
76
+ variants: {
77
+ size: buildSizeVariants(theme, 'badge', (size) => ({
78
+ minWidth: size.minWidth,
79
+ height: size.height,
80
+ paddingHorizontal: size.paddingHorizontal,
81
+ })),
82
+ type: createBadgeTypeVariants(theme, color),
83
+ },
84
+ _web: {
85
+ display: 'flex',
86
+ alignItems: 'center',
87
+ justifyContent: 'center',
88
+ boxSizing: 'border-box',
89
+ fontWeight: '600',
90
+ lineHeight: 1,
91
+ },
92
+ } as const;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Generate badge text styles
98
+ */
99
+ function createTextStyles(theme: Theme) {
100
+ return ({ color }: Partial<BadgeVariants>) => {
101
+ return {
102
+ fontWeight: '600',
103
+ textAlign: 'center',
104
+ variants: {
105
+ size: buildSizeVariants(theme, 'badge', (size) => ({
106
+ fontSize: size.fontSize,
107
+ lineHeight: size.lineHeight,
108
+ })),
109
+ type: createTextTypeVariants(theme, color),
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 badgeStyles = StyleSheet.create((theme: Theme) => {
118
+ return {
119
+ badge: createBadgeStyles(theme),
120
+ content: {
121
+ display: 'flex',
122
+ flexDirection: 'row',
123
+ alignItems: 'center',
124
+ justifyContent: 'center',
125
+ gap: 4,
141
126
  },
142
-
143
- compoundVariants: [
144
- // Outlined variant text colors - dynamically generated
145
- ...Object.entries(theme.palettes || {}).map(([colorKey, palette]: [string, any]) => ({
146
- variant: 'outlined',
147
- color: colorKey,
148
- styles: {
149
- color: palette?.[500] || '#6b7280',
127
+ icon: {
128
+ display: 'flex',
129
+ alignItems: 'center',
130
+ justifyContent: 'center',
131
+ variants: {
132
+ size: buildSizeVariants(theme, 'badge', (size) => ({
133
+ width: size.iconSize,
134
+ height: size.iconSize,
135
+ })),
150
136
  },
151
- })),
152
- ],
153
- },
154
- }));
137
+ },
138
+ text: createTextStyles(theme),
139
+ };
140
+ });
@@ -1,29 +1,68 @@
1
- import React from 'react';
1
+ import React, { isValidElement, forwardRef } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { BadgeProps } from './types';
4
4
  import { badgeStyles } from './Badge.styles';
5
+ import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
6
+ import useMergeRefs from '../hooks/useMergeRefs';
7
+
8
+ // Extended props to include path props added by Babel plugin
9
+ interface InternalBadgeProps extends BadgeProps {
10
+ iconPath?: string;
11
+ }
12
+
13
+ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props: InternalBadgeProps, ref) => {
14
+ const {
15
+ children,
16
+ size = 'md',
17
+ type = 'filled',
18
+ color = 'blue',
19
+ icon,
20
+ iconPath,
21
+ style,
22
+ testID,
23
+ } = props;
5
24
 
6
- const Badge: React.FC<BadgeProps> = ({
7
- children,
8
- size = 'medium',
9
- variant = 'filled',
10
- color = 'blue',
11
- style,
12
- testID,
13
- }) => {
14
25
  badgeStyles.useVariants({
15
26
  size,
16
- variant: variant as any,
17
- color,
27
+ type,
18
28
  });
29
+
30
+ const badgeStyle = badgeStyles.badge({ color });
31
+ const contentStyle = badgeStyles.content;
32
+ const textStyle = badgeStyles.text({ color });
33
+
34
+ const badgeProps = getWebProps(badgeStyle);
35
+ const contentProps = getWebProps(contentStyle);
36
+ const textProps = getWebProps(textStyle);
37
+ const iconProps = getWebProps([badgeStyles.icon, textStyle]);
38
+
39
+ // Helper to render icon
40
+ const renderIcon = (iconProp: typeof icon, path?: string) => {
41
+ if (typeof iconProp === 'string' && path) {
42
+ // Render IconSvg directly with the path from Babel plugin
43
+ // Don't pass size prop - let the style control the dimensions entirely
44
+ return (
45
+ <IconSvg
46
+ path={path}
47
+ {...iconProps}
48
+ aria-label={iconProp}
49
+ />
50
+ );
51
+ } else if (isValidElement(iconProp)) {
52
+ // Render custom component as-is
53
+ return iconProp;
54
+ }
55
+ return null;
56
+ };
19
57
 
20
- const badgeStyleArray = [badgeStyles.badge, style];
21
- const badgeProps = getWebProps(badgeStyleArray);
58
+ const mergedRef = useMergeRefs(ref, badgeProps.ref);
22
59
 
23
- if (variant === 'dot') {
60
+ if (type === 'dot') {
24
61
  return (
25
62
  <span
26
63
  {...badgeProps}
64
+ style={style as React.CSSProperties}
65
+ ref={mergedRef}
27
66
  data-testid={testID}
28
67
  role="status"
29
68
  aria-label="status indicator"
@@ -31,15 +70,32 @@ const Badge: React.FC<BadgeProps> = ({
31
70
  );
32
71
  }
33
72
 
73
+ const hasIcon = !!icon;
74
+
34
75
  return (
35
76
  <span
36
77
  {...badgeProps}
78
+ style={style as React.CSSProperties}
79
+ ref={mergedRef}
37
80
  data-testid={testID}
38
81
  role="status"
39
82
  >
40
- {children}
83
+ {hasIcon ? (
84
+ <span {...contentProps}>
85
+ {renderIcon(icon, iconPath)}
86
+ <span {...textProps}>
87
+ {children}
88
+ </span>
89
+ </span>
90
+ ) : (
91
+ <span {...textProps}>
92
+ {children}
93
+ </span>
94
+ )}
41
95
  </span>
42
96
  );
43
- };
97
+ });
98
+
99
+ Badge.displayName = 'Badge';
44
100
 
45
101
  export default Badge;
@@ -1,2 +1,5 @@
1
- export { default } from './Badge.web';
2
- export * from './types';
1
+ import BadgeComponent from './Badge.web';
2
+
3
+ export default BadgeComponent;
4
+ export { BadgeComponent as Badge };
5
+ export * from './types';
@@ -1,2 +1,5 @@
1
- export { default } from './Badge.web';
2
- export * from './types';
1
+ import BadgeComponent from './Badge.web';
2
+
3
+ export default BadgeComponent;
4
+ export { BadgeComponent as Badge };
5
+ export * from './types';
@@ -1,32 +1,44 @@
1
- import { ReactNode } from 'react';
2
- import type { DisplayColorVariant } from '../theme/variants';
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import type { IconName } from '../Icon/icon-types';
4
+ import { Color, Size } from '@idealyst/theme';
5
+
6
+ // Component-specific type aliases for future extensibility
7
+ export type BadgeColorVariant = Color;
8
+ export type BadgeSizeVariant = Size;
9
+ export type BadgeType = 'filled' | 'outlined' | 'dot';
3
10
 
4
11
  export interface BadgeProps {
5
12
  /**
6
13
  * The content to display inside the badge
7
14
  */
8
15
  children?: ReactNode;
9
-
16
+
10
17
  /**
11
18
  * The size of the badge
12
19
  */
13
- size?: 'small' | 'medium' | 'large';
14
-
20
+ size?: BadgeSizeVariant;
21
+
15
22
  /**
16
23
  * The visual style variant of the badge
17
24
  */
18
- variant?: 'filled' | 'outlined' | 'dot';
19
-
25
+ type?: BadgeType;
26
+
20
27
  /**
21
28
  * The color scheme of the badge
22
29
  */
23
- color?: DisplayColorVariant;
24
-
30
+ color?: BadgeColorVariant;
31
+
32
+ /**
33
+ * Icon to display. Can be an icon name or custom component (ReactNode)
34
+ */
35
+ icon?: IconName | ReactNode;
36
+
25
37
  /**
26
38
  * Additional styles
27
39
  */
28
- style?: any;
29
-
40
+ style?: CSSProperties | StyleProp<ViewStyle>;
41
+
30
42
  /**
31
43
  * Test ID for testing
32
44
  */