@netless/fastboard-ui 1.0.0-canary.9 → 1.0.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/dist/full.d.ts +178 -0
- package/dist/index.d.ts +74 -75
- package/dist/index.js +4200 -3679
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4188 -3650
- package/dist/index.mjs.map +1 -0
- package/dist/index.svelte.mjs +6102 -6540
- package/dist/index.svelte.mjs.map +1 -0
- package/dist/lite.d.ts +178 -0
- package/lite.d.ts +1 -0
- package/package.json +19 -10
- package/src/actions/tippy.ts +6 -11
- package/src/behaviors/apps.ts +38 -4
- package/src/components/Button/Button.svelte +1 -4
- package/src/components/Button/Button.svelte.d.ts +1 -2
- package/src/components/Fastboard/Fastboard.scss +14 -2
- package/src/components/Fastboard/Fastboard.svelte +15 -5
- package/src/components/Fastboard/Fastboard.svelte.d.ts +6 -2
- package/src/components/Fastboard/ReplayFastboard.svelte +2 -2
- package/src/components/Fastboard/ReplayFastboard.svelte.d.ts +3 -3
- package/src/components/Icon/Icon.svelte.d.ts +1 -1
- package/src/components/Icons/{PencilEraser.svelte → Drag.svelte} +2 -2
- package/src/components/Icons/{PencilEraserFilled.svelte → DragFilled.svelte} +1 -3
- package/src/components/Icons/Eraser.svelte +1 -35
- package/src/components/Icons/EraserFilled.svelte +2 -2
- package/src/components/Icons/Laser.svelte +21 -0
- package/src/components/Icons/LaserFilled.svelte +14 -0
- package/src/components/Icons/LaserPen.svelte +20 -0
- package/src/components/Icons/LaserPenFilled.svelte +20 -0
- package/src/components/Icons/index.ts +12 -11
- package/src/components/PageControl/PageControl.svelte +4 -3
- package/src/components/PageControl/PageControl.svelte.d.ts +1 -1
- package/src/components/PlayerControl/PlayerControl.svelte.d.ts +1 -1
- package/src/components/RedoUndo/RedoUndo.svelte +2 -1
- package/src/components/RedoUndo/RedoUndo.svelte.d.ts +1 -1
- package/src/components/Toolbar/Toolbar.scss +14 -4
- package/src/components/Toolbar/Toolbar.svelte +34 -27
- package/src/components/Toolbar/Toolbar.svelte.d.ts +2 -19
- package/src/components/Toolbar/components/Contents.scss +19 -14
- package/src/components/Toolbar/components/Contents.svelte +98 -235
- package/src/components/Toolbar/components/Slider.scss +3 -1
- package/src/components/Toolbar/components/StrokeColor.svelte +12 -7
- package/src/components/Toolbar/components/TextColor.svelte +12 -7
- package/src/components/Toolbar/components/constants.ts +22 -36
- package/src/components/Toolbar/components/helper.ts +24 -2
- package/src/components/Toolbar/definitions/Clear.svelte +13 -0
- package/src/components/Toolbar/definitions/Clicker.svelte +19 -0
- package/src/components/Toolbar/definitions/Eraser.svelte +19 -0
- package/src/components/Toolbar/definitions/Hand.svelte +19 -0
- package/src/components/Toolbar/definitions/Laser.svelte +19 -0
- package/src/components/Toolbar/definitions/Pencil.svelte +23 -0
- package/src/components/Toolbar/definitions/Selector.svelte +19 -0
- package/src/components/Toolbar/definitions/Shapes.svelte +41 -0
- package/src/components/Toolbar/definitions/Text.svelte +20 -0
- package/src/components/ZoomControl/ZoomControl.svelte +5 -6
- package/src/components/ZoomControl/ZoomControl.svelte.d.ts +1 -1
- package/src/components/theme.scss +4 -11
- package/src/helpers/index.ts +60 -64
- package/src/typings.ts +42 -25
- package/dist/index.css +0 -888
- package/src/components/Icons/Curve.svelte +0 -10
- package/src/components/Icons/CurveDashed.svelte +0 -16
- package/src/components/Toolbar/components/PencilEraserSize.svelte +0 -27
- /package/src/components/Toolbar/components/{Shapes.svelte → SelectShapes.svelte} +0 -0
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { FastboardApp, ExtendMemberState } from "@netless/fastboard-core";
|
|
3
3
|
import type { Writable } from "svelte/store";
|
|
4
|
-
import type {
|
|
5
|
-
import type { Language, Theme } from "../../../typings";
|
|
6
|
-
import type { Shape, Eraser } from "./constants";
|
|
7
|
-
import { eraserIcon, eraserIconActive, applianceShapes, shapesIcon, shapesIconActive } from "./constants";
|
|
4
|
+
import type { Language, Theme, ToolbarItem } from "../../../typings";
|
|
8
5
|
import { writable } from "svelte/store";
|
|
9
6
|
import { scrollHeight } from "../../../actions/height";
|
|
10
7
|
import { scrollTop } from "../../../actions/scroll";
|
|
11
8
|
import { tippy_hide_all } from "../../../actions/tippy";
|
|
12
9
|
import { clamp } from "../../helpers";
|
|
13
|
-
import { i18n } from "./constants";
|
|
14
|
-
import {
|
|
10
|
+
import { default_colors, default_items, i18n } from "./constants";
|
|
11
|
+
import { apps } from "../../../behaviors";
|
|
15
12
|
import { tooltip } from "./helper";
|
|
16
13
|
import Icons from "../../Icons";
|
|
17
|
-
import Button from "../../Button";
|
|
14
|
+
import Button, { type ButtonProps } from "../../Button";
|
|
15
|
+
|
|
18
16
|
import StrokeWidth from "./StrokeWidth.svelte";
|
|
19
17
|
import StrokeColor from "./StrokeColor.svelte";
|
|
20
|
-
import PencilEraserSize from "./PencilEraserSize.svelte";
|
|
21
18
|
import TextColor from "./TextColor.svelte";
|
|
22
|
-
import
|
|
19
|
+
import SelectShapes from "./SelectShapes.svelte";
|
|
20
|
+
|
|
21
|
+
import Clicker from "../definitions/Clicker.svelte";
|
|
22
|
+
import Selector from "../definitions/Selector.svelte";
|
|
23
|
+
import Pencil from "../definitions/Pencil.svelte";
|
|
24
|
+
import Text from "../definitions/Text.svelte";
|
|
25
|
+
import Shapes from "../definitions/Shapes.svelte";
|
|
26
|
+
import Eraser from "../definitions/Eraser.svelte";
|
|
27
|
+
import Clear from "../definitions/Clear.svelte";
|
|
28
|
+
import Hand from "../definitions/Hand.svelte";
|
|
29
|
+
import Laser from "../definitions/Laser.svelte";
|
|
23
30
|
|
|
24
31
|
export let app: FastboardApp | null | undefined = null;
|
|
25
32
|
export let theme: Theme = "light";
|
|
@@ -28,23 +35,27 @@
|
|
|
28
35
|
export let scroll_height: Writable<number>;
|
|
29
36
|
export let computed_height = 0;
|
|
30
37
|
export let scrollable = false;
|
|
31
|
-
export let
|
|
38
|
+
export let placement: "left" | "right" = "left";
|
|
39
|
+
export let items: ToolbarItem[] = default_items;
|
|
32
40
|
export let hide_apps = false;
|
|
33
|
-
export let
|
|
41
|
+
export let colors = default_colors;
|
|
34
42
|
|
|
35
43
|
const name = "fastboard-toolbar";
|
|
36
44
|
|
|
37
|
-
let last_shape: Shape = "rectangle";
|
|
38
|
-
let last_eraser: Eraser = "pencilEraser";
|
|
39
|
-
|
|
40
45
|
let pencil_panel: HTMLDivElement;
|
|
41
46
|
let text_panel: HTMLDivElement;
|
|
42
47
|
let shapes_panel: HTMLDivElement;
|
|
43
|
-
let eraser_panel: HTMLDivElement;
|
|
44
48
|
let apps_panel: HTMLDivElement;
|
|
45
49
|
|
|
46
|
-
let btn_props:
|
|
47
|
-
$: btn_props = {
|
|
50
|
+
let btn_props: Partial<ButtonProps>;
|
|
51
|
+
$: btn_props = {
|
|
52
|
+
name,
|
|
53
|
+
theme,
|
|
54
|
+
disabled,
|
|
55
|
+
placement: placement === "left" ? "right" : "left",
|
|
56
|
+
menu_placement: placement === "left" ? "right-start" : "left-start",
|
|
57
|
+
};
|
|
58
|
+
|
|
48
59
|
$: t = i18n[language];
|
|
49
60
|
$: hotkeys = app?.hotKeys;
|
|
50
61
|
$: c = {
|
|
@@ -52,29 +63,20 @@
|
|
|
52
63
|
selector: tooltip(t.selector, hotkeys?.changeToSelector),
|
|
53
64
|
pencil: tooltip(t.pencil, hotkeys?.changeToPencil),
|
|
54
65
|
eraser: tooltip(t.eraser, hotkeys?.changeToEraser),
|
|
55
|
-
pencilEraser: tooltip(t.pencilEraser, hotkeys?.changeToPencilEraser),
|
|
56
|
-
eraserForPanel: tooltip(t.eraser, hotkeys?.changeToEraser),
|
|
57
|
-
pencilEraserForPanel: tooltip(t.pencilEraser, hotkeys?.changeToPencilEraser),
|
|
58
66
|
text: tooltip(t.text, hotkeys?.changeToText),
|
|
67
|
+
hand: tooltip(t.hand, hotkeys?.changeToHand),
|
|
68
|
+
laserPointer: tooltip(t.laserPointer, hotkeys?.changeToLaserPointer),
|
|
59
69
|
};
|
|
70
|
+
|
|
60
71
|
$: memberState = app?.memberState;
|
|
61
72
|
$: appliance = $memberState?.currentApplianceName;
|
|
62
|
-
$: dotted = $memberState?.dottedLine;
|
|
63
|
-
$: shape = $memberState?.shapeType;
|
|
64
73
|
$: status = app?.appsStatus;
|
|
65
74
|
|
|
66
|
-
$: if (applianceShapes.includes(appliance as Appliance)) {
|
|
67
|
-
last_shape = appliance as Shape;
|
|
68
|
-
} else if (shape) {
|
|
69
|
-
last_shape = shape;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
$: if (["pencilEraser", "eraser"].includes(appliance as Appliance)) {
|
|
73
|
-
last_eraser = appliance as "pencilEraser" | "eraser";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
75
|
$: max_scroll = scrollable ? $scroll_height + (32 + 8) * 2 - computed_height : 0;
|
|
77
76
|
|
|
77
|
+
$: hasAppliancePlugin = !!app?.appliancePlugin;
|
|
78
|
+
$: hasUseLaserPen = (hasAppliancePlugin && ($memberState as ExtendMemberState)?.useLaserPen) || false;
|
|
79
|
+
|
|
78
80
|
let top = writable(0);
|
|
79
81
|
|
|
80
82
|
function scroll_up() {
|
|
@@ -96,31 +98,21 @@
|
|
|
96
98
|
function text() {
|
|
97
99
|
app?.setAppliance("text");
|
|
98
100
|
}
|
|
99
|
-
function
|
|
100
|
-
app?.toggleDottedLine(true);
|
|
101
|
-
}
|
|
102
|
-
function unset_dotted() {
|
|
103
|
-
app?.toggleDottedLine(false);
|
|
104
|
-
}
|
|
105
|
-
function select_last_shape() {
|
|
106
|
-
if (applianceShapes.includes(last_shape as Appliance)) {
|
|
107
|
-
app?.setAppliance(last_shape as Appliance);
|
|
108
|
-
} else {
|
|
109
|
-
app?.setAppliance("shape", last_shape as Exclude<Shape, Appliance>);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function select_last_eraser() {
|
|
113
|
-
app?.setAppliance(last_eraser);
|
|
114
|
-
}
|
|
115
|
-
function select_eraser() {
|
|
101
|
+
function eraser() {
|
|
116
102
|
app?.setAppliance("eraser");
|
|
117
103
|
}
|
|
118
|
-
function
|
|
119
|
-
app?.setAppliance("
|
|
104
|
+
function hand() {
|
|
105
|
+
app?.setAppliance("hand");
|
|
106
|
+
}
|
|
107
|
+
function laserPointer() {
|
|
108
|
+
app?.setAppliance("laserPointer");
|
|
120
109
|
}
|
|
121
110
|
function clear() {
|
|
122
111
|
app?.cleanCurrentScene();
|
|
123
112
|
}
|
|
113
|
+
function useLaserPen() {
|
|
114
|
+
app?.setAppliance("laserPen");
|
|
115
|
+
}
|
|
124
116
|
</script>
|
|
125
117
|
|
|
126
118
|
{#if scrollable}
|
|
@@ -129,124 +121,43 @@
|
|
|
129
121
|
</Button>
|
|
130
122
|
{/if}
|
|
131
123
|
<div class="{name}-scrollable" class:scrollable use:scrollHeight={scroll_height} use:scrollTop={top}>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
124
|
+
{#each items as item}
|
|
125
|
+
{#if item === "clicker"}
|
|
126
|
+
<Clicker {appliance} {theme} {btn_props} on:click={clicker} content={c.clicker} />
|
|
127
|
+
{:else if item === "selector"}
|
|
128
|
+
<Selector {appliance} {theme} {btn_props} on:click={selector} content={c.selector} />
|
|
129
|
+
{:else if item === "pencil"}
|
|
130
|
+
<Pencil
|
|
131
|
+
{hasUseLaserPen}
|
|
132
|
+
{appliance}
|
|
133
|
+
{theme}
|
|
134
|
+
{btn_props}
|
|
135
|
+
on:click={pencil}
|
|
136
|
+
content={c.pencil}
|
|
137
|
+
menu={pencil_panel}
|
|
138
|
+
/>
|
|
139
|
+
{:else if item === "text"}
|
|
140
|
+
<Text {appliance} {theme} {btn_props} on:click={text} content={c.text} menu={text_panel} />
|
|
141
|
+
{:else if item === "shapes"}
|
|
142
|
+
<Shapes {app} {appliance} {theme} {btn_props} content={t.shapes} menu={shapes_panel} />
|
|
143
|
+
{:else if item === "eraser"}
|
|
144
|
+
<Eraser {appliance} {theme} {btn_props} on:click={eraser} content={c.eraser} />
|
|
145
|
+
{:else if item === "clear"}
|
|
146
|
+
<Clear {theme} {btn_props} on:click={clear} content={t.clear} />
|
|
147
|
+
{:else if item === "hand"}
|
|
148
|
+
<Hand {appliance} {theme} {btn_props} on:click={hand} content={c.hand} />
|
|
149
|
+
{:else if item === "laserPointer"}
|
|
150
|
+
<Laser {appliance} {theme} {btn_props} on:click={laserPointer} content={c.laserPointer} />
|
|
143
151
|
{/if}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
class="selector"
|
|
147
|
-
active={appliance === "selector"}
|
|
148
|
-
{...btn_props}
|
|
149
|
-
on:click={selector}
|
|
150
|
-
content={c.selector}
|
|
151
|
-
>
|
|
152
|
-
{#if appliance === "selector"}
|
|
153
|
-
<Icons.SelectorFilled {theme} active />
|
|
154
|
-
{:else}
|
|
155
|
-
<Icons.Selector {theme} />
|
|
156
|
-
{/if}
|
|
157
|
-
</Button>
|
|
158
|
-
<Button
|
|
159
|
-
class="pencil"
|
|
160
|
-
active={appliance === "pencil"}
|
|
161
|
-
{...btn_props}
|
|
162
|
-
on:click={pencil}
|
|
163
|
-
content={c.pencil}
|
|
164
|
-
menu={pencil_panel}
|
|
165
|
-
>
|
|
166
|
-
{#if appliance === "pencil"}
|
|
167
|
-
<Icons.PencilFilled {theme} active />
|
|
168
|
-
{:else}
|
|
169
|
-
<Icons.Pencil {theme} />
|
|
170
|
-
{/if}
|
|
171
|
-
</Button>
|
|
172
|
-
<Button
|
|
173
|
-
class="text"
|
|
174
|
-
active={appliance === "text"}
|
|
175
|
-
{...btn_props}
|
|
176
|
-
on:click={text}
|
|
177
|
-
content={c.text}
|
|
178
|
-
menu={text_panel}
|
|
179
|
-
>
|
|
180
|
-
{#if appliance === "text"}
|
|
181
|
-
<Icons.TextFilled {theme} active />
|
|
182
|
-
{:else}
|
|
183
|
-
<Icons.Text {theme} />
|
|
184
|
-
{/if}
|
|
185
|
-
</Button>
|
|
186
|
-
<Button
|
|
187
|
-
class="shapes"
|
|
188
|
-
active={appliance === last_shape || (appliance === "shape" && shape === last_shape)}
|
|
189
|
-
{...btn_props}
|
|
190
|
-
on:click={select_last_shape}
|
|
191
|
-
content={t.shapes}
|
|
192
|
-
menu={shapes_panel}
|
|
193
|
-
>
|
|
194
|
-
{#if appliance === last_shape || (appliance === "shape" && shape === last_shape)}
|
|
195
|
-
<svelte:component this={shapesIconActive[last_shape]} {theme} active />
|
|
196
|
-
{:else}
|
|
197
|
-
<svelte:component this={shapesIcon[last_shape]} {theme} />
|
|
198
|
-
{/if}
|
|
199
|
-
</Button>
|
|
200
|
-
{#if eraser_type === "delete"}
|
|
201
|
-
<Button
|
|
202
|
-
class="eraser"
|
|
203
|
-
active={appliance === "eraser"}
|
|
204
|
-
{...btn_props}
|
|
205
|
-
on:click={select_eraser}
|
|
206
|
-
content={c.eraser}
|
|
207
|
-
>
|
|
208
|
-
{#if appliance === "eraser"}
|
|
209
|
-
<Icons.EraserFilled {theme} active />
|
|
210
|
-
{:else}
|
|
211
|
-
<Icons.Eraser {theme} />
|
|
212
|
-
{/if}
|
|
213
|
-
</Button>
|
|
214
|
-
{:else if eraser_type === "pencil"}
|
|
215
|
-
<Button
|
|
216
|
-
class="eraser"
|
|
217
|
-
active={appliance === "pencilEraser"}
|
|
218
|
-
{...btn_props}
|
|
219
|
-
on:click={select_pencil_eraser}
|
|
220
|
-
content={c.pencilEraser}
|
|
221
|
-
menu={eraser_panel}
|
|
222
|
-
>
|
|
223
|
-
{#if appliance === "pencilEraser"}
|
|
224
|
-
<Icons.PencilEraserFilled {theme} active />
|
|
225
|
-
{:else}
|
|
226
|
-
<Icons.PencilEraser {theme} />
|
|
227
|
-
{/if}
|
|
228
|
-
</Button>
|
|
229
|
-
{:else}
|
|
152
|
+
{/each}
|
|
153
|
+
{#if !hide_apps}
|
|
230
154
|
<Button
|
|
231
|
-
class="
|
|
232
|
-
active={appliance === last_eraser}
|
|
155
|
+
class="apps"
|
|
233
156
|
{...btn_props}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
157
|
+
content={t.apps}
|
|
158
|
+
menu={apps_panel}
|
|
159
|
+
menu_placement={placement === "left" ? "right-end" : "left-end"}
|
|
237
160
|
>
|
|
238
|
-
{#if appliance === last_eraser}
|
|
239
|
-
<svelte:component this={eraserIconActive[last_eraser]} {theme} active />
|
|
240
|
-
{:else}
|
|
241
|
-
<svelte:component this={eraserIcon[last_eraser]} {theme} />
|
|
242
|
-
{/if}
|
|
243
|
-
</Button>
|
|
244
|
-
{/if}
|
|
245
|
-
<Button class="clear" {...btn_props} on:click={clear} content={t.clear}>
|
|
246
|
-
<Icons.Clear {theme} />
|
|
247
|
-
</Button>
|
|
248
|
-
{#if !hide_apps}
|
|
249
|
-
<Button class="apps" {...btn_props} content={t.apps} menu={apps_panel} menu_placement="right-end">
|
|
250
161
|
<Icons.Apps {theme} />
|
|
251
162
|
</Button>
|
|
252
163
|
{/if}
|
|
@@ -259,87 +170,39 @@
|
|
|
259
170
|
|
|
260
171
|
<div class="{name}-panel-wrapper" style="display:none">
|
|
261
172
|
<div class="{name}-panel pencil" bind:this={pencil_panel}>
|
|
262
|
-
{
|
|
263
|
-
|
|
264
|
-
<Button
|
|
265
|
-
|
|
266
|
-
active={appliance === "pencil" && !dotted}
|
|
267
|
-
{...btn_props}
|
|
268
|
-
on:click={unset_dotted}
|
|
269
|
-
placement="top"
|
|
270
|
-
content={t.solid}
|
|
271
|
-
>
|
|
272
|
-
<Icons.Curve {theme} active={appliance === "pencil" && !dotted} />
|
|
173
|
+
<div class="{name}-panel-switch-pencil">
|
|
174
|
+
{#if !!app?.appliancePlugin && hasUseLaserPen}
|
|
175
|
+
<Button class="{name}-panel-switch-btn" {...btn_props} on:click={pencil}>
|
|
176
|
+
<Icons.Pencil {theme} />
|
|
273
177
|
</Button>
|
|
274
|
-
<Button
|
|
275
|
-
|
|
276
|
-
active={appliance === "pencil" && dotted}
|
|
277
|
-
{...btn_props}
|
|
278
|
-
on:click={set_dotted}
|
|
279
|
-
placement="top"
|
|
280
|
-
content={t.dashed}
|
|
281
|
-
>
|
|
282
|
-
<Icons.CurveDashed {theme} active={appliance === "pencil" && dotted} />
|
|
178
|
+
<Button class="{name}-panel-switch-btn" {...btn_props}>
|
|
179
|
+
<Icons.LaserPenFilled {theme} active />
|
|
283
180
|
</Button>
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
181
|
+
{:else if !!app?.appliancePlugin}
|
|
182
|
+
<Button class="{name}-panel-switch-btn" {...btn_props}>
|
|
183
|
+
<Icons.PencilFilled {theme} active />
|
|
184
|
+
</Button>
|
|
185
|
+
<Button class="{name}-panel-switch-btn" {...btn_props} on:click={useLaserPen}>
|
|
186
|
+
<Icons.LaserPen {theme} />
|
|
187
|
+
</Button>
|
|
188
|
+
{/if}
|
|
189
|
+
</div>
|
|
287
190
|
<StrokeWidth {app} {theme} {disabled} />
|
|
288
191
|
<div class="{name}-panel-divider" />
|
|
289
|
-
<StrokeColor {app} {theme} {disabled} />
|
|
192
|
+
<StrokeColor {app} {theme} {disabled} {colors} />
|
|
290
193
|
</div>
|
|
291
194
|
<div class="{name}-panel text" bind:this={text_panel}>
|
|
292
|
-
<TextColor {app} {theme} {disabled} />
|
|
195
|
+
<TextColor {app} {theme} {disabled} {colors} />
|
|
293
196
|
</div>
|
|
294
197
|
<div class="{name}-panel shapes" bind:this={shapes_panel}>
|
|
295
|
-
<
|
|
198
|
+
<SelectShapes {app} {theme} {language} {disabled} />
|
|
296
199
|
<div class="{name}-panel-divider" />
|
|
297
200
|
<StrokeWidth {app} {theme} {disabled} />
|
|
298
201
|
<div class="{name}-panel-divider" />
|
|
299
|
-
<StrokeColor {app} {theme} {disabled} />
|
|
300
|
-
</div>
|
|
301
|
-
<div class="{name}-panel eraser" bind:this={eraser_panel}>
|
|
302
|
-
{#if eraser_type === "both"}
|
|
303
|
-
<div class="{name}-panel-btns">
|
|
304
|
-
<Button
|
|
305
|
-
class="eraser"
|
|
306
|
-
active={appliance === "pencilEraser"}
|
|
307
|
-
{...btn_props}
|
|
308
|
-
on:click={select_pencil_eraser}
|
|
309
|
-
placement="top"
|
|
310
|
-
content={c.pencilEraserForPanel}
|
|
311
|
-
>
|
|
312
|
-
{#if appliance === "pencilEraser"}
|
|
313
|
-
<Icons.PencilEraserFilled {theme} active />
|
|
314
|
-
{:else}
|
|
315
|
-
<Icons.PencilEraser {theme} />
|
|
316
|
-
{/if}
|
|
317
|
-
</Button>
|
|
318
|
-
<Button
|
|
319
|
-
class="eraser"
|
|
320
|
-
active={appliance === "eraser"}
|
|
321
|
-
{...btn_props}
|
|
322
|
-
on:click={select_eraser}
|
|
323
|
-
placement="top"
|
|
324
|
-
content={c.eraserForPanel}
|
|
325
|
-
>
|
|
326
|
-
{#if appliance === "eraser"}
|
|
327
|
-
<Icons.EraserFilled {theme} active />
|
|
328
|
-
{:else}
|
|
329
|
-
<Icons.Eraser {theme} />
|
|
330
|
-
{/if}
|
|
331
|
-
</Button>
|
|
332
|
-
</div>
|
|
333
|
-
{#if appliance === "pencilEraser"}
|
|
334
|
-
<div class="{name}-panel-divider" />
|
|
335
|
-
<PencilEraserSize {app} {theme} {disabled} />
|
|
336
|
-
{/if}
|
|
337
|
-
{:else if eraser_type === "pencil"}
|
|
338
|
-
<PencilEraserSize {app} {theme} {disabled} />
|
|
339
|
-
{/if}
|
|
202
|
+
<StrokeColor {app} {theme} {disabled} {colors} />
|
|
340
203
|
</div>
|
|
341
|
-
<div class="{name}-panel apps" style="--n:{$
|
|
342
|
-
{#each $
|
|
204
|
+
<div class="{name}-panel apps" style="--n:{$apps.length}" bind:this={apps_panel}>
|
|
205
|
+
{#each $apps as netless_app}
|
|
343
206
|
{@const { icon, label, kind, onClick } = netless_app}
|
|
344
207
|
{@const state = $status && $status[kind]}
|
|
345
208
|
{@const on_click = () => {
|
|
@@ -22,7 +22,9 @@ $thumb-color: white;
|
|
|
22
22
|
background: $thumb-color;
|
|
23
23
|
border: 0;
|
|
24
24
|
border-radius: 100%;
|
|
25
|
-
box-shadow:
|
|
25
|
+
box-shadow:
|
|
26
|
+
0 1px 1px rgba($shadow-color, 0.15),
|
|
27
|
+
0 0 0 1px rgba($shadow-color, 0.2);
|
|
26
28
|
width: $thumb-height;
|
|
27
29
|
height: $thumb-height;
|
|
28
30
|
position: relative;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Color, FastboardApp } from "@netless/fastboard-core";
|
|
3
3
|
import type { Theme } from "../../../typings";
|
|
4
|
-
import {
|
|
4
|
+
import { default_colors } from "./constants";
|
|
5
|
+
import { hexToRgb, rgbToHex } from "./helper";
|
|
5
6
|
|
|
6
7
|
export let app: FastboardApp | null | undefined = null;
|
|
7
8
|
export let theme: Theme = "light";
|
|
8
9
|
export let disabled = false;
|
|
10
|
+
export let colors: Color[] = default_colors;
|
|
9
11
|
|
|
10
12
|
$: memberState = app?.memberState;
|
|
11
13
|
$: strokeColor = $memberState?.strokeColor;
|
|
@@ -17,9 +19,9 @@
|
|
|
17
19
|
function set_stroke_color(ev: MouseEvent) {
|
|
18
20
|
let button = ev.target as HTMLButtonElement | null;
|
|
19
21
|
if (button && button.dataset.colorKey) {
|
|
20
|
-
let color =
|
|
22
|
+
let color = button.dataset.colorKey;
|
|
21
23
|
if (color && app) {
|
|
22
|
-
app.setStrokeColor(color);
|
|
24
|
+
app.setStrokeColor(hexToRgb(color));
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
|
@@ -27,14 +29,17 @@
|
|
|
27
29
|
|
|
28
30
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
29
31
|
<div class="fastboard-toolbar-colors {theme}" on:click={set_stroke_color}>
|
|
30
|
-
{#each
|
|
32
|
+
{#each colors as color}
|
|
31
33
|
<button
|
|
32
34
|
class="fastboard-toolbar-btn fastboard-toolbar-color-btn {theme}"
|
|
33
|
-
class:is-active={is_equal_color(strokeColor,
|
|
34
|
-
data-color-key={
|
|
35
|
+
class:is-active={is_equal_color(strokeColor, color)}
|
|
36
|
+
data-color-key={rgbToHex(color[0], color[1], color[2])}
|
|
35
37
|
{disabled}
|
|
36
38
|
>
|
|
37
|
-
<span
|
|
39
|
+
<span
|
|
40
|
+
class="fastboard-toolbar-color-item"
|
|
41
|
+
style:background-color={rgbToHex(color[0], color[1], color[2])}
|
|
42
|
+
/>
|
|
38
43
|
</button>
|
|
39
44
|
{/each}
|
|
40
45
|
</div>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Color, FastboardApp } from "@netless/fastboard-core";
|
|
3
3
|
import type { Theme } from "../../../typings";
|
|
4
|
-
import {
|
|
4
|
+
import { default_colors } from "./constants";
|
|
5
|
+
import { hexToRgb, rgbToHex } from "./helper";
|
|
5
6
|
|
|
6
7
|
export let app: FastboardApp | null | undefined = null;
|
|
7
8
|
export let theme: Theme = "light";
|
|
8
9
|
export let disabled = false;
|
|
10
|
+
export let colors: Color[] = default_colors;
|
|
9
11
|
|
|
10
12
|
$: memberState = app?.memberState;
|
|
11
13
|
$: textColor = $memberState?.textColor;
|
|
@@ -17,9 +19,9 @@
|
|
|
17
19
|
function set_stroke_color(ev: MouseEvent) {
|
|
18
20
|
let button = ev.target as HTMLButtonElement | null;
|
|
19
21
|
if (button && button.dataset.colorKey) {
|
|
20
|
-
let color =
|
|
22
|
+
let color = button.dataset.colorKey;
|
|
21
23
|
if (color && app) {
|
|
22
|
-
app.setTextColor(color);
|
|
24
|
+
app.setTextColor(hexToRgb(color));
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
|
@@ -27,14 +29,17 @@
|
|
|
27
29
|
|
|
28
30
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
29
31
|
<div class="fastboard-toolbar-colors {theme}" on:click={set_stroke_color}>
|
|
30
|
-
{#each
|
|
32
|
+
{#each colors as color}
|
|
31
33
|
<button
|
|
32
34
|
class="fastboard-toolbar-btn fastboard-toolbar-color-btn {theme}"
|
|
33
|
-
class:is-active={is_equal_color(textColor,
|
|
34
|
-
data-color-key={
|
|
35
|
+
class:is-active={is_equal_color(textColor, color)}
|
|
36
|
+
data-color-key={rgbToHex(color[0], color[1], color[2])}
|
|
35
37
|
{disabled}
|
|
36
38
|
>
|
|
37
|
-
<span
|
|
39
|
+
<span
|
|
40
|
+
class="fastboard-toolbar-color-item"
|
|
41
|
+
style:background-color={rgbToHex(color[0], color[1], color[2])}
|
|
42
|
+
/>
|
|
38
43
|
</button>
|
|
39
44
|
{/each}
|
|
40
45
|
</div>
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { Appliance, Color } from "@netless/fastboard-core";
|
|
2
|
-
import type { I18nData } from "../../../typings";
|
|
2
|
+
import type { I18nData, ToolbarItem } from "../../../typings";
|
|
3
3
|
import Icons from "../../Icons";
|
|
4
4
|
|
|
5
|
+
export const default_items: ToolbarItem[] = [
|
|
6
|
+
"clicker",
|
|
7
|
+
"selector",
|
|
8
|
+
"pencil",
|
|
9
|
+
"text",
|
|
10
|
+
"shapes",
|
|
11
|
+
"eraser",
|
|
12
|
+
"clear",
|
|
13
|
+
];
|
|
14
|
+
|
|
5
15
|
export const colors: Record<string, Color> = {
|
|
6
16
|
"#E02020": [224, 32, 32],
|
|
7
17
|
"#F7B500": [247, 181, 0],
|
|
@@ -13,6 +23,10 @@ export const colors: Record<string, Color> = {
|
|
|
13
23
|
"#6D7278": [109, 114, 120],
|
|
14
24
|
};
|
|
15
25
|
|
|
26
|
+
export const colorKeys = Object.keys(colors);
|
|
27
|
+
|
|
28
|
+
export const default_colors: Color[] = Object.values(colors);
|
|
29
|
+
|
|
16
30
|
export const shapes = [
|
|
17
31
|
"rectangle",
|
|
18
32
|
"ellipse",
|
|
@@ -28,6 +42,7 @@ export type Shape = (typeof shapes)[number];
|
|
|
28
42
|
|
|
29
43
|
export const applianceShapes = shapes.slice(0, 4) as Appliance[];
|
|
30
44
|
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
46
|
export const shapesIcon: Record<Shape, any> = {
|
|
32
47
|
rectangle: Icons.Rectangle,
|
|
33
48
|
ellipse: Icons.Circle,
|
|
@@ -39,6 +54,7 @@ export const shapesIcon: Record<Shape, any> = {
|
|
|
39
54
|
speechBalloon: Icons.Balloon,
|
|
40
55
|
};
|
|
41
56
|
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
58
|
export const shapesIconActive: Record<Shape, any> = {
|
|
43
59
|
rectangle: Icons.RectangleBolded,
|
|
44
60
|
ellipse: Icons.CircleBolded,
|
|
@@ -50,59 +66,29 @@ export const shapesIconActive: Record<Shape, any> = {
|
|
|
50
66
|
speechBalloon: Icons.BalloonBolded,
|
|
51
67
|
};
|
|
52
68
|
|
|
53
|
-
export const
|
|
54
|
-
|
|
55
|
-
export type Eraser = (typeof erasers)[number];
|
|
56
|
-
|
|
57
|
-
export const eraserIcon: Record<Eraser, any> = {
|
|
58
|
-
eraser: Icons.Eraser,
|
|
59
|
-
pencilEraser: Icons.PencilEraser,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const eraserIconActive: Record<Eraser, any> = {
|
|
63
|
-
eraser: Icons.EraserFilled,
|
|
64
|
-
pencilEraser: Icons.PencilEraserFilled,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const i18n: I18nData<
|
|
68
|
-
| "clicker"
|
|
69
|
-
| "selector"
|
|
70
|
-
| "pencil"
|
|
71
|
-
| "pencilEraser"
|
|
72
|
-
| "text"
|
|
73
|
-
| "shapes"
|
|
74
|
-
| "eraser"
|
|
75
|
-
| "clear"
|
|
76
|
-
| "apps"
|
|
77
|
-
| "solid"
|
|
78
|
-
| "dashed"
|
|
79
|
-
> = {
|
|
69
|
+
export const i18n: I18nData<ToolbarItem | "apps"> = {
|
|
80
70
|
en: {
|
|
81
71
|
clicker: "clicker",
|
|
82
72
|
selector: "selector",
|
|
83
73
|
pencil: "pencil",
|
|
84
74
|
eraser: "eraser",
|
|
85
|
-
pencilEraser: "eraser",
|
|
86
75
|
text: "text",
|
|
87
76
|
shapes: "shapes",
|
|
88
77
|
clear: "clear",
|
|
89
78
|
apps: "apps",
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
hand: "hand",
|
|
80
|
+
laserPointer: "laser pointer",
|
|
92
81
|
},
|
|
93
82
|
"zh-CN": {
|
|
94
83
|
clicker: "点击",
|
|
95
84
|
selector: "选择",
|
|
96
85
|
pencil: "铅笔",
|
|
97
86
|
eraser: "橡皮",
|
|
98
|
-
pencilEraser: "板擦",
|
|
99
87
|
text: "文字",
|
|
100
88
|
shapes: "形状",
|
|
101
89
|
clear: "清屏",
|
|
102
90
|
apps: "Apps",
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
hand: "抓手",
|
|
92
|
+
laserPointer: "激光笔",
|
|
105
93
|
},
|
|
106
94
|
};
|
|
107
|
-
|
|
108
|
-
export const colorKeys = Object.keys(colors);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { HotKey } from "@netless/fastboard-core";
|
|
2
|
-
import {
|
|
1
|
+
import type { Color, HotKey } from "@netless/fastboard-core";
|
|
2
|
+
import { element, attr, append } from "svelte/internal";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* ```svelte
|
|
@@ -22,3 +22,25 @@ export function tooltip(text: string, hotkey?: HotKey) {
|
|
|
22
22
|
append(outer, hotkey_span);
|
|
23
23
|
return outer;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
export function rgbToHex(r: number, g: number, b: number) {
|
|
27
|
+
const hex = ((r << 16) + (g << 8) + b).toString(16).padStart(6, "0");
|
|
28
|
+
return "#" + hex;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function hexToRgb(hex: string): Color {
|
|
32
|
+
// 移除可能存在的 # 符号
|
|
33
|
+
hex = hex.replace(/^#/, "");
|
|
34
|
+
|
|
35
|
+
// 确保 hex 是 6 位
|
|
36
|
+
if (hex.length !== 6) {
|
|
37
|
+
throw new Error("Invalid hex color");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 解析每两个字符为一个十进制数
|
|
41
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
42
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
43
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
44
|
+
|
|
45
|
+
return [r, g, b];
|
|
46
|
+
}
|