@netless/fastboard-ui 1.0.0-canary.8 → 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 +91 -89
- package/dist/index.js +7341 -3085
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7364 -3113
- package/dist/index.mjs.map +1 -0
- package/dist/index.svelte.mjs +7350 -3166
- 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/scroll.ts +1 -1
- 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.ts → Fastboard.svelte.d.ts} +6 -2
- package/src/components/Fastboard/ReplayFastboard.svelte +2 -2
- package/src/components/Fastboard/{ReplayFastboard.svelte.ts → 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 -7
- 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 -25
- package/src/components/Toolbar/Toolbar.svelte.d.ts +2 -16
- package/src/components/Toolbar/components/Contents.scss +19 -14
- package/src/components/Toolbar/components/Contents.svelte +100 -204
- package/src/components/Toolbar/components/Slider.scss +3 -1
- package/src/components/Toolbar/components/Slider.svelte +0 -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 +23 -23
- 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/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,22 +35,27 @@
|
|
|
28
35
|
export let scroll_height: Writable<number>;
|
|
29
36
|
export let computed_height = 0;
|
|
30
37
|
export let scrollable = false;
|
|
38
|
+
export let placement: "left" | "right" = "left";
|
|
39
|
+
export let items: ToolbarItem[] = default_items;
|
|
31
40
|
export let hide_apps = false;
|
|
32
|
-
export let
|
|
41
|
+
export let colors = default_colors;
|
|
33
42
|
|
|
34
43
|
const name = "fastboard-toolbar";
|
|
35
44
|
|
|
36
|
-
let last_shape: Shape = "rectangle";
|
|
37
|
-
let last_eraser: Eraser = "pencilEraser";
|
|
38
|
-
|
|
39
45
|
let pencil_panel: HTMLDivElement;
|
|
40
46
|
let text_panel: HTMLDivElement;
|
|
41
47
|
let shapes_panel: HTMLDivElement;
|
|
42
|
-
let eraser_panel: HTMLDivElement;
|
|
43
48
|
let apps_panel: HTMLDivElement;
|
|
44
49
|
|
|
45
|
-
let btn_props:
|
|
46
|
-
$: 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
|
+
|
|
47
59
|
$: t = i18n[language];
|
|
48
60
|
$: hotkeys = app?.hotKeys;
|
|
49
61
|
$: c = {
|
|
@@ -51,28 +63,20 @@
|
|
|
51
63
|
selector: tooltip(t.selector, hotkeys?.changeToSelector),
|
|
52
64
|
pencil: tooltip(t.pencil, hotkeys?.changeToPencil),
|
|
53
65
|
eraser: tooltip(t.eraser, hotkeys?.changeToEraser),
|
|
54
|
-
pencilEraser: tooltip(t.pencilEraser, hotkeys?.changeToPencilEraser),
|
|
55
|
-
eraserForPanel: tooltip(t.eraser, hotkeys?.changeToEraser),
|
|
56
|
-
pencilEraserForPanel: tooltip(t.pencilEraser, hotkeys?.changeToPencilEraser),
|
|
57
66
|
text: tooltip(t.text, hotkeys?.changeToText),
|
|
67
|
+
hand: tooltip(t.hand, hotkeys?.changeToHand),
|
|
68
|
+
laserPointer: tooltip(t.laserPointer, hotkeys?.changeToLaserPointer),
|
|
58
69
|
};
|
|
70
|
+
|
|
59
71
|
$: memberState = app?.memberState;
|
|
60
72
|
$: appliance = $memberState?.currentApplianceName;
|
|
61
|
-
$: shape = $memberState?.shapeType;
|
|
62
73
|
$: status = app?.appsStatus;
|
|
63
74
|
|
|
64
|
-
$: if (applianceShapes.includes(appliance as Appliance)) {
|
|
65
|
-
last_shape = appliance as Shape;
|
|
66
|
-
} else if (shape) {
|
|
67
|
-
last_shape = shape;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
$: if (["pencilEraser", "eraser"].includes(appliance as Appliance)) {
|
|
71
|
-
last_eraser = appliance as "pencilEraser" | "eraser";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
75
|
$: max_scroll = scrollable ? $scroll_height + (32 + 8) * 2 - computed_height : 0;
|
|
75
76
|
|
|
77
|
+
$: hasAppliancePlugin = !!app?.appliancePlugin;
|
|
78
|
+
$: hasUseLaserPen = (hasAppliancePlugin && ($memberState as ExtendMemberState)?.useLaserPen) || false;
|
|
79
|
+
|
|
76
80
|
let top = writable(0);
|
|
77
81
|
|
|
78
82
|
function scroll_up() {
|
|
@@ -94,25 +98,21 @@
|
|
|
94
98
|
function text() {
|
|
95
99
|
app?.setAppliance("text");
|
|
96
100
|
}
|
|
97
|
-
function
|
|
98
|
-
if (applianceShapes.includes(last_shape as Appliance)) {
|
|
99
|
-
app?.setAppliance(last_shape as Appliance);
|
|
100
|
-
} else {
|
|
101
|
-
app?.setAppliance("shape", last_shape as Exclude<Shape, Appliance>);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function select_last_eraser() {
|
|
105
|
-
app?.setAppliance(last_eraser);
|
|
106
|
-
}
|
|
107
|
-
function select_eraser() {
|
|
101
|
+
function eraser() {
|
|
108
102
|
app?.setAppliance("eraser");
|
|
109
103
|
}
|
|
110
|
-
function
|
|
111
|
-
app?.setAppliance("
|
|
104
|
+
function hand() {
|
|
105
|
+
app?.setAppliance("hand");
|
|
106
|
+
}
|
|
107
|
+
function laserPointer() {
|
|
108
|
+
app?.setAppliance("laserPointer");
|
|
112
109
|
}
|
|
113
110
|
function clear() {
|
|
114
111
|
app?.cleanCurrentScene();
|
|
115
112
|
}
|
|
113
|
+
function useLaserPen() {
|
|
114
|
+
app?.setAppliance("laserPen");
|
|
115
|
+
}
|
|
116
116
|
</script>
|
|
117
117
|
|
|
118
118
|
{#if scrollable}
|
|
@@ -121,124 +121,43 @@
|
|
|
121
121
|
</Button>
|
|
122
122
|
{/if}
|
|
123
123
|
<div class="{name}-scrollable" class:scrollable use:scrollHeight={scroll_height} use:scrollTop={top}>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
class="pencil"
|
|
152
|
-
active={appliance === "pencil"}
|
|
153
|
-
{...btn_props}
|
|
154
|
-
on:click={pencil}
|
|
155
|
-
content={c.pencil}
|
|
156
|
-
menu={pencil_panel}
|
|
157
|
-
>
|
|
158
|
-
{#if appliance === "pencil"}
|
|
159
|
-
<Icons.PencilFilled {theme} active />
|
|
160
|
-
{:else}
|
|
161
|
-
<Icons.Pencil {theme} />
|
|
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} />
|
|
162
151
|
{/if}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
class="text"
|
|
166
|
-
active={appliance === "text"}
|
|
167
|
-
{...btn_props}
|
|
168
|
-
on:click={text}
|
|
169
|
-
content={c.text}
|
|
170
|
-
menu={text_panel}
|
|
171
|
-
>
|
|
172
|
-
{#if appliance === "text"}
|
|
173
|
-
<Icons.TextFilled {theme} active />
|
|
174
|
-
{:else}
|
|
175
|
-
<Icons.Text {theme} />
|
|
176
|
-
{/if}
|
|
177
|
-
</Button>
|
|
178
|
-
<Button
|
|
179
|
-
class="shapes"
|
|
180
|
-
active={appliance === last_shape || (appliance === "shape" && shape === last_shape)}
|
|
181
|
-
{...btn_props}
|
|
182
|
-
on:click={select_last_shape}
|
|
183
|
-
content={t.shapes}
|
|
184
|
-
menu={shapes_panel}
|
|
185
|
-
>
|
|
186
|
-
{#if appliance === last_shape || (appliance === "shape" && shape === last_shape)}
|
|
187
|
-
<svelte:component this={shapesIconActive[last_shape]} {theme} active />
|
|
188
|
-
{:else}
|
|
189
|
-
<svelte:component this={shapesIcon[last_shape]} {theme} />
|
|
190
|
-
{/if}
|
|
191
|
-
</Button>
|
|
192
|
-
{#if eraser_type === "delete"}
|
|
193
|
-
<Button
|
|
194
|
-
class="eraser"
|
|
195
|
-
active={appliance === "eraser"}
|
|
196
|
-
{...btn_props}
|
|
197
|
-
on:click={select_eraser}
|
|
198
|
-
content={c.eraser}
|
|
199
|
-
>
|
|
200
|
-
{#if appliance === "eraser"}
|
|
201
|
-
<Icons.EraserFilled {theme} active />
|
|
202
|
-
{:else}
|
|
203
|
-
<Icons.Eraser {theme} />
|
|
204
|
-
{/if}
|
|
205
|
-
</Button>
|
|
206
|
-
{:else if eraser_type === "pencil"}
|
|
207
|
-
<Button
|
|
208
|
-
class="eraser"
|
|
209
|
-
active={appliance === "pencilEraser"}
|
|
210
|
-
{...btn_props}
|
|
211
|
-
on:click={select_pencil_eraser}
|
|
212
|
-
content={c.pencilEraser}
|
|
213
|
-
menu={eraser_panel}
|
|
214
|
-
>
|
|
215
|
-
{#if appliance === "pencilEraser"}
|
|
216
|
-
<Icons.PencilEraserFilled {theme} active />
|
|
217
|
-
{:else}
|
|
218
|
-
<Icons.PencilEraser {theme} />
|
|
219
|
-
{/if}
|
|
220
|
-
</Button>
|
|
221
|
-
{:else}
|
|
152
|
+
{/each}
|
|
153
|
+
{#if !hide_apps}
|
|
222
154
|
<Button
|
|
223
|
-
class="
|
|
224
|
-
active={appliance === last_eraser}
|
|
155
|
+
class="apps"
|
|
225
156
|
{...btn_props}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
157
|
+
content={t.apps}
|
|
158
|
+
menu={apps_panel}
|
|
159
|
+
menu_placement={placement === "left" ? "right-end" : "left-end"}
|
|
229
160
|
>
|
|
230
|
-
{#if appliance === last_eraser}
|
|
231
|
-
<svelte:component this={eraserIconActive[last_eraser]} {theme} active />
|
|
232
|
-
{:else}
|
|
233
|
-
<svelte:component this={eraserIcon[last_eraser]} {theme} />
|
|
234
|
-
{/if}
|
|
235
|
-
</Button>
|
|
236
|
-
{/if}
|
|
237
|
-
<Button class="clear" {...btn_props} on:click={clear} content={t.clear}>
|
|
238
|
-
<Icons.Clear {theme} />
|
|
239
|
-
</Button>
|
|
240
|
-
{#if !hide_apps}
|
|
241
|
-
<Button class="apps" {...btn_props} content={t.apps} menu={apps_panel} menu_placement="right-end">
|
|
242
161
|
<Icons.Apps {theme} />
|
|
243
162
|
</Button>
|
|
244
163
|
{/if}
|
|
@@ -251,62 +170,39 @@
|
|
|
251
170
|
|
|
252
171
|
<div class="{name}-panel-wrapper" style="display:none">
|
|
253
172
|
<div class="{name}-panel pencil" bind:this={pencil_panel}>
|
|
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} />
|
|
177
|
+
</Button>
|
|
178
|
+
<Button class="{name}-panel-switch-btn" {...btn_props}>
|
|
179
|
+
<Icons.LaserPenFilled {theme} active />
|
|
180
|
+
</Button>
|
|
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>
|
|
254
190
|
<StrokeWidth {app} {theme} {disabled} />
|
|
255
191
|
<div class="{name}-panel-divider" />
|
|
256
|
-
<StrokeColor {app} {theme} {disabled} />
|
|
192
|
+
<StrokeColor {app} {theme} {disabled} {colors} />
|
|
257
193
|
</div>
|
|
258
194
|
<div class="{name}-panel text" bind:this={text_panel}>
|
|
259
|
-
<TextColor {app} {theme} {disabled} />
|
|
195
|
+
<TextColor {app} {theme} {disabled} {colors} />
|
|
260
196
|
</div>
|
|
261
197
|
<div class="{name}-panel shapes" bind:this={shapes_panel}>
|
|
262
|
-
<
|
|
198
|
+
<SelectShapes {app} {theme} {language} {disabled} />
|
|
263
199
|
<div class="{name}-panel-divider" />
|
|
264
200
|
<StrokeWidth {app} {theme} {disabled} />
|
|
265
201
|
<div class="{name}-panel-divider" />
|
|
266
|
-
<StrokeColor {app} {theme} {disabled} />
|
|
267
|
-
</div>
|
|
268
|
-
<div class="{name}-panel eraser" bind:this={eraser_panel}>
|
|
269
|
-
{#if eraser_type === "both"}
|
|
270
|
-
<div class="{name}-panel-btns">
|
|
271
|
-
<Button
|
|
272
|
-
class="eraser"
|
|
273
|
-
active={appliance === "pencilEraser"}
|
|
274
|
-
{...btn_props}
|
|
275
|
-
on:click={select_pencil_eraser}
|
|
276
|
-
placement="top"
|
|
277
|
-
content={c.pencilEraserForPanel}
|
|
278
|
-
>
|
|
279
|
-
{#if appliance === "pencilEraser"}
|
|
280
|
-
<Icons.PencilEraserFilled {theme} active />
|
|
281
|
-
{:else}
|
|
282
|
-
<Icons.PencilEraser {theme} />
|
|
283
|
-
{/if}
|
|
284
|
-
</Button>
|
|
285
|
-
<Button
|
|
286
|
-
class="eraser"
|
|
287
|
-
active={appliance === "eraser"}
|
|
288
|
-
{...btn_props}
|
|
289
|
-
on:click={select_eraser}
|
|
290
|
-
placement="top"
|
|
291
|
-
content={c.eraserForPanel}
|
|
292
|
-
>
|
|
293
|
-
{#if appliance === "eraser"}
|
|
294
|
-
<Icons.EraserFilled {theme} active />
|
|
295
|
-
{:else}
|
|
296
|
-
<Icons.Eraser {theme} />
|
|
297
|
-
{/if}
|
|
298
|
-
</Button>
|
|
299
|
-
</div>
|
|
300
|
-
{#if appliance === "pencilEraser"}
|
|
301
|
-
<div class="{name}-panel-divider" />
|
|
302
|
-
<PencilEraserSize {app} {theme} {disabled} />
|
|
303
|
-
{/if}
|
|
304
|
-
{:else if eraser_type === "pencil"}
|
|
305
|
-
<PencilEraserSize {app} {theme} {disabled} />
|
|
306
|
-
{/if}
|
|
202
|
+
<StrokeColor {app} {theme} {disabled} {colors} />
|
|
307
203
|
</div>
|
|
308
|
-
<div class="{name}-panel apps" style="--n:{$
|
|
309
|
-
{#each $
|
|
204
|
+
<div class="{name}-panel apps" style="--n:{$apps.length}" bind:this={apps_panel}>
|
|
205
|
+
{#each $apps as netless_app}
|
|
310
206
|
{@const { icon, label, kind, onClick } = netless_app}
|
|
311
207
|
{@const state = $status && $status[kind]}
|
|
312
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",
|
|
@@ -24,10 +38,11 @@ export const shapes = [
|
|
|
24
38
|
"speechBalloon",
|
|
25
39
|
] as const;
|
|
26
40
|
|
|
27
|
-
export type Shape = typeof shapes[number];
|
|
41
|
+
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,45 +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" | "selector" | "pencil" | "pencilEraser" | "text" | "shapes" | "eraser" | "clear" | "apps"
|
|
69
|
-
> = {
|
|
69
|
+
export const i18n: I18nData<ToolbarItem | "apps"> = {
|
|
70
70
|
en: {
|
|
71
71
|
clicker: "clicker",
|
|
72
72
|
selector: "selector",
|
|
73
73
|
pencil: "pencil",
|
|
74
74
|
eraser: "eraser",
|
|
75
|
-
pencilEraser: "eraser",
|
|
76
75
|
text: "text",
|
|
77
76
|
shapes: "shapes",
|
|
78
77
|
clear: "clear",
|
|
79
78
|
apps: "apps",
|
|
79
|
+
hand: "hand",
|
|
80
|
+
laserPointer: "laser pointer",
|
|
80
81
|
},
|
|
81
82
|
"zh-CN": {
|
|
82
83
|
clicker: "点击",
|
|
83
84
|
selector: "选择",
|
|
84
85
|
pencil: "铅笔",
|
|
85
86
|
eraser: "橡皮",
|
|
86
|
-
pencilEraser: "板擦",
|
|
87
87
|
text: "文字",
|
|
88
88
|
shapes: "形状",
|
|
89
89
|
clear: "清屏",
|
|
90
90
|
apps: "Apps",
|
|
91
|
+
hand: "抓手",
|
|
92
|
+
laserPointer: "激光笔",
|
|
91
93
|
},
|
|
92
94
|
};
|
|
93
|
-
|
|
94
|
-
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Theme } from "../../../typings";
|
|
3
|
+
import Button, { type ButtonProps } from "../../Button";
|
|
4
|
+
import Icons from "../../Icons";
|
|
5
|
+
|
|
6
|
+
export let btn_props: Partial<ButtonProps> = {};
|
|
7
|
+
export let content: ButtonProps["content"] | undefined;
|
|
8
|
+
export let theme: Theme = "light";
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<Button class="clear" {...btn_props} on:click {content}>
|
|
12
|
+
<Icons.Clear {theme} />
|
|
13
|
+
</Button>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ApplianceNames } from "@netless/fastboard-core";
|
|
3
|
+
import type { Theme } from "../../../typings";
|
|
4
|
+
import Button, { type ButtonProps } from "../../Button";
|
|
5
|
+
import Icons from "../../Icons";
|
|
6
|
+
|
|
7
|
+
export let btn_props: Partial<ButtonProps> = {};
|
|
8
|
+
export let content: ButtonProps["content"] | undefined;
|
|
9
|
+
export let appliance: ApplianceNames | undefined;
|
|
10
|
+
export let theme: Theme = "light";
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<Button class="clicker" {...btn_props} on:click {content}>
|
|
14
|
+
{#if appliance === "clicker"}
|
|
15
|
+
<Icons.ClickFilled {theme} active />
|
|
16
|
+
{:else}
|
|
17
|
+
<Icons.Click {theme} />
|
|
18
|
+
{/if}
|
|
19
|
+
</Button>
|