pangolin/src/components/tags/tag.tsx
2025-03-01 17:45:38 -05:00

169 lines
5.2 KiB
TypeScript

import React from "react";
import { Button } from "../ui/button";
import {
TagInputProps,
TagInputStyleClassesProps,
type Tag as TagType
} from "./tag-input";
import { cva } from "class-variance-authority";
import { cn } from "@app/lib/cn";
export const tagVariants = cva(
"transition-all border inline-flex items-center text-sm pl-2 rounded-md",
{
variants: {
variant: {
default:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50",
primary:
"bg-primary border-primary text-primary-foreground hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-50",
destructive:
"bg-destructive border-destructive text-destructive-foreground hover:bg-destructive/90 disabled:cursor-not-allowed disabled:opacity-50"
},
size: {
sm: "text-xs h-6",
md: "text-sm h-8",
lg: "text-base h-9",
xl: "text-lg h-10"
},
shape: {
default: "rounded-sm",
rounded: "rounded-lg",
square: "rounded-none",
pill: "rounded-full"
},
borderStyle: {
default: "border-solid",
none: "border-none",
dashed: "border-dashed",
dotted: "border-dotted",
double: "border-double"
},
textCase: {
uppercase: "uppercase",
lowercase: "lowercase",
capitalize: "capitalize"
},
interaction: {
clickable: "cursor-pointer hover:shadow-md",
nonClickable: "cursor-default"
},
animation: {
none: "",
fadeIn: "animate-fadeIn",
slideIn: "animate-slideIn",
bounce: "animate-bounce"
},
textStyle: {
normal: "font-normal",
bold: "font-bold",
italic: "italic",
underline: "underline",
lineThrough: "line-through"
}
},
defaultVariants: {
variant: "default",
size: "md",
shape: "default",
borderStyle: "none",
interaction: "nonClickable",
animation: "fadeIn",
textStyle: "normal"
}
}
);
export type TagProps = {
tagObj: TagType;
variant: TagInputProps["variant"];
size: TagInputProps["size"];
shape: TagInputProps["shape"];
borderStyle: TagInputProps["borderStyle"];
textCase: TagInputProps["textCase"];
interaction: TagInputProps["interaction"];
animation: TagInputProps["animation"];
textStyle: TagInputProps["textStyle"];
onRemoveTag: (id: string) => void;
isActiveTag?: boolean;
tagClasses?: TagInputStyleClassesProps["tag"];
disabled?: boolean;
} & Pick<TagInputProps, "direction" | "onTagClick" | "draggable">;
export const Tag: React.FC<TagProps> = ({
tagObj,
direction,
draggable,
onTagClick,
onRemoveTag,
variant,
size,
shape,
borderStyle,
textCase,
interaction,
animation,
textStyle,
isActiveTag,
tagClasses,
disabled
}) => {
return (
<span
key={tagObj.id}
draggable={draggable}
className={cn(
tagVariants({
variant,
size,
shape,
borderStyle,
textCase,
interaction,
animation,
textStyle
}),
{
"justify-between w-full": direction === "column",
"cursor-pointer": draggable,
"ring-ring ring-offset-2 ring-2 ring-offset-background":
isActiveTag
},
tagClasses?.body
)}
onClick={() => onTagClick?.(tagObj)}
>
{tagObj.text}
<Button
type="button"
variant="ghost"
onClick={(e) => {
e.stopPropagation(); // Prevent event from bubbling up to the tag span
onRemoveTag(tagObj.id);
}}
disabled={disabled}
className={cn(
`p-1 h-full hover:bg-transparent`,
tagClasses?.closeButton
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-x"
>
<path d="M18 6 6 18"></path>
<path d="m6 6 12 12"></path>
</svg>
</Button>
</span>
);
};