@zipify/wysiwyg 3.0.0 → 3.1.0-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.
@@ -1,11 +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';
8
- import { TooltipCloseOnScrollModifier } from './modifiers';
3
+ import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
9
4
 
10
5
  export class Tooltip {
11
6
  static get defaultOptions() {
@@ -18,11 +13,11 @@ export class Tooltip {
18
13
  static get animationOptions() {
19
14
  return {
20
15
  enterDuration: 150,
21
- leaveDuration: 200
16
+ leaveDuration: 100
22
17
  };
23
18
  }
24
19
 
25
- static get popperStyles() {
20
+ static get tooltipStyles() {
26
21
  return {
27
22
  willChange: 'transform',
28
23
  zIndex: 999999
@@ -35,17 +30,17 @@ export class Tooltip {
35
30
  /** @type {string} tooltip's content */
36
31
  message = '';
37
32
 
38
- /** @type {object} options for Popper.js instance */
39
- popperOptions = {};
33
+ /** @type {object} options for FloatingUI instance */
34
+ tooltipOptions = {};
40
35
 
41
36
  /** @type {object} styles for tooltip's wrapper element */
42
- popperStyles = {};
37
+ tooltipStyles = {};
43
38
 
44
39
  /** @type {{ enterDuration: number, leaveDuration: number }} tooltip's fade in / fade out durations */
45
40
  animationOptions = {};
46
41
 
47
- /** @type {object | null} created Popper.js instance */
48
- popperInstance = null;
42
+ /** @type {function | null} created FloatingUI instance */
43
+ floatingInstance = null;
49
44
 
50
45
  /** @type {{ tooltip: HTMLElement, container: HTMLElement, contentEl: HTMLElement, arrow: HTMLElement }} elements of tooltip */
51
46
  elements = {};
@@ -55,25 +50,25 @@ export class Tooltip {
55
50
  * @param params {object}
56
51
  * @param params.triggerEl {HTMLElement}
57
52
  * @param params.message {string}
58
- * @param [params.popperOptions] {{ placement: string, size: string }}
59
- * @param [params.popperStyles] {object}
53
+ * @param [params.tooltipOptions] {{ placement: string, size: string }}
54
+ * @param [params.tooltipStyles] {object}
60
55
  * @param [params.animationOptions] {{ enterDuration: number, leaveDuration: number }}
61
56
  */
62
- constructor({ triggerEl, message, popperOptions, popperStyles, animationOptions }) {
57
+ constructor({ triggerEl, message, tooltipOptions, tooltipStyles, animationOptions }) {
63
58
  this.triggerEl = triggerEl;
64
59
  this.message = message;
65
60
 
66
- this.popperOptions = Object.assign({}, Tooltip.defaultOptions, popperOptions);
67
- this.popperStyles = Object.assign({}, Tooltip.popperStyles, popperStyles);
61
+ this.tooltipOptions = Object.assign({}, Tooltip.defaultOptions, tooltipOptions);
62
+ this.tooltipStyles = Object.assign({}, Tooltip.tooltipStyles, tooltipStyles);
68
63
  this.animationOptions = Object.assign({}, Tooltip.animationOptions, animationOptions);
69
64
 
70
65
  this._isMacOS = window.navigator.userAgent.toLowerCase().includes('mac os');
71
66
 
72
- this._initialize();
67
+ this.#initialize();
73
68
  }
74
69
 
75
- _initialize() {
76
- let tooltip = this._createTooltip();
70
+ #initialize() {
71
+ let tooltip = this.#createTooltip();
77
72
 
78
73
  this.elements = {
79
74
  tooltip,
@@ -83,13 +78,15 @@ export class Tooltip {
83
78
  };
84
79
  }
85
80
 
86
- _createTooltip() {
81
+ #createTooltip() {
87
82
  let element = document.createElement('div');
88
83
 
89
- Object.keys(this.popperStyles).forEach((style) => (element.style[style] = this.popperStyles[style]));
84
+ Object.keys(this.tooltipStyles).forEach((style) => (element.style[style] = this.tooltipStyles[style]));
85
+
86
+ element.classList.add('zpa-tooltip__wrapper');
90
87
 
91
88
  element.innerHTML = `
92
- <div class="zpa-tooltip zpa-tooltip--${this.popperOptions.size}" data-container>
89
+ <div class="zpa-tooltip zpa-tooltip--${this.tooltipOptions.size}" data-container>
93
90
  <div class="zpa-tooltip__content" data-content></div>
94
91
  <div class="zpa-tooltip__arrow" data-arrow></div>
95
92
  </div>`;
@@ -97,20 +94,20 @@ export class Tooltip {
97
94
  return element;
98
95
  }
99
96
 
100
- _renderContent(message) {
97
+ #renderContent(message) {
101
98
  // Do not use .innerHTML, it will break tooltips in Scripts section!
102
99
  this.elements.contentEl.textContent = message;
103
100
 
104
101
  this.elements.hotkey?.remove();
105
- this._renderHotkeyTip();
102
+ this.#renderHotkeyTip();
106
103
  }
107
104
 
108
- _renderHotkeyTip() {
105
+ #renderHotkeyTip() {
109
106
  const raw = this.triggerEl.dataset.tooltipHotkey;
110
107
 
111
108
  if (!raw) return;
112
109
 
113
- const parts = raw.split(' ').map(this._formatHotkeyPart.bind(this));
110
+ const parts = raw.split(' ').map(this.#formatHotkeyPart.bind(this));
114
111
  const tipEl = document.createElement('span');
115
112
 
116
113
  tipEl.classList.add('zpa-tooltip__hotkey');
@@ -119,7 +116,7 @@ export class Tooltip {
119
116
  this.elements.contentEl.append(tipEl);
120
117
  }
121
118
 
122
- _formatHotkeyPart(part) {
119
+ #formatHotkeyPart(part) {
123
120
  switch (part.toLowerCase()) {
124
121
  case 'mod':
125
122
  return this._isMacOS ? '⌘' : 'Ctrl';
@@ -131,111 +128,89 @@ export class Tooltip {
131
128
  }
132
129
  }
133
130
 
134
- _getModifiers() {
131
+ #getModifiers() {
135
132
  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
- },
169
- TooltipCloseOnScrollModifier.init({ tooltip: this })
133
+ flip(),
134
+ offset({ mainAxis: 8 }),
135
+ shift({ padding: 4 }),
136
+ arrow({ element: this.elements.arrow })
170
137
  ];
171
138
  }
172
139
 
173
- _setTooltipDuration(duration = 0) {
140
+ #setTooltipDuration(duration = 0) {
174
141
  this.elements.container.style.transitionDuration = `${duration}ms`;
175
142
  }
176
143
 
177
- _renderPopper() {
178
- const modifiers = this._getModifiers();
144
+ #renderPopper() {
145
+ const modifiers = this.#getModifiers();
179
146
 
180
- const createPopper = popperGenerator({
181
- defaultModifiers: [...defaultModifiers, arrow, flip, offset, preventOverflow]
182
- });
147
+ this.floatingInstance = autoUpdate(this.triggerEl, this.elements.tooltip, async () => {
148
+ const positioning = await computePosition(this.triggerEl, this.elements.tooltip, {
149
+ placement: this.tooltipOptions.placement,
150
+ middleware: [...modifiers]
151
+ });
152
+
153
+ const { x, y, middlewareData, placement } = positioning;
154
+
155
+ if (middlewareData.arrow) this.#renderArrow(placement, middlewareData.arrow);
183
156
 
184
- this.popperInstance = createPopper(this.triggerEl, this.elements.tooltip, {
185
- placement: this.popperOptions.placement,
186
- modifiers
157
+ Object.assign(this.elements.tooltip.style, {
158
+ left: `${x}px`,
159
+ top: `${y}px`
160
+ });
187
161
  });
188
162
 
189
- return this.popperInstance;
163
+ return this.floatingInstance;
190
164
  }
191
165
 
192
- _toggle(makeVisible) {
166
+ #renderArrow(placement, { x, y }) {
167
+ this.elements.tooltip.classList.add(`zpa-tooltip--${placement}`);
168
+
169
+ Object.assign(this.elements.arrow.style, {
170
+ left: x != null ? `${x}px` : '',
171
+ top: y != null ? `${y}px` : ''
172
+ });
173
+ }
174
+
175
+ #toggle(makeVisible) {
193
176
  const { enterDuration, leaveDuration } = this.animationOptions;
194
177
 
195
- this._setTooltipDuration(makeVisible ? enterDuration : leaveDuration);
178
+ this.#setTooltipDuration(makeVisible ? enterDuration : leaveDuration);
196
179
  this.elements.container.classList.toggle('zpa-tooltip--open', makeVisible);
197
180
  }
198
181
 
199
182
  open() {
200
- if (this.popperInstance) {
201
- return;
202
- }
183
+ if (this.floatingInstance) return;
203
184
 
204
185
  document.body.appendChild(this.elements.tooltip);
205
- this.popperInstance = this._renderPopper();
186
+ this.popperInstance = this.#renderPopper();
206
187
 
207
- this._renderContent(this.message);
208
- this._toggle(true);
188
+ this.#renderContent(this.message);
189
+ this.#toggle(true);
209
190
  }
210
191
 
211
192
  close() {
212
- if (!this.popperInstance) {
213
- return;
214
- }
193
+ if (!this.floatingInstance) return;
215
194
 
216
- this._toggle(false);
195
+ this.#toggle(false);
217
196
 
218
197
  return setTimeout(() => {
219
198
  this.destroy();
220
199
  }, this.animationOptions.leaveDuration);
221
200
  }
222
201
 
223
- update() {
224
- this.popperInstance?.update();
225
- }
226
-
227
202
  updateContent(content) {
228
203
  this.message = content;
229
- this._renderContent(this.message);
230
- this.update();
204
+ this.#renderContent(this.message);
231
205
  }
232
206
 
233
207
  destroy() {
234
208
  this.elements.tooltip.remove();
235
209
 
236
210
  if (this.popperInstance) {
237
- this.popperInstance.destroy();
238
- this.popperInstance = null;
211
+ // destroy instance
212
+ this.floatingInstance();
213
+ this.floatingInstance = null;
239
214
  }
240
215
  }
241
216
  }
@@ -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,38 +1,32 @@
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
+ const { x, y } = positioning;
25
+
26
+ Object.assign(modalEl.value, {
27
+ left: `${x}px`,
28
+ top: `${y}px`
29
+ });
36
30
  });
37
31
  }
38
32
 
@@ -43,19 +37,16 @@ export function useModalToggler({ onBeforeOpened, onClosed, wrapperRef, modalRef
43
37
 
44
38
  await nextTick();
45
39
 
46
- initPopper();
40
+ initModal();
47
41
  }
48
42
 
49
43
  function close() {
50
44
  isOpened.value = false;
45
+ floatingInstance?.();
51
46
  editor.commands.restoreSelection();
52
47
  onClosed?.();
53
48
  }
54
49
 
55
- onUnmounted(() => {
56
- popper?.destroy();
57
- });
58
-
59
50
  const toggle = (toOpen) => toOpen ? open() : close();
60
51
 
61
52
  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.0",
3
+ "version": "3.1.0-0",
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
  "@rollup/plugin-json": "^4.1.0",
36
36
  "@tiptap/core": "2.0.0-beta.195",
37
37
  "@tiptap/extension-document": "2.0.0-beta.195",
@@ -96,5 +96,9 @@
96
96
  "engines": {
97
97
  "node": ">=18.12.0"
98
98
  },
99
- "engineStrict": true
99
+ "engineStrict": true,
100
+ "os": [
101
+ "darwin",
102
+ "linux"
103
+ ]
100
104
  }