Skip to content
team-alembicPublic

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Flicker

Flicker

Ash-native search, filtering, and record selection for Phoenix LiveView.

Searchable selects, multi-selects, faceted filter bars, and command palettes
backed directly by your data — no options plumbing required.

CI Apache 2.0 Elixir 1.17+ Phoenix LiveView 1.1+ Pre-release

Flicker is what Cinder is for tables, but for finding and picking records. Point it at an Ash resource, pass the actor, and get an authorised typeahead without loading every record into a giant options list.

Important

Flicker is under active development and has not been published to Hex yet. Install it from GitHub for evaluation. The public API may change before the first release.

Why Flicker?

  • Your resource is the data source. Give Flicker resource, search, and option_label; it builds and runs the Ash query itself.
  • Authorization stays in Ash. actor and tenant flow through every search and fetch. Flicker adds no parallel permission system.
  • Small core, clean escape hatch. Phoenix LiveView is the only hard runtime dependency. Ash support is optional, and any module implementing Flicker.Provider can search an API, cache, index, or several resources.
  • One interaction model, four surfaces. Single-select, multi-select, standalone faceted search, and a full command palette share the same query, provider, result, theming, and keyboard machinery.
  • Built for production details. Debounced async search, stale-result cancellation, batch resolution of existing values, reconnect-safe form inputs, windowed pagination, actor-scoped relationship facets, and user-facing error states are already covered.
  • Accessible by design. Editable-combobox semantics, active-descendant keyboard navigation, polite live-region announcements, a palette focus trap, overridable copy, axe-core scans, and real-browser keyboard tests ship in the repository. Manual assistive-technology testing is still in progress; see Accessibility.

What ships today

Surface Use it for Entry point
Searchable select Pick one record in a form or controlled LiveView Flicker.select/1
Multi-select Pick many records with chips or custom selected-item UI Flicker.select/1 with multiple
Faceted search Drive an Ash query, Cinder collection, stream, or list Flicker.search/1
Command palette Grouped global search with optional navigation Flicker.palette/1

The current implementation also includes:

  • form-field and controlled modes;
  • custom option and selected-item slots;
  • avatar stacks with max_visible overflow;
  • min_chars, debouncing, limits, base filters, sorting, and custom read actions;
  • opt-in infinite scroll with capped result windows;
  • Datadog-style facets such as status:active after:7d rock;
  • type-derived enum, boolean, date, number, and relationship suggestions;
  • removable facet pills in Flicker.search/1, including host-defined colour indicators;
  • global keyboard activation such as mod+k;
  • vanilla, Tailwind, and daisyUI theme presets;
  • fully overridable visible text and screen-reader announcements;
  • Cinder query and URL-state helpers;
  • an AshPhoenix form adapter and PhoenixTest helper;
  • an Igniter-powered installer; and
  • an in-repository playground with copyable examples.

See the spec index for shipped work, work in progress, and deliberately deferred ideas. In particular, Flicker.select/1 already parses facets and filters its records, but committed facets remain raw input text there; pill rendering inside selects is still a draft.

Install

Until the first Hex release, add the GitHub dependency:

def deps do
  [
    {:flicker, github: "team-alembic/flicker", branch: "main"},

    # Required only for the built-in Ash resource provider:
    {:ash, "~> 3.0"},
    {:ash_phoenix, "~> 2.0"}
  ]
end

Flicker requires Elixir 1.17 or later and Phoenix LiveView 1.1 or later. Its JavaScript is delivered through LiveView's colocated-hook dependency manifest.

If your project has Igniter, fetch the dependency and run the included installer:

mix deps.get
mix flicker.install

It imports Flicker into the formatter, adds the default limit and debounce configuration, and merges Flicker's colocated hooks into your LiveSocket. For manual installation, follow the exact hook wiring in the getting-started guide.

Pick a record

<Flicker.select
  id="client-select"
  field={@form[:client_id]}
  resource={MyApp.Client}
  actor={@current_user}
  search={[:first_name, :last_name, :uci_number]}
  option_label={:full_name}
  option_sublabel={fn client -> "#{client.uci_number} · #{client.city}" end}
  read_action={:search}
  limit={20}
/>

Pass a Phoenix.HTML.FormField for form mode, or replace field with an on_select message name for controlled mode:

<Flicker.select
  id="global-search"
  source={MyApp.Search.Global}
  actor={@current_user}
  on_select={:result_selected}
/>
def handle_info({:result_selected, result}, socket) do
  {:noreply, assign(socket, :selected, result)}
end

Exactly one of field or on_select is required.

Pick many

The same component becomes a multi-select with multiple. Existing values are resolved in one fetch/2 call, form mode emits array inputs, and controlled mode receives the complete selection list after every change.

<Flicker.select
  id="worker-select"
  field={@form[:worker_ids]}
  resource={MyApp.Worker}
  actor={@current_user}
  search={[:name]}
  option_label={:name}
  multiple
  max_selections={5}
/>

Use the :selected slot and max_visible for richer selected-item UI such as an avatar stack with a +N overflow token.

Filter with facets

Flicker.search/1 has no selection semantics. It parses the input, renders committed filters as removable pills, and sends the query plus its composed Ash filter to your LiveView.

<Flicker.search
  id="artist-search"
  resource={MyApp.Artist}
  actor={@current_user}
  facets={[
    {:status, value_colors: %{active: "#16a34a"}},
    :genre,
    after: [attribute: :formed_on, op: :>=]
  ]}
  on_change={:artist_query_changed}
/>
def handle_info({:artist_query_changed, _query, filter}, socket) do
  artists =
    MyApp.Artist
    |> Ash.Query.filter_input(filter)
    |> Ash.read!(actor: socket.assigns.current_user)

  {:noreply, assign(socket, :artists, artists)}
end

Distinct facet keys are ANDed; repeated values of the same facet are ORed. Unknown keys and malformed values degrade to free text instead of breaking the input. Read the faceted-search guide for the grammar and type-derived behavior.

Open a command palette

<Flicker.palette
  id="cmdk"
  source={MyApp.Search.Global}
  actor={@current_user}
  open={@palette_open}
  on_close={:palette_closed}
  on_select={:palette_selected}
/>

The palette opens with Cmd+K on macOS or Ctrl+K elsewhere. Results carrying a group render under group headings; a result with meta.href navigates with push_navigate/2 after selection. See the command-palette guide.

Bring any data source

Implement two callbacks to use a non-Ash or federated backend:

defmodule MyApp.Search.Global do
  @behaviour Flicker.Provider

  @impl true
  def search(%Flicker.Query{text: text}, opts) do
    MyApp.SearchIndex.search(text,
      actor: opts[:actor],
      tenant: opts[:tenant],
      limit: opts[:limit]
    )
  end

  @impl true
  def fetch(values, opts) do
    MyApp.SearchIndex.fetch_many(values,
      actor: opts[:actor],
      tenant: opts[:tenant]
    )
  end
end

Both callbacks return {:ok, [%Flicker.Result{}]} or {:error, reason}. Optional facets/0 and render_option/2 callbacks extend the surface. The provider guide documents the complete contract, pagination offset, and reference static provider.

Make it yours

Flicker uses a class-per-part %Flicker.Theme{} rather than owning your CSS framework:

config :flicker, default_theme: Flicker.Theme.tailwind()
<Flicker.select
  theme={%{search_input: "my-input", option_active: "my-active-option"}}
  ...
/>

Choose Flicker.Theme.vanilla/0, tailwind/0, or daisy_ui/0; replace a whole theme or merge only the parts you need. The theming guide contains the full part map.

Every visible string and live announcement goes through Flicker.Messages. Override the module globally or per component for localization and product voice without forking component markup.

Accessibility

Flicker implements WAI-ARIA editable-combobox behavior, keeps DOM focus on the input while moving aria-activedescendant, announces loading/results/ selection/error changes, supports the full keyboard map, and traps/restores focus in the command palette.

CI exercises axe-core against every playground route and drives the client-side keyboard behavior through Chrome. The remaining pre-1.0 accessibility work is the manual VoiceOver, NVDA, and JAWS matrix tracked in Spec 007. Read the accessibility statement and manual test script.

Documentation

Flicker’s API reference and user guides are built with ExDoc and are ready for HexDocs. Until the first Hex release, generate the same documentation locally:

mix deps.get
mix docs

Open doc/index.html to browse the complete guide set, grouped public API, providers, integrations, test helpers, and source links. Hex publishes this documentation automatically with the package; the canonical address will be https://hexdocs.pm/flicker.

Playground

The repository includes a database-free Phoenix app backed by seeded Ash ETS resources:

mix deps.get
mix dev

Visit http://localhost:4000 for live, copyable examples of the selection modes, custom slots, facets, pagination, keyboard activation, palette, themes, providers, Cinder integration, localization, and error states.

Development

Tool versions are pinned in .tool-versions. The main quality gate covers formatting, Credo, documentation coverage, Sobelow, dependency audits, Dialyzer, and ExUnit:

mix deps.get
mix check

The real-browser suite is separate because it requires Chrome and chromedriver:

mix test --only browser

Architecture and sequencing live in the design document; feature contracts live in specs; structural decisions live in ADRs. Contributions should follow AGENTS.md.

License

Flicker is released under the Apache License 2.0.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages