diff --git a/crates/cassette-core/src/cassette.rs b/crates/cassette-core/src/cassette.rs index 6c2ffc4..b3c405a 100644 --- a/crates/cassette-core/src/cassette.rs +++ b/crates/cassette-core/src/cassette.rs @@ -134,19 +134,11 @@ impl CassetteState { fn set(&mut self, name: &str, value: crate::task::TaskSpec) { self.root.set_child(name, value) } - - pub fn commit(self) { - let RootCassetteState { changed, trigger } = self.root; - if *changed.as_ref().borrow() { - trigger.force_update() - } - } } #[cfg(feature = "ui")] #[derive(Clone, Debug)] pub struct RootCassetteState { - changed: Rc>, trigger: UseForceUpdateHandle, } @@ -167,15 +159,11 @@ impl RootCassetteState { static SPEC: RefCell = Default::default(); } - fn new(trigger: UseForceUpdateHandle) -> Self { - Self { - changed: Default::default(), - trigger, - } + const fn new(trigger: UseForceUpdateHandle) -> Self { + Self { trigger } } fn update(&self, trigger: bool) { - *self.changed.borrow_mut() = true; if trigger { self.trigger.force_update() } @@ -208,13 +196,13 @@ impl RootCassetteState { }) } - fn set_handler(&self, id: (String, String), value: T) + fn set_handler(&self, id: (String, String), value: T, trigger: bool) where T: 'static, { Self::HANDLERS.with_borrow_mut(|handlers| { info!("Detected handler::update: {id:?}"); - self.update(true); + self.update(trigger); handlers.insert(id, Rc::new(value)); }) } @@ -394,6 +382,60 @@ impl GenericCassetteTaskHandle for CassetteTaskHandle { where T: 'static, { - RootCassetteState::set_handler(&self.root, self.id.clone(), value) + RootCassetteState::set_handler(&self.root, self.id.clone(), value, true) + } +} + +#[cfg(feature = "ui")] +impl CassetteTaskHandle { + pub fn lazy(self) -> CassetteLazyHandle { + CassetteLazyHandle(self) + } +} + +#[cfg(feature = "ui")] +#[derive(Debug)] +pub struct CassetteLazyHandle(CassetteTaskHandle); + +#[cfg(feature = "ui")] +impl Clone for CassetteLazyHandle { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +#[cfg(feature = "ui")] +impl IntoPropValue for CassetteLazyHandle +where + T: Clone, +{ + fn into_prop_value(self) -> T { + self.get().clone() + } +} + +#[cfg(feature = "ui")] +impl GenericCassetteTaskHandle for CassetteLazyHandle { + type Ref<'a> = &'a T where T: 'a; + + fn get<'a>(&'a self) -> >::Ref<'a> + where + >::Ref<'a>: ops::Deref, + { + self.0.get() + } + + fn set(&self, value: T) + where + T: 'static, + { + RootCassetteState::set_handler(&self.0.root, self.0.id.clone(), value, false) + } +} + +#[cfg(feature = "ui")] +impl CassetteLazyHandle { + pub fn trigger(&self) { + self.0.root.update(true) } } diff --git a/crates/cassette/src/components/text_input.rs b/crates/cassette/src/components/text_input.rs index 86a1f5c..fb450e8 100644 --- a/crates/cassette/src/components/text_input.rs +++ b/crates/cassette/src/components/text_input.rs @@ -42,7 +42,9 @@ impl ComponentRenderer for State { } = spec; let handler_name = "text"; - let text = ctx.use_state(handler_name, || default.unwrap_or_default()); + let text = ctx + .use_state(handler_name, || default.unwrap_or_default()) + .lazy(); let onchange = { let text = text.clone(); Callback::from(move |updated_text: String| { @@ -51,21 +53,33 @@ impl ComponentRenderer for State { } }) }; + let onkeydown = { + let text = text.clone(); + Callback::from(move |e: KeyboardEvent| { + const KEYCODE_ENTER: u32 = 13; + if e.key_code() == KEYCODE_ENTER { + e.prevent_default(); + text.trigger() + } + }) + }; + let onclick = { + let text = text.clone(); + Callback::from(move |_: MouseEvent| text.trigger()) + }; let label = label.map(|label| html! { { label } }); let body = html! { - <> - { label } - - - - - + + + + }; if text.get().is_empty() { diff --git a/crates/cassette/src/pages/cassette.rs b/crates/cassette/src/pages/cassette.rs index 45e97ae..d690a26 100644 --- a/crates/cassette/src/pages/cassette.rs +++ b/crates/cassette/src/pages/cassette.rs @@ -54,35 +54,36 @@ fn cassette_data(props: &DataProps) -> Html { let title = data.name.to_title_case(); let subtitle = data.description.clone(); - let trigger = use_force_update(); - let mut root_state = CassetteState::new(trigger); - let mut contents = vec![]; - for task in data.component.tasks.iter().map(RootCassetteTask) { - match task.render(&mut root_state) { - Ok(TaskState::Break { body, state: _ }) => { - contents.push(body); - break; - } - Ok(TaskState::Continue { body, state: _ }) => { - contents.push(body); - continue; - } - Ok(TaskState::Skip { state: _ }) => { - continue; - } - Err(error) => { - let body = html! { - - { error } - - }; - contents.push(body); - break; + { + let trigger = use_force_update(); + let mut root_state = CassetteState::new(trigger); + + for task in data.component.tasks.iter().map(RootCassetteTask) { + match task.render(&mut root_state) { + Ok(TaskState::Break { body, state: _ }) => { + contents.push(body); + break; + } + Ok(TaskState::Continue { body, state: _ }) => { + contents.push(body); + continue; + } + Ok(TaskState::Skip { state: _ }) => { + continue; + } + Err(error) => { + let body = html! { + + { error } + + }; + contents.push(body); + break; + } } } } - root_state.commit(); html! {