@zipify/wysiwyg 2.4.1 → 2.4.3

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 CHANGED
@@ -632,17 +632,17 @@
632
632
  color: rgb(var(--zw-color-white));
633
633
  }
634
634
 
635
- .zw-link-modal[data-v-3db89308] {
635
+ .zw-link-modal[data-v-eea2f92a] {
636
636
  width: 266px;
637
637
  }
638
- .zw-link-modal__body[data-v-3db89308] {
638
+ .zw-link-modal__body[data-v-eea2f92a] {
639
639
  padding: var(--zw-offset-sm);
640
640
  }
641
- .zw-link-modal__actions[data-v-3db89308] {
641
+ .zw-link-modal__actions[data-v-eea2f92a] {
642
642
  display: flex;
643
643
  justify-content: flex-end;
644
644
  }
645
- [data-v-3db89308] .zw-link-modal-dropdown__option {
645
+ [data-v-eea2f92a] .zw-link-modal-dropdown__option {
646
646
  width: 234px;
647
647
  }
648
648
 
package/dist/wysiwyg.mjs CHANGED
@@ -26,7 +26,7 @@ var __privateMethod = (obj, member, method) => {
26
26
  __accessCheck(obj, member, "access private method");
27
27
  return method;
28
28
  };
29
- var _domParser, _parser, _NodeFilter, NodeFilter_get, _Node, Node_get, _removeComments, removeComments_fn, _normalizeRootTags, normalizeRootTags_fn, _createNodeIterator, createNodeIterator_fn, _iterateNodes, iterateNodes_fn, _runIterator, runIterator_fn, _removeEmptyNodes, removeEmptyNodes_fn, _normalizeListItems, normalizeListItems_fn, _isBlockNode, isBlockNode_fn, _isRootNode, isRootNode_fn, _assignElementProperties, assignElementProperties_fn, _removeStyleProperties, removeStyleProperties_fn, _normalizeBreakLines, normalizeBreakLines_fn, _normalizeBlockTextDecoration, normalizeBlockTextDecoration_fn, _moveTextDecorationToChildren, moveTextDecorationToChildren_fn, _parseTextDecoration, parseTextDecoration_fn, _normalizeBlockBackgroundColor, normalizeBlockBackgroundColor_fn, _moveBackgroundColorToChildren, moveBackgroundColorToChildren_fn, _wrapTextNode, wrapTextNode_fn, _iterateNodes2, iterateNodes_fn2, _iterateChildNodes, iterateChildNodes_fn, _bubbleMarks, bubbleMarks_fn, _canBubbleMark, canBubbleMark_fn, _includesMark, includesMark_fn, _includesMarkType, includesMarkType_fn, _removeMark, removeMark_fn, _addMark, addMark_fn, _findMarkIndexByType, findMarkIndexByType_fn, _buildHtml, buildHtml_fn, _buildJson, buildJson_fn, _textBlock, textBlock_fn, _normalizeTextBlockArgs, normalizeTextBlockArgs_fn, _buildDecorations, buildDecorations_fn;
29
+ var _domParser, _parser, _NodeFilter, NodeFilter_get, _Node, Node_get, _removeComments, removeComments_fn, _normalizeRootTags, normalizeRootTags_fn, _createNodeIterator, createNodeIterator_fn, _iterateNodes, iterateNodes_fn, _runIterator, runIterator_fn, _removeEmptyNodes, removeEmptyNodes_fn, _normalizeListItems, normalizeListItems_fn, _isBlockNode, isBlockNode_fn, _isRootNode, isRootNode_fn, _assignElementProperties, assignElementProperties_fn, _removeStyleProperties, removeStyleProperties_fn, _normalizeBreakLines, normalizeBreakLines_fn, _normalizeBlockTextDecoration, normalizeBlockTextDecoration_fn, _moveTextDecorationToChildren, moveTextDecorationToChildren_fn, _parseTextDecoration, parseTextDecoration_fn, _normalizeBlockBackgroundColor, normalizeBlockBackgroundColor_fn, _moveBackgroundColorToChildren, moveBackgroundColorToChildren_fn, _wrapTextNode, wrapTextNode_fn, _iterateNodes2, iterateNodes_fn2, _iterateChildNodes, iterateChildNodes_fn, _bubbleMarks, bubbleMarks_fn, _canBubbleMark, canBubbleMark_fn, _includesMark, includesMark_fn, _includesMarkType, includesMarkType_fn, _removeMark, removeMark_fn, _addMark, addMark_fn, _findMarkIndexByType, findMarkIndexByType_fn, _buildHtml, buildHtml_fn, _buildJson, buildJson_fn, _textBlock, textBlock_fn, _normalizeTextBlockArgs, normalizeTextBlockArgs_fn, _buildDecorations, buildDecorations_fn, _getWeights, getWeights_fn;
30
30
  import { computed, ref, watch, inject, onUnmounted, nextTick, provide, onMounted, toRef, unref, reactive } from "vue";
31
31
  import { ColorModel, ZipifyColorPicker } from "@zipify/colorpicker";
32
32
  function OrderedMap(content) {
@@ -23703,6 +23703,12 @@ function __vue2_injectStyles$9(context) {
23703
23703
  const RemoveFormatControl = /* @__PURE__ */ function() {
23704
23704
  return __component__$9.exports;
23705
23705
  }();
23706
+ const RegExps = {
23707
+ URL: /^(https:\/\/www\.|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,100}(:[0-9]{1,5})?(\/.*)?$/i,
23708
+ HTTPS_PROTOCOL: /^https?:\/\/.+$/i,
23709
+ MAILTO_PROTOCOL: /^mailto:.+$/,
23710
+ TEL_PROTOCOL: /^tel:.+$/
23711
+ };
23706
23712
  var render$8 = function __render__37() {
23707
23713
  var _vm = this;
23708
23714
  var _h = _vm.$createElement;
@@ -23785,9 +23791,13 @@ function useLink() {
23785
23791
  function getFormattedHref() {
23786
23792
  if (currentDestination.value.id === LinkDestinations.URL) {
23787
23793
  const url = destinationHrefs.value.url;
23788
- const isRelative = !!url.startsWith("/");
23789
- const hasProtocol2 = /^https?:\/\/.+$/i.test(url);
23790
- return isRelative || hasProtocol2 ? url : `https://${url}`;
23794
+ const isTelOrMail = RegExps.TEL_PROTOCOL.test(url) || RegExps.MAILTO_PROTOCOL.test(url);
23795
+ if (isTelOrMail)
23796
+ return url;
23797
+ if (url.startsWith("/"))
23798
+ return url;
23799
+ const hasProtocol2 = RegExps.HTTPS_PROTOCOL.test(url);
23800
+ return hasProtocol2 ? url : `https://${url}`;
23791
23801
  }
23792
23802
  return destinationHrefs.value[currentDestination.value.id];
23793
23803
  }
@@ -24185,22 +24195,26 @@ const __vue2_script$4 = {
24185
24195
  const modalRef = ref(null);
24186
24196
  const editor = inject(InjectionTokens$1.EDITOR);
24187
24197
  const link = useLink();
24188
- const urlRegExp = /^(https:\/\/www\.|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,100}(:[0-9]{1,5})?(\/.*)?$/i;
24189
24198
  const isEmpty = () => {
24190
24199
  return link.linkData.value.text ? false : "Can't be empty";
24191
24200
  };
24192
- const isUrl = () => {
24201
+ const isValidUrl = () => {
24193
24202
  if (link.currentDestination.value.id !== LinkDestinations.URL)
24194
24203
  return false;
24195
24204
  const href = link.destinationHrefs.value.url;
24196
- const isValidUrl = href.startsWith("/") || urlRegExp.test(href);
24197
- return isValidUrl ? false : "Please enter a valid URL";
24205
+ const isTelOrMail = RegExps.TEL_PROTOCOL.test(href) || RegExps.MAILTO_PROTOCOL.test(href);
24206
+ if (isTelOrMail)
24207
+ return false;
24208
+ if (href.startsWith("/"))
24209
+ return false;
24210
+ const isUrl = RegExps.URL.test(href);
24211
+ return isUrl ? false : "Please enter a valid URL";
24198
24212
  };
24199
24213
  const nameValidator = useValidator({
24200
24214
  validations: [isEmpty]
24201
24215
  });
24202
24216
  const urlValidator = useValidator({
24203
- validations: [isUrl]
24217
+ validations: [isValidUrl]
24204
24218
  });
24205
24219
  const resetErrors = () => {
24206
24220
  nameValidator.reset();
@@ -24257,7 +24271,7 @@ var __component__$4 = /* @__PURE__ */ normalizeComponent(
24257
24271
  staticRenderFns$4,
24258
24272
  false,
24259
24273
  __vue2_injectStyles$4,
24260
- "3db89308",
24274
+ "eea2f92a",
24261
24275
  null,
24262
24276
  null
24263
24277
  );
@@ -25022,9 +25036,13 @@ const FontStyle = Mark.create({
25022
25036
  });
25023
25037
  }),
25024
25038
  isItalicAvailable: createCommand(({ commands: commands2 }) => {
25025
- const font = commands2.getFont();
25026
- const fontWeight = commands2.getFontWeight();
25027
- return computed(() => unref(font).isItalicSupported(unref(fontWeight)));
25039
+ const fontRef = commands2.getFont();
25040
+ const fontWeightRef = commands2.getFontWeight();
25041
+ return computed(() => {
25042
+ const font = unref(fontRef);
25043
+ const weight = unref(fontWeightRef);
25044
+ return font.isItalicSupported(weight) && !font.isWeightItalicOnly(weight);
25045
+ });
25028
25046
  }),
25029
25047
  getDefaultFontStyle: createCommand(({ commands: commands2 }) => {
25030
25048
  const preset = commands2.getPreset();
@@ -27808,19 +27826,21 @@ function buildExtensions(options) {
27808
27826
  }
27809
27827
  class Font {
27810
27828
  constructor({ name, category, styles }) {
27829
+ __privateAdd(this, _getWeights);
27811
27830
  this.name = name;
27812
27831
  this.category = category;
27813
27832
  this.styles = styles;
27814
- }
27815
- get weights() {
27816
- return this.styles.filter((style2) => !style2.endsWith("i"));
27833
+ this.weights = __privateMethod(this, _getWeights, getWeights_fn).call(this);
27817
27834
  }
27818
27835
  isWeightSupported(weight) {
27819
- return this.styles.includes(weight);
27836
+ return this.weights.includes(weight);
27820
27837
  }
27821
27838
  isItalicSupported(weight) {
27822
27839
  return this.styles.includes(`${weight}i`);
27823
27840
  }
27841
+ isWeightItalicOnly(weight) {
27842
+ return this.isItalicSupported(weight) && !this.styles.includes(weight);
27843
+ }
27824
27844
  findClosestWeight(searchedWeight) {
27825
27845
  const weights = this.weights.map((weight) => parseInt(weight));
27826
27846
  let closestWeight;
@@ -27835,6 +27855,11 @@ class Font {
27835
27855
  return String(closestWeight);
27836
27856
  }
27837
27857
  }
27858
+ _getWeights = new WeakSet();
27859
+ getWeights_fn = function() {
27860
+ const weights = this.styles.map((style2) => style2.replace("i", ""));
27861
+ return Array.from(new Set(weights));
27862
+ };
27838
27863
  var render = function __render__45() {
27839
27864
  var _vm = this;
27840
27865
  var _h = _vm.$createElement;
package/example/fonts.js CHANGED
@@ -2,13 +2,6 @@ export const FONTS = [
2
2
  {
3
3
  'name': 'Merriweather',
4
4
  'styles': [
5
- '300',
6
- '300i',
7
- '400',
8
- '400i',
9
- '700',
10
- '700i',
11
- '900',
12
5
  '900i'
13
6
  ],
14
7
  'category': 'Serif'
@@ -42,6 +42,7 @@
42
42
  import { computed, ref, inject, unref } from 'vue';
43
43
  import { LinkDestinations, TextSettings } from '../../../../enums';
44
44
  import { InjectionTokens } from '../../../../injectionTokens';
45
+ import { RegExps } from '../../../../regExps';
45
46
  import { tooltip } from '../../../../directives';
46
47
  import { useValidator } from '../../../base/composables';
47
48
  import { Button, Icon, Modal, TextField, useModalToggler } from '../../../base';
@@ -72,18 +73,23 @@ export default {
72
73
  const editor = inject(InjectionTokens.EDITOR);
73
74
 
74
75
  const link = useLink();
75
- const urlRegExp = /^(https:\/\/www\.|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,100}(:[0-9]{1,5})?(\/.*)?$/i;
76
76
 
77
77
  const isEmpty = () => {
78
78
  return link.linkData.value.text ? false : 'Can\'t be empty';
79
79
  };
80
- const isUrl = () => {
80
+
81
+ const isValidUrl = () => {
81
82
  if (link.currentDestination.value.id !== LinkDestinations.URL) return false;
82
83
 
83
84
  const href = link.destinationHrefs.value.url;
84
- const isValidUrl = href.startsWith('/') || urlRegExp.test(href);
85
+ const isTelOrMail = RegExps.TEL_PROTOCOL.test(href) || RegExps.MAILTO_PROTOCOL.test(href);
86
+
87
+ if (isTelOrMail) return false;
88
+ if (href.startsWith('/')) return false;
89
+
90
+ const isUrl = RegExps.URL.test(href);
85
91
 
86
- return isValidUrl ? false : 'Please enter a valid URL';
92
+ return isUrl ? false : 'Please enter a valid URL';
87
93
  };
88
94
 
89
95
  const nameValidator = useValidator({
@@ -91,7 +97,7 @@ export default {
91
97
  });
92
98
 
93
99
  const urlValidator = useValidator({
94
- validations: [isUrl]
100
+ validations: [isValidUrl]
95
101
  });
96
102
 
97
103
  const resetErrors = () => {
@@ -169,4 +169,36 @@ describe('format link', () => {
169
169
  text: 'hello'
170
170
  });
171
171
  });
172
+
173
+ test('should format telephone link', () => {
174
+ const link = useComposable();
175
+
176
+ link.updateText('hello');
177
+ link.updateLink('tel:0123456789');
178
+
179
+ link.apply();
180
+
181
+ expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
182
+ destination: LinkDestinations.URL,
183
+ href: 'tel:0123456789',
184
+ target: LinkTargets.SELF,
185
+ text: 'hello'
186
+ });
187
+ });
188
+
189
+ test('should format mail link', () => {
190
+ const link = useComposable();
191
+
192
+ link.updateText('hello');
193
+ link.updateLink('mailto:example@gmail.com');
194
+
195
+ link.apply();
196
+
197
+ expect(link.editor.commands.applyLink).toHaveBeenCalledWith({
198
+ destination: LinkDestinations.URL,
199
+ href: 'mailto:example@gmail.com',
200
+ target: LinkTargets.SELF,
201
+ text: 'hello'
202
+ });
203
+ });
172
204
  });
@@ -1,6 +1,7 @@
1
1
  import { ref, inject } from 'vue';
2
2
  import { LinkTargets, LinkDestinations, TextSettings } from '../../../../../enums';
3
3
  import { InjectionTokens } from '../../../../../injectionTokens';
4
+ import { RegExps } from '../../../../../regExps';
4
5
 
5
6
  export function useLink() {
6
7
  const editor = inject(InjectionTokens.EDITOR);
@@ -34,10 +35,14 @@ export function useLink() {
34
35
  function getFormattedHref() {
35
36
  if (currentDestination.value.id === LinkDestinations.URL) {
36
37
  const url = destinationHrefs.value.url;
37
- const isRelative = !!url.startsWith('/');
38
- const hasProtocol = /^https?:\/\/.+$/i.test(url);
38
+ const isTelOrMail = RegExps.TEL_PROTOCOL.test(url) || RegExps.MAILTO_PROTOCOL.test(url);
39
39
 
40
- return isRelative || hasProtocol ? url : `https://${url}`;
40
+ if (isTelOrMail) return url;
41
+ if (url.startsWith('/')) return url;
42
+
43
+ const hasProtocol = RegExps.HTTPS_PROTOCOL.test(url);
44
+
45
+ return hasProtocol ? url : `https://${url}`;
41
46
  }
42
47
 
43
48
  return destinationHrefs.value[currentDestination.value.id];
@@ -21,10 +21,15 @@ export const FontStyle = Mark.create({
21
21
  }),
22
22
 
23
23
  isItalicAvailable: createCommand(({ commands }) => {
24
- const font = commands.getFont();
25
- const fontWeight = commands.getFontWeight();
24
+ const fontRef = commands.getFont();
25
+ const fontWeightRef = commands.getFontWeight();
26
26
 
27
- return computed(() => unref(font).isItalicSupported(unref(fontWeight)));
27
+ return computed(() => {
28
+ const font = unref(fontRef);
29
+ const weight = unref(fontWeightRef);
30
+
31
+ return font.isItalicSupported(weight) && !font.isWeightItalicOnly(weight);
32
+ });
28
33
  }),
29
34
 
30
35
  getDefaultFontStyle: createCommand(({ commands }) => {
@@ -88,7 +88,9 @@ describe('get font family', () => {
88
88
  content: createContent(NodeFactory.text('hello world'))
89
89
  });
90
90
 
91
- expect(editor.commands.findFontByName('Bungee')).toEqual({ name: 'Bungee', styles: ['400'] });
91
+ expect(editor.commands.findFontByName('Bungee')).toEqual(
92
+ expect.objectContaining({ name: 'Bungee' })
93
+ );
92
94
  });
93
95
 
94
96
  test('should get font model from selection', () => {
@@ -3,20 +3,27 @@ export class Font {
3
3
  this.name = name;
4
4
  this.category = category;
5
5
  this.styles = styles;
6
+ this.weights = this.#getWeights();
6
7
  }
7
8
 
8
- get weights() {
9
- return this.styles.filter((style) => !style.endsWith('i'));
9
+ #getWeights() {
10
+ const weights = this.styles.map((style) => style.replace('i', ''));
11
+
12
+ return Array.from(new Set(weights));
10
13
  }
11
14
 
12
15
  isWeightSupported(weight) {
13
- return this.styles.includes(weight);
16
+ return this.weights.includes(weight);
14
17
  }
15
18
 
16
19
  isItalicSupported(weight) {
17
20
  return this.styles.includes(`${weight}i`);
18
21
  }
19
22
 
23
+ isWeightItalicOnly(weight) {
24
+ return this.isItalicSupported(weight) && !this.styles.includes(weight);
25
+ }
26
+
20
27
  findClosestWeight(searchedWeight) {
21
28
  const weights = this.weights.map((weight) => parseInt(weight));
22
29
 
@@ -9,6 +9,12 @@ describe('weights', () => {
9
9
  expect(font.weights).toEqual(['400', '700']);
10
10
  });
11
11
 
12
+ test('should display italic only style', () => {
13
+ const font = new Font({ styles: ['700i'] });
14
+
15
+ expect(font.weights).toEqual(['700']);
16
+ });
17
+
12
18
  test('should find closest font weight', () => {
13
19
  const font = new Font({ styles: ['400', '700'] });
14
20
  const weight = font.findClosestWeight('500');
@@ -41,7 +47,7 @@ describe('style checks', () => {
41
47
  test('should detect if weight not supported', () => {
42
48
  const font = new Font({ styles: ['400', '700i'] });
43
49
 
44
- expect(font.isWeightSupported('700')).toBe(false);
50
+ expect(font.isWeightSupported('800')).toBe(false);
45
51
  });
46
52
 
47
53
  test('should detect if italic supported', () => {
@@ -55,4 +61,16 @@ describe('style checks', () => {
55
61
 
56
62
  expect(font.isItalicSupported('700')).toBe(false);
57
63
  });
64
+
65
+ test('should detect italic only weight', () => {
66
+ const font = new Font({ styles: ['400i'] });
67
+
68
+ expect(font.isItalicSupported('400')).toBe(true);
69
+ });
70
+
71
+ test('should detect non italic only weight', () => {
72
+ const font = new Font({ styles: ['400i', '400'] });
73
+
74
+ expect(font.isItalicSupported('400')).toBe(true);
75
+ });
58
76
  });
package/lib/regExps.js ADDED
@@ -0,0 +1,6 @@
1
+ export const RegExps = {
2
+ URL: /^(https:\/\/www\.|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,100}(:[0-9]{1,5})?(\/.*)?$/i,
3
+ HTTPS_PROTOCOL: /^https?:\/\/.+$/i,
4
+ MAILTO_PROTOCOL: /^mailto:.+$/,
5
+ TEL_PROTOCOL: /^tel:.+$/
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zipify/wysiwyg",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
4
4
  "description": "Zipify modification of TipTap text editor",
5
5
  "main": "dist/wysiwyg.mjs",
6
6
  "bin": {