diff --git a/src/grid/Grid.tsx b/src/grid/Grid.tsx index 9aa7f95f..b6cf8fcd 100644 --- a/src/grid/Grid.tsx +++ b/src/grid/Grid.tsx @@ -91,6 +91,28 @@ export const Slot: FC = ({ tile, style, className, ...props }) => ( /> ); +interface Offset { + x: number; + y: number; +} + +/** + * Gets the offset of one element relative to an ancestor. + */ +function offset(element: HTMLElement, relativeTo: Element): Offset { + if ( + !(element.offsetParent instanceof HTMLElement) || + element.offsetParent === relativeTo + ) { + return { x: element.offsetLeft, y: element.offsetTop }; + } else { + const o = offset(element.offsetParent, relativeTo); + o.x += element.offsetLeft; + o.y += element.offsetTop; + return o; + } +} + export interface LayoutProps { ref: LegacyRef; model: Model; @@ -228,24 +250,23 @@ export function Grid< const slotRects = useMemo(() => { const rects = new Map(); - if (layoutRoot !== null) { + if (gridRoot !== null && layoutRoot !== null) { const slots = layoutRoot.getElementsByClassName( styles.slot, ) as HTMLCollectionOf; for (const slot of slots) rects.set(slot.getAttribute("data-tile")!, { - x: slot.offsetLeft, - y: slot.offsetTop, + ...offset(slot, gridRoot), width: slot.offsetWidth, height: slot.offsetHeight, }); } return rects; - // The rects may change due to the grid being resized or rerendered, but + // The rects may change due to the grid updating to a new generation, but // eslint can't statically verify this // eslint-disable-next-line react-hooks/exhaustive-deps - }, [layoutRoot, generation]); + }, [gridRoot, layoutRoot, generation]); const tileModels = useMemo( () => getTileModels(model),