@netless/app-slide 0.3.0-canary.13 → 0.3.0-canary.14
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 +17 -7
- package/README.md +4 -7
- package/dist/Refrigerator.d.ts +3 -1
- package/dist/SlideViewer/footer.d.ts +1 -0
- package/dist/SlideViewer/index.d.ts +8 -2
- package/dist/icons/save.d.ts +1 -0
- package/dist/icons/spinner.d.ts +1 -0
- package/dist/main.iife.js +2 -2
- package/dist/main.iife.js.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/main.mjs +589 -497
- package/dist/main.mjs.map +1 -1
- package/dist/typings.d.ts +2 -2
- package/package.json +3 -2
- package/src/Refrigerator.ts +11 -1
- package/src/SlideViewer/footer.ts +42 -19
- package/src/SlideViewer/index.ts +91 -0
- package/src/SlideViewer/sidebar.ts +15 -14
- package/src/connect.ts +24 -0
- package/src/icons/save.ts +18 -0
- package/src/icons/spinner.ts +37 -0
- package/src/index.ts +1 -1
- package/src/preview.ts +2 -2
package/dist/typings.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ISlideConfig, Slide, SLIDE_EVENTS, SyncEvent } from "@netless/slide";
|
|
2
2
|
import type { RegisterParams } from "@netless/window-manager";
|
|
3
|
-
export
|
|
4
|
-
export
|
|
3
|
+
export type SlideState = Slide["slideState"];
|
|
4
|
+
export type AddHooks = NonNullable<RegisterParams["addHooks"]>;
|
|
5
5
|
export interface Attributes {
|
|
6
6
|
/** convert task id */
|
|
7
7
|
taskId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/app-slide",
|
|
3
|
-
"version": "0.3.0-canary.
|
|
3
|
+
"version": "0.3.0-canary.14",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"module": "dist/main.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"README-zh.md"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@netless/slide": "^0.
|
|
13
|
+
"@netless/slide": "^0.9.2",
|
|
14
|
+
"jspdf": "2.5.1"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
16
17
|
"@types/color-string": "^1.5.2",
|
package/src/Refrigerator.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReadonlyTeleBox } from "@netless/window-manager";
|
|
1
2
|
import type { SlideViewer } from "./SlideViewer";
|
|
2
3
|
import type { AddHooks } from "./typings";
|
|
3
4
|
|
|
@@ -5,10 +6,12 @@ export class Refrigerator {
|
|
|
5
6
|
threshold = 2;
|
|
6
7
|
|
|
7
8
|
private _map = new Map<string, SlideViewer>();
|
|
9
|
+
private _box = new Map<string, ReadonlyTeleBox>();
|
|
8
10
|
private _queue: string[] = [];
|
|
9
11
|
|
|
10
|
-
set(appId: string, viewer: SlideViewer) {
|
|
12
|
+
set(appId: string, viewer: SlideViewer, box: ReadonlyTeleBox) {
|
|
11
13
|
this._map.set(appId, viewer);
|
|
14
|
+
this._box.set(appId, box);
|
|
12
15
|
if (!this._queue.includes(appId)) {
|
|
13
16
|
this._queue.unshift(appId);
|
|
14
17
|
}
|
|
@@ -20,6 +23,7 @@ export class Refrigerator {
|
|
|
20
23
|
|
|
21
24
|
delete(appId: string) {
|
|
22
25
|
this._map.delete(appId);
|
|
26
|
+
this._box.delete(appId);
|
|
23
27
|
this._queue = this._queue.filter(id => id !== appId);
|
|
24
28
|
this._refresh();
|
|
25
29
|
}
|
|
@@ -35,6 +39,12 @@ export class Refrigerator {
|
|
|
35
39
|
|
|
36
40
|
private _refresh() {
|
|
37
41
|
let viewer: SlideViewer | undefined;
|
|
42
|
+
// queue = [5, 4, 3, 2, 1]
|
|
43
|
+
this._queue.sort((a, b) => {
|
|
44
|
+
const za = this._box.get(a)?.zIndex ?? 0;
|
|
45
|
+
const zb = this._box.get(b)?.zIndex ?? 0;
|
|
46
|
+
return -(za - zb);
|
|
47
|
+
});
|
|
38
48
|
this._queue.forEach((appId, i) => {
|
|
39
49
|
if (!(viewer = this._map.get(appId))) return;
|
|
40
50
|
if (i < this.threshold) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { class_name } from "../constants";
|
|
2
2
|
import { arrowLeftSVG, arrowRightSVG, pauseSVG, playSVG, sidebarSVG } from "../icons";
|
|
3
|
-
import {
|
|
3
|
+
import { hc, noop, wrap_class } from "../utils";
|
|
4
|
+
import { saveSVG } from "../icons/save";
|
|
5
|
+
import { spinnerSVG } from "../icons/spinner";
|
|
4
6
|
|
|
5
7
|
export function create_footer() {
|
|
6
8
|
type NewPageIndexCallback = (newPageIndex: number) => void;
|
|
@@ -24,11 +26,9 @@ export function create_footer() {
|
|
|
24
26
|
let page_index = 0;
|
|
25
27
|
let page_count = 0;
|
|
26
28
|
|
|
27
|
-
const $footer =
|
|
28
|
-
set_class($footer, "footer");
|
|
29
|
+
const $footer = hc("div", "footer");
|
|
29
30
|
|
|
30
|
-
const $btnSidebar =
|
|
31
|
-
set_class($btnSidebar, "btn-sidebar");
|
|
31
|
+
const $btnSidebar = hc("button", "btn-sidebar");
|
|
32
32
|
$btnSidebar.classList.add(wrap_class("footer-btn"));
|
|
33
33
|
$btnSidebar.appendChild(sidebarSVG(class_name));
|
|
34
34
|
$btnSidebar.onclick = function on_click_btn_sidebar() {
|
|
@@ -38,11 +38,9 @@ export function create_footer() {
|
|
|
38
38
|
$btnSidebar.style.display = "none";
|
|
39
39
|
$footer.appendChild($btnSidebar);
|
|
40
40
|
|
|
41
|
-
const $pageJumps =
|
|
42
|
-
set_class($pageJumps, "page-jumps");
|
|
41
|
+
const $pageJumps = hc("div", "page-jumps");
|
|
43
42
|
|
|
44
|
-
const $btnPageBack =
|
|
45
|
-
set_class($btnPageBack, "btn-page-back");
|
|
43
|
+
const $btnPageBack = hc("button", "btn-page-back");
|
|
46
44
|
$btnPageBack.classList.add(wrap_class("footer-btn"));
|
|
47
45
|
$btnPageBack.appendChild(arrowLeftSVG(class_name));
|
|
48
46
|
$btnPageBack.onclick = function on_click_btn_page_back() {
|
|
@@ -51,8 +49,7 @@ export function create_footer() {
|
|
|
51
49
|
};
|
|
52
50
|
$pageJumps.appendChild($btnPageBack);
|
|
53
51
|
|
|
54
|
-
const $btnPlay =
|
|
55
|
-
set_class($btnPlay, "btn-page-play");
|
|
52
|
+
const $btnPlay = hc("button", "btn-page-play");
|
|
56
53
|
$btnPlay.classList.add(wrap_class("footer-btn"));
|
|
57
54
|
$btnPlay.appendChild(playSVG(class_name));
|
|
58
55
|
$btnPlay.appendChild(pauseSVG(class_name));
|
|
@@ -68,8 +65,7 @@ export function create_footer() {
|
|
|
68
65
|
};
|
|
69
66
|
$pageJumps.appendChild($btnPlay);
|
|
70
67
|
|
|
71
|
-
const $btnPageNext =
|
|
72
|
-
set_class($btnPageNext, "btn-page-next");
|
|
68
|
+
const $btnPageNext = hc("button", "btn-page-next");
|
|
73
69
|
$btnPageNext.classList.add(wrap_class("footer-btn"));
|
|
74
70
|
$btnPageNext.appendChild(arrowRightSVG(class_name));
|
|
75
71
|
$btnPageNext.onclick = function on_click_btn_page_next() {
|
|
@@ -78,11 +74,9 @@ export function create_footer() {
|
|
|
78
74
|
};
|
|
79
75
|
$pageJumps.appendChild($btnPageNext);
|
|
80
76
|
|
|
81
|
-
const $pageNumber =
|
|
82
|
-
set_class($pageNumber, "page-number");
|
|
77
|
+
const $pageNumber = hc("div", "page-number");
|
|
83
78
|
|
|
84
|
-
const $pageNumberInput =
|
|
85
|
-
set_class($pageNumberInput, "page-number-input");
|
|
79
|
+
const $pageNumberInput = hc("input", "page-number-input");
|
|
86
80
|
$pageNumberInput.value = String(page_index + 1);
|
|
87
81
|
$pageNumberInput.onchange = function on_change_page_number_input() {
|
|
88
82
|
if (readonly) return;
|
|
@@ -91,8 +85,7 @@ export function create_footer() {
|
|
|
91
85
|
}
|
|
92
86
|
};
|
|
93
87
|
|
|
94
|
-
const $totalPage =
|
|
95
|
-
set_class($totalPage, "total-page");
|
|
88
|
+
const $totalPage = hc("span", "total-page");
|
|
96
89
|
$totalPage.textContent = " / ";
|
|
97
90
|
|
|
98
91
|
$pageNumber.appendChild($pageNumberInput);
|
|
@@ -103,6 +96,35 @@ export function create_footer() {
|
|
|
103
96
|
|
|
104
97
|
const class_readonly = wrap_class("readonly");
|
|
105
98
|
|
|
99
|
+
let saveToPdf: (progressCallback: (p: number) => void) => void = noop;
|
|
100
|
+
const on_save_to_pdf = (callback: (progressCallback: (p: number) => void) => void) => {
|
|
101
|
+
saveToPdf = callback;
|
|
102
|
+
};
|
|
103
|
+
const progressCallback = (p: number) => {
|
|
104
|
+
if (p < 99) {
|
|
105
|
+
spinnerIcon.style.display = "block";
|
|
106
|
+
saveIcon.style.display = "none";
|
|
107
|
+
const progress = spinnerIcon.querySelector("[data-id]");
|
|
108
|
+
if (progress) {
|
|
109
|
+
progress.textContent = p.toString().padStart(2, "0");
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
spinnerIcon.style.display = "none";
|
|
113
|
+
saveIcon.style.display = "block";
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const saveIcon = saveSVG(class_name);
|
|
117
|
+
const spinnerIcon = spinnerSVG(class_name);
|
|
118
|
+
spinnerIcon.style.display = "none";
|
|
119
|
+
const $saveBtn = hc("button", "footer-btn");
|
|
120
|
+
$saveBtn.appendChild(saveIcon);
|
|
121
|
+
$saveBtn.onclick = function () {
|
|
122
|
+
saveToPdf(progressCallback);
|
|
123
|
+
};
|
|
124
|
+
$saveBtn.appendChild(spinnerIcon);
|
|
125
|
+
|
|
126
|
+
$footer.appendChild($saveBtn);
|
|
127
|
+
|
|
106
128
|
function set_readonly(readonly_: boolean) {
|
|
107
129
|
if (readonly !== readonly_) {
|
|
108
130
|
readonly = readonly_;
|
|
@@ -142,5 +164,6 @@ export function create_footer() {
|
|
|
142
164
|
on_new_page_index,
|
|
143
165
|
on_toggle_preview,
|
|
144
166
|
on_play,
|
|
167
|
+
on_save_to_pdf,
|
|
145
168
|
};
|
|
146
169
|
}
|
package/src/SlideViewer/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ISlideConfig } from "@netless/slide";
|
|
2
2
|
import type { SlideAttributes } from "./typings";
|
|
3
3
|
|
|
4
|
+
import { jsPDF } from "jspdf";
|
|
4
5
|
import { Slide } from "@netless/slide";
|
|
5
6
|
import { SideEffectManager } from "side-effect-manager";
|
|
6
7
|
import { Remitter } from "remitter";
|
|
@@ -49,6 +50,10 @@ export class SlideViewer {
|
|
|
49
50
|
|
|
50
51
|
readonly slide: Slide;
|
|
51
52
|
|
|
53
|
+
private _getWhiteSnapshot:
|
|
54
|
+
| ((index: number, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void)
|
|
55
|
+
| null = null;
|
|
56
|
+
private _slideTitle = "";
|
|
52
57
|
private readonly _readyPromise: Promise<void>;
|
|
53
58
|
private readonly _infoPromise: Promise<SlideViewerInfoResponse>;
|
|
54
59
|
private _ready = false;
|
|
@@ -69,6 +74,7 @@ export class SlideViewer {
|
|
|
69
74
|
this.footer.on_new_page_index(this.onNewPageIndex);
|
|
70
75
|
this.footer.on_toggle_preview(this.onTogglePreview);
|
|
71
76
|
this.footer.on_play(this.onPlay);
|
|
77
|
+
this.footer.on_save_to_pdf(this.toPdf);
|
|
72
78
|
|
|
73
79
|
// remove dom on cleanup
|
|
74
80
|
this.sideEffect.push(this.sidebar.destroy);
|
|
@@ -181,6 +187,16 @@ export class SlideViewer {
|
|
|
181
187
|
return this._infoPromise.then(callback);
|
|
182
188
|
}
|
|
183
189
|
|
|
190
|
+
attachWhiteSnapshot(
|
|
191
|
+
snapshot: (index: number, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void
|
|
192
|
+
) {
|
|
193
|
+
this._getWhiteSnapshot = snapshot;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
setSlideTitle(title: string) {
|
|
197
|
+
this._slideTitle = title;
|
|
198
|
+
}
|
|
199
|
+
|
|
184
200
|
ready(callback: () => void) {
|
|
185
201
|
return this._readyPromise.then(callback);
|
|
186
202
|
}
|
|
@@ -232,4 +248,79 @@ export class SlideViewer {
|
|
|
232
248
|
this.slide.release();
|
|
233
249
|
this.events.emit("unfreeze");
|
|
234
250
|
}
|
|
251
|
+
|
|
252
|
+
toPdf = async (progressCallback: (progress: number) => void) => {
|
|
253
|
+
const MAX = 1920;
|
|
254
|
+
const resizeCanvas = document.createElement("canvas");
|
|
255
|
+
const resizeCtx = resizeCanvas.getContext("2d");
|
|
256
|
+
const { slideCount, width, height } = this.slide;
|
|
257
|
+
let pdfWidth = Math.floor(width);
|
|
258
|
+
let pdfHeight = Math.floor(height);
|
|
259
|
+
if (pdfWidth > MAX) {
|
|
260
|
+
pdfWidth = MAX;
|
|
261
|
+
pdfHeight = Math.floor((height * pdfWidth) / width);
|
|
262
|
+
}
|
|
263
|
+
if (pdfHeight > MAX) {
|
|
264
|
+
pdfHeight = MAX;
|
|
265
|
+
pdfWidth = Math.floor((width * pdfHeight) / height);
|
|
266
|
+
}
|
|
267
|
+
resizeCanvas.width = pdfWidth;
|
|
268
|
+
resizeCanvas.height = pdfHeight;
|
|
269
|
+
|
|
270
|
+
const whiteSnapshotCanvas = document.createElement("canvas");
|
|
271
|
+
whiteSnapshotCanvas.width = width;
|
|
272
|
+
whiteSnapshotCanvas.height = height;
|
|
273
|
+
const whiteCtx = whiteSnapshotCanvas.getContext("2d");
|
|
274
|
+
if (!whiteCtx || !this._getWhiteSnapshot || !resizeCtx) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const orientation = pdfWidth > pdfHeight ? "l" : "p";
|
|
279
|
+
const pdf = new jsPDF({
|
|
280
|
+
format: [pdfWidth, pdfHeight],
|
|
281
|
+
orientation,
|
|
282
|
+
compress: true,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
for (let i = 1; i <= slideCount; i++) {
|
|
286
|
+
const slideSnapshot = await this.slide.snapshotWithTimingEnd(i);
|
|
287
|
+
|
|
288
|
+
if (slideSnapshot) {
|
|
289
|
+
const img = document.createElement("img");
|
|
290
|
+
img.src = slideSnapshot;
|
|
291
|
+
await new Promise(resolve => (img.onload = resolve));
|
|
292
|
+
resizeCtx.drawImage(img, 0, 0, pdfWidth, pdfHeight);
|
|
293
|
+
}
|
|
294
|
+
whiteCtx.clearRect(0, 0, width, height);
|
|
295
|
+
this._getWhiteSnapshot(i, whiteSnapshotCanvas, whiteCtx);
|
|
296
|
+
try {
|
|
297
|
+
const whiteSnapshot = whiteSnapshotCanvas.toDataURL("image/png");
|
|
298
|
+
const whiteImg = document.createElement("img");
|
|
299
|
+
whiteImg.src = whiteSnapshot;
|
|
300
|
+
await new Promise(resolve => (whiteImg.onload = resolve));
|
|
301
|
+
resizeCtx.drawImage(whiteImg, 0, 0, pdfWidth, pdfHeight);
|
|
302
|
+
} catch (e) {
|
|
303
|
+
// ignore
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const outputDataUrl = resizeCanvas.toDataURL("image/jpeg", 0.6);
|
|
307
|
+
if (i > 1) {
|
|
308
|
+
pdf.addPage();
|
|
309
|
+
}
|
|
310
|
+
pdf.addImage(outputDataUrl, "JPEG", 0, 0, pdfWidth, pdfHeight, "", "FAST");
|
|
311
|
+
resizeCtx.clearRect(0, 0, pdfWidth, pdfHeight);
|
|
312
|
+
progressCallback(Math.ceil((i / slideCount) * 100));
|
|
313
|
+
}
|
|
314
|
+
const dataUrl = pdf.output("arraybuffer");
|
|
315
|
+
const blob = new Blob([dataUrl]);
|
|
316
|
+
const downloadUrl = URL.createObjectURL(blob);
|
|
317
|
+
const element = document.createElement("a");
|
|
318
|
+
element.setAttribute("href", downloadUrl);
|
|
319
|
+
element.setAttribute("download", `${this._slideTitle || "snapshot"}.pdf`);
|
|
320
|
+
element.style.display = "none";
|
|
321
|
+
document.body.appendChild(element);
|
|
322
|
+
element.click();
|
|
323
|
+
document.body.removeChild(element);
|
|
324
|
+
window.postMessage({ type: "@app-slide/_download_pdf_", buf: dataUrl });
|
|
325
|
+
};
|
|
235
326
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { ILazyLoadInstance } from "vanilla-lazyload";
|
|
1
2
|
import type { PreviewPage } from "./typings";
|
|
2
3
|
|
|
3
4
|
import LazyLoad from "vanilla-lazyload";
|
|
4
|
-
import { append, h,
|
|
5
|
+
import { append, h, hc, noop, wrap_class } from "../utils";
|
|
5
6
|
|
|
6
7
|
export function create_sidebar() {
|
|
7
8
|
type NewPageIndexCallback = (newPageIndex: number) => void;
|
|
@@ -19,11 +20,9 @@ export function create_sidebar() {
|
|
|
19
20
|
const class_readonly = wrap_class("readonly");
|
|
20
21
|
const class_preview_active = wrap_class("preview-active");
|
|
21
22
|
|
|
22
|
-
const $content =
|
|
23
|
-
set_class($content, "content");
|
|
23
|
+
const $content = hc("div", "content");
|
|
24
24
|
|
|
25
|
-
const $previewMask =
|
|
26
|
-
set_class($previewMask, "preview-mask");
|
|
25
|
+
const $previewMask = hc("div", "preview-mask");
|
|
27
26
|
$previewMask.onclick = function on_click_preview_mask(ev) {
|
|
28
27
|
if (readonly) return;
|
|
29
28
|
if (ev.target !== $previewMask) return;
|
|
@@ -31,8 +30,7 @@ export function create_sidebar() {
|
|
|
31
30
|
};
|
|
32
31
|
append($content, $previewMask);
|
|
33
32
|
|
|
34
|
-
const $preview =
|
|
35
|
-
set_class($preview, "preview");
|
|
33
|
+
const $preview = hc("div", "preview");
|
|
36
34
|
$preview.classList.add(wrap_class("tele-fancy-scrollbar"));
|
|
37
35
|
$preview.onclick = function on_click_preview(ev) {
|
|
38
36
|
if (readonly) return;
|
|
@@ -47,10 +45,7 @@ export function create_sidebar() {
|
|
|
47
45
|
};
|
|
48
46
|
append($content, $preview);
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
container: $preview,
|
|
52
|
-
elements_selector: `.${wrap_class("preview-page>img")}`,
|
|
53
|
-
});
|
|
48
|
+
let lazyload: ILazyLoadInstance | undefined;
|
|
54
49
|
|
|
55
50
|
function set_readonly(readonly_: boolean) {
|
|
56
51
|
if (readonly !== readonly_) {
|
|
@@ -63,6 +58,12 @@ export function create_sidebar() {
|
|
|
63
58
|
if (active !== active_) {
|
|
64
59
|
active = active_;
|
|
65
60
|
$content.classList.toggle(class_preview_active, active);
|
|
61
|
+
if (active && !lazyload) {
|
|
62
|
+
lazyload = new LazyLoad({
|
|
63
|
+
container: $preview,
|
|
64
|
+
elements_selector: `.${wrap_class("preview-page>img")}`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -89,8 +90,8 @@ export function create_sidebar() {
|
|
|
89
90
|
const $img = h("img");
|
|
90
91
|
$img.width = page.width;
|
|
91
92
|
$img.height = page.height;
|
|
92
|
-
$img.src = page.thumbnail;
|
|
93
93
|
$img.alt = `page-${i}`;
|
|
94
|
+
$img.dataset.src = page.thumbnail;
|
|
94
95
|
$img.dataset.pageIndex = pageIndex;
|
|
95
96
|
|
|
96
97
|
append($page, $img);
|
|
@@ -99,7 +100,7 @@ export function create_sidebar() {
|
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
if (!destroyed) {
|
|
102
|
-
lazyload
|
|
103
|
+
lazyload?.update();
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
}
|
|
@@ -118,7 +119,7 @@ export function create_sidebar() {
|
|
|
118
119
|
|
|
119
120
|
function destroy() {
|
|
120
121
|
destroyed = true;
|
|
121
|
-
lazyload
|
|
122
|
+
lazyload?.destroy();
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
function get_active() {
|
package/src/connect.ts
CHANGED
|
@@ -23,6 +23,30 @@ export function connect({ context, storage, viewer, sideEffect, logger }: Connec
|
|
|
23
23
|
width && context.box.setStageRatio(height / width);
|
|
24
24
|
whiteboard = context.createWhiteBoardView({ size: slideCount });
|
|
25
25
|
whiteboard.setBaseRect({ width, height });
|
|
26
|
+
|
|
27
|
+
viewer.setSlideTitle(context.box.title);
|
|
28
|
+
viewer.attachWhiteSnapshot(
|
|
29
|
+
(index: number, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => {
|
|
30
|
+
if (!whiteboard) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const scenePath = whiteboard.pageState.pages[index - 1];
|
|
34
|
+
canvas.width = whiteboard.view.size.width;
|
|
35
|
+
canvas.height = whiteboard.view.size.height;
|
|
36
|
+
whiteboard.view.screenshotToCanvas(
|
|
37
|
+
ctx,
|
|
38
|
+
scenePath,
|
|
39
|
+
whiteboard.view.size.width,
|
|
40
|
+
whiteboard.view.size.height,
|
|
41
|
+
{
|
|
42
|
+
centerX: 0,
|
|
43
|
+
centerY: 0,
|
|
44
|
+
scale: whiteboard.view.camera.scale,
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
26
50
|
kick_start();
|
|
27
51
|
});
|
|
28
52
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function saveSVG(namespace: string): SVGElement {
|
|
2
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
3
|
+
|
|
4
|
+
const $svg = document.createElementNS(NS, "svg");
|
|
5
|
+
$svg.setAttribute("class", `${namespace}-footer-icon-save`);
|
|
6
|
+
$svg.setAttribute("viewBox", "0 0 448 512");
|
|
7
|
+
|
|
8
|
+
const $path = document.createElementNS(NS, "path");
|
|
9
|
+
$path.setAttribute("fill", "currentColor");
|
|
10
|
+
$path.setAttribute(
|
|
11
|
+
"d",
|
|
12
|
+
"M224 256c-35.2 0-64 28.8-64 64c0 35.2 28.8 64 64 64c35.2 0 64-28.8 64-64C288 284.8 259.2 256 224 256zM433.1 129.1l-83.9-83.9C341.1 37.06 328.8 32 316.1 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h320c35.35 0 64-28.65 64-64V163.9C448 151.2 442.9 138.9 433.1 129.1zM128 80h144V160H128V80zM400 416c0 8.836-7.164 16-16 16H64c-8.836 0-16-7.164-16-16V96c0-8.838 7.164-16 16-16h16v104c0 13.25 10.75 24 24 24h192C309.3 208 320 197.3 320 184V83.88l78.25 78.25C399.4 163.2 400 164.8 400 166.3V416z"
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
$svg.appendChild($path);
|
|
16
|
+
|
|
17
|
+
return $svg;
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function spinnerSVG(namespace: string): SVGElement {
|
|
2
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
3
|
+
|
|
4
|
+
const $svg = document.createElementNS(NS, "svg");
|
|
5
|
+
$svg.setAttribute("class", `${namespace}-footer-icon-spinner`);
|
|
6
|
+
$svg.setAttribute("viewBox", "0 0 512 512");
|
|
7
|
+
|
|
8
|
+
const $path = document.createElementNS(NS, "path");
|
|
9
|
+
$path.setAttribute("fill", "currentColor");
|
|
10
|
+
$path.setAttribute(
|
|
11
|
+
"d",
|
|
12
|
+
"M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const $animate = document.createElementNS(NS, "animateTransform");
|
|
16
|
+
$animate.setAttribute("attributeName", "transform");
|
|
17
|
+
$animate.setAttribute("attributeType", "xml");
|
|
18
|
+
$animate.setAttribute("type", "rotate");
|
|
19
|
+
$animate.setAttribute("from", "0 256 256");
|
|
20
|
+
$animate.setAttribute("to", "360 256 256");
|
|
21
|
+
$animate.setAttribute("begin", "0s");
|
|
22
|
+
$animate.setAttribute("dur", "2s");
|
|
23
|
+
$animate.setAttribute("fill", "freeze");
|
|
24
|
+
$animate.setAttribute("repeatCount", "indefinite");
|
|
25
|
+
$path.appendChild($animate);
|
|
26
|
+
|
|
27
|
+
const $progress = document.createElementNS(NS, "text");
|
|
28
|
+
$progress.setAttribute("x", "125");
|
|
29
|
+
$progress.setAttribute("y", "345");
|
|
30
|
+
$progress.setAttribute("data-id", "progress");
|
|
31
|
+
$progress.style.fontSize = "246px";
|
|
32
|
+
$progress.textContent = "20";
|
|
33
|
+
$svg.appendChild($progress);
|
|
34
|
+
$svg.appendChild($path);
|
|
35
|
+
|
|
36
|
+
return $svg;
|
|
37
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -70,7 +70,7 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, void> = {
|
|
|
70
70
|
// For debugging.
|
|
71
71
|
Object.assign(viewer, { context, logger });
|
|
72
72
|
|
|
73
|
-
sideEffect.push(refrigerator.set(context.appId, viewer));
|
|
73
|
+
sideEffect.push(refrigerator.set(context.appId, viewer, context.box));
|
|
74
74
|
|
|
75
75
|
if (import.meta.env.DEV) {
|
|
76
76
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/preview.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { SlideViewerOptions } from "./SlideViewer";
|
|
2
2
|
|
|
3
3
|
import { SlideViewer } from "./SlideViewer";
|
|
4
|
+
import { hc } from "./utils";
|
|
4
5
|
|
|
5
6
|
export function previewSlide(options: SlideViewerOptions & { container: HTMLElement }) {
|
|
6
7
|
const { container, ...slideOptions } = options;
|
|
7
|
-
const wrapper =
|
|
8
|
-
wrapper.className = "netless-app-slide-preview-wrapper";
|
|
8
|
+
const wrapper = hc("div", "preview-wrapper");
|
|
9
9
|
const viewer = new SlideViewer(slideOptions);
|
|
10
10
|
wrapper.appendChild(document.createElement("style")).textContent = SlideViewer.styles;
|
|
11
11
|
wrapper.appendChild(viewer.$content);
|