@templatical/renderer 0.44.3 → 0.46.0

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.
Files changed (310) hide show
  1. package/assets/social/circle/behance.png +0 -0
  2. package/assets/social/circle/discord.png +0 -0
  3. package/assets/social/circle/dribbble.png +0 -0
  4. package/assets/social/circle/email.png +0 -0
  5. package/assets/social/circle/facebook.png +0 -0
  6. package/assets/social/circle/github.png +0 -0
  7. package/assets/social/circle/instagram.png +0 -0
  8. package/assets/social/circle/linkedin.png +0 -0
  9. package/assets/social/circle/pinterest.png +0 -0
  10. package/assets/social/circle/reddit.png +0 -0
  11. package/assets/social/circle/snapchat.png +0 -0
  12. package/assets/social/circle/telegram.png +0 -0
  13. package/assets/social/circle/tiktok.png +0 -0
  14. package/assets/social/circle/twitter.png +0 -0
  15. package/assets/social/circle/website.png +0 -0
  16. package/assets/social/circle/whatsapp.png +0 -0
  17. package/assets/social/circle/youtube.png +0 -0
  18. package/assets/social/circle-dark/behance.png +0 -0
  19. package/assets/social/circle-dark/discord.png +0 -0
  20. package/assets/social/circle-dark/dribbble.png +0 -0
  21. package/assets/social/circle-dark/email.png +0 -0
  22. package/assets/social/circle-dark/facebook.png +0 -0
  23. package/assets/social/circle-dark/github.png +0 -0
  24. package/assets/social/circle-dark/instagram.png +0 -0
  25. package/assets/social/circle-dark/linkedin.png +0 -0
  26. package/assets/social/circle-dark/pinterest.png +0 -0
  27. package/assets/social/circle-dark/reddit.png +0 -0
  28. package/assets/social/circle-dark/snapchat.png +0 -0
  29. package/assets/social/circle-dark/telegram.png +0 -0
  30. package/assets/social/circle-dark/tiktok.png +0 -0
  31. package/assets/social/circle-dark/twitter.png +0 -0
  32. package/assets/social/circle-dark/website.png +0 -0
  33. package/assets/social/circle-dark/whatsapp.png +0 -0
  34. package/assets/social/circle-dark/youtube.png +0 -0
  35. package/assets/social/circle-light/behance.png +0 -0
  36. package/assets/social/circle-light/discord.png +0 -0
  37. package/assets/social/circle-light/dribbble.png +0 -0
  38. package/assets/social/circle-light/email.png +0 -0
  39. package/assets/social/circle-light/facebook.png +0 -0
  40. package/assets/social/circle-light/github.png +0 -0
  41. package/assets/social/circle-light/instagram.png +0 -0
  42. package/assets/social/circle-light/linkedin.png +0 -0
  43. package/assets/social/circle-light/pinterest.png +0 -0
  44. package/assets/social/circle-light/reddit.png +0 -0
  45. package/assets/social/circle-light/snapchat.png +0 -0
  46. package/assets/social/circle-light/telegram.png +0 -0
  47. package/assets/social/circle-light/tiktok.png +0 -0
  48. package/assets/social/circle-light/twitter.png +0 -0
  49. package/assets/social/circle-light/website.png +0 -0
  50. package/assets/social/circle-light/whatsapp.png +0 -0
  51. package/assets/social/circle-light/youtube.png +0 -0
  52. package/assets/social/outlined/behance.png +0 -0
  53. package/assets/social/outlined/discord.png +0 -0
  54. package/assets/social/outlined/dribbble.png +0 -0
  55. package/assets/social/outlined/email.png +0 -0
  56. package/assets/social/outlined/facebook.png +0 -0
  57. package/assets/social/outlined/github.png +0 -0
  58. package/assets/social/outlined/instagram.png +0 -0
  59. package/assets/social/outlined/linkedin.png +0 -0
  60. package/assets/social/outlined/pinterest.png +0 -0
  61. package/assets/social/outlined/reddit.png +0 -0
  62. package/assets/social/outlined/snapchat.png +0 -0
  63. package/assets/social/outlined/telegram.png +0 -0
  64. package/assets/social/outlined/tiktok.png +0 -0
  65. package/assets/social/outlined/twitter.png +0 -0
  66. package/assets/social/outlined/website.png +0 -0
  67. package/assets/social/outlined/whatsapp.png +0 -0
  68. package/assets/social/outlined/youtube.png +0 -0
  69. package/assets/social/outlined-dark/behance.png +0 -0
  70. package/assets/social/outlined-dark/discord.png +0 -0
  71. package/assets/social/outlined-dark/dribbble.png +0 -0
  72. package/assets/social/outlined-dark/email.png +0 -0
  73. package/assets/social/outlined-dark/facebook.png +0 -0
  74. package/assets/social/outlined-dark/github.png +0 -0
  75. package/assets/social/outlined-dark/instagram.png +0 -0
  76. package/assets/social/outlined-dark/linkedin.png +0 -0
  77. package/assets/social/outlined-dark/pinterest.png +0 -0
  78. package/assets/social/outlined-dark/reddit.png +0 -0
  79. package/assets/social/outlined-dark/snapchat.png +0 -0
  80. package/assets/social/outlined-dark/telegram.png +0 -0
  81. package/assets/social/outlined-dark/tiktok.png +0 -0
  82. package/assets/social/outlined-dark/twitter.png +0 -0
  83. package/assets/social/outlined-dark/website.png +0 -0
  84. package/assets/social/outlined-dark/whatsapp.png +0 -0
  85. package/assets/social/outlined-dark/youtube.png +0 -0
  86. package/assets/social/outlined-light/behance.png +0 -0
  87. package/assets/social/outlined-light/discord.png +0 -0
  88. package/assets/social/outlined-light/dribbble.png +0 -0
  89. package/assets/social/outlined-light/email.png +0 -0
  90. package/assets/social/outlined-light/facebook.png +0 -0
  91. package/assets/social/outlined-light/github.png +0 -0
  92. package/assets/social/outlined-light/instagram.png +0 -0
  93. package/assets/social/outlined-light/linkedin.png +0 -0
  94. package/assets/social/outlined-light/pinterest.png +0 -0
  95. package/assets/social/outlined-light/reddit.png +0 -0
  96. package/assets/social/outlined-light/snapchat.png +0 -0
  97. package/assets/social/outlined-light/telegram.png +0 -0
  98. package/assets/social/outlined-light/tiktok.png +0 -0
  99. package/assets/social/outlined-light/twitter.png +0 -0
  100. package/assets/social/outlined-light/website.png +0 -0
  101. package/assets/social/outlined-light/whatsapp.png +0 -0
  102. package/assets/social/outlined-light/youtube.png +0 -0
  103. package/assets/social/plain/behance.png +0 -0
  104. package/assets/social/plain/discord.png +0 -0
  105. package/assets/social/plain/dribbble.png +0 -0
  106. package/assets/social/plain/email.png +0 -0
  107. package/assets/social/plain/facebook.png +0 -0
  108. package/assets/social/plain/github.png +0 -0
  109. package/assets/social/plain/instagram.png +0 -0
  110. package/assets/social/plain/linkedin.png +0 -0
  111. package/assets/social/plain/pinterest.png +0 -0
  112. package/assets/social/plain/reddit.png +0 -0
  113. package/assets/social/plain/snapchat.png +0 -0
  114. package/assets/social/plain/telegram.png +0 -0
  115. package/assets/social/plain/tiktok.png +0 -0
  116. package/assets/social/plain/twitter.png +0 -0
  117. package/assets/social/plain/website.png +0 -0
  118. package/assets/social/plain/whatsapp.png +0 -0
  119. package/assets/social/plain/youtube.png +0 -0
  120. package/assets/social/plain-dark/behance.png +0 -0
  121. package/assets/social/plain-dark/discord.png +0 -0
  122. package/assets/social/plain-dark/dribbble.png +0 -0
  123. package/assets/social/plain-dark/email.png +0 -0
  124. package/assets/social/plain-dark/facebook.png +0 -0
  125. package/assets/social/plain-dark/github.png +0 -0
  126. package/assets/social/plain-dark/instagram.png +0 -0
  127. package/assets/social/plain-dark/linkedin.png +0 -0
  128. package/assets/social/plain-dark/pinterest.png +0 -0
  129. package/assets/social/plain-dark/reddit.png +0 -0
  130. package/assets/social/plain-dark/snapchat.png +0 -0
  131. package/assets/social/plain-dark/telegram.png +0 -0
  132. package/assets/social/plain-dark/tiktok.png +0 -0
  133. package/assets/social/plain-dark/twitter.png +0 -0
  134. package/assets/social/plain-dark/website.png +0 -0
  135. package/assets/social/plain-dark/whatsapp.png +0 -0
  136. package/assets/social/plain-dark/youtube.png +0 -0
  137. package/assets/social/plain-light/behance.png +0 -0
  138. package/assets/social/plain-light/discord.png +0 -0
  139. package/assets/social/plain-light/dribbble.png +0 -0
  140. package/assets/social/plain-light/email.png +0 -0
  141. package/assets/social/plain-light/facebook.png +0 -0
  142. package/assets/social/plain-light/github.png +0 -0
  143. package/assets/social/plain-light/instagram.png +0 -0
  144. package/assets/social/plain-light/linkedin.png +0 -0
  145. package/assets/social/plain-light/pinterest.png +0 -0
  146. package/assets/social/plain-light/reddit.png +0 -0
  147. package/assets/social/plain-light/snapchat.png +0 -0
  148. package/assets/social/plain-light/telegram.png +0 -0
  149. package/assets/social/plain-light/tiktok.png +0 -0
  150. package/assets/social/plain-light/twitter.png +0 -0
  151. package/assets/social/plain-light/website.png +0 -0
  152. package/assets/social/plain-light/whatsapp.png +0 -0
  153. package/assets/social/plain-light/youtube.png +0 -0
  154. package/assets/social/rounded/behance.png +0 -0
  155. package/assets/social/rounded/discord.png +0 -0
  156. package/assets/social/rounded/dribbble.png +0 -0
  157. package/assets/social/rounded/email.png +0 -0
  158. package/assets/social/rounded/facebook.png +0 -0
  159. package/assets/social/rounded/github.png +0 -0
  160. package/assets/social/rounded/instagram.png +0 -0
  161. package/assets/social/rounded/linkedin.png +0 -0
  162. package/assets/social/rounded/pinterest.png +0 -0
  163. package/assets/social/rounded/reddit.png +0 -0
  164. package/assets/social/rounded/snapchat.png +0 -0
  165. package/assets/social/rounded/telegram.png +0 -0
  166. package/assets/social/rounded/tiktok.png +0 -0
  167. package/assets/social/rounded/twitter.png +0 -0
  168. package/assets/social/rounded/website.png +0 -0
  169. package/assets/social/rounded/whatsapp.png +0 -0
  170. package/assets/social/rounded/youtube.png +0 -0
  171. package/assets/social/rounded-dark/behance.png +0 -0
  172. package/assets/social/rounded-dark/discord.png +0 -0
  173. package/assets/social/rounded-dark/dribbble.png +0 -0
  174. package/assets/social/rounded-dark/email.png +0 -0
  175. package/assets/social/rounded-dark/facebook.png +0 -0
  176. package/assets/social/rounded-dark/github.png +0 -0
  177. package/assets/social/rounded-dark/instagram.png +0 -0
  178. package/assets/social/rounded-dark/linkedin.png +0 -0
  179. package/assets/social/rounded-dark/pinterest.png +0 -0
  180. package/assets/social/rounded-dark/reddit.png +0 -0
  181. package/assets/social/rounded-dark/snapchat.png +0 -0
  182. package/assets/social/rounded-dark/telegram.png +0 -0
  183. package/assets/social/rounded-dark/tiktok.png +0 -0
  184. package/assets/social/rounded-dark/twitter.png +0 -0
  185. package/assets/social/rounded-dark/website.png +0 -0
  186. package/assets/social/rounded-dark/whatsapp.png +0 -0
  187. package/assets/social/rounded-dark/youtube.png +0 -0
  188. package/assets/social/rounded-light/behance.png +0 -0
  189. package/assets/social/rounded-light/discord.png +0 -0
  190. package/assets/social/rounded-light/dribbble.png +0 -0
  191. package/assets/social/rounded-light/email.png +0 -0
  192. package/assets/social/rounded-light/facebook.png +0 -0
  193. package/assets/social/rounded-light/github.png +0 -0
  194. package/assets/social/rounded-light/instagram.png +0 -0
  195. package/assets/social/rounded-light/linkedin.png +0 -0
  196. package/assets/social/rounded-light/pinterest.png +0 -0
  197. package/assets/social/rounded-light/reddit.png +0 -0
  198. package/assets/social/rounded-light/snapchat.png +0 -0
  199. package/assets/social/rounded-light/telegram.png +0 -0
  200. package/assets/social/rounded-light/tiktok.png +0 -0
  201. package/assets/social/rounded-light/twitter.png +0 -0
  202. package/assets/social/rounded-light/website.png +0 -0
  203. package/assets/social/rounded-light/whatsapp.png +0 -0
  204. package/assets/social/rounded-light/youtube.png +0 -0
  205. package/assets/social/solid/behance.png +0 -0
  206. package/assets/social/solid/discord.png +0 -0
  207. package/assets/social/solid/dribbble.png +0 -0
  208. package/assets/social/solid/email.png +0 -0
  209. package/assets/social/solid/facebook.png +0 -0
  210. package/assets/social/solid/github.png +0 -0
  211. package/assets/social/solid/instagram.png +0 -0
  212. package/assets/social/solid/linkedin.png +0 -0
  213. package/assets/social/solid/pinterest.png +0 -0
  214. package/assets/social/solid/reddit.png +0 -0
  215. package/assets/social/solid/snapchat.png +0 -0
  216. package/assets/social/solid/telegram.png +0 -0
  217. package/assets/social/solid/tiktok.png +0 -0
  218. package/assets/social/solid/twitter.png +0 -0
  219. package/assets/social/solid/website.png +0 -0
  220. package/assets/social/solid/whatsapp.png +0 -0
  221. package/assets/social/solid/youtube.png +0 -0
  222. package/assets/social/solid-dark/behance.png +0 -0
  223. package/assets/social/solid-dark/discord.png +0 -0
  224. package/assets/social/solid-dark/dribbble.png +0 -0
  225. package/assets/social/solid-dark/email.png +0 -0
  226. package/assets/social/solid-dark/facebook.png +0 -0
  227. package/assets/social/solid-dark/github.png +0 -0
  228. package/assets/social/solid-dark/instagram.png +0 -0
  229. package/assets/social/solid-dark/linkedin.png +0 -0
  230. package/assets/social/solid-dark/pinterest.png +0 -0
  231. package/assets/social/solid-dark/reddit.png +0 -0
  232. package/assets/social/solid-dark/snapchat.png +0 -0
  233. package/assets/social/solid-dark/telegram.png +0 -0
  234. package/assets/social/solid-dark/tiktok.png +0 -0
  235. package/assets/social/solid-dark/twitter.png +0 -0
  236. package/assets/social/solid-dark/website.png +0 -0
  237. package/assets/social/solid-dark/whatsapp.png +0 -0
  238. package/assets/social/solid-dark/youtube.png +0 -0
  239. package/assets/social/solid-light/behance.png +0 -0
  240. package/assets/social/solid-light/discord.png +0 -0
  241. package/assets/social/solid-light/dribbble.png +0 -0
  242. package/assets/social/solid-light/email.png +0 -0
  243. package/assets/social/solid-light/facebook.png +0 -0
  244. package/assets/social/solid-light/github.png +0 -0
  245. package/assets/social/solid-light/instagram.png +0 -0
  246. package/assets/social/solid-light/linkedin.png +0 -0
  247. package/assets/social/solid-light/pinterest.png +0 -0
  248. package/assets/social/solid-light/reddit.png +0 -0
  249. package/assets/social/solid-light/snapchat.png +0 -0
  250. package/assets/social/solid-light/telegram.png +0 -0
  251. package/assets/social/solid-light/tiktok.png +0 -0
  252. package/assets/social/solid-light/twitter.png +0 -0
  253. package/assets/social/solid-light/website.png +0 -0
  254. package/assets/social/solid-light/whatsapp.png +0 -0
  255. package/assets/social/solid-light/youtube.png +0 -0
  256. package/assets/social/square/behance.png +0 -0
  257. package/assets/social/square/discord.png +0 -0
  258. package/assets/social/square/dribbble.png +0 -0
  259. package/assets/social/square/email.png +0 -0
  260. package/assets/social/square/facebook.png +0 -0
  261. package/assets/social/square/github.png +0 -0
  262. package/assets/social/square/instagram.png +0 -0
  263. package/assets/social/square/linkedin.png +0 -0
  264. package/assets/social/square/pinterest.png +0 -0
  265. package/assets/social/square/reddit.png +0 -0
  266. package/assets/social/square/snapchat.png +0 -0
  267. package/assets/social/square/telegram.png +0 -0
  268. package/assets/social/square/tiktok.png +0 -0
  269. package/assets/social/square/twitter.png +0 -0
  270. package/assets/social/square/website.png +0 -0
  271. package/assets/social/square/whatsapp.png +0 -0
  272. package/assets/social/square/youtube.png +0 -0
  273. package/assets/social/square-dark/behance.png +0 -0
  274. package/assets/social/square-dark/discord.png +0 -0
  275. package/assets/social/square-dark/dribbble.png +0 -0
  276. package/assets/social/square-dark/email.png +0 -0
  277. package/assets/social/square-dark/facebook.png +0 -0
  278. package/assets/social/square-dark/github.png +0 -0
  279. package/assets/social/square-dark/instagram.png +0 -0
  280. package/assets/social/square-dark/linkedin.png +0 -0
  281. package/assets/social/square-dark/pinterest.png +0 -0
  282. package/assets/social/square-dark/reddit.png +0 -0
  283. package/assets/social/square-dark/snapchat.png +0 -0
  284. package/assets/social/square-dark/telegram.png +0 -0
  285. package/assets/social/square-dark/tiktok.png +0 -0
  286. package/assets/social/square-dark/twitter.png +0 -0
  287. package/assets/social/square-dark/website.png +0 -0
  288. package/assets/social/square-dark/whatsapp.png +0 -0
  289. package/assets/social/square-dark/youtube.png +0 -0
  290. package/assets/social/square-light/behance.png +0 -0
  291. package/assets/social/square-light/discord.png +0 -0
  292. package/assets/social/square-light/dribbble.png +0 -0
  293. package/assets/social/square-light/email.png +0 -0
  294. package/assets/social/square-light/facebook.png +0 -0
  295. package/assets/social/square-light/github.png +0 -0
  296. package/assets/social/square-light/instagram.png +0 -0
  297. package/assets/social/square-light/linkedin.png +0 -0
  298. package/assets/social/square-light/pinterest.png +0 -0
  299. package/assets/social/square-light/reddit.png +0 -0
  300. package/assets/social/square-light/snapchat.png +0 -0
  301. package/assets/social/square-light/telegram.png +0 -0
  302. package/assets/social/square-light/tiktok.png +0 -0
  303. package/assets/social/square-light/twitter.png +0 -0
  304. package/assets/social/square-light/website.png +0 -0
  305. package/assets/social/square-light/whatsapp.png +0 -0
  306. package/assets/social/square-light/youtube.png +0 -0
  307. package/dist/index.d.ts +17 -3
  308. package/dist/index.js +64 -29
  309. package/dist/index.js.map +1 -1
  310. package/package.json +3 -2
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/index.d.ts CHANGED
@@ -84,9 +84,13 @@ export declare class RenderContext {
84
84
  */
85
85
  withContainerWidth(width: number): RenderContext;
86
86
  /**
87
- * Resolve a font family name to include custom font fallbacks.
88
- * If the font matches a custom font, returns `'FontName', fallback`.
89
- * Otherwise returns the original font family string.
87
+ * The stack to emit for a block's or the template's `fontFamily`. A stack is
88
+ * the author's own fallback chain and is emitted as written, as is anything
89
+ * that doesn't parse as a font. A lone family gets a fallback: a custom
90
+ * font's own `fallback` (or `defaultFallbackFont`), a built-in's fixed stack,
91
+ * or, for any other family, `defaultFallbackFont`, so a client without the
92
+ * face falls back to it rather than to its own default (Times New Roman in
93
+ * Outlook on Windows). A generic family needs nothing appended.
90
94
  */
91
95
  resolveFontFamily(fontFamily: string): string;
92
96
  }
@@ -170,6 +174,16 @@ export declare function richTextGapClass(gap: number): string;
170
174
  */
171
175
  export declare function resolveParagraphGap(spacing: number | undefined): number;
172
176
  //#endregion
177
+ //#region src/renderers/button.d.ts
178
+ /**
179
+ * Marks the `mj-button` of a full-width button. MJML gives the button's `<a>`
180
+ * a width only for a px `width`; for `100%` it widens the `<table>` and leaves
181
+ * the `<a>` at `display:inline-block`, so only the label is clickable.
182
+ * `renderToMjml` scopes the head rule that makes the `<a>` fill the button to
183
+ * this class.
184
+ */
185
+ export declare const BUTTON_FULL_WIDTH_CSS_CLASS = "tpl-button-full";
186
+ //#endregion
173
187
  //#region src/columns.d.ts
174
188
  /**
175
189
  * Get width percentages for each column in a layout.
package/dist/index.js CHANGED
@@ -1,17 +1,7 @@
1
- import { HEADING_LEVEL_FONT_SIZE, RICH_TEXT_SPACING, applyLayout, assertNoSlotInContent, assertNoWrapperInContent, findOpenTagEnd, getTagAttrValue, isButton, isCustomBlock, isDivider, isHtml, isImage, isMenu, isParagraph, isSection, isSocialIcons, isSpacer, isTable, isTitle, isVideo, isWrapper, resolveContentDirection, toBorderDeclarations, toBorderRadiusCss } from "@templatical/types";
1
+ import { BUILT_IN_FONT_STACKS, HEADING_LEVEL_FONT_SIZE, RICH_TEXT_SPACING, applyLayout, assertNoSlotInContent, assertNoWrapperInContent, findOpenTagEnd, getTagAttrValue, isButton, isCustomBlock, isDivider, isGenericFontFamily, isHtml, isImage, isMenu, isParagraph, isSection, isSocialIcons, isSpacer, isTable, isTitle, isVideo, isWrapper, parseFontFamilies, quoteFontFamily, resolveContentDirection, socialIconAssetDir, toBorderDeclarations, toBorderRadiusCss } from "@templatical/types";
2
2
  //#endregion
3
3
  //#region src/render-context.ts
4
- const DEFAULT_SOCIAL_ICONS_BASE_URL = `https://cdn.jsdelivr.net/npm/@templatical/renderer@0.44.3/assets/social`;
5
- const BUILT_IN_FONT_FALLBACKS = {
6
- arial: "Arial, sans-serif",
7
- helvetica: "Helvetica, sans-serif",
8
- georgia: "Georgia, serif",
9
- "times new roman": "'Times New Roman', serif",
10
- verdana: "Verdana, sans-serif",
11
- "trebuchet ms": "'Trebuchet MS', sans-serif",
12
- "courier new": "'Courier New', monospace",
13
- tahoma: "Tahoma, sans-serif"
14
- };
4
+ const DEFAULT_SOCIAL_ICONS_BASE_URL = `https://cdn.jsdelivr.net/npm/@templatical/renderer@0.46.0/assets/social`;
15
5
  /**
16
6
  * Immutable context passed through the block rendering chain.
17
7
  */
@@ -42,18 +32,27 @@ var RenderContext = class RenderContext {
42
32
  return new RenderContext(width, this.customFonts, this.defaultFallbackFont, this.allowHtmlBlocks, this.customBlockHtml, this.socialIconsBaseUrl, this.blockRenderers, this.contentDirection);
43
33
  }
44
34
  /**
45
- * Resolve a font family name to include custom font fallbacks.
46
- * If the font matches a custom font, returns `'FontName', fallback`.
47
- * Otherwise returns the original font family string.
35
+ * The stack to emit for a block's or the template's `fontFamily`. A stack is
36
+ * the author's own fallback chain and is emitted as written, as is anything
37
+ * that doesn't parse as a font. A lone family gets a fallback: a custom
38
+ * font's own `fallback` (or `defaultFallbackFont`), a built-in's fixed stack,
39
+ * or, for any other family, `defaultFallbackFont`, so a client without the
40
+ * face falls back to it rather than to its own default (Times New Roman in
41
+ * Outlook on Windows). A generic family needs nothing appended.
48
42
  */
49
43
  resolveFontFamily(fontFamily) {
50
- for (const customFont of this.customFonts) if (customFont.name.toLowerCase() === fontFamily.toLowerCase()) {
44
+ const families = parseFontFamilies(fontFamily);
45
+ if (families.length !== 1) return fontFamily;
46
+ const [family] = families;
47
+ const key = family.name.toLowerCase();
48
+ for (const customFont of this.customFonts) if (customFont.name.toLowerCase() === key) {
51
49
  const fallback = customFont.fallback ?? this.defaultFallbackFont;
52
50
  return `'${customFont.name}', ${fallback}`;
53
51
  }
54
- const builtIn = BUILT_IN_FONT_FALLBACKS[fontFamily.toLowerCase()];
52
+ const builtIn = BUILT_IN_FONT_STACKS[key];
55
53
  if (builtIn) return builtIn;
56
- return fontFamily;
54
+ if (isGenericFontFamily(family)) return fontFamily;
55
+ return `${quoteFontFamily(family.name)}, ${this.defaultFallbackFont}`;
57
56
  }
58
57
  };
59
58
  //#endregion
@@ -177,6 +176,18 @@ function escapeCssValue(text) {
177
176
  if (text === "") return "";
178
177
  return escapeAttr(text).replace(/[;{}\r\n]/g, "");
179
178
  }
179
+ const FONT_FAMILY_ENTITY_REGEX = /[&<>"]/g;
180
+ /**
181
+ * Escape a font stack for a double-quoted `font-family="…"` MJML attribute.
182
+ * A `"`-quoted family (`"Helvetica Neue", Arial`) must not end the attribute,
183
+ * or the compiler reads it as empty. Unlike `escapeAttr`, single quotes are
184
+ * left alone: they can't end a double-quoted attribute, and they're how the
185
+ * renderer quotes families itself (`'Times New Roman', serif`), so every
186
+ * stack without `&<>"` is emitted byte-for-byte.
187
+ */
188
+ function escapeFontFamily(text) {
189
+ return text.replace(FONT_FAMILY_ENTITY_REGEX, (char) => HTML_ENTITIES[char] ?? char);
190
+ }
180
191
  /**
181
192
  * Replace merge tag span elements with their data attribute values.
182
193
  * Converts `<span data-merge-tag="{{name}}">Label</span>` to `{{name}}`.
@@ -442,7 +453,7 @@ function unwrapParagraph(html) {
442
453
  }
443
454
  function renderFontFamilyAttr$3(fontFamily, context) {
444
455
  if (!fontFamily) return "";
445
- return ` font-family="${context.resolveFontFamily(fontFamily)}"`;
456
+ return ` font-family="${escapeFontFamily(context.resolveFontFamily(fontFamily))}"`;
446
457
  }
447
458
  //#endregion
448
459
  //#region src/renderers/paragraph.ts
@@ -495,6 +506,14 @@ function renderImage(block, context) {
495
506
  //#endregion
496
507
  //#region src/renderers/button.ts
497
508
  /**
509
+ * Marks the `mj-button` of a full-width button. MJML gives the button's `<a>`
510
+ * a width only for a px `width`; for `100%` it widens the `<table>` and leaves
511
+ * the `<a>` at `display:inline-block`, so only the label is clickable.
512
+ * `renderToMjml` scopes the head rule that makes the `<a>` fill the button to
513
+ * this class.
514
+ */
515
+ const BUTTON_FULL_WIDTH_CSS_CLASS = "tpl-button-full";
516
+ /**
498
517
  * Render a button block to MJML markup.
499
518
  */
500
519
  function renderButton(block, context) {
@@ -512,7 +531,7 @@ function renderButton(block, context) {
512
531
  const targetAttr = block.openInNewTab ? " target=\"_blank\" rel=\"noopener\"" : "";
513
532
  const fontFamilyAttr = renderFontFamilyAttr$2(block.fontFamily, context);
514
533
  const widthAttr = renderWidthAttr(block.width);
515
- const visibilityAttr = getCssClassAttr(block);
534
+ const cssClassAttr = getCssClassAttr(block, block.width === "full" ? [BUTTON_FULL_WIDTH_CSS_CLASS] : []);
516
535
  return `<mj-button${hrefAttr}${targetAttr}
517
536
  background-color="${backgroundColor}"
518
537
  color="${textColor}"
@@ -521,7 +540,7 @@ function renderButton(block, context) {
521
540
  border-radius="${borderRadius}"${borderAttr(block.border)}
522
541
  inner-padding="${buttonPadding}"
523
542
  align="${block.align ?? "center"}"
524
- padding="${padding}"${bgColor}${fontFamilyAttr}${widthAttr}${visibilityAttr}
543
+ padding="${padding}"${bgColor}${fontFamilyAttr}${widthAttr}${cssClassAttr}
525
544
  >${text}</mj-button>`;
526
545
  }
527
546
  /**
@@ -534,7 +553,7 @@ function buttonBackgroundAttr(color) {
534
553
  }
535
554
  function renderFontFamilyAttr$2(fontFamily, context) {
536
555
  if (!fontFamily) return "";
537
- return ` font-family="${context.resolveFontFamily(fontFamily)}"`;
556
+ return ` font-family="${escapeFontFamily(context.resolveFontFamily(fontFamily))}"`;
538
557
  }
539
558
  /**
540
559
  * The button has always emitted its radius, `0px` included, so a plain number
@@ -603,13 +622,14 @@ ${content}
603
622
  /**
604
623
  * Render a social icons block to MJML markup.
605
624
  *
606
- * Icons are emitted as `<img src="…/{style}/{platform}.png">` rather than
625
+ * Icons are emitted as `<img src="…/{dir}/{platform}.png">` rather than
607
626
  * inline SVG or base64 data URIs. Outlook desktop (Word rendering engine)
608
627
  * does not support SVG and rejects base64 in `<img src>`, so hosted PNGs are
609
628
  * the only format that renders across every mainstream client. The base URL
610
629
  * is read from `context.socialIconsBaseUrl` (configurable via
611
630
  * `RenderOptions.socialIconsBaseUrl`; default is the version-pinned jsDelivr
612
- * mirror of this package).
631
+ * mirror of this package). `{dir}` is the style, plus `-{tone}` for a
632
+ * one-color tone (`socialIconAssetDir`).
613
633
  */
614
634
  function renderSocialIcons(block, context) {
615
635
  if (isHiddenOnAll(block)) return "";
@@ -621,6 +641,7 @@ function renderSocialIcons(block, context) {
621
641
  const align = block.align;
622
642
  const iconSize = block.iconSize;
623
643
  const iconStyle = block.iconStyle;
644
+ const assetDir = socialIconAssetDir(iconStyle, block.iconTone);
624
645
  const spacing = block.spacing;
625
646
  let iconSizePx;
626
647
  switch (iconSize) {
@@ -655,7 +676,7 @@ function renderSocialIcons(block, context) {
655
676
  ${icons.map((icon, index) => {
656
677
  const platform = icon.platform;
657
678
  const url = escapeAttr(icon.url);
658
- const iconSrc = `${context.socialIconsBaseUrl}/${iconStyle}/${platform}.png`;
679
+ const iconSrc = `${context.socialIconsBaseUrl}/${assetDir}/${platform}.png`;
659
680
  const rightPad = index === iconCount - 1 ? 0 : spacing;
660
681
  return `<mj-social-element src="${iconSrc}" href="${url}" icon-size="${iconSizePx}px" padding="0 ${rightPad}px 0 0" border-radius="${borderRadius}" background-color="transparent"></mj-social-element>`;
661
682
  }).join("\n")}
@@ -709,7 +730,7 @@ function renderMenuItem(item, linkColor) {
709
730
  }
710
731
  function renderFontFamilyAttr$1(fontFamily, context) {
711
732
  if (!fontFamily) return "";
712
- return ` font-family="${context.resolveFontFamily(fontFamily)}"`;
733
+ return ` font-family="${escapeFontFamily(context.resolveFontFamily(fontFamily))}"`;
713
734
  }
714
735
  //#endregion
715
736
  //#region src/renderers/table.ts
@@ -762,7 +783,7 @@ function renderCell(cell, block, isHeader, borderColor, borderWidth) {
762
783
  }
763
784
  function renderFontFamilyAttr(fontFamily, context) {
764
785
  if (!fontFamily) return "";
765
- return ` font-family="${context.resolveFontFamily(fontFamily)}"`;
786
+ return ` font-family="${escapeFontFamily(context.resolveFontFamily(fontFamily))}"`;
766
787
  }
767
788
  //#endregion
768
789
  //#region src/renderers/custom.ts
@@ -960,6 +981,19 @@ function renderBlock(block, context) {
960
981
  //#endregion
961
982
  //#region src/index.ts
962
983
  /**
984
+ * Makes a full-width button's whole fill clickable. MJML sizes a button's `<a>`
985
+ * only for a px width, so without this rule the link of a `100%` button stays
986
+ * the size of its label.
987
+ *
988
+ * It sits in the inline `<mj-style>` so `mjml`'s juice pass writes it onto the
989
+ * `<a>`; MRML's default build emits that block as a plain `<style>` instead.
990
+ * `!important` lets it beat MJML's inline `display:inline-block` either way.
991
+ * Every template carries it, so nothing has to walk the tree for full-width
992
+ * buttons: juice drops a rule that matches nothing, so with `mjml` it costs
993
+ * nothing in the compiled HTML.
994
+ */
995
+ const BUTTON_FULL_WIDTH_RULE = `.${BUTTON_FULL_WIDTH_CSS_CLASS} a { display: block !important; }`;
996
+ /**
963
997
  * Render template content to an MJML string.
964
998
  *
965
999
  * The function is async because resolving custom blocks may require
@@ -990,7 +1024,7 @@ async function renderToMjml(content, options) {
990
1024
  return `<mjml lang="${escapeAttr(toRender.settings.locale)}" dir="${escapeAttr(contentDirection)}">
991
1025
  <mj-head>${previewTag}
992
1026
  <mj-attributes>
993
- <mj-all font-family="${fontFamily}" />
1027
+ <mj-all font-family="${escapeFontFamily(fontFamily)}" />
994
1028
  <mj-text font-size="14px"${textColorAttr} />
995
1029
  <mj-section padding="0" />
996
1030
  <mj-column padding="0" />
@@ -998,6 +1032,7 @@ async function renderToMjml(content, options) {
998
1032
  </mj-attributes>${fontDeclarations}
999
1033
  <mj-style inline="inline">
1000
1034
  ${richTextStylesheet(collectParagraphGaps(blocks), contentDirection)}
1035
+ ${BUTTON_FULL_WIDTH_RULE}
1001
1036
  </mj-style>
1002
1037
  <mj-style>
1003
1038
  a { color: ${linkColor}; text-decoration: ${linkDecoration}; }
@@ -1139,6 +1174,6 @@ function renderCustomBlockStylesheets(stylesheets) {
1139
1174
  return stylesheets.map((css) => `\n <mj-style>\n${css}\n </mj-style>`).join("");
1140
1175
  }
1141
1176
  //#endregion
1142
- export { DEFAULT_SOCIAL_ICONS_BASE_URL, RICH_TEXT_CSS_CLASS, RenderContext, UNRENDERABLE_MARKER_PREFIX, convertMergeTagsToValues, escapeAttr, escapeHtml, getCssClassAttr, getCssClasses, getWidthPercentages, getWidthPixels, isHiddenOnAll, renderBlock, renderToMjml, renderUnrenderableBlock, resolveParagraphGap, richTextGapClass, toPaddingString };
1177
+ export { BUTTON_FULL_WIDTH_CSS_CLASS, DEFAULT_SOCIAL_ICONS_BASE_URL, RICH_TEXT_CSS_CLASS, RenderContext, UNRENDERABLE_MARKER_PREFIX, convertMergeTagsToValues, escapeAttr, escapeHtml, getCssClassAttr, getCssClasses, getWidthPercentages, getWidthPixels, isHiddenOnAll, renderBlock, renderToMjml, renderUnrenderableBlock, resolveParagraphGap, richTextGapClass, toPaddingString };
1143
1178
 
1144
1179
  //# sourceMappingURL=index.js.map