@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,3 +1,5 @@
1
- // Web-specific Input export
2
- export { default } from './Input.web';
3
- export * from './types';
1
+ import InputComponent from './Input.web';
2
+
3
+ export default InputComponent;
4
+ export { InputComponent as Input };
5
+ export * from './types';
@@ -1,77 +1,100 @@
1
- import type { IntentVariant } from '../theme/variants';
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ import type { IconName } from '../Icon/icon-types';
3
+ import { Intent, Size } from '@idealyst/theme';
4
+
5
+ // Component-specific type aliases for future extensibility
6
+ export type InputIntent = Intent;
7
+ export type InputSize = Size;
8
+ export type InputType = 'outlined' | 'filled' | 'bare';
9
+ export type InputInputType = 'text' | 'email' | 'password' | 'number';
2
10
 
3
11
  export interface InputProps {
4
12
  /**
5
13
  * The current value of the input
6
14
  */
7
15
  value?: string;
8
-
16
+
9
17
  /**
10
18
  * Called when the text changes
11
19
  */
12
20
  onChangeText?: (text: string) => void;
13
-
21
+
14
22
  /**
15
23
  * Called when the input receives focus
16
24
  */
17
25
  onFocus?: () => void;
18
-
26
+
19
27
  /**
20
28
  * Called when the input loses focus
21
29
  */
22
30
  onBlur?: () => void;
23
-
31
+
24
32
  /**
25
33
  * Placeholder text
26
34
  */
27
35
  placeholder?: string;
28
-
36
+
29
37
  /**
30
38
  * Whether the input is disabled
31
39
  */
32
40
  disabled?: boolean;
33
-
41
+
34
42
  /**
35
43
  * The type of input (affects keyboard type on mobile)
36
44
  */
37
- inputType?: 'text' | 'email' | 'password' | 'number';
38
-
45
+ inputType?: InputInputType;
46
+
39
47
  /**
40
48
  * Whether to show the password
41
49
  */
42
50
  secureTextEntry?: boolean;
43
-
51
+
52
+ /**
53
+ * Icon to display on the left side of the input
54
+ */
55
+ leftIcon?: IconName | React.ReactNode;
56
+
57
+ /**
58
+ * Icon to display on the right side of the input
59
+ */
60
+ rightIcon?: IconName | React.ReactNode;
61
+
62
+ /**
63
+ * Show password visibility toggle for password inputs (defaults to true for password type)
64
+ */
65
+ showPasswordToggle?: boolean;
66
+
44
67
  /**
45
68
  * Auto-capitalization behavior
46
69
  */
47
70
  autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
48
-
71
+
49
72
  /**
50
73
  * Size variant of the input
51
74
  */
52
- size?: 'small' | 'medium' | 'large';
53
-
75
+ size?: InputSize;
76
+
54
77
  /**
55
78
  * Style variant of the input
56
79
  */
57
- variant?: 'default' | 'outlined' | 'filled' | 'bare';
58
-
80
+ type?: InputType;
81
+
59
82
  /**
60
83
  * The intent/color scheme of the input (for focus states, validation, etc.)
61
84
  */
62
- intent?: IntentVariant;
63
-
85
+ intent?: InputIntent;
86
+
64
87
  /**
65
88
  * Whether the input has an error state
66
89
  * @deprecated Use intent="error" instead
67
90
  */
68
91
  hasError?: boolean;
69
-
92
+
70
93
  /**
71
94
  * Additional styles (platform-specific)
72
95
  */
73
- style?: any;
74
-
96
+ style?: StyleProp<ViewStyle>;
97
+
75
98
  /**
76
99
  * Test ID for testing
77
100
  */
@@ -0,0 +1,56 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { View, ScrollView } from 'react-native';
3
+ import { listStyles } from './List.styles';
4
+ import type { ListProps } from './types';
5
+ import { ListProvider } from './ListContext';
6
+
7
+ const List = forwardRef<View, ListProps>(({
8
+ children,
9
+ type = 'default',
10
+ size = 'md',
11
+ style,
12
+ testID,
13
+ scrollable = false,
14
+ maxHeight,
15
+ }, ref) => {
16
+ // Apply types
17
+ listStyles.useVariants({
18
+ size,
19
+ scrollable,
20
+ });
21
+
22
+ const containerStyle = [
23
+ listStyles.container,
24
+ maxHeight ? { maxHeight } : undefined,
25
+ style,
26
+ ];
27
+
28
+ const content = (
29
+ <ListProvider value={{ type, size }}>
30
+ {children}
31
+ </ListProvider>
32
+ );
33
+
34
+ if (scrollable) {
35
+ return (
36
+ <ScrollView
37
+ ref={ref as any}
38
+ style={containerStyle as any}
39
+ testID={testID}
40
+ showsVerticalScrollIndicator={true}
41
+ >
42
+ {content}
43
+ </ScrollView>
44
+ );
45
+ }
46
+
47
+ return (
48
+ <View ref={ref} style={containerStyle as any} testID={testID}>
49
+ {content}
50
+ </View>
51
+ );
52
+ });
53
+
54
+ List.displayName = 'List';
55
+
56
+ export default List;
@@ -0,0 +1,257 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, CompoundVariants } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import { ListSizeVariant, ListType } from './types';
5
+
6
+ type ListVariants = {
7
+ type: ListType;
8
+ size: ListSizeVariant;
9
+ scrollable: boolean;
10
+ active: boolean;
11
+ selected: boolean;
12
+ disabled: boolean;
13
+ clickable: boolean;
14
+ };
15
+
16
+ /**
17
+ * Create type variants for container
18
+ */
19
+ function createContainerTypeVariants(theme: Theme) {
20
+ return {
21
+ default: {
22
+ backgroundColor: 'transparent',
23
+ },
24
+ bordered: {
25
+ backgroundColor: theme.colors.surface.primary,
26
+ borderWidth: 1,
27
+ borderColor: theme.colors.border.primary,
28
+ borderRadius: 8,
29
+ _web: {
30
+ overflow: 'hidden',
31
+ border: `1px solid ${theme.colors.border.primary}`,
32
+ },
33
+ },
34
+ divided: {
35
+ backgroundColor: 'transparent',
36
+ },
37
+ };
38
+ }
39
+
40
+
41
+ /**
42
+ * Create compound variants for item
43
+ */
44
+ function createItemCompoundVariants(theme: Theme): CompoundVariants<keyof ListVariants> {
45
+ return [
46
+ {
47
+ type: 'divided',
48
+ styles: {
49
+ _web: {
50
+ ':last-child': {
51
+ borderBottom: 'none',
52
+ },
53
+ },
54
+ },
55
+ },
56
+ {
57
+ disabled: true,
58
+ styles: {
59
+ _web: {
60
+ _hover: {
61
+ backgroundColor: 'transparent',
62
+ borderRadius: 0,
63
+ },
64
+ },
65
+ },
66
+ },
67
+ {
68
+ clickable: false,
69
+ styles: {
70
+ _web: {
71
+ _hover: {
72
+ backgroundColor: 'transparent',
73
+ borderRadius: 0,
74
+ },
75
+ },
76
+ },
77
+ },
78
+ ];
79
+ }
80
+
81
+
82
+ export const listStyles = StyleSheet.create((theme: Theme) => {
83
+ return {
84
+ container: {
85
+ display: 'flex',
86
+ flexDirection: 'column',
87
+ width: '100%',
88
+ variants: {
89
+ type: createContainerTypeVariants(theme),
90
+ scrollable: {
91
+ true: {
92
+ _web: {
93
+ overflow: 'auto',
94
+ },
95
+ },
96
+ false: {},
97
+ },
98
+ },
99
+ },
100
+ item: {
101
+ display: 'flex',
102
+ flexDirection: 'row',
103
+ alignItems: 'center',
104
+ backgroundColor: 'transparent',
105
+ textAlign: 'left',
106
+ variants: {
107
+ size: buildSizeVariants(theme, 'list', (size) => ({
108
+ paddingVertical: size.paddingVertical,
109
+ paddingHorizontal: size.paddingHorizontal,
110
+ minHeight: size.minHeight,
111
+ })),
112
+ type: {
113
+ default: {},
114
+ bordered: {},
115
+ divided: {
116
+ borderBottomWidth: 1,
117
+ borderBottomStyle: 'solid',
118
+ borderBottomColor: theme.colors.border.primary,
119
+ _web: {
120
+ borderBottom: `1px solid ${theme.colors.border.primary}`,
121
+ },
122
+ },
123
+ },
124
+ active: {
125
+ true: {
126
+ backgroundColor: theme.colors.surface.secondary,
127
+ },
128
+ false: {},
129
+ },
130
+ selected: {
131
+ true: {
132
+ backgroundColor: theme.intents.primary.light + '20',
133
+ borderLeftWidth: 3,
134
+ borderLeftColor: theme.intents.primary.primary,
135
+ _web: {
136
+ borderLeft: `3px solid ${theme.intents.primary.primary}`,
137
+ },
138
+ },
139
+ false: {},
140
+ },
141
+ disabled: {
142
+ true: {
143
+ opacity: 0.5,
144
+ _web: {
145
+ cursor: 'not-allowed',
146
+ },
147
+ },
148
+ false: {},
149
+ },
150
+ clickable: {
151
+ true: {},
152
+ false: {
153
+ _web: {
154
+ cursor: 'default',
155
+ },
156
+ },
157
+ },
158
+ } as const,
159
+ compoundVariants: createItemCompoundVariants(theme),
160
+ _web: {
161
+ border: 'none',
162
+ cursor: 'pointer',
163
+ outline: 'none',
164
+ transition: 'background-color 0.2s ease, border-color 0.2s ease',
165
+ _hover: {
166
+ backgroundColor: theme.colors.surface.secondary,
167
+ borderRadius: 4,
168
+ },
169
+ },
170
+ } as const,
171
+ itemContent: {
172
+ display: 'flex',
173
+ flexDirection: 'row',
174
+ alignItems: 'center',
175
+ flex: 1,
176
+ gap: 8,
177
+ },
178
+ leading: {
179
+ display: 'flex',
180
+ alignItems: 'center',
181
+ justifyContent: 'center',
182
+ marginRight: 8,
183
+ color: theme.colors.text.secondary,
184
+ variants: {
185
+ size: buildSizeVariants(theme, 'list', (size) => ({
186
+ width: size.iconSize,
187
+ height: size.iconSize,
188
+ })),
189
+ } as const,
190
+ } as const,
191
+ labelContainer: {
192
+ flex: 1,
193
+ display: 'flex',
194
+ flexDirection: 'column',
195
+ },
196
+ label: {
197
+ fontWeight: '500',
198
+ color: theme.colors.text.primary,
199
+ variants: {
200
+ size: buildSizeVariants(theme, 'list', (size) => ({
201
+ fontSize: size.labelFontSize,
202
+ lineHeight: size.labelLineHeight,
203
+ })),
204
+ disabled: {
205
+ true: {
206
+ color: theme.colors.text.secondary,
207
+ },
208
+ false: {},
209
+ },
210
+ selected: {
211
+ true: {
212
+ color: theme.intents.primary.primary,
213
+ fontWeight: '600',
214
+ },
215
+ false: {},
216
+ },
217
+ },
218
+ },
219
+ trailing: {
220
+ display: 'flex',
221
+ alignItems: 'center',
222
+ justifyContent: 'center',
223
+ marginLeft: 8,
224
+ color: theme.colors.text.secondary,
225
+ flexShrink: 0,
226
+ },
227
+ trailingIcon: {
228
+ display: 'flex',
229
+ alignItems: 'center',
230
+ justifyContent: 'center',
231
+ variants: {
232
+ size: buildSizeVariants(theme, 'list', (size) => ({
233
+ width: size.iconSize,
234
+ height: size.iconSize,
235
+ })),
236
+ },
237
+ },
238
+ section: {
239
+ display: 'flex',
240
+ flexDirection: 'column',
241
+ },
242
+ sectionTitle: {
243
+ fontWeight: '600',
244
+ fontSize: 12,
245
+ lineHeight: 16,
246
+ textTransform: 'uppercase',
247
+ letterSpacing: 0.5,
248
+ color: theme.colors.text.secondary,
249
+ padding: 8,
250
+ paddingBottom: 4,
251
+ },
252
+ sectionContent: {
253
+ display: 'flex',
254
+ flexDirection: 'column',
255
+ },
256
+ };
257
+ });
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { listStyles } from './List.styles';
4
+ import type { ListProps } from './types';
5
+ import { ListProvider } from './ListContext';
6
+
7
+ const List: React.FC<ListProps> = ({
8
+ children,
9
+ type = 'default',
10
+ size = 'md',
11
+ style,
12
+ testID,
13
+ scrollable = false,
14
+ maxHeight,
15
+ }) => {
16
+ // Apply types
17
+ listStyles.useVariants({
18
+ size,
19
+ scrollable,
20
+ });
21
+
22
+ const containerStyle = [
23
+ listStyles.container,
24
+ maxHeight ? { maxHeight } : undefined,
25
+ style as any,
26
+ ];
27
+
28
+ const containerProps = getWebProps(containerStyle);
29
+
30
+ return (
31
+ <ListProvider value={{ type, size }}>
32
+ <div
33
+ {...containerProps}
34
+ role="list"
35
+ data-testid={testID}
36
+ >
37
+ {children}
38
+ </div>
39
+ </ListProvider>
40
+ );
41
+ };
42
+
43
+ export default List;
@@ -0,0 +1,16 @@
1
+ import { createContext, useContext } from 'react';
2
+ import type { ListSizeVariant, ListType } from './types';
3
+
4
+ interface ListContextValue {
5
+ type?: ListType;
6
+ size?: ListSizeVariant;
7
+ }
8
+
9
+ const ListContext = createContext<ListContextValue>({
10
+ type: 'default',
11
+ size: 'md',
12
+ });
13
+
14
+ export const ListProvider = ListContext.Provider;
15
+
16
+ export const useListContext = () => useContext(ListContext);
@@ -0,0 +1,111 @@
1
+ import React, { isValidElement, forwardRef, ComponentRef } from 'react';
2
+ import { View, TouchableOpacity, Text } from 'react-native';
3
+ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
4
+ import { listStyles } from './List.styles';
5
+ import type { ListItemProps } from './types';
6
+ import { useListContext } from './ListContext';
7
+
8
+ const ListItem = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof TouchableOpacity>, ListItemProps>(({
9
+ id,
10
+ label,
11
+ children,
12
+ leading,
13
+ trailing,
14
+ active = false,
15
+ selected = false,
16
+ disabled = false,
17
+ indent = 0,
18
+ size,
19
+ onPress,
20
+ style,
21
+ testID,
22
+ }, ref) => {
23
+ const listContext = useListContext();
24
+ const isClickable = !disabled && !!onPress;
25
+
26
+ // Use explicit size prop, fallback to context size, then default
27
+ const effectiveSize = size ?? listContext.size ?? 'md';
28
+ const effectiveVariant = listContext.type ?? 'default';
29
+
30
+ // Apply types
31
+ listStyles.useVariants({
32
+ size: effectiveSize,
33
+ active,
34
+ selected,
35
+ disabled,
36
+ clickable: isClickable,
37
+ });
38
+
39
+ // Helper to render leading/trailing icons
40
+ const renderElement = (element: typeof leading | typeof trailing, styleKey: 'leading' | 'trailingIcon') => {
41
+ if (!element) return null;
42
+
43
+ // If it's a string, treat it as an icon name
44
+ if (typeof element === 'string') {
45
+ const iconStyle = styleKey === 'leading' ? listStyles.leading : listStyles.trailingIcon;
46
+ return (
47
+ <MaterialCommunityIcons
48
+ name={element}
49
+ size={iconStyle.width}
50
+ color={iconStyle.color}
51
+ />
52
+ );
53
+ } else if (isValidElement(element)) {
54
+ // Custom React element
55
+ return element;
56
+ }
57
+
58
+ return null;
59
+ };
60
+
61
+ const content = (
62
+ <>
63
+ {leading && (
64
+ <View style={listStyles.leading}>
65
+ {renderElement(leading, 'leading')}
66
+ </View>
67
+ )}
68
+
69
+ <View style={listStyles.labelContainer}>
70
+ {label && (
71
+ <Text style={listStyles.label}>{label}</Text>
72
+ )}
73
+ {children}
74
+ </View>
75
+
76
+ {trailing && (
77
+ <View style={listStyles.trailing}>
78
+ {renderElement(trailing, 'trailingIcon')}
79
+ </View>
80
+ )}
81
+ </>
82
+ );
83
+
84
+ const indentStyle = indent > 0 ? { paddingLeft: indent * 16 } : {};
85
+ const combinedStyle = [listStyles.item, indentStyle, style];
86
+
87
+ if (isClickable) {
88
+ return (
89
+ <TouchableOpacity
90
+ ref={ref as any}
91
+ style={combinedStyle}
92
+ onPress={onPress}
93
+ disabled={disabled}
94
+ activeOpacity={0.7}
95
+ testID={testID}
96
+ >
97
+ {content}
98
+ </TouchableOpacity>
99
+ );
100
+ }
101
+
102
+ return (
103
+ <View ref={ref as any} style={combinedStyle} testID={testID}>
104
+ {content}
105
+ </View>
106
+ );
107
+ });
108
+
109
+ ListItem.displayName = 'ListItem';
110
+
111
+ export default ListItem;