@netless/fastboard 0.0.9 → 0.1.1

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 (108) hide show
  1. package/LICENSE.txt +1 -1
  2. package/dist/index.js +428 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.mjs +395 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +17 -79
  7. package/src/base.ts +55 -0
  8. package/src/core.ts +322 -0
  9. package/src/emitter.ts +21 -0
  10. package/src/index.ts +70 -24
  11. package/src/{behaviors/register-apps.ts → register-apps.ts} +6 -14
  12. package/src/utils.ts +74 -0
  13. package/src/value.ts +74 -0
  14. package/README.md +0 -126
  15. package/dist/index.cjs.js +0 -14
  16. package/dist/index.cjs.js.map +0 -1
  17. package/dist/index.es.js +0 -2622
  18. package/dist/index.es.js.map +0 -1
  19. package/dist/svelte.cjs.js +0 -2
  20. package/dist/svelte.cjs.js.map +0 -1
  21. package/dist/svelte.es.js +0 -31
  22. package/dist/svelte.es.js.map +0 -1
  23. package/dist/vue.cjs.js +0 -2
  24. package/dist/vue.cjs.js.map +0 -1
  25. package/dist/vue.es.js +0 -42
  26. package/dist/vue.es.js.map +0 -1
  27. package/src/WhiteboardApp.ts +0 -146
  28. package/src/behaviors/style.ts +0 -17
  29. package/src/components/PageControl.scss +0 -80
  30. package/src/components/PageControl.tsx +0 -181
  31. package/src/components/PlayerControl/PlayerControl.scss +0 -145
  32. package/src/components/PlayerControl/PlayerControl.tsx +0 -158
  33. package/src/components/PlayerControl/components/Button.tsx +0 -55
  34. package/src/components/PlayerControl/hooks.ts +0 -88
  35. package/src/components/PlayerControl/icons/Loading.tsx +0 -13
  36. package/src/components/PlayerControl/icons/Pause.tsx +0 -13
  37. package/src/components/PlayerControl/icons/Play.tsx +0 -13
  38. package/src/components/PlayerControl/icons/index.ts +0 -10
  39. package/src/components/PlayerControl/index.ts +0 -1
  40. package/src/components/RedoUndo.scss +0 -56
  41. package/src/components/RedoUndo.tsx +0 -95
  42. package/src/components/Root.scss +0 -55
  43. package/src/components/Root.tsx +0 -61
  44. package/src/components/Toolbar/Content.tsx +0 -93
  45. package/src/components/Toolbar/Toolbar.scss +0 -247
  46. package/src/components/Toolbar/Toolbar.tsx +0 -82
  47. package/src/components/Toolbar/components/ApplianceButtons.tsx +0 -132
  48. package/src/components/Toolbar/components/AppsButton.tsx +0 -106
  49. package/src/components/Toolbar/components/Button.tsx +0 -54
  50. package/src/components/Toolbar/components/ColorBox.tsx +0 -56
  51. package/src/components/Toolbar/components/CutLine.tsx +0 -8
  52. package/src/components/Toolbar/components/PencilButton.tsx +0 -70
  53. package/src/components/Toolbar/components/ShapesButton.tsx +0 -143
  54. package/src/components/Toolbar/components/Slider.tsx +0 -27
  55. package/src/components/Toolbar/components/TextButton.tsx +0 -66
  56. package/src/components/Toolbar/components/UpDownButtons.tsx +0 -49
  57. package/src/components/Toolbar/components/assets/cocos.png +0 -0
  58. package/src/components/Toolbar/components/assets/countdown.png +0 -0
  59. package/src/components/Toolbar/components/assets/geogebra.png +0 -0
  60. package/src/components/Toolbar/components/assets/vscode.png +0 -0
  61. package/src/components/Toolbar/const.ts +0 -32
  62. package/src/components/Toolbar/hooks.ts +0 -113
  63. package/src/components/Toolbar/icons/Apps.tsx +0 -16
  64. package/src/components/Toolbar/icons/Arrow.tsx +0 -16
  65. package/src/components/Toolbar/icons/Circle.tsx +0 -21
  66. package/src/components/Toolbar/icons/Clean.tsx +0 -16
  67. package/src/components/Toolbar/icons/Clicker.tsx +0 -19
  68. package/src/components/Toolbar/icons/Collapse.tsx +0 -17
  69. package/src/components/Toolbar/icons/Diamond.tsx +0 -17
  70. package/src/components/Toolbar/icons/Down.tsx +0 -17
  71. package/src/components/Toolbar/icons/Eraser.tsx +0 -16
  72. package/src/components/Toolbar/icons/Expand.tsx +0 -17
  73. package/src/components/Toolbar/icons/Line.tsx +0 -13
  74. package/src/components/Toolbar/icons/Pencil.tsx +0 -16
  75. package/src/components/Toolbar/icons/Rectangle.tsx +0 -13
  76. package/src/components/Toolbar/icons/Selector.tsx +0 -16
  77. package/src/components/Toolbar/icons/SpeechBalloon.tsx +0 -17
  78. package/src/components/Toolbar/icons/Star.tsx +0 -17
  79. package/src/components/Toolbar/icons/Text.tsx +0 -16
  80. package/src/components/Toolbar/icons/Triangle.tsx +0 -17
  81. package/src/components/Toolbar/icons/Up.tsx +0 -17
  82. package/src/components/Toolbar/icons/index.ts +0 -42
  83. package/src/components/Toolbar/index.ts +0 -1
  84. package/src/components/ZoomControl.scss +0 -80
  85. package/src/components/ZoomControl.tsx +0 -221
  86. package/src/i18n/en.json +0 -31
  87. package/src/i18n/index.ts +0 -22
  88. package/src/i18n/zh-CN.json +0 -32
  89. package/src/icons/ChevronLeft.tsx +0 -21
  90. package/src/icons/ChevronRight.tsx +0 -21
  91. package/src/icons/FilePlus.tsx +0 -18
  92. package/src/icons/Minus.tsx +0 -21
  93. package/src/icons/Plus.tsx +0 -21
  94. package/src/icons/Redo.tsx +0 -24
  95. package/src/icons/Reset.tsx +0 -23
  96. package/src/icons/Undo.tsx +0 -24
  97. package/src/icons/index.tsx +0 -11
  98. package/src/internal/Instance.tsx +0 -275
  99. package/src/internal/helpers.ts +0 -86
  100. package/src/internal/hooks.ts +0 -9
  101. package/src/internal/index.ts +0 -3
  102. package/src/internal/mount-whiteboard.ts +0 -90
  103. package/src/react.tsx +0 -26
  104. package/src/style.scss +0 -35
  105. package/src/svelte.ts +0 -45
  106. package/src/theme/index.ts +0 -36
  107. package/src/types/index.ts +0 -22
  108. package/src/vue.ts +0 -74
@@ -1,93 +0,0 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
2
-
3
- import { useInstance, clamp } from "../../internal";
4
- import { name } from "./Toolbar";
5
- import { ItemHeight, ItemsCount, MaxHeight, MinHeight } from "./const";
6
- import { DownButton, UpButton } from "./components/UpDownButtons";
7
- import {
8
- CleanButton,
9
- ClickerButton,
10
- EraserButton,
11
- SelectorButton,
12
- } from "./components/ApplianceButtons";
13
- import { AppsButton } from "./components/AppsButton";
14
- import { PencilButton } from "./components/PencilButton";
15
- import { TextButton } from "./components/TextButton";
16
- import { ShapesButton } from "./components/ShapesButton";
17
-
18
- export function Content() {
19
- const app = useInstance();
20
- const ref = useRef<HTMLDivElement>(null);
21
- const [scrollTop, setScrollTop] = useState(0);
22
- const [parentHeight, setParentHeight] = useState(0);
23
-
24
- const hasAppButton = app?.config.toolbar?.apps?.enable ?? true;
25
- const needScroll = parentHeight < ItemHeight * ItemsCount + 48;
26
- const sectionHeight = clamp(
27
- parentHeight - 48 * (needScroll ? 3 : 1),
28
- MinHeight,
29
- MaxHeight
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
- );
41
-
42
- useEffect(() => {
43
- if (ref.current) {
44
- ref.current.scrollTop = scrollTop;
45
- }
46
- }, [scrollTop]);
47
-
48
- useEffect(() => {
49
- const container = ref.current?.parentElement?.parentElement;
50
- if (container) {
51
- const { paddingTop, paddingBottom } = getComputedStyle(container);
52
- const padding = parseInt(paddingTop) + parseInt(paddingBottom) || 0;
53
- const resizeObserver = new ResizeObserver(() => {
54
- setParentHeight(container.getBoundingClientRect().height - padding);
55
- });
56
- resizeObserver.observe(container);
57
- return () => resizeObserver.disconnect();
58
- }
59
- }, []);
60
-
61
- return (
62
- <>
63
- {needScroll && (
64
- <UpButton scrollTo={scrollTo} disabled={disableScrollUp} />
65
- )}
66
- <div
67
- ref={ref}
68
- className={`${name}-section`}
69
- style={{
70
- height: `${sectionHeight}px`,
71
- overflow: needScroll ? "hidden" : "visible",
72
- }}
73
- >
74
- <ClickerButton />
75
- <SelectorButton />
76
- <PencilButton />
77
- <TextButton />
78
- <ShapesButton />
79
- <EraserButton />
80
- <CleanButton />
81
- {hasAppButton && (
82
- <AppsButton
83
- content={app?.config.toolbar?.apps?.content}
84
- onClick={app?.config.toolbar?.apps?.onClick}
85
- />
86
- )}
87
- </div>
88
- {needScroll && (
89
- <DownButton scrollTo={scrollTo} disabled={disableScrollDown} />
90
- )}
91
- </>
92
- );
93
- }
@@ -1,247 +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(2px);
13
- -webkit-backdrop-filter: blur(2px);
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($color: #fff, $alpha: 0.85);
40
- border: 1px solid rgba(0, 0, 0, 0.15);
41
- }
42
-
43
- &.dark {
44
- color: #ddd;
45
- background-color: rgba($color: #333, $alpha: 0.85);
46
- border: 1px solid rgba(0, 0, 0, 0.45);
47
- }
48
-
49
- &-tooltip {
50
- display: inline-flex;
51
- align-items: center;
52
- gap: 4px;
53
- }
54
-
55
- &-hotkey {
56
- margin-right: -4px;
57
- width: 24px;
58
- height: 24px;
59
- border-radius: 4px;
60
- background-color: rgba($color: #fff, $alpha: 0.1);
61
- display: inline-flex;
62
- align-items: center;
63
- justify-content: center;
64
- }
65
-
66
- &-btn {
67
- appearance: none;
68
- cursor: pointer;
69
- margin: 0;
70
- border: 0;
71
- padding: 4px;
72
- width: 32px;
73
- height: 32px;
74
- background-color: transparent;
75
- border-radius: 4px;
76
- font-size: 24px;
77
- line-height: 1;
78
- position: relative;
79
-
80
- &-interactive {
81
- display: inline-block;
82
- width: 32px;
83
- height: 32px;
84
- }
85
-
86
- svg,
87
- img {
88
- width: 1em;
89
- height: 1em;
90
- }
91
-
92
- &:disabled {
93
- opacity: 0.5;
94
- cursor: not-allowed;
95
- }
96
-
97
- &.light:not(:disabled):hover {
98
- background-color: rgba(51, 129, 255, 0.1);
99
- }
100
-
101
- &.dark:not(:disabled):hover {
102
- background-color: rgba(51, 129, 255, 0.25);
103
- }
104
- }
105
-
106
- &-triangle {
107
- width: 0px;
108
- height: 0px;
109
- border-bottom: 4px solid;
110
- border-left: 4px solid transparent;
111
- position: absolute;
112
- bottom: 0;
113
- right: 0;
114
- }
115
-
116
- &-cut-line {
117
- display: inline-block;
118
- height: 0.5px;
119
- width: 100%;
120
-
121
- &.light {
122
- background-color: #e7e7e7;
123
- }
124
-
125
- &.dark {
126
- background-color: rgba(255, 255, 255, 0.15);
127
- }
128
- }
129
-
130
- &-section {
131
- display: inline-flex;
132
- flex-flow: column nowrap;
133
- gap: 4px;
134
- scroll-behavior: smooth;
135
- }
136
-
137
- &-panel {
138
- width: 136px - 8px * 2;
139
- padding: 0;
140
- display: flex;
141
- flex-flow: column nowrap;
142
- align-items: center;
143
- gap: 8px;
144
-
145
- &.apps {
146
- width: 240px - 8px * 2;
147
- }
148
- }
149
-
150
- &-color-box,
151
- &-shapes {
152
- display: grid;
153
- grid-template-columns: repeat(4, 1fr);
154
- gap: 8px;
155
- align-items: center;
156
- justify-items: center;
157
-
158
- .#{$name}-btn {
159
- padding: 0;
160
- width: 24px;
161
- height: 24px;
162
- }
163
- }
164
-
165
- &-apps {
166
- width: 100%;
167
- display: grid;
168
- grid-template-columns: repeat(3, 1fr);
169
- gap: 8px;
170
- align-items: center;
171
- justify-items: center;
172
-
173
- .#{$name}-btn {
174
- width: 40px;
175
- height: 40px;
176
- font-size: 40px;
177
- }
178
- }
179
-
180
- &-app-icon {
181
- padding-top: 4px;
182
- display: inline-flex;
183
- flex-flow: column nowrap;
184
- align-items: center;
185
- gap: 4px;
186
-
187
- .fastboard-toolbar-btn {
188
- padding: 0;
189
- }
190
-
191
- &-text {
192
- font-size: 12px;
193
- color: #5d5d5d;
194
- overflow: hidden;
195
- white-space: nowrap;
196
- text-overflow: ellipsis;
197
- }
198
- }
199
-
200
- &-color-item {
201
- width: 24px;
202
- height: 24px;
203
- border-radius: 4px;
204
- cursor: pointer;
205
-
206
- *.light:hover {
207
- background-color: #f5f5f5;
208
- }
209
-
210
- *.dark:hover {
211
- background-color: #333;
212
- }
213
- }
214
-
215
- &-color-border {
216
- width: 24px;
217
- height: 24px;
218
- border: 1px solid transparent;
219
- border-radius: 4px;
220
- display: inline-flex;
221
- align-items: center;
222
- justify-content: center;
223
-
224
- &.active.light {
225
- border: 1px solid rgba(51, 129, 255, 0.8);
226
- }
227
-
228
- &.active.dark {
229
- border: 1px solid rgba(51, 129, 255, 0.8);
230
- }
231
- }
232
-
233
- &-color-btn {
234
- margin: 0;
235
- border: 1px solid rgba(0, 0, 0, 0.24);
236
- padding: 0;
237
- appearance: none;
238
- width: 16px;
239
- height: 16px;
240
- border-radius: 4px;
241
- cursor: pointer;
242
-
243
- &:focus-visible {
244
- outline-offset: 2px;
245
- }
246
- }
247
- }
@@ -1,82 +0,0 @@
1
- import type { CommonProps, GenericIcon, Theme } from "../../types";
2
- import type { i18n } from "i18next";
3
-
4
- import clsx from "clsx";
5
- import React, { createContext, useCallback, useState } from "react";
6
-
7
- import { Icon } from "../../icons";
8
- import { Icons } from "./icons";
9
- import { Button } from "./components/Button";
10
- import { CutLine } from "./components/CutLine";
11
- import { EmptyToolbarHook, useToolbar, type ToolbarHook } from "./hooks";
12
- import { Content } from "./Content";
13
-
14
- export type ToolbarProps = CommonProps & {
15
- icons?: GenericIcon<
16
- | "clicker"
17
- | "selector"
18
- | "pencil"
19
- | "eraser"
20
- | "clean"
21
- | "expand"
22
- | "collapse"
23
- | "up"
24
- | "down"
25
- | "text"
26
- | "apps"
27
- >;
28
- };
29
-
30
- type ToolbarContextType = ToolbarHook & {
31
- theme: Theme;
32
- icons?: ToolbarProps["icons"];
33
- i18n?: i18n | null;
34
- };
35
-
36
- export const ToolbarContext = createContext<ToolbarContextType>({
37
- theme: "light",
38
- ...EmptyToolbarHook,
39
- });
40
-
41
- export const name = "fastboard-toolbar";
42
-
43
- export const Toolbar = ({
44
- theme = "light",
45
- icons,
46
- room,
47
- i18n,
48
- }: ToolbarProps) => {
49
- const [expanded, setExpanded] = useState(true);
50
- const hook = useToolbar(room);
51
- const toggle = useCallback(() => setExpanded(e => !e), []);
52
-
53
- const disabled = !hook.writable;
54
-
55
- return (
56
- <ToolbarContext.Provider value={{ theme, icons, ...hook, i18n }}>
57
- <div className={clsx(name, theme)}>
58
- {expanded ? (
59
- <Button content={i18n?.t("collapse")} onClick={toggle}>
60
- <Icon
61
- fallback={<Icons.Collapse theme={theme} />}
62
- src={disabled ? icons?.collapseIconDisable : icons?.collapseIcon}
63
- />
64
- </Button>
65
- ) : (
66
- <Button content={i18n?.t("expand")} onClick={toggle}>
67
- <Icon
68
- fallback={<Icons.Expand theme={theme} />}
69
- src={disabled ? icons?.expandIconDisable : icons?.expandIcon}
70
- />
71
- </Button>
72
- )}
73
- {expanded && (
74
- <>
75
- <CutLine />
76
- <Content />
77
- </>
78
- )}
79
- </div>
80
- </ToolbarContext.Provider>
81
- );
82
- };
@@ -1,132 +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 { defaultHotKeys, useInstance } from "../../../internal";
7
- import { Icon } from "../../../icons";
8
- import { Icons } from "../icons";
9
- import { ToolbarContext } from "../Toolbar";
10
- import { Button } from "./Button";
11
-
12
- export function renderToolTip(text: string | undefined, hotkey?: HotKey) {
13
- if (!(typeof hotkey === "string")) return text;
14
- return (
15
- <span className="fastboard-toolbar-tooltip">
16
- <span>{text}</span>
17
- <span className="fastboard-toolbar-hotkey">{hotkey.toUpperCase()}</span>
18
- </span>
19
- );
20
- }
21
-
22
- export function ClickerButton() {
23
- const app = useInstance();
24
-
25
- const { theme, icons, writable, setAppliance, memberState, i18n } =
26
- useContext(ToolbarContext);
27
-
28
- const changeAppliance = useCallback(
29
- () => setAppliance(ApplianceNames.clicker),
30
- [setAppliance]
31
- );
32
-
33
- const shortcut = app?.config.joinRoom.hotKeys?.changeToClick;
34
- const appliance = memberState?.currentApplianceName;
35
- const active = appliance === ApplianceNames.clicker;
36
- const disabled = !writable;
37
-
38
- return (
39
- <Button
40
- content={renderToolTip(i18n?.t("clicker"), shortcut)}
41
- onClick={changeAppliance}
42
- active={active}
43
- >
44
- <Icon
45
- fallback={<Icons.Clicker theme={theme} active={active} />}
46
- src={disabled ? icons?.clickerIconDisable : icons?.clickerIcon}
47
- alt="[clicker]"
48
- />
49
- </Button>
50
- );
51
- }
52
-
53
- export function SelectorButton() {
54
- const app = useInstance();
55
-
56
- const { theme, icons, writable, setAppliance, memberState, i18n } =
57
- useContext(ToolbarContext);
58
-
59
- const changeAppliance = useCallback(
60
- () => setAppliance(ApplianceNames.selector),
61
- [setAppliance]
62
- );
63
-
64
- const appliance = memberState?.currentApplianceName;
65
- const active = appliance === ApplianceNames.selector;
66
- const disabled = !writable;
67
- const shortcut = (app?.config.joinRoom.hotKeys || defaultHotKeys)
68
- .changeToSelector;
69
-
70
- return (
71
- <Button
72
- content={renderToolTip(i18n?.t("selector"), shortcut)}
73
- onClick={changeAppliance}
74
- active={active}
75
- >
76
- <Icon
77
- fallback={<Icons.Selector theme={theme} active={active} />}
78
- src={disabled ? icons?.selectorIconDisable : icons?.selectorIcon}
79
- alt="[selector]"
80
- />
81
- </Button>
82
- );
83
- }
84
-
85
- export function EraserButton() {
86
- const app = useInstance();
87
-
88
- const { theme, icons, writable, setAppliance, memberState, i18n } =
89
- useContext(ToolbarContext);
90
-
91
- const changeAppliance = useCallback(
92
- () => setAppliance(ApplianceNames.eraser),
93
- [setAppliance]
94
- );
95
-
96
- const appliance = memberState?.currentApplianceName;
97
- const active = appliance === ApplianceNames.eraser;
98
- const disabled = !writable;
99
- const shortcut = (app?.config.joinRoom.hotKeys || defaultHotKeys)
100
- .changeToEraser;
101
-
102
- return (
103
- <Button
104
- content={renderToolTip(i18n?.t("eraser"), shortcut)}
105
- onClick={changeAppliance}
106
- active={active}
107
- >
108
- <Icon
109
- fallback={<Icons.Eraser theme={theme} active={active} />}
110
- src={disabled ? icons?.eraserIconDisable : icons?.eraserIcon}
111
- alt="[eraser]"
112
- />
113
- </Button>
114
- );
115
- }
116
-
117
- export function CleanButton() {
118
- const { theme, icons, writable, cleanCurrentScene, i18n } =
119
- useContext(ToolbarContext);
120
-
121
- const disabled = !writable;
122
-
123
- return (
124
- <Button content={i18n?.t("clean")} onClick={cleanCurrentScene}>
125
- <Icon
126
- fallback={<Icons.Clean theme={theme} />}
127
- src={disabled ? icons?.cleanIconDisable : icons?.cleanIcon}
128
- alt="[clean]"
129
- />
130
- </Button>
131
- );
132
- }
@@ -1,106 +0,0 @@
1
- import Tippy from "@tippyjs/react";
2
- import React, { useContext } from "react";
3
-
4
- import vscodePNG from "./assets/vscode.png";
5
- import geogebraPNG from "./assets/geogebra.png";
6
- import countdownPNG from "./assets/countdown.png";
7
-
8
- import { useInstance } from "../../../internal";
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
-
15
- export interface AppsButtonProps {
16
- content?: React.ReactNode;
17
- onClick?: () => void;
18
- }
19
-
20
- export function AppsButton({ content, onClick }: AppsButtonProps) {
21
- const { theme, icons, writable } = useContext(ToolbarContext);
22
-
23
- const disabled = !writable;
24
-
25
- const button = (
26
- <Button content="Apps" onClick={onClick}>
27
- <Icon
28
- fallback={<Icons.Apps theme={theme} />}
29
- src={disabled ? icons?.appsIconDisable : icons?.appsIcon}
30
- alt="[apps]"
31
- />
32
- </Button>
33
- );
34
-
35
- return content === false ? (
36
- button
37
- ) : (
38
- <span className="fastboard-toolbar-btn-interactive">
39
- <Tippy
40
- className="fastboard-tip"
41
- content={renderAppsButtonContent(content)}
42
- theme={theme}
43
- placement="right-end"
44
- trigger="click"
45
- offset={RightOffset}
46
- arrow={false}
47
- interactive
48
- >
49
- {button}
50
- </Tippy>
51
- </span>
52
- );
53
- }
54
-
55
- function renderAppsButtonContent(content?: React.ReactNode) {
56
- return (
57
- <div className="fastboard-toolbar-panel apps">
58
- <div className="fastboard-toolbar-apps">{content || <DefaultApps />}</div>
59
- </div>
60
- );
61
- }
62
-
63
- function DefaultApps() {
64
- const app = useInstance();
65
-
66
- return (
67
- <>
68
- <AppIcon
69
- title="Code Editor"
70
- src={vscodePNG}
71
- alt="[code editor]"
72
- onClick={app?.insertCodeEditor.bind(app)}
73
- />
74
- <AppIcon
75
- title="GeoGebra"
76
- src={geogebraPNG}
77
- alt="[geogebra]"
78
- onClick={app?.insertGeoGebra.bind(app)}
79
- />
80
- <AppIcon
81
- title="Countdown"
82
- src={countdownPNG}
83
- alt="[countdown]"
84
- onClick={app?.insertCountdown.bind(app)}
85
- />
86
- </>
87
- );
88
- }
89
-
90
- interface AppIconProps {
91
- title: string;
92
- src: string;
93
- alt: string;
94
- onClick?: () => void;
95
- }
96
-
97
- function AppIcon({ title, src, alt, onClick }: AppIconProps) {
98
- return (
99
- <span className="fastboard-toolbar-app-icon">
100
- <Button placement="top" content={title} onClick={onClick}>
101
- <img src={src} alt={alt} title={title} />
102
- </Button>
103
- <span className="fastboard-toolbar-app-icon-text">{title}</span>
104
- </span>
105
- );
106
- }
@@ -1,54 +0,0 @@
1
- import type { Placement } from "tippy.js";
2
-
3
- import clsx from "clsx";
4
- import React, { forwardRef, useContext, type PropsWithChildren } 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?: Placement;
17
- }>;
18
-
19
- export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
20
- (props, ref) => {
21
- const {
22
- content,
23
- disabled,
24
- active,
25
- onClick,
26
- interactive,
27
- placement = "right",
28
- children,
29
- } = props;
30
- const { writable, theme } = useContext(ToolbarContext);
31
-
32
- return (
33
- <Tippy
34
- className="fastboard-tip"
35
- content={content}
36
- interactive={interactive}
37
- theme={theme}
38
- disabled={disabled || !writable}
39
- placement={placement}
40
- offset={placement.includes("right") ? RightOffset : undefined}
41
- duration={300}
42
- >
43
- <button
44
- ref={ref}
45
- className={clsx("fastboard-toolbar-btn", theme, { active })}
46
- onClick={onClick}
47
- disabled={disabled || !writable}
48
- >
49
- {children}
50
- </button>
51
- </Tippy>
52
- );
53
- }
54
- );