1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
use crate::mjml::body::prelude::*; use crate::mjml::error::Error; use crate::mjml::prelude::*; use crate::util::condition::{END_CONDITIONAL_TAG, START_CONDITIONAL_TAG}; use crate::util::prelude::*; use crate::util::{Context, Header, Style, Tag}; use crate::Options; use log::debug; use roxmltree::Node; use std::collections::HashMap; #[derive(Clone, Debug)] pub struct MJSpacer { options: Options, attributes: HashMap<String, String>, context: Option<Context>, } impl MJSpacer { pub fn parse<'a, 'b>(node: Node<'a, 'b>, opts: &Options) -> Result<MJSpacer, Error> { Ok(MJSpacer { options: opts.clone(), attributes: get_node_attributes(&node), context: None, }) } fn get_style_div(&self) -> Style { let mut res = Style::new(); res.maybe_set("height", self.get_attribute("height")); res } } impl Component for MJSpacer { fn context(&self) -> Option<&Context> { self.context.as_ref() } fn set_context(&mut self, ctx: Context) { self.context = Some(ctx.clone()); } fn render(&self, _header: &Header) -> Result<String, Error> { let height = self.get_size_attribute("height"); let table = Tag::table(); let tr = Tag::tr(); let td = Tag::td() .set_style("vertical-align", "top") .maybe_set_style("height", height.clone()) .maybe_set_attribute("height", height.and_then(|h| Some(h.value()))); let div = Tag::div().insert_style(self.get_style_div().inner()); let mut res = vec![]; res.push(START_CONDITIONAL_TAG.into()); res.push(table.open()); res.push(tr.open()); res.push(td.open()); res.push(END_CONDITIONAL_TAG.into()); res.push(div.render(" ")); res.push(START_CONDITIONAL_TAG.into()); res.push(td.close()); res.push(tr.close()); res.push(table.close()); res.push(END_CONDITIONAL_TAG.into()); Ok(res.join("")) } } impl ComponentWithAttributes for MJSpacer { fn default_attribute(&self, key: &str) -> Option<String> { debug!("default_attribute {}", key); match key { "height" => Some("20px".into()), _ => None, } } fn source_attributes(&self) -> Option<&HashMap<String, String>> { Some(&self.attributes) } } impl BodyComponent for MJSpacer { fn get_style(&self, name: &str) -> Style { match name { "div" => self.get_style_div(), _ => Style::new(), } } } impl BodyContainedComponent for MJSpacer {} impl ComponentWithSizeAttribute for MJSpacer {} impl BodyComponentWithBorder for MJSpacer {} impl BodyComponentWithPadding for MJSpacer {} impl BodyComponentWithBoxWidths for MJSpacer {} #[cfg(test)] pub mod tests { use crate::tests::compare_render; #[test] fn base() { compare_render( include_str!("../../../test/mj-spacer.mjml"), include_str!("../../../test/mj-spacer.html"), ); } }