"use client"
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "../../lib/utils"
import {
STATUS_BADGE_TONE_CLASS,
type StatusBadgeTone,
} from "../../lib/status-badge-tints"
/**
* StatusBadge — centralized chips for product chrome and semantic entity status.
*
* **Product** (`status`): Beta / New / Alpha on nav, tabs, cards. Uppercase, solid fills.
* **Semantic** (`label` + `tone` + `icon`): Published, In review, Draft anywhere in the app.
* Use `size="sm"` (default) or `size="md"` for density — not location-specific props.
*
* Usage:
*
*
*
*/
export type StatusBadgeProductStatus =
| "beta"
| "new"
| "alpha"
| "preview"
| "deprecated"
export type StatusBadgeSemanticSize = "sm" | "md"
/** @deprecated Use `StatusBadgeSemanticSize` */
export type StatusBadgeSurface = "table" | "board" | "detail"
/** Default density — tables, lists, filters, inline rows. */
export const STATUS_BADGE_SEMANTIC_SM_SHELL =
"inline-flex w-fit shrink-0 items-center gap-1.5 px-2.5 py-1 text-xs font-medium leading-none"
/** Roomier density — cards, headers, panels. */
export const STATUS_BADGE_SEMANTIC_MD_SHELL =
"inline-flex h-6 min-h-6 w-fit shrink-0 items-center gap-1.5 px-2.5 py-0 text-xs font-medium leading-none border-0 shadow-none"
/** @deprecated Use `STATUS_BADGE_SEMANTIC_SM_SHELL` */
export const STATUS_BADGE_TABLE_SHELL = STATUS_BADGE_SEMANTIC_SM_SHELL
/** @deprecated Use `STATUS_BADGE_SEMANTIC_MD_SHELL` */
export const STATUS_BADGE_BOARD_SHELL = STATUS_BADGE_SEMANTIC_MD_SHELL
/** @deprecated Use `STATUS_BADGE_SEMANTIC_MD_SHELL` */
export const STATUS_BADGE_INSPECTOR_SHELL = STATUS_BADGE_SEMANTIC_MD_SHELL
const productStatusBadgeVariants = cva(
"inline-flex shrink-0 items-center justify-center font-semibold leading-none tracking-wide uppercase select-none border border-transparent",
{
variants: {
status: {
beta:
"bg-yellow-400 text-yellow-950 hc:bg-transparent hc:border-foreground hc:text-foreground forced-colors:bg-[Canvas] forced-colors:text-[CanvasText] forced-colors:border-[CanvasText]",
new:
"bg-brand text-brand-foreground hc:bg-transparent hc:border-foreground hc:text-foreground forced-colors:bg-[Highlight] forced-colors:text-[HighlightText] forced-colors:border-[HighlightText]",
alpha:
"bg-orange-500 text-white hc:bg-transparent hc:border-foreground hc:text-foreground forced-colors:bg-[Canvas] forced-colors:text-[CanvasText] forced-colors:border-[CanvasText]",
preview:
"bg-sky-500 text-white hc:bg-transparent hc:border-foreground hc:text-foreground forced-colors:bg-[Canvas] forced-colors:text-[CanvasText] forced-colors:border-[CanvasText]",
deprecated:
"bg-muted text-muted-foreground border-border hc:border-foreground forced-colors:bg-[Canvas] forced-colors:text-[GrayText] forced-colors:border-[GrayText]",
},
size: {
xs: "h-4 min-w-4 px-1 text-xs rounded-sm",
sm: "h-4 min-w-4 px-1.5 text-xs rounded-full",
md: "h-5 min-w-5 px-1.5 text-xs rounded-full",
},
variant: {
pill: "",
dot: "size-2 min-w-0 p-0 rounded-full",
},
},
defaultVariants: {
status: "beta",
size: "sm",
variant: "pill",
},
},
)
const semanticStatusBadgeVariants = cva("rounded-4xl", {
variants: {
size: {
sm: STATUS_BADGE_SEMANTIC_SM_SHELL,
md: STATUS_BADGE_SEMANTIC_MD_SHELL,
},
},
defaultVariants: {
size: "sm",
},
})
const PRODUCT_STATUS_LABEL: Record = {
beta: "Beta",
new: "New",
alpha: "Alpha",
preview: "Preview",
deprecated: "Deprecated",
}
function resolveSemanticSize(
size?: StatusBadgeSemanticSize,
surface?: StatusBadgeSurface,
): StatusBadgeSemanticSize {
if (size) return size
if (surface === "board" || surface === "detail") return "md"
return "sm"
}
type StatusBadgeBaseProps = Omit, "children">
export type StatusBadgeProductProps = StatusBadgeBaseProps &
VariantProps & {
status: StatusBadgeProductStatus
label?: string
tone?: never
icon?: never
tintClassName?: never
surface?: never
}
export type StatusBadgeSemanticProps = StatusBadgeBaseProps &
VariantProps & {
label: string
/** Font Awesome suffix (e.g. `fa-circle-check`). Shown at sm + md when set. */
icon?: string
tone?: StatusBadgeTone
tintClassName?: string
status?: never
variant?: never
/** @deprecated Use `size`. Maps table → sm; board | detail → md. */
surface?: StatusBadgeSurface
}
export type StatusBadgeProps = StatusBadgeProductProps | StatusBadgeSemanticProps
export function StatusBadge(props: StatusBadgeProps) {
if (props.status === undefined) {
const semantic = props as StatusBadgeSemanticProps
const {
className,
label,
icon,
tone,
tintClassName,
size,
surface,
"aria-label": ariaLabel,
...rest
} = semantic
const resolvedSize = resolveSemanticSize(size ?? undefined, surface)
const tint =
tintClassName ?? (tone ? STATUS_BADGE_TONE_CLASS[tone] : undefined)
return (
{icon ? (
) : null}
{label}
)
}
const {
className,
status = "beta",
size = "sm",
variant = "pill",
label,
"aria-label": ariaLabel,
...rest
} = props
const visibleLabel = label ?? PRODUCT_STATUS_LABEL[status]
const a11yLabel = ariaLabel ?? visibleLabel
return (
{variant === "dot" ? (
{a11yLabel}
) : (
visibleLabel
)}
)
}
export { productStatusBadgeVariants as statusBadgeVariants, STATUS_BADGE_TONE_CLASS }
export type { StatusBadgeTone }