class Inkcite::Renderer::Table

Constants

CLOSE_TABLE
TR_TRANSITION

Name of the attribute allowing a transition attribute to be placed on the automatically-added <tr> element.

Public Instance Methods

render(tag, opt, ctx) click to toggle source
# File lib/inkcite/renderer/table.rb, line 9
def render tag, opt, ctx

  html = ''

  # We're either going to be pushing a newly opened table onto this stack
  # or we're popping the open opts off of it.
  tag_stack = ctx.tag_stack(:table)

  if tag == CLOSE_TABLE

    # Pop the opts used when the table was originally opened.  Then grab the
    # mobile attribute. If this is a fluid table, we'll need to do close some
    # of the additional tags injected when it was opened.
    open_opt = tag_stack.pop
    open_mobile = open_opt[:mobile]

    # If the table was declared as Fluid-Hybrid Drop, then there are some additional
    # elements that need to be closed before the regular row-table closure that
    # the Table helper normally produces.
    if is_fluid_drop?(open_mobile)

      # Close the interior conditional table for Outlook that contains the floating blocks.
      html << if_mso('</tr></table>')

      # Close what @campaignmonitor calls the "secret weapon" cell that typically aligns
      # the text horizontally and vertically aligns the floating elements.
      html << '</td>' # Styled
    end

    # Normal Inkcite Helper close HTML.
    html << '</tr></table>'

    # Close the conditional table for Output that contains the entire fluid layout.
    html << if_mso('</td></tr></table>') if is_fluid?(open_mobile)

  else

    # Push this table onto the stack which will make it's declaration
    # available to its child TDs.
    tag_stack << opt

    table = Element.new(tag, { :border => 0, :cellspacing => 0 })

    # Grab the responsive mobile klass that is assigned to this table, if any.
    mobile = opt[:mobile]

    # Check if fluid-drop has been specified.  This will force a lot more HTML to
    # be produced for this table and its child TDs.
    is_fluid_drop = is_fluid_drop?(mobile)

    # Inherit base cell attributes - border, background color and image, etc.
    mix_all table, opt, ctx
    mix_margins table, opt, ctx
    mix_text_shadow table, opt, ctx

    # Conveniently accept padding (easier to type and consistent with CSS) or
    # cellpadding which must always be declared.
    #
    # If Fluid-Drop is enabled, padding is always zero at this top-level table
    # and will be applied in the TD renderer when it creates a new table to
    # wrap itself in.
    cellpadding = is_fluid_drop ? 0 : get_padding(opt)
    table[:cellpadding] = cellpadding

    # Need to specify padding as px to ensure that the table plays nicely
    # with high-DPI email clients like Outlook.
    if cellpadding > 0
      cellpaddingpx = px(cellpadding)
      table.style[MSO_PADDING_ALT] = "#{cellpaddingpx} #{cellpaddingpx} #{cellpaddingpx} #{cellpaddingpx}"
    end

    # Conveniently accept both float and align to mean the same thing.
    align = opt[:align] || opt[:float]
    table[:align] = align unless align.blank?

    mix_border_radius table, opt, ctx

    border_collapse = opt[BORDER_COLLAPSE]
    table.style[BORDER_COLLAPSE] = border_collapse unless border_collapse.blank?

    # For both fluid and fluid-drop share certain setup which is performed here.
    if is_fluid?(mobile)

      # Width must always be specified on fluid tables, like it is for images.
      # Warn the designer if a width has not been supplied in pixels.
      width = opt[:width].to_i
      ctx.error("Width is a required attribute when '#{mobile}' is applied to a {table}", opt) unless width > 0

      # Fluid table method courtesy of @campaignmonitor - assign the max-width in
      # pixels and set the normal width to 100%.
      # https://www.campaignmonitor.com/blog/email-marketing/2014/07/creating-a-centred-responsive-design-without-media-queries/
      table.style[MAX_WIDTH] = px(width)
      table[:width] = '100%'

      # Outlook (and Lotus Notes, if you can believe it) doesn't support max-width
      # so we need to wrap the entire fluid table in a conditional table that
      # ensures layout displays within the actual maximum pixels width.
      html << if_mso(Element.new('table', {
                  :align => opt[:align], :border => 0, :cellspacing => 0, :cellpadding => 0, :width => opt[:width].to_i
              }) + '<tr><td>')

    elsif mobile == DROP || mobile == SWITCH

      # When a Table is configured to have it's cells DROP then it
      # actually needs to FILL on mobile and it's child Tds will
      # be DROP'd.  Override the local mobile klass so the child Tds
      # see the parent as DROP.
      mobile = FILL

    end

    mix_responsive table, opt, ctx, mobile

    html << table.to_s

    tr = Element.new('tr')

    # Check for a row transition which is necessary to facilitate
    # cross-column animation - e.g. tr-transition="all .5s cubic-bezier(0.075, 0.82, 0.165, 1)"
    tr_transition = opt[TR_TRANSITION]
    tr.style[:transition] = tr_transition unless none?(tr_transition)

    html << tr.to_s

    if is_fluid_drop

      # Fluid-Drop tables need a default alignment specified which is inherited
      # by the child TD elements if not otherwise specified.
      #
      # 11/8/2015: For reasons I don't understand, if the table is not valigned
      # middle by default, then we lose the ability to valign-middle individual
      # TD children.  So, if we force 'middle' here, then the TDs can override
      # with 'top' or 'bottom' alignment when desired.
      valign = opt[:valign] ||= 'middle'

      # According to @campaignmonitor this is the secret weapon of Fluid-Hyrbid
      # drop which wraps the floating elements and centers them appropriately.
      # https://www.campaignmonitor.com/blog/email-marketing/2014/07/creating-a-centred-responsive-design-without-media-queries/
      #
      # The zero-size font addresses a rendering problem in Outlook:
      # https://css-tricks.com/fighting-the-space-between-inline-block-elements/
      fluid_td = Element.new('td', :style => { TEXT_ALIGN => :center, VERTICAL_ALIGN => opt[:valign], FONT_SIZE => 0 })

      # If fluid-stack is specified, then reverse the order of the columns to make
      # the right-most orient to the top.
      # http://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
      fluid_td[:dir] = :rtl if mobile == FLUID_STACK

      html << fluid_td.to_s

      # Lastly, Outlook needs yet another conditional table that will be used
      # to contain the floating blocks.  The TD elements are generated by
      # each of the columns within this Fluid-Drop table.
      html << if_mso(Element.new('table', :width => '100%', :align => :center, :cellpadding => 0, :cellspacing => 0, :border => 0) + '<tr>')

    end

  end

  html
end