js-draw 0.5.0 → 0.6.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 (73) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/src/Editor.d.ts +8 -5
  4. package/dist/src/Editor.js +4 -1
  5. package/dist/src/EditorImage.d.ts +3 -0
  6. package/dist/src/EditorImage.js +7 -0
  7. package/dist/src/SVGLoader.js +5 -6
  8. package/dist/src/components/AbstractComponent.d.ts +1 -0
  9. package/dist/src/components/AbstractComponent.js +4 -0
  10. package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -0
  11. package/dist/src/components/SVGGlobalAttributesObject.js +3 -0
  12. package/dist/src/components/Text.d.ts +3 -5
  13. package/dist/src/components/Text.js +19 -10
  14. package/dist/src/components/UnknownSVGObject.d.ts +1 -0
  15. package/dist/src/components/UnknownSVGObject.js +3 -0
  16. package/dist/src/components/builders/FreehandLineBuilder.js +2 -2
  17. package/dist/src/testing/beforeEachFile.js +4 -0
  18. package/dist/src/toolbar/HTMLToolbar.js +2 -3
  19. package/dist/src/toolbar/IconProvider.d.ts +24 -0
  20. package/dist/src/toolbar/IconProvider.js +415 -0
  21. package/dist/src/toolbar/lib.d.ts +1 -1
  22. package/dist/src/toolbar/lib.js +1 -2
  23. package/dist/src/toolbar/localization.d.ts +0 -1
  24. package/dist/src/toolbar/localization.js +0 -1
  25. package/dist/src/toolbar/makeColorInput.js +1 -2
  26. package/dist/src/toolbar/widgets/BaseWidget.js +1 -2
  27. package/dist/src/toolbar/widgets/EraserToolWidget.js +1 -2
  28. package/dist/src/toolbar/widgets/HandToolWidget.d.ts +5 -3
  29. package/dist/src/toolbar/widgets/HandToolWidget.js +35 -12
  30. package/dist/src/toolbar/widgets/PenToolWidget.js +2 -3
  31. package/dist/src/toolbar/widgets/SelectionToolWidget.d.ts +3 -0
  32. package/dist/src/toolbar/widgets/SelectionToolWidget.js +20 -7
  33. package/dist/src/toolbar/widgets/TextToolWidget.js +1 -2
  34. package/dist/src/tools/PanZoom.d.ts +1 -1
  35. package/dist/src/tools/PanZoom.js +4 -1
  36. package/dist/src/tools/SelectionTool/SelectionTool.d.ts +3 -0
  37. package/dist/src/tools/SelectionTool/SelectionTool.js +66 -3
  38. package/dist/src/tools/ToolController.js +1 -0
  39. package/dist/src/tools/localization.d.ts +1 -0
  40. package/dist/src/tools/localization.js +1 -0
  41. package/package.json +1 -1
  42. package/src/Editor.ts +11 -5
  43. package/src/EditorImage.ts +9 -0
  44. package/src/SVGLoader.test.ts +37 -0
  45. package/src/SVGLoader.ts +5 -6
  46. package/src/components/AbstractComponent.ts +5 -0
  47. package/src/components/SVGGlobalAttributesObject.ts +4 -0
  48. package/src/components/Text.test.ts +1 -16
  49. package/src/components/Text.ts +21 -11
  50. package/src/components/UnknownSVGObject.ts +4 -0
  51. package/src/components/builders/FreehandLineBuilder.ts +2 -2
  52. package/src/testing/beforeEachFile.ts +6 -1
  53. package/src/toolbar/HTMLToolbar.ts +2 -3
  54. package/src/toolbar/IconProvider.ts +476 -0
  55. package/src/toolbar/lib.ts +1 -1
  56. package/src/toolbar/localization.ts +0 -2
  57. package/src/toolbar/makeColorInput.ts +1 -2
  58. package/src/toolbar/widgets/BaseWidget.ts +1 -2
  59. package/src/toolbar/widgets/EraserToolWidget.ts +1 -2
  60. package/src/toolbar/widgets/HandToolWidget.ts +42 -20
  61. package/src/toolbar/widgets/PenToolWidget.ts +2 -3
  62. package/src/toolbar/widgets/SelectionToolWidget.ts +24 -8
  63. package/src/toolbar/widgets/TextToolWidget.ts +1 -2
  64. package/src/tools/PanZoom.ts +4 -1
  65. package/src/tools/SelectionTool/SelectionTool.css +1 -0
  66. package/src/tools/SelectionTool/SelectionTool.test.ts +40 -0
  67. package/src/tools/SelectionTool/SelectionTool.ts +73 -4
  68. package/src/tools/ToolController.ts +1 -0
  69. package/src/tools/localization.ts +4 -0
  70. package/typedoc.json +5 -1
  71. package/dist/src/toolbar/icons.d.ts +0 -20
  72. package/dist/src/toolbar/icons.js +0 -385
  73. package/src/toolbar/icons.ts +0 -443
@@ -0,0 +1,476 @@
1
+ import Color4 from '../Color4';
2
+ import { ComponentBuilderFactory } from '../components/builders/types';
3
+ import { TextStyle } from '../components/Text';
4
+ import EventDispatcher from '../EventDispatcher';
5
+ import { Vec2 } from '../math/Vec2';
6
+ import SVGRenderer from '../rendering/renderers/SVGRenderer';
7
+ import Pen from '../tools/Pen';
8
+ import { StrokeDataPoint } from '../types';
9
+ import Viewport from '../Viewport';
10
+
11
+
12
+
13
+ const svgNamespace = 'http://www.w3.org/2000/svg';
14
+ const iconColorFill = `
15
+ style='fill: var(--icon-color);'
16
+ `;
17
+ const iconColorStrokeFill = `
18
+ style='fill: var(--icon-color); stroke: var(--icon-color);'
19
+ `;
20
+ const checkerboardPatternDef = `
21
+ <pattern
22
+ id='checkerboard'
23
+ viewBox='0,0,10,10'
24
+ width='20%'
25
+ height='20%'
26
+ patternUnits='userSpaceOnUse'
27
+ >
28
+ <rect x=0 y=0 width=10 height=10 fill='white'/>
29
+ <rect x=0 y=0 width=5 height=5 fill='gray'/>
30
+ <rect x=5 y=5 width=5 height=5 fill='gray'/>
31
+ </pattern>
32
+ `;
33
+ const checkerboardPatternRef = 'url(#checkerboard)';
34
+
35
+ // Provides icons that can be used in the toolbar, etc.
36
+ // Extend this class and override methods to customize icons.
37
+ export default class IconProvider {
38
+
39
+ public makeUndoIcon() {
40
+ return this.makeRedoIcon(true);
41
+ }
42
+
43
+ // @param mirror - reflect across the x-axis @internal
44
+ // @returns a redo icon.
45
+ public makeRedoIcon(mirror: boolean = false) {
46
+ const icon = document.createElementNS(svgNamespace, 'svg');
47
+ icon.innerHTML = `
48
+ <style>
49
+ .toolbar-svg-undo-redo-icon {
50
+ stroke: var(--icon-color);
51
+ stroke-width: 12;
52
+ stroke-linejoin: round;
53
+ stroke-linecap: round;
54
+ fill: none;
55
+
56
+ transform-origin: center;
57
+ }
58
+ </style>
59
+ <path
60
+ d='M20,20 A15,15 0 0 1 70,80 L80,90 L60,70 L65,90 L87,90 L65,80'
61
+ class='toolbar-svg-undo-redo-icon'
62
+ style='${mirror ? 'transform: scale(-1, 1);' : ''}'/>
63
+ `;
64
+ icon.setAttribute('viewBox', '0 0 100 100');
65
+ return icon;
66
+ }
67
+
68
+ public makeDropdownIcon() {
69
+ const icon = document.createElementNS(svgNamespace, 'svg');
70
+ icon.innerHTML = `
71
+ <g>
72
+ <path
73
+ d='M5,10 L50,90 L95,10 Z'
74
+ ${iconColorFill}
75
+ />
76
+ </g>
77
+ `;
78
+ icon.setAttribute('viewBox', '0 0 100 100');
79
+ return icon;
80
+ }
81
+
82
+ public makeEraserIcon() {
83
+ const icon = document.createElementNS(svgNamespace, 'svg');
84
+
85
+ // Draw an eraser-like shape
86
+ icon.innerHTML = `
87
+ <g>
88
+ <rect x=10 y=50 width=80 height=30 rx=10 fill='pink' />
89
+ <rect
90
+ x=10 y=10 width=80 height=50
91
+ ${iconColorFill}
92
+ />
93
+ </g>
94
+ `;
95
+ icon.setAttribute('viewBox', '0 0 100 100');
96
+ return icon;
97
+ }
98
+
99
+ public makeSelectionIcon() {
100
+ const icon = document.createElementNS(svgNamespace, 'svg');
101
+
102
+ // Draw a cursor-like shape
103
+ icon.innerHTML = `
104
+ <g>
105
+ <rect x=10 y=10 width=70 height=70 fill='pink' stroke='black'/>
106
+ <rect x=75 y=75 width=10 height=10 fill='white' stroke='black'/>
107
+ </g>
108
+ `;
109
+ icon.setAttribute('viewBox', '0 0 100 100');
110
+
111
+ return icon;
112
+ }
113
+
114
+ protected makeIconFromPath(
115
+ pathData: string,
116
+ fill: string = 'var(--icon-color)',
117
+ strokeColor: string = 'none',
118
+ strokeWidth: string = '0px',
119
+ ) {
120
+ const icon = document.createElementNS(svgNamespace, 'svg');
121
+ const path = document.createElementNS(svgNamespace, 'path');
122
+ path.setAttribute('d', pathData);
123
+ path.style.fill = fill;
124
+ path.style.stroke = strokeColor;
125
+ path.style.strokeWidth = strokeWidth;
126
+ icon.appendChild(path);
127
+ icon.setAttribute('viewBox', '0 0 100 100');
128
+
129
+ return icon;
130
+ }
131
+
132
+ public makeHandToolIcon() {
133
+ const fill = 'none';
134
+ const strokeColor = 'var(--icon-color)';
135
+ const strokeWidth = '3';
136
+
137
+ // Draw a cursor-like shape (like some of the other icons, made with Inkscape)
138
+ return this.makeIconFromPath(`
139
+ m 10,60
140
+ 5,30
141
+ H 90
142
+ V 30
143
+ C 90,20 75,20 75,30
144
+ V 60
145
+ 20
146
+ C 75,10 60,10 60,20
147
+ V 60
148
+ 15
149
+ C 60,5 45,5 45,15
150
+ V 60
151
+ 25
152
+ C 45,15 30,15 30,25
153
+ V 60
154
+ 75
155
+ L 25,60
156
+ C 20,45 10,50 10,60
157
+ Z
158
+ `, fill, strokeColor, strokeWidth);
159
+ }
160
+
161
+ public makeTouchPanningIcon() {
162
+ const fill = 'none';
163
+ const strokeColor = 'var(--icon-color)';
164
+ const strokeWidth = '3';
165
+
166
+ return this.makeIconFromPath(`
167
+ M 5,5.5
168
+ V 17.2
169
+ L 16.25,5.46
170
+ Z
171
+
172
+ m 33.75,0
173
+ L 50,17
174
+ V 5.5
175
+ Z
176
+
177
+ M 5,40.7
178
+ v 11.7
179
+ h 11.25
180
+ z
181
+
182
+ M 26,19
183
+ C 19.8,19.4 17.65,30.4 21.9,34.8
184
+ L 50,70
185
+ H 27.5
186
+ c -11.25,0 -11.25,17.6 0,17.6
187
+ H 61.25
188
+ C 94.9,87.8 95,87.6 95,40.7 78.125,23 67,29 55.6,46.5
189
+ L 33.1,23
190
+ C 30.3125,20.128192 27.9,19 25.830078,19.119756
191
+ Z
192
+ `, fill, strokeColor, strokeWidth);
193
+ }
194
+
195
+ public makeAllDevicePanningIcon() {
196
+ const fill = 'none';
197
+ const strokeColor = 'var(--icon-color)';
198
+ const strokeWidth = '3';
199
+ return this.makeIconFromPath(`
200
+ M 5 5
201
+ L 5 17.5
202
+ 17.5 5
203
+ 5 5
204
+ z
205
+
206
+ M 42.5 5
207
+ L 55 17.5
208
+ 55 5
209
+ 42.5 5
210
+ z
211
+
212
+ M 70 10
213
+ L 70 21
214
+ 61 15
215
+ 55.5 23
216
+ 66 30
217
+ 56 37
218
+ 61 45
219
+ 70 39
220
+ 70 50
221
+ 80 50
222
+ 80 39
223
+ 89 45
224
+ 95 36
225
+ 84 30
226
+ 95 23
227
+ 89 15
228
+ 80 21
229
+ 80 10
230
+ 70 10
231
+ z
232
+
233
+ M 27.5 26.25
234
+ L 27.5 91.25
235
+ L 43.75 83.125
236
+ L 52 99
237
+ L 68 91
238
+ L 60 75
239
+ L 76.25 66.875
240
+ L 27.5 26.25
241
+ z
242
+
243
+ M 5 42.5
244
+ L 5 55
245
+ L 17.5 55
246
+ L 5 42.5
247
+ z
248
+ `, fill, strokeColor, strokeWidth);
249
+ }
250
+
251
+ public makeZoomIcon = () => {
252
+ const icon = document.createElementNS(svgNamespace, 'svg');
253
+ icon.setAttribute('viewBox', '0 0 100 100');
254
+
255
+ const addTextNode = (text: string, x: number, y: number) => {
256
+ const textNode = document.createElementNS(svgNamespace, 'text');
257
+ textNode.appendChild(document.createTextNode(text));
258
+ textNode.setAttribute('x', x.toString());
259
+ textNode.setAttribute('y', y.toString());
260
+ textNode.style.textAlign = 'center';
261
+ textNode.style.textAnchor = 'middle';
262
+ textNode.style.fontSize = '55px';
263
+ textNode.style.fill = 'var(--icon-color)';
264
+ textNode.style.fontFamily = 'monospace';
265
+
266
+ icon.appendChild(textNode);
267
+ };
268
+
269
+ addTextNode('+', 40, 45);
270
+ addTextNode('-', 70, 75);
271
+
272
+ return icon;
273
+ };
274
+
275
+ public makeTextIcon(textStyle: TextStyle) {
276
+ const icon = document.createElementNS(svgNamespace, 'svg');
277
+ icon.setAttribute('viewBox', '0 0 100 100');
278
+
279
+ const textNode = document.createElementNS(svgNamespace, 'text');
280
+ textNode.appendChild(document.createTextNode('T'));
281
+
282
+ textNode.style.fontFamily = textStyle.fontFamily;
283
+ textNode.style.fontWeight = textStyle.fontWeight ?? '';
284
+ textNode.style.fontVariant = textStyle.fontVariant ?? '';
285
+ textNode.style.fill = textStyle.renderingStyle.fill.toHexString();
286
+
287
+ textNode.style.textAnchor = 'middle';
288
+ textNode.setAttribute('x', '50');
289
+ textNode.setAttribute('y', '75');
290
+ textNode.style.fontSize = '65px';
291
+ textNode.style.filter = 'drop-shadow(0px 0px 10px var(--primary-shadow-color))';
292
+
293
+ icon.appendChild(textNode);
294
+
295
+ return icon;
296
+ }
297
+
298
+ public makePenIcon(tipThickness: number, color: string|Color4) {
299
+ if (color instanceof Color4) {
300
+ color = color.toHexString();
301
+ }
302
+
303
+ const icon = document.createElementNS(svgNamespace, 'svg');
304
+ icon.setAttribute('viewBox', '0 0 100 100');
305
+
306
+ const halfThickness = tipThickness / 2;
307
+
308
+ // Draw a pen-like shape
309
+ const primaryStrokeTipPath = `M14,63 L${50 - halfThickness},95 L${50 + halfThickness},90 L88,60 Z`;
310
+ const backgroundStrokeTipPath = `M14,63 L${50 - halfThickness},85 L${50 + halfThickness},83 L88,60 Z`;
311
+ icon.innerHTML = `
312
+ <defs>
313
+ ${checkerboardPatternDef}
314
+ </defs>
315
+ <g>
316
+ <!-- Pen grip -->
317
+ <path
318
+ d='M10,10 L90,10 L90,60 L${50 + halfThickness},80 L${50 - halfThickness},80 L10,60 Z'
319
+ ${iconColorStrokeFill}
320
+ />
321
+ </g>
322
+ <g>
323
+ <!-- Checkerboard background for slightly transparent pens -->
324
+ <path d='${backgroundStrokeTipPath}' fill='${checkerboardPatternRef}'/>
325
+
326
+ <!-- Actual pen tip -->
327
+ <path
328
+ d='${primaryStrokeTipPath}'
329
+ fill='${color}'
330
+ stroke='${color}'
331
+ />
332
+ </g>
333
+ `;
334
+ return icon;
335
+ }
336
+
337
+ public makeIconFromFactory(pen: Pen, factory: ComponentBuilderFactory) {
338
+ const toolThickness = pen.getThickness();
339
+
340
+ const nowTime = (new Date()).getTime();
341
+ const startPoint: StrokeDataPoint = {
342
+ pos: Vec2.of(10, 10),
343
+ width: toolThickness / 5,
344
+ color: pen.getColor(),
345
+ time: nowTime - 100,
346
+ };
347
+ const endPoint: StrokeDataPoint = {
348
+ pos: Vec2.of(90, 90),
349
+ width: toolThickness / 5,
350
+ color: pen.getColor(),
351
+ time: nowTime,
352
+ };
353
+
354
+ const viewport = new Viewport(new EventDispatcher());
355
+ const builder = factory(startPoint, viewport);
356
+ builder.addPoint(endPoint);
357
+
358
+ const icon = document.createElementNS(svgNamespace, 'svg');
359
+ icon.setAttribute('viewBox', '0 0 100 100');
360
+ viewport.updateScreenSize(Vec2.of(100, 100));
361
+
362
+ const renderer = new SVGRenderer(icon, viewport);
363
+ builder.preview(renderer);
364
+
365
+ return icon;
366
+ }
367
+
368
+ public makePipetteIcon(color?: Color4) {
369
+ const icon = document.createElementNS(svgNamespace, 'svg');
370
+ const pipette = document.createElementNS(svgNamespace, 'path');
371
+
372
+ pipette.setAttribute('d', `
373
+ M 47,6
374
+ C 35,5 25,15 35,30
375
+ c -9.2,1.3 -15,0 -15,3
376
+ 0,2 5,5 15,7
377
+ V 81
378
+ L 40,90
379
+ h 6
380
+ L 40,80
381
+ V 40
382
+ h 15
383
+ v 40
384
+ l -6,10
385
+ h 6
386
+ l 5,-9.2
387
+ V 40
388
+ C 70,38 75,35 75,33
389
+ 75,30 69.2,31.2 60,30
390
+ 65,15 65,5 47,6
391
+ Z
392
+ `);
393
+ pipette.style.fill = 'var(--icon-color)';
394
+
395
+ if (color) {
396
+ const defs = document.createElementNS(svgNamespace, 'defs');
397
+ defs.innerHTML = checkerboardPatternDef;
398
+ icon.appendChild(defs);
399
+
400
+ const fluidBackground = document.createElementNS(svgNamespace, 'path');
401
+ const fluid = document.createElementNS(svgNamespace, 'path');
402
+
403
+ const fluidPathData = `
404
+ m 40,50 c 5,5 10,0 15,-5 V 80 L 50,90 H 45 L 40,80 Z
405
+ `;
406
+
407
+ fluid.setAttribute('d', fluidPathData);
408
+ fluidBackground.setAttribute('d', fluidPathData);
409
+
410
+ fluid.style.fill = color.toHexString();
411
+ fluidBackground.style.fill = checkerboardPatternRef;
412
+
413
+ icon.appendChild(fluidBackground);
414
+ icon.appendChild(fluid);
415
+ }
416
+ icon.appendChild(pipette);
417
+
418
+ icon.setAttribute('viewBox', '0 0 100 100');
419
+ return icon;
420
+ }
421
+
422
+ public makeResizeViewportIcon() {
423
+ return this.makeIconFromPath(`
424
+ M 75 5 75 10 90 10 90 25 95 25 95 5 75 5 z
425
+ M 15 15 15 30 20 30 20 20 30 20 30 15 15 15 z
426
+ M 84 15 82 17 81 16 81 20 85 20 84 19 86 17 84 15 z
427
+ M 26 24 24 26 26 28 25 29 29 29 29 25 28 26 26 24 z
428
+ M 25 71 26 72 24 74 26 76 28 74 29 75 29 71 25 71 z
429
+ M 15 75 15 85 25 85 25 80 20 80 20 75 15 75 z
430
+ M 90 75 90 90 75 90 75 95 95 95 95 75 90 75 z
431
+ M 81 81 81 85 82 84 84 86 86 84 84 82 85 81 81 81 z
432
+ `);
433
+ }
434
+
435
+ public makeDuplicateSelectionIcon() {
436
+ return this.makeIconFromPath(`
437
+ M 45,10 45,55 90,55 90,10 45,10 z
438
+ M 10,25 10,90 70,90 70,60 40,60 40,25 10,25 z
439
+ `);
440
+ }
441
+
442
+ public makeDeleteSelectionIcon() {
443
+ const strokeWidth = '5px';
444
+ const strokeColor = 'var(--icon-color)';
445
+ const fillColor = 'none';
446
+
447
+ return this.makeIconFromPath(`
448
+ M 10,10 90,90
449
+ M 10,90 90,10
450
+ `, fillColor, strokeColor, strokeWidth);
451
+ }
452
+
453
+ public makeSaveIcon() {
454
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
455
+ svg.innerHTML = `
456
+ <style>
457
+ .toolbar-save-icon {
458
+ stroke: var(--icon-color);
459
+ stroke-width: 10;
460
+ stroke-linejoin: round;
461
+ stroke-linecap: round;
462
+ fill: none;
463
+ }
464
+ </style>
465
+ <path
466
+ d='
467
+ M 15,55 30,70 85,20
468
+ '
469
+ class='toolbar-save-icon'
470
+ />
471
+ `;
472
+ svg.setAttribute('viewBox', '0 0 100 100');
473
+ return svg;
474
+ }
475
+
476
+ }
@@ -1,4 +1,4 @@
1
1
 
2
2
  export * from './widgets/lib';
3
- export * as icons from './icons';
4
3
  export * from './makeColorInput';
4
+ export { default as IconProvider } from './IconProvider';
@@ -2,7 +2,6 @@
2
2
 
3
3
  export interface ToolbarLocalization {
4
4
  fontLabel: string;
5
- anyDevicePanning: string;
6
5
  touchPanning: string;
7
6
  outlinedRectanglePen: string;
8
7
  filledRectanglePen: string;
@@ -54,7 +53,6 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
54
53
  selectionToolKeyboardShortcuts: 'Selection tool: Use arrow keys to move selected items, lowercase/uppercase ‘i’ and ‘o’ to resize.',
55
54
 
56
55
  touchPanning: 'Touchscreen panning',
57
- anyDevicePanning: 'Any device panning',
58
56
 
59
57
  freehandPen: 'Freehand',
60
58
  arrowPen: 'Arrow',
@@ -2,7 +2,6 @@ import Color4 from '../Color4';
2
2
  import Editor from '../Editor';
3
3
  import PipetteTool from '../tools/PipetteTool';
4
4
  import { EditorEventType } from '../types';
5
- import { makePipetteIcon } from './icons';
6
5
 
7
6
  type OnColorChangeListener = (color: Color4)=>void;
8
7
 
@@ -72,7 +71,7 @@ const addPipetteTool = (editor: Editor, container: HTMLElement, onColorChange: O
72
71
  pipetteButton.setAttribute('alt', pipetteButton.title);
73
72
 
74
73
  const updatePipetteIcon = (color?: Color4) => {
75
- pipetteButton.replaceChildren(makePipetteIcon(color));
74
+ pipetteButton.replaceChildren(editor.icons.makePipetteIcon(color));
76
75
  };
77
76
  updatePipetteIcon();
78
77
 
@@ -2,7 +2,6 @@ import Editor from '../../Editor';
2
2
  import ToolbarShortcutHandler from '../../tools/ToolbarShortcutHandler';
3
3
  import { EditorEventType, InputEvtType, KeyPressEvent } from '../../types';
4
4
  import { toolbarCSSPrefix } from '../HTMLToolbar';
5
- import { makeDropdownIcon } from '../icons';
6
5
  import { ToolbarLocalization } from '../localization';
7
6
 
8
7
  export default abstract class BaseWidget {
@@ -248,7 +247,7 @@ export default abstract class BaseWidget {
248
247
  }
249
248
 
250
249
  private createDropdownIcon(): Element {
251
- const icon = makeDropdownIcon();
250
+ const icon = this.editor.icons.makeDropdownIcon();
252
251
  icon.classList.add(`${toolbarCSSPrefix}showHideDropdownIcon`);
253
252
  return icon;
254
253
  }
@@ -1,4 +1,3 @@
1
- import { makeEraserIcon } from '../icons';
2
1
  import BaseToolWidget from './BaseToolWidget';
3
2
 
4
3
  export default class EraserToolWidget extends BaseToolWidget {
@@ -6,7 +5,7 @@ export default class EraserToolWidget extends BaseToolWidget {
6
5
  return this.localizationTable.eraser;
7
6
  }
8
7
  protected createIcon(): Element {
9
- return makeEraserIcon();
8
+ return this.editor.icons.makeEraserIcon();
10
9
  }
11
10
 
12
11
  protected fillDropdown(_dropdown: HTMLElement): boolean {
@@ -1,10 +1,10 @@
1
1
  import Editor from '../../Editor';
2
2
  import Mat33 from '../../math/Mat33';
3
3
  import PanZoom, { PanZoomMode } from '../../tools/PanZoom';
4
+ import ToolController from '../../tools/ToolController';
4
5
  import { EditorEventType } from '../../types';
5
6
  import Viewport from '../../Viewport';
6
7
  import { toolbarCSSPrefix } from '../HTMLToolbar';
7
- import { makeAllDevicePanningIcon, makeHandToolIcon, makeTouchPanningIcon, makeZoomIcon } from '../icons';
8
8
  import { ToolbarLocalization } from '../localization';
9
9
  import BaseToolWidget from './BaseToolWidget';
10
10
  import BaseWidget from './BaseWidget';
@@ -86,7 +86,7 @@ class ZoomWidget extends BaseWidget {
86
86
  }
87
87
 
88
88
  protected createIcon(): Element {
89
- return makeZoomIcon();
89
+ return this.editor.icons.makeZoomIcon();
90
90
  }
91
91
 
92
92
  protected handleClick(): void {
@@ -149,49 +149,71 @@ class HandModeWidget extends BaseWidget {
149
149
 
150
150
  export default class HandToolWidget extends BaseToolWidget {
151
151
  private touchPanningWidget: HandModeWidget;
152
+ private allowTogglingBaseTool: boolean;
153
+
152
154
  public constructor(
153
- editor: Editor, protected tool: PanZoom, localizationTable: ToolbarLocalization
155
+ editor: Editor,
156
+
157
+ // Pan zoom tool that overrides all other tools (enabling this tool for a device
158
+ // causes that device to pan/zoom instead of interact with the primary tools)
159
+ protected overridePanZoomTool: PanZoom,
160
+
161
+ localizationTable: ToolbarLocalization,
154
162
  ) {
163
+ const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController);
164
+ const tool = primaryHandTool ?? overridePanZoomTool;
155
165
  super(editor, tool, localizationTable);
156
- this.container.classList.add('dropdownShowable');
157
166
 
167
+ // Only allow toggling a hand tool if we're using the primary hand tool and not the override
168
+ // hand tool for this button.
169
+ this.allowTogglingBaseTool = primaryHandTool !== null;
170
+
171
+ // Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
172
+ if (!this.allowTogglingBaseTool) {
173
+ this.container.classList.add('dropdownShowable');
174
+ }
175
+
176
+ // Controls for the overriding hand tool.
158
177
  this.touchPanningWidget = new HandModeWidget(
159
178
  editor, localizationTable,
160
179
 
161
- tool, PanZoomMode.OneFingerTouchGestures,
162
- makeTouchPanningIcon,
180
+ overridePanZoomTool, PanZoomMode.OneFingerTouchGestures,
181
+ () => this.editor.icons.makeTouchPanningIcon(),
163
182
 
164
183
  localizationTable.touchPanning
165
184
  );
166
185
 
167
186
  this.addSubWidget(this.touchPanningWidget);
168
- this.addSubWidget(
169
- new HandModeWidget(
170
- editor, localizationTable,
171
-
172
- tool, PanZoomMode.SinglePointerGestures,
173
- makeAllDevicePanningIcon,
174
-
175
- localizationTable.anyDevicePanning
176
- )
177
- );
178
187
  this.addSubWidget(
179
188
  new ZoomWidget(editor, localizationTable)
180
189
  );
181
190
  }
182
191
 
192
+ private static getPrimaryHandTool(toolController: ToolController): PanZoom|null {
193
+ const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom);
194
+ const primaryPanZoomTool = primaryPanZoomToolList[0];
195
+ return primaryPanZoomTool as PanZoom|null;
196
+ }
197
+
183
198
  protected getTitle(): string {
184
199
  return this.localizationTable.handTool;
185
200
  }
186
201
 
187
202
  protected createIcon(): Element {
188
- return makeHandToolIcon();
203
+ return this.editor.icons.makeHandToolIcon();
189
204
  }
190
205
 
191
- public setSelected(_selected: boolean): void {
206
+ protected handleClick(): void {
207
+ if (this.allowTogglingBaseTool) {
208
+ super.handleClick();
209
+ } else {
210
+ this.setDropdownVisible(!this.isDropdownVisible());
211
+ }
192
212
  }
193
213
 
194
- protected handleClick() {
195
- this.setDropdownVisible(!this.isDropdownVisible());
214
+ public setSelected(selected: boolean): void {
215
+ if (this.allowTogglingBaseTool) {
216
+ super.setSelected(selected);
217
+ }
196
218
  }
197
219
  }
@@ -7,7 +7,6 @@ import Editor from '../../Editor';
7
7
  import Pen from '../../tools/Pen';
8
8
  import { EditorEventType, KeyPressEvent } from '../../types';
9
9
  import { toolbarCSSPrefix } from '../HTMLToolbar';
10
- import { makeIconFromFactory, makePenIcon } from '../icons';
11
10
  import { ToolbarLocalization } from '../localization';
12
11
  import makeColorInput from '../makeColorInput';
13
12
  import BaseToolWidget from './BaseToolWidget';
@@ -77,10 +76,10 @@ export default class PenToolWidget extends BaseToolWidget {
77
76
  // Use a square-root scale to prevent the pen's tip from overflowing.
78
77
  const scale = Math.round(Math.sqrt(this.tool.getThickness()) * 4);
79
78
  const color = this.tool.getColor();
80
- return makePenIcon(scale, color.toHexString());
79
+ return this.editor.icons.makePenIcon(scale, color.toHexString());
81
80
  } else {
82
81
  const strokeFactory = this.tool.getStrokeFactory();
83
- return makeIconFromFactory(this.tool, strokeFactory);
82
+ return this.editor.icons.makeIconFromFactory(this.tool, strokeFactory);
84
83
  }
85
84
  }
86
85