import React from "react"; import { isHtmlElement } from "../../util/dom"; const SCREEN_PADDING = 16; export interface Props { screenLeft: number; screenTop: number; } export class CenteredAtPoint extends React.Component { private offsetParent: HTMLElement | undefined | null; private span: HTMLSpanElement | undefined | null; public componentWillUpdate() { this.span!.style.marginLeft = null; } public componentDidUpdate() { this.updatePositionStyle(); } public render() { return ( {this.props.children} ); } private updatePositionStyle() { if (this.offsetParent != null && this.span != null && typeof window !== "undefined") { this.span.style.top = `${window.scrollY + this.props.screenTop - this.offsetParent.offsetTop}px`; this.span.style.left = `${window.scrollX + this.props.screenLeft - this.offsetParent.offsetLeft}px`; const boundingClientRect = this.span.getBoundingClientRect(); if (boundingClientRect.left < SCREEN_PADDING) { this.span.style.marginLeft = `${0 - boundingClientRect.left + SCREEN_PADDING}px`; } } } private readonly handleRef = (span: HTMLSpanElement | null) => { this.span = span; if (span !== null) { this.offsetParent = this.findOffsetParent(span); this.updatePositionStyle(); } else { this.offsetParent = null; } }; private findOffsetParent(domNode: Element | null): HTMLElement | null { if (domNode !== null && isHtmlElement(domNode)) { const { offsetParent } = domNode; if (isHtmlElement(offsetParent)) { return offsetParent; } } return null; } }