@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,357 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Image } from './Image';
|
|
3
|
+
import { VStack } from '../VStack';
|
|
4
|
+
import { HStack } from '../HStack';
|
|
5
|
+
import { Text } from '../Text';
|
|
6
|
+
import { Box } from '../Box';
|
|
7
|
+
import { Grid, GridItem } from '../Grid';
|
|
8
|
+
import { colors, spacing, borderRadius, elevation } from '../../styles/tokens';
|
|
9
|
+
|
|
10
|
+
const meta: Meta<typeof Image> = {
|
|
11
|
+
title: 'Data Display/Image',
|
|
12
|
+
component: Image,
|
|
13
|
+
argTypes: {
|
|
14
|
+
objectFit: {
|
|
15
|
+
control: 'select',
|
|
16
|
+
options: ['cover', 'contain', 'fill', 'none', 'scale-down'],
|
|
17
|
+
},
|
|
18
|
+
isCircular: { control: 'boolean' },
|
|
19
|
+
showLoading: { control: 'boolean' },
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
src: 'https://picsum.photos/300/200',
|
|
23
|
+
width: 300,
|
|
24
|
+
height: 200,
|
|
25
|
+
objectFit: 'cover',
|
|
26
|
+
showLoading: true,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default meta;
|
|
31
|
+
|
|
32
|
+
type Story = StoryObj<typeof Image>;
|
|
33
|
+
|
|
34
|
+
export const Default: Story = {
|
|
35
|
+
render: (args) => <Image {...args} />,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const Sizes: Story = {
|
|
39
|
+
render: () => (
|
|
40
|
+
<VStack space={spacing.lg}>
|
|
41
|
+
<Text weight="semiBold">Different Sizes</Text>
|
|
42
|
+
<HStack space={spacing.lg} alignItems="flex-start">
|
|
43
|
+
<VStack space={spacing.xs}>
|
|
44
|
+
<Image src="https://picsum.photos/100/100" width={100} height={100} />
|
|
45
|
+
<Text size="sm" color={colors.text.secondary}>100×100</Text>
|
|
46
|
+
</VStack>
|
|
47
|
+
<VStack space={spacing.xs}>
|
|
48
|
+
<Image src="https://picsum.photos/150/100" width={150} height={100} />
|
|
49
|
+
<Text size="sm" color={colors.text.secondary}>150×100</Text>
|
|
50
|
+
</VStack>
|
|
51
|
+
<VStack space={spacing.xs}>
|
|
52
|
+
<Image src="https://picsum.photos/200/150" width={200} height={150} />
|
|
53
|
+
<Text size="sm" color={colors.text.secondary}>200×150</Text>
|
|
54
|
+
</VStack>
|
|
55
|
+
</HStack>
|
|
56
|
+
</VStack>
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const ObjectFit: Story = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<VStack space={spacing.lg}>
|
|
63
|
+
<Text weight="semiBold">Object Fit Options</Text>
|
|
64
|
+
<Grid columns={3} gap={spacing.lg}>
|
|
65
|
+
{(['cover', 'contain', 'fill'] as const).map((fit) => (
|
|
66
|
+
<GridItem key={fit}>
|
|
67
|
+
<VStack space={spacing.xs}>
|
|
68
|
+
<Image
|
|
69
|
+
src="https://picsum.photos/400/200"
|
|
70
|
+
width={150}
|
|
71
|
+
height={100}
|
|
72
|
+
objectFit={fit}
|
|
73
|
+
borderRadius={borderRadius.md}
|
|
74
|
+
/>
|
|
75
|
+
<Text size="sm" color={colors.text.secondary}>{fit}</Text>
|
|
76
|
+
</VStack>
|
|
77
|
+
</GridItem>
|
|
78
|
+
))}
|
|
79
|
+
</Grid>
|
|
80
|
+
</VStack>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const BorderRadius: Story = {
|
|
85
|
+
render: () => (
|
|
86
|
+
<VStack space={16}>
|
|
87
|
+
<Text weight="semiBold">Border Radius</Text>
|
|
88
|
+
<HStack space={16} alignItems="flex-start">
|
|
89
|
+
<VStack space={4}>
|
|
90
|
+
<Image
|
|
91
|
+
src="https://picsum.photos/100/100?1"
|
|
92
|
+
width={100}
|
|
93
|
+
height={100}
|
|
94
|
+
borderRadius={0}
|
|
95
|
+
/>
|
|
96
|
+
<Text size="sm" color="#64748b">None</Text>
|
|
97
|
+
</VStack>
|
|
98
|
+
<VStack space={4}>
|
|
99
|
+
<Image
|
|
100
|
+
src="https://picsum.photos/100/100?2"
|
|
101
|
+
width={100}
|
|
102
|
+
height={100}
|
|
103
|
+
borderRadius={8}
|
|
104
|
+
/>
|
|
105
|
+
<Text size="sm" color="#64748b">Rounded</Text>
|
|
106
|
+
</VStack>
|
|
107
|
+
<VStack space={4}>
|
|
108
|
+
<Image
|
|
109
|
+
src="https://picsum.photos/100/100?3"
|
|
110
|
+
width={100}
|
|
111
|
+
height={100}
|
|
112
|
+
borderRadius={16}
|
|
113
|
+
/>
|
|
114
|
+
<Text size="sm" color="#64748b">More Rounded</Text>
|
|
115
|
+
</VStack>
|
|
116
|
+
<VStack space={4}>
|
|
117
|
+
<Image
|
|
118
|
+
src="https://picsum.photos/100/100?4"
|
|
119
|
+
width={100}
|
|
120
|
+
height={100}
|
|
121
|
+
isCircular
|
|
122
|
+
/>
|
|
123
|
+
<Text size="sm" color="#64748b">Circular</Text>
|
|
124
|
+
</VStack>
|
|
125
|
+
</HStack>
|
|
126
|
+
</VStack>
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const Circular: Story = {
|
|
131
|
+
render: () => (
|
|
132
|
+
<VStack space={16}>
|
|
133
|
+
<Text weight="semiBold">Circular Images (Avatars)</Text>
|
|
134
|
+
<HStack space={12}>
|
|
135
|
+
<Image
|
|
136
|
+
src="https://picsum.photos/50/50?5"
|
|
137
|
+
width={50}
|
|
138
|
+
height={50}
|
|
139
|
+
isCircular
|
|
140
|
+
alt="User 1"
|
|
141
|
+
/>
|
|
142
|
+
<Image
|
|
143
|
+
src="https://picsum.photos/60/60?6"
|
|
144
|
+
width={60}
|
|
145
|
+
height={60}
|
|
146
|
+
isCircular
|
|
147
|
+
alt="User 2"
|
|
148
|
+
/>
|
|
149
|
+
<Image
|
|
150
|
+
src="https://picsum.photos/70/70?7"
|
|
151
|
+
width={70}
|
|
152
|
+
height={70}
|
|
153
|
+
isCircular
|
|
154
|
+
alt="User 3"
|
|
155
|
+
/>
|
|
156
|
+
<Image
|
|
157
|
+
src="https://picsum.photos/80/80?8"
|
|
158
|
+
width={80}
|
|
159
|
+
height={80}
|
|
160
|
+
isCircular
|
|
161
|
+
alt="User 4"
|
|
162
|
+
/>
|
|
163
|
+
</HStack>
|
|
164
|
+
</VStack>
|
|
165
|
+
),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const Fallback: Story = {
|
|
169
|
+
render: () => (
|
|
170
|
+
<VStack space={16}>
|
|
171
|
+
<Text weight="semiBold">Fallback States</Text>
|
|
172
|
+
<HStack space={16} alignItems="flex-start">
|
|
173
|
+
<VStack space={4}>
|
|
174
|
+
<Image
|
|
175
|
+
src="https://invalid-url-that-will-fail.com/image.jpg"
|
|
176
|
+
width={150}
|
|
177
|
+
height={100}
|
|
178
|
+
borderRadius={8}
|
|
179
|
+
fallbackText="Image"
|
|
180
|
+
/>
|
|
181
|
+
<Text size="sm" color="#64748b">Default Fallback</Text>
|
|
182
|
+
</VStack>
|
|
183
|
+
<VStack space={4}>
|
|
184
|
+
<Image
|
|
185
|
+
src="https://invalid-url-that-will-fail.com/image.jpg"
|
|
186
|
+
width={150}
|
|
187
|
+
height={100}
|
|
188
|
+
borderRadius={8}
|
|
189
|
+
alt="Product Image"
|
|
190
|
+
/>
|
|
191
|
+
<Text size="sm" color="#64748b">With Alt Text</Text>
|
|
192
|
+
</VStack>
|
|
193
|
+
<VStack space={4}>
|
|
194
|
+
<Image
|
|
195
|
+
src="https://invalid-url-that-will-fail.com/image.jpg"
|
|
196
|
+
width={150}
|
|
197
|
+
height={100}
|
|
198
|
+
borderRadius={8}
|
|
199
|
+
fallback={
|
|
200
|
+
<Box
|
|
201
|
+
flex={1}
|
|
202
|
+
alignItems="center"
|
|
203
|
+
justifyContent="center"
|
|
204
|
+
bg="#fee2e2"
|
|
205
|
+
style={{ width: 150, height: 100, borderRadius: 8 }}
|
|
206
|
+
>
|
|
207
|
+
<Text color="#ef4444">Failed</Text>
|
|
208
|
+
</Box>
|
|
209
|
+
}
|
|
210
|
+
/>
|
|
211
|
+
<Text size="sm" color="#64748b">Custom Fallback</Text>
|
|
212
|
+
</VStack>
|
|
213
|
+
</HStack>
|
|
214
|
+
</VStack>
|
|
215
|
+
),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const Gallery: Story = {
|
|
219
|
+
render: () => (
|
|
220
|
+
<VStack space={16}>
|
|
221
|
+
<Text weight="semiBold">Image Gallery</Text>
|
|
222
|
+
<Grid columns={3} gap={8}>
|
|
223
|
+
{[1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => (
|
|
224
|
+
<GridItem key={i}>
|
|
225
|
+
<Image
|
|
226
|
+
src={`https://picsum.photos/200/200?random=${i}`}
|
|
227
|
+
width="100%"
|
|
228
|
+
height={120}
|
|
229
|
+
borderRadius={8}
|
|
230
|
+
objectFit="cover"
|
|
231
|
+
/>
|
|
232
|
+
</GridItem>
|
|
233
|
+
))}
|
|
234
|
+
</Grid>
|
|
235
|
+
</VStack>
|
|
236
|
+
),
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const ProductCard: Story = {
|
|
240
|
+
render: () => (
|
|
241
|
+
<VStack space={spacing.lg}>
|
|
242
|
+
<Text weight="semiBold">Product Card</Text>
|
|
243
|
+
<Box w={250} bg={colors.white} rounded="lg" shadow="10" overflow="hidden">
|
|
244
|
+
<Image
|
|
245
|
+
src="https://picsum.photos/250/180"
|
|
246
|
+
width="100%"
|
|
247
|
+
height={180}
|
|
248
|
+
objectFit="cover"
|
|
249
|
+
/>
|
|
250
|
+
<Box p={spacing.lg}>
|
|
251
|
+
<VStack space={spacing.sm}>
|
|
252
|
+
<Text weight="semiBold">Product Name</Text>
|
|
253
|
+
<Text size="sm" color={colors.text.secondary}>
|
|
254
|
+
Short product description goes here
|
|
255
|
+
</Text>
|
|
256
|
+
<HStack justifyContent="space-between" alignItems="center">
|
|
257
|
+
<Text weight="bold" color={colors.brand.blue}>$99.99</Text>
|
|
258
|
+
<Box px={spacing.md} py={6} bg={colors.brand.blue} rounded="md">
|
|
259
|
+
<Text size="sm" style={{ color: 'white' }}>Add to Cart</Text>
|
|
260
|
+
</Box>
|
|
261
|
+
</HStack>
|
|
262
|
+
</VStack>
|
|
263
|
+
</Box>
|
|
264
|
+
</Box>
|
|
265
|
+
</VStack>
|
|
266
|
+
),
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export const UserProfile: Story = {
|
|
270
|
+
render: () => (
|
|
271
|
+
<VStack space={spacing.lg}>
|
|
272
|
+
<Text weight="semiBold">User Profile</Text>
|
|
273
|
+
<Box p={20} bg={colors.white} rounded="lg" shadow="10" alignItems="center">
|
|
274
|
+
<Image
|
|
275
|
+
src="https://picsum.photos/120/120?random=profile"
|
|
276
|
+
width={120}
|
|
277
|
+
height={120}
|
|
278
|
+
isCircular
|
|
279
|
+
/>
|
|
280
|
+
<VStack space={spacing.xs} alignItems="center" style={{ marginTop: spacing.lg }}>
|
|
281
|
+
<Text weight="semiBold" size="lg">John Doe</Text>
|
|
282
|
+
<Text color={colors.text.secondary}>Senior Developer</Text>
|
|
283
|
+
<Text size="sm" color={colors.text.secondary}>San Francisco, CA</Text>
|
|
284
|
+
</VStack>
|
|
285
|
+
</Box>
|
|
286
|
+
</VStack>
|
|
287
|
+
),
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export const HeroImage: Story = {
|
|
291
|
+
render: () => (
|
|
292
|
+
<VStack space={16}>
|
|
293
|
+
<Text weight="semiBold">Hero Image with Overlay</Text>
|
|
294
|
+
<Box w="100%" h={300} rounded="lg" overflow="hidden" position="relative">
|
|
295
|
+
<Image
|
|
296
|
+
src="https://picsum.photos/800/300?random=hero"
|
|
297
|
+
width="100%"
|
|
298
|
+
height="100%"
|
|
299
|
+
objectFit="cover"
|
|
300
|
+
/>
|
|
301
|
+
<Box
|
|
302
|
+
position="absolute"
|
|
303
|
+
style={{ top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.4)' }}
|
|
304
|
+
alignItems="center"
|
|
305
|
+
justifyContent="center"
|
|
306
|
+
>
|
|
307
|
+
<VStack space={8} alignItems="center">
|
|
308
|
+
<Text weight="bold" size="lg" style={{ color: 'white' }}>
|
|
309
|
+
Welcome to Our Platform
|
|
310
|
+
</Text>
|
|
311
|
+
<Text style={{ color: 'white' }}>
|
|
312
|
+
Discover amazing content
|
|
313
|
+
</Text>
|
|
314
|
+
</VStack>
|
|
315
|
+
</Box>
|
|
316
|
+
</Box>
|
|
317
|
+
</VStack>
|
|
318
|
+
),
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export const AspectRatios: Story = {
|
|
322
|
+
render: () => (
|
|
323
|
+
<VStack space={16}>
|
|
324
|
+
<Text weight="semiBold">Common Aspect Ratios</Text>
|
|
325
|
+
<HStack space={16} alignItems="flex-start">
|
|
326
|
+
<VStack space={4}>
|
|
327
|
+
<Image
|
|
328
|
+
src="https://picsum.photos/100/100?aspect1"
|
|
329
|
+
width={100}
|
|
330
|
+
height={100}
|
|
331
|
+
borderRadius={8}
|
|
332
|
+
/>
|
|
333
|
+
<Text size="sm" color="#64748b">1:1</Text>
|
|
334
|
+
</VStack>
|
|
335
|
+
<VStack space={4}>
|
|
336
|
+
<Image
|
|
337
|
+
src="https://picsum.photos/160/90?aspect2"
|
|
338
|
+
width={160}
|
|
339
|
+
height={90}
|
|
340
|
+
borderRadius={8}
|
|
341
|
+
/>
|
|
342
|
+
<Text size="sm" color="#64748b">16:9</Text>
|
|
343
|
+
</VStack>
|
|
344
|
+
<VStack space={4}>
|
|
345
|
+
<Image
|
|
346
|
+
src="https://picsum.photos/120/160?aspect3"
|
|
347
|
+
width={120}
|
|
348
|
+
height={160}
|
|
349
|
+
borderRadius={8}
|
|
350
|
+
/>
|
|
351
|
+
<Text size="sm" color="#64748b">3:4</Text>
|
|
352
|
+
</VStack>
|
|
353
|
+
</HStack>
|
|
354
|
+
</VStack>
|
|
355
|
+
),
|
|
356
|
+
};
|
|
357
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Component
|
|
3
|
+
* Enhanced image with loading states and fallback
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { forwardRef, useState } from 'react';
|
|
7
|
+
import {
|
|
8
|
+
Image as RNImage,
|
|
9
|
+
ImageProps as RNImageProps,
|
|
10
|
+
View,
|
|
11
|
+
ActivityIndicator,
|
|
12
|
+
Text,
|
|
13
|
+
StyleSheet,
|
|
14
|
+
} from 'react-native';
|
|
15
|
+
import { colors, typography } from '../../styles/tokens';
|
|
16
|
+
|
|
17
|
+
export interface ImageProps extends Omit<RNImageProps, 'source'> {
|
|
18
|
+
/** Image source URL */
|
|
19
|
+
src?: string;
|
|
20
|
+
/** Image source (for local images) */
|
|
21
|
+
source?: RNImageProps['source'];
|
|
22
|
+
/** Alt text */
|
|
23
|
+
alt?: string;
|
|
24
|
+
/** Fallback element */
|
|
25
|
+
fallback?: React.ReactNode;
|
|
26
|
+
/** Fallback text */
|
|
27
|
+
fallbackText?: string;
|
|
28
|
+
/** Object fit */
|
|
29
|
+
objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
30
|
+
/** Border radius */
|
|
31
|
+
borderRadius?: number;
|
|
32
|
+
/** Is circular */
|
|
33
|
+
isCircular?: boolean;
|
|
34
|
+
/** Width */
|
|
35
|
+
width?: number | string;
|
|
36
|
+
/** Height */
|
|
37
|
+
height?: number | string;
|
|
38
|
+
/** Show loading indicator */
|
|
39
|
+
showLoading?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const Image = forwardRef<RNImage, ImageProps>(
|
|
43
|
+
(
|
|
44
|
+
{
|
|
45
|
+
style,
|
|
46
|
+
src,
|
|
47
|
+
source,
|
|
48
|
+
alt,
|
|
49
|
+
fallback,
|
|
50
|
+
fallbackText,
|
|
51
|
+
objectFit = 'cover',
|
|
52
|
+
borderRadius,
|
|
53
|
+
isCircular = false,
|
|
54
|
+
width,
|
|
55
|
+
height,
|
|
56
|
+
showLoading = true,
|
|
57
|
+
onLoadStart,
|
|
58
|
+
onLoadEnd,
|
|
59
|
+
onError,
|
|
60
|
+
...props
|
|
61
|
+
},
|
|
62
|
+
ref
|
|
63
|
+
) => {
|
|
64
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
65
|
+
const [hasError, setHasError] = useState(false);
|
|
66
|
+
|
|
67
|
+
const imageSource = src ? { uri: src } : source;
|
|
68
|
+
|
|
69
|
+
const handleLoadStart = (e: any) => {
|
|
70
|
+
setIsLoading(true);
|
|
71
|
+
onLoadStart?.(e);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const handleLoadEnd = (e: any) => {
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
onLoadEnd?.(e);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleError = (e: any) => {
|
|
80
|
+
setIsLoading(false);
|
|
81
|
+
setHasError(true);
|
|
82
|
+
onError?.(e);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const resizeModeMap = {
|
|
86
|
+
cover: 'cover' as const,
|
|
87
|
+
contain: 'contain' as const,
|
|
88
|
+
fill: 'stretch' as const,
|
|
89
|
+
none: 'center' as const,
|
|
90
|
+
'scale-down': 'contain' as const,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const containerStyle = [
|
|
94
|
+
styles.container,
|
|
95
|
+
width !== undefined && { width },
|
|
96
|
+
height !== undefined && { height },
|
|
97
|
+
borderRadius !== undefined && { borderRadius },
|
|
98
|
+
isCircular && { borderRadius: 9999 },
|
|
99
|
+
style,
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
if (hasError) {
|
|
103
|
+
if (fallback) {
|
|
104
|
+
return <View style={containerStyle}>{fallback}</View>;
|
|
105
|
+
}
|
|
106
|
+
return (
|
|
107
|
+
<View style={[containerStyle, styles.fallback]}>
|
|
108
|
+
<Text style={styles.fallbackText}>{fallbackText || alt || '🖼'}</Text>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<View style={containerStyle}>
|
|
115
|
+
<RNImage
|
|
116
|
+
ref={ref}
|
|
117
|
+
source={imageSource}
|
|
118
|
+
style={[
|
|
119
|
+
styles.image,
|
|
120
|
+
width !== undefined && { width },
|
|
121
|
+
height !== undefined && { height },
|
|
122
|
+
borderRadius !== undefined && { borderRadius },
|
|
123
|
+
isCircular && { borderRadius: 9999 },
|
|
124
|
+
]}
|
|
125
|
+
resizeMode={resizeModeMap[objectFit]}
|
|
126
|
+
onLoadStart={handleLoadStart}
|
|
127
|
+
onLoadEnd={handleLoadEnd}
|
|
128
|
+
onError={handleError}
|
|
129
|
+
accessibilityLabel={alt}
|
|
130
|
+
{...props}
|
|
131
|
+
/>
|
|
132
|
+
{isLoading && showLoading && (
|
|
133
|
+
<View style={styles.loadingOverlay}>
|
|
134
|
+
<ActivityIndicator size="small" color={colors.brand.blue} />
|
|
135
|
+
</View>
|
|
136
|
+
)}
|
|
137
|
+
</View>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
Image.displayName = 'Image';
|
|
143
|
+
|
|
144
|
+
const styles = StyleSheet.create({
|
|
145
|
+
container: {
|
|
146
|
+
overflow: 'hidden',
|
|
147
|
+
backgroundColor: colors.background.secondary,
|
|
148
|
+
},
|
|
149
|
+
image: {
|
|
150
|
+
width: '100%',
|
|
151
|
+
height: '100%',
|
|
152
|
+
},
|
|
153
|
+
loadingOverlay: {
|
|
154
|
+
...StyleSheet.absoluteFillObject,
|
|
155
|
+
alignItems: 'center',
|
|
156
|
+
justifyContent: 'center',
|
|
157
|
+
backgroundColor: colors.background.secondary,
|
|
158
|
+
},
|
|
159
|
+
fallback: {
|
|
160
|
+
alignItems: 'center',
|
|
161
|
+
justifyContent: 'center',
|
|
162
|
+
backgroundColor: colors.background.tertiary,
|
|
163
|
+
},
|
|
164
|
+
fallbackText: {
|
|
165
|
+
fontSize: typography.fontSize.caption,
|
|
166
|
+
color: colors.text.secondary,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { Input } from './Input';
|
|
4
|
+
import { VStack } from '../VStack';
|
|
5
|
+
import { Text } from '../Text';
|
|
6
|
+
import Svg, { Path } from 'react-native-svg';
|
|
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 Input> = {
|
|
17
|
+
title: 'Components/Input',
|
|
18
|
+
component: Input,
|
|
19
|
+
decorators: [
|
|
20
|
+
(Story) => (
|
|
21
|
+
<StoryContainer>
|
|
22
|
+
<Story />
|
|
23
|
+
</StoryContainer>
|
|
24
|
+
),
|
|
25
|
+
],
|
|
26
|
+
argTypes: {
|
|
27
|
+
variant: {
|
|
28
|
+
control: 'select',
|
|
29
|
+
options: ['outline', 'filled', 'flushed'],
|
|
30
|
+
},
|
|
31
|
+
size: {
|
|
32
|
+
control: 'select',
|
|
33
|
+
options: ['sm', 'md', 'lg'],
|
|
34
|
+
},
|
|
35
|
+
isInvalid: {
|
|
36
|
+
control: 'boolean',
|
|
37
|
+
},
|
|
38
|
+
isDisabled: {
|
|
39
|
+
control: 'boolean',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
args: {
|
|
43
|
+
placeholder: 'Enter text...',
|
|
44
|
+
variant: 'outline',
|
|
45
|
+
size: 'md',
|
|
46
|
+
isInvalid: false,
|
|
47
|
+
isDisabled: false,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default meta;
|
|
52
|
+
|
|
53
|
+
type Story = StoryObj<typeof Input>;
|
|
54
|
+
|
|
55
|
+
export const Default: Story = {};
|
|
56
|
+
|
|
57
|
+
export const Variants: Story = {
|
|
58
|
+
render: () => (
|
|
59
|
+
<VStack space={spacing.lg}>
|
|
60
|
+
<Text weight="semiBold">Input Variants</Text>
|
|
61
|
+
<Input variant="outline" label="Outline" placeholder="Outline input" />
|
|
62
|
+
<Input variant="filled" label="Filled" placeholder="Filled input" />
|
|
63
|
+
<Input variant="flushed" label="Flushed" placeholder="Flushed input" />
|
|
64
|
+
</VStack>
|
|
65
|
+
),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Sizes: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<VStack space={spacing.lg}>
|
|
71
|
+
<Text weight="semiBold">Input Sizes</Text>
|
|
72
|
+
<Input size="sm" placeholder="Small input" />
|
|
73
|
+
<Input size="md" placeholder="Medium input" />
|
|
74
|
+
<Input size="lg" placeholder="Large input" />
|
|
75
|
+
</VStack>
|
|
76
|
+
),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const WithLabels: Story = {
|
|
80
|
+
render: () => (
|
|
81
|
+
<VStack space={spacing.lg}>
|
|
82
|
+
<Text weight="semiBold">Input with Labels</Text>
|
|
83
|
+
<Input
|
|
84
|
+
label="Email"
|
|
85
|
+
placeholder="Enter your email"
|
|
86
|
+
helperText="We'll never share your email"
|
|
87
|
+
/>
|
|
88
|
+
<Input
|
|
89
|
+
label="Username"
|
|
90
|
+
placeholder="Enter username"
|
|
91
|
+
isRequired
|
|
92
|
+
/>
|
|
93
|
+
</VStack>
|
|
94
|
+
),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const States: Story = {
|
|
98
|
+
render: () => (
|
|
99
|
+
<VStack space={spacing.lg}>
|
|
100
|
+
<Text weight="semiBold">Input States</Text>
|
|
101
|
+
<Input label="Normal" placeholder="Normal input" />
|
|
102
|
+
<Input
|
|
103
|
+
label="Invalid"
|
|
104
|
+
placeholder="Invalid input"
|
|
105
|
+
isInvalid
|
|
106
|
+
errorMessage="This field is required"
|
|
107
|
+
/>
|
|
108
|
+
<Input
|
|
109
|
+
label="Disabled"
|
|
110
|
+
placeholder="Disabled input"
|
|
111
|
+
isDisabled
|
|
112
|
+
/>
|
|
113
|
+
</VStack>
|
|
114
|
+
),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const Password: Story = {
|
|
118
|
+
render: () => (
|
|
119
|
+
<VStack space={spacing.lg}>
|
|
120
|
+
<Text weight="semiBold">Password Input</Text>
|
|
121
|
+
<Input
|
|
122
|
+
label="Password"
|
|
123
|
+
placeholder="Enter password"
|
|
124
|
+
isPassword
|
|
125
|
+
/>
|
|
126
|
+
</VStack>
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const WithElements: Story = {
|
|
131
|
+
render: () => {
|
|
132
|
+
const SearchIcon = (
|
|
133
|
+
<Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
|
|
134
|
+
<Path
|
|
135
|
+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
136
|
+
stroke={colors.text.disabled}
|
|
137
|
+
strokeWidth={2}
|
|
138
|
+
strokeLinecap="round"
|
|
139
|
+
/>
|
|
140
|
+
</Svg>
|
|
141
|
+
);
|
|
142
|
+
return (
|
|
143
|
+
<VStack space={spacing.lg}>
|
|
144
|
+
<Text weight="semiBold">Input with Elements</Text>
|
|
145
|
+
<Input
|
|
146
|
+
placeholder="Search..."
|
|
147
|
+
leftElement={SearchIcon}
|
|
148
|
+
/>
|
|
149
|
+
</VStack>
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Styles using design system tokens
|
|
156
|
+
*/
|
|
157
|
+
const styles = StyleSheet.create({
|
|
158
|
+
container: {
|
|
159
|
+
padding: spacing.lg,
|
|
160
|
+
backgroundColor: colors.background.default,
|
|
161
|
+
borderRadius: borderRadius.lg,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|