2021-07-09 06:51:08 +00:00
|
|
|
use super::{
|
2021-07-18 15:50:39 +00:00
|
|
|
utils::scroll_vertical::VerticalScroll, Component, DrawableComponent, EventState,
|
2021-09-17 06:16:31 +00:00
|
|
|
StatefulDrawableComponent, TableStatusComponent, TableValueComponent,
|
2021-07-09 06:51:08 +00:00
|
|
|
};
|
2021-07-31 16:03:39 +00:00
|
|
|
use crate::components::command::{self, CommandInfo};
|
|
|
|
use crate::config::KeyConfig;
|
2021-07-08 14:08:04 +00:00
|
|
|
use crate::event::Key;
|
|
|
|
use anyhow::Result;
|
2021-07-31 16:03:39 +00:00
|
|
|
use database_tree::{Database, Table as DTable};
|
2021-07-08 14:08:04 +00:00
|
|
|
use std::convert::From;
|
|
|
|
use tui::{
|
|
|
|
backend::Backend,
|
2021-07-09 06:51:08 +00:00
|
|
|
layout::{Constraint, Direction, Layout, Rect},
|
2021-07-23 18:16:17 +00:00
|
|
|
style::{Color, Modifier, Style},
|
2021-07-08 16:42:41 +00:00
|
|
|
widgets::{Block, Borders, Cell, Row, Table, TableState},
|
2021-07-08 14:08:04 +00:00
|
|
|
Frame,
|
|
|
|
};
|
2021-07-23 18:16:17 +00:00
|
|
|
use unicode_width::UnicodeWidthStr;
|
2021-07-08 14:08:04 +00:00
|
|
|
|
|
|
|
pub struct TableComponent {
|
|
|
|
pub headers: Vec<String>,
|
|
|
|
pub rows: Vec<Vec<String>>,
|
2021-07-18 15:50:39 +00:00
|
|
|
pub eod: bool,
|
2021-07-25 14:34:51 +00:00
|
|
|
pub selected_row: TableState,
|
2021-07-31 16:03:39 +00:00
|
|
|
table: Option<(Database, DTable)>,
|
2021-07-25 14:34:51 +00:00
|
|
|
selected_column: usize,
|
|
|
|
selection_area_corner: Option<(usize, usize)>,
|
2021-07-25 10:22:31 +00:00
|
|
|
column_page_start: std::cell::Cell<usize>,
|
|
|
|
scroll: VerticalScroll,
|
2021-07-31 16:03:39 +00:00
|
|
|
key_config: KeyConfig,
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
impl TableComponent {
|
|
|
|
pub fn new(key_config: KeyConfig) -> Self {
|
2021-07-08 14:08:04 +00:00
|
|
|
Self {
|
2021-07-25 14:34:51 +00:00
|
|
|
selected_row: TableState::default(),
|
2021-07-08 14:08:04 +00:00
|
|
|
headers: vec![],
|
|
|
|
rows: vec![],
|
2021-07-31 16:03:39 +00:00
|
|
|
table: None,
|
2021-07-25 14:34:51 +00:00
|
|
|
selected_column: 0,
|
|
|
|
selection_area_corner: None,
|
2021-07-23 18:16:17 +00:00
|
|
|
column_page_start: std::cell::Cell::new(0),
|
2021-09-05 13:50:38 +00:00
|
|
|
scroll: VerticalScroll::new(false, false),
|
2021-07-18 15:50:39 +00:00
|
|
|
eod: false,
|
2021-07-31 16:03:39 +00:00
|
|
|
key_config,
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
fn title(&self) -> String {
|
|
|
|
self.table.as_ref().map_or(" - ".to_string(), |table| {
|
2021-08-28 03:46:33 +00:00
|
|
|
format!("{}.{}", table.0.name, table.1.name)
|
2021-07-31 16:03:39 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update(
|
|
|
|
&mut self,
|
|
|
|
rows: Vec<Vec<String>>,
|
|
|
|
headers: Vec<String>,
|
|
|
|
database: Database,
|
|
|
|
table: DTable,
|
|
|
|
) {
|
2021-09-12 06:50:59 +00:00
|
|
|
self.selected_row.select(None);
|
2021-07-18 15:50:39 +00:00
|
|
|
if !rows.is_empty() {
|
2021-07-31 16:03:39 +00:00
|
|
|
self.selected_row.select(Some(0))
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|
2021-07-31 16:03:39 +00:00
|
|
|
self.headers = headers;
|
|
|
|
self.rows = rows;
|
|
|
|
self.selected_column = 0;
|
|
|
|
self.selection_area_corner = None;
|
|
|
|
self.column_page_start = std::cell::Cell::new(0);
|
2021-09-05 13:50:38 +00:00
|
|
|
self.scroll = VerticalScroll::new(false, false);
|
2021-07-31 16:03:39 +00:00
|
|
|
self.eod = false;
|
|
|
|
self.table = Some((database, table));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reset(&mut self) {
|
|
|
|
self.selected_row.select(None);
|
|
|
|
self.headers = Vec::new();
|
|
|
|
self.rows = Vec::new();
|
|
|
|
self.selected_column = 0;
|
|
|
|
self.selection_area_corner = None;
|
|
|
|
self.column_page_start = std::cell::Cell::new(0);
|
2021-09-05 13:50:38 +00:00
|
|
|
self.scroll = VerticalScroll::new(false, false);
|
2021-07-31 16:03:39 +00:00
|
|
|
self.eod = false;
|
|
|
|
self.table = None;
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
fn reset_selection(&mut self) {
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selection_area_corner = None;
|
2021-07-25 10:22:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-18 15:50:39 +00:00
|
|
|
pub fn end(&mut self) {
|
|
|
|
self.eod = true;
|
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn next_row(&mut self, lines: usize) {
|
|
|
|
let i = match self.selected_row.selected() {
|
2021-07-08 14:08:04 +00:00
|
|
|
Some(i) => {
|
|
|
|
if i + lines >= self.rows.len() {
|
2021-09-12 06:50:59 +00:00
|
|
|
Some(self.rows.len().saturating_sub(1))
|
2021-07-08 14:08:04 +00:00
|
|
|
} else {
|
|
|
|
Some(i + lines)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => None,
|
|
|
|
};
|
2021-07-31 16:03:39 +00:00
|
|
|
self.reset_selection();
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_row.select(i);
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn previous_row(&mut self, lines: usize) {
|
|
|
|
let i = match self.selected_row.selected() {
|
2021-07-08 16:42:41 +00:00
|
|
|
Some(i) => {
|
|
|
|
if i <= lines {
|
|
|
|
Some(0)
|
|
|
|
} else {
|
2021-09-12 06:50:59 +00:00
|
|
|
Some(i.saturating_sub(lines))
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None => None,
|
|
|
|
};
|
2021-07-31 16:03:39 +00:00
|
|
|
self.reset_selection();
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_row.select(i);
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-08-16 18:18:46 +00:00
|
|
|
fn scroll_to_top(&mut self) {
|
2021-07-08 14:08:04 +00:00
|
|
|
if self.rows.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-31 16:03:39 +00:00
|
|
|
self.reset_selection();
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_row.select(Some(0));
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-08-16 18:18:46 +00:00
|
|
|
fn scroll_to_bottom(&mut self) {
|
2021-07-08 14:08:04 +00:00
|
|
|
if self.rows.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-31 16:03:39 +00:00
|
|
|
self.reset_selection();
|
2021-09-12 06:50:59 +00:00
|
|
|
self.selected_row
|
|
|
|
.select(Some(self.rows.len().saturating_sub(1)));
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn next_column(&mut self) {
|
2021-07-08 16:42:41 +00:00
|
|
|
if self.rows.is_empty() {
|
|
|
|
return;
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
2021-09-06 15:41:44 +00:00
|
|
|
self.reset_selection();
|
2021-07-25 14:34:51 +00:00
|
|
|
if self.selected_column >= self.headers.len().saturating_sub(1) {
|
2021-07-08 16:42:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_column += 1;
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn previous_column(&mut self) {
|
2021-07-08 16:42:41 +00:00
|
|
|
if self.rows.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
2021-09-06 15:41:44 +00:00
|
|
|
self.reset_selection();
|
2021-07-25 14:34:51 +00:00
|
|
|
if self.selected_column == 0 {
|
2021-07-08 16:42:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_column -= 1;
|
2021-07-25 10:22:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn expand_selected_area_x(&mut self, positive: bool) {
|
|
|
|
if self.selection_area_corner.is_none() {
|
|
|
|
self.selection_area_corner = Some((
|
|
|
|
self.selected_column,
|
|
|
|
self.selected_row.selected().unwrap_or(0),
|
2021-07-25 10:22:31 +00:00
|
|
|
));
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
if let Some((x, y)) = self.selection_area_corner {
|
|
|
|
self.selection_area_corner = Some((
|
2021-07-25 10:22:31 +00:00
|
|
|
if positive {
|
|
|
|
(x + 1).min(self.headers.len().saturating_sub(1))
|
|
|
|
} else {
|
|
|
|
x.saturating_sub(1)
|
|
|
|
},
|
|
|
|
y,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn expand_selected_area_y(&mut self, positive: bool) {
|
|
|
|
if self.selection_area_corner.is_none() {
|
|
|
|
self.selection_area_corner = Some((
|
|
|
|
self.selected_column,
|
|
|
|
self.selected_row.selected().unwrap_or(0),
|
2021-07-25 10:22:31 +00:00
|
|
|
));
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
if let Some((x, y)) = self.selection_area_corner {
|
|
|
|
self.selection_area_corner = Some((
|
2021-07-25 10:22:31 +00:00
|
|
|
x,
|
|
|
|
if positive {
|
|
|
|
(y + 1).min(self.rows.len().saturating_sub(1))
|
|
|
|
} else {
|
|
|
|
y.saturating_sub(1)
|
|
|
|
},
|
|
|
|
));
|
|
|
|
}
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 10:22:31 +00:00
|
|
|
pub fn selected_cells(&self) -> Option<String> {
|
2021-07-25 14:34:51 +00:00
|
|
|
if let Some((x, y)) = self.selection_area_corner {
|
|
|
|
let selected_row_index = self.selected_row.selected()?;
|
2021-07-25 10:22:31 +00:00
|
|
|
return Some(
|
|
|
|
self.rows[y.min(selected_row_index)..y.max(selected_row_index) + 1]
|
|
|
|
.iter()
|
|
|
|
.map(|row| {
|
2021-07-25 14:34:51 +00:00
|
|
|
row[x.min(self.selected_column)..x.max(self.selected_column) + 1].join(",")
|
2021-07-25 10:22:31 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join("\n"),
|
|
|
|
);
|
|
|
|
}
|
2021-07-09 06:51:08 +00:00
|
|
|
self.rows
|
2021-07-25 14:34:51 +00:00
|
|
|
.get(self.selected_row.selected()?)?
|
|
|
|
.get(self.selected_column)
|
2021-07-09 06:51:08 +00:00
|
|
|
.map(|cell| cell.to_string())
|
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn selected_column_index(&self) -> usize {
|
|
|
|
if let Some((x, _)) = self.selection_area_corner {
|
2021-07-25 10:22:31 +00:00
|
|
|
return x;
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_column
|
2021-07-25 10:22:31 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn is_selected_cell(
|
2021-07-25 10:22:31 +00:00
|
|
|
&self,
|
|
|
|
row_index: usize,
|
|
|
|
column_index: usize,
|
|
|
|
selected_column_index: usize,
|
|
|
|
) -> bool {
|
2021-07-25 14:34:51 +00:00
|
|
|
if let Some((x, y)) = self.selection_area_corner {
|
2021-07-25 10:22:31 +00:00
|
|
|
let x_in_page = x
|
|
|
|
.saturating_add(1)
|
|
|
|
.saturating_sub(self.column_page_start.get());
|
|
|
|
return matches!(
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_row.selected(),
|
|
|
|
Some(selected_row_index)
|
2021-07-25 10:22:31 +00:00
|
|
|
if (x_in_page.min(selected_column_index).max(1)..x_in_page.max(selected_column_index) + 1)
|
|
|
|
.contains(&column_index)
|
|
|
|
&& (y.min(selected_row_index)..y.max(selected_row_index) + 1)
|
|
|
|
.contains(&row_index)
|
|
|
|
);
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
matches!(
|
|
|
|
self.selected_row.selected(),
|
|
|
|
Some(selected_row_index) if row_index == selected_row_index && column_index == selected_column_index
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_number_column(&self, row_index: usize, column_index: usize) -> bool {
|
|
|
|
matches!(
|
|
|
|
self.selected_row.selected(),
|
|
|
|
Some(selected_row_index) if row_index == selected_row_index && 0 == column_index
|
|
|
|
)
|
2021-07-23 18:16:17 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn headers(&self, left: usize, right: usize) -> Vec<String> {
|
2021-07-23 18:16:17 +00:00
|
|
|
let mut headers = self.headers.clone()[left..right].to_vec();
|
2021-07-08 14:08:04 +00:00
|
|
|
headers.insert(0, "".to_string());
|
|
|
|
headers
|
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn rows(&self, left: usize, right: usize) -> Vec<Vec<String>> {
|
2021-07-08 14:08:04 +00:00
|
|
|
let rows = self
|
|
|
|
.rows
|
|
|
|
.iter()
|
2021-07-23 18:16:17 +00:00
|
|
|
.map(|row| row.to_vec())
|
2021-07-08 14:08:04 +00:00
|
|
|
.collect::<Vec<Vec<String>>>();
|
2021-07-23 18:16:17 +00:00
|
|
|
let mut new_rows: Vec<Vec<String>> =
|
|
|
|
rows.iter().map(|row| row[left..right].to_vec()).collect();
|
2021-07-08 14:08:04 +00:00
|
|
|
for (index, row) in new_rows.iter_mut().enumerate() {
|
|
|
|
row.insert(0, (index + 1).to_string())
|
|
|
|
}
|
|
|
|
new_rows
|
|
|
|
}
|
2021-07-23 18:16:17 +00:00
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
fn calculate_cell_widths(
|
2021-07-23 18:16:17 +00:00
|
|
|
&self,
|
|
|
|
area_width: u16,
|
|
|
|
) -> (usize, Vec<String>, Vec<Vec<String>>, Vec<Constraint>) {
|
|
|
|
if self.rows.is_empty() {
|
|
|
|
return (0, Vec::new(), Vec::new(), Vec::new());
|
|
|
|
}
|
2021-07-25 10:22:31 +00:00
|
|
|
if self.selected_column_index() < self.column_page_start.get() {
|
|
|
|
self.column_page_start.set(self.selected_column_index());
|
2021-07-23 18:16:17 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
let far_right_column_index = self.selected_column_index();
|
2021-07-25 10:22:31 +00:00
|
|
|
let mut column_index = self.selected_column_index();
|
2021-07-25 14:34:51 +00:00
|
|
|
let number_column_width = (self.rows.len() + 1).to_string().width() as u16;
|
|
|
|
let mut widths = Vec::new();
|
2021-07-23 18:16:17 +00:00
|
|
|
loop {
|
|
|
|
let length = self
|
2021-07-25 10:22:31 +00:00
|
|
|
.rows
|
2021-07-23 18:16:17 +00:00
|
|
|
.iter()
|
|
|
|
.map(|row| {
|
|
|
|
row.get(column_index)
|
|
|
|
.map_or(String::new(), |cell| cell.to_string())
|
|
|
|
.width()
|
|
|
|
})
|
|
|
|
.collect::<Vec<usize>>()
|
|
|
|
.iter()
|
|
|
|
.max()
|
|
|
|
.map_or(3, |v| {
|
|
|
|
*v.max(
|
|
|
|
&self
|
2021-07-25 10:22:31 +00:00
|
|
|
.headers
|
2021-07-23 18:16:17 +00:00
|
|
|
.get(column_index)
|
|
|
|
.map_or(3, |header| header.to_string().width()),
|
|
|
|
)
|
2021-07-25 10:22:31 +00:00
|
|
|
.clamp(&3, &20)
|
2021-07-23 18:16:17 +00:00
|
|
|
});
|
2021-09-16 04:20:11 +00:00
|
|
|
if widths.iter().map(|(_, width)| width).sum::<usize>() + length + widths.len() + 1
|
2021-07-25 14:34:51 +00:00
|
|
|
>= area_width.saturating_sub(number_column_width) as usize
|
2021-07-23 18:16:17 +00:00
|
|
|
{
|
|
|
|
column_index += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
widths.push((self.headers[column_index].clone(), length));
|
|
|
|
if column_index == self.column_page_start.get() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
column_index -= 1;
|
|
|
|
}
|
|
|
|
widths.reverse();
|
2021-07-25 14:34:51 +00:00
|
|
|
|
|
|
|
let far_left_column_index = column_index;
|
2021-07-23 18:16:17 +00:00
|
|
|
let selected_column_index = widths.len().saturating_sub(1);
|
2021-07-25 14:34:51 +00:00
|
|
|
let mut column_index = far_right_column_index + 1;
|
2021-07-25 10:22:31 +00:00
|
|
|
while widths.iter().map(|(_, width)| width).sum::<usize>() + widths.len()
|
2021-09-16 04:20:11 +00:00
|
|
|
< area_width.saturating_sub(number_column_width) as usize
|
2021-07-23 18:16:17 +00:00
|
|
|
{
|
|
|
|
let length = self
|
2021-07-25 10:22:31 +00:00
|
|
|
.rows
|
2021-07-23 18:16:17 +00:00
|
|
|
.iter()
|
|
|
|
.map(|row| {
|
|
|
|
row.get(column_index)
|
|
|
|
.map_or(String::new(), |cell| cell.to_string())
|
|
|
|
.width()
|
|
|
|
})
|
|
|
|
.collect::<Vec<usize>>()
|
|
|
|
.iter()
|
|
|
|
.max()
|
|
|
|
.map_or(3, |v| {
|
|
|
|
*v.max(
|
2021-07-25 10:22:31 +00:00
|
|
|
self.headers
|
2021-07-23 18:16:17 +00:00
|
|
|
.iter()
|
|
|
|
.map(|header| header.to_string().width())
|
|
|
|
.collect::<Vec<usize>>()
|
|
|
|
.get(column_index)
|
|
|
|
.unwrap_or(&3),
|
|
|
|
)
|
2021-07-25 10:22:31 +00:00
|
|
|
.clamp(&3, &20)
|
2021-07-23 18:16:17 +00:00
|
|
|
});
|
|
|
|
match self.headers.get(column_index) {
|
|
|
|
Some(header) => {
|
|
|
|
widths.push((header.to_string(), length));
|
|
|
|
}
|
|
|
|
None => break,
|
|
|
|
}
|
|
|
|
column_index += 1
|
|
|
|
}
|
2021-08-08 11:57:40 +00:00
|
|
|
if self.selected_column_index() != self.headers.len().saturating_sub(1)
|
|
|
|
&& column_index.saturating_sub(1) != self.headers.len().saturating_sub(1)
|
|
|
|
{
|
2021-07-23 18:16:17 +00:00
|
|
|
widths.pop();
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
let far_right_column_index = column_index;
|
2021-07-23 18:16:17 +00:00
|
|
|
let mut constraints = widths
|
|
|
|
.iter()
|
2021-07-25 10:22:31 +00:00
|
|
|
.map(|(_, width)| Constraint::Length(*width as u16))
|
2021-07-23 18:16:17 +00:00
|
|
|
.collect::<Vec<Constraint>>();
|
2021-08-08 11:57:40 +00:00
|
|
|
if self.selected_column_index() != self.headers.len().saturating_sub(1)
|
|
|
|
&& column_index.saturating_sub(1) != self.headers.len().saturating_sub(1)
|
|
|
|
{
|
2021-07-23 18:16:17 +00:00
|
|
|
constraints.push(Constraint::Min(10));
|
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
constraints.insert(0, Constraint::Length(number_column_width));
|
|
|
|
self.column_page_start.set(far_left_column_index);
|
|
|
|
|
2021-07-23 18:16:17 +00:00
|
|
|
(
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selection_area_corner
|
2021-07-25 10:22:31 +00:00
|
|
|
.map_or(selected_column_index + 1, |(x, _)| {
|
2021-07-25 14:34:51 +00:00
|
|
|
if x > self.selected_column {
|
2021-07-25 10:22:31 +00:00
|
|
|
(selected_column_index + 1)
|
2021-07-25 14:34:51 +00:00
|
|
|
.saturating_sub(x.saturating_sub(self.selected_column))
|
2021-07-25 10:22:31 +00:00
|
|
|
} else {
|
|
|
|
(selected_column_index + 1)
|
2021-07-25 14:34:51 +00:00
|
|
|
.saturating_add(self.selected_column.saturating_sub(x))
|
2021-07-25 10:22:31 +00:00
|
|
|
}
|
|
|
|
}),
|
2021-07-25 14:34:51 +00:00
|
|
|
self.headers(far_left_column_index, far_right_column_index),
|
|
|
|
self.rows(far_left_column_index, far_right_column_index),
|
2021-07-23 18:16:17 +00:00
|
|
|
constraints,
|
|
|
|
)
|
|
|
|
}
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
|
2021-09-17 06:16:31 +00:00
|
|
|
impl StatefulDrawableComponent for TableComponent {
|
2021-07-08 14:08:04 +00:00
|
|
|
fn draw<B: Backend>(&mut self, f: &mut Frame<B>, area: Rect, focused: bool) -> Result<()> {
|
2021-09-11 04:07:53 +00:00
|
|
|
let chunks = Layout::default()
|
|
|
|
.vertical_margin(1)
|
|
|
|
.horizontal_margin(1)
|
2021-07-09 06:51:08 +00:00
|
|
|
.direction(Direction::Vertical)
|
2021-09-11 04:07:53 +00:00
|
|
|
.constraints(
|
|
|
|
[
|
|
|
|
Constraint::Length(2),
|
|
|
|
Constraint::Min(1),
|
|
|
|
Constraint::Length(2),
|
|
|
|
]
|
|
|
|
.as_ref(),
|
|
|
|
)
|
2021-07-09 06:51:08 +00:00
|
|
|
.split(area);
|
|
|
|
|
2021-09-05 13:50:38 +00:00
|
|
|
f.render_widget(
|
|
|
|
Block::default()
|
|
|
|
.title(self.title())
|
|
|
|
.borders(Borders::ALL)
|
|
|
|
.style(if focused {
|
|
|
|
Style::default()
|
|
|
|
} else {
|
|
|
|
Style::default().fg(Color::DarkGray)
|
|
|
|
}),
|
2021-09-11 04:07:53 +00:00
|
|
|
area,
|
2021-09-05 13:50:38 +00:00
|
|
|
);
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
self.selected_row.selected().map_or_else(
|
2021-07-08 14:08:04 +00:00
|
|
|
|| {
|
|
|
|
self.scroll.reset();
|
|
|
|
},
|
|
|
|
|selection| {
|
|
|
|
self.scroll.update(
|
|
|
|
selection,
|
|
|
|
self.rows.len(),
|
2021-09-11 04:07:53 +00:00
|
|
|
chunks[1].height.saturating_sub(2) as usize,
|
2021-07-08 14:08:04 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2021-09-05 13:50:38 +00:00
|
|
|
let block = Block::default().borders(Borders::NONE);
|
2021-07-23 18:16:17 +00:00
|
|
|
let (selected_column_index, headers, rows, constraints) =
|
2021-09-05 13:50:38 +00:00
|
|
|
self.calculate_cell_widths(block.inner(chunks[0]).width);
|
2021-07-23 18:16:17 +00:00
|
|
|
let header_cells = headers.iter().enumerate().map(|(column_index, h)| {
|
|
|
|
Cell::from(h.to_string()).style(if selected_column_index == column_index {
|
|
|
|
Style::default().add_modifier(Modifier::BOLD)
|
|
|
|
} else {
|
|
|
|
Style::default()
|
|
|
|
})
|
|
|
|
});
|
2021-07-08 14:08:04 +00:00
|
|
|
let header = Row::new(header_cells).height(1).bottom_margin(1);
|
2021-07-08 16:42:41 +00:00
|
|
|
let rows = rows.iter().enumerate().map(|(row_index, item)| {
|
2021-07-08 14:08:04 +00:00
|
|
|
let height = item
|
|
|
|
.iter()
|
|
|
|
.map(|content| content.chars().filter(|c| *c == '\n').count())
|
|
|
|
.max()
|
|
|
|
.unwrap_or(0)
|
|
|
|
+ 1;
|
2021-07-09 06:51:08 +00:00
|
|
|
let cells = item.iter().enumerate().map(|(column_index, c)| {
|
2021-07-25 10:22:31 +00:00
|
|
|
Cell::from(c.to_string()).style(
|
|
|
|
if self.is_selected_cell(row_index, column_index, selected_column_index) {
|
|
|
|
Style::default().bg(Color::Blue)
|
2021-07-25 14:34:51 +00:00
|
|
|
} else if self.is_number_column(row_index, column_index) {
|
2021-07-25 10:22:31 +00:00
|
|
|
Style::default().add_modifier(Modifier::BOLD)
|
|
|
|
} else {
|
|
|
|
Style::default()
|
|
|
|
},
|
|
|
|
)
|
2021-07-08 16:42:41 +00:00
|
|
|
});
|
2021-07-08 14:08:04 +00:00
|
|
|
Row::new(cells).height(height as u16).bottom_margin(1)
|
|
|
|
});
|
2021-07-23 18:16:17 +00:00
|
|
|
|
2021-07-09 06:51:08 +00:00
|
|
|
let table = Table::new(rows)
|
2021-07-08 14:08:04 +00:00
|
|
|
.header(header)
|
2021-07-23 18:16:17 +00:00
|
|
|
.block(block)
|
2021-07-08 14:08:04 +00:00
|
|
|
.style(if focused {
|
|
|
|
Style::default()
|
|
|
|
} else {
|
|
|
|
Style::default().fg(Color::DarkGray)
|
|
|
|
})
|
2021-07-23 18:16:17 +00:00
|
|
|
.widths(&constraints);
|
2021-07-25 14:34:51 +00:00
|
|
|
let mut state = self.selected_row.clone();
|
2021-07-25 10:22:31 +00:00
|
|
|
f.render_stateful_widget(
|
|
|
|
table,
|
2021-09-11 04:07:53 +00:00
|
|
|
chunks[1],
|
2021-07-25 14:34:51 +00:00
|
|
|
if let Some((_, y)) = self.selection_area_corner {
|
|
|
|
state.select(Some(y));
|
|
|
|
&mut state
|
2021-07-25 10:22:31 +00:00
|
|
|
} else {
|
2021-07-25 14:34:51 +00:00
|
|
|
&mut self.selected_row
|
2021-07-25 10:22:31 +00:00
|
|
|
},
|
|
|
|
);
|
2021-07-08 14:08:04 +00:00
|
|
|
|
2021-09-11 04:07:53 +00:00
|
|
|
TableValueComponent::new(self.selected_cells().unwrap_or_default())
|
|
|
|
.draw(f, chunks[0], focused)?;
|
|
|
|
|
2021-09-05 13:50:38 +00:00
|
|
|
TableStatusComponent::new(
|
|
|
|
if self.rows.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.rows.len())
|
|
|
|
},
|
|
|
|
if self.headers.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.headers.len())
|
|
|
|
},
|
2021-09-05 14:40:48 +00:00
|
|
|
self.table.as_ref().map(|t| t.1.clone()),
|
2021-09-05 13:50:38 +00:00
|
|
|
)
|
2021-09-11 04:07:53 +00:00
|
|
|
.draw(f, chunks[2], focused)?;
|
2021-09-05 13:50:38 +00:00
|
|
|
|
2021-09-11 04:07:53 +00:00
|
|
|
self.scroll.draw(f, chunks[1]);
|
2021-07-08 14:08:04 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Component for TableComponent {
|
2021-07-31 16:03:39 +00:00
|
|
|
fn commands(&self, out: &mut Vec<CommandInfo>) {
|
|
|
|
out.push(CommandInfo::new(command::extend_selection_by_one_cell(
|
|
|
|
&self.key_config,
|
|
|
|
)));
|
|
|
|
}
|
2021-07-29 10:47:36 +00:00
|
|
|
|
2021-07-18 15:50:39 +00:00
|
|
|
fn event(&mut self, key: Key) -> Result<EventState> {
|
2021-07-31 16:03:39 +00:00
|
|
|
if key == self.key_config.scroll_left {
|
|
|
|
self.previous_column();
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.scroll_down {
|
|
|
|
self.next_row(1);
|
|
|
|
return Ok(EventState::NotConsumed);
|
|
|
|
} else if key == self.key_config.scroll_down_multiple_lines {
|
|
|
|
self.next_row(10);
|
|
|
|
return Ok(EventState::NotConsumed);
|
|
|
|
} else if key == self.key_config.scroll_up {
|
|
|
|
self.previous_row(1);
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.scroll_up_multiple_lines {
|
|
|
|
self.previous_row(10);
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.scroll_to_top {
|
2021-08-16 18:18:46 +00:00
|
|
|
self.scroll_to_top();
|
2021-07-31 16:03:39 +00:00
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.scroll_to_bottom {
|
2021-08-16 18:18:46 +00:00
|
|
|
self.scroll_to_bottom();
|
2021-07-31 16:03:39 +00:00
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.scroll_right {
|
|
|
|
self.next_column();
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.extend_selection_by_one_cell_left {
|
|
|
|
self.expand_selected_area_x(false);
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.extend_selection_by_one_cell_up {
|
|
|
|
self.expand_selected_area_y(false);
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.extend_selection_by_one_cell_down {
|
|
|
|
self.expand_selected_area_y(true);
|
|
|
|
return Ok(EventState::Consumed);
|
|
|
|
} else if key == self.key_config.extend_selection_by_one_cell_right {
|
|
|
|
self.expand_selected_area_x(true);
|
|
|
|
return Ok(EventState::Consumed);
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
2021-07-18 15:50:39 +00:00
|
|
|
Ok(EventState::NotConsumed)
|
2021-07-08 14:08:04 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-08 16:42:41 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2021-07-31 16:03:39 +00:00
|
|
|
use super::{KeyConfig, TableComponent};
|
2021-07-23 18:16:17 +00:00
|
|
|
use tui::layout::Constraint;
|
2021-07-08 16:42:41 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_headers() {
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-08 16:42:41 +00:00
|
|
|
component.headers = vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect();
|
2021-07-25 14:34:51 +00:00
|
|
|
assert_eq!(component.headers(1, 2), vec!["", "b"])
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rows() {
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-08 16:42:41 +00:00
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
assert_eq!(component.rows(1, 2), vec![vec!["1", "b"], vec!["2", "e"]],)
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|
2021-07-23 18:16:17 +00:00
|
|
|
|
2021-07-25 10:22:31 +00:00
|
|
|
#[test]
|
|
|
|
fn test_expand_selected_area_x_left() {
|
|
|
|
// before
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a b c
|
|
|
|
// 2 d |e| f
|
|
|
|
|
|
|
|
// after
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a b c
|
|
|
|
// 2 |d e| f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(1));
|
|
|
|
component.selected_column = 1;
|
2021-07-25 10:22:31 +00:00
|
|
|
component.expand_selected_area_x(false);
|
2021-07-25 14:34:51 +00:00
|
|
|
assert_eq!(component.selection_area_corner, Some((0, 1)));
|
2021-07-25 10:22:31 +00:00
|
|
|
assert_eq!(component.selected_cells(), Some("d,e".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expand_selected_area_x_right() {
|
|
|
|
// before
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a b c
|
|
|
|
// 2 d |e| f
|
|
|
|
|
|
|
|
// after
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a b c
|
|
|
|
// 2 d |e f|
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(1));
|
|
|
|
component.selected_column = 1;
|
2021-07-25 10:22:31 +00:00
|
|
|
component.expand_selected_area_x(true);
|
2021-07-25 14:34:51 +00:00
|
|
|
assert_eq!(component.selection_area_corner, Some((2, 1)));
|
2021-07-25 10:22:31 +00:00
|
|
|
assert_eq!(component.selected_cells(), Some("e,f".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expand_selected_area_y_up() {
|
|
|
|
// before
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a b c
|
|
|
|
// 2 d |e| f
|
|
|
|
|
|
|
|
// after
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a |b| c
|
|
|
|
// 2 d |e| f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(1));
|
|
|
|
component.selected_column = 1;
|
2021-07-25 10:22:31 +00:00
|
|
|
component.expand_selected_area_y(false);
|
2021-07-25 14:34:51 +00:00
|
|
|
assert_eq!(component.selection_area_corner, Some((1, 0)));
|
2021-07-25 10:22:31 +00:00
|
|
|
assert_eq!(component.selected_cells(), Some("b\ne".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expand_selected_area_y_down() {
|
|
|
|
// before
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a |b| c
|
|
|
|
// 2 d e f
|
|
|
|
|
|
|
|
// after
|
|
|
|
// 1 2 3
|
|
|
|
// 1 a |b| c
|
|
|
|
// 2 d |e| f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(0));
|
|
|
|
component.selected_column = 1;
|
2021-07-25 10:22:31 +00:00
|
|
|
component.expand_selected_area_y(true);
|
2021-07-25 14:34:51 +00:00
|
|
|
assert_eq!(component.selection_area_corner, Some((1, 1)));
|
2021-07-25 10:22:31 +00:00
|
|
|
assert_eq!(component.selected_cells(), Some("b\ne".to_string()));
|
|
|
|
}
|
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
#[test]
|
|
|
|
fn test_is_number_column() {
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 14:34:51 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
|
|
|
component.selected_row.select(Some(0));
|
|
|
|
assert!(component.is_number_column(0, 0));
|
|
|
|
assert!(!component.is_number_column(0, 1));
|
|
|
|
}
|
|
|
|
|
2021-07-25 10:22:31 +00:00
|
|
|
#[test]
|
|
|
|
fn test_selected_cell_when_one_cell_selected() {
|
|
|
|
// 1 2 3
|
|
|
|
// 1 |a| b c
|
|
|
|
// 2 d e f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(0));
|
2021-07-25 10:22:31 +00:00
|
|
|
assert_eq!(component.selected_cells(), Some("a".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_selected_cell_when_multiple_cells_selected() {
|
|
|
|
// 1 2 3
|
|
|
|
// 1 |a b| c
|
|
|
|
// 2 |d e| f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(0));
|
|
|
|
component.selection_area_corner = Some((1, 1));
|
2021-07-25 10:22:31 +00:00
|
|
|
assert_eq!(component.selected_cells(), Some("a,b\nd,e".to_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_selected_cell_when_one_cell_selected() {
|
|
|
|
// 1 2 3
|
|
|
|
// 1 |a| b c
|
|
|
|
// 2 d e f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(0));
|
2021-07-25 10:22:31 +00:00
|
|
|
// a
|
|
|
|
assert!(component.is_selected_cell(0, 1, 1));
|
|
|
|
// d
|
|
|
|
assert!(!component.is_selected_cell(1, 1, 1));
|
|
|
|
// e
|
|
|
|
assert!(!component.is_selected_cell(1, 2, 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_selected_cell_when_multiple_cells_selected() {
|
|
|
|
// 1 2 3
|
|
|
|
// 1 |a b| c
|
|
|
|
// 2 |d e| f
|
|
|
|
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 10:22:31 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
component.selected_row.select(Some(0));
|
|
|
|
component.selection_area_corner = Some((1, 1));
|
2021-07-25 10:22:31 +00:00
|
|
|
// a
|
|
|
|
assert!(component.is_selected_cell(0, 1, 1));
|
|
|
|
// b
|
|
|
|
assert!(component.is_selected_cell(0, 2, 1));
|
|
|
|
// d
|
|
|
|
assert!(component.is_selected_cell(1, 1, 1));
|
|
|
|
// e
|
|
|
|
assert!(component.is_selected_cell(1, 2, 1));
|
|
|
|
// f
|
|
|
|
assert!(!component.is_selected_cell(1, 3, 1));
|
|
|
|
}
|
|
|
|
|
2021-07-23 18:16:17 +00:00
|
|
|
#[test]
|
2021-08-08 11:57:40 +00:00
|
|
|
fn test_calculate_cell_widths_when_sum_of_cell_widths_is_greater_than_table_width() {
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-23 18:16:17 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["aaaaa", "bbbbb", "ccccc"]
|
|
|
|
.iter()
|
|
|
|
.map(|h| h.to_string())
|
|
|
|
.collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-25 14:34:51 +00:00
|
|
|
let (selected_column_index, headers, rows, constraints) =
|
|
|
|
component.calculate_cell_widths(10);
|
2021-07-23 18:16:17 +00:00
|
|
|
assert_eq!(selected_column_index, 1);
|
|
|
|
assert_eq!(headers, vec!["", "1", "2"]);
|
|
|
|
assert_eq!(rows, vec![vec!["1", "aaaaa", "bbbbb"], vec!["2", "d", "e"]]);
|
|
|
|
assert_eq!(
|
|
|
|
constraints,
|
|
|
|
vec![
|
|
|
|
Constraint::Length(1),
|
|
|
|
Constraint::Length(5),
|
|
|
|
Constraint::Min(10),
|
|
|
|
]
|
|
|
|
);
|
2021-08-08 11:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_calculate_cell_widths_when_sum_of_cell_widths_is_less_than_table_width() {
|
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["aaaaa", "bbbbb", "ccccc"]
|
|
|
|
.iter()
|
|
|
|
.map(|h| h.to_string())
|
|
|
|
.collect(),
|
|
|
|
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
|
|
|
|
];
|
2021-07-23 18:16:17 +00:00
|
|
|
|
2021-07-25 14:34:51 +00:00
|
|
|
let (selected_column_index, headers, rows, constraints) =
|
|
|
|
component.calculate_cell_widths(20);
|
2021-07-23 18:16:17 +00:00
|
|
|
assert_eq!(selected_column_index, 1);
|
|
|
|
assert_eq!(headers, vec!["", "1", "2", "3"]);
|
|
|
|
assert_eq!(
|
|
|
|
rows,
|
|
|
|
vec![
|
|
|
|
vec!["1", "aaaaa", "bbbbb", "ccccc"],
|
|
|
|
vec!["2", "d", "e", "f"]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
constraints,
|
|
|
|
vec![
|
|
|
|
Constraint::Length(1),
|
|
|
|
Constraint::Length(5),
|
|
|
|
Constraint::Length(5),
|
2021-08-08 11:57:40 +00:00
|
|
|
Constraint::Length(5),
|
2021-07-23 18:16:17 +00:00
|
|
|
]
|
|
|
|
);
|
2021-08-08 11:57:40 +00:00
|
|
|
}
|
2021-07-25 14:34:51 +00:00
|
|
|
|
2021-08-08 11:57:40 +00:00
|
|
|
#[test]
|
|
|
|
fn test_calculate_cell_widths_when_component_has_multiple_rows() {
|
2021-07-31 16:03:39 +00:00
|
|
|
let mut component = TableComponent::new(KeyConfig::default());
|
2021-07-25 14:34:51 +00:00
|
|
|
component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
|
|
|
|
component.rows = vec![
|
|
|
|
vec!["aaaaa", "bbbbb", "ccccc"]
|
|
|
|
.iter()
|
|
|
|
.map(|h| h.to_string())
|
|
|
|
.collect(),
|
|
|
|
vec!["dddddddddd", "e", "f"]
|
|
|
|
.iter()
|
|
|
|
.map(|h| h.to_string())
|
|
|
|
.collect(),
|
|
|
|
];
|
|
|
|
|
|
|
|
let (selected_column_index, headers, rows, constraints) =
|
|
|
|
component.calculate_cell_widths(20);
|
|
|
|
assert_eq!(selected_column_index, 1);
|
|
|
|
assert_eq!(headers, vec!["", "1", "2", "3"]);
|
|
|
|
assert_eq!(
|
|
|
|
rows,
|
|
|
|
vec![
|
|
|
|
vec!["1", "aaaaa", "bbbbb", "ccccc"],
|
|
|
|
vec!["2", "dddddddddd", "e", "f"]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
constraints,
|
|
|
|
vec![
|
|
|
|
Constraint::Length(1),
|
|
|
|
Constraint::Length(10),
|
|
|
|
Constraint::Length(5),
|
2021-08-08 11:57:40 +00:00
|
|
|
Constraint::Length(5),
|
2021-07-25 14:34:51 +00:00
|
|
|
]
|
|
|
|
);
|
2021-07-23 18:16:17 +00:00
|
|
|
}
|
2021-07-08 16:42:41 +00:00
|
|
|
}
|