diff --git a/src/components/typeahead_input.rs b/src/components/typeahead_input.rs index 40a482b..8729a23 100644 --- a/src/components/typeahead_input.rs +++ b/src/components/typeahead_input.rs @@ -44,9 +44,65 @@ pub fn TypeaheadInput( }); view! { + + } @@ -83,13 +148,29 @@ fn initialize_bloodhound(fetch: Callback>) -> Js let query_str = query.as_string().unwrap_or_default(); log!("[BLOODHOUND] Fetching suggestions for: {}", query_str); let suggestions = fetch.call(query_str.clone()); - log!("[BLOODHOUND] Received {} suggestions", suggestions.len()); - + let array = Array::new(); for suggestion in &suggestions { let obj = Object::new(); - Reflect::set(&obj, &"label".into(), &suggestion.label.clone().into()).unwrap_or_default(); - Reflect::set(&obj, &"value".into(), &suggestion.id.clone().into()).unwrap_or_default(); + + // 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 + 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); array.push(&obj); } let _ = sync.call1(&JsValue::NULL, &array); @@ -117,6 +198,16 @@ fn initialize_bloodhound(fetch: Callback>) -> Js &"rateLimitWait".into(), &JsValue::from(300) ).unwrap(); + + // Response filter to prevent HTML parsing errors + let filter_fn = js_sys::Function::new_no_args( + "return function(response) { return response || []; }" + ); + Reflect::set( + &remote_config, + &"filter".into(), + &filter_fn + ).unwrap(); // Wildcard function Reflect::set( @@ -128,9 +219,14 @@ fn initialize_bloodhound(fetch: Callback>) -> Js Reflect::set(&bloodhound_options, &"remote".into(), &remote_config).unwrap(); // Tokenizer functions from Bloodhound - let tokenizer = js_sys::eval(r#"Bloodhound.tokenizers.whitespace"#) - .expect("Should get whitespace tokenizer"); - + let tokenizer = js_sys::Function::new_no_args( + r#" + return function(query) { + return query.trim().split(/\s+/); + } + "# + ); + Reflect::set( &bloodhound_options, &"datumTokenizer".into(), @@ -168,20 +264,7 @@ fn initialize_typeahead( let input_id = format!("typeahead-{}", uuid::Uuid::new_v4()); input.set_id(&input_id); - let dataset = Object::new(); - let bloodhound_ref = bloodhound.unchecked_ref::(); - - Reflect::set(&dataset, &"source".into(), &bloodhound_ref.tt_adapter()).unwrap(); - Reflect::set(&dataset, &"display".into(), &"label".into()).unwrap(); - Reflect::set(&dataset, &"limit".into(), &JsValue::from(10)).unwrap(); - - let templates = Object::new(); - let suggestion_fn = js_sys::Function::new_no_args( - "return '
' + data.label + '
';" - ); - Reflect::set(&templates, &"suggestion".into(), &suggestion_fn.into()).unwrap(); - Reflect::set(&dataset, &"templates".into(), &templates).unwrap(); - + // Create selection handler closure let closure = Closure::wrap(Box::new(move |_event: web_sys::Event, suggestion: JsValue| { log!("[TYPEAHEAD] Selection made"); if let Ok(data) = suggestion.into_serde::() { @@ -194,6 +277,7 @@ fn initialize_typeahead( } }) as Box); + // Register global handler let handler_name = format!("handler_{}", input_id); js_sys::Reflect::set( &js_sys::global(), @@ -202,7 +286,7 @@ fn initialize_typeahead( ).unwrap(); closure.forget(); - // Corrected initialization script using bracket notation for handler + // Initialization script let init_script = format!( r#" console.log('[JS] Starting Typeahead init for #{id}'); @@ -216,30 +300,49 @@ fn initialize_typeahead( }}, {{ name: 'suggestions', - source: bloodhound.ttAdapter(), display: 'label', + source: bloodhound.ttAdapter(), templates: {{ suggestion: function(data) {{ - console.log('[JS] Rendering suggestion', data); - return $('
').text(data.label); - }} + // 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; + }} + + return $('
') + .addClass('suggestion-item') + .append($('
').addClass('label').text(label)) + .append($('
').addClass('description').text(description)); + }}, + empty: $('
').addClass('empty-suggestion').text('No matches found') }} }} - ).on('typeahead:select', function(ev, suggestion) {{ - console.log('[JS] Selection event received'); + ) + .on('typeahead:asyncreceive', function(ev, dataset, suggestions) {{ + console.log('[JS] Received suggestions:', suggestions); + if (suggestions && suggestions.length > 0) {{ + $(this).data('ttTypeahead').dropdown.open(); + }} + }}) + .on('typeahead:select', function(ev, suggestion) {{ + console.log('[JS] Selection data:', JSON.stringify(suggestion, null, 2)); window['{handler}'](ev, suggestion); }}); - console.log('[JS] Typeahead initialized successfully'); }} catch (e) {{ console.error('[JS] Typeahead init error:', e); }} "#, id = input_id, - handler = handler_name.replace('-', "_") // Replace hyphens to avoid JS issues + handler = handler_name.replace('-', "_") ); log!("[RUST] Initialization script: {}", init_script); if let Err(e) = js_sys::eval(&init_script) { log!("[RUST] Eval error: {:?}", e); } -} \ No newline at end of file +}