@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,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Button Component
|
|
3
|
+
* Primary action component with multiple variants
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import {
|
|
8
|
+
Pressable,
|
|
9
|
+
PressableProps,
|
|
10
|
+
View,
|
|
11
|
+
ActivityIndicator,
|
|
12
|
+
Text,
|
|
13
|
+
StyleSheet,
|
|
14
|
+
ViewStyle,
|
|
15
|
+
TextStyle,
|
|
16
|
+
} from 'react-native';
|
|
17
|
+
import { colors, spacing, typography, borderRadius } from '../../styles/tokens';
|
|
18
|
+
|
|
19
|
+
export interface ButtonProps extends Omit<PressableProps, 'style' | 'children'> {
|
|
20
|
+
/** Button variant */
|
|
21
|
+
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'outline';
|
|
22
|
+
/** Button size */
|
|
23
|
+
size?: 'sm' | 'md' | 'lg';
|
|
24
|
+
/** Full width button */
|
|
25
|
+
fullWidth?: boolean;
|
|
26
|
+
/** Loading state */
|
|
27
|
+
isLoading?: boolean;
|
|
28
|
+
/** Disabled state */
|
|
29
|
+
isDisabled?: boolean;
|
|
30
|
+
/** Left icon */
|
|
31
|
+
leftIcon?: React.ReactNode;
|
|
32
|
+
/** Right icon */
|
|
33
|
+
rightIcon?: React.ReactNode;
|
|
34
|
+
/** Button label */
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
/** Custom style */
|
|
37
|
+
style?: ViewStyle;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Variant background colors
|
|
41
|
+
const variantColors = {
|
|
42
|
+
primary: {
|
|
43
|
+
default: colors.primary.default,
|
|
44
|
+
pressed: colors.primary.active,
|
|
45
|
+
disabled: colors.primary.disabled,
|
|
46
|
+
},
|
|
47
|
+
secondary: {
|
|
48
|
+
default: colors.secondary.default,
|
|
49
|
+
pressed: colors.secondary.active,
|
|
50
|
+
disabled: colors.secondary.disabled,
|
|
51
|
+
},
|
|
52
|
+
ghost: {
|
|
53
|
+
default: 'transparent',
|
|
54
|
+
pressed: colors.ghost.active,
|
|
55
|
+
disabled: 'transparent',
|
|
56
|
+
},
|
|
57
|
+
danger: {
|
|
58
|
+
default: colors.danger.default,
|
|
59
|
+
pressed: colors.danger.active,
|
|
60
|
+
disabled: colors.danger.disabled,
|
|
61
|
+
},
|
|
62
|
+
outline: {
|
|
63
|
+
default: 'transparent',
|
|
64
|
+
pressed: 'rgba(0, 160, 204, 0.1)',
|
|
65
|
+
disabled: 'transparent',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Size configurations
|
|
70
|
+
const sizeConfig = {
|
|
71
|
+
sm: { height: 32, paddingHorizontal: 12, fontSize: 12 },
|
|
72
|
+
md: { height: 40, paddingHorizontal: 16, fontSize: 14 },
|
|
73
|
+
lg: { height: 48, paddingHorizontal: 24, fontSize: 16 },
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const Button = ({
|
|
77
|
+
variant = 'primary',
|
|
78
|
+
size = 'md',
|
|
79
|
+
fullWidth = false,
|
|
80
|
+
isLoading = false,
|
|
81
|
+
isDisabled = false,
|
|
82
|
+
leftIcon,
|
|
83
|
+
rightIcon,
|
|
84
|
+
children,
|
|
85
|
+
style,
|
|
86
|
+
...props
|
|
87
|
+
}: ButtonProps) => {
|
|
88
|
+
const disabled = isDisabled || isLoading;
|
|
89
|
+
const variantColor = variantColors[variant];
|
|
90
|
+
const sizeStyles = sizeConfig[size];
|
|
91
|
+
|
|
92
|
+
// Text color based on variant
|
|
93
|
+
const getTextColor = () => {
|
|
94
|
+
if (variant === 'primary' || variant === 'danger') {
|
|
95
|
+
return colors.white;
|
|
96
|
+
}
|
|
97
|
+
if (variant === 'outline') {
|
|
98
|
+
return colors.brand.blue;
|
|
99
|
+
}
|
|
100
|
+
return colors.text.default;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Pressable
|
|
105
|
+
disabled={disabled}
|
|
106
|
+
style={({ pressed }) => {
|
|
107
|
+
const baseStyle: ViewStyle = {
|
|
108
|
+
flexDirection: 'row',
|
|
109
|
+
alignItems: 'center',
|
|
110
|
+
justifyContent: 'center',
|
|
111
|
+
borderRadius: borderRadius.md,
|
|
112
|
+
height: sizeStyles.height,
|
|
113
|
+
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
114
|
+
opacity: disabled ? 0.6 : 1,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Background color based on state
|
|
118
|
+
if (disabled) {
|
|
119
|
+
baseStyle.backgroundColor = variantColor.disabled;
|
|
120
|
+
} else if (pressed) {
|
|
121
|
+
baseStyle.backgroundColor = variantColor.pressed;
|
|
122
|
+
} else {
|
|
123
|
+
baseStyle.backgroundColor = variantColor.default;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Outline variant border
|
|
127
|
+
if (variant === 'outline') {
|
|
128
|
+
baseStyle.borderWidth = 1;
|
|
129
|
+
baseStyle.borderColor = disabled ? colors.border.disabled : colors.brand.blue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Full width
|
|
133
|
+
if (fullWidth) {
|
|
134
|
+
baseStyle.width = '100%';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return [baseStyle, style];
|
|
138
|
+
}}
|
|
139
|
+
accessibilityRole="button"
|
|
140
|
+
accessibilityState={{ disabled }}
|
|
141
|
+
{...props}
|
|
142
|
+
>
|
|
143
|
+
{isLoading ? (
|
|
144
|
+
<ActivityIndicator
|
|
145
|
+
size="small"
|
|
146
|
+
color={variant === 'primary' || variant === 'danger' ? colors.white : colors.brand.blue}
|
|
147
|
+
/>
|
|
148
|
+
) : (
|
|
149
|
+
<View style={styles.content}>
|
|
150
|
+
{leftIcon && <View style={styles.leftIcon}>{leftIcon}</View>}
|
|
151
|
+
<Text
|
|
152
|
+
style={{
|
|
153
|
+
color: getTextColor(),
|
|
154
|
+
fontSize: sizeStyles.fontSize,
|
|
155
|
+
fontWeight: '600',
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
</Text>
|
|
160
|
+
{rightIcon && <View style={styles.rightIcon}>{rightIcon}</View>}
|
|
161
|
+
</View>
|
|
162
|
+
)}
|
|
163
|
+
</Pressable>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
Button.displayName = 'Button';
|
|
168
|
+
|
|
169
|
+
const styles = StyleSheet.create({
|
|
170
|
+
content: {
|
|
171
|
+
flexDirection: 'row',
|
|
172
|
+
alignItems: 'center',
|
|
173
|
+
},
|
|
174
|
+
leftIcon: {
|
|
175
|
+
marginRight: 8,
|
|
176
|
+
},
|
|
177
|
+
rightIcon: {
|
|
178
|
+
marginLeft: 8,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { Card, CardHeader, CardBody, CardFooter } from './Card';
|
|
4
|
+
import { VStack } from '../VStack';
|
|
5
|
+
import { HStack } from '../HStack';
|
|
6
|
+
import { Text } from '../Text';
|
|
7
|
+
import { Heading } from '../Heading';
|
|
8
|
+
import { Button } from '../Button';
|
|
9
|
+
import { Avatar } from '../Avatar';
|
|
10
|
+
import { Badge } from '../Badge';
|
|
11
|
+
import { colors, spacing, borderRadius } from '../../styles/tokens';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Story container with design system tokens
|
|
15
|
+
*/
|
|
16
|
+
const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
17
|
+
<View style={styles.container}>{children}</View>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const meta: Meta<typeof Card> = {
|
|
21
|
+
title: 'Components/Card',
|
|
22
|
+
component: Card,
|
|
23
|
+
decorators: [
|
|
24
|
+
(Story) => (
|
|
25
|
+
<StoryContainer>
|
|
26
|
+
<Story />
|
|
27
|
+
</StoryContainer>
|
|
28
|
+
),
|
|
29
|
+
],
|
|
30
|
+
argTypes: {
|
|
31
|
+
variant: {
|
|
32
|
+
control: 'select',
|
|
33
|
+
options: ['elevated', 'outline', 'filled'],
|
|
34
|
+
},
|
|
35
|
+
padding: {
|
|
36
|
+
control: 'select',
|
|
37
|
+
options: ['none', 'sm', 'md', 'lg'],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
args: {
|
|
41
|
+
variant: 'elevated',
|
|
42
|
+
padding: 'md',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default meta;
|
|
47
|
+
|
|
48
|
+
type Story = StoryObj<typeof Card>;
|
|
49
|
+
|
|
50
|
+
export const Default: Story = {
|
|
51
|
+
render: (args) => (
|
|
52
|
+
<Card {...args}>
|
|
53
|
+
<Heading as="h4">Card Title</Heading>
|
|
54
|
+
<Text variant="caption" color={colors.text.secondary} style={{ marginTop: spacing.xs }}>
|
|
55
|
+
This is a basic card with some content inside.
|
|
56
|
+
</Text>
|
|
57
|
+
</Card>
|
|
58
|
+
),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const Variants: Story = {
|
|
62
|
+
render: () => (
|
|
63
|
+
<VStack space={spacing.lg}>
|
|
64
|
+
<Text weight="semiBold">Card Variants</Text>
|
|
65
|
+
<Card variant="elevated">
|
|
66
|
+
<Text weight="medium">Elevated Card</Text>
|
|
67
|
+
<Text variant="caption" color={colors.text.secondary}>With shadow elevation</Text>
|
|
68
|
+
</Card>
|
|
69
|
+
<Card variant="outline">
|
|
70
|
+
<Text weight="medium">Outline Card</Text>
|
|
71
|
+
<Text variant="caption" color={colors.text.secondary}>With border outline</Text>
|
|
72
|
+
</Card>
|
|
73
|
+
<Card variant="filled">
|
|
74
|
+
<Text weight="medium">Filled Card</Text>
|
|
75
|
+
<Text variant="caption" color={colors.text.secondary}>With filled background</Text>
|
|
76
|
+
</Card>
|
|
77
|
+
</VStack>
|
|
78
|
+
),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const WithSections: Story = {
|
|
82
|
+
render: () => (
|
|
83
|
+
<Card variant="elevated" padding="none">
|
|
84
|
+
<CardHeader>
|
|
85
|
+
<Heading as="h4">Project Update</Heading>
|
|
86
|
+
</CardHeader>
|
|
87
|
+
<CardBody>
|
|
88
|
+
<Text>
|
|
89
|
+
The latest deployment has been completed successfully. All services are
|
|
90
|
+
running normally and metrics are within expected ranges.
|
|
91
|
+
</Text>
|
|
92
|
+
</CardBody>
|
|
93
|
+
<CardFooter>
|
|
94
|
+
<Button variant="ghost" size="sm">Dismiss</Button>
|
|
95
|
+
<Button size="sm">View Details</Button>
|
|
96
|
+
</CardFooter>
|
|
97
|
+
</Card>
|
|
98
|
+
),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const UserCard: Story = {
|
|
102
|
+
render: () => (
|
|
103
|
+
<Card variant="elevated">
|
|
104
|
+
<HStack space={spacing.md}>
|
|
105
|
+
<Avatar name="John Doe" size="lg" />
|
|
106
|
+
<VStack space={spacing.xs} style={{ flex: 1 }}>
|
|
107
|
+
<HStack justifyContent="space-between" alignItems="center">
|
|
108
|
+
<Text weight="semiBold">John Doe</Text>
|
|
109
|
+
<Badge variant="green" size="sm">Active</Badge>
|
|
110
|
+
</HStack>
|
|
111
|
+
<Text variant="caption" color={colors.text.secondary}>Senior Engineer</Text>
|
|
112
|
+
<Text variant="caption" color={colors.text.secondary}>john.doe@company.com</Text>
|
|
113
|
+
</VStack>
|
|
114
|
+
</HStack>
|
|
115
|
+
</Card>
|
|
116
|
+
),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const PressableCard: Story = {
|
|
120
|
+
render: () => (
|
|
121
|
+
<Card variant="elevated" isPressable onPress={() => console.log('Card pressed')}>
|
|
122
|
+
<VStack space={spacing.sm}>
|
|
123
|
+
<HStack justifyContent="space-between" alignItems="center">
|
|
124
|
+
<Text weight="semiBold">Pressable Card</Text>
|
|
125
|
+
<Text variant="caption" color={colors.brand.blue}>→</Text>
|
|
126
|
+
</HStack>
|
|
127
|
+
<Text variant="caption" color={colors.text.secondary}>
|
|
128
|
+
Tap this card to trigger an action
|
|
129
|
+
</Text>
|
|
130
|
+
</VStack>
|
|
131
|
+
</Card>
|
|
132
|
+
),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Styles using design system tokens
|
|
137
|
+
*/
|
|
138
|
+
const styles = StyleSheet.create({
|
|
139
|
+
container: {
|
|
140
|
+
padding: spacing.lg,
|
|
141
|
+
backgroundColor: colors.background.secondary,
|
|
142
|
+
borderRadius: borderRadius.lg,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card Component
|
|
3
|
+
* Container component with elevation and styling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { forwardRef } from 'react';
|
|
7
|
+
import { View, ViewProps, Pressable, StyleSheet } from 'react-native';
|
|
8
|
+
import { colors, spacing, borderRadius, elevation } from '../../styles/tokens';
|
|
9
|
+
|
|
10
|
+
export interface CardProps extends ViewProps {
|
|
11
|
+
/** Card variant */
|
|
12
|
+
variant?: 'elevated' | 'outline' | 'filled';
|
|
13
|
+
/** Padding size */
|
|
14
|
+
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
15
|
+
/** Shadow elevation level (only applies to 'elevated' variant or can override any variant) */
|
|
16
|
+
shadow?: '5' | '10' | '20' | '30' | '40';
|
|
17
|
+
/** Make card pressable */
|
|
18
|
+
isPressable?: boolean;
|
|
19
|
+
/** Press handler */
|
|
20
|
+
onPress?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const elevationMap = {
|
|
24
|
+
'5': elevation['5'],
|
|
25
|
+
'10': elevation['10'],
|
|
26
|
+
'20': elevation['20'],
|
|
27
|
+
'30': elevation['30'],
|
|
28
|
+
'40': elevation['40'],
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export const Card = forwardRef<View, CardProps>(
|
|
32
|
+
(
|
|
33
|
+
{
|
|
34
|
+
style,
|
|
35
|
+
variant = 'elevated',
|
|
36
|
+
padding = 'md',
|
|
37
|
+
shadow,
|
|
38
|
+
isPressable = false,
|
|
39
|
+
onPress,
|
|
40
|
+
children,
|
|
41
|
+
...props
|
|
42
|
+
},
|
|
43
|
+
ref
|
|
44
|
+
) => {
|
|
45
|
+
// Determine shadow styles
|
|
46
|
+
const shadowStyle = shadow
|
|
47
|
+
? elevationMap[shadow]
|
|
48
|
+
: variant === 'elevated'
|
|
49
|
+
? elevationMap['20']
|
|
50
|
+
: undefined;
|
|
51
|
+
|
|
52
|
+
const cardContent = (
|
|
53
|
+
<View
|
|
54
|
+
ref={ref}
|
|
55
|
+
style={[
|
|
56
|
+
styles.card,
|
|
57
|
+
styles[variant],
|
|
58
|
+
styles[`padding${padding.charAt(0).toUpperCase() + padding.slice(1)}` as keyof typeof styles],
|
|
59
|
+
shadowStyle,
|
|
60
|
+
style,
|
|
61
|
+
]}
|
|
62
|
+
{...props}
|
|
63
|
+
>
|
|
64
|
+
{children}
|
|
65
|
+
</View>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (isPressable && onPress) {
|
|
69
|
+
return (
|
|
70
|
+
<Pressable onPress={onPress} style={({ pressed }) => [pressed && styles.pressed]}>
|
|
71
|
+
{cardContent}
|
|
72
|
+
</Pressable>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return cardContent;
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
Card.displayName = 'Card';
|
|
81
|
+
|
|
82
|
+
export interface CardHeaderProps extends ViewProps {}
|
|
83
|
+
export interface CardBodyProps extends ViewProps {}
|
|
84
|
+
export interface CardFooterProps extends ViewProps {}
|
|
85
|
+
|
|
86
|
+
export const CardHeader = forwardRef<View, CardHeaderProps>(
|
|
87
|
+
({ style, children, ...props }, ref) => {
|
|
88
|
+
return (
|
|
89
|
+
<View ref={ref} style={[styles.header, style]} {...props}>
|
|
90
|
+
{children}
|
|
91
|
+
</View>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
CardHeader.displayName = 'CardHeader';
|
|
97
|
+
|
|
98
|
+
export const CardBody = forwardRef<View, CardBodyProps>(
|
|
99
|
+
({ style, children, ...props }, ref) => {
|
|
100
|
+
return (
|
|
101
|
+
<View ref={ref} style={[styles.body, style]} {...props}>
|
|
102
|
+
{children}
|
|
103
|
+
</View>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CardBody.displayName = 'CardBody';
|
|
109
|
+
|
|
110
|
+
export const CardFooter = forwardRef<View, CardFooterProps>(
|
|
111
|
+
({ style, children, ...props }, ref) => {
|
|
112
|
+
return (
|
|
113
|
+
<View ref={ref} style={[styles.footer, style]} {...props}>
|
|
114
|
+
{children}
|
|
115
|
+
</View>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
CardFooter.displayName = 'CardFooter';
|
|
121
|
+
|
|
122
|
+
const styles = StyleSheet.create({
|
|
123
|
+
card: {
|
|
124
|
+
borderRadius: borderRadius.lg,
|
|
125
|
+
backgroundColor: colors.background.default,
|
|
126
|
+
},
|
|
127
|
+
elevated: {
|
|
128
|
+
// Shadow is applied dynamically via shadowStyle
|
|
129
|
+
},
|
|
130
|
+
outline: {
|
|
131
|
+
borderWidth: 1,
|
|
132
|
+
borderColor: colors.border.default,
|
|
133
|
+
},
|
|
134
|
+
filled: {
|
|
135
|
+
backgroundColor: colors.background.secondary,
|
|
136
|
+
},
|
|
137
|
+
paddingNone: {
|
|
138
|
+
padding: 0,
|
|
139
|
+
},
|
|
140
|
+
paddingSm: {
|
|
141
|
+
padding: spacing['3x'],
|
|
142
|
+
},
|
|
143
|
+
paddingMd: {
|
|
144
|
+
padding: spacing['4x'],
|
|
145
|
+
},
|
|
146
|
+
paddingLg: {
|
|
147
|
+
padding: spacing['6x'],
|
|
148
|
+
},
|
|
149
|
+
pressed: {
|
|
150
|
+
opacity: 0.9,
|
|
151
|
+
transform: [{ scale: 0.98 }],
|
|
152
|
+
},
|
|
153
|
+
header: {
|
|
154
|
+
paddingBottom: spacing['3x'],
|
|
155
|
+
borderBottomWidth: 1,
|
|
156
|
+
borderBottomColor: colors.border.disabled,
|
|
157
|
+
},
|
|
158
|
+
body: {
|
|
159
|
+
paddingVertical: spacing['3x'],
|
|
160
|
+
},
|
|
161
|
+
footer: {
|
|
162
|
+
paddingTop: spacing['3x'],
|
|
163
|
+
borderTopWidth: 1,
|
|
164
|
+
borderTopColor: colors.border.disabled,
|
|
165
|
+
flexDirection: 'row',
|
|
166
|
+
justifyContent: 'flex-end',
|
|
167
|
+
gap: spacing['2x'],
|
|
168
|
+
},
|
|
169
|
+
});
|