8
0
Просмотр исходного кода

Fix: Rect utility should work for collapsed DOM Ranges. Closes #153.

Aleksander Nowodzinski 8 лет назад
Родитель
Сommit
14e38d6c63
2 измененных файлов с 61 добавлено и 5 удалено
  1. 32 4
      packages/ckeditor5-utils/src/dom/rect.js
  2. 29 1
      packages/ckeditor5-utils/tests/dom/rect.js

+ 32 - 4
packages/ckeditor5-utils/src/dom/rect.js

@@ -54,11 +54,39 @@ export default class Rect {
 			enumerable: false
 		} );
 
-		if ( isElement( source ) || isRange( source ) ) {
-			source = source.getBoundingClientRect();
-		}
+		// Acquires all the rect properties from the passed source.
+		//
+		// @private
+		// @param {ClientRect|module:utils/dom/rect~Rect|Object} source}
+		const setProperties = source => {
+			rectProperties.forEach( p => this[ p ] = source[ p ] );
+		};
 
-		rectProperties.forEach( p => this[ p ] = source[ p ] );
+		if ( isElement( source ) ) {
+			setProperties( source.getBoundingClientRect() );
+		} else if ( isRange( source ) ) {
+			// Use getClientRects() when the range is collapsed
+			// https://github.com/ckeditor/ckeditor5-utils/issues/153
+			if ( source.collapsed ) {
+				const rects = source.getClientRects();
+
+				if ( rects.length ) {
+					setProperties( rects[ 0 ] );
+				}
+				// If there's no client rects for the Range, use parent container's bounding
+				// rect instead and adjust rect's width to simulate the actual geometry of such
+				// range.
+				// https://github.com/ckeditor/ckeditor5-utils/issues/153
+				else {
+					setProperties( source.startContainer.getBoundingClientRect() );
+					this.width = 0;
+				}
+			} else {
+				setProperties( source.getBoundingClientRect() );
+			}
+		} else {
+			setProperties( source );
+		}
 
 		/**
 		 * The "top" value of the rect.

+ 29 - 1
packages/ckeditor5-utils/tests/dom/rect.js

@@ -41,14 +41,41 @@ describe( 'Rect', () => {
 			assertRect( new Rect( element ), geometry );
 		} );
 
-		it( 'should accept Range', () => {
+		it( 'should accept Range (non–collapsed)', () => {
 			const range = document.createRange();
 
+			range.selectNode( document.body );
 			testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( geometry );
 
 			assertRect( new Rect( range ), geometry );
 		} );
 
+		// https://github.com/ckeditor/ckeditor5-utils/issues/153
+		it( 'should accept Range (collapsed)', () => {
+			const range = document.createRange();
+
+			range.collapse();
+			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
+
+			assertRect( new Rect( range ), geometry );
+		} );
+
+		// https://github.com/ckeditor/ckeditor5-utils/issues/153
+		it( 'should accept Range (collapsed, no Range rects available)', () => {
+			const range = document.createRange();
+			const element = document.createElement( 'div' );
+
+			range.setStart( element, 0 );
+			range.collapse();
+			testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
+			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+
+			const expectedGeometry = Object.assign( {}, geometry );
+			expectedGeometry.width = 0;
+
+			assertRect( new Rect( range ), expectedGeometry );
+		} );
+
 		it( 'should accept Rect', () => {
 			const sourceRect = new Rect( geometry );
 			const rect = new Rect( sourceRect );
@@ -481,6 +508,7 @@ describe( 'Rect', () => {
 
 		it( 'should return the visible rect (Range), partially cropped', () => {
 			range.setStart( ancestorA, 0 );
+			range.setEnd( ancestorA, 1 );
 
 			testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( {
 				top: 0,