// src/components/common/autocomplete.rs use crate::config::colors::themes::Theme; use crate::state::pages::form::FormState; use common::proto::komp_ac::search::search_response::Hit; use ratatui::{ layout::Rect, style::{Color, Modifier, Style}, widgets::{Block, List, ListItem, ListState}, Frame, }; use unicode_width::UnicodeWidthStr; /// Renders an opaque dropdown list for simple string-based suggestions. /// THIS IS THE RESTORED FUNCTION. pub fn render_autocomplete_dropdown( f: &mut Frame, input_rect: Rect, frame_area: Rect, theme: &Theme, suggestions: &[String], selected_index: Option, ) { if suggestions.is_empty() { return; } let max_suggestion_width = suggestions.iter().map(|s| s.width()).max().unwrap_or(0) as u16; let horizontal_padding: u16 = 2; let dropdown_width = (max_suggestion_width + horizontal_padding).max(10); let dropdown_height = (suggestions.len() as u16).min(5); let mut dropdown_area = Rect { x: input_rect.x, y: input_rect.y + 1, width: dropdown_width, height: dropdown_height, }; if dropdown_area.bottom() > frame_area.height { dropdown_area.y = input_rect.y.saturating_sub(dropdown_height); } if dropdown_area.right() > frame_area.width { dropdown_area.x = frame_area.width.saturating_sub(dropdown_width); } dropdown_area.x = dropdown_area.x.max(0); dropdown_area.y = dropdown_area.y.max(0); let background_block = Block::default().style(Style::default().bg(Color::DarkGray)); f.render_widget(background_block, dropdown_area); let items: Vec = suggestions .iter() .enumerate() .map(|(i, s)| { let is_selected = selected_index == Some(i); let s_width = s.width() as u16; let padding_needed = dropdown_width.saturating_sub(s_width); let padded_s = format!("{}{}", s, " ".repeat(padding_needed as usize)); ListItem::new(padded_s).style(if is_selected { Style::default() .fg(theme.bg) .bg(theme.highlight) .add_modifier(Modifier::BOLD) } else { Style::default().fg(theme.fg).bg(Color::DarkGray) }) }) .collect(); let list = List::new(items); let mut list_state = ListState::default(); list_state.select(selected_index); f.render_stateful_widget(list, dropdown_area, &mut list_state); } /// Renders an opaque dropdown list for rich `Hit`-based suggestions. /// RENAMED from render_rich_autocomplete_dropdown pub fn render_hit_autocomplete_dropdown( f: &mut Frame, input_rect: Rect, frame_area: Rect, theme: &Theme, suggestions: &[Hit], selected_index: Option, form_state: &FormState, ) { if suggestions.is_empty() { return; } let display_names: Vec = suggestions .iter() .map(|hit| form_state.get_display_name_for_hit(hit)) .collect(); let max_suggestion_width = display_names.iter().map(|s| s.width()).max().unwrap_or(0) as u16; let horizontal_padding: u16 = 2; let dropdown_width = (max_suggestion_width + horizontal_padding).max(10); let dropdown_height = (suggestions.len() as u16).min(5); let mut dropdown_area = Rect { x: input_rect.x, y: input_rect.y + 1, width: dropdown_width, height: dropdown_height, }; if dropdown_area.bottom() > frame_area.height { dropdown_area.y = input_rect.y.saturating_sub(dropdown_height); } if dropdown_area.right() > frame_area.width { dropdown_area.x = frame_area.width.saturating_sub(dropdown_width); } dropdown_area.x = dropdown_area.x.max(0); dropdown_area.y = dropdown_area.y.max(0); let background_block = Block::default().style(Style::default().bg(Color::DarkGray)); f.render_widget(background_block, dropdown_area); let items: Vec = display_names .iter() .enumerate() .map(|(i, s)| { let is_selected = selected_index == Some(i); let s_width = s.width() as u16; let padding_needed = dropdown_width.saturating_sub(s_width); let padded_s = format!("{}{}", s, " ".repeat(padding_needed as usize)); ListItem::new(padded_s).style(if is_selected { Style::default() .fg(theme.bg) .bg(theme.highlight) .add_modifier(Modifier::BOLD) } else { Style::default().fg(theme.fg).bg(Color::DarkGray) }) }) .collect(); let list = List::new(items); let mut list_state = ListState::default(); list_state.select(selected_index); f.render_stateful_widget(list, dropdown_area, &mut list_state); }