@zipify/wysiwyg 2.0.0-0 → 2.0.0-1
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/.eslintrc.js +1 -1
- package/config/build/lib.config.js +4 -2
- package/dist/cli.js +10 -2
- package/dist/wysiwyg.css +12 -6
- package/dist/wysiwyg.mjs +1464 -378
- package/example/ExampleApp.vue +3 -1
- package/lib/components/toolbar/controls/StylePresetControl.vue +1 -1
- package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +2 -2
- package/lib/enums/TextSettings.js +5 -5
- package/lib/extensions/Link.js +1 -1
- package/lib/extensions/StylePreset.js +1 -1
- package/lib/extensions/TextDecoration.js +12 -29
- package/lib/extensions/__tests__/FontWeight.test.js +2 -2
- package/lib/extensions/__tests__/TextDecoration.test.js +20 -24
- package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +1 -1
- package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +1 -1
- package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +19 -23
- package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +2 -2
- package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +1 -1
- package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +13 -17
- package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +102 -102
- package/lib/extensions/core/NodeProcessor.js +37 -15
- package/lib/extensions/core/TextProcessor.js +0 -5
- package/lib/extensions/core/__tests__/NodeProcessor.test.js +55 -0
- package/lib/extensions/core/__tests__/TextProcessor.test.js +0 -21
- package/lib/extensions/core/__tests__/__snapshots__/NodeProcessor.test.js.snap +60 -0
- package/lib/extensions/core/__tests__/__snapshots__/TextProcessor.test.js.snap +7 -27
- package/lib/extensions/core/steps/AttrStep.js +54 -0
- package/lib/extensions/core/steps/index.js +1 -0
- package/lib/services/NodeFactory.js +12 -18
- package/lib/services/index.js +1 -1
- package/lib/services/normalizer/BaseNormalizer.js +11 -0
- package/lib/services/{BrowserDomParser.js → normalizer/BrowserDomParser.js} +0 -0
- package/lib/services/normalizer/ContentNormalizer.js +24 -0
- package/lib/services/normalizer/HtmlNormalizer.js +245 -0
- package/lib/services/normalizer/JsonNormalizer.js +81 -0
- package/lib/services/{__tests__/ContentNormalizer.test.js → normalizer/__tests__/HtmlNormalizer.test.js} +31 -7
- package/lib/services/normalizer/__tests__/JsonNormalizer.test.js +70 -0
- package/lib/services/normalizer/__tests__/__snapshots__/JsonNormalizer.test.js.snap +159 -0
- package/lib/services/normalizer/index.js +1 -0
- package/lib/styles/content.css +8 -0
- package/lib/utils/isMarkAppliedToParent.js +2 -7
- package/package.json +3 -1
- package/lib/services/ContentNormalizer.js +0 -194
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`normalize json content should bubble mark from single text node to paragraph 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"content": Array [
|
|
6
|
+
Object {
|
|
7
|
+
"content": Array [
|
|
8
|
+
Object {
|
|
9
|
+
"text": "lorem ipsum",
|
|
10
|
+
"type": "text",
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
"marks": Array [
|
|
14
|
+
Object {
|
|
15
|
+
"attrs": Object {
|
|
16
|
+
"value": "700",
|
|
17
|
+
},
|
|
18
|
+
"type": "font_weight",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
"type": "paragraph",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
"type": "doc",
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
exports[`normalize json content should bubble mark from text to list item 1`] = `
|
|
29
|
+
Object {
|
|
30
|
+
"content": Array [
|
|
31
|
+
Object {
|
|
32
|
+
"attrs": Object {
|
|
33
|
+
"bullet": Object {
|
|
34
|
+
"type": "disc",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
"content": Array [
|
|
38
|
+
Object {
|
|
39
|
+
"content": Array [
|
|
40
|
+
Object {
|
|
41
|
+
"content": Array [
|
|
42
|
+
Object {
|
|
43
|
+
"text": "lorem ipsum",
|
|
44
|
+
"type": "text",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
"type": "paragraph",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
"marks": Array [
|
|
51
|
+
Object {
|
|
52
|
+
"attrs": Object {
|
|
53
|
+
"value": "700",
|
|
54
|
+
},
|
|
55
|
+
"type": "font_weight",
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
"type": "listItem",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
"type": "list",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
"type": "doc",
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
exports[`normalize json content should bubble mark from two text nodes to paragraph 1`] = `
|
|
69
|
+
Object {
|
|
70
|
+
"content": Array [
|
|
71
|
+
Object {
|
|
72
|
+
"content": Array [
|
|
73
|
+
Object {
|
|
74
|
+
"text": "lorem",
|
|
75
|
+
"type": "text",
|
|
76
|
+
},
|
|
77
|
+
Object {
|
|
78
|
+
"marks": Array [
|
|
79
|
+
Object {
|
|
80
|
+
"attrs": Object {
|
|
81
|
+
"value": "#FF0000",
|
|
82
|
+
},
|
|
83
|
+
"type": "font_color",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
"text": " ipsum",
|
|
87
|
+
"type": "text",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
"marks": Array [
|
|
91
|
+
Object {
|
|
92
|
+
"attrs": Object {
|
|
93
|
+
"value": "700",
|
|
94
|
+
},
|
|
95
|
+
"type": "font_weight",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
"type": "paragraph",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
"type": "doc",
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
exports[`normalize json content should bubble two marks 1`] = `
|
|
106
|
+
Object {
|
|
107
|
+
"content": Array [
|
|
108
|
+
Object {
|
|
109
|
+
"content": Array [
|
|
110
|
+
Object {
|
|
111
|
+
"text": "hello world",
|
|
112
|
+
"type": "text",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
"marks": Array [
|
|
116
|
+
Object {
|
|
117
|
+
"attrs": Object {
|
|
118
|
+
"value": "Bungee",
|
|
119
|
+
},
|
|
120
|
+
"type": "font_family",
|
|
121
|
+
},
|
|
122
|
+
Object {
|
|
123
|
+
"attrs": Object {
|
|
124
|
+
"value": "800",
|
|
125
|
+
},
|
|
126
|
+
"type": "font_weight",
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
"type": "paragraph",
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
"type": "doc",
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
exports[`normalize json content should not bubble inline marks 1`] = `
|
|
137
|
+
Object {
|
|
138
|
+
"content": Array [
|
|
139
|
+
Object {
|
|
140
|
+
"content": Array [
|
|
141
|
+
Object {
|
|
142
|
+
"marks": Array [
|
|
143
|
+
Object {
|
|
144
|
+
"attrs": Object {
|
|
145
|
+
"underline": true,
|
|
146
|
+
},
|
|
147
|
+
"type": "text_decoration",
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
"text": "hello world",
|
|
151
|
+
"type": "text",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
"type": "paragraph",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
"type": "doc",
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ContentNormalizer } from './ContentNormalizer';
|
package/lib/styles/content.css
CHANGED
|
@@ -112,6 +112,14 @@ img.ProseMirror-separator {
|
|
|
112
112
|
animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
.ProseMirror span {
|
|
116
|
+
display: block;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ProseMirror :where(p, h1, h2, h3, h4) span {
|
|
120
|
+
display: initial;
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
@keyframes ProseMirror-cursor-blink {
|
|
116
124
|
|
|
117
125
|
to {
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export function isMarkAppliedToParent({ doc }, position, checkingMark, comparator = DEFAULT_COMPARATOR) {
|
|
1
|
+
export function isMarkAppliedToParent({ doc }, position, checkingMark) {
|
|
4
2
|
const steps = doc.resolve(position).path.reverse();
|
|
5
3
|
|
|
6
4
|
for (const step of steps) {
|
|
7
5
|
if (typeof step === 'number') continue;
|
|
8
|
-
|
|
9
|
-
for (const mark of step.marks) {
|
|
10
|
-
if (comparator(mark, checkingMark)) return true;
|
|
11
|
-
}
|
|
6
|
+
if (checkingMark.isInSet(step.marks)) return true;
|
|
12
7
|
}
|
|
13
8
|
|
|
14
9
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zipify/wysiwyg",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-1",
|
|
4
4
|
"description": "Zipify modification of TipTap text editor",
|
|
5
5
|
"main": "dist/wysiwyg.mjs",
|
|
6
6
|
"bin": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@tiptap/vue-2": "^2.0.0-beta.85",
|
|
46
46
|
"commander": "^9.4.0",
|
|
47
47
|
"jsdom": "^20.0.0",
|
|
48
|
+
"lodash": "^4.17.21",
|
|
48
49
|
"simplebar": "^5.3.8"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"@babel/plugin-transform-runtime": "^7.18.10",
|
|
58
59
|
"@babel/preset-env": "^7.19.0",
|
|
59
60
|
"@babel/runtime": "^7.19.0",
|
|
61
|
+
"@optimize-lodash/rollup-plugin": "^3.0.0",
|
|
60
62
|
"@rollup/plugin-babel": "^5.3.1",
|
|
61
63
|
"@rollup/plugin-commonjs": "^22.0.2",
|
|
62
64
|
"@rollup/plugin-node-resolve": "^14.0.0",
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { BrowserDomParser } from './BrowserDomParser';
|
|
2
|
-
|
|
3
|
-
export class ContentNormalizer {
|
|
4
|
-
static BLOCK_NODE_NAMES = ['P', 'H1', 'H2', 'H3', 'H4'];
|
|
5
|
-
|
|
6
|
-
static BLOCK_STYLES = [
|
|
7
|
-
'text-align',
|
|
8
|
-
'line-height',
|
|
9
|
-
'margin',
|
|
10
|
-
'margin-top',
|
|
11
|
-
'margin-bottom',
|
|
12
|
-
'margin-left',
|
|
13
|
-
'margin-right'
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
static build(content, options = {}) {
|
|
17
|
-
return new ContentNormalizer({
|
|
18
|
-
content,
|
|
19
|
-
parser: options.parser || new BrowserDomParser()
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
static normalize(content, options = {}) {
|
|
24
|
-
return ContentNormalizer.build(content, options).normalize();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
constructor({ content, parser }) {
|
|
28
|
-
this._content = content;
|
|
29
|
-
this._parser = parser;
|
|
30
|
-
this.dom = null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
normalize() {
|
|
34
|
-
if (typeof this._content !== 'string') {
|
|
35
|
-
return this._content;
|
|
36
|
-
}
|
|
37
|
-
this.normalizeHTML();
|
|
38
|
-
return this.normalizedHTML;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
normalizeHTML() {
|
|
42
|
-
this.dom = this._parser.parse(this._content.replace(/(\r)?\n/g, ''));
|
|
43
|
-
|
|
44
|
-
this._removeComments();
|
|
45
|
-
this.iterateNodes(this._normalizeBreakLines, (node) => node.tagName === 'BR');
|
|
46
|
-
this.iterateNodes(this._removeEmptyNodes, this._isBlockNode);
|
|
47
|
-
this.iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get normalizedHTML() {
|
|
51
|
-
return this.dom.body.innerHTML;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get _NodeFilter() {
|
|
55
|
-
return this._parser.types.NodeFilter;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
_removeComments() {
|
|
59
|
-
const iterator = this.createNodeIterator(this._NodeFilter.SHOW_COMMENT);
|
|
60
|
-
|
|
61
|
-
this.runIterator(iterator, (node) => node.remove());
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
createNodeIterator(whatToShow, filter) {
|
|
65
|
-
return this.dom.createNodeIterator(this.dom.body, whatToShow, filter);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
iterateNodes(handler, condition = () => true) {
|
|
69
|
-
const checkCondition = (node) => node.tagName !== 'BODY' && condition.call(this, node);
|
|
70
|
-
|
|
71
|
-
const iterator = this.createNodeIterator(this._NodeFilter.SHOW_ELEMENT, {
|
|
72
|
-
acceptNode: (node) => checkCondition(node) ? this._NodeFilter.FILTER_ACCEPT : this._NodeFilter.FILTER_REJECT
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
this.runIterator(iterator, handler);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
runIterator(iterator, handler) {
|
|
79
|
-
let currentNode = iterator.nextNode();
|
|
80
|
-
|
|
81
|
-
while (currentNode) {
|
|
82
|
-
handler.call(this, currentNode);
|
|
83
|
-
currentNode = iterator.nextNode();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
_removeEmptyNodes(node) {
|
|
88
|
-
if (!node.innerHTML.trim()) node.remove();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
_normalizeListItems(itemEl) {
|
|
92
|
-
const fragment = this.dom.createDocumentFragment();
|
|
93
|
-
const children = Array.from(itemEl.childNodes);
|
|
94
|
-
let capturingParagraph;
|
|
95
|
-
let previousNode;
|
|
96
|
-
|
|
97
|
-
const append = (node) => {
|
|
98
|
-
this._assignElementProperties(node, itemEl, ContentNormalizer.BLOCK_STYLES);
|
|
99
|
-
fragment.append(node);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
this._assignElementProperties(itemEl, itemEl.parentElement, ContentNormalizer.BLOCK_STYLES);
|
|
103
|
-
|
|
104
|
-
for (const node of children) {
|
|
105
|
-
if (this._isBlockNode(node)) {
|
|
106
|
-
append(node);
|
|
107
|
-
capturingParagraph = null;
|
|
108
|
-
previousNode = node;
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (node.tagName === 'BR' && previousNode && previousNode?.tagName !== 'BR') {
|
|
113
|
-
node.remove();
|
|
114
|
-
previousNode = node;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (node.tagName === 'BR') {
|
|
119
|
-
const emptyLineEl = this.dom.createElement('p');
|
|
120
|
-
|
|
121
|
-
emptyLineEl.append(node);
|
|
122
|
-
append(emptyLineEl);
|
|
123
|
-
capturingParagraph = null;
|
|
124
|
-
previousNode = node;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (!capturingParagraph) {
|
|
129
|
-
capturingParagraph = this.dom.createElement('p');
|
|
130
|
-
append(capturingParagraph);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
capturingParagraph.append(node);
|
|
134
|
-
previousNode = node;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
itemEl.append(fragment);
|
|
138
|
-
this._removeStyleProperties(itemEl, ContentNormalizer.BLOCK_STYLES);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
_isBlockNode(node) {
|
|
142
|
-
return ContentNormalizer.BLOCK_NODE_NAMES.includes(node.tagName);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
_assignElementProperties(target, source, properties) {
|
|
146
|
-
for (const property of properties) {
|
|
147
|
-
const value = source.style.getPropertyValue(property);
|
|
148
|
-
|
|
149
|
-
if (value && !target.style.getPropertyValue(property)) {
|
|
150
|
-
target.style.setProperty(property, value);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
_removeStyleProperties(element, properties) {
|
|
156
|
-
for (const property of properties) {
|
|
157
|
-
element.style.removeProperty(property);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (element.style.length === 0) {
|
|
161
|
-
element.removeAttribute('style');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
_normalizeBreakLines({ parentElement }) {
|
|
166
|
-
if (!this._isBlockNode(parentElement)) return;
|
|
167
|
-
if (!parentElement.textContent) return;
|
|
168
|
-
|
|
169
|
-
const fragment = this.dom.createDocumentFragment();
|
|
170
|
-
const children = Array.from(parentElement.childNodes);
|
|
171
|
-
const parentTemplate = parentElement.cloneNode(true);
|
|
172
|
-
|
|
173
|
-
parentTemplate.innerHTML = '';
|
|
174
|
-
let capturingParagraph = parentTemplate.cloneNode();
|
|
175
|
-
|
|
176
|
-
const append = (node) => {
|
|
177
|
-
this._assignElementProperties(node, parentElement, ContentNormalizer.BLOCK_STYLES);
|
|
178
|
-
fragment.append(node);
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
for (const child of children) {
|
|
182
|
-
if (child.tagName === 'BR') {
|
|
183
|
-
append(capturingParagraph);
|
|
184
|
-
capturingParagraph = parentTemplate.cloneNode();
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
capturingParagraph.append(child);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
fragment.append(capturingParagraph);
|
|
192
|
-
parentElement.replaceWith(fragment);
|
|
193
|
-
}
|
|
194
|
-
}
|