projects/swimlane/ngx-charts/src/lib/common/tooltip/position/position.ts
import { PlacementTypes } from './placement-type.enum';
const caretOffset = 7;
function verticalPosition(elDimensions: DOMRect, popoverDimensions: DOMRect, alignment: PlacementTypes): number {
if (alignment === PlacementTypes.Top) {
return elDimensions.top - caretOffset;
}
if (alignment === PlacementTypes.Bottom) {
return elDimensions.top + elDimensions.height - popoverDimensions.height + caretOffset;
}
if (alignment === PlacementTypes.Center) {
return elDimensions.top + elDimensions.height / 2 - popoverDimensions.height / 2;
}
return undefined;
}
function horizontalPosition(elDimensions: DOMRect, popoverDimensions: DOMRect, alignment: PlacementTypes): number {
if (alignment === PlacementTypes.Left) {
return elDimensions.left - caretOffset;
}
if (alignment === PlacementTypes.Right) {
return elDimensions.left + elDimensions.width - popoverDimensions.width + caretOffset;
}
if (alignment === PlacementTypes.Center) {
return elDimensions.left + elDimensions.width / 2 - popoverDimensions.width / 2;
}
return undefined;
}
/**
* Position helper for the popover directive.
*
* @export
*/
export class PositionHelper {
/**
* Calculate vertical alignment position
*
* @memberOf PositionHelper
*/
static calculateVerticalAlignment(
elDimensions: DOMRect,
popoverDimensions: DOMRect,
alignment: PlacementTypes
): number {
let result = verticalPosition(elDimensions, popoverDimensions, alignment);
if (result + popoverDimensions.height > window.innerHeight) {
result = window.innerHeight - popoverDimensions.height;
}
return result;
}
/**
* Calculate vertical caret position
*
* @memberOf PositionHelper
*/
static calculateVerticalCaret(
elDimensions: DOMRect,
popoverDimensions: DOMRect,
caretDimensions: DOMRect,
alignment: PlacementTypes
): number {
let result;
if (alignment === PlacementTypes.Top) {
result = elDimensions.height / 2 - caretDimensions.height / 2 + caretOffset;
}
if (alignment === PlacementTypes.Bottom) {
result = popoverDimensions.height - elDimensions.height / 2 - caretDimensions.height / 2 - caretOffset;
}
if (alignment === PlacementTypes.Center) {
result = popoverDimensions.height / 2 - caretDimensions.height / 2;
}
const popoverPosition = verticalPosition(elDimensions, popoverDimensions, alignment);
if (popoverPosition + popoverDimensions.height > window.innerHeight) {
result += popoverPosition + popoverDimensions.height - window.innerHeight;
}
return result;
}
/**
* Calculate horz alignment position
*
* @memberOf PositionHelper
*/
static calculateHorizontalAlignment(
elDimensions: DOMRect,
popoverDimensions: DOMRect,
alignment: PlacementTypes
): number {
let result = horizontalPosition(elDimensions, popoverDimensions, alignment);
if (result + popoverDimensions.width > window.innerWidth) {
result = window.innerWidth - popoverDimensions.width;
}
return result;
}
/**
* Calculate horz caret position
*
* @memberOf PositionHelper
*/
static calculateHorizontalCaret(
elDimensions: DOMRect,
popoverDimensions: DOMRect,
caretDimensions: DOMRect,
alignment: PlacementTypes
): number {
let result;
if (alignment === PlacementTypes.Left) {
result = elDimensions.width / 2 - caretDimensions.width / 2 + caretOffset;
}
if (alignment === PlacementTypes.Right) {
result = popoverDimensions.width - elDimensions.width / 2 - caretDimensions.width / 2 - caretOffset;
}
if (alignment === PlacementTypes.Center) {
result = popoverDimensions.width / 2 - caretDimensions.width / 2;
}
const popoverPosition = horizontalPosition(elDimensions, popoverDimensions, alignment);
if (popoverPosition + popoverDimensions.width > window.innerWidth) {
result += popoverPosition + popoverDimensions.width - window.innerWidth;
}
return result;
}
/**
* Checks if the element's position should be flipped
*
* @memberOf PositionHelper
*/
static shouldFlip(
elDimensions: DOMRect,
popoverDimensions: DOMRect,
placement: PlacementTypes,
spacing: number
): boolean {
let flip = false;
if (placement === PlacementTypes.Right) {
if (elDimensions.left + elDimensions.width + popoverDimensions.width + spacing > window.innerWidth) {
flip = true;
}
}
if (placement === PlacementTypes.Left) {
if (elDimensions.left - popoverDimensions.width - spacing < 0) {
flip = true;
}
}
if (placement === PlacementTypes.Top) {
if (elDimensions.top - popoverDimensions.height - spacing < 0) {
flip = true;
}
}
if (placement === PlacementTypes.Bottom) {
if (elDimensions.top + elDimensions.height + popoverDimensions.height + spacing > window.innerHeight) {
flip = true;
}
}
return flip;
}
/**
* Position caret
*
* @memberOf PositionHelper
*/
static positionCaret(placement, elmDim, hostDim, caretDimensions, alignment: PlacementTypes): any {
let top = 0;
let left = 0;
if (placement === PlacementTypes.Right) {
left = -7;
top = PositionHelper.calculateVerticalCaret(hostDim, elmDim, caretDimensions, alignment);
} else if (placement === PlacementTypes.Left) {
left = elmDim.width;
top = PositionHelper.calculateVerticalCaret(hostDim, elmDim, caretDimensions, alignment);
} else if (placement === PlacementTypes.Top) {
top = elmDim.height;
left = PositionHelper.calculateHorizontalCaret(hostDim, elmDim, caretDimensions, alignment);
} else if (placement === PlacementTypes.Bottom) {
top = -7;
left = PositionHelper.calculateHorizontalCaret(hostDim, elmDim, caretDimensions, alignment);
}
return { top, left };
}
/**
* Position content
*
* @memberOf PositionHelper
*/
static positionContent(placement, elmDim, hostDim, spacing, alignment): any {
let top = 0;
let left = 0;
if (placement === PlacementTypes.Right) {
left = hostDim.left + hostDim.width + spacing;
top = PositionHelper.calculateVerticalAlignment(hostDim, elmDim, alignment);
} else if (placement === PlacementTypes.Left) {
left = hostDim.left - elmDim.width - spacing;
top = PositionHelper.calculateVerticalAlignment(hostDim, elmDim, alignment);
} else if (placement === PlacementTypes.Top) {
top = hostDim.top - elmDim.height - spacing;
left = PositionHelper.calculateHorizontalAlignment(hostDim, elmDim, alignment);
} else if (placement === PlacementTypes.Bottom) {
top = hostDim.top + hostDim.height + spacing;
left = PositionHelper.calculateHorizontalAlignment(hostDim, elmDim, alignment);
}
return { top, left };
}
/**
* Determine placement based on flip
*
* @memberOf PositionHelper
*/
static determinePlacement(placement: PlacementTypes, elmDim: DOMRect, hostDim: DOMRect, spacing: number): any {
const shouldFlip = PositionHelper.shouldFlip(hostDim, elmDim, placement, spacing);
if (shouldFlip) {
if (placement === PlacementTypes.Right) {
return PlacementTypes.Left;
} else if (placement === PlacementTypes.Left) {
return PlacementTypes.Right;
} else if (placement === PlacementTypes.Top) {
return PlacementTypes.Bottom;
} else if (placement === PlacementTypes.Bottom) {
return PlacementTypes.Top;
}
}
return placement;
}
}