@embedpdf/plugin-zoom 1.0.0
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/LICENSE +21 -0
- package/dist/index.cjs +439 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +140 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +416 -0
- package/dist/index.js.map +1 -0
- package/dist/preact/index.cjs +246 -0
- package/dist/preact/index.cjs.map +1 -0
- package/dist/preact/index.d.cts +50 -0
- package/dist/preact/index.d.ts +50 -0
- package/dist/preact/index.js +205 -0
- package/dist/preact/index.js.map +1 -0
- package/dist/react/index.cjs +246 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +49 -0
- package/dist/react/index.d.ts +49 -0
- package/dist/react/index.js +205 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CloudPDF
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
VerticalZoomFocus: () => VerticalZoomFocus,
|
|
24
|
+
ZOOM_PLUGIN_ID: () => ZOOM_PLUGIN_ID,
|
|
25
|
+
ZoomMode: () => ZoomMode,
|
|
26
|
+
ZoomPlugin: () => ZoomPlugin,
|
|
27
|
+
ZoomPluginPackage: () => ZoomPluginPackage,
|
|
28
|
+
manifest: () => manifest
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/lib/types.ts
|
|
33
|
+
var ZoomMode = /* @__PURE__ */ ((ZoomMode2) => {
|
|
34
|
+
ZoomMode2["Automatic"] = "automatic";
|
|
35
|
+
ZoomMode2["FitPage"] = "fit-page";
|
|
36
|
+
ZoomMode2["FitWidth"] = "fit-width";
|
|
37
|
+
return ZoomMode2;
|
|
38
|
+
})(ZoomMode || {});
|
|
39
|
+
var VerticalZoomFocus = /* @__PURE__ */ ((VerticalZoomFocus2) => {
|
|
40
|
+
VerticalZoomFocus2[VerticalZoomFocus2["Center"] = 0] = "Center";
|
|
41
|
+
VerticalZoomFocus2[VerticalZoomFocus2["Top"] = 1] = "Top";
|
|
42
|
+
return VerticalZoomFocus2;
|
|
43
|
+
})(VerticalZoomFocus || {});
|
|
44
|
+
|
|
45
|
+
// src/lib/manifest.ts
|
|
46
|
+
var ZOOM_PLUGIN_ID = "zoom";
|
|
47
|
+
var manifest = {
|
|
48
|
+
id: ZOOM_PLUGIN_ID,
|
|
49
|
+
name: "Zoom Plugin",
|
|
50
|
+
version: "1.0.0",
|
|
51
|
+
provides: ["zoom"],
|
|
52
|
+
requires: ["viewport", "scroll"],
|
|
53
|
+
optional: ["interaction-manager"],
|
|
54
|
+
defaultConfig: {
|
|
55
|
+
enabled: true,
|
|
56
|
+
defaultZoomLevel: "automatic" /* Automatic */,
|
|
57
|
+
minZoom: 0.2,
|
|
58
|
+
maxZoom: 60,
|
|
59
|
+
zoomStep: 0.1,
|
|
60
|
+
zoomRanges: [
|
|
61
|
+
{
|
|
62
|
+
min: 0.2,
|
|
63
|
+
max: 0.5,
|
|
64
|
+
step: 0.05
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
min: 0.5,
|
|
68
|
+
max: 1,
|
|
69
|
+
step: 0.1
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
min: 1,
|
|
73
|
+
max: 2,
|
|
74
|
+
step: 0.2
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
min: 2,
|
|
78
|
+
max: 4,
|
|
79
|
+
step: 0.4
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
min: 4,
|
|
83
|
+
max: 10,
|
|
84
|
+
step: 0.8
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
presets: [
|
|
88
|
+
{
|
|
89
|
+
name: "Fit Page",
|
|
90
|
+
value: "fit-page" /* FitPage */
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "Fit Width",
|
|
94
|
+
value: "fit-width" /* FitWidth */
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "Automatic",
|
|
98
|
+
value: "automatic" /* Automatic */
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "10%",
|
|
102
|
+
value: 0.1
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "20%",
|
|
106
|
+
value: 0.2
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "30%",
|
|
110
|
+
value: 0.3
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "50%",
|
|
114
|
+
value: 0.5
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "70%",
|
|
118
|
+
value: 0.7
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "100%",
|
|
122
|
+
value: 1
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "200%",
|
|
126
|
+
value: 2
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "400%",
|
|
130
|
+
value: 4
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/lib/actions.ts
|
|
137
|
+
var SET_ZOOM_LEVEL = "SET_ZOOM_LEVEL";
|
|
138
|
+
var SET_INITIAL_ZOOM_LEVEL = "SET_INITIAL_ZOOM_LEVEL";
|
|
139
|
+
function setZoomLevel(zoomLevel, currentZoomLevel) {
|
|
140
|
+
return {
|
|
141
|
+
type: SET_ZOOM_LEVEL,
|
|
142
|
+
payload: { zoomLevel, currentZoomLevel }
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function setInitialZoomLevel(zoomLevel) {
|
|
146
|
+
return {
|
|
147
|
+
type: SET_INITIAL_ZOOM_LEVEL,
|
|
148
|
+
payload: { zoomLevel }
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/lib/reducer.ts
|
|
153
|
+
var initialState = {
|
|
154
|
+
zoomLevel: "automatic" /* Automatic */,
|
|
155
|
+
currentZoomLevel: 1,
|
|
156
|
+
zoomReady: false
|
|
157
|
+
};
|
|
158
|
+
var zoomReducer = (state = initialState, action) => {
|
|
159
|
+
switch (action.type) {
|
|
160
|
+
case SET_ZOOM_LEVEL:
|
|
161
|
+
return {
|
|
162
|
+
...state,
|
|
163
|
+
zoomLevel: action.payload.zoomLevel,
|
|
164
|
+
currentZoomLevel: action.payload.currentZoomLevel,
|
|
165
|
+
zoomReady: true
|
|
166
|
+
};
|
|
167
|
+
case SET_INITIAL_ZOOM_LEVEL:
|
|
168
|
+
return {
|
|
169
|
+
...state,
|
|
170
|
+
zoomLevel: action.payload.zoomLevel
|
|
171
|
+
};
|
|
172
|
+
default:
|
|
173
|
+
return state;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/lib/zoom-plugin.ts
|
|
178
|
+
var import_core = require("@embedpdf/core");
|
|
179
|
+
var ZoomPlugin = class extends import_core.BasePlugin {
|
|
180
|
+
/* ------------------------------------------------------------------ */
|
|
181
|
+
constructor(id, registry, cfg) {
|
|
182
|
+
super(id, registry);
|
|
183
|
+
/* ------------------------------------------------------------------ */
|
|
184
|
+
/* internals */
|
|
185
|
+
/* ------------------------------------------------------------------ */
|
|
186
|
+
this.zoom$ = (0, import_core.createEmitter)();
|
|
187
|
+
this.viewport = registry.getPlugin("viewport").provides();
|
|
188
|
+
this.scroll = registry.getPlugin("scroll").provides();
|
|
189
|
+
const interactionManager = registry.getPlugin("interaction-manager");
|
|
190
|
+
this.interactionManager = interactionManager?.provides() ?? null;
|
|
191
|
+
this.minZoom = cfg.minZoom ?? 0.25;
|
|
192
|
+
this.maxZoom = cfg.maxZoom ?? 10;
|
|
193
|
+
this.zoomStep = cfg.zoomStep ?? 0.1;
|
|
194
|
+
this.presets = cfg.presets ?? [];
|
|
195
|
+
this.zoomRanges = this.normalizeRanges(cfg.zoomRanges ?? []);
|
|
196
|
+
this.dispatch(setInitialZoomLevel(cfg.defaultZoomLevel));
|
|
197
|
+
this.viewport.onViewportChange(() => this.recalcAuto(1 /* Top */), {
|
|
198
|
+
mode: "debounce",
|
|
199
|
+
wait: 150
|
|
200
|
+
});
|
|
201
|
+
this.coreStore.onAction(import_core.SET_ROTATION, () => this.recalcAuto(1 /* Top */));
|
|
202
|
+
this.coreStore.onAction(import_core.SET_PAGES, () => this.recalcAuto(1 /* Top */));
|
|
203
|
+
this.coreStore.onAction(import_core.SET_DOCUMENT, () => this.recalcAuto(1 /* Top */));
|
|
204
|
+
this.interactionManager?.registerMode({
|
|
205
|
+
id: "marqueeZoom",
|
|
206
|
+
scope: "page",
|
|
207
|
+
exclusive: true,
|
|
208
|
+
cursor: "zoom-in"
|
|
209
|
+
});
|
|
210
|
+
this.resetReady();
|
|
211
|
+
}
|
|
212
|
+
/* ------------------------------------------------------------------ */
|
|
213
|
+
/* capability */
|
|
214
|
+
/* ------------------------------------------------------------------ */
|
|
215
|
+
buildCapability() {
|
|
216
|
+
return {
|
|
217
|
+
onZoomChange: this.zoom$.on,
|
|
218
|
+
zoomIn: () => {
|
|
219
|
+
const cur = this.state.currentZoomLevel;
|
|
220
|
+
return this.handleRequest({ level: cur, delta: this.stepFor(cur) });
|
|
221
|
+
},
|
|
222
|
+
zoomOut: () => {
|
|
223
|
+
const cur = this.state.currentZoomLevel;
|
|
224
|
+
return this.handleRequest({ level: cur, delta: -this.stepFor(cur) });
|
|
225
|
+
},
|
|
226
|
+
zoomToArea: (pageIndex, rect) => this.handleZoomToArea(pageIndex, rect),
|
|
227
|
+
requestZoom: (level, c) => this.handleRequest({ level, center: c }),
|
|
228
|
+
requestZoomBy: (d, c) => {
|
|
229
|
+
const cur = this.state.currentZoomLevel;
|
|
230
|
+
const target = this.toZoom(cur + d);
|
|
231
|
+
return this.handleRequest({ level: target, center: c });
|
|
232
|
+
},
|
|
233
|
+
enableMarqueeZoom: () => {
|
|
234
|
+
this.interactionManager?.activate("marqueeZoom");
|
|
235
|
+
},
|
|
236
|
+
disableMarqueeZoom: () => {
|
|
237
|
+
this.interactionManager?.activate("default");
|
|
238
|
+
},
|
|
239
|
+
isMarqueeZoomActive: () => this.interactionManager?.getActiveMode() === "marqueeZoom",
|
|
240
|
+
getState: () => this.state,
|
|
241
|
+
getPresets: () => this.presets
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/* ------------------------------------------------------------------ */
|
|
245
|
+
/* plugin life‑cycle */
|
|
246
|
+
/* ------------------------------------------------------------------ */
|
|
247
|
+
async initialize() {
|
|
248
|
+
}
|
|
249
|
+
async destroy() {
|
|
250
|
+
this.zoom$.clear();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Sort ranges once, make sure they are sane
|
|
254
|
+
*/
|
|
255
|
+
normalizeRanges(ranges) {
|
|
256
|
+
return [...ranges].filter((r) => r.step > 0 && r.max > r.min).sort((a, b) => a.min - b.min);
|
|
257
|
+
}
|
|
258
|
+
/** pick the step that applies to a given numeric zoom */
|
|
259
|
+
stepFor(zoom) {
|
|
260
|
+
const r = this.zoomRanges.find((r2) => zoom >= r2.min && zoom < r2.max);
|
|
261
|
+
return r ? r.step : this.zoomStep;
|
|
262
|
+
}
|
|
263
|
+
/** clamp + round helper reused later */
|
|
264
|
+
toZoom(v) {
|
|
265
|
+
return parseFloat((0, import_core.clamp)(v, this.minZoom, this.maxZoom).toFixed(2));
|
|
266
|
+
}
|
|
267
|
+
/* ------------------------------------------------------------------ */
|
|
268
|
+
/* main entry – handles **every** zoom request */
|
|
269
|
+
/* ------------------------------------------------------------------ */
|
|
270
|
+
handleRequest({
|
|
271
|
+
level,
|
|
272
|
+
delta = 0,
|
|
273
|
+
center,
|
|
274
|
+
focus = 0 /* Center */,
|
|
275
|
+
align = "keep"
|
|
276
|
+
}) {
|
|
277
|
+
const metrics = this.viewport.getMetrics();
|
|
278
|
+
const oldZoom = this.state.currentZoomLevel;
|
|
279
|
+
if (metrics.clientWidth === 0 || metrics.clientHeight === 0) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const base = typeof level === "number" ? level : this.computeZoomForMode(level, metrics);
|
|
283
|
+
if (base === false) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const exactZoom = (0, import_core.clamp)(base + delta, this.minZoom, this.maxZoom);
|
|
287
|
+
const newZoom = Math.floor(exactZoom * 100) / 100;
|
|
288
|
+
const focusPoint = center ?? {
|
|
289
|
+
vx: metrics.clientWidth / 2,
|
|
290
|
+
vy: focus === 1 /* Top */ ? 0 : metrics.clientHeight / 2
|
|
291
|
+
};
|
|
292
|
+
const { desiredScrollLeft, desiredScrollTop } = this.computeScrollForZoomChange(
|
|
293
|
+
metrics,
|
|
294
|
+
oldZoom,
|
|
295
|
+
newZoom,
|
|
296
|
+
focusPoint,
|
|
297
|
+
align
|
|
298
|
+
);
|
|
299
|
+
if (!isNaN(desiredScrollLeft) && !isNaN(desiredScrollTop)) {
|
|
300
|
+
this.viewport.setViewportScrollMetrics({
|
|
301
|
+
scrollLeft: desiredScrollLeft,
|
|
302
|
+
scrollTop: desiredScrollTop
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
this.dispatch(setZoomLevel(typeof level === "number" ? newZoom : level, newZoom));
|
|
306
|
+
this.dispatchCoreAction((0, import_core.setScale)(newZoom));
|
|
307
|
+
this.markReady();
|
|
308
|
+
this.viewport.scrollTo({
|
|
309
|
+
x: desiredScrollLeft,
|
|
310
|
+
y: desiredScrollTop,
|
|
311
|
+
behavior: "instant"
|
|
312
|
+
});
|
|
313
|
+
const evt = {
|
|
314
|
+
oldZoom,
|
|
315
|
+
newZoom,
|
|
316
|
+
level,
|
|
317
|
+
center: focusPoint,
|
|
318
|
+
desiredScrollLeft,
|
|
319
|
+
desiredScrollTop,
|
|
320
|
+
viewport: metrics
|
|
321
|
+
};
|
|
322
|
+
this.zoom$.emit(evt);
|
|
323
|
+
}
|
|
324
|
+
/* ------------------------------------------------------------------ */
|
|
325
|
+
/* helpers */
|
|
326
|
+
/* ------------------------------------------------------------------ */
|
|
327
|
+
/** numeric zoom for Automatic / FitPage / FitWidth */
|
|
328
|
+
computeZoomForMode(mode, vp) {
|
|
329
|
+
const spreads = (0, import_core.getPagesWithRotatedSize)(this.coreState.core);
|
|
330
|
+
if (!spreads.length) return false;
|
|
331
|
+
const pgGap = this.scroll.getPageGap();
|
|
332
|
+
const vpGap = this.viewport.getViewportGap();
|
|
333
|
+
if (vp.clientWidth === 0 || vp.clientHeight === 0) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
const availableWidth = vp.clientWidth - 2 * vpGap;
|
|
337
|
+
const availableHeight = vp.clientHeight - 2 * vpGap;
|
|
338
|
+
if (availableWidth <= 0 || availableHeight <= 0) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
let maxContentW = 0, maxContentH = 0;
|
|
342
|
+
spreads.forEach((spread) => {
|
|
343
|
+
const contentW = spread.reduce((s, p, i) => s + p.rotatedSize.width + (i ? pgGap : 0), 0);
|
|
344
|
+
const contentH = Math.max(...spread.map((p) => p.rotatedSize.height));
|
|
345
|
+
maxContentW = Math.max(maxContentW, contentW);
|
|
346
|
+
maxContentH = Math.max(maxContentH, contentH);
|
|
347
|
+
});
|
|
348
|
+
switch (mode) {
|
|
349
|
+
case "fit-width" /* FitWidth */:
|
|
350
|
+
return availableWidth / maxContentW;
|
|
351
|
+
case "fit-page" /* FitPage */:
|
|
352
|
+
return Math.min(availableWidth / maxContentW, availableHeight / maxContentH);
|
|
353
|
+
case "automatic" /* Automatic */:
|
|
354
|
+
return Math.min(availableWidth / maxContentW, 1);
|
|
355
|
+
/* istanbul ignore next */
|
|
356
|
+
default:
|
|
357
|
+
return 1;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/** where to scroll so that *focus* stays stable after scaling */
|
|
361
|
+
computeScrollForZoomChange(vp, oldZoom, newZoom, focus, align = "keep") {
|
|
362
|
+
const layout = this.scroll.getLayout();
|
|
363
|
+
const vpGap = this.viewport.getViewportGap();
|
|
364
|
+
const contentW = layout.totalContentSize.width;
|
|
365
|
+
const contentH = layout.totalContentSize.height;
|
|
366
|
+
const availableWidth = vp.clientWidth - 2 * vpGap;
|
|
367
|
+
const availableHeight = vp.clientHeight - 2 * vpGap;
|
|
368
|
+
const off = (availableSpace, cw, zoom) => cw * zoom < availableSpace ? (availableSpace - cw * zoom) / 2 : 0;
|
|
369
|
+
const offXold = off(availableWidth, contentW, oldZoom);
|
|
370
|
+
const offYold = off(availableHeight, contentH, oldZoom);
|
|
371
|
+
const offXnew = off(availableWidth, contentW, newZoom);
|
|
372
|
+
const offYnew = off(availableHeight, contentH, newZoom);
|
|
373
|
+
const cx = (vp.scrollLeft + focus.vx - vpGap - offXold) / oldZoom;
|
|
374
|
+
const cy = (vp.scrollTop + focus.vy - vpGap - offYold) / oldZoom;
|
|
375
|
+
const baseLeft = cx * newZoom + vpGap + offXnew;
|
|
376
|
+
const baseTop = cy * newZoom + vpGap + offYnew;
|
|
377
|
+
const desiredScrollLeft = align === "center" ? baseLeft - vp.clientWidth / 2 : baseLeft - focus.vx;
|
|
378
|
+
const desiredScrollTop = align === "center" ? baseTop - vp.clientHeight / 2 : baseTop - focus.vy;
|
|
379
|
+
return {
|
|
380
|
+
desiredScrollLeft: Math.max(0, desiredScrollLeft),
|
|
381
|
+
desiredScrollTop: Math.max(0, desiredScrollTop)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
handleZoomToArea(pageIndex, rect) {
|
|
385
|
+
const vp = this.viewport.getMetrics();
|
|
386
|
+
const vpGap = this.viewport.getViewportGap();
|
|
387
|
+
const oldZ = this.state.currentZoomLevel;
|
|
388
|
+
const availableW = vp.clientWidth - 2 * vpGap;
|
|
389
|
+
const availableH = vp.clientHeight - 2 * vpGap;
|
|
390
|
+
const targetZoom = this.toZoom(
|
|
391
|
+
Math.min(availableW / rect.size.width, availableH / rect.size.height)
|
|
392
|
+
);
|
|
393
|
+
const layout = this.scroll.getLayout();
|
|
394
|
+
const vItem = layout.virtualItems.find(
|
|
395
|
+
(it) => it.pageLayouts.some((p) => p.pageIndex === pageIndex)
|
|
396
|
+
);
|
|
397
|
+
if (!vItem) return;
|
|
398
|
+
const pageRel = vItem.pageLayouts.find((p) => p.pageIndex === pageIndex);
|
|
399
|
+
const pageAbsX = vItem.x + pageRel.x;
|
|
400
|
+
const pageAbsY = vItem.y + pageRel.y;
|
|
401
|
+
const cxContent = pageAbsX + rect.origin.x + rect.size.width / 2;
|
|
402
|
+
const cyContent = pageAbsY + rect.origin.y + rect.size.height / 2;
|
|
403
|
+
const off = (avail, cw, z) => cw * z < avail ? (avail - cw * z) / 2 : 0;
|
|
404
|
+
const offXold = off(availableW, layout.totalContentSize.width, oldZ);
|
|
405
|
+
const offYold = off(availableH, layout.totalContentSize.height, oldZ);
|
|
406
|
+
const centerVX = vpGap + offXold + cxContent * oldZ - vp.scrollLeft;
|
|
407
|
+
const centerVY = vpGap + offYold + cyContent * oldZ - vp.scrollTop;
|
|
408
|
+
this.handleRequest({
|
|
409
|
+
level: targetZoom,
|
|
410
|
+
center: { vx: centerVX, vy: centerVY },
|
|
411
|
+
align: "center"
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
/** recalculates Automatic / Fit* when viewport or pages change */
|
|
415
|
+
recalcAuto(focus) {
|
|
416
|
+
const s = this.state;
|
|
417
|
+
if (s.zoomLevel === "automatic" /* Automatic */ || s.zoomLevel === "fit-page" /* FitPage */ || s.zoomLevel === "fit-width" /* FitWidth */)
|
|
418
|
+
this.handleRequest({ level: s.zoomLevel, focus });
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
ZoomPlugin.id = "zoom";
|
|
422
|
+
|
|
423
|
+
// src/lib/index.ts
|
|
424
|
+
var ZoomPluginPackage = {
|
|
425
|
+
manifest,
|
|
426
|
+
create: (registry, _engine, config) => new ZoomPlugin(ZOOM_PLUGIN_ID, registry, config),
|
|
427
|
+
reducer: zoomReducer,
|
|
428
|
+
initialState
|
|
429
|
+
};
|
|
430
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
431
|
+
0 && (module.exports = {
|
|
432
|
+
VerticalZoomFocus,
|
|
433
|
+
ZOOM_PLUGIN_ID,
|
|
434
|
+
ZoomMode,
|
|
435
|
+
ZoomPlugin,
|
|
436
|
+
ZoomPluginPackage,
|
|
437
|
+
manifest
|
|
438
|
+
});
|
|
439
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { BasePluginConfig, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
|
|
2
|
+
import { Rect } from '@embedpdf/models';
|
|
3
|
+
import { ViewportMetrics } from '@embedpdf/plugin-viewport';
|
|
4
|
+
|
|
5
|
+
declare enum ZoomMode {
|
|
6
|
+
Automatic = "automatic",
|
|
7
|
+
FitPage = "fit-page",
|
|
8
|
+
FitWidth = "fit-width"
|
|
9
|
+
}
|
|
10
|
+
type ZoomLevel = ZoomMode | number;
|
|
11
|
+
interface Point {
|
|
12
|
+
vx: number;
|
|
13
|
+
vy: number;
|
|
14
|
+
}
|
|
15
|
+
interface ZoomChangeEvent {
|
|
16
|
+
/** old and new *actual* scale factors */
|
|
17
|
+
oldZoom: number;
|
|
18
|
+
newZoom: number;
|
|
19
|
+
/** level used to obtain the newZoom (number | mode) */
|
|
20
|
+
level: ZoomLevel;
|
|
21
|
+
/** viewport point kept under the finger / mouse‑wheel focus */
|
|
22
|
+
center: Point;
|
|
23
|
+
/** where the viewport should scroll to after the scale change */
|
|
24
|
+
desiredScrollLeft: number;
|
|
25
|
+
desiredScrollTop: number;
|
|
26
|
+
/** metrics at the moment the zoom was requested */
|
|
27
|
+
viewport: ViewportMetrics;
|
|
28
|
+
}
|
|
29
|
+
interface ZoomCapability {
|
|
30
|
+
/** subscribe – returns the unsubscribe function */
|
|
31
|
+
onZoomChange(handler: (e: ZoomChangeEvent) => void): () => void;
|
|
32
|
+
/** absolute requests -------------------------------------------------- */
|
|
33
|
+
requestZoom(level: ZoomLevel, center?: Point): void;
|
|
34
|
+
/** relative requests -------------------------------------------------- */
|
|
35
|
+
requestZoomBy(delta: number, center?: Point): void;
|
|
36
|
+
/** absolute requests -------------------------------------------------- */
|
|
37
|
+
zoomIn(): void;
|
|
38
|
+
zoomOut(): void;
|
|
39
|
+
zoomToArea(pageIndex: number, rect: Rect): void;
|
|
40
|
+
/** zoom in on an area -------------------------------------------------- */
|
|
41
|
+
enableMarqueeZoom(): void;
|
|
42
|
+
disableMarqueeZoom(): void;
|
|
43
|
+
isMarqueeZoomActive(): boolean;
|
|
44
|
+
getState(): ZoomState;
|
|
45
|
+
getPresets(): ZoomPreset[];
|
|
46
|
+
}
|
|
47
|
+
interface ZoomRangeStep {
|
|
48
|
+
min: number;
|
|
49
|
+
max: number;
|
|
50
|
+
step: number;
|
|
51
|
+
}
|
|
52
|
+
interface ZoomPreset {
|
|
53
|
+
name: string;
|
|
54
|
+
value: ZoomLevel;
|
|
55
|
+
icon?: string;
|
|
56
|
+
}
|
|
57
|
+
interface ZoomPluginConfig extends BasePluginConfig {
|
|
58
|
+
defaultZoomLevel: ZoomLevel;
|
|
59
|
+
minZoom?: number;
|
|
60
|
+
maxZoom?: number;
|
|
61
|
+
zoomStep?: number;
|
|
62
|
+
zoomRanges?: ZoomRangeStep[];
|
|
63
|
+
presets?: ZoomPreset[];
|
|
64
|
+
}
|
|
65
|
+
interface ZoomState {
|
|
66
|
+
zoomLevel: ZoomLevel;
|
|
67
|
+
currentZoomLevel: number;
|
|
68
|
+
zoomReady: boolean;
|
|
69
|
+
}
|
|
70
|
+
declare enum VerticalZoomFocus {
|
|
71
|
+
Center = 0,
|
|
72
|
+
Top = 1
|
|
73
|
+
}
|
|
74
|
+
interface ZoomRequest {
|
|
75
|
+
level: ZoomLevel;
|
|
76
|
+
delta?: number;
|
|
77
|
+
center?: Point;
|
|
78
|
+
focus?: VerticalZoomFocus;
|
|
79
|
+
/** Scroll so that the focal point ends up …
|
|
80
|
+
* ▸ `"keep"` (default) at the same viewport coords
|
|
81
|
+
* ▸ `"center"` centred in the viewport */
|
|
82
|
+
align?: 'keep' | 'center';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare const SET_ZOOM_LEVEL = "SET_ZOOM_LEVEL";
|
|
86
|
+
declare const SET_INITIAL_ZOOM_LEVEL = "SET_INITIAL_ZOOM_LEVEL";
|
|
87
|
+
interface SetZoomLevelAction extends Action {
|
|
88
|
+
type: typeof SET_ZOOM_LEVEL;
|
|
89
|
+
payload: {
|
|
90
|
+
zoomLevel: ZoomLevel;
|
|
91
|
+
currentZoomLevel: number;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
interface SetInitialZoomLevelAction extends Action {
|
|
95
|
+
type: typeof SET_INITIAL_ZOOM_LEVEL;
|
|
96
|
+
payload: {
|
|
97
|
+
zoomLevel: ZoomLevel;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
type ZoomAction = SetZoomLevelAction | SetInitialZoomLevelAction;
|
|
101
|
+
|
|
102
|
+
declare class ZoomPlugin extends BasePlugin<ZoomPluginConfig, ZoomCapability, ZoomState, ZoomAction> {
|
|
103
|
+
static readonly id: "zoom";
|
|
104
|
+
private readonly zoom$;
|
|
105
|
+
private readonly viewport;
|
|
106
|
+
private readonly scroll;
|
|
107
|
+
private readonly interactionManager;
|
|
108
|
+
private readonly presets;
|
|
109
|
+
private readonly zoomRanges;
|
|
110
|
+
private readonly minZoom;
|
|
111
|
+
private readonly maxZoom;
|
|
112
|
+
private readonly zoomStep;
|
|
113
|
+
constructor(id: string, registry: PluginRegistry, cfg: ZoomPluginConfig);
|
|
114
|
+
protected buildCapability(): ZoomCapability;
|
|
115
|
+
initialize(): Promise<void>;
|
|
116
|
+
destroy(): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Sort ranges once, make sure they are sane
|
|
119
|
+
*/
|
|
120
|
+
private normalizeRanges;
|
|
121
|
+
/** pick the step that applies to a given numeric zoom */
|
|
122
|
+
private stepFor;
|
|
123
|
+
/** clamp + round helper reused later */
|
|
124
|
+
private toZoom;
|
|
125
|
+
private handleRequest;
|
|
126
|
+
/** numeric zoom for Automatic / FitPage / FitWidth */
|
|
127
|
+
private computeZoomForMode;
|
|
128
|
+
/** where to scroll so that *focus* stays stable after scaling */
|
|
129
|
+
private computeScrollForZoomChange;
|
|
130
|
+
private handleZoomToArea;
|
|
131
|
+
/** recalculates Automatic / Fit* when viewport or pages change */
|
|
132
|
+
private recalcAuto;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare const ZOOM_PLUGIN_ID = "zoom";
|
|
136
|
+
declare const manifest: PluginManifest<ZoomPluginConfig>;
|
|
137
|
+
|
|
138
|
+
declare const ZoomPluginPackage: PluginPackage<ZoomPlugin, ZoomPluginConfig, ZoomState, ZoomAction>;
|
|
139
|
+
|
|
140
|
+
export { type Point, VerticalZoomFocus, ZOOM_PLUGIN_ID, type ZoomCapability, type ZoomChangeEvent, type ZoomLevel, ZoomMode, ZoomPlugin, type ZoomPluginConfig, ZoomPluginPackage, type ZoomPreset, type ZoomRangeStep, type ZoomRequest, type ZoomState, manifest };
|