From 8cd277d66abb1a111e9dd05104748f74aca33f1a Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 27 Dec 2024 03:01:03 +0300 Subject: [PATCH] feat(item_list): Added active cell tracking with position updates for popup rendering. --- Cargo.lock | 1 + Cargo.toml | 3 +- assets/style.css | 24 +++++++ src/components/items_list.rs | 126 ++++++++++++++++++++++++----------- 4 files changed, 115 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ef3255..0a0052c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -724,6 +724,7 @@ dependencies = [ "futures", "gloo-net 0.5.0", "http 1.2.0", + "js-sys", "leptos", "leptos_actix", "leptos_meta", diff --git a/Cargo.toml b/Cargo.toml index f58b996..c9a4cd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ leptos_router = { version = "0.6" } wasm-bindgen = "=0.2.99" serde = { version = "1.0", features = ["derive"] } uuid = { version = "1.0", features = ["v4"] } -web-sys = { version = "0.3", features = ["Event"] } +web-sys = { version = "0.3", features = ["Event", "HtmlElement", "Window", "EventTarget", "Element", "DomRect"] } +js-sys = "0.3" nostr-sdk = "0.37" tokio = "1" gloo-net = "0.5" diff --git a/assets/style.css b/assets/style.css index 49b83d8..5b52e70 100644 --- a/assets/style.css +++ b/assets/style.css @@ -68,4 +68,28 @@ th, td { th { background-color: #f2f2f2; +} + +.suggestions-popup { + background-color: white; + border: 1px solid #ccc; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + max-height: 200px; + overflow-y: auto; + z-index: 1000; +} + +.suggestions-popup ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.suggestions-popup li { + padding: 8px 12px; + cursor: pointer; +} + +.suggestions-popup li:hover { + background-color: #f0f0f0; } \ No newline at end of file diff --git a/src/components/items_list.rs b/src/components/items_list.rs index c279b3b..d3fc4eb 100644 --- a/src/components/items_list.rs +++ b/src/components/items_list.rs @@ -6,6 +6,8 @@ use uuid::Uuid; use leptos::logging::log; use crate::models::item::Item; use std::sync::{Arc, Mutex}; +use wasm_bindgen::JsCast; +use web_sys::{FocusEvent, HtmlElement, Element, Node}; #[derive(Deserialize, Clone, Debug)] struct WikidataSuggestion { @@ -30,6 +32,8 @@ pub fn ItemsList( wikidata_id: None, }]); + let (active_cell_position, set_active_cell_position) = create_signal(None::<(f64, f64)>); + let (active_row_index, set_active_row_index) = create_signal(None::); let (wikidata_suggestions, set_wikidata_suggestions) = create_signal(Vec::::new()); @@ -65,13 +69,19 @@ pub fn ItemsList( "name" => { item.name = value.clone(); fetch_wikidata_suggestions(value.clone()); + set_active_row_index.set(Some(index)); + // Set active cell position + if let Some(element) = document().get_element_by_id(&format!("name-{}", index)) { + let rect = element.get_bounding_client_rect(); + set_active_cell_position.set(Some((rect.left(), rect.bottom()))); + } } "description" => { item.description = value.clone(); } _ => (), } - } + } // Automatically add a new row when editing the last row if index == items.len() - 1 && !value.is_empty() { @@ -112,6 +122,54 @@ pub fn ItemsList( }); }; + // Position and render the popup + let render_popup = move || { + view! { +
+
    + {move || wikidata_suggestions.get().iter().map(|suggestion| { + let label_for_click = suggestion.label.clone(); + let description_for_click = suggestion.description.clone().unwrap_or_default(); + let id = suggestion.id.clone(); + let label_for_display = label_for_click.clone(); + let description_for_display = description_for_click.clone(); + + view! { +
  • + {format!("{} - {}", label_for_display, description_for_display)} +
  • + } + }).collect::>()} +
+
+ } + }; + view! {

{ "Items List" }

@@ -130,44 +188,35 @@ pub fn ItemsList( // Editable Name Field with Wikidata Integration - -
    - {move || { - let suggestions = wikidata_suggestions.get().to_vec(); - suggestions.into_iter().map(|suggestion| { - let label_for_click = suggestion.label.clone(); - let label_for_display = suggestion.label.clone(); - let description_for_click = suggestion.description.clone().unwrap_or_default(); - let description_for_display = suggestion.description.clone().unwrap_or_default(); - let id = suggestion.id.clone(); - - // Tags for the item - let tags = vec![ - ("source".to_string(), "wikidata".to_string()), - ("wikidata_id".to_string(), id.clone()), - ]; - - view! { -
  • - { format!("{} - {}", label_for_display, description_for_display) } -
  • +
    () { + if let Some(element_ref) = node.dyn_ref::(){ + if let Some(element) = element_ref.dyn_ref::() { + 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"); } - }).collect::>() - }} -
+ } + } + on:blur=move |_| set_active_row_index.set(None) + > + +
// Editable Description Field @@ -194,6 +243,7 @@ pub fn ItemsList( }).collect::>()} + {render_popup()} } }