Fix (ItemsList)(inprogress): Improve Popup Positioning and Debounced Fetching

- Introduced `ActiveCell` struct to manage active cell state and position more robustly.
- Added debounce mechanism using `futures_timer::Delay` for optimized Wikidata suggestion fetching.
- Enhanced popup rendering logic to include z-index and improved styling for better positioning.
- Implemented validation for bounding box dimensions during active cell positioning.
- Adjusted event handling for focus and blur with asynchronous state updates.
- General refactoring for better readability and maintainability.

(Note: Popup functionality remains incomplete and will be addressed in future iterations.)
This commit is contained in:
ryan 2024-12-30 14:18:33 +03:00
parent 8cd277d66a
commit c4a45d9185

View file

@ -8,6 +8,8 @@ use crate::models::item::Item;
use std::sync::{Arc, Mutex};
use wasm_bindgen::JsCast;
use web_sys::{FocusEvent, HtmlElement, Element, Node};
use futures_timer::Delay;
use std::time::Duration;
#[derive(Deserialize, Clone, Debug)]
struct WikidataSuggestion {
@ -15,6 +17,11 @@ struct WikidataSuggestion {
label: String,
description: Option<String>,
}
#[derive(Clone)]
struct ActiveCell {
row_index: usize,
position: (f64, f64),
}
#[component]
pub fn ItemsList(
@ -32,14 +39,16 @@ pub fn ItemsList(
wikidata_id: None,
}]);
let (active_cell, set_active_cell) = create_signal(None::<ActiveCell>);
let (active_cell_position, set_active_cell_position) = create_signal(None::<(f64, f64)>);
let (active_row_index, set_active_row_index) = create_signal(None::<usize>);
let (wikidata_suggestions, set_wikidata_suggestions) =
create_signal(Vec::<WikidataSuggestion>::new());
let (wikidata_suggestions, set_wikidata_suggestions) = create_signal(Vec::<WikidataSuggestion>::new());
let debounce_duration = Duration::from_millis(300);
// Fetch Wikidata suggestions
let fetch_wikidata_suggestions = move |query: String| {
spawn_local(async move {
Delay::new(debounce_duration).await;
if query.is_empty() {
set_wikidata_suggestions.set(Vec::new());
return;
@ -56,7 +65,10 @@ pub fn ItemsList(
set_wikidata_suggestions.set(data.search);
}
}
Err(_) => log!("Failed to fetch Wikidata suggestions"),
Err(err) => {
log!("Failed to fetch Wikidata suggestions: {:?}", err);
set_wikidata_suggestions.set(Vec::new());
}
}
});
};
@ -70,10 +82,15 @@ pub fn ItemsList(
item.name = value.clone();
fetch_wikidata_suggestions(value.clone());
set_active_row_index.set(Some(index));
// Set active cell position
// Set active cell position with validation
if let Some(element) = document().get_element_by_id(&format!("name-{}", index)) {
let rect = element.get_bounding_client_rect();
log!("Bounding rect: {:?}", rect); // Log rect details
if rect.width() > 0.0 && rect.height() > 0.0 {
set_active_cell_position.set(Some((rect.left(), rect.bottom())));
} else {
log!("Element bounding box is not valid for popup positioning.");
}
}
}
"description" => {
@ -129,10 +146,15 @@ pub fn ItemsList(
class="suggestions-popup"
style=move || {
let suggestions = wikidata_suggestions.get();
let position = active_cell_position.get();
if !suggestions.is_empty() && position.is_some() {
let (x, y) = position.unwrap();
format!("position: absolute; left: {}px; top: {}px; display: block;", x, y)
if !suggestions.is_empty() {
if let Some((x, y)) = active_cell_position.get() {
format!(
"position: absolute; left: {}px; top: {}px; display: block; z-index: 1000;",
x, y
)
} else {
"display: none;".to_string()
}
} else {
"display: none;".to_string()
}
@ -190,26 +212,20 @@ pub fn ItemsList(
<td>
<div
on:focus=move |event: FocusEvent| {
if let Some(target) = event.target() {
// Try casting the target to Node first, then to HtmlElement
if let Some(node) = target.dyn_ref::<Node>() {
if let Some(element_ref) = node.dyn_ref::<HtmlElement>(){
if let Some(element) = element_ref.dyn_ref::<Element>() {
if let Some(element) = event.target().and_then(|t| t.dyn_into::<HtmlElement>().ok()) {
let rect = element.get_bounding_client_rect();
set_active_cell_position.set(Some((rect.left(), rect.top() + rect.height())));
set_active_row_index.set(Some(index));
} else {
log!("Failed to cast to Element");
}
} else {
log!("Target is not a valid HTML element");
}
} else {
log!("Target is not a valid Node");
set_active_cell.set(Some(ActiveCell {
row_index: index,
position: (rect.left(), rect.top() + rect.height()),
}));
}
}
on:blur=move |_| {
spawn_local(async move {
Delay::new(Duration::from_millis(100)).await;
set_active_cell.set(None);
});
}
on:blur=move |_| set_active_row_index.set(None)
>
<EditableCell
value=item.name.clone()