8
0
Эх сурвалжийг харах

Merge pull request #24 from ckeditor/t/23

Fix: Correctly handling select all keystroke when widget is selected. Closes #23.
Oskar Wróbel 8 жил өмнө
parent
commit
f073f4e22c

+ 66 - 14
packages/ckeditor5-widget/src/widget.js

@@ -44,28 +44,38 @@ export default class Widget extends Plugin {
 	init() {
 		const viewDocument = this.editor.editing.view;
 
-		let previouslySelected;
+		/**
+		 * Holds previously selected widgets.
+		 *
+		 * @private
+		 * @type {Set.<module:engine/view/element~Element>}
+		 */
+		this._previouslySelected = new Set();
 
 		// 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 );
-			}
+			// Remove selected class from previously selected widgets.
+			this._clearPreviouslySelectedWidgets();
 
 			const viewSelection = conversionApi.viewSelection;
-
-			// Check if widget was clicked or some sub-element.
 			const selectedElement = viewSelection.getSelectedElement();
 
-			if ( !selectedElement || !isWidget( selectedElement ) ) {
-				return;
-			}
+			for ( const range of viewSelection.getRanges() ) {
+				for ( const value of range ) {
+					const node = value.item;
 
-			viewSelection.setFake( true, { label: getLabel( selectedElement ) } );
-			selectedElement.addClass( WIDGET_SELECTED_CLASS_NAME );
-			previouslySelected = selectedElement;
+					if ( node.is( 'element' ) && isWidget( node ) ) {
+						node.addClass( WIDGET_SELECTED_CLASS_NAME );
+						this._previouslySelected.add( node );
+
+						// Check if widget is a single element selected.
+						if ( node == selectedElement ) {
+							viewSelection.setFake( true, { label: getLabel( selectedElement ) } );
+						}
+					}
+				}
+			}
 		}, { priority: 'low' } );
 
 		// If mouse down is pressed on widget - create selection over whole widget.
@@ -136,7 +146,7 @@ export default class Widget extends Plugin {
 		} else if ( isArrowKeyCode( keyCode ) ) {
 			wasHandled = this._handleArrowKeys( isForward );
 		} else if ( isSelectAllKeyCode( domEventData ) ) {
-			wasHandled = this._selectAllNestedEditableContent();
+			wasHandled = this._selectAllNestedEditableContent() || this._selectAllContent();
 		}
 
 		if ( wasHandled ) {
@@ -256,6 +266,36 @@ export default class Widget extends Plugin {
 		return true;
 	}
 
+	/**
+	 * Handles <kbd>CTRL + A</kbd> when widget is selected.
+	 *
+	 * @private
+	 * @returns {Boolean} Returns true if widget was selected and selecting all was handled by this method.
+	 */
+	_selectAllContent() {
+		const modelDocument = this.editor.document;
+		const modelSelection = modelDocument.selection;
+		const editing = this.editor.editing;
+		const viewDocument = editing.view;
+		const viewSelection = viewDocument.selection;
+
+		const selectedElement = viewSelection.getSelectedElement();
+
+		// Only widget is selected.
+		// https://github.com/ckeditor/ckeditor5-widget/issues/23
+		if ( selectedElement && isWidget( selectedElement ) ) {
+			const widgetParent = editing.mapper.toModelElement( selectedElement.parent );
+
+			modelDocument.enqueueChanges( () => {
+				modelSelection.setRanges( [ ModelRange.createIn( widgetParent ) ] );
+			} );
+
+			return true;
+		}
+
+		return false;
+	}
+
 	/**
 	 * Sets {@link module:engine/model/selection~Selection document's selection} over given element.
 	 *
@@ -293,6 +333,18 @@ export default class Widget extends Plugin {
 
 		return null;
 	}
+
+	/**
+	 * Removes CSS class from previously selected widgets.
+	 * @private
+	 */
+	_clearPreviouslySelectedWidgets() {
+		for ( const widget of this._previouslySelected ) {
+			widget.removeClass( WIDGET_SELECTED_CLASS_NAME );
+		}
+
+		this._previouslySelected.clear();
+	}
 }
 
 // Returns 'true' if provided key code represents one of the arrow keys.

+ 27 - 1
packages/ckeditor5-widget/tests/widget.js

@@ -196,6 +196,19 @@ describe( 'Widget', () => {
 		expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'element label' );
 	} );
 
+	it( 'should add selected class when no only a widget is selected', () => {
+		setModelData( doc, '[<paragraph>foo</paragraph><widget></widget><widget></widget>]' );
+
+		expect( viewDocument.selection.isFake ).to.be.false;
+		expect( getViewData( viewDocument ) ).to.equal(
+			'[' +
+			'<p>foo</p>' +
+			'<div class="ck-widget ck-widget_selected" contenteditable="false"><b></b></div>' +
+			'<div class="ck-widget ck-widget_selected" contenteditable="false"><b></b></div>' +
+			']'
+		);
+	} );
+
 	it( 'fake selection should be empty if widget is not selected', () => {
 		setModelData( doc, '<paragraph>foo</paragraph><widget>foo bar</widget>' );
 
@@ -1075,9 +1088,18 @@ describe( 'Widget', () => {
 				{ keyCode: keyCodes.a, ctrlKey: true },
 				'<widget><nested>foo</nested></widget><paragraph>[]bar</paragraph>'
 			);
+
+			test(
+				'should selected whole content when widget is selected',
+				'<paragraph>foo</paragraph>[<widget></widget>]<paragraph>bar</paragraph>',
+				{ keyCode: keyCodes.a, ctrlKey: true },
+				'[<paragraph>foo</paragraph><widget></widget><paragraph>bar</paragraph>]',
+				'[<p>foo</p><div class="ck-widget ck-widget_selected" contenteditable="false"><b></b></div><p>bar</p>]'
+
+			);
 		} );
 
-		function test( name, data, keyCodeOrMock, expected ) {
+		function test( name, data, keyCodeOrMock, expected, expectedView ) {
 			it( name, () => {
 				const domEventDataMock = ( typeof keyCodeOrMock == 'object' ) ? keyCodeOrMock : {
 					keyCode: keyCodeOrMock
@@ -1091,6 +1113,10 @@ describe( 'Widget', () => {
 				) );
 
 				expect( getModelData( doc ) ).to.equal( expected );
+
+				if ( expectedView ) {
+					expect( getViewData( viewDocument ) ).to.equal( expectedView );
+				}
 			} );
 		}
 	} );