use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratada::nav::cycle;
use ratatui::Frame;
use ratatui::layout::Rect;
use ratatui::style::Style;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Clear, List, ListItem, ListState};
use crate::domain::sections::UNGROUPED;
use crate::theme::Skin;
use crate::tui::skin::Colors;
use crate::tui::widgets::centered_rect;
pub enum SectionsAction {
Pending,
Close,
New,
Rename(String),
Delete(String),
Move {
from: usize,
to: usize,
},
}
pub struct SectionsModal {
names: Vec<String>,
cursor: usize,
}
impl SectionsModal {
pub fn new(names: Vec<String>, cursor: usize) -> Self {
let cursor = cursor.min(names.len().saturating_sub(1));
SectionsModal { names, cursor }
}
pub fn cursor(&self) -> usize {
self.cursor
}
pub fn handle_key(&mut self, key: KeyEvent) -> SectionsAction {
let alt = key.modifiers.contains(KeyModifiers::ALT);
match key.code {
KeyCode::Esc => SectionsAction::Close,
KeyCode::Up if alt => self.move_by(-1),
KeyCode::Down if alt => self.move_by(1),
KeyCode::Up => self.step(-1),
KeyCode::Down => self.step(1),
KeyCode::Char('n') => SectionsAction::New,
KeyCode::Char('r') => self.rename_action(),
KeyCode::Char('d') => self.delete_action(),
_ => SectionsAction::Pending,
}
}
fn step(&mut self, delta: isize) -> SectionsAction {
if !self.names.is_empty() {
self.cursor = cycle(self.cursor, self.names.len(), delta);
}
SectionsAction::Pending
}
fn move_by(&mut self, delta: isize) -> SectionsAction {
let target = self.cursor as isize + delta;
if target < 0 || target as usize >= self.names.len() {
return SectionsAction::Pending;
}
SectionsAction::Move {
from: self.cursor,
to: target as usize,
}
}
fn rename_action(&self) -> SectionsAction {
match self.names.get(self.cursor) {
Some(name) => SectionsAction::Rename(name.clone()),
None => SectionsAction::Pending,
}
}
fn delete_action(&self) -> SectionsAction {
match self.names.get(self.cursor) {
Some(name) => SectionsAction::Delete(name.clone()),
None => SectionsAction::Pending,
}
}
pub fn render(&self, frame: &mut Frame, area: Rect, skin: &Skin) {
let colors = Colors::from_palette(&skin.palette);
let height = (self.names.len() as u16 + 5).clamp(6, 18);
let rect = centered_rect(50, height, area);
frame.render_widget(Clear, rect);
let block = ratada::chrome::modal_block(skin, "Manage sections");
let inner = block.inner(rect);
frame.render_widget(block, rect);
let mut items: Vec<ListItem> = self
.names
.iter()
.map(|name| ListItem::new(Line::raw(name.clone())))
.collect();
items.push(ListItem::new(Line::from(Span::styled(
format!("{UNGROUPED} (auto)"),
Style::default().fg(colors.dim),
))));
items.push(ListItem::new(Line::raw("")));
items.push(ListItem::new(Line::from(Span::styled(
"n new · r rename · d delete · Alt+\u{2191}\u{2193} move · Esc close",
Style::default().fg(colors.dim),
))));
let list = List::new(items).highlight_style(colors.selection_style());
let mut state = ListState::default();
if !self.names.is_empty() {
state.select(Some(self.cursor));
}
frame.render_stateful_widget(list, inner, &mut state);
}
}