94 lines
2.7 KiB
TypeScript
94 lines
2.7 KiB
TypeScript
|
import Panzoom from "@panzoom/panzoom";
|
||
|
import type {
|
||
|
PanzoomEventDetail,
|
||
|
PanzoomObject,
|
||
|
} from "@panzoom/panzoom/dist/src/types";
|
||
|
import useTitle from "components/system/Window/useTitle";
|
||
|
import { useProcesses } from "contexts/process";
|
||
|
import useResizeObserver from "hooks/useResizeObserver";
|
||
|
import { basename } from "path";
|
||
|
import { useCallback, useEffect, useState } from "react";
|
||
|
|
||
|
export const panZoomConfig = {
|
||
|
cursor: "default",
|
||
|
maxScale: 7,
|
||
|
minScale: 1,
|
||
|
panOnlyWhenZoomed: true,
|
||
|
step: 0.1,
|
||
|
};
|
||
|
|
||
|
type PanZoomEvent = Event & { detail: PanzoomEventDetail };
|
||
|
|
||
|
type PanZoom = Partial<
|
||
|
Pick<PanzoomObject, "reset" | "zoomIn" | "zoomOut" | "zoomToPoint">
|
||
|
> & { scale?: number };
|
||
|
|
||
|
const usePanZoom = (
|
||
|
id: string,
|
||
|
imgElement: HTMLImageElement | null,
|
||
|
containerElement: HTMLElement | null
|
||
|
): PanZoom => {
|
||
|
const [panZoom, setPanZoom] = useState<ReturnType<typeof Panzoom>>();
|
||
|
const { getScale, reset, zoomIn, zoomOut, zoomToPoint, zoomWithWheel } =
|
||
|
panZoom || {};
|
||
|
const {
|
||
|
processes: { [id]: process },
|
||
|
} = useProcesses();
|
||
|
const { closing, componentWindow, url = "" } = process || {};
|
||
|
const { prependFileToTitle } = useTitle(id);
|
||
|
const zoomUpdate = useCallback<EventListener>(
|
||
|
(panZoomEvent) => {
|
||
|
const { detail: { scale = 0, x = 0, y = 0 } = {} } =
|
||
|
(panZoomEvent as PanZoomEvent) || {};
|
||
|
|
||
|
if (url && scale) {
|
||
|
const { minScale, step } = panZoomConfig;
|
||
|
const isMinScale = scale < minScale + step;
|
||
|
|
||
|
if (isMinScale && (x || y)) {
|
||
|
window.setTimeout(() => panZoom?.reset(), 50);
|
||
|
}
|
||
|
|
||
|
if (!closing) {
|
||
|
prependFileToTitle(
|
||
|
isMinScale
|
||
|
? basename(url)
|
||
|
: `${basename(url)} (${Math.floor(scale * 100)}%)`
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
[closing, panZoom, prependFileToTitle, url]
|
||
|
);
|
||
|
const zoomWheel = useCallback(
|
||
|
(event: WheelEvent) => zoomWithWheel?.(event, { step: 0.3 }),
|
||
|
[zoomWithWheel]
|
||
|
);
|
||
|
|
||
|
useResizeObserver(componentWindow, reset);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (imgElement && containerElement) {
|
||
|
imgElement.addEventListener("panzoomchange", zoomUpdate);
|
||
|
containerElement.addEventListener("wheel", zoomWheel);
|
||
|
}
|
||
|
|
||
|
return () => {
|
||
|
imgElement?.removeEventListener("panzoomchange", zoomUpdate);
|
||
|
containerElement?.removeEventListener("wheel", zoomWheel);
|
||
|
};
|
||
|
}, [containerElement, imgElement, zoomUpdate, zoomWheel]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (imgElement && !panZoom) {
|
||
|
setPanZoom(Panzoom(imgElement, panZoomConfig));
|
||
|
}
|
||
|
|
||
|
return () => panZoom?.destroy();
|
||
|
}, [imgElement, panZoom]);
|
||
|
|
||
|
return { reset, scale: getScale?.(), zoomIn, zoomOut, zoomToPoint };
|
||
|
};
|
||
|
|
||
|
export default usePanZoom;
|