feat: expand connected databases in a single explorer sidebar
This commit is contained in:
+13
-44
@@ -4,7 +4,7 @@ use ratatui::{
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
text::Line,
|
||||
widgets::{Block, Cell, List, ListItem, ListState, Paragraph, Row, Table, TableState},
|
||||
widgets::{Block, Cell, Paragraph, Row, Table, TableState},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -15,8 +15,7 @@ use crate::{
|
||||
|
||||
pub struct App {
|
||||
db: Database,
|
||||
relations: Vec<Relation>,
|
||||
selection: ListState,
|
||||
pub(crate) relations: Vec<Relation>,
|
||||
active: Option<usize>,
|
||||
columns: Vec<Column>,
|
||||
page: Page,
|
||||
@@ -24,7 +23,6 @@ pub struct App {
|
||||
rows_state: TableState,
|
||||
schema_state: TableState,
|
||||
column_offset: usize,
|
||||
pub(crate) sidebar_focus: bool,
|
||||
show_schema: bool,
|
||||
error: Option<String>,
|
||||
}
|
||||
@@ -36,7 +34,6 @@ impl App {
|
||||
let mut app = Self {
|
||||
db,
|
||||
relations,
|
||||
selection: ListState::default().with_selected(selected),
|
||||
active: None,
|
||||
columns: Vec::new(),
|
||||
page: Page {
|
||||
@@ -47,20 +44,15 @@ impl App {
|
||||
rows_state: TableState::default(),
|
||||
schema_state: TableState::default(),
|
||||
column_offset: 0,
|
||||
sidebar_focus: true,
|
||||
show_schema: false,
|
||||
error: None,
|
||||
};
|
||||
app.open_selection();
|
||||
app.open_relation(selected);
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_key(&mut self, key: KeyEvent) {
|
||||
match key.code {
|
||||
KeyCode::Enter if self.sidebar_focus => {
|
||||
self.open_selection();
|
||||
self.sidebar_focus = false;
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => self.move_selection(1),
|
||||
KeyCode::Up | KeyCode::Char('k') => self.move_selection(-1),
|
||||
KeyCode::Home | KeyCode::Char('g') => self.move_selection(isize::MIN),
|
||||
@@ -69,10 +61,10 @@ impl App {
|
||||
self.show_schema = !self.show_schema;
|
||||
self.column_offset = 0;
|
||||
}
|
||||
KeyCode::Left | KeyCode::Char('h') if !self.sidebar_focus => {
|
||||
KeyCode::Left | KeyCode::Char('h') => {
|
||||
self.column_offset = self.column_offset.saturating_sub(1);
|
||||
}
|
||||
KeyCode::Right | KeyCode::Char('l') if !self.sidebar_focus => {
|
||||
KeyCode::Right | KeyCode::Char('l') => {
|
||||
let count = if self.show_schema {
|
||||
5
|
||||
} else {
|
||||
@@ -94,10 +86,7 @@ impl App {
|
||||
}
|
||||
|
||||
fn move_selection(&mut self, delta: isize) {
|
||||
if self.sidebar_focus {
|
||||
let next = moved_index(self.selection.selected(), self.relations.len(), delta);
|
||||
self.selection.select(next);
|
||||
} else if self.show_schema {
|
||||
if self.show_schema {
|
||||
let next = moved_index(self.schema_state.selected(), self.columns.len(), delta);
|
||||
self.schema_state.select(next);
|
||||
} else {
|
||||
@@ -106,8 +95,8 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn open_selection(&mut self) {
|
||||
self.active = self.selection.selected();
|
||||
pub(crate) fn open_relation(&mut self, index: Option<usize>) {
|
||||
self.active = index;
|
||||
self.columns.clear();
|
||||
self.page = Page {
|
||||
rows: Vec::new(),
|
||||
@@ -156,15 +145,14 @@ impl App {
|
||||
.position(|relation| Some(relation.id) == id)
|
||||
.or_else(|| (!relations.is_empty()).then_some(0));
|
||||
self.relations = relations;
|
||||
self.selection = ListState::default().with_selected(selected);
|
||||
self.open_selection();
|
||||
self.open_relation(selected);
|
||||
}
|
||||
Err(error) => self.error = Some(error_text(error.as_ref())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn draw(&mut self, frame: &mut Frame, area: Rect, focused: bool) {
|
||||
if area.width < 46 || area.height < 12 {
|
||||
if area.width < 38 || area.height < 12 {
|
||||
frame.render_widget(
|
||||
Paragraph::new("Enlarge terminal to at least 70×12. q to quit."),
|
||||
area,
|
||||
@@ -187,32 +175,13 @@ impl App {
|
||||
.style(Style::default().fg(Color::Cyan)),
|
||||
header,
|
||||
);
|
||||
let [sidebar, content] =
|
||||
Layout::horizontal([Constraint::Length(28), Constraint::Min(1)]).areas(body);
|
||||
let sidebar_title = format!(" Relations ({}) ", self.relations.len());
|
||||
let sidebar_block = panel(&sidebar_title, focused && self.sidebar_focus);
|
||||
if self.relations.is_empty() {
|
||||
frame.render_widget(
|
||||
Paragraph::new("No readable tables or views.\nr to refresh.").block(sidebar_block),
|
||||
sidebar,
|
||||
);
|
||||
} else {
|
||||
let list = List::new(
|
||||
self.relations
|
||||
.iter()
|
||||
.map(|relation| ListItem::new(relation.label.as_str())),
|
||||
)
|
||||
.block(sidebar_block)
|
||||
.highlight_style(selected_style())
|
||||
.highlight_symbol("› ");
|
||||
frame.render_stateful_widget(list, sidebar, &mut self.selection);
|
||||
}
|
||||
let content = body;
|
||||
let title = if self.show_schema {
|
||||
" Schema · s for rows "
|
||||
} else {
|
||||
" Rows · s for schema "
|
||||
};
|
||||
let content_block = panel(title, focused && !self.sidebar_focus);
|
||||
let content_block = panel(title, focused);
|
||||
let count = if self.show_schema {
|
||||
5
|
||||
} else {
|
||||
@@ -339,7 +308,7 @@ impl App {
|
||||
);
|
||||
}
|
||||
frame.render_widget(Paragraph::new(
|
||||
" Tab focus · Esc databases · ↑↓/jk move · Enter open · s schema/rows\n ←→/hl columns · n/p page · r refresh · c add database · q quit"
|
||||
" Tab focus · Esc explorer · ↑↓/jk move · Enter open · s schema/rows\n ←→/hl columns · n/p page · r refresh · c add database · q quit"
|
||||
).style(Style::default().fg(Color::DarkGray)), help);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user