feat(item_list): Added active cell tracking with position updates for popup rendering.
This commit is contained in:
parent
9f28d30d48
commit
8cd277d66a
|
@ -724,6 +724,7 @@ dependencies = [
|
|||
"futures",
|
||||
"gloo-net 0.5.0",
|
||||
"http 1.2.0",
|
||||
"js-sys",
|
||||
"leptos",
|
||||
"leptos_actix",
|
||||
"leptos_meta",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue