@utilitywarehouse/hearth-react-native 0.2.0 → 0.3.1

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 (289) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +3 -1
  3. package/CHANGELOG.md +34 -0
  4. package/build/components/Alert/AlertCloseButton.js +25 -3
  5. package/build/components/Alert/AlertIcon.js +17 -1
  6. package/build/components/Alert/AlertIconButton.js +27 -1
  7. package/build/components/Alert/AlertLink.js +47 -1
  8. package/build/components/Alert/AlertText.d.ts +1 -1
  9. package/build/components/Alert/AlertText.js +26 -2
  10. package/build/components/Alert/AlertTitle.d.ts +1 -1
  11. package/build/components/Alert/AlertTitle.js +26 -2
  12. package/build/components/Badge/Badge.js +101 -14
  13. package/build/components/Badge/Badge.props.d.ts +2 -2
  14. package/build/components/Badge/BadgeIcon.js +27 -29
  15. package/build/components/Badge/BadgeText.js +29 -31
  16. package/build/components/Button/Button.d.ts +2 -2
  17. package/build/components/Button/ButtonGroupRoot.d.ts +3 -2
  18. package/build/components/Button/ButtonGroupRoot.js +9 -0
  19. package/build/components/Button/ButtonRoot.js +1 -0
  20. package/build/components/Card/Card.props.d.ts +2 -2
  21. package/build/components/Checkbox/Checkbox.d.ts +2 -2
  22. package/build/components/Checkbox/Checkbox.js +11 -10
  23. package/build/components/Checkbox/Checkbox.props.d.ts +3 -1
  24. package/build/components/Checkbox/CheckboxIcon.js +1 -1
  25. package/build/components/Checkbox/CheckboxImage.d.ts +6 -0
  26. package/build/components/Checkbox/CheckboxImage.js +5 -0
  27. package/build/components/Checkbox/CheckboxTileRoot.js +1 -1
  28. package/build/components/Checkbox/index.d.ts +3 -2
  29. package/build/components/Checkbox/index.js +2 -1
  30. package/build/components/CurrencyInput/CurrencyInput.d.ts +6 -0
  31. package/build/components/CurrencyInput/CurrencyInput.js +47 -0
  32. package/build/components/CurrencyInput/CurrencyInput.props.d.ts +14 -0
  33. package/build/components/CurrencyInput/CurrencyInput.props.js +1 -0
  34. package/build/components/CurrencyInput/index.d.ts +1 -0
  35. package/build/components/CurrencyInput/index.js +1 -0
  36. package/build/components/DescriptionList/DescriptionList.context.d.ts +6 -0
  37. package/build/components/DescriptionList/DescriptionList.context.js +9 -0
  38. package/build/components/DescriptionList/DescriptionList.d.ts +6 -0
  39. package/build/components/DescriptionList/DescriptionList.js +25 -0
  40. package/build/components/DescriptionList/DescriptionList.props.d.ts +18 -0
  41. package/build/components/DescriptionList/DescriptionList.props.js +1 -0
  42. package/build/components/DescriptionList/DescriptionListItem.d.ts +6 -0
  43. package/build/components/DescriptionList/DescriptionListItem.js +49 -0
  44. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +17 -0
  45. package/build/components/DescriptionList/DescriptionListItem.props.js +1 -0
  46. package/build/components/DescriptionList/index.d.ts +4 -0
  47. package/build/components/DescriptionList/index.js +2 -0
  48. package/build/components/Divider/Divider.js +46 -0
  49. package/build/components/Divider/Divider.props.d.ts +2 -2
  50. package/build/components/Flex/Flex.props.d.ts +3 -2
  51. package/build/components/Grid/Grid.props.d.ts +2 -2
  52. package/build/components/IconContainer/IconContainer.d.ts +5 -0
  53. package/build/components/IconContainer/IconContainer.js +161 -0
  54. package/build/components/IconContainer/IconContainer.props.d.ts +15 -0
  55. package/build/components/IconContainer/IconContainer.props.js +1 -0
  56. package/build/components/IconContainer/index.d.ts +2 -0
  57. package/build/components/IconContainer/index.js +1 -0
  58. package/build/components/Icons/CircleIcon.js +3 -3
  59. package/build/components/Input/Input.js +2 -34
  60. package/build/components/Input/Input.props.d.ts +1 -17
  61. package/build/components/Input/InputField.js +0 -7
  62. package/build/components/Link/Link.d.ts +1 -1
  63. package/build/components/Link/Link.js +4 -4
  64. package/build/components/Link/Link.props.d.ts +3 -0
  65. package/build/components/Modal/Modal.js +17 -1
  66. package/build/components/Radio/Radio.d.ts +2 -2
  67. package/build/components/Radio/Radio.js +9 -8
  68. package/build/components/Radio/Radio.props.d.ts +3 -1
  69. package/build/components/Radio/RadioImage.d.ts +6 -0
  70. package/build/components/Radio/RadioImage.js +5 -0
  71. package/build/components/Radio/RadioTileRoot.js +1 -1
  72. package/build/components/Radio/index.d.ts +3 -2
  73. package/build/components/Radio/index.js +2 -1
  74. package/build/components/SectionHeader/SectionHeader.js +1 -0
  75. package/build/components/Select/SelectOption.js +1 -7
  76. package/build/components/Tabs/Tab.d.ts +18 -0
  77. package/build/components/Tabs/Tab.js +74 -0
  78. package/build/components/Tabs/Tab.props.d.ts +14 -0
  79. package/build/components/Tabs/Tab.props.js +1 -0
  80. package/build/components/Tabs/TabPanel.d.ts +3 -0
  81. package/build/components/Tabs/TabPanel.js +34 -0
  82. package/build/components/Tabs/TabPanel.props.d.ts +8 -0
  83. package/build/components/Tabs/TabPanel.props.js +1 -0
  84. package/build/components/Tabs/Tabs.context.d.ts +23 -0
  85. package/build/components/Tabs/Tabs.context.js +8 -0
  86. package/build/components/Tabs/Tabs.d.ts +6 -0
  87. package/build/components/Tabs/Tabs.js +114 -0
  88. package/build/components/Tabs/Tabs.props.d.ts +19 -0
  89. package/build/components/Tabs/Tabs.props.js +1 -0
  90. package/build/components/Tabs/TabsList.d.ts +6 -0
  91. package/build/components/Tabs/TabsList.js +112 -0
  92. package/build/components/Tabs/TabsList.props.d.ts +6 -0
  93. package/build/components/Tabs/TabsList.props.js +1 -0
  94. package/build/components/Tabs/index.d.ts +8 -0
  95. package/build/components/Tabs/index.js +4 -0
  96. package/build/components/UnstyledIconButton/UnstyledIconButton.d.ts +1 -1
  97. package/build/components/UnstyledIconButton/UnstyledIconButton.js +4 -4
  98. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +2 -1
  99. package/build/components/index.d.ts +4 -0
  100. package/build/components/index.js +4 -0
  101. package/build/core/themes.d.ts +428 -160
  102. package/build/core/themes.js +57 -1
  103. package/build/tokens/color.d.ts +88 -80
  104. package/build/tokens/color.js +44 -40
  105. package/build/tokens/components/dark/alert.d.ts +13 -0
  106. package/build/tokens/components/dark/alert.js +13 -0
  107. package/build/tokens/components/dark/button.d.ts +1 -0
  108. package/build/tokens/components/dark/button.js +1 -0
  109. package/build/tokens/components/dark/checkbox.d.ts +4 -1
  110. package/build/tokens/components/dark/checkbox.js +4 -1
  111. package/build/tokens/components/dark/icon-button.d.ts +10 -3
  112. package/build/tokens/components/dark/icon-button.js +10 -3
  113. package/build/tokens/components/dark/index.d.ts +1 -0
  114. package/build/tokens/components/dark/index.js +1 -0
  115. package/build/tokens/components/dark/link.d.ts +5 -0
  116. package/build/tokens/components/dark/link.js +5 -0
  117. package/build/tokens/components/dark/progress-bar.d.ts +41 -0
  118. package/build/tokens/components/dark/progress-bar.js +40 -0
  119. package/build/tokens/components/dark/radio.d.ts +1 -1
  120. package/build/tokens/components/dark/radio.js +1 -1
  121. package/build/tokens/components/dark/tabs.d.ts +2 -0
  122. package/build/tokens/components/dark/tabs.js +2 -0
  123. package/build/tokens/components/light/alert.d.ts +13 -0
  124. package/build/tokens/components/light/alert.js +13 -0
  125. package/build/tokens/components/light/badge.d.ts +1 -1
  126. package/build/tokens/components/light/badge.js +1 -1
  127. package/build/tokens/components/light/button.d.ts +1 -0
  128. package/build/tokens/components/light/button.js +1 -0
  129. package/build/tokens/components/light/checkbox.d.ts +6 -3
  130. package/build/tokens/components/light/checkbox.js +6 -3
  131. package/build/tokens/components/light/icon-button.d.ts +8 -1
  132. package/build/tokens/components/light/icon-button.js +8 -1
  133. package/build/tokens/components/light/index.d.ts +1 -0
  134. package/build/tokens/components/light/index.js +1 -0
  135. package/build/tokens/components/light/link.d.ts +5 -0
  136. package/build/tokens/components/light/link.js +5 -0
  137. package/build/tokens/components/light/progress-bar.d.ts +41 -0
  138. package/build/tokens/components/light/progress-bar.js +40 -0
  139. package/build/tokens/components/light/radio.d.ts +3 -3
  140. package/build/tokens/components/light/radio.js +3 -3
  141. package/build/tokens/components/light/tabs.d.ts +2 -0
  142. package/build/tokens/components/light/tabs.js +2 -0
  143. package/build/tokens/index.d.ts +1 -0
  144. package/build/tokens/index.js +1 -0
  145. package/build/tokens/layout.d.ts +48 -30
  146. package/build/tokens/layout.js +24 -15
  147. package/build/tokens/motion.d.ts +23 -0
  148. package/build/tokens/motion.js +22 -0
  149. package/build/tokens/primitive.d.ts +19 -0
  150. package/build/tokens/primitive.js +19 -0
  151. package/build/tokens/semantic-dark.d.ts +26 -24
  152. package/build/tokens/semantic-dark.js +26 -24
  153. package/build/tokens/semantic-light.d.ts +18 -16
  154. package/build/tokens/semantic-light.js +18 -16
  155. package/build/types/values.d.ts +2 -1
  156. package/build/utils/formatThousands.d.ts +2 -0
  157. package/build/utils/formatThousands.js +16 -0
  158. package/build/utils/index.d.ts +1 -0
  159. package/build/utils/index.js +1 -0
  160. package/docs/assets/bank-logo.png +0 -0
  161. package/docs/assets/bank-logo1.png +0 -0
  162. package/docs/components/AllComponents.web.tsx +97 -8
  163. package/docs/components/NextPrevPage.tsx +11 -3
  164. package/docs/components/UsageWrap.tsx +2 -2
  165. package/docs/components/index.ts +6 -7
  166. package/docs/heplers/addReactNativePrefix.ts +8 -0
  167. package/docs/heplers/index.ts +1 -0
  168. package/docs/introduction.mdx +3 -3
  169. package/docs/theme-tokens.mdx +42 -0
  170. package/package.json +13 -13
  171. package/src/components/Alert/AlertCloseButton.tsx +33 -5
  172. package/src/components/Alert/AlertIcon.tsx +17 -1
  173. package/src/components/Alert/AlertIconButton.tsx +37 -4
  174. package/src/components/Alert/AlertLink.tsx +52 -1
  175. package/src/components/Alert/AlertText.tsx +28 -3
  176. package/src/components/Alert/AlertTitle.tsx +28 -3
  177. package/src/components/Badge/Badge.docs.mdx +7 -7
  178. package/src/components/Badge/Badge.props.ts +3 -2
  179. package/src/components/Badge/Badge.stories.tsx +81 -92
  180. package/src/components/Badge/Badge.tsx +101 -14
  181. package/src/components/Badge/BadgeIcon.tsx +27 -29
  182. package/src/components/Badge/BadgeText.tsx +29 -31
  183. package/src/components/Button/ButtonGroupRoot.tsx +12 -2
  184. package/src/components/Button/ButtonRoot.tsx +1 -0
  185. package/src/components/Card/Card.docs.mdx +1 -1
  186. package/src/components/Card/Card.props.ts +2 -2
  187. package/src/components/Checkbox/Checkbox.docs.mdx +45 -7
  188. package/src/components/Checkbox/Checkbox.props.ts +3 -1
  189. package/src/components/Checkbox/Checkbox.stories.tsx +37 -1
  190. package/src/components/Checkbox/Checkbox.tsx +12 -9
  191. package/src/components/Checkbox/CheckboxIcon.tsx +1 -1
  192. package/src/components/Checkbox/CheckboxImage.tsx +9 -0
  193. package/src/components/Checkbox/CheckboxTileRoot.tsx +1 -1
  194. package/src/components/Checkbox/index.ts +3 -2
  195. package/src/components/CurrencyInput/CurrencyInput.docs.mdx +120 -0
  196. package/src/components/CurrencyInput/CurrencyInput.props.ts +19 -0
  197. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +116 -0
  198. package/src/components/CurrencyInput/CurrencyInput.tsx +91 -0
  199. package/src/components/CurrencyInput/index.ts +1 -0
  200. package/src/components/DescriptionList/DescriptionList.context.ts +18 -0
  201. package/src/components/DescriptionList/DescriptionList.docs.mdx +98 -0
  202. package/src/components/DescriptionList/DescriptionList.props.ts +20 -0
  203. package/src/components/DescriptionList/DescriptionList.stories.tsx +154 -0
  204. package/src/components/DescriptionList/DescriptionList.tsx +64 -0
  205. package/src/components/DescriptionList/DescriptionListItem.props.ts +19 -0
  206. package/src/components/DescriptionList/DescriptionListItem.tsx +101 -0
  207. package/src/components/DescriptionList/index.ts +4 -0
  208. package/src/components/Divider/Divider.props.ts +2 -2
  209. package/src/components/Divider/Divider.stories.tsx +3 -3
  210. package/src/components/Divider/Divider.tsx +46 -0
  211. package/src/components/Flex/Flex.docs.mdx +4 -4
  212. package/src/components/Flex/Flex.props.ts +3 -2
  213. package/src/components/Flex/Flex.stories.tsx +1 -1
  214. package/src/components/Grid/Grid.docs.mdx +12 -12
  215. package/src/components/Grid/Grid.props.ts +2 -2
  216. package/src/components/Grid/Grid.stories.tsx +2 -2
  217. package/src/components/IconContainer/IconContainer.docs.mdx +90 -0
  218. package/src/components/IconContainer/IconContainer.props.ts +17 -0
  219. package/src/components/IconContainer/IconContainer.stories.tsx +130 -0
  220. package/src/components/IconContainer/IconContainer.tsx +180 -0
  221. package/src/components/IconContainer/index.tsx +2 -0
  222. package/src/components/Icons/CircleIcon.tsx +9 -11
  223. package/src/components/Input/Input.docs.mdx +3 -3
  224. package/src/components/Input/Input.props.ts +0 -20
  225. package/src/components/Input/Input.stories.tsx +0 -6
  226. package/src/components/Input/Input.tsx +2 -49
  227. package/src/components/Input/InputField.tsx +0 -7
  228. package/src/components/Link/Link.props.ts +3 -0
  229. package/src/components/Link/Link.tsx +12 -6
  230. package/src/components/List/List.docs.mdx +24 -23
  231. package/src/components/Modal/Modal.tsx +18 -0
  232. package/src/components/Radio/Radio.docs.mdx +96 -124
  233. package/src/components/Radio/Radio.props.ts +3 -1
  234. package/src/components/Radio/Radio.stories.tsx +47 -0
  235. package/src/components/Radio/Radio.tsx +10 -7
  236. package/src/components/Radio/RadioImage.tsx +9 -0
  237. package/src/components/Radio/RadioTileRoot.tsx +1 -1
  238. package/src/components/Radio/index.ts +3 -2
  239. package/src/components/SectionHeader/SectionHeader.tsx +1 -0
  240. package/src/components/Select/Select.docs.mdx +6 -6
  241. package/src/components/Select/Select.stories.tsx +7 -7
  242. package/src/components/Select/SelectOption.tsx +4 -10
  243. package/src/components/Tabs/Tab.props.ts +16 -0
  244. package/src/components/Tabs/Tab.tsx +113 -0
  245. package/src/components/Tabs/TabPanel.props.ts +10 -0
  246. package/src/components/Tabs/TabPanel.tsx +46 -0
  247. package/src/components/Tabs/Tabs.context.ts +26 -0
  248. package/src/components/Tabs/Tabs.docs.mdx +214 -0
  249. package/src/components/Tabs/Tabs.props.ts +21 -0
  250. package/src/components/Tabs/Tabs.stories.tsx +270 -0
  251. package/src/components/Tabs/Tabs.tsx +139 -0
  252. package/src/components/Tabs/TabsList.props.ts +8 -0
  253. package/src/components/Tabs/TabsList.tsx +194 -0
  254. package/src/components/Tabs/index.ts +8 -0
  255. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  256. package/src/components/UnstyledIconButton/UnstyledIconButton.tsx +9 -3
  257. package/src/components/index.ts +4 -0
  258. package/src/core/themes.ts +57 -1
  259. package/src/tokens/color.ts +44 -40
  260. package/src/tokens/components/dark/alert.ts +13 -0
  261. package/src/tokens/components/dark/button.ts +1 -0
  262. package/src/tokens/components/dark/checkbox.ts +4 -1
  263. package/src/tokens/components/dark/icon-button.ts +10 -3
  264. package/src/tokens/components/dark/index.ts +1 -0
  265. package/src/tokens/components/dark/link.ts +5 -0
  266. package/src/tokens/components/dark/progress-bar.ts +41 -0
  267. package/src/tokens/components/dark/radio.ts +1 -1
  268. package/src/tokens/components/dark/tabs.ts +2 -0
  269. package/src/tokens/components/light/alert.ts +13 -0
  270. package/src/tokens/components/light/badge.ts +1 -1
  271. package/src/tokens/components/light/button.ts +1 -0
  272. package/src/tokens/components/light/checkbox.ts +6 -3
  273. package/src/tokens/components/light/icon-button.ts +8 -1
  274. package/src/tokens/components/light/index.ts +1 -0
  275. package/src/tokens/components/light/link.ts +5 -0
  276. package/src/tokens/components/light/progress-bar.ts +41 -0
  277. package/src/tokens/components/light/radio.ts +3 -3
  278. package/src/tokens/components/light/tabs.ts +2 -0
  279. package/src/tokens/index.ts +1 -0
  280. package/src/tokens/layout.ts +24 -15
  281. package/src/tokens/motion.ts +23 -0
  282. package/src/tokens/primitive.ts +19 -0
  283. package/src/tokens/semantic-dark.ts +26 -24
  284. package/src/tokens/semantic-light.ts +18 -16
  285. package/src/types/values.ts +3 -1
  286. package/src/utils/formatThousands.ts +14 -0
  287. package/src/utils/index.ts +1 -0
  288. package/docs/assets/react-native-pig.png +0 -0
  289. package/docs/components/AdvancedRadioExample.tsx +0 -126
@@ -0,0 +1,270 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import {
3
+ BroadbandMediumIcon,
4
+ ElectricityMediumIcon,
5
+ InsuranceMediumIcon,
6
+ MobileMediumIcon,
7
+ } from '@utilitywarehouse/hearth-react-native-icons';
8
+ import { useState } from 'react';
9
+ import { Platform } from 'react-native';
10
+ import { BodyText, Button, Flex, Tab, TabPanel, Tabs, TabsList } from '../';
11
+
12
+ const meta = {
13
+ title: 'Stories / Tabs',
14
+ component: Tabs,
15
+ args: {
16
+ size: 'md',
17
+ children: undefined,
18
+ },
19
+ argTypes: {
20
+ size: { control: 'radio', options: ['md', 'lg'] },
21
+ },
22
+ } satisfies Meta<typeof Tabs>;
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof meta>;
26
+
27
+ export const Playground: Story = {
28
+ args: {
29
+ children: (
30
+ <>
31
+ <Tab value="account">Account</Tab>
32
+ <Tab value="billing">Billing</Tab>
33
+ <Tab value="usage">Usage</Tab>
34
+ <Tab value="settings">Settings</Tab>
35
+ </>
36
+ ),
37
+ },
38
+ render: ({ children, ...args }) => (
39
+ <Tabs {...args} defaultValue="account">
40
+ <TabsList>{children}</TabsList>
41
+ <TabPanel value="account">
42
+ <BodyText>Account content</BodyText>
43
+ </TabPanel>
44
+ <TabPanel value="billing">
45
+ <BodyText>Billing content</BodyText>
46
+ </TabPanel>
47
+ <TabPanel value="usage">
48
+ <BodyText>Usage metrics</BodyText>
49
+ </TabPanel>
50
+ <TabPanel value="settings">
51
+ <BodyText>Settings content</BodyText>
52
+ </TabPanel>
53
+ </Tabs>
54
+ ),
55
+ };
56
+
57
+ export const Sizes: Story = {
58
+ args: { children: null },
59
+ render: () => (
60
+ <>
61
+ <Tabs size="md" defaultValue="a">
62
+ <TabsList>
63
+ <Tab value="a">Medium A</Tab>
64
+ <Tab value="b">Medium B</Tab>
65
+ <Tab value="c">Medium C</Tab>
66
+ </TabsList>
67
+ <TabPanel value="a">
68
+ <BodyText>Medium A panel</BodyText>
69
+ </TabPanel>
70
+ <TabPanel value="b">
71
+ <BodyText>Medium B panel</BodyText>
72
+ </TabPanel>
73
+ <TabPanel value="c">
74
+ <BodyText>Medium C panel</BodyText>
75
+ </TabPanel>
76
+ </Tabs>
77
+ <Tabs size="lg" defaultValue="a" style={{ marginTop: 24 }}>
78
+ <TabsList>
79
+ <Tab value="a">Large A</Tab>
80
+ <Tab value="b">Large B</Tab>
81
+ <Tab value="c">Large C</Tab>
82
+ </TabsList>
83
+ <TabPanel value="a">
84
+ <BodyText>Large A panel</BodyText>
85
+ </TabPanel>
86
+ <TabPanel value="b">
87
+ <BodyText>Large B panel</BodyText>
88
+ </TabPanel>
89
+ <TabPanel value="c">
90
+ <BodyText>Large C panel</BodyText>
91
+ </TabPanel>
92
+ </Tabs>
93
+ </>
94
+ ),
95
+ };
96
+
97
+ export const WithScrolling: Story = {
98
+ args: { children: null },
99
+ render: () => (
100
+ <Tabs defaultValue="one">
101
+ <TabsList>
102
+ <Tab value="one">One</Tab>
103
+ <Tab value="two">Two</Tab>
104
+ <Tab value="three">Three</Tab>
105
+ <Tab value="four">Four</Tab>
106
+ <Tab value="five">Five</Tab>
107
+ <Tab value="six">Six</Tab>
108
+ <Tab value="seven">Seven</Tab>
109
+ <Tab value="eight">Eight</Tab>
110
+ <Tab value="nine">Nine</Tab>
111
+ <Tab value="ten">Ten</Tab>
112
+ <Tab value="eleven">Eleven</Tab>
113
+ <Tab value="twelve">Twelve</Tab>
114
+ <Tab value="thirteen">Thirteen</Tab>
115
+ <Tab value="fourteen">Fourteen</Tab>
116
+ <Tab value="fifteen">Fifteen</Tab>
117
+ <Tab value="sixteen">Sixteen</Tab>
118
+ <Tab value="seventeen">Seventeen</Tab>
119
+ <Tab value="eighteen">Eighteen</Tab>
120
+ <Tab value="nineteen">Nineteen</Tab>
121
+ <Tab value="twenty">Twenty</Tab>
122
+ </TabsList>
123
+ <TabPanel value="one">
124
+ <BodyText>One panel</BodyText>
125
+ </TabPanel>
126
+ <TabPanel value="two">
127
+ <BodyText>Two panel</BodyText>
128
+ </TabPanel>
129
+ <TabPanel value="three">
130
+ <BodyText>Three panel</BodyText>
131
+ </TabPanel>
132
+ <TabPanel value="four">
133
+ <BodyText>Four panel</BodyText>
134
+ </TabPanel>
135
+ <TabPanel value="five">
136
+ <BodyText>Five panel</BodyText>
137
+ </TabPanel>
138
+ <TabPanel value="six">
139
+ <BodyText>Six panel</BodyText>
140
+ </TabPanel>
141
+ <TabPanel value="seven">
142
+ <BodyText>Seven panel</BodyText>
143
+ </TabPanel>
144
+ <TabPanel value="eight">
145
+ <BodyText>Eight panel</BodyText>
146
+ </TabPanel>
147
+ <TabPanel value="nine">
148
+ <BodyText>Nine panel</BodyText>
149
+ </TabPanel>
150
+ <TabPanel value="ten">
151
+ <BodyText>Ten panel</BodyText>
152
+ </TabPanel>
153
+ <TabPanel value="eleven">
154
+ <BodyText>Eleven panel</BodyText>
155
+ </TabPanel>
156
+ <TabPanel value="twelve">
157
+ <BodyText>Twelve panel</BodyText>
158
+ </TabPanel>
159
+ <TabPanel value="thirteen">
160
+ <BodyText>Thirteen panel</BodyText>
161
+ </TabPanel>
162
+ <TabPanel value="fourteen">
163
+ <BodyText>Fourteen panel</BodyText>
164
+ </TabPanel>
165
+ <TabPanel value="fifteen">
166
+ <BodyText>Fifteen panel</BodyText>
167
+ </TabPanel>
168
+ <TabPanel value="sixteen">
169
+ <BodyText>Sixteen panel</BodyText>
170
+ </TabPanel>
171
+ <TabPanel value="seventeen">
172
+ <BodyText>Seventeen panel</BodyText>
173
+ </TabPanel>
174
+ <TabPanel value="eighteen">
175
+ <BodyText>Eighteen panel</BodyText>
176
+ </TabPanel>
177
+ <TabPanel value="nineteen">
178
+ <BodyText>Nineteen panel</BodyText>
179
+ </TabPanel>
180
+ <TabPanel value="twenty">
181
+ <BodyText>Twenty panel</BodyText>
182
+ </TabPanel>
183
+ </Tabs>
184
+ ),
185
+ };
186
+
187
+ export const WithIcons: Story = {
188
+ args: { children: null },
189
+ render: () => (
190
+ <Tabs defaultValue="one">
191
+ <TabsList>
192
+ <Tab value="one" icon={ElectricityMediumIcon}>
193
+ Energy
194
+ </Tab>
195
+ <Tab value="two" icon={BroadbandMediumIcon}>
196
+ Broadband
197
+ </Tab>
198
+ <Tab value="three" icon={MobileMediumIcon}>
199
+ Mobile
200
+ </Tab>
201
+ <Tab value="four" icon={InsuranceMediumIcon}>
202
+ Insurance
203
+ </Tab>
204
+ </TabsList>
205
+ <TabPanel value="one">
206
+ <BodyText>One panel</BodyText>
207
+ </TabPanel>
208
+ <TabPanel value="two">
209
+ <BodyText>Two panel</BodyText>
210
+ </TabPanel>
211
+ <TabPanel value="three">
212
+ <BodyText>Three panel</BodyText>
213
+ </TabPanel>
214
+ <TabPanel value="four">
215
+ <BodyText>Four panel</BodyText>
216
+ </TabPanel>
217
+ </Tabs>
218
+ ),
219
+ };
220
+
221
+ export const Disabled: Story = {
222
+ args: { children: null },
223
+ render: () => (
224
+ <Tabs defaultValue="one" disabled>
225
+ <TabsList>
226
+ <Tab value="one">One</Tab>
227
+ <Tab value="two">Two</Tab>
228
+ <Tab value="three">Three</Tab>
229
+ </TabsList>
230
+ <TabPanel value="one">One panel</TabPanel>
231
+ <TabPanel value="two">Two panel</TabPanel>
232
+ <TabPanel value="three">Three panel</TabPanel>
233
+ </Tabs>
234
+ ),
235
+ };
236
+
237
+ export const Controlled: Story = {
238
+ args: { children: null },
239
+ render: () => {
240
+ const [value, setValue] = useState('account');
241
+ const nextTab = () => {
242
+ const tabs = ['account', 'billing', 'usage'];
243
+ const currentIndex = tabs.indexOf(value);
244
+ const nextIndex = (currentIndex + 1) % tabs.length;
245
+ setValue(tabs[nextIndex]);
246
+ };
247
+ return (
248
+ <Flex align={Platform.OS === 'web' ? 'flex-start' : 'stretch'} space="lg">
249
+ <Tabs value={value}>
250
+ <TabsList>
251
+ <Tab value="account">Account</Tab>
252
+ <Tab value="billing">Billing</Tab>
253
+ <Tab value="usage">Usage</Tab>
254
+ </TabsList>
255
+ <TabPanel value="account">
256
+ <BodyText>Account content</BodyText>
257
+ </TabPanel>
258
+ <TabPanel value="billing">
259
+ <BodyText>Billing content</BodyText>
260
+ </TabPanel>
261
+ <TabPanel value="usage">
262
+ <BodyText>Usage metrics content</BodyText>
263
+ </TabPanel>
264
+ </Tabs>
265
+
266
+ <Button onPress={nextTab}>Next Tab</Button>
267
+ </Flex>
268
+ );
269
+ },
270
+ };
@@ -0,0 +1,139 @@
1
+ import { Children, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import { Easing, useSharedValue, withTiming } from 'react-native-reanimated';
4
+ import { TabsContext } from './Tabs.context';
5
+ import type TabsProps from './Tabs.props';
6
+
7
+ const Tabs = ({
8
+ value: controlledValue,
9
+ defaultValue,
10
+ onValueChange,
11
+ size = 'md',
12
+ disabled,
13
+ children,
14
+ withPanels,
15
+ ...props
16
+ }: TabsProps) => {
17
+ // Collect child tab values
18
+ const tabValues = useMemo(() => {
19
+ const vals: string[] = [];
20
+ const walk = (node: any) => {
21
+ Children.forEach(node, child => {
22
+ if (!isValidElement(child)) return;
23
+ const props: any = child.props;
24
+ const type: any = child.type;
25
+ if (type?.displayName === 'Tab' && typeof props?.value === 'string') {
26
+ vals.push(props.value);
27
+ }
28
+ if (props?.children) walk(props.children);
29
+ });
30
+ };
31
+ walk(children);
32
+ return vals;
33
+ }, [children]);
34
+
35
+ const getInitial = () => {
36
+ if (controlledValue !== undefined) return controlledValue;
37
+ if (defaultValue) return defaultValue;
38
+ return tabValues[0];
39
+ };
40
+
41
+ const [uncontrolled, setUncontrolled] = useState<string | undefined>(getInitial);
42
+
43
+ useEffect(() => {
44
+ if (controlledValue !== undefined) setUncontrolled(controlledValue);
45
+ }, [controlledValue]);
46
+
47
+ // Ensure value remains valid if tabs change
48
+ useEffect(() => {
49
+ setUncontrolled(prev => {
50
+ if (!prev) return tabValues[0];
51
+ if (!tabValues.includes(prev)) return tabValues[0];
52
+ return prev;
53
+ });
54
+ }, [tabValues.join('|')]);
55
+
56
+ const currentValue = controlledValue !== undefined ? controlledValue : uncontrolled;
57
+
58
+ const select = useCallback(
59
+ (val: string) => {
60
+ if (disabled) return;
61
+ if (controlledValue === undefined) setUncontrolled(val);
62
+ onValueChange?.(val);
63
+ },
64
+ [controlledValue, disabled, onValueChange]
65
+ );
66
+
67
+ // Indicator shared values (declared early so registerTabLayout can reference)
68
+ const indicatorX = useSharedValue(0);
69
+ const indicatorSize = useSharedValue(0);
70
+
71
+ // Layout registry for animated indicator
72
+ const layoutsRef = useRef<Map<string, { x: number; y: number; width: number; height: number }>>(
73
+ new Map()
74
+ );
75
+ const prevValueRef = useRef<string | undefined>(undefined);
76
+ const initialisedRef = useRef(false);
77
+ const registerTabLayout = useCallback(
78
+ (value: string, layout: { x: number; y: number; width: number; height: number }) => {
79
+ layoutsRef.current.set(value, layout);
80
+ const active = controlledValue !== undefined ? controlledValue : uncontrolled;
81
+ if (!active) return;
82
+ // Initial active tab: seed indicator & prevValue so first change animates
83
+ if (!initialisedRef.current && value === active) {
84
+ indicatorX.value = layout.x;
85
+ indicatorSize.value = layout.width;
86
+ prevValueRef.current = active;
87
+ initialisedRef.current = true;
88
+ }
89
+ },
90
+ [controlledValue, uncontrolled, indicatorX, indicatorSize]
91
+ );
92
+ const getTabLayout = useCallback((v: string) => layoutsRef.current.get(v), []);
93
+
94
+ const contextValue = useMemo(
95
+ () => ({
96
+ value: currentValue,
97
+ size,
98
+ select,
99
+ disabled,
100
+ registerTabLayout,
101
+ getTabLayout,
102
+ indicatorXSV: indicatorX,
103
+ indicatorSizeSV: indicatorSize,
104
+ }),
105
+ [
106
+ currentValue,
107
+ size,
108
+ select,
109
+ disabled,
110
+ registerTabLayout,
111
+ getTabLayout,
112
+ tabValues,
113
+ indicatorX,
114
+ indicatorSize,
115
+ ]
116
+ );
117
+
118
+ // Animate indicator only on value changes after initialisation
119
+ useEffect(() => {
120
+ if (!currentValue || !initialisedRef.current) return;
121
+ if (prevValueRef.current === undefined || prevValueRef.current === currentValue) return;
122
+ const layout = getTabLayout(currentValue);
123
+ if (!layout) return;
124
+ const cfg = { duration: 250, easing: Easing.out(Easing.ease) } as const;
125
+ indicatorX.value = withTiming(layout.x, cfg);
126
+ indicatorSize.value = withTiming(layout.width, cfg);
127
+ prevValueRef.current = currentValue;
128
+ }, [currentValue, getTabLayout, indicatorX, indicatorSize, tabValues.join('|')]);
129
+
130
+ return (
131
+ <TabsContext.Provider value={contextValue}>
132
+ <View {...props}>{children}</View>
133
+ </TabsContext.Provider>
134
+ );
135
+ };
136
+
137
+ Tabs.displayName = 'Tabs';
138
+
139
+ export default Tabs;
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+
4
+ export interface TabsListProps extends ViewProps {
5
+ children: ReactNode;
6
+ }
7
+
8
+ export default TabsListProps;
@@ -0,0 +1,194 @@
1
+ import {
2
+ ChevronLeftSmallIcon,
3
+ ChevronRightSmallIcon,
4
+ } from '@utilitywarehouse/hearth-react-native-icons';
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
6
+ import { LayoutChangeEvent, Platform, View } from 'react-native';
7
+ import Animated, {
8
+ runOnJS,
9
+ useAnimatedScrollHandler,
10
+ useAnimatedStyle,
11
+ useSharedValue,
12
+ } from 'react-native-reanimated';
13
+ import { StyleSheet } from 'react-native-unistyles';
14
+ import { UnstyledIconButton } from '../UnstyledIconButton';
15
+ import { useTabsContext } from './Tabs.context';
16
+ import type TabsListProps from './TabsList.props';
17
+
18
+ const Indicator = Animated.createAnimatedComponent(View);
19
+
20
+ const SCROLL_STEP_RATIO = 0.6;
21
+ const SCROLL_BUTTON_HITSLOP = { top: 10, bottom: 10, left: 10, right: 10 } as const;
22
+
23
+ const TabsList = ({ children, style, ...rest }: TabsListProps) => {
24
+ const { indicatorSizeSV, indicatorXSV } = useTabsContext();
25
+
26
+ const scrollRef = useRef<Animated.ScrollView | null>(null);
27
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
28
+ const [canScrollRight, setCanScrollRight] = useState(false);
29
+ const containerWidthRef = useRef(0);
30
+ const contentWidthRef = useRef(0);
31
+ const scrollX = useSharedValue(0);
32
+
33
+ const indicatorStyle = useAnimatedStyle(() => ({
34
+ transform: [{ translateX: indicatorXSV.value }],
35
+ width: indicatorSizeSV.value,
36
+ }));
37
+
38
+ const updateScrollState = useCallback(() => {
39
+ const cw = containerWidthRef.current;
40
+ const contentW = contentWidthRef.current;
41
+ const x = scrollX.value;
42
+ const overflow = contentW > cw + 1;
43
+ setCanScrollLeft(overflow && x > 0);
44
+ setCanScrollRight(overflow && x + cw < contentW - 1);
45
+ }, [scrollX]);
46
+
47
+ const onLayoutContainer = (e: LayoutChangeEvent) => {
48
+ containerWidthRef.current = e.nativeEvent.layout.width;
49
+ updateScrollState();
50
+ };
51
+ const onContentSizeChange = (w: number) => {
52
+ contentWidthRef.current = w;
53
+ updateScrollState();
54
+ };
55
+ const onScrollHandler = useAnimatedScrollHandler({
56
+ onScroll: (e: { contentOffset: { x: number } }) => {
57
+ scrollX.value = e.contentOffset.x;
58
+ runOnJS(updateScrollState)();
59
+ },
60
+ });
61
+
62
+ const scrollBy = (direction: 1 | -1) => {
63
+ const viewW = containerWidthRef.current;
64
+ const step = viewW * SCROLL_STEP_RATIO;
65
+ const current = scrollX.value;
66
+ const max = Math.max(0, contentWidthRef.current - viewW);
67
+ const target = Math.max(0, Math.min(current + direction * step, max));
68
+ scrollRef.current?.scrollTo({ x: target, animated: true });
69
+ };
70
+
71
+ useEffect(() => {
72
+ if (Platform.OS === 'web') {
73
+ const handler = () => updateScrollState();
74
+ window.addEventListener('resize', handler);
75
+ return () => window.removeEventListener('resize', handler);
76
+ }
77
+ return;
78
+ }, [updateScrollState]);
79
+
80
+ return (
81
+ <View style={[styles.wrapper, style]} {...rest} accessibilityRole="tablist">
82
+ {canScrollLeft && (
83
+ <View
84
+ style={[styles.iconButtonWrap, styles.scrollButtonLeft]}
85
+ accessibilityElementsHidden
86
+ importantForAccessibility="no-hide-descendants"
87
+ {...(Platform.OS === 'web' ? ({ 'aria-hidden': true } as any) : {})}
88
+ >
89
+ <UnstyledIconButton
90
+ accessibilityLabel={undefined}
91
+ icon={ChevronLeftSmallIcon}
92
+ onPress={() => scrollBy(-1)}
93
+ style={styles.iconButton}
94
+ accessibilityElementsHidden
95
+ importantForAccessibility="no-hide-descendants"
96
+ hitSlop={SCROLL_BUTTON_HITSLOP}
97
+ {...(Platform.OS === 'web' ? ({ 'aria-hidden': true, tabIndex: -1 } as any) : {})}
98
+ />
99
+ </View>
100
+ )}
101
+ <Animated.ScrollView
102
+ ref={scrollRef}
103
+ horizontal
104
+ showsHorizontalScrollIndicator={false}
105
+ onLayout={onLayoutContainer}
106
+ onContentSizeChange={onContentSizeChange}
107
+ onScroll={onScrollHandler}
108
+ scrollEventThrottle={16}
109
+ bounces={false}
110
+ contentContainerStyle={styles.scrollContent}
111
+ >
112
+ <View style={styles.container}>
113
+ {children}
114
+ <Indicator
115
+ accessibilityElementsHidden
116
+ importantForAccessibility="no-hide-descendants"
117
+ style={[styles.indicator, indicatorStyle]}
118
+ />
119
+ </View>
120
+ </Animated.ScrollView>
121
+ {canScrollRight && (
122
+ <View
123
+ style={[styles.iconButtonWrap, styles.scrollButtonRight]}
124
+ accessibilityElementsHidden
125
+ importantForAccessibility="no-hide-descendants"
126
+ {...(Platform.OS === 'web' ? ({ 'aria-hidden': true } as any) : {})}
127
+ >
128
+ <UnstyledIconButton
129
+ accessibilityLabel={undefined}
130
+ icon={ChevronRightSmallIcon}
131
+ onPress={() => scrollBy(1)}
132
+ style={styles.iconButton}
133
+ accessibilityElementsHidden
134
+ importantForAccessibility="no-hide-descendants"
135
+ hitSlop={SCROLL_BUTTON_HITSLOP}
136
+ {...(Platform.OS === 'web' ? ({ 'aria-hidden': true, tabIndex: -1 } as any) : {})}
137
+ />
138
+ </View>
139
+ )}
140
+ </View>
141
+ );
142
+ };
143
+
144
+ TabsList.displayName = 'TabsList';
145
+
146
+ const styles = StyleSheet.create(theme => ({
147
+ wrapper: {
148
+ position: 'relative',
149
+ width: '100%',
150
+ borderBottomWidth: theme.components.tabs.divider.borderWidth,
151
+ borderColor: theme.components.tabs.divider.color,
152
+ },
153
+ scrollContent: {
154
+ paddingBottom: 0,
155
+ },
156
+ container: {
157
+ position: 'relative',
158
+ flexDirection: 'row',
159
+ gap: theme.components.tabs.gap,
160
+ },
161
+ indicator: {
162
+ position: 'absolute',
163
+ height: 6,
164
+ borderTopLeftRadius: theme.components.tabs.item.selected.borderTopRadius,
165
+ borderTopRightRadius: theme.components.tabs.item.selected.borderTopRadius,
166
+ borderBottomLeftRadius: theme.components.tabs.item.selected.borderBottomRadius,
167
+ borderBottomRightRadius: theme.components.tabs.item.selected.borderBottomRadius,
168
+ backgroundColor: theme.color.surface.brand.default,
169
+ bottom: 0,
170
+ left: 0,
171
+ },
172
+ scrollButtonLeft: {
173
+ position: 'absolute',
174
+ left: 0,
175
+ top: '50%',
176
+ transform: [{ translateY: -theme.space[150] }],
177
+ zIndex: 10,
178
+ },
179
+ scrollButtonRight: {
180
+ position: 'absolute',
181
+ right: 0,
182
+ top: '50%',
183
+ transform: [{ translateY: -theme.space[150] }],
184
+ zIndex: 10,
185
+ },
186
+ iconButtonWrap: {},
187
+ iconButton: {
188
+ backgroundColor: theme.color.surface.neutral.subtle,
189
+ width: theme.space[300],
190
+ height: theme.space[300],
191
+ },
192
+ }));
193
+
194
+ export default TabsList;
@@ -0,0 +1,8 @@
1
+ export { default as Tab } from './Tab';
2
+ export type { TabProps } from './Tab.props';
3
+ export { default as TabPanel } from './TabPanel';
4
+ export type { TabPanelProps } from './TabPanel.props';
5
+ export { default as Tabs } from './Tabs';
6
+ export type { TabsProps } from './Tabs.props';
7
+ export { default as TabsList } from './TabsList';
8
+ export type { TabsListProps } from './TabsList.props';
@@ -1,5 +1,5 @@
1
1
  import type { ComponentType } from 'react';
2
- import type { PressableProps } from 'react-native';
2
+ import type { PressableProps, ViewProps } from 'react-native';
3
3
 
4
4
  export type UnstyledIconButtonProps = {
5
5
  /*
@@ -29,4 +29,5 @@ export type UnstyledIconButtonProps = {
29
29
  */
30
30
  inverted?: boolean;
31
31
  children?: React.ReactNode;
32
+ iconStyle?: ViewProps['style'];
32
33
  } & PressableProps;
@@ -1,9 +1,10 @@
1
1
  import { createButton } from '@gluestack-ui/button';
2
+ import { ViewStyle } from 'react-native';
3
+ import { useButtonGroupContext } from '../Button/ButtonGroup.context';
2
4
  import { UnstyledIconButtonProps } from './UnstyledIconButton.props';
3
- import UnstyledIconButtonRootComponent from './UnstyledIconButtonRoot';
4
5
  import UnstyledIconButtonIconComponent from './UnstyledIconButtonIcon';
6
+ import UnstyledIconButtonRootComponent from './UnstyledIconButtonRoot';
5
7
  import UnstyledIconButtonSpinerComponent from './UnstyledIconButtonSpinner';
6
- import { useButtonGroupContext } from '../Button/ButtonGroup.context';
7
8
 
8
9
  const UnstyledIconButtonComponent = createButton({
9
10
  Root: UnstyledIconButtonRootComponent,
@@ -25,6 +26,7 @@ const UnstyledIconButton = ({
25
26
  pressed,
26
27
  size = 'md',
27
28
  inverted = false,
29
+ iconStyle,
28
30
  ...props
29
31
  }: UnstyledIconButtonProps) => {
30
32
  const { disabled: groupDisabled, loading: groupLoading } = useButtonGroupContext();
@@ -41,7 +43,11 @@ const UnstyledIconButton = ({
41
43
  isPressed={pressed}
42
44
  icon={icon}
43
45
  >
44
- {loading ? <UnstyledIconButtonSpinner /> : <UnstyledIconButtonIcon as={icon} />}
46
+ {loading ? (
47
+ <UnstyledIconButtonSpinner />
48
+ ) : (
49
+ <UnstyledIconButtonIcon as={icon} style={iconStyle as ViewStyle} />
50
+ )}
45
51
  </UnstyledIconButtonComponent>
46
52
  );
47
53
  };
@@ -9,6 +9,8 @@ export * from './Button';
9
9
  export * from './Card';
10
10
  export * from './Center';
11
11
  export * from './Checkbox';
12
+ export * from './CurrencyInput';
13
+ export * from './DescriptionList';
12
14
  export * from './DetailText';
13
15
  export * from './Divider';
14
16
  export * from './Flex';
@@ -19,6 +21,7 @@ export * from './Helper';
19
21
  export * from './HTMLElements';
20
22
  export * from './Icon';
21
23
  export * from './IconButton';
24
+ export * from './IconContainer';
22
25
  export * from './InlineLink';
23
26
  export * from './Input';
24
27
  export * from './Label';
@@ -32,6 +35,7 @@ export * from './Select';
32
35
  export * from './Skeleton';
33
36
  export * from './Spinner';
34
37
  export * from './Switch';
38
+ export * from './Tabs';
35
39
  export * from './Textarea';
36
40
  export * from './ToggleButtonCard';
37
41