@linzjs/step-ag-grid
Version:
[](https://github.com/semantic-release/semantic-release) > Reusable [ag-grid](https://www.ag-grid.com/) component for LINZ / Toitū te whenua.
85 lines (75 loc) • 2.97 kB
text/typescript
import { MutableRefObject } from 'react';
import { parsePadding } from '../utils';
export const getPositionHelpers = (
containerRef: MutableRefObject<HTMLElement | undefined>,
menuRef: MutableRefObject<HTMLElement>,
menuScroll: Window | Element,
boundingBoxPadding: string | undefined,
) => {
const menuRect = menuRef.current.getBoundingClientRect();
const thisWindow = containerRef.current?.ownerDocument.defaultView ?? window;
const containerRect = (containerRef.current ?? thisWindow.document.body).getBoundingClientRect();
const boundingRect =
menuScroll === window || menuScroll === thisWindow
? {
left: 0,
top: 0,
right: thisWindow.document.documentElement.clientWidth,
bottom: thisWindow.innerHeight,
}
: (menuScroll as Element).getBoundingClientRect();
const padding = parsePadding(boundingBoxPadding);
// For left and top, overflows are negative value.
// For right and bottom, overflows are positive value.
const getLeftOverflow = (x: number) => x + containerRect.left - boundingRect.left - padding.left;
const getRightOverflow = (x: number) => x + containerRect.left + menuRect.width - boundingRect.right + padding.right;
const getTopOverflow = (y: number) => y + containerRect.top - boundingRect.top - padding.top;
const getBottomOverflow = (y: number) =>
y + containerRect.top + menuRect.height - boundingRect.bottom + padding.bottom;
const confineHorizontally = (x: number) => {
// If menu overflows to the left side, adjust x to have the menu contained within the viewport
// and there is no need to check the right side;
// if it doesn't overflow to the left, then check the right side
let leftOverflow = getLeftOverflow(x);
if (leftOverflow < 0) {
x -= leftOverflow;
} else {
const rightOverflow = getRightOverflow(x);
if (rightOverflow > 0) {
x -= rightOverflow;
// Check again to make sure menu doesn't overflow to the left
// because it may go off-screen and cannot be scrolled into view.
leftOverflow = getLeftOverflow(x);
if (leftOverflow < 0) x -= leftOverflow;
}
}
return x;
};
const confineVertically = (y: number) => {
// Similar logic to confineHorizontally above
let topOverflow = getTopOverflow(y);
if (topOverflow < 0) {
y -= topOverflow;
} else {
const bottomOverflow = getBottomOverflow(y);
if (bottomOverflow > 0) {
y -= bottomOverflow;
// Check again to make sure menu doesn't overflow to the bottom
// because it may go off screen and cannot be scroll into view.
topOverflow = getTopOverflow(y);
if (topOverflow < 0) y -= topOverflow;
}
}
return y;
};
return {
menuRect,
containerRect,
getLeftOverflow,
getRightOverflow,
getTopOverflow,
getBottomOverflow,
confineHorizontally,
confineVertically,
};
};