@netless/fastboard 0.0.1 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -10
- package/dist/index.cjs.js +4 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +478 -168
- 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 +25 -41
- package/src/WhiteboardApp.ts +39 -3
- package/src/behaviors/style.ts +1 -1
- package/src/components/PlayerControl/PlayerControl.scss +145 -0
- package/src/components/PlayerControl/PlayerControl.tsx +158 -0
- package/src/components/PlayerControl/components/Button.tsx +55 -0
- package/src/components/PlayerControl/hooks.ts +95 -0
- package/src/components/PlayerControl/icons/Loading.tsx +13 -0
- package/src/components/PlayerControl/icons/Pause.tsx +13 -0
- package/src/components/PlayerControl/icons/Play.tsx +13 -0
- package/src/components/PlayerControl/icons/index.ts +10 -0
- package/src/components/PlayerControl/index.ts +1 -0
- package/src/components/Root.tsx +1 -2
- package/src/components/Toolbar/Content.tsx +28 -15
- package/src/components/Toolbar/components/ColorBox.tsx +3 -3
- package/src/components/Toolbar/components/UpDownButtons.tsx +7 -10
- package/src/components/Toolbar/hooks.ts +1 -1
- package/src/components/ZoomControl.tsx +1 -1
- package/src/hooks.ts +18 -60
- package/src/i18n/en.json +2 -1
- package/src/i18n/zh-CN.json +2 -1
- package/src/index.ts +9 -2
- package/src/internal/Instance.tsx +40 -19
- package/src/internal/helpers.ts +15 -0
- package/src/internal/index.ts +1 -0
- package/src/internal/mount-whiteboard.ts +13 -5
- package/src/style.scss +1 -0
- package/src/svelte.ts +45 -0
- package/src/vue.ts +74 -0
- package/src/helpers/index.ts +0 -18
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { DependencyList } from "react";
|
|
2
|
+
import type { Player } from "white-web-sdk";
|
|
3
|
+
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
|
+
import { PlayerPhase } from "white-web-sdk";
|
|
6
|
+
|
|
7
|
+
const EMPTY_ARRAY: DependencyList = [];
|
|
8
|
+
|
|
9
|
+
function useForceUpdate() {
|
|
10
|
+
const [, forceUpdate_] = useState({});
|
|
11
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
12
|
+
return useCallback(() => forceUpdate_({}), EMPTY_ARRAY);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function useLastValue<T>(value: T) {
|
|
16
|
+
const ref = useRef<T>(value);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
ref.current = value;
|
|
19
|
+
}, [value]);
|
|
20
|
+
return ref.current;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function usePlayer(player?: Player | null) {
|
|
24
|
+
const togglePlay = useCallback(() => {
|
|
25
|
+
if (player) {
|
|
26
|
+
switch (player.phase) {
|
|
27
|
+
case PlayerPhase.WaitingFirstFrame:
|
|
28
|
+
case PlayerPhase.Pause:
|
|
29
|
+
case PlayerPhase.Ended: {
|
|
30
|
+
player.play();
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case PlayerPhase.Playing: {
|
|
34
|
+
player.pause();
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, [player]);
|
|
40
|
+
|
|
41
|
+
const seekToProgressTime = useCallback(
|
|
42
|
+
(time: number) => {
|
|
43
|
+
if (player) {
|
|
44
|
+
player.seekToProgressTime(time);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
[player]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const lastPlayer = useLastValue(player);
|
|
51
|
+
|
|
52
|
+
const forceUpdate = useForceUpdate();
|
|
53
|
+
|
|
54
|
+
const setSpeed = useCallback(
|
|
55
|
+
(speed: number) => {
|
|
56
|
+
if (player) {
|
|
57
|
+
player.playbackSpeed = speed;
|
|
58
|
+
forceUpdate();
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
[forceUpdate, player]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!lastPlayer && player) {
|
|
66
|
+
forceUpdate();
|
|
67
|
+
}
|
|
68
|
+
}, [forceUpdate, lastPlayer, player]);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (player) {
|
|
72
|
+
player.callbacks.on("onPhaseChanged", forceUpdate);
|
|
73
|
+
player.callbacks.on("onProgressTimeChanged", forceUpdate);
|
|
74
|
+
return () => {
|
|
75
|
+
player.callbacks.off("onPhaseChanged", forceUpdate);
|
|
76
|
+
player.callbacks.off("onProgressTimeChanged", forceUpdate);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}, [forceUpdate, player]);
|
|
80
|
+
|
|
81
|
+
const phase = player ? player.phase : PlayerPhase.WaitingFirstFrame;
|
|
82
|
+
const currentTime = player ? player.progressTime : 0;
|
|
83
|
+
const totalTime = player ? player.timeDuration : 0;
|
|
84
|
+
const speed = player ? player.playbackSpeed : 1;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
phase,
|
|
88
|
+
currentTime,
|
|
89
|
+
totalTime,
|
|
90
|
+
speed,
|
|
91
|
+
setSpeed,
|
|
92
|
+
togglePlay,
|
|
93
|
+
seekToProgressTime,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../types";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Loading = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8z" fill={stroke}></path>
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../types";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Pause = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path d="M14 19h4V5h-4M6 19h4V5H6v14z" fill={stroke}></path>
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../types";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Play = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path d="M8 5.14v14l11-7l-11-7z" fill={stroke}></path>
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PlayerControl, name, type PlayerControlProps } from "./PlayerControl";
|
package/src/components/Root.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { Lock } from "../internal
|
|
4
|
-
import { Instance } from "../internal";
|
|
3
|
+
import { Lock, Instance } from "../internal";
|
|
5
4
|
import { Toolbar } from "./Toolbar";
|
|
6
5
|
import { RedoUndo } from "./RedoUndo";
|
|
7
6
|
import { ZoomControl } from "./ZoomControl";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { useInstance } from "../../internal";
|
|
4
|
-
import { clamp } from "../../helpers";
|
|
3
|
+
import { useInstance, clamp } from "../../internal";
|
|
5
4
|
import { name } from "./Toolbar";
|
|
6
5
|
import { ItemHeight, ItemsCount, MaxHeight, MinHeight } from "./const";
|
|
7
6
|
import { DownButton, UpButton } from "./components/UpDownButtons";
|
|
@@ -16,42 +15,54 @@ import { PencilButton } from "./components/PencilButton";
|
|
|
16
15
|
import { TextButton } from "./components/TextButton";
|
|
17
16
|
import { ShapesButton } from "./components/ShapesButton";
|
|
18
17
|
|
|
19
|
-
export
|
|
20
|
-
padding?: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function Content({ padding = 16 }: ContentProps) {
|
|
18
|
+
export function Content() {
|
|
24
19
|
const app = useInstance();
|
|
25
20
|
const ref = useRef<HTMLDivElement>(null);
|
|
21
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
26
22
|
const [parentHeight, setParentHeight] = useState(0);
|
|
27
23
|
|
|
24
|
+
const hasAppButton = app?.config.toolbar?.apps?.enable ?? true;
|
|
28
25
|
const needScroll = parentHeight < ItemHeight * ItemsCount + 48;
|
|
29
26
|
const sectionHeight = clamp(
|
|
30
27
|
parentHeight - 48 * (needScroll ? 3 : 1),
|
|
31
28
|
MinHeight,
|
|
32
29
|
MaxHeight
|
|
33
30
|
);
|
|
31
|
+
const scrollBuffer = Math.max(parentHeight - sectionHeight - 1, 0);
|
|
32
|
+
const disableScrollUp = scrollTop === 0;
|
|
33
|
+
const disableScrollDown = scrollTop === scrollBuffer;
|
|
34
|
+
|
|
35
|
+
const scrollTo = useCallback(
|
|
36
|
+
(height: number) => {
|
|
37
|
+
setScrollTop(clamp(scrollTop + height, 0, scrollBuffer));
|
|
38
|
+
},
|
|
39
|
+
[scrollBuffer, scrollTop]
|
|
40
|
+
);
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
useEffect(() => {
|
|
36
43
|
if (ref.current) {
|
|
37
|
-
ref.current.scrollTop =
|
|
44
|
+
ref.current.scrollTop = scrollTop;
|
|
38
45
|
}
|
|
39
|
-
}, []);
|
|
46
|
+
}, [scrollTop]);
|
|
40
47
|
|
|
41
48
|
useEffect(() => {
|
|
42
49
|
const container = ref.current?.parentElement?.parentElement;
|
|
43
50
|
if (container) {
|
|
51
|
+
const { paddingTop, paddingBottom } = getComputedStyle(container);
|
|
52
|
+
const padding = parseInt(paddingTop) + parseInt(paddingBottom) || 0;
|
|
44
53
|
const resizeObserver = new ResizeObserver(() => {
|
|
45
|
-
setParentHeight(container.getBoundingClientRect().height - padding
|
|
54
|
+
setParentHeight(container.getBoundingClientRect().height - padding);
|
|
46
55
|
});
|
|
47
56
|
resizeObserver.observe(container);
|
|
48
57
|
return () => resizeObserver.disconnect();
|
|
49
58
|
}
|
|
50
|
-
}, [
|
|
59
|
+
}, []);
|
|
51
60
|
|
|
52
61
|
return (
|
|
53
62
|
<>
|
|
54
|
-
{needScroll &&
|
|
63
|
+
{needScroll && (
|
|
64
|
+
<UpButton scrollTo={scrollTo} disabled={disableScrollUp} />
|
|
65
|
+
)}
|
|
55
66
|
<div
|
|
56
67
|
ref={ref}
|
|
57
68
|
className={`${name}-section`}
|
|
@@ -67,14 +78,16 @@ export function Content({ padding = 16 }: ContentProps) {
|
|
|
67
78
|
<ShapesButton />
|
|
68
79
|
<EraserButton />
|
|
69
80
|
<CleanButton />
|
|
70
|
-
{
|
|
81
|
+
{hasAppButton && (
|
|
71
82
|
<AppsButton
|
|
72
83
|
content={app?.config.toolbar?.apps?.content}
|
|
73
84
|
onClick={app?.config.toolbar?.apps?.onClick}
|
|
74
85
|
/>
|
|
75
86
|
)}
|
|
76
87
|
</div>
|
|
77
|
-
{needScroll &&
|
|
88
|
+
{needScroll && (
|
|
89
|
+
<DownButton scrollTo={scrollTo} disabled={disableScrollDown} />
|
|
90
|
+
)}
|
|
78
91
|
</>
|
|
79
92
|
);
|
|
80
93
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Color } from "white-web-sdk";
|
|
2
2
|
|
|
3
3
|
import clsx from "clsx";
|
|
4
|
-
import React from "react";
|
|
5
|
-
|
|
6
|
-
import { isEqualArray } from "../../../
|
|
4
|
+
import React, { useContext } from "react";
|
|
5
|
+
|
|
6
|
+
import { isEqualArray } from "../../../internal";
|
|
7
7
|
import { ToolbarContext } from "../Toolbar";
|
|
8
8
|
|
|
9
9
|
const colors: Record<string, Color> = {
|
|
@@ -8,18 +8,17 @@ import { ToolbarContext } from "../Toolbar";
|
|
|
8
8
|
import { ItemHeight } from "../const";
|
|
9
9
|
|
|
10
10
|
export interface UpButtonProps {
|
|
11
|
+
disabled: boolean;
|
|
11
12
|
scrollTo: (height: number) => void;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export function UpButton({ scrollTo }: UpButtonProps) {
|
|
15
|
-
const { theme, icons
|
|
15
|
+
export function UpButton({ disabled, scrollTo }: UpButtonProps) {
|
|
16
|
+
const { theme, icons } = useContext(ToolbarContext);
|
|
16
17
|
const scrollUp = useCallback(() => scrollTo(-ItemHeight), [scrollTo]);
|
|
17
18
|
|
|
18
|
-
const disabled = !writable;
|
|
19
|
-
|
|
20
19
|
return (
|
|
21
20
|
<>
|
|
22
|
-
<Button content="Up" onClick={scrollUp}>
|
|
21
|
+
<Button content="Up" disabled={disabled} onClick={scrollUp}>
|
|
23
22
|
<Icon
|
|
24
23
|
fallback={<Icons.Up theme={theme} />}
|
|
25
24
|
src={disabled ? icons?.upIconDisable : icons?.upIcon}
|
|
@@ -31,16 +30,14 @@ export function UpButton({ scrollTo }: UpButtonProps) {
|
|
|
31
30
|
);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
export function DownButton({ scrollTo }: UpButtonProps) {
|
|
35
|
-
const { theme, icons
|
|
33
|
+
export function DownButton({ disabled, scrollTo }: UpButtonProps) {
|
|
34
|
+
const { theme, icons } = useContext(ToolbarContext);
|
|
36
35
|
const scrollDown = useCallback(() => scrollTo(ItemHeight), [scrollTo]);
|
|
37
36
|
|
|
38
|
-
const disabled = !writable;
|
|
39
|
-
|
|
40
37
|
return (
|
|
41
38
|
<>
|
|
42
39
|
<CutLine />
|
|
43
|
-
<Button content="Down" onClick={scrollDown}>
|
|
40
|
+
<Button content="Down" disabled={disabled} onClick={scrollDown}>
|
|
44
41
|
<Icon
|
|
45
42
|
fallback={<Icons.Down theme={theme} />}
|
|
46
43
|
src={disabled ? icons?.downIconDisable : icons?.downIcon}
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
} from "white-web-sdk";
|
|
9
9
|
|
|
10
10
|
import { useCallback, useEffect, useState } from "react";
|
|
11
|
-
import { noop } from "../../internal
|
|
11
|
+
import { noop } from "../../internal";
|
|
12
12
|
|
|
13
13
|
export function useWritable(room?: Room | null) {
|
|
14
14
|
const [value, setValue] = useState(false);
|
|
@@ -5,7 +5,7 @@ import clsx from "clsx";
|
|
|
5
5
|
import React, { useCallback, useEffect, useState } from "react";
|
|
6
6
|
import Tippy from "@tippyjs/react";
|
|
7
7
|
|
|
8
|
-
import { clamp } from "../
|
|
8
|
+
import { clamp } from "../internal";
|
|
9
9
|
import { TopOffset } from "../theme";
|
|
10
10
|
import { Icon } from "../icons";
|
|
11
11
|
import { Minus } from "../icons/Minus";
|
package/src/hooks.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { WhiteboardApp } from "./WhiteboardApp";
|
|
2
|
-
import type { FastBoardConfig } from "./index";
|
|
1
|
+
import type { WhiteboardApp, WhiteboardAppConfig } from "./WhiteboardApp";
|
|
3
2
|
|
|
4
|
-
import { useEffect,
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
5
4
|
import { createWhiteboardApp } from "./index";
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
export type FastBoardConfig = WhiteboardAppConfig;
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* @example
|
|
@@ -24,65 +23,24 @@ export function useFastboard(config: FastBoardConfig): readonly [
|
|
|
24
23
|
const [app, setApp] = useState<WhiteboardApp | null>(null);
|
|
25
24
|
const [currentTarget, ref] = useState<HTMLDivElement | null>(null);
|
|
26
25
|
|
|
27
|
-
interface WorkingState {
|
|
28
|
-
app: WhiteboardApp | undefined;
|
|
29
|
-
type: "creating" | "disposing" | undefined;
|
|
30
|
-
next: HTMLDivElement | null | undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const working = useRef<WorkingState>({
|
|
34
|
-
app: undefined,
|
|
35
|
-
type: undefined,
|
|
36
|
-
next: undefined,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
26
|
useEffect(() => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
27
|
+
let isMounted = true;
|
|
28
|
+
const promise = createWhiteboardApp(config).then(app => {
|
|
29
|
+
if (isMounted) setApp(app);
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
isMounted = false;
|
|
33
|
+
promise.then(() => app?.dispose());
|
|
34
|
+
};
|
|
35
|
+
// ignore config and app change
|
|
36
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
|
+
}, []);
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (app) {
|
|
41
|
+
app.bindElement(currentTarget);
|
|
55
42
|
}
|
|
56
|
-
|
|
57
|
-
(async () => {
|
|
58
|
-
let target: WorkingState["next"] = currentTarget;
|
|
59
|
-
|
|
60
|
-
do {
|
|
61
|
-
if (target) {
|
|
62
|
-
working.current.type = "creating";
|
|
63
|
-
if (working.current.app) {
|
|
64
|
-
await working.current.app.dispose();
|
|
65
|
-
}
|
|
66
|
-
working.current.app = await createWhiteboardApp({
|
|
67
|
-
...config,
|
|
68
|
-
target: target,
|
|
69
|
-
});
|
|
70
|
-
setApp(working.current.app);
|
|
71
|
-
} else if (target === null) {
|
|
72
|
-
working.current.type = "disposing";
|
|
73
|
-
if (working.current.app) {
|
|
74
|
-
await working.current.app.dispose();
|
|
75
|
-
working.current.app = undefined;
|
|
76
|
-
}
|
|
77
|
-
setApp(null);
|
|
78
|
-
}
|
|
79
|
-
working.current.type = undefined;
|
|
80
|
-
|
|
81
|
-
target = working.current.next;
|
|
82
|
-
working.current.next = undefined;
|
|
83
|
-
} while (target !== undefined);
|
|
84
|
-
})();
|
|
85
|
-
}, [config, currentTarget]);
|
|
43
|
+
}, [app, currentTarget]);
|
|
86
44
|
|
|
87
45
|
return Object.assign([app, ref] as const, { app, ref });
|
|
88
46
|
}
|
package/src/i18n/en.json
CHANGED
package/src/i18n/zh-CN.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -10,13 +10,20 @@ 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
|
|
|
16
20
|
export const register = WindowManager.register.bind(WindowManager);
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @example
|
|
24
|
+
* let app = await createWhiteboardApp(config)
|
|
25
|
+
* app.bindElement(el)
|
|
26
|
+
*/
|
|
20
27
|
export async function createWhiteboardApp(
|
|
21
28
|
config: WhiteboardAppConfig
|
|
22
29
|
): Promise<WhiteboardApp> {
|
|
@@ -37,7 +37,6 @@ export type InsertDocsParams = InsertDocsStatic | InsertDocsDynamic;
|
|
|
37
37
|
export type Language = "zh-CN" | "en-US";
|
|
38
38
|
|
|
39
39
|
export interface WhiteboardAppConfig {
|
|
40
|
-
readonly target: HTMLElement;
|
|
41
40
|
readonly sdkConfig: SdkConfig;
|
|
42
41
|
readonly joinRoom: JoinRoom;
|
|
43
42
|
readonly managerConfig?: Omit<ManagerConfig, "container">;
|
|
@@ -63,7 +62,6 @@ export class Instance {
|
|
|
63
62
|
|
|
64
63
|
readonly config: WhiteboardAppConfig;
|
|
65
64
|
|
|
66
|
-
target: HTMLElement | null = null;
|
|
67
65
|
sdk: WhiteWebSdk | null = null;
|
|
68
66
|
room: Room | null = null;
|
|
69
67
|
manager: WindowManager | null = null;
|
|
@@ -84,14 +82,41 @@ export class Instance {
|
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
constructor(config: WhiteboardAppConfig) {
|
|
87
|
-
this.target = config.target;
|
|
88
85
|
this.config = config;
|
|
89
86
|
this.refreshReadyPromise();
|
|
87
|
+
this.initialize();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private _target: HTMLElement | null = null;
|
|
91
|
+
|
|
92
|
+
async initialize() {
|
|
93
|
+
const essentials = await mountWhiteboard(
|
|
94
|
+
this.config.sdkConfig,
|
|
95
|
+
this.config.joinRoom,
|
|
96
|
+
this.config.managerConfig || {},
|
|
97
|
+
this.config.language || "en-US"
|
|
98
|
+
);
|
|
99
|
+
this.accept(essentials);
|
|
100
|
+
this.resolveReady();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get target(): HTMLElement | null {
|
|
104
|
+
return this._target;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
set target(value: HTMLElement | null) {
|
|
108
|
+
if (this._target && value) {
|
|
109
|
+
ReactDOM.unmountComponentAtNode(this._target);
|
|
110
|
+
}
|
|
111
|
+
this._target = value;
|
|
90
112
|
this.forceUpdate();
|
|
91
113
|
}
|
|
92
114
|
|
|
93
|
-
forceUpdate() {
|
|
94
|
-
|
|
115
|
+
async forceUpdate() {
|
|
116
|
+
await this.readyPromise;
|
|
117
|
+
if (this.target) {
|
|
118
|
+
ReactDOM.render(<Root instance={this} />, this.target);
|
|
119
|
+
}
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
accept({ sdk, room, manager, i18n }: AcceptParams) {
|
|
@@ -112,19 +137,11 @@ export class Instance {
|
|
|
112
137
|
}
|
|
113
138
|
}
|
|
114
139
|
|
|
115
|
-
async mount(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
140
|
+
async mount(node: HTMLElement) {
|
|
141
|
+
await this.readyPromise;
|
|
142
|
+
if (this.manager) {
|
|
143
|
+
this.manager.bindContainer(node);
|
|
119
144
|
}
|
|
120
|
-
const essentials = await mountWhiteboard(
|
|
121
|
-
this.config.sdkConfig,
|
|
122
|
-
this.config.joinRoom,
|
|
123
|
-
{ ...this.config.managerConfig, container },
|
|
124
|
-
this.config.language
|
|
125
|
-
);
|
|
126
|
-
this.accept(essentials);
|
|
127
|
-
this.resolveReady();
|
|
128
145
|
}
|
|
129
146
|
|
|
130
147
|
async unmount() {
|
|
@@ -203,8 +220,12 @@ export class Instance {
|
|
|
203
220
|
});
|
|
204
221
|
}
|
|
205
222
|
|
|
206
|
-
changeLanguage(language: Language) {
|
|
207
|
-
|
|
223
|
+
async changeLanguage(language: Language) {
|
|
224
|
+
try {
|
|
225
|
+
await this.i18n?.changeLanguage(language);
|
|
226
|
+
} finally {
|
|
227
|
+
await this.forceUpdate();
|
|
228
|
+
}
|
|
208
229
|
}
|
|
209
230
|
}
|
|
210
231
|
|
package/src/internal/helpers.ts
CHANGED
|
@@ -2,6 +2,21 @@ export function noop() {
|
|
|
2
2
|
return;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
export function applyStyles(css: string) {
|
|
6
|
+
const el = document.createElement("style");
|
|
7
|
+
el.appendChild(document.createTextNode(css));
|
|
8
|
+
document.head.appendChild(el);
|
|
9
|
+
return el;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function clamp(value: number, min: number, max: number) {
|
|
13
|
+
return value < min ? min : value > max ? max : value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isEqualArray<T>(a: T[], b: T[]) {
|
|
17
|
+
return a.length === b.length && a.every((e, i) => e === b[i]);
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
export type TaskFn = () => Promise<void> | void;
|
|
6
21
|
|
|
7
22
|
export class Lock {
|
package/src/internal/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { MountParams } from "@netless/window-manager";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
JoinRoomParams,
|
|
4
|
+
RoomCallbacks,
|
|
5
|
+
WhiteWebSdkConfiguration,
|
|
6
|
+
} from "white-web-sdk";
|
|
3
7
|
import type { Essentials, Language } from "./instance";
|
|
4
8
|
|
|
5
9
|
import { WindowManager } from "@netless/window-manager";
|
|
@@ -13,7 +17,7 @@ export type SdkConfig = Omit<
|
|
|
13
17
|
export type JoinRoom = Omit<
|
|
14
18
|
JoinRoomParams,
|
|
15
19
|
"useMultiViews" | "disableMagixEventDispatchLimit"
|
|
16
|
-
|
|
20
|
+
> & { callbacks?: Partial<RoomCallbacks> };
|
|
17
21
|
export type ManagerConfig = Omit<MountParams, "room">;
|
|
18
22
|
|
|
19
23
|
function ensureWindowManager(joinRoom: JoinRoom) {
|
|
@@ -45,7 +49,7 @@ export async function mountWhiteboard(
|
|
|
45
49
|
sdkConfig: SdkConfig,
|
|
46
50
|
joinRoom: JoinRoom,
|
|
47
51
|
managerConfig: ManagerConfig,
|
|
48
|
-
language
|
|
52
|
+
language: Language
|
|
49
53
|
): Promise<Essentials> {
|
|
50
54
|
const sdk = new WhiteWebSdk({
|
|
51
55
|
...sdkConfig,
|
|
@@ -53,7 +57,10 @@ export async function mountWhiteboard(
|
|
|
53
57
|
});
|
|
54
58
|
|
|
55
59
|
ensureWindowManager(joinRoom);
|
|
56
|
-
|
|
60
|
+
joinRoom = { ...joinRoom };
|
|
61
|
+
const callbacks = joinRoom.callbacks;
|
|
62
|
+
delete joinRoom.callbacks;
|
|
63
|
+
const joinRoomParams: JoinRoomParams = {
|
|
57
64
|
floatBar: true,
|
|
58
65
|
hotKeys: {
|
|
59
66
|
...DefaultHotKeys,
|
|
@@ -63,7 +70,8 @@ export async function mountWhiteboard(
|
|
|
63
70
|
useMultiViews: true,
|
|
64
71
|
disableNewPencil: false,
|
|
65
72
|
disableMagixEventDispatchLimit: true,
|
|
66
|
-
}
|
|
73
|
+
};
|
|
74
|
+
const room = await sdk.joinRoom(joinRoomParams, callbacks);
|
|
67
75
|
|
|
68
76
|
const manager = await WindowManager.mount({
|
|
69
77
|
cursor: true,
|
package/src/style.scss
CHANGED