@djangocfg/ui-core 2.1.381 → 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.
- package/README.md +85 -21
- package/package.json +5 -12
- package/src/components/boundary/Boundary.tsx +204 -33
- package/src/components/boundary/README.md +249 -0
- package/src/components/boundary/index.ts +9 -2
- package/src/components/index.ts +9 -2
- package/src/components/select/combobox.tsx +47 -19
- package/src/hooks/audio/createSoundBus.ts +172 -0
- package/src/hooks/audio/index.ts +21 -0
- package/src/hooks/audio/useAudioPrefs.ts +91 -0
- package/src/hooks/audio/useNotificationSounds.ts +271 -0
- package/src/hooks/audio/useSoundEffect.ts +78 -0
- package/src/hooks/hotkey/formatHotkey.ts +96 -0
- package/src/hooks/hotkey/index.ts +10 -0
- package/src/hooks/hotkey/useHotkey.ts +106 -34
- package/src/hooks/hotkey/useHotkeyChord.ts +96 -0
- package/src/hooks/hotkey/useHotkeyHelp.ts +68 -0
- package/src/hooks/index.ts +1 -0
- package/src/components/boundary/boundary.story.tsx +0 -109
- package/src/components/data/avatar/avatar.story.tsx +0 -115
- package/src/components/data/badge/badge.story.tsx +0 -56
- package/src/components/data/calendar/calendar.story.tsx +0 -127
- package/src/components/data/carousel/carousel.story.tsx +0 -122
- package/src/components/data/progress/progress.story.tsx +0 -97
- package/src/components/data/table/table.story.tsx +0 -148
- package/src/components/data/toggle/toggle.story.tsx +0 -104
- package/src/components/data/toggle-group/toggle-group.story.tsx +0 -118
- package/src/components/feedback/alert/alert.story.tsx +0 -77
- package/src/components/feedback/empty/empty.story.tsx +0 -115
- package/src/components/feedback/preloader/preloader.story.tsx +0 -86
- package/src/components/feedback/spinner/spinner.story.tsx +0 -66
- package/src/components/forms/button/button.story.tsx +0 -116
- package/src/components/forms/button-download/button-download.story.tsx +0 -112
- package/src/components/forms/button-group/button-group.story.tsx +0 -79
- package/src/components/forms/checkbox/checkbox.story.tsx +0 -89
- package/src/components/forms/input/input.story.tsx +0 -77
- package/src/components/forms/input-group/input-group.story.tsx +0 -119
- package/src/components/forms/input-otp/input-otp.story.tsx +0 -105
- package/src/components/forms/label/label.story.tsx +0 -52
- package/src/components/forms/radio-group/radio-group.story.tsx +0 -113
- package/src/components/forms/slider/slider.story.tsx +0 -134
- package/src/components/forms/switch/switch.story.tsx +0 -98
- package/src/components/forms/textarea/textarea.story.tsx +0 -94
- package/src/components/layout/aspect-ratio/aspect-ratio.story.tsx +0 -94
- package/src/components/layout/card/card.story.tsx +0 -105
- package/src/components/layout/resizable/resizable.story.tsx +0 -119
- package/src/components/layout/scroll-area/scroll-area.story.tsx +0 -172
- package/src/components/layout/separator/separator.story.tsx +0 -69
- package/src/components/layout/skeleton/skeleton.story.tsx +0 -101
- package/src/components/navigation/accordion/accordion.story.tsx +0 -110
- package/src/components/navigation/collapsible/collapsible.story.tsx +0 -133
- package/src/components/navigation/command/command.story.tsx +0 -121
- package/src/components/navigation/context-menu/context-menu.story.tsx +0 -125
- package/src/components/navigation/dropdown-menu/dropdown-menu.story.tsx +0 -208
- package/src/components/navigation/menubar/menubar.story.tsx +0 -152
- package/src/components/navigation/navigation-menu/navigation-menu.story.tsx +0 -154
- package/src/components/navigation/tabs/tabs.story.tsx +0 -98
- package/src/components/overlay/alert-dialog/alert-dialog.story.tsx +0 -104
- package/src/components/overlay/dialog/dialog.story.tsx +0 -212
- package/src/components/overlay/drawer/drawer.story.tsx +0 -359
- package/src/components/overlay/hover-card/hover-card.story.tsx +0 -102
- package/src/components/overlay/popover/popover.story.tsx +0 -127
- package/src/components/overlay/responsive-sheet/responsive-sheet.story.tsx +0 -117
- package/src/components/overlay/sheet/sheet.story.tsx +0 -148
- package/src/components/overlay/tooltip/tooltip.story.tsx +0 -139
- package/src/components/select/combobox-async.story.tsx +0 -215
- package/src/components/select/combobox.story.tsx +0 -226
- package/src/components/select/country-select.story.tsx +0 -261
- package/src/components/select/language-select.story.tsx +0 -264
- package/src/components/select/multi-select.story.tsx +0 -122
- package/src/components/select/select.story.tsx +0 -112
- package/src/components/specialized/copy/copy.story.tsx +0 -77
- package/src/components/specialized/flag/flag.story.tsx +0 -82
- package/src/components/specialized/image-with-fallback/image-with-fallback.story.tsx +0 -105
- package/src/components/specialized/kbd/kbd.story.tsx +0 -113
- package/src/lib/dialog-service/dialog-service.story.tsx +0 -263
- package/src/stories/index.ts +0 -28
- package/src/styles/theme/theme-tokens.story.tsx +0 -157
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { defineStory, useBoolean } from '@djangocfg/playground';
|
|
3
|
-
import { Switch } from '.';
|
|
4
|
-
import { Label } from '../../forms/label';
|
|
5
|
-
|
|
6
|
-
export default defineStory({
|
|
7
|
-
title: 'Core/Switch',
|
|
8
|
-
component: Switch,
|
|
9
|
-
description: 'Toggle switch for on/off states.',
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export const Interactive = () => {
|
|
13
|
-
const [disabled] = useBoolean('disabled', {
|
|
14
|
-
defaultValue: false,
|
|
15
|
-
label: 'Disabled',
|
|
16
|
-
description: 'Disable switch',
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="flex items-center space-x-2">
|
|
21
|
-
<Switch id="airplane-mode" disabled={disabled} />
|
|
22
|
-
<Label htmlFor="airplane-mode">Airplane Mode</Label>
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const Default = () => (
|
|
28
|
-
<div className="flex items-center space-x-2">
|
|
29
|
-
<Switch id="default" />
|
|
30
|
-
<Label htmlFor="default">Default switch</Label>
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
export const Checked = () => (
|
|
35
|
-
<div className="flex items-center space-x-2">
|
|
36
|
-
<Switch id="checked" defaultChecked />
|
|
37
|
-
<Label htmlFor="checked">Checked by default</Label>
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
export const Disabled = () => (
|
|
42
|
-
<div className="space-y-4">
|
|
43
|
-
<div className="flex items-center space-x-2">
|
|
44
|
-
<Switch id="disabled-off" disabled />
|
|
45
|
-
<Label htmlFor="disabled-off">Disabled off</Label>
|
|
46
|
-
</div>
|
|
47
|
-
<div className="flex items-center space-x-2">
|
|
48
|
-
<Switch id="disabled-on" disabled defaultChecked />
|
|
49
|
-
<Label htmlFor="disabled-on">Disabled on</Label>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
export const Settings = () => {
|
|
55
|
-
const [settings, setSettings] = useState({
|
|
56
|
-
notifications: true,
|
|
57
|
-
darkMode: false,
|
|
58
|
-
autoSave: true,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div className="space-y-6 max-w-sm">
|
|
63
|
-
<div className="flex items-center justify-between">
|
|
64
|
-
<div>
|
|
65
|
-
<Label htmlFor="notifications" className="block">Notifications</Label>
|
|
66
|
-
<span className="text-sm text-muted-foreground">Receive push notifications</span>
|
|
67
|
-
</div>
|
|
68
|
-
<Switch
|
|
69
|
-
id="notifications"
|
|
70
|
-
checked={settings.notifications}
|
|
71
|
-
onCheckedChange={(checked) => setSettings({ ...settings, notifications: checked })}
|
|
72
|
-
/>
|
|
73
|
-
</div>
|
|
74
|
-
<div className="flex items-center justify-between">
|
|
75
|
-
<div>
|
|
76
|
-
<Label htmlFor="darkMode" className="block">Dark Mode</Label>
|
|
77
|
-
<span className="text-sm text-muted-foreground">Use dark theme</span>
|
|
78
|
-
</div>
|
|
79
|
-
<Switch
|
|
80
|
-
id="darkMode"
|
|
81
|
-
checked={settings.darkMode}
|
|
82
|
-
onCheckedChange={(checked) => setSettings({ ...settings, darkMode: checked })}
|
|
83
|
-
/>
|
|
84
|
-
</div>
|
|
85
|
-
<div className="flex items-center justify-between">
|
|
86
|
-
<div>
|
|
87
|
-
<Label htmlFor="autoSave" className="block">Auto Save</Label>
|
|
88
|
-
<span className="text-sm text-muted-foreground">Save changes automatically</span>
|
|
89
|
-
</div>
|
|
90
|
-
<Switch
|
|
91
|
-
id="autoSave"
|
|
92
|
-
checked={settings.autoSave}
|
|
93
|
-
onCheckedChange={(checked) => setSettings({ ...settings, autoSave: checked })}
|
|
94
|
-
/>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
);
|
|
98
|
-
};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { defineStory, useBoolean, useNumber } from '@djangocfg/playground';
|
|
2
|
-
import { Textarea } from '.';
|
|
3
|
-
import { Label } from '../../forms/label';
|
|
4
|
-
|
|
5
|
-
export default defineStory({
|
|
6
|
-
title: 'Core/Textarea',
|
|
7
|
-
component: Textarea,
|
|
8
|
-
description: 'Multi-line text input.',
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export const Interactive = () => {
|
|
12
|
-
const [disabled] = useBoolean('disabled', {
|
|
13
|
-
defaultValue: false,
|
|
14
|
-
label: 'Disabled',
|
|
15
|
-
description: 'Disable textarea',
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const [rows] = useNumber('rows', {
|
|
19
|
-
defaultValue: 4,
|
|
20
|
-
min: 2,
|
|
21
|
-
max: 10,
|
|
22
|
-
label: 'Rows',
|
|
23
|
-
description: 'Number of visible rows',
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div className="max-w-md space-y-2">
|
|
28
|
-
<Label htmlFor="message">Message</Label>
|
|
29
|
-
<Textarea
|
|
30
|
-
id="message"
|
|
31
|
-
placeholder="Type your message here..."
|
|
32
|
-
disabled={disabled}
|
|
33
|
-
rows={rows}
|
|
34
|
-
/>
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const Default = () => (
|
|
40
|
-
<div className="max-w-md">
|
|
41
|
-
<Textarea placeholder="Type your message here..." />
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
export const WithLabel = () => (
|
|
46
|
-
<div className="max-w-md space-y-2">
|
|
47
|
-
<Label htmlFor="bio">Bio</Label>
|
|
48
|
-
<Textarea id="bio" placeholder="Tell us about yourself..." />
|
|
49
|
-
<p className="text-sm text-muted-foreground">
|
|
50
|
-
Write a short introduction about yourself.
|
|
51
|
-
</p>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
export const Disabled = () => (
|
|
56
|
-
<div className="max-w-md">
|
|
57
|
-
<Textarea placeholder="Disabled textarea" disabled />
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
export const WithDefaultValue = () => (
|
|
62
|
-
<div className="max-w-md space-y-2">
|
|
63
|
-
<Label htmlFor="notes">Notes</Label>
|
|
64
|
-
<Textarea
|
|
65
|
-
id="notes"
|
|
66
|
-
defaultValue="This is some default text that was pre-filled in the textarea."
|
|
67
|
-
/>
|
|
68
|
-
</div>
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
export const Sizes = () => (
|
|
72
|
-
<div className="max-w-md space-y-4">
|
|
73
|
-
<div className="space-y-2">
|
|
74
|
-
<Label>Small (2 rows)</Label>
|
|
75
|
-
<Textarea placeholder="Small textarea" rows={2} />
|
|
76
|
-
</div>
|
|
77
|
-
<div className="space-y-2">
|
|
78
|
-
<Label>Medium (4 rows)</Label>
|
|
79
|
-
<Textarea placeholder="Medium textarea" rows={4} />
|
|
80
|
-
</div>
|
|
81
|
-
<div className="space-y-2">
|
|
82
|
-
<Label>Large (8 rows)</Label>
|
|
83
|
-
<Textarea placeholder="Large textarea" rows={8} />
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
export const WithCharacterCount = () => (
|
|
89
|
-
<div className="max-w-md space-y-2">
|
|
90
|
-
<Label htmlFor="limited">Description</Label>
|
|
91
|
-
<Textarea id="limited" placeholder="Enter description..." maxLength={200} />
|
|
92
|
-
<p className="text-sm text-muted-foreground text-right">0/200</p>
|
|
93
|
-
</div>
|
|
94
|
-
);
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { defineStory, useSelect } from '@djangocfg/playground';
|
|
2
|
-
import { AspectRatio } from '.';
|
|
3
|
-
|
|
4
|
-
export default defineStory({
|
|
5
|
-
title: 'Core/Aspect Ratio',
|
|
6
|
-
component: AspectRatio,
|
|
7
|
-
description: 'Container that maintains a specific aspect ratio.',
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
export const Interactive = () => {
|
|
11
|
-
const [ratio] = useSelect('ratio', {
|
|
12
|
-
options: ['16/9', '4/3', '1/1', '21/9'] as const,
|
|
13
|
-
defaultValue: '16/9',
|
|
14
|
-
label: 'Ratio',
|
|
15
|
-
description: 'Aspect ratio',
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const ratioMap = {
|
|
19
|
-
'16/9': 16 / 9,
|
|
20
|
-
'4/3': 4 / 3,
|
|
21
|
-
'1/1': 1,
|
|
22
|
-
'21/9': 21 / 9,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className="w-[450px]">
|
|
27
|
-
<AspectRatio ratio={ratioMap[ratio]} className="bg-muted rounded-lg overflow-hidden">
|
|
28
|
-
<img
|
|
29
|
-
src="https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=800"
|
|
30
|
-
alt="Car"
|
|
31
|
-
className="h-full w-full object-cover"
|
|
32
|
-
/>
|
|
33
|
-
</AspectRatio>
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const Ratio16By9 = () => (
|
|
39
|
-
<div className="w-[450px]">
|
|
40
|
-
<AspectRatio ratio={16 / 9} className="bg-muted rounded-lg overflow-hidden">
|
|
41
|
-
<img
|
|
42
|
-
src="https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=800"
|
|
43
|
-
alt="Car"
|
|
44
|
-
className="h-full w-full object-cover"
|
|
45
|
-
/>
|
|
46
|
-
</AspectRatio>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
export const Ratio4By3 = () => (
|
|
51
|
-
<div className="w-[300px]">
|
|
52
|
-
<AspectRatio ratio={4 / 3} className="bg-muted rounded-lg overflow-hidden">
|
|
53
|
-
<img
|
|
54
|
-
src="https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=600"
|
|
55
|
-
alt="Porsche"
|
|
56
|
-
className="h-full w-full object-cover"
|
|
57
|
-
/>
|
|
58
|
-
</AspectRatio>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
export const Square = () => (
|
|
63
|
-
<div className="w-[200px]">
|
|
64
|
-
<AspectRatio ratio={1} className="bg-muted rounded-lg overflow-hidden">
|
|
65
|
-
<img
|
|
66
|
-
src="https://images.unsplash.com/photo-1542362567-b07e54358753?w=400"
|
|
67
|
-
alt="BMW"
|
|
68
|
-
className="h-full w-full object-cover"
|
|
69
|
-
/>
|
|
70
|
-
</AspectRatio>
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
export const WithPlaceholder = () => (
|
|
75
|
-
<div className="w-[400px]">
|
|
76
|
-
<AspectRatio ratio={16 / 9} className="bg-muted rounded-lg flex items-center justify-center">
|
|
77
|
-
<span className="text-muted-foreground">No image available</span>
|
|
78
|
-
</AspectRatio>
|
|
79
|
-
</div>
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
export const Video = () => (
|
|
83
|
-
<div className="w-[560px]">
|
|
84
|
-
<AspectRatio ratio={16 / 9} className="bg-black rounded-lg overflow-hidden">
|
|
85
|
-
<iframe
|
|
86
|
-
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
|
|
87
|
-
title="Video"
|
|
88
|
-
className="h-full w-full"
|
|
89
|
-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
90
|
-
allowFullScreen
|
|
91
|
-
/>
|
|
92
|
-
</AspectRatio>
|
|
93
|
-
</div>
|
|
94
|
-
);
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { defineStory } from '@djangocfg/playground';
|
|
2
|
-
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '.';
|
|
3
|
-
import { Button } from '../../forms/button';
|
|
4
|
-
import { Input } from '../../forms/input';
|
|
5
|
-
import { Label } from '../../forms/label';
|
|
6
|
-
|
|
7
|
-
export default defineStory({
|
|
8
|
-
title: 'Core/Card',
|
|
9
|
-
component: Card,
|
|
10
|
-
description: 'Card container for grouping related content.',
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
export const Default = () => (
|
|
14
|
-
<Card className="max-w-sm">
|
|
15
|
-
<CardHeader>
|
|
16
|
-
<CardTitle>Card Title</CardTitle>
|
|
17
|
-
<CardDescription>Card description goes here.</CardDescription>
|
|
18
|
-
</CardHeader>
|
|
19
|
-
<CardContent>
|
|
20
|
-
<p>This is the card content. You can put any content here.</p>
|
|
21
|
-
</CardContent>
|
|
22
|
-
<CardFooter>
|
|
23
|
-
<Button>Action</Button>
|
|
24
|
-
</CardFooter>
|
|
25
|
-
</Card>
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
export const LoginCard = () => (
|
|
29
|
-
<Card className="max-w-sm">
|
|
30
|
-
<CardHeader>
|
|
31
|
-
<CardTitle>Login</CardTitle>
|
|
32
|
-
<CardDescription>Enter your credentials to access your account.</CardDescription>
|
|
33
|
-
</CardHeader>
|
|
34
|
-
<CardContent className="space-y-4">
|
|
35
|
-
<div className="space-y-2">
|
|
36
|
-
<Label htmlFor="email">Email</Label>
|
|
37
|
-
<Input id="email" type="email" placeholder="Enter your email" />
|
|
38
|
-
</div>
|
|
39
|
-
<div className="space-y-2">
|
|
40
|
-
<Label htmlFor="password">Password</Label>
|
|
41
|
-
<Input id="password" type="password" placeholder="Enter password" />
|
|
42
|
-
</div>
|
|
43
|
-
</CardContent>
|
|
44
|
-
<CardFooter className="flex justify-between">
|
|
45
|
-
<Button variant="outline">Cancel</Button>
|
|
46
|
-
<Button>Login</Button>
|
|
47
|
-
</CardFooter>
|
|
48
|
-
</Card>
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
export const ProductCard = () => (
|
|
52
|
-
<Card className="max-w-xs overflow-hidden">
|
|
53
|
-
<div className="aspect-video bg-muted" />
|
|
54
|
-
<CardHeader>
|
|
55
|
-
<CardTitle>Product Name</CardTitle>
|
|
56
|
-
<CardDescription>$99.99</CardDescription>
|
|
57
|
-
</CardHeader>
|
|
58
|
-
<CardContent>
|
|
59
|
-
<p className="text-sm text-muted-foreground">
|
|
60
|
-
This is a brief description of the product with key features.
|
|
61
|
-
</p>
|
|
62
|
-
</CardContent>
|
|
63
|
-
<CardFooter>
|
|
64
|
-
<Button className="w-full">Add to Cart</Button>
|
|
65
|
-
</CardFooter>
|
|
66
|
-
</Card>
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
export const StatsCards = () => (
|
|
70
|
-
<div className="grid grid-cols-3 gap-4">
|
|
71
|
-
<Card>
|
|
72
|
-
<CardHeader className="pb-2">
|
|
73
|
-
<CardDescription>Total Revenue</CardDescription>
|
|
74
|
-
<CardTitle className="text-2xl">$45,231.89</CardTitle>
|
|
75
|
-
</CardHeader>
|
|
76
|
-
<CardContent>
|
|
77
|
-
<p className="text-xs text-muted-foreground">+20.1% from last month</p>
|
|
78
|
-
</CardContent>
|
|
79
|
-
</Card>
|
|
80
|
-
<Card>
|
|
81
|
-
<CardHeader className="pb-2">
|
|
82
|
-
<CardDescription>Subscriptions</CardDescription>
|
|
83
|
-
<CardTitle className="text-2xl">+2,350</CardTitle>
|
|
84
|
-
</CardHeader>
|
|
85
|
-
<CardContent>
|
|
86
|
-
<p className="text-xs text-muted-foreground">+180.1% from last month</p>
|
|
87
|
-
</CardContent>
|
|
88
|
-
</Card>
|
|
89
|
-
<Card>
|
|
90
|
-
<CardHeader className="pb-2">
|
|
91
|
-
<CardDescription>Active Now</CardDescription>
|
|
92
|
-
<CardTitle className="text-2xl">+573</CardTitle>
|
|
93
|
-
</CardHeader>
|
|
94
|
-
<CardContent>
|
|
95
|
-
<p className="text-xs text-muted-foreground">+201 since last hour</p>
|
|
96
|
-
</CardContent>
|
|
97
|
-
</Card>
|
|
98
|
-
</div>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
export const SimpleCard = () => (
|
|
102
|
-
<Card className="max-w-md p-6">
|
|
103
|
-
<p>A simple card with just content and padding.</p>
|
|
104
|
-
</Card>
|
|
105
|
-
);
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { defineStory } from '@djangocfg/playground';
|
|
2
|
-
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '.';
|
|
3
|
-
|
|
4
|
-
export default defineStory({
|
|
5
|
-
title: 'Core/Resizable',
|
|
6
|
-
component: ResizablePanelGroup,
|
|
7
|
-
description: 'Resizable panels with drag handles.',
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
export const Horizontal = () => (
|
|
11
|
-
<ResizablePanelGroup
|
|
12
|
-
direction="horizontal"
|
|
13
|
-
className="max-w-md rounded-lg border"
|
|
14
|
-
>
|
|
15
|
-
<ResizablePanel defaultSize={50}>
|
|
16
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
17
|
-
<span className="font-semibold">One</span>
|
|
18
|
-
</div>
|
|
19
|
-
</ResizablePanel>
|
|
20
|
-
<ResizableHandle />
|
|
21
|
-
<ResizablePanel defaultSize={50}>
|
|
22
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
23
|
-
<span className="font-semibold">Two</span>
|
|
24
|
-
</div>
|
|
25
|
-
</ResizablePanel>
|
|
26
|
-
</ResizablePanelGroup>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
export const Vertical = () => (
|
|
30
|
-
<ResizablePanelGroup
|
|
31
|
-
direction="vertical"
|
|
32
|
-
className="max-w-md rounded-lg border"
|
|
33
|
-
>
|
|
34
|
-
<ResizablePanel defaultSize={25}>
|
|
35
|
-
<div className="flex h-full items-center justify-center p-6">
|
|
36
|
-
<span className="font-semibold">Header</span>
|
|
37
|
-
</div>
|
|
38
|
-
</ResizablePanel>
|
|
39
|
-
<ResizableHandle />
|
|
40
|
-
<ResizablePanel defaultSize={75}>
|
|
41
|
-
<div className="flex h-full items-center justify-center p-6">
|
|
42
|
-
<span className="font-semibold">Content</span>
|
|
43
|
-
</div>
|
|
44
|
-
</ResizablePanel>
|
|
45
|
-
</ResizablePanelGroup>
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
export const ThreePanels = () => (
|
|
49
|
-
<ResizablePanelGroup
|
|
50
|
-
direction="horizontal"
|
|
51
|
-
className="max-w-lg rounded-lg border"
|
|
52
|
-
>
|
|
53
|
-
<ResizablePanel defaultSize={25} minSize={15}>
|
|
54
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
55
|
-
<span className="font-semibold">Sidebar</span>
|
|
56
|
-
</div>
|
|
57
|
-
</ResizablePanel>
|
|
58
|
-
<ResizableHandle />
|
|
59
|
-
<ResizablePanel defaultSize={50}>
|
|
60
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
61
|
-
<span className="font-semibold">Content</span>
|
|
62
|
-
</div>
|
|
63
|
-
</ResizablePanel>
|
|
64
|
-
<ResizableHandle />
|
|
65
|
-
<ResizablePanel defaultSize={25} minSize={15}>
|
|
66
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
67
|
-
<span className="font-semibold">Details</span>
|
|
68
|
-
</div>
|
|
69
|
-
</ResizablePanel>
|
|
70
|
-
</ResizablePanelGroup>
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
export const Nested = () => (
|
|
74
|
-
<ResizablePanelGroup
|
|
75
|
-
direction="horizontal"
|
|
76
|
-
className="max-w-lg rounded-lg border"
|
|
77
|
-
>
|
|
78
|
-
<ResizablePanel defaultSize={25}>
|
|
79
|
-
<div className="flex h-[300px] items-center justify-center p-6">
|
|
80
|
-
<span className="font-semibold">Sidebar</span>
|
|
81
|
-
</div>
|
|
82
|
-
</ResizablePanel>
|
|
83
|
-
<ResizableHandle />
|
|
84
|
-
<ResizablePanel defaultSize={75}>
|
|
85
|
-
<ResizablePanelGroup direction="vertical">
|
|
86
|
-
<ResizablePanel defaultSize={50}>
|
|
87
|
-
<div className="flex h-full items-center justify-center p-6">
|
|
88
|
-
<span className="font-semibold">Top</span>
|
|
89
|
-
</div>
|
|
90
|
-
</ResizablePanel>
|
|
91
|
-
<ResizableHandle />
|
|
92
|
-
<ResizablePanel defaultSize={50}>
|
|
93
|
-
<div className="flex h-full items-center justify-center p-6">
|
|
94
|
-
<span className="font-semibold">Bottom</span>
|
|
95
|
-
</div>
|
|
96
|
-
</ResizablePanel>
|
|
97
|
-
</ResizablePanelGroup>
|
|
98
|
-
</ResizablePanel>
|
|
99
|
-
</ResizablePanelGroup>
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
export const WithHandle = () => (
|
|
103
|
-
<ResizablePanelGroup
|
|
104
|
-
direction="horizontal"
|
|
105
|
-
className="max-w-md rounded-lg border"
|
|
106
|
-
>
|
|
107
|
-
<ResizablePanel defaultSize={50}>
|
|
108
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
109
|
-
<span className="font-semibold">Left</span>
|
|
110
|
-
</div>
|
|
111
|
-
</ResizablePanel>
|
|
112
|
-
<ResizableHandle withHandle />
|
|
113
|
-
<ResizablePanel defaultSize={50}>
|
|
114
|
-
<div className="flex h-[200px] items-center justify-center p-6">
|
|
115
|
-
<span className="font-semibold">Right</span>
|
|
116
|
-
</div>
|
|
117
|
-
</ResizablePanel>
|
|
118
|
-
</ResizablePanelGroup>
|
|
119
|
-
);
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { defineStory } from '@djangocfg/playground';
|
|
2
|
-
import { ScrollArea, ScrollBar } from '.';
|
|
3
|
-
import { Separator } from '../../layout/separator';
|
|
4
|
-
|
|
5
|
-
export default defineStory({
|
|
6
|
-
title: 'Core/Scroll Area',
|
|
7
|
-
component: ScrollArea,
|
|
8
|
-
description: 'Custom scrollable area with styled scrollbars.',
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const tags = Array.from({ length: 50 }).map(
|
|
12
|
-
(_, i, a) => `v1.2.0-beta.${a.length - i}`
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
const works = [
|
|
16
|
-
{ artist: 'Ornella Binni', art: 'Sunset Dreams' },
|
|
17
|
-
{ artist: 'Tom Byrom', art: 'Mountain Peak' },
|
|
18
|
-
{ artist: 'Vladimir Malyavko', art: 'City Lights' },
|
|
19
|
-
{ artist: 'Fabio Fistarol', art: 'Ocean Blue' },
|
|
20
|
-
{ artist: 'Anthony Intraversato', art: 'Forest Path' },
|
|
21
|
-
{ artist: 'Ash Edmonds', art: 'Desert Storm' },
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
export const Vertical = () => (
|
|
25
|
-
<ScrollArea className="h-72 w-48 rounded-md border">
|
|
26
|
-
<div className="p-4">
|
|
27
|
-
<h4 className="mb-4 text-sm font-medium leading-none">Tags</h4>
|
|
28
|
-
{tags.map((tag) => (
|
|
29
|
-
<div key={tag}>
|
|
30
|
-
<div className="text-sm">{tag}</div>
|
|
31
|
-
<Separator className="my-2" />
|
|
32
|
-
</div>
|
|
33
|
-
))}
|
|
34
|
-
</div>
|
|
35
|
-
</ScrollArea>
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
export const Horizontal = () => (
|
|
39
|
-
<ScrollArea className="w-96 whitespace-nowrap rounded-md border" orientation="horizontal">
|
|
40
|
-
<div className="flex w-max space-x-4 p-4">
|
|
41
|
-
{works.map((work) => (
|
|
42
|
-
<figure key={work.artist} className="shrink-0">
|
|
43
|
-
<div className="overflow-hidden rounded-md">
|
|
44
|
-
<div className="h-[150px] w-[150px] bg-muted flex items-center justify-center">
|
|
45
|
-
<span className="text-muted-foreground text-sm">{work.art}</span>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
<figcaption className="pt-2 text-xs text-muted-foreground">
|
|
49
|
-
Photo by{' '}
|
|
50
|
-
<span className="font-semibold text-foreground">{work.artist}</span>
|
|
51
|
-
</figcaption>
|
|
52
|
-
</figure>
|
|
53
|
-
))}
|
|
54
|
-
</div>
|
|
55
|
-
</ScrollArea>
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
export const Both = () => (
|
|
59
|
-
<ScrollArea className="h-72 w-72 rounded-md border" orientation="both">
|
|
60
|
-
<div className="p-4">
|
|
61
|
-
{Array.from({ length: 20 }).map((_, i) => (
|
|
62
|
-
<div key={i} className="whitespace-nowrap py-2">
|
|
63
|
-
This is a very long line of text that will require horizontal scrolling. Item {i + 1}
|
|
64
|
-
</div>
|
|
65
|
-
))}
|
|
66
|
-
</div>
|
|
67
|
-
</ScrollArea>
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
export const InCard = () => (
|
|
71
|
-
<div className="rounded-lg border p-4 max-w-sm">
|
|
72
|
-
<h4 className="font-medium mb-2">Notifications</h4>
|
|
73
|
-
<ScrollArea className="h-48">
|
|
74
|
-
<div className="space-y-2">
|
|
75
|
-
{Array.from({ length: 10 }).map((_, i) => (
|
|
76
|
-
<div key={i} className="rounded-md border p-3">
|
|
77
|
-
<p className="text-sm font-medium">Notification {i + 1}</p>
|
|
78
|
-
<p className="text-xs text-muted-foreground">Just now</p>
|
|
79
|
-
</div>
|
|
80
|
-
))}
|
|
81
|
-
</div>
|
|
82
|
-
</ScrollArea>
|
|
83
|
-
</div>
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
export const Chat = () => (
|
|
87
|
-
<div className="rounded-lg border max-w-md">
|
|
88
|
-
<div className="p-4 border-b">
|
|
89
|
-
<h4 className="font-medium">Chat</h4>
|
|
90
|
-
</div>
|
|
91
|
-
<ScrollArea className="h-64 p-4">
|
|
92
|
-
<div className="space-y-4">
|
|
93
|
-
{Array.from({ length: 15 }).map((_, i) => (
|
|
94
|
-
<div
|
|
95
|
-
key={i}
|
|
96
|
-
className={`flex ${i % 2 === 0 ? 'justify-start' : 'justify-end'}`}
|
|
97
|
-
>
|
|
98
|
-
<div
|
|
99
|
-
className={`rounded-lg px-3 py-2 max-w-[80%] ${
|
|
100
|
-
i % 2 === 0 ? 'bg-muted' : 'bg-primary text-primary-foreground'
|
|
101
|
-
}`}
|
|
102
|
-
>
|
|
103
|
-
<p className="text-sm">Message {i + 1}</p>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
))}
|
|
107
|
-
</div>
|
|
108
|
-
</ScrollArea>
|
|
109
|
-
</div>
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
const filters = [
|
|
113
|
-
'All', 'Electronics', 'Clothing', 'Home & Garden', 'Sports', 'Books',
|
|
114
|
-
'Toys', 'Beauty', 'Automotive', 'Health', 'Food', 'Music', 'Movies'
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
export const HorizontalFilterTabs = () => (
|
|
118
|
-
<div className="max-w-sm">
|
|
119
|
-
<ScrollArea className="w-full" orientation="horizontal">
|
|
120
|
-
<div className="flex gap-2 pb-2">
|
|
121
|
-
{filters.map((filter) => (
|
|
122
|
-
<button
|
|
123
|
-
key={filter}
|
|
124
|
-
className="px-3 py-1.5 rounded-md text-xs font-medium whitespace-nowrap bg-muted text-muted-foreground hover:text-foreground transition-colors"
|
|
125
|
-
>
|
|
126
|
-
{filter}
|
|
127
|
-
</button>
|
|
128
|
-
))}
|
|
129
|
-
</div>
|
|
130
|
-
</ScrollArea>
|
|
131
|
-
<p className="text-xs text-muted-foreground mt-2">
|
|
132
|
-
Scroll horizontally to see more filters
|
|
133
|
-
</p>
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
export const VerticalNoHorizontalScroll = () => (
|
|
138
|
-
<div className="max-w-xs border rounded-lg">
|
|
139
|
-
<p className="text-sm font-medium p-3 border-b">overflowX="hidden" — no horizontal bleed</p>
|
|
140
|
-
<ScrollArea className="h-48" overflowX="hidden">
|
|
141
|
-
<div className="p-3 space-y-2">
|
|
142
|
-
{Array.from({ length: 10 }).map((_, i) => (
|
|
143
|
-
<div key={i} className="flex items-center gap-2 p-2 rounded-md bg-muted/40">
|
|
144
|
-
<div className="w-2 h-8 rounded-full bg-primary/50 flex-shrink-0" />
|
|
145
|
-
<span className="text-sm truncate flex-1 min-w-0">
|
|
146
|
-
Track name that is very long and should truncate gracefully {i + 1}
|
|
147
|
-
</span>
|
|
148
|
-
</div>
|
|
149
|
-
))}
|
|
150
|
-
</div>
|
|
151
|
-
</ScrollArea>
|
|
152
|
-
</div>
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
export const HorizontalChips = () => (
|
|
156
|
-
<div className="max-w-xs">
|
|
157
|
-
<p className="text-sm font-medium mb-2">Selected tags:</p>
|
|
158
|
-
<ScrollArea className="w-full" orientation="horizontal">
|
|
159
|
-
<div className="flex gap-1 pb-2">
|
|
160
|
-
{['React', 'TypeScript', 'Tailwind', 'Next.js', 'Radix UI', 'Zustand', 'TanStack Query'].map((tag) => (
|
|
161
|
-
<span
|
|
162
|
-
key={tag}
|
|
163
|
-
className="inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs bg-primary/10 text-primary whitespace-nowrap"
|
|
164
|
-
>
|
|
165
|
-
{tag}
|
|
166
|
-
<button className="hover:bg-primary/20 rounded-full p-0.5">×</button>
|
|
167
|
-
</span>
|
|
168
|
-
))}
|
|
169
|
-
</div>
|
|
170
|
-
</ScrollArea>
|
|
171
|
-
</div>
|
|
172
|
-
);
|