Combobox

Wrapper - Phlex implementation

Examples

Default

Autocomplete input and command palette composition.

Next.js
SvelteKit
Nuxt.js
Remix
Astro
<%
  frameworks = [
    { value: "next", label: "Next.js" },
    { value: "sveltekit", label: "SvelteKit" },
    { value: "nuxt", label: "Nuxt.js" },
    { value: "remix", label: "Remix" },
    { value: "astro", label: "Astro" }
  ]
%>
<%= render "ui/popover", placement: "bottom-start", data: { controller: "ui--popover ui--combobox", ui__combobox_value_value: "" } do %>
  <%= render "ui/popover/trigger", as_child: true do |trigger_attrs| %>
    <%= render "ui/button", **trigger_attrs, variant: :outline, classes: "w-[200px] justify-between" do %>
      <span data-ui--combobox-target="text">Select framework...</span>
      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ml-2 h-4 w-4 shrink-0 opacity-50">
        <path d="m7 15 5 5 5-5"/>
        <path d="m7 9 5-5 5 5"/>
      </svg>
    <% end %>
  <% end %>
  <%= render "ui/popover/content", classes: "w-[200px] p-0" do %>
    <%= render "ui/command" do %>
      <%= render "ui/command/input", placeholder: "Search framework..." %>
      <%= render "ui/command/list" do %>
        <%= render "ui/command/empty" do %>No framework found.<% end %>
        <%= render "ui/command/group" do %>
          <% frameworks.each do |framework| %>
            <%= render "ui/command/item", value: framework[:value], data: { ui__combobox_target: "item" } do %>
              <span><%= framework[:label] %></span>
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ml-auto h-4 w-4 opacity-0">
                <path d="M20 6 9 17l-5-5"/>
              </svg>
            <% end %>
          <% end %>
        <% end %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

Status Selector

Select a status with visual indicators and icons.

Backlog
Todo
In Progress
Done
Canceled
<%
  statuses = [
    { value: "backlog", label: "Backlog", icon: "help-circle" },
    { value: "todo", label: "Todo", icon: "circle" },
    { value: "in-progress", label: "In Progress", icon: "arrow-up-circle" },
    { value: "done", label: "Done", icon: "check-circle" },
    { value: "canceled", label: "Canceled", icon: "x-circle" }
  ]
%>
<%= render "ui/popover", placement: "bottom-start", data: { controller: "ui--popover ui--combobox", ui__combobox_value_value: "" } do %>
  <%= render "ui/popover/trigger", as_child: true do |trigger_attrs| %>
    <%= render "ui/button", **trigger_attrs, variant: :outline, classes: "w-[200px] justify-start" do %>
      <span data-ui--combobox-target="text">+ Set status</span>
    <% end %>
  <% end %>
  <%= render "ui/popover/content", classes: "w-[200px] p-0", align: "start" do %>
    <%= render "ui/command" do %>
      <%= render "ui/command/input", placeholder: "Change status..." %>
      <%= render "ui/command/list" do %>
        <%= render "ui/command/empty" do %>No results found.<% end %>
        <%= render "ui/command/group" do %>
          <% statuses.each do |status| %>
            <%= render "ui/command/item", value: status[:value], data: { ui__combobox_target: "item" } do %>
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2 h-4 w-4">
                <% if status[:icon] == "help-circle" %>
                  <circle cx="12" cy="12" r="10"/>
                  <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
                  <path d="M12 17h.01"/>
                <% elsif status[:icon] == "circle" %>
                  <circle cx="12" cy="12" r="10"/>
                <% elsif status[:icon] == "arrow-up-circle" %>
                  <circle cx="12" cy="12" r="10"/>
                  <path d="m16 12-4-4-4 4"/>
                  <path d="M12 16V8"/>
                <% elsif status[:icon] == "check-circle" %>
                  <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
                  <path d="m9 11 3 3L22 4"/>
                <% elsif status[:icon] == "x-circle" %>
                  <circle cx="12" cy="12" r="10"/>
                  <path d="m15 9-6 6"/>
                  <path d="m9 9 6 6"/>
                <% end %>
              </svg>
              <span><%= status[:label] %></span>
            <% end %>
          <% end %>
        <% end %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

Responsive

Desktop shows Popover, mobile shows Drawer. Resize browser to see responsive behavior.

<%
  statuses = [
    { value: "backlog", label: "Backlog" },
    { value: "todo", label: "Todo" },
    { value: "in-progress", label: "In Progress" },
    { value: "done", label: "Done" },
    { value: "canceled", label: "Canceled" }
  ]
%>
<%# Desktop: Popover %>
<div class="hidden md:block">
  <%= render "ui/popover", placement: "bottom-start", data: { controller: "ui--popover ui--combobox", ui__combobox_value_value: "" } do %>
    <%= render "ui/popover/trigger", as_child: true do |trigger_attrs| %>
      <%= render "ui/button", **trigger_attrs, variant: :outline, classes: "w-[200px] justify-start" do %>
        <span data-ui--combobox-target="text">+ Set status</span>
      <% end %>
    <% end %>
    <%= render "ui/popover/content", classes: "w-[200px] p-0", align: "start" do %>
      <%= render "ui/command" do %>
        <%= render "ui/command/input", placeholder: "Change status..." %>
        <%= render "ui/command/list" do %>
          <%= render "ui/command/empty" do %>No results found.<% end %>
          <%= render "ui/command/group" do %>
            <% statuses.each do |status| %>
              <%= render "ui/command/item", value: status[:value], data: { ui__combobox_target: "item" } do %>
                <span><%= status[:label] %></span>
              <% end %>
            <% end %>
          <% end %>
        <% end %>
      <% end %>
    <% end %>
  <% end %>
</div>
<%# Mobile: Drawer %>
<div class="md:hidden" data-controller="ui--combobox" data-ui--combobox-value-value="">
  <%= render "ui/drawer" do %>
    <%= render "ui/drawer/trigger", as_child: true do |trigger_attrs| %>
      <%= render "ui/button", attributes: trigger_attrs, variant: :outline, classes: "w-[200px] justify-start" do %>
        <span data-ui--combobox-target="text">+ Set status</span>
      <% end %>
    <% end %>
    <%= render "ui/drawer/overlay" %>
    <%= render "ui/drawer/content" do %>
      <%= render "ui/drawer/handle" %>
      <%= render "ui/drawer/header" do %>
        <%= render "ui/drawer/title" do %>Set Status<% end %>
        <%= render "ui/drawer/description" do %>Select a status for your task<% end %>
      <% end %>
      <div class="p-4">
        <%= render "ui/command" do %>
          <%= render "ui/command/input", placeholder: "Change status..." %>
          <%= render "ui/command/list" do %>
            <%= render "ui/command/empty" do %>No results found.<% end %>
            <%= render "ui/command/group" do %>
              <% statuses.each do |status| %>
                <%= render "ui/command/item", value: status[:value], data: { ui__combobox_target: "item" } do %>
                  <span><%= status[:label] %></span>
                <% end %>
              <% end %>
            <% end %>
          <% end %>
        <% end %>
      </div>
    <% end %>
  <% end %>
</div>

API Reference

Combobox Wrapper

Wrapper component that yields combobox attributes to be spread into a container component.

Parameters

NameTypeDefaultDescription
valueStringThe current value