migrating to the new canvas library
This commit is contained in:
@@ -8,7 +8,7 @@ license.workspace = true
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
canvas = { path = "../canvas", features = ["gui"] }
|
canvas = { path = "../canvas", features = ["gui", "suggestions"] }
|
||||||
|
|
||||||
ratatui = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// src/state/pages/auth.rs
|
// src/state/pages/auth.rs
|
||||||
use canvas::canvas::{CanvasState, ActionContext, CanvasAction, AppMode};
|
use canvas::{DataProvider, AppMode, SuggestionItem};
|
||||||
use canvas::autocomplete::{AutocompleteCanvasState, AutocompleteState, SuggestionItem};
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -60,8 +59,10 @@ pub struct RegisterState {
|
|||||||
pub current_field: usize,
|
pub current_field: usize,
|
||||||
pub current_cursor_pos: usize,
|
pub current_cursor_pos: usize,
|
||||||
pub has_unsaved_changes: bool,
|
pub has_unsaved_changes: bool,
|
||||||
pub autocomplete: AutocompleteState<String>,
|
|
||||||
pub app_mode: AppMode,
|
pub app_mode: AppMode,
|
||||||
|
// Keep role suggestions for later integration
|
||||||
|
pub role_suggestions: Vec<String>,
|
||||||
|
pub role_suggestions_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RegisterState {
|
impl Default for RegisterState {
|
||||||
@@ -76,8 +77,9 @@ impl Default for RegisterState {
|
|||||||
current_field: 0,
|
current_field: 0,
|
||||||
current_cursor_pos: 0,
|
current_cursor_pos: 0,
|
||||||
has_unsaved_changes: false,
|
has_unsaved_changes: false,
|
||||||
autocomplete: AutocompleteState::new(),
|
|
||||||
app_mode: AppMode::Edit,
|
app_mode: AppMode::Edit,
|
||||||
|
role_suggestions: AVAILABLE_ROLES.clone(),
|
||||||
|
role_suggestions_active: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,51 +97,27 @@ impl LoginState {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl RegisterState {
|
// Legacy method compatibility
|
||||||
pub fn new() -> Self {
|
pub fn current_field(&self) -> usize {
|
||||||
let mut state = Self {
|
|
||||||
autocomplete: AutocompleteState::new(),
|
|
||||||
app_mode: AppMode::Edit,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize autocomplete with role suggestions
|
|
||||||
let suggestions: Vec<SuggestionItem<String>> = AVAILABLE_ROLES
|
|
||||||
.iter()
|
|
||||||
.map(|role| SuggestionItem::simple(role.clone(), role.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Set suggestions but keep inactive initially
|
|
||||||
state.autocomplete.set_suggestions(suggestions);
|
|
||||||
state.autocomplete.is_active = false; // Not active by default
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement external library's CanvasState for LoginState
|
|
||||||
impl CanvasState for LoginState {
|
|
||||||
fn current_field(&self) -> usize {
|
|
||||||
self.current_field
|
self.current_field
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_cursor_pos(&self) -> usize {
|
pub fn current_cursor_pos(&self) -> usize {
|
||||||
self.current_cursor_pos
|
self.current_cursor_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_field(&mut self, index: usize) {
|
pub fn set_current_field(&mut self, index: usize) {
|
||||||
if index < 2 {
|
if index < 2 {
|
||||||
self.current_field = index;
|
self.current_field = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_cursor_pos(&mut self, pos: usize) {
|
pub fn set_current_cursor_pos(&mut self, pos: usize) {
|
||||||
self.current_cursor_pos = pos;
|
self.current_cursor_pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_input(&self) -> &str {
|
pub fn get_current_input(&self) -> &str {
|
||||||
match self.current_field {
|
match self.current_field {
|
||||||
0 => &self.username,
|
0 => &self.username,
|
||||||
1 => &self.password,
|
1 => &self.password,
|
||||||
@@ -147,7 +125,7 @@ impl CanvasState for LoginState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_input_mut(&mut self) -> &mut String {
|
pub fn get_current_input_mut(&mut self) -> &mut String {
|
||||||
match self.current_field {
|
match self.current_field {
|
||||||
0 => &mut self.username,
|
0 => &mut self.username,
|
||||||
1 => &mut self.password,
|
1 => &mut self.password,
|
||||||
@@ -155,68 +133,57 @@ impl CanvasState for LoginState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inputs(&self) -> Vec<&String> {
|
pub fn current_mode(&self) -> AppMode {
|
||||||
vec![&self.username, &self.password]
|
self.app_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fields(&self) -> Vec<&str> {
|
// Add missing methods that used to come from CanvasState trait
|
||||||
vec!["Username/Email", "Password"]
|
pub fn has_unsaved_changes(&self) -> bool {
|
||||||
}
|
|
||||||
|
|
||||||
fn has_unsaved_changes(&self) -> bool {
|
|
||||||
self.has_unsaved_changes
|
self.has_unsaved_changes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_has_unsaved_changes(&mut self, changed: bool) {
|
pub fn set_has_unsaved_changes(&mut self, changed: bool) {
|
||||||
self.has_unsaved_changes = changed;
|
self.has_unsaved_changes = changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
|
||||||
match action {
|
|
||||||
CanvasAction::Custom(action_str) if action_str == "submit" => {
|
|
||||||
if !self.username.is_empty() && !self.password.is_empty() {
|
|
||||||
Some(format!("Submitting login for: {}", self.username))
|
|
||||||
} else {
|
|
||||||
Some("Please fill in all required fields".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_mode(&self) -> AppMode {
|
|
||||||
self.app_mode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement external library's CanvasState for RegisterState
|
impl RegisterState {
|
||||||
impl CanvasState for RegisterState {
|
pub fn new() -> Self {
|
||||||
fn current_field(&self) -> usize {
|
Self {
|
||||||
|
app_mode: AppMode::Edit,
|
||||||
|
role_suggestions: AVAILABLE_ROLES.clone(),
|
||||||
|
role_suggestions_active: false,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy method compatibility
|
||||||
|
pub fn current_field(&self) -> usize {
|
||||||
self.current_field
|
self.current_field
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_cursor_pos(&self) -> usize {
|
pub fn current_cursor_pos(&self) -> usize {
|
||||||
self.current_cursor_pos
|
self.current_cursor_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_field(&mut self, index: usize) {
|
pub fn set_current_field(&mut self, index: usize) {
|
||||||
if index < 5 {
|
if index < 5 {
|
||||||
self.current_field = index;
|
self.current_field = index;
|
||||||
|
|
||||||
// Auto-activate autocomplete when moving to role field (index 4)
|
// Auto-activate role suggestions when moving to role field (index 4)
|
||||||
if index == 4 && !self.autocomplete.is_active {
|
if index == 4 {
|
||||||
self.activate_autocomplete();
|
self.activate_role_suggestions();
|
||||||
} else if index != 4 && self.autocomplete.is_active {
|
} else {
|
||||||
self.deactivate_autocomplete();
|
self.deactivate_role_suggestions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_cursor_pos(&mut self, pos: usize) {
|
pub fn set_current_cursor_pos(&mut self, pos: usize) {
|
||||||
self.current_cursor_pos = pos;
|
self.current_cursor_pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_input(&self) -> &str {
|
pub fn get_current_input(&self) -> &str {
|
||||||
match self.current_field {
|
match self.current_field {
|
||||||
0 => &self.username,
|
0 => &self.username,
|
||||||
1 => &self.email,
|
1 => &self.email,
|
||||||
@@ -227,7 +194,7 @@ impl CanvasState for RegisterState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_input_mut(&mut self) -> &mut String {
|
pub fn get_current_input_mut(&mut self) -> &mut String {
|
||||||
match self.current_field {
|
match self.current_field {
|
||||||
0 => &mut self.username,
|
0 => &mut self.username,
|
||||||
1 => &mut self.email,
|
1 => &mut self.email,
|
||||||
@@ -238,123 +205,121 @@ impl CanvasState for RegisterState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inputs(&self) -> Vec<&String> {
|
pub fn current_mode(&self) -> AppMode {
|
||||||
vec![
|
self.app_mode
|
||||||
&self.username,
|
|
||||||
&self.email,
|
|
||||||
&self.password,
|
|
||||||
&self.password_confirmation,
|
|
||||||
&self.role,
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fields(&self) -> Vec<&str> {
|
// Role suggestions management
|
||||||
vec![
|
pub fn activate_role_suggestions(&mut self) {
|
||||||
"Username",
|
self.role_suggestions_active = true;
|
||||||
"Email (Optional)",
|
// Filter suggestions based on current input
|
||||||
"Password (Optional)",
|
let current_input = self.role.to_lowercase();
|
||||||
"Confirm Password",
|
self.role_suggestions = AVAILABLE_ROLES
|
||||||
"Role (Optional)"
|
.iter()
|
||||||
]
|
.filter(|role| role.to_lowercase().contains(¤t_input))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_unsaved_changes(&self) -> bool {
|
pub fn deactivate_role_suggestions(&mut self) {
|
||||||
|
self.role_suggestions_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_role_suggestions_active(&self) -> bool {
|
||||||
|
self.role_suggestions_active
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_role_suggestions(&self) -> &[String] {
|
||||||
|
&self.role_suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add missing methods that used to come from CanvasState trait
|
||||||
|
pub fn has_unsaved_changes(&self) -> bool {
|
||||||
self.has_unsaved_changes
|
self.has_unsaved_changes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_has_unsaved_changes(&mut self, changed: bool) {
|
pub fn set_has_unsaved_changes(&mut self, changed: bool) {
|
||||||
self.has_unsaved_changes = changed;
|
self.has_unsaved_changes = changed;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
// Step 2: Implement DataProvider for LoginState
|
||||||
match action {
|
impl DataProvider for LoginState {
|
||||||
CanvasAction::Custom(action_str) if action_str == "submit" => {
|
fn field_count(&self) -> usize {
|
||||||
if !self.username.is_empty() {
|
2
|
||||||
Some(format!("Submitting registration for: {}", self.username))
|
}
|
||||||
} else {
|
|
||||||
Some("Username is required".to_string())
|
fn field_name(&self, index: usize) -> &str {
|
||||||
}
|
match index {
|
||||||
}
|
0 => "Username/Email",
|
||||||
_ => None,
|
1 => "Password",
|
||||||
|
_ => "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_mode(&self) -> AppMode {
|
fn field_value(&self, index: usize) -> &str {
|
||||||
self.app_mode
|
match index {
|
||||||
|
0 => &self.username,
|
||||||
|
1 => &self.password,
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_field_value(&mut self, index: usize, value: String) {
|
||||||
|
match index {
|
||||||
|
0 => self.username = value,
|
||||||
|
1 => self.password = value,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
self.has_unsaved_changes = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supports_suggestions(&self, _field_index: usize) -> bool {
|
||||||
|
false // Login form doesn't support suggestions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add autocomplete support for RegisterState
|
// Step 3: Implement DataProvider for RegisterState
|
||||||
impl AutocompleteCanvasState for RegisterState {
|
impl DataProvider for RegisterState {
|
||||||
type SuggestionData = String;
|
fn field_count(&self) -> usize {
|
||||||
|
5
|
||||||
fn supports_autocomplete(&self, field_index: usize) -> bool {
|
|
||||||
field_index == 4 // Only role field supports autocomplete
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn autocomplete_state(&self) -> Option<&AutocompleteState<Self::SuggestionData>> {
|
fn field_name(&self, index: usize) -> &str {
|
||||||
Some(&self.autocomplete)
|
match index {
|
||||||
}
|
0 => "Username",
|
||||||
|
1 => "Email (Optional)",
|
||||||
fn autocomplete_state_mut(&mut self) -> Option<&mut AutocompleteState<Self::SuggestionData>> {
|
2 => "Password (Optional)",
|
||||||
Some(&mut self.autocomplete)
|
3 => "Confirm Password",
|
||||||
}
|
4 => "Role (Optional)",
|
||||||
|
_ => "",
|
||||||
fn activate_autocomplete(&mut self) {
|
|
||||||
let current_field = self.current_field();
|
|
||||||
if self.supports_autocomplete(current_field) {
|
|
||||||
self.autocomplete.activate(current_field);
|
|
||||||
|
|
||||||
// Re-filter suggestions based on current input
|
|
||||||
let current_input = self.role.to_lowercase();
|
|
||||||
let filtered_suggestions: Vec<SuggestionItem<String>> = AVAILABLE_ROLES
|
|
||||||
.iter()
|
|
||||||
.filter(|role| role.to_lowercase().contains(¤t_input))
|
|
||||||
.map(|role| SuggestionItem::simple(role.clone(), role.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.autocomplete.set_suggestions(filtered_suggestions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deactivate_autocomplete(&mut self) {
|
fn field_value(&self, index: usize) -> &str {
|
||||||
self.autocomplete.deactivate();
|
match index {
|
||||||
}
|
0 => &self.username,
|
||||||
|
1 => &self.email,
|
||||||
fn is_autocomplete_active(&self) -> bool {
|
2 => &self.password,
|
||||||
self.autocomplete.is_active
|
3 => &self.password_confirmation,
|
||||||
}
|
4 => &self.role,
|
||||||
|
_ => "",
|
||||||
fn is_autocomplete_ready(&self) -> bool {
|
|
||||||
self.autocomplete.is_ready()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_autocomplete_selection(&mut self) -> Option<String> {
|
|
||||||
// First, get the data we need and clone it to avoid borrowing conflicts
|
|
||||||
let selection_info = self.autocomplete.get_selected().map(|selected| {
|
|
||||||
(selected.value_to_store.clone(), selected.display_text.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now do the mutable operations
|
|
||||||
if let Some((value, display_text)) = selection_info {
|
|
||||||
self.role = value;
|
|
||||||
self.set_has_unsaved_changes(true);
|
|
||||||
self.deactivate_autocomplete();
|
|
||||||
Some(format!("Selected role: {}", display_text))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_autocomplete_suggestions(&mut self, suggestions: Vec<SuggestionItem<Self::SuggestionData>>) {
|
fn set_field_value(&mut self, index: usize, value: String) {
|
||||||
if let Some(state) = self.autocomplete_state_mut() {
|
match index {
|
||||||
state.set_suggestions(suggestions);
|
0 => self.username = value,
|
||||||
|
1 => self.email = value,
|
||||||
|
2 => self.password = value,
|
||||||
|
3 => self.password_confirmation = value,
|
||||||
|
4 => self.role = value,
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
self.has_unsaved_changes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_autocomplete_loading(&mut self, loading: bool) {
|
fn supports_suggestions(&self, field_index: usize) -> bool {
|
||||||
if let Some(state) = self.autocomplete_state_mut() {
|
field_index == 4 // only Role field supports suggestions
|
||||||
state.is_loading = loading;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// src/state/pages/form.rs
|
// src/state/pages/form.rs
|
||||||
|
|
||||||
use crate::config::colors::themes::Theme;
|
use crate::config::colors::themes::Theme;
|
||||||
use canvas::canvas::{CanvasState, CanvasAction, ActionContext, HighlightState, AppMode};
|
use canvas::{DataProvider, AppMode, EditorState, FormEditor};
|
||||||
|
use canvas::canvas::HighlightState;
|
||||||
use common::proto::komp_ac::search::search_response::Hit;
|
use common::proto::komp_ac::search::search_response::Hit;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
@@ -122,26 +123,19 @@ impl FormState {
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
is_edit_mode: bool,
|
is_edit_mode: bool,
|
||||||
highlight_state: &HighlightState, // Now using canvas::HighlightState
|
highlight_state: &HighlightState,
|
||||||
) {
|
) {
|
||||||
let fields_str_slice: Vec<&str> =
|
// Wrap in FormEditor for new API
|
||||||
self.fields().iter().map(|s| *s).collect();
|
let mut editor = FormEditor::new(self.clone());
|
||||||
let values_str_slice: Vec<&String> = self.values.iter().collect();
|
|
||||||
|
// Use new canvas rendering
|
||||||
crate::components::form::form::render_form(
|
canvas::render_canvas_default(f, area, &editor);
|
||||||
f,
|
|
||||||
area,
|
// If autocomplete is active, render suggestions
|
||||||
self,
|
if self.autocomplete_active && !self.autocomplete_suggestions.is_empty() {
|
||||||
&fields_str_slice,
|
// Note: This will need to be updated when suggestions are integrated
|
||||||
&self.current_field,
|
// canvas::render_suggestions_dropdown(f, area, input_rect, &canvas::DefaultCanvasTheme, &editor);
|
||||||
&values_str_slice,
|
}
|
||||||
&self.table_name,
|
|
||||||
theme,
|
|
||||||
is_edit_mode,
|
|
||||||
highlight_state,
|
|
||||||
self.total_count,
|
|
||||||
self.current_position,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_to_empty(&mut self) {
|
pub fn reset_to_empty(&mut self) {
|
||||||
@@ -242,97 +236,84 @@ impl FormState {
|
|||||||
pub fn set_readonly_mode(&mut self) {
|
pub fn set_readonly_mode(&mut self) {
|
||||||
self.app_mode = AppMode::ReadOnly;
|
self.app_mode = AppMode::ReadOnly;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CanvasState for FormState {
|
// Legacy method compatibility
|
||||||
fn current_field(&self) -> usize {
|
pub fn fields(&self) -> Vec<&str> {
|
||||||
self.current_field
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_cursor_pos(&self) -> usize {
|
|
||||||
self.current_cursor_pos
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_unsaved_changes(&self) -> bool {
|
|
||||||
self.has_unsaved_changes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(&self) -> Vec<&String> {
|
|
||||||
self.values.iter().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_input(&self) -> &str {
|
|
||||||
FormState::get_current_input(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_input_mut(&mut self) -> &mut String {
|
|
||||||
FormState::get_current_input_mut(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fields(&self) -> Vec<&str> {
|
|
||||||
self.fields
|
self.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| f.display_name.as_str())
|
.map(|f| f.display_name.as_str())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_field(&mut self, index: usize) {
|
pub fn get_display_value_for_field(&self, index: usize) -> &str {
|
||||||
|
if let Some(display_text) = self.link_display_map.get(&index) {
|
||||||
|
return display_text.as_str();
|
||||||
|
}
|
||||||
|
self.values
|
||||||
|
.get(index)
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_display_override(&self, index: usize) -> bool {
|
||||||
|
self.link_display_map.contains_key(&index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_mode(&self) -> AppMode {
|
||||||
|
self.app_mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add missing methods that used to come from CanvasState trait
|
||||||
|
pub fn has_unsaved_changes(&self) -> bool {
|
||||||
|
self.has_unsaved_changes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_has_unsaved_changes(&mut self, changed: bool) {
|
||||||
|
self.has_unsaved_changes = changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_field(&self) -> usize {
|
||||||
|
self.current_field
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_current_field(&mut self, index: usize) {
|
||||||
if index < self.fields.len() {
|
if index < self.fields.len() {
|
||||||
self.current_field = index;
|
self.current_field = index;
|
||||||
}
|
}
|
||||||
self.deactivate_autocomplete();
|
self.deactivate_autocomplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_cursor_pos(&mut self, pos: usize) {
|
pub fn current_cursor_pos(&self) -> usize {
|
||||||
|
self.current_cursor_pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_current_cursor_pos(&mut self, pos: usize) {
|
||||||
self.current_cursor_pos = pos;
|
self.current_cursor_pos = pos;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn set_has_unsaved_changes(&mut self, changed: bool) {
|
// Step 2: Implement DataProvider for FormState
|
||||||
self.has_unsaved_changes = changed;
|
impl DataProvider for FormState {
|
||||||
|
fn field_count(&self) -> usize {
|
||||||
|
self.fields.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- FEATURE-SPECIFIC ACTION HANDLING ---
|
fn field_name(&self, index: usize) -> &str {
|
||||||
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
&self.fields[index].display_name
|
||||||
match action {
|
}
|
||||||
CanvasAction::SelectSuggestion => {
|
|
||||||
if let Some(selected_idx) = self.selected_suggestion_index {
|
fn field_value(&self, index: usize) -> &str {
|
||||||
if let Some(hit) = self.autocomplete_suggestions.get(selected_idx).cloned() {
|
&self.values[index]
|
||||||
// Extract the value from the selected suggestion
|
}
|
||||||
if let Ok(content_map) = serde_json::from_str::<HashMap<String, serde_json::Value>>(&hit.content_json) {
|
|
||||||
let current_field_def = &self.fields[self.current_field];
|
fn set_field_value(&mut self, index: usize, value: String) {
|
||||||
if let Some(value) = content_map.get(¤t_field_def.data_key) {
|
if let Some(v) = self.values.get_mut(index) {
|
||||||
let new_value = json_value_to_string(value);
|
*v = value;
|
||||||
let display_name = self.get_display_name_for_hit(&hit);
|
self.has_unsaved_changes = true;
|
||||||
*self.get_current_input_mut() = new_value.clone();
|
|
||||||
self.set_current_cursor_pos(new_value.len());
|
|
||||||
self.set_has_unsaved_changes(true);
|
|
||||||
self.deactivate_autocomplete();
|
|
||||||
return Some(format!("Selected: {}", display_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None, // Let canvas handle other actions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_display_value_for_field(&self, index: usize) -> &str {
|
fn supports_suggestions(&self, field_index: usize) -> bool {
|
||||||
if let Some(display_text) = self.link_display_map.get(&index) {
|
self.fields.get(field_index).map(|f| f.is_link).unwrap_or(false)
|
||||||
return display_text.as_str();
|
|
||||||
}
|
|
||||||
self.inputs()
|
|
||||||
.get(index)
|
|
||||||
.map(|s| s.as_str())
|
|
||||||
.unwrap_or("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_display_override(&self, index: usize) -> bool {
|
|
||||||
self.link_display_map.contains_key(&index)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_mode(&self) -> AppMode {
|
|
||||||
self.app_mode
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user