@zipify/wysiwyg 1.0.0-dev.9 → 1.0.0-dev.90

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 (232) hide show
  1. package/.editorconfig +1 -1
  2. package/.eslintrc.js +2 -2
  3. package/.github/dependabot.yaml +1 -0
  4. package/.lintstagedrc +2 -2
  5. package/.release-it.json +3 -1
  6. package/.stylelintrc +0 -4
  7. package/README.md +2 -2
  8. package/config/jest/setupTests.js +0 -3
  9. package/config/vite/example.config.js +25 -0
  10. package/config/vite/lib.config.js +30 -0
  11. package/config/{webpack → vite}/settings.js +0 -0
  12. package/dist/wysiwyg.css +404 -373
  13. package/dist/wysiwyg.mjs +26014 -0
  14. package/example/ExampleApp.vue +35 -4
  15. package/example/example.js +0 -3
  16. package/example/{example.html → index.html} +1 -0
  17. package/example/pageBlocks.js +31 -0
  18. package/example/presets.js +2 -2
  19. package/example/tooltip/Tooltip.js +1 -1
  20. package/jest.config.js +3 -1
  21. package/lib/Wysiwyg.vue +73 -14
  22. package/lib/__tests__/utils/index.js +0 -1
  23. package/lib/__tests__/utils/withComponentContext.js +1 -1
  24. package/lib/assets/icons/background-color.svg +3 -1
  25. package/lib/assets/icons/font-color.svg +1 -3
  26. package/lib/assets/icons/link.svg +3 -0
  27. package/lib/assets/icons/unlink.svg +3 -0
  28. package/lib/components/base/Button.vue +23 -2
  29. package/lib/components/base/Checkbox.vue +89 -0
  30. package/lib/components/base/FieldLabel.vue +2 -1
  31. package/lib/components/base/Icon.vue +3 -3
  32. package/lib/components/base/Modal.vue +1 -2
  33. package/lib/components/base/NumberField.vue +2 -2
  34. package/lib/components/base/Range.vue +1 -1
  35. package/lib/components/base/ScrollView.vue +1 -3
  36. package/lib/components/base/TextField.vue +108 -0
  37. package/lib/components/base/__tests__/Modal.test.js +7 -2
  38. package/lib/components/base/__tests__/TextField.test.js +57 -0
  39. package/lib/components/base/__tests__/__snapshots__/TextField.test.js.snap +9 -0
  40. package/lib/components/base/colorPicker/ColorPicker.vue +14 -12
  41. package/lib/components/base/colorPicker/composables/__tests__/usePickerApi.test.js +6 -6
  42. package/lib/components/base/colorPicker/composables/usePickerApi.js +15 -8
  43. package/lib/components/base/colorPicker/composables/usePickerHotkeys.js +3 -2
  44. package/lib/components/base/composables/__tests__/useActivatedListener.test.js +1 -1
  45. package/lib/components/base/composables/__tests__/useDeselectionLock.test.js +1 -1
  46. package/lib/components/base/composables/__tests__/useElementRef.test.js +1 -1
  47. package/lib/components/base/composables/__tests__/useModalToggler.test.js +1 -1
  48. package/lib/components/base/composables/__tests__/useNumberValue.test.js +1 -1
  49. package/lib/components/base/composables/__tests__/useScrollView.test.js +1 -1
  50. package/lib/components/base/composables/__tests__/useTempValue.test.js +1 -1
  51. package/lib/components/base/composables/__tests__/useValidator.test.js +44 -0
  52. package/lib/components/base/composables/index.js +1 -0
  53. package/lib/components/base/composables/useActivatedListener.js +1 -1
  54. package/lib/components/base/composables/useDeselectionLock.js +1 -1
  55. package/lib/components/base/composables/useElementRef.js +1 -1
  56. package/lib/components/base/composables/useModalToggler.js +6 -2
  57. package/lib/components/base/composables/useScrollView.js +1 -1
  58. package/lib/components/base/composables/useTempValue.js +1 -1
  59. package/lib/components/base/composables/useValidator.js +23 -0
  60. package/lib/components/base/dropdown/Dropdown.vue +16 -4
  61. package/lib/components/base/dropdown/DropdownActivator.vue +19 -3
  62. package/lib/components/base/dropdown/DropdownGroup.vue +1 -1
  63. package/lib/components/base/dropdown/DropdownMenu.vue +1 -1
  64. package/lib/components/base/dropdown/DropdownOption.vue +1 -1
  65. package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +1 -1
  66. package/lib/components/base/dropdown/__tests__/DropdownMenu.test.js +1 -1
  67. package/lib/components/base/dropdown/__tests__/DropdownOption.test.js +1 -1
  68. package/lib/components/base/dropdown/composables/__tests__/useActiveOptionManager.test.js +1 -1
  69. package/lib/components/base/dropdown/composables/__tests__/useDropdownEntityTitle.test.js +1 -1
  70. package/lib/components/base/dropdown/composables/useActiveOptionManager.js +1 -1
  71. package/lib/components/base/dropdown/composables/useDropdownEntityTitle.js +1 -1
  72. package/lib/components/base/index.js +3 -1
  73. package/lib/components/toolbar/Toolbar.vue +51 -9
  74. package/lib/components/toolbar/ToolbarDivider.vue +1 -1
  75. package/lib/components/toolbar/ToolbarFull.vue +41 -10
  76. package/lib/components/toolbar/ToolbarRow.vue +1 -0
  77. package/lib/components/toolbar/__tests__/Toolbar.test.js +6 -0
  78. package/lib/components/toolbar/controls/AlignmentControl.vue +1 -1
  79. package/lib/components/toolbar/controls/AlignmentDeviceControl.vue +1 -1
  80. package/lib/components/toolbar/controls/BackgroundColorControl.vue +1 -1
  81. package/lib/components/toolbar/controls/CaseStyleControl.vue +1 -1
  82. package/lib/components/toolbar/controls/FontColorControl.vue +1 -1
  83. package/lib/components/toolbar/controls/FontFamilyControl.vue +1 -1
  84. package/lib/components/toolbar/controls/FontSizeControl.vue +8 -1
  85. package/lib/components/toolbar/controls/FontWeightControl.vue +1 -1
  86. package/lib/components/toolbar/controls/ItalicControl.vue +1 -1
  87. package/lib/components/toolbar/controls/LineHeightControl.vue +1 -1
  88. package/lib/components/toolbar/controls/ListControl.vue +68 -34
  89. package/lib/components/toolbar/controls/RemoveFormatControl.vue +1 -1
  90. package/lib/components/toolbar/controls/StrikeThroughControl.vue +1 -1
  91. package/lib/components/toolbar/controls/StylePresetControl.vue +15 -2
  92. package/lib/components/toolbar/controls/SuperscriptControl.vue +1 -1
  93. package/lib/components/toolbar/controls/UnderlineControl.vue +2 -2
  94. package/lib/components/toolbar/controls/__tests__/AlignmentControl.test.js +1 -1
  95. package/lib/components/toolbar/controls/__tests__/AlignmentDeviceControl.test.js +1 -1
  96. package/lib/components/toolbar/controls/__tests__/BackgroundColorControl.test.js +1 -1
  97. package/lib/components/toolbar/controls/__tests__/CaseStyleControl.test.js +1 -1
  98. package/lib/components/toolbar/controls/__tests__/FontColorControl.test.js +1 -1
  99. package/lib/components/toolbar/controls/__tests__/FontFamilyControl.test.js +1 -1
  100. package/lib/components/toolbar/controls/__tests__/FontSizeControl.test.js +1 -1
  101. package/lib/components/toolbar/controls/__tests__/FontWeightControl.test.js +1 -1
  102. package/lib/components/toolbar/controls/__tests__/ItalicControl.test.js +1 -1
  103. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +1 -1
  104. package/lib/components/toolbar/controls/__tests__/ListControl.test.js +18 -3
  105. package/lib/components/toolbar/controls/__tests__/StrikeThroughControl.test.js +1 -1
  106. package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +17 -1
  107. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +5 -1
  108. package/lib/components/toolbar/controls/composables/useRecentFonts.js +1 -1
  109. package/lib/components/toolbar/controls/index.js +1 -0
  110. package/lib/components/toolbar/controls/link/LinkControl.vue +160 -0
  111. package/lib/components/toolbar/controls/link/LinkControlApply.vue +35 -0
  112. package/lib/components/toolbar/controls/link/LinkControlHeader.vue +69 -0
  113. package/lib/components/toolbar/controls/link/__tests__/LinkControl.test.js +79 -0
  114. package/lib/components/toolbar/controls/link/__tests__/LinkControlHeader.test.js +42 -0
  115. package/lib/components/toolbar/controls/link/composables/__tests__/__snapshots__/useLink.test.js.snap +15 -0
  116. package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +172 -0
  117. package/lib/components/toolbar/controls/link/composables/index.js +1 -0
  118. package/lib/components/toolbar/controls/link/composables/useLink.js +82 -0
  119. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +103 -0
  120. package/lib/components/toolbar/controls/link/destination/LinkControlPageBlock.vue +54 -0
  121. package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +52 -0
  122. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlPageBlock.test.js +36 -0
  123. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlUrl.test.js +46 -0
  124. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlPageBlock.test.js.snap +9 -0
  125. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlUrl.test.js.snap +17 -0
  126. package/lib/components/toolbar/controls/link/destination/index.js +1 -0
  127. package/lib/components/toolbar/controls/link/index.js +1 -0
  128. package/lib/composables/__tests__/__snapshots__/useEditor.test.js.snap +1 -0
  129. package/lib/composables/__tests__/useEditor.test.js +5 -4
  130. package/lib/composables/useEditor.js +12 -9
  131. package/lib/composables/useToolbar.js +26 -19
  132. package/lib/directives/__tests__/outClick.test.js +6 -0
  133. package/lib/directives/outClick.js +19 -6
  134. package/lib/enums/Alignments.js +10 -1
  135. package/lib/enums/LinkDestinations.js +4 -0
  136. package/lib/enums/LinkTargets.js +4 -0
  137. package/lib/enums/TextSettings.js +3 -1
  138. package/lib/enums/index.js +2 -0
  139. package/lib/extensions/Alignment.js +22 -8
  140. package/lib/extensions/BackgroundColor.js +15 -7
  141. package/lib/extensions/DeviceManager.js +2 -5
  142. package/lib/extensions/FontColor.js +15 -7
  143. package/lib/extensions/FontFamily.js +26 -9
  144. package/lib/extensions/FontSize.js +33 -13
  145. package/lib/extensions/FontStyle.js +24 -14
  146. package/lib/extensions/FontWeight.js +25 -15
  147. package/lib/extensions/LineHeight.js +33 -29
  148. package/lib/extensions/Link.js +90 -0
  149. package/lib/extensions/StylePreset.js +20 -19
  150. package/lib/extensions/Superscript.js +5 -1
  151. package/lib/extensions/TextDecoration.js +46 -13
  152. package/lib/extensions/__tests__/Alignment.test.js +14 -9
  153. package/lib/extensions/__tests__/BackgroundColor.test.js +13 -8
  154. package/lib/extensions/__tests__/CaseStyle.test.js +4 -7
  155. package/lib/extensions/__tests__/FontColor.test.js +13 -8
  156. package/lib/extensions/__tests__/FontFamily.test.js +34 -10
  157. package/lib/extensions/__tests__/FontSize.test.js +16 -10
  158. package/lib/extensions/__tests__/FontStyle.test.js +13 -8
  159. package/lib/extensions/__tests__/FontWeight.test.js +21 -8
  160. package/lib/extensions/__tests__/LineHeight.test.js +25 -14
  161. package/lib/extensions/__tests__/Link.test.js +101 -0
  162. package/lib/extensions/__tests__/StylePreset.test.js +72 -10
  163. package/lib/extensions/__tests__/TextDecoration.test.js +53 -8
  164. package/lib/extensions/__tests__/__snapshots__/Alignment.test.js.snap +26 -2
  165. package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +30 -1
  166. package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +18 -1
  167. package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +88 -1
  168. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +33 -2
  169. package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +25 -4
  170. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +47 -1
  171. package/lib/extensions/__tests__/__snapshots__/LineHeight.test.js.snap +26 -2
  172. package/lib/extensions/__tests__/__snapshots__/Link.test.js.snap +225 -0
  173. package/lib/extensions/__tests__/__snapshots__/StylePreset.test.js.snap +6 -2
  174. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +183 -3
  175. package/lib/extensions/core/CopyPasteProcessor.js +10 -0
  176. package/lib/extensions/core/NodeProcessor.js +1 -1
  177. package/lib/extensions/core/TextProcessor.js +11 -24
  178. package/lib/extensions/core/__tests__/NodeProcessor.test.js +4 -7
  179. package/lib/extensions/core/__tests__/SelectionProcessor.test.js +4 -7
  180. package/lib/extensions/core/__tests__/TextProcessor.test.js +139 -14
  181. package/lib/extensions/core/__tests__/__snapshots__/TextProcessor.test.js.snap +26 -0
  182. package/lib/extensions/core/index.js +11 -2
  183. package/lib/extensions/core/plugins/PastePlugin.js +57 -0
  184. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +20 -0
  185. package/lib/extensions/core/plugins/index.js +1 -0
  186. package/lib/extensions/index.js +46 -34
  187. package/lib/extensions/list/List.js +5 -4
  188. package/lib/extensions/list/__tests__/List.test.js +6 -10
  189. package/lib/extensions/list/__tests__/__snapshots__/List.test.js.snap +60 -20
  190. package/lib/index.js +3 -0
  191. package/lib/injectionTokens.js +3 -1
  192. package/lib/services/ContentNormalizer.js +144 -28
  193. package/lib/services/ContextWidnow.js +23 -0
  194. package/lib/{__tests__/utils → services}/NodeFactory.js +5 -1
  195. package/lib/services/__tests__/ContentNormalizer.test.js +124 -8
  196. package/lib/services/__tests__/FavoriteColors.test.js +1 -1
  197. package/lib/services/index.js +2 -0
  198. package/lib/styles/content.css +108 -13
  199. package/lib/styles/helpers/offsets.css +16 -0
  200. package/lib/styles/variables.css +6 -0
  201. package/lib/utils/__tests__/__snapshots__/renderInlineSetting.test.js.snap +4 -4
  202. package/lib/utils/__tests__/convertAlignment.test.js +16 -0
  203. package/lib/utils/__tests__/convertFontSize.test.js +21 -0
  204. package/lib/utils/__tests__/convertLineHeight.test.js +21 -0
  205. package/lib/utils/convertAlignment.js +12 -0
  206. package/lib/utils/convertColor.js +1 -1
  207. package/lib/utils/convertFontSize.js +8 -0
  208. package/lib/utils/convertLineHeight.js +17 -0
  209. package/lib/utils/importIcon.js +7 -6
  210. package/lib/utils/index.js +4 -0
  211. package/lib/utils/isWysiwygContent.js +12 -0
  212. package/lib/utils/renderInlineSetting.js +1 -1
  213. package/package.json +26 -30
  214. package/config/webpack/example.config.js +0 -88
  215. package/config/webpack/lib.config.js +0 -44
  216. package/config/webpack/loaders/index.js +0 -6
  217. package/config/webpack/loaders/js-loader.js +0 -5
  218. package/config/webpack/loaders/style-loader.js +0 -9
  219. package/config/webpack/loaders/svg-loader.js +0 -4
  220. package/config/webpack/loaders/vue-loader.js +0 -4
  221. package/dist/wysiwyg.js +0 -2
  222. package/dist/wysiwyg.js.LICENSE.txt +0 -1
  223. package/lib/extensions/core/inputRules/closeDoubleQuote.js +0 -6
  224. package/lib/extensions/core/inputRules/closeSingleQuote.js +0 -6
  225. package/lib/extensions/core/inputRules/copyright.js +0 -6
  226. package/lib/extensions/core/inputRules/ellipsis.js +0 -6
  227. package/lib/extensions/core/inputRules/emDash.js +0 -6
  228. package/lib/extensions/core/inputRules/index.js +0 -9
  229. package/lib/extensions/core/inputRules/openDoubleQuote.js +0 -6
  230. package/lib/extensions/core/inputRules/openSingleQuote.js +0 -6
  231. package/lib/extensions/core/inputRules/registeredTrademark.js +0 -6
  232. package/lib/extensions/core/inputRules/trademark.js +0 -6
@@ -14,7 +14,9 @@ Object {
14
14
  "content": Array [
15
15
  Object {
16
16
  "attrs": Object {
17
- "preset": null,
17
+ "preset": Object {
18
+ "id": "regular-1",
19
+ },
18
20
  },
19
21
  "content": Array [
20
22
  Object {
@@ -49,7 +51,9 @@ Object {
49
51
  "content": Array [
50
52
  Object {
51
53
  "attrs": Object {
52
- "preset": null,
54
+ "preset": Object {
55
+ "id": "regular-1",
56
+ },
53
57
  },
54
58
  "content": Array [
55
59
  Object {
@@ -66,7 +70,9 @@ Object {
66
70
  "content": Array [
67
71
  Object {
68
72
  "attrs": Object {
69
- "preset": null,
73
+ "preset": Object {
74
+ "id": "regular-1",
75
+ },
70
76
  },
71
77
  "content": Array [
72
78
  Object {
@@ -92,7 +98,9 @@ Object {
92
98
  "content": Array [
93
99
  Object {
94
100
  "attrs": Object {
95
- "preset": null,
101
+ "preset": Object {
102
+ "id": "regular-1",
103
+ },
96
104
  },
97
105
  "content": Array [
98
106
  Object {
@@ -104,7 +112,9 @@ Object {
104
112
  },
105
113
  Object {
106
114
  "attrs": Object {
107
- "preset": null,
115
+ "preset": Object {
116
+ "id": "regular-1",
117
+ },
108
118
  },
109
119
  "content": Array [
110
120
  Object {
@@ -133,7 +143,9 @@ Object {
133
143
  "content": Array [
134
144
  Object {
135
145
  "attrs": Object {
136
- "preset": null,
146
+ "preset": Object {
147
+ "id": "regular-1",
148
+ },
137
149
  },
138
150
  "content": Array [
139
151
  Object {
@@ -150,7 +162,9 @@ Object {
150
162
  "content": Array [
151
163
  Object {
152
164
  "attrs": Object {
153
- "preset": null,
165
+ "preset": Object {
166
+ "id": "regular-1",
167
+ },
154
168
  },
155
169
  "content": Array [
156
170
  Object {
@@ -221,7 +235,9 @@ Object {
221
235
  "content": Array [
222
236
  Object {
223
237
  "attrs": Object {
224
- "preset": null,
238
+ "preset": Object {
239
+ "id": "regular-1",
240
+ },
225
241
  },
226
242
  "content": Array [
227
243
  Object {
@@ -256,7 +272,9 @@ Object {
256
272
  "content": Array [
257
273
  Object {
258
274
  "attrs": Object {
259
- "preset": null,
275
+ "preset": Object {
276
+ "id": "regular-1",
277
+ },
260
278
  },
261
279
  "content": Array [
262
280
  Object {
@@ -291,7 +309,9 @@ Object {
291
309
  "content": Array [
292
310
  Object {
293
311
  "attrs": Object {
294
- "preset": null,
312
+ "preset": Object {
313
+ "id": "regular-1",
314
+ },
295
315
  },
296
316
  "content": Array [
297
317
  Object {
@@ -326,7 +346,9 @@ Object {
326
346
  "content": Array [
327
347
  Object {
328
348
  "attrs": Object {
329
- "preset": null,
349
+ "preset": Object {
350
+ "id": "regular-1",
351
+ },
330
352
  },
331
353
  "content": Array [
332
354
  Object {
@@ -361,7 +383,9 @@ Object {
361
383
  "content": Array [
362
384
  Object {
363
385
  "attrs": Object {
364
- "preset": null,
386
+ "preset": Object {
387
+ "id": "regular-1",
388
+ },
365
389
  },
366
390
  "content": Array [
367
391
  Object {
@@ -396,7 +420,9 @@ Object {
396
420
  "content": Array [
397
421
  Object {
398
422
  "attrs": Object {
399
- "preset": null,
423
+ "preset": Object {
424
+ "id": "regular-1",
425
+ },
400
426
  },
401
427
  "content": Array [
402
428
  Object {
@@ -431,7 +457,9 @@ Object {
431
457
  "content": Array [
432
458
  Object {
433
459
  "attrs": Object {
434
- "preset": null,
460
+ "preset": Object {
461
+ "id": "regular-1",
462
+ },
435
463
  },
436
464
  "content": Array [
437
465
  Object {
@@ -466,7 +494,9 @@ Object {
466
494
  "content": Array [
467
495
  Object {
468
496
  "attrs": Object {
469
- "preset": null,
497
+ "preset": Object {
498
+ "id": "regular-1",
499
+ },
470
500
  },
471
501
  "content": Array [
472
502
  Object {
@@ -501,7 +531,9 @@ Object {
501
531
  "content": Array [
502
532
  Object {
503
533
  "attrs": Object {
504
- "preset": null,
534
+ "preset": Object {
535
+ "id": "regular-1",
536
+ },
505
537
  },
506
538
  "content": Array [
507
539
  Object {
@@ -536,7 +568,9 @@ Object {
536
568
  "content": Array [
537
569
  Object {
538
570
  "attrs": Object {
539
- "preset": null,
571
+ "preset": Object {
572
+ "id": "regular-1",
573
+ },
540
574
  },
541
575
  "content": Array [
542
576
  Object {
@@ -571,7 +605,9 @@ Object {
571
605
  "content": Array [
572
606
  Object {
573
607
  "attrs": Object {
574
- "preset": null,
608
+ "preset": Object {
609
+ "id": "regular-1",
610
+ },
575
611
  },
576
612
  "content": Array [
577
613
  Object {
@@ -606,7 +642,9 @@ Object {
606
642
  "content": Array [
607
643
  Object {
608
644
  "attrs": Object {
609
- "preset": null,
645
+ "preset": Object {
646
+ "id": "regular-1",
647
+ },
610
648
  },
611
649
  "content": Array [
612
650
  Object {
@@ -641,7 +679,9 @@ Object {
641
679
  "content": Array [
642
680
  Object {
643
681
  "attrs": Object {
644
- "preset": null,
682
+ "preset": Object {
683
+ "id": "regular-1",
684
+ },
645
685
  },
646
686
  "content": Array [
647
687
  Object {
package/lib/index.js CHANGED
@@ -1 +1,4 @@
1
1
  export { default as Wysiwyg } from './Wysiwyg';
2
+ export { NodeFactory } from './services';
3
+ export { NodeTypes, TextSettings, Alignments } from './enums';
4
+ export { isWysiwygContent } from './utils';
@@ -3,5 +3,7 @@ export const InjectionTokens = Object.freeze({
3
3
  FONT_SIZES: Symbol('fontSizes'),
4
4
  EDITOR: Symbol('editor'),
5
5
  LOCAL_STORAGE: Symbol('localStorage'),
6
- FAVORITE_COLORS: Symbol('favoriteColors')
6
+ FAVORITE_COLORS: Symbol('favoriteColors'),
7
+ PAGE_BLOCKS: Symbol('pageBlocks'),
8
+ POPUP_MODE: Symbol('popupMode')
7
9
  });
@@ -1,42 +1,108 @@
1
1
  export class ContentNormalizer {
2
- static NORMALIZING_STYLE_BLACKLIST = ['text-align', 'line-height'];
2
+ static PARSER = new DOMParser();
3
+ static BLOCK_STYLES = ['text-align', 'line-height'];
4
+ static BLOCK_NODE_NAMES = ['P', 'H1', 'H2', 'H3', 'H4'];
5
+
6
+ static ASSIGN_STYLE_RULES = [
7
+ {
8
+ tag: /^(b|strong)$/,
9
+ ignore: /font-weight/
10
+ },
11
+ {
12
+ tag: /^i$/,
13
+ ignore: /font-style/
14
+ },
15
+ {
16
+ tag: /^s$/,
17
+ ignore: /text-decoration(.+)?/
18
+ }
19
+ ];
20
+
21
+ static normalize(content) {
22
+ const options = { content, parser: ContentNormalizer.PARSER };
23
+
24
+ return new ContentNormalizer(options).normalize();
25
+ }
3
26
 
4
- normalize(content) {
5
- if (typeof content !== 'string') return content;
27
+ constructor({ content, parser }) {
28
+ this._content = content;
29
+ this._parser = parser;
30
+ this._dom = null;
31
+ }
6
32
 
7
- return this._normalizeTextContent(content);
33
+ normalize() {
34
+ if (typeof this._content !== 'string') {
35
+ return this._content;
36
+ }
37
+ return this._normalizeTextContent();
8
38
  }
9
39
 
10
- _normalizeTextContent(content) {
11
- const parser = new DOMParser();
12
- const dom = parser.parseFromString(content, 'text/html');
40
+ _normalizeTextContent() {
41
+ this._dom = this._parser.parseFromString(this._content.replace(/(\r)?\n/g, ''), 'text/html');
13
42
 
14
- this._iterateNodes(dom, this._normalizeStructure.bind(this), (node) => node.tagName === 'SPAN');
15
- this._iterateNodes(dom, this._normalizeStyles.bind(this), (node) => node.tagName !== 'SPAN');
43
+ this._iterateNodes(this._normalizeBreakLines, (node) => node.tagName === 'BR');
44
+ this._iterateNodes(this._removeEmptyNodes, this._isBlockNode);
45
+ this._iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
46
+ this._iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === 'SPAN');
47
+ this._iterateNodes(this._normalizeStyles, (node) => node.tagName !== 'SPAN');
16
48
 
17
- return dom.body.innerHTML;
49
+ return this._dom.body.innerHTML;
18
50
  }
19
51
 
20
- _iterateNodes(dom, handler, condition) {
21
- const iterator = dom.createNodeIterator(dom.body, NodeFilter.SHOW_ELEMENT, {
22
- acceptNode: (node) => condition(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
52
+ _iterateNodes(handler, condition = () => true) {
53
+ const iterator = this._dom.createNodeIterator(this._dom.body, NodeFilter.SHOW_ELEMENT, {
54
+ acceptNode: (node) => condition.call(this, node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
23
55
  });
24
56
  let currentNode = iterator.nextNode();
25
57
 
26
58
  while (currentNode) {
27
- handler(currentNode);
59
+ handler.call(this, currentNode);
28
60
  currentNode = iterator.nextNode();
29
61
  }
30
62
  }
31
63
 
32
- _normalizeStructure(element) {
33
- const isTextChildren = Array.from(element.childNodes)
34
- .every((node) => node.nodeType === Node.TEXT_NODE);
64
+ _removeEmptyNodes(node) {
65
+ const html = node.innerHTML.replace(/ /g, '').trim();
35
66
 
36
- if (isTextChildren) return;
67
+ if (!html) node.remove();
68
+ }
69
+
70
+ _normalizeListItems(itemEl) {
71
+ const fragment = new DocumentFragment();
72
+ const children = Array.from(itemEl.childNodes);
73
+ let capturingParagraph;
74
+
75
+ const append = (node) => {
76
+ this._assignElementProperties(node, itemEl, ContentNormalizer.BLOCK_STYLES);
77
+ fragment.append(node);
78
+ };
79
+
80
+ this._assignElementProperties(itemEl, itemEl.parentElement, ContentNormalizer.BLOCK_STYLES);
81
+
82
+ for (const node of children) {
83
+ if (this._isBlockNode(node)) {
84
+ append(node);
85
+ capturingParagraph = null;
86
+ continue;
87
+ }
88
+
89
+ if (!capturingParagraph) {
90
+ capturingParagraph = document.createElement('p');
91
+ append(capturingParagraph);
92
+ }
93
+
94
+ capturingParagraph.append(node);
95
+ }
96
+
97
+ itemEl.append(fragment);
98
+ this._removeStyleProperties(itemEl, ContentNormalizer.BLOCK_STYLES);
99
+ }
100
+
101
+ _normalizeSettingsStructure(element) {
102
+ if (this._isOnlyTextContent(element)) return;
37
103
 
38
104
  const cloned = element.cloneNode(true);
39
- const migratingStyles = this._getMigratingStyles(element);
105
+ const migratingStyles = this._getMigratingStyles(element, { customProperties: true });
40
106
  const content = [];
41
107
 
42
108
  for (const node of cloned.childNodes) {
@@ -54,8 +120,12 @@ export class ContentNormalizer {
54
120
  }
55
121
 
56
122
  _normalizeStyles(element) {
123
+ if (!this._isBlockNode(element) && this._isOnlyTextContent(element)) return;
124
+
57
125
  const properties = this._getMigratingStyles(element);
58
126
 
127
+ if (!properties.length) return;
128
+
59
129
  for (const node of element.childNodes) {
60
130
  const child = this._wrapTextNode(element, node);
61
131
 
@@ -65,16 +135,25 @@ export class ContentNormalizer {
65
135
  this._removeStyleProperties(element, properties);
66
136
  }
67
137
 
68
- _getMigratingStyles(element) {
69
- const blacklist = ContentNormalizer.NORMALIZING_STYLE_BLACKLIST;
138
+ _isOnlyTextContent(node) {
139
+ return Array.from(node.childNodes).every((node) => node.nodeType === Node.TEXT_NODE);
140
+ }
141
+
142
+ _isBlockNode(node) {
143
+ return ContentNormalizer.BLOCK_NODE_NAMES.includes(node.tagName);
144
+ }
145
+
146
+ _getMigratingStyles(element, { customProperties } = {}) {
147
+ const blacklist = ContentNormalizer.BLOCK_STYLES;
70
148
  const properties = [];
71
149
 
72
150
  for (let index = 0; index < element.style.length; index++) {
73
151
  const property = element.style.item(index);
74
152
 
75
- if (!blacklist.includes(property)) {
76
- properties.push(property);
77
- }
153
+ if (blacklist.includes(property)) continue;
154
+ if (!customProperties && property.startsWith('--')) continue;
155
+
156
+ properties.push(property);
78
157
  }
79
158
 
80
159
  return properties;
@@ -93,14 +172,24 @@ export class ContentNormalizer {
93
172
 
94
173
  _assignElementProperties(target, source, properties) {
95
174
  for (const property of properties) {
96
- if (target.style.getPropertyValue(property)) {
97
- continue;
98
- }
175
+ const value = source.style.getPropertyValue(property);
99
176
 
100
- target.style.setProperty(property, source.style.getPropertyValue(property));
177
+ if (value && this._canAssignElementProperty(target, source, property)) {
178
+ target.style.setProperty(property, value);
179
+ }
101
180
  }
102
181
  }
103
182
 
183
+ _canAssignElementProperty(target, source, property) {
184
+ if (target.style.getPropertyValue(property)) return false;
185
+
186
+ return ContentNormalizer.ASSIGN_STYLE_RULES.every((rule) => {
187
+ if (!rule.tag.test(target.tagName.toLowerCase())) return true;
188
+
189
+ return !rule.ignore.test(property);
190
+ });
191
+ }
192
+
104
193
  _removeStyleProperties(element, properties) {
105
194
  for (const property of properties) {
106
195
  element.style.removeProperty(property);
@@ -110,4 +199,31 @@ export class ContentNormalizer {
110
199
  element.removeAttribute('style');
111
200
  }
112
201
  }
202
+
203
+ _normalizeBreakLines({ parentElement }) {
204
+ if (!this._isBlockNode(parentElement)) return;
205
+ if (!parentElement.textContent) return;
206
+
207
+ const fragment = new DocumentFragment();
208
+ const children = Array.from(parentElement.childNodes);
209
+ let capturingParagraph = document.createElement('p');
210
+
211
+ const append = (node) => {
212
+ this._assignElementProperties(node, parentElement, ContentNormalizer.BLOCK_STYLES);
213
+ fragment.append(node);
214
+ };
215
+
216
+ for (const child of children) {
217
+ if (child.tagName === 'BR') {
218
+ append(capturingParagraph);
219
+ capturingParagraph = document.createElement('p');
220
+ continue;
221
+ }
222
+
223
+ capturingParagraph.append(child);
224
+ }
225
+
226
+ fragment.append(capturingParagraph);
227
+ parentElement.replaceWith(fragment);
228
+ }
113
229
  }
@@ -0,0 +1,23 @@
1
+ export class ContextWindow {
2
+ static window = window;
3
+
4
+ static use(window) {
5
+ this.window = window;
6
+ }
7
+
8
+ static get document() {
9
+ return this.window.document;
10
+ }
11
+
12
+ static get body() {
13
+ return this.document.body;
14
+ }
15
+
16
+ static get head() {
17
+ return this.document.head;
18
+ }
19
+
20
+ static getComputedStyle(element) {
21
+ return this.window.getComputedStyle(element);
22
+ }
23
+ }
@@ -1,4 +1,4 @@
1
- import { NodeTypes } from '../../enums';
1
+ import { NodeTypes } from '../enums';
2
2
 
3
3
  export class NodeFactory {
4
4
  static doc(content) {
@@ -64,4 +64,8 @@ export class NodeFactory {
64
64
  static mark(type, attrs) {
65
65
  return { type, attrs };
66
66
  }
67
+
68
+ static populateAllDevices(value) {
69
+ return { mobile: value, tablet: value, desktop: value };
70
+ }
67
71
  }
@@ -1,13 +1,11 @@
1
1
  import { ContentNormalizer } from '../ContentNormalizer';
2
- import { NodeFactory } from '../../__tests__/utils';
2
+ import { NodeFactory } from '../NodeFactory';
3
3
 
4
- const normalize = (content) => new ContentNormalizer().normalize(content);
5
-
6
- describe('normalize text settings', () => {
4
+ describe('normalize text content', () => {
7
5
  test('should ignore json content', () => {
8
6
  const content = NodeFactory.doc([NodeFactory.paragraph('Test')]);
9
7
 
10
- expect(normalize(content)).toBe(content);
8
+ expect(ContentNormalizer.normalize(content)).toBe(content);
11
9
  });
12
10
 
13
11
  test('should flat structure', () => {
@@ -18,14 +16,14 @@ describe('normalize text settings', () => {
18
16
  '<span style="background-color: rgb(255, 0, 0); color: rgb(255, 255, 255);">sum</span>' +
19
17
  '</p>';
20
18
 
21
- expect(normalize(input)).toBe(output);
19
+ expect(ContentNormalizer.normalize(input)).toBe(output);
22
20
  });
23
21
 
24
22
  test('should move styles from paragraph to text', () => {
25
23
  const input = '<p style="background-color: red;">lorem ipsum</p>';
26
24
  const output = '<p><span style="background-color: red;">lorem ipsum</span></p>';
27
25
 
28
- expect(normalize(input)).toBe(output);
26
+ expect(ContentNormalizer.normalize(input)).toBe(output);
29
27
  });
30
28
 
31
29
  test('should move styles from paragraph to unstyled text', () => {
@@ -36,6 +34,124 @@ describe('normalize text settings', () => {
36
34
  '<span style="color: white; background-color: red;">one</span' +
37
35
  '></p>';
38
36
 
39
- expect(normalize(input)).toBe(output);
37
+ expect(ContentNormalizer.normalize(input)).toBe(output);
38
+ });
39
+
40
+ test('should wrap list content in paragraph', () => {
41
+ const input = '<ul><li style="line-height: 2;">lorem impsum</li></ul>';
42
+ const output = '<ul><li><p style="line-height: 2;">lorem impsum</p></li></ul>';
43
+
44
+ expect(ContentNormalizer.normalize(input)).toBe(output);
45
+ });
46
+
47
+ test('should keep order in list nodes', () => {
48
+ const input = '<ul><li style="line-height: 2;"><span style="font-weight: 700">lorem</span> impsum</li></ul>';
49
+ const output = '<ul>' +
50
+ '<li>' +
51
+ '<p style="line-height: 2;"><span style="font-weight: 700">lorem</span> impsum</p>' +
52
+ '</li>' +
53
+ '</ul>';
54
+
55
+ expect(ContentNormalizer.normalize(input)).toBe(output);
56
+ });
57
+
58
+ test('should wrap non paragraph list content', () => {
59
+ const input = '<ul>' +
60
+ '<li style="line-height: 2;">' +
61
+ '<span style="font-weight: 700">lorem</span> impsum' +
62
+ '<p>paragraph text</p>' +
63
+ '</li>' +
64
+ '</ul>';
65
+ const output = '<ul>' +
66
+ '<li>' +
67
+ '<p style="line-height: 2;"><span style="font-weight: 700">lorem</span> impsum</p>' +
68
+ '<p style="line-height: 2;">paragraph text</p>' +
69
+ '</li>' +
70
+ '</ul>';
71
+
72
+ expect(ContentNormalizer.normalize(input)).toBe(output);
73
+ });
74
+
75
+ test('should allow using heading in list', () => {
76
+ const input = '<ul><li><h2>lorem ipsum</h2></li></ul>';
77
+
78
+ expect(ContentNormalizer.normalize(input)).toBe(input);
79
+ });
80
+
81
+ test('should ignore empty nodes', () => {
82
+ const input = '<p>lorem ipsum 1</p><p></p><p>lorem ipsum 2</p>';
83
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
84
+
85
+ expect(ContentNormalizer.normalize(input)).toBe(output);
86
+ });
87
+
88
+ test('should ignore space only nodes', () => {
89
+ const input = '<p>lorem ipsum 1</p><p> </p><p>lorem ipsum 2</p>';
90
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
91
+
92
+ expect(ContentNormalizer.normalize(input)).toBe(output);
93
+ });
94
+
95
+ test('should ignore non-breaking space only nodes', () => {
96
+ const input = '<p>lorem ipsum 1</p><p>&nbsp;</p><p>lorem ipsum 2</p>';
97
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
98
+
99
+ expect(ContentNormalizer.normalize(input)).toBe(output);
100
+ });
101
+
102
+ test('should ignore newline chapters only nodes', () => {
103
+ const input = '<p>lorem ipsum 1</p><p>\n</p><p>lorem ipsum 2</p>';
104
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
105
+
106
+ expect(ContentNormalizer.normalize(input)).toBe(output);
107
+ });
108
+
109
+ test('should not ignore setting', () => {
110
+ const input = '<p style="text-decoration-line: underline;">lorem ipsum</p>';
111
+ const output = '<p><span style="text-decoration-line: underline;">lorem ipsum</span></p>';
112
+
113
+ expect(ContentNormalizer.normalize(input)).toBe(output);
114
+ });
115
+
116
+ test('should not assign font-weight to b tag', () => {
117
+ const input = '<p style="font-weight: 400;"><b>lorem ipsum</b></p>';
118
+ const output = '<p><b>lorem ipsum</b></p>';
119
+
120
+ expect(ContentNormalizer.normalize(input)).toBe(output);
121
+ });
122
+
123
+ test('should not assign font-style to i tag', () => {
124
+ const input = '<p style="font-style: normal;"><i>lorem ipsum</i></p>';
125
+ const output = '<p><i>lorem ipsum</i></p>';
126
+
127
+ expect(ContentNormalizer.normalize(input)).toBe(output);
128
+ });
129
+
130
+ test('should not assign text-decoration to s tag', () => {
131
+ const input = '<p style="text-decoration-line: initial;"><s>lorem ipsum</s></p>';
132
+ const output = '<p><s>lorem ipsum</s></p>';
133
+
134
+ expect(ContentNormalizer.normalize(input)).toBe(output);
135
+ });
136
+
137
+ test('should assign block styles from list to paragraph', () => {
138
+ const input = '<ul style="line-height: 2;"><li>lorem ipsum</li></ul>';
139
+ const output = '<ul style="line-height: 2;"><li><p style="line-height: 2;">lorem ipsum</p></li></ul>';
140
+
141
+ expect(ContentNormalizer.normalize(input)).toBe(output);
142
+ });
143
+
144
+ test('should migrate br to paragraphs', () => {
145
+ const input = '<p><b>lorem</b><br>ipsum</p>';
146
+ const output = '<p><b>lorem</b></p><p>ipsum</p>';
147
+
148
+ expect(ContentNormalizer.normalize(input)).toBe(output);
149
+ });
150
+
151
+ test('should not migrate single br to paragraph', () => {
152
+ const input = '<p><br></p>';
153
+ const output = '<p><br></p>';
154
+
155
+ expect(ContentNormalizer.normalize(input)).toBe(output);
40
156
  });
41
157
  });
@@ -1,4 +1,4 @@
1
- import { ref } from '@vue/composition-api';
1
+ import { ref } from 'vue';
2
2
  import { FavoriteColors } from '../FavoriteColors';
3
3
 
4
4
  function createService({ colors, onUpdate }) {
@@ -2,3 +2,5 @@ export { JsonSerializer } from './JsonSerializer';
2
2
  export { Storage } from './Storage';
3
3
  export { FavoriteColors } from './FavoriteColors';
4
4
  export { ContentNormalizer } from './ContentNormalizer';
5
+ export { ContextWindow } from './ContextWidnow';
6
+ export { NodeFactory } from './NodeFactory';