goodteditor-ui 1.0.24 → 1.0.26
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 +1 -1
- package/src/components/ui/Datalist.vue +10 -3
- package/src/components/ui/InputAutocomplete.vue +17 -9
- package/src/components/ui/WysiwygEditor/WysiwygEditor.d.ts +11 -2
- package/src/components/ui/WysiwygEditor/constants.js +14 -5
- package/src/components/ui/WysiwygEditor/extensions/font-size.js +1 -1
- package/src/components/ui/WysiwygEditor/extensions/image.js +17 -3
- package/src/components/ui/WysiwygEditor/extensions/text-style.js +0 -3
- package/src/components/ui/WysiwygEditor/renders/Image.vue +162 -0
- package/src/components/ui/WysiwygEditor/renders/InputUnits.vue +0 -1
- package/src/components/ui/WysiwygEditor/renders/Link.vue +36 -41
- package/src/components/ui/WysiwygEditor/renders/components/WithPopover.vue +35 -0
- package/src/components/ui/WysiwygEditor/renders/index.d.ts +1 -1
- package/src/components/ui/WysiwygEditor/renders/index.js +1 -1
- package/src/components/ui/WysiwygEditor/tools-and-commands.js +28 -30
- package/src/components/ui/WysiwygEditor/utils.js +1 -1
- package/src/components/ui/WysiwygEditor/renders/InputBrowse.vue +0 -35
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
@slot Dropdown header slot
|
|
5
5
|
-->
|
|
6
6
|
<slot name="header"></slot>
|
|
7
|
-
<ul ref="ul">
|
|
7
|
+
<ul ref="ul" :style="{ maxHeight }">
|
|
8
8
|
<!--
|
|
9
9
|
@slot Label slot for single mode
|
|
10
10
|
@binding {Object} option option
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
:key="index"
|
|
28
28
|
@click="onOptionClick(option, index)"
|
|
29
29
|
>
|
|
30
|
-
<div class="text-truncate" style="min-height: calc(var(--line-height)*1rem)">
|
|
30
|
+
<div class="text-truncate" style="min-height: calc(var(--line-height) * 1rem)">
|
|
31
31
|
{{ option }}
|
|
32
32
|
</div>
|
|
33
33
|
</li>
|
|
@@ -73,10 +73,17 @@ export default {
|
|
|
73
73
|
size: {
|
|
74
74
|
type: String,
|
|
75
75
|
default: '',
|
|
76
|
-
validator: function(value) {
|
|
76
|
+
validator: function (value) {
|
|
77
77
|
return ['', 'small', 'large'].indexOf(value) >= 0;
|
|
78
78
|
},
|
|
79
79
|
},
|
|
80
|
+
/**
|
|
81
|
+
* Defines the max-height of the dropdown list (value may be any css unit/expression)
|
|
82
|
+
*/
|
|
83
|
+
maxHeight: {
|
|
84
|
+
type: String,
|
|
85
|
+
default: '',
|
|
86
|
+
},
|
|
80
87
|
/**
|
|
81
88
|
* The current cursor index position (.sync)
|
|
82
89
|
*/
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<ui-datalist
|
|
38
38
|
class="w-100 pull-left"
|
|
39
39
|
@select-option="onDatalistSelectOption"
|
|
40
|
-
v-bind="{ size, options: suggestedOptions }"
|
|
40
|
+
v-bind="{ size, maxHeight: dropdownMaxHeight, options: suggestedOptions }"
|
|
41
41
|
:cursorIndex.sync="dataListCursorIndex"
|
|
42
42
|
ref="datalist"
|
|
43
43
|
>
|
|
@@ -51,14 +51,7 @@
|
|
|
51
51
|
-->
|
|
52
52
|
<slot name="noresults" v-if="!loading && !suggestedOptions.length"></slot>
|
|
53
53
|
</template>
|
|
54
|
-
<template
|
|
55
|
-
#option="{
|
|
56
|
-
option,
|
|
57
|
-
index,
|
|
58
|
-
cursorIndex,
|
|
59
|
-
setCursorIndex,
|
|
60
|
-
}"
|
|
61
|
-
>
|
|
54
|
+
<template #option="{ option, index, cursorIndex, setCursorIndex }">
|
|
62
55
|
<!--
|
|
63
56
|
@slot Dropdown option
|
|
64
57
|
@binding {String} option option label
|
|
@@ -159,7 +152,17 @@ export default {
|
|
|
159
152
|
type: Number,
|
|
160
153
|
default: 0,
|
|
161
154
|
},
|
|
155
|
+
/**
|
|
156
|
+
* Defines the dropdown's max-height
|
|
157
|
+
*/
|
|
158
|
+
dropdownMaxHeight: {
|
|
159
|
+
default: '',
|
|
160
|
+
},
|
|
161
|
+
/**
|
|
162
|
+
* Auto width
|
|
163
|
+
*/
|
|
162
164
|
autoWidth: {
|
|
165
|
+
type: Boolean,
|
|
163
166
|
default: true,
|
|
164
167
|
},
|
|
165
168
|
},
|
|
@@ -266,6 +269,11 @@ export default {
|
|
|
266
269
|
},
|
|
267
270
|
onInputBlur(e) {
|
|
268
271
|
this.rootHasFocus = false;
|
|
272
|
+
/**
|
|
273
|
+
* Blur event reemit
|
|
274
|
+
* @property {Event} event
|
|
275
|
+
*/
|
|
276
|
+
this.$emit('blur', e);
|
|
269
277
|
},
|
|
270
278
|
onInputFocus(e) {
|
|
271
279
|
this.rootHasFocus = true;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Select,
|
|
4
|
+
InputUnits,
|
|
5
|
+
ColorPicker,
|
|
6
|
+
ToolbarPopover,
|
|
7
|
+
InputAuto,
|
|
8
|
+
Link,
|
|
9
|
+
Image
|
|
10
|
+
} from './renders';
|
|
2
11
|
|
|
3
12
|
export type Render = Readonly<{
|
|
4
13
|
BUTTON: Button;
|
|
@@ -7,8 +16,8 @@ export type Render = Readonly<{
|
|
|
7
16
|
SELECT: Select;
|
|
8
17
|
TOOLBAR_POPOVER: ToolbarPopover;
|
|
9
18
|
INPUT_AUTO: InputAuto;
|
|
10
|
-
INPUT_BROWSE: InputBrowse;
|
|
11
19
|
LINK: Link;
|
|
20
|
+
IMAGE: Image;
|
|
12
21
|
}>;
|
|
13
22
|
|
|
14
23
|
export type CommandDef = {
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Select,
|
|
4
|
+
InputUnits,
|
|
5
|
+
ColorPicker,
|
|
6
|
+
ToolbarPopover,
|
|
7
|
+
InputAuto,
|
|
8
|
+
Link,
|
|
9
|
+
Image
|
|
10
|
+
} from './renders';
|
|
2
11
|
|
|
3
12
|
/**
|
|
4
13
|
* @typedef {import('./WysiwygEditor').NodeType} NodeType
|
|
@@ -147,8 +156,8 @@ export const Render = Object.freeze({
|
|
|
147
156
|
SELECT: Select,
|
|
148
157
|
TOOLBAR_POPOVER: ToolbarPopover,
|
|
149
158
|
INPUT_AUTO: InputAuto,
|
|
150
|
-
|
|
151
|
-
|
|
159
|
+
LINK: Link,
|
|
160
|
+
IMAGE: Image
|
|
152
161
|
});
|
|
153
162
|
|
|
154
163
|
/**
|
|
@@ -231,8 +240,8 @@ export const createInputAutoTool = ({ getValue = () => null, options = [], ...to
|
|
|
231
240
|
* @param {Tool} tool
|
|
232
241
|
* @return Tool
|
|
233
242
|
*/
|
|
234
|
-
export const
|
|
235
|
-
...createBaseTool({ render: Render.
|
|
243
|
+
export const createImageTool = ({ getValue = () => null, ...toolOptions }) => ({
|
|
244
|
+
...createBaseTool({ render: Render.IMAGE, ...toolOptions }),
|
|
236
245
|
getValue
|
|
237
246
|
});
|
|
238
247
|
|
|
@@ -24,7 +24,7 @@ export const FontSize = Extension.create({
|
|
|
24
24
|
},
|
|
25
25
|
addCommands() {
|
|
26
26
|
return {
|
|
27
|
-
setFontSize: fontSize => ({
|
|
27
|
+
setFontSize: fontSize => ({ commands }) => commands.setMark(MarkType.TEXT_STYLE, { fontSize }),
|
|
28
28
|
unsetFontSize: () => ({ chain }) => chain()
|
|
29
29
|
.setMark(MarkType.TEXT_STYLE, { fontSize: null })
|
|
30
30
|
.removeEmptyTextStyle()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Image as ImageToExtend } from '@tiptap/extension-image';
|
|
2
|
+
import { NodeType } from '../constants';
|
|
2
3
|
|
|
3
4
|
export const Image = ImageToExtend
|
|
4
5
|
.extend({
|
|
@@ -13,7 +14,20 @@ export const Image = ImageToExtend
|
|
|
13
14
|
},
|
|
14
15
|
};
|
|
15
16
|
},
|
|
17
|
+
addCommands() {
|
|
18
|
+
return {
|
|
19
|
+
...this.parent?.(),
|
|
20
|
+
toggleResponsive: (value = false) => ({ commands }) => {
|
|
21
|
+
value === true
|
|
22
|
+
? commands.updateAttributes(NodeType.IMAGE, { class: 'responsive' })
|
|
23
|
+
: commands.updateAttributes(NodeType.IMAGE, { class: null });
|
|
24
|
+
},
|
|
25
|
+
setStyles: (styles) => ({ commands }) => {
|
|
26
|
+
const styleObjToString = () => Object
|
|
27
|
+
.entries(styles)
|
|
28
|
+
.reduce((acc, [styleName, style]) => `${acc}${styleName}:${style};`, '');
|
|
29
|
+
commands.updateAttributes(NodeType.IMAGE, { style: styleObjToString() });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
16
33
|
})
|
|
17
|
-
.configure({
|
|
18
|
-
inline: true,
|
|
19
|
-
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<with-popover :show.sync="isPopoverShown" auto-width>
|
|
3
|
+
<template #button="{ togglePopover }">
|
|
4
|
+
<button :title="title" class="btn btn-small btn-outline btn-icon" @click="togglePopover">
|
|
5
|
+
<div class="icon">
|
|
6
|
+
<i class="mdi" :class="icon"></i>
|
|
7
|
+
</div>
|
|
8
|
+
</button>
|
|
9
|
+
</template>
|
|
10
|
+
<div class="w-f2">
|
|
11
|
+
<div class="row row-hgap-3 mar-bot-l1">
|
|
12
|
+
<div class="col col-vmid text-truncate">
|
|
13
|
+
<div class="form-label form-label-xsmall text-truncate">Url</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="col col-vmid col-12-12">
|
|
16
|
+
<div class="form-control form-control-icon-right w-12-12">
|
|
17
|
+
<input-autocomplete v-model.trim.lazy="image.url" size="small" @change="onChange" />
|
|
18
|
+
<div class="icon">
|
|
19
|
+
<i class="mdi mdi-folder cursor-pointer color-grey" @click="browse" />
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="row row-hgap-3 mar-bot-l1">
|
|
26
|
+
<div class="col col-vmid text-truncate">
|
|
27
|
+
<label for="resp" class="form-label form-label-small text-truncate">
|
|
28
|
+
Отзывчивое изображение
|
|
29
|
+
</label>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="col col-vmid col-auto">
|
|
32
|
+
<input
|
|
33
|
+
id="resp"
|
|
34
|
+
v-model="image.isResponsive"
|
|
35
|
+
type="checkbox"
|
|
36
|
+
class="switch switch-small pull-right">
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="row row-hgap-l1 mar-bot-l1">
|
|
41
|
+
<div class="col">
|
|
42
|
+
<div class="row row-hgap-3">
|
|
43
|
+
<div class="col col-vmid text-truncate">
|
|
44
|
+
<div class="form-label form-label-xsmall text-truncate">Ширина</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="col col-vmid col-12-12">
|
|
47
|
+
<input-units
|
|
48
|
+
v-model="image.width"
|
|
49
|
+
:units="$options.static.SizeUnits"
|
|
50
|
+
:disabled="image.isResponsive"
|
|
51
|
+
size="small"
|
|
52
|
+
@change="onChange">
|
|
53
|
+
</input-units>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="col">
|
|
58
|
+
<div class="row row-hgap-3">
|
|
59
|
+
<div class="col col-vmid text-truncate">
|
|
60
|
+
<div class="form-label form-label-xsmall text-truncate">Высота</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="col col-vmid col-12-12">
|
|
63
|
+
<input-units
|
|
64
|
+
v-model="image.height"
|
|
65
|
+
:units="$options.static.SizeUnits"
|
|
66
|
+
:disabled="image.isResponsive"
|
|
67
|
+
size="small"
|
|
68
|
+
@change="onChange">
|
|
69
|
+
</input-units>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</with-popover>
|
|
76
|
+
</template>
|
|
77
|
+
|
|
78
|
+
<script>
|
|
79
|
+
import InputAutocomplete from '../../InputAutocomplete.vue';
|
|
80
|
+
import InputUnits from '../../InputUnits.vue';
|
|
81
|
+
import WithPopover from './components/WithPopover.vue';
|
|
82
|
+
import { useRender } from './mixins';
|
|
83
|
+
|
|
84
|
+
const SizeUnits = ["rem", "em", "%", "px", "vh", "vw"];
|
|
85
|
+
|
|
86
|
+
const defaultImgSettings = {
|
|
87
|
+
url: '',
|
|
88
|
+
isResponsive: true,
|
|
89
|
+
height: '100%',
|
|
90
|
+
width: '100%'
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const cssStrToObj = (str) => {
|
|
94
|
+
if (str == null) {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
return str.split(';').reduce((obj, style) => {
|
|
98
|
+
const [name, val] = style.split(':').map((el) => el.trim());
|
|
99
|
+
if (name != null && val != null) {
|
|
100
|
+
obj[name] = val;
|
|
101
|
+
}
|
|
102
|
+
return obj;
|
|
103
|
+
}, {});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default {
|
|
107
|
+
components: {
|
|
108
|
+
InputAutocomplete,
|
|
109
|
+
InputUnits,
|
|
110
|
+
WithPopover
|
|
111
|
+
},
|
|
112
|
+
mixins: [useRender()],
|
|
113
|
+
data: () => ({
|
|
114
|
+
isPopoverShown: false,
|
|
115
|
+
image: { ...defaultImgSettings }
|
|
116
|
+
}),
|
|
117
|
+
watch: {
|
|
118
|
+
isPopoverShown(isShown) {
|
|
119
|
+
if (isShown === false) {
|
|
120
|
+
this.image = { ...defaultImgSettings };
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.setImageSettings();
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
static: {
|
|
128
|
+
SizeUnits
|
|
129
|
+
},
|
|
130
|
+
methods: {
|
|
131
|
+
setImageSettings() {
|
|
132
|
+
const attrs = this.tool.getValue();
|
|
133
|
+
|
|
134
|
+
if (JSON.stringify(attrs) === '{}') {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { src: url, class: className, style } = attrs;
|
|
139
|
+
const { width, height } = cssStrToObj(style);
|
|
140
|
+
|
|
141
|
+
this.image = {
|
|
142
|
+
...defaultImgSettings,
|
|
143
|
+
url,
|
|
144
|
+
isResponsive: className?.includes?.('responsive') ?? false,
|
|
145
|
+
...(width && { width }),
|
|
146
|
+
...(height && { height })
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
async browse() {
|
|
150
|
+
await this.tool.exec(this.image);
|
|
151
|
+
const { src: url } = this.tool.getValue();
|
|
152
|
+
|
|
153
|
+
this.image = { ...this.image, url };
|
|
154
|
+
this.emitExecuted();
|
|
155
|
+
},
|
|
156
|
+
onChange() {
|
|
157
|
+
this.tool.exec(this.image);
|
|
158
|
+
this.emitExecuted();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
</script>
|
|
@@ -1,63 +1,58 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<button
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
</button>
|
|
8
|
-
<popover :show="popoverShow" v-bind="popoverOptions">
|
|
9
|
-
<div class="dropdown pad-l1 pad-top-l2">
|
|
10
|
-
<div class="close pos-abs pos-top-right mar-2" @click="togglePopover">
|
|
11
|
-
<i class="mdi mdi-close color-grey" />
|
|
12
|
-
</div>
|
|
13
|
-
<div class="row row-hgap-3 mar-bot-l1">
|
|
14
|
-
<div class="col col-vmid text-truncate">
|
|
15
|
-
<div class="form-label form-label-xsmall text-truncate">Url</div>
|
|
16
|
-
</div>
|
|
17
|
-
<div class="col col-vmid col-12-12">
|
|
18
|
-
<input-autocomplete v-model="url" class="w-100" size="small" />
|
|
19
|
-
</div>
|
|
2
|
+
<with-popover :show.sync="isPopoverShown">
|
|
3
|
+
<template #button="{ togglePopover }">
|
|
4
|
+
<button :title="title" class="btn btn-small btn-outline btn-icon" @click="togglePopover">
|
|
5
|
+
<div class="icon">
|
|
6
|
+
<i class="mdi" :class="icon"></i>
|
|
20
7
|
</div>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class="w-100"
|
|
30
|
-
size="small" />
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
<button class="btn btn-outline btn-small w-100" @click="execute">Применить</button>
|
|
8
|
+
</button>
|
|
9
|
+
</template>
|
|
10
|
+
<div class="row row-hgap-3 mar-bot-l1">
|
|
11
|
+
<div class="col col-vmid text-truncate">
|
|
12
|
+
<div class="form-label form-label-xsmall text-truncate">Url</div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="col col-vmid col-12-12">
|
|
15
|
+
<input-autocomplete v-model="url" class="w-100" size="small" />
|
|
34
16
|
</div>
|
|
35
|
-
</
|
|
36
|
-
|
|
17
|
+
</div>
|
|
18
|
+
<div class="row row-hgap-3 mar-bot-l1">
|
|
19
|
+
<div class="col col-vmid text-truncate">
|
|
20
|
+
<div class="form-label form-label-xsmall text-truncate">Target</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="col col-vmid col-12-12">
|
|
23
|
+
<ui-select
|
|
24
|
+
v-model="target"
|
|
25
|
+
:options="$options.static.TargetTypeOptions"
|
|
26
|
+
class="w-100"
|
|
27
|
+
size="small" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<button class="btn btn-outline btn-small w-100" @click="execute">Применить</button>
|
|
31
|
+
</with-popover>
|
|
37
32
|
</template>
|
|
38
33
|
|
|
39
34
|
<script>
|
|
40
|
-
import Popover from '../../Popover.vue';
|
|
41
35
|
import InputAutocomplete from '../../InputAutocomplete.vue';
|
|
42
36
|
import UiSelect from '../../Select.vue';
|
|
43
|
-
import WithPopover from '
|
|
37
|
+
import WithPopover from './components/WithPopover.vue';
|
|
44
38
|
import { useRender } from './mixins';
|
|
45
39
|
|
|
46
40
|
const TargetType = {
|
|
47
41
|
BLANK: '_blank',
|
|
48
42
|
SELF: '_self'
|
|
49
|
-
}
|
|
43
|
+
};
|
|
50
44
|
|
|
51
45
|
export default {
|
|
52
46
|
components: {
|
|
53
|
-
|
|
47
|
+
WithPopover,
|
|
54
48
|
InputAutocomplete,
|
|
55
49
|
UiSelect
|
|
56
50
|
},
|
|
57
|
-
mixins: [useRender()
|
|
51
|
+
mixins: [useRender()],
|
|
58
52
|
data: () => ({
|
|
59
53
|
url: '',
|
|
60
|
-
target: null
|
|
54
|
+
target: null,
|
|
55
|
+
isPopoverShown: false
|
|
61
56
|
}),
|
|
62
57
|
static: {
|
|
63
58
|
TargetTypeOptions: [
|
|
@@ -66,7 +61,7 @@ export default {
|
|
|
66
61
|
]
|
|
67
62
|
},
|
|
68
63
|
watch: {
|
|
69
|
-
|
|
64
|
+
isPopoverShown(isShown) {
|
|
70
65
|
if (isShown) {
|
|
71
66
|
const { url = '', target = TargetType.BLANK } = this.tool.getValue();
|
|
72
67
|
this.url = url;
|
|
@@ -79,7 +74,7 @@ export default {
|
|
|
79
74
|
const { tool, url, target } = this;
|
|
80
75
|
tool.exec({ url, target });
|
|
81
76
|
|
|
82
|
-
this.
|
|
77
|
+
this.isPopoverShown = false;
|
|
83
78
|
this.emitExecuted();
|
|
84
79
|
}
|
|
85
80
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :data-popover="popoverTargetId">
|
|
3
|
+
<slot name="button" v-bind="{ togglePopover }"></slot>
|
|
4
|
+
|
|
5
|
+
<popover :show.sync="popoverShow" v-bind="popoverOptions">
|
|
6
|
+
<div class="dropdown pad-l1 pad-top-l2">
|
|
7
|
+
<div class="close pos-abs pos-top-right mar-2" @click="togglePopover">
|
|
8
|
+
<i class="mdi mdi-close color-grey" />
|
|
9
|
+
</div>
|
|
10
|
+
<slot></slot>
|
|
11
|
+
</div>
|
|
12
|
+
</popover>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script>
|
|
17
|
+
import Popover from '../../../Popover.vue';
|
|
18
|
+
import WithPopover from '../../../utils/WithPopover';
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
components: { Popover },
|
|
22
|
+
mixins: [WithPopover],
|
|
23
|
+
props: {
|
|
24
|
+
show: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
default: false
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
watch: {
|
|
30
|
+
popoverShow(val) {
|
|
31
|
+
this.$nextTick(() => this.$emit('update:show', val));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
@@ -4,5 +4,5 @@ export { default as InputUnits } from './InputUnits.vue';
|
|
|
4
4
|
export { default as ColorPicker } from './ColorPicker.vue';
|
|
5
5
|
export { default as ToolbarPopover } from './ToolbarPopover.vue';
|
|
6
6
|
export { default as InputAuto } from './InputAuto.vue';
|
|
7
|
-
export { default as InputBrowse } from './InputBrowse.vue';
|
|
8
7
|
export { default as Link } from './Link.vue';
|
|
8
|
+
export { default as Image } from './Image.vue';
|
|
@@ -4,5 +4,5 @@ export { default as InputUnits } from './InputUnits.vue';
|
|
|
4
4
|
export { default as ColorPicker } from './ColorPicker.vue';
|
|
5
5
|
export { default as ToolbarPopover } from './ToolbarPopover.vue';
|
|
6
6
|
export { default as InputAuto } from './InputAuto.vue';
|
|
7
|
-
export { default as InputBrowse } from './InputBrowse.vue';
|
|
8
7
|
export { default as Link } from './Link.vue';
|
|
8
|
+
export { default as Image } from './Image.vue';
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
createSelectTool,
|
|
11
11
|
createToolbarPopoverTool,
|
|
12
12
|
createInputAutoTool,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
createLinkTool,
|
|
14
|
+
createImageTool,
|
|
15
15
|
} from './constants';
|
|
16
16
|
|
|
17
17
|
export const CommandMap = {
|
|
@@ -382,32 +382,6 @@ export const TableOptionsMap = {
|
|
|
382
382
|
})
|
|
383
383
|
};
|
|
384
384
|
|
|
385
|
-
export const ImageOptionsMap = {
|
|
386
|
-
[ToolType.INSERT_IMAGE]: createInputBrowseTool({
|
|
387
|
-
name: ToolType.INSERT_IMAGE,
|
|
388
|
-
title: 'Вставить изображение',
|
|
389
|
-
async exec(fromInputUrl) {
|
|
390
|
-
const setImage = (url) => {
|
|
391
|
-
this.editor.chain().focus().setImage({ src: url }).run();
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
if (fromInputUrl != null) {
|
|
395
|
-
setImage(fromInputUrl);
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const imageUrl = await this.getImageUrl();
|
|
400
|
-
|
|
401
|
-
if (imageUrl != null) {
|
|
402
|
-
setImage(imageUrl);
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
getValue() {
|
|
406
|
-
return this.editor.getAttributes(NodeType.IMAGE).src;
|
|
407
|
-
}
|
|
408
|
-
})
|
|
409
|
-
};
|
|
410
|
-
|
|
411
385
|
export const ToolsMap = {
|
|
412
386
|
[ToolType.UNDO]: createButtonTool({
|
|
413
387
|
name: ToolType.UNDO,
|
|
@@ -537,11 +511,35 @@ export const ToolsMap = {
|
|
|
537
511
|
return this.editor.isActive(MarkType.CODE);
|
|
538
512
|
}
|
|
539
513
|
}),
|
|
540
|
-
[ToolType.IMAGE]:
|
|
514
|
+
[ToolType.IMAGE]: createImageTool({
|
|
541
515
|
name: ToolType.IMAGE,
|
|
542
516
|
icon: 'image-plus',
|
|
543
517
|
title: 'Изображение',
|
|
544
|
-
|
|
518
|
+
async exec({ url = null, isResponsive, width, height }) {
|
|
519
|
+
const setImage = (src) => {
|
|
520
|
+
const editor = this.editor.chain().setImage({ src });
|
|
521
|
+
|
|
522
|
+
if (isResponsive === false) {
|
|
523
|
+
editor.setStyles({ width, height }).toggleResponsive().run();
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
editor.run();
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
if ([null, ''].includes(url) === false) {
|
|
530
|
+
setImage(url);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const imageUrl = await this.getImageUrl();
|
|
535
|
+
|
|
536
|
+
if (imageUrl != null) {
|
|
537
|
+
setImage(imageUrl);
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
getValue() {
|
|
541
|
+
return this.editor.getAttributes(NodeType.IMAGE);
|
|
542
|
+
}
|
|
545
543
|
}),
|
|
546
544
|
[ToolType.ORDERED_LIST]: createButtonTool({
|
|
547
545
|
name: ToolType.ORDERED_LIST,
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div :title="title" class="form-control form-control-icon-right w-12-12">
|
|
3
|
-
<input-autocomplete
|
|
4
|
-
v-bind="{ value, disabled: !isEnabled, size: 'small' }"
|
|
5
|
-
@change="onChange" />
|
|
6
|
-
<div class="icon">
|
|
7
|
-
<i class="mdi mdi-folder cursor-pointer color-grey" @click="browse" />
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
</template>
|
|
11
|
-
|
|
12
|
-
<script>
|
|
13
|
-
import InputAutocomplete from '../../InputAutocomplete.vue';
|
|
14
|
-
import { useRender } from './mixins';
|
|
15
|
-
|
|
16
|
-
export default {
|
|
17
|
-
components: { InputAutocomplete },
|
|
18
|
-
mixins: [useRender()],
|
|
19
|
-
computed: {
|
|
20
|
-
value() {
|
|
21
|
-
return this.tool.getValue();
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
methods: {
|
|
25
|
-
onChange(value) {
|
|
26
|
-
this.tool.exec(value);
|
|
27
|
-
this.emitExecuted();
|
|
28
|
-
},
|
|
29
|
-
async browse() {
|
|
30
|
-
await this.tool.exec();
|
|
31
|
-
this.emitExecuted();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
</script>
|