@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.
- package/.cursor/rules +313 -0
- package/.rnstorybook/index.ts +11 -0
- package/.rnstorybook/main.ts +8 -0
- package/.rnstorybook/preview.tsx +14 -0
- package/.rnstorybook/storybook.requires.ts +49 -0
- package/.storybook/main.ts +16 -0
- package/.storybook/preview.ts +32 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/App.tsx +422 -0
- package/README.md +229 -0
- package/app.json +33 -0
- package/assets/adaptive-icon.png +0 -0
- package/assets/favicon.png +0 -0
- package/assets/icon.png +0 -0
- package/assets/splash-icon.png +0 -0
- package/babel.config.js +16 -0
- package/docs/components/CodeBlock.tsx +80 -0
- package/docs/components/PropTable.tsx +93 -0
- package/docs/components/Sidebar.tsx +199 -0
- package/docs/components/index.ts +8 -0
- package/docs/data/colorTokens.ts +70 -0
- package/docs/data/componentData.tsx +1685 -0
- package/docs/data/index.ts +7 -0
- package/docs/index.ts +19 -0
- package/docs/navigation.ts +94 -0
- package/docs/pages/ColorsPage.tsx +226 -0
- package/docs/pages/ComponentPage.tsx +235 -0
- package/docs/pages/InstallationPage.tsx +232 -0
- package/docs/pages/IntroductionPage.tsx +163 -0
- package/docs/pages/ThemingPage.tsx +251 -0
- package/docs/pages/index.ts +10 -0
- package/docs/theme.ts +64 -0
- package/docs/types.ts +54 -0
- package/index.ts +8 -0
- package/llms.txt +1893 -0
- package/mcp-config.example.json +10 -0
- package/mcp-server/README.md +192 -0
- package/mcp-server/package-lock.json +1707 -0
- package/mcp-server/package.json +38 -0
- package/mcp-server/src/index.ts +1136 -0
- package/mcp-server/src/registry/components.ts +1446 -0
- package/mcp-server/src/registry/index.ts +3 -0
- package/mcp-server/src/registry/tokens.ts +256 -0
- package/mcp-server/tsconfig.json +19 -0
- package/package.json +92 -0
- package/src/components/Accordion/Accordion.stories.tsx +226 -0
- package/src/components/Accordion/Accordion.tsx +255 -0
- package/src/components/Accordion/index.ts +12 -0
- package/src/components/ActionSheet/ActionSheet.stories.tsx +393 -0
- package/src/components/ActionSheet/ActionSheet.tsx +258 -0
- package/src/components/ActionSheet/index.ts +2 -0
- package/src/components/Alert/Alert.stories.tsx +165 -0
- package/src/components/Alert/Alert.tsx +164 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/AlertDialog/AlertDialog.stories.tsx +330 -0
- package/src/components/AlertDialog/AlertDialog.tsx +234 -0
- package/src/components/AlertDialog/index.ts +2 -0
- package/src/components/Avatar/Avatar.stories.tsx +154 -0
- package/src/components/Avatar/Avatar.tsx +219 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/Badge/Badge.stories.tsx +146 -0
- package/src/components/Badge/Badge.tsx +125 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Box/Box.stories.tsx +192 -0
- package/src/components/Box/Box.tsx +184 -0
- package/src/components/Box/index.ts +2 -0
- package/src/components/Button/Button.stories.tsx +157 -0
- package/src/components/Button/Button.tsx +180 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Card/Card.stories.tsx +145 -0
- package/src/components/Card/Card.tsx +169 -0
- package/src/components/Card/index.ts +11 -0
- package/src/components/Center/Center.stories.tsx +215 -0
- package/src/components/Center/Center.tsx +29 -0
- package/src/components/Center/index.ts +2 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +94 -0
- package/src/components/Checkbox/Checkbox.tsx +242 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +623 -0
- package/src/components/DatePicker/DatePicker.tsx +1228 -0
- package/src/components/DatePicker/index.ts +8 -0
- package/src/components/Divider/Divider.stories.tsx +224 -0
- package/src/components/Divider/Divider.tsx +73 -0
- package/src/components/Divider/index.ts +2 -0
- package/src/components/Drawer/Drawer.stories.tsx +414 -0
- package/src/components/Drawer/Drawer.tsx +342 -0
- package/src/components/Drawer/index.ts +11 -0
- package/src/components/Fab/Fab.stories.tsx +360 -0
- package/src/components/Fab/Fab.tsx +185 -0
- package/src/components/Fab/index.ts +2 -0
- package/src/components/FormControl/FormControl.stories.tsx +276 -0
- package/src/components/FormControl/FormControl.tsx +185 -0
- package/src/components/FormControl/index.ts +12 -0
- package/src/components/Grid/Grid.stories.tsx +244 -0
- package/src/components/Grid/Grid.tsx +93 -0
- package/src/components/Grid/index.ts +2 -0
- package/src/components/HStack/HStack.stories.tsx +230 -0
- package/src/components/HStack/HStack.tsx +80 -0
- package/src/components/HStack/index.ts +2 -0
- package/src/components/Heading/Heading.stories.tsx +111 -0
- package/src/components/Heading/Heading.tsx +85 -0
- package/src/components/Heading/index.ts +2 -0
- package/src/components/Icon/Icon.stories.tsx +320 -0
- package/src/components/Icon/Icon.tsx +117 -0
- package/src/components/Icon/index.ts +2 -0
- package/src/components/Image/Image.stories.tsx +357 -0
- package/src/components/Image/Image.tsx +168 -0
- package/src/components/Image/index.ts +2 -0
- package/src/components/Input/Input.stories.tsx +164 -0
- package/src/components/Input/Input.tsx +274 -0
- package/src/components/Input/index.ts +2 -0
- package/src/components/Link/Link.stories.tsx +187 -0
- package/src/components/Link/Link.tsx +104 -0
- package/src/components/Link/index.ts +2 -0
- package/src/components/Menu/Menu.stories.tsx +363 -0
- package/src/components/Menu/Menu.tsx +238 -0
- package/src/components/Menu/index.ts +2 -0
- package/src/components/Modal/Modal.stories.tsx +156 -0
- package/src/components/Modal/Modal.tsx +280 -0
- package/src/components/Modal/index.ts +11 -0
- package/src/components/Popover/Popover.stories.tsx +330 -0
- package/src/components/Popover/Popover.tsx +315 -0
- package/src/components/Popover/index.ts +11 -0
- package/src/components/Portal/Portal.stories.tsx +376 -0
- package/src/components/Portal/Portal.tsx +100 -0
- package/src/components/Portal/index.ts +2 -0
- package/src/components/Pressable/Pressable.stories.tsx +338 -0
- package/src/components/Pressable/Pressable.tsx +71 -0
- package/src/components/Pressable/index.ts +2 -0
- package/src/components/Progress/Progress.stories.tsx +131 -0
- package/src/components/Progress/Progress.tsx +219 -0
- package/src/components/Progress/index.ts +2 -0
- package/src/components/Radio/Radio.stories.tsx +101 -0
- package/src/components/Radio/Radio.tsx +234 -0
- package/src/components/Radio/index.ts +2 -0
- package/src/components/Select/Select.stories.tsx +908 -0
- package/src/components/Select/Select.tsx +659 -0
- package/src/components/Select/index.ts +8 -0
- package/src/components/Skeleton/Skeleton.stories.tsx +154 -0
- package/src/components/Skeleton/Skeleton.tsx +192 -0
- package/src/components/Skeleton/index.ts +8 -0
- package/src/components/Slider/Slider.stories.tsx +363 -0
- package/src/components/Slider/Slider.tsx +209 -0
- package/src/components/Slider/index.ts +2 -0
- package/src/components/Spinner/Spinner.stories.tsx +108 -0
- package/src/components/Spinner/Spinner.tsx +121 -0
- package/src/components/Spinner/index.ts +2 -0
- package/src/components/Switch/Switch.stories.tsx +116 -0
- package/src/components/Switch/Switch.tsx +172 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Table/Table.stories.tsx +417 -0
- package/src/components/Table/Table.tsx +233 -0
- package/src/components/Table/index.ts +2 -0
- package/src/components/Text/Text.stories.tsx +93 -0
- package/src/components/Text/Text.tsx +119 -0
- package/src/components/Text/index.ts +2 -0
- package/src/components/Textarea/Textarea.stories.tsx +280 -0
- package/src/components/Textarea/Textarea.tsx +212 -0
- package/src/components/Textarea/index.ts +2 -0
- package/src/components/Toast/Toast.stories.tsx +446 -0
- package/src/components/Toast/Toast.tsx +221 -0
- package/src/components/Toast/index.ts +2 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +354 -0
- package/src/components/Tooltip/Tooltip.tsx +261 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/VStack/VStack.stories.tsx +183 -0
- package/src/components/VStack/VStack.tsx +76 -0
- package/src/components/VStack/index.ts +2 -0
- package/src/components/index.ts +62 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useControllableState.ts +41 -0
- package/src/hooks/useDisclosure.ts +51 -0
- package/src/index.ts +22 -0
- package/src/stories/Button.stories.tsx +53 -0
- package/src/stories/Button.tsx +101 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/Header.stories.tsx +33 -0
- package/src/stories/Header.tsx +75 -0
- package/src/stories/Page.stories.tsx +25 -0
- package/src/stories/Page.tsx +154 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/styles/index.ts +7 -0
- package/src/styles/tokens.ts +318 -0
- package/src/styles/unistyles.ts +254 -0
- package/src/utils/createContext.tsx +25 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/mergeRefs.ts +21 -0
- package/tsconfig.json +26 -0
- package/urbint-cl-1.0.0.tgz +0 -0
- package/vitest.config.ts +37 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { VStack } from './VStack';
|
|
4
|
+
import { Box } from '../Box';
|
|
5
|
+
import { Text } from '../Text';
|
|
6
|
+
import { colors, spacing, borderRadius } from '../../styles/tokens';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Story container with design system tokens
|
|
10
|
+
*/
|
|
11
|
+
const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
12
|
+
<View style={styles.container}>{children}</View>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const meta: Meta<typeof VStack> = {
|
|
16
|
+
title: 'Layout/VStack',
|
|
17
|
+
component: VStack,
|
|
18
|
+
decorators: [
|
|
19
|
+
(Story) => (
|
|
20
|
+
<StoryContainer>
|
|
21
|
+
<Story />
|
|
22
|
+
</StoryContainer>
|
|
23
|
+
),
|
|
24
|
+
],
|
|
25
|
+
argTypes: {
|
|
26
|
+
space: { control: 'number' },
|
|
27
|
+
alignItems: {
|
|
28
|
+
control: 'select',
|
|
29
|
+
options: ['flex-start', 'center', 'flex-end', 'stretch'],
|
|
30
|
+
},
|
|
31
|
+
justifyContent: {
|
|
32
|
+
control: 'select',
|
|
33
|
+
options: ['flex-start', 'center', 'flex-end', 'space-between', 'space-around'],
|
|
34
|
+
},
|
|
35
|
+
reversed: { control: 'boolean' },
|
|
36
|
+
},
|
|
37
|
+
args: {
|
|
38
|
+
space: spacing.md,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default meta;
|
|
43
|
+
|
|
44
|
+
type Story = StoryObj<typeof VStack>;
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {
|
|
47
|
+
render: (args) => (
|
|
48
|
+
<VStack {...args}>
|
|
49
|
+
<Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
|
|
50
|
+
<Text>Item 1</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
<Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
|
|
53
|
+
<Text>Item 2</Text>
|
|
54
|
+
</Box>
|
|
55
|
+
<Box p={spacing.md} bg={colors.feedback.info.background} rounded="md">
|
|
56
|
+
<Text>Item 3</Text>
|
|
57
|
+
</Box>
|
|
58
|
+
</VStack>
|
|
59
|
+
),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Spacing: Story = {
|
|
63
|
+
render: () => (
|
|
64
|
+
<VStack space={spacing.lg}>
|
|
65
|
+
<Text weight="semiBold">Different Spacing Values</Text>
|
|
66
|
+
<VStack space={spacing.xs} style={styles.infoSection}>
|
|
67
|
+
<Text variant="small" color={colors.text.secondary}>space={spacing.xs}</Text>
|
|
68
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>A</Text></Box>
|
|
69
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>B</Text></Box>
|
|
70
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>C</Text></Box>
|
|
71
|
+
</VStack>
|
|
72
|
+
<VStack space={spacing.md} style={styles.infoSection}>
|
|
73
|
+
<Text variant="small" color={colors.text.secondary}>space={spacing.md}</Text>
|
|
74
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>A</Text></Box>
|
|
75
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>B</Text></Box>
|
|
76
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>C</Text></Box>
|
|
77
|
+
</VStack>
|
|
78
|
+
<VStack space={spacing.xl} style={styles.infoSection}>
|
|
79
|
+
<Text variant="small" color={colors.text.secondary}>space={spacing.xl}</Text>
|
|
80
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>A</Text></Box>
|
|
81
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>B</Text></Box>
|
|
82
|
+
<Box p={spacing.sm} bg={colors.feedback.info.border} rounded="sm"><Text>C</Text></Box>
|
|
83
|
+
</VStack>
|
|
84
|
+
</VStack>
|
|
85
|
+
),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const Alignment: Story = {
|
|
89
|
+
render: () => (
|
|
90
|
+
<VStack space={spacing.lg}>
|
|
91
|
+
<Text weight="semiBold">Alignment Options</Text>
|
|
92
|
+
<VStack space={spacing.sm} alignItems="flex-start" style={styles.warningSection}>
|
|
93
|
+
<Text variant="small">alignItems="flex-start"</Text>
|
|
94
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Short</Text></Box>
|
|
95
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Medium Text</Text></Box>
|
|
96
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Longer Text Here</Text></Box>
|
|
97
|
+
</VStack>
|
|
98
|
+
<VStack space={spacing.sm} alignItems="center" style={styles.warningSection}>
|
|
99
|
+
<Text variant="small">alignItems="center"</Text>
|
|
100
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Short</Text></Box>
|
|
101
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Medium Text</Text></Box>
|
|
102
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Longer Text Here</Text></Box>
|
|
103
|
+
</VStack>
|
|
104
|
+
<VStack space={spacing.sm} alignItems="flex-end" style={styles.warningSection}>
|
|
105
|
+
<Text variant="small">alignItems="flex-end"</Text>
|
|
106
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Short</Text></Box>
|
|
107
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Medium Text</Text></Box>
|
|
108
|
+
<Box p={spacing.sm} bg={colors.feedback.warning.border} rounded="sm"><Text>Longer Text Here</Text></Box>
|
|
109
|
+
</VStack>
|
|
110
|
+
</VStack>
|
|
111
|
+
),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const Reversed: Story = {
|
|
115
|
+
render: () => (
|
|
116
|
+
<VStack space={spacing.lg}>
|
|
117
|
+
<Text weight="semiBold">Normal vs Reversed</Text>
|
|
118
|
+
<VStack space={spacing.sm} style={styles.successSection}>
|
|
119
|
+
<Text variant="small">Normal (reversed=false)</Text>
|
|
120
|
+
<Box p={spacing.sm} bg={colors.feedback.success.border} rounded="sm"><Text>First</Text></Box>
|
|
121
|
+
<Box p={spacing.sm} bg={colors.feedback.success.border} rounded="sm"><Text>Second</Text></Box>
|
|
122
|
+
<Box p={spacing.sm} bg={colors.feedback.success.border} rounded="sm"><Text>Third</Text></Box>
|
|
123
|
+
</VStack>
|
|
124
|
+
<VStack space={spacing.sm} reversed style={styles.successSection}>
|
|
125
|
+
<Text variant="small">Reversed (reversed=true)</Text>
|
|
126
|
+
<Box p={spacing.sm} bg={colors.feedback.success.border} rounded="sm"><Text>First</Text></Box>
|
|
127
|
+
<Box p={spacing.sm} bg={colors.feedback.success.border} rounded="sm"><Text>Second</Text></Box>
|
|
128
|
+
<Box p={spacing.sm} bg={colors.feedback.success.border} rounded="sm"><Text>Third</Text></Box>
|
|
129
|
+
</VStack>
|
|
130
|
+
</VStack>
|
|
131
|
+
),
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const NestedStacks: Story = {
|
|
135
|
+
render: () => (
|
|
136
|
+
<VStack space={spacing.md}>
|
|
137
|
+
<Text weight="semiBold">Nested VStacks</Text>
|
|
138
|
+
<Box p={spacing.lg} bg={colors.background.tertiary} rounded="lg">
|
|
139
|
+
<VStack space={spacing.lg}>
|
|
140
|
+
<VStack space={spacing.xs}>
|
|
141
|
+
<Text weight="semiBold">Section 1</Text>
|
|
142
|
+
<Text variant="small" color={colors.text.secondary}>Description for section 1</Text>
|
|
143
|
+
</VStack>
|
|
144
|
+
<VStack space={spacing.xs}>
|
|
145
|
+
<Text weight="semiBold">Section 2</Text>
|
|
146
|
+
<Text variant="small" color={colors.text.secondary}>Description for section 2</Text>
|
|
147
|
+
</VStack>
|
|
148
|
+
<VStack space={spacing.xs}>
|
|
149
|
+
<Text weight="semiBold">Section 3</Text>
|
|
150
|
+
<Text variant="small" color={colors.text.secondary}>Description for section 3</Text>
|
|
151
|
+
</VStack>
|
|
152
|
+
</VStack>
|
|
153
|
+
</Box>
|
|
154
|
+
</VStack>
|
|
155
|
+
),
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Styles using design system tokens
|
|
160
|
+
*/
|
|
161
|
+
const styles = StyleSheet.create({
|
|
162
|
+
container: {
|
|
163
|
+
padding: spacing.lg,
|
|
164
|
+
backgroundColor: colors.background.default,
|
|
165
|
+
borderRadius: borderRadius.lg,
|
|
166
|
+
},
|
|
167
|
+
infoSection: {
|
|
168
|
+
backgroundColor: colors.feedback.info.background,
|
|
169
|
+
padding: spacing.md,
|
|
170
|
+
borderRadius: borderRadius.lg,
|
|
171
|
+
},
|
|
172
|
+
warningSection: {
|
|
173
|
+
backgroundColor: colors.feedback.warning.background,
|
|
174
|
+
padding: spacing.md,
|
|
175
|
+
borderRadius: borderRadius.lg,
|
|
176
|
+
},
|
|
177
|
+
successSection: {
|
|
178
|
+
backgroundColor: colors.feedback.success.background,
|
|
179
|
+
padding: spacing.md,
|
|
180
|
+
borderRadius: borderRadius.lg,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VStack Component
|
|
3
|
+
* Vertical stack - arranges children in a column
|
|
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 VStackProps 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
|
+
/** Reverse direction */
|
|
33
|
+
reversed?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const VStack = forwardRef<View, VStackProps>(
|
|
37
|
+
(
|
|
38
|
+
{
|
|
39
|
+
style,
|
|
40
|
+
space = 'none',
|
|
41
|
+
p,
|
|
42
|
+
px,
|
|
43
|
+
py,
|
|
44
|
+
alignItems,
|
|
45
|
+
justifyContent,
|
|
46
|
+
reversed = false,
|
|
47
|
+
children,
|
|
48
|
+
...props
|
|
49
|
+
},
|
|
50
|
+
ref
|
|
51
|
+
) => {
|
|
52
|
+
return (
|
|
53
|
+
<View
|
|
54
|
+
ref={ref}
|
|
55
|
+
style={[
|
|
56
|
+
{
|
|
57
|
+
flexDirection: reversed ? 'column-reverse' : 'column',
|
|
58
|
+
gap: resolveSpacing(space),
|
|
59
|
+
alignItems,
|
|
60
|
+
justifyContent,
|
|
61
|
+
...(p !== undefined && { padding: resolveSpacing(p) }),
|
|
62
|
+
...(px !== undefined && { paddingHorizontal: resolveSpacing(px) }),
|
|
63
|
+
...(py !== undefined && { paddingVertical: resolveSpacing(py) }),
|
|
64
|
+
},
|
|
65
|
+
style,
|
|
66
|
+
]}
|
|
67
|
+
{...props}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
VStack.displayName = 'VStack';
|
|
76
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Urbint Component Library
|
|
3
|
+
* Enterprise-ready React Native component library
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Layout Components
|
|
7
|
+
export * from './Box';
|
|
8
|
+
export * from './Center';
|
|
9
|
+
export * from './HStack';
|
|
10
|
+
export * from './VStack';
|
|
11
|
+
export * from './Grid';
|
|
12
|
+
export * from './Divider';
|
|
13
|
+
|
|
14
|
+
// Typography Components
|
|
15
|
+
export * from './Text';
|
|
16
|
+
export * from './Heading';
|
|
17
|
+
export * from './Link';
|
|
18
|
+
|
|
19
|
+
// Form Components
|
|
20
|
+
export * from './Input';
|
|
21
|
+
export * from './Textarea';
|
|
22
|
+
export * from './Checkbox';
|
|
23
|
+
export * from './Radio';
|
|
24
|
+
export * from './Select';
|
|
25
|
+
export * from './Switch';
|
|
26
|
+
export * from './Slider';
|
|
27
|
+
export * from './FormControl';
|
|
28
|
+
export * from './DatePicker';
|
|
29
|
+
|
|
30
|
+
// Feedback Components
|
|
31
|
+
export * from './Alert';
|
|
32
|
+
export * from './Toast';
|
|
33
|
+
export * from './Progress';
|
|
34
|
+
export * from './Spinner';
|
|
35
|
+
export * from './Skeleton';
|
|
36
|
+
|
|
37
|
+
// Overlay Components
|
|
38
|
+
export * from './Modal';
|
|
39
|
+
export * from './Drawer';
|
|
40
|
+
export * from './AlertDialog';
|
|
41
|
+
export * from './ActionSheet';
|
|
42
|
+
export * from './Popover';
|
|
43
|
+
export * from './Menu';
|
|
44
|
+
export * from './Tooltip';
|
|
45
|
+
|
|
46
|
+
// Data Display Components
|
|
47
|
+
export * from './Avatar';
|
|
48
|
+
export * from './Badge';
|
|
49
|
+
export * from './Card';
|
|
50
|
+
export * from './Table';
|
|
51
|
+
export * from './Image';
|
|
52
|
+
export * from './Icon';
|
|
53
|
+
|
|
54
|
+
// Action Components
|
|
55
|
+
export * from './Button';
|
|
56
|
+
export * from './Pressable';
|
|
57
|
+
export * from './Fab';
|
|
58
|
+
export * from './Accordion';
|
|
59
|
+
|
|
60
|
+
// Utility Components
|
|
61
|
+
export * from './Portal';
|
|
62
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useControllableState Hook
|
|
3
|
+
* Manages controlled and uncontrolled state
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
7
|
+
|
|
8
|
+
interface UseControllableStateProps<T> {
|
|
9
|
+
value?: T;
|
|
10
|
+
defaultValue?: T;
|
|
11
|
+
onChange?: (value: T) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useControllableState<T>({
|
|
15
|
+
value: controlledValue,
|
|
16
|
+
defaultValue,
|
|
17
|
+
onChange,
|
|
18
|
+
}: UseControllableStateProps<T>) {
|
|
19
|
+
const [uncontrolledValue, setUncontrolledValue] = useState<T | undefined>(defaultValue);
|
|
20
|
+
const isControlled = controlledValue !== undefined;
|
|
21
|
+
const value = isControlled ? controlledValue : uncontrolledValue;
|
|
22
|
+
|
|
23
|
+
const onChangeRef = useRef(onChange);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
onChangeRef.current = onChange;
|
|
26
|
+
}, [onChange]);
|
|
27
|
+
|
|
28
|
+
const setValue = useCallback((nextValue: T | ((prev: T | undefined) => T)) => {
|
|
29
|
+
const resolvedValue = typeof nextValue === 'function'
|
|
30
|
+
? (nextValue as (prev: T | undefined) => T)(value)
|
|
31
|
+
: nextValue;
|
|
32
|
+
|
|
33
|
+
if (!isControlled) {
|
|
34
|
+
setUncontrolledValue(resolvedValue);
|
|
35
|
+
}
|
|
36
|
+
onChangeRef.current?.(resolvedValue);
|
|
37
|
+
}, [isControlled, value]);
|
|
38
|
+
|
|
39
|
+
return [value, setValue] as const;
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useDisclosure Hook
|
|
3
|
+
* Manages open/close state for modals, drawers, etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
interface UseDisclosureProps {
|
|
9
|
+
defaultIsOpen?: boolean;
|
|
10
|
+
isOpen?: boolean;
|
|
11
|
+
onOpen?: () => void;
|
|
12
|
+
onClose?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useDisclosure(props: UseDisclosureProps = {}) {
|
|
16
|
+
const { defaultIsOpen = false, isOpen: controlledIsOpen, onOpen: onOpenProp, onClose: onCloseProp } = props;
|
|
17
|
+
|
|
18
|
+
const [isOpenState, setIsOpenState] = useState(defaultIsOpen);
|
|
19
|
+
const isControlled = controlledIsOpen !== undefined;
|
|
20
|
+
const isOpen = isControlled ? controlledIsOpen : isOpenState;
|
|
21
|
+
|
|
22
|
+
const onOpen = useCallback(() => {
|
|
23
|
+
if (!isControlled) {
|
|
24
|
+
setIsOpenState(true);
|
|
25
|
+
}
|
|
26
|
+
onOpenProp?.();
|
|
27
|
+
}, [isControlled, onOpenProp]);
|
|
28
|
+
|
|
29
|
+
const onClose = useCallback(() => {
|
|
30
|
+
if (!isControlled) {
|
|
31
|
+
setIsOpenState(false);
|
|
32
|
+
}
|
|
33
|
+
onCloseProp?.();
|
|
34
|
+
}, [isControlled, onCloseProp]);
|
|
35
|
+
|
|
36
|
+
const onToggle = useCallback(() => {
|
|
37
|
+
if (isOpen) {
|
|
38
|
+
onClose();
|
|
39
|
+
} else {
|
|
40
|
+
onOpen();
|
|
41
|
+
}
|
|
42
|
+
}, [isOpen, onOpen, onClose]);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
isOpen,
|
|
46
|
+
onOpen,
|
|
47
|
+
onClose,
|
|
48
|
+
onToggle,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Urbint Component Library
|
|
3
|
+
* Enterprise-ready React Native component library built with Unistyles
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Import styles configuration (must be imported before any components)
|
|
9
|
+
import './styles/unistyles';
|
|
10
|
+
|
|
11
|
+
// Export all components
|
|
12
|
+
export * from './components';
|
|
13
|
+
|
|
14
|
+
// Export styles and tokens
|
|
15
|
+
export * from './styles';
|
|
16
|
+
|
|
17
|
+
// Export hooks
|
|
18
|
+
export * from './hooks';
|
|
19
|
+
|
|
20
|
+
// Export utilities
|
|
21
|
+
export * from './utils';
|
|
22
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
|
|
2
|
+
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
import { fn } from 'storybook/test';
|
|
5
|
+
|
|
6
|
+
import { Button } from './Button';
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: 'Example/Button',
|
|
10
|
+
component: Button,
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story) => (
|
|
13
|
+
<View style={{ flex: 1, alignItems: 'flex-start' }}>
|
|
14
|
+
<Story />
|
|
15
|
+
</View>
|
|
16
|
+
),
|
|
17
|
+
],
|
|
18
|
+
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
|
|
19
|
+
tags: ['autodocs'],
|
|
20
|
+
// Use `fn` to spy on the onPress arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args
|
|
21
|
+
args: { onPress: fn() },
|
|
22
|
+
} satisfies Meta<typeof Button>;
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
|
|
26
|
+
type Story = StoryObj<typeof meta>;
|
|
27
|
+
|
|
28
|
+
export const Primary: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
primary: true,
|
|
31
|
+
label: 'Button',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const Secondary: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
label: 'Button',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Large: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
size: 'large',
|
|
44
|
+
label: 'Button',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Small: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
size: 'small',
|
|
51
|
+
label: 'Button',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface ButtonProps {
|
|
5
|
+
/** Is this the principal call to action on the page? */
|
|
6
|
+
primary?: boolean;
|
|
7
|
+
/** What background color to use */
|
|
8
|
+
backgroundColor?: string;
|
|
9
|
+
/** How large should the button be? */
|
|
10
|
+
size?: 'small' | 'medium' | 'large';
|
|
11
|
+
/** Button contents */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Optional click handler */
|
|
14
|
+
onPress?: () => void;
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Primary UI component for user interaction */
|
|
19
|
+
export const Button = ({
|
|
20
|
+
primary = false,
|
|
21
|
+
size = 'medium',
|
|
22
|
+
backgroundColor,
|
|
23
|
+
label,
|
|
24
|
+
style,
|
|
25
|
+
onPress,
|
|
26
|
+
}: ButtonProps) => {
|
|
27
|
+
const modeStyle = primary ? styles.primary : styles.secondary;
|
|
28
|
+
const textModeStyle = primary ? styles.primaryText : styles.secondaryText;
|
|
29
|
+
|
|
30
|
+
const sizeStyle = styles[size];
|
|
31
|
+
const textSizeStyle = textSizeStyles[size];
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<TouchableOpacity accessibilityRole="button" activeOpacity={0.6} onPress={onPress}>
|
|
35
|
+
<View
|
|
36
|
+
style={[
|
|
37
|
+
styles.button,
|
|
38
|
+
modeStyle,
|
|
39
|
+
sizeStyle,
|
|
40
|
+
style,
|
|
41
|
+
!!backgroundColor && { backgroundColor },
|
|
42
|
+
{ borderColor: 'black' },
|
|
43
|
+
]}
|
|
44
|
+
>
|
|
45
|
+
<Text style={[textModeStyle, textSizeStyle]}>{label}</Text>
|
|
46
|
+
</View>
|
|
47
|
+
</TouchableOpacity>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const styles = StyleSheet.create({
|
|
52
|
+
button: {
|
|
53
|
+
borderWidth: 0,
|
|
54
|
+
borderRadius: 48,
|
|
55
|
+
},
|
|
56
|
+
buttonText: {
|
|
57
|
+
fontWeight: '700',
|
|
58
|
+
lineHeight: 1,
|
|
59
|
+
},
|
|
60
|
+
primary: {
|
|
61
|
+
backgroundColor: '#1ea7fd',
|
|
62
|
+
},
|
|
63
|
+
primaryText: {
|
|
64
|
+
color: 'white',
|
|
65
|
+
},
|
|
66
|
+
secondary: {
|
|
67
|
+
backgroundColor: 'transparent',
|
|
68
|
+
borderColor: 'rgba(0, 0, 0, 0.15)',
|
|
69
|
+
borderWidth: 1,
|
|
70
|
+
},
|
|
71
|
+
secondaryText: {
|
|
72
|
+
color: '#333',
|
|
73
|
+
},
|
|
74
|
+
small: {
|
|
75
|
+
paddingVertical: 10,
|
|
76
|
+
paddingHorizontal: 16,
|
|
77
|
+
},
|
|
78
|
+
smallText: {
|
|
79
|
+
fontSize: 12,
|
|
80
|
+
},
|
|
81
|
+
medium: {
|
|
82
|
+
paddingVertical: 11,
|
|
83
|
+
paddingHorizontal: 20,
|
|
84
|
+
},
|
|
85
|
+
mediumText: {
|
|
86
|
+
fontSize: 14,
|
|
87
|
+
},
|
|
88
|
+
large: {
|
|
89
|
+
paddingVertical: 12,
|
|
90
|
+
paddingHorizontal: 24,
|
|
91
|
+
},
|
|
92
|
+
largeText: {
|
|
93
|
+
fontSize: 16,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const textSizeStyles = {
|
|
98
|
+
small: styles.smallText,
|
|
99
|
+
medium: styles.mediumText,
|
|
100
|
+
large: styles.largeText,
|
|
101
|
+
};
|