From 4dc91c4ed6764938b33eecdc3db393cdb7e3fc35 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Jun 2018 14:34:20 +0200 Subject: [PATCH] Feature: Creating a paragraph next to the selected widget is possible using the (Shift+)Enter key (see ckeditor/ckeditor5#407). --- src/widget.js | 31 +++++++++++++++++++++++++++++++ tests/widget.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/widget.js b/src/widget.js index 7c8cff5f..e05924a4 100644 --- a/src/widget.js +++ b/src/widget.js @@ -154,6 +154,8 @@ export default class Widget extends Plugin { wasHandled = this._handleArrowKeys( isForward ); } else if ( isSelectAllKeyCode( domEventData ) ) { wasHandled = this._selectAllNestedEditableContent() || this._selectAllContent(); + } else if ( keyCode === keyCodes.enter ) { + wasHandled = this._handleEnterKey( domEventData.shiftKey ); } if ( wasHandled ) { @@ -207,6 +209,7 @@ export default class Widget extends Plugin { /** * Handles arrow keys. * + * @private * @param {Boolean} isForward Set to true if arrow key should be handled in forward direction. * @returns {Boolean|undefined} Returns `true` if keys were handled correctly. */ @@ -246,6 +249,34 @@ export default class Widget extends Plugin { } } + /** + * Handles the enter key, giving users and access to positions in the editable directly before + * (Shift+Enter) or after (Enter) the selected widget. + * It improves the UX, mainly when the widget is the first or last child of the root editable + * and there's no other way to type after or before it. + * + * @private + * @param {Boolean} isBackwards Set to true if the new paragraph is to be inserted before + * the selected widget (Shift+Enter). + * @returns {Boolean|undefined} Returns `true` if keys were handled correctly. + */ + _handleEnterKey( isBackwards ) { + const model = this.editor.model; + const modelSelection = model.document.selection; + const objectElement = modelSelection.getSelectedElement(); + + if ( objectElement && model.schema.isObject( objectElement ) ) { + model.change( writer => { + const paragraph = writer.createElement( 'paragraph' ); + + writer.insert( paragraph, objectElement, isBackwards ? 'before' : 'after' ); + writer.setSelection( paragraph, 'in' ); + } ); + + return true; + } + } + /** * Extends the {@link module:engine/model/selection~Selection document's selection} to span the entire * content of the nested editable if already anchored in one. diff --git a/tests/widget.js b/tests/widget.js index 3f4c54d7..6511f343 100644 --- a/tests/widget.js +++ b/tests/widget.js @@ -681,6 +681,50 @@ describe( 'Widget', () => { ); } ); + describe( 'enter', () => { + test( + 'should insert a paragraph after the selected widget upon Enter', + '[]', + keyCodes.enter, + '[]' + ); + + test( + 'should insert a paragraph before the selected widget upon Shift+Enter', + '[]', + { keyCode: keyCodes.enter, shiftKey: true }, + '[]' + ); + + test( + 'should insert a paragraph when not a first-child of the root', + '[]foo', + keyCodes.enter, + '[]foo' + ); + + test( + 'should insert a paragraph when not a last-child of the root', + 'foo[]', + { keyCode: keyCodes.enter, shiftKey: true }, + 'foo[]' + ); + + test( + 'should insert a paragraph only when an entire widget is selected (#1)', + '[foo] bar', + keyCodes.enter, + '[] bar' + ); + + test( + 'should insert a paragraph only when an entire widget is selected (#2)', + 'f[oob]ar', + keyCodes.enter, + 'f[]ar' + ); + } ); + function test( name, data, keyCodeOrMock, expected, expectedView ) { it( name, () => { const domEventDataMock = ( typeof keyCodeOrMock == 'object' ) ? keyCodeOrMock : {