<div class="flex flex-col gap-10 p-6"> <div> <h2 class="heading-20 mb-4">Single — Default</h2> <div style="width: 430px;"> <form id="select2_grid_single_empty" action="#" accept-charset="UTF-8" method="get"> <p class="copy-12 text-gray-500 mb-2">Label</p> <div id="looposui-inputs-select2_option_single_5793709857" class="grow" data-controller="input-select2" data-input-select2-mode-value="form" data-input-select2-multiple-value="false" data-input-select2-variant-value="grid" data-input-select2-show-loading-on-submit-value="true"> <div class="lui-input-select lui-input-select--grid " role="listbox"> <div class="lui-input-select__grid-options"> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 1" data-value="1" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 1 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 2" data-value="2" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 2 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 3" data-value="3" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 3 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> </div> </div> <div data-input-select2-target="hiddenInputWrapper"> <input type="hidden" name="option_single" id="option_single" data-input-select2-target="hiddenInput" /> </div> </div> </form> </div> </div> <div> <h2 class="heading-20 mb-4">Single — Pre-selected</h2> <div style="width: 430px;"> <form id="select2_grid_single_preselected" action="#" accept-charset="UTF-8" method="get"> <p class="copy-12 text-gray-500 mb-2">Label</p> <div id="looposui-inputs-select2_option_single_preselected_9912568611" class="grow" data-controller="input-select2" data-input-select2-mode-value="form" data-input-select2-multiple-value="false" data-input-select2-variant-value="grid" data-input-select2-show-loading-on-submit-value="true"> <div class="lui-input-select lui-input-select--grid " role="listbox"> <div class="lui-input-select__grid-options"> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 1" data-value="1" type="button" role="option" aria-selected="true"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 1 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 2" data-value="2" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 2 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 3" data-value="3" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 3 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> </div> </div> <div data-input-select2-target="hiddenInputWrapper"> <input type="hidden" name="option_single_preselected" id="option_single_preselected" value="1" data-input-select2-target="hiddenInput" /> </div> </div> <div class="mt-6 flex justify-end"> <span class="lui-button__tooltip-wrapper inline-flex w-fit"> <button class="lui-button lui-button--neutral--primary lui-button--size-default w-fit w-fit relative" data-controller="lui--button"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Submit </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 16px;"> progress_activity </i> </div> </button></span> </div> </form> </div> </div> <div> <h2 class="heading-20 mb-4">Multiple — Default</h2> <div style="width: 430px;"> <form id="select2_grid_multiple_empty" action="#" accept-charset="UTF-8" method="get"> <p class="copy-12 text-gray-500 mb-2">Label</p> <div id="looposui-inputs-select2_option_multiple_2322156580" class="grow" data-controller="input-select2" data-input-select2-mode-value="form" data-input-select2-multiple-value="true" data-input-select2-variant-value="grid" data-input-select2-show-loading-on-submit-value="true"> <div class="lui-input-select lui-input-select--grid " role="listbox"> <div class="lui-input-select__grid-options"> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 1" data-value="1" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 1 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 2" data-value="2" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 2 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 3" data-value="3" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 3 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> </div> </div> <div data-input-select2-target="hiddenInputWrapper"> <input type="hidden" name="option_multiple[]" id="option_multiple_" data-input-select2-target="hiddenInput" /> </div> </div> </form> </div> </div> <div> <h2 class="heading-20 mb-4">Multiple — Pre-selected</h2> <div style="width: 430px;"> <form id="select2_grid_multiple_preselected" action="#" accept-charset="UTF-8" method="get"> <p class="copy-12 text-gray-500 mb-2">Label</p> <div id="looposui-inputs-select2_option_multiple_preselected_6597424226" class="grow" data-controller="input-select2" data-input-select2-mode-value="form" data-input-select2-multiple-value="true" data-input-select2-variant-value="grid" data-input-select2-show-loading-on-submit-value="true"> <div class="lui-input-select lui-input-select--grid " role="listbox"> <div class="lui-input-select__grid-options"> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 1" data-value="1" type="button" role="option" aria-selected="true"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 1 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 2" data-value="2" type="button" role="option" aria-selected="false"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 2 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> <span class="lui-button__tooltip-wrapper inline-flex w-full"> <button class="lui-button lui-button--size-small lui-button--neutral--secondary w-full w-full relative" data-controller="lui--button" data-input-select2-target="option" data-action="click->input-select2#selectGridOption" data-text="Option 3" data-value="3" type="button" role="option" aria-selected="true"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Option 3 </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 14px;"> progress_activity </i> </div> </button></span> </div> </div> <div data-input-select2-target="hiddenInputWrapper"> <input type="hidden" name="option_multiple_preselected[]" id="option_multiple_preselected_" value="1" data-input-select2-target="hiddenInput" /> </div> </div> <div class="mt-6 flex justify-end"> <span class="lui-button__tooltip-wrapper inline-flex w-fit"> <button class="lui-button lui-button--neutral--primary lui-button--size-default w-fit w-fit relative" data-controller="lui--button"> <span class="lui-button__text opacity-100 inline-flex" data-lui--button-target="text"> Submit </span> <div class="absolute w-full flex items-center justify-center opacity-0" data-lui--button-target="loadingIcon"> <i class="lui-m_icon animate-spin material-symbols-rounded" style="--lui-micon-size: 16px;"> progress_activity </i> </div> </button></span> </div> </form> </div> </div></div>Inputs::Select2
Description
Related components
| Used Components | Components where is Used |
|---|---|
| Label |
Usage rules
- ✅ Do
- ❌ Don't
<% options = [ { value: "1", text: "Option 1" }, { value: "2", text: "Option 2" }, { value: "3", text: "Option 3" }, ]%><div class="flex flex-col gap-10 p-6"> <div> <h2 class="heading-20 mb-4">Single — Default</h2> <div style="width: 430px;"> <%= form_with url: "#", method: :get, id: "select2_grid_single_empty" do %> <p class="copy-12 text-gray-500 mb-2">Label</p> <%= render LooposUi::Inputs::Select2.new( name: "option_single", options: options, variant: :grid, mode: :form, help: params[:help].presence, error: params[:error].presence, ) %> <% end %> </div> </div> <div> <h2 class="heading-20 mb-4">Single — Pre-selected</h2> <div style="width: 430px;"> <%= form_with url: "#", method: :get, id: "select2_grid_single_preselected" do %> <p class="copy-12 text-gray-500 mb-2">Label</p> <%= render LooposUi::Inputs::Select2.new( name: "option_single_preselected", options: options, value: "1", variant: :grid, mode: :form, help: params[:help].presence, error: params[:error].presence, ) %> <div class="mt-6 flex justify-end"> <%= render LooposUi::Button.new(text: "Submit", kind: :neutral) %> </div> <% end %> </div> </div> <div> <h2 class="heading-20 mb-4">Multiple — Default</h2> <div style="width: 430px;"> <%= form_with url: "#", method: :get, id: "select2_grid_multiple_empty" do %> <p class="copy-12 text-gray-500 mb-2">Label</p> <%= render LooposUi::Inputs::Select2.new( name: "option_multiple", options: options, multiple: true, variant: :grid, mode: :form, help: params[:help].presence, error: params[:error].presence, ) %> <% end %> </div> </div> <div> <h2 class="heading-20 mb-4">Multiple — Pre-selected</h2> <div style="width: 430px;"> <%= form_with url: "#", method: :get, id: "select2_grid_multiple_preselected" do %> <p class="copy-12 text-gray-500 mb-2">Label</p> <%= render LooposUi::Inputs::Select2.new( name: "option_multiple_preselected", options: options, value: ["1", "3"], multiple: true, variant: :grid, mode: :form, help: params[:help].presence, error: params[:error].presence, ) %> <div class="mt-6 flex justify-end"> <%= render LooposUi::Button.new(text: "Submit", kind: :neutral) %> </div> <% end %> </div> </div></div>No notes provided.
| Param | Description | Input |
|---|---|---|
|
Helper text below the input |
|
|
|
Validation error message |
|
Description
The Inputs::Select2 component is an enhanced select input that supports searching, multi-selection, nested options, icons, tooltips, and custom actions. It is designed for selecting one or more options from a potentially large or hierarchical dataset, with a user-friendly interface and keyboard accessibility.
Arguments
| Property | Default | Description |
|---|---|---|
model |
nil |
ActiveRecord model instance. |
attribute |
nil |
Attribute name of the model. |
name |
:value |
The name attribute for the input. |
options |
[] |
Array of option objects. Each option can have value, text, icon, tooltip, child_options. |
multiple |
false |
Allows selecting multiple options if true. |
placeholder |
nil |
Placeholder text shown when no option is selected. |
error |
nil |
Error message shown below the input. |
help |
nil |
Help text shown below the input when no error is present. |
mode |
:inline |
Behavioural mode. Can be :inline, :form, or :autosubmit. |
appearance |
nil |
Visual modifier. Defaults to mode. |
variant |
:list |
Visual presentation. :list renders the dropdown. :grid renders all options as visible buttons. |
include_blank |
false |
Adds a blank option. When a string is passed, it is used as the blank label. |
option_indent_size |
20 |
Indentation (in px) for nested child options. Only applies to variant: :list. |
parent_selects_children |
true |
If true, selecting a parent option selects all its children. Only applies to variant: :list. |
show_actions_in_menu |
false |
If true (and multiple), shows Reset and Apply buttons in the dropdown menu. Only applies to variant: :list. |
portal_target |
nil |
DOM selector for rendering the dropdown in a portal. Only applies to variant: :list. |
clearable |
true |
Shows the clear button in list mode. |
searchable |
true |
Shows the search input in list mode. |
submit_selected_value |
false |
When true, submitted hidden fields contain option values instead of presented text. Useful for protocol/multiselect answers. |
bind_hidden_input_to_form |
false |
When true, hidden inputs receive the form attribute. |
show_loading_on_submit |
true |
Controls internal input loading state during form submit (mode: :form / :autosubmit). |
value |
nil |
The currently selected value(s). |
form |
nil |
The form id or object for integration with Rails forms. |
readonly |
false |
Renders selected value(s) as plain text. |
custom_readonly |
false |
In readonly mode, renders provided content instead of the plain value. |
open_actions |
false |
Open field in edit mode by default. |
extra_input_attributes |
{} |
Extra attributes merged into the rendered input wrapper. |
Option Object Schema
| Property | Default | Description |
|---|---|---|
value |
- | The value for the option (string or symbol). |
text |
- | The display text for the option. |
icon |
"" |
Optional icon class for the option. |
tooltip |
"" |
Optional tooltip text for the option. |
child_options |
[] |
Array of nested option objects (for hierarchical selects). |
attributes |
{} |
Additional HTML attributes for the option element. |
Slots
This component does not use named slots, but you can customize the options array to render complex option trees, icons, and tooltips.
Usage Example
<%= render LooposUi::Inputs::Select2.new( name: "user_id", options: [ { value: :admin, text: "Admin", icon: "fa-user-shield" }, { value: :user, text: "User", icon: "fa-user" }, { value: :guest, text: "Guest", icon: "fa-user-secret", tooltip: "Limited access" }, { value: :group, text: "Group", icon: "fa-users", child_options: [ { value: :group_member, text: "Group Member", icon: "fa-user-friends" } ] } ], multiple: true, placeholder: "Select user type", show_actions_in_menu: true) %>Apply / Reset behavior
- When
multiple: trueandshow_actions_in_menu: true, the dropdown showsResetandApply. Applyis rendered as a primary action button.- On
Apply, the dropdown closes immediately and then the parent form is submitted.
Variants
variant controls the visual presentation of the Select2:
:list(default) — The current dropdown: a readonly text input triggers a Tippy popup with the option list, supporting search, hierarchical options, and apply/reset actions.:grid— All options are rendered as visibleLooposUi::Buttonelements. The selected option(s) are highlighted with theactiveclass. Multiple buttons can be active whenmultiple: true. The hidden input contract and form submission behaviour are identical to the list variant. Inmode: :autosubmit, each grid button click updates the hidden input(s) and callsform.requestSubmit()on the parent form (or the form identified byform).
Grid variant restrictions
When variant: :grid is active, the following features are disabled or ignored:
searchable— no search input is renderedparent_selects_children— hierarchy selection is not supportedshow_actions_in_menu— apply/reset buttons are not renderedinclude_blank— the blank option is omitted; only real options appear as buttonsportal_target— the Tippy dropdown is not used
Hierarchical options with grid
If any option has child_options, variant: :grid is silently ignored and the component falls back to variant: :list.
Readonly
When readonly: true, the grid is ignored regardless of variant. The component renders the selected value(s) as plain text, identical to the list readonly behaviour.
⚠️ Important
It's the responsibility of the developer to handle the generation of the form, and handling any turbo frame updates or form submissions.
The recommendation is to use the form_with helper from Rails, along with a turbo_frame_tag, so only the inline component is updated.