Files
komp_ac/client/src/pages/admin_panel/add_logic/ui.rs

303 lines
11 KiB
Rust

// src/pages/admin_panel/add_logic/ui.rs
use crate::config::colors::themes::Theme;
use crate::state::app::state::AppState;
use crate::pages::admin_panel::add_logic::state::{AddLogicFocus, AddLogicState, AddLogicFormState};
use canvas::{render_canvas, render_suggestions_dropdown, DefaultCanvasTheme, FormEditor};
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, BorderType, Borders, Paragraph},
Frame,
};
use crate::components::common::autocomplete;
use crate::dialog;
use crate::config::binds::config::EditorKeybindingMode;
pub fn render_add_logic(
f: &mut Frame,
area: Rect,
theme: &Theme,
app_state: &AppState,
add_logic_state: &mut AddLogicFormState,
) {
let main_block = Block::default()
.title(" Add New Logic Script ")
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(theme.border))
.style(Style::default().bg(theme.bg));
let inner_area = main_block.inner(area);
f.render_widget(main_block, area);
// Handle full-screen script editing
if add_logic_state.current_focus() == AddLogicFocus::InsideScriptContent {
let mut editor_ref = add_logic_state
.state
.script_content_editor
.borrow_mut();
let border_style_color = if crate::components::common::text_editor::TextEditor::is_vim_insert_mode(add_logic_state.vim_state()) {
theme.highlight
} else {
theme.secondary
};
let border_style = Style::default().fg(border_style_color);
editor_ref.set_cursor_line_style(Style::default());
editor_ref.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
let script_title_hint = match add_logic_state.editor_keybinding_mode() {
EditorKeybindingMode::Vim => {
let vim_mode_status = crate::components::common::text_editor::TextEditor::get_vim_mode_status(add_logic_state.vim_state());
format!("Script {}", vim_mode_status)
}
EditorKeybindingMode::Emacs | EditorKeybindingMode::Default => {
if crate::components::common::text_editor::TextEditor::is_vim_insert_mode(add_logic_state.vim_state()) {
"Script (Editing)".to_string()
} else {
"Script".to_string()
}
}
};
editor_ref.set_block(
Block::default()
.title(Span::styled(script_title_hint, Style::default().fg(theme.fg)))
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(border_style),
);
f.render_widget(&*editor_ref, inner_area);
// Drop the editor borrow before accessing autocomplete state
drop(editor_ref);
// === SCRIPT EDITOR AUTOCOMPLETE RENDERING ===
if add_logic_state.script_editor_autocomplete_active() && !add_logic_state.script_editor_suggestions().is_empty() {
// Get the current cursor position from textarea
let current_cursor = {
let editor_borrow = add_logic_state.script_content_editor().borrow();
editor_borrow.cursor() // Returns (row, col) as (usize, usize)
};
let (cursor_line, cursor_col) = current_cursor;
// Account for TextArea's block borders (1 for each side)
let block_offset_x = 1;
let block_offset_y = 1;
// Position autocomplete at current cursor position
// Add 1 to column to position dropdown right after the cursor
let autocomplete_x = cursor_col + 1;
let autocomplete_y = cursor_line;
let input_rect = Rect {
x: (inner_area.x + block_offset_x + autocomplete_x as u16).min(inner_area.right().saturating_sub(20)),
y: (inner_area.y + block_offset_y + autocomplete_y as u16).min(inner_area.bottom().saturating_sub(5)),
width: 1, // Minimum width for positioning
height: 1,
};
// Render autocomplete dropdown
autocomplete::render_autocomplete_dropdown(
f,
input_rect,
f.area(), // Full frame area for clamping
theme,
add_logic_state.script_editor_suggestions(),
add_logic_state.script_editor_selected_suggestion_index(),
);
}
return; // Exit early for fullscreen mode
}
// Regular layout with preview
let main_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3), // Top info
Constraint::Length(9), // Canvas for 3 inputs (each 1 line + 1 padding = 2 lines * 3 + 2 border = 8, +1 for good measure)
Constraint::Min(5), // Script preview
Constraint::Length(3), // Buttons
])
.split(inner_area);
let top_info_area = main_chunks[0];
let canvas_area = main_chunks[1];
let script_content_area = main_chunks[2];
let buttons_area = main_chunks[3];
// Top info
let table_label = if let Some(name) = add_logic_state.selected_table_name() {
name.clone()
} else if let Some(id) = add_logic_state.selected_table_id() {
format!("ID {}", id)
} else {
"Global (Not Selected)".to_string()
};
let profile_text = Paragraph::new(vec![
Line::from(Span::styled(
format!("Profile: {}", add_logic_state.profile_name()),
Style::default().fg(theme.fg),
)),
Line::from(Span::styled(
format!("Table: {}", table_label),
Style::default().fg(theme.fg),
)),
])
.block(
Block::default()
.borders(Borders::BOTTOM)
.border_style(Style::default().fg(theme.secondary)),
);
f.render_widget(profile_text, top_info_area);
// Canvas - USING CANVAS LIBRARY
let focus_on_canvas_inputs = matches!(
add_logic_state.current_focus(),
AddLogicFocus::InputLogicName
| AddLogicFocus::InputTargetColumn
| AddLogicFocus::InputDescription
);
let editor = &add_logic_state.editor;
let active_field_rect = render_canvas(f, canvas_area, editor, theme);
// --- Canvas suggestions dropdown (Target Column, etc.) ---
if editor.mode() == canvas::AppMode::Edit {
if let Some(input_rect) = active_field_rect {
render_suggestions_dropdown(
f,
f.area(),
input_rect,
&DefaultCanvasTheme,
editor,
);
}
}
// Script content preview
{
let mut editor_ref = add_logic_state.script_content_editor().borrow_mut();
editor_ref.set_cursor_line_style(Style::default());
let is_script_preview_focused = add_logic_state.current_focus() == AddLogicFocus::ScriptContentPreview;
if is_script_preview_focused {
editor_ref.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
} else {
let underscore_cursor_style = Style::default()
.add_modifier(Modifier::UNDERLINED)
.fg(theme.secondary);
editor_ref.set_cursor_style(underscore_cursor_style);
}
let border_style_color = if is_script_preview_focused {
theme.highlight
} else {
theme.secondary
};
let title_text = "Script Preview"; // Title doesn't need to change based on focus here
let title_style = if is_script_preview_focused {
Style::default().fg(theme.highlight).add_modifier(Modifier::BOLD)
} else {
Style::default().fg(theme.fg)
};
editor_ref.set_block(
Block::default()
.title(Span::styled(title_text, title_style))
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(border_style_color)),
);
f.render_widget(&*editor_ref, script_content_area);
}
// Buttons
let get_button_style = |button_focus: AddLogicFocus, current_focus_state: AddLogicFocus| {
let is_focused = current_focus_state == button_focus;
let base_style = Style::default().fg(if is_focused {
theme.highlight
} else {
theme.secondary
});
if is_focused {
base_style.add_modifier(Modifier::BOLD)
} else {
base_style
}
};
let get_button_border_style = |is_focused: bool, current_theme: &Theme| {
if is_focused {
Style::default().fg(current_theme.highlight)
} else {
Style::default().fg(current_theme.secondary)
}
};
let button_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(50),
Constraint::Percentage(50),
])
.split(buttons_area);
let save_button = Paragraph::new(" Save Logic ")
.style(get_button_style(
AddLogicFocus::SaveButton,
add_logic_state.current_focus(),
))
.alignment(Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(get_button_border_style(
add_logic_state.current_focus() == AddLogicFocus::SaveButton,
theme,
)),
);
f.render_widget(save_button, button_chunks[0]);
let cancel_button = Paragraph::new(" Cancel ")
.style(get_button_style(
AddLogicFocus::CancelButton,
add_logic_state.current_focus(),
))
.alignment(Alignment::Center)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(get_button_border_style(
add_logic_state.current_focus() == AddLogicFocus::CancelButton,
theme,
)),
);
f.render_widget(cancel_button, button_chunks[1]);
// Dialog
if app_state.ui.dialog.dialog_show {
dialog::render_dialog(
f,
f.area(),
theme,
&app_state.ui.dialog.dialog_title,
&app_state.ui.dialog.dialog_message,
&app_state.ui.dialog.dialog_buttons,
app_state.ui.dialog.dialog_active_button_index,
app_state.ui.dialog.is_loading,
);
}
}