The editor for a facet whose values come from a set — an :enum's closed
picklist, or a relationship's related records
(Spec 019).
Both cases are driven by one thing — Flicker.Facet.value_source/1 — so the
enum and relationship editors are the same code with a different provider
behind them, which was
ADR-011's
actual goal.
Why this isn't literally a nested Flicker.select
ADR-011 said the set editor is a nested Flicker.select. Implementation
showed that doesn't work: Flicker.select's controlled mode sends
{on_select, result} with send(self(), ...), and self() inside a
Phoenix.LiveComponent is the host LiveView, not the enclosing component.
A nested select could therefore never hand its selection back to the editor
that contains it without every host adding a handle_info clause to forward
it — exactly the boilerplate a library shouldn't impose.
So this renders the value list itself, against the same value_source/1, with
its events targeted at the component that owns the editor. The unification
ADR-011 wanted is intact; the specific mechanism isn't. What is genuinely lost
is inheriting Flicker.select's own listbox behaviour for free — windowing
over a very large related set is the notable gap, and is why :count_limit
and a search box matter here.
Multi mode commits the whole selection as one :in token rather than
per-toggle, so widening a selection is one dispatch, not five. Facet editing
never recurses: this list has no facets of its own.
Summary
Functions
A non-empty selection is complete; nothing selected is not.
Splits a list literal for a multiple? facet, and casts through the facet so
an enum comes back as atoms rather than strings.
A single value bare, a multi selection comma-joined — exactly the list literal Spec 018's grammar accepts.
Functions
@spec complete?(term(), Flicker.Facet.t()) :: boolean()
A non-empty selection is complete; nothing selected is not.
Examples
iex> Flicker.FacetEditor.Set.complete?([:a], %Flicker.Facet{key: :s})
true
iex> Flicker.FacetEditor.Set.complete?([], %Flicker.Facet{key: :s})
false
iex> Flicker.FacetEditor.Set.complete?(nil, %Flicker.Facet{key: :s})
false
@spec parse(String.t(), Flicker.Facet.t()) :: {:ok, term()} | :error
Splits a list literal for a multiple? facet, and casts through the facet so
an enum comes back as atoms rather than strings.
Examples
iex> facet = Flicker.Facet.new(key: :status, type: :enum, values: [:a, :b], multiple?: true)
...> Flicker.FacetEditor.Set.parse("a,b", facet)
{:ok, [:a, :b]}
iex> facet = Flicker.Facet.new(key: :status, type: :enum, values: [:a])
...> Flicker.FacetEditor.Set.parse("a", facet)
{:ok, :a}
iex> facet = Flicker.Facet.new(key: :status, type: :enum, values: [:a])
...> Flicker.FacetEditor.Set.parse("nope", facet)
:error
@spec serialise(term(), Flicker.Facet.t()) :: String.t()
A single value bare, a multi selection comma-joined — exactly the list literal Spec 018's grammar accepts.
Examples
iex> facet = Flicker.Facet.new(key: :status, type: :enum, values: [:a, :b], multiple?: true)
...> Flicker.FacetEditor.Set.serialise([:a, :b], facet)
"a,b"
iex> facet = Flicker.Facet.new(key: :status, type: :enum, values: [:a])
...> Flicker.FacetEditor.Set.serialise(:a, facet)
"a"
iex> facet = Flicker.Facet.new(key: :worker, type: :string)
...> Flicker.FacetEditor.Set.serialise("Casey Nguyen", facet)
~s("Casey Nguyen")