@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.
- package/config/build/settings.js +1 -1
- package/config/jest/setupTests.js +3 -0
- package/dist/cli.js +2 -3
- package/dist/wysiwyg.css +38 -38
- package/dist/wysiwyg.mjs +1807 -798
- package/example/ai-component/AiComponent.vue +16 -50
- package/example/tooltip/Tooltip.js +69 -92
- package/example/tooltip/modifiers/TooltipCloseOnScrollModifier.js +5 -2
- package/example/tooltip/tooltip.css +31 -8
- package/lib/Wysiwyg.vue +10 -20
- package/lib/components/base/Modal.vue +19 -18
- package/lib/components/base/ModalFloating.vue +30 -0
- package/lib/components/base/__tests__/Modal.test.js +2 -19
- package/lib/components/base/composables/useModalToggler.js +5 -42
- package/lib/components/base/dropdown/Dropdown.vue +7 -6
- package/lib/components/base/dropdown/DropdownActivator.vue +4 -5
- package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +1 -1
- package/lib/components/base/dropdown/__tests__/DropdownOption.test.js +1 -1
- package/lib/components/toolbar/Toolbar.vue +30 -14
- package/lib/components/toolbar/ToolbarFloating.vue +41 -0
- package/lib/components/toolbar/__tests__/Toolbar.test.js +4 -6
- package/lib/components/toolbar/controls/LineHeightControl.vue +9 -7
- package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +2 -2
- package/lib/components/toolbar/controls/link/LinkControl.vue +13 -14
- package/lib/composables/index.js +0 -1
- package/lib/composables/useEditor.js +6 -1
- package/lib/styles/content.css +0 -1
- package/package.json +49 -49
- package/lib/components/base/composables/__tests__/useModalToggler.test.js +0 -57
- package/lib/composables/useToolbar.js +0 -40
- /package/config/build/{example.config.js → example.config.mjs} +0 -0
- /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="
|
|
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
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
28
|
+
const editor = inject(InjectionTokens.EDITOR);
|
|
29
|
+
const wrapperRef = ref(null);
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
Modal,
|
|
28
|
-
Button,
|
|
29
|
-
Icon
|
|
30
|
-
},
|
|
31
|
+
const toggler = useModalToggler();
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 {
|
|
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:
|
|
17
|
+
leaveDuration: 100
|
|
22
18
|
};
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
static get
|
|
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
|
|
39
|
-
|
|
34
|
+
/** @type {object} options for FloatingUI instance */
|
|
35
|
+
tooltipOptions = {};
|
|
40
36
|
|
|
41
37
|
/** @type {object} styles for tooltip's wrapper element */
|
|
42
|
-
|
|
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 {
|
|
48
|
-
|
|
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.
|
|
59
|
-
* @param [params.
|
|
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,
|
|
58
|
+
constructor({ triggerEl, message, tooltipOptions, tooltipStyles, animationOptions }) {
|
|
63
59
|
this.triggerEl = triggerEl;
|
|
64
60
|
this.message = message;
|
|
65
61
|
|
|
66
|
-
this.
|
|
67
|
-
this.
|
|
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
|
|
68
|
+
this.#initialize();
|
|
73
69
|
}
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
let tooltip = this
|
|
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
|
-
|
|
82
|
+
#createTooltip() {
|
|
87
83
|
let element = document.createElement('div');
|
|
88
84
|
|
|
89
|
-
Object.keys(this.
|
|
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.
|
|
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
|
-
|
|
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
|
|
103
|
+
this.#renderHotkeyTip();
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
132
|
+
#getModifiers() {
|
|
135
133
|
return [
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
142
|
+
#setTooltipDuration(duration = 0) {
|
|
174
143
|
this.elements.container.style.transitionDuration = `${duration}ms`;
|
|
175
144
|
}
|
|
176
145
|
|
|
177
|
-
|
|
178
|
-
const modifiers = this
|
|
146
|
+
#renderPopper() {
|
|
147
|
+
const modifiers = this.#getModifiers();
|
|
179
148
|
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
159
|
+
Object.assign(this.elements.tooltip.style, {
|
|
160
|
+
left: `${x}px`,
|
|
161
|
+
top: `${y}px`
|
|
162
|
+
});
|
|
187
163
|
});
|
|
188
164
|
|
|
189
|
-
return this.
|
|
165
|
+
return this.floatingInstance;
|
|
190
166
|
}
|
|
191
167
|
|
|
192
|
-
|
|
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
|
|
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.
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
185
|
+
if (this.floatingInstance) return;
|
|
203
186
|
|
|
204
187
|
document.body.appendChild(this.elements.tooltip);
|
|
205
|
-
this.popperInstance = this
|
|
188
|
+
this.popperInstance = this.#renderPopper();
|
|
206
189
|
|
|
207
|
-
this
|
|
208
|
-
this
|
|
190
|
+
this.#renderContent(this.message);
|
|
191
|
+
this.#toggle(true);
|
|
209
192
|
}
|
|
210
193
|
|
|
211
194
|
close() {
|
|
212
|
-
if (!this.
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
195
|
+
if (!this.floatingInstance) return;
|
|
215
196
|
|
|
216
|
-
this
|
|
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
|
|
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
|
-
|
|
238
|
-
this.
|
|
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>>} -
|
|
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: () =>
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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 {
|
|
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
|
|
143
|
-
const
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
-
<
|
|
3
|
-
<
|
|
2
|
+
<Transition name="zw-modal-" :duration="transitionDuration">
|
|
3
|
+
<ModalFloating
|
|
4
4
|
class="zw-modal"
|
|
5
5
|
ref="hostRef"
|
|
6
6
|
tabindex="-1"
|
|
7
|
-
:
|
|
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
|
-
</
|
|
14
|
-
</
|
|
13
|
+
</ModalFloating>
|
|
14
|
+
</Transition>
|
|
15
15
|
</template>
|
|
16
16
|
|
|
17
17
|
<script setup>
|
|
18
|
-
import {
|
|
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
|
|
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
|
|
65
|
+
isActiveRef: toRef(toggler, 'isOpened'),
|
|
65
66
|
hostRef
|
|
66
67
|
});
|
|
67
68
|
|
|
68
69
|
if (props.focusFirstControl) {
|
|
69
|
-
watch(toggler
|
|
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:
|
|
86
|
-
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>
|