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

Copy Markdown View Source

The horizontal scale PhoenixKitWeb.Components.Core.Chart.line_chart/1 draws with, for lining your own HTML up with a chart.

A line chart stretches its SVG across its wrapper, so an x value always lands at the same percentage of the wrapper's width, whatever size the page gives it. Anything positioned with left: <percent>% inside a box of the same width — a band, a label, a "now" line — sits exactly over the chart. PhoenixKitWeb.Components.Core.ChartLanes.chart_lanes/1 is built on this module; use it directly for overlays of your own.

domain = ChartScale.domain(@x_domain, Enum.map(@points, &elem(&1, 0)))
left = ChartScale.percent(domain, @now_minute)
# <div class="absolute inset-y-0" style={"left: #{left}%"}></div>

Pass the chart the same x_domain you give the overlay. Without one, the chart fits its own data, and the overlay only matches if it computes the domain from the same x values.

Like the charts, this module takes plain numbers (and Decimals). Map times, prices or dates to numbers first.

Summary

Types

An x range, {low, high}, low <= high.

Where a range sits across the chart, in percent of its width.

Functions

The x domain line_chart/1 uses: the explicit {low, high} when both bounds are numbers (a reversed pair is swapped), otherwise the lowest and highest of xs. nil when neither gives a number.

Where x sits across the domain, as a fraction: 0.0 at the low edge, 1.0 at the high edge. Not clamped — a value outside the domain lands outside 0..1, as it does on the chart. A domain with no width puts every value at the centre (0.5).

A chart value as a number: integers and floats as they are, a Decimal as a float, anything else nil.

fraction/2 as a percentage of the chart's width, for left: …%.

The part of the chart the range from..to covers, clipped to the domain: %{left: percent, width: percent}. A nil bound is open and runs to the domain's edge. nil when nothing of the range is left — it lies outside the domain, is empty, runs backwards, or a bound is not a number.

Types

domain()

@type domain() :: {number(), number()}

An x range, {low, high}, low <= high.

span()

@type span() :: %{left: float(), width: float()}

Where a range sits across the chart, in percent of its width.

Functions

domain(explicit, xs)

@spec domain(term(), [term()]) :: domain() | nil

The x domain line_chart/1 uses: the explicit {low, high} when both bounds are numbers (a reversed pair is swapped), otherwise the lowest and highest of xs. nil when neither gives a number.

An unusable explicit bound falls back to the data, never to a constant: a {0, 1} fallback would scale every real range off the chart.

iex> PhoenixKitWeb.Components.Core.ChartScale.domain({1440, 0}, [])
{0, 1440}

iex> PhoenixKitWeb.Components.Core.ChartScale.domain(nil, [30, 10, 20])
{10, 30}

iex> PhoenixKitWeb.Components.Core.ChartScale.domain({nil, 5}, [])
nil

fraction(arg, x)

@spec fraction(domain(), number()) :: float()

Where x sits across the domain, as a fraction: 0.0 at the low edge, 1.0 at the high edge. Not clamped — a value outside the domain lands outside 0..1, as it does on the chart. A domain with no width puts every value at the centre (0.5).

iex> PhoenixKitWeb.Components.Core.ChartScale.fraction({0, 1440}, 360)
0.25

numeric(value)

@spec numeric(term()) :: number() | nil

A chart value as a number: integers and floats as they are, a Decimal as a float, anything else nil.

iex> PhoenixKitWeb.Components.Core.ChartScale.numeric(Decimal.new("1.5"))
1.5

iex> PhoenixKitWeb.Components.Core.ChartScale.numeric("12")
nil

percent(domain, x)

@spec percent(domain(), number()) :: float()

fraction/2 as a percentage of the chart's width, for left: …%.

iex> PhoenixKitWeb.Components.Core.ChartScale.percent({0, 1440}, 720)
50.0

span(domain, from, to)

@spec span(domain(), term(), term()) :: span() | nil

The part of the chart the range from..to covers, clipped to the domain: %{left: percent, width: percent}. A nil bound is open and runs to the domain's edge. nil when nothing of the range is left — it lies outside the domain, is empty, runs backwards, or a bound is not a number.

iex> PhoenixKitWeb.Components.Core.ChartScale.span({0, 100}, 25, 50)
%{left: 25.0, width: 25.0}

iex> PhoenixKitWeb.Components.Core.ChartScale.span({0, 100}, 90, nil)
%{left: 90.0, width: 10.0}

iex> PhoenixKitWeb.Components.Core.ChartScale.span({0, 100}, 120, 150)
nil