@traky-app/ui 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -19
- package/package.json +50 -3
- package/registry/traky/json/command.json +1 -1
- package/registry/traky/json/icon.json +17 -0
- package/registry/traky/json/logo.json +17 -0
- package/registry/traky/registry.json +20 -0
- package/registry/traky/ui/command.tsx +2 -0
- package/registry/traky/ui/icon.tsx +71 -0
- package/registry/traky/ui/logo.tsx +77 -0
- package/src/hooks/use-document-theme.ts +25 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/disable-input-autofill.ts +23 -0
- package/src/lib/sidebar-persistence.ts +76 -0
- package/src/lib/utils.ts +30 -0
- package/src/ui/accordion.tsx +65 -0
- package/src/ui/alert-dialog.tsx +163 -0
- package/src/ui/alert.tsx +66 -0
- package/src/ui/animated-beam.tsx +185 -0
- package/src/ui/animated-list.tsx +71 -0
- package/src/ui/avatar.tsx +53 -0
- package/src/ui/badge.tsx +46 -0
- package/src/ui/bento-grid.tsx +109 -0
- package/src/ui/breadcrumb.tsx +108 -0
- package/src/ui/bubble.tsx +125 -0
- package/src/ui/button-group.tsx +83 -0
- package/src/ui/button.tsx +64 -0
- package/src/ui/calendar.tsx +213 -0
- package/src/ui/card.tsx +92 -0
- package/src/ui/carousel.tsx +240 -0
- package/src/ui/chart.tsx +380 -0
- package/src/ui/checkbox.tsx +31 -0
- package/src/ui/checkpoint.tsx +94 -0
- package/src/ui/code-block.tsx +232 -0
- package/src/ui/collapsible.tsx +33 -0
- package/src/ui/command.tsx +192 -0
- package/src/ui/context-menu.tsx +251 -0
- package/src/ui/dialog.tsx +142 -0
- package/src/ui/drawer.tsx +135 -0
- package/src/ui/dropdown-menu.tsx +256 -0
- package/src/ui/empty.tsx +104 -0
- package/src/ui/field.tsx +248 -0
- package/src/ui/globe.tsx +234 -0
- package/src/ui/hover-card.tsx +44 -0
- package/src/ui/icon.tsx +71 -0
- package/src/ui/input-group.tsx +170 -0
- package/src/ui/input-otp.tsx +77 -0
- package/src/ui/input.tsx +24 -0
- package/src/ui/item.tsx +193 -0
- package/src/ui/kbd.tsx +28 -0
- package/src/ui/label.tsx +24 -0
- package/src/ui/logo.tsx +77 -0
- package/src/ui/map.tsx +2226 -0
- package/src/ui/marker.tsx +69 -0
- package/src/ui/marquee.tsx +74 -0
- package/src/ui/message.tsx +92 -0
- package/src/ui/navigation-menu.tsx +169 -0
- package/src/ui/number-ticker.tsx +90 -0
- package/src/ui/pagination.tsx +122 -0
- package/src/ui/popover.tsx +48 -0
- package/src/ui/progress.tsx +31 -0
- package/src/ui/select.tsx +193 -0
- package/src/ui/separator.tsx +28 -0
- package/src/ui/sheet.tsx +143 -0
- package/src/ui/sidebar.tsx +764 -0
- package/src/ui/skeleton.tsx +13 -0
- package/src/ui/sonner.tsx +71 -0
- package/src/ui/spinner.tsx +15 -0
- package/src/ui/switch.tsx +41 -0
- package/src/ui/table.tsx +116 -0
- package/src/ui/tabs.tsx +66 -0
- package/src/ui/textarea.tsx +20 -0
- package/src/ui/toggle-group.tsx +83 -0
- package/src/ui/toggle.tsx +47 -0
- package/src/ui/tooltip.tsx +61 -0
- package/src/ui/trace.tsx +165 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
import { buttonVariants } from "./button"
|
|
8
|
+
|
|
9
|
+
function AlertDialog({
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
12
|
+
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function AlertDialogTrigger({
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
18
|
+
return (
|
|
19
|
+
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function AlertDialogPortal({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
|
26
|
+
return (
|
|
27
|
+
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function AlertDialogOverlay({
|
|
32
|
+
className,
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
|
35
|
+
return (
|
|
36
|
+
<AlertDialogPrimitive.Overlay
|
|
37
|
+
data-slot="alert-dialog-overlay"
|
|
38
|
+
className={cn(
|
|
39
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function AlertDialogContent({
|
|
48
|
+
className,
|
|
49
|
+
size = "default",
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
|
|
52
|
+
size?: "default" | "lg"
|
|
53
|
+
}) {
|
|
54
|
+
return (
|
|
55
|
+
<AlertDialogPortal>
|
|
56
|
+
<AlertDialogOverlay />
|
|
57
|
+
<AlertDialogPrimitive.Content
|
|
58
|
+
data-slot="alert-dialog-content"
|
|
59
|
+
className={cn(
|
|
60
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 w-full translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200",
|
|
61
|
+
size === "lg"
|
|
62
|
+
? "flex max-h-[92dvh] max-w-[calc(100%-1.25rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-5xl"
|
|
63
|
+
: "grid max-w-[calc(100%-2rem)] gap-4 p-6 sm:max-w-lg",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
</AlertDialogPortal>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function AlertDialogHeader({
|
|
73
|
+
className,
|
|
74
|
+
...props
|
|
75
|
+
}: React.ComponentProps<"div">) {
|
|
76
|
+
return (
|
|
77
|
+
<div
|
|
78
|
+
data-slot="alert-dialog-header"
|
|
79
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function AlertDialogFooter({
|
|
86
|
+
className,
|
|
87
|
+
...props
|
|
88
|
+
}: React.ComponentProps<"div">) {
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
data-slot="alert-dialog-footer"
|
|
92
|
+
className={cn(
|
|
93
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
94
|
+
className
|
|
95
|
+
)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function AlertDialogTitle({
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
105
|
+
return (
|
|
106
|
+
<AlertDialogPrimitive.Title
|
|
107
|
+
data-slot="alert-dialog-title"
|
|
108
|
+
className={cn("text-lg font-semibold", className)}
|
|
109
|
+
{...props}
|
|
110
|
+
/>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function AlertDialogDescription({
|
|
115
|
+
className,
|
|
116
|
+
...props
|
|
117
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
|
118
|
+
return (
|
|
119
|
+
<AlertDialogPrimitive.Description
|
|
120
|
+
data-slot="alert-dialog-description"
|
|
121
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function AlertDialogAction({
|
|
128
|
+
className,
|
|
129
|
+
...props
|
|
130
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
|
131
|
+
return (
|
|
132
|
+
<AlertDialogPrimitive.Action
|
|
133
|
+
className={cn(buttonVariants(), className)}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function AlertDialogCancel({
|
|
140
|
+
className,
|
|
141
|
+
...props
|
|
142
|
+
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
|
143
|
+
return (
|
|
144
|
+
<AlertDialogPrimitive.Cancel
|
|
145
|
+
className={cn(buttonVariants({ variant: "outline" }), className)}
|
|
146
|
+
{...props}
|
|
147
|
+
/>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export {
|
|
152
|
+
AlertDialog,
|
|
153
|
+
AlertDialogPortal,
|
|
154
|
+
AlertDialogOverlay,
|
|
155
|
+
AlertDialogTrigger,
|
|
156
|
+
AlertDialogContent,
|
|
157
|
+
AlertDialogHeader,
|
|
158
|
+
AlertDialogFooter,
|
|
159
|
+
AlertDialogTitle,
|
|
160
|
+
AlertDialogDescription,
|
|
161
|
+
AlertDialogAction,
|
|
162
|
+
AlertDialogCancel,
|
|
163
|
+
}
|
package/src/ui/alert.tsx
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva(
|
|
7
|
+
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-card text-card-foreground",
|
|
12
|
+
destructive:
|
|
13
|
+
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: "default",
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
function Alert({
|
|
23
|
+
className,
|
|
24
|
+
variant,
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
data-slot="alert"
|
|
30
|
+
role="alert"
|
|
31
|
+
className={cn(alertVariants({ variant }), className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
38
|
+
return (
|
|
39
|
+
<div
|
|
40
|
+
data-slot="alert-title"
|
|
41
|
+
className={cn(
|
|
42
|
+
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function AlertDescription({
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}: React.ComponentProps<"div">) {
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
data-slot="alert-description"
|
|
57
|
+
className={cn(
|
|
58
|
+
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { Alert, AlertTitle, AlertDescription }
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { RefObject, useEffect, useId, useState } from "react"
|
|
4
|
+
import { motion } from "motion/react"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
|
|
8
|
+
export interface AnimatedBeamProps {
|
|
9
|
+
className?: string
|
|
10
|
+
containerRef: RefObject<HTMLElement | null> // Container ref
|
|
11
|
+
fromRef: RefObject<HTMLElement | null>
|
|
12
|
+
toRef: RefObject<HTMLElement | null>
|
|
13
|
+
curvature?: number
|
|
14
|
+
reverse?: boolean
|
|
15
|
+
pathColor?: string
|
|
16
|
+
pathWidth?: number
|
|
17
|
+
pathOpacity?: number
|
|
18
|
+
gradientStartColor?: string
|
|
19
|
+
gradientStopColor?: string
|
|
20
|
+
delay?: number
|
|
21
|
+
duration?: number
|
|
22
|
+
startXOffset?: number
|
|
23
|
+
startYOffset?: number
|
|
24
|
+
endXOffset?: number
|
|
25
|
+
endYOffset?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({
|
|
29
|
+
className,
|
|
30
|
+
containerRef,
|
|
31
|
+
fromRef,
|
|
32
|
+
toRef,
|
|
33
|
+
curvature = 0,
|
|
34
|
+
reverse = false, // Include the reverse prop
|
|
35
|
+
duration = Math.random() * 3 + 4,
|
|
36
|
+
delay = 0,
|
|
37
|
+
pathColor = "gray",
|
|
38
|
+
pathWidth = 2,
|
|
39
|
+
pathOpacity = 0.2,
|
|
40
|
+
gradientStartColor = "#ffaa40",
|
|
41
|
+
gradientStopColor = "#9c40ff",
|
|
42
|
+
startXOffset = 0,
|
|
43
|
+
startYOffset = 0,
|
|
44
|
+
endXOffset = 0,
|
|
45
|
+
endYOffset = 0,
|
|
46
|
+
}) => {
|
|
47
|
+
const id = useId()
|
|
48
|
+
const [pathD, setPathD] = useState("")
|
|
49
|
+
const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 })
|
|
50
|
+
|
|
51
|
+
// Calculate the gradient coordinates based on the reverse prop
|
|
52
|
+
const gradientCoordinates = reverse
|
|
53
|
+
? {
|
|
54
|
+
x1: ["90%", "-10%"],
|
|
55
|
+
x2: ["100%", "0%"],
|
|
56
|
+
y1: ["0%", "0%"],
|
|
57
|
+
y2: ["0%", "0%"],
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
x1: ["10%", "110%"],
|
|
61
|
+
x2: ["0%", "100%"],
|
|
62
|
+
y1: ["0%", "0%"],
|
|
63
|
+
y2: ["0%", "0%"],
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const updatePath = () => {
|
|
68
|
+
if (containerRef.current && fromRef.current && toRef.current) {
|
|
69
|
+
const containerRect = containerRef.current.getBoundingClientRect()
|
|
70
|
+
const rectA = fromRef.current.getBoundingClientRect()
|
|
71
|
+
const rectB = toRef.current.getBoundingClientRect()
|
|
72
|
+
|
|
73
|
+
const svgWidth = containerRect.width
|
|
74
|
+
const svgHeight = containerRect.height
|
|
75
|
+
setSvgDimensions({ width: svgWidth, height: svgHeight })
|
|
76
|
+
|
|
77
|
+
const startX =
|
|
78
|
+
rectA.left - containerRect.left + rectA.width / 2 + startXOffset
|
|
79
|
+
const startY =
|
|
80
|
+
rectA.top - containerRect.top + rectA.height / 2 + startYOffset
|
|
81
|
+
const endX =
|
|
82
|
+
rectB.left - containerRect.left + rectB.width / 2 + endXOffset
|
|
83
|
+
const endY =
|
|
84
|
+
rectB.top - containerRect.top + rectB.height / 2 + endYOffset
|
|
85
|
+
|
|
86
|
+
const controlY = startY - curvature
|
|
87
|
+
const d = `M ${startX},${startY} Q ${
|
|
88
|
+
(startX + endX) / 2
|
|
89
|
+
},${controlY} ${endX},${endY}`
|
|
90
|
+
setPathD(d)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Initialize ResizeObserver
|
|
95
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
96
|
+
updatePath()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Observe the container element
|
|
100
|
+
if (containerRef.current) {
|
|
101
|
+
resizeObserver.observe(containerRef.current)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Call the updatePath initially to set the initial path
|
|
105
|
+
updatePath()
|
|
106
|
+
|
|
107
|
+
// Clean up the observer on component unmount
|
|
108
|
+
return () => {
|
|
109
|
+
resizeObserver.disconnect()
|
|
110
|
+
}
|
|
111
|
+
}, [
|
|
112
|
+
containerRef,
|
|
113
|
+
fromRef,
|
|
114
|
+
toRef,
|
|
115
|
+
curvature,
|
|
116
|
+
startXOffset,
|
|
117
|
+
startYOffset,
|
|
118
|
+
endXOffset,
|
|
119
|
+
endYOffset,
|
|
120
|
+
])
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<svg
|
|
124
|
+
fill="none"
|
|
125
|
+
width={svgDimensions.width}
|
|
126
|
+
height={svgDimensions.height}
|
|
127
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
128
|
+
className={cn(
|
|
129
|
+
"pointer-events-none absolute top-0 left-0 transform-gpu stroke-2",
|
|
130
|
+
className
|
|
131
|
+
)}
|
|
132
|
+
viewBox={`0 0 ${svgDimensions.width} ${svgDimensions.height}`}
|
|
133
|
+
>
|
|
134
|
+
<path
|
|
135
|
+
d={pathD}
|
|
136
|
+
stroke={pathColor}
|
|
137
|
+
strokeWidth={pathWidth}
|
|
138
|
+
strokeOpacity={pathOpacity}
|
|
139
|
+
strokeLinecap="round"
|
|
140
|
+
/>
|
|
141
|
+
<path
|
|
142
|
+
d={pathD}
|
|
143
|
+
strokeWidth={pathWidth}
|
|
144
|
+
stroke={`url(#${id})`}
|
|
145
|
+
strokeOpacity="1"
|
|
146
|
+
strokeLinecap="round"
|
|
147
|
+
/>
|
|
148
|
+
<defs>
|
|
149
|
+
<motion.linearGradient
|
|
150
|
+
className="transform-gpu"
|
|
151
|
+
id={id}
|
|
152
|
+
gradientUnits={"userSpaceOnUse"}
|
|
153
|
+
initial={{
|
|
154
|
+
x1: "0%",
|
|
155
|
+
x2: "0%",
|
|
156
|
+
y1: "0%",
|
|
157
|
+
y2: "0%",
|
|
158
|
+
}}
|
|
159
|
+
animate={{
|
|
160
|
+
x1: gradientCoordinates.x1,
|
|
161
|
+
x2: gradientCoordinates.x2,
|
|
162
|
+
y1: gradientCoordinates.y1,
|
|
163
|
+
y2: gradientCoordinates.y2,
|
|
164
|
+
}}
|
|
165
|
+
transition={{
|
|
166
|
+
delay,
|
|
167
|
+
duration,
|
|
168
|
+
ease: [0.16, 1, 0.3, 1], // https://easings.net/#easeOutExpo
|
|
169
|
+
repeat: Infinity,
|
|
170
|
+
repeatDelay: 0,
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
<stop stopColor={gradientStartColor} stopOpacity="0"></stop>
|
|
174
|
+
<stop stopColor={gradientStartColor}></stop>
|
|
175
|
+
<stop offset="32.5%" stopColor={gradientStopColor}></stop>
|
|
176
|
+
<stop
|
|
177
|
+
offset="100%"
|
|
178
|
+
stopColor={gradientStopColor}
|
|
179
|
+
stopOpacity="0"
|
|
180
|
+
></stop>
|
|
181
|
+
</motion.linearGradient>
|
|
182
|
+
</defs>
|
|
183
|
+
</svg>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React, {
|
|
4
|
+
ComponentPropsWithoutRef,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react"
|
|
9
|
+
import { AnimatePresence, motion, MotionProps } from "motion/react"
|
|
10
|
+
|
|
11
|
+
import { cn } from "../lib/utils"
|
|
12
|
+
|
|
13
|
+
export function AnimatedListItem({ children }: { children: React.ReactNode }) {
|
|
14
|
+
const animations: MotionProps = {
|
|
15
|
+
initial: { scale: 0, opacity: 0 },
|
|
16
|
+
animate: { scale: 1, opacity: 1, originY: 0 },
|
|
17
|
+
exit: { scale: 0, opacity: 0 },
|
|
18
|
+
transition: { type: "spring", stiffness: 350, damping: 40 },
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<motion.div {...animations} layout className="mx-auto w-full">
|
|
23
|
+
{children}
|
|
24
|
+
</motion.div>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface AnimatedListProps extends ComponentPropsWithoutRef<"div"> {
|
|
29
|
+
children: React.ReactNode
|
|
30
|
+
delay?: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const AnimatedList = React.memo(
|
|
34
|
+
({ children, className, delay = 1000, ...props }: AnimatedListProps) => {
|
|
35
|
+
const [index, setIndex] = useState(0)
|
|
36
|
+
const childrenArray = useMemo(
|
|
37
|
+
() => React.Children.toArray(children),
|
|
38
|
+
[children]
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const timeout = setTimeout(() => {
|
|
43
|
+
setIndex((prevIndex) => (prevIndex + 1) % childrenArray.length)
|
|
44
|
+
}, delay)
|
|
45
|
+
|
|
46
|
+
return () => clearTimeout(timeout)
|
|
47
|
+
}, [index, delay, childrenArray.length])
|
|
48
|
+
|
|
49
|
+
const itemsToShow = useMemo(() => {
|
|
50
|
+
const result = childrenArray.slice(0, index + 1).reverse()
|
|
51
|
+
return result
|
|
52
|
+
}, [index, childrenArray])
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
className={cn(`flex flex-col items-center gap-4`, className)}
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
<AnimatePresence>
|
|
60
|
+
{itemsToShow.map((item) => (
|
|
61
|
+
<AnimatedListItem key={(item as React.ReactElement).key}>
|
|
62
|
+
{item}
|
|
63
|
+
</AnimatedListItem>
|
|
64
|
+
))}
|
|
65
|
+
</AnimatePresence>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
AnimatedList.displayName = "AnimatedList"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils"
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
data-slot="avatar"
|
|
15
|
+
className={cn(
|
|
16
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function AvatarImage({
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
28
|
+
return (
|
|
29
|
+
<AvatarPrimitive.Image
|
|
30
|
+
data-slot="avatar-image"
|
|
31
|
+
className={cn("aspect-square size-full", className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function AvatarFallback({
|
|
38
|
+
className,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
41
|
+
return (
|
|
42
|
+
<AvatarPrimitive.Fallback
|
|
43
|
+
data-slot="avatar-fallback"
|
|
44
|
+
className={cn(
|
|
45
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
package/src/ui/badge.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils"
|
|
6
|
+
|
|
7
|
+
const badgeVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
14
|
+
secondary:
|
|
15
|
+
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
16
|
+
destructive:
|
|
17
|
+
"border-transparent bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
18
|
+
outline:
|
|
19
|
+
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
function Badge({
|
|
29
|
+
className,
|
|
30
|
+
variant,
|
|
31
|
+
asChild = false,
|
|
32
|
+
...props
|
|
33
|
+
}: React.ComponentProps<"span"> &
|
|
34
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
35
|
+
const Comp = asChild ? Slot : "span"
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Comp
|
|
39
|
+
data-slot="badge"
|
|
40
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ReactNode } from "react"
|
|
2
|
+
import { IconArrowRight } from "@tabler/icons-react"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
5
|
+
import { Button } from "./button"
|
|
6
|
+
|
|
7
|
+
interface BentoGridProps extends ComponentPropsWithoutRef<"div"> {
|
|
8
|
+
children: ReactNode
|
|
9
|
+
className?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface BentoCardProps extends ComponentPropsWithoutRef<"div"> {
|
|
13
|
+
name: string
|
|
14
|
+
className: string
|
|
15
|
+
background: ReactNode
|
|
16
|
+
Icon: React.ElementType
|
|
17
|
+
description: string
|
|
18
|
+
href: string
|
|
19
|
+
cta: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const BentoGrid = ({ children, className, ...props }: BentoGridProps) => {
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={cn(
|
|
26
|
+
"grid w-full auto-rows-[22rem] grid-cols-3 gap-4",
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const BentoCard = ({
|
|
37
|
+
name,
|
|
38
|
+
className,
|
|
39
|
+
background,
|
|
40
|
+
Icon,
|
|
41
|
+
description,
|
|
42
|
+
href,
|
|
43
|
+
cta,
|
|
44
|
+
...props
|
|
45
|
+
}: BentoCardProps) => (
|
|
46
|
+
<div
|
|
47
|
+
key={name}
|
|
48
|
+
className={cn(
|
|
49
|
+
"group relative col-span-3 flex flex-col justify-between overflow-hidden rounded-xl",
|
|
50
|
+
// light styles
|
|
51
|
+
"bg-background [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
|
|
52
|
+
// dark styles
|
|
53
|
+
"dark:bg-background transform-gpu dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] dark:[border:1px_solid_rgba(255,255,255,.1)]",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
>
|
|
58
|
+
<div>{background}</div>
|
|
59
|
+
<div className="p-4">
|
|
60
|
+
<div className="pointer-events-none z-10 flex transform-gpu flex-col gap-1 transition-all duration-300 lg:group-hover:-translate-y-10">
|
|
61
|
+
<Icon className="h-12 w-12 origin-left transform-gpu text-neutral-700 transition-all duration-300 ease-in-out group-hover:scale-75" />
|
|
62
|
+
<h3 className="text-xl font-semibold text-neutral-700 dark:text-neutral-300">
|
|
63
|
+
{name}
|
|
64
|
+
</h3>
|
|
65
|
+
<p className="max-w-lg text-neutral-400">{description}</p>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div
|
|
69
|
+
className={cn(
|
|
70
|
+
"pointer-events-none flex w-full translate-y-0 transform-gpu flex-row items-center transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100 lg:hidden"
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
<Button
|
|
74
|
+
variant="link"
|
|
75
|
+
asChild
|
|
76
|
+
size="sm"
|
|
77
|
+
className="pointer-events-auto p-0"
|
|
78
|
+
>
|
|
79
|
+
<a href={href}>
|
|
80
|
+
{cta}
|
|
81
|
+
<IconArrowRight className="ms-2 size-4 rtl:rotate-180" />
|
|
82
|
+
</a>
|
|
83
|
+
</Button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div
|
|
88
|
+
className={cn(
|
|
89
|
+
"pointer-events-none absolute bottom-0 hidden w-full translate-y-10 transform-gpu flex-row items-center p-4 opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100 lg:flex"
|
|
90
|
+
)}
|
|
91
|
+
>
|
|
92
|
+
<Button
|
|
93
|
+
variant="link"
|
|
94
|
+
asChild
|
|
95
|
+
size="sm"
|
|
96
|
+
className="pointer-events-auto p-0"
|
|
97
|
+
>
|
|
98
|
+
<a href={href}>
|
|
99
|
+
{cta}
|
|
100
|
+
<IconArrowRight className="ms-2 size-4 rtl:rotate-180" />
|
|
101
|
+
</a>
|
|
102
|
+
</Button>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div className="pointer-events-none absolute inset-0 transform-gpu transition-all duration-300 group-hover:bg-black/[.03] group-hover:dark:bg-neutral-800/10" />
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
export { BentoCard, BentoGrid }
|