kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1"use client";
2
3import { mergeProps } from "@base-ui/react/merge-props";
4import { useRender } from "@base-ui/react/use-render";
5import { cva, type VariantProps } from "class-variance-authority";
6import * as React from "react";
7
8import { cn } from "@/lib/cn";
9
10const buttonVariants = cva(
11 "[&_svg]:-mx-0.5 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
12 {
13 defaultVariants: {
14 size: "default",
15 variant: "default",
16 },
17 variants: {
18 size: {
19 default: "h-9 px-[calc(--spacing(3)-1px)] sm:h-8",
20 icon: "size-9 sm:size-8",
21 "icon-lg": "size-10 sm:size-9",
22 "icon-sm": "size-8 sm:size-7",
23 "icon-xl":
24 "size-11 sm:size-10 [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
25 "icon-xs":
26 "size-7 rounded-md before:rounded-[calc(var(--radius-md)-1px)] sm:size-6 not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-4 sm:not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-3.5",
27 lg: "h-10 px-[calc(--spacing(3.5)-1px)] sm:h-9",
28 sm: "h-8 gap-1.5 px-[calc(--spacing(2.5)-1px)] sm:h-7",
29 xl: "h-11 px-[calc(--spacing(4)-1px)] text-lg sm:h-10 sm:text-base [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
30 xs: "h-7 gap-1 rounded-md px-[calc(--spacing(2)-1px)] text-sm before:rounded-[calc(var(--radius-md)-1px)] sm:h-6 sm:text-xs [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5",
31 },
32 variant: {
33 default:
34 "not-disabled:inset-shadow-[0_1px_--theme(--color-white/16%)] border-primary bg-primary text-primary-foreground shadow-primary/24 shadow-xs [:active,[data-pressed]]:inset-shadow-[0_1px_--theme(--color-black/8%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-primary/90",
35 destructive:
36 "not-disabled:inset-shadow-[0_1px_--theme(--color-white/16%)] border-destructive bg-destructive text-white shadow-destructive/24 shadow-xs [:active,[data-pressed]]:inset-shadow-[0_1px_--theme(--color-black/8%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-destructive/90",
37 "destructive-outline":
38 "border-input bg-popover not-dark:bg-clip-padding text-destructive-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:border-destructive/32 [:hover,[data-pressed]]:bg-destructive/4",
39 ghost:
40 "border-transparent text-foreground data-pressed:bg-accent [:hover,[data-pressed]]:bg-accent",
41 link: "border-transparent underline-offset-4 [:hover,[data-pressed]]:underline",
42 outline:
43 "border-input bg-popover not-dark:bg-clip-padding text-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none [:hover,[data-pressed]]:bg-accent/50 dark:[:hover,[data-pressed]]:bg-input/64",
44 secondary:
45 "border-transparent bg-secondary text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80 [:hover,[data-pressed]]:bg-secondary/90",
46 },
47 },
48 },
49);
50
51interface ButtonProps extends useRender.ComponentProps<"button"> {
52 asChild?: boolean;
53 variant?: VariantProps<typeof buttonVariants>["variant"];
54 size?: VariantProps<typeof buttonVariants>["size"];
55}
56
57function Button({
58 asChild = false,
59 className,
60 children,
61 variant,
62 size,
63 render,
64 ...props
65}: ButtonProps) {
66 const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
67 render ? undefined : "button";
68
69 const defaultProps = {
70 className: cn(buttonVariants({ className, size, variant })),
71 "data-slot": "button",
72 type: typeValue,
73 };
74
75 const resolvedRender =
76 asChild && React.isValidElement(children) ? children : render;
77
78 return useRender({
79 defaultTagName: "button",
80 props: mergeProps<"button">(defaultProps, {
81 ...props,
82 children: asChild ? undefined : children,
83 }),
84 render: resolvedRender,
85 });
86}
87
88export { Button, buttonVariants };