kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import { cva, type VariantProps } from "class-variance-authority";
2
3import { cn } from "@/lib/utils";
4
5function Empty({ className, ...props }: React.ComponentProps<"div">) {
6 return (
7 <div
8 className={cn(
9 "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance p-6 text-center md:p-12",
10 className,
11 )}
12 data-slot="empty"
13 {...props}
14 />
15 );
16}
17
18function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
19 return (
20 <div
21 className={cn(
22 "flex max-w-sm flex-col items-center text-center",
23 className,
24 )}
25 data-slot="empty-header"
26 {...props}
27 />
28 );
29}
30
31const emptyMediaVariants = cva(
32 "flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
33 {
34 defaultVariants: {
35 variant: "default",
36 },
37 variants: {
38 variant: {
39 default: "bg-transparent",
40 icon: "relative flex size-9 shrink-0 items-center justify-center rounded-md border bg-card not-dark:bg-clip-padding text-foreground shadow-sm/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-md)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='size-'])]:size-4.5",
41 },
42 },
43 },
44);
45
46function EmptyMedia({
47 className,
48 variant = "default",
49 ...props
50}: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
51 return (
52 <div
53 className={cn("relative mb-6", className)}
54 data-slot="empty-media"
55 data-variant={variant}
56 {...props}
57 >
58 {variant === "icon" && (
59 <>
60 <div
61 aria-hidden="true"
62 className={cn(
63 emptyMediaVariants({ className, variant }),
64 "-translate-x-0.5 -rotate-10 pointer-events-none absolute bottom-px origin-bottom-left scale-84 shadow-none",
65 )}
66 />
67 <div
68 aria-hidden="true"
69 className={cn(
70 emptyMediaVariants({ className, variant }),
71 "pointer-events-none absolute bottom-px origin-bottom-right translate-x-0.5 rotate-10 scale-84 shadow-none",
72 )}
73 />
74 </>
75 )}
76 <div
77 className={cn(emptyMediaVariants({ className, variant }))}
78 {...props}
79 />
80 </div>
81 );
82}
83
84function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
85 return (
86 <div
87 className={cn("font-heading font-semibold text-xl", className)}
88 data-slot="empty-title"
89 {...props}
90 />
91 );
92}
93
94function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
95 return (
96 <div
97 className={cn(
98 "text-muted-foreground text-sm [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4 [[data-slot=empty-title]+&]:mt-1",
99 className,
100 )}
101 data-slot="empty-description"
102 {...props}
103 />
104 );
105}
106
107function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
108 return (
109 <div
110 className={cn(
111 "flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm",
112 className,
113 )}
114 data-slot="empty-content"
115 {...props}
116 />
117 );
118}
119
120export {
121 Empty,
122 EmptyContent,
123 EmptyDescription,
124 EmptyHeader,
125 EmptyMedia,
126 EmptyTitle,
127};