@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.
Files changed (75) hide show
  1. package/README.md +12 -19
  2. package/package.json +50 -3
  3. package/registry/traky/json/command.json +1 -1
  4. package/registry/traky/json/icon.json +17 -0
  5. package/registry/traky/json/logo.json +17 -0
  6. package/registry/traky/registry.json +20 -0
  7. package/registry/traky/ui/command.tsx +2 -0
  8. package/registry/traky/ui/icon.tsx +71 -0
  9. package/registry/traky/ui/logo.tsx +77 -0
  10. package/src/hooks/use-document-theme.ts +25 -0
  11. package/src/hooks/use-mobile.ts +19 -0
  12. package/src/lib/disable-input-autofill.ts +23 -0
  13. package/src/lib/sidebar-persistence.ts +76 -0
  14. package/src/lib/utils.ts +30 -0
  15. package/src/ui/accordion.tsx +65 -0
  16. package/src/ui/alert-dialog.tsx +163 -0
  17. package/src/ui/alert.tsx +66 -0
  18. package/src/ui/animated-beam.tsx +185 -0
  19. package/src/ui/animated-list.tsx +71 -0
  20. package/src/ui/avatar.tsx +53 -0
  21. package/src/ui/badge.tsx +46 -0
  22. package/src/ui/bento-grid.tsx +109 -0
  23. package/src/ui/breadcrumb.tsx +108 -0
  24. package/src/ui/bubble.tsx +125 -0
  25. package/src/ui/button-group.tsx +83 -0
  26. package/src/ui/button.tsx +64 -0
  27. package/src/ui/calendar.tsx +213 -0
  28. package/src/ui/card.tsx +92 -0
  29. package/src/ui/carousel.tsx +240 -0
  30. package/src/ui/chart.tsx +380 -0
  31. package/src/ui/checkbox.tsx +31 -0
  32. package/src/ui/checkpoint.tsx +94 -0
  33. package/src/ui/code-block.tsx +232 -0
  34. package/src/ui/collapsible.tsx +33 -0
  35. package/src/ui/command.tsx +192 -0
  36. package/src/ui/context-menu.tsx +251 -0
  37. package/src/ui/dialog.tsx +142 -0
  38. package/src/ui/drawer.tsx +135 -0
  39. package/src/ui/dropdown-menu.tsx +256 -0
  40. package/src/ui/empty.tsx +104 -0
  41. package/src/ui/field.tsx +248 -0
  42. package/src/ui/globe.tsx +234 -0
  43. package/src/ui/hover-card.tsx +44 -0
  44. package/src/ui/icon.tsx +71 -0
  45. package/src/ui/input-group.tsx +170 -0
  46. package/src/ui/input-otp.tsx +77 -0
  47. package/src/ui/input.tsx +24 -0
  48. package/src/ui/item.tsx +193 -0
  49. package/src/ui/kbd.tsx +28 -0
  50. package/src/ui/label.tsx +24 -0
  51. package/src/ui/logo.tsx +77 -0
  52. package/src/ui/map.tsx +2226 -0
  53. package/src/ui/marker.tsx +69 -0
  54. package/src/ui/marquee.tsx +74 -0
  55. package/src/ui/message.tsx +92 -0
  56. package/src/ui/navigation-menu.tsx +169 -0
  57. package/src/ui/number-ticker.tsx +90 -0
  58. package/src/ui/pagination.tsx +122 -0
  59. package/src/ui/popover.tsx +48 -0
  60. package/src/ui/progress.tsx +31 -0
  61. package/src/ui/select.tsx +193 -0
  62. package/src/ui/separator.tsx +28 -0
  63. package/src/ui/sheet.tsx +143 -0
  64. package/src/ui/sidebar.tsx +764 -0
  65. package/src/ui/skeleton.tsx +13 -0
  66. package/src/ui/sonner.tsx +71 -0
  67. package/src/ui/spinner.tsx +15 -0
  68. package/src/ui/switch.tsx +41 -0
  69. package/src/ui/table.tsx +116 -0
  70. package/src/ui/tabs.tsx +66 -0
  71. package/src/ui/textarea.tsx +20 -0
  72. package/src/ui/toggle-group.tsx +83 -0
  73. package/src/ui/toggle.tsx +47 -0
  74. package/src/ui/tooltip.tsx +61 -0
  75. 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
+ }
@@ -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 }
@@ -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 }