@netless/fastboard 0.0.10 → 0.0.11

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.
Files changed (43) hide show
  1. package/README.md +11 -1
  2. package/dist/index.cjs.js +4 -4
  3. package/dist/index.cjs.js.map +1 -1
  4. package/dist/index.es.js +527 -365
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/svelte.cjs.js +1 -1
  7. package/dist/svelte.cjs.js.map +1 -1
  8. package/dist/svelte.es.js +1 -0
  9. package/dist/svelte.es.js.map +1 -1
  10. package/dist/vue.cjs.js +1 -1
  11. package/dist/vue.cjs.js.map +1 -1
  12. package/dist/vue.es.js +1 -0
  13. package/dist/vue.es.js.map +1 -1
  14. package/package.json +3 -2
  15. package/src/components/{PageControl.scss → PageControl/PageControl.scss} +0 -0
  16. package/src/components/PageControl/PageControl.tsx +110 -0
  17. package/src/components/PageControl/hooks.ts +70 -0
  18. package/src/components/PageControl/index.ts +2 -0
  19. package/src/components/PlayerControl/PlayerControl.tsx +7 -8
  20. package/src/components/PlayerControl/hooks.ts +1 -1
  21. package/src/components/PlayerControl/index.ts +1 -0
  22. package/src/components/{RedoUndo.scss → RedoUndo/RedoUndo.scss} +0 -0
  23. package/src/components/{RedoUndo.tsx → RedoUndo/RedoUndo.tsx} +13 -29
  24. package/src/components/RedoUndo/hooks.ts +50 -0
  25. package/src/components/RedoUndo/index.ts +2 -0
  26. package/src/components/Root.tsx +10 -6
  27. package/src/components/Toolbar/Content.tsx +4 -3
  28. package/src/components/Toolbar/Toolbar.scss +35 -1
  29. package/src/components/Toolbar/Toolbar.tsx +78 -28
  30. package/src/components/Toolbar/components/Mask.tsx +44 -0
  31. package/src/components/Toolbar/components/assets/collapsed.png +0 -0
  32. package/src/components/Toolbar/components/assets/expanded.png +0 -0
  33. package/src/components/Toolbar/hooks.ts +28 -29
  34. package/src/components/Toolbar/index.ts +1 -0
  35. package/src/components/{ZoomControl.scss → ZoomControl/ZoomControl.scss} +0 -0
  36. package/src/components/ZoomControl/ZoomControl.tsx +109 -0
  37. package/src/components/ZoomControl/hooks.ts +111 -0
  38. package/src/components/ZoomControl/index.ts +2 -0
  39. package/src/components/hooks.ts +80 -0
  40. package/src/index.ts +19 -4
  41. package/src/style.scss +3 -3
  42. package/src/components/PageControl.tsx +0 -181
  43. package/src/components/ZoomControl.tsx +0 -221
@@ -1,2 +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;
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("framer-motion");require("clsx");require("@tippyjs/react");require("rc-slider");function o(i,t){return a.readable(null,s=>{let e=null;n.createWhiteboardApp(t).then(r=>{s(e=r)});const u=i.subscribe(r=>{r&&e&&e.bindElement(r)});return()=>{u(),e&&e.dispose()}})}exports.useFastboard=o;
2
2
  //# sourceMappingURL=svelte.cjs.js.map
@@ -1 +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"}
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":"2XAqBE,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"}
package/dist/svelte.es.js CHANGED
@@ -5,6 +5,7 @@ import "white-web-sdk";
5
5
  import "i18next";
6
6
  import "react";
7
7
  import "react-dom";
8
+ import "framer-motion";
8
9
  import "clsx";
9
10
  import "@tippyjs/react";
10
11
  import "rc-slider";
@@ -1 +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;;"}
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 CHANGED
@@ -1,2 +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;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});exports[Symbol.toStringTag]="Module";var t=require("vue-demi"),o=require("./index.cjs.js");require("@netless/window-manager");require("white-web-sdk");require("i18next");require("react");require("react-dom");require("framer-motion");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 s(u){return t.getCurrentScope()?(t.onScopeDispose(u),!0):!1}function l(u,r){const e=t.shallowRef(null),i=t.computed(()=>n(u));return o.createWhiteboardApp(r).then(a=>{e.value=a}),t.watchEffect(()=>{i.value&&e.value&&e.value.bindElement(i.value)}),s(()=>{e.value&&e.value.dispose()}),e}exports.unrefElement=n;exports.useFastboard=l;
2
2
  //# sourceMappingURL=vue.cjs.js.map
@@ -1 +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"}
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":"uXAwB6B,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 CHANGED
@@ -5,6 +5,7 @@ import "white-web-sdk";
5
5
  import "i18next";
6
6
  import "react";
7
7
  import "react-dom";
8
+ import "framer-motion";
8
9
  import "clsx";
9
10
  import "@tippyjs/react";
10
11
  import "rc-slider";
@@ -1 +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;;"}
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.10",
3
+ "version": "0.0.11",
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",
@@ -86,10 +86,11 @@
86
86
  "dependencies": {
87
87
  "@tippyjs/react": "^4.2.6",
88
88
  "clsx": "^1.1.1",
89
+ "framer-motion": "^5.5.6",
89
90
  "i18next": "^21.6.5",
90
91
  "rc-slider": "^9.7.5",
91
92
  "tippy.js": "^6.3.7",
92
93
  "vue-demi": "^0.12.1"
93
94
  },
94
- "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 app = 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```jsx\nimport { createWhiteboardApp, Fastboard } from \"@netless/fastboard\";\nimport ReactDOM from \"react-dom\";\n\nlet app = await createWhiteboardApp({\n /* ... */\n});\n\nfunction App() {\n return <Fastboard app={app} />;\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 app.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 app.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 app.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"
95
+ "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 app = await createWhiteboardApp({\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\napp.bindElement(document.getElementById(\"whiteboard\"));\n```\n\n> 使用 `React`\n\n```jsx\nimport { createWhiteboardApp, Fastboard } from \"@netless/fastboard\";\nimport ReactDOM from \"react-dom\";\n\nlet app = await createWhiteboardApp({\n /* ... */\n});\n\nfunction App() {\n return <Fastboard app={app} />;\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关于窗口最小化后显示的小图标,可以通过 CSS 覆盖样式的方式修改它的位置:\n\n```css\n.telebox-collector {\n right: 20px;\n bottom: 40px;\n}\n```\n\n### 使用 APPS\n\n**注意:** 需要先安装对应的 APP\n\n```bash\nnpm add @netless/app-slide\n```\n\n```typescript\n// 插入动态 PPTX 至白板\nconst appId = await app.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 app.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 app.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"
95
96
  }
@@ -0,0 +1,110 @@
1
+ import type { CommonProps, GenericIcon } from "../../types";
2
+
3
+ import clsx from "clsx";
4
+ import React from "react";
5
+ import Tippy from "@tippyjs/react";
6
+
7
+ import { TopOffset } from "../../theme";
8
+ import { Icon } from "../../icons";
9
+ import { FilePlus } from "../../icons/FilePlus";
10
+ import { ChevronLeft } from "../../icons/ChevronLeft";
11
+ import { ChevronRight } from "../../icons/ChevronRight";
12
+ import { useWritable } from "../hooks";
13
+ import { usePageControl } from "./hooks";
14
+
15
+ export const name = "fastboard-page-control";
16
+
17
+ export type PageControlProps = CommonProps &
18
+ GenericIcon<"add" | "prev" | "next">;
19
+
20
+ export function PageControl({
21
+ room,
22
+ manager,
23
+ theme = "light",
24
+ addIcon,
25
+ addIconDisable,
26
+ prevIcon,
27
+ prevIconDisable,
28
+ nextIcon,
29
+ nextIconDisable,
30
+ i18n,
31
+ }: PageControlProps) {
32
+ const writable = useWritable(room);
33
+ const { pageIndex, pageCount, ...actions } = usePageControl(room, manager);
34
+
35
+ const disabled = !writable;
36
+
37
+ return (
38
+ <div className={clsx(name, theme)}>
39
+ {/* <span className={clsx(`${name}-cut-line`, theme)} />{" "} */}
40
+ <Tippy
41
+ className="fastboard-tip"
42
+ content={i18n?.t("prevPage")}
43
+ theme={theme}
44
+ disabled={disabled}
45
+ placement="top"
46
+ duration={300}
47
+ offset={TopOffset}
48
+ >
49
+ <button
50
+ className={clsx(`${name}-btn`, "prev", theme)}
51
+ disabled={disabled || pageIndex === 0}
52
+ onClick={actions.prevPage}
53
+ >
54
+ <Icon
55
+ fallback={<ChevronLeft theme={theme} />}
56
+ src={disabled ? prevIconDisable : prevIcon}
57
+ alt="[prev]"
58
+ />
59
+ </button>
60
+ </Tippy>
61
+ <span className={clsx(`${name}-page`, theme)}>
62
+ {pageCount === 0 ? "\u2026" : pageIndex + 1}
63
+ </span>
64
+ <span className={clsx(`${name}-slash`, theme)}>/</span>
65
+ <span className={clsx(`${name}-page-count`, theme)}>{pageCount}</span>
66
+ <Tippy
67
+ className="fastboard-tip"
68
+ content={i18n?.t("nextPage")}
69
+ theme={theme}
70
+ disabled={disabled}
71
+ placement="top"
72
+ duration={300}
73
+ offset={TopOffset}
74
+ >
75
+ <button
76
+ className={clsx(`${name}-btn`, "next", theme)}
77
+ disabled={disabled || pageIndex === pageCount - 1}
78
+ onClick={actions.nextPage}
79
+ >
80
+ <Icon
81
+ fallback={<ChevronRight theme={theme} />}
82
+ src={disabled ? nextIconDisable : nextIcon}
83
+ alt="[next]"
84
+ />
85
+ </button>
86
+ </Tippy>
87
+ <Tippy
88
+ className="fastboard-tip"
89
+ content={i18n?.t("addPage")}
90
+ theme={theme}
91
+ disabled={disabled}
92
+ placement="top"
93
+ duration={300}
94
+ offset={TopOffset}
95
+ >
96
+ <button
97
+ className={clsx(`${name}-btn`, "add", theme)}
98
+ disabled={disabled}
99
+ onClick={actions.addPage}
100
+ >
101
+ <Icon
102
+ fallback={<FilePlus theme={theme} />}
103
+ src={disabled ? addIconDisable : addIcon}
104
+ alt="[add]"
105
+ />
106
+ </button>
107
+ </Tippy>
108
+ </div>
109
+ );
110
+ }
@@ -0,0 +1,70 @@
1
+ import type { Room, RoomState } from "white-web-sdk";
2
+ import type { WindowManager } from "@netless/window-manager";
3
+ import { useCallback, useEffect, useState } from "react";
4
+
5
+ export function usePageControl(
6
+ room?: Room | null,
7
+ manager?: WindowManager | null
8
+ ) {
9
+ const [pageIndex, setPageIndex] = useState(0);
10
+ const [pageCount, setPageCount] = useState(0);
11
+
12
+ const addPage = useCallback(async () => {
13
+ if (manager && room) {
14
+ await manager.switchMainViewToWriter();
15
+ const path = room.state.sceneState.contextPath;
16
+ room.putScenes(path, [{}], pageIndex + 1);
17
+ await manager.setMainViewSceneIndex(pageIndex + 1);
18
+ } else if (!manager && room) {
19
+ const path = room.state.sceneState.contextPath;
20
+ room.putScenes(path, [{}], pageIndex + 1);
21
+ room.setSceneIndex(pageIndex + 1);
22
+ }
23
+ }, [room, manager, pageIndex]);
24
+
25
+ const prevPage = useCallback(() => {
26
+ if (manager) {
27
+ manager.setMainViewSceneIndex(pageIndex - 1);
28
+ } else if (room) {
29
+ room.pptPreviousStep();
30
+ }
31
+ }, [room, manager, pageIndex]);
32
+
33
+ const nextPage = useCallback(() => {
34
+ if (manager) {
35
+ manager.setMainViewSceneIndex(pageIndex + 1);
36
+ } else if (room) {
37
+ room.pptNextStep();
38
+ }
39
+ }, [room, manager, pageIndex]);
40
+
41
+ useEffect(() => {
42
+ if (room) {
43
+ setPageIndex(room.state.sceneState.index);
44
+ setPageCount(room.state.sceneState.scenes.length);
45
+
46
+ if (manager) {
47
+ manager.emitter.on("mainViewSceneIndexChange", setPageIndex);
48
+
49
+ return () => {
50
+ manager.emitter.off("mainViewSceneIndexChange", setPageIndex);
51
+ };
52
+ } else {
53
+ const onRoomStateChanged = (modifyState: Partial<RoomState>) => {
54
+ if (modifyState.sceneState) {
55
+ setPageIndex(modifyState.sceneState.index);
56
+ setPageCount(modifyState.sceneState.scenes.length);
57
+ }
58
+ };
59
+
60
+ room.callbacks.on("onRoomStateChanged", onRoomStateChanged);
61
+
62
+ return () => {
63
+ room.callbacks.off("onRoomStateChanged", onRoomStateChanged);
64
+ };
65
+ }
66
+ }
67
+ }, [room, manager]);
68
+
69
+ return { pageIndex, pageCount, prevPage, nextPage, addPage };
70
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./hooks";
2
+ export { name, PageControl, type PageControlProps } from "./PageControl";
@@ -1,16 +1,15 @@
1
- import type { Player } from "white-web-sdk";
2
1
  import type { CommonProps, GenericIcon } from "../../types";
3
-
2
+ import Tippy from "@tippyjs/react";
4
3
  import clsx from "clsx";
5
- import React, { useEffect, useState } from "react";
6
4
  import RcSlider from "rc-slider";
7
- import { PlayerPhase } from "white-web-sdk";
8
- import { usePlayer } from "./hooks";
5
+ import React, { useEffect, useState } from "react";
6
+ import { PlayerPhase, type Player } from "white-web-sdk";
7
+
9
8
  import { Icon } from "../../icons";
10
9
  import { themes, TopOffset } from "../../theme";
11
- import { Icons } from "./icons";
12
- import Tippy from "@tippyjs/react";
13
10
  import { Button } from "./components/Button";
11
+ import { Icons } from "./icons";
12
+ import { usePlayerControl } from "./hooks";
14
13
 
15
14
  export type PlayerControlProps = {
16
15
  autoHide?: boolean;
@@ -28,7 +27,7 @@ export function PlayerControl({
28
27
  ...icons
29
28
  }: PlayerControlProps) {
30
29
  const [currentTime, setCurrentTime] = useState(0);
31
- const player = usePlayer(player_);
30
+ const player = usePlayerControl(player_);
32
31
 
33
32
  useEffect(() => {
34
33
  setCurrentTime(player.currentTime);
@@ -13,7 +13,7 @@ function useForceUpdate() {
13
13
  return useCallback(() => forceUpdate_({}), EMPTY_ARRAY);
14
14
  }
15
15
 
16
- export function usePlayer(player?: Player | null) {
16
+ export function usePlayerControl(player?: Player | null) {
17
17
  const togglePlay = useCallback(() => {
18
18
  if (player) {
19
19
  switch (player.phase) {
@@ -1 +1,2 @@
1
+ export * from "./hooks";
1
2
  export { PlayerControl, name, type PlayerControlProps } from "./PlayerControl";
@@ -1,13 +1,15 @@
1
- import type { CommonProps, GenericIcon } from "../types";
1
+ import type { CommonProps, GenericIcon } from "../../types";
2
2
 
3
3
  import clsx from "clsx";
4
- import React, { useCallback, useEffect, useState } from "react";
4
+ import React from "react";
5
5
  import Tippy from "@tippyjs/react";
6
6
 
7
- import { Icon } from "../icons";
8
- import { Undo } from "../icons/Undo";
9
- import { Redo } from "../icons/Redo";
10
- import { TopOffset } from "../theme";
7
+ import { Icon } from "../../icons";
8
+ import { Undo } from "../../icons/Undo";
9
+ import { Redo } from "../../icons/Redo";
10
+ import { TopOffset } from "../../theme";
11
+ import { useWritable } from "../hooks";
12
+ import { useRedoUndo } from "./hooks";
11
13
 
12
14
  export const name = "fastboard-redo-undo";
13
15
 
@@ -15,6 +17,7 @@ export type RedoUndoProps = CommonProps & GenericIcon<"undo" | "redo">;
15
17
 
16
18
  export function RedoUndo({
17
19
  room,
20
+ manager,
18
21
  theme = "light",
19
22
  undoIcon,
20
23
  undoIconDisable,
@@ -22,27 +25,8 @@ export function RedoUndo({
22
25
  redoIconDisable,
23
26
  i18n,
24
27
  }: RedoUndoProps) {
25
- const [writable, setWritable] = useState(false);
26
- const [undoSteps, setUndoSteps] = useState(0);
27
- const [redoSteps, setRedoSteps] = useState(0);
28
-
29
- useEffect(() => {
30
- if (room) {
31
- setWritable(room.isWritable);
32
- room.isWritable && (room.disableSerialization = false);
33
-
34
- const updateWritable = () => setWritable(room?.isWritable || false);
35
-
36
- room.callbacks.on("onEnableWriteNowChanged", updateWritable);
37
- room.callbacks.on("onCanUndoStepsUpdate", setUndoSteps);
38
- room.callbacks.on("onCanRedoStepsUpdate", setRedoSteps);
39
- return () => {
40
- room.callbacks.off("onEnableWriteNowChanged", updateWritable);
41
- room.callbacks.off("onCanUndoStepsUpdate", setUndoSteps);
42
- room.callbacks.off("onCanRedoStepsUpdate", setRedoSteps);
43
- };
44
- }
45
- }, [room]);
28
+ const writable = useWritable(room);
29
+ const { redoSteps, undoSteps, redo, undo } = useRedoUndo(room, manager);
46
30
 
47
31
  const disabled = !writable;
48
32
 
@@ -60,7 +44,7 @@ export function RedoUndo({
60
44
  <button
61
45
  className={clsx(`${name}-btn`, "undo", theme)}
62
46
  disabled={disabled || undoSteps === 0}
63
- onClick={useCallback(() => room && room.undo(), [room])}
47
+ onClick={undo}
64
48
  >
65
49
  <Icon
66
50
  fallback={<Undo theme={theme} />}
@@ -81,7 +65,7 @@ export function RedoUndo({
81
65
  <button
82
66
  className={clsx(`${name}-btn`, "redo", theme)}
83
67
  disabled={disabled || redoSteps === 0}
84
- onClick={useCallback(() => room && room.redo(), [room])}
68
+ onClick={redo}
85
69
  >
86
70
  <Icon
87
71
  fallback={<Redo theme={theme} />}
@@ -0,0 +1,50 @@
1
+ import type { WindowManager, Room } from "@netless/window-manager";
2
+ import { useCallback, useEffect, useState } from "react";
3
+
4
+ export function useRedoUndo(
5
+ room?: Room | null,
6
+ manager?: WindowManager | null
7
+ ) {
8
+ const [undoSteps, setUndoSteps] = useState(0);
9
+ const [redoSteps, setRedoSteps] = useState(0);
10
+
11
+ useEffect(() => {
12
+ if (manager) {
13
+ manager.mainView.callbacks.on("onCanUndoStepsUpdate", setUndoSteps);
14
+ manager.mainView.callbacks.on("onCanRedoStepsUpdate", setRedoSteps);
15
+
16
+ return () => {
17
+ manager.mainView.callbacks.off("onCanUndoStepsUpdate", setUndoSteps);
18
+ manager.mainView.callbacks.off("onCanRedoStepsUpdate", setRedoSteps);
19
+ };
20
+ }
21
+
22
+ if (room) {
23
+ room.callbacks.on("onCanUndoStepsUpdate", setUndoSteps);
24
+ room.callbacks.on("onCanRedoStepsUpdate", setRedoSteps);
25
+
26
+ return () => {
27
+ room.callbacks.off("onCanUndoStepsUpdate", setUndoSteps);
28
+ room.callbacks.off("onCanRedoStepsUpdate", setRedoSteps);
29
+ };
30
+ }
31
+ }, [room, manager]);
32
+
33
+ const undo = useCallback(() => {
34
+ if (manager) {
35
+ manager.mainView.undo();
36
+ } else if (room) {
37
+ room.undo();
38
+ }
39
+ }, [manager, room]);
40
+
41
+ const redo = useCallback(() => {
42
+ if (manager) {
43
+ manager.mainView.redo();
44
+ } else if (room) {
45
+ room.redo();
46
+ }
47
+ }, [manager, room]);
48
+
49
+ return { redoSteps, undoSteps, redo, undo };
50
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./hooks";
2
+ export { name, RedoUndo, type RedoUndoProps } from "./RedoUndo";
@@ -5,6 +5,7 @@ import { Toolbar } from "./Toolbar";
5
5
  import { RedoUndo } from "./RedoUndo";
6
6
  import { ZoomControl } from "./ZoomControl";
7
7
  import { PageControl } from "./PageControl";
8
+ import { useHideControls } from "./hooks";
8
9
 
9
10
  export interface RootProps {
10
11
  instance: Instance;
@@ -21,12 +22,8 @@ export function Root({ instance: app }: RootProps) {
21
22
  [app, mux]
22
23
  );
23
24
 
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 || {};
25
+ const hideControls = useHideControls(app.manager);
26
+ const showControls = !hideControls;
30
27
 
31
28
  const props = {
32
29
  room: app.room,
@@ -34,6 +31,13 @@ export function Root({ instance: app }: RootProps) {
34
31
  i18n: app.i18n,
35
32
  };
36
33
 
34
+ const {
35
+ Toolbar: toolbar = showControls || hideControls === "toolbar-only",
36
+ RedoUndo: redo_undo = showControls,
37
+ ZoomControl: zoom_control = showControls,
38
+ PageControl: page_control = showControls,
39
+ } = app.config.layout || {};
40
+
37
41
  return (
38
42
  <Instance.Context.Provider value={app}>
39
43
  <div className="fastboard-root">
@@ -14,8 +14,9 @@ import { AppsButton } from "./components/AppsButton";
14
14
  import { PencilButton } from "./components/PencilButton";
15
15
  import { TextButton } from "./components/TextButton";
16
16
  import { ShapesButton } from "./components/ShapesButton";
17
+ import clsx from "clsx";
17
18
 
18
- export function Content() {
19
+ export const Content = React.memo(() => {
19
20
  const app = useInstance();
20
21
  const ref = useRef<HTMLDivElement>(null);
21
22
  const [scrollTop, setScrollTop] = useState(0);
@@ -65,7 +66,7 @@ export function Content() {
65
66
  )}
66
67
  <div
67
68
  ref={ref}
68
- className={`${name}-section`}
69
+ className={clsx(`${name}-section`)}
69
70
  style={{
70
71
  height: `${sectionHeight}px`,
71
72
  overflow: needScroll ? "hidden" : "visible",
@@ -90,4 +91,4 @@ export function Content() {
90
91
  )}
91
92
  </>
92
93
  );
93
- }
94
+ });
@@ -40,10 +40,23 @@ $name: "fastboard-toolbar";
40
40
  border: 1px solid rgba(0, 0, 0, 0.15);
41
41
  }
42
42
 
43
+ &.expanded {
44
+ border: 1px solid rgba(0, 0, 0, 0.15);
45
+ }
46
+
43
47
  &.dark {
44
48
  color: #ddd;
45
49
  background-color: rgba($color: #333, $alpha: 0.85);
46
- border: 1px solid rgba(0, 0, 0, 0.45);
50
+ }
51
+
52
+ &.expanded:hover {
53
+ box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.25);
54
+ transform: translate3d(0, 0, 0);
55
+ }
56
+
57
+ &.collapsed {
58
+ padding: 0;
59
+ background-color: transparent;
47
60
  }
48
61
 
49
62
  &-tooltip {
@@ -132,6 +145,11 @@ $name: "fastboard-toolbar";
132
145
  flex-flow: column nowrap;
133
146
  gap: 4px;
134
147
  scroll-behavior: smooth;
148
+
149
+ &.collapsed {
150
+ transform: translateX(-100%);
151
+ transition: 1s transform;
152
+ }
135
153
  }
136
154
 
137
155
  &-panel {
@@ -244,4 +262,20 @@ $name: "fastboard-toolbar";
244
262
  outline-offset: 2px;
245
263
  }
246
264
  }
265
+
266
+ &-mask-btn {
267
+ width: 17px;
268
+ height: 62px;
269
+ cursor: pointer;
270
+ &.dark {
271
+ filter: invert(0.8);
272
+ }
273
+ }
274
+
275
+ &-expand-btn {
276
+ display: flex;
277
+ align-items: center;
278
+ position: absolute;
279
+ left: 0;
280
+ }
247
281
  }