fix(editable cells): enhance EditableCell component with editing toggle and Rc for on_input

- Replaced `on_input` function with `Rc<dyn Fn(String)>` to enable cloning and safe usage in closures.
- Introduced `is_editing` signal to manage toggling between display and input modes.
- Added a persistent default key reference to avoid unnecessary updates.
- Improved focus and blur handling for better state management.
- Wrapped input and display logic in a dynamic `cell_view` function for cleaner separation.
This commit is contained in:
ryan 2024-12-31 00:39:31 +03:00
parent b84cd37c44
commit a02fcabe19
2 changed files with 39 additions and 14 deletions

View file

@ -1,17 +1,22 @@
use leptos::*; use leptos::*;
use leptos::logging::log; use leptos::logging::log;
use std::rc::Rc;
use web_sys::FocusEvent; use web_sys::FocusEvent;
#[component] #[component]
pub fn EditableCell( pub fn EditableCell(
value: String, value: String,
on_input: impl Fn(String) + 'static, on_input: Rc<dyn Fn(String)>, // Use `Rc` to allow cloning
#[prop(into)] on_focus: Callback<FocusEvent>, #[prop(into)] on_focus: Callback<FocusEvent>,
#[prop(optional)] key: Option<String>, // Optional `key` prop #[prop(optional)] key: Option<String>, // Optional `key` prop
) -> impl IntoView { ) -> impl IntoView {
let (input_value, set_input_value) = create_signal(value.clone()); let (input_value, set_input_value) = create_signal(value.clone());
let (has_focus, set_has_focus) = create_signal(false); // Track focus state locally let (has_focus, set_has_focus) = create_signal(false); // Track focus state locally
let (is_disposed, set_disposed) = create_signal(false); // Track disposal state let (is_disposed, set_disposed) = create_signal(false); // Track disposal state
let (is_editing, set_is_editing) = create_signal(false);
// persistent default key value
let default_key = String::new();
let key_ref = key.as_ref().unwrap_or(&default_key);
// Ensure signals aren't updated after disposal // Ensure signals aren't updated after disposal
on_cleanup(move || { on_cleanup(move || {
@ -26,6 +31,7 @@ pub fn EditableCell(
}; };
let handle_input = move |e: web_sys::Event| { let handle_input = move |e: web_sys::Event| {
let on_input = Rc::clone(&on_input); // Clone `on_input` to use inside the closure
log_signal_get("input_value"); log_signal_get("input_value");
if is_disposed.get_untracked() { if is_disposed.get_untracked() {
return; return;
@ -39,6 +45,7 @@ pub fn EditableCell(
if is_disposed.get() { if is_disposed.get() {
return; return;
} }
set_is_editing.set(true);
set_has_focus.set(true); set_has_focus.set(true);
on_focus.call(ev); on_focus.call(ev);
}; };
@ -47,20 +54,37 @@ pub fn EditableCell(
if is_disposed.get() { if is_disposed.get() {
return; return;
} }
set_is_editing.set(false);
set_has_focus.set(false); set_has_focus.set(false);
}; };
// Use key to force updates only when necessary let cell_view = move || {
let _key = key.unwrap_or_default(); if is_editing.get() {
view! { view! {
<input <input
type="text" type="text"
value={input_value.get()} value={input_value.get()}
on:input=handle_input on:input=handle_input.clone()
on:focus=handle_focus on:focus=handle_focus.clone()
on:blur=handle_blur on:blur=handle_blur.clone()
class={if has_focus.get() { "focused" } else { "not-focused" }} class={if has_focus.get() { "focused" } else { "not-focused" }}
/> />
}.into_view()
} else {
view! {
<div
tabindex="0"
on:focus=handle_focus.clone()
>
{input_value.get()}
</div>
}.into_view()
}
};
view! {
<div key={key_ref.clone()}>
{cell_view}
</div>
} }
} }

View file

@ -10,6 +10,7 @@ use wasm_bindgen::JsCast;
use web_sys::{FocusEvent, HtmlElement}; use web_sys::{FocusEvent, HtmlElement};
use futures_timer::Delay; use futures_timer::Delay;
use std::time::Duration; use std::time::Duration;
use std::rc::Rc;
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
struct WikidataSuggestion { struct WikidataSuggestion {
@ -211,7 +212,7 @@ pub fn ItemsList(
<td> <td>
<EditableCell <EditableCell
value=item.name.clone() value=item.name.clone()
on_input=move |value| update_item(index, "name", value) on_input=Rc::new(move |value| update_item(index, "name", value))
on_focus=move |event| handle_focus(index, "name", event) on_focus=move |event| handle_focus(index, "name", event)
key=format!("name-{}", index) key=format!("name-{}", index)
/> />
@ -220,7 +221,7 @@ pub fn ItemsList(
<td> <td>
<EditableCell <EditableCell
value=item.description.clone() value=item.description.clone()
on_input=move |value| update_item(index, "description", value) on_input=Rc::new(move |value| update_item(index, "description", value))
on_focus=move |event| handle_focus(index, "description", event) on_focus=move |event| handle_focus(index, "description", event)
key=format!("description-{}", index) key=format!("description-{}", index)
/> />