@netless/fastboard 0.0.4 → 0.0.8
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/index.cjs.js +4 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +415 -105
- package/dist/index.es.js.map +1 -1
- package/dist/svelte.cjs.js +2 -0
- package/dist/svelte.cjs.js.map +1 -0
- package/dist/svelte.es.js +31 -0
- package/dist/svelte.es.js.map +1 -0
- package/dist/vue.cjs.js +2 -0
- package/dist/vue.cjs.js.map +1 -0
- package/dist/vue.es.js +42 -0
- package/dist/vue.es.js.map +1 -0
- package/package.json +23 -2
- package/src/WhiteboardApp.ts +51 -3
- package/src/components/PageControl.tsx +5 -5
- package/src/components/Root.tsx +30 -11
- package/src/hooks.ts +11 -4
- package/src/index.ts +4 -0
- package/src/internal/Instance.tsx +30 -13
- package/src/internal/mount-whiteboard.ts +1 -1
- package/src/svelte.ts +45 -0
- package/src/vue.ts +74 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});exports[Symbol.toStringTag]="Module";var a=require("svelte/store"),n=require("./index.cjs.js");require("@netless/window-manager");require("white-web-sdk");require("i18next");require("react");require("react-dom");require("clsx");require("@tippyjs/react");require("rc-slider");function o(t,i){return a.readable(null,s=>{let e=null;n.createWhiteboardApp(i).then(r=>{s(e=r)});const u=t.subscribe(r=>{r&&e&&e.bindElement(r)});return()=>{u(),e&&e.dispose()}})}exports.useFastboard=o;
|
|
2
|
+
//# sourceMappingURL=svelte.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.cjs.js","sources":["../src/svelte.ts"],"sourcesContent":["import { readable, type Readable } from \"svelte/store\";\nimport { createWhiteboardApp } from \"./index\";\n\nimport type { WhiteboardApp, WhiteboardAppConfig } from \"./WhiteboardApp\";\n\nexport type FastBoardConfig = WhiteboardAppConfig;\n\n/**\n * @example\n * ```svelte\n * <script>\n * let ref = writable(null)\n * let app = useFastboard(ref, { sdkConfig, joinRoom })\n * if (app) {\n * app.insertDocs({...})\n * }\n * </script>\n * <div style=\"width: 100%; height: 100%\" bind:this={$ref} />\n * ```\n */\nexport function useFastboard(\n ref: Readable<HTMLElement | null>,\n config: FastBoardConfig\n): Readable<WhiteboardApp | null> {\n return readable<WhiteboardApp | null>(null, set => {\n let app_: WhiteboardApp | null = null;\n\n createWhiteboardApp(config).then(app => {\n set((app_ = app));\n });\n\n const dispose = ref.subscribe(div => {\n if (div && app_) {\n app_.bindElement(div);\n }\n });\n\n return () => {\n dispose();\n if (app_) {\n app_.dispose();\n }\n };\n });\n}\n"],"names":["readable"],"mappings":"kWAqBE,EACA,EACgC,OACzBA,YAA+B,KAAM,GAAO,IAC7C,GAA6B,2BAEb,GAAQ,KAAK,GAAO,GACjC,EAAO,UAGR,GAAU,EAAI,UAAU,GAAO,CAC/B,GAAO,KACJ,YAAY,WAId,IAAM,KAEP,KACG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readable } from "svelte/store";
|
|
2
|
+
import { createWhiteboardApp } from "./index.es.js";
|
|
3
|
+
import "@netless/window-manager";
|
|
4
|
+
import "white-web-sdk";
|
|
5
|
+
import "i18next";
|
|
6
|
+
import "react";
|
|
7
|
+
import "react-dom";
|
|
8
|
+
import "clsx";
|
|
9
|
+
import "@tippyjs/react";
|
|
10
|
+
import "rc-slider";
|
|
11
|
+
function useFastboard(ref, config) {
|
|
12
|
+
return readable(null, (set) => {
|
|
13
|
+
let app_ = null;
|
|
14
|
+
createWhiteboardApp(config).then((app) => {
|
|
15
|
+
set(app_ = app);
|
|
16
|
+
});
|
|
17
|
+
const dispose = ref.subscribe((div) => {
|
|
18
|
+
if (div && app_) {
|
|
19
|
+
app_.bindElement(div);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return () => {
|
|
23
|
+
dispose();
|
|
24
|
+
if (app_) {
|
|
25
|
+
app_.dispose();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export { useFastboard };
|
|
31
|
+
//# sourceMappingURL=svelte.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.es.js","sources":["../src/svelte.ts"],"sourcesContent":["import { readable, type Readable } from \"svelte/store\";\nimport { createWhiteboardApp } from \"./index\";\n\nimport type { WhiteboardApp, WhiteboardAppConfig } from \"./WhiteboardApp\";\n\nexport type FastBoardConfig = WhiteboardAppConfig;\n\n/**\n * @example\n * ```svelte\n * <script>\n * let ref = writable(null)\n * let app = useFastboard(ref, { sdkConfig, joinRoom })\n * if (app) {\n * app.insertDocs({...})\n * }\n * </script>\n * <div style=\"width: 100%; height: 100%\" bind:this={$ref} />\n * ```\n */\nexport function useFastboard(\n ref: Readable<HTMLElement | null>,\n config: FastBoardConfig\n): Readable<WhiteboardApp | null> {\n return readable<WhiteboardApp | null>(null, set => {\n let app_: WhiteboardApp | null = null;\n\n createWhiteboardApp(config).then(app => {\n set((app_ = app));\n });\n\n const dispose = ref.subscribe(div => {\n if (div && app_) {\n app_.bindElement(div);\n }\n });\n\n return () => {\n dispose();\n if (app_) {\n app_.dispose();\n }\n };\n });\n}\n"],"names":[],"mappings":";;;;;;;;;;sBAqBE,KACA,QACgC;SACzB,SAA+B,MAAM,SAAO;QAC7C,OAA6B;wBAEb,QAAQ,KAAK,SAAO;UACjC,OAAO;AAAA;UAGR,UAAU,IAAI,UAAU,SAAO;UAC/B,OAAO,MAAM;aACV,YAAY;AAAA;AAAA;WAId,MAAM;;UAEP,MAAM;aACH;AAAA;AAAA;AAAA;AAAA;;"}
|
package/dist/vue.cjs.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});exports[Symbol.toStringTag]="Module";var t=require("vue-demi"),s=require("./index.cjs.js");require("@netless/window-manager");require("white-web-sdk");require("i18next");require("react");require("react-dom");require("clsx");require("@tippyjs/react");require("rc-slider");function n(u){var e;const r=t.unref(u);return(e=r==null?void 0:r.$el)!=null?e:r}function o(u){return t.getCurrentScope()?(t.onScopeDispose(u),!0):!1}function l(u,r){const e=t.shallowRef(null),i=t.computed(()=>n(u));return s.createWhiteboardApp(r).then(a=>{e.value=a}),t.watchEffect(()=>{i.value&&e.value&&e.value.bindElement(i.value)}),o(()=>{e.value&&e.value.dispose()}),e}exports.unrefElement=n;exports.useFastboard=l;
|
|
2
|
+
//# sourceMappingURL=vue.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.cjs.js","sources":["../src/vue.ts"],"sourcesContent":["import type { ComponentPublicInstance, Ref } from \"vue-demi\";\nimport {\n computed,\n getCurrentScope,\n onScopeDispose,\n shallowRef,\n unref,\n watchEffect,\n} from \"vue-demi\";\nimport { createWhiteboardApp } from \"./index\";\n\nimport type { WhiteboardApp, WhiteboardAppConfig } from \"./WhiteboardApp\";\n\nexport type FastBoardConfig = WhiteboardAppConfig;\n\nexport type MaybeRef<T> = T | Ref<T>;\nexport type VueInstance = ComponentPublicInstance;\nexport type MaybeElementRef = MaybeRef<\n HTMLElement | SVGElement | VueInstance | undefined | null\n>;\n\n/**\n * Get the dom element of a ref of element or Vue component instance\n */\nexport function unrefElement(elRef: MaybeElementRef) {\n const plain = unref(elRef);\n return (plain as VueInstance)?.$el ?? plain;\n}\n\nfunction tryOnScopeDispose(fn: () => void) {\n if (getCurrentScope()) {\n onScopeDispose(fn);\n return true;\n }\n return false;\n}\n\n/**\n * @example\n * ```vue\n * <script setup>\n * const el = ref(null)\n * const app = useFastboard(el, { sdkConfig, joinRoom })\n * if (app.value) {\n * app.value.insertDocs({...})\n * }\n * </script>\n * <template>\n * <div style=\"width: 100%; height: 100%\" ref=\"el\"></div>\n * </template>\n * ```\n */\nexport function useFastboard(el: MaybeElementRef, config: FastBoardConfig) {\n const app = shallowRef<WhiteboardApp | null>(null);\n const target = computed(() => unrefElement(el));\n\n createWhiteboardApp(config).then(app_ => {\n app.value = app_;\n });\n\n watchEffect(() => {\n if (target.value && app.value) {\n app.value.bindElement(target.value);\n }\n });\n\n tryOnScopeDispose(() => {\n if (app.value) {\n app.value.dispose();\n }\n });\n\n return app;\n}\n"],"names":["unref","getCurrentScope","shallowRef","computed"],"mappings":"8VAwB6B,EAAwB,YAC7C,GAAQA,QAAM,SACZ,oBAAuB,MAAvB,OAA8B,EAGxC,WAA2B,EAAgB,OACrCC,uCACa,GACR,IAEF,cAkBoB,EAAqB,EAAyB,MACnE,GAAMC,aAAiC,MACvC,EAASC,WAAS,IAAM,EAAa,iCAEvB,GAAQ,KAAK,GAAQ,GACnC,MAAQ,kBAGF,IAAM,CACZ,EAAO,OAAS,EAAI,SAClB,MAAM,YAAY,EAAO,WAIf,IAAM,CAClB,EAAI,SACF,MAAM,YAIP"}
|
package/dist/vue.es.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { unref, shallowRef, computed, watchEffect, getCurrentScope, onScopeDispose } from "vue-demi";
|
|
2
|
+
import { createWhiteboardApp } from "./index.es.js";
|
|
3
|
+
import "@netless/window-manager";
|
|
4
|
+
import "white-web-sdk";
|
|
5
|
+
import "i18next";
|
|
6
|
+
import "react";
|
|
7
|
+
import "react-dom";
|
|
8
|
+
import "clsx";
|
|
9
|
+
import "@tippyjs/react";
|
|
10
|
+
import "rc-slider";
|
|
11
|
+
function unrefElement(elRef) {
|
|
12
|
+
var _a;
|
|
13
|
+
const plain = unref(elRef);
|
|
14
|
+
return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;
|
|
15
|
+
}
|
|
16
|
+
function tryOnScopeDispose(fn) {
|
|
17
|
+
if (getCurrentScope()) {
|
|
18
|
+
onScopeDispose(fn);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
function useFastboard(el, config) {
|
|
24
|
+
const app = shallowRef(null);
|
|
25
|
+
const target = computed(() => unrefElement(el));
|
|
26
|
+
createWhiteboardApp(config).then((app_) => {
|
|
27
|
+
app.value = app_;
|
|
28
|
+
});
|
|
29
|
+
watchEffect(() => {
|
|
30
|
+
if (target.value && app.value) {
|
|
31
|
+
app.value.bindElement(target.value);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
tryOnScopeDispose(() => {
|
|
35
|
+
if (app.value) {
|
|
36
|
+
app.value.dispose();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return app;
|
|
40
|
+
}
|
|
41
|
+
export { unrefElement, useFastboard };
|
|
42
|
+
//# sourceMappingURL=vue.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.es.js","sources":["../src/vue.ts"],"sourcesContent":["import type { ComponentPublicInstance, Ref } from \"vue-demi\";\nimport {\n computed,\n getCurrentScope,\n onScopeDispose,\n shallowRef,\n unref,\n watchEffect,\n} from \"vue-demi\";\nimport { createWhiteboardApp } from \"./index\";\n\nimport type { WhiteboardApp, WhiteboardAppConfig } from \"./WhiteboardApp\";\n\nexport type FastBoardConfig = WhiteboardAppConfig;\n\nexport type MaybeRef<T> = T | Ref<T>;\nexport type VueInstance = ComponentPublicInstance;\nexport type MaybeElementRef = MaybeRef<\n HTMLElement | SVGElement | VueInstance | undefined | null\n>;\n\n/**\n * Get the dom element of a ref of element or Vue component instance\n */\nexport function unrefElement(elRef: MaybeElementRef) {\n const plain = unref(elRef);\n return (plain as VueInstance)?.$el ?? plain;\n}\n\nfunction tryOnScopeDispose(fn: () => void) {\n if (getCurrentScope()) {\n onScopeDispose(fn);\n return true;\n }\n return false;\n}\n\n/**\n * @example\n * ```vue\n * <script setup>\n * const el = ref(null)\n * const app = useFastboard(el, { sdkConfig, joinRoom })\n * if (app.value) {\n * app.value.insertDocs({...})\n * }\n * </script>\n * <template>\n * <div style=\"width: 100%; height: 100%\" ref=\"el\"></div>\n * </template>\n * ```\n */\nexport function useFastboard(el: MaybeElementRef, config: FastBoardConfig) {\n const app = shallowRef<WhiteboardApp | null>(null);\n const target = computed(() => unrefElement(el));\n\n createWhiteboardApp(config).then(app_ => {\n app.value = app_;\n });\n\n watchEffect(() => {\n if (target.value && app.value) {\n app.value.bindElement(target.value);\n }\n });\n\n tryOnScopeDispose(() => {\n if (app.value) {\n app.value.dispose();\n }\n });\n\n return app;\n}\n"],"names":[],"mappings":";;;;;;;;;;sBAwB6B,OAAwB;;QAC7C,QAAQ,MAAM;SACZ,qCAAuB,QAAvB,YAA8B;AAAA;AAGxC,2BAA2B,IAAgB;MACrC,mBAAmB;mBACN;WACR;AAAA;SAEF;AAAA;sBAkBoB,IAAqB,QAAyB;QACnE,MAAM,WAAiC;QACvC,SAAS,SAAS,MAAM,aAAa;sBAEvB,QAAQ,KAAK,UAAQ;QACnC,QAAQ;AAAA;cAGF,MAAM;QACZ,OAAO,SAAS,IAAI,OAAO;UACzB,MAAM,YAAY,OAAO;AAAA;AAAA;oBAIf,MAAM;QAClB,IAAI,OAAO;UACT,MAAM;AAAA;AAAA;SAIP;AAAA;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/fastboard",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "An open sourced sdk based on white-web-sdk.",
|
|
5
5
|
"main": "./dist/index.cjs.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
"default": "./dist/index.es.js",
|
|
15
15
|
"types": "./src/index.ts"
|
|
16
16
|
},
|
|
17
|
+
"./vue": {
|
|
18
|
+
"node": {
|
|
19
|
+
"import": "./dist/vue.es.js",
|
|
20
|
+
"require": "./dist/vue.cjs.js"
|
|
21
|
+
},
|
|
22
|
+
"default": "./dist/vue.es.js",
|
|
23
|
+
"types": "./src/vue.ts"
|
|
24
|
+
},
|
|
17
25
|
"./package.json": "./package.json"
|
|
18
26
|
},
|
|
19
27
|
"files": [
|
|
@@ -37,8 +45,11 @@
|
|
|
37
45
|
"@netless/app-monaco": "*",
|
|
38
46
|
"@netless/app-slide": "*",
|
|
39
47
|
"@netless/window-manager": "*",
|
|
48
|
+
"@vue/composition-api": "*",
|
|
40
49
|
"react": "*",
|
|
41
50
|
"react-dom": "*",
|
|
51
|
+
"svelte": "*",
|
|
52
|
+
"vue": "*",
|
|
42
53
|
"white-web-sdk": "*"
|
|
43
54
|
},
|
|
44
55
|
"peerDependenciesMeta": {
|
|
@@ -53,6 +64,15 @@
|
|
|
53
64
|
},
|
|
54
65
|
"@netless/app-geogebra": {
|
|
55
66
|
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"@vue/composition-api": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"svelte": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"vue": {
|
|
75
|
+
"optional": true
|
|
56
76
|
}
|
|
57
77
|
},
|
|
58
78
|
"dependencies": {
|
|
@@ -60,7 +80,8 @@
|
|
|
60
80
|
"clsx": "^1.1.1",
|
|
61
81
|
"i18next": "^21.6.5",
|
|
62
82
|
"rc-slider": "^9.7.5",
|
|
63
|
-
"tippy.js": "^6.3.7"
|
|
83
|
+
"tippy.js": "^6.3.7",
|
|
84
|
+
"vue-demi": "^0.12.1"
|
|
64
85
|
},
|
|
65
86
|
"readme": "# @netless/fastboard\n\nA whiteboard starter, based on [white-web-sdk](https://www.npmjs.com/package/white-web-sdk), [@netless/window-manager](https://www.npmjs.com/package/@netless/window-manager)\nand [netless-app](https://github.com/netless-io/netless-app).\n\n## 目录\n\n- [安装](#install)\n- [使用](#usage)\n- [进阶](./docs/advanced.md)\n- [开发](#开发)\n\n<h2 id=\"install\">安装</h2>\n\n```bash\nnpm add @netless/fastboard @netless/window-manager white-web-sdk react react-dom\n```\n\n<h2 id=\"usage\">使用</h2>\n\n<h3 id=\"mount-whiteboard\">挂载白板</h3>\n\n> 原生 `javascript`\n\n```js\nimport { createWhiteboardApp } from \"@netless/fastboard\";\n\nlet whiteboard = await createWhiteboardApp({\n target: document.getElementById(\"whiteboard\"),\n // [1]\n sdkConfig: {\n appIdentifier: \"whiteboard-appid\",\n },\n // [2]\n joinRoom: {\n uid: \"unique_id_for_each_client\",\n uuid: \"room-uuid\",\n roomToken: \"NETLESSROOM_...\",\n },\n // [3]\n managerConfig: {\n cursor: true,\n },\n});\n```\n\n> 使用 `React`\n\n```typescript\nimport { useFastboard, FastBoardConfig } from \"@netless/fastboard\";\nimport ReactDOM from \"react-dom\";\n\nconst config: FastBoardConfig = {\n sdkConfig: {\n appIdentifier: \"whiteboard-appid\",\n },\n joinRoom: {\n uid: \"unique_id_for_each_client\",\n uuid: \"room-uuid\",\n roomToken: \"NETLESSROOM_...\",\n },\n};\n\nfunction App() {\n const [app, ref] = useFastboard(config);\n return <div ref={ref} />;\n}\nReactDOM.render(<App />, document.getElementById(\"root\"));\n```\n\n[1] 关于 SDK 更多配置请看 [构造 WhiteWebSDK](https://developer.netless.link/javascript-zh/home/construct-white-web-sdk)\n\n[2] 加入房间更多配置请看 [构造 Room 与 Player 对象](https://developer.netless.link/javascript-zh/home/construct-room-and-player)\n\n[3] 配置 `WindowManager` 请看 [WindowManager](https://github.com/netless-io/window-manager#mount)\n\n### 使用 APPS\n\n**注意:** 需要先安装对应的 APP\n\n```bash\nnpm add @netless/app-slide\n```\n\n```typescript\n// 插入动态 PPTX 至白板\nconst appId = await whiteboard.insertDocs({\n fileType: \"pptx\",\n params: {\n scenePath: `/ppt/${uuid}`, // [1]\n title: \"a.pptx\",\n taskId: \"1234567...\", // [2]\n url: \"https://convertcdn.netless.link/dynamicConvert\", // [3]\n },\n});\n\n// 插入 PDF/静态 PPT 至白板\nconst appId = await whiteboard.insertDocs({\n fileType: \"pdf\", // or ppt\n options: {\n scenePath: `/pdf${uuid}`,\n title: \"a.pdf\", // 可选\n scenes: [], // SceneDefinition[] 静态/动态 Scene 数据\n },\n});\n\n// 插入音频/视频至白板\nconst appId = await whiteboard.manager.addApp({\n kind: \"MediaPlayer\",\n options: {\n title: \"test.mp3\", // 可选\n },\n attributes: {\n src: \"xxxx\", // 音视频 url\n },\n});\n```\n\n更多 `app` 请看 [netless-app](#https://github.com/netless-io/netless-app)\n\n## Develop\n\n```bash\npnpm i\n# upgrade dependencies\npnpm up -Li\n# build and see bundle size\npnpm build\nopen node_modules/.visualizer/stats.html\n```\n\n## License\n\nMIT @ [netless](https://github.com/netless-io)\n"
|
|
66
87
|
}
|
package/src/WhiteboardApp.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
InsertDocsParams,
|
|
3
3
|
Language,
|
|
4
|
+
Layout,
|
|
4
5
|
WhiteboardAppConfig,
|
|
5
6
|
} from "./internal";
|
|
6
7
|
import { Instance } from "./internal";
|
|
7
8
|
|
|
8
|
-
export type { WhiteboardAppConfig, InsertDocsParams };
|
|
9
|
+
export type { Language, Layout, WhiteboardAppConfig, InsertDocsParams };
|
|
9
10
|
|
|
10
11
|
export class WhiteboardApp {
|
|
11
12
|
private readonly _instance: Instance;
|
|
@@ -14,14 +15,61 @@ export class WhiteboardApp {
|
|
|
14
15
|
this._instance = new Instance(config);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
this._instance.
|
|
18
|
+
get room() {
|
|
19
|
+
return this._instance.room;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get manager() {
|
|
23
|
+
return this._instance.manager;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get sdk() {
|
|
27
|
+
return this._instance.sdk;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get i18n() {
|
|
31
|
+
return this._instance.i18n;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public get target(): HTMLElement | null {
|
|
35
|
+
return this._instance.target;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public get collector(): HTMLElement | null {
|
|
39
|
+
return this._instance.collector;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public bindElement(
|
|
43
|
+
target?: HTMLElement | null,
|
|
44
|
+
collector?: HTMLElement | null
|
|
45
|
+
) {
|
|
46
|
+
this._instance.bindElement(target || null, collector || null);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public get layout() {
|
|
50
|
+
return this._instance.config.layout;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public updateLayout(layout?: Layout | undefined) {
|
|
54
|
+
this._instance.updateLayout(layout);
|
|
19
55
|
}
|
|
20
56
|
|
|
21
57
|
public insertDocs(params: InsertDocsParams) {
|
|
22
58
|
return this._instance.insertDocs(params);
|
|
23
59
|
}
|
|
24
60
|
|
|
61
|
+
public insertCodeEditor() {
|
|
62
|
+
return this._instance.insertCodeEditor();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public insertGeoGebra() {
|
|
66
|
+
return this._instance.insertGeoGebra();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public insertCountdown() {
|
|
70
|
+
return this._instance.insertCountdown();
|
|
71
|
+
}
|
|
72
|
+
|
|
25
73
|
public changeLanguage(language: Language) {
|
|
26
74
|
return this._instance.changeLanguage(language);
|
|
27
75
|
}
|
|
@@ -36,14 +36,14 @@ export function PageControl({
|
|
|
36
36
|
if (manager && room) {
|
|
37
37
|
await manager.switchMainViewToWriter();
|
|
38
38
|
const path = room.state.sceneState.contextPath;
|
|
39
|
-
room.putScenes(path, [{}],
|
|
40
|
-
await manager.setMainViewSceneIndex(pageIndex);
|
|
39
|
+
room.putScenes(path, [{}], pageIndex + 1);
|
|
40
|
+
await manager.setMainViewSceneIndex(pageIndex + 1);
|
|
41
41
|
} else if (!manager && room) {
|
|
42
42
|
const path = room.state.sceneState.contextPath;
|
|
43
|
-
room.putScenes(path, [{}],
|
|
44
|
-
room.setSceneIndex(pageIndex);
|
|
43
|
+
room.putScenes(path, [{}], pageIndex + 1);
|
|
44
|
+
room.setSceneIndex(pageIndex + 1);
|
|
45
45
|
}
|
|
46
|
-
}, [room, manager,
|
|
46
|
+
}, [room, manager, pageIndex]);
|
|
47
47
|
|
|
48
48
|
const prevPage = useCallback(() => {
|
|
49
49
|
if (room?.isWritable) {
|
package/src/components/Root.tsx
CHANGED
|
@@ -10,7 +10,7 @@ export interface RootProps {
|
|
|
10
10
|
instance: Instance;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export
|
|
13
|
+
export function Root({ instance: app }: RootProps) {
|
|
14
14
|
const [mux] = useState(() => new Lock());
|
|
15
15
|
|
|
16
16
|
const useWhiteboard = useCallback(
|
|
@@ -21,21 +21,40 @@ export default function Root({ instance: app }: RootProps) {
|
|
|
21
21
|
[app, mux]
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
+
const {
|
|
25
|
+
Toolbar: toolbar = true,
|
|
26
|
+
RedoUndo: redo_undo = true,
|
|
27
|
+
ZoomControl: zoom_control = true,
|
|
28
|
+
PageControl: page_control = true,
|
|
29
|
+
} = app.config.layout || {};
|
|
30
|
+
|
|
31
|
+
const props = {
|
|
32
|
+
room: app.room,
|
|
33
|
+
manager: app.manager,
|
|
34
|
+
i18n: app.i18n,
|
|
35
|
+
};
|
|
36
|
+
|
|
24
37
|
return (
|
|
25
38
|
<Instance.Context.Provider value={app}>
|
|
26
39
|
<div className="fastboard-root">
|
|
27
40
|
{!app.room && <div className="fastboard-loading">Loading…</div>}
|
|
28
41
|
<div className="fastboard-view" ref={useWhiteboard} />
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
{toolbar && (
|
|
43
|
+
<div className="fastboard-left">
|
|
44
|
+
<Toolbar {...props} />
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
{(redo_undo || zoom_control) && (
|
|
48
|
+
<div className="fastboard-bottom-left">
|
|
49
|
+
{redo_undo && <RedoUndo {...props} />}
|
|
50
|
+
{zoom_control && <ZoomControl {...props} />}
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
{page_control && (
|
|
54
|
+
<div className="fastboard-bottom-right">
|
|
55
|
+
<PageControl {...props} />
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
39
58
|
</div>
|
|
40
59
|
</Instance.Context.Provider>
|
|
41
60
|
);
|
package/src/hooks.ts
CHANGED
|
@@ -15,13 +15,16 @@ export type FastBoardConfig = WhiteboardAppConfig;
|
|
|
15
15
|
*/
|
|
16
16
|
export function useFastboard(config: FastBoardConfig): readonly [
|
|
17
17
|
app: WhiteboardApp | null,
|
|
18
|
-
ref: (div: HTMLDivElement | null) => void
|
|
18
|
+
ref: (div: HTMLDivElement | null) => void,
|
|
19
|
+
collectorRef: (div: HTMLDivElement | null) => void
|
|
19
20
|
] & {
|
|
20
21
|
readonly app: WhiteboardApp | null;
|
|
21
22
|
readonly ref: (div: HTMLDivElement | null) => void;
|
|
23
|
+
readonly collectorRef: (div: HTMLDivElement | null) => void;
|
|
22
24
|
} {
|
|
23
25
|
const [app, setApp] = useState<WhiteboardApp | null>(null);
|
|
24
26
|
const [currentTarget, ref] = useState<HTMLDivElement | null>(null);
|
|
27
|
+
const [collector, collectorRef] = useState<HTMLDivElement | null>(null);
|
|
25
28
|
|
|
26
29
|
useEffect(() => {
|
|
27
30
|
let isMounted = true;
|
|
@@ -38,9 +41,13 @@ export function useFastboard(config: FastBoardConfig): readonly [
|
|
|
38
41
|
|
|
39
42
|
useEffect(() => {
|
|
40
43
|
if (app) {
|
|
41
|
-
app.bindElement(currentTarget);
|
|
44
|
+
app.bindElement(currentTarget, collector);
|
|
42
45
|
}
|
|
43
|
-
}, [app, currentTarget]);
|
|
46
|
+
}, [app, collector, currentTarget]);
|
|
44
47
|
|
|
45
|
-
return Object.assign([app, ref] as const, {
|
|
48
|
+
return Object.assign([app, ref, collectorRef] as const, {
|
|
49
|
+
app,
|
|
50
|
+
ref,
|
|
51
|
+
collectorRef,
|
|
52
|
+
});
|
|
46
53
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,10 @@ export { PageControl, type PageControlProps } from "./components/PageControl";
|
|
|
10
10
|
export { RedoUndo, type RedoUndoProps } from "./components/RedoUndo";
|
|
11
11
|
export { Toolbar, type ToolbarProps } from "./components/Toolbar";
|
|
12
12
|
export { ZoomControl, type ZoomControlProps } from "./components/ZoomControl";
|
|
13
|
+
export {
|
|
14
|
+
PlayerControl,
|
|
15
|
+
type PlayerControlProps,
|
|
16
|
+
} from "./components/PlayerControl";
|
|
13
17
|
export * from "./WhiteboardApp";
|
|
14
18
|
export * from "./hooks";
|
|
15
19
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Mutable } from "type-fest";
|
|
1
2
|
import type { WindowManager } from "@netless/window-manager";
|
|
2
3
|
import type { Room, SceneDefinition, WhiteWebSdk } from "white-web-sdk";
|
|
3
4
|
import type { JoinRoom, ManagerConfig, SdkConfig } from "./mount-whiteboard";
|
|
@@ -6,7 +7,7 @@ import type { i18n } from "i18next";
|
|
|
6
7
|
import React, { createContext, useContext } from "react";
|
|
7
8
|
import ReactDOM from "react-dom";
|
|
8
9
|
|
|
9
|
-
import Root from "../components/Root";
|
|
10
|
+
import { Root } from "../components/Root";
|
|
10
11
|
import { mountWhiteboard } from "./mount-whiteboard";
|
|
11
12
|
import { noop } from "./helpers";
|
|
12
13
|
|
|
@@ -36,10 +37,18 @@ export type InsertDocsParams = InsertDocsStatic | InsertDocsDynamic;
|
|
|
36
37
|
|
|
37
38
|
export type Language = "zh-CN" | "en-US";
|
|
38
39
|
|
|
40
|
+
export interface Layout {
|
|
41
|
+
Toolbar?: boolean;
|
|
42
|
+
PageControl?: boolean;
|
|
43
|
+
RedoUndo?: boolean;
|
|
44
|
+
ZoomControl?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
export interface WhiteboardAppConfig {
|
|
40
48
|
readonly sdkConfig: SdkConfig;
|
|
41
49
|
readonly joinRoom: JoinRoom;
|
|
42
50
|
readonly managerConfig?: Omit<ManagerConfig, "container">;
|
|
51
|
+
readonly layout?: Layout;
|
|
43
52
|
readonly toolbar?: {
|
|
44
53
|
apps?: {
|
|
45
54
|
enable?: boolean;
|
|
@@ -60,7 +69,7 @@ export interface Essentials {
|
|
|
60
69
|
export class Instance {
|
|
61
70
|
static readonly Context = createContext<Instance | null>(null);
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
config: Mutable<WhiteboardAppConfig>;
|
|
64
73
|
|
|
65
74
|
sdk: WhiteWebSdk | null = null;
|
|
66
75
|
room: Room | null = null;
|
|
@@ -82,13 +91,11 @@ export class Instance {
|
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
constructor(config: WhiteboardAppConfig) {
|
|
85
|
-
this.config = config;
|
|
94
|
+
this.config = { ...config };
|
|
86
95
|
this.refreshReadyPromise();
|
|
87
96
|
this.initialize();
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
private _target: HTMLElement | null = null;
|
|
91
|
-
|
|
92
99
|
async initialize() {
|
|
93
100
|
const essentials = await mountWhiteboard(
|
|
94
101
|
this.config.sdkConfig,
|
|
@@ -100,15 +107,20 @@ export class Instance {
|
|
|
100
107
|
this.resolveReady();
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
110
|
+
target: HTMLElement | null = null;
|
|
111
|
+
collector: HTMLElement | null = null;
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
if (this.
|
|
109
|
-
ReactDOM.unmountComponentAtNode(this.
|
|
113
|
+
bindElement(target: HTMLElement | null, collector: HTMLElement | null) {
|
|
114
|
+
if (this.target && target) {
|
|
115
|
+
ReactDOM.unmountComponentAtNode(this.target);
|
|
110
116
|
}
|
|
111
|
-
this.
|
|
117
|
+
this.target = target;
|
|
118
|
+
this.collector = collector;
|
|
119
|
+
this.forceUpdate();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
updateLayout(layout: Layout | undefined) {
|
|
123
|
+
this.config.layout = layout;
|
|
112
124
|
this.forceUpdate();
|
|
113
125
|
}
|
|
114
126
|
|
|
@@ -139,7 +151,12 @@ export class Instance {
|
|
|
139
151
|
|
|
140
152
|
async mount(node: HTMLElement) {
|
|
141
153
|
await this.readyPromise;
|
|
142
|
-
if (this.manager) {
|
|
154
|
+
if (!this.manager) {
|
|
155
|
+
throw new Error(`[WhiteboardApp] mounted, but not found window manager`);
|
|
156
|
+
}
|
|
157
|
+
if (this.collector) {
|
|
158
|
+
this.manager.bindContainer(node, this.collector);
|
|
159
|
+
} else {
|
|
143
160
|
this.manager.bindContainer(node);
|
|
144
161
|
}
|
|
145
162
|
}
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
RoomCallbacks,
|
|
5
5
|
WhiteWebSdkConfiguration,
|
|
6
6
|
} from "white-web-sdk";
|
|
7
|
-
import type { Essentials, Language } from "./
|
|
7
|
+
import type { Essentials, Language } from "./Instance";
|
|
8
8
|
|
|
9
9
|
import { WindowManager } from "@netless/window-manager";
|
|
10
10
|
import { DefaultHotKeys, WhiteWebSdk } from "white-web-sdk";
|
package/src/svelte.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readable, type Readable } from "svelte/store";
|
|
2
|
+
import { createWhiteboardApp } from "./index";
|
|
3
|
+
|
|
4
|
+
import type { WhiteboardApp, WhiteboardAppConfig } from "./WhiteboardApp";
|
|
5
|
+
|
|
6
|
+
export type FastBoardConfig = WhiteboardAppConfig;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @example
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <script>
|
|
12
|
+
* let ref = writable(null)
|
|
13
|
+
* let app = useFastboard(ref, { sdkConfig, joinRoom })
|
|
14
|
+
* if (app) {
|
|
15
|
+
* app.insertDocs({...})
|
|
16
|
+
* }
|
|
17
|
+
* </script>
|
|
18
|
+
* <div style="width: 100%; height: 100%" bind:this={$ref} />
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function useFastboard(
|
|
22
|
+
ref: Readable<HTMLElement | null>,
|
|
23
|
+
config: FastBoardConfig
|
|
24
|
+
): Readable<WhiteboardApp | null> {
|
|
25
|
+
return readable<WhiteboardApp | null>(null, set => {
|
|
26
|
+
let app_: WhiteboardApp | null = null;
|
|
27
|
+
|
|
28
|
+
createWhiteboardApp(config).then(app => {
|
|
29
|
+
set((app_ = app));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const dispose = ref.subscribe(div => {
|
|
33
|
+
if (div && app_) {
|
|
34
|
+
app_.bindElement(div);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
dispose();
|
|
40
|
+
if (app_) {
|
|
41
|
+
app_.dispose();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
package/src/vue.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ComponentPublicInstance, Ref } from "vue-demi";
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
getCurrentScope,
|
|
5
|
+
onScopeDispose,
|
|
6
|
+
shallowRef,
|
|
7
|
+
unref,
|
|
8
|
+
watchEffect,
|
|
9
|
+
} from "vue-demi";
|
|
10
|
+
import { createWhiteboardApp } from "./index";
|
|
11
|
+
|
|
12
|
+
import type { WhiteboardApp, WhiteboardAppConfig } from "./WhiteboardApp";
|
|
13
|
+
|
|
14
|
+
export type FastBoardConfig = WhiteboardAppConfig;
|
|
15
|
+
|
|
16
|
+
export type MaybeRef<T> = T | Ref<T>;
|
|
17
|
+
export type VueInstance = ComponentPublicInstance;
|
|
18
|
+
export type MaybeElementRef = MaybeRef<
|
|
19
|
+
HTMLElement | SVGElement | VueInstance | undefined | null
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the dom element of a ref of element or Vue component instance
|
|
24
|
+
*/
|
|
25
|
+
export function unrefElement(elRef: MaybeElementRef) {
|
|
26
|
+
const plain = unref(elRef);
|
|
27
|
+
return (plain as VueInstance)?.$el ?? plain;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function tryOnScopeDispose(fn: () => void) {
|
|
31
|
+
if (getCurrentScope()) {
|
|
32
|
+
onScopeDispose(fn);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @example
|
|
40
|
+
* ```vue
|
|
41
|
+
* <script setup>
|
|
42
|
+
* const el = ref(null)
|
|
43
|
+
* const app = useFastboard(el, { sdkConfig, joinRoom })
|
|
44
|
+
* if (app.value) {
|
|
45
|
+
* app.value.insertDocs({...})
|
|
46
|
+
* }
|
|
47
|
+
* </script>
|
|
48
|
+
* <template>
|
|
49
|
+
* <div style="width: 100%; height: 100%" ref="el"></div>
|
|
50
|
+
* </template>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function useFastboard(el: MaybeElementRef, config: FastBoardConfig) {
|
|
54
|
+
const app = shallowRef<WhiteboardApp | null>(null);
|
|
55
|
+
const target = computed(() => unrefElement(el));
|
|
56
|
+
|
|
57
|
+
createWhiteboardApp(config).then(app_ => {
|
|
58
|
+
app.value = app_;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
watchEffect(() => {
|
|
62
|
+
if (target.value && app.value) {
|
|
63
|
+
app.value.bindElement(target.value);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
tryOnScopeDispose(() => {
|
|
68
|
+
if (app.value) {
|
|
69
|
+
app.value.dispose();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return app;
|
|
74
|
+
}
|