shared ui: poster cards, content rows, scrollers, lazy mount
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
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>
|
||||
)
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user