mjml-rb 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/mjml-rb/components/accordion.rb +24 -20
- data/lib/mjml-rb/components/attributes.rb +53 -0
- data/lib/mjml-rb/components/base.rb +4 -0
- data/lib/mjml-rb/components/navbar.rb +10 -8
- data/lib/mjml-rb/components/raw.rb +21 -0
- data/lib/mjml-rb/components/section.rb +25 -21
- data/lib/mjml-rb/components/social.rb +17 -13
- data/lib/mjml-rb/parser.rb +15 -1
- data/lib/mjml-rb/renderer.rb +60 -14
- data/lib/mjml-rb/version.rb +1 -1
- data/mjml-rb.gemspec +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c0d1fcf5bc3d3da63f1e883fbf9fc1c5d596f23e2d1845b52fac3a2795c15ec
|
|
4
|
+
data.tar.gz: 96945245aea91b2d4cb94336a2d29a58eb6c5cbf332ac76e2828f581f4e31a4f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ab219c36a7dfb49bc89ce1f7b6da2dc8d5089e60bfb640b2f2606783d8f27ee5560133274935d0f6aee96ac076cdfac346c4d85775452a2933f2a47ff9d87e0
|
|
7
|
+
data.tar.gz: d3fea2c9c4cd07a967d62dc07ff16328c0b61cc7f20b108209f21c7d5f8eb061040eaf5085875c0bb09a81d56607e08e9efa3cfd83d147f296a5a8308687cca9
|
data/README.md
CHANGED
|
@@ -58,11 +58,11 @@ The table below tracks current JS-to-Ruby migration status for MJML components i
|
|
|
58
58
|
| `mj-hero` | migrated | Implemented in `hero.rb` with fixed/fluid modes, inner content wrapper, and Outlook VML background fallback. |
|
|
59
59
|
| `mj-navbar` | migrated | Implemented in `navbar.rb`, including `base-url` propagation and breakpoint-aware hamburger CSS. |
|
|
60
60
|
| `mj-navbar-link` | migrated | Implemented in `navbar.rb` as an ending-tag navbar child component. |
|
|
61
|
-
| `mj-raw` |
|
|
61
|
+
| `mj-raw` | migrated | Implemented in `raw.rb`, including head insertion and top-level `position="file-start"` output before the doctype. |
|
|
62
62
|
| `mj-head` | partial | Core tags such as `mj-title`, `mj-preview`, `mj-style`, `mj-font`, and `mj-attributes` are supported. |
|
|
63
|
-
| `mj-attributes` |
|
|
63
|
+
| `mj-attributes` | migrated | Implemented in `attributes.rb`, including npm-style `mj-class` descendant defaults. |
|
|
64
64
|
| `mj-all` | partial | Supported through `mj-attributes`. |
|
|
65
|
-
| `mj-class` |
|
|
65
|
+
| `mj-class` | migrated | Supported through `attributes.rb`, including nested per-tag descendant defaults. |
|
|
66
66
|
| `mj-title` | partial | Supported through head context. |
|
|
67
67
|
| `mj-preview` | partial | Supported through head context. |
|
|
68
68
|
| `mj-style` | partial | Supported, including inline CSS application. |
|
|
@@ -94,16 +94,18 @@ module MjmlRb
|
|
|
94
94
|
"border-bottom" => "none",
|
|
95
95
|
"font-family" => accordion_attrs["font-family"]
|
|
96
96
|
)
|
|
97
|
-
inner = node
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
inner = with_inherited_mj_class(context, node) do
|
|
98
|
+
node.element_children.map do |child|
|
|
99
|
+
case child.tag_name
|
|
100
|
+
when "mj-accordion-element"
|
|
101
|
+
render_accordion_element(child, context, accordion_attrs)
|
|
102
|
+
when "mj-raw"
|
|
103
|
+
raw_inner(child)
|
|
104
|
+
else
|
|
105
|
+
render_node(child, context, parent: "mj-accordion")
|
|
106
|
+
end
|
|
107
|
+
end.join
|
|
108
|
+
end
|
|
107
109
|
|
|
108
110
|
%(<tr><td style="#{outer_style}"><table role="presentation" width="100%" cellspacing="0" cellpadding="0" class="mj-accordion" style="#{table_style}"><tbody>#{inner}</tbody></table></td></tr>)
|
|
109
111
|
end
|
|
@@ -125,16 +127,18 @@ module MjmlRb
|
|
|
125
127
|
content = []
|
|
126
128
|
content << render_accordion_title(nil, attrs) unless has_title
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
with_inherited_mj_class(context, node) do
|
|
131
|
+
children.each do |child|
|
|
132
|
+
case child.tag_name
|
|
133
|
+
when "mj-accordion-title"
|
|
134
|
+
child_attrs = attrs.merge(resolved_attributes(child, context))
|
|
135
|
+
content << render_accordion_title(child, child_attrs)
|
|
136
|
+
when "mj-accordion-text"
|
|
137
|
+
child_attrs = attrs.merge(resolved_attributes(child, context))
|
|
138
|
+
content << render_accordion_text(child, child_attrs)
|
|
139
|
+
when "mj-raw"
|
|
140
|
+
content << raw_inner(child)
|
|
141
|
+
end
|
|
138
142
|
end
|
|
139
143
|
end
|
|
140
144
|
content << render_accordion_text(nil, attrs) unless has_text
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require_relative "base"
|
|
2
|
+
|
|
3
|
+
module MjmlRb
|
|
4
|
+
module Components
|
|
5
|
+
class Attributes < Base
|
|
6
|
+
TAGS = %w[mj-attributes mj-all mj-class].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def allowed_attributes_for(tag_name)
|
|
10
|
+
{}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def allowed_attributes
|
|
14
|
+
{}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def tags
|
|
19
|
+
TAGS
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render(tag_name:, node:, context:, attrs:, parent:)
|
|
23
|
+
""
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def handle_head(attributes_node, context)
|
|
27
|
+
attributes_node.element_children.each do |child|
|
|
28
|
+
case child.tag_name
|
|
29
|
+
when "mj-all"
|
|
30
|
+
context[:global_defaults].merge!(child.attributes)
|
|
31
|
+
when "mj-class"
|
|
32
|
+
name = child.attributes["name"]
|
|
33
|
+
next unless name
|
|
34
|
+
|
|
35
|
+
context[:classes][name] ||= {}
|
|
36
|
+
context[:classes][name].merge!(child.attributes.reject { |key, _| key == "name" })
|
|
37
|
+
|
|
38
|
+
defaults = child.element_children.each_with_object({}) do |class_child, memo|
|
|
39
|
+
memo[class_child.tag_name] = class_child.attributes
|
|
40
|
+
end
|
|
41
|
+
next if defaults.empty?
|
|
42
|
+
|
|
43
|
+
context[:classes_default][name] ||= {}
|
|
44
|
+
context[:classes_default][name].merge!(defaults)
|
|
45
|
+
else
|
|
46
|
+
context[:tag_defaults][child.tag_name] ||= {}
|
|
47
|
+
context[:tag_defaults][child.tag_name].merge!(child.attributes)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -45,6 +45,10 @@ module MjmlRb
|
|
|
45
45
|
renderer.send(:resolved_attributes, node, context)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def with_inherited_mj_class(context, node, &block)
|
|
49
|
+
renderer.send(:with_inherited_mj_class, context, node, &block)
|
|
50
|
+
end
|
|
51
|
+
|
|
48
52
|
def raw_inner(node)
|
|
49
53
|
renderer.send(:raw_inner, node)
|
|
50
54
|
end
|
|
@@ -164,14 +164,16 @@ module MjmlRb
|
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
def render_navbar_children(node, context)
|
|
167
|
-
node
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
with_inherited_mj_class(context, node) do
|
|
168
|
+
node.element_children.map do |child|
|
|
169
|
+
case child.tag_name
|
|
170
|
+
when "mj-navbar-link", "mj-raw"
|
|
171
|
+
render_node(child, context, parent: "mj-navbar")
|
|
172
|
+
else
|
|
173
|
+
""
|
|
174
|
+
end
|
|
175
|
+
end.join
|
|
176
|
+
end
|
|
175
177
|
end
|
|
176
178
|
|
|
177
179
|
def render_navbar_link(node, context, attrs, parent:)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative "base"
|
|
2
|
+
|
|
3
|
+
module MjmlRb
|
|
4
|
+
module Components
|
|
5
|
+
class Raw < Base
|
|
6
|
+
TAGS = ["mj-raw"].freeze
|
|
7
|
+
|
|
8
|
+
ALLOWED_ATTRIBUTES = {
|
|
9
|
+
"position" => "enum(file-start)"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def tags
|
|
13
|
+
TAGS
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render(tag_name:, node:, context:, attrs:, parent:)
|
|
17
|
+
raw_inner(node)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -224,22 +224,24 @@ module MjmlRb
|
|
|
224
224
|
close_tr = %(<!--[if mso | IE]></tr><![endif]-->)
|
|
225
225
|
close_table = %(<!--[if mso | IE]></table><![endif]-->)
|
|
226
226
|
|
|
227
|
-
col_parts =
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
227
|
+
col_parts = with_inherited_mj_class(context, node) do
|
|
228
|
+
columns.each_with_index.map do |col, i|
|
|
229
|
+
col_attrs = resolved_attributes(col, context)
|
|
230
|
+
v_align = col_attrs["vertical-align"] || "top"
|
|
231
|
+
col_px = (box_width.to_f * widths[i] / 100.0).round
|
|
232
|
+
|
|
233
|
+
td_open = %(<!--[if mso | IE]><td class="" style="vertical-align:#{v_align};width:#{col_px}px;" ><![endif]-->)
|
|
234
|
+
td_close = %(<!--[if mso | IE]></td><![endif]-->)
|
|
235
|
+
|
|
236
|
+
col_html = if col.tag_name == "mj-group"
|
|
237
|
+
renderer.send(:render_group, col, context, widths[i])
|
|
238
|
+
else
|
|
239
|
+
context[:_column_width_pct] = widths[i]
|
|
240
|
+
render_node(col, context, parent: "mj-section")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
"#{td_open}\n#{col_html}\n#{td_close}"
|
|
244
|
+
end
|
|
243
245
|
end
|
|
244
246
|
|
|
245
247
|
([open_table, open_tr] + col_parts + [close_tr, close_table]).join("\n")
|
|
@@ -317,11 +319,13 @@ module MjmlRb
|
|
|
317
319
|
close_tr = %(<!--[if mso | IE]></tr><![endif]-->)
|
|
318
320
|
close_table = %(<!--[if mso | IE]></table><![endif]-->)
|
|
319
321
|
|
|
320
|
-
section_parts =
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
section_parts = with_inherited_mj_class(context, node) do
|
|
323
|
+
children.map do |child|
|
|
324
|
+
td_open = %(<!--[if mso | IE]><td class="" width="#{container_px}px" ><![endif]-->)
|
|
325
|
+
td_close = %(<!--[if mso | IE]></td><![endif]-->)
|
|
326
|
+
child_html = render_node(child, context, parent: "mj-wrapper")
|
|
327
|
+
"#{td_open}\n#{child_html}\n#{td_close}"
|
|
328
|
+
end
|
|
325
329
|
end
|
|
326
330
|
|
|
327
331
|
([open_table, open_tr] + section_parts + [close_tr, close_table]).join("\n")
|
|
@@ -139,14 +139,16 @@ module MjmlRb
|
|
|
139
139
|
outlook_open = %(<!--[if mso | IE]><table align="#{escape_attr(align)}" border="0" cellpadding="0" cellspacing="0" role="presentation" ><tr>)
|
|
140
140
|
outlook_close = %(</tr></table><![endif]-->)
|
|
141
141
|
|
|
142
|
-
children_html =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
142
|
+
children_html = with_inherited_mj_class(context, node) do
|
|
143
|
+
elements.map.with_index do |child, idx|
|
|
144
|
+
child_attrs = resolved_attributes(child, context)
|
|
145
|
+
merged_attrs = ELEMENT_DEFAULTS.merge(inherited).merge(child_attrs)
|
|
146
|
+
el_html = render_social_element(child, merged_attrs)
|
|
147
|
+
|
|
148
|
+
outlook_td_open = idx == 0 ? "<td>" : "</td><td>"
|
|
149
|
+
%(#{outlook_td_open}<![endif]--><table align="#{escape_attr(align)}" border="0" cellpadding="0" cellspacing="0" role="presentation" style="float:none;display:inline-table;"><tbody>#{el_html}</tbody></table><!--[if mso | IE]>)
|
|
150
|
+
end.join
|
|
151
|
+
end
|
|
150
152
|
|
|
151
153
|
%(#{outlook_open}#{children_html}</td>#{outlook_close})
|
|
152
154
|
end
|
|
@@ -155,11 +157,13 @@ module MjmlRb
|
|
|
155
157
|
inherited = inherited_attrs(social_attrs)
|
|
156
158
|
elements = social_element_children(node)
|
|
157
159
|
|
|
158
|
-
children_html =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
children_html = with_inherited_mj_class(context, node) do
|
|
161
|
+
elements.map do |child|
|
|
162
|
+
child_attrs = resolved_attributes(child, context)
|
|
163
|
+
merged_attrs = ELEMENT_DEFAULTS.merge(inherited).merge(child_attrs)
|
|
164
|
+
render_social_element(child, merged_attrs)
|
|
165
|
+
end.join
|
|
166
|
+
end
|
|
163
167
|
|
|
164
168
|
%(<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin:0px;"><tbody>#{children_html}</tbody></table>)
|
|
165
169
|
end
|
data/lib/mjml-rb/parser.rb
CHANGED
|
@@ -20,6 +20,7 @@ module MjmlRb
|
|
|
20
20
|
def parse(mjml, options = {})
|
|
21
21
|
opts = normalize_options(options)
|
|
22
22
|
xml = apply_preprocessors(mjml.to_s, opts[:preprocessors])
|
|
23
|
+
xml = wrap_raw_tags_in_cdata(xml)
|
|
23
24
|
xml = normalize_html_void_tags(xml)
|
|
24
25
|
xml = expand_includes(xml, opts) unless opts[:ignore_includes]
|
|
25
26
|
|
|
@@ -48,6 +49,7 @@ module MjmlRb
|
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
def expand_includes(xml, options)
|
|
52
|
+
xml = wrap_raw_tags_in_cdata(xml)
|
|
51
53
|
xml = normalize_html_void_tags(xml)
|
|
52
54
|
doc = Document.new(sanitize_bare_ampersands(xml))
|
|
53
55
|
includes = XPath.match(doc, "//mj-include")
|
|
@@ -64,7 +66,7 @@ module MjmlRb
|
|
|
64
66
|
replacement = if include_type == "html"
|
|
65
67
|
%(<mj-raw><![CDATA[#{escape_cdata(include_content)}]]></mj-raw>)
|
|
66
68
|
else
|
|
67
|
-
normalize_html_void_tags(strip_xml_declaration(include_content))
|
|
69
|
+
wrap_raw_tags_in_cdata(normalize_html_void_tags(strip_xml_declaration(include_content)))
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
fragment = Document.new(sanitize_bare_ampersands("<include-root>#{replacement}</include-root>"))
|
|
@@ -101,6 +103,18 @@ module MjmlRb
|
|
|
101
103
|
end
|
|
102
104
|
end
|
|
103
105
|
|
|
106
|
+
def wrap_raw_tags_in_cdata(content)
|
|
107
|
+
content.gsub(/<mj-raw(\s[^<>]*?)?>(.*?)<\/mj-raw>/mi) do
|
|
108
|
+
attrs = Regexp.last_match(1).to_s
|
|
109
|
+
inner = Regexp.last_match(2).to_s
|
|
110
|
+
if inner.include?("<![CDATA[")
|
|
111
|
+
"<mj-raw#{attrs}>#{inner}</mj-raw>"
|
|
112
|
+
else
|
|
113
|
+
"<mj-raw#{attrs}><![CDATA[#{escape_cdata(inner)}]]></mj-raw>"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
104
118
|
def escape_cdata(content)
|
|
105
119
|
content.to_s.gsub("]]>", "]]]]><![CDATA[>")
|
|
106
120
|
end
|
data/lib/mjml-rb/renderer.rb
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
require "cgi"
|
|
2
2
|
require "nokogiri"
|
|
3
3
|
require_relative "components/accordion"
|
|
4
|
+
require_relative "components/attributes"
|
|
4
5
|
require_relative "components/body"
|
|
5
6
|
require_relative "components/breakpoint"
|
|
6
7
|
require_relative "components/button"
|
|
7
8
|
require_relative "components/hero"
|
|
8
9
|
require_relative "components/image"
|
|
9
10
|
require_relative "components/navbar"
|
|
11
|
+
require_relative "components/raw"
|
|
10
12
|
require_relative "components/text"
|
|
11
13
|
require_relative "components/divider"
|
|
12
14
|
require_relative "components/html_attributes"
|
|
@@ -36,6 +38,7 @@ module MjmlRb
|
|
|
36
38
|
raise ArgumentError, "Missing <mj-body>" unless body
|
|
37
39
|
|
|
38
40
|
context = build_context(head, options)
|
|
41
|
+
context[:before_doctype] = root_file_start_raw(document)
|
|
39
42
|
context[:lang] = options[:lang] || document.attributes["lang"] || "und"
|
|
40
43
|
context[:dir] = options[:dir] || document.attributes["dir"] || "auto"
|
|
41
44
|
context[:column_widths] = {}
|
|
@@ -52,14 +55,17 @@ module MjmlRb
|
|
|
52
55
|
title: "",
|
|
53
56
|
preview: "",
|
|
54
57
|
breakpoint: "480px",
|
|
58
|
+
before_doctype: "",
|
|
59
|
+
head_raw: [],
|
|
55
60
|
head_styles: [],
|
|
56
61
|
inline_styles: [],
|
|
57
|
-
body_styles: [],
|
|
58
62
|
html_attributes: {},
|
|
59
63
|
fonts: DEFAULT_FONTS.merge(hash_or_empty(options[:fonts])),
|
|
60
64
|
global_defaults: {},
|
|
61
65
|
tag_defaults: {},
|
|
62
|
-
classes: {}
|
|
66
|
+
classes: {},
|
|
67
|
+
classes_default: {},
|
|
68
|
+
inherited_mj_class: ""
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
return context unless head
|
|
@@ -81,11 +87,15 @@ module MjmlRb
|
|
|
81
87
|
width = node.attributes["width"].to_s.strip
|
|
82
88
|
context[:breakpoint] = width unless width.empty?
|
|
83
89
|
when "mj-attributes"
|
|
84
|
-
|
|
90
|
+
if (component = component_for(node.tag_name)) && component.respond_to?(:handle_head)
|
|
91
|
+
component.handle_head(node, context)
|
|
92
|
+
else
|
|
93
|
+
absorb_attribute_node(node, context)
|
|
94
|
+
end
|
|
85
95
|
when "mj-html-attributes"
|
|
86
96
|
absorb_html_attributes_node(node, context)
|
|
87
97
|
when "mj-raw"
|
|
88
|
-
context[:
|
|
98
|
+
context[:head_raw] << raw_inner(node)
|
|
89
99
|
end
|
|
90
100
|
end
|
|
91
101
|
|
|
@@ -135,6 +145,8 @@ module MjmlRb
|
|
|
135
145
|
title = context[:title].to_s
|
|
136
146
|
preview = context[:preview]
|
|
137
147
|
head_styles = ([DOCUMENT_RESET_CSS] + unique_strings(context[:head_styles])).join("\n")
|
|
148
|
+
head_raw = Array(context[:head_raw]).join("\n")
|
|
149
|
+
before_doctype = context[:before_doctype].to_s
|
|
138
150
|
font_links = context[:fonts].values.uniq.map { |href| %(<link href="#{escape_attr(href)}" rel="stylesheet" type="text/css">) }.join("\n")
|
|
139
151
|
preview_block = preview.empty? ? "" : %(<div style="display:none;max-height:0;overflow:hidden;opacity:0;">#{escape_html(preview)}</div>)
|
|
140
152
|
html_attributes = { "lang" => context[:lang], "dir" => context[:dir] }
|
|
@@ -153,6 +165,7 @@ module MjmlRb
|
|
|
153
165
|
<title>#{escape_html(title)}</title>
|
|
154
166
|
#{font_links}
|
|
155
167
|
<style type="text/css">#{head_styles}</style>
|
|
168
|
+
#{head_raw}
|
|
156
169
|
</head>
|
|
157
170
|
<body style="#{body_style}">
|
|
158
171
|
#{preview_block}
|
|
@@ -162,11 +175,14 @@ module MjmlRb
|
|
|
162
175
|
HTML
|
|
163
176
|
|
|
164
177
|
html = apply_html_attributes(html, context)
|
|
165
|
-
apply_inline_styles(html, context)
|
|
178
|
+
html = apply_inline_styles(html, context)
|
|
179
|
+
before_doctype.empty? ? html : "#{before_doctype}\n#{html}"
|
|
166
180
|
end
|
|
167
181
|
|
|
168
182
|
def render_children(node, context, parent:)
|
|
169
|
-
|
|
183
|
+
with_inherited_mj_class(context, node) do
|
|
184
|
+
node.children.map { |child| render_node(child, context, parent: parent) }.join("\n")
|
|
185
|
+
end
|
|
170
186
|
end
|
|
171
187
|
|
|
172
188
|
def render_node(node, context, parent:)
|
|
@@ -181,8 +197,6 @@ module MjmlRb
|
|
|
181
197
|
case node.tag_name
|
|
182
198
|
when "mj-group"
|
|
183
199
|
render_group(node, context)
|
|
184
|
-
when "mj-raw"
|
|
185
|
-
raw_inner(node)
|
|
186
200
|
else
|
|
187
201
|
render_children(node, context, parent: node.tag_name)
|
|
188
202
|
end
|
|
@@ -191,10 +205,12 @@ module MjmlRb
|
|
|
191
205
|
def render_group(node, context, width_pct = 100)
|
|
192
206
|
items = node.element_children.select { |e| e.tag_name == "mj-column" }
|
|
193
207
|
widths = compute_column_widths(items, context)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
with_inherited_mj_class(context, node) do
|
|
209
|
+
items.each_with_index.map do |item, i|
|
|
210
|
+
context[:_column_width_pct] = widths[i]
|
|
211
|
+
render_node(item, context, parent: "mj-group")
|
|
212
|
+
end.join("\n")
|
|
213
|
+
end
|
|
198
214
|
end
|
|
199
215
|
|
|
200
216
|
def compute_column_widths(columns, context)
|
|
@@ -386,12 +402,14 @@ module MjmlRb
|
|
|
386
402
|
registry = {}
|
|
387
403
|
# Register component classes here as they are implemented.
|
|
388
404
|
register_component(registry, Components::Body.new(self))
|
|
405
|
+
register_component(registry, Components::Attributes.new(self))
|
|
389
406
|
register_component(registry, Components::Breakpoint.new(self))
|
|
390
407
|
register_component(registry, Components::Accordion.new(self))
|
|
391
408
|
register_component(registry, Components::Button.new(self))
|
|
392
409
|
register_component(registry, Components::Hero.new(self))
|
|
393
410
|
register_component(registry, Components::Image.new(self))
|
|
394
411
|
register_component(registry, Components::Navbar.new(self))
|
|
412
|
+
register_component(registry, Components::Raw.new(self))
|
|
395
413
|
register_component(registry, Components::Text.new(self))
|
|
396
414
|
register_component(registry, Components::Divider.new(self))
|
|
397
415
|
register_component(registry, Components::Table.new(self))
|
|
@@ -413,8 +431,18 @@ module MjmlRb
|
|
|
413
431
|
attrs.merge!(context[:tag_defaults][node.tag_name] || {})
|
|
414
432
|
|
|
415
433
|
node_classes = node.attributes["mj-class"].to_s.split(/\s+/).reject(&:empty?)
|
|
416
|
-
node_classes.
|
|
417
|
-
|
|
434
|
+
class_attrs = node_classes.each_with_object({}) do |klass, memo|
|
|
435
|
+
mj_class_attrs = (context[:classes] || {})[klass] || {}
|
|
436
|
+
if memo["css-class"] && mj_class_attrs["css-class"]
|
|
437
|
+
memo["css-class"] = "#{memo["css-class"]} #{mj_class_attrs["css-class"]}"
|
|
438
|
+
end
|
|
439
|
+
memo.merge!(mj_class_attrs)
|
|
440
|
+
end
|
|
441
|
+
attrs.merge!(class_attrs)
|
|
442
|
+
|
|
443
|
+
inherited_classes = context[:inherited_mj_class].to_s.split(/\s+/).reject(&:empty?)
|
|
444
|
+
inherited_classes.each do |klass|
|
|
445
|
+
attrs.merge!(((context[:classes_default] || {})[klass] || {})[node.tag_name] || {})
|
|
418
446
|
end
|
|
419
447
|
|
|
420
448
|
attrs.merge!(node.attributes)
|
|
@@ -476,6 +504,24 @@ module MjmlRb
|
|
|
476
504
|
node.element_children.find { |child| child.tag_name == tag_name }
|
|
477
505
|
end
|
|
478
506
|
|
|
507
|
+
def with_inherited_mj_class(context, node)
|
|
508
|
+
previous = context[:inherited_mj_class]
|
|
509
|
+
current = node.attributes["mj-class"]
|
|
510
|
+
context[:inherited_mj_class] = (current && !current.empty?) ? current : previous
|
|
511
|
+
yield
|
|
512
|
+
ensure
|
|
513
|
+
context[:inherited_mj_class] = previous
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def root_file_start_raw(document)
|
|
517
|
+
document.element_children.filter_map do |child|
|
|
518
|
+
next unless child.tag_name == "mj-raw"
|
|
519
|
+
next unless child.attributes["position"] == "file-start"
|
|
520
|
+
|
|
521
|
+
raw_inner(child)
|
|
522
|
+
end.join("\n")
|
|
523
|
+
end
|
|
524
|
+
|
|
479
525
|
def contains_tag?(node, tag_name)
|
|
480
526
|
return false unless node.respond_to?(:tag_name)
|
|
481
527
|
return true if node.tag_name == tag_name
|
data/lib/mjml-rb/version.rb
CHANGED
data/mjml-rb.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.license = "MIT"
|
|
12
12
|
spec.required_ruby_version = ">= 3.0"
|
|
13
13
|
|
|
14
|
-
spec.homepage = "https://github.com/faraquet/mjml-
|
|
14
|
+
spec.homepage = "https://github.com/faraquet/mjml-rb"
|
|
15
15
|
spec.files = Dir.chdir(__dir__) do
|
|
16
16
|
Dir.glob("{bin,lib}/**/*") + ["Gemfile", "LICENSE", "mjml-rb.gemspec", "README.md"]
|
|
17
17
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mjml-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Andriichuk
|
|
@@ -54,6 +54,7 @@ files:
|
|
|
54
54
|
- lib/mjml-rb/cli.rb
|
|
55
55
|
- lib/mjml-rb/compiler.rb
|
|
56
56
|
- lib/mjml-rb/components/accordion.rb
|
|
57
|
+
- lib/mjml-rb/components/attributes.rb
|
|
57
58
|
- lib/mjml-rb/components/base.rb
|
|
58
59
|
- lib/mjml-rb/components/body.rb
|
|
59
60
|
- lib/mjml-rb/components/breakpoint.rb
|
|
@@ -64,6 +65,7 @@ files:
|
|
|
64
65
|
- lib/mjml-rb/components/html_attributes.rb
|
|
65
66
|
- lib/mjml-rb/components/image.rb
|
|
66
67
|
- lib/mjml-rb/components/navbar.rb
|
|
68
|
+
- lib/mjml-rb/components/raw.rb
|
|
67
69
|
- lib/mjml-rb/components/section.rb
|
|
68
70
|
- lib/mjml-rb/components/social.rb
|
|
69
71
|
- lib/mjml-rb/components/spacer.rb
|
|
@@ -77,7 +79,7 @@ files:
|
|
|
77
79
|
- lib/mjml-rb/validator.rb
|
|
78
80
|
- lib/mjml-rb/version.rb
|
|
79
81
|
- mjml-rb.gemspec
|
|
80
|
-
homepage: https://github.com/faraquet/mjml-
|
|
82
|
+
homepage: https://github.com/faraquet/mjml-rb
|
|
81
83
|
licenses:
|
|
82
84
|
- MIT
|
|
83
85
|
metadata: {}
|