@zipify/wysiwyg 1.3.0-0 → 2.0.0-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.
Files changed (84) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/wysiwyg.css +31 -42
  3. package/dist/wysiwyg.mjs +454 -399
  4. package/lib/__tests__/utils/buildTestExtensions.js +14 -0
  5. package/lib/__tests__/utils/index.js +1 -0
  6. package/lib/components/base/Button.vue +0 -7
  7. package/lib/components/base/dropdown/Dropdown.vue +1 -7
  8. package/lib/components/base/dropdown/DropdownActivator.vue +4 -19
  9. package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +1 -23
  10. package/lib/components/toolbar/controls/AlignmentControl.vue +1 -11
  11. package/lib/components/toolbar/controls/FontColorControl.vue +0 -13
  12. package/lib/components/toolbar/controls/FontFamilyControl.vue +0 -4
  13. package/lib/components/toolbar/controls/FontSizeControl.vue +1 -6
  14. package/lib/components/toolbar/controls/FontWeightControl.vue +0 -12
  15. package/lib/components/toolbar/controls/ItalicControl.vue +0 -13
  16. package/lib/components/toolbar/controls/LineHeightControl.vue +0 -14
  17. package/lib/components/toolbar/controls/SuperscriptControl.vue +2 -2
  18. package/lib/components/toolbar/controls/UnderlineControl.vue +0 -12
  19. package/lib/components/toolbar/controls/__tests__/AlignmentControl.test.js +5 -72
  20. package/lib/components/toolbar/controls/__tests__/FontColorControl.test.js +1 -22
  21. package/lib/components/toolbar/controls/__tests__/FontFamilyControl.test.js +0 -1
  22. package/lib/components/toolbar/controls/__tests__/FontSizeControl.test.js +0 -1
  23. package/lib/components/toolbar/controls/__tests__/FontWeightControl.test.js +0 -1
  24. package/lib/components/toolbar/controls/__tests__/ItalicControl.test.js +1 -23
  25. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +1 -23
  26. package/lib/components/toolbar/controls/__tests__/SuperscriptControl.test.js +2 -2
  27. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +1 -25
  28. package/lib/composables/__tests__/useEditor.test.js +2 -2
  29. package/lib/extensions/BackgroundColor.js +4 -4
  30. package/lib/extensions/FontColor.js +4 -5
  31. package/lib/extensions/FontFamily.js +4 -5
  32. package/lib/extensions/FontSize.js +5 -7
  33. package/lib/extensions/FontStyle.js +13 -11
  34. package/lib/extensions/FontWeight.js +6 -9
  35. package/lib/extensions/StylePreset.js +0 -14
  36. package/lib/extensions/Superscript.js +23 -1
  37. package/lib/extensions/TextDecoration.js +33 -20
  38. package/lib/extensions/__tests__/Alignment.test.js +10 -7
  39. package/lib/extensions/__tests__/BackgroundColor.test.js +6 -3
  40. package/lib/extensions/__tests__/CaseStyle.test.js +11 -7
  41. package/lib/extensions/__tests__/FontColor.test.js +6 -3
  42. package/lib/extensions/__tests__/FontFamily.test.js +29 -22
  43. package/lib/extensions/__tests__/FontSize.test.js +24 -17
  44. package/lib/extensions/__tests__/FontStyle.test.js +22 -16
  45. package/lib/extensions/__tests__/FontWeight.test.js +26 -19
  46. package/lib/extensions/__tests__/LineHeight.test.js +14 -11
  47. package/lib/extensions/__tests__/Link.test.js +14 -10
  48. package/lib/extensions/__tests__/Margin.test.js +2 -2
  49. package/lib/extensions/__tests__/StylePreset.test.js +49 -100
  50. package/lib/extensions/__tests__/TextDecoration.test.js +72 -46
  51. package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +24 -24
  52. package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +24 -24
  53. package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +86 -82
  54. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +70 -70
  55. package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +53 -45
  56. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +64 -60
  57. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +148 -83
  58. package/lib/extensions/core/Document.js +5 -0
  59. package/lib/extensions/core/Heading.js +10 -0
  60. package/lib/extensions/core/NodeProcessor.js +84 -4
  61. package/lib/extensions/core/Paragraph.js +9 -0
  62. package/lib/extensions/core/TextProcessor.js +10 -12
  63. package/lib/extensions/core/__tests__/NodeProcessor.test.js +82 -10
  64. package/lib/extensions/core/__tests__/SelectionProcessor.test.js +2 -2
  65. package/lib/extensions/core/__tests__/TextProcessor.test.js +18 -20
  66. package/lib/extensions/core/__tests__/__snapshots__/NodeProcessor.test.js.snap +132 -0
  67. package/lib/extensions/core/index.js +5 -5
  68. package/lib/extensions/core/steps/AddNodeMarkStep.js +60 -0
  69. package/lib/extensions/core/steps/RemoveNodeMarkStep.js +50 -0
  70. package/lib/extensions/core/steps/index.js +2 -0
  71. package/lib/extensions/list/List.js +1 -0
  72. package/lib/extensions/list/ListItem.js +5 -0
  73. package/lib/extensions/list/__tests__/List.test.js +30 -25
  74. package/lib/services/ContentNormalizer.js +1 -100
  75. package/lib/services/NodeFactory.js +16 -6
  76. package/lib/services/__tests__/ContentNormalizer.test.js +0 -64
  77. package/lib/utils/findMarkByType.js +5 -0
  78. package/lib/utils/index.js +5 -0
  79. package/lib/utils/isMarkAppliedToParent.js +15 -0
  80. package/lib/utils/isNodeFullySelected.js +10 -0
  81. package/lib/utils/resolveNodePosition.js +6 -0
  82. package/lib/utils/resolveTextPosition.js +6 -0
  83. package/package.json +1 -1
  84. package/lib/assets/icons/indicator.svg +0 -5
@@ -13,21 +13,6 @@ export class ContentNormalizer {
13
13
  'margin-right'
14
14
  ];
15
15
 
16
- static ASSIGN_STYLE_RULES = [
17
- {
18
- tag: /^(b|strong)$/,
19
- ignore: /font-weight/
20
- },
21
- {
22
- tag: /^i$/,
23
- ignore: /font-style/
24
- },
25
- {
26
- tag: /^s$/,
27
- ignore: /text-decoration(.+)?/
28
- }
29
- ];
30
-
31
16
  static build(content, options = {}) {
32
17
  return new ContentNormalizer({
33
18
  content,
@@ -60,8 +45,6 @@ export class ContentNormalizer {
60
45
  this.iterateNodes(this._normalizeBreakLines, (node) => node.tagName === 'BR');
61
46
  this.iterateNodes(this._removeEmptyNodes, this._isBlockNode);
62
47
  this.iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
63
- this.iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === 'SPAN');
64
- this.iterateNodes(this._normalizeStyles, (node) => node.tagName !== 'SPAN');
65
48
  }
66
49
 
67
50
  get normalizedHTML() {
@@ -72,10 +55,6 @@ export class ContentNormalizer {
72
55
  return this._parser.types.NodeFilter;
73
56
  }
74
57
 
75
- get _Node() {
76
- return this._parser.types.Node;
77
- }
78
-
79
58
  _removeComments() {
80
59
  const iterator = this.createNodeIterator(this._NodeFilter.SHOW_COMMENT);
81
60
 
@@ -159,98 +138,20 @@ export class ContentNormalizer {
159
138
  this._removeStyleProperties(itemEl, ContentNormalizer.BLOCK_STYLES);
160
139
  }
161
140
 
162
- _normalizeSettingsStructure(element) {
163
- if (this._isOnlyTextContent(element)) return;
164
-
165
- const cloned = element.cloneNode(true);
166
- const migratingStyles = this._getMigratingStyles(element, { customProperties: true });
167
- const content = [];
168
-
169
- for (const node of cloned.childNodes) {
170
- let child = node;
171
-
172
- if (migratingStyles.length) {
173
- child = this._wrapTextNode(cloned, node);
174
- this._assignElementProperties(child, cloned, migratingStyles);
175
- }
176
-
177
- content.push(child);
178
- }
179
-
180
- element.replaceWith(...content);
181
- }
182
-
183
- _normalizeStyles(element) {
184
- if (!this._isBlockNode(element) && this._isOnlyTextContent(element)) return;
185
-
186
- const properties = this._getMigratingStyles(element);
187
-
188
- if (!properties.length) return;
189
-
190
- for (const node of element.childNodes) {
191
- const child = this._wrapTextNode(element, node);
192
-
193
- this._assignElementProperties(child, element, properties);
194
- }
195
-
196
- this._removeStyleProperties(element, properties);
197
- }
198
-
199
- _isOnlyTextContent(node) {
200
- return Array.from(node.childNodes).every((node) => node.nodeType === this._Node.TEXT_NODE);
201
- }
202
-
203
141
  _isBlockNode(node) {
204
142
  return ContentNormalizer.BLOCK_NODE_NAMES.includes(node.tagName);
205
143
  }
206
144
 
207
- _getMigratingStyles(element, { customProperties } = {}) {
208
- const blacklist = ContentNormalizer.BLOCK_STYLES;
209
- const properties = [];
210
-
211
- for (let index = 0; index < element.style.length; index++) {
212
- const property = element.style.item(index);
213
-
214
- if (blacklist.includes(property)) continue;
215
- if (!customProperties && property.startsWith('--')) continue;
216
-
217
- properties.push(property);
218
- }
219
-
220
- return properties;
221
- }
222
-
223
- _wrapTextNode(parent, node) {
224
- if (node.nodeType !== this._Node.TEXT_NODE) return node;
225
-
226
- const span = this.dom.createElement('span');
227
-
228
- span.append(node.cloneNode());
229
- parent.replaceChild(span, node);
230
-
231
- return span;
232
- }
233
-
234
145
  _assignElementProperties(target, source, properties) {
235
146
  for (const property of properties) {
236
147
  const value = source.style.getPropertyValue(property);
237
148
 
238
- if (value && this._canAssignElementProperty(target, source, property)) {
149
+ if (value && !target.style.getPropertyValue(property)) {
239
150
  target.style.setProperty(property, value);
240
151
  }
241
152
  }
242
153
  }
243
154
 
244
- _canAssignElementProperty(target, source, property) {
245
- if (target.style.getPropertyValue(property)) return false;
246
-
247
- return ContentNormalizer.ASSIGN_STYLE_RULES.every((rule) => {
248
- if (!rule.tag.test(target.tagName.toLowerCase())) return true;
249
-
250
- return !rule.ignore.test(property);
251
- });
252
- }
253
-
254
155
  _removeStyleProperties(element, properties) {
255
156
  for (const property of properties) {
256
157
  element.style.removeProperty(property);
@@ -24,7 +24,7 @@ export class NodeFactory {
24
24
  }
25
25
 
26
26
  static heading(level, ...args) {
27
- const config = this._textBlock(args);
27
+ const config = this.#textBlock(args);
28
28
 
29
29
  config.attrs ??= {};
30
30
  config.attrs.level = level;
@@ -38,21 +38,31 @@ export class NodeFactory {
38
38
  static paragraph(...args) {
39
39
  return {
40
40
  type: NodeTypes.PARAGRAPH,
41
- ...this._textBlock(args)
41
+ ...this.#textBlock(args)
42
42
  };
43
43
  }
44
44
 
45
- static _textBlock(args) {
46
- const attrs = args.length === 1 ? null : args[0];
47
- const content = args.length === 1 ? args[0] : args[1];
45
+ static #textBlock(args) {
46
+ const { attrs, content, marks } = this.#normalizeTextBlockArgs(args);
48
47
  const children = typeof content === 'string' ? [this.text(content)] : content;
49
48
 
50
49
  return {
51
50
  content: children,
52
- ...(attrs ? { attrs } : {})
51
+ ...(attrs ? { attrs } : {}),
52
+ ...(marks ? { marks } : {})
53
53
  };
54
54
  }
55
55
 
56
+ static #normalizeTextBlockArgs(args) {
57
+ if (args.length === 1) {
58
+ return { attrs: null, marks: null, content: args[0] };
59
+ }
60
+ if (args.length === 2) {
61
+ return { attrs: args[0], marks: null, content: args[1] };
62
+ }
63
+ return { attrs: args[0], marks: args[1], content: args[2] };
64
+ }
65
+
56
66
  static text(text, marks) {
57
67
  return {
58
68
  type: NodeTypes.TEXT,
@@ -8,35 +8,6 @@ describe('normalize text content', () => {
8
8
  expect(ContentNormalizer.normalize(content)).toBe(content);
9
9
  });
10
10
 
11
- test('should flat structure', () => {
12
- const input = '<p style="text-align: center; color: rgb(255, 255, 255);"><span style="background-color: rgb(255, 0, 0);">lore<span style="color: rgb(0, 0, 0);">m ip</span>sum</span></p>';
13
- const output = '<p style="text-align: center;">' +
14
- '<span style="background-color: rgb(255, 0, 0); color: rgb(255, 255, 255);">lore</span>' +
15
- '<span style="color: rgb(0, 0, 0); background-color: rgb(255, 0, 0);">m ip</span>' +
16
- '<span style="background-color: rgb(255, 0, 0); color: rgb(255, 255, 255);">sum</span>' +
17
- '</p>';
18
-
19
- expect(ContentNormalizer.normalize(input)).toBe(output);
20
- });
21
-
22
- test('should move styles from paragraph to text', () => {
23
- const input = '<p style="background-color: red;">lorem ipsum</p>';
24
- const output = '<p><span style="background-color: red;">lorem ipsum</span></p>';
25
-
26
- expect(ContentNormalizer.normalize(input)).toBe(output);
27
- });
28
-
29
- test('should move styles from paragraph to unstyled text', () => {
30
- const input = '<p style="background-color: red;"><span style="background-color: #000;">lorem</span> ipsum <span style="color: white;">one</span></p>';
31
- const output = '<p>' +
32
- '<span style="background-color: #000;">lorem</span>' +
33
- '<span style="background-color: red;"> ipsum </span>' +
34
- '<span style="color: white; background-color: red;">one</span' +
35
- '></p>';
36
-
37
- expect(ContentNormalizer.normalize(input)).toBe(output);
38
- });
39
-
40
11
  test('should wrap list content in paragraph', () => {
41
12
  const input = '<ul><li style="line-height: 2;">lorem impsum</li></ul>';
42
13
  const output = '<ul><li><p style="line-height: 2;">lorem impsum</p></li></ul>';
@@ -99,34 +70,6 @@ describe('normalize text content', () => {
99
70
  expect(ContentNormalizer.normalize(input)).toBe(output);
100
71
  });
101
72
 
102
- test('should not ignore setting', () => {
103
- const input = '<p style="text-decoration-line: underline;">lorem ipsum</p>';
104
- const output = '<p><span style="text-decoration-line: underline;">lorem ipsum</span></p>';
105
-
106
- expect(ContentNormalizer.normalize(input)).toBe(output);
107
- });
108
-
109
- test('should not assign font-weight to b tag', () => {
110
- const input = '<p style="font-weight: 400;"><b>lorem ipsum</b></p>';
111
- const output = '<p><b>lorem ipsum</b></p>';
112
-
113
- expect(ContentNormalizer.normalize(input)).toBe(output);
114
- });
115
-
116
- test('should not assign font-style to i tag', () => {
117
- const input = '<p style="font-style: normal;"><i>lorem ipsum</i></p>';
118
- const output = '<p><i>lorem ipsum</i></p>';
119
-
120
- expect(ContentNormalizer.normalize(input)).toBe(output);
121
- });
122
-
123
- test('should not assign text-decoration to s tag', () => {
124
- const input = '<p style="text-decoration-line: initial;"><s>lorem ipsum</s></p>';
125
- const output = '<p><s>lorem ipsum</s></p>';
126
-
127
- expect(ContentNormalizer.normalize(input)).toBe(output);
128
- });
129
-
130
73
  test('should assign block styles from list to paragraph', () => {
131
74
  const input = '<ul style="line-height: 2;"><li>lorem ipsum</li></ul>';
132
75
  const output = '<ul style="line-height: 2;"><li><p style="line-height: 2;">lorem ipsum</p></li></ul>';
@@ -164,11 +107,4 @@ describe('normalize text content', () => {
164
107
 
165
108
  expect(ContentNormalizer.normalize(input)).toBe(output);
166
109
  });
167
-
168
- test('should remove comments', () => {
169
- const input = '<p>lorem ipsum</p><p style="color: red;">Hello <!-- world --></p>';
170
- const output = '<p>lorem ipsum</p><p><span style="color: red;">Hello </span></p>';
171
-
172
- expect(ContentNormalizer.normalize(input)).toBe(output);
173
- });
174
110
  });
@@ -0,0 +1,5 @@
1
+ export function findMarkByType(list, typeOrName) {
2
+ const name = typeof typeOrName === 'string' ? typeOrName : typeOrName.name;
3
+
4
+ return list.find((mark) => mark.type.name === name);
5
+ }
@@ -8,3 +8,8 @@ export { convertFontSize } from './convertFontSize';
8
8
  export { convertAlignment } from './convertAlignment';
9
9
  export { importIcon } from './importIcon';
10
10
  export { isWysiwygContent, markWysiwygContent, unmarkWysiwygContent } from './isWysiwygContent';
11
+ export { resolveNodePosition } from './resolveNodePosition';
12
+ export { resolveTextPosition } from './resolveTextPosition';
13
+ export { isNodeFullySelected } from './isNodeFullySelected';
14
+ export { isMarkAppliedToParent } from './isMarkAppliedToParent';
15
+ export { findMarkByType } from './findMarkByType';
@@ -0,0 +1,15 @@
1
+ const DEFAULT_COMPARATOR = (parent, child) => child.eq(parent);
2
+
3
+ export function isMarkAppliedToParent({ doc }, position, checkingMark, comparator = DEFAULT_COMPARATOR) {
4
+ const steps = doc.resolve(position).path.reverse();
5
+
6
+ for (const step of steps) {
7
+ if (typeof step === 'number') continue;
8
+
9
+ for (const mark of step.marks) {
10
+ if (comparator(mark, checkingMark)) return true;
11
+ }
12
+ }
13
+
14
+ return false;
15
+ }
@@ -0,0 +1,10 @@
1
+ import { resolveNodePosition } from './resolveNodePosition';
2
+
3
+ export function isNodeFullySelected($from, $to, node, position) {
4
+ const fromPosition = resolveNodePosition($from, node, -1);
5
+ const toPosition = resolveNodePosition($to, node, 1);
6
+ const isFromMatch = fromPosition <= position;
7
+ const isToMatch = toPosition >= node.nodeSize + position;
8
+
9
+ return isFromMatch && isToMatch;
10
+ }
@@ -0,0 +1,6 @@
1
+ export function resolveNodePosition({ path, pos }, node, modifier) {
2
+ const index = path.indexOf(node);
3
+ const slice = path.slice(index).reverse().filter((step) => typeof step === 'object');
4
+
5
+ return pos + (modifier * slice.length);
6
+ }
@@ -0,0 +1,6 @@
1
+ export function resolveTextPosition($from, $to, node, position) {
2
+ return {
3
+ from: Math.max(position, $from.pos),
4
+ to: Math.min(position + node.nodeSize, $to.pos)
5
+ };
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zipify/wysiwyg",
3
- "version": "1.3.0-0",
3
+ "version": "2.0.0-0",
4
4
  "description": "Zipify modification of TipTap text editor",
5
5
  "main": "dist/wysiwyg.mjs",
6
6
  "bin": {
@@ -1,5 +0,0 @@
1
- <svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M0 4.5C0 2.01472 2.01472 0 4.5 0C6.98528 0 9 2.01472 9 4.5C9 6.98528 6.98528 9 4.5 9C2.01472 9 0 6.98528 0 4.5Z" fill="#FFAB00"/>
3
- <path fill-rule="evenodd" clip-rule="evenodd" d="M5.0625 2.25H3.9375V5.625H5.0625V2.25Z" fill="white"/>
4
- <path fill-rule="evenodd" clip-rule="evenodd" d="M3.9375 6.75C3.9375 6.43894 4.18894 6.1875 4.5 6.1875C4.8105 6.1875 5.0625 6.43894 5.0625 6.75C5.0625 7.06106 4.8105 7.3125 4.5 7.3125C4.18894 7.3125 3.9375 7.06106 3.9375 6.75Z" fill="white"/>
5
- </svg>