瀏覽代碼

Feature: Ctrl+A in a nested editable should select nested editable's content. Closes #13.

Aleksander Nowodzinski 8 年之前
父節點
當前提交
e7863c58a3
共有 2 個文件被更改,包括 60 次插入5 次删除
  1. 43 5
      packages/ckeditor5-widget/src/widget.js
  2. 17 0
      packages/ckeditor5-widget/tests/widget.js

+ 43 - 5
packages/ckeditor5-widget/src/widget.js

@@ -15,10 +15,12 @@ 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, WIDGET_SELECTED_CLASS_NAME, getLabel } from './utils';
-import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import { keyCodes, getCode, parseKeystroke } from '@ckeditor/ckeditor5-utils/src/keyboard';
 
 import '../theme/theme.scss';
 
+const selectAllKeystrokeCode = parseKeystroke( 'Ctrl+A' );
+
 /**
  * The widget plugin.
  * Registers model to view selection converter for editing pipeline. It is hooked after default selection conversion.
@@ -126,10 +128,13 @@ export default class Widget extends Plugin {
 		const keyCode = domEventData.keyCode;
 		const isForward = keyCode == keyCodes.delete || keyCode == keyCodes.arrowdown || keyCode == keyCodes.arrowright;
 
-		// Checks if delete/backspace or arrow keys were handled and then prevents default event behaviour and stops
-		// event propagation.
-		if ( ( isDeleteKeyCode( keyCode ) && this._handleDelete( isForward ) ) ||
-			( isArrowKeyCode( keyCode ) && this._handleArrowKeys( isForward ) ) ) {
+		// Checks if the keys were handled and then prevents the default event behaviour and stops
+		// the propagation.
+		if (
+			( isDeleteKeyCode( keyCode ) && this._handleDelete( isForward ) ) ||
+			( isArrowKeyCode( keyCode ) && this._handleArrowKeys( isForward ) ) ||
+			( isSelectAllKeyCode( domEventData ) && this._selectAllNestedEditableContent() )
+		) {
 			domEventData.preventDefault();
 			eventInfo.stop();
 		}
@@ -214,6 +219,31 @@ export default class Widget extends Plugin {
 	}
 
 	/**
+	 * 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.
+	 *
+	 * See: {@link module:engine/model/schema~Schema#getLimitElement}.
+	 *
+	 * @private
+	 */
+	_selectAllNestedEditableContent() {
+		const modelDocument = this.editor.document;
+		const modelSelection = modelDocument.selection;
+		const schema = modelDocument.schema;
+		const limitElement = schema.getLimitElement( modelSelection );
+
+		if ( modelSelection.getFirstRange().root == limitElement ) {
+			return false;
+		}
+
+		modelDocument.enqueueChanges( () => {
+			modelSelection.setIn( limitElement );
+		} );
+
+		return true;
+	}
+
+	/**
 	 * Sets {@link module:engine/model/selection~Selection document's selection} over given element.
 	 *
 	 * @private
@@ -271,6 +301,14 @@ function isDeleteKeyCode( keyCode ) {
 	return keyCode == keyCodes.delete || keyCode == keyCodes.backspace;
 }
 
+// Returns 'true' if provided (DOM) key event data corresponds with the Ctrl+A keystroke.
+//
+// @param {module:engine/view/observer/keyobserver~KeyEventData} domEventData
+// @returns {Boolean}
+function isSelectAllKeyCode( domEventData ) {
+	return getCode( domEventData ) == selectAllKeystrokeCode;
+}
+
 // Returns `true` when element is a nested editable or is placed inside one.
 //
 // @param {module:engine/view/element~Element}

+ 17 - 0
packages/ckeditor5-widget/tests/widget.js

@@ -34,6 +34,7 @@ describe( 'Widget', () => {
 				doc.schema.registerItem( 'inline', '$inline' );
 				doc.schema.objects.add( 'inline' );
 				doc.schema.registerItem( 'nested' );
+				doc.schema.limits.add( 'nested' );
 				doc.schema.allow( { name: '$inline', inside: 'nested' } );
 				doc.schema.allow( { name: 'nested', inside: 'widget' } );
 				doc.schema.registerItem( 'editable' );
@@ -744,6 +745,22 @@ describe( 'Widget', () => {
 			);
 		} );
 
+		describe( 'Ctrl+A', () => {
+			test(
+				'should select the entire content of the nested editable',
+				'<widget><nested>foo[]</nested></widget><paragraph>bar</paragraph>',
+				{ keyCode: keyCodes.a, ctrlKey: true },
+				'<widget><nested>[foo]</nested></widget><paragraph>bar</paragraph>'
+			);
+
+			test(
+				'should not change the selection if outside of the nested editable',
+				'<widget><nested>foo</nested></widget><paragraph>[]bar</paragraph>',
+				{ keyCode: keyCodes.a, ctrlKey: true },
+				'<widget><nested>foo</nested></widget><paragraph>[]bar</paragraph>'
+			);
+		} );
+
 		function test( name, data, keyCodeOrMock, expected ) {
 			it( name, () => {
 				const domEventDataMock = ( typeof keyCodeOrMock == 'object' ) ? keyCodeOrMock : {