@netless/app-slide 0.2.106-beta.1 → 0.2.106-beta.2
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-zh.md +11 -0
- package/README.md +11 -0
- package/dist/index.d.ts +9 -2
- package/dist/main.cjs.js +165 -165
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +148 -89
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +158 -158
- package/dist/main.iife.js.map +1 -1
- package/package.json +3 -3
- package/src/SlideDocsViewer/index.ts +44 -18
- package/src/SlideDocsViewer/navigation.ts +33 -0
- package/src/index.ts +23 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/app-slide",
|
|
3
|
-
"version": "0.2.106-beta.
|
|
3
|
+
"version": "0.2.106-beta.2",
|
|
4
4
|
"main": "dist/main.cjs.js",
|
|
5
5
|
"module": "dist/main.es.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"build": "vite build && npm run types",
|
|
10
10
|
"build:dev": "vite build --mode development && npm run types",
|
|
11
11
|
"cleanup": "rimraf ./dist",
|
|
12
|
-
"test": "rimraf test/.cache && esbuild test/ResizableContainer.test.ts --bundle --platform=node --format=cjs --
|
|
12
|
+
"test": "rimraf test/.cache && esbuild test/ResizableContainer.test.ts test/NavigationButtonMode.test.ts test/DocsViewerNavigation.integration.test.ts --bundle --platform=node --format=cjs --outdir=test/.cache && node test/.cache/ResizableContainer.test.js && node test/.cache/NavigationButtonMode.test.js && node test/.cache/DocsViewerNavigation.integration.test.js"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"src",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@juggle/resize-observer": "^3.3.1",
|
|
21
|
-
"@netless/slide": "1.4.61
|
|
21
|
+
"@netless/slide": "1.4.61",
|
|
22
22
|
"@types/color-string": "^1.5.2",
|
|
23
23
|
"color-string": "^1.9.1",
|
|
24
24
|
"jspdf": "2.5.1",
|
|
@@ -12,10 +12,11 @@ import { SideEffectManager } from "side-effect-manager";
|
|
|
12
12
|
import { createDocsViewerPages } from "../SlideController";
|
|
13
13
|
import { DocsViewer, type DocsViewerPage } from "../DocsViewer";
|
|
14
14
|
import { ResizableContainer } from "../DocsViewer/ResizableContainer";
|
|
15
|
+
import { navigateWithNavigationButton, resolveNavigationButtonMode } from "./navigation";
|
|
15
16
|
import { logger } from "../utils/logger";
|
|
16
17
|
import { isEditable } from "../utils/helpers";
|
|
17
18
|
import type { Attributes, MagixEvents } from "../typings";
|
|
18
|
-
import type { AppOptions } from "..";
|
|
19
|
+
import type { AppOptions, NavigationButtonMode } from "..";
|
|
19
20
|
import type { SyncEventQueuePolicy } from "@netless/slide";
|
|
20
21
|
import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
|
|
21
22
|
|
|
@@ -55,6 +56,7 @@ export interface SlideDocsViewerConfig {
|
|
|
55
56
|
appId: string;
|
|
56
57
|
urlInterrupter?: (url: string) => Promise<string>;
|
|
57
58
|
enableScale?: boolean;
|
|
59
|
+
navigationButtonMode?: NavigationButtonMode;
|
|
58
60
|
onPagesReady?: (pages: DocsViewerPage[]) => void;
|
|
59
61
|
onNavigate?: (index: number, origin?: string) => void;
|
|
60
62
|
}
|
|
@@ -79,6 +81,7 @@ export class SlideDocsViewer {
|
|
|
79
81
|
protected isViewMounted = false;
|
|
80
82
|
protected justSildeReadonly = false;
|
|
81
83
|
protected syncEventQueuePolicy: SyncEventQueuePolicy = "fifo";
|
|
84
|
+
private readonly navigationButtonMode: NavigationButtonMode;
|
|
82
85
|
private enableScale: boolean;
|
|
83
86
|
private boxSizeEventCount = 0;
|
|
84
87
|
private latestBoxSize: BoxSize | undefined;
|
|
@@ -98,6 +101,7 @@ export class SlideDocsViewer {
|
|
|
98
101
|
appId,
|
|
99
102
|
urlInterrupter,
|
|
100
103
|
enableScale,
|
|
104
|
+
navigationButtonMode,
|
|
101
105
|
onPagesReady,
|
|
102
106
|
onNavigate,
|
|
103
107
|
}: SlideDocsViewerConfig) {
|
|
@@ -109,6 +113,7 @@ export class SlideDocsViewer {
|
|
|
109
113
|
this.onNavigate = onNavigate || noop;
|
|
110
114
|
this.baseScenePath = baseScenePath;
|
|
111
115
|
this.enableScale = enableScale ?? false;
|
|
116
|
+
this.navigationButtonMode = resolveNavigationButtonMode(navigationButtonMode);
|
|
112
117
|
this.appId = appId;
|
|
113
118
|
this.viewer = new DocsViewer({
|
|
114
119
|
readonly: box.readonly,
|
|
@@ -298,6 +303,7 @@ export class SlideDocsViewer {
|
|
|
298
303
|
}
|
|
299
304
|
|
|
300
305
|
public mount() {
|
|
306
|
+
this.unmountPromise = undefined;
|
|
301
307
|
this.box.mountContent(this.viewer.$content);
|
|
302
308
|
this.box.mountFooter(this.viewer.$footer);
|
|
303
309
|
|
|
@@ -465,29 +471,42 @@ export class SlideDocsViewer {
|
|
|
465
471
|
return (page > 0 ? page : 1) - 1;
|
|
466
472
|
}
|
|
467
473
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
this.
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
474
|
+
protected unmountPromise?: Promise<this>;
|
|
475
|
+
protected destroyPromise?: Promise<void>;
|
|
476
|
+
|
|
477
|
+
public unmount(): Promise<this> {
|
|
478
|
+
if (!this.unmountPromise) {
|
|
479
|
+
this.unmountPromise = Promise.resolve().then(async () => {
|
|
480
|
+
this.offBoxSizeChange?.();
|
|
481
|
+
this.offBoxSizeChange = undefined;
|
|
482
|
+
this.contentResizeObserver?.disconnect();
|
|
483
|
+
this.contentResizeObserver = undefined;
|
|
484
|
+
if (this.slideController) {
|
|
485
|
+
const controller = this.slideController;
|
|
486
|
+
this.slideController = null;
|
|
487
|
+
await controller.destroy();
|
|
488
|
+
}
|
|
489
|
+
this.viewer.unmount();
|
|
490
|
+
this.resizableContainer.destroy(this.box);
|
|
491
|
+
return this;
|
|
492
|
+
});
|
|
477
493
|
}
|
|
478
|
-
this.
|
|
479
|
-
this.resizableContainer.destroy(this.box);
|
|
480
|
-
return this;
|
|
494
|
+
return this.unmountPromise;
|
|
481
495
|
}
|
|
482
496
|
|
|
483
497
|
public setReadonly(readonly: boolean) {
|
|
484
498
|
this.viewer.setReadonly(readonly);
|
|
485
499
|
}
|
|
486
500
|
|
|
487
|
-
public
|
|
488
|
-
this.
|
|
489
|
-
|
|
490
|
-
|
|
501
|
+
public destroy(): Promise<void> {
|
|
502
|
+
if (!this.destroyPromise) {
|
|
503
|
+
this.destroyPromise = Promise.resolve().then(async () => {
|
|
504
|
+
this.sideEffect.flushAll();
|
|
505
|
+
await this.unmount();
|
|
506
|
+
this.viewer.destroy();
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
return this.destroyPromise;
|
|
491
510
|
}
|
|
492
511
|
|
|
493
512
|
public toggleClickThrough(tool?: string) {
|
|
@@ -541,7 +560,14 @@ export class SlideDocsViewer {
|
|
|
541
560
|
|
|
542
561
|
protected onNewPageIndex = (index: number, origin?: string) => {
|
|
543
562
|
if (this.slideController) {
|
|
544
|
-
|
|
563
|
+
navigateWithNavigationButton(
|
|
564
|
+
this.navigationButtonMode,
|
|
565
|
+
origin,
|
|
566
|
+
this.viewer.pageIndex,
|
|
567
|
+
index,
|
|
568
|
+
this.slideController,
|
|
569
|
+
this.onNavigate
|
|
570
|
+
);
|
|
545
571
|
}
|
|
546
572
|
};
|
|
547
573
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { NavigationButtonMode } from "..";
|
|
2
|
+
|
|
3
|
+
export type NavigationButtonAction = "prevStep" | "nextStep";
|
|
4
|
+
|
|
5
|
+
export function resolveNavigationButtonMode(
|
|
6
|
+
mode: NavigationButtonMode | undefined
|
|
7
|
+
): NavigationButtonMode {
|
|
8
|
+
return mode ?? "page";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface NavigationController {
|
|
12
|
+
readonly page: number;
|
|
13
|
+
readonly slide: Record<NavigationButtonAction, () => void>;
|
|
14
|
+
jumpToPage(page: number, origin?: string): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function navigateWithNavigationButton(
|
|
18
|
+
mode: NavigationButtonMode,
|
|
19
|
+
origin: string | undefined,
|
|
20
|
+
currentPageIndex: number,
|
|
21
|
+
targetPageIndex: number,
|
|
22
|
+
controller: NavigationController,
|
|
23
|
+
onNavigate: (page: number, origin?: string) => void
|
|
24
|
+
): void {
|
|
25
|
+
if (mode === "step" && origin === "navigation") {
|
|
26
|
+
const action: NavigationButtonAction =
|
|
27
|
+
targetPageIndex < currentPageIndex ? "prevStep" : "nextStep";
|
|
28
|
+
controller.slide[action]();
|
|
29
|
+
onNavigate(controller.page, origin);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
controller.jumpToPage(targetPageIndex + 1, origin);
|
|
33
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,8 @@ export { DefaultUrl, apps, FreezerLength, addHooks, useFreezer, log, logger };
|
|
|
43
43
|
|
|
44
44
|
export { setFreezerLength, getFreezerLength, onCreated, onDestroyed } from "./utils/freezer";
|
|
45
45
|
|
|
46
|
+
export type NavigationButtonMode = "page" | "step";
|
|
47
|
+
|
|
46
48
|
export interface AppOptions
|
|
47
49
|
extends Pick<
|
|
48
50
|
ISlideConfig,
|
|
@@ -86,6 +88,8 @@ export interface AppOptions
|
|
|
86
88
|
invisibleBehavior?: "frozen" | "pause";
|
|
87
89
|
/** just readonly, no operate silder */
|
|
88
90
|
justSildeReadonly?: true;
|
|
91
|
+
/** footer navigation button behavior @default 'page' */
|
|
92
|
+
navigationButtonMode?: NavigationButtonMode;
|
|
89
93
|
enableScale?: boolean;
|
|
90
94
|
resourceMaxRetries?: number;
|
|
91
95
|
onResourceMaxRetries: (url: string, error: Error) => void;
|
|
@@ -235,6 +239,7 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
235
239
|
appId: context.appId,
|
|
236
240
|
urlInterrupter: context.getAppOptions()?.urlInterrupter,
|
|
237
241
|
enableScale: context.getAppOptions()?.enableScale ?? false,
|
|
242
|
+
navigationButtonMode: context.getAppOptions()?.navigationButtonMode,
|
|
238
243
|
onPagesReady: ({ length }) => {
|
|
239
244
|
const index = docsViewer?.viewer.pageIndex || 0;
|
|
240
245
|
context.dispatchAppEvent("pageStateChange", { index, length });
|
|
@@ -313,20 +318,25 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> & {
|
|
|
313
318
|
|
|
314
319
|
let disposed = false;
|
|
315
320
|
let offDestroy: (() => void) | undefined;
|
|
316
|
-
|
|
317
|
-
|
|
321
|
+
let teardownPromise: Promise<void> | undefined;
|
|
322
|
+
const teardown = (): Promise<void> => {
|
|
323
|
+
if (teardownPromise) return teardownPromise;
|
|
318
324
|
disposed = true;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
docsViewer
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
// Store the promise before cleanup can synchronously trigger another teardown.
|
|
326
|
+
teardownPromise = Promise.resolve().then(async () => {
|
|
327
|
+
const removeDestroy = offDestroy;
|
|
328
|
+
offDestroy = undefined;
|
|
329
|
+
removeDestroy?.();
|
|
330
|
+
log("[Slide] destroy", context.appId);
|
|
331
|
+
if (useFreezer) apps.delete(context.appId);
|
|
332
|
+
sideEffect.flushAll();
|
|
333
|
+
if (docsViewer) {
|
|
334
|
+
const viewer = docsViewer;
|
|
335
|
+
docsViewer = null;
|
|
336
|
+
await viewer.destroy();
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
return teardownPromise;
|
|
330
340
|
};
|
|
331
341
|
offDestroy = context.emitter.on("destroy", teardown);
|
|
332
342
|
|