chore(overlay): refactor clipping detection calculation to its own file (#6504)

This commit is contained in:
Jeremy Elbourn
2017-08-21 15:55:09 -07:00
committed by Kara
parent 90a6ac95e5
commit 5f75939a01
3 changed files with 62 additions and 64 deletions
@@ -13,23 +13,14 @@ import {
ConnectionPositionPair,
OriginConnectionPosition,
OverlayConnectionPosition,
ConnectedOverlayPositionChange, ScrollableViewProperties
ConnectedOverlayPositionChange,
ScrollingVisibility,
} from './connected-position';
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
import {Scrollable} from '../scroll/scrollable';
import {isElementScrolledOutsideView, isElementClippedByScrolling} from './scroll-clip';
/**
* Container to hold the bounding positions of a particular element with respect to the viewport,
* where top and bottom are the y-axis coordinates of the bounding rectangle and left and right are
* the x-axis coordinates.
*/
type ElementBoundingPositions = {
top: number;
right: number;
bottom: number;
left: number;
};
/**
* A strategy for positioning overlays. Using this strategy, an overlay is given an
@@ -306,49 +297,20 @@ export class ConnectedPositionStrategy implements PositionStrategy {
* Gets the view properties of the trigger and overlay, including whether they are clipped
* or completely outside the view of any of the strategy's scrollables.
*/
private getScrollableViewProperties(overlay: HTMLElement): ScrollableViewProperties {
const originBounds = this._getElementBounds(this._origin);
const overlayBounds = this._getElementBounds(overlay);
const scrollContainerBounds = this.scrollables.map((scrollable: Scrollable) => {
return this._getElementBounds(scrollable.getElementRef().nativeElement);
});
private _getScrollVisibility(overlay: HTMLElement): ScrollingVisibility {
const originBounds = this._origin.getBoundingClientRect();
const overlayBounds = overlay.getBoundingClientRect();
const scrollContainerBounds =
this.scrollables.map(s => s.getElementRef().nativeElement.getBoundingClientRect());
return {
isOriginClipped: this.isElementClipped(originBounds, scrollContainerBounds),
isOriginOutsideView: this.isElementOutsideView(originBounds, scrollContainerBounds),
isOverlayClipped: this.isElementClipped(overlayBounds, scrollContainerBounds),
isOverlayOutsideView: this.isElementOutsideView(overlayBounds, scrollContainerBounds),
isOriginClipped: isElementClippedByScrolling(originBounds, scrollContainerBounds),
isOriginOutsideView: isElementScrolledOutsideView(originBounds, scrollContainerBounds),
isOverlayClipped: isElementClippedByScrolling(overlayBounds, scrollContainerBounds),
isOverlayOutsideView: isElementScrolledOutsideView(overlayBounds, scrollContainerBounds),
};
}
/** Whether the element is completely out of the view of any of the containers. */
private isElementOutsideView(
elementBounds: ElementBoundingPositions,
containersBounds: ElementBoundingPositions[]): boolean {
return containersBounds.some((containerBounds: ElementBoundingPositions) => {
const outsideAbove = elementBounds.bottom < containerBounds.top;
const outsideBelow = elementBounds.top > containerBounds.bottom;
const outsideLeft = elementBounds.right < containerBounds.left;
const outsideRight = elementBounds.left > containerBounds.right;
return outsideAbove || outsideBelow || outsideLeft || outsideRight;
});
}
/** Whether the element is clipped by any of the containers. */
private isElementClipped(
elementBounds: ElementBoundingPositions,
containersBounds: ElementBoundingPositions[]): boolean {
return containersBounds.some((containerBounds: ElementBoundingPositions) => {
const clippedAbove = elementBounds.top < containerBounds.top;
const clippedBelow = elementBounds.bottom > containerBounds.bottom;
const clippedLeft = elementBounds.left < containerBounds.left;
const clippedRight = elementBounds.right > containerBounds.right;
return clippedAbove || clippedBelow || clippedLeft || clippedRight;
});
}
/** Physically positions the overlay element to the given coordinate. */
private _setElementPosition(
element: HTMLElement,
@@ -392,22 +354,11 @@ export class ConnectedPositionStrategy implements PositionStrategy {
element.style[horizontalStyleProperty] = `${x}px`;
// Notify that the position has been changed along with its change properties.
const scrollableViewProperties = this.getScrollableViewProperties(element);
const scrollableViewProperties = this._getScrollVisibility(element);
const positionChange = new ConnectedOverlayPositionChange(pos, scrollableViewProperties);
this._onPositionChange.next(positionChange);
}
/** Returns the bounding positions of the provided element with respect to the viewport. */
private _getElementBounds(element: HTMLElement): ElementBoundingPositions {
const boundingClientRect = element.getBoundingClientRect();
return {
top: boundingClientRect.top,
right: boundingClientRect.left + boundingClientRect.width,
bottom: boundingClientRect.top + boundingClientRect.height,
left: boundingClientRect.left
};
}
/**
* Subtracts the amount that an element is overflowing on an axis from it's length.
*/
@@ -64,7 +64,7 @@ export class ConnectionPositionPair {
* | |
* --------------------------
*/
export class ScrollableViewProperties {
export class ScrollingVisibility {
isOriginClipped: boolean;
isOriginOutsideView: boolean;
isOverlayClipped: boolean;
@@ -74,5 +74,5 @@ export class ScrollableViewProperties {
/** The change event emitted by the strategy when a fallback position is used. */
export class ConnectedOverlayPositionChange {
constructor(public connectionPair: ConnectionPositionPair,
@Optional() public scrollableViewProperties: ScrollableViewProperties) {}
@Optional() public scrollableViewProperties: ScrollingVisibility) {}
}
+47
View File
@@ -0,0 +1,47 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// TODO(jelbourn): move this to live with the rest of the scrolling code
// TODO(jelbourn): someday replace this with IntersectionObservers
/**
* Gets whether an element is scrolled outside of view by any of its parent scrolling containers.
* @param element Dimensions of the element (from getBoundingClientRect)
* @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
* @returns Whether the element is scrolled out of view
* @docs-private
*/
export function isElementScrolledOutsideView(element: ClientRect, scrollContainers: ClientRect[]) {
return scrollContainers.some(containerBounds => {
const outsideAbove = element.bottom < containerBounds.top;
const outsideBelow = element.top > containerBounds.bottom;
const outsideLeft = element.right < containerBounds.left;
const outsideRight = element.left > containerBounds.right;
return outsideAbove || outsideBelow || outsideLeft || outsideRight;
});
}
/**
* Gets whether an element is clipped by any of its scrolling containers.
* @param element Dimensions of the element (from getBoundingClientRect)
* @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
* @returns Whether the element is clipped
* @docs-private
*/
export function isElementClippedByScrolling(element: ClientRect, scrollContainers: ClientRect[]) {
return scrollContainers.some(scrollContainerRect => {
const clippedAbove = element.top < scrollContainerRect.top;
const clippedBelow = element.bottom > scrollContainerRect.bottom;
const clippedLeft = element.left < scrollContainerRect.left;
const clippedRight = element.right > scrollContainerRect.right;
return clippedAbove || clippedBelow || clippedLeft || clippedRight;
});
}