add_table now ported to the canvas library also

This commit is contained in:
Priec
2025-07-30 14:06:05 +02:00
parent 3c2eef9596
commit 0011ba0c04
5 changed files with 177 additions and 62 deletions

View File

@@ -3,8 +3,7 @@ use crate::config::colors::themes::Theme;
use crate::state::app::highlight::HighlightState; use crate::state::app::highlight::HighlightState;
use crate::state::app::state::AppState; use crate::state::app::state::AppState;
use crate::state::pages::add_table::{AddTableFocus, AddTableState}; use crate::state::pages::add_table::{AddTableFocus, AddTableState};
use crate::state::pages::canvas_state::CanvasState; use canvas::canvas::{render_canvas, CanvasState, HighlightState as CanvasHighlightState}; // Use canvas library
// use crate::state::pages::add_table::{ColumnDefinition, LinkDefinition}; // Not directly used here
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect}, layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Modifier, Style}, style::{Modifier, Style},
@@ -12,16 +11,24 @@ use ratatui::{
widgets::{Block, BorderType, Borders, Cell, Paragraph, Row, Table}, widgets::{Block, BorderType, Borders, Cell, Paragraph, Row, Table},
Frame, Frame,
}; };
use crate::components::handlers::canvas::render_canvas;
use crate::components::common::dialog; use crate::components::common::dialog;
// Helper function to convert between HighlightState types
fn convert_highlight_state(local: &HighlightState) -> CanvasHighlightState {
match local {
HighlightState::Off => CanvasHighlightState::Off,
HighlightState::Characterwise { anchor } => CanvasHighlightState::Characterwise { anchor: *anchor },
HighlightState::Linewise { anchor_line } => CanvasHighlightState::Linewise { anchor_line: *anchor_line },
}
}
/// Renders the Add New Table page layout, structuring the display of table information, /// Renders the Add New Table page layout, structuring the display of table information,
/// input fields, and action buttons. Adapts layout based on terminal width. /// input fields, and action buttons. Adapts layout based on terminal width.
pub fn render_add_table( pub fn render_add_table(
f: &mut Frame, f: &mut Frame,
area: Rect, area: Rect,
theme: &Theme, theme: &Theme,
app_state: &AppState, // Currently unused, might be needed later app_state: &AppState,
add_table_state: &mut AddTableState, add_table_state: &mut AddTableState,
is_edit_mode: bool, // Determines if canvas inputs are in edit mode is_edit_mode: bool, // Determines if canvas inputs are in edit mode
highlight_state: &HighlightState, // For text highlighting in canvas highlight_state: &HighlightState, // For text highlighting in canvas
@@ -349,17 +356,15 @@ pub fn render_add_table(
&mut add_table_state.column_table_state, &mut add_table_state.column_table_state,
); );
// --- Canvas Rendering (Column Definition Input) --- // --- Canvas Rendering (Column Definition Input) - USING CANVAS LIBRARY ---
let canvas_highlight_state = convert_highlight_state(highlight_state);
let _active_field_rect = render_canvas( let _active_field_rect = render_canvas(
f, f,
canvas_area, canvas_area,
add_table_state, add_table_state, // AddTableState implements CanvasState
&add_table_state.fields(), theme, // Theme implements CanvasTheme
&add_table_state.current_field(),
&add_table_state.inputs(),
theme,
is_edit_mode && focus_on_canvas_inputs, is_edit_mode && focus_on_canvas_inputs,
highlight_state, &canvas_highlight_state,
); );
// --- Button Style Helpers --- // --- Button Style Helpers ---
@@ -557,7 +562,7 @@ pub fn render_add_table(
// --- DIALOG --- // --- DIALOG ---
// Render the dialog overlay if it's active // Render the dialog overlay if it's active
if app_state.ui.dialog.dialog_show { // Use the passed-in app_state if app_state.ui.dialog.dialog_show {
dialog::render_dialog( dialog::render_dialog(
f, f,
f.area(), // Render over the whole frame area f.area(), // Render over the whole frame area

View File

@@ -1,7 +1,7 @@
// src/functions/modes/edit/add_table_e.rs // src/functions/modes/edit/add_table_e.rs
use crate::state::pages::add_table::AddTableState; use crate::state::pages::add_table::AddTableState;
use crate::state::pages::canvas_state::CanvasState; // Use trait
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use canvas::canvas::CanvasState;
use anyhow::Result; use anyhow::Result;
#[derive(PartialEq)] #[derive(PartialEq)]

View File

@@ -1,8 +1,8 @@
// src/functions/modes/read_only/add_table_ro.rs // src/functions/modes/read_only/add_table_ro.rs
use crate::config::binds::key_sequences::KeySequenceTracker; use crate::config::binds::key_sequences::KeySequenceTracker;
use crate::state::pages::add_table::AddTableState; use crate::state::pages::add_table::AddTableState;
use crate::state::pages::canvas_state::CanvasState;
use crate::state::app::state::AppState; use crate::state::app::state::AppState;
use canvas::canvas::CanvasState;
use anyhow::Result; use anyhow::Result;
// Re-use word navigation helpers if they are public or move them to a common module // Re-use word navigation helpers if they are public or move them to a common module

View File

@@ -348,10 +348,10 @@ pub async fn handle_edit_event(
) )
.await? .await?
} else if app_state.ui.show_add_table { } else if app_state.ui.show_add_table {
// FIX: Pass &mut event_handler.ideal_cursor_column // NEW: Use unified canvas handler instead of add_table_e::execute_edit_action
add_table_e::execute_edit_action( handle_canvas_state_edit(
action_str,
key, key,
config,
&mut admin_state.add_table_state, &mut admin_state.add_table_state,
&mut event_handler.ideal_cursor_column, &mut event_handler.ideal_cursor_column,
) )
@@ -399,10 +399,10 @@ pub async fn handle_edit_event(
) )
.await? .await?
} else if app_state.ui.show_add_table { } else if app_state.ui.show_add_table {
// FIX: Pass &mut event_handler.ideal_cursor_column // NEW: Use unified canvas handler instead of add_table_e::execute_edit_action
add_table_e::execute_edit_action( handle_canvas_state_edit(
"insert_char",
key, key,
config,
&mut admin_state.add_table_state, &mut admin_state.add_table_state,
&mut event_handler.ideal_cursor_column, &mut event_handler.ideal_cursor_column,
) )

View File

@@ -1,5 +1,5 @@
// src/state/pages/add_table.rs // src/state/pages/add_table.rs
use crate::state::pages::canvas_state::CanvasState; use canvas::canvas::{CanvasState, ActionContext, CanvasAction}; // External library
use ratatui::widgets::TableState; use ratatui::widgets::TableState;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -67,7 +67,6 @@ pub struct AddTableState {
impl Default for AddTableState { impl Default for AddTableState {
fn default() -> Self { fn default() -> Self {
// Initialize with some dummy data for demonstration
AddTableState { AddTableState {
profile_name: "default".to_string(), profile_name: "default".to_string(),
table_name: String::new(), table_name: String::new(),
@@ -92,16 +91,91 @@ impl Default for AddTableState {
impl AddTableState { impl AddTableState {
pub const INPUT_FIELD_COUNT: usize = 3; pub const INPUT_FIELD_COUNT: usize = 3;
/// Helper method to add a column from current inputs
pub fn add_column_from_inputs(&mut self) -> Option<String> {
if self.column_name_input.trim().is_empty() || self.column_type_input.trim().is_empty() {
return Some("Both column name and type are required".to_string());
}
// Check for duplicate column names
if self.columns.iter().any(|col| col.name == self.column_name_input.trim()) {
return Some("Column name already exists".to_string());
}
// Add the column
self.columns.push(ColumnDefinition {
name: self.column_name_input.trim().to_string(),
data_type: self.column_type_input.trim().to_string(),
selected: false,
});
// Clear inputs and reset focus to column name for next entry
self.column_name_input.clear();
self.column_type_input.clear();
self.column_name_cursor_pos = 0;
self.column_type_cursor_pos = 0;
self.current_focus = AddTableFocus::InputColumnName;
self.last_canvas_field = 1;
self.has_unsaved_changes = true;
Some(format!("Column '{}' added successfully", self.columns.last().unwrap().name))
}
/// Helper method to delete selected items
pub fn delete_selected_items(&mut self) -> Option<String> {
let mut deleted_items = Vec::new();
// Remove selected columns
let initial_column_count = self.columns.len();
self.columns.retain(|col| {
if col.selected {
deleted_items.push(format!("column '{}'", col.name));
false
} else {
true
}
});
// Remove selected indexes
let initial_index_count = self.indexes.len();
self.indexes.retain(|idx| {
if idx.selected {
deleted_items.push(format!("index '{}'", idx.name));
false
} else {
true
}
});
// Remove selected links
let initial_link_count = self.links.len();
self.links.retain(|link| {
if link.selected {
deleted_items.push(format!("link to '{}'", link.linked_table_name));
false
} else {
true
}
});
if deleted_items.is_empty() {
Some("No items selected for deletion".to_string())
} else {
self.has_unsaved_changes = true;
Some(format!("Deleted: {}", deleted_items.join(", ")))
}
}
} }
// Implement CanvasState for the input fields // Implement external library's CanvasState for AddTableState
impl CanvasState for AddTableState { impl CanvasState for AddTableState {
fn current_field(&self) -> usize { fn current_field(&self) -> usize {
match self.current_focus { match self.current_focus {
AddTableFocus::InputTableName => 0, AddTableFocus::InputTableName => 0,
AddTableFocus::InputColumnName => 1, AddTableFocus::InputColumnName => 1,
AddTableFocus::InputColumnType => 2, AddTableFocus::InputColumnType => 2,
// If focus is elsewhere, default to the first field for canvas rendering logic // If focus is elsewhere, return the last canvas field used
_ => self.last_canvas_field, _ => self.last_canvas_field,
} }
} }
@@ -115,37 +189,6 @@ impl CanvasState for AddTableState {
} }
} }
fn has_unsaved_changes(&self) -> bool {
self.has_unsaved_changes
}
fn inputs(&self) -> Vec<&String> {
vec![&self.table_name_input, &self.column_name_input, &self.column_type_input]
}
fn get_current_input(&self) -> &str {
match self.current_focus {
AddTableFocus::InputTableName => &self.table_name_input,
AddTableFocus::InputColumnName => &self.column_name_input,
AddTableFocus::InputColumnType => &self.column_type_input,
_ => "", // Should not happen if called correctly
}
}
fn get_current_input_mut(&mut self) -> &mut String {
match self.current_focus {
AddTableFocus::InputTableName => &mut self.table_name_input,
AddTableFocus::InputColumnName => &mut self.column_name_input,
AddTableFocus::InputColumnType => &mut self.column_type_input,
_ => &mut self.table_name_input,
}
}
fn fields(&self) -> Vec<&str> {
// These must match the order used in render_add_table
vec!["Table name", "Name", "Type"]
}
fn set_current_field(&mut self, index: usize) { fn set_current_field(&mut self, index: usize) {
// Update both current focus and last canvas field // Update both current focus and last canvas field
self.current_focus = match index { self.current_focus = match index {
@@ -174,17 +217,84 @@ impl CanvasState for AddTableState {
} }
} }
fn get_current_input(&self) -> &str {
match self.current_focus {
AddTableFocus::InputTableName => &self.table_name_input,
AddTableFocus::InputColumnName => &self.column_name_input,
AddTableFocus::InputColumnType => &self.column_type_input,
_ => "", // Should not happen if called correctly
}
}
fn get_current_input_mut(&mut self) -> &mut String {
match self.current_focus {
AddTableFocus::InputTableName => &mut self.table_name_input,
AddTableFocus::InputColumnName => &mut self.column_name_input,
AddTableFocus::InputColumnType => &mut self.column_type_input,
_ => &mut self.table_name_input, // Fallback
}
}
fn inputs(&self) -> Vec<&String> {
vec![&self.table_name_input, &self.column_name_input, &self.column_type_input]
}
fn fields(&self) -> Vec<&str> {
// These must match the order used in render_add_table
vec!["Table name", "Name", "Type"]
}
fn has_unsaved_changes(&self) -> bool {
self.has_unsaved_changes
}
fn set_has_unsaved_changes(&mut self, changed: bool) { fn set_has_unsaved_changes(&mut self, changed: bool) {
self.has_unsaved_changes = changed; self.has_unsaved_changes = changed;
} }
// --- Autocomplete Support (Not needed for this form yet) --- fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
fn get_suggestions(&self) -> Option<&[String]> { match action {
None // Handle adding column when user presses Enter on the Add button or uses specific action
} CanvasAction::Custom(action_str) if action_str == "add_column" => {
self.add_column_from_inputs()
}
fn get_selected_suggestion_index(&self) -> Option<usize> { // Handle table saving
None CanvasAction::Custom(action_str) if action_str == "save_table" => {
if self.table_name_input.trim().is_empty() {
Some("Table name is required".to_string())
} else if self.columns.is_empty() {
Some("At least one column is required".to_string())
} else {
Some(format!("Saving table: {}", self.table_name_input))
}
}
// Handle deleting selected items
CanvasAction::Custom(action_str) if action_str == "delete_selected" => {
self.delete_selected_items()
}
// Handle canceling (clear form)
CanvasAction::Custom(action_str) if action_str == "cancel" => {
// Reset to defaults but keep profile_name
let profile = self.profile_name.clone();
*self = Self::default();
self.profile_name = profile;
Some("Form cleared".to_string())
}
// Custom validation when moving between fields
CanvasAction::NextField => {
// When leaving table name field, update the table_name for display
if self.current_field() == 0 && !self.table_name_input.trim().is_empty() {
self.table_name = self.table_name_input.trim().to_string();
}
None // Let canvas library handle the normal field movement
}
// Let canvas library handle everything else
_ => None,
}
} }
} }