Skip to content

Commit 5f75939

Browse files
jelbournkara
authored andcommitted
chore(overlay): refactor clipping detection calculation to its own file (#6504)
1 parent 90a6ac9 commit 5f75939

File tree

3 files changed

+62
-64
lines changed

3 files changed

+62
-64
lines changed

src/cdk/overlay/position/connected-position-strategy.ts

Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,14 @@ import {
1313
ConnectionPositionPair,
1414
OriginConnectionPosition,
1515
OverlayConnectionPosition,
16-
ConnectedOverlayPositionChange, ScrollableViewProperties
16+
ConnectedOverlayPositionChange,
17+
ScrollingVisibility,
1718
} from './connected-position';
1819
import {Subject} from 'rxjs/Subject';
1920
import {Observable} from 'rxjs/Observable';
2021
import {Scrollable} from '../scroll/scrollable';
22+
import {isElementScrolledOutsideView, isElementClippedByScrolling} from './scroll-clip';
2123

22-
/**
23-
* Container to hold the bounding positions of a particular element with respect to the viewport,
24-
* where top and bottom are the y-axis coordinates of the bounding rectangle and left and right are
25-
* the x-axis coordinates.
26-
*/
27-
type ElementBoundingPositions = {
28-
top: number;
29-
right: number;
30-
bottom: number;
31-
left: number;
32-
};
3324

3425
/**
3526
* A strategy for positioning overlays. Using this strategy, an overlay is given an
@@ -306,49 +297,20 @@ export class ConnectedPositionStrategy implements PositionStrategy {
306297
* Gets the view properties of the trigger and overlay, including whether they are clipped
307298
* or completely outside the view of any of the strategy's scrollables.
308299
*/
309-
private getScrollableViewProperties(overlay: HTMLElement): ScrollableViewProperties {
310-
const originBounds = this._getElementBounds(this._origin);
311-
const overlayBounds = this._getElementBounds(overlay);
312-
const scrollContainerBounds = this.scrollables.map((scrollable: Scrollable) => {
313-
return this._getElementBounds(scrollable.getElementRef().nativeElement);
314-
});
300+
private _getScrollVisibility(overlay: HTMLElement): ScrollingVisibility {
301+
const originBounds = this._origin.getBoundingClientRect();
302+
const overlayBounds = overlay.getBoundingClientRect();
303+
const scrollContainerBounds =
304+
this.scrollables.map(s => s.getElementRef().nativeElement.getBoundingClientRect());
315305

316306
return {
317-
isOriginClipped: this.isElementClipped(originBounds, scrollContainerBounds),
318-
isOriginOutsideView: this.isElementOutsideView(originBounds, scrollContainerBounds),
319-
isOverlayClipped: this.isElementClipped(overlayBounds, scrollContainerBounds),
320-
isOverlayOutsideView: this.isElementOutsideView(overlayBounds, scrollContainerBounds),
307+
isOriginClipped: isElementClippedByScrolling(originBounds, scrollContainerBounds),
308+
isOriginOutsideView: isElementScrolledOutsideView(originBounds, scrollContainerBounds),
309+
isOverlayClipped: isElementClippedByScrolling(overlayBounds, scrollContainerBounds),
310+
isOverlayOutsideView: isElementScrolledOutsideView(overlayBounds, scrollContainerBounds),
321311
};
322312
}
323313

324-
/** Whether the element is completely out of the view of any of the containers. */
325-
private isElementOutsideView(
326-
elementBounds: ElementBoundingPositions,
327-
containersBounds: ElementBoundingPositions[]): boolean {
328-
return containersBounds.some((containerBounds: ElementBoundingPositions) => {
329-
const outsideAbove = elementBounds.bottom < containerBounds.top;
330-
const outsideBelow = elementBounds.top > containerBounds.bottom;
331-
const outsideLeft = elementBounds.right < containerBounds.left;
332-
const outsideRight = elementBounds.left > containerBounds.right;
333-
334-
return outsideAbove || outsideBelow || outsideLeft || outsideRight;
335-
});
336-
}
337-
338-
/** Whether the element is clipped by any of the containers. */
339-
private isElementClipped(
340-
elementBounds: ElementBoundingPositions,
341-
containersBounds: ElementBoundingPositions[]): boolean {
342-
return containersBounds.some((containerBounds: ElementBoundingPositions) => {
343-
const clippedAbove = elementBounds.top < containerBounds.top;
344-
const clippedBelow = elementBounds.bottom > containerBounds.bottom;
345-
const clippedLeft = elementBounds.left < containerBounds.left;
346-
const clippedRight = elementBounds.right > containerBounds.right;
347-
348-
return clippedAbove || clippedBelow || clippedLeft || clippedRight;
349-
});
350-
}
351-
352314
/** Physically positions the overlay element to the given coordinate. */
353315
private _setElementPosition(
354316
element: HTMLElement,
@@ -392,22 +354,11 @@ export class ConnectedPositionStrategy implements PositionStrategy {
392354
element.style[horizontalStyleProperty] = `${x}px`;
393355

394356
// Notify that the position has been changed along with its change properties.
395-
const scrollableViewProperties = this.getScrollableViewProperties(element);
357+
const scrollableViewProperties = this._getScrollVisibility(element);
396358
const positionChange = new ConnectedOverlayPositionChange(pos, scrollableViewProperties);
397359
this._onPositionChange.next(positionChange);
398360
}
399361

400-
/** Returns the bounding positions of the provided element with respect to the viewport. */
401-
private _getElementBounds(element: HTMLElement): ElementBoundingPositions {
402-
const boundingClientRect = element.getBoundingClientRect();
403-
return {
404-
top: boundingClientRect.top,
405-
right: boundingClientRect.left + boundingClientRect.width,
406-
bottom: boundingClientRect.top + boundingClientRect.height,
407-
left: boundingClientRect.left
408-
};
409-
}
410-
411362
/**
412363
* Subtracts the amount that an element is overflowing on an axis from it's length.
413364
*/

src/cdk/overlay/position/connected-position.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class ConnectionPositionPair {
6464
* | |
6565
* --------------------------
6666
*/
67-
export class ScrollableViewProperties {
67+
export class ScrollingVisibility {
6868
isOriginClipped: boolean;
6969
isOriginOutsideView: boolean;
7070
isOverlayClipped: boolean;
@@ -74,5 +74,5 @@ export class ScrollableViewProperties {
7474
/** The change event emitted by the strategy when a fallback position is used. */
7575
export class ConnectedOverlayPositionChange {
7676
constructor(public connectionPair: ConnectionPositionPair,
77-
@Optional() public scrollableViewProperties: ScrollableViewProperties) {}
77+
@Optional() public scrollableViewProperties: ScrollingVisibility) {}
7878
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// TODO(jelbourn): move this to live with the rest of the scrolling code
10+
// TODO(jelbourn): someday replace this with IntersectionObservers
11+
12+
/**
13+
* Gets whether an element is scrolled outside of view by any of its parent scrolling containers.
14+
* @param element Dimensions of the element (from getBoundingClientRect)
15+
* @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
16+
* @returns Whether the element is scrolled out of view
17+
* @docs-private
18+
*/
19+
export function isElementScrolledOutsideView(element: ClientRect, scrollContainers: ClientRect[]) {
20+
return scrollContainers.some(containerBounds => {
21+
const outsideAbove = element.bottom < containerBounds.top;
22+
const outsideBelow = element.top > containerBounds.bottom;
23+
const outsideLeft = element.right < containerBounds.left;
24+
const outsideRight = element.left > containerBounds.right;
25+
26+
return outsideAbove || outsideBelow || outsideLeft || outsideRight;
27+
});
28+
}
29+
30+
31+
/**
32+
* Gets whether an element is clipped by any of its scrolling containers.
33+
* @param element Dimensions of the element (from getBoundingClientRect)
34+
* @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
35+
* @returns Whether the element is clipped
36+
* @docs-private
37+
*/
38+
export function isElementClippedByScrolling(element: ClientRect, scrollContainers: ClientRect[]) {
39+
return scrollContainers.some(scrollContainerRect => {
40+
const clippedAbove = element.top < scrollContainerRect.top;
41+
const clippedBelow = element.bottom > scrollContainerRect.bottom;
42+
const clippedLeft = element.left < scrollContainerRect.left;
43+
const clippedRight = element.right > scrollContainerRect.right;
44+
45+
return clippedAbove || clippedBelow || clippedLeft || clippedRight;
46+
});
47+
}

0 commit comments

Comments
 (0)