@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,330 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { AlertDialog } from './AlertDialog';
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 { colors, spacing, borderRadius, elevation } from '../../styles/tokens';
10
+
11
+ const meta: Meta<typeof AlertDialog> = {
12
+ title: 'Overlay/AlertDialog',
13
+ component: AlertDialog,
14
+ argTypes: {
15
+ isDestructive: { control: 'boolean' },
16
+ isLoading: { control: 'boolean' },
17
+ closeOnOverlayClick: { control: 'boolean' },
18
+ },
19
+ args: {
20
+ title: 'Confirm Action',
21
+ description: 'Are you sure you want to proceed with this action?',
22
+ isDestructive: false,
23
+ isLoading: false,
24
+ closeOnOverlayClick: true,
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+
30
+ type Story = StoryObj<typeof AlertDialog>;
31
+
32
+ const AlertDialogDemo = () => {
33
+ const [isOpen, setIsOpen] = useState(false);
34
+
35
+ return (
36
+ <VStack space={spacing.lg}>
37
+ <Button onPress={() => setIsOpen(true)}>Open Alert Dialog</Button>
38
+ <AlertDialog
39
+ isOpen={isOpen}
40
+ onClose={() => setIsOpen(false)}
41
+ title="Confirm Action"
42
+ description="Are you sure you want to proceed with this action? This cannot be undone."
43
+ onConfirm={() => console.log('Confirmed')}
44
+ onCancel={() => console.log('Cancelled')}
45
+ />
46
+ </VStack>
47
+ );
48
+ };
49
+
50
+ export const Default: Story = {
51
+ render: () => <AlertDialogDemo />,
52
+ };
53
+
54
+ const DestructiveDialog = () => {
55
+ const [isOpen, setIsOpen] = useState(false);
56
+
57
+ return (
58
+ <VStack space={spacing.lg}>
59
+ <Text weight="semiBold">Destructive Action</Text>
60
+ <Button variant="danger" onPress={() => setIsOpen(true)}>
61
+ Delete Account
62
+ </Button>
63
+ <AlertDialog
64
+ isOpen={isOpen}
65
+ onClose={() => setIsOpen(false)}
66
+ title="Delete Account"
67
+ description="Are you sure you want to delete your account? All of your data will be permanently removed. This action cannot be undone."
68
+ confirmText="Delete"
69
+ cancelText="Cancel"
70
+ isDestructive
71
+ onConfirm={() => {
72
+ console.log('Account deleted');
73
+ setIsOpen(false);
74
+ }}
75
+ />
76
+ </VStack>
77
+ );
78
+ };
79
+
80
+ export const Destructive: Story = {
81
+ render: () => <DestructiveDialog />,
82
+ };
83
+
84
+ const CustomButtonsDialog = () => {
85
+ const [isOpen, setIsOpen] = useState(false);
86
+
87
+ return (
88
+ <VStack space={spacing.lg}>
89
+ <Text weight="semiBold">Custom Button Text</Text>
90
+ <Button onPress={() => setIsOpen(true)}>Publish Post</Button>
91
+ <AlertDialog
92
+ isOpen={isOpen}
93
+ onClose={() => setIsOpen(false)}
94
+ title="Publish Post"
95
+ description="Your post will be visible to all users. Are you ready to publish?"
96
+ confirmText="Publish Now"
97
+ cancelText="Save as Draft"
98
+ onConfirm={() => {
99
+ console.log('Published');
100
+ setIsOpen(false);
101
+ }}
102
+ onCancel={() => {
103
+ console.log('Saved as draft');
104
+ setIsOpen(false);
105
+ }}
106
+ />
107
+ </VStack>
108
+ );
109
+ };
110
+
111
+ export const CustomButtons: Story = {
112
+ render: () => <CustomButtonsDialog />,
113
+ };
114
+
115
+ const LoadingDialog = () => {
116
+ const [isOpen, setIsOpen] = useState(false);
117
+ const [isLoading, setIsLoading] = useState(false);
118
+
119
+ const handleConfirm = () => {
120
+ setIsLoading(true);
121
+ setTimeout(() => {
122
+ setIsLoading(false);
123
+ setIsOpen(false);
124
+ }, 2000);
125
+ };
126
+
127
+ return (
128
+ <VStack space={spacing.lg}>
129
+ <Text weight="semiBold">With Loading State</Text>
130
+ <Button onPress={() => setIsOpen(true)}>Process Payment</Button>
131
+ <AlertDialog
132
+ isOpen={isOpen}
133
+ onClose={() => setIsOpen(false)}
134
+ title="Confirm Payment"
135
+ description="You are about to charge $99.99 to your card ending in 4242."
136
+ confirmText="Pay Now"
137
+ isLoading={isLoading}
138
+ onConfirm={handleConfirm}
139
+ />
140
+ </VStack>
141
+ );
142
+ };
143
+
144
+ export const Loading: Story = {
145
+ render: () => <LoadingDialog />,
146
+ };
147
+
148
+ const DeleteItemDialog = () => {
149
+ const [isOpen, setIsOpen] = useState(false);
150
+ const [selectedItem, setSelectedItem] = useState('');
151
+
152
+ const items = ['Document.pdf', 'Image.png', 'Report.xlsx'];
153
+
154
+ return (
155
+ <VStack space={spacing.lg}>
156
+ <Text weight="semiBold">Delete Items</Text>
157
+ <Box p={spacing.lg} bg={colors.background.secondary} rounded="lg">
158
+ <VStack space={spacing.sm}>
159
+ {items.map((item) => (
160
+ <HStack key={item} justifyContent="space-between" alignItems="center">
161
+ <Text>{item}</Text>
162
+ <Button
163
+ size="sm"
164
+ variant="danger"
165
+ onPress={() => {
166
+ setSelectedItem(item);
167
+ setIsOpen(true);
168
+ }}
169
+ >
170
+ Delete
171
+ </Button>
172
+ </HStack>
173
+ ))}
174
+ </VStack>
175
+ </Box>
176
+ <AlertDialog
177
+ isOpen={isOpen}
178
+ onClose={() => setIsOpen(false)}
179
+ title={`Delete ${selectedItem}?`}
180
+ description={`Are you sure you want to delete "${selectedItem}"? This file will be moved to trash.`}
181
+ confirmText="Delete"
182
+ isDestructive
183
+ onConfirm={() => {
184
+ console.log(`Deleted ${selectedItem}`);
185
+ setIsOpen(false);
186
+ }}
187
+ />
188
+ </VStack>
189
+ );
190
+ };
191
+
192
+ export const DeleteItem: Story = {
193
+ render: () => <DeleteItemDialog />,
194
+ };
195
+
196
+ const DiscardChangesDialog = () => {
197
+ const [isOpen, setIsOpen] = useState(false);
198
+
199
+ return (
200
+ <VStack space={spacing.lg}>
201
+ <Text weight="semiBold">Discard Changes</Text>
202
+ <Box p={spacing.lg} bg={colors.background.secondary} rounded="lg">
203
+ <VStack space={spacing.md}>
204
+ <Text size="sm" color={colors.text.secondary}>You have unsaved changes</Text>
205
+ <Button variant="ghost" onPress={() => setIsOpen(true)}>
206
+ Leave Page
207
+ </Button>
208
+ </VStack>
209
+ </Box>
210
+ <AlertDialog
211
+ isOpen={isOpen}
212
+ onClose={() => setIsOpen(false)}
213
+ title="Discard changes?"
214
+ description="You have unsaved changes. If you leave now, your changes will be lost."
215
+ confirmText="Discard"
216
+ cancelText="Keep Editing"
217
+ isDestructive
218
+ onConfirm={() => {
219
+ console.log('Changes discarded');
220
+ setIsOpen(false);
221
+ }}
222
+ />
223
+ </VStack>
224
+ );
225
+ };
226
+
227
+ export const DiscardChanges: Story = {
228
+ render: () => <DiscardChangesDialog />,
229
+ };
230
+
231
+ const LogoutDialog = () => {
232
+ const [isOpen, setIsOpen] = useState(false);
233
+
234
+ return (
235
+ <VStack space={spacing.lg}>
236
+ <Text weight="semiBold">Logout Confirmation</Text>
237
+ <Button variant="outline" onPress={() => setIsOpen(true)}>
238
+ Sign Out
239
+ </Button>
240
+ <AlertDialog
241
+ isOpen={isOpen}
242
+ onClose={() => setIsOpen(false)}
243
+ title="Sign out"
244
+ description="Are you sure you want to sign out? You will need to enter your credentials again to access your account."
245
+ confirmText="Sign Out"
246
+ cancelText="Stay Signed In"
247
+ onConfirm={() => {
248
+ console.log('Signed out');
249
+ setIsOpen(false);
250
+ }}
251
+ />
252
+ </VStack>
253
+ );
254
+ };
255
+
256
+ export const Logout: Story = {
257
+ render: () => <LogoutDialog />,
258
+ };
259
+
260
+ const SubscriptionDialog = () => {
261
+ const [isOpen, setIsOpen] = useState(false);
262
+
263
+ return (
264
+ <VStack space={spacing.lg}>
265
+ <Text weight="semiBold">Cancel Subscription</Text>
266
+ <Box p={spacing.lg} bg={colors.white} rounded="lg" shadow="10">
267
+ <VStack space={spacing.sm}>
268
+ <Text weight="semiBold">Pro Plan</Text>
269
+ <Text size="sm" color={colors.text.secondary}>$9.99/month</Text>
270
+ <Button
271
+ variant="ghost"
272
+ size="sm"
273
+ onPress={() => setIsOpen(true)}
274
+ >
275
+ Cancel Subscription
276
+ </Button>
277
+ </VStack>
278
+ </Box>
279
+ <AlertDialog
280
+ isOpen={isOpen}
281
+ onClose={() => setIsOpen(false)}
282
+ title="Cancel Subscription?"
283
+ description="If you cancel now, you'll lose access to premium features at the end of your current billing period. You can always resubscribe later."
284
+ confirmText="Cancel Subscription"
285
+ cancelText="Keep Subscription"
286
+ isDestructive
287
+ onConfirm={() => {
288
+ console.log('Subscription cancelled');
289
+ setIsOpen(false);
290
+ }}
291
+ />
292
+ </VStack>
293
+ );
294
+ };
295
+
296
+ export const CancelSubscription: Story = {
297
+ render: () => <SubscriptionDialog />,
298
+ };
299
+
300
+ const MultipleOptionsDialog = () => {
301
+ const [isOpen, setIsOpen] = useState(false);
302
+
303
+ return (
304
+ <VStack space={spacing.lg}>
305
+ <Text weight="semiBold">Save Before Closing</Text>
306
+ <Button onPress={() => setIsOpen(true)}>Close Document</Button>
307
+ <AlertDialog
308
+ isOpen={isOpen}
309
+ onClose={() => setIsOpen(false)}
310
+ title="Save changes?"
311
+ description="Do you want to save changes to your document before closing?"
312
+ confirmText="Save"
313
+ cancelText="Don't Save"
314
+ onConfirm={() => {
315
+ console.log('Document saved');
316
+ setIsOpen(false);
317
+ }}
318
+ onCancel={() => {
319
+ console.log('Document closed without saving');
320
+ setIsOpen(false);
321
+ }}
322
+ />
323
+ </VStack>
324
+ );
325
+ };
326
+
327
+ export const SaveChanges: Story = {
328
+ render: () => <MultipleOptionsDialog />,
329
+ };
330
+
@@ -0,0 +1,234 @@
1
+ /**
2
+ * AlertDialog Component
3
+ * Confirmation dialog for critical actions
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
+ StyleSheet,
15
+ } from 'react-native';
16
+ import { colors, spacing, borderRadius, typography, elevation } from '../../styles/tokens';
17
+
18
+ export interface AlertDialogProps extends ViewProps {
19
+ /** Is open */
20
+ isOpen: boolean;
21
+ /** On close handler */
22
+ onClose: () => void;
23
+ /** Close on overlay click */
24
+ closeOnOverlayClick?: boolean;
25
+ /** Title */
26
+ title: string;
27
+ /** Description */
28
+ description?: string;
29
+ /** Cancel button text */
30
+ cancelText?: string;
31
+ /** Confirm button text */
32
+ confirmText?: string;
33
+ /** On confirm handler */
34
+ onConfirm?: () => void;
35
+ /** On cancel handler */
36
+ onCancel?: () => void;
37
+ /** Is destructive action */
38
+ isDestructive?: boolean;
39
+ /** Is loading */
40
+ isLoading?: boolean;
41
+ }
42
+
43
+ export const AlertDialog = forwardRef<View, AlertDialogProps>(
44
+ (
45
+ {
46
+ style,
47
+ isOpen,
48
+ onClose,
49
+ closeOnOverlayClick = true,
50
+ title,
51
+ description,
52
+ cancelText = 'Cancel',
53
+ confirmText = 'Confirm',
54
+ onConfirm,
55
+ onCancel,
56
+ isDestructive = false,
57
+ isLoading = false,
58
+ children,
59
+ ...props
60
+ },
61
+ ref
62
+ ) => {
63
+ const scaleAnim = useRef(new Animated.Value(0.9)).current;
64
+ const opacityAnim = useRef(new Animated.Value(0)).current;
65
+
66
+ useEffect(() => {
67
+ if (isOpen) {
68
+ Animated.parallel([
69
+ Animated.spring(scaleAnim, {
70
+ toValue: 1,
71
+ useNativeDriver: true,
72
+ tension: 50,
73
+ friction: 7,
74
+ }),
75
+ Animated.timing(opacityAnim, {
76
+ toValue: 1,
77
+ duration: 200,
78
+ useNativeDriver: true,
79
+ }),
80
+ ]).start();
81
+ } else {
82
+ scaleAnim.setValue(0.9);
83
+ opacityAnim.setValue(0);
84
+ }
85
+ }, [isOpen]);
86
+
87
+ const handleCancel = () => {
88
+ onCancel?.();
89
+ onClose();
90
+ };
91
+
92
+ const handleConfirm = () => {
93
+ onConfirm?.();
94
+ if (!isLoading) {
95
+ onClose();
96
+ }
97
+ };
98
+
99
+ return (
100
+ <Modal
101
+ visible={isOpen}
102
+ transparent
103
+ animationType="fade"
104
+ onRequestClose={onClose}
105
+ >
106
+ <Pressable
107
+ style={styles.overlay}
108
+ onPress={closeOnOverlayClick ? onClose : undefined}
109
+ >
110
+ <Animated.View
111
+ ref={ref}
112
+ style={[
113
+ styles.dialog,
114
+ {
115
+ transform: [{ scale: scaleAnim }],
116
+ opacity: opacityAnim,
117
+ },
118
+ style,
119
+ ]}
120
+ {...props}
121
+ >
122
+ <Pressable>
123
+ <View style={styles.header}>
124
+ <Text style={styles.title}>{title}</Text>
125
+ </View>
126
+ {description && (
127
+ <View style={styles.body}>
128
+ <Text style={styles.description}>{description}</Text>
129
+ </View>
130
+ )}
131
+ {children}
132
+ <View style={styles.footer}>
133
+ <Pressable
134
+ onPress={handleCancel}
135
+ style={[styles.button, styles.cancelButton]}
136
+ disabled={isLoading}
137
+ >
138
+ <Text style={styles.cancelButtonText}>{cancelText}</Text>
139
+ </Pressable>
140
+ <Pressable
141
+ onPress={handleConfirm}
142
+ style={[
143
+ styles.button,
144
+ styles.confirmButton,
145
+ isDestructive && styles.destructiveButton,
146
+ ]}
147
+ disabled={isLoading}
148
+ >
149
+ <Text style={[styles.confirmButtonText, isDestructive && styles.destructiveButtonText]}>
150
+ {isLoading ? 'Loading...' : confirmText}
151
+ </Text>
152
+ </Pressable>
153
+ </View>
154
+ </Pressable>
155
+ </Animated.View>
156
+ </Pressable>
157
+ </Modal>
158
+ );
159
+ }
160
+ );
161
+
162
+ AlertDialog.displayName = 'AlertDialog';
163
+
164
+ const styles = StyleSheet.create({
165
+ overlay: {
166
+ flex: 1,
167
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
168
+ justifyContent: 'center',
169
+ alignItems: 'center',
170
+ padding: spacing['4x'],
171
+ },
172
+ dialog: {
173
+ backgroundColor: colors.background.default,
174
+ borderRadius: borderRadius.lg,
175
+ width: '100%',
176
+ maxWidth: 400,
177
+ ...elevation['40'],
178
+ },
179
+ header: {
180
+ padding: spacing['4x'],
181
+ paddingBottom: spacing['2x'],
182
+ },
183
+ title: {
184
+ fontSize: typography.fontSize.h4,
185
+ fontWeight: typography.fontWeight.semiBold,
186
+ color: colors.text.default,
187
+ },
188
+ body: {
189
+ paddingHorizontal: spacing['4x'],
190
+ paddingBottom: spacing['4x'],
191
+ },
192
+ description: {
193
+ fontSize: typography.fontSize.body,
194
+ color: colors.text.secondary,
195
+ lineHeight: typography.fontSize.body * typography.lineHeight.normal,
196
+ },
197
+ footer: {
198
+ flexDirection: 'row',
199
+ justifyContent: 'flex-end',
200
+ padding: spacing['3x'],
201
+ gap: spacing['2x'],
202
+ borderTopWidth: 1,
203
+ borderTopColor: colors.border.disabled,
204
+ },
205
+ button: {
206
+ paddingVertical: spacing['2x'],
207
+ paddingHorizontal: spacing['4x'],
208
+ borderRadius: borderRadius.md,
209
+ minWidth: 80,
210
+ alignItems: 'center',
211
+ },
212
+ cancelButton: {
213
+ backgroundColor: colors.secondary.default,
214
+ },
215
+ cancelButtonText: {
216
+ fontSize: typography.fontSize.body,
217
+ fontWeight: typography.fontWeight.medium,
218
+ color: colors.text.default,
219
+ },
220
+ confirmButton: {
221
+ backgroundColor: colors.brand.blue,
222
+ },
223
+ confirmButtonText: {
224
+ fontSize: typography.fontSize.body,
225
+ fontWeight: typography.fontWeight.medium,
226
+ color: colors.white,
227
+ },
228
+ destructiveButton: {
229
+ backgroundColor: colors.danger.default,
230
+ },
231
+ destructiveButtonText: {
232
+ color: colors.white,
233
+ },
234
+ });
@@ -0,0 +1,2 @@
1
+ export { AlertDialog, type AlertDialogProps } from './AlertDialog';
2
+