@ponti-studios/ui 0.1.2

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 (108) hide show
  1. package/README.md +27 -0
  2. package/dist/index.js +2118 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +130 -0
  5. package/src/animations.css +437 -0
  6. package/src/components/data-display/accordion.stories.tsx +57 -0
  7. package/src/components/data-display/accordion.tsx +65 -0
  8. package/src/components/data-display/filter-chip.tsx +41 -0
  9. package/src/components/data-display/index.ts +13 -0
  10. package/src/components/data-display/table.stories.tsx +75 -0
  11. package/src/components/data-display/table.tsx +84 -0
  12. package/src/components/feedback/alert.stories.tsx +51 -0
  13. package/src/components/feedback/alert.tsx +60 -0
  14. package/src/components/feedback/empty-state.tsx +66 -0
  15. package/src/components/feedback/index.ts +6 -0
  16. package/src/components/feedback/progress.stories.tsx +37 -0
  17. package/src/components/feedback/progress.tsx +30 -0
  18. package/src/components/feedback/spinner.stories.tsx +52 -0
  19. package/src/components/feedback/spinner.tsx +48 -0
  20. package/src/components/forms/calendar.stories.tsx +30 -0
  21. package/src/components/forms/calendar.tsx +67 -0
  22. package/src/components/forms/date-picker.stories.tsx +48 -0
  23. package/src/components/forms/date-picker.tsx +60 -0
  24. package/src/components/forms/field.stories.tsx +62 -0
  25. package/src/components/forms/field.tsx +65 -0
  26. package/src/components/forms/field.types.ts +7 -0
  27. package/src/components/forms/index.ts +24 -0
  28. package/src/components/forms/input.stories.tsx +47 -0
  29. package/src/components/forms/input.tsx +20 -0
  30. package/src/components/forms/radio-group.stories.tsx +48 -0
  31. package/src/components/forms/radio-group.tsx +38 -0
  32. package/src/components/forms/select.stories.tsx +88 -0
  33. package/src/components/forms/select.tsx +147 -0
  34. package/src/components/forms/slider.stories.tsx +67 -0
  35. package/src/components/forms/slider.tsx +25 -0
  36. package/src/components/forms/stepper.stories.tsx +111 -0
  37. package/src/components/forms/stepper.tsx +66 -0
  38. package/src/components/forms/switch.stories.tsx +144 -0
  39. package/src/components/forms/switch.tsx +36 -0
  40. package/src/components/forms/text-field.stories.tsx +87 -0
  41. package/src/components/forms/text-field.tsx +40 -0
  42. package/src/components/forms/text-field.types.ts +9 -0
  43. package/src/components/forms/textarea.stories.tsx +150 -0
  44. package/src/components/forms/textarea.tsx +18 -0
  45. package/src/components/layout/index.ts +2 -0
  46. package/src/components/layout/scroll-area.stories.tsx +83 -0
  47. package/src/components/layout/scroll-area.tsx +40 -0
  48. package/src/components/navigation/app-navigation.stories.tsx +81 -0
  49. package/src/components/navigation/app-navigation.tsx +155 -0
  50. package/src/components/navigation/index.ts +3 -0
  51. package/src/components/navigation/tabs.stories.tsx +69 -0
  52. package/src/components/navigation/tabs.tsx +49 -0
  53. package/src/components/overlays/alert-dialog.stories.tsx +45 -0
  54. package/src/components/overlays/alert-dialog.tsx +119 -0
  55. package/src/components/overlays/command.stories.tsx +65 -0
  56. package/src/components/overlays/command.tsx +130 -0
  57. package/src/components/overlays/dialog.stories.tsx +111 -0
  58. package/src/components/overlays/dialog.tsx +162 -0
  59. package/src/components/overlays/dropdown-menu.stories.tsx +79 -0
  60. package/src/components/overlays/dropdown-menu.tsx +214 -0
  61. package/src/components/overlays/index.ts +65 -0
  62. package/src/components/overlays/popover.stories.tsx +39 -0
  63. package/src/components/overlays/popover.tsx +40 -0
  64. package/src/components/overlays/sheet.stories.tsx +68 -0
  65. package/src/components/overlays/sheet.tsx +98 -0
  66. package/src/components/primitives/avatar.stories.tsx +71 -0
  67. package/src/components/primitives/avatar.tsx +132 -0
  68. package/src/components/primitives/badge.stories.tsx +33 -0
  69. package/src/components/primitives/badge.tsx +44 -0
  70. package/src/components/primitives/button.stories.tsx +113 -0
  71. package/src/components/primitives/button.tsx +95 -0
  72. package/src/components/primitives/button.types.ts +10 -0
  73. package/src/components/primitives/card.stories.tsx +59 -0
  74. package/src/components/primitives/card.tsx +69 -0
  75. package/src/components/primitives/index.ts +13 -0
  76. package/src/components/primitives/label.stories.tsx +47 -0
  77. package/src/components/primitives/label.tsx +21 -0
  78. package/src/constants/chart-colors.ts +39 -0
  79. package/src/env.d.ts +9 -0
  80. package/src/hooks/index.ts +6 -0
  81. package/src/hooks/sort.types.ts +12 -0
  82. package/src/hooks/use-api-client.ts +122 -0
  83. package/src/hooks/use-debounced-value.ts +12 -0
  84. package/src/hooks/use-filter-state.ts +68 -0
  85. package/src/hooks/use-media-query.ts +65 -0
  86. package/src/hooks/use-mobile.tsx +19 -0
  87. package/src/index.ts +25 -0
  88. package/src/lib/auth.ts +5 -0
  89. package/src/lib/clipboard.ts +8 -0
  90. package/src/lib/create-memory-storage.ts +24 -0
  91. package/src/lib/index.ts +4 -0
  92. package/src/lib/utils.ts +6 -0
  93. package/src/storybook/controls.ts +117 -0
  94. package/src/storybook/options.ts +52 -0
  95. package/src/styles.css +1052 -0
  96. package/src/tokens/color-systems.stories.tsx +133 -0
  97. package/src/tokens/colors.ts +156 -0
  98. package/src/tokens/foundations.stories.tsx +151 -0
  99. package/src/tokens/index.ts +16 -0
  100. package/src/tokens/layout.stories.tsx +118 -0
  101. package/src/tokens/motion.ts +51 -0
  102. package/src/tokens/radii.ts +10 -0
  103. package/src/tokens/shadows.ts +34 -0
  104. package/src/tokens/spacing.ts +30 -0
  105. package/src/tokens/typography.ts +42 -0
  106. package/src/translations/en.ts +34 -0
  107. package/src/translations/index.ts +32 -0
  108. package/src/types/upload.ts +42 -0
@@ -0,0 +1,214 @@
1
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ function DropdownMenu(props: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
8
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
9
+ }
10
+
11
+ function DropdownMenuPortal(props: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
12
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
13
+ }
14
+
15
+ function DropdownMenuTrigger(props: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
16
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
17
+ }
18
+
19
+ function DropdownMenuContent({
20
+ className,
21
+ sideOffset = 4,
22
+ ...props
23
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
24
+ return (
25
+ <DropdownMenuPrimitive.Portal>
26
+ <DropdownMenuPrimitive.Content
27
+ data-slot="dropdown-menu-content"
28
+ sideOffset={sideOffset}
29
+ className={cn(
30
+ "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1",
31
+ className,
32
+ )}
33
+ {...props}
34
+ />
35
+ </DropdownMenuPrimitive.Portal>
36
+ );
37
+ }
38
+
39
+ function DropdownMenuGroup(props: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
40
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
41
+ }
42
+
43
+ function DropdownMenuLabel({
44
+ className,
45
+ inset,
46
+ ...props
47
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }) {
48
+ return (
49
+ <DropdownMenuPrimitive.Label
50
+ data-slot="dropdown-menu-label"
51
+ data-inset={inset}
52
+ className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+
58
+ function DropdownMenuItem({
59
+ className,
60
+ inset,
61
+ variant = "default",
62
+ ...props
63
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
64
+ inset?: boolean;
65
+ variant?: "default" | "destructive";
66
+ }) {
67
+ return (
68
+ <DropdownMenuPrimitive.Item
69
+ data-slot="dropdown-menu-item"
70
+ data-inset={inset}
71
+ data-variant={variant}
72
+ className={cn(
73
+ "focus:bg-accent focus:text-on-accent data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
74
+ className,
75
+ )}
76
+ {...props}
77
+ />
78
+ );
79
+ }
80
+
81
+ function DropdownMenuCheckboxItem({
82
+ className,
83
+ children,
84
+ checked,
85
+ ...props
86
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
87
+ return (
88
+ <DropdownMenuPrimitive.CheckboxItem
89
+ data-slot="dropdown-menu-checkbox-item"
90
+ className={cn(
91
+ "focus:bg-accent focus:text-on-accent relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
92
+ className,
93
+ )}
94
+ checked={checked}
95
+ {...props}
96
+ >
97
+ <span className="absolute left-2 flex size-3.5 items-center justify-center">
98
+ <DropdownMenuPrimitive.ItemIndicator>
99
+ <CheckIcon className="size-4" />
100
+ </DropdownMenuPrimitive.ItemIndicator>
101
+ </span>
102
+ {children}
103
+ </DropdownMenuPrimitive.CheckboxItem>
104
+ );
105
+ }
106
+
107
+ function DropdownMenuRadioGroup(
108
+ props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>,
109
+ ) {
110
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
111
+ }
112
+
113
+ function DropdownMenuRadioItem({
114
+ className,
115
+ children,
116
+ ...props
117
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
118
+ return (
119
+ <DropdownMenuPrimitive.RadioItem
120
+ data-slot="dropdown-menu-radio-item"
121
+ className={cn(
122
+ "focus:bg-accent focus:text-on-accent relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
123
+ className,
124
+ )}
125
+ {...props}
126
+ >
127
+ <span className="absolute left-2 flex size-3.5 items-center justify-center">
128
+ <DropdownMenuPrimitive.ItemIndicator>
129
+ <CircleIcon className="size-2 fill-current" />
130
+ </DropdownMenuPrimitive.ItemIndicator>
131
+ </span>
132
+ {children}
133
+ </DropdownMenuPrimitive.RadioItem>
134
+ );
135
+ }
136
+
137
+ function DropdownMenuSeparator({
138
+ className,
139
+ ...props
140
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
141
+ return (
142
+ <DropdownMenuPrimitive.Separator
143
+ data-slot="dropdown-menu-separator"
144
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
145
+ {...props}
146
+ />
147
+ );
148
+ }
149
+
150
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
151
+ return (
152
+ <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
153
+ );
154
+ }
155
+
156
+ function DropdownMenuSub(props: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
157
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
158
+ }
159
+
160
+ function DropdownMenuSubTrigger({
161
+ className,
162
+ inset,
163
+ children,
164
+ ...props
165
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }) {
166
+ return (
167
+ <DropdownMenuPrimitive.SubTrigger
168
+ data-slot="dropdown-menu-sub-trigger"
169
+ data-inset={inset}
170
+ className={cn(
171
+ "focus:bg-accent focus:text-on-accent data-[state=open]:bg-accent data-[state=open]:text-on-accent flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
172
+ className,
173
+ )}
174
+ {...props}
175
+ >
176
+ {children}
177
+ <ChevronRightIcon className="ml-auto size-4" />
178
+ </DropdownMenuPrimitive.SubTrigger>
179
+ );
180
+ }
181
+
182
+ function DropdownMenuSubContent({
183
+ className,
184
+ ...props
185
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
186
+ return (
187
+ <DropdownMenuPrimitive.SubContent
188
+ data-slot="dropdown-menu-sub-content"
189
+ className={cn(
190
+ "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg",
191
+ className,
192
+ )}
193
+ {...props}
194
+ />
195
+ );
196
+ }
197
+
198
+ export {
199
+ DropdownMenu,
200
+ DropdownMenuCheckboxItem,
201
+ DropdownMenuContent,
202
+ DropdownMenuGroup,
203
+ DropdownMenuItem,
204
+ DropdownMenuLabel,
205
+ DropdownMenuPortal,
206
+ DropdownMenuRadioGroup,
207
+ DropdownMenuRadioItem,
208
+ DropdownMenuSeparator,
209
+ DropdownMenuShortcut,
210
+ DropdownMenuSub,
211
+ DropdownMenuSubContent,
212
+ DropdownMenuSubTrigger,
213
+ DropdownMenuTrigger,
214
+ };
@@ -0,0 +1,65 @@
1
+ export {
2
+ AlertDialog,
3
+ AlertDialogAction,
4
+ AlertDialogCancel,
5
+ AlertDialogContent,
6
+ AlertDialogDescription,
7
+ AlertDialogFooter,
8
+ AlertDialogHeader,
9
+ AlertDialogTitle,
10
+ AlertDialogTrigger,
11
+ } from "./alert-dialog";
12
+ export {
13
+ Command,
14
+ CommandDialog,
15
+ CommandEmpty,
16
+ CommandGroup,
17
+ CommandInput,
18
+ CommandItem,
19
+ CommandList,
20
+ CommandListLoading,
21
+ CommandSeparator,
22
+ CommandShortcut,
23
+ } from "./command";
24
+ export {
25
+ Dialog,
26
+ DialogClose,
27
+ DialogContent,
28
+ DialogDescription,
29
+ DialogFooter,
30
+ DialogHeader,
31
+ DialogOverlay,
32
+ DialogPortal,
33
+ DialogTitle,
34
+ DialogTrigger,
35
+ } from "./dialog";
36
+ export {
37
+ DropdownMenu,
38
+ DropdownMenuCheckboxItem,
39
+ DropdownMenuContent,
40
+ DropdownMenuGroup,
41
+ DropdownMenuItem,
42
+ DropdownMenuLabel,
43
+ DropdownMenuPortal,
44
+ DropdownMenuRadioGroup,
45
+ DropdownMenuRadioItem,
46
+ DropdownMenuSeparator,
47
+ DropdownMenuShortcut,
48
+ DropdownMenuSub,
49
+ DropdownMenuSubContent,
50
+ DropdownMenuSubTrigger,
51
+ DropdownMenuTrigger,
52
+ } from "./dropdown-menu";
53
+ export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./popover";
54
+ export {
55
+ Sheet,
56
+ SheetClose,
57
+ SheetContent,
58
+ SheetDescription,
59
+ SheetFooter,
60
+ SheetHeader,
61
+ SheetOverlay,
62
+ SheetPortal,
63
+ SheetTitle,
64
+ SheetTrigger,
65
+ } from "./sheet";
@@ -0,0 +1,39 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { Button } from "../primitives/button";
4
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover";
5
+
6
+ const meta = {
7
+ title: "Overlays/Popover",
8
+ component: Popover,
9
+ tags: ["autodocs"],
10
+ } satisfies Meta<typeof Popover>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Default: Story = {
16
+ render: () => (
17
+ <Popover>
18
+ <PopoverTrigger asChild>
19
+ <Button variant="outline">Open Popover</Button>
20
+ </PopoverTrigger>
21
+ <PopoverContent>
22
+ <div className="grid gap-1.5">
23
+ <h4 className="text-primary leading-none font-medium">Dimensions</h4>
24
+ <p className="text-secondary text-sm">Set the dimensions for the layer.</p>
25
+ </div>
26
+ <div className="grid gap-4 pt-4">
27
+ <div className="grid grid-cols-3 items-center gap-4">
28
+ <label className="text-sm">Width</label>
29
+ <input className="col-span-2 h-8 rounded border px-2 text-sm" defaultValue="100%" />
30
+ </div>
31
+ <div className="grid grid-cols-3 items-center gap-4">
32
+ <label className="text-sm">Height</label>
33
+ <input className="col-span-2 h-8 rounded border px-2 text-sm" defaultValue="25px" />
34
+ </div>
35
+ </div>
36
+ </PopoverContent>
37
+ </Popover>
38
+ ),
39
+ };
@@ -0,0 +1,40 @@
1
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
2
+ import * as React from "react";
3
+
4
+ import { cn } from "../../lib/utils";
5
+
6
+ function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
7
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />;
8
+ }
9
+
10
+ function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
11
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
12
+ }
13
+
14
+ function PopoverContent({
15
+ className,
16
+ align = "center",
17
+ sideOffset = 4,
18
+ ...props
19
+ }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
20
+ return (
21
+ <PopoverPrimitive.Portal>
22
+ <PopoverPrimitive.Content
23
+ data-slot="popover-content"
24
+ align={align}
25
+ sideOffset={sideOffset}
26
+ className={cn(
27
+ "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 outline-hidden",
28
+ className,
29
+ )}
30
+ {...props}
31
+ />
32
+ </PopoverPrimitive.Portal>
33
+ );
34
+ }
35
+
36
+ function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
37
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
38
+ }
39
+
40
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import * as React from "react";
3
+
4
+ import { Button } from "../primitives/button";
5
+ import {
6
+ Sheet,
7
+ SheetClose,
8
+ SheetContent,
9
+ SheetDescription,
10
+ SheetFooter,
11
+ SheetHeader,
12
+ SheetTitle,
13
+ SheetTrigger,
14
+ } from "./sheet";
15
+
16
+ const meta: Meta<typeof Sheet> = {
17
+ title: "Overlays/Sheet",
18
+ component: Sheet,
19
+ tags: ["autodocs"],
20
+ };
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof meta>;
24
+
25
+ const SheetDemo = () => (
26
+ <Sheet>
27
+ <SheetTrigger asChild>
28
+ <Button variant="outline">Open sheet</Button>
29
+ </SheetTrigger>
30
+ <SheetContent>
31
+ <SheetHeader>
32
+ <SheetTitle>Edit profile</SheetTitle>
33
+ <SheetDescription>
34
+ Make changes to your profile here. Click save when you're done.
35
+ </SheetDescription>
36
+ </SheetHeader>
37
+ <div className="flex flex-col gap-4 py-6">
38
+ <div className="flex flex-col gap-2">
39
+ <span className="text-sm font-medium">Name</span>
40
+ <input
41
+ className="border-default placeholder:text-secondary focus-visible:ring-focus flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
42
+ placeholder="Jane Doe"
43
+ />
44
+ </div>
45
+ <div className="flex flex-col gap-2">
46
+ <span className="text-sm font-medium">Email</span>
47
+ <input
48
+ className="border-default placeholder:text-secondary focus-visible:ring-focus flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
49
+ placeholder="jane@example.com"
50
+ type="email"
51
+ />
52
+ </div>
53
+ </div>
54
+ <SheetFooter>
55
+ <SheetClose asChild>
56
+ <Button variant="outline">Cancel</Button>
57
+ </SheetClose>
58
+ <SheetClose asChild>
59
+ <Button>Save changes</Button>
60
+ </SheetClose>
61
+ </SheetFooter>
62
+ </SheetContent>
63
+ </Sheet>
64
+ );
65
+
66
+ export const Default: Story = {
67
+ render: () => <SheetDemo />,
68
+ };
@@ -0,0 +1,98 @@
1
+ "use client";
2
+
3
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
4
+ import { X } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "../../lib/utils";
8
+
9
+ const Sheet = SheetPrimitive.Root;
10
+
11
+ const SheetTrigger = SheetPrimitive.Trigger;
12
+
13
+ const SheetClose = SheetPrimitive.Close;
14
+
15
+ const SheetPortal = SheetPrimitive.Portal;
16
+
17
+ const SheetOverlay = React.forwardRef<
18
+ React.ComponentRef<typeof SheetPrimitive.Overlay>,
19
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
20
+ >(({ className, ...props }, ref) => (
21
+ <SheetPrimitive.Overlay
22
+ className={cn(
23
+ "overlay-backdrop data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 transition-opacity",
24
+ className,
25
+ )}
26
+ {...props}
27
+ ref={ref}
28
+ />
29
+ ));
30
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
31
+
32
+ const sheetClassName =
33
+ "sheet-responsive sheet-edge-responsive bg-canvas text-primary border-subtle fixed inset-x-0 bottom-0 z-50 flex max-h-[90dvh] min-h-[30dvh] flex-col gap-6 rounded-t-xl border border-r-0 border-b-0 border-l-0 p-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:inset-y-0 md:right-0 md:bottom-auto md:left-auto md:h-full md:w-3/4 md:max-w-sm md:min-h-0 md:rounded-t-none md:rounded-l-xl md:border-t-0 md:border-r-0 md:border-b-0 md:border-l md:p-6 md:pb-6";
34
+
35
+ interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> {}
36
+
37
+ const SheetContent = React.forwardRef<
38
+ React.ComponentRef<typeof SheetPrimitive.Content>,
39
+ SheetContentProps
40
+ >(({ className, children, ...props }, ref) => (
41
+ <SheetPortal>
42
+ <SheetOverlay />
43
+ <SheetPrimitive.Content ref={ref} className={cn(sheetClassName, className)} {...props}>
44
+ <SheetPrimitive.Close className="sheet-glass-control text-secondary hover:bg-inset/70 hover:text-primary focus-visible:outline-ring absolute top-3 right-3 inline-flex size-11 items-center justify-center rounded-md border border-transparent transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none md:top-4 md:right-4">
45
+ <X className="size-4.5" />
46
+ <span className="sr-only">Close</span>
47
+ </SheetPrimitive.Close>
48
+ {children}
49
+ </SheetPrimitive.Content>
50
+ </SheetPortal>
51
+ ));
52
+ SheetContent.displayName = SheetPrimitive.Content.displayName;
53
+
54
+ const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
55
+ <div className={cn("flex flex-col gap-2 text-left", className)} {...props} />
56
+ );
57
+ SheetHeader.displayName = "SheetHeader";
58
+
59
+ const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
60
+ <div
61
+ className={cn("mt-auto flex flex-col-reverse gap-3 sm:flex-row sm:justify-end", className)}
62
+ {...props}
63
+ />
64
+ );
65
+ SheetFooter.displayName = "SheetFooter";
66
+
67
+ const SheetTitle = React.forwardRef<
68
+ React.ComponentRef<typeof SheetPrimitive.Title>,
69
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
70
+ >(({ className, ...props }, ref) => (
71
+ <SheetPrimitive.Title ref={ref} className={cn("heading-2 text-primary", className)} {...props} />
72
+ ));
73
+ SheetTitle.displayName = SheetPrimitive.Title.displayName;
74
+
75
+ const SheetDescription = React.forwardRef<
76
+ React.ComponentRef<typeof SheetPrimitive.Description>,
77
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
78
+ >(({ className, ...props }, ref) => (
79
+ <SheetPrimitive.Description
80
+ ref={ref}
81
+ className={cn("body-2 text-secondary max-w-prose", className)}
82
+ {...props}
83
+ />
84
+ ));
85
+ SheetDescription.displayName = SheetPrimitive.Description.displayName;
86
+
87
+ export {
88
+ Sheet,
89
+ SheetClose,
90
+ SheetContent,
91
+ SheetDescription,
92
+ SheetFooter,
93
+ SheetHeader,
94
+ SheetOverlay,
95
+ SheetPortal,
96
+ SheetTitle,
97
+ SheetTrigger,
98
+ };
@@ -0,0 +1,71 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { Avatar, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from "./avatar";
4
+
5
+ const meta = {
6
+ title: "Primitives/Avatar",
7
+ component: Avatar,
8
+ tags: ["autodocs"],
9
+ } satisfies Meta<typeof Avatar>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const WithImage: Story = {
15
+ render: () => (
16
+ <Avatar>
17
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
18
+ <AvatarFallback>CN</AvatarFallback>
19
+ </Avatar>
20
+ ),
21
+ };
22
+
23
+ export const WithFallback: Story = {
24
+ render: () => (
25
+ <Avatar>
26
+ <AvatarImage src="/broken-image.jpg" alt="Broken" />
27
+ <AvatarFallback>AB</AvatarFallback>
28
+ </Avatar>
29
+ ),
30
+ };
31
+
32
+ export const Sizes: Story = {
33
+ render: () => (
34
+ <div className="flex items-center gap-4">
35
+ <Avatar size="sm">
36
+ <AvatarFallback>SM</AvatarFallback>
37
+ </Avatar>
38
+ <Avatar size="default">
39
+ <AvatarFallback>MD</AvatarFallback>
40
+ </Avatar>
41
+ <Avatar size="lg">
42
+ <AvatarFallback>LG</AvatarFallback>
43
+ </Avatar>
44
+ </div>
45
+ ),
46
+ };
47
+
48
+ export const WithBadge: Story = {
49
+ render: () => (
50
+ <Avatar statusBadge>
51
+ <AvatarFallback>JD</AvatarFallback>
52
+ </Avatar>
53
+ ),
54
+ };
55
+
56
+ export const Group: Story = {
57
+ render: () => (
58
+ <AvatarGroup>
59
+ <Avatar>
60
+ <AvatarFallback>AB</AvatarFallback>
61
+ </Avatar>
62
+ <Avatar>
63
+ <AvatarFallback>CD</AvatarFallback>
64
+ </Avatar>
65
+ <Avatar>
66
+ <AvatarFallback>EF</AvatarFallback>
67
+ </Avatar>
68
+ <AvatarGroupCount>+3</AvatarGroupCount>
69
+ </AvatarGroup>
70
+ ),
71
+ };