import * as React from 'react' import { Avatar as BaseAvatar } from '@base-ui/react/avatar' import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '../../internal/utils' const thumbnailVariants = cva( "relative inline-flex size-[1em] shrink-0 items-center justify-center overflow-hidden [&_svg:not([class*='size-'])]:size-[0.5em]", { variants: { size: { '2xs': 'text-[1.25rem]', xs: 'text-[1.5rem]', sm: 'text-[2rem]', md: 'text-[2.5rem]', lg: 'text-[3rem]', xl: 'text-[4rem]', '2xl': 'text-[5rem]', }, shape: { rounded: 'rounded-[calc(tan(atan2(var(--radius-md),2.5rem))*100%)]', circle: 'rounded-full', }, variant: { image: 'bg-background-muted', 'icon-soft': 'bg-background-muted text-foreground-intense', 'icon-outline': 'bg-background text-foreground-intense border border-border', 'icon-primary': 'bg-primary text-primary-foreground', 'icon-primary-outline': 'text-primary border border-primary', 'icon-secondary': 'bg-secondary-muted text-secondary-foreground', 'icon-error': 'bg-error-muted text-error-foreground', 'icon-success': 'bg-success-muted text-success-foreground', 'icon-warning': 'bg-warning-muted text-warning-foreground', 'icon-info': 'bg-info-muted text-info-foreground', }, }, }, ) type ThumbnailPresetSize = NonNullable['size']> type ThumbnailShape = NonNullable['shape']> type ThumbnailVariant = NonNullable['variant']> type BaseImageProps = React.ComponentProps interface ThumbnailProps extends Omit, 'size' | 'children'> { /** * Image tile, or a colored icon tile. * @default 'image' */ variant?: ThumbnailVariant /** * Rounded square or full circle. * @default 'rounded' */ shape?: ThumbnailShape /** * A preset scale, or a pixel number for an exact size. * @default 'md' */ size?: ThumbnailPresetSize | number /** Image URL (image variant). Lifted from `render` when omitted. */ src?: string /** * Alternative text describing the image. Defaults to `""` (decorative) when omitted. * @default '' */ alt?: string /** Swap the `` for another element - e.g. a Next.js ``. */ render?: BaseImageProps['render'] /** Fires as the image moves through its loading lifecycle. */ onLoadingStatusChange?: BaseImageProps['onLoadingStatusChange'] /** The icon to frame (icon variants). */ children?: React.ReactNode } function Thumbnail({ className, style, variant = 'image', shape = 'rounded', size = 'md', src, alt, render, onLoadingStatusChange, children, ...props }: ThumbnailProps) { const isNumeric = typeof size === 'number' const variantClass = thumbnailVariants({ variant, shape, size: isNumeric ? undefined : size, }) const numericStyle = isNumeric ? { fontSize: `${size}px` } : undefined const mergedStyle = { ...numericStyle, ...style } const mergedClassName = cn(variantClass, className) if (variant === 'image') { const lifted = React.isValidElement<{ src?: string; alt?: string }>(render) && render.props ? render.props : undefined if (process.env.NODE_ENV !== 'production' && children != null) { // eslint-disable-next-line no-console console.warn('[Thumbnail] `children` is ignored when `variant="image"`.') } return ( } {...(props as React.ComponentProps)} > ) } return (
{children}
) } function ImageFallbackIcon() { return ( ) } export { Thumbnail } export type { ThumbnailProps }