@djangocfg/ui-core 2.1.382 → 2.1.383

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 (61) hide show
  1. package/package.json +5 -12
  2. package/src/components/boundary/boundary.story.tsx +0 -191
  3. package/src/components/data/avatar/avatar.story.tsx +0 -115
  4. package/src/components/data/badge/badge.story.tsx +0 -56
  5. package/src/components/data/calendar/calendar.story.tsx +0 -127
  6. package/src/components/data/carousel/carousel.story.tsx +0 -122
  7. package/src/components/data/progress/progress.story.tsx +0 -97
  8. package/src/components/data/table/table.story.tsx +0 -148
  9. package/src/components/data/toggle/toggle.story.tsx +0 -104
  10. package/src/components/data/toggle-group/toggle-group.story.tsx +0 -118
  11. package/src/components/feedback/alert/alert.story.tsx +0 -77
  12. package/src/components/feedback/empty/empty.story.tsx +0 -115
  13. package/src/components/feedback/preloader/preloader.story.tsx +0 -86
  14. package/src/components/feedback/spinner/spinner.story.tsx +0 -66
  15. package/src/components/forms/button/button.story.tsx +0 -116
  16. package/src/components/forms/button-download/button-download.story.tsx +0 -112
  17. package/src/components/forms/button-group/button-group.story.tsx +0 -79
  18. package/src/components/forms/checkbox/checkbox.story.tsx +0 -89
  19. package/src/components/forms/input/input.story.tsx +0 -77
  20. package/src/components/forms/input-group/input-group.story.tsx +0 -119
  21. package/src/components/forms/input-otp/input-otp.story.tsx +0 -105
  22. package/src/components/forms/label/label.story.tsx +0 -52
  23. package/src/components/forms/radio-group/radio-group.story.tsx +0 -113
  24. package/src/components/forms/slider/slider.story.tsx +0 -134
  25. package/src/components/forms/switch/switch.story.tsx +0 -98
  26. package/src/components/forms/textarea/textarea.story.tsx +0 -94
  27. package/src/components/layout/aspect-ratio/aspect-ratio.story.tsx +0 -94
  28. package/src/components/layout/card/card.story.tsx +0 -105
  29. package/src/components/layout/resizable/resizable.story.tsx +0 -119
  30. package/src/components/layout/scroll-area/scroll-area.story.tsx +0 -172
  31. package/src/components/layout/separator/separator.story.tsx +0 -69
  32. package/src/components/layout/skeleton/skeleton.story.tsx +0 -101
  33. package/src/components/navigation/accordion/accordion.story.tsx +0 -110
  34. package/src/components/navigation/collapsible/collapsible.story.tsx +0 -133
  35. package/src/components/navigation/command/command.story.tsx +0 -121
  36. package/src/components/navigation/context-menu/context-menu.story.tsx +0 -125
  37. package/src/components/navigation/dropdown-menu/dropdown-menu.story.tsx +0 -208
  38. package/src/components/navigation/menubar/menubar.story.tsx +0 -152
  39. package/src/components/navigation/navigation-menu/navigation-menu.story.tsx +0 -154
  40. package/src/components/navigation/tabs/tabs.story.tsx +0 -98
  41. package/src/components/overlay/alert-dialog/alert-dialog.story.tsx +0 -104
  42. package/src/components/overlay/dialog/dialog.story.tsx +0 -212
  43. package/src/components/overlay/drawer/drawer.story.tsx +0 -359
  44. package/src/components/overlay/hover-card/hover-card.story.tsx +0 -102
  45. package/src/components/overlay/popover/popover.story.tsx +0 -127
  46. package/src/components/overlay/responsive-sheet/responsive-sheet.story.tsx +0 -117
  47. package/src/components/overlay/sheet/sheet.story.tsx +0 -148
  48. package/src/components/overlay/tooltip/tooltip.story.tsx +0 -139
  49. package/src/components/select/combobox-async.story.tsx +0 -215
  50. package/src/components/select/combobox.story.tsx +0 -226
  51. package/src/components/select/country-select.story.tsx +0 -261
  52. package/src/components/select/language-select.story.tsx +0 -264
  53. package/src/components/select/multi-select.story.tsx +0 -122
  54. package/src/components/select/select.story.tsx +0 -112
  55. package/src/components/specialized/copy/copy.story.tsx +0 -77
  56. package/src/components/specialized/flag/flag.story.tsx +0 -82
  57. package/src/components/specialized/image-with-fallback/image-with-fallback.story.tsx +0 -105
  58. package/src/components/specialized/kbd/kbd.story.tsx +0 -113
  59. package/src/lib/dialog-service/dialog-service.story.tsx +0 -263
  60. package/src/stories/index.ts +0 -28
  61. package/src/styles/theme/theme-tokens.story.tsx +0 -157
@@ -1,104 +0,0 @@
1
- import { defineStory } from '@djangocfg/playground';
2
- import {
3
- AlertDialog,
4
- AlertDialogTrigger,
5
- AlertDialogContent,
6
- AlertDialogHeader,
7
- AlertDialogFooter,
8
- AlertDialogTitle,
9
- AlertDialogDescription,
10
- AlertDialogAction,
11
- AlertDialogCancel,
12
- } from '.';
13
- import { Button } from '../../forms/button';
14
-
15
- export default defineStory({
16
- title: 'Core/Alert Dialog',
17
- component: AlertDialog,
18
- description: 'Modal dialog for important confirmations.',
19
- });
20
-
21
- export const Default = () => (
22
- <AlertDialog>
23
- <AlertDialogTrigger asChild>
24
- <Button variant="outline">Show Dialog</Button>
25
- </AlertDialogTrigger>
26
- <AlertDialogContent>
27
- <AlertDialogHeader>
28
- <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
29
- <AlertDialogDescription>
30
- This action cannot be undone. This will permanently delete your
31
- account and remove your data from our servers.
32
- </AlertDialogDescription>
33
- </AlertDialogHeader>
34
- <AlertDialogFooter>
35
- <AlertDialogCancel>Cancel</AlertDialogCancel>
36
- <AlertDialogAction>Continue</AlertDialogAction>
37
- </AlertDialogFooter>
38
- </AlertDialogContent>
39
- </AlertDialog>
40
- );
41
-
42
- export const Destructive = () => (
43
- <AlertDialog>
44
- <AlertDialogTrigger asChild>
45
- <Button variant="destructive">Delete Account</Button>
46
- </AlertDialogTrigger>
47
- <AlertDialogContent>
48
- <AlertDialogHeader>
49
- <AlertDialogTitle>Delete Account</AlertDialogTitle>
50
- <AlertDialogDescription>
51
- Are you sure you want to delete your account? All of your data will be
52
- permanently removed. This action cannot be undone.
53
- </AlertDialogDescription>
54
- </AlertDialogHeader>
55
- <AlertDialogFooter>
56
- <AlertDialogCancel>Cancel</AlertDialogCancel>
57
- <AlertDialogAction className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
58
- Delete
59
- </AlertDialogAction>
60
- </AlertDialogFooter>
61
- </AlertDialogContent>
62
- </AlertDialog>
63
- );
64
-
65
- export const SaveChanges = () => (
66
- <AlertDialog>
67
- <AlertDialogTrigger asChild>
68
- <Button>Leave Page</Button>
69
- </AlertDialogTrigger>
70
- <AlertDialogContent>
71
- <AlertDialogHeader>
72
- <AlertDialogTitle>Unsaved Changes</AlertDialogTitle>
73
- <AlertDialogDescription>
74
- You have unsaved changes. Are you sure you want to leave? Your changes
75
- will be lost.
76
- </AlertDialogDescription>
77
- </AlertDialogHeader>
78
- <AlertDialogFooter>
79
- <AlertDialogCancel>Stay</AlertDialogCancel>
80
- <AlertDialogAction>Leave</AlertDialogAction>
81
- </AlertDialogFooter>
82
- </AlertDialogContent>
83
- </AlertDialog>
84
- );
85
-
86
- export const Logout = () => (
87
- <AlertDialog>
88
- <AlertDialogTrigger asChild>
89
- <Button variant="ghost">Log Out</Button>
90
- </AlertDialogTrigger>
91
- <AlertDialogContent>
92
- <AlertDialogHeader>
93
- <AlertDialogTitle>Log out</AlertDialogTitle>
94
- <AlertDialogDescription>
95
- Are you sure you want to log out of your account?
96
- </AlertDialogDescription>
97
- </AlertDialogHeader>
98
- <AlertDialogFooter>
99
- <AlertDialogCancel>Cancel</AlertDialogCancel>
100
- <AlertDialogAction>Log Out</AlertDialogAction>
101
- </AlertDialogFooter>
102
- </AlertDialogContent>
103
- </AlertDialog>
104
- );
@@ -1,212 +0,0 @@
1
- import { ArrowLeft, X } from 'lucide-react';
2
- import { defineStory } from '@djangocfg/playground';
3
- import {
4
- Dialog,
5
- DialogTrigger,
6
- DialogContent,
7
- DialogHeader,
8
- DialogFooter,
9
- DialogTitle,
10
- DialogDescription,
11
- DialogClose,
12
- } from '.';
13
- import { Button } from '../../forms/button';
14
- import { Input } from '../../forms/input';
15
- import { Label } from '../../forms/label';
16
-
17
- export default defineStory({
18
- title: 'Core/Dialog',
19
- component: Dialog,
20
- description: 'Modal dialog for focused interactions.',
21
- });
22
-
23
- export const Default = () => (
24
- <Dialog>
25
- <DialogTrigger asChild>
26
- <Button variant="outline">Open Dialog</Button>
27
- </DialogTrigger>
28
- <DialogContent>
29
- <DialogHeader>
30
- <DialogTitle>Dialog Title</DialogTitle>
31
- <DialogDescription>
32
- This is a dialog description. It explains what this dialog is about.
33
- </DialogDescription>
34
- </DialogHeader>
35
- <div className="py-4">
36
- <p>Dialog content goes here.</p>
37
- </div>
38
- <DialogFooter>
39
- <DialogClose asChild>
40
- <Button variant="outline">Cancel</Button>
41
- </DialogClose>
42
- <Button>Confirm</Button>
43
- </DialogFooter>
44
- </DialogContent>
45
- </Dialog>
46
- );
47
-
48
- export const EditProfile = () => (
49
- <Dialog>
50
- <DialogTrigger asChild>
51
- <Button>Edit Profile</Button>
52
- </DialogTrigger>
53
- <DialogContent className="sm:max-w-md">
54
- <DialogHeader>
55
- <DialogTitle>Edit Profile</DialogTitle>
56
- <DialogDescription>
57
- Make changes to your profile here. Click save when you're done.
58
- </DialogDescription>
59
- </DialogHeader>
60
- <div className="space-y-4 py-4">
61
- <div className="space-y-2">
62
- <Label htmlFor="name">Name</Label>
63
- <Input id="name" defaultValue="John Doe" />
64
- </div>
65
- <div className="space-y-2">
66
- <Label htmlFor="email">Email</Label>
67
- <Input id="email" type="email" defaultValue="john@example.com" />
68
- </div>
69
- </div>
70
- <DialogFooter>
71
- <DialogClose asChild>
72
- <Button variant="outline">Cancel</Button>
73
- </DialogClose>
74
- <Button>Save Changes</Button>
75
- </DialogFooter>
76
- </DialogContent>
77
- </Dialog>
78
- );
79
-
80
- export const Confirmation = () => (
81
- <Dialog>
82
- <DialogTrigger asChild>
83
- <Button variant="destructive">Delete Account</Button>
84
- </DialogTrigger>
85
- <DialogContent>
86
- <DialogHeader>
87
- <DialogTitle>Are you sure?</DialogTitle>
88
- <DialogDescription>
89
- This action cannot be undone. This will permanently delete your account
90
- and remove all your data from our servers.
91
- </DialogDescription>
92
- </DialogHeader>
93
- <DialogFooter>
94
- <DialogClose asChild>
95
- <Button variant="outline">Cancel</Button>
96
- </DialogClose>
97
- <Button variant="destructive">Delete Account</Button>
98
- </DialogFooter>
99
- </DialogContent>
100
- </Dialog>
101
- );
102
-
103
- export const Fullscreen = () => (
104
- <Dialog>
105
- <DialogTrigger asChild>
106
- <Button variant="outline">Open fullscreen dialog</Button>
107
- </DialogTrigger>
108
- <DialogContent fullscreen>
109
- <div className="mx-auto flex h-full w-full max-w-3xl flex-col px-4 py-10 sm:px-6 lg:px-8">
110
- <DialogTitle className="text-2xl font-semibold tracking-tight">
111
- Fullscreen layout
112
- </DialogTitle>
113
- <DialogDescription className="mt-1 text-muted-foreground">
114
- `fullscreen` removes the centred-card chrome and stretches the content to
115
- the viewport. Build your own header / footer inside.
116
- </DialogDescription>
117
- <div className="mt-6 flex-1 rounded-xl border border-dashed border-border/60 bg-muted/30 p-6 text-sm text-muted-foreground">
118
- Slot for a custom layout — gallery, onboarding, command palette, etc.
119
- </div>
120
- </div>
121
- </DialogContent>
122
- </Dialog>
123
- );
124
-
125
- export const CustomCloseButton = () => (
126
- <Dialog>
127
- <DialogTrigger asChild>
128
- <Button variant="outline">Open with pill close</Button>
129
- </DialogTrigger>
130
- <DialogContent
131
- fullscreen
132
- closeButton={
133
- <DialogClose
134
- aria-label="Close"
135
- className="absolute right-4 top-4 inline-flex h-10 w-10 cursor-pointer items-center justify-center rounded-full border border-border/60 bg-background/90 text-muted-foreground shadow-sm transition-colors hover:bg-accent hover:text-foreground"
136
- >
137
- <X className="h-4 w-4" />
138
- </DialogClose>
139
- }
140
- >
141
- <div className="mx-auto flex h-full w-full max-w-2xl flex-col px-4 py-10">
142
- <DialogTitle>Custom close affordance</DialogTitle>
143
- <DialogDescription className="mt-1 text-muted-foreground">
144
- Pass `closeButton` to override the default Radix `X`. Pass `false` to
145
- drop the affordance entirely.
146
- </DialogDescription>
147
- </div>
148
- </DialogContent>
149
- </Dialog>
150
- );
151
-
152
- export const NoCloseButton = () => (
153
- <Dialog>
154
- <DialogTrigger asChild>
155
- <Button variant="outline">Modal step (no close)</Button>
156
- </DialogTrigger>
157
- <DialogContent closeButton={false}>
158
- <DialogHeader>
159
- <DialogTitle>Saving…</DialogTitle>
160
- <DialogDescription>
161
- The dialog has no close affordance — dismissal is driven by the action
162
- buttons below or programmatically.
163
- </DialogDescription>
164
- </DialogHeader>
165
- <DialogFooter>
166
- <DialogClose asChild>
167
- <Button variant="outline">
168
- <ArrowLeft className="mr-1.5 h-3.5 w-3.5" />
169
- Back
170
- </Button>
171
- </DialogClose>
172
- </DialogFooter>
173
- </DialogContent>
174
- </Dialog>
175
- );
176
-
177
- export const LongContent = () => (
178
- <Dialog>
179
- <DialogTrigger asChild>
180
- <Button variant="outline">Terms of Service</Button>
181
- </DialogTrigger>
182
- <DialogContent className="max-h-[80vh] overflow-y-auto">
183
- <DialogHeader>
184
- <DialogTitle>Terms of Service</DialogTitle>
185
- </DialogHeader>
186
- <div className="space-y-4 py-4 text-sm text-muted-foreground">
187
- <p>
188
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
189
- tempor incididunt ut labore et dolore magna aliqua.
190
- </p>
191
- <p>
192
- Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
193
- ut aliquip ex ea commodo consequat.
194
- </p>
195
- <p>
196
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
197
- dolore eu fugiat nulla pariatur.
198
- </p>
199
- <p>
200
- Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
201
- deserunt mollit anim id est laborum.
202
- </p>
203
- </div>
204
- <DialogFooter>
205
- <DialogClose asChild>
206
- <Button variant="outline">Decline</Button>
207
- </DialogClose>
208
- <Button>Accept</Button>
209
- </DialogFooter>
210
- </DialogContent>
211
- </Dialog>
212
- );
@@ -1,359 +0,0 @@
1
- import * as React from 'react';
2
- import { defineStory, useSelect } from '@djangocfg/playground';
3
- import {
4
- Drawer,
5
- DrawerTrigger,
6
- DrawerContent,
7
- DrawerHeader,
8
- DrawerFooter,
9
- DrawerTitle,
10
- DrawerDescription,
11
- DrawerClose,
12
- useDrawerSize,
13
- type DrawerSize,
14
- } from '.';
15
- import { Button } from '../../forms/button';
16
- import { Input } from '../../forms/input';
17
- import { Label } from '../../forms/label';
18
-
19
- export default defineStory({
20
- title: 'Core/Drawer',
21
- component: Drawer,
22
- description: 'Mobile-friendly drawer that slides up from the bottom.',
23
- });
24
-
25
- export const Default = () => (
26
- <Drawer>
27
- <DrawerTrigger asChild>
28
- <Button variant="outline">Open Drawer</Button>
29
- </DrawerTrigger>
30
- <DrawerContent>
31
- <DrawerHeader>
32
- <DrawerTitle>Drawer Title</DrawerTitle>
33
- <DrawerDescription>
34
- This is a description of the drawer content.
35
- </DrawerDescription>
36
- </DrawerHeader>
37
- <div className="p-4">
38
- <p>Drawer content goes here.</p>
39
- </div>
40
- <DrawerFooter>
41
- <Button>Submit</Button>
42
- <DrawerClose asChild>
43
- <Button variant="outline">Cancel</Button>
44
- </DrawerClose>
45
- </DrawerFooter>
46
- </DrawerContent>
47
- </Drawer>
48
- );
49
-
50
- export const WithForm = () => (
51
- <Drawer>
52
- <DrawerTrigger asChild>
53
- <Button>Edit Profile</Button>
54
- </DrawerTrigger>
55
- <DrawerContent>
56
- <DrawerHeader>
57
- <DrawerTitle>Edit Profile</DrawerTitle>
58
- <DrawerDescription>
59
- Make changes to your profile information.
60
- </DrawerDescription>
61
- </DrawerHeader>
62
- <div className="p-4 space-y-4">
63
- <div className="space-y-2">
64
- <Label htmlFor="name">Name</Label>
65
- <Input id="name" defaultValue="John Doe" />
66
- </div>
67
- <div className="space-y-2">
68
- <Label htmlFor="email">Email</Label>
69
- <Input id="email" type="email" defaultValue="john@example.com" />
70
- </div>
71
- </div>
72
- <DrawerFooter>
73
- <Button>Save Changes</Button>
74
- <DrawerClose asChild>
75
- <Button variant="outline">Cancel</Button>
76
- </DrawerClose>
77
- </DrawerFooter>
78
- </DrawerContent>
79
- </Drawer>
80
- );
81
-
82
- export const Confirmation = () => (
83
- <Drawer>
84
- <DrawerTrigger asChild>
85
- <Button variant="destructive">Delete Account</Button>
86
- </DrawerTrigger>
87
- <DrawerContent>
88
- <DrawerHeader>
89
- <DrawerTitle>Are you sure?</DrawerTitle>
90
- <DrawerDescription>
91
- This action cannot be undone. Your account and all associated data will be permanently deleted.
92
- </DrawerDescription>
93
- </DrawerHeader>
94
- <DrawerFooter>
95
- <Button variant="destructive">Yes, Delete My Account</Button>
96
- <DrawerClose asChild>
97
- <Button variant="outline">Cancel</Button>
98
- </DrawerClose>
99
- </DrawerFooter>
100
- </DrawerContent>
101
- </Drawer>
102
- );
103
-
104
- export const ActionSheet = () => (
105
- <Drawer>
106
- <DrawerTrigger asChild>
107
- <Button variant="outline">Share</Button>
108
- </DrawerTrigger>
109
- <DrawerContent>
110
- <DrawerHeader>
111
- <DrawerTitle>Share this item</DrawerTitle>
112
- </DrawerHeader>
113
- <div className="p-4 space-y-2">
114
- <Button variant="ghost" className="w-full justify-start">
115
- Copy Link
116
- </Button>
117
- <Button variant="ghost" className="w-full justify-start">
118
- Share to Twitter
119
- </Button>
120
- <Button variant="ghost" className="w-full justify-start">
121
- Share to Facebook
122
- </Button>
123
- <Button variant="ghost" className="w-full justify-start">
124
- Send via Email
125
- </Button>
126
- </div>
127
- <DrawerFooter>
128
- <DrawerClose asChild>
129
- <Button variant="outline" className="w-full">Cancel</Button>
130
- </DrawerClose>
131
- </DrawerFooter>
132
- </DrawerContent>
133
- </Drawer>
134
- );
135
-
136
- const SIZES: readonly DrawerSize[] = ['sm', 'md', 'lg', 'xl', 'full'] as const;
137
-
138
- export const Sizes = () => {
139
- const [size, setSize] = React.useState<DrawerSize>('md');
140
- const [open, setOpen] = React.useState(false);
141
-
142
- return (
143
- <div className="space-y-4">
144
- <div className="flex flex-wrap gap-2">
145
- {SIZES.map((s) => (
146
- <Button
147
- key={s}
148
- variant={size === s ? 'default' : 'outline'}
149
- onClick={() => {
150
- setSize(s);
151
- setOpen(true);
152
- }}
153
- >
154
- {s}
155
- </Button>
156
- ))}
157
- </div>
158
- <p className="text-sm text-muted-foreground">
159
- Current size: <code>{size}</code>. Direction: right.
160
- </p>
161
- <Drawer open={open} onOpenChange={setOpen} direction="right">
162
- <DrawerContent direction="right" size={size}>
163
- <DrawerHeader>
164
- <DrawerTitle>Size: {size}</DrawerTitle>
165
- <DrawerDescription>
166
- Width preset is applied via inline style — vaul measures it
167
- correctly on first paint.
168
- </DrawerDescription>
169
- </DrawerHeader>
170
- <div className="p-4 text-sm">Content adapts to the chosen size.</div>
171
- <DrawerFooter>
172
- <DrawerClose asChild>
173
- <Button variant="outline">Close</Button>
174
- </DrawerClose>
175
- </DrawerFooter>
176
- </DrawerContent>
177
- </Drawer>
178
- </div>
179
- );
180
- };
181
-
182
- export const Directions = () => {
183
- const [direction] = useSelect('direction', {
184
- options: ['top', 'right', 'bottom', 'left'] as const,
185
- defaultValue: 'right',
186
- label: 'Direction',
187
- description: 'Edge from which the drawer slides.',
188
- });
189
- const [size] = useSelect('size', {
190
- options: ['sm', 'md', 'lg', 'xl', 'full'] as const,
191
- defaultValue: 'md',
192
- label: 'Size',
193
- description: 'Width (left/right) or height (top/bottom).',
194
- });
195
-
196
- return (
197
- <Drawer direction={direction}>
198
- <DrawerTrigger asChild>
199
- <Button variant="outline">Open from {direction}</Button>
200
- </DrawerTrigger>
201
- <DrawerContent direction={direction} size={size}>
202
- <DrawerHeader>
203
- <DrawerTitle>Direction: {direction}</DrawerTitle>
204
- <DrawerDescription>
205
- Size presets adapt: horizontal directions size width, vertical
206
- ones size height.
207
- </DrawerDescription>
208
- </DrawerHeader>
209
- <div className="p-4 text-sm">size = {size}</div>
210
- <DrawerFooter>
211
- <DrawerClose asChild>
212
- <Button variant="outline">Close</Button>
213
- </DrawerClose>
214
- </DrawerFooter>
215
- </DrawerContent>
216
- </Drawer>
217
- );
218
- };
219
-
220
- export const CustomWidth = () => (
221
- <Drawer direction="right">
222
- <DrawerTrigger asChild>
223
- <Button variant="outline">Open with width=720px</Button>
224
- </DrawerTrigger>
225
- <DrawerContent direction="right" width="720px">
226
- <DrawerHeader>
227
- <DrawerTitle>Custom width</DrawerTitle>
228
- <DrawerDescription>
229
- Explicit <code>width</code> overrides the <code>size</code> preset.
230
- </DrawerDescription>
231
- </DrawerHeader>
232
- <div className="p-4 text-sm">Width is exactly 720px.</div>
233
- <DrawerFooter>
234
- <DrawerClose asChild>
235
- <Button variant="outline">Close</Button>
236
- </DrawerClose>
237
- </DrawerFooter>
238
- </DrawerContent>
239
- </Drawer>
240
- );
241
-
242
- export const Resizable = () => {
243
- const [size, setSize] = React.useState<number | null>(null);
244
- return (
245
- <div className="space-y-3">
246
- <p className="text-sm text-muted-foreground">
247
- Drag the inner edge to resize. Disabled on mobile (&lt; 768px) by
248
- default — pass <code>resizableOnDesktopOnly={'{false}'}</code> to allow
249
- on touch.
250
- </p>
251
- <Drawer direction="right">
252
- <DrawerTrigger asChild>
253
- <Button variant="outline">Open resizable drawer</Button>
254
- </DrawerTrigger>
255
- <DrawerContent
256
- direction="right"
257
- size="md"
258
- resizable
259
- minSize={320}
260
- maxSize={900}
261
- onSizeChange={setSize}
262
- >
263
- <DrawerHeader>
264
- <DrawerTitle>Resizable</DrawerTitle>
265
- <DrawerDescription>
266
- Current width: {size != null ? `${Math.round(size)}px` : 'preset (480px)'}
267
- </DrawerDescription>
268
- </DrawerHeader>
269
- <div className="p-4 text-sm">Drag the left edge to resize.</div>
270
- <DrawerFooter>
271
- <DrawerClose asChild>
272
- <Button variant="outline">Close</Button>
273
- </DrawerClose>
274
- </DrawerFooter>
275
- </DrawerContent>
276
- </Drawer>
277
- </div>
278
- );
279
- };
280
-
281
- export const ResizablePersisted = () => {
282
- const drawer = useDrawerSize('story-demo', { axis: 'width', min: 320, max: 900 });
283
- return (
284
- <div className="space-y-3">
285
- <p className="text-sm text-muted-foreground">
286
- Persistence is wired via the <code>useDrawerSize(key)</code> hook —
287
- the drawer itself is just a controlled component. Reload the page and
288
- reopen, the last width is restored from <code>localStorage</code>.
289
- </p>
290
- <div className="flex gap-2">
291
- <Drawer direction="right">
292
- <DrawerTrigger asChild>
293
- <Button variant="outline">Open persisted drawer</Button>
294
- </DrawerTrigger>
295
- <DrawerContent
296
- direction="right"
297
- size="md"
298
- resizable
299
- minSize={320}
300
- maxSize={900}
301
- resizedSize={drawer.size}
302
- onSizeChange={drawer.setSize}
303
- >
304
- <DrawerHeader>
305
- <DrawerTitle>Resizable + persisted</DrawerTitle>
306
- <DrawerDescription>
307
- Stored in <code>djangocfg.ui.state</code>. Current:{' '}
308
- {drawer.size != null ? `${Math.round(drawer.size)}px` : 'preset (480px)'}
309
- </DrawerDescription>
310
- </DrawerHeader>
311
- <div className="p-4 text-sm">Drag the left edge to resize.</div>
312
- <DrawerFooter>
313
- <DrawerClose asChild>
314
- <Button variant="outline">Close</Button>
315
- </DrawerClose>
316
- </DrawerFooter>
317
- </DrawerContent>
318
- </Drawer>
319
- <Button variant="ghost" onClick={drawer.reset}>Reset stored size</Button>
320
- </div>
321
- </div>
322
- );
323
- };
324
-
325
- export const NarrowViewport = () => (
326
- <div className="space-y-3">
327
- <p className="text-sm text-muted-foreground">
328
- Resize the browser narrower than the size preset (e.g. &lt; 480px for
329
- <code> md</code>) to see <code>min(100vw, …)</code> clamp the drawer
330
- to the viewport. This is the regression we now guard against.
331
- </p>
332
- <div
333
- style={{ width: 360 }}
334
- className="rounded border border-dashed p-4"
335
- >
336
- <Drawer direction="right">
337
- <DrawerTrigger asChild>
338
- <Button variant="outline">Open in 360px container</Button>
339
- </DrawerTrigger>
340
- <DrawerContent direction="right" size="md">
341
- <DrawerHeader>
342
- <DrawerTitle>Narrow viewport</DrawerTitle>
343
- <DrawerDescription>
344
- Drawer is portaled to the body — viewport width still applies.
345
- </DrawerDescription>
346
- </DrawerHeader>
347
- <div className="p-4 text-sm">
348
- min(100vw, 480px) keeps the drawer within bounds on tiny screens.
349
- </div>
350
- <DrawerFooter>
351
- <DrawerClose asChild>
352
- <Button variant="outline">Close</Button>
353
- </DrawerClose>
354
- </DrawerFooter>
355
- </DrawerContent>
356
- </Drawer>
357
- </div>
358
- </div>
359
- );