@maizzle/framework
Version:
Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.
80 lines (79 loc) • 3.46 kB
JavaScript
//#region src/components/utils.ts
function normalizeToPixels(value) {
if (typeof value === "number" || Number.isFinite(Number(value))) return `${value}px`;
return value;
}
const counters = {};
/**
* Module-scoped sequential ID generator. Used by components to mint
* unique marker ids (e.g. `c1`, `c2`) for the post-render transformer.
*
* Must live here (not inside `<script setup>`) because Vue compiles
* `<script setup>` into the component's `setup()` function — any
* `let counter = 0` there resets per instance, causing id collisions.
*/
function nextId(prefix) {
counters[prefix] = (counters[prefix] ?? 0) + 1;
return `${prefix}${counters[prefix]}`;
}
function hasWidthUtility(classStr) {
return classStr.split(/\s+/).some((c) => {
const clean = (c.split(":").pop() ?? "").replace(/^!/, "");
return /^(w-|max-w-|min-w-)/.test(clean);
});
}
function hasWidthInStyle(styleStr) {
return /(?:^|;\s*)(?:max-width|width)\s*:/i.test(styleStr);
}
function hasHeightUtility(classStr) {
return classStr.split(/\s+/).some((c) => {
const clean = (c.split(":").pop() ?? "").replace(/^!/, "");
return /^(h-|max-h-|min-h-)/.test(clean);
});
}
function hasHeightInStyle(styleStr) {
return /(?:^|;\s*)(?:max-height|height)\s*:/i.test(styleStr);
}
/**
* Shared prop for components that emit MSO/VML fallback markup. The
* `null` default acts as the "unset" sentinel — `useOutlookFallback`
* treats `null` as inherit-from-ancestor (root default `true`),
* letting users override per-component without losing inheritance.
*/
const outlookFallbackProp = {
type: Boolean,
default: null
};
/**
* Default utility classes for a code-block `<pre>`. `whitespace-pre!` is
* forced important so Gmail's stylesheet can't reset it to `normal`, and
* `m-0` strips the browser's default `<pre>` margins so it isn't spaced
* away from its wrapper.
*/
function codeBlockPreClass(bg) {
return `font-mono bg-[${bg}] p-4 m-0 overflow-auto whitespace-pre! [word-wrap:normal] [word-break:normal] [word-spacing:normal]`;
}
/**
* Build the email-safe table wrapper around highlighted code. Shared by the
* `<CodeBlock>` component and the Markdown fenced/indented code-block
* rules so both render identical markup: a full-width table whose
* cell carries the theme background, wrapping a `<pre>` styled
* with utility classes (not Shiki's raw inline styles).
*/
function buildCodeBlock(codeContent, bg, options = {}) {
const preClass = options.preClass ?? codeBlockPreClass(bg);
return `<table class="w-full"><tr><td class="${options.tdClass ?? `bg-[${bg}] max-w-0 mso-padding-alt-4`}"><pre class="${preClass}"${options.styleAttr ?? ""} data-juice-important><code>${codeContent}</code></pre></td></tr></table>`;
}
/**
* Re-wrap a Shiki (or plain markdown-it) `<pre><code>` block as a CodeBlock,
* pulling the inner code and the theme background out of the highlighted
* HTML. Falls back to a white background for unhighlighted blocks.
*/
function shikiToCodeBlock(highlighted) {
const trimmed = highlighted.trim();
const bg = (trimmed.match(/^<pre[^>]*>/)?.[0] ?? "").match(/background-color:\s*(#[0-9a-fA-F]+)/)?.[1] ?? "#fff";
return buildCodeBlock(trimmed.replace(/^<pre[^>]*><code[^>]*>/, "").replace(/<\/code><\/pre>$/, ""), bg);
}
//#endregion
export { buildCodeBlock, codeBlockPreClass, hasHeightInStyle, hasHeightUtility, hasWidthInStyle, hasWidthUtility, nextId, normalizeToPixels, outlookFallbackProp, shikiToCodeBlock };
//# sourceMappingURL=utils.js.map