defmodule Legendary.CoreWeb.EmailHelpers do
@moduledoc """
HTML helpers for emails.
"""
import Phoenix.LiveView.Helpers, only: [sigil_H: 2]
import ShorterMaps
def framework_styles do
%{
background: %{
color: "#222222",
},
body: %{
font_family: "sans-serif",
font_size: "15px",
line_height: "20px",
text_color: "#555555",
},
button: %{
border_radius: "4px",
border: "1px solid #000000",
background: "#222222",
color: "#ffffff",
font_family: "sans-serif",
font_size: "15px",
line_height: "15px",
text_decoration: "none",
padding: "13px 17px",
display: "block",
},
column: %{
background: "#FFFFFF",
padding: "0 10px 40px 10px",
},
footer: %{
padding: "20px",
font_family: "sans-serif",
font_size: "12px",
line_height: "15px",
text_align: "center",
color: "#ffffff",
},
global: %{
width: 600,
},
h1: %{
margin: "0 0 10px 0",
font_family: "sans-serif",
font_size: "25px",
line_height: "30px",
color: "#333333",
font_weight: "normal",
},
h2: %{
margin: "0 0 10px 0",
font_family: "sans-serif",
font_size: "18px",
line_height: "22px",
color: "#333333",
font_weight: "bold",
},
header: %{
padding: "20px 0",
text_align: "center",
},
hero_image: %{
background: "#dddddd",
display: "block",
margin: "auto",
},
inner_column: %{
padding: "10px 10px 0",
},
li: %{
margin: "0 0 0 10px",
},
last_li: %{
margin: "0 0 10px 30px",
},
spacer: %{
height: "40",
font_size: "0px",
line_height: "0px",
},
ul: %{
margin: "0 0 10px 0",
padding: "0",
},
}
end
def framework_styles(group) do
Map.get(framework_styles(), group, %{})
end
def application_styles(group) do
styles = Application.get_env(:core, :email, %{}) |> Map.get(:styles, %{})
Map.get(styles, group, %{})
end
def effective_styles(group, overrides \\ %{}) do
group
|> framework_styles()
|> Legendary.Core.MapUtils.deep_merge(application_styles(group))
|> Map.merge(overrides)
end
def preview(do: content) do
assigns = ~M{content}
~H"""
<div style="max-height:0; overflow:hidden; mso-hide:all;" aria-hidden="true">
<%= content %>
</div>
<!-- Visually Hidden Preheader Text : END -->
<!-- Create white space after the desired preview text so email clients don’t pull other distracting text into the inbox preview. Extend as necessary. -->
<!-- Preview Text Spacing Hack : BEGIN -->
<div style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
</div>
"""
end
def header(do: content) do
assigns = ~M{content}
~H"""
<tr>
<td style={map_style(effective_styles(:header))}>
<%= content %>
</td>
</tr>
"""
end
def spacer do
style = effective_styles(:spacer)
assigns = ~M{style}
~H"""
<tr>
<td aria-hidden="true" height={style[:height]} style={map_style(style)}>
</td>
</tr>
"""
end
def row(do: content) do
assigns = ~M{content}
~H"""
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<%= content %>
</tr>
</table>
"""
end
@spec col(number, keyword, [{:do, any}, ...]) :: {:safe, [...]}
def col(n, opts, do: content) do
{of, _opts} = Keyword.pop!(opts, :of)
width = n * 100.0 / of
assigns = ~M{of, width, content}
~H"""
<td valign="top" width={"#{width}%"} style={map_style(effective_styles(:column))}>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style={map_style(effective_styles(:body)) <> " " <> map_style(effective_styles(:inner_column))}>
<%= content %>
</td>
</tr>
</table>
</td>
"""
end
def hero_image(opts) do
{src, _rest_opts} = Keyword.pop!(opts, :src)
assigns = ~M{src}
~H"""
<%= row do %>
<%= col 1, of: 1 do %>
<img
src={src}
width={effective_styles(:global)[:width]}
height="auto"
alt="alt_text"
border="0"
style={"#{map_style(effective_styles(:body))} width: 100%; max-width: #{effective_styles(:global)[:width]}; height: auto; #{map_style(effective_styles(:hero_image))}"}
class="g-img"
>
<% end %>
<% end %>
"""
end
def h1(do: content) do
assigns = ~M{content}
~H"""
<h1 style={map_style(effective_styles(:h1))}>
<%= content %>
</h1>
"""
end
def h2(do: content) do
assigns = ~M{content}
~H"""
<h2 style={map_style(effective_styles(:h2))}>
<%= content %>
</h2>
"""
end
def p(do: content) do
assigns = ~M{content}
~H"""
<p style={map_style(effective_styles(:body))}>
<%= content %>
</p>
"""
end
def styled_button(opts, do: content), do: button(opts, do: content)
def button(opts, do: content) do
{overrides, opts_without_style} = Keyword.pop(opts, :style, %{})
{href, _rest_opts} = Keyword.pop!(opts_without_style, :href)
style = effective_styles(:button, overrides)
cell_style = style |> Map.take([:border_radius, :background])
assigns = ~M{cell_style, content, href, style}
~H"""
<%= wrapper do %>
<td
class="button-td button-td-primary"
style={map_style(cell_style)}
>
<a
class="button-a button-a-primary"
href={href}
style={map_style(style)}
>
<%= content %>
</a>
</td>
<% end %>
"""
end
def ul(opts) do
{items, _rest_opts} = Keyword.pop!(opts, :items)
item_count = Enum.count(items)
item_tags =
items
|> Enum.with_index()
|> Enum.map(fn {item, index} ->
li_for_ul(index, item_count, item)
end)
assigns = ~M{item_tags}
~H"""
<ul style={map_style(effective_styles(:ul))}>
<%= for li <- item_tags do %>
<%= li %>
<% end %>
</ul>
"""
end
def footer do
assigns = %{}
~H"""
<%= wrapper do %>
<td style={map_style(effective_styles(:footer))}>
<%= Legendary.I18n.t! "en", "email.company.name" %><br>
<span class="unstyle-auto-detected-links">
<%= Legendary.I18n.t! "en", "email.company.address" %><br>
<%= Legendary.I18n.t! "en", "email.company.phone" %>
</span>
<br><br>
</td>
<% end %>
"""
end
defp li_for_ul(index, list_length, content) do
style_type = if index == list_length - 1, do: :last_li, else: :li
assigns = ~M{content, style_type}
~H"""
<li style={map_style(effective_styles(style_type))}>
<%= content %>
</li>
"""
end
defp wrapper(do: content) do
assigns = ~M{content}
~H"""
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;">
<tr>
<%= content %>
</tr>
</table>
"""
end
defp map_style(map) do
map
|> Enum.map(fn {key, value} ->
new_key =
key
|> Atom.to_string()
|> String.replace("_", "-")
"#{new_key}: #{value};"
end)
|> Enum.join("\n")
end
end