@netless/forge-slide 0.1.0 → 0.1.1-alpha.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/dist/Slide.d.ts +57 -1
- package/dist/Slide.d.ts.map +1 -1
- package/dist/SlideApplication.d.ts +7 -1
- package/dist/SlideApplication.d.ts.map +1 -1
- package/dist/index.esm.js +190 -11
- package/dist/index.esm.js.map +2 -2
- package/dist/index.js +190 -11
- package/dist/index.js.map +3 -3
- package/package.json +6 -5
- package/src/Slide.ts +57 -2
- package/src/SlideApplication.ts +164 -18
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/forge-slide",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-alpha.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@netless/slide": "^1.4.15"
|
|
9
|
+
"@netless/slide": "^1.4.15",
|
|
10
|
+
"uuid": "^11.0.5",
|
|
11
|
+
"@netless/forge-room": "0.1.8",
|
|
12
|
+
"@netless/forge-whiteboard": "0.1.14"
|
|
10
13
|
},
|
|
11
14
|
"peerDependencies": {
|
|
12
15
|
"eventemitter3": "^5.0.1",
|
|
13
16
|
"yjs": "^13.6.18",
|
|
14
|
-
"
|
|
15
|
-
"@netless/forge-room": "0.1.8",
|
|
16
|
-
"@netless/forge-whiteboard": "0.1.14"
|
|
17
|
+
"@netless/forge-room": "0.1.8"
|
|
17
18
|
},
|
|
18
19
|
"keywords": [],
|
|
19
20
|
"author": "",
|
package/src/Slide.ts
CHANGED
|
@@ -22,6 +22,18 @@ export interface SlideEvents {
|
|
|
22
22
|
* @param {number} pageIndex 页面索引
|
|
23
23
|
*/
|
|
24
24
|
renderEnd: (pageIndex: number) => void;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 主序列动画完成时触发
|
|
28
|
+
* @param {number} animateIndex 主动画索引
|
|
29
|
+
*/
|
|
30
|
+
mainSeqStepEnd: (animateIndex: number) => void;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 主序列动画开始时触发
|
|
34
|
+
* @param {number} animateIndex 主动画索引
|
|
35
|
+
*/
|
|
36
|
+
mainSeqStepStart: (animateIndex: number) => void;
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
|
|
@@ -29,11 +41,54 @@ export class SlideForge extends EventEmitter<SlideEvents> implements Application
|
|
|
29
41
|
|
|
30
42
|
public readonly view!: HTMLDivElement;
|
|
31
43
|
public readonly permissions!: ForgeSlidePermissions;
|
|
44
|
+
public readonly footView!: HTMLDivElement;
|
|
45
|
+
/**
|
|
46
|
+
* 当前页面索引, 从 0 开始
|
|
47
|
+
*/
|
|
32
48
|
public readonly pageIndex!: number;
|
|
33
|
-
|
|
49
|
+
/**
|
|
50
|
+
* 总页数
|
|
51
|
+
*/
|
|
52
|
+
public readonly pageCount!: number;
|
|
34
53
|
/**
|
|
35
54
|
* 切换到参数指定页面, index 从 0 开始
|
|
36
55
|
* @param {number} index 页面索引
|
|
37
56
|
*/
|
|
38
|
-
public goto!: (index: number) =>
|
|
57
|
+
public goto!: (index: number) => void
|
|
58
|
+
/**
|
|
59
|
+
* 下一步, 如果已经是本页的最后一步, 则切换到下一页
|
|
60
|
+
*/
|
|
61
|
+
public nextStep!: () => void
|
|
62
|
+
/**
|
|
63
|
+
* 上一步, 如果已经是本页的第一步, 则切换到上一页
|
|
64
|
+
*/
|
|
65
|
+
public prevStep!: () => void
|
|
66
|
+
/**
|
|
67
|
+
* 下一页, 如果是最后一页, 则不执行任何操作
|
|
68
|
+
*/
|
|
69
|
+
public nextPage!: () => void
|
|
70
|
+
/**
|
|
71
|
+
* 上一页, 如果是第一页, 则不执行任何操作
|
|
72
|
+
*/
|
|
73
|
+
public prevPage!: () => void
|
|
74
|
+
/**
|
|
75
|
+
* 切换侧栏显示状态
|
|
76
|
+
*/
|
|
77
|
+
public sideBarToggle!: () => void
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 获取预览图图片内容, base64 编码
|
|
81
|
+
* @param {number} index 页面索引
|
|
82
|
+
*/
|
|
83
|
+
public imgContent!: (index: number) => Promise<string>;
|
|
84
|
+
/**
|
|
85
|
+
* 获取预览图图片链接
|
|
86
|
+
* @param {number} index 页面索引
|
|
87
|
+
*/
|
|
88
|
+
public imgUrl!: (index: number) => Promise<string>;
|
|
89
|
+
/**
|
|
90
|
+
* 获取预览图图片尺寸
|
|
91
|
+
* @param {number} index 页面索引
|
|
92
|
+
*/
|
|
93
|
+
public imgSize!: (index: number) => Promise<{width: number, height: number}>;
|
|
39
94
|
}
|
package/src/SlideApplication.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {FooterView} from "./FoorerView";
|
|
|
7
7
|
import * as Y from "yjs";
|
|
8
8
|
import {SideBarView} from "./SiderBarView";
|
|
9
9
|
import {deepEqual, delay} from "./utils";
|
|
10
|
+
import { kvStore } from "@netless/forge-room";
|
|
10
11
|
|
|
11
12
|
export interface SlideApplicationOption {
|
|
12
13
|
prefix: string;
|
|
@@ -18,6 +19,13 @@ export interface SlideApplicationOption {
|
|
|
18
19
|
inheritWhiteboardId?: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
interface PreviewImage {
|
|
23
|
+
url: string;
|
|
24
|
+
src: string;
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
export const Slide_APP_NAME = "forge_slide";
|
|
22
30
|
|
|
23
31
|
export class SlideApplication extends AbstractApplication<SlideApplicationOption, SlideForge> {
|
|
@@ -25,6 +33,7 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
25
33
|
static applicationName = Slide_APP_NAME;
|
|
26
34
|
|
|
27
35
|
public readonly name: string = Slide_APP_NAME;
|
|
36
|
+
public readonly emitter: SlideForge = new SlideForge();
|
|
28
37
|
|
|
29
38
|
private whiteboardApp!: WhiteboardApplication;
|
|
30
39
|
private whiteboard!: Whiteboard;
|
|
@@ -37,9 +46,12 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
37
46
|
private sideBar: SideBarView;
|
|
38
47
|
private slide!: Slide;
|
|
39
48
|
private currentSlideIndex: number = 0;
|
|
49
|
+
private taskId: string = "";
|
|
50
|
+
private prefix: string = "";
|
|
40
51
|
|
|
41
52
|
constructor() {
|
|
42
53
|
super();
|
|
54
|
+
(window as any).emitter = this.emitter;
|
|
43
55
|
this.rootView.setAttribute("data-forge-app", Slide_APP_NAME);
|
|
44
56
|
this.rootView.style.background = "#f0f0f0f0";
|
|
45
57
|
this.rootView.style.overflow = "hidden";
|
|
@@ -56,7 +68,9 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
56
68
|
this.footer = new FooterView();
|
|
57
69
|
this.sideBar = new SideBarView();
|
|
58
70
|
this.sideBar.on("pageChange", index => {
|
|
59
|
-
this.slide.
|
|
71
|
+
if (index > 0 && index <= this.slide.slideCount) {
|
|
72
|
+
this.slide.renderSlide(index);
|
|
73
|
+
}
|
|
60
74
|
});
|
|
61
75
|
this.footer.on("prevStep", () => {
|
|
62
76
|
if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changeStep)) {
|
|
@@ -129,34 +143,154 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
129
143
|
return that.permissions;
|
|
130
144
|
}
|
|
131
145
|
});
|
|
146
|
+
Object.defineProperty(this.emitter, "footView", {
|
|
147
|
+
get(): any {
|
|
148
|
+
return that.footer.root;
|
|
149
|
+
}
|
|
150
|
+
})
|
|
132
151
|
Object.defineProperty(this.emitter, "pageIndex", {
|
|
133
152
|
get(): any {
|
|
134
153
|
return that.currentSlideIndex;
|
|
135
154
|
}
|
|
136
155
|
});
|
|
137
|
-
Object.defineProperty(this.emitter, "
|
|
156
|
+
Object.defineProperty(this.emitter, "pageCount", {
|
|
138
157
|
get(): any {
|
|
139
|
-
return
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
return that.slide.slideCount;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
Object.defineProperty(this.emitter, "goto", {
|
|
162
|
+
writable: false,
|
|
163
|
+
enumerable: false,
|
|
164
|
+
value: (pageIndex: number) => {
|
|
165
|
+
this.sideBar.emit("pageChange", pageIndex);
|
|
144
166
|
}
|
|
145
167
|
});
|
|
168
|
+
Object.defineProperty(this.emitter, "nextStep", {
|
|
169
|
+
writable: false,
|
|
170
|
+
enumerable: false,
|
|
171
|
+
value: () => {
|
|
172
|
+
this.footer.emit("nextStep");
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
Object.defineProperty(this.emitter, "prevStep", {
|
|
176
|
+
writable: false,
|
|
177
|
+
enumerable: false,
|
|
178
|
+
value: () => {
|
|
179
|
+
this.footer.emit("prevStep");
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
Object.defineProperty(this.emitter, "nextPage", {
|
|
183
|
+
writable: false,
|
|
184
|
+
enumerable: false,
|
|
185
|
+
value: () => {
|
|
186
|
+
this.footer.emit("nextPage");
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
Object.defineProperty(this.emitter, "prevPage", {
|
|
190
|
+
writable: false,
|
|
191
|
+
enumerable: false,
|
|
192
|
+
value: () => {
|
|
193
|
+
this.footer.emit("prevPage");
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
Object.defineProperty(this.emitter, "sideBarToggle", {
|
|
197
|
+
writable: false,
|
|
198
|
+
enumerable: false,
|
|
199
|
+
value: () => {
|
|
200
|
+
this.footer.emit("sideBarToggle");
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
Object.defineProperty(this.emitter, "imgContent", {
|
|
204
|
+
writable: false,
|
|
205
|
+
enumerable: false,
|
|
206
|
+
value: (pageIndex: number) => {
|
|
207
|
+
return this.getImageContent(pageIndex);
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
Object.defineProperty(this.emitter, "imgUrl", {
|
|
212
|
+
writable: false,
|
|
213
|
+
enumerable: false,
|
|
214
|
+
value: (pageIndex: number) => {
|
|
215
|
+
return this.getImageUrl(pageIndex);
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
Object.defineProperty(this.emitter, "imgSize", {
|
|
220
|
+
writable: false,
|
|
221
|
+
enumerable: false,
|
|
222
|
+
value: (pageIndex: number) => {
|
|
223
|
+
return this.getImageSize(pageIndex);
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private async getPreviewImage(imageUrl: string) {
|
|
229
|
+
const image = fetch(imageUrl);
|
|
230
|
+
return await image.then(res => res.blob()).then(blob => {
|
|
231
|
+
return new Promise<PreviewImage>(resolve => {
|
|
232
|
+
const reader = new FileReader();
|
|
233
|
+
reader.onloadend = () => {
|
|
234
|
+
const base64Data = reader.result as string;
|
|
235
|
+
const img = document.createElement("img");
|
|
236
|
+
img.src = base64Data;
|
|
237
|
+
img.onload = () => {
|
|
238
|
+
resolve({
|
|
239
|
+
url: imageUrl,
|
|
240
|
+
src: base64Data,
|
|
241
|
+
width: img.width,
|
|
242
|
+
height: img.height,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
reader.readAsDataURL(blob);
|
|
247
|
+
})
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private async getImageUrl(pageIndex: number) {
|
|
252
|
+
return `${this.prefix}/${this.taskId}/preview/${pageIndex + 1}.png`;
|
|
146
253
|
}
|
|
147
254
|
|
|
148
|
-
|
|
255
|
+
private async getImageSize(pageIndex: number) {
|
|
256
|
+
const imageUrl = `${this.prefix}/${this.taskId}/preview/${pageIndex + 1}.png`;
|
|
257
|
+
let preview: PreviewImage | null = null;
|
|
258
|
+
try {
|
|
259
|
+
const result = await kvStore.getItem(imageUrl);
|
|
260
|
+
preview = result ? JSON.parse(result) : null;
|
|
261
|
+
} catch (e) {
|
|
262
|
+
console.warn("kvStore getItem error", e);
|
|
263
|
+
}
|
|
264
|
+
if (preview) {
|
|
265
|
+
return { width: preview.width, height: preview.height };
|
|
266
|
+
}
|
|
267
|
+
preview = await this.getPreviewImage(imageUrl);
|
|
268
|
+
await kvStore.setItem(imageUrl, JSON.stringify(preview));
|
|
269
|
+
return { width: preview.width, height: preview.height };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private async getImageContent(pageIndex: number) {
|
|
273
|
+
const imageUrl = `${this.prefix}/${this.taskId}/preview/${pageIndex + 1}.png`;
|
|
274
|
+
let preview: PreviewImage | null = null;
|
|
275
|
+
try {
|
|
276
|
+
const result = await kvStore.getItem(imageUrl);
|
|
277
|
+
preview = result ? JSON.parse(result) : null;
|
|
278
|
+
} catch (e) {
|
|
279
|
+
console.warn("kvStore getItem error", e);
|
|
280
|
+
}
|
|
281
|
+
if (preview) {
|
|
282
|
+
return preview.src;
|
|
283
|
+
}
|
|
284
|
+
preview = await this.getPreviewImage(imageUrl);
|
|
285
|
+
await kvStore.setItem(imageUrl, JSON.stringify(preview));
|
|
286
|
+
return preview.src;
|
|
287
|
+
}
|
|
149
288
|
|
|
150
289
|
private applySlideState = async (slideState: any, lastDispatch: any) => {
|
|
151
|
-
// console.log('%cstate', 'color: blue; font-size: 20px;');
|
|
152
|
-
// console.log(slideStateFromServer);
|
|
153
|
-
// console.log('%cevent', 'color: blue; font-size: 20px;');
|
|
154
|
-
// console.log(dispatchFromServer);
|
|
155
290
|
if (this.slide.slideState.currentSlideIndex < 0) {
|
|
156
291
|
this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
|
|
157
292
|
return;
|
|
158
293
|
}
|
|
159
|
-
console.log('%cisEqual', 'color: blue; font-size: 20px;', deepEqual(this.slide.slideState, slideState));
|
|
160
294
|
if (this.slide.slideState.currentSlideIndex > 0 && deepEqual(this.slide.slideState, slideState)) {
|
|
161
295
|
this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
|
|
162
296
|
} else {
|
|
@@ -178,6 +312,8 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
178
312
|
}
|
|
179
313
|
|
|
180
314
|
public async initialize(option: SlideApplicationOption): Promise<void> {
|
|
315
|
+
this.prefix = option.prefix;
|
|
316
|
+
this.taskId = option.taskId;
|
|
181
317
|
const whiteboardApp = new WhiteboardApplication();
|
|
182
318
|
// @ts-ignore
|
|
183
319
|
whiteboardApp.roomDoc = this.roomDoc;
|
|
@@ -220,7 +356,6 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
220
356
|
|
|
221
357
|
if (option.inheritWhiteboardId) {
|
|
222
358
|
whiteboardApp.linkToWhiteboard(option.inheritWhiteboardId);
|
|
223
|
-
console.log("inheritWhiteboardId", option.inheritWhiteboardId);
|
|
224
359
|
}
|
|
225
360
|
|
|
226
361
|
this.whiteboard.setViewModeToMain();
|
|
@@ -248,25 +383,36 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
248
383
|
})
|
|
249
384
|
})
|
|
250
385
|
|
|
386
|
+
this.slide.on("mainSeqStepStart", (animateIndex: number) => {
|
|
387
|
+
this.emitter.emit("mainSeqStepStart", animateIndex);
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
this.slide.on("mainSeqStepEnd", (animateIndex: number) => {
|
|
391
|
+
this.emitter.emit("mainSeqStepEnd", animateIndex);
|
|
392
|
+
})
|
|
393
|
+
|
|
251
394
|
this.slide.on("renderStart", (slideIndex) => {
|
|
252
395
|
this.whiteboardApp.emitter.view.style.opacity = "0";
|
|
253
396
|
this.whiteboardApp.emitter.addPage(`${slideIndex}`);
|
|
254
397
|
this.whiteboardApp.emitter.gotoPage(`${slideIndex}`);
|
|
398
|
+
this.emitter.emit("renderStart", slideIndex);
|
|
255
399
|
})
|
|
256
400
|
|
|
257
401
|
this.slide.on("renderEnd", (slideIndex) => {
|
|
258
402
|
this.currentSlideIndex = slideIndex;
|
|
259
403
|
this.whiteboardApp.emitter.view.style.opacity = "1";
|
|
404
|
+
this.emitter.emit("renderEnd", slideIndex);
|
|
260
405
|
});
|
|
261
406
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
407
|
+
this.slide.on("stateChange", (state) => {
|
|
408
|
+
this.getMap(this.name).set("slideState", state)
|
|
409
|
+
})
|
|
265
410
|
|
|
266
411
|
this.getMap(this.name).observe(this.onSlideEventHandler);
|
|
267
412
|
|
|
268
413
|
(window as any).slide = this.slide;
|
|
269
414
|
(window as any).slideWhiteboard = this.whiteboardApp;
|
|
415
|
+
(window as any).forgeSlide = this;
|
|
270
416
|
|
|
271
417
|
const syncSlide = this.getMap(this.name).get("slideState");
|
|
272
418
|
if (syncSlide && syncSlide.taskId === option.taskId) {
|
|
@@ -279,7 +425,7 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
|
|
|
279
425
|
return this.userMap(userId);
|
|
280
426
|
});
|
|
281
427
|
this.permissions.on("change", (userId, flags, value) => {
|
|
282
|
-
|
|
428
|
+
this.emitter.emit("permissionChange", userId, flags, value);
|
|
283
429
|
if (this.userId === userId) {
|
|
284
430
|
if (flags.includes(ForgeSlidePermissionFlag.clickAnim)) {
|
|
285
431
|
this.slideContainer.style.pointerEvents = "auto";
|