@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.
Files changed (64) hide show
  1. package/dist/full.d.ts +178 -0
  2. package/dist/index.d.ts +91 -89
  3. package/dist/index.js +7341 -3085
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +7364 -3113
  6. package/dist/index.mjs.map +1 -0
  7. package/dist/index.svelte.mjs +7350 -3166
  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/scroll.ts +1 -1
  13. package/src/actions/tippy.ts +6 -11
  14. package/src/behaviors/apps.ts +38 -4
  15. package/src/components/Button/Button.svelte +1 -4
  16. package/src/components/Button/Button.svelte.d.ts +1 -2
  17. package/src/components/Fastboard/Fastboard.scss +14 -2
  18. package/src/components/Fastboard/Fastboard.svelte +15 -5
  19. package/src/components/Fastboard/{Fastboard.svelte.ts → Fastboard.svelte.d.ts} +6 -2
  20. package/src/components/Fastboard/ReplayFastboard.svelte +2 -2
  21. package/src/components/Fastboard/{ReplayFastboard.svelte.ts → ReplayFastboard.svelte.d.ts} +3 -3
  22. package/src/components/Icon/Icon.svelte.d.ts +1 -1
  23. package/src/components/Icons/{PencilEraser.svelte → Drag.svelte} +2 -2
  24. package/src/components/Icons/{PencilEraserFilled.svelte → DragFilled.svelte} +1 -3
  25. package/src/components/Icons/Eraser.svelte +1 -35
  26. package/src/components/Icons/EraserFilled.svelte +2 -2
  27. package/src/components/Icons/Laser.svelte +21 -0
  28. package/src/components/Icons/LaserFilled.svelte +14 -0
  29. package/src/components/Icons/LaserPen.svelte +20 -0
  30. package/src/components/Icons/LaserPenFilled.svelte +20 -0
  31. package/src/components/Icons/index.ts +12 -7
  32. package/src/components/PageControl/PageControl.svelte +4 -3
  33. package/src/components/PageControl/PageControl.svelte.d.ts +1 -1
  34. package/src/components/PlayerControl/PlayerControl.svelte.d.ts +1 -1
  35. package/src/components/RedoUndo/RedoUndo.svelte +2 -1
  36. package/src/components/RedoUndo/RedoUndo.svelte.d.ts +1 -1
  37. package/src/components/Toolbar/Toolbar.scss +14 -4
  38. package/src/components/Toolbar/Toolbar.svelte +34 -25
  39. package/src/components/Toolbar/Toolbar.svelte.d.ts +2 -16
  40. package/src/components/Toolbar/components/Contents.scss +19 -14
  41. package/src/components/Toolbar/components/Contents.svelte +100 -204
  42. package/src/components/Toolbar/components/Slider.scss +3 -1
  43. package/src/components/Toolbar/components/Slider.svelte +0 -1
  44. package/src/components/Toolbar/components/StrokeColor.svelte +12 -7
  45. package/src/components/Toolbar/components/TextColor.svelte +12 -7
  46. package/src/components/Toolbar/components/constants.ts +23 -23
  47. package/src/components/Toolbar/components/helper.ts +24 -2
  48. package/src/components/Toolbar/definitions/Clear.svelte +13 -0
  49. package/src/components/Toolbar/definitions/Clicker.svelte +19 -0
  50. package/src/components/Toolbar/definitions/Eraser.svelte +19 -0
  51. package/src/components/Toolbar/definitions/Hand.svelte +19 -0
  52. package/src/components/Toolbar/definitions/Laser.svelte +19 -0
  53. package/src/components/Toolbar/definitions/Pencil.svelte +23 -0
  54. package/src/components/Toolbar/definitions/Selector.svelte +19 -0
  55. package/src/components/Toolbar/definitions/Shapes.svelte +41 -0
  56. package/src/components/Toolbar/definitions/Text.svelte +20 -0
  57. package/src/components/ZoomControl/ZoomControl.svelte +5 -6
  58. package/src/components/ZoomControl/ZoomControl.svelte.d.ts +1 -1
  59. package/src/components/theme.scss +4 -11
  60. package/src/helpers/index.ts +60 -64
  61. package/src/typings.ts +42 -25
  62. package/dist/index.css +0 -888
  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,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 eraser_type: "delete" | "pencil" | "both" = "both";
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: { name: string; theme: Theme; disabled: boolean; placement: Placement };
46
- $: 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
+
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 select_last_shape() {
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 select_pencil_eraser() {
111
- app?.setAppliance("pencilEraser");
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
- <Button
125
- class="clicker"
126
- active={appliance === "clicker"}
127
- {...btn_props}
128
- on:click={clicker}
129
- content={c.clicker}
130
- >
131
- {#if appliance === "clicker"}
132
- <Icons.ClickFilled {theme} active />
133
- {:else}
134
- <Icons.Click {theme} />
135
- {/if}
136
- </Button>
137
- <Button
138
- class="selector"
139
- active={appliance === "selector"}
140
- {...btn_props}
141
- on:click={selector}
142
- content={c.selector}
143
- >
144
- {#if appliance === "selector"}
145
- <Icons.SelectorFilled {theme} active />
146
- {:else}
147
- <Icons.Selector {theme} />
148
- {/if}
149
- </Button>
150
- <Button
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
- </Button>
164
- <Button
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="eraser"
224
- active={appliance === last_eraser}
155
+ class="apps"
225
156
  {...btn_props}
226
- on:click={select_last_eraser}
227
- content={t[last_eraser]}
228
- menu={eraser_panel}
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
- <Shapes {app} {theme} {language} {disabled} />
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:{$stockedApps.length}" bind:this={apps_panel}>
309
- {#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}
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: 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;
@@ -39,7 +39,6 @@
39
39
  class="{name}-track {theme}"
40
40
  class:grabbing
41
41
  type="range"
42
- role="slider"
43
42
  {disabled}
44
43
  {min}
45
44
  {max}
@@ -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",
@@ -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 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" | "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 { 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
+ }
@@ -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>