@urbint/cl 1.0.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 (206) hide show
  1. package/.cursor/rules +313 -0
  2. package/.rnstorybook/index.ts +11 -0
  3. package/.rnstorybook/main.ts +8 -0
  4. package/.rnstorybook/preview.tsx +14 -0
  5. package/.rnstorybook/storybook.requires.ts +49 -0
  6. package/.storybook/main.ts +16 -0
  7. package/.storybook/preview.ts +32 -0
  8. package/.storybook/vitest.setup.ts +7 -0
  9. package/App.tsx +422 -0
  10. package/README.md +229 -0
  11. package/app.json +33 -0
  12. package/assets/adaptive-icon.png +0 -0
  13. package/assets/favicon.png +0 -0
  14. package/assets/icon.png +0 -0
  15. package/assets/splash-icon.png +0 -0
  16. package/babel.config.js +16 -0
  17. package/docs/components/CodeBlock.tsx +80 -0
  18. package/docs/components/PropTable.tsx +93 -0
  19. package/docs/components/Sidebar.tsx +199 -0
  20. package/docs/components/index.ts +8 -0
  21. package/docs/data/colorTokens.ts +70 -0
  22. package/docs/data/componentData.tsx +1685 -0
  23. package/docs/data/index.ts +7 -0
  24. package/docs/index.ts +19 -0
  25. package/docs/navigation.ts +94 -0
  26. package/docs/pages/ColorsPage.tsx +226 -0
  27. package/docs/pages/ComponentPage.tsx +235 -0
  28. package/docs/pages/InstallationPage.tsx +232 -0
  29. package/docs/pages/IntroductionPage.tsx +163 -0
  30. package/docs/pages/ThemingPage.tsx +251 -0
  31. package/docs/pages/index.ts +10 -0
  32. package/docs/theme.ts +64 -0
  33. package/docs/types.ts +54 -0
  34. package/index.ts +8 -0
  35. package/llms.txt +1893 -0
  36. package/mcp-config.example.json +10 -0
  37. package/mcp-server/README.md +192 -0
  38. package/mcp-server/package-lock.json +1707 -0
  39. package/mcp-server/package.json +38 -0
  40. package/mcp-server/src/index.ts +1136 -0
  41. package/mcp-server/src/registry/components.ts +1446 -0
  42. package/mcp-server/src/registry/index.ts +3 -0
  43. package/mcp-server/src/registry/tokens.ts +256 -0
  44. package/mcp-server/tsconfig.json +19 -0
  45. package/package.json +92 -0
  46. package/src/components/Accordion/Accordion.stories.tsx +226 -0
  47. package/src/components/Accordion/Accordion.tsx +255 -0
  48. package/src/components/Accordion/index.ts +12 -0
  49. package/src/components/ActionSheet/ActionSheet.stories.tsx +393 -0
  50. package/src/components/ActionSheet/ActionSheet.tsx +258 -0
  51. package/src/components/ActionSheet/index.ts +2 -0
  52. package/src/components/Alert/Alert.stories.tsx +165 -0
  53. package/src/components/Alert/Alert.tsx +164 -0
  54. package/src/components/Alert/index.ts +2 -0
  55. package/src/components/AlertDialog/AlertDialog.stories.tsx +330 -0
  56. package/src/components/AlertDialog/AlertDialog.tsx +234 -0
  57. package/src/components/AlertDialog/index.ts +2 -0
  58. package/src/components/Avatar/Avatar.stories.tsx +154 -0
  59. package/src/components/Avatar/Avatar.tsx +219 -0
  60. package/src/components/Avatar/index.ts +2 -0
  61. package/src/components/Badge/Badge.stories.tsx +146 -0
  62. package/src/components/Badge/Badge.tsx +125 -0
  63. package/src/components/Badge/index.ts +2 -0
  64. package/src/components/Box/Box.stories.tsx +192 -0
  65. package/src/components/Box/Box.tsx +184 -0
  66. package/src/components/Box/index.ts +2 -0
  67. package/src/components/Button/Button.stories.tsx +157 -0
  68. package/src/components/Button/Button.tsx +180 -0
  69. package/src/components/Button/index.ts +2 -0
  70. package/src/components/Card/Card.stories.tsx +145 -0
  71. package/src/components/Card/Card.tsx +169 -0
  72. package/src/components/Card/index.ts +11 -0
  73. package/src/components/Center/Center.stories.tsx +215 -0
  74. package/src/components/Center/Center.tsx +29 -0
  75. package/src/components/Center/index.ts +2 -0
  76. package/src/components/Checkbox/Checkbox.stories.tsx +94 -0
  77. package/src/components/Checkbox/Checkbox.tsx +242 -0
  78. package/src/components/Checkbox/index.ts +2 -0
  79. package/src/components/DatePicker/DatePicker.stories.tsx +623 -0
  80. package/src/components/DatePicker/DatePicker.tsx +1228 -0
  81. package/src/components/DatePicker/index.ts +8 -0
  82. package/src/components/Divider/Divider.stories.tsx +224 -0
  83. package/src/components/Divider/Divider.tsx +73 -0
  84. package/src/components/Divider/index.ts +2 -0
  85. package/src/components/Drawer/Drawer.stories.tsx +414 -0
  86. package/src/components/Drawer/Drawer.tsx +342 -0
  87. package/src/components/Drawer/index.ts +11 -0
  88. package/src/components/Fab/Fab.stories.tsx +360 -0
  89. package/src/components/Fab/Fab.tsx +185 -0
  90. package/src/components/Fab/index.ts +2 -0
  91. package/src/components/FormControl/FormControl.stories.tsx +276 -0
  92. package/src/components/FormControl/FormControl.tsx +185 -0
  93. package/src/components/FormControl/index.ts +12 -0
  94. package/src/components/Grid/Grid.stories.tsx +244 -0
  95. package/src/components/Grid/Grid.tsx +93 -0
  96. package/src/components/Grid/index.ts +2 -0
  97. package/src/components/HStack/HStack.stories.tsx +230 -0
  98. package/src/components/HStack/HStack.tsx +80 -0
  99. package/src/components/HStack/index.ts +2 -0
  100. package/src/components/Heading/Heading.stories.tsx +111 -0
  101. package/src/components/Heading/Heading.tsx +85 -0
  102. package/src/components/Heading/index.ts +2 -0
  103. package/src/components/Icon/Icon.stories.tsx +320 -0
  104. package/src/components/Icon/Icon.tsx +117 -0
  105. package/src/components/Icon/index.ts +2 -0
  106. package/src/components/Image/Image.stories.tsx +357 -0
  107. package/src/components/Image/Image.tsx +168 -0
  108. package/src/components/Image/index.ts +2 -0
  109. package/src/components/Input/Input.stories.tsx +164 -0
  110. package/src/components/Input/Input.tsx +274 -0
  111. package/src/components/Input/index.ts +2 -0
  112. package/src/components/Link/Link.stories.tsx +187 -0
  113. package/src/components/Link/Link.tsx +104 -0
  114. package/src/components/Link/index.ts +2 -0
  115. package/src/components/Menu/Menu.stories.tsx +363 -0
  116. package/src/components/Menu/Menu.tsx +238 -0
  117. package/src/components/Menu/index.ts +2 -0
  118. package/src/components/Modal/Modal.stories.tsx +156 -0
  119. package/src/components/Modal/Modal.tsx +280 -0
  120. package/src/components/Modal/index.ts +11 -0
  121. package/src/components/Popover/Popover.stories.tsx +330 -0
  122. package/src/components/Popover/Popover.tsx +315 -0
  123. package/src/components/Popover/index.ts +11 -0
  124. package/src/components/Portal/Portal.stories.tsx +376 -0
  125. package/src/components/Portal/Portal.tsx +100 -0
  126. package/src/components/Portal/index.ts +2 -0
  127. package/src/components/Pressable/Pressable.stories.tsx +338 -0
  128. package/src/components/Pressable/Pressable.tsx +71 -0
  129. package/src/components/Pressable/index.ts +2 -0
  130. package/src/components/Progress/Progress.stories.tsx +131 -0
  131. package/src/components/Progress/Progress.tsx +219 -0
  132. package/src/components/Progress/index.ts +2 -0
  133. package/src/components/Radio/Radio.stories.tsx +101 -0
  134. package/src/components/Radio/Radio.tsx +234 -0
  135. package/src/components/Radio/index.ts +2 -0
  136. package/src/components/Select/Select.stories.tsx +908 -0
  137. package/src/components/Select/Select.tsx +659 -0
  138. package/src/components/Select/index.ts +8 -0
  139. package/src/components/Skeleton/Skeleton.stories.tsx +154 -0
  140. package/src/components/Skeleton/Skeleton.tsx +192 -0
  141. package/src/components/Skeleton/index.ts +8 -0
  142. package/src/components/Slider/Slider.stories.tsx +363 -0
  143. package/src/components/Slider/Slider.tsx +209 -0
  144. package/src/components/Slider/index.ts +2 -0
  145. package/src/components/Spinner/Spinner.stories.tsx +108 -0
  146. package/src/components/Spinner/Spinner.tsx +121 -0
  147. package/src/components/Spinner/index.ts +2 -0
  148. package/src/components/Switch/Switch.stories.tsx +116 -0
  149. package/src/components/Switch/Switch.tsx +172 -0
  150. package/src/components/Switch/index.ts +2 -0
  151. package/src/components/Table/Table.stories.tsx +417 -0
  152. package/src/components/Table/Table.tsx +233 -0
  153. package/src/components/Table/index.ts +2 -0
  154. package/src/components/Text/Text.stories.tsx +93 -0
  155. package/src/components/Text/Text.tsx +119 -0
  156. package/src/components/Text/index.ts +2 -0
  157. package/src/components/Textarea/Textarea.stories.tsx +280 -0
  158. package/src/components/Textarea/Textarea.tsx +212 -0
  159. package/src/components/Textarea/index.ts +2 -0
  160. package/src/components/Toast/Toast.stories.tsx +446 -0
  161. package/src/components/Toast/Toast.tsx +221 -0
  162. package/src/components/Toast/index.ts +2 -0
  163. package/src/components/Tooltip/Tooltip.stories.tsx +354 -0
  164. package/src/components/Tooltip/Tooltip.tsx +261 -0
  165. package/src/components/Tooltip/index.ts +2 -0
  166. package/src/components/VStack/VStack.stories.tsx +183 -0
  167. package/src/components/VStack/VStack.tsx +76 -0
  168. package/src/components/VStack/index.ts +2 -0
  169. package/src/components/index.ts +62 -0
  170. package/src/hooks/index.ts +7 -0
  171. package/src/hooks/useControllableState.ts +41 -0
  172. package/src/hooks/useDisclosure.ts +51 -0
  173. package/src/index.ts +22 -0
  174. package/src/stories/Button.stories.tsx +53 -0
  175. package/src/stories/Button.tsx +101 -0
  176. package/src/stories/Configure.mdx +364 -0
  177. package/src/stories/Header.stories.tsx +33 -0
  178. package/src/stories/Header.tsx +75 -0
  179. package/src/stories/Page.stories.tsx +25 -0
  180. package/src/stories/Page.tsx +154 -0
  181. package/src/stories/assets/accessibility.png +0 -0
  182. package/src/stories/assets/accessibility.svg +1 -0
  183. package/src/stories/assets/addon-library.png +0 -0
  184. package/src/stories/assets/assets.png +0 -0
  185. package/src/stories/assets/avif-test-image.avif +0 -0
  186. package/src/stories/assets/context.png +0 -0
  187. package/src/stories/assets/discord.svg +1 -0
  188. package/src/stories/assets/docs.png +0 -0
  189. package/src/stories/assets/figma-plugin.png +0 -0
  190. package/src/stories/assets/github.svg +1 -0
  191. package/src/stories/assets/share.png +0 -0
  192. package/src/stories/assets/styling.png +0 -0
  193. package/src/stories/assets/testing.png +0 -0
  194. package/src/stories/assets/theming.png +0 -0
  195. package/src/stories/assets/tutorials.svg +1 -0
  196. package/src/stories/assets/youtube.svg +1 -0
  197. package/src/styles/index.ts +7 -0
  198. package/src/styles/tokens.ts +318 -0
  199. package/src/styles/unistyles.ts +254 -0
  200. package/src/utils/createContext.tsx +25 -0
  201. package/src/utils/index.ts +7 -0
  202. package/src/utils/mergeRefs.ts +21 -0
  203. package/tsconfig.json +26 -0
  204. package/urbint-cl-1.0.0.tgz +0 -0
  205. package/vitest.config.ts +37 -0
  206. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,244 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { Grid, GridItem } from './Grid';
4
+ import { VStack } from '../VStack';
5
+ import { Box } from '../Box';
6
+ import { Text } from '../Text';
7
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
8
+
9
+ /**
10
+ * Story container with design system tokens
11
+ */
12
+ const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
13
+ <View style={styles.container}>{children}</View>
14
+ );
15
+
16
+ const meta: Meta<typeof Grid> = {
17
+ title: 'Layout/Grid',
18
+ component: Grid,
19
+ decorators: [
20
+ (Story) => (
21
+ <StoryContainer>
22
+ <Story />
23
+ </StoryContainer>
24
+ ),
25
+ ],
26
+ argTypes: {
27
+ columns: { control: 'number' },
28
+ gap: { control: 'number' },
29
+ rowGap: { control: 'number' },
30
+ columnGap: { control: 'number' },
31
+ },
32
+ args: {
33
+ columns: 3,
34
+ gap: spacing.md,
35
+ },
36
+ };
37
+
38
+ export default meta;
39
+
40
+ type Story = StoryObj<typeof Grid>;
41
+
42
+ export const Default: Story = {
43
+ render: (args) => (
44
+ <Grid {...args}>
45
+ {[1, 2, 3, 4, 5, 6].map((i) => (
46
+ <GridItem key={i}>
47
+ <Box p={spacing.lg} bg={colors.feedback.info.background} rounded="md" alignItems="center">
48
+ <Text>Item {i}</Text>
49
+ </Box>
50
+ </GridItem>
51
+ ))}
52
+ </Grid>
53
+ ),
54
+ };
55
+
56
+ export const TwoColumns: Story = {
57
+ render: () => (
58
+ <VStack space={spacing.lg}>
59
+ <Text weight="semiBold">2 Column Grid</Text>
60
+ <Grid columns={2} gap={spacing.md}>
61
+ {[1, 2, 3, 4].map((i) => (
62
+ <GridItem key={i}>
63
+ <Box p={spacing.lg} bg={colors.feedback.info.border} rounded="md" alignItems="center">
64
+ <Text>Item {i}</Text>
65
+ </Box>
66
+ </GridItem>
67
+ ))}
68
+ </Grid>
69
+ </VStack>
70
+ ),
71
+ };
72
+
73
+ export const ThreeColumns: Story = {
74
+ render: () => (
75
+ <VStack space={spacing.lg}>
76
+ <Text weight="semiBold">3 Column Grid</Text>
77
+ <Grid columns={3} gap={spacing.md}>
78
+ {[1, 2, 3, 4, 5, 6].map((i) => (
79
+ <GridItem key={i}>
80
+ <Box p={spacing.lg} bg={colors.brand.blue} rounded="md" alignItems="center">
81
+ <Text color={colors.white}>Item {i}</Text>
82
+ </Box>
83
+ </GridItem>
84
+ ))}
85
+ </Grid>
86
+ </VStack>
87
+ ),
88
+ };
89
+
90
+ export const FourColumns: Story = {
91
+ render: () => (
92
+ <VStack space={spacing.lg}>
93
+ <Text weight="semiBold">4 Column Grid</Text>
94
+ <Grid columns={4} gap={spacing.sm}>
95
+ {[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
96
+ <GridItem key={i}>
97
+ <Box p={spacing.md} bg={colors.brand.navy} rounded="md" alignItems="center">
98
+ <Text color={colors.white}>{i}</Text>
99
+ </Box>
100
+ </GridItem>
101
+ ))}
102
+ </Grid>
103
+ </VStack>
104
+ ),
105
+ };
106
+
107
+ export const ColumnSpan: Story = {
108
+ render: () => (
109
+ <VStack space={spacing.lg}>
110
+ <Text weight="semiBold">Grid with Column Span</Text>
111
+ <Grid columns={4} gap={spacing.md}>
112
+ <GridItem colSpan={2}>
113
+ <Box p={spacing.lg} bg={colors.feedback.error.background} rounded="md" alignItems="center">
114
+ <Text>Span 2</Text>
115
+ </Box>
116
+ </GridItem>
117
+ <GridItem>
118
+ <Box p={spacing.lg} bg={colors.feedback.warning.background} rounded="md" alignItems="center">
119
+ <Text>1</Text>
120
+ </Box>
121
+ </GridItem>
122
+ <GridItem>
123
+ <Box p={spacing.lg} bg={colors.feedback.warning.border} rounded="md" alignItems="center">
124
+ <Text>1</Text>
125
+ </Box>
126
+ </GridItem>
127
+ <GridItem>
128
+ <Box p={spacing.lg} bg={colors.feedback.success.background} rounded="md" alignItems="center">
129
+ <Text>1</Text>
130
+ </Box>
131
+ </GridItem>
132
+ <GridItem colSpan={3}>
133
+ <Box p={spacing.lg} bg={colors.feedback.info.background} rounded="md" alignItems="center">
134
+ <Text>Span 3</Text>
135
+ </Box>
136
+ </GridItem>
137
+ <GridItem colSpan={4}>
138
+ <Box p={spacing.lg} bg={colors.secondary.default} rounded="md" alignItems="center">
139
+ <Text>Span 4 (Full Width)</Text>
140
+ </Box>
141
+ </GridItem>
142
+ </Grid>
143
+ </VStack>
144
+ ),
145
+ };
146
+
147
+ export const DifferentGaps: Story = {
148
+ render: () => (
149
+ <VStack space={spacing.xl}>
150
+ <Text weight="semiBold">Different Gap Sizes</Text>
151
+ <VStack space={spacing.sm}>
152
+ <Text variant="small" color={colors.text.secondary}>gap={spacing.xs}</Text>
153
+ <Grid columns={3} gap={spacing.xs}>
154
+ {[1, 2, 3, 4, 5, 6].map((i) => (
155
+ <GridItem key={i}>
156
+ <Box p={spacing.md} bg={colors.feedback.error.background} rounded="sm" alignItems="center">
157
+ <Text>{i}</Text>
158
+ </Box>
159
+ </GridItem>
160
+ ))}
161
+ </Grid>
162
+ </VStack>
163
+ <VStack space={spacing.sm}>
164
+ <Text variant="small" color={colors.text.secondary}>gap={spacing.lg}</Text>
165
+ <Grid columns={3} gap={spacing.lg}>
166
+ {[1, 2, 3, 4, 5, 6].map((i) => (
167
+ <GridItem key={i}>
168
+ <Box p={spacing.md} bg={colors.feedback.success.background} rounded="sm" alignItems="center">
169
+ <Text>{i}</Text>
170
+ </Box>
171
+ </GridItem>
172
+ ))}
173
+ </Grid>
174
+ </VStack>
175
+ <VStack space={spacing.sm}>
176
+ <Text variant="small" color={colors.text.secondary}>gap={spacing.xl}</Text>
177
+ <Grid columns={3} gap={spacing.xl}>
178
+ {[1, 2, 3, 4, 5, 6].map((i) => (
179
+ <GridItem key={i}>
180
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="sm" alignItems="center">
181
+ <Text>{i}</Text>
182
+ </Box>
183
+ </GridItem>
184
+ ))}
185
+ </Grid>
186
+ </VStack>
187
+ </VStack>
188
+ ),
189
+ };
190
+
191
+ export const AsymmetricGaps: Story = {
192
+ render: () => (
193
+ <VStack space={spacing.lg}>
194
+ <Text weight="semiBold">Different Row and Column Gaps</Text>
195
+ <Text variant="small" color={colors.text.secondary}>rowGap={spacing.xl}, columnGap={spacing.sm}</Text>
196
+ <Grid columns={3} rowGap={spacing.xl} columnGap={spacing.sm}>
197
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => (
198
+ <GridItem key={i}>
199
+ <Box p={spacing.lg} bg={colors.badge.purple} rounded="md" alignItems="center">
200
+ <Text color={colors.white}>Item {i}</Text>
201
+ </Box>
202
+ </GridItem>
203
+ ))}
204
+ </Grid>
205
+ </VStack>
206
+ ),
207
+ };
208
+
209
+ export const CardGrid: Story = {
210
+ render: () => (
211
+ <VStack space={spacing.lg}>
212
+ <Text weight="semiBold">Card Grid Layout</Text>
213
+ <Grid columns={2} gap={spacing.lg}>
214
+ {['Dashboard', 'Analytics', 'Reports', 'Settings'].map((title, i) => (
215
+ <GridItem key={i}>
216
+ <Box p={spacing.xl} bg={colors.background.default} rounded="lg" shadow="10">
217
+ <VStack space={spacing.sm}>
218
+ <Box w={40} h={40} bg={colors.feedback.info.background} rounded="md" alignItems="center" justifyContent="center">
219
+ <Text>📊</Text>
220
+ </Box>
221
+ <Text weight="semiBold">{title}</Text>
222
+ <Text variant="small" color={colors.text.secondary}>
223
+ Click to view details
224
+ </Text>
225
+ </VStack>
226
+ </Box>
227
+ </GridItem>
228
+ ))}
229
+ </Grid>
230
+ </VStack>
231
+ ),
232
+ };
233
+
234
+ /**
235
+ * Styles using design system tokens
236
+ */
237
+ const styles = StyleSheet.create({
238
+ container: {
239
+ padding: spacing.lg,
240
+ backgroundColor: colors.background.default,
241
+ borderRadius: borderRadius.lg,
242
+ },
243
+ });
244
+
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Grid Component
3
+ * CSS-like grid layout for React Native
4
+ */
5
+
6
+ import React, { forwardRef, Children, isValidElement, cloneElement } from 'react';
7
+ import { View, ViewProps, ViewStyle } from 'react-native';
8
+ import { spacing, SpacingToken } from '../../styles/tokens';
9
+
10
+ /** Spacing value can be a token name or raw number */
11
+ type SpacingValue = SpacingToken | number;
12
+
13
+ /** Resolve spacing value to pixels */
14
+ const resolveSpacing = (value: SpacingValue): number => {
15
+ if (typeof value === 'number') return value;
16
+ return spacing[value];
17
+ };
18
+
19
+ export interface GridProps extends ViewProps {
20
+ /** Number of columns */
21
+ columns?: number;
22
+ /** Gap between items - accepts token ('sm', 'md', 'lg') or number */
23
+ gap?: SpacingValue;
24
+ /** Row gap - accepts token or number */
25
+ rowGap?: SpacingValue;
26
+ /** Column gap - accepts token or number */
27
+ columnGap?: SpacingValue;
28
+ }
29
+
30
+ export interface GridItemProps extends ViewProps {
31
+ /** Column span */
32
+ colSpan?: number;
33
+ }
34
+
35
+ export const Grid = forwardRef<View, GridProps>(
36
+ ({ style, columns = 1, gap = 'none', rowGap, columnGap, children, ...props }, ref) => {
37
+ const resolvedGap = resolveSpacing(gap);
38
+ const effectiveRowGap = rowGap !== undefined ? resolveSpacing(rowGap) : resolvedGap;
39
+ const effectiveColumnGap = columnGap !== undefined ? resolveSpacing(columnGap) : resolvedGap;
40
+
41
+ const childArray = Children.toArray(children);
42
+
43
+ return (
44
+ <View
45
+ ref={ref}
46
+ style={[
47
+ {
48
+ flexDirection: 'row',
49
+ flexWrap: 'wrap',
50
+ marginHorizontal: -effectiveColumnGap / 2,
51
+ marginVertical: -effectiveRowGap / 2,
52
+ },
53
+ style,
54
+ ]}
55
+ {...props}
56
+ >
57
+ {childArray.map((child, index) => {
58
+ if (!isValidElement<GridItemProps>(child)) return child;
59
+
60
+ const colSpan = child.props.colSpan || 1;
61
+ const itemWidth = `${(colSpan / columns) * 100}%` as const;
62
+
63
+ return cloneElement(child, {
64
+ key: index,
65
+ style: [
66
+ {
67
+ width: itemWidth as ViewStyle['width'],
68
+ paddingHorizontal: effectiveColumnGap / 2,
69
+ paddingVertical: effectiveRowGap / 2,
70
+ },
71
+ child.props.style,
72
+ ],
73
+ });
74
+ })}
75
+ </View>
76
+ );
77
+ }
78
+ );
79
+
80
+ Grid.displayName = 'Grid';
81
+
82
+ export const GridItem = forwardRef<View, GridItemProps>(
83
+ ({ style, children, ...props }, ref) => {
84
+ return (
85
+ <View ref={ref} style={style} {...props}>
86
+ {children}
87
+ </View>
88
+ );
89
+ }
90
+ );
91
+
92
+ GridItem.displayName = 'GridItem';
93
+
@@ -0,0 +1,2 @@
1
+ export { Grid, GridItem, type GridProps, type GridItemProps } from './Grid';
2
+
@@ -0,0 +1,230 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { HStack } from './HStack';
4
+ import { VStack } from '../VStack';
5
+ import { Box } from '../Box';
6
+ import { Text } from '../Text';
7
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
8
+
9
+ /**
10
+ * Story container with design system tokens
11
+ */
12
+ const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
13
+ <View style={styles.container}>{children}</View>
14
+ );
15
+
16
+ const meta: Meta<typeof HStack> = {
17
+ title: 'Layout/HStack',
18
+ component: HStack,
19
+ decorators: [
20
+ (Story) => (
21
+ <StoryContainer>
22
+ <Story />
23
+ </StoryContainer>
24
+ ),
25
+ ],
26
+ argTypes: {
27
+ space: { control: 'number' },
28
+ alignItems: {
29
+ control: 'select',
30
+ options: ['flex-start', 'center', 'flex-end', 'stretch'],
31
+ },
32
+ justifyContent: {
33
+ control: 'select',
34
+ options: ['flex-start', 'center', 'flex-end', 'space-between', 'space-around'],
35
+ },
36
+ wrap: { control: 'boolean' },
37
+ reversed: { control: 'boolean' },
38
+ },
39
+ args: {
40
+ space: spacing.md,
41
+ },
42
+ };
43
+
44
+ export default meta;
45
+
46
+ type Story = StoryObj<typeof HStack>;
47
+
48
+ export const Default: Story = {
49
+ render: (args) => (
50
+ <HStack {...args}>
51
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
52
+ <Text>Item 1</Text>
53
+ </Box>
54
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
55
+ <Text>Item 2</Text>
56
+ </Box>
57
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
58
+ <Text>Item 3</Text>
59
+ </Box>
60
+ </HStack>
61
+ ),
62
+ };
63
+
64
+ export const Spacing: Story = {
65
+ render: () => (
66
+ <VStack space={spacing.lg}>
67
+ <Text weight="semiBold">Different Spacing Values</Text>
68
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
69
+ <Text variant="small" color={colors.text.secondary}>space={spacing.xs}</Text>
70
+ <HStack space={spacing.xs}>
71
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>A</Text></Box>
72
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>B</Text></Box>
73
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>C</Text></Box>
74
+ </HStack>
75
+ </Box>
76
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
77
+ <Text variant="small" color={colors.text.secondary}>space={spacing.lg}</Text>
78
+ <HStack space={spacing.lg}>
79
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>A</Text></Box>
80
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>B</Text></Box>
81
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>C</Text></Box>
82
+ </HStack>
83
+ </Box>
84
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
85
+ <Text variant="small" color={colors.text.secondary}>space={spacing['2xl']}</Text>
86
+ <HStack space={spacing['2xl']}>
87
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>A</Text></Box>
88
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>B</Text></Box>
89
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>C</Text></Box>
90
+ </HStack>
91
+ </Box>
92
+ </VStack>
93
+ ),
94
+ };
95
+
96
+ export const JustifyContent: Story = {
97
+ render: () => (
98
+ <VStack space={spacing.lg}>
99
+ <Text weight="semiBold">Justify Content Options</Text>
100
+ <Box p={spacing.md} bg={colors.feedback.warning.background} rounded="md">
101
+ <Text variant="small">justifyContent="flex-start"</Text>
102
+ <HStack justifyContent="flex-start" space={spacing.sm}>
103
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>1</Text></Box>
104
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>2</Text></Box>
105
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>3</Text></Box>
106
+ </HStack>
107
+ </Box>
108
+ <Box p={spacing.md} bg={colors.feedback.warning.background} rounded="md">
109
+ <Text variant="small">justifyContent="center"</Text>
110
+ <HStack justifyContent="center" space={spacing.sm}>
111
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>1</Text></Box>
112
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>2</Text></Box>
113
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>3</Text></Box>
114
+ </HStack>
115
+ </Box>
116
+ <Box p={spacing.md} bg={colors.feedback.warning.background} rounded="md">
117
+ <Text variant="small">justifyContent="flex-end"</Text>
118
+ <HStack justifyContent="flex-end" space={spacing.sm}>
119
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>1</Text></Box>
120
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>2</Text></Box>
121
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>3</Text></Box>
122
+ </HStack>
123
+ </Box>
124
+ <Box p={spacing.md} bg={colors.feedback.warning.background} rounded="md">
125
+ <Text variant="small">justifyContent="space-between"</Text>
126
+ <HStack justifyContent="space-between">
127
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>1</Text></Box>
128
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>2</Text></Box>
129
+ <Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>3</Text></Box>
130
+ </HStack>
131
+ </Box>
132
+ </VStack>
133
+ ),
134
+ };
135
+
136
+ export const AlignItems: Story = {
137
+ render: () => (
138
+ <VStack space={spacing.lg}>
139
+ <Text weight="semiBold">Align Items Options</Text>
140
+ <Box p={spacing.md} bg={colors.feedback.success.background} rounded="md">
141
+ <Text variant="small">alignItems="flex-start"</Text>
142
+ <HStack alignItems="flex-start" space={spacing.sm} style={{ height: 80 }}>
143
+ <Box p={spacing.sm} h={30} bg={colors.feedback.success.border} rounded="sm"><Text>Short</Text></Box>
144
+ <Box p={spacing.sm} h={50} bg={colors.feedback.success.border} rounded="sm"><Text>Medium</Text></Box>
145
+ <Box p={spacing.sm} h={70} bg={colors.feedback.success.border} rounded="sm"><Text>Tall</Text></Box>
146
+ </HStack>
147
+ </Box>
148
+ <Box p={spacing.md} bg={colors.feedback.success.background} rounded="md">
149
+ <Text variant="small">alignItems="center"</Text>
150
+ <HStack alignItems="center" space={spacing.sm} style={{ height: 80 }}>
151
+ <Box p={spacing.sm} h={30} bg={colors.feedback.success.border} rounded="sm"><Text>Short</Text></Box>
152
+ <Box p={spacing.sm} h={50} bg={colors.feedback.success.border} rounded="sm"><Text>Medium</Text></Box>
153
+ <Box p={spacing.sm} h={70} bg={colors.feedback.success.border} rounded="sm"><Text>Tall</Text></Box>
154
+ </HStack>
155
+ </Box>
156
+ <Box p={spacing.md} bg={colors.feedback.success.background} rounded="md">
157
+ <Text variant="small">alignItems="flex-end"</Text>
158
+ <HStack alignItems="flex-end" space={spacing.sm} style={{ height: 80 }}>
159
+ <Box p={spacing.sm} h={30} bg={colors.feedback.success.border} rounded="sm"><Text>Short</Text></Box>
160
+ <Box p={spacing.sm} h={50} bg={colors.feedback.success.border} rounded="sm"><Text>Medium</Text></Box>
161
+ <Box p={spacing.sm} h={70} bg={colors.feedback.success.border} rounded="sm"><Text>Tall</Text></Box>
162
+ </HStack>
163
+ </Box>
164
+ </VStack>
165
+ ),
166
+ };
167
+
168
+ export const Wrap: Story = {
169
+ render: () => (
170
+ <VStack space={spacing.lg}>
171
+ <Text weight="semiBold">Wrap Behavior</Text>
172
+ <Box p={spacing.md} bg={colors.feedback.error.background} rounded="md">
173
+ <Text variant="small">wrap=false (default)</Text>
174
+ <HStack space={spacing.sm} wrap={false}>
175
+ {[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
176
+ <Box key={i} p={spacing.sm} bg={colors.feedback.error.border} rounded="sm">
177
+ <Text>Item {i}</Text>
178
+ </Box>
179
+ ))}
180
+ </HStack>
181
+ </Box>
182
+ <Box p={spacing.md} bg={colors.feedback.error.background} rounded="md">
183
+ <Text variant="small">wrap=true</Text>
184
+ <HStack space={spacing.sm} wrap>
185
+ {[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
186
+ <Box key={i} p={spacing.sm} bg={colors.feedback.error.border} rounded="sm">
187
+ <Text>Item {i}</Text>
188
+ </Box>
189
+ ))}
190
+ </HStack>
191
+ </Box>
192
+ </VStack>
193
+ ),
194
+ };
195
+
196
+ export const Reversed: Story = {
197
+ render: () => (
198
+ <VStack space={spacing.lg}>
199
+ <Text weight="semiBold">Reversed Direction</Text>
200
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
201
+ <Text variant="small">Normal (reversed=false)</Text>
202
+ <HStack space={spacing.sm}>
203
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>1st</Text></Box>
204
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>2nd</Text></Box>
205
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>3rd</Text></Box>
206
+ </HStack>
207
+ </Box>
208
+ <Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
209
+ <Text variant="small">Reversed (reversed=true)</Text>
210
+ <HStack space={spacing.sm} reversed>
211
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>1st</Text></Box>
212
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>2nd</Text></Box>
213
+ <Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>3rd</Text></Box>
214
+ </HStack>
215
+ </Box>
216
+ </VStack>
217
+ ),
218
+ };
219
+
220
+ /**
221
+ * Styles using design system tokens
222
+ */
223
+ const styles = StyleSheet.create({
224
+ container: {
225
+ padding: spacing.lg,
226
+ backgroundColor: colors.background.default,
227
+ borderRadius: borderRadius.lg,
228
+ },
229
+ });
230
+
@@ -0,0 +1,80 @@
1
+ /**
2
+ * HStack Component
3
+ * Horizontal stack - arranges children in a row
4
+ */
5
+
6
+ import React, { forwardRef } from 'react';
7
+ import { View, ViewProps, ViewStyle } from 'react-native';
8
+ import { spacing, SpacingToken } from '../../styles/tokens';
9
+
10
+ /** Spacing value can be a token name or raw number */
11
+ type SpacingValue = SpacingToken | number;
12
+
13
+ /** Resolve spacing value to pixels */
14
+ const resolveSpacing = (value: SpacingValue): number => {
15
+ if (typeof value === 'number') return value;
16
+ return spacing[value];
17
+ };
18
+
19
+ export interface HStackProps extends ViewProps {
20
+ /** Space between children - accepts token ('sm', 'md', 'lg') or number */
21
+ space?: SpacingValue;
22
+ /** Padding on all sides - accepts token ('sm', 'md', 'lg') or number */
23
+ p?: SpacingValue;
24
+ /** Horizontal padding - accepts token ('sm', 'md', 'lg') or number */
25
+ px?: SpacingValue;
26
+ /** Vertical padding - accepts token ('sm', 'md', 'lg') or number */
27
+ py?: SpacingValue;
28
+ /** Align items */
29
+ alignItems?: ViewStyle['alignItems'];
30
+ /** Justify content */
31
+ justifyContent?: ViewStyle['justifyContent'];
32
+ /** Wrap children */
33
+ wrap?: boolean;
34
+ /** Reverse direction */
35
+ reversed?: boolean;
36
+ }
37
+
38
+ export const HStack = forwardRef<View, HStackProps>(
39
+ (
40
+ {
41
+ style,
42
+ space = 'none',
43
+ p,
44
+ px,
45
+ py,
46
+ alignItems = 'center',
47
+ justifyContent,
48
+ wrap = false,
49
+ reversed = false,
50
+ children,
51
+ ...props
52
+ },
53
+ ref
54
+ ) => {
55
+ return (
56
+ <View
57
+ ref={ref}
58
+ style={[
59
+ {
60
+ flexDirection: reversed ? 'row-reverse' : 'row',
61
+ gap: resolveSpacing(space),
62
+ alignItems,
63
+ justifyContent,
64
+ flexWrap: wrap ? 'wrap' : 'nowrap',
65
+ ...(p !== undefined && { padding: resolveSpacing(p) }),
66
+ ...(px !== undefined && { paddingHorizontal: resolveSpacing(px) }),
67
+ ...(py !== undefined && { paddingVertical: resolveSpacing(py) }),
68
+ },
69
+ style,
70
+ ]}
71
+ {...props}
72
+ >
73
+ {children}
74
+ </View>
75
+ );
76
+ }
77
+ );
78
+
79
+ HStack.displayName = 'HStack';
80
+
@@ -0,0 +1,2 @@
1
+ export { HStack, type HStackProps } from './HStack';
2
+