@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.
- package/.cursor/rules +313 -0
- package/.rnstorybook/index.ts +11 -0
- package/.rnstorybook/main.ts +8 -0
- package/.rnstorybook/preview.tsx +14 -0
- package/.rnstorybook/storybook.requires.ts +49 -0
- package/.storybook/main.ts +16 -0
- package/.storybook/preview.ts +32 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/App.tsx +422 -0
- package/README.md +229 -0
- package/app.json +33 -0
- package/assets/adaptive-icon.png +0 -0
- package/assets/favicon.png +0 -0
- package/assets/icon.png +0 -0
- package/assets/splash-icon.png +0 -0
- package/babel.config.js +16 -0
- package/docs/components/CodeBlock.tsx +80 -0
- package/docs/components/PropTable.tsx +93 -0
- package/docs/components/Sidebar.tsx +199 -0
- package/docs/components/index.ts +8 -0
- package/docs/data/colorTokens.ts +70 -0
- package/docs/data/componentData.tsx +1685 -0
- package/docs/data/index.ts +7 -0
- package/docs/index.ts +19 -0
- package/docs/navigation.ts +94 -0
- package/docs/pages/ColorsPage.tsx +226 -0
- package/docs/pages/ComponentPage.tsx +235 -0
- package/docs/pages/InstallationPage.tsx +232 -0
- package/docs/pages/IntroductionPage.tsx +163 -0
- package/docs/pages/ThemingPage.tsx +251 -0
- package/docs/pages/index.ts +10 -0
- package/docs/theme.ts +64 -0
- package/docs/types.ts +54 -0
- package/index.ts +8 -0
- package/llms.txt +1893 -0
- package/mcp-config.example.json +10 -0
- package/mcp-server/README.md +192 -0
- package/mcp-server/package-lock.json +1707 -0
- package/mcp-server/package.json +38 -0
- package/mcp-server/src/index.ts +1136 -0
- package/mcp-server/src/registry/components.ts +1446 -0
- package/mcp-server/src/registry/index.ts +3 -0
- package/mcp-server/src/registry/tokens.ts +256 -0
- package/mcp-server/tsconfig.json +19 -0
- package/package.json +92 -0
- package/src/components/Accordion/Accordion.stories.tsx +226 -0
- package/src/components/Accordion/Accordion.tsx +255 -0
- package/src/components/Accordion/index.ts +12 -0
- package/src/components/ActionSheet/ActionSheet.stories.tsx +393 -0
- package/src/components/ActionSheet/ActionSheet.tsx +258 -0
- package/src/components/ActionSheet/index.ts +2 -0
- package/src/components/Alert/Alert.stories.tsx +165 -0
- package/src/components/Alert/Alert.tsx +164 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/AlertDialog/AlertDialog.stories.tsx +330 -0
- package/src/components/AlertDialog/AlertDialog.tsx +234 -0
- package/src/components/AlertDialog/index.ts +2 -0
- package/src/components/Avatar/Avatar.stories.tsx +154 -0
- package/src/components/Avatar/Avatar.tsx +219 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/Badge/Badge.stories.tsx +146 -0
- package/src/components/Badge/Badge.tsx +125 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Box/Box.stories.tsx +192 -0
- package/src/components/Box/Box.tsx +184 -0
- package/src/components/Box/index.ts +2 -0
- package/src/components/Button/Button.stories.tsx +157 -0
- package/src/components/Button/Button.tsx +180 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Card/Card.stories.tsx +145 -0
- package/src/components/Card/Card.tsx +169 -0
- package/src/components/Card/index.ts +11 -0
- package/src/components/Center/Center.stories.tsx +215 -0
- package/src/components/Center/Center.tsx +29 -0
- package/src/components/Center/index.ts +2 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +94 -0
- package/src/components/Checkbox/Checkbox.tsx +242 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +623 -0
- package/src/components/DatePicker/DatePicker.tsx +1228 -0
- package/src/components/DatePicker/index.ts +8 -0
- package/src/components/Divider/Divider.stories.tsx +224 -0
- package/src/components/Divider/Divider.tsx +73 -0
- package/src/components/Divider/index.ts +2 -0
- package/src/components/Drawer/Drawer.stories.tsx +414 -0
- package/src/components/Drawer/Drawer.tsx +342 -0
- package/src/components/Drawer/index.ts +11 -0
- package/src/components/Fab/Fab.stories.tsx +360 -0
- package/src/components/Fab/Fab.tsx +185 -0
- package/src/components/Fab/index.ts +2 -0
- package/src/components/FormControl/FormControl.stories.tsx +276 -0
- package/src/components/FormControl/FormControl.tsx +185 -0
- package/src/components/FormControl/index.ts +12 -0
- package/src/components/Grid/Grid.stories.tsx +244 -0
- package/src/components/Grid/Grid.tsx +93 -0
- package/src/components/Grid/index.ts +2 -0
- package/src/components/HStack/HStack.stories.tsx +230 -0
- package/src/components/HStack/HStack.tsx +80 -0
- package/src/components/HStack/index.ts +2 -0
- package/src/components/Heading/Heading.stories.tsx +111 -0
- package/src/components/Heading/Heading.tsx +85 -0
- package/src/components/Heading/index.ts +2 -0
- package/src/components/Icon/Icon.stories.tsx +320 -0
- package/src/components/Icon/Icon.tsx +117 -0
- package/src/components/Icon/index.ts +2 -0
- package/src/components/Image/Image.stories.tsx +357 -0
- package/src/components/Image/Image.tsx +168 -0
- package/src/components/Image/index.ts +2 -0
- package/src/components/Input/Input.stories.tsx +164 -0
- package/src/components/Input/Input.tsx +274 -0
- package/src/components/Input/index.ts +2 -0
- package/src/components/Link/Link.stories.tsx +187 -0
- package/src/components/Link/Link.tsx +104 -0
- package/src/components/Link/index.ts +2 -0
- package/src/components/Menu/Menu.stories.tsx +363 -0
- package/src/components/Menu/Menu.tsx +238 -0
- package/src/components/Menu/index.ts +2 -0
- package/src/components/Modal/Modal.stories.tsx +156 -0
- package/src/components/Modal/Modal.tsx +280 -0
- package/src/components/Modal/index.ts +11 -0
- package/src/components/Popover/Popover.stories.tsx +330 -0
- package/src/components/Popover/Popover.tsx +315 -0
- package/src/components/Popover/index.ts +11 -0
- package/src/components/Portal/Portal.stories.tsx +376 -0
- package/src/components/Portal/Portal.tsx +100 -0
- package/src/components/Portal/index.ts +2 -0
- package/src/components/Pressable/Pressable.stories.tsx +338 -0
- package/src/components/Pressable/Pressable.tsx +71 -0
- package/src/components/Pressable/index.ts +2 -0
- package/src/components/Progress/Progress.stories.tsx +131 -0
- package/src/components/Progress/Progress.tsx +219 -0
- package/src/components/Progress/index.ts +2 -0
- package/src/components/Radio/Radio.stories.tsx +101 -0
- package/src/components/Radio/Radio.tsx +234 -0
- package/src/components/Radio/index.ts +2 -0
- package/src/components/Select/Select.stories.tsx +908 -0
- package/src/components/Select/Select.tsx +659 -0
- package/src/components/Select/index.ts +8 -0
- package/src/components/Skeleton/Skeleton.stories.tsx +154 -0
- package/src/components/Skeleton/Skeleton.tsx +192 -0
- package/src/components/Skeleton/index.ts +8 -0
- package/src/components/Slider/Slider.stories.tsx +363 -0
- package/src/components/Slider/Slider.tsx +209 -0
- package/src/components/Slider/index.ts +2 -0
- package/src/components/Spinner/Spinner.stories.tsx +108 -0
- package/src/components/Spinner/Spinner.tsx +121 -0
- package/src/components/Spinner/index.ts +2 -0
- package/src/components/Switch/Switch.stories.tsx +116 -0
- package/src/components/Switch/Switch.tsx +172 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Table/Table.stories.tsx +417 -0
- package/src/components/Table/Table.tsx +233 -0
- package/src/components/Table/index.ts +2 -0
- package/src/components/Text/Text.stories.tsx +93 -0
- package/src/components/Text/Text.tsx +119 -0
- package/src/components/Text/index.ts +2 -0
- package/src/components/Textarea/Textarea.stories.tsx +280 -0
- package/src/components/Textarea/Textarea.tsx +212 -0
- package/src/components/Textarea/index.ts +2 -0
- package/src/components/Toast/Toast.stories.tsx +446 -0
- package/src/components/Toast/Toast.tsx +221 -0
- package/src/components/Toast/index.ts +2 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +354 -0
- package/src/components/Tooltip/Tooltip.tsx +261 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/VStack/VStack.stories.tsx +183 -0
- package/src/components/VStack/VStack.tsx +76 -0
- package/src/components/VStack/index.ts +2 -0
- package/src/components/index.ts +62 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useControllableState.ts +41 -0
- package/src/hooks/useDisclosure.ts +51 -0
- package/src/index.ts +22 -0
- package/src/stories/Button.stories.tsx +53 -0
- package/src/stories/Button.tsx +101 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/Header.stories.tsx +33 -0
- package/src/stories/Header.tsx +75 -0
- package/src/stories/Page.stories.tsx +25 -0
- package/src/stories/Page.tsx +154 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/styles/index.ts +7 -0
- package/src/styles/tokens.ts +318 -0
- package/src/styles/unistyles.ts +254 -0
- package/src/utils/createContext.tsx +25 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/mergeRefs.ts +21 -0
- package/tsconfig.json +26 -0
- package/urbint-cl-1.0.0.tgz +0 -0
- package/vitest.config.ts +37 -0
- 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
|
+
});
|