PhoenixPaper.Chip (PhoenixPaper v0.1.0)

Copy Markdown View Source

A compact element for input, attribute, or action (pp_chip/1), in the spirit of MUI's Chip.

<.pp_chip>Basic</.pp_chip>
<.pp_chip variant="outlined" color="primary">Outlined</.pp_chip>

<.pp_chip deletable on_delete={JS.push("remove_tag", value: %{tag: "react"})}>
  React
  <:icon><.pp_icon name="hero-check" /></:icon>
</.pp_chip>

<.pp_chip clickable phx-click="select_filter" phx-value-id="unread">
  Unread
</.pp_chip>

clickable (default false) picks the root element: a real <button> (gets native keyboard/focus/disabled handling and a Button-style ripple for free — see PhoenixPaper.Ripple) when true, a plain <div> otherwise, the same conditional-root-tag approach PhoenixPaper.ListItem uses for link-vs-static (see AGENTS.md) — HEEx can't parameterize a tag name, so this is two :if/:if={!...} branches sharing one private chip_content/1 for the icon/label/delete markup. Pass phx-click through rest (like PhoenixPaper.ToggleButton) for the click itself; clickable={false} (the default) with deletable={true} is exactly MUI's "chip with a delete affordance but no other interaction" case — a static tag the caller can still remove.

The delete "button" (only rendered when deletable is true) is deliberately a <span role="button" tabindex="0">, not a real <button> — a real <button> nested inside clickable's own <button> root would be invalid HTML (browsers auto-close the outer one, breaking the whole chip's layout). A small onkeydown snippet (Enter/Space triggers a synthetic click, same "small vanilla snippet, no hook" precedent as PhoenixPaper.Ripple) keeps it keyboard-operable despite not being a real button.

Does not call event.stopPropagation() — an earlier version did, to stop clicking delete on a clickable chip from also firing the chip's own click, but that broke on_delete entirely: LiveView's phx-click binding is one delegated window-level listener (bound during the bubble phase), so stopPropagation() on the delete span prevented the click from ever reaching it, and the delete control silently did nothing. Confirmed with a real click in a real browser, not just a static render — the rendered markup and phx-click attribute both looked correct in isolation. Unnecessary anyway: LiveView resolves a click to the nearest phx-click-bearing ancestor-or-self via closestPhxBinding (starting from the actual event target and walking up), so a click on the delete span already resolves to the delete span's own phx-click, never the outer button's — no manual propagation-stopping needed for that.

disabled dims and disables both the root (when clickable, a real disabled attribute; when not, pointer-events-none — a plain <div> has no native disabled) and the delete control (pointer-events-none plus tabindex="-1", removing it from the tab order) — it isn't gated behind clickable since deletable-only chips (no other interaction) can still need to be disabled.

color="default" (gray, using --color-pp-surface-variant/ -on-surface/-outline — the same neutral tokens Input/Select/ NumberField already use for their filled backgrounds) is the default here, unlike every other colored component in this library — a plain tag chip (MUI's most common real-world case) is neutral, not brand-colored. Every other color value (primary/secondary/tertiary/error, plus success/warning/info for status, see PhoenixPaper.Alert) is also available.

clickable's hover/active feedback is one filter: brightness() step (hover:brightness-95 active:brightness-90) applied uniformly across every color/variant combination, rather than a hand-picked color-matched tint per branch the way PhoenixPaper.Button's outlined/ text variants do (hover:bg-pp-primary/10, etc.) — simpler, and the one place a chip's hover feedback is a little more subtle for an outlined/transparent-background chip than for a filled one, since a brightness filter has less visible effect over a transparent background.

Summary

Functions

Renders a chip. See the module doc.

Functions

pp_chip(assigns)

Renders a chip. See the module doc.

Attributes

  • paperize (:boolean) - Defaults to true.
  • variant (:string) - Defaults to "filled". Must be one of "filled", or "outlined".
  • color (:string) - Defaults to "default". Must be one of "default", "primary", "secondary", "tertiary", "error", "success", "warning", or "info".
  • size (:string) - Defaults to "medium". Must be one of "small", or "medium".
  • clickable (:boolean) - renders as a real <button> with hover/focus/ripple, for filter/action chips. Defaults to false.
  • ripple (:boolean) - the Material ripple effect on click/tap when clickable — off whenever paperize is false, see PhoenixPaper.Ripple. Defaults to true.
  • disabled (:boolean) - Defaults to false.
  • type (:string) - Defaults to "button". Must be one of "button", "submit", or "reset".
  • deletable (:boolean) - renders a trailing delete (x) control wired to on_delete. Defaults to false.
  • on_delete (Phoenix.LiveView.JS) - JS command run when the delete control is clicked, e.g. JS.push("remove_chip"). Defaults to %Phoenix.LiveView.JS{ops: []}.
  • class (:any) - Defaults to nil.
  • Global attributes are accepted. Supports all globals plus: ["form", "name", "value", "phx-click"].

Slots

  • icon - a leading icon or avatar.
  • inner_block (required) - the chip's label.