defmodule Raxol.Examples.SelectListShowcase do @moduledoc """ Showcase for the enhanced SelectList component with its various features. This example demonstrates: - Basic selection (single and multiple) - Searching and filtering - Pagination - Keyboard navigation - Styling options """ use Raxol.UI.Components.Base.Component require Raxol.Core.Runtime.Log require Raxol.View.Elements import Raxol.View.Elements @impl Raxol.UI.Components.Base.Component def init(_props) do %{ # Current tab selection active_tab: :basic, # Selected options from each list basic_selected: nil, multiple_selected: [], search_selected: nil, pagination_selected: nil, # Search text search_text: "", # Sample data users: [ %{ id: 1, name: "Alice Smith", email: "alice@example.com", role: "Admin" }, %{id: 2, name: "Bob Johnson", email: "bob@example.com", role: "User"}, %{ id: 3, name: "Carol Williams", email: "carol@example.com", role: "User" }, %{ id: 4, name: "Dave Brown", email: "dave@example.com", role: "Moderator" }, %{id: 5, name: "Eve Davis", email: "eve@example.com", role: "Admin"}, %{ id: 6, name: "Frank Miller", email: "frank@example.com", role: "User" }, %{ id: 7, name: "Grace Wilson", email: "grace@example.com", role: "User" }, %{ id: 8, name: "Heidi Moore", email: "heidi@example.com", role: "Moderator" }, %{id: 9, name: "Ivan Taylor", email: "ivan@example.com", role: "User"}, %{id: 10, name: "Judy White", email: "judy@example.com", role: "Admin"}, %{ id: 11, name: "Kevin Lewis", email: "kevin@example.com", role: "User" }, %{ id: 12, name: "Laura Harris", email: "laura@example.com", role: "Moderator" }, %{id: 13, name: "Mike Clark", email: "mike@example.com", role: "User"}, %{ id: 14, name: "Nancy Young", email: "nancy@example.com", role: "User" }, %{ id: 15, name: "Oscar Scott", email: "oscar@example.com", role: "Admin" } ], countries: [ %{ code: "US", name: "United States", population: "331 million", continent: "North America" }, %{ code: "CN", name: "China", population: "1.4 billion", continent: "Asia" }, %{ code: "IN", name: "India", population: "1.38 billion", continent: "Asia" }, %{ code: "ID", name: "Indonesia", population: "273 million", continent: "Asia" }, %{ code: "PK", name: "Pakistan", population: "220 million", continent: "Asia" }, %{ code: "BR", name: "Brazil", population: "212 million", continent: "South America" }, %{ code: "NG", name: "Nigeria", population: "206 million", continent: "Africa" }, %{ code: "BD", name: "Bangladesh", population: "164 million", continent: "Asia" }, %{ code: "RU", name: "Russia", population: "144 million", continent: "Europe/Asia" }, %{ code: "MX", name: "Mexico", population: "128 million", continent: "North America" }, %{ code: "JP", name: "Japan", population: "126 million", continent: "Asia" }, %{ code: "ET", name: "Ethiopia", population: "114 million", continent: "Africa" }, %{ code: "PH", name: "Philippines", population: "109 million", continent: "Asia" }, %{ code: "EG", name: "Egypt", population: "102 million", continent: "Africa" }, %{ code: "VN", name: "Vietnam", population: "97 million", continent: "Asia" }, %{ code: "DE", name: "Germany", population: "83 million", continent: "Europe" }, %{ code: "IR", name: "Iran", population: "83 million", continent: "Asia" }, %{ code: "TR", name: "Turkey", population: "83 million", continent: "Europe/Asia" }, %{ code: "FR", name: "France", population: "67 million", continent: "Europe" }, %{ code: "GB", name: "United Kingdom", population: "67 million", continent: "Europe" }, %{ code: "TH", name: "Thailand", population: "66 million", continent: "Asia" }, %{ code: "IT", name: "Italy", population: "60 million", continent: "Europe" }, %{ code: "ZA", name: "South Africa", population: "59 million", continent: "Africa" }, %{ code: "MM", name: "Myanmar", population: "54 million", continent: "Asia" }, %{ code: "KR", name: "South Korea", population: "51 million", continent: "Asia" }, %{ code: "CO", name: "Colombia", population: "50 million", continent: "South America" }, %{ code: "KE", name: "Kenya", population: "53 million", continent: "Africa" }, %{ code: "ES", name: "Spain", population: "47 million", continent: "Europe" }, %{ code: "AR", name: "Argentina", population: "45 million", continent: "South America" }, %{ code: "DZ", name: "Algeria", population: "43 million", continent: "Africa" } ], fruits: [ {"Apple", :apple}, {"Banana", :banana}, {"Cherry", :cherry}, {"Date", :date}, {"Elderberry", :elderberry}, {"Fig", :fig}, {"Grape", :grape}, {"Honeydew", :honeydew}, {"Imbe", :imbe}, {"Jackfruit", :jackfruit} ] } end @impl Raxol.UI.Components.Base.Component def handle_event({:select_tab, tab}, _props, state) when is_atom(tab) do {%{state | active_tab: tab}, []} end def handle_event({:select_fruit, fruit_value}, _props, state) do # Basic single selection {%{state | basic_selected: fruit_value}, []} end def handle_event({:select_user, user_id}, _props, state) do # For multiple selection handle_user_selection( state.multiple_selected |> Enum.member?(user_id), user_id, state ) end def handle_event({:select_user_with_search, user_id}, _props, state) do # Search list selection user = state.users |> Enum.find(&(&1.id == user_id)) {%{state | search_selected: user}, []} end def handle_event({:select_country, country_code}, _props, state) do # Pagination list selection country = state.countries |> Enum.find(&(&1.code == country_code)) {%{state | pagination_selected: country}, []} end def handle_event({:update_search, search_text}, _props, state) do {%{state | search_text: search_text}, []} end def handle_event(_event, _props, state) do {state, []} end @impl Raxol.UI.Components.Base.Component def render(state, _context) do # Custom theme settings theme = %{ select_list: %{ label_fg: :cyan, focused_fg: :black, focused_bg: :cyan, selected_fg: :black, selected_bg: :green, search_fg: :white, search_bg: :blue, pagination_fg: :white, pagination_bg: :blue } } panel title: "SelectList Component Showcase", border: :single, width: 100 do column padding: 1, gap: 1 do # Description label( "This showcase demonstrates the enhanced SelectList component with various features." ) # Tab navigation row gap: 2 do button( label: "Basic", on_click: {:select_tab, :basic}, style: get_tab_style(state.active_tab == :basic) ) button( label: "Multiple Selection", on_click: {:select_tab, :multiple}, style: get_tab_style(state.active_tab == :multiple) ) button( label: "Search & Filter", on_click: {:select_tab, :search}, style: get_tab_style(state.active_tab == :search) ) button( label: "Pagination", on_click: {:select_tab, :pagination}, style: get_tab_style(state.active_tab == :pagination) ) end # Content based on selected tab panel title: tab_title(state.active_tab), border: :single, height: 20 do column padding: 1, gap: 1 do case state.active_tab do :basic -> render_basic_tab(state, theme) :multiple -> render_multiple_tab(state, theme) :search -> render_search_tab(state, theme) :pagination -> render_pagination_tab(state, theme) end end end # Selection display panel title: "Selected Items", border: :single do column padding: 1, gap: 1 do case state.active_tab do :basic -> render_basic_selection(state.basic_selected) :multiple -> render_multiple_selection(state.multiple_selected, state.users) :search -> render_search_selection(state.search_selected) :pagination -> render_pagination_selection(state.pagination_selected) end end end # Instructions panel panel title: "Keyboard Navigation", border: :single do column padding: 1 do label(text: "Arrow Up/Down: Navigate items") label(text: "Page Up/Down: Navigate pages") label(text: "Home/End: Jump to first/last item") label( text: "Tab: Switch between search box and list (when search enabled)" ) label(text: "Enter: Select item") label(text: "Space: Toggle selection (in multiple selection mode)") label(text: "Backspace: Delete search text (when search focused)") label(text: "Type any text: Incremental search in list") end end end end end # Helper functions for refactored if statements # Handle user selection instead of if statement defp handle_user_selection(true, user_id, state) do # Remove from selection {%{ state | multiple_selected: state.multiple_selected |> Enum.reject(&(&1 == user_id)) }, []} end defp handle_user_selection(false, user_id, state) do # Add to selection {%{state | multiple_selected: [user_id | state.multiple_selected]}, []} end # Get tab style instead of if statement defp get_tab_style(true), do: [bg: :blue, fg: :white] defp get_tab_style(false), do: [] # Render basic selection instead of if statement defp render_basic_selection(nil), do: label(text: "No fruit selected") defp render_basic_selection(selected), do: label(text: "Selected fruit: #{selected}") # Render multiple selection instead of if statement defp render_multiple_selection(selected, _users) when selected == [] do label(text: "No users selected") end defp render_multiple_selection(selected, users) do selected_users = users |> Enum.filter(&Enum.member?(selected, &1.id)) |> Enum.map_join(", ", & &1.name) label(text: "Selected users: #{selected_users}") end # Render search selection instead of if statement defp render_search_selection(nil), do: label(text: "No user selected") defp render_search_selection(selected) do column do label(text: "Selected user:") label(text: "Name: #{selected.name}") label(text: "Email: #{selected.email}") label(text: "Role: #{selected.role}") end end # Render pagination selection instead of if statement defp render_pagination_selection(nil), do: label(text: "No country selected") defp render_pagination_selection(selected) do column do label(text: "Selected country:") label(text: "Name: #{selected.name}") label(text: "Population: #{selected.population}") label(text: "Continent: #{selected.continent}") end end # Tab-specific rendering functions defp render_basic_tab(state, theme) do column gap: 1 do label(text: "Basic single-selection list with fruits:") # Convert fruits to expected format for SelectList options options = Enum.map(state.fruits, fn {label, value} -> {label, value} end) %{ type: Raxol.UI.Components.Input.SelectList, id: :basic_select, assigns: %{ options: options, label: "Select a fruit:", on_select: {:select_fruit}, max_height: 10, theme: theme.select_list } } end end defp render_multiple_tab(state, theme) do column gap: 1 do label(text: "Multiple selection list with users:") # Convert users to expected format for SelectList options options = Enum.map(state.users, fn user -> {"#{user.name} (#{user.role})", user.id} end) %{ type: Raxol.UI.Components.Input.SelectList, id: :multiple_select, assigns: %{ options: options, label: "Select users (use Space to toggle):", on_select: {:select_user}, max_height: 12, multiple: true, theme: theme.select_list } } end end defp render_search_tab(state, theme) do column gap: 1 do label(text: "Searchable list with users:") # Convert users to expected format for SelectList options options = Enum.map(state.users, fn user -> {"#{user.name} (#{user.email})", user.id} end) %{ type: Raxol.UI.Components.Input.SelectList, id: :search_select, assigns: %{ options: options, label: "Search users (by name or email):", on_select: {:select_user_with_search}, max_height: 12, enable_search: true, searchable_fields: [:name, :email], placeholder: "Type to search...", theme: theme.select_list } } end end defp render_pagination_tab(state, theme) do column gap: 1 do label(text: "Paginated list with countries:") # Convert countries to expected format for SelectList options options = Enum.map(state.countries, fn country -> {"#{country.name} (#{country.continent})", country.code} end) %{ type: Raxol.UI.Components.Input.SelectList, id: :pagination_select, assigns: %{ options: options, label: "Select a country:", on_select: {:select_country}, max_height: 12, page_size: 8, show_pagination: true, theme: theme.select_list } } end end defp tab_title(tab) do case tab do :basic -> "Basic Selection" :multiple -> "Multiple Selection" :search -> "Search & Filter" :pagination -> "Pagination" end end @impl Raxol.UI.Components.Base.Component def update({:update_props, new_props}, state) do {:noreply, Map.merge(state, new_props)} end def update({:tab_selected, tab}, state) do {:noreply, %{state | active_tab: tab}} end end