Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit f67aea1

Browse files
authored
Merge pull request #172 from ckeditor/t/168
Feature: Introduced a static `Rect#getDomRangeRects` method for external usage. Closes #168.
2 parents f86b1ad + 37bb62b commit f67aea1

File tree

2 files changed

+82
-22
lines changed

2 files changed

+82
-22
lines changed

src/dom/rect.js

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,25 +55,7 @@ export default class Rect {
5555
if ( isElement( source ) ) {
5656
copyRectProperties( this, source.getBoundingClientRect() );
5757
} else if ( isRange( source ) ) {
58-
// Use getClientRects() when the range is collapsed.
59-
// https://github.com/ckeditor/ckeditor5-utils/issues/153
60-
if ( source.collapsed ) {
61-
const rects = source.getClientRects();
62-
63-
if ( rects.length ) {
64-
copyRectProperties( this, rects[ 0 ] );
65-
}
66-
// If there's no client rects for the Range, use parent container's bounding
67-
// rect instead and adjust rect's width to simulate the actual geometry of such
68-
// range.
69-
// https://github.com/ckeditor/ckeditor5-utils/issues/153
70-
else {
71-
copyRectProperties( this, source.startContainer.getBoundingClientRect() );
72-
this.width = 0;
73-
}
74-
} else {
75-
copyRectProperties( this, source.getBoundingClientRect() );
76-
}
58+
copyRectProperties( this, Rect.getDomRangeRects( source )[ 0 ] );
7759
} else {
7860
copyRectProperties( this, source );
7961
}
@@ -268,6 +250,36 @@ export default class Rect {
268250
height: innerHeight
269251
} );
270252
}
253+
254+
/**
255+
* Returns an array of rects of the given native DOM Range.
256+
*
257+
* @param {Range} range A native DOM range.
258+
* @returns {Array.<module:utils/dom/rect~Rect>} DOM Range rects.
259+
*/
260+
static getDomRangeRects( range ) {
261+
const rects = [];
262+
// Safari does not iterate over ClientRectList using for...of loop.
263+
const clientRects = Array.from( range.getClientRects() );
264+
265+
if ( clientRects.length ) {
266+
for ( const rect of clientRects ) {
267+
rects.push( new Rect( rect ) );
268+
}
269+
}
270+
// If there's no client rects for the Range, use parent container's bounding rect
271+
// instead and adjust rect's width to simulate the actual geometry of such range.
272+
// https://github.com/ckeditor/ckeditor5-utils/issues/153
273+
else {
274+
const startContainerRect = new Rect( range.startContainer.getBoundingClientRect() );
275+
startContainerRect.right = startContainerRect.left;
276+
startContainerRect.width = 0;
277+
278+
rects.push( startContainerRect );
279+
}
280+
281+
return rects;
282+
}
271283
}
272284

273285
const rectProperties = [ 'top', 'right', 'bottom', 'left', 'width', 'height' ];

tests/dom/rect.js

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe( 'Rect', () => {
4545
const range = document.createRange();
4646

4747
range.selectNode( document.body );
48-
testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( geometry );
48+
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
4949

5050
assertRect( new Rect( range ), geometry );
5151
} );
@@ -71,6 +71,7 @@ describe( 'Rect', () => {
7171
testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
7272

7373
const expectedGeometry = Object.assign( {}, geometry );
74+
expectedGeometry.right = expectedGeometry.left;
7475
expectedGeometry.width = 0;
7576

7677
assertRect( new Rect( range ), expectedGeometry );
@@ -510,14 +511,14 @@ describe( 'Rect', () => {
510511
range.setStart( ancestorA, 0 );
511512
range.setEnd( ancestorA, 1 );
512513

513-
testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( {
514+
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ {
514515
top: 0,
515516
right: 100,
516517
bottom: 100,
517518
left: 0,
518519
width: 100,
519520
height: 100
520-
} );
521+
} ] );
521522

522523
testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
523524
top: 50,
@@ -584,6 +585,53 @@ describe( 'Rect', () => {
584585
} );
585586
} );
586587
} );
588+
589+
describe( 'getDomRangeRects() ', () => {
590+
it( 'should return rects for a Range (non–collapsed)', () => {
591+
const range = document.createRange();
592+
593+
range.selectNode( document.body );
594+
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
595+
596+
const rects = Rect.getDomRangeRects( range );
597+
expect( rects ).to.have.length( 1 );
598+
assertRect( rects[ 0 ], geometry );
599+
} );
600+
601+
// https://github.com/ckeditor/ckeditor5-utils/issues/153
602+
it( 'should return rects for a Range (collapsed)', () => {
603+
const range = document.createRange();
604+
const secondGeometry = { top: 20, right: 80, bottom: 60, left: 40, width: 40, height: 40 };
605+
606+
range.collapse();
607+
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry, secondGeometry ] );
608+
609+
const rects = Rect.getDomRangeRects( range );
610+
expect( rects ).to.have.length( 2 );
611+
612+
assertRect( rects[ 0 ], geometry );
613+
assertRect( rects[ 1 ], secondGeometry );
614+
} );
615+
616+
// https://github.com/ckeditor/ckeditor5-utils/issues/153
617+
it( 'should return rects for a Range (collapsed, no Range rects available)', () => {
618+
const range = document.createRange();
619+
const element = document.createElement( 'div' );
620+
621+
range.setStart( element, 0 );
622+
range.collapse();
623+
testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
624+
testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
625+
626+
const expectedGeometry = Object.assign( {}, geometry );
627+
expectedGeometry.right = expectedGeometry.left;
628+
expectedGeometry.width = 0;
629+
630+
const rects = Rect.getDomRangeRects( range );
631+
expect( rects ).to.have.length( 1 );
632+
assertRect( rects[ 0 ], expectedGeometry );
633+
} );
634+
} );
587635
} );
588636

589637
function assertRect( rect, expected ) {

0 commit comments

Comments
 (0)