mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-22 20:23:09 +00:00
Merge pull request #904 from Demonthos/fix-style-attributes-ssr
Fix style attributes in SSR
This commit is contained in:
commit
b09207e75d
3 changed files with 123 additions and 8 deletions
|
@ -17,6 +17,12 @@ pub enum Segment {
|
|||
Attr(usize),
|
||||
Node(usize),
|
||||
PreRendered(String),
|
||||
/// A marker for where to insert a dynamic styles
|
||||
StyleMarker {
|
||||
// If the marker is inside a style tag or not
|
||||
// This will be true if there are static styles
|
||||
inside_style_tag: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Write for StringChain {
|
||||
|
@ -61,16 +67,45 @@ impl StringCache {
|
|||
} => {
|
||||
cur_path.push(root_idx);
|
||||
write!(chain, "<{tag}")?;
|
||||
// we need to collect the styles and write them at the end
|
||||
let mut styles = Vec::new();
|
||||
let mut has_dynamic_attrs = false;
|
||||
for attr in *attrs {
|
||||
match attr {
|
||||
TemplateAttribute::Static { name, value, .. } => {
|
||||
write!(chain, " {name}=\"{value}\"")?;
|
||||
TemplateAttribute::Static {
|
||||
name,
|
||||
value,
|
||||
namespace,
|
||||
} => {
|
||||
if let Some("style") = namespace {
|
||||
styles.push((name, value));
|
||||
} else {
|
||||
write!(chain, " {name}=\"{value}\"")?;
|
||||
}
|
||||
}
|
||||
TemplateAttribute::Dynamic { id: index } => {
|
||||
chain.segments.push(Segment::Attr(*index))
|
||||
chain.segments.push(Segment::Attr(*index));
|
||||
has_dynamic_attrs = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write the styles
|
||||
if !styles.is_empty() {
|
||||
write!(chain, " style=\"")?;
|
||||
for (name, value) in styles {
|
||||
write!(chain, "{name}:{value};")?;
|
||||
}
|
||||
chain.segments.push(Segment::StyleMarker {
|
||||
inside_style_tag: true,
|
||||
});
|
||||
write!(chain, "\"")?;
|
||||
} else if has_dynamic_attrs {
|
||||
chain.segments.push(Segment::StyleMarker {
|
||||
inside_style_tag: false,
|
||||
});
|
||||
}
|
||||
|
||||
if children.is_empty() && tag_is_self_closing(tag) {
|
||||
write!(chain, "/>")?;
|
||||
} else {
|
||||
|
|
|
@ -70,15 +70,24 @@ impl Renderer {
|
|||
.or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap()))
|
||||
.clone();
|
||||
|
||||
// We need to keep track of the dynamic styles so we can insert them into the right place
|
||||
let mut accumulated_dynamic_styles = Vec::new();
|
||||
|
||||
for segment in entry.segments.iter() {
|
||||
match segment {
|
||||
Segment::Attr(idx) => {
|
||||
let attr = &template.dynamic_attrs[*idx];
|
||||
match attr.value {
|
||||
AttributeValue::Text(value) => write!(buf, " {}=\"{}\"", attr.name, value)?,
|
||||
AttributeValue::Bool(value) => write!(buf, " {}={}", attr.name, value)?,
|
||||
_ => {}
|
||||
};
|
||||
if attr.namespace == Some("style") {
|
||||
accumulated_dynamic_styles.push(attr);
|
||||
} else {
|
||||
match attr.value {
|
||||
AttributeValue::Text(value) => {
|
||||
write!(buf, " {}=\"{}\"", attr.name, value)?
|
||||
}
|
||||
AttributeValue::Bool(value) => write!(buf, " {}={}", attr.name, value)?,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
Segment::Node(idx) => match &template.dynamic_nodes[*idx] {
|
||||
DynamicNode::Component(node) => {
|
||||
|
@ -128,6 +137,34 @@ impl Renderer {
|
|||
},
|
||||
|
||||
Segment::PreRendered(contents) => write!(buf, "{contents}")?,
|
||||
|
||||
Segment::StyleMarker { inside_style_tag } => {
|
||||
if !accumulated_dynamic_styles.is_empty() {
|
||||
// if we are inside a style tag, we don't need to write the style attribute
|
||||
if !*inside_style_tag {
|
||||
write!(buf, " style=\"")?;
|
||||
}
|
||||
for attr in &accumulated_dynamic_styles {
|
||||
match attr.value {
|
||||
AttributeValue::Text(value) => {
|
||||
write!(buf, "{}:{};", attr.name, value)?
|
||||
}
|
||||
AttributeValue::Bool(value) => {
|
||||
write!(buf, "{}:{};", attr.name, value)?
|
||||
}
|
||||
AttributeValue::Float(f) => write!(buf, "{}:{};", attr.name, f)?,
|
||||
AttributeValue::Int(i) => write!(buf, "{}:{};", attr.name, i)?,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
if !*inside_style_tag {
|
||||
write!(buf, "\"")?;
|
||||
}
|
||||
|
||||
// clear the accumulated styles
|
||||
accumulated_dynamic_styles.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,6 +205,9 @@ fn to_string_works() {
|
|||
vec![
|
||||
PreRendered("<div class=\"asdasdasd\" class=\"asdasdasd\"".into(),),
|
||||
Attr(0,),
|
||||
StyleMarker {
|
||||
inside_style_tag: false,
|
||||
},
|
||||
PreRendered(">Hello world 1 -->".into(),),
|
||||
Node(0,),
|
||||
PreRendered(
|
||||
|
|
40
packages/ssr/tests/styles.rs
Normal file
40
packages/ssr/tests/styles.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn static_styles() {
|
||||
fn app(cx: Scope) -> Element {
|
||||
render! { div { width: "100px" } }
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
_ = dom.rebuild();
|
||||
|
||||
assert_eq!(
|
||||
dioxus_ssr::render(&dom),
|
||||
r#"<div style="width:100px;"></div>"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partially_dynamic_styles() {
|
||||
let dynamic = 123;
|
||||
|
||||
assert_eq!(
|
||||
dioxus_ssr::render_lazy(rsx! {
|
||||
div { width: "100px", height: "{dynamic}px" }
|
||||
}),
|
||||
r#"<div style="width:100px;height:123px;"></div>"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_styles() {
|
||||
let dynamic = 123;
|
||||
|
||||
assert_eq!(
|
||||
dioxus_ssr::render_lazy(rsx! {
|
||||
div { width: "{dynamic}px" }
|
||||
}),
|
||||
r#"<div style="width:123px;"></div>"#
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue