Я пытаюсь интегрировать CKeditor в пользовательский редактор документов, с помощью модуля ckeditor5-реагировать было легко интегрировать состояние в данные, однако поведение по умолчанию для вставки изображения нежелательно в моем конкретном случае использования. У меня есть библиотека изображений, встроенная в мое приложение, поэтому я хотел бы, чтобы кнопка CKeditor открыла мой компонент библиотеки изображений, чтобы пользователь мог выбрать изображение из библиотеки, а затем вставить это изображение в том месте, где находится курсор.
После попыток добиться этого с помощью обычной готовой сборки ClassicEditor, я понял, что это невозможно, поэтому я создал собственный плагин, который должен достичь того, чего я хочу:
import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Autoformat from '@ckeditor/ckeditor5-autoformat/src/autoformat';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
import Heading from '@ckeditor/ckeditor5-heading/src/heading';
import Image from '@ckeditor/ckeditor5-image/src/image';
import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle';
import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar';
import ImageUpload from '@ckeditor/ckeditor5-image/src/imageupload';
import Link from '@ckeditor/ckeditor5-link/src/link';
import List from '@ckeditor/ckeditor5-list/src/list';
import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice';
import Table from '@ckeditor/ckeditor5-table/src/table';
import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
import mix from '@ckeditor/ckeditor5-utils/src/mix';
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
export default class ClassicEditor extends ClassicEditorBase {}
class InsertImage extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add( 'insertImage', locale => {
const view = new ButtonView( locale );
view.set( {
label: 'Insert image',
icon: imageIcon,
tooltip: true,
} );
// set observables on editor
editor.set( {
insertImageRequested: false,
imageFileRequested: null
} );
// Callback executed once the image button is clicked.
view.on( 'execute', () => {
// set observable to indicate a request to insert image has been made...
editor.set( { insertImageRequested: true } );
});
// Now this waits for the user to have selected a file URL which should show up
// in the imageFileRequested observable
editor.on( 'change:imageFileRequested', (evt, newShizz, oldShizz) => {
// // Which then injects the image into the body...
const imageUrl = editor.imageFileRequested;
editor.model.change( writer => {
const imageElement = writer.createElement( 'image', {
src: imageUrl
} );
// Insert the image in the current selection location.
editor.model.insertContent( imageElement, editor.model.document.selection );
});
// and unsets our observables back to their original state
editor.set( {
insertImageRequested: false,
imageFileRequested: null
} );
} )
return view;
} );
}
}
mix( InsertImage, ObservableMixin );
// Plugins to include in the build.
ClassicEditor.builtinPlugins = [
Essentials,
Autoformat,
Bold,
Italic,
BlockQuote,
EasyImage,
Heading,
Image,
ImageStyle,
ImageToolbar,
ImageUpload,
Link,
List,
MediaEmbed,
Paragraph,
PasteFromOffice,
Table,
TableToolbar,
InsertImage
];
// Editor configuration.
ClassicEditor.defaultConfig = {
toolbar: {
items: [
'heading',
'|',
'bold',
'italic',
'link',
'bulletedList',
'numberedList',
'blockQuote',
'insertTable',
'mediaEmbed',
'undo',
'redo',
'|',
'InsertImage'
]
},
image: {
toolbar: [
'imageStyle:full',
'imageStyle:side',
'|',
'imageTextAlternative'
]
},
table: {
contentToolbar: [
'tableColumn',
'tableRow',
'mergeTableCells'
]
},
// This value must be kept in sync with the language defined in webpack.config.js.
language: 'en'
};
Из приведенного выше кода вы можете видеть, что он использует функцию наблюдаемых событий CKeditor5 для установки 2 наблюдаемых: insertImageRequested (bool) или imageFileRequested (null или string).
Теперь в моем компоненте реагирования я получаю событие изменения «insertImageRequested» и обновляю свое состояние, чтобы библиотека открывалась, и мой компонент делает это нормально (однако курсор перемещается в начало документа, что мне не нужно). Проблема в том, что я просто не могу заставить редактор установить второй Observable "imageFileRequested" снаружи редактора с URL-адресом выбранных изображений, поэтому я не могу отправить строку fileURL обратно в пользовательский редактор, чтобы установить Observable
import React from 'react';
import PropTypes from 'prop-types';
...
import CKEditor from '@ckeditor/ckeditor5-react';
//import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import CustomEditor from './customCKeditor/ckeditor.js';
import styles from './articleEditor.scss';
class ArticleEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
insertImageRequested: false,
imageFileRequested: null,
eventLogData: null
}
...
this.imageClick = this.imageClick.bind(this);
}
...
imageClick(){
// Temp Data to be passed to custom Ckeditor
let tempURL = "https://via.placeholder.com/350x150";
this.setState({
insertImageRequested: false,
imageFileRequested: tempURL
})
// HOW DO I PASS 'tempURL' variable to editor so that I can set it as the value for the imageFileRequested observable (so CKeditor on change event is triggered as shown above)?
}
render() {
return(
<div className={styles.container}>
<CKEditor
editor={ CustomEditor }
data={this.state.articleBody}
name={'articleBody'}
config={{
toolbar: [ 'InsertImage', '|', 'heading', '|', 'bold', 'italic', '|', 'link', 'bulletedList', 'numberedList', 'blockQuote', '|', "mediaEmbed", '|', 'undo', 'redo', '|', "insertTable", "tableColumn", "tableRow", "mergeTableCells", '|' ],
heading: {
options: [
{ model: 'paragraph', title: 'Paragraph' },
{ model: 'heading1', view: 'h1', title: 'Heading 1'},
{ model: 'heading2', view: 'h2', title: 'Heading 2'},
{ model: 'heading3', view: 'h3', title: 'Heading 3'},
{ model: 'heading4', view: 'h4', title: 'Heading 4'},
{ model: 'heading5', view: 'h5', title: 'Heading 5'},
{ model: 'heading6', view: 'h6', title: 'Heading 6'}
]
}
}}
onInit={ editor => {
// A request for a new image has been made
editor.on( 'change:insertImageRequested', (evt, newShizz, oldShizz) => {
//reflect that in the react app state
this.setState({
insertImageRequested: true
})
} )
} }
onChange={ ( event, editor ) => {
console.log(event);
console.log(editor);
//console.log(editor.insertImageRequested);
const data = editor.getData();
console.log( { event, editor, data } );
this.updateArticleBodyState(data);
this.eventLogger(editor);
// THIS DOES NOT WORK
// //if(editor.imageFileRequested != null){
// console.log("imagefilerequested is defined!")
// editor.set( { imageFileRequested: this.state.imageFileRequested } );
// //}
} }
onBlur={ editor => {
//console.log( 'Blur.', editor );
} }
onFocus={ editor => {
//console.log( 'Focus.', editor );
} }
/>
</div>
);
}
}
...
После 2 дней борьбы с этим, когда решение перешло от относительно простого к ненужному сложному, мне пришло в голову, что то, как я это делаю, скорее всего, неверно, и весь мой подход был неправильным, поэтому я предполагаю, что мой вопрос в два раза. Во-первых, приведенный выше подход к тому, что я делаю правильно?
Если это так, вы можете помочь мне установить переменную tempUrl как наблюдаемую в ckeditor.
Во-вторых, если это совершенно неправильный подход, пожалуйста, помогите мне найти правильный путь для реализации этого. Я рад предоставить больше информации, если это необходимо, однако этот пост стал очень длинным, поэтому я закончу, заранее благодарю за любую помощь, которую вы можете предоставить.