@h-k-dev/angular-email-editor 0.0.3 → 0.0.4
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/fesm2022/h-k-dev-angular-email-editor-fonts.mjs +144 -0
- package/fesm2022/h-k-dev-angular-email-editor-fonts.mjs.map +1 -0
- package/fesm2022/h-k-dev-angular-email-editor.mjs +12 -71
- package/fesm2022/h-k-dev-angular-email-editor.mjs.map +1 -1
- package/package.json +5 -1
- package/types/h-k-dev-angular-email-editor-fonts.d.ts +110 -0
- package/types/h-k-dev-angular-email-editor.d.ts +9 -38
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fonts an email is written in — Gmail's set, in Gmail's order — as
|
|
3
|
+
* plain data: no Angular, no ProseMirror. The editor's `textStyle` mark
|
|
4
|
+
* stores and emits only these stacks, the toolbar offers them, and an
|
|
5
|
+
* editor of another kind (summernote's `fontNames`) can offer the same set
|
|
6
|
+
* from the same constant, so a message reads the same font whichever
|
|
7
|
+
* composer wrote it.
|
|
8
|
+
*/
|
|
9
|
+
/** The clients, in the order a picker shows them (G, A, O), with the letter
|
|
10
|
+
and name it shows them by. */
|
|
11
|
+
const fontClients = [
|
|
12
|
+
{ id: 'gmail', letter: 'G', name: 'Gmail' },
|
|
13
|
+
{ id: 'apple-mail', letter: 'A', name: 'Apple Mail' },
|
|
14
|
+
{ id: 'outlook-desktop', letter: 'O', name: 'Outlook' },
|
|
15
|
+
];
|
|
16
|
+
/** The family names of a `font-family` value, unquoted, in order. */
|
|
17
|
+
function familiesOf(stack) {
|
|
18
|
+
return stack
|
|
19
|
+
.split(',')
|
|
20
|
+
.map((family) => family.replace(/["']/g, '').trim())
|
|
21
|
+
.filter(Boolean);
|
|
22
|
+
}
|
|
23
|
+
const G = 'gmail';
|
|
24
|
+
const A = 'apple-mail';
|
|
25
|
+
const O = 'outlook-desktop';
|
|
26
|
+
function font(id, name, stack, clients) {
|
|
27
|
+
return { id, name, stack, face: familiesOf(stack)[0], clients };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Gmail's fonts, in its menu's order. A stack is the face, then what a
|
|
31
|
+
* client without it should draw instead, then the generic. Each face leads
|
|
32
|
+
* only its own stack, so a single family read back from another editor
|
|
33
|
+
* (`font-family: 'Arial Black'`) finds its font exactly.
|
|
34
|
+
*
|
|
35
|
+
* The first stack naming a family claims it (see {@link matchEmailFont}):
|
|
36
|
+
* `helvetica` and `sans-serif` read as Sans Serif, `times` and `serif` as
|
|
37
|
+
* Serif, `courier` and `monospace` as Fixed width.
|
|
38
|
+
*/
|
|
39
|
+
const emailFontFamilies = [
|
|
40
|
+
font('sans-serif', 'Sans Serif', 'Arial, Helvetica, sans-serif', [G, A, O]),
|
|
41
|
+
font('serif', 'Serif', '"Times New Roman", Times, serif', [G, A, O]),
|
|
42
|
+
font('fixed-width', 'Fixed width', '"Courier New", Courier, monospace', [G, A, O]),
|
|
43
|
+
// Not on iOS.
|
|
44
|
+
font('wide', 'Wide', '"Arial Black", sans-serif', [G, O]),
|
|
45
|
+
// Office's, not Windows' own — nor on iOS.
|
|
46
|
+
font('narrow', 'Narrow', '"Arial Narrow", sans-serif', [O]),
|
|
47
|
+
// Not on iOS.
|
|
48
|
+
font('comic-sans-ms', 'Comic Sans MS', '"Comic Sans MS", sans-serif', [G, O]),
|
|
49
|
+
// Office's only: neither macOS, iOS nor Windows itself has it.
|
|
50
|
+
font('garamond', 'Garamond', 'Garamond, "Times New Roman", serif', [O]),
|
|
51
|
+
font('georgia', 'Georgia', 'Georgia, serif', [G, A, O]),
|
|
52
|
+
// Not on iOS.
|
|
53
|
+
font('tahoma', 'Tahoma', 'Tahoma, sans-serif', [G, O]),
|
|
54
|
+
font('trebuchet-ms', 'Trebuchet MS', '"Trebuchet MS", sans-serif', [G, A, O]),
|
|
55
|
+
font('verdana', 'Verdana', 'Verdana, sans-serif', [G, A, O]),
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* The font a message is written in until the writer picks another — Gmail's
|
|
59
|
+
* Sans Serif, Arial. Nothing is emitted for it: unstyled text is drawn in
|
|
60
|
+
* the client's own default, as Gmail's is. An editor shows it as its
|
|
61
|
+
* surface font, so what is written looks as it will in Gmail; summernote
|
|
62
|
+
* takes its face (`fontName: emailDefaultFont.face`).
|
|
63
|
+
*/
|
|
64
|
+
const emailDefaultFont = emailFontFamilies[0];
|
|
65
|
+
/**
|
|
66
|
+
* The faces of {@link emailFontFamilies}, in order — the set for an editor
|
|
67
|
+
* that offers single families. For summernote, both lists: `fontNamesIgnoreCheck`
|
|
68
|
+
* keeps a face this machine lacks (Garamond, Arial Narrow) in the menu.
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* $('#editor').summernote({ fontNames: emailFontFaces, fontNamesIgnoreCheck: emailFontFaces });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
const emailFontFaces = emailFontFamilies.map((f) => f.face);
|
|
75
|
+
/** Every size the editor stores, in px. */
|
|
76
|
+
const allowedFontSizes = [10, 12, 14, 16, 18, 24, 32];
|
|
77
|
+
/**
|
|
78
|
+
* The sizes a picker offers: the phone-safe subset (≥14px). Below ~13px iOS
|
|
79
|
+
* auto-inflates text and reflows the layout, so they are not *offered* —
|
|
80
|
+
* but every {@link allowedFontSizes} size is still read and kept, because a
|
|
81
|
+
* hand-typed size in the HTML source is the author's own responsibility.
|
|
82
|
+
*/
|
|
83
|
+
const emailFontSizes = [14, 16, 18, 24, 32];
|
|
84
|
+
/** A `font-family` value as a comparison key: lower-cased, quotes dropped,
|
|
85
|
+
whitespace around commas collapsed — so a hand-typed or CSSOM-reserialized
|
|
86
|
+
stack matches a curated one regardless of cosmetic differences. */
|
|
87
|
+
function fontFamilyKey(raw) {
|
|
88
|
+
return raw
|
|
89
|
+
.toLowerCase()
|
|
90
|
+
.replace(/["']/g, '')
|
|
91
|
+
.replace(/\s*,\s*/g, ',')
|
|
92
|
+
.trim();
|
|
93
|
+
}
|
|
94
|
+
const FONT_BY_KEY = new Map(emailFontFamilies.map((f) => [fontFamilyKey(f.stack), f]));
|
|
95
|
+
/** Each family a stack names to the first stack naming it. */
|
|
96
|
+
const FONT_BY_FAMILY = new Map();
|
|
97
|
+
for (const f of emailFontFamilies) {
|
|
98
|
+
for (const family of fontFamilyKey(f.stack).split(',')) {
|
|
99
|
+
if (!FONT_BY_FAMILY.has(family))
|
|
100
|
+
FONT_BY_FAMILY.set(family, f);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Whether `value` is exactly one of the stacks, give or take quotes, case
|
|
104
|
+
and spacing. */
|
|
105
|
+
function isEmailFontStack(value) {
|
|
106
|
+
return typeof value === 'string' && FONT_BY_KEY.has(fontFamilyKey(value));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The font a `font-family` value is. One of the stacks is itself; a stack of
|
|
110
|
+
* someone else's is the font it falls back to — the first of its families a
|
|
111
|
+
* stack names, its generic last: a builder's web font (`Ubuntu, Helvetica,
|
|
112
|
+
* Arial, sans-serif`) is Sans Serif, the face a client without that font
|
|
113
|
+
* draws anyway, and summernote's `'Comic Sans MS'` is Comic Sans MS.
|
|
114
|
+
* Nothing to fall back to, no font.
|
|
115
|
+
*/
|
|
116
|
+
function matchEmailFont(raw) {
|
|
117
|
+
if (!raw)
|
|
118
|
+
return null;
|
|
119
|
+
const key = fontFamilyKey(raw);
|
|
120
|
+
const exact = FONT_BY_KEY.get(key);
|
|
121
|
+
if (exact)
|
|
122
|
+
return exact;
|
|
123
|
+
for (const family of key.split(',')) {
|
|
124
|
+
const found = FONT_BY_FAMILY.get(family.trim());
|
|
125
|
+
if (found)
|
|
126
|
+
return found;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* Public API Surface of @h-k-dev/angular-email-editor/fonts
|
|
133
|
+
*
|
|
134
|
+
* Its own entry because it is plain data: a host whose other composer is
|
|
135
|
+
* not this editor (summernote) shares the font set without pulling in
|
|
136
|
+
* ProseMirror or Angular.
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generated bundle index. Do not edit.
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
export { allowedFontSizes, emailDefaultFont, emailFontFaces, emailFontFamilies, emailFontSizes, fontClients, fontFamilyKey, isEmailFontStack, matchEmailFont };
|
|
144
|
+
//# sourceMappingURL=h-k-dev-angular-email-editor-fonts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h-k-dev-angular-email-editor-fonts.mjs","sources":["../../../projects/angular-email-editor/fonts/fonts.ts","../../../projects/angular-email-editor/fonts/public-api.ts","../../../projects/angular-email-editor/fonts/h-k-dev-angular-email-editor-fonts.ts"],"sourcesContent":["/**\n * The fonts an email is written in — Gmail's set, in Gmail's order — as\n * plain data: no Angular, no ProseMirror. The editor's `textStyle` mark\n * stores and emits only these stacks, the toolbar offers them, and an\n * editor of another kind (summernote's `fontNames`) can offer the same set\n * from the same constant, so a message reads the same font whichever\n * composer wrote it.\n */\n\n/**\n * A client a font is checked against — the three a writer's recipients\n * mostly read in, the same ids as the library's `RenderingClient`.\n */\nexport type FontClient = 'gmail' | 'apple-mail' | 'outlook-desktop';\n\n/** The clients, in the order a picker shows them (G, A, O), with the letter\n and name it shows them by. */\nexport const fontClients: readonly { id: FontClient; letter: string; name: string }[] = [\n { id: 'gmail', letter: 'G', name: 'Gmail' },\n { id: 'apple-mail', letter: 'A', name: 'Apple Mail' },\n { id: 'outlook-desktop', letter: 'O', name: 'Outlook' },\n];\n\n/** A font the editor offers: one of {@link emailFontFamilies}. */\nexport interface EmailFont {\n /** Stable, for a host's translation key: `sans-serif`, `fixed-width`, … */\n readonly id: string;\n /** The label, in English — Gmail's words. */\n readonly name: string;\n /**\n * The exact `font-family` value emitted — the canonical stored form. A\n * family name of more than one word is double-quoted: the CSSOM (Chrome's\n * and jsdom's alike) quotes it on reserialization, so the stored bytes are\n * already what comes back and a round trip is stable.\n */\n readonly stack: string;\n /** The face the stack is for — its first family, unquoted: what an editor\n that sets a single family (summernote) offers for it. */\n readonly face: string;\n /**\n * The clients that draw the face itself rather than its fallback — the\n * face is on the machine the client runs on:\n *\n * - `gmail`: Gmail on the web, read on Windows or macOS (it draws with\n * the reader's own fonts; one of them lacking the face is a no).\n * - `apple-mail`: Apple Mail on macOS *and* iOS — a phone lacks Arial\n * Black, Comic Sans, Tahoma.\n * - `outlook-desktop`: Outlook on Windows, which brings Office's fonts\n * (Arial Narrow, Garamond).\n *\n * Elsewhere the stack's fallback draws: the email still reads, in a near\n * face. Curated, conservative — a face only some readers have is a no.\n */\n readonly clients: readonly FontClient[];\n}\n\n/** The family names of a `font-family` value, unquoted, in order. */\nfunction familiesOf(stack: string): string[] {\n return stack\n .split(',')\n .map((family) => family.replace(/[\"']/g, '').trim())\n .filter(Boolean);\n}\n\nconst G: FontClient = 'gmail';\nconst A: FontClient = 'apple-mail';\nconst O: FontClient = 'outlook-desktop';\n\nfunction font(id: string, name: string, stack: string, clients: FontClient[]): EmailFont {\n return { id, name, stack, face: familiesOf(stack)[0], clients };\n}\n\n/**\n * Gmail's fonts, in its menu's order. A stack is the face, then what a\n * client without it should draw instead, then the generic. Each face leads\n * only its own stack, so a single family read back from another editor\n * (`font-family: 'Arial Black'`) finds its font exactly.\n *\n * The first stack naming a family claims it (see {@link matchEmailFont}):\n * `helvetica` and `sans-serif` read as Sans Serif, `times` and `serif` as\n * Serif, `courier` and `monospace` as Fixed width.\n */\nexport const emailFontFamilies: readonly EmailFont[] = [\n font('sans-serif', 'Sans Serif', 'Arial, Helvetica, sans-serif', [G, A, O]),\n font('serif', 'Serif', '\"Times New Roman\", Times, serif', [G, A, O]),\n font('fixed-width', 'Fixed width', '\"Courier New\", Courier, monospace', [G, A, O]),\n // Not on iOS.\n font('wide', 'Wide', '\"Arial Black\", sans-serif', [G, O]),\n // Office's, not Windows' own — nor on iOS.\n font('narrow', 'Narrow', '\"Arial Narrow\", sans-serif', [O]),\n // Not on iOS.\n font('comic-sans-ms', 'Comic Sans MS', '\"Comic Sans MS\", sans-serif', [G, O]),\n // Office's only: neither macOS, iOS nor Windows itself has it.\n font('garamond', 'Garamond', 'Garamond, \"Times New Roman\", serif', [O]),\n font('georgia', 'Georgia', 'Georgia, serif', [G, A, O]),\n // Not on iOS.\n font('tahoma', 'Tahoma', 'Tahoma, sans-serif', [G, O]),\n font('trebuchet-ms', 'Trebuchet MS', '\"Trebuchet MS\", sans-serif', [G, A, O]),\n font('verdana', 'Verdana', 'Verdana, sans-serif', [G, A, O]),\n];\n\n/**\n * The font a message is written in until the writer picks another — Gmail's\n * Sans Serif, Arial. Nothing is emitted for it: unstyled text is drawn in\n * the client's own default, as Gmail's is. An editor shows it as its\n * surface font, so what is written looks as it will in Gmail; summernote\n * takes its face (`fontName: emailDefaultFont.face`).\n */\nexport const emailDefaultFont: EmailFont = emailFontFamilies[0];\n\n/**\n * The faces of {@link emailFontFamilies}, in order — the set for an editor\n * that offers single families. For summernote, both lists: `fontNamesIgnoreCheck`\n * keeps a face this machine lacks (Garamond, Arial Narrow) in the menu.\n *\n * ```ts\n * $('#editor').summernote({ fontNames: emailFontFaces, fontNamesIgnoreCheck: emailFontFaces });\n * ```\n */\nexport const emailFontFaces: readonly string[] = emailFontFamilies.map((f) => f.face);\n\n/** Every size the editor stores, in px. */\nexport const allowedFontSizes = [10, 12, 14, 16, 18, 24, 32] as const;\nexport type FontSize = (typeof allowedFontSizes)[number];\n\n/**\n * The sizes a picker offers: the phone-safe subset (≥14px). Below ~13px iOS\n * auto-inflates text and reflows the layout, so they are not *offered* —\n * but every {@link allowedFontSizes} size is still read and kept, because a\n * hand-typed size in the HTML source is the author's own responsibility.\n */\nexport const emailFontSizes: readonly FontSize[] = [14, 16, 18, 24, 32];\n\n/** A `font-family` value as a comparison key: lower-cased, quotes dropped,\n whitespace around commas collapsed — so a hand-typed or CSSOM-reserialized\n stack matches a curated one regardless of cosmetic differences. */\nexport function fontFamilyKey(raw: string): string {\n return raw\n .toLowerCase()\n .replace(/[\"']/g, '')\n .replace(/\\s*,\\s*/g, ',')\n .trim();\n}\n\nconst FONT_BY_KEY = new Map(emailFontFamilies.map((f) => [fontFamilyKey(f.stack), f]));\n\n/** Each family a stack names to the first stack naming it. */\nconst FONT_BY_FAMILY = new Map<string, EmailFont>();\nfor (const f of emailFontFamilies) {\n for (const family of fontFamilyKey(f.stack).split(',')) {\n if (!FONT_BY_FAMILY.has(family)) FONT_BY_FAMILY.set(family, f);\n }\n}\n\n/** Whether `value` is exactly one of the stacks, give or take quotes, case\n and spacing. */\nexport function isEmailFontStack(value: unknown): value is string {\n return typeof value === 'string' && FONT_BY_KEY.has(fontFamilyKey(value));\n}\n\n/**\n * The font a `font-family` value is. One of the stacks is itself; a stack of\n * someone else's is the font it falls back to — the first of its families a\n * stack names, its generic last: a builder's web font (`Ubuntu, Helvetica,\n * Arial, sans-serif`) is Sans Serif, the face a client without that font\n * draws anyway, and summernote's `'Comic Sans MS'` is Comic Sans MS.\n * Nothing to fall back to, no font.\n */\nexport function matchEmailFont(raw: string | null | undefined): EmailFont | null {\n if (!raw) return null;\n const key = fontFamilyKey(raw);\n const exact = FONT_BY_KEY.get(key);\n if (exact) return exact;\n for (const family of key.split(',')) {\n const found = FONT_BY_FAMILY.get(family.trim());\n if (found) return found;\n }\n return null;\n}\n","/*\n * Public API Surface of @h-k-dev/angular-email-editor/fonts\n *\n * Its own entry because it is plain data: a host whose other composer is\n * not this editor (summernote) shares the font set without pulling in\n * ProseMirror or Angular.\n */\n\nexport * from './fonts';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ import { NodeSelection, TextSelection, Plugin, PluginKey, EditorState, Selection
|
|
|
3
3
|
import { keymap } from 'prosemirror-keymap';
|
|
4
4
|
import { splitBlockAs, chainCommands, newlineInCode, createParagraphNear, liftEmptyBlock, setBlockType, exitCode, lift, wrapIn, toggleMark as toggleMark$1, baseKeymap } from 'prosemirror-commands';
|
|
5
5
|
import { tableEditing, fixTables, splitCell, mergeCells, deleteTable, deleteColumn, deleteRow, addRowAfter, addRowBefore, setCellAttr, removeColumn as removeColumn$1, removeRow, addColumn as addColumn$1, addRow, addColumnAfter, addColumnBefore, TableMap, isInTable, selectedRect, CellSelection, nextCell, goToNextCell } from 'prosemirror-tables';
|
|
6
|
+
import { allowedFontSizes, isEmailFontStack, matchEmailFont } from '@h-k-dev/angular-email-editor/fonts';
|
|
7
|
+
export * from '@h-k-dev/angular-email-editor/fonts';
|
|
6
8
|
import { AddMarkStep, RemoveMarkStep, Transform } from 'prosemirror-transform';
|
|
7
9
|
import { closeHistory, redo, undo, history } from 'prosemirror-history';
|
|
8
10
|
import { InputRule, inputRules, wrappingInputRule, textblockTypeInputRule } from 'prosemirror-inputrules';
|
|
@@ -1686,9 +1688,8 @@ function isUppercase(state, schema) {
|
|
|
1686
1688
|
});
|
|
1687
1689
|
return found;
|
|
1688
1690
|
}
|
|
1689
|
-
const ALLOWED_SIZES = [10, 12, 14, 16, 18, 24, 32];
|
|
1690
1691
|
function isSafeFontSize(value) {
|
|
1691
|
-
return
|
|
1692
|
+
return allowedFontSizes.includes(value);
|
|
1692
1693
|
}
|
|
1693
1694
|
/**
|
|
1694
1695
|
* Parse a font-size value from the DOM into one of our allowed sizes.
|
|
@@ -1705,8 +1706,8 @@ function parseFontSize(raw) {
|
|
|
1705
1706
|
// A size off the ladder — a builder's 11px, 13px, 15px — snaps to the
|
|
1706
1707
|
// nearest rung, upward on a tie: read as the size it is nearest to, not
|
|
1707
1708
|
// dropped to the default. (The schema still emits only its own rungs.)
|
|
1708
|
-
let nearest =
|
|
1709
|
-
for (const size of
|
|
1709
|
+
let nearest = allowedFontSizes[0];
|
|
1710
|
+
for (const size of allowedFontSizes) {
|
|
1710
1711
|
const better = Math.abs(size - n) < Math.abs(nearest - n) ||
|
|
1711
1712
|
(Math.abs(size - n) === Math.abs(nearest - n) && size > nearest);
|
|
1712
1713
|
if (better)
|
|
@@ -1714,79 +1715,19 @@ function parseFontSize(raw) {
|
|
|
1714
1715
|
}
|
|
1715
1716
|
return nearest;
|
|
1716
1717
|
}
|
|
1717
|
-
/**
|
|
1718
|
-
* The sizes the toolbar picker offers: the phone-safe subset (≥14px). Below
|
|
1719
|
-
* ~13px iOS auto-inflates text and reflows the layout (responsiveness ledger,
|
|
1720
|
-
* principle 8), so we don't *offer* those — but {@link parseFontSize} still
|
|
1721
|
-
* accepts the full {@link ALLOWED_SIZES} range, because a hand-typed size in
|
|
1722
|
-
* the HTML source pane is the author's own responsibility, exactly like a
|
|
1723
|
-
* hand-typed hex colour.
|
|
1724
|
-
*/
|
|
1725
|
-
const emailFontSizes = [14, 16, 18, 24, 32];
|
|
1726
|
-
/**
|
|
1727
|
-
* The curated font stacks — the picker offers only these, no free-form fonts
|
|
1728
|
-
* (principle 7: if a mainstream client can't render it, we don't emit it).
|
|
1729
|
-
*
|
|
1730
|
-
* Deliberately built from *single-word* family identifiers plus a generic
|
|
1731
|
-
* fallback: a stack like `Courier New` would round-trip through the CSSOM
|
|
1732
|
-
* (serialization builds real elements and re-reads them) and come back quoted
|
|
1733
|
-
* as `"Courier New"` in Chrome but unquoted in jsdom — a byte-instability that
|
|
1734
|
-
* breaks canonical determinism and makes tests disagree with the runtime (the
|
|
1735
|
-
* same trap the longhand/`rgb()` rule guards against). Bare identifiers and
|
|
1736
|
-
* generic keywords serialize identically everywhere.
|
|
1737
|
-
*/
|
|
1738
|
-
const emailFontFamilies = [
|
|
1739
|
-
{ name: 'Sans-serif', stack: 'Arial, Helvetica, sans-serif' },
|
|
1740
|
-
{ name: 'Serif', stack: 'Georgia, Times, serif' },
|
|
1741
|
-
{ name: 'Monospace', stack: 'Courier, monospace' },
|
|
1742
|
-
{ name: 'System', stack: 'system-ui, sans-serif' },
|
|
1743
|
-
];
|
|
1744
|
-
/** Normalise a `font-family` value to a comparison key: lower-cased, quotes
|
|
1745
|
-
dropped, whitespace around commas collapsed. Lets a hand-typed or CSSOM-
|
|
1746
|
-
reserialized stack match a curated one regardless of cosmetic differences. */
|
|
1747
|
-
function fontFamilyKey(raw) {
|
|
1748
|
-
return raw
|
|
1749
|
-
.toLowerCase()
|
|
1750
|
-
.replace(/["']/g, '')
|
|
1751
|
-
.replace(/\s*,\s*/g, ',')
|
|
1752
|
-
.trim();
|
|
1753
|
-
}
|
|
1754
|
-
const FONT_STACK_BY_KEY = new Map(emailFontFamilies.map((f) => [fontFamilyKey(f.stack), f.stack]));
|
|
1755
|
-
/** Each family a curated stack names — a face or its generic — to the first
|
|
1756
|
-
stack naming it: `helvetica` and `sans-serif` to Sans-serif, `times` and
|
|
1757
|
-
`serif` to Serif. */
|
|
1758
|
-
const FONT_STACK_BY_FAMILY = new Map();
|
|
1759
|
-
for (const { stack } of emailFontFamilies) {
|
|
1760
|
-
for (const family of fontFamilyKey(stack).split(',')) {
|
|
1761
|
-
if (!FONT_STACK_BY_FAMILY.has(family))
|
|
1762
|
-
FONT_STACK_BY_FAMILY.set(family, stack);
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1718
|
+
/** Whether `value` is one of the curated stacks (`@h-k-dev/angular-email-editor/fonts`). */
|
|
1765
1719
|
function isSafeFontFamily(value) {
|
|
1766
|
-
return
|
|
1720
|
+
return isEmailFontStack(value);
|
|
1767
1721
|
}
|
|
1768
1722
|
/**
|
|
1769
|
-
* Parse a font-family value from the DOM into one of
|
|
1723
|
+
* Parse a font-family value from the DOM into one of the curated stacks,
|
|
1770
1724
|
* returning the *canonical* stack string (so a cosmetically different but
|
|
1771
1725
|
* equivalent input normalises to the exact bytes we emit). A stack of
|
|
1772
|
-
* someone else's reads as the curated one it falls back to
|
|
1773
|
-
*
|
|
1774
|
-
* font (`Ubuntu, Helvetica, Arial, sans-serif`) is Sans-serif, the face a
|
|
1775
|
-
* client without that font draws anyway. Nothing to fall back to, no font.
|
|
1726
|
+
* someone else's reads as the curated one it falls back to
|
|
1727
|
+
* ({@link matchEmailFont}). Nothing to fall back to, no font.
|
|
1776
1728
|
*/
|
|
1777
1729
|
function parseFontFamily(raw) {
|
|
1778
|
-
|
|
1779
|
-
return null;
|
|
1780
|
-
const key = fontFamilyKey(raw);
|
|
1781
|
-
const exact = FONT_STACK_BY_KEY.get(key);
|
|
1782
|
-
if (exact)
|
|
1783
|
-
return exact;
|
|
1784
|
-
for (const family of key.split(',')) {
|
|
1785
|
-
const stack = FONT_STACK_BY_FAMILY.get(family.trim());
|
|
1786
|
-
if (stack)
|
|
1787
|
-
return stack;
|
|
1788
|
-
}
|
|
1789
|
-
return null;
|
|
1730
|
+
return matchEmailFont(raw)?.stack ?? null;
|
|
1790
1731
|
}
|
|
1791
1732
|
/**
|
|
1792
1733
|
* TipTap-style `textStyle` mark: a `<span style="…">` container holding inline
|
|
@@ -18840,5 +18781,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
18840
18781
|
* Generated bundle index. Do not edit.
|
|
18841
18782
|
*/
|
|
18842
18783
|
|
|
18843
|
-
export { AUTHORED_ATTR, AUTHORED_SPEC_KEY, AUTHORED_STYLE_DEFAULTS_KEY, AuthoredEnter, BLOCKQUOTE_STYLE, BLOCK_MENU_FOCUS_KEY, BUTTON_FILL, BUTTON_STYLE, BaseKeymap, Blockquote, Bold, BulletList, Button, CARET_ROOM, CLIENT_LABELS, COMMENT_TAG, CONTENT_ATTRS, CSS_SUPPORT, ClearFormatting, CodeLine, CodeLineIndent, Column, ColumnResize, Columns, ColumnsResize, DOCUMENT_CLASS, DOCUMENT_TAGS, DUAL_BACKGROUND_MIN_RATIO, DUAL_CONTRAST_DARK, DUAL_CONTRAST_LIGHT, DUAL_CONTRAST_MIN_RATIO, Divider, Document$1 as Document, DocumentStyles, EMAIL_ATTRIBUTES, EMAIL_CSS_PROPERTIES, EMAIL_SAFE_STYLE_PROPERTIES, EMAIL_SAFE_TAGS, EMAIL_TAGS, EMAIL_TAG_ATTRIBUTES, EmailParagraph, FILE_CHOOSER_POINTER_WAIT, FILL_TEXT_COLOR, FILL_TEXT_COLOR_LIGHT, FILL_TEXT_COLOR_RGB, FOLD_WIDTH, FORMAT_WIDTH, GMAIL_CLIP_BYTES, GUIDES_ACTIVE_CLASS, GUIDES_PEEK_CLASS, GUIDES_PEEK_DELAY_MS, Gapcursor, HEADING_SIZES, HardBreak, Heading, History, HtmlComment, HtmlCommentInline, HtmlElement, HtmlInline, HtmlLanguage, HtmlRaw, HtmlTextElement, HtmlVoid, HtmlVoidBlock, IMPORT_VIEWPORT_WIDTH, INDENT_MAX, INDENT_STEP, INLINE_TAGS, INLINE_VOID_TAGS, IN_SELECTION_CLASS, Image, InlineAtomSelection, InlineImageStore, InlineImages, Italic, LIST_STYLE, LayoutGuides, Link, ListItem, MAX_COLUMNS, MAX_IMAGE_WIDTH, MAX_MERGE_TAG_LENGTH, MAX_UNBROKEN_RUN, MIN_COLUMN_CAP, MIN_COLUMN_PCT, MIN_FONT_SIZE, MIN_IMAGE_WIDTH, MIN_TABLE_PCT, MJML_WORLDLY_HTML, MergeTag, NoTextDrag, OrderedList, PARSE_SCOPE_KEY, PLACEHOLDER_WIDTH, Paragraph, PasteHygiene, QuotedHistory, REFUSED_TAGS, RENDERING_CLIENTS, SECTION_CONTENT_MAX, SECTION_INSET, SECTION_PADDING, STACK_CLASS, STACK_RULE, Section, SplitKeepingMarks, TABLE_BORDER_COLOR, TABLE_CELL_PADDING, TABLE_SECTION_TAGS, TYPING_REST, Table, TableCell, TableRow, Text, TextStyle, UNSET_BUTTON_HREF, Underline, VOID_TAGS, acceptProposal, addColumn, addColumnAtEnd, addRowAtEnd, analyzeAngularExpression, angularDialect, angularRequiredFields, applyAuthoredAttributes, applyEmailVocabulary, attributeString, authoredAttribute, authoredMarkupOf, authoredViewAttributes, bareButtons, buttonStyle, canonicalHead, canonicalMergeTagInner, canonicalRender, caretCellPos, caretInsideMergeTag, claimedImageFiles, clearCells, clientList, columnCaps, columnWidths, completionContextAt, containerStyle, contrastRatio, correctStoredMistakes, createAngularExpressions, createBlockMenu, createBubbleMenu, createButtonEdit, createContentProposal, createContentStream, createDOMParser, createEditor, createHtmlAutocomplete, createHtmlLanguage, createImageDrag, createInlineImages, createMergeTagResolution, createOffsetMapper, createQuotedHistory, createSchema, createSendIntent, createSourceMarks, createSuggestionMenu, createTableHandles, declarationProperty, declaredBackground, decodeDataUrl, defaultBubbleShouldShow, defineExtension, defineMark, defineNode, deleteColumns, deleteFullySelected, discardProposal, distributePadding, docText, dropDeadOfficeClasses, dropHidden, dropMediaQueries, dropZeroSizedWhitespace, duplicateColumn, duplicateRow, editQuotedHistory, effectiveWidths, emailBackgroundPalette, emailDocument, emailExtensions,
|
|
18784
|
+
export { AUTHORED_ATTR, AUTHORED_SPEC_KEY, AUTHORED_STYLE_DEFAULTS_KEY, AuthoredEnter, BLOCKQUOTE_STYLE, BLOCK_MENU_FOCUS_KEY, BUTTON_FILL, BUTTON_STYLE, BaseKeymap, Blockquote, Bold, BulletList, Button, CARET_ROOM, CLIENT_LABELS, COMMENT_TAG, CONTENT_ATTRS, CSS_SUPPORT, ClearFormatting, CodeLine, CodeLineIndent, Column, ColumnResize, Columns, ColumnsResize, DOCUMENT_CLASS, DOCUMENT_TAGS, DUAL_BACKGROUND_MIN_RATIO, DUAL_CONTRAST_DARK, DUAL_CONTRAST_LIGHT, DUAL_CONTRAST_MIN_RATIO, Divider, Document$1 as Document, DocumentStyles, EMAIL_ATTRIBUTES, EMAIL_CSS_PROPERTIES, EMAIL_SAFE_STYLE_PROPERTIES, EMAIL_SAFE_TAGS, EMAIL_TAGS, EMAIL_TAG_ATTRIBUTES, EmailParagraph, FILE_CHOOSER_POINTER_WAIT, FILL_TEXT_COLOR, FILL_TEXT_COLOR_LIGHT, FILL_TEXT_COLOR_RGB, FOLD_WIDTH, FORMAT_WIDTH, GMAIL_CLIP_BYTES, GUIDES_ACTIVE_CLASS, GUIDES_PEEK_CLASS, GUIDES_PEEK_DELAY_MS, Gapcursor, HEADING_SIZES, HardBreak, Heading, History, HtmlComment, HtmlCommentInline, HtmlElement, HtmlInline, HtmlLanguage, HtmlRaw, HtmlTextElement, HtmlVoid, HtmlVoidBlock, IMPORT_VIEWPORT_WIDTH, INDENT_MAX, INDENT_STEP, INLINE_TAGS, INLINE_VOID_TAGS, IN_SELECTION_CLASS, Image, InlineAtomSelection, InlineImageStore, InlineImages, Italic, LIST_STYLE, LayoutGuides, Link, ListItem, MAX_COLUMNS, MAX_IMAGE_WIDTH, MAX_MERGE_TAG_LENGTH, MAX_UNBROKEN_RUN, MIN_COLUMN_CAP, MIN_COLUMN_PCT, MIN_FONT_SIZE, MIN_IMAGE_WIDTH, MIN_TABLE_PCT, MJML_WORLDLY_HTML, MergeTag, NoTextDrag, OrderedList, PARSE_SCOPE_KEY, PLACEHOLDER_WIDTH, Paragraph, PasteHygiene, QuotedHistory, REFUSED_TAGS, RENDERING_CLIENTS, SECTION_CONTENT_MAX, SECTION_INSET, SECTION_PADDING, STACK_CLASS, STACK_RULE, Section, SplitKeepingMarks, TABLE_BORDER_COLOR, TABLE_CELL_PADDING, TABLE_SECTION_TAGS, TYPING_REST, Table, TableCell, TableRow, Text, TextStyle, UNSET_BUTTON_HREF, Underline, VOID_TAGS, acceptProposal, addColumn, addColumnAtEnd, addRowAtEnd, analyzeAngularExpression, angularDialect, angularRequiredFields, applyAuthoredAttributes, applyEmailVocabulary, attributeString, authoredAttribute, authoredMarkupOf, authoredViewAttributes, bareButtons, buttonStyle, canonicalHead, canonicalMergeTagInner, canonicalRender, caretCellPos, caretInsideMergeTag, claimedImageFiles, clearCells, clientList, columnCaps, columnWidths, completionContextAt, containerStyle, contrastRatio, correctStoredMistakes, createAngularExpressions, createBlockMenu, createBubbleMenu, createButtonEdit, createContentProposal, createContentStream, createDOMParser, createEditor, createHtmlAutocomplete, createHtmlLanguage, createImageDrag, createInlineImages, createMergeTagResolution, createOffsetMapper, createQuotedHistory, createSchema, createSendIntent, createSourceMarks, createSuggestionMenu, createTableHandles, declarationProperty, declaredBackground, decodeDataUrl, defaultBubbleShouldShow, defineExtension, defineMark, defineNode, deleteColumns, deleteFullySelected, discardProposal, distributePadding, docText, dropDeadOfficeClasses, dropHidden, dropMediaQueries, dropZeroSizedWhitespace, duplicateColumn, duplicateRow, editQuotedHistory, effectiveWidths, emailBackgroundPalette, emailDocument, emailExtensions, emailPlainText, emailSizeBudget, emailTextPalette, embedsAsLinks, entitySpans, escapeAttribute$2 as escapeAttribute, escapeText$1 as escapeText, expressionDialect, expressionDialectKey, extensionActions, extensionSuggestions, fillTextColor, filledPlaceholderAttrs, filterEmailStyle, findColumnContext, findColumnsContext, findCssIssues, findListDepth, findSectionContext, findTableContext, fitImageWidth, foldHTML, formatHTML, formatPct, forwardDocument, forwardQuote, hasBlockChildren$1 as hasBlockChildren, hasDocumentEnvelope, headingStyle, holdsBlock, holdsColumn, hrefProblem, htmlSourceExtensions, imageDropTarget, importLoss, importedDocument, inheritTextStyles, inheritedMarkup, inlineBootstrapTableBorders, inlineImageCids, inlineImageRegistry, inlineImagesKey, inlineStyles, insertHTML, insertMergeTag, isActionEnabled, isBlock$1 as isBlock, isBlockLevel, isBlockish, isBooleanAttribute, isEmailAttribute, isEmailCssProperty, isEmailTag, isFillTextColor, isInlineAtom, isMarkActive, isMergeTagExpression, isMergeTagPath, isNodeActive, isPadded, isProposing, isQuotedHistoryFolded, isSafeColor, isSafeFontFamily, isSafeFontSize, isSafeUrl$1 as isSafeUrl, isSectionImageUrl, isStreaming, italicStarInputRegex, italicStarPasteRegex, italicUnderscoreInputRegex, italicUnderscorePasteRegex, keepGmailRules, layoutBlockAt, linkClickPlugin, linkPastePlugin, linkRangeAt, lintHTML, lowerInlinesAroundBlocks, markAttrsOf, markStackingColumns, marksAcrossBreak, materializeComments, measureMenuPlacement, mediaMatches, mergeTagAt, mergeTagExpressions, mergeTagFields, mergeTagRanges, mergeTagResolutionPending, moveColumn, moveRow, normalizeCommentText, normalizeCss, normalizeHref, normalizeMergeTagText, noteOwnWidths, openFileChooser, openTag$1 as openTag, openTags, outlookColumns, paddingOf, parseAngularExpression, parseDocument, parseFontFamily, parseFontSize, parseHTML, parsePadding, parseRules, parseTextTransform, passesDualBackground, passesDualContrast, preserveExtensions, preservedAttributes, promoteInlineImages, promoteMergeTags, proposalRange, proposeContent, quotedHistory, readEnvelope, readImageFile, realizeComments, relativeLuminance, removeColumn, removeQuotedHistory, renderForClient, renderedPath, repairLinkSchemes, repairTables, replaceHTML, replyDocument, replyEnvelope, replyQuote, reviseProposal, rewriteInlineImageSources, richTextExtensions, ruleForScope, ruleScope, sanitizePastedHTML, scanHTML, scanMergeTags, scopeOf$1 as scopeOf, scopedDocumentCss, selectColumn, selectRow, selectedButton, selectedImage, selectedImageAlt, selectedMarkAtom, selectionInsideMergeTag, serializeDocument, serializeToHTML, setCellBackground, setColumnBackground, setColumnBoundary, setColumnsBoundary, setQuotedHistory, setSectionBackground, setSectionImage, setTableBox, setTableWidth, soleInlineAtom, splitDeclarations$2 as splitDeclarations, splitListItemKeepMarks, starInputRegex, starPasteRegex, stopProposal, streamContent, streamedRange, streamingRange, suggestionSectionTitles, tableStyle, textOffsetAt, textRegions, toEmailSafeColor, toInboundMessage, toggleList, trimInlineBoxes, underscoreInputRegex, underscorePasteRegex, unwrapLayoutTables, unwrapOfficeParagraphMarks, verbatimElement, videoPage, watchMenuScroll, withAttribute, withAuthoredMarkup, withQuoted, withQuotedHistory, withStyleDeclaration, withoutAttribute, withoutStyleDeclaration };
|
|
18844
18785
|
//# sourceMappingURL=h-k-dev-angular-email-editor.mjs.map
|