Clone in a terminal:
Commit a49ec95
Johan Mattsson - 2026-06-29T11:06:08+00:00
a49ec9587f45d22b2c96274bd69803972df154b8johan.mattsson.m@gmail.com

More callbacks

Changed files (1)

Diff

diff --git a/src/init.rs b/src/init.rsindex 27eb6dc..183685e 100644--- a/src/init.rs+++ b/src/init.rs@@ -44,6 +44,7 @@ pub fn component_impl(attr: pm::TokenStream, item: pm::TokenStream) -> pm::Token     let attach_js = format!("attachComponent{}", struct_name_ident);     let detach_js = format!("detachComponent{}", struct_name_ident);     let observed_js = format!("observedComponentAttribute{}", struct_name_ident);+    let changed_js = format!("changedComponentAttribute{}", struct_name_ident);      let create_class_js_indent = Ident::new(&create_class_js, Span::call_site());     let init_self_js_ident = Ident::new(&init_self_js, Span::call_site());@@ -55,21 +56,26 @@ pub fn component_impl(attr: pm::TokenStream, item: pm::TokenStream) -> pm::Token         attach_js.as_str(),         detach_js.as_str(),         observed_js.as_str(),+        changed_js.as_str(),     );      let rs_self = gen_rust_self(component_struct_item.clone(), init_self_js_ident);     let attach_fn = attach_quote(&attach_js, struct_name_ident);     let detach_fn = detach_quote(&detach_js, struct_name_ident);     let observed_fn = observed_quote(&observed_js, struct_name_ident);+    let changed_fn = changed_quote(&changed_js, struct_name_ident);+    let self_from_id = component_from_id(component_struct_item.clone());      let expanded = quote! {         #global_store         #rs_self         #component_struct_item         #create_js_class_fn-                #attach_fn-                #detach_fn-                #observed_fn+        #attach_fn+        #detach_fn+        #observed_fn+        #self_from_id+        #changed_fn     };      expanded.into()@@ -85,8 +91,8 @@ fn attach_quote(atach_state_js_fn: &str, struct_name: &Ident) -> proc_macro2::To      let expanded = quote! {         #[callback(#js_callback_ident)]-        fn #rs_name_ident(element: HtmlElement, wasm_id: u32) {-            gloo::console::log!("componentConnect generated wasm_id: {}", wasm_id);+        fn #rs_name_ident(element: HtmlElement, wasm_id: String) {+            gloo::console::log!("componentConnect generated wasm_id: {}", wasm_id.clone());              let mut store = #store_ident.write().unwrap(); @@ -100,7 +106,7 @@ fn attach_quote(atach_state_js_fn: &str, struct_name: &Ident) -> proc_macro2::To             if let Some(component) = store_map.get_mut(&wasm_id) {                 component.connected(element);             } else {-                error!("Component id {} not found in connect.", wasm_id);+                error!("Component id {} not found in connect.", wasm_id.clone());             }         }     };@@ -118,7 +124,7 @@ fn detach_quote(detach_state_js_fn: &str, struct_name: &Ident) -> proc_macro2::T      let expanded = quote! {         #[callback(#js_callback_ident)]-        fn #rs_name_ident(element: HtmlElement, wasm_id: u32) {+        fn #rs_name_ident(element: HtmlElement, wasm_id: String) {             let mut store = #store_ident.write().unwrap();              if store.is_none() {@@ -131,7 +137,7 @@ fn detach_quote(detach_state_js_fn: &str, struct_name: &Ident) -> proc_macro2::T             if let Some(component) = store_map.get_mut(&wasm_id) {                 component.disconnected(element);             } else {-                error!("Component id {} not found in disconnected.", wasm_id);+                error!("Component id {} not found in disconnected.", wasm_id.clone());             }              store_map.remove(&wasm_id);@@ -141,6 +147,40 @@ fn detach_quote(detach_state_js_fn: &str, struct_name: &Ident) -> proc_macro2::T     expanded } +fn changed_quote(changed_state_js_fn: &str, struct_name: &Ident) -> proc_macro2::TokenStream {+    let js_callback_ident = Ident::new(changed_state_js_fn, Span::call_site());+    let rs_name_str = from_pascal_to_snake(changed_state_js_fn);+    let rs_name_ident = Ident::new(&rs_name_str, Span::call_site());++    let store_name_str = store_name(struct_name);+    let store_ident = Ident::new(&store_name_str, Span::call_site());++    let expanded = quote! {+        #[callback(#js_callback_ident)]+        fn #rs_name_ident(element: HtmlElement, wasm_id: String, key: String, from: String, to: String) {+            let mut store = #store_ident.write().unwrap();++            if store.is_none() {+                error!("Component not found in connect.");+                return;+            }++            let store_map = store.as_mut().unwrap();++            if let Some(component) = store_map.get_mut(&wasm_id) {+                let name = key.as_str();+                let old_value = ::wasm_bindgen::JsValue::from_str(&from);+                let new_value = ::wasm_bindgen::JsValue::from_str(&to);+                component.attribute_changed(element, name, &old_value, &new_value);+            } else {+                error!("Component id {} not found in changed attribute.", wasm_id.clone());+            }+        }+    };++    expanded+}+ fn observed_quote(observed_js_fn: &str, struct_name: &Ident) -> proc_macro2::TokenStream {     let js_callback_ident = Ident::new(observed_js_fn, Span::call_site());     let rs_name_str = from_pascal_to_snake(observed_js_fn);@@ -169,7 +209,7 @@ fn generate_component_store(struct_name: Ident) -> proc_macro2::TokenStream {     let store_ident = Ident::new(&store_name, Span::call_site());      let expanded = quote! {-        pub static #store_ident: std::sync::RwLock<Option<std::collections::HashMap<u32, #struct_name>>> = std::sync::RwLock::new(None);+        pub static #store_ident: std::sync::RwLock<Option<std::collections::HashMap<String, #struct_name>>> = std::sync::RwLock::new(None);     };      expanded@@ -193,6 +233,18 @@ fn from_pascal_to_snake(fn_name: &str) -> String {     result } +fn component_from_id(struct_item: ItemStruct) -> proc_macro2::TokenStream {+    let struct_ident = &struct_item.ident;+    let expanded = quote! {+        pub fn get_counter() -> #struct_ident {  // returns an instance of the component+            log!("Get counter");+            #struct_ident::default()+        }+    };++    expanded+}+ fn gen_rust_self(struct_item: ItemStruct, js_name: Ident) -> proc_macro2::TokenStream {     let struct_ident = &struct_item.ident;     let store_name_str = store_name(struct_ident);@@ -208,7 +260,7 @@ fn gen_rust_self(struct_item: ItemStruct, js_name: Ident) -> proc_macro2::TokenS     let expanded = quote! {         #[callback(#js_name)]         #[allow(non_snake_case)]-        pub fn #rust_ident() -> u32 {  // init_component_state_struct_name, returns id for the instance of the element+        pub fn #rust_ident() -> String {  // init_component_state_struct_name, returns id for the instance of the element             log!("Run counter init rs state.");             let mut counter = #struct_ident::default();             let wasm_id = ::kaja_web::NEXT_COMPONENT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);            let mut store = #store_ident.write().expect("failed to acquire write lock");@@ -217,9 +269,10 @@ fn gen_rust_self(struct_item: ItemStruct, js_name: Ident) -> proc_macro2::TokenS                 *store = Some(std::collections::HashMap::new());             } +            let key = wasm_id.to_string();             let store_map = store.as_mut().unwrap();-            store_map.insert(wasm_id, counter);-            wasm_id+            store_map.insert(key.clone(), counter);+            key         }     }; @@ -233,6 +286,7 @@ fn create_class(     attach_state_fn: &str,     detach_state_fn: &str,     observed_fn: &str,+    change_fn: &str, ) -> proc_macro2::TokenStream {     let name_str_lit = LitStr::new(&class_creator.to_string(), Span::call_site()); @@ -254,23 +308,30 @@ fn create_class(                     return class extends HTMLElement {                         constructor() {                             super();-                            this.kajaComponentId = -1;+                            this.setAttribute("componentId", "-1");                         }                          connectedCallback() {-                            if (this.kajaComponentId === -1) {-                                this.kajaComponentId = __INIT_CALLBACK();+                            let componentId = this.getAttribute("componentId");++                            if (componentId === "-1") {+                                let id = __INIT_CALLBACK();+                                this.setAttribute("componentId", id);+                                componentId = id;                             } -                            __ATTACH_CALLBACK(this, this.kajaComponentId);+                            __ATTACH_CALLBACK(this, componentId);                         }                          disconnectedCallback() {-                            __DETACH_CALLBACK(this, this.kajaComponentId);+                            let componentId = this.getAttribute("componentId");+                            __DETACH_CALLBACK(this, componentId);                         }                          attributeChangedCallback(name, oldValue, newValue) {-                            //callbacks.attributeChanged(this, name, oldValue, newValue);+                            let componentId = this.getAttribute("componentId");+                            console.log(componentId, name, oldValue, newValue);+                            __ATTRIBUTE_CHANGED_CALLBACK(this, componentId, name, oldValue === null ? "" : String(oldValue), newValue === null ? "" : String(newValue));                         }                          static get observedAttributes() {@@ -283,6 +344,7 @@ fn create_class(             let js_init_with_state = js_init.replace("__INIT_CALLBACK", #init_self_fn);             let js_init_with_state = js_init_with_state.replace("__ATTACH_CALLBACK", #attach_state_fn);             let js_init_with_state = js_init_with_state.replace("__DETACH_CALLBACK", #detach_state_fn);+            let js_init_with_state = js_init_with_state.replace("__ATTRIBUTE_CHANGED_CALLBACK", #change_fn);             let js_init_with_state = js_init_with_state.replace("__OBSERVED_CALLBACK", #observed_fn);              let component = js_sys::Function::new_no_args(js_init_with_state.as_str());

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112