@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.
Files changed (147) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +120 -0
  4. package/components.json +21 -0
  5. package/package.json +108 -0
  6. package/scripts/audit-base-ui-props.mjs +179 -0
  7. package/scripts/audit-tailwind-typography.mjs +100 -0
  8. package/src/components/design-system/design-system-showcase.tsx +138 -0
  9. package/src/components/design-system/sections/showcase-data-display.tsx +454 -0
  10. package/src/components/design-system/sections/showcase-feedback.tsx +273 -0
  11. package/src/components/design-system/sections/showcase-forms.tsx +373 -0
  12. package/src/components/design-system/sections/showcase-foundations.tsx +384 -0
  13. package/src/components/design-system/sections/showcase-guidelines-recipes.tsx +397 -0
  14. package/src/components/design-system/sections/showcase-overlays-navigation.tsx +479 -0
  15. package/src/components/design-system/sections/showcase-overview.tsx +368 -0
  16. package/src/components/design-system/sections/showcase-primitives.tsx +463 -0
  17. package/src/components/shared/__tests__/multi-select-combobox.test.tsx +180 -0
  18. package/src/components/shared/__tests__/name-id-fields.test.ts +24 -0
  19. package/src/components/shared/button/add-new-button.tsx +19 -0
  20. package/src/components/shared/button/base/action-button.tsx +134 -0
  21. package/src/components/shared/button/base/link-button.tsx +130 -0
  22. package/src/components/shared/button/cancel-button.tsx +27 -0
  23. package/src/components/shared/button/confirm-button.tsx +26 -0
  24. package/src/components/shared/button/delete-button.tsx +36 -0
  25. package/src/components/shared/button/floating-save-button.tsx +89 -0
  26. package/src/components/shared/button/index.ts +13 -0
  27. package/src/components/shared/button/print-button.tsx +67 -0
  28. package/src/components/shared/button/refresh-button.tsx +63 -0
  29. package/src/components/shared/button/save-button.tsx +29 -0
  30. package/src/components/shared/button/submit-button.tsx +58 -0
  31. package/src/components/shared/client-redirect.tsx +11 -0
  32. package/src/components/shared/combobox.tsx +116 -0
  33. package/src/components/shared/data-list.tsx +65 -0
  34. package/src/components/shared/date/date-range-picker.tsx +106 -0
  35. package/src/components/shared/description-list.tsx +27 -0
  36. package/src/components/shared/empty-row.tsx +13 -0
  37. package/src/components/shared/empty-state.tsx +31 -0
  38. package/src/components/shared/env-snippet.tsx +62 -0
  39. package/src/components/shared/form/__tests__/simple-select.test.tsx +72 -0
  40. package/src/components/shared/form/form-field-boolean.tsx +93 -0
  41. package/src/components/shared/form/form-field-color-select.tsx +86 -0
  42. package/src/components/shared/form/form-field-combobox.tsx +167 -0
  43. package/src/components/shared/form/form-field-date.tsx +55 -0
  44. package/src/components/shared/form/form-field-formatted-number.tsx +103 -0
  45. package/src/components/shared/form/form-field-input.tsx +372 -0
  46. package/src/components/shared/form/form-field-radio-cards.tsx +127 -0
  47. package/src/components/shared/form/form-field-radio-group.tsx +56 -0
  48. package/src/components/shared/form/form-field-select.tsx +312 -0
  49. package/src/components/shared/form/simple-select.tsx +152 -0
  50. package/src/components/shared/form-dialog.tsx +109 -0
  51. package/src/components/shared/form-message.tsx +30 -0
  52. package/src/components/shared/initials.tsx +33 -0
  53. package/src/components/shared/layout/error-state.tsx +160 -0
  54. package/src/components/shared/layout/page-shell.tsx +57 -0
  55. package/src/components/shared/link/transition-link.tsx +72 -0
  56. package/src/components/shared/multi-select-combobox.tsx +392 -0
  57. package/src/components/shared/name-id-fields.tsx +133 -0
  58. package/src/components/shared/navigation/page-breadcrumb.tsx +100 -0
  59. package/src/components/shared/notice.tsx +35 -0
  60. package/src/components/shared/number/formatted-number.tsx +133 -0
  61. package/src/components/shared/page-header.tsx +25 -0
  62. package/src/components/shared/page-skeleton.tsx +18 -0
  63. package/src/components/shared/row-actions.tsx +93 -0
  64. package/src/components/shared/row-menu.tsx +55 -0
  65. package/src/components/shared/secret-reveal.tsx +53 -0
  66. package/src/components/shared/section-card.tsx +44 -0
  67. package/src/components/shared/stat.tsx +26 -0
  68. package/src/components/shared/status-badge.tsx +47 -0
  69. package/src/components/shared/text/primary-secondary-text.tsx +186 -0
  70. package/src/components/shared/theme/index.ts +4 -0
  71. package/src/components/shared/theme/theme-menu-items.tsx +43 -0
  72. package/src/components/shared/theme/theme-provider.tsx +39 -0
  73. package/src/components/shared/theme/theme-switch.tsx +36 -0
  74. package/src/components/shared/theme/theme-toggle.tsx +76 -0
  75. package/src/components/shared/time/client-date-formatter.tsx +188 -0
  76. package/src/components/shared/ui-text.tsx +47 -0
  77. package/src/components/ui/__tests__/combobox.test.tsx +40 -0
  78. package/src/components/ui/__tests__/date-picker.test.tsx +53 -0
  79. package/src/components/ui/__tests__/slider.test.tsx +18 -0
  80. package/src/components/ui/__tests__/tabs.test.tsx +33 -0
  81. package/src/components/ui/accordion.tsx +69 -0
  82. package/src/components/ui/alert-dialog.tsx +161 -0
  83. package/src/components/ui/alert.tsx +68 -0
  84. package/src/components/ui/aspect-ratio.tsx +22 -0
  85. package/src/components/ui/attachment.tsx +194 -0
  86. package/src/components/ui/avatar.tsx +92 -0
  87. package/src/components/ui/badge.tsx +48 -0
  88. package/src/components/ui/breadcrumb.tsx +102 -0
  89. package/src/components/ui/button-group.tsx +77 -0
  90. package/src/components/ui/button.tsx +57 -0
  91. package/src/components/ui/calendar.tsx +184 -0
  92. package/src/components/ui/card.tsx +87 -0
  93. package/src/components/ui/carousel.tsx +230 -0
  94. package/src/components/ui/chart.tsx +338 -0
  95. package/src/components/ui/checkbox.tsx +27 -0
  96. package/src/components/ui/collapsible.tsx +17 -0
  97. package/src/components/ui/combobox.tsx +272 -0
  98. package/src/components/ui/command.tsx +180 -0
  99. package/src/components/ui/context-menu.tsx +244 -0
  100. package/src/components/ui/date-picker.tsx +308 -0
  101. package/src/components/ui/dialog.tsx +137 -0
  102. package/src/components/ui/drawer.tsx +208 -0
  103. package/src/components/ui/dropdown-menu.tsx +257 -0
  104. package/src/components/ui/empty.tsx +93 -0
  105. package/src/components/ui/field.tsx +223 -0
  106. package/src/components/ui/form.tsx +161 -0
  107. package/src/components/ui/hover-card.tsx +46 -0
  108. package/src/components/ui/input-group.tsx +145 -0
  109. package/src/components/ui/input-otp.tsx +85 -0
  110. package/src/components/ui/input.tsx +19 -0
  111. package/src/components/ui/item.tsx +187 -0
  112. package/src/components/ui/kbd.tsx +26 -0
  113. package/src/components/ui/label.tsx +19 -0
  114. package/src/components/ui/menubar.tsx +266 -0
  115. package/src/components/ui/navigation-menu.tsx +160 -0
  116. package/src/components/ui/pagination.tsx +117 -0
  117. package/src/components/ui/popover.tsx +76 -0
  118. package/src/components/ui/progress.tsx +66 -0
  119. package/src/components/ui/radio-group.tsx +38 -0
  120. package/src/components/ui/relative-date.tsx +40 -0
  121. package/src/components/ui/resizable.tsx +41 -0
  122. package/src/components/ui/scroll-area.tsx +67 -0
  123. package/src/components/ui/searchable-select.tsx +124 -0
  124. package/src/components/ui/select.tsx +189 -0
  125. package/src/components/ui/separator.tsx +21 -0
  126. package/src/components/ui/sheet.tsx +124 -0
  127. package/src/components/ui/sidebar.tsx +691 -0
  128. package/src/components/ui/skeleton.tsx +13 -0
  129. package/src/components/ui/slider.tsx +55 -0
  130. package/src/components/ui/sonner.tsx +45 -0
  131. package/src/components/ui/spinner.tsx +16 -0
  132. package/src/components/ui/switch.tsx +32 -0
  133. package/src/components/ui/table.tsx +95 -0
  134. package/src/components/ui/tabs.tsx +79 -0
  135. package/src/components/ui/textarea.tsx +17 -0
  136. package/src/components/ui/toggle-group.tsx +86 -0
  137. package/src/components/ui/toggle.tsx +44 -0
  138. package/src/components/ui/tooltip.tsx +54 -0
  139. package/src/hooks/use-copy-to-clipboard.ts +27 -0
  140. package/src/hooks/use-mobile.ts +21 -0
  141. package/src/index.ts +156 -0
  142. package/src/lib/formatters.ts +44 -0
  143. package/src/lib/permission.ts +75 -0
  144. package/src/lib/status-colors.ts +52 -0
  145. package/src/lib/utils.ts +6 -0
  146. package/src/next.ts +15 -0
  147. 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 };