Skip to content

Commit

Permalink
feat: add lazy submit support for text input
Browse files Browse the repository at this point in the history
  • Loading branch information
HoKim98 committed Jul 20, 2024
1 parent 24ed554 commit c9af653
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 55 deletions.
76 changes: 59 additions & 17 deletions crates/cassette-core/src/cassette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RefCell<bool>>,
trigger: UseForceUpdateHandle,
}

Expand All @@ -167,15 +159,11 @@ impl RootCassetteState {
static SPEC: RefCell<crate::task::TaskSpec> = 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()
}
Expand Down Expand Up @@ -208,13 +196,13 @@ impl RootCassetteState {
})
}

fn set_handler<T>(&self, id: (String, String), value: T)
fn set_handler<T>(&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));
})
}
Expand Down Expand Up @@ -394,6 +382,60 @@ impl<T> GenericCassetteTaskHandle<T> for CassetteTaskHandle<T> {
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<T> CassetteTaskHandle<T> {
pub fn lazy(self) -> CassetteLazyHandle<T> {
CassetteLazyHandle(self)
}
}

#[cfg(feature = "ui")]
#[derive(Debug)]
pub struct CassetteLazyHandle<T>(CassetteTaskHandle<T>);

#[cfg(feature = "ui")]
impl<T> Clone for CassetteLazyHandle<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

#[cfg(feature = "ui")]
impl<T> IntoPropValue<T> for CassetteLazyHandle<T>
where
T: Clone,
{
fn into_prop_value(self) -> T {
self.get().clone()
}
}

#[cfg(feature = "ui")]
impl<T> GenericCassetteTaskHandle<T> for CassetteLazyHandle<T> {
type Ref<'a> = &'a T where T: 'a;

fn get<'a>(&'a self) -> <Self as GenericCassetteTaskHandle<T>>::Ref<'a>
where
<Self as GenericCassetteTaskHandle<T>>::Ref<'a>: ops::Deref<Target = T>,
{
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<T> CassetteLazyHandle<T> {
pub fn trigger(&self) {
self.0.root.update(true)
}
}
40 changes: 27 additions & 13 deletions crates/cassette/src/components/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ impl ComponentRenderer<Spec> 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| {
Expand All @@ -51,21 +53,33 @@ impl ComponentRenderer<Spec> 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! { <Content>{ label }</Content> });
let body = html! {
<>
{ label }
<TextInputGroup style="padding: 4px;">
<TextInputGroupMain style="margin-right: 4px;"
autofocus=true
{ onchange }
{ placeholder }
value={ text.clone() }
/>
<Button variant={ ButtonVariant::Primary }>{ label_submit }</Button>
</TextInputGroup>
</>
<TextInputGroup style="padding: 4px;">
<TextInputGroupMain style="margin-right: 4px;"
autofocus=true
{ onchange }
{ onkeydown }
{ placeholder }
value={ text.clone() }
/>
<Button { onclick } variant={ ButtonVariant::Primary }>{ label_submit }</Button>
</TextInputGroup>
};

if text.get().is_empty() {
Expand Down
51 changes: 26 additions & 25 deletions crates/cassette/src/pages/cassette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
<Alert inline=true title="Error" r#type={AlertType::Danger}>
{ error }
</Alert>
};
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! {
<Alert inline=true title="Error" r#type={AlertType::Danger}>
{ error }
</Alert>
};
contents.push(body);
break;
}
}
}
}
root_state.commit();

html! {
<super::PageBody {title} {subtitle} >
Expand Down

0 comments on commit c9af653

Please sign in to comment.