@saena-io/create 0.1.0 → 0.2.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 (100) hide show
  1. package/dist/index.js +9 -9
  2. package/package.json +1 -1
  3. package/template/base/package.json +44 -2
  4. package/template/base/scripts/ui-update.ts +83 -0
  5. package/template/base/src/components/ui/accordion.tsx +75 -0
  6. package/template/base/src/components/ui/alert-dialog.tsx +162 -0
  7. package/template/base/src/components/ui/alert.tsx +73 -0
  8. package/template/base/src/components/ui/app-sidebar.tsx +183 -0
  9. package/template/base/src/components/ui/aspect-ratio.tsx +22 -0
  10. package/template/base/src/components/ui/asset-input.tsx +211 -0
  11. package/template/base/src/components/ui/avatar.tsx +91 -0
  12. package/template/base/src/components/ui/badge.tsx +50 -0
  13. package/template/base/src/components/ui/breadcrumb.tsx +104 -0
  14. package/template/base/src/components/ui/button-group.tsx +78 -0
  15. package/template/base/src/components/ui/button.tsx +56 -0
  16. package/template/base/src/components/ui/calendar.tsx +205 -0
  17. package/template/base/src/components/ui/card.tsx +85 -0
  18. package/template/base/src/components/ui/carousel.tsx +232 -0
  19. package/template/base/src/components/ui/chart.tsx +337 -0
  20. package/template/base/src/components/ui/checkbox.tsx +29 -0
  21. package/template/base/src/components/ui/collapsible.tsx +15 -0
  22. package/template/base/src/components/ui/combobox.tsx +276 -0
  23. package/template/base/src/components/ui/command.tsx +190 -0
  24. package/template/base/src/components/ui/context-menu.tsx +243 -0
  25. package/template/base/src/components/ui/dialog.tsx +134 -0
  26. package/template/base/src/components/ui/direction.tsx +4 -0
  27. package/template/base/src/components/ui/drawer.tsx +120 -0
  28. package/template/base/src/components/ui/dropdown-menu.tsx +254 -0
  29. package/template/base/src/components/ui/empty.tsx +94 -0
  30. package/template/base/src/components/ui/field.tsx +222 -0
  31. package/template/base/src/components/ui/focal-point-picker.tsx +175 -0
  32. package/template/base/src/components/ui/hover-card.tsx +46 -0
  33. package/template/base/src/components/ui/input-group.tsx +149 -0
  34. package/template/base/src/components/ui/input-otp.tsx +85 -0
  35. package/template/base/src/components/ui/input.tsx +20 -0
  36. package/template/base/src/components/ui/item.tsx +188 -0
  37. package/template/base/src/components/ui/kbd.tsx +26 -0
  38. package/template/base/src/components/ui/label.tsx +20 -0
  39. package/template/base/src/components/ui/menubar.tsx +268 -0
  40. package/template/base/src/components/ui/native-select.tsx +58 -0
  41. package/template/base/src/components/ui/nav-main.tsx +70 -0
  42. package/template/base/src/components/ui/nav-projects.tsx +97 -0
  43. package/template/base/src/components/ui/nav-secondary.tsx +37 -0
  44. package/template/base/src/components/ui/nav-user.tsx +108 -0
  45. package/template/base/src/components/ui/navigation-menu.tsx +164 -0
  46. package/template/base/src/components/ui/pagination.tsx +123 -0
  47. package/template/base/src/components/ui/popover.tsx +80 -0
  48. package/template/base/src/components/ui/progress.tsx +66 -0
  49. package/template/base/src/components/ui/radio-group.tsx +36 -0
  50. package/template/base/src/components/ui/resizable.tsx +42 -0
  51. package/template/base/src/components/ui/rich-text/ai-chat-editor.tsx +20 -0
  52. package/template/base/src/components/ui/rich-text/ai-command.tsx +90 -0
  53. package/template/base/src/components/ui/rich-text/ai-copilot.tsx +67 -0
  54. package/template/base/src/components/ui/rich-text/ai-menu.tsx +456 -0
  55. package/template/base/src/components/ui/rich-text/ai-node.tsx +42 -0
  56. package/template/base/src/components/ui/rich-text/ai-toolbar-button.tsx +29 -0
  57. package/template/base/src/components/ui/rich-text/block-draggable.tsx +187 -0
  58. package/template/base/src/components/ui/rich-text/block-selection.tsx +17 -0
  59. package/template/base/src/components/ui/rich-text/code-block-node.tsx +204 -0
  60. package/template/base/src/components/ui/rich-text/codec.ts +63 -0
  61. package/template/base/src/components/ui/rich-text/extension.ts +53 -0
  62. package/template/base/src/components/ui/rich-text/ghost-text.tsx +23 -0
  63. package/template/base/src/components/ui/rich-text/import-export-toolbar.tsx +103 -0
  64. package/template/base/src/components/ui/rich-text/link.tsx +18 -0
  65. package/template/base/src/components/ui/rich-text/list-node.tsx +65 -0
  66. package/template/base/src/components/ui/rich-text/nodes.tsx +44 -0
  67. package/template/base/src/components/ui/rich-text/plugins.ts +233 -0
  68. package/template/base/src/components/ui/rich-text/rich-text-editor.tsx +82 -0
  69. package/template/base/src/components/ui/rich-text/static.tsx +117 -0
  70. package/template/base/src/components/ui/rich-text/table-node.tsx +934 -0
  71. package/template/base/src/components/ui/rich-text/table-toolbar.tsx +232 -0
  72. package/template/base/src/components/ui/rich-text/toggle-node.tsx +36 -0
  73. package/template/base/src/components/ui/rich-text/toolbar-slots.ts +41 -0
  74. package/template/base/src/components/ui/rich-text/toolbar.tsx +668 -0
  75. package/template/base/src/components/ui/rich-text/use-ai-chat.ts +35 -0
  76. package/template/base/src/components/ui/rich-text/variable-type.ts +4 -0
  77. package/template/base/src/components/ui/rich-text/variable.tsx +97 -0
  78. package/template/base/src/components/ui/scroll-area.tsx +49 -0
  79. package/template/base/src/components/ui/select.tsx +202 -0
  80. package/template/base/src/components/ui/separator.tsx +19 -0
  81. package/template/base/src/components/ui/sheet.tsx +126 -0
  82. package/template/base/src/components/ui/sidebar.tsx +695 -0
  83. package/template/base/src/components/ui/skeleton.tsx +13 -0
  84. package/template/base/src/components/ui/slider.tsx +52 -0
  85. package/template/base/src/components/ui/sonner.tsx +50 -0
  86. package/template/base/src/components/ui/spinner.tsx +18 -0
  87. package/template/base/src/components/ui/switch.tsx +30 -0
  88. package/template/base/src/components/ui/table.tsx +89 -0
  89. package/template/base/src/components/ui/tabs.tsx +73 -0
  90. package/template/base/src/components/ui/textarea.tsx +18 -0
  91. package/template/base/src/components/ui/toggle-group.tsx +85 -0
  92. package/template/base/src/components/ui/toggle.tsx +45 -0
  93. package/template/base/src/components/ui/toolbar.tsx +451 -0
  94. package/template/base/src/components/ui/tooltip.tsx +52 -0
  95. package/template/base/src/hooks/use-mobile.ts +19 -0
  96. package/template/base/src/lib/utils.ts +6 -0
  97. package/template/base/src/routes/__root.tsx +1 -1
  98. package/template/base/src/server/auth.ts +2 -2
  99. package/template/base/src/styles/globals.css +230 -0
  100. package/template/base/vite.config.ts +15 -1
@@ -0,0 +1,190 @@
1
+ 'use client';
2
+
3
+ import { Command as CommandPrimitive } from 'cmdk';
4
+ import type * as React from 'react';
5
+
6
+ import { SearchIcon, Tick02Icon } from '@hugeicons/core-free-icons';
7
+ import { HugeiconsIcon } from '@hugeicons/react';
8
+ import {
9
+ Dialog,
10
+ DialogContent,
11
+ DialogDescription,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ } from '@saena-io/ui/components/dialog';
15
+ import { InputGroup, InputGroupAddon } from '@saena-io/ui/components/input-group';
16
+ import { cn } from '@saena-io/ui/lib/utils';
17
+
18
+ function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
19
+ return (
20
+ <CommandPrimitive
21
+ data-slot="command"
22
+ className={cn(
23
+ 'flex size-full flex-col overflow-hidden rounded-xl bg-popover p-1 text-popover-foreground',
24
+ className,
25
+ )}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function CommandDialog({
32
+ title = 'Command Palette',
33
+ description = 'Search for a command to run...',
34
+ children,
35
+ className,
36
+ showCloseButton = false,
37
+ ...props
38
+ }: Omit<React.ComponentProps<typeof Dialog>, 'children'> & {
39
+ title?: string;
40
+ description?: string;
41
+ className?: string;
42
+ showCloseButton?: boolean;
43
+ children: React.ReactNode;
44
+ }) {
45
+ return (
46
+ <Dialog {...props}>
47
+ <DialogHeader className="sr-only">
48
+ <DialogTitle>{title}</DialogTitle>
49
+ <DialogDescription>{description}</DialogDescription>
50
+ </DialogHeader>
51
+ <DialogContent
52
+ className={cn('top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0', className)}
53
+ showCloseButton={showCloseButton}
54
+ >
55
+ {children}
56
+ </DialogContent>
57
+ </Dialog>
58
+ );
59
+ }
60
+
61
+ function CommandInput({
62
+ className,
63
+ ...props
64
+ }: React.ComponentProps<typeof CommandPrimitive.Input>) {
65
+ return (
66
+ <div data-slot="command-input-wrapper" className="p-1 pb-0">
67
+ <InputGroup className="h-8! bg-input/20 dark:bg-input/30">
68
+ <CommandPrimitive.Input
69
+ data-slot="command-input"
70
+ className={cn(
71
+ 'w-full text-xs/relaxed outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
72
+ className,
73
+ )}
74
+ {...props}
75
+ />
76
+ <InputGroupAddon>
77
+ <HugeiconsIcon
78
+ icon={SearchIcon}
79
+ strokeWidth={2}
80
+ className="size-3.5 shrink-0 opacity-50"
81
+ />
82
+ </InputGroupAddon>
83
+ </InputGroup>
84
+ </div>
85
+ );
86
+ }
87
+
88
+ function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
89
+ return (
90
+ <CommandPrimitive.List
91
+ data-slot="command-list"
92
+ className={cn(
93
+ 'no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none',
94
+ className,
95
+ )}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ function CommandEmpty({
102
+ className,
103
+ ...props
104
+ }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
105
+ return (
106
+ <CommandPrimitive.Empty
107
+ data-slot="command-empty"
108
+ className={cn('py-6 text-center text-xs/relaxed', className)}
109
+ {...props}
110
+ />
111
+ );
112
+ }
113
+
114
+ function CommandGroup({
115
+ className,
116
+ ...props
117
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) {
118
+ return (
119
+ <CommandPrimitive.Group
120
+ data-slot="command-group"
121
+ className={cn(
122
+ 'overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:px-2.5 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground',
123
+ className,
124
+ )}
125
+ {...props}
126
+ />
127
+ );
128
+ }
129
+
130
+ function CommandSeparator({
131
+ className,
132
+ ...props
133
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
134
+ return (
135
+ <CommandPrimitive.Separator
136
+ data-slot="command-separator"
137
+ className={cn('-mx-1 my-1 h-px bg-border/50', className)}
138
+ {...props}
139
+ />
140
+ );
141
+ }
142
+
143
+ function CommandItem({
144
+ className,
145
+ children,
146
+ ...props
147
+ }: React.ComponentProps<typeof CommandPrimitive.Item>) {
148
+ return (
149
+ <CommandPrimitive.Item
150
+ data-slot="command-item"
151
+ className={cn(
152
+ "group/command-item relative flex min-h-7 cursor-default items-center gap-2 rounded-md px-2.5 py-1.5 text-xs/relaxed outline-hidden select-none in-data-[slot=dialog-content]:rounded-md data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-muted data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 data-selected:*:[svg]:text-foreground",
153
+ className,
154
+ )}
155
+ {...props}
156
+ >
157
+ {children}
158
+ <HugeiconsIcon
159
+ icon={Tick02Icon}
160
+ strokeWidth={2}
161
+ className="ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100"
162
+ />
163
+ </CommandPrimitive.Item>
164
+ );
165
+ }
166
+
167
+ function CommandShortcut({ className, ...props }: React.ComponentProps<'span'>) {
168
+ return (
169
+ <span
170
+ data-slot="command-shortcut"
171
+ className={cn(
172
+ 'ml-auto text-[0.625rem] tracking-widest text-muted-foreground group-data-selected/command-item:text-foreground',
173
+ className,
174
+ )}
175
+ {...props}
176
+ />
177
+ );
178
+ }
179
+
180
+ export {
181
+ Command,
182
+ CommandDialog,
183
+ CommandInput,
184
+ CommandList,
185
+ CommandEmpty,
186
+ CommandGroup,
187
+ CommandItem,
188
+ CommandShortcut,
189
+ CommandSeparator,
190
+ };
@@ -0,0 +1,243 @@
1
+ 'use client';
2
+
3
+ import { ContextMenu as ContextMenuPrimitive } from '@base-ui/react/context-menu';
4
+ import type * as React from 'react';
5
+
6
+ import { ArrowRight01Icon, Tick02Icon } from '@hugeicons/core-free-icons';
7
+ import { HugeiconsIcon } from '@hugeicons/react';
8
+ import { cn } from '@saena-io/ui/lib/utils';
9
+
10
+ function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
11
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
12
+ }
13
+
14
+ function ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {
15
+ return <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />;
16
+ }
17
+
18
+ function ContextMenuTrigger({ className, ...props }: ContextMenuPrimitive.Trigger.Props) {
19
+ return (
20
+ <ContextMenuPrimitive.Trigger
21
+ data-slot="context-menu-trigger"
22
+ className={cn('select-none', className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function ContextMenuContent({
29
+ className,
30
+ align = 'start',
31
+ alignOffset = 4,
32
+ side = 'right',
33
+ sideOffset = 0,
34
+ ...props
35
+ }: ContextMenuPrimitive.Popup.Props &
36
+ Pick<ContextMenuPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
37
+ return (
38
+ <ContextMenuPrimitive.Portal>
39
+ <ContextMenuPrimitive.Positioner
40
+ className="isolate z-50 outline-none"
41
+ align={align}
42
+ alignOffset={alignOffset}
43
+ side={side}
44
+ sideOffset={sideOffset}
45
+ >
46
+ <ContextMenuPrimitive.Popup
47
+ data-slot="context-menu-content"
48
+ className={cn(
49
+ 'z-50 max-h-(--available-height) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 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',
50
+ className,
51
+ )}
52
+ {...props}
53
+ />
54
+ </ContextMenuPrimitive.Positioner>
55
+ </ContextMenuPrimitive.Portal>
56
+ );
57
+ }
58
+
59
+ function ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {
60
+ return <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />;
61
+ }
62
+
63
+ function ContextMenuLabel({
64
+ className,
65
+ inset,
66
+ ...props
67
+ }: ContextMenuPrimitive.GroupLabel.Props & {
68
+ inset?: boolean;
69
+ }) {
70
+ return (
71
+ <ContextMenuPrimitive.GroupLabel
72
+ data-slot="context-menu-label"
73
+ data-inset={inset}
74
+ className={cn('px-2 py-1.5 text-xs text-muted-foreground data-inset:pl-7.5', className)}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function ContextMenuItem({
81
+ className,
82
+ inset,
83
+ variant = 'default',
84
+ ...props
85
+ }: ContextMenuPrimitive.Item.Props & {
86
+ inset?: boolean;
87
+ variant?: 'default' | 'destructive';
88
+ }) {
89
+ return (
90
+ <ContextMenuPrimitive.Item
91
+ data-slot="context-menu-item"
92
+ data-inset={inset}
93
+ data-variant={variant}
94
+ className={cn(
95
+ "group/context-menu-item relative flex min-h-7 cursor-default items-center gap-2 rounded-md px-2 py-1 text-xs/relaxed outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7.5 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 data-[variant=destructive]:*:[svg]:text-destructive",
96
+ className,
97
+ )}
98
+ {...props}
99
+ />
100
+ );
101
+ }
102
+
103
+ function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
104
+ return <ContextMenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />;
105
+ }
106
+
107
+ function ContextMenuSubTrigger({
108
+ className,
109
+ inset,
110
+ children,
111
+ ...props
112
+ }: ContextMenuPrimitive.SubmenuTrigger.Props & {
113
+ inset?: boolean;
114
+ }) {
115
+ return (
116
+ <ContextMenuPrimitive.SubmenuTrigger
117
+ data-slot="context-menu-sub-trigger"
118
+ data-inset={inset}
119
+ className={cn(
120
+ "flex min-h-7 cursor-default items-center gap-2 rounded-md px-2 py-1 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7.5 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
121
+ className,
122
+ )}
123
+ {...props}
124
+ >
125
+ {children}
126
+ <HugeiconsIcon icon={ArrowRight01Icon} strokeWidth={2} className="ml-auto" />
127
+ </ContextMenuPrimitive.SubmenuTrigger>
128
+ );
129
+ }
130
+
131
+ function ContextMenuSubContent({ ...props }: React.ComponentProps<typeof ContextMenuContent>) {
132
+ return (
133
+ <ContextMenuContent
134
+ data-slot="context-menu-sub-content"
135
+ className="shadow-lg"
136
+ side="right"
137
+ {...props}
138
+ />
139
+ );
140
+ }
141
+
142
+ function ContextMenuCheckboxItem({
143
+ className,
144
+ children,
145
+ checked,
146
+ inset,
147
+ ...props
148
+ }: ContextMenuPrimitive.CheckboxItem.Props & {
149
+ inset?: boolean;
150
+ }) {
151
+ return (
152
+ <ContextMenuPrimitive.CheckboxItem
153
+ data-slot="context-menu-checkbox-item"
154
+ data-inset={inset}
155
+ className={cn(
156
+ "relative flex min-h-7 cursor-default items-center gap-2 rounded-md py-1.5 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
157
+ className,
158
+ )}
159
+ checked={checked}
160
+ {...props}
161
+ >
162
+ <span className="pointer-events-none absolute right-2 flex items-center justify-center">
163
+ <ContextMenuPrimitive.CheckboxItemIndicator>
164
+ <HugeiconsIcon icon={Tick02Icon} strokeWidth={2} />
165
+ </ContextMenuPrimitive.CheckboxItemIndicator>
166
+ </span>
167
+ {children}
168
+ </ContextMenuPrimitive.CheckboxItem>
169
+ );
170
+ }
171
+
172
+ function ContextMenuRadioGroup({ ...props }: ContextMenuPrimitive.RadioGroup.Props) {
173
+ return <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />;
174
+ }
175
+
176
+ function ContextMenuRadioItem({
177
+ className,
178
+ children,
179
+ inset,
180
+ ...props
181
+ }: ContextMenuPrimitive.RadioItem.Props & {
182
+ inset?: boolean;
183
+ }) {
184
+ return (
185
+ <ContextMenuPrimitive.RadioItem
186
+ data-slot="context-menu-radio-item"
187
+ data-inset={inset}
188
+ className={cn(
189
+ "relative flex min-h-7 cursor-default items-center gap-2 rounded-md py-1.5 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7.5 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
190
+ className,
191
+ )}
192
+ {...props}
193
+ >
194
+ <span className="pointer-events-none absolute right-2 flex items-center justify-center">
195
+ <ContextMenuPrimitive.RadioItemIndicator>
196
+ <HugeiconsIcon icon={Tick02Icon} strokeWidth={2} />
197
+ </ContextMenuPrimitive.RadioItemIndicator>
198
+ </span>
199
+ {children}
200
+ </ContextMenuPrimitive.RadioItem>
201
+ );
202
+ }
203
+
204
+ function ContextMenuSeparator({ className, ...props }: ContextMenuPrimitive.Separator.Props) {
205
+ return (
206
+ <ContextMenuPrimitive.Separator
207
+ data-slot="context-menu-separator"
208
+ className={cn('-mx-1 my-1 h-px bg-border/50', className)}
209
+ {...props}
210
+ />
211
+ );
212
+ }
213
+
214
+ function ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
215
+ return (
216
+ <span
217
+ data-slot="context-menu-shortcut"
218
+ className={cn(
219
+ 'ml-auto text-[0.625rem] tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground',
220
+ className,
221
+ )}
222
+ {...props}
223
+ />
224
+ );
225
+ }
226
+
227
+ export {
228
+ ContextMenu,
229
+ ContextMenuTrigger,
230
+ ContextMenuContent,
231
+ ContextMenuItem,
232
+ ContextMenuCheckboxItem,
233
+ ContextMenuRadioItem,
234
+ ContextMenuLabel,
235
+ ContextMenuSeparator,
236
+ ContextMenuShortcut,
237
+ ContextMenuGroup,
238
+ ContextMenuPortal,
239
+ ContextMenuSub,
240
+ ContextMenuSubContent,
241
+ ContextMenuSubTrigger,
242
+ ContextMenuRadioGroup,
243
+ };
@@ -0,0 +1,134 @@
1
+ import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
2
+ import type * as React from 'react';
3
+
4
+ import { Cancel01Icon } from '@hugeicons/core-free-icons';
5
+ import { HugeiconsIcon } from '@hugeicons/react';
6
+ import { Button } from '@saena-io/ui/components/button';
7
+ import { cn } from '@saena-io/ui/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/80 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-xl bg-popover p-4 text-xs/relaxed text-popover-foreground ring-1 ring-foreground/10 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-sm" />}
62
+ >
63
+ <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} />
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 data-slot="dialog-header" className={cn('flex flex-col gap-1', className)} {...props} />
75
+ );
76
+ }
77
+
78
+ function DialogFooter({
79
+ className,
80
+ showCloseButton = false,
81
+ children,
82
+ ...props
83
+ }: React.ComponentProps<'div'> & {
84
+ showCloseButton?: boolean;
85
+ }) {
86
+ return (
87
+ <div
88
+ data-slot="dialog-footer"
89
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
90
+ {...props}
91
+ >
92
+ {children}
93
+ {showCloseButton && (
94
+ <DialogPrimitive.Close render={<Button variant="outline" />}>Close</DialogPrimitive.Close>
95
+ )}
96
+ </div>
97
+ );
98
+ }
99
+
100
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
101
+ return (
102
+ <DialogPrimitive.Title
103
+ data-slot="dialog-title"
104
+ className={cn('font-heading text-sm font-medium', className)}
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+
110
+ function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) {
111
+ return (
112
+ <DialogPrimitive.Description
113
+ data-slot="dialog-description"
114
+ className={cn(
115
+ 'text-xs/relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
116
+ className,
117
+ )}
118
+ {...props}
119
+ />
120
+ );
121
+ }
122
+
123
+ export {
124
+ Dialog,
125
+ DialogClose,
126
+ DialogContent,
127
+ DialogDescription,
128
+ DialogFooter,
129
+ DialogHeader,
130
+ DialogOverlay,
131
+ DialogPortal,
132
+ DialogTitle,
133
+ DialogTrigger,
134
+ };
@@ -0,0 +1,4 @@
1
+ export {
2
+ DirectionProvider,
3
+ useDirection,
4
+ } from '@base-ui/react/direction-provider';
@@ -0,0 +1,120 @@
1
+ 'use client';
2
+
3
+ import type * as React from 'react';
4
+ import { Drawer as DrawerPrimitive } from 'vaul';
5
+
6
+ import { cn } from '@saena-io/ui/lib/utils';
7
+
8
+ function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
9
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
10
+ }
11
+
12
+ function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
13
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
14
+ }
15
+
16
+ function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
17
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
18
+ }
19
+
20
+ function DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
21
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
22
+ }
23
+
24
+ function DrawerOverlay({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
28
+ return (
29
+ <DrawerPrimitive.Overlay
30
+ data-slot="drawer-overlay"
31
+ className={cn(
32
+ 'fixed inset-0 z-50 bg-black/80 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function DrawerContent({
41
+ className,
42
+ children,
43
+ ...props
44
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
45
+ return (
46
+ <DrawerPortal data-slot="drawer-portal">
47
+ <DrawerOverlay />
48
+ <DrawerPrimitive.Content
49
+ data-slot="drawer-content"
50
+ className={cn(
51
+ 'group/drawer-content fixed z-50 flex h-auto flex-col bg-transparent p-2 text-xs/relaxed text-popover-foreground before:absolute before:inset-2 before:-z-10 before:rounded-xl before:border before:border-border before:bg-popover data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm',
52
+ className,
53
+ )}
54
+ {...props}
55
+ >
56
+ <div className="mx-auto mt-4 hidden h-1.5 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
57
+ {children}
58
+ </DrawerPrimitive.Content>
59
+ </DrawerPortal>
60
+ );
61
+ }
62
+
63
+ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
64
+ return (
65
+ <div
66
+ data-slot="drawer-header"
67
+ className={cn(
68
+ 'flex flex-col gap-1 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left',
69
+ className,
70
+ )}
71
+ {...props}
72
+ />
73
+ );
74
+ }
75
+
76
+ function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
77
+ return (
78
+ <div
79
+ data-slot="drawer-footer"
80
+ className={cn('mt-auto flex flex-col gap-2 p-4', className)}
81
+ {...props}
82
+ />
83
+ );
84
+ }
85
+
86
+ function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
87
+ return (
88
+ <DrawerPrimitive.Title
89
+ data-slot="drawer-title"
90
+ className={cn('font-heading text-sm font-medium text-foreground', className)}
91
+ {...props}
92
+ />
93
+ );
94
+ }
95
+
96
+ function DrawerDescription({
97
+ className,
98
+ ...props
99
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
100
+ return (
101
+ <DrawerPrimitive.Description
102
+ data-slot="drawer-description"
103
+ className={cn('text-xs/relaxed text-muted-foreground', className)}
104
+ {...props}
105
+ />
106
+ );
107
+ }
108
+
109
+ export {
110
+ Drawer,
111
+ DrawerPortal,
112
+ DrawerOverlay,
113
+ DrawerTrigger,
114
+ DrawerClose,
115
+ DrawerContent,
116
+ DrawerHeader,
117
+ DrawerFooter,
118
+ DrawerTitle,
119
+ DrawerDescription,
120
+ };