@netless/fastboard-react 0.2.11 → 0.3.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +49 -0
  2. package/dist/index.d.ts +11 -140
  3. package/dist/index.js +73 -2130
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +71 -2129
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +16 -24
  8. package/src/Fastboard.tsx +12 -0
  9. package/src/PageControl.tsx +11 -0
  10. package/src/RedoUndo.tsx +8 -0
  11. package/src/Toolbar.tsx +8 -0
  12. package/src/ZoomControl.tsx +11 -0
  13. package/src/hooks.tsx +53 -0
  14. package/src/index.tsx +19 -0
  15. package/src/style.scss +3 -26
  16. package/src/behaviors/style.ts +0 -4
  17. package/src/components/Fastboard.scss +0 -46
  18. package/src/components/Fastboard.tsx +0 -108
  19. package/src/components/PageControl/PageControl.scss +0 -84
  20. package/src/components/PageControl/PageControl.tsx +0 -101
  21. package/src/components/PageControl/hooks.ts +0 -24
  22. package/src/components/PageControl/index.ts +0 -2
  23. package/src/components/PlayerControl/PlayerControl.scss +0 -146
  24. package/src/components/PlayerControl/PlayerControl.tsx +0 -131
  25. package/src/components/PlayerControl/components/Button.tsx +0 -44
  26. package/src/components/PlayerControl/hooks.ts +0 -88
  27. package/src/components/PlayerControl/icons/Loading.tsx +0 -13
  28. package/src/components/PlayerControl/icons/Pause.tsx +0 -13
  29. package/src/components/PlayerControl/icons/Play.tsx +0 -13
  30. package/src/components/PlayerControl/icons/index.ts +0 -10
  31. package/src/components/PlayerControl/index.ts +0 -2
  32. package/src/components/RedoUndo/RedoUndo.scss +0 -56
  33. package/src/components/RedoUndo/RedoUndo.tsx +0 -76
  34. package/src/components/RedoUndo/hooks.ts +0 -18
  35. package/src/components/RedoUndo/index.ts +0 -2
  36. package/src/components/ReplayFastboard.tsx +0 -36
  37. package/src/components/Toolbar/Content.tsx +0 -89
  38. package/src/components/Toolbar/Toolbar.scss +0 -343
  39. package/src/components/Toolbar/Toolbar.tsx +0 -84
  40. package/src/components/Toolbar/components/ApplianceButtons.tsx +0 -108
  41. package/src/components/Toolbar/components/AppsButton.tsx +0 -134
  42. package/src/components/Toolbar/components/Button.tsx +0 -45
  43. package/src/components/Toolbar/components/ColorBox.tsx +0 -55
  44. package/src/components/Toolbar/components/CutLine.tsx +0 -8
  45. package/src/components/Toolbar/components/PencilButton.tsx +0 -66
  46. package/src/components/Toolbar/components/ShapesButton.tsx +0 -135
  47. package/src/components/Toolbar/components/Slider.tsx +0 -26
  48. package/src/components/Toolbar/components/TextButton.tsx +0 -62
  49. package/src/components/Toolbar/components/UpDownButtons.tsx +0 -49
  50. package/src/components/Toolbar/components/assets/cocos.png +0 -0
  51. package/src/components/Toolbar/components/assets/collapsed.png +0 -0
  52. package/src/components/Toolbar/components/assets/countdown.png +0 -0
  53. package/src/components/Toolbar/components/assets/expanded.png +0 -0
  54. package/src/components/Toolbar/components/assets/geogebra.png +0 -0
  55. package/src/components/Toolbar/components/assets/vscode.png +0 -0
  56. package/src/components/Toolbar/const.ts +0 -32
  57. package/src/components/Toolbar/hooks.ts +0 -89
  58. package/src/components/Toolbar/icons/Apps.tsx +0 -16
  59. package/src/components/Toolbar/icons/Arrow.tsx +0 -13
  60. package/src/components/Toolbar/icons/Circle.tsx +0 -13
  61. package/src/components/Toolbar/icons/Clean.tsx +0 -16
  62. package/src/components/Toolbar/icons/Clicker.tsx +0 -19
  63. package/src/components/Toolbar/icons/Collapse.tsx +0 -13
  64. package/src/components/Toolbar/icons/Diamond.tsx +0 -13
  65. package/src/components/Toolbar/icons/Down.tsx +0 -13
  66. package/src/components/Toolbar/icons/Eraser.tsx +0 -16
  67. package/src/components/Toolbar/icons/Expand.tsx +0 -13
  68. package/src/components/Toolbar/icons/Laser.tsx +0 -21
  69. package/src/components/Toolbar/icons/Line.tsx +0 -13
  70. package/src/components/Toolbar/icons/Loading.tsx +0 -13
  71. package/src/components/Toolbar/icons/Pencil.tsx +0 -16
  72. package/src/components/Toolbar/icons/Rectangle.tsx +0 -13
  73. package/src/components/Toolbar/icons/Selector.tsx +0 -13
  74. package/src/components/Toolbar/icons/SpeechBalloon.tsx +0 -17
  75. package/src/components/Toolbar/icons/Star.tsx +0 -17
  76. package/src/components/Toolbar/icons/Text.tsx +0 -13
  77. package/src/components/Toolbar/icons/Triangle.tsx +0 -13
  78. package/src/components/Toolbar/icons/Up.tsx +0 -13
  79. package/src/components/Toolbar/icons/index.ts +0 -42
  80. package/src/components/Toolbar/index.ts +0 -2
  81. package/src/components/ZoomControl/ZoomControl.scss +0 -84
  82. package/src/components/ZoomControl/ZoomControl.tsx +0 -96
  83. package/src/components/ZoomControl/hooks.ts +0 -50
  84. package/src/components/ZoomControl/index.ts +0 -2
  85. package/src/components/hooks.ts +0 -66
  86. package/src/components/tippy-util.ts +0 -8
  87. package/src/i18n/en.json +0 -31
  88. package/src/i18n/index.ts +0 -29
  89. package/src/i18n/zh-CN.json +0 -32
  90. package/src/icons/Left.tsx +0 -15
  91. package/src/icons/Minus.tsx +0 -15
  92. package/src/icons/Plus.tsx +0 -15
  93. package/src/icons/Redo.tsx +0 -19
  94. package/src/icons/Reset.tsx +0 -17
  95. package/src/icons/Right.tsx +0 -15
  96. package/src/icons/Undo.tsx +0 -19
  97. package/src/icons/WhiteboardAdd.tsx +0 -26
  98. package/src/icons/index.tsx +0 -11
  99. package/src/index.ts +0 -12
  100. package/src/internal/helpers.ts +0 -31
  101. package/src/internal/hooks.ts +0 -23
  102. package/src/internal/index.ts +0 -2
  103. package/src/theme.ts +0 -36
  104. package/src/typings.ts +0 -15
  105. package/src/vanilla/index.tsx +0 -28
@@ -1,343 +0,0 @@
1
- $name: "fastboard-toolbar";
2
-
3
- .#{$name} {
4
- display: flex;
5
- align-items: center;
6
- padding: 4px;
7
- border-radius: 4px;
8
- flex-direction: column;
9
- gap: 4px;
10
- position: absolute;
11
- z-index: 100;
12
- backdrop-filter: blur(5px);
13
- -webkit-backdrop-filter: blur(5px);
14
-
15
- .rc-slider {
16
- padding: 6px 0;
17
- }
18
- .rc-slider-rail,
19
- .rc-slider-track {
20
- height: 2px;
21
- }
22
-
23
- .tippy-content {
24
- padding: 8px;
25
- }
26
- .tippy-box {
27
- border: 1px solid rgba(0, 0, 0, 0.15);
28
- background-color: rgba($color: #333, $alpha: 0.95);
29
- backdrop-filter: blur(2px);
30
- -webkit-backdrop-filter: blur(2px);
31
- }
32
- .tippy-box[data-theme~="light"] {
33
- background-color: rgba($color: #fff, $alpha: 0.95);
34
- box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.25);
35
- }
36
-
37
- &.light {
38
- color: #333;
39
- background-color: rgba(255, 255, 255, 0.9);
40
- border: 1px solid #e5e8f0;
41
- }
42
-
43
- &.dark {
44
- color: #ddd;
45
- background-color: #14181e;
46
- border: 1px solid #383b42;
47
- }
48
-
49
- &.expanded {
50
- border: 1px solid rgba(0, 0, 0, 0.15);
51
- }
52
-
53
- &.expanded:hover {
54
- box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.25);
55
- transform: translate3d(0, 0, 0);
56
- }
57
-
58
- &.collapsed {
59
- padding: 0;
60
- background-color: transparent;
61
- }
62
-
63
- &-tooltip {
64
- display: inline-flex;
65
- align-items: center;
66
- gap: 4px;
67
- }
68
-
69
- &-hotkey {
70
- margin-right: -4px;
71
- width: 24px;
72
- height: 24px;
73
- border-radius: 4px;
74
- background-color: rgba($color: #fff, $alpha: 0.1);
75
- display: inline-flex;
76
- align-items: center;
77
- justify-content: center;
78
- }
79
-
80
- &-btn {
81
- appearance: none;
82
- cursor: pointer;
83
- margin: 0;
84
- border: 0;
85
- padding: 4px;
86
- width: 32px;
87
- height: 32px;
88
- background-color: transparent;
89
- border-radius: 4px;
90
- font-size: 24px;
91
- line-height: 1;
92
- position: relative;
93
-
94
- &-interactive {
95
- display: inline-block;
96
- width: 32px;
97
- height: 32px;
98
- }
99
-
100
- svg,
101
- img {
102
- width: 100%;
103
- height: 100%;
104
- }
105
-
106
- &:disabled {
107
- opacity: 0.5;
108
- cursor: not-allowed;
109
- }
110
-
111
- &.light:not(:disabled):hover {
112
- background-color: #ebf2ff;
113
- }
114
-
115
- &.dark:not(:disabled):hover {
116
- background-color: #383b42;
117
- }
118
- }
119
-
120
- &-triangle {
121
- width: 0px;
122
- height: 0px;
123
- border-bottom: 4px solid;
124
- border-left: 4px solid transparent;
125
- position: absolute;
126
- bottom: 0;
127
- right: 0;
128
- }
129
-
130
- &-cut-line {
131
- display: inline-block;
132
- height: 0.5px;
133
- width: 100%;
134
-
135
- &.light {
136
- background-color: #e7e7e7;
137
- }
138
-
139
- &.dark {
140
- background-color: rgba(255, 255, 255, 0.15);
141
- }
142
- }
143
-
144
- &-section {
145
- display: inline-flex;
146
- flex-flow: column nowrap;
147
- gap: 4px;
148
- scroll-behavior: smooth;
149
-
150
- &.collapsed {
151
- transform: translateX(-100%);
152
- transition: 1s transform;
153
- }
154
- }
155
-
156
- &-section ~ &-mask {
157
- opacity: 0;
158
- transition: 0.5s opacity 0.4s;
159
- }
160
-
161
- &-section:hover ~ &-mask,
162
- &-mask:hover {
163
- opacity: 1;
164
- transition: 0.2s opacity;
165
- }
166
-
167
- &-panel {
168
- width: 136px - 8px * 2;
169
- padding: 0;
170
- display: flex;
171
- flex-flow: column nowrap;
172
- align-items: center;
173
- gap: 8px;
174
-
175
- &.apps {
176
- width: 240px + 8px * 2;
177
- }
178
- }
179
-
180
- &-color-box,
181
- &-shapes {
182
- display: grid;
183
- grid-template-columns: repeat(4, 1fr);
184
- gap: 8px;
185
- align-items: center;
186
- justify-items: center;
187
-
188
- .#{$name}-btn {
189
- padding: 0;
190
- width: 24px;
191
- height: 24px;
192
- }
193
- }
194
-
195
- &-apps {
196
- width: 100%;
197
- display: grid;
198
- grid-template-columns: repeat(3, 1fr);
199
- gap: 8px;
200
- align-items: center;
201
- justify-items: center;
202
-
203
- .#{$name}-btn {
204
- width: 40px;
205
- height: 40px;
206
- font-size: 0;
207
- }
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
-
239
- &-app-icon {
240
- padding-top: 4px;
241
- display: inline-flex;
242
- flex-flow: column nowrap;
243
- align-items: center;
244
- gap: 4px;
245
-
246
- .fastboard-toolbar-btn {
247
- padding: 0;
248
- }
249
-
250
- &-text {
251
- font-size: 14px;
252
- color: #5d5d5d;
253
- overflow: hidden;
254
- white-space: nowrap;
255
- text-overflow: ellipsis;
256
- }
257
- }
258
-
259
- &-color-item {
260
- width: 24px;
261
- height: 24px;
262
- border-radius: 4px;
263
- cursor: pointer;
264
-
265
- *.light:hover {
266
- background-color: #f5f5f5;
267
- }
268
-
269
- *.dark:hover {
270
- background-color: #333;
271
- }
272
- }
273
-
274
- &-color-border {
275
- width: 24px;
276
- height: 24px;
277
- border: 1px solid transparent;
278
- border-radius: 4px;
279
- display: inline-flex;
280
- align-items: center;
281
- justify-content: center;
282
-
283
- &.active.light {
284
- border: 1px solid rgba(51, 129, 255, 0.8);
285
- }
286
-
287
- &.active.dark {
288
- border: 1px solid rgba(51, 129, 255, 0.8);
289
- }
290
- }
291
-
292
- &-color-btn {
293
- margin: 0;
294
- border: 1px solid rgba(0, 0, 0, 0.24);
295
- padding: 0;
296
- appearance: none;
297
- width: 16px;
298
- height: 16px;
299
- border-radius: 4px;
300
- cursor: pointer;
301
-
302
- &:focus-visible {
303
- outline-offset: 2px;
304
- }
305
- }
306
-
307
- &-mask {
308
- position: absolute;
309
- left: calc(100% + 1px);
310
- top: 50%;
311
- transform: translateY(-50%);
312
- opacity: 0.85;
313
- &.dark {
314
- left: calc(100%);
315
- }
316
- }
317
-
318
- &-mask-btn {
319
- width: 17px;
320
- height: 62px;
321
- cursor: pointer;
322
- opacity: 0.85;
323
- &.dark {
324
- filter: invert(0.8);
325
- }
326
- }
327
-
328
- &-expand-btn {
329
- display: flex;
330
- align-items: center;
331
- position: absolute;
332
- left: 0;
333
- }
334
- }
335
-
336
- @keyframes fastboard-app-loading-rotate {
337
- from {
338
- transform: rotate(0deg);
339
- }
340
- to {
341
- transform: rotate(360deg);
342
- }
343
- }
@@ -1,84 +0,0 @@
1
- import type { CommonProps, GenericIcon, Theme } from "../../typings";
2
- import type { ToolbarHook } from "./hooks";
3
-
4
- import clsx from "clsx";
5
- import { AnimatePresence, motion } from "framer-motion";
6
- import React, { createContext, useState } from "react";
7
-
8
- import expandPNG from "./components/assets/expanded.png";
9
-
10
- import { Icon } from "../../icons";
11
- import { useTheme } from "../hooks";
12
- import { Content } from "./Content";
13
- import { EmptyToolbarHook, useToolbar } from "./hooks";
14
-
15
- export type ToolbarProps = CommonProps & {
16
- icons?: GenericIcon<
17
- | "clicker"
18
- | "selector"
19
- | "pencil"
20
- | "eraser"
21
- | "clean"
22
- | "expand"
23
- | "collapse"
24
- | "up"
25
- | "down"
26
- | "text"
27
- | "apps"
28
- >;
29
- };
30
-
31
- type ToolbarContextType = ToolbarHook & {
32
- theme: Theme;
33
- icons?: ToolbarProps["icons"];
34
- };
35
-
36
- export const ToolbarContext = createContext<ToolbarContextType>({
37
- theme: "light",
38
- ...EmptyToolbarHook,
39
- });
40
-
41
- export const name = "fastboard-toolbar";
42
-
43
- export function Toolbar({ theme, icons }: ToolbarProps) {
44
- theme = useTheme(theme);
45
-
46
- const hook = useToolbar();
47
- const [expanded, setExpanded] = useState(true);
48
- const [pointerEvents, setPointerEvents] = useState<"auto" | "none">("auto");
49
- const disabled = !hook.writable;
50
-
51
- return (
52
- <ToolbarContext.Provider value={{ theme, icons, ...hook }}>
53
- <AnimatePresence>
54
- {expanded ? (
55
- <motion.div
56
- key="toolbar"
57
- className={clsx(name, theme)}
58
- initial={{ x: -100 }}
59
- animate={{ x: 0, transition: { duration: 0.5 } }}
60
- exit={{ x: -100, transition: { duration: 0.5 } }}
61
- onAnimationStart={() => setPointerEvents("none")}
62
- onAnimationComplete={() => setPointerEvents("auto")}
63
- style={{ pointerEvents }}
64
- >
65
- <Content onCollapse={() => setExpanded(false)} />
66
- </motion.div>
67
- ) : (
68
- <motion.div
69
- className={clsx(`${name}-expand-btn`, theme)}
70
- key="expand"
71
- onClick={() => setExpanded(true)}
72
- initial={{ x: -100 }}
73
- animate={{ x: 0, transition: { duration: 0.5 } }}
74
- >
75
- <Icon
76
- fallback={<img draggable={false} src={expandPNG} className={clsx(`${name}-mask-btn`, theme)} />}
77
- src={disabled ? icons?.expandIconDisable : icons?.expandIcon}
78
- />
79
- </motion.div>
80
- )}
81
- </AnimatePresence>
82
- </ToolbarContext.Provider>
83
- );
84
- }
@@ -1,108 +0,0 @@
1
- import type { HotKey } from "white-web-sdk";
2
-
3
- import React, { useCallback, useContext } from "react";
4
- import { ApplianceNames } from "white-web-sdk";
5
-
6
- import { useTranslation } from "../../../i18n";
7
- import { Icon } from "../../../icons";
8
- import { defaultHotKeys } from "../../../internal";
9
- import { useFastboardApp } from "../../hooks";
10
- import { Icons } from "../icons";
11
- import { ToolbarContext } from "../Toolbar";
12
- import { Button } from "./Button";
13
-
14
- export function renderToolTip(text: string | undefined, hotkey?: HotKey) {
15
- if (!(typeof hotkey === "string")) return text;
16
- return (
17
- <span className="fastboard-toolbar-tooltip">
18
- <span>{text}</span>
19
- <span className="fastboard-toolbar-hotkey">{hotkey.toUpperCase()}</span>
20
- </span>
21
- );
22
- }
23
-
24
- export function ClickerButton() {
25
- const app = useFastboardApp();
26
- const { t } = useTranslation();
27
- const { theme, icons, writable, setAppliance, memberState } = useContext(ToolbarContext);
28
-
29
- const changeAppliance = useCallback(() => setAppliance(ApplianceNames.clicker), [setAppliance]);
30
-
31
- const shortcut = app.hotKeys?.changeToClick;
32
- const appliance = memberState?.currentApplianceName;
33
- const active = appliance === ApplianceNames.clicker;
34
- const disabled = !writable;
35
-
36
- return (
37
- <Button content={renderToolTip(t("clicker"), shortcut)} onClick={changeAppliance} active={active}>
38
- <Icon
39
- fallback={<Icons.Clicker theme={theme} active={active} />}
40
- src={disabled ? icons?.clickerIconDisable : icons?.clickerIcon}
41
- alt="[clicker]"
42
- />
43
- </Button>
44
- );
45
- }
46
-
47
- export function SelectorButton() {
48
- const app = useFastboardApp();
49
- const { t } = useTranslation();
50
- const { theme, icons, writable, setAppliance, memberState } = useContext(ToolbarContext);
51
-
52
- const changeAppliance = useCallback(() => setAppliance(ApplianceNames.selector), [setAppliance]);
53
-
54
- const appliance = memberState?.currentApplianceName;
55
- const active = appliance === ApplianceNames.selector;
56
- const disabled = !writable;
57
- const shortcut = (app.hotKeys || defaultHotKeys).changeToSelector;
58
-
59
- return (
60
- <Button content={renderToolTip(t("selector"), shortcut)} onClick={changeAppliance} active={active}>
61
- <Icon
62
- fallback={<Icons.Selector theme={theme} active={active} />}
63
- src={disabled ? icons?.selectorIconDisable : icons?.selectorIcon}
64
- alt="[selector]"
65
- />
66
- </Button>
67
- );
68
- }
69
-
70
- export function EraserButton() {
71
- const app = useFastboardApp();
72
- const { t } = useTranslation();
73
- const { theme, icons, writable, setAppliance, memberState } = useContext(ToolbarContext);
74
-
75
- const changeAppliance = useCallback(() => setAppliance(ApplianceNames.eraser), [setAppliance]);
76
-
77
- const appliance = memberState?.currentApplianceName;
78
- const active = appliance === ApplianceNames.eraser;
79
- const disabled = !writable;
80
- const shortcut = (app?.hotKeys || defaultHotKeys).changeToEraser;
81
-
82
- return (
83
- <Button content={renderToolTip(t("eraser"), shortcut)} onClick={changeAppliance} active={active}>
84
- <Icon
85
- fallback={<Icons.Eraser theme={theme} active={active} />}
86
- src={disabled ? icons?.eraserIconDisable : icons?.eraserIcon}
87
- alt="[eraser]"
88
- />
89
- </Button>
90
- );
91
- }
92
-
93
- export function CleanButton() {
94
- const { t } = useTranslation();
95
- const { theme, icons, writable, cleanCurrentScene } = useContext(ToolbarContext);
96
-
97
- const disabled = !writable;
98
-
99
- return (
100
- <Button content={t("clean")} onClick={cleanCurrentScene}>
101
- <Icon
102
- fallback={<Icons.Clean theme={theme} />}
103
- src={disabled ? icons?.cleanIconDisable : icons?.cleanIcon}
104
- alt="[clean]"
105
- />
106
- </Button>
107
- );
108
- }
@@ -1,134 +0,0 @@
1
- import clsx from "clsx";
2
- import Tippy from "@tippyjs/react";
3
- import React, { useContext } from "react";
4
-
5
- import vscodePNG from "./assets/vscode.png";
6
- import geogebraPNG from "./assets/geogebra.png";
7
- import countdownPNG from "./assets/countdown.png";
8
-
9
- import { Icon } from "../../../icons";
10
- import { RightOffset } from "../../../theme";
11
- import { Icons } from "../icons";
12
- import { ToolbarContext } from "../Toolbar";
13
- import { Button } from "./Button";
14
- import { useFastboardApp } from "../../hooks";
15
- import { Loading } from "../icons/Loading";
16
-
17
- export interface AppsButtonProps {
18
- content?: React.ReactNode;
19
- onClick?: () => void;
20
- }
21
-
22
- export function AppsButton({ content, onClick }: AppsButtonProps) {
23
- const { theme, icons, writable } = useContext(ToolbarContext);
24
-
25
- const disabled = !writable;
26
-
27
- const button = (
28
- <Button content="Apps" onClick={onClick}>
29
- <Icon
30
- fallback={<Icons.Apps theme={theme} />}
31
- src={disabled ? icons?.appsIconDisable : icons?.appsIcon}
32
- alt="[apps]"
33
- />
34
- </Button>
35
- );
36
-
37
- return content === false ? (
38
- button
39
- ) : (
40
- <span className="fastboard-toolbar-btn-interactive">
41
- <Tippy
42
- className="fastboard-tip"
43
- content={renderAppsButtonContent(content)}
44
- theme={theme}
45
- placement="right-end"
46
- trigger="click"
47
- offset={RightOffset}
48
- arrow={false}
49
- interactive
50
- >
51
- {button}
52
- </Tippy>
53
- </span>
54
- );
55
- }
56
-
57
- function renderAppsButtonContent(content?: React.ReactNode) {
58
- return (
59
- <div className="fastboard-toolbar-panel apps">
60
- <div className="fastboard-toolbar-apps">{content || <DefaultApps />}</div>
61
- </div>
62
- );
63
- }
64
-
65
- function DefaultApps() {
66
- const app = useFastboardApp();
67
- const { appsStatus } = useContext(ToolbarContext);
68
-
69
- return (
70
- <>
71
- <AppIcon
72
- title="Code Editor"
73
- src={vscodePNG}
74
- alt="[code editor]"
75
- appStatus={appsStatus["Monaco"]}
76
- onClick={app?.insertCodeEditor.bind(app)}
77
- />
78
- <AppIcon
79
- title="GeoGebra"
80
- src={geogebraPNG}
81
- alt="[geogebra]"
82
- appStatus={appsStatus["GeoGebra"]}
83
- onClick={app?.insertGeoGebra.bind(app)}
84
- />
85
- <AppIcon
86
- title="Countdown"
87
- src={countdownPNG}
88
- alt="[countdown]"
89
- appStatus={appsStatus["Countdown"]}
90
- onClick={app?.insertCountdown.bind(app)}
91
- />
92
- </>
93
- );
94
- }
95
-
96
- interface AppIconProps {
97
- title: string;
98
- src: string;
99
- alt: string;
100
- appStatus?: {
101
- status: "idle" | "loading" | "failed";
102
- reason?: string | undefined;
103
- };
104
- onClick?: () => void;
105
- }
106
-
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
-
114
- return (
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>
133
- );
134
- }
@@ -1,45 +0,0 @@
1
- import type { PropsWithChildren } from "react";
2
-
3
- import clsx from "clsx";
4
- import React, { forwardRef, useContext } from "react";
5
- import Tippy from "@tippyjs/react";
6
-
7
- import { RightOffset } from "../../../theme";
8
- import { ToolbarContext } from "../Toolbar";
9
-
10
- type ButtonProps = PropsWithChildren<{
11
- content: React.ReactNode;
12
- disabled?: boolean;
13
- active?: boolean;
14
- onClick?: () => void;
15
- interactive?: boolean;
16
- placement?: "top" | "right"; // not using tippy.js's placement to satisfy dts
17
- }>;
18
-
19
- export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
20
- const { content, disabled, active, onClick, interactive, placement = "right", children } = props;
21
- const { writable, theme } = useContext(ToolbarContext);
22
-
23
- return (
24
- <Tippy
25
- className="fastboard-tip"
26
- content={content}
27
- interactive={interactive}
28
- theme={theme}
29
- disabled={disabled || !writable}
30
- placement={placement}
31
- offset={placement.includes("right") ? RightOffset : undefined}
32
- delay={[1000, 400]}
33
- duration={300}
34
- >
35
- <button
36
- ref={ref}
37
- className={clsx("fastboard-toolbar-btn", theme, { active })}
38
- onClick={onClick}
39
- disabled={disabled || !writable}
40
- >
41
- {children}
42
- </button>
43
- </Tippy>
44
- );
45
- });