@bagelink/vue 0.0.378 → 0.0.384
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/dist/components/BglVideo.vue.d.ts +6 -6
- package/dist/components/BglVideo.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts +2 -0
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/ModalForm.vue.d.ts +6 -0
- package/dist/components/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/FileUpload.vue.d.ts +9 -2
- package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText.vue.d.ts.map +1 -1
- package/dist/index.cjs +425 -67
- package/dist/index.mjs +425 -67
- package/dist/plugins/modal.d.ts +2 -0
- package/dist/plugins/modal.d.ts.map +1 -1
- package/dist/style.css +127 -123
- package/package.json +3 -1
- package/src/components/BglVideo.vue +27 -21
- package/src/components/Modal.vue +67 -46
- package/src/components/ModalForm.vue +39 -18
- package/src/components/form/inputs/FileUpload.vue +17 -6
- package/src/components/form/inputs/FileUploadURL.vue +296 -0
- package/src/components/form/inputs/RichText.vue +48 -8
- package/src/plugins/modal.ts +2 -0
- package/src/styles/modal.css +74 -74
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Modal
|
|
3
|
-
@onUpdate:isModalVisible="props['onUpdate:isModalVisible']"
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
@onUpdate:isModalVisible="props['onUpdate:isModalVisible']"
|
|
4
|
+
:side
|
|
5
|
+
ref="modal"
|
|
6
|
+
:width
|
|
7
|
+
:dismissable
|
|
8
|
+
:title="title">
|
|
9
|
+
<BagelForm
|
|
10
|
+
@submit="runSubmit"
|
|
11
|
+
ref="form"
|
|
12
|
+
v-model="formData"
|
|
13
|
+
:schema="computedFormSchema" />
|
|
7
14
|
<template #footer v-if="onDelete || onSubmit">
|
|
8
15
|
<div>
|
|
9
16
|
<Btn thin flat value="Cancel" @click="closeModal" />
|
|
10
|
-
<Btn
|
|
17
|
+
<Btn
|
|
18
|
+
thin
|
|
19
|
+
icon="delete"
|
|
20
|
+
v-if="onDelete"
|
|
21
|
+
flat
|
|
22
|
+
value="Delete"
|
|
23
|
+
@click="runDelete"
|
|
24
|
+
color="red" />
|
|
11
25
|
</div>
|
|
12
26
|
<Btn value="Submit" @click="runSubmit" />
|
|
13
27
|
</template>
|
|
@@ -16,28 +30,33 @@
|
|
|
16
30
|
|
|
17
31
|
<script lang="ts" setup>
|
|
18
32
|
import {
|
|
19
|
-
Modal,
|
|
33
|
+
Modal,
|
|
34
|
+
Btn,
|
|
35
|
+
BagelForm,
|
|
36
|
+
type BglFormSchemaT,
|
|
37
|
+
type BtnOptions,
|
|
38
|
+
useBagel,
|
|
20
39
|
} from '@bagelink/vue';
|
|
21
40
|
|
|
22
41
|
const bagel = useBagel();
|
|
23
42
|
|
|
24
43
|
const props = defineProps<{
|
|
25
|
-
side?: boolean;
|
|
26
|
-
title?: string;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
'side'?: boolean;
|
|
45
|
+
'title'?: string;
|
|
46
|
+
'width'?: string;
|
|
47
|
+
'dismissable'?: boolean;
|
|
48
|
+
'actions'?: BtnOptions[];
|
|
49
|
+
'schema': BglFormSchemaT | (() => BglFormSchemaT);
|
|
30
50
|
// eslint-disable-next-line no-unused-vars
|
|
31
|
-
onSubmit?: (
|
|
51
|
+
'onSubmit'?: (formData: any) => Promise<void>;
|
|
32
52
|
// eslint-disable-next-line no-unused-vars
|
|
33
|
-
onDelete?: (
|
|
53
|
+
'onDelete'?: (id: string) => void;
|
|
34
54
|
// eslint-disable-next-line no-unused-vars
|
|
35
|
-
'onUpdate:isModalVisible'?: (
|
|
55
|
+
'onUpdate:isModalVisible'?: (visible: boolean) => void;
|
|
36
56
|
// eslint-disable-next-line no-unused-vars
|
|
37
|
-
onError?: (
|
|
38
|
-
|
|
39
|
-
modelValue?: Record<string, any>;
|
|
57
|
+
'onError'?: (err: any) => void;
|
|
40
58
|
|
|
59
|
+
'modelValue'?: Record<string, any>;
|
|
41
60
|
}>();
|
|
42
61
|
|
|
43
62
|
const modal = $ref<InstanceType<typeof Modal>>();
|
|
@@ -47,7 +66,9 @@ const computedFormSchema = $computed(() => {
|
|
|
47
66
|
return props.schema;
|
|
48
67
|
});
|
|
49
68
|
|
|
50
|
-
const formData = defineModel<Record<string, any>>('modelValue', {
|
|
69
|
+
const formData = defineModel<Record<string, any>>('modelValue', {
|
|
70
|
+
default: {},
|
|
71
|
+
});
|
|
51
72
|
const form = $ref<InstanceType<typeof BagelForm>>();
|
|
52
73
|
const closeModal = () => modal?.closeModal();
|
|
53
74
|
|
|
@@ -79,17 +79,24 @@ type QueueFile = {
|
|
|
79
79
|
uploaded?: boolean;
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
type StrKey = keyof StorageFile;
|
|
83
|
+
|
|
84
|
+
type FSValue = string[] | string | number;
|
|
85
|
+
|
|
82
86
|
const props = defineProps<{
|
|
83
87
|
label: string;
|
|
84
88
|
multiple?: boolean;
|
|
85
89
|
files?: StorageFile | StorageFile[];
|
|
86
90
|
deleteEndpoint?: string;
|
|
91
|
+
bindkey?: StrKey;
|
|
87
92
|
}>();
|
|
88
93
|
|
|
89
|
-
const
|
|
94
|
+
const bindKey: StrKey = props.bindkey || 'id';
|
|
95
|
+
|
|
96
|
+
const file_bindkeys = defineModel<FSValue>('modelValue');
|
|
90
97
|
const storageFiles = $ref<StorageFile[]>([]);
|
|
91
98
|
|
|
92
|
-
const compareIds = (v1?:
|
|
99
|
+
const compareIds = (v1?: FSValue, v2?: FSValue) =>
|
|
93
100
|
[v1].flat().join(',') === [v2].flat().join(',');
|
|
94
101
|
|
|
95
102
|
watch(
|
|
@@ -103,10 +110,14 @@ watch(
|
|
|
103
110
|
watch(
|
|
104
111
|
() => storageFiles,
|
|
105
112
|
(newFiles) => {
|
|
106
|
-
let idValue:
|
|
107
|
-
if (props.multiple) idValue = newFiles.map((f) => f
|
|
108
|
-
else
|
|
109
|
-
|
|
113
|
+
let idValue: FSValue;
|
|
114
|
+
if (props.multiple) idValue = newFiles.map((f) => f[bindKey]) as FSValue;
|
|
115
|
+
else {
|
|
116
|
+
idValue = (newFiles[0]?.[bindKey] as string) || '';
|
|
117
|
+
}
|
|
118
|
+
if (!compareIds(file_bindkeys?.value, idValue)) {
|
|
119
|
+
file_bindkeys.value = idValue;
|
|
120
|
+
}
|
|
110
121
|
},
|
|
111
122
|
{ deep: true }
|
|
112
123
|
);
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="bagel-input">
|
|
3
|
+
<label>
|
|
4
|
+
{{ label }}
|
|
5
|
+
</label>
|
|
6
|
+
<div
|
|
7
|
+
@click="browse"
|
|
8
|
+
@dragover="dragover"
|
|
9
|
+
@drop="drop"
|
|
10
|
+
@dragleave="dragleave"
|
|
11
|
+
class="fileUploadWrap"
|
|
12
|
+
:class="{
|
|
13
|
+
fileDropZone: !storageFiles.length && !fileQueue.length,
|
|
14
|
+
dragover: isDragOver,
|
|
15
|
+
}">
|
|
16
|
+
<template v-for="file in storageFiles" :key="file.id">
|
|
17
|
+
<div class="imagePreviewWrap" v-if="!multiple">
|
|
18
|
+
<img class="preview" :src="file.url" alt="" />
|
|
19
|
+
</div>
|
|
20
|
+
<div class="previewName">
|
|
21
|
+
<img
|
|
22
|
+
height="60"
|
|
23
|
+
v-if="multiple"
|
|
24
|
+
class="preview"
|
|
25
|
+
:src="file.url"
|
|
26
|
+
alt="" />
|
|
27
|
+
<p class="no-margin">
|
|
28
|
+
{{ file.name }}
|
|
29
|
+
</p>
|
|
30
|
+
<Btn
|
|
31
|
+
thin
|
|
32
|
+
@click.stop="removeFile(file)"
|
|
33
|
+
flat
|
|
34
|
+
icon="delete"
|
|
35
|
+
color="red" />
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
<template v-for="fileQ in fileQueue" :key="fileQ.file">
|
|
39
|
+
<div class="imagePreviewWrap" v-if="!multiple">
|
|
40
|
+
<img class="preview" :src="fileToUrl(fileQ.file)" alt="" />
|
|
41
|
+
</div>
|
|
42
|
+
<div class="previewName">
|
|
43
|
+
<img
|
|
44
|
+
height="60"
|
|
45
|
+
v-if="multiple"
|
|
46
|
+
class="preview"
|
|
47
|
+
:src="fileToUrl(fileQ.file)"
|
|
48
|
+
alt="" />
|
|
49
|
+
<p class="no-margin">
|
|
50
|
+
{{ fileQ.name }}
|
|
51
|
+
</p>
|
|
52
|
+
<div
|
|
53
|
+
class="pie"
|
|
54
|
+
:style="`--p:${fileQ.progress}`"
|
|
55
|
+
style="--b: 2px"
|
|
56
|
+
:class="{ complete: fileQ.progress === 100 }">
|
|
57
|
+
<span class="progress" v-if="fileQ.progress < 100">
|
|
58
|
+
{{ `${fileQ.progress.toFixed(0)}` }}
|
|
59
|
+
</span>
|
|
60
|
+
<MaterialIcon class="success" icon="check" />
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import { watch } from 'vue';
|
|
70
|
+
import { Btn, type StorageFile, useBagel, MaterialIcon } from '@bagelink/vue';
|
|
71
|
+
|
|
72
|
+
const bagel = useBagel();
|
|
73
|
+
|
|
74
|
+
type QueueFile = {
|
|
75
|
+
file: File;
|
|
76
|
+
progress: number;
|
|
77
|
+
uploading?: boolean;
|
|
78
|
+
name?: string;
|
|
79
|
+
uploaded?: boolean;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const props = defineProps<{
|
|
83
|
+
label: string;
|
|
84
|
+
multiple?: boolean;
|
|
85
|
+
files?: StorageFile | StorageFile[];
|
|
86
|
+
deleteEndpoint?: string;
|
|
87
|
+
}>();
|
|
88
|
+
|
|
89
|
+
const fileURLs = defineModel<string[] | string>('modelValue');
|
|
90
|
+
const storageFiles = $ref<StorageFile[]>([]);
|
|
91
|
+
|
|
92
|
+
const compareIds = (v1?: string[] | string, v2?: string[] | string) =>
|
|
93
|
+
[v1].flat().join(',') === [v2].flat().join(',');
|
|
94
|
+
|
|
95
|
+
watch(
|
|
96
|
+
() => props.files,
|
|
97
|
+
(newFiles) => {
|
|
98
|
+
if (newFiles) storageFiles.push(...[newFiles].flat());
|
|
99
|
+
},
|
|
100
|
+
{ immediate: true }
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
watch(
|
|
104
|
+
() => storageFiles,
|
|
105
|
+
(newFiles) => {
|
|
106
|
+
let idValue: string[] | string = '';
|
|
107
|
+
if (props.multiple) idValue = newFiles.map((f) => f.url || '');
|
|
108
|
+
else idValue = newFiles[0]?.url || '';
|
|
109
|
+
if (!compareIds(fileURLs?.value, idValue)) fileURLs.value = idValue;
|
|
110
|
+
},
|
|
111
|
+
{ deep: true }
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const fileQueue = $ref<QueueFile[]>([]);
|
|
115
|
+
let isDragOver = $ref(false);
|
|
116
|
+
|
|
117
|
+
const preventDefault = (e: Event) => {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
e.stopPropagation();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const fileToUrl = (file: File) => URL.createObjectURL(file);
|
|
123
|
+
const removeFile = (file: StorageFile) => {
|
|
124
|
+
const index = storageFiles.indexOf(file);
|
|
125
|
+
storageFiles.splice(index, 1);
|
|
126
|
+
|
|
127
|
+
if (props.deleteEndpoint) {
|
|
128
|
+
void bagel.delete(`${props.deleteEndpoint}/${file.id}`);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const flushQueue = () => {
|
|
133
|
+
fileQueue.forEach(async (file, i) => {
|
|
134
|
+
if (!props.multiple) storageFiles.splice(0, 1);
|
|
135
|
+
const serverFile = await bagel.uploadFile<StorageFile>(file.file, {
|
|
136
|
+
onUploadProgress: (e: any) => (file.progress = e.progress * 100 - 1),
|
|
137
|
+
});
|
|
138
|
+
storageFiles.push(serverFile);
|
|
139
|
+
fileQueue.splice(i, 1);
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const browse = () => {
|
|
144
|
+
const input = document.createElement('input');
|
|
145
|
+
input.type = 'file';
|
|
146
|
+
input.multiple = props.multiple;
|
|
147
|
+
input.onchange = (e: Event) => {
|
|
148
|
+
const target = e?.target as HTMLInputElement;
|
|
149
|
+
if (target?.files) {
|
|
150
|
+
Array.from(target.files).forEach((file: File) =>
|
|
151
|
+
fileQueue.push({ name: file.name, file, progress: 0 })
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
flushQueue();
|
|
155
|
+
};
|
|
156
|
+
input.click();
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const dragleave = (e: DragEvent) => {
|
|
160
|
+
preventDefault(e);
|
|
161
|
+
if (e.dataTransfer) isDragOver = false;
|
|
162
|
+
else isDragOver = false;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const dragover = (e: DragEvent) => {
|
|
166
|
+
preventDefault(e);
|
|
167
|
+
if (e.dataTransfer) isDragOver = true;
|
|
168
|
+
else isDragOver = false;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const drop = (e: DragEvent) => {
|
|
172
|
+
preventDefault(e);
|
|
173
|
+
if (e.dataTransfer) {
|
|
174
|
+
Array.from(e.dataTransfer.files).forEach((file: File) =>
|
|
175
|
+
fileQueue.push({ name: file.name, file, progress: 0 })
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
isDragOver = false;
|
|
179
|
+
flushQueue();
|
|
180
|
+
};
|
|
181
|
+
</script>
|
|
182
|
+
|
|
183
|
+
<style scoped>
|
|
184
|
+
.bagel-input .fileUploadWrap {
|
|
185
|
+
outline: 1px solid var(--border-color);
|
|
186
|
+
border-radius: var(--input-border-radius);
|
|
187
|
+
text-align: center;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
transition: var(--bgl-transition);
|
|
190
|
+
position: relative;
|
|
191
|
+
height: 132px;
|
|
192
|
+
font-size: var(--input-font-size);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.previewName {
|
|
196
|
+
padding: 0.5rem;
|
|
197
|
+
text-align: start;
|
|
198
|
+
color: var(--input-color);
|
|
199
|
+
display: grid;
|
|
200
|
+
grid-template-columns: 1fr 22px;
|
|
201
|
+
align-items: center;
|
|
202
|
+
padding-inline: 14px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.previewName p {
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
text-overflow: ellipsis;
|
|
208
|
+
white-space: nowrap;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.imagePreviewWrap {
|
|
212
|
+
background: var(--input-bg);
|
|
213
|
+
border-radius: var(--input-border-radius);
|
|
214
|
+
height: 90px;
|
|
215
|
+
padding: 5px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
img.preview {
|
|
219
|
+
height: 80px;
|
|
220
|
+
border-radius: var(--input-border-radius);
|
|
221
|
+
object-fit: contain;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.fileUploadWrap.dragover,
|
|
225
|
+
.fileUploadWrap:hover {
|
|
226
|
+
box-shadow: inset 0 0 10px #00000012;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.bagel-input .fileUploadWrap.fileDropZone {
|
|
230
|
+
background: var(--input-bg);
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
justify-content: center;
|
|
234
|
+
color: var(--bgl-gray);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.bagel-input .fileUploadWrap.fileDropZone:after {
|
|
238
|
+
content: 'Drop files here or click to upload';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.pie {
|
|
242
|
+
width: 30px;
|
|
243
|
+
height: 30px;
|
|
244
|
+
position: relative;
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.pie:before {
|
|
251
|
+
content: '';
|
|
252
|
+
position: absolute;
|
|
253
|
+
border-radius: 50%;
|
|
254
|
+
inset: 0;
|
|
255
|
+
transition: all 0.2s ease-in-out;
|
|
256
|
+
background: conic-gradient(var(--bgl-primary) calc(var(--p) * 1%), #0000 0);
|
|
257
|
+
-webkit-mask: radial-gradient(
|
|
258
|
+
farthest-side,
|
|
259
|
+
#0000 calc(99% - var(--b)),
|
|
260
|
+
#000 calc(100% - var(--b))
|
|
261
|
+
);
|
|
262
|
+
mask: radial-gradient(
|
|
263
|
+
farthest-side,
|
|
264
|
+
#0000 calc(99% - var(--b)),
|
|
265
|
+
#000 calc(100% - var(--b))
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.pie .success {
|
|
270
|
+
transform: scale(0);
|
|
271
|
+
opacity: 0;
|
|
272
|
+
transition: all 0.3s ease-in-out;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.pie .progress {
|
|
276
|
+
position: absolute;
|
|
277
|
+
font-size: 10px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.pie.complete .progress {
|
|
281
|
+
display: none;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.pie.complete .success {
|
|
285
|
+
transform: scale(1.3);
|
|
286
|
+
opacity: 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.pie.complete:before {
|
|
290
|
+
background: conic-gradient(var(--bgl-green) calc(var(--p) * 1%), #0000 0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.pie.complete {
|
|
294
|
+
color: var(--bgl-green);
|
|
295
|
+
}
|
|
296
|
+
</style>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<div class="RichText">
|
|
3
3
|
<div class="RichText-tools" v-if="editor">
|
|
4
4
|
<Btn
|
|
5
|
+
tabindex="-1"
|
|
5
6
|
:flat="!editor.isActive(item.name, item.option)"
|
|
6
7
|
@click="item.command"
|
|
7
8
|
thin
|
|
@@ -16,12 +17,21 @@
|
|
|
16
17
|
</template>
|
|
17
18
|
|
|
18
19
|
<script setup lang="ts">
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
Btn,
|
|
22
|
+
useModal,
|
|
23
|
+
FileUpload as $el,
|
|
24
|
+
bagelFormUtils,
|
|
25
|
+
BglVideo,
|
|
26
|
+
} from '@bagelink/vue';
|
|
20
27
|
import { onMounted, onBeforeUnmount, watch } from 'vue';
|
|
21
28
|
import StarterKit from '@tiptap/starter-kit';
|
|
22
29
|
import { Editor, EditorContent } from '@tiptap/vue-3';
|
|
30
|
+
import Image from '@tiptap/extension-image';
|
|
31
|
+
import YouTube from '@tiptap/extension-youtube';
|
|
23
32
|
import type { MaterialIcons } from '@bagelink/vue';
|
|
24
|
-
|
|
33
|
+
// import $el from './FileUploadURL.vue';
|
|
34
|
+
const { txtField } = bagelFormUtils;
|
|
25
35
|
let editor = $ref<Editor>();
|
|
26
36
|
|
|
27
37
|
const props = defineProps<{ modelValue: string }>();
|
|
@@ -30,6 +40,19 @@ const focus = () => {
|
|
|
30
40
|
if (!editor) throw new Error('editor is not defined');
|
|
31
41
|
return editor.chain().focus();
|
|
32
42
|
};
|
|
43
|
+
const { showModalForm } = useModal();
|
|
44
|
+
|
|
45
|
+
function addImage() {
|
|
46
|
+
showModalForm({
|
|
47
|
+
title: 'Add Image',
|
|
48
|
+
schema: [
|
|
49
|
+
txtField('title', 'Title'),
|
|
50
|
+
{ $el, id: 'src', attrs: { bindkey: 'url' } },
|
|
51
|
+
txtField('alt', 'Alt Text'),
|
|
52
|
+
],
|
|
53
|
+
onSubmit: (imgObj) => focus().setImage(imgObj).run(),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
33
56
|
|
|
34
57
|
const config: {
|
|
35
58
|
name: string;
|
|
@@ -61,11 +84,28 @@ const config: {
|
|
|
61
84
|
command: () => focus().toggleCode().run(),
|
|
62
85
|
icon: 'code',
|
|
63
86
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
{
|
|
88
|
+
name: 'Image',
|
|
89
|
+
command: addImage,
|
|
90
|
+
icon: 'image',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'YouTube',
|
|
94
|
+
command: () => {
|
|
95
|
+
showModalForm({
|
|
96
|
+
title: 'Add YouTube Video',
|
|
97
|
+
schema: [
|
|
98
|
+
txtField('src', 'YouTube URL'),
|
|
99
|
+
{
|
|
100
|
+
$el: BglVideo,
|
|
101
|
+
attrs: { src: (_, { src }: { src: string }) => src || '' },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
onSubmit: (videoObj) => editor?.commands.setYoutubeVideo(videoObj),
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
icon: 'youtube_activity',
|
|
108
|
+
},
|
|
69
109
|
{
|
|
70
110
|
name: 'Clear',
|
|
71
111
|
command: () => focus().clearNodes().run(),
|
|
@@ -160,7 +200,7 @@ const emit = defineEmits(['update:modelValue']);
|
|
|
160
200
|
|
|
161
201
|
function initEditor() {
|
|
162
202
|
editor = new Editor({
|
|
163
|
-
extensions: [StarterKit],
|
|
203
|
+
extensions: [StarterKit, Image, YouTube],
|
|
164
204
|
content: props.modelValue,
|
|
165
205
|
onUpdate: ({ editor }) => emit('update:modelValue', editor.getHTML()),
|
|
166
206
|
});
|
package/src/plugins/modal.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface ModalOptions {
|
|
|
9
9
|
title?: string;
|
|
10
10
|
dismissable?: boolean;
|
|
11
11
|
side?: boolean;
|
|
12
|
+
width?: string;
|
|
12
13
|
actions?: BtnOptions[];
|
|
13
14
|
class?: string;
|
|
14
15
|
visible?: boolean;
|
|
@@ -18,6 +19,7 @@ export interface ModalFormOptions {
|
|
|
18
19
|
side?: boolean;
|
|
19
20
|
title?: string;
|
|
20
21
|
visible?: boolean;
|
|
22
|
+
width?: string;
|
|
21
23
|
dismissable?: boolean;
|
|
22
24
|
schema: BglFormSchemaT<any> | (() => BglFormSchemaT) | BglFormSchemaT
|
|
23
25
|
onSubmit?: (formData: any) => any;
|