kviewer 0.2.0 → 0.2.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/README.md +28 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/Viewer.d.vue.ts +12 -0
- package/dist/runtime/components/Viewer.vue +35 -2
- package/dist/runtime/components/Viewer.vue.d.ts +12 -0
- package/dist/runtime/composables/usePageVirtualization.d.ts +11 -0
- package/dist/runtime/composables/usePageVirtualization.js +40 -0
- package/dist/runtime/embed/bridge-client.d.ts +9 -1
- package/dist/runtime/embed/bridge-client.js +13 -0
- package/dist/runtime/embed/bridge-host.d.ts +3 -0
- package/dist/runtime/embed/bridge-host.js +30 -8
- package/dist/runtime/embed/protocol.d.ts +21 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ A Nuxt module for viewing, annotating, and exporting PDFs. Built on pdfjs-dist,
|
|
|
20
20
|
- ↩️ Undo/redo history
|
|
21
21
|
- 🗂️ Multi-tab document support (`KViewerTabs`)
|
|
22
22
|
- 🎨 Customizable header and footer slots
|
|
23
|
+
- 👁️ Read tracking — `allPagesRead()` + `all-pages-read` event to gate "must read all pages" signing
|
|
23
24
|
|
|
24
25
|
## Quick Setup
|
|
25
26
|
|
|
@@ -112,6 +113,9 @@ const isEditing = viewerRef.value?.formEditMode.value
|
|
|
112
113
|
| `formEditMode` | `Ref<boolean>` |
|
|
113
114
|
| `setFormEditMode(enabled)` | `void` |
|
|
114
115
|
| `toggleFormEditMode()` | `boolean` (the new state) |
|
|
116
|
+
| `allPagesRead()` | `boolean` |
|
|
117
|
+
| `getViewedPages()` | `number[]` |
|
|
118
|
+
| `resetViewedPages()` | `void` |
|
|
115
119
|
|
|
116
120
|
`exportPdf` options default to `{ flatten: false, download: false, preserveOriginalAnnotations: false }`.
|
|
117
121
|
|
|
@@ -138,6 +142,30 @@ Or imperatively from the ref API (`setFormEditMode`, `toggleFormEditMode`, react
|
|
|
138
142
|
|
|
139
143
|
When native PDF annotations are auto-imported into Konva, exporting with `preserveOriginalAnnotations: true` may duplicate annotations. Prefer `preserveOriginalAnnotations: false` in that workflow.
|
|
140
144
|
|
|
145
|
+
### Read tracking
|
|
146
|
+
|
|
147
|
+
For "must read all pages before signing" flows, `KViewer` tracks which pages have entered the viewport. Gate your sign action on the `all-pages-read` event (push) or the `allPagesRead()` ref method (pull):
|
|
148
|
+
|
|
149
|
+
```vue
|
|
150
|
+
<template>
|
|
151
|
+
<KViewer
|
|
152
|
+
:source="pdfUrl"
|
|
153
|
+
@all-pages-read="canSign = true"
|
|
154
|
+
@update:viewed-pages="(pages) => (readCount = pages.length)"
|
|
155
|
+
/>
|
|
156
|
+
<button :disabled="!canSign">Sign</button>
|
|
157
|
+
</template>
|
|
158
|
+
|
|
159
|
+
<script setup lang="ts">
|
|
160
|
+
const canSign = ref(false)
|
|
161
|
+
const readCount = ref(0)
|
|
162
|
+
</script>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A page counts as read once its top edge scrolls into view (computed from geometry, so a fast scroll to the bottom still marks every page it passed). The viewer deliberately does not enforce that the user dwelled on each page — skipping is the signer's choice. The host app owns the policy (e.g. an extra "I have read all pages" checkbox); the viewer only reports the status. Call `resetViewedPages()` to restart tracking; loading a new `source` resets it automatically.
|
|
166
|
+
|
|
167
|
+
The same surface is available over the [embed bridge](src/runtime/embed): `client.allPagesRead()`, `client.getViewedPages()`, `client.resetViewedPages()`, and the `all-pages-read` / `viewedPages-changed` events via `client.on(...)`.
|
|
168
|
+
|
|
141
169
|
## Development
|
|
142
170
|
|
|
143
171
|
<details>
|
package/dist/module.json
CHANGED
|
@@ -84,12 +84,24 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
84
84
|
setFormEditMode: (enabled: boolean) => void;
|
|
85
85
|
/** Flip form-edit mode. Returns the new state. */
|
|
86
86
|
toggleFormEditMode: () => boolean;
|
|
87
|
+
/** True once the user has viewed every page — each page has entered the
|
|
88
|
+
* viewport at least once. Use to gate a "must read all pages" sign action.
|
|
89
|
+
* See also the `all-pages-read` event for a push-style signal. */
|
|
90
|
+
allPagesRead: () => boolean;
|
|
91
|
+
/** Page numbers viewed so far, ascending. */
|
|
92
|
+
getViewedPages: () => number[];
|
|
93
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
94
|
+
resetViewedPages: () => void;
|
|
87
95
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
96
|
+
"all-pages-read": () => any;
|
|
88
97
|
"update:formEditMode": (value: boolean) => any;
|
|
89
98
|
"update:activeRoleId": (value: string | null) => any;
|
|
99
|
+
"update:viewedPages": (pages: number[]) => any;
|
|
90
100
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
101
|
+
"onAll-pages-read"?: (() => any) | undefined;
|
|
91
102
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
92
103
|
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
104
|
+
"onUpdate:viewedPages"?: ((pages: number[]) => any) | undefined;
|
|
93
105
|
}>, {
|
|
94
106
|
userName: string;
|
|
95
107
|
freehandGroupingDelay: number;
|
|
@@ -229,7 +229,7 @@ const props = defineProps({
|
|
|
229
229
|
scripting: { type: Boolean, required: false, default: false },
|
|
230
230
|
menuItems: { type: Array, required: false, default: void 0 }
|
|
231
231
|
});
|
|
232
|
-
const emit = defineEmits(["update:formEditMode", "update:activeRoleId"]);
|
|
232
|
+
const emit = defineEmits(["update:formEditMode", "update:activeRoleId", "update:viewedPages", "all-pages-read"]);
|
|
233
233
|
const viewerRoot = ref(null);
|
|
234
234
|
const scrollContainer = ref(null);
|
|
235
235
|
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn } = provideViewerState();
|
|
@@ -306,6 +306,31 @@ watch(
|
|
|
306
306
|
viewerState.currentPage.value = page;
|
|
307
307
|
}
|
|
308
308
|
);
|
|
309
|
+
watch(
|
|
310
|
+
() => viewerState.currentPage.value,
|
|
311
|
+
(page) => virtualization.markPageViewed(page),
|
|
312
|
+
{ immediate: true }
|
|
313
|
+
);
|
|
314
|
+
watch(
|
|
315
|
+
() => pageMetas.value.length,
|
|
316
|
+
(len) => {
|
|
317
|
+
if (len > 0 && !isSinglePageMode.value) {
|
|
318
|
+
nextTick(() => virtualization.updateCurrentPageByRect());
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
watch(
|
|
323
|
+
virtualization.allPagesRead,
|
|
324
|
+
(read, prev) => {
|
|
325
|
+
if (read && !prev) emit("all-pages-read");
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
watch(
|
|
329
|
+
virtualization.viewedPages,
|
|
330
|
+
(pages) => {
|
|
331
|
+
emit("update:viewedPages", [...pages].sort((a, b) => a - b));
|
|
332
|
+
}
|
|
333
|
+
);
|
|
309
334
|
function onPageRef(pageNumber, el) {
|
|
310
335
|
if (el) {
|
|
311
336
|
virtualization.observePage(pageNumber, el);
|
|
@@ -892,7 +917,15 @@ defineExpose({
|
|
|
892
917
|
const next = !viewerState.formEditMode.value;
|
|
893
918
|
viewerState.setFormEditMode(next);
|
|
894
919
|
return next;
|
|
895
|
-
}
|
|
920
|
+
},
|
|
921
|
+
/** True once the user has viewed every page — each page has entered the
|
|
922
|
+
* viewport at least once. Use to gate a "must read all pages" sign action.
|
|
923
|
+
* See also the `all-pages-read` event for a push-style signal. */
|
|
924
|
+
allPagesRead: () => virtualization.allPagesRead.value,
|
|
925
|
+
/** Page numbers viewed so far, ascending. */
|
|
926
|
+
getViewedPages: () => [...virtualization.viewedPages.value].sort((a, b) => a - b),
|
|
927
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
928
|
+
resetViewedPages: () => virtualization.resetViewedPages()
|
|
896
929
|
});
|
|
897
930
|
</script>
|
|
898
931
|
|
|
@@ -84,12 +84,24 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
84
84
|
setFormEditMode: (enabled: boolean) => void;
|
|
85
85
|
/** Flip form-edit mode. Returns the new state. */
|
|
86
86
|
toggleFormEditMode: () => boolean;
|
|
87
|
+
/** True once the user has viewed every page — each page has entered the
|
|
88
|
+
* viewport at least once. Use to gate a "must read all pages" sign action.
|
|
89
|
+
* See also the `all-pages-read` event for a push-style signal. */
|
|
90
|
+
allPagesRead: () => boolean;
|
|
91
|
+
/** Page numbers viewed so far, ascending. */
|
|
92
|
+
getViewedPages: () => number[];
|
|
93
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
94
|
+
resetViewedPages: () => void;
|
|
87
95
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
96
|
+
"all-pages-read": () => any;
|
|
88
97
|
"update:formEditMode": (value: boolean) => any;
|
|
89
98
|
"update:activeRoleId": (value: string | null) => any;
|
|
99
|
+
"update:viewedPages": (pages: number[]) => any;
|
|
90
100
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
101
|
+
"onAll-pages-read"?: (() => any) | undefined;
|
|
91
102
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
92
103
|
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
104
|
+
"onUpdate:viewedPages"?: ((pages: number[]) => any) | undefined;
|
|
93
105
|
}>, {
|
|
94
106
|
userName: string;
|
|
95
107
|
freehandGroupingDelay: number;
|
|
@@ -21,6 +21,17 @@ export interface PageVirtualization {
|
|
|
21
21
|
pageMetas: ShallowRef<PageMeta[]>;
|
|
22
22
|
currentPage: Ref<number>;
|
|
23
23
|
renderedPages: ComputedRef<Set<number>>;
|
|
24
|
+
/** Pages that have entered the viewport at least once since the document
|
|
25
|
+
* loaded (or since the last `resetViewedPages()`). Marked from scroll/pan
|
|
26
|
+
* geometry — not render state — so a fast fling that skips rendering the
|
|
27
|
+
* middle pages still counts them as viewed. */
|
|
28
|
+
viewedPages: ShallowRef<Set<number>>;
|
|
29
|
+
/** True once every page has been viewed. */
|
|
30
|
+
allPagesRead: ComputedRef<boolean>;
|
|
31
|
+
/** Mark a single page as viewed (used by single-page navigation). */
|
|
32
|
+
markPageViewed: (pageNumber: number) => void;
|
|
33
|
+
/** Clear the viewed-pages set, e.g. to start a fresh signing session. */
|
|
34
|
+
resetViewedPages: () => void;
|
|
24
35
|
isPageRendered: (pageNumber: number) => boolean;
|
|
25
36
|
observePage: (pageNumber: number, element: HTMLElement) => void;
|
|
26
37
|
unobservePage: (pageNumber: number) => void;
|
|
@@ -5,6 +5,30 @@ export function createPageVirtualization() {
|
|
|
5
5
|
const pageMetas = shallowRef([]);
|
|
6
6
|
const currentPage = ref(1);
|
|
7
7
|
const visiblePages = shallowRef(/* @__PURE__ */ new Set());
|
|
8
|
+
const viewedPages = shallowRef(/* @__PURE__ */ new Set());
|
|
9
|
+
const allPagesRead = computed(() => {
|
|
10
|
+
const total = pageMetas.value.length;
|
|
11
|
+
return total > 0 && viewedPages.value.size >= total;
|
|
12
|
+
});
|
|
13
|
+
function markPageViewed(pageNumber) {
|
|
14
|
+
if (viewedPages.value.has(pageNumber)) return;
|
|
15
|
+
const next = new Set(viewedPages.value);
|
|
16
|
+
next.add(pageNumber);
|
|
17
|
+
viewedPages.value = next;
|
|
18
|
+
}
|
|
19
|
+
function markPagesViewed(pageNumbers) {
|
|
20
|
+
let next = null;
|
|
21
|
+
for (const n of pageNumbers) {
|
|
22
|
+
if (viewedPages.value.has(n) || next?.has(n)) continue;
|
|
23
|
+
if (!next) next = new Set(viewedPages.value);
|
|
24
|
+
next.add(n);
|
|
25
|
+
}
|
|
26
|
+
if (next) viewedPages.value = next;
|
|
27
|
+
}
|
|
28
|
+
function resetViewedPages() {
|
|
29
|
+
if (viewedPages.value.size === 0) return;
|
|
30
|
+
viewedPages.value = /* @__PURE__ */ new Set();
|
|
31
|
+
}
|
|
8
32
|
let observer = null;
|
|
9
33
|
let scrollRoot = null;
|
|
10
34
|
let scrollListener = null;
|
|
@@ -88,12 +112,19 @@ export function createPageVirtualization() {
|
|
|
88
112
|
if (bestPage !== currentPage.value) {
|
|
89
113
|
currentPage.value = bestPage;
|
|
90
114
|
}
|
|
115
|
+
const viewportBottom = rootTop + scrollRoot.clientHeight;
|
|
116
|
+
const reached = [];
|
|
117
|
+
for (const [pageNum, el] of pageToElement) {
|
|
118
|
+
if (el.offsetTop < viewportBottom) reached.push(pageNum);
|
|
119
|
+
}
|
|
120
|
+
markPagesViewed(reached);
|
|
91
121
|
}
|
|
92
122
|
function scrollToPage(pageNumber) {
|
|
93
123
|
const element = pageToElement.get(pageNumber);
|
|
94
124
|
if (element && scrollRoot) {
|
|
95
125
|
element.scrollIntoView({ block: "start", behavior: "instant" });
|
|
96
126
|
currentPage.value = pageNumber;
|
|
127
|
+
markPageViewed(pageNumber);
|
|
97
128
|
}
|
|
98
129
|
}
|
|
99
130
|
function getPageElement(pageNumber) {
|
|
@@ -103,8 +134,10 @@ export function createPageVirtualization() {
|
|
|
103
134
|
if (!scrollRoot) return;
|
|
104
135
|
const rootRect = scrollRoot.getBoundingClientRect();
|
|
105
136
|
const rootMid = rootRect.top + rootRect.height / 2;
|
|
137
|
+
const rootBottom = rootRect.top + rootRect.height;
|
|
106
138
|
let bestPage = currentPage.value;
|
|
107
139
|
let bestDist = Infinity;
|
|
140
|
+
const reached = [];
|
|
108
141
|
for (const [pageNum, el] of pageToElement) {
|
|
109
142
|
const rect = el.getBoundingClientRect();
|
|
110
143
|
const dist = rootMid < rect.top ? rect.top - rootMid : rootMid > rect.bottom ? rootMid - rect.bottom : 0;
|
|
@@ -112,7 +145,9 @@ export function createPageVirtualization() {
|
|
|
112
145
|
bestDist = dist;
|
|
113
146
|
bestPage = pageNum;
|
|
114
147
|
}
|
|
148
|
+
if (rect.top < rootBottom) reached.push(pageNum);
|
|
115
149
|
}
|
|
150
|
+
markPagesViewed(reached);
|
|
116
151
|
if (bestPage !== currentPage.value) {
|
|
117
152
|
currentPage.value = bestPage;
|
|
118
153
|
}
|
|
@@ -221,6 +256,7 @@ export function createPageVirtualization() {
|
|
|
221
256
|
elementToPage.clear();
|
|
222
257
|
pageToElement.clear();
|
|
223
258
|
visiblePages.value = /* @__PURE__ */ new Set();
|
|
259
|
+
viewedPages.value = /* @__PURE__ */ new Set();
|
|
224
260
|
pageMetas.value = [];
|
|
225
261
|
scrollRoot = null;
|
|
226
262
|
}
|
|
@@ -228,6 +264,10 @@ export function createPageVirtualization() {
|
|
|
228
264
|
pageMetas,
|
|
229
265
|
currentPage,
|
|
230
266
|
renderedPages,
|
|
267
|
+
viewedPages,
|
|
268
|
+
allPagesRead,
|
|
269
|
+
markPageViewed,
|
|
270
|
+
resetViewedPages,
|
|
231
271
|
isPageRendered,
|
|
232
272
|
observePage,
|
|
233
273
|
unobservePage,
|
|
@@ -20,7 +20,8 @@ type EventHandler<E extends EventName> = (payload: EventPayloads[E]) => void;
|
|
|
20
20
|
* Parent-page client that drives a `<KViewer>` instance running inside
|
|
21
21
|
* an iframe. Wraps `postMessage` into a Promise-based RPC mirroring the
|
|
22
22
|
* viewer's exposed methods, and surfaces iframe-emitted events
|
|
23
|
-
* (`ready`, `formEditMode-changed`
|
|
23
|
+
* (`ready`, `formEditMode-changed`, `viewedPages-changed`, `all-pages-read`)
|
|
24
|
+
* via `on()`.
|
|
24
25
|
*/
|
|
25
26
|
export declare class KViewerEmbedClient {
|
|
26
27
|
private readonly iframe;
|
|
@@ -52,6 +53,13 @@ export declare class KViewerEmbedClient {
|
|
|
52
53
|
getFormEditMode(): Promise<boolean>;
|
|
53
54
|
setFormEditMode(enabled: boolean): Promise<void>;
|
|
54
55
|
toggleFormEditMode(): Promise<boolean>;
|
|
56
|
+
/** True once the user has viewed every page. For a push-style signal,
|
|
57
|
+
* subscribe via `on('all-pages-read', …)` / `on('viewedPages-changed', …)`. */
|
|
58
|
+
allPagesRead(): Promise<boolean>;
|
|
59
|
+
/** Page numbers viewed so far, ascending. */
|
|
60
|
+
getViewedPages(): Promise<number[]>;
|
|
61
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
62
|
+
resetViewedPages(): Promise<void>;
|
|
55
63
|
private call;
|
|
56
64
|
private handleMessage;
|
|
57
65
|
}
|
|
@@ -87,6 +87,19 @@ export class KViewerEmbedClient {
|
|
|
87
87
|
toggleFormEditMode() {
|
|
88
88
|
return this.call("toggleFormEditMode");
|
|
89
89
|
}
|
|
90
|
+
/** True once the user has viewed every page. For a push-style signal,
|
|
91
|
+
* subscribe via `on('all-pages-read', …)` / `on('viewedPages-changed', …)`. */
|
|
92
|
+
allPagesRead() {
|
|
93
|
+
return this.call("allPagesRead");
|
|
94
|
+
}
|
|
95
|
+
/** Page numbers viewed so far, ascending. */
|
|
96
|
+
getViewedPages() {
|
|
97
|
+
return this.call("getViewedPages");
|
|
98
|
+
}
|
|
99
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
100
|
+
resetViewedPages() {
|
|
101
|
+
return this.call("resetViewedPages");
|
|
102
|
+
}
|
|
90
103
|
// ── internals ──────────────────────────────────────────────────────────
|
|
91
104
|
call(method, ...args) {
|
|
92
105
|
if (this.disposed) {
|
|
@@ -26,6 +26,9 @@ export interface KViewerApi {
|
|
|
26
26
|
formEditMode: Ref<boolean>;
|
|
27
27
|
setFormEditMode: (enabled: boolean) => void;
|
|
28
28
|
toggleFormEditMode: () => boolean;
|
|
29
|
+
allPagesRead: () => boolean;
|
|
30
|
+
getViewedPages: () => number[];
|
|
31
|
+
resetViewedPages: () => void;
|
|
29
32
|
}
|
|
30
33
|
export interface KViewerEmbedBridgeOptions {
|
|
31
34
|
/**
|
|
@@ -75,6 +75,13 @@ export function useKViewerEmbedBridge(viewerRef, options) {
|
|
|
75
75
|
}
|
|
76
76
|
case "toggleFormEditMode":
|
|
77
77
|
return viewer.toggleFormEditMode();
|
|
78
|
+
case "allPagesRead":
|
|
79
|
+
return viewer.allPagesRead();
|
|
80
|
+
case "getViewedPages":
|
|
81
|
+
return viewer.getViewedPages();
|
|
82
|
+
case "resetViewedPages":
|
|
83
|
+
viewer.resetViewedPages();
|
|
84
|
+
return void 0;
|
|
78
85
|
default:
|
|
79
86
|
throw new Error(`Unknown KViewer embed method: ${req.method}`);
|
|
80
87
|
}
|
|
@@ -110,17 +117,32 @@ export function useKViewerEmbedBridge(viewerRef, options) {
|
|
|
110
117
|
target.postMessage(reply, event.origin === "" ? options.parentOrigin : event.origin);
|
|
111
118
|
}
|
|
112
119
|
win.addEventListener("message", handleMessage);
|
|
113
|
-
let
|
|
120
|
+
let stopViewerStateWatches = [];
|
|
114
121
|
const stopViewerWatch = watch(
|
|
115
122
|
viewerRef,
|
|
116
123
|
(viewer) => {
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
for (const stop of stopViewerStateWatches) stop();
|
|
125
|
+
stopViewerStateWatches = [];
|
|
119
126
|
if (!viewer) return;
|
|
120
127
|
postEvent("ready", { protocolVersion: KVIEWER_EMBED_PROTOCOL_VERSION });
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
stopViewerStateWatches.push(
|
|
129
|
+
watch(
|
|
130
|
+
viewer.formEditMode,
|
|
131
|
+
(enabled) => postEvent("formEditMode-changed", { enabled })
|
|
132
|
+
),
|
|
133
|
+
// Read-tracking. The getters touch the viewer's reactive viewed-pages
|
|
134
|
+
// state, so watch re-runs them whenever a page is viewed or tracking
|
|
135
|
+
// is reset.
|
|
136
|
+
watch(
|
|
137
|
+
() => viewer.allPagesRead(),
|
|
138
|
+
(read, prev) => {
|
|
139
|
+
if (read && !prev) postEvent("all-pages-read", {});
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
watch(
|
|
143
|
+
() => viewer.getViewedPages(),
|
|
144
|
+
(pages) => postEvent("viewedPages-changed", { pages, allRead: viewer.allPagesRead() })
|
|
145
|
+
)
|
|
124
146
|
);
|
|
125
147
|
},
|
|
126
148
|
{ immediate: true }
|
|
@@ -128,8 +150,8 @@ export function useKViewerEmbedBridge(viewerRef, options) {
|
|
|
128
150
|
function unmount() {
|
|
129
151
|
win.removeEventListener("message", handleMessage);
|
|
130
152
|
stopViewerWatch();
|
|
131
|
-
|
|
132
|
-
|
|
153
|
+
for (const stop of stopViewerStateWatches) stop();
|
|
154
|
+
stopViewerStateWatches = [];
|
|
133
155
|
}
|
|
134
156
|
onScopeDispose(unmount);
|
|
135
157
|
return { unmount };
|
|
@@ -59,6 +59,18 @@ export interface MethodSignatures {
|
|
|
59
59
|
args: [];
|
|
60
60
|
result: boolean;
|
|
61
61
|
};
|
|
62
|
+
allPagesRead: {
|
|
63
|
+
args: [];
|
|
64
|
+
result: boolean;
|
|
65
|
+
};
|
|
66
|
+
getViewedPages: {
|
|
67
|
+
args: [];
|
|
68
|
+
result: number[];
|
|
69
|
+
};
|
|
70
|
+
resetViewedPages: {
|
|
71
|
+
args: [];
|
|
72
|
+
result: undefined;
|
|
73
|
+
};
|
|
62
74
|
}
|
|
63
75
|
export type MethodName = keyof MethodSignatures;
|
|
64
76
|
export interface RequestMessage<M extends MethodName = MethodName> {
|
|
@@ -91,6 +103,15 @@ export interface EventPayloads {
|
|
|
91
103
|
'formEditMode-changed': {
|
|
92
104
|
enabled: boolean;
|
|
93
105
|
};
|
|
106
|
+
/** The set of viewed pages changed. `allRead` is true once every page has
|
|
107
|
+
* been viewed. */
|
|
108
|
+
'viewedPages-changed': {
|
|
109
|
+
pages: number[];
|
|
110
|
+
allRead: boolean;
|
|
111
|
+
};
|
|
112
|
+
/** Fires once when the final unseen page is viewed. Re-arms after
|
|
113
|
+
* `resetViewedPages()` or a new document. */
|
|
114
|
+
'all-pages-read': Record<string, never>;
|
|
94
115
|
}
|
|
95
116
|
export type EventName = keyof EventPayloads;
|
|
96
117
|
export interface EventMessage<E extends EventName = EventName> {
|