Commit 8def408
New example component
Changed files (4)
modifiedexamples/main-component/src/lib.rsmodifiedexamples/main-component/src/main_component.rsmodifiedsrc/lib.rsmodifiedsrc/prelude.rs
Diff
@@ -1,6 +1,4 @@-use crate::main_component::init_test_loop; use kaja_web::prelude::*;-mod main_component; #[wasm_bindgen(start)] pub fn init() {@@ -8,5 +6,4 @@ pub fn init() { console_error_panic_hook::set_once(); init_callbacks();- init_test_loop(); }@@ -1,30 +1,28 @@ use crate::log; use async_channel::{Receiver, Sender};-use kaja_web::update; use std::collections::HashMap; use std::sync::{Arc, LazyLock, Mutex, OnceLock, RwLock}; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -#[derive(Default)]+#[component("main-component")] struct Main { test: bool, } -#[derive(Default)]+#[component("other-component")] struct Other { the_number: i32, } -trait Component {- fn update(id: &str)- where- Self: Sized;-}- impl Component for Main {+ fn connected(id: &str) {+ #[cfg(feature = "threads")]+ start_background(id);+ }+ fn update(id: &str) {- log!("Main update:", id);+ log!("Main ui_thread_update:", id); let handle = Self::get_component_arc(id); let mut component = handle.lock().unwrap(); @@ -41,91 +39,32 @@ impl Main { } } -impl Main {- fn get_component_arc(_id: &str) -> Arc<Mutex<Main>> {- Arc::new(Mutex::new(Main::default()))- }-}- impl Component for Other {- fn update(id: &str) {+ fn ui_thread_update(id: &str) { log!("Other update:", id); } } -static UPDATE_SENDER: OnceLock<Sender<String>> = OnceLock::new();--pub fn schedule_update(id: &str) {- if let Some(sender) = UPDATE_SENDER.get() {- match sender.try_send(id.to_string()) {- Ok(_) => {- log!("scheduled update for:", id);- }- Err(e) => {- log!("could not schedule update for", id);- }- }- }-}--async fn ui_update_loop(receiver: Receiver<String>) {- while let Ok(id) = receiver.recv().await {- update(&id);- }-}--static MAIN_COMPONENT: LazyLock<RwLock<HashMap<String, Arc<Mutex<Main>>>>> =- LazyLock::new(|| RwLock::new(HashMap::new()));--fn update_component_main(id: &str) -> bool {- {- let lock = MAIN_COMPONENT- .read()- .expect("Failed to lock MAIN_COMPONENT");-- if !lock.contains_key(id) {- return false;- }- }-- Main::update(id);- true-}- #[cfg(not(feature = "threads"))]-fn start_background(sender: Sender<String>) {+fn start_background(id: &str) {+ let component_id = id.to_string();+ wasm_bindgen_futures::spawn_local(async move { loop {- schedule_update("main-component-1"); // await?- schedule_update("other-component-1"); // await?-+ post_ui_thread_update(component_id.as_str()); // await? gloo_timers::future::TimeoutFuture::new(1000).await; } }); } #[cfg(feature = "threads")]-fn start_background(sender: Sender<String>) {+fn start_background(id: &str) {+ let component_id = id.to_string();+ std::thread::spawn(move || { loop {- schedule_update("main-component-1");- schedule_update("other-component-1");-+ post_ui_thread_update(component_id.as_str()); std::thread::sleep(std::time::Duration::from_secs(1)); } }); }--pub fn init_test_loop() {- let (sender, receiver) = async_channel::unbounded::<String>();-- UPDATE_SENDER- .set(sender.clone())- .expect("UPDATE_SENDER already initialized");-- // This runs on the browser UI thread- wasm_bindgen_futures::spawn_local(ui_update_loop(receiver));-- #[cfg(feature = "threads")]- start_background(sender);-}@@ -17,9 +17,6 @@ inventory::collect!(InitFn); pub struct InitComponentFn(pub fn()); inventory::collect!(InitComponentFn); -pub struct UpdateComponentFn(pub fn(id: &str) -> bool);-inventory::collect!(UpdateComponentFn);- /// Global counter shared by all components created by the #[component] macro. /// The proc-macro-generated code will increment this to obtain a unique wasm id. pub static NEXT_COMPONENT_COUNTER: std::sync::atomic::AtomicU32 =@@ -80,16 +77,6 @@ pub fn init_callbacks() { } } -pub fn update(id: &str) {- for init in inventory::iter::<UpdateComponentFn> {- let result = (init.0)(id);-- if result {- return;- }- }-}- /// Queries the document for a single element matching the given selector. /// Returns `None` if no element is found. Returns first matching element if multiple are found. pub fn get_element(selector: &str) -> Option<HtmlElement> {@@ -337,30 +324,12 @@ pub fn get_callback(name: &str) -> Option<js_sys::Function> { /// HTML Component. See kaja-web-component. pub trait Component {- fn connected(&mut self, _parent: HtmlElement) {}- fn disconnected(&mut self, _parent: HtmlElement) {}- fn observed_attributes() -> &'static [&'static str] {- &[]- }-- fn attribute_changed(- &mut self,- _parent: HtmlElement,- name: &str,- _old_value: &str,- _new_value: &str,- ) {+ fn connected(id: &str) {}+ fn disconnected(id: &str) {}+ fn attribute_changed(id: &str, name: &str, _old_value: &str, _new_value: &str) { log!("Attribute changed but no update handler for ", name); }-- /// Render the component into the given element.- fn render(&self, _element: &HtmlElement) {}-}--pub fn get_component_element(id: &str) -> Option<HtmlElement> {- let selector = format!("[componentid='{}']", id);- let element = get_element(selector.as_str());- element+ fn update(id: &str) {} } pub fn get_element_by_id(id: &str) -> Option<HtmlElement> {@@ -369,44 +338,6 @@ pub fn get_element_by_id(id: &str) -> Option<HtmlElement> { element } -pub trait ComponentStorage: Sized {- fn storage() -> &'static RwLock<Option<HashMap<String, std::sync::Arc<std::sync::Mutex<Self>>>>>;-}--/// Returns a cloned Arc pointing to the stored component. The component itself is-/// protected by an `RwLock` so callers can lock it for mutable or shared access when needed.-pub fn get_component<T: ComponentStorage + 'static>(- id: &str,-) -> Option<std::sync::Arc<std::sync::Mutex<T>>> {- let map_lock = T::storage().read();-- if map_lock.is_err() {- error!("Failed to take storage lock");- return None;- }-- let store_opt = map_lock.unwrap();-- if store_opt.is_none() {- error!("Store not initialized");- return None;- }-- let map = store_opt.as_ref().unwrap();- map.get(id).cloned()-}--pub fn get_component_id(element: &HtmlElement) -> String {- let id = element.get_attribute("componentid");-- if id.is_none() {- error!("No component id is set for element.");- return "".to_string();- }-- id.unwrap()-}- // Example: let count = get_value!(&id, Counter.field); #[macro_export] macro_rules! get_value {@@ -427,24 +358,6 @@ macro_rules! set_value { }}; } -pub fn rerender<T>(id: &str)-where- T: ComponentStorage + Component + 'static,-{- let component = get_component::<T>(id);- let binding = component.unwrap();- let component_binding = binding.lock().unwrap();- let html_element = get_component_element(id);-- if html_element.is_none() {- error!("No html_element in rerender.");- return;- }-- let element = html_element.unwrap();- component_binding.render(&element);-}- pub fn remove_css_class(element: &HtmlElement, css_class: &str) { use js_sys::Array; use wasm_bindgen::JsValue;@@ -1,8 +1,7 @@ pub use crate::{- CallbackRegistration, Component, ComponentStorage, Error, InitComponentFn, InitFn, Result,- get_callback, get_component, get_component_element, get_component_id, get_cookie_value,- get_element, get_element_by_id, get_elements, get_value, init_callbacks, inner_html,- inner_text, rerender, set_value, try_get_value, try_inner_html, try_inner_text,+ CallbackRegistration, Component, Error, InitComponentFn, InitFn, Result, get_callback,+ get_cookie_value, get_element, get_element_by_id, get_elements, get_value, init_callbacks,+ inner_html, inner_text, set_value, try_get_value, try_inner_html, try_inner_text, }; pub use gloo::console::error;