@embedpdf/plugin-zoom 1.0.19 → 1.0.21

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 (37) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +106 -10
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/handlers/index.d.ts +1 -0
  6. package/dist/lib/handlers/marquee-zoom.handler.d.ts +10 -0
  7. package/dist/lib/types.d.ts +12 -0
  8. package/dist/lib/zoom-plugin.d.ts +5 -1
  9. package/dist/preact/index.cjs +1 -1
  10. package/dist/preact/index.cjs.map +1 -1
  11. package/dist/preact/index.js +88 -128
  12. package/dist/preact/index.js.map +1 -1
  13. package/dist/react/index.cjs +1 -1
  14. package/dist/react/index.cjs.map +1 -1
  15. package/dist/react/index.js +88 -128
  16. package/dist/react/index.js.map +1 -1
  17. package/dist/shared-preact/components/marquee-zoom.d.ts +1 -9
  18. package/dist/shared-preact/index.d.ts +1 -0
  19. package/dist/shared-preact/utils/pinch-zoom-logic.d.ts +8 -0
  20. package/dist/shared-react/components/marquee-zoom.d.ts +1 -9
  21. package/dist/shared-react/index.d.ts +1 -0
  22. package/dist/shared-react/utils/pinch-zoom-logic.d.ts +8 -0
  23. package/dist/shared-vue/utils/pinch-zoom-logic.d.ts +8 -0
  24. package/dist/vue/components/index.d.ts +2 -0
  25. package/dist/vue/components/marquee-zoom.vue.d.ts +16 -0
  26. package/dist/vue/components/pinch-wrapper.vue.d.ts +12 -0
  27. package/dist/vue/hooks/index.d.ts +2 -0
  28. package/dist/vue/hooks/use-pinch-zoom.d.ts +3 -0
  29. package/dist/vue/hooks/use-zoom.d.ts +13 -0
  30. package/dist/vue/index.cjs +2 -0
  31. package/dist/vue/index.cjs.map +1 -0
  32. package/dist/vue/index.d.ts +3 -0
  33. package/dist/vue/index.js +197 -0
  34. package/dist/vue/index.js.map +1 -0
  35. package/package.json +18 -11
  36. package/dist/preact/interaction-manager.d.ts +0 -1
  37. package/dist/react/interaction-manager.d.ts +0 -1
@@ -25,6 +25,16 @@ export interface ZoomChangeEvent {
25
25
  /** metrics at the moment the zoom was requested */
26
26
  viewport: ViewportMetrics;
27
27
  }
28
+ export interface MarqueeZoomCallback {
29
+ onPreview?: (rect: Rect | null) => void;
30
+ onCommit?: (rect: Rect) => void;
31
+ onSmallDrag?: () => void;
32
+ }
33
+ export interface RegisterMarqueeOnPageOptions {
34
+ pageIndex: number;
35
+ scale: number;
36
+ callback: MarqueeZoomCallback;
37
+ }
28
38
  export interface ZoomCapability {
29
39
  /** subscribe – returns the unsubscribe function */
30
40
  onZoomChange: EventHook<ZoomChangeEvent>;
@@ -43,6 +53,8 @@ export interface ZoomCapability {
43
53
  disableMarqueeZoom(): void;
44
54
  toggleMarqueeZoom(): void;
45
55
  isMarqueeZoomActive(): boolean;
56
+ /** register a marquee handler on a page -------------------------------- */
57
+ registerMarqueeOnPage: (opts: RegisterMarqueeOnPageOptions) => () => void;
46
58
  getState(): ZoomState;
47
59
  getPresets(): ZoomPreset[];
48
60
  }
@@ -1,6 +1,6 @@
1
1
  import { BasePlugin, PluginRegistry } from '@embedpdf/core';
2
2
  import { ZoomAction } from './actions';
3
- import { ZoomPluginConfig, ZoomState, ZoomCapability } from './types';
3
+ import { ZoomPluginConfig, ZoomState, ZoomCapability, RegisterMarqueeOnPageOptions } from './types';
4
4
  export declare class ZoomPlugin extends BasePlugin<ZoomPluginConfig, ZoomCapability, ZoomState, ZoomAction> {
5
5
  static readonly id: "zoom";
6
6
  private readonly zoom$;
@@ -16,6 +16,9 @@ export declare class ZoomPlugin extends BasePlugin<ZoomPluginConfig, ZoomCapabil
16
16
  private readonly zoomStep;
17
17
  constructor(id: string, registry: PluginRegistry, cfg: ZoomPluginConfig);
18
18
  protected buildCapability(): ZoomCapability;
19
+ private zoomOut;
20
+ private zoomIn;
21
+ private zoomToArea;
19
22
  initialize(): Promise<void>;
20
23
  destroy(): Promise<void>;
21
24
  /**
@@ -35,4 +38,5 @@ export declare class ZoomPlugin extends BasePlugin<ZoomPluginConfig, ZoomCapabil
35
38
  /** recalculates Automatic / Fit* when viewport or pages change */
36
39
  private recalcAuto;
37
40
  onStoreUpdated(_prevState: ZoomState, newState: ZoomState): void;
41
+ registerMarqueeOnPage(opts: RegisterMarqueeOnPageOptions): () => void;
38
42
  }
@@ -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 n=require("preact/hooks"),r=require("preact/jsx-runtime"),o=require("@embedpdf/plugin-interaction-manager/preact"),i=()=>e.useCapability(t.ZoomPlugin.id);function a(){const{provides:t}=e.useCapability("viewport"),{provides:r}=i(),o=n.useRef(null);return n.useEffect((()=>{const e=o.current;if(!e||!t||!r)return;if("undefined"==typeof window)return;let n,i=0,a={x:0,y:0};const s=()=>{e.style.transform="none",e.style.transformOrigin="0 0"},l=n=>{var o;i=r.getState().currentZoomLevel;const s=t.getBoundingRect();a={x:n.center.x-s.origin.x,y:n.center.y-s.origin.y};const l=e.getBoundingClientRect();e.style.transformOrigin=`${n.center.x-l.left}px ${n.center.y-l.top}px`,(null==(o=n.srcEvent)?void 0:o.cancelable)&&(n.srcEvent.preventDefault(),n.srcEvent.stopPropagation())},u=t=>{var n,r;r=t.scale,e.style.transform=`scale(${r})`,(null==(n=t.srcEvent)?void 0:n.cancelable)&&(t.srcEvent.preventDefault(),t.srcEvent.stopPropagation())},c=e=>{const t=(e.scale-1)*i;r.requestZoomBy(t,{vx:a.x,vy:a.y}),s(),i=0};return(async()=>{try{const t=(await Promise.resolve().then((()=>require("../hammer-Bs-QCG8V.cjs"))).then((e=>e.hammer))).default,r=(()=>{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})();n=new t(e,{touchAction:"pan-x pan-y",inputClass:r}),n.get("pinch").set({enable:!0,pointers:2,threshold:.1}),n.on("pinchstart",l),n.on("pinchmove",u),n.on("pinchend",c)}catch(t){console.warn("Failed to load HammerJS:",t)}})(),()=>{null==n||n.destroy(),s()}}),[t,r]),{elementRef:o}}exports.MarqueeZoom=({pageIndex:e,scale:t,pageWidth:a,pageHeight:s,className:l,stroke:u="rgba(33,150,243,0.8)",fill:c="rgba(33,150,243,0.15)"})=>{const{provides:p}=i(),{register:d}=o.usePointerHandlers({modeId:"marqueeZoom",pageIndex:e}),g=(e,t,n)=>Math.max(t,Math.min(n,e)),h=n.useRef(null),[m,v]=n.useState(null),x=a/t,f=s/t,y=n.useMemo((()=>({onPointerDown:(e,t)=>{var n,r;h.current=e,v({origin:{x:e.x,y:e.y},size:{width:0,height:0}}),null==(r=null==(n=t.target)?void 0:n.setPointerCapture)||r.call(n,t.pointerId)},onPointerMove:e=>{if(!h.current)return;const t=g(e.x,0,x),n=g(e.y,0,f),{x:r,y:o}=h.current,i=Math.min(r,t),a=Math.min(o,n),s=Math.abs(t-r),l=Math.abs(n-o);v({origin:{x:i,y:a},size:{width:s,height:l}})},onPointerUp:(n,r)=>{var o,i;if(m&&p){Math.max(m.size.width,m.size.height)*t>5?p.zoomToArea(e,m):p.zoomIn()}h.current=null,v(null),null==(i=null==(o=r.target)?void 0:o.releasePointerCapture)||i.call(o,r.pointerId)},onPointerCancel:(e,t)=>{var n,r;h.current=null,v(null),null==(r=null==(n=t.target)?void 0:n.releasePointerCapture)||r.call(n,t.pointerId)}})),[x,x,p,t,m,e]);return n.useEffect((()=>{if(d)return d(y)}),[d,y]),m?r.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:m.origin.x*t,top:m.origin.y*t,width:m.size.width*t,height:m.size.height*t,border:`1px solid ${u}`,background:c,boxSizing:"border-box"},className:l}):null},exports.PinchWrapper=function({children:e,style:t,...n}){const{elementRef:o}=a();return r.jsx("div",{ref:o,...n,style:{...t,display:"block",width:"fit-content",overflow:"visible",boxSizing:"border-box",margin:"0px auto"},children:e})},exports.usePinch=a,exports.useZoom=()=>{const{provides:e}=i(),[r,o]=n.useState(t.initialState);return n.useEffect((()=>null==e?void 0:e.onStateChange((e=>{o(e)}))),[e]),{state:r,provides:e}},exports.useZoomCapability=i,exports.useZoomPlugin=()=>e.usePlugin(t.ZoomPlugin.id);
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,viewportProvides:t,zoomProvides:o}){if("undefined"==typeof window)return()=>{};let n,r=0,i={x:0,y:0};const s=()=>{e.style.transform="none",e.style.transformOrigin="0 0"},a=n=>{var s;r=o.getState().currentZoomLevel;const a=t.getBoundingRect();i={x:n.center.x-a.origin.x,y:n.center.y-a.origin.y};const l=e.getBoundingClientRect();e.style.transformOrigin=`${n.center.x-l.left}px ${n.center.y-l.top}px`,(null==(s=n.srcEvent)?void 0:s.cancelable)&&(n.srcEvent.preventDefault(),n.srcEvent.stopPropagation())},l=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())},c=e=>{const t=(e.scale-1)*r;o.requestZoomBy(t,{vx:i.x,vy:i.y}),s(),r=0};return(async()=>{try{const t=(await Promise.resolve().then((()=>require("../hammer-Bs-QCG8V.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})();n=new t(e,{touchAction:"pan-x pan-y",inputClass:o}),n.get("pinch").set({enable:!0,pointers:2,threshold:.1}),n.on("pinchstart",a),n.on("pinchmove",l),n.on("pinchend",c)}catch(t){console.warn("Failed to load HammerJS:",t)}})(),()=>{null==n||n.destroy(),s()}}function s(){const{provides:t}=e.useCapability("viewport"),{provides:n}=r(),s=o.useRef(null);return o.useEffect((()=>{const e=s.current;if(e&&t&&n)return i({element:e,viewportProvides:t,zoomProvides:n})}),[t,n]),{elementRef:s}}exports.MarqueeZoom=({pageIndex:e,scale:t,className:i,stroke:s="rgba(33,150,243,0.8)",fill:a="rgba(33,150,243,0.15)"})=>{const{provides:l}=r(),[c,u]=o.useState(null);return o.useEffect((()=>{if(l)return l.registerMarqueeOnPage({pageIndex:e,scale:t,callback:{onPreview:u}})}),[l,e,t]),c?n.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:c.origin.x*t,top:c.origin.y*t,width:c.size.width*t,height:c.size.height*t,border:`1px solid ${s}`,background:a,boxSizing:"border-box"},className:i}):null},exports.PinchWrapper=function({children:e,style:t,...o}){const{elementRef:r}=s();return n.jsx("div",{ref:r,...o,style:{...t,display:"block",width:"fit-content",overflow:"visible",boxSizing:"border-box",margin:"0px auto"},children:e})},exports.usePinch=s,exports.useZoom=()=>{const{provides:e}=r(),[n,i]=o.useState(t.initialState);return o.useEffect((()=>null==e?void 0:e.onStateChange((e=>{i(e)}))),[e]),{state:n,provides:e}},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]})}));
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-zoom.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 { initialState, ZoomPlugin, ZoomState } 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\nexport const useZoom = () => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomState>(initialState);\n\n useEffect(() => {\n return provides?.onStateChange((action) => {\n setState(action);\n });\n }, [provides]);\n\n return {\n state,\n provides,\n };\n};\n","import { useEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { ZoomState } from '@embedpdf/plugin-zoom';\n\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch() {\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 // Check if we're on the client side\n if (typeof window === 'undefined') {\n return;\n }\n\n let hammer: any | undefined;\n let initialZoom = 0; // numeric scale at pinchstart\n let lastCenter = { x: 0, y: 0 };\n\n const getState = (): ZoomState => zoomProvides.getState();\n\n const updateTransform = (scale: number) => {\n // 1 → no scale; we only scale *relatively* to the start\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 = viewportProvides.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n // put the transform-origin under the fingers so the preview feels right\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n // stop the browser’s own pinch-zoom\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); // *only* CSS, no real zoom yet\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n // translate the relative hammer scale into a delta for requestZoomBy\n const delta = (e.scale - 1) * initialZoom;\n zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n // Dynamically import and setup Hammer\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n /* ------------------------------------------------------------------ */\n /* Hammer setup */\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', // allow scroll in every direction\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 }, [viewportProvides, zoomProvides]);\n\n return { elementRef };\n}\n","import { useEffect, useMemo, useRef, useState } from '@framework';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useZoomCapability } from '../hooks/use-zoom';\n\ninterface MarqueeZoomProps {\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page */\n scale: number;\n /** Width of the page */\n pageWidth: number;\n /** Height of the page */\n pageHeight: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke / fill colours (defaults below) */\n stroke?: string;\n fill?: string;\n}\n\n/**\n * Draws a marquee rectangle while the user drags.\n * Hook it into the interaction-manager with modeId = 'marqueeZoom'.\n */\nexport const MarqueeZoom = ({\n pageIndex,\n scale,\n pageWidth,\n pageHeight,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n /* ------------------------------------------------------------------ */\n /* zoom capability */\n /* ------------------------------------------------------------------ */\n const { provides: zoom } = useZoomCapability();\n\n /* ------------------------------------------------------------------ */\n /* integration with interaction-manager */\n /* ------------------------------------------------------------------ */\n const { register } = usePointerHandlers({ modeId: 'marqueeZoom', pageIndex });\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));\n\n /* ------------------------------------------------------------------ */\n /* local state – start / current drag position */\n /* ------------------------------------------------------------------ */\n const startRef = useRef<{ x: number; y: number } | null>(null);\n const [rect, setRect] = useState<Rect | null>(null);\n\n /* page size in **PDF-space** (unscaled) ----------------------------- */\n const pageWidthPDF = pageWidth / scale;\n const pageHeightPDF = pageHeight / scale;\n\n /* ------------------------------------------------------------------ */\n /* pointer handlers */\n /* ------------------------------------------------------------------ */\n const handlers = useMemo<PointerEventHandlers<PointerEvent>>(\n () => ({\n onPointerDown: (pos, evt) => {\n startRef.current = pos;\n setRect({ origin: { x: pos.x, y: pos.y }, size: { width: 0, height: 0 } });\n (evt.target as HTMLElement)?.setPointerCapture?.(evt.pointerId);\n },\n onPointerMove: (pos) => {\n if (!startRef.current) return;\n /* clamp current position to the page bounds */\n const curX = clamp(pos.x, 0, pageWidthPDF);\n const curY = clamp(pos.y, 0, pageHeightPDF);\n\n const { x: sx, y: sy } = startRef.current;\n const left = Math.min(sx, curX);\n const top = Math.min(sy, curY);\n const width = Math.abs(curX - sx);\n const height = Math.abs(curY - sy);\n\n setRect({ origin: { x: left, y: top }, size: { width, height } });\n },\n onPointerUp: (_, evt) => {\n if (rect && zoom) {\n const dragPx = Math.max(rect.size.width, rect.size.height) * scale;\n if (dragPx > 5) {\n // real drag → zoom to it\n zoom.zoomToArea(pageIndex, rect);\n } else {\n // tiny drag → simple zoom-in\n zoom.zoomIn();\n }\n }\n\n startRef.current = null;\n setRect(null);\n (evt.target as HTMLElement)?.releasePointerCapture?.(evt.pointerId);\n },\n onPointerCancel: (_, evt) => {\n startRef.current = null;\n setRect(null);\n (evt.target as HTMLElement)?.releasePointerCapture?.(evt.pointerId);\n },\n }),\n [pageWidthPDF, pageWidthPDF, zoom, scale, rect, pageIndex],\n );\n\n /* register with the interaction-manager */\n useEffect(() => {\n if (!register) return;\n return register(handlers);\n }, [register, handlers]);\n\n /* ------------------------------------------------------------------ */\n /* render */\n /* ------------------------------------------------------------------ */\n if (!rect) return null; // nothing to draw\n\n return (\n <div\n /* Each page wrapper is position:relative, so absolute is fine */\n style={{\n position: 'absolute',\n pointerEvents: 'none', // ignore hits – underlying page still gets events\n left: rect.origin.x * scale,\n top: rect.origin.y * scale,\n width: rect.size.width * scale,\n height: rect.size.height * scale,\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';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch();\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","usePinch","provides","viewportProvides","zoomProvides","elementRef","useRef","useEffect","element","current","window","hammer","initialZoom","lastCenter","x","y","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","pageIndex","pageWidth","pageHeight","className","stroke","fill","zoom","register","usePointerHandlers","modeId","clamp","v","min","max","Math","startRef","rect","setRect","useState","pageWidthPDF","pageHeightPDF","handlers","useMemo","onPointerDown","pos","evt","size","width","height","_b","target","setPointerCapture","call","pointerId","onPointerMove","curX","curY","sx","sy","abs","onPointerUp","_","zoomToArea","zoomIn","releasePointerCapture","onPointerCancel","jsxRuntime","jsx","position","pointerEvents","border","background","boxSizing","children","props","ref","display","overflow","margin","state","setState","initialState","onStateChange","action","usePlugin"],"mappings":"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"}
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 { initialState, ZoomPlugin, ZoomState } 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\nexport const useZoom = () => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomState>(initialState);\n\n useEffect(() => {\n return provides?.onStateChange((action) => {\n setState(action);\n });\n }, [provides]);\n\n return {\n state,\n provides,\n };\n};\n","import type { ViewportCapability } from '@embedpdf/plugin-viewport';\nimport type { ZoomCapability, ZoomState } from '@embedpdf/plugin-zoom';\n\nexport interface PinchZoomDeps {\n element: HTMLDivElement;\n viewportProvides: ViewportCapability;\n zoomProvides: ZoomCapability;\n}\n\nexport function setupPinchZoom({ element, viewportProvides, zoomProvides }: PinchZoomDeps) {\n // Check if we're on the client side\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n let hammer: any | undefined;\n let initialZoom = 0; // numeric scale at pinchstart\n let lastCenter = { x: 0, y: 0 };\n\n const getState = (): ZoomState => zoomProvides.getState();\n\n const updateTransform = (scale: number) => {\n // 1 → no scale; we only scale *relatively* to the start\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 = viewportProvides.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n // put the transform-origin under the fingers so the preview feels right\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n // stop the browser's own pinch-zoom\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); // *only* CSS, no real zoom yet\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n // translate the relative hammer scale into a delta for requestZoomBy\n const delta = (e.scale - 1) * initialZoom;\n zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n // Async Hammer setup (internal)\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n /* ------------------------------------------------------------------ */\n /* Hammer setup */\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', // allow scroll in every direction\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(); // Fire and forget\n\n // Return cleanup immediately\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';\n\nimport { setupPinchZoom } from '../utils/pinch-zoom-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch() {\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({ element, viewportProvides, zoomProvides });\n }, [viewportProvides, zoomProvides]);\n\n return { elementRef };\n}\n","import { useEffect, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useZoomCapability } from '../hooks/use-zoom';\n\ninterface MarqueeZoomProps {\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page */\n scale: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke / fill colours (defaults below) */\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n pageIndex,\n scale,\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 [rect, setRect] = useState<Rect | null>(null);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n pageIndex,\n scale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, pageIndex, scale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * scale,\n top: rect.origin.y * scale,\n width: rect.size.width * scale,\n height: rect.size.height * scale,\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';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch();\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","viewportProvides","zoomProvides","window","hammer","initialZoom","lastCenter","x","y","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","className","stroke","fill","zoomPlugin","rect","setRect","useState","registerMarqueeOnPage","callback","onPreview","jsxRuntime","jsx","position","pointerEvents","width","size","height","border","background","boxSizing","children","props","ref","display","overflow","margin","state","setState","initialState","onStateChange","action","usePlugin"],"mappings":"8OAIaA,EAAoB,IAAMC,gBAA0BC,EAAAA,WAAWC,ICKrE,SAASC,GAAeC,QAAEA,EAASC,iBAAAA,EAAAC,aAAkBA,IAEtD,GAAkB,oBAAXC,OACT,MAAO,OAGL,IAAAC,EACAC,EAAc,EACdC,EAAa,CAAEC,EAAG,EAAGC,EAAG,GAEtB,MAOAC,EAAiB,KACrBT,EAAQU,MAAMC,UAAY,OAC1BX,EAAQU,MAAME,gBAAkB,KAAA,EAG5BC,EAAcC,UAClBT,EAbgCH,EAAaa,WAapBC,iBAEnB,MAAAC,EAAWhB,EAAiBiB,kBAErBZ,EAAA,CACXC,EAAGO,EAAEK,OAAOZ,EAAIU,EAASG,OAAOb,EAChCC,EAAGM,EAAEK,OAAOX,EAAIS,EAASG,OAAOZ,GAI5B,MAAAa,EAAYrB,EAAQsB,wBAC1BtB,EAAQU,MAAME,gBAAkB,GAAGE,EAAEK,OAAOZ,EAAIc,EAAUE,UAAUT,EAAEK,OAAOX,EAAIa,EAAUG,SAGvF,OAAAC,EAAAX,EAAEY,eAAF,EAAAD,EAAYE,cACdb,EAAEY,SAASE,iBACXd,EAAEY,SAASG,kBAAgB,EAIzBC,EAAahB,UA/BMiB,IAgCPjB,EAAEiB,MA9BV/B,EAAAU,MAAMC,UAAY,SAASoB,MA+B/B,OAAAN,EAAAX,EAAEY,eAAF,EAAAD,EAAYE,cACdb,EAAEY,SAASE,iBACXd,EAAEY,SAASG,kBAAgB,EAIzBG,EAAYlB,IAEV,MAAAmB,GAASnB,EAAEiB,MAAQ,GAAK1B,EACjBH,EAAAgC,cAAcD,EAAO,CAAEE,GAAI7B,EAAWC,EAAG6B,GAAI9B,EAAWE,IAEtDC,IACDJ,EAAA,CAAA,EAsChB,MAlCoBgC,WACd,IACF,MAAMC,SAAgBC,QAAOC,UAAAC,MAAA,IAAAC,QAAA,4BAAaD,MAAAE,GAAAA,EAAAvC,UAAAwC,QAKpCC,QACJ,MACMC,EAAgB,iBAAkB3C,QAAU4C,UAAUC,eAAiB,EAEzE,OADuBF,GAFN,wCAEoCG,KAAKF,UAAUG,WACzCZ,EAAOa,WACjCL,EACER,EAAOc,gBADad,EAAOe,UAEjC,KAEMjD,EAAA,IAAIkC,EAAOtC,EAAS,CAC3BsD,YAAa,cACbT,eAGKzC,EAAAmD,IAAI,SAASC,IAAI,CAAEC,QAAQ,EAAMC,SAAU,EAAGC,UAAW,KAEzDvD,EAAAwD,GAAG,aAAc/C,GACjBT,EAAAwD,GAAG,YAAa9B,GAChB1B,EAAAwD,GAAG,WAAY5B,SACf6B,GACCC,QAAAC,KAAK,2BAA4BF,EAAK,GAItCG,GAGL,KACG,MAAA5D,GAAAA,EAAA6D,UACOxD,GAAA,CAEnB,CCrGO,SAASyD,IACd,MAAQC,SAAUlE,GAAqBL,EAAAA,cAA8B,aAC7DuE,SAAUjE,GAAiBP,IAC7ByE,EAAaC,SAAuB,MAW1C,OATAC,EAAAA,WAAU,KACR,MAAMtE,EAAUoE,EAAWG,QAC3B,GAAKvE,GAAYC,GAAqBC,EAItC,OAAOH,EAAe,CAAEC,UAASC,mBAAkBC,gBAAc,GAChE,CAACD,EAAkBC,IAEf,CAAEkE,aACX,qBCL2B,EACzBI,YACAzC,QACA0C,YACAC,SAAS,uBACTC,OAAO,4BAEP,MAAQR,SAAUS,GAAejF,KAC1BkF,EAAMC,GAAWC,EAAAA,SAAsB,MAa1C,OAXJT,EAAAA,WAAU,KACR,GAAKM,EACL,OAAOA,EAAWI,sBAAsB,CACtCR,YACAzC,QACAkD,SAAU,CACRC,UAAWJ,IAEd,GACA,CAACF,EAAYJ,EAAWzC,IAEtB8C,EAGHM,EAAAC,IAAC,MAAA,CACC1E,MAAO,CACL2E,SAAU,WACVC,cAAe,OACf/D,KAAMsD,EAAKzD,OAAOb,EAAIwB,EACtBP,IAAKqD,EAAKzD,OAAOZ,EAAIuB,EACrBwD,MAAOV,EAAKW,KAAKD,MAAQxD,EACzB0D,OAAQZ,EAAKW,KAAKC,OAAS1D,EAC3B2D,OAAQ,aAAahB,IACrBiB,WAAYhB,EACZiB,UAAW,cAEbnB,cAfc,IAgBhB,uBC7CG,UAAsBoB,SAAEA,EAAAnF,MAAUA,KAAUoF,IAC3C,MAAA1B,WAAEA,GAAeF,IAGrB,OAAAiB,EAAAC,IAAC,MAAA,CACCW,IAAK3B,KACD0B,EACJpF,MAAO,IACFA,EACHsF,QAAS,QACTT,MAAO,cACPU,SAAU,UACVL,UAAW,aACXM,OAAQ,YAGTL,YAGP,qCJrBuB,KACf,MAAA1B,SAAEA,GAAaxE,KACdwG,EAAOC,GAAYrB,EAAAA,SAAoBsB,EAAAA,cAQvC,OANP/B,EAAAA,WAAU,IACD,MAAAH,OAAA,EAAAA,EAAUmC,eAAeC,IAC9BH,EAASG,EAAM,KAEhB,CAACpC,IAEG,CACLgC,QACAhC,WACF,oDAf2B,IAAMqC,YAAsB3G,EAAAA,WAAWC"}
@@ -1,9 +1,9 @@
1
1
  import { useCapability, usePlugin } from "@embedpdf/core/preact";
2
2
  import { ZoomPlugin, initialState } from "@embedpdf/plugin-zoom";
3
+ export * from "@embedpdf/plugin-zoom";
3
4
  import "preact";
4
- import { useState, useEffect, useRef, useMemo } from "preact/hooks";
5
+ import { useState, useEffect, useRef } from "preact/hooks";
5
6
  import { jsx } from "preact/jsx-runtime";
6
- import { usePointerHandlers } from "@embedpdf/plugin-interaction-manager/preact";
7
7
  const useZoomCapability = () => useCapability(ZoomPlugin.id);
8
8
  const useZoomPlugin = () => usePlugin(ZoomPlugin.id);
9
9
  const useZoom = () => {
@@ -19,6 +19,81 @@ const useZoom = () => {
19
19
  provides
20
20
  };
21
21
  };
22
+ function setupPinchZoom({ element, viewportProvides, zoomProvides }) {
23
+ if (typeof window === "undefined") {
24
+ return () => {
25
+ };
26
+ }
27
+ let hammer;
28
+ let initialZoom = 0;
29
+ let lastCenter = { x: 0, y: 0 };
30
+ const getState = () => zoomProvides.getState();
31
+ const updateTransform = (scale) => {
32
+ element.style.transform = `scale(${scale})`;
33
+ };
34
+ const resetTransform = () => {
35
+ element.style.transform = "none";
36
+ element.style.transformOrigin = "0 0";
37
+ };
38
+ const pinchStart = (e) => {
39
+ var _a;
40
+ initialZoom = getState().currentZoomLevel;
41
+ const contRect = viewportProvides.getBoundingRect();
42
+ lastCenter = {
43
+ x: e.center.x - contRect.origin.x,
44
+ y: e.center.y - contRect.origin.y
45
+ };
46
+ const innerRect = element.getBoundingClientRect();
47
+ element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;
48
+ if ((_a = e.srcEvent) == null ? void 0 : _a.cancelable) {
49
+ e.srcEvent.preventDefault();
50
+ e.srcEvent.stopPropagation();
51
+ }
52
+ };
53
+ const pinchMove = (e) => {
54
+ var _a;
55
+ updateTransform(e.scale);
56
+ if ((_a = e.srcEvent) == null ? void 0 : _a.cancelable) {
57
+ e.srcEvent.preventDefault();
58
+ e.srcEvent.stopPropagation();
59
+ }
60
+ };
61
+ const pinchEnd = (e) => {
62
+ const delta = (e.scale - 1) * initialZoom;
63
+ zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });
64
+ resetTransform();
65
+ initialZoom = 0;
66
+ };
67
+ const setupHammer = async () => {
68
+ try {
69
+ const Hammer = (await import("../hammer-e1aXHboh.js").then((n) => n.h)).default;
70
+ const inputClass = (() => {
71
+ const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
72
+ const SUPPORT_TOUCH = "ontouchstart" in window || navigator.maxTouchPoints > 0;
73
+ const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
74
+ if (SUPPORT_ONLY_TOUCH) return Hammer.TouchInput;
75
+ if (!SUPPORT_TOUCH) return Hammer.MouseInput;
76
+ return Hammer.TouchMouseInput;
77
+ })();
78
+ hammer = new Hammer(element, {
79
+ touchAction: "pan-x pan-y",
80
+ // allow scroll in every direction
81
+ inputClass
82
+ });
83
+ hammer.get("pinch").set({ enable: true, pointers: 2, threshold: 0.1 });
84
+ hammer.on("pinchstart", pinchStart);
85
+ hammer.on("pinchmove", pinchMove);
86
+ hammer.on("pinchend", pinchEnd);
87
+ } catch (error) {
88
+ console.warn("Failed to load HammerJS:", error);
89
+ }
90
+ };
91
+ setupHammer();
92
+ return () => {
93
+ hammer == null ? void 0 : hammer.destroy();
94
+ resetTransform();
95
+ };
96
+ }
22
97
  function usePinch() {
23
98
  const { provides: viewportProvides } = useCapability("viewport");
24
99
  const { provides: zoomProvides } = useZoomCapability();
@@ -28,78 +103,7 @@ function usePinch() {
28
103
  if (!element || !viewportProvides || !zoomProvides) {
29
104
  return;
30
105
  }
31
- if (typeof window === "undefined") {
32
- return;
33
- }
34
- let hammer;
35
- let initialZoom = 0;
36
- let lastCenter = { x: 0, y: 0 };
37
- const getState = () => zoomProvides.getState();
38
- const updateTransform = (scale) => {
39
- element.style.transform = `scale(${scale})`;
40
- };
41
- const resetTransform = () => {
42
- element.style.transform = "none";
43
- element.style.transformOrigin = "0 0";
44
- };
45
- const pinchStart = (e) => {
46
- var _a;
47
- initialZoom = getState().currentZoomLevel;
48
- const contRect = viewportProvides.getBoundingRect();
49
- lastCenter = {
50
- x: e.center.x - contRect.origin.x,
51
- y: e.center.y - contRect.origin.y
52
- };
53
- const innerRect = element.getBoundingClientRect();
54
- element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;
55
- if ((_a = e.srcEvent) == null ? void 0 : _a.cancelable) {
56
- e.srcEvent.preventDefault();
57
- e.srcEvent.stopPropagation();
58
- }
59
- };
60
- const pinchMove = (e) => {
61
- var _a;
62
- updateTransform(e.scale);
63
- if ((_a = e.srcEvent) == null ? void 0 : _a.cancelable) {
64
- e.srcEvent.preventDefault();
65
- e.srcEvent.stopPropagation();
66
- }
67
- };
68
- const pinchEnd = (e) => {
69
- const delta = (e.scale - 1) * initialZoom;
70
- zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });
71
- resetTransform();
72
- initialZoom = 0;
73
- };
74
- const setupHammer = async () => {
75
- try {
76
- const Hammer = (await import("../hammer-e1aXHboh.js").then((n) => n.h)).default;
77
- const inputClass = (() => {
78
- const MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
79
- const SUPPORT_TOUCH = "ontouchstart" in window || navigator.maxTouchPoints > 0;
80
- const SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
81
- if (SUPPORT_ONLY_TOUCH) return Hammer.TouchInput;
82
- if (!SUPPORT_TOUCH) return Hammer.MouseInput;
83
- return Hammer.TouchMouseInput;
84
- })();
85
- hammer = new Hammer(element, {
86
- touchAction: "pan-x pan-y",
87
- // allow scroll in every direction
88
- inputClass
89
- });
90
- hammer.get("pinch").set({ enable: true, pointers: 2, threshold: 0.1 });
91
- hammer.on("pinchstart", pinchStart);
92
- hammer.on("pinchmove", pinchMove);
93
- hammer.on("pinchend", pinchEnd);
94
- } catch (error) {
95
- console.warn("Failed to load HammerJS:", error);
96
- }
97
- };
98
- setupHammer();
99
- return () => {
100
- hammer == null ? void 0 : hammer.destroy();
101
- resetTransform();
102
- };
106
+ return setupPinchZoom({ element, viewportProvides, zoomProvides });
103
107
  }, [viewportProvides, zoomProvides]);
104
108
  return { elementRef };
105
109
  }
@@ -125,65 +129,22 @@ function PinchWrapper({ children, style, ...props }) {
125
129
  const MarqueeZoom = ({
126
130
  pageIndex,
127
131
  scale,
128
- pageWidth,
129
- pageHeight,
130
132
  className,
131
133
  stroke = "rgba(33,150,243,0.8)",
132
134
  fill = "rgba(33,150,243,0.15)"
133
135
  }) => {
134
- const { provides: zoom } = useZoomCapability();
135
- const { register } = usePointerHandlers({ modeId: "marqueeZoom", pageIndex });
136
- const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
137
- const startRef = useRef(null);
136
+ const { provides: zoomPlugin } = useZoomCapability();
138
137
  const [rect, setRect] = useState(null);
139
- const pageWidthPDF = pageWidth / scale;
140
- const pageHeightPDF = pageHeight / scale;
141
- const handlers = useMemo(
142
- () => ({
143
- onPointerDown: (pos, evt) => {
144
- var _a, _b;
145
- startRef.current = pos;
146
- setRect({ origin: { x: pos.x, y: pos.y }, size: { width: 0, height: 0 } });
147
- (_b = (_a = evt.target) == null ? void 0 : _a.setPointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
148
- },
149
- onPointerMove: (pos) => {
150
- if (!startRef.current) return;
151
- const curX = clamp(pos.x, 0, pageWidthPDF);
152
- const curY = clamp(pos.y, 0, pageHeightPDF);
153
- const { x: sx, y: sy } = startRef.current;
154
- const left = Math.min(sx, curX);
155
- const top = Math.min(sy, curY);
156
- const width = Math.abs(curX - sx);
157
- const height = Math.abs(curY - sy);
158
- setRect({ origin: { x: left, y: top }, size: { width, height } });
159
- },
160
- onPointerUp: (_, evt) => {
161
- var _a, _b;
162
- if (rect && zoom) {
163
- const dragPx = Math.max(rect.size.width, rect.size.height) * scale;
164
- if (dragPx > 5) {
165
- zoom.zoomToArea(pageIndex, rect);
166
- } else {
167
- zoom.zoomIn();
168
- }
169
- }
170
- startRef.current = null;
171
- setRect(null);
172
- (_b = (_a = evt.target) == null ? void 0 : _a.releasePointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
173
- },
174
- onPointerCancel: (_, evt) => {
175
- var _a, _b;
176
- startRef.current = null;
177
- setRect(null);
178
- (_b = (_a = evt.target) == null ? void 0 : _a.releasePointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
179
- }
180
- }),
181
- [pageWidthPDF, pageWidthPDF, zoom, scale, rect, pageIndex]
182
- );
183
138
  useEffect(() => {
184
- if (!register) return;
185
- return register(handlers);
186
- }, [register, handlers]);
139
+ if (!zoomPlugin) return;
140
+ return zoomPlugin.registerMarqueeOnPage({
141
+ pageIndex,
142
+ scale,
143
+ callback: {
144
+ onPreview: setRect
145
+ }
146
+ });
147
+ }, [zoomPlugin, pageIndex, scale]);
187
148
  if (!rect) return null;
188
149
  return /* @__PURE__ */ jsx(
189
150
  "div",
@@ -191,7 +152,6 @@ const MarqueeZoom = ({
191
152
  style: {
192
153
  position: "absolute",
193
154
  pointerEvents: "none",
194
- // ignore hits – underlying page still gets events
195
155
  left: rect.origin.x * scale,
196
156
  top: rect.origin.y * scale,
197
157
  width: rect.size.width * scale,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-zoom.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 { initialState, ZoomPlugin, ZoomState } 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\nexport const useZoom = () => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomState>(initialState);\n\n useEffect(() => {\n return provides?.onStateChange((action) => {\n setState(action);\n });\n }, [provides]);\n\n return {\n state,\n provides,\n };\n};\n","import { useEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { ZoomState } from '@embedpdf/plugin-zoom';\n\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch() {\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 // Check if we're on the client side\n if (typeof window === 'undefined') {\n return;\n }\n\n let hammer: any | undefined;\n let initialZoom = 0; // numeric scale at pinchstart\n let lastCenter = { x: 0, y: 0 };\n\n const getState = (): ZoomState => zoomProvides.getState();\n\n const updateTransform = (scale: number) => {\n // 1 → no scale; we only scale *relatively* to the start\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 = viewportProvides.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n // put the transform-origin under the fingers so the preview feels right\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n // stop the browser’s own pinch-zoom\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); // *only* CSS, no real zoom yet\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n // translate the relative hammer scale into a delta for requestZoomBy\n const delta = (e.scale - 1) * initialZoom;\n zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n // Dynamically import and setup Hammer\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n /* ------------------------------------------------------------------ */\n /* Hammer setup */\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', // allow scroll in every direction\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 }, [viewportProvides, zoomProvides]);\n\n return { elementRef };\n}\n","import { ReactNode, HTMLAttributes, CSSProperties } from '@framework';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch();\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, useRef, useState } from '@framework';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useZoomCapability } from '../hooks/use-zoom';\n\ninterface MarqueeZoomProps {\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page */\n scale: number;\n /** Width of the page */\n pageWidth: number;\n /** Height of the page */\n pageHeight: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke / fill colours (defaults below) */\n stroke?: string;\n fill?: string;\n}\n\n/**\n * Draws a marquee rectangle while the user drags.\n * Hook it into the interaction-manager with modeId = 'marqueeZoom'.\n */\nexport const MarqueeZoom = ({\n pageIndex,\n scale,\n pageWidth,\n pageHeight,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n /* ------------------------------------------------------------------ */\n /* zoom capability */\n /* ------------------------------------------------------------------ */\n const { provides: zoom } = useZoomCapability();\n\n /* ------------------------------------------------------------------ */\n /* integration with interaction-manager */\n /* ------------------------------------------------------------------ */\n const { register } = usePointerHandlers({ modeId: 'marqueeZoom', pageIndex });\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));\n\n /* ------------------------------------------------------------------ */\n /* local state – start / current drag position */\n /* ------------------------------------------------------------------ */\n const startRef = useRef<{ x: number; y: number } | null>(null);\n const [rect, setRect] = useState<Rect | null>(null);\n\n /* page size in **PDF-space** (unscaled) ----------------------------- */\n const pageWidthPDF = pageWidth / scale;\n const pageHeightPDF = pageHeight / scale;\n\n /* ------------------------------------------------------------------ */\n /* pointer handlers */\n /* ------------------------------------------------------------------ */\n const handlers = useMemo<PointerEventHandlers<PointerEvent>>(\n () => ({\n onPointerDown: (pos, evt) => {\n startRef.current = pos;\n setRect({ origin: { x: pos.x, y: pos.y }, size: { width: 0, height: 0 } });\n (evt.target as HTMLElement)?.setPointerCapture?.(evt.pointerId);\n },\n onPointerMove: (pos) => {\n if (!startRef.current) return;\n /* clamp current position to the page bounds */\n const curX = clamp(pos.x, 0, pageWidthPDF);\n const curY = clamp(pos.y, 0, pageHeightPDF);\n\n const { x: sx, y: sy } = startRef.current;\n const left = Math.min(sx, curX);\n const top = Math.min(sy, curY);\n const width = Math.abs(curX - sx);\n const height = Math.abs(curY - sy);\n\n setRect({ origin: { x: left, y: top }, size: { width, height } });\n },\n onPointerUp: (_, evt) => {\n if (rect && zoom) {\n const dragPx = Math.max(rect.size.width, rect.size.height) * scale;\n if (dragPx > 5) {\n // real drag → zoom to it\n zoom.zoomToArea(pageIndex, rect);\n } else {\n // tiny drag → simple zoom-in\n zoom.zoomIn();\n }\n }\n\n startRef.current = null;\n setRect(null);\n (evt.target as HTMLElement)?.releasePointerCapture?.(evt.pointerId);\n },\n onPointerCancel: (_, evt) => {\n startRef.current = null;\n setRect(null);\n (evt.target as HTMLElement)?.releasePointerCapture?.(evt.pointerId);\n },\n }),\n [pageWidthPDF, pageWidthPDF, zoom, scale, rect, pageIndex],\n );\n\n /* register with the interaction-manager */\n useEffect(() => {\n if (!register) return;\n return register(handlers);\n }, [register, handlers]);\n\n /* ------------------------------------------------------------------ */\n /* render */\n /* ------------------------------------------------------------------ */\n if (!rect) return null; // nothing to draw\n\n return (\n <div\n /* Each page wrapper is position:relative, so absolute is fine */\n style={{\n position: 'absolute',\n pointerEvents: 'none', // ignore hits – underlying page still gets events\n left: rect.origin.x * scale,\n top: rect.origin.y * scale,\n width: rect.size.width * scale,\n height: rect.size.height * scale,\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/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 { initialState, ZoomPlugin, ZoomState } 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\nexport const useZoom = () => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomState>(initialState);\n\n useEffect(() => {\n return provides?.onStateChange((action) => {\n setState(action);\n });\n }, [provides]);\n\n return {\n state,\n provides,\n };\n};\n","import type { ViewportCapability } from '@embedpdf/plugin-viewport';\nimport type { ZoomCapability, ZoomState } from '@embedpdf/plugin-zoom';\n\nexport interface PinchZoomDeps {\n element: HTMLDivElement;\n viewportProvides: ViewportCapability;\n zoomProvides: ZoomCapability;\n}\n\nexport function setupPinchZoom({ element, viewportProvides, zoomProvides }: PinchZoomDeps) {\n // Check if we're on the client side\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n let hammer: any | undefined;\n let initialZoom = 0; // numeric scale at pinchstart\n let lastCenter = { x: 0, y: 0 };\n\n const getState = (): ZoomState => zoomProvides.getState();\n\n const updateTransform = (scale: number) => {\n // 1 → no scale; we only scale *relatively* to the start\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 = viewportProvides.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n // put the transform-origin under the fingers so the preview feels right\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n // stop the browser's own pinch-zoom\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); // *only* CSS, no real zoom yet\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n // translate the relative hammer scale into a delta for requestZoomBy\n const delta = (e.scale - 1) * initialZoom;\n zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n // Async Hammer setup (internal)\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n /* ------------------------------------------------------------------ */\n /* Hammer setup */\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', // allow scroll in every direction\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(); // Fire and forget\n\n // Return cleanup immediately\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';\n\nimport { setupPinchZoom } from '../utils/pinch-zoom-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch() {\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({ element, viewportProvides, zoomProvides });\n }, [viewportProvides, zoomProvides]);\n\n return { elementRef };\n}\n","import { ReactNode, HTMLAttributes, CSSProperties } from '@framework';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch();\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, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useZoomCapability } from '../hooks/use-zoom';\n\ninterface MarqueeZoomProps {\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page */\n scale: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke / fill colours (defaults below) */\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n pageIndex,\n scale,\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 [rect, setRect] = useState<Rect | null>(null);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n pageIndex,\n scale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, pageIndex, scale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * scale,\n top: rect.origin.y * scale,\n width: rect.size.width * scale,\n height: rect.size.height * scale,\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,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core/react"),t=require("@embedpdf/plugin-zoom"),n=require("react"),r=require("react/jsx-runtime"),o=require("@embedpdf/plugin-interaction-manager/react"),i=()=>e.useCapability(t.ZoomPlugin.id);function a(){const{provides:t}=e.useCapability("viewport"),{provides:r}=i(),o=n.useRef(null);return n.useEffect((()=>{const e=o.current;if(!e||!t||!r)return;if("undefined"==typeof window)return;let n,i=0,a={x:0,y:0};const s=()=>{e.style.transform="none",e.style.transformOrigin="0 0"},l=n=>{var o;i=r.getState().currentZoomLevel;const s=t.getBoundingRect();a={x:n.center.x-s.origin.x,y:n.center.y-s.origin.y};const l=e.getBoundingClientRect();e.style.transformOrigin=`${n.center.x-l.left}px ${n.center.y-l.top}px`,(null==(o=n.srcEvent)?void 0:o.cancelable)&&(n.srcEvent.preventDefault(),n.srcEvent.stopPropagation())},u=t=>{var n,r;r=t.scale,e.style.transform=`scale(${r})`,(null==(n=t.srcEvent)?void 0:n.cancelable)&&(t.srcEvent.preventDefault(),t.srcEvent.stopPropagation())},c=e=>{const t=(e.scale-1)*i;r.requestZoomBy(t,{vx:a.x,vy:a.y}),s(),i=0};return(async()=>{try{const t=(await Promise.resolve().then((()=>require("../hammer-Bs-QCG8V.cjs"))).then((e=>e.hammer))).default,r=(()=>{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})();n=new t(e,{touchAction:"pan-x pan-y",inputClass:r}),n.get("pinch").set({enable:!0,pointers:2,threshold:.1}),n.on("pinchstart",l),n.on("pinchmove",u),n.on("pinchend",c)}catch(t){console.warn("Failed to load HammerJS:",t)}})(),()=>{null==n||n.destroy(),s()}}),[t,r]),{elementRef:o}}exports.MarqueeZoom=({pageIndex:e,scale:t,pageWidth:a,pageHeight:s,className:l,stroke:u="rgba(33,150,243,0.8)",fill:c="rgba(33,150,243,0.15)"})=>{const{provides:p}=i(),{register:d}=o.usePointerHandlers({modeId:"marqueeZoom",pageIndex:e}),g=(e,t,n)=>Math.max(t,Math.min(n,e)),h=n.useRef(null),[m,v]=n.useState(null),x=a/t,f=s/t,y=n.useMemo((()=>({onPointerDown:(e,t)=>{var n,r;h.current=e,v({origin:{x:e.x,y:e.y},size:{width:0,height:0}}),null==(r=null==(n=t.target)?void 0:n.setPointerCapture)||r.call(n,t.pointerId)},onPointerMove:e=>{if(!h.current)return;const t=g(e.x,0,x),n=g(e.y,0,f),{x:r,y:o}=h.current,i=Math.min(r,t),a=Math.min(o,n),s=Math.abs(t-r),l=Math.abs(n-o);v({origin:{x:i,y:a},size:{width:s,height:l}})},onPointerUp:(n,r)=>{var o,i;if(m&&p){Math.max(m.size.width,m.size.height)*t>5?p.zoomToArea(e,m):p.zoomIn()}h.current=null,v(null),null==(i=null==(o=r.target)?void 0:o.releasePointerCapture)||i.call(o,r.pointerId)},onPointerCancel:(e,t)=>{var n,r;h.current=null,v(null),null==(r=null==(n=t.target)?void 0:n.releasePointerCapture)||r.call(n,t.pointerId)}})),[x,x,p,t,m,e]);return n.useEffect((()=>{if(d)return d(y)}),[d,y]),m?r.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:m.origin.x*t,top:m.origin.y*t,width:m.size.width*t,height:m.size.height*t,border:`1px solid ${u}`,background:c,boxSizing:"border-box"},className:l}):null},exports.PinchWrapper=function({children:e,style:t,...n}){const{elementRef:o}=a();return r.jsx("div",{ref:o,...n,style:{...t,display:"block",width:"fit-content",overflow:"visible",boxSizing:"border-box",margin:"0px auto"},children:e})},exports.usePinch=a,exports.useZoom=()=>{const{provides:e}=i(),[r,o]=n.useState(t.initialState);return n.useEffect((()=>null==e?void 0:e.onStateChange((e=>{o(e)}))),[e]),{state:r,provides:e}},exports.useZoomCapability=i,exports.useZoomPlugin=()=>e.usePlugin(t.ZoomPlugin.id);
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,viewportProvides:t,zoomProvides:o}){if("undefined"==typeof window)return()=>{};let n,r=0,i={x:0,y:0};const s=()=>{e.style.transform="none",e.style.transformOrigin="0 0"},a=n=>{var s;r=o.getState().currentZoomLevel;const a=t.getBoundingRect();i={x:n.center.x-a.origin.x,y:n.center.y-a.origin.y};const l=e.getBoundingClientRect();e.style.transformOrigin=`${n.center.x-l.left}px ${n.center.y-l.top}px`,(null==(s=n.srcEvent)?void 0:s.cancelable)&&(n.srcEvent.preventDefault(),n.srcEvent.stopPropagation())},l=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())},c=e=>{const t=(e.scale-1)*r;o.requestZoomBy(t,{vx:i.x,vy:i.y}),s(),r=0};return(async()=>{try{const t=(await Promise.resolve().then((()=>require("../hammer-Bs-QCG8V.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})();n=new t(e,{touchAction:"pan-x pan-y",inputClass:o}),n.get("pinch").set({enable:!0,pointers:2,threshold:.1}),n.on("pinchstart",a),n.on("pinchmove",l),n.on("pinchend",c)}catch(t){console.warn("Failed to load HammerJS:",t)}})(),()=>{null==n||n.destroy(),s()}}function s(){const{provides:t}=e.useCapability("viewport"),{provides:n}=r(),s=o.useRef(null);return o.useEffect((()=>{const e=s.current;if(e&&t&&n)return i({element:e,viewportProvides:t,zoomProvides:n})}),[t,n]),{elementRef:s}}exports.MarqueeZoom=({pageIndex:e,scale:t,className:i,stroke:s="rgba(33,150,243,0.8)",fill:a="rgba(33,150,243,0.15)"})=>{const{provides:l}=r(),[c,u]=o.useState(null);return o.useEffect((()=>{if(l)return l.registerMarqueeOnPage({pageIndex:e,scale:t,callback:{onPreview:u}})}),[l,e,t]),c?n.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:c.origin.x*t,top:c.origin.y*t,width:c.size.width*t,height:c.size.height*t,border:`1px solid ${s}`,background:a,boxSizing:"border-box"},className:i}):null},exports.PinchWrapper=function({children:e,style:t,...o}){const{elementRef:r}=s();return n.jsx("div",{ref:r,...o,style:{...t,display:"block",width:"fit-content",overflow:"visible",boxSizing:"border-box",margin:"0px auto"},children:e})},exports.usePinch=s,exports.useZoom=()=>{const{provides:e}=r(),[n,i]=o.useState(t.initialState);return o.useEffect((()=>null==e?void 0:e.onStateChange((e=>{i(e)}))),[e]),{state:n,provides:e}},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]})}));
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-zoom.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 { initialState, ZoomPlugin, ZoomState } 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\nexport const useZoom = () => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomState>(initialState);\n\n useEffect(() => {\n return provides?.onStateChange((action) => {\n setState(action);\n });\n }, [provides]);\n\n return {\n state,\n provides,\n };\n};\n","import { useEffect, useRef } from '@framework';\nimport { useCapability } from '@embedpdf/core/@framework';\nimport { ViewportPlugin } from '@embedpdf/plugin-viewport';\nimport { ZoomState } from '@embedpdf/plugin-zoom';\n\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch() {\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 // Check if we're on the client side\n if (typeof window === 'undefined') {\n return;\n }\n\n let hammer: any | undefined;\n let initialZoom = 0; // numeric scale at pinchstart\n let lastCenter = { x: 0, y: 0 };\n\n const getState = (): ZoomState => zoomProvides.getState();\n\n const updateTransform = (scale: number) => {\n // 1 → no scale; we only scale *relatively* to the start\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 = viewportProvides.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n // put the transform-origin under the fingers so the preview feels right\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n // stop the browser’s own pinch-zoom\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); // *only* CSS, no real zoom yet\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n // translate the relative hammer scale into a delta for requestZoomBy\n const delta = (e.scale - 1) * initialZoom;\n zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n // Dynamically import and setup Hammer\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n /* ------------------------------------------------------------------ */\n /* Hammer setup */\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', // allow scroll in every direction\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 }, [viewportProvides, zoomProvides]);\n\n return { elementRef };\n}\n","import { useEffect, useMemo, useRef, useState } from '@framework';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useZoomCapability } from '../hooks/use-zoom';\n\ninterface MarqueeZoomProps {\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page */\n scale: number;\n /** Width of the page */\n pageWidth: number;\n /** Height of the page */\n pageHeight: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke / fill colours (defaults below) */\n stroke?: string;\n fill?: string;\n}\n\n/**\n * Draws a marquee rectangle while the user drags.\n * Hook it into the interaction-manager with modeId = 'marqueeZoom'.\n */\nexport const MarqueeZoom = ({\n pageIndex,\n scale,\n pageWidth,\n pageHeight,\n className,\n stroke = 'rgba(33,150,243,0.8)',\n fill = 'rgba(33,150,243,0.15)',\n}: MarqueeZoomProps) => {\n /* ------------------------------------------------------------------ */\n /* zoom capability */\n /* ------------------------------------------------------------------ */\n const { provides: zoom } = useZoomCapability();\n\n /* ------------------------------------------------------------------ */\n /* integration with interaction-manager */\n /* ------------------------------------------------------------------ */\n const { register } = usePointerHandlers({ modeId: 'marqueeZoom', pageIndex });\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));\n\n /* ------------------------------------------------------------------ */\n /* local state – start / current drag position */\n /* ------------------------------------------------------------------ */\n const startRef = useRef<{ x: number; y: number } | null>(null);\n const [rect, setRect] = useState<Rect | null>(null);\n\n /* page size in **PDF-space** (unscaled) ----------------------------- */\n const pageWidthPDF = pageWidth / scale;\n const pageHeightPDF = pageHeight / scale;\n\n /* ------------------------------------------------------------------ */\n /* pointer handlers */\n /* ------------------------------------------------------------------ */\n const handlers = useMemo<PointerEventHandlers<PointerEvent>>(\n () => ({\n onPointerDown: (pos, evt) => {\n startRef.current = pos;\n setRect({ origin: { x: pos.x, y: pos.y }, size: { width: 0, height: 0 } });\n (evt.target as HTMLElement)?.setPointerCapture?.(evt.pointerId);\n },\n onPointerMove: (pos) => {\n if (!startRef.current) return;\n /* clamp current position to the page bounds */\n const curX = clamp(pos.x, 0, pageWidthPDF);\n const curY = clamp(pos.y, 0, pageHeightPDF);\n\n const { x: sx, y: sy } = startRef.current;\n const left = Math.min(sx, curX);\n const top = Math.min(sy, curY);\n const width = Math.abs(curX - sx);\n const height = Math.abs(curY - sy);\n\n setRect({ origin: { x: left, y: top }, size: { width, height } });\n },\n onPointerUp: (_, evt) => {\n if (rect && zoom) {\n const dragPx = Math.max(rect.size.width, rect.size.height) * scale;\n if (dragPx > 5) {\n // real drag → zoom to it\n zoom.zoomToArea(pageIndex, rect);\n } else {\n // tiny drag → simple zoom-in\n zoom.zoomIn();\n }\n }\n\n startRef.current = null;\n setRect(null);\n (evt.target as HTMLElement)?.releasePointerCapture?.(evt.pointerId);\n },\n onPointerCancel: (_, evt) => {\n startRef.current = null;\n setRect(null);\n (evt.target as HTMLElement)?.releasePointerCapture?.(evt.pointerId);\n },\n }),\n [pageWidthPDF, pageWidthPDF, zoom, scale, rect, pageIndex],\n );\n\n /* register with the interaction-manager */\n useEffect(() => {\n if (!register) return;\n return register(handlers);\n }, [register, handlers]);\n\n /* ------------------------------------------------------------------ */\n /* render */\n /* ------------------------------------------------------------------ */\n if (!rect) return null; // nothing to draw\n\n return (\n <div\n /* Each page wrapper is position:relative, so absolute is fine */\n style={{\n position: 'absolute',\n pointerEvents: 'none', // ignore hits – underlying page still gets events\n left: rect.origin.x * scale,\n top: rect.origin.y * scale,\n width: rect.size.width * scale,\n height: rect.size.height * scale,\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';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch();\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","usePinch","provides","viewportProvides","zoomProvides","elementRef","useRef","useEffect","element","current","window","hammer","initialZoom","lastCenter","x","y","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","pageIndex","pageWidth","pageHeight","className","stroke","fill","zoom","register","usePointerHandlers","modeId","clamp","v","min","max","Math","startRef","rect","setRect","useState","pageWidthPDF","pageHeightPDF","handlers","useMemo","onPointerDown","pos","evt","size","width","height","_b","target","setPointerCapture","call","pointerId","onPointerMove","curX","curY","sx","sy","abs","onPointerUp","_","zoomToArea","zoomIn","releasePointerCapture","onPointerCancel","jsxRuntime","jsx","position","pointerEvents","border","background","boxSizing","children","props","ref","display","overflow","margin","state","setState","initialState","onStateChange","action","usePlugin"],"mappings":"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"}
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 { initialState, ZoomPlugin, ZoomState } 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\nexport const useZoom = () => {\n const { provides } = useZoomCapability();\n const [state, setState] = useState<ZoomState>(initialState);\n\n useEffect(() => {\n return provides?.onStateChange((action) => {\n setState(action);\n });\n }, [provides]);\n\n return {\n state,\n provides,\n };\n};\n","import type { ViewportCapability } from '@embedpdf/plugin-viewport';\nimport type { ZoomCapability, ZoomState } from '@embedpdf/plugin-zoom';\n\nexport interface PinchZoomDeps {\n element: HTMLDivElement;\n viewportProvides: ViewportCapability;\n zoomProvides: ZoomCapability;\n}\n\nexport function setupPinchZoom({ element, viewportProvides, zoomProvides }: PinchZoomDeps) {\n // Check if we're on the client side\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n let hammer: any | undefined;\n let initialZoom = 0; // numeric scale at pinchstart\n let lastCenter = { x: 0, y: 0 };\n\n const getState = (): ZoomState => zoomProvides.getState();\n\n const updateTransform = (scale: number) => {\n // 1 → no scale; we only scale *relatively* to the start\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 = viewportProvides.getBoundingRect();\n\n lastCenter = {\n x: e.center.x - contRect.origin.x,\n y: e.center.y - contRect.origin.y,\n };\n\n // put the transform-origin under the fingers so the preview feels right\n const innerRect = element.getBoundingClientRect();\n element.style.transformOrigin = `${e.center.x - innerRect.left}px ${e.center.y - innerRect.top}px`;\n\n // stop the browser's own pinch-zoom\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); // *only* CSS, no real zoom yet\n if (e.srcEvent?.cancelable) {\n e.srcEvent.preventDefault();\n e.srcEvent.stopPropagation();\n }\n };\n\n const pinchEnd = (e: HammerInput) => {\n // translate the relative hammer scale into a delta for requestZoomBy\n const delta = (e.scale - 1) * initialZoom;\n zoomProvides.requestZoomBy(delta, { vx: lastCenter.x, vy: lastCenter.y });\n\n resetTransform();\n initialZoom = 0;\n };\n\n // Async Hammer setup (internal)\n const setupHammer = async () => {\n try {\n const Hammer = (await import('hammerjs')).default;\n\n /* ------------------------------------------------------------------ */\n /* Hammer setup */\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', // allow scroll in every direction\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(); // Fire and forget\n\n // Return cleanup immediately\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';\n\nimport { setupPinchZoom } from '../utils/pinch-zoom-logic';\nimport { useZoomCapability } from './use-zoom';\n\nexport function usePinch() {\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({ element, viewportProvides, zoomProvides });\n }, [viewportProvides, zoomProvides]);\n\n return { elementRef };\n}\n","import { useEffect, useState } from '@framework';\nimport { Rect } from '@embedpdf/models';\n\nimport { useZoomCapability } from '../hooks/use-zoom';\n\ninterface MarqueeZoomProps {\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page */\n scale: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke / fill colours (defaults below) */\n stroke?: string;\n fill?: string;\n}\n\nexport const MarqueeZoom = ({\n pageIndex,\n scale,\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 [rect, setRect] = useState<Rect | null>(null);\n\n useEffect(() => {\n if (!zoomPlugin) return;\n return zoomPlugin.registerMarqueeOnPage({\n pageIndex,\n scale,\n callback: {\n onPreview: setRect,\n },\n });\n }, [zoomPlugin, pageIndex, scale]);\n\n if (!rect) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n pointerEvents: 'none',\n left: rect.origin.x * scale,\n top: rect.origin.y * scale,\n width: rect.size.width * scale,\n height: rect.size.height * scale,\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';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: CSSProperties;\n};\n\nexport function PinchWrapper({ children, style, ...props }: PinchWrapperProps) {\n const { elementRef } = usePinch();\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","viewportProvides","zoomProvides","window","hammer","initialZoom","lastCenter","x","y","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","className","stroke","fill","zoomPlugin","rect","setRect","useState","registerMarqueeOnPage","callback","onPreview","jsxRuntime","jsx","position","pointerEvents","width","size","height","border","background","boxSizing","children","props","ref","display","overflow","margin","state","setState","initialState","onStateChange","action","usePlugin"],"mappings":"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"}