feat(typeahead): update structure and enhance Typeahead integration and logging:

- Bloodhound Initialization:
  - Changed the suggestion object structure to a flattened format for Typeahead compatibility.
  - Removed nested `display` structure in favor of direct `displayLabel` and `displayDescription` fields.
  - Simplified the construction of suggestion objects by directly setting flattened fields.

- Typeahead Initialization:
  - Enhanced logging in the JavaScript initialization script for better debugging.
  - Updated the `display` function in Typeahead to use flattened fields (`displayLabel` and `displayDescription`) instead of nested `display` objects.
  - Improved error handling and logging for cases where suggestions are not arrays or are empty.

- Selection Handling:
  - Added detailed logging for selected suggestions in both Rust and JavaScript.
  - Ensured the input field is updated with the selected suggestion's label.

- Code Cleanup:
  - Removed redundant nested object creation for `display` fields in Bloodhound initialization.
  - Streamlined the JavaScript initialization script for better readability and maintainability.
  - Consolidated logging statements for consistent debugging output.
This commit is contained in:
ryan 2025-04-14 17:42:04 +03:00
parent b017df9b35
commit 486bf9cbad
3 changed files with 85 additions and 38 deletions

View file

@ -828,8 +828,8 @@ pub fn ItemsList(
on_select=Callback::new(move |suggestion: WikidataSuggestion| {
set_items.update(|items| {
if let Some(item) = items.get_mut(index) {
item.name = suggestion.label.clone();
item.description = suggestion.description.clone().unwrap_or_default();
item.name = suggestion.display.label.value.clone();
item.description = suggestion.display.description.value.clone();
item.wikidata_id = Some(suggestion.id.clone());
// Automatically fetch properties when Wikidata ID is set

View file

@ -144,26 +144,29 @@ fn initialize_bloodhound(fetch: Callback<String, Vec<WikidataSuggestion>>) -> Js
for suggestion in &suggestions {
let obj = Object::new();
// Create nested display structure matching API response
let display = Object::new();
let label_obj = Object::new();
Reflect::set(&label_obj, &"value".into(), &suggestion.label.clone().into()).unwrap();
Reflect::set(&display, &"label".into(), &label_obj).unwrap();
let desc_obj = Object::new();
Reflect::set(&desc_obj, &"value".into(), &suggestion.description.clone().into()).unwrap();
Reflect::set(&display, &"description".into(), &desc_obj).unwrap();
Reflect::set(&obj, &"display".into(), &display).unwrap();
// Add flat fields as fallback
// Set flattened structure for Typeahead compatibility
Reflect::set(&obj, &"id".into(), &suggestion.id.clone().into()).unwrap();
Reflect::set(&obj, &"label".into(), &suggestion.label.clone().into()).unwrap();
Reflect::set(&obj, &"description".into(), &suggestion.description.clone().into()).unwrap();
log!("[BLOODHOUND] Constructed suggestion object: {:?}", obj);
// Flatten display values for direct access
Reflect::set(
&obj,
&"displayLabel".into(),
&suggestion.display.label.value.clone().into()
).unwrap();
Reflect::set(
&obj,
&"displayDescription".into(),
&suggestion.display.description.value.clone().into()
).unwrap();
array.push(&obj);
}
log!("[BLOODHOUND] suggestions: {:?}", array);
let _ = sync.call1(&JsValue::NULL, &array);
}) as Box<dyn Fn(JsValue, Function)>);
@ -259,6 +262,7 @@ fn initialize_typeahead(
let closure = Closure::wrap(Box::new(move |_event: web_sys::Event, suggestion: JsValue| {
log!("[TYPEAHEAD] Selection made");
if let Ok(data) = suggestion.into_serde::<WikidataSuggestion>() {
log!("[TYPEAHEAD] Selected suggestion: {:?}", data);
on_select.call(data.clone());
if let Some(input) = node_ref.get() {
input.set_value(&data.label);
@ -277,7 +281,7 @@ fn initialize_typeahead(
).unwrap();
closure.forget();
// Initialization script
// Initialization script with enhanced logging
let init_script = format!(
r#"
console.log('[JS] Starting Typeahead init for #{id}');
@ -291,39 +295,52 @@ fn initialize_typeahead(
}},
{{
name: 'suggestions',
display: 'label',
source: bloodhound.ttAdapter(),
display: function(data) {{
console.log('[JS] Display function called with data:', data);
return data.display?.label?.value || data.label || '';
}},
source: function(query, syncResults) {{
console.log('[JS] Bloodhound source called with query:', query);
var bloodhound = window.bloodhoundInstance;
bloodhound.ttAdapter()(query, function(suggestions) {{
console.log('[JS] Suggestions from Bloodhound before syncResults:', suggestions);
if (Array.isArray(suggestions)) {{
console.log('[JS] Passing suggestions to syncResults:', suggestions);
syncResults(suggestions);
}} else {{
console.warn('[JS] Suggestions are not an array:', suggestions);
}}
}});
}},
templates: {{
suggestion: function(data) {{
// Handle nested Wikidata structure
var label = data.label || '';
var description = data.description || '';
// If nested display exists, use those values
if (data.display) {{
label = data.display.label?.value || label;
description = data.display.description?.value || description;
}}
console.log('[JS] Rendering suggestion:', data);
return $('<div>')
.addClass('suggestion-item')
.append($('<div>').addClass('label').text(label))
.append($('<div>').addClass('description').text(description));
.append($('<div>').addClass('label').text(data.displayLabel || data.label))
.append($('<div>').addClass('description').text(data.displayDescription || data.description));
}},
empty: $('<div>').addClass('empty-suggestion').text('No matches found')
empty: function() {{
console.log('[JS] No suggestions found');
return $('<div>').addClass('empty-suggestion').text('No matches found');
}}
}}
}}
)
.on('typeahead:asyncreceive', function(ev, dataset, suggestions) {{
console.log('[JS] Received suggestions:', suggestions);
console.log('[JS] Received suggestions in typeahead:asyncreceive:', suggestions);
if (suggestions && suggestions.length > 0) {{
console.log('[JS] Suggestions passed to dropdown:', suggestions);
$(this).data('ttTypeahead').dropdown.open();
}} else {{
console.warn('[JS] No suggestions received or suggestions are empty.');
}}
}})
.on('typeahead:select', function(ev, suggestion) {{
console.log('[JS] Selection data:', JSON.stringify(suggestion, null, 2));
console.log('[JS] Selection event received with suggestion:', suggestion);
window['{handler}'](ev, suggestion);
}});
console.log('[JS] Typeahead initialized successfully');
}} catch (e) {{
console.error('[JS] Typeahead init error:', e);
}}

View file

@ -11,9 +11,39 @@ pub struct Item {
pub custom_properties: HashMap<String, String>,
}
#[derive(Deserialize, Clone, Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WikidataSuggestion {
pub id: String,
#[serde(default)]
pub label: String,
pub description: Option<String>,
#[serde(default)]
pub description: String,
#[serde(default)]
pub title: String,
#[serde(default, rename = "display")]
pub display: DisplayInfo,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct DisplayInfo {
#[serde(default, rename = "label")]
pub label: LabelInfo,
#[serde(default, rename = "description")]
pub description: DescriptionInfo,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct LabelInfo {
#[serde(default, rename = "value")]
pub value: String,
#[serde(default, rename = "language")]
pub language: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct DescriptionInfo {
#[serde(default, rename = "value")]
pub value: String,
#[serde(default, rename = "language")]
pub language: String,
}