diff --git a/moon.mod.json b/moon.mod.json index 770ee92..3fd1d96 100644 --- a/moon.mod.json +++ b/moon.mod.json @@ -1,6 +1,6 @@ { "name": "tiye/respo", - "version": "0.0.11", + "version": "0.0.13", "deps": { "tiye/dom-ffi": "0.0.4", "tiye/cirru-parser": "0.0.7" diff --git a/src/lib/app.mbt b/src/lib/app.mbt index 9ce2f45..bdf672d 100644 --- a/src/lib/app.mbt +++ b/src/lib/app.mbt @@ -1,28 +1,73 @@ -// pub trait RespoApp { -// // TODO better use RespoAction instead of Show -// dispatch(Self, (Show) -> UInt) -> Unit!@node.RespoCommonError -// pick_storage_key(Self) -> String -// get_mount_target(Self) -> @dom_ffi.Node -// get_store(Self) -> @json.FromJson -// // get_loop_delay() -> UInt? -// // view(Self) -> @node.RespoNode[T]!@node.RespoCommonError -// // render_loop(Self) -> Unit!@node.RespoCommonError -// backup_model_beforeunload(Self) -> Unit!@node.RespoCommonError -// try_load_storage(Self) -> Unit!@node.RespoCommonError -// } +/// get basic App structure +pub struct RespoApp[Model] { + store : Ref[Model] + storage_key : String + mount_target : @dom_ffi.Node +} -// impl RespoApp with try_load_storage(self) { -// let window = @dom_ffi.window() -// let storage = window.local_storage() -// let key = self.pick_storage_key() -// match storage.get_item(key) { -// Some(s) => { -// let store = self.get_store() -// store.val = try_from_string!(s) -// } -// None => @dom_ffi.log("no storage") -// } -// } +/// backup store to local storage before unload + +pub fn backup_model_beforeunload[Model : ToJson]( + self : RespoApp[Model] +) -> Unit { + let window = @dom_ffi.window() + let storage = window.local_storage() + let p = self.storage_key + let store = self.store.val + let beforeunload = fn(_e : @dom_ffi.BeforeUnloadEvent) { + let content = store.to_json().stringify() + // util::log!("before unload {} {}", p, content); + storage.set_item(p, content) + } + window.set_onbeforeunload(beforeunload) +} + +pub fn try_load_storage[Model : @json.FromJson + Default]( + key : String +) -> Model { + let window = @dom_ffi.window() + let storage = window.local_storage() + match storage.get_item(key) { + Some(s) => + match @json.parse?(s) { + Ok(j) => + match @json.from_json?(j) { + Ok(s) => s + Err(e) => { + @dom_ffi.log("failed to parse storage: \{e}") + Model::default() + } + } + Err(e) => { + @dom_ffi.log("failed to parse storage: \{e}") + Model::default() + } + } + None => { + @dom_ffi.log("no storage") + Model::default() + } + } +} + +pub fn RespoApp::render_loop[Model, ActionOp]( + self : RespoApp[Model], + renderer : () -> @node.RespoNode[ActionOp]!@node.RespoCommonError, + dispatch_action : (ActionOp) -> Unit!@node.RespoCommonError +) -> Unit { + let mount_target = self.mount_target + let ret = render_node?( + mount_target, + self.store, + renderer, + dispatch_action, + Some(100), + ) + match ret { + Ok(_) => () + Err(e) => @dom_ffi.error_log(e.to_string()) + } +} // pub trait RespoStore: ToJson + @json.FromJson { // // TODO better use RespoAction instead of Show diff --git a/src/lib/dialog/alert.mbt b/src/lib/dialog/alert.mbt index b12bf12..35957dc 100644 --- a/src/lib/dialog/alert.mbt +++ b/src/lib/dialog/alert.mbt @@ -27,11 +27,11 @@ fn comp_alert_modal[T]( effect_focus_data as @node.RespoEffect, effect_modal_fade_data as @node.RespoEffect, ], - @node.div( - style=@node.RespoStyle::new(position=Absolute), + div( + style=respo_style(position=Absolute), [ if show { - @node.div( + div( class_name=@node.str_spaced( [@respo.ui_fullscreen, @respo.ui_center, css_backdrop], ), @@ -47,11 +47,11 @@ fn comp_alert_modal[T]( }, ), [ - @node.div( + div( class_name=@node.str_spaced( - [@respo.column, @respo.ui_global, css_modal_card], + [@respo.ui_column, ui_global, css_modal_card], ), - style=@node.RespoStyle::new(line_height=Px(32.0)).merge( + style=respo_style(line_height=Px(32.0)).merge( options.card_style, ), event={}..set( @@ -65,18 +65,18 @@ fn comp_alert_modal[T]( }, ), [ - @node.div( + div( [ - @node.span(inner_text=options.text.or("Alert!"), []), - @node.space(height=8), - @node.div( + span(inner_text=options.text.or("Alert!"), []), + space(height=8), + div( class_name=@respo.ui_row_parted, [ - @node.span([]), - @node.button( + span([]), + button( inner_text=options.button_text.or("Read"), - class_name=@node.str_spaced( - [@respo.ui_button, css_button, button_name], + class_name=str_spaced( + [ui_button, css_button, button_name], ), event={}..set( Click, @@ -96,10 +96,7 @@ fn comp_alert_modal[T]( ], ) } else { - @node.span( - attrs=@node.respo_attrs()..set("data-name", "placeholder"), - [], - ) + span(attrs=respo_attrs()..set("data-name", "placeholder"), []) }, ], ), diff --git a/src/lib/dialog/confirm.mbt b/src/lib/dialog/confirm.mbt index 5c5187f..b6acc2f 100644 --- a/src/lib/dialog/confirm.mbt +++ b/src/lib/dialog/confirm.mbt @@ -30,11 +30,11 @@ fn comp_confirm_modal[T]( let effect_modal_fade_data : EffectModalFade = { show, } @node.RespoComponent::named( "confirm-modal", - @node.div( - style=@node.RespoStyle::new(position=Absolute), + div( + style=respo_style(position=Absolute), [ if show { - @node.div( + div( class_name=@node.str_spaced( [@respo.ui_fullscreen, @respo.ui_center, css_backdrop], ), @@ -52,9 +52,9 @@ fn comp_confirm_modal[T]( }, ), [ - @node.div( + div( class_name=@node.str_spaced( - [@respo.column, @respo.ui_global, css_modal_card], + [@respo.ui_column, @respo.ui_global, css_modal_card], ), style=options.card_style, event={}..set( @@ -69,20 +69,20 @@ fn comp_confirm_modal[T]( }, ), [ - @node.div( + div( [ - @node.span( + span( inner_text=options.text.or("Need confirmation..."), [], ), - @node.space(height=8), - @node.div( + space(height=8), + div( class_name=@respo.ui_row_parted, [ - @node.span([]), - @node.button( + span([]), + button( inner_text=options.button_text.or("Confirm"), - class_name=@node.str_spaced( + class_name=str_spaced( [@respo.ui_button, css_button, button_name], ), event={}..set( @@ -103,7 +103,7 @@ fn comp_confirm_modal[T]( ], ) } else { - @node.span( + span( // attrs=@node.respo_attrs(data_name="placeholder"), [], ) diff --git a/src/lib/dialog/dialogs.mbt b/src/lib/dialog/dialogs.mbt index 0c999f4..6a3d9e8 100644 --- a/src/lib/dialog/dialogs.mbt +++ b/src/lib/dialog/dialogs.mbt @@ -159,9 +159,9 @@ pub fn comp_esc_listener[T]( @node.RespoComponent::named( "esc-listener", effects=[effect_modal_close_data as @node.RespoEffect], - @node.input( + input( value="", - style=@node.RespoStyle::new(display=None), + style=respo_style(display=None), event={}..set( Keydown, fn(e, dispatch) { @@ -175,11 +175,11 @@ pub fn comp_esc_listener[T]( ).to_node() } -let css_backdrop : String = @node.declare_static_style( +let css_backdrop : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( background_color=Hsla(0, 30, 10, 0.6), position=Fixed, z_index=999, @@ -188,11 +188,11 @@ let css_backdrop : String = @node.declare_static_style( ], ) -let css_modal_card : String = @node.declare_static_style( +let css_modal_card : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( background_color=Hsl(0, 0, 100), max_width=Px(600), width=Percent(100), @@ -207,11 +207,11 @@ let css_modal_card : String = @node.declare_static_style( ], ) -let css_drawer_card : String = @node.declare_static_style( +let css_drawer_card : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( background_color=Hsl(0, 0, 100), max_width=Vw(50), width=Px(400), @@ -229,11 +229,11 @@ let css_drawer_card : String = @node.declare_static_style( ], ) -let css_button : String = @node.declare_static_style( +let css_button : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( border_radius=4.0, background_color=White, border_color=Hsl(0, 0, 0), diff --git a/src/lib/dialog/drawer.mbt b/src/lib/dialog/drawer.mbt index 8a0465f..ffb8661 100644 --- a/src/lib/dialog/drawer.mbt +++ b/src/lib/dialog/drawer.mbt @@ -16,11 +16,11 @@ type DrawerRenderer[T] ((@node.DispatchFn[T]) -> Unit!@node.RespoCommonError) -> ]!@node.RespoCommonError fn to_string[T](self : DrawerRenderer[T]) -> String { - "(DrawerRenderer)" + "(DrawerRenderer \{self})" } pub fn DrawerRenderer::default[T]() -> DrawerRenderer[T] { - fn(_close) { @node.div([]) } + fn(_close) { div([]) } } pub fn DrawerRenderer::new[T]( @@ -53,13 +53,13 @@ fn comp_drawer[T]( effect_focus_data as @node.RespoEffect, effect_drawer_fade_data as @node.RespoEffect, ], - @node.div( - style=@node.RespoStyle::new(position=Absolute), + div( + style=respo_style(position=Absolute), [ if show { - @node.div( + div( class_name=@node.str_spaced( - [@respo.ui_fullscreen, @respo.ui_center, css_backdrop], + [ui_fullscreen, ui_center, css_backdrop], ), style=options.backdrop_style, event={}..set( @@ -74,13 +74,11 @@ fn comp_drawer[T]( }, ), [ - @node.div( + div( class_name=@node.str_spaced( - [@respo.column, @respo.ui_global, css_drawer_card], - ), - style=@node.RespoStyle::new(padding=Px(0)).merge( - options.card_style, + [ui_column, ui_global, css_drawer_card], ), + style=respo_style(padding=Px(0)).merge(options.card_style), event={}..set( Click, fn(e, _dispatch) { @@ -92,14 +90,14 @@ fn comp_drawer[T]( }, ), [ - @node.div( - class_name=@respo.column, + div( + class_name=@respo.ui_column, [ - @node.div( - class_name=@respo.ui_center, - [@node.span(inner_text=options.title.or("Drawer"), [])], + div( + class_name=ui_center, + [span(inner_text=options.title.or("Drawer"), [])], ), - @node.space(height=8), + space(height=8), { let f = options.render f.run!(fn(dispatch) { close(dispatch) }) @@ -112,10 +110,7 @@ fn comp_drawer[T]( ], ) } else { - @node.span( - attrs=@node.respo_attrs()..set("data-name", "placeholder"), - [], - ) + span(attrs=respo_attrs()..set("data-name", "placeholder"), []) }, ], ), diff --git a/src/lib/dialog/modal.mbt b/src/lib/dialog/modal.mbt index 5a2b148..d32454c 100644 --- a/src/lib/dialog/modal.mbt +++ b/src/lib/dialog/modal.mbt @@ -16,7 +16,7 @@ pub type ModalRenderer[T] ((@node.DispatchFn[T]) -> Unit!@node.RespoCommonError) ]!@node.RespoCommonError pub fn ModalRenderer::default[T]() -> ModalRenderer[T] { - fn(_close) { @node.div([]) } + fn(_close) { div([]) } } pub fn ModalRenderer::new[T]( @@ -48,12 +48,12 @@ fn comp_modal[T]( @node.RespoComponent::named( "modal", effects=[effect_modal_fade_data as @node.RespoEffect], - @node.div( - style=@node.RespoStyle::new(position=Absolute), + div( + style=respo_style(position=Absolute), [ if show { - @node.div( - class_name=@node.str_spaced( + div( + class_name=str_spaced( [@respo.ui_fullscreen, @respo.ui_center, css_backdrop], ), style=options.backdrop_style, @@ -69,11 +69,9 @@ fn comp_modal[T]( }, ), [ - @node.div( - class_name=@node.str_spaced( - [@respo.column, @respo.ui_global, css_modal_card], - ), - style=@node.RespoStyle::new(padding=Px(0), line_height=Px(32.0)).merge( + div( + class_name=str_spaced([ui_column, ui_global, css_modal_card]), + style=respo_style(padding=Px(0), line_height=Px(32.0)).merge( options.card_style, ), event={}..set( @@ -87,12 +85,12 @@ fn comp_modal[T]( }, ), [ - @node.div( - class_name=@respo.column, + div( + class_name=ui_column, [ - @node.div( - class_name=@respo.ui_center, - [@node.span(inner_text=options.title.or("Modal"), [])], + div( + class_name=ui_center, + [span(inner_text=options.title.or("Modal"), [])], ), @node.space(height=8), { @@ -107,10 +105,7 @@ fn comp_modal[T]( ], ) } else { - @node.span( - attrs=@node.respo_attrs()..set("data-name", "placeholder"), - [], - ) + span(attrs=respo_attrs()..set("data-name", "placeholder"), []) }, ], ), diff --git a/src/lib/dialog/moon.pkg.json b/src/lib/dialog/moon.pkg.json index 140066b..42366d3 100644 --- a/src/lib/dialog/moon.pkg.json +++ b/src/lib/dialog/moon.pkg.json @@ -1,7 +1,35 @@ { "import": [ - { "path": "tiye/respo/lib/node", "alias": "node" }, - { "path": "tiye/respo/lib", "alias": "respo" }, + { + "path": "tiye/respo/lib/node", + "alias": "node", + "value": [ + "div", + "space", + "str_spaced", + "span", + "button", + "respo_attrs", + "respo_style", + "input", + "declare_static_style" + ] + }, + { + "path": "tiye/respo/lib", + "alias": "respo", + "value": [ + "ui_button", + "ui_input", + "ui_global", + "ui_row", + "ui_column", + "ui_center", + "ui_fullscreen", + "ui_textarea", + "ui_row_parted" + ] + }, { "path": "tiye/dom-ffi/lib", "alias": "dom_ffi" } ] } diff --git a/src/lib/dialog/prompt.mbt b/src/lib/dialog/prompt.mbt index 8ef0d80..398471c 100644 --- a/src/lib/dialog/prompt.mbt +++ b/src/lib/dialog/prompt.mbt @@ -27,7 +27,7 @@ pub struct PromptOptions { pub type PromptValidator (String) -> Result[Unit, String] pub fn to_string(self : PromptValidator) -> String { - "(PromptValidator)" + "(PromptValidator \{self})" } impl Show for PromptValidator with output(self, logger) { @@ -73,10 +73,10 @@ fn comp_prompt_modal[T : @node.RespoAction]( // @dom_ffi.log("Prompt state: " + state.to_json().stringify(indent=2)) let submit = on_submit let close = on_close - @dom_ffi.warn_log( - "recreate prompt modal with states: " + - states.data.to_json().stringify(indent=2), - ) + // @dom_ffi.warn_log( + // "recreate prompt modal with states: " + + // states.data.to_json().stringify(indent=2), + // ) let on_text_input = fn( e : @node.RespoEvent, dispatch : @node.DispatchFn[T] @@ -133,11 +133,11 @@ fn comp_prompt_modal[T : @node.RespoAction]( // (effect_focus_data as @node.RespoEffect).turn_effect(), effect_modal_fade_data as @node.RespoEffect, ], - @node.div( - style=@node.RespoStyle::new(position=Absolute), + div( + style=respo_style(position=Absolute), [ if show { - @node.div( + div( class_name=@node.str_spaced( [@respo.ui_fullscreen, @respo.ui_center, css_backdrop], ), @@ -155,11 +155,11 @@ fn comp_prompt_modal[T : @node.RespoAction]( }, ), [ - @node.div( + div( class_name=@node.str_spaced( - [@respo.column, @respo.ui_global, css_modal_card], + [@respo.ui_column, @respo.ui_global, css_modal_card], ), - style=@node.RespoStyle::new(line_height=Px(32.0)) + style=respo_style(line_height=Px(32.0)) .merge(options.card_style) .merge(options.input_style), event={}..set( @@ -173,50 +173,47 @@ fn comp_prompt_modal[T : @node.RespoAction]( }, ), [ - @node.div( + div( [ - @node.span( - inner_text=options.text.or("Input your text:"), - [], - ), - @node.space(height=8), - @node.div( + span(inner_text=options.text.or("Input your text:"), []), + space(height=8), + div( [ if options.multilines { - @node.input( + input( value=state.draft, placeholder="Content...", - class_name=@respo.ui_textarea, - style=@node.RespoStyle::new(width=Percent(100)), + class_name=ui_textarea, + style=respo_style(width=Percent(100)), event={}..set(Input, on_text_input), ) } else { - @node.input( + input( value=state.draft, placeholder="Content...", class_name=@respo.ui_input, - style=@node.RespoStyle::new(width=Percent(100)), + style=respo_style(width=Percent(100)), event={}..set(Input, on_text_input), ) }, match state.error { Some(message) => - @node.div( + div( class_name=css_error, - attrs=@node.respo_attrs(inner_text=message), + attrs=respo_attrs(inner_text=message), [], ) - None => @node.span([]) + None => span([]) }, - @node.space(height=8), - @node.div( - class_name=@respo.ui_row_parted, + space(height=8), + div( + class_name=ui_row_parted, [ - @node.span([]), - @node.button( + span([]), + button( inner_text=options.button_text.or("Submit"), - class_name=@node.str_spaced( - [@respo.ui_button, css_button, button_name], + class_name=str_spaced( + [ui_button, css_button, button_name], ), event={}..set( Click, @@ -237,10 +234,7 @@ fn comp_prompt_modal[T : @node.RespoAction]( ], ) } else { - @node.span( - attrs=@node.respo_attrs()..set("data-name", "placeholder"), - [], - ) + span(attrs=@node.respo_attrs()..set("data-name", "placeholder"), []) }, ], ), @@ -335,6 +329,4 @@ pub fn PromptPlugin::new[T : @node.RespoAction]( { states, state, options, text: None, cursor, on_submit } } -let css_error : String = @node.declare_static_style( - [("&", @node.RespoStyle::new(color=Red))], -) +let css_error : String = declare_static_style([("&", respo_style(color=Red))]) diff --git a/src/lib/moon.pkg.json b/src/lib/moon.pkg.json index 969d88e..ebc6bcf 100644 --- a/src/lib/moon.pkg.json +++ b/src/lib/moon.pkg.json @@ -1,6 +1,10 @@ { "import": [ - "tiye/respo/lib/node", + { + "path": "tiye/respo/lib/node", + "alias": "node", + "value": ["respo_style", "declare_static_style"] + }, { "path": "tiye/dom-ffi/lib", "alias": "dom_ffi" } ] } diff --git a/src/lib/node/alias.mbt b/src/lib/node/alias.mbt index 5a54639..42aab42 100644 --- a/src/lib/node/alias.mbt +++ b/src/lib/node/alias.mbt @@ -9,7 +9,7 @@ pub fn div[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new(), + ~style : RespoStyle = respo_style(), children : Array[RespoNode[T]] ) -> RespoNode[T] { match class_name { @@ -37,7 +37,7 @@ pub fn div_listed[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new(), + ~style : RespoStyle = respo_style(), children : Array[(RespoIndexKey, RespoNode[T])] ) -> RespoNode[T] { match class_name { @@ -55,7 +55,7 @@ pub fn span[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new(), + ~style : RespoStyle = respo_style(), children : Array[RespoNode[T]] ) -> RespoNode[T] { match class_name { @@ -87,7 +87,7 @@ pub fn button[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { match class_name { Some(class_name) => attrs.set("class", class_name) @@ -112,7 +112,7 @@ pub fn input[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { match class_name { Some(class_name) => attrs.set("class", class_name) @@ -181,7 +181,7 @@ pub fn textarea[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { match class_name { Some(class_name) => attrs.set("class", class_name) @@ -267,7 +267,7 @@ pub fn a[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { attrs.set("href", href) attrs.set("innerText", inner_text) @@ -296,7 +296,7 @@ pub fn br[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { Element({ name: "br", attrs, event, style, children: [] }) } @@ -316,7 +316,7 @@ pub fn iframe[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { attrs.set("src", src) match height { @@ -349,7 +349,7 @@ pub fn canvas[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { match height { Some(height) => attrs.set("height", height.to_string()) @@ -370,7 +370,7 @@ pub fn code[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { match class_name { Some(class_name) => attrs.set("class", class_name) @@ -388,7 +388,7 @@ pub fn pre[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { match class_name { Some(class_name) => attrs.set("class", class_name) @@ -402,7 +402,7 @@ pub fn p[T]( ~class_name? : String, ~inner_text : String, ~attrs : Map[String, String] = {}, - ~style : RespoStyle = RespoStyle::new(), + ~style : RespoStyle = respo_style(), children : Array[RespoNode[T]] ) -> RespoNode[T] { match class_name { @@ -432,7 +432,7 @@ pub fn img[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { attrs.set("alt", alt) attrs.set("src", src) @@ -475,7 +475,7 @@ pub fn video[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new() + ~style : RespoStyle = respo_style() ) -> RespoNode[T] { attrs.set("src", src) match height { @@ -518,7 +518,7 @@ pub fn space[T]( None => () } div( - style=RespoStyle::new( + style=respo_style( width=Px(width), height=Px(height), display=if height > 1 { Block } else { InlineBlock }, @@ -536,7 +536,7 @@ pub fn create_element[T]( RespoEventType, (RespoEvent, DispatchFn[T]) -> Unit!RespoCommonError, ] = {}, - ~style : RespoStyle = RespoStyle::new(), + ~style : RespoStyle = respo_style(), children : Array[RespoNode[T]] ) -> RespoNode[T] { Element( diff --git a/src/lib/node/css.mbt b/src/lib/node/css.mbt index 6bb4008..5300f5b 100644 --- a/src/lib/node/css.mbt +++ b/src/lib/node/css.mbt @@ -37,7 +37,7 @@ fn length(self : RespoStyle) -> Int { } /// Create a new RespoStyle object -pub fn RespoStyle::new( +pub fn respo_style( // Basic Styling ~color? : CssColor, ~background_color? : CssColor, diff --git a/src/lib/node/dom-change.mbt b/src/lib/node/dom-change.mbt index 75c7f0c..a3025f2 100644 --- a/src/lib/node/dom-change.mbt +++ b/src/lib/node/dom-change.mbt @@ -131,7 +131,7 @@ pub fn to_cirru[T](self : DomChange[T]) -> @cirru.Cirru { effect_type.to_cirru(), coord_path_to_cirru(coord), dom_path_to_cirru(dom_path), - // skip_indexes.to_cirru(), // TODO + indexes_to_cirru(skip_indexes), ], ) ReplaceElement(~coord, ~dom_path, ~node) => @@ -279,7 +279,7 @@ pub fn to_cirru[T](self : ChildDomOp[T]) -> @cirru.Cirru { coord_path_to_cirru(nested_coord), dom_path_to_cirru(nested_dom_path), effect_type.to_cirru(), - // skip_indexes.to_cirru(), // TODO + indexes_to_cirru(skip_indexes), ], ) } @@ -296,7 +296,7 @@ pub fn get_dom_path[T](self : DomChange[T]) -> Array[UInt] { } } -pub fn to_cirru(self : RespoCoord) -> @cirru.Cirru { +pub fn RespoCoord::to_cirru(self : RespoCoord) -> @cirru.Cirru { match self { Key(key) => Leaf(key.to_string()) Comp(comp) => List([@cirru.Leaf("::Comp"), @cirru.Leaf(comp)]) @@ -312,3 +312,12 @@ pub fn dom_path_to_cirru(dom_path : Array[UInt]) -> @cirru.Cirru { let items = dom_path.map(fn(x) { @cirru.Leaf(x.to_string()) }) @cirru.List(items) } + +// turn @hashset.T[Int] to Cirru +pub fn indexes_to_cirru(xs : @hashset.T[Int]) -> @cirru.Cirru { + let items = [] + for x in xs { + items.push(@cirru.Leaf(x.to_string())) + } + @cirru.List(items) +} diff --git a/src/lib/renderer.mbt b/src/lib/renderer.mbt index aa2ffa2..d973182 100644 --- a/src/lib/renderer.mbt +++ b/src/lib/renderer.mbt @@ -23,12 +23,12 @@ pub fn mark_need_rerender() -> Unit { pub fn render_node[T, U]( mount_target : @dom_ffi.Node, // TODO it copies the whole store, need to optimize - get_store : () -> U, + store : Ref[U], renderer : () -> @node.RespoNode[T]!@node.RespoCommonError, dispatch_action : (T) -> Unit!@node.RespoCommonError, _interval : Float? ) -> Unit!@node.RespoCommonError { - let prev_store = @ref.new(get_store()) + let prev_store = @ref.new(store.val) let tree0 : @node.RespoNode[T] = renderer!() let prev_tree = @ref.new(tree0) let handle_event = fn( @@ -65,7 +65,7 @@ pub fn render_node[T, U]( @node.raf_loop( fn(_t : Float) -> Unit!@node.RespoCommonError { if drain_rerender_status() { - prev_store.val = get_store() + prev_store.val = store.val let new_tree = renderer!() let changes : Ref[Array[@node.DomChange[T]]] = Ref::new([]) @node.diff_tree!(new_tree, prev_tree.val, [], [], changes) diff --git a/src/lib/ui.mbt b/src/lib/ui.mbt index 846f4ca..053dce4 100644 --- a/src/lib/ui.mbt +++ b/src/lib/ui.mbt @@ -19,26 +19,26 @@ pub let fancy_fonts : String = "Josefin Sans, Helvetica neue, Arial, sans-serif" pub let normal_fonts : String = "Hind, Helvatica, Arial, sans-serif" -pub let ui_global : String = @node.declare_static_style( +pub let ui_global : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( font_family=default_fonts, line_height=Em(2), font_size=14, color=Hsl(0, 0, 20), ), ), - ("& *", @node.RespoStyle::new(box_sizing=BorderBox)), + ("& *", respo_style(box_sizing=BorderBox)), ], ) -pub let ui_input : String = @node.declare_static_style( +pub let ui_input : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( border=@node.CssBorder::new(color=Hsl(0, 0, 80)), border_radius=4, padding_top=Px(4), @@ -53,7 +53,7 @@ pub let ui_input : String = @node.declare_static_style( ), ( "&:focus", - @node.RespoStyle::new( + respo_style( border=@node.CssBorder::new( width=1, style=Solid, @@ -65,11 +65,11 @@ pub let ui_input : String = @node.declare_static_style( ], ) -pub let ui_button : String = @node.declare_static_style( +pub let ui_button : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( background_color=White, border_style=Solid, border_width=Px(1), @@ -83,19 +83,16 @@ pub let ui_button : String = @node.declare_static_style( text_align=Center, ), ), - ("&:hover", @node.RespoStyle::new(background_color=Hsl(0, 0, 98))), - ( - "&:active", - @node.RespoStyle::new(transform=Scale(1.02), transition_duration=Ms(0)), - ), + ("&:hover", respo_style(background_color=Hsl(0, 0, 98))), + ("&:active", respo_style(transform=Scale(1.02), transition_duration=Ms(0))), ], ) -pub let ui_button_primary : String = @node.declare_static_style( +pub let ui_button_primary : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( outline=None, color=White, background_color=Hsl(220, 80, 60), @@ -113,10 +110,10 @@ pub let ui_button_primary : String = @node.declare_static_style( text_align=Center, ), ), - ("&:hover", @node.RespoStyle::new(background_color=Hsl(220, 80, 64))), + ("&:hover", respo_style(background_color=Hsl(220, 80, 64))), ( "&:active", - @node.RespoStyle::new( + respo_style( transform=Scale(1.02), background_color=Hsl(220, 80, 68), transition_duration=Ms(0), @@ -125,11 +122,11 @@ pub let ui_button_primary : String = @node.declare_static_style( ], ) -pub let ui_button_danger : String = @node.declare_static_style( +pub let ui_button_danger : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( outline=None, color=White, background_color=Hsl(6, 100, 60), @@ -143,10 +140,10 @@ pub let ui_button_danger : String = @node.declare_static_style( text_align=Center, ), ), - ("&:hover", @node.RespoStyle::new(background_color=Hsl(6, 100, 64))), + ("&:hover", respo_style(background_color=Hsl(6, 100, 64))), ( "&:active", - @node.RespoStyle::new( + respo_style( transform=Scale(1.02), background_color=Hsl(6, 100, 68), transition_duration=Ms(0), @@ -157,11 +154,11 @@ pub let ui_button_danger : String = @node.declare_static_style( /// layout items in column and center them with flexbox /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_center : String = @node.declare_static_style( +pub let ui_center : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Column, justify_content=Center, @@ -173,26 +170,17 @@ pub let ui_center : String = @node.declare_static_style( /// layout items in column and center them with flexbox /// demos https://ui.respo-mvc.org/#/layouts.html -pub let column : String = @node.declare_static_style( - [ - ( - "&", - @node.RespoStyle::new( - display=Flex, - flex_direction=Column, - align_items=Stretch, - ), - ), - ], +pub let ui_column : String = declare_static_style( + [("&", respo_style(display=Flex, flex_direction=Column, align_items=Stretch))], ) /// layout items in column with flexbox, space around /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_column_dispersive : String = @node.declare_static_style( +pub let ui_column_dispersive : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Column, justify_content=SpaceAround, @@ -204,11 +192,11 @@ pub let ui_column_dispersive : String = @node.declare_static_style( /// layout items in column with flexbox, space evenly /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_column_evenly : String = @node.declare_static_style( +pub let ui_column_evenly : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Column, justify_content=SpaceEvenly, @@ -220,11 +208,11 @@ pub let ui_column_evenly : String = @node.declare_static_style( /// layout items in column with flexbox, space between /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_column_parted : String = @node.declare_static_style( +pub let ui_column_parted : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Column, justify_content=SpaceBetween, @@ -235,16 +223,14 @@ pub let ui_column_parted : String = @node.declare_static_style( ) /// expand item with flex:1 -pub let ui_expand : String = @node.declare_static_style( - [("&", @node.RespoStyle::new(flex=1))], -) +pub let ui_expand : String = declare_static_style([("&", respo_style(flex=1))]) /// full page with absolute position -pub let ui_fullscreen : String = @node.declare_static_style( +pub let ui_fullscreen : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( position=Absolute, left=Px(0), top=Px(0), @@ -257,27 +243,27 @@ pub let ui_fullscreen : String = @node.declare_static_style( ) /// monospace font for code, Source Code Pro, Menlo, Ubuntu Mono, Consolas -pub let ui_font_code : String = @node.declare_static_style( - [("&", @node.RespoStyle::new(font_family=code_fonts))], +pub let ui_font_code : String = declare_static_style( + [("&", respo_style(font_family=code_fonts))], ) /// fancy font for title, Josefin Sans, Helvetica neue, Arial /// refers to https://fonts.google.com/specimen/Josefin+Sans or https://cdn.tiye.me/favored-fonts/main-fonts.css -pub let ui_font_fancy : String = @node.declare_static_style( - [("&", @node.RespoStyle::new(font_family=fancy_fonts))], +pub let ui_font_fancy : String = declare_static_style( + [("&", respo_style(font_family=fancy_fonts))], ) /// normal font for text, Hind, Helvatica, Arial /// refers to https://fonts.google.com/specimen/Hind or https://cdn.tiye.me/favored-fonts/main-fonts.css -pub let ui_font_normal : String = @node.declare_static_style( - [("&", @node.RespoStyle::new(font_family=normal_fonts))], +pub let ui_font_normal : String = declare_static_style( + [("&", respo_style(font_family=normal_fonts))], ) -pub let ui_textarea : String = @node.declare_static_style( +pub let ui_textarea : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( outline=None, font_size=14, font_family=default_fonts, @@ -291,11 +277,11 @@ pub let ui_textarea : String = @node.declare_static_style( ], ) -pub let ui_link : String = @node.declare_static_style( +pub let ui_link : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( text_decoration=Underline, user_select=None, height=Px(24), @@ -311,26 +297,17 @@ pub let ui_link : String = @node.declare_static_style( /// layout items in row with flexbox, items are stretched /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_row : String = @node.declare_static_style( - [ - ( - "&", - @node.RespoStyle::new( - display=Flex, - flex_direction=Row, - align_items=Stretch, - ), - ), - ], +pub let ui_row : String = declare_static_style( + [("&", respo_style(display=Flex, flex_direction=Row, align_items=Stretch))], ) /// layout items in row with flexbox, center them /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_row_center : String = @node.declare_static_style( +pub let ui_row_center : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Row, justify_content=Center, @@ -342,11 +319,11 @@ pub let ui_row_center : String = @node.declare_static_style( /// layout items in row with flexbox, space around /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_row_dispersive : String = @node.declare_static_style( +pub let ui_row_dispersive : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Row, justify_content=SpaceAround, @@ -358,11 +335,11 @@ pub let ui_row_dispersive : String = @node.declare_static_style( /// layout items in row with flexbox, space evenly /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_row_evenly : String = @node.declare_static_style( +pub let ui_row_evenly : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Row, justify_content=SpaceEvenly, @@ -374,11 +351,11 @@ pub let ui_row_evenly : String = @node.declare_static_style( /// layout items in row with flexbox, space between /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_row_middle : String = @node.declare_static_style( +pub let ui_row_middle : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Row, justify_content=FlexStart, @@ -390,11 +367,11 @@ pub let ui_row_middle : String = @node.declare_static_style( /// layout items in row with flexbox, space between /// demos https://ui.respo-mvc.org/#/layouts.html -pub let ui_row_parted : String = @node.declare_static_style( +pub let ui_row_parted : String = declare_static_style( [ ( "&", - @node.RespoStyle::new( + respo_style( display=Flex, flex_direction=Row, justify_content=SpaceBetween, diff --git a/src/main/counter.mbt b/src/main/counter.mbt index 9a3aec0..33f786c 100644 --- a/src/main/counter.mbt +++ b/src/main/counter.mbt @@ -46,19 +46,19 @@ fn comp_counter( @respo_node.button( inner_text="demo inc", class_name=@respo.ui_button, - style=@respo_node.RespoStyle::new(margin=Px(4)), + style=respo_style(margin=Px(4)), event={}..set(Click, on_inc), ), @respo_node.button( inner_text="demo dec", class_name=@respo.ui_button, - style=@respo_node.RespoStyle::new(margin=Px(4)), + style=respo_style(margin=Px(4)), event={}..set(Click, on_dec), ), @respo_node.button( inner_text="demo inc twice", class_name=@respo.ui_button, - style=@respo_node.RespoStyle::new(margin=Px(4)), + style=respo_style(margin=Px(4)), event={}..set(Click, on_inc_twice), ), ], @@ -67,7 +67,7 @@ fn comp_counter( [ @respo_node.span( inner_text="value is: " + counted.to_string(), - style=@respo_node.RespoStyle::new( + style=respo_style( color=Hsluv(270, 100, 40), font_family="Menlo", font_size=counted.reinterpret_as_uint() + 10, diff --git a/src/main/main.mbt b/src/main/main.mbt index a0182a7..204741f 100644 --- a/src/main/main.mbt +++ b/src/main/main.mbt @@ -1,36 +1,16 @@ let app_store_key : String = "demo_respo_store" -struct App { - store : Ref[Store] - mount_target : @dom_ffi.Node -} - -fn get_store(self : App) -> Ref[Store] { - self.store -} - -fn get_mount_target(self : App) -> @dom_ffi.Node { - self.mount_target -} - -fn dispatch(self : App, op : ActionOp) -> Unit { - // TODO intent - @dom_ffi.log("Action: " + op.to_string()) - self.store.val.update(op) -} - fn view( - self : App + store : Store ) -> @respo_node.RespoNode[ActionOp]!@respo_node.RespoCommonError { - let store = self.store.val // @dom_ffi.log("Store to render: " + store.to_json().stringify(indent=2)) let states = store.get_states() - @respo_node.div( + div( class_name=@respo.ui_global, - style=@respo_node.RespoStyle::new(padding=Px(12)), + style=respo_style(padding=Px(12)), [ comp_counter(states.pick("counter"), store.counted), - @respo_node.space(height=80), + space(height=80), comp_panel(states.pick("panel")), comp_todolist(states.pick("todolist"), store.tasks), comp_plugins_demo!(states.pick("plugins-demo")), @@ -38,92 +18,28 @@ fn view( ) } -fn pick_storage_key() -> String { - app_store_key -} - -fn try_load_storage(self : App) -> Unit!@respo_node.RespoCommonError { - let window = @dom_ffi.window() - let storage = window.local_storage() - let key = pick_storage_key() - match storage.get_item(key) { - Some(s) => { - let store = self.get_store() - store.val = try_from_string!(s) - } - None => @dom_ffi.log("no storage") - } -} - -/// backup store to local storage before unload - -fn backup_model_beforeunload(self : App) -> Unit { - let window = @dom_ffi.window() - let storage = window.local_storage() - let p = pick_storage_key() - let store = self.get_store().val - let beforeunload = fn(_e : @dom_ffi.BeforeUnloadEvent) { - let content = store.to_string() - // util::log!("before unload {} {}", p, content); - storage.set_item(p, content) - } - window.set_onbeforeunload(beforeunload) -} - fn main { - let app : App = { - mount_target: @dom_ffi.window() + let window = @dom_ffi.window() + let mount_target = window .document() .query_selector(".app") - .reinterpret_as_node(), - store: Ref::new(Store::default()), - } - let ret = app.try_load_storage?() - match ret { - Ok(_) => () - Err(e) => @dom_ffi.error_log(e.to_string()) + .reinterpret_as_node() + let app : @respo.RespoApp[Store] = { + store: Ref::new(@respo.try_load_storage(app_store_key)), + mount_target, + storage_key: app_store_key, } app.backup_model_beforeunload() - @dom_ffi.log("store: " + app.store.val.to_json().stringify(indent=2)) - app.render_loop() - let window = @dom_ffi.window() + // @dom_ffi.log("store: " + app.store.val.to_json().stringify(indent=2)) + app.render_loop( + fn() { view!(app.store.val) }, + fn(op) { + @dom_ffi.log("Action: " + op.to_string()) + app.store.val.update(op) + }, + ) let dev_mode = @dom_ffi.get_url_search_params( window.get_location().get_search(), ).get("mode") @dom_ffi.log("dev mode: " + dev_mode.to_string()) } - -fn App::render_loop(self : App) -> Unit { - let mount_target = self.mount_target - // let store_to_action = global_store.to_owned() - - let dispatch_action = fn(op : ActionOp) -> Unit!@respo_node.RespoCommonError { - // @dom_ffi.log("Action: \{op.to_string()}") - // @dom_ffi.log("Global store: \{global_store.val.to_json()}") - - // Self::dispatch(store_to_action.to_owned(), op)?; - self.store.val.update(op) - match op { - Noop => raise @respo_node.RespoCommonError("TODO") - _ => () - } - } - let renderer = fn() -> @respo_node.RespoNode[_]!@respo_node.RespoCommonError { - let node = self.view!() - // @dom_ffi.log( - // @cirru.format?([node.to_cirru()], { use_inline: false }).unwrap(), - // ) - node - } - let ret = @respo.render_node?( - mount_target, - fn() { self.store }, - renderer, - dispatch_action, - Some(100), - ) - match ret { - Ok(_) => () - Err(e) => @dom_ffi.error_log(e.to_string()) - } -} diff --git a/src/main/moon.pkg.json b/src/main/moon.pkg.json index 7ad2f60..7f20fef 100644 --- a/src/main/moon.pkg.json +++ b/src/main/moon.pkg.json @@ -1,10 +1,26 @@ { "is-main": true, "import": [ - { "path": "tiye/respo/lib", "alias": "respo" }, - { "path": "tiye/respo/lib/node", "alias": "respo_node" }, + { + "path": "tiye/respo/lib", + "alias": "respo", + "value": ["ui_input", "ui_button", "ui_center", "ui_row_middle"] + }, + { + "path": "tiye/respo/lib/node", + "alias": "respo_node", + "value": [ + "div", + "space", + "span", + "str_spaced", + "input", + "button", + "declare_static_style", + "respo_style" + ] + }, { "path": "tiye/respo/lib/dialog", "alias": "dialog" }, - { "path": "tiye/cirru-parser/lib", "alias": "cirru" }, { "path": "tiye/dom-ffi/lib", "alias": "dom_ffi" } ] } diff --git a/src/main/panel.mbt b/src/main/panel.mbt index cbc3bb4..19f0f66 100644 --- a/src/main/panel.mbt +++ b/src/main/panel.mbt @@ -54,21 +54,21 @@ fn comp_panel( @respo_node.RespoComponent::named( "panel", effects=[effect_panel_mount_data as @respo_node.RespoEffect], - @respo_node.div( + div( [ - @respo_node.input( - class_name=@respo.ui_input, + input( + class_name=ui_input, placeholder="some content...", value=state.content, event={}..set(Input, on_input), ), - @respo_node.space(width=8), - @respo_node.button( - class_name=@respo.ui_button, + space(width=8), + button( + class_name=ui_button, inner_text="add", event={}..set(Click, on_submit), ), - @respo_node.span(inner_text="got panel state: \{state.to_json()}", []), + span(inner_text="got panel state: \{state.to_json()}", []), ], ), ) diff --git a/src/main/plugins.mbt b/src/main/plugins.mbt index 25a9e3b..da30815 100644 --- a/src/main/plugins.mbt +++ b/src/main/plugins.mbt @@ -92,7 +92,7 @@ fn comp_plugins_demo( close_modal!(dispatch) } @respo_node.div( - style=@respo_node.RespoStyle::new(padding=Px(8)), + style=respo_style(padding=Px(8)), [ @respo_node.div( [@respo_node.span(inner_text="content in custom modal", [])], @@ -139,7 +139,7 @@ fn comp_plugins_demo( close_drawer!(dispatch) } @respo_node.div( - style=@respo_node.RespoStyle::new(padding=Px(8)), + style=respo_style(padding=Px(8)), [ @respo_node.div( [@respo_node.span(inner_text="content in custom drawer", [])], diff --git a/src/main/store.mbt b/src/main/store.mbt index 2d254a8..d646d8f 100644 --- a/src/main/store.mbt +++ b/src/main/store.mbt @@ -15,15 +15,15 @@ struct Task { time : Double } derive(Default, Eq, Hash, ToJson, @json.FromJson) -enum IndentOp { - Noop - IncTwice -} +// enum IndentOp { +// Noop +// IncTwice +// } enum ActionOp { Noop StatesChange(@respo.RespoUpdateState) - Intent(IndentOp) + // Intent(IndentOp) Increment Decrement IncTwice @@ -46,7 +46,7 @@ fn ActionOp::to_string(self : ActionOp) -> String { Noop => "Noop" StatesChange(states) => "StatesChange(\{states.cursor} \{states.data.to_json()})" - Intent(intent) => "Intent(...)" + // Intent(intent) => "Intent(...)" Increment => "Increment" Decrement => "Decrement" IncTwice => "IncTwice" @@ -100,15 +100,3 @@ fn update(self : Store, op : ActionOp) -> Unit { fn to_string(self : Store) -> String { self.to_json().stringify(indent=2) } - -fn try_from_string(s : String) -> Store!@respo_node.RespoCommonError { - let j = match @json.parse?(s) { - Ok(j) => j - Err(e) => raise @respo_node.RespoCommonError(e.to_string()) - } - let store : Store = match @json.from_json?(j) { - Ok(s) => s - Err(e) => raise @respo_node.RespoCommonError(e.to_string()) - } - store -} diff --git a/src/main/task.mbt b/src/main/task.mbt index ee565c8..327eeeb 100644 --- a/src/main/task.mbt +++ b/src/main/task.mbt @@ -69,42 +69,36 @@ fn comp_task( @respo_node.RespoComponent::named( "task", effects=[effect_state as @respo_node.RespoEffect], - @respo_node.div( - class_name=@respo.ui_row_middle + " " + style_task_container, + div( + class_name=str_spaced([ui_row_middle, style_task_container]), [ - @respo_node.div( + div( class_name=style_done_button, style=if task.done { - @respo_node.RespoStyle::new(background_color=Blue) + respo_style(background_color=Blue) } else { - @respo_node.RespoStyle::new() + respo_style() }, event={}..set(Click, on_toggle), [], ), - @respo_node.div( - attrs=@respo_node.respo_attrs(inner_text=task.content), - [], - ), - @respo_node.span( - class_name=@respo.ui_center + " " + style_remove_button, + div(attrs=@respo_node.respo_attrs(inner_text=task.content), []), + span( + class_name=str_spaced([ui_center, style_remove_button]), event={}..set(Click, on_remove), [], ), - @respo_node.div( - style=@respo_node.RespoStyle::new(margin_left=Px(20)), - [], - ), - @respo_node.input( - class_name=@respo.ui_input, + div(style=respo_style(margin_left=Px(20)), []), + input( + class_name=ui_input, value=state.draft, placeholder="something to update...", event={}..set(Input, on_input), ), - @respo_node.space(width=8), - @respo_node.button( + space(width=8), + button( inner_text="Update", - class_name=@respo.ui_button, + class_name=ui_button, event={}..set(Click, on_update), ), ], @@ -112,23 +106,15 @@ fn comp_task( ).to_node() } -let style_task_container : String = @respo_node.declare_static_style( - [ - ( - "&", - @respo_node.RespoStyle::new( - margin=Px(4), - background_color=Hsl(200, 90, 96), - ), - ), - ], +let style_task_container : String = declare_static_style( + [("&", respo_style(margin=Px(4), background_color=Hsl(200, 90, 96)))], ) -let style_done_button : String = @respo_node.declare_static_style( +let style_done_button : String = declare_static_style( [ ( "&", - @respo_node.RespoStyle::new( + respo_style( width=Px(24), height=Px(24), margin=Px(4), @@ -139,11 +125,11 @@ let style_done_button : String = @respo_node.declare_static_style( ], ) -let style_remove_button : String = @respo_node.declare_static_style( +let style_remove_button : String = declare_static_style( [ ( "&", - @respo_node.RespoStyle::new( + respo_style( width=Px(16), height=Px(16), margin=Px(4), @@ -152,6 +138,6 @@ let style_remove_button : String = @respo_node.declare_static_style( color=Hsl(0, 90, 90), ), ), - ("$0:hover", @respo_node.RespoStyle::new(color=Hsl(0, 90, 80))), + ("$0:hover", respo_style(color=Hsl(0, 90, 80))), ], )