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::logging::log;
use std::rc::Rc;
use web_sys::FocusEvent;
#[component]
pub fn EditableCell(
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(optional)] key: Option<String>, // Optional `key` prop
) -> impl IntoView {
let (input_value, set_input_value) = create_signal(value.clone());
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_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
on_cleanup(move || {
@ -26,6 +31,7 @@ pub fn EditableCell(
};
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");
if is_disposed.get_untracked() {
return;
@ -39,6 +45,7 @@ pub fn EditableCell(
if is_disposed.get() {
return;
}
set_is_editing.set(true);
set_has_focus.set(true);
on_focus.call(ev);
};
@ -47,20 +54,37 @@ pub fn EditableCell(
if is_disposed.get() {
return;
}
set_is_editing.set(false);
set_has_focus.set(false);
};
// Use key to force updates only when necessary
let _key = key.unwrap_or_default();
let cell_view = move || {
if is_editing.get() {
view! {
<input
type="text"
value={input_value.get()}
on:input=handle_input
on:focus=handle_focus
on:blur=handle_blur
on:input=handle_input.clone()
on:focus=handle_focus.clone()
on:blur=handle_blur.clone()
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 futures_timer::Delay;
use std::time::Duration;
use std::rc::Rc;
#[derive(Deserialize, Clone, Debug)]
struct WikidataSuggestion {
@ -211,7 +212,7 @@ pub fn ItemsList(
<td>
<EditableCell
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)
key=format!("name-{}", index)
/>
@ -220,7 +221,7 @@ pub fn ItemsList(
<td>
<EditableCell
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)
key=format!("description-{}", index)
/>