Corex.Combobox (Corex v0.1.0-alpha.33)

View Source

Phoenix implementation of Zag.js Combobox.

Minimal

<.combobox
      class="combobox"
      translation={%Corex.Combobox.Translation{placeholder: "Select a country", empty: "No results"}}
      collection={[
        %{label: "France", id: "fra", disabled: true},
        %{label: "Belgium", id: "bel"},
        %{label: "Germany", id: "deu"},
        %{label: "Netherlands", id: "nld"},
        %{label: "Switzerland", id: "che"},
        %{label: "Austria", id: "aut"}
      ]}
    >
      <:trigger>
        <.heroicon name="hero-chevron-down" />
      </:trigger>
    </.combobox>

Grouped

<.combobox
      class="combobox"
      translation={%Corex.Combobox.Translation{placeholder: "Select a country", empty: "No results"}}
      collection={[
        %{label: "France", id: "fra", group: "Europe"},
        %{label: "Belgium", id: "bel", group: "Europe"},
        %{label: "Germany", id: "deu", group: "Europe"},
        %{label: "Netherlands", id: "nld", group: "Europe"},
        %{label: "Switzerland", id: "che", group: "Europe"},
        %{label: "Austria", id: "aut", group: "Europe"},
        %{label: "Japan", id: "jpn", group: "Asia"},
        %{label: "China", id: "chn", group: "Asia"},
        %{label: "South Korea", id: "kor", group: "Asia"},
        %{label: "Thailand", id: "tha", group: "Asia"},
        %{label: "USA", id: "usa", group: "North America"},
        %{label: "Canada", id: "can", group: "North America"},
        %{label: "Mexico", id: "mex", group: "North America"}
      ]}
    >
      <:trigger>
        <.heroicon name="hero-chevron-down" />
      </:trigger>
    </.combobox>

Extended

This example requires the installation of Flagpack to display the use of custom item rendering.

  <.combobox
      class="combobox"
      translation={%Corex.Combobox.Translation{placeholder: "Select a country", empty: "No results"}}
      collection={[
        %{label: "France", id: "fra"},
        %{label: "Belgium", id: "bel"},
        %{label: "Germany", id: "deu"},
        %{label: "Netherlands", id: "nld"},
        %{label: "Switzerland", id: "che"},
        %{label: "Austria", id: "aut"}
      ]}
    >
      <:item :let={item}>
        <Flagpack.flag name={String.to_atom(item.id)} />
        {item.label}
      </:item>
      <:trigger>
        <.heroicon name="hero-chevron-down" />
      </:trigger>
      <:clear_trigger>
        <.heroicon name="hero-backspace" />
      </:clear_trigger>
      <:item_indicator>
        <.heroicon name="hero-check" />
      </:item_indicator>
    </.combobox>

Extended Grouped

This example requires the installation of Flagpack to display the use of custom item rendering.

<.combobox
      class="combobox"
      translation={%Corex.Combobox.Translation{placeholder: "Select a country", empty: "No results"}}
      collection={[
        %{label: "France", id: "fra", group: "Europe"},
        %{label: "Belgium", id: "bel", group: "Europe"},
        %{label: "Germany", id: "deu", group: "Europe"},
        %{label: "Japan", id: "jpn", group: "Asia"},
        %{label: "China", id: "chn", group: "Asia"},
        %{label: "South Korea", id: "kor", group: "Asia"}
      ]}
    >
      <:item :let={item}>
        <Flagpack.flag name={String.to_atom(item.id)} />
        {item.label}
      </:item>
      <:trigger>
        <.heroicon name="hero-chevron-down" />
      </:trigger>
      <:clear_trigger>
        <.heroicon name="hero-backspace" />
      </:clear_trigger>
      <:item_indicator>
        <.heroicon name="hero-check" />
      </:item_indicator>
    </.combobox>

Phoenix Form Integration

Use field={f[:key]} or field={@form[:key]} with a form built from an Ecto changeset. Set the form id with Corex.Form.get_form_id/1. Build the form in the controller with Schema.changeset(%Schema{}, %{}) |> Phoenix.Component.to_form(as: :form_name, id: "form-id"). In Live view add controlled mode and use the same changeset pattern. See the Select or NumberInput component docs for the full Controller and Live View examples.

Server-side Filtering

Disable client filtering with disabled={false} and use on_input_value_change to filter on the server. This example uses a local list; replace with a database query for real apps.

defmodule MyAppWeb.CountryCombobox do
  use MyAppWeb, :live_view

  @items [
    %{id: "fra", label: "France"},
    %{id: "bel", label: "Belgium"},
    %{id: "deu", label: "Germany"},
    %{id: "usa", label: "USA"},
    %{id: "jpn", label: "Japan"}
  ]

  def mount(_params, _session, socket) do
    {:ok, assign(socket, items: [])}
  end

  def handle_event("search", %{"value" => value, "reason" => "input-change"}, socket) do
    filtered =
      if byte_size(value) < 1 do
        []
      else
        term = String.downcase(value)
        Enum.filter(@items, fn item ->
          String.contains?(String.downcase(item.label), term)
        end)
      end

    {:noreply, assign(socket, items: filtered)}
  end

  def render(assigns) do
    ~H"""
    <.combobox
      id="country-combobox"
      collection={@items}
      filter={false}
      on_input_value_change="search"
    >
      <:trigger><.heroicon name="hero-chevron-down" /></:trigger>
    </.combobox>
    """
  end
end

Styling

Use data attributes to target elements:

[data-scope="combobox"][data-part="root"] {}
[data-scope="combobox"][data-part="control"] {}
[data-scope="combobox"][data-part="input"] {}
[data-scope="combobox"][data-part="trigger"] {}
[data-scope="combobox"][data-part="clear-trigger"] {}
[data-scope="combobox"][data-part="content"] {}
[data-scope="combobox"][data-part="empty"] {}
[data-scope="combobox"][data-part="item-group"] {}
[data-scope="combobox"][data-part="item-group-label"] {}
[data-scope="combobox"][data-part="item"] {}
[data-scope="combobox"][data-part="item-text"] {}
[data-scope="combobox"][data-part="item-indicator"] {}

If you wish to use the default Corex styling, you can use the class combobox on the component. This requires to install Mix.Tasks.Corex.Design first and import the component css file.

@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/combobox.css";

You can then use modifiers

<.combobox class="combobox combobox--accent combobox--lg" collection={[]}>
  <:empty>No results</:empty>
  <:trigger>
    <.heroicon name="hero-chevron-down" />
  </:trigger>
</.combobox>

Learn more about modifiers and Corex Design

Summary

Components

Renders a combobox component.

Components

combobox(assigns)

Renders a combobox component.

Attributes

  • id (:string) - The id of the combobox, useful for API to identify the combobox.
  • collection (:list) - The collection of items to display in the combobox. Defaults to [].
  • controlled (:boolean) - Whether the combobox is controlled. Defaults to false.
  • on_open_change (:string) - The server event name to trigger on open change. Defaults to nil.
  • on_open_change_client (:string) - The client event name to trigger on open change. Defaults to nil.
  • bubble (:boolean) - Whether the client events are bubbled. Defaults to false.
  • disabled (:boolean) - Whether the combobox is disabled. Defaults to false.
  • open (:boolean) - Whether the combobox is open. Defaults to false.
  • value (:list) - The value of the combobox. Defaults to [].
  • translation (Corex.Combobox.Translation) - Override translatable strings. Defaults to nil.
  • always_submit_on_enter (:boolean) - Whether to always submit on enter. Defaults to false.
  • auto_focus (:boolean) - Whether to auto focus the combobox. Defaults to false.
  • close_on_select (:boolean) - Whether to close the combobox on select. Defaults to true.
  • dir (:string) - The direction of the combobox. When nil, derived from document (html lang + config :rtl_locales). Defaults to nil.
  • input_behavior (:string) - The input behavior of the combobox. Defaults to "autohighlight".
  • loop_focus (:boolean) - Whether to loop focus the combobox. Defaults to false.
  • multiple (:boolean) - Whether to allow multiple selection. Defaults to false.
  • invalid (:boolean) - Whether the combobox is invalid. Defaults to false.
  • name (:string) - The name of the combobox.
  • form (:string) - The id of the form of the combobox.
  • read_only (:boolean) - Whether the combobox is read only. Defaults to false.
  • required (:boolean) - Whether the combobox is required. Defaults to false.
  • filter (:boolean) - When true, filter options client-side by input value. Set to false when using on_input_value_change for server-side filtering. Defaults to true.
  • on_input_value_change (:string) - The server event name to trigger on input value change. Defaults to nil.
  • on_value_change (:string) - The server event name to trigger on value change. Defaults to nil.
  • positioning (:map) - The positioning of the combobox. Defaults to %Corex.Positioning{hide_when_detached: true, strategy: "fixed", placement: "bottom", gutter: 8, shift: 0, overflow_padding: 0, arrow_padding: 4, flip: true, slide: true, overlap: false, same_width: true, fit_viewport: false}.
  • field (Phoenix.HTML.FormField) - A form field struct retrieved from the form, for example: @form[:country]. Automatically sets id, name, value, and errors from the form field.
  • errors (:list) - List of error messages to display. Defaults to [].
  • Global attributes are accepted.

Slots

  • label - The label content. Accepts attributes:
    • class (:string)
  • empty - Content when there are no results. When omitted, translation.empty is used. Accepts attributes:
    • class (:string)
  • trigger (required) - The trigger button content. Accepts attributes:
    • class (:string)
  • clear_trigger - The clear button content. Accepts attributes:
    • class (:string)
  • item_indicator - Optional indicator for selected items. Accepts attributes:
    • class (:string)
  • error - Accepts attributes:
    • class (:string)
  • item - Custom content for each item. Receives the item as :let binding. Accepts attributes:
    • class (:string)