@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,342 @@
1
+ /**
2
+ * Drawer Component
3
+ * Slide-in panel from screen edges
4
+ */
5
+
6
+ import React, { forwardRef, useRef, useEffect } from 'react';
7
+ import {
8
+ View,
9
+ ViewProps,
10
+ Modal,
11
+ Pressable,
12
+ Text,
13
+ Animated,
14
+ Dimensions,
15
+ ScrollView,
16
+ StyleSheet,
17
+ } from 'react-native';
18
+ import Svg, { Path } from 'react-native-svg';
19
+ import { colors, spacing, borderRadius, typography, elevation } from '../../styles/tokens';
20
+
21
+ export interface DrawerProps extends ViewProps {
22
+ /** Is open */
23
+ isOpen: boolean;
24
+ /** On close handler */
25
+ onClose: () => void;
26
+ /** Drawer title */
27
+ title?: string;
28
+ /** Placement */
29
+ placement?: 'left' | 'right' | 'top' | 'bottom';
30
+ /** Drawer size */
31
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
32
+ /** Close on overlay click */
33
+ closeOnOverlayClick?: boolean;
34
+ /** Show close button */
35
+ showCloseButton?: boolean;
36
+ }
37
+
38
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
39
+
40
+ const sizeConfig = {
41
+ sm: { width: 280, height: 280 },
42
+ md: { width: 320, height: 320 },
43
+ lg: { width: 400, height: 400 },
44
+ xl: { width: 480, height: 480 },
45
+ full: { width: SCREEN_WIDTH, height: SCREEN_HEIGHT },
46
+ };
47
+
48
+ export const Drawer = forwardRef<View, DrawerProps>(
49
+ (
50
+ {
51
+ style,
52
+ isOpen,
53
+ onClose,
54
+ title,
55
+ placement = 'left',
56
+ size = 'md',
57
+ closeOnOverlayClick = true,
58
+ showCloseButton = true,
59
+ children,
60
+ ...props
61
+ },
62
+ ref
63
+ ) => {
64
+ const translateAnim = useRef(new Animated.Value(0)).current;
65
+ const opacityAnim = useRef(new Animated.Value(0)).current;
66
+
67
+ const config = sizeConfig[size];
68
+ const isHorizontal = placement === 'left' || placement === 'right';
69
+ const drawerSize = isHorizontal ? config.width : config.height;
70
+
71
+ const getInitialTranslate = () => {
72
+ switch (placement) {
73
+ case 'left':
74
+ return -drawerSize;
75
+ case 'right':
76
+ return drawerSize;
77
+ case 'top':
78
+ return -drawerSize;
79
+ case 'bottom':
80
+ return drawerSize;
81
+ }
82
+ };
83
+
84
+ useEffect(() => {
85
+ if (isOpen) {
86
+ translateAnim.setValue(getInitialTranslate());
87
+ Animated.parallel([
88
+ Animated.spring(translateAnim, {
89
+ toValue: 0,
90
+ useNativeDriver: true,
91
+ tension: 65,
92
+ friction: 11,
93
+ }),
94
+ Animated.timing(opacityAnim, {
95
+ toValue: 1,
96
+ duration: 200,
97
+ useNativeDriver: true,
98
+ }),
99
+ ]).start();
100
+ } else {
101
+ Animated.parallel([
102
+ Animated.timing(translateAnim, {
103
+ toValue: getInitialTranslate(),
104
+ duration: 200,
105
+ useNativeDriver: true,
106
+ }),
107
+ Animated.timing(opacityAnim, {
108
+ toValue: 0,
109
+ duration: 200,
110
+ useNativeDriver: true,
111
+ }),
112
+ ]).start();
113
+ }
114
+ }, [isOpen, placement, drawerSize]);
115
+
116
+ const getDrawerStyle = () => {
117
+ const base: any = {
118
+ position: 'absolute',
119
+ backgroundColor: colors.background.default,
120
+ ...elevation['40'],
121
+ };
122
+
123
+ switch (placement) {
124
+ case 'left':
125
+ return {
126
+ ...base,
127
+ left: 0,
128
+ top: 0,
129
+ bottom: 0,
130
+ width: config.width,
131
+ transform: [{ translateX: translateAnim }],
132
+ };
133
+ case 'right':
134
+ return {
135
+ ...base,
136
+ right: 0,
137
+ top: 0,
138
+ bottom: 0,
139
+ width: config.width,
140
+ transform: [{ translateX: translateAnim }],
141
+ };
142
+ case 'top':
143
+ return {
144
+ ...base,
145
+ left: 0,
146
+ right: 0,
147
+ top: 0,
148
+ height: config.height,
149
+ transform: [{ translateY: translateAnim }],
150
+ };
151
+ case 'bottom':
152
+ return {
153
+ ...base,
154
+ left: 0,
155
+ right: 0,
156
+ bottom: 0,
157
+ height: config.height,
158
+ borderTopLeftRadius: borderRadius.xl,
159
+ borderTopRightRadius: borderRadius.xl,
160
+ transform: [{ translateY: translateAnim }],
161
+ };
162
+ }
163
+ };
164
+
165
+ return (
166
+ <Modal
167
+ visible={isOpen}
168
+ transparent
169
+ animationType="none"
170
+ onRequestClose={onClose}
171
+ >
172
+ <View style={styles.container}>
173
+ <Animated.View style={[styles.overlay, { opacity: opacityAnim }]}>
174
+ <Pressable
175
+ style={styles.overlayPressable}
176
+ onPress={closeOnOverlayClick ? onClose : undefined}
177
+ />
178
+ </Animated.View>
179
+ <Animated.View ref={ref} style={[getDrawerStyle(), style]} {...props}>
180
+ {/* Only show built-in header when title is provided */}
181
+ {title ? (
182
+ <View style={styles.drawerHeader}>
183
+ <Text style={styles.titleText}>{title}</Text>
184
+ {showCloseButton && (
185
+ <Pressable onPress={onClose} style={styles.closeButton}>
186
+ <Svg width={24} height={24} viewBox="0 0 24 24" fill="none">
187
+ <Path
188
+ d="M18 6L6 18M6 6l12 12"
189
+ stroke={colors.text.secondary}
190
+ strokeWidth={2}
191
+ strokeLinecap="round"
192
+ strokeLinejoin="round"
193
+ />
194
+ </Svg>
195
+ </Pressable>
196
+ )}
197
+ </View>
198
+ ) : (
199
+ /* When no title, show floating close button (user may use DrawerHeader) */
200
+ showCloseButton && (
201
+ <Pressable onPress={onClose} style={styles.floatingCloseButton}>
202
+ <Svg width={24} height={24} viewBox="0 0 24 24" fill="none">
203
+ <Path
204
+ d="M18 6L6 18M6 6l12 12"
205
+ stroke={colors.text.secondary}
206
+ strokeWidth={2}
207
+ strokeLinecap="round"
208
+ strokeLinejoin="round"
209
+ />
210
+ </Svg>
211
+ </Pressable>
212
+ )
213
+ )}
214
+ <ScrollView
215
+ style={styles.content}
216
+ contentContainerStyle={title ? styles.contentContainer : styles.contentContainerNoTitle}
217
+ >
218
+ {children}
219
+ </ScrollView>
220
+ </Animated.View>
221
+ </View>
222
+ </Modal>
223
+ );
224
+ }
225
+ );
226
+
227
+ Drawer.displayName = 'Drawer';
228
+
229
+ export interface DrawerHeaderProps extends ViewProps {}
230
+ export interface DrawerBodyProps extends ViewProps {}
231
+ export interface DrawerFooterProps extends ViewProps {}
232
+
233
+ export const DrawerHeader = forwardRef<View, DrawerHeaderProps>(
234
+ ({ style, children, ...props }, ref) => {
235
+ return (
236
+ <View ref={ref} style={[styles.header, style]} {...props}>
237
+ {typeof children === 'string' ? (
238
+ <Text style={styles.headerText}>{children}</Text>
239
+ ) : (
240
+ children
241
+ )}
242
+ </View>
243
+ );
244
+ }
245
+ );
246
+
247
+ DrawerHeader.displayName = 'DrawerHeader';
248
+
249
+ export const DrawerBody = forwardRef<View, DrawerBodyProps>(
250
+ ({ style, children, ...props }, ref) => {
251
+ return (
252
+ <View ref={ref} style={[styles.body, style]} {...props}>
253
+ {children}
254
+ </View>
255
+ );
256
+ }
257
+ );
258
+
259
+ DrawerBody.displayName = 'DrawerBody';
260
+
261
+ export const DrawerFooter = forwardRef<View, DrawerFooterProps>(
262
+ ({ style, children, ...props }, ref) => {
263
+ return (
264
+ <View ref={ref} style={[styles.footer, style]} {...props}>
265
+ {children}
266
+ </View>
267
+ );
268
+ }
269
+ );
270
+
271
+ DrawerFooter.displayName = 'DrawerFooter';
272
+
273
+ const styles = StyleSheet.create({
274
+ container: {
275
+ flex: 1,
276
+ },
277
+ overlay: {
278
+ ...StyleSheet.absoluteFillObject,
279
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
280
+ },
281
+ overlayPressable: {
282
+ flex: 1,
283
+ },
284
+ drawerHeader: {
285
+ flexDirection: 'row',
286
+ alignItems: 'center',
287
+ justifyContent: 'space-between',
288
+ paddingHorizontal: spacing['4x'],
289
+ paddingTop: spacing['4x'],
290
+ paddingBottom: spacing['3x'],
291
+ borderBottomWidth: 1,
292
+ borderBottomColor: colors.border.disabled,
293
+ },
294
+ titleText: {
295
+ fontSize: typography.fontSize.h3,
296
+ fontWeight: typography.fontWeight.semiBold,
297
+ color: colors.text.default,
298
+ flex: 1,
299
+ },
300
+ closeButton: {
301
+ padding: spacing.base,
302
+ },
303
+ floatingCloseButton: {
304
+ position: 'absolute',
305
+ top: spacing['3x'],
306
+ right: spacing['3x'],
307
+ zIndex: 10,
308
+ padding: spacing.base,
309
+ },
310
+ content: {
311
+ flex: 1,
312
+ },
313
+ contentContainer: {
314
+ paddingTop: spacing['2x'],
315
+ },
316
+ contentContainerNoTitle: {
317
+ paddingTop: spacing['12x'],
318
+ },
319
+ header: {
320
+ padding: spacing['4x'],
321
+ paddingBottom: spacing['2x'],
322
+ borderBottomWidth: 1,
323
+ borderBottomColor: colors.border.disabled,
324
+ },
325
+ headerText: {
326
+ fontSize: typography.fontSize.h3,
327
+ fontWeight: typography.fontWeight.semiBold,
328
+ color: colors.text.default,
329
+ },
330
+ body: {
331
+ padding: spacing['4x'],
332
+ flex: 1,
333
+ },
334
+ footer: {
335
+ flexDirection: 'row',
336
+ justifyContent: 'flex-end',
337
+ padding: spacing['4x'],
338
+ gap: spacing['2x'],
339
+ borderTopWidth: 1,
340
+ borderTopColor: colors.border.disabled,
341
+ },
342
+ });
@@ -0,0 +1,11 @@
1
+ export {
2
+ Drawer,
3
+ DrawerHeader,
4
+ DrawerBody,
5
+ DrawerFooter,
6
+ type DrawerProps,
7
+ type DrawerHeaderProps,
8
+ type DrawerBodyProps,
9
+ type DrawerFooterProps,
10
+ } from './Drawer';
11
+
@@ -0,0 +1,360 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Fab } from './Fab';
3
+ import { VStack } from '../VStack';
4
+ import { HStack } from '../HStack';
5
+ import { Text } from '../Text';
6
+ import { Box } from '../Box';
7
+ import Svg, { Path } from 'react-native-svg';
8
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
9
+
10
+ const meta: Meta<typeof Fab> = {
11
+ title: 'Action/Fab',
12
+ component: Fab,
13
+ argTypes: {
14
+ size: {
15
+ control: 'select',
16
+ options: ['sm', 'md', 'lg'],
17
+ },
18
+ colorScheme: {
19
+ control: 'select',
20
+ options: ['primary', 'secondary', 'danger'],
21
+ },
22
+ placement: {
23
+ control: 'select',
24
+ options: ['bottom-right', 'bottom-left', 'bottom-center', 'top-right', 'top-left'],
25
+ },
26
+ isExtended: { control: 'boolean' },
27
+ isLoading: { control: 'boolean' },
28
+ isDisabled: { control: 'boolean' },
29
+ },
30
+ args: {
31
+ size: 'md',
32
+ colorScheme: 'primary',
33
+ placement: 'bottom-right',
34
+ isExtended: false,
35
+ isLoading: false,
36
+ isDisabled: false,
37
+ },
38
+ };
39
+
40
+ export default meta;
41
+
42
+ type Story = StoryObj<typeof Fab>;
43
+
44
+ export const Default: Story = {
45
+ render: (args) => (
46
+ <Box h={200} position="relative">
47
+ <Fab {...args} onPress={() => console.log('FAB pressed')} />
48
+ </Box>
49
+ ),
50
+ };
51
+
52
+ export const Sizes: Story = {
53
+ render: () => (
54
+ <VStack space={spacing.lg}>
55
+ <Text weight="semiBold">FAB Sizes</Text>
56
+ <HStack space={spacing.lg} alignItems="center">
57
+ <VStack space={spacing.xs} alignItems="center">
58
+ <Fab size="sm" style={{ position: 'relative' }} />
59
+ <Text size="sm" color={colors.text.secondary}>Small</Text>
60
+ </VStack>
61
+ <VStack space={spacing.xs} alignItems="center">
62
+ <Fab size="md" style={{ position: 'relative' }} />
63
+ <Text size="sm" color={colors.text.secondary}>Medium</Text>
64
+ </VStack>
65
+ <VStack space={spacing.xs} alignItems="center">
66
+ <Fab size="lg" style={{ position: 'relative' }} />
67
+ <Text size="sm" color={colors.text.secondary}>Large</Text>
68
+ </VStack>
69
+ </HStack>
70
+ </VStack>
71
+ ),
72
+ };
73
+
74
+ export const ColorSchemes: Story = {
75
+ render: () => (
76
+ <VStack space={spacing.lg}>
77
+ <Text weight="semiBold">Color Schemes</Text>
78
+ <HStack space={spacing.lg} alignItems="center">
79
+ <VStack space={spacing.xs} alignItems="center">
80
+ <Fab colorScheme="primary" style={{ position: 'relative' }} />
81
+ <Text size="sm" color={colors.text.secondary}>Primary</Text>
82
+ </VStack>
83
+ <VStack space={spacing.xs} alignItems="center">
84
+ <Fab colorScheme="secondary" style={{ position: 'relative' }} />
85
+ <Text size="sm" color={colors.text.secondary}>Secondary</Text>
86
+ </VStack>
87
+ <VStack space={spacing.xs} alignItems="center">
88
+ <Fab colorScheme="danger" style={{ position: 'relative' }} />
89
+ <Text size="sm" color={colors.text.secondary}>Danger</Text>
90
+ </VStack>
91
+ </HStack>
92
+ </VStack>
93
+ ),
94
+ };
95
+
96
+ export const CustomIcons: Story = {
97
+ render: () => {
98
+ const EditIcon = (
99
+ <Svg width={24} height={24} viewBox="0 0 24 24" fill="none">
100
+ <Path
101
+ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
102
+ stroke="white"
103
+ strokeWidth={2}
104
+ strokeLinecap="round"
105
+ strokeLinejoin="round"
106
+ />
107
+ </Svg>
108
+ );
109
+
110
+ const MessageIcon = (
111
+ <Svg width={24} height={24} viewBox="0 0 24 24" fill="none">
112
+ <Path
113
+ d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
114
+ stroke="white"
115
+ strokeWidth={2}
116
+ strokeLinecap="round"
117
+ strokeLinejoin="round"
118
+ />
119
+ </Svg>
120
+ );
121
+
122
+ const DeleteIcon = (
123
+ <Svg width={24} height={24} viewBox="0 0 24 24" fill="none">
124
+ <Path
125
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
126
+ stroke="white"
127
+ strokeWidth={2}
128
+ strokeLinecap="round"
129
+ strokeLinejoin="round"
130
+ />
131
+ </Svg>
132
+ );
133
+
134
+ return (
135
+ <VStack space={spacing.lg}>
136
+ <Text weight="semiBold">Custom Icons</Text>
137
+ <HStack space={spacing.lg} alignItems="center">
138
+ <VStack space={spacing.xs} alignItems="center">
139
+ <Fab icon={EditIcon} style={{ position: 'relative' }} />
140
+ <Text size="sm" color={colors.text.secondary}>Edit</Text>
141
+ </VStack>
142
+ <VStack space={spacing.xs} alignItems="center">
143
+ <Fab icon={MessageIcon} style={{ position: 'relative' }} />
144
+ <Text size="sm" color={colors.text.secondary}>Message</Text>
145
+ </VStack>
146
+ <VStack space={spacing.xs} alignItems="center">
147
+ <Fab icon={DeleteIcon} colorScheme="danger" style={{ position: 'relative' }} />
148
+ <Text size="sm" color={colors.text.secondary}>Delete</Text>
149
+ </VStack>
150
+ </HStack>
151
+ </VStack>
152
+ );
153
+ },
154
+ };
155
+
156
+ export const Extended: Story = {
157
+ render: () => (
158
+ <VStack space={spacing.lg}>
159
+ <Text weight="semiBold">Extended FAB</Text>
160
+ <VStack space={spacing.md}>
161
+ <Fab isExtended label="Create" style={{ position: 'relative' }} />
162
+ <Fab isExtended label="Add Item" colorScheme="secondary" style={{ position: 'relative' }} />
163
+ <Fab isExtended label="Delete" colorScheme="danger" style={{ position: 'relative' }} />
164
+ </VStack>
165
+ </VStack>
166
+ ),
167
+ };
168
+
169
+ export const ExtendedWithCustomIcon: Story = {
170
+ render: () => {
171
+ const PlusIcon = (
172
+ <Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
173
+ <Path
174
+ d="M12 5v14M5 12h14"
175
+ stroke="white"
176
+ strokeWidth={2}
177
+ strokeLinecap="round"
178
+ strokeLinejoin="round"
179
+ />
180
+ </Svg>
181
+ );
182
+
183
+ return (
184
+ <VStack space={spacing.lg}>
185
+ <Text weight="semiBold">Extended FAB with Custom Icon</Text>
186
+ <Fab
187
+ isExtended
188
+ label="New Project"
189
+ icon={PlusIcon}
190
+ style={{ position: 'relative' }}
191
+ />
192
+ </VStack>
193
+ );
194
+ },
195
+ };
196
+
197
+ export const States: Story = {
198
+ render: () => (
199
+ <VStack space={spacing.lg}>
200
+ <Text weight="semiBold">FAB States</Text>
201
+ <HStack space={spacing.lg} alignItems="center">
202
+ <VStack space={spacing.xs} alignItems="center">
203
+ <Fab style={{ position: 'relative' }} />
204
+ <Text size="sm" color={colors.text.secondary}>Normal</Text>
205
+ </VStack>
206
+ <VStack space={spacing.xs} alignItems="center">
207
+ <Fab isLoading style={{ position: 'relative' }} />
208
+ <Text size="sm" color={colors.text.secondary}>Loading</Text>
209
+ </VStack>
210
+ <VStack space={spacing.xs} alignItems="center">
211
+ <Fab isDisabled style={{ position: 'relative' }} />
212
+ <Text size="sm" color={colors.text.secondary}>Disabled</Text>
213
+ </VStack>
214
+ </HStack>
215
+ </VStack>
216
+ ),
217
+ };
218
+
219
+ export const Placement: Story = {
220
+ render: () => (
221
+ <VStack space={spacing.lg}>
222
+ <Text weight="semiBold">FAB Placement (in container)</Text>
223
+ <Box h={300} bg={colors.background.secondary} rounded="lg" position="relative" overflow="hidden">
224
+ <Fab
225
+ placement="top-left"
226
+ size="sm"
227
+ colorScheme="secondary"
228
+ />
229
+ <Fab
230
+ placement="top-right"
231
+ size="sm"
232
+ colorScheme="secondary"
233
+ />
234
+ <Fab
235
+ placement="bottom-left"
236
+ size="sm"
237
+ colorScheme="secondary"
238
+ />
239
+ <Fab
240
+ placement="bottom-right"
241
+ size="sm"
242
+ />
243
+ <Fab
244
+ placement="bottom-center"
245
+ size="sm"
246
+ colorScheme="secondary"
247
+ />
248
+ </Box>
249
+ </VStack>
250
+ ),
251
+ };
252
+
253
+ export const InContext: Story = {
254
+ render: () => (
255
+ <VStack space={16}>
256
+ <Text weight="semiBold">FAB in Context</Text>
257
+ <Box h={400} bg="#f8fafc" rounded="lg" position="relative" overflow="hidden">
258
+ <Box p={16}>
259
+ <VStack space={12}>
260
+ <Text weight="semiBold" size="lg">My Tasks</Text>
261
+ {['Complete project proposal', 'Review pull requests', 'Update documentation', 'Team meeting at 3pm'].map((task, i) => (
262
+ <Box key={i} p={12} bg="#ffffff" rounded="md" shadow="5">
263
+ <HStack space={8} alignItems="center">
264
+ <Box w={20} h={20} rounded="sm" borderWidth={2} borderColor="#cbd5e1" />
265
+ <Text>{task}</Text>
266
+ </HStack>
267
+ </Box>
268
+ ))}
269
+ </VStack>
270
+ </Box>
271
+ <Fab
272
+ placement="bottom-right"
273
+ isExtended
274
+ label="Add Task"
275
+ onPress={() => console.log('Add task')}
276
+ />
277
+ </Box>
278
+ </VStack>
279
+ ),
280
+ };
281
+
282
+ export const ScrollingContent: Story = {
283
+ render: () => (
284
+ <VStack space={16}>
285
+ <Text weight="semiBold">FAB with Scrolling Content</Text>
286
+ <Box h={350} bg="#f8fafc" rounded="lg" position="relative" overflow="hidden">
287
+ <Box p={16} style={{ height: '100%', overflow: 'scroll' }}>
288
+ <VStack space={8}>
289
+ {Array.from({ length: 15 }, (_, i) => (
290
+ <Box key={i} p={16} bg="#ffffff" rounded="md">
291
+ <Text>Item {i + 1}</Text>
292
+ </Box>
293
+ ))}
294
+ </VStack>
295
+ </Box>
296
+ <Fab placement="bottom-right" />
297
+ </Box>
298
+ </VStack>
299
+ ),
300
+ };
301
+
302
+ export const ChatApplication: Story = {
303
+ render: () => {
304
+ const MessageIcon = (
305
+ <Svg width={24} height={24} viewBox="0 0 24 24" fill="none">
306
+ <Path
307
+ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
308
+ stroke="white"
309
+ strokeWidth={2}
310
+ strokeLinecap="round"
311
+ strokeLinejoin="round"
312
+ />
313
+ </Svg>
314
+ );
315
+
316
+ return (
317
+ <VStack space={16}>
318
+ <Text weight="semiBold">Chat Application</Text>
319
+ <Box h={400} bg="#f8fafc" rounded="lg" position="relative" overflow="hidden">
320
+ <Box p={16}>
321
+ <VStack space={8}>
322
+ {[
323
+ { user: 'John', message: 'Hey, how are you?' },
324
+ { user: 'You', message: 'I\'m good, thanks!' },
325
+ { user: 'John', message: 'Great! Want to grab lunch?' },
326
+ ].map((msg, i) => (
327
+ <Box
328
+ key={i}
329
+ p={12}
330
+ bg={msg.user === 'You' ? '#3b82f6' : '#ffffff'}
331
+ rounded="lg"
332
+ shadow="5"
333
+ alignSelf={msg.user === 'You' ? 'flex-end' : 'flex-start'}
334
+ maxW="80%"
335
+ >
336
+ <Text
337
+ size="sm"
338
+ weight="medium"
339
+ style={{ color: msg.user === 'You' ? 'white' : '#64748b' }}
340
+ >
341
+ {msg.user}
342
+ </Text>
343
+ <Text style={{ color: msg.user === 'You' ? 'white' : undefined }}>
344
+ {msg.message}
345
+ </Text>
346
+ </Box>
347
+ ))}
348
+ </VStack>
349
+ </Box>
350
+ <Fab
351
+ placement="bottom-right"
352
+ icon={MessageIcon}
353
+ onPress={() => console.log('New message')}
354
+ />
355
+ </Box>
356
+ </VStack>
357
+ );
358
+ },
359
+ };
360
+