mirror of
https://github.com/documenso/documenso.git
synced 2026-07-14 06:47:10 +10:00
118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
|
import { PDF_VIEWER_CONTENT_SELECTOR, PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
|
import type { Field } from '@prisma/client';
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
|
|
export const useFieldPageCoords = (field: Pick<Field, 'positionX' | 'positionY' | 'width' | 'height' | 'page'>) => {
|
|
const [coords, setCoords] = useState({
|
|
x: 0,
|
|
y: 0,
|
|
height: 0,
|
|
width: 0,
|
|
});
|
|
|
|
const calculateCoords = useCallback(() => {
|
|
const $page = document.querySelector<HTMLElement>(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`);
|
|
|
|
if (!$page) {
|
|
return;
|
|
}
|
|
|
|
const { top, left, height, width } = getBoundingClientRect($page);
|
|
|
|
// X and Y are percentages of the page's height and width
|
|
const fieldX = (Number(field.positionX) / 100) * width + left;
|
|
const fieldY = (Number(field.positionY) / 100) * height + top;
|
|
|
|
const fieldHeight = (Number(field.height) / 100) * height;
|
|
const fieldWidth = (Number(field.width) / 100) * width;
|
|
|
|
setCoords({
|
|
x: fieldX,
|
|
y: fieldY,
|
|
height: fieldHeight,
|
|
width: fieldWidth,
|
|
});
|
|
}, [field.height, field.page, field.positionX, field.positionY, field.width]);
|
|
|
|
useEffect(() => {
|
|
calculateCoords();
|
|
}, [calculateCoords]);
|
|
|
|
useEffect(() => {
|
|
const onResize = () => {
|
|
calculateCoords();
|
|
};
|
|
|
|
window.addEventListener('resize', onResize);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', onResize);
|
|
};
|
|
}, [calculateCoords]);
|
|
|
|
// Watch for the page element to appear in the DOM (e.g. after a virtual list
|
|
// scroll) and recalculate coords. Also attach a ResizeObserver once the page
|
|
// element exists.
|
|
useEffect(() => {
|
|
const pageSelector = `${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.page}"]`;
|
|
|
|
let resizeObserver: ResizeObserver | null = null;
|
|
let observedElement: HTMLElement | null = null;
|
|
|
|
const attachResizeObserver = ($page: HTMLElement) => {
|
|
if ($page === observedElement) {
|
|
return;
|
|
}
|
|
|
|
resizeObserver?.disconnect();
|
|
resizeObserver = new ResizeObserver(() => {
|
|
calculateCoords();
|
|
});
|
|
resizeObserver.observe($page);
|
|
observedElement = $page;
|
|
};
|
|
|
|
// Try to attach immediately if the page already exists.
|
|
const existingPage = document.querySelector<HTMLElement>(pageSelector);
|
|
|
|
if (existingPage) {
|
|
attachResizeObserver(existingPage);
|
|
}
|
|
|
|
// Watch for DOM mutations to detect when the page element appears (e.g.
|
|
// after the virtual list scrolls to a new page and renders it).
|
|
// Scope to the PDF viewer content container to avoid firing on unrelated
|
|
// DOM changes elsewhere in the document.
|
|
const mutationObserver = new MutationObserver(() => {
|
|
const $page = document.querySelector<HTMLElement>(pageSelector);
|
|
|
|
if (!$page) {
|
|
return;
|
|
}
|
|
|
|
if ($page === observedElement) {
|
|
return;
|
|
}
|
|
|
|
calculateCoords();
|
|
attachResizeObserver($page);
|
|
});
|
|
|
|
const $container = document.querySelector(PDF_VIEWER_CONTENT_SELECTOR) ?? document.body;
|
|
|
|
mutationObserver.observe($container, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
|
|
return () => {
|
|
mutationObserver.disconnect();
|
|
resizeObserver?.disconnect();
|
|
observedElement = null;
|
|
};
|
|
}, [calculateCoords, field.page]);
|
|
|
|
return coords;
|
|
};
|