@vendure-io/ui 1.0.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/package.json +59 -0
- package/src/components/ui/accordion.tsx +74 -0
- package/src/components/ui/alert-dialog.tsx +187 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/components/ui/avatar.tsx +109 -0
- package/src/components/ui/badge.tsx +52 -0
- package/src/components/ui/breadcrumb.tsx +125 -0
- package/src/components/ui/button-group.tsx +87 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/calendar.tsx +221 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/carousel.tsx +242 -0
- package/src/components/ui/chart.tsx +356 -0
- package/src/components/ui/checkbox.tsx +29 -0
- package/src/components/ui/collapsible.tsx +21 -0
- package/src/components/ui/combobox.tsx +297 -0
- package/src/components/ui/command.tsx +196 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/dialog.tsx +157 -0
- package/src/components/ui/direction.tsx +6 -0
- package/src/components/ui/drawer.tsx +131 -0
- package/src/components/ui/dropdown-menu.tsx +271 -0
- package/src/components/ui/empty.tsx +101 -0
- package/src/components/ui/field.tsx +238 -0
- package/src/components/ui/hover-card.tsx +51 -0
- package/src/components/ui/input-group.tsx +158 -0
- package/src/components/ui/input-otp.tsx +87 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/item.tsx +201 -0
- package/src/components/ui/kbd.tsx +26 -0
- package/src/components/ui/label.tsx +20 -0
- package/src/components/ui/menubar.tsx +283 -0
- package/src/components/ui/native-select.tsx +52 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +130 -0
- package/src/components/ui/popover.tsx +90 -0
- package/src/components/ui/progress.tsx +83 -0
- package/src/components/ui/radio-group.tsx +38 -0
- package/src/components/ui/resizable.tsx +50 -0
- package/src/components/ui/scroll-area.tsx +55 -0
- package/src/components/ui/select.tsx +201 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +135 -0
- package/src/components/ui/sidebar.tsx +723 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +59 -0
- package/src/components/ui/sonner.tsx +49 -0
- package/src/components/ui/spinner.tsx +10 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +82 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +44 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/utils.ts +25 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils"
|
|
2
|
+
|
|
3
|
+
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
data-slot="skeleton"
|
|
7
|
+
className={cn("bg-muted animate-pulse rounded-md", className)}
|
|
8
|
+
{...props}
|
|
9
|
+
/>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { Skeleton }
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Slider as SliderPrimitive } from "@base-ui/react/slider"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
function Slider({
|
|
9
|
+
className,
|
|
10
|
+
defaultValue,
|
|
11
|
+
value,
|
|
12
|
+
min = 0,
|
|
13
|
+
max = 100,
|
|
14
|
+
...props
|
|
15
|
+
}: SliderPrimitive.Root.Props) {
|
|
16
|
+
const _values = React.useMemo(
|
|
17
|
+
() =>
|
|
18
|
+
Array.isArray(value)
|
|
19
|
+
? value
|
|
20
|
+
: Array.isArray(defaultValue)
|
|
21
|
+
? defaultValue
|
|
22
|
+
: [min, max],
|
|
23
|
+
[value, defaultValue, min, max]
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<SliderPrimitive.Root
|
|
28
|
+
className={cn("data-horizontal:w-full data-vertical:h-full", className)}
|
|
29
|
+
data-slot="slider"
|
|
30
|
+
defaultValue={defaultValue}
|
|
31
|
+
value={value}
|
|
32
|
+
min={min}
|
|
33
|
+
max={max}
|
|
34
|
+
thumbAlignment="edge"
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
|
|
38
|
+
<SliderPrimitive.Track
|
|
39
|
+
data-slot="slider-track"
|
|
40
|
+
className="bg-muted relative grow overflow-hidden rounded-full select-none data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5"
|
|
41
|
+
>
|
|
42
|
+
<SliderPrimitive.Indicator
|
|
43
|
+
data-slot="slider-range"
|
|
44
|
+
className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
|
|
45
|
+
/>
|
|
46
|
+
</SliderPrimitive.Track>
|
|
47
|
+
{Array.from({ length: _values.length }, (_, index) => (
|
|
48
|
+
<SliderPrimitive.Thumb
|
|
49
|
+
data-slot="slider-thumb"
|
|
50
|
+
key={index}
|
|
51
|
+
className="border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] select-none hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
|
|
52
|
+
/>
|
|
53
|
+
))}
|
|
54
|
+
</SliderPrimitive.Control>
|
|
55
|
+
</SliderPrimitive.Root>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Slider }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useTheme } from "next-themes"
|
|
4
|
+
import { Toaster as Sonner, type ToasterProps } from "sonner"
|
|
5
|
+
import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
8
|
+
const { theme = "system" } = useTheme()
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Sonner
|
|
12
|
+
theme={theme as ToasterProps["theme"]}
|
|
13
|
+
className="toaster group"
|
|
14
|
+
icons={{
|
|
15
|
+
success: (
|
|
16
|
+
<CircleCheckIcon className="size-4" />
|
|
17
|
+
),
|
|
18
|
+
info: (
|
|
19
|
+
<InfoIcon className="size-4" />
|
|
20
|
+
),
|
|
21
|
+
warning: (
|
|
22
|
+
<TriangleAlertIcon className="size-4" />
|
|
23
|
+
),
|
|
24
|
+
error: (
|
|
25
|
+
<OctagonXIcon className="size-4" />
|
|
26
|
+
),
|
|
27
|
+
loading: (
|
|
28
|
+
<Loader2Icon className="size-4 animate-spin" />
|
|
29
|
+
),
|
|
30
|
+
}}
|
|
31
|
+
style={
|
|
32
|
+
{
|
|
33
|
+
"--normal-bg": "var(--popover)",
|
|
34
|
+
"--normal-text": "var(--popover-foreground)",
|
|
35
|
+
"--normal-border": "var(--border)",
|
|
36
|
+
"--border-radius": "var(--radius)",
|
|
37
|
+
} as React.CSSProperties
|
|
38
|
+
}
|
|
39
|
+
toastOptions={{
|
|
40
|
+
classNames: {
|
|
41
|
+
toast: "cn-toast",
|
|
42
|
+
},
|
|
43
|
+
}}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { Toaster }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils"
|
|
2
|
+
import { Loader2Icon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
|
5
|
+
return (
|
|
6
|
+
<Loader2Icon role="status" aria-label="Loading" className={cn("size-4 animate-spin", className)} {...props} />
|
|
7
|
+
)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { Spinner }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Switch({
|
|
8
|
+
className,
|
|
9
|
+
size = "default",
|
|
10
|
+
...props
|
|
11
|
+
}: SwitchPrimitive.Root.Props & {
|
|
12
|
+
size?: "sm" | "default"
|
|
13
|
+
}) {
|
|
14
|
+
return (
|
|
15
|
+
<SwitchPrimitive.Root
|
|
16
|
+
data-slot="switch"
|
|
17
|
+
data-size={size}
|
|
18
|
+
className={cn(
|
|
19
|
+
"data-checked:bg-primary data-unchecked:bg-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 dark:data-unchecked:bg-input/80 peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-3 aria-invalid:ring-3 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px]",
|
|
20
|
+
className
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
<SwitchPrimitive.Thumb
|
|
25
|
+
data-slot="switch-thumb"
|
|
26
|
+
className="bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0"
|
|
27
|
+
/>
|
|
28
|
+
</SwitchPrimitive.Root>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { Switch }
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
data-slot="table-container"
|
|
11
|
+
className="relative w-full overflow-x-auto"
|
|
12
|
+
>
|
|
13
|
+
<table
|
|
14
|
+
data-slot="table"
|
|
15
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|
23
|
+
return (
|
|
24
|
+
<thead
|
|
25
|
+
data-slot="table-header"
|
|
26
|
+
className={cn("[&_tr]:border-b", className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
33
|
+
return (
|
|
34
|
+
<tbody
|
|
35
|
+
data-slot="table-body"
|
|
36
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
43
|
+
return (
|
|
44
|
+
<tfoot
|
|
45
|
+
data-slot="table-footer"
|
|
46
|
+
className={cn(
|
|
47
|
+
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
56
|
+
return (
|
|
57
|
+
<tr
|
|
58
|
+
data-slot="table-row"
|
|
59
|
+
className={cn(
|
|
60
|
+
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
69
|
+
return (
|
|
70
|
+
<th
|
|
71
|
+
data-slot="table-head"
|
|
72
|
+
className={cn(
|
|
73
|
+
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0",
|
|
74
|
+
className
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
82
|
+
return (
|
|
83
|
+
<td
|
|
84
|
+
data-slot="table-cell"
|
|
85
|
+
className={cn(
|
|
86
|
+
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function TableCaption({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}: React.ComponentProps<"caption">) {
|
|
98
|
+
return (
|
|
99
|
+
<caption
|
|
100
|
+
data-slot="table-caption"
|
|
101
|
+
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
Table,
|
|
109
|
+
TableHeader,
|
|
110
|
+
TableBody,
|
|
111
|
+
TableFooter,
|
|
112
|
+
TableHead,
|
|
113
|
+
TableRow,
|
|
114
|
+
TableCell,
|
|
115
|
+
TableCaption,
|
|
116
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
function Tabs({
|
|
9
|
+
className,
|
|
10
|
+
orientation = "horizontal",
|
|
11
|
+
...props
|
|
12
|
+
}: TabsPrimitive.Root.Props) {
|
|
13
|
+
return (
|
|
14
|
+
<TabsPrimitive.Root
|
|
15
|
+
data-slot="tabs"
|
|
16
|
+
data-orientation={orientation}
|
|
17
|
+
className={cn(
|
|
18
|
+
"group/tabs flex gap-2 data-horizontal:flex-col",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const tabsListVariants = cva(
|
|
27
|
+
"rounded-lg p-[3px] group-data-horizontal/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col",
|
|
28
|
+
{
|
|
29
|
+
variants: {
|
|
30
|
+
variant: {
|
|
31
|
+
default: "bg-muted",
|
|
32
|
+
line: "gap-1 bg-transparent",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
variant: "default",
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
function TabsList({
|
|
42
|
+
className,
|
|
43
|
+
variant = "default",
|
|
44
|
+
...props
|
|
45
|
+
}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
|
|
46
|
+
return (
|
|
47
|
+
<TabsPrimitive.List
|
|
48
|
+
data-slot="tabs-list"
|
|
49
|
+
data-variant={variant}
|
|
50
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
|
57
|
+
return (
|
|
58
|
+
<TabsPrimitive.Tab
|
|
59
|
+
data-slot="tabs-trigger"
|
|
60
|
+
className={cn(
|
|
61
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
62
|
+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
|
63
|
+
"data-active:bg-background dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground",
|
|
64
|
+
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
|
65
|
+
className
|
|
66
|
+
)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
|
|
73
|
+
return (
|
|
74
|
+
<TabsPrimitive.Panel
|
|
75
|
+
data-slot="tabs-content"
|
|
76
|
+
className={cn("flex-1 text-sm outline-none", className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
"border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Textarea }
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
|
|
5
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
|
|
6
|
+
import { type VariantProps } from "class-variance-authority"
|
|
7
|
+
|
|
8
|
+
import { cn } from "@/lib/utils"
|
|
9
|
+
import { toggleVariants } from "@/components/ui/toggle"
|
|
10
|
+
|
|
11
|
+
const ToggleGroupContext = React.createContext<
|
|
12
|
+
VariantProps<typeof toggleVariants> & {
|
|
13
|
+
spacing?: number
|
|
14
|
+
orientation?: "horizontal" | "vertical"
|
|
15
|
+
}
|
|
16
|
+
>({
|
|
17
|
+
size: "default",
|
|
18
|
+
variant: "default",
|
|
19
|
+
spacing: 0,
|
|
20
|
+
orientation: "horizontal",
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function ToggleGroup({
|
|
24
|
+
className,
|
|
25
|
+
variant,
|
|
26
|
+
size,
|
|
27
|
+
spacing = 0,
|
|
28
|
+
orientation = "horizontal",
|
|
29
|
+
children,
|
|
30
|
+
...props
|
|
31
|
+
}: ToggleGroupPrimitive.Props &
|
|
32
|
+
VariantProps<typeof toggleVariants> & {
|
|
33
|
+
spacing?: number
|
|
34
|
+
orientation?: "horizontal" | "vertical"
|
|
35
|
+
}) {
|
|
36
|
+
return (
|
|
37
|
+
<ToggleGroupPrimitive
|
|
38
|
+
data-slot="toggle-group"
|
|
39
|
+
data-variant={variant}
|
|
40
|
+
data-size={size}
|
|
41
|
+
data-spacing={spacing}
|
|
42
|
+
data-orientation={orientation}
|
|
43
|
+
style={{ "--gap": spacing } as React.CSSProperties}
|
|
44
|
+
className={cn(
|
|
45
|
+
"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-vertical:flex-col data-vertical:items-stretch data-[spacing=0]:data-[variant=outline]:shadow-xs",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
<ToggleGroupContext.Provider
|
|
51
|
+
value={{ variant, size, spacing, orientation }}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</ToggleGroupContext.Provider>
|
|
55
|
+
</ToggleGroupPrimitive>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ToggleGroupItem({
|
|
60
|
+
className,
|
|
61
|
+
children,
|
|
62
|
+
variant = "default",
|
|
63
|
+
size = "default",
|
|
64
|
+
...props
|
|
65
|
+
}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
|
|
66
|
+
const context = React.useContext(ToggleGroupContext)
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<TogglePrimitive
|
|
70
|
+
data-slot="toggle-group-item"
|
|
71
|
+
data-variant={context.variant || variant}
|
|
72
|
+
data-size={context.size || size}
|
|
73
|
+
data-spacing={context.spacing}
|
|
74
|
+
className={cn(
|
|
75
|
+
"data-[state=on]:bg-muted shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
|
|
76
|
+
toggleVariants({
|
|
77
|
+
variant: context.variant || variant,
|
|
78
|
+
size: context.size || size,
|
|
79
|
+
}),
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</TogglePrimitive>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
const toggleVariants = cva(
|
|
9
|
+
"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive gap-1 rounded-md text-sm font-medium transition-[color,box-shadow] [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-transparent",
|
|
14
|
+
outline: "border-input hover:bg-muted border bg-transparent shadow-xs",
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
default: "h-9 min-w-9 px-2",
|
|
18
|
+
sm: "h-8 min-w-8 px-1.5",
|
|
19
|
+
lg: "h-10 min-w-10 px-2.5",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
function Toggle({
|
|
30
|
+
className,
|
|
31
|
+
variant = "default",
|
|
32
|
+
size = "default",
|
|
33
|
+
...props
|
|
34
|
+
}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
|
|
35
|
+
return (
|
|
36
|
+
<TogglePrimitive
|
|
37
|
+
data-slot="toggle"
|
|
38
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function TooltipProvider({
|
|
8
|
+
delay = 0,
|
|
9
|
+
...props
|
|
10
|
+
}: TooltipPrimitive.Provider.Props) {
|
|
11
|
+
return (
|
|
12
|
+
<TooltipPrimitive.Provider
|
|
13
|
+
data-slot="tooltip-provider"
|
|
14
|
+
delay={delay}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
|
|
21
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
|
|
25
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function TooltipContent({
|
|
29
|
+
className,
|
|
30
|
+
side = "top",
|
|
31
|
+
sideOffset = 4,
|
|
32
|
+
align = "center",
|
|
33
|
+
alignOffset = 0,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: TooltipPrimitive.Popup.Props &
|
|
37
|
+
Pick<
|
|
38
|
+
TooltipPrimitive.Positioner.Props,
|
|
39
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
40
|
+
>) {
|
|
41
|
+
return (
|
|
42
|
+
<TooltipPrimitive.Portal>
|
|
43
|
+
<TooltipPrimitive.Positioner
|
|
44
|
+
align={align}
|
|
45
|
+
alignOffset={alignOffset}
|
|
46
|
+
side={side}
|
|
47
|
+
sideOffset={sideOffset}
|
|
48
|
+
className="isolate z-50"
|
|
49
|
+
>
|
|
50
|
+
<TooltipPrimitive.Popup
|
|
51
|
+
data-slot="tooltip-content"
|
|
52
|
+
className={cn(
|
|
53
|
+
"data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 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-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 bg-foreground text-background z-50 w-fit max-w-xs origin-(--transform-origin) rounded-md px-3 py-1.5 text-xs",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
|
|
60
|
+
</TooltipPrimitive.Popup>
|
|
61
|
+
</TooltipPrimitive.Positioner>
|
|
62
|
+
</TooltipPrimitive.Portal>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener("change", onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener("change", onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ClassValue, clsx } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]): string {
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function slugify(str: string): string {
|
|
9
|
+
return str
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.trim()
|
|
12
|
+
.replace(/[^\w\s-]/g, '')
|
|
13
|
+
.replace(/[\s_-]+/g, '-')
|
|
14
|
+
.replace(/^-+|-+$/g, '');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function hashString(str: string): number {
|
|
18
|
+
let hash = 0;
|
|
19
|
+
for (let i = 0; i < str.length; i++) {
|
|
20
|
+
const char = str.charCodeAt(i);
|
|
21
|
+
hash = (hash << 5) - hash + char;
|
|
22
|
+
hash |= 0;
|
|
23
|
+
}
|
|
24
|
+
return Math.abs(hash);
|
|
25
|
+
}
|