@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,154 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { Avatar, AvatarGroup } from './Avatar';
4
+ import { VStack } from '../VStack';
5
+ import { HStack } from '../HStack';
6
+ import { Text } from '../Text';
7
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
8
+
9
+ /**
10
+ * Story container with design system tokens
11
+ */
12
+ const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
13
+ <View style={styles.container}>{children}</View>
14
+ );
15
+
16
+ const meta: Meta<typeof Avatar> = {
17
+ title: 'Components/Avatar',
18
+ component: Avatar,
19
+ decorators: [
20
+ (Story) => (
21
+ <StoryContainer>
22
+ <Story />
23
+ </StoryContainer>
24
+ ),
25
+ ],
26
+ argTypes: {
27
+ size: {
28
+ control: 'select',
29
+ options: ['xs', 'sm', 'md', 'lg', 'xl', '2xl'],
30
+ },
31
+ showBadge: {
32
+ control: 'boolean',
33
+ },
34
+ },
35
+ args: {
36
+ name: 'John Doe',
37
+ size: 'md',
38
+ showBadge: false,
39
+ },
40
+ };
41
+
42
+ export default meta;
43
+
44
+ type Story = StoryObj<typeof Avatar>;
45
+
46
+ export const Default: Story = {};
47
+
48
+ export const Sizes: Story = {
49
+ render: () => (
50
+ <VStack space={spacing.lg}>
51
+ <Text weight="semiBold">Avatar Sizes</Text>
52
+ <HStack space={spacing.sm} alignItems="center">
53
+ <Avatar name="XS" size="xs" />
54
+ <Avatar name="SM" size="sm" />
55
+ <Avatar name="MD" size="md" />
56
+ <Avatar name="LG" size="lg" />
57
+ <Avatar name="XL" size="xl" />
58
+ <Avatar name="2XL" size="2xl" />
59
+ </HStack>
60
+ </VStack>
61
+ ),
62
+ };
63
+
64
+ export const WithInitials: Story = {
65
+ render: () => (
66
+ <VStack space={spacing.lg}>
67
+ <Text weight="semiBold">Avatars with Initials</Text>
68
+ <HStack space={spacing.sm}>
69
+ <Avatar name="John Doe" size="lg" />
70
+ <Avatar name="Jane Smith" size="lg" />
71
+ <Avatar name="Bob" size="lg" />
72
+ <Avatar name="Alice Williams" size="lg" />
73
+ </HStack>
74
+ </VStack>
75
+ ),
76
+ };
77
+
78
+ export const WithImage: Story = {
79
+ render: () => (
80
+ <VStack space={spacing.lg}>
81
+ <Text weight="semiBold">Avatars with Images</Text>
82
+ <HStack space={spacing.sm}>
83
+ <Avatar
84
+ src="https://i.pravatar.cc/150?img=1"
85
+ name="User 1"
86
+ size="lg"
87
+ />
88
+ <Avatar
89
+ src="https://i.pravatar.cc/150?img=2"
90
+ name="User 2"
91
+ size="lg"
92
+ />
93
+ <Avatar
94
+ src="https://i.pravatar.cc/150?img=3"
95
+ name="User 3"
96
+ size="lg"
97
+ />
98
+ </HStack>
99
+ </VStack>
100
+ ),
101
+ };
102
+
103
+ export const WithBadge: Story = {
104
+ render: () => (
105
+ <VStack space={spacing.lg}>
106
+ <Text weight="semiBold">Avatars with Status Badge</Text>
107
+ <HStack space={spacing.md}>
108
+ <Avatar name="Online User" size="lg" showBadge badgeColor={colors.feedback.success.content} />
109
+ <Avatar name="Away User" size="lg" showBadge badgeColor={colors.feedback.warning.content} />
110
+ <Avatar name="Offline User" size="lg" showBadge badgeColor={colors.feedback.error.content} />
111
+ </HStack>
112
+ </VStack>
113
+ ),
114
+ };
115
+
116
+ export const Group: Story = {
117
+ render: () => (
118
+ <VStack space={spacing.lg}>
119
+ <Text weight="semiBold">Avatar Group</Text>
120
+ <AvatarGroup max={3} size="md">
121
+ <Avatar name="John Doe" />
122
+ <Avatar name="Jane Smith" />
123
+ <Avatar name="Bob Wilson" />
124
+ <Avatar name="Alice Brown" />
125
+ <Avatar name="Charlie Davis" />
126
+ </AvatarGroup>
127
+ </VStack>
128
+ ),
129
+ };
130
+
131
+ export const Placeholder: Story = {
132
+ render: () => (
133
+ <VStack space={spacing.lg}>
134
+ <Text weight="semiBold">Avatar Placeholder</Text>
135
+ <HStack space={spacing.sm}>
136
+ <Avatar size="lg" />
137
+ <Avatar size="md" />
138
+ <Avatar size="sm" />
139
+ </HStack>
140
+ </VStack>
141
+ ),
142
+ };
143
+
144
+ /**
145
+ * Styles using design system tokens
146
+ */
147
+ const styles = StyleSheet.create({
148
+ container: {
149
+ padding: spacing.lg,
150
+ backgroundColor: colors.background.default,
151
+ borderRadius: borderRadius.lg,
152
+ },
153
+ });
154
+
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Avatar Component
3
+ * User profile picture or initials display
4
+ */
5
+
6
+ import React, { forwardRef, useState } from 'react';
7
+ import { View, ViewProps, Image, Text, StyleSheet } from 'react-native';
8
+ import { colors, typography } from '../../styles/tokens';
9
+
10
+ export interface AvatarProps extends ViewProps {
11
+ /** Image source */
12
+ src?: string;
13
+ /** Alt text / name for initials */
14
+ name?: string;
15
+ /** Avatar size */
16
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
17
+ /** Show status badge */
18
+ showBadge?: boolean;
19
+ /** Badge color */
20
+ badgeColor?: string;
21
+ /** Badge position */
22
+ badgePosition?: 'top-right' | 'bottom-right';
23
+ }
24
+
25
+ const sizeMap = {
26
+ xs: 24,
27
+ sm: 32,
28
+ md: 40,
29
+ lg: 48,
30
+ xl: 64,
31
+ '2xl': 96,
32
+ } as const;
33
+
34
+ const getInitials = (name: string): string => {
35
+ const parts = name.trim().split(' ');
36
+ if (parts.length >= 2) {
37
+ return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
38
+ }
39
+ return name.slice(0, 2).toUpperCase();
40
+ };
41
+
42
+ export const Avatar = forwardRef<View, AvatarProps>(
43
+ (
44
+ {
45
+ style,
46
+ src,
47
+ name,
48
+ size = 'md',
49
+ showBadge = false,
50
+ badgeColor,
51
+ badgePosition = 'bottom-right',
52
+ ...props
53
+ },
54
+ ref
55
+ ) => {
56
+ const [imageError, setImageError] = useState(false);
57
+
58
+ const avatarSize = sizeMap[size];
59
+ const badgeSize = Math.max(avatarSize * 0.25, 8);
60
+
61
+ const showImage = src && !imageError;
62
+ const showInitials = !showImage && name;
63
+
64
+ return (
65
+ <View
66
+ ref={ref}
67
+ style={[
68
+ styles.container,
69
+ { width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2 },
70
+ style,
71
+ ]}
72
+ {...props}
73
+ >
74
+ {showImage ? (
75
+ <Image
76
+ source={{ uri: src }}
77
+ style={[styles.image, { width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2 }]}
78
+ onError={() => setImageError(true)}
79
+ />
80
+ ) : showInitials ? (
81
+ <View style={[styles.initialsContainer, { width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2 }]}>
82
+ <Text style={[styles.initials, styles[`${size}Text` as keyof typeof styles]]}>{getInitials(name)}</Text>
83
+ </View>
84
+ ) : (
85
+ <View style={[styles.placeholder, { width: avatarSize, height: avatarSize, borderRadius: avatarSize / 2 }]}>
86
+ <Text style={[styles.placeholderIcon, styles[`${size}Text` as keyof typeof styles]]}>👤</Text>
87
+ </View>
88
+ )}
89
+ {showBadge && (
90
+ <View
91
+ style={[
92
+ styles.badge,
93
+ {
94
+ width: badgeSize,
95
+ height: badgeSize,
96
+ borderRadius: badgeSize / 2,
97
+ backgroundColor: badgeColor || colors.feedback.success.content,
98
+ },
99
+ badgePosition === 'top-right' ? styles.badgeTopRight : styles.badgeBottomRight,
100
+ ]}
101
+ />
102
+ )}
103
+ </View>
104
+ );
105
+ }
106
+ );
107
+
108
+ Avatar.displayName = 'Avatar';
109
+
110
+ export const AvatarGroup = forwardRef<View, ViewProps & { max?: number; size?: AvatarProps['size'] }>(
111
+ ({ children, max = 3, size = 'md', style, ...props }, ref) => {
112
+ const childArray = React.Children.toArray(children);
113
+ const visibleCount = Math.min(childArray.length, max);
114
+ const remainingCount = childArray.length - visibleCount;
115
+
116
+ return (
117
+ <View ref={ref} style={[styles.group, style]} {...props}>
118
+ {childArray.slice(0, visibleCount).map((child, index) => (
119
+ <View key={index} style={[styles.groupItem, { marginLeft: index > 0 ? -sizeMap[size] / 4 : 0 }]}>
120
+ {React.isValidElement(child)
121
+ ? React.cloneElement(child, { size } as any)
122
+ : child}
123
+ </View>
124
+ ))}
125
+ {remainingCount > 0 && (
126
+ <View style={[styles.groupItem, { marginLeft: -sizeMap[size] / 4 }]}>
127
+ <View
128
+ style={[
129
+ styles.remainingBadge,
130
+ { width: sizeMap[size], height: sizeMap[size], borderRadius: sizeMap[size] / 2 },
131
+ ]}
132
+ >
133
+ <Text style={styles.remainingText}>+{remainingCount}</Text>
134
+ </View>
135
+ </View>
136
+ )}
137
+ </View>
138
+ );
139
+ }
140
+ );
141
+
142
+ AvatarGroup.displayName = 'AvatarGroup';
143
+
144
+ const styles = StyleSheet.create({
145
+ container: {
146
+ position: 'relative',
147
+ overflow: 'hidden',
148
+ },
149
+ image: {
150
+ backgroundColor: colors.background.secondary,
151
+ },
152
+ initialsContainer: {
153
+ backgroundColor: colors.brand.blue,
154
+ alignItems: 'center',
155
+ justifyContent: 'center',
156
+ },
157
+ initials: {
158
+ color: colors.white,
159
+ fontWeight: typography.fontWeight.semiBold,
160
+ },
161
+ placeholder: {
162
+ backgroundColor: colors.background.tertiary,
163
+ alignItems: 'center',
164
+ justifyContent: 'center',
165
+ },
166
+ placeholderIcon: {
167
+ opacity: 0.5,
168
+ },
169
+ badge: {
170
+ position: 'absolute',
171
+ borderWidth: 2,
172
+ borderColor: colors.background.default,
173
+ },
174
+ badgeTopRight: {
175
+ top: 0,
176
+ right: 0,
177
+ },
178
+ badgeBottomRight: {
179
+ bottom: 0,
180
+ right: 0,
181
+ },
182
+ xsText: {
183
+ fontSize: 8,
184
+ },
185
+ smText: {
186
+ fontSize: 10,
187
+ },
188
+ mdText: {
189
+ fontSize: 12,
190
+ },
191
+ lgText: {
192
+ fontSize: 14,
193
+ },
194
+ xlText: {
195
+ fontSize: 18,
196
+ },
197
+ '2xlText': {
198
+ fontSize: 28,
199
+ },
200
+ group: {
201
+ flexDirection: 'row',
202
+ alignItems: 'center',
203
+ },
204
+ groupItem: {
205
+ borderWidth: 2,
206
+ borderColor: colors.background.default,
207
+ borderRadius: 9999,
208
+ },
209
+ remainingBadge: {
210
+ backgroundColor: colors.background.tertiary,
211
+ alignItems: 'center',
212
+ justifyContent: 'center',
213
+ },
214
+ remainingText: {
215
+ fontSize: typography.fontSize.small,
216
+ fontWeight: typography.fontWeight.semiBold,
217
+ color: colors.text.secondary,
218
+ },
219
+ });
@@ -0,0 +1,2 @@
1
+ export { Avatar, AvatarGroup, type AvatarProps } from './Avatar';
2
+
@@ -0,0 +1,146 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { Badge } from './Badge';
4
+ import { VStack } from '../VStack';
5
+ import { HStack } from '../HStack';
6
+ import { Text } from '../Text';
7
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
8
+
9
+ /**
10
+ * Story container with design system tokens
11
+ */
12
+ const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
13
+ <View style={styles.container}>{children}</View>
14
+ );
15
+
16
+ const meta: Meta<typeof Badge> = {
17
+ title: 'Components/Badge',
18
+ component: Badge,
19
+ decorators: [
20
+ (Story) => (
21
+ <StoryContainer>
22
+ <Story />
23
+ </StoryContainer>
24
+ ),
25
+ ],
26
+ argTypes: {
27
+ variant: {
28
+ control: 'select',
29
+ options: ['gray', 'red', 'orange', 'yellow', 'blue', 'green', 'darkGray', 'neonGreen', 'purple'],
30
+ },
31
+ size: {
32
+ control: 'select',
33
+ options: ['sm', 'md', 'lg'],
34
+ },
35
+ dot: {
36
+ control: 'boolean',
37
+ },
38
+ },
39
+ args: {
40
+ children: 'Badge',
41
+ variant: 'gray',
42
+ size: 'md',
43
+ dot: false,
44
+ },
45
+ };
46
+
47
+ export default meta;
48
+
49
+ type Story = StoryObj<typeof Badge>;
50
+
51
+ export const Default: Story = {};
52
+
53
+ export const AllVariants: Story = {
54
+ render: () => (
55
+ <VStack space={spacing.lg}>
56
+ <Text weight="semiBold">Badge Variants</Text>
57
+ <HStack space={spacing.sm} wrap>
58
+ <Badge variant="gray">Gray</Badge>
59
+ <Badge variant="red">Red</Badge>
60
+ <Badge variant="orange">Orange</Badge>
61
+ <Badge variant="yellow">Yellow</Badge>
62
+ <Badge variant="blue">Blue</Badge>
63
+ <Badge variant="green">Green</Badge>
64
+ <Badge variant="darkGray">Dark Gray</Badge>
65
+ <Badge variant="neonGreen">Neon Green</Badge>
66
+ <Badge variant="purple">Purple</Badge>
67
+ </HStack>
68
+ </VStack>
69
+ ),
70
+ };
71
+
72
+ export const Sizes: Story = {
73
+ render: () => (
74
+ <VStack space={spacing.lg}>
75
+ <Text weight="semiBold">Badge Sizes</Text>
76
+ <HStack space={spacing.sm} alignItems="center">
77
+ <Badge size="sm" variant="blue">Small</Badge>
78
+ <Badge size="md" variant="blue">Medium</Badge>
79
+ <Badge size="lg" variant="blue">Large</Badge>
80
+ </HStack>
81
+ </VStack>
82
+ ),
83
+ };
84
+
85
+ export const StatusBadges: Story = {
86
+ render: () => (
87
+ <VStack space={spacing.lg}>
88
+ <Text weight="semiBold">Status Badges</Text>
89
+ <HStack space={spacing.sm}>
90
+ <Badge variant="green">Active</Badge>
91
+ <Badge variant="yellow">Pending</Badge>
92
+ <Badge variant="red">Inactive</Badge>
93
+ <Badge variant="gray">Draft</Badge>
94
+ </HStack>
95
+ </VStack>
96
+ ),
97
+ };
98
+
99
+ export const RiskLevels: Story = {
100
+ render: () => (
101
+ <VStack space={spacing.lg}>
102
+ <Text weight="semiBold">Risk Levels</Text>
103
+ <HStack space={spacing.sm}>
104
+ <Badge variant="red">Very High</Badge>
105
+ <Badge variant="orange">High</Badge>
106
+ <Badge variant="yellow">Medium</Badge>
107
+ <Badge variant="blue">Low</Badge>
108
+ <Badge variant="green">Minimal</Badge>
109
+ </HStack>
110
+ </VStack>
111
+ ),
112
+ };
113
+
114
+ export const DotBadges: Story = {
115
+ render: () => (
116
+ <VStack space={spacing.lg}>
117
+ <Text weight="semiBold">Dot Badges</Text>
118
+ <HStack space={spacing.lg} alignItems="center">
119
+ <HStack space={spacing.sm} alignItems="center">
120
+ <Badge dot variant="green" size="sm" />
121
+ <Text variant="caption">Online</Text>
122
+ </HStack>
123
+ <HStack space={spacing.sm} alignItems="center">
124
+ <Badge dot variant="yellow" size="sm" />
125
+ <Text variant="caption">Away</Text>
126
+ </HStack>
127
+ <HStack space={spacing.sm} alignItems="center">
128
+ <Badge dot variant="red" size="sm" />
129
+ <Text variant="caption">Offline</Text>
130
+ </HStack>
131
+ </HStack>
132
+ </VStack>
133
+ ),
134
+ };
135
+
136
+ /**
137
+ * Styles using design system tokens
138
+ */
139
+ const styles = StyleSheet.create({
140
+ container: {
141
+ padding: spacing.lg,
142
+ backgroundColor: colors.background.default,
143
+ borderRadius: borderRadius.lg,
144
+ },
145
+ });
146
+
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Badge Component
3
+ * Status indicator and label component
4
+ */
5
+
6
+ import React, { forwardRef } from 'react';
7
+ import { View, ViewProps, Text, StyleSheet } from 'react-native';
8
+ import { colors, spacing, typography, borderRadius } from '../../styles/tokens';
9
+
10
+ export interface BadgeProps extends ViewProps {
11
+ /** Badge variant/color */
12
+ variant?: 'gray' | 'red' | 'orange' | 'yellow' | 'blue' | 'green' | 'darkGray' | 'neonGreen' | 'purple';
13
+ /** Badge size */
14
+ size?: 'sm' | 'md' | 'lg';
15
+ /** Badge content */
16
+ children: React.ReactNode;
17
+ /** Show as dot only */
18
+ dot?: boolean;
19
+ }
20
+
21
+ const bgColors: Record<string, string> = {
22
+ gray: colors.badge.gray,
23
+ red: colors.badge.red,
24
+ orange: colors.badge.orange,
25
+ yellow: colors.badge.yellow,
26
+ blue: colors.badge.blue,
27
+ green: colors.badge.green,
28
+ darkGray: colors.badge.darkGray,
29
+ neonGreen: colors.badge.neonGreen,
30
+ purple: colors.badge.purple,
31
+ };
32
+
33
+ const textColors: Record<string, string> = {
34
+ gray: colors.text.default,
35
+ red: colors.white,
36
+ orange: colors.white,
37
+ yellow: colors.text.default,
38
+ blue: colors.white,
39
+ green: colors.white,
40
+ darkGray: colors.white,
41
+ neonGreen: colors.white,
42
+ purple: colors.white,
43
+ };
44
+
45
+ export const Badge = forwardRef<View, BadgeProps>(
46
+ ({ style, variant = 'gray', size = 'md', dot = false, children, ...props }, ref) => {
47
+ if (dot) {
48
+ return (
49
+ <View
50
+ ref={ref}
51
+ style={[
52
+ styles.dot,
53
+ styles[`dot${size.charAt(0).toUpperCase() + size.slice(1)}` as keyof typeof styles],
54
+ { backgroundColor: bgColors[variant] },
55
+ style,
56
+ ]}
57
+ {...props}
58
+ />
59
+ );
60
+ }
61
+
62
+ return (
63
+ <View
64
+ ref={ref}
65
+ style={[styles.badge, styles[size], { backgroundColor: bgColors[variant] }, style]}
66
+ {...props}
67
+ >
68
+ <Text style={[styles.text, styles[`${size}Text` as keyof typeof styles], { color: textColors[variant] }]}>
69
+ {children}
70
+ </Text>
71
+ </View>
72
+ );
73
+ }
74
+ );
75
+
76
+ Badge.displayName = 'Badge';
77
+
78
+ const styles = StyleSheet.create({
79
+ badge: {
80
+ flexDirection: 'row',
81
+ alignItems: 'center',
82
+ justifyContent: 'center',
83
+ borderRadius: borderRadius.full,
84
+ },
85
+ sm: {
86
+ paddingHorizontal: spacing['2x'],
87
+ paddingVertical: spacing['0.5x'],
88
+ },
89
+ md: {
90
+ paddingHorizontal: spacing['3x'],
91
+ paddingVertical: spacing.base,
92
+ },
93
+ lg: {
94
+ paddingHorizontal: spacing['4x'],
95
+ paddingVertical: spacing['2x'],
96
+ },
97
+ dot: {
98
+ borderRadius: borderRadius.full,
99
+ },
100
+ dotSm: {
101
+ width: 6,
102
+ height: 6,
103
+ },
104
+ dotMd: {
105
+ width: 8,
106
+ height: 8,
107
+ },
108
+ dotLg: {
109
+ width: 10,
110
+ height: 10,
111
+ },
112
+ text: {
113
+ fontWeight: typography.fontWeight.semiBold,
114
+ },
115
+ smText: {
116
+ fontSize: 10,
117
+ },
118
+ mdText: {
119
+ fontSize: typography.fontSize.small,
120
+ },
121
+ lgText: {
122
+ fontSize: typography.fontSize.componentLabel,
123
+ },
124
+ });
125
+
@@ -0,0 +1,2 @@
1
+ export { Badge, type BadgeProps } from './Badge';
2
+