@zipify/wysiwyg 1.0.0-dev.58 → 1.0.0-dev.60

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.
@@ -25,7 +25,7 @@ export class Tooltip {
25
25
  static get popperStyles() {
26
26
  return {
27
27
  willChange: 'transform',
28
- zIndex: 10001
28
+ zIndex: 999999
29
29
  };
30
30
  }
31
31
 
@@ -13,6 +13,7 @@ export default {
13
13
  <style scoped>
14
14
  .zw-toolbar__row {
15
15
  display: flex;
16
+ justify-content: space-between;
16
17
  column-gap: var(--zw-offset-xs);
17
18
  padding: var(--zw-offset-xxs) var(--zw-offset-xs);
18
19
  }
@@ -1,31 +1,51 @@
1
1
  <template>
2
- <Dropdown
3
- :value="currentValue"
4
- :options="$options.listTypes"
5
- @change="apply"
6
- >
7
- <template #activator="{ open, isOpened }">
8
- <Button
9
- icon
10
- skin="toolbar"
11
- :active="isOpened || isList"
12
- @click="open"
13
- v-tooltip="'Bullet Styles'"
14
- >
15
- <Icon :name="currentIcon" size="28px" auto-color />
16
- </Button>
17
- </template>
18
-
19
- <template #option="{ option }">
20
- <DropdownOption class="zw-list-control__option" :option="option">
21
- <Icon :name="option.icon" size="28px" auto-color />
22
- </DropdownOption>
23
- </template>
24
- </Dropdown>
2
+ <div class="zpa-list-control">
3
+ <Button
4
+ icon
5
+ skin="toolbar"
6
+ :active="isCurrentListSelected"
7
+ @click="toggle"
8
+ v-tooltip="'Bullets'"
9
+ >
10
+ <Icon :name="currentIcon" size="28px" auto-color />
11
+ </Button>
12
+
13
+ <Dropdown
14
+ :value="currentValue"
15
+ :options="$options.listTypes"
16
+ @change="apply"
17
+ >
18
+ <template #activator="{ open, isOpened }">
19
+ <Button
20
+ icon
21
+ skin="toolbar"
22
+ :active="isOpened"
23
+ @click="open"
24
+ v-tooltip="'Bullet Styles'"
25
+ >
26
+ <Icon
27
+ class="zw-list-control__activator"
28
+ name="arrow"
29
+ size="8px"
30
+ auto-color
31
+ />
32
+ </Button>
33
+ </template>
34
+
35
+ <template #option="{ option }">
36
+ <DropdownOption
37
+ class="zw-list-control__option"
38
+ :option="option"
39
+ >
40
+ <Icon :name="option.icon" size="28px" auto-color />
41
+ </DropdownOption>
42
+ </template>
43
+ </Dropdown>
44
+ </div>
25
45
  </template>
26
46
 
27
47
  <script>
28
- import { computed, inject } from 'vue';
48
+ import { computed, inject, ref } from 'vue';
29
49
  import { InjectionTokens } from '../../../injectionTokens';
30
50
  import { Dropdown, DropdownOption, Button, Icon } from '../../base';
31
51
  import { ListTypes } from '../../../enums';
@@ -55,26 +75,44 @@ export default {
55
75
  const selectionValue = editor.commands.getListType();
56
76
  const isList = computed(() => !!selectionValue.value);
57
77
  const currentValue = computed(() => selectionValue.value || 'none');
78
+ const recentListType = ref(ListTypes.DISC);
79
+ const isCurrentListSelected = computed(() => selectionValue.value === recentListType.value);
58
80
 
59
- const currentIcon = computed(() => {
60
- const type = selectionValue.value || ListTypes.DISC;
81
+ const currentIcon = computed(() => `list-${recentListType.value}`);
61
82
 
62
- return `list-${type}`;
63
- });
83
+ const apply = (type) => {
84
+ recentListType.value = type;
85
+ editor.chain().focus().applyList(type).run();
86
+ };
64
87
 
65
- const apply = (type) => editor.chain().focus().applyList(type).run();
88
+ const toggle = () => apply(recentListType.value || ListTypes.DISC);
66
89
 
67
90
  return {
68
91
  isList,
69
92
  currentValue,
70
93
  currentIcon,
71
- apply
94
+ isCurrentListSelected,
95
+ apply,
96
+ toggle
72
97
  };
73
98
  }
74
99
  };
75
100
  </script>
76
101
 
77
102
  <style scoped>
103
+ .zpa-list-control {
104
+ display: flex;
105
+ }
106
+
107
+ .zpa-list-control:hover {
108
+ color: rgb(var(--zw-color-white));
109
+ background-color: rgb(var(--zw-color-n5));
110
+ }
111
+
112
+ .zw-list-control__activator {
113
+ padding: 0 var(--zw-offset-xs);
114
+ }
115
+
78
116
  .zw-list-control__option {
79
117
  padding: 0 var(--zw-offset-xs);
80
118
  display: flex;
@@ -1,7 +1,7 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
- import { h, ref } from 'vue';
2
+ import { h, ref, nextTick } from 'vue';
3
3
  import { InjectionTokens } from '../../../../injectionTokens';
4
- import { Dropdown, Icon } from '../../../base';
4
+ import { Button, Dropdown, Icon } from '../../../base';
5
5
  import ListControl from '../ListControl';
6
6
  import { ListTypes } from '../../../../enums';
7
7
 
@@ -62,10 +62,15 @@ describe('selection value', () => {
62
62
  expect(iconWrapper.props('name')).toBe('list-disc');
63
63
  });
64
64
 
65
- test('should render type icon', () => {
65
+ test('should render type icon', async () => {
66
66
  const editor = createEditor({ listType: ListTypes.DECIMAL });
67
67
  const wrapper = createComponent({ editor });
68
68
  const iconWrapper = wrapper.findComponent(Icon);
69
+ const dropdownWrapper = wrapper.findComponent(Dropdown);
70
+
71
+ dropdownWrapper.vm.$emit('change', ListTypes.DECIMAL);
72
+
73
+ await nextTick();
69
74
 
70
75
  expect(iconWrapper.props('name')).toBe('list-decimal');
71
76
  });
@@ -79,4 +84,14 @@ describe('selection value', () => {
79
84
 
80
85
  expect(editor.commands.applyList).toHaveBeenCalledWith(ListTypes.LATIN);
81
86
  });
87
+
88
+ test('should apply list with default type', () => {
89
+ const editor = createEditor();
90
+ const wrapper = createComponent({ editor });
91
+ const buttonWrapper = wrapper.findComponent(Button);
92
+
93
+ buttonWrapper.vm.$emit('click');
94
+
95
+ expect(editor.commands.applyList).toHaveBeenCalledWith(ListTypes.DISC);
96
+ });
82
97
  });
@@ -1,6 +1,7 @@
1
1
  import { ContextWindow } from './ContextWidnow';
2
2
 
3
3
  export class ContentNormalizer {
4
+ static PARSER = new DOMParser();
4
5
  static BLOCK_STYLES = ['text-align', 'line-height'];
5
6
  static BLOCK_NODE_NAMES = ['P', 'H1', 'H2', 'H3', 'H4'];
6
7
 
@@ -20,11 +21,17 @@ export class ContentNormalizer {
20
21
  ];
21
22
 
22
23
  static normalize(content) {
23
- return new ContentNormalizer(content).normalize();
24
+ const options = {
25
+ content,
26
+ parser: ContentNormalizer.PARSER
27
+ };
28
+
29
+ return new ContentNormalizer(options).normalize();
24
30
  }
25
31
 
26
- constructor(content) {
32
+ constructor({ content, parser }) {
27
33
  this._content = content;
34
+ this._parser = parser;
28
35
  this._dom = null;
29
36
  }
30
37
 
@@ -36,7 +43,7 @@ export class ContentNormalizer {
36
43
  }
37
44
 
38
45
  _normalizeTextContent() {
39
- this._dom = new DOMParser().parseFromString(this._content, 'text/html');
46
+ this._dom = this._parser.parseFromString(this._content, 'text/html');
40
47
 
41
48
  this._iterateNodes(this._removeEmptyNodes, this._isBlockNode);
42
49
  this._iterateNodes(this._normalizeListItems, (node) => node.tagName === 'LI');
@@ -59,7 +66,7 @@ export class ContentNormalizer {
59
66
  }
60
67
 
61
68
  _removeEmptyNodes(node) {
62
- if (node.childNodes.length === 0) {
69
+ if (!node.innerHTML.replace(/\n/g, '').trim()) {
63
70
  node.remove();
64
71
  }
65
72
  }
@@ -74,6 +81,8 @@ export class ContentNormalizer {
74
81
  fragment.append(node);
75
82
  };
76
83
 
84
+ this._assignElementProperties(itemEl, itemEl.parentElement, ContentNormalizer.BLOCK_STYLES);
85
+
77
86
  for (const node of children) {
78
87
  if (node.tagName === 'P') {
79
88
  append(node);
@@ -79,6 +79,20 @@ describe('normalize text content', () => {
79
79
  expect(ContentNormalizer.normalize(input)).toBe(output);
80
80
  });
81
81
 
82
+ test('should ignore space only nodes', () => {
83
+ const input = '<p>lorem ipsum 1</p><p> </p><p>lorem ipsum 2</p>';
84
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
85
+
86
+ expect(ContentNormalizer.normalize(input)).toBe(output);
87
+ });
88
+
89
+ test('should ignore newline chapters only nodes', () => {
90
+ const input = '<p>lorem ipsum 1</p><p>\n</p><p>lorem ipsum 2</p>';
91
+ const output = '<p>lorem ipsum 1</p><p>lorem ipsum 2</p>';
92
+
93
+ expect(ContentNormalizer.normalize(input)).toBe(output);
94
+ });
95
+
82
96
  test('should not ignore setting', () => {
83
97
  const input = '<p style="text-decoration-line: underline;">lorem ipsum</p>';
84
98
  const output = '<p><span style="text-decoration-line: underline;">lorem ipsum</span></p>';
@@ -106,4 +120,11 @@ describe('normalize text content', () => {
106
120
 
107
121
  expect(ContentNormalizer.normalize(input)).toBe(output);
108
122
  });
123
+
124
+ test('should assign block styles from list to paragraph', () => {
125
+ const input = '<ul style="line-height: 2;"><li>lorem ipsum</li></ul>';
126
+ const output = '<ul style="line-height: 2;"><li><p style="line-height: 2;">lorem ipsum</p></li></ul>';
127
+
128
+ expect(ContentNormalizer.normalize(input)).toBe(output);
129
+ });
109
130
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zipify/wysiwyg",
3
- "version": "1.0.0-dev.58",
3
+ "version": "1.0.0-dev.60",
4
4
  "description": "Zipify modification of TipTap text editor",
5
5
  "main": "dist/wysiwyg.mjs",
6
6
  "repository": {