diff --git a/src/app.rs b/src/app.rs
index 927f816..8b69dc6 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -30,6 +30,8 @@ pub fn App() -> impl IntoView {
// });
view! {
+
+
diff --git a/src/components/typeahead_input.rs b/src/components/typeahead_input.rs
index 02484f1..501245e 100644
--- a/src/components/typeahead_input.rs
+++ b/src/components/typeahead_input.rs
@@ -7,6 +7,8 @@ use gloo_utils::format::JsValueSerdeExt;
use wasm_bindgen::JsCast;
use web_sys::HtmlInputElement;
use leptos::logging::log;
+use std::time::Duration;
+
#[component]
pub fn TypeaheadInput(
value: String,
@@ -16,11 +18,20 @@ pub fn TypeaheadInput(
) -> impl IntoView {
let (is_initialized, set_initialized) = create_signal(false);
- create_effect(move |_| {
- if let (Some(input), false) = (node_ref.get(), is_initialized.get()) {
- let bloodhound = initialize_bloodhound(fetch_suggestions.clone());
- initialize_typeahead(&input, bloodhound, on_select.clone(), node_ref.clone());
- set_initialized.set(true);
+ spawn_local(async move {
+ log!("[INIT] Component mounted");
+
+ let mut retries = 0;
+ while retries < 10 {
+ if let Some(input) = node_ref.get() {
+ log!("[INIT] Input element found");
+ let bloodhound = initialize_bloodhound(fetch_suggestions.clone());
+ initialize_typeahead(&input, bloodhound, on_select.clone(), node_ref.clone());
+ set_initialized.set(true);
+ break;
+ }
+ gloo_timers::future::sleep(Duration::from_millis(100)).await;
+ retries += 1;
}
});
@@ -30,6 +41,18 @@ pub fn TypeaheadInput(
class="typeahead"
prop:value=value
node_ref=node_ref
+ on:focus=move |_| log!("[FOCUS] Name input focused")
+ on:blur=move |_| log!("[FOCUS] Name input blurred")
+ on:input=move |ev| {
+ let value = event_target_value(&ev);
+ log!("[INPUT] Value changed: {}", value);
+ if let Some(input) = node_ref.get() {
+ // Correct DOM element access using JsCast
+ let dom_input: &web_sys::HtmlInputElement = &*input;
+ let id = dom_input.id();
+ let _ = js_sys::eval(&format!("console.log('JS Value:', $('#{}').val())", id));
+ }
+ }
/>
}
}
@@ -52,10 +75,12 @@ extern "C" {
fn initialize_bloodhound(fetch: Callback>) -> JsValue {
let bloodhound_options = Object::new();
- // Store the Closure in a variable to prevent premature garbage collection
- let remote_fn = Closure::wrap(Box::new(move |query: String, sync: js_sys::Function| {
- log!("Fetching suggestions for: {}", query);
+ // Configure Bloodhound remote with proper parameters
+ let remote_fn = Closure::wrap(Box::new(move |query: String, sync: Function| {
+ log!("[BLOODHOUND] Fetching suggestions for: {}", query);
let suggestions = fetch.call(query.clone());
+ log!("[BLOODHOUND] Received {} suggestions", suggestions.len());
+
let array = Array::new();
for suggestion in &suggestions {
let obj = Object::new();
@@ -63,26 +88,36 @@ fn initialize_bloodhound(fetch: Callback>) -> Js
Reflect::set(&obj, &"value".into(), &suggestion.id.clone().into()).unwrap();
array.push(&obj);
}
- sync.call1(&JsValue::NULL, &array).unwrap();
- }) as Box);
- // Configure Bloodhound
+ sync.call1(&JsValue::NULL, &array).unwrap();
+ }) as Box);
+
let remote_config = Object::new();
- Reflect::set(&remote_config, &"url".into(), &"".into()).unwrap();
- Reflect::set(&remote_config, &"wildcard".into(), &"%QUERY".into()).unwrap();
- Reflect::set(&remote_config, &"prepare".into(), &remote_fn.as_ref()).unwrap();
- Reflect::set(&remote_config, &"rateLimitWait".into(), &JsValue::from(300)).unwrap();
+ Reflect::set(
+ &remote_config,
+ &"prepare".into(),
+ &js_sys::eval(&format!(
+ "function(query, callback) {{
+ return {}(query, callback);
+ }}",
+ remote_fn.as_ref().unchecked_ref::().to_string()
+ )).unwrap()
+ ).unwrap();
+
+ Reflect::set(
+ &remote_config,
+ &"wildcard".into(),
+ &JsValue::from_str("%QUERY")
+ ).unwrap();
Reflect::set(&bloodhound_options, &"remote".into(), &remote_config).unwrap();
- Reflect::set(&bloodhound_options, &"queryTokenizer".into(), &JsValue::from("whitespace")).unwrap();
- Reflect::set(&bloodhound_options, &"datumTokenizer".into(), &JsValue::from("whitespace")).unwrap();
+ Reflect::set(&bloodhound_options, &"datumTokenizer".into(), &JsValue::from_str("whitespace")).unwrap();
+ Reflect::set(&bloodhound_options, &"queryTokenizer".into(), &JsValue::from_str("whitespace")).unwrap();
let bloodhound = Bloodhound::new(&bloodhound_options.into());
bloodhound.initialize(true);
-
- // Prevent Closure from being dropped
remote_fn.forget();
-
+
bloodhound.into()
}
@@ -92,92 +127,63 @@ fn initialize_typeahead(
on_select: Callback,
node_ref: NodeRef,
) {
- // input event handler for direct typing
- let node_ref_clone = node_ref.clone();
- let input_handler = Closure::wrap(Box::new(move |_event: web_sys::Event| {
- if let Some(input) = node_ref_clone.get() {
- let value = input.value();
- log!("Input updated: {}", value);
- // Create synthetic change event for Leptos
- let event = web_sys::CustomEvent::new("input").unwrap();
- input.dispatch_event(&event).unwrap();
- }
- }) as Box);
+ log!("[TYPEAHEAD] Initializing for input: {}", input.id());
+ let input_id = format!("typeahead-{}", uuid::Uuid::new_v4());
+ input.set_id(&input_id);
- input.add_event_listener_with_callback(
- "input",
- input_handler.as_ref().unchecked_ref()
- ).unwrap();
- input_handler.forget();
-
- let typeahead_options = Object::new();
- Reflect::set(&typeahead_options, &"hint".into(), &JsValue::TRUE).unwrap();
- Reflect::set(&typeahead_options, &"highlight".into(), &JsValue::TRUE).unwrap();
- Reflect::set(&typeahead_options, &"minLength".into(), &JsValue::from(1)).unwrap();
-
- // Bloodhound remote configuration
- let bloodhound_ref = bloodhound.unchecked_ref::();
- let remote_config = Object::new();
- Reflect::set(&remote_config, &"prepare".into(), &JsValue::from_str("function(q) { return { q: q }; }")).unwrap();
- Reflect::set(&remote_config, &"transform".into(), &js_sys::Function::new_with_args("response", "return response;")).unwrap();
-
- // Update dataset configuration
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();
- // Create proper templates
- let templates = Object::new();
- Reflect::set(&templates, &"suggestion".into(), &js_sys::eval(r#"
- function(data) {
- return '' +
- '' + data.label + '' +
- (data.description ? '
' + data.description + '' : '') +
- '
';
+ // Create and register the closure
+ let closure = Closure::wrap(Box::new(move |_event: web_sys::Event, suggestion: JsValue| {
+ log!("[TYPEAHEAD] Selection made");
+ let data: WikidataSuggestion = suggestion.into_serde().unwrap();
+ on_select.call(data.clone());
+
+ if let Some(input) = node_ref.get() {
+ input.set_value(&data.label);
}
- "#).unwrap()).unwrap();
+ }) as Box);
- Reflect::set(&dataset, &"templates".into(), &templates).unwrap();
+ // Register the closure in the JS global scope
+ let handler_name = format!("handler_{}", input_id);
+ let handler_name_global = handler_name.clone();
+ let global = js_sys::global();
+ Reflect::set(
+ &global,
+ &handler_name_global.into(),
+ &closure.as_ref()
+ ).unwrap();
// Typeahead initialization using jQuery
let init_script = format!(
- r#"(function() {{
- $('#{}').typeahead({}, {});
- }})"#,
- input.id(),
- JSON::stringify(&typeahead_options).unwrap(),
- JSON::stringify(&dataset).unwrap()
+ r#"
+ (function() {{
+ console.log('[TYPEAHEAD] Initializing for #{id}');
+ $('#{id}').typeahead(
+ {{
+ hint: true,
+ highlight: true,
+ minLength: 1
+ }},
+ {dataset}
+ ).on('typeahead:select', function(ev, suggestion) {{
+ console.log('[TYPEAHEAD] Select event triggered');
+ {handler}(ev, suggestion);
+ }});
+ console.log('[TYPEAHEAD] Initialization complete for #{id}');
+ }})();
+ "#,
+ id = input_id,
+ dataset = JSON::stringify(&dataset).unwrap(),
+ handler = handler_name
);
-
+
+ log!("[TYPEAHEAD] Init script: {}", init_script);
let _ = js_sys::eval(&init_script).unwrap();
-
- // Handle selection events
- let closure = Closure::wrap(Box::new(move |event: web_sys::Event| {
- if let Some(selected) = event.target() {
- let js_value = selected.unchecked_into::();
- let data: WikidataSuggestion = js_sys::JSON::parse(
- &js_sys::JSON::stringify(&js_value)
- .unwrap()
- .as_string()
- .unwrap()
- .as_str()
- ).unwrap()
- .into_serde()
- .unwrap();
- let data_clone = data.clone();
-
- on_select.call(data);
- // Explicitly update the input value
- if let Some(input) = node_ref.get() {
- input.set_value(&data_clone.label);
- }
- }
- }) as Box);
-
- input.add_event_listener_with_callback(
- "typeahead:select",
- closure.as_ref().unchecked_ref()
- ).unwrap();
closure.forget();
}
\ No newline at end of file