"use client"; import { memo, type CSSProperties } from "react"; import type { ChipViewProps } from "./Chip.types"; function chipSurfaceStyle( state: ChipViewProps["state"], palette: ChipViewProps["palette"], size: ChipViewProps["size"], ): CSSProperties { const borderWidth = size === "s" ? "1.25px" : "2px"; const reset: CSSProperties = { appearance: "none", WebkitAppearance: "none", backgroundImage: "none", }; const paint = ( backgroundColor: string, color: string, borderColor?: string, ): CSSProperties => ({ ...reset, backgroundColor, color, WebkitTextFillColor: color, ...(borderColor ? { borderWidth, borderStyle: "solid", borderColor } : { borderWidth: 0, borderStyle: "none", borderColor: "transparent" }), }); if (palette === "inverse") { if (state === "disabled") { return paint( "var(--color-surface-inverse-tertiary)", "var(--color-content-inverse-primary)", ); } if (state === "selected") { return paint( "var(--color-surface-default-semi-opaque)", "var(--color-content-inverse-primary)", "var(--color-border-default-primary)", ); } return paint( "transparent", "var(--color-content-inverse-primary)", "var(--color-border-default-primary)", ); } if (state === "custom") { return paint( "var(--color-surface-default-secondary)", "var(--color-content-default-tertiary)", ); } if (state === "disabled") { return paint( "var(--color-surface-default-secondary)", "var(--color-content-invert-tertiary)", ); } if (state === "selected") { return paint( "var(--color-surface-invert-brand-primary, #fefcc9)", "var(--color-content-invert-primary, #000)", "var(--color-border-default-brand-primary, #fdfaa8)", ); } return paint( "transparent", "var(--color-content-default-brand-primary, #fefcc9)", "var(--color-border-default-tertiary, #464646)", ); } function ChipView({ label, state, palette, size, className = "", disabled = false, onClick, onRemove, onCheck, onClose, inputValue, onInputChange, onInputKeyDown, inputRef, ariaLabel, confirmAriaLabel, typeToAddPlaceholder, closeAriaLabel, }: ChipViewProps) { // The container is the source of truth for `disabled`. `state="disabled"` // is the non-interactive visual only — toggle groups use selected/unselected. const isDisabled = disabled ?? false; const isSelected = state === "selected"; const isCustom = state === "custom"; const isToggle = !isCustom; const isSmall = size === "s"; const surfaceStyle = chipSurfaceStyle(state, palette, size); // Size-based styles from Figma tokens // Custom state has different padding const sizeClasses = isCustom ? isSmall ? "px-[var(--measures-spacing-100,4px)] py-[3px] text-x-small-paragraph" : "px-[var(--measures-spacing-150,6px)] py-[10px] text-medium-paragraph" : isSmall ? "h-[30px] px-[var(--measures-spacing-200,8px)] gap-[var(--measures-spacing-050,2px)] text-x-small-label" : "px-[var(--measures-spacing-300,12px)] py-[var(--measures-spacing-300,12px)] gap-[var(--measures-spacing-150,6px)] text-medium-label"; const baseClasses = ` appearance-none inline-flex max-w-full items-center justify-center rounded-[var(--measures-radius-full,9999px)] overflow-clip box-border focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-default-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent transition-[background,border-color,color,box-shadow] duration-200 ease-in-out ` .trim() .replace(/\s+/g, " "); const stateClasses = isDisabled ? "cursor-not-allowed opacity-60" : "cursor-pointer"; const combinedClasses = [baseClasses, sizeClasses, stateClasses, className] .filter(Boolean) .join(" "); const handleClick = ( event: React.MouseEvent, ) => { if (isDisabled) { event.preventDefault(); return; } onClick?.(event as React.MouseEvent); }; const sharedA11y = { "aria-label": ariaLabel, ...(isToggle ? { "aria-pressed": isSelected } : {}), }; // Custom state rendering with check/close buttons if (isCustom) { return (
{ if (e.target instanceof HTMLInputElement) return; if (e.key === "Enter" || e.key === " ") { e.preventDefault(); handleClick(e as unknown as React.MouseEvent); } }} {...sharedA11y} >
{/* Check button */} {onCheck && ( )} {/* Input field */}
onInputChange?.(e.target.value)} onKeyDown={onInputKeyDown} placeholder={typeToAddPlaceholder} className="bg-transparent border-none outline-none flex-1 min-w-0 font-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[color:var(--color-content-default-tertiary,#b4b4b4)]" style={{ fontSize: isSmall ? "var(--sizing-300,12px)" : "var(--sizing-400,16px)", lineHeight: isSmall ? "16px" : "24px", }} onClick={(e) => e.stopPropagation()} onFocus={(e) => e.stopPropagation()} />
{/* Close button */} {onClose && ( )}
); } // Regular state rendering return ( )} ); } ChipView.displayName = "ChipView"; export default memo(ChipView);