basalt-tui 0.4.0

Basalt TUI application for Obsidian notes.
Documentation
use std::marker::PhantomData;

use basalt_core::obsidian::Vault;
use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Color, Style, Stylize},
    widgets::{Block, BorderType, List, ListItem, ListState, StatefulWidgetRef},
};

#[derive(Debug, Default, Clone, PartialEq)]
pub struct VaultSelectorState<'a> {
    pub(crate) selected_item_index: Option<usize>,
    pub(crate) items: Vec<&'a Vault>,
    list_state: ListState,
}

impl<'a> VaultSelectorState<'a> {
    pub fn new(items: Vec<&'a Vault>) -> Self {
        VaultSelectorState {
            items,
            selected_item_index: None,
            list_state: ListState::default().with_selected(Some(0)),
        }
    }

    pub fn select(&self) -> Self {
        Self {
            selected_item_index: self.list_state.selected(),
            ..self.clone()
        }
    }

    pub fn items(self) -> Vec<&'a Vault> {
        self.items
    }

    pub fn get_item(self, index: usize) -> Option<&'a Vault> {
        self.items.get(index).cloned()
    }

    pub fn selected(&self) -> Option<usize> {
        self.selected_item_index
    }

    pub fn next(mut self) -> Self {
        let index = self
            .list_state
            .selected()
            .map(|i| (i + 1).min(self.items.len() - 1));

        self.list_state.select(index);

        Self {
            list_state: self.list_state,
            ..self
        }
    }

    pub fn previous(mut self) -> Self {
        self.list_state.select_previous();

        Self {
            list_state: self.list_state,
            ..self
        }
    }
}

#[derive(Default)]
pub struct VaultSelector<'a> {
    _lifetime: PhantomData<&'a ()>,
}

impl<'a> StatefulWidgetRef for VaultSelector<'a> {
    type State = VaultSelectorState<'a>;

    fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let items: Vec<ListItem> = state
            .items
            .iter()
            .map(|item| {
                if item.open {
                    ListItem::new(format!("{}", item.name))
                } else {
                    ListItem::new(format!("  {}", item.name))
                }
            })
            .collect();

        List::new(items)
            .block(
                Block::bordered()
                    .black()
                    .title(" Vaults ")
                    .title_style(Style::default().italic().bold())
                    .border_type(BorderType::Rounded),
            )
            .fg(Color::default())
            .highlight_style(Style::new().reversed().dark_gray())
            .highlight_symbol(" ")
            .render_ref(area, buf, &mut state.list_state);
    }
}