Kaynağa Gözat

Merge branch 'master' into memory-test

Maciej Gołaszewski 5 yıl önce
ebeveyn
işleme
9d3c9c66ef

+ 5 - 1
packages/ckeditor5-select-all/docs/features/select-all.md

@@ -9,10 +9,14 @@ The {@link module:select-all/selectall~SelectAll} feature allows selecting the e
 
 ## Demo
 
-Press <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> or use the toolbar button to select the entire content of the editor. Note that when editing an {@link features/image#image-captions image caption}, the selection will only expand to the boundaries of the caption.
+Press <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> or use the toolbar button to select the entire content of the editor.
 
 {@snippet features/select-all}
 
+<info-box>
+	When the selection is inside the {@link features/image#image-captions image caption}, it will only expand to the boundaries of the caption. Use the keystroke or the toolbar button again to include more content until the entire content of the editor is selected. The same rule applies, for instance, when the selection is inside a table cell or any self–contained (nested) editable region in the content.
+</info-box>
+
 ## Installation
 
 <info-box info>

+ 29 - 3
packages/ckeditor5-select-all/src/selectallcommand.js

@@ -20,7 +20,8 @@ import Command from '@ckeditor/ckeditor5-core/src/command';
  * {@link module:engine/model/selection~Selection#anchor anchored} in.
  *
  * If the selection was anchored in a {@glink framework/guides/tutorials/implementing-a-block-widget nested editable}
- * (e.g. a caption of an image), the new selection will contain its entire content.
+ * (e.g. a caption of an image), the new selection will contain its entire content. Successive executions of this command
+ * will expand the selection to encompass more and more content up to the entire editable root of the editor.
  *
  * @extends module:core/command~Command
  */
@@ -30,10 +31,35 @@ export default class SelectAllCommand extends Command {
 	 */
 	execute() {
 		const model = this.editor.model;
-		const limitElement = model.schema.getLimitElement( model.document.selection );
+		const selection = model.document.selection;
+		let scopeElement = model.schema.getLimitElement( selection );
+
+		// If an entire scope is selected, or the selection's ancestor is not a scope yet,
+		// browse through ancestors to find the enclosing parent scope.
+		if ( selection.containsEntireContent( scopeElement ) || !isSelectAllScope( model.schema, scopeElement ) ) {
+			do {
+				scopeElement = scopeElement.parent;
+
+				// Do nothing, if the entire `root` is already selected.
+				if ( !scopeElement ) {
+					return;
+				}
+			} while ( !isSelectAllScope( model.schema, scopeElement ) );
+		}
 
 		model.change( writer => {
-			writer.setSelection( limitElement, 'in' );
+			writer.setSelection( scopeElement, 'in' );
 		} );
 	}
 }
+
+// Checks whether the element is a valid select-all scope.
+// Returns true, if the element is a {@link module:engine/model/schema~Schema#isLimit limit},
+// and can contain any text or paragraph.
+//
+// @param {module:engine/model/schema~Schema} schema The schema to check against.
+// @param {module:engine/model/element~Element} element
+// @return {Boolean}
+function isSelectAllScope( schema, element ) {
+	return schema.isLimit( element ) && ( schema.checkChild( element, '$text' ) || schema.checkChild( element, 'paragraph' ) );
+}

+ 115 - 1
packages/ckeditor5-select-all/tests/selectallcommand.js

@@ -70,13 +70,51 @@ describe( 'SelectAllCommand', () => {
 		} );
 
 		it( 'should select all (selection in a nested editable)', () => {
-			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>b[ar]</caption></image>' );
 
 			editor.execute( 'selectAll' );
 
 			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
 		} );
 
+		it( 'should select all (selection within limit element)', () => {
+			setData( model,
+				'<paragraph>foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+						'</tableCell>' +
+						'[<tableCell>' +
+							'<paragraph>bar</paragraph>' +
+						'</tableCell>]' +
+						'[<tableCell>' +
+							'<paragraph>baz</paragraph>' +
+						'</tableCell>]' +
+					'</tableRow>' +
+				'</table>'
+			);
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal(
+				'<paragraph>[foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+						'</tableCell>' +
+						'<tableCell>' +
+							'<paragraph>bar</paragraph>' +
+						'</tableCell>' +
+						'<tableCell>' +
+							'<paragraph>baz</paragraph>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>]'
+			);
+		} );
+
 		it( 'should select all in the closest nested editable (nested editable inside another nested editable)', () => {
 			setData( model,
 				'<paragraph>foo</paragraph>' +
@@ -103,5 +141,81 @@ describe( 'SelectAllCommand', () => {
 				'</table>'
 			);
 		} );
+
+		it( 'should select all in the parent select-all-limit element (the entire editable is selected)', () => {
+			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph><image src="foo.png"><caption>bar</caption></image>]' );
+		} );
+
+		it( 'should select all in the parent sellect-all-limit element (consecutive execute() on a nested editable)', () => {
+			setData( model,
+				'<paragraph>foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+							'<image src="foo.png"><caption>b[]ar</caption></image>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>'
+			);
+
+			editor.execute( 'selectAll' );
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>[foo</paragraph>' +
+							'<image src="foo.png"><caption>bar</caption></image>]' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>'
+			);
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+							'<image src="foo.png"><caption>bar</caption></image>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>]'
+			);
+		} );
+
+		it( 'should not change the selection (the entire editor is selected)', () => {
+			setData( model,
+				'<paragraph>[foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+							'<image src="foo.png"><caption>bar</caption></image>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>]'
+			);
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+							'<image src="foo.png"><caption>bar</caption></image>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>]'
+			);
+		} );
 	} );
 } );