@netless/fastboard-react 0.2.6 → 0.2.9
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.js +3281 -342
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3245 -306
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -15
- package/src/components/Fastboard.scss +6 -1
- package/src/components/Fastboard.tsx +3 -2
- package/src/components/PageControl/PageControl.scss +15 -11
- package/src/components/PageControl/PageControl.tsx +15 -17
- package/src/components/PlayerControl/PlayerControl.scss +13 -12
- package/src/components/PlayerControl/PlayerControl.tsx +3 -3
- package/src/components/RedoUndo/RedoUndo.scss +10 -10
- package/src/components/ReplayFastboard.tsx +36 -0
- package/src/components/Toolbar/Toolbar.scss +54 -15
- package/src/components/Toolbar/components/AppsButton.tsx +41 -8
- package/src/components/Toolbar/components/ShapesButton.tsx +11 -3
- package/src/components/Toolbar/hooks.ts +9 -0
- package/src/components/Toolbar/icons/Laser.tsx +21 -0
- package/src/components/Toolbar/icons/Loading.tsx +13 -0
- package/src/components/ZoomControl/ZoomControl.scss +15 -11
- package/src/components/ZoomControl/ZoomControl.tsx +4 -2
- package/src/components/tippy-util.ts +18 -0
- package/src/icons/Left.tsx +15 -0
- package/src/icons/Minus.tsx +2 -2
- package/src/icons/Plus.tsx +2 -2
- package/src/icons/Redo.tsx +6 -5
- package/src/icons/Reset.tsx +4 -6
- package/src/icons/Right.tsx +15 -0
- package/src/icons/Undo.tsx +6 -5
- package/src/icons/WhiteboardAdd.tsx +26 -0
- package/src/index.ts +1 -0
- package/src/vanilla/index.tsx +14 -4
- package/src/icons/ChevronLeft.tsx +0 -21
- package/src/icons/ChevronRight.tsx +0 -21
- package/src/icons/FilePlus.tsx +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/fastboard-react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "A UI kit built on top of @netless/fastboard.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@tippyjs/react": "^4.2.6",
|
|
12
12
|
"clsx": "^1.1.1",
|
|
13
|
-
"framer-motion": "^6.2.
|
|
14
|
-
"i18next": "^21.6.
|
|
13
|
+
"framer-motion": "^6.2.8",
|
|
14
|
+
"i18next": "^21.6.13",
|
|
15
15
|
"rc-slider": "^9.7.5"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
@@ -22,19 +22,14 @@
|
|
|
22
22
|
"white-web-sdk": ">=2.16.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@netless/fastboard-core": "0.2.
|
|
26
|
-
"@netless/window-manager": "^0.4.
|
|
27
|
-
"@types/react": "^17.0.
|
|
28
|
-
"@types/react-dom": "^17.0.
|
|
29
|
-
"sass": "^1.49.
|
|
25
|
+
"@netless/fastboard-core": "0.2.9",
|
|
26
|
+
"@netless/window-manager": "^0.4.10",
|
|
27
|
+
"@types/react": "^17.0.39",
|
|
28
|
+
"@types/react-dom": "^17.0.13",
|
|
29
|
+
"sass": "^1.49.9",
|
|
30
30
|
"tippy.js": "^6.3.7",
|
|
31
|
-
"tsup": "^5.11.
|
|
32
|
-
"white-web-sdk": "^2.16.
|
|
33
|
-
},
|
|
34
|
-
"publishConfig": {
|
|
35
|
-
"main": "dist/index.js",
|
|
36
|
-
"module": "dist/index.mjs",
|
|
37
|
-
"types": "src/index.ts"
|
|
31
|
+
"tsup": "^5.11.13",
|
|
32
|
+
"white-web-sdk": "^2.16.10"
|
|
38
33
|
},
|
|
39
34
|
"scripts": {
|
|
40
35
|
"build": "tsup",
|
|
@@ -10,6 +10,7 @@ import { PageControl } from "./PageControl";
|
|
|
10
10
|
import { RedoUndo } from "./RedoUndo";
|
|
11
11
|
import { Toolbar } from "./Toolbar";
|
|
12
12
|
import { ZoomControl } from "./ZoomControl";
|
|
13
|
+
import { hideAll } from "./tippy-util";
|
|
13
14
|
|
|
14
15
|
export interface FastboardProps {
|
|
15
16
|
app?: FastboardApp | null;
|
|
@@ -58,7 +59,7 @@ function FastboardInternal({
|
|
|
58
59
|
|
|
59
60
|
const useWhiteboard = useCallback(
|
|
60
61
|
(container: HTMLDivElement | null) => {
|
|
61
|
-
if (container && app) app.
|
|
62
|
+
if (container && app) app.bindContainer(container);
|
|
62
63
|
},
|
|
63
64
|
[app]
|
|
64
65
|
);
|
|
@@ -77,7 +78,7 @@ function FastboardInternal({
|
|
|
77
78
|
<ThemeContext.Provider value={theme}>
|
|
78
79
|
<I18nContext.Provider value={i18n}>
|
|
79
80
|
<div {...restProps} className="fastboard-root" ref={forwardedRef}>
|
|
80
|
-
<div className="fastboard-view" ref={useWhiteboard} />
|
|
81
|
+
<div className="fastboard-view" ref={useWhiteboard} onTouchStartCapture={hideAll} />
|
|
81
82
|
{children ? (
|
|
82
83
|
children
|
|
83
84
|
) : (
|
|
@@ -6,19 +6,19 @@ $name: "fastboard-page-control";
|
|
|
6
6
|
gap: 4px;
|
|
7
7
|
padding: 4px;
|
|
8
8
|
border-radius: 4px;
|
|
9
|
-
backdrop-filter: blur(
|
|
10
|
-
-webkit-backdrop-filter: blur(
|
|
9
|
+
backdrop-filter: blur(5px);
|
|
10
|
+
-webkit-backdrop-filter: blur(5px);
|
|
11
11
|
|
|
12
12
|
&.light {
|
|
13
13
|
color: #333;
|
|
14
|
-
background-color: rgba(
|
|
15
|
-
border: 1px solid
|
|
14
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
15
|
+
border: 1px solid #e5e8f0;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
&.dark {
|
|
19
19
|
color: #ddd;
|
|
20
|
-
background-color:
|
|
21
|
-
border: 1px solid
|
|
20
|
+
background-color: #14181e;
|
|
21
|
+
border: 1px solid #383b42;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -37,8 +37,8 @@ $name: "fastboard-page-control";
|
|
|
37
37
|
|
|
38
38
|
svg,
|
|
39
39
|
img {
|
|
40
|
-
width:
|
|
41
|
-
height:
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 100%;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
&:disabled {
|
|
@@ -47,11 +47,11 @@ $name: "fastboard-page-control";
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
&.light:not(:disabled):hover {
|
|
50
|
-
background-color:
|
|
50
|
+
background-color: #ebf2ff;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
&.dark:not(:disabled):hover {
|
|
54
|
-
background-color:
|
|
54
|
+
background-color: #383b42;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -72,9 +72,13 @@ $name: "fastboard-page-control";
|
|
|
72
72
|
opacity: 0.6;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
.#{$name}-text {
|
|
76
|
+
line-height: 24px;
|
|
77
|
+
}
|
|
78
|
+
|
|
75
79
|
.#{$name}-page,
|
|
76
80
|
.#{$name}-slash,
|
|
77
81
|
.#{$name}-page-count {
|
|
78
|
-
font-size:
|
|
82
|
+
font-size: 14px;
|
|
79
83
|
font-variant-numeric: tabular-nums;
|
|
80
84
|
}
|
|
@@ -6,9 +6,9 @@ import React from "react";
|
|
|
6
6
|
|
|
7
7
|
import { useTranslation } from "../../i18n";
|
|
8
8
|
import { Icon } from "../../icons";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { Left } from "../../icons/Left";
|
|
10
|
+
import { Right } from "../../icons/Right";
|
|
11
|
+
import { WhiteboardAdd } from "../../icons/WhiteboardAdd";
|
|
12
12
|
import { TopOffset } from "../../theme";
|
|
13
13
|
import { useTheme, useWritable } from "../hooks";
|
|
14
14
|
import { usePageControl } from "./hooks";
|
|
@@ -52,16 +52,14 @@ export function PageControl({
|
|
|
52
52
|
disabled={disabled || pageIndex === 0}
|
|
53
53
|
onClick={actions.prevPage}
|
|
54
54
|
>
|
|
55
|
-
<Icon
|
|
56
|
-
fallback={<ChevronLeft theme={theme} />}
|
|
57
|
-
src={disabled ? prevIconDisable : prevIcon}
|
|
58
|
-
alt="[prev]"
|
|
59
|
-
/>
|
|
55
|
+
<Icon fallback={<Left theme={theme} />} src={disabled ? prevIconDisable : prevIcon} alt="[prev]" />
|
|
60
56
|
</button>
|
|
61
57
|
</Tippy>
|
|
62
|
-
<span className={clsx(`${name}-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
<span className={clsx(`${name}-text`, theme)}>
|
|
59
|
+
<span className={clsx(`${name}-page`, theme)}>{pageCount === 0 ? "\u2026" : pageIndex + 1}</span>
|
|
60
|
+
<span className={clsx(`${name}-slash`, theme)}>/</span>
|
|
61
|
+
<span className={clsx(`${name}-page-count`, theme)}>{pageCount}</span>
|
|
62
|
+
</span>
|
|
65
63
|
<Tippy
|
|
66
64
|
className="fastboard-tip"
|
|
67
65
|
content={t("nextPage")}
|
|
@@ -77,11 +75,7 @@ export function PageControl({
|
|
|
77
75
|
disabled={disabled || pageIndex === pageCount - 1}
|
|
78
76
|
onClick={actions.nextPage}
|
|
79
77
|
>
|
|
80
|
-
<Icon
|
|
81
|
-
fallback={<ChevronRight theme={theme} />}
|
|
82
|
-
src={disabled ? nextIconDisable : nextIcon}
|
|
83
|
-
alt="[next]"
|
|
84
|
-
/>
|
|
78
|
+
<Icon fallback={<Right theme={theme} />} src={disabled ? nextIconDisable : nextIcon} alt="[next]" />
|
|
85
79
|
</button>
|
|
86
80
|
</Tippy>
|
|
87
81
|
<Tippy
|
|
@@ -95,7 +89,11 @@ export function PageControl({
|
|
|
95
89
|
offset={TopOffset}
|
|
96
90
|
>
|
|
97
91
|
<button className={clsx(`${name}-btn`, "add", theme)} disabled={disabled} onClick={actions.addPage}>
|
|
98
|
-
<Icon
|
|
92
|
+
<Icon
|
|
93
|
+
fallback={<WhiteboardAdd theme={theme} />}
|
|
94
|
+
src={disabled ? addIconDisable : addIcon}
|
|
95
|
+
alt="[add]"
|
|
96
|
+
/>
|
|
99
97
|
</button>
|
|
100
98
|
</Tippy>
|
|
101
99
|
</div>
|
|
@@ -7,8 +7,9 @@ $name: "fastboard-player-control";
|
|
|
7
7
|
gap: 4px;
|
|
8
8
|
padding: 4px;
|
|
9
9
|
border-radius: 4px;
|
|
10
|
-
backdrop-filter: blur(
|
|
11
|
-
-webkit-backdrop-filter: blur(
|
|
10
|
+
backdrop-filter: blur(5px);
|
|
11
|
+
-webkit-backdrop-filter: blur(5px);
|
|
12
|
+
z-index: 100;
|
|
12
13
|
|
|
13
14
|
&.auto-hide {
|
|
14
15
|
opacity: 0;
|
|
@@ -45,14 +46,14 @@ $name: "fastboard-player-control";
|
|
|
45
46
|
|
|
46
47
|
&.light {
|
|
47
48
|
color: #333;
|
|
48
|
-
background-color: rgba(
|
|
49
|
-
border: 1px solid
|
|
49
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
50
|
+
border: 1px solid #e5e8f0;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
&.dark {
|
|
53
54
|
color: #ddd;
|
|
54
|
-
background-color:
|
|
55
|
-
border: 1px solid
|
|
55
|
+
background-color: #14181e;
|
|
56
|
+
border: 1px solid #383b42;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -74,8 +75,8 @@ $name: "fastboard-player-control";
|
|
|
74
75
|
|
|
75
76
|
svg,
|
|
76
77
|
img {
|
|
77
|
-
width:
|
|
78
|
-
height:
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
&:disabled {
|
|
@@ -84,11 +85,11 @@ $name: "fastboard-player-control";
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
&.light:not(:disabled):hover {
|
|
87
|
-
background-color:
|
|
88
|
+
background-color: #ebf2ff;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
&.dark:not(:disabled):hover {
|
|
91
|
-
background-color:
|
|
92
|
+
background-color: #383b42;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
&.loading {
|
|
@@ -113,7 +114,7 @@ $name: "fastboard-player-control";
|
|
|
113
114
|
width: initial;
|
|
114
115
|
height: initial;
|
|
115
116
|
user-select: none;
|
|
116
|
-
font-size:
|
|
117
|
+
font-size: 14px;
|
|
117
118
|
padding: 4px;
|
|
118
119
|
justify-content: flex-end;
|
|
119
120
|
|
|
@@ -140,6 +141,6 @@ $name: "fastboard-player-control";
|
|
|
140
141
|
.#{$name}-slash,
|
|
141
142
|
.#{$name}-total,
|
|
142
143
|
.#{$name}-speed-text {
|
|
143
|
-
font-size:
|
|
144
|
+
font-size: 14px;
|
|
144
145
|
font-variant-numeric: tabular-nums;
|
|
145
146
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FastboardPlayer } from "@netless/fastboard-core";
|
|
2
2
|
import type { CommonProps, GenericIcon } from "../../typings";
|
|
3
3
|
|
|
4
4
|
import Tippy from "@tippyjs/react";
|
|
@@ -17,7 +17,7 @@ import { Icons } from "./icons";
|
|
|
17
17
|
|
|
18
18
|
export type PlayerControlProps = {
|
|
19
19
|
autoHide?: boolean;
|
|
20
|
-
player?:
|
|
20
|
+
player?: FastboardPlayer;
|
|
21
21
|
} & Omit<CommonProps, "room"> &
|
|
22
22
|
GenericIcon<"play" | "pause" | "loading">;
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ export function PlayerControl({ theme, autoHide = false, player: player_, ...ico
|
|
|
28
28
|
const { t } = useTranslation();
|
|
29
29
|
|
|
30
30
|
const [currentTime, setCurrentTime] = useState(0);
|
|
31
|
-
const player = usePlayerControl(player_);
|
|
31
|
+
const player = usePlayerControl(player_?.player);
|
|
32
32
|
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
setCurrentTime(player.currentTime);
|
|
@@ -6,19 +6,19 @@ $name: "fastboard-redo-undo";
|
|
|
6
6
|
gap: 4px;
|
|
7
7
|
padding: 4px;
|
|
8
8
|
border-radius: 4px;
|
|
9
|
-
backdrop-filter: blur(
|
|
10
|
-
-webkit-backdrop-filter: blur(
|
|
9
|
+
backdrop-filter: blur(5px);
|
|
10
|
+
-webkit-backdrop-filter: blur(5px);
|
|
11
11
|
|
|
12
12
|
&.light {
|
|
13
13
|
color: #333;
|
|
14
|
-
background-color: rgba(
|
|
15
|
-
border: 1px solid
|
|
14
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
15
|
+
border: 1px solid #e5e8f0;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
&.dark {
|
|
19
19
|
color: #ddd;
|
|
20
|
-
background-color:
|
|
21
|
-
border: 1px solid
|
|
20
|
+
background-color: #14181e;
|
|
21
|
+
border: 1px solid #383b42;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -37,8 +37,8 @@ $name: "fastboard-redo-undo";
|
|
|
37
37
|
|
|
38
38
|
svg,
|
|
39
39
|
img {
|
|
40
|
-
width:
|
|
41
|
-
height:
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 100%;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
&:disabled {
|
|
@@ -47,10 +47,10 @@ $name: "fastboard-redo-undo";
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
&.light:not(:disabled):hover {
|
|
50
|
-
background-color:
|
|
50
|
+
background-color: #ebf2ff;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
&.dark:not(:disabled):hover {
|
|
54
|
-
background-color:
|
|
54
|
+
background-color: #383b42;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FastboardPlayer } from "@netless/fastboard-core";
|
|
2
|
+
import type { DivProps } from "./Fastboard";
|
|
3
|
+
import type { Theme } from "../typings";
|
|
4
|
+
|
|
5
|
+
import React, { forwardRef, useCallback } from "react";
|
|
6
|
+
import { PlayerControl } from "./PlayerControl";
|
|
7
|
+
|
|
8
|
+
export interface ReplayFastboardProps {
|
|
9
|
+
player?: FastboardPlayer | null;
|
|
10
|
+
theme?: Theme;
|
|
11
|
+
autoHideControl?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ReplayFastboard = /* @__PURE__ */ forwardRef<HTMLDivElement, ReplayFastboardProps & DivProps>(
|
|
15
|
+
function ReplayFastboard({ player, theme = "light", autoHideControl = false, ...restProps }, ref) {
|
|
16
|
+
const useWhiteboard = useCallback(
|
|
17
|
+
(container: HTMLDivElement | null) => {
|
|
18
|
+
if (container && player) player.bindContainer(container);
|
|
19
|
+
},
|
|
20
|
+
[player]
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (!player) {
|
|
24
|
+
return <div className="fastboard-root" ref={ref} {...restProps} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="fastboard-root" ref={ref} {...restProps}>
|
|
29
|
+
<div className="fastboard-view" ref={useWhiteboard} />
|
|
30
|
+
<div className="fastboard-bottom">
|
|
31
|
+
<PlayerControl player={player} theme={theme} autoHide={autoHideControl} />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
@@ -9,8 +9,8 @@ $name: "fastboard-toolbar";
|
|
|
9
9
|
gap: 4px;
|
|
10
10
|
position: absolute;
|
|
11
11
|
z-index: 100;
|
|
12
|
-
backdrop-filter: blur(
|
|
13
|
-
-webkit-backdrop-filter: blur(
|
|
12
|
+
backdrop-filter: blur(5px);
|
|
13
|
+
-webkit-backdrop-filter: blur(5px);
|
|
14
14
|
|
|
15
15
|
.rc-slider {
|
|
16
16
|
padding: 6px 0;
|
|
@@ -36,17 +36,18 @@ $name: "fastboard-toolbar";
|
|
|
36
36
|
|
|
37
37
|
&.light {
|
|
38
38
|
color: #333;
|
|
39
|
-
background-color: rgba(
|
|
40
|
-
border: 1px solid
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
&.expanded {
|
|
44
|
-
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
39
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
40
|
+
border: 1px solid #e5e8f0;
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
&.dark {
|
|
48
44
|
color: #ddd;
|
|
49
|
-
background-color:
|
|
45
|
+
background-color: #14181e;
|
|
46
|
+
border: 1px solid #383b42;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&.expanded {
|
|
50
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
&.expanded:hover {
|
|
@@ -108,11 +109,11 @@ $name: "fastboard-toolbar";
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
&.light:not(:disabled):hover {
|
|
111
|
-
background-color:
|
|
112
|
+
background-color: #ebf2ff;
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
&.dark:not(:disabled):hover {
|
|
115
|
-
background-color:
|
|
116
|
+
background-color: #383b42;
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
|
|
@@ -152,12 +153,12 @@ $name: "fastboard-toolbar";
|
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
&-section
|
|
156
|
+
&-section ~ &-mask {
|
|
156
157
|
opacity: 0;
|
|
157
158
|
transition: 0.5s opacity 0.4s;
|
|
158
159
|
}
|
|
159
160
|
|
|
160
|
-
&-section:hover
|
|
161
|
+
&-section:hover ~ &-mask,
|
|
161
162
|
&-mask:hover {
|
|
162
163
|
opacity: 1;
|
|
163
164
|
transition: 0.2s opacity;
|
|
@@ -172,7 +173,7 @@ $name: "fastboard-toolbar";
|
|
|
172
173
|
gap: 8px;
|
|
173
174
|
|
|
174
175
|
&.apps {
|
|
175
|
-
width: 240px
|
|
176
|
+
width: 240px + 8px * 2;
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
|
|
@@ -206,6 +207,35 @@ $name: "fastboard-toolbar";
|
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
&-app-icon-wrapper {
|
|
211
|
+
position: relative;
|
|
212
|
+
display: flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
&-app-icon-mask {
|
|
218
|
+
position: absolute;
|
|
219
|
+
width: 36px;
|
|
220
|
+
height: 36px;
|
|
221
|
+
animation: fastboard-app-loading-rotate 0.5s linear infinite;
|
|
222
|
+
transform-origin: center;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
&-app-is-loading {
|
|
226
|
+
cursor: wait;
|
|
227
|
+
button:disabled {
|
|
228
|
+
cursor: wait;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
&-app-is-failed {
|
|
233
|
+
cursor: not-allowed;
|
|
234
|
+
button:disabled {
|
|
235
|
+
cursor: not-allowed;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
209
239
|
&-app-icon {
|
|
210
240
|
padding-top: 4px;
|
|
211
241
|
display: inline-flex;
|
|
@@ -218,7 +248,7 @@ $name: "fastboard-toolbar";
|
|
|
218
248
|
}
|
|
219
249
|
|
|
220
250
|
&-text {
|
|
221
|
-
font-size:
|
|
251
|
+
font-size: 14px;
|
|
222
252
|
color: #5d5d5d;
|
|
223
253
|
overflow: hidden;
|
|
224
254
|
white-space: nowrap;
|
|
@@ -302,3 +332,12 @@ $name: "fastboard-toolbar";
|
|
|
302
332
|
left: 0;
|
|
303
333
|
}
|
|
304
334
|
}
|
|
335
|
+
|
|
336
|
+
@keyframes fastboard-app-loading-rotate {
|
|
337
|
+
from {
|
|
338
|
+
transform: rotate(0deg);
|
|
339
|
+
}
|
|
340
|
+
to {
|
|
341
|
+
transform: rotate(360deg);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
1
2
|
import Tippy from "@tippyjs/react";
|
|
2
3
|
import React, { useContext } from "react";
|
|
3
4
|
|
|
@@ -11,6 +12,7 @@ import { Icons } from "../icons";
|
|
|
11
12
|
import { ToolbarContext } from "../Toolbar";
|
|
12
13
|
import { Button } from "./Button";
|
|
13
14
|
import { useFastboardApp } from "../../hooks";
|
|
15
|
+
import { Loading } from "../icons/Loading";
|
|
14
16
|
|
|
15
17
|
export interface AppsButtonProps {
|
|
16
18
|
content?: React.ReactNode;
|
|
@@ -62,6 +64,7 @@ function renderAppsButtonContent(content?: React.ReactNode) {
|
|
|
62
64
|
|
|
63
65
|
function DefaultApps() {
|
|
64
66
|
const app = useFastboardApp();
|
|
67
|
+
const { appsStatus } = useContext(ToolbarContext);
|
|
65
68
|
|
|
66
69
|
return (
|
|
67
70
|
<>
|
|
@@ -69,13 +72,21 @@ function DefaultApps() {
|
|
|
69
72
|
title="Code Editor"
|
|
70
73
|
src={vscodePNG}
|
|
71
74
|
alt="[code editor]"
|
|
75
|
+
appStatus={appsStatus["Monaco"]}
|
|
72
76
|
onClick={app?.insertCodeEditor.bind(app)}
|
|
73
77
|
/>
|
|
74
|
-
<AppIcon
|
|
78
|
+
<AppIcon
|
|
79
|
+
title="GeoGebra"
|
|
80
|
+
src={geogebraPNG}
|
|
81
|
+
alt="[geogebra]"
|
|
82
|
+
appStatus={appsStatus["GeoGebra"]}
|
|
83
|
+
onClick={app?.insertGeoGebra.bind(app)}
|
|
84
|
+
/>
|
|
75
85
|
<AppIcon
|
|
76
86
|
title="Countdown"
|
|
77
87
|
src={countdownPNG}
|
|
78
88
|
alt="[countdown]"
|
|
89
|
+
appStatus={appsStatus["Countdown"]}
|
|
79
90
|
onClick={app?.insertCountdown.bind(app)}
|
|
80
91
|
/>
|
|
81
92
|
</>
|
|
@@ -86,16 +97,38 @@ interface AppIconProps {
|
|
|
86
97
|
title: string;
|
|
87
98
|
src: string;
|
|
88
99
|
alt: string;
|
|
100
|
+
appStatus?: {
|
|
101
|
+
status: "idle" | "loading" | "failed";
|
|
102
|
+
reason?: string | undefined;
|
|
103
|
+
};
|
|
89
104
|
onClick?: () => void;
|
|
90
105
|
}
|
|
91
106
|
|
|
92
|
-
function AppIcon({ title, src, alt, onClick }: AppIconProps) {
|
|
107
|
+
function AppIcon({ title, src, alt, appStatus, onClick }: AppIconProps) {
|
|
108
|
+
const { theme } = useContext(ToolbarContext);
|
|
109
|
+
const { status = "idle", reason } = appStatus || {};
|
|
110
|
+
const loading = status === "loading";
|
|
111
|
+
const failed = status === "failed";
|
|
112
|
+
const unifiedTitle = loading ? "loading" : failed ? reason : title;
|
|
113
|
+
|
|
93
114
|
return (
|
|
94
|
-
<
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
<div className="fastboard-toolbar-app-icon-wrapper">
|
|
116
|
+
<span
|
|
117
|
+
className={clsx("fastboard-toolbar-app-icon", {
|
|
118
|
+
"fastboard-toolbar-app-is-loading": loading,
|
|
119
|
+
"fastboard-toolbar-app-is-failed": failed,
|
|
120
|
+
})}
|
|
121
|
+
>
|
|
122
|
+
<Button disabled={failed || loading} placement="top" content={unifiedTitle} onClick={onClick}>
|
|
123
|
+
<img src={src} alt={alt} title={unifiedTitle} />
|
|
124
|
+
</Button>
|
|
125
|
+
<span className="fastboard-toolbar-app-icon-text">{title}</span>
|
|
126
|
+
</span>
|
|
127
|
+
{loading && (
|
|
128
|
+
<span className="fastboard-toolbar-app-icon-mask">
|
|
129
|
+
<Loading theme={theme} />
|
|
130
|
+
</span>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
100
133
|
);
|
|
101
134
|
}
|
|
@@ -2,7 +2,7 @@ import type { ShapeType } from "white-web-sdk";
|
|
|
2
2
|
import type { IconProps } from "../../../typings";
|
|
3
3
|
|
|
4
4
|
import Tippy from "@tippyjs/react";
|
|
5
|
-
import React, { useContext } from "react";
|
|
5
|
+
import React, { useCallback, useContext } from "react";
|
|
6
6
|
import { ApplianceNames } from "white-web-sdk";
|
|
7
7
|
|
|
8
8
|
import { useTranslation } from "../../../i18n";
|
|
@@ -18,7 +18,7 @@ const ShapeTypes = new Set([...ApplianceShapes, ...Shapes]);
|
|
|
18
18
|
|
|
19
19
|
export function ShapesButton() {
|
|
20
20
|
const { t } = useTranslation();
|
|
21
|
-
const { theme, memberState, lastShape } = useContext(ToolbarContext);
|
|
21
|
+
const { writable, theme, memberState, lastShape, setAppliance } = useContext(ToolbarContext);
|
|
22
22
|
|
|
23
23
|
const appliance = memberState?.currentApplianceName;
|
|
24
24
|
const shape = memberState?.shapeType;
|
|
@@ -29,6 +29,14 @@ export function ShapesButton() {
|
|
|
29
29
|
|
|
30
30
|
const CurrentIcon = ShapesMap[lastShape];
|
|
31
31
|
|
|
32
|
+
const onClick = useCallback(() => {
|
|
33
|
+
if ((ApplianceShapes as readonly ApplianceNames[]).includes(lastShape as ApplianceNames)) {
|
|
34
|
+
setAppliance(lastShape as ApplianceNames);
|
|
35
|
+
} else if (Shapes.includes(lastShape as ShapeType)) {
|
|
36
|
+
setAppliance(ApplianceNames.shape, lastShape as ShapeType);
|
|
37
|
+
}
|
|
38
|
+
}, [lastShape, setAppliance]);
|
|
39
|
+
|
|
32
40
|
return (
|
|
33
41
|
<span className="fastboard-toolbar-btn-interactive">
|
|
34
42
|
<Tippy
|
|
@@ -41,7 +49,7 @@ export function ShapesButton() {
|
|
|
41
49
|
arrow={false}
|
|
42
50
|
interactive
|
|
43
51
|
>
|
|
44
|
-
<Button content={t("shape")} active={active}>
|
|
52
|
+
<Button content={t("shape")} active={active} disabled={!writable} onClick={onClick}>
|
|
45
53
|
<CurrentIcon theme={theme} active={active} />
|
|
46
54
|
<span className="fastboard-toolbar-triangle" />
|
|
47
55
|
</Button>
|