@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.
- package/dist/index.js +9 -9
- package/package.json +1 -1
- package/template/base/package.json +44 -2
- package/template/base/scripts/ui-update.ts +83 -0
- package/template/base/src/components/ui/accordion.tsx +75 -0
- package/template/base/src/components/ui/alert-dialog.tsx +162 -0
- package/template/base/src/components/ui/alert.tsx +73 -0
- package/template/base/src/components/ui/app-sidebar.tsx +183 -0
- package/template/base/src/components/ui/aspect-ratio.tsx +22 -0
- package/template/base/src/components/ui/asset-input.tsx +211 -0
- package/template/base/src/components/ui/avatar.tsx +91 -0
- package/template/base/src/components/ui/badge.tsx +50 -0
- package/template/base/src/components/ui/breadcrumb.tsx +104 -0
- package/template/base/src/components/ui/button-group.tsx +78 -0
- package/template/base/src/components/ui/button.tsx +56 -0
- package/template/base/src/components/ui/calendar.tsx +205 -0
- package/template/base/src/components/ui/card.tsx +85 -0
- package/template/base/src/components/ui/carousel.tsx +232 -0
- package/template/base/src/components/ui/chart.tsx +337 -0
- package/template/base/src/components/ui/checkbox.tsx +29 -0
- package/template/base/src/components/ui/collapsible.tsx +15 -0
- package/template/base/src/components/ui/combobox.tsx +276 -0
- package/template/base/src/components/ui/command.tsx +190 -0
- package/template/base/src/components/ui/context-menu.tsx +243 -0
- package/template/base/src/components/ui/dialog.tsx +134 -0
- package/template/base/src/components/ui/direction.tsx +4 -0
- package/template/base/src/components/ui/drawer.tsx +120 -0
- package/template/base/src/components/ui/dropdown-menu.tsx +254 -0
- package/template/base/src/components/ui/empty.tsx +94 -0
- package/template/base/src/components/ui/field.tsx +222 -0
- package/template/base/src/components/ui/focal-point-picker.tsx +175 -0
- package/template/base/src/components/ui/hover-card.tsx +46 -0
- package/template/base/src/components/ui/input-group.tsx +149 -0
- package/template/base/src/components/ui/input-otp.tsx +85 -0
- package/template/base/src/components/ui/input.tsx +20 -0
- package/template/base/src/components/ui/item.tsx +188 -0
- package/template/base/src/components/ui/kbd.tsx +26 -0
- package/template/base/src/components/ui/label.tsx +20 -0
- package/template/base/src/components/ui/menubar.tsx +268 -0
- package/template/base/src/components/ui/native-select.tsx +58 -0
- package/template/base/src/components/ui/nav-main.tsx +70 -0
- package/template/base/src/components/ui/nav-projects.tsx +97 -0
- package/template/base/src/components/ui/nav-secondary.tsx +37 -0
- package/template/base/src/components/ui/nav-user.tsx +108 -0
- package/template/base/src/components/ui/navigation-menu.tsx +164 -0
- package/template/base/src/components/ui/pagination.tsx +123 -0
- package/template/base/src/components/ui/popover.tsx +80 -0
- package/template/base/src/components/ui/progress.tsx +66 -0
- package/template/base/src/components/ui/radio-group.tsx +36 -0
- package/template/base/src/components/ui/resizable.tsx +42 -0
- package/template/base/src/components/ui/rich-text/ai-chat-editor.tsx +20 -0
- package/template/base/src/components/ui/rich-text/ai-command.tsx +90 -0
- package/template/base/src/components/ui/rich-text/ai-copilot.tsx +67 -0
- package/template/base/src/components/ui/rich-text/ai-menu.tsx +456 -0
- package/template/base/src/components/ui/rich-text/ai-node.tsx +42 -0
- package/template/base/src/components/ui/rich-text/ai-toolbar-button.tsx +29 -0
- package/template/base/src/components/ui/rich-text/block-draggable.tsx +187 -0
- package/template/base/src/components/ui/rich-text/block-selection.tsx +17 -0
- package/template/base/src/components/ui/rich-text/code-block-node.tsx +204 -0
- package/template/base/src/components/ui/rich-text/codec.ts +63 -0
- package/template/base/src/components/ui/rich-text/extension.ts +53 -0
- package/template/base/src/components/ui/rich-text/ghost-text.tsx +23 -0
- package/template/base/src/components/ui/rich-text/import-export-toolbar.tsx +103 -0
- package/template/base/src/components/ui/rich-text/link.tsx +18 -0
- package/template/base/src/components/ui/rich-text/list-node.tsx +65 -0
- package/template/base/src/components/ui/rich-text/nodes.tsx +44 -0
- package/template/base/src/components/ui/rich-text/plugins.ts +233 -0
- package/template/base/src/components/ui/rich-text/rich-text-editor.tsx +82 -0
- package/template/base/src/components/ui/rich-text/static.tsx +117 -0
- package/template/base/src/components/ui/rich-text/table-node.tsx +934 -0
- package/template/base/src/components/ui/rich-text/table-toolbar.tsx +232 -0
- package/template/base/src/components/ui/rich-text/toggle-node.tsx +36 -0
- package/template/base/src/components/ui/rich-text/toolbar-slots.ts +41 -0
- package/template/base/src/components/ui/rich-text/toolbar.tsx +668 -0
- package/template/base/src/components/ui/rich-text/use-ai-chat.ts +35 -0
- package/template/base/src/components/ui/rich-text/variable-type.ts +4 -0
- package/template/base/src/components/ui/rich-text/variable.tsx +97 -0
- package/template/base/src/components/ui/scroll-area.tsx +49 -0
- package/template/base/src/components/ui/select.tsx +202 -0
- package/template/base/src/components/ui/separator.tsx +19 -0
- package/template/base/src/components/ui/sheet.tsx +126 -0
- package/template/base/src/components/ui/sidebar.tsx +695 -0
- package/template/base/src/components/ui/skeleton.tsx +13 -0
- package/template/base/src/components/ui/slider.tsx +52 -0
- package/template/base/src/components/ui/sonner.tsx +50 -0
- package/template/base/src/components/ui/spinner.tsx +18 -0
- package/template/base/src/components/ui/switch.tsx +30 -0
- package/template/base/src/components/ui/table.tsx +89 -0
- package/template/base/src/components/ui/tabs.tsx +73 -0
- package/template/base/src/components/ui/textarea.tsx +18 -0
- package/template/base/src/components/ui/toggle-group.tsx +85 -0
- package/template/base/src/components/ui/toggle.tsx +45 -0
- package/template/base/src/components/ui/toolbar.tsx +451 -0
- package/template/base/src/components/ui/tooltip.tsx +52 -0
- package/template/base/src/hooks/use-mobile.ts +19 -0
- package/template/base/src/lib/utils.ts +6 -0
- package/template/base/src/routes/__root.tsx +1 -1
- package/template/base/src/server/auth.ts +2 -2
- package/template/base/src/styles/globals.css +230 -0
- package/template/base/vite.config.ts +15 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@saena-io/ui/lib/utils';
|
|
6
|
+
|
|
7
|
+
function Label({ className, ...props }: React.ComponentProps<'label'>) {
|
|
8
|
+
return (
|
|
9
|
+
<label
|
|
10
|
+
data-slot="label"
|
|
11
|
+
className={cn(
|
|
12
|
+
'flex items-center gap-2 text-xs/relaxed leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Label };
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Menu as MenuPrimitive } from '@base-ui/react/menu';
|
|
4
|
+
import { Menubar as MenubarPrimitive } from '@base-ui/react/menubar';
|
|
5
|
+
import type * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Tick02Icon } from '@hugeicons/core-free-icons';
|
|
8
|
+
import { HugeiconsIcon } from '@hugeicons/react';
|
|
9
|
+
import {
|
|
10
|
+
DropdownMenu,
|
|
11
|
+
DropdownMenuContent,
|
|
12
|
+
DropdownMenuGroup,
|
|
13
|
+
DropdownMenuItem,
|
|
14
|
+
DropdownMenuLabel,
|
|
15
|
+
DropdownMenuPortal,
|
|
16
|
+
DropdownMenuRadioGroup,
|
|
17
|
+
DropdownMenuSeparator,
|
|
18
|
+
DropdownMenuShortcut,
|
|
19
|
+
DropdownMenuSub,
|
|
20
|
+
DropdownMenuSubContent,
|
|
21
|
+
DropdownMenuSubTrigger,
|
|
22
|
+
DropdownMenuTrigger,
|
|
23
|
+
} from '@saena-io/ui/components/dropdown-menu';
|
|
24
|
+
import { cn } from '@saena-io/ui/lib/utils';
|
|
25
|
+
|
|
26
|
+
function Menubar({ className, ...props }: MenubarPrimitive.Props) {
|
|
27
|
+
return (
|
|
28
|
+
<MenubarPrimitive
|
|
29
|
+
data-slot="menubar"
|
|
30
|
+
className={cn('flex h-9 items-center rounded-lg border p-1', className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {
|
|
37
|
+
return <DropdownMenu data-slot="menubar-menu" {...props} />;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function MenubarGroup({ ...props }: React.ComponentProps<typeof DropdownMenuGroup>) {
|
|
41
|
+
return <DropdownMenuGroup data-slot="menubar-group" {...props} />;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function MenubarPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPortal>) {
|
|
45
|
+
return <DropdownMenuPortal data-slot="menubar-portal" {...props} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function MenubarTrigger({ className, ...props }: React.ComponentProps<typeof DropdownMenuTrigger>) {
|
|
49
|
+
return (
|
|
50
|
+
<DropdownMenuTrigger
|
|
51
|
+
data-slot="menubar-trigger"
|
|
52
|
+
className={cn(
|
|
53
|
+
'flex items-center rounded-[calc(var(--radius-md)-2px)] px-2 py-[calc(--spacing(0.85))] text-xs/relaxed font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted',
|
|
54
|
+
className,
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function MenubarContent({
|
|
62
|
+
className,
|
|
63
|
+
align = 'start',
|
|
64
|
+
alignOffset = -4,
|
|
65
|
+
sideOffset = 8,
|
|
66
|
+
...props
|
|
67
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
68
|
+
return (
|
|
69
|
+
<DropdownMenuContent
|
|
70
|
+
data-slot="menubar-content"
|
|
71
|
+
align={align}
|
|
72
|
+
alignOffset={alignOffset}
|
|
73
|
+
sideOffset={sideOffset}
|
|
74
|
+
className={cn(
|
|
75
|
+
'min-w-32 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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',
|
|
76
|
+
className,
|
|
77
|
+
)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function MenubarItem({
|
|
84
|
+
className,
|
|
85
|
+
inset,
|
|
86
|
+
variant = 'default',
|
|
87
|
+
...props
|
|
88
|
+
}: React.ComponentProps<typeof DropdownMenuItem>) {
|
|
89
|
+
return (
|
|
90
|
+
<DropdownMenuItem
|
|
91
|
+
data-slot="menubar-item"
|
|
92
|
+
data-inset={inset}
|
|
93
|
+
data-variant={variant}
|
|
94
|
+
className={cn(
|
|
95
|
+
"group/menubar-item min-h-7 gap-2 rounded-md px-2 py-1 text-xs/relaxed 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:opacity-50 [&_svg:not([class*='size-'])]:size-3.5 data-[variant=destructive]:*:[svg]:text-destructive!",
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function MenubarCheckboxItem({
|
|
104
|
+
className,
|
|
105
|
+
children,
|
|
106
|
+
checked,
|
|
107
|
+
inset,
|
|
108
|
+
...props
|
|
109
|
+
}: MenuPrimitive.CheckboxItem.Props & {
|
|
110
|
+
inset?: boolean;
|
|
111
|
+
}) {
|
|
112
|
+
return (
|
|
113
|
+
<MenuPrimitive.CheckboxItem
|
|
114
|
+
data-slot="menubar-checkbox-item"
|
|
115
|
+
data-inset={inset}
|
|
116
|
+
className={cn(
|
|
117
|
+
'relative flex min-h-7 cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-7.5 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',
|
|
118
|
+
className,
|
|
119
|
+
)}
|
|
120
|
+
checked={checked}
|
|
121
|
+
{...props}
|
|
122
|
+
>
|
|
123
|
+
<span className="pointer-events-none absolute left-2 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
124
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
125
|
+
<HugeiconsIcon icon={Tick02Icon} strokeWidth={2} />
|
|
126
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
127
|
+
</span>
|
|
128
|
+
{children}
|
|
129
|
+
</MenuPrimitive.CheckboxItem>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function MenubarRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuRadioGroup>) {
|
|
134
|
+
return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function MenubarRadioItem({
|
|
138
|
+
className,
|
|
139
|
+
children,
|
|
140
|
+
inset,
|
|
141
|
+
...props
|
|
142
|
+
}: MenuPrimitive.RadioItem.Props & {
|
|
143
|
+
inset?: boolean;
|
|
144
|
+
}) {
|
|
145
|
+
return (
|
|
146
|
+
<MenuPrimitive.RadioItem
|
|
147
|
+
data-slot="menubar-radio-item"
|
|
148
|
+
data-inset={inset}
|
|
149
|
+
className={cn(
|
|
150
|
+
"relative flex min-h-7 cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-7.5 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",
|
|
151
|
+
className,
|
|
152
|
+
)}
|
|
153
|
+
{...props}
|
|
154
|
+
>
|
|
155
|
+
<span className="pointer-events-none absolute left-2 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
156
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
157
|
+
<HugeiconsIcon icon={Tick02Icon} strokeWidth={2} />
|
|
158
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
159
|
+
</span>
|
|
160
|
+
{children}
|
|
161
|
+
</MenuPrimitive.RadioItem>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function MenubarLabel({
|
|
166
|
+
className,
|
|
167
|
+
inset,
|
|
168
|
+
...props
|
|
169
|
+
}: React.ComponentProps<typeof DropdownMenuLabel> & {
|
|
170
|
+
inset?: boolean;
|
|
171
|
+
}) {
|
|
172
|
+
return (
|
|
173
|
+
<DropdownMenuLabel
|
|
174
|
+
data-slot="menubar-label"
|
|
175
|
+
data-inset={inset}
|
|
176
|
+
className={cn('px-2 py-1.5 text-xs text-muted-foreground data-inset:pl-7.5', className)}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function MenubarSeparator({
|
|
183
|
+
className,
|
|
184
|
+
...props
|
|
185
|
+
}: React.ComponentProps<typeof DropdownMenuSeparator>) {
|
|
186
|
+
return (
|
|
187
|
+
<DropdownMenuSeparator
|
|
188
|
+
data-slot="menubar-separator"
|
|
189
|
+
className={cn('-mx-1 my-1 h-px bg-border/50', className)}
|
|
190
|
+
{...props}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function MenubarShortcut({
|
|
196
|
+
className,
|
|
197
|
+
...props
|
|
198
|
+
}: React.ComponentProps<typeof DropdownMenuShortcut>) {
|
|
199
|
+
return (
|
|
200
|
+
<DropdownMenuShortcut
|
|
201
|
+
data-slot="menubar-shortcut"
|
|
202
|
+
className={cn(
|
|
203
|
+
'ml-auto text-[0.625rem] tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground',
|
|
204
|
+
className,
|
|
205
|
+
)}
|
|
206
|
+
{...props}
|
|
207
|
+
/>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function MenubarSub({ ...props }: React.ComponentProps<typeof DropdownMenuSub>) {
|
|
212
|
+
return <DropdownMenuSub data-slot="menubar-sub" {...props} />;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function MenubarSubTrigger({
|
|
216
|
+
className,
|
|
217
|
+
inset,
|
|
218
|
+
...props
|
|
219
|
+
}: React.ComponentProps<typeof DropdownMenuSubTrigger> & {
|
|
220
|
+
inset?: boolean;
|
|
221
|
+
}) {
|
|
222
|
+
return (
|
|
223
|
+
<DropdownMenuSubTrigger
|
|
224
|
+
data-slot="menubar-sub-trigger"
|
|
225
|
+
data-inset={inset}
|
|
226
|
+
className={cn(
|
|
227
|
+
"min-h-7 gap-2 rounded-md px-2 py-1 text-xs 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:not([class*='size-'])]:size-3.5",
|
|
228
|
+
className,
|
|
229
|
+
)}
|
|
230
|
+
{...props}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function MenubarSubContent({
|
|
236
|
+
className,
|
|
237
|
+
...props
|
|
238
|
+
}: React.ComponentProps<typeof DropdownMenuSubContent>) {
|
|
239
|
+
return (
|
|
240
|
+
<DropdownMenuSubContent
|
|
241
|
+
data-slot="menubar-sub-content"
|
|
242
|
+
className={cn(
|
|
243
|
+
'min-w-32 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 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',
|
|
244
|
+
className,
|
|
245
|
+
)}
|
|
246
|
+
{...props}
|
|
247
|
+
/>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export {
|
|
252
|
+
Menubar,
|
|
253
|
+
MenubarPortal,
|
|
254
|
+
MenubarMenu,
|
|
255
|
+
MenubarTrigger,
|
|
256
|
+
MenubarContent,
|
|
257
|
+
MenubarGroup,
|
|
258
|
+
MenubarSeparator,
|
|
259
|
+
MenubarLabel,
|
|
260
|
+
MenubarItem,
|
|
261
|
+
MenubarShortcut,
|
|
262
|
+
MenubarCheckboxItem,
|
|
263
|
+
MenubarRadioGroup,
|
|
264
|
+
MenubarRadioItem,
|
|
265
|
+
MenubarSub,
|
|
266
|
+
MenubarSubTrigger,
|
|
267
|
+
MenubarSubContent,
|
|
268
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { UnfoldMoreIcon } from '@hugeicons/core-free-icons';
|
|
4
|
+
import { HugeiconsIcon } from '@hugeicons/react';
|
|
5
|
+
import { cn } from '@saena-io/ui/lib/utils';
|
|
6
|
+
|
|
7
|
+
type NativeSelectProps = Omit<React.ComponentProps<'select'>, 'size'> & {
|
|
8
|
+
size?: 'sm' | 'default';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function NativeSelect({ className, size = 'default', ...props }: NativeSelectProps) {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
className={cn(
|
|
15
|
+
'group/native-select relative w-fit has-[select:disabled]:opacity-50',
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
data-slot="native-select-wrapper"
|
|
19
|
+
data-size={size}
|
|
20
|
+
>
|
|
21
|
+
<select
|
|
22
|
+
data-slot="native-select"
|
|
23
|
+
data-size={size}
|
|
24
|
+
className="h-7 w-full min-w-0 appearance-none rounded-md border border-input bg-input/20 py-0.5 pr-6 pl-2 text-xs/relaxed transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-[size=sm]:h-6 data-[size=sm]:text-[0.625rem] dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40"
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
<HugeiconsIcon
|
|
28
|
+
icon={UnfoldMoreIcon}
|
|
29
|
+
strokeWidth={2}
|
|
30
|
+
className="pointer-events-none absolute top-1/2 right-1.5 size-3.5 -translate-y-1/2 text-muted-foreground select-none group-data-[size=sm]/native-select:size-3 group-data-[size=sm]/native-select:-translate-y-[calc(--spacing(1.25))]"
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
data-slot="native-select-icon"
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function NativeSelectOption({ className, ...props }: React.ComponentProps<'option'>) {
|
|
39
|
+
return (
|
|
40
|
+
<option
|
|
41
|
+
data-slot="native-select-option"
|
|
42
|
+
className={cn('bg-[Canvas] text-[CanvasText]', className)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function NativeSelectOptGroup({ className, ...props }: React.ComponentProps<'optgroup'>) {
|
|
49
|
+
return (
|
|
50
|
+
<optgroup
|
|
51
|
+
data-slot="native-select-optgroup"
|
|
52
|
+
className={cn('bg-[Canvas] text-[CanvasText]', className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ArrowRight01Icon } from '@hugeicons/core-free-icons';
|
|
2
|
+
import { HugeiconsIcon } from '@hugeicons/react';
|
|
3
|
+
import {
|
|
4
|
+
Collapsible,
|
|
5
|
+
CollapsibleContent,
|
|
6
|
+
CollapsibleTrigger,
|
|
7
|
+
} from '@saena-io/ui/components/collapsible';
|
|
8
|
+
import {
|
|
9
|
+
SidebarGroup,
|
|
10
|
+
SidebarGroupLabel,
|
|
11
|
+
SidebarMenu,
|
|
12
|
+
SidebarMenuAction,
|
|
13
|
+
SidebarMenuButton,
|
|
14
|
+
SidebarMenuItem,
|
|
15
|
+
SidebarMenuSub,
|
|
16
|
+
SidebarMenuSubButton,
|
|
17
|
+
SidebarMenuSubItem,
|
|
18
|
+
} from '@saena-io/ui/components/sidebar';
|
|
19
|
+
|
|
20
|
+
export function NavMain({
|
|
21
|
+
items,
|
|
22
|
+
}: {
|
|
23
|
+
items: {
|
|
24
|
+
title: string;
|
|
25
|
+
url: string;
|
|
26
|
+
icon: React.ReactNode;
|
|
27
|
+
isActive?: boolean;
|
|
28
|
+
items?: {
|
|
29
|
+
title: string;
|
|
30
|
+
url: string;
|
|
31
|
+
}[];
|
|
32
|
+
}[];
|
|
33
|
+
}) {
|
|
34
|
+
return (
|
|
35
|
+
<SidebarGroup>
|
|
36
|
+
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
|
37
|
+
<SidebarMenu>
|
|
38
|
+
{items.map((item) => (
|
|
39
|
+
<Collapsible key={item.title} defaultOpen={item.isActive} render={<SidebarMenuItem />}>
|
|
40
|
+
<SidebarMenuButton tooltip={item.title} render={<a href={item.url} />}>
|
|
41
|
+
{item.icon}
|
|
42
|
+
<span>{item.title}</span>
|
|
43
|
+
</SidebarMenuButton>
|
|
44
|
+
{item.items?.length ? (
|
|
45
|
+
<>
|
|
46
|
+
<CollapsibleTrigger
|
|
47
|
+
render={<SidebarMenuAction className="aria-expanded:rotate-90" />}
|
|
48
|
+
>
|
|
49
|
+
<HugeiconsIcon icon={ArrowRight01Icon} strokeWidth={2} />
|
|
50
|
+
<span className="sr-only">Toggle</span>
|
|
51
|
+
</CollapsibleTrigger>
|
|
52
|
+
<CollapsibleContent>
|
|
53
|
+
<SidebarMenuSub>
|
|
54
|
+
{item.items?.map((subItem) => (
|
|
55
|
+
<SidebarMenuSubItem key={subItem.title}>
|
|
56
|
+
<SidebarMenuSubButton render={<a href={subItem.url} />}>
|
|
57
|
+
<span>{subItem.title}</span>
|
|
58
|
+
</SidebarMenuSubButton>
|
|
59
|
+
</SidebarMenuSubItem>
|
|
60
|
+
))}
|
|
61
|
+
</SidebarMenuSub>
|
|
62
|
+
</CollapsibleContent>
|
|
63
|
+
</>
|
|
64
|
+
) : null}
|
|
65
|
+
</Collapsible>
|
|
66
|
+
))}
|
|
67
|
+
</SidebarMenu>
|
|
68
|
+
</SidebarGroup>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Delete02Icon,
|
|
5
|
+
FolderIcon,
|
|
6
|
+
MoreHorizontalCircle01Icon,
|
|
7
|
+
Share03Icon,
|
|
8
|
+
} from '@hugeicons/core-free-icons';
|
|
9
|
+
import { HugeiconsIcon } from '@hugeicons/react';
|
|
10
|
+
import {
|
|
11
|
+
DropdownMenu,
|
|
12
|
+
DropdownMenuContent,
|
|
13
|
+
DropdownMenuItem,
|
|
14
|
+
DropdownMenuSeparator,
|
|
15
|
+
DropdownMenuTrigger,
|
|
16
|
+
} from '@saena-io/ui/components/dropdown-menu';
|
|
17
|
+
import {
|
|
18
|
+
SidebarGroup,
|
|
19
|
+
SidebarGroupLabel,
|
|
20
|
+
SidebarMenu,
|
|
21
|
+
SidebarMenuAction,
|
|
22
|
+
SidebarMenuButton,
|
|
23
|
+
SidebarMenuItem,
|
|
24
|
+
useSidebar,
|
|
25
|
+
} from '@saena-io/ui/components/sidebar';
|
|
26
|
+
|
|
27
|
+
export function NavProjects({
|
|
28
|
+
projects,
|
|
29
|
+
}: {
|
|
30
|
+
projects: {
|
|
31
|
+
name: string;
|
|
32
|
+
url: string;
|
|
33
|
+
icon: React.ReactNode;
|
|
34
|
+
}[];
|
|
35
|
+
}) {
|
|
36
|
+
const { isMobile } = useSidebar();
|
|
37
|
+
return (
|
|
38
|
+
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
|
|
39
|
+
<SidebarGroupLabel>Projects</SidebarGroupLabel>
|
|
40
|
+
<SidebarMenu>
|
|
41
|
+
{projects.map((item) => (
|
|
42
|
+
<SidebarMenuItem key={item.name}>
|
|
43
|
+
<SidebarMenuButton render={<a href={item.url} />}>
|
|
44
|
+
{item.icon}
|
|
45
|
+
<span>{item.name}</span>
|
|
46
|
+
</SidebarMenuButton>
|
|
47
|
+
<DropdownMenu>
|
|
48
|
+
<DropdownMenuTrigger
|
|
49
|
+
render={<SidebarMenuAction showOnHover className="aria-expanded:bg-muted" />}
|
|
50
|
+
>
|
|
51
|
+
<HugeiconsIcon icon={MoreHorizontalCircle01Icon} strokeWidth={2} />
|
|
52
|
+
<span className="sr-only">More</span>
|
|
53
|
+
</DropdownMenuTrigger>
|
|
54
|
+
<DropdownMenuContent
|
|
55
|
+
className="w-48"
|
|
56
|
+
side={isMobile ? 'bottom' : 'right'}
|
|
57
|
+
align={isMobile ? 'end' : 'start'}
|
|
58
|
+
>
|
|
59
|
+
<DropdownMenuItem>
|
|
60
|
+
<HugeiconsIcon
|
|
61
|
+
icon={FolderIcon}
|
|
62
|
+
strokeWidth={2}
|
|
63
|
+
className="text-muted-foreground"
|
|
64
|
+
/>
|
|
65
|
+
<span>View Project</span>
|
|
66
|
+
</DropdownMenuItem>
|
|
67
|
+
<DropdownMenuItem>
|
|
68
|
+
<HugeiconsIcon
|
|
69
|
+
icon={Share03Icon}
|
|
70
|
+
strokeWidth={2}
|
|
71
|
+
className="text-muted-foreground"
|
|
72
|
+
/>
|
|
73
|
+
<span>Share Project</span>
|
|
74
|
+
</DropdownMenuItem>
|
|
75
|
+
<DropdownMenuSeparator />
|
|
76
|
+
<DropdownMenuItem>
|
|
77
|
+
<HugeiconsIcon
|
|
78
|
+
icon={Delete02Icon}
|
|
79
|
+
strokeWidth={2}
|
|
80
|
+
className="text-muted-foreground"
|
|
81
|
+
/>
|
|
82
|
+
<span>Delete Project</span>
|
|
83
|
+
</DropdownMenuItem>
|
|
84
|
+
</DropdownMenuContent>
|
|
85
|
+
</DropdownMenu>
|
|
86
|
+
</SidebarMenuItem>
|
|
87
|
+
))}
|
|
88
|
+
<SidebarMenuItem>
|
|
89
|
+
<SidebarMenuButton>
|
|
90
|
+
<HugeiconsIcon icon={MoreHorizontalCircle01Icon} strokeWidth={2} />
|
|
91
|
+
<span>More</span>
|
|
92
|
+
</SidebarMenuButton>
|
|
93
|
+
</SidebarMenuItem>
|
|
94
|
+
</SidebarMenu>
|
|
95
|
+
</SidebarGroup>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
SidebarGroup,
|
|
5
|
+
SidebarGroupContent,
|
|
6
|
+
SidebarMenu,
|
|
7
|
+
SidebarMenuButton,
|
|
8
|
+
SidebarMenuItem,
|
|
9
|
+
} from '@saena-io/ui/components/sidebar';
|
|
10
|
+
|
|
11
|
+
export function NavSecondary({
|
|
12
|
+
items,
|
|
13
|
+
...props
|
|
14
|
+
}: {
|
|
15
|
+
items: {
|
|
16
|
+
title: string;
|
|
17
|
+
url: string;
|
|
18
|
+
icon: React.ReactNode;
|
|
19
|
+
}[];
|
|
20
|
+
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
|
|
21
|
+
return (
|
|
22
|
+
<SidebarGroup {...props}>
|
|
23
|
+
<SidebarGroupContent>
|
|
24
|
+
<SidebarMenu>
|
|
25
|
+
{items.map((item) => (
|
|
26
|
+
<SidebarMenuItem key={item.title}>
|
|
27
|
+
<SidebarMenuButton size="sm" render={<a href={item.url} />}>
|
|
28
|
+
{item.icon}
|
|
29
|
+
<span>{item.title}</span>
|
|
30
|
+
</SidebarMenuButton>
|
|
31
|
+
</SidebarMenuItem>
|
|
32
|
+
))}
|
|
33
|
+
</SidebarMenu>
|
|
34
|
+
</SidebarGroupContent>
|
|
35
|
+
</SidebarGroup>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CheckmarkBadgeIcon,
|
|
5
|
+
CreditCardIcon,
|
|
6
|
+
LogoutIcon,
|
|
7
|
+
NotificationIcon,
|
|
8
|
+
SparklesIcon,
|
|
9
|
+
UnfoldMoreIcon,
|
|
10
|
+
} from '@hugeicons/core-free-icons';
|
|
11
|
+
import { HugeiconsIcon } from '@hugeicons/react';
|
|
12
|
+
import { Avatar, AvatarFallback, AvatarImage } from '@saena-io/ui/components/avatar';
|
|
13
|
+
import {
|
|
14
|
+
DropdownMenu,
|
|
15
|
+
DropdownMenuContent,
|
|
16
|
+
DropdownMenuGroup,
|
|
17
|
+
DropdownMenuItem,
|
|
18
|
+
DropdownMenuLabel,
|
|
19
|
+
DropdownMenuSeparator,
|
|
20
|
+
DropdownMenuTrigger,
|
|
21
|
+
} from '@saena-io/ui/components/dropdown-menu';
|
|
22
|
+
import {
|
|
23
|
+
SidebarMenu,
|
|
24
|
+
SidebarMenuButton,
|
|
25
|
+
SidebarMenuItem,
|
|
26
|
+
useSidebar,
|
|
27
|
+
} from '@saena-io/ui/components/sidebar';
|
|
28
|
+
|
|
29
|
+
export function NavUser({
|
|
30
|
+
user,
|
|
31
|
+
}: {
|
|
32
|
+
user: {
|
|
33
|
+
name: string;
|
|
34
|
+
email: string;
|
|
35
|
+
avatar: string;
|
|
36
|
+
};
|
|
37
|
+
}) {
|
|
38
|
+
const { isMobile } = useSidebar();
|
|
39
|
+
return (
|
|
40
|
+
<SidebarMenu>
|
|
41
|
+
<SidebarMenuItem>
|
|
42
|
+
<DropdownMenu>
|
|
43
|
+
<DropdownMenuTrigger
|
|
44
|
+
render={<SidebarMenuButton size="lg" className="aria-expanded:bg-muted" />}
|
|
45
|
+
>
|
|
46
|
+
<Avatar>
|
|
47
|
+
<AvatarImage src={user.avatar} alt={user.name} />
|
|
48
|
+
<AvatarFallback>CN</AvatarFallback>
|
|
49
|
+
</Avatar>
|
|
50
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
51
|
+
<span className="truncate font-medium">{user.name}</span>
|
|
52
|
+
<span className="truncate text-xs">{user.email}</span>
|
|
53
|
+
</div>
|
|
54
|
+
<HugeiconsIcon icon={UnfoldMoreIcon} strokeWidth={2} className="ml-auto size-4" />
|
|
55
|
+
</DropdownMenuTrigger>
|
|
56
|
+
<DropdownMenuContent
|
|
57
|
+
className="min-w-56 rounded-lg"
|
|
58
|
+
side={isMobile ? 'bottom' : 'right'}
|
|
59
|
+
align="end"
|
|
60
|
+
sideOffset={4}
|
|
61
|
+
>
|
|
62
|
+
<DropdownMenuGroup>
|
|
63
|
+
<DropdownMenuLabel className="p-0 font-normal">
|
|
64
|
+
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
|
65
|
+
<Avatar>
|
|
66
|
+
<AvatarImage src={user.avatar} alt={user.name} />
|
|
67
|
+
<AvatarFallback>CN</AvatarFallback>
|
|
68
|
+
</Avatar>
|
|
69
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
70
|
+
<span className="truncate font-medium">{user.name}</span>
|
|
71
|
+
<span className="truncate text-xs">{user.email}</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</DropdownMenuLabel>
|
|
75
|
+
</DropdownMenuGroup>
|
|
76
|
+
<DropdownMenuSeparator />
|
|
77
|
+
<DropdownMenuGroup>
|
|
78
|
+
<DropdownMenuItem>
|
|
79
|
+
<HugeiconsIcon icon={SparklesIcon} strokeWidth={2} />
|
|
80
|
+
Upgrade to Pro
|
|
81
|
+
</DropdownMenuItem>
|
|
82
|
+
</DropdownMenuGroup>
|
|
83
|
+
<DropdownMenuSeparator />
|
|
84
|
+
<DropdownMenuGroup>
|
|
85
|
+
<DropdownMenuItem>
|
|
86
|
+
<HugeiconsIcon icon={CheckmarkBadgeIcon} strokeWidth={2} />
|
|
87
|
+
Account
|
|
88
|
+
</DropdownMenuItem>
|
|
89
|
+
<DropdownMenuItem>
|
|
90
|
+
<HugeiconsIcon icon={CreditCardIcon} strokeWidth={2} />
|
|
91
|
+
Billing
|
|
92
|
+
</DropdownMenuItem>
|
|
93
|
+
<DropdownMenuItem>
|
|
94
|
+
<HugeiconsIcon icon={NotificationIcon} strokeWidth={2} />
|
|
95
|
+
Notifications
|
|
96
|
+
</DropdownMenuItem>
|
|
97
|
+
</DropdownMenuGroup>
|
|
98
|
+
<DropdownMenuSeparator />
|
|
99
|
+
<DropdownMenuItem>
|
|
100
|
+
<HugeiconsIcon icon={LogoutIcon} strokeWidth={2} />
|
|
101
|
+
Log out
|
|
102
|
+
</DropdownMenuItem>
|
|
103
|
+
</DropdownMenuContent>
|
|
104
|
+
</DropdownMenu>
|
|
105
|
+
</SidebarMenuItem>
|
|
106
|
+
</SidebarMenu>
|
|
107
|
+
);
|
|
108
|
+
}
|