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

t/1: The first implementation of the balloon toolbar editor #2

Merged
merged 3 commits into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions src/balloontoolbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module editor-balloon-toolbar/balloontoolbareditor
*/

import StandardEditor from '@ckeditor/ckeditor5-core/src/editor/standardeditor';
import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
import ContextualToolbar from '@ckeditor/ckeditor5-ui/src/toolbar/contextual/contextualtoolbar';
import BalloonToolbarEditorUI from './balloontoolbareditorui';
import BalloonToolbarEditorUIView from './balloontoolbareditoruiview';

import '../theme/theme.scss';

/**
* The balloon toolbar editor. Uses an inline editable and a toolbar based
* on the {@link module:ui/toolbar/contextual/contextualtoolbar~ContextualToolbar}.
*
* @extends module:core/editor/standardeditor~StandardEditor
*/
export default class BalloonToolbarEditor extends StandardEditor {
/**
* Creates an instance of the balloon toolbar editor.
*
* @param {HTMLElement} element The DOM element that will be the source for the created editor.
* @param {Object} config The editor configuration.
*/
constructor( element, config ) {
super( element, config );

this.config.get( 'plugins' ).push( ContextualToolbar );
this.config.define( 'contextualToolbar', this.config.get( 'toolbar' ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if this will not cause any issues, because it will (correct me if I am wrong) copy reference to same object, so further changes to toolbar configuration will also affect contextualToolbar. Or maybe this is desired behavior?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... this is quite a story. At the very beginning, this editor type expected developers to configure it like this:

BalloonToolbarEditor.create( editorElement, {
	plugins: [ ContextualToolbar ],
	contextualToolbar: [ ... ]
} );

but in a F2F talk with @Reinmar we found out this is not a very straightforward since editor-inline and editor-classic don't require a) any special plugins b) custom config names (toolbar vs contextualToolbar).

So we decided to hack things a little bit to load ContextualToolbar automatically and create a "bridge" between cfg.toolbar and cfg.contextualToolbar to keep the API between the creators as similar as possible.

so further changes to toolbar configuration will also affect contextualToolbar

Well, I suppose it doesn't matter. Once you decide to use BalloonToolbarEditor, it takes the full control over ContextualToolbar. I don't see how ContextualToolbar could remain useful for other features once seized by the BalloonToolbarEditor 🤔 .


this.document.createRoot();
this.data.processor = new HtmlDataProcessor();
this.ui = new BalloonToolbarEditorUI( this, new BalloonToolbarEditorUIView( this.locale, element ) );
}

/**
* Destroys the editor instance, releasing all resources used by it.
*
* Updates the original editor element with the data.
*
* @returns {Promise}
*/
destroy() {
this.updateEditorElement();

return this.ui.destroy()
.then( () => super.destroy() );
}

/**
* Creates a balloon toolbar editor instance.
*
* BalloonToolbarEditor.create( document.querySelector( '#editor' ), {
* plugins: [ Delete, Enter, Typing, Paragraph, Undo, Bold, Italic ],
* toolbar: [ 'bold', 'italic' ]
* } )
* .then( editor => {
* console.log( 'Editor was initialized', editor );
* } )
* .catch( err => {
* console.error( err.stack );
* } );
*
* @param {HTMLElement} element See {@link #constructor}'s parameters.
* @param {Object} config See {@link #constructor}'s parameters.
* @returns {Promise} A promise resolved once the editor is ready.
* @returns {module:core/editor/standardeditor~StandardEditor} return.editor The editor instance.
*/
static create( element, config ) {
return new Promise( resolve => {
const editor = new this( element, config );

resolve(
editor.initPlugins()
.then( () => editor.ui.init() )
.then( () => editor.fire( 'uiReady' ) )
.then( () => editor.loadDataFromEditorElement() )
.then( () => {
editor.fire( 'dataReady' );
editor.fire( 'ready' );
} )
.then( () => editor )
);
} );
}
}
86 changes: 86 additions & 0 deletions src/balloontoolbareditorui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module editor-balloon-toolbar/balloontoolbareditorui
*/

import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';
import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
import enableToolbarKeyboardFocus from '@ckeditor/ckeditor5-ui/src/toolbar/enabletoolbarkeyboardfocus';

/**
* The balloon toolbar editor UI class.
*
* @implements module:core/editor/editorui~EditorUI
*/
export default class BalloonToolbarEditorUI {
/**
* Creates an instance of the balloon toolbar editor UI class.
*
* @param {module:core/editor/editor~Editor} editor The editor instance.
* @param {module:ui/editorui/editoruiview~EditorUIView} view View of the ui.
*/
constructor( editor, view ) {
/**
* @inheritDoc
*/
this.editor = editor;

/**
* @inheritDoc
*/
this.view = view;

/**
* @inheritDoc
*/
this.componentFactory = new ComponentFactory( editor );

/**
* @inheritDoc
*/
this.focusTracker = new FocusTracker();

// Setup the editable.
const editingRoot = editor.editing.createRoot( view.editableElement );
view.editable.bind( 'isReadOnly' ).to( editingRoot );

// Bind to focusTracker instead of editor.editing.view because otherwise
// focused editable styles disappear when view#toolbar is focused.
view.editable.bind( 'isFocused' ).to( this.focusTracker );
view.editable.name = editingRoot.rootName;

this.focusTracker.add( view.editableElement );
}

/**
* Initializes the UI.
*
* @returns {Promise} A Promise resolved when the initialization process is finished.
*/
init() {
const editor = this.editor;
const contextualToolbar = editor.plugins.get( 'ui/contextualtoolbar' );

return this.view.init().then( () => {
enableToolbarKeyboardFocus( {
origin: editor.editing.view,
originFocusTracker: this.focusTracker,
originKeystrokeHandler: editor.keystrokes,
toolbar: contextualToolbar.toolbarView
} );
} );
}

/**
* Destroys the UI.
*
* @returns {Promise} A Promise resolved when the destruction process is finished.
*/
destroy() {
return this.view.destroy();
}
}
44 changes: 44 additions & 0 deletions src/balloontoolbareditoruiview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module editor-balloon-toolbar/balloontoolbareditoruiview
*/

import EditorUIView from '@ckeditor/ckeditor5-ui/src/editorui/editoruiview';
import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview';

/**
* Contextual editor UI view. Uses the {@link module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}.
*
* @extends module:ui/editorui/editoruiview~EditorUIView
*/
export default class BalloonToolbarEditorUIView extends EditorUIView {
/**
* Creates an instance of the balloon toolbar editor UI view.
*
* @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
*/
constructor( locale, editableElement ) {
super( locale );

/**
* The editable UI view.
*
* @readonly
* @member {module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}
*/
this.editable = new InlineEditableUIView( locale, editableElement );

this.addChildren( this.editable );
}

/**
* @inheritDoc
*/
get editableElement() {
return this.editable.element;
}
}
Loading