Compare commits
No commits in common. "d565563edb3b18613c24aacc83d0a926ed8e415d" and "a8088c232be1bc7edc207a67d0f361bfcf22f0ce" have entirely different histories.
d565563edb
...
a8088c232b
2 changed files with 108 additions and 65 deletions
|
@ -865,21 +865,12 @@ pub fn ItemsList(
|
||||||
<TypeaheadInput
|
<TypeaheadInput
|
||||||
value=item.name.clone()
|
value=item.name.clone()
|
||||||
fetch_suggestions=Callback::new({
|
fetch_suggestions=Callback::new({
|
||||||
// Use the item's unique ID in the key to ensure uniqueness
|
let key = format!("name-{}", index);
|
||||||
let key = format!("name-{}-{}", index, item.id);
|
|
||||||
let wikidata_suggestions_clone = wikidata_suggestions.clone();
|
let wikidata_suggestions_clone = wikidata_suggestions.clone();
|
||||||
|
|
||||||
move |query: String| -> Vec<WikidataSuggestion> {
|
move |query: String| -> Vec<WikidataSuggestion> {
|
||||||
// Only fetch suggestions if the query is not empty
|
|
||||||
if !query.is_empty() {
|
|
||||||
// Fetch suggestions in a separate function to avoid capturing too much
|
// Fetch suggestions in a separate function to avoid capturing too much
|
||||||
fetch_wikidata_suggestions(key.clone(), query.clone());
|
fetch_wikidata_suggestions(key.clone(), query.clone());
|
||||||
} else {
|
|
||||||
// Clear suggestions for this key if query is empty
|
|
||||||
set_wikidata_suggestions.update(|suggestions| {
|
|
||||||
suggestions.remove(&key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current suggestions from the signal
|
// Return current suggestions from the signal
|
||||||
let suggestions = wikidata_suggestions_clone.get();
|
let suggestions = wikidata_suggestions_clone.get();
|
||||||
|
@ -892,14 +883,10 @@ pub fn ItemsList(
|
||||||
let property_cache_clone = property_cache.clone();
|
let property_cache_clone = property_cache.clone();
|
||||||
let set_property_cache_clone = set_property_cache.clone();
|
let set_property_cache_clone = set_property_cache.clone();
|
||||||
let property_labels_clone = property_labels.clone();
|
let property_labels_clone = property_labels.clone();
|
||||||
let current_url_clone = Rc::clone(¤t_url_clone);
|
|
||||||
let selected_properties_clone = selected_properties.clone();
|
|
||||||
let items_len = items.len();
|
|
||||||
|
|
||||||
move |suggestion: WikidataSuggestion| {
|
move |suggestion: WikidataSuggestion| {
|
||||||
let wikidata_id = suggestion.id.clone();
|
let wikidata_id = suggestion.id.clone();
|
||||||
|
|
||||||
// Update the current item with the selected suggestion
|
|
||||||
set_items_clone.update(|items| {
|
set_items_clone.update(|items| {
|
||||||
if let Some(item) = items.get_mut(index) {
|
if let Some(item) = items.get_mut(index) {
|
||||||
item.name = suggestion.display.label.value.clone();
|
item.name = suggestion.display.label.value.clone();
|
||||||
|
@ -924,10 +911,33 @@ pub fn ItemsList(
|
||||||
property_labels_for_task
|
property_labels_for_task
|
||||||
).await;
|
).await;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
is_last_row={index == items.len() - 1}
|
||||||
|
on_input=Callback::new({
|
||||||
|
// Clone items.len() before moving into the closure
|
||||||
|
let items_len = items.len();
|
||||||
|
let set_items_clone = set_items.clone();
|
||||||
|
let current_url_clone = Rc::clone(¤t_url_clone);
|
||||||
|
let selected_properties_clone = selected_properties.clone();
|
||||||
|
let node_ref_clone = node_ref.clone(); // Clone the node_ref for later use
|
||||||
|
|
||||||
// Add a new row if this is the last row - in a separate update call
|
move |value: String| {
|
||||||
if index == items_len - 1 {
|
if index == items_len - 1 && !value.is_empty() {
|
||||||
// First, create a completely new item
|
// Store the current active element before modifying the DOM
|
||||||
|
let document = web_sys::window().unwrap().document().unwrap();
|
||||||
|
let active_element_id = document
|
||||||
|
.active_element()
|
||||||
|
.map(|el| el.id())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// Store the current input value
|
||||||
|
let input_value = value.clone();
|
||||||
|
|
||||||
|
let current_url_for_new_item = Rc::clone(¤t_url_clone);
|
||||||
|
let selected_properties_for_new_item = selected_properties_clone.clone();
|
||||||
|
|
||||||
|
set_items_clone.update(|items| {
|
||||||
let new_item = Item {
|
let new_item = Item {
|
||||||
id: Uuid::new_v4().to_string(),
|
id: Uuid::new_v4().to_string(),
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
|
@ -935,18 +945,10 @@ pub fn ItemsList(
|
||||||
wikidata_id: None,
|
wikidata_id: None,
|
||||||
custom_properties: HashMap::new(),
|
custom_properties: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
items.push(new_item.clone());
|
||||||
// Clone for database save
|
|
||||||
let new_item_clone = new_item.clone();
|
|
||||||
let current_url_for_new_item = Rc::clone(¤t_url_clone);
|
|
||||||
let selected_properties_for_new_item = selected_properties_clone.clone();
|
|
||||||
|
|
||||||
// Add the new item in a separate update to force re-rendering
|
|
||||||
set_items_clone.update(|items| {
|
|
||||||
items.push(new_item);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save the new item to the database in a separate task
|
// Save the new item to the database in a separate task
|
||||||
|
let new_item_clone = new_item.clone();
|
||||||
let current_url_for_task = Rc::clone(¤t_url_for_new_item);
|
let current_url_for_task = Rc::clone(¤t_url_for_new_item);
|
||||||
let selected_properties_for_task = selected_properties_for_new_item;
|
let selected_properties_for_task = selected_properties_for_new_item;
|
||||||
|
|
||||||
|
@ -957,18 +959,60 @@ pub fn ItemsList(
|
||||||
current_url_for_task.to_string()
|
current_url_for_task.to_string()
|
||||||
).await;
|
).await;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schedule focus restoration after the DOM has been updated
|
||||||
|
spawn_local(async move {
|
||||||
|
// Small delay to ensure DOM is updated
|
||||||
|
gloo_timers::future::TimeoutFuture::new(50).await;
|
||||||
|
|
||||||
|
// Try to restore focus to the element that had it
|
||||||
|
if !active_element_id.is_empty() {
|
||||||
|
if let Some(element) = document.get_element_by_id(&active_element_id) {
|
||||||
|
if let Some(input) = element.dyn_ref::<web_sys::HtmlInputElement>() {
|
||||||
|
// Set the value before focusing to preserve what was typed
|
||||||
|
input.set_value(&input_value);
|
||||||
|
let _ = input.focus();
|
||||||
|
|
||||||
|
// Trigger the typeahead to show suggestions
|
||||||
|
let trigger_typeahead_script = format!(
|
||||||
|
r#"
|
||||||
|
try {{
|
||||||
|
// Get the input element
|
||||||
|
var $input = $('#{}');
|
||||||
|
if ($input.length > 0) {{
|
||||||
|
// Manually trigger the typeahead query
|
||||||
|
$input.typeahead('val', '{}');
|
||||||
|
|
||||||
|
// Force the menu to open
|
||||||
|
setTimeout(function() {{
|
||||||
|
var event = new Event('input', {{
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
}});
|
||||||
|
$input[0].dispatchEvent(event);
|
||||||
|
}}, 100);
|
||||||
|
}}
|
||||||
|
}} catch(e) {{
|
||||||
|
console.error('Error triggering typeahead:', e);
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
active_element_id,
|
||||||
|
input_value.replace("'", "\\'") // Escape single quotes
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute the script after a short delay to ensure typeahead is initialized
|
||||||
|
gloo_timers::future::TimeoutFuture::new(200).await;
|
||||||
|
let _ = js_sys::eval(&trigger_typeahead_script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
is_last_row={index == items.len() - 1}
|
node_ref=node_ref.clone() // Use the node_ref to track this input
|
||||||
on_input=Callback::new({
|
id=format!("name-input-{}", index) // Add a unique ID to each input
|
||||||
|
|
||||||
move |value: String| {
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
node_ref=node_ref.clone()
|
|
||||||
id=format!("name-input-{}-{}", index, item.id)
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}.into_view(),
|
}.into_view(),
|
||||||
|
|
|
@ -100,7 +100,6 @@ pub fn TypeaheadInput(
|
||||||
#[prop(optional)] is_last_row: bool,
|
#[prop(optional)] is_last_row: bool,
|
||||||
#[prop(optional)] on_input: Option<Callback<String>>,
|
#[prop(optional)] on_input: Option<Callback<String>>,
|
||||||
#[prop(optional)] id: Option<String>,
|
#[prop(optional)] id: Option<String>,
|
||||||
#[prop(optional)] key: Option<String>,
|
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let (is_initialized, set_initialized) = create_signal(false);
|
let (is_initialized, set_initialized) = create_signal(false);
|
||||||
|
|
||||||
|
@ -524,12 +523,12 @@ pub fn TypeaheadInput(
|
||||||
let value = event_target_value(&ev);
|
let value = event_target_value(&ev);
|
||||||
log!("[INPUT] Value changed: {} ({})", value, component_id_for_input);
|
log!("[INPUT] Value changed: {} ({})", value, component_id_for_input);
|
||||||
|
|
||||||
// // If this is the last row and we have an on_input callback, call it
|
// If this is the last row and we have an on_input callback, call it
|
||||||
// if is_last_row && !value.is_empty() {
|
if is_last_row && !value.is_empty() {
|
||||||
// if let Some(callback) = &on_input {
|
if let Some(callback) = &on_input {
|
||||||
// callback.call(value.clone());
|
callback.call(value.clone());
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue