@suphark/ui 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.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/components.json +21 -0
- package/package.json +108 -0
- package/scripts/audit-base-ui-props.mjs +179 -0
- package/scripts/audit-tailwind-typography.mjs +100 -0
- package/src/components/design-system/design-system-showcase.tsx +138 -0
- package/src/components/design-system/sections/showcase-data-display.tsx +454 -0
- package/src/components/design-system/sections/showcase-feedback.tsx +273 -0
- package/src/components/design-system/sections/showcase-forms.tsx +373 -0
- package/src/components/design-system/sections/showcase-foundations.tsx +384 -0
- package/src/components/design-system/sections/showcase-guidelines-recipes.tsx +397 -0
- package/src/components/design-system/sections/showcase-overlays-navigation.tsx +479 -0
- package/src/components/design-system/sections/showcase-overview.tsx +368 -0
- package/src/components/design-system/sections/showcase-primitives.tsx +463 -0
- package/src/components/shared/__tests__/multi-select-combobox.test.tsx +180 -0
- package/src/components/shared/__tests__/name-id-fields.test.ts +24 -0
- package/src/components/shared/button/add-new-button.tsx +19 -0
- package/src/components/shared/button/base/action-button.tsx +134 -0
- package/src/components/shared/button/base/link-button.tsx +130 -0
- package/src/components/shared/button/cancel-button.tsx +27 -0
- package/src/components/shared/button/confirm-button.tsx +26 -0
- package/src/components/shared/button/delete-button.tsx +36 -0
- package/src/components/shared/button/floating-save-button.tsx +89 -0
- package/src/components/shared/button/index.ts +13 -0
- package/src/components/shared/button/print-button.tsx +67 -0
- package/src/components/shared/button/refresh-button.tsx +63 -0
- package/src/components/shared/button/save-button.tsx +29 -0
- package/src/components/shared/button/submit-button.tsx +58 -0
- package/src/components/shared/client-redirect.tsx +11 -0
- package/src/components/shared/combobox.tsx +116 -0
- package/src/components/shared/data-list.tsx +65 -0
- package/src/components/shared/date/date-range-picker.tsx +106 -0
- package/src/components/shared/description-list.tsx +27 -0
- package/src/components/shared/empty-row.tsx +13 -0
- package/src/components/shared/empty-state.tsx +31 -0
- package/src/components/shared/env-snippet.tsx +62 -0
- package/src/components/shared/form/__tests__/simple-select.test.tsx +72 -0
- package/src/components/shared/form/form-field-boolean.tsx +93 -0
- package/src/components/shared/form/form-field-color-select.tsx +86 -0
- package/src/components/shared/form/form-field-combobox.tsx +167 -0
- package/src/components/shared/form/form-field-date.tsx +55 -0
- package/src/components/shared/form/form-field-formatted-number.tsx +103 -0
- package/src/components/shared/form/form-field-input.tsx +372 -0
- package/src/components/shared/form/form-field-radio-cards.tsx +127 -0
- package/src/components/shared/form/form-field-radio-group.tsx +56 -0
- package/src/components/shared/form/form-field-select.tsx +312 -0
- package/src/components/shared/form/simple-select.tsx +152 -0
- package/src/components/shared/form-dialog.tsx +109 -0
- package/src/components/shared/form-message.tsx +30 -0
- package/src/components/shared/initials.tsx +33 -0
- package/src/components/shared/layout/error-state.tsx +160 -0
- package/src/components/shared/layout/page-shell.tsx +57 -0
- package/src/components/shared/link/transition-link.tsx +72 -0
- package/src/components/shared/multi-select-combobox.tsx +392 -0
- package/src/components/shared/name-id-fields.tsx +133 -0
- package/src/components/shared/navigation/page-breadcrumb.tsx +100 -0
- package/src/components/shared/notice.tsx +35 -0
- package/src/components/shared/number/formatted-number.tsx +133 -0
- package/src/components/shared/page-header.tsx +25 -0
- package/src/components/shared/page-skeleton.tsx +18 -0
- package/src/components/shared/row-actions.tsx +93 -0
- package/src/components/shared/row-menu.tsx +55 -0
- package/src/components/shared/secret-reveal.tsx +53 -0
- package/src/components/shared/section-card.tsx +44 -0
- package/src/components/shared/stat.tsx +26 -0
- package/src/components/shared/status-badge.tsx +47 -0
- package/src/components/shared/text/primary-secondary-text.tsx +186 -0
- package/src/components/shared/theme/index.ts +4 -0
- package/src/components/shared/theme/theme-menu-items.tsx +43 -0
- package/src/components/shared/theme/theme-provider.tsx +39 -0
- package/src/components/shared/theme/theme-switch.tsx +36 -0
- package/src/components/shared/theme/theme-toggle.tsx +76 -0
- package/src/components/shared/time/client-date-formatter.tsx +188 -0
- package/src/components/shared/ui-text.tsx +47 -0
- package/src/components/ui/__tests__/combobox.test.tsx +40 -0
- package/src/components/ui/__tests__/date-picker.test.tsx +53 -0
- package/src/components/ui/__tests__/slider.test.tsx +18 -0
- package/src/components/ui/__tests__/tabs.test.tsx +33 -0
- package/src/components/ui/accordion.tsx +69 -0
- package/src/components/ui/alert-dialog.tsx +161 -0
- package/src/components/ui/alert.tsx +68 -0
- package/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/components/ui/attachment.tsx +194 -0
- package/src/components/ui/avatar.tsx +92 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/breadcrumb.tsx +102 -0
- package/src/components/ui/button-group.tsx +77 -0
- package/src/components/ui/button.tsx +57 -0
- package/src/components/ui/calendar.tsx +184 -0
- package/src/components/ui/card.tsx +87 -0
- package/src/components/ui/carousel.tsx +230 -0
- package/src/components/ui/chart.tsx +338 -0
- package/src/components/ui/checkbox.tsx +27 -0
- package/src/components/ui/collapsible.tsx +17 -0
- package/src/components/ui/combobox.tsx +272 -0
- package/src/components/ui/command.tsx +180 -0
- package/src/components/ui/context-menu.tsx +244 -0
- package/src/components/ui/date-picker.tsx +308 -0
- package/src/components/ui/dialog.tsx +137 -0
- package/src/components/ui/drawer.tsx +208 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/empty.tsx +93 -0
- package/src/components/ui/field.tsx +223 -0
- package/src/components/ui/form.tsx +161 -0
- package/src/components/ui/hover-card.tsx +46 -0
- package/src/components/ui/input-group.tsx +145 -0
- package/src/components/ui/input-otp.tsx +85 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/item.tsx +187 -0
- package/src/components/ui/kbd.tsx +26 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/menubar.tsx +266 -0
- package/src/components/ui/navigation-menu.tsx +160 -0
- package/src/components/ui/pagination.tsx +117 -0
- package/src/components/ui/popover.tsx +76 -0
- package/src/components/ui/progress.tsx +66 -0
- package/src/components/ui/radio-group.tsx +38 -0
- package/src/components/ui/relative-date.tsx +40 -0
- package/src/components/ui/resizable.tsx +41 -0
- package/src/components/ui/scroll-area.tsx +67 -0
- package/src/components/ui/searchable-select.tsx +124 -0
- package/src/components/ui/select.tsx +189 -0
- package/src/components/ui/separator.tsx +21 -0
- package/src/components/ui/sheet.tsx +124 -0
- package/src/components/ui/sidebar.tsx +691 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +55 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +95 -0
- package/src/components/ui/tabs.tsx +79 -0
- package/src/components/ui/textarea.tsx +17 -0
- package/src/components/ui/toggle-group.tsx +86 -0
- package/src/components/ui/toggle.tsx +44 -0
- package/src/components/ui/tooltip.tsx +54 -0
- package/src/hooks/use-copy-to-clipboard.ts +27 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/index.ts +156 -0
- package/src/lib/formatters.ts +44 -0
- package/src/lib/permission.ts +75 -0
- package/src/lib/status-colors.ts +52 -0
- package/src/lib/utils.ts +6 -0
- package/src/next.ts +15 -0
- package/src/styles.css +214 -0
|
@@ -0,0 +1,266 @@
|
|
|
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 {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuGroup,
|
|
9
|
+
DropdownMenuItem,
|
|
10
|
+
DropdownMenuLabel,
|
|
11
|
+
DropdownMenuPortal,
|
|
12
|
+
DropdownMenuRadioGroup,
|
|
13
|
+
DropdownMenuSeparator,
|
|
14
|
+
DropdownMenuShortcut,
|
|
15
|
+
DropdownMenuSub,
|
|
16
|
+
DropdownMenuSubContent,
|
|
17
|
+
DropdownMenuSubTrigger,
|
|
18
|
+
DropdownMenuTrigger,
|
|
19
|
+
} from "@suphark/ui/components/ui/dropdown-menu";
|
|
20
|
+
import { cn } from "@suphark/ui/lib/utils";
|
|
21
|
+
import { CheckIcon } from "lucide-react";
|
|
22
|
+
import type * as React from "react";
|
|
23
|
+
|
|
24
|
+
function Menubar({ className, ...props }: MenubarPrimitive.Props) {
|
|
25
|
+
return (
|
|
26
|
+
<MenubarPrimitive
|
|
27
|
+
data-slot="menubar"
|
|
28
|
+
className={cn("flex h-8 items-center gap-0.5 rounded-lg border p-[3px]", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {
|
|
35
|
+
return <DropdownMenu data-slot="menubar-menu" {...props} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function MenubarGroup({ ...props }: React.ComponentProps<typeof DropdownMenuGroup>) {
|
|
39
|
+
return <DropdownMenuGroup data-slot="menubar-group" {...props} />;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function MenubarPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPortal>) {
|
|
43
|
+
return <DropdownMenuPortal data-slot="menubar-portal" {...props} />;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function MenubarTrigger({ className, ...props }: React.ComponentProps<typeof DropdownMenuTrigger>) {
|
|
47
|
+
return (
|
|
48
|
+
<DropdownMenuTrigger
|
|
49
|
+
data-slot="menubar-trigger"
|
|
50
|
+
className={cn(
|
|
51
|
+
"flex select-none items-center rounded-sm px-1.5 py-[2px] font-medium text-sm outline-hidden hover:bg-muted aria-expanded:bg-muted",
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function MenubarContent({
|
|
60
|
+
className,
|
|
61
|
+
align = "start",
|
|
62
|
+
alignOffset = -4,
|
|
63
|
+
sideOffset = 8,
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
66
|
+
return (
|
|
67
|
+
<DropdownMenuContent
|
|
68
|
+
data-slot="menubar-content"
|
|
69
|
+
align={align}
|
|
70
|
+
alignOffset={alignOffset}
|
|
71
|
+
sideOffset={sideOffset}
|
|
72
|
+
className={cn(
|
|
73
|
+
"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:fade-in-0 data-open:zoom-in-95 min-w-36 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-open:animate-in",
|
|
74
|
+
className,
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function MenubarItem({
|
|
82
|
+
className,
|
|
83
|
+
inset,
|
|
84
|
+
variant = "default",
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<typeof DropdownMenuItem>) {
|
|
87
|
+
return (
|
|
88
|
+
<DropdownMenuItem
|
|
89
|
+
data-slot="menubar-item"
|
|
90
|
+
data-inset={inset}
|
|
91
|
+
data-variant={variant}
|
|
92
|
+
className={cn(
|
|
93
|
+
"group/menubar-item gap-1.5 rounded-md px-1.5 py-1 text-sm focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-disabled:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive!",
|
|
94
|
+
className,
|
|
95
|
+
)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function MenubarCheckboxItem({
|
|
102
|
+
className,
|
|
103
|
+
children,
|
|
104
|
+
checked,
|
|
105
|
+
inset,
|
|
106
|
+
...props
|
|
107
|
+
}: MenuPrimitive.CheckboxItem.Props & {
|
|
108
|
+
inset?: boolean;
|
|
109
|
+
}) {
|
|
110
|
+
return (
|
|
111
|
+
<MenuPrimitive.CheckboxItem
|
|
112
|
+
data-slot="menubar-checkbox-item"
|
|
113
|
+
data-inset={inset}
|
|
114
|
+
className={cn(
|
|
115
|
+
"relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-disabled:pointer-events-none data-inset:pl-7 data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
116
|
+
className,
|
|
117
|
+
)}
|
|
118
|
+
checked={checked}
|
|
119
|
+
{...props}
|
|
120
|
+
>
|
|
121
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
122
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
123
|
+
<CheckIcon />
|
|
124
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
125
|
+
</span>
|
|
126
|
+
{children}
|
|
127
|
+
</MenuPrimitive.CheckboxItem>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function MenubarRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuRadioGroup>) {
|
|
132
|
+
return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function MenubarRadioItem({
|
|
136
|
+
className,
|
|
137
|
+
children,
|
|
138
|
+
inset,
|
|
139
|
+
...props
|
|
140
|
+
}: MenuPrimitive.RadioItem.Props & {
|
|
141
|
+
inset?: boolean;
|
|
142
|
+
}) {
|
|
143
|
+
return (
|
|
144
|
+
<MenuPrimitive.RadioItem
|
|
145
|
+
data-slot="menubar-radio-item"
|
|
146
|
+
data-inset={inset}
|
|
147
|
+
className={cn(
|
|
148
|
+
"relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-disabled:pointer-events-none data-inset:pl-7 data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
149
|
+
className,
|
|
150
|
+
)}
|
|
151
|
+
{...props}
|
|
152
|
+
>
|
|
153
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
154
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
155
|
+
<CheckIcon />
|
|
156
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
157
|
+
</span>
|
|
158
|
+
{children}
|
|
159
|
+
</MenuPrimitive.RadioItem>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function MenubarLabel({
|
|
164
|
+
className,
|
|
165
|
+
inset,
|
|
166
|
+
...props
|
|
167
|
+
}: React.ComponentProps<typeof DropdownMenuLabel> & {
|
|
168
|
+
inset?: boolean;
|
|
169
|
+
}) {
|
|
170
|
+
return (
|
|
171
|
+
<DropdownMenuLabel
|
|
172
|
+
data-slot="menubar-label"
|
|
173
|
+
data-inset={inset}
|
|
174
|
+
className={cn("px-1.5 py-1 font-medium text-sm data-inset:pl-7", className)}
|
|
175
|
+
{...props}
|
|
176
|
+
/>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function MenubarSeparator({
|
|
181
|
+
className,
|
|
182
|
+
...props
|
|
183
|
+
}: React.ComponentProps<typeof DropdownMenuSeparator>) {
|
|
184
|
+
return (
|
|
185
|
+
<DropdownMenuSeparator
|
|
186
|
+
data-slot="menubar-separator"
|
|
187
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
188
|
+
{...props}
|
|
189
|
+
/>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function MenubarShortcut({
|
|
194
|
+
className,
|
|
195
|
+
...props
|
|
196
|
+
}: React.ComponentProps<typeof DropdownMenuShortcut>) {
|
|
197
|
+
return (
|
|
198
|
+
<DropdownMenuShortcut
|
|
199
|
+
data-slot="menubar-shortcut"
|
|
200
|
+
className={cn(
|
|
201
|
+
"ml-auto text-muted-foreground text-xs tracking-widest group-focus/menubar-item:text-accent-foreground",
|
|
202
|
+
className,
|
|
203
|
+
)}
|
|
204
|
+
{...props}
|
|
205
|
+
/>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function MenubarSub({ ...props }: React.ComponentProps<typeof DropdownMenuSub>) {
|
|
210
|
+
return <DropdownMenuSub data-slot="menubar-sub" {...props} />;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function MenubarSubTrigger({
|
|
214
|
+
className,
|
|
215
|
+
inset,
|
|
216
|
+
...props
|
|
217
|
+
}: React.ComponentProps<typeof DropdownMenuSubTrigger> & {
|
|
218
|
+
inset?: boolean;
|
|
219
|
+
}) {
|
|
220
|
+
return (
|
|
221
|
+
<DropdownMenuSubTrigger
|
|
222
|
+
data-slot="menubar-sub-trigger"
|
|
223
|
+
data-inset={inset}
|
|
224
|
+
className={cn(
|
|
225
|
+
"gap-1.5 rounded-md px-1.5 py-1 text-sm focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-inset:pl-7 data-open:text-accent-foreground [&_svg:not([class*='size-'])]:size-4",
|
|
226
|
+
className,
|
|
227
|
+
)}
|
|
228
|
+
{...props}
|
|
229
|
+
/>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function MenubarSubContent({
|
|
234
|
+
className,
|
|
235
|
+
...props
|
|
236
|
+
}: React.ComponentProps<typeof DropdownMenuSubContent>) {
|
|
237
|
+
return (
|
|
238
|
+
<DropdownMenuSubContent
|
|
239
|
+
data-slot="menubar-sub-content"
|
|
240
|
+
className={cn(
|
|
241
|
+
"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:fade-in-0 data-open:zoom-in-95 data-closed:fade-out-0 data-closed:zoom-out-95 min-w-32 rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-open:animate-in",
|
|
242
|
+
className,
|
|
243
|
+
)}
|
|
244
|
+
{...props}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export {
|
|
250
|
+
Menubar,
|
|
251
|
+
MenubarCheckboxItem,
|
|
252
|
+
MenubarContent,
|
|
253
|
+
MenubarGroup,
|
|
254
|
+
MenubarItem,
|
|
255
|
+
MenubarLabel,
|
|
256
|
+
MenubarMenu,
|
|
257
|
+
MenubarPortal,
|
|
258
|
+
MenubarRadioGroup,
|
|
259
|
+
MenubarRadioItem,
|
|
260
|
+
MenubarSeparator,
|
|
261
|
+
MenubarShortcut,
|
|
262
|
+
MenubarSub,
|
|
263
|
+
MenubarSubContent,
|
|
264
|
+
MenubarSubTrigger,
|
|
265
|
+
MenubarTrigger,
|
|
266
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu";
|
|
2
|
+
import { cn } from "@suphark/ui/lib/utils";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
function NavigationMenu({
|
|
7
|
+
align = "start",
|
|
8
|
+
className,
|
|
9
|
+
children,
|
|
10
|
+
...props
|
|
11
|
+
}: NavigationMenuPrimitive.Root.Props & Pick<NavigationMenuPrimitive.Positioner.Props, "align">) {
|
|
12
|
+
return (
|
|
13
|
+
<NavigationMenuPrimitive.Root
|
|
14
|
+
data-slot="navigation-menu"
|
|
15
|
+
className={cn(
|
|
16
|
+
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
<NavigationMenuPositioner align={align} />
|
|
23
|
+
</NavigationMenuPrimitive.Root>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function NavigationMenuList({
|
|
28
|
+
className,
|
|
29
|
+
...props
|
|
30
|
+
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {
|
|
31
|
+
return (
|
|
32
|
+
<NavigationMenuPrimitive.List
|
|
33
|
+
data-slot="navigation-menu-list"
|
|
34
|
+
className={cn("group flex flex-1 list-none items-center justify-center gap-0", className)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function NavigationMenuItem({
|
|
41
|
+
className,
|
|
42
|
+
...props
|
|
43
|
+
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item>) {
|
|
44
|
+
return (
|
|
45
|
+
<NavigationMenuPrimitive.Item
|
|
46
|
+
data-slot="navigation-menu-item"
|
|
47
|
+
className={cn("relative", className)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const navigationMenuTriggerStyle = cva(
|
|
54
|
+
"group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-lg px-2.5 py-1.5 font-medium text-sm outline-none transition-all hover:bg-muted focus:bg-muted focus-visible:outline-1 focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-open:bg-muted/50 data-popup-open:bg-muted/50 data-open:focus:bg-muted data-open:hover:bg-muted data-popup-open:hover:bg-muted",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
function NavigationMenuTrigger({
|
|
58
|
+
className,
|
|
59
|
+
children,
|
|
60
|
+
...props
|
|
61
|
+
}: NavigationMenuPrimitive.Trigger.Props) {
|
|
62
|
+
return (
|
|
63
|
+
<NavigationMenuPrimitive.Trigger
|
|
64
|
+
data-slot="navigation-menu-trigger"
|
|
65
|
+
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{children}{" "}
|
|
69
|
+
<ChevronDownIcon
|
|
70
|
+
className="relative top-px ml-1 size-3 transition duration-300 group-data-open/navigation-menu-trigger:rotate-180 group-data-popup-open/navigation-menu-trigger:rotate-180"
|
|
71
|
+
aria-hidden="true"
|
|
72
|
+
/>
|
|
73
|
+
</NavigationMenuPrimitive.Trigger>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function NavigationMenuContent({ className, ...props }: NavigationMenuPrimitive.Content.Props) {
|
|
78
|
+
return (
|
|
79
|
+
<NavigationMenuPrimitive.Content
|
|
80
|
+
data-slot="navigation-menu-content"
|
|
81
|
+
className={cn(
|
|
82
|
+
"data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95 h-full w-auto p-1 transition-[opacity,transform,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-ending-style:opacity-0 data-starting-style:opacity-0 **:data-[slot=navigation-menu-link]:focus:outline-none **:data-[slot=navigation-menu-link]:focus:ring-0 group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-open:animate-in",
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function NavigationMenuPositioner({
|
|
91
|
+
className,
|
|
92
|
+
side = "bottom",
|
|
93
|
+
sideOffset = 8,
|
|
94
|
+
align = "start",
|
|
95
|
+
alignOffset = 0,
|
|
96
|
+
...props
|
|
97
|
+
}: NavigationMenuPrimitive.Positioner.Props) {
|
|
98
|
+
return (
|
|
99
|
+
<NavigationMenuPrimitive.Portal>
|
|
100
|
+
<NavigationMenuPrimitive.Positioner
|
|
101
|
+
side={side}
|
|
102
|
+
sideOffset={sideOffset}
|
|
103
|
+
align={align}
|
|
104
|
+
alignOffset={alignOffset}
|
|
105
|
+
className={cn(
|
|
106
|
+
"isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0",
|
|
107
|
+
className,
|
|
108
|
+
)}
|
|
109
|
+
{...props}
|
|
110
|
+
>
|
|
111
|
+
<NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] relative h-(--popup-height) w-(--popup-width) xs:w-(--popup-width) origin-(--transform-origin) rounded-lg bg-popover text-popover-foreground shadow outline-none ring-1 ring-foreground/10 transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-ending-style:scale-90 data-starting-style:scale-90 data-ending-style:opacity-0 data-starting-style:opacity-0 data-ending-style:duration-150">
|
|
112
|
+
<NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
|
|
113
|
+
</NavigationMenuPrimitive.Popup>
|
|
114
|
+
</NavigationMenuPrimitive.Positioner>
|
|
115
|
+
</NavigationMenuPrimitive.Portal>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function NavigationMenuLink({ className, ...props }: NavigationMenuPrimitive.Link.Props) {
|
|
120
|
+
return (
|
|
121
|
+
<NavigationMenuPrimitive.Link
|
|
122
|
+
data-slot="navigation-menu-link"
|
|
123
|
+
className={cn(
|
|
124
|
+
"flex items-center gap-2 in-data-[slot=navigation-menu-content]:rounded-md rounded-lg p-2 text-sm outline-none transition-all hover:bg-muted focus:bg-muted focus-visible:outline-1 focus-visible:ring-3 focus-visible:ring-ring/50 data-active:bg-muted/50 data-active:focus:bg-muted data-active:hover:bg-muted [&_svg:not([class*='size-'])]:size-4",
|
|
125
|
+
className,
|
|
126
|
+
)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function NavigationMenuIndicator({
|
|
133
|
+
className,
|
|
134
|
+
...props
|
|
135
|
+
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) {
|
|
136
|
+
return (
|
|
137
|
+
<NavigationMenuPrimitive.Icon
|
|
138
|
+
data-slot="navigation-menu-indicator"
|
|
139
|
+
className={cn(
|
|
140
|
+
"data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=visible]:animate-in",
|
|
141
|
+
className,
|
|
142
|
+
)}
|
|
143
|
+
{...props}
|
|
144
|
+
>
|
|
145
|
+
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
|
146
|
+
</NavigationMenuPrimitive.Icon>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
NavigationMenu,
|
|
152
|
+
NavigationMenuContent,
|
|
153
|
+
NavigationMenuIndicator,
|
|
154
|
+
NavigationMenuItem,
|
|
155
|
+
NavigationMenuLink,
|
|
156
|
+
NavigationMenuList,
|
|
157
|
+
NavigationMenuPositioner,
|
|
158
|
+
NavigationMenuTrigger,
|
|
159
|
+
navigationMenuTriggerStyle,
|
|
160
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Button } from "@suphark/ui/components/ui/button";
|
|
2
|
+
import { cn } from "@suphark/ui/lib/utils";
|
|
3
|
+
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
|
|
6
|
+
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
|
|
7
|
+
return (
|
|
8
|
+
<nav
|
|
9
|
+
role="navigation"
|
|
10
|
+
aria-label="pagination"
|
|
11
|
+
data-slot="pagination"
|
|
12
|
+
className={cn("mx-auto flex w-full justify-center", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function PaginationContent({ className, ...props }: React.ComponentProps<"ul">) {
|
|
19
|
+
return (
|
|
20
|
+
<ul
|
|
21
|
+
data-slot="pagination-content"
|
|
22
|
+
className={cn("flex items-center gap-0.5", className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function PaginationItem({ ...props }: React.ComponentProps<"li">) {
|
|
29
|
+
return <li data-slot="pagination-item" {...props} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type PaginationLinkProps = {
|
|
33
|
+
isActive?: boolean;
|
|
34
|
+
} & Pick<React.ComponentProps<typeof Button>, "size"> &
|
|
35
|
+
React.ComponentProps<"a">;
|
|
36
|
+
|
|
37
|
+
function PaginationLink({ className, isActive, size = "icon", ...props }: PaginationLinkProps) {
|
|
38
|
+
return (
|
|
39
|
+
<Button
|
|
40
|
+
variant={isActive ? "outline" : "ghost"}
|
|
41
|
+
size={size}
|
|
42
|
+
className={cn(className)}
|
|
43
|
+
nativeButton={false}
|
|
44
|
+
render={
|
|
45
|
+
<a
|
|
46
|
+
aria-current={isActive ? "page" : undefined}
|
|
47
|
+
data-slot="pagination-link"
|
|
48
|
+
data-active={isActive}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function PaginationPrevious({
|
|
57
|
+
className,
|
|
58
|
+
text = "Previous",
|
|
59
|
+
...props
|
|
60
|
+
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
|
|
61
|
+
return (
|
|
62
|
+
<PaginationLink
|
|
63
|
+
aria-label="Go to previous page"
|
|
64
|
+
size="default"
|
|
65
|
+
className={cn("pl-1.5!", className)}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
<ChevronLeftIcon data-icon="inline-start" />
|
|
69
|
+
<span className="hidden sm:block">{text}</span>
|
|
70
|
+
</PaginationLink>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function PaginationNext({
|
|
75
|
+
className,
|
|
76
|
+
text = "Next",
|
|
77
|
+
...props
|
|
78
|
+
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
|
|
79
|
+
return (
|
|
80
|
+
<PaginationLink
|
|
81
|
+
aria-label="Go to next page"
|
|
82
|
+
size="default"
|
|
83
|
+
className={cn("pr-1.5!", className)}
|
|
84
|
+
{...props}
|
|
85
|
+
>
|
|
86
|
+
<span className="hidden sm:block">{text}</span>
|
|
87
|
+
<ChevronRightIcon data-icon="inline-end" />
|
|
88
|
+
</PaginationLink>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">) {
|
|
93
|
+
return (
|
|
94
|
+
<span
|
|
95
|
+
aria-hidden
|
|
96
|
+
data-slot="pagination-ellipsis"
|
|
97
|
+
className={cn(
|
|
98
|
+
"flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
|
|
99
|
+
className,
|
|
100
|
+
)}
|
|
101
|
+
{...props}
|
|
102
|
+
>
|
|
103
|
+
<MoreHorizontalIcon />
|
|
104
|
+
<span className="sr-only">More pages</span>
|
|
105
|
+
</span>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
Pagination,
|
|
111
|
+
PaginationContent,
|
|
112
|
+
PaginationEllipsis,
|
|
113
|
+
PaginationItem,
|
|
114
|
+
PaginationLink,
|
|
115
|
+
PaginationNext,
|
|
116
|
+
PaginationPrevious,
|
|
117
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
|
|
4
|
+
import { cn } from "@suphark/ui/lib/utils";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
|
|
7
|
+
function Popover({ ...props }: PopoverPrimitive.Root.Props) {
|
|
8
|
+
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {
|
|
12
|
+
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function PopoverContent({
|
|
16
|
+
className,
|
|
17
|
+
align = "center",
|
|
18
|
+
alignOffset = 0,
|
|
19
|
+
side = "bottom",
|
|
20
|
+
sideOffset = 4,
|
|
21
|
+
...props
|
|
22
|
+
}: PopoverPrimitive.Popup.Props &
|
|
23
|
+
Pick<PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) {
|
|
24
|
+
return (
|
|
25
|
+
<PopoverPrimitive.Portal>
|
|
26
|
+
<PopoverPrimitive.Positioner
|
|
27
|
+
align={align}
|
|
28
|
+
alignOffset={alignOffset}
|
|
29
|
+
side={side}
|
|
30
|
+
sideOffset={sideOffset}
|
|
31
|
+
className="isolate z-50"
|
|
32
|
+
>
|
|
33
|
+
<PopoverPrimitive.Popup
|
|
34
|
+
data-slot="popover-content"
|
|
35
|
+
className={cn(
|
|
36
|
+
"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:fade-in-0 data-open:zoom-in-95 data-closed:fade-out-0 data-closed:zoom-out-95 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-popover-foreground text-sm shadow-md outline-hidden ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-open:animate-in",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
</PopoverPrimitive.Positioner>
|
|
42
|
+
</PopoverPrimitive.Portal>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
data-slot="popover-header"
|
|
50
|
+
className={cn("flex flex-col gap-0.5 text-sm", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
|
|
57
|
+
return (
|
|
58
|
+
<PopoverPrimitive.Title
|
|
59
|
+
data-slot="popover-title"
|
|
60
|
+
className={cn("font-medium", className)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function PopoverDescription({ className, ...props }: PopoverPrimitive.Description.Props) {
|
|
67
|
+
return (
|
|
68
|
+
<PopoverPrimitive.Description
|
|
69
|
+
data-slot="popover-description"
|
|
70
|
+
className={cn("text-muted-foreground", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Progress as ProgressPrimitive } from "@base-ui/react/progress";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@suphark/ui/lib/utils";
|
|
6
|
+
|
|
7
|
+
function Progress({ className, children, value, ...props }: ProgressPrimitive.Root.Props) {
|
|
8
|
+
return (
|
|
9
|
+
<ProgressPrimitive.Root
|
|
10
|
+
value={value}
|
|
11
|
+
data-slot="progress"
|
|
12
|
+
className={cn("flex flex-wrap gap-3", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
<ProgressTrack>
|
|
17
|
+
<ProgressIndicator />
|
|
18
|
+
</ProgressTrack>
|
|
19
|
+
</ProgressPrimitive.Root>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
|
|
24
|
+
return (
|
|
25
|
+
<ProgressPrimitive.Track
|
|
26
|
+
className={cn(
|
|
27
|
+
"relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-muted",
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
data-slot="progress-track"
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ProgressIndicator({ className, ...props }: ProgressPrimitive.Indicator.Props) {
|
|
37
|
+
return (
|
|
38
|
+
<ProgressPrimitive.Indicator
|
|
39
|
+
data-slot="progress-indicator"
|
|
40
|
+
className={cn("h-full bg-primary transition-all", className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
|
|
47
|
+
return (
|
|
48
|
+
<ProgressPrimitive.Label
|
|
49
|
+
className={cn("font-medium text-sm", className)}
|
|
50
|
+
data-slot="progress-label"
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
|
|
57
|
+
return (
|
|
58
|
+
<ProgressPrimitive.Value
|
|
59
|
+
className={cn("ml-auto text-muted-foreground text-sm tabular-nums", className)}
|
|
60
|
+
data-slot="progress-value"
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue };
|