Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #11 from ckeditor/t/10
Browse files Browse the repository at this point in the history
Other: Allowed the editable element to be passed into `DecoupledEditor.create()`. Removed `config.toolbarContainer` and `config.editableContainer`. Closes #10. Closes ckeditor/ckeditor5#912.

BREAKING CHANGE: The config options `config.toolbarContainer` and `config.editableContainer` have been removed. Please refer to the `DecoupledEditor` class API documentation to learn about possible methods of bootstrapping the UI.
  • Loading branch information
Reinmar committed Mar 29, 2018
2 parents 1659a93 + f0898b0 commit 327b2ed
Show file tree
Hide file tree
Showing 13 changed files with 450 additions and 393 deletions.
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,5 @@ after_success:
- ckeditor5-dev-tests-save-revision
env:
global:
- secure: yGjqvN+FNsbRuAneBgIi9AyraRbhCSm8MEd7FPq/exSTjfsWofOq5i9O7o7/02bJVZ8rveD71OaqAKQCWVG8BD4umbpu0N0PpkJbjVi8jmytzN28mUxgrmZkFbr96C5YnGqvdxf4AB9wT5ExtzTfp5XUO383/WoGPu9qt6rmb5II1PHwhT1WzXql/pCU5eGLccCYtlEr3ybWD9n3wiC4fM90M5axuSu9EwpvTEfMJQbeqEv7jSu+qlPUAJ4rSTmgt1pGIMxJgUpDi76qnn4VdTxRUZy+4heAFT9c/96tPV0QAz8cpVM3qGKSTuF8FAY71sddF4PX8wfG/1GjO+dHEmbop5Kz8UXnS8rnp9gx4Q5be8iW0DfPCqjc8a2ULWgUMr4jBnbZKpMHNT83Li1TfGflIiDksh68EKMRHHoVzxKJ2ysBSMzPqLfLXSyY9Jl/j5OBAiVl1n22oq5t9AlT6JEm0bYcgIwVIRpJqPMZMkZBCoLZHSyVfdIqdT+bcNgSEG5iLrXS9FlqsOS9FB4U3DbvPZbNBq+uoEuAyk5w+T5OkqIhXBddUG9emaa6TuQNNcATyucwWxB3HC1UAK8s5XTWVUzQ86nUShzH7aHJky+PJN0hkYAxwjldwtzTWwKdw+SBjk3Cl5h8JEepJSX0Lqtwci4XGoHsJ4JL5UCU5OM=
- secure: KDj0ramEp45sMIyRogmNesjH5O2q7k5kopvDjG5+O1PWjMc+moxoSLYdcqknINLdHkid/SfwyfDppO6rvsVuLR9nvq7SPsM/xDGtLMoLYwUoWPrK57tQ1exl1FUWHzZs/7UJOwC+GnqqtL6bN6PAAj4aSLoTai/xDiDBTRMrVzDmYGeHaHihI44jtqsfBEkCGd9vfB0JDZvhncZqQrEubTwFnh+QwZXTnVR837p+EWhT87HvH/OPioXPnEF3O3N72u/WpqmQKrgM2vUWOfjHzZTEleBBJZnbrFFKzoOczig2a4I0hLiaqtK/bsSpjMpyKVzW+Jh+bQiKLk2B3DlUL+/6RH/dioo0DM438dI5sT46dNDl8k3mXCfJyGpn1by3EKdjEkkgLz095YzaCIEKBLVqKky4FhqRduEGpmXXMJw5xJQuaLOqC8jg0bgAl/E6q0uMyVPJuaC1ewGOHSx33j09ZXrOCAk57KzXgJzVi3BJR2kf4x2KygRqqVtFVYjOyvdAS2EieytMKwMVfQfrtmgJU5KCcbYzNX6ZEKEdS7SxTlyPtDdJssxvuuRU8xxgkswBxTkFGFG77dy0BTsa0xmAdiJnb/Z3F9hJP3VySOKj/lnATO3Yzr5v5l0jD1SQqfG9IYvDE/fDULs7/cFuj46voHIVBkziQ+IAqv/3vYo=
- secure: Gx267GbmaD2jMWCirha+1wQ/A7kJnHMGIkzUUBgzsy8CszGfQv8WZe8KnSeiLjV/+/gXg9v+/0LPULDjs9e2d8FdpGdA+7iE3HPCXijR/LWCJq9ZiMN6QrCSaLI5o6mpkUB29sfgKOVfC6CEjyd00sK3LzcjSJeApSgPY90g1FOsbMg/t1PIzFUrJytvFnjUr2cOPay2ZeXrXFLox2je8n1YvtCmWt6QgMGmT0bBQRjYFHhiw7IsGZ9aT36Um4qx9hXADixIXfE+RRxhdrVcFhcmESfr7US9inZKMDGQa48VYUX8kdPr073bi9Wh8c5WsmiIT4cspviyLhWfHvq5hwhGt1FTbfcuQIeq172hFoLRphwWujENrzJnXjSbKIDFHqv08p0TJJLe/Q2af0imeWSL9+hszIN7Z7S12VYxbixAMmxaB9DXhqdVN7qhNKV4e/KhZv+60DLUPIrP7iLYNpb2TK1D9tr5rnF3v0FbJTCCr1XyCRbZSdGi9HnAm5bw7n5rllCW7NB6QUIPrBwBu7cJ37PqFeJXZDIXlmBEGP+ZBV4u7vIBBO8CXaaVCEvflIGqE15PbGvSc8wPiP4X4tLjnpgUY6OP67sCNRbuH0XeuvhGPRf3C15GNp14qM7cFtmoKSKxy+sUQBtx5LCpESI/JMEUiotqk232l0vqyR8=
- secure: eqj8DaCp7F6/XXy6npJ4YO7jfQaAbMBYo2rsvYAk0lubYSL0fJsbtMR1Qm8kbXFENxFKhpcU6fI9pAPKBT2fvn3/dnIHTng4xklpWWdl8CgojGJ8wPu/RjTa9UxDXvsDaiixipg/uPfc9oReCzEhulNVB6V6iq9jHeBJaQqgI5R1b3GB3U/sIF+Y7vWhICjM9s7WE/ESqADVHMpSQnzMIAiFJwJgqB1zu9bkwSsjhMgNscgifWuB8+AoXVeCesSYyJQsG7PHSoY+JlzGUw05xtSc6x+3bivs7dyrt68bkaFkVN2YmAstxl45R0Itd5dgPt/bvXKo63CTtkNAsyvUrK8Z3F2VqnqqTJ+3exQbkHCxHW2Pd7QOyxVAy00OCdIcjEAtSQp5HrI9ZAipP/9JJq5KzdaNzpSLwuL6jt9WysnMQBF3LMB1ONWRlrCcGdkUfTt2cjBMR3yiaaPJK+Bk93eSNPDJ9BaQbIGtsI6p2aojlgYzz7Uvon9d1lY9mlZi5BnyWwiOzfpmdALZlnl9yl6Bh/PI7SfW0zWRYOWS33pSWt9nkoNre9uTOdAhlJpS1PohNraEzw1AYR1j6hRZszqpf8JpMIrdoUOdf4QMh1LIJZW7EbEULsvU9fDzLZucjQyYpZLvFjXQ15UR+u4iTsC8c9rNGdDJkuBAsg81zis=
- secure: czAq3wmkippaXuAaoiibg+6vtcfF6ylU6QgzLIABhegKQaOARSB6Cae7WeveCBKu03aiVxWSTzPtAkNnq4qGK8mxEh012r1F50zKBInR9QiEB8HHkiUrkO2cVt21hX/PzdCJ+Ys4Cjthm1xsQwZ8vrfXsZnyahkQCzLxqkQR7mYNiQGPRaGvxaJmgMlfNNgoRFO7KzcmTNX9OeU+4k2BJOuua8NuoZx01aCktaC6qGpYLFE9Kj+oDpWMcQXp8y20hIDeY7GOh2zRDlQqXQV9FslxOykHE0uJ2dRa6Wvf65PTQknrpkBLHQgdaIM0e/E4+xtKMxl7676P1gbpQAntyCD8QybE3N+ldJqrOzzLmQ5hTxKjHxzU3/rtXqowRCtJS1a9/gV6ABbE3PKzfBi1fhkx7uyzc84owKTuR8VMTuXwPqjAuBbbD2Limv1GbS+Of6VPlaM7FGg22ZIjWr5H/kEIydvB9+9ffIOCjwi+5LObss1MiqZTn6tYwJvq6ELTj4a/6rXF4r2G4GLqPfheNxad3FXrTGsT2BYkrp8J1YiNwHVPnaec16S8tzRS7dQjKQiTinp/3wtOsdJ87XY+Z0wKFwv1nvn9wFWpTLo2Lh9a1grxW05HvrrKr0MIwlVdieHHsjtaBMYbwS1WtIoOMA1znsghM6ZPUVGzYX5mn5w=
- secure: VJkWZMdkuuvwdHMyhJdTiJ9jjAtuVOQn7sQjpBAPzfLyjE7DliaS0gYHz5KG8RLs1Qn99ShNMr6PXcuZ7tMVF4YMSLU4AHh4+wg+EYXxWDbiiz3AbSPa2ScRVQrqayEdvdHv77ydPSXRj7wY2wWzpSQml6zdPHNhLRi9czKxPmehEloZKZfBiJY9/CmeiIF/Fv1KK/OsuOgafyOOG/ffJeqiI4agYW8PgxrMRl71YITl9IOWDZw093x4AqlXFG8JUxYk0JqiSJDWJCSxID7A/12lOe/ExsPPRQ69EKXMsMxsVLKhNlHpo3GHMXO0bf461Y0Y/1exCbeKP+F2ILaBJOKxhPD6ulWLTBcB/58XV/ke8G8cwYyFKl3nNxcejK1bR8fE9/4q+vsjnCTNq4tJxRN1RW324uC7AbL8f/Mur5TIWUGftmjhFLU+bjKkuTADhbwB9XYU2KhmAN96fpEvQ4RIz4/nIs9dbfKY/Hj2YAyblfXeWLMPxmhOXTScR7dkw+TWDiU2gUCw2MjU8AyOhB3Iwenxgpe+jGObvt+zPZz8UWgyJYb+KtZsNlmr+vvISa5m+jAukOcY2afH0tH/mMvwdXntLH7A+X40BWrP/14cA609clyw5ou5Jaxo+XPVX0GAsR/B99NkJbUGLKUgGP4fWf3WfGra1WVZZ9PKmuo=
33 changes: 20 additions & 13 deletions docs/framework/guides/document-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,48 @@ order: 30

# Document editor

The {@link examples/builds/document-editor document editor example} showcases the {@link builds/guides/quick-start#document-editor document editor build} designed for document editing with a customized UI representing the layout of a sheet of paper. It was created on top of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor `DecoupledEditor`} and makes the best of what it offers: the freedom to choose the location of the crucial UI elements in the application.
The {@link examples/builds/document-editor document editor example} showcases the {@link builds/guides/quick-start#document-editor document editor build} designed for document editing with a customized UI representing the layout of a sheet of paper. It was created on top of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor `DecoupledEditor`} class and makes the best of what it offers: the freedom to choose the location of the crucial UI elements in the application.

In this tutorial you will learn how to create your own document editor with a customized user interface, step–by–step.

{@snippet examples/document-editor}

## The editor

The `DecoupledDocumentEditor` includes all the necessary features for the task. All you need to do is import it and create a new instance.
The document editor build includes all the necessary features for the task. All you need to do is import it and create a new instance.

<info-box>
See the {@link builds/guides/quick-start#document-editor quick start guide} to learn how to install the document editor build.
</info-box>

Unlike the {@link builds/guides/overview#classic-editor classic editor}, the document editor does not require any data container in the DOM. Instead, it accepts a string containing the initial data as the first argument of the static `create()` method. To get the output data, use the {@link module:core/editor/utils/dataapimixin~DataApi#getData `getData`} method.
The document editor can be created using the existing data container in the DOM. It can also accept a raw data string and create the editable by itself. To get the output data, use the {@link module:core/editor/utils/dataapimixin~DataApi#getData `getData()`} method.

<info-box>
See the {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} to learn about different approaches to the initialization of the editor.
</info-box>

```js
import DecoupledDocumentEditor from '@ckeditor/ckeditor5-build-decoupled-document/src/ckeditor';

DecoupledDocumentEditor
.create( '<p>Initial editor data.</p>', {
toolbarContainer: document.querySelector( '.document-editor__toolbar' ),
editableContainer: document.querySelector( '.document-editor__editable' ),

.create( document.querySelector( '.document-editor__editable' ), {
cloudServices: {
....
}
} )
.then( editor => {
const toolbarContainer = document.querySelector( '.document-editor__toolbar' );

toolbarContainer.appendChild( editor.ui.view.toolbar.element );

window.editor = editor;
} )
.catch( err => {
console.error( err );
} );
```

You may have noticed two configuration options used here: {@link module:core/editor/editorconfig~EditorConfig#toolbarContainer `config.toolbarContainer`} and {@link module:core/editor/editorconfig~EditorConfig#editableContainer `config.editableContainer`}. They specify the location of the editor toolbar and editable in your application.

If you do not specify these configuration options, you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorwithui~EditorWithUI#event:uiReady `uiReady`} event. The toolbar element is accessible via `editor.ui.view.toolbar.element` and the editable element can be found under `editor.ui.view.editable.element`.
You may have noticed that you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorwithui~EditorWithUI#event:uiReady `Editor#uiReady`} event. The toolbar element can be found under `editor.ui.view.toolbar.element`.

<info-box>
The document editor supports the Easy Image plugin provided by [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services/) out of the box. Please refer to the {@link features/image-upload#easy-image documentation} to learn more.
Expand All @@ -60,7 +63,11 @@ The following structure has two containers that correspond to the configuration
```html
<div class="document-editor">
<div class="document-editor__toolbar"></div>
<div class="document-editor__editable"></div>
<div class="document-editor__editable-container">
<div class="document-editor__editable">
<p>The initial editor data.</p>
</div>
</div>
</div>
```

Expand Down Expand Up @@ -114,15 +121,15 @@ The editable should look like a sheet of paper, centered in its scrollable conta

```css
/* Make the editable container look like the inside of a native word processor application. */
.document-editor__editable {
.document-editor__editable-container {
padding: calc( 2 * var(--ck-spacing-large) );
background: var(--ck-color-base-foreground);

/* Make it possible to scroll the "page" of the edited content. */
overflow-y: scroll;
}

.document-editor__editable .ck-editor__editable {
.document-editor__editable-container .ck-editor__editable {
/* Set the dimensions of the "page". */
width: 15.8cm;
min-height: 21cm;
Expand Down
154 changes: 73 additions & 81 deletions src/decouplededitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin
import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
import DecoupledEditorUI from './decouplededitorui';
import DecoupledEditorUIView from './decouplededitoruiview';
import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
import setDataInElement from '@ckeditor/ckeditor5-utils/src/dom/setdatainelement';
import mix from '@ckeditor/ckeditor5-utils/src/mix';
import isElement from '@ckeditor/ckeditor5-utils/src/lib/lodash/isElement';

/**
* The {@glink builds/guides/overview#decoupled-editor decoupled editor} implementation.
Expand All @@ -28,11 +31,11 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
* In order to create a decoupled editor instance, use the static
* {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} method.
*
* # Decoupled editor and document build
* # Decoupled editor and document editor build
*
* The decoupled editor can be used directly from source (if you installed the
* [`@ckeditor/ckeditor5-editor-decoupled`](https://www.npmjs.com/package/@ckeditor/ckeditor5-editor-decoupled) package)
* but it is also available in the {@glink builds/guides/overview#document-editor document build}.
* but it is also available in the {@glink builds/guides/overview#document-editor document editor build}.
*
* {@glink builds/guides/overview Builds} are ready-to-use editors with plugins bundled in. When using the editor from
* source you need to take care of loading all plugins by yourself
Expand All @@ -54,44 +57,80 @@ export default class DecoupledEditor extends Editor {
* {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`} method instead.
*
* @protected
* @param {String} data The data to be loaded into the editor.
* @param {HTMLElement|String} elementOrData The DOM element that serves as an editable.
* The data will be loaded from it and loaded back to it once the editor is destroyed.
* Alternatively, a data string to be loaded into the editor.
* @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
*/
constructor( config ) {
constructor( elementOrData, config ) {
super( config );

if ( isElement( elementOrData ) ) {
/**
* The element used as an editable. The data will be loaded from it and loaded back to
* it once the editor is destroyed.
*
* **Note:** The property is available only when such element has been passed
* to the {@link #constructor}.
*
* @readonly
* @member {HTMLElement}
*/
this.element = elementOrData;
}

this.data.processor = new HtmlDataProcessor();

this.model.document.createRoot();

this.ui = new DecoupledEditorUI( this, new DecoupledEditorUIView( this.locale ) );
this.ui = new DecoupledEditorUI( this, new DecoupledEditorUIView( this.locale, this.element ) );
}

/**
* Destroys the editor instance, releasing all resources used by it.
*
* **Note**: The decoupled editor does not remove the toolbar and editable when destroyed. You can
* do that yourself in the destruction chain:
*
* editor.destroy()
* .then( () => {
* // Remove the toolbar from DOM.
* editor.ui.view.toolbar.element.remove();
*
* // Remove the editable from DOM.
* editor.ui.view.editable.element.remove();
*
* console.log( 'Editor was destroyed' );
* } );
*
* @returns {Promise}
*/
destroy() {
// Cache the data, then destroy.
// It's safe to assume that the model->view conversion will not work after super.destroy().
const data = this.getData();

this.ui.destroy();

return super.destroy();
return super.destroy()
.then( () => {
if ( this.element ) {
setDataInElement( this.element, data );
}
} );
}

/**
* Creates a decoupled editor instance.
*
* Creating an instance when using the {@glink builds/index CKEditor build}:
* Creating an instance when using the {@glink builds/index CKEditor 5 build}:
*
* DecoupledEditor
* .create( '<p>Editor data</p>', {
* // The location of the toolbar in the DOM.
* toolbarContainer: document.querySelector( 'body div.toolbar-container' ),
*
* // The location of the editable in the DOM.
* editableContainer: document.querySelector( 'body div.editable-container' )
* } )
* .create( document.querySelector( '#editor' ) )
* .then( editor => {
* // Append the toolbar to the <body> element.
* document.body.appendChild( editor.ui.view.toolbar.element );
*
* console.log( 'Editor was initialized', editor );
* } )
* .catch( err => {
Expand All @@ -107,57 +146,58 @@ export default class DecoupledEditor extends Editor {
* import ...
*
* DecoupledEditor
* .create( '<p>Editor data</p>', {
* .create( document.querySelector( '#editor' ), {
* plugins: [ Essentials, Bold, Italic, ... ],
* toolbar: [ 'bold', 'italic', ... ],
*
* // The location of the toolbar in the DOM.
* toolbarContainer: document.querySelector( 'div.toolbar-container' ),
*
* // The location of the editable in the DOM.
* editableContainer: document.querySelector( 'div.editable-container' )
* toolbar: [ 'bold', 'italic', ... ]
* } )
* .then( editor => {
* // Append the toolbar to the <body> element.
* document.body.appendChild( editor.ui.view.toolbar.element );
*
* console.log( 'Editor was initialized', editor );
* } )
* .catch( err => {
* console.error( err.stack );
* } );
*
* **Note**: The {@link module:core/editor/editorconfig~EditorConfig#toolbarContainer `config.toolbarContainer`} and
* {@link module:core/editor/editorconfig~EditorConfig#editableContainer `config.editableContainer`} settings are optional.
* It is possible to define the location of the UI elements manually once the editor is up and running:
* **Note**: It is possible to create the editor out of a pure data string. The editor will then render
* an editable element that must be inserted into the DOM for the editor to work properly:
*
* DecoupledEditor
* .create( '<p>Editor data</p>' )
* .then( editor => {
* console.log( 'Editor was initialized', editor );
*
* // Append the toolbar and editable straight into the <body> element.
* // Append the toolbar to the <body> element.
* document.body.appendChild( editor.ui.view.toolbar.element );
*
* // Append the editable to the <body> element.
* document.body.appendChild( editor.ui.view.editable.element );
*
* console.log( 'Editor was initialized', editor );
* } )
* .catch( err => {
* console.error( err.stack );
* } );
*
* @param {String} data The data to be loaded into the editor.
* @param {HTMLElement|String} elementOrData The DOM element that serves as an editable.
* The data will be loaded from it and loaded back to it once the editor is destroyed.
* Alternatively, a data string to be loaded into the editor.
* @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
* @returns {Promise} A promise resolved once the editor is ready.
* The promise returns the created {@link module:editor-decoupled/decouplededitor~DecoupledEditor} instance.
*/
static create( data, config ) {
static create( elementOrData, config ) {
return new Promise( resolve => {
const editor = new this( config );
const editor = new this( elementOrData, config );

resolve(
editor.initPlugins()
.then( () => {
editor.ui.init();
editor.fire( 'uiReady' );
} )
.then( () => editor.editing.view.attachDomRoot( editor.ui.view.editableElement ) )
.then( () => editor.data.init( data ) )
.then( () => {
editor.data.init( editor.element ? getDataFromElement( editor.element ) : elementOrData );
} )
.then( () => {
editor.fire( 'dataReady' );
editor.fire( 'ready' );
Expand All @@ -169,51 +209,3 @@ export default class DecoupledEditor extends Editor {
}

mix( DecoupledEditor, DataApiMixin );

/**
* The configuration of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor}.
*
* When specified, it controls the location of the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#toolbar}:
*
* DecoupledEditor
* .create( '<p>Hello world!</p>', {
* // Append the toolbar to the <body> element.
* toolbarContainer: document.body
* } )
* .then( editor => {
* console.log( editor );
* } )
* .catch( error => {
* console.error( error );
* } );
*
* **Note**: If not specified, the toolbar must be manually injected into the DOM. See
* {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`}
* to learn more.
*
* @member {HTMLElement} module:core/editor/editorconfig~EditorConfig#toolbarContainer
*/

/**
* The configuration of the {@link module:editor-decoupled/decouplededitor~DecoupledEditor}.
*
* When specified, it controls the location of the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#editable}:
*
* DecoupledEditor
* .create( '<p>Hello world!</p>', {
* // Append the editable to the <body> element.
* editableContainer: document.body
* } )
* .then( editor => {
* console.log( editor );
* } )
* .catch( error => {
* console.error( error );
* } );
*
* **Note**: If not specified, the editable must be manually injected into the DOM. See
* {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`}
* to learn more.
*
* @member {HTMLElement} module:core/editor/editorconfig~EditorConfig#editableContainer
*/
Loading

0 comments on commit 327b2ed

Please sign in to comment.