@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.
- package/LICENSE.txt +21 -0
- package/dist/behaviors/style.d.ts +1 -0
- package/dist/components/Fastboard.d.ts +15 -0
- package/dist/components/PageControl/PageControl.d.ts +5 -0
- package/dist/components/PageControl/hooks.d.ts +9 -0
- package/dist/components/PageControl/index.d.ts +2 -0
- package/dist/components/PlayerControl/PlayerControl.d.ts +9 -0
- package/dist/components/PlayerControl/hooks.d.ts +11 -0
- package/dist/components/PlayerControl/icons/Loading.d.ts +3 -0
- package/dist/components/PlayerControl/icons/Pause.d.ts +3 -0
- package/dist/components/PlayerControl/icons/Play.d.ts +3 -0
- package/dist/components/PlayerControl/icons/index.d.ts +6 -0
- package/dist/components/PlayerControl/index.d.ts +2 -0
- package/dist/components/RedoUndo/RedoUndo.d.ts +5 -0
- package/dist/components/RedoUndo/hooks.d.ts +6 -0
- package/dist/components/RedoUndo/index.d.ts +2 -0
- package/dist/components/Toolbar/Content.d.ts +2 -0
- package/dist/components/Toolbar/Toolbar.d.ts +14 -0
- package/dist/components/Toolbar/components/ApplianceButtons.d.ts +7 -0
- package/dist/components/Toolbar/components/AppsButton.d.ts +6 -0
- package/dist/components/Toolbar/components/ColorBox.d.ts +2 -0
- package/dist/components/Toolbar/components/CutLine.d.ts +2 -0
- package/dist/components/Toolbar/components/Mask.d.ts +7 -0
- package/dist/components/Toolbar/components/PencilButton.d.ts +2 -0
- package/dist/components/Toolbar/components/ShapesButton.d.ts +3 -0
- package/dist/components/Toolbar/components/Slider.d.ts +2 -0
- package/dist/components/Toolbar/components/TextButton.d.ts +2 -0
- package/dist/components/Toolbar/components/UpDownButtons.d.ts +7 -0
- package/dist/components/Toolbar/const.d.ts +18 -0
- package/dist/components/Toolbar/hooks.d.ts +12 -0
- package/dist/components/Toolbar/icons/Apps.d.ts +3 -0
- package/dist/components/Toolbar/icons/Arrow.d.ts +3 -0
- package/dist/components/Toolbar/icons/Circle.d.ts +3 -0
- package/dist/components/Toolbar/icons/Clean.d.ts +3 -0
- package/dist/components/Toolbar/icons/Clicker.d.ts +3 -0
- package/dist/components/Toolbar/icons/Collapse.d.ts +3 -0
- package/dist/components/Toolbar/icons/Diamond.d.ts +3 -0
- package/dist/components/Toolbar/icons/Down.d.ts +3 -0
- package/dist/components/Toolbar/icons/Eraser.d.ts +3 -0
- package/dist/components/Toolbar/icons/Expand.d.ts +3 -0
- package/dist/components/Toolbar/icons/Line.d.ts +3 -0
- package/dist/components/Toolbar/icons/Pencil.d.ts +3 -0
- package/dist/components/Toolbar/icons/Rectangle.d.ts +3 -0
- package/dist/components/Toolbar/icons/Selector.d.ts +3 -0
- package/dist/components/Toolbar/icons/SpeechBalloon.d.ts +3 -0
- package/dist/components/Toolbar/icons/Star.d.ts +3 -0
- package/dist/components/Toolbar/icons/Text.d.ts +3 -0
- package/dist/components/Toolbar/icons/Triangle.d.ts +3 -0
- package/dist/components/Toolbar/icons/Up.d.ts +3 -0
- package/dist/components/Toolbar/icons/index.d.ts +22 -0
- package/dist/components/Toolbar/index.d.ts +2 -0
- package/dist/components/ZoomControl/ZoomControl.d.ts +5 -0
- package/dist/components/ZoomControl/hooks.d.ts +7 -0
- package/dist/components/ZoomControl/index.d.ts +2 -0
- package/dist/components/hooks.d.ts +13 -0
- package/dist/i18n/index.d.ts +12 -0
- package/dist/icons/ChevronLeft.d.ts +3 -0
- package/dist/icons/ChevronRight.d.ts +3 -0
- package/dist/icons/FilePlus.d.ts +3 -0
- package/dist/icons/Minus.d.ts +3 -0
- package/dist/icons/Plus.d.ts +3 -0
- package/dist/icons/Redo.d.ts +3 -0
- package/dist/icons/Reset.d.ts +3 -0
- package/dist/icons/Undo.d.ts +3 -0
- package/dist/icons/index.d.ts +7 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +2116 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2083 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal/helpers.d.ts +16 -0
- package/dist/internal/hooks.d.ts +3 -0
- package/dist/internal/index.d.ts +2 -0
- package/dist/theme.d.ts +16 -0
- package/dist/typings.d.ts +10 -0
- package/package.json +41 -0
- package/src/behaviors/style.ts +4 -0
- package/src/components/Fastboard.scss +41 -0
- package/src/components/Fastboard.tsx +97 -0
- package/src/components/PageControl/PageControl.scss +80 -0
- package/src/components/PageControl/PageControl.tsx +105 -0
- package/src/components/PageControl/hooks.ts +67 -0
- package/src/components/PageControl/index.ts +2 -0
- package/src/components/PlayerControl/PlayerControl.scss +145 -0
- package/src/components/PlayerControl/PlayerControl.tsx +131 -0
- package/src/components/PlayerControl/components/Button.tsx +45 -0
- package/src/components/PlayerControl/hooks.ts +88 -0
- package/src/components/PlayerControl/icons/Loading.tsx +13 -0
- package/src/components/PlayerControl/icons/Pause.tsx +13 -0
- package/src/components/PlayerControl/icons/Play.tsx +13 -0
- package/src/components/PlayerControl/icons/index.ts +10 -0
- package/src/components/PlayerControl/index.ts +2 -0
- package/src/components/RedoUndo/RedoUndo.scss +56 -0
- package/src/components/RedoUndo/RedoUndo.tsx +76 -0
- package/src/components/RedoUndo/hooks.ts +18 -0
- package/src/components/RedoUndo/index.ts +2 -0
- package/src/components/Toolbar/Content.tsx +74 -0
- package/src/components/Toolbar/Toolbar.scss +281 -0
- package/src/components/Toolbar/Toolbar.tsx +116 -0
- package/src/components/Toolbar/components/ApplianceButtons.tsx +108 -0
- package/src/components/Toolbar/components/AppsButton.tsx +101 -0
- package/src/components/Toolbar/components/Button.tsx +46 -0
- package/src/components/Toolbar/components/ColorBox.tsx +55 -0
- package/src/components/Toolbar/components/CutLine.tsx +8 -0
- package/src/components/Toolbar/components/Mask.tsx +44 -0
- package/src/components/Toolbar/components/PencilButton.tsx +66 -0
- package/src/components/Toolbar/components/ShapesButton.tsx +128 -0
- package/src/components/Toolbar/components/Slider.tsx +26 -0
- package/src/components/Toolbar/components/TextButton.tsx +62 -0
- package/src/components/Toolbar/components/UpDownButtons.tsx +49 -0
- package/src/components/Toolbar/components/assets/cocos.png +0 -0
- package/src/components/Toolbar/components/assets/collapsed.png +0 -0
- package/src/components/Toolbar/components/assets/countdown.png +0 -0
- package/src/components/Toolbar/components/assets/expanded.png +0 -0
- package/src/components/Toolbar/components/assets/geogebra.png +0 -0
- package/src/components/Toolbar/components/assets/vscode.png +0 -0
- package/src/components/Toolbar/const.ts +32 -0
- package/src/components/Toolbar/hooks.ts +68 -0
- package/src/components/Toolbar/icons/Apps.tsx +16 -0
- package/src/components/Toolbar/icons/Arrow.tsx +13 -0
- package/src/components/Toolbar/icons/Circle.tsx +13 -0
- package/src/components/Toolbar/icons/Clean.tsx +16 -0
- package/src/components/Toolbar/icons/Clicker.tsx +19 -0
- package/src/components/Toolbar/icons/Collapse.tsx +13 -0
- package/src/components/Toolbar/icons/Diamond.tsx +13 -0
- package/src/components/Toolbar/icons/Down.tsx +13 -0
- package/src/components/Toolbar/icons/Eraser.tsx +16 -0
- package/src/components/Toolbar/icons/Expand.tsx +13 -0
- package/src/components/Toolbar/icons/Line.tsx +13 -0
- package/src/components/Toolbar/icons/Pencil.tsx +16 -0
- package/src/components/Toolbar/icons/Rectangle.tsx +13 -0
- package/src/components/Toolbar/icons/Selector.tsx +13 -0
- package/src/components/Toolbar/icons/SpeechBalloon.tsx +17 -0
- package/src/components/Toolbar/icons/Star.tsx +17 -0
- package/src/components/Toolbar/icons/Text.tsx +13 -0
- package/src/components/Toolbar/icons/Triangle.tsx +13 -0
- package/src/components/Toolbar/icons/Up.tsx +13 -0
- package/src/components/Toolbar/icons/index.ts +42 -0
- package/src/components/Toolbar/index.ts +2 -0
- package/src/components/ZoomControl/ZoomControl.scss +80 -0
- package/src/components/ZoomControl/ZoomControl.tsx +94 -0
- package/src/components/ZoomControl/hooks.ts +52 -0
- package/src/components/ZoomControl/index.ts +2 -0
- package/src/components/hooks.ts +59 -0
- package/src/i18n/en.json +31 -0
- package/src/i18n/index.ts +29 -0
- package/src/i18n/zh-CN.json +32 -0
- package/src/icons/ChevronLeft.tsx +21 -0
- package/src/icons/ChevronRight.tsx +21 -0
- package/src/icons/FilePlus.tsx +18 -0
- package/src/icons/Minus.tsx +15 -0
- package/src/icons/Plus.tsx +15 -0
- package/src/icons/Redo.tsx +18 -0
- package/src/icons/Reset.tsx +19 -0
- package/src/icons/Undo.tsx +18 -0
- package/src/icons/index.tsx +11 -0
- package/src/index.ts +10 -0
- package/src/internal/helpers.ts +31 -0
- package/src/internal/hooks.ts +23 -0
- package/src/internal/index.ts +2 -0
- package/src/style.scss +29 -0
- package/src/theme.ts +36 -0
- package/src/typings.ts +15 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ApplianceNames, Color, MemberState, ShapeType } from "white-web-sdk";
|
|
2
|
+
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
import { noop } from "../../internal";
|
|
6
|
+
import { useFastboardApp, useFastboardValue, useWritable } from "../hooks";
|
|
7
|
+
|
|
8
|
+
export interface ToolbarHook {
|
|
9
|
+
readonly writable: boolean;
|
|
10
|
+
readonly memberState: MemberState | undefined;
|
|
11
|
+
cleanCurrentScene(): void;
|
|
12
|
+
setAppliance(appliance: ApplianceNames, shape?: ShapeType): void;
|
|
13
|
+
setStrokeWidth(width: number): void;
|
|
14
|
+
setStrokeColor(color: Color): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useRoomState() {
|
|
18
|
+
return useFastboardValue(useFastboardApp().memberState);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useToolbar(): ToolbarHook {
|
|
22
|
+
const app = useFastboardApp();
|
|
23
|
+
const writable = useWritable();
|
|
24
|
+
const memberState = useRoomState();
|
|
25
|
+
|
|
26
|
+
const cleanCurrentScene = useCallback(() => {
|
|
27
|
+
app.cleanCurrentScene();
|
|
28
|
+
}, [app]);
|
|
29
|
+
|
|
30
|
+
const setAppliance = useCallback(
|
|
31
|
+
(appliance: ApplianceNames, shape?: ShapeType) => {
|
|
32
|
+
app.setAppliance(appliance, shape);
|
|
33
|
+
},
|
|
34
|
+
[app]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const setStrokeWidth = useCallback(
|
|
38
|
+
(strokeWidth: number) => {
|
|
39
|
+
app.setStrokeWidth(strokeWidth);
|
|
40
|
+
},
|
|
41
|
+
[app]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const setStrokeColor = useCallback(
|
|
45
|
+
(strokeColor: Color) => {
|
|
46
|
+
app.setStrokeColor(strokeColor);
|
|
47
|
+
},
|
|
48
|
+
[app]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
writable,
|
|
53
|
+
memberState,
|
|
54
|
+
cleanCurrentScene,
|
|
55
|
+
setAppliance,
|
|
56
|
+
setStrokeWidth,
|
|
57
|
+
setStrokeColor,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const EmptyToolbarHook: ToolbarHook = {
|
|
62
|
+
writable: false,
|
|
63
|
+
memberState: undefined,
|
|
64
|
+
cleanCurrentScene: noop,
|
|
65
|
+
setAppliance: noop,
|
|
66
|
+
setStrokeWidth: noop,
|
|
67
|
+
setStrokeColor: noop,
|
|
68
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Apps = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<g fill={stroke}>
|
|
11
|
+
<path d="M17.667 4.5h-3.334c-1.012 0-1.833.82-1.833 1.833V11.5h5.167c1.012 0 1.833-.82 1.833-1.833V6.333c0-1.012-.82-1.833-1.833-1.833Zm-3.334 1h3.334c.46 0 .833.373.833.833v3.334l-.006.104a.833.833 0 0 1-.827.729H13.5V6.333c0-.46.373-.833.833-.833Z" />
|
|
12
|
+
<path d="M6.333 19.5A1.833 1.833 0 0 1 4.5 17.667v-3.334c0-.525.221-1 .576-1.334a1.822 1.822 0 0 1-.576-1.332V8.333c0-1.012.82-1.833 1.833-1.833H10A1.5 1.5 0 0 1 11.5 8v4.5h4.167c.962 0 1.75.74 1.827 1.683l.006.15v3.334c0 1.012-.82 1.833-1.833 1.833Zm4.167-6H6.333a.833.833 0 0 0-.827.729l-.006.104v3.334c0 .46.373.833.833.833H10.5v-5Zm5.167 0H11.5v5h4.167c.46 0 .833-.373.833-.833v-3.334a.833.833 0 0 0-.833-.833ZM10 7.5H6.333a.833.833 0 0 0-.833.833v3.334c0 .46.373.833.833.833H10.5V8a.5.5 0 0 0-.41-.492L10 7.5Z" />
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Arrow = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill={stroke} d="M19 5v6l-2.647-2.646L5.99 18.718l-.708-.708L15.645 7.646 13 5h6Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Circle = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<rect width="15" height="15" x="4.5" y="4.5" fill="none" stroke={stroke} rx="7.5" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Clean = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path
|
|
11
|
+
fill={stroke}
|
|
12
|
+
d="M9.754 11.99c0 1.856-.711 3.62-1.96 4.951l-.151.155h1.403l.855-.853h.707l.853.853h2.635l.094-.064a6.237 6.237 0 0 0 2.559-4.781l.005-.26h1a7.237 7.237 0 0 1-2.994 5.862l-.229.16-.277.083h-3l-.353-.146-.647-.647-.646.647-.354.146h-3l-.286-.91.214-.148a6.237 6.237 0 0 0 2.567-4.787l.005-.26h1Zm4.772-6.502v2l.35.039a2.98 2.98 0 0 1 2.644 2.78l.006.181h-8a2.98 2.98 0 0 1 2.65-2.961l.35-.039v-2h2Z"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Clicker = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<g fill="none">
|
|
11
|
+
<path d="M0 0h24v24H0z" />
|
|
12
|
+
<path
|
|
13
|
+
fill={stroke}
|
|
14
|
+
d="m7 5.072 10.33 7.892-4.879.549 3.232 5.598-.866.5-3.233-5.597-2.914 3.95L7 5.072Z"
|
|
15
|
+
/>
|
|
16
|
+
</g>
|
|
17
|
+
</svg>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Collapse = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="m8 10-2 2 2 2m10-8H6m12 12H6m12-4h-8m8-4h-8" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Diamond = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="M4.222 12 12 4.222 19.778 12 12 19.778z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Down = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="m16 13-2 2-2 2-2-2-2-2m8-6-2 2-2 2-2-2-2-2" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Eraser = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path
|
|
11
|
+
fill={stroke}
|
|
12
|
+
d="m14.333 5.183.165.007c.494.037.978.245 1.356.623l2.333 2.333a2.15 2.15 0 0 1 0 3.04l-5.833 5.834a3.8 3.8 0 0 1-5.374 0l-1.167-1.166a2.15 2.15 0 0 1 0-3.04l7-7c.42-.42.97-.63 1.52-.63ZM11.52 8.52l-4.999 5a1.15 1.15 0 0 0 0 1.626l1.167 1.167a2.8 2.8 0 0 0 3.96 0l3.832-3.833-3.96-3.96Z"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Expand = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="m16 10 2 2-2 2M6 6h12M6 18h12M6 14h8m-8-4h8" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Line = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill={stroke} d="m18.01 5.282.708.708L5.99 18.718l-.708-.708z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Pencil = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path
|
|
11
|
+
fill={stroke}
|
|
12
|
+
d="m17.497 4.84.116.105 1.442 1.442a1.52 1.52 0 0 1 .104 2.034l-.104.116L8.733 18.858l-4.347.756.756-4.347L15.463 4.945a1.52 1.52 0 0 1 2.034-.104ZM5.967 16.349l-.353 2.037 2.037-.354-1.683-1.683Zm8.407-8.901-7.946 7.946 2.178 2.178 7.946-7.946-2.178-2.178Zm-.728 2.2.708.707-5 5-.708-.708 5-5Zm2.596-4.055-.072.06-1.09 1.088 2.179 2.178 1.089-1.088a.52.52 0 0 0 .105-.584l-.045-.08-.06-.072-1.442-1.442a.52.52 0 0 0-.664-.06Z"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Rectangle = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="M5.5 5.5h13v13h-13z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Selector = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill={stroke} d="m12 12 8 2.667-3.556 1.777L14.667 20 12 12Zm3-8v7.5h-1V5H5v9h6.5v1H4V4h11Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const SpeechBalloon = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke={stroke}
|
|
13
|
+
d="M17 4.5c.414 0 .79.168 1.06.44.272.27.44.646.44 1.06v9c0 .414-.168.79-.44 1.06a1.49 1.49 0 0 1-1.06.44h-4.207l-2.715 2.715-1.81-2.715H7a1.49 1.49 0 0 1-1.06-.44A1.495 1.495 0 0 1 5.5 15V6c0-.414.168-.79.44-1.06A1.49 1.49 0 0 1 7 4.5Z"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Star = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke={stroke}
|
|
13
|
+
d="m12 3.523 1.993 5.734 6.07.123-4.838 3.668 1.758 5.81L12 15.391l-4.983 3.467 1.758-5.81L3.938 9.38l6.069-.123L12 3.523Z"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Text = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill={stroke} d="M18.5 5.5V8h-1V6.5H13v11h2v1H9v-1h2v-11H6.5V8h-1V5.5h13Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Triangle = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="M12 6.008 19.138 18.5H4.862L12 6.008Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IconProps } from "../../../typings";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { getStroke } from "../../../theme";
|
|
5
|
+
|
|
6
|
+
export const Up = (props: IconProps) => {
|
|
7
|
+
const stroke = getStroke(props);
|
|
8
|
+
return (
|
|
9
|
+
<svg viewBox="0 0 24 24">
|
|
10
|
+
<path fill="none" stroke={stroke} d="m16 11-2-2-2-2-2 2-2 2m8 6-2-2-2-2-2 2-2 2" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { memo } from "react";
|
|
2
|
+
import { Apps } from "./Apps";
|
|
3
|
+
import { Arrow } from "./Arrow";
|
|
4
|
+
import { Circle } from "./Circle";
|
|
5
|
+
import { Clean } from "./Clean";
|
|
6
|
+
import { Clicker } from "./Clicker";
|
|
7
|
+
import { Collapse } from "./Collapse";
|
|
8
|
+
import { Diamond } from "./Diamond";
|
|
9
|
+
import { Down } from "./Down";
|
|
10
|
+
import { Eraser } from "./Eraser";
|
|
11
|
+
import { Expand } from "./Expand";
|
|
12
|
+
import { Line } from "./Line";
|
|
13
|
+
import { Pencil } from "./Pencil";
|
|
14
|
+
import { Rectangle } from "./Rectangle";
|
|
15
|
+
import { Selector } from "./Selector";
|
|
16
|
+
import { SpeechBalloon } from "./SpeechBalloon";
|
|
17
|
+
import { Star } from "./Star";
|
|
18
|
+
import { Text } from "./Text";
|
|
19
|
+
import { Triangle } from "./Triangle";
|
|
20
|
+
import { Up } from "./Up";
|
|
21
|
+
|
|
22
|
+
export const Icons = {
|
|
23
|
+
Clicker: memo(Clicker),
|
|
24
|
+
Collapse: memo(Collapse),
|
|
25
|
+
Eraser: memo(Eraser),
|
|
26
|
+
Expand: memo(Expand),
|
|
27
|
+
Pencil: memo(Pencil),
|
|
28
|
+
Selector: memo(Selector),
|
|
29
|
+
Rectangle: memo(Rectangle),
|
|
30
|
+
Text: memo(Text),
|
|
31
|
+
Apps: memo(Apps),
|
|
32
|
+
Clean: memo(Clean),
|
|
33
|
+
Circle: memo(Circle),
|
|
34
|
+
Line: memo(Line),
|
|
35
|
+
Arrow: memo(Arrow),
|
|
36
|
+
Star: memo(Star),
|
|
37
|
+
Diamond: memo(Diamond),
|
|
38
|
+
SpeechBalloon: memo(SpeechBalloon),
|
|
39
|
+
Triangle: memo(Triangle),
|
|
40
|
+
Up: memo(Up),
|
|
41
|
+
Down: memo(Down),
|
|
42
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
$name: "fastboard-zoom-control";
|
|
2
|
+
|
|
3
|
+
.#{$name} {
|
|
4
|
+
position: relative;
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
gap: 4px;
|
|
8
|
+
padding: 4px;
|
|
9
|
+
border-radius: 4px;
|
|
10
|
+
backdrop-filter: blur(2px);
|
|
11
|
+
-webkit-backdrop-filter: blur(2px);
|
|
12
|
+
|
|
13
|
+
&.light {
|
|
14
|
+
color: #333;
|
|
15
|
+
background-color: rgba($color: #fff, $alpha: 0.85);
|
|
16
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&.dark {
|
|
20
|
+
color: #ddd;
|
|
21
|
+
background-color: rgba($color: #333, $alpha: 0.85);
|
|
22
|
+
border: 1px solid rgba(0, 0, 0, 0.45);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.#{$name}-btn {
|
|
27
|
+
appearance: none;
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
margin: 0;
|
|
30
|
+
border: 0;
|
|
31
|
+
padding: 0;
|
|
32
|
+
width: 24px;
|
|
33
|
+
height: 24px;
|
|
34
|
+
background-color: transparent;
|
|
35
|
+
border-radius: 4px;
|
|
36
|
+
font-size: 24px;
|
|
37
|
+
line-height: 1;
|
|
38
|
+
|
|
39
|
+
svg,
|
|
40
|
+
img {
|
|
41
|
+
width: 1em;
|
|
42
|
+
height: 1em;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&:disabled {
|
|
46
|
+
opacity: 0.5;
|
|
47
|
+
cursor: not-allowed;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.light:not(:disabled):hover {
|
|
51
|
+
background-color: rgba(51, 129, 255, 0.1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&.dark:not(:disabled):hover {
|
|
55
|
+
background-color: rgba(51, 129, 255, 0.25);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.#{$name}-cut-line {
|
|
60
|
+
height: 24px;
|
|
61
|
+
width: 0.5px;
|
|
62
|
+
|
|
63
|
+
&.light {
|
|
64
|
+
background-color: #e7e7e7;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&.dark {
|
|
68
|
+
background-color: rgba(255, 255, 255, 0.15);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.#{$name}-percent {
|
|
73
|
+
opacity: 0.6;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.#{$name}-scale,
|
|
77
|
+
.#{$name}-percent {
|
|
78
|
+
font-size: 12px;
|
|
79
|
+
font-variant-numeric: tabular-nums;
|
|
80
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { CommonProps, GenericIcon } from "../../typings";
|
|
2
|
+
|
|
3
|
+
import Tippy from "@tippyjs/react";
|
|
4
|
+
import clsx from "clsx";
|
|
5
|
+
import React from "react";
|
|
6
|
+
|
|
7
|
+
import { useTranslation } from "../../i18n";
|
|
8
|
+
import { Icon } from "../../icons";
|
|
9
|
+
import { Minus } from "../../icons/Minus";
|
|
10
|
+
import { Plus } from "../../icons/Plus";
|
|
11
|
+
import { Reset } from "../../icons/Reset";
|
|
12
|
+
import { TopOffset } from "../../theme";
|
|
13
|
+
import { useTheme, useWritable } from "../hooks";
|
|
14
|
+
import { useZoomControl } from "./hooks";
|
|
15
|
+
|
|
16
|
+
export const name = "fastboard-zoom-control";
|
|
17
|
+
|
|
18
|
+
export type ZoomControlProps = CommonProps & GenericIcon<"reset" | "minus" | "plus">;
|
|
19
|
+
|
|
20
|
+
export function ZoomControl({
|
|
21
|
+
theme,
|
|
22
|
+
resetIcon,
|
|
23
|
+
resetIconDisable,
|
|
24
|
+
minusIcon,
|
|
25
|
+
minusIconDisable,
|
|
26
|
+
plusIcon,
|
|
27
|
+
plusIconDisable,
|
|
28
|
+
}: ZoomControlProps) {
|
|
29
|
+
theme = useTheme(theme);
|
|
30
|
+
const { t } = useTranslation();
|
|
31
|
+
|
|
32
|
+
const writable = useWritable();
|
|
33
|
+
const { scale, resetCamera, zoomIn, zoomOut } = useZoomControl();
|
|
34
|
+
|
|
35
|
+
const disabled = !writable;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className={clsx(name, theme)}>
|
|
39
|
+
{/* <span className={clsx(`${name}-cut-line`, theme)} /> */}
|
|
40
|
+
<Tippy
|
|
41
|
+
className="fastboard-tip"
|
|
42
|
+
content={t("zoomOut")}
|
|
43
|
+
theme={theme}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
placement="top"
|
|
46
|
+
delay={[1000, 400]}
|
|
47
|
+
duration={300}
|
|
48
|
+
offset={TopOffset}
|
|
49
|
+
>
|
|
50
|
+
<button className={clsx(`${name}-btn`, "minus", theme)} disabled={disabled} onClick={zoomOut}>
|
|
51
|
+
<Icon
|
|
52
|
+
fallback={<Minus theme={theme} />}
|
|
53
|
+
src={disabled ? minusIconDisable : minusIcon}
|
|
54
|
+
alt="[minus]"
|
|
55
|
+
/>
|
|
56
|
+
</button>
|
|
57
|
+
</Tippy>
|
|
58
|
+
<span className={clsx(`${name}-scale`, theme)}>{Math.ceil(scale * 100)}</span>
|
|
59
|
+
<span className={clsx(`${name}-percent`, theme)}>%</span>
|
|
60
|
+
<Tippy
|
|
61
|
+
className="fastboard-tip"
|
|
62
|
+
content={t("zoomIn")}
|
|
63
|
+
theme={theme}
|
|
64
|
+
disabled={disabled}
|
|
65
|
+
placement="top"
|
|
66
|
+
delay={[1000, 400]}
|
|
67
|
+
duration={300}
|
|
68
|
+
offset={TopOffset}
|
|
69
|
+
>
|
|
70
|
+
<button className={clsx(`${name}-btn`, "plus", theme)} disabled={disabled} onClick={zoomIn}>
|
|
71
|
+
<Icon fallback={<Plus theme={theme} />} src={disabled ? plusIconDisable : plusIcon} alt="[plus]" />
|
|
72
|
+
</button>
|
|
73
|
+
</Tippy>
|
|
74
|
+
<Tippy
|
|
75
|
+
className="fastboard-tip"
|
|
76
|
+
content={t("reset")}
|
|
77
|
+
theme={theme}
|
|
78
|
+
disabled={disabled}
|
|
79
|
+
placement="top"
|
|
80
|
+
delay={[1000, 400]}
|
|
81
|
+
duration={300}
|
|
82
|
+
offset={TopOffset}
|
|
83
|
+
>
|
|
84
|
+
<button className={clsx(`${name}-btn`, "reset", theme)} disabled={disabled} onClick={resetCamera}>
|
|
85
|
+
<Icon
|
|
86
|
+
fallback={<Reset theme={theme} />}
|
|
87
|
+
src={disabled ? resetIconDisable : resetIcon}
|
|
88
|
+
alt="[reset]"
|
|
89
|
+
/>
|
|
90
|
+
</button>
|
|
91
|
+
</Tippy>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
import { clamp } from "../../internal";
|
|
4
|
+
import { useFastboardApp, useFastboardValue } from "../hooks";
|
|
5
|
+
|
|
6
|
+
export const ScalePoints: readonly number[] = [
|
|
7
|
+
0.10737418240000011, 0.13421772800000012, 0.16777216000000014, 0.20971520000000016, 0.26214400000000015,
|
|
8
|
+
0.3276800000000002, 0.4096000000000002, 0.5120000000000001, 0.6400000000000001, 0.8, 1, 1.26,
|
|
9
|
+
1.5876000000000001, 2.000376, 2.5204737600000002, 3.1757969376000004, 4.001504141376, 5.041895218133761,
|
|
10
|
+
6.352787974848539, 8.00451284830916, 10,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function nextScale(scale: number, delta: 1 | -1) {
|
|
14
|
+
const { length } = ScalePoints;
|
|
15
|
+
const last = length - 1;
|
|
16
|
+
if (scale < ScalePoints[0]) return ScalePoints[0];
|
|
17
|
+
if (scale > ScalePoints[last]) return ScalePoints[last];
|
|
18
|
+
for (let i = 0; i < length; ++i) {
|
|
19
|
+
const curr = ScalePoints[i];
|
|
20
|
+
const prev = i === 0 ? -Infinity : (ScalePoints[i - 1] + curr) / 2;
|
|
21
|
+
const next = i === last ? Infinity : (ScalePoints[i + 1] + curr) / 2;
|
|
22
|
+
if (prev <= scale && scale <= next) return ScalePoints[clamp(i + delta, 0, last)];
|
|
23
|
+
}
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useZoomControl() {
|
|
28
|
+
const app = useFastboardApp();
|
|
29
|
+
const scale = useFastboardValue(app.camera).scale || 1;
|
|
30
|
+
|
|
31
|
+
const resetCamera = useCallback(() => {
|
|
32
|
+
app.moveCamera({ scale: 1, centerX: 0, centerY: 0 });
|
|
33
|
+
}, [app]);
|
|
34
|
+
|
|
35
|
+
const zoomIn = useCallback(() => {
|
|
36
|
+
app.moveCamera({
|
|
37
|
+
scale: nextScale(scale, 1),
|
|
38
|
+
centerX: 0,
|
|
39
|
+
centerY: 0,
|
|
40
|
+
});
|
|
41
|
+
}, [app, scale]);
|
|
42
|
+
|
|
43
|
+
const zoomOut = useCallback(() => {
|
|
44
|
+
app.moveCamera({
|
|
45
|
+
scale: nextScale(scale, -1),
|
|
46
|
+
centerX: 0,
|
|
47
|
+
centerY: 0,
|
|
48
|
+
});
|
|
49
|
+
}, [app, scale]);
|
|
50
|
+
|
|
51
|
+
return { scale, resetCamera, zoomIn, zoomOut };
|
|
52
|
+
}
|