@lupinum/nuxt-email 0.1.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.
- package/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/THIRD_PARTY_NOTICES +75 -0
- package/dist/chunks/setup.mjs +149 -0
- package/dist/module.d.mts +15 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +300 -0
- package/dist/runtime/code-block/create-component.d.ts +16 -0
- package/dist/runtime/code-block/create-component.js +106 -0
- package/dist/runtime/components/EBody.d.ts +4 -0
- package/dist/runtime/components/EBody.js +75 -0
- package/dist/runtime/components/EButton.d.ts +11 -0
- package/dist/runtime/components/EButton.js +89 -0
- package/dist/runtime/components/EButton.padding.d.ts +12 -0
- package/dist/runtime/components/EButton.padding.js +118 -0
- package/dist/runtime/components/ECodeInline.d.ts +13 -0
- package/dist/runtime/components/ECodeInline.js +43 -0
- package/dist/runtime/components/EColumn.d.ts +4 -0
- package/dist/runtime/components/EColumn.js +12 -0
- package/dist/runtime/components/EContainer.d.ts +4 -0
- package/dist/runtime/components/EContainer.js +35 -0
- package/dist/runtime/components/EFont.d.ts +24 -0
- package/dist/runtime/components/EFont.js +124 -0
- package/dist/runtime/components/EHead.d.ts +4 -0
- package/dist/runtime/components/EHead.js +17 -0
- package/dist/runtime/components/EHeading.d.ts +7 -0
- package/dist/runtime/components/EHeading.js +23 -0
- package/dist/runtime/components/EHr.d.ts +4 -0
- package/dist/runtime/components/EHr.js +26 -0
- package/dist/runtime/components/EHtml.d.ts +4 -0
- package/dist/runtime/components/EHtml.js +17 -0
- package/dist/runtime/components/EImg.d.ts +7 -0
- package/dist/runtime/components/EImg.js +44 -0
- package/dist/runtime/components/ELink.d.ts +6 -0
- package/dist/runtime/components/ELink.js +35 -0
- package/dist/runtime/components/EMarkdown.d.ts +8 -0
- package/dist/runtime/components/EMarkdown.js +64 -0
- package/dist/runtime/components/EPreview.d.ts +8 -0
- package/dist/runtime/components/EPreview.js +85 -0
- package/dist/runtime/components/ERow.d.ts +4 -0
- package/dist/runtime/components/ERow.js +53 -0
- package/dist/runtime/components/ESection.d.ts +4 -0
- package/dist/runtime/components/ESection.js +35 -0
- package/dist/runtime/components/ETailwind.d.ts +24 -0
- package/dist/runtime/components/ETailwind.js +49 -0
- package/dist/runtime/components/EText.d.ts +4 -0
- package/dist/runtime/components/EText.js +20 -0
- package/dist/runtime/components/EText.margins.d.ts +18 -0
- package/dist/runtime/components/EText.margins.js +81 -0
- package/dist/runtime/components/attributes.d.ts +4 -0
- package/dist/runtime/components/attributes.js +11 -0
- package/dist/runtime/components/email-component-names.d.ts +2 -0
- package/dist/runtime/components/email-component-names.js +20 -0
- package/dist/runtime/components/email-component-registry.d.ts +8 -0
- package/dist/runtime/components/email-component-registry.js +2 -0
- package/dist/runtime/components/index.d.ts +23 -0
- package/dist/runtime/components/index.js +18 -0
- package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.d.ts +2 -0
- package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.js +60 -0
- package/dist/runtime/components/markdown/render-markdown.d.ts +2 -0
- package/dist/runtime/components/markdown/render-markdown.js +137 -0
- package/dist/runtime/components/markdown/styles.d.ts +30 -0
- package/dist/runtime/components/markdown/styles.js +91 -0
- package/dist/runtime/components/presentation-table.d.ts +7 -0
- package/dist/runtime/components/presentation-table.js +11 -0
- package/dist/runtime/components/style.d.ts +5 -0
- package/dist/runtime/components/style.js +32 -0
- package/dist/runtime/components/table-padding.d.ts +6 -0
- package/dist/runtime/components/table-padding.js +20 -0
- package/dist/runtime/define-email.d.ts +2 -0
- package/dist/runtime/define-email.js +5 -0
- package/dist/runtime/dev-preview/page-script.d.ts +1 -0
- package/dist/runtime/dev-preview/page-script.js +403 -0
- package/dist/runtime/dev-preview/page.css.d.ts +1 -0
- package/dist/runtime/dev-preview/page.css.js +602 -0
- package/dist/runtime/dev-preview/page.get.d.ts +4 -0
- package/dist/runtime/dev-preview/page.get.js +119 -0
- package/dist/runtime/dev-preview/render.get.d.ts +3 -0
- package/dist/runtime/dev-preview/render.get.js +123 -0
- package/dist/runtime/dev-preview/templates.get.d.ts +7 -0
- package/dist/runtime/dev-preview/templates.get.js +14 -0
- package/dist/runtime/errors.d.ts +4 -0
- package/dist/runtime/errors.js +7 -0
- package/dist/runtime/render/define-email.d.ts +56 -0
- package/dist/runtime/render/define-email.js +40 -0
- package/dist/runtime/render/document.d.ts +4 -0
- package/dist/runtime/render/document.js +75 -0
- package/dist/runtime/render/errors.d.ts +4 -0
- package/dist/runtime/render/errors.js +8 -0
- package/dist/runtime/render/plain-text.d.ts +1 -0
- package/dist/runtime/render/plain-text.js +15 -0
- package/dist/runtime/render/render-component.d.ts +4 -0
- package/dist/runtime/render/render-component.js +46 -0
- package/dist/runtime/render/render-email-component.d.ts +16 -0
- package/dist/runtime/render/render-email-component.js +46 -0
- package/dist/runtime/render/types.d.ts +9 -0
- package/dist/runtime/render/types.js +0 -0
- package/dist/runtime/tailwind/engine/compatibility/escape-class-name.d.ts +8 -0
- package/dist/runtime/tailwind/engine/compatibility/escape-class-name.js +10 -0
- package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.d.ts +7 -0
- package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.js +17 -0
- package/dist/runtime/tailwind/engine/compatibility/unescape-class.d.ts +1 -0
- package/dist/runtime/tailwind/engine/compatibility/unescape-class.js +3 -0
- package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.d.ts +63 -0
- package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.js +107 -0
- package/dist/runtime/tailwind/engine/css/extract-rules-per-class.d.ts +6 -0
- package/dist/runtime/tailwind/engine/css/extract-rules-per-class.js +54 -0
- package/dist/runtime/tailwind/engine/css/get-css-property.d.ts +17 -0
- package/dist/runtime/tailwind/engine/css/get-css-property.js +3 -0
- package/dist/runtime/tailwind/engine/css/get-custom-properties.d.ts +8 -0
- package/dist/runtime/tailwind/engine/css/get-custom-properties.js +37 -0
- package/dist/runtime/tailwind/engine/css/is-part-inlinable.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/is-part-inlinable.js +9 -0
- package/dist/runtime/tailwind/engine/css/is-rule-inlinable.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/is-rule-inlinable.js +9 -0
- package/dist/runtime/tailwind/engine/css/make-inline-styles-for.d.ts +21 -0
- package/dist/runtime/tailwind/engine/css/make-inline-styles-for.js +62 -0
- package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.d.ts +8 -0
- package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.js +138 -0
- package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.d.ts +5 -0
- package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.js +101 -0
- package/dist/runtime/tailwind/engine/css/sanitize-declarations.d.ts +15 -0
- package/dist/runtime/tailwind/engine/css/sanitize-declarations.js +358 -0
- package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.d.ts +17 -0
- package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.js +30 -0
- package/dist/runtime/tailwind/engine/css/split-mixed-rule.d.ts +12 -0
- package/dist/runtime/tailwind/engine/css/split-mixed-rule.js +37 -0
- package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.d.ts +19 -0
- package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.js +44 -0
- package/dist/runtime/tailwind/engine/css/unwrap-value.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/unwrap-value.js +6 -0
- package/dist/runtime/tailwind/engine/index.d.ts +63 -0
- package/dist/runtime/tailwind/engine/index.js +53 -0
- package/dist/runtime/tailwind/engine/pixel-based-preset.d.ts +9 -0
- package/dist/runtime/tailwind/engine/pixel-based-preset.js +58 -0
- package/dist/runtime/tailwind/engine/sanitize-stylesheet.d.ts +10 -0
- package/dist/runtime/tailwind/engine/sanitize-stylesheet.js +8 -0
- package/dist/runtime/tailwind/engine/setup-tailwind.d.ts +23 -0
- package/dist/runtime/tailwind/engine/setup-tailwind.js +98 -0
- package/dist/runtime/tailwind/engine/stylesheets.d.ts +4 -0
- package/dist/runtime/tailwind/engine/stylesheets.js +1756 -0
- package/dist/runtime/tailwind/engine/types.d.ts +25 -0
- package/dist/runtime/tailwind/engine/types.js +0 -0
- package/dist/runtime/tailwind/errors.d.ts +7 -0
- package/dist/runtime/tailwind/errors.js +9 -0
- package/dist/runtime/tailwind/inline-utils.d.ts +19 -0
- package/dist/runtime/tailwind/inline-utils.js +24 -0
- package/dist/runtime/tailwind/nested.d.ts +59 -0
- package/dist/runtime/tailwind/nested.js +47 -0
- package/dist/runtime/tailwind/post-render.d.ts +7 -0
- package/dist/runtime/tailwind/post-render.js +156 -0
- package/dist/runtime/template-registry/errors.d.ts +5 -0
- package/dist/runtime/template-registry/errors.js +11 -0
- package/dist/runtime/template-registry/registry.d.ts +13 -0
- package/dist/runtime/testing/index.d.ts +13 -0
- package/dist/runtime/testing/index.js +2 -0
- package/dist/types.d.mts +5 -0
- package/package.json +122 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { normalizeEmailStyle } from "./style.js";
|
|
2
|
+
export function convertToPixels(value) {
|
|
3
|
+
if (typeof value === "number") {
|
|
4
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
5
|
+
throw new TypeError(`EButton padding must be a finite non-negative value; received ${String(value)}`);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
if (typeof value !== "string") {
|
|
10
|
+
throw new TypeError(`EButton padding must be a number or CSS length string; received ${String(value)}`);
|
|
11
|
+
}
|
|
12
|
+
const normalizedValue = value.trim();
|
|
13
|
+
if (normalizedValue === "0") {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const matches = /^(\d+(?:\.\d+)?|\.\d+)(px|em|rem|%)$/.exec(normalizedValue);
|
|
17
|
+
if (!matches) {
|
|
18
|
+
throw new TypeError(
|
|
19
|
+
`EButton padding supports only non-negative px, em, rem, and % values; received ${value}`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const amount = Number.parseFloat(matches[1]);
|
|
23
|
+
if (!Number.isFinite(amount)) {
|
|
24
|
+
throw new TypeError(`EButton padding must resolve to finite pixels; received ${value}`);
|
|
25
|
+
}
|
|
26
|
+
switch (matches[2]) {
|
|
27
|
+
case "em":
|
|
28
|
+
case "rem":
|
|
29
|
+
return amount * 16;
|
|
30
|
+
case "%":
|
|
31
|
+
return amount / 100 * 600;
|
|
32
|
+
default:
|
|
33
|
+
return amount;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function expandPadding(value) {
|
|
37
|
+
if (typeof value === "number") {
|
|
38
|
+
return {
|
|
39
|
+
paddingTop: value,
|
|
40
|
+
paddingBottom: value,
|
|
41
|
+
paddingLeft: value,
|
|
42
|
+
paddingRight: value
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === "string") {
|
|
46
|
+
const values = value.trim().split(/\s+/);
|
|
47
|
+
if (values.length === 1) {
|
|
48
|
+
return {
|
|
49
|
+
paddingTop: values[0],
|
|
50
|
+
paddingBottom: values[0],
|
|
51
|
+
paddingLeft: values[0],
|
|
52
|
+
paddingRight: values[0]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (values.length === 2) {
|
|
56
|
+
return {
|
|
57
|
+
paddingTop: values[0],
|
|
58
|
+
paddingRight: values[1],
|
|
59
|
+
paddingBottom: values[0],
|
|
60
|
+
paddingLeft: values[1]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (values.length === 3) {
|
|
64
|
+
return {
|
|
65
|
+
paddingTop: values[0],
|
|
66
|
+
paddingRight: values[1],
|
|
67
|
+
paddingBottom: values[2],
|
|
68
|
+
paddingLeft: values[1]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (values.length === 4) {
|
|
72
|
+
return {
|
|
73
|
+
paddingTop: values[0],
|
|
74
|
+
paddingRight: values[1],
|
|
75
|
+
paddingBottom: values[2],
|
|
76
|
+
paddingLeft: values[3]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new TypeError(`EButton padding shorthand must contain one to four values; received ${String(value)}`);
|
|
81
|
+
}
|
|
82
|
+
function convertOptionalPadding(value) {
|
|
83
|
+
return value === void 0 ? void 0 : convertToPixels(value);
|
|
84
|
+
}
|
|
85
|
+
export function parseButtonPadding(style) {
|
|
86
|
+
let paddingTop;
|
|
87
|
+
let paddingRight;
|
|
88
|
+
let paddingBottom;
|
|
89
|
+
let paddingLeft;
|
|
90
|
+
for (const [property, value] of Object.entries(normalizeEmailStyle(style) ?? {})) {
|
|
91
|
+
if (property === "padding") {
|
|
92
|
+
({ paddingTop, paddingRight, paddingBottom, paddingLeft } = expandPadding(value));
|
|
93
|
+
} else if (property === "paddingTop") {
|
|
94
|
+
paddingTop = value;
|
|
95
|
+
} else if (property === "paddingRight") {
|
|
96
|
+
paddingRight = value;
|
|
97
|
+
} else if (property === "paddingBottom") {
|
|
98
|
+
paddingBottom = value;
|
|
99
|
+
} else if (property === "paddingLeft") {
|
|
100
|
+
paddingLeft = value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
paddingTop: convertOptionalPadding(paddingTop),
|
|
105
|
+
paddingRight: convertOptionalPadding(paddingRight),
|
|
106
|
+
paddingBottom: convertOptionalPadding(paddingBottom),
|
|
107
|
+
paddingLeft: convertOptionalPadding(paddingLeft)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export function pixelsToPoints(pixels) {
|
|
111
|
+
return typeof pixels === "number" && !Number.isNaN(Number(pixels)) ? pixels * 3 / 4 : void 0;
|
|
112
|
+
}
|
|
113
|
+
export function pixelStyle(value) {
|
|
114
|
+
if (value === void 0 || value === 0) {
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
return `${value}px`;
|
|
118
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DefineComponent, HTMLAttributes } from 'vue';
|
|
2
|
+
import type { SafeEmailAttributes } from './attributes.js';
|
|
3
|
+
export type ECodeInlineProps = SafeEmailAttributes<HTMLAttributes>;
|
|
4
|
+
/**
|
|
5
|
+
* Byte-identical to React Email's Orange.fr fix-up `<style>` (see
|
|
6
|
+
* code-inline.tsx). Exported so conformance can assert the exact bytes.
|
|
7
|
+
*
|
|
8
|
+
* On the Orange.fr email client the head/html elements are removed, turning
|
|
9
|
+
* the meta tag into a sibling; the `meta ~` selectors then swap which copy of
|
|
10
|
+
* the children is visible.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ORANGE_FR_STYLE = "\n meta ~ .cino {\n display: none !important;\n opacity: 0 !important;\n }\n\n meta ~ .cio {\n display: block !important;\n }\n ";
|
|
13
|
+
export declare const ECodeInline: DefineComponent<ECodeInlineProps>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineComponent, h } from "vue";
|
|
2
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
3
|
+
import { mergeEmailStyles } from "./style.js";
|
|
4
|
+
export const ORANGE_FR_STYLE = `
|
|
5
|
+
meta ~ .cino {
|
|
6
|
+
display: none !important;
|
|
7
|
+
opacity: 0 !important;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
meta ~ .cio {
|
|
11
|
+
display: block !important;
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
export const ECodeInline = defineComponent({
|
|
15
|
+
name: "ECodeInline",
|
|
16
|
+
inheritAttrs: false,
|
|
17
|
+
setup(_props, { attrs, slots }) {
|
|
18
|
+
return () => {
|
|
19
|
+
assertSafeEmailAttributes("ECodeInline", attrs);
|
|
20
|
+
const { class: className, style, ...attributes } = attrs;
|
|
21
|
+
const userClass = typeof className === "string" && className ? className : "";
|
|
22
|
+
const codeProps = {
|
|
23
|
+
...attributes,
|
|
24
|
+
class: `${userClass} cino`
|
|
25
|
+
};
|
|
26
|
+
if (style !== void 0) {
|
|
27
|
+
codeProps.style = style;
|
|
28
|
+
}
|
|
29
|
+
return [
|
|
30
|
+
h("style", ORANGE_FR_STYLE),
|
|
31
|
+
// Does not render on Orange.fr
|
|
32
|
+
h("code", codeProps, slots.default?.()),
|
|
33
|
+
// Renders only on Orange.fr
|
|
34
|
+
h("span", {
|
|
35
|
+
...attributes,
|
|
36
|
+
"class": `${userClass} cio`,
|
|
37
|
+
"style": mergeEmailStyles({ display: "none" }, style),
|
|
38
|
+
"data-skip-in-text": "true"
|
|
39
|
+
}, slots.default?.())
|
|
40
|
+
];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineComponent, h } from "vue";
|
|
2
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
3
|
+
export const EColumn = defineComponent({
|
|
4
|
+
name: "EColumn",
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
setup(_props, { attrs, slots }) {
|
|
7
|
+
return () => {
|
|
8
|
+
assertSafeEmailAttributes("EColumn", attrs);
|
|
9
|
+
return h("td", attrs, slots.default?.());
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DefineComponent, TableHTMLAttributes } from 'vue';
|
|
2
|
+
import type { SafeEmailAttributes } from './attributes.js';
|
|
3
|
+
export type EContainerProps = Omit<SafeEmailAttributes<TableHTMLAttributes>, 'border' | 'cellpadding' | 'cellspacing' | 'role'>;
|
|
4
|
+
export declare const EContainer: DefineComponent<EContainerProps>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineComponent, h, inject } from "vue";
|
|
2
|
+
import { resolveNestedTailwindStyle, TAILWIND_NESTED_KEY } from "../tailwind/nested.js";
|
|
3
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
4
|
+
import { assertFixedPresentationTable } from "./presentation-table.js";
|
|
5
|
+
import { splitTablePadding } from "./table-padding.js";
|
|
6
|
+
export const EContainer = defineComponent({
|
|
7
|
+
name: "EContainer",
|
|
8
|
+
inheritAttrs: false,
|
|
9
|
+
setup(_props, { attrs, slots }) {
|
|
10
|
+
const holder = inject(TAILWIND_NESTED_KEY, null);
|
|
11
|
+
return () => {
|
|
12
|
+
assertSafeEmailAttributes("EContainer", attrs);
|
|
13
|
+
assertFixedPresentationTable("EContainer", attrs);
|
|
14
|
+
const { style, ...attributes } = attrs;
|
|
15
|
+
const effectiveStyle = resolveNestedTailwindStyle(holder, attributes, style).style;
|
|
16
|
+
const { tableStyle, cellStyle } = splitTablePadding(effectiveStyle);
|
|
17
|
+
return h("table", {
|
|
18
|
+
align: "center",
|
|
19
|
+
width: "100%",
|
|
20
|
+
...attributes,
|
|
21
|
+
border: 0,
|
|
22
|
+
cellpadding: "0",
|
|
23
|
+
cellspacing: "0",
|
|
24
|
+
role: "presentation",
|
|
25
|
+
style: { maxWidth: "37.5em", ...tableStyle }
|
|
26
|
+
}, [
|
|
27
|
+
h("tbody", [
|
|
28
|
+
h("tr", { style: { width: "100%" } }, [
|
|
29
|
+
h("td", Object.keys(cellStyle).length > 0 ? { style: cellStyle } : {}, slots.default?.())
|
|
30
|
+
])
|
|
31
|
+
])
|
|
32
|
+
]);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DefineComponent } from 'vue';
|
|
2
|
+
export type FontFallback = 'Arial' | 'Helvetica' | 'Verdana' | 'Georgia' | 'Times New Roman' | 'serif' | 'sans-serif' | 'monospace' | 'cursive' | 'fantasy';
|
|
3
|
+
export type FontFormat = 'woff' | 'woff2' | 'truetype' | 'opentype' | 'embedded-opentype' | 'svg';
|
|
4
|
+
export interface FontWebFont {
|
|
5
|
+
url: string;
|
|
6
|
+
format: FontFormat;
|
|
7
|
+
}
|
|
8
|
+
export type FontStyle = 'normal' | 'italic' | 'oblique';
|
|
9
|
+
export type FontWeight = number | 'normal' | 'bold' | 'bolder' | 'lighter';
|
|
10
|
+
export type FontFallbackList = readonly [FontFallback, ...FontFallback[]];
|
|
11
|
+
export interface EFontProps {
|
|
12
|
+
/** The font you want to use. Do not insert multiple fonts here, use fallbackFontFamily for that. */
|
|
13
|
+
fontFamily: string;
|
|
14
|
+
/** A non-empty array is possible; the order of the array is the priority order. */
|
|
15
|
+
fallbackFontFamily: FontFallback | FontFallbackList;
|
|
16
|
+
/** Web font source; not all clients support web fonts. */
|
|
17
|
+
webFont?: FontWebFont;
|
|
18
|
+
/** Default: 'normal' */
|
|
19
|
+
fontStyle?: FontStyle;
|
|
20
|
+
/** Default: 400 */
|
|
21
|
+
fontWeight?: FontWeight;
|
|
22
|
+
}
|
|
23
|
+
/** The component MUST be placed inside the <head> tag. */
|
|
24
|
+
export declare const EFont: DefineComponent<EFontProps>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { defineComponent, h } from "vue";
|
|
2
|
+
const FONT_FALLBACKS = /* @__PURE__ */ new Set([
|
|
3
|
+
"Arial",
|
|
4
|
+
"Helvetica",
|
|
5
|
+
"Verdana",
|
|
6
|
+
"Georgia",
|
|
7
|
+
"Times New Roman",
|
|
8
|
+
"serif",
|
|
9
|
+
"sans-serif",
|
|
10
|
+
"monospace",
|
|
11
|
+
"cursive",
|
|
12
|
+
"fantasy"
|
|
13
|
+
]);
|
|
14
|
+
const FONT_FORMATS = /* @__PURE__ */ new Set([
|
|
15
|
+
"woff",
|
|
16
|
+
"woff2",
|
|
17
|
+
"truetype",
|
|
18
|
+
"opentype",
|
|
19
|
+
"embedded-opentype",
|
|
20
|
+
"svg"
|
|
21
|
+
]);
|
|
22
|
+
const FONT_STYLES = /* @__PURE__ */ new Set(["normal", "italic", "oblique"]);
|
|
23
|
+
const FONT_WEIGHTS = /* @__PURE__ */ new Set(["normal", "bold", "bolder", "lighter"]);
|
|
24
|
+
const SAFE_UNQUOTED_URL = /^[\w\-.~:/?#[\]@!$&*+,;=%]+$/;
|
|
25
|
+
function cssString(value) {
|
|
26
|
+
return value.replace(/[\\'\n\r\f\0<>]/g, (character) => {
|
|
27
|
+
switch (character) {
|
|
28
|
+
case "\\":
|
|
29
|
+
return "\\\\";
|
|
30
|
+
case "'":
|
|
31
|
+
return "\\'";
|
|
32
|
+
case "\n":
|
|
33
|
+
case "\r":
|
|
34
|
+
case "\f":
|
|
35
|
+
return "\\A ";
|
|
36
|
+
case "\0":
|
|
37
|
+
return "\\FFFD ";
|
|
38
|
+
case "<":
|
|
39
|
+
return "\\3C ";
|
|
40
|
+
case ">":
|
|
41
|
+
return "\\3E ";
|
|
42
|
+
default:
|
|
43
|
+
return character;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function cssUrl(value) {
|
|
48
|
+
return SAFE_UNQUOTED_URL.test(value) ? value : `'${cssString(value)}'`;
|
|
49
|
+
}
|
|
50
|
+
function assertValidFontProps(props) {
|
|
51
|
+
if (props.fontFamily.trim().length === 0) {
|
|
52
|
+
throw new TypeError("EFont fontFamily must be a non-empty string");
|
|
53
|
+
}
|
|
54
|
+
const fallbacks = Array.isArray(props.fallbackFontFamily) ? props.fallbackFontFamily : [props.fallbackFontFamily];
|
|
55
|
+
if (fallbacks.length === 0) {
|
|
56
|
+
throw new TypeError("EFont fallbackFontFamily must contain at least one fallback");
|
|
57
|
+
}
|
|
58
|
+
const invalidFallback = fallbacks.find((fallback) => !FONT_FALLBACKS.has(fallback));
|
|
59
|
+
if (invalidFallback !== void 0) {
|
|
60
|
+
throw new TypeError(`EFont fallbackFontFamily contains unsupported fallback ${JSON.stringify(invalidFallback)}`);
|
|
61
|
+
}
|
|
62
|
+
if (!FONT_STYLES.has(props.fontStyle ?? "normal")) {
|
|
63
|
+
throw new TypeError(`EFont fontStyle must be normal, italic, or oblique; received ${JSON.stringify(props.fontStyle)}`);
|
|
64
|
+
}
|
|
65
|
+
const weight = props.fontWeight ?? 400;
|
|
66
|
+
if (typeof weight === "number" ? !Number.isFinite(weight) || weight < 1 || weight > 1e3 : !FONT_WEIGHTS.has(weight)) {
|
|
67
|
+
throw new TypeError(`EFont fontWeight must be between 1 and 1000 or a supported keyword; received ${JSON.stringify(weight)}`);
|
|
68
|
+
}
|
|
69
|
+
if (props.webFont !== void 0) {
|
|
70
|
+
if (props.webFont.url.trim().length === 0) {
|
|
71
|
+
throw new TypeError("EFont webFont.url must be a non-empty string");
|
|
72
|
+
}
|
|
73
|
+
if (!FONT_FORMATS.has(props.webFont.format)) {
|
|
74
|
+
throw new TypeError(`EFont webFont.format is unsupported: ${JSON.stringify(props.webFont.format)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export const EFont = defineComponent({
|
|
79
|
+
name: "EFont",
|
|
80
|
+
inheritAttrs: false,
|
|
81
|
+
props: {
|
|
82
|
+
fontFamily: {
|
|
83
|
+
type: String,
|
|
84
|
+
required: true
|
|
85
|
+
},
|
|
86
|
+
fallbackFontFamily: {
|
|
87
|
+
type: [String, Array],
|
|
88
|
+
required: true
|
|
89
|
+
},
|
|
90
|
+
webFont: {
|
|
91
|
+
type: Object,
|
|
92
|
+
default: void 0
|
|
93
|
+
},
|
|
94
|
+
fontStyle: {
|
|
95
|
+
type: String,
|
|
96
|
+
default: "normal"
|
|
97
|
+
},
|
|
98
|
+
fontWeight: {
|
|
99
|
+
type: [String, Number],
|
|
100
|
+
default: 400
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
setup(props) {
|
|
104
|
+
return () => {
|
|
105
|
+
assertValidFontProps(props);
|
|
106
|
+
const fallbacks = Array.isArray(props.fallbackFontFamily) ? props.fallbackFontFamily : [props.fallbackFontFamily];
|
|
107
|
+
const src = props.webFont ? `src: url(${cssUrl(props.webFont.url)}) format('${props.webFont.format}');` : "";
|
|
108
|
+
const style = `
|
|
109
|
+
@font-face {
|
|
110
|
+
font-family: '${cssString(props.fontFamily)}';
|
|
111
|
+
font-style: ${props.fontStyle};
|
|
112
|
+
font-weight: ${props.fontWeight};
|
|
113
|
+
mso-font-alt: '${fallbacks[0]}';
|
|
114
|
+
${src}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
* {
|
|
118
|
+
font-family: '${cssString(props.fontFamily)}', ${fallbacks.join(", ")};
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
return h("style", { innerHTML: style });
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineComponent, h } from "vue";
|
|
2
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
3
|
+
export const EHead = defineComponent({
|
|
4
|
+
name: "EHead",
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
setup(_props, { attrs, slots }) {
|
|
7
|
+
return () => {
|
|
8
|
+
assertSafeEmailAttributes("EHead", attrs);
|
|
9
|
+
const children = slots.default?.() ?? [];
|
|
10
|
+
return h("head", attrs, [
|
|
11
|
+
h("meta", { "content": "text/html; charset=UTF-8", "http-equiv": "Content-Type" }),
|
|
12
|
+
h("meta", { name: "x-apple-disable-message-reformatting" }),
|
|
13
|
+
...children
|
|
14
|
+
]);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DefineComponent, HTMLAttributes } from 'vue';
|
|
2
|
+
import type { SafeEmailAttributes } from './attributes.js';
|
|
3
|
+
export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
4
|
+
export type EHeadingProps = SafeEmailAttributes<HTMLAttributes> & {
|
|
5
|
+
as?: HeadingTag;
|
|
6
|
+
};
|
|
7
|
+
export declare const EHeading: DefineComponent<EHeadingProps>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineComponent, h } from "vue";
|
|
2
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
3
|
+
const HEADING_TAGS = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
4
|
+
export const EHeading = defineComponent({
|
|
5
|
+
name: "EHeading",
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
props: {
|
|
8
|
+
as: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: "h1"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
setup(props, { attrs, slots }) {
|
|
14
|
+
return () => {
|
|
15
|
+
assertSafeEmailAttributes("EHeading", attrs);
|
|
16
|
+
const tag = props.as ?? "h1";
|
|
17
|
+
if (!HEADING_TAGS.has(tag)) {
|
|
18
|
+
throw new TypeError(`EHeading as must be one of ${[...HEADING_TAGS].join(", ")}; received ${String(tag)}`);
|
|
19
|
+
}
|
|
20
|
+
return h(tag, attrs, slots.default?.());
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineComponent, h, inject } from "vue";
|
|
2
|
+
import { resolveNestedTailwindStyle, TAILWIND_NESTED_KEY } from "../tailwind/nested.js";
|
|
3
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
4
|
+
import { mergeEmailStyles } from "./style.js";
|
|
5
|
+
const DEFAULT_HR_STYLE = {
|
|
6
|
+
width: "100%",
|
|
7
|
+
border: "none",
|
|
8
|
+
borderColor: "transparent",
|
|
9
|
+
borderTop: "1px solid #eaeaea"
|
|
10
|
+
};
|
|
11
|
+
export const EHr = defineComponent({
|
|
12
|
+
name: "EHr",
|
|
13
|
+
inheritAttrs: false,
|
|
14
|
+
setup(_props, { attrs }) {
|
|
15
|
+
const holder = inject(TAILWIND_NESTED_KEY, null);
|
|
16
|
+
return () => {
|
|
17
|
+
assertSafeEmailAttributes("EHr", attrs);
|
|
18
|
+
const { style, ...attributes } = attrs;
|
|
19
|
+
const effectiveStyle = resolveNestedTailwindStyle(holder, attributes, style).style;
|
|
20
|
+
return h("hr", {
|
|
21
|
+
...attributes,
|
|
22
|
+
style: mergeEmailStyles(DEFAULT_HR_STYLE, effectiveStyle)
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineComponent, h } from "vue";
|
|
2
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
3
|
+
export const EHtml = defineComponent({
|
|
4
|
+
name: "EHtml",
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
setup(_props, { attrs, slots }) {
|
|
7
|
+
return () => {
|
|
8
|
+
assertSafeEmailAttributes("EHtml", attrs);
|
|
9
|
+
const { dir = "ltr", lang = "en", ...attributes } = attrs;
|
|
10
|
+
return h("html", {
|
|
11
|
+
...attributes,
|
|
12
|
+
dir,
|
|
13
|
+
lang
|
|
14
|
+
}, slots.default?.());
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DefineComponent, ImgHTMLAttributes } from 'vue';
|
|
2
|
+
import type { SafeEmailAttributes } from './attributes.js';
|
|
3
|
+
export type EImgProps = Omit<SafeEmailAttributes<ImgHTMLAttributes>, 'alt' | 'src'> & {
|
|
4
|
+
alt: string;
|
|
5
|
+
src: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const EImg: DefineComponent<EImgProps>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { defineComponent, h, inject } from "vue";
|
|
2
|
+
import { resolveNestedTailwindStyle, TAILWIND_NESTED_KEY } from "../tailwind/nested.js";
|
|
3
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
4
|
+
import { mergeEmailStyles } from "./style.js";
|
|
5
|
+
const DEFAULT_IMAGE_STYLE = {
|
|
6
|
+
display: "block",
|
|
7
|
+
outline: "none",
|
|
8
|
+
border: "none",
|
|
9
|
+
textDecoration: "none"
|
|
10
|
+
};
|
|
11
|
+
export const EImg = defineComponent({
|
|
12
|
+
name: "EImg",
|
|
13
|
+
inheritAttrs: false,
|
|
14
|
+
props: {
|
|
15
|
+
alt: {
|
|
16
|
+
type: String,
|
|
17
|
+
required: true
|
|
18
|
+
},
|
|
19
|
+
src: {
|
|
20
|
+
type: String,
|
|
21
|
+
required: true
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
setup(props, { attrs }) {
|
|
25
|
+
const holder = inject(TAILWIND_NESTED_KEY, null);
|
|
26
|
+
return () => {
|
|
27
|
+
assertSafeEmailAttributes("EImg", attrs);
|
|
28
|
+
if (typeof props.alt !== "string") {
|
|
29
|
+
throw new TypeError("EImg alt must be a string; use an empty string for decorative images");
|
|
30
|
+
}
|
|
31
|
+
if (typeof props.src !== "string" || props.src.length === 0) {
|
|
32
|
+
throw new TypeError("EImg src must be a non-empty string");
|
|
33
|
+
}
|
|
34
|
+
const { style, ...attributes } = attrs;
|
|
35
|
+
const effectiveStyle = resolveNestedTailwindStyle(holder, attributes, style).style;
|
|
36
|
+
return h("img", {
|
|
37
|
+
...attributes,
|
|
38
|
+
alt: props.alt,
|
|
39
|
+
src: props.src,
|
|
40
|
+
style: mergeEmailStyles(DEFAULT_IMAGE_STYLE, effectiveStyle)
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, DefineComponent } from 'vue';
|
|
2
|
+
import type { SafeEmailAttributes } from './attributes.js';
|
|
3
|
+
export type ELinkProps = Omit<SafeEmailAttributes<AnchorHTMLAttributes>, 'href'> & {
|
|
4
|
+
href: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const ELink: DefineComponent<ELinkProps>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineComponent, h, inject } from "vue";
|
|
2
|
+
import { resolveNestedTailwindStyle, TAILWIND_NESTED_KEY } from "../tailwind/nested.js";
|
|
3
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
4
|
+
import { mergeEmailStyles } from "./style.js";
|
|
5
|
+
const DEFAULT_LINK_STYLE = {
|
|
6
|
+
color: "#067df7",
|
|
7
|
+
textDecorationLine: "none"
|
|
8
|
+
};
|
|
9
|
+
export const ELink = defineComponent({
|
|
10
|
+
name: "ELink",
|
|
11
|
+
inheritAttrs: false,
|
|
12
|
+
props: {
|
|
13
|
+
href: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
setup(props, { attrs, slots }) {
|
|
19
|
+
const holder = inject(TAILWIND_NESTED_KEY, null);
|
|
20
|
+
return () => {
|
|
21
|
+
assertSafeEmailAttributes("ELink", attrs);
|
|
22
|
+
if (typeof props.href !== "string" || props.href.length === 0) {
|
|
23
|
+
throw new TypeError("ELink href must be a non-empty string");
|
|
24
|
+
}
|
|
25
|
+
const { style, ...attributes } = attrs;
|
|
26
|
+
const effectiveStyle = resolveNestedTailwindStyle(holder, attributes, style).style;
|
|
27
|
+
return h("a", {
|
|
28
|
+
...attributes,
|
|
29
|
+
href: props.href,
|
|
30
|
+
style: mergeEmailStyles(DEFAULT_LINK_STYLE, effectiveStyle),
|
|
31
|
+
target: attrs.target ?? "_blank"
|
|
32
|
+
}, slots.default?.());
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DefineComponent, HTMLAttributes } from 'vue';
|
|
2
|
+
import type { SafeEmailAttributes } from './attributes.js';
|
|
3
|
+
import type { StylesType } from './markdown/styles.js';
|
|
4
|
+
export type EMarkdownProps = SafeEmailAttributes<HTMLAttributes> & {
|
|
5
|
+
source?: string;
|
|
6
|
+
markdownCustomStyles?: StylesType;
|
|
7
|
+
};
|
|
8
|
+
export declare const EMarkdown: DefineComponent<EMarkdownProps>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Comment, defineComponent, Fragment, h, isVNode, Text } from "vue";
|
|
2
|
+
import { assertSafeEmailAttributes } from "./attributes.js";
|
|
3
|
+
import { renderMarkdown } from "./markdown/render-markdown.js";
|
|
4
|
+
function appendSlotText(child, text) {
|
|
5
|
+
if (child === null || child === void 0 || typeof child === "boolean") {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
9
|
+
text.value += String(child);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(child)) {
|
|
13
|
+
for (const nestedChild of child) {
|
|
14
|
+
appendSlotText(nestedChild, text);
|
|
15
|
+
}
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!isVNode(child)) {
|
|
19
|
+
throw new TypeError("EMarkdown default slot must contain text only");
|
|
20
|
+
}
|
|
21
|
+
if (child.type === Comment) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (child.type === Text) {
|
|
25
|
+
appendSlotText(child.children, text);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (child.type === Fragment && Array.isArray(child.children)) {
|
|
29
|
+
appendSlotText(child.children, text);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
throw new TypeError("EMarkdown default slot must contain text only");
|
|
33
|
+
}
|
|
34
|
+
function slotSource(children) {
|
|
35
|
+
const text = { value: "" };
|
|
36
|
+
appendSlotText(children, text);
|
|
37
|
+
return text.value;
|
|
38
|
+
}
|
|
39
|
+
export const EMarkdown = defineComponent({
|
|
40
|
+
name: "EMarkdown",
|
|
41
|
+
inheritAttrs: false,
|
|
42
|
+
props: {
|
|
43
|
+
source: {
|
|
44
|
+
type: String,
|
|
45
|
+
required: false,
|
|
46
|
+
default: void 0
|
|
47
|
+
},
|
|
48
|
+
markdownCustomStyles: {
|
|
49
|
+
type: Object,
|
|
50
|
+
required: false,
|
|
51
|
+
default: void 0
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
setup(props, { attrs, slots }) {
|
|
55
|
+
return () => {
|
|
56
|
+
assertSafeEmailAttributes("EMarkdown", attrs);
|
|
57
|
+
const source = typeof props.source === "string" ? props.source : slotSource(slots.default?.() ?? []);
|
|
58
|
+
return h("div", {
|
|
59
|
+
...attrs,
|
|
60
|
+
innerHTML: renderMarkdown(source, props.markdownCustomStyles)
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|