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
raxol lib raxol ui components input select_list renderer.ex
Raw

lib/raxol/ui/components/input/select_list/renderer.ex

defmodule Raxol.UI.Components.Input.SelectList.Renderer do
@moduledoc """
Rendering logic for SelectList component.
"""
alias Raxol.UI.Components.Input.SelectList
alias Raxol.UI.Components.Input.SelectList.{Pagination, Selection}
@doc """
Renders the SelectList component.
"""
@spec render(SelectList.t(), map()) :: map()
def render(state, context \\ %{}) do
visible_options = get_visible_options(state)
rendered_options =
visible_options
|> Enum.with_index(state.scroll_offset)
|> Enum.map(fn {option, index} ->
render_option(option, index, state)
end)
search_bar =
if state.search_enabled do
[render_search_bar(state)]
else
[]
end
pagination_info =
if state.paginated do
[render_pagination_info(state)]
else
[]
end
children =
(search_bar ++ rendered_options ++ pagination_info)
|> Enum.filter(&(&1 != nil))
container_style =
if state.has_focus do
Raxol.UI.FocusHelper.focus_style(state.style || %{}, context)
else
state.style || %{}
end
%{
type: :container,
id: state[:id],
style: container_style,
children: children
}
end
# Private functions
defp get_visible_options(state) do
effective_options =
case state.filtered_options do
nil -> state.options
filtered -> filtered
end
visible_items = state.visible_items || 10
start_index = state.scroll_offset
Enum.slice(effective_options, start_index, visible_items)
end
defp render_option(option, index, state) do
label = get_option_label(option)
is_selected = Selection.selected?(state, index)
prefix =
if is_selected do
state.selected_marker || "> "
else
String.duplicate(" ", String.length(state.selected_marker || "> "))
end
style =
if is_selected do
state.selected_style || %{reverse: true}
else
%{}
end
Raxol.View.Components.text(
content: apply_style("#{prefix}#{label}\n", style)
)
end
defp render_search_bar(state) do
query = state.search_query || ""
cursor = if state.search_active, do: "_", else: ""
content =
[
"Search: ",
query,
cursor,
"\n",
String.duplicate("-", 40),
"\n"
]
|> IO.iodata_to_binary()
Raxol.View.Components.text(content: content)
end
defp render_pagination_info(state) do
current_page = Pagination.get_current_page(state) + 1
total_pages = Pagination.calculate_total_pages(state)
content =
[
"\n",
String.duplicate("-", 40),
"\n",
"Page #{current_page} of #{total_pages}",
if(Pagination.has_prev_page?(state), do: " [<-Prev]", else: ""),
if(Pagination.has_next_page?(state), do: " [Next->]", else: ""),
"\n"
]
|> IO.iodata_to_binary()
Raxol.View.Components.text(content: content)
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(option), do: to_string(option)
defp apply_style(text, style) when map_size(style) == 0, do: text
defp apply_style(text, style) do
# Apply ANSI style codes based on style map
codes = []
codes = if style[:bold], do: ["\e[1m" | codes], else: codes
codes = if style[:reverse], do: ["\e[7m" | codes], else: codes
codes = if style[:underline], do: ["\e[4m" | codes], else: codes
if codes == [] do
text
else
IO.iodata_to_binary([codes, text, "\e[0m"])
end
end
end