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

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 (180) 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 -0
  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 +20 -1
  18. package/example/{example.html → index.html} +1 -0
  19. package/example/pageBlocks.js +31 -0
  20. package/example/presets.js +2 -2
  21. package/jest.config.js +3 -1
  22. package/lib/Wysiwyg.vue +50 -25
  23. package/lib/__tests__/utils/NodeFactory.js +13 -0
  24. package/lib/assets/icons/alignment-center.svg +3 -0
  25. package/lib/assets/icons/alignment-justify.svg +3 -0
  26. package/lib/assets/icons/alignment-left.svg +3 -0
  27. package/lib/assets/icons/alignment-right.svg +3 -0
  28. package/lib/assets/icons/arrow.svg +3 -0
  29. package/lib/assets/icons/background-color.svg +3 -0
  30. package/lib/assets/icons/case-style.svg +3 -0
  31. package/lib/assets/icons/font-color.svg +5 -0
  32. package/lib/assets/icons/italic.svg +3 -0
  33. package/lib/assets/icons/line-height.svg +3 -0
  34. package/lib/assets/icons/link.svg +3 -0
  35. package/lib/assets/icons/list-circle.svg +3 -0
  36. package/lib/assets/icons/list-decimal.svg +3 -0
  37. package/lib/assets/icons/list-disc.svg +3 -0
  38. package/lib/assets/icons/list-latin.svg +3 -0
  39. package/lib/assets/icons/list-roman.svg +3 -0
  40. package/lib/assets/icons/list-square.svg +3 -0
  41. package/lib/assets/icons/remove-format.svg +3 -0
  42. package/lib/assets/icons/reset-styles.svg +3 -0
  43. package/lib/assets/icons/strike-through.svg +3 -0
  44. package/lib/assets/icons/superscript.svg +3 -0
  45. package/lib/assets/icons/underline.svg +3 -0
  46. package/lib/assets/icons/unlink.svg +3 -0
  47. package/lib/components/base/Button.vue +21 -1
  48. package/lib/components/base/Checkbox.vue +89 -0
  49. package/lib/components/base/FieldLabel.vue +2 -1
  50. package/lib/components/base/Icon.vue +18 -10
  51. package/lib/components/base/Modal.vue +0 -1
  52. package/lib/components/base/NumberField.vue +1 -1
  53. package/lib/components/base/ScrollView.vue +0 -2
  54. package/lib/components/base/TextField.vue +106 -0
  55. package/lib/components/base/__tests__/Icon.test.js +6 -13
  56. package/lib/components/base/__tests__/Modal.test.js +6 -1
  57. package/lib/components/base/__tests__/TextField.test.js +57 -0
  58. package/lib/components/base/__tests__/__snapshots__/TextField.test.js.snap +9 -0
  59. package/lib/components/base/colorPicker/ColorPicker.vue +1 -1
  60. package/lib/components/base/colorPicker/composables/usePickerHotkeys.js +2 -1
  61. package/lib/components/base/composables/__tests__/useValidator.test.js +44 -0
  62. package/lib/components/base/composables/index.js +1 -0
  63. package/lib/components/base/composables/useValidator.js +23 -0
  64. package/lib/components/base/dropdown/Dropdown.vue +15 -3
  65. package/lib/components/base/dropdown/DropdownActivator.vue +19 -3
  66. package/lib/components/base/index.js +3 -1
  67. package/lib/components/toolbar/Toolbar.vue +49 -9
  68. package/lib/components/toolbar/ToolbarFull.vue +10 -2
  69. package/lib/components/toolbar/__tests__/Toolbar.test.js +6 -0
  70. package/lib/components/toolbar/controls/FontSizeControl.vue +7 -0
  71. package/lib/components/toolbar/controls/ListControl.vue +1 -5
  72. package/lib/components/toolbar/controls/StylePresetControl.vue +14 -1
  73. package/lib/components/toolbar/controls/UnderlineControl.vue +2 -2
  74. package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +16 -0
  75. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +4 -0
  76. package/lib/components/toolbar/controls/index.js +1 -0
  77. package/lib/components/toolbar/controls/link/LinkControl.vue +155 -0
  78. package/lib/components/toolbar/controls/link/LinkControlApply.vue +35 -0
  79. package/lib/components/toolbar/controls/link/LinkControlHeader.vue +67 -0
  80. package/lib/components/toolbar/controls/link/__tests__/LinkControl.test.js +79 -0
  81. package/lib/components/toolbar/controls/link/__tests__/LinkControlHeader.test.js +42 -0
  82. package/lib/components/toolbar/controls/link/composables/__tests__/__snapshots__/useLink.test.js.snap +8 -0
  83. package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +114 -0
  84. package/lib/components/toolbar/controls/link/composables/index.js +1 -0
  85. package/lib/components/toolbar/controls/link/composables/useLink.js +61 -0
  86. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +103 -0
  87. package/lib/components/toolbar/controls/link/destination/LinkControlPageBlock.vue +54 -0
  88. package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +52 -0
  89. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlPageBlock.test.js +36 -0
  90. package/lib/components/toolbar/controls/link/destination/__tests__/LinkControlUrl.test.js +46 -0
  91. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlPageBlock.test.js.snap +9 -0
  92. package/lib/components/toolbar/controls/link/destination/__tests__/__snapshots__/LinkControlUrl.test.js.snap +17 -0
  93. package/lib/components/toolbar/controls/link/destination/index.js +1 -0
  94. package/lib/components/toolbar/controls/link/index.js +1 -0
  95. package/lib/composables/__tests__/useEditor.test.js +2 -2
  96. package/lib/composables/useEditor.js +5 -7
  97. package/lib/composables/useToolbar.js +23 -28
  98. package/lib/directives/__tests__/outClick.test.js +6 -0
  99. package/lib/directives/outClick.js +19 -6
  100. package/lib/enums/Alignments.js +10 -1
  101. package/lib/enums/LinkDestinations.js +4 -0
  102. package/lib/enums/LinkTargets.js +4 -0
  103. package/lib/enums/TextSettings.js +3 -1
  104. package/lib/enums/index.js +2 -0
  105. package/lib/extensions/Alignment.js +21 -7
  106. package/lib/extensions/BackgroundColor.js +14 -6
  107. package/lib/extensions/DeviceManager.js +0 -4
  108. package/lib/extensions/FontColor.js +14 -6
  109. package/lib/extensions/FontFamily.js +25 -8
  110. package/lib/extensions/FontSize.js +31 -12
  111. package/lib/extensions/FontStyle.js +23 -13
  112. package/lib/extensions/FontWeight.js +24 -14
  113. package/lib/extensions/LineHeight.js +31 -28
  114. package/lib/extensions/Link.js +89 -0
  115. package/lib/extensions/StylePreset.js +16 -15
  116. package/lib/extensions/TextDecoration.js +45 -12
  117. package/lib/extensions/__tests__/Alignment.test.js +11 -5
  118. package/lib/extensions/__tests__/BackgroundColor.test.js +11 -5
  119. package/lib/extensions/__tests__/CaseStyle.test.js +3 -5
  120. package/lib/extensions/__tests__/FontColor.test.js +11 -5
  121. package/lib/extensions/__tests__/FontFamily.test.js +32 -7
  122. package/lib/extensions/__tests__/FontSize.test.js +13 -6
  123. package/lib/extensions/__tests__/FontStyle.test.js +11 -5
  124. package/lib/extensions/__tests__/FontWeight.test.js +19 -5
  125. package/lib/extensions/__tests__/LineHeight.test.js +23 -11
  126. package/lib/extensions/__tests__/Link.test.js +102 -0
  127. package/lib/extensions/__tests__/StylePreset.test.js +70 -6
  128. package/lib/extensions/__tests__/TextDecoration.test.js +51 -5
  129. package/lib/extensions/__tests__/__snapshots__/Alignment.test.js.snap +26 -2
  130. package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +30 -1
  131. package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +18 -1
  132. package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +88 -1
  133. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +33 -2
  134. package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +25 -4
  135. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +47 -1
  136. package/lib/extensions/__tests__/__snapshots__/LineHeight.test.js.snap +26 -2
  137. package/lib/extensions/__tests__/__snapshots__/Link.test.js.snap +225 -0
  138. package/lib/extensions/__tests__/__snapshots__/StylePreset.test.js.snap +6 -2
  139. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +183 -3
  140. package/lib/extensions/core/CopyPasteProcessor.js +10 -0
  141. package/lib/extensions/core/TextProcessor.js +10 -0
  142. package/lib/extensions/core/__tests__/NodeProcessor.test.js +3 -5
  143. package/lib/extensions/core/__tests__/SelectionProcessor.test.js +3 -5
  144. package/lib/extensions/core/__tests__/TextProcessor.test.js +138 -12
  145. package/lib/extensions/core/__tests__/__snapshots__/TextProcessor.test.js.snap +26 -0
  146. package/lib/extensions/core/index.js +11 -2
  147. package/lib/extensions/core/plugins/PastePlugin.js +57 -0
  148. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +20 -0
  149. package/lib/extensions/core/plugins/index.js +1 -0
  150. package/lib/extensions/index.js +46 -34
  151. package/lib/extensions/list/__tests__/List.test.js +3 -5
  152. package/lib/extensions/list/__tests__/__snapshots__/List.test.js.snap +45 -15
  153. package/lib/injectionTokens.js +2 -1
  154. package/lib/services/ContentNormalizer.js +113 -29
  155. package/lib/services/ContextWidnow.js +23 -0
  156. package/lib/services/__tests__/ContentNormalizer.test.js +75 -7
  157. package/lib/services/index.js +1 -0
  158. package/lib/styles/content.css +102 -13
  159. package/lib/styles/helpers/offsets.css +16 -0
  160. package/lib/styles/variables.css +6 -0
  161. package/lib/utils/__tests__/__snapshots__/renderInlineSetting.test.js.snap +4 -4
  162. package/lib/utils/__tests__/convertAlignment.test.js +16 -0
  163. package/lib/utils/__tests__/convertFontSize.test.js +21 -0
  164. package/lib/utils/__tests__/convertLineHeight.test.js +21 -0
  165. package/lib/utils/convertAlignment.js +12 -0
  166. package/lib/utils/convertColor.js +1 -1
  167. package/lib/utils/convertFontSize.js +8 -0
  168. package/lib/utils/convertLineHeight.js +17 -0
  169. package/lib/utils/importIcon.js +13 -0
  170. package/lib/utils/index.js +4 -0
  171. package/lib/utils/renderInlineSetting.js +1 -1
  172. package/package.json +21 -25
  173. package/config/webpack/example.config.js +0 -86
  174. package/config/webpack/loaders/index.js +0 -6
  175. package/config/webpack/loaders/js-loader.js +0 -5
  176. package/config/webpack/loaders/style-loader.js +0 -7
  177. package/config/webpack/loaders/svg-loader.js +0 -4
  178. package/config/webpack/loaders/vue-loader.js +0 -4
  179. package/lib/assets/icons.svg +0 -69
  180. package/lib/composables/__tests__/useToolbar.test.js +0 -56
@@ -4,10 +4,6 @@ import { createCommand } from '../utils';
4
4
  export const DeviceManager = Extension.create({
5
5
  name: 'device_manager',
6
6
 
7
- addOptions: () => ({
8
- deviceRef: null
9
- }),
10
-
11
7
  addCommands() {
12
8
  return {
13
9
  getDevice: createCommand(() => this.options.deviceRef)
@@ -30,12 +30,20 @@ export const FontColor = Mark.create({
30
30
  };
31
31
  },
32
32
 
33
- parseHTML: () => [
34
- {
35
- style: 'color',
36
- getAttrs: (value) => ({ value: convertColor(value) })
37
- }
38
- ],
33
+ parseHTML() {
34
+ const getAttrs = (value) => ({ value: convertColor(value) });
35
+
36
+ return [
37
+ {
38
+ style: '--zw-font-color',
39
+ getAttrs
40
+ },
41
+ {
42
+ style: 'color',
43
+ getAttrs
44
+ }
45
+ ];
46
+ },
39
47
 
40
48
  renderHTML({ HTMLAttributes: attrs }) {
41
49
  return renderMark({ font_color: attrs.value });
@@ -7,7 +7,8 @@ export const FontFamily = Mark.create({
7
7
  name: TextSettings.FONT_FAMILY,
8
8
 
9
9
  addOptions: () => ({
10
- fonts: []
10
+ fonts: [],
11
+ defaultFont: ''
11
12
  }),
12
13
 
13
14
  addAttributes: () => ({
@@ -56,14 +57,30 @@ export const FontFamily = Mark.create({
56
57
  };
57
58
  },
58
59
 
59
- parseHTML: () => [
60
- {
61
- style: 'font-family',
62
- getAttrs: (value) => ({ value })
63
- }
64
- ],
60
+ parseHTML() {
61
+ const getAttrs = (input) => {
62
+ const parsed = input.replace(/"/g, '');
63
+ const isExists = this.options.fonts.some((font) => font.name === parsed);
64
+ const value = isExists ? parsed : this.options.defaultFont;
65
+
66
+ return { value };
67
+ };
68
+
69
+ return [
70
+ {
71
+ style: '--zw-font-family',
72
+ getAttrs
73
+ },
74
+ {
75
+ style: 'font-family',
76
+ getAttrs
77
+ }
78
+ ];
79
+ },
65
80
 
66
81
  renderHTML({ HTMLAttributes: attrs }) {
67
- return renderMark({ font_family: attrs.value });
82
+ const font_family = attrs.value ? `"${attrs.value}"` : null;
83
+
84
+ return renderMark({ font_family });
68
85
  }
69
86
  });
@@ -1,6 +1,6 @@
1
1
  import { Mark } from '@tiptap/vue-2';
2
2
  import { computed } from '@vue/composition-api';
3
- import { createCommand, createKeyboardShortcut, renderMark } from '../utils';
3
+ import { convertFontSize, createCommand, createKeyboardShortcut, renderMark } from '../utils';
4
4
  import { TextSettings } from '../enums';
5
5
 
6
6
  export const FontSize = Mark.create({
@@ -62,25 +62,44 @@ export const FontSize = Mark.create({
62
62
  'Mod-Shift--': createKeyboardShortcut('decreaseFontSize')
63
63
  }),
64
64
 
65
- parseHTML: () => [
66
- {
67
- style: 'font-size',
65
+ parseHTML() {
66
+ const parseSize = (value) => {
67
+ if (!value) return null;
68
68
 
69
- getAttrs: (input) => {
70
- const value = String(parseInt(input));
69
+ const converted = convertFontSize(value, this.options.wrapperRef.value);
71
70
 
72
- return { desktop: value, tablet: value, mobile: value };
71
+ return String(converted);
72
+ };
73
+
74
+ return [
75
+ {
76
+ tag: '[style*="--zw-font-size"]',
77
+
78
+ getAttrs: ({ style }) => ({
79
+ mobile: parseSize(style.getPropertyValue('--zw-font-size-mobile')),
80
+ tablet: parseSize(style.getPropertyValue('--zw-font-size-tablet')),
81
+ desktop: parseSize(style.getPropertyValue('--zw-font-size-desktop'))
82
+ })
83
+ },
84
+ {
85
+ style: 'font-size',
86
+
87
+ getAttrs: (input) => {
88
+ const value = parseSize(input);
89
+
90
+ return { desktop: value, tablet: value, mobile: value };
91
+ }
73
92
  }
74
- }
75
- ],
93
+ ];
94
+ },
76
95
 
77
96
  renderHTML({ HTMLAttributes: attrs }) {
78
97
  const addUnits = (value) => value ? `${value}px` : null;
79
98
 
80
99
  return renderMark({
81
- mobile_font_size: addUnits(attrs.mobile),
82
- tablet_font_size: addUnits(attrs.tablet),
83
- desktop_font_size: addUnits(attrs.desktop)
100
+ font_size_mobile: addUnits(attrs.mobile),
101
+ font_size_tablet: addUnits(attrs.tablet),
102
+ font_size_desktop: addUnits(attrs.desktop)
84
103
  });
85
104
  }
86
105
  });
@@ -56,18 +56,28 @@ export const FontStyle = Mark.create({
56
56
  'Mod-I': createKeyboardShortcut('toggleItalic')
57
57
  }),
58
58
 
59
- parseHTML: () => [
60
- {
61
- tag: 'i',
62
- attrs: { italic: true }
63
- },
64
- {
65
- style: 'font-style',
66
- getAttrs: (value) => ({ italic: value.includes('italic') })
67
- }
68
- ],
69
-
70
- renderHTML() {
71
- return renderMark({ font_style: 'italic' });
59
+ parseHTML() {
60
+ const getAttrs = (value) => value.includes('italic') && { italic: true };
61
+
62
+ return [
63
+ {
64
+ style: '--zw-font-style',
65
+ getAttrs
66
+ },
67
+ {
68
+ style: 'font-style',
69
+ getAttrs
70
+ },
71
+ {
72
+ tag: 'i',
73
+ attrs: { italic: true }
74
+ }
75
+ ];
76
+ },
77
+
78
+ renderHTML({ HTMLAttributes: attrs }) {
79
+ const font_style = attrs.italic ? 'italic' : null;
80
+
81
+ return renderMark({ font_style });
72
82
  }
73
83
  });
@@ -59,20 +59,30 @@ export const FontWeight = Mark.create({
59
59
  'Mod-B': createKeyboardShortcut('toggleBold')
60
60
  }),
61
61
 
62
- parseHTML: () => [
63
- {
64
- tag: 'b',
65
- attrs: { value: '700' }
66
- },
67
- {
68
- tag: 'strong',
69
- attrs: { value: '700' }
70
- },
71
- {
72
- style: 'font-weight',
73
- getAttrs: (value) => ({ value })
74
- }
75
- ],
62
+ parseHTML() {
63
+ const getAttrs = (value) => {
64
+ return Number(value) ? { value } : false;
65
+ };
66
+
67
+ return [
68
+ {
69
+ style: '--zw-font-weight',
70
+ getAttrs
71
+ },
72
+ {
73
+ style: 'font-weight',
74
+ getAttrs
75
+ },
76
+ {
77
+ tag: 'b',
78
+ attrs: { value: '700' }
79
+ },
80
+ {
81
+ tag: 'strong',
82
+ attrs: { value: '700' }
83
+ }
84
+ ];
85
+ },
76
86
 
77
87
  renderHTML({ HTMLAttributes: attrs }) {
78
88
  return renderMark({ font_weight: attrs.value });
@@ -1,6 +1,6 @@
1
1
  import { Extension } from '@tiptap/vue-2';
2
2
  import { computed } from '@vue/composition-api';
3
- import { createCommand, renderInlineSetting } from '../utils';
3
+ import { createCommand, renderInlineSetting, convertLineHeight } from '../utils';
4
4
  import { NodeTypes, TextSettings } from '../enums';
5
5
 
6
6
  const DEFAULTS = {
@@ -12,43 +12,46 @@ const DEFAULTS = {
12
12
  export const LineHeight = Extension.create({
13
13
  name: TextSettings.LINE_HEIGHT,
14
14
 
15
- addGlobalAttributes: () => [
16
- {
17
- types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
18
- attributes: {
19
- [TextSettings.LINE_HEIGHT]: {
20
- isRequired: false,
15
+ addGlobalAttributes() {
16
+ return [
17
+ {
18
+ types: [NodeTypes.PARAGRAPH, NodeTypes.HEADING],
19
+ attributes: {
20
+ [TextSettings.LINE_HEIGHT]: {
21
+ isRequired: false,
21
22
 
22
- parseHTML(element) {
23
- const value = element.style.lineHeight;
23
+ parseHTML: (element) => {
24
+ if (element.matches('[style*="--zw-line-height"]')) {
25
+ const mobile = element.style.getPropertyValue('--zw-line-height-mobile') || null;
26
+ const tablet = element.style.getPropertyValue('--zw-line-height-tablet') || null;
27
+ const desktop = element.style.getPropertyValue('--zw-line-height-desktop') || null;
24
28
 
25
- if (!value) return null;
29
+ return { mobile, tablet, desktop };
30
+ }
26
31
 
27
- if (!value.includes('px')) {
28
- return { desktop: value, tablet: value, mobile: value };
29
- }
32
+ const value = element.style.lineHeight;
33
+
34
+ if (!value) return null;
30
35
 
31
- // element is not connected to window so getComputedStyle is not working on element
32
- const childFontSize = element.firstElementChild.style.fontSize;
33
- const fontSize = childFontSize || window.getComputedStyle(document.body).fontSize;
34
- const relative = (parseFloat(value) / parseFloat(fontSize)).toFixed(2);
36
+ const converted = convertLineHeight(value, element, this.options.wrapperRef.value);
35
37
 
36
- return { desktop: relative, tablet: relative, mobile: relative };
37
- },
38
+ return { desktop: converted, tablet: converted, mobile: converted };
39
+ },
38
40
 
39
- renderHTML(attrs) {
40
- if (!attrs.line_height) return null;
41
+ renderHTML(attrs) {
42
+ if (!attrs.line_height) return null;
41
43
 
42
- return renderInlineSetting({
43
- mobile_line_height: attrs.line_height.mobile,
44
- tablet_line_height: attrs.line_height.tablet,
45
- desktop_line_height: attrs.line_height.desktop
46
- });
44
+ return renderInlineSetting({
45
+ line_height_mobile: attrs.line_height.mobile,
46
+ line_height_tablet: attrs.line_height.tablet,
47
+ line_height_desktop: attrs.line_height.desktop
48
+ });
49
+ }
47
50
  }
48
51
  }
49
52
  }
50
- }
51
- ],
53
+ ];
54
+ },
52
55
 
53
56
  addCommands() {
54
57
  return {
@@ -0,0 +1,89 @@
1
+ import Base from '@tiptap/extension-link';
2
+ import { createCommand } from '../utils';
3
+ import { LinkDestinations, LinkTargets } from '../enums';
4
+
5
+ export const Link = Base.extend({
6
+ name: 'link',
7
+
8
+ addOptions() {
9
+ return {
10
+ ...this.parent?.(),
11
+ openOnClick: false
12
+ };
13
+ },
14
+
15
+ addAttributes() {
16
+ return {
17
+ href: {
18
+ default: null,
19
+ parseHTML: (element) => {
20
+ const href = element.getAttribute('href');
21
+
22
+ if (!href.startsWith('#')) return href;
23
+
24
+ return parseFloat(element.getAttribute('href').replace('#', ''));
25
+ }
26
+ },
27
+
28
+ target: {
29
+ default: LinkTargets.SELF,
30
+ parseHTML: (element) => element.getAttribute('target') || LinkTargets.SELF
31
+ },
32
+
33
+ destination: {
34
+ default: LinkDestinations.URL,
35
+ parseHTML: (element) => {
36
+ const href = element.getAttribute('href');
37
+
38
+ if (!href.startsWith('#')) return LinkDestinations.URL;
39
+
40
+ const id = href.replace('#', '');
41
+ const block = this.options.pageBlocksRef.value.find((block) => block.id === parseInt(id));
42
+
43
+ return block ? LinkDestinations.BLOCK : LinkDestinations.URL;
44
+ }
45
+ }
46
+ };
47
+ },
48
+
49
+ addCommands() {
50
+ return {
51
+ ...this.parent?.(),
52
+ applyLink: createCommand(({ commands, chain }, attributes) => {
53
+ if (!commands.getSelectedText()) {
54
+ return commands.insertContent({
55
+ type: 'text',
56
+ marks: [
57
+ {
58
+ type: 'link',
59
+ attrs: { ...attributes }
60
+ }
61
+ ],
62
+ text: attributes.text
63
+ });
64
+ }
65
+
66
+ return chain()
67
+ .setMark(this.name, attributes)
68
+ .transformText(() => attributes.text)
69
+ .extendMarkRange('link')
70
+ .run();
71
+ })
72
+ };
73
+ },
74
+
75
+ renderHTML({ HTMLAttributes: attrs }) {
76
+ const href = attrs.destination === LinkDestinations.BLOCK ? `#${attrs.href}` : attrs.href;
77
+
78
+ const presetClass = this.options.basePresetClass + this.options.preset.id;
79
+ const classes = `${presetClass} zw-style`;
80
+
81
+ const linkAttrs = {
82
+ href,
83
+ target: attrs.target,
84
+ class: classes
85
+ };
86
+
87
+ return ['a', linkAttrs, 0];
88
+ }
89
+ });
@@ -3,6 +3,7 @@ import { computed, toRef } from '@vue/composition-api';
3
3
  import { Heading } from '@tiptap/extension-heading';
4
4
  import { createCommand } from '../utils';
5
5
  import { Devices, NodeTypes, TextSettings } from '../enums';
6
+ import { ContextWindow } from '../services';
6
7
 
7
8
  function makePresetClass(base, preset) {
8
9
  const baseClass = base.split(' ').map((part) => `.${part}`).join('');
@@ -20,13 +21,6 @@ export const StylePreset = Extension.create({
20
21
  })
21
22
  ],
22
23
 
23
- addOptions: () => ({
24
- presetsRef: null,
25
- defaultId: null,
26
- baseClass: null,
27
- makeVariable: null
28
- }),
29
-
30
24
  addStorage: () => ({
31
25
  presetStyleEl: null
32
26
  }),
@@ -38,11 +32,13 @@ export const StylePreset = Extension.create({
38
32
  attributes: {
39
33
  preset: {
40
34
  isRequired: false,
41
- default: null,
35
+ default: { id: this.options.defaultId },
42
36
 
43
37
  parseHTML: (element) => {
44
38
  const presets = this.options.presetsRef.value;
45
39
 
40
+ if (element.parentElement.tagName === 'LI') return null;
41
+
46
42
  for (const { id, node, fallbackClass } of presets) {
47
43
  const isFallback = fallbackClass && element.classList.contains(fallbackClass);
48
44
 
@@ -147,29 +143,34 @@ export const StylePreset = Extension.create({
147
143
  chain()
148
144
  .storeSelection()
149
145
  .expandSelectionToBlock()
150
- .unsetAllMarks()
146
+ .unsetMarks(TextSettings.marks)
151
147
  .resetAttributes(NodeTypes.PARAGRAPH, TextSettings.attributes)
152
148
  .resetAttributes(NodeTypes.HEADING, TextSettings.attributes)
153
149
  .restoreSelection()
154
150
  .run();
155
151
  }),
156
152
 
157
- removeFormat: createCommand(({ commands }) => {
158
- commands.removePresetCustomization();
159
- commands.applyDefaultPreset();
153
+ removeFormat: createCommand(({ chain }) => {
154
+ chain()
155
+ .storeSelection()
156
+ .expandSelectionToBlock()
157
+ .unsetAllMarks()
158
+ .applyDefaultPreset()
159
+ .restoreSelection()
160
+ .run();
160
161
  })
161
162
  };
162
163
  },
163
164
 
164
165
  onCreate() {
165
- const existingStyleEl = document.querySelector('[data-zw-styles]');
166
+ const existingStyleEl = ContextWindow.document.querySelector('[data-zw-styles]');
166
167
 
167
168
  if (existingStyleEl) {
168
169
  this.storage.presetStyleEl = existingStyleEl;
169
170
  return;
170
171
  }
171
172
 
172
- this.storage.presetStyleEl = document.createElement('style');
173
+ this.storage.presetStyleEl = ContextWindow.document.createElement('style');
173
174
  this.storage.presetStyleEl.dataset.zwStyles = '';
174
175
 
175
176
  for (const preset of this.options.presetsRef.value) {
@@ -190,6 +191,6 @@ export const StylePreset = Extension.create({
190
191
  this.storage.presetStyleEl.innerHTML += css.join(' ');
191
192
  }
192
193
 
193
- document.head.append(this.storage.presetStyleEl);
194
+ ContextWindow.head.append(this.storage.presetStyleEl);
194
195
  }
195
196
  });
@@ -13,10 +13,10 @@ export const TextDecoration = Mark.create({
13
13
 
14
14
  addCommands() {
15
15
  return {
16
- isUnderline: createCommand(({ commands }) => {
16
+ isUnderline: createCommand(({ commands, editor }) => {
17
17
  const decoration = commands.getTextDecoration();
18
18
 
19
- return computed(() => decoration.value.underline);
19
+ return computed(() => editor.isActive('link') || decoration.value.underline);
20
20
  }),
21
21
 
22
22
  isStrikeThrough: createCommand(({ commands }) => {
@@ -51,7 +51,9 @@ export const TextDecoration = Mark.create({
51
51
  });
52
52
  }),
53
53
 
54
- toggleUnderline: createCommand(({ commands }) => {
54
+ toggleUnderline: createCommand(({ commands, editor }) => {
55
+ if (editor.isActive('link')) return;
56
+
55
57
  commands.toggleTextDecoration('underline');
56
58
  }),
57
59
 
@@ -86,16 +88,47 @@ export const TextDecoration = Mark.create({
86
88
  'Mod-U': createKeyboardShortcut('toggleUnderline')
87
89
  }),
88
90
 
89
- parseHTML: () => [
90
- {
91
- style: 'text-decoration-line',
91
+ parseHTML() {
92
+ const getAttrs = (value) => {
93
+ const underline = value.includes('underline');
94
+ const strike_through = value.includes('line-through');
92
95
 
93
- getAttrs: (value) => ({
94
- underline: value.includes('underline'),
95
- strike_through: value.includes('line-through')
96
- })
97
- }
98
- ],
96
+ if (!underline && !strike_through) {
97
+ return false;
98
+ }
99
+
100
+ return { underline, strike_through };
101
+ };
102
+
103
+ return [
104
+ {
105
+ style: '--zw-text-decoration',
106
+ getAttrs
107
+ },
108
+ {
109
+ style: 'text-decoration-line',
110
+ getAttrs
111
+ },
112
+ {
113
+ style: 'text-decoration',
114
+ getAttrs
115
+ },
116
+ {
117
+ tag: 's',
118
+ attrs: {
119
+ underline: false,
120
+ strike_through: true
121
+ }
122
+ },
123
+ {
124
+ tag: 'u',
125
+ attrs: {
126
+ underline: true,
127
+ strike_through: false
128
+ }
129
+ }
130
+ ];
131
+ },
99
132
 
100
133
  renderHTML({ HTMLAttributes: attrs }) {
101
134
  const decorations = [];
@@ -2,11 +2,11 @@ import { Editor, Extension } from '@tiptap/vue-2';
2
2
  import { ref } from '@vue/composition-api';
3
3
  import { NodeFactory } from '../../__tests__/utils';
4
4
  import { createCommand } from '../../utils';
5
- import { CORE_EXTENSIONS } from '../core';
6
5
  import { Alignment } from '../Alignment';
7
6
  import { DeviceManager } from '../DeviceManager';
8
7
  import { Alignments } from '../../enums';
9
8
  import { ContentNormalizer } from '../../services';
9
+ import { buildCoreExtensions } from '../core';
10
10
 
11
11
  const MockStylePreset = Extension.create({
12
12
  name: 'style_preset',
@@ -23,11 +23,9 @@ const MockStylePreset = Extension.create({
23
23
  });
24
24
 
25
25
  function createEditor({ content }) {
26
- const normalizer = new ContentNormalizer();
27
-
28
26
  return new Editor({
29
- content: normalizer.normalize(content),
30
- extensions: CORE_EXTENSIONS.concat(
27
+ content: ContentNormalizer.normalize(content),
28
+ extensions: buildCoreExtensions().concat(
31
29
  MockStylePreset,
32
30
  DeviceManager.configure({ deviceRef: ref('desktop') }),
33
31
  Alignment
@@ -118,6 +116,14 @@ describe('parsing html', () => {
118
116
  expect(editor.getJSON()).toMatchSnapshot();
119
117
  });
120
118
 
119
+ test('should get alignment from rendered view', () => {
120
+ const editor = createEditor({
121
+ content: '<p style="--zw-text-align-mobile:center;--zw-text-align-desktop:right">test</p>'
122
+ });
123
+
124
+ expect(editor.getJSON()).toMatchSnapshot();
125
+ });
126
+
121
127
  test('should set null if no alignment', () => {
122
128
  const editor = createEditor({
123
129
  content: '<p>test</p>'
@@ -2,9 +2,9 @@ import { Editor, Extension } from '@tiptap/vue-2';
2
2
  import { ref } from '@vue/composition-api';
3
3
  import { NodeFactory } from '../../__tests__/utils';
4
4
  import { createCommand } from '../../utils';
5
- import { CORE_EXTENSIONS } from '../core';
6
5
  import { BackgroundColor } from '../BackgroundColor';
7
6
  import { ContentNormalizer } from '../../services';
7
+ import { buildCoreExtensions } from '../core';
8
8
 
9
9
  const MockStylePreset = Extension.create({
10
10
  name: 'style_preset',
@@ -19,11 +19,9 @@ const MockStylePreset = Extension.create({
19
19
  });
20
20
 
21
21
  function createEditor({ content }) {
22
- const normalizer = new ContentNormalizer();
23
-
24
22
  return new Editor({
25
- content: normalizer.normalize(content),
26
- extensions: CORE_EXTENSIONS.concat(MockStylePreset, BackgroundColor)
23
+ content: ContentNormalizer.normalize(content),
24
+ extensions: buildCoreExtensions().concat(MockStylePreset, BackgroundColor)
27
25
  });
28
26
  }
29
27
 
@@ -94,6 +92,14 @@ describe('parsing html', () => {
94
92
  expect(editor.getJSON()).toMatchSnapshot();
95
93
  });
96
94
 
95
+ test('should get value from rendered view', () => {
96
+ const editor = createEditor({
97
+ content: '<p><span style="--zw-background-color: red">lorem</span> ipsum</p>'
98
+ });
99
+
100
+ expect(editor.getJSON()).toMatchSnapshot();
101
+ });
102
+
97
103
  test('should merge paragraph and text settings', () => {
98
104
  const editor = createEditor({
99
105
  content: '<p style="background-color: red"><span style="background-color: #000">lorem</span> ipsum</p>'