@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
@@ -0,0 +1,156 @@
1
+ import React, { useState, useRef, useEffect, forwardRef } from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { textAreaStyles } from './TextArea.styles';
4
+ import type { TextAreaProps } from './types';
5
+ import useMergeRefs from '../hooks/useMergeRefs';
6
+
7
+ const TextArea = forwardRef<HTMLDivElement, TextAreaProps>(({
8
+ value: controlledValue,
9
+ defaultValue = '',
10
+ onChange,
11
+ placeholder,
12
+ disabled = false,
13
+ rows = 4,
14
+ minHeight,
15
+ maxHeight,
16
+ autoGrow = false,
17
+ maxLength,
18
+ label,
19
+ error,
20
+ helperText,
21
+ resize = 'none',
22
+ showCharacterCount = false,
23
+ intent = 'primary',
24
+ size = 'md',
25
+ style,
26
+ textareaStyle,
27
+ testID,
28
+ }, ref) => {
29
+ const [internalValue, setInternalValue] = useState(defaultValue);
30
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
31
+
32
+ const value = controlledValue !== undefined ? controlledValue : internalValue;
33
+ const hasError = Boolean(error);
34
+
35
+ // Apply variants
36
+ textAreaStyles.useVariants({
37
+ size,
38
+ intent,
39
+ disabled,
40
+ hasError,
41
+ resize,
42
+ isNearLimit: maxLength ? value.length >= maxLength * 0.9 : false,
43
+ isAtLimit: maxLength ? value.length >= maxLength : false,
44
+ });
45
+
46
+ const containerProps = getWebProps([textAreaStyles.container, style as any]);
47
+ const labelProps = getWebProps([textAreaStyles.label]);
48
+ const textareaContainerProps = getWebProps([textAreaStyles.textareaContainer]);
49
+ const footerProps = getWebProps([textAreaStyles.footer]);
50
+ const helperTextProps = getWebProps([textAreaStyles.helperText]);
51
+ const characterCountProps = getWebProps([textAreaStyles.characterCount]);
52
+
53
+ const adjustHeight = () => {
54
+ if (!autoGrow || !textareaRef.current) return;
55
+
56
+ const textarea = textareaRef.current;
57
+
58
+ // Reset height to allow shrinking
59
+ textarea.style.height = '1px';
60
+
61
+ let newHeight = textarea.scrollHeight;
62
+
63
+ if (minHeight && newHeight < minHeight) {
64
+ newHeight = minHeight;
65
+ }
66
+
67
+ if (maxHeight && newHeight > maxHeight) {
68
+ newHeight = maxHeight;
69
+ }
70
+
71
+ textarea.style.height = `${newHeight}px`;
72
+ };
73
+
74
+ useEffect(() => {
75
+ adjustHeight();
76
+ }, [value, autoGrow, minHeight, maxHeight]);
77
+
78
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
79
+ const newValue = e.target.value;
80
+
81
+ if (maxLength && newValue.length > maxLength) {
82
+ return;
83
+ }
84
+
85
+ if (controlledValue === undefined) {
86
+ setInternalValue(newValue);
87
+ }
88
+
89
+ onChange?.(newValue);
90
+ };
91
+
92
+ const showFooter = (error || helperText) || (showCharacterCount && maxLength);
93
+ const isNearLimit = maxLength ? value.length >= maxLength * 0.9 : false;
94
+ const isAtLimit = maxLength ? value.length >= maxLength : false;
95
+
96
+ const computedTextareaProps = getWebProps([
97
+ textAreaStyles.textarea({ intent, disabled, hasError }),
98
+ textareaStyle,
99
+ minHeight && { minHeight: `${minHeight}px` },
100
+ maxHeight && { maxHeight: `${maxHeight}px` },
101
+ autoGrow && maxHeight && textareaRef.current && textareaRef.current.scrollHeight > maxHeight && { overflowY: 'auto' as const },
102
+ ]);
103
+
104
+ const mergedRef = useMergeRefs(ref, containerProps.ref);
105
+ const mergedTextareaRef = useMergeRefs(textareaRef, computedTextareaProps.ref);
106
+
107
+ return (
108
+ <div {...containerProps} ref={mergedRef} data-testid={testID}>
109
+ {label && (
110
+ <label {...labelProps}>{label}</label>
111
+ )}
112
+
113
+ <div {...textareaContainerProps}>
114
+ <textarea
115
+ {...computedTextareaProps}
116
+ ref={mergedTextareaRef}
117
+ value={value}
118
+ onChange={handleChange}
119
+ placeholder={placeholder}
120
+ disabled={disabled}
121
+ rows={autoGrow ? undefined : rows}
122
+ maxLength={maxLength}
123
+ aria-invalid={hasError}
124
+ aria-describedby={error ? `${testID}-error` : helperText ? `${testID}-helper` : undefined}
125
+ />
126
+ </div>
127
+
128
+ {showFooter && (
129
+ <div {...footerProps}>
130
+ <div style={{ flex: 1 }}>
131
+ {error && (
132
+ <span {...helperTextProps} id={`${testID}-error`}>
133
+ {error}
134
+ </span>
135
+ )}
136
+ {!error && helperText && (
137
+ <span {...helperTextProps} id={`${testID}-helper`}>
138
+ {helperText}
139
+ </span>
140
+ )}
141
+ </div>
142
+
143
+ {showCharacterCount && maxLength && (
144
+ <span {...characterCountProps}>
145
+ {value.length}/{maxLength}
146
+ </span>
147
+ )}
148
+ </div>
149
+ )}
150
+ </div>
151
+ );
152
+ });
153
+
154
+ TextArea.displayName = 'TextArea';
155
+
156
+ export default TextArea;
@@ -0,0 +1,3 @@
1
+ export { default } from './TextArea.native';
2
+ export { default as TextArea } from './TextArea.native';
3
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ export { default } from './TextArea.web';
2
+ export { default as TextArea } from './TextArea.web';
3
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ export { default } from './TextArea.web';
2
+ export { default as TextArea } from './TextArea.web';
3
+ export * from './types';
@@ -0,0 +1,30 @@
1
+ import { Intent, Size } from '@idealyst/theme';
2
+ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
3
+
4
+ // Component-specific type aliases for future extensibility
5
+ export type TextAreaIntentVariant = Intent;
6
+ export type TextAreaSizeVariant = Size;
7
+ export type TextAreaResizeVariant = 'none' | 'vertical' | 'horizontal' | 'both';
8
+
9
+ export interface TextAreaProps {
10
+ value?: string;
11
+ defaultValue?: string;
12
+ onChange?: (value: string) => void;
13
+ placeholder?: string;
14
+ disabled?: boolean;
15
+ rows?: number;
16
+ minHeight?: number;
17
+ maxHeight?: number;
18
+ autoGrow?: boolean;
19
+ maxLength?: number;
20
+ label?: string;
21
+ error?: string;
22
+ helperText?: string;
23
+ resize?: TextAreaResizeVariant;
24
+ showCharacterCount?: boolean;
25
+ intent?: TextAreaIntentVariant;
26
+ size?: TextAreaSizeVariant;
27
+ style?: StyleProp<ViewStyle>;
28
+ textareaStyle?: StyleProp<TextStyle>;
29
+ testID?: string;
30
+ }
@@ -0,0 +1,165 @@
1
+ import React, { useState, useRef, useEffect, isValidElement, cloneElement, forwardRef } from 'react';
2
+ import { View, Modal, Text, Pressable } from 'react-native';
3
+ import { tooltipStyles } from './Tooltip.styles';
4
+ import type { TooltipProps } from './types';
5
+
6
+ const Tooltip = forwardRef<View, TooltipProps>(({
7
+ content,
8
+ children,
9
+ placement = 'top',
10
+ delay = 200,
11
+ intent = 'neutral',
12
+ size = 'md',
13
+ style,
14
+ testID,
15
+ }, ref) => {
16
+ const [visible, setVisible] = useState(false);
17
+ const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0, width: 0 });
18
+ const triggerRef = useRef<any>(null);
19
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
20
+
21
+ // Apply variants
22
+ tooltipStyles.useVariants({
23
+ size,
24
+ intent,
25
+ });
26
+
27
+ useEffect(() => {
28
+ return () => {
29
+ if (timeoutRef.current) {
30
+ clearTimeout(timeoutRef.current);
31
+ }
32
+ };
33
+ }, []);
34
+
35
+ const calculateTooltipPosition = (x: number, y: number, width: number, height: number) => {
36
+ const offset = 8;
37
+ let top = 0;
38
+ let left = 0;
39
+
40
+ switch (placement) {
41
+ case 'top':
42
+ top = y - offset;
43
+ left = x + width / 2;
44
+ break;
45
+ case 'bottom':
46
+ top = y + height + offset;
47
+ left = x + width / 2;
48
+ break;
49
+ case 'left':
50
+ top = y + height / 2;
51
+ left = x - offset;
52
+ break;
53
+ case 'right':
54
+ top = y + height / 2;
55
+ left = x + width + offset;
56
+ break;
57
+ }
58
+
59
+ setTooltipPosition({ top, left, width });
60
+ };
61
+
62
+ const handleLongPress = () => {
63
+ if (!triggerRef.current) return;
64
+
65
+ triggerRef.current.measureInWindow((x: number, y: number, width: number, height: number) => {
66
+ calculateTooltipPosition(x, y, width, height);
67
+ setVisible(true);
68
+ });
69
+ };
70
+
71
+ const handlePress = () => {
72
+ if (visible) {
73
+ setVisible(false);
74
+ }
75
+ };
76
+
77
+ // Clone child and inject long press handler
78
+ const trigger = isValidElement(children)
79
+ ? cloneElement(children as React.ReactElement<any>, {
80
+ onLongPress: () => {
81
+ const originalOnLongPress = (children as any).props?.onLongPress;
82
+ originalOnLongPress?.();
83
+ handleLongPress();
84
+ },
85
+ onPress: (e: any) => {
86
+ const originalOnPress = (children as any).props?.onPress;
87
+ originalOnPress?.(e);
88
+ handlePress();
89
+ },
90
+ })
91
+ : children;
92
+
93
+ const getPositionStyle = () => {
94
+ switch (placement) {
95
+ case 'top':
96
+ return {
97
+ bottom: tooltipPosition.top,
98
+ left: tooltipPosition.left,
99
+ transform: [{ translateX: -50 }],
100
+ };
101
+ case 'bottom':
102
+ return {
103
+ top: tooltipPosition.top,
104
+ left: tooltipPosition.left,
105
+ transform: [{ translateX: -50 }],
106
+ };
107
+ case 'left':
108
+ return {
109
+ top: tooltipPosition.top,
110
+ right: tooltipPosition.left,
111
+ transform: [{ translateY: -50 }],
112
+ };
113
+ case 'right':
114
+ return {
115
+ top: tooltipPosition.top,
116
+ left: tooltipPosition.left,
117
+ transform: [{ translateY: -50 }],
118
+ };
119
+ default:
120
+ return {
121
+ top: tooltipPosition.top,
122
+ left: tooltipPosition.left,
123
+ };
124
+ }
125
+ };
126
+
127
+ return (
128
+ <>
129
+ <View ref={ref} collapsable={false} style={style}>
130
+ {trigger}
131
+ </View>
132
+
133
+ {visible && (
134
+ <Modal
135
+ visible={visible}
136
+ transparent
137
+ animationType="fade"
138
+ onRequestClose={() => setVisible(false)}
139
+ testID={testID}
140
+ >
141
+ <Pressable style={{ flex: 1 }} onPress={() => setVisible(false)}>
142
+ <View
143
+ style={[
144
+ tooltipStyles.tooltip,
145
+ { position: 'absolute' },
146
+ getPositionStyle(),
147
+ ]}
148
+ pointerEvents="none"
149
+ >
150
+ {typeof content === 'string' ? (
151
+ <Text style={tooltipStyles.tooltip}>{content}</Text>
152
+ ) : (
153
+ content
154
+ )}
155
+ </View>
156
+ </Pressable>
157
+ </Modal>
158
+ )}
159
+ </>
160
+ );
161
+ });
162
+
163
+ Tooltip.displayName = 'Tooltip';
164
+
165
+ export default Tooltip;
@@ -0,0 +1,73 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, Intent, Size} from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+
5
+ type TooltipSize = Size;
6
+ type TooltipIntent = Intent;
7
+
8
+ type TooltipTooltipVariants = {
9
+ size: TooltipSize;
10
+ intent: TooltipIntent;
11
+ }
12
+
13
+ export type ExpandedTooltipTooltipStyles = StylesheetStyles<keyof TooltipTooltipVariants>;
14
+ export type ExpandedTooltipStyles = StylesheetStyles<never>;
15
+
16
+ export type TooltipStylesheet = {
17
+ container: ExpandedTooltipStyles;
18
+ tooltip: ExpandedTooltipTooltipStyles;
19
+ }
20
+
21
+ function createTooltipSizeVariants(theme: Theme) {
22
+ return buildSizeVariants(theme, 'tooltip', (size) => ({
23
+ fontSize: size.fontSize,
24
+ padding: size.padding,
25
+ }));
26
+ }
27
+
28
+ function createTooltipIntentVariants(theme: Theme) {
29
+ const intents: Record<string, any> = {};
30
+
31
+ for (const [intentName, intentValue] of Object.entries(theme.intents)) {
32
+ intents[intentName] = {
33
+ backgroundColor: intentValue.primary,
34
+ color: intentValue.contrast,
35
+ };
36
+ }
37
+
38
+ return intents;
39
+ }
40
+
41
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
42
+ // transform on native cannot resolve function calls to extract variant structures.
43
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
44
+ export const tooltipStyles = StyleSheet.create((theme: Theme) => {
45
+ return {
46
+ container: {
47
+ position: 'relative',
48
+ _web: {
49
+ display: 'inline-flex',
50
+ width: 'fit-content',
51
+ },
52
+ } as const,
53
+ tooltip: {
54
+ borderRadius: 8,
55
+ maxWidth: 300,
56
+ shadowColor: '#000',
57
+ shadowOffset: { width: 0, height: 2 },
58
+ shadowOpacity: 0.15,
59
+ shadowRadius: 8,
60
+ elevation: 4,
61
+ variants: {
62
+ size: createTooltipSizeVariants(theme),
63
+ intent: createTooltipIntentVariants(theme),
64
+ },
65
+ _web: {
66
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
67
+ pointerEvents: 'none',
68
+ width: 'max-content',
69
+ wordWrap: 'break-word',
70
+ },
71
+ } as const,
72
+ } as const;
73
+ });
@@ -0,0 +1,87 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { tooltipStyles } from './Tooltip.styles';
4
+ import type { TooltipProps } from './types';
5
+ import { PositionedPortal } from '../internal/PositionedPortal';
6
+
7
+ const Tooltip: React.FC<TooltipProps> = ({
8
+ content,
9
+ children,
10
+ placement = 'top',
11
+ delay = 200,
12
+ intent = 'neutral',
13
+ size = 'md',
14
+ style,
15
+ testID,
16
+ }) => {
17
+ const [visible, setVisible] = useState(false);
18
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
19
+ const anchorRef = useRef<HTMLDivElement>(null);
20
+
21
+ // Apply variants - PositionedPortal handles positioning and visibility
22
+ tooltipStyles.useVariants({
23
+ size,
24
+ intent,
25
+ });
26
+
27
+ const containerProps = getWebProps([tooltipStyles.container, style as any]);
28
+ const tooltipContentProps = getWebProps(tooltipStyles.tooltip);
29
+
30
+ const handleMouseEnter = () => {
31
+ if (timeoutRef.current) {
32
+ clearTimeout(timeoutRef.current);
33
+ }
34
+
35
+ timeoutRef.current = setTimeout(() => {
36
+ setVisible(true);
37
+ }, delay);
38
+ };
39
+
40
+ const handleMouseLeave = () => {
41
+ if (timeoutRef.current) {
42
+ clearTimeout(timeoutRef.current);
43
+ }
44
+
45
+ setVisible(false);
46
+ };
47
+
48
+ useEffect(() => {
49
+ return () => {
50
+ if (timeoutRef.current) {
51
+ clearTimeout(timeoutRef.current);
52
+ }
53
+ };
54
+ }, []);
55
+
56
+ return (
57
+ <>
58
+ <div
59
+ ref={anchorRef}
60
+ {...containerProps}
61
+ onMouseEnter={handleMouseEnter}
62
+ onMouseLeave={handleMouseLeave}
63
+ data-testid={testID}
64
+ >
65
+ {children}
66
+ </div>
67
+
68
+ <PositionedPortal
69
+ open={visible}
70
+ anchor={anchorRef}
71
+ placement={placement}
72
+ offset={8}
73
+ zIndex={1000}
74
+ >
75
+ <div
76
+ {...tooltipContentProps}
77
+ role="tooltip"
78
+ data-testid={`${testID}-tooltip`}
79
+ >
80
+ {content}
81
+ </div>
82
+ </PositionedPortal>
83
+ </>
84
+ );
85
+ };
86
+
87
+ export default Tooltip;
@@ -0,0 +1,3 @@
1
+ export { default } from './Tooltip.native';
2
+ export { default as Tooltip } from './Tooltip.native';
3
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ export { default } from './Tooltip.web';
2
+ export { default as Tooltip } from './Tooltip.web';
3
+ export * from './types';
@@ -0,0 +1,18 @@
1
+ import { Intent, Size } from '@idealyst/theme';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ // Component-specific type aliases for future extensibility
5
+ export type TooltipIntentVariant = Intent;
6
+ export type TooltipSizeVariant = Size;
7
+ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
8
+
9
+ export interface TooltipProps {
10
+ content: string | React.ReactNode;
11
+ children: React.ReactNode;
12
+ placement?: TooltipPlacement;
13
+ delay?: number;
14
+ intent?: TooltipIntentVariant;
15
+ size?: TooltipSizeVariant;
16
+ style?: StyleProp<ViewStyle>;
17
+ testID?: string;
18
+ }
@@ -0,0 +1,105 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { videoStyles } from './Video.styles';
4
+ import type { VideoProps, VideoSource } from './types';
5
+
6
+ // Import react-native-video - it's a peer dependency
7
+ let RNVideo: any;
8
+ try {
9
+ const videoModule = require('react-native-video');
10
+ // Try default export first (v5 and earlier), then named export (v6+)
11
+ RNVideo = videoModule.default || videoModule.Video || videoModule;
12
+ } catch (e) {
13
+ console.warn('react-native-video not installed. Video component will not work on native.');
14
+ }
15
+
16
+ const Video = React.forwardRef<View, VideoProps>(({
17
+ source,
18
+ poster,
19
+ width,
20
+ height,
21
+ aspectRatio,
22
+ controls = true,
23
+ autoPlay = false,
24
+ loop = false,
25
+ muted = false,
26
+ onLoad,
27
+ onError,
28
+ onPlay,
29
+ onPause,
30
+ onEnd,
31
+ onProgress,
32
+ borderRadius,
33
+ style,
34
+ testID,
35
+ }, ref) => {
36
+
37
+ if (!RNVideo) {
38
+ return (
39
+ <View ref={ref} style={[videoStyles.container, { aspectRatio, borderRadius }, style]} testID={testID}>
40
+ <View style={videoStyles.fallback}>
41
+ {/* Fallback when react-native-video is not installed */}
42
+ </View>
43
+ </View>
44
+ );
45
+ }
46
+
47
+ const videoSource = typeof source === 'string'
48
+ ? { uri: source }
49
+ : source;
50
+
51
+ const containerStyle = [
52
+ videoStyles.container,
53
+ {
54
+ aspectRatio: aspectRatio || undefined,
55
+ borderRadius: borderRadius || undefined,
56
+ },
57
+ style,
58
+ ];
59
+
60
+ const handleLoad = (data: any) => {
61
+ onLoad?.();
62
+ };
63
+
64
+ const handleError = (error: any) => {
65
+ onError?.(error);
66
+ };
67
+
68
+ const handleProgress = (data: any) => {
69
+ if (onProgress) {
70
+ onProgress({
71
+ currentTime: data.currentTime,
72
+ playableDuration: data.playableDuration,
73
+ });
74
+ }
75
+ };
76
+
77
+ const handleEnd = () => {
78
+ onEnd?.();
79
+ };
80
+
81
+ return (
82
+ <View ref={ref} style={containerStyle} testID={testID}>
83
+ <RNVideo
84
+ source={videoSource}
85
+ poster={poster}
86
+ style={[videoStyles.video, { borderRadius }]}
87
+ controls={controls}
88
+ paused={!autoPlay}
89
+ repeat={loop}
90
+ muted={muted}
91
+ resizeMode="contain"
92
+ // Support both v5 and v6+ event names
93
+ onLoad={handleLoad}
94
+ onReadyForDisplay={handleLoad}
95
+ onError={handleError}
96
+ onProgress={handleProgress}
97
+ onEnd={handleEnd}
98
+ />
99
+ </View>
100
+ );
101
+ });
102
+
103
+ Video.displayName = 'Video';
104
+
105
+ export default Video;
@@ -0,0 +1,39 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles} from '@idealyst/theme';
3
+
4
+ export type ExpandedVideoStyles = StylesheetStyles<never>;
5
+
6
+ export type VideoStylesheet = {
7
+ container: ExpandedVideoStyles;
8
+ video: ExpandedVideoStyles;
9
+ fallback: ExpandedVideoStyles;
10
+ }
11
+
12
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
13
+ // transform on native cannot resolve function calls to extract variant structures.
14
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
15
+ export const videoStyles = StyleSheet.create((theme: Theme) => {
16
+ return {
17
+ container: {
18
+ position: 'relative',
19
+ overflow: 'hidden',
20
+ backgroundColor: theme.colors['black'],
21
+ },
22
+ video: {
23
+ width: '100%',
24
+ height: '100%',
25
+ },
26
+ fallback: {
27
+ position: 'absolute',
28
+ top: 0,
29
+ left: 0,
30
+ right: 0,
31
+ bottom: 0,
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ backgroundColor: theme.colors['gray.300'],
36
+ color: theme.colors['gray.600'],
37
+ },
38
+ };
39
+ });