@netless/fastboard-react 0.1.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 (163) hide show
  1. package/LICENSE.txt +21 -0
  2. package/dist/behaviors/style.d.ts +1 -0
  3. package/dist/components/Fastboard.d.ts +15 -0
  4. package/dist/components/PageControl/PageControl.d.ts +5 -0
  5. package/dist/components/PageControl/hooks.d.ts +9 -0
  6. package/dist/components/PageControl/index.d.ts +2 -0
  7. package/dist/components/PlayerControl/PlayerControl.d.ts +9 -0
  8. package/dist/components/PlayerControl/hooks.d.ts +11 -0
  9. package/dist/components/PlayerControl/icons/Loading.d.ts +3 -0
  10. package/dist/components/PlayerControl/icons/Pause.d.ts +3 -0
  11. package/dist/components/PlayerControl/icons/Play.d.ts +3 -0
  12. package/dist/components/PlayerControl/icons/index.d.ts +6 -0
  13. package/dist/components/PlayerControl/index.d.ts +2 -0
  14. package/dist/components/RedoUndo/RedoUndo.d.ts +5 -0
  15. package/dist/components/RedoUndo/hooks.d.ts +6 -0
  16. package/dist/components/RedoUndo/index.d.ts +2 -0
  17. package/dist/components/Toolbar/Content.d.ts +2 -0
  18. package/dist/components/Toolbar/Toolbar.d.ts +14 -0
  19. package/dist/components/Toolbar/components/ApplianceButtons.d.ts +7 -0
  20. package/dist/components/Toolbar/components/AppsButton.d.ts +6 -0
  21. package/dist/components/Toolbar/components/ColorBox.d.ts +2 -0
  22. package/dist/components/Toolbar/components/CutLine.d.ts +2 -0
  23. package/dist/components/Toolbar/components/Mask.d.ts +7 -0
  24. package/dist/components/Toolbar/components/PencilButton.d.ts +2 -0
  25. package/dist/components/Toolbar/components/ShapesButton.d.ts +3 -0
  26. package/dist/components/Toolbar/components/Slider.d.ts +2 -0
  27. package/dist/components/Toolbar/components/TextButton.d.ts +2 -0
  28. package/dist/components/Toolbar/components/UpDownButtons.d.ts +7 -0
  29. package/dist/components/Toolbar/const.d.ts +18 -0
  30. package/dist/components/Toolbar/hooks.d.ts +12 -0
  31. package/dist/components/Toolbar/icons/Apps.d.ts +3 -0
  32. package/dist/components/Toolbar/icons/Arrow.d.ts +3 -0
  33. package/dist/components/Toolbar/icons/Circle.d.ts +3 -0
  34. package/dist/components/Toolbar/icons/Clean.d.ts +3 -0
  35. package/dist/components/Toolbar/icons/Clicker.d.ts +3 -0
  36. package/dist/components/Toolbar/icons/Collapse.d.ts +3 -0
  37. package/dist/components/Toolbar/icons/Diamond.d.ts +3 -0
  38. package/dist/components/Toolbar/icons/Down.d.ts +3 -0
  39. package/dist/components/Toolbar/icons/Eraser.d.ts +3 -0
  40. package/dist/components/Toolbar/icons/Expand.d.ts +3 -0
  41. package/dist/components/Toolbar/icons/Line.d.ts +3 -0
  42. package/dist/components/Toolbar/icons/Pencil.d.ts +3 -0
  43. package/dist/components/Toolbar/icons/Rectangle.d.ts +3 -0
  44. package/dist/components/Toolbar/icons/Selector.d.ts +3 -0
  45. package/dist/components/Toolbar/icons/SpeechBalloon.d.ts +3 -0
  46. package/dist/components/Toolbar/icons/Star.d.ts +3 -0
  47. package/dist/components/Toolbar/icons/Text.d.ts +3 -0
  48. package/dist/components/Toolbar/icons/Triangle.d.ts +3 -0
  49. package/dist/components/Toolbar/icons/Up.d.ts +3 -0
  50. package/dist/components/Toolbar/icons/index.d.ts +22 -0
  51. package/dist/components/Toolbar/index.d.ts +2 -0
  52. package/dist/components/ZoomControl/ZoomControl.d.ts +5 -0
  53. package/dist/components/ZoomControl/hooks.d.ts +7 -0
  54. package/dist/components/ZoomControl/index.d.ts +2 -0
  55. package/dist/components/hooks.d.ts +13 -0
  56. package/dist/i18n/index.d.ts +12 -0
  57. package/dist/icons/ChevronLeft.d.ts +3 -0
  58. package/dist/icons/ChevronRight.d.ts +3 -0
  59. package/dist/icons/FilePlus.d.ts +3 -0
  60. package/dist/icons/Minus.d.ts +3 -0
  61. package/dist/icons/Plus.d.ts +3 -0
  62. package/dist/icons/Redo.d.ts +3 -0
  63. package/dist/icons/Reset.d.ts +3 -0
  64. package/dist/icons/Undo.d.ts +3 -0
  65. package/dist/icons/index.d.ts +7 -0
  66. package/dist/index.d.ts +9 -0
  67. package/dist/index.js +2116 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/index.mjs +2083 -0
  70. package/dist/index.mjs.map +1 -0
  71. package/dist/internal/helpers.d.ts +16 -0
  72. package/dist/internal/hooks.d.ts +3 -0
  73. package/dist/internal/index.d.ts +2 -0
  74. package/dist/theme.d.ts +16 -0
  75. package/dist/typings.d.ts +10 -0
  76. package/package.json +41 -0
  77. package/src/behaviors/style.ts +4 -0
  78. package/src/components/Fastboard.scss +41 -0
  79. package/src/components/Fastboard.tsx +97 -0
  80. package/src/components/PageControl/PageControl.scss +80 -0
  81. package/src/components/PageControl/PageControl.tsx +105 -0
  82. package/src/components/PageControl/hooks.ts +67 -0
  83. package/src/components/PageControl/index.ts +2 -0
  84. package/src/components/PlayerControl/PlayerControl.scss +145 -0
  85. package/src/components/PlayerControl/PlayerControl.tsx +131 -0
  86. package/src/components/PlayerControl/components/Button.tsx +45 -0
  87. package/src/components/PlayerControl/hooks.ts +88 -0
  88. package/src/components/PlayerControl/icons/Loading.tsx +13 -0
  89. package/src/components/PlayerControl/icons/Pause.tsx +13 -0
  90. package/src/components/PlayerControl/icons/Play.tsx +13 -0
  91. package/src/components/PlayerControl/icons/index.ts +10 -0
  92. package/src/components/PlayerControl/index.ts +2 -0
  93. package/src/components/RedoUndo/RedoUndo.scss +56 -0
  94. package/src/components/RedoUndo/RedoUndo.tsx +76 -0
  95. package/src/components/RedoUndo/hooks.ts +18 -0
  96. package/src/components/RedoUndo/index.ts +2 -0
  97. package/src/components/Toolbar/Content.tsx +74 -0
  98. package/src/components/Toolbar/Toolbar.scss +281 -0
  99. package/src/components/Toolbar/Toolbar.tsx +116 -0
  100. package/src/components/Toolbar/components/ApplianceButtons.tsx +108 -0
  101. package/src/components/Toolbar/components/AppsButton.tsx +101 -0
  102. package/src/components/Toolbar/components/Button.tsx +46 -0
  103. package/src/components/Toolbar/components/ColorBox.tsx +55 -0
  104. package/src/components/Toolbar/components/CutLine.tsx +8 -0
  105. package/src/components/Toolbar/components/Mask.tsx +44 -0
  106. package/src/components/Toolbar/components/PencilButton.tsx +66 -0
  107. package/src/components/Toolbar/components/ShapesButton.tsx +128 -0
  108. package/src/components/Toolbar/components/Slider.tsx +26 -0
  109. package/src/components/Toolbar/components/TextButton.tsx +62 -0
  110. package/src/components/Toolbar/components/UpDownButtons.tsx +49 -0
  111. package/src/components/Toolbar/components/assets/cocos.png +0 -0
  112. package/src/components/Toolbar/components/assets/collapsed.png +0 -0
  113. package/src/components/Toolbar/components/assets/countdown.png +0 -0
  114. package/src/components/Toolbar/components/assets/expanded.png +0 -0
  115. package/src/components/Toolbar/components/assets/geogebra.png +0 -0
  116. package/src/components/Toolbar/components/assets/vscode.png +0 -0
  117. package/src/components/Toolbar/const.ts +32 -0
  118. package/src/components/Toolbar/hooks.ts +68 -0
  119. package/src/components/Toolbar/icons/Apps.tsx +16 -0
  120. package/src/components/Toolbar/icons/Arrow.tsx +13 -0
  121. package/src/components/Toolbar/icons/Circle.tsx +13 -0
  122. package/src/components/Toolbar/icons/Clean.tsx +16 -0
  123. package/src/components/Toolbar/icons/Clicker.tsx +19 -0
  124. package/src/components/Toolbar/icons/Collapse.tsx +13 -0
  125. package/src/components/Toolbar/icons/Diamond.tsx +13 -0
  126. package/src/components/Toolbar/icons/Down.tsx +13 -0
  127. package/src/components/Toolbar/icons/Eraser.tsx +16 -0
  128. package/src/components/Toolbar/icons/Expand.tsx +13 -0
  129. package/src/components/Toolbar/icons/Line.tsx +13 -0
  130. package/src/components/Toolbar/icons/Pencil.tsx +16 -0
  131. package/src/components/Toolbar/icons/Rectangle.tsx +13 -0
  132. package/src/components/Toolbar/icons/Selector.tsx +13 -0
  133. package/src/components/Toolbar/icons/SpeechBalloon.tsx +17 -0
  134. package/src/components/Toolbar/icons/Star.tsx +17 -0
  135. package/src/components/Toolbar/icons/Text.tsx +13 -0
  136. package/src/components/Toolbar/icons/Triangle.tsx +13 -0
  137. package/src/components/Toolbar/icons/Up.tsx +13 -0
  138. package/src/components/Toolbar/icons/index.ts +42 -0
  139. package/src/components/Toolbar/index.ts +2 -0
  140. package/src/components/ZoomControl/ZoomControl.scss +80 -0
  141. package/src/components/ZoomControl/ZoomControl.tsx +94 -0
  142. package/src/components/ZoomControl/hooks.ts +52 -0
  143. package/src/components/ZoomControl/index.ts +2 -0
  144. package/src/components/hooks.ts +59 -0
  145. package/src/i18n/en.json +31 -0
  146. package/src/i18n/index.ts +29 -0
  147. package/src/i18n/zh-CN.json +32 -0
  148. package/src/icons/ChevronLeft.tsx +21 -0
  149. package/src/icons/ChevronRight.tsx +21 -0
  150. package/src/icons/FilePlus.tsx +18 -0
  151. package/src/icons/Minus.tsx +15 -0
  152. package/src/icons/Plus.tsx +15 -0
  153. package/src/icons/Redo.tsx +18 -0
  154. package/src/icons/Reset.tsx +19 -0
  155. package/src/icons/Undo.tsx +18 -0
  156. package/src/icons/index.tsx +11 -0
  157. package/src/index.ts +10 -0
  158. package/src/internal/helpers.ts +31 -0
  159. package/src/internal/hooks.ts +23 -0
  160. package/src/internal/index.ts +2 -0
  161. package/src/style.scss +29 -0
  162. package/src/theme.ts +36 -0
  163. package/src/typings.ts +15 -0
@@ -0,0 +1,101 @@
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 { Icon } from "../../../icons";
9
+ import { RightOffset } from "../../../theme";
10
+ import { Icons } from "../icons";
11
+ import { ToolbarContext } from "../Toolbar";
12
+ import { Button } from "./Button";
13
+ import { useFastboardApp } from "../../hooks";
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 = useFastboardApp();
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 title="GeoGebra" src={geogebraPNG} alt="[geogebra]" onClick={app?.insertGeoGebra.bind(app)} />
75
+ <AppIcon
76
+ title="Countdown"
77
+ src={countdownPNG}
78
+ alt="[countdown]"
79
+ onClick={app?.insertCountdown.bind(app)}
80
+ />
81
+ </>
82
+ );
83
+ }
84
+
85
+ interface AppIconProps {
86
+ title: string;
87
+ src: string;
88
+ alt: string;
89
+ onClick?: () => void;
90
+ }
91
+
92
+ function AppIcon({ title, src, alt, onClick }: AppIconProps) {
93
+ return (
94
+ <span className="fastboard-toolbar-app-icon">
95
+ <Button placement="top" content={title} onClick={onClick}>
96
+ <img src={src} alt={alt} title={title} />
97
+ </Button>
98
+ <span className="fastboard-toolbar-app-icon-text">{title}</span>
99
+ </span>
100
+ );
101
+ }
@@ -0,0 +1,46 @@
1
+ import type { Placement } from "tippy.js";
2
+ import type { PropsWithChildren } from "react";
3
+
4
+ import clsx from "clsx";
5
+ import React, { forwardRef, useContext } from "react";
6
+ import Tippy from "@tippyjs/react";
7
+
8
+ import { RightOffset } from "../../../theme";
9
+ import { ToolbarContext } from "../Toolbar";
10
+
11
+ type ButtonProps = PropsWithChildren<{
12
+ content: React.ReactNode;
13
+ disabled?: boolean;
14
+ active?: boolean;
15
+ onClick?: () => void;
16
+ interactive?: boolean;
17
+ placement?: Placement;
18
+ }>;
19
+
20
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
21
+ const { content, disabled, active, onClick, interactive, placement = "right", children } = props;
22
+ const { writable, theme } = useContext(ToolbarContext);
23
+
24
+ return (
25
+ <Tippy
26
+ className="fastboard-tip"
27
+ content={content}
28
+ interactive={interactive}
29
+ theme={theme}
30
+ disabled={disabled || !writable}
31
+ placement={placement}
32
+ offset={placement.includes("right") ? RightOffset : undefined}
33
+ delay={[1000, 400]}
34
+ duration={300}
35
+ >
36
+ <button
37
+ ref={ref}
38
+ className={clsx("fastboard-toolbar-btn", theme, { active })}
39
+ onClick={onClick}
40
+ disabled={disabled || !writable}
41
+ >
42
+ {children}
43
+ </button>
44
+ </Tippy>
45
+ );
46
+ });
@@ -0,0 +1,55 @@
1
+ import type { Color } from "white-web-sdk";
2
+
3
+ import clsx from "clsx";
4
+ import React, { useContext } from "react";
5
+
6
+ import { isEqualArray } from "../../../internal";
7
+ import { ToolbarContext } from "../Toolbar";
8
+
9
+ const colors: Record<string, Color> = {
10
+ "#E02020": [224, 32, 32],
11
+ "#F7B500": [247, 181, 0],
12
+ "#6DD400": [109, 212, 0],
13
+ "#32C5FF": [50, 197, 255],
14
+ "#0091FF": [0, 145, 255],
15
+ "#6236FF": [98, 54, 255],
16
+ "#B620E0": [182, 32, 224],
17
+ "#6D7278": [109, 114, 120],
18
+ };
19
+
20
+ const colorKeys = Object.keys(colors);
21
+
22
+ export function ColorBox() {
23
+ const { theme, memberState, setStrokeColor, writable } = useContext(ToolbarContext);
24
+
25
+ const strokeColor = memberState?.strokeColor;
26
+ const disabled = !writable;
27
+
28
+ return (
29
+ <div className={clsx("fastboard-toolbar-color-box", theme)}>
30
+ {colorKeys.map((key: string) => (
31
+ <div
32
+ key={key}
33
+ className={clsx("fastboard-toolbar-color-item", theme)}
34
+ onClick={() => setStrokeColor(colors[key])}
35
+ >
36
+ <div
37
+ className={clsx("fastboard-toolbar-color-border", theme, {
38
+ active: strokeColor && isEqualArray(strokeColor, colors[key]),
39
+ })}
40
+ >
41
+ <button
42
+ className={clsx("fastboard-toolbar-color-btn")}
43
+ style={{ background: key }}
44
+ disabled={disabled}
45
+ onClick={ev => {
46
+ ev.stopPropagation();
47
+ setStrokeColor(colors[key]);
48
+ }}
49
+ />
50
+ </div>
51
+ </div>
52
+ ))}
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,8 @@
1
+ import clsx from "clsx";
2
+ import React, { useContext } from "react";
3
+ import { ToolbarContext, name } from "../Toolbar";
4
+
5
+ export function CutLine() {
6
+ const { theme } = useContext(ToolbarContext);
7
+ return <span className={clsx(`${name}-cut-line`, theme)} />;
8
+ }
@@ -0,0 +1,44 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import ReactDOM from "react-dom";
3
+
4
+ interface MaskProps {
5
+ toolbar: HTMLDivElement | null;
6
+ children: React.ReactNode;
7
+ }
8
+
9
+ export const Mask = React.memo(({ toolbar, children }: MaskProps) => {
10
+ const [rootElement] = useState<HTMLDivElement | null>(() => {
11
+ const element = document.createElement("div");
12
+ element.style.position = "absolute";
13
+ return element;
14
+ });
15
+
16
+ useEffect(() => {
17
+ if (toolbar && rootElement) {
18
+ toolbar.appendChild(rootElement);
19
+ }
20
+ }, [rootElement, toolbar]);
21
+
22
+ useEffect(() => {
23
+ if (rootElement && toolbar) {
24
+ toolbar.appendChild(rootElement);
25
+
26
+ const toolbarRect = toolbar.getBoundingClientRect();
27
+ const halfHeight = toolbarRect.height / 2 - 31;
28
+ rootElement.style.top = halfHeight + "px";
29
+ rootElement.style.left = "41px";
30
+ rootElement.style.width = "17px";
31
+ rootElement.style.height = "62px";
32
+
33
+ return () => {
34
+ toolbar.removeChild(rootElement);
35
+ };
36
+ }
37
+ }, [rootElement, toolbar]);
38
+
39
+ if (rootElement) {
40
+ return ReactDOM.createPortal(children, rootElement);
41
+ } else {
42
+ return null;
43
+ }
44
+ });
@@ -0,0 +1,66 @@
1
+ import Tippy from "@tippyjs/react";
2
+ import React, { useCallback, useContext } from "react";
3
+ import { ApplianceNames } from "white-web-sdk";
4
+
5
+ import { useTranslation } from "../../../i18n";
6
+ import { Icon } from "../../../icons";
7
+ import { defaultHotKeys } from "../../../internal";
8
+ import { RightOffset } from "../../../theme";
9
+ import { useFastboardApp } from "../../hooks";
10
+ import { Icons } from "../icons";
11
+ import { ToolbarContext } from "../Toolbar";
12
+ import { renderToolTip } from "./ApplianceButtons";
13
+ import { Button } from "./Button";
14
+ import { ColorBox } from "./ColorBox";
15
+ import { CutLine } from "./CutLine";
16
+ import { Slider } from "./Slider";
17
+
18
+ export function PencilButton() {
19
+ const app = useFastboardApp();
20
+ const { t } = useTranslation();
21
+
22
+ const { theme, icons, writable, setAppliance, memberState } = useContext(ToolbarContext);
23
+
24
+ const changeAppliance = useCallback(() => {
25
+ setAppliance(ApplianceNames.pencil);
26
+ }, [setAppliance]);
27
+
28
+ const appliance = memberState?.currentApplianceName;
29
+ const active = appliance === ApplianceNames.pencil;
30
+ const disabled = !writable;
31
+ const shortcut = (app?.hotKeys || defaultHotKeys).changeToPencil;
32
+
33
+ return (
34
+ <span className="fastboard-toolbar-btn-interactive">
35
+ <Tippy
36
+ className="fastboard-tip"
37
+ content={renderPencilButtonContent()}
38
+ theme={theme}
39
+ placement="right-start"
40
+ trigger="click"
41
+ offset={RightOffset}
42
+ arrow={false}
43
+ interactive
44
+ >
45
+ <Button content={renderToolTip(t("pencil"), shortcut)} active={active} onClick={changeAppliance}>
46
+ <Icon
47
+ fallback={<Icons.Pencil theme={theme} active={active} />}
48
+ src={disabled ? icons?.pencilIconDisable : icons?.pencilIcon}
49
+ alt="[pencil]"
50
+ />
51
+ <span className="fastboard-toolbar-triangle" />
52
+ </Button>
53
+ </Tippy>
54
+ </span>
55
+ );
56
+ }
57
+
58
+ function renderPencilButtonContent() {
59
+ return (
60
+ <div className="fastboard-toolbar-panel pencil">
61
+ <Slider />
62
+ <CutLine />
63
+ <ColorBox />
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,128 @@
1
+ import type { ShapeType } from "white-web-sdk";
2
+ import type { IconProps } from "../../../typings";
3
+
4
+ import Tippy from "@tippyjs/react";
5
+ import React, { useContext } from "react";
6
+ import { ApplianceNames } from "white-web-sdk";
7
+
8
+ import { useTranslation } from "../../../i18n";
9
+ import { RightOffset } from "../../../theme";
10
+ import { ApplianceShapes, Shapes, ShapesMap } from "../const";
11
+ import { Icons } from "../icons";
12
+ import { ToolbarContext } from "../Toolbar";
13
+ import { Button } from "./Button";
14
+ import { ColorBox } from "./ColorBox";
15
+ import { CutLine } from "./CutLine";
16
+ import { Slider } from "./Slider";
17
+
18
+ const ShapeTypes = new Set([...ApplianceShapes, ...Shapes]);
19
+
20
+ export function ShapesButton() {
21
+ const { t } = useTranslation();
22
+ const { theme, memberState } = useContext(ToolbarContext);
23
+
24
+ const appliance = memberState?.currentApplianceName;
25
+ const shape = memberState?.shapeType;
26
+
27
+ const key = (appliance === ApplianceNames.shape ? shape : appliance) as keyof typeof ShapesMap;
28
+
29
+ const active = ShapeTypes.has(key);
30
+
31
+ const CurrentIcon = ShapesMap[key] || Icons.Rectangle;
32
+
33
+ return (
34
+ <span className="fastboard-toolbar-btn-interactive">
35
+ <Tippy
36
+ className="fastboard-tip"
37
+ content={renderShapesButtonContent()}
38
+ theme={theme}
39
+ placement="right-start"
40
+ trigger="click"
41
+ offset={RightOffset}
42
+ arrow={false}
43
+ interactive
44
+ >
45
+ <Button content={t("shape")} active={active}>
46
+ <CurrentIcon theme={theme} active={active} />
47
+ <span className="fastboard-toolbar-triangle" />
48
+ </Button>
49
+ </Tippy>
50
+ </span>
51
+ );
52
+ }
53
+
54
+ function renderShapesButtonContent() {
55
+ return (
56
+ <div className="fastboard-toolbar-panel shapes">
57
+ <ShapesBox />
58
+ <CutLine />
59
+ <Slider />
60
+ <CutLine />
61
+ <ColorBox />
62
+ </div>
63
+ );
64
+ }
65
+
66
+ export function ShapesBox() {
67
+ const { t } = useTranslation();
68
+
69
+ return (
70
+ <div className="fastboard-toolbar-shapes">
71
+ {ApplianceShapes.map(Appliance => (
72
+ <ApplianceShapeButton
73
+ key={Appliance}
74
+ content={t(Appliance)}
75
+ Appliance={Appliance}
76
+ Icon={ShapesMap[Appliance]}
77
+ />
78
+ ))}
79
+ {Shapes.map(shape => (
80
+ <ShapeShapeButton key={shape} content={t(shape)} shape={shape} Icon={ShapesMap[shape]} />
81
+ ))}
82
+ </div>
83
+ );
84
+ }
85
+
86
+ interface ApplianceShapeButtonProps {
87
+ content?: string;
88
+ Appliance: ApplianceNames;
89
+ Icon: React.ComponentType<IconProps>;
90
+ }
91
+
92
+ function ApplianceShapeButton({ content, Appliance, Icon }: ApplianceShapeButtonProps) {
93
+ const { theme, writable, setAppliance, memberState } = useContext(ToolbarContext);
94
+
95
+ const current = memberState?.currentApplianceName;
96
+ const disabled = !writable;
97
+
98
+ return (
99
+ <Button content={content} disabled={disabled} placement="top" onClick={() => setAppliance(Appliance)}>
100
+ <Icon theme={theme} active={current === Appliance} />
101
+ </Button>
102
+ );
103
+ }
104
+
105
+ interface ShapeShapeButtonProps {
106
+ content?: string;
107
+ shape: ShapeType;
108
+ Icon: React.ComponentType<IconProps>;
109
+ }
110
+
111
+ function ShapeShapeButton({ content, shape, Icon }: ShapeShapeButtonProps) {
112
+ const { theme, writable, setAppliance, memberState } = useContext(ToolbarContext);
113
+
114
+ const appliance = memberState?.currentApplianceName;
115
+ const current = appliance === ApplianceNames.shape && memberState?.shapeType;
116
+ const disabled = !writable;
117
+
118
+ return (
119
+ <Button
120
+ content={content}
121
+ disabled={disabled}
122
+ placement="top"
123
+ onClick={() => setAppliance(ApplianceNames.shape, shape)}
124
+ >
125
+ <Icon theme={theme} active={current === shape} />
126
+ </Button>
127
+ );
128
+ }
@@ -0,0 +1,26 @@
1
+ import clsx from "clsx";
2
+ import RcSlider from "rc-slider";
3
+ import React, { useContext } from "react";
4
+
5
+ import { themes } from "../../../theme";
6
+ import { ToolbarContext } from "../Toolbar";
7
+
8
+ export function Slider() {
9
+ const { theme, writable, memberState, setStrokeWidth } = useContext(ToolbarContext);
10
+ const { activeColor } = themes[theme];
11
+
12
+ const strokeWidth = memberState?.strokeWidth || 0;
13
+
14
+ return (
15
+ <RcSlider
16
+ disabled={!writable}
17
+ className={clsx("fastboard-toolbar-slider", theme)}
18
+ trackStyle={{ background: activeColor }}
19
+ handleStyle={{ border: `1px solid ${activeColor}` }}
20
+ value={strokeWidth}
21
+ onChange={setStrokeWidth}
22
+ min={1}
23
+ max={32}
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,62 @@
1
+ import Tippy from "@tippyjs/react";
2
+ import React, { useCallback, useContext } from "react";
3
+ import { ApplianceNames } from "white-web-sdk";
4
+
5
+ import { useTranslation } from "../../../i18n";
6
+ import { Icon } from "../../../icons";
7
+ import { defaultHotKeys } from "../../../internal";
8
+ import { RightOffset } from "../../../theme";
9
+ import { useFastboardApp } from "../../hooks";
10
+ import { Icons } from "../icons";
11
+ import { ToolbarContext } from "../Toolbar";
12
+ import { renderToolTip } from "./ApplianceButtons";
13
+ import { Button } from "./Button";
14
+ import { ColorBox } from "./ColorBox";
15
+
16
+ export function TextButton() {
17
+ const app = useFastboardApp();
18
+ const { t } = useTranslation();
19
+
20
+ const { theme, icons, writable, setAppliance, memberState } = useContext(ToolbarContext);
21
+
22
+ const changeAppliance = useCallback(() => {
23
+ setAppliance(ApplianceNames.text);
24
+ }, [setAppliance]);
25
+
26
+ const appliance = memberState?.currentApplianceName;
27
+ const active = appliance === ApplianceNames.text;
28
+ const disabled = !writable;
29
+ const shortcut = (app?.hotKeys || defaultHotKeys).changeToText;
30
+
31
+ return (
32
+ <span className="fastboard-toolbar-btn-interactive">
33
+ <Tippy
34
+ className="fastboard-tip"
35
+ content={renderTextButtonContent()}
36
+ theme={theme}
37
+ placement="right-start"
38
+ trigger="click"
39
+ offset={RightOffset}
40
+ arrow={false}
41
+ interactive
42
+ >
43
+ <Button content={renderToolTip(t("text"), shortcut)} active={active} onClick={changeAppliance}>
44
+ <Icon
45
+ fallback={<Icons.Text theme={theme} active={active} />}
46
+ src={disabled ? icons?.textIconDisable : icons?.textIcon}
47
+ alt="[text]"
48
+ />
49
+ <span className="fastboard-toolbar-triangle" />
50
+ </Button>
51
+ </Tippy>
52
+ </span>
53
+ );
54
+ }
55
+
56
+ function renderTextButtonContent() {
57
+ return (
58
+ <div className="fastboard-toolbar-panel text">
59
+ <ColorBox />
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,49 @@
1
+ import React, { useCallback, useContext } from "react";
2
+
3
+ import { Icon } from "../../../icons";
4
+ import { Icons } from "../icons";
5
+ import { Button } from "./Button";
6
+ import { CutLine } from "./CutLine";
7
+ import { ToolbarContext } from "../Toolbar";
8
+ import { ItemHeight } from "../const";
9
+
10
+ export interface UpButtonProps {
11
+ disabled: boolean;
12
+ scrollTo: (height: number) => void;
13
+ }
14
+
15
+ export function UpButton({ disabled, scrollTo }: UpButtonProps) {
16
+ const { theme, icons } = useContext(ToolbarContext);
17
+ const scrollUp = useCallback(() => scrollTo(-ItemHeight), [scrollTo]);
18
+
19
+ return (
20
+ <>
21
+ <Button content="Up" disabled={disabled} onClick={scrollUp}>
22
+ <Icon
23
+ fallback={<Icons.Up theme={theme} />}
24
+ src={disabled ? icons?.upIconDisable : icons?.upIcon}
25
+ alt="[up]"
26
+ />
27
+ </Button>
28
+ <CutLine />
29
+ </>
30
+ );
31
+ }
32
+
33
+ export function DownButton({ disabled, scrollTo }: UpButtonProps) {
34
+ const { theme, icons } = useContext(ToolbarContext);
35
+ const scrollDown = useCallback(() => scrollTo(ItemHeight), [scrollTo]);
36
+
37
+ return (
38
+ <>
39
+ <CutLine />
40
+ <Button content="Down" disabled={disabled} onClick={scrollDown}>
41
+ <Icon
42
+ fallback={<Icons.Down theme={theme} />}
43
+ src={disabled ? icons?.downIconDisable : icons?.downIcon}
44
+ alt="[down]"
45
+ />
46
+ </Button>
47
+ </>
48
+ );
49
+ }
@@ -0,0 +1,32 @@
1
+ import { ApplianceNames, ShapeType } from "white-web-sdk";
2
+ import { Icons } from "./icons";
3
+
4
+ export const ShapesMap = {
5
+ [ApplianceNames.rectangle]: Icons.Rectangle,
6
+ [ApplianceNames.ellipse]: Icons.Circle,
7
+ [ApplianceNames.straight]: Icons.Line,
8
+ [ApplianceNames.arrow]: Icons.Arrow,
9
+ [ShapeType.Pentagram]: Icons.Star,
10
+ [ShapeType.Rhombus]: Icons.Diamond,
11
+ [ShapeType.Triangle]: Icons.Triangle,
12
+ [ShapeType.SpeechBalloon]: Icons.SpeechBalloon,
13
+ } as const;
14
+
15
+ export const ApplianceShapes = [
16
+ ApplianceNames.rectangle,
17
+ ApplianceNames.ellipse,
18
+ ApplianceNames.straight,
19
+ ApplianceNames.arrow,
20
+ ] as const;
21
+
22
+ export const Shapes = [
23
+ ShapeType.Pentagram,
24
+ ShapeType.Rhombus,
25
+ ShapeType.Triangle,
26
+ ShapeType.SpeechBalloon,
27
+ ] as const;
28
+
29
+ export const ItemHeight = 32 + 4;
30
+ export const ItemsCount = 8;
31
+ export const MaxHeight = ItemHeight * ItemsCount - 4;
32
+ export const MinHeight = ItemHeight * 2 - 4;