use ratatui::{
Frame,
crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind},
layout::{Constraint, Flex},
style::Style,
widgets::{Clear, List, ListState, Paragraph, Row, Table, TableState, Wrap},
};
use serde_json::Value;
use crate::PostEventAction;
pub struct DetailsScreen {
value: Value,
list_state: ListState,
table_state: TableState,
}
impl DetailsScreen {
pub fn new(value: Value) -> Self {
Self {
value,
list_state: ListState::default().with_selected(Some(0)),
table_state: TableState::new().with_selected_cell(Some((0, 1))),
}
}
pub fn handle_event(&mut self, event: Event) -> Option<PostEventAction> {
match event {
Event::Key(KeyEvent {
code: KeyCode::Esc | KeyCode::Char('q'),
kind: KeyEventKind::Press,
..
}) => Some(PostEventAction::PopScreen),
Event::Key(KeyEvent {
code: KeyCode::Down | KeyCode::Char('j'),
kind: KeyEventKind::Press | KeyEventKind::Repeat,
..
}) => {
if self.value.is_array() {
self.list_state.select_next();
} else if self.value.is_object() {
self.table_state.select_next();
}
None
}
Event::Key(KeyEvent {
code: KeyCode::Up | KeyCode::Char('k'),
kind: KeyEventKind::Press | KeyEventKind::Repeat,
..
}) => {
if self.value.is_array() {
self.list_state.select_previous();
} else if self.value.is_object() {
self.table_state.select_previous();
}
None
}
Event::Key(KeyEvent {
code: KeyCode::Enter,
kind: KeyEventKind::Press,
..
}) => match &self.value {
Value::Array(values) => Some(PostEventAction::PushScreen(
DetailsScreen::new(values[self.list_state.selected().unwrap()].clone()).into(),
)),
Value::Object(map) => Some(PostEventAction::PushScreen(
DetailsScreen::new(
map.values()
.nth(self.table_state.selected().unwrap())
.cloned()
.unwrap(),
)
.into(),
)),
_ => None,
},
_ => None,
}
}
pub fn render(&mut self, frame: &mut Frame) {
frame.render_widget(Clear, frame.area());
match &self.value {
Value::Null => frame.render_widget("null", frame.area()),
Value::Bool(value) => frame.render_widget(value.to_string(), frame.area()),
Value::Number(number) => frame.render_widget(number.to_string(), frame.area()),
Value::String(text) => frame.render_widget(
Paragraph::new(text.clone()).wrap(Wrap { trim: false }),
frame.area(),
),
Value::Array(values) => {
let list = List::new(values.iter().map(|value| value.to_string()))
.highlight_style(Style::new().reversed());
frame.render_stateful_widget(list, frame.area(), &mut self.list_state);
}
Value::Object(map) => {
let rows = map
.iter()
.map(|(key, value)| Row::new([key.clone(), value.to_string()]));
let widest_key_length = map.keys().fold(0, |acc, value| acc.max(value.len()));
let table = Table::new(
rows,
[
Constraint::Length(widest_key_length.min(30) as u16),
Constraint::Percentage(100),
],
)
.flex(Flex::Start)
.cell_highlight_style(Style::new().reversed());
frame.render_stateful_widget(table, frame.area(), &mut self.table_state);
}
}
}
}