/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /* global window */ import Babel from 'babel-standalone'; // Imports necessary to run a React application. import React from 'react'; import ReactDOM from 'react-dom'; // The official component for React. import CKEditor from '@ckeditor/ckeditor5-react'; // The base editor class and features required to run the editor. import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials'; import Heading from '@ckeditor/ckeditor5-heading/src/heading'; import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'; import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic'; import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline'; import Link from '@ckeditor/ckeditor5-link/src/link'; import Table from '@ckeditor/ckeditor5-table/src/table'; import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import Command from '@ckeditor/ckeditor5-core/src/command'; import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils'; import Widget from '@ckeditor/ckeditor5-widget/src/widget'; // ckeditor/productpreviewediting.js class ProductPreviewEditing extends Plugin { static get requires() { return [ Widget ]; } init() { this._defineSchema(); this._defineConverters(); this.editor.commands.add( 'insertProduct', new InsertProductPreviewCommand( this.editor ) ); } _defineSchema() { const schema = this.editor.model.schema; schema.register( 'productPreview', { // Behaves like a self-contained object (e.g. an image). isObject: true, // Allow in places where other blocks are allowed (e.g. directly in the root). allowWhere: '$block', // Each product preview has an ID. A unique ID tells the application which // product it represents and makes it possible to render it inside a widget. allowAttributes: [ 'id' ] } ); } _defineConverters() { const editor = this.editor; const conversion = editor.conversion; const renderProduct = editor.config.get( 'products' ).productRenderer; // converters ((data) view → model) conversion.for( 'upcast' ).elementToElement( { view: { name: 'section', classes: 'product' }, model: ( viewElement, modelWriter ) => { // Read the "data-id" attribute from the view and set it as the "id" in the model. return modelWriter.createElement( 'productPreview', { id: parseInt( viewElement.getAttribute( 'data-id' ) ) } ); } } ); // converters (model → data view) conversion.for( 'dataDowncast' ).elementToElement( { model: 'productPreview', view: ( modelElement, viewWriter ) => { // In the data view, the model corresponds to: // //
return viewWriter.createEmptyElement( 'section', { class: 'product', 'data-id': modelElement.getAttribute( 'id' ) } ); } } ); // converters (model → editing view) conversion.for( 'editingDowncast' ).elementToElement( { model: 'productPreview', view: ( modelElement, viewWriter ) => { // In the editing view, the model corresponds to: // //
//
// (React component) //
//
const id = modelElement.getAttribute( 'id' ); // The outermost
element. const section = viewWriter.createContainerElement( 'section', { class: 'product', 'data-id': id } ); // The inner
element. // This element will host a React component. const reactWrapper = viewWriter.createRawElement( 'div', { class: 'product__react-wrapper' }, function( domElement ) { // This the place where React renders the actual product preview hosted // by a UIElement in the view. you are using a function (renderer) passed as // editor.config.products#productRenderer. renderProduct( id, domElement ); } ); viewWriter.insert( viewWriter.createPositionAt( section, 0 ), reactWrapper ); return toWidget( section, viewWriter, { label: 'product preview widget' } ); } } ); } } // ckeditor/insertproductpreviewcommand.js class InsertProductPreviewCommand extends Command { execute( id ) { this.editor.model.change( writer => { // Insert * at the current selection position // in a way which will result in creating a valid model structure. this.editor.model.insertContent( writer.createElement( 'productPreview', { id } ) ); } ); } refresh() { const model = this.editor.model; const selection = model.document.selection; const allowedIn = model.schema.findAllowedParent( selection.getFirstPosition(), 'productPreview' ); this.isEnabled = allowedIn !== null; } } Object.assign( window, { Babel, React, ReactDOM, CKEditor, ClassicEditor, Essentials, Heading, Bold, Italic, Underline, Link, Table, TableToolbar, Paragraph, ProductPreviewEditing, InsertProductPreviewCommand } );