@djangocfg/ui-core 2.1.120 → 2.1.123

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 (55) hide show
  1. package/package.json +8 -5
  2. package/src/components/accordion.story.tsx +110 -0
  3. package/src/components/alert-dialog.story.tsx +104 -0
  4. package/src/components/alert.story.tsx +77 -0
  5. package/src/components/aspect-ratio.story.tsx +94 -0
  6. package/src/components/avatar.story.tsx +115 -0
  7. package/src/components/badge.story.tsx +56 -0
  8. package/src/components/button-download.story.tsx +112 -0
  9. package/src/components/button-group.story.tsx +79 -0
  10. package/src/components/button.story.tsx +116 -0
  11. package/src/components/calendar.story.tsx +126 -0
  12. package/src/components/card.story.tsx +105 -0
  13. package/src/components/carousel.story.tsx +122 -0
  14. package/src/components/carousel.tsx +2 -2
  15. package/src/components/checkbox.story.tsx +89 -0
  16. package/src/components/collapsible.story.tsx +133 -0
  17. package/src/components/combobox.story.tsx +145 -0
  18. package/src/components/command.story.tsx +121 -0
  19. package/src/components/context-menu.story.tsx +125 -0
  20. package/src/components/copy.story.tsx +77 -0
  21. package/src/components/dialog.story.tsx +137 -0
  22. package/src/components/drawer.story.tsx +131 -0
  23. package/src/components/dropdown-menu.story.tsx +208 -0
  24. package/src/components/empty.story.tsx +115 -0
  25. package/src/components/hover-card.story.tsx +102 -0
  26. package/src/components/image-with-fallback.story.tsx +105 -0
  27. package/src/components/input-group.story.tsx +119 -0
  28. package/src/components/input-otp.story.tsx +105 -0
  29. package/src/components/input.story.tsx +77 -0
  30. package/src/components/kbd.story.tsx +113 -0
  31. package/src/components/label.story.tsx +52 -0
  32. package/src/components/menubar.story.tsx +152 -0
  33. package/src/components/multi-select.story.tsx +122 -0
  34. package/src/components/navigation-menu.story.tsx +154 -0
  35. package/src/components/popover.story.tsx +127 -0
  36. package/src/components/preloader.story.tsx +86 -0
  37. package/src/components/progress.story.tsx +97 -0
  38. package/src/components/radio-group.story.tsx +113 -0
  39. package/src/components/resizable.story.tsx +119 -0
  40. package/src/components/responsive-sheet.story.tsx +117 -0
  41. package/src/components/scroll-area.story.tsx +112 -0
  42. package/src/components/select.story.tsx +112 -0
  43. package/src/components/separator.story.tsx +69 -0
  44. package/src/components/sheet.story.tsx +148 -0
  45. package/src/components/skeleton.story.tsx +101 -0
  46. package/src/components/slider.story.tsx +113 -0
  47. package/src/components/spinner.story.tsx +66 -0
  48. package/src/components/switch.story.tsx +98 -0
  49. package/src/components/table.story.tsx +148 -0
  50. package/src/components/tabs.story.tsx +98 -0
  51. package/src/components/tabs.tsx +1 -1
  52. package/src/components/textarea.story.tsx +94 -0
  53. package/src/components/toggle-group.story.tsx +118 -0
  54. package/src/components/toggle.story.tsx +104 -0
  55. package/src/components/tooltip.story.tsx +139 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.120",
3
+ "version": "2.1.123",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -62,10 +62,11 @@
62
62
  ],
63
63
  "scripts": {
64
64
  "lint": "eslint .",
65
- "check": "tsc --noEmit"
65
+ "check": "tsc --noEmit",
66
+ "playground": "playground dev"
66
67
  },
67
68
  "peerDependencies": {
68
- "@djangocfg/i18n": "^2.1.120",
69
+ "@djangocfg/i18n": "^2.1.123",
69
70
  "lucide-react": "^0.545.0",
70
71
  "moment": "^2.30.1",
71
72
  "react": "^19.0.0",
@@ -123,11 +124,13 @@
123
124
  "vaul": "1.1.2"
124
125
  },
125
126
  "devDependencies": {
126
- "@djangocfg/i18n": "^2.1.120",
127
- "@djangocfg/typescript-config": "^2.1.120",
127
+ "@djangocfg/i18n": "^2.1.123",
128
+ "@djangocfg/playground": "workspace:*",
129
+ "@djangocfg/typescript-config": "^2.1.123",
128
130
  "@types/node": "^24.7.2",
129
131
  "@types/react": "^19.1.0",
130
132
  "@types/react-dom": "^19.1.0",
133
+ "lucide-react": "^0.545.0",
131
134
  "typescript": "^5.9.3"
132
135
  },
133
136
  "publishConfig": {
@@ -0,0 +1,110 @@
1
+ import { defineStory } from '@djangocfg/playground';
2
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './accordion';
3
+
4
+ export default defineStory({
5
+ title: 'Core/Accordion',
6
+ component: Accordion,
7
+ description: 'Collapsible content sections.',
8
+ });
9
+
10
+ export const Default = () => (
11
+ <Accordion type="single" collapsible className="w-full max-w-md">
12
+ <AccordionItem value="item-1">
13
+ <AccordionTrigger>Is it accessible?</AccordionTrigger>
14
+ <AccordionContent>
15
+ Yes. It adheres to the WAI-ARIA design pattern.
16
+ </AccordionContent>
17
+ </AccordionItem>
18
+ <AccordionItem value="item-2">
19
+ <AccordionTrigger>Is it styled?</AccordionTrigger>
20
+ <AccordionContent>
21
+ Yes. It comes with default styles that matches the other components.
22
+ </AccordionContent>
23
+ </AccordionItem>
24
+ <AccordionItem value="item-3">
25
+ <AccordionTrigger>Is it animated?</AccordionTrigger>
26
+ <AccordionContent>
27
+ Yes. It's animated by default, but you can disable it if you prefer.
28
+ </AccordionContent>
29
+ </AccordionItem>
30
+ </Accordion>
31
+ );
32
+
33
+ export const Multiple = () => (
34
+ <Accordion type="multiple" className="w-full max-w-md">
35
+ <AccordionItem value="item-1">
36
+ <AccordionTrigger>Section 1</AccordionTrigger>
37
+ <AccordionContent>
38
+ Content for section 1. Multiple sections can be open at the same time.
39
+ </AccordionContent>
40
+ </AccordionItem>
41
+ <AccordionItem value="item-2">
42
+ <AccordionTrigger>Section 2</AccordionTrigger>
43
+ <AccordionContent>
44
+ Content for section 2.
45
+ </AccordionContent>
46
+ </AccordionItem>
47
+ <AccordionItem value="item-3">
48
+ <AccordionTrigger>Section 3</AccordionTrigger>
49
+ <AccordionContent>
50
+ Content for section 3.
51
+ </AccordionContent>
52
+ </AccordionItem>
53
+ </Accordion>
54
+ );
55
+
56
+ export const FAQ = () => (
57
+ <Accordion type="single" collapsible className="w-full max-w-lg">
58
+ <AccordionItem value="faq-1">
59
+ <AccordionTrigger>What payment methods do you accept?</AccordionTrigger>
60
+ <AccordionContent>
61
+ We accept all major credit cards (Visa, MasterCard, American Express),
62
+ PayPal, and bank transfers. All payments are processed securely.
63
+ </AccordionContent>
64
+ </AccordionItem>
65
+ <AccordionItem value="faq-2">
66
+ <AccordionTrigger>How long does shipping take?</AccordionTrigger>
67
+ <AccordionContent>
68
+ Standard shipping takes 5-7 business days. Express shipping is available
69
+ for 2-3 business day delivery. International shipping times vary by location.
70
+ </AccordionContent>
71
+ </AccordionItem>
72
+ <AccordionItem value="faq-3">
73
+ <AccordionTrigger>What is your return policy?</AccordionTrigger>
74
+ <AccordionContent>
75
+ We offer a 30-day return policy for all unused items in original packaging.
76
+ Please contact our support team to initiate a return.
77
+ </AccordionContent>
78
+ </AccordionItem>
79
+ <AccordionItem value="faq-4">
80
+ <AccordionTrigger>Do you offer international shipping?</AccordionTrigger>
81
+ <AccordionContent>
82
+ Yes, we ship to over 100 countries worldwide. Shipping costs and delivery
83
+ times vary by destination. You can see exact costs at checkout.
84
+ </AccordionContent>
85
+ </AccordionItem>
86
+ </Accordion>
87
+ );
88
+
89
+ export const WithDefaultOpen = () => (
90
+ <Accordion type="single" defaultValue="item-2" className="w-full max-w-md">
91
+ <AccordionItem value="item-1">
92
+ <AccordionTrigger>First Item</AccordionTrigger>
93
+ <AccordionContent>
94
+ This item is closed by default.
95
+ </AccordionContent>
96
+ </AccordionItem>
97
+ <AccordionItem value="item-2">
98
+ <AccordionTrigger>Second Item (Default Open)</AccordionTrigger>
99
+ <AccordionContent>
100
+ This item is open by default.
101
+ </AccordionContent>
102
+ </AccordionItem>
103
+ <AccordionItem value="item-3">
104
+ <AccordionTrigger>Third Item</AccordionTrigger>
105
+ <AccordionContent>
106
+ This item is also closed by default.
107
+ </AccordionContent>
108
+ </AccordionItem>
109
+ </Accordion>
110
+ );
@@ -0,0 +1,104 @@
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 './alert-dialog';
13
+ import { Button } from './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
+ );
@@ -0,0 +1,77 @@
1
+ import { defineStory, useSelect } from '@djangocfg/playground';
2
+ import { Alert, AlertTitle, AlertDescription } from './alert';
3
+ import { AlertCircle, CheckCircle2, Info, AlertTriangle } from 'lucide-react';
4
+
5
+ export default defineStory({
6
+ title: 'Core/Alert',
7
+ component: Alert,
8
+ description: 'Alert messages for important information.',
9
+ });
10
+
11
+ export const Interactive = () => {
12
+ const [variant] = useSelect('variant', {
13
+ options: ['default', 'destructive'] as const,
14
+ defaultValue: 'default',
15
+ label: 'Variant',
16
+ description: 'Alert style variant',
17
+ });
18
+
19
+ return (
20
+ <Alert variant={variant} className="max-w-lg">
21
+ <AlertCircle className="h-4 w-4" />
22
+ <AlertTitle>Heads up!</AlertTitle>
23
+ <AlertDescription>
24
+ You can add components to your app using the CLI.
25
+ </AlertDescription>
26
+ </Alert>
27
+ );
28
+ };
29
+
30
+ export const Default = () => (
31
+ <Alert className="max-w-lg">
32
+ <Info className="h-4 w-4" />
33
+ <AlertTitle>Information</AlertTitle>
34
+ <AlertDescription>
35
+ This is an informational alert message.
36
+ </AlertDescription>
37
+ </Alert>
38
+ );
39
+
40
+ export const Destructive = () => (
41
+ <Alert variant="destructive" className="max-w-lg">
42
+ <AlertCircle className="h-4 w-4" />
43
+ <AlertTitle>Error</AlertTitle>
44
+ <AlertDescription>
45
+ Your session has expired. Please log in again.
46
+ </AlertDescription>
47
+ </Alert>
48
+ );
49
+
50
+ export const Success = () => (
51
+ <Alert className="max-w-lg border-green-500/50 text-green-600 [&>svg]:text-green-600">
52
+ <CheckCircle2 className="h-4 w-4" />
53
+ <AlertTitle>Success</AlertTitle>
54
+ <AlertDescription>
55
+ Your changes have been saved successfully.
56
+ </AlertDescription>
57
+ </Alert>
58
+ );
59
+
60
+ export const Warning = () => (
61
+ <Alert className="max-w-lg border-yellow-500/50 text-yellow-600 [&>svg]:text-yellow-600">
62
+ <AlertTriangle className="h-4 w-4" />
63
+ <AlertTitle>Warning</AlertTitle>
64
+ <AlertDescription>
65
+ Your subscription will expire in 3 days.
66
+ </AlertDescription>
67
+ </Alert>
68
+ );
69
+
70
+ export const WithoutIcon = () => (
71
+ <Alert className="max-w-lg">
72
+ <AlertTitle>Note</AlertTitle>
73
+ <AlertDescription>
74
+ You can also use alerts without icons.
75
+ </AlertDescription>
76
+ </Alert>
77
+ );
@@ -0,0 +1,94 @@
1
+ import { defineStory, useSelect } from '@djangocfg/playground';
2
+ import { AspectRatio } from './aspect-ratio';
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
+ );
@@ -0,0 +1,115 @@
1
+ import { defineStory, useSelect } from '@djangocfg/playground';
2
+ import { Avatar, AvatarImage, AvatarFallback } from './avatar';
3
+
4
+ export default defineStory({
5
+ title: 'Core/Avatar',
6
+ component: Avatar,
7
+ description: 'User avatar with image and fallback support.',
8
+ });
9
+
10
+ const AVATARS = [
11
+ { src: 'https://github.com/shadcn.png', fallback: 'CN' },
12
+ { src: 'https://github.com/vercel.png', fallback: 'VC' },
13
+ { src: 'https://github.com/radix-ui.png', fallback: 'RX' },
14
+ ];
15
+
16
+ export const Interactive = () => {
17
+ const [size] = useSelect('size', {
18
+ options: ['sm', 'md', 'lg'] as const,
19
+ defaultValue: 'md',
20
+ label: 'Size',
21
+ description: 'Avatar size',
22
+ });
23
+
24
+ const sizeClasses = {
25
+ sm: 'h-8 w-8',
26
+ md: 'h-10 w-10',
27
+ lg: 'h-14 w-14',
28
+ };
29
+
30
+ return (
31
+ <Avatar className={sizeClasses[size]}>
32
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
33
+ <AvatarFallback>CN</AvatarFallback>
34
+ </Avatar>
35
+ );
36
+ };
37
+
38
+ export const Default = () => (
39
+ <Avatar>
40
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
41
+ <AvatarFallback>CN</AvatarFallback>
42
+ </Avatar>
43
+ );
44
+
45
+ export const Fallback = () => (
46
+ <Avatar>
47
+ <AvatarImage src="/broken-image.jpg" alt="User" />
48
+ <AvatarFallback>JD</AvatarFallback>
49
+ </Avatar>
50
+ );
51
+
52
+ export const Sizes = () => (
53
+ <div className="flex items-center gap-4">
54
+ <Avatar className="h-6 w-6">
55
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
56
+ <AvatarFallback className="text-xs">CN</AvatarFallback>
57
+ </Avatar>
58
+ <Avatar className="h-8 w-8">
59
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
60
+ <AvatarFallback>CN</AvatarFallback>
61
+ </Avatar>
62
+ <Avatar className="h-10 w-10">
63
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
64
+ <AvatarFallback>CN</AvatarFallback>
65
+ </Avatar>
66
+ <Avatar className="h-14 w-14">
67
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
68
+ <AvatarFallback>CN</AvatarFallback>
69
+ </Avatar>
70
+ <Avatar className="h-20 w-20">
71
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
72
+ <AvatarFallback className="text-xl">CN</AvatarFallback>
73
+ </Avatar>
74
+ </div>
75
+ );
76
+
77
+ export const Group = () => (
78
+ <div className="flex -space-x-4">
79
+ {AVATARS.map((avatar, i) => (
80
+ <Avatar key={i} className="border-2 border-background">
81
+ <AvatarImage src={avatar.src} />
82
+ <AvatarFallback>{avatar.fallback}</AvatarFallback>
83
+ </Avatar>
84
+ ))}
85
+ <Avatar className="border-2 border-background">
86
+ <AvatarFallback>+5</AvatarFallback>
87
+ </Avatar>
88
+ </div>
89
+ );
90
+
91
+ export const WithStatus = () => (
92
+ <div className="flex gap-4">
93
+ <div className="relative">
94
+ <Avatar>
95
+ <AvatarImage src="https://github.com/shadcn.png" />
96
+ <AvatarFallback>CN</AvatarFallback>
97
+ </Avatar>
98
+ <span className="absolute bottom-0 right-0 h-3 w-3 rounded-full bg-green-500 border-2 border-background" />
99
+ </div>
100
+ <div className="relative">
101
+ <Avatar>
102
+ <AvatarImage src="https://github.com/vercel.png" />
103
+ <AvatarFallback>VC</AvatarFallback>
104
+ </Avatar>
105
+ <span className="absolute bottom-0 right-0 h-3 w-3 rounded-full bg-yellow-500 border-2 border-background" />
106
+ </div>
107
+ <div className="relative">
108
+ <Avatar>
109
+ <AvatarImage src="https://github.com/radix-ui.png" />
110
+ <AvatarFallback>RX</AvatarFallback>
111
+ </Avatar>
112
+ <span className="absolute bottom-0 right-0 h-3 w-3 rounded-full bg-gray-400 border-2 border-background" />
113
+ </div>
114
+ </div>
115
+ );
@@ -0,0 +1,56 @@
1
+ import { defineStory, useSelect } from '@djangocfg/playground';
2
+ import { Badge } from './badge';
3
+
4
+ export default defineStory({
5
+ title: 'Core/Badge',
6
+ component: Badge,
7
+ description: 'Small status indicators and labels.',
8
+ });
9
+
10
+ export const Interactive = () => {
11
+ const [variant] = useSelect('variant', {
12
+ options: ['default', 'secondary', 'destructive', 'outline'] as const,
13
+ defaultValue: 'default',
14
+ label: 'Variant',
15
+ description: 'Badge style variant',
16
+ });
17
+
18
+ return (
19
+ <div className="flex gap-4">
20
+ <Badge variant={variant}>Badge</Badge>
21
+ </div>
22
+ );
23
+ };
24
+
25
+ export const Variants = () => (
26
+ <div className="flex flex-wrap gap-4">
27
+ <Badge variant="default">Default</Badge>
28
+ <Badge variant="secondary">Secondary</Badge>
29
+ <Badge variant="destructive">Destructive</Badge>
30
+ <Badge variant="outline">Outline</Badge>
31
+ </div>
32
+ );
33
+
34
+ export const StatusBadges = () => (
35
+ <div className="flex flex-wrap gap-4">
36
+ <Badge variant="default">Active</Badge>
37
+ <Badge variant="secondary">Pending</Badge>
38
+ <Badge variant="destructive">Expired</Badge>
39
+ <Badge variant="outline">Draft</Badge>
40
+ </div>
41
+ );
42
+
43
+ export const InContext = () => (
44
+ <div className="space-y-4">
45
+ <div className="flex items-center gap-2">
46
+ <span className="font-medium">Status:</span>
47
+ <Badge variant="default">Published</Badge>
48
+ </div>
49
+ <div className="flex items-center gap-2">
50
+ <span className="font-medium">Tags:</span>
51
+ <Badge variant="secondary">React</Badge>
52
+ <Badge variant="secondary">TypeScript</Badge>
53
+ <Badge variant="secondary">UI</Badge>
54
+ </div>
55
+ </div>
56
+ );