Flicker.FacetEditor.Dial (Flicker v0.1.0)

Copy Markdown View Source

The numeric and duration editor (Spec 019): a dual-thumb slider when the facet is bounded, paired numeric inputs either way.

The slider is for feel and the inputs are for exactness; they are two views of one value. Both exist because either alone is worse — a slider can't express 10.. and a text box can't show you where the data sits.

Which is the point of the open bound. A control that cannot say "over 100" is worse than the two text boxes it replaced, so an endpoint left blank serialises to 10.. or ..50 and is reachable from both views.

Unbounded facets get the inputs alone: a slider needs endpoints, and inventing them would be a lie about the data.

Summary

Functions

Clamps a would-be endpoint so the thumbs can't cross — the lower may equal the upper, but never pass it.

At least one endpoint is required. Both open would mean "don't filter", which is what removing the facet is for.

Delegates to the facet's own casting, so a dial can never produce a token the parser rejects.

A range as a range literal, keeping an open end open; a scalar bare.

A thumb's position as a percentage of the facet's bounds, clamped to 0..100.

Functions

clamp_endpoint(arg1, value, range)

@spec clamp_endpoint(:from | :to, number(), Flicker.Facet.Range.t()) :: number()

Clamps a would-be endpoint so the thumbs can't cross — the lower may equal the upper, but never pass it.

Examples

iex> Flicker.FacetEditor.Dial.clamp_endpoint(:from, 80, %Flicker.Facet.Range{from: 10, to: 50})
50

iex> Flicker.FacetEditor.Dial.clamp_endpoint(:to, 5, %Flicker.Facet.Range{from: 10, to: 50})
10

iex> Flicker.FacetEditor.Dial.clamp_endpoint(:from, 20, %Flicker.Facet.Range{from: 10, to: 50})
20

iex> Flicker.FacetEditor.Dial.clamp_endpoint(:from, 80, %Flicker.Facet.Range{to: nil})
80

complete?(value, facet)

@spec complete?(term(), Flicker.Facet.t()) :: boolean()

At least one endpoint is required. Both open would mean "don't filter", which is what removing the facet is for.

Examples

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> Flicker.FacetEditor.Dial.complete?(%Flicker.Facet.Range{from: 10}, facet)
true

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> Flicker.FacetEditor.Dial.complete?(%Flicker.Facet.Range{}, facet)
false

iex> facet = Flicker.Facet.new(key: :n, type: :integer)
...> Flicker.FacetEditor.Dial.complete?(5, facet)
true

parse(text, facet)

@spec parse(String.t(), Flicker.Facet.t()) :: {:ok, term()} | :error

Delegates to the facet's own casting, so a dial can never produce a token the parser rejects.

Examples

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> {:ok, range} = Flicker.FacetEditor.Dial.parse("10..50", facet)
...> {range.from, range.to}
{10, 50}

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> Flicker.FacetEditor.Dial.parse("nonsense", facet)
:error

serialise(value, facet)

@spec serialise(term(), Flicker.Facet.t()) :: String.t()

A range as a range literal, keeping an open end open; a scalar bare.

Examples

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> Flicker.FacetEditor.Dial.serialise(%Flicker.Facet.Range{from: 10, to: 50}, facet)
"10..50"

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> Flicker.FacetEditor.Dial.serialise(%Flicker.Facet.Range{from: 10, to: nil}, facet)
"10.."

iex> facet = Flicker.Facet.new(key: :price, type: :number_range)
...> Flicker.FacetEditor.Dial.serialise(%Flicker.Facet.Range{from: nil, to: 50}, facet)
"..50"

iex> facet = Flicker.Facet.new(key: :length, type: :duration)
...> Flicker.FacetEditor.Dial.serialise(9000, facet)
"9000s"

thumb_percent(value, bounds)

@spec thumb_percent(number() | nil, map() | nil) :: float() | nil

A thumb's position as a percentage of the facet's bounds, clamped to 0..100.

Returns nil for an open endpoint or an unbounded facet — there is no meaningful position for either, and guessing one would misrepresent the value.

Examples

iex> Flicker.FacetEditor.Dial.thumb_percent(50, %{min: 0, max: 100})
50.0

iex> Flicker.FacetEditor.Dial.thumb_percent(150, %{min: 0, max: 100})
100.0

iex> Flicker.FacetEditor.Dial.thumb_percent(nil, %{min: 0, max: 100})
nil

iex> Flicker.FacetEditor.Dial.thumb_percent(50, nil)
nil