mjml-rb 0.2.18 → 0.2.20
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/lib/mjml-rb/components/section.rb +171 -93
- data/lib/mjml-rb/components/table.rb +23 -0
- data/lib/mjml-rb/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 97ca7a3d968fdd4dee9f34ef7806283cb1a53fda0810dcf2456770f298831b35
|
|
4
|
+
data.tar.gz: 53a50ac16e06c719fa6533de98e43c35233a1ab0470bf81c366c1be62726c2b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22bb47ef2611887dbf6a19ee38cfb7da366060b632bf3cfb9ea548798a29e120287ae4de035255560d36dc5c407eeef3c04a007695b297da9230ecbd17d0dcb1
|
|
7
|
+
data.tar.gz: f9f14bde5dd817a322e40da7b0de8645185c6b25e884d9d3aa45df7f3a5fca94eb92723204f4a193fc696bfe93f1b31a2033bf99042a3637adc7d0d61aa65343
|
|
@@ -16,27 +16,29 @@ module MjmlRb
|
|
|
16
16
|
"border" => "string",
|
|
17
17
|
"border-bottom" => "string",
|
|
18
18
|
"border-left" => "string",
|
|
19
|
-
"border-radius" => "
|
|
19
|
+
"border-radius" => "string",
|
|
20
20
|
"border-right" => "string",
|
|
21
21
|
"border-top" => "string",
|
|
22
22
|
"direction" => "enum(ltr,rtl)",
|
|
23
|
+
"full-width" => "enum(full-width,false,)",
|
|
23
24
|
"padding" => "unit(px,%){1,4}",
|
|
24
25
|
"padding-bottom" => "unit(px,%)",
|
|
25
26
|
"padding-left" => "unit(px,%)",
|
|
26
27
|
"padding-right" => "unit(px,%)",
|
|
27
28
|
"padding-top" => "unit(px,%)",
|
|
28
|
-
"text-align" => "enum(left,center,right)"
|
|
29
|
+
"text-align" => "enum(left,center,right)",
|
|
30
|
+
"text-padding" => "unit(px,%){1,4}"
|
|
29
31
|
}.freeze
|
|
30
32
|
|
|
31
33
|
WRAPPER_ALLOWED_ATTRIBUTES = SECTION_ALLOWED_ATTRIBUTES.merge(
|
|
32
34
|
"gap" => "unit(px)",
|
|
33
|
-
"full-width" => "enum(full-width)"
|
|
34
35
|
).freeze
|
|
35
36
|
|
|
36
37
|
DEFAULT_ATTRIBUTES = {
|
|
37
38
|
"direction" => "ltr",
|
|
38
39
|
"padding" => "20px 0",
|
|
39
40
|
"text-align" => "center",
|
|
41
|
+
"text-padding" => "4px 4px 4px 0",
|
|
40
42
|
"background-repeat" => "repeat",
|
|
41
43
|
"background-size" => "auto",
|
|
42
44
|
"background-position" => "top center"
|
|
@@ -61,7 +63,12 @@ module MjmlRb
|
|
|
61
63
|
when "mj-wrapper"
|
|
62
64
|
render_wrapper(node, context, attrs)
|
|
63
65
|
else
|
|
64
|
-
|
|
66
|
+
a = self.class.default_attributes.merge(attrs)
|
|
67
|
+
if a["full-width"] == "full-width"
|
|
68
|
+
render_full_width_section(node, context, a)
|
|
69
|
+
else
|
|
70
|
+
render_section(node, context, a)
|
|
71
|
+
end
|
|
65
72
|
end
|
|
66
73
|
end
|
|
67
74
|
|
|
@@ -112,6 +119,10 @@ module MjmlRb
|
|
|
112
119
|
end
|
|
113
120
|
end
|
|
114
121
|
|
|
122
|
+
def has_border_radius?(value)
|
|
123
|
+
value && !value.to_s.strip.empty?
|
|
124
|
+
end
|
|
125
|
+
|
|
115
126
|
# Merge adjacent Outlook conditional comments. Applied locally within
|
|
116
127
|
# each section/wrapper to avoid incorrectly merging across sibling sections.
|
|
117
128
|
def merge_outlook_conditionals(html)
|
|
@@ -205,7 +216,7 @@ module MjmlRb
|
|
|
205
216
|
"right" => "100%", "bottom" => "100%"
|
|
206
217
|
}.freeze
|
|
207
218
|
|
|
208
|
-
def render_with_background(section_html, a, container_px)
|
|
219
|
+
def render_with_background(section_html, a, container_px, full_width: false)
|
|
209
220
|
bg_url = a["background-url"]
|
|
210
221
|
bg_color = a["background-color"]
|
|
211
222
|
bg_repeat = a["background-repeat"] || "repeat"
|
|
@@ -247,7 +258,9 @@ module MjmlRb
|
|
|
247
258
|
fill_pairs << ["aspect", v_size_attrs[:aspect]] if v_size_attrs[:aspect]
|
|
248
259
|
fill_str = fill_pairs.map { |(k, v)| %(#{k}="#{escape_attr(v.to_s)}") }.join(" ")
|
|
249
260
|
|
|
250
|
-
|
|
261
|
+
rect_style = full_width ? "mso-width-percent:1000;" : "width:#{container_px}px;"
|
|
262
|
+
|
|
263
|
+
%(<!--[if mso | IE]><v:rect style="#{rect_style}" xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false"><v:fill #{fill_str} /><v:textbox style="mso-fit-shape-to-text:true" inset="0,0,0,0"><![endif]-->) +
|
|
251
264
|
section_html +
|
|
252
265
|
%(<!--[if mso | IE]></v:textbox></v:rect><![endif]-->)
|
|
253
266
|
end
|
|
@@ -288,8 +301,7 @@ module MjmlRb
|
|
|
288
301
|
|
|
289
302
|
# ── mj-section ─────────────────────────────────────────────────────────
|
|
290
303
|
|
|
291
|
-
def render_section(node, context,
|
|
292
|
-
a = self.class.default_attributes.merge(attrs)
|
|
304
|
+
def render_section(node, context, a)
|
|
293
305
|
container_px = parse_px(context[:container_width] || "600px")
|
|
294
306
|
css_class = a["css-class"]
|
|
295
307
|
bg_color = a["background-color"]
|
|
@@ -304,7 +316,78 @@ module MjmlRb
|
|
|
304
316
|
pad_right = parse_padding_side(a, "right")
|
|
305
317
|
box_width = container_px - pad_left - pad_right - border_left - border_right
|
|
306
318
|
|
|
307
|
-
|
|
319
|
+
render_before = render_section_before(css_class, container_px, bg_color, wrapper_gap)
|
|
320
|
+
|
|
321
|
+
section_html = build_section_html(
|
|
322
|
+
node,
|
|
323
|
+
context,
|
|
324
|
+
a,
|
|
325
|
+
container_px: container_px,
|
|
326
|
+
box_width: box_width,
|
|
327
|
+
css_class: css_class,
|
|
328
|
+
bg_color: bg_color,
|
|
329
|
+
border_radius: border_radius,
|
|
330
|
+
bg_has: bg_has,
|
|
331
|
+
wrapper_gap: wrapper_gap,
|
|
332
|
+
full_width: false
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
render_after = %(<!--[if mso | IE]></td></tr></table><![endif]-->)
|
|
336
|
+
|
|
337
|
+
body = bg_has ? render_with_background(section_html, a, container_px) : section_html
|
|
338
|
+
|
|
339
|
+
"#{render_before}\n#{body}\n#{render_after}"
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def render_full_width_section(node, context, a)
|
|
343
|
+
container_px = parse_px(context[:container_width] || "600px")
|
|
344
|
+
css_class = a["css-class"]
|
|
345
|
+
bg_color = a["background-color"]
|
|
346
|
+
border_radius = a["border-radius"]
|
|
347
|
+
bg_has = has_background?(a)
|
|
348
|
+
wrapper_gap = context[:_wrapper_child_gap]
|
|
349
|
+
|
|
350
|
+
border_left = parse_border_width(a["border-left"] || a["border"])
|
|
351
|
+
border_right = parse_border_width(a["border-right"] || a["border"])
|
|
352
|
+
pad_left = parse_padding_side(a, "left")
|
|
353
|
+
pad_right = parse_padding_side(a, "right")
|
|
354
|
+
box_width = container_px - pad_left - pad_right - border_left - border_right
|
|
355
|
+
|
|
356
|
+
render_before = render_section_before(css_class, container_px, bg_color, wrapper_gap)
|
|
357
|
+
section_html = build_section_html(
|
|
358
|
+
node,
|
|
359
|
+
context,
|
|
360
|
+
a,
|
|
361
|
+
container_px: container_px,
|
|
362
|
+
box_width: box_width,
|
|
363
|
+
css_class: nil,
|
|
364
|
+
bg_color: bg_color,
|
|
365
|
+
border_radius: border_radius,
|
|
366
|
+
bg_has: bg_has,
|
|
367
|
+
wrapper_gap: wrapper_gap,
|
|
368
|
+
full_width: true
|
|
369
|
+
)
|
|
370
|
+
render_after = %(<!--[if mso | IE]></td></tr></table><![endif]-->)
|
|
371
|
+
|
|
372
|
+
inner = "#{render_before}\n#{section_html}\n#{render_after}"
|
|
373
|
+
body = bg_has ? render_with_background(inner, a, container_px, full_width: true) : inner
|
|
374
|
+
|
|
375
|
+
outer_style = full_width_table_style(a)
|
|
376
|
+
outer_attrs = {
|
|
377
|
+
"align" => "center",
|
|
378
|
+
"class" => css_class,
|
|
379
|
+
"background" => bg_has ? a["background-url"] : nil,
|
|
380
|
+
"border" => "0",
|
|
381
|
+
"cellpadding" => "0",
|
|
382
|
+
"cellspacing" => "0",
|
|
383
|
+
"role" => "presentation",
|
|
384
|
+
"style" => outer_style
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
%(<table#{html_attrs(outer_attrs)}><tbody><tr><td>#{body}</td></tr></tbody></table>)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def render_section_before(css_class, container_px, bg_color, wrapper_gap)
|
|
308
391
|
outlook_class = css_class ? "#{css_class}-outlook" : ""
|
|
309
392
|
before_pairs = [
|
|
310
393
|
["align", "center"],
|
|
@@ -318,89 +401,68 @@ module MjmlRb
|
|
|
318
401
|
]
|
|
319
402
|
before_pairs << ["bgcolor", bg_color] if bg_color
|
|
320
403
|
|
|
321
|
-
|
|
404
|
+
%(<!--[if mso | IE]><table#{outlook_attrs(before_pairs)}><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->)
|
|
405
|
+
end
|
|
322
406
|
|
|
323
|
-
|
|
407
|
+
def build_section_html(node, context, a, container_px:, box_width:, css_class:, bg_color:, border_radius:, bg_has:, wrapper_gap:, full_width:)
|
|
324
408
|
border_val = a["border"]
|
|
325
409
|
border_val = nil if border_val.nil? || border_val.to_s.strip.empty? || border_val.to_s.strip == "none"
|
|
410
|
+
has_border_radius = has_border_radius?(border_radius)
|
|
411
|
+
|
|
412
|
+
background_styles = if bg_has
|
|
413
|
+
bg_value = get_background(a)
|
|
414
|
+
{
|
|
415
|
+
"background" => bg_value,
|
|
416
|
+
"background-position" => get_background_string(a),
|
|
417
|
+
"background-repeat" => a["background-repeat"],
|
|
418
|
+
"background-size" => a["background-size"]
|
|
419
|
+
}
|
|
420
|
+
else
|
|
421
|
+
{
|
|
422
|
+
"background" => bg_color,
|
|
423
|
+
"background-color" => bg_color
|
|
424
|
+
}
|
|
425
|
+
end
|
|
326
426
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
"
|
|
339
|
-
"
|
|
340
|
-
"
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"padding-right" => a["padding-right"],
|
|
362
|
-
"padding-bottom" => a["padding-bottom"],
|
|
363
|
-
"padding-left" => a["padding-left"],
|
|
364
|
-
"text-align" => a["text-align"]
|
|
365
|
-
)
|
|
366
|
-
else
|
|
367
|
-
div_style = style_join(
|
|
368
|
-
"background" => bg_color,
|
|
369
|
-
"background-color" => bg_color,
|
|
370
|
-
"margin" => "0px auto",
|
|
371
|
-
"margin-top" => wrapper_gap,
|
|
372
|
-
"max-width" => "#{container_px}px"
|
|
373
|
-
)
|
|
374
|
-
table_style = style_join(
|
|
375
|
-
"background" => bg_color,
|
|
376
|
-
"background-color" => bg_color,
|
|
377
|
-
"border-radius" => border_radius,
|
|
378
|
-
"width" => "100%"
|
|
379
|
-
)
|
|
380
|
-
td_style = style_join(
|
|
381
|
-
"border" => border_val,
|
|
382
|
-
"border-top" => a["border-top"],
|
|
383
|
-
"border-right" => a["border-right"],
|
|
384
|
-
"border-bottom" => a["border-bottom"],
|
|
385
|
-
"border-left" => a["border-left"],
|
|
386
|
-
"border-radius" => border_radius,
|
|
387
|
-
"background" => bg_color,
|
|
388
|
-
"background-color" => bg_color,
|
|
389
|
-
"direction" => a["direction"],
|
|
390
|
-
"font-size" => "0px",
|
|
391
|
-
"padding" => a["padding"],
|
|
392
|
-
"padding-top" => a["padding-top"],
|
|
393
|
-
"padding-right" => a["padding-right"],
|
|
394
|
-
"padding-bottom" => a["padding-bottom"],
|
|
395
|
-
"padding-left" => a["padding-left"],
|
|
396
|
-
"text-align" => a["text-align"]
|
|
397
|
-
)
|
|
398
|
-
end
|
|
427
|
+
div_style = style_join(
|
|
428
|
+
{
|
|
429
|
+
"border-radius" => border_radius,
|
|
430
|
+
"overflow" => (has_border_radius ? "hidden" : nil),
|
|
431
|
+
"margin" => "0px auto",
|
|
432
|
+
"margin-top" => wrapper_gap,
|
|
433
|
+
"max-width" => "#{container_px}px"
|
|
434
|
+
}.merge(full_width ? {} : background_styles)
|
|
435
|
+
)
|
|
436
|
+
table_style = style_join(
|
|
437
|
+
{
|
|
438
|
+
"border-radius" => border_radius,
|
|
439
|
+
"border-collapse" => (has_border_radius ? "separate" : nil),
|
|
440
|
+
"width" => "100%"
|
|
441
|
+
}.merge(full_width ? {} : background_styles)
|
|
442
|
+
)
|
|
443
|
+
td_style = style_join(
|
|
444
|
+
"border" => border_val,
|
|
445
|
+
"border-top" => a["border-top"],
|
|
446
|
+
"border-right" => a["border-right"],
|
|
447
|
+
"border-bottom" => a["border-bottom"],
|
|
448
|
+
"border-left" => a["border-left"],
|
|
449
|
+
"border-radius" => border_radius,
|
|
450
|
+
"background" => (full_width ? nil : bg_color),
|
|
451
|
+
"background-color" => (full_width ? nil : bg_color),
|
|
452
|
+
"direction" => a["direction"],
|
|
453
|
+
"font-size" => "0px",
|
|
454
|
+
"padding" => a["padding"],
|
|
455
|
+
"padding-top" => a["padding-top"],
|
|
456
|
+
"padding-right" => a["padding-right"],
|
|
457
|
+
"padding-bottom" => a["padding-bottom"],
|
|
458
|
+
"padding-left" => a["padding-left"],
|
|
459
|
+
"text-align" => a["text-align"]
|
|
460
|
+
)
|
|
399
461
|
|
|
400
462
|
div_attrs = {"class" => css_class, "style" => div_style}
|
|
401
463
|
table_attrs = {
|
|
402
464
|
"align" => "center",
|
|
403
|
-
"background" => bg_has ? a["background-url"] : nil,
|
|
465
|
+
"background" => (bg_has && !full_width ? a["background-url"] : nil),
|
|
404
466
|
"border" => "0",
|
|
405
467
|
"cellpadding" => "0",
|
|
406
468
|
"cellspacing" => "0",
|
|
@@ -414,20 +476,29 @@ module MjmlRb
|
|
|
414
476
|
"style" => td_style
|
|
415
477
|
}
|
|
416
478
|
inner = merge_outlook_conditionals(render_section_columns(node, context, box_width))
|
|
417
|
-
|
|
418
|
-
# Wrap in innerDiv when background image is present (prevents Yahoo whitespace gaps)
|
|
419
479
|
inner_content = bg_has ? %(<div style="line-height:0;font-size:0">#{inner}</div>) : inner
|
|
420
480
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
%(<table#{html_attrs(table_attrs)}>) +
|
|
424
|
-
%(<tbody><tr><td#{html_attrs(td_attrs)}>#{inner_content}</td></tr></tbody></table></div>)
|
|
481
|
+
%(<div#{html_attrs(div_attrs)}><table#{html_attrs(table_attrs)}><tbody><tr><td#{html_attrs(td_attrs)}>#{inner_content}</td></tr></tbody></table></div>)
|
|
482
|
+
end
|
|
425
483
|
|
|
426
|
-
|
|
484
|
+
def full_width_table_style(a)
|
|
485
|
+
style = {"width" => "100%"}
|
|
427
486
|
|
|
428
|
-
|
|
487
|
+
if has_background?(a)
|
|
488
|
+
style.merge!(
|
|
489
|
+
"background" => get_background(a),
|
|
490
|
+
"background-position" => get_background_string(a),
|
|
491
|
+
"background-repeat" => a["background-repeat"],
|
|
492
|
+
"background-size" => a["background-size"]
|
|
493
|
+
)
|
|
494
|
+
else
|
|
495
|
+
style.merge!(
|
|
496
|
+
"background" => a["background-color"],
|
|
497
|
+
"background-color" => a["background-color"]
|
|
498
|
+
)
|
|
499
|
+
end
|
|
429
500
|
|
|
430
|
-
|
|
501
|
+
style_join(style)
|
|
431
502
|
end
|
|
432
503
|
|
|
433
504
|
# Generate Outlook IE conditional wrappers around each column/group.
|
|
@@ -468,8 +539,10 @@ module MjmlRb
|
|
|
468
539
|
container_px = parse_px(context[:container_width] || "600px")
|
|
469
540
|
css_class = a["css-class"]
|
|
470
541
|
bg_color = a["background-color"]
|
|
542
|
+
border_radius = a["border-radius"]
|
|
471
543
|
full_width = a["full-width"] == "full-width"
|
|
472
544
|
wrapper_gap = context[:_wrapper_child_gap]
|
|
545
|
+
has_border_radius = has_border_radius?(border_radius)
|
|
473
546
|
|
|
474
547
|
# renderBefore — same structure as section
|
|
475
548
|
outlook_class = css_class ? "#{css_class}-outlook" : ""
|
|
@@ -490,6 +563,8 @@ module MjmlRb
|
|
|
490
563
|
div_style = style_join(
|
|
491
564
|
"background" => bg_color,
|
|
492
565
|
"background-color" => bg_color,
|
|
566
|
+
"border-radius" => border_radius,
|
|
567
|
+
"overflow" => (has_border_radius ? "hidden" : nil),
|
|
493
568
|
"margin" => "0px auto",
|
|
494
569
|
"margin-top" => wrapper_gap,
|
|
495
570
|
"max-width" => (full_width ? nil : "#{container_px}px")
|
|
@@ -498,10 +573,13 @@ module MjmlRb
|
|
|
498
573
|
table_style = style_join(
|
|
499
574
|
"background" => bg_color,
|
|
500
575
|
"background-color" => bg_color,
|
|
576
|
+
"border-radius" => border_radius,
|
|
577
|
+
"border-collapse" => (has_border_radius ? "separate" : nil),
|
|
501
578
|
"width" => "100%"
|
|
502
579
|
)
|
|
503
580
|
|
|
504
581
|
td_style = style_join(
|
|
582
|
+
"border-radius" => border_radius,
|
|
505
583
|
"direction" => a["direction"],
|
|
506
584
|
"font-size" => "0px",
|
|
507
585
|
"padding" => a["padding"],
|
|
@@ -5,6 +5,28 @@ module MjmlRb
|
|
|
5
5
|
class Table < Base
|
|
6
6
|
TAGS = ["mj-table"].freeze
|
|
7
7
|
|
|
8
|
+
ALLOWED_ATTRIBUTES = {
|
|
9
|
+
"align" => "enum(left,right,center)",
|
|
10
|
+
"border" => "string",
|
|
11
|
+
"cellpadding" => "string",
|
|
12
|
+
"cellspacing" => "string",
|
|
13
|
+
"color" => "color",
|
|
14
|
+
"container-background-color" => "color",
|
|
15
|
+
"font-family" => "string",
|
|
16
|
+
"font-size" => "string",
|
|
17
|
+
"font-weight" => "string",
|
|
18
|
+
"line-height" => "string",
|
|
19
|
+
"padding" => "unit(px,%){1,4}",
|
|
20
|
+
"padding-top" => "unit(px,%)",
|
|
21
|
+
"padding-right" => "unit(px,%)",
|
|
22
|
+
"padding-bottom" => "unit(px,%)",
|
|
23
|
+
"padding-left" => "unit(px,%)",
|
|
24
|
+
"role" => "string",
|
|
25
|
+
"table-layout" => "enum(auto,fixed)",
|
|
26
|
+
"vertical-align" => "enum(top,bottom,middle)",
|
|
27
|
+
"width" => "string"
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
8
30
|
DEFAULTS = {
|
|
9
31
|
"align" => "left",
|
|
10
32
|
"border" => "none",
|
|
@@ -49,6 +71,7 @@ module MjmlRb
|
|
|
49
71
|
"color" => a["color"],
|
|
50
72
|
"font-family" => a["font-family"],
|
|
51
73
|
"font-size" => a["font-size"],
|
|
74
|
+
"font-weight" => a["font-weight"],
|
|
52
75
|
"line-height" => a["line-height"],
|
|
53
76
|
"table-layout" => a["table-layout"],
|
|
54
77
|
"width" => a["width"],
|
data/lib/mjml-rb/version.rb
CHANGED