Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@zoom-image/core

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zoom-image/core - npm Package Compare versions

Comparing version
0.41.2
to
0.41.3
+369
dist/chunk-5D7YXNAE.mjs
import { getSourceImage, makeMaybeCallFunction, enableScroll, clamp, disableScroll, computeZoomGesture } from './chunk-5KET5YPV.mjs';
import { createStore } from '@namnode/store';
var ZOOM_DELTA = 0.5;
var defaultInitialState = {
currentZoom: 1,
enable: true,
currentPositionX: 0,
currentPositionY: 0,
currentRotation: 0
};
var defaultShouldZoomOnSingleTouch = () => true;
function createZoomImageWheel(container, options = {}) {
const sourceImgElement = getSourceImage(container);
const finalOptions = {
maxZoom: options.maxZoom || 4,
wheelZoomRatio: options.wheelZoomRatio || 0.1,
dblTapAnimationDuration: options.dblTapAnimationDuration || 300,
initialState: { ...defaultInitialState, ...options.initialState },
shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch
};
const store = createStore(finalOptions.initialState);
const checkDimensionSwitched = () => {
return [90, 270].includes(store.getState().currentRotation % 360);
};
const calculatePositionX = (newPositionX, currentZoom, isDimensionSwitched) => {
if (newPositionX > 0)
return 0;
const width = isDimensionSwitched ? container.clientHeight : container.clientWidth;
if (newPositionX + width * currentZoom < width)
return -width * (currentZoom - 1);
return newPositionX;
};
const calculatePositionY = (newPositionY, currentZoom, isDimensionSwitched) => {
if (newPositionY > 0)
return 0;
const height = isDimensionSwitched ? container.clientWidth : container.clientHeight;
if (newPositionY + height * currentZoom < height)
return -height * (currentZoom - 1);
return newPositionY;
};
const updateStateOnNewZoom = (currentZoom) => {
const zoomPointX = container.clientWidth / 2;
const zoomPointY = container.clientHeight / 2;
const currentState = store.getState();
const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom;
const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom;
store.setState({
currentZoom,
currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),
currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false)
});
};
let prevTwoPositions = null;
let enabledScroll = true;
const pointerMap = /* @__PURE__ */ new Map();
let lastPositionX = 0;
let lastPositionY = 0;
let startX = 0;
let startY = 0;
container.style.overflow = "hidden";
sourceImgElement.style.transformOrigin = "0 0";
function updateZoom() {
const currentState = store.getState();
sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`;
container.style.rotate = `${currentState.currentRotation}deg`;
}
function setState(newState) {
store.batch(() => {
const currentState = store.getState();
if (typeof newState.enable === "boolean" && newState.enable !== currentState.enable) {
store.setState({
enable: newState.enable
});
if (!newState.enable) {
return;
}
}
if (typeof newState.currentRotation === "number") {
const newCurrentRotation = newState.currentRotation;
store.setState({
currentRotation: newCurrentRotation
});
}
if (typeof newState.currentZoom === "number" && newState.currentZoom !== currentState.currentZoom) {
const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom);
if (newCurrentZoom === currentState.currentZoom) {
return;
}
updateStateOnNewZoom(newCurrentZoom);
}
});
updateZoom();
}
function processZoomWheel({ delta, x, y }) {
const containerRect = container.getBoundingClientRect();
const currentState = store.getState();
const isDimensionSwitched = checkDimensionSwitched();
let zoomPointX = -1;
let zoomPointY = -1;
switch (currentState.currentRotation % 360) {
case 0:
zoomPointX = x - containerRect.left;
zoomPointY = y - containerRect.top;
break;
case 90:
zoomPointX = Math.abs(x - containerRect.right);
zoomPointY = Math.abs(y - containerRect.top);
break;
case 180:
zoomPointX = Math.abs(x - containerRect.right);
zoomPointY = Math.abs(y - containerRect.bottom);
break;
case 270:
zoomPointX = Math.abs(x - containerRect.left);
zoomPointY = Math.abs(y - containerRect.bottom);
break;
}
const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX;
const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY;
const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom;
const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom;
const newCurrentZoom = clamp(
currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,
1,
finalOptions.maxZoom
);
const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched);
const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched);
store.setState({
currentZoom: newCurrentZoom,
currentPositionX: isDimensionSwitched ? newY : newX,
currentPositionY: isDimensionSwitched ? newX : newY
});
}
function updatePositionsForSinglePointerFlow() {
if (pointerMap.size === 1) {
const { x, y } = pointerMap.values().next().value;
const isDimensionSwitched = checkDimensionSwitched();
startX = isDimensionSwitched ? y : x;
startY = isDimensionSwitched ? x : y;
}
const currentState = store.getState();
lastPositionX = currentState.currentPositionX;
lastPositionY = currentState.currentPositionY;
}
function _handleWheel(event) {
event.preventDefault();
if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {
return;
}
const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA);
processZoomWheel({ delta, x: event.clientX, y: event.clientY });
updateZoom();
updatePositionsForSinglePointerFlow();
}
function _handlePointerMove(event) {
event.preventDefault();
const { clientX, clientY, pointerId } = event;
for (const [cachedPointerId] of pointerMap.entries()) {
if (cachedPointerId === pointerId) {
pointerMap.set(cachedPointerId, { x: clientX, y: clientY });
}
}
const { currentZoom, currentRotation } = store.getState();
if (pointerMap.size === 1 && currentZoom !== 1) {
const isDimensionSwitched = checkDimensionSwitched();
const normalizedClientX = isDimensionSwitched ? clientY : clientX;
const normalizedClientY = isDimensionSwitched ? clientX : clientY;
let offsetX = -1;
let offsetY = -1;
switch (currentRotation % 360) {
case 0:
offsetX = normalizedClientX - startX;
offsetY = normalizedClientY - startY;
break;
case 90:
offsetX = normalizedClientX - startX;
offsetY = startY - normalizedClientY;
break;
case 180:
offsetX = startX - normalizedClientX;
offsetY = startY - normalizedClientY;
break;
case 270:
offsetX = startX - normalizedClientX;
offsetY = normalizedClientY - startY;
break;
}
store.setState({
currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),
currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false)
});
updateZoom();
}
}
const animationState = {
startTimestamp: null,
// the state at the start of the zoom animation
start: { x: 0, y: 0, zoom: 0 },
// the target state at the end of the zoom animation
target: { x: 0, y: 0, zoom: 0 }
};
function animateZoom(touchCoordinate) {
const currentState = store.getState();
animationState.startTimestamp = null;
animationState.start = {
x: currentState.currentPositionX,
y: currentState.currentPositionY,
zoom: currentState.currentZoom
};
if (currentState.currentZoom > 1) {
animationState.target = {
x: 0,
y: 0,
zoom: 1
};
} else {
animationState.target = {
zoom: finalOptions.maxZoom,
x: touchCoordinate.x * (1 - finalOptions.maxZoom),
y: touchCoordinate.y * (1 - finalOptions.maxZoom)
};
}
function lerp(a, b, t) {
return a * (1 - t) + b * t;
}
function frame(timestamp) {
if (animationState.startTimestamp === null) {
animationState.startTimestamp = timestamp;
}
let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration;
if (t > 1) {
t = 1;
}
store.setState({
currentPositionX: lerp(animationState.start.x, animationState.target.x, t),
currentPositionY: lerp(animationState.start.y, animationState.target.y, t),
currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t)
});
updateZoom();
if (t < 1) {
requestAnimationFrame(frame);
}
}
requestAnimationFrame(frame);
}
let touchTimer = null;
const durationBetweenTap = 300;
function _handleTouchStart(event) {
if (event.touches.length > 1) {
return;
}
if (touchTimer === null) {
touchTimer = setTimeout(() => {
touchTimer = null;
}, durationBetweenTap);
} else {
clearTimeout(touchTimer);
touchTimer = null;
const rect = container.getBoundingClientRect();
const touch = event.touches[0];
animateZoom({
x: touch.clientX - rect.left,
y: touch.clientY - rect.top
});
return;
}
}
function _handleTouchMove(event) {
if (finalOptions.shouldZoomOnSingleTouch())
event.preventDefault();
if (event.touches.length > 1) {
event.preventDefault();
const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY }));
if (prevTwoPositions !== null) {
const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions);
processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center });
}
prevTwoPositions = currentTwoPositions;
updateZoom();
return;
}
}
function _handlePointerDown(event) {
if (event.pointerType === "touch" && !finalOptions.shouldZoomOnSingleTouch())
return;
event.preventDefault();
if (pointerMap.size === 2) {
return;
}
if (enabledScroll) {
disableScroll();
enabledScroll = false;
}
const { clientX, clientY, pointerId } = event;
const currentState = store.getState();
lastPositionX = currentState.currentPositionX;
lastPositionY = currentState.currentPositionY;
const isDimensionSwitched = checkDimensionSwitched();
startX = isDimensionSwitched ? clientY : clientX;
startY = isDimensionSwitched ? clientX : clientY;
pointerMap.set(pointerId, { x: clientX, y: clientY });
}
function _handlePointerUp(event) {
pointerMap.delete(event.pointerId);
if (pointerMap.size < 2) {
prevTwoPositions = null;
}
if (pointerMap.size === 0 && !enabledScroll) {
enableScroll();
enabledScroll = true;
}
updatePositionsForSinglePointerFlow();
}
function _handlePointerLeave(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);
prevTwoPositions = null;
if (!enabledScroll) {
enableScroll();
enabledScroll = true;
}
}
function checkZoomEnabled() {
return store.getState().enable;
}
const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel);
const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown);
const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave);
const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove);
const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp);
const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart);
const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove);
const controller = new AbortController();
const { signal } = controller;
container.addEventListener("wheel", handleWheel, { signal });
container.addEventListener("touchstart", handleTouchStart, { signal });
container.addEventListener("touchmove", handleTouchMove, { signal });
container.addEventListener("pointerdown", handlePointerDown, { signal });
container.addEventListener("pointerleave", handlePointerLeave, { signal });
container.addEventListener("pointermove", handlePointerMove, { signal });
container.addEventListener("pointerup", handlePointerUp, { signal });
container.addEventListener(
"touchend",
() => {
enabledScroll = true;
enableScroll();
},
{ signal }
);
if (store.getState().currentZoom !== defaultInitialState.currentZoom) {
updateStateOnNewZoom(store.getState().currentZoom);
updateZoom();
}
return {
cleanup() {
controller.abort();
store.cleanup();
},
subscribe: store.subscribe,
setState,
getState: store.getState
};
}
export { createZoomImageWheel };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=chunk-5D7YXNAE.mjs.map
{"version":3,"sources":["../src/createZoomImageWheel.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,SAAS,mBAAmB;AAgB5B,IAAM,aAAa;AAgBnB,IAAM,sBAA2C;AAAA,EAC/C,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAiB;AACnB;AAEA,IAAM,iCAAiC,MAAM;AAEtC,SAAS,qBAAqB,WAAwB,UAAiC,CAAC,GAAG;AAChG,QAAM,mBAAmB,eAAe,SAAS;AACjD,QAAM,eAAgD;AAAA,IACpD,SAAS,QAAQ,WAAW;AAAA,IAC5B,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,yBAAyB,QAAQ,2BAA2B;AAAA,IAC5D,cAAc,EAAE,GAAG,qBAAqB,GAAG,QAAQ,aAAa;AAAA,IAChE,yBAAyB,QAAQ,2BAA2B;AAAA,EAC9D;AAEA,QAAM,QAAQ,YAAiC,aAAa,YAAmC;AAE/F,QAAM,yBAAyB,MAAM;AACnC,WAAO,CAAC,IAAI,GAAG,EAAE,SAAS,MAAM,SAAS,EAAE,kBAAkB,GAAG;AAAA,EAClE;AAEA,QAAM,qBAAqB,CAAC,cAAsB,aAAqB,wBAAiC;AACtG,QAAI,eAAe;AAAG,aAAO;AAE7B,UAAM,QAAQ,sBAAsB,UAAU,eAAe,UAAU;AACvE,QAAI,eAAe,QAAQ,cAAc;AAAO,aAAO,CAAC,SAAS,cAAc;AAC/E,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,CAAC,cAAsB,aAAqB,wBAAiC;AACtG,QAAI,eAAe;AAAG,aAAO;AAE7B,UAAM,SAAS,sBAAsB,UAAU,cAAc,UAAU;AACvE,QAAI,eAAe,SAAS,cAAc;AAAQ,aAAO,CAAC,UAAU,cAAc;AAClF,WAAO;AAAA,EACT;AAEA,QAAM,uBAAuB,CAAC,gBAAwB;AACpD,UAAM,aAAa,UAAU,cAAc;AAC3C,UAAM,aAAa,UAAU,eAAe;AAC5C,UAAM,eAAe,MAAM,SAAS;AACpC,UAAM,eAAe,aAAa,aAAa,oBAAoB,aAAa;AAChF,UAAM,eAAe,aAAa,aAAa,oBAAoB,aAAa;AAEhF,UAAM,SAAS;AAAA,MACb;AAAA,MACA,kBAAkB,mBAAmB,CAAC,cAAc,cAAc,YAAY,aAAa,KAAK;AAAA,MAChG,kBAAkB,mBAAmB,CAAC,cAAc,cAAc,YAAY,aAAa,KAAK;AAAA,IAClG,CAAC;AAAA,EACH;AAGA,MAAI,mBAA8D;AAClE,MAAI,gBAAgB;AACpB,QAAM,aAAa,oBAAI,IAA6B;AAEpD,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,MAAI,SAAS;AACb,MAAI,SAAS;AAEb,YAAU,MAAM,WAAW;AAC3B,mBAAiB,MAAM,kBAAkB;AAEzC,WAAS,aAAa;AACpB,UAAM,eAAe,MAAM,SAAS;AACpC,qBAAiB,MAAM,YAAY,aAAa,aAAa,gBAAgB,OAAO,aAAa,gBAAgB,aAAa,aAAa,WAAW;AACtJ,cAAU,MAAM,SAAS,GAAG,aAAa,eAAe;AAAA,EAC1D;AAEA,WAAS,SAAS,UAAqC;AACrD,UAAM,MAAM,MAAM;AAChB,YAAM,eAAe,MAAM,SAAS;AACpC,UAAI,OAAO,SAAS,WAAW,aAAa,SAAS,WAAW,aAAa,QAAQ;AACnF,cAAM,SAAS;AAAA,UACb,QAAQ,SAAS;AAAA,QACnB,CAAC;AAED,YAAI,CAAC,SAAS,QAAQ;AACpB;AAAA,QACF;AAAA,MACF;AAEA,UAAI,OAAO,SAAS,oBAAoB,UAAU;AAChD,cAAM,qBAAqB,SAAS;AACpC,cAAM,SAAS;AAAA,UACb,iBAAiB;AAAA,QACnB,CAAC;AAAA,MACH;AAEA,UAAI,OAAO,SAAS,gBAAgB,YAAY,SAAS,gBAAgB,aAAa,aAAa;AACjG,cAAM,iBAAiB,MAAM,SAAS,aAAa,GAAG,aAAa,OAAO;AAE1E,YAAI,mBAAmB,aAAa,aAAa;AAC/C;AAAA,QACF;AAEA,6BAAqB,cAAc;AAAA,MACrC;AAAA,IACF,CAAC;AAED,eAAW;AAAA,EACb;AAEA,WAAS,iBAAiB,EAAE,OAAO,GAAG,EAAE,GAA4C;AAClF,UAAM,gBAAgB,UAAU,sBAAsB;AACtD,UAAM,eAAe,MAAM,SAAS;AACpC,UAAM,sBAAsB,uBAAuB;AAEnD,QAAI,aAAa;AACjB,QAAI,aAAa;AAEjB,YAAQ,aAAa,kBAAkB,KAAK;AAAA,MAC1C,KAAK;AACH,qBAAa,IAAI,cAAc;AAC/B,qBAAa,IAAI,cAAc;AAC/B;AAAA,MACF,KAAK;AACH,qBAAa,KAAK,IAAI,IAAI,cAAc,KAAK;AAC7C,qBAAa,KAAK,IAAI,IAAI,cAAc,GAAG;AAC3C;AAAA,MACF,KAAK;AACH,qBAAa,KAAK,IAAI,IAAI,cAAc,KAAK;AAC7C,qBAAa,KAAK,IAAI,IAAI,cAAc,MAAM;AAC9C;AAAA,MACF,KAAK;AACH,qBAAa,KAAK,IAAI,IAAI,cAAc,IAAI;AAC5C,qBAAa,KAAK,IAAI,IAAI,cAAc,MAAM;AAC9C;AAAA,IACJ;AAEA,UAAM,QAAQ,sBAAsB,aAAa,mBAAmB,aAAa;AACjF,UAAM,QAAQ,sBAAsB,aAAa,mBAAmB,aAAa;AAEjF,UAAM,eAAe,aAAa,SAAS,aAAa;AACxD,UAAM,eAAe,aAAa,SAAS,aAAa;AAExD,UAAM,iBAAiB;AAAA,MACrB,aAAa,cAAc,QAAQ,aAAa,iBAAiB,aAAa;AAAA,MAC9E;AAAA,MACA,aAAa;AAAA,IACf;AAEA,UAAM,OAAO,mBAAmB,CAAC,cAAc,iBAAiB,YAAY,gBAAgB,mBAAmB;AAC/G,UAAM,OAAO,mBAAmB,CAAC,cAAc,iBAAiB,YAAY,gBAAgB,mBAAmB;AAE/G,UAAM,SAAS;AAAA,MACb,aAAa;AAAA,MACb,kBAAkB,sBAAsB,OAAO;AAAA,MAC/C,kBAAkB,sBAAsB,OAAO;AAAA,IACjD,CAAC;AAAA,EACH;AAEA,WAAS,sCAAsC;AAC7C,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,EAAE,GAAG,EAAE,IAAI,WAAW,OAAO,EAAE,KAAK,EAAE;AAC5C,YAAM,sBAAsB,uBAAuB;AACnD,eAAS,sBAAsB,IAAI;AACnC,eAAS,sBAAsB,IAAI;AAAA,IACrC;AAEA,UAAM,eAAe,MAAM,SAAS;AACpC,oBAAgB,aAAa;AAC7B,oBAAgB,aAAa;AAAA,EAC/B;AAEA,WAAS,aAAa,OAAmB;AACvC,UAAM,eAAe;AAErB,QAAI,MAAM,SAAS,EAAE,gBAAgB,aAAa,WAAW,MAAM,SAAS,GAAG;AAC7E;AAAA,IACF;AAEA,UAAM,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,YAAY,UAAU;AAC1D,qBAAiB,EAAE,OAAO,GAAG,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC;AAC9D,eAAW;AAEX,wCAAoC;AAAA,EACtC;AAEA,WAAS,mBAAmB,OAAqB;AAC/C,UAAM,eAAe;AACrB,UAAM,EAAE,SAAS,SAAS,UAAU,IAAI;AACxC,eAAW,CAAC,eAAe,KAAK,WAAW,QAAQ,GAAG;AACpD,UAAI,oBAAoB,WAAW;AACjC,mBAAW,IAAI,iBAAiB,EAAE,GAAG,SAAS,GAAG,QAAQ,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,UAAM,EAAE,aAAa,gBAAgB,IAAI,MAAM,SAAS;AACxD,QAAI,WAAW,SAAS,KAAK,gBAAgB,GAAG;AAC9C,YAAM,sBAAsB,uBAAuB;AACnD,YAAM,oBAAoB,sBAAsB,UAAU;AAC1D,YAAM,oBAAoB,sBAAsB,UAAU;AAE1D,UAAI,UAAU;AACd,UAAI,UAAU;AAEd,cAAQ,kBAAkB,KAAK;AAAA,QAC7B,KAAK;AACH,oBAAU,oBAAoB;AAC9B,oBAAU,oBAAoB;AAC9B;AAAA,QACF,KAAK;AACH,oBAAU,oBAAoB;AAC9B,oBAAU,SAAS;AACnB;AAAA,QACF,KAAK;AACH,oBAAU,SAAS;AACnB,oBAAU,SAAS;AACnB;AAAA,QACF,KAAK;AACH,oBAAU,SAAS;AACnB,oBAAU,oBAAoB;AAC9B;AAAA,MACJ;AAEA,YAAM,SAAS;AAAA,QACb,kBAAkB,mBAAmB,gBAAgB,SAAS,aAAa,KAAK;AAAA,QAChF,kBAAkB,mBAAmB,gBAAgB,SAAS,aAAa,KAAK;AAAA,MAClF,CAAC;AACD,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,QAAM,iBAAiB;AAAA,IACrB,gBAAgB;AAAA;AAAA,IAEhB,OAAO,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE;AAAA;AAAA,IAE7B,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE;AAAA,EAChC;AAEA,WAAS,YAAY,iBAA2C;AAE9D,UAAM,eAAe,MAAM,SAAS;AAEpC,mBAAe,iBAAiB;AAChC,mBAAe,QAAQ;AAAA,MACrB,GAAG,aAAa;AAAA,MAChB,GAAG,aAAa;AAAA,MAChB,MAAM,aAAa;AAAA,IACrB;AAEA,QAAI,aAAa,cAAc,GAAG;AAChC,qBAAe,SAAS;AAAA,QACtB,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,MACR;AAAA,IACF,OAAO;AACL,qBAAe,SAAS;AAAA,QACtB,MAAM,aAAa;AAAA,QACnB,GAAG,gBAAgB,KAAK,IAAI,aAAa;AAAA,QACzC,GAAG,gBAAgB,KAAK,IAAI,aAAa;AAAA,MAC3C;AAAA,IACF;AAEA,aAAS,KAAK,GAAW,GAAW,GAAmB;AACrD,aAAO,KAAK,IAAI,KAAK,IAAI;AAAA,IAC3B;AAEA,aAAS,MAAM,WAAgC;AAC7C,UAAI,eAAe,mBAAmB,MAAM;AAC1C,uBAAe,iBAAiB;AAAA,MAClC;AAGA,UAAI,KAAK,YAAY,eAAe,kBAAkB,aAAa;AACnE,UAAI,IAAI,GAAG;AACT,YAAI;AAAA,MACN;AAEA,YAAM,SAAS;AAAA,QACb,kBAAkB,KAAK,eAAe,MAAM,GAAG,eAAe,OAAO,GAAG,CAAC;AAAA,QACzE,kBAAkB,KAAK,eAAe,MAAM,GAAG,eAAe,OAAO,GAAG,CAAC;AAAA,QACzE,aAAa,KAAK,eAAe,MAAM,MAAM,eAAe,OAAO,MAAM,CAAC;AAAA,MAC5E,CAAC;AACD,iBAAW;AAEX,UAAI,IAAI,GAAG;AACT,8BAAsB,KAAK;AAAA,MAC7B;AAAA,IACF;AAEA,0BAAsB,KAAK;AAAA,EAC7B;AAGA,MAAI,aAAoC;AACxC,QAAM,qBAAqB;AAE3B,WAAS,kBAAkB,OAAmB;AAC5C,QAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B;AAAA,IACF;AAEA,QAAI,eAAe,MAAM;AACvB,mBAAa,WAAW,MAAM;AAC5B,qBAAa;AAAA,MACf,GAAG,kBAAkB;AAAA,IACvB,OAAO;AACL,mBAAa,UAAU;AACvB,mBAAa;AAEb,YAAM,OAAO,UAAU,sBAAsB;AAC7C,YAAM,QAAQ,MAAM,QAAQ,CAAC;AAC7B,kBAAY;AAAA,QACV,GAAG,MAAM,UAAU,KAAK;AAAA,QACxB,GAAG,MAAM,UAAU,KAAK;AAAA,MAC1B,CAAC;AACD;AAAA,IACF;AAAA,EACF;AAEA,WAAS,iBAAiB,OAAmB;AAC3C,QAAI,aAAa,wBAAwB;AAAG,YAAM,eAAe;AACjE,QAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,YAAM,eAAe;AACrB,YAAM,sBAAsB,CAAC,GAAG,MAAM,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,EAAE;AAK1F,UAAI,qBAAqB,MAAM;AAC7B,cAAM,EAAE,OAAO,OAAO,IAAI,mBAAmB,kBAAkB,mBAAmB;AAClF,yBAAiB,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,aAAa,gBAAgB,GAAG,OAAO,CAAC;AAAA,MACtF;AAEA,yBAAmB;AACnB,iBAAW;AACX;AAAA,IACF;AAAA,EACF;AAEA,WAAS,mBAAmB,OAAqB;AAC/C,QAAI,MAAM,gBAAgB,WAAW,CAAC,aAAa,wBAAwB;AAAG;AAC9E,UAAM,eAAe;AACrB,QAAI,WAAW,SAAS,GAAG;AACzB;AAAA,IACF;AAEA,QAAI,eAAe;AACjB,oBAAc;AACd,sBAAgB;AAAA,IAClB;AAEA,UAAM,EAAE,SAAS,SAAS,UAAU,IAAI;AAExC,UAAM,eAAe,MAAM,SAAS;AACpC,oBAAgB,aAAa;AAC7B,oBAAgB,aAAa;AAE7B,UAAM,sBAAsB,uBAAuB;AACnD,aAAS,sBAAsB,UAAU;AACzC,aAAS,sBAAsB,UAAU;AACzC,eAAW,IAAI,WAAW,EAAE,GAAG,SAAS,GAAG,QAAQ,CAAC;AAAA,EACtD;AAEA,WAAS,iBAAiB,OAAqB;AAC7C,eAAW,OAAO,MAAM,SAAS;AAGjC,QAAI,WAAW,OAAO,GAAG;AACvB,yBAAmB;AAAA,IACrB;AAEA,QAAI,WAAW,SAAS,KAAK,CAAC,eAAe;AAC3C,mBAAa;AACb,sBAAgB;AAAA,IAClB;AAEA,wCAAoC;AAAA,EACtC;AAEA,WAAS,oBAAoB,OAAqB;AAChD,UAAM,eAAe;AACrB,eAAW,OAAO,MAAM,SAAS;AACjC,uBAAmB;AACnB,QAAI,CAAC,eAAe;AAClB,mBAAa;AACb,sBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,WAAS,mBAAmB;AAC1B,WAAO,MAAM,SAAS,EAAE;AAAA,EAC1B;AAEA,QAAM,cAAc,sBAAsB,kBAAkB,YAAY;AACxE,QAAM,oBAAoB,sBAAsB,kBAAkB,kBAAkB;AACpF,QAAM,qBAAqB,sBAAsB,kBAAkB,mBAAmB;AACtF,QAAM,oBAAoB,sBAAsB,kBAAkB,kBAAkB;AACpF,QAAM,kBAAkB,sBAAsB,kBAAkB,gBAAgB;AAChF,QAAM,mBAAmB,sBAAsB,kBAAkB,iBAAiB;AAClF,QAAM,kBAAkB,sBAAsB,kBAAkB,gBAAgB;AAEhF,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,EAAE,OAAO,IAAI;AACnB,YAAU,iBAAiB,SAAS,aAAa,EAAE,OAAO,CAAC;AAC3D,YAAU,iBAAiB,cAAc,kBAAkB,EAAE,OAAO,CAAC;AACrE,YAAU,iBAAiB,aAAa,iBAAiB,EAAE,OAAO,CAAC;AACnE,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,OAAO,CAAC;AACvE,YAAU,iBAAiB,gBAAgB,oBAAoB,EAAE,OAAO,CAAC;AACzE,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,OAAO,CAAC;AACvE,YAAU,iBAAiB,aAAa,iBAAiB,EAAE,OAAO,CAAC;AACnE,YAAU;AAAA,IACR;AAAA,IACA,MAAM;AACJ,sBAAgB;AAChB,mBAAa;AAAA,IACf;AAAA,IACA,EAAE,OAAO;AAAA,EACX;AAGA,MAAI,MAAM,SAAS,EAAE,gBAAgB,oBAAoB,aAAa;AACpE,yBAAqB,MAAM,SAAS,EAAE,WAAW;AACjD,eAAW;AAAA,EACb;AAEA,SAAO;AAAA,IACL,UAAU;AACR,iBAAW,MAAM;AACjB,YAAM,QAAQ;AAAA,IAChB;AAAA,IACA,WAAW,MAAM;AAAA,IACjB;AAAA,IACA,UAAU,MAAM;AAAA,EAClB;AACF","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n"]}
+0
-1

@@ -401,3 +401,2 @@ var ZoomImage = (function (exports) {

function _handlePointerUp(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);

@@ -404,0 +403,0 @@ if (pointerMap.size < 2) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../node_modules/.pnpm/@namnode+store@0.1.0/node_modules/@namnode/store/dist/chunk-TZNK2OF3.mjs","../src/utils.ts","../src/createZoomImageWheel.ts"],"names":["controller","signal"],"mappings":"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","sourcesContent":["function f(o){let r=new Set,s=!1,a=o,e,c=(t={})=>{e={...e,...t},i();},i=()=>{if(s)return;let t=!1;if(e){for(let n in e)if(a[n]!==e[n]){t=!0;break}}t&&(a={...a,...e},r.forEach(n=>n({state:a,updatedProperties:e})),e=void 0);};return {subscribe:t=>(r.add(t),()=>{r.delete(t);}),cleanup:()=>r.clear(),getState:()=>a,setState:c,batch:t=>{s=!0,t(),s=!1,i();}}}\n\nexport { f as a };\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n","import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n"]}
{"version":3,"sources":["../../../node_modules/.pnpm/@namnode+store@0.1.0/node_modules/@namnode/store/dist/chunk-TZNK2OF3.mjs","../src/utils.ts","../src/createZoomImageWheel.ts"],"names":["controller","signal"],"mappings":"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","sourcesContent":["function f(o){let r=new Set,s=!1,a=o,e,c=(t={})=>{e={...e,...t},i();},i=()=>{if(s)return;let t=!1;if(e){for(let n in e)if(a[n]!==e[n]){t=!0;break}}t&&(a={...a,...e},r.forEach(n=>n({state:a,updatedProperties:e})),e=void 0);};return {subscribe:t=>(r.add(t),()=>{r.delete(t);}),cleanup:()=>r.clear(),getState:()=>a,setState:c,batch:t=>{s=!0,t(),s=!1,i();}}}\n\nexport { f as a };\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n","import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n"]}

@@ -380,3 +380,2 @@ 'use strict';

function _handlePointerUp(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);

@@ -383,0 +382,0 @@ if (pointerMap.size < 2) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/createZoomImageWheel.ts","../src/utils.ts"],"names":["controller","signal"],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n"]}
{"version":3,"sources":["../src/createZoomImageWheel.ts","../src/utils.ts"],"names":["controller","signal"],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n"]}

@@ -1,4 +0,4 @@

export { createZoomImageWheel } from './chunk-75T2QBQQ.mjs';
export { createZoomImageWheel } from './chunk-5D7YXNAE.mjs';
import './chunk-5KET5YPV.mjs';
//# sourceMappingURL=out.js.map
//# sourceMappingURL=createZoomImageWheel.mjs.map

@@ -691,3 +691,2 @@ var ZoomImage = (function (exports) {

function _handlePointerUp(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);

@@ -694,0 +693,0 @@ if (pointerMap.size < 2) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../node_modules/.pnpm/@namnode+store@0.1.0/node_modules/@namnode/store/dist/chunk-TZNK2OF3.mjs","../src/imageLoader.ts","../src/utils.ts","../src/createZoomImageHover.ts","../src/createZoomImageMove.ts","../src/createZoomImageWheel.ts","../src/createZoomImageClick.ts","../src/makeCalculatePercentage.ts","../src/makeCalculateZoom.ts","../src/cropImage.ts"],"names":["controller","signal"],"mappings":";AAAA,SAAS,EAAE,GAAE;AAAC,MAAI,IAAE,oBAAI,OAAI,IAAE,OAAG,IAAE,GAAE,GAAE,IAAE,CAAC,IAAE,CAAC,MAAI;AAAC,QAAE,EAAC,GAAG,GAAE,GAAG,EAAC,GAAE,EAAE;AAAA,EAAE,GAAE,IAAE,MAAI;AAAC,QAAG;AAAE;AAAO,QAAI,IAAE;AAAG,QAAG,GAAE;AAAC,eAAQ,KAAK;AAAE,YAAG,EAAE,CAAC,MAAI,EAAE,CAAC,GAAE;AAAC,cAAE;AAAG;AAAA,QAAK;AAAA,IAAC;AAAC,UAAI,IAAE,EAAC,GAAG,GAAE,GAAG,EAAC,GAAE,EAAE,QAAQ,OAAG,EAAE,EAAC,OAAM,GAAE,mBAAkB,EAAC,CAAC,CAAC,GAAE,IAAE;AAAA,EAAQ;AAAE,SAAO,EAAC,WAAU,QAAI,EAAE,IAAI,CAAC,GAAE,MAAI;AAAC,MAAE,OAAO,CAAC;AAAA,EAAE,IAAG,SAAQ,MAAI,EAAE,MAAM,GAAE,UAAS,MAAI,GAAE,UAAS,GAAE,OAAM,OAAG;AAAC,QAAE,MAAG,EAAE,GAAE,IAAE,OAAG,EAAE;AAAA,EAAE,EAAC;AAAC;;;ACMjW,IAAM,YAAY;AAEX,IAAM,kBAAkB,MAAM;AACnC,QAAM,kBAAkB,CACtB,KACA,KACA,UAKG;AACH,QAAI,IAAI,QAAQ;AAAK;AACrB,QAAI,MAAM;AACV,QAAI,WAAW;AAEf,QAAI,SAAS,MAAM;AACjB,iBAAW;AACX,YAAM,SAAS,EAAE,iBAAiB,SAAS,CAAC;AAAA,IAC9C;AAEA,QAAI,UAAU,MAAM;AAClB,iBAAW;AACX,YAAM,SAAS,EAAE,iBAAiB,QAAQ,CAAC;AAAA,IAC7C;AAEA,eAAW,MAAM;AACf,UAAI,CAAC;AAAU,cAAM,SAAS,EAAE,iBAAiB,UAAU,CAAC;AAAA,IAC9D,GAAG,SAAS;AAAA,EACd;AAEA,SAAO;AAAA,IACL;AAAA,EACF;AACF;AAEO,IAAM,cAAc,gBAAgB;;;AC1CpC,SAAS,MAAM,OAAe,KAAa,KAAa;AAC7D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAEO,SAAS,OAAO;AAAC;AAExB,SAAS,eAAe,OAAc;AACpC,QAAM,eAAe;AACvB;AAEA,IAAM,SAAS,oBAAI,IAAI,CAAC,WAAW,cAAc,aAAa,WAAW,CAAC;AAC1E,SAAS,4BAA4B,OAAsB;AACzD,MAAI,OAAO,IAAI,MAAM,GAAG,GAAG;AACzB,mBAAe,KAAK;AACpB,WAAO;AAAA,EACT;AACF;AAEA,IAAM,aAAa,IAAI,gBAAgB;AACvC,IAAM,SAAS,WAAW;AAEnB,SAAS,gBAAgB;AAC9B,SAAO,iBAAiB,kBAAkB,gBAAgB,EAAE,OAAO,CAAC;AACpE,SAAO,iBAAiB,SAAS,gBAAgB,EAAE,SAAS,OAAO,OAAO,CAAC;AAC3E,SAAO,iBAAiB,aAAa,gBAAgB,EAAE,SAAS,OAAO,OAAO,CAAC;AAC/E,SAAO,iBAAiB,WAAW,6BAA6B,EAAE,OAAO,CAAC;AAC5E;AAEO,SAAS,eAAe;AAC7B,cAAY,MAAM;AACpB;AAEO,SAAS,eAAe,WAAwB;AACrD,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AAEA,QAAM,mBAAmB,UAAU,cAAc,KAAK;AACtD,MAAI,CAAC,kBAAkB;AACrB,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AAEA,SAAO;AACT;AAOO,SAAS,kBAAkB,OAAwB,QAAyB;AACjF,SAAO;AAAA,IACL,IAAI,MAAM,IAAI,OAAO,KAAK;AAAA,IAC1B,IAAI,MAAM,IAAI,OAAO,KAAK;AAAA,EAC5B;AACF;AAIO,SAAS,mBAAmB,MAA0C,MAA0C;AACrH,QAAM,aAAa,kBAAkB,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AACrD,QAAM,aAAa,kBAAkB,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AACrD,QAAM,aAAa,EAAE,GAAG,WAAW,IAAI,WAAW,GAAG,GAAG,WAAW,IAAI,WAAW,EAAE;AAEpF,QAAM,eAAe,KAAK,MAAM,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,EAAE,CAAC;AAC5E,QAAM,eAAe,KAAK,MAAM,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,EAAE,CAAC;AAC5E,MAAI,QAAQ,eAAe;AAG3B,QAAM,MAAM;AACZ,MAAI,KAAK,IAAI,QAAQ,CAAC,IAAI,KAAK;AAC7B,YAAQ,IAAI;AAAA,EACd;AAEA,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA;AAAA;AAAA,MAGN,GAAG,WAAW,IAAI,WAAW,KAAK,IAAI;AAAA,MACtC,GAAG,WAAW,IAAI,WAAW,KAAK,IAAI;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,sBAAyB,aAA4B,IAAsB;AACzF,SAAO,CAAC,QAAW;AACjB,QAAI,YAAY,GAAG;AACjB,SAAG,GAAG;AAAA,IACR;AAAA,EACF;AACF;AAEO,IAAM,cACX,CAAC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAMA,CAAC,UACC,cAAc,YAAY,gBAAgB,QAAQ,gBAAgB,aAAa;;;AChF5E,SAAS,qBAAqB,WAAwB,SAAgC;AAC3F,QAAMA,cAAa,IAAI,gBAAgB;AACvC,QAAM,EAAE,QAAAC,QAAO,IAAID;AACnB,QAAM,mBAAmB,eAAe,SAAS;AACjD,QAAM,mBAAmB,SAAS,cAAc,KAAK;AACrD,mBAAiB,MAAM,WAAW;AAClC,QAAM,YAAY,iBAAiB,YAAY,SAAS,cAAc,KAAK,CAAC;AAC5E,YAAU,MAAM,QAAQ,gBAAgB,OAAO;AAC/C,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,UAAU;AAC1B,QAAM,WAAW,UAAU,YAAY,SAAS,cAAc,KAAK,CAAC;AACpE,WAAS,MAAM,UAAU;AAIzB,MAAI,0BAA0B;AAC9B,MAAI,2BAA2B;AAE/B,QAAM,eAAwE;AAAA,IAC5E,iBAAiB,QAAQ,mBAAmB,iBAAiB;AAAA,IAC7D,eAAe,QAAQ,iBAAiB;AAAA,IACxC,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,YAAY,QAAQ;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,IACxB,YAAY,QAAQ;AAAA,IACpB,eAAe,QAAQ,iBAAiB;AAAA,IACxC,mBAAmB,QAAQ,qBAAqB;AAAA,EAClD;AAEA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,QAAQ,EAAiC;AAAA,IAC7C,iBAAiB;AAAA,IACjB,SAAS;AAAA,EACX,CAAC;AAED,MAAI,SAAwC,UAAU,gBAAgB;AAEtE,WAAS,UAAU,SAAsB;AACvC,UAAM,SAAS,QAAQ,sBAAsB;AAC7C,WAAO,EAAE,MAAM,OAAO,MAAM,KAAK,OAAO,IAAI;AAAA,EAC9C;AAEA,WAAS,UAAU,OAAe;AAChC,WAAO,0BAA0B;AAAA,EACnC;AAEA,WAAS,UAAU,OAAe;AAChC,WAAO,2BAA2B;AAAA,EACpC;AAEA,WAAS,aAAa,MAAc;AAClC,UAAM,OAAO,SAAS,cAAc;AACpC,WAAO,MAAM,MAAM,MAAM,UAAU,IAAI,CAAC,IAAI;AAAA,EAC9C;AAEA,WAAS,YAAY,KAAa;AAChC,UAAM,OAAO,SAAS,eAAe;AACrC,WAAO,MAAM,KAAK,MAAM,UAAU,IAAI,CAAC,IAAI;AAAA,EAC7C;AAEA,WAAS,YAAY,OAAqB;AACxC,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI,QAAQ;AACV,gBAAU,aAAa,MAAM,UAAU,OAAO,IAAI;AAClD,gBAAU,YAAY,MAAM,UAAU,OAAO,GAAG;AAChD,oBAAe,UAAU,QAAS;AAClC,oBAAe,UAAU,QAAS;AAClC,gBAAU,MAAM,YAAY,eAAe,CAAC,cAAc,QAAQ,CAAC,cAAc;AACjF,eAAS,MAAM,WAAW,yBAA8B,UAAU,QAAQ,UAAU;AAAA,IACtF;AAAA,EACF;AAEA,iBAAe,qBAAqB;AAClC,gBAAY,gBAAgB,WAAW,iBAAiB,KAAK;AAC7D,cAAU,MAAM,UAAU;AAC1B,aAAS,MAAM,UAAU;AAEzB,QAAI,iBAAiB;AACnB,YAAM,UAAU,gBAAgB,MAAM,GAAG;AACzC,cAAQ,QAAQ,CAAC,cAAc,WAAW,UAAU,IAAI,SAAS,CAAC;AAAA,IACpE;AAEA,QAAI,CAAC;AAAmB,oBAAc;AAAA,EACxC;AAEA,WAAS,qBAAqB;AAC5B,cAAU,MAAM,UAAU;AAC1B,aAAS,MAAM,UAAU;AAEzB,QAAI,iBAAiB;AACnB,YAAM,UAAU,gBAAgB,MAAM,GAAG;AACzC,cAAQ,QAAQ,CAAC,cAAc,WAAW,UAAU,OAAO,SAAS,CAAC;AAAA,IACvE;AAEA,QAAI,CAAC;AAAmB,mBAAa;AAAA,EACvC;AAEA,WAAS,eAAe;AACtB,aAAS,UAAU,gBAAgB;AAAA,EACrC;AAEA,iBAAe,QAAQ;AACrB,QAAI,eAAe;AACjB,eAAS,YAAY;AAAA,IACvB,OAAO;AACL,eAAS,MAAM,aAAa;AAAA,IAC9B;AAGA,cAAU,iBAAiB,eAAe,aAAa,EAAE,QAAAC,QAAO,CAAC;AACjE,cAAU,iBAAiB,eAAe,aAAa,EAAE,QAAAA,QAAO,CAAC;AACjE,cAAU,iBAAiB,gBAAgB,oBAAoB,EAAE,QAAAA,QAAO,CAAC;AACzE,cAAU,iBAAiB,gBAAgB,oBAAoB,EAAE,QAAAA,QAAO,CAAC;AACzE,WAAO,iBAAiB,UAAU,cAAc,EAAE,QAAAA,QAAO,CAAC;AAC1D,cAAU,iBAAiB,YAAY,cAAc,EAAE,QAAAA,QAAO,CAAC;AAG/D,eAAW,YAAY,gBAAgB;AAGvC,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,CAAC,CAAC;AACrD,UAAM,gBAAgB,UAAU,sBAAsB;AACtD,8BAA0B,cAAc;AACxC,+BAA2B,cAAc;AAGzC,QAAI,YAAY;AACd,uBAAiB,MAAM,QAAQ,WAAW,QAAQ;AAClD,uBAAiB,MAAM,SAAS,WAAW,SAAS;AAAA,IACtD,OAAO;AAEL,uBAAiB,MAAM,QAAQ,0BAA0B;AACzD,uBAAiB,MAAM,SAAS,2BAA2B;AAAA,IAC7D;AAEA,cAAU,QAAS,0BAA0B,QAAS;AACtD,cAAU,SAAU,2BAA2B,QAAS;AAGxD,UAAM,kBAAkB,iBAAiB,sBAAsB;AAC/D,UAAM,WAAW,gBAAgB,OAAO,cAAc;AACtD,UAAM,UAAU,gBAAgB,MAAM,cAAc;AACpD,eAAW,MAAM,gBAAgB;AACjC,aAAS,MAAM,WAAW;AAC1B,aAAS,MAAM,OAAO,WAAW;AACjC,aAAS,MAAM,MAAM,UAAU;AAC/B,aAAS,MAAM,QAAS,WAAW,QAAQ,QAAS,gBAAgB;AACpE,aAAS,MAAM,SAAU,WAAW,SAAS,QAAS,gBAAgB;AAAA,EACxE;AAEA,QAAM;AAEN,SAAO;AAAA,IACL,SAAS,MAAM;AACb,MAAAD,YAAW,MAAM;AACjB,gBAAU,SAAS,QAAQ,KAAK,UAAU,YAAY,QAAQ;AAE9D,UAAI,cAAc,WAAW,SAAS,gBAAgB,GAAG;AACvD,mBAAW,YAAY,gBAAgB;AACvC;AAAA,MACF;AAEA,gBAAU,SAAS,gBAAgB,KAAK,UAAU,YAAY,gBAAgB;AAAA,IAChF;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,UAAU,CAAC,aAAwC;AACjD,YAAM,SAAS,QAAQ;AAAA,IACzB;AAAA,EACF;AACF;;;AC5LO,SAAS,oBAAoB,WAAwB,UAAgC,CAAC,GAAG;AAC9F,MAAI,kBAAiC;AACrC,QAAM,mBAAmB,eAAe,SAAS;AACjD,QAAM,eAA6F;AAAA,IACjG,YAAY,QAAQ,cAAc;AAAA,IAClC,iBAAiB,QAAQ,mBAAmB,iBAAiB;AAAA,IAC7D,qBAAqB,QAAQ,uBAAuB;AAAA,EACtD;AAEA,QAAM,EAAE,qBAAqB,YAAY,gBAAgB,IAAI;AAE7D,QAAM,QAAQ,EAAgC;AAAA,IAC5C,iBAAiB;AAAA,EACnB,CAAC;AAED,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,UAAQ,gBAAgB,QAAQ,UAAU,MAAM,QAAQ,eAAe;AACvE,UAAQ,gBAAgB,cAAc,UAAU,YAAY,QAAQ,eAAe;AACnF,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,MAAM;AACtB,YAAU,MAAM,OAAO;AAGvB,MAAI,qBAAqB;AACvB,cAAU,MAAM,qBAAqB,IAAI;AACzC,cAAU,MAAM,uBAAuB,IAAI;AAC3C,cAAU,gBAAgB,MAAM;AAAA,EAClC;AAEA,QAAM,oBAAoB,CAAC,UAAwB;AACjD,WAAO,mBAAmB,MAAM,cAAc;AAAA,EAChD;AAEA,WAAS,mBAAmB,OAAqB;AAC/C,QAAI,oBAAoB,MAAM;AAC5B,wBAAkB,MAAM;AACxB,gBAAU,YAAY,SAAS;AAC/B,gBAAU,MAAM,UAAU;AAC1B,YAAM,iBAAiB,iBAAiB,cAAc;AACtD,YAAM,kBAAkB,iBAAiB,eAAe;AACxD,gBAAU,MAAM,QAAQ,GAAG,cAAc;AACzC,gBAAU,MAAM,SAAS,GAAG,eAAe;AAC3C,kBAAY,gBAAgB,WAAW,iBAAiB,KAAK;AAE7D,kBAAY,KAAK;AAEjB,YAAM,gBAAgB,WAAW,cAAc;AAAA,IACjD;AAAA,EACF;AAEA,WAAS,kBAAkB,OAAqB;AAC9C,QAAI,kBAAkB,KAAK,GAAG;AAC5B,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,WAAS,eAAe,OAAqB;AAC3C,QAAI,kBAAkB,KAAK,GAAG;AAC5B,gBAAU,SAAS,SAAS,KAAK,UAAU,YAAY,SAAS;AAChE,gBAAU,MAAM,UAAU;AAC1B,gBAAU,MAAM,YAAY;AAC5B,mBAAa;AACb,wBAAkB;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,qBAAqB,CAAC,iBAAyB;AACnD,UAAM,QAAQ,UAAU;AACxB,QAAI,eAAe;AAAG,aAAO;AAC7B,QAAI,eAAe,QAAQ,aAAa;AAAO,aAAO,CAAC,SAAS,aAAa;AAC7E,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,CAAC,iBAAyB;AACnD,UAAM,SAAS,UAAU;AACzB,QAAI,eAAe;AAAG,aAAO;AAC7B,QAAI,eAAe,SAAS,aAAa;AAAQ,aAAO,CAAC,UAAU,aAAa;AAChF,WAAO;AAAA,EACT;AAEA,WAAS,YAAY,OAAqB;AACxC,cAAU,MAAM,UAAU;AAE1B,UAAM,gBAAgB,UAAU,sBAAsB;AACtD,UAAM,aAAa,MAAM,UAAU,cAAc;AACjD,UAAM,aAAa,MAAM,UAAU,cAAc;AAEjD,UAAM,mBAAmB,mBAAmB,CAAC,aAAa,aAAa,UAAU;AACjF,UAAM,mBAAmB,mBAAmB,CAAC,aAAa,aAAa,UAAU;AACjF,cAAU,MAAM,YAAY,aAAa,gBAAgB,OAAO,gBAAgB;AAAA,EAClF;AAEA,QAAMA,cAAa,IAAI,gBAAgB;AACvC,QAAM,EAAE,QAAAC,QAAO,IAAID;AACnB,YAAU,iBAAiB,gBAAgB,oBAAoB,EAAE,QAAAC,QAAO,CAAC;AACzE,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,QAAAA,QAAO,CAAC;AACvE,YAAU,iBAAiB,gBAAgB,gBAAgB,EAAE,QAAAA,QAAO,CAAC;AACrE,YAAU;AAAA,IACR;AAAA,IACA,CAAC,UAAU;AACT,6BAAuB,MAAM,eAAe;AAAA,IAC9C;AAAA,IACA,EAAE,QAAAA,QAAO;AAAA,EACX;AAEA,SAAO;AAAA,IACL,SAAS,MAAM;AACb,MAAAD,YAAW,MAAM;AACjB,gBAAU,SAAS,SAAS,KAAK,UAAU,YAAY,SAAS;AAChE,YAAM,QAAQ;AAAA,IAChB;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,EAClB;AACF;;;ACxHA,IAAM,aAAa;AAgBnB,IAAM,sBAA2C;AAAA,EAC/C,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAiB;AACnB;AAEA,IAAM,iCAAiC,MAAM;AAEtC,SAAS,qBAAqB,WAAwB,UAAiC,CAAC,GAAG;AAChG,QAAM,mBAAmB,eAAe,SAAS;AACjD,QAAM,eAAgD;AAAA,IACpD,SAAS,QAAQ,WAAW;AAAA,IAC5B,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,yBAAyB,QAAQ,2BAA2B;AAAA,IAC5D,cAAc,EAAE,GAAG,qBAAqB,GAAG,QAAQ,aAAa;AAAA,IAChE,yBAAyB,QAAQ,2BAA2B;AAAA,EAC9D;AAEA,QAAM,QAAQ,EAAiC,aAAa,YAAmC;AAE/F,QAAM,yBAAyB,MAAM;AACnC,WAAO,CAAC,IAAI,GAAG,EAAE,SAAS,MAAM,SAAS,EAAE,kBAAkB,GAAG;AAAA,EAClE;AAEA,QAAM,qBAAqB,CAAC,cAAsB,aAAqB,wBAAiC;AACtG,QAAI,eAAe;AAAG,aAAO;AAE7B,UAAM,QAAQ,sBAAsB,UAAU,eAAe,UAAU;AACvE,QAAI,eAAe,QAAQ,cAAc;AAAO,aAAO,CAAC,SAAS,cAAc;AAC/E,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,CAAC,cAAsB,aAAqB,wBAAiC;AACtG,QAAI,eAAe;AAAG,aAAO;AAE7B,UAAM,SAAS,sBAAsB,UAAU,cAAc,UAAU;AACvE,QAAI,eAAe,SAAS,cAAc;AAAQ,aAAO,CAAC,UAAU,cAAc;AAClF,WAAO;AAAA,EACT;AAEA,QAAM,uBAAuB,CAAC,gBAAwB;AACpD,UAAM,aAAa,UAAU,cAAc;AAC3C,UAAM,aAAa,UAAU,eAAe;AAC5C,UAAM,eAAe,MAAM,SAAS;AACpC,UAAM,eAAe,aAAa,aAAa,oBAAoB,aAAa;AAChF,UAAM,eAAe,aAAa,aAAa,oBAAoB,aAAa;AAEhF,UAAM,SAAS;AAAA,MACb;AAAA,MACA,kBAAkB,mBAAmB,CAAC,cAAc,cAAc,YAAY,aAAa,KAAK;AAAA,MAChG,kBAAkB,mBAAmB,CAAC,cAAc,cAAc,YAAY,aAAa,KAAK;AAAA,IAClG,CAAC;AAAA,EACH;AAGA,MAAI,mBAA8D;AAClE,MAAI,gBAAgB;AACpB,QAAM,aAAa,oBAAI,IAA6B;AAEpD,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,MAAI,SAAS;AACb,MAAI,SAAS;AAEb,YAAU,MAAM,WAAW;AAC3B,mBAAiB,MAAM,kBAAkB;AAEzC,WAAS,aAAa;AACpB,UAAM,eAAe,MAAM,SAAS;AACpC,qBAAiB,MAAM,YAAY,aAAa,aAAa,gBAAgB,OAAO,aAAa,gBAAgB,aAAa,aAAa,WAAW;AACtJ,cAAU,MAAM,SAAS,GAAG,aAAa,eAAe;AAAA,EAC1D;AAEA,WAAS,SAAS,UAAqC;AACrD,UAAM,MAAM,MAAM;AAChB,YAAM,eAAe,MAAM,SAAS;AACpC,UAAI,OAAO,SAAS,WAAW,aAAa,SAAS,WAAW,aAAa,QAAQ;AACnF,cAAM,SAAS;AAAA,UACb,QAAQ,SAAS;AAAA,QACnB,CAAC;AAED,YAAI,CAAC,SAAS,QAAQ;AACpB;AAAA,QACF;AAAA,MACF;AAEA,UAAI,OAAO,SAAS,oBAAoB,UAAU;AAChD,cAAM,qBAAqB,SAAS;AACpC,cAAM,SAAS;AAAA,UACb,iBAAiB;AAAA,QACnB,CAAC;AAAA,MACH;AAEA,UAAI,OAAO,SAAS,gBAAgB,YAAY,SAAS,gBAAgB,aAAa,aAAa;AACjG,cAAM,iBAAiB,MAAM,SAAS,aAAa,GAAG,aAAa,OAAO;AAE1E,YAAI,mBAAmB,aAAa,aAAa;AAC/C;AAAA,QACF;AAEA,6BAAqB,cAAc;AAAA,MACrC;AAAA,IACF,CAAC;AAED,eAAW;AAAA,EACb;AAEA,WAAS,iBAAiB,EAAE,OAAO,GAAG,EAAE,GAA4C;AAClF,UAAM,gBAAgB,UAAU,sBAAsB;AACtD,UAAM,eAAe,MAAM,SAAS;AACpC,UAAM,sBAAsB,uBAAuB;AAEnD,QAAI,aAAa;AACjB,QAAI,aAAa;AAEjB,YAAQ,aAAa,kBAAkB,KAAK;AAAA,MAC1C,KAAK;AACH,qBAAa,IAAI,cAAc;AAC/B,qBAAa,IAAI,cAAc;AAC/B;AAAA,MACF,KAAK;AACH,qBAAa,KAAK,IAAI,IAAI,cAAc,KAAK;AAC7C,qBAAa,KAAK,IAAI,IAAI,cAAc,GAAG;AAC3C;AAAA,MACF,KAAK;AACH,qBAAa,KAAK,IAAI,IAAI,cAAc,KAAK;AAC7C,qBAAa,KAAK,IAAI,IAAI,cAAc,MAAM;AAC9C;AAAA,MACF,KAAK;AACH,qBAAa,KAAK,IAAI,IAAI,cAAc,IAAI;AAC5C,qBAAa,KAAK,IAAI,IAAI,cAAc,MAAM;AAC9C;AAAA,IACJ;AAEA,UAAM,QAAQ,sBAAsB,aAAa,mBAAmB,aAAa;AACjF,UAAM,QAAQ,sBAAsB,aAAa,mBAAmB,aAAa;AAEjF,UAAM,eAAe,aAAa,SAAS,aAAa;AACxD,UAAM,eAAe,aAAa,SAAS,aAAa;AAExD,UAAM,iBAAiB;AAAA,MACrB,aAAa,cAAc,QAAQ,aAAa,iBAAiB,aAAa;AAAA,MAC9E;AAAA,MACA,aAAa;AAAA,IACf;AAEA,UAAM,OAAO,mBAAmB,CAAC,cAAc,iBAAiB,YAAY,gBAAgB,mBAAmB;AAC/G,UAAM,OAAO,mBAAmB,CAAC,cAAc,iBAAiB,YAAY,gBAAgB,mBAAmB;AAE/G,UAAM,SAAS;AAAA,MACb,aAAa;AAAA,MACb,kBAAkB,sBAAsB,OAAO;AAAA,MAC/C,kBAAkB,sBAAsB,OAAO;AAAA,IACjD,CAAC;AAAA,EACH;AAEA,WAAS,sCAAsC;AAC7C,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,EAAE,GAAG,EAAE,IAAI,WAAW,OAAO,EAAE,KAAK,EAAE;AAC5C,YAAM,sBAAsB,uBAAuB;AACnD,eAAS,sBAAsB,IAAI;AACnC,eAAS,sBAAsB,IAAI;AAAA,IACrC;AAEA,UAAM,eAAe,MAAM,SAAS;AACpC,oBAAgB,aAAa;AAC7B,oBAAgB,aAAa;AAAA,EAC/B;AAEA,WAAS,aAAa,OAAmB;AACvC,UAAM,eAAe;AAErB,QAAI,MAAM,SAAS,EAAE,gBAAgB,aAAa,WAAW,MAAM,SAAS,GAAG;AAC7E;AAAA,IACF;AAEA,UAAM,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,YAAY,UAAU;AAC1D,qBAAiB,EAAE,OAAO,GAAG,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC;AAC9D,eAAW;AAEX,wCAAoC;AAAA,EACtC;AAEA,WAAS,mBAAmB,OAAqB;AAC/C,UAAM,eAAe;AACrB,UAAM,EAAE,SAAS,SAAS,UAAU,IAAI;AACxC,eAAW,CAAC,eAAe,KAAK,WAAW,QAAQ,GAAG;AACpD,UAAI,oBAAoB,WAAW;AACjC,mBAAW,IAAI,iBAAiB,EAAE,GAAG,SAAS,GAAG,QAAQ,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,UAAM,EAAE,aAAa,gBAAgB,IAAI,MAAM,SAAS;AACxD,QAAI,WAAW,SAAS,KAAK,gBAAgB,GAAG;AAC9C,YAAM,sBAAsB,uBAAuB;AACnD,YAAM,oBAAoB,sBAAsB,UAAU;AAC1D,YAAM,oBAAoB,sBAAsB,UAAU;AAE1D,UAAI,UAAU;AACd,UAAI,UAAU;AAEd,cAAQ,kBAAkB,KAAK;AAAA,QAC7B,KAAK;AACH,oBAAU,oBAAoB;AAC9B,oBAAU,oBAAoB;AAC9B;AAAA,QACF,KAAK;AACH,oBAAU,oBAAoB;AAC9B,oBAAU,SAAS;AACnB;AAAA,QACF,KAAK;AACH,oBAAU,SAAS;AACnB,oBAAU,SAAS;AACnB;AAAA,QACF,KAAK;AACH,oBAAU,SAAS;AACnB,oBAAU,oBAAoB;AAC9B;AAAA,MACJ;AAEA,YAAM,SAAS;AAAA,QACb,kBAAkB,mBAAmB,gBAAgB,SAAS,aAAa,KAAK;AAAA,QAChF,kBAAkB,mBAAmB,gBAAgB,SAAS,aAAa,KAAK;AAAA,MAClF,CAAC;AACD,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,QAAM,iBAAiB;AAAA,IACrB,gBAAgB;AAAA;AAAA,IAEhB,OAAO,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE;AAAA;AAAA,IAE7B,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE;AAAA,EAChC;AAEA,WAAS,YAAY,iBAA2C;AAE9D,UAAM,eAAe,MAAM,SAAS;AAEpC,mBAAe,iBAAiB;AAChC,mBAAe,QAAQ;AAAA,MACrB,GAAG,aAAa;AAAA,MAChB,GAAG,aAAa;AAAA,MAChB,MAAM,aAAa;AAAA,IACrB;AAEA,QAAI,aAAa,cAAc,GAAG;AAChC,qBAAe,SAAS;AAAA,QACtB,GAAG;AAAA,QACH,GAAG;AAAA,QACH,MAAM;AAAA,MACR;AAAA,IACF,OAAO;AACL,qBAAe,SAAS;AAAA,QACtB,MAAM,aAAa;AAAA,QACnB,GAAG,gBAAgB,KAAK,IAAI,aAAa;AAAA,QACzC,GAAG,gBAAgB,KAAK,IAAI,aAAa;AAAA,MAC3C;AAAA,IACF;AAEA,aAAS,KAAK,GAAW,GAAW,GAAmB;AACrD,aAAO,KAAK,IAAI,KAAK,IAAI;AAAA,IAC3B;AAEA,aAAS,MAAM,WAAgC;AAC7C,UAAI,eAAe,mBAAmB,MAAM;AAC1C,uBAAe,iBAAiB;AAAA,MAClC;AAGA,UAAI,KAAK,YAAY,eAAe,kBAAkB,aAAa;AACnE,UAAI,IAAI,GAAG;AACT,YAAI;AAAA,MACN;AAEA,YAAM,SAAS;AAAA,QACb,kBAAkB,KAAK,eAAe,MAAM,GAAG,eAAe,OAAO,GAAG,CAAC;AAAA,QACzE,kBAAkB,KAAK,eAAe,MAAM,GAAG,eAAe,OAAO,GAAG,CAAC;AAAA,QACzE,aAAa,KAAK,eAAe,MAAM,MAAM,eAAe,OAAO,MAAM,CAAC;AAAA,MAC5E,CAAC;AACD,iBAAW;AAEX,UAAI,IAAI,GAAG;AACT,8BAAsB,KAAK;AAAA,MAC7B;AAAA,IACF;AAEA,0BAAsB,KAAK;AAAA,EAC7B;AAGA,MAAI,aAAoC;AACxC,QAAM,qBAAqB;AAE3B,WAAS,kBAAkB,OAAmB;AAC5C,QAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B;AAAA,IACF;AAEA,QAAI,eAAe,MAAM;AACvB,mBAAa,WAAW,MAAM;AAC5B,qBAAa;AAAA,MACf,GAAG,kBAAkB;AAAA,IACvB,OAAO;AACL,mBAAa,UAAU;AACvB,mBAAa;AAEb,YAAM,OAAO,UAAU,sBAAsB;AAC7C,YAAM,QAAQ,MAAM,QAAQ,CAAC;AAC7B,kBAAY;AAAA,QACV,GAAG,MAAM,UAAU,KAAK;AAAA,QACxB,GAAG,MAAM,UAAU,KAAK;AAAA,MAC1B,CAAC;AACD;AAAA,IACF;AAAA,EACF;AAEA,WAAS,iBAAiB,OAAmB;AAC3C,QAAI,aAAa,wBAAwB;AAAG,YAAM,eAAe;AACjE,QAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,YAAM,eAAe;AACrB,YAAM,sBAAsB,CAAC,GAAG,MAAM,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,EAAE;AAK1F,UAAI,qBAAqB,MAAM;AAC7B,cAAM,EAAE,OAAO,OAAO,IAAI,mBAAmB,kBAAkB,mBAAmB;AAClF,yBAAiB,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,aAAa,gBAAgB,GAAG,OAAO,CAAC;AAAA,MACtF;AAEA,yBAAmB;AACnB,iBAAW;AACX;AAAA,IACF;AAAA,EACF;AAEA,WAAS,mBAAmB,OAAqB;AAC/C,QAAI,MAAM,gBAAgB,WAAW,CAAC,aAAa,wBAAwB;AAAG;AAC9E,UAAM,eAAe;AACrB,QAAI,WAAW,SAAS,GAAG;AACzB;AAAA,IACF;AAEA,QAAI,eAAe;AACjB,oBAAc;AACd,sBAAgB;AAAA,IAClB;AAEA,UAAM,EAAE,SAAS,SAAS,UAAU,IAAI;AAExC,UAAM,eAAe,MAAM,SAAS;AACpC,oBAAgB,aAAa;AAC7B,oBAAgB,aAAa;AAE7B,UAAM,sBAAsB,uBAAuB;AACnD,aAAS,sBAAsB,UAAU;AACzC,aAAS,sBAAsB,UAAU;AACzC,eAAW,IAAI,WAAW,EAAE,GAAG,SAAS,GAAG,QAAQ,CAAC;AAAA,EACtD;AAEA,WAAS,iBAAiB,OAAqB;AAC7C,UAAM,eAAe;AACrB,eAAW,OAAO,MAAM,SAAS;AAGjC,QAAI,WAAW,OAAO,GAAG;AACvB,yBAAmB;AAAA,IACrB;AAEA,QAAI,WAAW,SAAS,KAAK,CAAC,eAAe;AAC3C,mBAAa;AACb,sBAAgB;AAAA,IAClB;AAEA,wCAAoC;AAAA,EACtC;AAEA,WAAS,oBAAoB,OAAqB;AAChD,UAAM,eAAe;AACrB,eAAW,OAAO,MAAM,SAAS;AACjC,uBAAmB;AACnB,QAAI,CAAC,eAAe;AAClB,mBAAa;AACb,sBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,WAAS,mBAAmB;AAC1B,WAAO,MAAM,SAAS,EAAE;AAAA,EAC1B;AAEA,QAAM,cAAc,sBAAsB,kBAAkB,YAAY;AACxE,QAAM,oBAAoB,sBAAsB,kBAAkB,kBAAkB;AACpF,QAAM,qBAAqB,sBAAsB,kBAAkB,mBAAmB;AACtF,QAAM,oBAAoB,sBAAsB,kBAAkB,kBAAkB;AACpF,QAAM,kBAAkB,sBAAsB,kBAAkB,gBAAgB;AAChF,QAAM,mBAAmB,sBAAsB,kBAAkB,iBAAiB;AAClF,QAAM,kBAAkB,sBAAsB,kBAAkB,gBAAgB;AAEhF,QAAMA,cAAa,IAAI,gBAAgB;AACvC,QAAM,EAAE,QAAAC,QAAO,IAAID;AACnB,YAAU,iBAAiB,SAAS,aAAa,EAAE,QAAAC,QAAO,CAAC;AAC3D,YAAU,iBAAiB,cAAc,kBAAkB,EAAE,QAAAA,QAAO,CAAC;AACrE,YAAU,iBAAiB,aAAa,iBAAiB,EAAE,QAAAA,QAAO,CAAC;AACnE,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,QAAAA,QAAO,CAAC;AACvE,YAAU,iBAAiB,gBAAgB,oBAAoB,EAAE,QAAAA,QAAO,CAAC;AACzE,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,QAAAA,QAAO,CAAC;AACvE,YAAU,iBAAiB,aAAa,iBAAiB,EAAE,QAAAA,QAAO,CAAC;AACnE,YAAU;AAAA,IACR;AAAA,IACA,MAAM;AACJ,sBAAgB;AAChB,mBAAa;AAAA,IACf;AAAA,IACA,EAAE,QAAAA,QAAO;AAAA,EACX;AAGA,MAAI,MAAM,SAAS,EAAE,gBAAgB,oBAAoB,aAAa;AACpE,yBAAqB,MAAM,SAAS,EAAE,WAAW;AACjD,eAAW;AAAA,EACb;AAEA,SAAO;AAAA,IACL,UAAU;AACR,MAAAD,YAAW,MAAM;AACjB,YAAM,QAAQ;AAAA,IAChB;AAAA,IACA,WAAW,MAAM;AAAA,IACjB;AAAA,IACA,UAAU,MAAM;AAAA,EAClB;AACF;;;AClcO,SAAS,qBAAqB,WAAwB,UAAiC,CAAC,GAAG;AAChG,QAAM,mBAAmB,eAAe,SAAS;AACjD,QAAM,eAAwE;AAAA,IAC5E,YAAY,QAAQ,cAAc;AAAA,IAClC,iBAAiB,QAAQ,mBAAmB,iBAAiB;AAAA,IAC7D,mBAAmB,QAAQ,qBAAqB;AAAA,EAClD;AAEA,QAAM,EAAE,YAAY,iBAAiB,kBAAkB,IAAI;AAE3D,MAAI,WAAW;AAEf,QAAM,QAAQ,EAAiC;AAAA,IAC7C,iBAAiB;AAAA,EACnB,CAAC;AAED,QAAM,iBAAiB,iBAAiB,cAAc;AACtD,QAAM,kBAAkB,iBAAiB,eAAe;AACxD,QAAM,YAAY,UAAU,YAAY,SAAS,cAAc,KAAK,CAAC;AACrE,YAAU,MAAM,QAAQ,gBAAgB,OAAO;AAC/C,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,UAAU;AAC1B,YAAU,MAAM,QAAQ,GAAG,cAAc;AACzC,YAAU,MAAM,SAAS,GAAG,eAAe;AAC3C,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,MAAM;AACtB,YAAU,MAAM,OAAO;AAEvB,WAAS,kBAAkB,OAAqB;AAC9C,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,gBAAY,KAAK;AAAA,EACnB;AAEA,QAAM,qBAAqB,CAAC,iBAAyB;AACnD,UAAM,QAAQ,UAAU;AACxB,QAAI,eAAe;AAAG,aAAO;AAC7B,QAAI,eAAe,QAAQ,aAAa;AAAO,aAAO,CAAC,SAAS,aAAa;AAC7E,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,CAAC,iBAAyB;AACnD,UAAM,SAAS,UAAU;AACzB,QAAI,eAAe;AAAG,aAAO;AAC7B,QAAI,eAAe,SAAS,aAAa;AAAQ,aAAO,CAAC,UAAU,aAAa;AAChF,WAAO;AAAA,EACT;AAEA,WAAS,YAAY,OAAqB;AACxC,cAAU,MAAM,UAAU;AAC1B,gBAAY,gBAAgB,WAAW,iBAAiB,KAAK;AAE7D,UAAM,gBAAgB,UAAU,sBAAsB;AACtD,UAAM,aAAa,MAAM,UAAU,cAAc;AACjD,UAAM,aAAa,MAAM,UAAU,cAAc;AAEjD,UAAM,mBAAmB,mBAAmB,CAAC,aAAa,aAAa,UAAU;AACjF,UAAM,mBAAmB,mBAAmB,CAAC,aAAa,aAAa,UAAU;AAEjF,cAAU,MAAM,YAAY,aAAa,gBAAgB,OAAO,gBAAgB;AAAA,EAClF;AAEA,WAAS,kBAAkB,OAAqB;AAC9C,QAAI,UAAU;AACZ,iBAAW;AACX,gBAAU,MAAM,UAAU;AAC1B;AAAA,IACF;AAEA,gBAAY,KAAK;AACjB,eAAW;AAAA,EACb;AAEA,QAAMA,cAAa,IAAI,gBAAgB;AACvC,QAAM,EAAE,QAAAC,QAAO,IAAID;AACnB,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,QAAAC,QAAO,CAAC;AACvE,YAAU,iBAAiB,gBAAgB,oBAAoB,OAAO,eAAe,EAAE,QAAAA,QAAO,CAAC;AAC/F,YAAU,iBAAiB,gBAAgB,oBAAoB,OAAO,cAAc,EAAE,QAAAA,QAAO,CAAC;AAC9F,YAAU,iBAAiB,eAAe,mBAAmB,EAAE,QAAAA,QAAO,CAAC;AACvE,YAAU,iBAAiB,YAAY,cAAc,EAAE,QAAAA,QAAO,CAAC;AAE/D,SAAO;AAAA,IACL,SAAS,MAAM;AACb,MAAAD,YAAW,MAAM;AACjB,gBAAU,SAAS,SAAS,KAAK,UAAU,YAAY,SAAS;AAChE,YAAM,QAAQ;AAAA,IAChB;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,EAClB;AACF;;;AC5GO,IAAM,0BAA0B,CAAC,YACtC,YAAY;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AACb,CAAC;;;ACNI,IAAM,oBAAoB,CAAC,YAChC,YAAY;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AACb,CAAC;;;ACAI,IAAM,YAAY,OAAO,EAAE,OAAO,WAAW,WAAW,aAAa,WAAW,EAAE,MAAoB;AAC3G,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,QAAQ,MAAM,gBAAgB,MAAM,cAAc;AACxD,QAAM,qBAAqB,WAAW;AACtC,QAAM,oBAAoB,MAAM,cAAc;AAC9C,QAAM,qBAAqB,MAAM,eAAe;AAChD,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,gBAAgB,OAAO,WAAW,IAAI;AAE5C,QAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,IAAI,KAAK;AAClD,QAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,IAAI,KAAK;AAElD,gBAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,gBAAgB,IAAI,MAAM;AAChC,gBAAc,MAAM,OAAO,UAAU;AAErC,QAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,CAAC,CAAC;AAErD,QAAM,gBAAgB,SAAS,cAAc,QAAQ;AACrD,QAAM,uBAAuB,cAAc,WAAW,IAAI;AAE1D,MAAI,uBAAuB,MAAM,uBAAuB,KAAK;AAC3D,kBAAc,QAAQ,cAAc;AACpC,kBAAc,SAAS,cAAc;AAAA,EACvC,OAAO;AACL,kBAAc,QAAQ,cAAc;AACpC,kBAAc,SAAS,cAAc;AAAA,EACvC;AAEA,uBAAqB,UAAU,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAChE,MAAI,uBAAuB,MAAM,uBAAuB,KAAK;AAC3D,yBAAqB,UAAU,cAAc,SAAS,GAAG,cAAc,QAAQ,CAAC;AAAA,EAClF,OAAO;AACL,yBAAqB,UAAU,cAAc,QAAQ,GAAG,cAAc,SAAS,CAAC;AAAA,EAClF;AACA,uBAAqB,OAAQ,qBAAqB,KAAK,KAAM,GAAG;AAChE,uBAAqB,UAAU,eAAe,CAAC,cAAc,QAAQ,GAAG,CAAC,cAAc,SAAS,CAAC;AAEjG,SAAO,cAAc,UAAU;AACjC","sourcesContent":["function f(o){let r=new Set,s=!1,a=o,e,c=(t={})=>{e={...e,...t},i();},i=()=>{if(s)return;let t=!1;if(e){for(let n in e)if(a[n]!==e[n]){t=!0;break}}t&&(a={...a,...e},r.forEach(n=>n({state:a,updatedProperties:e})),e=void 0);};return {subscribe:t=>(r.add(t),()=>{r.delete(t);}),cleanup:()=>r.clear(),getState:()=>a,setState:c,batch:t=>{s=!0,t(),s=!1,i();}}}\n\nexport { f as a };\n","import { createStore } from \"@namnode/store\"\nimport { ZoomedImgStatus } from \"./types\"\n\n// There is a scenario where the image is already in the cache,\n// we don't want to trigger the loading state too quickly in that case\n// 50ms should be enough to wait before triggering the loading state\nconst THRESHOLD = 50\n\nexport const makeImageLoader = () => {\n const createZoomImage = (\n img: HTMLImageElement,\n src: string,\n store: ReturnType<\n typeof createStore<{\n zoomedImgStatus: ZoomedImgStatus\n }>\n >,\n ) => {\n if (img.src === src) return\n img.src = src\n let complete = false\n\n img.onload = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"loaded\" })\n }\n\n img.onerror = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"error\" })\n }\n\n setTimeout(() => {\n if (!complete) store.setState({ zoomedImgStatus: \"loading\" })\n }, THRESHOLD)\n }\n\n return {\n createZoomImage,\n }\n}\n\nexport const imageLoader = makeImageLoader()\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { clamp, disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageHoverOptions = {\n customZoom: { width: number; height: number }\n zoomImageSource?: string\n zoomLensClass?: string\n zoomLensScale?: number\n zoomTarget: HTMLElement\n zoomTargetClass?: string\n scale?: number\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageHoverState = {\n zoomedImgStatus: ZoomedImgStatus\n enabled: boolean\n}\n\nexport type ZoomImageHoverStateUpdate = { enabled: boolean }\n\nexport function createZoomImageHover(container: HTMLElement, options: ZoomImageHoverOptions) {\n const controller = new AbortController()\n const { signal } = controller\n const sourceImgElement = getSourceImage(container)\n const zoomedImgWrapper = document.createElement(\"div\")\n zoomedImgWrapper.style.overflow = \"hidden\"\n const zoomedImg = zoomedImgWrapper.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n const zoomLens = container.appendChild(document.createElement(\"div\"))\n zoomLens.style.display = \"none\"\n\n // Sometimes, source image element's width and height are not available until the image is loaded\n // and container size is set. These values are expected to be same as the container's width and height\n let sourceImageElementWidth = 0\n let sourceImageElementHeight = 0\n\n const finalOptions: Omit<Required<ZoomImageHoverOptions>, \"zoomImageProps\"> = {\n zoomImageSource: options.zoomImageSource || sourceImgElement.src,\n zoomLensClass: options.zoomLensClass || \"\",\n zoomTargetClass: options.zoomTargetClass || \"\",\n customZoom: options.customZoom,\n scale: options.scale || 2,\n zoomTarget: options.zoomTarget,\n zoomLensScale: options.zoomLensScale || 1,\n disableScrollLock: options.disableScrollLock || false,\n }\n\n const {\n scale,\n zoomImageSource,\n customZoom,\n zoomLensClass,\n zoomTarget,\n zoomLensScale,\n zoomTargetClass,\n disableScrollLock,\n } = finalOptions\n\n const store = createStore<ZoomImageHoverState>({\n zoomedImgStatus: \"idle\",\n enabled: true,\n })\n\n let offset: { left: number; top: number } = getOffset(sourceImgElement)\n\n function getOffset(element: HTMLElement) {\n const elRect = element.getBoundingClientRect()\n return { left: elRect.left, top: elRect.top }\n }\n\n function getLimitX(value: number) {\n return sourceImageElementWidth - value\n }\n\n function getLimitY(value: number) {\n return sourceImageElementHeight - value\n }\n\n function zoomLensLeft(left: number) {\n const minX = zoomLens.clientWidth / 2\n return clamp(left, minX, getLimitX(minX)) - minX\n }\n\n function zoomLensTop(top: number) {\n const minY = zoomLens.clientHeight / 2\n return clamp(top, minY, getLimitY(minY)) - minY\n }\n\n function processZoom(event: PointerEvent) {\n let offsetX: number\n let offsetY: number\n let backgroundX: number\n let backgroundY: number\n if (offset) {\n offsetX = zoomLensLeft(event.clientX - offset.left)\n offsetY = zoomLensTop(event.clientY - offset.top)\n backgroundX = (offsetX * scale) / zoomLensScale\n backgroundY = (offsetY * scale) / zoomLensScale\n zoomedImg.style.transform = \"translate(\" + -backgroundX + \"px,\" + -backgroundY + \"px)\"\n zoomLens.style.cssText += \"transform:\" + \"translate(\" + offsetX + \"px,\" + offsetY + \"px);\"\n }\n }\n\n async function handlePointerEnter() {\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n zoomedImg.style.display = \"block\"\n zoomLens.style.display = \"block\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.add(className))\n }\n\n if (!disableScrollLock) disableScroll()\n }\n\n function handlePointerLeave() {\n zoomedImg.style.display = \"none\"\n zoomLens.style.display = \"none\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.remove(className))\n }\n\n if (!disableScrollLock) enableScroll()\n }\n\n function handleScroll() {\n offset = getOffset(sourceImgElement)\n }\n\n async function setup() {\n if (zoomLensClass) {\n zoomLens.className = zoomLensClass\n } else {\n zoomLens.style.background = \"rgba(238, 130, 238, 0.5)\"\n }\n\n // setup event listeners\n container.addEventListener(\"pointerdown\", processZoom, { signal })\n container.addEventListener(\"pointermove\", processZoom, { signal })\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n window.addEventListener(\"scroll\", handleScroll, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n // Append zoomed image wrapper to zoom target\n zoomTarget.appendChild(zoomedImgWrapper)\n\n // Wait for next tick to get container size\n await new Promise((resolve) => setTimeout(resolve, 1))\n const containerRect = container.getBoundingClientRect()\n sourceImageElementWidth = containerRect.width\n sourceImageElementHeight = containerRect.height\n\n // Set up styles if custom zoom available\n if (customZoom) {\n zoomedImgWrapper.style.width = customZoom.width + \"px\"\n zoomedImgWrapper.style.height = customZoom.height + \"px\"\n } else {\n // Else default zoom to source image size\n zoomedImgWrapper.style.width = sourceImageElementWidth + \"px\"\n zoomedImgWrapper.style.height = sourceImageElementHeight + \"px\"\n }\n\n zoomedImg.width = (sourceImageElementWidth * scale) / zoomLensScale\n zoomedImg.height = (sourceImageElementHeight * scale) / zoomLensScale\n\n // Setup default zoom lens style\n const sourceImageRect = sourceImgElement.getBoundingClientRect()\n const fromLeft = sourceImageRect.left - containerRect.left\n const fromTop = sourceImageRect.top - containerRect.top\n zoomTarget.style.pointerEvents = \"none\"\n zoomLens.style.position = \"absolute\"\n zoomLens.style.left = fromLeft + \"px\"\n zoomLens.style.top = fromTop + \"px\"\n zoomLens.style.width = (customZoom.width / scale) * zoomLensScale + \"px\"\n zoomLens.style.height = (customZoom.height / scale) * zoomLensScale + \"px\"\n }\n\n setup()\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomLens) && container.removeChild(zoomLens)\n\n if (zoomTarget && zoomTarget.contains(zoomedImgWrapper)) {\n zoomTarget.removeChild(zoomedImgWrapper)\n return\n }\n\n container.contains(zoomedImgWrapper) && container.removeChild(zoomedImgWrapper)\n },\n subscribe: store.subscribe,\n getState: store.getState,\n setState: (newState: ZoomImageHoverStateUpdate) => {\n store.setState(newState)\n },\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageMoveOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n // @deprecated\n disableScrollLock?: boolean\n disabledContextMenu?: boolean\n zoomImageProps?: {\n alt?: string\n className?: string\n }\n}\n\nexport type ZoomImageMoveState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageMove(container: HTMLElement, options: ZoomImageMoveOptions = {}) {\n let activePointerId: number | null = null\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageMoveOptions>, \"zoomImageProps\" | \"disableScrollLock\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disabledContextMenu: options.disabledContextMenu ?? false,\n }\n\n const { disabledContextMenu, zoomFactor, zoomImageSource } = finalOptions\n\n const store = createStore<ZoomImageMoveState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImg = document.createElement(\"img\")\n options.zoomImageProps?.alt && (zoomedImg.alt = options.zoomImageProps.alt)\n options.zoomImageProps?.className && (zoomedImg.className = options.zoomImageProps.className)\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n // Credit to https://stackoverflow.com/questions/19587555/disable-default-save-image-option-in-mobile/19590365#19590365\n if (disabledContextMenu) {\n zoomedImg.style[\"-webkit-user-select\"] = \"none\"\n zoomedImg.style[\"-webkit-touch-callout\"] = \"none\"\n zoomedImg.oncontextmenu = () => false\n }\n\n const checkValidPointer = (event: PointerEvent) => {\n return activePointerId && event.pointerId === activePointerId\n }\n\n function handlePointerEnter(event: PointerEvent) {\n if (activePointerId === null) {\n activePointerId = event.pointerId\n container.appendChild(zoomedImg)\n zoomedImg.style.display = \"block\"\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n processZoom(event)\n\n event.pointerType !== \"mouse\" && disableScroll()\n }\n }\n\n function handlePointerMove(event: PointerEvent) {\n if (checkValidPointer(event)) {\n processZoom(event)\n }\n }\n\n function resetZoomedImg(event: PointerEvent) {\n if (checkValidPointer(event)) {\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n zoomedImg.style.display = \"none\"\n zoomedImg.style.transform = \"none\"\n enableScroll()\n activePointerId = null\n }\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerleave\", resetZoomedImg, { signal })\n container.addEventListener(\n \"touchstart\",\n (event) => {\n disabledContextMenu && event.preventDefault()\n },\n { signal },\n )\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage, noop } from \"./utils\"\n\nexport type ZoomImageClickOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageClickState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageClick(container: HTMLElement, options: ZoomImageClickOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageClickOptions>, \"zoomImageProps\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disableScrollLock: options.disableScrollLock ?? false,\n }\n\n const { zoomFactor, zoomImageSource, disableScrollLock } = finalOptions\n\n let isOnMove = false\n\n const store = createStore<ZoomImageClickState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n const zoomedImg = container.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n function handlePointerMove(event: PointerEvent) {\n if (!isOnMove) {\n return\n }\n\n processZoom(event)\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n function handlePointerDown(event: PointerEvent) {\n if (isOnMove) {\n isOnMove = false\n zoomedImg.style.display = \"none\"\n return\n }\n\n processZoom(event)\n isOnMove = true\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerenter\", disableScrollLock ? noop : disableScroll, { signal })\n container.addEventListener(\"pointerleave\", disableScrollLock ? noop : enableScroll, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculatePercentage = (maxZoom: number) =>\n scaleLinear({\n domainStart: 1,\n domainStop: maxZoom,\n rangeStart: 0,\n rangeStop: 100,\n })\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculateZoom = (maxZoom: number) =>\n scaleLinear({\n domainStart: 0,\n domainStop: 100,\n rangeStart: 1,\n rangeStop: maxZoom,\n })\n","type CropImageArg = {\n currentZoom: number\n image: HTMLImageElement\n positionX: number\n positionY: number\n rotation?: number\n}\n\nexport const cropImage = async ({ image, positionX, positionY, currentZoom, rotation = 0 }: CropImageArg) => {\n const canvas = document.createElement(\"canvas\")\n const scale = image.naturalWidth / (image.clientWidth * currentZoom)\n const normalizedRotation = rotation % 360\n const croppedImageWidth = image.clientWidth * scale\n const croppedImageHeight = image.clientHeight * scale\n canvas.width = croppedImageWidth\n canvas.height = croppedImageHeight\n const canvasContext = canvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n const sx = Math.max(0, Math.abs(positionX) * scale)\n const sy = Math.max(0, Math.abs(positionY) * scale)\n\n canvasContext.drawImage(\n image,\n sx,\n sy,\n croppedImageWidth,\n croppedImageHeight,\n 0,\n 0,\n croppedImageWidth,\n croppedImageHeight,\n )\n\n const originalImage = new Image()\n originalImage.src = canvas.toDataURL()\n\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n const rotatedCanvas = document.createElement(\"canvas\") as HTMLCanvasElement\n const rotatedCanvasContext = rotatedCanvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvas.width = originalImage.naturalHeight\n rotatedCanvas.height = originalImage.naturalWidth\n } else {\n rotatedCanvas.width = originalImage.naturalWidth\n rotatedCanvas.height = originalImage.naturalHeight\n }\n\n rotatedCanvasContext.clearRect(0, 0, canvas.width, canvas.height)\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvasContext.translate(originalImage.height / 2, originalImage.width / 2)\n } else {\n rotatedCanvasContext.translate(originalImage.width / 2, originalImage.height / 2)\n }\n rotatedCanvasContext.rotate((normalizedRotation * Math.PI) / 180)\n rotatedCanvasContext.drawImage(originalImage, -originalImage.width / 2, -originalImage.height / 2)\n\n return rotatedCanvas.toDataURL()\n}\n"]}
{"version":3,"sources":["../../../node_modules/.pnpm/@namnode+store@0.1.0/node_modules/@namnode/store/dist/chunk-TZNK2OF3.mjs","../src/imageLoader.ts","../src/utils.ts","../src/createZoomImageHover.ts","../src/createZoomImageMove.ts","../src/createZoomImageWheel.ts","../src/createZoomImageClick.ts","../src/makeCalculatePercentage.ts","../src/makeCalculateZoom.ts","../src/cropImage.ts"],"names":["controller","signal"],"mappings":"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","sourcesContent":["function f(o){let r=new Set,s=!1,a=o,e,c=(t={})=>{e={...e,...t},i();},i=()=>{if(s)return;let t=!1;if(e){for(let n in e)if(a[n]!==e[n]){t=!0;break}}t&&(a={...a,...e},r.forEach(n=>n({state:a,updatedProperties:e})),e=void 0);};return {subscribe:t=>(r.add(t),()=>{r.delete(t);}),cleanup:()=>r.clear(),getState:()=>a,setState:c,batch:t=>{s=!0,t(),s=!1,i();}}}\n\nexport { f as a };\n","import { createStore } from \"@namnode/store\"\nimport { ZoomedImgStatus } from \"./types\"\n\n// There is a scenario where the image is already in the cache,\n// we don't want to trigger the loading state too quickly in that case\n// 50ms should be enough to wait before triggering the loading state\nconst THRESHOLD = 50\n\nexport const makeImageLoader = () => {\n const createZoomImage = (\n img: HTMLImageElement,\n src: string,\n store: ReturnType<\n typeof createStore<{\n zoomedImgStatus: ZoomedImgStatus\n }>\n >,\n ) => {\n if (img.src === src) return\n img.src = src\n let complete = false\n\n img.onload = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"loaded\" })\n }\n\n img.onerror = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"error\" })\n }\n\n setTimeout(() => {\n if (!complete) store.setState({ zoomedImgStatus: \"loading\" })\n }, THRESHOLD)\n }\n\n return {\n createZoomImage,\n }\n}\n\nexport const imageLoader = makeImageLoader()\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { clamp, disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageHoverOptions = {\n customZoom: { width: number; height: number }\n zoomImageSource?: string\n zoomLensClass?: string\n zoomLensScale?: number\n zoomTarget: HTMLElement\n zoomTargetClass?: string\n scale?: number\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageHoverState = {\n zoomedImgStatus: ZoomedImgStatus\n enabled: boolean\n}\n\nexport type ZoomImageHoverStateUpdate = { enabled: boolean }\n\nexport function createZoomImageHover(container: HTMLElement, options: ZoomImageHoverOptions) {\n const controller = new AbortController()\n const { signal } = controller\n const sourceImgElement = getSourceImage(container)\n const zoomedImgWrapper = document.createElement(\"div\")\n zoomedImgWrapper.style.overflow = \"hidden\"\n const zoomedImg = zoomedImgWrapper.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n const zoomLens = container.appendChild(document.createElement(\"div\"))\n zoomLens.style.display = \"none\"\n\n // Sometimes, source image element's width and height are not available until the image is loaded\n // and container size is set. These values are expected to be same as the container's width and height\n let sourceImageElementWidth = 0\n let sourceImageElementHeight = 0\n\n const finalOptions: Omit<Required<ZoomImageHoverOptions>, \"zoomImageProps\"> = {\n zoomImageSource: options.zoomImageSource || sourceImgElement.src,\n zoomLensClass: options.zoomLensClass || \"\",\n zoomTargetClass: options.zoomTargetClass || \"\",\n customZoom: options.customZoom,\n scale: options.scale || 2,\n zoomTarget: options.zoomTarget,\n zoomLensScale: options.zoomLensScale || 1,\n disableScrollLock: options.disableScrollLock || false,\n }\n\n const {\n scale,\n zoomImageSource,\n customZoom,\n zoomLensClass,\n zoomTarget,\n zoomLensScale,\n zoomTargetClass,\n disableScrollLock,\n } = finalOptions\n\n const store = createStore<ZoomImageHoverState>({\n zoomedImgStatus: \"idle\",\n enabled: true,\n })\n\n let offset: { left: number; top: number } = getOffset(sourceImgElement)\n\n function getOffset(element: HTMLElement) {\n const elRect = element.getBoundingClientRect()\n return { left: elRect.left, top: elRect.top }\n }\n\n function getLimitX(value: number) {\n return sourceImageElementWidth - value\n }\n\n function getLimitY(value: number) {\n return sourceImageElementHeight - value\n }\n\n function zoomLensLeft(left: number) {\n const minX = zoomLens.clientWidth / 2\n return clamp(left, minX, getLimitX(minX)) - minX\n }\n\n function zoomLensTop(top: number) {\n const minY = zoomLens.clientHeight / 2\n return clamp(top, minY, getLimitY(minY)) - minY\n }\n\n function processZoom(event: PointerEvent) {\n let offsetX: number\n let offsetY: number\n let backgroundX: number\n let backgroundY: number\n if (offset) {\n offsetX = zoomLensLeft(event.clientX - offset.left)\n offsetY = zoomLensTop(event.clientY - offset.top)\n backgroundX = (offsetX * scale) / zoomLensScale\n backgroundY = (offsetY * scale) / zoomLensScale\n zoomedImg.style.transform = \"translate(\" + -backgroundX + \"px,\" + -backgroundY + \"px)\"\n zoomLens.style.cssText += \"transform:\" + \"translate(\" + offsetX + \"px,\" + offsetY + \"px);\"\n }\n }\n\n async function handlePointerEnter() {\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n zoomedImg.style.display = \"block\"\n zoomLens.style.display = \"block\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.add(className))\n }\n\n if (!disableScrollLock) disableScroll()\n }\n\n function handlePointerLeave() {\n zoomedImg.style.display = \"none\"\n zoomLens.style.display = \"none\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.remove(className))\n }\n\n if (!disableScrollLock) enableScroll()\n }\n\n function handleScroll() {\n offset = getOffset(sourceImgElement)\n }\n\n async function setup() {\n if (zoomLensClass) {\n zoomLens.className = zoomLensClass\n } else {\n zoomLens.style.background = \"rgba(238, 130, 238, 0.5)\"\n }\n\n // setup event listeners\n container.addEventListener(\"pointerdown\", processZoom, { signal })\n container.addEventListener(\"pointermove\", processZoom, { signal })\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n window.addEventListener(\"scroll\", handleScroll, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n // Append zoomed image wrapper to zoom target\n zoomTarget.appendChild(zoomedImgWrapper)\n\n // Wait for next tick to get container size\n await new Promise((resolve) => setTimeout(resolve, 1))\n const containerRect = container.getBoundingClientRect()\n sourceImageElementWidth = containerRect.width\n sourceImageElementHeight = containerRect.height\n\n // Set up styles if custom zoom available\n if (customZoom) {\n zoomedImgWrapper.style.width = customZoom.width + \"px\"\n zoomedImgWrapper.style.height = customZoom.height + \"px\"\n } else {\n // Else default zoom to source image size\n zoomedImgWrapper.style.width = sourceImageElementWidth + \"px\"\n zoomedImgWrapper.style.height = sourceImageElementHeight + \"px\"\n }\n\n zoomedImg.width = (sourceImageElementWidth * scale) / zoomLensScale\n zoomedImg.height = (sourceImageElementHeight * scale) / zoomLensScale\n\n // Setup default zoom lens style\n const sourceImageRect = sourceImgElement.getBoundingClientRect()\n const fromLeft = sourceImageRect.left - containerRect.left\n const fromTop = sourceImageRect.top - containerRect.top\n zoomTarget.style.pointerEvents = \"none\"\n zoomLens.style.position = \"absolute\"\n zoomLens.style.left = fromLeft + \"px\"\n zoomLens.style.top = fromTop + \"px\"\n zoomLens.style.width = (customZoom.width / scale) * zoomLensScale + \"px\"\n zoomLens.style.height = (customZoom.height / scale) * zoomLensScale + \"px\"\n }\n\n setup()\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomLens) && container.removeChild(zoomLens)\n\n if (zoomTarget && zoomTarget.contains(zoomedImgWrapper)) {\n zoomTarget.removeChild(zoomedImgWrapper)\n return\n }\n\n container.contains(zoomedImgWrapper) && container.removeChild(zoomedImgWrapper)\n },\n subscribe: store.subscribe,\n getState: store.getState,\n setState: (newState: ZoomImageHoverStateUpdate) => {\n store.setState(newState)\n },\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageMoveOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n // @deprecated\n disableScrollLock?: boolean\n disabledContextMenu?: boolean\n zoomImageProps?: {\n alt?: string\n className?: string\n }\n}\n\nexport type ZoomImageMoveState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageMove(container: HTMLElement, options: ZoomImageMoveOptions = {}) {\n let activePointerId: number | null = null\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageMoveOptions>, \"zoomImageProps\" | \"disableScrollLock\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disabledContextMenu: options.disabledContextMenu ?? false,\n }\n\n const { disabledContextMenu, zoomFactor, zoomImageSource } = finalOptions\n\n const store = createStore<ZoomImageMoveState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImg = document.createElement(\"img\")\n options.zoomImageProps?.alt && (zoomedImg.alt = options.zoomImageProps.alt)\n options.zoomImageProps?.className && (zoomedImg.className = options.zoomImageProps.className)\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n // Credit to https://stackoverflow.com/questions/19587555/disable-default-save-image-option-in-mobile/19590365#19590365\n if (disabledContextMenu) {\n zoomedImg.style[\"-webkit-user-select\"] = \"none\"\n zoomedImg.style[\"-webkit-touch-callout\"] = \"none\"\n zoomedImg.oncontextmenu = () => false\n }\n\n const checkValidPointer = (event: PointerEvent) => {\n return activePointerId && event.pointerId === activePointerId\n }\n\n function handlePointerEnter(event: PointerEvent) {\n if (activePointerId === null) {\n activePointerId = event.pointerId\n container.appendChild(zoomedImg)\n zoomedImg.style.display = \"block\"\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n processZoom(event)\n\n event.pointerType !== \"mouse\" && disableScroll()\n }\n }\n\n function handlePointerMove(event: PointerEvent) {\n if (checkValidPointer(event)) {\n processZoom(event)\n }\n }\n\n function resetZoomedImg(event: PointerEvent) {\n if (checkValidPointer(event)) {\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n zoomedImg.style.display = \"none\"\n zoomedImg.style.transform = \"none\"\n enableScroll()\n activePointerId = null\n }\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerleave\", resetZoomedImg, { signal })\n container.addEventListener(\n \"touchstart\",\n (event) => {\n disabledContextMenu && event.preventDefault()\n },\n { signal },\n )\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage, noop } from \"./utils\"\n\nexport type ZoomImageClickOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageClickState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageClick(container: HTMLElement, options: ZoomImageClickOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageClickOptions>, \"zoomImageProps\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disableScrollLock: options.disableScrollLock ?? false,\n }\n\n const { zoomFactor, zoomImageSource, disableScrollLock } = finalOptions\n\n let isOnMove = false\n\n const store = createStore<ZoomImageClickState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n const zoomedImg = container.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n function handlePointerMove(event: PointerEvent) {\n if (!isOnMove) {\n return\n }\n\n processZoom(event)\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n function handlePointerDown(event: PointerEvent) {\n if (isOnMove) {\n isOnMove = false\n zoomedImg.style.display = \"none\"\n return\n }\n\n processZoom(event)\n isOnMove = true\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerenter\", disableScrollLock ? noop : disableScroll, { signal })\n container.addEventListener(\"pointerleave\", disableScrollLock ? noop : enableScroll, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculatePercentage = (maxZoom: number) =>\n scaleLinear({\n domainStart: 1,\n domainStop: maxZoom,\n rangeStart: 0,\n rangeStop: 100,\n })\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculateZoom = (maxZoom: number) =>\n scaleLinear({\n domainStart: 0,\n domainStop: 100,\n rangeStart: 1,\n rangeStop: maxZoom,\n })\n","type CropImageArg = {\n currentZoom: number\n image: HTMLImageElement\n positionX: number\n positionY: number\n rotation?: number\n}\n\nexport const cropImage = async ({ image, positionX, positionY, currentZoom, rotation = 0 }: CropImageArg) => {\n const canvas = document.createElement(\"canvas\")\n const scale = image.naturalWidth / (image.clientWidth * currentZoom)\n const normalizedRotation = rotation % 360\n const croppedImageWidth = image.clientWidth * scale\n const croppedImageHeight = image.clientHeight * scale\n canvas.width = croppedImageWidth\n canvas.height = croppedImageHeight\n const canvasContext = canvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n const sx = Math.max(0, Math.abs(positionX) * scale)\n const sy = Math.max(0, Math.abs(positionY) * scale)\n\n canvasContext.drawImage(\n image,\n sx,\n sy,\n croppedImageWidth,\n croppedImageHeight,\n 0,\n 0,\n croppedImageWidth,\n croppedImageHeight,\n )\n\n const originalImage = new Image()\n originalImage.src = canvas.toDataURL()\n\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n const rotatedCanvas = document.createElement(\"canvas\") as HTMLCanvasElement\n const rotatedCanvasContext = rotatedCanvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvas.width = originalImage.naturalHeight\n rotatedCanvas.height = originalImage.naturalWidth\n } else {\n rotatedCanvas.width = originalImage.naturalWidth\n rotatedCanvas.height = originalImage.naturalHeight\n }\n\n rotatedCanvasContext.clearRect(0, 0, canvas.width, canvas.height)\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvasContext.translate(originalImage.height / 2, originalImage.width / 2)\n } else {\n rotatedCanvasContext.translate(originalImage.width / 2, originalImage.height / 2)\n }\n rotatedCanvasContext.rotate((normalizedRotation * Math.PI) / 180)\n rotatedCanvasContext.drawImage(originalImage, -originalImage.width / 2, -originalImage.height / 2)\n\n return rotatedCanvas.toDataURL()\n}\n"]}

@@ -666,3 +666,2 @@ 'use strict';

function _handlePointerUp(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);

@@ -669,0 +668,0 @@ if (pointerMap.size < 2) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/createZoomImageHover.ts","../src/imageLoader.ts","../src/utils.ts","../src/createZoomImageMove.ts","../src/createZoomImageWheel.ts","../src/createZoomImageClick.ts","../src/makeCalculatePercentage.ts","../src/makeCalculateZoom.ts","../src/cropImage.ts"],"names":["controller","signal","createStore"],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { clamp, disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageHoverOptions = {\n customZoom: { width: number; height: number }\n zoomImageSource?: string\n zoomLensClass?: string\n zoomLensScale?: number\n zoomTarget: HTMLElement\n zoomTargetClass?: string\n scale?: number\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageHoverState = {\n zoomedImgStatus: ZoomedImgStatus\n enabled: boolean\n}\n\nexport type ZoomImageHoverStateUpdate = { enabled: boolean }\n\nexport function createZoomImageHover(container: HTMLElement, options: ZoomImageHoverOptions) {\n const controller = new AbortController()\n const { signal } = controller\n const sourceImgElement = getSourceImage(container)\n const zoomedImgWrapper = document.createElement(\"div\")\n zoomedImgWrapper.style.overflow = \"hidden\"\n const zoomedImg = zoomedImgWrapper.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n const zoomLens = container.appendChild(document.createElement(\"div\"))\n zoomLens.style.display = \"none\"\n\n // Sometimes, source image element's width and height are not available until the image is loaded\n // and container size is set. These values are expected to be same as the container's width and height\n let sourceImageElementWidth = 0\n let sourceImageElementHeight = 0\n\n const finalOptions: Omit<Required<ZoomImageHoverOptions>, \"zoomImageProps\"> = {\n zoomImageSource: options.zoomImageSource || sourceImgElement.src,\n zoomLensClass: options.zoomLensClass || \"\",\n zoomTargetClass: options.zoomTargetClass || \"\",\n customZoom: options.customZoom,\n scale: options.scale || 2,\n zoomTarget: options.zoomTarget,\n zoomLensScale: options.zoomLensScale || 1,\n disableScrollLock: options.disableScrollLock || false,\n }\n\n const {\n scale,\n zoomImageSource,\n customZoom,\n zoomLensClass,\n zoomTarget,\n zoomLensScale,\n zoomTargetClass,\n disableScrollLock,\n } = finalOptions\n\n const store = createStore<ZoomImageHoverState>({\n zoomedImgStatus: \"idle\",\n enabled: true,\n })\n\n let offset: { left: number; top: number } = getOffset(sourceImgElement)\n\n function getOffset(element: HTMLElement) {\n const elRect = element.getBoundingClientRect()\n return { left: elRect.left, top: elRect.top }\n }\n\n function getLimitX(value: number) {\n return sourceImageElementWidth - value\n }\n\n function getLimitY(value: number) {\n return sourceImageElementHeight - value\n }\n\n function zoomLensLeft(left: number) {\n const minX = zoomLens.clientWidth / 2\n return clamp(left, minX, getLimitX(minX)) - minX\n }\n\n function zoomLensTop(top: number) {\n const minY = zoomLens.clientHeight / 2\n return clamp(top, minY, getLimitY(minY)) - minY\n }\n\n function processZoom(event: PointerEvent) {\n let offsetX: number\n let offsetY: number\n let backgroundX: number\n let backgroundY: number\n if (offset) {\n offsetX = zoomLensLeft(event.clientX - offset.left)\n offsetY = zoomLensTop(event.clientY - offset.top)\n backgroundX = (offsetX * scale) / zoomLensScale\n backgroundY = (offsetY * scale) / zoomLensScale\n zoomedImg.style.transform = \"translate(\" + -backgroundX + \"px,\" + -backgroundY + \"px)\"\n zoomLens.style.cssText += \"transform:\" + \"translate(\" + offsetX + \"px,\" + offsetY + \"px);\"\n }\n }\n\n async function handlePointerEnter() {\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n zoomedImg.style.display = \"block\"\n zoomLens.style.display = \"block\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.add(className))\n }\n\n if (!disableScrollLock) disableScroll()\n }\n\n function handlePointerLeave() {\n zoomedImg.style.display = \"none\"\n zoomLens.style.display = \"none\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.remove(className))\n }\n\n if (!disableScrollLock) enableScroll()\n }\n\n function handleScroll() {\n offset = getOffset(sourceImgElement)\n }\n\n async function setup() {\n if (zoomLensClass) {\n zoomLens.className = zoomLensClass\n } else {\n zoomLens.style.background = \"rgba(238, 130, 238, 0.5)\"\n }\n\n // setup event listeners\n container.addEventListener(\"pointerdown\", processZoom, { signal })\n container.addEventListener(\"pointermove\", processZoom, { signal })\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n window.addEventListener(\"scroll\", handleScroll, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n // Append zoomed image wrapper to zoom target\n zoomTarget.appendChild(zoomedImgWrapper)\n\n // Wait for next tick to get container size\n await new Promise((resolve) => setTimeout(resolve, 1))\n const containerRect = container.getBoundingClientRect()\n sourceImageElementWidth = containerRect.width\n sourceImageElementHeight = containerRect.height\n\n // Set up styles if custom zoom available\n if (customZoom) {\n zoomedImgWrapper.style.width = customZoom.width + \"px\"\n zoomedImgWrapper.style.height = customZoom.height + \"px\"\n } else {\n // Else default zoom to source image size\n zoomedImgWrapper.style.width = sourceImageElementWidth + \"px\"\n zoomedImgWrapper.style.height = sourceImageElementHeight + \"px\"\n }\n\n zoomedImg.width = (sourceImageElementWidth * scale) / zoomLensScale\n zoomedImg.height = (sourceImageElementHeight * scale) / zoomLensScale\n\n // Setup default zoom lens style\n const sourceImageRect = sourceImgElement.getBoundingClientRect()\n const fromLeft = sourceImageRect.left - containerRect.left\n const fromTop = sourceImageRect.top - containerRect.top\n zoomTarget.style.pointerEvents = \"none\"\n zoomLens.style.position = \"absolute\"\n zoomLens.style.left = fromLeft + \"px\"\n zoomLens.style.top = fromTop + \"px\"\n zoomLens.style.width = (customZoom.width / scale) * zoomLensScale + \"px\"\n zoomLens.style.height = (customZoom.height / scale) * zoomLensScale + \"px\"\n }\n\n setup()\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomLens) && container.removeChild(zoomLens)\n\n if (zoomTarget && zoomTarget.contains(zoomedImgWrapper)) {\n zoomTarget.removeChild(zoomedImgWrapper)\n return\n }\n\n container.contains(zoomedImgWrapper) && container.removeChild(zoomedImgWrapper)\n },\n subscribe: store.subscribe,\n getState: store.getState,\n setState: (newState: ZoomImageHoverStateUpdate) => {\n store.setState(newState)\n },\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { ZoomedImgStatus } from \"./types\"\n\n// There is a scenario where the image is already in the cache,\n// we don't want to trigger the loading state too quickly in that case\n// 50ms should be enough to wait before triggering the loading state\nconst THRESHOLD = 50\n\nexport const makeImageLoader = () => {\n const createZoomImage = (\n img: HTMLImageElement,\n src: string,\n store: ReturnType<\n typeof createStore<{\n zoomedImgStatus: ZoomedImgStatus\n }>\n >,\n ) => {\n if (img.src === src) return\n img.src = src\n let complete = false\n\n img.onload = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"loaded\" })\n }\n\n img.onerror = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"error\" })\n }\n\n setTimeout(() => {\n if (!complete) store.setState({ zoomedImgStatus: \"loading\" })\n }, THRESHOLD)\n }\n\n return {\n createZoomImage,\n }\n}\n\nexport const imageLoader = makeImageLoader()\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageMoveOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n // @deprecated\n disableScrollLock?: boolean\n disabledContextMenu?: boolean\n zoomImageProps?: {\n alt?: string\n className?: string\n }\n}\n\nexport type ZoomImageMoveState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageMove(container: HTMLElement, options: ZoomImageMoveOptions = {}) {\n let activePointerId: number | null = null\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageMoveOptions>, \"zoomImageProps\" | \"disableScrollLock\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disabledContextMenu: options.disabledContextMenu ?? false,\n }\n\n const { disabledContextMenu, zoomFactor, zoomImageSource } = finalOptions\n\n const store = createStore<ZoomImageMoveState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImg = document.createElement(\"img\")\n options.zoomImageProps?.alt && (zoomedImg.alt = options.zoomImageProps.alt)\n options.zoomImageProps?.className && (zoomedImg.className = options.zoomImageProps.className)\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n // Credit to https://stackoverflow.com/questions/19587555/disable-default-save-image-option-in-mobile/19590365#19590365\n if (disabledContextMenu) {\n zoomedImg.style[\"-webkit-user-select\"] = \"none\"\n zoomedImg.style[\"-webkit-touch-callout\"] = \"none\"\n zoomedImg.oncontextmenu = () => false\n }\n\n const checkValidPointer = (event: PointerEvent) => {\n return activePointerId && event.pointerId === activePointerId\n }\n\n function handlePointerEnter(event: PointerEvent) {\n if (activePointerId === null) {\n activePointerId = event.pointerId\n container.appendChild(zoomedImg)\n zoomedImg.style.display = \"block\"\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n processZoom(event)\n\n event.pointerType !== \"mouse\" && disableScroll()\n }\n }\n\n function handlePointerMove(event: PointerEvent) {\n if (checkValidPointer(event)) {\n processZoom(event)\n }\n }\n\n function resetZoomedImg(event: PointerEvent) {\n if (checkValidPointer(event)) {\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n zoomedImg.style.display = \"none\"\n zoomedImg.style.transform = \"none\"\n enableScroll()\n activePointerId = null\n }\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerleave\", resetZoomedImg, { signal })\n container.addEventListener(\n \"touchstart\",\n (event) => {\n disabledContextMenu && event.preventDefault()\n },\n { signal },\n )\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage, noop } from \"./utils\"\n\nexport type ZoomImageClickOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageClickState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageClick(container: HTMLElement, options: ZoomImageClickOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageClickOptions>, \"zoomImageProps\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disableScrollLock: options.disableScrollLock ?? false,\n }\n\n const { zoomFactor, zoomImageSource, disableScrollLock } = finalOptions\n\n let isOnMove = false\n\n const store = createStore<ZoomImageClickState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n const zoomedImg = container.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n function handlePointerMove(event: PointerEvent) {\n if (!isOnMove) {\n return\n }\n\n processZoom(event)\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n function handlePointerDown(event: PointerEvent) {\n if (isOnMove) {\n isOnMove = false\n zoomedImg.style.display = \"none\"\n return\n }\n\n processZoom(event)\n isOnMove = true\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerenter\", disableScrollLock ? noop : disableScroll, { signal })\n container.addEventListener(\"pointerleave\", disableScrollLock ? noop : enableScroll, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculatePercentage = (maxZoom: number) =>\n scaleLinear({\n domainStart: 1,\n domainStop: maxZoom,\n rangeStart: 0,\n rangeStop: 100,\n })\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculateZoom = (maxZoom: number) =>\n scaleLinear({\n domainStart: 0,\n domainStop: 100,\n rangeStart: 1,\n rangeStop: maxZoom,\n })\n","type CropImageArg = {\n currentZoom: number\n image: HTMLImageElement\n positionX: number\n positionY: number\n rotation?: number\n}\n\nexport const cropImage = async ({ image, positionX, positionY, currentZoom, rotation = 0 }: CropImageArg) => {\n const canvas = document.createElement(\"canvas\")\n const scale = image.naturalWidth / (image.clientWidth * currentZoom)\n const normalizedRotation = rotation % 360\n const croppedImageWidth = image.clientWidth * scale\n const croppedImageHeight = image.clientHeight * scale\n canvas.width = croppedImageWidth\n canvas.height = croppedImageHeight\n const canvasContext = canvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n const sx = Math.max(0, Math.abs(positionX) * scale)\n const sy = Math.max(0, Math.abs(positionY) * scale)\n\n canvasContext.drawImage(\n image,\n sx,\n sy,\n croppedImageWidth,\n croppedImageHeight,\n 0,\n 0,\n croppedImageWidth,\n croppedImageHeight,\n )\n\n const originalImage = new Image()\n originalImage.src = canvas.toDataURL()\n\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n const rotatedCanvas = document.createElement(\"canvas\") as HTMLCanvasElement\n const rotatedCanvasContext = rotatedCanvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvas.width = originalImage.naturalHeight\n rotatedCanvas.height = originalImage.naturalWidth\n } else {\n rotatedCanvas.width = originalImage.naturalWidth\n rotatedCanvas.height = originalImage.naturalHeight\n }\n\n rotatedCanvasContext.clearRect(0, 0, canvas.width, canvas.height)\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvasContext.translate(originalImage.height / 2, originalImage.width / 2)\n } else {\n rotatedCanvasContext.translate(originalImage.width / 2, originalImage.height / 2)\n }\n rotatedCanvasContext.rotate((normalizedRotation * Math.PI) / 180)\n rotatedCanvasContext.drawImage(originalImage, -originalImage.width / 2, -originalImage.height / 2)\n\n return rotatedCanvas.toDataURL()\n}\n"]}
{"version":3,"sources":["../src/createZoomImageHover.ts","../src/imageLoader.ts","../src/utils.ts","../src/createZoomImageMove.ts","../src/createZoomImageWheel.ts","../src/createZoomImageClick.ts","../src/makeCalculatePercentage.ts","../src/makeCalculateZoom.ts","../src/cropImage.ts"],"names":["controller","signal","createStore"],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { clamp, disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageHoverOptions = {\n customZoom: { width: number; height: number }\n zoomImageSource?: string\n zoomLensClass?: string\n zoomLensScale?: number\n zoomTarget: HTMLElement\n zoomTargetClass?: string\n scale?: number\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageHoverState = {\n zoomedImgStatus: ZoomedImgStatus\n enabled: boolean\n}\n\nexport type ZoomImageHoverStateUpdate = { enabled: boolean }\n\nexport function createZoomImageHover(container: HTMLElement, options: ZoomImageHoverOptions) {\n const controller = new AbortController()\n const { signal } = controller\n const sourceImgElement = getSourceImage(container)\n const zoomedImgWrapper = document.createElement(\"div\")\n zoomedImgWrapper.style.overflow = \"hidden\"\n const zoomedImg = zoomedImgWrapper.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n const zoomLens = container.appendChild(document.createElement(\"div\"))\n zoomLens.style.display = \"none\"\n\n // Sometimes, source image element's width and height are not available until the image is loaded\n // and container size is set. These values are expected to be same as the container's width and height\n let sourceImageElementWidth = 0\n let sourceImageElementHeight = 0\n\n const finalOptions: Omit<Required<ZoomImageHoverOptions>, \"zoomImageProps\"> = {\n zoomImageSource: options.zoomImageSource || sourceImgElement.src,\n zoomLensClass: options.zoomLensClass || \"\",\n zoomTargetClass: options.zoomTargetClass || \"\",\n customZoom: options.customZoom,\n scale: options.scale || 2,\n zoomTarget: options.zoomTarget,\n zoomLensScale: options.zoomLensScale || 1,\n disableScrollLock: options.disableScrollLock || false,\n }\n\n const {\n scale,\n zoomImageSource,\n customZoom,\n zoomLensClass,\n zoomTarget,\n zoomLensScale,\n zoomTargetClass,\n disableScrollLock,\n } = finalOptions\n\n const store = createStore<ZoomImageHoverState>({\n zoomedImgStatus: \"idle\",\n enabled: true,\n })\n\n let offset: { left: number; top: number } = getOffset(sourceImgElement)\n\n function getOffset(element: HTMLElement) {\n const elRect = element.getBoundingClientRect()\n return { left: elRect.left, top: elRect.top }\n }\n\n function getLimitX(value: number) {\n return sourceImageElementWidth - value\n }\n\n function getLimitY(value: number) {\n return sourceImageElementHeight - value\n }\n\n function zoomLensLeft(left: number) {\n const minX = zoomLens.clientWidth / 2\n return clamp(left, minX, getLimitX(minX)) - minX\n }\n\n function zoomLensTop(top: number) {\n const minY = zoomLens.clientHeight / 2\n return clamp(top, minY, getLimitY(minY)) - minY\n }\n\n function processZoom(event: PointerEvent) {\n let offsetX: number\n let offsetY: number\n let backgroundX: number\n let backgroundY: number\n if (offset) {\n offsetX = zoomLensLeft(event.clientX - offset.left)\n offsetY = zoomLensTop(event.clientY - offset.top)\n backgroundX = (offsetX * scale) / zoomLensScale\n backgroundY = (offsetY * scale) / zoomLensScale\n zoomedImg.style.transform = \"translate(\" + -backgroundX + \"px,\" + -backgroundY + \"px)\"\n zoomLens.style.cssText += \"transform:\" + \"translate(\" + offsetX + \"px,\" + offsetY + \"px);\"\n }\n }\n\n async function handlePointerEnter() {\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n zoomedImg.style.display = \"block\"\n zoomLens.style.display = \"block\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.add(className))\n }\n\n if (!disableScrollLock) disableScroll()\n }\n\n function handlePointerLeave() {\n zoomedImg.style.display = \"none\"\n zoomLens.style.display = \"none\"\n\n if (zoomTargetClass) {\n const classes = zoomTargetClass.split(\" \")\n classes.forEach((className) => zoomTarget.classList.remove(className))\n }\n\n if (!disableScrollLock) enableScroll()\n }\n\n function handleScroll() {\n offset = getOffset(sourceImgElement)\n }\n\n async function setup() {\n if (zoomLensClass) {\n zoomLens.className = zoomLensClass\n } else {\n zoomLens.style.background = \"rgba(238, 130, 238, 0.5)\"\n }\n\n // setup event listeners\n container.addEventListener(\"pointerdown\", processZoom, { signal })\n container.addEventListener(\"pointermove\", processZoom, { signal })\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n window.addEventListener(\"scroll\", handleScroll, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n // Append zoomed image wrapper to zoom target\n zoomTarget.appendChild(zoomedImgWrapper)\n\n // Wait for next tick to get container size\n await new Promise((resolve) => setTimeout(resolve, 1))\n const containerRect = container.getBoundingClientRect()\n sourceImageElementWidth = containerRect.width\n sourceImageElementHeight = containerRect.height\n\n // Set up styles if custom zoom available\n if (customZoom) {\n zoomedImgWrapper.style.width = customZoom.width + \"px\"\n zoomedImgWrapper.style.height = customZoom.height + \"px\"\n } else {\n // Else default zoom to source image size\n zoomedImgWrapper.style.width = sourceImageElementWidth + \"px\"\n zoomedImgWrapper.style.height = sourceImageElementHeight + \"px\"\n }\n\n zoomedImg.width = (sourceImageElementWidth * scale) / zoomLensScale\n zoomedImg.height = (sourceImageElementHeight * scale) / zoomLensScale\n\n // Setup default zoom lens style\n const sourceImageRect = sourceImgElement.getBoundingClientRect()\n const fromLeft = sourceImageRect.left - containerRect.left\n const fromTop = sourceImageRect.top - containerRect.top\n zoomTarget.style.pointerEvents = \"none\"\n zoomLens.style.position = \"absolute\"\n zoomLens.style.left = fromLeft + \"px\"\n zoomLens.style.top = fromTop + \"px\"\n zoomLens.style.width = (customZoom.width / scale) * zoomLensScale + \"px\"\n zoomLens.style.height = (customZoom.height / scale) * zoomLensScale + \"px\"\n }\n\n setup()\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomLens) && container.removeChild(zoomLens)\n\n if (zoomTarget && zoomTarget.contains(zoomedImgWrapper)) {\n zoomTarget.removeChild(zoomedImgWrapper)\n return\n }\n\n container.contains(zoomedImgWrapper) && container.removeChild(zoomedImgWrapper)\n },\n subscribe: store.subscribe,\n getState: store.getState,\n setState: (newState: ZoomImageHoverStateUpdate) => {\n store.setState(newState)\n },\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { ZoomedImgStatus } from \"./types\"\n\n// There is a scenario where the image is already in the cache,\n// we don't want to trigger the loading state too quickly in that case\n// 50ms should be enough to wait before triggering the loading state\nconst THRESHOLD = 50\n\nexport const makeImageLoader = () => {\n const createZoomImage = (\n img: HTMLImageElement,\n src: string,\n store: ReturnType<\n typeof createStore<{\n zoomedImgStatus: ZoomedImgStatus\n }>\n >,\n ) => {\n if (img.src === src) return\n img.src = src\n let complete = false\n\n img.onload = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"loaded\" })\n }\n\n img.onerror = () => {\n complete = true\n store.setState({ zoomedImgStatus: \"error\" })\n }\n\n setTimeout(() => {\n if (!complete) store.setState({ zoomedImgStatus: \"loading\" })\n }, THRESHOLD)\n }\n\n return {\n createZoomImage,\n }\n}\n\nexport const imageLoader = makeImageLoader()\n","export function clamp(value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function noop() {}\n\nfunction preventDefault(event: Event) {\n event.preventDefault()\n}\n\nconst keySet = new Set([\"ArrowUp\", \"ArrowRight\", \"ArrowDown\", \"ArrowLeft\"])\nfunction preventDefaultForScrollKeys(event: KeyboardEvent) {\n if (keySet.has(event.key)) {\n preventDefault(event)\n return false\n }\n}\n\nconst controller = new AbortController()\nconst signal = controller.signal\n\nexport function disableScroll() {\n window.addEventListener(\"DOMMouseScroll\", preventDefault, { signal })\n window.addEventListener(\"wheel\", preventDefault, { passive: false, signal })\n window.addEventListener(\"touchmove\", preventDefault, { passive: false, signal })\n window.addEventListener(\"keydown\", preventDefaultForScrollKeys, { signal })\n}\n\nexport function enableScroll() {\n controller?.abort()\n}\n\nexport function getSourceImage(container: HTMLElement) {\n if (!container) {\n throw new Error(\"Please specify a container for the zoom image\")\n }\n\n const sourceImgElement = container.querySelector(\"img\")\n if (!sourceImgElement) {\n throw new Error(\"Please place an image inside the container\")\n }\n\n return sourceImgElement\n}\n\nexport type PointerPosition = {\n x: number\n y: number\n}\n\nexport function getPointersCenter(first: PointerPosition, second: PointerPosition) {\n return {\n x: (first.x + second.x) / 2,\n y: (first.y + second.y) / 2,\n }\n}\n\n// Given the previous and current positions of two touch inputs, compute the zoom\n// factor and the origin of the zoom gesture.\nexport function computeZoomGesture(prev: [PointerPosition, PointerPosition], curr: [PointerPosition, PointerPosition]) {\n const prevCenter = getPointersCenter(prev[0], prev[1])\n const currCenter = getPointersCenter(curr[0], curr[1])\n const centerDist = { x: currCenter.x - prevCenter.x, y: currCenter.y - prevCenter.y }\n\n const prevDistance = Math.hypot(prev[0].x - prev[1].x, prev[0].y - prev[1].y)\n const currDistance = Math.hypot(curr[0].x - curr[1].x, curr[0].y - curr[1].y)\n let scale = currDistance / prevDistance\n\n // avoid division by zero\n const eps = 0.00001\n if (Math.abs(scale - 1) < eps) {\n scale = 1 + eps\n }\n\n return {\n scale,\n center: {\n // We shift the zoom center away such that the translation part of the gesture\n // is also captured by the zoom operation.\n x: prevCenter.x + centerDist.x / (1 - scale),\n y: prevCenter.y + centerDist.y / (1 - scale),\n },\n }\n}\n\nexport function makeMaybeCallFunction<T>(predicateFn: () => boolean, fn: (arg: T) => void) {\n return (arg: T) => {\n if (predicateFn()) {\n fn(arg)\n }\n }\n}\n\nexport const scaleLinear =\n ({\n domainStart,\n domainStop,\n rangeStart,\n rangeStop,\n }: {\n domainStart: number\n domainStop: number\n rangeStart: number\n rangeStop: number\n }) =>\n (value: number) =>\n rangeStart + (rangeStop - rangeStart) * ((value - domainStart) / (domainStop - domainStart))\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage } from \"./utils\"\n\nexport type ZoomImageMoveOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n // @deprecated\n disableScrollLock?: boolean\n disabledContextMenu?: boolean\n zoomImageProps?: {\n alt?: string\n className?: string\n }\n}\n\nexport type ZoomImageMoveState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageMove(container: HTMLElement, options: ZoomImageMoveOptions = {}) {\n let activePointerId: number | null = null\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageMoveOptions>, \"zoomImageProps\" | \"disableScrollLock\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disabledContextMenu: options.disabledContextMenu ?? false,\n }\n\n const { disabledContextMenu, zoomFactor, zoomImageSource } = finalOptions\n\n const store = createStore<ZoomImageMoveState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImg = document.createElement(\"img\")\n options.zoomImageProps?.alt && (zoomedImg.alt = options.zoomImageProps.alt)\n options.zoomImageProps?.className && (zoomedImg.className = options.zoomImageProps.className)\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n // Credit to https://stackoverflow.com/questions/19587555/disable-default-save-image-option-in-mobile/19590365#19590365\n if (disabledContextMenu) {\n zoomedImg.style[\"-webkit-user-select\"] = \"none\"\n zoomedImg.style[\"-webkit-touch-callout\"] = \"none\"\n zoomedImg.oncontextmenu = () => false\n }\n\n const checkValidPointer = (event: PointerEvent) => {\n return activePointerId && event.pointerId === activePointerId\n }\n\n function handlePointerEnter(event: PointerEvent) {\n if (activePointerId === null) {\n activePointerId = event.pointerId\n container.appendChild(zoomedImg)\n zoomedImg.style.display = \"block\"\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n processZoom(event)\n\n event.pointerType !== \"mouse\" && disableScroll()\n }\n }\n\n function handlePointerMove(event: PointerEvent) {\n if (checkValidPointer(event)) {\n processZoom(event)\n }\n }\n\n function resetZoomedImg(event: PointerEvent) {\n if (checkValidPointer(event)) {\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n zoomedImg.style.display = \"none\"\n zoomedImg.style.transform = \"none\"\n enableScroll()\n activePointerId = null\n }\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerenter\", handlePointerEnter, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerleave\", resetZoomedImg, { signal })\n container.addEventListener(\n \"touchstart\",\n (event) => {\n disabledContextMenu && event.preventDefault()\n },\n { signal },\n )\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n","import { createStore } from \"@namnode/store\"\nimport { imageLoader } from \"./imageLoader\"\nimport { ZoomedImgStatus } from \"./types\"\nimport { disableScroll, enableScroll, getSourceImage, noop } from \"./utils\"\n\nexport type ZoomImageClickOptions = {\n zoomFactor?: number\n zoomImageSource?: string\n disableScrollLock?: boolean\n zoomImageProps?: {\n alt?: string\n }\n}\n\nexport type ZoomImageClickState = {\n zoomedImgStatus: ZoomedImgStatus\n}\n\nexport function createZoomImageClick(container: HTMLElement, options: ZoomImageClickOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Omit<Required<ZoomImageClickOptions>, \"zoomImageProps\"> = {\n zoomFactor: options.zoomFactor ?? 4,\n zoomImageSource: options.zoomImageSource ?? sourceImgElement.src,\n disableScrollLock: options.disableScrollLock ?? false,\n }\n\n const { zoomFactor, zoomImageSource, disableScrollLock } = finalOptions\n\n let isOnMove = false\n\n const store = createStore<ZoomImageClickState>({\n zoomedImgStatus: \"idle\",\n })\n\n const zoomedImgWidth = sourceImgElement.clientWidth * zoomFactor\n const zoomedImgHeight = sourceImgElement.clientHeight * zoomFactor\n const zoomedImg = container.appendChild(document.createElement(\"img\"))\n zoomedImg.alt = options.zoomImageProps?.alt || \"\"\n zoomedImg.style.maxWidth = \"none\"\n zoomedImg.style.display = \"none\"\n zoomedImg.style.width = `${zoomedImgWidth}px`\n zoomedImg.style.height = `${zoomedImgHeight}px`\n zoomedImg.style.position = \"absolute\"\n zoomedImg.style.top = \"0\"\n zoomedImg.style.left = \"0\"\n\n function handlePointerMove(event: PointerEvent) {\n if (!isOnMove) {\n return\n }\n\n processZoom(event)\n }\n\n const calculatePositionX = (newPositionX: number) => {\n const width = container.clientWidth\n if (newPositionX > 0) return 0\n if (newPositionX + width * zoomFactor < width) return -width * (zoomFactor - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number) => {\n const height = container.clientHeight\n if (newPositionY > 0) return 0\n if (newPositionY + height * zoomFactor < height) return -height * (zoomFactor - 1)\n return newPositionY\n }\n\n function processZoom(event: PointerEvent) {\n zoomedImg.style.display = \"block\"\n imageLoader.createZoomImage(zoomedImg, zoomImageSource, store)\n\n const containerRect = container.getBoundingClientRect()\n const zoomPointX = event.clientX - containerRect.left\n const zoomPointY = event.clientY - containerRect.top\n\n const currentPositionX = calculatePositionX(-zoomPointX * zoomFactor + zoomPointX)\n const currentPositionY = calculatePositionY(-zoomPointY * zoomFactor + zoomPointY)\n\n zoomedImg.style.transform = `translate(${currentPositionX}px, ${currentPositionY}px)`\n }\n\n function handlePointerDown(event: PointerEvent) {\n if (isOnMove) {\n isOnMove = false\n zoomedImg.style.display = \"none\"\n return\n }\n\n processZoom(event)\n isOnMove = true\n }\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerenter\", disableScrollLock ? noop : disableScroll, { signal })\n container.addEventListener(\"pointerleave\", disableScrollLock ? noop : enableScroll, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"touchend\", enableScroll, { signal })\n\n return {\n cleanup: () => {\n controller.abort()\n container.contains(zoomedImg) && container.removeChild(zoomedImg)\n store.cleanup()\n },\n subscribe: store.subscribe,\n getState: store.getState,\n }\n}\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculatePercentage = (maxZoom: number) =>\n scaleLinear({\n domainStart: 1,\n domainStop: maxZoom,\n rangeStart: 0,\n rangeStop: 100,\n })\n","import { scaleLinear } from \"./utils\"\n\nexport const makeCalculateZoom = (maxZoom: number) =>\n scaleLinear({\n domainStart: 0,\n domainStop: 100,\n rangeStart: 1,\n rangeStop: maxZoom,\n })\n","type CropImageArg = {\n currentZoom: number\n image: HTMLImageElement\n positionX: number\n positionY: number\n rotation?: number\n}\n\nexport const cropImage = async ({ image, positionX, positionY, currentZoom, rotation = 0 }: CropImageArg) => {\n const canvas = document.createElement(\"canvas\")\n const scale = image.naturalWidth / (image.clientWidth * currentZoom)\n const normalizedRotation = rotation % 360\n const croppedImageWidth = image.clientWidth * scale\n const croppedImageHeight = image.clientHeight * scale\n canvas.width = croppedImageWidth\n canvas.height = croppedImageHeight\n const canvasContext = canvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n const sx = Math.max(0, Math.abs(positionX) * scale)\n const sy = Math.max(0, Math.abs(positionY) * scale)\n\n canvasContext.drawImage(\n image,\n sx,\n sy,\n croppedImageWidth,\n croppedImageHeight,\n 0,\n 0,\n croppedImageWidth,\n croppedImageHeight,\n )\n\n const originalImage = new Image()\n originalImage.src = canvas.toDataURL()\n\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n const rotatedCanvas = document.createElement(\"canvas\") as HTMLCanvasElement\n const rotatedCanvasContext = rotatedCanvas.getContext(\"2d\") as CanvasRenderingContext2D\n\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvas.width = originalImage.naturalHeight\n rotatedCanvas.height = originalImage.naturalWidth\n } else {\n rotatedCanvas.width = originalImage.naturalWidth\n rotatedCanvas.height = originalImage.naturalHeight\n }\n\n rotatedCanvasContext.clearRect(0, 0, canvas.width, canvas.height)\n if (normalizedRotation === 90 || normalizedRotation === 270) {\n rotatedCanvasContext.translate(originalImage.height / 2, originalImage.width / 2)\n } else {\n rotatedCanvasContext.translate(originalImage.width / 2, originalImage.height / 2)\n }\n rotatedCanvasContext.rotate((normalizedRotation * Math.PI) / 180)\n rotatedCanvasContext.drawImage(originalImage, -originalImage.width / 2, -originalImage.height / 2)\n\n return rotatedCanvas.toDataURL()\n}\n"]}

@@ -5,3 +5,3 @@ export { makeCalculateZoom } from './chunk-FA74QUJD.mjs';

export { createZoomImageMove } from './chunk-FD7T6CMU.mjs';
export { createZoomImageWheel } from './chunk-75T2QBQQ.mjs';
export { createZoomImageWheel } from './chunk-5D7YXNAE.mjs';
export { cropImage } from './chunk-AWAB2JUH.mjs';

@@ -8,0 +8,0 @@ import './chunk-XAQU5VJO.mjs';

{
"name": "@zoom-image/core",
"version": "0.41.2",
"version": "0.41.3",
"description": "A core implementation of zoom image",

@@ -5,0 +5,0 @@ "author": "Nam Nguyen <nam.nguyen.node@gmail.com>",

@@ -398,3 +398,2 @@ import { createStore } from "@namnode/store"

function _handlePointerUp(event: PointerEvent) {
event.preventDefault()
pointerMap.delete(event.pointerId)

@@ -401,0 +400,0 @@

import { getSourceImage, makeMaybeCallFunction, enableScroll, clamp, disableScroll, computeZoomGesture } from './chunk-5KET5YPV.mjs';
import { createStore } from '@namnode/store';
var ZOOM_DELTA = 0.5;
var defaultInitialState = {
currentZoom: 1,
enable: true,
currentPositionX: 0,
currentPositionY: 0,
currentRotation: 0
};
var defaultShouldZoomOnSingleTouch = () => true;
function createZoomImageWheel(container, options = {}) {
const sourceImgElement = getSourceImage(container);
const finalOptions = {
maxZoom: options.maxZoom || 4,
wheelZoomRatio: options.wheelZoomRatio || 0.1,
dblTapAnimationDuration: options.dblTapAnimationDuration || 300,
initialState: { ...defaultInitialState, ...options.initialState },
shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch
};
const store = createStore(finalOptions.initialState);
const checkDimensionSwitched = () => {
return [90, 270].includes(store.getState().currentRotation % 360);
};
const calculatePositionX = (newPositionX, currentZoom, isDimensionSwitched) => {
if (newPositionX > 0)
return 0;
const width = isDimensionSwitched ? container.clientHeight : container.clientWidth;
if (newPositionX + width * currentZoom < width)
return -width * (currentZoom - 1);
return newPositionX;
};
const calculatePositionY = (newPositionY, currentZoom, isDimensionSwitched) => {
if (newPositionY > 0)
return 0;
const height = isDimensionSwitched ? container.clientWidth : container.clientHeight;
if (newPositionY + height * currentZoom < height)
return -height * (currentZoom - 1);
return newPositionY;
};
const updateStateOnNewZoom = (currentZoom) => {
const zoomPointX = container.clientWidth / 2;
const zoomPointY = container.clientHeight / 2;
const currentState = store.getState();
const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom;
const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom;
store.setState({
currentZoom,
currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),
currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false)
});
};
let prevTwoPositions = null;
let enabledScroll = true;
const pointerMap = /* @__PURE__ */ new Map();
let lastPositionX = 0;
let lastPositionY = 0;
let startX = 0;
let startY = 0;
container.style.overflow = "hidden";
sourceImgElement.style.transformOrigin = "0 0";
function updateZoom() {
const currentState = store.getState();
sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`;
container.style.rotate = `${currentState.currentRotation}deg`;
}
function setState(newState) {
store.batch(() => {
const currentState = store.getState();
if (typeof newState.enable === "boolean" && newState.enable !== currentState.enable) {
store.setState({
enable: newState.enable
});
if (!newState.enable) {
return;
}
}
if (typeof newState.currentRotation === "number") {
const newCurrentRotation = newState.currentRotation;
store.setState({
currentRotation: newCurrentRotation
});
}
if (typeof newState.currentZoom === "number" && newState.currentZoom !== currentState.currentZoom) {
const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom);
if (newCurrentZoom === currentState.currentZoom) {
return;
}
updateStateOnNewZoom(newCurrentZoom);
}
});
updateZoom();
}
function processZoomWheel({ delta, x, y }) {
const containerRect = container.getBoundingClientRect();
const currentState = store.getState();
const isDimensionSwitched = checkDimensionSwitched();
let zoomPointX = -1;
let zoomPointY = -1;
switch (currentState.currentRotation % 360) {
case 0:
zoomPointX = x - containerRect.left;
zoomPointY = y - containerRect.top;
break;
case 90:
zoomPointX = Math.abs(x - containerRect.right);
zoomPointY = Math.abs(y - containerRect.top);
break;
case 180:
zoomPointX = Math.abs(x - containerRect.right);
zoomPointY = Math.abs(y - containerRect.bottom);
break;
case 270:
zoomPointX = Math.abs(x - containerRect.left);
zoomPointY = Math.abs(y - containerRect.bottom);
break;
}
const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX;
const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY;
const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom;
const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom;
const newCurrentZoom = clamp(
currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,
1,
finalOptions.maxZoom
);
const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched);
const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched);
store.setState({
currentZoom: newCurrentZoom,
currentPositionX: isDimensionSwitched ? newY : newX,
currentPositionY: isDimensionSwitched ? newX : newY
});
}
function updatePositionsForSinglePointerFlow() {
if (pointerMap.size === 1) {
const { x, y } = pointerMap.values().next().value;
const isDimensionSwitched = checkDimensionSwitched();
startX = isDimensionSwitched ? y : x;
startY = isDimensionSwitched ? x : y;
}
const currentState = store.getState();
lastPositionX = currentState.currentPositionX;
lastPositionY = currentState.currentPositionY;
}
function _handleWheel(event) {
event.preventDefault();
if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {
return;
}
const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA);
processZoomWheel({ delta, x: event.clientX, y: event.clientY });
updateZoom();
updatePositionsForSinglePointerFlow();
}
function _handlePointerMove(event) {
event.preventDefault();
const { clientX, clientY, pointerId } = event;
for (const [cachedPointerId] of pointerMap.entries()) {
if (cachedPointerId === pointerId) {
pointerMap.set(cachedPointerId, { x: clientX, y: clientY });
}
}
const { currentZoom, currentRotation } = store.getState();
if (pointerMap.size === 1 && currentZoom !== 1) {
const isDimensionSwitched = checkDimensionSwitched();
const normalizedClientX = isDimensionSwitched ? clientY : clientX;
const normalizedClientY = isDimensionSwitched ? clientX : clientY;
let offsetX = -1;
let offsetY = -1;
switch (currentRotation % 360) {
case 0:
offsetX = normalizedClientX - startX;
offsetY = normalizedClientY - startY;
break;
case 90:
offsetX = normalizedClientX - startX;
offsetY = startY - normalizedClientY;
break;
case 180:
offsetX = startX - normalizedClientX;
offsetY = startY - normalizedClientY;
break;
case 270:
offsetX = startX - normalizedClientX;
offsetY = normalizedClientY - startY;
break;
}
store.setState({
currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),
currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false)
});
updateZoom();
}
}
const animationState = {
startTimestamp: null,
// the state at the start of the zoom animation
start: { x: 0, y: 0, zoom: 0 },
// the target state at the end of the zoom animation
target: { x: 0, y: 0, zoom: 0 }
};
function animateZoom(touchCoordinate) {
const currentState = store.getState();
animationState.startTimestamp = null;
animationState.start = {
x: currentState.currentPositionX,
y: currentState.currentPositionY,
zoom: currentState.currentZoom
};
if (currentState.currentZoom > 1) {
animationState.target = {
x: 0,
y: 0,
zoom: 1
};
} else {
animationState.target = {
zoom: finalOptions.maxZoom,
x: touchCoordinate.x * (1 - finalOptions.maxZoom),
y: touchCoordinate.y * (1 - finalOptions.maxZoom)
};
}
function lerp(a, b, t) {
return a * (1 - t) + b * t;
}
function frame(timestamp) {
if (animationState.startTimestamp === null) {
animationState.startTimestamp = timestamp;
}
let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration;
if (t > 1) {
t = 1;
}
store.setState({
currentPositionX: lerp(animationState.start.x, animationState.target.x, t),
currentPositionY: lerp(animationState.start.y, animationState.target.y, t),
currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t)
});
updateZoom();
if (t < 1) {
requestAnimationFrame(frame);
}
}
requestAnimationFrame(frame);
}
let touchTimer = null;
const durationBetweenTap = 300;
function _handleTouchStart(event) {
if (event.touches.length > 1) {
return;
}
if (touchTimer === null) {
touchTimer = setTimeout(() => {
touchTimer = null;
}, durationBetweenTap);
} else {
clearTimeout(touchTimer);
touchTimer = null;
const rect = container.getBoundingClientRect();
const touch = event.touches[0];
animateZoom({
x: touch.clientX - rect.left,
y: touch.clientY - rect.top
});
return;
}
}
function _handleTouchMove(event) {
if (finalOptions.shouldZoomOnSingleTouch())
event.preventDefault();
if (event.touches.length > 1) {
event.preventDefault();
const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY }));
if (prevTwoPositions !== null) {
const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions);
processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center });
}
prevTwoPositions = currentTwoPositions;
updateZoom();
return;
}
}
function _handlePointerDown(event) {
if (event.pointerType === "touch" && !finalOptions.shouldZoomOnSingleTouch())
return;
event.preventDefault();
if (pointerMap.size === 2) {
return;
}
if (enabledScroll) {
disableScroll();
enabledScroll = false;
}
const { clientX, clientY, pointerId } = event;
const currentState = store.getState();
lastPositionX = currentState.currentPositionX;
lastPositionY = currentState.currentPositionY;
const isDimensionSwitched = checkDimensionSwitched();
startX = isDimensionSwitched ? clientY : clientX;
startY = isDimensionSwitched ? clientX : clientY;
pointerMap.set(pointerId, { x: clientX, y: clientY });
}
function _handlePointerUp(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);
if (pointerMap.size < 2) {
prevTwoPositions = null;
}
if (pointerMap.size === 0 && !enabledScroll) {
enableScroll();
enabledScroll = true;
}
updatePositionsForSinglePointerFlow();
}
function _handlePointerLeave(event) {
event.preventDefault();
pointerMap.delete(event.pointerId);
prevTwoPositions = null;
if (!enabledScroll) {
enableScroll();
enabledScroll = true;
}
}
function checkZoomEnabled() {
return store.getState().enable;
}
const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel);
const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown);
const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave);
const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove);
const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp);
const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart);
const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove);
const controller = new AbortController();
const { signal } = controller;
container.addEventListener("wheel", handleWheel, { signal });
container.addEventListener("touchstart", handleTouchStart, { signal });
container.addEventListener("touchmove", handleTouchMove, { signal });
container.addEventListener("pointerdown", handlePointerDown, { signal });
container.addEventListener("pointerleave", handlePointerLeave, { signal });
container.addEventListener("pointermove", handlePointerMove, { signal });
container.addEventListener("pointerup", handlePointerUp, { signal });
container.addEventListener(
"touchend",
() => {
enabledScroll = true;
enableScroll();
},
{ signal }
);
if (store.getState().currentZoom !== defaultInitialState.currentZoom) {
updateStateOnNewZoom(store.getState().currentZoom);
updateZoom();
}
return {
cleanup() {
controller.abort();
store.cleanup();
},
subscribe: store.subscribe,
setState,
getState: store.getState
};
}
export { createZoomImageWheel };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=chunk-75T2QBQQ.mjs.map
{"version":3,"sources":["../src/createZoomImageWheel.ts"],"names":[],"mappings":"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","sourcesContent":["import { createStore } from \"@namnode/store\"\nimport type { PointerPosition } from \"./utils\"\nimport { clamp, computeZoomGesture, disableScroll, enableScroll, getSourceImage, makeMaybeCallFunction } from \"./utils\"\n\nexport type ZoomImageWheelOptions = {\n maxZoom?: number\n wheelZoomRatio?: number\n dblTapAnimationDuration?: number\n initialState?: Partial<ZoomImageWheelStateUpdate>\n shouldZoomOnSingleTouch?: () => boolean\n}\n\n/* The delta values are not consistent across browsers.\n * We need to normalize them to a consistent value.\n * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY\n */\nconst ZOOM_DELTA = 0.5\n\nexport type ZoomImageWheelState = {\n currentRotation: number\n currentZoom: number\n enable: boolean\n currentPositionX: number\n currentPositionY: number\n}\n\nexport type ZoomImageWheelStateUpdate = Partial<{\n enable: boolean\n currentZoom: number\n currentRotation: number\n}>\n\nconst defaultInitialState: ZoomImageWheelState = {\n currentZoom: 1,\n enable: true,\n currentPositionX: 0,\n currentPositionY: 0,\n currentRotation: 0,\n}\n\nconst defaultShouldZoomOnSingleTouch = () => true\n\nexport function createZoomImageWheel(container: HTMLElement, options: ZoomImageWheelOptions = {}) {\n const sourceImgElement = getSourceImage(container)\n const finalOptions: Required<ZoomImageWheelOptions> = {\n maxZoom: options.maxZoom || 4,\n wheelZoomRatio: options.wheelZoomRatio || 0.1,\n dblTapAnimationDuration: options.dblTapAnimationDuration || 300,\n initialState: { ...defaultInitialState, ...options.initialState },\n shouldZoomOnSingleTouch: options.shouldZoomOnSingleTouch || defaultShouldZoomOnSingleTouch,\n }\n\n const store = createStore<ZoomImageWheelState>(finalOptions.initialState as ZoomImageWheelState)\n\n const checkDimensionSwitched = () => {\n return [90, 270].includes(store.getState().currentRotation % 360)\n }\n\n const calculatePositionX = (newPositionX: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionX > 0) return 0\n\n const width = isDimensionSwitched ? container.clientHeight : container.clientWidth\n if (newPositionX + width * currentZoom < width) return -width * (currentZoom - 1)\n return newPositionX\n }\n\n const calculatePositionY = (newPositionY: number, currentZoom: number, isDimensionSwitched: boolean) => {\n if (newPositionY > 0) return 0\n\n const height = isDimensionSwitched ? container.clientWidth : container.clientHeight\n if (newPositionY + height * currentZoom < height) return -height * (currentZoom - 1)\n return newPositionY\n }\n\n const updateStateOnNewZoom = (currentZoom: number) => {\n const zoomPointX = container.clientWidth / 2\n const zoomPointY = container.clientHeight / 2\n const currentState = store.getState()\n const zoomTargetX = (zoomPointX - currentState.currentPositionX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - currentState.currentPositionY) / currentState.currentZoom\n\n store.setState({\n currentZoom,\n currentPositionX: calculatePositionX(-zoomTargetX * currentZoom + zoomPointX, currentZoom, false),\n currentPositionY: calculatePositionY(-zoomTargetY * currentZoom + zoomPointY, currentZoom, false),\n })\n }\n\n // last pair of coordinates of a touch with two fingers\n let prevTwoPositions: [PointerPosition, PointerPosition] | null = null\n let enabledScroll = true\n const pointerMap = new Map<number, PointerPosition>()\n\n let lastPositionX = 0\n let lastPositionY = 0\n let startX = 0\n let startY = 0\n\n container.style.overflow = \"hidden\"\n sourceImgElement.style.transformOrigin = \"0 0\"\n\n function updateZoom() {\n const currentState = store.getState()\n sourceImgElement.style.transform = `translate(${currentState.currentPositionX}px, ${currentState.currentPositionY}px) scale(${currentState.currentZoom})`\n container.style.rotate = `${currentState.currentRotation}deg`\n }\n\n function setState(newState: ZoomImageWheelStateUpdate) {\n store.batch(() => {\n const currentState = store.getState()\n if (typeof newState.enable === \"boolean\" && newState.enable !== currentState.enable) {\n store.setState({\n enable: newState.enable,\n })\n\n if (!newState.enable) {\n return\n }\n }\n\n if (typeof newState.currentRotation === \"number\") {\n const newCurrentRotation = newState.currentRotation\n store.setState({\n currentRotation: newCurrentRotation,\n })\n }\n\n if (typeof newState.currentZoom === \"number\" && newState.currentZoom !== currentState.currentZoom) {\n const newCurrentZoom = clamp(newState.currentZoom, 1, finalOptions.maxZoom)\n\n if (newCurrentZoom === currentState.currentZoom) {\n return\n }\n\n updateStateOnNewZoom(newCurrentZoom)\n }\n })\n\n updateZoom()\n }\n\n function processZoomWheel({ delta, x, y }: { delta: number; x: number; y: number }) {\n const containerRect = container.getBoundingClientRect()\n const currentState = store.getState()\n const isDimensionSwitched = checkDimensionSwitched()\n\n let zoomPointX = -1\n let zoomPointY = -1\n\n switch (currentState.currentRotation % 360) {\n case 0:\n zoomPointX = x - containerRect.left\n zoomPointY = y - containerRect.top\n break\n case 90:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.top)\n break\n case 180:\n zoomPointX = Math.abs(x - containerRect.right)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n case 270:\n zoomPointX = Math.abs(x - containerRect.left)\n zoomPointY = Math.abs(y - containerRect.bottom)\n break\n }\n\n const zoomX = isDimensionSwitched ? currentState.currentPositionY : currentState.currentPositionX\n const zoomY = isDimensionSwitched ? currentState.currentPositionX : currentState.currentPositionY\n\n const zoomTargetX = (zoomPointX - zoomX) / currentState.currentZoom\n const zoomTargetY = (zoomPointY - zoomY) / currentState.currentZoom\n\n const newCurrentZoom = clamp(\n currentState.currentZoom + delta * finalOptions.wheelZoomRatio * currentState.currentZoom,\n 1,\n finalOptions.maxZoom,\n )\n\n const newX = calculatePositionX(-zoomTargetX * newCurrentZoom + zoomPointX, newCurrentZoom, isDimensionSwitched)\n const newY = calculatePositionY(-zoomTargetY * newCurrentZoom + zoomPointY, newCurrentZoom, isDimensionSwitched)\n\n store.setState({\n currentZoom: newCurrentZoom,\n currentPositionX: isDimensionSwitched ? newY : newX,\n currentPositionY: isDimensionSwitched ? newX : newY,\n })\n }\n\n function updatePositionsForSinglePointerFlow() {\n if (pointerMap.size === 1) {\n const { x, y } = pointerMap.values().next().value as PointerPosition\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? y : x\n startY = isDimensionSwitched ? x : y\n }\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n }\n\n function _handleWheel(event: WheelEvent) {\n event.preventDefault()\n\n if (store.getState().currentZoom === finalOptions.maxZoom && event.deltaY < 0) {\n return\n }\n\n const delta = -clamp(event.deltaY, -ZOOM_DELTA, ZOOM_DELTA)\n processZoomWheel({ delta, x: event.clientX, y: event.clientY })\n updateZoom()\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerMove(event: PointerEvent) {\n event.preventDefault()\n const { clientX, clientY, pointerId } = event\n for (const [cachedPointerId] of pointerMap.entries()) {\n if (cachedPointerId === pointerId) {\n pointerMap.set(cachedPointerId, { x: clientX, y: clientY })\n }\n }\n\n const { currentZoom, currentRotation } = store.getState()\n if (pointerMap.size === 1 && currentZoom !== 1) {\n const isDimensionSwitched = checkDimensionSwitched()\n const normalizedClientX = isDimensionSwitched ? clientY : clientX\n const normalizedClientY = isDimensionSwitched ? clientX : clientY\n\n let offsetX = -1\n let offsetY = -1\n\n switch (currentRotation % 360) {\n case 0:\n offsetX = normalizedClientX - startX\n offsetY = normalizedClientY - startY\n break\n case 90:\n offsetX = normalizedClientX - startX\n offsetY = startY - normalizedClientY\n break\n case 180:\n offsetX = startX - normalizedClientX\n offsetY = startY - normalizedClientY\n break\n case 270:\n offsetX = startX - normalizedClientX\n offsetY = normalizedClientY - startY\n break\n }\n\n store.setState({\n currentPositionX: calculatePositionX(lastPositionX + offsetX, currentZoom, false),\n currentPositionY: calculatePositionY(lastPositionY + offsetY, currentZoom, false),\n })\n updateZoom()\n }\n }\n\n const animationState = {\n startTimestamp: null as DOMHighResTimeStamp | null,\n // the state at the start of the zoom animation\n start: { x: 0, y: 0, zoom: 0 },\n // the target state at the end of the zoom animation\n target: { x: 0, y: 0, zoom: 0 },\n }\n\n function animateZoom(touchCoordinate: { x: number; y: number }) {\n // the `touchCoordinate` should be relative to the container\n const currentState = store.getState()\n\n animationState.startTimestamp = null\n animationState.start = {\n x: currentState.currentPositionX,\n y: currentState.currentPositionY,\n zoom: currentState.currentZoom,\n }\n\n if (currentState.currentZoom > 1) {\n animationState.target = {\n x: 0,\n y: 0,\n zoom: 1,\n }\n } else {\n animationState.target = {\n zoom: finalOptions.maxZoom,\n x: touchCoordinate.x * (1 - finalOptions.maxZoom),\n y: touchCoordinate.y * (1 - finalOptions.maxZoom),\n }\n }\n\n function lerp(a: number, b: number, t: number): number {\n return a * (1 - t) + b * t\n }\n\n function frame(timestamp: DOMHighResTimeStamp) {\n if (animationState.startTimestamp === null) {\n animationState.startTimestamp = timestamp\n }\n\n // interpolation parameter that linearly goes from 0 to 1 during the animation\n let t = (timestamp - animationState.startTimestamp) / finalOptions.dblTapAnimationDuration\n if (t > 1) {\n t = 1\n }\n\n store.setState({\n currentPositionX: lerp(animationState.start.x, animationState.target.x, t),\n currentPositionY: lerp(animationState.start.y, animationState.target.y, t),\n currentZoom: lerp(animationState.start.zoom, animationState.target.zoom, t),\n })\n updateZoom()\n\n if (t < 1) {\n requestAnimationFrame(frame)\n }\n }\n\n requestAnimationFrame(frame)\n }\n\n // These variables are used for zooming on double tap\n let touchTimer: NodeJS.Timeout | null = null\n const durationBetweenTap = 300\n\n function _handleTouchStart(event: TouchEvent) {\n if (event.touches.length > 1) {\n return\n }\n\n if (touchTimer === null) {\n touchTimer = setTimeout(() => {\n touchTimer = null\n }, durationBetweenTap)\n } else {\n clearTimeout(touchTimer)\n touchTimer = null\n\n const rect = container.getBoundingClientRect()\n const touch = event.touches[0]\n animateZoom({\n x: touch.clientX - rect.left,\n y: touch.clientY - rect.top,\n })\n return\n }\n }\n\n function _handleTouchMove(event: TouchEvent) {\n if (finalOptions.shouldZoomOnSingleTouch()) event.preventDefault()\n if (event.touches.length > 1) {\n event.preventDefault()\n const currentTwoPositions = [...event.touches].map((t) => ({ x: t.clientX, y: t.clientY })) as [\n PointerPosition,\n PointerPosition,\n ]\n\n if (prevTwoPositions !== null) {\n const { scale, center } = computeZoomGesture(prevTwoPositions, currentTwoPositions)\n processZoomWheel({ delta: Math.log(scale) / finalOptions.wheelZoomRatio, ...center })\n }\n // Store the current two pointer positions for the next move event\n prevTwoPositions = currentTwoPositions\n updateZoom()\n return\n }\n }\n\n function _handlePointerDown(event: PointerEvent) {\n if (event.pointerType === \"touch\" && !finalOptions.shouldZoomOnSingleTouch()) return\n event.preventDefault()\n if (pointerMap.size === 2) {\n return\n }\n\n if (enabledScroll) {\n disableScroll()\n enabledScroll = false\n }\n\n const { clientX, clientY, pointerId } = event\n\n const currentState = store.getState()\n lastPositionX = currentState.currentPositionX\n lastPositionY = currentState.currentPositionY\n\n const isDimensionSwitched = checkDimensionSwitched()\n startX = isDimensionSwitched ? clientY : clientX\n startY = isDimensionSwitched ? clientX : clientY\n pointerMap.set(pointerId, { x: clientX, y: clientY })\n }\n\n function _handlePointerUp(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n\n // Reset the distance as soon as one of the pointers is released\n if (pointerMap.size < 2) {\n prevTwoPositions = null\n }\n\n if (pointerMap.size === 0 && !enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n\n updatePositionsForSinglePointerFlow()\n }\n\n function _handlePointerLeave(event: PointerEvent) {\n event.preventDefault()\n pointerMap.delete(event.pointerId)\n prevTwoPositions = null\n if (!enabledScroll) {\n enableScroll()\n enabledScroll = true\n }\n }\n\n function checkZoomEnabled() {\n return store.getState().enable\n }\n\n const handleWheel = makeMaybeCallFunction(checkZoomEnabled, _handleWheel)\n const handlePointerDown = makeMaybeCallFunction(checkZoomEnabled, _handlePointerDown)\n const handlePointerLeave = makeMaybeCallFunction(checkZoomEnabled, _handlePointerLeave)\n const handlePointerMove = makeMaybeCallFunction(checkZoomEnabled, _handlePointerMove)\n const handlePointerUp = makeMaybeCallFunction(checkZoomEnabled, _handlePointerUp)\n const handleTouchStart = makeMaybeCallFunction(checkZoomEnabled, _handleTouchStart)\n const handleTouchMove = makeMaybeCallFunction(checkZoomEnabled, _handleTouchMove)\n\n const controller = new AbortController()\n const { signal } = controller\n container.addEventListener(\"wheel\", handleWheel, { signal })\n container.addEventListener(\"touchstart\", handleTouchStart, { signal })\n container.addEventListener(\"touchmove\", handleTouchMove, { signal })\n container.addEventListener(\"pointerdown\", handlePointerDown, { signal })\n container.addEventListener(\"pointerleave\", handlePointerLeave, { signal })\n container.addEventListener(\"pointermove\", handlePointerMove, { signal })\n container.addEventListener(\"pointerup\", handlePointerUp, { signal })\n container.addEventListener(\n \"touchend\",\n () => {\n enabledScroll = true\n enableScroll()\n },\n { signal },\n )\n\n // Kick things off in case we have initial zoom other than 1\n if (store.getState().currentZoom !== defaultInitialState.currentZoom) {\n updateStateOnNewZoom(store.getState().currentZoom)\n updateZoom()\n }\n\n return {\n cleanup() {\n controller.abort()\n store.cleanup()\n },\n subscribe: store.subscribe,\n setState,\n getState: store.getState,\n }\n}\n"]}