goodteditor-ui 1.0.55 → 1.0.57
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/package.json +13 -14
- package/src/components/ui/InputTags.vue +2 -12
- package/src/components/ui/WysiwygEditor/WysiwygEditor.md +1 -1
- package/src/components/ui/WysiwygEditor/WysiwygEditor.vue +47 -71
- package/src/components/ui/WysiwygEditor/extensions/bubble-menu.js +3 -3
- package/src/components/ui/WysiwygEditor/extensions/text-align.js +69 -3
- package/src/components/ui/WysiwygEditor/renders/Link.vue +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goodteditor-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.57",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"homepage": "https://goodt-ui.netlify.app/",
|
|
6
6
|
"scripts": {
|
|
@@ -13,19 +13,18 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@popperjs/core": "2.11.2",
|
|
15
15
|
"@tiptap/extension-bubble-menu": "^2.2.4",
|
|
16
|
-
"@tiptap/extension-color": "2.0.
|
|
17
|
-
"@tiptap/extension-font-family": "2.0.
|
|
18
|
-
"@tiptap/extension-image": "2.0.
|
|
19
|
-
"@tiptap/extension-link": "2.0.
|
|
20
|
-
"@tiptap/extension-table": "2.0.
|
|
21
|
-
"@tiptap/extension-table-cell": "2.0.
|
|
22
|
-
"@tiptap/extension-table-header": "2.0.
|
|
23
|
-
"@tiptap/extension-table-row": "2.0.
|
|
24
|
-
"@tiptap/extension-text-
|
|
25
|
-
"@tiptap/extension-
|
|
26
|
-
"@tiptap/
|
|
27
|
-
"@tiptap/
|
|
28
|
-
"@tiptap/vue-2": "2.0.x"
|
|
16
|
+
"@tiptap/extension-color": "^2.0.0",
|
|
17
|
+
"@tiptap/extension-font-family": "^2.0.0",
|
|
18
|
+
"@tiptap/extension-image": "^2.0.0",
|
|
19
|
+
"@tiptap/extension-link": "^2.0.0",
|
|
20
|
+
"@tiptap/extension-table": "^2.0.0",
|
|
21
|
+
"@tiptap/extension-table-cell": "^2.0.0",
|
|
22
|
+
"@tiptap/extension-table-header": "^2.0.0",
|
|
23
|
+
"@tiptap/extension-table-row": "^2.0.0",
|
|
24
|
+
"@tiptap/extension-text-style": "^2.0.0",
|
|
25
|
+
"@tiptap/extension-underline": "^2.0.0",
|
|
26
|
+
"@tiptap/starter-kit": "^2.0.0",
|
|
27
|
+
"@tiptap/vue-2": "^2.0.0"
|
|
29
28
|
},
|
|
30
29
|
"devDependencies": {
|
|
31
30
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
@@ -234,23 +234,12 @@ export default {
|
|
|
234
234
|
}
|
|
235
235
|
this.tagSelected = tagSelected;
|
|
236
236
|
},
|
|
237
|
-
/**
|
|
238
|
-
*
|
|
239
|
-
* @param {KeyboardEvent} e
|
|
240
|
-
* @return {boolean}
|
|
241
|
-
*/
|
|
242
|
-
canSelectTag(e) {
|
|
243
|
-
return e.target.value === '' && ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].indexOf(e.code)
|
|
244
|
-
},
|
|
245
237
|
/**
|
|
246
238
|
*
|
|
247
239
|
* @param {KeyboardEvent} e
|
|
248
240
|
*/
|
|
249
241
|
onInputKeydown(e) {
|
|
250
242
|
switch (e.key) {
|
|
251
|
-
case ',':
|
|
252
|
-
case ';':
|
|
253
|
-
case 'Tab':
|
|
254
243
|
case 'Enter': {
|
|
255
244
|
this.onInputEnter(e);
|
|
256
245
|
return;
|
|
@@ -261,7 +250,8 @@ export default {
|
|
|
261
250
|
}
|
|
262
251
|
}
|
|
263
252
|
|
|
264
|
-
if
|
|
253
|
+
// if some text in input exist
|
|
254
|
+
if (e.target.value !== '') {
|
|
265
255
|
return;
|
|
266
256
|
}
|
|
267
257
|
|
|
@@ -1,81 +1,66 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<div
|
|
16
|
-
v-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
<div class="d-flex flex-col">
|
|
3
|
+
<div ref="menu" :class="{ bubble: bubbleMenu, 'mar-bot-l1': !bubbleMenu }" class="editor-toolbar">
|
|
4
|
+
<!--
|
|
5
|
+
@slot Toolbar slot
|
|
6
|
+
@binding {title: string, group: Tool[]} toolGroups array of using tools
|
|
7
|
+
@binding {Function} buildToolBinds build tool binds function(tool:Tool)
|
|
8
|
+
-->
|
|
9
|
+
<slot v-if="editor != null" name="toolbar" v-bind="{ toolGroups, buildToolBinds }">
|
|
10
|
+
<div class="row row-gap-l1">
|
|
11
|
+
<div
|
|
12
|
+
v-for="({ title, group }, groupIndex) of toolGroups"
|
|
13
|
+
:key="groupIndex"
|
|
14
|
+
class="col col-vbot col-auto">
|
|
15
|
+
<div class="d-flex flex-col flex-v-center">
|
|
16
|
+
<div v-if="resolveGroupTitle(title, groupIndex) !== ''" class="group-header">
|
|
17
|
+
<!--
|
|
18
|
+
@slot Group header slot
|
|
19
|
+
@binding {number} groupIndex index of the tools group
|
|
20
|
+
@binding {string} title tools group title
|
|
21
|
+
-->
|
|
22
|
+
<slot name="group-header" v-bind="{ groupIndex, title }">
|
|
23
|
+
<div class="text-small text-center">{{ resolveGroupTitle(title, groupIndex) }}</div>
|
|
24
|
+
</slot>
|
|
25
|
+
</div>
|
|
26
|
+
<div>
|
|
27
|
+
<div v-for="tool of group" :key="tool.name" class="tool d-inline-block">
|
|
21
28
|
<!--
|
|
22
|
-
@slot
|
|
23
|
-
@binding {
|
|
24
|
-
@binding {string} title tools group title
|
|
29
|
+
@slot Tool slot
|
|
30
|
+
@binding {Tool} tool tool's already bound to editor context
|
|
25
31
|
-->
|
|
26
|
-
<slot name="
|
|
27
|
-
<
|
|
32
|
+
<slot name="tool" v-bind="{ tool: buildToolBinds(tool) }">
|
|
33
|
+
<component :is="tool.render" :tool="buildToolBinds(tool)" />
|
|
28
34
|
</slot>
|
|
29
35
|
</div>
|
|
30
|
-
<div>
|
|
31
|
-
<div
|
|
32
|
-
v-for="tool of group"
|
|
33
|
-
:key="tool.name"
|
|
34
|
-
class="tool d-inline-block">
|
|
35
|
-
<!--
|
|
36
|
-
@slot Tool slot
|
|
37
|
-
@binding {Tool} tool tool's already bound to editor context
|
|
38
|
-
-->
|
|
39
|
-
<slot name="tool" v-bind="{ tool: buildToolBinds(tool) }">
|
|
40
|
-
<component :is="tool.render" :tool="buildToolBinds(tool)" />
|
|
41
|
-
</slot>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
36
|
</div>
|
|
45
37
|
</div>
|
|
46
38
|
</div>
|
|
47
|
-
</slot>
|
|
48
|
-
</div>
|
|
49
|
-
</template>
|
|
50
|
-
<template #editor-content="{ style }">
|
|
51
|
-
<div :style="style" class="pad-v-l1" >
|
|
52
|
-
<div class="editor-content h-100">
|
|
53
|
-
<editor-content :editor="editor" class="h-100"/>
|
|
54
39
|
</div>
|
|
55
|
-
</
|
|
56
|
-
</
|
|
57
|
-
|
|
40
|
+
</slot>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="editor-content flex-grow d-flex">
|
|
43
|
+
<editor-content :editor="editor" class="w-100"/>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
58
46
|
</template>
|
|
59
47
|
|
|
60
48
|
<script>
|
|
61
49
|
import { Editor, EditorContent } from '@tiptap/vue-2';
|
|
62
50
|
|
|
63
51
|
import { debounce } from '../utils/Helpers';
|
|
64
|
-
import Grid from '../Grid.vue';
|
|
65
52
|
import { resolveExtensions } from './extensions';
|
|
66
53
|
import { buildToolGroups, bindContext } from './utils';
|
|
67
54
|
import { DefaultTools, WysiwygAutofocus } from './constants';
|
|
68
55
|
|
|
69
56
|
/**
|
|
70
57
|
* @typedef {Omit<import('@tiptap/extension-bubble-menu').BubbleMenuOptions, 'element'>} BubbleMenuOptions
|
|
71
|
-
* @
|
|
72
|
-
* @typedef {import('vue').PropOptions.<BubbleMenuOptions>} BubbleMenuOptionsProp
|
|
58
|
+
* @typedef {import('vue').PropOptions.<Boolean|BubbleMenuOptions>} BubbleMenuOptionsProp
|
|
73
59
|
* @typedef {import('./constants').ITool} ITool
|
|
74
60
|
*/
|
|
75
61
|
|
|
76
62
|
export default {
|
|
77
63
|
components: {
|
|
78
|
-
Grid,
|
|
79
64
|
EditorContent
|
|
80
65
|
},
|
|
81
66
|
props: {
|
|
@@ -108,10 +93,8 @@ export default {
|
|
|
108
93
|
* @see See [Bubble menu settings](https://tiptap.dev/docs/editor/api/extensions/bubble-menu#settings)
|
|
109
94
|
*/
|
|
110
95
|
bubbleMenu: {
|
|
111
|
-
type: Object,
|
|
112
|
-
default:
|
|
113
|
-
isEnabled: false
|
|
114
|
-
})
|
|
96
|
+
type: [Boolean, Object],
|
|
97
|
+
default: false
|
|
115
98
|
},
|
|
116
99
|
/**
|
|
117
100
|
* whether the outline of the focused content editor should be visible
|
|
@@ -147,7 +130,7 @@ export default {
|
|
|
147
130
|
return buildToolGroups(this.tools);
|
|
148
131
|
},
|
|
149
132
|
editorClass() {
|
|
150
|
-
const classes = ['pad-3', 'scroll-y'];
|
|
133
|
+
const classes = ['h-100', 'pad-3', 'scroll-y'];
|
|
151
134
|
|
|
152
135
|
if (this.focusVisible === false) {
|
|
153
136
|
classes.push('focus-outline-none');
|
|
@@ -156,13 +139,6 @@ export default {
|
|
|
156
139
|
return classes.join(' ');
|
|
157
140
|
}
|
|
158
141
|
},
|
|
159
|
-
static: {
|
|
160
|
-
GridProps: {
|
|
161
|
-
areas: [['editor-toolbar'], ['editor-content']],
|
|
162
|
-
rows: ['auto', '1fr'],
|
|
163
|
-
gap: ['0.5rem', 0]
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
142
|
watch: {
|
|
167
143
|
/**
|
|
168
144
|
* @param {string} value
|
|
@@ -182,21 +158,21 @@ export default {
|
|
|
182
158
|
},
|
|
183
159
|
mounted() {
|
|
184
160
|
const { $refs, value, bubbleMenu, autofocus, editorClass, onSelectionUpdateDebounced } = this;
|
|
185
|
-
const { isEnabled: isBubbleMenuEnabled, ...bubbleMenuOptions } = bubbleMenu;
|
|
186
161
|
|
|
187
162
|
this.editor = new Editor({
|
|
188
163
|
content: value,
|
|
189
164
|
extensions: resolveExtensions({
|
|
190
|
-
bubbleMenu
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
165
|
+
...(bubbleMenu && {
|
|
166
|
+
bubbleMenu: {
|
|
167
|
+
element: $refs.menu,
|
|
168
|
+
...bubbleMenu
|
|
169
|
+
}
|
|
170
|
+
})
|
|
194
171
|
}),
|
|
195
172
|
autofocus,
|
|
196
173
|
editorProps: {
|
|
197
174
|
attributes: {
|
|
198
|
-
class: editorClass
|
|
199
|
-
style: 'min-height: 100%; height: 0;'
|
|
175
|
+
class: editorClass
|
|
200
176
|
}
|
|
201
177
|
},
|
|
202
178
|
onUpdate: () => {
|
|
@@ -10,9 +10,9 @@ import BubbleMenuToExtend from '@tiptap/extension-bubble-menu';
|
|
|
10
10
|
* @return {Extension<BubbleMenuOptions, any>}
|
|
11
11
|
*/
|
|
12
12
|
export const BubbleMenu = ({
|
|
13
|
-
element,
|
|
14
|
-
tippyOptions = { maxWidth: 'none', zIndex:
|
|
15
|
-
}) => BubbleMenuToExtend.configure({
|
|
13
|
+
element = null,
|
|
14
|
+
tippyOptions = { maxWidth: 'none', zIndex: 1010 },
|
|
15
|
+
} = {}) => BubbleMenuToExtend.configure({
|
|
16
16
|
element,
|
|
17
17
|
tippyOptions,
|
|
18
18
|
});
|
|
@@ -1,6 +1,72 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
2
|
import { NodeType, MarkType } from '../constants';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* we cannot use existing textAlign extension due to need to change textAlign global attribute renderHTML function
|
|
6
|
+
* that not allowed by simple default extension configuration
|
|
7
|
+
*/
|
|
8
|
+
export const TextAlign = Extension.create({
|
|
9
|
+
name: 'textAlign',
|
|
10
|
+
addOptions() {
|
|
11
|
+
return {
|
|
12
|
+
types: [NodeType.HEADING, NodeType.PARAGRAPH, MarkType.LINK, NodeType.CODE_BLOCK, NodeType.BLOCKQUOTE],
|
|
13
|
+
alignments: ['left', 'center', 'right', 'justify'],
|
|
14
|
+
defaultAlignment: 'left',
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
addGlobalAttributes() {
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
types: this.options.types,
|
|
21
|
+
attributes: {
|
|
22
|
+
textAlign: {
|
|
23
|
+
default: this.options.defaultAlignment,
|
|
24
|
+
parseHTML: element => {
|
|
25
|
+
return element.style.textAlign || this.options.defaultAlignment;
|
|
26
|
+
},
|
|
27
|
+
renderHTML: attributes => {
|
|
28
|
+
if (attributes.textAlign === this.options.defaultAlignment) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* because we gave users access to style attribute
|
|
33
|
+
* (and now editor doesn't reset it when renders HTML)
|
|
34
|
+
* we have to sanitize style attribute by ourselves to avoid duplication of textAlign written
|
|
35
|
+
* as independent attribute and part of style attribute string in the same time
|
|
36
|
+
*/
|
|
37
|
+
if (/text-align/.test(attributes?.style)) {
|
|
38
|
+
attributes.style = attributes.style.replace(/text-align:\s?\w+;?\s?/, '');
|
|
39
|
+
}
|
|
40
|
+
return { style: `text-align: ${attributes.textAlign}` };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
},
|
|
47
|
+
addCommands() {
|
|
48
|
+
return {
|
|
49
|
+
setTextAlign: (alignment) => ({ commands }) => {
|
|
50
|
+
if (!this.options.alignments.includes(alignment)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return this.options.types
|
|
54
|
+
.map(type => commands.updateAttributes(type, { textAlign: alignment }))
|
|
55
|
+
.every(response => response);
|
|
56
|
+
},
|
|
57
|
+
unsetTextAlign: () => ({ commands }) => {
|
|
58
|
+
return this.options.types
|
|
59
|
+
.map(type => commands.resetAttributes(type, 'textAlign'))
|
|
60
|
+
.every(response => response);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
addKeyboardShortcuts() {
|
|
65
|
+
return {
|
|
66
|
+
'Mod-Shift-l': () => this.editor.commands.setTextAlign('left'),
|
|
67
|
+
'Mod-Shift-e': () => this.editor.commands.setTextAlign('center'),
|
|
68
|
+
'Mod-Shift-r': () => this.editor.commands.setTextAlign('right'),
|
|
69
|
+
'Mod-Shift-j': () => this.editor.commands.setTextAlign('justify'),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
6
72
|
});
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</div>
|
|
26
26
|
<div class="row row-hgap-3 mar-bot-l1">
|
|
27
27
|
<div class="col col-vmid text-truncate">
|
|
28
|
-
<div class="form-label form-label-xsmall text-truncate"
|
|
28
|
+
<div class="form-label form-label-xsmall text-truncate">Тип перехода</div>
|
|
29
29
|
</div>
|
|
30
30
|
<div class="col col-vmid col-12-12">
|
|
31
31
|
<ui-select
|
|
@@ -68,8 +68,8 @@ export default {
|
|
|
68
68
|
}),
|
|
69
69
|
static: {
|
|
70
70
|
TargetTypeOptions: [
|
|
71
|
-
{ label: '
|
|
72
|
-
{ label: '
|
|
71
|
+
{ label: 'В новой вкладке', value: TargetType.BLANK },
|
|
72
|
+
{ label: 'В текущей вкладке', value: TargetType.SELF }
|
|
73
73
|
]
|
|
74
74
|
},
|
|
75
75
|
methods: {
|