import React, { type AnchorHTMLAttributes, type ButtonHTMLAttributes, type ComponentProps, type DetailedHTMLProps, type HTMLAttributeAnchorTarget, type HTMLAttributes, } from "react"; import { twMerge } from "tailwind-merge"; import Loading from "../elements/Loading"; import LucideIcon, { type TWUILucideIconName } from "../elements/lucide-icon"; import type { LucideProps } from "lucide-react"; export const TWUIButtonColors = [ "primary", "secondary", "text", "white", "accent", "gray", "error", "warning", "success", ] as const; export const TWUIButtonSizes = [ "small", "smaller", "normal", "large", "larger", ] as const; export const TWUIButtonVariants = ["normal", "ghost", "outlined"] as const; export type TWUIButtonColorType = (typeof TWUIButtonColors)[number]; export type TWUIButtonSizeType = (typeof TWUIButtonSizes)[number]; export type TWUIButtonVariantType = (typeof TWUIButtonVariants)[number]; export type TWUIButtonProps = DetailedHTMLProps< ButtonHTMLAttributes, HTMLButtonElement > & { title: string; variant?: TWUIButtonVariantType; color?: TWUIButtonColorType; size?: TWUIButtonSizeType; loadingIconSize?: React.ComponentProps["size"]; href?: string; target?: HTMLAttributeAnchorTarget; loading?: boolean; linkProps?: DetailedHTMLProps< AnchorHTMLAttributes, HTMLAnchorElement >; beforeIcon?: TWUILucideIconName | React.JSX.Element; afterIcon?: TWUILucideIconName | React.JSX.Element; before_icon_name?: TWUILucideIconName; after_icon_name?: TWUILucideIconName; before_icon_props?: LucideProps; after_icon_props?: LucideProps; buttonContentProps?: DetailedHTMLProps< HTMLAttributes, HTMLDivElement >; loadingProps?: ComponentProps; }; export const ButtonClasses: Record> = { normal: { primary: twMerge( "bg-primary hover:bg-primary-hover text-white", "dark:bg-primary-dark hover:dark:bg-primary-dark-hover", "twui-button-primary", ), secondary: twMerge( "bg-secondary hover:bg-secondary-hover text-white", "twui-button-secondary", ), white: twMerge( "!bg-white hover:!bg-slate-200 !text-slate-800", "twui-button-white", ), accent: twMerge( "bg-accent hover:bg-accent-hover text-white", "twui-button-accent", ), gray: twMerge( "bg-gray hover:bg-gray-hover text-foreground-light", "dark:bg-gray-dark hover:dark:bg-gray-dark-hover dark:text-foreground-dark", "twui-button-gray", ), success: twMerge( "bg-success hover:bg-success-hover text-white", "dark:bg-success hover:dark:bg-success-hover", "twui-button-success", ), error: twMerge( "bg-error hover:bg-error-hover text-white", "dark:bg-error hover:dark:bg-error-hover", "twui-button-error", ), }, outlined: { primary: twMerge( "bg-transparent outline outline-1 outline-primary", "text-primary-text dark:text-primary-dark-text dark:outline-primary-dark-outline", "twui-button-primary-outlined", ), secondary: twMerge( "bg-transparent outline outline-1 outline-secondary", "text-secondary", "twui-button-secondary-outlined", ), accent: twMerge( "bg-transparent outline outline-1 outline-accent", "text-accent", "twui-button-accent-outlined", ), gray: twMerge( "bg-transparent outline outline-1 outline-gray", "text-dark dark:text-white/60 dark:outline-white/30", "twui-button-gray-outlined", ), white: twMerge( "bg-transparent outline outline-1 outline-white/50", "text-white", "twui-button-white-outlined", ), error: twMerge( "bg-transparent outline outline-1 outline-error text-error", "dark:outline-error dark:text-error-dark", "twui-button-error-outlined", ), warning: twMerge( "bg-transparent outline outline-1 outline-warning text-warning", "dark:outline-warning dark:text-warning-dark", "twui-button-warning-outlined", ), }, ghost: { primary: twMerge( "bg-transparent dark:bg-transparent outline-none p-2", "text-primary-text dark:text-primary-dark-text hover:bg-transparent dark:hover:bg-transparent", "twui-button-primary-ghost", ), secondary: twMerge( "bg-transparent dark:bg-transparent outline-none p-2", "text-secondary hover:bg-transparent dark:hover:bg-transparent", "twui-button-secondary-ghost", ), text: twMerge( "bg-transparent dark:bg-transparent outline-none p-2 dark:text-foreground-dark", "text-foreground-light hover:bg-transparent dark:hover:bg-transparent", "twui-button-secondary-ghost", ), accent: twMerge( "bg-transparent dark:bg-transparent outline-none p-2", "text-accent hover:bg-transparent dark:hover:bg-transparent", "twui-button-accent-ghost", ), gray: twMerge( "bg-transparent dark:bg-transparent outline-none p-2 hover:bg-transparent dark:hover:bg-transparent", "text-slate-600 dark:text-white/70 hover:opacity-80", "twui-button-gray-ghost", ), error: twMerge( "bg-transparent outline-none p-2", "text-red-600 dark:text-red-400", "twui-button-error-ghost", ), warning: twMerge( "bg-transparent outline-none p-2", "text-yellow-600", "twui-button-warning-ghost", ), success: twMerge( "bg-transparent outline-none p-2", "text-success", "twui-button-success-ghost", ), white: twMerge( "bg-transparent outline-none p-2", "text-white", "twui-button-white-ghost", ), }, }; /** * # Buttons * @className twui-button-general * @className twui-button-content-wrapper * @className twui-button-primary * @className twui-button-primary-outlined * @className twui-button-primary-ghost * @className twui-button-secondary * @className twui-button-secondary-outlined * @className twui-button-secondary-ghost * @className twui-button-white * @className twui-button-white-outlined * @className twui-button-white-ghost * @className twui-button-accent * @className twui-button-accent-outlined * @className twui-button-accent-ghost * @className twui-button-gray * @className twui-button-gray-outlined * @className twui-button-gray-ghost * * @example ```css CSS directive: //@theme inline { --breakpoint-xs: 350px; --color-primary: #000000; --color-primary-hover: #29292b; --color-primary-outline: #29292b; --color-primary-text: #29292b; --color-primary-dark: #29292b; --color-primary-dark-hover: #4b4b4b; --color-primary-dark-outline: #4b4b4b; --color-primary-dark-text: #4b4b4b; --color-secondary: #000000; --color-secondary-hover: #dddddd; --color-secondary-outline: #dddddd; --color-secondary-text: #dddddd; --color-secondary-dark: #000000; --color-secondary-dark-hover: #dddddd; --color-secondary-dark-outline: #dddddd; --color-secondary-dark-text: #dddddd; --color-accent: #000000; --color-accent-hover: #dddddd; --color-accent-outline: #dddddd; --color-accent-text: #dddddd; --color-accent-dark: #000000; --color-accent-dark-hover: #dddddd; --color-accent-dark-outline: #dddddd; --color-accent-dark-text: #dddddd; } ``` */ export default function Button({ href, target, variant, color, size, buttonContentProps, linkProps, beforeIcon, afterIcon, loading, loadingIconSize, loadingProps, before_icon_name, after_icon_name, before_icon_props, after_icon_props, ...props }: TWUIButtonProps) { const finalClassName = ButtonClasses[variant || "normal"]?.[color || "primary"] ?? ""; const buttonComponent = ( ); if (href) return ( {buttonComponent} ); return buttonComponent; }