professional layout working

This commit is contained in:
filipriec
2025-04-17 19:50:48 +02:00
parent 9511970a1a
commit bf55417901

View File

@@ -4,27 +4,28 @@ 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 crate::state::pages::canvas_state::CanvasState;
use crate::state::pages::add_table::{ColumnDefinition, LinkDefinition};
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect}, layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Modifier, Style, Stylize}, style::{Modifier, Style},
text::{Line, Span}, text::{Line, Span},
widgets::{Block, BorderType, Borders, Paragraph, Cell, Row, Table}, widgets::{Block, BorderType, Borders, Cell, Paragraph, Row, Table},
Frame, Frame,
}; };
// Assuming render_canvas exists and works like in register.rs
use crate::components::handlers::canvas::render_canvas; use crate::components::handlers::canvas::render_canvas;
/// Renders the Add New Table page layout. /// Renders the Add New Table page layout, structuring the display of table information,
/// input fields, and action buttons.
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, _app_state: &AppState, // Currently unused, might be needed later
add_table_state: &mut AddTableState, add_table_state: &mut AddTableState,
is_edit_mode: bool, is_edit_mode: bool, // Determines if canvas inputs are in edit mode
highlight_state: &HighlightState, highlight_state: &HighlightState, // For text highlighting in canvas
) { ) {
// Determine if focus is on canvas inputs vs other elements based on AddTableState // Determine if focus is on canvas inputs vs other elements
let focus_on_canvas_inputs = matches!( let focus_on_canvas_inputs = matches!(
add_table_state.current_focus, add_table_state.current_focus,
AddTableFocus::InputTableName AddTableFocus::InputTableName
@@ -32,9 +33,10 @@ pub fn render_add_table(
| AddTableFocus::InputColumnType | AddTableFocus::InputColumnType
); );
// Main block for the whole page // --- Main Page Block ---
let main_block = Block::default() let main_block = Block::default()
.title(" Add New Table ") .title(" Add New Table ")
.title_alignment(Alignment::Center)
.borders(Borders::ALL) .borders(Borders::ALL)
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.border_style(Style::default().fg(theme.border)) .border_style(Style::default().fg(theme.border))
@@ -42,51 +44,82 @@ pub fn render_add_table(
let inner_area = main_block.inner(area); let inner_area = main_block.inner(area);
f.render_widget(main_block, area); f.render_widget(main_block, area);
// Split the inner area horizontally: Left info pane | Right input/action pane // --- Main Vertical Layout ---
let horizontal_chunks = Layout::default() // Splits the page into: Top Info, Middle Area, Bottom Buttons
.direction(Direction::Horizontal) let main_chunks = Layout::default()
.constraints([
Constraint::Percentage(50), // Left Pane
Constraint::Percentage(50), // Right Pane
].as_ref())
.split(inner_area);
let left_pane = horizontal_chunks[0];
let right_pane = horizontal_chunks[1];
// --- Left Pane ---
let left_vertical_chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
Constraint::Length(3), // Profile & Table Name header Constraint::Length(3), // Top: Profile & Committed Table Name
Constraint::Min(5), // Columns section (expandable) Constraint::Min(10), // Middle: Columns | Inputs/Indexes/Links
Constraint::Length(1), // Separator (placeholder for now) Constraint::Length(3), // Bottom: Save/Cancel Buttons
Constraint::Min(3), // Indexes section (expandable) ])
Constraint::Length(1), // Separator (placeholder for now) .split(inner_area);
Constraint::Min(3), // Links section (expandable)
].as_ref())
.split(left_pane);
// --- Left Pane Rendering --- let top_info_area = main_chunks[0];
// Profile & Table Name section (Displays current state) let middle_area = main_chunks[1];
let bottom_buttons_area = main_chunks[2];
// --- Top Info Rendering ---
let profile_text = Paragraph::new(vec![ let profile_text = Paragraph::new(vec![
Line::from(Span::styled( Line::from(Span::styled(
format!("profile: {}", add_table_state.profile_name), // Use actual profile format!("profile: {}", add_table_state.profile_name),
theme.fg, theme.fg,
)), )),
// Display the *committed* table name from the state
Line::from(Span::styled( Line::from(Span::styled(
format!("table name: {}", add_table_state.table_name), // Use actual table name (from input) format!("table name: {}", add_table_state.table_name),
theme.fg, theme.fg,
)) )),
]) ])
.block( .block(
Block::default() Block::default()
.borders(Borders::BOTTOM) .borders(Borders::BOTTOM)
.border_style(Style::default().fg(theme.secondary)), .border_style(Style::default().fg(theme.secondary)),
); );
f.render_widget(profile_text, left_vertical_chunks[0]); f.render_widget(profile_text, top_info_area);
// --- Columns Table --- // --- Middle Area Layout ---
// Splits into: Left (Columns Table) | Right (Inputs & Other Tables)
let middle_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(60), // Left Pane: Columns Table
Constraint::Percentage(40), // Right Pane: Inputs etc.
])
.split(middle_area);
let columns_area = middle_chunks[0];
let right_pane_area = middle_chunks[1];
// --- Right Pane Layout ---
// Splits vertically: Canvas, Add Button, Indexes/Links Area
let right_pane_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(5), // Input Canvas Area
Constraint::Length(3), // Add Button Area
Constraint::Min(5), // Indexes & Links Area
])
.split(right_pane_area);
let canvas_area = right_pane_chunks[0];
let add_button_area = right_pane_chunks[1];
let indexes_links_area = right_pane_chunks[2];
// --- Indexes & Links Layout ---
// Splits horizontally within their allocated area
let indexes_links_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(50), // Indexes Table
Constraint::Percentage(50), // Links Table
])
.split(indexes_links_area);
let indexes_area = indexes_links_chunks[0];
let links_area = indexes_links_chunks[1];
// --- Columns Table Rendering ---
let columns_focused = let columns_focused =
add_table_state.current_focus == AddTableFocus::ColumnsTable; add_table_state.current_focus == AddTableFocus::ColumnsTable;
let columns_border_style = if columns_focused { let columns_border_style = if columns_focused {
@@ -94,8 +127,7 @@ pub fn render_add_table(
} else { } else {
Style::default().fg(theme.secondary) Style::default().fg(theme.secondary)
}; };
// --- Create Table Rows from State --- let column_rows: Vec<Row<'_>> = add_table_state
let column_rows: Vec<Row> = add_table_state
.columns .columns
.iter() .iter()
.map(|col_def| { .map(|col_def| {
@@ -106,114 +138,55 @@ pub fn render_add_table(
.style(Style::default().fg(theme.fg)) .style(Style::default().fg(theme.fg))
}) })
.collect(); .collect();
// --- Define Table Header ---
let header_cells = ["Name", "Type"] let header_cells = ["Name", "Type"]
.iter() .iter()
.map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent)));
let header = Row::new(header_cells).height(1).bottom_margin(1); let header = Row::new(header_cells).height(1).bottom_margin(1);
let columns_table = Table::new(
// --- Create the Table Widget --- column_rows,
let columns_table = Table::new(column_rows, [Constraint::Percentage(50), Constraint::Percentage(50)]) [Constraint::Percentage(60), Constraint::Percentage(40)],
)
.header(header) .header(header)
.block( .block(
Block::default() Block::default()
.title(Span::styled(" Columns ", theme.fg)) .title(Span::styled(" Columns ", theme.fg))
.borders(Borders::TOP) // Separator from Profile/Name .title_alignment(Alignment::Center)
.borders(Borders::TOP | Borders::RIGHT) // Add right border
.border_style(columns_border_style), .border_style(columns_border_style),
) )
.highlight_style(Style::default().add_modifier(Modifier::REVERSED).fg(theme.highlight)) // Style for selected row .row_highlight_style(
.highlight_symbol(" > "); // Symbol for selected row Style::default()
.add_modifier(Modifier::REVERSED)
// --- Render the Table --- .fg(theme.highlight),
)
.highlight_symbol(" > ");
f.render_stateful_widget( f.render_stateful_widget(
columns_table, columns_table,
left_vertical_chunks[1], columns_area,
&mut add_table_state.column_table_state, &mut add_table_state.column_table_state,
); );
// --- Indexes Table --- // --- Canvas Rendering ---
let indexes_focused =
add_table_state.current_focus == AddTableFocus::IndexesTable;
let indexes_border_style = if indexes_focused {
Style::default().fg(theme.highlight)
} else {
Style::default().fg(theme.secondary)
};
// TODO: Replace this Paragraph with a Table/List widget for add_table_state.indexes
let indexes_content = Paragraph::new(vec![
Line::from(Span::styled("Column name", theme.accent)), // Header
Line::from("... Index list placeholder ..."),
])
.block(
Block::default()
.title(Span::styled(" Indexes ", theme.fg))
.borders(Borders::TOP) // Separator from Columns
.border_style(indexes_border_style), // Indicate focus
);
f.render_widget(indexes_content, left_vertical_chunks[3]);
// --- Links Table ---
let links_focused = add_table_state.current_focus == AddTableFocus::LinksTable;
let links_border_style = if links_focused {
Style::default().fg(theme.highlight)
} else {
Style::default().fg(theme.secondary)
};
// TODO: Replace this Paragraph with a Table widget for add_table_state.links
let links_content = Paragraph::new(vec![
Line::from(Span::styled("Linked table Required", theme.accent)), // Header
Line::from("... Link list placeholder ..."),
])
.block(
Block::default()
.title(Span::styled(" Links ", theme.fg))
.borders(Borders::TOP) // Separator from Indexes
.border_style(links_border_style), // Indicate focus
);
f.render_widget(links_content, left_vertical_chunks[5]);
// --- Right Pane ---
let right_vertical_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(5), // Area for render_canvas (3 fields + 2 border)
Constraint::Length(3), // Add Button Area
Constraint::Min(1), // Spacer
Constraint::Length(3), // Save/Cancel buttons area
].as_ref())
.split(right_pane);
let canvas_area = right_vertical_chunks[0];
let add_button_area = right_vertical_chunks[1];
let bottom_buttons_area = right_vertical_chunks[3];
// --- Use render_canvas for Inputs ---
// Pass is_edit_mode determined by the main loop based on AppMode
// Only show edit styling if AppMode is Edit AND focus is on one of the canvas inputs
let _active_field_rect = render_canvas( let _active_field_rect = render_canvas(
f, f,
canvas_area, canvas_area,
add_table_state, // Implements CanvasState add_table_state, // Implements CanvasState
&add_table_state.fields(), // Get field names from state &add_table_state.fields(),
&add_table_state.current_field(), &add_table_state.current_field(),
&add_table_state.inputs(), // Get inputs from state &add_table_state.inputs(),
theme, theme,
is_edit_mode && focus_on_canvas_inputs, // Only truly edit mode if focus is on canvas is_edit_mode && focus_on_canvas_inputs,
highlight_state, highlight_state,
); );
// --- Buttons --- // --- Button Style Helpers ---
// Helper to get style based on focus let get_button_style = |button_focus: AddTableFocus, current_focus| {
let get_button_style = |button_focus: AddTableFocus| { let is_focused = current_focus == button_focus;
let is_focused = add_table_state.current_focus == button_focus;
// Base style: secondary color, unless focused then highlight color
let mut style = Style::default().fg(if is_focused { let mut style = Style::default().fg(if is_focused {
theme.highlight theme.highlight
} else { } else {
theme.secondary theme.secondary
}); });
// Apply bold and reverse if focused
if is_focused { if is_focused {
style = style style = style
.add_modifier(Modifier::BOLD) .add_modifier(Modifier::BOLD)
@@ -221,18 +194,20 @@ pub fn render_add_table(
} }
style style
}; };
// Helper to get border style based on focus let get_button_border_style = |button_focus: AddTableFocus, current_focus| {
let get_button_border_style = |button_focus: AddTableFocus| { if current_focus == button_focus {
if add_table_state.current_focus == button_focus { Style::default().fg(theme.highlight)
Style::default().fg(theme.highlight) // Highlight border when focused
} else { } else {
Style::default().fg(theme.secondary) // Dim border otherwise Style::default().fg(theme.secondary)
} }
}; };
// Add Button // --- Add Button Rendering ---
let add_button = Paragraph::new(" Add ") let add_button = Paragraph::new(" Add ")
.style(get_button_style(AddTableFocus::AddColumnButton)) .style(get_button_style(
AddTableFocus::AddColumnButton,
add_table_state.current_focus,
))
.alignment(Alignment::Center) .alignment(Alignment::Center)
.block( .block(
Block::default() Block::default()
@@ -240,34 +215,129 @@ pub fn render_add_table(
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.border_style(get_button_border_style( .border_style(get_button_border_style(
AddTableFocus::AddColumnButton, AddTableFocus::AddColumnButton,
add_table_state.current_focus,
)), )),
); );
f.render_widget(add_button, add_button_area); f.render_widget(add_button, add_button_area);
// Bottom Buttons Area (Save, Cancel) // --- Indexes Table Rendering ---
let indexes_focused =
add_table_state.current_focus == AddTableFocus::IndexesTable;
let indexes_border_style = if indexes_focused {
Style::default().fg(theme.highlight)
} else {
Style::default().fg(theme.secondary)
};
let index_rows: Vec<Row<'_>> = add_table_state
.indexes
.iter()
.map(|index_name| {
Row::new(vec![Cell::from(index_name.clone())])
.style(Style::default().fg(theme.fg))
})
.collect();
let index_header_cells = ["Column Name"]
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(theme.accent)));
let index_header = Row::new(index_header_cells).height(1).bottom_margin(1);
let indexes_table =
Table::new(index_rows, [Constraint::Percentage(100)])
.header(index_header)
.block(
Block::default()
.title(Span::styled(" Indexes ", theme.fg))
.title_alignment(Alignment::Center)
.borders(Borders::TOP | Borders::RIGHT) // Add right border
.border_style(indexes_border_style),
)
.row_highlight_style(
Style::default()
.add_modifier(Modifier::REVERSED)
.fg(theme.highlight),
)
.highlight_symbol(" > ");
f.render_stateful_widget(
indexes_table,
indexes_area,
&mut add_table_state.index_table_state,
);
// --- Links Table Rendering ---
let links_focused = add_table_state.current_focus == AddTableFocus::LinksTable;
let links_border_style = if links_focused {
Style::default().fg(theme.highlight)
} else {
Style::default().fg(theme.secondary)
};
let link_rows: Vec<Row<'_>> = add_table_state
.links
.iter()
.map(|link_def| {
Row::new(vec![
Cell::from(link_def.linked_table_name.clone()),
// Display checkbox style for boolean
Cell::from(if link_def.is_required { "[X]" } else { "[ ]" }),
])
.style(Style::default().fg(theme.fg))
})
.collect();
let link_header_cells = ["Linked Table", "Req"] // Shortened header
.iter()
.map(|h| Cell::from(*h).style(Style::default().fg(theme.accent)));
let link_header = Row::new(link_header_cells).height(1).bottom_margin(1);
let links_table =
Table::new(link_rows, [Constraint::Percentage(80), Constraint::Min(5)]) // Adjust constraints
.header(link_header)
.block(
Block::default()
.title(Span::styled(" Links ", theme.fg))
.title_alignment(Alignment::Center)
.borders(Borders::TOP) // No left border needed here
.border_style(links_border_style),
)
.row_highlight_style(
Style::default()
.add_modifier(Modifier::REVERSED)
.fg(theme.highlight),
)
.highlight_symbol(" > ");
f.render_stateful_widget(
links_table,
links_area,
&mut add_table_state.link_table_state,
);
// --- Save/Cancel Buttons Rendering ---
let bottom_button_chunks = Layout::default() let bottom_button_chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([ .constraints([
Constraint::Percentage(50), // Save Button Constraint::Percentage(50), // Save Button
Constraint::Percentage(50), // Cancel Button Constraint::Percentage(50), // Cancel Button
].as_ref()) ])
.split(bottom_buttons_area); .split(bottom_buttons_area);
// Save Button
let save_button = Paragraph::new(" Save table ") let save_button = Paragraph::new(" Save table ")
.style(get_button_style(AddTableFocus::SaveButton)) .style(get_button_style(
AddTableFocus::SaveButton,
add_table_state.current_focus,
))
.alignment(Alignment::Center) .alignment(Alignment::Center)
.block( .block(
Block::default() Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.border_style(get_button_border_style(AddTableFocus::SaveButton)), .border_style(get_button_border_style(
AddTableFocus::SaveButton,
add_table_state.current_focus,
)),
); );
f.render_widget(save_button, bottom_button_chunks[0]); f.render_widget(save_button, bottom_button_chunks[0]);
// Cancel Button
let cancel_button = Paragraph::new(" Cancel ") let cancel_button = Paragraph::new(" Cancel ")
.style(get_button_style(AddTableFocus::CancelButton)) .style(get_button_style(
AddTableFocus::CancelButton,
add_table_state.current_focus,
))
.alignment(Alignment::Center) .alignment(Alignment::Center)
.block( .block(
Block::default() Block::default()
@@ -275,6 +345,7 @@ pub fn render_add_table(
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.border_style(get_button_border_style( .border_style(get_button_border_style(
AddTableFocus::CancelButton, AddTableFocus::CancelButton,
add_table_state.current_focus,
)), )),
); );
f.render_widget(cancel_button, bottom_button_chunks[1]); f.render_widget(cancel_button, bottom_button_chunks[1]);