UNPKG

@steroidsjs/ckeditor5

Version:

The development environment of CKEditor 5 – the best browser-based rich text editor.

157 lines (137 loc) 4.59 kB
/** * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module ui/editableui/editableuiview */ import View from '../view'; /** * The editable UI view class. * * @extends module:ui/view~View */ export default class EditableUIView extends View { /** * Creates an instance of EditableUIView class. * * @param {module:utils/locale~Locale} [locale] The locale instance. * @param {module:engine/view/view~View} editingView The editing view instance the editable is related to. * @param {HTMLElement} [editableElement] The editable element. If not specified, this view * should create it. Otherwise, the existing element should be used. */ constructor( locale, editingView, editableElement ) { super( locale ); this.setTemplate( { tag: 'div', attributes: { class: [ 'ck', 'ck-content', 'ck-editor__editable', 'ck-rounded-corners' ], lang: locale.contentLanguage, dir: locale.contentLanguageDirection } } ); /** * The name of the editable UI view. * * @member {String} #name */ this.name = null; /** * Controls whether the editable is focused, i.e. the user is typing in it. * * @observable * @member {Boolean} #isFocused */ this.set( 'isFocused', false ); /** * The element which is the main editable element (usually the one with `contentEditable="true"`). * * @private * @type {HTMLElement} */ this._editableElement = editableElement; /** * Whether an external {@link #_editableElement} was passed into the constructor, which also means * the view will not render its {@link #template}. * * @private * @type {Boolean} */ this._hasExternalElement = !!this._editableElement; /** * The editing view instance the editable is related to. Editable uses the editing * view to dynamically modify its certain DOM attributes after {@link #render rendering}. * * **Note**: The DOM attributes are performed by the editing view and not UI * {@link module:ui/view~View#bindTemplate template bindings} because once rendered, * the editable DOM element must remain under the full control of the engine to work properly. * * @protected * @type {module:engine/view/view~View} */ this._editingView = editingView; } /** * Renders the view by either applying the {@link #template} to the existing * {@link #_editableElement} or assigning {@link #element} as {@link #_editableElement}. */ render() { super.render(); if ( this._hasExternalElement ) { this.template.apply( this.element = this._editableElement ); } else { this._editableElement = this.element; } this.on( 'change:isFocused', () => this._updateIsFocusedClasses() ); this._updateIsFocusedClasses(); } /** * @inheritDoc */ destroy() { if ( this._hasExternalElement ) { this.template.revert( this._editableElement ); } super.destroy(); } /** * Updates the `ck-focused` and `ck-blurred` CSS classes on the {@link #element} according to * the {@link #isFocused} property value using the {@link #_editingView editing view} API. * * @private */ _updateIsFocusedClasses() { const editingView = this._editingView; if ( editingView.isRenderingInProgress ) { updateAfterRender( this ); } else { update( this ); } function update( view ) { editingView.change( writer => { const viewRoot = editingView.document.getRoot( view.name ); writer.addClass( view.isFocused ? 'ck-focused' : 'ck-blurred', viewRoot ); writer.removeClass( view.isFocused ? 'ck-blurred' : 'ck-focused', viewRoot ); } ); } // In a case of a multi-root editor, a callback will be attached more than once (one callback for each root). // While executing one callback the `isRenderingInProgress` observable is changing what causes executing another // callback and render is called inside the already pending render. // We need to be sure that callback is executed only when the value has changed from `true` to `false`. // See https://github.com/ckeditor/ckeditor5/issues/1676. function updateAfterRender( view ) { editingView.once( 'change:isRenderingInProgress', ( evt, name, value ) => { if ( !value ) { update( view ); } else { updateAfterRender( view ); } } ); } } }