@netless/forge-slide 0.1.0-alpha.0

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.
@@ -0,0 +1,3 @@
1
+ export declare function deepEqual(a: any, b: any, excludeKeys?: string[]): boolean;
2
+ export declare function delay(time: number): Promise<void>;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,WAAW,GAAE,MAAM,EAAa,WAkDzE;AAWD,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAA8D"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@netless/forge-slide",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "dependencies": {
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"
13
+ },
14
+ "peerDependencies": {
15
+ "eventemitter3": "^5.0.1",
16
+ "yjs": "^13.6.18",
17
+ "@netless/forge-room": "0.1.8"
18
+ },
19
+ "keywords": [],
20
+ "author": "",
21
+ "license": "ISC",
22
+ "scripts": {
23
+ "dev": "onChange 'src/**/*' -- npm run build",
24
+ "build": "tsc --emitDeclarationOnly && node build.mjs"
25
+ }
26
+ }
@@ -0,0 +1,109 @@
1
+ import EventEmitter from "eventemitter3";
2
+ import { Icons } from "./icons";
3
+
4
+ interface FooterViewEvents {
5
+ prevPage: () => void;
6
+ nextPage: () => void;
7
+ prevStep: () => void;
8
+ nextStep: () => void;
9
+ sideBarToggle: () => void;
10
+ }
11
+
12
+ const EM_COLOR = "#8C8C8C";
13
+
14
+ export class FooterView extends EventEmitter<FooterViewEvents> {
15
+
16
+ public readonly root: HTMLDivElement;
17
+
18
+ private prevStep: HTMLDivElement;
19
+ private nextStep: HTMLDivElement;
20
+
21
+ private prevPage: HTMLDivElement;
22
+ private nextPage: HTMLDivElement;
23
+
24
+ private sideBarToggle: HTMLDivElement;
25
+
26
+ public constructor() {
27
+ super();
28
+ this.root = document.createElement("div");
29
+ this.root.style.height = "24px";
30
+ this.root.style.zIndex = "6";
31
+ this.root.style.display = "flex";
32
+ this.root.style.alignItems = "center";
33
+ this.root.style.justifyContent = "center";
34
+ this.root.style.backgroundColor = "#fff";
35
+
36
+ this.prevStep = this.createIcon(Icons.prevStep(EM_COLOR), () => {
37
+ this.emit("prevStep");
38
+ });
39
+
40
+ this.nextStep = this.createIcon(Icons.nextStep(EM_COLOR), () => {
41
+ this.emit("nextStep");
42
+ });
43
+
44
+ this.prevPage = this.createIcon(Icons.prevPage(EM_COLOR), () => {
45
+ this.emit("prevPage");
46
+ });
47
+
48
+ this.nextPage = this.createIcon(Icons.nextPage(EM_COLOR), () => {
49
+ this.emit("nextPage");
50
+ });
51
+
52
+ this.sideBarToggle = this.createIcon(Icons.sideBar(EM_COLOR), () => {
53
+ this.emit("sideBarToggle")
54
+ });
55
+
56
+ this.root.appendChild(this.sideBarToggle);
57
+ this.root.appendChild(this.createSpacer());
58
+ this.root.appendChild(this.prevPage);
59
+ this.root.appendChild(this.prevStep);
60
+ this.root.appendChild(this.nextStep);
61
+ this.root.appendChild(this.nextPage);
62
+ this.root.appendChild(this.createSpacer());
63
+ }
64
+
65
+ public prevPageState(enable: boolean): void {
66
+ if (enable) {
67
+ this.prevPage.style.pointerEvents = "all";
68
+ this.prevPage.style.opacity = "1";
69
+ } else {
70
+ this.prevPage.style.pointerEvents = "none";
71
+ this.prevPage.style.opacity = "0.5";
72
+ }
73
+ }
74
+
75
+ public nextPageState(enable: boolean): void {
76
+ if (enable) {
77
+ this.nextPage.style.pointerEvents = "all";
78
+ this.nextPage.style.opacity = "1";
79
+ } else {
80
+ this.nextPage.style.pointerEvents = "none";
81
+ this.nextPage.style.opacity = "0.5";
82
+ }
83
+ }
84
+
85
+ private createSpacer(): HTMLDivElement {
86
+ const div = document.createElement("div");
87
+ div.style.flex = "1 1 auto";
88
+ return div;
89
+ }
90
+
91
+ private createIcon(svgContent: string, action: () => void): HTMLDivElement {
92
+ const icon = document.createElement("div");
93
+ icon.style.width = "18px";
94
+ icon.style.height = "18px";
95
+ icon.style.borderRadius = "2px";
96
+ icon.style.margin = "6px";
97
+ icon.innerHTML = svgContent;
98
+ icon.addEventListener("click", () => {
99
+ action();
100
+ });
101
+ icon.addEventListener("mouseover", () => {
102
+ icon.style.backgroundColor = "#f0f0f0";
103
+ });
104
+ icon.addEventListener("mouseout", () => {
105
+ icon.style.backgroundColor = "transparent";
106
+ });
107
+ return icon;
108
+ }
109
+ }
@@ -0,0 +1,159 @@
1
+ import { type RoomUser, type UserManager } from "@netless/forge-room";
2
+ import EventEmitter from "eventemitter3";
3
+ import * as Y from "yjs";
4
+
5
+ export enum ForgeSlidePermissionFlag {
6
+ /**
7
+ * 没有任何权限, 只能同步他人的操作
8
+ */
9
+ none = 0b0000000000000000,
10
+ /**
11
+ * 可以操作上一步下一步动画
12
+ */
13
+ changeStep = 0b0000000000000001,
14
+ /**
15
+ * 可以切面页码
16
+ */
17
+ changePage = 0b0000000000000010,
18
+ /**
19
+ * 可以触发点击动画
20
+ */
21
+ clickAnim = 0b0000000000000100,
22
+ /**
23
+ * 拥有所有权限
24
+ */
25
+ all = ForgeSlidePermissionFlag.changePage |
26
+ ForgeSlidePermissionFlag.changeStep |
27
+ ForgeSlidePermissionFlag.clickAnim
28
+ }
29
+
30
+ export interface ForgeSlidePermissionEvents {
31
+ /**
32
+ * 当用户权限变更时触发
33
+ * @param { string } userId 对应 userId
34
+ * @param { ForgeSlidePermissionFlag[] } flags 当前权限列表
35
+ * @param { number } value 权限值
36
+ */
37
+ change: (userId: string, flags: ForgeSlidePermissionFlag[], value: number) => void;
38
+ }
39
+
40
+ export class ForgeSlidePermissions extends EventEmitter<ForgeSlidePermissionEvents> {
41
+
42
+ private requestUserMap: (userId: string) => Y.Map<any>;
43
+ private userManager: UserManager;
44
+ private observers: Map<string, any> = new Map();
45
+
46
+ public constructor(
47
+ userManager: UserManager,
48
+ requestUserMap: (userId: string) => Y.Map<any>,
49
+ ) {
50
+ super();
51
+ this.userManager = userManager;
52
+ this.requestUserMap = requestUserMap;
53
+ this.createModel(this.userManager.selfId);
54
+ this.userManager.userIdList().forEach((userId) => {
55
+ this.addObserve(userId);
56
+ });
57
+
58
+ this.userManager.on("join", this.handleUserJoin);
59
+ this.userManager.on("leave", this.handleUserLeave);
60
+ }
61
+
62
+ private handleUserLeave = (user: RoomUser) => {
63
+ const cb = this.observers.get(user.id);
64
+ if (cb) {
65
+ this.requestUserMap(user.id).unobserve(cb);
66
+ }
67
+ };
68
+
69
+ private handleUserJoin = (user: RoomUser) => {
70
+ this.addObserve(user.id);
71
+ };
72
+
73
+ private addObserve(userId: string) {
74
+ const observer = (evt: Y.YMapEvent<any>) => {
75
+ this.handleUserPermissionChange(userId, evt);
76
+ };
77
+ this.observers.set(userId, observer);
78
+ this.requestUserMap(userId).observe(observer)
79
+ }
80
+
81
+ private createModel(userId: string) {
82
+ const userMap = this.requestUserMap(userId);
83
+ if (!userMap.has("permission")) {
84
+ userMap.set("permission", 0);
85
+ }
86
+ }
87
+
88
+ private handleUserPermissionChange(userId: string, evt: Y.YMapEvent<any>) {
89
+ for (const [key, value] of evt.changes.keys.entries()) {
90
+ if (key === "permission") {
91
+ if (value.action === "add" || value.action === "update") {
92
+ const newValue = this.requestUserMap(userId).get("permission");
93
+ this.emit("change", userId, this.resolveFlags(newValue), newValue);
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 解析权限列表组合
101
+ * @param {number} value - 权限数字值
102
+ * @return {WhiteboardPermissionFlag[]} - 权限列表
103
+ */
104
+ public resolveFlags(value: number): ForgeSlidePermissionFlag[] {
105
+ return [
106
+ ForgeSlidePermissionFlag.changePage,
107
+ ForgeSlidePermissionFlag.changeStep,
108
+ ForgeSlidePermissionFlag.clickAnim
109
+ ].filter(v => (v & value) !== 0)
110
+ }
111
+
112
+ /**
113
+ * 获取权限列表组合对应的数值
114
+ * @param { string } userId 不传表示获取自己
115
+ */
116
+ public getPermissionValue(userId?: string): number {
117
+ return this.requestUserMap(userId ?? this.userManager.selfId).get("permission") ?? 0;
118
+ }
119
+
120
+ /**
121
+ * 获取权限列表
122
+ * @param {string=} userId 可选, 不传表示获取自己
123
+ */
124
+ public getPermissionFlags(userId?: string): ForgeSlidePermissionFlag[] {
125
+ const value = this.requestUserMap(userId ?? this.userManager.selfId).get("permission") ?? 0;
126
+ return this.resolveFlags(value);
127
+ }
128
+
129
+ /**
130
+ * 返回对应 userId 是否有相应权限
131
+ * @param {string=} userId 可选, 不传表示返回自己是否有相应权限
132
+ * @param {WhiteboardPermissionFlag} flag
133
+ */
134
+ public hasPermission(flag: ForgeSlidePermissionFlag, userId?: string): boolean {
135
+ return ((this.requestUserMap(userId ?? this.userManager.selfId).get("permission") ?? 0) & flag) !== 0;
136
+ }
137
+
138
+ /**
139
+ * 添加权限
140
+ * @param {WhiteboardPermissionFlag} flag 权限标记
141
+ * @param {string=} userId 可选, 为 userId 添加权限, 不传表示为自己添加权限
142
+ */
143
+ public addPermission(flag: ForgeSlidePermissionFlag, userId?: string) {
144
+ const userMap = this.requestUserMap(userId ?? this.userManager.selfId);
145
+ const oldValue = userMap.get("permission") ?? 0;
146
+ this.requestUserMap(userId ?? this.userManager.selfId).set("permission", oldValue | flag);
147
+ }
148
+
149
+ /**
150
+ * 移除权限
151
+ * @param {WhiteboardPermissionFlag} flag 权限标记
152
+ * @param {string=} userId 可选, 为 userId 移除权限, 不传表示为自己移除权限
153
+ */
154
+ public removePermission(flag: ForgeSlidePermissionFlag, userId?: string) {
155
+ const userMap = this.requestUserMap(userId ?? this.userManager.selfId);
156
+ const oldValue = userMap.get("permission") ?? 0;
157
+ this.requestUserMap(userId ?? this.userManager.selfId).set("permission", oldValue & (~flag));
158
+ }
159
+ }
@@ -0,0 +1,91 @@
1
+ import {SlideApplicationOption} from "./SlideApplication";
2
+ import EventEmitter from "eventemitter3";
3
+
4
+ interface SideBarViewEvents {
5
+ pageChange: (index: number) => void;
6
+ }
7
+
8
+ export class SideBarView extends EventEmitter<SideBarViewEvents> {
9
+ public readonly root: HTMLDivElement = document.createElement("div");
10
+ private itemList: HTMLDivElement[] = [];
11
+
12
+ public constructor() {
13
+ super();
14
+ this.root.style.backgroundColor = '#eee';
15
+ this.root.className = "slide-sidebar";
16
+ this.root.style.width = "240px";
17
+ this.root.style.height = "100%";
18
+ this.root.style.position = "absolute";
19
+ this.root.style.left = "-240px";
20
+ this.root.style.top = "0";
21
+ this.root.style.zIndex = "5";
22
+ this.root.style.transition = "left 0.3s ease-in-out";
23
+ this.root.style.overflow = "auto";
24
+ this.root.style.border = "1px solid #ccc";
25
+ this.root.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.1)";
26
+ this.root.style.display = "flex";
27
+ this.root.style.flexDirection = "column";
28
+ this.root.style.justifyContent = "flex-start";
29
+ this.root.style.alignItems = "center";
30
+
31
+ }
32
+
33
+ private onMouseOver = (itemContainer: HTMLDivElement) => {
34
+ itemContainer.style.borderColor = "#ccc";
35
+ }
36
+
37
+ private onMouseOut = (itemContainer: HTMLDivElement) => {
38
+ itemContainer.style.borderColor = "transparent";
39
+ }
40
+
41
+ private onClickHandle = (index: number) => {
42
+ this.emit("pageChange", index);
43
+ }
44
+
45
+ public initialize(slideCount: number, option: SlideApplicationOption): void {
46
+ for (let i = 1; i <= slideCount; i++) {
47
+ const itemContainer = document.createElement("div");
48
+ this.itemList.push(itemContainer);
49
+ itemContainer.style.width = "60%";
50
+ itemContainer.style.display = "flex";
51
+ itemContainer.style.justifyContent = "center";
52
+ itemContainer.style.alignItems = "flex-start";
53
+ itemContainer.style.border = "7px solid transparent";
54
+ itemContainer.style.position = "relative";
55
+ itemContainer.style.borderRadius = "4px"
56
+ itemContainer.style.transition = "border-color .3s";
57
+ itemContainer.style.marginBottom = "10px";
58
+
59
+ itemContainer.addEventListener("mouseover", () => this.onMouseOver(itemContainer))
60
+ itemContainer.addEventListener("mouseout", () => this.onMouseOut(itemContainer))
61
+ itemContainer.addEventListener("click", () => this.onClickHandle(i))
62
+
63
+ const pageIndex = document.createElement("span");
64
+ pageIndex.textContent = `${i}`;
65
+ pageIndex.style.position = "absolute";
66
+ pageIndex.style.top = "1px";
67
+ pageIndex.style.left = "-10px";
68
+ pageIndex.style.transform = "translate(-100%)";
69
+ pageIndex.style.fontSize = "12px";
70
+ pageIndex.style.color = "#5f5f5f";
71
+
72
+ const preview = document.createElement("img");
73
+ preview.style.width = "100%";
74
+ preview.style.display = "inline-block";
75
+ preview.src = `${option.prefix}/${option.taskId}/preview/${i}.png`;
76
+
77
+ itemContainer.appendChild(preview);
78
+ itemContainer.appendChild(pageIndex);
79
+
80
+ this.root.appendChild(itemContainer);
81
+ }
82
+ }
83
+
84
+ public dispose() {
85
+ this.itemList.forEach(item => {
86
+ item.removeEventListener("mouseover", () => this.onMouseOver(item))
87
+ item.removeEventListener("mouseout", () => this.onMouseOut(item))
88
+ })
89
+ }
90
+
91
+ }
package/src/Slide.ts ADDED
@@ -0,0 +1,65 @@
1
+ import EventEmitter from "eventemitter3";
2
+ import { ApplicationInstanceType } from "@netless/forge-room";
3
+ import { ForgeSlidePermissionFlag, ForgeSlidePermissions } from "./ForgeSlidePermession";
4
+
5
+ export interface SlideEvents {
6
+ /**
7
+ * 当用户权限变更时触发
8
+ * @param { string } userId 对应 userId
9
+ * @param { ForgeSlidePermissionFlag[] } flags 当前权限列表
10
+ * @param { number } value 权限值
11
+ */
12
+ permissionChange: (userId: string, flags: ForgeSlidePermissionFlag[], value: number) => void;
13
+
14
+ /**
15
+ * 开始渲染页面时触发
16
+ * @param {number} pageIndex 页面索引
17
+ */
18
+ renderStart: (pageIndex: number) => void;
19
+
20
+ /**
21
+ * 页面渲染完成时触发
22
+ * @param {number} pageIndex 页面索引
23
+ */
24
+ renderEnd: (pageIndex: number) => void;
25
+ }
26
+
27
+
28
+ export class SlideForge extends EventEmitter<SlideEvents> implements ApplicationInstanceType {
29
+
30
+ public readonly view!: HTMLDivElement;
31
+ public readonly permissions!: ForgeSlidePermissions;
32
+ /**
33
+ * 当前页面索引, 从 0 开始
34
+ */
35
+ public readonly pageIndex!: number;
36
+ /**
37
+ * 总页数
38
+ */
39
+ public readonly pageCount!: number;
40
+ /**
41
+ * 切换到参数指定页面, index 从 0 开始
42
+ * @param {number} index 页面索引
43
+ */
44
+ public goto!: (index: number) => Promise<void>
45
+ /**
46
+ * 下一步, 如果已经是本页的最后一步, 则切换到下一页
47
+ */
48
+ public nextStep!: () => Promise<void>
49
+ /**
50
+ * 上一步, 如果已经是本页的第一步, 则切换到上一页
51
+ */
52
+ public prevStep!: () => Promise<void>
53
+ /**
54
+ * 下一页, 如果是最后一页, 则不执行任何操作
55
+ */
56
+ public nextPage!: () => Promise<void>
57
+ /**
58
+ * 上一页, 如果是第一页, 则不执行任何操作
59
+ */
60
+ public prevPage!: () => Promise<void>
61
+ /**
62
+ * 切换侧栏显示状态
63
+ */
64
+ public sideBarToggle!: () => Promise<void>
65
+ }