Как подключить пользовательский селектор изображений к CKeditor5 вставить изображение в реагировать JS - PullRequest
0 голосов
/ 11 марта 2019

Я пытаюсь интегрировать 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. Во-вторых, если это совершенно неправильный подход, пожалуйста, помогите мне найти правильный путь для реализации этого. Я рад предоставить больше информации, если это необходимо, однако этот пост стал очень длинным, поэтому я закончу, заранее благодарю за любую помощь, которую вы можете предоставить.

1 Ответ

0 голосов
/ 13 марта 2019

Я выбрал Quill и React-quill в конце.Он не требует изменений в веб-пакете и имеет подходящий API для моих скромных нужд.

...