@leitware/dockets 0.1.0

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 (135) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +18 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/add.d.ts +3 -0
  6. package/dist/commands/add.d.ts.map +1 -0
  7. package/dist/commands/add.js +86 -0
  8. package/dist/commands/add.js.map +1 -0
  9. package/dist/commands/list.d.ts +3 -0
  10. package/dist/commands/list.d.ts.map +1 -0
  11. package/dist/commands/list.js +36 -0
  12. package/dist/commands/list.js.map +1 -0
  13. package/dist/registry.d.ts +18 -0
  14. package/dist/registry.d.ts.map +1 -0
  15. package/dist/registry.js +712 -0
  16. package/dist/registry.js.map +1 -0
  17. package/package.json +40 -0
  18. package/templates/accordion.tsx +77 -0
  19. package/templates/alert-dialog.tsx +66 -0
  20. package/templates/alert.tsx +41 -0
  21. package/templates/aspect-ratio.tsx +15 -0
  22. package/templates/avatar.tsx +27 -0
  23. package/templates/badge.tsx +1 -0
  24. package/templates/block-loader.tsx +1 -0
  25. package/templates/breadcrumb.tsx +31 -0
  26. package/templates/button.tsx +1 -0
  27. package/templates/calendar.tsx +45 -0
  28. package/templates/card.tsx +35 -0
  29. package/templates/carousel.tsx +39 -0
  30. package/templates/checkbox.tsx +50 -0
  31. package/templates/code-block.tsx +1 -0
  32. package/templates/collapsible.tsx +35 -0
  33. package/templates/combobox.tsx +154 -0
  34. package/templates/command.tsx +50 -0
  35. package/templates/contact-footer.tsx +193 -0
  36. package/templates/context-menu.tsx +16 -0
  37. package/templates/dialog.tsx +67 -0
  38. package/templates/drawer.tsx +12 -0
  39. package/templates/dropdown-menu.tsx +95 -0
  40. package/templates/form-input.tsx +64 -0
  41. package/templates/form.tsx +10 -0
  42. package/templates/hover-card.tsx +5 -0
  43. package/templates/input-otp.tsx +6 -0
  44. package/templates/label.tsx +1 -0
  45. package/templates/layout-primitives.tsx +11 -0
  46. package/templates/layouts.tsx +346 -0
  47. package/templates/lib/utils.ts +49 -0
  48. package/templates/list-item.tsx +1 -0
  49. package/templates/list-items.tsx +41 -0
  50. package/templates/list.tsx +89 -0
  51. package/templates/logo.tsx +12 -0
  52. package/templates/marketing-footer.tsx +33 -0
  53. package/templates/marketing-header.tsx +46 -0
  54. package/templates/menubar.tsx +16 -0
  55. package/templates/navigation-menu.tsx +11 -0
  56. package/templates/pagination.tsx +86 -0
  57. package/templates/popover.tsx +8 -0
  58. package/templates/pricing-receipt.tsx +71 -0
  59. package/templates/pricing-tabs.tsx +60 -0
  60. package/templates/progress.tsx +29 -0
  61. package/templates/radio-group.tsx +58 -0
  62. package/templates/receipt-card.tsx +1 -0
  63. package/templates/receipt.tsx +269 -0
  64. package/templates/resizable.tsx +1 -0
  65. package/templates/scroll-area.tsx +1 -0
  66. package/templates/select.tsx +110 -0
  67. package/templates/separator.tsx +1 -0
  68. package/templates/sheet.tsx +12 -0
  69. package/templates/sidebar.tsx +15 -0
  70. package/templates/simple-footer.tsx +43 -0
  71. package/templates/simple-header.tsx +77 -0
  72. package/templates/skeleton.tsx +33 -0
  73. package/templates/slider.tsx +55 -0
  74. package/templates/styles/dockets.css +104 -0
  75. package/templates/switch.tsx +49 -0
  76. package/templates/table.tsx +73 -0
  77. package/templates/tabs.tsx +61 -0
  78. package/templates/theme-toggle.tsx +46 -0
  79. package/templates/toast.tsx +1 -0
  80. package/templates/toggle-group.tsx +1 -0
  81. package/templates/toggle.tsx +1 -0
  82. package/templates/tooltip.tsx +31 -0
  83. package/templates/tree-view.tsx +1 -0
  84. package/templates/ui/accordion.tsx +73 -0
  85. package/templates/ui/alert-dialog.tsx +128 -0
  86. package/templates/ui/alert.tsx +56 -0
  87. package/templates/ui/aspect-ratio.tsx +19 -0
  88. package/templates/ui/avatar.tsx +74 -0
  89. package/templates/ui/badge.tsx +48 -0
  90. package/templates/ui/block-loader.tsx +40 -0
  91. package/templates/ui/button.tsx +77 -0
  92. package/templates/ui/calendar.tsx +160 -0
  93. package/templates/ui/card.tsx +73 -0
  94. package/templates/ui/carousel.tsx +149 -0
  95. package/templates/ui/checkbox.tsx +33 -0
  96. package/templates/ui/code-block.tsx +36 -0
  97. package/templates/ui/collapsible.tsx +48 -0
  98. package/templates/ui/combobox.tsx +295 -0
  99. package/templates/ui/command.tsx +148 -0
  100. package/templates/ui/context-menu.tsx +212 -0
  101. package/templates/ui/dialog.tsx +138 -0
  102. package/templates/ui/drawer.tsx +134 -0
  103. package/templates/ui/dropdown-menu.tsx +254 -0
  104. package/templates/ui/form.tsx +122 -0
  105. package/templates/ui/hover-card.tsx +44 -0
  106. package/templates/ui/input-group.tsx +148 -0
  107. package/templates/ui/input-otp.tsx +153 -0
  108. package/templates/ui/input.tsx +20 -0
  109. package/templates/ui/label.tsx +17 -0
  110. package/templates/ui/layout.tsx +252 -0
  111. package/templates/ui/list-item.tsx +50 -0
  112. package/templates/ui/menubar.tsx +225 -0
  113. package/templates/ui/navigation-menu.tsx +117 -0
  114. package/templates/ui/pagination.tsx +110 -0
  115. package/templates/ui/popover.tsx +77 -0
  116. package/templates/ui/progress.tsx +37 -0
  117. package/templates/ui/radio-group.tsx +41 -0
  118. package/templates/ui/receipt-card.tsx +70 -0
  119. package/templates/ui/resizable.tsx +140 -0
  120. package/templates/ui/scroll-area.tsx +64 -0
  121. package/templates/ui/select.tsx +186 -0
  122. package/templates/ui/separator.tsx +21 -0
  123. package/templates/ui/sheet.tsx +134 -0
  124. package/templates/ui/sidebar.tsx +222 -0
  125. package/templates/ui/skeleton.tsx +35 -0
  126. package/templates/ui/slider.tsx +60 -0
  127. package/templates/ui/switch.tsx +33 -0
  128. package/templates/ui/table.tsx +114 -0
  129. package/templates/ui/tabs.tsx +79 -0
  130. package/templates/ui/textarea.tsx +18 -0
  131. package/templates/ui/toast.tsx +139 -0
  132. package/templates/ui/toggle-group.tsx +68 -0
  133. package/templates/ui/toggle.tsx +47 -0
  134. package/templates/ui/tooltip.tsx +53 -0
  135. package/templates/ui/tree-view.tsx +76 -0
@@ -0,0 +1,148 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { SearchIcon } from 'lucide-react'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ interface CommandContextValue {
8
+ query: string
9
+ setQuery: (q: string) => void
10
+ }
11
+
12
+ const CommandContext = React.createContext<CommandContextValue>({
13
+ query: '',
14
+ setQuery: () => {},
15
+ })
16
+
17
+ function Command({ className, children, ...props }: React.ComponentProps<'div'>) {
18
+ const [query, setQuery] = React.useState('')
19
+ return (
20
+ <CommandContext.Provider value={{ query, setQuery }}>
21
+ <div
22
+ data-slot="command"
23
+ className={cn(
24
+ 'flex flex-col rounded-[var(--radius)] border-[length:var(--border-width)] border-foreground bg-popover text-popover-foreground',
25
+ className,
26
+ )}
27
+ {...props}
28
+ >
29
+ {children}
30
+ </div>
31
+ </CommandContext.Provider>
32
+ )
33
+ }
34
+
35
+ function CommandInput({ className, ...props }: React.ComponentProps<'input'>) {
36
+ const { setQuery } = React.useContext(CommandContext)
37
+ return (
38
+ <div
39
+ data-slot="command-input-wrapper"
40
+ className="flex items-center border-b-[length:var(--border-width)] border-foreground px-2"
41
+ >
42
+ <SearchIcon className="size-3.5 shrink-0 text-muted-foreground" />
43
+ <input
44
+ data-slot="command-input"
45
+ type="text"
46
+ role="combobox"
47
+ autoComplete="off"
48
+ autoCorrect="off"
49
+ spellCheck={false}
50
+ onChange={(e) => setQuery(e.target.value)}
51
+ className={cn(
52
+ 'h-9 w-full bg-transparent px-2 text-xs placeholder:text-muted-foreground outline-none disabled:cursor-not-allowed disabled:opacity-50',
53
+ className,
54
+ )}
55
+ {...props}
56
+ />
57
+ </div>
58
+ )
59
+ }
60
+
61
+ function CommandList({ className, ...props }: React.ComponentProps<'div'>) {
62
+ return (
63
+ <div
64
+ data-slot="command-list"
65
+ role="listbox"
66
+ className={cn('max-h-64 overflow-y-auto overflow-x-hidden', className)}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function CommandEmpty({ className, ...props }: React.ComponentProps<'div'>) {
73
+ return (
74
+ <div
75
+ data-slot="command-empty"
76
+ className={cn('py-6 text-center text-xs text-muted-foreground', className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ function CommandGroup({ className, heading, children, ...props }: React.ComponentProps<'div'> & { heading?: string }) {
83
+ return (
84
+ <div data-slot="command-group" className={cn('py-1', className)} {...props}>
85
+ {heading && (
86
+ <div className="px-2 py-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
87
+ {heading}
88
+ </div>
89
+ )}
90
+ {children}
91
+ </div>
92
+ )
93
+ }
94
+
95
+ function CommandItem({ className, onSelect, children, ...props }: React.ComponentProps<'div'> & { onSelect?: () => void }) {
96
+ return (
97
+ <div
98
+ data-slot="command-item"
99
+ role="option"
100
+ onClick={onSelect}
101
+ onKeyDown={(e) => {
102
+ if (e.key === 'Enter' || e.key === ' ') {
103
+ e.preventDefault()
104
+ onSelect?.()
105
+ }
106
+ }}
107
+ tabIndex={0}
108
+ className={cn(
109
+ 'flex cursor-default select-none items-center gap-2 rounded-[var(--radius)] px-2 py-1.5 text-xs outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
110
+ className,
111
+ )}
112
+ {...props}
113
+ >
114
+ {children}
115
+ </div>
116
+ )
117
+ }
118
+
119
+ function CommandSeparator({ className, ...props }: React.ComponentProps<'div'>) {
120
+ return (
121
+ <div
122
+ data-slot="command-separator"
123
+ className={cn('-mx-1 h-[length:var(--border-width)] bg-border', className)}
124
+ {...props}
125
+ />
126
+ )
127
+ }
128
+
129
+ function CommandShortcut({ className, ...props }: React.ComponentProps<'span'>) {
130
+ return (
131
+ <span
132
+ data-slot="command-shortcut"
133
+ className={cn('ml-auto text-[10px] tracking-widest text-muted-foreground', className)}
134
+ {...props}
135
+ />
136
+ )
137
+ }
138
+
139
+ export {
140
+ Command,
141
+ CommandInput,
142
+ CommandList,
143
+ CommandEmpty,
144
+ CommandGroup,
145
+ CommandItem,
146
+ CommandSeparator,
147
+ CommandShortcut,
148
+ }
@@ -0,0 +1,212 @@
1
+ 'use client'
2
+
3
+ import { Menu as MenuPrimitive } from '@base-ui/react/menu'
4
+ import { CheckIcon, ChevronRightIcon } from 'lucide-react'
5
+ import * as React from 'react'
6
+ import { cn } from '@/lib/utils'
7
+
8
+ function ContextMenu({ ...props }: MenuPrimitive.Root.Props) {
9
+ return <MenuPrimitive.Root data-slot="context-menu" {...props} />
10
+ }
11
+
12
+ function ContextMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
13
+ return <MenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
14
+ }
15
+
16
+ function ContextMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
17
+ return <MenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
18
+ }
19
+
20
+ function ContextMenuContent({
21
+ className,
22
+ side = 'bottom',
23
+ align = 'start',
24
+ sideOffset = 4,
25
+ ...props
26
+ }: MenuPrimitive.Popup.Props &
27
+ Pick<MenuPrimitive.Positioner.Props, 'align' | 'side' | 'sideOffset'>) {
28
+ return (
29
+ <MenuPrimitive.Portal>
30
+ <MenuPrimitive.Positioner
31
+ side={side}
32
+ align={align}
33
+ sideOffset={sideOffset}
34
+ className="isolate z-50 outline-none"
35
+ >
36
+ <MenuPrimitive.Popup
37
+ data-slot="context-menu-content"
38
+ className={cn(
39
+ 'z-50 min-w-32 origin-(--transform-origin) overflow-hidden rounded-[var(--radius)] border-[length:var(--border-width)] border-foreground bg-popover text-popover-foreground shadow-none duration-100 outline-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ </MenuPrimitive.Positioner>
45
+ </MenuPrimitive.Portal>
46
+ )
47
+ }
48
+
49
+ function ContextMenuItem({
50
+ className,
51
+ inset,
52
+ variant = 'default',
53
+ ...props
54
+ }: MenuPrimitive.Item.Props & {
55
+ inset?: boolean
56
+ variant?: 'default' | 'destructive'
57
+ }) {
58
+ return (
59
+ <MenuPrimitive.Item
60
+ data-slot="context-menu-item"
61
+ data-inset={inset}
62
+ data-variant={variant}
63
+ className={cn(
64
+ "relative flex cursor-default items-center gap-2 rounded-[var(--radius)] px-2 py-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
65
+ className,
66
+ )}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function ContextMenuCheckboxItem({
73
+ className,
74
+ children,
75
+ checked,
76
+ ...props
77
+ }: MenuPrimitive.CheckboxItem.Props) {
78
+ return (
79
+ <MenuPrimitive.CheckboxItem
80
+ data-slot="context-menu-checkbox-item"
81
+ className={cn(
82
+ "relative flex cursor-default items-center gap-2 rounded-[var(--radius)] py-2 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
83
+ className,
84
+ )}
85
+ checked={checked}
86
+ {...props}
87
+ >
88
+ <span className="pointer-events-none absolute right-2 flex items-center justify-center">
89
+ <MenuPrimitive.CheckboxItemIndicator>
90
+ <CheckIcon className="size-3" />
91
+ </MenuPrimitive.CheckboxItemIndicator>
92
+ </span>
93
+ {children}
94
+ </MenuPrimitive.CheckboxItem>
95
+ )
96
+ }
97
+
98
+ function ContextMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
99
+ return <MenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />
100
+ }
101
+
102
+ function ContextMenuRadioItem({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: MenuPrimitive.RadioItem.Props) {
107
+ return (
108
+ <MenuPrimitive.RadioItem
109
+ data-slot="context-menu-radio-item"
110
+ className={cn(
111
+ "relative flex cursor-default items-center gap-2 rounded-[var(--radius)] py-2 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
112
+ className,
113
+ )}
114
+ {...props}
115
+ >
116
+ <span className="pointer-events-none absolute right-2 flex items-center justify-center">
117
+ <MenuPrimitive.RadioItemIndicator>
118
+ <CheckIcon className="size-3" />
119
+ </MenuPrimitive.RadioItemIndicator>
120
+ </span>
121
+ {children}
122
+ </MenuPrimitive.RadioItem>
123
+ )
124
+ }
125
+
126
+ function ContextMenuLabel({
127
+ className,
128
+ inset,
129
+ ...props
130
+ }: React.ComponentProps<'div'> & { inset?: boolean }) {
131
+ return (
132
+ <div
133
+ data-slot="context-menu-label"
134
+ data-inset={inset}
135
+ className={cn('px-2 py-2 text-xs text-muted-foreground data-inset:pl-7', className)}
136
+ {...props}
137
+ />
138
+ )
139
+ }
140
+
141
+ function ContextMenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {
142
+ return (
143
+ <MenuPrimitive.Separator
144
+ data-slot="context-menu-separator"
145
+ className={cn('-mx-1 h-[length:var(--border-width)] bg-border', className)}
146
+ {...props}
147
+ />
148
+ )
149
+ }
150
+
151
+ function ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
152
+ return (
153
+ <span
154
+ data-slot="context-menu-shortcut"
155
+ className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
156
+ {...props}
157
+ />
158
+ )
159
+ }
160
+
161
+ function ContextMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
162
+ return <MenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />
163
+ }
164
+
165
+ function ContextMenuSubTrigger({
166
+ className,
167
+ children,
168
+ inset,
169
+ ...props
170
+ }: MenuPrimitive.SubmenuTrigger.Props & { inset?: boolean }) {
171
+ return (
172
+ <MenuPrimitive.SubmenuTrigger
173
+ data-slot="context-menu-sub-trigger"
174
+ data-inset={inset}
175
+ className={cn(
176
+ "flex cursor-default items-center gap-2 rounded-[var(--radius)] px-2 py-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
177
+ className,
178
+ )}
179
+ {...props}
180
+ >
181
+ {children}
182
+ <ChevronRightIcon className="ml-auto" />
183
+ </MenuPrimitive.SubmenuTrigger>
184
+ )
185
+ }
186
+
187
+ function ContextMenuSubContent({ className, ...props }: React.ComponentProps<typeof ContextMenuContent>) {
188
+ return (
189
+ <ContextMenuContent
190
+ data-slot="context-menu-sub-content"
191
+ className={cn('w-auto min-w-[96px]', className)}
192
+ {...props}
193
+ />
194
+ )
195
+ }
196
+
197
+ export {
198
+ ContextMenu,
199
+ ContextMenuTrigger,
200
+ ContextMenuPortal,
201
+ ContextMenuContent,
202
+ ContextMenuItem,
203
+ ContextMenuCheckboxItem,
204
+ ContextMenuRadioGroup,
205
+ ContextMenuRadioItem,
206
+ ContextMenuLabel,
207
+ ContextMenuSeparator,
208
+ ContextMenuShortcut,
209
+ ContextMenuSub,
210
+ ContextMenuSubTrigger,
211
+ ContextMenuSubContent,
212
+ }
@@ -0,0 +1,138 @@
1
+ 'use client'
2
+
3
+ import { Dialog as DialogPrimitive } from '@base-ui/react/dialog'
4
+ import { XIcon } from 'lucide-react'
5
+ import type * as React from 'react'
6
+ import { Button } from '@/components/ui/button'
7
+ import { cn } from '@/lib/utils'
8
+
9
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
10
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
11
+ }
12
+
13
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
14
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
15
+ }
16
+
17
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
18
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
19
+ }
20
+
21
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
22
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
23
+ }
24
+
25
+ function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {
26
+ return (
27
+ <DialogPrimitive.Backdrop
28
+ data-slot="dialog-overlay"
29
+ className={cn(
30
+ 'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
31
+ className,
32
+ )}
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ function DialogContent({
39
+ className,
40
+ children,
41
+ showCloseButton = true,
42
+ ...props
43
+ }: DialogPrimitive.Popup.Props & {
44
+ showCloseButton?: boolean
45
+ }) {
46
+ return (
47
+ <DialogPortal>
48
+ <DialogOverlay />
49
+ <DialogPrimitive.Popup
50
+ data-slot="dialog-content"
51
+ className={cn(
52
+ 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-[var(--radius)] border-[length:var(--border-width)] border-foreground bg-card p-4 text-xs/relaxed text-card-foreground duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
53
+ className,
54
+ )}
55
+ {...props}
56
+ >
57
+ {children}
58
+ {showCloseButton && (
59
+ <DialogPrimitive.Close
60
+ data-slot="dialog-close"
61
+ render={<Button variant="ghost" className="absolute top-2 right-2" size="icon" />}
62
+ >
63
+ <XIcon />
64
+ <span className="sr-only">Close</span>
65
+ </DialogPrimitive.Close>
66
+ )}
67
+ </DialogPrimitive.Popup>
68
+ </DialogPortal>
69
+ )
70
+ }
71
+
72
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
73
+ return (
74
+ <div
75
+ data-slot="dialog-header"
76
+ className={cn('flex flex-col gap-1 text-left', className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ function DialogFooter({
83
+ className,
84
+ showCloseButton = false,
85
+ children,
86
+ ...props
87
+ }: React.ComponentProps<'div'> & {
88
+ showCloseButton?: boolean
89
+ }) {
90
+ return (
91
+ <div
92
+ data-slot="dialog-footer"
93
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
94
+ {...props}
95
+ >
96
+ {children}
97
+ {showCloseButton && (
98
+ <DialogPrimitive.Close render={<Button variant="outline" />}>Close</DialogPrimitive.Close>
99
+ )}
100
+ </div>
101
+ )
102
+ }
103
+
104
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
105
+ return (
106
+ <DialogPrimitive.Title
107
+ data-slot="dialog-title"
108
+ className={cn('text-sm font-medium', className)}
109
+ {...props}
110
+ />
111
+ )
112
+ }
113
+
114
+ function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) {
115
+ return (
116
+ <DialogPrimitive.Description
117
+ data-slot="dialog-description"
118
+ className={cn(
119
+ 'text-xs/relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
120
+ className,
121
+ )}
122
+ {...props}
123
+ />
124
+ )
125
+ }
126
+
127
+ export {
128
+ Dialog,
129
+ DialogClose,
130
+ DialogContent,
131
+ DialogDescription,
132
+ DialogFooter,
133
+ DialogHeader,
134
+ DialogOverlay,
135
+ DialogPortal,
136
+ DialogTitle,
137
+ DialogTrigger,
138
+ }
@@ -0,0 +1,134 @@
1
+ 'use client'
2
+
3
+ import { Dialog as DialogPrimitive } from '@base-ui/react/dialog'
4
+ import { XIcon } from 'lucide-react'
5
+ import * as React from 'react'
6
+ import { cn } from '@/lib/utils'
7
+
8
+ type DrawerSide = 'left' | 'right' | 'top' | 'bottom'
9
+
10
+ function Drawer({ ...props }: DialogPrimitive.Root.Props) {
11
+ return <DialogPrimitive.Root data-slot="drawer" {...props} />
12
+ }
13
+
14
+ function DrawerTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
15
+ return <DialogPrimitive.Trigger data-slot="drawer-trigger" {...props} />
16
+ }
17
+
18
+ function DrawerClose({ ...props }: DialogPrimitive.Close.Props) {
19
+ return <DialogPrimitive.Close data-slot="drawer-close" {...props} />
20
+ }
21
+
22
+ function DrawerPortal({ ...props }: DialogPrimitive.Portal.Props) {
23
+ return <DialogPrimitive.Portal data-slot="drawer-portal" {...props} />
24
+ }
25
+
26
+ function DrawerOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {
27
+ return (
28
+ <DialogPrimitive.Backdrop
29
+ data-slot="drawer-overlay"
30
+ className={cn(
31
+ 'fixed inset-0 z-50 bg-black/20 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ const sideClasses: Record<DrawerSide, string> = {
40
+ left: 'inset-y-0 left-0 h-full w-3/4 max-w-sm border-r-[length:var(--border-width)] data-open:slide-in-from-left data-closed:slide-out-to-left',
41
+ right: 'inset-y-0 right-0 h-full w-3/4 max-w-sm border-l-[length:var(--border-width)] data-open:slide-in-from-right data-closed:slide-out-to-right',
42
+ top: 'inset-x-0 top-0 w-full border-b-[length:var(--border-width)] data-open:slide-in-from-top data-closed:slide-out-to-top',
43
+ bottom: 'inset-x-0 bottom-0 w-full border-t-[length:var(--border-width)] data-open:slide-in-from-bottom data-closed:slide-out-to-bottom',
44
+ }
45
+
46
+ function DrawerContent({
47
+ className,
48
+ children,
49
+ side = 'right',
50
+ showCloseButton = true,
51
+ ...props
52
+ }: DialogPrimitive.Popup.Props & {
53
+ side?: DrawerSide
54
+ showCloseButton?: boolean
55
+ }) {
56
+ return (
57
+ <DrawerPortal>
58
+ <DrawerOverlay />
59
+ <DialogPrimitive.Popup
60
+ data-slot="drawer-content"
61
+ className={cn(
62
+ 'fixed z-50 flex flex-col gap-4 rounded-[var(--radius)] border-foreground bg-card p-4 text-xs/relaxed text-card-foreground duration-200 outline-none data-open:animate-in data-closed:animate-out data-closed:duration-200',
63
+ sideClasses[side],
64
+ className,
65
+ )}
66
+ {...props}
67
+ >
68
+ {showCloseButton && (
69
+ <DialogPrimitive.Close
70
+ data-slot="drawer-close"
71
+ className="absolute top-3 right-3 flex size-7 items-center justify-center text-muted-foreground hover:text-foreground"
72
+ aria-label="Close drawer"
73
+ >
74
+ <XIcon className="size-4" />
75
+ </DialogPrimitive.Close>
76
+ )}
77
+ {children}
78
+ </DialogPrimitive.Popup>
79
+ </DrawerPortal>
80
+ )
81
+ }
82
+
83
+ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
84
+ return (
85
+ <div
86
+ data-slot="drawer-header"
87
+ className={cn('flex flex-col gap-1', className)}
88
+ {...props}
89
+ />
90
+ )
91
+ }
92
+
93
+ function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
94
+ return (
95
+ <div
96
+ data-slot="drawer-footer"
97
+ className={cn('mt-auto flex flex-col gap-2', className)}
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ function DrawerTitle({ className, ...props }: DialogPrimitive.Title.Props) {
104
+ return (
105
+ <DialogPrimitive.Title
106
+ data-slot="drawer-title"
107
+ className={cn('text-sm font-medium uppercase tracking-wider', className)}
108
+ {...props}
109
+ />
110
+ )
111
+ }
112
+
113
+ function DrawerDescription({ className, ...props }: DialogPrimitive.Description.Props) {
114
+ return (
115
+ <DialogPrimitive.Description
116
+ data-slot="drawer-description"
117
+ className={cn('text-xs/relaxed text-muted-foreground', className)}
118
+ {...props}
119
+ />
120
+ )
121
+ }
122
+
123
+ export {
124
+ Drawer,
125
+ DrawerTrigger,
126
+ DrawerClose,
127
+ DrawerPortal,
128
+ DrawerOverlay,
129
+ DrawerContent,
130
+ DrawerHeader,
131
+ DrawerFooter,
132
+ DrawerTitle,
133
+ DrawerDescription,
134
+ }