using-react-in-widget.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global window */
  6. import Babel from 'babel-standalone';
  7. // Imports necessary to run a React application.
  8. import React from 'react';
  9. import ReactDOM from 'react-dom';
  10. // The official <CKEditor> component for React.
  11. import CKEditor from '@ckeditor/ckeditor5-react';
  12. // The base editor class and features required to run the editor.
  13. import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
  14. import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
  15. import Heading from '@ckeditor/ckeditor5-heading/src/heading';
  16. import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
  17. import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
  18. import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
  19. import Link from '@ckeditor/ckeditor5-link/src/link';
  20. import Table from '@ckeditor/ckeditor5-table/src/table';
  21. import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
  22. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  23. import Command from '@ckeditor/ckeditor5-core/src/command';
  24. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  25. import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
  26. import Widget from '@ckeditor/ckeditor5-widget/src/widget';
  27. // ckeditor/productpreviewediting.js
  28. class ProductPreviewEditing extends Plugin {
  29. static get requires() {
  30. return [ Widget ];
  31. }
  32. init() {
  33. this._defineSchema();
  34. this._defineConverters();
  35. this.editor.commands.add( 'insertProduct', new InsertProductPreviewCommand( this.editor ) );
  36. }
  37. _defineSchema() {
  38. const schema = this.editor.model.schema;
  39. schema.register( 'productPreview', {
  40. // Behaves like a self-contained object (e.g. an image).
  41. isObject: true,
  42. // Allow in places where other blocks are allowed (e.g. directly in the root).
  43. allowWhere: '$block',
  44. // Each product preview has an ID. A unique ID tells the application which
  45. // product it represents and makes it possible to render it inside a widget.
  46. allowAttributes: [ 'id' ]
  47. } );
  48. }
  49. _defineConverters() {
  50. const editor = this.editor;
  51. const conversion = editor.conversion;
  52. const renderProduct = editor.config.get( 'products' ).productRenderer;
  53. // <productPreview> converters ((data) view → model)
  54. conversion.for( 'upcast' ).elementToElement( {
  55. view: {
  56. name: 'section',
  57. classes: 'product'
  58. },
  59. model: ( viewElement, modelWriter ) => {
  60. // Read the "data-id" attribute from the view and set it as the "id" in the model.
  61. return modelWriter.createElement( 'productPreview', {
  62. id: parseInt( viewElement.getAttribute( 'data-id' ) )
  63. } );
  64. }
  65. } );
  66. // <productPreview> converters (model → data view)
  67. conversion.for( 'dataDowncast' ).elementToElement( {
  68. model: 'productPreview',
  69. view: ( modelElement, viewWriter ) => {
  70. // In the data view, the model <productPreview> corresponds to:
  71. //
  72. // <section class="product" data-id="..."></section>
  73. return viewWriter.createEmptyElement( 'section', {
  74. class: 'product',
  75. 'data-id': modelElement.getAttribute( 'id' )
  76. } );
  77. }
  78. } );
  79. // <productPreview> converters (model → editing view)
  80. conversion.for( 'editingDowncast' ).elementToElement( {
  81. model: 'productPreview',
  82. view: ( modelElement, viewWriter ) => {
  83. // In the editing view, the model <productPreview> corresponds to:
  84. //
  85. // <section class="product" data-id="...">
  86. // <div class="product__react-wrapper">
  87. // <ProductPreview /> (React component)
  88. // </div>
  89. // </section>
  90. const id = modelElement.getAttribute( 'id' );
  91. // The outermost <section class="product" data-id="..."></section> element.
  92. const section = viewWriter.createContainerElement( 'section', {
  93. class: 'product',
  94. 'data-id': id
  95. } );
  96. // The inner <div class="product__react-wrapper"></div> element.
  97. // This element will host a React <ProductPreview /> component.
  98. const reactWrapper = viewWriter.createRawElement( 'div', {
  99. class: 'product__react-wrapper'
  100. }, function( domElement ) {
  101. // This the place where React renders the actual product preview hosted
  102. // by a UIElement in the view. you are using a function (renderer) passed as
  103. // editor.config.products#productRenderer.
  104. renderProduct( id, domElement );
  105. } );
  106. viewWriter.insert( viewWriter.createPositionAt( section, 0 ), reactWrapper );
  107. return toWidget( section, viewWriter, { label: 'product preview widget' } );
  108. }
  109. } );
  110. }
  111. }
  112. // ckeditor/insertproductpreviewcommand.js
  113. class InsertProductPreviewCommand extends Command {
  114. execute( id ) {
  115. this.editor.model.change( writer => {
  116. // Insert <productPreview id="...">*</productPreview> at the current selection position
  117. // in a way which will result in creating a valid model structure.
  118. this.editor.model.insertContent( writer.createElement( 'productPreview', { id } ) );
  119. } );
  120. }
  121. refresh() {
  122. const model = this.editor.model;
  123. const selection = model.document.selection;
  124. const allowedIn = model.schema.findAllowedParent( selection.getFirstPosition(), 'productPreview' );
  125. this.isEnabled = allowedIn !== null;
  126. }
  127. }
  128. Object.assign( window, {
  129. Babel,
  130. React,
  131. ReactDOM,
  132. CKEditor,
  133. ClassicEditor,
  134. Essentials,
  135. Heading,
  136. Bold,
  137. Italic,
  138. Underline,
  139. Link,
  140. Table,
  141. TableToolbar,
  142. Paragraph,
  143. ProductPreviewEditing,
  144. InsertProductPreviewCommand
  145. } );