@zipify/wysiwyg 1.0.0-dev.17 → 1.0.0-dev.18

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 (98) hide show
  1. package/.github/dependabot.yaml +1 -0
  2. package/dist/wysiwyg.css +199 -12
  3. package/dist/wysiwyg.js +1 -1
  4. package/example/ExampleApp.vue +6 -2
  5. package/example/pageBlocks.js +31 -0
  6. package/example/presets.js +2 -2
  7. package/lib/Wysiwyg.vue +14 -3
  8. package/lib/assets/icons/link.svg +3 -0
  9. package/lib/assets/icons/unlink.svg +3 -0
  10. package/lib/components/base/Button.vue +21 -1
  11. package/lib/components/base/Checkbox.vue +89 -0
  12. package/lib/components/base/FieldLabel.vue +2 -1
  13. package/lib/components/base/Icon.vue +2 -2
  14. package/lib/components/base/Modal.vue +0 -1
  15. package/lib/components/base/TextField.vue +106 -0
  16. package/lib/components/base/__tests__/TextField.test.js +57 -0
  17. package/lib/components/base/__tests__/__snapshots__/TextField.test.js.snap +9 -0
  18. package/lib/components/base/composables/index.js +1 -0
  19. package/lib/components/base/composables/useValidator.js +19 -0
  20. package/lib/components/base/dropdown/Dropdown.vue +15 -3
  21. package/lib/components/base/dropdown/DropdownActivator.vue +19 -3
  22. package/lib/components/base/index.js +2 -0
  23. package/lib/components/toolbar/Toolbar.vue +5 -5
  24. package/lib/components/toolbar/ToolbarFull.vue +10 -2
  25. package/lib/components/toolbar/controls/FontSizeControl.vue +7 -0
  26. package/lib/components/toolbar/controls/UnderlineControl.vue +2 -2
  27. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +4 -0
  28. package/lib/components/toolbar/controls/index.js +1 -0
  29. package/lib/components/toolbar/controls/link/LinkControl.vue +152 -0
  30. package/lib/components/toolbar/controls/link/LinkControlApply.vue +35 -0
  31. package/lib/components/toolbar/controls/link/LinkControlHeader.vue +67 -0
  32. package/lib/components/toolbar/controls/link/composables/index.js +1 -0
  33. package/lib/components/toolbar/controls/link/composables/useLink.js +61 -0
  34. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +103 -0
  35. package/lib/components/toolbar/controls/link/destination/LinkControlPageBlock.vue +54 -0
  36. package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +52 -0
  37. package/lib/components/toolbar/controls/link/destination/index.js +1 -0
  38. package/lib/components/toolbar/controls/link/index.js +1 -0
  39. package/lib/composables/__tests__/useEditor.test.js +2 -2
  40. package/lib/composables/useEditor.js +2 -4
  41. package/lib/enums/LinkDestinations.js +4 -0
  42. package/lib/enums/LinkTargets.js +4 -0
  43. package/lib/enums/TextSettings.js +3 -1
  44. package/lib/enums/index.js +2 -0
  45. package/lib/extensions/Alignment.js +18 -6
  46. package/lib/extensions/BackgroundColor.js +14 -6
  47. package/lib/extensions/FontColor.js +14 -6
  48. package/lib/extensions/FontFamily.js +25 -8
  49. package/lib/extensions/FontSize.js +26 -13
  50. package/lib/extensions/FontStyle.js +23 -13
  51. package/lib/extensions/FontWeight.js +22 -14
  52. package/lib/extensions/LineHeight.js +11 -3
  53. package/lib/extensions/Link.js +101 -0
  54. package/lib/extensions/StylePreset.js +4 -2
  55. package/lib/extensions/TextDecoration.js +27 -12
  56. package/lib/extensions/__tests__/Alignment.test.js +11 -5
  57. package/lib/extensions/__tests__/BackgroundColor.test.js +11 -5
  58. package/lib/extensions/__tests__/CaseStyle.test.js +3 -5
  59. package/lib/extensions/__tests__/FontColor.test.js +11 -5
  60. package/lib/extensions/__tests__/FontFamily.test.js +32 -7
  61. package/lib/extensions/__tests__/FontSize.test.js +11 -5
  62. package/lib/extensions/__tests__/FontStyle.test.js +11 -5
  63. package/lib/extensions/__tests__/FontWeight.test.js +11 -5
  64. package/lib/extensions/__tests__/LineHeight.test.js +11 -5
  65. package/lib/extensions/__tests__/StylePreset.test.js +70 -6
  66. package/lib/extensions/__tests__/TextDecoration.test.js +27 -5
  67. package/lib/extensions/__tests__/__snapshots__/Alignment.test.js.snap +26 -2
  68. package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +30 -1
  69. package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +18 -1
  70. package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +88 -1
  71. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +33 -2
  72. package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +25 -4
  73. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +30 -1
  74. package/lib/extensions/__tests__/__snapshots__/LineHeight.test.js.snap +26 -2
  75. package/lib/extensions/__tests__/__snapshots__/StylePreset.test.js.snap +6 -2
  76. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +93 -3
  77. package/lib/extensions/core/CopyPasteProcessor.js +10 -0
  78. package/lib/extensions/core/TextProcessor.js +10 -0
  79. package/lib/extensions/core/__tests__/NodeProcessor.test.js +3 -5
  80. package/lib/extensions/core/__tests__/SelectionProcessor.test.js +3 -5
  81. package/lib/extensions/core/__tests__/TextProcessor.test.js +138 -12
  82. package/lib/extensions/core/__tests__/__snapshots__/TextProcessor.test.js.snap +26 -0
  83. package/lib/extensions/core/index.js +11 -2
  84. package/lib/extensions/core/plugins/PastePlugin.js +48 -0
  85. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +20 -0
  86. package/lib/extensions/core/plugins/index.js +1 -0
  87. package/lib/extensions/index.js +41 -34
  88. package/lib/extensions/list/__tests__/List.test.js +3 -5
  89. package/lib/extensions/list/__tests__/__snapshots__/List.test.js.snap +45 -15
  90. package/lib/injectionTokens.js +2 -1
  91. package/lib/services/ContentNormalizer.js +62 -20
  92. package/lib/services/__tests__/ContentNormalizer.test.js +40 -7
  93. package/lib/styles/content.css +17 -9
  94. package/lib/styles/helpers/offsets.css +16 -0
  95. package/lib/styles/variables.css +6 -0
  96. package/lib/utils/__tests__/__snapshots__/renderInlineSetting.test.js.snap +4 -4
  97. package/lib/utils/renderInlineSetting.js +1 -1
  98. package/package.json +11 -9
@@ -33,12 +33,18 @@
33
33
 
34
34
  <ToolbarGroup>
35
35
  <LineHeightControl />
36
+ </ToolbarGroup>
37
+
38
+ <ToolbarDivider vertical />
39
+
40
+ <ToolbarGroup>
36
41
  <ListControl />
37
42
  </ToolbarGroup>
38
43
 
39
44
  <ToolbarDivider vertical />
40
45
 
41
46
  <ToolbarGroup>
47
+ <LinkControl />
42
48
  <RemoveFormatControl />
43
49
  </ToolbarGroup>
44
50
  </ToolbarRow>
@@ -64,7 +70,8 @@ import {
64
70
  SuperscriptControl,
65
71
  UnderlineControl,
66
72
  ListControl,
67
- RemoveFormatControl
73
+ RemoveFormatControl,
74
+ LinkControl
68
75
  } from './controls';
69
76
 
70
77
  export default {
@@ -88,7 +95,8 @@ export default {
88
95
  AlignmentControl,
89
96
  LineHeightControl,
90
97
  ListControl,
91
- RemoveFormatControl
98
+ RemoveFormatControl,
99
+ LinkControl
92
100
  }
93
101
  };
94
102
  </script>
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <Dropdown
3
+ class="zw-font-size-control"
3
4
  :options="options"
4
5
  :value="currentValue"
5
6
  @change="apply"
@@ -43,3 +44,9 @@ export default {
43
44
  }
44
45
  };
45
46
  </script>
47
+
48
+ <style scoped>
49
+ .zw-font-size-control {
50
+ width: 64px;
51
+ }
52
+ </style>
@@ -11,7 +11,7 @@
11
11
  </template>
12
12
 
13
13
  <script>
14
- import { inject } from '@vue/composition-api';
14
+ import { inject, computed } from '@vue/composition-api';
15
15
  import { Button, Icon } from '../../base';
16
16
  import { InjectionTokens } from '../../../injectionTokens';
17
17
  import { tooltip } from '../../../directives';
@@ -31,7 +31,7 @@ export default {
31
31
  setup() {
32
32
  const editor = inject(InjectionTokens.EDITOR);
33
33
 
34
- const currentValue = editor.commands.isUnderline();
34
+ const currentValue = computed(() => editor.commands.isUnderline().value);
35
35
  const apply = () => editor.chain().focus().toggleUnderline().run();
36
36
 
37
37
  return {
@@ -12,6 +12,10 @@ const createEditor = ({ isUnderline } = {}) => ({
12
12
  run: jest.fn()
13
13
  },
14
14
 
15
+ isActive() {
16
+ return false;
17
+ },
18
+
15
19
  chain() {
16
20
  return this.commands;
17
21
  }
@@ -14,3 +14,4 @@ export { default as AlignmentDeviceControl } from './AlignmentDeviceControl';
14
14
  export { default as LineHeightControl } from './LineHeightControl';
15
15
  export { default as ListControl } from './ListControl';
16
16
  export { default as RemoveFormatControl } from './RemoveFormatControl';
17
+ export { LinkControl } from './link';
@@ -0,0 +1,152 @@
1
+ <template>
2
+ <div class="zw-position--relative" ref="wrapperRef">
3
+ <Button icon skin="toolbar" :active="isActive" @click="toggler.open" v-tooltip="'Link'">
4
+ <Icon name="link" size="28px" auto-color />
5
+ </Button>
6
+
7
+ <Modal class="zw-link-modal" :toggler="toggler" ref="modalRef">
8
+ <LinkControlHeader @remove-link="removeLink" />
9
+
10
+ <div class="zw-link-modal__body">
11
+ <TextField
12
+ class="zw-margin-bottom--sm"
13
+ :value="link.linkData.value.text"
14
+ label="Text to display"
15
+ placeholder="Type Text"
16
+ :error="nameValidator.error.value"
17
+ @input="link.updateText"
18
+ />
19
+
20
+ <LinkControlDestination
21
+ class="zw-margin-bottom--md"
22
+ :link="link"
23
+ :validator="urlValidator"
24
+ @reset-errors="resetErrors"
25
+ />
26
+
27
+ <LinkControlApply @cancel="toggler.close" @apply="applyLink" />
28
+ </div>
29
+ </Modal>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ import { computed, ref, inject } from '@vue/composition-api';
35
+ import { InjectionTokens } from '../../../../injectionTokens';
36
+ import { tooltip } from '../../../../directives';
37
+ import { useValidator } from '../../../base/composables';
38
+ import { Button, Icon, Modal, TextField, useModalToggler } from '../../../base';
39
+ import LinkControlHeader from './LinkControlHeader';
40
+ import LinkControlApply from './LinkControlApply';
41
+ import { useLink } from './composables';
42
+ import { LinkControlDestination } from './destination';
43
+
44
+ export default {
45
+ name: 'LinkControl',
46
+
47
+ components: {
48
+ LinkControlDestination,
49
+ LinkControlApply,
50
+ LinkControlHeader,
51
+ TextField,
52
+ Modal,
53
+ Icon,
54
+ Button
55
+ },
56
+
57
+ directives: {
58
+ tooltip
59
+ },
60
+
61
+ setup() {
62
+ const wrapperRef = ref(null);
63
+ const modalRef = ref(null);
64
+ const nameError = ref(false);
65
+ const urlError = ref(false);
66
+
67
+ const editor = inject(InjectionTokens.EDITOR);
68
+
69
+ const link = useLink();
70
+
71
+ const resetErrors = () => {
72
+ nameError.value = null;
73
+ urlError.value = null;
74
+ };
75
+
76
+ const onBeforeOpened = () => {
77
+ editor.commands.extendMarkRange('link');
78
+ resetErrors();
79
+ link.prepareInitialFields();
80
+ };
81
+
82
+ const toggler = useModalToggler({
83
+ onBeforeOpened: () => onBeforeOpened(),
84
+ wrapperRef,
85
+ modalRef
86
+ });
87
+
88
+ const isActive = computed(() => toggler.isOpened.value || editor.isActive('link'));
89
+ const isEmpty = () => {
90
+ return link.linkData.value.text ? false : 'Can\'t be empty';
91
+ };
92
+ const isUrl = () => {
93
+ if (link.currentDestination.value.id !== 'url') return false;
94
+
95
+ return /(^(https?:\/\/|\/)(?:www\.|(?!www))?[^\s])/.test(link.destinationHrefs.value.url) ? false : 'Please enter a valid URL';
96
+ };
97
+
98
+ const nameValidator = useValidator({
99
+ validations: [isEmpty],
100
+ value: link.linkData.value.text
101
+ });
102
+
103
+ const urlValidator = useValidator({
104
+ validations: [isUrl],
105
+ value: link.linkData.value.text
106
+ });
107
+
108
+ const applyLink = () => {
109
+ urlError.value = urlValidator.validate();
110
+ nameError.value = nameValidator.validate();
111
+
112
+ if (urlError.value || nameError.value) return;
113
+
114
+ link.apply();
115
+ toggler.close();
116
+ };
117
+
118
+ const removeLink = () => {
119
+ link.removeLink();
120
+ toggler.close();
121
+ };
122
+
123
+ return {
124
+ wrapperRef,
125
+ modalRef,
126
+ link,
127
+ toggler,
128
+ isActive,
129
+ nameValidator,
130
+ urlValidator,
131
+ nameError,
132
+ resetErrors,
133
+ applyLink,
134
+ removeLink
135
+ };
136
+ }
137
+ };
138
+ </script>
139
+
140
+ <style scoped>
141
+ .zw-link-modal {
142
+ width: 266px;
143
+ }
144
+
145
+ .zw-link-modal__body {
146
+ padding: var(--zw-offset-sm);
147
+ }
148
+
149
+ ::v-deep .zw-link-modal-dropdown__option {
150
+ width: 234px;
151
+ }
152
+ </style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <div class="zw-link-modal__apply">
3
+ <Button class="zw-margin-right--xs" skin="secondary" @click="cancel">
4
+ Cancel
5
+ </Button>
6
+
7
+ <Button @click="apply" skin="primary">
8
+ Save
9
+ </Button>
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ import { Button } from '../../../base';
15
+
16
+ export default {
17
+ name: 'LinkControlApply',
18
+
19
+ components: { Button },
20
+
21
+ setup(_, { emit }) {
22
+ const cancel = () => emit('cancel');
23
+ const apply = () => emit('apply');
24
+
25
+ return { cancel, apply };
26
+ }
27
+ };
28
+ </script>
29
+
30
+ <style scoped>
31
+ .zw-link-modal__apply {
32
+ display: flex;
33
+ justify-content: flex-end;
34
+ }
35
+ </style>
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <div class="zw-link-modal-header">
3
+ <span class="zw-link-modal-header__title">Link</span>
4
+
5
+ <Button class="zw-link-modal-header__unlink-button" :disabled="!isLink" @click="removeLink">
6
+ <Icon class="zw-link-modal-header__unlink-icon" name="unlink" size="14px" auto-color />
7
+ Remove
8
+ </Button>
9
+ </div>
10
+ </template>
11
+
12
+ <script>
13
+ import { computed, inject } from '@vue/composition-api';
14
+ import { Icon, Button } from '../../../base';
15
+ import { InjectionTokens } from '../../../../injectionTokens';
16
+
17
+ export default {
18
+ name: 'LinkControlHeader',
19
+
20
+ components: { Icon, Button },
21
+
22
+ setup(_, { emit }) {
23
+ const editor = inject(InjectionTokens.EDITOR);
24
+ const isLink = computed(() => editor.isActive('link'));
25
+
26
+ const removeLink = () => emit('remove-link');
27
+
28
+ return { isLink, removeLink };
29
+ }
30
+ };
31
+ </script>
32
+
33
+ <style scoped>
34
+ .zw-link-modal-header {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: space-between;
38
+ padding: var(--zw-offset-sm);
39
+ border-bottom: 2px solid rgb(var(--zw-color-n5));
40
+ }
41
+
42
+ .zw-link-modal-header__title {
43
+ text-transform: uppercase;
44
+ font-weight: var(--zw-font-weight-semibold);
45
+ font-size: var(--zw-font-size-xxs);
46
+ color: rgb(var(--zw-color-white));
47
+ }
48
+
49
+ .zw-link-modal-header__unlink-icon {
50
+ margin-right: var(--zw-offset-xxs);
51
+ }
52
+
53
+ .zw-link-modal-header__unlink-button {
54
+ color: rgb(var(--zw-color-n80));
55
+ font-size: var(--zw-font-size-xxs);
56
+ transition: 0.1s opacity ease-out;
57
+ will-change: opacity;
58
+ }
59
+
60
+ .zw-link-modal-header__unlink-button:disabled {
61
+ opacity: 0.35;
62
+ }
63
+
64
+ .zw-link-modal-header__unlink-button:hover {
65
+ color: rgb(var(--zw-color-white));
66
+ }
67
+ </style>
@@ -0,0 +1 @@
1
+ export { useLink } from './useLink';
@@ -0,0 +1,61 @@
1
+ import { ref, inject } from '@vue/composition-api';
2
+ import { LinkTargets, LinkDestinations } from '../../../../../enums';
3
+ import { InjectionTokens } from '../../../../../injectionTokens';
4
+
5
+ export function useLink() {
6
+ const editor = inject(InjectionTokens.EDITOR);
7
+ const pageBlocks = inject(InjectionTokens.PAGE_BLOCKS);
8
+
9
+ const linkData = ref({ text: '', target: LinkTargets.SELF, destination: LinkDestinations.URL });
10
+ const destinationHrefs = ref({ block: pageBlocks.value[0].id, url: '' });
11
+ const currentDestination = ref({ id: LinkDestinations.URL });
12
+
13
+ function updateTarget(isChecked) {
14
+ linkData.value.target = isChecked ? LinkTargets.BLANK : LinkTargets.SELF;
15
+ }
16
+
17
+ function prepareInitialFields() {
18
+ linkData.value.text = editor.commands.getSelectedText();
19
+ currentDestination.value.id = editor.getAttributes('link').destination || LinkDestinations.URL;
20
+ destinationHrefs.value[currentDestination.value.id] = editor.getAttributes('link').href || '';
21
+ linkData.value.target = editor.getAttributes('link').target || LinkTargets.SELF;
22
+ }
23
+
24
+ function apply() {
25
+ editor
26
+ .chain()
27
+ .focus()
28
+ .applyLink({
29
+ href: destinationHrefs.value[currentDestination.value.id],
30
+ text: linkData.value.text,
31
+ target: linkData.value.target,
32
+ destination: currentDestination.value.id
33
+ })
34
+ .run();
35
+ }
36
+
37
+ function removeLink() {
38
+ editor.chain().focus().unsetLink().run();
39
+ }
40
+
41
+ function updateLink(value) {
42
+ destinationHrefs.value[currentDestination.value.id] = value;
43
+ }
44
+
45
+ function updateText(text) {
46
+ linkData.value.text = text;
47
+ }
48
+
49
+ return {
50
+ editor,
51
+ linkData,
52
+ destinationHrefs,
53
+ currentDestination,
54
+ prepareInitialFields,
55
+ updateTarget,
56
+ updateLink,
57
+ apply,
58
+ removeLink,
59
+ updateText
60
+ };
61
+ }
@@ -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
+ @updateLink="updateLink"
30
+ @updateTarget="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('updateLink', value);
47
+ const updateTarget = (value) => emit('updateTarget', value);
48
+
49
+ return { updateLink, updateTarget };
50
+ }
51
+ };
52
+ </script>
@@ -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
  });
@@ -3,10 +3,8 @@ import { onUnmounted, watch, reactive } from '@vue/composition-api';
3
3
  import { ContentNormalizer } from '../services';
4
4
 
5
5
  export function useEditor({ content, onChange, extensions }) {
6
- const normalizer = new ContentNormalizer();
7
-
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
9
  extensions
12
10
  }));
@@ -17,7 +15,7 @@ export function useEditor({ content, onChange, extensions }) {
17
15
  const isChanged = JSON.stringify(editor.getJSON()) !== JSON.stringify(value);
18
16
 
19
17
  if (isChanged) {
20
- const content = normalizer.normalize(value);
18
+ const content = ContentNormalizer.normalize(value);
21
19
 
22
20
  editor.commands.setContent(content, false);
23
21
  }
@@ -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
+ });