| 1 | // Ported from DeepSeek Harness c291e7961a (MIT). |
| 2 | // Pill: capsule at the 24px text-line size, selectable when given `onClick` |
| 3 | // (view switcher tabs, filters) and a static span otherwise — TerminalBlock's |
| 4 | // exit status is the read-only case. The 11px read-only badge is `Tag`; size |
| 5 | // separates the two as much as interactivity does. |
| 6 | |
| 7 | import type { ButtonHTMLAttributes, ReactNode } from 'react' |
| 8 | const clsx = (...values: Array<string | false | undefined>) => values.filter(Boolean).join(' ') |
| 9 | import css from './Pill.styles' |
| 10 | |
| 11 | /** |
| 12 | * Render a pill chip. Interactive when onClick is supplied (renders a button); |
| 13 | * otherwise a static span. |
| 14 | * @param props.active - selected/active visual state. |
| 15 | * @returns pill element. |
| 16 | */ |
| 17 | export function Pill({ active = false, className, children, onClick, ...rest }: { |
| 18 | active?: boolean |
| 19 | // `| undefined` so a caller can forward an optional class straight through |
| 20 | // under exactOptionalPropertyTypes (a CSS-module lookup is string|undefined). |
| 21 | className?: string | undefined |
| 22 | children?: ReactNode |
| 23 | } & ButtonHTMLAttributes<HTMLButtonElement>) { |
| 24 | if (!onClick) { |
| 25 | return <span className={clsx(css.pill, active && css.active, className)}>{children}</span> |
| 26 | } |
| 27 | return ( |
| 28 | <button |
| 29 | type="button" |
| 30 | className={clsx(css.pill, css.interactive, active && css.active, className)} |
| 31 | onClick={onClick} |
| 32 | {...rest} |
| 33 | > |
| 34 | {children} |
| 35 | </button> |
| 36 | ) |
| 37 | } |
| 38 |