@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,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,2 @@
1
+ export { Button, type ButtonProps } from './Button';
2
+
@@ -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
+ });
@@ -0,0 +1,11 @@
1
+ export {
2
+ Card,
3
+ CardHeader,
4
+ CardBody,
5
+ CardFooter,
6
+ type CardProps,
7
+ type CardHeaderProps,
8
+ type CardBodyProps,
9
+ type CardFooterProps,
10
+ } from './Card';
11
+