@ckeditor/ckeditor5-editor-decoupled
Version:
Decoupled editor implementation for CKEditor 5.
105 lines (104 loc) • 4.28 kB
JavaScript
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
import { EditorUI } from 'ckeditor5/src/ui.js';
import { enablePlaceholder } from 'ckeditor5/src/engine.js';
/**
* The decoupled editor UI class.
*/
export default class DecoupledEditorUI extends EditorUI {
/**
* The main (top–most) view of the editor UI.
*/
view;
/**
* Creates an instance of the decoupled editor UI class.
*
* @param editor The editor instance.
* @param view The view of the UI.
*/
constructor(editor, view) {
super(editor);
this.view = view;
}
/**
* Initializes the UI.
*/
init() {
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 DecoupledEditor.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 decoupled editor has only one.
this.setEditableElement(editable.name, 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);
this._initPlaceholder();
this._initToolbar();
this.initMenuBar(this.view.menuBarView);
this.fire('ready');
}
/**
* @inheritDoc
*/
destroy() {
super.destroy();
const view = this.view;
const editingView = this.editor.editing.view;
if (editingView.getDomRoot(view.editable.name)) {
editingView.detachDomRoot(view.editable.name);
}
view.destroy();
}
/**
* Initializes the inline editor toolbar and its panel.
*/
_initToolbar() {
const editor = this.editor;
const view = this.view;
const toolbar = view.toolbar;
toolbar.fillFromConfig(editor.config.get('toolbar'), this.componentFactory);
// Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
this.addToolbar(view.toolbar);
}
/**
* Enable the placeholder text on the editing root.
*/
_initPlaceholder() {
const editor = this.editor;
const editingView = editor.editing.view;
const editingRoot = editingView.document.getRoot();
const placeholder = editor.config.get('placeholder');
if (placeholder) {
const placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[editingRoot.rootName];
if (placeholderText) {
editingRoot.placeholder = placeholderText;
}
}
enablePlaceholder({
view: editingView,
element: editingRoot,
isDirectHost: false,
keepOnFocus: true
});
}
}