@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.
Files changed (64) hide show
  1. package/dist/full.d.ts +178 -0
  2. package/dist/index.d.ts +74 -75
  3. package/dist/index.js +4200 -3679
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +4188 -3650
  6. package/dist/index.mjs.map +1 -0
  7. package/dist/index.svelte.mjs +6102 -6540
  8. package/dist/index.svelte.mjs.map +1 -0
  9. package/dist/lite.d.ts +178 -0
  10. package/lite.d.ts +1 -0
  11. package/package.json +19 -10
  12. package/src/actions/tippy.ts +6 -11
  13. package/src/behaviors/apps.ts +38 -4
  14. package/src/components/Button/Button.svelte +1 -4
  15. package/src/components/Button/Button.svelte.d.ts +1 -2
  16. package/src/components/Fastboard/Fastboard.scss +14 -2
  17. package/src/components/Fastboard/Fastboard.svelte +15 -5
  18. package/src/components/Fastboard/Fastboard.svelte.d.ts +6 -2
  19. package/src/components/Fastboard/ReplayFastboard.svelte +2 -2
  20. package/src/components/Fastboard/ReplayFastboard.svelte.d.ts +3 -3
  21. package/src/components/Icon/Icon.svelte.d.ts +1 -1
  22. package/src/components/Icons/{PencilEraser.svelte → Drag.svelte} +2 -2
  23. package/src/components/Icons/{PencilEraserFilled.svelte → DragFilled.svelte} +1 -3
  24. package/src/components/Icons/Eraser.svelte +1 -35
  25. package/src/components/Icons/EraserFilled.svelte +2 -2
  26. package/src/components/Icons/Laser.svelte +21 -0
  27. package/src/components/Icons/LaserFilled.svelte +14 -0
  28. package/src/components/Icons/LaserPen.svelte +20 -0
  29. package/src/components/Icons/LaserPenFilled.svelte +20 -0
  30. package/src/components/Icons/index.ts +12 -11
  31. package/src/components/PageControl/PageControl.svelte +4 -3
  32. package/src/components/PageControl/PageControl.svelte.d.ts +1 -1
  33. package/src/components/PlayerControl/PlayerControl.svelte.d.ts +1 -1
  34. package/src/components/RedoUndo/RedoUndo.svelte +2 -1
  35. package/src/components/RedoUndo/RedoUndo.svelte.d.ts +1 -1
  36. package/src/components/Toolbar/Toolbar.scss +14 -4
  37. package/src/components/Toolbar/Toolbar.svelte +34 -27
  38. package/src/components/Toolbar/Toolbar.svelte.d.ts +2 -19
  39. package/src/components/Toolbar/components/Contents.scss +19 -14
  40. package/src/components/Toolbar/components/Contents.svelte +98 -235
  41. package/src/components/Toolbar/components/Slider.scss +3 -1
  42. package/src/components/Toolbar/components/StrokeColor.svelte +12 -7
  43. package/src/components/Toolbar/components/TextColor.svelte +12 -7
  44. package/src/components/Toolbar/components/constants.ts +22 -36
  45. package/src/components/Toolbar/components/helper.ts +24 -2
  46. package/src/components/Toolbar/definitions/Clear.svelte +13 -0
  47. package/src/components/Toolbar/definitions/Clicker.svelte +19 -0
  48. package/src/components/Toolbar/definitions/Eraser.svelte +19 -0
  49. package/src/components/Toolbar/definitions/Hand.svelte +19 -0
  50. package/src/components/Toolbar/definitions/Laser.svelte +19 -0
  51. package/src/components/Toolbar/definitions/Pencil.svelte +23 -0
  52. package/src/components/Toolbar/definitions/Selector.svelte +19 -0
  53. package/src/components/Toolbar/definitions/Shapes.svelte +41 -0
  54. package/src/components/Toolbar/definitions/Text.svelte +20 -0
  55. package/src/components/ZoomControl/ZoomControl.svelte +5 -6
  56. package/src/components/ZoomControl/ZoomControl.svelte.d.ts +1 -1
  57. package/src/components/theme.scss +4 -11
  58. package/src/helpers/index.ts +60 -64
  59. package/src/typings.ts +42 -25
  60. package/dist/index.css +0 -888
  61. package/src/components/Icons/Curve.svelte +0 -10
  62. package/src/components/Icons/CurveDashed.svelte +0 -16
  63. package/src/components/Toolbar/components/PencilEraserSize.svelte +0 -27
  64. /package/src/components/Toolbar/components/{Shapes.svelte → SelectShapes.svelte} +0 -0
@@ -1,25 +1,32 @@
1
1
  <script lang="ts">
2
- import type { Appliance, FastboardApp } from "@netless/fastboard-core";
2
+ import type { FastboardApp, ExtendMemberState } from "@netless/fastboard-core";
3
3
  import type { Writable } from "svelte/store";
4
- import type { Placement } from "tippy.js";
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 { stockedApps } from "../../../behaviors";
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 Shapes from "./Shapes.svelte";
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 hide_dotted = false;
38
+ export let placement: "left" | "right" = "left";
39
+ export let items: ToolbarItem[] = default_items;
32
40
  export let hide_apps = false;
33
- export let eraser_type: "delete" | "pencil" | "both" = "both";
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: { name: string; theme: Theme; disabled: boolean; placement: Placement };
47
- $: btn_props = { name, theme, disabled, placement: "right" };
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 set_dotted() {
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 select_pencil_eraser() {
119
- app?.setAppliance("pencilEraser");
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
- <Button
133
- class="clicker"
134
- active={appliance === "clicker"}
135
- {...btn_props}
136
- on:click={clicker}
137
- content={c.clicker}
138
- >
139
- {#if appliance === "clicker"}
140
- <Icons.ClickFilled {theme} active />
141
- {:else}
142
- <Icons.Click {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} />
143
151
  {/if}
144
- </Button>
145
- <Button
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="eraser"
232
- active={appliance === last_eraser}
155
+ class="apps"
233
156
  {...btn_props}
234
- on:click={select_last_eraser}
235
- content={t[last_eraser]}
236
- menu={eraser_panel}
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
- {#if !hide_dotted}
263
- <div class="{name}-panel-btns">
264
- <Button
265
- class="pencil"
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
- class="pencil"
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
- </div>
285
- <div class="{name}-panel-divider" />
286
- {/if}
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
- <Shapes {app} {theme} {language} {disabled} />
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:{$stockedApps.length}" bind:this={apps_panel}>
342
- {#each $stockedApps as netless_app}
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: 0 1px 1px rgba($shadow-color, 0.15), 0 0 0 1px rgba($shadow-color, 0.2);
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 { colorKeys, colors } from "./constants";
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 = colors[button.dataset.colorKey];
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 colorKeys as key (key)}
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, colors[key])}
34
- data-color-key={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 class="fastboard-toolbar-color-item" style:background-color={key} />
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 { colorKeys, colors } from "./constants";
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 = colors[button.dataset.colorKey];
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 colorKeys as key (key)}
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, colors[key])}
34
- data-color-key={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 class="fastboard-toolbar-color-item" style:background-color={key} />
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 erasers = ["eraser", "pencilEraser"] as 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
- solid: "solid",
91
- dashed: "dashed",
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
- solid: "实线",
104
- dashed: "虚线",
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 { append, attr, element } from "svelte/internal";
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
+ }