@idealyst/components 1.0.83 → 1.0.85

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 +20 -2
  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 +140 -63
  170. package/src/Select/Select.styles.tsx +312 -302
  171. package/src/Select/Select.web.tsx +156 -316
  172. package/src/Select/index.ts +5 -2
  173. package/src/Select/index.web.ts +5 -2
  174. package/src/Select/types.ts +13 -7
  175. package/src/Skeleton/Skeleton.native.tsx +139 -0
  176. package/src/Skeleton/Skeleton.styles.tsx +59 -0
  177. package/src/Skeleton/Skeleton.web.tsx +112 -0
  178. package/src/Skeleton/index.native.ts +4 -0
  179. package/src/Skeleton/index.ts +5 -0
  180. package/src/Skeleton/index.web.ts +5 -0
  181. package/src/Skeleton/types.ts +75 -0
  182. package/src/Slider/Slider.native.tsx +248 -0
  183. package/src/Slider/Slider.styles.tsx +241 -0
  184. package/src/Slider/Slider.web.tsx +226 -0
  185. package/src/Slider/index.native.ts +3 -0
  186. package/src/Slider/index.ts +5 -0
  187. package/src/Slider/index.web.ts +5 -0
  188. package/src/Slider/types.ts +31 -0
  189. package/src/Switch/Switch.native.tsx +131 -0
  190. package/src/Switch/Switch.styles.tsx +169 -0
  191. package/src/Switch/Switch.web.tsx +121 -0
  192. package/src/Switch/index.native.ts +3 -0
  193. package/src/Switch/index.ts +5 -0
  194. package/src/Switch/index.web.ts +5 -0
  195. package/src/Switch/types.ts +21 -0
  196. package/src/TabBar/TabBar.native.tsx +142 -0
  197. package/src/TabBar/TabBar.styles.tsx +399 -0
  198. package/src/TabBar/TabBar.web.tsx +205 -0
  199. package/src/TabBar/index.native.tsx +3 -0
  200. package/src/TabBar/index.ts +3 -0
  201. package/src/TabBar/index.web.tsx +3 -0
  202. package/src/TabBar/types.ts +26 -0
  203. package/src/Table/Table.native.tsx +122 -0
  204. package/src/Table/Table.styles.tsx +283 -0
  205. package/src/Table/Table.web.tsx +112 -0
  206. package/src/Table/index.native.tsx +3 -0
  207. package/src/Table/index.ts +3 -0
  208. package/src/Table/index.web.tsx +3 -0
  209. package/src/Table/types.ts +28 -0
  210. package/src/Text/Text.native.tsx +12 -11
  211. package/src/Text/Text.styles.tsx +76 -64
  212. package/src/Text/Text.web.tsx +14 -9
  213. package/src/Text/index.ts +5 -5
  214. package/src/Text/index.web.ts +5 -3
  215. package/src/Text/types.ts +20 -13
  216. package/src/TextArea/TextArea.native.tsx +134 -0
  217. package/src/TextArea/TextArea.styles.tsx +175 -0
  218. package/src/TextArea/TextArea.web.tsx +156 -0
  219. package/src/TextArea/index.native.ts +3 -0
  220. package/src/TextArea/index.ts +3 -0
  221. package/src/TextArea/index.web.ts +3 -0
  222. package/src/TextArea/types.ts +30 -0
  223. package/src/Tooltip/Tooltip.native.tsx +165 -0
  224. package/src/Tooltip/Tooltip.styles.tsx +73 -0
  225. package/src/Tooltip/Tooltip.web.tsx +87 -0
  226. package/src/Tooltip/index.native.ts +3 -0
  227. package/src/Tooltip/index.ts +3 -0
  228. package/src/Tooltip/types.ts +18 -0
  229. package/src/Video/Video.native.tsx +105 -0
  230. package/src/Video/Video.styles.tsx +39 -0
  231. package/src/Video/Video.web.tsx +115 -0
  232. package/src/Video/index.native.ts +5 -0
  233. package/src/Video/index.ts +5 -0
  234. package/src/Video/types.ts +29 -0
  235. package/src/View/View.native.tsx +9 -14
  236. package/src/View/View.styles.tsx +101 -93
  237. package/src/View/View.web.tsx +16 -17
  238. package/src/View/index.ts +5 -5
  239. package/src/View/index.web.ts +5 -3
  240. package/src/View/types.ts +29 -21
  241. package/src/examples/AccordionExamples.tsx +126 -0
  242. package/src/examples/AlertExamples.tsx +280 -0
  243. package/src/examples/AvatarExamples.tsx +23 -23
  244. package/src/examples/BadgeExamples.tsx +109 -41
  245. package/src/examples/BreadcrumbExamples.tsx +312 -0
  246. package/src/examples/ButtonExamples.tsx +160 -33
  247. package/src/examples/CardExamples.tsx +40 -40
  248. package/src/examples/CheckboxExamples.tsx +12 -12
  249. package/src/examples/ChipExamples.tsx +197 -0
  250. package/src/examples/DialogExamples.tsx +22 -22
  251. package/src/examples/DividerExamples.tsx +49 -49
  252. package/src/examples/IconExamples.tsx +270 -54
  253. package/src/examples/ImageExamples.tsx +174 -0
  254. package/src/examples/InputExamples.tsx +75 -17
  255. package/src/examples/ListExamples.tsx +288 -0
  256. package/src/examples/MenuExamples.tsx +144 -0
  257. package/src/examples/PopoverExamples.tsx +69 -73
  258. package/src/examples/ProgressExamples.tsx +137 -0
  259. package/src/examples/RadioButtonExamples.tsx +161 -0
  260. package/src/examples/SVGImageExamples.tsx +19 -17
  261. package/src/examples/ScreenExamples.tsx +31 -31
  262. package/src/examples/SelectExamples.tsx +67 -67
  263. package/src/examples/SkeletonExamples.tsx +206 -0
  264. package/src/examples/SliderExamples.tsx +200 -0
  265. package/src/examples/SwitchExamples.tsx +182 -0
  266. package/src/examples/TabBarExamples.tsx +143 -0
  267. package/src/examples/TableExamples.tsx +280 -0
  268. package/src/examples/TextAreaExamples.tsx +173 -0
  269. package/src/examples/TextExamples.tsx +28 -32
  270. package/src/examples/ThemeExtensionExamples.tsx +10 -10
  271. package/src/examples/TooltipExamples.tsx +126 -0
  272. package/src/examples/VideoExamples.tsx +144 -0
  273. package/src/examples/ViewExamples.tsx +64 -56
  274. package/src/examples/index.ts +17 -3
  275. package/src/hooks/useMergeRefs.ts +16 -0
  276. package/src/hooks/useSmartPosition.native.ts +169 -0
  277. package/src/index.native.ts +80 -9
  278. package/src/index.ts +71 -1
  279. package/src/internal/BoundedModalContent.native.tsx +58 -0
  280. package/src/internal/PositionedPortal.tsx +254 -0
  281. package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
  282. package/src/unistyles.d.ts +6 -0
  283. package/src/utils/buildSizeVariants.ts +16 -0
  284. package/src/utils/deepMerge.ts +43 -0
  285. package/src/utils/positionUtils.native.ts +280 -0
  286. package/src/utils/styleHelpers.ts +48 -0
  287. package/LLM-ACCESS-GUIDE.md +0 -143
  288. package/src/ActivityIndicator/README.md +0 -132
  289. package/src/Avatar/README.md +0 -139
  290. package/src/Badge/README.md +0 -170
  291. package/src/Button/Button.types.ts +0 -12
  292. package/src/Button/README.md +0 -262
  293. package/src/Card/README.md +0 -258
  294. package/src/Checkbox/README.md +0 -102
  295. package/src/Dialog/README.md +0 -210
  296. package/src/Divider/README.md +0 -108
  297. package/src/Icon/README.md +0 -81
  298. package/src/Input/README.md +0 -100
  299. package/src/SVGImage/README.md +0 -209
  300. package/src/Screen/README.md +0 -86
  301. package/src/Select/README.md +0 -166
  302. package/src/Text/README.md +0 -94
  303. package/src/View/README.md +0 -107
  304. package/src/examples/AllExamples.tsx +0 -88
  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,268 @@
1
+ import React, { isValidElement, useState, useRef, useEffect } from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import {
4
+ breadcrumbContainerStyles,
5
+ breadcrumbItemStyles,
6
+ breadcrumbSeparatorStyles,
7
+ breadcrumbEllipsisStyles,
8
+ breadcrumbMenuButtonStyles
9
+ } from './Breadcrumb.styles';
10
+ import type { BreadcrumbProps, BreadcrumbItem as BreadcrumbItemType } from './types';
11
+ import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
12
+ import { resolveIconPath, isIconName } from '../Icon/icon-resolver';
13
+ import Menu from '../Menu/Menu.web';
14
+ import type { MenuItem } from '../Menu/types';
15
+
16
+ interface BreadcrumbItemProps {
17
+ item: BreadcrumbItemType;
18
+ isLast: boolean;
19
+ size: BreadcrumbProps['size'];
20
+ intent: BreadcrumbProps['intent'];
21
+ itemStyle?: BreadcrumbProps['itemStyle'];
22
+ }
23
+
24
+ const BreadcrumbItem: React.FC<BreadcrumbItemProps> = ({ item, isLast, size, intent, itemStyle }) => {
25
+ // Apply variants for this item only
26
+ breadcrumbItemStyles.useVariants({
27
+ size,
28
+ intent,
29
+ disabled: item.disabled || false,
30
+ isLast,
31
+ clickable: !!item.onPress && !item.disabled,
32
+ });
33
+
34
+ const itemProps = getWebProps([breadcrumbItemStyles.item]);
35
+ const itemTextProps = getWebProps([breadcrumbItemStyles.itemText, itemStyle]);
36
+ const iconProps = getWebProps([breadcrumbItemStyles.icon]);
37
+
38
+ const handleClick = () => {
39
+ if (!item.disabled && item.onPress) {
40
+ item.onPress();
41
+ }
42
+ };
43
+
44
+ const renderIcon = () => {
45
+ if (!item.icon) return null;
46
+
47
+ if (isIconName(item.icon)) {
48
+ const iconPath = resolveIconPath(item.icon);
49
+ return (
50
+ <IconSvg
51
+ path={iconPath}
52
+ {...iconProps}
53
+ aria-label={item.icon}
54
+ />
55
+ );
56
+ } else if (isValidElement(item.icon)) {
57
+ return item.icon;
58
+ }
59
+
60
+ return null;
61
+ };
62
+
63
+ const content = (
64
+ <div {...itemProps}>
65
+ {item.icon && (
66
+ <span
67
+ {...iconProps}
68
+ style={{
69
+ display: 'inline-flex',
70
+ alignItems: 'center',
71
+ justifyContent: 'center',
72
+ }}
73
+ >
74
+ {renderIcon()}
75
+ </span>
76
+ )}
77
+ <span {...itemTextProps}>
78
+ {item.label}
79
+ </span>
80
+ </div>
81
+ );
82
+
83
+ if (item.onPress && !item.disabled) {
84
+ return (
85
+ <button
86
+ onClick={handleClick}
87
+ style={{
88
+ background: 'none',
89
+ border: 'none',
90
+ padding: 0,
91
+ cursor: 'pointer',
92
+ textDecoration: 'none',
93
+ }}
94
+ disabled={item.disabled}
95
+ aria-current={isLast ? 'page' : undefined}
96
+ >
97
+ {content}
98
+ </button>
99
+ );
100
+ }
101
+
102
+ return (
103
+ <div aria-current={isLast ? 'page' : undefined}>
104
+ {content}
105
+ </div>
106
+ );
107
+ };
108
+
109
+ interface BreadcrumbSeparatorProps {
110
+ separator: React.ReactNode;
111
+ size: BreadcrumbProps['size'];
112
+ separatorStyle?: BreadcrumbProps['separatorStyle'];
113
+ }
114
+
115
+ const BreadcrumbSeparator: React.FC<BreadcrumbSeparatorProps> = ({ separator, size, separatorStyle }) => {
116
+ breadcrumbSeparatorStyles.useVariants({ size });
117
+ const separatorProps = getWebProps([breadcrumbSeparatorStyles.separator, separatorStyle]);
118
+
119
+ return (
120
+ <span {...separatorProps} aria-hidden="true">
121
+ {separator}
122
+ </span>
123
+ );
124
+ };
125
+
126
+ interface BreadcrumbEllipsisProps {
127
+ size: BreadcrumbProps['size'];
128
+ intent: BreadcrumbProps['intent'];
129
+ }
130
+
131
+ const BreadcrumbEllipsis: React.FC<BreadcrumbEllipsisProps> = ({ size, intent }) => {
132
+ breadcrumbEllipsisStyles.useVariants({ size });
133
+ const ellipsisProps = getWebProps([breadcrumbEllipsisStyles.ellipsis]);
134
+ const iconProps = getWebProps([breadcrumbEllipsisStyles.icon({ intent })]);
135
+ const ellipsisIconPath = resolveIconPath('dots-horizontal');
136
+
137
+ return (
138
+ <span {...ellipsisProps}>
139
+ <IconSvg
140
+ path={ellipsisIconPath}
141
+ {...iconProps}
142
+ aria-label="more items"
143
+ />
144
+ </span>
145
+ );
146
+ };
147
+
148
+ const Breadcrumb: React.FC<BreadcrumbProps> = ({
149
+ items,
150
+ separator = '/',
151
+ maxItems,
152
+ intent = 'primary',
153
+ size = 'md',
154
+ style,
155
+ itemStyle,
156
+ separatorStyle,
157
+ testID,
158
+ responsive = false,
159
+ minVisibleItems = 3,
160
+ }) => {
161
+ const [menuOpen, setMenuOpen] = useState(false);
162
+ const containerProps = getWebProps([breadcrumbContainerStyles.container, style as any]);
163
+ const menuIconPath = resolveIconPath('dots-horizontal');
164
+
165
+ // Apply variants for menu button
166
+ breadcrumbMenuButtonStyles.useVariants({ size });
167
+ const menuButtonProps = getWebProps([breadcrumbMenuButtonStyles.button]);
168
+ const menuIconProps = getWebProps([breadcrumbMenuButtonStyles.icon({ intent })]);
169
+
170
+ // Handle responsive collapsing
171
+ let displayItems = items;
172
+ let collapsedItems: BreadcrumbItemType[] = [];
173
+ let showMenu = false;
174
+ let showEllipsis = false;
175
+
176
+ if (responsive && items.length > minVisibleItems) {
177
+ // Responsive mode: show first item + menu + last (minVisibleItems - 2) items
178
+ showMenu = true;
179
+ const lastItemCount = Math.max(1, minVisibleItems - 2);
180
+ displayItems = [
181
+ items[0],
182
+ ...items.slice(-lastItemCount),
183
+ ];
184
+ collapsedItems = items.slice(1, -(lastItemCount));
185
+ } else if (maxItems && items.length > maxItems) {
186
+ // Legacy truncation mode
187
+ showEllipsis = true;
188
+ const firstItems = items.slice(0, 1);
189
+ const lastItems = items.slice(-(maxItems - 1));
190
+ displayItems = [...firstItems, ...lastItems];
191
+ }
192
+
193
+ // Convert collapsed breadcrumb items to menu items
194
+ const menuItems: MenuItem[] = collapsedItems.map((item, index) => ({
195
+ id: `collapsed-${index}`,
196
+ label: item.label,
197
+ onClick: item.onPress,
198
+ disabled: item.disabled,
199
+ icon: isIconName(item.icon) ? item.icon : undefined,
200
+ }));
201
+
202
+ return (
203
+ <nav {...containerProps} aria-label="Breadcrumb" data-testid={testID}>
204
+ {displayItems.map((item, index) => {
205
+ const isLast = index === displayItems.length - 1;
206
+ const shouldShowEllipsis = showEllipsis && index === 1;
207
+ const shouldShowMenu = showMenu && index === 1;
208
+
209
+ return (
210
+ <React.Fragment key={index}>
211
+ {shouldShowEllipsis && (
212
+ <>
213
+ <BreadcrumbEllipsis size={size} intent={intent} />
214
+ <BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
215
+ </>
216
+ )}
217
+
218
+ {shouldShowMenu && (
219
+ <>
220
+ <Menu
221
+ items={menuItems}
222
+ open={menuOpen}
223
+ onOpenChange={setMenuOpen}
224
+ placement="bottom-start"
225
+ size={size}
226
+ >
227
+ <button
228
+ className={menuButtonProps.className}
229
+ style={{
230
+ background: 'none',
231
+ border: 'none',
232
+ cursor: 'pointer',
233
+ display: 'inline-flex',
234
+ alignItems: 'center',
235
+ justifyContent: 'center',
236
+ }}
237
+ aria-label="Show more breadcrumb items"
238
+ >
239
+ <IconSvg
240
+ path={menuIconPath}
241
+ {...menuIconProps}
242
+ aria-label="dots-horizontal"
243
+ />
244
+ </button>
245
+ </Menu>
246
+ <BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
247
+ </>
248
+ )}
249
+
250
+ <BreadcrumbItem
251
+ item={item}
252
+ isLast={isLast}
253
+ size={size}
254
+ intent={intent}
255
+ itemStyle={itemStyle}
256
+ />
257
+
258
+ {!isLast && (
259
+ <BreadcrumbSeparator separator={separator} size={size} separatorStyle={separatorStyle} />
260
+ )}
261
+ </React.Fragment>
262
+ );
263
+ })}
264
+ </nav>
265
+ );
266
+ };
267
+
268
+ export default Breadcrumb;
@@ -0,0 +1,5 @@
1
+ import BreadcrumbComponent from './Breadcrumb.native';
2
+
3
+ export default BreadcrumbComponent;
4
+ export { BreadcrumbComponent as Breadcrumb };
5
+ export type { BreadcrumbProps, BreadcrumbItem } from './types';
@@ -0,0 +1,5 @@
1
+ import BreadcrumbComponent from './Breadcrumb.web';
2
+
3
+ export default BreadcrumbComponent;
4
+ export { BreadcrumbComponent as Breadcrumb };
5
+ export type { BreadcrumbProps, BreadcrumbItem } from './types';
@@ -0,0 +1,5 @@
1
+ import BreadcrumbComponent from './Breadcrumb.web';
2
+
3
+ export default BreadcrumbComponent;
4
+ export { BreadcrumbComponent as Breadcrumb };
5
+ export type { BreadcrumbProps, BreadcrumbItem } from './types';
@@ -0,0 +1,56 @@
1
+ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
2
+ import type { IconName } from '../Icon/icon-types';
3
+ import { Size } from '@idealyst/theme';
4
+
5
+ // Component-specific type aliases for future extensibility
6
+ export type BreadcrumbIntentVariant = 'primary' | 'neutral';
7
+ export type BreadcrumbSizeVariant = Size;
8
+
9
+ export interface BreadcrumbItem {
10
+ /** Label text for the breadcrumb item */
11
+ label: string;
12
+
13
+ /** Optional icon to display before the label - can be an IconName or custom component */
14
+ icon?: IconName | React.ReactNode;
15
+
16
+ /** Click handler for the breadcrumb item */
17
+ onPress?: () => void;
18
+
19
+ /** Whether this item is disabled */
20
+ disabled?: boolean;
21
+ }
22
+
23
+ export interface BreadcrumbProps {
24
+ /** Array of breadcrumb items */
25
+ items: BreadcrumbItem[];
26
+
27
+ /** Custom separator between items (default: '/') */
28
+ separator?: React.ReactNode;
29
+
30
+ /** Maximum number of items to show before truncating */
31
+ maxItems?: number;
32
+
33
+ /** Intent color for links */
34
+ intent?: BreadcrumbIntentVariant;
35
+
36
+ /** Size of the breadcrumb text */
37
+ size?: BreadcrumbSizeVariant;
38
+
39
+ /** Custom container style */
40
+ style?: StyleProp<ViewStyle>;
41
+
42
+ /** Custom item style */
43
+ itemStyle?: StyleProp<ViewStyle>;
44
+
45
+ /** Custom separator style */
46
+ separatorStyle?: StyleProp<TextStyle>;
47
+
48
+ /** Test ID for testing */
49
+ testID?: string;
50
+
51
+ /** Enable responsive collapsing on narrow screens */
52
+ responsive?: boolean;
53
+
54
+ /** Minimum number of items to show before collapsing (default: 3) */
55
+ minVisibleItems?: number;
56
+ }
@@ -1,39 +1,90 @@
1
- import React from 'react';
2
- import { TouchableOpacity, Text } from 'react-native';
1
+ import React, { isValidElement, forwardRef, ComponentRef } from 'react';
2
+ import { TouchableOpacity, Text, View } from 'react-native';
3
+ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
3
4
  import { ButtonProps } from './types';
4
5
  import { buttonStyles } from './Button.styles';
5
6
 
6
- const Button: React.FC<ButtonProps> = ({
7
- children,
8
- title,
9
- onPress,
10
- disabled = false,
11
- variant = 'contained',
12
- intent = 'primary',
13
- size = 'medium',
14
- style,
15
- testID,
16
- }) => {
17
- buttonStyles.useVariants({
18
- size,
19
- intent,
20
- variant,
21
- disabled,
22
- });
7
+ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((props, ref) => {
8
+ const {
9
+ children,
10
+ title,
11
+ onPress,
12
+ disabled = false,
13
+ type = 'contained',
14
+ intent = 'primary',
15
+ size = 'md',
16
+ leftIcon,
17
+ rightIcon,
18
+ style,
19
+ testID,
20
+ } = props;
21
+
22
+ // Compute dynamic styles
23
+ const buttonStyle = buttonStyles.button(size, intent, type, disabled);
24
+ const textStyle = buttonStyles.text(size, intent, type, disabled);
25
+ const iconStyle = buttonStyles.icon(size, intent, type);
26
+
27
+ // Map button size to icon size
28
+ const iconSizeMap = {
29
+ xs: 12,
30
+ sm: 14,
31
+ md: 16,
32
+ lg: 18,
33
+ xl: 20,
34
+ } as const;
35
+ const iconSize = iconSizeMap[size];
36
+
37
+ // Helper to render icon - uses the icon styles from buttonStyles
38
+ const renderIcon = (icon: string | React.ReactNode) => {
39
+ if (typeof icon === 'string') {
40
+ // Render MaterialCommunityIcons with explicit size prop
41
+ // The icon styles provide the correct color based on dynamic styles
42
+ return (
43
+ <MaterialCommunityIcons
44
+ name={icon}
45
+ size={iconSize}
46
+ style={iconStyle}
47
+ />
48
+ );
49
+ } else if (isValidElement(icon)) {
50
+ // Render custom component as-is
51
+ return icon;
52
+ }
53
+ return null;
54
+ };
55
+
56
+ // Use children if available, otherwise use title
57
+ const buttonContent = children || title;
58
+
59
+ // Determine if we need to wrap content in icon container
60
+ const hasIcons = leftIcon || rightIcon;
23
61
 
24
62
  return (
25
63
  <TouchableOpacity
64
+ ref={ref}
26
65
  onPress={onPress}
27
66
  disabled={disabled}
28
67
  testID={testID}
29
68
  activeOpacity={0.7}
30
- style={[buttonStyles.button, style]}
69
+ style={[buttonStyle, style]}
31
70
  >
32
- <Text style={buttonStyles.text}>
33
- {children || title}
34
- </Text>
71
+ {hasIcons ? (
72
+ <View style={buttonStyles.iconContainer}>
73
+ {leftIcon && renderIcon(leftIcon)}
74
+ <Text style={textStyle}>
75
+ {buttonContent}
76
+ </Text>
77
+ {rightIcon && renderIcon(rightIcon)}
78
+ </View>
79
+ ) : (
80
+ <Text style={textStyle}>
81
+ {buttonContent}
82
+ </Text>
83
+ )}
35
84
  </TouchableOpacity>
36
85
  );
37
- };
86
+ });
87
+
88
+ Button.displayName = 'Button';
38
89
 
39
90
  export default Button;