@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.
@@ -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
- onClosed: (color) => {
55
- editor.commands.restoreSelection();
56
- if (color !== props.value) emit('change', color);
56
+ onChange: (color) => {
57
+ emit('change', color);
58
+ editor.chain().focus().restoreSelection().run();
57
59
  },
58
- onBeforeOpened: () => {
59
- editor.commands.storeSelection();
60
- emit('before-opened');
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
- initialColorRef: ref('red')
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
- initialColorRef: ref('red')
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
- initialColorRef: ref('red')
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
- initialColorRef: ref('red')
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
- initialColorRef: ref('red')
74
+ colorRef: ref('red')
75
75
  });
76
76
 
77
77
  toggler.close();
@@ -1,18 +1,18 @@
1
- import { computed, ref } from 'vue';
1
+ import { computed } from 'vue';
2
2
 
3
- export function usePickerApi({ pickerRef, initialColorRef, onClosed, onBeforeOpened }) {
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
- editingColor.value = initialColorRef.value;
9
+ initialColor = colorRef.value;
10
10
  pickerRef.value.open(pickerRef.value.$el);
11
11
  }
12
12
 
13
13
  function close() {
14
- pickerRef.value?.close(editingColor.value);
15
- onClosed(editingColor.value);
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
- editingColor.value = initialColorRef.value;
24
- pickerRef.value?.close(editingColor.value);
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,
@@ -60,6 +60,7 @@ export default {
60
60
  background-color: rgb(var(--zw-color-n15));
61
61
  color: rgb(var(--zw-color-n70));
62
62
  z-index: 999999;
63
+ text-align: left;
63
64
  }
64
65
 
65
66
  .zw-toolbar::before,
@@ -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 = /(^(https?:\/\/|\/)(?:www\.|(?!www))?[^\s])/;
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 !== 'url') return false;
75
+ if (link.currentDestination.value.id !== LinkDestinations.URL) return false;
75
76
 
76
- return urlRegExp.test(link.destinationHrefs.value.url) ? false : 'Please enter a valid URL';
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 = () => {
@@ -1,5 +1,12 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`actions with link should reset destination 1`] = `
4
+ Object {
5
+ "block": 1,
6
+ "url": "",
7
+ }
8
+ `;
9
+
3
10
  exports[`init link should prepare initial fields 1`] = `
4
11
  Object {
5
12
  "block": 1,
@@ -89,7 +89,7 @@ describe('actions with link', () => {
89
89
  link.apply();
90
90
 
91
91
  expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
92
- destination: 'url',
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: destinationHrefs.value[currentDestination.value.id],
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 === 'url');
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
@@ -1,3 +1,3 @@
1
1
  export { default as Wysiwyg } from './Wysiwyg';
2
2
  export { NodeFactory } from './services';
3
- export { NodeTypes } from './enums';
3
+ export { NodeTypes, TextSettings, Alignments } from './enums';
@@ -65,16 +65,7 @@ export class NodeFactory {
65
65
  return { type, attrs };
66
66
  }
67
67
 
68
- static link(attrs) {
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.66",
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:pre-build": "npm run lint:js && npm run lint:css && npm run test:unit",
18
- "lib:build": "npm run lib:pre-build && vite build --config ./config/vite/lib.config.js",
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",