@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
@@ -0,0 +1,103 @@
1
+ <template>
2
+ <div>
3
+ <FieldLabel class="zw-margin-bottom--xxs">
4
+ Destination
5
+ </FieldLabel>
6
+
7
+ <Dropdown
8
+ class="zw-margin-bottom--sm"
9
+ color="gray"
10
+ :max-width="266"
11
+ :value="link.currentDestination.value.id"
12
+ :options="$options.destinationTypes"
13
+ @change="changeDestination"
14
+ >
15
+ <template #option="{ option }">
16
+ <DropdownOption
17
+ class="zw-link-modal-dropdown__option"
18
+ :option="option"
19
+ />
20
+ </template>
21
+ </Dropdown>
22
+
23
+ <LinkControlUrl
24
+ class="zw-margin-bottom--md"
25
+ v-if="isURLDestination"
26
+ :href="link.destinationHrefs.value.url"
27
+ :isTargetBlank="isTargetBlank"
28
+ :validator="validator"
29
+ @update-link="updateLink"
30
+ @update-target="link.updateTarget"
31
+ />
32
+
33
+ <LinkControlPageBlock
34
+ v-else
35
+ :value="link.destinationHrefs.value.block"
36
+ @update="updateLink"
37
+ />
38
+ </div>
39
+ </template>
40
+
41
+ <script>
42
+ import { computed, toRef } from '@vue/composition-api';
43
+ import { LinkDestinations, LinkTargets } from '../../../../../enums';
44
+ import { Dropdown, DropdownOption, FieldLabel } from '../../../../base';
45
+ import LinkControlPageBlock from './LinkControlPageBlock';
46
+ import LinkControlUrl from './LinkControlUrl';
47
+
48
+ export default {
49
+ name: 'LinkControlDestination',
50
+
51
+ destinationTypes: [
52
+ { id: LinkDestinations.URL, title: 'URL' },
53
+ { id: LinkDestinations.BLOCK, title: 'Page Block' }
54
+ ],
55
+
56
+ components: {
57
+ LinkControlPageBlock,
58
+ LinkControlUrl,
59
+ FieldLabel,
60
+ DropdownOption,
61
+ Dropdown
62
+ },
63
+
64
+ props: {
65
+ link: {
66
+ type: Object,
67
+ required: true
68
+ },
69
+
70
+ validator: {
71
+ type: Object,
72
+ required: true
73
+ }
74
+ },
75
+
76
+ setup(props, { emit }) {
77
+ const link = toRef(props, 'link');
78
+ const isURLDestination = computed(() => link.value.currentDestination.value.id === 'url');
79
+ const isTargetBlank = computed(() => link.value.linkData.value.target === LinkTargets.BLANK);
80
+
81
+ const changeDestination = (value) => {
82
+ emit('reset-errors');
83
+ link.value.currentDestination.value.id = value;
84
+ if (!isURLDestination.value) link.value.target = LinkTargets.SELF;
85
+
86
+ link.value.destinationHrefs.value.url = '';
87
+ };
88
+
89
+ const updateLink = (value) => {
90
+ emit('reset-errors');
91
+
92
+ link.value.updateLink(value);
93
+ };
94
+
95
+ return {
96
+ isTargetBlank,
97
+ isURLDestination,
98
+ changeDestination,
99
+ updateLink
100
+ };
101
+ }
102
+ };
103
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div>
3
+ <FieldLabel class="zw-margin-bottom--xxs" field-id="link-destination">
4
+ Select block
5
+ </FieldLabel>
6
+
7
+ <Dropdown
8
+ color="gray"
9
+ :max-width="266"
10
+ :value="value"
11
+ :options="pageBlocks"
12
+ @change="applyBlock"
13
+ >
14
+ <template #option="{ option }">
15
+ <DropdownOption
16
+ class="zw-link-modal-dropdown__option"
17
+ :option="option"
18
+ />
19
+ </template>
20
+ </Dropdown>
21
+ </div>
22
+ </template>
23
+
24
+ <script>
25
+ import { inject } from '@vue/composition-api';
26
+ import { FieldLabel, DropdownOption, Dropdown } from '../../../../base';
27
+ import { InjectionTokens } from '../../../../../injectionTokens';
28
+
29
+ export default {
30
+ name: 'LinkControlPageBlock',
31
+
32
+ components: { DropdownOption, Dropdown, FieldLabel },
33
+
34
+ props: {
35
+ value: {
36
+ type: Number,
37
+ required: true
38
+ }
39
+ },
40
+
41
+ setup(_, { emit }) {
42
+ const pageBlocks = inject(InjectionTokens.PAGE_BLOCKS);
43
+
44
+ const applyBlock = (id) => {
45
+ const block = pageBlocks.value.find((block) => block.id === id);
46
+
47
+ emit('update', block.id);
48
+ };
49
+
50
+ return { pageBlocks, applyBlock };
51
+ }
52
+ };
53
+ </script>
54
+
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <div>
3
+ <TextField
4
+ class="zw-margin-bottom--xs"
5
+ label="To what URL should this link go?"
6
+ placeholder="https://"
7
+ :value="href"
8
+ :error="validator.error.value"
9
+ @input="updateLink"
10
+ />
11
+
12
+ <Checkbox
13
+ label="Open in new tab"
14
+ :value="isTargetBlank"
15
+ @input="updateTarget"
16
+ />
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ import { TextField, Checkbox } from '../../../../base';
22
+
23
+ export default {
24
+ name: 'LinkControlUrl',
25
+
26
+ components: { TextField, Checkbox },
27
+
28
+ props: {
29
+ href: {
30
+ type: String,
31
+ required: true
32
+ },
33
+
34
+ isTargetBlank: {
35
+ type: Boolean,
36
+ required: true
37
+ },
38
+
39
+ validator: {
40
+ type: Object,
41
+ required: true
42
+ }
43
+ },
44
+
45
+ setup(props, { emit }) {
46
+ const updateLink = (value) => emit('update-link', value);
47
+ const updateTarget = (value) => emit('update-target', value);
48
+
49
+ return { updateLink, updateTarget };
50
+ }
51
+ };
52
+ </script>
@@ -0,0 +1,36 @@
1
+ import { ref, h } from '@vue/composition-api';
2
+ import { shallowMount } from '@vue/test-utils';
3
+ import LinkControlPageBlock from '../LinkControlPageBlock';
4
+ import { InjectionTokens } from '../../../../../../injectionTokens';
5
+ import { Dropdown } from '../../../../../base';
6
+
7
+ function createComponent() {
8
+ return shallowMount(LinkControlPageBlock, {
9
+ propsData: {
10
+ value: 12
11
+ },
12
+
13
+ stubs: {
14
+ Dropdown: {
15
+ render: () => h('div'),
16
+ props: ['value']
17
+ }
18
+ },
19
+
20
+ provide: {
21
+ [InjectionTokens.PAGE_BLOCKS]: ref([{ id: 1 }, { id: 2 }])
22
+ }
23
+ });
24
+ }
25
+
26
+ describe('applying block', () => {
27
+ test('should apply block', () => {
28
+ const wrapper = createComponent();
29
+
30
+ const dropdownWrapper = wrapper.findComponent(Dropdown);
31
+
32
+ dropdownWrapper.vm.$emit('change', 2);
33
+
34
+ expect(wrapper.emitted('update')).toMatchSnapshot();
35
+ });
36
+ });
@@ -0,0 +1,46 @@
1
+ import { ref, h } from '@vue/composition-api';
2
+ import { shallowMount } from '@vue/test-utils';
3
+ import { Checkbox, TextField } from '../../../../../base';
4
+ import LinkControlUrl from '../LinkControlUrl';
5
+
6
+ function createComponent() {
7
+ return shallowMount(LinkControlUrl, {
8
+ propsData: {
9
+ validator: { error: ref({ value: null }) },
10
+ href: '',
11
+ isTargetBlank: false
12
+ },
13
+
14
+ stubs: {
15
+ TextField: {
16
+ render: () => h('div'),
17
+ props: ['value']
18
+ },
19
+
20
+ Checkbox: {
21
+ render: () => h('div'),
22
+ props: ['value']
23
+ }
24
+ }
25
+ });
26
+ }
27
+
28
+ describe('update link', () => {
29
+ test('should update link url', () => {
30
+ const wrapper = createComponent();
31
+ const textFieldWrapper = wrapper.findComponent(TextField);
32
+
33
+ textFieldWrapper.vm.$emit('input', 'Some text');
34
+
35
+ expect(wrapper.emitted('update-link')).toMatchSnapshot();
36
+ });
37
+
38
+ test('should update link target', () => {
39
+ const wrapper = createComponent();
40
+ const textFieldWrapper = wrapper.findComponent(Checkbox);
41
+
42
+ textFieldWrapper.vm.$emit('input', true);
43
+
44
+ expect(wrapper.emitted('update-target')).toMatchSnapshot();
45
+ });
46
+ });
@@ -0,0 +1,9 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`applying block should apply block 1`] = `
4
+ Array [
5
+ Array [
6
+ 2,
7
+ ],
8
+ ]
9
+ `;
@@ -0,0 +1,17 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`update link should update link target 1`] = `
4
+ Array [
5
+ Array [
6
+ true,
7
+ ],
8
+ ]
9
+ `;
10
+
11
+ exports[`update link should update link url 1`] = `
12
+ Array [
13
+ Array [
14
+ "Some text",
15
+ ],
16
+ ]
17
+ `;
@@ -0,0 +1 @@
1
+ export { default as LinkControlDestination } from './LinkControlDestination';
@@ -0,0 +1 @@
1
+ export { default as LinkControl } from './LinkControl';
@@ -1,9 +1,9 @@
1
1
  import { h, toRef } from '@vue/composition-api';
2
2
  import { EditorContent } from '@tiptap/vue-2';
3
3
  import { shallowMount } from '@vue/test-utils';
4
- import { CORE_EXTENSIONS } from '../../extensions';
5
4
  import { useEditor } from '../useEditor';
6
5
  import { NodeFactory } from '../../__tests__/utils';
6
+ import { buildCoreExtensions } from '../../extensions/core';
7
7
 
8
8
  const TestComponent = {
9
9
  name: 'Test',
@@ -19,7 +19,7 @@ const TestComponent = {
19
19
  setup(props, { emit }) {
20
20
  const editor = useEditor({
21
21
  content: toRef(props, 'value'),
22
- extensions: CORE_EXTENSIONS,
22
+ extensions: buildCoreExtensions(),
23
23
  onChange: (content) => emit('input', content),
24
24
  onFocus: () => emit('focus')
25
25
  });
@@ -2,14 +2,12 @@ import { Editor } from '@tiptap/vue-2';
2
2
  import { onUnmounted, watch, reactive } from '@vue/composition-api';
3
3
  import { ContentNormalizer } from '../services';
4
4
 
5
- export function useEditor({ content, onChange, onFocus, extensions }) {
6
- const normalizer = new ContentNormalizer();
7
-
5
+ export function useEditor({ content, onChange, extensions }) {
8
6
  const editor = reactive(new Editor({
9
- content: normalizer.normalize(content.value),
7
+ content: ContentNormalizer.normalize(content.value),
10
8
  onUpdate: () => onChange(editor.getJSON()),
11
- onFocus: () => onFocus(),
12
- extensions
9
+ extensions,
10
+ injectCSS: false
13
11
  }));
14
12
 
15
13
  onUnmounted(() => editor.destroy());
@@ -18,7 +16,7 @@ export function useEditor({ content, onChange, onFocus, extensions }) {
18
16
  const isChanged = JSON.stringify(editor.getJSON()) !== JSON.stringify(value);
19
17
 
20
18
  if (isChanged) {
21
- const content = normalizer.normalize(value);
19
+ const content = ContentNormalizer.normalize(value);
22
20
 
23
21
  editor.commands.setContent(content, false);
24
22
  }
@@ -1,44 +1,39 @@
1
1
  import { createPopper } from '@popperjs/core';
2
- import { onUnmounted, onMounted, ref } from '@vue/composition-api';
3
- import { useElementRef } from '../components/base/composables';
2
+ import { useElementRef } from '../components/base';
4
3
 
5
- export function useToolbar({ wrapperRef, popperRef, offsets }) {
6
- let toolbar;
7
- const isShow = ref(false);
4
+ export function useToolbar({ wrapperRef, offsets, isActiveRef }) {
5
+ const wrapperEl = useElementRef(wrapperRef);
6
+ let popper;
8
7
 
9
- onMounted(() => {
10
- const wrapperEl = useElementRef(wrapperRef).value;
11
- const toolbarEl = useElementRef(popperRef).value;
12
-
13
- toolbar = createPopper(wrapperEl, toolbarEl, {
8
+ function mount(element) {
9
+ popper = createPopper(wrapperEl.value, element, {
14
10
  placement: 'top-start',
15
11
  modifiers: [
16
12
  {
17
13
  name: 'offset',
14
+ options: { offset: offsets }
15
+ },
16
+ {
17
+ name: 'preventOverflow',
18
18
  options: {
19
- offset: offsets
19
+ altAxis: true,
20
+ padding: 2
20
21
  }
22
+ },
23
+ {
24
+ name: 'flip',
25
+ enabled: false
21
26
  }
22
27
  ]
23
28
  });
24
- });
25
-
26
- onUnmounted(() => {
27
- toolbar.destroy();
28
- });
29
-
30
- function hide() {
31
- isShow.value = false;
32
- }
33
-
34
- function show() {
35
- isShow.value = true;
36
- this.update();
37
29
  }
38
30
 
39
- function update() {
40
- toolbar.update();
41
- }
31
+ const update = () => popper?.update();
42
32
 
43
- return { toolbar, show, hide, update, isShow };
33
+ return {
34
+ update,
35
+ mount,
36
+ isActiveRef,
37
+ offsets
38
+ };
44
39
  }
@@ -10,6 +10,7 @@ describe('out click', () => {
10
10
  await waitAsyncOperation();
11
11
 
12
12
  document.dispatchEvent(new Event('click'));
13
+ await waitAsyncOperation();
13
14
 
14
15
  expect(onOutClick).toHaveBeenCalled();
15
16
  });
@@ -22,6 +23,7 @@ describe('out click', () => {
22
23
  await waitAsyncOperation();
23
24
 
24
25
  el.dispatchEvent(new Event('click'));
26
+ await waitAsyncOperation();
25
27
 
26
28
  expect(onOutClick).not.toHaveBeenCalled();
27
29
  });
@@ -36,6 +38,7 @@ describe('out click', () => {
36
38
  await waitAsyncOperation();
37
39
 
38
40
  child.dispatchEvent(new Event('click'));
41
+ await waitAsyncOperation();
39
42
 
40
43
  expect(onOutClick).not.toHaveBeenCalled();
41
44
  });
@@ -50,6 +53,7 @@ describe('out click', () => {
50
53
  outClick.unbind(el);
51
54
 
52
55
  document.dispatchEvent(new Event('click'));
56
+ await waitAsyncOperation();
53
57
 
54
58
  expect(onOutClick).not.toHaveBeenCalled();
55
59
  });
@@ -65,6 +69,7 @@ describe('out click', () => {
65
69
  await waitAsyncOperation();
66
70
 
67
71
  document.dispatchEvent(new Event('click'));
72
+ await waitAsyncOperation();
68
73
 
69
74
  expect(onOutClick).toHaveBeenCalled();
70
75
  });
@@ -80,6 +85,7 @@ describe('out click', () => {
80
85
  await waitAsyncOperation();
81
86
 
82
87
  document.dispatchEvent(new Event('click'));
88
+ await waitAsyncOperation();
83
89
 
84
90
  expect(onOutClick).not.toHaveBeenCalled();
85
91
  });
@@ -1,5 +1,18 @@
1
+ import { ContextWindow } from '../services';
2
+
1
3
  const dataStorage = new WeakMap();
2
4
 
5
+ function toggleListener(toEnable, onClick) {
6
+ const args = ['click', onClick, { capture: true }];
7
+ const action = toEnable ? 'addEventListener' : 'removeEventListener';
8
+
9
+ if (ContextWindow.window !== window) {
10
+ window.document[action](...args);
11
+ }
12
+
13
+ ContextWindow.document[action](...args);
14
+ }
15
+
3
16
  export const outClick = {
4
17
  bind(el, { value }) {
5
18
  const onOutClick = value.onOutClick || value;
@@ -8,13 +21,15 @@ export const outClick = {
8
21
  function onClick(event) {
9
22
  const isInside = event.target === el || el.contains(event.target);
10
23
 
11
- if (event.target.isConnected && !isInside) onOutClick(event);
24
+ if (event.target.isConnected && !isInside) {
25
+ setTimeout(() => onOutClick(event));
26
+ }
12
27
  }
13
28
 
14
29
  dataStorage.set(el, { callback: onClick, isEnabled });
15
30
 
16
31
  if (isEnabled) {
17
- setTimeout(() => document.addEventListener('click', onClick));
32
+ setTimeout(() => toggleListener(true, onClick));
18
33
  }
19
34
  },
20
35
 
@@ -24,14 +39,12 @@ export const outClick = {
24
39
 
25
40
  if (isEnabled === data.isEnabled) return;
26
41
 
27
- isEnabled
28
- ? document.addEventListener('click', data.callback)
29
- : document.removeEventListener('click', data.callback);
42
+ toggleListener(isEnabled, data.callback);
30
43
 
31
44
  dataStorage.set(el, { callback: data.callback, isEnabled });
32
45
  },
33
46
 
34
47
  unbind(el) {
35
- document.removeEventListener('click', dataStorage.get(el).callback);
48
+ toggleListener(false, dataStorage.get(el).callback);
36
49
  }
37
50
  };
@@ -2,5 +2,14 @@ export const Alignments = Object.freeze({
2
2
  LEFT: 'left',
3
3
  CENTER: 'center',
4
4
  RIGHT: 'right',
5
- JUSTIFY: 'justify'
5
+ JUSTIFY: 'justify',
6
+
7
+ get values() {
8
+ return [
9
+ this.LEFT,
10
+ this.CENTER,
11
+ this.RIGHT,
12
+ this.JUSTIFY
13
+ ];
14
+ }
6
15
  });
@@ -0,0 +1,4 @@
1
+ export const LinkDestinations = Object.freeze({
2
+ URL: 'url',
3
+ BLOCK: 'block'
4
+ });
@@ -0,0 +1,4 @@
1
+ export const LinkTargets = Object.freeze({
2
+ BLANK: '_blank',
3
+ SELF: '_self'
4
+ });
@@ -8,6 +8,7 @@ export const TextSettings = Object.freeze({
8
8
  FONT_WEIGHT: 'font_weight',
9
9
  LINE_HEIGHT: 'line_height',
10
10
  TEXT_DECORATION: 'text_decoration',
11
+ SUPERSCRIPT: 'superscript',
11
12
 
12
13
  get attributes() {
13
14
  return [
@@ -24,7 +25,8 @@ export const TextSettings = Object.freeze({
24
25
  this.FONT_SIZE,
25
26
  this.FONT_STYLE,
26
27
  this.FONT_WEIGHT,
27
- this.TEXT_DECORATION
28
+ this.TEXT_DECORATION,
29
+ this.SUPERSCRIPT
28
30
  ];
29
31
  }
30
32
  });
@@ -4,3 +4,5 @@ export { Alignments } from './Alignments';
4
4
  export { NodeTypes } from './NodeTypes';
5
5
  export { ListTypes } from './ListTypes';
6
6
  export { TextSettings } from './TextSettings';
7
+ export { LinkTargets } from './LinkTargets';
8
+ export { LinkDestinations } from './LinkDestinations';
@@ -1,6 +1,6 @@
1
1
  import { Extension } from '@tiptap/vue-2';
2
2
  import { computed } from '@vue/composition-api';
3
- import { createCommand, createKeyboardShortcut, renderInlineSetting } from '../utils';
3
+ import { convertAlignment, createCommand, createKeyboardShortcut, renderInlineSetting } from '../utils';
4
4
  import { Alignments, NodeTypes, TextSettings } from '../enums';
5
5
 
6
6
  const DEFAULTS = {
@@ -19,19 +19,33 @@ export const Alignment = Extension.create({
19
19
  [TextSettings.ALIGNMENT]: {
20
20
  isRequired: false,
21
21
 
22
- parseHTML(el) {
23
- const value = el.style.textAlign;
22
+ parseHTML({ style }) {
23
+ const textAlign = convertAlignment(style.textAlign);
24
24
 
25
- return value ? { desktop: value, tablet: value, mobile: value } : null;
25
+ if (textAlign) {
26
+ return {
27
+ desktop: textAlign,
28
+ tablet: textAlign,
29
+ mobile: textAlign
30
+ };
31
+ }
32
+
33
+ const mobile = style.getPropertyValue('--zw-text-align-mobile') || null;
34
+ const tablet = style.getPropertyValue('--zw-text-align-tablet') || null;
35
+ const desktop = style.getPropertyValue('--zw-text-align-desktop') || null;
36
+
37
+ if (!mobile && !tablet && !desktop) return null;
38
+
39
+ return { desktop, tablet, mobile };
26
40
  },
27
41
 
28
42
  renderHTML(attrs) {
29
43
  if (!attrs.alignment) return null;
30
44
 
31
45
  return renderInlineSetting({
32
- mobile_text_align: attrs.alignment.mobile,
33
- tablet_text_align: attrs.alignment.tablet,
34
- desktop_text_align: attrs.alignment.desktop
46
+ text_align_mobile: attrs.alignment.mobile,
47
+ text_align_tablet: attrs.alignment.tablet,
48
+ text_align_desktop: attrs.alignment.desktop
35
49
  });
36
50
  }
37
51
  }
@@ -22,12 +22,20 @@ export const BackgroundColor = Mark.create({
22
22
  };
23
23
  },
24
24
 
25
- parseHTML: () => [
26
- {
27
- style: 'background-color',
28
- getAttrs: (value) => ({ value: convertColor(value) })
29
- }
30
- ],
25
+ parseHTML() {
26
+ const getAttrs = (value) => ({ value: convertColor(value) });
27
+
28
+ return [
29
+ {
30
+ style: '--zw-background-color',
31
+ getAttrs
32
+ },
33
+ {
34
+ style: 'background-color',
35
+ getAttrs
36
+ }
37
+ ];
38
+ },
31
39
 
32
40
  renderHTML({ HTMLAttributes: attrs }) {
33
41
  return renderMark({ background_color: attrs.value });