@zipify/wysiwyg 1.0.0-dev.66 → 1.0.0-dev.69
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/config/vite/lib.config.js +2 -0
- package/dist/wysiwyg.css +862 -1
- package/dist/wysiwyg.mjs +20537 -12948
- package/lib/components/base/colorPicker/ColorPicker.vue +11 -10
- 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/Toolbar.vue +1 -0
- 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/lib/index.js +1 -1
- package/lib/services/NodeFactory.js +2 -11
- package/package.json +6 -6
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
ref="pickerRef"
|
|
4
4
|
placement="bottom-end"
|
|
5
5
|
:favorite-colors="favoriteColors"
|
|
6
|
+
:window="window"
|
|
6
7
|
@changeFavoriteColors="updateFavoriteColors"
|
|
7
8
|
v-model="editingColor"
|
|
8
9
|
v-out-click="{ onOutClick: close, isDisabled: !isOpened }"
|
|
@@ -24,6 +25,7 @@ import { ZipifyColorPicker } from '@zipify/colorpicker';
|
|
|
24
25
|
import { inject, ref, toRef } from 'vue';
|
|
25
26
|
import { outClick } from '../../../directives';
|
|
26
27
|
import { InjectionTokens } from '../../../injectionTokens';
|
|
28
|
+
import { ContextWindow } from '../../../services';
|
|
27
29
|
import { useDeselectionLock } from '../composables';
|
|
28
30
|
import { usePickerApi, usePickerHotkeys } from './composables';
|
|
29
31
|
|
|
@@ -51,16 +53,14 @@ export default {
|
|
|
51
53
|
const pickerRef = ref(null);
|
|
52
54
|
|
|
53
55
|
const api = usePickerApi({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
onChange: (color) => {
|
|
57
|
+
emit('change', color);
|
|
58
|
+
editor.chain().focus().restoreSelection().run();
|
|
57
59
|
},
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
pickerRef,
|
|
63
|
-
initialColorRef: toRef(props, 'value')
|
|
60
|
+
onClosed: () => editor.commands.restoreSelection(),
|
|
61
|
+
onBeforeOpened: () => editor.commands.storeSelection(),
|
|
62
|
+
colorRef: toRef(props, 'value'),
|
|
63
|
+
pickerRef
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
usePickerHotkeys({
|
|
@@ -86,7 +86,8 @@ export default {
|
|
|
86
86
|
close: api.close,
|
|
87
87
|
toggle: api.toggle,
|
|
88
88
|
favoriteColors: favoriteColors.listRef,
|
|
89
|
-
updateFavoriteColors
|
|
89
|
+
updateFavoriteColors,
|
|
90
|
+
window: ContextWindow.window
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
93
|
};
|
|
@@ -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) => {
|
package/lib/index.js
CHANGED
|
@@ -65,16 +65,7 @@ export class NodeFactory {
|
|
|
65
65
|
return { type, attrs };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
static
|
|
69
|
-
return {
|
|
70
|
-
type: 'text',
|
|
71
|
-
marks: [
|
|
72
|
-
{
|
|
73
|
-
type: 'link',
|
|
74
|
-
attrs: { ...attrs }
|
|
75
|
-
}
|
|
76
|
-
],
|
|
77
|
-
text: attrs.text
|
|
78
|
-
};
|
|
68
|
+
static populateAllDevices(value) {
|
|
69
|
+
return { mobile: value, tablet: value, desktop: value };
|
|
79
70
|
}
|
|
80
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zipify/wysiwyg",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.69",
|
|
4
4
|
"description": "Zipify modification of TipTap text editor",
|
|
5
5
|
"main": "dist/wysiwyg.mjs",
|
|
6
6
|
"repository": {
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
"url": "https://github.com/ZipifyApps/ZipifyWysiwyg/issues"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"lib:
|
|
18
|
-
"lib:
|
|
19
|
-
"lib:release": "export $(cat ./.env | xargs) && npm run lib:build && release-it",
|
|
17
|
+
"lib:build": "vite build --config ./config/vite/lib.config.js",
|
|
18
|
+
"lib:pre-release": "npm run lint:js && npm run lint:css && npm run test:unit",
|
|
19
|
+
"lib:release": "export $(cat ./.env | xargs) && npm run lib:pre-release && npm run lib:build && release-it",
|
|
20
20
|
"example:start": "NODE_ENV=development vite serve --config ./config/vite/example.config.js",
|
|
21
21
|
"example:build": "NODE_ENV=production vite build --config ./config/vite/example.config.js",
|
|
22
22
|
"test:unit": "jest .",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"simplebar": "^5.3.8"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@zipify/colorpicker": "2
|
|
46
|
-
"vue": "2.7
|
|
45
|
+
"@zipify/colorpicker": "^2.1",
|
|
46
|
+
"vue": "^2.7"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@babel/core": "^7.18.9",
|