Add file library integration for WYSIWYG interface (#3263)

* Add file library integration for WYSIWYG interface

* Fix typing; Prevent selecting non-image on image field

* Allow imageToken in the interface; addTokenToURL() replace token if presents

* Add file library integration for WYSIWYG interface

* Fix typing; Prevent selecting non-image on image field

* Allow imageToken in the interface; addTokenToURL() replace token if presents

Co-authored-by: Nitwel <nitwel@arcor.de>
This commit is contained in:
Adrian Dimitrov
2021-02-15 17:15:11 +02:00
committed by GitHub
parent a589b9553c
commit e4bf978655
3 changed files with 82 additions and 2 deletions

View File

@@ -272,5 +272,15 @@ export default defineInterface(({ i18n }) => ({
},
},
},
{
field: 'imageToken',
name: i18n.t('interfaces.markdown.imageToken'),
type: 'string',
meta: {
note: i18n.t('interfaces.markdown.imageToken_label'),
width: 'full',
interface: 'text-input',
},
},
],
}));

View File

@@ -175,7 +175,11 @@ body.dark .tox .tox-toolbar__overflow {
/* Modal */
.tox .tox-dialog-wrap__backdrop {
background-color: rgba(0, 0, 0, 0.75);
background-color: var(--v-overlay-color);
}
.tox.tox-tinymce-aux {
z-index: 400;
}
.tox .tox-dialog {

View File

@@ -8,6 +8,17 @@
@onFocusIn="setFocus(true)"
@onFocusOut="setFocus(false)"
/>
<v-dialog :active="_imageDialogOpen" @toggle="unsetImageUploadHandler" @esc="unsetImageUploadHandler">
<v-card>
<v-card-title>{{ $t('upload_from_device') }}</v-card-title>
<v-card-text>
<v-upload @input="onImageUpload" :multiple="false" from-library from-url />
</v-card-text>
<v-card-actions>
<v-button @click="unsetImageUploadHandler" secondary>{{ $t('cancel') }}</v-button>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
@@ -37,6 +48,9 @@ import Editor from '@tinymce/tinymce-vue';
import getEditorStyles from './get-editor-styles';
import { getPublicURL } from '@/utils/get-root-path';
import { addTokenToURL } from '@/api';
type CustomFormat = {
title: string;
inline: string;
@@ -89,9 +103,16 @@ export default defineComponent({
type: Boolean,
default: true,
},
imageToken: {
type: String,
default: undefined,
},
},
setup(props, { emit }) {
const editorElement = ref<Vue | null>(null);
const imageUploadHandler = ref<CallableFunction | null>(null);
const _imageDialogOpen = computed(() => !!imageUploadHandler.value);
const _value = computed({
get() {
@@ -131,11 +152,56 @@ export default defineComponent({
extended_valid_elements: 'audio[loop],source',
toolbar: toolbarString,
style_formats: styleFormats,
file_picker_types: 'image media',
file_picker_callback: setImageUploadHandler,
urlconverter_callback: urlConverter,
...(props.tinymceOverrides || {}),
};
});
return { editorElement, editorOptions, _value, setFocus };
return {
editorElement,
editorOptions,
_value,
setFocus,
onImageUpload,
unsetImageUploadHandler,
_imageDialogOpen,
};
function onImageUpload(file: Record<string, any>) {
if (imageUploadHandler.value) imageUploadHandler.value(file);
unsetImageUploadHandler();
}
function setImageUploadHandler(cb: CallableFunction, value: any, meta: Record<string, any>) {
imageUploadHandler.value = (result: Record<string, any>) => {
if (meta.filetype === 'image' && !/^image\//.test(result.type)) return;
const imageUrl = getPublicURL() + 'assets/' + result.id;
cb(imageUrl, {
alt: result.title,
title: result.title,
width: (result.width || '').toString(),
height: (result.height || '').toString(),
});
};
}
function urlConverter(url: string, node: string) {
if (url && props.imageToken && ['img', 'source', 'poster', 'audio'].includes(node)) {
const baseUrl = getPublicURL() + 'assets/';
if (url.includes(baseUrl)) {
url = addTokenToURL(url, props.imageToken);
}
}
return url;
}
function unsetImageUploadHandler() {
imageUploadHandler.value = null;
}
function setFocus(val: boolean) {
if (editorElement.value == null) return;