feat(item_list): Added active cell tracking with position updates for popup rendering.

This commit is contained in:
ryan 2024-12-27 03:01:03 +03:00
parent 9f28d30d48
commit 8cd277d66a
4 changed files with 115 additions and 39 deletions

1
Cargo.lock generated
View File

@ -724,6 +724,7 @@ dependencies = [
"futures",
"gloo-net 0.5.0",
"http 1.2.0",
"js-sys",
"leptos",
"leptos_actix",
"leptos_meta",

View File

@ -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"

View File

@ -69,3 +69,27 @@ 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;
}

View File

@ -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::<usize>);
let (wikidata_suggestions, set_wikidata_suggestions) =
create_signal(Vec::<WikidataSuggestion>::new());
@ -65,6 +69,12 @@ 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();
@ -112,6 +122,54 @@ pub fn ItemsList(
});
};
// Position and render the popup
let render_popup = move || {
view! {
<div
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)
} else {
"display: none;".to_string()
}
}
>
<ul>
{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! {
<li on:click=move |_| {
if let Some(index) = active_row_index.get() {
set_items.update(|items| {
if let Some(item) = items.get_mut(index) {
item.name = label_for_click.clone();
item.description = description_for_click.clone();
item.wikidata_id = Some(id.clone());
item.tags.push(("wikidata_id".to_string(), id.clone()));
}
});
}
set_wikidata_suggestions.set(Vec::new());
set_active_cell_position.set(None);
}>
{format!("{} - {}", label_for_display, description_for_display)}
</li>
}
}).collect::<Vec<_>>()}
</ul>
</div>
}
};
view! {
<div>
<h1>{ "Items List" }</h1>
@ -130,44 +188,35 @@ pub fn ItemsList(
<tr>
// Editable Name Field with Wikidata Integration
<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>() {
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");
}
}
}
on:blur=move |_| set_active_row_index.set(None)
>
<EditableCell
value=item.name.clone()
on_input=move |value| update_item(index, "name", value)
key=format!("name-{}", index) // Unique key per cell
key=format!("name-{}", index)
/>
<ul>
{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! {
<li on:click=move |_| {
set_items.update(|items| {
if let Some(item) = items.get_mut(index) {
item.description = description_for_click.clone();
item.tags.extend(tags.clone());
item.wikidata_id = Some(id.clone());
item.name = label_for_click.clone();
}
});
}>
{ format!("{} - {}", label_for_display, description_for_display) }
</li>
}
}).collect::<Vec<_>>()
}}
</ul>
</div>
</td>
// Editable Description Field
<td>
@ -194,6 +243,7 @@ pub fn ItemsList(
}).collect::<Vec<_>>()}
</tbody>
</table>
{render_popup()}
</div>
}
}