mjml-rb 0.2.30 → 0.2.32

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60152ad5a5d0db48c258f49e6f8b0f3af02fafc7a792b2eb5466a9ea9a2e5648
4
- data.tar.gz: 39e06f0b1987f17f75a03a18b20e35926a46972d8a128be50dc903d52643629b
3
+ metadata.gz: 5f70adefb32b3a7bb2df503b8749cfbd2fde06bb819c6bfc3e587a1a8eb65772
4
+ data.tar.gz: ef1c17cd44ec046c37c75b3a6650610161fe74f71824dfe64b72db2958e24f77
5
5
  SHA512:
6
- metadata.gz: 68d49bbb4d009ab6438e7a768f9fcc83c585d9bb7b95ab7966eed475cfea825226adb5af16c86f6a457513b3abd46f4c3b8db3ff32fcc881cdc5a7c5d2993fea
7
- data.tar.gz: 104af894e026ca52a8132f8797ed57d9541da6ebe47f38cc24d61a67daf248df81f972fb90f9d5fb51eba3cc4294a428621db8f287fa0fd2a781a1de6de7a95c
6
+ metadata.gz: 64230656cfbf00b6d220cb579e078cddda30edeb5ccf1faa9f4245ed389ba76f6fe1990a10f48a6ccc2e67ecc3deb0c09cb6d4d146079d992e08cf5376f55c60
7
+ data.tar.gz: cb12486d8dd7571e11f19bbed0054a7698def27350678874c65c07688baf28a5b2e19f5bb31db4d4ce7c761e3509114c6d2b85155567a842e1812a1d82ba985b
@@ -42,11 +42,18 @@ module MjmlRb
42
42
  css_class = attrs["css-class"]
43
43
  a = self.class.default_attributes.merge(attrs)
44
44
 
45
- pct_str = width_pct.to_f.to_s.sub(/\.?0+$/, "")
46
- col_class_suffix = pct_str.gsub(".", "-")
47
- context[:column_widths][col_class_suffix] = pct_str if context[:column_widths]
45
+ explicit_width = a["width"]
46
+ if explicit_width && explicit_width.strip.end_with?("px")
47
+ px_val = explicit_width.to_f.to_s.sub(/\.?0+$/, "")
48
+ col_class_name = "mj-column-px-#{px_val.gsub('.', '-')}"
49
+ context[:column_widths][col_class_name] = "#{px_val}px" if context[:column_widths]
50
+ else
51
+ pct_str = width_pct.to_f.to_s.sub(/\.?0+$/, "")
52
+ col_class_name = "mj-column-per-#{pct_str.gsub('.', '-')}"
53
+ context[:column_widths][col_class_name] = "#{pct_str}%" if context[:column_widths]
54
+ end
48
55
 
49
- col_class = "mj-column-per-#{col_class_suffix} mj-outlook-group-fix"
56
+ col_class = "#{col_class_name} mj-outlook-group-fix"
50
57
  col_class = "#{col_class} #{css_class}" if css_class && !css_class.empty?
51
58
 
52
59
  vertical_align = a["vertical-align"]
@@ -26,10 +26,10 @@ module MjmlRb
26
26
  css_class = a["css-class"]
27
27
 
28
28
  pct_str = width_pct.to_f.to_s.sub(/\.?0+$/, "")
29
- class_suffix = pct_str.gsub(".", "-")
30
- context[:column_widths][class_suffix] = pct_str if context[:column_widths]
29
+ col_class_name = "mj-column-per-#{pct_str.gsub('.', '-')}"
30
+ context[:column_widths][col_class_name] = "#{pct_str}%" if context[:column_widths]
31
31
 
32
- group_class = "mj-column-per-#{class_suffix} mj-outlook-group-fix"
32
+ group_class = "#{col_class_name} mj-outlook-group-fix"
33
33
  group_class = "#{group_class} #{css_class}" if css_class && !css_class.empty?
34
34
 
35
35
  group_width = group_container_width(context, a, width_pct)
@@ -123,6 +123,12 @@ module MjmlRb
123
123
  value && !value.to_s.strip.empty?
124
124
  end
125
125
 
126
+ # Matches npm's suffixCssClasses: "foo bar" → "foo-outlook bar-outlook"
127
+ def suffix_css_classes(classes, suffix = "outlook")
128
+ return "" unless classes && !classes.strip.empty?
129
+ classes.split(" ").map { |c| "#{c}-#{suffix}" }.join(" ")
130
+ end
131
+
126
132
  # Merge adjacent Outlook conditional comments. Applied locally within
127
133
  # each section/wrapper to avoid incorrectly merging across sibling sections.
128
134
  def merge_outlook_conditionals(html)
@@ -388,7 +394,8 @@ module MjmlRb
388
394
  end
389
395
 
390
396
  def render_section_before(css_class, container_px, bg_color, wrapper_gap)
391
- outlook_class = css_class ? "#{css_class}-outlook" : ""
397
+ outlook_class = suffix_css_classes(css_class)
398
+ has_gap = wrapper_gap && !wrapper_gap.to_s.strip.empty?
392
399
  before_pairs = [
393
400
  ["align", "center"],
394
401
  ["border", "0"],
@@ -399,7 +406,7 @@ module MjmlRb
399
406
  ["style", style_join("width" => "#{container_px}px", "padding-top" => wrapper_gap) + ";"],
400
407
  ["width", container_px.to_s]
401
408
  ]
402
- before_pairs << ["bgcolor", bg_color] if bg_color
409
+ before_pairs << ["bgcolor", bg_color] if bg_color && !has_gap
403
410
 
404
411
  %(<!--[if mso | IE]><table#{outlook_attrs(before_pairs)}><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->)
405
412
  end
@@ -502,7 +509,12 @@ module MjmlRb
502
509
  end
503
510
 
504
511
  # Generate Outlook IE conditional wrappers around each column/group.
512
+ # Mirrors npm section's getChildContext() by setting containerWidth to
513
+ # the section's box width so that column children compute correct widths.
505
514
  def render_section_columns(node, context, box_width)
515
+ previous_container_width = context[:container_width]
516
+ context[:container_width] = "#{box_width}px"
517
+
506
518
  columns = node.element_children.select { |e| %w[mj-column mj-group].include?(e.tag_name) }
507
519
  return render_children(node, context, parent: "mj-section") if columns.empty?
508
520
 
@@ -517,9 +529,16 @@ module MjmlRb
517
529
  columns.each_with_index.map do |col, i|
518
530
  col_attrs = resolved_attributes(col, context)
519
531
  v_align = col_attrs["vertical-align"] || "top"
520
- col_px = (box_width.to_f * widths[i] / 100.0).round
521
-
522
- td_open = %(<!--[if mso | IE]><td class="" style="vertical-align:#{v_align};width:#{col_px}px;" ><![endif]-->)
532
+ col_css = col_attrs["css-class"]
533
+ outlook_class = col_css ? col_css.split(" ").map { |c| "#{c}-outlook" }.join(" ") : ""
534
+ col_width_attr = col_attrs["width"]
535
+ col_px = if col_width_attr && col_width_attr.strip.end_with?("px")
536
+ col_width_attr.to_f.round
537
+ else
538
+ (box_width.to_f * widths[i] / 100.0).round
539
+ end
540
+
541
+ td_open = %(<!--[if mso | IE]><td class="#{escape_attr(outlook_class)}" style="vertical-align:#{v_align};width:#{col_px}px;" ><![endif]-->)
523
542
  td_close = %(<!--[if mso | IE]></td><![endif]-->)
524
543
 
525
544
  context[:_column_width_pct] = widths[i]
@@ -530,6 +549,8 @@ module MjmlRb
530
549
  end
531
550
 
532
551
  ([open_table, open_tr] + col_parts + [close_tr, close_table]).join("\n")
552
+ ensure
553
+ context[:container_width] = previous_container_width
533
554
  end
534
555
 
535
556
  # ── mj-wrapper ─────────────────────────────────────────────────────────
@@ -562,7 +583,8 @@ module MjmlRb
562
583
  end
563
584
 
564
585
  # renderBefore — Outlook conditional table wrapper
565
- outlook_class = css_class ? "#{css_class}-outlook" : ""
586
+ outlook_class = suffix_css_classes(css_class)
587
+ has_gap = wrapper_gap && !wrapper_gap.to_s.strip.empty?
566
588
  before_pairs = [
567
589
  ["align", "center"],
568
590
  ["border", "0"],
@@ -573,7 +595,7 @@ module MjmlRb
573
595
  ["style", style_join("width" => "#{container_px}px", "padding-top" => wrapper_gap) + ";"],
574
596
  ["width", container_px.to_s]
575
597
  ]
576
- before_pairs << ["bgcolor", bg_color] if bg_color
598
+ before_pairs << ["bgcolor", bg_color] if bg_color && !has_gap
577
599
 
578
600
  render_before = %(<!--[if mso | IE]><table#{outlook_attrs(before_pairs)}><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->)
579
601
 
@@ -608,6 +630,17 @@ module MjmlRb
608
630
  "text-align" => a["text-align"]
609
631
  )
610
632
 
633
+ # Compute box width and update context for children, matching npm
634
+ # wrapper's getChildContext() which sets containerWidth to box width.
635
+ border_left = parse_border_width(a["border-left"] || a["border"])
636
+ border_right = parse_border_width(a["border-right"] || a["border"])
637
+ pad_left = parse_padding_side(a, "left")
638
+ pad_right = parse_padding_side(a, "right")
639
+ box_width = container_px - pad_left - pad_right - border_left - border_right
640
+
641
+ previous_container_width = context[:container_width]
642
+ context[:container_width] = "#{box_width}px"
643
+
611
644
  div_attrs = {"class" => (full_width ? nil : css_class), "style" => div_style}
612
645
  inner = merge_outlook_conditionals(render_wrapped_children_wrapper(node, context, container_px, a["gap"]))
613
646
  inner_content = bg_has ? %(<div style="line-height:0;font-size:0">#{inner}</div>) : inner
@@ -651,22 +684,27 @@ module MjmlRb
651
684
  body = bg_has ? render_with_background(wrapper_html, a, container_px) : wrapper_html
652
685
  "#{render_before}\n#{body}\n#{render_after}"
653
686
  end
687
+ ensure
688
+ context[:container_width] = previous_container_width if previous_container_width
654
689
  end
655
690
 
656
- # Wrap each child mj-section/mj-wrapper in an Outlook conditional <td>.
691
+ # Wrap each child mj-section/mj-wrapper in an Outlook conditional <tr><td>.
692
+ # npm wrapper renders each child in its own row, unlike section which
693
+ # places all columns in a single row.
657
694
  def render_wrapped_children_wrapper(node, context, container_px, gap)
658
695
  children = node.element_children.select { |e| %w[mj-section mj-wrapper].include?(e.tag_name) }
659
696
  return render_children(node, context, parent: "mj-wrapper") if children.empty?
660
697
 
661
698
  open_table = %(<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><![endif]-->)
662
- open_tr = %(<!--[if mso | IE]><tr><![endif]-->)
663
- close_tr = %(<!--[if mso | IE]></tr><![endif]-->)
664
699
  close_table = %(<!--[if mso | IE]></table><![endif]-->)
665
700
 
666
701
  section_parts = with_inherited_mj_class(context, node) do
667
702
  children.each_with_index.map do |child, index|
668
- td_open = %(<!--[if mso | IE]><td class="" width="#{container_px}px" ><![endif]-->)
669
- td_close = %(<!--[if mso | IE]></td><![endif]-->)
703
+ child_attrs = resolved_attributes(child, context)
704
+ child_css = child_attrs["css-class"]
705
+ outlook_class = suffix_css_classes(child_css)
706
+ td_open = %(<!--[if mso | IE]><tr><td class="#{escape_attr(outlook_class)}" width="#{container_px}px" ><![endif]-->)
707
+ td_close = %(<!--[if mso | IE]></td></tr><![endif]-->)
670
708
  child_html = with_wrapper_child_gap(context, index.zero? ? nil : gap) do
671
709
  render_node(child, context, parent: "mj-wrapper")
672
710
  end
@@ -674,7 +712,7 @@ module MjmlRb
674
712
  end
675
713
  end
676
714
 
677
- ([open_table, open_tr] + section_parts + [close_tr, close_table]).join("\n")
715
+ ([open_table] + section_parts + [close_table]).join("\n")
678
716
  end
679
717
 
680
718
  def with_wrapper_child_gap(context, gap)
@@ -235,14 +235,14 @@ module MjmlRb
235
235
  widths = column_widths || {}
236
236
  return "" if widths.empty?
237
237
 
238
- base_rules = widths.map do |suffix, pct|
239
- ".mj-column-per-#{suffix} { width:#{pct}% !important; max-width: #{pct}%; }"
238
+ base_rules = widths.map do |class_name, width_str|
239
+ ".#{class_name} { width:#{width_str} !important; max-width: #{width_str}; }"
240
240
  end
241
- moz_rules = widths.map do |suffix, pct|
242
- ".moz-text-html .mj-column-per-#{suffix} { width:#{pct}% !important; max-width: #{pct}%; }"
241
+ moz_rules = widths.map do |class_name, width_str|
242
+ ".moz-text-html .#{class_name} { width:#{width_str} !important; max-width: #{width_str}; }"
243
243
  end
244
- owa_rules = widths.map do |suffix, pct|
245
- "[owa] .mj-column-per-#{suffix} { width:#{pct}% !important; max-width: #{pct}%; }"
244
+ owa_rules = widths.map do |class_name, width_str|
245
+ "[owa] .#{class_name} { width:#{width_str} !important; max-width: #{width_str}; }"
246
246
  end
247
247
 
248
248
  bp = breakpoint.to_s.strip
@@ -1,3 +1,3 @@
1
1
  module MjmlRb
2
- VERSION = "0.2.30".freeze
2
+ VERSION = "0.2.32".freeze
3
3
  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.30
4
+ version: 0.2.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Andriichuk