@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,16 @@
1
+ import { AllComponentSizes, Size, Theme, Styles } from '@idealyst/theme';
2
+
3
+ /**
4
+ * Builds a generic size variant. Not really useful on its own tbh, just good to show how it can be used.
5
+ * Context really matters for sizes
6
+ * @param theme
7
+ * @param builder
8
+ * @returns
9
+ */
10
+ export function buildSizeVariants<T extends keyof AllComponentSizes>(theme: Theme, component: T, builder: (value: AllComponentSizes[T][Size]) => Styles): Record<Size, Styles> {
11
+ const variants = {} as Record<Size, Styles>;
12
+ for (const size in theme.sizes[component]) {
13
+ variants[size as Size] = builder(theme.sizes[component][size as Size]);
14
+ }
15
+ return variants;
16
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Check if a value is a plain object (not an array, null, or other special object)
3
+ */
4
+ function isPlainObject(value: unknown): value is Record<string, any> {
5
+ return (
6
+ value !== null &&
7
+ typeof value === 'object' &&
8
+ !Array.isArray(value) &&
9
+ Object.prototype.toString.call(value) === '[object Object]'
10
+ )
11
+ }
12
+
13
+ /**
14
+ * Deep merge two objects together, with the second object taking priority.
15
+ * Arrays and non-plain objects are replaced rather than merged.
16
+ *
17
+ * @param target - The base object
18
+ * @param source - The object to merge in (takes priority)
19
+ * @returns A new merged object
20
+ */
21
+ export function deepMerge<T extends Record<string, any>, S extends Record<string, any>>(
22
+ target: T,
23
+ source: S
24
+ ): T & S {
25
+ const result: Record<string, any> = { ...target }
26
+
27
+ for (const key in source) {
28
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
29
+ const sourceValue = source[key]
30
+ const targetValue = result[key]
31
+
32
+ // If both values are plain objects, merge them recursively
33
+ if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {
34
+ result[key] = deepMerge(targetValue, sourceValue)
35
+ } else {
36
+ // Otherwise, source value takes priority (including arrays, primitives, null, undefined)
37
+ result[key] = sourceValue
38
+ }
39
+ }
40
+ }
41
+
42
+ return result as T & S
43
+ }
@@ -0,0 +1,280 @@
1
+ import { Dimensions } from 'react-native';
2
+
3
+ /**
4
+ * Calculate the maximum available height for content at a given position
5
+ * This matches the logic in BoundedModalContent to ensure positioning is accurate
6
+ */
7
+ export const calculateAvailableHeight = (
8
+ top: number,
9
+ safeAreaInsets?: SafeAreaInsets
10
+ ): number => {
11
+ const { height: windowHeight } = Dimensions.get('window');
12
+ const padding = 12;
13
+ const bottomSafeEdge = windowHeight - (safeAreaInsets?.bottom || 0);
14
+ const bottomBound = bottomSafeEdge - padding;
15
+ return Math.max(100, bottomBound - top);
16
+ };
17
+
18
+ export type Placement =
19
+ | 'top' | 'top-start' | 'top-end'
20
+ | 'bottom' | 'bottom-start' | 'bottom-end'
21
+ | 'left' | 'left-start' | 'left-end'
22
+ | 'right' | 'right-start' | 'right-end';
23
+
24
+ export interface Position {
25
+ top: number;
26
+ left: number;
27
+ width?: number;
28
+ }
29
+
30
+ export interface AnchorMeasurements {
31
+ x: number;
32
+ y: number;
33
+ width: number;
34
+ height: number;
35
+ }
36
+
37
+ export interface ContentSize {
38
+ width: number;
39
+ height: number;
40
+ }
41
+
42
+ export interface SafeAreaInsets {
43
+ top: number;
44
+ right: number;
45
+ bottom: number;
46
+ left: number;
47
+ }
48
+
49
+ /**
50
+ * Get the opposite placement (for flipping)
51
+ */
52
+ const getOppositePlacement = (placement: Placement): Placement => {
53
+ const opposites: Record<Placement, Placement> = {
54
+ 'top': 'bottom',
55
+ 'top-start': 'bottom-start',
56
+ 'top-end': 'bottom-end',
57
+ 'bottom': 'top',
58
+ 'bottom-start': 'top-start',
59
+ 'bottom-end': 'top-end',
60
+ 'left': 'right',
61
+ 'left-start': 'right-start',
62
+ 'left-end': 'right-end',
63
+ 'right': 'left',
64
+ 'right-start': 'left-start',
65
+ 'right-end': 'left-end',
66
+ };
67
+ return opposites[placement];
68
+ };
69
+
70
+ /**
71
+ * Calculate position for a given placement without boundary checks
72
+ */
73
+ const calculatePositionForPlacement = (
74
+ anchor: AnchorMeasurements,
75
+ contentSize: ContentSize,
76
+ placement: Placement,
77
+ offset: number
78
+ ): Position => {
79
+ let top = 0;
80
+ let left = 0;
81
+
82
+ switch (placement) {
83
+ case 'top':
84
+ top = anchor.y - contentSize.height - offset;
85
+ left = anchor.x + anchor.width / 2 - contentSize.width / 2;
86
+ break;
87
+ case 'top-start':
88
+ top = anchor.y - contentSize.height - offset;
89
+ left = anchor.x;
90
+ break;
91
+ case 'top-end':
92
+ top = anchor.y - contentSize.height - offset;
93
+ left = anchor.x + anchor.width - contentSize.width;
94
+ break;
95
+ case 'bottom':
96
+ top = anchor.y + anchor.height + offset;
97
+ left = anchor.x + anchor.width / 2 - contentSize.width / 2;
98
+ break;
99
+ case 'bottom-start':
100
+ top = anchor.y + anchor.height + offset;
101
+ left = anchor.x;
102
+ break;
103
+ case 'bottom-end':
104
+ top = anchor.y + anchor.height + offset;
105
+ left = anchor.x + anchor.width - contentSize.width;
106
+ break;
107
+ case 'left':
108
+ top = anchor.y + anchor.height / 2 - contentSize.height / 2;
109
+ left = anchor.x - contentSize.width - offset;
110
+ break;
111
+ case 'left-start':
112
+ top = anchor.y;
113
+ left = anchor.x - contentSize.width - offset;
114
+ break;
115
+ case 'left-end':
116
+ top = anchor.y + anchor.height - contentSize.height;
117
+ left = anchor.x - contentSize.width - offset;
118
+ break;
119
+ case 'right':
120
+ top = anchor.y + anchor.height / 2 - contentSize.height / 2;
121
+ left = anchor.x + anchor.width + offset;
122
+ break;
123
+ case 'right-start':
124
+ top = anchor.y;
125
+ left = anchor.x + anchor.width + offset;
126
+ break;
127
+ case 'right-end':
128
+ top = anchor.y + anchor.height - contentSize.height;
129
+ left = anchor.x + anchor.width + offset;
130
+ break;
131
+ }
132
+
133
+ return { top, left };
134
+ };
135
+
136
+ /**
137
+ * Check if position fits within window bounds accounting for safe areas
138
+ * Position is in window coordinates (from measureInWindow)
139
+ */
140
+ const fitsInViewport = (
141
+ position: Position,
142
+ contentSize: ContentSize,
143
+ windowSize: { width: number; height: number },
144
+ padding: number = 12,
145
+ safeAreaInsets?: SafeAreaInsets
146
+ ): boolean => {
147
+ const right = position.left + contentSize.width;
148
+ const bottom = position.top + contentSize.height;
149
+
150
+ // Calculate bounds in window coordinates
151
+ // Respect safe areas to avoid overlapping with system UI
152
+ const topBound = padding + (safeAreaInsets?.top || 0);
153
+ const leftBound = padding + (safeAreaInsets?.left || 0);
154
+ const rightBound = windowSize.width - padding - (safeAreaInsets?.right || 0);
155
+ const bottomBound = windowSize.height - topBound - (safeAreaInsets?.bottom || 0) - padding;
156
+
157
+ // Add a buffer to account for floating point precision and give some breathing room
158
+ const buffer = 2;
159
+
160
+ const fits = (
161
+ position.left >= leftBound &&
162
+ position.top >= topBound &&
163
+ right <= rightBound - buffer &&
164
+ bottom <= bottomBound - buffer
165
+ );
166
+
167
+ console.log(bottom, bottomBound);
168
+
169
+ if (__DEV__) {
170
+ console.log('[fitsInViewport]', {
171
+ position,
172
+ contentSize,
173
+ windowSize,
174
+ safeAreaInsets,
175
+ bounds: { topBound, leftBound, rightBound, bottomBound },
176
+ edges: { right, bottom },
177
+ fits,
178
+ bottomOverflow: bottom > bottomBound ? bottom - bottomBound : 0
179
+ });
180
+ }
181
+
182
+ return fits;
183
+ };
184
+
185
+ /**
186
+ * Calculate the best position with smart boundary detection and flipping
187
+ */
188
+ export const calculateSmartPosition = (
189
+ anchor: AnchorMeasurements,
190
+ contentSize: ContentSize,
191
+ placement: Placement,
192
+ offset: number = 8,
193
+ matchWidth: boolean = false,
194
+ safeAreaInsets?: SafeAreaInsets
195
+ ): Position => {
196
+ // Use window dimensions - this is the actual visible content area
197
+ const { width: windowWidth, height: windowHeight } = Dimensions.get('window');
198
+ const padding = 12;
199
+
200
+ // Calculate actual usable space accounting for safe areas
201
+ const topBound = padding;
202
+ const rightBound = windowWidth - padding - (safeAreaInsets?.right || 0);
203
+ const bottomBound = windowHeight - padding - (safeAreaInsets?.bottom || 0);
204
+ const leftBound = padding + (safeAreaInsets?.left || 0);
205
+
206
+ // Try original placement
207
+ let position = calculatePositionForPlacement(anchor, contentSize, placement, offset);
208
+
209
+ // Check if it fits using window dimensions
210
+ const windowSize = { width: windowWidth, height: windowHeight };
211
+ const originalFits = fitsInViewport(position, contentSize, windowSize, padding, safeAreaInsets);
212
+
213
+ if (__DEV__) {
214
+ console.log('[calculateSmartPosition] Original placement:', placement, 'fits:', originalFits, 'position:', position);
215
+ }
216
+
217
+ if (originalFits) {
218
+ if (matchWidth) {
219
+ position.width = anchor.width;
220
+ }
221
+ return position;
222
+ }
223
+
224
+ // Try flipping to opposite side
225
+ const oppositePlacement = getOppositePlacement(placement);
226
+ let flippedPosition = calculatePositionForPlacement(anchor, contentSize, oppositePlacement, offset);
227
+ const flippedFits = fitsInViewport(flippedPosition, contentSize, windowSize, padding, safeAreaInsets);
228
+
229
+ if (__DEV__) {
230
+ console.log('[calculateSmartPosition] Flipped placement:', oppositePlacement, 'fits:', flippedFits, 'position:', flippedPosition);
231
+ }
232
+
233
+ if (flippedFits) {
234
+ if (matchWidth) {
235
+ flippedPosition.width = anchor.width;
236
+ }
237
+ return flippedPosition;
238
+ }
239
+
240
+ // Try alternative alignments for the original side
241
+ const alternativePlacements: Placement[] = [];
242
+ const basePlacement = placement.split('-')[0] as 'top' | 'bottom' | 'left' | 'right';
243
+
244
+ if (placement.includes('-')) {
245
+ // If we have an alignment, try other alignments on the same side
246
+ alternativePlacements.push(`${basePlacement}` as Placement);
247
+ if (!placement.endsWith('-start')) alternativePlacements.push(`${basePlacement}-start` as Placement);
248
+ if (!placement.endsWith('-end')) alternativePlacements.push(`${basePlacement}-end` as Placement);
249
+ }
250
+
251
+ // Try alternative alignments
252
+ for (const altPlacement of alternativePlacements) {
253
+ const altPosition = calculatePositionForPlacement(anchor, contentSize, altPlacement, offset);
254
+ if (fitsInViewport(altPosition, contentSize, windowSize, padding, safeAreaInsets)) {
255
+ if (matchWidth) {
256
+ altPosition.width = anchor.width;
257
+ }
258
+ return altPosition;
259
+ }
260
+ }
261
+
262
+ // If nothing fits perfectly, constrain to viewport bounds as fallback
263
+ // This handles when content is too large to fit anywhere
264
+ if (__DEV__) {
265
+ console.log('[calculateSmartPosition] Nothing fits, constraining to bounds. Original position:', position, 'bounds:', { topBound, bottomBound, leftBound, rightBound });
266
+ }
267
+
268
+ position.left = Math.max(leftBound, Math.min(position.left, rightBound - contentSize.width));
269
+ position.top = Math.max(topBound, Math.min(position.top, bottomBound - contentSize.height));
270
+
271
+ if (__DEV__) {
272
+ console.log('[calculateSmartPosition] Constrained position:', position);
273
+ }
274
+
275
+ if (matchWidth) {
276
+ position.width = anchor.width;
277
+ }
278
+
279
+ return position;
280
+ };
@@ -0,0 +1,48 @@
1
+ import { Theme } from '@idealyst/theme';
2
+
3
+ // Type definitions
4
+ export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
5
+
6
+ // Helper functions
7
+ export function isPlainObject(value: unknown): value is Record<string, any> {
8
+ return (
9
+ value !== null &&
10
+ typeof value === 'object' &&
11
+ !Array.isArray(value) &&
12
+ Object.prototype.toString.call(value) === '[object Object]'
13
+ )
14
+ }
15
+
16
+ export function deepMerge<T extends Record<string, any>, S extends Record<string, any>>(
17
+ target: T,
18
+ source: S
19
+ ): T & S {
20
+ const result: Record<string, any> = { ...target }
21
+
22
+ for (const key in source) {
23
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
24
+ const sourceValue = source[key]
25
+ const targetValue = result[key]
26
+
27
+ if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {
28
+ result[key] = deepMerge(targetValue, sourceValue)
29
+ } else {
30
+ result[key] = sourceValue
31
+ }
32
+ }
33
+ }
34
+
35
+ return result as T & S
36
+ }
37
+
38
+ export function buildSizeVariants<T>(
39
+ theme: Theme,
40
+ component: string,
41
+ builder: (value: any) => any
42
+ ): Record<Size, any> {
43
+ const variants = {} as Record<Size, any>;
44
+ for (const size in (theme.sizes as any)[component]) {
45
+ variants[size as Size] = builder((theme.sizes as any)[component][size as Size]);
46
+ }
47
+ return variants;
48
+ }
@@ -1,143 +0,0 @@
1
- # LLM Documentation Access Guide
2
-
3
- This guide explains exactly how LLMs can access @idealyst/components documentation in real-world scenarios.
4
-
5
- ## Scenario 1: Working in a Project with the Package Installed
6
-
7
- When helping a developer who has `@idealyst/components` installed in their project:
8
-
9
- ### Quick Overview
10
- ```bash
11
- # Read the main documentation file
12
- cat node_modules/@idealyst/components/CLAUDE.md
13
- ```
14
- This gives you everything in one file - all components, patterns, and usage examples.
15
-
16
- ### Specific Component Details
17
- ```bash
18
- # For detailed Button documentation
19
- cat node_modules/@idealyst/components/src/Button/README.md
20
-
21
- # For detailed Card documentation
22
- cat node_modules/@idealyst/components/src/Card/README.md
23
-
24
- # And so on for each component...
25
- ```
26
-
27
- ### Component Discovery
28
- ```bash
29
- # See all available components
30
- ls node_modules/@idealyst/components/src/*/README.md
31
-
32
- # Will show:
33
- # node_modules/@idealyst/components/src/Avatar/README.md
34
- # node_modules/@idealyst/components/src/Badge/README.md
35
- # node_modules/@idealyst/components/src/Button/README.md
36
- # ... etc
37
- ```
38
-
39
- ## Scenario 2: Repository/GitHub Access
40
-
41
- When working with the source repository or GitHub:
42
-
43
- ### Main Documentation
44
- - `packages/components/README.md` - Complete overview with component table
45
- - `packages/components/CLAUDE.md` - LLM-optimized quick reference
46
-
47
- ### Individual Component Docs
48
- - `packages/components/src/Avatar/README.md`
49
- - `packages/components/src/Button/README.md`
50
- - `packages/components/src/Card/README.md`
51
- - ... etc for all 11 components
52
-
53
- ## Scenario 3: Package Manager Info
54
-
55
- ```bash
56
- # View package information
57
- npm info @idealyst/components
58
-
59
- # View package README
60
- npm docs @idealyst/components
61
-
62
- # Download and examine (if needed)
63
- npm pack @idealyst/components
64
- tar -tf idealyst-components-*.tgz | grep README
65
- ```
66
-
67
- ## Scenario 4: Examples and Live Code
68
-
69
- For working examples and demonstrations:
70
-
71
- ```bash
72
- # Import component examples
73
- import { ButtonExamples, CardExamples } from '@idealyst/components/examples';
74
-
75
- # Or read example source code
76
- cat node_modules/@idealyst/components/src/examples/ButtonExamples.tsx
77
- cat node_modules/@idealyst/components/src/examples/CardExamples.tsx
78
- ```
79
-
80
- ## Recommended LLM Workflow
81
-
82
- ### Step 1: Quick Reference
83
- Start with `CLAUDE.md` for:
84
- - All component names and categories
85
- - Common usage patterns
86
- - Intent system explanation
87
- - Quick examples
88
-
89
- ### Step 2: Specific Component Help
90
- When user asks about a specific component:
91
- 1. Read `src/ComponentName/README.md` for complete details
92
- 2. Use the detailed props table, examples, and best practices
93
-
94
- ### Step 3: Component Discovery
95
- When user asks "what components are available for X":
96
- 1. Check the component table in main README.md
97
- 2. Use component categories (layout, form, display, etc.)
98
- 3. Read individual component descriptions
99
-
100
- ## File Locations Summary
101
-
102
- ```
103
- @idealyst/components/
104
- ├── README.md # Complete overview + component table
105
- ├── CLAUDE.md # LLM quick reference (START HERE)
106
- ├── src/
107
- │ ├── Avatar/README.md # Detailed Avatar docs
108
- │ ├── Badge/README.md # Detailed Badge docs
109
- │ ├── Button/README.md # Detailed Button docs
110
- │ ├── Card/README.md # Detailed Card docs
111
- │ ├── Checkbox/README.md # Detailed Checkbox docs
112
- │ ├── Divider/README.md # Detailed Divider docs
113
- │ ├── Icon/README.md # Detailed Icon docs
114
- │ ├── Input/README.md # Detailed Input docs
115
- │ ├── Screen/README.md # Detailed Screen docs
116
- │ ├── Text/README.md # Detailed Text docs
117
- │ └── View/README.md # Detailed View docs
118
- ```
119
-
120
- ## Pro Tips for LLMs
121
-
122
- 1. **Always start with `CLAUDE.md`** - it has everything you need for 90% of questions
123
- 2. **Component table in main README** - perfect for "what components are available" questions
124
- 3. **Individual README files** - use when user needs specific component details
125
- 4. **Check the examples/** folder - contains working code examples for all components
126
- 5. **Intent system is key** - primary, neutral, success, error, warning are used across all components
127
-
128
- ## Quick Command Reference
129
-
130
- ```bash
131
- # Essential reads for any LLM session
132
- cat CLAUDE.md # Complete LLM reference
133
- cat README.md # Overview + component table
134
-
135
- # Specific component help
136
- cat src/Button/README.md # Button documentation
137
- cat src/Card/README.md # Card documentation
138
-
139
- # Discovery
140
- ls src/*/README.md # List all component docs
141
- ```
142
-
143
- This approach ensures LLMs have clear, practical access to all documentation without cluttering the main component API.
@@ -1,132 +0,0 @@
1
- # ActivityIndicator
2
-
3
- A cross-platform loading indicator component that displays a spinning animation to indicate loading or processing state.
4
-
5
- ## Features
6
-
7
- - **Cross-platform**: Works seamlessly on both React and React Native
8
- - **Intent-based colors**: Uses semantic color system (primary, neutral, success, error, warning)
9
- - **Multiple sizes**: Supports small, medium, large, or custom numeric sizes
10
- - **Customizable**: Override colors and styles as needed
11
- - **Animation control**: Start/stop animation with `animating` prop
12
- - **Auto-hide**: Optionally hide when not animating
13
-
14
- ## Usage
15
-
16
- ```tsx
17
- import { ActivityIndicator } from '@idealyst/components';
18
-
19
- // Basic usage
20
- <ActivityIndicator />
21
-
22
- // With different sizes
23
- <ActivityIndicator size="small" />
24
- <ActivityIndicator size="medium" />
25
- <ActivityIndicator size="large" />
26
- <ActivityIndicator size={64} /> // Custom size in pixels
27
-
28
- // With different intents
29
- <ActivityIndicator intent="primary" />
30
- <ActivityIndicator intent="success" />
31
- <ActivityIndicator intent="error" />
32
- <ActivityIndicator intent="warning" />
33
- <ActivityIndicator intent="neutral" />
34
-
35
- // Custom color
36
- <ActivityIndicator color="#FF5733" />
37
-
38
- // Control animation
39
- <ActivityIndicator animating={isLoading} />
40
-
41
- // Don't hide when stopped
42
- <ActivityIndicator animating={false} hidesWhenStopped={false} />
43
- ```
44
-
45
- ## Props
46
-
47
- | Prop | Type | Default | Description |
48
- |------|------|---------|-------------|
49
- | `animating` | `boolean` | `true` | Whether the indicator is animating (visible) |
50
- | `size` | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | The size of the indicator |
51
- | `intent` | `'primary' \| 'neutral' \| 'success' \| 'error' \| 'warning'` | `'primary'` | The color intent of the indicator |
52
- | `color` | `string` | - | Custom color to override intent |
53
- | `style` | `ViewStyle` | - | Additional styles to apply to the container |
54
- | `testID` | `string` | - | Test identifier for testing |
55
- | `hidesWhenStopped` | `boolean` | `true` | Whether to hide the indicator when not animating |
56
-
57
- ## Examples
58
-
59
- ### Loading State
60
- ```tsx
61
- const LoadingScreen = () => {
62
- const [isLoading, setIsLoading] = useState(true);
63
-
64
- return (
65
- <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
66
- <ActivityIndicator
67
- animating={isLoading}
68
- size="large"
69
- intent="primary"
70
- />
71
- {isLoading && <Text>Loading data...</Text>}
72
- </View>
73
- );
74
- };
75
- ```
76
-
77
- ### Button with Loading
78
- ```tsx
79
- const SubmitButton = ({ onSubmit }) => {
80
- const [isSubmitting, setIsSubmitting] = useState(false);
81
-
82
- const handleSubmit = async () => {
83
- setIsSubmitting(true);
84
- await onSubmit();
85
- setIsSubmitting(false);
86
- };
87
-
88
- return (
89
- <Button onPress={handleSubmit} disabled={isSubmitting}>
90
- {isSubmitting ? (
91
- <ActivityIndicator size="small" color="white" />
92
- ) : (
93
- 'Submit'
94
- )}
95
- </Button>
96
- );
97
- };
98
- ```
99
-
100
- ### Custom Styled Indicator
101
- ```tsx
102
- <ActivityIndicator
103
- size={50}
104
- color="#8B5CF6"
105
- style={{
106
- backgroundColor: 'rgba(139, 92, 246, 0.1)',
107
- padding: 20,
108
- borderRadius: 10,
109
- }}
110
- />
111
- ```
112
-
113
- ## Platform Differences
114
-
115
- - **Web**: Uses CSS animations with a custom spinner implementation
116
- - **Native**: Uses React Native's built-in `ActivityIndicator` component
117
- - Both platforms support all the same props for consistency
118
-
119
- ## Accessibility
120
-
121
- The ActivityIndicator component includes:
122
- - Proper ARIA roles for screen readers on web
123
- - Visual indication of loading state
124
- - Support for test IDs for testing
125
-
126
- ## Best Practices
127
-
128
- 1. **Always provide context**: Pair with text to explain what's loading
129
- 2. **Use appropriate sizes**: Small for inline, large for full-screen loading
130
- 3. **Match intent to context**: Use error intent for retry states, success for completion
131
- 4. **Consider animation performance**: Avoid too many simultaneous indicators
132
- 5. **Provide alternative content**: Show skeleton screens or placeholders when appropriate