From 40cdde00c7194420fdca87a922900147681bec03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 19 Jun 2018 11:39:23 +0200 Subject: [PATCH 1/9] Feature: Implement the selection handler. --- src/utils.js | 34 ++++++++++++++++++++++++++++++++++ tests/utils.js | 23 +++++++++++++++++++++++ theme/icons/drag-handler.svg | 1 + 3 files changed, 58 insertions(+) create mode 100644 theme/icons/drag-handler.svg diff --git a/src/utils.js b/src/utils.js index e2f4d182..621e7b6b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -8,6 +8,10 @@ */ import HighlightStack from './highlightstack'; +import Position from '@ckeditor/ckeditor5-engine/src/view/position'; +import IconView from '@ckeditor/ckeditor5-ui/src/icon/iconview'; + +import dragHandlerIcon from '../theme/icons/drag-handler.svg'; const widgetSymbol = Symbol( 'isWidget' ); const labelSymbol = Symbol( 'label' ); @@ -49,6 +53,7 @@ export function isWidget( element ) { * @param {Object} [options={}] * @param {String|Function} [options.label] Element's label provided to {@link ~setLabel} function. It can be passed as * a plain string or a function returning a string. + * @param {Boolean} [options.addSelectionHandler=false] If set to true the widget will have a selection handler added. * @returns {module:engine/view/element~Element} Returns same element. */ export function toWidget( element, writer, options = {} ) { @@ -61,6 +66,10 @@ export function toWidget( element, writer, options = {} ) { setLabel( element, options.label, writer ); } + if ( options.addSelectionHandler ) { + addSelectionHandler( element, writer ); + } + setHighlightHandling( element, writer, @@ -170,3 +179,28 @@ export function toWidgetEditable( editable, writer ) { function getFillerOffset() { return null; } + +// Adds a drag handler to the editable element. +// +// @param {module:engine/view/editableelement~EditableElement} +// @param {module:engine/view/writer~Writer} writer +function addSelectionHandler( editable, writer ) { + const selectionHandler = writer.createUIElement( 'div', { class: 'ck ck-selection-handler' }, function( domDocument ) { + const domElement = this.toDomElement( domDocument ); + + // Use the IconView from the ui library. + const icon = new IconView(); + icon.set( 'content', dragHandlerIcon ); + + // Render the icon view right away to append its #element to the selectionHandler DOM element. + icon.render(); + + domElement.appendChild( icon.element ); + + return domElement; + } ); + + // Append the selection handler into the widget wrapper. + writer.insert( Position.createAt( editable ), selectionHandler ); + writer.addClass( [ 'ck-widget__selectable' ], editable ); +} diff --git a/tests/utils.js b/tests/utils.js index 6eebc925..3bb5fd01 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -3,6 +3,8 @@ * For licensing, see LICENSE.md. */ +/* global document */ + import Writer from '@ckeditor/ckeditor5-engine/src/view/writer'; import ViewElement from '@ckeditor/ckeditor5-engine/src/view/element'; import ViewEditableElement from '@ckeditor/ckeditor5-engine/src/view/editableelement'; @@ -16,6 +18,7 @@ import { setHighlightHandling, WIDGET_CLASS_NAME } from '../src/utils'; +import UIElement from '@ckeditor/ckeditor5-engine/src/view/uielement'; describe( 'widget utils', () => { let element, writer, viewDocument; @@ -87,6 +90,26 @@ describe( 'widget utils', () => { expect( element.hasClass( 'highlight' ) ).to.be.false; expect( element.hasClass( 'foo' ) ).to.be.false; } ); + + it( 'should add element a selection handler to widget if addSelectionHandler=true is passed', () => { + toWidget( element, writer, { addSelectionHandler: true } ); + + expect( element.hasClass( 'ck-widget__selectable' ) ).to.be.true; + + const selectionHandler = element.getChild( 0 ); + expect( selectionHandler ).to.be.instanceof( UIElement ); + + const domSelectionHandler = selectionHandler.render( document ); + + expect( domSelectionHandler.classList.contains( 'ck' ) ).to.be.true; + expect( domSelectionHandler.classList.contains( 'ck-selection-handler' ) ).to.be.true; + + const icon = domSelectionHandler.firstChild; + + expect( icon.nodeName ).to.equal( 'svg' ); + expect( icon.classList.contains( 'ck' ) ).to.be.true; + expect( icon.classList.contains( 'ck-icon' ) ).to.be.true; + } ); } ); describe( 'isWidget()', () => { diff --git a/theme/icons/drag-handler.svg b/theme/icons/drag-handler.svg new file mode 100644 index 00000000..43d5cd5f --- /dev/null +++ b/theme/icons/drag-handler.svg @@ -0,0 +1 @@ + \ No newline at end of file From 47317a779122f411f980e04af90fba5faccdf4d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 20 Jun 2018 17:00:44 +0200 Subject: [PATCH 2/9] Tests: Add widget's selection handler mouse click test. --- tests/widget.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/widget.js b/tests/widget.js index 6511f343..8f6caf3f 100644 --- a/tests/widget.js +++ b/tests/widget.js @@ -1159,4 +1159,50 @@ describe( 'Widget', () => { scrollStub.restore(); } ); } ); + + describe( 'selection handler', () => { + beforeEach( () => { + return VirtualTestEditor.create( { plugins: [ Widget, Typing ] } ) + .then( newEditor => { + editor = newEditor; + model = editor.model; + view = editor.editing.view; + viewDocument = view.document; + + model.schema.register( 'widget', { + inheritAllFrom: '$block', + isObject: true + } ); + model.schema.register( 'paragraph', { + inheritAllFrom: '$block' + } ); + + editor.conversion.for( 'downcast' ) + .add( downcastElementToElement( { model: 'paragraph', view: 'p' } ) ) + .add( downcastElementToElement( { + model: 'widget', + view: ( modelItem, viewWriter ) => { + const widget = viewWriter.createContainerElement( 'div' ); + + return toWidget( widget, viewWriter, { addSelectionHandler: true } ); + } + } ) ); + } ); + } ); + + it( 'should select a widget on mouse click', () => { + setModelData( model, 'barfoo[]' ); + + const viewWidgetSelectionHandler = viewDocument.getRoot().getChild( 1 ).getChild( 0 ); + + const domEventDataMock = { + target: viewWidgetSelectionHandler, + preventDefault: sinon.spy() + }; + + viewDocument.fire( 'mousedown', domEventDataMock ); + + expect( getModelData( model ) ).to.equal( 'bar[]foo' ); + } ); + } ); } ); From 92c95e06932caa726ffc88fea96db364e1cfea24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 20 Jun 2018 17:21:54 +0200 Subject: [PATCH 3/9] Other: Update package.json dependencies. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 60e0ab2a..db98fa75 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "@ckeditor/ckeditor5-core": "^10.0.0", "@ckeditor/ckeditor5-engine": "^10.0.0", "@ckeditor/ckeditor5-utils": "^10.0.0", - "@ckeditor/ckeditor5-theme-lark": "^10.0.0" + "@ckeditor/ckeditor5-theme-lark": "^10.0.0", + "@ckeditor/ckeditor5-ui": "^10.0.0" }, "devDependencies": { "@ckeditor/ckeditor5-typing": "^10.0.0", From 859f5af7df9857eb0f8ee8ece62e8bcaab1df29a Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 25 Jun 2018 14:25:59 +0200 Subject: [PATCH 4/9] Fixed formatting in package.json. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a3d3050e..7d49518d 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "@ckeditor/ckeditor5-core": "^10.1.0", "@ckeditor/ckeditor5-engine": "^10.1.0", "@ckeditor/ckeditor5-utils": "^10.1.0", - "@ckeditor/ckeditor5-theme-lark": "^10.1.0", - "@ckeditor/ckeditor5-ui": "^10.1.0" + "@ckeditor/ckeditor5-theme-lark": "^10.1.0", + "@ckeditor/ckeditor5-ui": "^10.1.0" }, "devDependencies": { "@ckeditor/ckeditor5-typing": "^10.0.1", From 69d3503e10df587404593776898389f58d64c317 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 26 Jun 2018 13:01:14 +0200 Subject: [PATCH 5/9] Renamed .ck-widget__selectable to .ck-widget_selectable to match BEM standards. Moved widget selection handler styles to ckeditor5-theme-lark. Implemented a 2-state handler icon. --- src/utils.js | 2 +- tests/utils.js | 2 +- theme/icons/drag-handler.svg | 2 +- theme/widget.css | 34 +++++++++++++++++++++++++++++----- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/utils.js b/src/utils.js index 621e7b6b..6c4a3467 100644 --- a/src/utils.js +++ b/src/utils.js @@ -202,5 +202,5 @@ function addSelectionHandler( editable, writer ) { // Append the selection handler into the widget wrapper. writer.insert( Position.createAt( editable ), selectionHandler ); - writer.addClass( [ 'ck-widget__selectable' ], editable ); + writer.addClass( [ 'ck-widget_selectable' ], editable ); } diff --git a/tests/utils.js b/tests/utils.js index 3bb5fd01..9fc67bdd 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -94,7 +94,7 @@ describe( 'widget utils', () => { it( 'should add element a selection handler to widget if addSelectionHandler=true is passed', () => { toWidget( element, writer, { addSelectionHandler: true } ); - expect( element.hasClass( 'ck-widget__selectable' ) ).to.be.true; + expect( element.hasClass( 'ck-widget_selectable' ) ).to.be.true; const selectionHandler = element.getChild( 0 ); expect( selectionHandler ).to.be.instanceof( UIElement ); diff --git a/theme/icons/drag-handler.svg b/theme/icons/drag-handler.svg index 43d5cd5f..f8bf8680 100644 --- a/theme/icons/drag-handler.svg +++ b/theme/icons/drag-handler.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/theme/widget.css b/theme/widget.css index 66c26cca..b949e053 100644 --- a/theme/widget.css +++ b/theme/widget.css @@ -3,8 +3,32 @@ * For licensing, see LICENSE.md. */ -/* - * Note: This file should contain the wireframe styles only. But since there are no such styles, - * it acts as a message to the builder telling that it should look for the corresponding styles - * **in the theme** when compiling the editor. - */ +.ck .ck-widget.ck-widget_selectable { + /* Make the widget wrapper a relative positioning container for the drag handler. */ + position: relative; + + & .ck-selection-handler { + visibility: hidden; + position: absolute; + + & .ck-icon { + /* Make sure the icon in not a subject to fon-size/line-height to avoid + unnecessary spacing around it. */ + display: block; + } + } + + /* Show the selection handler on mouse hover over the widget. */ + &:hover { + & .ck-selection-handler { + visibility: visible; + } + } + + /* Show the selection handler when the widget is selected. */ + &.ck-widget_selected { + & .ck-selection-handler { + visibility: visible; + } + } +} From 34b1714d5851f7fc6039e56f92f181fc81fba3ba Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 26 Jun 2018 14:30:38 +0200 Subject: [PATCH 6/9] Removed obsolete data from drag-handler icon. --- theme/icons/drag-handler.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/icons/drag-handler.svg b/theme/icons/drag-handler.svg index f8bf8680..bca04fb4 100644 --- a/theme/icons/drag-handler.svg +++ b/theme/icons/drag-handler.svg @@ -1 +1 @@ - + From 2bdf9c6e2236ed3761d40e3eb304a56a783c5a0a Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 26 Jun 2018 16:18:14 +0200 Subject: [PATCH 7/9] Renamed toWidget option addSelectionHandler to hasSelectionHandler. --- src/utils.js | 8 ++++---- tests/utils.js | 4 ++-- tests/widget.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils.js b/src/utils.js index 6c4a3467..025f66ad 100644 --- a/src/utils.js +++ b/src/utils.js @@ -53,7 +53,7 @@ export function isWidget( element ) { * @param {Object} [options={}] * @param {String|Function} [options.label] Element's label provided to {@link ~setLabel} function. It can be passed as * a plain string or a function returning a string. - * @param {Boolean} [options.addSelectionHandler=false] If set to true the widget will have a selection handler added. + * @param {Boolean} [options.hasSelectionHandler=false] If `true`, the widget will have a selection handler added. * @returns {module:engine/view/element~Element} Returns same element. */ export function toWidget( element, writer, options = {} ) { @@ -66,8 +66,8 @@ export function toWidget( element, writer, options = {} ) { setLabel( element, options.label, writer ); } - if ( options.addSelectionHandler ) { - addSelectionHandler( element, writer ); + if ( options.hasSelectionHandler ) { + hasSelectionHandler( element, writer ); } setHighlightHandling( @@ -184,7 +184,7 @@ function getFillerOffset() { // // @param {module:engine/view/editableelement~EditableElement} // @param {module:engine/view/writer~Writer} writer -function addSelectionHandler( editable, writer ) { +function hasSelectionHandler( editable, writer ) { const selectionHandler = writer.createUIElement( 'div', { class: 'ck ck-selection-handler' }, function( domDocument ) { const domElement = this.toDomElement( domDocument ); diff --git a/tests/utils.js b/tests/utils.js index 9fc67bdd..9470f351 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -91,8 +91,8 @@ describe( 'widget utils', () => { expect( element.hasClass( 'foo' ) ).to.be.false; } ); - it( 'should add element a selection handler to widget if addSelectionHandler=true is passed', () => { - toWidget( element, writer, { addSelectionHandler: true } ); + it( 'should add element a selection handler to widget if hasSelectionHandler=true is passed', () => { + toWidget( element, writer, { hasSelectionHandler: true } ); expect( element.hasClass( 'ck-widget_selectable' ) ).to.be.true; diff --git a/tests/widget.js b/tests/widget.js index 8f6caf3f..4105c290 100644 --- a/tests/widget.js +++ b/tests/widget.js @@ -1184,7 +1184,7 @@ describe( 'Widget', () => { view: ( modelItem, viewWriter ) => { const widget = viewWriter.createContainerElement( 'div' ); - return toWidget( widget, viewWriter, { addSelectionHandler: true } ); + return toWidget( widget, viewWriter, { hasSelectionHandler: true } ); } } ) ); } ); From 9889dbd7f1559562824a74f1b0bf70a09a001257 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 26 Jun 2018 16:22:27 +0200 Subject: [PATCH 8/9] Renamed .ck-selection-handler to .ck-widget__selection-handler (BEM). --- src/utils.js | 2 +- tests/utils.js | 2 +- theme/widget.css | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils.js b/src/utils.js index 025f66ad..a0efbd03 100644 --- a/src/utils.js +++ b/src/utils.js @@ -185,7 +185,7 @@ function getFillerOffset() { // @param {module:engine/view/editableelement~EditableElement} // @param {module:engine/view/writer~Writer} writer function hasSelectionHandler( editable, writer ) { - const selectionHandler = writer.createUIElement( 'div', { class: 'ck ck-selection-handler' }, function( domDocument ) { + const selectionHandler = writer.createUIElement( 'div', { class: 'ck ck-widget__selection-handler' }, function( domDocument ) { const domElement = this.toDomElement( domDocument ); // Use the IconView from the ui library. diff --git a/tests/utils.js b/tests/utils.js index 9470f351..47d8aa93 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -102,7 +102,7 @@ describe( 'widget utils', () => { const domSelectionHandler = selectionHandler.render( document ); expect( domSelectionHandler.classList.contains( 'ck' ) ).to.be.true; - expect( domSelectionHandler.classList.contains( 'ck-selection-handler' ) ).to.be.true; + expect( domSelectionHandler.classList.contains( 'ck-widget__selection-handler' ) ).to.be.true; const icon = domSelectionHandler.firstChild; diff --git a/theme/widget.css b/theme/widget.css index b949e053..b8c63a71 100644 --- a/theme/widget.css +++ b/theme/widget.css @@ -7,7 +7,7 @@ /* Make the widget wrapper a relative positioning container for the drag handler. */ position: relative; - & .ck-selection-handler { + & .ck-widget__selection-handler { visibility: hidden; position: absolute; @@ -20,14 +20,14 @@ /* Show the selection handler on mouse hover over the widget. */ &:hover { - & .ck-selection-handler { + & .ck-widget__selection-handler { visibility: visible; } } /* Show the selection handler when the widget is selected. */ &.ck-widget_selected { - & .ck-selection-handler { + & .ck-widget__selection-handler { visibility: visible; } } From 610fde26b47ea232fea1683e2d5ea560bfcf3620 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 26 Jun 2018 16:38:57 +0200 Subject: [PATCH 9/9] Internal: Reverted the old name of the widget utils helper function addSelectionHandler. --- src/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils.js b/src/utils.js index a0efbd03..973a8bac 100644 --- a/src/utils.js +++ b/src/utils.js @@ -67,7 +67,7 @@ export function toWidget( element, writer, options = {} ) { } if ( options.hasSelectionHandler ) { - hasSelectionHandler( element, writer ); + addSelectionHandler( element, writer ); } setHighlightHandling( @@ -184,7 +184,7 @@ function getFillerOffset() { // // @param {module:engine/view/editableelement~EditableElement} // @param {module:engine/view/writer~Writer} writer -function hasSelectionHandler( editable, writer ) { +function addSelectionHandler( editable, writer ) { const selectionHandler = writer.createUIElement( 'div', { class: 'ck ck-widget__selection-handler' }, function( domDocument ) { const domElement = this.toDomElement( domDocument );