Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/raxol/ui/components/input/select_list/search.ex
defmodule Raxol.UI.Components.Input.SelectList.Search do
@moduledoc """
Search/filter functionality for SelectList component.
"""
alias Raxol.UI.Components.Input.SelectList
@doc """
Updates the search state with a new query.
"""
@spec update_search_state(SelectList.t(), String.t()) :: SelectList.t()
def update_search_state(state, query) do
is_filtering = query != ""
filtered_options =
if is_filtering do
filter_options(state.options, query)
else
nil
end
%{
state
| search_query: query,
filtered_options: filtered_options,
is_filtering: is_filtering,
selected_index: 0,
scroll_offset: 0
}
end
@doc """
Clears the current search.
"""
@spec clear_search(SelectList.t()) :: SelectList.t()
def clear_search(state) do
%{
state
| search_query: "",
filtered_options: nil,
selected_index: 0,
scroll_offset: 0
}
end
@doc """
Checks if search is active.
"""
@spec search_active?(SelectList.t()) :: boolean()
def search_active?(state) do
state.search_query != "" and state.search_query != nil
end
@doc """
Gets the current search results count.
"""
@spec get_results_count(SelectList.t()) :: non_neg_integer()
def get_results_count(state) do
case state.filtered_options do
nil -> length(state.options)
filtered -> length(filtered)
end
end
@doc """
Appends a character to the search query.
"""
@spec append_to_search(SelectList.t(), String.t()) :: SelectList.t()
def append_to_search(state, char) do
new_query = (state.search_query || "") <> char
update_search_state(state, new_query)
end
@doc """
Removes the last character from the search query.
"""
@spec backspace_search(SelectList.t()) :: SelectList.t()
def backspace_search(state) do
query = state.search_query || ""
new_query =
if String.length(query) > 0 do
String.slice(query, 0..-2//1)
else
""
end
update_search_state(state, new_query)
end
# Private functions
defp filter_options(options, query) when query == "", do: options
defp filter_options(options, query) do
normalized_query = String.downcase(query)
Enum.filter(options, fn option ->
label = get_option_label(option)
String.downcase(label) =~ normalized_query
end)
end
defp get_option_label(option) when is_binary(option), do: option
defp get_option_label({label, _value}), do: label
defp get_option_label(%{label: label}), do: label
defp get_option_label(%{text: text}), do: text
defp get_option_label(%{name: name}), do: name
defp get_option_label(%{value: value}), do: to_string(value)
defp get_option_label(option), do: to_string(option)
end