@zipify/wysiwyg 3.1.0-2 → 3.1.0

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.
Files changed (65) hide show
  1. package/.eslintrc.js +17 -235
  2. package/.github/actions/setup/action.yaml +1 -1
  3. package/README.md +2 -0
  4. package/config/build/cli.config.js +6 -6
  5. package/config/build/lib.config.js +5 -3
  6. package/config/svgo.js +6 -3
  7. package/dist/cli.js +5 -4
  8. package/dist/wysiwyg.css +18 -18
  9. package/dist/wysiwyg.mjs +12496 -11732
  10. package/example/tooltip/Tooltip.js +92 -69
  11. package/example/tooltip/modifiers/TooltipCloseOnScrollModifier.js +2 -5
  12. package/example/tooltip/tooltip.css +8 -31
  13. package/lib/Wysiwyg.vue +3 -0
  14. package/lib/cli/commands/ToJsonCommand.js +6 -6
  15. package/lib/components/base/__tests__/Button.test.js +1 -1
  16. package/lib/components/base/composables/__tests__/useDeselectionLock.test.js +2 -2
  17. package/lib/components/base/composables/__tests__/useElementRef.test.js +1 -1
  18. package/lib/components/base/composables/__tests__/useModalToggler.test.js +0 -2
  19. package/lib/components/base/composables/__tests__/useValidator.test.js +2 -2
  20. package/lib/components/base/composables/useModalToggler.js +30 -24
  21. package/lib/components/toolbar/Toolbar.vue +1 -1
  22. package/lib/components/toolbar/base/__tests__/ToolbarDivider.test.js +1 -1
  23. package/lib/components/toolbar/controls/StylePresetControl.vue +1 -1
  24. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +0 -2
  25. package/lib/components/toolbar/controls/composables/__tests__/useRecentFonts.test.js +1 -1
  26. package/lib/components/toolbar/controls/link/composables/__tests__/useLink.test.js +2 -2
  27. package/lib/composables/useToolbar.js +24 -19
  28. package/lib/extensions/FontSize.js +1 -2
  29. package/lib/extensions/Link.js +2 -0
  30. package/lib/extensions/__tests__/BackgroundColor.test.js +2 -2
  31. package/lib/extensions/__tests__/FontColor.test.js +3 -3
  32. package/lib/extensions/__tests__/FontFamily.test.js +3 -3
  33. package/lib/extensions/__tests__/FontSize.test.js +3 -3
  34. package/lib/extensions/__tests__/FontWeight.test.js +4 -4
  35. package/lib/extensions/__tests__/LineHeight.test.js +2 -2
  36. package/lib/extensions/__tests__/Link.test.js +33 -5
  37. package/lib/extensions/__tests__/__snapshots__/Link.test.js.snap +27 -0
  38. package/lib/extensions/core/NodeProcessor.js +3 -4
  39. package/lib/extensions/core/__tests__/NodeProcessor.test.js +6 -8
  40. package/lib/extensions/core/__tests__/TextProcessor.test.js +1 -1
  41. package/lib/extensions/core/index.js +0 -2
  42. package/lib/extensions/core/plugins/PlaceholderPlugin.js +2 -2
  43. package/lib/extensions/list/List.js +4 -5
  44. package/lib/extensions/list/ListItem.js +1 -2
  45. package/lib/extensions/list/__tests__/List.test.js +1 -1
  46. package/lib/models/Font.js +2 -2
  47. package/lib/models/__tests__/Font.test.js +3 -9
  48. package/lib/services/ContentSerializer.js +8 -8
  49. package/lib/services/HtmlToJsonParser.js +3 -3
  50. package/lib/services/NodeFactory.js +6 -6
  51. package/lib/services/StylePresetRenderer.js +11 -11
  52. package/lib/services/__tests__/JsonSerializer.test.js +1 -1
  53. package/lib/services/__tests__/Storage.test.js +1 -1
  54. package/lib/services/__tests__/StylePresetRenderer.test.js +1 -1
  55. package/lib/services/normalizer/BrowserDomParser.js +2 -2
  56. package/lib/services/normalizer/ContentNormalizer.js +3 -3
  57. package/lib/services/normalizer/HtmlNormalizer.js +52 -52
  58. package/lib/services/normalizer/JsonNormalizer.js +21 -21
  59. package/lib/utils/__tests__/convertAlignment.test.js +1 -1
  60. package/lib/utils/__tests__/renderInlineSetting.test.js +2 -2
  61. package/package.json +50 -48
  62. package/lib/extensions/core/steps/AddNodeMarkStep.js +0 -66
  63. package/lib/extensions/core/steps/AttrStep.js +0 -60
  64. package/lib/extensions/core/steps/RemoveNodeMarkStep.js +0 -56
  65. package/lib/extensions/core/steps/index.js +0 -3
@@ -1,6 +1,10 @@
1
1
  import './tooltip.css';
2
2
 
3
- import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
3
+ import { popperGenerator, defaultModifiers } from '@popperjs/core/lib/popper-lite';
4
+ import arrow from '@popperjs/core/lib/modifiers/arrow';
5
+ import flip from '@popperjs/core/lib/modifiers/flip';
6
+ import offset from '@popperjs/core/lib/modifiers/offset';
7
+ import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow';
4
8
  import { TooltipCloseOnScrollModifier } from './modifiers';
5
9
 
6
10
  export class Tooltip {
@@ -14,11 +18,11 @@ export class Tooltip {
14
18
  static get animationOptions() {
15
19
  return {
16
20
  enterDuration: 150,
17
- leaveDuration: 100
21
+ leaveDuration: 200
18
22
  };
19
23
  }
20
24
 
21
- static get tooltipStyles() {
25
+ static get popperStyles() {
22
26
  return {
23
27
  willChange: 'transform',
24
28
  zIndex: 999999
@@ -31,17 +35,17 @@ export class Tooltip {
31
35
  /** @type {string} tooltip's content */
32
36
  message = '';
33
37
 
34
- /** @type {object} options for FloatingUI instance */
35
- tooltipOptions = {};
38
+ /** @type {object} options for Popper.js instance */
39
+ popperOptions = {};
36
40
 
37
41
  /** @type {object} styles for tooltip's wrapper element */
38
- tooltipStyles = {};
42
+ popperStyles = {};
39
43
 
40
44
  /** @type {{ enterDuration: number, leaveDuration: number }} tooltip's fade in / fade out durations */
41
45
  animationOptions = {};
42
46
 
43
- /** @type {function | null} created FloatingUI instance */
44
- floatingInstance = null;
47
+ /** @type {object | null} created Popper.js instance */
48
+ popperInstance = null;
45
49
 
46
50
  /** @type {{ tooltip: HTMLElement, container: HTMLElement, contentEl: HTMLElement, arrow: HTMLElement }} elements of tooltip */
47
51
  elements = {};
@@ -51,25 +55,25 @@ export class Tooltip {
51
55
  * @param params {object}
52
56
  * @param params.triggerEl {HTMLElement}
53
57
  * @param params.message {string}
54
- * @param [params.tooltipOptions] {{ placement: string, size: string }}
55
- * @param [params.tooltipStyles] {object}
58
+ * @param [params.popperOptions] {{ placement: string, size: string }}
59
+ * @param [params.popperStyles] {object}
56
60
  * @param [params.animationOptions] {{ enterDuration: number, leaveDuration: number }}
57
61
  */
58
- constructor({ triggerEl, message, tooltipOptions, tooltipStyles, animationOptions }) {
62
+ constructor({ triggerEl, message, popperOptions, popperStyles, animationOptions }) {
59
63
  this.triggerEl = triggerEl;
60
64
  this.message = message;
61
65
 
62
- this.tooltipOptions = Object.assign({}, Tooltip.defaultOptions, tooltipOptions);
63
- this.tooltipStyles = Object.assign({}, Tooltip.tooltipStyles, tooltipStyles);
66
+ this.popperOptions = Object.assign({}, Tooltip.defaultOptions, popperOptions);
67
+ this.popperStyles = Object.assign({}, Tooltip.popperStyles, popperStyles);
64
68
  this.animationOptions = Object.assign({}, Tooltip.animationOptions, animationOptions);
65
69
 
66
70
  this._isMacOS = window.navigator.userAgent.toLowerCase().includes('mac os');
67
71
 
68
- this.#initialize();
72
+ this._initialize();
69
73
  }
70
74
 
71
- #initialize() {
72
- let tooltip = this.#createTooltip();
75
+ _initialize() {
76
+ let tooltip = this._createTooltip();
73
77
 
74
78
  this.elements = {
75
79
  tooltip,
@@ -79,15 +83,13 @@ export class Tooltip {
79
83
  };
80
84
  }
81
85
 
82
- #createTooltip() {
86
+ _createTooltip() {
83
87
  let element = document.createElement('div');
84
88
 
85
- Object.keys(this.tooltipStyles).forEach((style) => (element.style[style] = this.tooltipStyles[style]));
86
-
87
- element.classList.add('zpa-tooltip__wrapper');
89
+ Object.keys(this.popperStyles).forEach((style) => (element.style[style] = this.popperStyles[style]));
88
90
 
89
91
  element.innerHTML = `
90
- <div class="zpa-tooltip zpa-tooltip--${this.tooltipOptions.size}" data-container>
92
+ <div class="zpa-tooltip zpa-tooltip--${this.popperOptions.size}" data-container>
91
93
  <div class="zpa-tooltip__content" data-content></div>
92
94
  <div class="zpa-tooltip__arrow" data-arrow></div>
93
95
  </div>`;
@@ -95,20 +97,20 @@ export class Tooltip {
95
97
  return element;
96
98
  }
97
99
 
98
- #renderContent(message) {
100
+ _renderContent(message) {
99
101
  // Do not use .innerHTML, it will break tooltips in Scripts section!
100
102
  this.elements.contentEl.textContent = message;
101
103
 
102
104
  this.elements.hotkey?.remove();
103
- this.#renderHotkeyTip();
105
+ this._renderHotkeyTip();
104
106
  }
105
107
 
106
- #renderHotkeyTip() {
108
+ _renderHotkeyTip() {
107
109
  const raw = this.triggerEl.dataset.tooltipHotkey;
108
110
 
109
111
  if (!raw) return;
110
112
 
111
- const parts = raw.split(' ').map(this.#formatHotkeyPart.bind(this));
113
+ const parts = raw.split(' ').map(this._formatHotkeyPart.bind(this));
112
114
  const tipEl = document.createElement('span');
113
115
 
114
116
  tipEl.classList.add('zpa-tooltip__hotkey');
@@ -117,7 +119,7 @@ export class Tooltip {
117
119
  this.elements.contentEl.append(tipEl);
118
120
  }
119
121
 
120
- #formatHotkeyPart(part) {
122
+ _formatHotkeyPart(part) {
121
123
  switch (part.toLowerCase()) {
122
124
  case 'mod':
123
125
  return this._isMacOS ? '⌘' : 'Ctrl';
@@ -129,90 +131,111 @@ export class Tooltip {
129
131
  }
130
132
  }
131
133
 
132
- #getModifiers() {
134
+ _getModifiers() {
133
135
  return [
134
- flip(),
135
- offset({ mainAxis: 8 }),
136
- shift({ padding: 4 }),
137
- arrow({ element: this.elements.arrow }),
136
+ {
137
+ name: 'arrow',
138
+ options: {
139
+ element: '[data-arrow]'
140
+ }
141
+ },
142
+ {
143
+ name: 'offset',
144
+ options: {
145
+ offset: [0, 8]
146
+ }
147
+ },
148
+ {
149
+ name: 'preventOverflow',
150
+ options: {
151
+ padding: {
152
+ top: 4,
153
+ bottom: 4,
154
+ left: 8,
155
+ right: 8
156
+ }
157
+ }
158
+ },
159
+ {
160
+ name: 'customAttributes',
161
+ enabled: true,
162
+ phase: 'beforeWrite',
163
+ requires: ['computeStyles'],
164
+ fn: ({ state }) => {
165
+ this.elements.arrow.classList.add(`zpa-tooltip__arrow--${state.placement}`);
166
+ state.attributes.popper = {};
167
+ }
168
+ },
138
169
  TooltipCloseOnScrollModifier.init({ tooltip: this })
139
170
  ];
140
171
  }
141
172
 
142
- #setTooltipDuration(duration = 0) {
173
+ _setTooltipDuration(duration = 0) {
143
174
  this.elements.container.style.transitionDuration = `${duration}ms`;
144
175
  }
145
176
 
146
- #renderPopper() {
147
- const modifiers = this.#getModifiers();
148
-
149
- this.floatingInstance = autoUpdate(this.triggerEl, this.elements.tooltip, async () => {
150
- const positioning = await computePosition(this.triggerEl, this.elements.tooltip, {
151
- placement: this.tooltipOptions.placement,
152
- middleware: [...modifiers]
153
- });
154
-
155
- const { x, y, middlewareData, placement } = positioning;
156
-
157
- if (middlewareData.arrow) this.#renderArrow(placement, middlewareData.arrow);
177
+ _renderPopper() {
178
+ const modifiers = this._getModifiers();
158
179
 
159
- Object.assign(this.elements.tooltip.style, {
160
- left: `${x}px`,
161
- top: `${y}px`
162
- });
180
+ const createPopper = popperGenerator({
181
+ defaultModifiers: [...defaultModifiers, arrow, flip, offset, preventOverflow]
163
182
  });
164
183
 
165
- return this.floatingInstance;
166
- }
167
-
168
- #renderArrow(placement, { x, y }) {
169
- this.elements.tooltip.classList.add(`zpa-tooltip--${placement}`);
170
-
171
- Object.assign(this.elements.arrow.style, {
172
- left: x != null ? `${x}px` : '',
173
- top: y != null ? `${y}px` : ''
184
+ this.popperInstance = createPopper(this.triggerEl, this.elements.tooltip, {
185
+ placement: this.popperOptions.placement,
186
+ modifiers
174
187
  });
188
+
189
+ return this.popperInstance;
175
190
  }
176
191
 
177
- #toggle(makeVisible) {
192
+ _toggle(makeVisible) {
178
193
  const { enterDuration, leaveDuration } = this.animationOptions;
179
194
 
180
- this.#setTooltipDuration(makeVisible ? enterDuration : leaveDuration);
195
+ this._setTooltipDuration(makeVisible ? enterDuration : leaveDuration);
181
196
  this.elements.container.classList.toggle('zpa-tooltip--open', makeVisible);
182
197
  }
183
198
 
184
199
  open() {
185
- if (this.floatingInstance) return;
200
+ if (this.popperInstance) {
201
+ return;
202
+ }
186
203
 
187
204
  document.body.appendChild(this.elements.tooltip);
188
- this.popperInstance = this.#renderPopper();
205
+ this.popperInstance = this._renderPopper();
189
206
 
190
- this.#renderContent(this.message);
191
- this.#toggle(true);
207
+ this._renderContent(this.message);
208
+ this._toggle(true);
192
209
  }
193
210
 
194
211
  close() {
195
- if (!this.floatingInstance) return;
212
+ if (!this.popperInstance) {
213
+ return;
214
+ }
196
215
 
197
- this.#toggle(false);
216
+ this._toggle(false);
198
217
 
199
218
  return setTimeout(() => {
200
219
  this.destroy();
201
220
  }, this.animationOptions.leaveDuration);
202
221
  }
203
222
 
223
+ update() {
224
+ this.popperInstance?.update();
225
+ }
226
+
204
227
  updateContent(content) {
205
228
  this.message = content;
206
- this.#renderContent(this.message);
229
+ this._renderContent(this.message);
230
+ this.update();
207
231
  }
208
232
 
209
233
  destroy() {
210
234
  this.elements.tooltip.remove();
211
235
 
212
236
  if (this.popperInstance) {
213
- // destroy instance
214
- this.floatingInstance();
215
- this.floatingInstance = null;
237
+ this.popperInstance.destroy();
238
+ this.popperInstance = null;
216
239
  }
217
240
  }
218
241
  }
@@ -21,7 +21,7 @@ export class TooltipCloseOnScrollModifier {
21
21
  /**
22
22
  * @param {Tooltip} tooltip - tooltip element
23
23
  *
24
- * @return {Partial<Modifier<any, any>>} - Floating modifier
24
+ * @return {Partial<Modifier<any, any>>} - Popper modifier
25
25
  * */
26
26
  static init({ tooltip }) {
27
27
  const modifier = new TooltipCloseOnScrollModifier(tooltip);
@@ -30,10 +30,7 @@ export class TooltipCloseOnScrollModifier {
30
30
  name: 'closeOnScroll',
31
31
  enabled: true,
32
32
  phase: 'main',
33
- fn: ({ x, y }) => {
34
- modifier.closeOnScroll();
35
- return { x, y };
36
- }
33
+ fn: () => modifier.closeOnScroll()
37
34
  };
38
35
  }
39
36
 
@@ -1,9 +1,3 @@
1
- .zpa-tooltip__wrapper {
2
- position: absolute;
3
- top: 0;
4
- left: 0;
5
- }
6
-
7
1
  .zpa-tooltip {
8
2
  position: relative;
9
3
  background-color: #36404C;
@@ -37,7 +31,6 @@
37
31
  }
38
32
 
39
33
  .zpa-tooltip__arrow {
40
- position: absolute;
41
34
  width: 16px;
42
35
  height: 16px;
43
36
  color: #36404C;
@@ -50,15 +43,11 @@
50
43
  border-style: solid;
51
44
  }
52
45
 
53
- .zpa-tooltip--top {
54
- transform-origin: center bottom;
55
- }
56
-
57
- .zpa-tooltip--top .zpa-tooltip__arrow {
46
+ .zpa-tooltip__arrow--top {
58
47
  bottom: 0;
59
48
  }
60
49
 
61
- .zpa-tooltip--top .zpa-tooltip__arrow::before {
50
+ .zpa-tooltip__arrow--top::before {
62
51
  bottom: -7px;
63
52
  left: 0;
64
53
  border-width: 8px 8px 0;
@@ -66,15 +55,11 @@
66
55
  transform-origin: center top;
67
56
  }
68
57
 
69
- .zpa-tooltip--bottom {
70
- transform-origin: center top;
71
- }
72
-
73
- .zpa-tooltip--bottom .zpa-tooltip__arrow {
58
+ .zpa-tooltip__arrow--bottom {
74
59
  top: 0;
75
60
  }
76
61
 
77
- .zpa-tooltip--bottom .zpa-tooltip__arrow::before {
62
+ .zpa-tooltip__arrow--bottom::before {
78
63
  top: -7px;
79
64
  left: 0;
80
65
  border-width: 0 8px 8px;
@@ -82,30 +67,22 @@
82
67
  transform-origin: center bottom;
83
68
  }
84
69
 
85
- .zpa-tooltip--left {
86
- transform-origin: center right;
87
- }
88
-
89
- .zpa-tooltip--left .zpa-tooltip__arrow {
70
+ .zpa-tooltip__arrow--left {
90
71
  right: 0;
91
72
  }
92
73
 
93
- .zpa-tooltip--left .zpa-tooltip__arrow::before {
74
+ .zpa-tooltip__arrow--left::before {
94
75
  border-width: 8px 0 8px 8px;
95
76
  border-left-color: initial;
96
77
  right: -7px;
97
78
  transform-origin: center left;
98
79
  }
99
80
 
100
- .zpa-tooltip--right {
101
- transform-origin: center left;
102
- }
103
-
104
- .zpa-tooltip--right .zpa-tooltip__arrow {
81
+ .zpa-tooltip__arrow--right {
105
82
  left: 0;
106
83
  }
107
84
 
108
- .zpa-tooltip--right .zpa-tooltip__arrow::before {
85
+ .zpa-tooltip__arrow--right::before {
109
86
  left: -7px;
110
87
  border-width: 8px 8px 8px 0;
111
88
  border-right-color: initial;
package/lib/Wysiwyg.vue CHANGED
@@ -155,9 +155,11 @@ export default {
155
155
  isActiveRef: isToolbarActiveRef,
156
156
  offsets: props.toolbarOffsets
157
157
  });
158
+ const updateToolbar = () => toolbar.update();
158
159
 
159
160
  function onChange(content) {
160
161
  emit('input', content);
162
+ updateToolbar();
161
163
  }
162
164
 
163
165
  const pageBlocks = toRef(props, 'pageBlocks');
@@ -206,6 +208,7 @@ export default {
206
208
  toolbarRef,
207
209
  wysiwygRef,
208
210
  toolbar,
211
+ updateToolbar,
209
212
  getContent,
210
213
  getContentCustomization
211
214
  };
@@ -32,14 +32,14 @@ export class ToJsonCommand extends Command {
32
32
  nodeDomParser: new NodeDomParser()
33
33
  });
34
34
 
35
- const jsonList = htmlList.map((html) => serializer.toJSON(this.#formatInputHtml(html)));
35
+ const jsonList = htmlList.map((html) => serializer.toJSON(this._formatInputHtml(html)));
36
36
  const content = jsonList.length === 1 ? jsonList[0] : jsonList;
37
- const json = this.#stringifyContent(content);
37
+ const json = this._stringifyContent(content);
38
38
 
39
- this.output(format === 'rb' ? this.#formatOutputRb(json) : json);
39
+ this.output(format === 'rb' ? this._formatOutputRb(json) : json);
40
40
  }
41
41
 
42
- #formatInputHtml(html) {
42
+ _formatInputHtml(html) {
43
43
  return html
44
44
  .replace(/\\(["'])/g, '$1')
45
45
  .replace(/rgba\(\d{1,3}, ?\d{1,3}, ?\d{1,3}, (\d{1,2}%)\)/g, (substring, alpha) => {
@@ -47,13 +47,13 @@ export class ToJsonCommand extends Command {
47
47
  });
48
48
  }
49
49
 
50
- #stringifyContent(content) {
50
+ _stringifyContent(content) {
51
51
  const skipNullValue = (_, value) => value === null ? undefined : value;
52
52
 
53
53
  return JSON.stringify(content, skipNullValue, 2);
54
54
  }
55
55
 
56
- #formatOutputRb(json) {
56
+ _formatOutputRb(json) {
57
57
  return json
58
58
  .replace(/\\"/g, '"')
59
59
  .replace(/font-family: ?'(.+)'/g, 'font-family: "$1"')
@@ -42,7 +42,7 @@ describe('rendering', () => {
42
42
  expect(wrapper.classes('zw-button--icon')).toBeTruthy();
43
43
  });
44
44
 
45
- test('should render icon style', () => {
45
+ test('should render iconless style', () => {
46
46
  const wrapper = createComponent({ icon: false });
47
47
 
48
48
  expect(wrapper.classes('zw-button--icon')).toBeFalsy();
@@ -33,8 +33,8 @@ describe('prevent deselection', () => {
33
33
  function click(el, { target } = {}) {
34
34
  const event = new MouseEvent('mousedown');
35
35
 
36
- event.preventDefault = jest.fn();
37
- event.stopPropagation = jest.fn();
36
+ jest.spyOn(event, 'preventDefault').mockImplementation();
37
+ jest.spyOn(event, 'stopPropagation').mockImplementation();
38
38
  setReadonlyProperty(event, 'target', target || { tagName: 'DIV' });
39
39
 
40
40
  el.dispatchEvent(event);
@@ -10,7 +10,7 @@ describe('resolve element from ref', () => {
10
10
  test('should return null if no element', () => {
11
11
  const elementRef = useElementRef(ref(null));
12
12
 
13
- expect(elementRef.value).toBe(null);
13
+ expect(elementRef.value).toBeNull();
14
14
  });
15
15
 
16
16
  test('should return element if dom ref', () => {
@@ -4,8 +4,6 @@ import { InjectionTokens } from '../../../../injectionTokens';
4
4
  import { useModalToggler } from '../useModalToggler';
5
5
  import { useElementRef } from '../useElementRef';
6
6
 
7
- jest.mock('@floating-ui/dom');
8
-
9
7
  const createEditor = () => ({
10
8
  commands: {
11
9
  storeSelection: jest.fn(),
@@ -24,7 +24,7 @@ describe('validation', () => {
24
24
 
25
25
  validator.validate();
26
26
 
27
- expect(validator.error.value).toBe(null);
27
+ expect(validator.error.value).toBeNull();
28
28
  });
29
29
 
30
30
  test('should reset error', () => {
@@ -39,6 +39,6 @@ describe('validation', () => {
39
39
  expect(validator.error.value).toBe('Can\'t be empty');
40
40
 
41
41
  validator.reset();
42
- expect(validator.error.value).toBe(null);
42
+ expect(validator.error.value).toBeNull();
43
43
  });
44
44
  });
@@ -1,55 +1,61 @@
1
- import { inject, nextTick, ref } from 'vue';
2
- import { autoUpdate, computePosition, limitShift, offset, shift } from '@floating-ui/dom';
1
+ import { createPopper } from '@popperjs/core';
2
+ import { inject, nextTick, ref, onUnmounted } from 'vue';
3
3
  import { InjectionTokens } from '../../../injectionTokens';
4
4
  import { useElementRef } from './useElementRef';
5
5
 
6
6
  export function useModalToggler({ onBeforeOpened, onClosed, wrapperRef, modalRef } = {}) {
7
7
  const editor = inject(InjectionTokens.EDITOR);
8
8
  const isOpened = ref(false);
9
- let floatingInstance;
9
+ let popper;
10
10
 
11
- function initModal() {
11
+ function initPopper() {
12
12
  const wrapperEl = useElementRef(wrapperRef);
13
13
  const modalEl = useElementRef(modalRef);
14
14
 
15
- floatingInstance = autoUpdate(wrapperEl.value, modalEl.value, async () => {
16
- const positioning = await computePosition(wrapperEl.value, modalEl.value, {
17
- placement: 'bottom',
18
- strategy: 'fixed',
19
- middleware: [
20
- shift({ padding: 16, crossAxis: true, limiter: limitShift() }),
21
- offset({ mainAxis: 4 })
22
- ]
23
- });
24
-
25
- const { x, y } = positioning;
26
-
27
- Object.assign(modalEl.value, {
28
- left: `${x}px`,
29
- top: `${y}px`
30
- });
15
+ popper = createPopper(wrapperEl.value, modalEl.value, {
16
+ placement: 'bottom',
17
+ strategy: 'fixed',
18
+ modifiers: [
19
+ {
20
+ name: 'offset',
21
+ options: {
22
+ offset: [0, 4]
23
+ }
24
+ },
25
+ {
26
+ name: 'preventOverflow',
27
+ options: {
28
+ padding: 16
29
+ }
30
+ },
31
+ {
32
+ name: 'flip',
33
+ enabled: false
34
+ }
35
+ ]
31
36
  });
32
37
  }
33
38
 
34
39
  async function open() {
35
- if (isOpened.value) return;
36
-
37
40
  onBeforeOpened?.();
38
41
  editor.commands.storeSelection();
39
42
  isOpened.value = true;
40
43
 
41
44
  await nextTick();
42
45
 
43
- initModal();
46
+ initPopper();
44
47
  }
45
48
 
46
49
  function close() {
47
50
  isOpened.value = false;
48
- floatingInstance?.();
49
51
  editor.commands.restoreSelection();
50
52
  onClosed?.();
51
53
  }
52
54
 
55
+ onUnmounted(() => {
56
+ popper?.destroy();
57
+ });
58
+
53
59
  const toggle = (toOpen) => toOpen ? open() : close();
54
60
 
55
61
  return { isOpened, open, close, toggle };
@@ -68,7 +68,7 @@ export default {
68
68
  color: rgb(var(--zw-color-n70));
69
69
  z-index: 999999;
70
70
  text-align: left;
71
- position: fixed;
71
+ position: absolute;
72
72
  }
73
73
 
74
74
  .zw-toolbar::before,
@@ -18,7 +18,7 @@ describe('rendering', () => {
18
18
  });
19
19
 
20
20
 
21
- test('should render vertical divider', () => {
21
+ test('should render horizontal divider', () => {
22
22
  const wrapper = createComponent({ isHorizontal: true });
23
23
 
24
24
  expect(wrapper.classes('zw-toolbar__divider--horizontal')).toBeTruthy();
@@ -73,7 +73,7 @@ export default {
73
73
  .run();
74
74
  };
75
75
 
76
- const tooltip = computed(() => isCustomized.value ? 'Reset Styles to the Page Styles' : '');
76
+ const tooltip = computed(() => isCustomized.value ? 'Reset Styles to Page Styles' : '');
77
77
 
78
78
  const removeCustomization = () => editor.chain().focus().removePresetCustomization().run();
79
79
 
@@ -4,8 +4,6 @@ import { InjectionTokens } from '../../../../injectionTokens';
4
4
  import { Button, Modal, NumberField, Range } from '../../../base';
5
5
  import LineHeightControl from '../LineHeightControl';
6
6
 
7
- jest.mock('@floating-ui/dom');
8
-
9
7
  const createEditor = ({ height } = {}) => {
10
8
  const heightRef = ref(height ?? '1.2');
11
9
 
@@ -57,7 +57,7 @@ describe('list of fonts', () => {
57
57
  expect(recentFonts.fonts.value).toEqual(['Lato', 'Roboto', 'Source Sans']);
58
58
  });
59
59
 
60
- test('should move up font in list', () => {
60
+ test('should move up font in list new font', () => {
61
61
  const recentFonts = createWrapper({
62
62
  initial: ['Roboto', 'Source Sans', 'Times New Roman']
63
63
  });
@@ -97,7 +97,7 @@ describe('actions with link', () => {
97
97
  });
98
98
  });
99
99
 
100
- test('should apply link target', () => {
100
+ test('should apply link target to new window', () => {
101
101
  const link = useComposable();
102
102
 
103
103
  link.updateTarget(false);
@@ -105,7 +105,7 @@ describe('actions with link', () => {
105
105
  expect(link.linkData.value.target).toBe(LinkTargets.SELF);
106
106
  });
107
107
 
108
- test('should apply link target', () => {
108
+ test('should apply link target to current window', () => {
109
109
  const link = useComposable();
110
110
 
111
111
  link.updateTarget(true);