@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,255 @@
1
+ /**
2
+ * Accordion Component
3
+ * Expandable content sections
4
+ */
5
+
6
+ import React, { forwardRef, createContext, useContext, useState, useRef } from 'react';
7
+ import { View, ViewProps, Pressable, Text, Animated, LayoutAnimation, Platform, UIManager, StyleSheet } from 'react-native';
8
+ import Svg, { Path } from 'react-native-svg';
9
+ import { colors, spacing, typography } from '../../styles/tokens';
10
+
11
+ if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
12
+ UIManager.setLayoutAnimationEnabledExperimental(true);
13
+ }
14
+
15
+ /** Position of the expand/collapse icon */
16
+ export type IconPosition = 'left' | 'right';
17
+
18
+ interface AccordionContextValue {
19
+ expandedItems: string[];
20
+ toggleItem: (value: string) => void;
21
+ allowMultiple: boolean;
22
+ iconPosition: IconPosition;
23
+ }
24
+
25
+ const AccordionContext = createContext<AccordionContextValue | null>(null);
26
+
27
+ export interface AccordionProps extends ViewProps {
28
+ /** Allow multiple items to be expanded */
29
+ allowMultiple?: boolean;
30
+ /** Allow toggling items */
31
+ allowToggle?: boolean;
32
+ /** Default expanded items */
33
+ defaultIndex?: string[];
34
+ /** Controlled expanded items */
35
+ index?: string[];
36
+ /** On change handler */
37
+ onChange?: (expandedItems: string[]) => void;
38
+ /** Position of expand/collapse icon for all items */
39
+ iconPosition?: IconPosition;
40
+ }
41
+
42
+ export const Accordion = forwardRef<View, AccordionProps>(
43
+ (
44
+ {
45
+ style,
46
+ allowMultiple = false,
47
+ allowToggle = true,
48
+ defaultIndex = [],
49
+ index: controlledIndex,
50
+ onChange,
51
+ iconPosition = 'right',
52
+ children,
53
+ ...props
54
+ },
55
+ ref
56
+ ) => {
57
+ const [internalIndex, setInternalIndex] = useState<string[]>(defaultIndex);
58
+ const isControlled = controlledIndex !== undefined;
59
+ const expandedItems = isControlled ? controlledIndex : internalIndex;
60
+
61
+ const toggleItem = (value: string) => {
62
+ let newExpanded: string[];
63
+
64
+ if (expandedItems.includes(value)) {
65
+ if (!allowToggle && expandedItems.length === 1) return;
66
+ newExpanded = expandedItems.filter((v) => v !== value);
67
+ } else {
68
+ newExpanded = allowMultiple ? [...expandedItems, value] : [value];
69
+ }
70
+
71
+ if (!isControlled) {
72
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
73
+ setInternalIndex(newExpanded);
74
+ }
75
+ onChange?.(newExpanded);
76
+ };
77
+
78
+ return (
79
+ <AccordionContext.Provider value={{ expandedItems, toggleItem, allowMultiple, iconPosition }}>
80
+ <View ref={ref} style={[styles.accordion, style]} {...props}>
81
+ {children}
82
+ </View>
83
+ </AccordionContext.Provider>
84
+ );
85
+ }
86
+ );
87
+
88
+ Accordion.displayName = 'Accordion';
89
+
90
+ export interface AccordionItemProps extends ViewProps {
91
+ /** Item value for tracking expanded state */
92
+ value: string;
93
+ /** Is disabled */
94
+ isDisabled?: boolean;
95
+ }
96
+
97
+ export const AccordionItem = forwardRef<View, AccordionItemProps>(
98
+ ({ style, value, isDisabled = false, children, ...props }, ref) => {
99
+ const context = useContext(AccordionContext);
100
+ const isExpanded = context?.expandedItems.includes(value) ?? false;
101
+
102
+ return (
103
+ <View
104
+ ref={ref}
105
+ style={[styles.item, isDisabled && styles.itemDisabled, style]}
106
+ {...props}
107
+ >
108
+ {React.Children.map(children, (child) => {
109
+ if (React.isValidElement(child)) {
110
+ return React.cloneElement(child as React.ReactElement<any>, {
111
+ _value: value,
112
+ _isExpanded: isExpanded,
113
+ _isDisabled: isDisabled,
114
+ });
115
+ }
116
+ return child;
117
+ })}
118
+ </View>
119
+ );
120
+ }
121
+ );
122
+
123
+ AccordionItem.displayName = 'AccordionItem';
124
+
125
+ export interface AccordionButtonProps extends ViewProps {
126
+ _value?: string;
127
+ _isExpanded?: boolean;
128
+ _isDisabled?: boolean;
129
+ /** Override icon position for this button */
130
+ iconPosition?: IconPosition;
131
+ }
132
+
133
+ export const AccordionButton = forwardRef<View, AccordionButtonProps>(
134
+ ({ style, _value, _isExpanded, _isDisabled, iconPosition, children, ...props }, ref) => {
135
+ const context = useContext(AccordionContext);
136
+ const rotateAnim = useRef(new Animated.Value(_isExpanded ? 1 : 0)).current;
137
+
138
+ // Use prop override, fall back to context, then default to 'right'
139
+ const iconPos = iconPosition ?? context?.iconPosition ?? 'right';
140
+
141
+ React.useEffect(() => {
142
+ Animated.timing(rotateAnim, {
143
+ toValue: _isExpanded ? 1 : 0,
144
+ duration: 200,
145
+ useNativeDriver: true,
146
+ }).start();
147
+ }, [_isExpanded]);
148
+
149
+ const rotation = rotateAnim.interpolate({
150
+ inputRange: [0, 1],
151
+ outputRange: ['0deg', '180deg'],
152
+ });
153
+
154
+ const handlePress = () => {
155
+ if (_isDisabled || !_value) return;
156
+ context?.toggleItem(_value);
157
+ };
158
+
159
+ const ChevronIcon = (
160
+ <Animated.View style={[{ transform: [{ rotate: rotation }] }, iconPos === 'left' && styles.iconLeft]}>
161
+ <Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
162
+ <Path
163
+ d="M6 9l6 6 6-6"
164
+ stroke={_isDisabled ? colors.text.disabled : colors.text.secondary}
165
+ strokeWidth={2}
166
+ strokeLinecap="round"
167
+ strokeLinejoin="round"
168
+ />
169
+ </Svg>
170
+ </Animated.View>
171
+ );
172
+
173
+ return (
174
+ <Pressable
175
+ ref={ref}
176
+ onPress={handlePress}
177
+ disabled={_isDisabled}
178
+ style={[styles.button, style]}
179
+ accessibilityRole="button"
180
+ accessibilityState={{ expanded: _isExpanded, disabled: _isDisabled }}
181
+ {...props}
182
+ >
183
+ {iconPos === 'left' && ChevronIcon}
184
+ <View style={styles.buttonContent}>
185
+ {typeof children === 'string' ? (
186
+ <Text style={[styles.buttonText, _isDisabled && styles.buttonTextDisabled]}>
187
+ {children}
188
+ </Text>
189
+ ) : (
190
+ children
191
+ )}
192
+ </View>
193
+ {iconPos === 'right' && ChevronIcon}
194
+ </Pressable>
195
+ );
196
+ }
197
+ );
198
+
199
+ AccordionButton.displayName = 'AccordionButton';
200
+
201
+ export interface AccordionPanelProps extends ViewProps {
202
+ _isExpanded?: boolean;
203
+ }
204
+
205
+ export const AccordionPanel = forwardRef<View, AccordionPanelProps>(
206
+ ({ style, _isExpanded, children, ...props }, ref) => {
207
+ if (!_isExpanded) return null;
208
+
209
+ return (
210
+ <View ref={ref} style={[styles.panel, style]} {...props}>
211
+ {children}
212
+ </View>
213
+ );
214
+ }
215
+ );
216
+
217
+ AccordionPanel.displayName = 'AccordionPanel';
218
+
219
+ const styles = StyleSheet.create({
220
+ accordion: {
221
+ width: '100%',
222
+ },
223
+ item: {
224
+ borderBottomWidth: 1,
225
+ borderBottomColor: colors.border.disabled,
226
+ },
227
+ itemDisabled: {
228
+ opacity: 0.5,
229
+ },
230
+ button: {
231
+ flexDirection: 'row',
232
+ alignItems: 'center',
233
+ justifyContent: 'space-between',
234
+ paddingVertical: spacing['4x'],
235
+ paddingHorizontal: spacing['2x'],
236
+ },
237
+ buttonContent: {
238
+ flex: 1,
239
+ },
240
+ buttonText: {
241
+ fontSize: typography.fontSize.body,
242
+ fontWeight: typography.fontWeight.medium,
243
+ color: colors.text.default,
244
+ },
245
+ buttonTextDisabled: {
246
+ color: colors.text.disabled,
247
+ },
248
+ iconLeft: {
249
+ marginRight: spacing['2x'],
250
+ },
251
+ panel: {
252
+ paddingHorizontal: spacing['2x'],
253
+ paddingBottom: spacing['4x'],
254
+ },
255
+ });
@@ -0,0 +1,12 @@
1
+ export {
2
+ Accordion,
3
+ AccordionItem,
4
+ AccordionButton,
5
+ AccordionPanel,
6
+ type AccordionProps,
7
+ type AccordionItemProps,
8
+ type AccordionButtonProps,
9
+ type AccordionPanelProps,
10
+ type IconPosition,
11
+ } from './Accordion';
12
+
@@ -0,0 +1,393 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { ActionSheet } from './ActionSheet';
4
+ import { Button } from '../Button';
5
+ import { VStack } from '../VStack';
6
+ import { HStack } from '../HStack';
7
+ import { Text } from '../Text';
8
+ import { Box } from '../Box';
9
+ import Svg, { Path } from 'react-native-svg';
10
+ import { colors, spacing, borderRadius } from '../../styles/tokens';
11
+
12
+ const meta: Meta<typeof ActionSheet> = {
13
+ title: 'Overlay/ActionSheet',
14
+ component: ActionSheet,
15
+ argTypes: {
16
+ showCancel: { control: 'boolean' },
17
+ },
18
+ args: {
19
+ showCancel: true,
20
+ cancelText: 'Cancel',
21
+ },
22
+ };
23
+
24
+ export default meta;
25
+
26
+ type Story = StoryObj<typeof ActionSheet>;
27
+
28
+ const ActionSheetDemo = () => {
29
+ const [isOpen, setIsOpen] = useState(false);
30
+
31
+ return (
32
+ <VStack space={spacing.lg}>
33
+ <Button onPress={() => setIsOpen(true)}>Open Action Sheet</Button>
34
+ <ActionSheet
35
+ isOpen={isOpen}
36
+ onClose={() => setIsOpen(false)}
37
+ title="Actions"
38
+ items={[
39
+ { label: 'Edit', value: 'edit' },
40
+ { label: 'Duplicate', value: 'duplicate' },
41
+ { label: 'Share', value: 'share' },
42
+ { label: 'Delete', value: 'delete', isDestructive: true },
43
+ ]}
44
+ onItemPress={(value) => console.log('Selected:', value)}
45
+ />
46
+ </VStack>
47
+ );
48
+ };
49
+
50
+ export const Default: Story = {
51
+ render: () => <ActionSheetDemo />,
52
+ };
53
+
54
+ const WithTitleAndDescription = () => {
55
+ const [isOpen, setIsOpen] = useState(false);
56
+
57
+ return (
58
+ <VStack space={spacing.lg}>
59
+ <Text weight="semiBold">With Title and Description</Text>
60
+ <Button onPress={() => setIsOpen(true)}>Choose Action</Button>
61
+ <ActionSheet
62
+ isOpen={isOpen}
63
+ onClose={() => setIsOpen(false)}
64
+ title="What would you like to do?"
65
+ description="Select an action from the list below"
66
+ items={[
67
+ { label: 'View Details', value: 'view' },
68
+ { label: 'Edit Item', value: 'edit' },
69
+ { label: 'Move to Folder', value: 'move' },
70
+ ]}
71
+ onItemPress={(value) => {
72
+ console.log('Selected:', value);
73
+ setIsOpen(false);
74
+ }}
75
+ />
76
+ </VStack>
77
+ );
78
+ };
79
+
80
+ export const WithDescription: Story = {
81
+ render: () => <WithTitleAndDescription />,
82
+ };
83
+
84
+ const DestructiveActions = () => {
85
+ const [isOpen, setIsOpen] = useState(false);
86
+
87
+ return (
88
+ <VStack space={spacing.lg}>
89
+ <Text weight="semiBold">With Destructive Action</Text>
90
+ <Button variant="danger" onPress={() => setIsOpen(true)}>
91
+ Delete Item
92
+ </Button>
93
+ <ActionSheet
94
+ isOpen={isOpen}
95
+ onClose={() => setIsOpen(false)}
96
+ title="Delete this item?"
97
+ description="This action cannot be undone"
98
+ items={[
99
+ { label: 'Delete Permanently', value: 'delete', isDestructive: true },
100
+ { label: 'Move to Trash', value: 'trash' },
101
+ ]}
102
+ onItemPress={(value) => {
103
+ console.log('Selected:', value);
104
+ setIsOpen(false);
105
+ }}
106
+ />
107
+ </VStack>
108
+ );
109
+ };
110
+
111
+ export const Destructive: Story = {
112
+ render: () => <DestructiveActions />,
113
+ };
114
+
115
+ const DisabledOptions = () => {
116
+ const [isOpen, setIsOpen] = useState(false);
117
+
118
+ return (
119
+ <VStack space={spacing.lg}>
120
+ <Text weight="semiBold">With Disabled Options</Text>
121
+ <Button onPress={() => setIsOpen(true)}>Export Options</Button>
122
+ <ActionSheet
123
+ isOpen={isOpen}
124
+ onClose={() => setIsOpen(false)}
125
+ title="Export As"
126
+ items={[
127
+ { label: 'PDF', value: 'pdf' },
128
+ { label: 'Word Document', value: 'docx' },
129
+ { label: 'Excel (Pro only)', value: 'xlsx', isDisabled: true },
130
+ { label: 'CSV (Pro only)', value: 'csv', isDisabled: true },
131
+ ]}
132
+ onItemPress={(value) => {
133
+ console.log('Selected:', value);
134
+ setIsOpen(false);
135
+ }}
136
+ />
137
+ </VStack>
138
+ );
139
+ };
140
+
141
+ export const Disabled: Story = {
142
+ render: () => <DisabledOptions />,
143
+ };
144
+
145
+ const WithIcons = () => {
146
+ const [isOpen, setIsOpen] = useState(false);
147
+
148
+ const EditIcon = (
149
+ <Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
150
+ <Path
151
+ 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"
152
+ stroke={colors.brand.blue}
153
+ strokeWidth={2}
154
+ strokeLinecap="round"
155
+ strokeLinejoin="round"
156
+ />
157
+ </Svg>
158
+ );
159
+
160
+ const ShareIcon = (
161
+ <Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
162
+ <Path
163
+ d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
164
+ stroke={colors.brand.blue}
165
+ strokeWidth={2}
166
+ strokeLinecap="round"
167
+ strokeLinejoin="round"
168
+ />
169
+ </Svg>
170
+ );
171
+
172
+ const CopyIcon = (
173
+ <Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
174
+ <Path
175
+ d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
176
+ stroke={colors.brand.blue}
177
+ strokeWidth={2}
178
+ strokeLinecap="round"
179
+ strokeLinejoin="round"
180
+ />
181
+ </Svg>
182
+ );
183
+
184
+ const TrashIcon = (
185
+ <Svg width={20} height={20} viewBox="0 0 24 24" fill="none">
186
+ <Path
187
+ 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"
188
+ stroke={colors.feedback.error.content}
189
+ strokeWidth={2}
190
+ strokeLinecap="round"
191
+ strokeLinejoin="round"
192
+ />
193
+ </Svg>
194
+ );
195
+
196
+ return (
197
+ <VStack space={spacing.lg}>
198
+ <Text weight="semiBold">With Icons</Text>
199
+ <Button onPress={() => setIsOpen(true)}>More Options</Button>
200
+ <ActionSheet
201
+ isOpen={isOpen}
202
+ onClose={() => setIsOpen(false)}
203
+ title="Options"
204
+ items={[
205
+ { label: 'Edit', value: 'edit', icon: EditIcon },
206
+ { label: 'Share', value: 'share', icon: ShareIcon },
207
+ { label: 'Copy Link', value: 'copy', icon: CopyIcon },
208
+ { label: 'Delete', value: 'delete', icon: TrashIcon, isDestructive: true },
209
+ ]}
210
+ onItemPress={(value) => {
211
+ console.log('Selected:', value);
212
+ setIsOpen(false);
213
+ }}
214
+ />
215
+ </VStack>
216
+ );
217
+ };
218
+
219
+ export const Icons: Story = {
220
+ render: () => <WithIcons />,
221
+ };
222
+
223
+ const PhotoPicker = () => {
224
+ const [isOpen, setIsOpen] = useState(false);
225
+
226
+ return (
227
+ <VStack space={spacing.lg}>
228
+ <Text weight="semiBold">Photo Picker</Text>
229
+ <Box
230
+ w={100}
231
+ h={100}
232
+ bg={colors.brand.blue + '20'}
233
+ rounded="full"
234
+ alignItems="center"
235
+ justifyContent="center"
236
+ style={{ alignSelf: 'center' }}
237
+ >
238
+ <Text size="lg">📷</Text>
239
+ </Box>
240
+ <Button variant="secondary" onPress={() => setIsOpen(true)}>
241
+ Change Photo
242
+ </Button>
243
+ <ActionSheet
244
+ isOpen={isOpen}
245
+ onClose={() => setIsOpen(false)}
246
+ title="Change Profile Photo"
247
+ items={[
248
+ { label: 'Take Photo', value: 'camera' },
249
+ { label: 'Choose from Library', value: 'library' },
250
+ { label: 'Choose from Files', value: 'files' },
251
+ { label: 'Remove Photo', value: 'remove', isDestructive: true },
252
+ ]}
253
+ onItemPress={(value) => {
254
+ console.log('Selected:', value);
255
+ setIsOpen(false);
256
+ }}
257
+ />
258
+ </VStack>
259
+ );
260
+ };
261
+
262
+ export const PhotoPickerExample: Story = {
263
+ render: () => <PhotoPicker />,
264
+ };
265
+
266
+ const SortOptions = () => {
267
+ const [isOpen, setIsOpen] = useState(false);
268
+ const [sortBy, setSortBy] = useState('date');
269
+
270
+ return (
271
+ <VStack space={spacing.lg}>
272
+ <Text weight="semiBold">Sort Options</Text>
273
+ <HStack justifyContent="space-between" alignItems="center">
274
+ <Text>Current sort: {sortBy}</Text>
275
+ <Button size="sm" variant="outline" onPress={() => setIsOpen(true)}>
276
+ Sort By
277
+ </Button>
278
+ </HStack>
279
+ <ActionSheet
280
+ isOpen={isOpen}
281
+ onClose={() => setIsOpen(false)}
282
+ title="Sort By"
283
+ items={[
284
+ { label: 'Date (Newest First)', value: 'date' },
285
+ { label: 'Name (A-Z)', value: 'name_asc' },
286
+ { label: 'Name (Z-A)', value: 'name_desc' },
287
+ { label: 'Size (Largest)', value: 'size_desc' },
288
+ { label: 'Size (Smallest)', value: 'size_asc' },
289
+ ]}
290
+ onItemPress={(value) => {
291
+ setSortBy(value);
292
+ setIsOpen(false);
293
+ }}
294
+ />
295
+ </VStack>
296
+ );
297
+ };
298
+
299
+ export const Sort: Story = {
300
+ render: () => <SortOptions />,
301
+ };
302
+
303
+ const ShareSheet = () => {
304
+ const [isOpen, setIsOpen] = useState(false);
305
+
306
+ return (
307
+ <VStack space={spacing.lg}>
308
+ <Text weight="semiBold">Share Sheet</Text>
309
+ <Button onPress={() => setIsOpen(true)}>Share</Button>
310
+ <ActionSheet
311
+ isOpen={isOpen}
312
+ onClose={() => setIsOpen(false)}
313
+ title="Share via"
314
+ items={[
315
+ { label: 'Messages', value: 'messages' },
316
+ { label: 'Email', value: 'email' },
317
+ { label: 'Copy Link', value: 'copy' },
318
+ { label: 'Twitter', value: 'twitter' },
319
+ { label: 'Facebook', value: 'facebook' },
320
+ { label: 'WhatsApp', value: 'whatsapp' },
321
+ ]}
322
+ onItemPress={(value) => {
323
+ console.log('Share via:', value);
324
+ setIsOpen(false);
325
+ }}
326
+ />
327
+ </VStack>
328
+ );
329
+ };
330
+
331
+ export const Share: Story = {
332
+ render: () => <ShareSheet />,
333
+ };
334
+
335
+ const FileOptions = () => {
336
+ const [isOpen, setIsOpen] = useState(false);
337
+ const [selectedFile, setSelectedFile] = useState('');
338
+
339
+ const files = [
340
+ { name: 'Report.pdf', size: '2.4 MB' },
341
+ { name: 'Photo.jpg', size: '1.2 MB' },
342
+ { name: 'Presentation.pptx', size: '5.8 MB' },
343
+ ];
344
+
345
+ return (
346
+ <VStack space={spacing.lg}>
347
+ <Text weight="semiBold">File Actions</Text>
348
+ <Box p={spacing.lg} bg={colors.background.secondary} rounded="lg">
349
+ <VStack space={spacing.sm}>
350
+ {files.map((file) => (
351
+ <HStack key={file.name} justifyContent="space-between" alignItems="center">
352
+ <VStack space={spacing.xs}>
353
+ <Text>{file.name}</Text>
354
+ <Text size="sm" color={colors.text.secondary}>{file.size}</Text>
355
+ </VStack>
356
+ <Button
357
+ size="sm"
358
+ variant="ghost"
359
+ onPress={() => {
360
+ setSelectedFile(file.name);
361
+ setIsOpen(true);
362
+ }}
363
+ >
364
+
365
+ </Button>
366
+ </HStack>
367
+ ))}
368
+ </VStack>
369
+ </Box>
370
+ <ActionSheet
371
+ isOpen={isOpen}
372
+ onClose={() => setIsOpen(false)}
373
+ title={selectedFile}
374
+ items={[
375
+ { label: 'Open', value: 'open' },
376
+ { label: 'Rename', value: 'rename' },
377
+ { label: 'Move', value: 'move' },
378
+ { label: 'Download', value: 'download' },
379
+ { label: 'Delete', value: 'delete', isDestructive: true },
380
+ ]}
381
+ onItemPress={(value) => {
382
+ console.log(`${value} ${selectedFile}`);
383
+ setIsOpen(false);
384
+ }}
385
+ />
386
+ </VStack>
387
+ );
388
+ };
389
+
390
+ export const FileActions: Story = {
391
+ render: () => <FileOptions />,
392
+ };
393
+