hefesto-widgets 0.6.0

Ratatui widgets for the Hefesto TUI toolkit: popups, scrollable lists, trees, text input and spinners
Documentation
use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Color, Style},
    widgets::StatefulWidget,
};

use crate::popups::FileBrowserState;
use crate::scroll_list::ScrollList;
use crate::DEFAULT_HIGHLIGHT;

/// Widget que renderiza una lista de archivos con iconos y estilos.
///
/// No incluye chrome de popup. Usá [`FileBrowserPopup`](crate::FileBrowserPopup)
/// para agregar título, borde, posición, etc.
///
/// ## Render
///
/// Toma los items de [`FileBrowserState::items`] y los estiliza según
/// `dir_style` (para directorios) y `file_style` (para archivos).
///
/// El filtro por nombre no está disponible en este widget;
/// está implementado en [`FileBrowserPopup`](crate::FileBrowserPopup).
#[derive(Clone)]
pub struct FileBrowser {
    dir_style: Style,
    file_style: Style,
    highlight_style: Style,
    highlight_symbol: String,
    dir_icon: String,
    file_icon: String,
    max_selected: Option<usize>,
}

impl FileBrowser {
    pub fn new() -> Self {
        Self {
            dir_style: Style::new().fg(Color::Cyan),
            file_style: Style::new().fg(Color::White),
            highlight_style: DEFAULT_HIGHLIGHT,
            highlight_symbol: crate::HIGHLIGHT_SYMBOL.to_string(),
            dir_icon: "📁 ".to_string(),
            file_icon: "📄 ".to_string(),
            max_selected: None,
        }
    }

    pub fn dir_style(mut self, style: Style) -> Self {
        self.dir_style = style;
        self
    }

    pub fn file_style(mut self, style: Style) -> Self {
        self.file_style = style;
        self
    }

    pub fn highlight_style(mut self, style: Style) -> Self {
        self.highlight_style = style;
        self
    }

    pub fn highlight_symbol(mut self, symbol: &str) -> Self {
        self.highlight_symbol = symbol.to_string();
        self
    }

    pub fn dir_icon(mut self, icon: &str) -> Self {
        self.dir_icon = icon.to_string();
        self
    }

    pub fn file_icon(mut self, icon: &str) -> Self {
        self.file_icon = icon.to_string();
        self
    }

    pub fn max_selected(mut self, max: usize) -> Self {
        self.max_selected = Some(max);
        self
    }

    pub fn no_max_selected(mut self) -> Self {
        self.max_selected = None;
        self
    }
}

impl Default for FileBrowser {
    fn default() -> Self {
        Self::new()
    }
}

impl StatefulWidget for FileBrowser {
    type State = FileBrowserState;

    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let items: Vec<(String, Style)> = state
            .items
            .iter()
            .enumerate()
            .map(|(i, (text, _))| {
                let is_dir = state.entries.get(i).map(|e| e.is_dir).unwrap_or(false);
                let style = if is_dir { self.dir_style } else { self.file_style };
                (text.clone(), style)
            })
            .collect();

        let mut scroll_state = state.choose_popup_state.scroll_list_state.clone();
        scroll_state.follow = false;

        let list = ScrollList::new_styled(items)
            .highlight_style(self.highlight_style)
            .highlight_symbol(&self.highlight_symbol);
        list.render(area, buf, &mut scroll_state);

        state.choose_popup_state.scroll_list_state = scroll_state;
    }
}