Back to Onyx

LineItemButton

web/lib/opal/src/components/buttons/line-item-button/README.md

4.8.0-cloud.15.7 KB
Original Source

LineItemButton

Import: import { LineItemButton, type LineItemButtonProps } from "@opal/components";

A composite component that wraps Interactive.Stateful > Interactive.Container > ContentAction into a single API. Use it for selectable list rows such as model pickers, menu items, or any row that acts like a button.

Architecture

Interactive.Stateful         <- selectVariant, state, interaction, onClick, href, ref
  └─ Interactive.Container   <- width, rounding
       └─ ContentAction      <- withInteractive, padding
            ├─ Content       <- icon, title, description, sizePreset, variant, ...
            └─ rightChildren

The row renders as a focusable <div role="button"> (with Enter/Space activation) rather than a native <button>, so interactive rightChildren such as action buttons don't produce invalid button-in-button nesting. With href it renders an anchor instead.

withInteractive is always true and is not exposed. padding is forwarded to the inner ContentAction, on top of the row's own p-1.5 inset.

It is not an open Spacing: the prop is inherited from ContentActionProps, which narrows it to 0 | 0.5 | 1 | 2 — the four paddings Interactive.Container applies at its size presets, so that a row's label lines up with an adjacent button. A step outside that set is a type error.

Props

Interactive surface

PropTypeDefaultDescription
selectVariant"select-light" | "select-heavy""select-light"Interactive select variant
stateInteractiveStatefulState"empty"Value state ("empty", "filled", "selected")
interactionInteractiveStatefulInteraction"rest"JS-controlled interaction state override
onClickMouseEventHandler<HTMLElement>Click handler
hrefstringRenders an anchor instead of a div
targetstringAnchor target (e.g. "_blank")
groupstringInteractive group key
refReact.Ref<HTMLElement>Forwarded ref
disabledbooleanfalseDisabled colors; suppresses the row's own click only — nested rightChildren stay clickable

Sizing

PropTypeDefaultDescription
roundingRounding3Corner radius step (N / 4 rem, or "full"); height is content-driven
widthWidthVariant"full"Container width
padding0 | 0.5 | 1 | 20.5Padding around the inner ContentAction, as a spacing step (N / 4 rem)
tooltipstringTooltip text shown on hover
tooltipSideTooltipSide"top"Tooltip side

Content (pass-through to ContentAction)

PropTypeDefaultDescription
titlestring(required)Row label
iconIconFunctionComponentLeft icon
descriptionstringDescription below the title
sizePresetSizePreset"headline"Content size preset
variantContentVariant"heading"Content layout variant
rightChildrenReactNodeContent after the label (e.g. action button)
colorColorTypes"interactive"Content colour mode. Defaults to "interactive", which is what lets the row's hover / selected / disabled colours reach its title and icon — passing anything else opts out of that. undefined counts as not passing one.

All other ContentAction / Content props (editable, onTitleChange, optional, auxIcon, tag, etc.) are also passed through. Note: withInteractive is always true inside LineItemButton and cannot be overridden.

Usage

tsx
import { LineItemButton } from "@opal/components";

// Simple selectable row
<LineItemButton
  selectVariant="select-heavy"
  state={isSelected ? "selected" : "empty"}
  rounding={2}
  onClick={handleClick}
  title="gpt-4o"
  sizePreset="main-ui"
  variant="section"
/>

// With right-side action
<LineItemButton
  selectVariant="select-heavy"
  state={isSelected ? "selected" : "empty"}
  onClick={handleClick}
  title="claude-opus-4"
  sizePreset="main-ui"
  variant="section"
  rightChildren={<Tag title="Default" color="blue" />}
/>