PhoenixKitWeb.Components.Core.ChartLanes (phoenix_kit v2.29.1)

Copy Markdown View Source

Rows of horizontal bands on a chart's x axis — what was scheduled, on or booked over the stretch a chart above shows. No JavaScript.

A row is one thing (a device, a room, a person, a task); its bands are the stretches of x it occupies. Bands say nothing about what they mean: each carries a variant (how it is drawn) and a class (its colour), and the caller decides which of its own states map to which. Layer several bands in one row to show, say, a planned window as a dashed outline with the actually used part filled inside it.

Lining up with a chart

Bands are placed in percentages of the lanes' width with PhoenixKitWeb.Components.Core.ChartScale — the scale line_chart/1 draws with. Give both the same x_domain and the same width (one wrapper, no padding between them) and every band sits under the chart's x:

<div class="w-full">
  <div class="h-48">
    <.line_chart id="price" data={@prices} x_domain={{0, 1440}} step />
  </div>
  <.chart_lanes id="devices" rows={@device_rows} x_domain={{0, 1440}} marker_x={@now} />
</div>

Bands are HTML, not SVG, so dashed borders and rounded ends keep their shape however wide the page is.

Scrolling

With more rows than scroll_after, the list scrolls inside a fixed height while the chart above stays put. A classic (non-overlay) scrollbar takes a few pixels from the right edge of the scrolling list; when exact alignment matters at the right edge, give the chart the same right padding or set scroll_after={nil} and let the page scroll.

Origin: generalised from NordSwitch's device schedule under its price chart.

Summary

Functions

Renders rows of bands on a shared x axis.

Functions

chart_lanes(assigns)

Renders rows of bands on a shared x axis.

Rows

Each row is a map:

  • :id — optional stable id; the row's DOM id follows it, so a reordered list keeps each row's element
  • :label — what the row is (rendered over the row unless the :row_label slot is given)
  • :note — optional secondary text beside the label
  • :bands — a list of bands

Each band is a map:

  • :from, :to — x values, in the chart's units. nil is open-ended and runs to that edge of the domain. from == to is a point (a milestone, an event) and is drawn as a thin mark. A band outside the domain or running backwards is not drawn.
  • :variant — :fill (default), :soft, :outline or :dashed
  • :class — its colour, as a text colour class ("text-primary")
  • :label — optional text inside the band (a guest name on a booking)
  • :title — the native tooltip and the screen-reader text; defaults to the label, then the range through x_format

Bands are drawn in list order, so later bands sit on top. Any other keys are kept and handed to the :band slot.

Interactive bands

The :band slot renders inside each band and receives the band's own map, so a band can hold a real link or button — the accessible way to make it open a booking or a task:

<.chart_lanes id="bookings" rows={@rows} x_domain={{8, 20}}>
  <:band :let={band}>
    <button type="button" phx-click="open" phx-value-id={band.id} class="w-full h-full text-left px-1 truncate">
      {band.label}
    </button>
  </:band>
</.chart_lanes>

Examples

<.chart_lanes
  id="boilers"
  x_domain={{0, 1440}}
  marker_x={@now_minute}
  x_format={&clock_label/1}
  rows={[
    %{label: "Office boiler", note: "22 °C",
      bands: [
        %{from: 360, to: 540, variant: :dashed, class: "text-info", title: "Scheduled"},
        %{from: 380, to: 470, variant: :fill, class: "text-success", title: "Heating"}
      ]}
  ]}
/>

<%!-- a custom label --%>
<.chart_lanes id="rooms" rows={@rooms} x_domain={{8, 20}}>
  <:row_label :let={row}><.link navigate={row.path}>{row.label}</.link></:row_label>
</.chart_lanes>

Attributes

  • id (:string) (required)
  • rows (:list) (required) - Rows, each %{id, label, note, bands} (see above).
  • x_domain (:any) (required) - {low, high} — pass the chart's own x_domain so bands line up with it. Required: bands alone cannot say where the axis starts and ends.
  • marker_x (:any) - x for a dashed vertical line across every row. Defaults to nil.
  • ticks (:list) - x values for faint vertical gridlines (hours, days) across every row. Defaults to [].
  • x_format (:any) - 1-arity fun formatting an x for band titles (e.g. minutes to a clock time). Defaults to nil.
  • row_height (:any) - Row height in rem. Defaults to 2.
  • scroll_after (:any) - Rows shown before the list scrolls inside a fixed height; nil never scrolls. Defaults to 12.
  • class (:any) - Classes for the wrapper (set text-* for the marker). Defaults to nil.
  • aria_label (:string) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • row_label - Custom content over each row; receives the row map via :let.
  • band - Custom content inside each band; receives the band's map via :let.
  • empty - Shown when there are no rows or the domain is unusable.