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