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

Commit

Permalink
Merge 984e948 into 1659a93
Browse files Browse the repository at this point in the history
  • Loading branch information
oleq committed Mar 28, 2018
2 parents 1659a93 + 984e948 commit d136e75
Show file tree
Hide file tree
Showing 12 changed files with 439 additions and 379 deletions.
31 changes: 19 additions & 12 deletions docs/framework/guides/document-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,34 @@ The `DecoupledDocumentEditor` includes all the necessary features for the task.
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 @@ -58,9 +61,13 @@ The code you have just created will run the editor but still, the user interface
The following structure has two containers that correspond to the configuration you have just used. The editor will inject the toolbar and editable into respective containers as it starts.

```html
<div class="document-editor">
<div class="document-editor ck-rounded-corners">
<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
148 changes: 70 additions & 78 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 Down Expand Up @@ -54,28 +57,67 @@ 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 );
}
} );
}

/**
Expand All @@ -84,14 +126,11 @@ export default class DecoupledEditor extends Editor {
* Creating an instance when using the {@glink builds/index CKEditor 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
*/
27 changes: 2 additions & 25 deletions src/decouplededitorui.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,6 @@ export default class DecoupledEditorUI {
* @private
*/
this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) );

/**
* The container for the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#toolbar}.
*
* @type {HTMLElement|String}
* @private
*/
this._toolbarContainer = editor.config.get( 'toolbarContainer' );

/**
* The container for the {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#editable}.
*
* @type {HTMLElement|String}
* @private
*/
this._editableContainer = editor.config.get( 'editableContainer' );
}

/**
Expand All @@ -83,19 +67,12 @@ export default class DecoupledEditorUI {
const editingRoot = editor.editing.view.document.getRoot();
view.editable.bind( 'isReadOnly' ).to( editingRoot );
view.editable.bind( 'isFocused' ).to( editor.editing.view.document );
editor.editing.view.attachDomRoot( view.editableElement );
view.editable.name = editingRoot.rootName;

this.focusTracker.add( this.view.editableElement );
this.view.toolbar.fillFromConfig( this._toolbarConfig.items, this.componentFactory );

if ( this._toolbarContainer ) {
this._toolbarContainer.appendChild( view.toolbar.element );
}

if ( this._editableContainer ) {
this._editableContainer.appendChild( view.editable.element );
}

enableToolbarKeyboardFocus( {
origin: editor.editing.view,
originFocusTracker: this.focusTracker,
Expand All @@ -108,6 +85,6 @@ export default class DecoupledEditorUI {
* Destroys the UI.
*/
destroy() {
this.view.destroy( !!this._toolbarContainer, !!this._editableContainer );
this.view.destroy();
}
}

0 comments on commit d136e75

Please sign in to comment.