@embedpdf/plugin-zoom 2.0.0-next.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/preact/adapter.d.ts +1 -1
  2. package/dist/preact/index.cjs +1 -1
  3. package/dist/preact/index.cjs.map +1 -1
  4. package/dist/preact/index.js +217 -71
  5. package/dist/preact/index.js.map +1 -1
  6. package/dist/preact/viewport.d.ts +1 -0
  7. package/dist/react/adapter.d.ts +1 -1
  8. package/dist/react/index.cjs +1 -1
  9. package/dist/react/index.cjs.map +1 -1
  10. package/dist/react/index.js +217 -71
  11. package/dist/react/index.js.map +1 -1
  12. package/dist/react/viewport.d.ts +1 -0
  13. package/dist/shared/components/index.d.ts +1 -1
  14. package/dist/shared/components/zoom-gesture-wrapper.d.ts +12 -0
  15. package/dist/shared/hooks/index.d.ts +1 -1
  16. package/dist/shared/hooks/use-pinch-zoom.d.ts +3 -1
  17. package/dist/shared/hooks/use-zoom-gesture.d.ts +5 -0
  18. package/dist/shared/utils/pinch-zoom-logic.d.ts +11 -2
  19. package/dist/shared/utils/zoom-gesture-logic.d.ts +18 -0
  20. package/dist/shared-preact/components/index.d.ts +1 -1
  21. package/dist/shared-preact/components/zoom-gesture-wrapper.d.ts +12 -0
  22. package/dist/shared-preact/hooks/index.d.ts +1 -1
  23. package/dist/shared-preact/hooks/use-pinch-zoom.d.ts +3 -1
  24. package/dist/shared-preact/hooks/use-zoom-gesture.d.ts +5 -0
  25. package/dist/shared-preact/utils/pinch-zoom-logic.d.ts +11 -2
  26. package/dist/shared-preact/utils/zoom-gesture-logic.d.ts +18 -0
  27. package/dist/shared-react/components/index.d.ts +1 -1
  28. package/dist/shared-react/components/zoom-gesture-wrapper.d.ts +12 -0
  29. package/dist/shared-react/hooks/index.d.ts +1 -1
  30. package/dist/shared-react/hooks/use-pinch-zoom.d.ts +3 -1
  31. package/dist/shared-react/hooks/use-zoom-gesture.d.ts +5 -0
  32. package/dist/shared-react/utils/pinch-zoom-logic.d.ts +11 -2
  33. package/dist/shared-react/utils/zoom-gesture-logic.d.ts +18 -0
  34. package/dist/shared-svelte/utils/zoom-gesture-logic.d.ts +18 -0
  35. package/dist/shared-vue/utils/zoom-gesture-logic.d.ts +18 -0
  36. package/dist/svelte/components/ZoomGestureWrapper.svelte.d.ts +14 -0
  37. package/dist/svelte/components/index.d.ts +1 -1
  38. package/dist/svelte/hooks/index.d.ts +1 -1
  39. package/dist/svelte/hooks/use-pinch-zoom.svelte.d.ts +11 -2
  40. package/dist/svelte/hooks/use-zoom-gesture.svelte.d.ts +16 -0
  41. package/dist/svelte/index.cjs +1 -1
  42. package/dist/svelte/index.cjs.map +1 -1
  43. package/dist/svelte/index.js +214 -71
  44. package/dist/svelte/index.js.map +1 -1
  45. package/dist/vue/components/index.d.ts +1 -1
  46. package/dist/vue/components/{pinch-wrapper.vue.d.ts → zoom-gesture-wrapper.vue.d.ts} +8 -1
  47. package/dist/vue/hooks/index.d.ts +1 -1
  48. package/dist/vue/hooks/use-zoom-gesture.d.ts +17 -0
  49. package/dist/vue/index.cjs +1 -1
  50. package/dist/vue/index.cjs.map +1 -1
  51. package/dist/vue/index.js +211 -72
  52. package/dist/vue/index.js.map +1 -1
  53. package/package.json +10 -12
  54. package/dist/hammer-DhVzwxwy.cjs +0 -7
  55. package/dist/hammer-DhVzwxwy.cjs.map +0 -1
  56. package/dist/hammer-e1aXHboh.js +0 -1810
  57. package/dist/hammer-e1aXHboh.js.map +0 -1
  58. package/dist/shared/components/pinch-wrapper.d.ts +0 -8
  59. package/dist/shared-preact/components/pinch-wrapper.d.ts +0 -8
  60. package/dist/shared-react/components/pinch-wrapper.d.ts +0 -8
  61. package/dist/shared-svelte/utils/pinch-zoom-logic.d.ts +0 -9
  62. package/dist/shared-vue/utils/pinch-zoom-logic.d.ts +0 -9
  63. package/dist/svelte/components/PinchWrapper.svelte.d.ts +0 -10
  64. package/dist/vue/hooks/use-pinch-zoom.d.ts +0 -8
@@ -1,5 +1,5 @@
1
1
  export { Fragment } from 'preact';
2
- export { useEffect, useRef, useState, useCallback, useMemo } from 'preact/hooks';
2
+ export { useEffect, useRef, useState, useCallback, useMemo, useLayoutEffect, useContext, } from 'preact/hooks';
3
3
  export type { ComponentChildren as ReactNode } from 'preact';
4
4
  export type CSSProperties = import('preact').JSX.CSSProperties;
5
5
  export type HTMLAttributes<T = any> = import('preact').JSX.HTMLAttributes<T extends EventTarget ? T : never>;
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/preact"),t=require("@embedpdf/plugin-zoom");require("preact");const o=require("preact/hooks"),n=require("preact/jsx-runtime"),r=()=>e.useCapability(t.ZoomPlugin.id);function i({element:e,documentId:t,viewportProvides:o,zoomProvides:n}){if("undefined"==typeof window)return()=>{};let r,i=0,s={x:0,y:0};const a=o.forDocument(t),c=n.forDocument(t),u=()=>{e.style.transform="none",e.style.transformOrigin="0 0"},l=t=>{var o;i=c.getState().currentZoomLevel;const n=a.getBoundingRect();s={x:t.center.x-n.origin.x,y:t.center.y-n.origin.y};const r=e.getBoundingClientRect();e.style.transformOrigin=`${t.center.x-r.left}px ${t.center.y-r.top}px`,(null==(o=t.srcEvent)?void 0:o.cancelable)&&(t.srcEvent.preventDefault(),t.srcEvent.stopPropagation())},d=t=>{var o,n;n=t.scale,e.style.transform=`scale(${n})`,(null==(o=t.srcEvent)?void 0:o.cancelable)&&(t.srcEvent.preventDefault(),t.srcEvent.stopPropagation())},p=e=>{const t=(e.scale-1)*i;c.requestZoomBy(t,{vx:s.x,vy:s.y}),u(),i=0};return(async()=>{try{const t=(await Promise.resolve().then(()=>require("../hammer-DhVzwxwy.cjs")).then(e=>e.hammer)).default,o=(()=>{const e="ontouchstart"in window||navigator.maxTouchPoints>0;return e&&/mobile|tablet|ip(ad|hone|od)|android/i.test(navigator.userAgent)?t.TouchInput:e?t.TouchMouseInput:t.MouseInput})();r=new t(e,{touchAction:"pan-x pan-y",inputClass:o}),r.get("pinch").set({enable:!0,pointers:2,threshold:.1}),r.on("pinchstart",l),r.on("pinchmove",d),r.on("pinchend",p)}catch(t){console.warn("Failed to load HammerJS:",t)}})(),()=>{null==r||r.destroy(),u()}}function s(t){const{provides:n}=e.useCapability("viewport"),{provides:s}=r(),a=o.useRef(null);return o.useEffect(()=>{const e=a.current;if(e&&n&&s)return i({element:e,documentId:t,viewportProvides:n,zoomProvides:s})},[n,s,t]),{elementRef:a}}exports.MarqueeZoom=({documentId:t,pageIndex:i,scale:s,className:a,stroke:c="rgba(33,150,243,0.8)",fill:u="rgba(33,150,243,0.15)"})=>{const{provides:l}=r(),d=e.useDocumentState(t),[p,m]=o.useState(null),v=o.useMemo(()=>void 0!==s?s:(null==d?void 0:d.scale)??1,[s,null==d?void 0:d.scale]);return o.useEffect(()=>{if(l)return l.registerMarqueeOnPage({documentId:t,pageIndex:i,scale:v,callback:{onPreview:m}})},[l,t,i,v]),p?n.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:p.origin.x*v,top:p.origin.y*v,width:p.size.width*v,height:p.size.height*v,border:`1px solid ${c}`,background:u,boxSizing:"border-box"},className:a}):null},exports.PinchWrapper=function({children:e,documentId:t,style:o,...r}){const{elementRef:i}=s(t);return n.jsx("div",{ref:i,...r,style:{...o,display:"block",width:"fit-content",overflow:"visible",boxSizing:"border-box",margin:"0px auto"},children:e})},exports.usePinch=s,exports.useZoom=e=>{const{provides:n}=r(),[i,s]=o.useState(t.initialDocumentState);return o.useEffect(()=>{if(!n)return;const t=n.forDocument(e);return s(t.getState()),t.onStateChange(e=>{s(e)})},[n,e]),{state:i,provides:(null==n?void 0:n.forDocument(e))??null}},exports.useZoomCapability=r,exports.useZoomPlugin=()=>e.usePlugin(t.ZoomPlugin.id),Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/preact"),t=require("@embedpdf/plugin-zoom");require("preact");const n=require("preact/hooks"),o=require("@embedpdf/plugin-viewport/preact"),r=require("preact/jsx-runtime"),i=()=>e.useCapability(t.ZoomPlugin.id);function s(e){const[t,n]=[e[0],e[1]],o=n.clientX-t.clientX,r=n.clientY-t.clientY;return Math.hypot(o,r)}function l(t,r={}){const{provides:l}=e.useCapability("viewport"),{provides:c}=i(),u=o.useViewportElement(),a=n.useRef(null);return n.useLayoutEffect(()=>{const e=a.current,n=null==u?void 0:u.current;if(e&&n&&c)return function({element:e,container:t,documentId:n,zoomProvides:o,viewportGap:r=0,options:i={}}){const{enablePinch:l=!0,enableWheel:c=!0}=i;if("undefined"==typeof window)return()=>{};const u=o.forDocument(n),a=()=>u.getState();let d=0,p=1,m=!1,h=0,f=null,v=1,g=0,b=0,x=0,y=0,w=0,E=0,L=0,P=0,M=0,S=0,Z=0,W=0,q=0;const O=(e,t,n)=>Math.min(Math.max(e,t),n),z=()=>{const n=t.clientWidth-2*r,o=e.offsetWidth,i=o<n?(n-o)/2:0;e.style.marginLeft=`${i}px`},D=e=>{const t=g*e,n=b*e;let o=M*(1-e);const r=P-t/2-x,i=S-q*e-x,s=Math.max(0,t-L),l=.3*L,c=Math.min(1,s/l);let u=r+(i-r)*c;return n>E-2*W&&(o=O(y+o,E-W-n,W)-y),t>w-2*W&&(u=O(x+u,w-W-t,W)-x),{tx:u,ty:o,blend:c,finalWidth:t}},j=t=>{p=t;const{tx:n,ty:o}=D(t);e.style.transformOrigin="0 0",e.style.transform=`translate(${n}px, ${o}px) scale(${t})`},C=()=>{e.style.transform="none",e.style.transformOrigin="0 0",p=1},I=()=>{const{tx:e,finalWidth:t}=D(p),n=(p-1)*d;let o,r=Z;if(t<=L)o=P;else{const t=1-p;o=Math.abs(t)>.001?x+e/t:S}u.requestZoomBy(n,{vx:o,vy:r}),C(),d=0},k=(n,o)=>{const i=t.getBoundingClientRect(),s=e.getBoundingClientRect();W=r,g=s.width,b=s.height,x=s.left-i.left,y=s.top-i.top,w=i.width,E=i.height,L=t.clientWidth,P=t.clientLeft+L/2;const l=n-s.left;M=o-s.top,S=n-i.left,Z=o-i.top,q=g<L?S*g/L:l},R=e=>{if(2!==e.touches.length)return;m=!0,d=a().currentZoomLevel,h=s(e.touches);const t=function(e){const[t,n]=[e[0],e[1]];return{x:(t.clientX+n.clientX)/2,y:(t.clientY+n.clientY)/2}}(e.touches);k(t.x,t.y),e.preventDefault()},Y=e=>{if(!m||2!==e.touches.length)return;const t=s(e.touches);j(t/h),e.preventDefault()},G=e=>{m&&(e.touches.length>=2||(m=!1,I()))},X=e=>{if(!e.ctrlKey&&!e.metaKey)return;e.preventDefault(),null===f?(d=a().currentZoomLevel,v=1,k(e.clientX,e.clientY)):clearTimeout(f);const t=1-.01*e.deltaY;v*=t,v=Math.max(.1,Math.min(10,v)),j(v),f=setTimeout(()=>{f=null,I(),v=1},150)},$=u.onStateChange(()=>z()),T=new ResizeObserver(()=>z());return T.observe(e),T.observe(t),z(),l&&(t.addEventListener("touchstart",R,{passive:!1}),t.addEventListener("touchmove",Y,{passive:!1}),t.addEventListener("touchend",G),t.addEventListener("touchcancel",G)),c&&t.addEventListener("wheel",X,{passive:!1}),()=>{l&&(t.removeEventListener("touchstart",R),t.removeEventListener("touchmove",Y),t.removeEventListener("touchend",G),t.removeEventListener("touchcancel",G)),c&&t.removeEventListener("wheel",X),f&&clearTimeout(f),$(),T.disconnect(),C(),e.style.marginLeft=""}}({element:e,container:n,documentId:t,zoomProvides:c,viewportGap:(null==l?void 0:l.getViewportGap())||0,options:r})},[l,c,t,u,r.enablePinch,r.enableWheel]),{elementRef:a}}exports.MarqueeZoom=({documentId:t,pageIndex:o,scale:s,className:l,stroke:c="rgba(33,150,243,0.8)",fill:u="rgba(33,150,243,0.15)"})=>{const{provides:a}=i(),d=e.useDocumentState(t),[p,m]=n.useState(null),h=n.useMemo(()=>void 0!==s?s:(null==d?void 0:d.scale)??1,[s,null==d?void 0:d.scale]);return n.useEffect(()=>{if(a)return a.registerMarqueeOnPage({documentId:t,pageIndex:o,scale:h,callback:{onPreview:m}})},[a,t,o,h]),p?r.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:p.origin.x*h,top:p.origin.y*h,width:p.size.width*h,height:p.size.height*h,border:`1px solid ${c}`,background:u,boxSizing:"border-box"},className:l}):null},exports.ZoomGestureWrapper=function({children:e,documentId:t,style:o,enablePinch:i=!0,enableWheel:s=!0,...c}){const u=n.useMemo(()=>({enablePinch:i,enableWheel:s}),[i,s]),{elementRef:a}=l(t,u);return r.jsx("div",{ref:a,...c,style:{...o,display:"inline-block",overflow:"visible",boxSizing:"border-box"},children:e})},exports.useZoom=e=>{const{provides:o}=i(),[r,s]=n.useState(t.initialDocumentState);return n.useEffect(()=>{if(!o)return;const t=o.forDocument(e);return s(t.getState()),t.onStateChange(e=>{s(e)})},[o,e]),{state:r,provides:(null==o?void 0:o.forDocument(e))??null}},exports.useZoomCapability=i,exports.useZoomGesture=l,exports.useZoomPlugin=()=>e.usePlugin(t.ZoomPlugin.id),Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-zoom.ts","../../src/shared/utils/pinch-zoom-logic.ts","../../src/shared/hooks/use-pinch-zoom.ts","../../src/shared/components/marquee-zoom.tsx","../../src/shared/components/pinch-wrapper.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { initialDocumentState, ZoomDocumentState, ZoomPlugin } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from '@framework';\n\nexport const useZoomCapability = () => useCapability<ZoomPlugin>(ZoomPlugin.id);\nexport const useZoomPlugin = () => usePlugin<ZoomPlugin>(ZoomPlugin.id);\n\n/**\n * Hook for zoom state for a specific document\n * @param documentId Document ID\n */\nexport const useZoom = (documentId: string) => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomDocumentState>(initialDocumentState);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n\n // Get initial state\n setState(scope.getState());\n\n // Subscribe to state changes\n return scope.onStateChange((newState) => {\n setState(newState);\n });\n }, [provides, documentId]);\n\n return {\n state,\n provides: provides?.forDocument(documentId) ?? null,\n };\n};\n","import type { ViewportCapability } from '@embedpdf/plugin-viewport';\nimport type { ZoomCapability } from '@embedpdf/plugin-zoom';\n\nexport interface PinchZoomDeps {\n element: HTMLDivElement;\n documentId: string;\n viewportProvides: ViewportCapability;\n zoomProvides: ZoomCapability;\n}\n\nexport function setupPinchZoom({\n element,\n documentId,\n viewportProvides,\n zoomProvides,\n}: PinchZoomDeps) {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n let hammer: any | undefined;\n let initialZoom = 0;\n let lastCenter = { x: 0, y: 0 };\n\n const viewportScope = viewportProvides.forDocument(documentId);\n const zoomScope = zoomProvides.forDocument(documentId);\n\n const getState = () => zoomScope.getState();\n\n const updateTransform = (scale: number) => {\n element.style.transform = `scale(${scale})`;\n };\n\n const resetTransform = () => {\n element.style.transform = 'none';\n element.style.transformOrigin = '0 0';\n };\n\n const pinchStart = (e: HammerInput) => {\n initialZoom = getState().currentZoomLevel;\n\n const contRect = viewportScope.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchMove = (e: HammerInput) => {\n updateTransform(e.scale);\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n const delta = (e.scale - 1) * initialZoom;\n zoomScope.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n const inputClass = (() => {\n const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;\n const SUPPORT_TOUCH = 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);\n if (SUPPORT_ONLY_TOUCH) return Hammer.TouchInput;\n if (!SUPPORT_TOUCH) return Hammer.MouseInput;\n return Hammer.TouchMouseInput;\n })();\n\n hammer = new Hammer(element, {\n touchAction: 'pan-x pan-y',\n inputClass,\n });\n\n hammer.get('pinch').set({ enable: true, pointers: 2, threshold: 0.1 });\n\n hammer.on('pinchstart', pinchStart);\n hammer.on('pinchmove', pinchMove);\n hammer.on('pinchend', pinchEnd);\n } catch (error) {\n console.warn('Failed to load HammerJS:', error);\n }\n };\n\n setupHammer();\n\n return () => {\n hammer?.destroy();\n resetTransform();\n };\n}\n","import { useEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { setupPinchZoom } from '../utils/pinch-zoom-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch(documentId: string) {\n const { provides: viewportProvides } = useCapability<ViewportPlugin>('viewport');\n const { provides: zoomProvides } = useZoomCapability();\n const elementRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element || !viewportProvides || !zoomProvides) {\n return;\n }\n\n return setupPinchZoom({\n element,\n documentId,\n viewportProvides,\n zoomProvides,\n });\n }, [viewportProvides, zoomProvides, documentId]);\n\n return { elementRef };\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\nimport { useZoomCapability } from '../hooks/use-zoom';\nimport { useDocumentState } from '@embedpdf/core/@framework';\n\ninterface MarqueeZoomProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n className?: string;\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n documentId,\n pageIndex,\n scale: scaleOverride,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n const { provides: zoomPlugin } = useZoomCapability();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px solid ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n }}\n className={className}\n />\n );\n};\n","import { ReactNode, HTMLAttributes, CSSProperties } from '@framework';\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n documentId: string;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, documentId, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch(documentId);\n\n return (\n <div\n ref={elementRef}\n {...props}\n style={{\n ...style,\n display: 'block',\n width: 'fit-content',\n overflow: 'visible',\n boxSizing: 'border-box',\n margin: '0px auto',\n }}\n >\n {children}\n </div>\n );\n}\n"],"names":["useZoomCapability","useCapability","ZoomPlugin","id","setupPinchZoom","element","documentId","viewportProvides","zoomProvides","window","hammer","initialZoom","lastCenter","x","y","viewportScope","forDocument","zoomScope","resetTransform","style","transform","transformOrigin","pinchStart","e","getState","currentZoomLevel","contRect","getBoundingRect","center","origin","innerRect","getBoundingClientRect","left","top","_a","srcEvent","cancelable","preventDefault","stopPropagation","pinchMove","scale","pinchEnd","delta","requestZoomBy","vx","vy","async","Hammer","Promise","resolve","then","require","n","default","inputClass","SUPPORT_TOUCH","navigator","maxTouchPoints","test","userAgent","TouchInput","TouchMouseInput","MouseInput","touchAction","get","set","enable","pointers","threshold","on","error","console","warn","setupHammer","destroy","usePinch","provides","elementRef","useRef","useEffect","current","pageIndex","scaleOverride","className","stroke","fill","zoomPlugin","documentState","useDocumentState","rect","setRect","useState","actualScale","useMemo","registerMarqueeOnPage","callback","onPreview","jsx","position","pointerEvents","width","size","height","border","background","boxSizing","children","props","ref","display","overflow","margin","state","setState","initialDocumentState","scope","onStateChange","newState","usePlugin"],"mappings":"8OAIaA,EAAoB,IAAMC,gBAA0BC,EAAAA,WAAWC,ICMrE,SAASC,GAAeC,QAC7BA,EAAAC,WACAA,EAAAC,iBACAA,EAAAC,aACAA,IAEA,GAAsB,oBAAXC,OACT,MAAO,OAGT,IAAIC,EACAC,EAAc,EACdC,EAAa,CAAEC,EAAG,EAAGC,EAAG,GAE5B,MAAMC,EAAgBR,EAAiBS,YAAYV,GAC7CW,EAAYT,EAAaQ,YAAYV,GAQrCY,EAAiB,KACrBb,EAAQc,MAAMC,UAAY,OAC1Bf,EAAQc,MAAME,gBAAkB,OAG5BC,EAAcC,UAClBZ,EAZqBM,EAAUO,WAYNC,iBAEzB,MAAMC,EAAWX,EAAcY,kBAE/Bf,EAAa,CACXC,EAAGU,EAAEK,OAAOf,EAAIa,EAASG,OAAOhB,EAChCC,EAAGS,EAAEK,OAAOd,EAAIY,EAASG,OAAOf,GAGlC,MAAMgB,EAAYzB,EAAQ0B,wBAC1B1B,EAAQc,MAAME,gBAAkB,GAAGE,EAAEK,OAAOf,EAAIiB,EAAUE,UAAUT,EAAEK,OAAOd,EAAIgB,EAAUG,SAEvF,OAAAC,EAAAX,EAAEY,eAAF,EAAAD,EAAYE,cACdb,EAAEY,SAASE,iBACXd,EAAEY,SAASG,oBAITC,EAAahB,UA5BMiB,IA6BPjB,EAAEiB,MA5BlBnC,EAAQc,MAAMC,UAAY,SAASoB,MA6B/B,OAAAN,EAAAX,EAAEY,eAAF,EAAAD,EAAYE,cACdb,EAAEY,SAASE,iBACXd,EAAEY,SAASG,oBAITG,EAAYlB,IAChB,MAAMmB,GAASnB,EAAEiB,MAAQ,GAAK7B,EAC9BM,EAAU0B,cAAcD,EAAO,CAAEE,GAAIhC,EAAWC,EAAGgC,GAAIjC,EAAWE,IAElEI,IACAP,EAAc,GAiChB,MA9BoBmC,WAClB,IACE,MAAMC,SAAgBC,QAAAC,UAAAC,KAAA,IAAAC,QAAO,2BAAUD,KAAAE,GAAAA,EAAA1C,SAAG2C,QAEpCC,QACJ,MACMC,EAAgB,iBAAkB9C,QAAU+C,UAAUC,eAAiB,EAE7E,OAD2BF,GAFN,wCAEoCG,KAAKF,UAAUG,WACzCZ,EAAOa,WACjCL,EACER,EAAOc,gBADad,EAAOe,UAEpC,KAEApD,EAAS,IAAIqC,EAAO1C,EAAS,CAC3B0D,YAAa,cACbT,eAGF5C,EAAOsD,IAAI,SAASC,IAAI,CAAEC,QAAQ,EAAMC,SAAU,EAAGC,UAAW,KAEhE1D,EAAO2D,GAAG,aAAc/C,GACxBZ,EAAO2D,GAAG,YAAa9B,GACvB7B,EAAO2D,GAAG,WAAY5B,EACxB,OAAS6B,GACPC,QAAQC,KAAK,2BAA4BF,EAC3C,GAGFG,GAEO,KACL,MAAA/D,GAAAA,EAAQgE,UACRxD,IAEJ,CCrGO,SAASyD,EAASrE,GACvB,MAAQsE,SAAUrE,GAAqBN,EAAAA,cAA8B,aAC7D2E,SAAUpE,GAAiBR,IAC7B6E,EAAaC,EAAAA,OAAuB,MAgB1C,OAdAC,EAAAA,UAAU,KACR,MAAM1E,EAAUwE,EAAWG,QAC3B,GAAK3E,GAAYE,GAAqBC,EAItC,OAAOJ,EAAe,CACpBC,UACAC,aACAC,mBACAC,kBAED,CAACD,EAAkBC,EAAcF,IAE7B,CAAEuE,aACX,qBCZ2B,EACzBvE,aACA2E,YACAzC,MAAO0C,EACPC,YACAC,SAAS,uBACTC,OAAO,4BAEP,MAAQT,SAAUU,GAAetF,IAC3BuF,EAAgBC,EAAAA,iBAAiBlF,IAChCmF,EAAMC,GAAWC,EAAAA,SAAsB,MAExCC,EAAcC,EAAAA,QAAQ,SACJ,IAAlBX,EAAoCA,SACjCK,WAAe/C,QAAS,EAC9B,CAAC0C,EAAe,MAAAK,OAAA,EAAAA,EAAe/C,QAclC,OAZAuC,EAAAA,UAAU,KACR,GAAKO,EACL,OAAOA,EAAWQ,sBAAsB,CACtCxF,aACA2E,YACAzC,MAAOoD,EACPG,SAAU,CACRC,UAAWN,MAGd,CAACJ,EAAYhF,EAAY2E,EAAWW,IAElCH,EAGHQ,EAAAA,IAAC,MAAA,CACC9E,MAAO,CACL+E,SAAU,WACVC,cAAe,OACfnE,KAAMyD,EAAK5D,OAAOhB,EAAI+E,EACtB3D,IAAKwD,EAAK5D,OAAOf,EAAI8E,EACrBQ,MAAOX,EAAKY,KAAKD,MAAQR,EACzBU,OAAQb,EAAKY,KAAKC,OAASV,EAC3BW,OAAQ,aAAanB,IACrBoB,WAAYnB,EACZoB,UAAW,cAEbtB,cAfc,2BClCb,UAAsBuB,SAAEA,EAAApG,WAAUA,QAAYa,KAAUwF,IAC7D,MAAM9B,WAAEA,GAAeF,EAASrE,GAEhC,OACE2F,EAAAA,IAAC,MAAA,CACCW,IAAK/B,KACD8B,EACJxF,MAAO,IACFA,EACH0F,QAAS,QACTT,MAAO,cACPU,SAAU,UACVL,UAAW,aACXM,OAAQ,YAGTL,YAGP,qCJjBwBpG,IACtB,MAAMsE,SAAEA,GAAa5E,KACdgH,EAAOC,GAAYtB,EAAAA,SAA4BuB,EAAAA,sBAgBtD,OAdAnC,EAAAA,UAAU,KACR,IAAKH,EAAU,OAEf,MAAMuC,EAAQvC,EAAS5D,YAAYV,GAMnC,OAHA2G,EAASE,EAAM3F,YAGR2F,EAAMC,cAAeC,IAC1BJ,EAASI,MAEV,CAACzC,EAAUtE,IAEP,CACL0G,QACApC,UAAU,MAAAA,OAAA,EAAAA,EAAU5D,YAAYV,KAAe,yDA1BtB,IAAMgH,YAAsBpH,EAAAA,WAAWC"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-zoom.ts","../../src/shared/utils/zoom-gesture-logic.ts","../../src/shared/hooks/use-zoom-gesture.ts","../../src/shared/components/marquee-zoom.tsx","../../src/shared/components/zoom-gesture-wrapper.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { initialDocumentState, ZoomDocumentState, ZoomPlugin } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from '@framework';\n\nexport const useZoomCapability = () => useCapability<ZoomPlugin>(ZoomPlugin.id);\nexport const useZoomPlugin = () => usePlugin<ZoomPlugin>(ZoomPlugin.id);\n\n/**\n * Hook for zoom state for a specific document\n * @param documentId Document ID\n */\nexport const useZoom = (documentId: string) => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomDocumentState>(initialDocumentState);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n\n // Get initial state\n setState(scope.getState());\n\n // Subscribe to state changes\n return scope.onStateChange((newState) => {\n setState(newState);\n });\n }, [provides, documentId]);\n\n return {\n state,\n provides: provides?.forDocument(documentId) ?? null,\n };\n};\n","import type { ZoomCapability } from '@embedpdf/plugin-zoom';\n\nexport interface ZoomGestureOptions {\n /** Enable pinch-to-zoom gesture (default: true) */\n enablePinch?: boolean;\n /** Enable wheel zoom with ctrl/cmd key (default: true) */\n enableWheel?: boolean;\n}\n\nexport interface ZoomGestureDeps {\n element: HTMLDivElement;\n /** Viewport container element for attaching events */\n container: HTMLElement;\n documentId: string;\n zoomProvides: ZoomCapability;\n /** Viewport gap in pixels (default: 0) */\n viewportGap?: number;\n options?: ZoomGestureOptions;\n}\n\nfunction getTouchDistance(touches: TouchList): number {\n const [t1, t2] = [touches[0], touches[1]];\n const dx = t2.clientX - t1.clientX;\n const dy = t2.clientY - t1.clientY;\n return Math.hypot(dx, dy);\n}\n\nfunction getTouchCenter(touches: TouchList): { x: number; y: number } {\n const [t1, t2] = [touches[0], touches[1]];\n return {\n x: (t1.clientX + t2.clientX) / 2,\n y: (t1.clientY + t2.clientY) / 2,\n };\n}\n\nexport function setupZoomGestures({\n element,\n container,\n documentId,\n zoomProvides,\n viewportGap = 0,\n options = {},\n}: ZoomGestureDeps) {\n const { enablePinch = true, enableWheel = true } = options;\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const zoomScope = zoomProvides.forDocument(documentId);\n const getState = () => zoomScope.getState();\n\n // Shared state\n let initialZoom = 0;\n let currentScale = 1;\n let isPinching = false;\n let initialDistance = 0;\n\n // Wheel state\n let wheelZoomTimeout: ReturnType<typeof setTimeout> | null = null;\n let accumulatedWheelScale = 1;\n\n // Gesture state\n let initialElementWidth = 0;\n let initialElementHeight = 0;\n let initialElementLeft = 0;\n let initialElementTop = 0;\n\n // Container Dimensions (Bounding Box)\n let containerRectWidth = 0;\n let containerRectHeight = 0;\n\n // Layout Dimensions (Client Box from Metrics)\n let layoutWidth = 0;\n let layoutCenterX = 0;\n\n let pointerLocalY = 0;\n let pointerContainerX = 0;\n let pointerContainerY = 0;\n\n let currentGap = 0;\n let pivotLocalX = 0;\n\n const clamp = (val: number, min: number, max: number) => Math.min(Math.max(val, min), max);\n\n // --- Margin calculation ---\n const updateMargin = () => {\n const availableWidth = container.clientWidth - 2 * viewportGap;\n const elementWidth = element.offsetWidth;\n\n const newMargin = elementWidth < availableWidth ? (availableWidth - elementWidth) / 2 : 0;\n element.style.marginLeft = `${newMargin}px`;\n };\n\n const calculateTransform = (scale: number) => {\n const finalWidth = initialElementWidth * scale;\n const finalHeight = initialElementHeight * scale;\n\n let ty = pointerLocalY * (1 - scale);\n\n const targetX = layoutCenterX - finalWidth / 2;\n const txCenter = targetX - initialElementLeft;\n const txMouse = pointerContainerX - pivotLocalX * scale - initialElementLeft;\n\n const overflow = Math.max(0, finalWidth - layoutWidth);\n const blendRange = layoutWidth * 0.3;\n const blend = Math.min(1, overflow / blendRange);\n\n let tx = txCenter + (txMouse - txCenter) * blend;\n\n const safeHeight = containerRectHeight - currentGap * 2;\n if (finalHeight > safeHeight) {\n const currentTop = initialElementTop + ty;\n const maxTop = currentGap;\n const minTop = containerRectHeight - currentGap - finalHeight;\n const constrainedTop = clamp(currentTop, minTop, maxTop);\n ty = constrainedTop - initialElementTop;\n }\n\n const safeWidth = containerRectWidth - currentGap * 2;\n if (finalWidth > safeWidth) {\n const currentLeft = initialElementLeft + tx;\n const maxLeft = currentGap;\n const minLeft = containerRectWidth - currentGap - finalWidth;\n const constrainedLeft = clamp(currentLeft, minLeft, maxLeft);\n tx = constrainedLeft - initialElementLeft;\n }\n\n return { tx, ty, blend, finalWidth };\n };\n\n const updateTransform = (scale: number) => {\n currentScale = scale;\n const { tx, ty } = calculateTransform(scale);\n element.style.transformOrigin = '0 0';\n element.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`;\n };\n\n const resetTransform = () => {\n element.style.transform = 'none';\n element.style.transformOrigin = '0 0';\n currentScale = 1;\n };\n\n const commitZoom = () => {\n const { tx, finalWidth } = calculateTransform(currentScale);\n const delta = (currentScale - 1) * initialZoom;\n\n let anchorX: number;\n let anchorY: number = pointerContainerY;\n\n if (finalWidth <= layoutWidth) {\n anchorX = layoutCenterX;\n } else {\n const scaleDiff = 1 - currentScale;\n anchorX =\n Math.abs(scaleDiff) > 0.001 ? initialElementLeft + tx / scaleDiff : pointerContainerX;\n }\n\n zoomScope.requestZoomBy(delta, { vx: anchorX, vy: anchorY });\n resetTransform();\n initialZoom = 0;\n };\n\n const initializeGestureState = (clientX: number, clientY: number) => {\n const containerRect = container.getBoundingClientRect();\n const innerRect = element.getBoundingClientRect();\n\n currentGap = viewportGap;\n initialElementWidth = innerRect.width;\n initialElementHeight = innerRect.height;\n initialElementLeft = innerRect.left - containerRect.left;\n initialElementTop = innerRect.top - containerRect.top;\n\n containerRectWidth = containerRect.width;\n containerRectHeight = containerRect.height;\n\n // Layout dimensions from container's client area\n layoutWidth = container.clientWidth;\n layoutCenterX = container.clientLeft + layoutWidth / 2;\n\n const rawPointerLocalX = clientX - innerRect.left;\n pointerLocalY = clientY - innerRect.top;\n pointerContainerX = clientX - containerRect.left;\n pointerContainerY = clientY - containerRect.top;\n\n if (initialElementWidth < layoutWidth) {\n pivotLocalX = (pointerContainerX * initialElementWidth) / layoutWidth;\n } else {\n pivotLocalX = rawPointerLocalX;\n }\n };\n\n // --- Handlers ---\n const handleTouchStart = (e: TouchEvent) => {\n if (e.touches.length !== 2) return;\n isPinching = true;\n initialZoom = getState().currentZoomLevel;\n initialDistance = getTouchDistance(e.touches);\n const center = getTouchCenter(e.touches);\n initializeGestureState(center.x, center.y);\n e.preventDefault();\n };\n\n const handleTouchMove = (e: TouchEvent) => {\n if (!isPinching || e.touches.length !== 2) return;\n const currentDistance = getTouchDistance(e.touches);\n const scale = currentDistance / initialDistance;\n updateTransform(scale);\n e.preventDefault();\n };\n\n const handleTouchEnd = (e: TouchEvent) => {\n if (!isPinching) return;\n if (e.touches.length >= 2) return;\n isPinching = false;\n commitZoom();\n };\n\n const handleWheel = (e: WheelEvent) => {\n if (!e.ctrlKey && !e.metaKey) return;\n e.preventDefault();\n\n if (wheelZoomTimeout === null) {\n initialZoom = getState().currentZoomLevel;\n accumulatedWheelScale = 1;\n initializeGestureState(e.clientX, e.clientY);\n } else {\n clearTimeout(wheelZoomTimeout);\n }\n\n const zoomFactor = 1 - e.deltaY * 0.01;\n accumulatedWheelScale *= zoomFactor;\n accumulatedWheelScale = Math.max(0.1, Math.min(10, accumulatedWheelScale));\n updateTransform(accumulatedWheelScale);\n\n wheelZoomTimeout = setTimeout(() => {\n wheelZoomTimeout = null;\n commitZoom();\n accumulatedWheelScale = 1;\n }, 150);\n };\n\n // Subscribe to zoom changes to update margin\n const unsubZoom = zoomScope.onStateChange(() => updateMargin());\n\n // Use ResizeObserver to update margin when element or container size changes\n const resizeObserver = new ResizeObserver(() => updateMargin());\n resizeObserver.observe(element);\n resizeObserver.observe(container);\n\n // Initial margin calculation\n updateMargin();\n\n // Attach events to the viewport container for better UX\n // (gestures work anywhere in viewport, not just on the PDF)\n if (enablePinch) {\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n }\n if (enableWheel) {\n container.addEventListener('wheel', handleWheel, { passive: false });\n }\n\n return () => {\n if (enablePinch) {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n }\n if (enableWheel) {\n container.removeEventListener('wheel', handleWheel);\n }\n if (wheelZoomTimeout) {\n clearTimeout(wheelZoomTimeout);\n }\n unsubZoom();\n resizeObserver.disconnect();\n resetTransform();\n element.style.marginLeft = '';\n };\n}\n","import { useLayoutEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin, useViewportElement } from '@embedpdf/plugin-viewport/@framework';\nimport { setupZoomGestures, ZoomGestureOptions } from '../utils/zoom-gesture-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport type { ZoomGestureOptions };\n\nexport function useZoomGesture(documentId: string, options: ZoomGestureOptions = {}) {\n const { provides: viewportProvides } = useCapability<ViewportPlugin>('viewport');\n const { provides: zoomProvides } = useZoomCapability();\n const viewportElementRef = useViewportElement();\n const elementRef = useRef<HTMLDivElement>(null);\n\n // Use useLayoutEffect to set up zoom gestures synchronously before paint\n // This prevents flashing and layout jumps that occur with useEffect\n useLayoutEffect(() => {\n const element = elementRef.current;\n const container = viewportElementRef?.current;\n\n if (!element || !container || !zoomProvides) {\n return;\n }\n\n return setupZoomGestures({\n element,\n container,\n documentId,\n zoomProvides,\n viewportGap: viewportProvides?.getViewportGap() || 0,\n options,\n });\n }, [\n viewportProvides,\n zoomProvides,\n documentId,\n viewportElementRef,\n options.enablePinch,\n options.enableWheel,\n ]);\n\n return { elementRef };\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\nimport { useZoomCapability } from '../hooks/use-zoom';\nimport { useDocumentState } from '@embedpdf/core/@framework';\n\ninterface MarqueeZoomProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n className?: string;\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n documentId,\n pageIndex,\n scale: scaleOverride,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n const { provides: zoomPlugin } = useZoomCapability();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px solid ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n }}\n className={className}\n />\n );\n};\n","import { ReactNode, HTMLAttributes, CSSProperties, useMemo } from '@framework';\nimport { useZoomGesture, ZoomGestureOptions } from '../hooks';\n\ntype ZoomGestureWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n documentId: string;\n style?: CSSProperties;\n /** Enable pinch-to-zoom gesture (default: true) */\n enablePinch?: boolean;\n /** Enable wheel zoom with ctrl/cmd key (default: true) */\n enableWheel?: boolean;\n};\n\nexport function ZoomGestureWrapper({\n children,\n documentId,\n style,\n enablePinch = true,\n enableWheel = true,\n ...props\n}: ZoomGestureWrapperProps) {\n const options = useMemo<ZoomGestureOptions>(\n () => ({ enablePinch, enableWheel }),\n [enablePinch, enableWheel],\n );\n const { elementRef } = useZoomGesture(documentId, options);\n\n return (\n <div\n ref={elementRef}\n {...props}\n style={{\n ...style,\n display: 'inline-block',\n overflow: 'visible',\n boxSizing: 'border-box',\n }}\n >\n {children}\n </div>\n );\n}\n"],"names":["useZoomCapability","useCapability","ZoomPlugin","id","getTouchDistance","touches","t1","t2","dx","clientX","dy","clientY","Math","hypot","useZoomGesture","documentId","options","provides","viewportProvides","zoomProvides","viewportElementRef","useViewportElement","elementRef","useRef","useLayoutEffect","element","current","container","viewportGap","enablePinch","enableWheel","window","zoomScope","forDocument","getState","initialZoom","currentScale","isPinching","initialDistance","wheelZoomTimeout","accumulatedWheelScale","initialElementWidth","initialElementHeight","initialElementLeft","initialElementTop","containerRectWidth","containerRectHeight","layoutWidth","layoutCenterX","pointerLocalY","pointerContainerX","pointerContainerY","currentGap","pivotLocalX","clamp","val","min","max","updateMargin","availableWidth","clientWidth","elementWidth","offsetWidth","newMargin","style","marginLeft","calculateTransform","scale","finalWidth","finalHeight","ty","txCenter","txMouse","overflow","blendRange","blend","tx","updateTransform","transformOrigin","transform","resetTransform","commitZoom","delta","anchorX","anchorY","scaleDiff","abs","requestZoomBy","vx","vy","initializeGestureState","containerRect","getBoundingClientRect","innerRect","width","height","left","top","clientLeft","rawPointerLocalX","handleTouchStart","e","length","currentZoomLevel","center","x","y","getTouchCenter","preventDefault","handleTouchMove","currentDistance","handleTouchEnd","handleWheel","ctrlKey","metaKey","clearTimeout","zoomFactor","deltaY","setTimeout","unsubZoom","onStateChange","resizeObserver","ResizeObserver","observe","addEventListener","passive","removeEventListener","disconnect","setupZoomGestures","getViewportGap","pageIndex","scaleOverride","className","stroke","fill","zoomPlugin","documentState","useDocumentState","rect","setRect","useState","actualScale","useMemo","useEffect","registerMarqueeOnPage","callback","onPreview","jsx","position","pointerEvents","origin","size","border","background","boxSizing","children","props","ref","display","state","setState","initialDocumentState","scope","newState","usePlugin"],"mappings":"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"}
@@ -2,7 +2,8 @@ import { useCapability, usePlugin, useDocumentState } from "@embedpdf/core/preac
2
2
  import { ZoomPlugin, initialDocumentState } from "@embedpdf/plugin-zoom";
3
3
  export * from "@embedpdf/plugin-zoom";
4
4
  import "preact";
5
- import { useState, useEffect, useRef, useMemo } from "preact/hooks";
5
+ import { useState, useEffect, useRef, useLayoutEffect, useMemo } from "preact/hooks";
6
+ import { useViewportElement } from "@embedpdf/plugin-viewport/preact";
6
7
  import { jsx } from "preact/jsx-runtime";
7
8
  const useZoomCapability = () => useCapability(ZoomPlugin.id);
8
9
  const useZoomPlugin = () => usePlugin(ZoomPlugin.id);
@@ -22,107 +23,254 @@ const useZoom = (documentId) => {
22
23
  provides: (provides == null ? void 0 : provides.forDocument(documentId)) ?? null
23
24
  };
24
25
  };
25
- function setupPinchZoom({
26
+ function getTouchDistance(touches) {
27
+ const [t1, t2] = [touches[0], touches[1]];
28
+ const dx = t2.clientX - t1.clientX;
29
+ const dy = t2.clientY - t1.clientY;
30
+ return Math.hypot(dx, dy);
31
+ }
32
+ function getTouchCenter(touches) {
33
+ const [t1, t2] = [touches[0], touches[1]];
34
+ return {
35
+ x: (t1.clientX + t2.clientX) / 2,
36
+ y: (t1.clientY + t2.clientY) / 2
37
+ };
38
+ }
39
+ function setupZoomGestures({
26
40
  element,
41
+ container,
27
42
  documentId,
28
- viewportProvides,
29
- zoomProvides
43
+ zoomProvides,
44
+ viewportGap = 0,
45
+ options = {}
30
46
  }) {
47
+ const { enablePinch = true, enableWheel = true } = options;
31
48
  if (typeof window === "undefined") {
32
49
  return () => {
33
50
  };
34
51
  }
35
- let hammer;
36
- let initialZoom = 0;
37
- let lastCenter = { x: 0, y: 0 };
38
- const viewportScope = viewportProvides.forDocument(documentId);
39
52
  const zoomScope = zoomProvides.forDocument(documentId);
40
53
  const getState = () => zoomScope.getState();
54
+ let initialZoom = 0;
55
+ let currentScale = 1;
56
+ let isPinching = false;
57
+ let initialDistance = 0;
58
+ let wheelZoomTimeout = null;
59
+ let accumulatedWheelScale = 1;
60
+ let initialElementWidth = 0;
61
+ let initialElementHeight = 0;
62
+ let initialElementLeft = 0;
63
+ let initialElementTop = 0;
64
+ let containerRectWidth = 0;
65
+ let containerRectHeight = 0;
66
+ let layoutWidth = 0;
67
+ let layoutCenterX = 0;
68
+ let pointerLocalY = 0;
69
+ let pointerContainerX = 0;
70
+ let pointerContainerY = 0;
71
+ let currentGap = 0;
72
+ let pivotLocalX = 0;
73
+ const clamp = (val, min, max) => Math.min(Math.max(val, min), max);
74
+ const updateMargin = () => {
75
+ const availableWidth = container.clientWidth - 2 * viewportGap;
76
+ const elementWidth = element.offsetWidth;
77
+ const newMargin = elementWidth < availableWidth ? (availableWidth - elementWidth) / 2 : 0;
78
+ element.style.marginLeft = `${newMargin}px`;
79
+ };
80
+ const calculateTransform = (scale) => {
81
+ const finalWidth = initialElementWidth * scale;
82
+ const finalHeight = initialElementHeight * scale;
83
+ let ty = pointerLocalY * (1 - scale);
84
+ const targetX = layoutCenterX - finalWidth / 2;
85
+ const txCenter = targetX - initialElementLeft;
86
+ const txMouse = pointerContainerX - pivotLocalX * scale - initialElementLeft;
87
+ const overflow = Math.max(0, finalWidth - layoutWidth);
88
+ const blendRange = layoutWidth * 0.3;
89
+ const blend = Math.min(1, overflow / blendRange);
90
+ let tx = txCenter + (txMouse - txCenter) * blend;
91
+ const safeHeight = containerRectHeight - currentGap * 2;
92
+ if (finalHeight > safeHeight) {
93
+ const currentTop = initialElementTop + ty;
94
+ const maxTop = currentGap;
95
+ const minTop = containerRectHeight - currentGap - finalHeight;
96
+ const constrainedTop = clamp(currentTop, minTop, maxTop);
97
+ ty = constrainedTop - initialElementTop;
98
+ }
99
+ const safeWidth = containerRectWidth - currentGap * 2;
100
+ if (finalWidth > safeWidth) {
101
+ const currentLeft = initialElementLeft + tx;
102
+ const maxLeft = currentGap;
103
+ const minLeft = containerRectWidth - currentGap - finalWidth;
104
+ const constrainedLeft = clamp(currentLeft, minLeft, maxLeft);
105
+ tx = constrainedLeft - initialElementLeft;
106
+ }
107
+ return { tx, ty, blend, finalWidth };
108
+ };
41
109
  const updateTransform = (scale) => {
42
- element.style.transform = `scale(${scale})`;
110
+ currentScale = scale;
111
+ const { tx, ty } = calculateTransform(scale);
112
+ element.style.transformOrigin = "0 0";
113
+ element.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`;
43
114
  };
44
115
  const resetTransform = () => {
45
116
  element.style.transform = "none";
46
117
  element.style.transformOrigin = "0 0";
118
+ currentScale = 1;
47
119
  };
48
- const pinchStart = (e) => {
49
- var _a;
50
- initialZoom = getState().currentZoomLevel;
51
- const contRect = viewportScope.getBoundingRect();
52
- lastCenter = {
53
- x: e.center.x - contRect.origin.x,
54
- y: e.center.y - contRect.origin.y
55
- };
56
- const innerRect = element.getBoundingClientRect();
57
- element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;
58
- if ((_a = e.srcEvent) == null ? void 0 : _a.cancelable) {
59
- e.srcEvent.preventDefault();
60
- e.srcEvent.stopPropagation();
120
+ const commitZoom = () => {
121
+ const { tx, finalWidth } = calculateTransform(currentScale);
122
+ const delta = (currentScale - 1) * initialZoom;
123
+ let anchorX;
124
+ let anchorY = pointerContainerY;
125
+ if (finalWidth <= layoutWidth) {
126
+ anchorX = layoutCenterX;
127
+ } else {
128
+ const scaleDiff = 1 - currentScale;
129
+ anchorX = Math.abs(scaleDiff) > 1e-3 ? initialElementLeft + tx / scaleDiff : pointerContainerX;
61
130
  }
131
+ zoomScope.requestZoomBy(delta, { vx: anchorX, vy: anchorY });
132
+ resetTransform();
133
+ initialZoom = 0;
62
134
  };
63
- const pinchMove = (e) => {
64
- var _a;
65
- updateTransform(e.scale);
66
- if ((_a = e.srcEvent) == null ? void 0 : _a.cancelable) {
67
- e.srcEvent.preventDefault();
68
- e.srcEvent.stopPropagation();
135
+ const initializeGestureState = (clientX, clientY) => {
136
+ const containerRect = container.getBoundingClientRect();
137
+ const innerRect = element.getBoundingClientRect();
138
+ currentGap = viewportGap;
139
+ initialElementWidth = innerRect.width;
140
+ initialElementHeight = innerRect.height;
141
+ initialElementLeft = innerRect.left - containerRect.left;
142
+ initialElementTop = innerRect.top - containerRect.top;
143
+ containerRectWidth = containerRect.width;
144
+ containerRectHeight = containerRect.height;
145
+ layoutWidth = container.clientWidth;
146
+ layoutCenterX = container.clientLeft + layoutWidth / 2;
147
+ const rawPointerLocalX = clientX - innerRect.left;
148
+ pointerLocalY = clientY - innerRect.top;
149
+ pointerContainerX = clientX - containerRect.left;
150
+ pointerContainerY = clientY - containerRect.top;
151
+ if (initialElementWidth < layoutWidth) {
152
+ pivotLocalX = pointerContainerX * initialElementWidth / layoutWidth;
153
+ } else {
154
+ pivotLocalX = rawPointerLocalX;
69
155
  }
70
156
  };
71
- const pinchEnd = (e) => {
72
- const delta = (e.scale - 1) * initialZoom;
73
- zoomScope.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });
74
- resetTransform();
75
- initialZoom = 0;
157
+ const handleTouchStart = (e) => {
158
+ if (e.touches.length !== 2) return;
159
+ isPinching = true;
160
+ initialZoom = getState().currentZoomLevel;
161
+ initialDistance = getTouchDistance(e.touches);
162
+ const center = getTouchCenter(e.touches);
163
+ initializeGestureState(center.x, center.y);
164
+ e.preventDefault();
76
165
  };
77
- const setupHammer = async () => {
78
- try {
79
- const Hammer = (await import("../hammer-e1aXHboh.js").then((n) => n.h)).default;
80
- const inputClass = (() => {
81
- const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
82
- const SUPPORT_TOUCH = "ontouchstart" in window || navigator.maxTouchPoints > 0;
83
- const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
84
- if (SUPPORT_ONLY_TOUCH) return Hammer.TouchInput;
85
- if (!SUPPORT_TOUCH) return Hammer.MouseInput;
86
- return Hammer.TouchMouseInput;
87
- })();
88
- hammer = new Hammer(element, {
89
- touchAction: "pan-x pan-y",
90
- inputClass
91
- });
92
- hammer.get("pinch").set({ enable: true, pointers: 2, threshold: 0.1 });
93
- hammer.on("pinchstart", pinchStart);
94
- hammer.on("pinchmove", pinchMove);
95
- hammer.on("pinchend", pinchEnd);
96
- } catch (error) {
97
- console.warn("Failed to load HammerJS:", error);
166
+ const handleTouchMove = (e) => {
167
+ if (!isPinching || e.touches.length !== 2) return;
168
+ const currentDistance = getTouchDistance(e.touches);
169
+ const scale = currentDistance / initialDistance;
170
+ updateTransform(scale);
171
+ e.preventDefault();
172
+ };
173
+ const handleTouchEnd = (e) => {
174
+ if (!isPinching) return;
175
+ if (e.touches.length >= 2) return;
176
+ isPinching = false;
177
+ commitZoom();
178
+ };
179
+ const handleWheel = (e) => {
180
+ if (!e.ctrlKey && !e.metaKey) return;
181
+ e.preventDefault();
182
+ if (wheelZoomTimeout === null) {
183
+ initialZoom = getState().currentZoomLevel;
184
+ accumulatedWheelScale = 1;
185
+ initializeGestureState(e.clientX, e.clientY);
186
+ } else {
187
+ clearTimeout(wheelZoomTimeout);
98
188
  }
189
+ const zoomFactor = 1 - e.deltaY * 0.01;
190
+ accumulatedWheelScale *= zoomFactor;
191
+ accumulatedWheelScale = Math.max(0.1, Math.min(10, accumulatedWheelScale));
192
+ updateTransform(accumulatedWheelScale);
193
+ wheelZoomTimeout = setTimeout(() => {
194
+ wheelZoomTimeout = null;
195
+ commitZoom();
196
+ accumulatedWheelScale = 1;
197
+ }, 150);
99
198
  };
100
- setupHammer();
199
+ const unsubZoom = zoomScope.onStateChange(() => updateMargin());
200
+ const resizeObserver = new ResizeObserver(() => updateMargin());
201
+ resizeObserver.observe(element);
202
+ resizeObserver.observe(container);
203
+ updateMargin();
204
+ if (enablePinch) {
205
+ container.addEventListener("touchstart", handleTouchStart, { passive: false });
206
+ container.addEventListener("touchmove", handleTouchMove, { passive: false });
207
+ container.addEventListener("touchend", handleTouchEnd);
208
+ container.addEventListener("touchcancel", handleTouchEnd);
209
+ }
210
+ if (enableWheel) {
211
+ container.addEventListener("wheel", handleWheel, { passive: false });
212
+ }
101
213
  return () => {
102
- hammer == null ? void 0 : hammer.destroy();
214
+ if (enablePinch) {
215
+ container.removeEventListener("touchstart", handleTouchStart);
216
+ container.removeEventListener("touchmove", handleTouchMove);
217
+ container.removeEventListener("touchend", handleTouchEnd);
218
+ container.removeEventListener("touchcancel", handleTouchEnd);
219
+ }
220
+ if (enableWheel) {
221
+ container.removeEventListener("wheel", handleWheel);
222
+ }
223
+ if (wheelZoomTimeout) {
224
+ clearTimeout(wheelZoomTimeout);
225
+ }
226
+ unsubZoom();
227
+ resizeObserver.disconnect();
103
228
  resetTransform();
229
+ element.style.marginLeft = "";
104
230
  };
105
231
  }
106
- function usePinch(documentId) {
232
+ function useZoomGesture(documentId, options = {}) {
107
233
  const { provides: viewportProvides } = useCapability("viewport");
108
234
  const { provides: zoomProvides } = useZoomCapability();
235
+ const viewportElementRef = useViewportElement();
109
236
  const elementRef = useRef(null);
110
- useEffect(() => {
237
+ useLayoutEffect(() => {
111
238
  const element = elementRef.current;
112
- if (!element || !viewportProvides || !zoomProvides) {
239
+ const container = viewportElementRef == null ? void 0 : viewportElementRef.current;
240
+ if (!element || !container || !zoomProvides) {
113
241
  return;
114
242
  }
115
- return setupPinchZoom({
243
+ return setupZoomGestures({
116
244
  element,
245
+ container,
117
246
  documentId,
118
- viewportProvides,
119
- zoomProvides
247
+ zoomProvides,
248
+ viewportGap: (viewportProvides == null ? void 0 : viewportProvides.getViewportGap()) || 0,
249
+ options
120
250
  });
121
- }, [viewportProvides, zoomProvides, documentId]);
251
+ }, [
252
+ viewportProvides,
253
+ zoomProvides,
254
+ documentId,
255
+ viewportElementRef,
256
+ options.enablePinch,
257
+ options.enableWheel
258
+ ]);
122
259
  return { elementRef };
123
260
  }
124
- function PinchWrapper({ children, documentId, style, ...props }) {
125
- const { elementRef } = usePinch(documentId);
261
+ function ZoomGestureWrapper({
262
+ children,
263
+ documentId,
264
+ style,
265
+ enablePinch = true,
266
+ enableWheel = true,
267
+ ...props
268
+ }) {
269
+ const options = useMemo(
270
+ () => ({ enablePinch, enableWheel }),
271
+ [enablePinch, enableWheel]
272
+ );
273
+ const { elementRef } = useZoomGesture(documentId, options);
126
274
  return /* @__PURE__ */ jsx(
127
275
  "div",
128
276
  {
@@ -130,11 +278,9 @@ function PinchWrapper({ children, documentId, style, ...props }) {
130
278
  ...props,
131
279
  style: {
132
280
  ...style,
133
- display: "block",
134
- width: "fit-content",
281
+ display: "inline-block",
135
282
  overflow: "visible",
136
- boxSizing: "border-box",
137
- margin: "0px auto"
283
+ boxSizing: "border-box"
138
284
  },
139
285
  children
140
286
  }
@@ -187,10 +333,10 @@ const MarqueeZoom = ({
187
333
  };
188
334
  export {
189
335
  MarqueeZoom,
190
- PinchWrapper,
191
- usePinch,
336
+ ZoomGestureWrapper,
192
337
  useZoom,
193
338
  useZoomCapability,
339
+ useZoomGesture,
194
340
  useZoomPlugin
195
341
  };
196
342
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-zoom.ts","../../src/shared/utils/pinch-zoom-logic.ts","../../src/shared/hooks/use-pinch-zoom.ts","../../src/shared/components/pinch-wrapper.tsx","../../src/shared/components/marquee-zoom.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { initialDocumentState, ZoomDocumentState, ZoomPlugin } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from '@framework';\n\nexport const useZoomCapability = () => useCapability<ZoomPlugin>(ZoomPlugin.id);\nexport const useZoomPlugin = () => usePlugin<ZoomPlugin>(ZoomPlugin.id);\n\n/**\n * Hook for zoom state for a specific document\n * @param documentId Document ID\n */\nexport const useZoom = (documentId: string) => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomDocumentState>(initialDocumentState);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n\n // Get initial state\n setState(scope.getState());\n\n // Subscribe to state changes\n return scope.onStateChange((newState) => {\n setState(newState);\n });\n }, [provides, documentId]);\n\n return {\n state,\n provides: provides?.forDocument(documentId) ?? null,\n };\n};\n","import type { ViewportCapability } from '@embedpdf/plugin-viewport';\nimport type { ZoomCapability } from '@embedpdf/plugin-zoom';\n\nexport interface PinchZoomDeps {\n element: HTMLDivElement;\n documentId: string;\n viewportProvides: ViewportCapability;\n zoomProvides: ZoomCapability;\n}\n\nexport function setupPinchZoom({\n element,\n documentId,\n viewportProvides,\n zoomProvides,\n}: PinchZoomDeps) {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n let hammer: any | undefined;\n let initialZoom = 0;\n let lastCenter = { x: 0, y: 0 };\n\n const viewportScope = viewportProvides.forDocument(documentId);\n const zoomScope = zoomProvides.forDocument(documentId);\n\n const getState = () => zoomScope.getState();\n\n const updateTransform = (scale: number) => {\n element.style.transform = `scale(${scale})`;\n };\n\n const resetTransform = () => {\n element.style.transform = 'none';\n element.style.transformOrigin = '0 0';\n };\n\n const pinchStart = (e: HammerInput) => {\n initialZoom = getState().currentZoomLevel;\n\n const contRect = viewportScope.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchMove = (e: HammerInput) => {\n updateTransform(e.scale);\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n const delta = (e.scale - 1) * initialZoom;\n zoomScope.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n const inputClass = (() => {\n const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;\n const SUPPORT_TOUCH = 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);\n if (SUPPORT_ONLY_TOUCH) return Hammer.TouchInput;\n if (!SUPPORT_TOUCH) return Hammer.MouseInput;\n return Hammer.TouchMouseInput;\n })();\n\n hammer = new Hammer(element, {\n touchAction: 'pan-x pan-y',\n inputClass,\n });\n\n hammer.get('pinch').set({ enable: true, pointers: 2, threshold: 0.1 });\n\n hammer.on('pinchstart', pinchStart);\n hammer.on('pinchmove', pinchMove);\n hammer.on('pinchend', pinchEnd);\n } catch (error) {\n console.warn('Failed to load HammerJS:', error);\n }\n };\n\n setupHammer();\n\n return () => {\n hammer?.destroy();\n resetTransform();\n };\n}\n","import { useEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { setupPinchZoom } from '../utils/pinch-zoom-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch(documentId: string) {\n const { provides: viewportProvides } = useCapability<ViewportPlugin>('viewport');\n const { provides: zoomProvides } = useZoomCapability();\n const elementRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element || !viewportProvides || !zoomProvides) {\n return;\n }\n\n return setupPinchZoom({\n element,\n documentId,\n viewportProvides,\n zoomProvides,\n });\n }, [viewportProvides, zoomProvides, documentId]);\n\n return { elementRef };\n}\n","import { ReactNode, HTMLAttributes, CSSProperties } from '@framework';\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n documentId: string;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, documentId, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch(documentId);\n\n return (\n <div\n ref={elementRef}\n {...props}\n style={{\n ...style,\n display: 'block',\n width: 'fit-content',\n overflow: 'visible',\n boxSizing: 'border-box',\n margin: '0px auto',\n }}\n >\n {children}\n </div>\n );\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\nimport { useZoomCapability } from '../hooks/use-zoom';\nimport { useDocumentState } from '@embedpdf/core/@framework';\n\ninterface MarqueeZoomProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n className?: string;\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n documentId,\n pageIndex,\n scale: scaleOverride,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n const { provides: zoomPlugin } = useZoomCapability();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px solid ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n }}\n className={className}\n />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-zoom.ts","../../src/shared/utils/zoom-gesture-logic.ts","../../src/shared/hooks/use-zoom-gesture.ts","../../src/shared/components/zoom-gesture-wrapper.tsx","../../src/shared/components/marquee-zoom.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { initialDocumentState, ZoomDocumentState, ZoomPlugin } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from '@framework';\n\nexport const useZoomCapability = () => useCapability<ZoomPlugin>(ZoomPlugin.id);\nexport const useZoomPlugin = () => usePlugin<ZoomPlugin>(ZoomPlugin.id);\n\n/**\n * Hook for zoom state for a specific document\n * @param documentId Document ID\n */\nexport const useZoom = (documentId: string) => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomDocumentState>(initialDocumentState);\n\n useEffect(() => {\n if (!provides) return;\n\n const scope = provides.forDocument(documentId);\n\n // Get initial state\n setState(scope.getState());\n\n // Subscribe to state changes\n return scope.onStateChange((newState) => {\n setState(newState);\n });\n }, [provides, documentId]);\n\n return {\n state,\n provides: provides?.forDocument(documentId) ?? null,\n };\n};\n","import type { ZoomCapability } from '@embedpdf/plugin-zoom';\n\nexport interface ZoomGestureOptions {\n /** Enable pinch-to-zoom gesture (default: true) */\n enablePinch?: boolean;\n /** Enable wheel zoom with ctrl/cmd key (default: true) */\n enableWheel?: boolean;\n}\n\nexport interface ZoomGestureDeps {\n element: HTMLDivElement;\n /** Viewport container element for attaching events */\n container: HTMLElement;\n documentId: string;\n zoomProvides: ZoomCapability;\n /** Viewport gap in pixels (default: 0) */\n viewportGap?: number;\n options?: ZoomGestureOptions;\n}\n\nfunction getTouchDistance(touches: TouchList): number {\n const [t1, t2] = [touches[0], touches[1]];\n const dx = t2.clientX - t1.clientX;\n const dy = t2.clientY - t1.clientY;\n return Math.hypot(dx, dy);\n}\n\nfunction getTouchCenter(touches: TouchList): { x: number; y: number } {\n const [t1, t2] = [touches[0], touches[1]];\n return {\n x: (t1.clientX + t2.clientX) / 2,\n y: (t1.clientY + t2.clientY) / 2,\n };\n}\n\nexport function setupZoomGestures({\n element,\n container,\n documentId,\n zoomProvides,\n viewportGap = 0,\n options = {},\n}: ZoomGestureDeps) {\n const { enablePinch = true, enableWheel = true } = options;\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const zoomScope = zoomProvides.forDocument(documentId);\n const getState = () => zoomScope.getState();\n\n // Shared state\n let initialZoom = 0;\n let currentScale = 1;\n let isPinching = false;\n let initialDistance = 0;\n\n // Wheel state\n let wheelZoomTimeout: ReturnType<typeof setTimeout> | null = null;\n let accumulatedWheelScale = 1;\n\n // Gesture state\n let initialElementWidth = 0;\n let initialElementHeight = 0;\n let initialElementLeft = 0;\n let initialElementTop = 0;\n\n // Container Dimensions (Bounding Box)\n let containerRectWidth = 0;\n let containerRectHeight = 0;\n\n // Layout Dimensions (Client Box from Metrics)\n let layoutWidth = 0;\n let layoutCenterX = 0;\n\n let pointerLocalY = 0;\n let pointerContainerX = 0;\n let pointerContainerY = 0;\n\n let currentGap = 0;\n let pivotLocalX = 0;\n\n const clamp = (val: number, min: number, max: number) => Math.min(Math.max(val, min), max);\n\n // --- Margin calculation ---\n const updateMargin = () => {\n const availableWidth = container.clientWidth - 2 * viewportGap;\n const elementWidth = element.offsetWidth;\n\n const newMargin = elementWidth < availableWidth ? (availableWidth - elementWidth) / 2 : 0;\n element.style.marginLeft = `${newMargin}px`;\n };\n\n const calculateTransform = (scale: number) => {\n const finalWidth = initialElementWidth * scale;\n const finalHeight = initialElementHeight * scale;\n\n let ty = pointerLocalY * (1 - scale);\n\n const targetX = layoutCenterX - finalWidth / 2;\n const txCenter = targetX - initialElementLeft;\n const txMouse = pointerContainerX - pivotLocalX * scale - initialElementLeft;\n\n const overflow = Math.max(0, finalWidth - layoutWidth);\n const blendRange = layoutWidth * 0.3;\n const blend = Math.min(1, overflow / blendRange);\n\n let tx = txCenter + (txMouse - txCenter) * blend;\n\n const safeHeight = containerRectHeight - currentGap * 2;\n if (finalHeight > safeHeight) {\n const currentTop = initialElementTop + ty;\n const maxTop = currentGap;\n const minTop = containerRectHeight - currentGap - finalHeight;\n const constrainedTop = clamp(currentTop, minTop, maxTop);\n ty = constrainedTop - initialElementTop;\n }\n\n const safeWidth = containerRectWidth - currentGap * 2;\n if (finalWidth > safeWidth) {\n const currentLeft = initialElementLeft + tx;\n const maxLeft = currentGap;\n const minLeft = containerRectWidth - currentGap - finalWidth;\n const constrainedLeft = clamp(currentLeft, minLeft, maxLeft);\n tx = constrainedLeft - initialElementLeft;\n }\n\n return { tx, ty, blend, finalWidth };\n };\n\n const updateTransform = (scale: number) => {\n currentScale = scale;\n const { tx, ty } = calculateTransform(scale);\n element.style.transformOrigin = '0 0';\n element.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`;\n };\n\n const resetTransform = () => {\n element.style.transform = 'none';\n element.style.transformOrigin = '0 0';\n currentScale = 1;\n };\n\n const commitZoom = () => {\n const { tx, finalWidth } = calculateTransform(currentScale);\n const delta = (currentScale - 1) * initialZoom;\n\n let anchorX: number;\n let anchorY: number = pointerContainerY;\n\n if (finalWidth <= layoutWidth) {\n anchorX = layoutCenterX;\n } else {\n const scaleDiff = 1 - currentScale;\n anchorX =\n Math.abs(scaleDiff) > 0.001 ? initialElementLeft + tx / scaleDiff : pointerContainerX;\n }\n\n zoomScope.requestZoomBy(delta, { vx: anchorX, vy: anchorY });\n resetTransform();\n initialZoom = 0;\n };\n\n const initializeGestureState = (clientX: number, clientY: number) => {\n const containerRect = container.getBoundingClientRect();\n const innerRect = element.getBoundingClientRect();\n\n currentGap = viewportGap;\n initialElementWidth = innerRect.width;\n initialElementHeight = innerRect.height;\n initialElementLeft = innerRect.left - containerRect.left;\n initialElementTop = innerRect.top - containerRect.top;\n\n containerRectWidth = containerRect.width;\n containerRectHeight = containerRect.height;\n\n // Layout dimensions from container's client area\n layoutWidth = container.clientWidth;\n layoutCenterX = container.clientLeft + layoutWidth / 2;\n\n const rawPointerLocalX = clientX - innerRect.left;\n pointerLocalY = clientY - innerRect.top;\n pointerContainerX = clientX - containerRect.left;\n pointerContainerY = clientY - containerRect.top;\n\n if (initialElementWidth < layoutWidth) {\n pivotLocalX = (pointerContainerX * initialElementWidth) / layoutWidth;\n } else {\n pivotLocalX = rawPointerLocalX;\n }\n };\n\n // --- Handlers ---\n const handleTouchStart = (e: TouchEvent) => {\n if (e.touches.length !== 2) return;\n isPinching = true;\n initialZoom = getState().currentZoomLevel;\n initialDistance = getTouchDistance(e.touches);\n const center = getTouchCenter(e.touches);\n initializeGestureState(center.x, center.y);\n e.preventDefault();\n };\n\n const handleTouchMove = (e: TouchEvent) => {\n if (!isPinching || e.touches.length !== 2) return;\n const currentDistance = getTouchDistance(e.touches);\n const scale = currentDistance / initialDistance;\n updateTransform(scale);\n e.preventDefault();\n };\n\n const handleTouchEnd = (e: TouchEvent) => {\n if (!isPinching) return;\n if (e.touches.length >= 2) return;\n isPinching = false;\n commitZoom();\n };\n\n const handleWheel = (e: WheelEvent) => {\n if (!e.ctrlKey && !e.metaKey) return;\n e.preventDefault();\n\n if (wheelZoomTimeout === null) {\n initialZoom = getState().currentZoomLevel;\n accumulatedWheelScale = 1;\n initializeGestureState(e.clientX, e.clientY);\n } else {\n clearTimeout(wheelZoomTimeout);\n }\n\n const zoomFactor = 1 - e.deltaY * 0.01;\n accumulatedWheelScale *= zoomFactor;\n accumulatedWheelScale = Math.max(0.1, Math.min(10, accumulatedWheelScale));\n updateTransform(accumulatedWheelScale);\n\n wheelZoomTimeout = setTimeout(() => {\n wheelZoomTimeout = null;\n commitZoom();\n accumulatedWheelScale = 1;\n }, 150);\n };\n\n // Subscribe to zoom changes to update margin\n const unsubZoom = zoomScope.onStateChange(() => updateMargin());\n\n // Use ResizeObserver to update margin when element or container size changes\n const resizeObserver = new ResizeObserver(() => updateMargin());\n resizeObserver.observe(element);\n resizeObserver.observe(container);\n\n // Initial margin calculation\n updateMargin();\n\n // Attach events to the viewport container for better UX\n // (gestures work anywhere in viewport, not just on the PDF)\n if (enablePinch) {\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n }\n if (enableWheel) {\n container.addEventListener('wheel', handleWheel, { passive: false });\n }\n\n return () => {\n if (enablePinch) {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n }\n if (enableWheel) {\n container.removeEventListener('wheel', handleWheel);\n }\n if (wheelZoomTimeout) {\n clearTimeout(wheelZoomTimeout);\n }\n unsubZoom();\n resizeObserver.disconnect();\n resetTransform();\n element.style.marginLeft = '';\n };\n}\n","import { useLayoutEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin, useViewportElement } from '@embedpdf/plugin-viewport/@framework';\nimport { setupZoomGestures, ZoomGestureOptions } from '../utils/zoom-gesture-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport type { ZoomGestureOptions };\n\nexport function useZoomGesture(documentId: string, options: ZoomGestureOptions = {}) {\n const { provides: viewportProvides } = useCapability<ViewportPlugin>('viewport');\n const { provides: zoomProvides } = useZoomCapability();\n const viewportElementRef = useViewportElement();\n const elementRef = useRef<HTMLDivElement>(null);\n\n // Use useLayoutEffect to set up zoom gestures synchronously before paint\n // This prevents flashing and layout jumps that occur with useEffect\n useLayoutEffect(() => {\n const element = elementRef.current;\n const container = viewportElementRef?.current;\n\n if (!element || !container || !zoomProvides) {\n return;\n }\n\n return setupZoomGestures({\n element,\n container,\n documentId,\n zoomProvides,\n viewportGap: viewportProvides?.getViewportGap() || 0,\n options,\n });\n }, [\n viewportProvides,\n zoomProvides,\n documentId,\n viewportElementRef,\n options.enablePinch,\n options.enableWheel,\n ]);\n\n return { elementRef };\n}\n","import { ReactNode, HTMLAttributes, CSSProperties, useMemo } from '@framework';\nimport { useZoomGesture, ZoomGestureOptions } from '../hooks';\n\ntype ZoomGestureWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n documentId: string;\n style?: CSSProperties;\n /** Enable pinch-to-zoom gesture (default: true) */\n enablePinch?: boolean;\n /** Enable wheel zoom with ctrl/cmd key (default: true) */\n enableWheel?: boolean;\n};\n\nexport function ZoomGestureWrapper({\n children,\n documentId,\n style,\n enablePinch = true,\n enableWheel = true,\n ...props\n}: ZoomGestureWrapperProps) {\n const options = useMemo<ZoomGestureOptions>(\n () => ({ enablePinch, enableWheel }),\n [enablePinch, enableWheel],\n );\n const { elementRef } = useZoomGesture(documentId, options);\n\n return (\n <div\n ref={elementRef}\n {...props}\n style={{\n ...style,\n display: 'inline-block',\n overflow: 'visible',\n boxSizing: 'border-box',\n }}\n >\n {children}\n </div>\n );\n}\n","import { useEffect, useMemo, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\nimport { useZoomCapability } from '../hooks/use-zoom';\nimport { useDocumentState } from '@embedpdf/core/@framework';\n\ninterface MarqueeZoomProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n className?: string;\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n documentId,\n pageIndex,\n scale: scaleOverride,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n const { provides: zoomPlugin } = useZoomCapability();\n const documentState = useDocumentState(documentId);\n const [rect, setRect] = useState<Rect | null>(null);\n\n const actualScale = useMemo(() => {\n if (scaleOverride !== undefined) return scaleOverride;\n return documentState?.scale ?? 1;\n }, [scaleOverride, documentState?.scale]);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, documentId, pageIndex, actualScale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * actualScale,\n top: rect.origin.y * actualScale,\n width: rect.size.width * actualScale,\n height: rect.size.height * actualScale,\n border: `1px solid ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n }}\n className={className}\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from '@embedpdf/plugin-viewport/preact';
@@ -1,2 +1,2 @@
1
- export { Fragment, useEffect, useRef, useState, useCallback, useMemo } from 'react';
1
+ export { Fragment, useEffect, useRef, useState, useCallback, useMemo, useLayoutEffect, useContext, } from 'react';
2
2
  export type { ReactNode, HTMLAttributes, CSSProperties } from 'react';
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),t=require("@embedpdf/plugin-zoom"),o=require("react"),n=require("react/jsx-runtime"),r=()=>e.useCapability(t.ZoomPlugin.id);function i({element:e,documentId:t,viewportProvides:o,zoomProvides:n}){if("undefined"==typeof window)return()=>{};let r,i=0,s={x:0,y:0};const a=o.forDocument(t),c=n.forDocument(t),u=()=>{e.style.transform="none",e.style.transformOrigin="0 0"},l=t=>{var o;i=c.getState().currentZoomLevel;const n=a.getBoundingRect();s={x:t.center.x-n.origin.x,y:t.center.y-n.origin.y};const r=e.getBoundingClientRect();e.style.transformOrigin=`${t.center.x-r.left}px ${t.center.y-r.top}px`,(null==(o=t.srcEvent)?void 0:o.cancelable)&&(t.srcEvent.preventDefault(),t.srcEvent.stopPropagation())},d=t=>{var o,n;n=t.scale,e.style.transform=`scale(${n})`,(null==(o=t.srcEvent)?void 0:o.cancelable)&&(t.srcEvent.preventDefault(),t.srcEvent.stopPropagation())},p=e=>{const t=(e.scale-1)*i;c.requestZoomBy(t,{vx:s.x,vy:s.y}),u(),i=0};return(async()=>{try{const t=(await Promise.resolve().then(()=>require("../hammer-DhVzwxwy.cjs")).then(e=>e.hammer)).default,o=(()=>{const e="ontouchstart"in window||navigator.maxTouchPoints>0;return e&&/mobile|tablet|ip(ad|hone|od)|android/i.test(navigator.userAgent)?t.TouchInput:e?t.TouchMouseInput:t.MouseInput})();r=new t(e,{touchAction:"pan-x pan-y",inputClass:o}),r.get("pinch").set({enable:!0,pointers:2,threshold:.1}),r.on("pinchstart",l),r.on("pinchmove",d),r.on("pinchend",p)}catch(t){console.warn("Failed to load HammerJS:",t)}})(),()=>{null==r||r.destroy(),u()}}function s(t){const{provides:n}=e.useCapability("viewport"),{provides:s}=r(),a=o.useRef(null);return o.useEffect(()=>{const e=a.current;if(e&&n&&s)return i({element:e,documentId:t,viewportProvides:n,zoomProvides:s})},[n,s,t]),{elementRef:a}}exports.MarqueeZoom=({documentId:t,pageIndex:i,scale:s,className:a,stroke:c="rgba(33,150,243,0.8)",fill:u="rgba(33,150,243,0.15)"})=>{const{provides:l}=r(),d=e.useDocumentState(t),[p,m]=o.useState(null),v=o.useMemo(()=>void 0!==s?s:(null==d?void 0:d.scale)??1,[s,null==d?void 0:d.scale]);return o.useEffect(()=>{if(l)return l.registerMarqueeOnPage({documentId:t,pageIndex:i,scale:v,callback:{onPreview:m}})},[l,t,i,v]),p?n.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:p.origin.x*v,top:p.origin.y*v,width:p.size.width*v,height:p.size.height*v,border:`1px solid ${c}`,background:u,boxSizing:"border-box"},className:a}):null},exports.PinchWrapper=function({children:e,documentId:t,style:o,...r}){const{elementRef:i}=s(t);return n.jsx("div",{ref:i,...r,style:{...o,display:"block",width:"fit-content",overflow:"visible",boxSizing:"border-box",margin:"0px auto"},children:e})},exports.usePinch=s,exports.useZoom=e=>{const{provides:n}=r(),[i,s]=o.useState(t.initialDocumentState);return o.useEffect(()=>{if(!n)return;const t=n.forDocument(e);return s(t.getState()),t.onStateChange(e=>{s(e)})},[n,e]),{state:i,provides:(null==n?void 0:n.forDocument(e))??null}},exports.useZoomCapability=r,exports.useZoomPlugin=()=>e.usePlugin(t.ZoomPlugin.id),Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),t=require("@embedpdf/plugin-zoom"),n=require("react"),o=require("@embedpdf/plugin-viewport/react"),r=require("react/jsx-runtime"),i=()=>e.useCapability(t.ZoomPlugin.id);function s(e){const[t,n]=[e[0],e[1]],o=n.clientX-t.clientX,r=n.clientY-t.clientY;return Math.hypot(o,r)}function l(t,r={}){const{provides:l}=e.useCapability("viewport"),{provides:c}=i(),u=o.useViewportElement(),a=n.useRef(null);return n.useLayoutEffect(()=>{const e=a.current,n=null==u?void 0:u.current;if(e&&n&&c)return function({element:e,container:t,documentId:n,zoomProvides:o,viewportGap:r=0,options:i={}}){const{enablePinch:l=!0,enableWheel:c=!0}=i;if("undefined"==typeof window)return()=>{};const u=o.forDocument(n),a=()=>u.getState();let d=0,m=1,p=!1,h=0,f=null,v=1,g=0,b=0,x=0,y=0,w=0,E=0,L=0,P=0,M=0,S=0,Z=0,W=0,O=0;const q=(e,t,n)=>Math.min(Math.max(e,t),n),z=()=>{const n=t.clientWidth-2*r,o=e.offsetWidth,i=o<n?(n-o)/2:0;e.style.marginLeft=`${i}px`},D=e=>{const t=g*e,n=b*e;let o=M*(1-e);const r=P-t/2-x,i=S-O*e-x,s=Math.max(0,t-L),l=.3*L,c=Math.min(1,s/l);let u=r+(i-r)*c;return n>E-2*W&&(o=q(y+o,E-W-n,W)-y),t>w-2*W&&(u=q(x+u,w-W-t,W)-x),{tx:u,ty:o,blend:c,finalWidth:t}},j=t=>{m=t;const{tx:n,ty:o}=D(t);e.style.transformOrigin="0 0",e.style.transform=`translate(${n}px, ${o}px) scale(${t})`},C=()=>{e.style.transform="none",e.style.transformOrigin="0 0",m=1},I=()=>{const{tx:e,finalWidth:t}=D(m),n=(m-1)*d;let o,r=Z;if(t<=L)o=P;else{const t=1-m;o=Math.abs(t)>.001?x+e/t:S}u.requestZoomBy(n,{vx:o,vy:r}),C(),d=0},R=(n,o)=>{const i=t.getBoundingClientRect(),s=e.getBoundingClientRect();W=r,g=s.width,b=s.height,x=s.left-i.left,y=s.top-i.top,w=i.width,E=i.height,L=t.clientWidth,P=t.clientLeft+L/2;const l=n-s.left;M=o-s.top,S=n-i.left,Z=o-i.top,O=g<L?S*g/L:l},Y=e=>{if(2!==e.touches.length)return;p=!0,d=a().currentZoomLevel,h=s(e.touches);const t=function(e){const[t,n]=[e[0],e[1]];return{x:(t.clientX+n.clientX)/2,y:(t.clientY+n.clientY)/2}}(e.touches);R(t.x,t.y),e.preventDefault()},k=e=>{if(!p||2!==e.touches.length)return;const t=s(e.touches);j(t/h),e.preventDefault()},G=e=>{p&&(e.touches.length>=2||(p=!1,I()))},X=e=>{if(!e.ctrlKey&&!e.metaKey)return;e.preventDefault(),null===f?(d=a().currentZoomLevel,v=1,R(e.clientX,e.clientY)):clearTimeout(f);const t=1-.01*e.deltaY;v*=t,v=Math.max(.1,Math.min(10,v)),j(v),f=setTimeout(()=>{f=null,I(),v=1},150)},$=u.onStateChange(()=>z()),T=new ResizeObserver(()=>z());return T.observe(e),T.observe(t),z(),l&&(t.addEventListener("touchstart",Y,{passive:!1}),t.addEventListener("touchmove",k,{passive:!1}),t.addEventListener("touchend",G),t.addEventListener("touchcancel",G)),c&&t.addEventListener("wheel",X,{passive:!1}),()=>{l&&(t.removeEventListener("touchstart",Y),t.removeEventListener("touchmove",k),t.removeEventListener("touchend",G),t.removeEventListener("touchcancel",G)),c&&t.removeEventListener("wheel",X),f&&clearTimeout(f),$(),T.disconnect(),C(),e.style.marginLeft=""}}({element:e,container:n,documentId:t,zoomProvides:c,viewportGap:(null==l?void 0:l.getViewportGap())||0,options:r})},[l,c,t,u,r.enablePinch,r.enableWheel]),{elementRef:a}}exports.MarqueeZoom=({documentId:t,pageIndex:o,scale:s,className:l,stroke:c="rgba(33,150,243,0.8)",fill:u="rgba(33,150,243,0.15)"})=>{const{provides:a}=i(),d=e.useDocumentState(t),[m,p]=n.useState(null),h=n.useMemo(()=>void 0!==s?s:(null==d?void 0:d.scale)??1,[s,null==d?void 0:d.scale]);return n.useEffect(()=>{if(a)return a.registerMarqueeOnPage({documentId:t,pageIndex:o,scale:h,callback:{onPreview:p}})},[a,t,o,h]),m?r.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:m.origin.x*h,top:m.origin.y*h,width:m.size.width*h,height:m.size.height*h,border:`1px solid ${c}`,background:u,boxSizing:"border-box"},className:l}):null},exports.ZoomGestureWrapper=function({children:e,documentId:t,style:o,enablePinch:i=!0,enableWheel:s=!0,...c}){const u=n.useMemo(()=>({enablePinch:i,enableWheel:s}),[i,s]),{elementRef:a}=l(t,u);return r.jsx("div",{ref:a,...c,style:{...o,display:"inline-block",overflow:"visible",boxSizing:"border-box"},children:e})},exports.useZoom=e=>{const{provides:o}=i(),[r,s]=n.useState(t.initialDocumentState);return n.useEffect(()=>{if(!o)return;const t=o.forDocument(e);return s(t.getState()),t.onStateChange(e=>{s(e)})},[o,e]),{state:r,provides:(null==o?void 0:o.forDocument(e))??null}},exports.useZoomCapability=i,exports.useZoomGesture=l,exports.useZoomPlugin=()=>e.usePlugin(t.ZoomPlugin.id),Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map