kviewer 0.3.5 → 0.3.6
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/module.json +1 -1
- package/dist/runtime/components/Viewer.d.vue.ts +4 -0
- package/dist/runtime/components/Viewer.vue +26 -4
- package/dist/runtime/components/Viewer.vue.d.ts +4 -0
- package/dist/runtime/composables/useInertiaPanzoom.d.ts +8 -2
- package/dist/runtime/composables/useInertiaPanzoom.js +36 -12
- package/dist/runtime/embed/bridge-client.d.ts +1 -1
- package/dist/runtime/embed/bridge-host.d.ts +2 -1
- package/dist/runtime/embed/bridge-host.js +8 -0
- package/dist/runtime/embed/protocol.d.ts +7 -0
- package/dist/runtime/public-types.d.ts +1 -1
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ViewMode } from '../composables/useViewerState.js';
|
|
2
|
+
import type { ScrollBoundaryPayload } from '../embed/protocol.js';
|
|
2
3
|
import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, PlacedFieldDefaults, SignatureFieldStatus, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
3
4
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
5
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
@@ -154,6 +155,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
154
155
|
allSigned: boolean;
|
|
155
156
|
fields: SignatureFieldStatus[];
|
|
156
157
|
}) => void) => (() => void);
|
|
158
|
+
/** Subscribe to vertical wheel movement left over when the viewer reaches
|
|
159
|
+
* a document boundary. Used by the embed bridge for parent scrolling. */
|
|
160
|
+
onScrollBoundary: (cb: (payload: ScrollBoundaryPayload) => void) => (() => void);
|
|
157
161
|
/** Reactive form-edit-mode flag — read with `.value`. */
|
|
158
162
|
formEditMode: import("vue").Ref<boolean, boolean>;
|
|
159
163
|
/** Programmatically enter or leave form-edit mode. */
|
|
@@ -263,6 +263,7 @@ const fieldPlacedCallbacks = /* @__PURE__ */ new Set();
|
|
|
263
263
|
const signedStatusCallbacks = /* @__PURE__ */ new Set();
|
|
264
264
|
const fieldUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
265
265
|
const fieldRemovedCallbacks = /* @__PURE__ */ new Set();
|
|
266
|
+
const scrollBoundaryCallbacks = /* @__PURE__ */ new Set();
|
|
266
267
|
const viewerRoot = ref(null);
|
|
267
268
|
const scrollContainer = ref(null);
|
|
268
269
|
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn, setPrintPdfFn, setApplyScaleFn } = provideViewerState();
|
|
@@ -864,28 +865,43 @@ function initPanzoom() {
|
|
|
864
865
|
inertiaPanzoom.attach(zoomWrapper.value);
|
|
865
866
|
}
|
|
866
867
|
let wheelCooldown = false;
|
|
868
|
+
function notifyScrollBoundary(deltaY) {
|
|
869
|
+
if (deltaY === 0) return;
|
|
870
|
+
const payload = {
|
|
871
|
+
edge: deltaY > 0 ? "end" : "start",
|
|
872
|
+
deltaY
|
|
873
|
+
};
|
|
874
|
+
for (const callback of scrollBoundaryCallbacks) callback(payload);
|
|
875
|
+
}
|
|
867
876
|
function onWheel(event) {
|
|
868
877
|
if (event.ctrlKey || event.metaKey) return;
|
|
869
878
|
if (isSinglePageMode.value) {
|
|
870
879
|
event.preventDefault();
|
|
871
|
-
|
|
880
|
+
const total = viewerState.totalPages.value;
|
|
881
|
+
const cur = viewerState.currentPage.value;
|
|
882
|
+
const isAtBoundary = event.deltaY > 0 && cur >= total || event.deltaY < 0 && cur <= 1;
|
|
883
|
+
if (wheelCooldown) {
|
|
884
|
+
if (isAtBoundary) notifyScrollBoundary(event.deltaY);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
872
887
|
wheelCooldown = true;
|
|
873
888
|
setTimeout(() => {
|
|
874
889
|
wheelCooldown = false;
|
|
875
890
|
}, 300);
|
|
876
|
-
const total = viewerState.totalPages.value;
|
|
877
|
-
const cur = viewerState.currentPage.value;
|
|
878
891
|
if (event.deltaY > 0 && cur < total) {
|
|
879
892
|
viewerState.currentPage.value = cur + 1;
|
|
880
893
|
} else if (event.deltaY < 0 && cur > 1) {
|
|
881
894
|
viewerState.currentPage.value = cur - 1;
|
|
895
|
+
} else {
|
|
896
|
+
notifyScrollBoundary(event.deltaY);
|
|
882
897
|
}
|
|
883
898
|
return;
|
|
884
899
|
}
|
|
885
900
|
event.preventDefault();
|
|
886
901
|
const dx = event.shiftKey ? event.deltaY : event.deltaX;
|
|
887
902
|
const dy = event.shiftKey ? 0 : event.deltaY;
|
|
888
|
-
inertiaPanzoom.scrollBy(dx, dy);
|
|
903
|
+
const remaining = inertiaPanzoom.scrollBy(dx, dy);
|
|
904
|
+
notifyScrollBoundary(remaining.y);
|
|
889
905
|
}
|
|
890
906
|
function isTextInputTarget(target) {
|
|
891
907
|
if (!(target instanceof HTMLElement)) return false;
|
|
@@ -1181,6 +1197,12 @@ defineExpose({
|
|
|
1181
1197
|
signedStatusCallbacks.add(cb);
|
|
1182
1198
|
return () => signedStatusCallbacks.delete(cb);
|
|
1183
1199
|
},
|
|
1200
|
+
/** Subscribe to vertical wheel movement left over when the viewer reaches
|
|
1201
|
+
* a document boundary. Used by the embed bridge for parent scrolling. */
|
|
1202
|
+
onScrollBoundary: (cb) => {
|
|
1203
|
+
scrollBoundaryCallbacks.add(cb);
|
|
1204
|
+
return () => scrollBoundaryCallbacks.delete(cb);
|
|
1205
|
+
},
|
|
1184
1206
|
/** Reactive form-edit-mode flag — read with `.value`. */
|
|
1185
1207
|
formEditMode: viewerState.formEditMode,
|
|
1186
1208
|
/** Programmatically enter or leave form-edit mode. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ViewMode } from '../composables/useViewerState.js';
|
|
2
|
+
import type { ScrollBoundaryPayload } from '../embed/protocol.js';
|
|
2
3
|
import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, PlacedFieldDefaults, SignatureFieldStatus, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
3
4
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
5
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
@@ -154,6 +155,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
154
155
|
allSigned: boolean;
|
|
155
156
|
fields: SignatureFieldStatus[];
|
|
156
157
|
}) => void) => (() => void);
|
|
158
|
+
/** Subscribe to vertical wheel movement left over when the viewer reaches
|
|
159
|
+
* a document boundary. Used by the embed bridge for parent scrolling. */
|
|
160
|
+
onScrollBoundary: (cb: (payload: ScrollBoundaryPayload) => void) => (() => void);
|
|
157
161
|
/** Reactive form-edit-mode flag — read with `.value`. */
|
|
158
162
|
formEditMode: import("vue").Ref<boolean, boolean>;
|
|
159
163
|
/** Programmatically enter or leave form-edit mode. */
|
|
@@ -17,6 +17,11 @@ export interface Transform {
|
|
|
17
17
|
tx: number;
|
|
18
18
|
ty: number;
|
|
19
19
|
}
|
|
20
|
+
export interface ScrollDelta {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function getUnconsumedScrollDelta(requested: ScrollDelta, before: Transform, after: Transform): ScrollDelta;
|
|
20
25
|
export interface InertiaPanzoomHandle {
|
|
21
26
|
/** Bind gesture handlers to the given element (its parent becomes the viewport). */
|
|
22
27
|
attach: (target: HTMLElement) => void;
|
|
@@ -30,8 +35,9 @@ export interface InertiaPanzoomHandle {
|
|
|
30
35
|
getTransform: () => Transform;
|
|
31
36
|
/** Animate the view so `elem` is at the top (or center) of the viewport. */
|
|
32
37
|
scrollToElement: (elem: HTMLElement, opts?: ScrollToElementOptions) => void;
|
|
33
|
-
/** Pan by a delta in viewport pixels
|
|
34
|
-
|
|
38
|
+
/** Pan by a delta in viewport pixels and return the portion that could not
|
|
39
|
+
* be consumed because the viewport reached a boundary. */
|
|
40
|
+
scrollBy: (dx: number, dy: number) => ScrollDelta;
|
|
35
41
|
/** Enable/disable pan+pinch gestures (wheel/scrollbar/keyboard still work). */
|
|
36
42
|
setGesturesEnabled: (enabled: boolean) => void;
|
|
37
43
|
/** Subscribe to transform updates. Returns an unsubscribe function. */
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { onBeforeUnmount } from "vue";
|
|
2
|
+
export function getUnconsumedScrollDelta(requested, before, after) {
|
|
3
|
+
return {
|
|
4
|
+
x: requested.x - (before.tx - after.tx),
|
|
5
|
+
y: requested.y - (before.ty - after.ty)
|
|
6
|
+
};
|
|
7
|
+
}
|
|
2
8
|
const DECELERATION = 0.998;
|
|
3
9
|
const RUBBER_BAND_C = 0.55;
|
|
4
10
|
const BOUNCE_BACK_RATE = 0.04;
|
|
5
11
|
const BOUNCE_VELOCITY_DECAY = 0.04;
|
|
6
12
|
const MIN_VELOCITY = 0.01;
|
|
7
13
|
export function useInertiaPanzoom(options = {}) {
|
|
8
|
-
const {
|
|
9
|
-
axisLockThreshold = 10,
|
|
10
|
-
axisLockRatio = 2
|
|
11
|
-
} = options;
|
|
14
|
+
const { axisLockThreshold = 10, axisLockRatio = 2 } = options;
|
|
12
15
|
let minScale = options.minScale ?? 0.5;
|
|
13
16
|
let maxScale = options.maxScale ?? 4;
|
|
14
17
|
const subscribers = /* @__PURE__ */ new Set();
|
|
@@ -139,11 +142,17 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
139
142
|
if (barDrag.axis === "y") {
|
|
140
143
|
const dy = e.clientY - barDrag.startPointer;
|
|
141
144
|
const b = getBounds();
|
|
142
|
-
ty = Math.max(
|
|
145
|
+
ty = Math.max(
|
|
146
|
+
b.minY,
|
|
147
|
+
Math.min(b.maxY, barDrag.startT - dy * barDrag.ratio)
|
|
148
|
+
);
|
|
143
149
|
} else {
|
|
144
150
|
const dx = e.clientX - barDrag.startPointer;
|
|
145
151
|
const b = getBounds();
|
|
146
|
-
tx = Math.max(
|
|
152
|
+
tx = Math.max(
|
|
153
|
+
b.minX,
|
|
154
|
+
Math.min(b.maxX, barDrag.startT - dx * barDrag.ratio)
|
|
155
|
+
);
|
|
147
156
|
}
|
|
148
157
|
apply();
|
|
149
158
|
}
|
|
@@ -226,8 +235,10 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
226
235
|
return { minX, maxX, minY, maxY };
|
|
227
236
|
}
|
|
228
237
|
function rubberBandAxis(v, min, max, dim) {
|
|
229
|
-
if (v < min)
|
|
230
|
-
|
|
238
|
+
if (v < min)
|
|
239
|
+
return min - (1 - 1 / ((min - v) * RUBBER_BAND_C / dim + 1)) * dim;
|
|
240
|
+
if (v > max)
|
|
241
|
+
return max + (1 - 1 / ((v - max) * RUBBER_BAND_C / dim + 1)) * dim;
|
|
231
242
|
return v;
|
|
232
243
|
}
|
|
233
244
|
function stopMomentum() {
|
|
@@ -320,7 +331,10 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
320
331
|
const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
321
332
|
const midX = (pts[0].x + pts[1].x) / 2;
|
|
322
333
|
const midY = (pts[0].y + pts[1].y) / 2;
|
|
323
|
-
const newScale = Math.max(
|
|
334
|
+
const newScale = Math.max(
|
|
335
|
+
minScale,
|
|
336
|
+
Math.min(maxScale, pinchStart.s * (dist / pinchStart.dist))
|
|
337
|
+
);
|
|
324
338
|
const rect = parent.getBoundingClientRect();
|
|
325
339
|
const focalX = (pinchStart.midX - rect.left - pinchStart.tx) / pinchStart.s;
|
|
326
340
|
const focalY = (pinchStart.midY - rect.top - pinchStart.ty) / pinchStart.s;
|
|
@@ -386,7 +400,10 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
386
400
|
const mx = e.clientX - rect.left;
|
|
387
401
|
const my = e.clientY - rect.top;
|
|
388
402
|
const delta = e.deltaY < 0 ? 1 : -1;
|
|
389
|
-
const newScale = Math.max(
|
|
403
|
+
const newScale = Math.max(
|
|
404
|
+
minScale,
|
|
405
|
+
Math.min(maxScale, scale * Math.exp(delta * 0.1))
|
|
406
|
+
);
|
|
390
407
|
const focalX = (mx - tx) / scale;
|
|
391
408
|
const focalY = (my - ty) / scale;
|
|
392
409
|
tx = mx - newScale * focalX;
|
|
@@ -398,7 +415,8 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
398
415
|
}
|
|
399
416
|
function setTransform(t) {
|
|
400
417
|
stopMomentum();
|
|
401
|
-
if (t.scale !== void 0)
|
|
418
|
+
if (t.scale !== void 0)
|
|
419
|
+
scale = Math.max(minScale, Math.min(maxScale, t.scale));
|
|
402
420
|
if (t.tx !== void 0) tx = t.tx;
|
|
403
421
|
if (t.ty !== void 0) ty = t.ty;
|
|
404
422
|
const b = getBounds();
|
|
@@ -424,9 +442,15 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
424
442
|
function scrollBy(dx, dy) {
|
|
425
443
|
stopMomentum();
|
|
426
444
|
const b = getBounds();
|
|
445
|
+
const before = { scale, tx, ty };
|
|
427
446
|
tx = Math.max(b.minX, Math.min(b.maxX, tx - dx));
|
|
428
447
|
ty = Math.max(b.minY, Math.min(b.maxY, ty - dy));
|
|
429
448
|
apply();
|
|
449
|
+
return getUnconsumedScrollDelta({ x: dx, y: dy }, before, {
|
|
450
|
+
scale,
|
|
451
|
+
tx,
|
|
452
|
+
ty
|
|
453
|
+
});
|
|
430
454
|
}
|
|
431
455
|
parent.addEventListener("pointerdown", onDown);
|
|
432
456
|
parent.addEventListener("pointermove", onMove);
|
|
@@ -504,7 +528,7 @@ export function useInertiaPanzoom(options = {}) {
|
|
|
504
528
|
getScale: () => controller?.getScale() ?? 1,
|
|
505
529
|
getTransform: () => controller?.getTransform() ?? { scale: 1, tx: 0, ty: 0 },
|
|
506
530
|
scrollToElement: (elem, opts) => controller?.scrollToElement(elem, opts),
|
|
507
|
-
scrollBy: (dx, dy) => controller?.scrollBy(dx, dy),
|
|
531
|
+
scrollBy: (dx, dy) => controller?.scrollBy(dx, dy) ?? { x: dx, y: dy },
|
|
508
532
|
setGesturesEnabled: (enabled) => controller?.setGesturesEnabled(enabled),
|
|
509
533
|
setTransform: (t) => controller?.setTransform(t),
|
|
510
534
|
setScaleLimits: (min, max) => {
|
|
@@ -22,7 +22,7 @@ type EventHandler<E extends EventName> = (payload: EventPayloads[E]) => void;
|
|
|
22
22
|
* viewer's exposed methods, and surfaces iframe-emitted events
|
|
23
23
|
* (`ready`, `formEditMode-changed`, `viewedPages-changed`, `all-pages-read`,
|
|
24
24
|
* `field-placed`, `field-updated`, `field-removed`, `signedStatus-changed`)
|
|
25
|
-
* via `on()`.
|
|
25
|
+
* and `scroll-boundary`) via `on()`.
|
|
26
26
|
*/
|
|
27
27
|
export declare class KViewerEmbedClient {
|
|
28
28
|
private readonly iframe;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Ref } from 'vue';
|
|
2
2
|
import type { ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
3
3
|
import type { AddFormFieldPayload, FormFieldDefinition, FormFieldValue, IAnnotationStore, PlacedFieldDefaults, SignatureFieldStatus } from '../annotation/engine/types.js';
|
|
4
|
-
import { type MethodName } from './protocol.js';
|
|
4
|
+
import { type MethodName, type ScrollBoundaryPayload } from './protocol.js';
|
|
5
5
|
/**
|
|
6
6
|
* Surface the bridge calls on the KViewer template ref. Mirrors the
|
|
7
7
|
* methods defined in `defineExpose` inside Viewer.vue. Kept as a
|
|
@@ -35,6 +35,7 @@ export interface KViewerApi {
|
|
|
35
35
|
allSigned: boolean;
|
|
36
36
|
fields: SignatureFieldStatus[];
|
|
37
37
|
}) => void) => () => void;
|
|
38
|
+
onScrollBoundary: (cb: (payload: ScrollBoundaryPayload) => void) => () => void;
|
|
38
39
|
formEditMode: boolean | Ref<boolean>;
|
|
39
40
|
setFormEditMode: (enabled: boolean) => void;
|
|
40
41
|
toggleFormEditMode: () => boolean;
|
|
@@ -226,6 +226,14 @@ export function useKViewerEmbedBridge(viewerRef, options) {
|
|
|
226
226
|
)
|
|
227
227
|
);
|
|
228
228
|
}
|
|
229
|
+
if (typeof viewer.onScrollBoundary === "function") {
|
|
230
|
+
events.push("scroll-boundary");
|
|
231
|
+
stopViewerStateWatches.push(
|
|
232
|
+
viewer.onScrollBoundary(
|
|
233
|
+
(payload) => postEvent("scroll-boundary", payload)
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
}
|
|
229
237
|
if (typeof viewer.allPagesRead === "function") {
|
|
230
238
|
events.push("all-pages-read");
|
|
231
239
|
stopViewerStateWatches.push(
|
|
@@ -11,6 +11,10 @@ export declare const EVENT_KIND: "kviewer:event";
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const UNSUPPORTED_METHOD_ERROR_NAME = "KViewerUnsupportedMethodError";
|
|
13
13
|
export type ImportAnnotationsMode = 'replace' | 'merge';
|
|
14
|
+
export interface ScrollBoundaryPayload {
|
|
15
|
+
edge: 'start' | 'end';
|
|
16
|
+
deltaY: number;
|
|
17
|
+
}
|
|
14
18
|
export interface MethodSignatures {
|
|
15
19
|
getAnnotations: {
|
|
16
20
|
args: [];
|
|
@@ -145,6 +149,9 @@ export interface EventPayloads {
|
|
|
145
149
|
/** Fires once when the final unseen page is viewed. Re-arms after
|
|
146
150
|
* `resetViewedPages()` or a new document. */
|
|
147
151
|
'all-pages-read': Record<string, never>;
|
|
152
|
+
/** Vertical wheel movement that the embedded viewer could not consume
|
|
153
|
+
* because it reached the start or end of the document. */
|
|
154
|
+
'scroll-boundary': ScrollBoundaryPayload;
|
|
148
155
|
/** The user placed a form field via a placement tool. Patch it with
|
|
149
156
|
* `updateFormField()` to set host-driven properties. */
|
|
150
157
|
'field-placed': {
|
|
@@ -7,7 +7,7 @@ export type { KViewerApi, KViewerHostApi, KViewerEmbedBridgeOptions, } from './e
|
|
|
7
7
|
export { useKViewerEmbedBridge } from './embed/bridge-host.js';
|
|
8
8
|
export type { KViewerEmbedClientOptions } from './embed/bridge-client.js';
|
|
9
9
|
export { KViewerEmbedClient } from './embed/bridge-client.js';
|
|
10
|
-
export type { EventName as KViewerEmbedEventName, EventPayloads as KViewerEmbedEventPayloads, MethodName as KViewerEmbedMethodName, } from './embed/protocol.js';
|
|
10
|
+
export type { EventName as KViewerEmbedEventName, EventPayloads as KViewerEmbedEventPayloads, MethodName as KViewerEmbedMethodName, ScrollBoundaryPayload, } from './embed/protocol.js';
|
|
11
11
|
export { KVIEWER_EMBED_PROTOCOL_VERSION, UNSUPPORTED_METHOD_ERROR_NAME as KVIEWER_EMBED_UNSUPPORTED_METHOD_ERROR_NAME, } from './embed/protocol.js';
|
|
12
12
|
export type { ViewerMenuItem, ViewerMenuButtonItem, ViewerMenuCheckboxItem, ViewerMenuSeparatorItem, } from './menu-items.js';
|
|
13
13
|
export type { ViewerSelectionItem, ViewerSelectionButtonItem, ViewerSelectionSelectItem, ViewerSelectionContext, } from './selection-items.js';
|