igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
127 lines (118 loc) • 5.55 kB
text/typescript
import { ConnectedFit, HorizontalAlignment, Point, PositionSettings, Size, Util, VerticalAlignment } from '../utilities';
import { ConnectedPositioningStrategy } from './connected-positioning-strategy';
export abstract class BaseFitPositionStrategy extends ConnectedPositioningStrategy {
protected _initialSize: Size;
protected _initialSettings: PositionSettings;
/**
* Position the element based on the PositionStrategy implementing this interface.
*
* @param contentElement The HTML element to be positioned
* @param size Size of the element
* @param document reference to the Document object
* @param initialCall should be true if this is the initial call to the method
* @param target attaching target for the component to show
* ```typescript
* settings.positionStrategy.position(content, size, document, true);
* ```
*/
public override position(
contentElement: HTMLElement, size: Size, document?: Document, initialCall?: boolean, target?: Point | HTMLElement): void {
const targetElement = target || this.settings.target;
const rects = super.calculateElementRectangles(contentElement, targetElement);
const connectedFit: ConnectedFit = {};
if (initialCall) {
connectedFit.targetRect = rects.targetRect;
connectedFit.contentElementRect = rects.elementRect;
this._initialSettings = this._initialSettings || Object.assign({}, this.settings);
this.settings = Object.assign({}, this._initialSettings);
connectedFit.viewPortRect = Util.getViewportRect(document);
this.updateViewPortFit(connectedFit);
if (this.shouldFitInViewPort(connectedFit)) {
this.fitInViewport(contentElement, connectedFit);
}
}
this.setStyle(contentElement, rects.targetRect, rects.elementRect, connectedFit);
}
/**
* Checks if element can fit in viewport and updates provided connectedFit
* with the result
*
* @param connectedFit connectedFit to update
*/
protected updateViewPortFit(connectedFit: ConnectedFit) {
connectedFit.left = this.calculateLeft(
connectedFit.targetRect,
connectedFit.contentElementRect,
this.settings.horizontalStartPoint,
this.settings.horizontalDirection,
connectedFit.horizontalOffset ? connectedFit.horizontalOffset : 0);
connectedFit.right = connectedFit.left + connectedFit.contentElementRect.width;
connectedFit.fitHorizontal = {
back: Math.round(connectedFit.left),
forward: Math.round(connectedFit.viewPortRect.width - connectedFit.right)
};
connectedFit.top = this.calculateTop(
connectedFit.targetRect,
connectedFit.contentElementRect,
this.settings.verticalStartPoint,
this.settings.verticalDirection,
connectedFit.verticalOffset ? connectedFit.verticalOffset : 0);
connectedFit.bottom = connectedFit.top + connectedFit.contentElementRect.height;
connectedFit.fitVertical = {
back: Math.round(connectedFit.top),
forward: Math.round(connectedFit.viewPortRect.height - connectedFit.bottom)
};
}
/**
* Calculates the position of the left border of the element if it gets positioned
* with provided start point and direction
*
* @param targetRect Rectangle of the target where element is attached
* @param elementRect Rectangle of the element
* @param startPoint Start point of the target
* @param direction Direction in which to show the element
*/
protected calculateLeft(
targetRect: Partial<DOMRect>,
elementRect: Partial<DOMRect>,
startPoint: HorizontalAlignment,
direction: HorizontalAlignment,
offset?: number): number {
return targetRect.right + targetRect.width * startPoint + elementRect.width * direction + offset;
}
/**
* Calculates the position of the top border of the element if it gets positioned
* with provided position settings related to the target
*
* @param targetRect Rectangle of the target where element is attached
* @param elementRect Rectangle of the element
* @param startPoint Start point of the target
* @param direction Direction in which to show the element
*/
protected calculateTop(
targetRect: Partial<DOMRect>,
elementRect: Partial<DOMRect>,
startPoint: VerticalAlignment,
direction: VerticalAlignment,
offset?: number): number {
return targetRect.bottom + targetRect.height * startPoint + elementRect.height * direction + offset;
}
/**
* Returns whether the element should fit in viewport
*
* @param connectedFit connectedFit object containing all necessary parameters
*/
protected shouldFitInViewPort(connectedFit: ConnectedFit) {
return connectedFit.fitHorizontal.back < 0 || connectedFit.fitHorizontal.forward < 0 ||
connectedFit.fitVertical.back < 0 || connectedFit.fitVertical.forward < 0;
}
/**
* Fits the element into viewport according to the position settings
*
* @param element element to fit in viewport
* @param connectedFit connectedFit object containing all necessary parameters
*/
protected abstract fitInViewport(
element: HTMLElement,
connectedFit: ConnectedFit);
}