feat: delete container, closes #27

Enable a user to delete a container. A dialog will pop up to ask the user to confirm the deletion. A user can then click on either button, or press N/Y to make a selection
This commit is contained in:
Jack Wills
2023-03-30 02:12:03 +00:00
parent 7c92ffef7d
commit 937202fe34
9 changed files with 310 additions and 55 deletions
+116 -16
View File
@@ -21,7 +21,7 @@ use crate::{
app_error::AppError,
};
use super::gui_state::{BoxLocation, Region};
use super::gui_state::{BoxLocation, DeleteButton, Region};
use super::{GuiState, SelectablePanel};
const NAME_TEXT: &str = r#"
@@ -43,6 +43,14 @@ const MARGIN: &str = " ";
const ARROW: &str = "";
const CIRCLE: &str = "";
/// From a given &str, return the maximum number of chars on a single line
fn max_line_width(text: &str) -> usize {
text.lines()
.map(|i| i.chars().count())
.max()
.unwrap_or_default()
}
/// Generate block, add a border if is the selected panel,
/// add custom title based on state of each panel
fn generate_block<'a>(
@@ -53,7 +61,7 @@ fn generate_block<'a>(
) -> Block<'a> {
gui_state
.lock()
.update_heading_map(Region::Panel(panel), area);
.update_region_map(Region::Panel(panel), area);
let current_selected_panel = gui_state.lock().selected_panel;
let mut title = match panel {
SelectablePanel::Containers => {
@@ -459,7 +467,7 @@ pub fn heading_bar<B: Backend>(
let rect = headers_section[index];
gui_state
.lock()
.update_heading_map(Region::Header(header), rect);
.update_region_map(Region::Header(header), rect);
f.render_widget(paragraph, rect);
}
}
@@ -479,14 +487,6 @@ pub fn heading_bar<B: Backend>(
f.render_widget(help_paragraph, split_bar[help_index]);
}
/// From a given &str, return the maximum number of chars on a single line
fn max_line_width(text: &str) -> usize {
text.lines()
.map(|i| i.chars().count())
.max()
.unwrap_or_default()
}
/// Help popup box needs these three pieces of information
struct HelpInfo {
spans: Vec<Spans<'static>>,
@@ -593,11 +593,7 @@ impl HelpInfo {
button_item("h"),
button_desc("to toggle this help information"),
]),
Spans::from(vec![
space(),
button_item("0"),
button_desc("to stop sort"),
]),
Spans::from(vec![space(), button_item("0"), button_desc("to stop sort")]),
Spans::from(vec![
space(),
button_item("1 - 9"),
@@ -728,6 +724,110 @@ pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) {
f.render_widget(block, area);
}
/// Draw the delete confirm box in the centre of the screen
/// take in container id and container name here?
pub fn delete_confirm<B: Backend>(
f: &mut Frame<'_, B>,
gui_state: &Arc<Mutex<GuiState>>,
name: &str,
) {
let block = Block::default()
.title(" Confirm Delete ")
.border_type(BorderType::Rounded)
.style(Style::default().bg(Color::White).fg(Color::Black))
.title_alignment(Alignment::Center)
.borders(Borders::ALL);
let confirm = Spans::from(vec![
Span::from("Are you sure you want to delete container: "),
Span::styled(
name,
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
),
]);
let yes_text = " (Y)es ";
let no_text = " (N)o ";
// Find the maximum line width & height, and add some padding
let max_line_width = u16::try_from(confirm.width()).unwrap_or(64) + 12;
let lines = 8;
let confirm_para = Paragraph::new(confirm).alignment(Alignment::Center);
let button_block = || {
Block::default()
.border_type(BorderType::Rounded)
.borders(Borders::ALL)
};
let yes_para = Paragraph::new(yes_text)
.alignment(Alignment::Center)
.block(button_block());
// Need to add some padding for the borders
let yes_chars = u16::try_from(yes_text.chars().count() + 2).unwrap_or(9);
let no_para = Paragraph::new(no_text)
.alignment(Alignment::Center)
.block(button_block());
// Need to add some padding for the borders
let no_chars = u16::try_from(no_text.chars().count() + 2).unwrap_or(8);
let area = popup(
lines,
max_line_width.into(),
f.size(),
BoxLocation::MiddleCentre,
);
let split_popup = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Min(2),
Constraint::Max(1),
Constraint::Max(1),
Constraint::Max(3),
Constraint::Min(1),
]
.as_ref(),
)
.split(area);
let button_spacing = (max_line_width - no_chars - yes_chars) / 3;
let split_buttons = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Min(button_spacing),
Constraint::Max(no_chars),
Constraint::Min(button_spacing),
Constraint::Max(yes_chars),
Constraint::Min(button_spacing),
]
.as_ref(),
)
.split(split_popup[3]);
let no_area = split_buttons[1];
let yes_area = split_buttons[3];
// Insert button areas into region map, so can interact with them on click
gui_state
.lock()
.update_region_map(Region::Delete(DeleteButton::No), no_area);
gui_state
.lock()
.update_region_map(Region::Delete(DeleteButton::Yes), yes_area);
f.render_widget(Clear, area);
f.render_widget(block, area);
f.render_widget(confirm_para, split_popup[1]);
f.render_widget(no_para, no_area);
f.render_widget(yes_para, yes_area);
}
/// Draw an error popup over whole screen
pub fn error<B: Backend>(f: &mut Frame<'_, B>, error: AppError, seconds: Option<u8>) {
let block = Block::default()