@zipify/wysiwyg 4.0.7 → 4.1.0-1

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 (32) hide show
  1. package/config/build/settings.js +1 -1
  2. package/config/jest/setupTests.js +3 -0
  3. package/dist/cli.js +2 -3
  4. package/dist/wysiwyg.css +38 -38
  5. package/dist/wysiwyg.mjs +1807 -798
  6. package/example/ai-component/AiComponent.vue +16 -50
  7. package/example/tooltip/Tooltip.js +69 -92
  8. package/example/tooltip/modifiers/TooltipCloseOnScrollModifier.js +5 -2
  9. package/example/tooltip/tooltip.css +31 -8
  10. package/lib/Wysiwyg.vue +10 -20
  11. package/lib/components/base/Modal.vue +19 -18
  12. package/lib/components/base/ModalFloating.vue +30 -0
  13. package/lib/components/base/__tests__/Modal.test.js +2 -19
  14. package/lib/components/base/composables/useModalToggler.js +5 -42
  15. package/lib/components/base/dropdown/Dropdown.vue +7 -6
  16. package/lib/components/base/dropdown/DropdownActivator.vue +4 -5
  17. package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +1 -1
  18. package/lib/components/base/dropdown/__tests__/DropdownOption.test.js +1 -1
  19. package/lib/components/toolbar/Toolbar.vue +30 -14
  20. package/lib/components/toolbar/ToolbarFloating.vue +41 -0
  21. package/lib/components/toolbar/__tests__/Toolbar.test.js +4 -6
  22. package/lib/components/toolbar/controls/LineHeightControl.vue +9 -7
  23. package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +2 -2
  24. package/lib/components/toolbar/controls/link/LinkControl.vue +13 -14
  25. package/lib/composables/index.js +0 -1
  26. package/lib/composables/useEditor.js +6 -1
  27. package/lib/styles/content.css +0 -1
  28. package/package.json +49 -49
  29. package/lib/components/base/composables/__tests__/useModalToggler.test.js +0 -57
  30. package/lib/composables/useToolbar.js +0 -40
  31. /package/config/build/{example.config.js → example.config.mjs} +0 -0
  32. /package/config/build/{lib.config.js → lib.config.mjs} +0 -0
@@ -1,11 +1,16 @@
1
1
  <template>
2
2
  <div ref="wrapperRef">
3
- <Button icon skin="toolbar" :active="isActive" @click="toggler.open">
3
+ <Button icon skin="toolbar" :active="toggler.isOpened" @click="toggler.open">
4
4
  <Icon name="sparkles" size="28px" />
5
5
  <span class="zw-ai-control__caption">AI</span>
6
6
  </Button>
7
7
 
8
- <Modal ref="modalRef" class="zw-ai-component__modal" :toggler="toggler" focus-first-control>
8
+ <Modal
9
+ class="zw-ai-component__modal"
10
+ :toggler="toggler"
11
+ :reference-ref="wrapperRef"
12
+ focus-first-control
13
+ >
9
14
  <p>Modal content</p>
10
15
 
11
16
  <Button skin="primary" @click="onInsert">
@@ -15,57 +20,18 @@
15
20
  </div>
16
21
  </template>
17
22
 
18
- <script>
19
- import { inject, ref, unref, computed } from 'vue';
23
+ <script setup>
24
+ import { inject, ref } from 'vue';
20
25
  import { Button, Icon, useModalToggler, Modal } from '../../lib/components/base';
21
26
  import { InjectionTokens } from '../../lib/injectionTokens';
22
27
 
23
- export default {
24
- name: 'AiComponent',
28
+ const editor = inject(InjectionTokens.EDITOR);
29
+ const wrapperRef = ref(null);
25
30
 
26
- components: {
27
- Modal,
28
- Button,
29
- Icon
30
- },
31
+ const toggler = useModalToggler();
31
32
 
32
- setup() {
33
- const editor = inject(InjectionTokens.EDITOR);
34
- const wrapperRef = ref(null);
35
- const modalRef = ref(null);
36
-
37
- const onBeforeOpened = () => {};
38
-
39
- const toggler = useModalToggler({
40
- options: {
41
- placement: 'bottom-start',
42
- strategy: 'absolute',
43
- offset: [-8, 5]
44
- },
45
- onBeforeOpened: () => onBeforeOpened(),
46
- wrapperRef,
47
- modalRef
48
- });
49
-
50
- const onInsert = () => {
51
- editor.chain()
52
- .focus()
53
- .insertContent('Hello from AI component')
54
- .setDocMeta('ai_generated', true)
55
- .run();
56
- toggler.close();
57
- };
58
-
59
- const isActive = computed(() => unref(toggler.isOpened));
60
-
61
- return {
62
- editor,
63
- wrapperRef,
64
- modalRef,
65
- toggler,
66
- onInsert,
67
- isActive
68
- };
69
- }
70
- };
33
+ function onInsert() {
34
+ editor.chain().focus().insertContent('Hello from AI component').run();
35
+ toggler.close();
36
+ }
71
37
  </script>
@@ -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
@@ -1,11 +1,13 @@
1
1
  <template>
2
2
  <div class="zw-wysiwyg" ref="wysiwygRef">
3
3
  <Toolbar
4
- :toolbar="toolbar"
5
4
  :device="device"
5
+ :visible="isToolbarVisible"
6
6
  :popup-mode="popupMode"
7
+ :reference-ref="wysiwygRef"
8
+ :placement="toolbarPlacement"
9
+ :offsets="toolbarOffsets"
7
10
  :ai-component="aiComponent"
8
- ref="toolbarRef"
9
11
  />
10
12
 
11
13
  <EditorContent :editor="editor" />
@@ -16,7 +18,7 @@
16
18
  import { EditorContent } from '@tiptap/vue-3';
17
19
  import { provide, toRef, ref, computed } from 'vue';
18
20
  import { Toolbar } from './components';
19
- import { useToolbar, useEditor } from './composables';
21
+ import { useEditor } from './composables';
20
22
  import { buildExtensions } from './extensions';
21
23
  import { InjectionTokens } from './injectionTokens';
22
24
  import { ContextWindow, FavoriteColors, Storage } from './services';
@@ -137,29 +139,17 @@ const MAX_FONT_SIZE = 112;
137
139
  ContextWindow.use(props.window);
138
140
 
139
141
  const fonts = props.fonts.map((font) => new Font(font));
140
- const toolbarRef = ref(null);
141
142
  const wysiwygRef = ref(null);
142
- const wrapperRef = computed(() => wysiwygRef.value?.$el || document.body);
143
- const isToolbarActiveRef = computed(() => props.active && !props.readonly);
144
-
145
- const toolbar = useToolbar({
146
- wrapperRef: wysiwygRef,
147
- placementRef: toRef(props, 'toolbarPlacement'),
148
- isActiveRef: isToolbarActiveRef,
149
- offsets: props.toolbarOffsets
150
- });
151
- const updateToolbar = () => toolbar.update();
143
+ const wrapperRef = computed(() => wysiwygRef.value || document.body);
144
+ const isToolbarVisible = computed(() => props.active && !props.readonly);
152
145
 
153
- function onChange(content) {
154
- emit('update:model-value', content);
155
- updateToolbar();
156
- }
146
+ const onChange = (content) => emit('update:model-value', content);
157
147
 
158
148
  const pageBlocks = toRef(props, 'pageBlocks');
159
149
 
160
150
  const { editor, getContent } = useEditor({
161
151
  content: toRef(props, 'modelValue'),
162
- onChange: (content) => onChange(content),
152
+ onChange,
163
153
  isReadonlyRef: toRef(props, 'readonly'),
164
154
 
165
155
  extensions: buildExtensions({
@@ -196,7 +186,7 @@ provide(InjectionTokens.LOCAL_STORAGE, new Storage(localStorage));
196
186
  provide(InjectionTokens.FAVORITE_COLORS, favoriteColors);
197
187
  provide(InjectionTokens.PAGE_BLOCKS, pageBlocks);
198
188
 
199
- defineExpose({ getContentCustomization, getContent, updateToolbar, editor });
189
+ defineExpose({ getContentCustomization, getContent, editor });
200
190
  </script>
201
191
 
202
192
  <style src="./styles/main.css" />
@@ -1,25 +1,32 @@
1
1
  <template>
2
- <transition name="zw-modal-" :duration="transition">
3
- <div
2
+ <Transition name="zw-modal-" :duration="transitionDuration">
3
+ <ModalFloating
4
4
  class="zw-modal"
5
5
  ref="hostRef"
6
6
  tabindex="-1"
7
- :style="modalStyles"
8
- v-if="isOpened"
7
+ :reference-ref="referenceRef"
9
8
  v-out-click="toggler.close"
9
+ v-if="toggler.isOpened && referenceRef"
10
10
  data-test-selector="modal"
11
11
  >
12
12
  <slot />
13
- </div>
14
- </transition>
13
+ </ModalFloating>
14
+ </Transition>
15
15
  </template>
16
16
 
17
17
  <script setup>
18
- import { computed, nextTick, ref, unref, watch } from 'vue';
18
+ import { nextTick, ref, toRef, watch } from 'vue';
19
19
  import { outClick as vOutClick } from '../../directives';
20
20
  import { useDeselectionLock, useElementRef, useModalToggler } from './composables';
21
+ import ModalFloating from './ModalFloating';
21
22
 
22
23
  const props = defineProps({
24
+ referenceRef: {
25
+ type: Object,
26
+ required: false,
27
+ default: null
28
+ },
29
+
23
30
  toggler: {
24
31
  type: Object,
25
32
  required: false,
@@ -45,28 +52,22 @@ const props = defineProps({
45
52
  }
46
53
  });
47
54
 
48
- const transition = {
55
+ const transitionDuration = {
49
56
  enter: 200,
50
57
  leave: 100
51
58
  };
52
59
 
53
60
  const toggler = props.toggler || useModalToggler();
54
- const isOpened = computed(() => unref(toggler.isOpened));
55
61
  const hostRef = ref(null);
56
62
  const hostEl = useElementRef(hostRef);
57
63
 
58
- const modalStyles = computed(() => ({
59
- '--zw-modal-max-height': `${props.maxHeight}px`,
60
- '--zw-modal-max-width': `${props.maxWidth}px`
61
- }));
62
-
63
64
  useDeselectionLock({
64
- isActiveRef: toggler.isOpened,
65
+ isActiveRef: toRef(toggler, 'isOpened'),
65
66
  hostRef
66
67
  });
67
68
 
68
69
  if (props.focusFirstControl) {
69
- watch(toggler.isOpened, async (_, wasOpened) => {
70
+ watch(toRef(toggler, 'isOpened'), async (_, wasOpened) => {
70
71
  if (wasOpened) return;
71
72
 
72
73
  await nextTick();
@@ -82,8 +83,8 @@ if (props.focusFirstControl) {
82
83
  border-radius: 2px;
83
84
  box-shadow: 0 0 4px rgba(var(--zw-color-black), 0.3);
84
85
  background-color: rgb(var(--zw-color-n15));
85
- max-height: var(--zw-modal-max-height);
86
- max-width: var(--zw-modal-max-width);
86
+ max-height: v-bind("maxHeight + 'px'");
87
+ max-width: v-bind("maxWidth + 'px'");
87
88
  z-index: 1000;
88
89
  position: fixed;
89
90
  will-change: transform;
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <div ref="floatingRef" :style="floatingStyles">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { ref, toRef } from 'vue';
9
+ import { useFloating, limitShift, offset, shift } from '@floating-ui/vue';
10
+
11
+ const props = defineProps({
12
+ referenceRef: {
13
+ type: Object,
14
+ required: true
15
+ }
16
+ });
17
+
18
+ const floatingRef = ref(null);
19
+ const referenceRef = toRef(props, 'referenceRef');
20
+
21
+ const { floatingStyles } = useFloating(referenceRef, floatingRef, {
22
+ placement: 'bottom',
23
+ strategy: 'fixed',
24
+
25
+ middleware: [
26
+ shift({ padding: 16, crossAxis: true, limiter: limitShift() }),
27
+ offset({ mainAxis: 4 })
28
+ ]
29
+ });
30
+ </script>