@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,215 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { Center } from './Center';
4
+ import { VStack } from '../VStack';
5
+ import { Box } from '../Box';
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 Center> = {
17
+ title: 'Layout/Center',
18
+ component: Center,
19
+ decorators: [
20
+ (Story) => (
21
+ <StoryContainer>
22
+ <Story />
23
+ </StoryContainer>
24
+ ),
25
+ ],
26
+ };
27
+
28
+ export default meta;
29
+
30
+ type Story = StoryObj<typeof Center>;
31
+
32
+ export const Default: Story = {
33
+ render: () => (
34
+ <Center style={styles.infoBox}>
35
+ <Text>Centered Content</Text>
36
+ </Center>
37
+ ),
38
+ };
39
+
40
+ export const WithBox: Story = {
41
+ render: () => (
42
+ <VStack space={spacing.lg}>
43
+ <Text weight="semiBold">Center with Various Content</Text>
44
+ <Center style={[styles.infoBox, { height: 150 }]}>
45
+ <Box p={spacing.lg} bg={colors.brand.blue} rounded="lg">
46
+ <Text color={colors.white}>Centered Box</Text>
47
+ </Box>
48
+ </Center>
49
+ </VStack>
50
+ ),
51
+ };
52
+
53
+ export const SquareContent: Story = {
54
+ render: () => (
55
+ <VStack space={spacing.lg}>
56
+ <Text weight="semiBold">Centered Square Elements</Text>
57
+ <Center style={styles.warningBox}>
58
+ <Box
59
+ w={80}
60
+ h={80}
61
+ bg={colors.feedback.warning.content}
62
+ rounded="lg"
63
+ alignItems="center"
64
+ justifyContent="center"
65
+ >
66
+ <Text color={colors.white} style={styles.largeText}>A</Text>
67
+ </Box>
68
+ </Center>
69
+ </VStack>
70
+ ),
71
+ };
72
+
73
+ export const CircularContent: Story = {
74
+ render: () => (
75
+ <VStack space={spacing.lg}>
76
+ <Text weight="semiBold">Centered Circular Element</Text>
77
+ <Center style={styles.successBox}>
78
+ <Box
79
+ w={100}
80
+ h={100}
81
+ bg={colors.feedback.success.content}
82
+ rounded="full"
83
+ alignItems="center"
84
+ justifyContent="center"
85
+ >
86
+ <Text color={colors.white} style={styles.xlText}>✓</Text>
87
+ </Box>
88
+ </Center>
89
+ </VStack>
90
+ ),
91
+ };
92
+
93
+ export const MultipleElements: Story = {
94
+ render: () => (
95
+ <VStack space={spacing.lg}>
96
+ <Text weight="semiBold">Centered Stack of Elements</Text>
97
+ <Center style={styles.errorBox}>
98
+ <VStack space={spacing.md} alignItems="center">
99
+ <Box
100
+ w={60}
101
+ h={60}
102
+ bg={colors.danger.default}
103
+ rounded="full"
104
+ alignItems="center"
105
+ justifyContent="center"
106
+ >
107
+ <Text color={colors.white} style={styles.largeText}>👤</Text>
108
+ </Box>
109
+ <Text weight="semiBold">Welcome Back!</Text>
110
+ <Text variant="small" color={colors.text.secondary}>
111
+ Please sign in to continue
112
+ </Text>
113
+ </VStack>
114
+ </Center>
115
+ </VStack>
116
+ ),
117
+ };
118
+
119
+ export const NestedCenters: Story = {
120
+ render: () => (
121
+ <VStack space={spacing.lg}>
122
+ <Text weight="semiBold">Nested Centers</Text>
123
+ <Center style={styles.nestedOuter}>
124
+ <Center style={styles.nestedMiddle}>
125
+ <Center style={styles.nestedInner}>
126
+ <Text color={colors.white}>Deep</Text>
127
+ </Center>
128
+ </Center>
129
+ </Center>
130
+ </VStack>
131
+ ),
132
+ };
133
+
134
+ export const LoadingState: Story = {
135
+ render: () => (
136
+ <VStack space={spacing.lg}>
137
+ <Text weight="semiBold">Loading State Example</Text>
138
+ <Center style={styles.loadingBox}>
139
+ <VStack space={spacing.sm} alignItems="center">
140
+ <Box
141
+ w={40}
142
+ h={40}
143
+ bg={colors.brand.blue}
144
+ rounded="full"
145
+ alignItems="center"
146
+ justifyContent="center"
147
+ >
148
+ <Text color={colors.white}>⏳</Text>
149
+ </Box>
150
+ <Text color={colors.text.secondary}>Loading...</Text>
151
+ </VStack>
152
+ </Center>
153
+ </VStack>
154
+ ),
155
+ };
156
+
157
+ /**
158
+ * Styles using design system tokens
159
+ */
160
+ const styles = StyleSheet.create({
161
+ container: {
162
+ padding: spacing.lg,
163
+ backgroundColor: colors.background.default,
164
+ borderRadius: borderRadius.lg,
165
+ },
166
+ infoBox: {
167
+ height: 200,
168
+ backgroundColor: colors.feedback.info.background,
169
+ borderRadius: borderRadius.lg,
170
+ },
171
+ warningBox: {
172
+ height: 200,
173
+ backgroundColor: colors.feedback.warning.background,
174
+ borderRadius: borderRadius.lg,
175
+ },
176
+ successBox: {
177
+ height: 200,
178
+ backgroundColor: colors.feedback.success.background,
179
+ borderRadius: borderRadius.lg,
180
+ },
181
+ errorBox: {
182
+ height: 250,
183
+ backgroundColor: colors.feedback.error.background,
184
+ borderRadius: borderRadius.lg,
185
+ },
186
+ loadingBox: {
187
+ height: 200,
188
+ backgroundColor: colors.background.tertiary,
189
+ borderRadius: borderRadius.lg,
190
+ },
191
+ nestedOuter: {
192
+ height: 300,
193
+ backgroundColor: colors.feedback.info.background,
194
+ borderRadius: borderRadius.lg,
195
+ },
196
+ nestedMiddle: {
197
+ width: 200,
198
+ height: 200,
199
+ backgroundColor: colors.feedback.info.border,
200
+ borderRadius: borderRadius.lg,
201
+ },
202
+ nestedInner: {
203
+ width: 100,
204
+ height: 100,
205
+ backgroundColor: colors.brand.blue,
206
+ borderRadius: borderRadius.lg,
207
+ },
208
+ largeText: {
209
+ fontSize: 24,
210
+ },
211
+ xlText: {
212
+ fontSize: 32,
213
+ },
214
+ });
215
+
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Center Component
3
+ * Centers its children both horizontally and vertically
4
+ */
5
+
6
+ import React, { forwardRef } from 'react';
7
+ import { View, ViewProps, StyleSheet } from 'react-native';
8
+
9
+ export interface CenterProps extends ViewProps {}
10
+
11
+ export const Center = forwardRef<View, CenterProps>(
12
+ ({ style, children, ...props }, ref) => {
13
+ return (
14
+ <View ref={ref} style={[styles.center, style]} {...props}>
15
+ {children}
16
+ </View>
17
+ );
18
+ }
19
+ );
20
+
21
+ Center.displayName = 'Center';
22
+
23
+ const styles = StyleSheet.create({
24
+ center: {
25
+ alignItems: 'center',
26
+ justifyContent: 'center',
27
+ },
28
+ });
29
+
@@ -0,0 +1,2 @@
1
+ export { Center, type CenterProps } from './Center';
2
+
@@ -0,0 +1,94 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { Checkbox, CheckboxGroup } from './Checkbox';
4
+ import { VStack } from '../VStack';
5
+ import { Text } from '../Text';
6
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
7
+
8
+ /**
9
+ * Story container with design system tokens
10
+ */
11
+ const StoryContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => (
12
+ <View style={styles.container}>{children}</View>
13
+ );
14
+
15
+ const meta: Meta<typeof Checkbox> = {
16
+ title: 'Components/Checkbox',
17
+ component: Checkbox,
18
+ decorators: [
19
+ (Story) => (
20
+ <StoryContainer>
21
+ <Story />
22
+ </StoryContainer>
23
+ ),
24
+ ],
25
+ args: {
26
+ label: 'Accept terms and conditions',
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+
32
+ type Story = StoryObj<typeof Checkbox>;
33
+
34
+ export const Default: Story = {};
35
+
36
+ export const Sizes: Story = {
37
+ render: () => (
38
+ <VStack space={spacing.md}>
39
+ <Checkbox label="Small checkbox" size="sm" />
40
+ <Checkbox label="Medium checkbox" size="md" />
41
+ <Checkbox label="Large checkbox" size="lg" />
42
+ </VStack>
43
+ ),
44
+ };
45
+
46
+ export const States: Story = {
47
+ render: () => (
48
+ <VStack space={spacing.md}>
49
+ <Checkbox label="Unchecked" />
50
+ <Checkbox label="Checked" defaultChecked />
51
+ <Checkbox label="Disabled" isDisabled />
52
+ <Checkbox label="Disabled Checked" isDisabled defaultChecked />
53
+ <Checkbox label="Invalid" isInvalid />
54
+ <Checkbox label="Indeterminate" isIndeterminate />
55
+ </VStack>
56
+ ),
57
+ };
58
+
59
+ export const ColorSchemes: Story = {
60
+ render: () => (
61
+ <VStack space={spacing.md}>
62
+ <Checkbox label="Primary" colorScheme="primary" defaultChecked />
63
+ <Checkbox label="Success" colorScheme="success" defaultChecked />
64
+ <Checkbox label="Danger" colorScheme="danger" defaultChecked />
65
+ </VStack>
66
+ ),
67
+ };
68
+
69
+ export const Group: Story = {
70
+ render: () => (
71
+ <CheckboxGroup
72
+ label="Select your interests"
73
+ defaultValue={['design']}
74
+ direction="column"
75
+ >
76
+ <Checkbox value="design" label="Design" />
77
+ <Checkbox value="development" label="Development" />
78
+ <Checkbox value="marketing" label="Marketing" />
79
+ <Checkbox value="sales" label="Sales" />
80
+ </CheckboxGroup>
81
+ ),
82
+ };
83
+
84
+ /**
85
+ * Styles using design system tokens
86
+ */
87
+ const styles = StyleSheet.create({
88
+ container: {
89
+ padding: spacing.lg,
90
+ backgroundColor: colors.background.default,
91
+ borderRadius: borderRadius.lg,
92
+ },
93
+ });
94
+
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Checkbox Component
3
+ * Selectable checkbox with label
4
+ */
5
+
6
+ import React, { forwardRef } from 'react';
7
+ import { View, ViewProps, Pressable, Text, StyleSheet } from 'react-native';
8
+ import Svg, { Path } from 'react-native-svg';
9
+ import { colors, spacing, borderRadius, typography } from '../../styles/tokens';
10
+
11
+ export interface CheckboxProps extends Omit<ViewProps, 'children'> {
12
+ /** Checkbox label */
13
+ label?: string;
14
+ /** Is checked */
15
+ isChecked?: boolean;
16
+ /** Default checked state (uncontrolled) */
17
+ defaultChecked?: boolean;
18
+ /** On change handler */
19
+ onChange?: (isChecked: boolean) => void;
20
+ /** Is disabled */
21
+ isDisabled?: boolean;
22
+ /** Is invalid */
23
+ isInvalid?: boolean;
24
+ /** Checkbox size */
25
+ size?: 'sm' | 'md' | 'lg';
26
+ /** Checkbox color */
27
+ colorScheme?: 'primary' | 'success' | 'danger';
28
+ /** Is indeterminate */
29
+ isIndeterminate?: boolean;
30
+ }
31
+
32
+ const sizeMap = {
33
+ sm: 16,
34
+ md: 20,
35
+ lg: 24,
36
+ } as const;
37
+
38
+ const colorMap = {
39
+ primary: colors.brand.blue,
40
+ success: colors.feedback.success.content,
41
+ danger: colors.feedback.error.content,
42
+ };
43
+
44
+ export const Checkbox = forwardRef<View, CheckboxProps>(
45
+ (
46
+ {
47
+ style,
48
+ label,
49
+ isChecked: controlledChecked,
50
+ defaultChecked = false,
51
+ onChange,
52
+ isDisabled = false,
53
+ isInvalid = false,
54
+ size = 'md',
55
+ colorScheme = 'primary',
56
+ isIndeterminate = false,
57
+ ...props
58
+ },
59
+ ref
60
+ ) => {
61
+ const [internalChecked, setInternalChecked] = React.useState(defaultChecked);
62
+ const isControlled = controlledChecked !== undefined;
63
+ const isChecked = isControlled ? controlledChecked : internalChecked;
64
+
65
+ const boxSize = sizeMap[size];
66
+ const checkColor = colorMap[colorScheme];
67
+
68
+ const handlePress = () => {
69
+ if (isDisabled) return;
70
+ const newValue = !isChecked;
71
+ if (!isControlled) {
72
+ setInternalChecked(newValue);
73
+ }
74
+ onChange?.(newValue);
75
+ };
76
+
77
+ return (
78
+ <Pressable
79
+ ref={ref}
80
+ onPress={handlePress}
81
+ disabled={isDisabled}
82
+ style={[styles.container, style]}
83
+ accessibilityRole="checkbox"
84
+ accessibilityState={{ checked: isIndeterminate ? 'mixed' : isChecked, disabled: isDisabled }}
85
+ {...props}
86
+ >
87
+ <View
88
+ style={[
89
+ styles.checkbox,
90
+ { width: boxSize, height: boxSize },
91
+ (isChecked || isIndeterminate) && { backgroundColor: checkColor, borderColor: checkColor },
92
+ isInvalid && styles.invalid,
93
+ isDisabled && styles.disabled,
94
+ ]}
95
+ >
96
+ {isChecked && !isIndeterminate && (
97
+ <Svg width={boxSize - 4} height={boxSize - 4} viewBox="0 0 24 24" fill="none">
98
+ <Path
99
+ d="M20 6L9 17l-5-5"
100
+ stroke={colors.white}
101
+ strokeWidth={3}
102
+ strokeLinecap="round"
103
+ strokeLinejoin="round"
104
+ />
105
+ </Svg>
106
+ )}
107
+ {isIndeterminate && (
108
+ <View style={[styles.indeterminate, { width: boxSize - 8, height: 2 }]} />
109
+ )}
110
+ </View>
111
+ {label && (
112
+ <Text style={[styles.label, styles[`${size}Label` as keyof typeof styles], isDisabled && styles.labelDisabled]}>
113
+ {label}
114
+ </Text>
115
+ )}
116
+ </Pressable>
117
+ );
118
+ }
119
+ );
120
+
121
+ Checkbox.displayName = 'Checkbox';
122
+
123
+ export interface CheckboxGroupProps extends ViewProps {
124
+ /** Group label */
125
+ label?: string;
126
+ /** Selected values */
127
+ value?: string[];
128
+ /** Default selected values */
129
+ defaultValue?: string[];
130
+ /** On change handler */
131
+ onChange?: (values: string[]) => void;
132
+ /** Is disabled */
133
+ isDisabled?: boolean;
134
+ /** Direction */
135
+ direction?: 'row' | 'column';
136
+ }
137
+
138
+ export const CheckboxGroup = forwardRef<View, CheckboxGroupProps>(
139
+ (
140
+ {
141
+ style,
142
+ label,
143
+ value: controlledValue,
144
+ defaultValue = [],
145
+ onChange,
146
+ isDisabled = false,
147
+ direction = 'column',
148
+ children,
149
+ ...props
150
+ },
151
+ ref
152
+ ) => {
153
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
154
+ const isControlled = controlledValue !== undefined;
155
+ const values = isControlled ? controlledValue : internalValue;
156
+
157
+ const handleChange = (itemValue: string, isChecked: boolean) => {
158
+ const newValues = isChecked
159
+ ? [...values, itemValue]
160
+ : values.filter((v) => v !== itemValue);
161
+
162
+ if (!isControlled) {
163
+ setInternalValue(newValues);
164
+ }
165
+ onChange?.(newValues);
166
+ };
167
+
168
+ return (
169
+ <View ref={ref} style={[styles.group, style]} {...props}>
170
+ {label && <Text style={styles.groupLabel}>{label}</Text>}
171
+ <View style={[styles.groupContent, { flexDirection: direction }]}>
172
+ {React.Children.map(children, (child) => {
173
+ if (React.isValidElement<CheckboxProps & { value?: string }>(child)) {
174
+ const itemValue = (child.props as any).value;
175
+ return React.cloneElement(child, {
176
+ isChecked: itemValue ? values.includes(itemValue) : child.props.isChecked,
177
+ onChange: itemValue
178
+ ? (checked: boolean) => handleChange(itemValue, checked)
179
+ : child.props.onChange,
180
+ isDisabled: isDisabled || child.props.isDisabled,
181
+ });
182
+ }
183
+ return child;
184
+ })}
185
+ </View>
186
+ </View>
187
+ );
188
+ }
189
+ );
190
+
191
+ CheckboxGroup.displayName = 'CheckboxGroup';
192
+
193
+ const styles = StyleSheet.create({
194
+ container: {
195
+ flexDirection: 'row',
196
+ alignItems: 'center',
197
+ },
198
+ checkbox: {
199
+ borderWidth: 2,
200
+ borderColor: colors.border.default,
201
+ borderRadius: borderRadius.sm,
202
+ alignItems: 'center',
203
+ justifyContent: 'center',
204
+ },
205
+ invalid: {
206
+ borderColor: colors.border.danger,
207
+ },
208
+ disabled: {
209
+ backgroundColor: colors.background.secondary,
210
+ borderColor: colors.border.disabled,
211
+ },
212
+ indeterminate: {
213
+ backgroundColor: colors.white,
214
+ borderRadius: 1,
215
+ },
216
+ label: {
217
+ marginLeft: spacing['2x'],
218
+ color: colors.text.default,
219
+ },
220
+ smLabel: {
221
+ fontSize: typography.fontSize.small,
222
+ },
223
+ mdLabel: {
224
+ fontSize: typography.fontSize.body,
225
+ },
226
+ lgLabel: {
227
+ fontSize: typography.fontSize.body,
228
+ },
229
+ labelDisabled: {
230
+ color: colors.text.disabled,
231
+ },
232
+ group: {},
233
+ groupLabel: {
234
+ fontSize: typography.fontSize.componentLabel,
235
+ fontWeight: typography.fontWeight.semiBold,
236
+ color: colors.text.default,
237
+ marginBottom: spacing['2x'],
238
+ },
239
+ groupContent: {
240
+ gap: spacing['3x'],
241
+ },
242
+ });
@@ -0,0 +1,2 @@
1
+ export { Checkbox, CheckboxGroup, type CheckboxProps, type CheckboxGroupProps } from './Checkbox';
2
+