305 lines
6.9 KiB
Plaintext
305 lines
6.9 KiB
Plaintext
---
|
|
import { Icon } from 'astro-icon/components'
|
|
import { tv, type VariantProps } from 'tailwind-variants'
|
|
|
|
import { cn } from '../lib/cn'
|
|
|
|
import type { HTMLAttributes, Polymorphic } from 'astro/types'
|
|
|
|
type Props<Tag extends 'a' | 'button' | 'label' = 'button'> = Polymorphic<
|
|
Required<Pick<HTMLAttributes<'label'>, Tag extends 'label' ? 'for' : never>> &
|
|
VariantProps<typeof button> & {
|
|
as: Tag
|
|
label: string
|
|
icon?: string
|
|
endIcon?: string
|
|
classNames?: {
|
|
label?: string
|
|
icon?: string
|
|
endIcon?: string
|
|
}
|
|
dataAstroReload?: boolean
|
|
children?: never
|
|
disabled?: boolean
|
|
inlineIcon?: boolean
|
|
}
|
|
>
|
|
|
|
export type ButtonProps<Tag extends 'a' | 'button' | 'label' = 'button'> = Props<Tag>
|
|
|
|
const button = tv({
|
|
slots: {
|
|
base: 'inline-flex shrink-0 items-center justify-center gap-2 rounded-lg border transition-colors duration-100 focus-visible:ring-2 focus-visible:ring-current focus-visible:ring-offset-2 focus-visible:ring-offset-black focus-visible:outline-hidden',
|
|
icon: 'size-4 shrink-0',
|
|
label: 'text-left whitespace-nowrap',
|
|
endIcon: 'size-4 shrink-0',
|
|
},
|
|
variants: {
|
|
size: {
|
|
sm: {
|
|
base: 'h-8 px-3 text-sm',
|
|
icon: 'size-4',
|
|
endIcon: 'size-4',
|
|
},
|
|
md: {
|
|
base: 'h-9 px-4 text-sm',
|
|
icon: 'size-4',
|
|
endIcon: 'size-4',
|
|
label: 'font-medium',
|
|
},
|
|
lg: {
|
|
base: 'h-10 px-5 text-base',
|
|
icon: 'size-5',
|
|
endIcon: 'size-5',
|
|
label: 'font-bold tracking-wider uppercase',
|
|
},
|
|
},
|
|
iconOnly: {
|
|
true: {
|
|
base: 'p-0',
|
|
label: 'sr-only',
|
|
},
|
|
},
|
|
color: {
|
|
black: '',
|
|
white: '',
|
|
gray: '',
|
|
success: '',
|
|
danger: '',
|
|
warning: '',
|
|
info: '',
|
|
},
|
|
variant: {
|
|
solid: '',
|
|
faded: '',
|
|
},
|
|
shadow: {
|
|
true: {
|
|
base: 'shadow-lg',
|
|
},
|
|
},
|
|
disabled: {
|
|
true: {
|
|
base: 'cursor-not-allowed',
|
|
},
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
// Color variants - solid
|
|
{
|
|
color: 'black',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-night-500 bg-night-800 hover:bg-night-900 hover:text-day-200 focus-visible:bg-night-500 text-white/50 focus-visible:text-white focus-visible:ring-white',
|
|
},
|
|
},
|
|
{
|
|
color: 'white',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-day-300 bg-day-100 hover:bg-day-200 text-black focus-visible:ring-green-500',
|
|
},
|
|
},
|
|
{
|
|
color: 'gray',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-day-500 bg-day-400 hover:bg-day-500 text-black focus-visible:ring-white',
|
|
},
|
|
},
|
|
{
|
|
color: 'success',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-green-600 bg-green-500 text-black hover:bg-green-600',
|
|
},
|
|
},
|
|
{
|
|
color: 'danger',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-red-600 bg-red-500 text-white hover:bg-red-600',
|
|
},
|
|
},
|
|
{
|
|
color: 'warning',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-yellow-600 bg-yellow-500 text-white hover:bg-yellow-600',
|
|
},
|
|
},
|
|
{
|
|
color: 'info',
|
|
variant: 'solid',
|
|
class: {
|
|
base: 'border-blue-600 bg-blue-500 text-white hover:bg-blue-600',
|
|
},
|
|
},
|
|
// Color variants - faded
|
|
{
|
|
color: 'black',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'bg2night-800/15 hover:bg-night-700/30 border-current/30 text-white/70 hover:text-white/90 focus-visible:ring-white/50',
|
|
},
|
|
},
|
|
{
|
|
color: 'white',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'b2-day-100/15 hover:bg-day-200/30 border-current/30 text-white/70 hover:text-white/90 focus-visible:ring-white/50',
|
|
},
|
|
},
|
|
{
|
|
color: 'gray',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'b2-day-400/15 hover:bg-day-500/30 text-day-300 hover:text-day-100 border-current/30 focus-visible:ring-white/50',
|
|
},
|
|
},
|
|
{
|
|
color: 'success',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'border-current/20 bg-green-500/15 text-green-300 hover:bg-green-500/30 hover:text-green-100',
|
|
},
|
|
},
|
|
{
|
|
color: 'danger',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'border-current/20 bg-red-500/15 text-red-300 hover:bg-red-500/30 hover:text-red-100',
|
|
},
|
|
},
|
|
{
|
|
color: 'warning',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'border-current/20 bg-yellow-500/15 text-yellow-300 hover:bg-yellow-500/30 hover:text-yellow-100',
|
|
},
|
|
},
|
|
{
|
|
color: 'info',
|
|
variant: 'faded',
|
|
class: {
|
|
base: 'border-current/20 bg-blue-500/15 text-blue-300 hover:bg-blue-500/30 hover:text-blue-100',
|
|
},
|
|
},
|
|
// Shadow variants
|
|
{
|
|
color: 'black',
|
|
shadow: true,
|
|
class: 'shadow-black/30',
|
|
},
|
|
{
|
|
color: 'white',
|
|
shadow: true,
|
|
class: 'shadow-white/30',
|
|
},
|
|
{
|
|
color: 'gray',
|
|
shadow: true,
|
|
class: 'shadow-day-500/30',
|
|
},
|
|
{
|
|
color: 'success',
|
|
shadow: true,
|
|
class: 'shadow-green-500/30',
|
|
},
|
|
{
|
|
color: 'danger',
|
|
shadow: true,
|
|
class: 'shadow-red-500/30',
|
|
},
|
|
{
|
|
color: 'warning',
|
|
shadow: true,
|
|
class: 'shadow-yellow-500/30',
|
|
},
|
|
{
|
|
color: 'info',
|
|
shadow: true,
|
|
class: 'shadow-blue-500/30',
|
|
},
|
|
// Icon only variants
|
|
{
|
|
iconOnly: true,
|
|
size: 'sm',
|
|
class: 'w-8',
|
|
},
|
|
{
|
|
iconOnly: true,
|
|
size: 'md',
|
|
class: 'w-9',
|
|
},
|
|
{
|
|
iconOnly: true,
|
|
size: 'lg',
|
|
class: 'w-10',
|
|
},
|
|
],
|
|
defaultVariants: {
|
|
size: 'md',
|
|
color: 'black',
|
|
variant: 'solid',
|
|
shadow: false,
|
|
disabled: false,
|
|
iconOnly: false,
|
|
},
|
|
})
|
|
|
|
const {
|
|
as: Tag = 'button' as 'a' | 'button' | 'label',
|
|
label,
|
|
icon,
|
|
endIcon,
|
|
size,
|
|
color,
|
|
variant,
|
|
shadow,
|
|
class: className,
|
|
classNames,
|
|
role,
|
|
dataAstroReload,
|
|
disabled,
|
|
inlineIcon,
|
|
iconOnly,
|
|
...htmlProps
|
|
} = Astro.props
|
|
|
|
const {
|
|
base,
|
|
icon: iconSlot,
|
|
label: labelSlot,
|
|
endIcon: endIconSlot,
|
|
} = button({
|
|
size,
|
|
color,
|
|
variant,
|
|
shadow,
|
|
disabled,
|
|
iconOnly: iconOnly ?? (!label && !(!!icon && !!endIcon)),
|
|
})
|
|
|
|
const ActualTag = disabled && Tag === 'a' ? 'span' : Tag
|
|
---
|
|
|
|
<ActualTag
|
|
class={base({ class: cn({ 'opacity-20 hover:opacity-50': disabled }, className) })}
|
|
role={role ??
|
|
(ActualTag === 'button' || ActualTag === 'label' || ActualTag === 'span' ? undefined : 'button')}
|
|
aria-disabled={disabled}
|
|
{...dataAstroReload && { 'data-astro-reload': dataAstroReload }}
|
|
{...htmlProps}
|
|
>
|
|
{!!icon && <Icon name={icon} class={iconSlot({ class: classNames?.icon })} is:inline={inlineIcon} />}
|
|
<span class={labelSlot({ class: classNames?.label })}>{label}</span>
|
|
{
|
|
!!endIcon && (
|
|
<Icon name={endIcon} class={endIconSlot({ class: classNames?.endIcon })} is:inline={inlineIcon}>
|
|
{endIcon}
|
|
</Icon>
|
|
)
|
|
}
|
|
</ActualTag>
|