diff --git a/src/classiceditor.js b/src/classiceditor.js index e7c9aca..aff5389 100644 --- a/src/classiceditor.js +++ b/src/classiceditor.js @@ -69,7 +69,7 @@ export default class ClassicEditor extends Editor { this.model.document.createRoot(); - this.ui = new ClassicEditorUI( this, new ClassicEditorUIView( this.locale ) ); + this.ui = new ClassicEditorUI( this, new ClassicEditorUIView( this.locale, this.editing.view ) ); attachToForm( this ); } @@ -173,7 +173,6 @@ export default class ClassicEditor extends Editor { resolve( editor.initPlugins() .then( () => editor.ui.init( isElement( sourceElementOrData ) ? sourceElementOrData : null ) ) - .then( () => editor.editing.view.attachDomRoot( editor.ui.getEditableElement() ) ) .then( () => { const initialData = isElement( sourceElementOrData ) ? getDataFromElement( sourceElementOrData ) : diff --git a/src/classiceditorui.js b/src/classiceditorui.js index 9511dd6..44c1dc3 100644 --- a/src/classiceditorui.js +++ b/src/classiceditorui.js @@ -10,6 +10,7 @@ import EditorUI from '@ckeditor/ckeditor5-core/src/editor/editorui'; import enableToolbarKeyboardFocus from '@ckeditor/ckeditor5-ui/src/toolbar/enabletoolbarkeyboardfocus'; import normalizeToolbarConfig from '@ckeditor/ckeditor5-ui/src/toolbar/normalizetoolbarconfig'; +import { enablePlaceholder } from '@ckeditor/ckeditor5-engine/src/view/placeholder'; import ElementReplacer from '@ckeditor/ckeditor5-utils/src/elementreplacer'; /** @@ -67,9 +68,78 @@ export default class ClassicEditorUI extends EditorUI { init( replacementElement ) { const editor = this.editor; const view = this.view; + const editingView = editor.editing.view; + const editable = view.editable; + const editingRoot = editingView.document.getRoot(); + + // The editable UI and editing root should share the same name. Then name is used + // to recognize the particular editable, for instance in ARIA attributes. + editable.name = editingRoot.rootName; view.render(); + // The editable UI element in DOM is available for sure only after the editor UI view has been rendered. + // But it can be available earlier if a DOM element has been passed to BalloonEditor.create(). + const editableElement = editable.element; + + // Register the editable UI view in the editor. A single editor instance can aggregate multiple + // editable areas (roots) but the classic editor has only one. + this._editableElements.set( editable.name, editableElement ); + + // Let the global focus tracker know that the editable UI element is focusable and + // belongs to the editor. From now on, the focus tracker will sustain the editor focus + // as long as the editable is focused (e.g. the user is typing). + this.focusTracker.add( editableElement ); + + // Let the editable UI element respond to the changes in the global editor focus + // tracker. It has been added to the same tracker a few lines above but, in reality, there are + // many focusable areas in the editor, like balloons, toolbars or dropdowns and as long + // as they have focus, the editable should act like it is focused too (although technically + // it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user. + // Doing otherwise will result in editable focus styles disappearing, once e.g. the + // toolbar gets focused. + view.editable.bind( 'isFocused' ).to( this.focusTracker ); + + // Bind the editable UI element to the editing view, making it an end– and entry–point + // of the editor's engine. This is where the engine meets the UI. + editingView.attachDomRoot( editableElement ); + + // If an element containing the initial data of the editor was provided, replace it with + // an editor instance's UI in DOM until the editor is destroyed. For instance, a