Files
jellybloom/src/components/ui/Chip.tsx
T

80 lines
2.5 KiB
TypeScript

import { forwardRef, type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from 'react'
type ChipTone = 'neutral' | 'accent' | 'cool' | 'success' | 'warning' | 'error' | 'glow' | 'outline'
type ChipSize = 'xs' | 'sm' | 'md'
const toneClass: Record<ChipTone, string> = {
neutral: 'bg-white/8 text-text-1 border-white/8 hover:bg-white/12',
accent: 'bg-accent/15 text-accent border-accent/25 hover:bg-accent/20',
cool: 'bg-cool/12 text-cool border-cool/25',
success: 'bg-success/12 text-success border-success/25',
warning: 'bg-warning/12 text-warning border-warning/25',
error: 'bg-error/12 text-error border-error/25',
glow:
'bg-gradient-to-r from-accent/20 to-cool/15 text-text-1 border-accent/30 shadow-[0_0_12px_rgba(245,182,66,0.25)]',
outline: 'bg-transparent text-text-2 border-border hover:border-border-hover hover:text-text-1',
}
const sizeClass: Record<ChipSize, string> = {
xs: 'h-5 px-1.5 text-[10px] gap-1 rounded',
sm: 'h-6 px-2 text-[11px] gap-1.5 rounded-md',
md: 'h-7 px-2.5 text-[12px] gap-1.5 rounded-md',
}
interface ChipBase {
tone?: ChipTone
size?: ChipSize
icon?: ReactNode
trailing?: ReactNode
active?: boolean
}
export type ChipProps = ChipBase & HTMLAttributes<HTMLSpanElement> & { as?: 'span' }
export type ChipButtonProps = ChipBase & ButtonHTMLAttributes<HTMLButtonElement> & { as: 'button' }
export const Chip = forwardRef<HTMLSpanElement | HTMLButtonElement, ChipProps | ChipButtonProps>(
function Chip(props, ref) {
const {
tone = 'neutral',
size = 'sm',
icon,
trailing,
active,
className = '',
children,
as = 'span',
...rest
} = props as ChipBase & { as?: 'span' | 'button' } & Record<string, any>
const baseCls =
`inline-flex items-center font-medium tracking-tight border whitespace-nowrap select-none transition-colors duration-150 ${
sizeClass[size]
} ${toneClass[active ? 'accent' : tone]} ${className}`
if (as === 'button') {
return (
<button
{...(rest as ButtonHTMLAttributes<HTMLButtonElement>)}
ref={ref as React.Ref<HTMLButtonElement>}
className={`${baseCls} cursor-pointer focus-ring`}
>
{icon}
{children}
{trailing}
</button>
)
}
return (
<span
{...(rest as HTMLAttributes<HTMLSpanElement>)}
ref={ref as React.Ref<HTMLSpanElement>}
className={baseCls}
>
{icon}
{children}
{trailing}
</span>
)
},
)