1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/packages/ui-components/tooltip/src/helpers.ts
Janne Savolainen 65f04b6e38 chore: Fix code style
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
2023-05-03 09:31:24 +03:00

141 lines
3.8 KiB
TypeScript

/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { iter } from "@k8slens/utilities";
import { TooltipPosition } from "./tooltip";
export type RectangleDimensions = {
left: number;
width: number;
top: number;
height: number;
bottom: number;
right: number;
};
export type FloatingRectangleDimensions = {
width: number;
height: number;
};
export type DomElementWithRectangle = {
getBoundingClientRect: () => RectangleDimensions;
};
export type DomElementWithFloatingRectangle = {
getBoundingClientRect: () => FloatingRectangleDimensions;
};
export type ComputeNextPositionArgs = {
tooltip: DomElementWithFloatingRectangle;
target: DomElementWithRectangle;
offset: number;
preferredPositions?: TooltipPosition | TooltipPosition[];
};
export type NextPosition = {
position: TooltipPosition;
top: number;
left: number;
};
type GetPositionArgs = {
offset: number;
position: TooltipPosition;
tooltipBounds: FloatingRectangleDimensions;
targetBounds: RectangleDimensions;
};
const getPosition = ({ offset, position, tooltipBounds, targetBounds }: GetPositionArgs) => {
const horizontalCenter = targetBounds.left + (targetBounds.width - tooltipBounds.width) / 2;
const verticalCenter = targetBounds.top + (targetBounds.height - tooltipBounds.height) / 2;
const topCenter = targetBounds.top - tooltipBounds.height - offset;
const bottomCenter = targetBounds.bottom + offset;
const [left, top] = (() => {
switch (position) {
case TooltipPosition.TOP:
return [horizontalCenter, topCenter];
case TooltipPosition.BOTTOM:
return [horizontalCenter, bottomCenter];
case TooltipPosition.LEFT:
return [targetBounds.left - tooltipBounds.width - offset, verticalCenter];
case TooltipPosition.RIGHT:
return [targetBounds.right + offset, verticalCenter];
case TooltipPosition.TOP_LEFT:
return [targetBounds.left, topCenter];
case TooltipPosition.TOP_RIGHT:
return [targetBounds.right - tooltipBounds.width, topCenter];
case TooltipPosition.BOTTOM_LEFT:
return [targetBounds.left, bottomCenter];
case TooltipPosition.BOTTOM_RIGHT:
return [targetBounds.right - tooltipBounds.width, bottomCenter];
}
})();
return {
left,
top,
right: left + tooltipBounds.width,
bottom: top + tooltipBounds.height,
};
};
const isTooltipPosition = (value: unknown): value is TooltipPosition =>
Object.values(TooltipPosition).includes(value as TooltipPosition);
export const computeNextPosition = ({
offset,
preferredPositions,
target,
tooltip,
}: ComputeNextPositionArgs): NextPosition => {
const positions = new Set<TooltipPosition>([
...[preferredPositions ?? []].filter(isTooltipPosition).flat(),
TooltipPosition.RIGHT,
TooltipPosition.BOTTOM,
TooltipPosition.TOP,
TooltipPosition.LEFT,
TooltipPosition.TOP_RIGHT,
TooltipPosition.TOP_LEFT,
TooltipPosition.BOTTOM_RIGHT,
TooltipPosition.BOTTOM_LEFT,
]);
const tooltipBounds = tooltip.getBoundingClientRect();
const targetBounds = target.getBoundingClientRect();
const { innerWidth: viewportWidth, innerHeight: viewportHeight } = window;
// find proper position
for (const position of positions) {
const { left, top, right, bottom } = getPosition({
offset,
position,
tooltipBounds,
targetBounds,
});
const fitsToWindow = left >= 0 && top >= 0 && right <= viewportWidth && bottom <= viewportHeight;
if (fitsToWindow) {
return {
left,
top,
position,
};
}
}
const position = iter.first(positions) as TooltipPosition;
return {
position,
...getPosition({
offset,
position,
tooltipBounds,
targetBounds,
}),
};
};