@zipify/wysiwyg 3.0.2 → 3.1.0-2

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.
@@ -1,10 +1,6 @@
1
1
  import './tooltip.css';
2
2
 
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';
3
+ import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
8
4
  import { TooltipCloseOnScrollModifier } from './modifiers';
9
5
 
10
6
  export class Tooltip {
@@ -18,11 +14,11 @@ export class Tooltip {
18
14
  static get animationOptions() {
19
15
  return {
20
16
  enterDuration: 150,
21
- leaveDuration: 200
17
+ leaveDuration: 100
22
18
  };
23
19
  }
24
20
 
25
- static get popperStyles() {
21
+ static get tooltipStyles() {
26
22
  return {
27
23
  willChange: 'transform',
28
24
  zIndex: 999999
@@ -35,17 +31,17 @@ export class Tooltip {
35
31
  /** @type {string} tooltip's content */
36
32
  message = '';
37
33
 
38
- /** @type {object} options for Popper.js instance */
39
- popperOptions = {};
34
+ /** @type {object} options for FloatingUI instance */
35
+ tooltipOptions = {};
40
36
 
41
37
  /** @type {object} styles for tooltip's wrapper element */
42
- popperStyles = {};
38
+ tooltipStyles = {};
43
39
 
44
40
  /** @type {{ enterDuration: number, leaveDuration: number }} tooltip's fade in / fade out durations */
45
41
  animationOptions = {};
46
42
 
47
- /** @type {object | null} created Popper.js instance */
48
- popperInstance = null;
43
+ /** @type {function | null} created FloatingUI instance */
44
+ floatingInstance = null;
49
45
 
50
46
  /** @type {{ tooltip: HTMLElement, container: HTMLElement, contentEl: HTMLElement, arrow: HTMLElement }} elements of tooltip */
51
47
  elements = {};
@@ -55,25 +51,25 @@ export class Tooltip {
55
51
  * @param params {object}
56
52
  * @param params.triggerEl {HTMLElement}
57
53
  * @param params.message {string}
58
- * @param [params.popperOptions] {{ placement: string, size: string }}
59
- * @param [params.popperStyles] {object}
54
+ * @param [params.tooltipOptions] {{ placement: string, size: string }}
55
+ * @param [params.tooltipStyles] {object}
60
56
  * @param [params.animationOptions] {{ enterDuration: number, leaveDuration: number }}
61
57
  */
62
- constructor({ triggerEl, message, popperOptions, popperStyles, animationOptions }) {
58
+ constructor({ triggerEl, message, tooltipOptions, tooltipStyles, animationOptions }) {
63
59
  this.triggerEl = triggerEl;
64
60
  this.message = message;
65
61
 
66
- this.popperOptions = Object.assign({}, Tooltip.defaultOptions, popperOptions);
67
- this.popperStyles = Object.assign({}, Tooltip.popperStyles, popperStyles);
62
+ this.tooltipOptions = Object.assign({}, Tooltip.defaultOptions, tooltipOptions);
63
+ this.tooltipStyles = Object.assign({}, Tooltip.tooltipStyles, tooltipStyles);
68
64
  this.animationOptions = Object.assign({}, Tooltip.animationOptions, animationOptions);
69
65
 
70
66
  this._isMacOS = window.navigator.userAgent.toLowerCase().includes('mac os');
71
67
 
72
- this._initialize();
68
+ this.#initialize();
73
69
  }
74
70
 
75
- _initialize() {
76
- let tooltip = this._createTooltip();
71
+ #initialize() {
72
+ let tooltip = this.#createTooltip();
77
73
 
78
74
  this.elements = {
79
75
  tooltip,
@@ -83,13 +79,15 @@ export class Tooltip {
83
79
  };
84
80
  }
85
81
 
86
- _createTooltip() {
82
+ #createTooltip() {
87
83
  let element = document.createElement('div');
88
84
 
89
- Object.keys(this.popperStyles).forEach((style) => (element.style[style] = this.popperStyles[style]));
85
+ Object.keys(this.tooltipStyles).forEach((style) => (element.style[style] = this.tooltipStyles[style]));
86
+
87
+ element.classList.add('zpa-tooltip__wrapper');
90
88
 
91
89
  element.innerHTML = `
92
- <div class="zpa-tooltip zpa-tooltip--${this.popperOptions.size}" data-container>
90
+ <div class="zpa-tooltip zpa-tooltip--${this.tooltipOptions.size}" data-container>
93
91
  <div class="zpa-tooltip__content" data-content></div>
94
92
  <div class="zpa-tooltip__arrow" data-arrow></div>
95
93
  </div>`;
@@ -97,20 +95,20 @@ export class Tooltip {
97
95
  return element;
98
96
  }
99
97
 
100
- _renderContent(message) {
98
+ #renderContent(message) {
101
99
  // Do not use .innerHTML, it will break tooltips in Scripts section!
102
100
  this.elements.contentEl.textContent = message;
103
101
 
104
102
  this.elements.hotkey?.remove();
105
- this._renderHotkeyTip();
103
+ this.#renderHotkeyTip();
106
104
  }
107
105
 
108
- _renderHotkeyTip() {
106
+ #renderHotkeyTip() {
109
107
  const raw = this.triggerEl.dataset.tooltipHotkey;
110
108
 
111
109
  if (!raw) return;
112
110
 
113
- const parts = raw.split(' ').map(this._formatHotkeyPart.bind(this));
111
+ const parts = raw.split(' ').map(this.#formatHotkeyPart.bind(this));
114
112
  const tipEl = document.createElement('span');
115
113
 
116
114
  tipEl.classList.add('zpa-tooltip__hotkey');
@@ -119,7 +117,7 @@ export class Tooltip {
119
117
  this.elements.contentEl.append(tipEl);
120
118
  }
121
119
 
122
- _formatHotkeyPart(part) {
120
+ #formatHotkeyPart(part) {
123
121
  switch (part.toLowerCase()) {
124
122
  case 'mod':
125
123
  return this._isMacOS ? '⌘' : 'Ctrl';
@@ -131,111 +129,90 @@ export class Tooltip {
131
129
  }
132
130
  }
133
131
 
134
- _getModifiers() {
132
+ #getModifiers() {
135
133
  return [
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
- },
134
+ flip(),
135
+ offset({ mainAxis: 8 }),
136
+ shift({ padding: 4 }),
137
+ arrow({ element: this.elements.arrow }),
169
138
  TooltipCloseOnScrollModifier.init({ tooltip: this })
170
139
  ];
171
140
  }
172
141
 
173
- _setTooltipDuration(duration = 0) {
142
+ #setTooltipDuration(duration = 0) {
174
143
  this.elements.container.style.transitionDuration = `${duration}ms`;
175
144
  }
176
145
 
177
- _renderPopper() {
178
- const modifiers = this._getModifiers();
146
+ #renderPopper() {
147
+ const modifiers = this.#getModifiers();
179
148
 
180
- const createPopper = popperGenerator({
181
- defaultModifiers: [...defaultModifiers, arrow, flip, offset, preventOverflow]
182
- });
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);
183
158
 
184
- this.popperInstance = createPopper(this.triggerEl, this.elements.tooltip, {
185
- placement: this.popperOptions.placement,
186
- modifiers
159
+ Object.assign(this.elements.tooltip.style, {
160
+ left: `${x}px`,
161
+ top: `${y}px`
162
+ });
187
163
  });
188
164
 
189
- return this.popperInstance;
165
+ return this.floatingInstance;
190
166
  }
191
167
 
192
- _toggle(makeVisible) {
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` : ''
174
+ });
175
+ }
176
+
177
+ #toggle(makeVisible) {
193
178
  const { enterDuration, leaveDuration } = this.animationOptions;
194
179
 
195
- this._setTooltipDuration(makeVisible ? enterDuration : leaveDuration);
180
+ this.#setTooltipDuration(makeVisible ? enterDuration : leaveDuration);
196
181
  this.elements.container.classList.toggle('zpa-tooltip--open', makeVisible);
197
182
  }
198
183
 
199
184
  open() {
200
- if (this.popperInstance) {
201
- return;
202
- }
185
+ if (this.floatingInstance) return;
203
186
 
204
187
  document.body.appendChild(this.elements.tooltip);
205
- this.popperInstance = this._renderPopper();
188
+ this.popperInstance = this.#renderPopper();
206
189
 
207
- this._renderContent(this.message);
208
- this._toggle(true);
190
+ this.#renderContent(this.message);
191
+ this.#toggle(true);
209
192
  }
210
193
 
211
194
  close() {
212
- if (!this.popperInstance) {
213
- return;
214
- }
195
+ if (!this.floatingInstance) return;
215
196
 
216
- this._toggle(false);
197
+ this.#toggle(false);
217
198
 
218
199
  return setTimeout(() => {
219
200
  this.destroy();
220
201
  }, this.animationOptions.leaveDuration);
221
202
  }
222
203
 
223
- update() {
224
- this.popperInstance?.update();
225
- }
226
-
227
204
  updateContent(content) {
228
205
  this.message = content;
229
- this._renderContent(this.message);
230
- this.update();
206
+ this.#renderContent(this.message);
231
207
  }
232
208
 
233
209
  destroy() {
234
210
  this.elements.tooltip.remove();
235
211
 
236
212
  if (this.popperInstance) {
237
- this.popperInstance.destroy();
238
- this.popperInstance = null;
213
+ // destroy instance
214
+ this.floatingInstance();
215
+ this.floatingInstance = null;
239
216
  }
240
217
  }
241
218
  }
@@ -21,7 +21,7 @@ export class TooltipCloseOnScrollModifier {
21
21
  /**
22
22
  * @param {Tooltip} tooltip - tooltip element
23
23
  *
24
- * @return {Partial<Modifier<any, any>>} - Popper modifier
24
+ * @return {Partial<Modifier<any, any>>} - Floating modifier
25
25
  * */
26
26
  static init({ tooltip }) {
27
27
  const modifier = new TooltipCloseOnScrollModifier(tooltip);
@@ -30,7 +30,10 @@ export class TooltipCloseOnScrollModifier {
30
30
  name: 'closeOnScroll',
31
31
  enabled: true,
32
32
  phase: 'main',
33
- fn: () => modifier.closeOnScroll()
33
+ fn: ({ x, y }) => {
34
+ modifier.closeOnScroll();
35
+ return { x, y };
36
+ }
34
37
  };
35
38
  }
36
39
 
@@ -1,3 +1,9 @@
1
+ .zpa-tooltip__wrapper {
2
+ position: absolute;
3
+ top: 0;
4
+ left: 0;
5
+ }
6
+
1
7
  .zpa-tooltip {
2
8
  position: relative;
3
9
  background-color: #36404C;
@@ -31,6 +37,7 @@
31
37
  }
32
38
 
33
39
  .zpa-tooltip__arrow {
40
+ position: absolute;
34
41
  width: 16px;
35
42
  height: 16px;
36
43
  color: #36404C;
@@ -43,11 +50,15 @@
43
50
  border-style: solid;
44
51
  }
45
52
 
46
- .zpa-tooltip__arrow--top {
53
+ .zpa-tooltip--top {
54
+ transform-origin: center bottom;
55
+ }
56
+
57
+ .zpa-tooltip--top .zpa-tooltip__arrow {
47
58
  bottom: 0;
48
59
  }
49
60
 
50
- .zpa-tooltip__arrow--top::before {
61
+ .zpa-tooltip--top .zpa-tooltip__arrow::before {
51
62
  bottom: -7px;
52
63
  left: 0;
53
64
  border-width: 8px 8px 0;
@@ -55,11 +66,15 @@
55
66
  transform-origin: center top;
56
67
  }
57
68
 
58
- .zpa-tooltip__arrow--bottom {
69
+ .zpa-tooltip--bottom {
70
+ transform-origin: center top;
71
+ }
72
+
73
+ .zpa-tooltip--bottom .zpa-tooltip__arrow {
59
74
  top: 0;
60
75
  }
61
76
 
62
- .zpa-tooltip__arrow--bottom::before {
77
+ .zpa-tooltip--bottom .zpa-tooltip__arrow::before {
63
78
  top: -7px;
64
79
  left: 0;
65
80
  border-width: 0 8px 8px;
@@ -67,22 +82,30 @@
67
82
  transform-origin: center bottom;
68
83
  }
69
84
 
70
- .zpa-tooltip__arrow--left {
85
+ .zpa-tooltip--left {
86
+ transform-origin: center right;
87
+ }
88
+
89
+ .zpa-tooltip--left .zpa-tooltip__arrow {
71
90
  right: 0;
72
91
  }
73
92
 
74
- .zpa-tooltip__arrow--left::before {
93
+ .zpa-tooltip--left .zpa-tooltip__arrow::before {
75
94
  border-width: 8px 0 8px 8px;
76
95
  border-left-color: initial;
77
96
  right: -7px;
78
97
  transform-origin: center left;
79
98
  }
80
99
 
81
- .zpa-tooltip__arrow--right {
100
+ .zpa-tooltip--right {
101
+ transform-origin: center left;
102
+ }
103
+
104
+ .zpa-tooltip--right .zpa-tooltip__arrow {
82
105
  left: 0;
83
106
  }
84
107
 
85
- .zpa-tooltip__arrow--right::before {
108
+ .zpa-tooltip--right .zpa-tooltip__arrow::before {
86
109
  left: -7px;
87
110
  border-width: 8px 8px 8px 0;
88
111
  border-right-color: initial;
package/lib/Wysiwyg.vue CHANGED
@@ -155,11 +155,9 @@ export default {
155
155
  isActiveRef: isToolbarActiveRef,
156
156
  offsets: props.toolbarOffsets
157
157
  });
158
- const updateToolbar = () => toolbar.update();
159
158
 
160
159
  function onChange(content) {
161
160
  emit('input', content);
162
- updateToolbar();
163
161
  }
164
162
 
165
163
  const pageBlocks = toRef(props, 'pageBlocks');
@@ -208,7 +206,6 @@ export default {
208
206
  toolbarRef,
209
207
  wysiwygRef,
210
208
  toolbar,
211
- updateToolbar,
212
209
  getContent,
213
210
  getContentCustomization
214
211
  };
@@ -4,6 +4,8 @@ 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
+
7
9
  const createEditor = () => ({
8
10
  commands: {
9
11
  storeSelection: jest.fn(),
@@ -1,61 +1,55 @@
1
- import { createPopper } from '@popperjs/core';
2
- import { inject, nextTick, ref, onUnmounted } from 'vue';
1
+ import { inject, nextTick, ref } from 'vue';
2
+ import { autoUpdate, computePosition, limitShift, offset, shift } from '@floating-ui/dom';
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 popper;
9
+ let floatingInstance;
10
10
 
11
- function initPopper() {
11
+ function initModal() {
12
12
  const wrapperEl = useElementRef(wrapperRef);
13
13
  const modalEl = useElementRef(modalRef);
14
14
 
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
- ]
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
+ });
36
31
  });
37
32
  }
38
33
 
39
34
  async function open() {
35
+ if (isOpened.value) return;
36
+
40
37
  onBeforeOpened?.();
41
38
  editor.commands.storeSelection();
42
39
  isOpened.value = true;
43
40
 
44
41
  await nextTick();
45
42
 
46
- initPopper();
43
+ initModal();
47
44
  }
48
45
 
49
46
  function close() {
50
47
  isOpened.value = false;
48
+ floatingInstance?.();
51
49
  editor.commands.restoreSelection();
52
50
  onClosed?.();
53
51
  }
54
52
 
55
- onUnmounted(() => {
56
- popper?.destroy();
57
- });
58
-
59
53
  const toggle = (toOpen) => toOpen ? open() : close();
60
54
 
61
55
  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: absolute;
71
+ position: fixed;
72
72
  }
73
73
 
74
74
  .zw-toolbar::before,
@@ -4,6 +4,8 @@ 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
+
7
9
  const createEditor = ({ height } = {}) => {
8
10
  const heightRef = ref(height ?? '1.2');
9
11
 
@@ -1,38 +1,33 @@
1
- import { createPopper } from '@popperjs/core';
1
+ import { onUnmounted } from 'vue';
2
+ import { autoUpdate, computePosition, limitShift, offset, shift } from '@floating-ui/dom';
2
3
  import { useElementRef } from '../components/base';
3
4
 
4
5
  export function useToolbar({ wrapperRef, offsets, isActiveRef, placementRef }) {
5
6
  const wrapperEl = useElementRef(wrapperRef);
6
- let popper;
7
+ let floatingInstance;
7
8
 
8
9
  function mount(element) {
9
- popper = createPopper(wrapperEl.value, element, {
10
- placement: placementRef.value,
11
- strategy: 'fixed',
12
- modifiers: [
13
- {
14
- name: 'offset',
15
- options: { offset: offsets }
16
- },
17
- {
18
- name: 'preventOverflow',
19
- options: {
20
- altAxis: true,
21
- padding: 2
22
- }
23
- },
24
- {
25
- name: 'flip',
26
- enabled: false
27
- }
28
- ]
10
+ floatingInstance = autoUpdate(wrapperEl.value, element, async () => {
11
+ const positioning = await computePosition(wrapperEl.value, element, {
12
+ placement: placementRef.value,
13
+ strategy: 'fixed',
14
+ middleware: [
15
+ shift({ padding: 16, crossAxis: true, limiter: limitShift() }),
16
+ offset({ crossAxis: offsets[0], mainAxis: offsets[1] })]
17
+ });
18
+
19
+ const { x, y } = positioning;
20
+
21
+ Object.assign(element.style, {
22
+ left: `${x}px`,
23
+ top: `${y}px`
24
+ });
29
25
  });
30
26
  }
31
27
 
32
- const update = () => popper?.update();
28
+ onUnmounted(() => floatingInstance?.());
33
29
 
34
30
  return {
35
- update,
36
31
  mount,
37
32
  isActiveRef,
38
33
  offsets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zipify/wysiwyg",
3
- "version": "3.0.2",
3
+ "version": "3.1.0-2",
4
4
  "description": "Zipify modification of TipTap text editor",
5
5
  "main": "dist/wysiwyg.mjs",
6
6
  "bin": {
@@ -31,7 +31,7 @@
31
31
  "prepare": "husky install"
32
32
  },
33
33
  "dependencies": {
34
- "@popperjs/core": "^2.11.6",
34
+ "@floating-ui/dom": "^1.0.7",
35
35
  "@tiptap/core": "2.0.0-beta.195",
36
36
  "@tiptap/extension-document": "2.0.0-beta.195",
37
37
  "@tiptap/extension-heading": "2.0.0-beta.195",
@@ -47,7 +47,7 @@
47
47
  "simplebar": "^5.3.8"
48
48
  },
49
49
  "peerDependencies": {
50
- "@zipify/colorpicker": "^2.2",
50
+ "@zipify/colorpicker": "^2.3",
51
51
  "vue": "^2.7"
52
52
  },
53
53
  "peerDependenciesMeta": {
@@ -72,7 +72,7 @@
72
72
  "@rollup/plugin-replace": "^4.0.0",
73
73
  "@vue/test-utils": "^1.3.0",
74
74
  "@vue/vue2-jest": "^29.1.0",
75
- "@zipify/colorpicker": "^2.2.1",
75
+ "@zipify/colorpicker": "^2.3.0-0",
76
76
  "babel-jest": "^29.0.3",
77
77
  "eslint": "8.23.1",
78
78
  "eslint-plugin-import": "^2.26.0",