@zipify/wysiwyg 1.0.0-dev.5 → 1.0.0-dev.50

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 (237) hide show
  1. package/.editorconfig +1 -1
  2. package/.eslintignore +1 -0
  3. package/.eslintrc.js +2 -2
  4. package/.github/dependabot.yaml +1 -0
  5. package/.lintstagedrc +2 -2
  6. package/.release-it.json +3 -1
  7. package/.stylelintignore +1 -0
  8. package/.stylelintrc +0 -4
  9. package/README.md +2 -2
  10. package/config/jest/setupTests.js +4 -3
  11. package/config/vite/example.config.js +25 -0
  12. package/config/vite/lib.config.js +30 -0
  13. package/config/{webpack → vite}/settings.js +0 -0
  14. package/dist/wysiwyg.css +1 -0
  15. package/dist/wysiwyg.mjs +18305 -0
  16. package/dist/wysiwyg.mjs.map +1 -0
  17. package/example/ExampleApp.vue +21 -2
  18. package/example/example.js +0 -3
  19. package/example/{example.html → index.html} +1 -0
  20. package/example/pageBlocks.js +31 -0
  21. package/example/presets.js +2 -2
  22. package/jest.config.js +3 -1
  23. package/lib/Wysiwyg.vue +50 -25
  24. package/lib/__tests__/utils/NodeFactory.js +13 -0
  25. package/lib/__tests__/utils/withComponentContext.js +1 -1
  26. package/lib/assets/icons/alignment-center.svg +3 -0
  27. package/lib/assets/icons/alignment-justify.svg +3 -0
  28. package/lib/assets/icons/alignment-left.svg +3 -0
  29. package/lib/assets/icons/alignment-right.svg +3 -0
  30. package/lib/assets/icons/arrow.svg +3 -0
  31. package/lib/assets/icons/background-color.svg +3 -0
  32. package/lib/assets/icons/case-style.svg +3 -0
  33. package/lib/assets/icons/font-color.svg +5 -0
  34. package/lib/assets/icons/italic.svg +3 -0
  35. package/lib/assets/icons/line-height.svg +3 -0
  36. package/lib/assets/icons/link.svg +3 -0
  37. package/lib/assets/icons/list-circle.svg +3 -0
  38. package/lib/assets/icons/list-decimal.svg +3 -0
  39. package/lib/assets/icons/list-disc.svg +3 -0
  40. package/lib/assets/icons/list-latin.svg +3 -0
  41. package/lib/assets/icons/list-roman.svg +3 -0
  42. package/lib/assets/icons/list-square.svg +3 -0
  43. package/lib/assets/icons/remove-format.svg +3 -0
  44. package/lib/assets/icons/reset-styles.svg +3 -0
  45. package/lib/assets/icons/strike-through.svg +3 -0
  46. package/lib/assets/icons/superscript.svg +3 -0
  47. package/lib/assets/icons/underline.svg +3 -0
  48. package/lib/assets/icons/unlink.svg +3 -0
  49. package/lib/components/base/Button.vue +22 -2
  50. package/lib/components/base/Checkbox.vue +89 -0
  51. package/lib/components/base/FieldLabel.vue +2 -1
  52. package/lib/components/base/Icon.vue +19 -11
  53. package/lib/components/base/Modal.vue +1 -2
  54. package/lib/components/base/NumberField.vue +2 -2
  55. package/lib/components/base/Range.vue +1 -1
  56. package/lib/components/base/ScrollView.vue +1 -3
  57. package/lib/components/base/TextField.vue +106 -0
  58. package/lib/components/base/__tests__/Icon.test.js +6 -13
  59. package/lib/components/base/__tests__/Modal.test.js +7 -2
  60. package/lib/components/base/__tests__/TextField.test.js +57 -0
  61. package/lib/components/base/__tests__/__snapshots__/TextField.test.js.snap +9 -0
  62. package/lib/components/base/colorPicker/ColorPicker.vue +2 -2
  63. package/lib/components/base/colorPicker/composables/__tests__/usePickerApi.test.js +1 -1
  64. package/lib/components/base/colorPicker/composables/usePickerApi.js +1 -1
  65. package/lib/components/base/colorPicker/composables/usePickerHotkeys.js +3 -2
  66. package/lib/components/base/composables/__tests__/useActivatedListener.test.js +1 -1
  67. package/lib/components/base/composables/__tests__/useDeselectionLock.test.js +1 -1
  68. package/lib/components/base/composables/__tests__/useElementRef.test.js +1 -1
  69. package/lib/components/base/composables/__tests__/useModalToggler.test.js +1 -1
  70. package/lib/components/base/composables/__tests__/useNumberValue.test.js +1 -1
  71. package/lib/components/base/composables/__tests__/useScrollView.test.js +1 -1
  72. package/lib/components/base/composables/__tests__/useTempValue.test.js +1 -1
  73. package/lib/components/base/composables/__tests__/useValidator.test.js +44 -0
  74. package/lib/components/base/composables/index.js +1 -0
  75. package/lib/components/base/composables/useActivatedListener.js +1 -1
  76. package/lib/components/base/composables/useDeselectionLock.js +1 -1
  77. package/lib/components/base/composables/useElementRef.js +1 -1
  78. package/lib/components/base/composables/useModalToggler.js +1 -1
  79. package/lib/components/base/composables/useScrollView.js +1 -1
  80. package/lib/components/base/composables/useTempValue.js +1 -1
  81. package/lib/components/base/composables/useValidator.js +23 -0
  82. package/lib/components/base/dropdown/Dropdown.vue +16 -4
  83. package/lib/components/base/dropdown/DropdownActivator.vue +19 -3
  84. package/lib/components/base/dropdown/DropdownGroup.vue +1 -1
  85. package/lib/components/base/dropdown/DropdownMenu.vue +1 -1
  86. package/lib/components/base/dropdown/DropdownOption.vue +1 -1
  87. package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +1 -1
  88. package/lib/components/base/dropdown/__tests__/DropdownMenu.test.js +1 -1
  89. package/lib/components/base/dropdown/__tests__/DropdownOption.test.js +1 -1
  90. package/lib/components/base/dropdown/composables/__tests__/useActiveOptionManager.test.js +1 -1
  91. package/lib/components/base/dropdown/composables/__tests__/useDropdownEntityTitle.test.js +1 -1
  92. package/lib/components/base/dropdown/composables/useActiveOptionManager.js +1 -1
  93. package/lib/components/base/dropdown/composables/useDropdownEntityTitle.js +1 -1
  94. package/lib/components/base/index.js +3 -1
  95. package/lib/components/toolbar/Toolbar.vue +49 -9
  96. package/lib/components/toolbar/ToolbarDivider.vue +1 -1
  97. package/lib/components/toolbar/ToolbarFull.vue +10 -2
  98. package/lib/components/toolbar/__tests__/Toolbar.test.js +6 -0
  99. package/lib/components/toolbar/controls/AlignmentControl.vue +1 -1
  100. package/lib/components/toolbar/controls/AlignmentDeviceControl.vue +1 -1
  101. package/lib/components/toolbar/controls/BackgroundColorControl.vue +1 -1
  102. package/lib/components/toolbar/controls/CaseStyleControl.vue +1 -1
  103. package/lib/components/toolbar/controls/FontColorControl.vue +1 -1
  104. package/lib/components/toolbar/controls/FontFamilyControl.vue +1 -1
  105. package/lib/components/toolbar/controls/FontSizeControl.vue +8 -1
  106. package/lib/components/toolbar/controls/FontWeightControl.vue +1 -1
  107. package/lib/components/toolbar/controls/ItalicControl.vue +1 -1
  108. package/lib/components/toolbar/controls/LineHeightControl.vue +1 -1
  109. package/lib/components/toolbar/controls/ListControl.vue +2 -6
  110. package/lib/components/toolbar/controls/RemoveFormatControl.vue +1 -1
  111. package/lib/components/toolbar/controls/StrikeThroughControl.vue +1 -1
  112. package/lib/components/toolbar/controls/StylePresetControl.vue +15 -2
  113. package/lib/components/toolbar/controls/SuperscriptControl.vue +1 -1
  114. package/lib/components/toolbar/controls/UnderlineControl.vue +2 -2
  115. package/lib/components/toolbar/controls/__tests__/AlignmentControl.test.js +1 -1
  116. package/lib/components/toolbar/controls/__tests__/AlignmentDeviceControl.test.js +1 -1
  117. package/lib/components/toolbar/controls/__tests__/BackgroundColorControl.test.js +1 -1
  118. package/lib/components/toolbar/controls/__tests__/CaseStyleControl.test.js +1 -1
  119. package/lib/components/toolbar/controls/__tests__/FontColorControl.test.js +1 -1
  120. package/lib/components/toolbar/controls/__tests__/FontFamilyControl.test.js +1 -1
  121. package/lib/components/toolbar/controls/__tests__/FontSizeControl.test.js +1 -1
  122. package/lib/components/toolbar/controls/__tests__/FontWeightControl.test.js +1 -1
  123. package/lib/components/toolbar/controls/__tests__/ItalicControl.test.js +1 -1
  124. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +1 -1
  125. package/lib/components/toolbar/controls/__tests__/ListControl.test.js +1 -1
  126. package/lib/components/toolbar/controls/__tests__/StrikeThroughControl.test.js +1 -1
  127. package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +17 -1
  128. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +5 -1
  129. package/lib/components/toolbar/controls/composables/useRecentFonts.js +1 -1
  130. package/lib/components/toolbar/controls/index.js +1 -0
  131. package/lib/components/toolbar/controls/link/LinkControl.vue +155 -0
  132. package/lib/components/toolbar/controls/link/LinkControlApply.vue +35 -0
  133. package/lib/components/toolbar/controls/link/LinkControlHeader.vue +67 -0
  134. package/lib/components/toolbar/controls/link/__tests__/LinkControl.test.js +79 -0
  135. package/lib/components/toolbar/controls/link/__tests__/LinkControlHeader.test.js +42 -0
  136. package/lib/components/toolbar/controls/link/composables/__tests__/__snapshots__/useLink.test.js.snap +8 -0
  137. package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +114 -0
  138. package/lib/components/toolbar/controls/link/composables/index.js +1 -0
  139. package/lib/components/toolbar/controls/link/composables/useLink.js +61 -0
  140. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +103 -0
  141. package/lib/components/toolbar/controls/link/destination/LinkControlPageBlock.vue +54 -0
  142. package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +52 -0
  143. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlPageBlock.test.js +36 -0
  144. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlUrl.test.js +46 -0
  145. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlPageBlock.test.js.snap +9 -0
  146. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlUrl.test.js.snap +17 -0
  147. package/lib/components/toolbar/controls/link/destination/index.js +1 -0
  148. package/lib/components/toolbar/controls/link/index.js +1 -0
  149. package/lib/composables/__tests__/useEditor.test.js +3 -3
  150. package/lib/composables/useEditor.js +6 -8
  151. package/lib/composables/useToolbar.js +23 -28
  152. package/lib/directives/__tests__/outClick.test.js +6 -0
  153. package/lib/directives/outClick.js +19 -6
  154. package/lib/enums/Alignments.js +10 -1
  155. package/lib/enums/LinkDestinations.js +4 -0
  156. package/lib/enums/LinkTargets.js +4 -0
  157. package/lib/enums/TextSettings.js +3 -1
  158. package/lib/enums/index.js +2 -0
  159. package/lib/extensions/Alignment.js +22 -8
  160. package/lib/extensions/BackgroundColor.js +15 -7
  161. package/lib/extensions/DeviceManager.js +2 -5
  162. package/lib/extensions/FontColor.js +15 -7
  163. package/lib/extensions/FontFamily.js +26 -9
  164. package/lib/extensions/FontSize.js +32 -13
  165. package/lib/extensions/FontStyle.js +24 -14
  166. package/lib/extensions/FontWeight.js +25 -15
  167. package/lib/extensions/LineHeight.js +32 -29
  168. package/lib/extensions/Link.js +89 -0
  169. package/lib/extensions/StylePreset.js +20 -19
  170. package/lib/extensions/TextDecoration.js +46 -13
  171. package/lib/extensions/__tests__/Alignment.test.js +13 -7
  172. package/lib/extensions/__tests__/BackgroundColor.test.js +12 -6
  173. package/lib/extensions/__tests__/CaseStyle.test.js +3 -5
  174. package/lib/extensions/__tests__/FontColor.test.js +12 -6
  175. package/lib/extensions/__tests__/FontFamily.test.js +33 -8
  176. package/lib/extensions/__tests__/FontSize.test.js +15 -8
  177. package/lib/extensions/__tests__/FontStyle.test.js +12 -6
  178. package/lib/extensions/__tests__/FontWeight.test.js +20 -6
  179. package/lib/extensions/__tests__/LineHeight.test.js +24 -12
  180. package/lib/extensions/__tests__/Link.test.js +102 -0
  181. package/lib/extensions/__tests__/StylePreset.test.js +71 -8
  182. package/lib/extensions/__tests__/TextDecoration.test.js +52 -6
  183. package/lib/extensions/__tests__/__snapshots__/Alignment.test.js.snap +26 -2
  184. package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +30 -1
  185. package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +18 -1
  186. package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +88 -1
  187. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +33 -2
  188. package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +25 -4
  189. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +47 -1
  190. package/lib/extensions/__tests__/__snapshots__/LineHeight.test.js.snap +26 -2
  191. package/lib/extensions/__tests__/__snapshots__/Link.test.js.snap +225 -0
  192. package/lib/extensions/__tests__/__snapshots__/StylePreset.test.js.snap +6 -2
  193. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +183 -3
  194. package/lib/extensions/core/CopyPasteProcessor.js +10 -0
  195. package/lib/extensions/core/NodeProcessor.js +1 -1
  196. package/lib/extensions/core/TextProcessor.js +10 -0
  197. package/lib/extensions/core/__tests__/NodeProcessor.test.js +3 -5
  198. package/lib/extensions/core/__tests__/SelectionProcessor.test.js +3 -5
  199. package/lib/extensions/core/__tests__/TextProcessor.test.js +138 -12
  200. package/lib/extensions/core/__tests__/__snapshots__/TextProcessor.test.js.snap +26 -0
  201. package/lib/extensions/core/index.js +11 -2
  202. package/lib/extensions/core/plugins/PastePlugin.js +57 -0
  203. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +20 -0
  204. package/lib/extensions/core/plugins/index.js +1 -0
  205. package/lib/extensions/index.js +46 -34
  206. package/lib/extensions/list/List.js +1 -1
  207. package/lib/extensions/list/__tests__/List.test.js +5 -8
  208. package/lib/extensions/list/__tests__/__snapshots__/List.test.js.snap +45 -15
  209. package/lib/injectionTokens.js +2 -1
  210. package/lib/services/ContentNormalizer.js +113 -29
  211. package/lib/services/ContextWidnow.js +23 -0
  212. package/lib/services/__tests__/ContentNormalizer.test.js +75 -7
  213. package/lib/services/__tests__/FavoriteColors.test.js +1 -1
  214. package/lib/services/index.js +1 -0
  215. package/lib/styles/content.css +102 -13
  216. package/lib/styles/helpers/offsets.css +16 -0
  217. package/lib/styles/variables.css +6 -0
  218. package/lib/utils/__tests__/__snapshots__/renderInlineSetting.test.js.snap +4 -4
  219. package/lib/utils/__tests__/convertAlignment.test.js +16 -0
  220. package/lib/utils/__tests__/convertFontSize.test.js +21 -0
  221. package/lib/utils/__tests__/convertLineHeight.test.js +21 -0
  222. package/lib/utils/convertAlignment.js +12 -0
  223. package/lib/utils/convertColor.js +1 -1
  224. package/lib/utils/convertFontSize.js +8 -0
  225. package/lib/utils/convertLineHeight.js +17 -0
  226. package/lib/utils/importIcon.js +13 -0
  227. package/lib/utils/index.js +4 -0
  228. package/lib/utils/renderInlineSetting.js +1 -1
  229. package/package.json +23 -28
  230. package/config/webpack/example.config.js +0 -86
  231. package/config/webpack/loaders/index.js +0 -6
  232. package/config/webpack/loaders/js-loader.js +0 -5
  233. package/config/webpack/loaders/style-loader.js +0 -7
  234. package/config/webpack/loaders/svg-loader.js +0 -4
  235. package/config/webpack/loaders/vue-loader.js +0 -4
  236. package/lib/assets/icons.svg +0 -69
  237. package/lib/composables/__tests__/useToolbar.test.js +0 -56
@@ -1,42 +1,103 @@
1
+ import { ContextWindow } from './ContextWidnow';
2
+
1
3
  export class ContentNormalizer {
2
- static NORMALIZING_STYLE_BLACKLIST = ['text-align', 'line-height'];
4
+ static BLOCK_STYLES = ['text-align', 'line-height'];
5
+ static BLOCK_NODE_NAMES = ['P', 'H1', 'H2', 'H3', 'H4'];
6
+
7
+ static ASSIGN_STYLE_RULES = [
8
+ {
9
+ tag: /^(b|strong)$/,
10
+ ignore: /font-weight/
11
+ },
12
+ {
13
+ tag: /^i$/,
14
+ ignore: /font-style/
15
+ },
16
+ {
17
+ tag: /^s$/,
18
+ ignore: /text-decoration(.+)?/
19
+ }
20
+ ];
3
21
 
4
- normalize(content) {
5
- if (typeof content !== 'string') return content;
22
+ static normalize(content) {
23
+ return new ContentNormalizer(content).normalize();
24
+ }
6
25
 
7
- return this._normalizeTextContent(content);
26
+ constructor(content) {
27
+ this._content = content;
28
+ this._dom = null;
8
29
  }
9
30
 
10
- _normalizeTextContent(content) {
11
- const parser = new DOMParser();
12
- const dom = parser.parseFromString(content, 'text/html');
31
+ normalize() {
32
+ if (typeof this._content !== 'string') {
33
+ return this._content;
34
+ }
35
+ return this._normalizeTextContent();
36
+ }
13
37
 
14
- this._iterateNodes(dom, this._normalizeStructure.bind(this), (node) => node.tagName === 'SPAN');
15
- this._iterateNodes(dom, this._normalizeStyles.bind(this), (node) => node.tagName !== 'SPAN');
38
+ _normalizeTextContent() {
39
+ this._dom = new DOMParser().parseFromString(this._content, 'text/html');
16
40
 
17
- return dom.body.innerHTML;
41
+ this._iterateNodes(this._removeEmptyNodes, this._isBlockNode);
42
+ this._iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
43
+ this._iterateNodes(this._normalizeSettingsStructure, (node) => node.tagName === 'SPAN');
44
+ this._iterateNodes(this._normalizeStyles, (node) => node.tagName !== 'SPAN');
45
+
46
+ return this._dom.body.innerHTML;
18
47
  }
19
48
 
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
49
+ _iterateNodes(handler, condition = () => true) {
50
+ const iterator = this._dom.createNodeIterator(this._dom.body, NodeFilter.SHOW_ELEMENT, {
51
+ acceptNode: (node) => condition.call(this, node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
23
52
  });
24
53
  let currentNode = iterator.nextNode();
25
54
 
26
55
  while (currentNode) {
27
- handler(currentNode);
56
+ handler.call(this, currentNode);
28
57
  currentNode = iterator.nextNode();
29
58
  }
30
59
  }
31
60
 
32
- _normalizeStructure(element) {
33
- const isTextChildren = Array.from(element.childNodes)
34
- .every((node) => node.nodeType === Node.TEXT_NODE);
61
+ _removeEmptyNodes(node) {
62
+ if (node.childNodes.length === 0) {
63
+ node.remove();
64
+ }
65
+ }
66
+
67
+ _normalizeListItems(itemEl) {
68
+ const fragment = new DocumentFragment();
69
+ const children = Array.from(itemEl.childNodes);
70
+ let capturingParagraph;
35
71
 
36
- if (isTextChildren) return;
72
+ const append = (node) => {
73
+ this._assignElementProperties(node, itemEl, ContentNormalizer.BLOCK_STYLES);
74
+ fragment.append(node);
75
+ };
76
+
77
+ for (const node of children) {
78
+ if (node.tagName === 'P') {
79
+ append(node);
80
+ capturingParagraph = null;
81
+ continue;
82
+ }
83
+
84
+ if (!capturingParagraph) {
85
+ capturingParagraph = document.createElement('p');
86
+ append(capturingParagraph);
87
+ }
88
+
89
+ capturingParagraph.append(node);
90
+ }
91
+
92
+ itemEl.append(fragment);
93
+ this._removeStyleProperties(itemEl, ContentNormalizer.BLOCK_STYLES);
94
+ }
95
+
96
+ _normalizeSettingsStructure(element) {
97
+ if (this._isOnlyTextContent(element)) return;
37
98
 
38
99
  const cloned = element.cloneNode(true);
39
- const migratingStyles = this._getMigratingStyles(element);
100
+ const migratingStyles = this._getMigratingStyles(element, { customProperties: true });
40
101
  const content = [];
41
102
 
42
103
  for (const node of cloned.childNodes) {
@@ -54,8 +115,12 @@ export class ContentNormalizer {
54
115
  }
55
116
 
56
117
  _normalizeStyles(element) {
118
+ if (!this._isBlockNode(element) && this._isOnlyTextContent(element)) return;
119
+
57
120
  const properties = this._getMigratingStyles(element);
58
121
 
122
+ if (!properties.length) return;
123
+
59
124
  for (const node of element.childNodes) {
60
125
  const child = this._wrapTextNode(element, node);
61
126
 
@@ -65,16 +130,25 @@ export class ContentNormalizer {
65
130
  this._removeStyleProperties(element, properties);
66
131
  }
67
132
 
68
- _getMigratingStyles(element) {
69
- const blacklist = ContentNormalizer.NORMALIZING_STYLE_BLACKLIST;
133
+ _isOnlyTextContent(node) {
134
+ return Array.from(node.childNodes).every((node) => node.nodeType === Node.TEXT_NODE);
135
+ }
136
+
137
+ _isBlockNode(node) {
138
+ return ContentNormalizer.BLOCK_NODE_NAMES.includes(node.tagName);
139
+ }
140
+
141
+ _getMigratingStyles(element, { customProperties } = {}) {
142
+ const blacklist = ContentNormalizer.BLOCK_STYLES;
70
143
  const properties = [];
71
144
 
72
145
  for (let index = 0; index < element.style.length; index++) {
73
146
  const property = element.style.item(index);
74
147
 
75
- if (!blacklist.includes(property)) {
76
- properties.push(property);
77
- }
148
+ if (blacklist.includes(property)) continue;
149
+ if (!customProperties && property.startsWith('--')) continue;
150
+
151
+ properties.push(property);
78
152
  }
79
153
 
80
154
  return properties;
@@ -83,7 +157,7 @@ export class ContentNormalizer {
83
157
  _wrapTextNode(parent, node) {
84
158
  if (node.nodeType !== Node.TEXT_NODE) return node;
85
159
 
86
- const span = document.createElement('span');
160
+ const span = ContextWindow.document.createElement('span');
87
161
 
88
162
  span.append(node.cloneNode());
89
163
  parent.replaceChild(span, node);
@@ -93,14 +167,24 @@ export class ContentNormalizer {
93
167
 
94
168
  _assignElementProperties(target, source, properties) {
95
169
  for (const property of properties) {
96
- if (target.style.getPropertyValue(property)) {
97
- continue;
98
- }
170
+ const value = source.style.getPropertyValue(property);
99
171
 
100
- target.style.setProperty(property, source.style.getPropertyValue(property));
172
+ if (value && this._canAssignElementProperty(target, source, property)) {
173
+ target.style.setProperty(property, value);
174
+ }
101
175
  }
102
176
  }
103
177
 
178
+ _canAssignElementProperty(target, source, property) {
179
+ if (target.style.getPropertyValue(property)) return false;
180
+
181
+ return ContentNormalizer.ASSIGN_STYLE_RULES.every((rule) => {
182
+ if (!rule.tag.test(target.tagName.toLowerCase())) return true;
183
+
184
+ return !rule.ignore.test(property);
185
+ });
186
+ }
187
+
104
188
  _removeStyleProperties(element, properties) {
105
189
  for (const property of properties) {
106
190
  element.style.removeProperty(property);
@@ -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,13 +1,11 @@
1
1
  import { ContentNormalizer } from '../ContentNormalizer';
2
2
  import { NodeFactory } from '../../__tests__/utils';
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,76 @@ 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 ignore empty nodes', () => {
76
+ const input = '<p>lorem ipsum 1</p><p></p><p>lorem ipsum 2</p>';
77
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
78
+
79
+ expect(ContentNormalizer.normalize(input)).toBe(output);
80
+ });
81
+
82
+ test('should not ignore setting', () => {
83
+ const input = '<p style="text-decoration-line: underline;">lorem ipsum</p>';
84
+ const output = '<p><span style="text-decoration-line: underline;">lorem ipsum</span></p>';
85
+
86
+ expect(ContentNormalizer.normalize(input)).toBe(output);
87
+ });
88
+
89
+ test('should not assign font-weight to b tag', () => {
90
+ const input = '<p style="font-weight: 400;"><b>lorem ipsum</b></p>';
91
+ const output = '<p><b>lorem ipsum</b></p>';
92
+
93
+ expect(ContentNormalizer.normalize(input)).toBe(output);
94
+ });
95
+
96
+ test('should not assign font-style to i tag', () => {
97
+ const input = '<p style="font-style: normal;"><i>lorem ipsum</i></p>';
98
+ const output = '<p><i>lorem ipsum</i></p>';
99
+
100
+ expect(ContentNormalizer.normalize(input)).toBe(output);
101
+ });
102
+
103
+ test('should not assign text-decoration to s tag', () => {
104
+ const input = '<p style="text-decoration-line: initial;"><s>lorem ipsum</s></p>';
105
+ const output = '<p><s>lorem ipsum</s></p>';
106
+
107
+ expect(ContentNormalizer.normalize(input)).toBe(output);
40
108
  });
41
109
  });
@@ -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,4 @@ 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';
@@ -2,7 +2,15 @@
2
2
  outline: none;
3
3
  }
4
4
 
5
- .zw-style {
5
+ .zw-wysiwyg__placeholder:first-child:last-child::before {
6
+ content: attr(data-placeholder);
7
+ color: rgb(var(--zw-color-n70));
8
+ float: left;
9
+ height: 0;
10
+ pointer-events: none;
11
+ }
12
+
13
+ .zw-style.zw-style.zw-style {
6
14
  font-weight: var(--zw-font-weight, var(--zw-preset-font-weight));
7
15
  font-family: var(--zw-font-family, var(--zw-preset-font-family));
8
16
  color: var(--zw-font-color, var(--zw-preset-color));
@@ -13,27 +21,108 @@
13
21
 
14
22
  @media (min-width: 1200px) {
15
23
 
16
- .zw-style {
17
- font-size: var(--zw-desktop-font-size, var(--zw-preset-desktop-font-size));
18
- text-align: var(--zw-desktop-text-align, var(--zw-preset-desktop-text-align));
19
- line-height: var(--zw-desktop-line-height, var(--zw-preset-desktop-line-height));
24
+ .zw-style.zw-style.zw-style {
25
+ font-size: var(--zw-font-size-desktop, var(--zw-preset-desktop-font-size));
26
+ text-align: var(--zw-text-align-desktop, var(--zw-preset-desktop-text-align));
27
+ line-height: var(--zw-line-height-desktop, var(--zw-preset-desktop-line-height));
20
28
  }
21
29
  }
22
30
 
23
31
  @media (min-width: 769px) and (max-width: 1199.98px) {
24
32
 
25
- .zw-style {
26
- font-size: var(--zw-tablet-font-size, var(--zw-preset-tablet-font-size));
27
- text-align: var(--zw-tablet-text-align, var(--zw-preset-tablet-text-align));
28
- line-height: var(--zw-tablet-line-height, var(--zw-preset-tablet-line-height));
33
+ .zw-style.zw-style.zw-style {
34
+ font-size: var(--zw-font-size-tablet, var(--zw-preset-tablet-font-size));
35
+ text-align: var(--zw-text-align-tablet, var(--zw-preset-tablet-text-align));
36
+ line-height: var(--zw-line-height-tablet, var(--zw-preset-tablet-line-height));
29
37
  }
30
38
  }
31
39
 
32
40
  @media (max-width: 768.98px) {
33
41
 
34
- .zw-style {
35
- font-size: var(--zw-mobile-font-size, var(--zw-preset-mobile-font-size));
36
- text-align: var(--zw-mobile-text-align, var(--zw-preset-mobile-text-align));
37
- line-height: var(--zw-mobile-line-height, var(--zw-preset-mobile-line-height));
42
+ .zw-style.zw-style.zw-style {
43
+ font-size: var(--zw-font-size-mobile, var(--zw-preset-mobile-font-size));
44
+ text-align: var(--zw-text-align-mobile, var(--zw-preset-mobile-text-align));
45
+ line-height: var(--zw-line-height-mobile, var(--zw-preset-mobile-line-height));
38
46
  }
39
47
  }
48
+
49
+
50
+ /* ProseMirror styles */
51
+
52
+
53
+ .ProseMirror {
54
+ position: relative;
55
+ }
56
+
57
+ .ProseMirror {
58
+ word-wrap: break-word;
59
+ white-space: pre-wrap;
60
+ white-space: break-spaces;
61
+ -webkit-font-variant-ligatures: none;
62
+ font-variant-ligatures: none;
63
+ font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
64
+ }
65
+
66
+ .ProseMirror [contenteditable="false"] {
67
+ white-space: normal;
68
+ }
69
+
70
+ .ProseMirror [contenteditable="false"] [contenteditable="true"] {
71
+ white-space: pre-wrap;
72
+ }
73
+
74
+ .ProseMirror pre {
75
+ white-space: pre-wrap;
76
+ }
77
+
78
+ img.ProseMirror-separator {
79
+ display: inline !important;
80
+ border: none !important;
81
+ margin: 0 !important;
82
+ width: 1px !important;
83
+ height: 1px !important;
84
+ }
85
+
86
+ .ProseMirror-gapcursor {
87
+ display: none;
88
+ pointer-events: none;
89
+ position: absolute;
90
+ margin: 0;
91
+ }
92
+
93
+ .ProseMirror-gapcursor::after {
94
+ content: "";
95
+ display: block;
96
+ position: absolute;
97
+ top: -2px;
98
+ width: 20px;
99
+ border-top: 1px solid #000;
100
+ animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
101
+ }
102
+
103
+ @keyframes ProseMirror-cursor-blink {
104
+
105
+ to {
106
+ visibility: hidden;
107
+ }
108
+ }
109
+
110
+ .ProseMirror-hideselection *::selection {
111
+ background: transparent;
112
+ }
113
+
114
+ .ProseMirror-hideselection *::-moz-selection {
115
+ background: transparent;
116
+ }
117
+
118
+ .ProseMirror-hideselection * {
119
+ caret-color: transparent;
120
+ }
121
+
122
+ .ProseMirror-focused .ProseMirror-gapcursor {
123
+ display: block;
124
+ }
125
+
126
+ .tippy-box[data-animation=fade][data-state=hidden] {
127
+ opacity: 0
128
+ }
@@ -1,3 +1,19 @@
1
+ .zw-margin-bottom--xxs {
2
+ margin-bottom: var(--zw-offset-xxs);
3
+ }
4
+
1
5
  .zw-margin-bottom--xs {
2
6
  margin-bottom: var(--zw-offset-xs);
3
7
  }
8
+
9
+ .zw-margin-bottom--sm {
10
+ margin-bottom: var(--zw-offset-sm);
11
+ }
12
+
13
+ .zw-margin-bottom--md {
14
+ margin-bottom: var(--zw-offset-md);
15
+ }
16
+
17
+ .zw-margin-right--xs {
18
+ margin-right: var(--zw-offset-xs);
19
+ }
@@ -8,13 +8,17 @@
8
8
  --zw-color-n80: 196, 196, 196; /* #C4C4C4 */
9
9
  --zw-color-n85: 217, 217, 217; /* #D9D9D9 */
10
10
  --zw-color-n90: 230, 230, 230; /* #E6E6E6 */
11
+ --zw-color-n200: 194, 200, 209; /* #C2C8D1 */
11
12
  --zw-color-black: 0, 0, 0;
12
13
  --zw-color-white: 255, 255, 255;
14
+ --zw-color-green: 59, 180, 74; /* #3BB44A */
15
+ --zw-color-red: 234, 58, 58; /* #EA3A3A */
13
16
 
14
17
  --zw-offset-xxs: 4px;
15
18
  --zw-offset-xs: 8px;
16
19
  --zw-offset-xsm: 12px;
17
20
  --zw-offset-sm: 16px;
21
+ --zw-offset-md: 24px;
18
22
 
19
23
  --zw-font-weight-thin: 400;
20
24
  --zw-font-weight-semibold: 500;
@@ -42,6 +46,8 @@ $builder-N90: #E6E6E6;
42
46
  $builder-N94: #F0F0F0;
43
47
  $builder-N96: #F5F5F5;
44
48
  $builder-N98: #FAFAFA;
49
+ $builder-N200: #C2C8D1;
50
+ $builder-R50: #EA3A3A;
45
51
 
46
52
  $font-size-xxs: 12px;
47
53
  $font-size-xs: 14px;
@@ -5,7 +5,7 @@ Array [
5
5
  "span",
6
6
  Object {
7
7
  "class": "zw-style",
8
- "style": "--zw-mobile-font-size: 12px; --zw-tablet-font-size: 14px; --zw-desktop-font-size: 16px;",
8
+ "style": "--zw-mobile-font-size:12px;--zw-tablet-font-size:14px;--zw-desktop-font-size:16px;",
9
9
  },
10
10
  0,
11
11
  ]
@@ -16,7 +16,7 @@ Array [
16
16
  "span",
17
17
  Object {
18
18
  "class": "zw-style",
19
- "style": "--zw-desktop-font-size: 16px;",
19
+ "style": "--zw-desktop-font-size:16px;",
20
20
  },
21
21
  0,
22
22
  ]
@@ -27,7 +27,7 @@ Array [
27
27
  "span",
28
28
  Object {
29
29
  "class": "zw-style",
30
- "style": "--zw-font-size: 12px;",
30
+ "style": "--zw-font-size:12px;",
31
31
  },
32
32
  0,
33
33
  ]
@@ -35,6 +35,6 @@ Array [
35
35
 
36
36
  exports[`render settings should render unwrapped styles 1`] = `
37
37
  Object {
38
- "style": "--zw-desktop-font-size: 16px;",
38
+ "style": "--zw-desktop-font-size:16px;",
39
39
  }
40
40
  `;
@@ -0,0 +1,16 @@
1
+ import { convertAlignment } from '../convertAlignment';
2
+ import { Alignments } from '../../enums';
3
+
4
+ describe('convert alignment', () => {
5
+ test('should try convert non supported value', () => {
6
+ expect(convertAlignment('start')).toBe(Alignments.LEFT);
7
+ });
8
+
9
+ test('should not change supported value', () => {
10
+ expect(convertAlignment('center')).toBe(Alignments.CENTER);
11
+ });
12
+
13
+ test('should return null if cannot convert', () => {
14
+ expect(convertAlignment('test-test')).toBe(null);
15
+ });
16
+ });
@@ -0,0 +1,21 @@
1
+ import { convertFontSize } from '../convertFontSize';
2
+
3
+ describe('convert font size', () => {
4
+ const createWrapper = ({ fontSize }) => {
5
+ const wrapperEl = document.createElement('div');
6
+
7
+ wrapperEl.style.fontSize = `${fontSize}px`;
8
+
9
+ return wrapperEl;
10
+ };
11
+
12
+ test('should format px values', () => {
13
+ expect(convertFontSize('20px')).toBe(20);
14
+ });
15
+
16
+ test('should parse em value', () => {
17
+ const wrapperEl = createWrapper({ fontSize: 24 });
18
+
19
+ expect(convertFontSize('1.5em', wrapperEl)).toBe(36);
20
+ });
21
+ });
@@ -0,0 +1,21 @@
1
+ import { convertLineHeight } from '../convertLineHeight';
2
+
3
+ describe('convert line height', () => {
4
+ const createWrapper = ({ fontSize }) => {
5
+ const wrapperEl = document.createElement('div');
6
+
7
+ wrapperEl.style.fontSize = `${fontSize}px`;
8
+
9
+ return wrapperEl;
10
+ };
11
+
12
+ test('should allow relative values', () => {
13
+ expect(convertLineHeight('1.2')).toBe('1.2');
14
+ });
15
+
16
+ test('should convert px to relative', () => {
17
+ const wrapperEl = createWrapper({ fontSize: 20 });
18
+
19
+ expect(convertLineHeight('24px', wrapperEl)).toBe('1.20');
20
+ });
21
+ });
@@ -0,0 +1,12 @@
1
+ import { Alignments } from '../enums';
2
+
3
+ const MAPPING = {
4
+ start: Alignments.LEFT,
5
+ end: Alignments.RIGHT
6
+ };
7
+
8
+ export function convertAlignment(alignment) {
9
+ const mapped = MAPPING[alignment] || alignment;
10
+
11
+ return Alignments.values.includes(mapped) ? mapped : null;
12
+ }
@@ -1,4 +1,4 @@
1
- import { ColorModel } from 'zipify-colorpicker';
1
+ import { ColorModel } from '@zipify/colorpicker';
2
2
 
3
3
  export function convertColor(raw) {
4
4
  const model = ColorModel.create(raw);
@@ -0,0 +1,8 @@
1
+ export function convertFontSize(value, wrapperEl) {
2
+ if (!value.includes('em')) return parseInt(value);
3
+
4
+ const containerValue = window.getComputedStyle(wrapperEl).fontSize;
5
+ const size = parseFloat(value) * parseFloat(containerValue);
6
+
7
+ return Math.round(size);
8
+ }