@traky-app/ui 1.0.0 → 1.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/README.md +12 -19
- package/package.json +48 -3
- package/registry/traky/json/command.json +1 -1
- package/registry/traky/ui/command.tsx +1 -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 +191 -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/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/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,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 }
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { IconChevronRight, IconDots } from "@tabler/icons-react";
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
5
|
+
|
|
6
|
+
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
|
7
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
11
|
+
return (
|
|
12
|
+
<ol
|
|
13
|
+
data-slot="breadcrumb-list"
|
|
14
|
+
className={cn(
|
|
15
|
+
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
24
|
+
return (
|
|
25
|
+
<li
|
|
26
|
+
data-slot="breadcrumb-item"
|
|
27
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function BreadcrumbLink({
|
|
34
|
+
asChild,
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
|
+
}: React.ComponentProps<"a"> & {
|
|
38
|
+
asChild?: boolean
|
|
39
|
+
}) {
|
|
40
|
+
const Comp = asChild ? Slot : "a"
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Comp
|
|
44
|
+
data-slot="breadcrumb-link"
|
|
45
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
52
|
+
return (
|
|
53
|
+
<span
|
|
54
|
+
data-slot="breadcrumb-page"
|
|
55
|
+
role="link"
|
|
56
|
+
aria-disabled="true"
|
|
57
|
+
aria-current="page"
|
|
58
|
+
className={cn("text-foreground font-normal", className)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function BreadcrumbSeparator({
|
|
65
|
+
children,
|
|
66
|
+
className,
|
|
67
|
+
...props
|
|
68
|
+
}: React.ComponentProps<"li">) {
|
|
69
|
+
return (
|
|
70
|
+
<li
|
|
71
|
+
data-slot="breadcrumb-separator"
|
|
72
|
+
role="presentation"
|
|
73
|
+
aria-hidden="true"
|
|
74
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
{children ?? <IconChevronRight />}
|
|
78
|
+
</li>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function BreadcrumbEllipsis({
|
|
83
|
+
className,
|
|
84
|
+
...props
|
|
85
|
+
}: React.ComponentProps<"span">) {
|
|
86
|
+
return (
|
|
87
|
+
<span
|
|
88
|
+
data-slot="breadcrumb-ellipsis"
|
|
89
|
+
role="presentation"
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
92
|
+
{...props}
|
|
93
|
+
>
|
|
94
|
+
<IconDots className="size-4" />
|
|
95
|
+
<span className="sr-only">More</span>
|
|
96
|
+
</span>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export {
|
|
101
|
+
Breadcrumb,
|
|
102
|
+
BreadcrumbList,
|
|
103
|
+
BreadcrumbItem,
|
|
104
|
+
BreadcrumbLink,
|
|
105
|
+
BreadcrumbPage,
|
|
106
|
+
BreadcrumbSeparator,
|
|
107
|
+
BreadcrumbEllipsis,
|
|
108
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Slot } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils"
|
|
6
|
+
|
|
7
|
+
function BubbleGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
8
|
+
return (
|
|
9
|
+
<div
|
|
10
|
+
data-slot="bubble-group"
|
|
11
|
+
className={cn("flex min-w-0 flex-col gap-2", className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const bubbleVariants = cva(
|
|
18
|
+
"group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",
|
|
19
|
+
{
|
|
20
|
+
variants: {
|
|
21
|
+
variant: {
|
|
22
|
+
default:
|
|
23
|
+
"*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80",
|
|
24
|
+
secondary:
|
|
25
|
+
"*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]",
|
|
26
|
+
muted:
|
|
27
|
+
"*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]",
|
|
28
|
+
tinted:
|
|
29
|
+
"*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]",
|
|
30
|
+
outline:
|
|
31
|
+
"*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-background [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30",
|
|
32
|
+
ghost:
|
|
33
|
+
"border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50",
|
|
34
|
+
destructive:
|
|
35
|
+
"*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: "default",
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
function Bubble({
|
|
45
|
+
variant = "default",
|
|
46
|
+
align = "start",
|
|
47
|
+
className,
|
|
48
|
+
...props
|
|
49
|
+
}: React.ComponentProps<"div"> &
|
|
50
|
+
VariantProps<typeof bubbleVariants> & {
|
|
51
|
+
align?: "start" | "end"
|
|
52
|
+
}) {
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
data-slot="bubble"
|
|
56
|
+
data-variant={variant}
|
|
57
|
+
data-align={align}
|
|
58
|
+
className={cn(bubbleVariants({ variant }), className)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function BubbleContent({
|
|
65
|
+
asChild = false,
|
|
66
|
+
className,
|
|
67
|
+
...props
|
|
68
|
+
}: React.ComponentProps<"div"> & {
|
|
69
|
+
asChild?: boolean
|
|
70
|
+
}) {
|
|
71
|
+
const Comp = asChild ? Slot.Root : "div"
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Comp
|
|
75
|
+
data-slot="bubble-content"
|
|
76
|
+
className={cn(
|
|
77
|
+
"w-fit max-w-full min-w-0 overflow-hidden rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed wrap-break-word group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50",
|
|
78
|
+
className
|
|
79
|
+
)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const bubbleReactionsVariants = cva(
|
|
86
|
+
"absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0",
|
|
87
|
+
{
|
|
88
|
+
variants: {
|
|
89
|
+
side: {
|
|
90
|
+
top: "top-0 -translate-y-3/4",
|
|
91
|
+
bottom: "bottom-0 translate-y-3/4",
|
|
92
|
+
},
|
|
93
|
+
align: {
|
|
94
|
+
start: "left-3",
|
|
95
|
+
end: "right-3",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
defaultVariants: {
|
|
99
|
+
side: "bottom",
|
|
100
|
+
align: "end",
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
function BubbleReactions({
|
|
106
|
+
side = "bottom",
|
|
107
|
+
align = "end",
|
|
108
|
+
className,
|
|
109
|
+
...props
|
|
110
|
+
}: React.ComponentProps<"div"> & {
|
|
111
|
+
align?: "start" | "end"
|
|
112
|
+
side?: "top" | "bottom"
|
|
113
|
+
}) {
|
|
114
|
+
return (
|
|
115
|
+
<div
|
|
116
|
+
data-slot="bubble-reactions"
|
|
117
|
+
data-align={align}
|
|
118
|
+
data-side={side}
|
|
119
|
+
className={cn(bubbleReactionsVariants({ side, align }), className)}
|
|
120
|
+
{...props}
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { BubbleGroup, Bubble, BubbleContent, BubbleReactions }
|