@embedpdf/plugin-zoom 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -82,6 +82,21 @@ var manifest = {
82
82
  min: 4,
83
83
  max: 10,
84
84
  step: 0.8
85
+ },
86
+ {
87
+ min: 10,
88
+ max: 20,
89
+ step: 1.6
90
+ },
91
+ {
92
+ min: 20,
93
+ max: 40,
94
+ step: 3.2
95
+ },
96
+ {
97
+ min: 40,
98
+ max: 60,
99
+ step: 6.4
85
100
  }
86
101
  ],
87
102
  presets: [
@@ -184,7 +199,8 @@ var ZoomPlugin = class extends import_core.BasePlugin {
184
199
  /* internals */
185
200
  /* ------------------------------------------------------------------ */
186
201
  this.zoom$ = (0, import_core.createEmitter)();
187
- this.viewport = registry.getPlugin("viewport").provides();
202
+ this.viewportPlugin = registry.getPlugin("viewport");
203
+ this.viewport = this.viewportPlugin.provides();
188
204
  this.scroll = registry.getPlugin("scroll").provides();
189
205
  const interactionManager = registry.getPlugin("interaction-manager");
190
206
  this.interactionManager = interactionManager?.provides() ?? null;
@@ -194,7 +210,7 @@ var ZoomPlugin = class extends import_core.BasePlugin {
194
210
  this.presets = cfg.presets ?? [];
195
211
  this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);
196
212
  this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));
197
- this.viewport.onViewportChange(() => this.recalcAuto(1 /* Top */), {
213
+ this.viewport.onViewportResize(() => this.recalcAuto(1 /* Top */), {
198
214
  mode: "debounce",
199
215
  wait: 150
200
216
  });
@@ -297,7 +313,7 @@ var ZoomPlugin = class extends import_core.BasePlugin {
297
313
  align
298
314
  );
299
315
  if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {
300
- this.viewport.setViewportScrollMetrics({
316
+ this.viewportPlugin.setViewportScrollMetrics({
301
317
  scrollLeft: desiredScrollLeft,
302
318
  scrollTop: desiredScrollTop
303
319
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/lib/types.ts","../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/zoom-plugin.ts","../src/lib/index.ts"],"sourcesContent":["export * from './lib';\n","import { BasePluginConfig } from '@embedpdf/core';\nimport { Rect } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\n\n/* ------------------------------------------------------------------ */\n/* public */\n/* ------------------------------------------------------------------ */\n\nexport enum ZoomMode {\n Automatic = 'automatic',\n FitPage = 'fit-page',\n FitWidth = 'fit-width',\n}\n\nexport type ZoomLevel = ZoomMode | number;\n\nexport interface Point {\n vx: number;\n vy: number;\n}\n\nexport interface ZoomChangeEvent {\n /** old and new *actual* scale factors */\n oldZoom: number;\n newZoom: number;\n\n /** level used to obtain the newZoom (number | mode) */\n level: ZoomLevel;\n\n /** viewport point kept under the finger / mouse‑wheel focus */\n center: Point;\n\n /** where the viewport should scroll to after the scale change */\n desiredScrollLeft: number;\n desiredScrollTop: number;\n\n /** metrics at the moment the zoom was requested */\n viewport: ViewportMetrics;\n}\n\nexport interface ZoomCapability {\n /** subscribe – returns the unsubscribe function */\n onZoomChange(handler: (e: ZoomChangeEvent) => void): () => void;\n\n /** absolute requests -------------------------------------------------- */\n requestZoom(level: ZoomLevel, center?: Point): void;\n /** relative requests -------------------------------------------------- */\n requestZoomBy(delta: number, center?: Point): void;\n\n /** absolute requests -------------------------------------------------- */\n zoomIn(): void;\n zoomOut(): void;\n\n zoomToArea(pageIndex: number, rect: Rect): void;\n\n /** zoom in on an area -------------------------------------------------- */\n enableMarqueeZoom(): void;\n disableMarqueeZoom(): void;\n isMarqueeZoomActive(): boolean;\n\n getState(): ZoomState;\n getPresets(): ZoomPreset[];\n}\n\n/* ------------------------------------------------------------------ */\n/* config / store */\n/* ------------------------------------------------------------------ */\n\nexport interface ZoomRangeStep {\n min: number;\n max: number;\n step: number;\n}\n\nexport interface ZoomPreset {\n name: string;\n value: ZoomLevel;\n icon?: string;\n}\n\nexport interface ZoomPluginConfig extends BasePluginConfig {\n defaultZoomLevel: ZoomLevel;\n minZoom?: number;\n maxZoom?: number;\n zoomStep?: number;\n zoomRanges?: ZoomRangeStep[]; // Define different step sizes for different zoom ranges\n presets?: ZoomPreset[]; // Preset zoom options for dropdown\n}\n\nexport interface ZoomState {\n zoomLevel: ZoomLevel; // last **requested** level\n currentZoomLevel: number; // actual numeric factor\n zoomReady: boolean; // whether the zoom is ready to be used\n}\n\nexport enum VerticalZoomFocus {\n Center,\n Top,\n}\n\nexport interface ZoomRequest {\n level: ZoomLevel;\n delta?: number;\n center?: Point;\n focus?: VerticalZoomFocus;\n /** Scroll so that the focal point ends up …\n * ▸ `\"keep\"` (default) at the same viewport coords\n * ▸ `\"center\"` centred in the viewport */\n align?: 'keep' | 'center';\n}\n","import { PluginManifest } from '@embedpdf/core';\n\nimport { ZoomMode, ZoomPluginConfig } from './types';\n\nexport const ZOOM_PLUGIN_ID = 'zoom';\n\nexport const manifest: PluginManifest<ZoomPluginConfig> = {\n id: ZOOM_PLUGIN_ID,\n name: 'Zoom Plugin',\n version: '1.0.0',\n provides: ['zoom'],\n requires: ['viewport', 'scroll'],\n optional: ['interaction-manager'],\n defaultConfig: {\n enabled: true,\n defaultZoomLevel: ZoomMode.Automatic,\n minZoom: 0.2,\n maxZoom: 60,\n zoomStep: 0.1,\n zoomRanges: [\n {\n min: 0.2,\n max: 0.5,\n step: 0.05,\n },\n {\n min: 0.5,\n max: 1.0,\n step: 0.1,\n },\n {\n min: 1.0,\n max: 2.0,\n step: 0.2,\n },\n {\n min: 2.0,\n max: 4.0,\n step: 0.4,\n },\n {\n min: 4.0,\n max: 10.0,\n step: 0.8,\n },\n ],\n presets: [\n {\n name: 'Fit Page',\n value: ZoomMode.FitPage,\n },\n {\n name: 'Fit Width',\n value: ZoomMode.FitWidth,\n },\n {\n name: 'Automatic',\n value: ZoomMode.Automatic,\n },\n {\n name: '10%',\n value: 0.1,\n },\n {\n name: '20%',\n value: 0.2,\n },\n {\n name: '30%',\n value: 0.3,\n },\n {\n name: '50%',\n value: 0.5,\n },\n {\n name: '70%',\n value: 0.7,\n },\n {\n name: '100%',\n value: 1,\n },\n {\n name: '200%',\n value: 2,\n },\n {\n name: '400%',\n value: 4,\n },\n ],\n },\n};\n","import { Action } from '@embedpdf/core';\n\nimport { ZoomLevel } from './types';\n\n// Action Types\nexport const SET_ZOOM_LEVEL = 'SET_ZOOM_LEVEL';\nexport const SET_INITIAL_ZOOM_LEVEL = 'SET_INITIAL_ZOOM_LEVEL';\n\n// Action Interfaces\nexport interface SetZoomLevelAction extends Action {\n type: typeof SET_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n currentZoomLevel: number;\n };\n}\n\nexport interface SetInitialZoomLevelAction extends Action {\n type: typeof SET_INITIAL_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n };\n}\n\n// Union Type for All Actions\nexport type ZoomAction = SetZoomLevelAction | SetInitialZoomLevelAction;\n\n// Action Creators\nexport function setZoomLevel(zoomLevel: ZoomLevel, currentZoomLevel: number): SetZoomLevelAction {\n return {\n type: SET_ZOOM_LEVEL,\n payload: { zoomLevel, currentZoomLevel },\n };\n}\n\nexport function setInitialZoomLevel(zoomLevel: ZoomLevel): SetInitialZoomLevelAction {\n return {\n type: SET_INITIAL_ZOOM_LEVEL,\n payload: { zoomLevel },\n };\n}\n","import { Reducer } from '@embedpdf/core';\n\nimport { SET_INITIAL_ZOOM_LEVEL, SET_ZOOM_LEVEL, ZoomAction } from './actions';\nimport { ZoomState, ZoomMode } from './types';\n\nexport const initialState: ZoomState = {\n zoomLevel: ZoomMode.Automatic,\n currentZoomLevel: 1,\n zoomReady: false,\n};\n\nexport const zoomReducer: Reducer<ZoomState, ZoomAction> = (state = initialState, action) => {\n switch (action.type) {\n case SET_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n currentZoomLevel: action.payload.currentZoomLevel,\n zoomReady: true,\n };\n case SET_INITIAL_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n };\n default:\n return state;\n }\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createEmitter,\n clamp,\n setScale,\n SET_PAGES,\n SET_DOCUMENT,\n getPagesWithRotatedSize,\n SET_ROTATION,\n} from '@embedpdf/core';\nimport { ScrollPlugin, ScrollCapability } from '@embedpdf/plugin-scroll';\nimport { ViewportPlugin, ViewportCapability, ViewportMetrics } from '@embedpdf/plugin-viewport';\n\nimport { setInitialZoomLevel, setZoomLevel, ZoomAction } from './actions';\nimport {\n ZoomPluginConfig,\n ZoomState,\n ZoomMode,\n Point,\n ZoomChangeEvent,\n ZoomCapability,\n ZoomPreset,\n ZoomRangeStep,\n VerticalZoomFocus,\n ZoomRequest,\n} from './types';\nimport {\n InteractionManagerCapability,\n InteractionManagerPlugin,\n} from '@embedpdf/plugin-interaction-manager';\nimport { Rect } from '@embedpdf/models';\n\nexport class ZoomPlugin extends BasePlugin<\n ZoomPluginConfig,\n ZoomCapability,\n ZoomState,\n ZoomAction\n> {\n static readonly id = 'zoom' as const;\n /* ------------------------------------------------------------------ */\n /* internals */\n /* ------------------------------------------------------------------ */\n private readonly zoom$ = createEmitter<ZoomChangeEvent>();\n private readonly viewport: ViewportCapability;\n private readonly scroll: ScrollCapability;\n private readonly interactionManager: InteractionManagerCapability | null;\n private readonly presets: ZoomPreset[];\n private readonly zoomRanges: ZoomRangeStep[];\n\n private readonly minZoom: number;\n private readonly maxZoom: number;\n private readonly zoomStep: number;\n\n /* ------------------------------------------------------------------ */\n constructor(id: string, registry: PluginRegistry, cfg: ZoomPluginConfig) {\n super(id, registry);\n\n this.viewport = registry.getPlugin<ViewportPlugin>('viewport')!.provides();\n this.scroll = registry.getPlugin<ScrollPlugin>('scroll')!.provides();\n const interactionManager = registry.getPlugin<InteractionManagerPlugin>('interaction-manager');\n this.interactionManager = interactionManager?.provides() ?? null;\n this.minZoom = cfg.minZoom ?? 0.25;\n this.maxZoom = cfg.maxZoom ?? 10;\n this.zoomStep = cfg.zoomStep ?? 0.1;\n this.presets = cfg.presets ?? [];\n this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);\n this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));\n /* keep \"automatic\" modes up to date -------------------------------- */\n this.viewport.onViewportChange(() => this.recalcAuto(VerticalZoomFocus.Top), {\n mode: 'debounce',\n wait: 150,\n });\n this.coreStore.onAction(SET_ROTATION, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_PAGES, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_DOCUMENT, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.interactionManager?.registerMode({\n id: 'marqueeZoom',\n scope: 'page',\n exclusive: true,\n cursor: 'zoom-in',\n });\n this.resetReady();\n }\n\n /* ------------------------------------------------------------------ */\n /* capability */\n /* ------------------------------------------------------------------ */\n protected buildCapability(): ZoomCapability {\n return {\n onZoomChange: this.zoom$.on,\n zoomIn: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: this.stepFor(cur) });\n },\n zoomOut: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: -this.stepFor(cur) });\n },\n zoomToArea: (pageIndex, rect) => this.handleZoomToArea(pageIndex, rect),\n requestZoom: (level, c) => this.handleRequest({ level, center: c }),\n requestZoomBy: (d, c) => {\n const cur = this.state.currentZoomLevel;\n const target = this.toZoom(cur + d);\n return this.handleRequest({ level: target, center: c });\n },\n enableMarqueeZoom: () => {\n this.interactionManager?.activate('marqueeZoom');\n },\n disableMarqueeZoom: () => {\n this.interactionManager?.activate('default');\n },\n isMarqueeZoomActive: () => this.interactionManager?.getActiveMode() === 'marqueeZoom',\n getState: () => this.state,\n getPresets: () => this.presets,\n };\n }\n\n /* ------------------------------------------------------------------ */\n /* plugin life‑cycle */\n /* ------------------------------------------------------------------ */\n async initialize(): Promise<void> {\n /* apply the initial zoom */\n }\n\n async destroy() {\n this.zoom$.clear();\n }\n\n /**\n * Sort ranges once, make sure they are sane\n */\n private normalizeRanges(ranges: ZoomRangeStep[]): ZoomRangeStep[] {\n return [...ranges]\n .filter((r) => r.step > 0 && r.max > r.min) // basic sanity\n .sort((a, b) => a.min - b.min);\n }\n\n /** pick the step that applies to a given numeric zoom */\n private stepFor(zoom: number): number {\n const r = this.zoomRanges.find((r) => zoom >= r.min && zoom < r.max);\n return r ? r.step : this.zoomStep; // fallback\n }\n\n /** clamp + round helper reused later */\n private toZoom(v: number) {\n return parseFloat(clamp(v, this.minZoom, this.maxZoom).toFixed(2));\n }\n\n /* ------------------------------------------------------------------ */\n /* main entry – handles **every** zoom request */\n /* ------------------------------------------------------------------ */\n private handleRequest({\n level,\n delta = 0,\n center,\n focus = VerticalZoomFocus.Center,\n align = 'keep',\n }: ZoomRequest) {\n const metrics = this.viewport.getMetrics();\n const oldZoom = this.state.currentZoomLevel;\n\n if (metrics.clientWidth === 0 || metrics.clientHeight === 0) {\n return;\n }\n\n /* ------------------------------------------------------------------ */\n /* step 1 – resolve the **target numeric zoom** */\n /* ------------------------------------------------------------------ */\n const base = typeof level === 'number' ? level : this.computeZoomForMode(level, metrics);\n\n if (base === false) {\n return;\n }\n const exactZoom = clamp(base + delta, this.minZoom, this.maxZoom);\n const newZoom = Math.floor(exactZoom * 100) / 100;\n\n /* ------------------------------------------------------------------ */\n /* step 2 – figure out the viewport point we should keep under focus */\n /* ------------------------------------------------------------------ */\n const focusPoint: Point = center ?? {\n vx: metrics.clientWidth / 2,\n vy: focus === VerticalZoomFocus.Top ? 0 : metrics.clientHeight / 2,\n };\n\n /* ------------------------------------------------------------------ */\n /* step 3 – translate that into desired scroll offsets */\n /* ------------------------------------------------------------------ */\n const { desiredScrollLeft, desiredScrollTop } = this.computeScrollForZoomChange(\n metrics,\n oldZoom,\n newZoom,\n focusPoint,\n align,\n );\n\n /* ------------------------------------------------------------------ */\n /* step 4 – dispatch + notify */\n /* ------------------------------------------------------------------ */\n\n if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {\n this.viewport.setViewportScrollMetrics({\n scrollLeft: desiredScrollLeft,\n scrollTop: desiredScrollTop,\n });\n }\n\n this.dispatch(setZoomLevel(typeof level === 'number' ? newZoom : level, newZoom));\n this.dispatchCoreAction(setScale(newZoom));\n this.markReady();\n\n this.viewport.scrollTo({\n x: desiredScrollLeft,\n y: desiredScrollTop,\n behavior: 'instant',\n });\n\n const evt: ZoomChangeEvent = {\n oldZoom,\n newZoom,\n level,\n center: focusPoint,\n desiredScrollLeft,\n desiredScrollTop,\n viewport: metrics,\n };\n\n this.zoom$.emit(evt);\n }\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n\n /** numeric zoom for Automatic / FitPage / FitWidth */\n private computeZoomForMode(mode: ZoomMode, vp: ViewportMetrics): number | false {\n const spreads = getPagesWithRotatedSize(this.coreState.core);\n if (!spreads.length) return false;\n\n const pgGap = this.scroll.getPageGap();\n const vpGap = this.viewport.getViewportGap();\n\n if (vp.clientWidth === 0 || vp.clientHeight === 0) {\n return false;\n }\n\n // Available space after accounting for fixed viewport gaps\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n if (availableWidth <= 0 || availableHeight <= 0) {\n return false;\n }\n\n let maxContentW = 0,\n maxContentH = 0;\n\n spreads.forEach((spread) => {\n // Only include scalable content (pages + page gaps), not viewport gaps\n const contentW = spread.reduce((s, p, i) => s + p.rotatedSize.width + (i ? pgGap : 0), 0);\n const contentH = Math.max(...spread.map((p) => p.rotatedSize.height));\n maxContentW = Math.max(maxContentW, contentW);\n maxContentH = Math.max(maxContentH, contentH);\n });\n\n switch (mode) {\n case ZoomMode.FitWidth:\n return availableWidth / maxContentW;\n case ZoomMode.FitPage:\n return Math.min(availableWidth / maxContentW, availableHeight / maxContentH);\n case ZoomMode.Automatic:\n return Math.min(availableWidth / maxContentW, 1);\n /* istanbul ignore next */\n default:\n return 1;\n }\n }\n\n /** where to scroll so that *focus* stays stable after scaling */\n private computeScrollForZoomChange(\n vp: ViewportMetrics,\n oldZoom: number,\n newZoom: number,\n focus: Point,\n align: 'keep' | 'center' = 'keep',\n ) {\n /* unscaled content size ------------------------------------------- */\n const layout = this.scroll.getLayout();\n const vpGap = this.viewport.getViewportGap();\n\n const contentW = layout.totalContentSize.width;\n const contentH = layout.totalContentSize.height;\n\n // Available space for content (excluding fixed viewport gaps)\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n /* helper: offset if content is narrower than available space ------- */\n const off = (availableSpace: number, cw: number, zoom: number) =>\n cw * zoom < availableSpace ? (availableSpace - cw * zoom) / 2 : 0;\n\n const offXold = off(availableWidth, contentW, oldZoom);\n const offYold = off(availableHeight, contentH, oldZoom);\n\n const offXnew = off(availableWidth, contentW, newZoom);\n const offYnew = off(availableHeight, contentH, newZoom);\n\n /* content coords of the focal point -------------------------------- */\n // Adjust focus point to account for vpGap and centering offset\n const cx = (vp.scrollLeft + focus.vx - vpGap - offXold) / oldZoom;\n const cy = (vp.scrollTop + focus.vy - vpGap - offYold) / oldZoom;\n\n /* new scroll so that (cx,cy) appears under focus again ------------- */\n const baseLeft = cx * newZoom + vpGap + offXnew;\n const baseTop = cy * newZoom + vpGap + offYnew;\n\n const desiredScrollLeft =\n align === 'center' ? baseLeft - vp.clientWidth / 2 : baseLeft - focus.vx;\n const desiredScrollTop =\n align === 'center' ? baseTop - vp.clientHeight / 2 : baseTop - focus.vy;\n\n return {\n desiredScrollLeft: Math.max(0, desiredScrollLeft),\n desiredScrollTop: Math.max(0, desiredScrollTop),\n };\n }\n\n private handleZoomToArea(pageIndex: number, rect: Rect) {\n const vp = this.viewport.getMetrics();\n const vpGap = this.viewport.getViewportGap();\n const oldZ = this.state.currentZoomLevel;\n\n const availableW = vp.clientWidth - 2 * vpGap;\n const availableH = vp.clientHeight - 2 * vpGap;\n\n /* 1 – numeric zoom so the rect fits -------------------------------- */\n const targetZoom = this.toZoom(\n Math.min(availableW / rect.size.width, availableH / rect.size.height),\n );\n\n /* 2 — absolute page position in content coordinates --------------- */\n const layout = this.scroll.getLayout();\n\n // the VirtualItem that actually contains the requested page\n const vItem = layout.virtualItems.find((it) =>\n it.pageLayouts.some((p) => p.pageIndex === pageIndex),\n );\n if (!vItem) return;\n\n // the page-layout _inside_ that virtual item\n const pageRel = vItem.pageLayouts.find((p) => p.pageIndex === pageIndex)!;\n\n /* add the virtual-item's own offset to get absolute coords */\n const pageAbsX = vItem.x + pageRel.x;\n const pageAbsY = vItem.y + pageRel.y;\n\n /* 3 — centre of the marquee in content space ---------------------- */\n const cxContent = pageAbsX + rect.origin.x + rect.size.width / 2;\n const cyContent = pageAbsY + rect.origin.y + rect.size.height / 2;\n\n /* 4 – viewport coords of that centre *before* zoom ----------------- */\n const off = (avail: number, cw: number, z: number) =>\n cw * z < avail ? (avail - cw * z) / 2 : 0;\n\n const offXold = off(availableW, layout.totalContentSize.width, oldZ);\n const offYold = off(availableH, layout.totalContentSize.height, oldZ);\n\n const centerVX = vpGap + offXold + cxContent * oldZ - vp.scrollLeft;\n const centerVY = vpGap + offYold + cyContent * oldZ - vp.scrollTop;\n\n /* 4 – reuse the generic handler ------------------------------------ */\n this.handleRequest({\n level: targetZoom,\n center: { vx: centerVX, vy: centerVY },\n align: 'center',\n });\n }\n\n /** recalculates Automatic / Fit* when viewport or pages change */\n private recalcAuto(focus?: VerticalZoomFocus) {\n const s = this.state;\n if (\n s.zoomLevel === ZoomMode.Automatic ||\n s.zoomLevel === ZoomMode.FitPage ||\n s.zoomLevel === ZoomMode.FitWidth\n )\n this.handleRequest({ level: s.zoomLevel, focus });\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\n\nimport { ZoomAction } from './actions';\nimport { manifest, ZOOM_PLUGIN_ID } from './manifest';\nimport { zoomReducer, initialState } from './reducer';\nimport { ZoomPluginConfig, ZoomState } from './types';\nimport { ZoomPlugin } from './zoom-plugin';\n\nexport const ZoomPluginPackage: PluginPackage<ZoomPlugin, ZoomPluginConfig, ZoomState, ZoomAction> =\n {\n manifest,\n create: (registry, _engine, config) => new ZoomPlugin(ZOOM_PLUGIN_ID, registry, config),\n reducer: zoomReducer,\n initialState,\n };\n\nexport * from './zoom-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"mappings":"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","names":["ZoomMode","VerticalZoomFocus","r"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/types.ts","../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/zoom-plugin.ts","../src/lib/index.ts"],"sourcesContent":["export * from './lib';\n","import { BasePluginConfig } from '@embedpdf/core';\nimport { Rect } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\n\n/* ------------------------------------------------------------------ */\n/* public */\n/* ------------------------------------------------------------------ */\n\nexport enum ZoomMode {\n Automatic = 'automatic',\n FitPage = 'fit-page',\n FitWidth = 'fit-width',\n}\n\nexport type ZoomLevel = ZoomMode | number;\n\nexport interface Point {\n vx: number;\n vy: number;\n}\n\nexport interface ZoomChangeEvent {\n /** old and new *actual* scale factors */\n oldZoom: number;\n newZoom: number;\n\n /** level used to obtain the newZoom (number | mode) */\n level: ZoomLevel;\n\n /** viewport point kept under the finger / mouse‑wheel focus */\n center: Point;\n\n /** where the viewport should scroll to after the scale change */\n desiredScrollLeft: number;\n desiredScrollTop: number;\n\n /** metrics at the moment the zoom was requested */\n viewport: ViewportMetrics;\n}\n\nexport interface ZoomCapability {\n /** subscribe – returns the unsubscribe function */\n onZoomChange(handler: (e: ZoomChangeEvent) => void): () => void;\n\n /** absolute requests -------------------------------------------------- */\n requestZoom(level: ZoomLevel, center?: Point): void;\n /** relative requests -------------------------------------------------- */\n requestZoomBy(delta: number, center?: Point): void;\n\n /** absolute requests -------------------------------------------------- */\n zoomIn(): void;\n zoomOut(): void;\n\n zoomToArea(pageIndex: number, rect: Rect): void;\n\n /** zoom in on an area -------------------------------------------------- */\n enableMarqueeZoom(): void;\n disableMarqueeZoom(): void;\n isMarqueeZoomActive(): boolean;\n\n getState(): ZoomState;\n getPresets(): ZoomPreset[];\n}\n\n/* ------------------------------------------------------------------ */\n/* config / store */\n/* ------------------------------------------------------------------ */\n\nexport interface ZoomRangeStep {\n min: number;\n max: number;\n step: number;\n}\n\nexport interface ZoomPreset {\n name: string;\n value: ZoomLevel;\n icon?: string;\n}\n\nexport interface ZoomPluginConfig extends BasePluginConfig {\n defaultZoomLevel: ZoomLevel;\n minZoom?: number;\n maxZoom?: number;\n zoomStep?: number;\n zoomRanges?: ZoomRangeStep[]; // Define different step sizes for different zoom ranges\n presets?: ZoomPreset[]; // Preset zoom options for dropdown\n}\n\nexport interface ZoomState {\n zoomLevel: ZoomLevel; // last **requested** level\n currentZoomLevel: number; // actual numeric factor\n zoomReady: boolean; // whether the zoom is ready to be used\n}\n\nexport enum VerticalZoomFocus {\n Center,\n Top,\n}\n\nexport interface ZoomRequest {\n level: ZoomLevel;\n delta?: number;\n center?: Point;\n focus?: VerticalZoomFocus;\n /** Scroll so that the focal point ends up …\n * ▸ `\"keep\"` (default) at the same viewport coords\n * ▸ `\"center\"` centred in the viewport */\n align?: 'keep' | 'center';\n}\n","import { PluginManifest } from '@embedpdf/core';\n\nimport { ZoomMode, ZoomPluginConfig } from './types';\n\nexport const ZOOM_PLUGIN_ID = 'zoom';\n\nexport const manifest: PluginManifest<ZoomPluginConfig> = {\n id: ZOOM_PLUGIN_ID,\n name: 'Zoom Plugin',\n version: '1.0.0',\n provides: ['zoom'],\n requires: ['viewport', 'scroll'],\n optional: ['interaction-manager'],\n defaultConfig: {\n enabled: true,\n defaultZoomLevel: ZoomMode.Automatic,\n minZoom: 0.2,\n maxZoom: 60,\n zoomStep: 0.1,\n zoomRanges: [\n {\n min: 0.2,\n max: 0.5,\n step: 0.05,\n },\n {\n min: 0.5,\n max: 1.0,\n step: 0.1,\n },\n {\n min: 1.0,\n max: 2.0,\n step: 0.2,\n },\n {\n min: 2.0,\n max: 4.0,\n step: 0.4,\n },\n {\n min: 4.0,\n max: 10.0,\n step: 0.8,\n },\n {\n min: 10.0,\n max: 20.0,\n step: 1.6,\n },\n {\n min: 20.0,\n max: 40.0,\n step: 3.2,\n },\n {\n min: 40.0,\n max: 60.0,\n step: 6.4,\n },\n ],\n presets: [\n {\n name: 'Fit Page',\n value: ZoomMode.FitPage,\n },\n {\n name: 'Fit Width',\n value: ZoomMode.FitWidth,\n },\n {\n name: 'Automatic',\n value: ZoomMode.Automatic,\n },\n {\n name: '10%',\n value: 0.1,\n },\n {\n name: '20%',\n value: 0.2,\n },\n {\n name: '30%',\n value: 0.3,\n },\n {\n name: '50%',\n value: 0.5,\n },\n {\n name: '70%',\n value: 0.7,\n },\n {\n name: '100%',\n value: 1,\n },\n {\n name: '200%',\n value: 2,\n },\n {\n name: '400%',\n value: 4,\n },\n ],\n },\n};\n","import { Action } from '@embedpdf/core';\n\nimport { ZoomLevel } from './types';\n\n// Action Types\nexport const SET_ZOOM_LEVEL = 'SET_ZOOM_LEVEL';\nexport const SET_INITIAL_ZOOM_LEVEL = 'SET_INITIAL_ZOOM_LEVEL';\n\n// Action Interfaces\nexport interface SetZoomLevelAction extends Action {\n type: typeof SET_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n currentZoomLevel: number;\n };\n}\n\nexport interface SetInitialZoomLevelAction extends Action {\n type: typeof SET_INITIAL_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n };\n}\n\n// Union Type for All Actions\nexport type ZoomAction = SetZoomLevelAction | SetInitialZoomLevelAction;\n\n// Action Creators\nexport function setZoomLevel(zoomLevel: ZoomLevel, currentZoomLevel: number): SetZoomLevelAction {\n return {\n type: SET_ZOOM_LEVEL,\n payload: { zoomLevel, currentZoomLevel },\n };\n}\n\nexport function setInitialZoomLevel(zoomLevel: ZoomLevel): SetInitialZoomLevelAction {\n return {\n type: SET_INITIAL_ZOOM_LEVEL,\n payload: { zoomLevel },\n };\n}\n","import { Reducer } from '@embedpdf/core';\n\nimport { SET_INITIAL_ZOOM_LEVEL, SET_ZOOM_LEVEL, ZoomAction } from './actions';\nimport { ZoomState, ZoomMode } from './types';\n\nexport const initialState: ZoomState = {\n zoomLevel: ZoomMode.Automatic,\n currentZoomLevel: 1,\n zoomReady: false,\n};\n\nexport const zoomReducer: Reducer<ZoomState, ZoomAction> = (state = initialState, action) => {\n switch (action.type) {\n case SET_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n currentZoomLevel: action.payload.currentZoomLevel,\n zoomReady: true,\n };\n case SET_INITIAL_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n };\n default:\n return state;\n }\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createEmitter,\n clamp,\n setScale,\n SET_PAGES,\n SET_DOCUMENT,\n getPagesWithRotatedSize,\n SET_ROTATION,\n} from '@embedpdf/core';\nimport { ScrollPlugin, ScrollCapability } from '@embedpdf/plugin-scroll';\nimport { ViewportPlugin, ViewportCapability, ViewportMetrics } from '@embedpdf/plugin-viewport';\n\nimport { setInitialZoomLevel, setZoomLevel, ZoomAction } from './actions';\nimport {\n ZoomPluginConfig,\n ZoomState,\n ZoomMode,\n Point,\n ZoomChangeEvent,\n ZoomCapability,\n ZoomPreset,\n ZoomRangeStep,\n VerticalZoomFocus,\n ZoomRequest,\n} from './types';\nimport {\n InteractionManagerCapability,\n InteractionManagerPlugin,\n} from '@embedpdf/plugin-interaction-manager';\nimport { Rect } from '@embedpdf/models';\n\nexport class ZoomPlugin extends BasePlugin<\n ZoomPluginConfig,\n ZoomCapability,\n ZoomState,\n ZoomAction\n> {\n static readonly id = 'zoom' as const;\n /* ------------------------------------------------------------------ */\n /* internals */\n /* ------------------------------------------------------------------ */\n private readonly zoom$ = createEmitter<ZoomChangeEvent>();\n private readonly viewport: ViewportCapability;\n private readonly viewportPlugin: ViewportPlugin;\n private readonly scroll: ScrollCapability;\n private readonly interactionManager: InteractionManagerCapability | null;\n private readonly presets: ZoomPreset[];\n private readonly zoomRanges: ZoomRangeStep[];\n\n private readonly minZoom: number;\n private readonly maxZoom: number;\n private readonly zoomStep: number;\n\n /* ------------------------------------------------------------------ */\n constructor(id: string, registry: PluginRegistry, cfg: ZoomPluginConfig) {\n super(id, registry);\n\n this.viewportPlugin = registry.getPlugin<ViewportPlugin>('viewport')!;\n this.viewport = this.viewportPlugin.provides();\n this.scroll = registry.getPlugin<ScrollPlugin>('scroll')!.provides();\n const interactionManager = registry.getPlugin<InteractionManagerPlugin>('interaction-manager');\n this.interactionManager = interactionManager?.provides() ?? null;\n this.minZoom = cfg.minZoom ?? 0.25;\n this.maxZoom = cfg.maxZoom ?? 10;\n this.zoomStep = cfg.zoomStep ?? 0.1;\n this.presets = cfg.presets ?? [];\n this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);\n this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));\n /* keep \"automatic\" modes up to date -------------------------------- */\n this.viewport.onViewportResize(() => this.recalcAuto(VerticalZoomFocus.Top), {\n mode: 'debounce',\n wait: 150,\n });\n this.coreStore.onAction(SET_ROTATION, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_PAGES, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_DOCUMENT, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.interactionManager?.registerMode({\n id: 'marqueeZoom',\n scope: 'page',\n exclusive: true,\n cursor: 'zoom-in',\n });\n this.resetReady();\n }\n\n /* ------------------------------------------------------------------ */\n /* capability */\n /* ------------------------------------------------------------------ */\n protected buildCapability(): ZoomCapability {\n return {\n onZoomChange: this.zoom$.on,\n zoomIn: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: this.stepFor(cur) });\n },\n zoomOut: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: -this.stepFor(cur) });\n },\n zoomToArea: (pageIndex, rect) => this.handleZoomToArea(pageIndex, rect),\n requestZoom: (level, c) => this.handleRequest({ level, center: c }),\n requestZoomBy: (d, c) => {\n const cur = this.state.currentZoomLevel;\n const target = this.toZoom(cur + d);\n return this.handleRequest({ level: target, center: c });\n },\n enableMarqueeZoom: () => {\n this.interactionManager?.activate('marqueeZoom');\n },\n disableMarqueeZoom: () => {\n this.interactionManager?.activate('default');\n },\n isMarqueeZoomActive: () => this.interactionManager?.getActiveMode() === 'marqueeZoom',\n getState: () => this.state,\n getPresets: () => this.presets,\n };\n }\n\n /* ------------------------------------------------------------------ */\n /* plugin life‑cycle */\n /* ------------------------------------------------------------------ */\n async initialize(): Promise<void> {\n /* apply the initial zoom */\n }\n\n async destroy() {\n this.zoom$.clear();\n }\n\n /**\n * Sort ranges once, make sure they are sane\n */\n private normalizeRanges(ranges: ZoomRangeStep[]): ZoomRangeStep[] {\n return [...ranges]\n .filter((r) => r.step > 0 && r.max > r.min) // basic sanity\n .sort((a, b) => a.min - b.min);\n }\n\n /** pick the step that applies to a given numeric zoom */\n private stepFor(zoom: number): number {\n const r = this.zoomRanges.find((r) => zoom >= r.min && zoom < r.max);\n return r ? r.step : this.zoomStep; // fallback\n }\n\n /** clamp + round helper reused later */\n private toZoom(v: number) {\n return parseFloat(clamp(v, this.minZoom, this.maxZoom).toFixed(2));\n }\n\n /* ------------------------------------------------------------------ */\n /* main entry – handles **every** zoom request */\n /* ------------------------------------------------------------------ */\n private handleRequest({\n level,\n delta = 0,\n center,\n focus = VerticalZoomFocus.Center,\n align = 'keep',\n }: ZoomRequest) {\n const metrics = this.viewport.getMetrics();\n const oldZoom = this.state.currentZoomLevel;\n\n if (metrics.clientWidth === 0 || metrics.clientHeight === 0) {\n return;\n }\n\n /* ------------------------------------------------------------------ */\n /* step 1 – resolve the **target numeric zoom** */\n /* ------------------------------------------------------------------ */\n const base = typeof level === 'number' ? level : this.computeZoomForMode(level, metrics);\n\n if (base === false) {\n return;\n }\n const exactZoom = clamp(base + delta, this.minZoom, this.maxZoom);\n const newZoom = Math.floor(exactZoom * 100) / 100;\n\n /* ------------------------------------------------------------------ */\n /* step 2 – figure out the viewport point we should keep under focus */\n /* ------------------------------------------------------------------ */\n const focusPoint: Point = center ?? {\n vx: metrics.clientWidth / 2,\n vy: focus === VerticalZoomFocus.Top ? 0 : metrics.clientHeight / 2,\n };\n\n /* ------------------------------------------------------------------ */\n /* step 3 – translate that into desired scroll offsets */\n /* ------------------------------------------------------------------ */\n const { desiredScrollLeft, desiredScrollTop } = this.computeScrollForZoomChange(\n metrics,\n oldZoom,\n newZoom,\n focusPoint,\n align,\n );\n\n /* ------------------------------------------------------------------ */\n /* step 4 – dispatch + notify */\n /* ------------------------------------------------------------------ */\n\n if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {\n this.viewportPlugin.setViewportScrollMetrics({\n scrollLeft: desiredScrollLeft,\n scrollTop: desiredScrollTop,\n });\n }\n\n this.dispatch(setZoomLevel(typeof level === 'number' ? newZoom : level, newZoom));\n this.dispatchCoreAction(setScale(newZoom));\n this.markReady();\n\n this.viewport.scrollTo({\n x: desiredScrollLeft,\n y: desiredScrollTop,\n behavior: 'instant',\n });\n\n const evt: ZoomChangeEvent = {\n oldZoom,\n newZoom,\n level,\n center: focusPoint,\n desiredScrollLeft,\n desiredScrollTop,\n viewport: metrics,\n };\n\n this.zoom$.emit(evt);\n }\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n\n /** numeric zoom for Automatic / FitPage / FitWidth */\n private computeZoomForMode(mode: ZoomMode, vp: ViewportMetrics): number | false {\n const spreads = getPagesWithRotatedSize(this.coreState.core);\n if (!spreads.length) return false;\n\n const pgGap = this.scroll.getPageGap();\n const vpGap = this.viewport.getViewportGap();\n\n if (vp.clientWidth === 0 || vp.clientHeight === 0) {\n return false;\n }\n\n // Available space after accounting for fixed viewport gaps\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n if (availableWidth <= 0 || availableHeight <= 0) {\n return false;\n }\n\n let maxContentW = 0,\n maxContentH = 0;\n\n spreads.forEach((spread) => {\n // Only include scalable content (pages + page gaps), not viewport gaps\n const contentW = spread.reduce((s, p, i) => s + p.rotatedSize.width + (i ? pgGap : 0), 0);\n const contentH = Math.max(...spread.map((p) => p.rotatedSize.height));\n maxContentW = Math.max(maxContentW, contentW);\n maxContentH = Math.max(maxContentH, contentH);\n });\n\n switch (mode) {\n case ZoomMode.FitWidth:\n return availableWidth / maxContentW;\n case ZoomMode.FitPage:\n return Math.min(availableWidth / maxContentW, availableHeight / maxContentH);\n case ZoomMode.Automatic:\n return Math.min(availableWidth / maxContentW, 1);\n /* istanbul ignore next */\n default:\n return 1;\n }\n }\n\n /** where to scroll so that *focus* stays stable after scaling */\n private computeScrollForZoomChange(\n vp: ViewportMetrics,\n oldZoom: number,\n newZoom: number,\n focus: Point,\n align: 'keep' | 'center' = 'keep',\n ) {\n /* unscaled content size ------------------------------------------- */\n const layout = this.scroll.getLayout();\n const vpGap = this.viewport.getViewportGap();\n\n const contentW = layout.totalContentSize.width;\n const contentH = layout.totalContentSize.height;\n\n // Available space for content (excluding fixed viewport gaps)\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n /* helper: offset if content is narrower than available space ------- */\n const off = (availableSpace: number, cw: number, zoom: number) =>\n cw * zoom < availableSpace ? (availableSpace - cw * zoom) / 2 : 0;\n\n const offXold = off(availableWidth, contentW, oldZoom);\n const offYold = off(availableHeight, contentH, oldZoom);\n\n const offXnew = off(availableWidth, contentW, newZoom);\n const offYnew = off(availableHeight, contentH, newZoom);\n\n /* content coords of the focal point -------------------------------- */\n // Adjust focus point to account for vpGap and centering offset\n const cx = (vp.scrollLeft + focus.vx - vpGap - offXold) / oldZoom;\n const cy = (vp.scrollTop + focus.vy - vpGap - offYold) / oldZoom;\n\n /* new scroll so that (cx,cy) appears under focus again ------------- */\n const baseLeft = cx * newZoom + vpGap + offXnew;\n const baseTop = cy * newZoom + vpGap + offYnew;\n\n const desiredScrollLeft =\n align === 'center' ? baseLeft - vp.clientWidth / 2 : baseLeft - focus.vx;\n const desiredScrollTop =\n align === 'center' ? baseTop - vp.clientHeight / 2 : baseTop - focus.vy;\n\n return {\n desiredScrollLeft: Math.max(0, desiredScrollLeft),\n desiredScrollTop: Math.max(0, desiredScrollTop),\n };\n }\n\n private handleZoomToArea(pageIndex: number, rect: Rect) {\n const vp = this.viewport.getMetrics();\n const vpGap = this.viewport.getViewportGap();\n const oldZ = this.state.currentZoomLevel;\n\n const availableW = vp.clientWidth - 2 * vpGap;\n const availableH = vp.clientHeight - 2 * vpGap;\n\n /* 1 – numeric zoom so the rect fits -------------------------------- */\n const targetZoom = this.toZoom(\n Math.min(availableW / rect.size.width, availableH / rect.size.height),\n );\n\n /* 2 — absolute page position in content coordinates --------------- */\n const layout = this.scroll.getLayout();\n\n // the VirtualItem that actually contains the requested page\n const vItem = layout.virtualItems.find((it) =>\n it.pageLayouts.some((p) => p.pageIndex === pageIndex),\n );\n if (!vItem) return;\n\n // the page-layout _inside_ that virtual item\n const pageRel = vItem.pageLayouts.find((p) => p.pageIndex === pageIndex)!;\n\n /* add the virtual-item's own offset to get absolute coords */\n const pageAbsX = vItem.x + pageRel.x;\n const pageAbsY = vItem.y + pageRel.y;\n\n /* 3 — centre of the marquee in content space ---------------------- */\n const cxContent = pageAbsX + rect.origin.x + rect.size.width / 2;\n const cyContent = pageAbsY + rect.origin.y + rect.size.height / 2;\n\n /* 4 – viewport coords of that centre *before* zoom ----------------- */\n const off = (avail: number, cw: number, z: number) =>\n cw * z < avail ? (avail - cw * z) / 2 : 0;\n\n const offXold = off(availableW, layout.totalContentSize.width, oldZ);\n const offYold = off(availableH, layout.totalContentSize.height, oldZ);\n\n const centerVX = vpGap + offXold + cxContent * oldZ - vp.scrollLeft;\n const centerVY = vpGap + offYold + cyContent * oldZ - vp.scrollTop;\n\n /* 4 – reuse the generic handler ------------------------------------ */\n this.handleRequest({\n level: targetZoom,\n center: { vx: centerVX, vy: centerVY },\n align: 'center',\n });\n }\n\n /** recalculates Automatic / Fit* when viewport or pages change */\n private recalcAuto(focus?: VerticalZoomFocus) {\n const s = this.state;\n if (\n s.zoomLevel === ZoomMode.Automatic ||\n s.zoomLevel === ZoomMode.FitPage ||\n s.zoomLevel === ZoomMode.FitWidth\n )\n this.handleRequest({ level: s.zoomLevel, focus });\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\n\nimport { ZoomAction } from './actions';\nimport { manifest, ZOOM_PLUGIN_ID } from './manifest';\nimport { zoomReducer, initialState } from './reducer';\nimport { ZoomPluginConfig, ZoomState } from './types';\nimport { ZoomPlugin } from './zoom-plugin';\n\nexport const ZoomPluginPackage: PluginPackage<ZoomPlugin, ZoomPluginConfig, ZoomState, ZoomAction> =\n {\n manifest,\n create: (registry, _engine, config) => new ZoomPlugin(ZOOM_PLUGIN_ID, registry, config),\n reducer: zoomReducer,\n initialState,\n };\n\nexport * from './zoom-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"mappings":"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","names":["ZoomMode","VerticalZoomFocus","r"]}
package/dist/index.d.cts CHANGED
@@ -103,6 +103,7 @@ declare class ZoomPlugin extends BasePlugin<ZoomPluginConfig, ZoomCapability, Zo
103
103
  static readonly id: "zoom";
104
104
  private readonly zoom$;
105
105
  private readonly viewport;
106
+ private readonly viewportPlugin;
106
107
  private readonly scroll;
107
108
  private readonly interactionManager;
108
109
  private readonly presets;
package/dist/index.d.ts CHANGED
@@ -103,6 +103,7 @@ declare class ZoomPlugin extends BasePlugin<ZoomPluginConfig, ZoomCapability, Zo
103
103
  static readonly id: "zoom";
104
104
  private readonly zoom$;
105
105
  private readonly viewport;
106
+ private readonly viewportPlugin;
106
107
  private readonly scroll;
107
108
  private readonly interactionManager;
108
109
  private readonly presets;
package/dist/index.js CHANGED
@@ -51,6 +51,21 @@ var manifest = {
51
51
  min: 4,
52
52
  max: 10,
53
53
  step: 0.8
54
+ },
55
+ {
56
+ min: 10,
57
+ max: 20,
58
+ step: 1.6
59
+ },
60
+ {
61
+ min: 20,
62
+ max: 40,
63
+ step: 3.2
64
+ },
65
+ {
66
+ min: 40,
67
+ max: 60,
68
+ step: 6.4
54
69
  }
55
70
  ],
56
71
  presets: [
@@ -162,7 +177,8 @@ var ZoomPlugin = class extends BasePlugin {
162
177
  /* internals */
163
178
  /* ------------------------------------------------------------------ */
164
179
  this.zoom$ = createEmitter();
165
- this.viewport = registry.getPlugin("viewport").provides();
180
+ this.viewportPlugin = registry.getPlugin("viewport");
181
+ this.viewport = this.viewportPlugin.provides();
166
182
  this.scroll = registry.getPlugin("scroll").provides();
167
183
  const interactionManager = registry.getPlugin("interaction-manager");
168
184
  this.interactionManager = interactionManager?.provides() ?? null;
@@ -172,7 +188,7 @@ var ZoomPlugin = class extends BasePlugin {
172
188
  this.presets = cfg.presets ?? [];
173
189
  this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);
174
190
  this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));
175
- this.viewport.onViewportChange(() => this.recalcAuto(1 /* Top */), {
191
+ this.viewport.onViewportResize(() => this.recalcAuto(1 /* Top */), {
176
192
  mode: "debounce",
177
193
  wait: 150
178
194
  });
@@ -275,7 +291,7 @@ var ZoomPlugin = class extends BasePlugin {
275
291
  align
276
292
  );
277
293
  if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {
278
- this.viewport.setViewportScrollMetrics({
294
+ this.viewportPlugin.setViewportScrollMetrics({
279
295
  scrollLeft: desiredScrollLeft,
280
296
  scrollTop: desiredScrollTop
281
297
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/types.ts","../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/zoom-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePluginConfig } from '@embedpdf/core';\nimport { Rect } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\n\n/* ------------------------------------------------------------------ */\n/* public */\n/* ------------------------------------------------------------------ */\n\nexport enum ZoomMode {\n Automatic = 'automatic',\n FitPage = 'fit-page',\n FitWidth = 'fit-width',\n}\n\nexport type ZoomLevel = ZoomMode | number;\n\nexport interface Point {\n vx: number;\n vy: number;\n}\n\nexport interface ZoomChangeEvent {\n /** old and new *actual* scale factors */\n oldZoom: number;\n newZoom: number;\n\n /** level used to obtain the newZoom (number | mode) */\n level: ZoomLevel;\n\n /** viewport point kept under the finger / mouse‑wheel focus */\n center: Point;\n\n /** where the viewport should scroll to after the scale change */\n desiredScrollLeft: number;\n desiredScrollTop: number;\n\n /** metrics at the moment the zoom was requested */\n viewport: ViewportMetrics;\n}\n\nexport interface ZoomCapability {\n /** subscribe – returns the unsubscribe function */\n onZoomChange(handler: (e: ZoomChangeEvent) => void): () => void;\n\n /** absolute requests -------------------------------------------------- */\n requestZoom(level: ZoomLevel, center?: Point): void;\n /** relative requests -------------------------------------------------- */\n requestZoomBy(delta: number, center?: Point): void;\n\n /** absolute requests -------------------------------------------------- */\n zoomIn(): void;\n zoomOut(): void;\n\n zoomToArea(pageIndex: number, rect: Rect): void;\n\n /** zoom in on an area -------------------------------------------------- */\n enableMarqueeZoom(): void;\n disableMarqueeZoom(): void;\n isMarqueeZoomActive(): boolean;\n\n getState(): ZoomState;\n getPresets(): ZoomPreset[];\n}\n\n/* ------------------------------------------------------------------ */\n/* config / store */\n/* ------------------------------------------------------------------ */\n\nexport interface ZoomRangeStep {\n min: number;\n max: number;\n step: number;\n}\n\nexport interface ZoomPreset {\n name: string;\n value: ZoomLevel;\n icon?: string;\n}\n\nexport interface ZoomPluginConfig extends BasePluginConfig {\n defaultZoomLevel: ZoomLevel;\n minZoom?: number;\n maxZoom?: number;\n zoomStep?: number;\n zoomRanges?: ZoomRangeStep[]; // Define different step sizes for different zoom ranges\n presets?: ZoomPreset[]; // Preset zoom options for dropdown\n}\n\nexport interface ZoomState {\n zoomLevel: ZoomLevel; // last **requested** level\n currentZoomLevel: number; // actual numeric factor\n zoomReady: boolean; // whether the zoom is ready to be used\n}\n\nexport enum VerticalZoomFocus {\n Center,\n Top,\n}\n\nexport interface ZoomRequest {\n level: ZoomLevel;\n delta?: number;\n center?: Point;\n focus?: VerticalZoomFocus;\n /** Scroll so that the focal point ends up …\n * ▸ `\"keep\"` (default) at the same viewport coords\n * ▸ `\"center\"` centred in the viewport */\n align?: 'keep' | 'center';\n}\n","import { PluginManifest } from '@embedpdf/core';\n\nimport { ZoomMode, ZoomPluginConfig } from './types';\n\nexport const ZOOM_PLUGIN_ID = 'zoom';\n\nexport const manifest: PluginManifest<ZoomPluginConfig> = {\n id: ZOOM_PLUGIN_ID,\n name: 'Zoom Plugin',\n version: '1.0.0',\n provides: ['zoom'],\n requires: ['viewport', 'scroll'],\n optional: ['interaction-manager'],\n defaultConfig: {\n enabled: true,\n defaultZoomLevel: ZoomMode.Automatic,\n minZoom: 0.2,\n maxZoom: 60,\n zoomStep: 0.1,\n zoomRanges: [\n {\n min: 0.2,\n max: 0.5,\n step: 0.05,\n },\n {\n min: 0.5,\n max: 1.0,\n step: 0.1,\n },\n {\n min: 1.0,\n max: 2.0,\n step: 0.2,\n },\n {\n min: 2.0,\n max: 4.0,\n step: 0.4,\n },\n {\n min: 4.0,\n max: 10.0,\n step: 0.8,\n },\n ],\n presets: [\n {\n name: 'Fit Page',\n value: ZoomMode.FitPage,\n },\n {\n name: 'Fit Width',\n value: ZoomMode.FitWidth,\n },\n {\n name: 'Automatic',\n value: ZoomMode.Automatic,\n },\n {\n name: '10%',\n value: 0.1,\n },\n {\n name: '20%',\n value: 0.2,\n },\n {\n name: '30%',\n value: 0.3,\n },\n {\n name: '50%',\n value: 0.5,\n },\n {\n name: '70%',\n value: 0.7,\n },\n {\n name: '100%',\n value: 1,\n },\n {\n name: '200%',\n value: 2,\n },\n {\n name: '400%',\n value: 4,\n },\n ],\n },\n};\n","import { Action } from '@embedpdf/core';\n\nimport { ZoomLevel } from './types';\n\n// Action Types\nexport const SET_ZOOM_LEVEL = 'SET_ZOOM_LEVEL';\nexport const SET_INITIAL_ZOOM_LEVEL = 'SET_INITIAL_ZOOM_LEVEL';\n\n// Action Interfaces\nexport interface SetZoomLevelAction extends Action {\n type: typeof SET_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n currentZoomLevel: number;\n };\n}\n\nexport interface SetInitialZoomLevelAction extends Action {\n type: typeof SET_INITIAL_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n };\n}\n\n// Union Type for All Actions\nexport type ZoomAction = SetZoomLevelAction | SetInitialZoomLevelAction;\n\n// Action Creators\nexport function setZoomLevel(zoomLevel: ZoomLevel, currentZoomLevel: number): SetZoomLevelAction {\n return {\n type: SET_ZOOM_LEVEL,\n payload: { zoomLevel, currentZoomLevel },\n };\n}\n\nexport function setInitialZoomLevel(zoomLevel: ZoomLevel): SetInitialZoomLevelAction {\n return {\n type: SET_INITIAL_ZOOM_LEVEL,\n payload: { zoomLevel },\n };\n}\n","import { Reducer } from '@embedpdf/core';\n\nimport { SET_INITIAL_ZOOM_LEVEL, SET_ZOOM_LEVEL, ZoomAction } from './actions';\nimport { ZoomState, ZoomMode } from './types';\n\nexport const initialState: ZoomState = {\n zoomLevel: ZoomMode.Automatic,\n currentZoomLevel: 1,\n zoomReady: false,\n};\n\nexport const zoomReducer: Reducer<ZoomState, ZoomAction> = (state = initialState, action) => {\n switch (action.type) {\n case SET_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n currentZoomLevel: action.payload.currentZoomLevel,\n zoomReady: true,\n };\n case SET_INITIAL_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n };\n default:\n return state;\n }\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createEmitter,\n clamp,\n setScale,\n SET_PAGES,\n SET_DOCUMENT,\n getPagesWithRotatedSize,\n SET_ROTATION,\n} from '@embedpdf/core';\nimport { ScrollPlugin, ScrollCapability } from '@embedpdf/plugin-scroll';\nimport { ViewportPlugin, ViewportCapability, ViewportMetrics } from '@embedpdf/plugin-viewport';\n\nimport { setInitialZoomLevel, setZoomLevel, ZoomAction } from './actions';\nimport {\n ZoomPluginConfig,\n ZoomState,\n ZoomMode,\n Point,\n ZoomChangeEvent,\n ZoomCapability,\n ZoomPreset,\n ZoomRangeStep,\n VerticalZoomFocus,\n ZoomRequest,\n} from './types';\nimport {\n InteractionManagerCapability,\n InteractionManagerPlugin,\n} from '@embedpdf/plugin-interaction-manager';\nimport { Rect } from '@embedpdf/models';\n\nexport class ZoomPlugin extends BasePlugin<\n ZoomPluginConfig,\n ZoomCapability,\n ZoomState,\n ZoomAction\n> {\n static readonly id = 'zoom' as const;\n /* ------------------------------------------------------------------ */\n /* internals */\n /* ------------------------------------------------------------------ */\n private readonly zoom$ = createEmitter<ZoomChangeEvent>();\n private readonly viewport: ViewportCapability;\n private readonly scroll: ScrollCapability;\n private readonly interactionManager: InteractionManagerCapability | null;\n private readonly presets: ZoomPreset[];\n private readonly zoomRanges: ZoomRangeStep[];\n\n private readonly minZoom: number;\n private readonly maxZoom: number;\n private readonly zoomStep: number;\n\n /* ------------------------------------------------------------------ */\n constructor(id: string, registry: PluginRegistry, cfg: ZoomPluginConfig) {\n super(id, registry);\n\n this.viewport = registry.getPlugin<ViewportPlugin>('viewport')!.provides();\n this.scroll = registry.getPlugin<ScrollPlugin>('scroll')!.provides();\n const interactionManager = registry.getPlugin<InteractionManagerPlugin>('interaction-manager');\n this.interactionManager = interactionManager?.provides() ?? null;\n this.minZoom = cfg.minZoom ?? 0.25;\n this.maxZoom = cfg.maxZoom ?? 10;\n this.zoomStep = cfg.zoomStep ?? 0.1;\n this.presets = cfg.presets ?? [];\n this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);\n this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));\n /* keep \"automatic\" modes up to date -------------------------------- */\n this.viewport.onViewportChange(() => this.recalcAuto(VerticalZoomFocus.Top), {\n mode: 'debounce',\n wait: 150,\n });\n this.coreStore.onAction(SET_ROTATION, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_PAGES, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_DOCUMENT, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.interactionManager?.registerMode({\n id: 'marqueeZoom',\n scope: 'page',\n exclusive: true,\n cursor: 'zoom-in',\n });\n this.resetReady();\n }\n\n /* ------------------------------------------------------------------ */\n /* capability */\n /* ------------------------------------------------------------------ */\n protected buildCapability(): ZoomCapability {\n return {\n onZoomChange: this.zoom$.on,\n zoomIn: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: this.stepFor(cur) });\n },\n zoomOut: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: -this.stepFor(cur) });\n },\n zoomToArea: (pageIndex, rect) => this.handleZoomToArea(pageIndex, rect),\n requestZoom: (level, c) => this.handleRequest({ level, center: c }),\n requestZoomBy: (d, c) => {\n const cur = this.state.currentZoomLevel;\n const target = this.toZoom(cur + d);\n return this.handleRequest({ level: target, center: c });\n },\n enableMarqueeZoom: () => {\n this.interactionManager?.activate('marqueeZoom');\n },\n disableMarqueeZoom: () => {\n this.interactionManager?.activate('default');\n },\n isMarqueeZoomActive: () => this.interactionManager?.getActiveMode() === 'marqueeZoom',\n getState: () => this.state,\n getPresets: () => this.presets,\n };\n }\n\n /* ------------------------------------------------------------------ */\n /* plugin life‑cycle */\n /* ------------------------------------------------------------------ */\n async initialize(): Promise<void> {\n /* apply the initial zoom */\n }\n\n async destroy() {\n this.zoom$.clear();\n }\n\n /**\n * Sort ranges once, make sure they are sane\n */\n private normalizeRanges(ranges: ZoomRangeStep[]): ZoomRangeStep[] {\n return [...ranges]\n .filter((r) => r.step > 0 && r.max > r.min) // basic sanity\n .sort((a, b) => a.min - b.min);\n }\n\n /** pick the step that applies to a given numeric zoom */\n private stepFor(zoom: number): number {\n const r = this.zoomRanges.find((r) => zoom >= r.min && zoom < r.max);\n return r ? r.step : this.zoomStep; // fallback\n }\n\n /** clamp + round helper reused later */\n private toZoom(v: number) {\n return parseFloat(clamp(v, this.minZoom, this.maxZoom).toFixed(2));\n }\n\n /* ------------------------------------------------------------------ */\n /* main entry – handles **every** zoom request */\n /* ------------------------------------------------------------------ */\n private handleRequest({\n level,\n delta = 0,\n center,\n focus = VerticalZoomFocus.Center,\n align = 'keep',\n }: ZoomRequest) {\n const metrics = this.viewport.getMetrics();\n const oldZoom = this.state.currentZoomLevel;\n\n if (metrics.clientWidth === 0 || metrics.clientHeight === 0) {\n return;\n }\n\n /* ------------------------------------------------------------------ */\n /* step 1 – resolve the **target numeric zoom** */\n /* ------------------------------------------------------------------ */\n const base = typeof level === 'number' ? level : this.computeZoomForMode(level, metrics);\n\n if (base === false) {\n return;\n }\n const exactZoom = clamp(base + delta, this.minZoom, this.maxZoom);\n const newZoom = Math.floor(exactZoom * 100) / 100;\n\n /* ------------------------------------------------------------------ */\n /* step 2 – figure out the viewport point we should keep under focus */\n /* ------------------------------------------------------------------ */\n const focusPoint: Point = center ?? {\n vx: metrics.clientWidth / 2,\n vy: focus === VerticalZoomFocus.Top ? 0 : metrics.clientHeight / 2,\n };\n\n /* ------------------------------------------------------------------ */\n /* step 3 – translate that into desired scroll offsets */\n /* ------------------------------------------------------------------ */\n const { desiredScrollLeft, desiredScrollTop } = this.computeScrollForZoomChange(\n metrics,\n oldZoom,\n newZoom,\n focusPoint,\n align,\n );\n\n /* ------------------------------------------------------------------ */\n /* step 4 – dispatch + notify */\n /* ------------------------------------------------------------------ */\n\n if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {\n this.viewport.setViewportScrollMetrics({\n scrollLeft: desiredScrollLeft,\n scrollTop: desiredScrollTop,\n });\n }\n\n this.dispatch(setZoomLevel(typeof level === 'number' ? newZoom : level, newZoom));\n this.dispatchCoreAction(setScale(newZoom));\n this.markReady();\n\n this.viewport.scrollTo({\n x: desiredScrollLeft,\n y: desiredScrollTop,\n behavior: 'instant',\n });\n\n const evt: ZoomChangeEvent = {\n oldZoom,\n newZoom,\n level,\n center: focusPoint,\n desiredScrollLeft,\n desiredScrollTop,\n viewport: metrics,\n };\n\n this.zoom$.emit(evt);\n }\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n\n /** numeric zoom for Automatic / FitPage / FitWidth */\n private computeZoomForMode(mode: ZoomMode, vp: ViewportMetrics): number | false {\n const spreads = getPagesWithRotatedSize(this.coreState.core);\n if (!spreads.length) return false;\n\n const pgGap = this.scroll.getPageGap();\n const vpGap = this.viewport.getViewportGap();\n\n if (vp.clientWidth === 0 || vp.clientHeight === 0) {\n return false;\n }\n\n // Available space after accounting for fixed viewport gaps\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n if (availableWidth <= 0 || availableHeight <= 0) {\n return false;\n }\n\n let maxContentW = 0,\n maxContentH = 0;\n\n spreads.forEach((spread) => {\n // Only include scalable content (pages + page gaps), not viewport gaps\n const contentW = spread.reduce((s, p, i) => s + p.rotatedSize.width + (i ? pgGap : 0), 0);\n const contentH = Math.max(...spread.map((p) => p.rotatedSize.height));\n maxContentW = Math.max(maxContentW, contentW);\n maxContentH = Math.max(maxContentH, contentH);\n });\n\n switch (mode) {\n case ZoomMode.FitWidth:\n return availableWidth / maxContentW;\n case ZoomMode.FitPage:\n return Math.min(availableWidth / maxContentW, availableHeight / maxContentH);\n case ZoomMode.Automatic:\n return Math.min(availableWidth / maxContentW, 1);\n /* istanbul ignore next */\n default:\n return 1;\n }\n }\n\n /** where to scroll so that *focus* stays stable after scaling */\n private computeScrollForZoomChange(\n vp: ViewportMetrics,\n oldZoom: number,\n newZoom: number,\n focus: Point,\n align: 'keep' | 'center' = 'keep',\n ) {\n /* unscaled content size ------------------------------------------- */\n const layout = this.scroll.getLayout();\n const vpGap = this.viewport.getViewportGap();\n\n const contentW = layout.totalContentSize.width;\n const contentH = layout.totalContentSize.height;\n\n // Available space for content (excluding fixed viewport gaps)\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n /* helper: offset if content is narrower than available space ------- */\n const off = (availableSpace: number, cw: number, zoom: number) =>\n cw * zoom < availableSpace ? (availableSpace - cw * zoom) / 2 : 0;\n\n const offXold = off(availableWidth, contentW, oldZoom);\n const offYold = off(availableHeight, contentH, oldZoom);\n\n const offXnew = off(availableWidth, contentW, newZoom);\n const offYnew = off(availableHeight, contentH, newZoom);\n\n /* content coords of the focal point -------------------------------- */\n // Adjust focus point to account for vpGap and centering offset\n const cx = (vp.scrollLeft + focus.vx - vpGap - offXold) / oldZoom;\n const cy = (vp.scrollTop + focus.vy - vpGap - offYold) / oldZoom;\n\n /* new scroll so that (cx,cy) appears under focus again ------------- */\n const baseLeft = cx * newZoom + vpGap + offXnew;\n const baseTop = cy * newZoom + vpGap + offYnew;\n\n const desiredScrollLeft =\n align === 'center' ? baseLeft - vp.clientWidth / 2 : baseLeft - focus.vx;\n const desiredScrollTop =\n align === 'center' ? baseTop - vp.clientHeight / 2 : baseTop - focus.vy;\n\n return {\n desiredScrollLeft: Math.max(0, desiredScrollLeft),\n desiredScrollTop: Math.max(0, desiredScrollTop),\n };\n }\n\n private handleZoomToArea(pageIndex: number, rect: Rect) {\n const vp = this.viewport.getMetrics();\n const vpGap = this.viewport.getViewportGap();\n const oldZ = this.state.currentZoomLevel;\n\n const availableW = vp.clientWidth - 2 * vpGap;\n const availableH = vp.clientHeight - 2 * vpGap;\n\n /* 1 – numeric zoom so the rect fits -------------------------------- */\n const targetZoom = this.toZoom(\n Math.min(availableW / rect.size.width, availableH / rect.size.height),\n );\n\n /* 2 — absolute page position in content coordinates --------------- */\n const layout = this.scroll.getLayout();\n\n // the VirtualItem that actually contains the requested page\n const vItem = layout.virtualItems.find((it) =>\n it.pageLayouts.some((p) => p.pageIndex === pageIndex),\n );\n if (!vItem) return;\n\n // the page-layout _inside_ that virtual item\n const pageRel = vItem.pageLayouts.find((p) => p.pageIndex === pageIndex)!;\n\n /* add the virtual-item's own offset to get absolute coords */\n const pageAbsX = vItem.x + pageRel.x;\n const pageAbsY = vItem.y + pageRel.y;\n\n /* 3 — centre of the marquee in content space ---------------------- */\n const cxContent = pageAbsX + rect.origin.x + rect.size.width / 2;\n const cyContent = pageAbsY + rect.origin.y + rect.size.height / 2;\n\n /* 4 – viewport coords of that centre *before* zoom ----------------- */\n const off = (avail: number, cw: number, z: number) =>\n cw * z < avail ? (avail - cw * z) / 2 : 0;\n\n const offXold = off(availableW, layout.totalContentSize.width, oldZ);\n const offYold = off(availableH, layout.totalContentSize.height, oldZ);\n\n const centerVX = vpGap + offXold + cxContent * oldZ - vp.scrollLeft;\n const centerVY = vpGap + offYold + cyContent * oldZ - vp.scrollTop;\n\n /* 4 – reuse the generic handler ------------------------------------ */\n this.handleRequest({\n level: targetZoom,\n center: { vx: centerVX, vy: centerVY },\n align: 'center',\n });\n }\n\n /** recalculates Automatic / Fit* when viewport or pages change */\n private recalcAuto(focus?: VerticalZoomFocus) {\n const s = this.state;\n if (\n s.zoomLevel === ZoomMode.Automatic ||\n s.zoomLevel === ZoomMode.FitPage ||\n s.zoomLevel === ZoomMode.FitWidth\n )\n this.handleRequest({ level: s.zoomLevel, focus });\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\n\nimport { ZoomAction } from './actions';\nimport { manifest, ZOOM_PLUGIN_ID } from './manifest';\nimport { zoomReducer, initialState } from './reducer';\nimport { ZoomPluginConfig, ZoomState } from './types';\nimport { ZoomPlugin } from './zoom-plugin';\n\nexport const ZoomPluginPackage: PluginPackage<ZoomPlugin, ZoomPluginConfig, ZoomState, ZoomAction> =\n {\n manifest,\n create: (registry, _engine, config) => new ZoomPlugin(ZOOM_PLUGIN_ID, registry, config),\n reducer: zoomReducer,\n initialState,\n };\n\nexport * from './zoom-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"mappings":"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","names":["ZoomMode","VerticalZoomFocus","r"]}
1
+ {"version":3,"sources":["../src/lib/types.ts","../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/reducer.ts","../src/lib/zoom-plugin.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePluginConfig } from '@embedpdf/core';\nimport { Rect } from '@embedpdf/models';\nimport { ViewportMetrics } from '@embedpdf/plugin-viewport';\n\n/* ------------------------------------------------------------------ */\n/* public */\n/* ------------------------------------------------------------------ */\n\nexport enum ZoomMode {\n Automatic = 'automatic',\n FitPage = 'fit-page',\n FitWidth = 'fit-width',\n}\n\nexport type ZoomLevel = ZoomMode | number;\n\nexport interface Point {\n vx: number;\n vy: number;\n}\n\nexport interface ZoomChangeEvent {\n /** old and new *actual* scale factors */\n oldZoom: number;\n newZoom: number;\n\n /** level used to obtain the newZoom (number | mode) */\n level: ZoomLevel;\n\n /** viewport point kept under the finger / mouse‑wheel focus */\n center: Point;\n\n /** where the viewport should scroll to after the scale change */\n desiredScrollLeft: number;\n desiredScrollTop: number;\n\n /** metrics at the moment the zoom was requested */\n viewport: ViewportMetrics;\n}\n\nexport interface ZoomCapability {\n /** subscribe – returns the unsubscribe function */\n onZoomChange(handler: (e: ZoomChangeEvent) => void): () => void;\n\n /** absolute requests -------------------------------------------------- */\n requestZoom(level: ZoomLevel, center?: Point): void;\n /** relative requests -------------------------------------------------- */\n requestZoomBy(delta: number, center?: Point): void;\n\n /** absolute requests -------------------------------------------------- */\n zoomIn(): void;\n zoomOut(): void;\n\n zoomToArea(pageIndex: number, rect: Rect): void;\n\n /** zoom in on an area -------------------------------------------------- */\n enableMarqueeZoom(): void;\n disableMarqueeZoom(): void;\n isMarqueeZoomActive(): boolean;\n\n getState(): ZoomState;\n getPresets(): ZoomPreset[];\n}\n\n/* ------------------------------------------------------------------ */\n/* config / store */\n/* ------------------------------------------------------------------ */\n\nexport interface ZoomRangeStep {\n min: number;\n max: number;\n step: number;\n}\n\nexport interface ZoomPreset {\n name: string;\n value: ZoomLevel;\n icon?: string;\n}\n\nexport interface ZoomPluginConfig extends BasePluginConfig {\n defaultZoomLevel: ZoomLevel;\n minZoom?: number;\n maxZoom?: number;\n zoomStep?: number;\n zoomRanges?: ZoomRangeStep[]; // Define different step sizes for different zoom ranges\n presets?: ZoomPreset[]; // Preset zoom options for dropdown\n}\n\nexport interface ZoomState {\n zoomLevel: ZoomLevel; // last **requested** level\n currentZoomLevel: number; // actual numeric factor\n zoomReady: boolean; // whether the zoom is ready to be used\n}\n\nexport enum VerticalZoomFocus {\n Center,\n Top,\n}\n\nexport interface ZoomRequest {\n level: ZoomLevel;\n delta?: number;\n center?: Point;\n focus?: VerticalZoomFocus;\n /** Scroll so that the focal point ends up …\n * ▸ `\"keep\"` (default) at the same viewport coords\n * ▸ `\"center\"` centred in the viewport */\n align?: 'keep' | 'center';\n}\n","import { PluginManifest } from '@embedpdf/core';\n\nimport { ZoomMode, ZoomPluginConfig } from './types';\n\nexport const ZOOM_PLUGIN_ID = 'zoom';\n\nexport const manifest: PluginManifest<ZoomPluginConfig> = {\n id: ZOOM_PLUGIN_ID,\n name: 'Zoom Plugin',\n version: '1.0.0',\n provides: ['zoom'],\n requires: ['viewport', 'scroll'],\n optional: ['interaction-manager'],\n defaultConfig: {\n enabled: true,\n defaultZoomLevel: ZoomMode.Automatic,\n minZoom: 0.2,\n maxZoom: 60,\n zoomStep: 0.1,\n zoomRanges: [\n {\n min: 0.2,\n max: 0.5,\n step: 0.05,\n },\n {\n min: 0.5,\n max: 1.0,\n step: 0.1,\n },\n {\n min: 1.0,\n max: 2.0,\n step: 0.2,\n },\n {\n min: 2.0,\n max: 4.0,\n step: 0.4,\n },\n {\n min: 4.0,\n max: 10.0,\n step: 0.8,\n },\n {\n min: 10.0,\n max: 20.0,\n step: 1.6,\n },\n {\n min: 20.0,\n max: 40.0,\n step: 3.2,\n },\n {\n min: 40.0,\n max: 60.0,\n step: 6.4,\n },\n ],\n presets: [\n {\n name: 'Fit Page',\n value: ZoomMode.FitPage,\n },\n {\n name: 'Fit Width',\n value: ZoomMode.FitWidth,\n },\n {\n name: 'Automatic',\n value: ZoomMode.Automatic,\n },\n {\n name: '10%',\n value: 0.1,\n },\n {\n name: '20%',\n value: 0.2,\n },\n {\n name: '30%',\n value: 0.3,\n },\n {\n name: '50%',\n value: 0.5,\n },\n {\n name: '70%',\n value: 0.7,\n },\n {\n name: '100%',\n value: 1,\n },\n {\n name: '200%',\n value: 2,\n },\n {\n name: '400%',\n value: 4,\n },\n ],\n },\n};\n","import { Action } from '@embedpdf/core';\n\nimport { ZoomLevel } from './types';\n\n// Action Types\nexport const SET_ZOOM_LEVEL = 'SET_ZOOM_LEVEL';\nexport const SET_INITIAL_ZOOM_LEVEL = 'SET_INITIAL_ZOOM_LEVEL';\n\n// Action Interfaces\nexport interface SetZoomLevelAction extends Action {\n type: typeof SET_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n currentZoomLevel: number;\n };\n}\n\nexport interface SetInitialZoomLevelAction extends Action {\n type: typeof SET_INITIAL_ZOOM_LEVEL;\n payload: {\n zoomLevel: ZoomLevel;\n };\n}\n\n// Union Type for All Actions\nexport type ZoomAction = SetZoomLevelAction | SetInitialZoomLevelAction;\n\n// Action Creators\nexport function setZoomLevel(zoomLevel: ZoomLevel, currentZoomLevel: number): SetZoomLevelAction {\n return {\n type: SET_ZOOM_LEVEL,\n payload: { zoomLevel, currentZoomLevel },\n };\n}\n\nexport function setInitialZoomLevel(zoomLevel: ZoomLevel): SetInitialZoomLevelAction {\n return {\n type: SET_INITIAL_ZOOM_LEVEL,\n payload: { zoomLevel },\n };\n}\n","import { Reducer } from '@embedpdf/core';\n\nimport { SET_INITIAL_ZOOM_LEVEL, SET_ZOOM_LEVEL, ZoomAction } from './actions';\nimport { ZoomState, ZoomMode } from './types';\n\nexport const initialState: ZoomState = {\n zoomLevel: ZoomMode.Automatic,\n currentZoomLevel: 1,\n zoomReady: false,\n};\n\nexport const zoomReducer: Reducer<ZoomState, ZoomAction> = (state = initialState, action) => {\n switch (action.type) {\n case SET_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n currentZoomLevel: action.payload.currentZoomLevel,\n zoomReady: true,\n };\n case SET_INITIAL_ZOOM_LEVEL:\n return {\n ...state,\n zoomLevel: action.payload.zoomLevel,\n };\n default:\n return state;\n }\n};\n","import {\n BasePlugin,\n PluginRegistry,\n createEmitter,\n clamp,\n setScale,\n SET_PAGES,\n SET_DOCUMENT,\n getPagesWithRotatedSize,\n SET_ROTATION,\n} from '@embedpdf/core';\nimport { ScrollPlugin, ScrollCapability } from '@embedpdf/plugin-scroll';\nimport { ViewportPlugin, ViewportCapability, ViewportMetrics } from '@embedpdf/plugin-viewport';\n\nimport { setInitialZoomLevel, setZoomLevel, ZoomAction } from './actions';\nimport {\n ZoomPluginConfig,\n ZoomState,\n ZoomMode,\n Point,\n ZoomChangeEvent,\n ZoomCapability,\n ZoomPreset,\n ZoomRangeStep,\n VerticalZoomFocus,\n ZoomRequest,\n} from './types';\nimport {\n InteractionManagerCapability,\n InteractionManagerPlugin,\n} from '@embedpdf/plugin-interaction-manager';\nimport { Rect } from '@embedpdf/models';\n\nexport class ZoomPlugin extends BasePlugin<\n ZoomPluginConfig,\n ZoomCapability,\n ZoomState,\n ZoomAction\n> {\n static readonly id = 'zoom' as const;\n /* ------------------------------------------------------------------ */\n /* internals */\n /* ------------------------------------------------------------------ */\n private readonly zoom$ = createEmitter<ZoomChangeEvent>();\n private readonly viewport: ViewportCapability;\n private readonly viewportPlugin: ViewportPlugin;\n private readonly scroll: ScrollCapability;\n private readonly interactionManager: InteractionManagerCapability | null;\n private readonly presets: ZoomPreset[];\n private readonly zoomRanges: ZoomRangeStep[];\n\n private readonly minZoom: number;\n private readonly maxZoom: number;\n private readonly zoomStep: number;\n\n /* ------------------------------------------------------------------ */\n constructor(id: string, registry: PluginRegistry, cfg: ZoomPluginConfig) {\n super(id, registry);\n\n this.viewportPlugin = registry.getPlugin<ViewportPlugin>('viewport')!;\n this.viewport = this.viewportPlugin.provides();\n this.scroll = registry.getPlugin<ScrollPlugin>('scroll')!.provides();\n const interactionManager = registry.getPlugin<InteractionManagerPlugin>('interaction-manager');\n this.interactionManager = interactionManager?.provides() ?? null;\n this.minZoom = cfg.minZoom ?? 0.25;\n this.maxZoom = cfg.maxZoom ?? 10;\n this.zoomStep = cfg.zoomStep ?? 0.1;\n this.presets = cfg.presets ?? [];\n this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);\n this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));\n /* keep \"automatic\" modes up to date -------------------------------- */\n this.viewport.onViewportResize(() => this.recalcAuto(VerticalZoomFocus.Top), {\n mode: 'debounce',\n wait: 150,\n });\n this.coreStore.onAction(SET_ROTATION, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_PAGES, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.coreStore.onAction(SET_DOCUMENT, () => this.recalcAuto(VerticalZoomFocus.Top));\n this.interactionManager?.registerMode({\n id: 'marqueeZoom',\n scope: 'page',\n exclusive: true,\n cursor: 'zoom-in',\n });\n this.resetReady();\n }\n\n /* ------------------------------------------------------------------ */\n /* capability */\n /* ------------------------------------------------------------------ */\n protected buildCapability(): ZoomCapability {\n return {\n onZoomChange: this.zoom$.on,\n zoomIn: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: this.stepFor(cur) });\n },\n zoomOut: () => {\n const cur = this.state.currentZoomLevel;\n return this.handleRequest({ level: cur, delta: -this.stepFor(cur) });\n },\n zoomToArea: (pageIndex, rect) => this.handleZoomToArea(pageIndex, rect),\n requestZoom: (level, c) => this.handleRequest({ level, center: c }),\n requestZoomBy: (d, c) => {\n const cur = this.state.currentZoomLevel;\n const target = this.toZoom(cur + d);\n return this.handleRequest({ level: target, center: c });\n },\n enableMarqueeZoom: () => {\n this.interactionManager?.activate('marqueeZoom');\n },\n disableMarqueeZoom: () => {\n this.interactionManager?.activate('default');\n },\n isMarqueeZoomActive: () => this.interactionManager?.getActiveMode() === 'marqueeZoom',\n getState: () => this.state,\n getPresets: () => this.presets,\n };\n }\n\n /* ------------------------------------------------------------------ */\n /* plugin life‑cycle */\n /* ------------------------------------------------------------------ */\n async initialize(): Promise<void> {\n /* apply the initial zoom */\n }\n\n async destroy() {\n this.zoom$.clear();\n }\n\n /**\n * Sort ranges once, make sure they are sane\n */\n private normalizeRanges(ranges: ZoomRangeStep[]): ZoomRangeStep[] {\n return [...ranges]\n .filter((r) => r.step > 0 && r.max > r.min) // basic sanity\n .sort((a, b) => a.min - b.min);\n }\n\n /** pick the step that applies to a given numeric zoom */\n private stepFor(zoom: number): number {\n const r = this.zoomRanges.find((r) => zoom >= r.min && zoom < r.max);\n return r ? r.step : this.zoomStep; // fallback\n }\n\n /** clamp + round helper reused later */\n private toZoom(v: number) {\n return parseFloat(clamp(v, this.minZoom, this.maxZoom).toFixed(2));\n }\n\n /* ------------------------------------------------------------------ */\n /* main entry – handles **every** zoom request */\n /* ------------------------------------------------------------------ */\n private handleRequest({\n level,\n delta = 0,\n center,\n focus = VerticalZoomFocus.Center,\n align = 'keep',\n }: ZoomRequest) {\n const metrics = this.viewport.getMetrics();\n const oldZoom = this.state.currentZoomLevel;\n\n if (metrics.clientWidth === 0 || metrics.clientHeight === 0) {\n return;\n }\n\n /* ------------------------------------------------------------------ */\n /* step 1 – resolve the **target numeric zoom** */\n /* ------------------------------------------------------------------ */\n const base = typeof level === 'number' ? level : this.computeZoomForMode(level, metrics);\n\n if (base === false) {\n return;\n }\n const exactZoom = clamp(base + delta, this.minZoom, this.maxZoom);\n const newZoom = Math.floor(exactZoom * 100) / 100;\n\n /* ------------------------------------------------------------------ */\n /* step 2 – figure out the viewport point we should keep under focus */\n /* ------------------------------------------------------------------ */\n const focusPoint: Point = center ?? {\n vx: metrics.clientWidth / 2,\n vy: focus === VerticalZoomFocus.Top ? 0 : metrics.clientHeight / 2,\n };\n\n /* ------------------------------------------------------------------ */\n /* step 3 – translate that into desired scroll offsets */\n /* ------------------------------------------------------------------ */\n const { desiredScrollLeft, desiredScrollTop } = this.computeScrollForZoomChange(\n metrics,\n oldZoom,\n newZoom,\n focusPoint,\n align,\n );\n\n /* ------------------------------------------------------------------ */\n /* step 4 – dispatch + notify */\n /* ------------------------------------------------------------------ */\n\n if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {\n this.viewportPlugin.setViewportScrollMetrics({\n scrollLeft: desiredScrollLeft,\n scrollTop: desiredScrollTop,\n });\n }\n\n this.dispatch(setZoomLevel(typeof level === 'number' ? newZoom : level, newZoom));\n this.dispatchCoreAction(setScale(newZoom));\n this.markReady();\n\n this.viewport.scrollTo({\n x: desiredScrollLeft,\n y: desiredScrollTop,\n behavior: 'instant',\n });\n\n const evt: ZoomChangeEvent = {\n oldZoom,\n newZoom,\n level,\n center: focusPoint,\n desiredScrollLeft,\n desiredScrollTop,\n viewport: metrics,\n };\n\n this.zoom$.emit(evt);\n }\n\n /* ------------------------------------------------------------------ */\n /* helpers */\n /* ------------------------------------------------------------------ */\n\n /** numeric zoom for Automatic / FitPage / FitWidth */\n private computeZoomForMode(mode: ZoomMode, vp: ViewportMetrics): number | false {\n const spreads = getPagesWithRotatedSize(this.coreState.core);\n if (!spreads.length) return false;\n\n const pgGap = this.scroll.getPageGap();\n const vpGap = this.viewport.getViewportGap();\n\n if (vp.clientWidth === 0 || vp.clientHeight === 0) {\n return false;\n }\n\n // Available space after accounting for fixed viewport gaps\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n if (availableWidth <= 0 || availableHeight <= 0) {\n return false;\n }\n\n let maxContentW = 0,\n maxContentH = 0;\n\n spreads.forEach((spread) => {\n // Only include scalable content (pages + page gaps), not viewport gaps\n const contentW = spread.reduce((s, p, i) => s + p.rotatedSize.width + (i ? pgGap : 0), 0);\n const contentH = Math.max(...spread.map((p) => p.rotatedSize.height));\n maxContentW = Math.max(maxContentW, contentW);\n maxContentH = Math.max(maxContentH, contentH);\n });\n\n switch (mode) {\n case ZoomMode.FitWidth:\n return availableWidth / maxContentW;\n case ZoomMode.FitPage:\n return Math.min(availableWidth / maxContentW, availableHeight / maxContentH);\n case ZoomMode.Automatic:\n return Math.min(availableWidth / maxContentW, 1);\n /* istanbul ignore next */\n default:\n return 1;\n }\n }\n\n /** where to scroll so that *focus* stays stable after scaling */\n private computeScrollForZoomChange(\n vp: ViewportMetrics,\n oldZoom: number,\n newZoom: number,\n focus: Point,\n align: 'keep' | 'center' = 'keep',\n ) {\n /* unscaled content size ------------------------------------------- */\n const layout = this.scroll.getLayout();\n const vpGap = this.viewport.getViewportGap();\n\n const contentW = layout.totalContentSize.width;\n const contentH = layout.totalContentSize.height;\n\n // Available space for content (excluding fixed viewport gaps)\n const availableWidth = vp.clientWidth - 2 * vpGap;\n const availableHeight = vp.clientHeight - 2 * vpGap;\n\n /* helper: offset if content is narrower than available space ------- */\n const off = (availableSpace: number, cw: number, zoom: number) =>\n cw * zoom < availableSpace ? (availableSpace - cw * zoom) / 2 : 0;\n\n const offXold = off(availableWidth, contentW, oldZoom);\n const offYold = off(availableHeight, contentH, oldZoom);\n\n const offXnew = off(availableWidth, contentW, newZoom);\n const offYnew = off(availableHeight, contentH, newZoom);\n\n /* content coords of the focal point -------------------------------- */\n // Adjust focus point to account for vpGap and centering offset\n const cx = (vp.scrollLeft + focus.vx - vpGap - offXold) / oldZoom;\n const cy = (vp.scrollTop + focus.vy - vpGap - offYold) / oldZoom;\n\n /* new scroll so that (cx,cy) appears under focus again ------------- */\n const baseLeft = cx * newZoom + vpGap + offXnew;\n const baseTop = cy * newZoom + vpGap + offYnew;\n\n const desiredScrollLeft =\n align === 'center' ? baseLeft - vp.clientWidth / 2 : baseLeft - focus.vx;\n const desiredScrollTop =\n align === 'center' ? baseTop - vp.clientHeight / 2 : baseTop - focus.vy;\n\n return {\n desiredScrollLeft: Math.max(0, desiredScrollLeft),\n desiredScrollTop: Math.max(0, desiredScrollTop),\n };\n }\n\n private handleZoomToArea(pageIndex: number, rect: Rect) {\n const vp = this.viewport.getMetrics();\n const vpGap = this.viewport.getViewportGap();\n const oldZ = this.state.currentZoomLevel;\n\n const availableW = vp.clientWidth - 2 * vpGap;\n const availableH = vp.clientHeight - 2 * vpGap;\n\n /* 1 – numeric zoom so the rect fits -------------------------------- */\n const targetZoom = this.toZoom(\n Math.min(availableW / rect.size.width, availableH / rect.size.height),\n );\n\n /* 2 — absolute page position in content coordinates --------------- */\n const layout = this.scroll.getLayout();\n\n // the VirtualItem that actually contains the requested page\n const vItem = layout.virtualItems.find((it) =>\n it.pageLayouts.some((p) => p.pageIndex === pageIndex),\n );\n if (!vItem) return;\n\n // the page-layout _inside_ that virtual item\n const pageRel = vItem.pageLayouts.find((p) => p.pageIndex === pageIndex)!;\n\n /* add the virtual-item's own offset to get absolute coords */\n const pageAbsX = vItem.x + pageRel.x;\n const pageAbsY = vItem.y + pageRel.y;\n\n /* 3 — centre of the marquee in content space ---------------------- */\n const cxContent = pageAbsX + rect.origin.x + rect.size.width / 2;\n const cyContent = pageAbsY + rect.origin.y + rect.size.height / 2;\n\n /* 4 – viewport coords of that centre *before* zoom ----------------- */\n const off = (avail: number, cw: number, z: number) =>\n cw * z < avail ? (avail - cw * z) / 2 : 0;\n\n const offXold = off(availableW, layout.totalContentSize.width, oldZ);\n const offYold = off(availableH, layout.totalContentSize.height, oldZ);\n\n const centerVX = vpGap + offXold + cxContent * oldZ - vp.scrollLeft;\n const centerVY = vpGap + offYold + cyContent * oldZ - vp.scrollTop;\n\n /* 4 – reuse the generic handler ------------------------------------ */\n this.handleRequest({\n level: targetZoom,\n center: { vx: centerVX, vy: centerVY },\n align: 'center',\n });\n }\n\n /** recalculates Automatic / Fit* when viewport or pages change */\n private recalcAuto(focus?: VerticalZoomFocus) {\n const s = this.state;\n if (\n s.zoomLevel === ZoomMode.Automatic ||\n s.zoomLevel === ZoomMode.FitPage ||\n s.zoomLevel === ZoomMode.FitWidth\n )\n this.handleRequest({ level: s.zoomLevel, focus });\n }\n}\n","import { PluginPackage } from '@embedpdf/core';\n\nimport { ZoomAction } from './actions';\nimport { manifest, ZOOM_PLUGIN_ID } from './manifest';\nimport { zoomReducer, initialState } from './reducer';\nimport { ZoomPluginConfig, ZoomState } from './types';\nimport { ZoomPlugin } from './zoom-plugin';\n\nexport const ZoomPluginPackage: PluginPackage<ZoomPlugin, ZoomPluginConfig, ZoomState, ZoomAction> =\n {\n manifest,\n create: (registry, _engine, config) => new ZoomPlugin(ZOOM_PLUGIN_ID, registry, config),\n reducer: zoomReducer,\n initialState,\n };\n\nexport * from './zoom-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"mappings":"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","names":["ZoomMode","VerticalZoomFocus","r"]}
@@ -34,24 +34,42 @@ __export(preact_exports, {
34
34
  PinchWrapper: () => PinchWrapper,
35
35
  usePinch: () => usePinch,
36
36
  useZoom: () => useZoom,
37
- useZoomCapability: () => useZoomCapability
37
+ useZoomCapability: () => useZoomCapability,
38
+ useZoomPlugin: () => useZoomPlugin
38
39
  });
39
40
  module.exports = __toCommonJS(preact_exports);
40
41
 
41
42
  // src/preact/hooks/use-zoom.ts
42
43
  var import_preact = require("@embedpdf/core/preact");
43
44
  var import_plugin_zoom = require("@embedpdf/plugin-zoom");
45
+ var import_hooks = require("preact/hooks");
44
46
  var useZoomCapability = () => (0, import_preact.useCapability)(import_plugin_zoom.ZoomPlugin.id);
45
- var useZoom = () => (0, import_preact.usePlugin)(import_plugin_zoom.ZoomPlugin.id);
47
+ var useZoomPlugin = () => (0, import_preact.usePlugin)(import_plugin_zoom.ZoomPlugin.id);
48
+ var useZoom = () => {
49
+ const { provides: zoomCapability } = useZoomCapability();
50
+ const [currentZoomLevel, setCurrentZoomLevel] = (0, import_hooks.useState)(1);
51
+ const [currentZoom, setCurrentZoom] = (0, import_hooks.useState)(1);
52
+ (0, import_hooks.useEffect)(() => {
53
+ return zoomCapability?.onZoomChange((action) => {
54
+ setCurrentZoomLevel(action.level);
55
+ setCurrentZoom(action.newZoom);
56
+ });
57
+ }, [zoomCapability]);
58
+ return {
59
+ ...zoomCapability,
60
+ currentZoomLevel,
61
+ currentZoom
62
+ };
63
+ };
46
64
 
47
65
  // src/preact/hooks/use-pinch-zoom.ts
48
- var import_hooks = require("preact/hooks");
66
+ var import_hooks2 = require("preact/hooks");
49
67
  var import_preact2 = require("@embedpdf/core/preact");
50
68
  function usePinch() {
51
69
  const { provides: viewportProvides } = (0, import_preact2.useCapability)("viewport");
52
70
  const { provides: zoomProvides } = useZoomCapability();
53
- const elementRef = (0, import_hooks.useRef)(null);
54
- (0, import_hooks.useEffect)(() => {
71
+ const elementRef = (0, import_hooks2.useRef)(null);
72
+ (0, import_hooks2.useEffect)(() => {
55
73
  const element = elementRef.current;
56
74
  if (!element || !viewportProvides || !zoomProvides) {
57
75
  return;
@@ -153,7 +171,7 @@ function PinchWrapper({ children, style, ...props }) {
153
171
  }
154
172
 
155
173
  // src/preact/components/marquee-zoom.tsx
156
- var import_hooks3 = require("preact/hooks");
174
+ var import_hooks4 = require("preact/hooks");
157
175
  var import_preact3 = require("@embedpdf/plugin-interaction-manager/preact");
158
176
  var import_jsx_runtime2 = require("preact/jsx-runtime");
159
177
  var MarqueeZoom = ({
@@ -168,11 +186,11 @@ var MarqueeZoom = ({
168
186
  const { provides: zoom } = useZoomCapability();
169
187
  const { register } = (0, import_preact3.usePointerHandlers)({ modeId: "marqueeZoom", pageIndex });
170
188
  const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
171
- const startRef = (0, import_hooks3.useRef)(null);
172
- const [rect, setRect] = (0, import_hooks3.useState)(null);
189
+ const startRef = (0, import_hooks4.useRef)(null);
190
+ const [rect, setRect] = (0, import_hooks4.useState)(null);
173
191
  const pageWidthPDF = pageWidth / scale;
174
192
  const pageHeightPDF = pageHeight / scale;
175
- const handlers = (0, import_hooks3.useMemo)(
193
+ const handlers = (0, import_hooks4.useMemo)(
176
194
  () => ({
177
195
  onPointerDown: (pos, evt) => {
178
196
  startRef.current = pos;
@@ -211,7 +229,7 @@ var MarqueeZoom = ({
211
229
  }),
212
230
  [pageWidthPDF, pageWidthPDF, zoom, scale, rect, pageIndex]
213
231
  );
214
- (0, import_hooks3.useEffect)(() => {
232
+ (0, import_hooks4.useEffect)(() => {
215
233
  if (!register) return;
216
234
  return register(handlers);
217
235
  }, [register, handlers]);
@@ -241,6 +259,7 @@ var MarqueeZoom = ({
241
259
  PinchWrapper,
242
260
  usePinch,
243
261
  useZoom,
244
- useZoomCapability
262
+ useZoomCapability,
263
+ useZoomPlugin
245
264
  });
246
265
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/preact/index.ts","../../src/preact/hooks/use-zoom.ts","../../src/preact/hooks/use-pinch-zoom.ts","../../src/preact/components/pinch-wrapper.tsx","../../src/preact/components/marquee-zoom.tsx"],"sourcesContent":["export * from './hooks';\nexport * from './components';\n","import { useCapability, usePlugin } from '@embedpdf/core/preact';\nimport { ZoomPlugin } from '@embedpdf/plugin-zoom';\n\nexport const useZoomCapability = () => useCapability<ZoomPlugin>(ZoomPlugin.id);\nexport const useZoom = () => usePlugin<ZoomPlugin>(ZoomPlugin.id);\n","import { useEffect, useRef } from 'preact/hooks';\nimport { useCapability } from '@embedpdf/core/preact';\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","/** @jsxImportSource preact */\nimport { ComponentChildren, JSX } from 'preact';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ComponentChildren;\n style?: JSX.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","/** @jsxImportSource preact */\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/preact';\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":["import_preact","import_hooks","import_preact","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/preact/index.ts","../../src/preact/hooks/use-zoom.ts","../../src/preact/hooks/use-pinch-zoom.ts","../../src/preact/components/pinch-wrapper.tsx","../../src/preact/components/marquee-zoom.tsx"],"sourcesContent":["export * from './hooks';\nexport * from './components';\n","import { useCapability, usePlugin } from '@embedpdf/core/preact';\nimport { ZoomLevel, ZoomPlugin } from '@embedpdf/plugin-zoom';\nimport { useEffect, useState } from 'preact/hooks';\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 'preact/hooks';\nimport { useCapability } from '@embedpdf/core/preact';\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","/** @jsxImportSource preact */\nimport { ComponentChildren, JSX } from 'preact';\n\nimport { usePinch } from '../hooks';\n\ntype PinchWrapperProps = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'style'> & {\n children: ComponentChildren;\n style?: JSX.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","/** @jsxImportSource preact */\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport type { PointerEventHandlers } from '@embedpdf/plugin-interaction-manager';\nimport { usePointerHandlers } from '@embedpdf/plugin-interaction-manager/preact';\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":["import_hooks","import_preact","import_hooks","import_preact","import_jsx_runtime"]}