@embedpdf/plugin-zoom 1.0.1 → 1.0.3

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.
@@ -1,6 +1,5 @@
1
- import * as _embedpdf_models from '@embedpdf/models';
2
1
  import * as _embedpdf_plugin_zoom from '@embedpdf/plugin-zoom';
3
- import { ZoomPlugin, ZoomLevel } from '@embedpdf/plugin-zoom';
2
+ import { ZoomPlugin, ZoomState } from '@embedpdf/plugin-zoom';
4
3
  import * as react from 'react';
5
4
  import { ReactNode } from 'react';
6
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -16,19 +15,8 @@ declare const useZoomPlugin: () => {
16
15
  ready: Promise<void>;
17
16
  };
18
17
  declare const useZoom: () => {
19
- currentZoomLevel: ZoomLevel;
20
- currentZoom: number;
21
- onZoomChange?: ((handler: (e: _embedpdf_plugin_zoom.ZoomChangeEvent) => void) => () => void) | undefined;
22
- requestZoom?: ((level: ZoomLevel, center?: _embedpdf_plugin_zoom.Point) => void) | undefined;
23
- requestZoomBy?: ((delta: number, center?: _embedpdf_plugin_zoom.Point) => void) | undefined;
24
- zoomIn?: (() => void) | undefined;
25
- zoomOut?: (() => void) | undefined;
26
- zoomToArea?: ((pageIndex: number, rect: _embedpdf_models.Rect) => void) | undefined;
27
- enableMarqueeZoom?: (() => void) | undefined;
28
- disableMarqueeZoom?: (() => void) | undefined;
29
- isMarqueeZoomActive?: (() => boolean) | undefined;
30
- getState?: (() => _embedpdf_plugin_zoom.ZoomState) | undefined;
31
- getPresets?: (() => _embedpdf_plugin_zoom.ZoomPreset[]) | undefined;
18
+ state: ZoomState;
19
+ provides: Readonly<_embedpdf_plugin_zoom.ZoomCapability> | null;
32
20
  };
33
21
 
34
22
  declare function usePinch(): {
@@ -1,23 +1,20 @@
1
1
  // src/react/hooks/use-zoom.ts
2
2
  import { useCapability, usePlugin } from "@embedpdf/core/react";
3
- import { ZoomPlugin } from "@embedpdf/plugin-zoom";
3
+ import { initialState, ZoomPlugin } from "@embedpdf/plugin-zoom";
4
4
  import { useEffect, useState } from "react";
5
5
  var useZoomCapability = () => useCapability(ZoomPlugin.id);
6
6
  var useZoomPlugin = () => usePlugin(ZoomPlugin.id);
7
7
  var useZoom = () => {
8
- const { provides: zoomCapability } = useZoomCapability();
9
- const [currentZoomLevel, setCurrentZoomLevel] = useState(1);
10
- const [currentZoom, setCurrentZoom] = useState(1);
8
+ const { provides } = useZoomCapability();
9
+ const [state, setState] = useState(initialState);
11
10
  useEffect(() => {
12
- return zoomCapability?.onZoomChange((action) => {
13
- setCurrentZoomLevel(action.level);
14
- setCurrentZoom(action.newZoom);
11
+ return provides?.onStateChange((action) => {
12
+ setState(action);
15
13
  });
16
- }, [zoomCapability]);
14
+ }, [provides]);
17
15
  return {
18
- ...zoomCapability,
19
- currentZoomLevel,
20
- currentZoom
16
+ state,
17
+ provides
21
18
  };
22
19
  };
23
20
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/hooks/use-zoom.ts","../../src/react/hooks/use-pinch-zoom.ts","../../src/react/components/pinch-wrapper.tsx","../../src/react/components/marquee-zoom.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/react';\nimport { ZoomLevel, ZoomPlugin } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from 'react';\n\nexport const useZoomCapability = () => useCapability<ZoomPlugin>(ZoomPlugin.id);\nexport const useZoomPlugin = () => usePlugin<ZoomPlugin>(ZoomPlugin.id);\n\nexport const useZoom = () => {\n const { provides: zoomCapability } = useZoomCapability();\n const [currentZoomLevel, setCurrentZoomLevel] = useState<ZoomLevel>(1);\n const [currentZoom, setCurrentZoom] = useState<number>(1);\n\n useEffect(() => {\n return zoomCapability?.onZoomChange((action) => {\n setCurrentZoomLevel(action.level);\n setCurrentZoom(action.newZoom);\n });\n }, [zoomCapability]);\n\n return {\n ...zoomCapability,\n currentZoomLevel,\n currentZoom,\n };\n};\n","import { useEffect, useRef } from 'react';\nimport { useCapability } from '@embedpdf/core/react';\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 } from 'react';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: React.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 'react';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/react';\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"],"mappings":"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","names":["useEffect","useCapability","useCapability","useEffect","useEffect","useRef","useState","jsx","useRef","useState","useEffect"]}
1
+ {"version":3,"sources":["../../src/react/hooks/use-zoom.ts","../../src/react/hooks/use-pinch-zoom.ts","../../src/react/components/pinch-wrapper.tsx","../../src/react/components/marquee-zoom.tsx"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/react';\nimport { initialState, ZoomPlugin, ZoomState } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from 'react';\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 'react';\nimport { useCapability } from '@embedpdf/core/react';\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 } from 'react';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ReactNode;\n style?: React.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 'react';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/react';\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"],"mappings":"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","names":["useEffect","useCapability","useCapability","useEffect","useEffect","useRef","useState","jsx","useRef","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embedpdf/plugin-zoom",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -24,26 +24,26 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "hammerjs": "^2.0.8",
27
- "@embedpdf/models": "1.0.1"
27
+ "@embedpdf/models": "1.0.3"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/hammerjs": "^2.0.46",
31
31
  "@types/react": "^18.2.0",
32
32
  "tsup": "^8.0.0",
33
33
  "typescript": "^5.0.0",
34
- "@embedpdf/core": "1.0.1",
35
- "@embedpdf/plugin-scroll": "1.0.1",
36
- "@embedpdf/plugin-interaction-manager": "1.0.1",
37
- "@embedpdf/plugin-viewport": "1.0.1"
34
+ "@embedpdf/plugin-viewport": "1.0.3",
35
+ "@embedpdf/core": "1.0.3",
36
+ "@embedpdf/plugin-scroll": "1.0.3",
37
+ "@embedpdf/plugin-interaction-manager": "1.0.3"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "react": ">=16.8.0",
41
41
  "react-dom": ">=16.8.0",
42
42
  "preact": "^10.26.4",
43
- "@embedpdf/plugin-viewport": "1.0.1",
44
- "@embedpdf/plugin-scroll": "1.0.1",
45
- "@embedpdf/plugin-interaction-manager": "1.0.1",
46
- "@embedpdf/core": "1.0.1"
43
+ "@embedpdf/core": "1.0.3",
44
+ "@embedpdf/plugin-viewport": "1.0.3",
45
+ "@embedpdf/plugin-scroll": "1.0.3",
46
+ "@embedpdf/plugin-interaction-manager": "1.0.3"
47
47
  },
48
48
  "files": [
49
49
  "dist",