@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
package/src/Icon/types.ts CHANGED
@@ -1,34 +1,40 @@
1
- import { IconName } from "./icon-types";
2
- import type { DisplayColorVariant } from '../theme/variants';
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ import type { IconName } from "./icon-types";
3
+ import type { Size } from '@idealyst/theme';
4
+ import { Color, Intent } from '@idealyst/theme';
3
5
 
4
- export type IconSizeVariant = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
6
+ export type IconSizeVariant = Size | number;
5
7
 
6
8
  export interface IconProps {
7
9
  /**
8
10
  * The name of the icon to display
9
11
  */
10
- name: IconName;
11
-
12
+ name: IconName | `mdi:${IconName}`;
13
+
12
14
  /**
13
15
  * The size variant of the icon
14
16
  */
15
- size?: IconSizeVariant | number;
17
+ size?: IconSizeVariant;
16
18
 
17
19
  /**
18
20
  * Predefined color variant based on theme
19
21
  */
20
- color?: DisplayColorVariant;
21
-
22
+ color?: Color;
23
+ /**
24
+ * Intent variant for the icon
25
+ */
26
+ intent?: Intent;
27
+
22
28
  /**
23
29
  * Additional styles (platform-specific)
24
30
  */
25
- style?: any;
26
-
31
+ style?: React.CSSProperties | StyleProp<ViewStyle>;
32
+
27
33
  /**
28
34
  * Test ID for testing
29
35
  */
30
36
  testID?: string;
31
-
37
+
32
38
  /**
33
39
  * Accessibility label for screen readers
34
40
  */
@@ -0,0 +1,86 @@
1
+ import React, { useState, forwardRef } from 'react';
2
+ import { Image as RNImage, View } from 'react-native';
3
+ import { imageStyles } from './Image.styles';
4
+ import type { ImageProps } from './types';
5
+ import ActivityIndicator from '../ActivityIndicator';
6
+
7
+ const Image = forwardRef<View, ImageProps>(({
8
+ source,
9
+ alt,
10
+ width,
11
+ height,
12
+ aspectRatio,
13
+ objectFit = 'cover',
14
+ placeholder,
15
+ fallback,
16
+ onLoad,
17
+ onError,
18
+ borderRadius,
19
+ style,
20
+ testID,
21
+ accessibilityLabel,
22
+ }, ref) => {
23
+
24
+ const [isLoading, setIsLoading] = useState(true);
25
+ const [hasError, setHasError] = useState(false);
26
+
27
+ const handleLoad = () => {
28
+ setIsLoading(false);
29
+ onLoad?.();
30
+ };
31
+
32
+ const handleError = (e: any) => {
33
+ setIsLoading(false);
34
+ setHasError(true);
35
+ onError?.(e);
36
+ };
37
+
38
+ // Map objectFit to React Native resizeMode
39
+ const resizeMode = objectFit === 'contain' ? 'contain'
40
+ : objectFit === 'cover' ? 'cover'
41
+ : objectFit === 'fill' ? 'stretch'
42
+ : objectFit === 'scale-down' ? 'contain'
43
+ : 'cover';
44
+
45
+ const imageSource = typeof source === 'string' ? { uri: source } : source;
46
+
47
+ const containerStyle = [
48
+ imageStyles.container,
49
+ {
50
+ width: width || '100%',
51
+ height: height || undefined,
52
+ aspectRatio: aspectRatio || undefined,
53
+ borderRadius: borderRadius || undefined,
54
+ },
55
+ style,
56
+ ];
57
+
58
+ return (
59
+ <View ref={ref} style={containerStyle as any} testID={testID}>
60
+ <RNImage
61
+ source={imageSource as any}
62
+ style={[imageStyles.image, { borderRadius }]}
63
+ resizeMode={resizeMode}
64
+ onLoad={handleLoad}
65
+ onError={handleError}
66
+ accessibilityLabel={accessibilityLabel || alt}
67
+ />
68
+
69
+ {isLoading && !hasError && (
70
+ <View style={imageStyles.placeholder}>
71
+ {placeholder || <ActivityIndicator size="md" />}
72
+ </View>
73
+ )}
74
+
75
+ {hasError && (
76
+ <View style={imageStyles.fallback}>
77
+ {fallback}
78
+ </View>
79
+ )}
80
+ </View>
81
+ );
82
+ });
83
+
84
+ Image.displayName = 'Image';
85
+
86
+ export default Image;
@@ -0,0 +1,57 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles} from '@idealyst/theme';
3
+
4
+ type ImageVariants = {}
5
+
6
+ export type ExpandedImageStyles = StylesheetStyles<keyof ImageVariants>;
7
+
8
+ export type ImageStylesheet = {
9
+ container: ExpandedImageStyles;
10
+ image: ExpandedImageStyles;
11
+ placeholder: ExpandedImageStyles;
12
+ fallback: ExpandedImageStyles;
13
+ loadingIndicator: ExpandedImageStyles;
14
+ }
15
+
16
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
17
+ // transform on native cannot resolve function calls to extract variant structures.
18
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
19
+ export const imageStyles = StyleSheet.create((theme: Theme) => {
20
+ return {
21
+ container: {
22
+ position: 'relative',
23
+ overflow: 'hidden',
24
+ backgroundColor: theme.colors['gray.200'],
25
+ },
26
+ image: {
27
+ width: '100%',
28
+ height: '100%',
29
+ },
30
+ placeholder: {
31
+ position: 'absolute',
32
+ top: 0,
33
+ left: 0,
34
+ right: 0,
35
+ bottom: 0,
36
+ display: 'flex',
37
+ alignItems: 'center',
38
+ justifyContent: 'center',
39
+ backgroundColor: theme.colors['gray.200'],
40
+ },
41
+ fallback: {
42
+ position: 'absolute',
43
+ top: 0,
44
+ left: 0,
45
+ right: 0,
46
+ bottom: 0,
47
+ display: 'flex',
48
+ alignItems: 'center',
49
+ justifyContent: 'center',
50
+ backgroundColor: theme.colors['gray.300'],
51
+ color: theme.colors['gray.600'],
52
+ },
53
+ loadingIndicator: {
54
+ color: theme.colors['gray.600'],
55
+ },
56
+ };
57
+ });
@@ -0,0 +1,92 @@
1
+ import React, { useState } from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { imageStyles } from './Image.styles';
4
+ import type { ImageProps } from './types';
5
+ import ActivityIndicator from '../ActivityIndicator';
6
+
7
+ const Image: React.FC<ImageProps> = ({
8
+ source,
9
+ alt = '',
10
+ width,
11
+ height,
12
+ aspectRatio,
13
+ objectFit = 'cover',
14
+ loading = 'lazy',
15
+ placeholder,
16
+ fallback,
17
+ onLoad,
18
+ onError,
19
+ borderRadius,
20
+ style,
21
+ testID,
22
+ accessibilityLabel,
23
+ }) => {
24
+ const [isLoading, setIsLoading] = useState(true);
25
+ const [hasError, setHasError] = useState(false);
26
+
27
+ const handleLoad = () => {
28
+ setIsLoading(false);
29
+ onLoad?.();
30
+ };
31
+
32
+ const handleError = (e: React.SyntheticEvent<HTMLImageElement, Event>) => {
33
+ setIsLoading(false);
34
+ setHasError(true);
35
+ onError?.(e);
36
+ };
37
+
38
+ const imageSource = typeof source === 'string' ? source : (source as any)?.uri || '';
39
+
40
+ const containerProps = getWebProps([
41
+ imageStyles.container,
42
+ style,
43
+ {
44
+ width: width || '100%',
45
+ height: height || 'auto',
46
+ aspectRatio: aspectRatio ? String(aspectRatio) : undefined,
47
+ borderRadius: borderRadius ? `${borderRadius}px` : undefined,
48
+ } as any,
49
+ ]);
50
+
51
+ const imageProps = getWebProps([
52
+ imageStyles.image,
53
+ {
54
+ objectFit: objectFit,
55
+ borderRadius: borderRadius ? `${borderRadius}px` : undefined,
56
+ }
57
+ ]);
58
+
59
+ const placeholderProps = getWebProps([imageStyles.placeholder]);
60
+ const fallbackProps = getWebProps([imageStyles.fallback]);
61
+
62
+ return (
63
+ <div
64
+ {...containerProps}
65
+ data-testid={testID}
66
+ >
67
+ <img
68
+ {...imageProps}
69
+ src={imageSource}
70
+ alt={alt}
71
+ loading={loading}
72
+ onLoad={handleLoad}
73
+ onError={handleError}
74
+ aria-label={accessibilityLabel || alt}
75
+ />
76
+
77
+ {isLoading && !hasError && (
78
+ <div {...placeholderProps}>
79
+ {placeholder || <ActivityIndicator size="md" />}
80
+ </div>
81
+ )}
82
+
83
+ {hasError && (
84
+ <div {...fallbackProps}>
85
+ {fallback || <span>Failed to load image</span>}
86
+ </div>
87
+ )}
88
+ </div>
89
+ );
90
+ };
91
+
92
+ export default Image;
@@ -0,0 +1,5 @@
1
+ import ImageComponent from './Image.native';
2
+
3
+ export default ImageComponent;
4
+ export { ImageComponent as Image };
5
+ export * from './types';
@@ -0,0 +1,5 @@
1
+ import ImageComponent from './Image.web';
2
+
3
+ export default ImageComponent;
4
+ export { ImageComponent as Image };
5
+ export * from './types';
@@ -0,0 +1,21 @@
1
+ import type { StyleProp, ImageStyle, ImageSourcePropType } from 'react-native';
2
+
3
+ export type ImageObjectFit = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
4
+
5
+ export interface ImageProps {
6
+ source: ImageSourcePropType | string;
7
+ alt?: string;
8
+ width?: number | string;
9
+ height?: number | string;
10
+ aspectRatio?: number;
11
+ objectFit?: ImageObjectFit;
12
+ loading?: 'lazy' | 'eager';
13
+ placeholder?: React.ReactNode;
14
+ fallback?: React.ReactNode;
15
+ onLoad?: () => void;
16
+ onError?: (error: any) => void;
17
+ borderRadius?: number;
18
+ style?: StyleProp<ImageStyle>;
19
+ testID?: string;
20
+ accessibilityLabel?: string;
21
+ }
@@ -1,5 +1,6 @@
1
- import React, { useState } from 'react';
2
- import { TextInput } from 'react-native';
1
+ import React, { useState, isValidElement } from 'react';
2
+ import { View, TextInput, TouchableOpacity } from 'react-native';
3
+ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
3
4
  import { InputProps } from './types';
4
5
  import { inputStyles } from './Input.styles';
5
6
 
@@ -12,14 +13,22 @@ const Input = React.forwardRef<TextInput, InputProps>(({
12
13
  disabled = false,
13
14
  inputType = 'text',
14
15
  secureTextEntry = false,
16
+ leftIcon,
17
+ rightIcon,
18
+ showPasswordToggle,
15
19
  autoCapitalize = 'sentences',
16
- size = 'medium',
17
- variant = 'default',
20
+ size = 'md',
21
+ type = 'outlined',
18
22
  hasError = false,
19
23
  style,
20
24
  testID,
21
25
  }, ref) => {
22
26
  const [isFocused, setIsFocused] = useState(false);
27
+ const [isPasswordVisible, setIsPasswordVisible] = useState(false);
28
+
29
+ // Determine if we should show password toggle
30
+ const isPasswordField = inputType === 'password' || secureTextEntry;
31
+ const shouldShowPasswordToggle = isPasswordField && (showPasswordToggle !== false);
23
32
 
24
33
  const getKeyboardType = () => {
25
34
  switch (inputType) {
@@ -48,36 +57,104 @@ const Input = React.forwardRef<TextInput, InputProps>(({
48
57
  }
49
58
  };
50
59
 
60
+ const togglePasswordVisibility = () => {
61
+ setIsPasswordVisible(!isPasswordVisible);
62
+ };
63
+
51
64
  // Apply variants to the stylesheet
52
65
  inputStyles.useVariants({
53
66
  size,
54
- variant,
67
+ type,
55
68
  focused: isFocused,
69
+ hasError,
70
+ disabled,
56
71
  });
57
72
 
58
- const inputStyleArray = [
59
- inputStyles.input,
60
- disabled && inputStyles.disabled,
61
- hasError && inputStyles.error,
62
- style,
63
- ];
73
+ // Helper to render left icon
74
+ const renderLeftIcon = () => {
75
+ if (!leftIcon) return null;
76
+
77
+ if (typeof leftIcon === 'string') {
78
+ const iconStyle = inputStyles.leftIcon;
79
+ return (
80
+ <MaterialCommunityIcons
81
+ name={leftIcon}
82
+ size={iconStyle.width}
83
+ color={iconStyle.color}
84
+ />
85
+ );
86
+ } else if (isValidElement(leftIcon)) {
87
+ return leftIcon;
88
+ }
89
+
90
+ return null;
91
+ };
92
+
93
+ // Helper to render right icon (not password toggle)
94
+ const renderRightIcon = () => {
95
+ if (!rightIcon) return null;
96
+
97
+ if (typeof rightIcon === 'string') {
98
+ const iconStyle = inputStyles.rightIcon;
99
+ return (
100
+ <MaterialCommunityIcons
101
+ name={rightIcon}
102
+ size={iconStyle.width}
103
+ color={iconStyle.color}
104
+ />
105
+ );
106
+ } else if (isValidElement(rightIcon)) {
107
+ return rightIcon;
108
+ }
109
+
110
+ return null;
111
+ };
64
112
 
65
113
  return (
66
- <TextInput
67
- ref={ref}
68
- value={value}
69
- onChangeText={onChangeText}
70
- placeholder={placeholder}
71
- editable={!disabled}
72
- keyboardType={getKeyboardType()}
73
- secureTextEntry={secureTextEntry || inputType === 'password'}
74
- autoCapitalize={autoCapitalize}
75
- onFocus={handleFocus}
76
- onBlur={handleBlur}
77
- style={inputStyleArray}
78
- testID={testID}
79
- placeholderTextColor="#999999"
80
- />
114
+ <View style={[inputStyles.container({ type, hasError }), style]} testID={testID}>
115
+ {/* Left Icon */}
116
+ {leftIcon && (
117
+ <View style={inputStyles.leftIconContainer}>
118
+ {renderLeftIcon()}
119
+ </View>
120
+ )}
121
+
122
+ {/* Input */}
123
+ <TextInput
124
+ ref={ref}
125
+ value={value}
126
+ onChangeText={onChangeText}
127
+ placeholder={placeholder}
128
+ editable={!disabled}
129
+ keyboardType={getKeyboardType()}
130
+ secureTextEntry={(secureTextEntry || inputType === 'password') && !isPasswordVisible}
131
+ autoCapitalize={autoCapitalize}
132
+ onFocus={handleFocus}
133
+ onBlur={handleBlur}
134
+ style={inputStyles.input}
135
+ placeholderTextColor="#999999"
136
+ />
137
+
138
+ {/* Right Icon or Password Toggle */}
139
+ {shouldShowPasswordToggle ? (
140
+ <TouchableOpacity
141
+ style={inputStyles.passwordToggle}
142
+ onPress={togglePasswordVisibility}
143
+ disabled={disabled}
144
+ accessibilityLabel={isPasswordVisible ? 'Hide password' : 'Show password'}
145
+ >
146
+ <MaterialCommunityIcons
147
+ name={isPasswordVisible ? 'eye-off' : 'eye'}
148
+ size={inputStyles.passwordToggleIcon.width}
149
+ color={inputStyles.passwordToggleIcon.color}
150
+ />
151
+ </TouchableOpacity>
152
+ ) : rightIcon ? (
153
+ <View style={inputStyles.rightIconContainer}>
154
+ {renderRightIcon()}
155
+ </View>
156
+ ) : null}
157
+ </View>
81
158
  );
82
159
  });
83
160