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

Commit e5ea25f

Browse files
authored
Merge pull request #233 from ckeditor/t/227
Fix: Contextual toolbar should re–position correctly on window scroll. Closes #227.
2 parents 2107ffb + 651c652 commit e5ea25f

File tree

4 files changed

+41
-18
lines changed

4 files changed

+41
-18
lines changed

src/panel/balloon/balloonpanelview.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,19 +251,27 @@ export default class BalloonPanelView extends View {
251251
this.attachTo( options );
252252

253253
const limiter = options.limiter || defaultLimiterElement;
254-
let target = null;
254+
let targetElement = null;
255255

256256
// We need to take HTMLElement related to the target if it is possible.
257257
if ( isElement( options.target ) ) {
258-
target = options.target;
258+
targetElement = options.target;
259259
} else if ( isRange( options.target ) ) {
260-
target = options.target.commonAncestorContainer;
260+
targetElement = options.target.commonAncestorContainer;
261261
}
262262

263263
// Then we need to listen on scroll event of eny element in the document.
264264
this.listenTo( global.document, 'scroll', ( evt, domEvt ) => {
265-
// We need to update position if scrolled element contains related to the balloon elements.
266-
if ( ( target && domEvt.target.contains( target ) ) || domEvt.target.contains( limiter ) ) {
265+
const scrollTarget = domEvt.target;
266+
267+
// The position needs to be updated if the positioning target is within the scrolled element.
268+
const isWithinScrollTarget = targetElement && scrollTarget.contains( targetElement );
269+
270+
// The position needs to be updated if the positioning limiter is within the scrolled element.
271+
const isLimiterWithinScrollTarget = scrollTarget.contains( limiter );
272+
273+
// The positioning target can be a Rect, object etc.. There's no way to optimize the listener then.
274+
if ( isWithinScrollTarget || isLimiterWithinScrollTarget || !targetElement ) {
267275
this.attachTo( options );
268276
}
269277
}, { useCapture: true } );

src/toolbar/contextual/contextualtoolbar.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -217,16 +217,20 @@ export default class ContextualToolbar extends Plugin {
217217
// Get direction of the selection.
218218
const isBackward = editingView.selection.isBackward;
219219

220-
// getBoundingClientRect() makes no sense when the selection spans across number
221-
// of lines of text. Using getClientRects() allows us to browse micro–ranges
222-
// that would normally make up the bounding client rect.
223-
const rangeRects = editingView.domConverter.viewRangeToDom( editingView.selection.getFirstRange() ).getClientRects();
224-
225-
// Select the proper range rect depending on the direction of the selection.
226-
const rangeRect = isBackward ? rangeRects.item( 0 ) : rangeRects.item( rangeRects.length - 1 );
227-
228220
return {
229-
target: rangeRect,
221+
// Because the target for BalloonPanelView is a Rect (not DOMRange), it's geometry will stay fixed
222+
// as the window scrolls. To let the BalloonPanelView follow such Rect, is must be continuously
223+
// computed and hence, the target is defined as a function instead of a static value.
224+
// https://github.com/ckeditor/ckeditor5-ui/issues/195
225+
target: () => {
226+
// getBoundingClientRect() makes no sense when the selection spans across number
227+
// of lines of text. Using getClientRects() allows us to browse micro–ranges
228+
// that would normally make up the bounding client rect.
229+
const rangeRects = editingView.domConverter.viewRangeToDom( editingView.selection.getFirstRange() ).getClientRects();
230+
231+
// Select the proper range rect depending on the direction of the selection.
232+
return isBackward ? rangeRects.item( 0 ) : rangeRects.item( rangeRects.length - 1 );
233+
},
230234
positions: isBackward ?
231235
[ defaultPositions.northWestArrowSouth, defaultPositions.southWestArrowNorth ] :
232236
[ defaultPositions.southEastArrowNorth, defaultPositions.northEastArrowSouth ]

tests/panel/balloon/balloonpanelview.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ describe( 'BalloonPanelView', () => {
592592
sinon.assert.calledTwice( attachToSpy );
593593
} );
594594

595-
it( 'should work for rect as a target', () => {
595+
it( 'should work for a Rect as a target', () => {
596596
// Just check if this normally works without errors.
597597
const rect = {};
598598

@@ -604,6 +604,17 @@ describe( 'BalloonPanelView', () => {
604604

605605
sinon.assert.calledTwice( attachToSpy );
606606
} );
607+
608+
// https://github.com/ckeditor/ckeditor5-ui/issues/227
609+
it( 'should react to #scroll from anywhere when the target is not an HTMLElement or Range', () => {
610+
const rect = {};
611+
612+
view.pin( { target: rect } );
613+
sinon.assert.calledOnce( attachToSpy );
614+
615+
notRelatedElement.dispatchEvent( new Event( 'scroll' ) );
616+
sinon.assert.calledTwice( attachToSpy );
617+
} );
607618
} );
608619

609620
describe( 'unpin()', () => {

tests/toolbar/contextual/contextualtoolbar.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ describe( 'ContextualToolbar', () => {
140140
editor.editing.view.isFocused = true;
141141
} );
142142

143-
it( 'should return promise', () => {
143+
it( 'should return a promise', () => {
144144
setData( editor.document, '<paragraph>b[a]r</paragraph>' );
145145

146146
const returned = contextualToolbar._showPanel();
@@ -160,7 +160,7 @@ describe( 'ContextualToolbar', () => {
160160
view: contextualToolbar.toolbarView,
161161
balloonClassName: 'ck-toolbar-container ck-editor-toolbar-container',
162162
position: {
163-
target: forwardSelectionRect,
163+
target: sinon.match( value => value() == backwardSelectionRect ) ,
164164
positions: [ defaultPositions.southEastArrowNorth, defaultPositions.northEastArrowSouth ]
165165
}
166166
} );
@@ -178,7 +178,7 @@ describe( 'ContextualToolbar', () => {
178178
view: contextualToolbar.toolbarView,
179179
balloonClassName: 'ck-toolbar-container ck-editor-toolbar-container',
180180
position: {
181-
target: backwardSelectionRect,
181+
target: sinon.match( value => value() == forwardSelectionRect ),
182182
positions: [ defaultPositions.northWestArrowSouth, defaultPositions.southWestArrowNorth ]
183183
}
184184
} );

0 commit comments

Comments
 (0)