@zipify/wysiwyg 1.0.0-dev.68 → 1.0.0-dev.70
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.
- package/dist/wysiwyg.css +12 -12
- package/dist/wysiwyg.mjs +49 -30
- package/lib/components/base/Checkbox.vue +3 -5
- package/lib/components/base/colorPicker/ColorPicker.vue +7 -9
- package/lib/components/base/colorPicker/composables/__tests__/usePickerApi.test.js +5 -5
- package/lib/components/base/colorPicker/composables/usePickerApi.js +15 -8
- package/lib/components/toolbar/controls/link/LinkControl.vue +8 -3
- package/lib/components/toolbar/controls/link/composables/__tests__/__snapshots__/useLink.test.js.snap +7 -0
- package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +59 -1
- package/lib/components/toolbar/controls/link/composables/useLink.js +23 -2
- package/lib/components/toolbar/controls/link/destination/LinkControlDestination.vue +1 -1
- package/package.json +1 -1
package/dist/wysiwyg.css
CHANGED
|
@@ -405,27 +405,27 @@
|
|
|
405
405
|
color: rgb(var(--zw-color-red));
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
.zw-checkbox[data-v-
|
|
408
|
+
.zw-checkbox[data-v-15e487f5] {
|
|
409
409
|
display: inline-flex;
|
|
410
410
|
align-items: center;
|
|
411
411
|
position: relative;
|
|
412
412
|
cursor: pointer;
|
|
413
413
|
padding: var(--zw-offset-xxs) var(--zw-offset-xxs) var(--zw-offset-xxs) 0;
|
|
414
414
|
}
|
|
415
|
-
.zw-checkbox__field + .zw-checkbox__indicator[data-v-
|
|
415
|
+
.zw-checkbox__field + .zw-checkbox__indicator[data-v-15e487f5] {
|
|
416
416
|
color: var(--zw-color-n200);
|
|
417
417
|
box-shadow: inset 0 0 0 2px currentColor;
|
|
418
418
|
}
|
|
419
|
-
.zw-checkbox:hover .zw-checkbox__indicator[data-v-
|
|
419
|
+
.zw-checkbox:hover .zw-checkbox__indicator[data-v-15e487f5] {
|
|
420
420
|
box-shadow: inset 0 0 0 2px rgb(var(--zw-color-green));
|
|
421
421
|
}
|
|
422
|
-
.zw-checkbox__field:checked + .zw-checkbox__indicator[data-v-
|
|
422
|
+
.zw-checkbox__field:checked + .zw-checkbox__indicator[data-v-15e487f5] {
|
|
423
423
|
color: rgb(var(--zw-color-green))
|
|
424
424
|
}
|
|
425
|
-
.zw-checkbox .zw-checkbox__field:checked + .zw-checkbox__indicator[data-v-
|
|
425
|
+
.zw-checkbox .zw-checkbox__field:checked + .zw-checkbox__indicator[data-v-15e487f5] {
|
|
426
426
|
background-color: rgb(var(--zw-color-green));
|
|
427
427
|
}
|
|
428
|
-
.zw-checkbox .zw-checkbox__indicator[data-v-
|
|
428
|
+
.zw-checkbox .zw-checkbox__indicator[data-v-15e487f5]::after {
|
|
429
429
|
content: "";
|
|
430
430
|
display: block;
|
|
431
431
|
height: 16px;
|
|
@@ -437,16 +437,16 @@
|
|
|
437
437
|
background-size: 16px;
|
|
438
438
|
background-position: center center;
|
|
439
439
|
}
|
|
440
|
-
.zw-checkbox__field:not(:checked) + .zw-checkbox__indicator[data-v-
|
|
440
|
+
.zw-checkbox__field:not(:checked) + .zw-checkbox__indicator[data-v-15e487f5]::after {
|
|
441
441
|
transform: scale(0);
|
|
442
442
|
}
|
|
443
|
-
.zw-checkbox__field[data-v-
|
|
443
|
+
.zw-checkbox__field[data-v-15e487f5] {
|
|
444
444
|
position: absolute;
|
|
445
445
|
opacity: 0;
|
|
446
446
|
height: 0;
|
|
447
447
|
width: 0;
|
|
448
448
|
}
|
|
449
|
-
.zw-checkbox__label[data-v-
|
|
449
|
+
.zw-checkbox__label[data-v-15e487f5] {
|
|
450
450
|
font-size: var(--zw-font-size-xs);
|
|
451
451
|
}
|
|
452
452
|
|
|
@@ -620,13 +620,13 @@
|
|
|
620
620
|
justify-content: flex-end;
|
|
621
621
|
}
|
|
622
622
|
|
|
623
|
-
.zw-link-modal[data-v-
|
|
623
|
+
.zw-link-modal[data-v-28e9497e] {
|
|
624
624
|
width: 266px;
|
|
625
625
|
}
|
|
626
|
-
.zw-link-modal__body[data-v-
|
|
626
|
+
.zw-link-modal__body[data-v-28e9497e] {
|
|
627
627
|
padding: var(--zw-offset-sm);
|
|
628
628
|
}
|
|
629
|
-
[data-v-
|
|
629
|
+
[data-v-28e9497e] .zw-link-modal-dropdown__option {
|
|
630
630
|
width: 234px;
|
|
631
631
|
}
|
|
632
632
|
|
package/dist/wysiwyg.mjs
CHANGED
|
@@ -19895,7 +19895,7 @@ var render$x = function __render__13() {
|
|
|
19895
19895
|
"checked": _vm.value
|
|
19896
19896
|
},
|
|
19897
19897
|
on: {
|
|
19898
|
-
"
|
|
19898
|
+
"click": _vm.onCheckedChanged
|
|
19899
19899
|
}
|
|
19900
19900
|
}), _c("span", {
|
|
19901
19901
|
staticClass: "zw-checkbox__indicator zw-margin-right--xs"
|
|
@@ -19918,10 +19918,8 @@ const __vue2_script$x = {
|
|
|
19918
19918
|
default: null
|
|
19919
19919
|
}
|
|
19920
19920
|
},
|
|
19921
|
-
setup(
|
|
19922
|
-
const onCheckedChanged = (
|
|
19923
|
-
emit("input", event.target.checked);
|
|
19924
|
-
};
|
|
19921
|
+
setup(props, { emit }) {
|
|
19922
|
+
const onCheckedChanged = () => emit("input", !props.value);
|
|
19925
19923
|
return { onCheckedChanged };
|
|
19926
19924
|
}
|
|
19927
19925
|
};
|
|
@@ -19932,7 +19930,7 @@ var __component__$x = /* @__PURE__ */ normalizeComponent(
|
|
|
19932
19930
|
staticRenderFns$x,
|
|
19933
19931
|
false,
|
|
19934
19932
|
__vue2_injectStyles$x,
|
|
19935
|
-
"
|
|
19933
|
+
"15e487f5",
|
|
19936
19934
|
null,
|
|
19937
19935
|
null
|
|
19938
19936
|
);
|
|
@@ -20409,30 +20407,35 @@ function __vue2_injectStyles$r(context) {
|
|
|
20409
20407
|
const Dropdown = /* @__PURE__ */ function() {
|
|
20410
20408
|
return __component__$r.exports;
|
|
20411
20409
|
}();
|
|
20412
|
-
function usePickerApi({ pickerRef,
|
|
20413
|
-
const editingColor = ref(initialColorRef.value);
|
|
20410
|
+
function usePickerApi({ pickerRef, colorRef, onChange, onClosed, onBeforeOpened }) {
|
|
20414
20411
|
const isOpened = computed(() => {
|
|
20415
20412
|
var _a, _b;
|
|
20416
20413
|
return (_b = (_a = pickerRef.value) == null ? void 0 : _a.isVisible) != null ? _b : false;
|
|
20417
20414
|
});
|
|
20415
|
+
let initialColor;
|
|
20418
20416
|
function open() {
|
|
20419
20417
|
onBeforeOpened();
|
|
20420
|
-
|
|
20418
|
+
initialColor = colorRef.value;
|
|
20421
20419
|
pickerRef.value.open(pickerRef.value.$el);
|
|
20422
20420
|
}
|
|
20423
20421
|
function close2() {
|
|
20424
20422
|
var _a;
|
|
20425
|
-
(_a = pickerRef.value) == null ? void 0 : _a.close(
|
|
20426
|
-
onClosed(
|
|
20423
|
+
(_a = pickerRef.value) == null ? void 0 : _a.close(colorRef.value);
|
|
20424
|
+
onClosed();
|
|
20427
20425
|
}
|
|
20428
20426
|
function toggle() {
|
|
20429
20427
|
isOpened.value ? close2() : open();
|
|
20430
20428
|
}
|
|
20431
20429
|
function cancel() {
|
|
20432
20430
|
var _a;
|
|
20433
|
-
|
|
20434
|
-
(
|
|
20431
|
+
const color = initialColor != null ? initialColor : colorRef.value;
|
|
20432
|
+
onChange(color);
|
|
20433
|
+
(_a = pickerRef.value) == null ? void 0 : _a.close(color);
|
|
20435
20434
|
}
|
|
20435
|
+
const editingColor = computed({
|
|
20436
|
+
get: () => colorRef.value,
|
|
20437
|
+
set: (color) => colorRef.value !== color && onChange(color)
|
|
20438
|
+
});
|
|
20436
20439
|
return {
|
|
20437
20440
|
isOpened,
|
|
20438
20441
|
editingColor,
|
|
@@ -20528,17 +20531,14 @@ const __vue2_script$q = {
|
|
|
20528
20531
|
const editor = inject(InjectionTokens$1.EDITOR);
|
|
20529
20532
|
const pickerRef = ref(null);
|
|
20530
20533
|
const api = usePickerApi({
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
emit("change", color);
|
|
20535
|
-
},
|
|
20536
|
-
onBeforeOpened: () => {
|
|
20537
|
-
editor.commands.storeSelection();
|
|
20538
|
-
emit("before-opened");
|
|
20534
|
+
onChange: (color) => {
|
|
20535
|
+
emit("change", color);
|
|
20536
|
+
editor.chain().focus().restoreSelection().run();
|
|
20539
20537
|
},
|
|
20540
|
-
|
|
20541
|
-
|
|
20538
|
+
onClosed: () => editor.commands.restoreSelection(),
|
|
20539
|
+
onBeforeOpened: () => editor.commands.storeSelection(),
|
|
20540
|
+
colorRef: toRef(props, "value"),
|
|
20541
|
+
pickerRef
|
|
20542
20542
|
});
|
|
20543
20543
|
usePickerHotkeys({
|
|
20544
20544
|
isOpenedRef: api.isOpened,
|
|
@@ -22086,12 +22086,27 @@ function useLink() {
|
|
|
22086
22086
|
function prepareInitialFields() {
|
|
22087
22087
|
linkData.value.text = editor.commands.getSelectedText();
|
|
22088
22088
|
currentDestination.value.id = editor.getAttributes("link").destination || LinkDestinations.URL;
|
|
22089
|
-
destinationHrefs.value[currentDestination.value.id] = editor.getAttributes("link").href || "";
|
|
22090
22089
|
linkData.value.target = editor.getAttributes("link").target || LinkTargets.SELF;
|
|
22090
|
+
if (editor.getAttributes("link").href) {
|
|
22091
|
+
destinationHrefs.value[currentDestination.value.id] = editor.getAttributes("link").href;
|
|
22092
|
+
}
|
|
22093
|
+
}
|
|
22094
|
+
function resetDestinations() {
|
|
22095
|
+
destinationHrefs.value.block = pageBlocks.value[0].id;
|
|
22096
|
+
destinationHrefs.value.url = "";
|
|
22097
|
+
}
|
|
22098
|
+
function getFormattedHref() {
|
|
22099
|
+
if (currentDestination.value.id === LinkDestinations.URL) {
|
|
22100
|
+
const url = destinationHrefs.value.url;
|
|
22101
|
+
const isRelative = !!url.startsWith("/");
|
|
22102
|
+
const hasProtocol2 = /^https?:\/\/.+$/i.test(url);
|
|
22103
|
+
return isRelative || hasProtocol2 ? url : `https://${url}`;
|
|
22104
|
+
}
|
|
22105
|
+
return destinationHrefs.value[currentDestination.value.id];
|
|
22091
22106
|
}
|
|
22092
22107
|
function apply2() {
|
|
22093
22108
|
editor.chain().focus().applyLink({
|
|
22094
|
-
href:
|
|
22109
|
+
href: getFormattedHref(),
|
|
22095
22110
|
text: linkData.value.text,
|
|
22096
22111
|
target: linkData.value.target,
|
|
22097
22112
|
destination: currentDestination.value.id
|
|
@@ -22111,6 +22126,7 @@ function useLink() {
|
|
|
22111
22126
|
linkData,
|
|
22112
22127
|
destinationHrefs,
|
|
22113
22128
|
currentDestination,
|
|
22129
|
+
resetDestinations,
|
|
22114
22130
|
prepareInitialFields,
|
|
22115
22131
|
updateTarget,
|
|
22116
22132
|
updateLink,
|
|
@@ -22333,7 +22349,7 @@ const __vue2_script$5 = {
|
|
|
22333
22349
|
},
|
|
22334
22350
|
setup(props, { emit }) {
|
|
22335
22351
|
const link = toRef(props, "link");
|
|
22336
|
-
const isURLDestination = computed(() => link.value.currentDestination.value.id ===
|
|
22352
|
+
const isURLDestination = computed(() => link.value.currentDestination.value.id === LinkDestinations.URL);
|
|
22337
22353
|
const isTargetBlank = computed(() => link.value.linkData.value.target === LinkTargets.BLANK);
|
|
22338
22354
|
const changeDestination = (value) => {
|
|
22339
22355
|
emit("reset-errors");
|
|
@@ -22461,14 +22477,16 @@ const __vue2_script$4 = {
|
|
|
22461
22477
|
const modalRef = ref(null);
|
|
22462
22478
|
const editor = inject(InjectionTokens$1.EDITOR);
|
|
22463
22479
|
const link = useLink();
|
|
22464
|
-
const urlRegExp =
|
|
22480
|
+
const urlRegExp = /^(https:\/\/www\.|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,100}(:[0-9]{1,5})?(\/.*)?$/i;
|
|
22465
22481
|
const isEmpty = () => {
|
|
22466
22482
|
return link.linkData.value.text ? false : "Can't be empty";
|
|
22467
22483
|
};
|
|
22468
22484
|
const isUrl = () => {
|
|
22469
|
-
if (link.currentDestination.value.id !==
|
|
22485
|
+
if (link.currentDestination.value.id !== LinkDestinations.URL)
|
|
22470
22486
|
return false;
|
|
22471
|
-
|
|
22487
|
+
const href = link.destinationHrefs.value.url;
|
|
22488
|
+
const isValidUrl = href.startsWith("/") || urlRegExp.test(href);
|
|
22489
|
+
return isValidUrl ? false : "Please enter a valid URL";
|
|
22472
22490
|
};
|
|
22473
22491
|
const nameValidator = useValidator({
|
|
22474
22492
|
validations: [isEmpty]
|
|
@@ -22502,6 +22520,7 @@ const __vue2_script$4 = {
|
|
|
22502
22520
|
return;
|
|
22503
22521
|
link.apply();
|
|
22504
22522
|
toggler.close();
|
|
22523
|
+
link.resetDestinations();
|
|
22505
22524
|
};
|
|
22506
22525
|
const removeLink = () => {
|
|
22507
22526
|
link.removeLink();
|
|
@@ -22529,7 +22548,7 @@ var __component__$4 = /* @__PURE__ */ normalizeComponent(
|
|
|
22529
22548
|
staticRenderFns$4,
|
|
22530
22549
|
false,
|
|
22531
22550
|
__vue2_injectStyles$4,
|
|
22532
|
-
"
|
|
22551
|
+
"28e9497e",
|
|
22533
22552
|
null,
|
|
22534
22553
|
null
|
|
22535
22554
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<label class="zw-checkbox">
|
|
3
|
-
<input class="zw-checkbox__field" type="checkbox" :checked="value" @
|
|
3
|
+
<input class="zw-checkbox__field" type="checkbox" :checked="value" @click="onCheckedChanged">
|
|
4
4
|
<span class="zw-checkbox__indicator zw-margin-right--xs" />
|
|
5
5
|
<span class="zw-checkbox__label">{{ label }}</span>
|
|
6
6
|
</label>
|
|
@@ -23,10 +23,8 @@ export default {
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
|
|
26
|
-
setup(
|
|
27
|
-
const onCheckedChanged = (
|
|
28
|
-
emit('input', event.target.checked);
|
|
29
|
-
};
|
|
26
|
+
setup(props, { emit }) {
|
|
27
|
+
const onCheckedChanged = () => emit('input', !props.value);
|
|
30
28
|
|
|
31
29
|
return { onCheckedChanged };
|
|
32
30
|
}
|
|
@@ -53,16 +53,14 @@ export default {
|
|
|
53
53
|
const pickerRef = ref(null);
|
|
54
54
|
|
|
55
55
|
const api = usePickerApi({
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
onChange: (color) => {
|
|
57
|
+
emit('change', color);
|
|
58
|
+
editor.chain().focus().restoreSelection().run();
|
|
59
59
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
pickerRef,
|
|
65
|
-
initialColorRef: toRef(props, 'value')
|
|
60
|
+
onClosed: () => editor.commands.restoreSelection(),
|
|
61
|
+
onBeforeOpened: () => editor.commands.storeSelection(),
|
|
62
|
+
colorRef: toRef(props, 'value'),
|
|
63
|
+
pickerRef
|
|
66
64
|
});
|
|
67
65
|
|
|
68
66
|
usePickerHotkeys({
|
|
@@ -20,7 +20,7 @@ describe('open/close picker', () => {
|
|
|
20
20
|
onClosed: jest.fn(),
|
|
21
21
|
onBeforeOpened: jest.fn(),
|
|
22
22
|
pickerRef: createPickerRef({ isOpened: true }),
|
|
23
|
-
|
|
23
|
+
colorRef: ref('red')
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
expect(toggler.isOpened.value).toBe(true);
|
|
@@ -31,7 +31,7 @@ describe('open/close picker', () => {
|
|
|
31
31
|
onClosed: jest.fn(),
|
|
32
32
|
onBeforeOpened: jest.fn(),
|
|
33
33
|
pickerRef: ref(null),
|
|
34
|
-
|
|
34
|
+
colorRef: ref('red')
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
expect(toggler.isOpened.value).toBe(false);
|
|
@@ -43,7 +43,7 @@ describe('open/close picker', () => {
|
|
|
43
43
|
onClosed: jest.fn(),
|
|
44
44
|
onBeforeOpened: jest.fn(),
|
|
45
45
|
pickerRef,
|
|
46
|
-
|
|
46
|
+
colorRef: ref('red')
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
toggler.open();
|
|
@@ -57,7 +57,7 @@ describe('open/close picker', () => {
|
|
|
57
57
|
onClosed: jest.fn(),
|
|
58
58
|
onBeforeOpened: jest.fn(),
|
|
59
59
|
pickerRef,
|
|
60
|
-
|
|
60
|
+
colorRef: ref('red')
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
toggler.toggle();
|
|
@@ -71,7 +71,7 @@ describe('open/close picker', () => {
|
|
|
71
71
|
onClosed: jest.fn(),
|
|
72
72
|
onBeforeOpened: jest.fn(),
|
|
73
73
|
pickerRef,
|
|
74
|
-
|
|
74
|
+
colorRef: ref('red')
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
toggler.close();
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { computed
|
|
1
|
+
import { computed } from 'vue';
|
|
2
2
|
|
|
3
|
-
export function usePickerApi({ pickerRef,
|
|
4
|
-
const editingColor = ref(initialColorRef.value);
|
|
3
|
+
export function usePickerApi({ pickerRef, colorRef, onChange, onClosed, onBeforeOpened }) {
|
|
5
4
|
const isOpened = computed(() => pickerRef.value?.isVisible ?? false);
|
|
5
|
+
let initialColor;
|
|
6
6
|
|
|
7
7
|
function open() {
|
|
8
8
|
onBeforeOpened();
|
|
9
|
-
|
|
9
|
+
initialColor = colorRef.value;
|
|
10
10
|
pickerRef.value.open(pickerRef.value.$el);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function close() {
|
|
14
|
-
pickerRef.value?.close(
|
|
15
|
-
onClosed(
|
|
14
|
+
pickerRef.value?.close(colorRef.value);
|
|
15
|
+
onClosed();
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function toggle() {
|
|
@@ -20,10 +20,17 @@ export function usePickerApi({ pickerRef, initialColorRef, onClosed, onBeforeOpe
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function cancel() {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const color = initialColor ?? colorRef.value;
|
|
24
|
+
|
|
25
|
+
onChange(color);
|
|
26
|
+
pickerRef.value?.close(color);
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
const editingColor = computed({
|
|
30
|
+
get: () => colorRef.value,
|
|
31
|
+
set: (color) => colorRef.value !== color && onChange(color)
|
|
32
|
+
});
|
|
33
|
+
|
|
27
34
|
return {
|
|
28
35
|
isOpened,
|
|
29
36
|
editingColor,
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
<script>
|
|
34
34
|
import { computed, ref, inject } from 'vue';
|
|
35
|
+
import { LinkDestinations } from '../../../../enums';
|
|
35
36
|
import { InjectionTokens } from '../../../../injectionTokens';
|
|
36
37
|
import { tooltip } from '../../../../directives';
|
|
37
38
|
import { useValidator } from '../../../base/composables';
|
|
@@ -65,15 +66,18 @@ export default {
|
|
|
65
66
|
const editor = inject(InjectionTokens.EDITOR);
|
|
66
67
|
|
|
67
68
|
const link = useLink();
|
|
68
|
-
const urlRegExp =
|
|
69
|
+
const urlRegExp = /^(https:\/\/www\.|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,100}(:[0-9]{1,5})?(\/.*)?$/i;
|
|
69
70
|
|
|
70
71
|
const isEmpty = () => {
|
|
71
72
|
return link.linkData.value.text ? false : 'Can\'t be empty';
|
|
72
73
|
};
|
|
73
74
|
const isUrl = () => {
|
|
74
|
-
if (link.currentDestination.value.id !==
|
|
75
|
+
if (link.currentDestination.value.id !== LinkDestinations.URL) return false;
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
const href = link.destinationHrefs.value.url;
|
|
78
|
+
const isValidUrl = href.startsWith('/') || urlRegExp.test(href);
|
|
79
|
+
|
|
80
|
+
return isValidUrl ? false : 'Please enter a valid URL';
|
|
77
81
|
};
|
|
78
82
|
|
|
79
83
|
const nameValidator = useValidator({
|
|
@@ -116,6 +120,7 @@ export default {
|
|
|
116
120
|
|
|
117
121
|
link.apply();
|
|
118
122
|
toggler.close();
|
|
123
|
+
link.resetDestinations();
|
|
119
124
|
};
|
|
120
125
|
|
|
121
126
|
const removeLink = () => {
|
|
@@ -89,7 +89,7 @@ describe('actions with link', () => {
|
|
|
89
89
|
link.apply();
|
|
90
90
|
|
|
91
91
|
expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
|
|
92
|
-
destination:
|
|
92
|
+
destination: LinkDestinations.URL,
|
|
93
93
|
href: '/world',
|
|
94
94
|
target: LinkTargets.SELF,
|
|
95
95
|
text: 'hello'
|
|
@@ -111,4 +111,62 @@ describe('actions with link', () => {
|
|
|
111
111
|
|
|
112
112
|
expect(link.linkData.value.target).toBe(LinkTargets.BLANK);
|
|
113
113
|
});
|
|
114
|
+
|
|
115
|
+
test('should reset destination', () => {
|
|
116
|
+
const link = useComposable();
|
|
117
|
+
|
|
118
|
+
link.resetDestinations();
|
|
119
|
+
|
|
120
|
+
expect(link.destinationHrefs.value).toMatchSnapshot();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('format link', () => {
|
|
125
|
+
test('should format absolute link without href', () => {
|
|
126
|
+
const link = useComposable();
|
|
127
|
+
|
|
128
|
+
link.updateText('hello');
|
|
129
|
+
link.updateLink('world.com');
|
|
130
|
+
|
|
131
|
+
link.apply();
|
|
132
|
+
|
|
133
|
+
expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
|
|
134
|
+
destination: LinkDestinations.URL,
|
|
135
|
+
href: 'https://world.com',
|
|
136
|
+
target: LinkTargets.SELF,
|
|
137
|
+
text: 'hello'
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('should format absolute link with href', () => {
|
|
142
|
+
const link = useComposable();
|
|
143
|
+
|
|
144
|
+
link.updateText('hello');
|
|
145
|
+
link.updateLink('https://world.com');
|
|
146
|
+
|
|
147
|
+
link.apply();
|
|
148
|
+
|
|
149
|
+
expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
|
|
150
|
+
destination: LinkDestinations.URL,
|
|
151
|
+
href: 'https://world.com',
|
|
152
|
+
target: LinkTargets.SELF,
|
|
153
|
+
text: 'hello'
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('should format relative link', () => {
|
|
158
|
+
const link = useComposable();
|
|
159
|
+
|
|
160
|
+
link.updateText('hello');
|
|
161
|
+
link.updateLink('/world/new');
|
|
162
|
+
|
|
163
|
+
link.apply();
|
|
164
|
+
|
|
165
|
+
expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
|
|
166
|
+
destination: LinkDestinations.URL,
|
|
167
|
+
href: '/world/new',
|
|
168
|
+
target: LinkTargets.SELF,
|
|
169
|
+
text: 'hello'
|
|
170
|
+
});
|
|
171
|
+
});
|
|
114
172
|
});
|
|
@@ -17,8 +17,28 @@ export function useLink() {
|
|
|
17
17
|
function prepareInitialFields() {
|
|
18
18
|
linkData.value.text = editor.commands.getSelectedText();
|
|
19
19
|
currentDestination.value.id = editor.getAttributes('link').destination || LinkDestinations.URL;
|
|
20
|
-
destinationHrefs.value[currentDestination.value.id] = editor.getAttributes('link').href || '';
|
|
21
20
|
linkData.value.target = editor.getAttributes('link').target || LinkTargets.SELF;
|
|
21
|
+
|
|
22
|
+
if (editor.getAttributes('link').href) {
|
|
23
|
+
destinationHrefs.value[currentDestination.value.id] = editor.getAttributes('link').href;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resetDestinations() {
|
|
28
|
+
destinationHrefs.value.block = pageBlocks.value[0].id;
|
|
29
|
+
destinationHrefs.value.url = '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getFormattedHref() {
|
|
33
|
+
if (currentDestination.value.id === LinkDestinations.URL) {
|
|
34
|
+
const url = destinationHrefs.value.url;
|
|
35
|
+
const isRelative = !!url.startsWith('/');
|
|
36
|
+
const hasProtocol = /^https?:\/\/.+$/i.test(url);
|
|
37
|
+
|
|
38
|
+
return isRelative || hasProtocol ? url : `https://${url}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return destinationHrefs.value[currentDestination.value.id];
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
function apply() {
|
|
@@ -26,7 +46,7 @@ export function useLink() {
|
|
|
26
46
|
.chain()
|
|
27
47
|
.focus()
|
|
28
48
|
.applyLink({
|
|
29
|
-
href:
|
|
49
|
+
href: getFormattedHref(),
|
|
30
50
|
text: linkData.value.text,
|
|
31
51
|
target: linkData.value.target,
|
|
32
52
|
destination: currentDestination.value.id
|
|
@@ -51,6 +71,7 @@ export function useLink() {
|
|
|
51
71
|
linkData,
|
|
52
72
|
destinationHrefs,
|
|
53
73
|
currentDestination,
|
|
74
|
+
resetDestinations,
|
|
54
75
|
prepareInitialFields,
|
|
55
76
|
updateTarget,
|
|
56
77
|
updateLink,
|
|
@@ -75,7 +75,7 @@ export default {
|
|
|
75
75
|
|
|
76
76
|
setup(props, { emit }) {
|
|
77
77
|
const link = toRef(props, 'link');
|
|
78
|
-
const isURLDestination = computed(() => link.value.currentDestination.value.id ===
|
|
78
|
+
const isURLDestination = computed(() => link.value.currentDestination.value.id === LinkDestinations.URL);
|
|
79
79
|
const isTargetBlank = computed(() => link.value.linkData.value.target === LinkTargets.BLANK);
|
|
80
80
|
|
|
81
81
|
const changeDestination = (value) => {
|