# `Flicker.FacetEditor.Set`
[🔗](https://github.com/team-alembic/flicker/blob/v0.1.0/lib/flicker/facet_editor/set.ex#L1)

The editor for a facet whose values come from a set — an `:enum`'s closed
picklist, or a relationship's related records
([Spec 019](https://github.com/team-alembic/flicker/blob/main/docs/specs/spec-019-facet-editors.md)).

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](https://github.com/team-alembic/flicker/blob/main/docs/adrs/adr-011-facet-editors-are-modal-subcontexts.md)'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.

# `complete?`

```elixir
@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

# `parse`

```elixir
@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

# `serialise`

```elixir
@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")

---

*Consult [api-reference.md](api-reference.md) for complete listing*
