@seliseblocks/mailcraft 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/DOCS.md +557 -0
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/mailcraft-editor.bundle.js +519 -0
- package/dist/mailcraft-editor.bundle.js.map +7 -0
- package/examples/templates/activate-your-account.html +54 -0
- package/examples/templates/back-in-stock.html +63 -0
- package/examples/templates/cart-left-behind.html +74 -0
- package/examples/templates/community-giveaway.html +81 -0
- package/examples/templates/frontend-futures-invite.html +69 -0
- package/examples/templates/give-25-get-25.html +69 -0
- package/examples/templates/invoice-paid.html +77 -0
- package/examples/templates/meet-nova-launch.html +69 -0
- package/examples/templates/mega-weekend-sale.html +57 -0
- package/examples/templates/order-confirmed.html +80 -0
- package/examples/templates/rate-your-headphones.html +71 -0
- package/examples/templates/reset-your-password.html +54 -0
- package/examples/templates/thankyou-promo-code.html +68 -0
- package/examples/templates/the-sunday-brief.html +47 -0
- package/examples/templates/welcome-to-your-workspace.html +62 -0
- package/examples/templates/your-order-shipped.html +74 -0
- package/examples/templates/your-password-was-changed.html +58 -0
- package/examples/templates/your-signin-code.html +58 -0
- package/examples/vanilla.html +1574 -0
- package/package.json +66 -0
- package/src/core/accent.js +194 -0
- package/src/core/assets.js +15 -0
- package/src/core/binder.js +119 -0
- package/src/core/blocks.js +256 -0
- package/src/core/css-cascade.js +117 -0
- package/src/core/editor-core.js +1492 -0
- package/src/core/export.js +58 -0
- package/src/core/footer.js +73 -0
- package/src/core/i18n/ar.js +177 -0
- package/src/core/i18n/bg.js +152 -0
- package/src/core/i18n/bn.js +176 -0
- package/src/core/i18n/ca.js +152 -0
- package/src/core/i18n/cs.js +152 -0
- package/src/core/i18n/da.js +152 -0
- package/src/core/i18n/de-CH.js +152 -0
- package/src/core/i18n/de.js +152 -0
- package/src/core/i18n/dz.js +179 -0
- package/src/core/i18n/el.js +152 -0
- package/src/core/i18n/en.js +269 -0
- package/src/core/i18n/es.js +152 -0
- package/src/core/i18n/et.js +152 -0
- package/src/core/i18n/fi.js +152 -0
- package/src/core/i18n/fr.js +152 -0
- package/src/core/i18n/hr.js +152 -0
- package/src/core/i18n/hu.js +152 -0
- package/src/core/i18n/index.js +83 -0
- package/src/core/i18n/it.js +152 -0
- package/src/core/i18n/lt.js +152 -0
- package/src/core/i18n/lv.js +152 -0
- package/src/core/i18n/nb.js +152 -0
- package/src/core/i18n/nl.js +152 -0
- package/src/core/i18n/pl.js +152 -0
- package/src/core/i18n/pt.js +152 -0
- package/src/core/i18n/ro.js +152 -0
- package/src/core/i18n/ru.js +152 -0
- package/src/core/i18n/sk.js +152 -0
- package/src/core/i18n/sl.js +152 -0
- package/src/core/i18n/sv.js +152 -0
- package/src/core/i18n/tables.js +50 -0
- package/src/core/i18n/tr.js +152 -0
- package/src/core/i18n/uk.js +152 -0
- package/src/core/icons.js +235 -0
- package/src/core/ids.js +1 -0
- package/src/core/import-html.js +959 -0
- package/src/core/layout-style.js +115 -0
- package/src/core/parse.js +10 -0
- package/src/core/placeholder.js +15 -0
- package/src/core/sanitize.js +141 -0
- package/src/core/storage-limits.js +184 -0
- package/src/core/storage.js +85 -0
- package/src/core/theme.js +1 -0
- package/src/core/toolbar.js +67 -0
- package/src/core/variables.js +11 -0
- package/src/create-editor.js +109 -0
- package/src/index.js +9 -0
- package/src/mailcraft-editor.js +1862 -0
- package/src/render/block-body.js +295 -0
- package/src/render/canvas.js +284 -0
- package/src/render/fields.js +609 -0
- package/src/render/focus-preserve.js +158 -0
- package/src/render/rte.js +212 -0
- package/src/render/screenshot.js +132 -0
- package/src/render/story.js +415 -0
- package/src/render/style.js +452 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import-time CSS cascade: folds a parsed document's `<style>` rules into the
|
|
3
|
+
* elements' inline styles, so the importer's classifiers (which read inline
|
|
4
|
+
* styles only) see class-styled templates -- Mailchimp exports, hand-written
|
|
5
|
+
* emails, framework output that was never inlined -- the same way a mail
|
|
6
|
+
* client would.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately a subset of a real cascade, matched to what email CSS uses:
|
|
9
|
+
* - `@media` blocks (and every other at-rule) are dropped whole: responsive
|
|
10
|
+
* overrides can't be represented in the imported model, desktop values win.
|
|
11
|
+
* A side benefit: base-rule `.desktop_hide { display:none }` still applies,
|
|
12
|
+
* so mobile-only duplicate content is correctly dropped by the importer's
|
|
13
|
+
* hidden-element skip.
|
|
14
|
+
* - Selectors containing `:` are skipped -- pseudo-classes/-elements are
|
|
15
|
+
* interactive or generated state with no place in a static import.
|
|
16
|
+
* - Specificity is the classic ids/classes/tags count; equal specificity
|
|
17
|
+
* resolves by source order; `!important` wins over everything including
|
|
18
|
+
* inline styles, which otherwise always win (matching browser behavior for
|
|
19
|
+
* the combinations that matter here).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
function stripComments(css) {
|
|
23
|
+
return String(css || '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Removes every at-rule: block-less ones (`@import ...;`) to the semicolon, block ones (`@media { ... }`) across balanced braces. */
|
|
27
|
+
function stripAtRules(css) {
|
|
28
|
+
let out = '';
|
|
29
|
+
let i = 0;
|
|
30
|
+
while (i < css.length) {
|
|
31
|
+
if (css[i] === '@') {
|
|
32
|
+
const semi = css.indexOf(';', i);
|
|
33
|
+
const brace = css.indexOf('{', i);
|
|
34
|
+
if (brace === -1 || (semi !== -1 && semi < brace)) {
|
|
35
|
+
i = semi === -1 ? css.length : semi + 1;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let depth = 0;
|
|
39
|
+
let j = brace;
|
|
40
|
+
for (; j < css.length; j++) {
|
|
41
|
+
if (css[j] === '{') depth++;
|
|
42
|
+
else if (css[j] === '}') { depth--; if (!depth) break; }
|
|
43
|
+
}
|
|
44
|
+
i = j + 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
out += css[i];
|
|
48
|
+
i++;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function specificity(sel) {
|
|
54
|
+
const ids = (sel.match(/#[\w-]+/g) || []).length;
|
|
55
|
+
const classes = (sel.match(/\.[\w-]+|\[[^\]]*\]/g) || []).length;
|
|
56
|
+
const tags = (sel.match(/(^|[\s>+~])[a-zA-Z][\w-]*/g) || []).length;
|
|
57
|
+
return ids * 100 + classes * 10 + tags;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function inlineStylesheets(doc) {
|
|
61
|
+
const sheets = Array.from(doc.querySelectorAll('style'));
|
|
62
|
+
if (!sheets.length) return;
|
|
63
|
+
const css = stripAtRules(stripComments(sheets.map((s) => s.textContent || '').join('\n')));
|
|
64
|
+
|
|
65
|
+
const rules = [];
|
|
66
|
+
const ruleRe = /([^{}]+)\{([^{}]*)\}/g;
|
|
67
|
+
let m;
|
|
68
|
+
let order = 0;
|
|
69
|
+
while ((m = ruleRe.exec(css))) {
|
|
70
|
+
const decls = [];
|
|
71
|
+
m[2].split(';').forEach((d) => {
|
|
72
|
+
const at = d.indexOf(':');
|
|
73
|
+
if (at < 0) return;
|
|
74
|
+
const prop = d.slice(0, at).trim().toLowerCase();
|
|
75
|
+
let value = d.slice(at + 1).trim();
|
|
76
|
+
if (!prop || !value || prop.indexOf('--') === 0 || prop.indexOf('mso-') === 0) return;
|
|
77
|
+
const important = /!important\s*$/i.test(value);
|
|
78
|
+
if (important) value = value.replace(/!important\s*$/i, '').trim();
|
|
79
|
+
if (value) decls.push({ prop, value, important });
|
|
80
|
+
});
|
|
81
|
+
if (!decls.length) continue;
|
|
82
|
+
m[1].split(',').forEach((raw) => {
|
|
83
|
+
const sel = raw.trim();
|
|
84
|
+
if (!sel || sel === '*' || sel.indexOf(':') > -1) return;
|
|
85
|
+
rules.push({ sel, decls, spec: specificity(sel), order: order++ });
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (!rules.length) return;
|
|
89
|
+
|
|
90
|
+
// Ascending: later (more specific / later-in-source) rules overwrite
|
|
91
|
+
// earlier winners per property below.
|
|
92
|
+
rules.sort((a, b) => a.spec - b.spec || a.order - b.order);
|
|
93
|
+
|
|
94
|
+
const winners = new Map(); // element -> Map(prop -> {value, important})
|
|
95
|
+
rules.forEach((rule) => {
|
|
96
|
+
let matched;
|
|
97
|
+
try { matched = doc.querySelectorAll(rule.sel); } catch { return; }
|
|
98
|
+
matched.forEach((el) => {
|
|
99
|
+
if (el !== doc.body && !doc.body.contains(el)) return;
|
|
100
|
+
let bucket = winners.get(el);
|
|
101
|
+
if (!bucket) { bucket = new Map(); winners.set(el, bucket); }
|
|
102
|
+
rule.decls.forEach((d) => {
|
|
103
|
+
const cur = bucket.get(d.prop);
|
|
104
|
+
if (cur && cur.important && !d.important) return;
|
|
105
|
+
bucket.set(d.prop, d);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
winners.forEach((bucket, el) => {
|
|
111
|
+
if (!el.style) return;
|
|
112
|
+
bucket.forEach((d, prop) => {
|
|
113
|
+
if (!d.important && el.style.getPropertyValue(prop)) return; // inline wins
|
|
114
|
+
try { el.style.setProperty(prop, d.value); } catch { /* unparseable value -- skip */ }
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|