Rewrite some components to use render method

This commit is contained in:
eraden 2021-04-18 23:24:20 +02:00
parent 2d7ce5762a
commit e3e55acd54
10 changed files with 351 additions and 346 deletions

View File

@ -81,7 +81,7 @@ impl<'l> ChildBuilder<'l> {
} }
} }
#[derive(Debug, Default)] #[derive(Debug)]
pub struct StyledCheckbox<'l, Options> pub struct StyledCheckbox<'l, Options>
where where
Options: Iterator<Item = ChildBuilder<'l>>, Options: Iterator<Item = ChildBuilder<'l>>,
@ -90,6 +90,18 @@ where
pub class_list: &'l str, pub class_list: &'l str,
} }
impl<'l, Options> Default for StyledCheckbox<'l, Options>
where
Options: Iterator<Item = ChildBuilder<'l>>,
{
fn default() -> Self {
Self {
options: None,
class_list: "",
}
}
}
impl<'l, Options> StyledCheckbox<'l, Options> impl<'l, Options> StyledCheckbox<'l, Options>
where where
Options: Iterator<Item = ChildBuilder<'l>>, Options: Iterator<Item = ChildBuilder<'l>>,

View File

@ -49,89 +49,81 @@ impl<'l> Default for StyledEditor<'l> {
} }
} }
impl<'l> ToNode for StyledEditor<'l> { impl<'l> StyledEditor<'l> {
#[inline] #[inline]
fn into_node(self) -> Node<Msg> { pub fn render(self) -> Node<Msg> {
render(self) let StyledEditor {
id,
initial_text,
text: _,
html,
mode,
update_event,
} = self;
let id = id.expect("Styled Editor requires ID");
let on_editor_clicked = click_handler(id.clone(), Mode::Editor);
let on_view_clicked = click_handler(id.clone(), Mode::View);
let editor_id = format!("editor-{}", id);
let view_id = format!("view-{}", id);
let name = format!("styled-editor-{}", id);
let text_area = StyledTextarea {
id: Some(id),
height: 40,
max_height: 0,
value: initial_text,
class_list: "",
update_event,
placeholder: "",
disable_auto_resize: false,
}
.into_node();
div![
C!["styledEditor"],
label![
C![
"navbar viewTab activeTab",
IF![mode == Mode::View => "activeTab"]
],
attrs![At::For => view_id.as_str()],
"View",
on_view_clicked
],
label![
C!["navbar editorTab", IF![mode == Mode::Editor => "activeTab"]],
attrs![At::For => editor_id.as_str()],
"Editor",
on_editor_clicked
],
seed::input![
id![editor_id.as_str()],
C!["editorRadio"],
attrs![At::Type => "radio"; At::Name => name.as_str(); At::Checked => true],
],
text_area,
seed::input![
id![view_id.as_str()],
C!["viewRadio"],
attrs![ At::Type => "radio"; At::Name => name.as_str();],
IF![mode == Mode::View => attrs![At::Checked => true]]
],
div![
C!["view"],
IF![mode == Mode::Editor => empty![]],
IF![mode == Mode::View => raw![html]],
],
]
} }
} }
#[inline] impl<'l> ToNode for StyledEditor<'l> {
pub fn render(values: StyledEditor) -> Node<Msg> { #[inline]
let StyledEditor { fn into_node(self) -> Node<Msg> {
id, self.render()
initial_text,
text: _,
html,
mode,
update_event,
} = values;
let id = id.expect("Styled Editor requires ID");
let on_editor_clicked = click_handler(id.clone(), Mode::Editor);
let on_view_clicked = click_handler(id.clone(), Mode::View);
let editor_id = format!("editor-{}", id);
let view_id = format!("view-{}", id);
let name = format!("styled-editor-{}", id);
let text_area = StyledTextarea {
id: Some(id),
height: 40,
max_height: 0,
value: initial_text,
class_list: "",
update_event,
placeholder: "",
disable_auto_resize: false,
} }
.into_node();
let (editor_radio_node, view_radio_node) = (
seed::input![
id![editor_id.as_str()],
C!["editorRadio"],
attrs![At::Type => "radio"; At::Name => name.as_str(); At::Checked => true],
],
seed::input![
id![view_id.as_str()],
C!["viewRadio"],
attrs![ At::Type => "radio"; At::Name => name.as_str();],
IF![mode == Mode::View => attrs![At::Checked => true]]
],
);
div![
C!["styledEditor"],
label![
if mode == Mode::View {
C!["navbar viewTab activeTab"]
} else {
C!["navbar viewTab"]
},
attrs![At::For => view_id.as_str()],
"View",
on_view_clicked
],
label![
if mode == Mode::Editor {
C!["navbar editorTab activeTab"]
} else {
C!["navbar editorTab"]
},
attrs![At::For => editor_id.as_str()],
"Editor",
on_editor_clicked
],
editor_radio_node,
text_area,
view_radio_node,
div![
C!["view"],
IF![mode == Mode::Editor => empty![]],
IF![mode == Mode::View => raw![html]],
],
]
} }
#[inline] #[inline]

View File

@ -23,25 +23,27 @@ impl<'l> Default for StyledField<'l> {
} }
} }
impl<'l> ToNode for StyledField<'l> { impl<'l> StyledField<'l> {
fn into_node(self) -> Node<Msg> { pub fn render(self) -> Node<Msg> {
render(self) let StyledField {
label,
tip,
input,
class_list,
} = self;
let tip_node = tip.map_or_else(|| empty![], |s| div![C!["styledTip"], s]);
div![
C!["styledField", class_list],
seed::label![C!["styledLabel"], label],
input,
tip_node,
]
} }
} }
pub fn render(values: StyledField) -> Node<Msg> { impl<'l> ToNode for StyledField<'l> {
let StyledField { fn into_node(self) -> Node<Msg> {
label, self.render()
tip, }
input,
class_list,
} = values;
let tip_node = tip.map(|s| div![C!["styledTip"], s]).unwrap_or(empty![]);
div![
C!["styledField", class_list],
seed::label![C!["styledLabel"], label],
input,
tip_node,
]
} }

View File

@ -11,27 +11,29 @@ pub struct StyledForm<'l> {
pub on_submit: Option<EventHandler<Msg>>, pub on_submit: Option<EventHandler<Msg>>,
} }
impl<'l> ToNode for StyledForm<'l> { impl<'l> StyledForm<'l> {
#[inline] #[inline]
fn into_node(self) -> Node<Msg> { pub fn render(self) -> Node<Msg> {
render(self) let StyledForm {
heading,
fields,
on_submit,
} = self;
let handlers = match on_submit {
Some(handler) => vec![handler],
_ => vec![],
};
seed::form![
handlers,
C!["styledForm"],
div![C!["formElement"], div![C!["formHeading"], heading], fields],
]
} }
} }
#[inline] impl<'l> ToNode for StyledForm<'l> {
pub fn render(values: StyledForm) -> Node<Msg> { #[inline]
let StyledForm { fn into_node(self) -> Node<Msg> {
heading, self.render()
fields, }
on_submit,
} = values;
let handlers = match on_submit {
Some(handler) => vec![handler],
_ => vec![],
};
seed::form![
handlers,
C!["styledForm"],
div![C!["formElement"], div![C!["formHeading"], heading], fields],
]
} }

View File

@ -271,53 +271,55 @@ impl<'l> Default for StyledIcon<'l> {
impl<'l> ToNode for StyledIcon<'l> { impl<'l> ToNode for StyledIcon<'l> {
fn into_node(self) -> Node<Msg> { fn into_node(self) -> Node<Msg> {
render(self) self.render()
} }
} }
pub fn render(values: StyledIcon) -> Node<Msg> { impl<'l> StyledIcon<'l> {
let StyledIcon { pub fn render(self) -> Node<Msg> {
icon, let StyledIcon {
size, icon,
color, size,
class_list, color,
style_list, class_list,
on_click, style_list,
} = values; on_click,
} = self;
let styles: Vec<Attrs> = vec![ let styles: Vec<Attrs> = vec![
size.map(|s| { size.map(|s| {
let font_size = format!("font-size: {}", s); let font_size = format!("font-size: {}", s);
attrs![At::Style => font_size] attrs![At::Style => font_size]
}), }),
icon.to_color().map(|s| { icon.to_color().map(|s| {
let color = format!("color: {}", s); let color = format!("color: {}", s);
attrs![At::Style => color] attrs![At::Style => color]
}), }),
color.map(|s| attrs![At::Style => format!("color: var(--{})", s)]), color.map(|s| attrs![At::Style => format!("color: var(--{})", s)]),
] ]
.into_iter() .into_iter()
.flatten() .flatten()
.collect(); .collect();
let style_list = style_list.into_iter().fold("".to_string(), |mut mem, s| { let style_list = style_list.into_iter().fold("".to_string(), |mut mem, s| {
match s { match s {
Cow::Borrowed(s) => { Cow::Borrowed(s) => {
mem.push_str(s); mem.push_str(s);
}
Cow::Owned(owned) => {
mem.push_str(owned.as_str());
}
} }
Cow::Owned(owned) => { mem.push(';');
mem.push_str(owned.as_str()); mem
} });
}
mem.push(';');
mem
});
i![ i![
C!["styledIcon", class_list, icon.to_str()], C!["styledIcon", class_list, icon.to_str()],
styles, styles,
attrs![ At::Style => style_list ], attrs![ At::Style => style_list ],
on_click, on_click,
"" ""
] ]
}
} }

View File

@ -39,46 +39,48 @@ pub struct StyledImageInput<'l> {
impl<'l> ToNode for StyledImageInput<'l> { impl<'l> ToNode for StyledImageInput<'l> {
fn into_node(self) -> Node<Msg> { fn into_node(self) -> Node<Msg> {
render(self) self.render()
} }
} }
fn render(values: StyledImageInput) -> Node<Msg> { impl<'l> StyledImageInput<'l> {
let StyledImageInput { pub fn render(self) -> Node<Msg> {
id, let StyledImageInput {
class_list, id,
url, class_list,
} = values; url,
} = self;
let field_id = id.clone(); let field_id = id.clone();
let on_change = ev(Ev::Change, move |ev| { let on_change = ev(Ev::Change, move |ev| {
let target = ev.target().unwrap(); let target = ev.target().unwrap();
let input = seed::to_input(&target); let input = seed::to_input(&target);
let v = input let v = input
.files() .files()
.map(|list| { .map(|list| {
let mut v = vec![]; let mut v = vec![];
for i in 0..list.length() { for i in 0..list.length() {
v.push(list.get(i).unwrap()); v.push(list.get(i).unwrap());
} }
v v
}) })
.unwrap_or_default(); .unwrap_or_default();
Msg::FileInputChanged(field_id, v) Msg::FileInputChanged(field_id, v)
}); });
let input_id = id.to_string(); let input_id = id.to_string();
div![ div![
C!["styledImageInput", class_list], C!["styledImageInput", class_list],
label![ label![
C!["label"], C!["label"],
attrs![At::For => input_id], attrs![At::For => input_id],
img![C!["mask"], attrs![At::Src => url.unwrap_or_default()], " "] img![C!["mask"], attrs![At::Src => url.unwrap_or_default()], " "]
], ],
input![ input![
C!["input"], C!["input"],
attrs![At::Type => "file", At::Id => input_id], attrs![At::Type => "file", At::Id => input_id],
on_change on_change
]
] ]
] }
} }

View File

@ -163,62 +163,64 @@ impl<'l, 'm: 'l> StyledInput<'l, 'm> {
impl<'l, 'm: 'l> ToNode for StyledInput<'l, 'm> { impl<'l, 'm: 'l> ToNode for StyledInput<'l, 'm> {
#[inline] #[inline]
fn into_node(self) -> Node<Msg> { fn into_node(self) -> Node<Msg> {
render(self) self.render()
} }
} }
pub fn render(values: StyledInput) -> Node<Msg> { impl<'l, 'm: 'l> StyledInput<'l, 'm> {
let StyledInput { pub fn render(self) -> Node<Msg> {
id, let StyledInput {
icon, id,
valid, icon,
value, valid,
input_type, value,
input_class_list, input_type,
wrapper_class_list, input_class_list,
variant, wrapper_class_list,
auto_focus, variant,
input_handlers, auto_focus,
} = values;
let id = id.expect("Input id is required");
let icon_node = icon
.map(|icon| StyledIcon::from(icon).into_node())
.unwrap_or(Node::Empty);
let on_change = {
let field_id = id.clone();
ev(Ev::Change, move |event| {
event.stop_propagation();
let target = event.target().unwrap();
Msg::StrInputChanged(field_id, seed::to_input(&target).value())
})
};
div![
C![
"styledInput",
format!("{}", id),
variant.to_str(),
wrapper_class_list
],
IF![!valid => C!["invalid"]],
icon_node,
seed::input![
C![
"inputElement",
variant.to_str(),
input_class_list,
icon.as_ref().map(|_| "withIcon").unwrap_or_default()
],
attrs![
"id" => format!("{}", id),
"value" => value,
"type" => input_type.unwrap_or("text"),
],
IF![auto_focus => attrs![At::AutoFocus => true]],
on_change,
input_handlers, input_handlers,
], } = self;
] let id = id.expect("Input id is required");
let icon_node = icon
.map(|icon| StyledIcon::from(icon).render())
.unwrap_or(Node::Empty);
let on_change = {
let field_id = id.clone();
ev(Ev::Change, move |event| {
event.stop_propagation();
let target = event.target().unwrap();
Msg::StrInputChanged(field_id, seed::to_input(&target).value())
})
};
div![
C![
"styledInput",
format!("{}", id),
variant.to_str(),
wrapper_class_list
],
IF![!valid => C!["invalid"]],
icon_node,
seed::input![
C![
"inputElement",
variant.to_str(),
input_class_list,
icon.as_ref().map(|_| "withIcon").unwrap_or_default()
],
attrs![
"id" => format!("{}", id),
"value" => value,
"type" => input_type.unwrap_or("text"),
],
IF![auto_focus => attrs![At::AutoFocus => true]],
on_change,
input_handlers,
],
]
}
} }

View File

@ -14,36 +14,38 @@ pub struct StyledLink<'l> {
impl<'l> ToNode for StyledLink<'l> { impl<'l> ToNode for StyledLink<'l> {
fn into_node(self) -> Node<Msg> { fn into_node(self) -> Node<Msg> {
render(self) self.render()
} }
} }
pub fn render(values: StyledLink) -> Node<Msg> { impl<'l> StyledLink<'l> {
let StyledLink { pub fn render(self) -> Node<Msg> {
children, let StyledLink {
class_list, children,
href, class_list,
} = values; href,
} = self;
let on_click = { let on_click = {
let href = href.to_string(); let href = href.to_string();
mouse_ev("click", move |ev| { mouse_ev("click", move |ev| {
if href.starts_with('/') { if href.starts_with('/') {
ev.prevent_default(); ev.prevent_default();
ev.stop_propagation(); ev.stop_propagation();
if let Ok(url) = seed::Url::from_str(href.as_str()) { if let Ok(url) = seed::Url::from_str(href.as_str()) {
url.go_and_push(); url.go_and_push();
}
} }
}
None as Option<Msg> None as Option<Msg>
}) })
}; };
a![ a![
C!["styledLink", class_list], C!["styledLink", class_list],
attrs![ At::Href => href, ], attrs![ At::Href => href, ],
on_click, on_click,
children, children,
] ]
}
} }

View File

@ -63,60 +63,55 @@ impl<'l> StyledModal<'l> {
impl<'l> ToNode for StyledModal<'l> { impl<'l> ToNode for StyledModal<'l> {
fn into_node(self) -> Node<Msg> { fn into_node(self) -> Node<Msg> {
render(self) self.render()
} }
} }
#[inline] impl<'l> StyledModal<'l> {
pub fn render(values: StyledModal) -> Node<Msg> { #[inline]
let StyledModal { pub fn render(self) -> Node<Msg> {
variant, let StyledModal {
width, variant,
with_icon, width,
children, with_icon,
class_list, children,
} = values; class_list,
} = self;
let icon = if with_icon { let close_handler = mouse_ev(Ev::Click, |ev| {
StyledIcon { ev.stop_propagation();
icon: Icon::Close, ev.prevent_default();
class_list: variant.to_icon_class_name(), Msg::ModalDropped
..Default::default() });
} let body_handler = mouse_ev(Ev::Click, |ev| {
.into_node() ev.stop_propagation();
} else { ev.prevent_default();
empty![] None as Option<Msg>
}; });
let close_handler = mouse_ev(Ev::Click, |ev| { let styled_modal_style = match width {
ev.stop_propagation(); Some(0) => "".to_string(),
ev.prevent_default(); Some(n) => format!("max-width: {width}px", width = n),
Msg::ModalDropped _ => format!("max-width: {width}px", width = 130),
}); };
let body_handler = mouse_ev(Ev::Click, |ev| {
ev.stop_propagation();
ev.prevent_default();
None as Option<Msg>
});
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
let styled_modal_style = match width {
Some(0) => "".to_string(),
Some(n) => format!("max-width: {width}px", width = n),
_ => format!("max-width: {width}px", width = 130),
};
div![
C!["modal"],
div![ div![
C![clickable_class], C!["modal"],
close_handler,
div![ div![
C![class_list, "styledModal", variant.to_class_name()], C!["clickableOverlay", variant.to_class_name()],
attrs![At::Style => styled_modal_style], close_handler,
body_handler, div![
icon, C![class_list, "styledModal", variant.to_class_name()],
children attrs![At::Style => styled_modal_style],
body_handler,
IF![with_icon => StyledIcon {
icon: Icon::Close,
class_list: variant.to_icon_class_name(),
..Default::default()
}
.render()],
children
]
] ]
] ]
] }
} }

View File

@ -246,30 +246,6 @@ where
vec![] vec![]
}; };
let option_list = match (opened, children.is_empty()) {
(false, _) => empty![],
(_, true) => seed::div![C!["noOptions"], "No results"],
_ => seed::div![C!["options"], children],
};
let value: Vec<Node<Msg>> = if is_multi {
let add_icon = StyledIcon::from(Icon::Plus).into_node();
let mut children: Vec<Node<Msg>> = selected
.into_iter()
.map(|m| into_multi_value(m, id.clone()))
.collect();
if !children.is_empty() {
children.push(div![C!["addMore"], add_icon, "Add more"]);
} else {
children.push(div![C!["placeholder"], "Select"]);
}
vec![div![C!["valueMulti"], children]]
} else {
selected.into_iter().map(|m| m.render_value()).collect()
};
seed::div![ seed::div![
C!["styledSelect", variant.to_str(), IF![!valid => "invalid"]], C!["styledSelect", variant.to_str(), IF![!valid => "invalid"]],
attrs![At::Style => dropdown_style.as_str()], attrs![At::Style => dropdown_style.as_str()],
@ -280,7 +256,21 @@ where
div![ div![
C!["valueContainer", variant.to_str()], C!["valueContainer", variant.to_str()],
on_handler, on_handler,
value, match is_multi {
true => vec![div![
C!["valueMulti"],
selected
.into_iter()
.map(|m| into_multi_value(m, id.clone()))
.collect::<Vec<Node<Msg>>>(),
IF![children.is_empty() => div![C!["placeholder"], "Select"]],
IF![!children.is_empty() => div![C!["addMore"], StyledIcon::from(Icon::Plus).render(), "Add more"]],
]],
false => selected
.into_iter()
.map(|m| m.render_value())
.collect::<Vec<Node<Msg>>>(),
},
action_icon, action_icon,
], ],
div![ div![
@ -296,7 +286,11 @@ where
], ],
on_text, on_text,
]], ]],
option_list match (opened, children.is_empty()) {
(false, _) => empty![],
(_, true) => seed::div![C!["noOptions"], "No results"],
_ => seed::div![C!["options"], children],
}
] ]
] ]
} }