const getCursorHeightFrom = (node: HTMLElement) => parseFloat(window.getComputedStyle(node, undefined).lineHeight || ''); export const getOffsetParent = ( editorViewDom: HTMLElement, popupsMountPoint?: HTMLElement, ): HTMLElement => popupsMountPoint ? (popupsMountPoint.offsetParent as HTMLElement) : (editorViewDom.offsetParent as HTMLElement); export const getNearestNonTextNode = (node: Node) => node.nodeType === Node.TEXT_NODE ? (node.parentNode as HTMLElement) : (node as HTMLElement); /** * We need to translate the co-ordinates because `coordsAtPos` returns co-ordinates * relative to `window`. And, also need to adjust the cursor container height. * (0, 0) * +--------------------- [window] ---------------------+ * | (left, top) +-------- [Offset Parent] --------+ | * | {coordsAtPos} | [Cursor] <- cursorHeight | | * | | [FloatingToolbar] | | */ const convertFixedCoordinatesToAbsolutePositioning = ( coordinates: { top: number; left: number; bottom: number; right: number }, offsetParent: HTMLElement, cursorHeight: number, ) => { const { left: offsetParentLeft, top: offsetParentTop, height: offsetParentHeight, } = offsetParent.getBoundingClientRect(); return { left: coordinates.left - offsetParentLeft, right: coordinates.right - offsetParentLeft, top: coordinates.top - (offsetParentTop - cursorHeight) + offsetParent.scrollTop, bottom: offsetParentHeight - (coordinates.top - (offsetParentTop - cursorHeight) - offsetParent.scrollTop), }; }; export const handlePositionCalculatedWith = ( offsetParent: HTMLElement, node: Node, getCurrentFixedCoordinates: () => any, ) => (position: { top?: number; left?: number; bottom?: number; right?: number; }) => { if (!offsetParent) { return position; } const target = getNearestNonTextNode(node)!; const cursorHeight = getCursorHeightFrom(target); const fixedCoordinates = getCurrentFixedCoordinates(); const absoluteCoordinates = convertFixedCoordinatesToAbsolutePositioning( fixedCoordinates, offsetParent, cursorHeight, ); return { left: position.left ? absoluteCoordinates.left : undefined, right: position.right ? absoluteCoordinates.right : undefined, top: position.top ? absoluteCoordinates.top : undefined, bottom: position.bottom ? absoluteCoordinates.bottom : undefined, }; };