From e3193ee341dd1d8b414a426f28ace4eb9b98547c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Kup=C5=9B?= Date: Mon, 3 Apr 2017 11:15:27 +0200 Subject: [PATCH] Merged WidgetEngine into Widget. --- src/widget.js | 37 +++++++++++++---- src/widgetengine.js | 50 ---------------------- tests/widget.js | 60 +++++++++++++++++++++++++- tests/widgetengine.js | 97 ------------------------------------------- 4 files changed, 87 insertions(+), 157 deletions(-) delete mode 100644 src/widgetengine.js delete mode 100644 tests/widgetengine.js diff --git a/src/widget.js b/src/widget.js index b28e1c55..a8d48fc5 100644 --- a/src/widget.js +++ b/src/widget.js @@ -8,38 +8,57 @@ */ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import WidgetEngine from './widgetengine'; import MouseObserver from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver'; import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range'; import ModelSelection from '@ckeditor/ckeditor5-engine/src/model/selection'; import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element'; import ViewEditableElement from '@ckeditor/ckeditor5-engine/src/view/editableelement'; import RootEditableElement from '@ckeditor/ckeditor5-engine/src/view/rooteditableelement'; -import { isWidget } from './utils'; +import { isWidget, WIDGET_SELECTED_CLASS_NAME, getLabel } from './utils'; import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard'; import '../theme/theme.scss'; /** * The widget plugin. + * Registers model to view selection converter for editing pipeline. It is hooked after default selection conversion. + * If converted selection is placed around widget element, selection is marked as fake. Additionally, proper CSS class + * is added to indicate that widget has been selected. * Adds default {@link module:engine/view/document~Document#event:mousedown mousedown} handling on widget elements. * * @extends module:core/plugin~Plugin. */ export default class Widget extends Plugin { - /** - * @inheritDoc - */ - static get requires() { - return [ WidgetEngine ]; - } - /** * @inheritDoc */ init() { const viewDocument = this.editor.editing.view; + let previouslySelected; + + // Model to view selection converter. + // Converts selection placed over widget element to fake selection + this.editor.editing.modelToView.on( 'selection', ( evt, data, consumable, conversionApi ) => { + // Remove selected class from previously selected widget. + if ( previouslySelected && previouslySelected.hasClass( WIDGET_SELECTED_CLASS_NAME ) ) { + previouslySelected.removeClass( WIDGET_SELECTED_CLASS_NAME ); + } + + const viewSelection = conversionApi.viewSelection; + + // Check if widget was clicked or some sub-element. + const selectedElement = viewSelection.getSelectedElement(); + + if ( !selectedElement || !isWidget( selectedElement ) ) { + return; + } + + viewSelection.setFake( true, { label: getLabel( selectedElement ) } ); + selectedElement.addClass( WIDGET_SELECTED_CLASS_NAME ); + previouslySelected = selectedElement; + }, { priority: 'low' } ); + // If mouse down is pressed on widget - create selection over whole widget. viewDocument.addObserver( MouseObserver ); this.listenTo( viewDocument, 'mousedown', ( ...args ) => this._onMousedown( ...args ) ); diff --git a/src/widgetengine.js b/src/widgetengine.js deleted file mode 100644 index 611751bc..00000000 --- a/src/widgetengine.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/** - * @module widget/widgetengine - */ - -import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import { WIDGET_SELECTED_CLASS_NAME, isWidget, getLabel } from './utils'; - -/** - * The widget engine plugin. - * Registers model to view selection converter for editing pipeline. It is hooked after default selection conversion. - * If converted selection is placed around widget element, selection is marked as fake. Additionally, proper CSS class - * is added to indicate that widget has been selected. - * - * @extends module:core/plugin~Plugin. - */ -export default class WidgetEngine extends Plugin { - /** - * @inheritDoc - */ - init() { - let previouslySelected; - - // Model to view selection converter. - // Converts selection placed over widget element to fake selection - this.editor.editing.modelToView.on( 'selection', ( evt, data, consumable, conversionApi ) => { - // Remove selected class from previously selected widget. - if ( previouslySelected && previouslySelected.hasClass( WIDGET_SELECTED_CLASS_NAME ) ) { - previouslySelected.removeClass( WIDGET_SELECTED_CLASS_NAME ); - } - - const viewSelection = conversionApi.viewSelection; - - // Check if widget was clicked or some sub-element. - const selectedElement = viewSelection.getSelectedElement(); - - if ( !selectedElement || !isWidget( selectedElement ) ) { - return; - } - - viewSelection.setFake( true, { label: getLabel( selectedElement ) } ); - selectedElement.addClass( WIDGET_SELECTED_CLASS_NAME ); - previouslySelected = selectedElement; - }, { priority: 'low' } ); - } -} diff --git a/tests/widget.js b/tests/widget.js index 7efe6c60..3d356103 100644 --- a/tests/widget.js +++ b/tests/widget.js @@ -13,6 +13,7 @@ import ViewEditable from '@ckeditor/ckeditor5-engine/src/view/editableelement'; import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata'; import AttributeContainer from '@ckeditor/ckeditor5-engine/src/view/attributeelement'; import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; +import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view'; import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard'; /* global document */ @@ -37,6 +38,10 @@ describe( 'Widget', () => { doc.schema.registerItem( 'nested' ); doc.schema.allow( { name: '$inline', inside: 'nested' } ); doc.schema.allow( { name: 'nested', inside: 'widget' } ); + doc.schema.registerItem( 'editable' ); + doc.schema.allow( { name: '$inline', inside: 'editable' } ); + doc.schema.allow( { name: 'editable', inside: 'widget' } ); + doc.schema.allow( { name: 'editable', inside: '$root' } ); buildModelConverter().for( editor.editing.modelToView ) .fromElement( 'paragraph' ) @@ -48,7 +53,7 @@ describe( 'Widget', () => { const b = new AttributeContainer( 'b' ); const div = new ViewContainer( 'div', null, b ); - return toWidget( div ); + return toWidget( div, { label: 'element label' } ); } ); buildModelConverter().for( editor.editing.modelToView ) @@ -58,6 +63,10 @@ describe( 'Widget', () => { buildModelConverter().for( editor.editing.modelToView ) .fromElement( 'nested' ) .toElement( () => new ViewEditable( 'figcaption', { contenteditable: true } ) ); + + buildModelConverter().for( editor.editing.modelToView ) + .fromElement( 'editable' ) + .toElement( () => new ViewEditable( 'figcaption', { contenteditable: true } ) ); } ); } ); @@ -145,6 +154,55 @@ describe( 'Widget', () => { expect( getModelData( doc ) ).to.equal( '[]' ); } ); + it( 'should apply fake view selection if model selection is on widget element', () => { + setModelData( doc, '[foo bar]' ); + + expect( getViewData( viewDocument ) ).to.equal( + '[
foo bar
]' + ); + expect( viewDocument.selection.isFake ).to.be.true; + } ); + + it( 'should use element\'s label to set fake selection if one is provided', () => { + setModelData( doc, '[foo bar]' ); + + expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'element label' ); + } ); + + it( 'fake selection should be empty if widget is not selected', () => { + setModelData( doc, 'foo bar' ); + + expect( viewDocument.selection.fakeSelectionLabel ).to.equal( '' ); + } ); + + it( 'should toggle selected class', () => { + setModelData( doc, '[foo]' ); + + expect( getViewData( viewDocument ) ).to.equal( + '[
foo
]' + ); + + doc.enqueueChanges( () => { + doc.selection.collapseToStart(); + } ); + + expect( getViewData( viewDocument ) ).to.equal( + '[]
foo
' + ); + } ); + + it( 'should do nothing when selection is placed in other editable', () => { + setModelData( doc, 'foo bar[baz]' ); + + expect( getViewData( viewDocument ) ).to.equal( + '
' + + '
foo bar
' + + '' + + '
' + + '
{baz}
' + ); + } ); + describe( 'keys handling', () => { describe( 'delete and backspace', () => { test( diff --git a/tests/widgetengine.js b/tests/widgetengine.js deleted file mode 100644 index 268e8a76..00000000 --- a/tests/widgetengine.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; -import WidgetEngine from '../src/widgetengine'; -import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter'; -import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; -import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view'; -import ViewContainer from '@ckeditor/ckeditor5-engine/src/view/containerelement'; -import ViewEditable from '@ckeditor/ckeditor5-engine/src/view/editableelement'; -import { toWidget } from '../src/utils'; - -describe( 'WidgetEngine', () => { - let editor, document, viewDocument; - - beforeEach( () => { - return VirtualTestEditor.create( { - plugins: [ WidgetEngine ] - } ) - .then( newEditor => { - editor = newEditor; - document = editor.document; - viewDocument = editor.editing.view; - document.schema.registerItem( 'widget', '$block' ); - document.schema.registerItem( 'editable' ); - document.schema.allow( { name: '$inline', inside: 'editable' } ); - document.schema.allow( { name: 'editable', inside: 'widget' } ); - document.schema.allow( { name: 'editable', inside: '$root' } ); - - buildModelConverter().for( editor.editing.modelToView ) - .fromElement( 'widget' ) - .toElement( () => { - const element = toWidget( new ViewContainer( 'div' ), { label: 'element label' } ); - - return element; - } ); - - buildModelConverter().for( editor.editing.modelToView ) - .fromElement( 'editable' ) - .toElement( () => new ViewEditable( 'figcaption', { contenteditable: true } ) ); - } ); - } ); - - it( 'should be loaded', () => { - expect( editor.plugins.get( WidgetEngine ) ).to.be.instanceOf( WidgetEngine ); - } ); - - it( 'should apply fake view selection if model selection is on widget element', () => { - setModelData( document, '[foo bar]' ); - - expect( getViewData( viewDocument ) ).to.equal( - '[
foo bar
]' - ); - expect( viewDocument.selection.isFake ).to.be.true; - } ); - - it( 'should use element\'s label to set fake selection if one is provided', () => { - setModelData( document, '[foo bar]' ); - - expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'element label' ); - } ); - - it( 'fake selection should be empty if widget is not selected', () => { - setModelData( document, 'foo bar' ); - - expect( viewDocument.selection.fakeSelectionLabel ).to.equal( '' ); - } ); - - it( 'should toggle selected class', () => { - setModelData( document, '[foo]' ); - - expect( getViewData( viewDocument ) ).to.equal( - '[
foo
]' - ); - - document.enqueueChanges( () => { - document.selection.collapseToStart(); - } ); - - expect( getViewData( viewDocument ) ).to.equal( - '[]
foo
' - ); - } ); - - it( 'should do nothing when selection is placed in other editable', () => { - setModelData( document, 'foo bar[baz]' ); - - expect( getViewData( viewDocument ) ).to.equal( - '
' + - '
foo bar
' + - '
' + - '
{baz}
' - ); - } ); -} );