trnovel 0.9.0

Terminal reader for novel
Documentation
use crossterm::event::{Event, KeyCode, KeyEventKind};
use ratatui::{
    layout::{Alignment, Constraint},
    text::Line,
    widgets::{Block, Scrollbar},
};
use ratatui_kit::prelude::*;
use std::path::PathBuf;
use tui_tree_widget::{TreeItem, TreeState};

use crate::hooks::UseThemeConfig;

#[derive(Default, Props)]
pub struct FileSelectProps {
    pub items: Vec<TreeItem<'static, PathBuf>>,
    pub on_select: Handler<'static, PathBuf>,
    pub default_value: Option<usize>,
    pub top_title: Option<Line<'static>>,
    pub bottom_title: Option<Line<'static>>,
    pub is_editing: bool,
    pub empty_message: String,
}

#[component]
pub fn FileSelect(props: &mut FileSelectProps, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
    let state = hooks.use_state(TreeState::default);
    let theme = hooks.use_theme_config();

    let is_empty = props.items.is_empty();

    let mut on_select = props.on_select.take();

    hooks.use_events({
        let is_editing = props.is_editing;
        move |event| {
            if let Event::Key(key) = event
                && key.kind == KeyEventKind::Press
                && is_editing
            {
                match key.code {
                    KeyCode::Char('h') | KeyCode::Left => {
                        state.write().key_left();
                    }
                    KeyCode::Char('j') | KeyCode::Down => {
                        state.write().key_down();
                    }
                    KeyCode::Char('k') | KeyCode::Up => {
                        state.write().key_up();
                    }
                    KeyCode::Char('l') | KeyCode::Right | KeyCode::Enter => {
                        let res: Option<PathBuf> = state.read().selected().last().cloned();
                        if let Some(path) = res {
                            state.write().toggle_selected();
                            if path.is_file() {
                                on_select(path);
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
    });

    let mut border = Block::bordered().border_style(theme.basic.border);

    if let Some(title) = props.top_title.clone() {
        border = border.title_top(title);
    }
    if let Some(title) = props.bottom_title.clone() {
        border = border.title_bottom(title);
    }

    if is_empty {
        return element!(
            Border(
                top_title: props.top_title.clone(),
                bottom_title: props.bottom_title.clone(),
                border_style: theme.basic.border,
            ){
                Center(
                    height:Constraint::Length(5),
                    width:Constraint::Percentage(50)
                ){
                    Text(
                        text: props.empty_message.clone(),
                        alignment: Alignment::Center,
                        style: theme.colors.warning_color,
                        wrap: true,
                    )
                }
            }
        )
        .into_any();
    }

    element!(TreeSelect<PathBuf>(
        style: theme.basic.text,
        highlight_style: theme.selected,
        state: state,
        items: props.items.clone(),
        scrollbar: Scrollbar::default(),
        block: border,
    ))
    .into_any()
}