@zipify/wysiwyg 1.0.0-dev.16 → 1.0.0-dev.19

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 (100) hide show
  1. package/.github/dependabot.yaml +1 -0
  2. package/dist/wysiwyg.css +293 -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 +16 -6
  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 +3 -1
  23. package/lib/components/toolbar/Toolbar.vue +48 -8
  24. package/lib/components/toolbar/ToolbarFull.vue +10 -2
  25. package/lib/components/toolbar/__tests__/Toolbar.test.js +6 -0
  26. package/lib/components/toolbar/controls/FontSizeControl.vue +7 -0
  27. package/lib/components/toolbar/controls/UnderlineControl.vue +2 -2
  28. package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +4 -0
  29. package/lib/components/toolbar/controls/index.js +1 -0
  30. package/lib/components/toolbar/controls/link/LinkControl.vue +152 -0
  31. package/lib/components/toolbar/controls/link/LinkControlApply.vue +35 -0
  32. package/lib/components/toolbar/controls/link/LinkControlHeader.vue +67 -0
  33. package/lib/components/toolbar/controls/link/composables/index.js +1 -0
  34. package/lib/components/toolbar/controls/link/composables/useLink.js +61 -0
  35. package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +103 -0
  36. package/lib/components/toolbar/controls/link/destination/LinkControlPageBlock.vue +54 -0
  37. package/lib/components/toolbar/controls/link/destination/LinkControlUrl.vue +52 -0
  38. package/lib/components/toolbar/controls/link/destination/index.js +1 -0
  39. package/lib/components/toolbar/controls/link/index.js +1 -0
  40. package/lib/composables/__tests__/useEditor.test.js +2 -2
  41. package/lib/composables/useEditor.js +4 -5
  42. package/lib/composables/useToolbar.js +15 -19
  43. package/lib/enums/LinkDestinations.js +4 -0
  44. package/lib/enums/LinkTargets.js +4 -0
  45. package/lib/enums/TextSettings.js +3 -1
  46. package/lib/enums/index.js +2 -0
  47. package/lib/extensions/Alignment.js +18 -6
  48. package/lib/extensions/BackgroundColor.js +14 -6
  49. package/lib/extensions/FontColor.js +14 -6
  50. package/lib/extensions/FontFamily.js +25 -8
  51. package/lib/extensions/FontSize.js +26 -13
  52. package/lib/extensions/FontStyle.js +23 -13
  53. package/lib/extensions/FontWeight.js +22 -14
  54. package/lib/extensions/LineHeight.js +11 -3
  55. package/lib/extensions/Link.js +101 -0
  56. package/lib/extensions/StylePreset.js +4 -2
  57. package/lib/extensions/TextDecoration.js +27 -12
  58. package/lib/extensions/__tests__/Alignment.test.js +11 -5
  59. package/lib/extensions/__tests__/BackgroundColor.test.js +11 -5
  60. package/lib/extensions/__tests__/CaseStyle.test.js +3 -5
  61. package/lib/extensions/__tests__/FontColor.test.js +11 -5
  62. package/lib/extensions/__tests__/FontFamily.test.js +32 -7
  63. package/lib/extensions/__tests__/FontSize.test.js +11 -5
  64. package/lib/extensions/__tests__/FontStyle.test.js +11 -5
  65. package/lib/extensions/__tests__/FontWeight.test.js +11 -5
  66. package/lib/extensions/__tests__/LineHeight.test.js +11 -5
  67. package/lib/extensions/__tests__/StylePreset.test.js +70 -6
  68. package/lib/extensions/__tests__/TextDecoration.test.js +27 -5
  69. package/lib/extensions/__tests__/__snapshots__/Alignment.test.js.snap +26 -2
  70. package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +30 -1
  71. package/lib/extensions/__tests__/__snapshots__/FontColor.test.js.snap +18 -1
  72. package/lib/extensions/__tests__/__snapshots__/FontFamily.test.js.snap +88 -1
  73. package/lib/extensions/__tests__/__snapshots__/FontSize.test.js.snap +33 -2
  74. package/lib/extensions/__tests__/__snapshots__/FontStyle.test.js.snap +25 -4
  75. package/lib/extensions/__tests__/__snapshots__/FontWeight.test.js.snap +30 -1
  76. package/lib/extensions/__tests__/__snapshots__/LineHeight.test.js.snap +26 -2
  77. package/lib/extensions/__tests__/__snapshots__/StylePreset.test.js.snap +6 -2
  78. package/lib/extensions/__tests__/__snapshots__/TextDecoration.test.js.snap +93 -3
  79. package/lib/extensions/core/CopyPasteProcessor.js +10 -0
  80. package/lib/extensions/core/TextProcessor.js +10 -0
  81. package/lib/extensions/core/__tests__/NodeProcessor.test.js +3 -5
  82. package/lib/extensions/core/__tests__/SelectionProcessor.test.js +3 -5
  83. package/lib/extensions/core/__tests__/TextProcessor.test.js +138 -12
  84. package/lib/extensions/core/__tests__/__snapshots__/TextProcessor.test.js.snap +26 -0
  85. package/lib/extensions/core/index.js +11 -2
  86. package/lib/extensions/core/plugins/PastePlugin.js +48 -0
  87. package/lib/extensions/core/plugins/ProseMirrorPlugin.js +20 -0
  88. package/lib/extensions/core/plugins/index.js +1 -0
  89. package/lib/extensions/index.js +41 -34
  90. package/lib/extensions/list/__tests__/List.test.js +3 -5
  91. package/lib/extensions/list/__tests__/__snapshots__/List.test.js.snap +45 -15
  92. package/lib/injectionTokens.js +2 -1
  93. package/lib/services/ContentNormalizer.js +62 -20
  94. package/lib/services/__tests__/ContentNormalizer.test.js +40 -7
  95. package/lib/styles/content.css +96 -9
  96. package/lib/styles/helpers/offsets.css +16 -0
  97. package/lib/styles/variables.css +6 -0
  98. package/lib/utils/__tests__/__snapshots__/renderInlineSetting.test.js.snap +4 -4
  99. package/lib/utils/renderInlineSetting.js +1 -1
  100. package/package.json +11 -9
@@ -1,13 +1,15 @@
1
1
  <template>
2
- <Transition name="zw-toolbar-" :duration="800">
3
- <div class="zw-toolbar">
4
- <component :is="toolbarComponent" />
5
- </div>
6
- </Transition>
2
+ <keep-alive>
3
+ <transition name="zw-toolbar-" duration="150">
4
+ <div class="zw-toolbar" :style="toolbarStyles" ref="toolbarRef" v-if="isVisible">
5
+ <component :is="toolbarComponent" />
6
+ </div>
7
+ </transition>
8
+ </keep-alive>
7
9
  </template>
8
10
 
9
11
  <script>
10
- import { computed } from '@vue/composition-api';
12
+ import { computed, ref, watch } from '@vue/composition-api';
11
13
  import { Devices } from '../../enums';
12
14
  import ToolbarFull from './ToolbarFull';
13
15
  import ToolbarDevice from './ToolbarDevice';
@@ -19,6 +21,11 @@ export default {
19
21
  device: {
20
22
  type: String,
21
23
  required: true
24
+ },
25
+
26
+ toolbar: {
27
+ type: Object,
28
+ required: true
22
29
  }
23
30
  },
24
31
 
@@ -26,8 +33,23 @@ export default {
26
33
  const toolbarComponent = computed(() => {
27
34
  return props.device === Devices.DESKTOP ? ToolbarFull : ToolbarDevice;
28
35
  });
36
+ const isVisible = computed(() => props.toolbar.isActiveRef.value);
37
+ const toolbarRef = ref(null);
38
+
39
+ watch(toolbarRef, (toolbarEl) => {
40
+ toolbarEl && props.toolbar.mount(toolbarEl);
41
+ });
42
+
43
+ const toolbarStyles = computed(() => ({
44
+ '--zw-toolbar-offset-y': `${props.toolbar.offsets[1]}px`
45
+ }));
29
46
 
30
- return { toolbarComponent };
47
+ return {
48
+ toolbarComponent,
49
+ isVisible,
50
+ toolbarRef,
51
+ toolbarStyles
52
+ };
31
53
  }
32
54
  };
33
55
  </script>
@@ -40,9 +62,27 @@ export default {
40
62
  z-index: 999999;
41
63
  }
42
64
 
65
+ .zw-toolbar::before,
66
+ .zw-toolbar::after {
67
+ content: "";
68
+ display: block;
69
+ width: 100%;
70
+ height: calc(var(--zw-toolbar-offset-y) + 4px);
71
+ position: absolute;
72
+ --zw-toolbar-safe-zone: calc(-1 * var(--zw-toolbar-offset-y));
73
+ }
74
+
75
+ .zw-toolbar::before {
76
+ top: var(--zw-toolbar-safe-zone);
77
+ }
78
+
79
+ .zw-toolbar::after {
80
+ bottom: var(--zw-toolbar-safe-zone);
81
+ }
82
+
43
83
  .zw-toolbar--enter-active,
44
84
  .zw-toolbar--leave-active {
45
- transition: opacity 0.15s ease-out;
85
+ transition: opacity 150ms ease-out;
46
86
  }
47
87
 
48
88
  .zw-toolbar--leave-active {
@@ -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,4 +1,5 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
+ import { ref } from '@vue/composition-api';
2
3
  import { Devices } from '../../../enums';
3
4
  import Toolbar from '../Toolbar';
4
5
  import ToolbarFull from '../ToolbarFull';
@@ -7,6 +8,11 @@ import ToolbarDevice from '../ToolbarDevice';
7
8
  function createComponent({ device }) {
8
9
  return shallowMount(Toolbar, {
9
10
  propsData: {
11
+ toolbar: {
12
+ mount: jest.fn(),
13
+ isActiveRef: ref(true),
14
+ offsets: [0, 8]
15
+ },
10
16
  device: device ?? Devices.DESKTOP
11
17
  }
12
18
  });
@@ -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
+