js-draw 0.17.2 → 0.17.4

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 (51) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +17 -8
  3. package/dist/bundle.js +1 -1
  4. package/dist/src/Editor.d.ts +62 -1
  5. package/dist/src/Editor.js +75 -24
  6. package/dist/src/EditorImage.d.ts +4 -2
  7. package/dist/src/EditorImage.js +4 -2
  8. package/dist/src/SVGLoader.d.ts +4 -0
  9. package/dist/src/SVGLoader.js +4 -0
  10. package/dist/src/components/lib.d.ts +2 -2
  11. package/dist/src/components/lib.js +2 -2
  12. package/dist/src/lib.d.ts +2 -1
  13. package/dist/src/lib.js +2 -1
  14. package/dist/src/rendering/lib.d.ts +2 -0
  15. package/dist/src/rendering/lib.js +2 -0
  16. package/dist/src/rendering/renderers/CanvasRenderer.d.ts +25 -0
  17. package/dist/src/rendering/renderers/CanvasRenderer.js +27 -0
  18. package/dist/src/rendering/renderers/SVGRenderer.d.ts +15 -0
  19. package/dist/src/rendering/renderers/SVGRenderer.js +27 -1
  20. package/dist/src/testing/lib.d.ts +2 -0
  21. package/dist/src/testing/lib.js +2 -0
  22. package/dist/src/testing/sendPenEvent.d.ts +12 -0
  23. package/dist/src/testing/sendPenEvent.js +19 -0
  24. package/dist/src/testing/sendTouchEvent.d.ts +36 -0
  25. package/dist/src/testing/sendTouchEvent.js +36 -0
  26. package/dist/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +0 -1
  27. package/dist/src/toolbar/widgets/DocumentPropertiesWidget.js +5 -20
  28. package/dist/src/toolbar/widgets/PenToolWidget.js +1 -0
  29. package/dist/src/toolbar/widgets/TextToolWidget.js +4 -1
  30. package/dist/src/tools/SelectionTool/SelectionTool.js +5 -6
  31. package/package.json +1 -1
  32. package/src/Editor.ts +78 -28
  33. package/src/EditorImage.ts +4 -2
  34. package/src/SVGLoader.ts +4 -0
  35. package/src/components/lib.ts +2 -1
  36. package/src/lib.ts +2 -1
  37. package/src/rendering/lib.ts +2 -0
  38. package/src/rendering/renderers/CanvasRenderer.ts +27 -0
  39. package/src/rendering/renderers/SVGRenderer.ts +32 -1
  40. package/src/testing/lib.ts +3 -0
  41. package/src/testing/sendPenEvent.ts +31 -0
  42. package/src/testing/sendTouchEvent.ts +36 -1
  43. package/src/toolbar/toolbar.css +5 -0
  44. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +5 -23
  45. package/src/toolbar/widgets/PenToolWidget.ts +1 -0
  46. package/src/toolbar/widgets/TextToolWidget.ts +4 -1
  47. package/src/tools/Eraser.test.ts +11 -10
  48. package/src/tools/PanZoom.test.ts +1 -1
  49. package/src/tools/Pen.test.ts +63 -62
  50. package/src/tools/SelectionTool/SelectionTool.test.ts +15 -14
  51. package/src/tools/SelectionTool/SelectionTool.ts +5 -7
@@ -3,7 +3,42 @@ import { Vec2 } from '../math/Vec2';
3
3
  import Pointer, { PointerDevice } from '../Pointer';
4
4
  import { InputEvtType } from '../types';
5
5
 
6
-
6
+ /**
7
+ * Dispatch a touch event to the currently selected tool. Intended for unit tests.
8
+ *
9
+ * @see {@link sendPenEvent}
10
+ *
11
+ * @example
12
+ * **Simulating a horizontal swipe gesture:**
13
+ * ```ts
14
+ * sendTouchEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
15
+ * for (let i = 1; i <= 10; i++) {
16
+ * jest.advanceTimersByTime(10);
17
+ * sendTouchEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(i * 10, 0));
18
+ * }
19
+ * ```
20
+ *
21
+ * @example
22
+ * **Simulating a pinch gesture.** This example assumes that you're using [Jest with timer mocks enabled](https://jestjs.io/docs/timer-mocks).
23
+ * ```ts
24
+ * let firstPointer = sendTouchEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
25
+ * let secondPointer = sendTouchEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(100, 0), [ firstPointer ]);
26
+ *
27
+ * // Simulate a pinch
28
+ * const maxIterations = 10;
29
+ * for (let i = 0; i < maxIterations; i++) {
30
+ * // Use the unit testing framework's tool for increasing the current time
31
+ * // returned by (new Date()).getTime(), etc.
32
+ * jest.advanceTimersByTime(100);
33
+ *
34
+ * const point1 = Vec2.of(-i * 5, 0);
35
+ * const point2 = Vec2.of(i * 5 + 100, 0);
36
+ *
37
+ * firstPointer = sendTouchEvent(editor, InputEvtType.PointerMoveEvt, point1, [ secondPointer ]);
38
+ * secondPointer = sendTouchEvent(editor, InputEvtType.PointerMoveEvt, point2, [ firstPointer ]);
39
+ * }
40
+ * ```
41
+ */
7
42
  const sendTouchEvent = (
8
43
  editor: Editor,
9
44
  eventType: InputEvtType.PointerDownEvt|InputEvtType.PointerMoveEvt|InputEvtType.PointerUpEvt,
@@ -206,3 +206,8 @@
206
206
  background-color: var(--secondary-background-color);
207
207
  --icon-color: var(--secondary-foreground-color);
208
208
  }
209
+
210
+ .toolbar-spacedList > * {
211
+ padding-bottom: 5px;
212
+ padding-top: 5px;
213
+ }
@@ -1,9 +1,9 @@
1
1
  import Color4 from '../../Color4';
2
- import ImageBackground from '../../components/ImageBackground';
3
2
  import Editor from '../../Editor';
4
3
  import { EditorImageEventType } from '../../EditorImage';
5
4
  import Rect2 from '../../math/Rect2';
6
5
  import { EditorEventType } from '../../types';
6
+ import { toolbarCSSPrefix } from '../HTMLToolbar';
7
7
  import { ToolbarLocalization } from '../localization';
8
8
  import makeColorInput from '../makeColorInput';
9
9
  import BaseWidget from './BaseWidget';
@@ -56,31 +56,12 @@ export default class DocumentPropertiesWidget extends BaseWidget {
56
56
  }
57
57
  }
58
58
 
59
- private getBackgroundElem() {
60
- const backgroundComponents = [];
61
-
62
- for (const component of this.editor.image.getBackgroundComponents()) {
63
- if (component instanceof ImageBackground) {
64
- backgroundComponents.push(component);
65
- }
66
- }
67
-
68
- if (backgroundComponents.length === 0) {
69
- return null;
70
- }
71
-
72
- // Return the last background component in the list — the component with highest z-index.
73
- return backgroundComponents[backgroundComponents.length - 1];
74
- }
75
-
76
59
  private setBackgroundColor(color: Color4) {
77
60
  this.editor.dispatch(this.editor.setBackgroundColor(color));
78
61
  }
79
62
 
80
63
  private getBackgroundColor() {
81
- const background = this.getBackgroundElem();
82
-
83
- return background?.getStyle()?.color ?? Color4.transparent;
64
+ return this.editor.estimateBackgroundColor();
84
65
  }
85
66
 
86
67
  private updateImportExportRectSize(size: { width?: number, height?: number }) {
@@ -109,6 +90,7 @@ export default class DocumentPropertiesWidget extends BaseWidget {
109
90
 
110
91
  protected fillDropdown(dropdown: HTMLElement): boolean {
111
92
  const container = document.createElement('div');
93
+ container.classList.add(`${toolbarCSSPrefix}spacedList`);
112
94
 
113
95
  const backgroundColorRow = document.createElement('div');
114
96
  const backgroundColorLabel = document.createElement('label');
@@ -121,7 +103,7 @@ export default class DocumentPropertiesWidget extends BaseWidget {
121
103
  }
122
104
  });
123
105
 
124
- colorInput.id = `document-properties-color-input-${DocumentPropertiesWidget.idCounter++}`;
106
+ colorInput.id = `${toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
125
107
  backgroundColorLabel.htmlFor = colorInput.id;
126
108
 
127
109
  backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
@@ -135,7 +117,7 @@ export default class DocumentPropertiesWidget extends BaseWidget {
135
117
  label.innerText = labelContent;
136
118
  input.type = 'number';
137
119
  input.min = '0';
138
- input.id = `document-properties-dimension-row-${DocumentPropertiesWidget.idCounter++}`;
120
+ input.id = `${toolbarCSSPrefix}docPropertiesDimensionRow-${DocumentPropertiesWidget.idCounter++}`;
139
121
  label.htmlFor = input.id;
140
122
 
141
123
  spacer.style.flexGrow = '1';
@@ -134,6 +134,7 @@ export default class PenToolWidget extends BaseToolWidget {
134
134
  private static idCounter: number = 0;
135
135
  protected fillDropdown(dropdown: HTMLElement): boolean {
136
136
  const container = document.createElement('div');
137
+ container.classList.add(`${toolbarCSSPrefix}spacedList`);
137
138
 
138
139
  const thicknessRow = document.createElement('div');
139
140
  const objectTypeRow = document.createElement('div');
@@ -32,6 +32,8 @@ export default class TextToolWidget extends BaseToolWidget {
32
32
 
33
33
  private static idCounter: number = 0;
34
34
  protected fillDropdown(dropdown: HTMLElement): boolean {
35
+ const container = document.createElement('div');
36
+ container.classList.add(`${toolbarCSSPrefix}spacedList`);
35
37
  const fontRow = document.createElement('div');
36
38
  const colorRow = document.createElement('div');
37
39
  const sizeRow = document.createElement('div');
@@ -108,7 +110,8 @@ export default class TextToolWidget extends BaseToolWidget {
108
110
  };
109
111
  this.updateDropdownInputs();
110
112
 
111
- dropdown.replaceChildren(colorRow, sizeRow, fontRow);
113
+ container.replaceChildren(colorRow, sizeRow, fontRow);
114
+ dropdown.appendChild(container);
112
115
  return true;
113
116
  }
114
117
 
@@ -3,6 +3,7 @@ import Editor from '../Editor';
3
3
  import { EditorImage, Rect2, StrokeComponent } from '../lib';
4
4
  import { Vec2 } from '../math/Vec2';
5
5
  import createEditor from '../testing/createEditor';
6
+ import sendPenEvent from '../testing/sendPenEvent';
6
7
  import { InputEvtType } from '../types';
7
8
  import Eraser from './Eraser';
8
9
 
@@ -23,9 +24,9 @@ describe('Eraser', () => {
23
24
  const editor = createEditor();
24
25
 
25
26
  // Draw a line
26
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0, 0));
27
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
27
28
  jest.advanceTimersByTime(100);
28
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(200, 200));
29
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(200, 200));
29
30
 
30
31
  // Should have drawn a line
31
32
  const strokes = getAllStrokes(editor);
@@ -35,9 +36,9 @@ describe('Eraser', () => {
35
36
  selectEraser(editor);
36
37
 
37
38
  // Erase the line.
38
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(200, 0));
39
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(200, 0));
39
40
  jest.advanceTimersByTime(400);
40
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(0, 200));
41
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0, 200));
41
42
 
42
43
  // Should have erased the line
43
44
  expect(getAllStrokes(editor)).toHaveLength(0);
@@ -64,16 +65,16 @@ describe('Eraser', () => {
64
65
  eraser.setThickness(10);
65
66
 
66
67
  // Erase the first stroke
67
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(3, 0));
68
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(3, 0));
68
69
  jest.advanceTimersByTime(100);
69
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(3, 0));
70
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(3, 0));
70
71
 
71
72
  expect(getAllStrokes(editor)).toHaveLength(1);
72
73
 
73
74
  // Erase the remaining stroke
74
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(47, 47));
75
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(47, 47));
75
76
  jest.advanceTimersByTime(100);
76
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(47, 47));
77
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(47, 47));
77
78
 
78
79
  expect(getAllStrokes(editor)).toHaveLength(0);
79
80
  });
@@ -92,9 +93,9 @@ describe('Eraser', () => {
92
93
  eraser.setThickness(100);
93
94
 
94
95
  // Try to erase it.
95
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0, 0));
96
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
96
97
  jest.advanceTimersByTime(100);
97
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(3, 0));
98
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(3, 0));
98
99
 
99
100
  // Should not have been erased
100
101
  expect(editor.image.getAllElements()).toHaveLength(1);
@@ -68,7 +68,7 @@ describe('PanZoom', () => {
68
68
  firstPointer = sendTouchEvent(editor, eventType, point1, [ secondPointer ]);
69
69
  secondPointer = sendTouchEvent(editor, eventType, point2, [ firstPointer ]);
70
70
  expectedScale = point1.minus(point2).magnitude() / 100;
71
- Vec2.zero;
71
+
72
72
  if (i === maxIterations - 1) {
73
73
  jest.advanceTimersByTime(10);
74
74
 
@@ -7,16 +7,17 @@ import Rect2 from '../math/Rect2';
7
7
  import StrokeComponent from '../components/Stroke';
8
8
  import Mat33 from '../math/Mat33';
9
9
  import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
10
+ import sendPenEvent from '../testing/sendPenEvent';
10
11
 
11
12
  describe('Pen', () => {
12
13
  it('should draw horizontal lines', () => {
13
14
  const editor = createEditor();
14
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0, 0));
15
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
15
16
  for (let i = 0; i < 10; i++) {
16
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(i, 0));
17
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(i, 0));
17
18
  jest.advanceTimersByTime(200);
18
19
  }
19
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(200, 0));
20
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(200, 0));
20
21
 
21
22
  const elems = editor.image.getElementsIntersectingRegion(new Rect2(0, 10, 10, -10));
22
23
  expect(elems).toHaveLength(1);
@@ -29,12 +30,12 @@ describe('Pen', () => {
29
30
 
30
31
  it('should draw vertical line', () => {
31
32
  const editor = createEditor();
32
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0, 0));
33
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
33
34
  for (let i = 0; i < 10; i++) {
34
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(0, i * 20));
35
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0, i * 20));
35
36
  jest.advanceTimersByTime(200);
36
37
  }
37
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(0, 150));
38
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0, 150));
38
39
 
39
40
  const elems = editor.image.getElementsIntersectingRegion(Rect2.unitSquare);
40
41
  expect(elems).toHaveLength(1);
@@ -46,104 +47,104 @@ describe('Pen', () => {
46
47
  it('should draw vertical line with slight bend', () => {
47
48
  const editor = createEditor();
48
49
 
49
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(417, 24));
50
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(417, 24));
50
51
  jest.advanceTimersByTime(245);
51
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 197));
52
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 197));
52
53
  jest.advanceTimersByTime(20);
53
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 199));
54
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 199));
54
55
  jest.advanceTimersByTime(12);
55
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 201));
56
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 201));
56
57
  jest.advanceTimersByTime(40);
57
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 203));
58
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 203));
58
59
  jest.advanceTimersByTime(14);
59
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 206));
60
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 206));
60
61
  jest.advanceTimersByTime(35);
61
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 208));
62
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 208));
62
63
  jest.advanceTimersByTime(16);
63
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 211));
64
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 211));
64
65
  jest.advanceTimersByTime(51);
65
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 215));
66
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 215));
66
67
  jest.advanceTimersByTime(32);
67
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 218));
68
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 218));
68
69
  jest.advanceTimersByTime(30);
69
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 220));
70
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 220));
70
71
  jest.advanceTimersByTime(24);
71
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 222));
72
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 222));
72
73
  jest.advanceTimersByTime(14);
73
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 224));
74
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 224));
74
75
  jest.advanceTimersByTime(32);
75
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 227));
76
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 227));
76
77
  jest.advanceTimersByTime(17);
77
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 229));
78
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 229));
78
79
  jest.advanceTimersByTime(53);
79
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 234));
80
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 234));
80
81
  jest.advanceTimersByTime(34);
81
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 236));
82
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 236));
82
83
  jest.advanceTimersByTime(17);
83
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 238));
84
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 238));
84
85
  jest.advanceTimersByTime(39);
85
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 240));
86
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 240));
86
87
  jest.advanceTimersByTime(10);
87
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 243));
88
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 243));
88
89
  jest.advanceTimersByTime(34);
89
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 250));
90
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 250));
90
91
  jest.advanceTimersByTime(57);
91
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(423, 252));
92
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 252));
92
93
  jest.advanceTimersByTime(8);
93
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(422, 256));
94
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(422, 256));
94
95
  jest.advanceTimersByTime(28);
95
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(422, 258));
96
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(422, 258));
96
97
  jest.advanceTimersByTime(21);
97
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(421, 262));
98
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(421, 262));
98
99
  jest.advanceTimersByTime(34);
99
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 264));
100
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 264));
100
101
  jest.advanceTimersByTime(5);
101
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 266));
102
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 266));
102
103
  jest.advanceTimersByTime(22);
103
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 268));
104
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 268));
104
105
  jest.advanceTimersByTime(22);
105
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 271));
106
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 271));
106
107
  jest.advanceTimersByTime(18);
107
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 274));
108
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 274));
108
109
  jest.advanceTimersByTime(33);
109
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 277));
110
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 277));
110
111
  jest.advanceTimersByTime(16);
111
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 279));
112
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 279));
112
113
  jest.advanceTimersByTime(36);
113
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 282));
114
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 282));
114
115
  jest.advanceTimersByTime(15);
115
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 284));
116
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 284));
116
117
  jest.advanceTimersByTime(48);
117
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 289));
118
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 289));
118
119
  jest.advanceTimersByTime(16);
119
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 291));
120
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 291));
120
121
  jest.advanceTimersByTime(31);
121
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 295));
122
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 295));
122
123
  jest.advanceTimersByTime(23);
123
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 301));
124
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 301));
124
125
  jest.advanceTimersByTime(31);
125
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 306));
126
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 306));
126
127
  jest.advanceTimersByTime(18);
127
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 308));
128
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 308));
128
129
  jest.advanceTimersByTime(20);
129
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 310));
130
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 310));
130
131
  jest.advanceTimersByTime(13);
131
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 313));
132
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 313));
132
133
  jest.advanceTimersByTime(17);
133
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 317));
134
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 317));
134
135
  jest.advanceTimersByTime(33);
135
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 321));
136
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 321));
136
137
  jest.advanceTimersByTime(15);
137
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 324));
138
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 324));
138
139
  jest.advanceTimersByTime(23);
139
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 326));
140
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 326));
140
141
  jest.advanceTimersByTime(14);
141
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(419, 329));
142
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 329));
142
143
  jest.advanceTimersByTime(36);
143
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 333));
144
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 333));
144
145
  jest.advanceTimersByTime(8);
145
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(420, 340));
146
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(420, 340));
146
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 340));
147
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(420, 340));
147
148
 
148
149
  const elems = editor.image.getElementsIntersectingRegion(new Rect2(0, 0, 1000, 1000));
149
150
  expect(elems).toHaveLength(1);
@@ -157,9 +158,9 @@ describe('Pen', () => {
157
158
 
158
159
  expect(editor.history.undoStackSize).toBe(0);
159
160
 
160
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(10, 10));
161
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(10, 10));
161
162
  jest.advanceTimersByTime(100);
162
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(20, 10));
163
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(20, 10));
163
164
 
164
165
  const ctrlKeyDown = true;
165
166
  editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'z', ctrlKeyDown);
@@ -168,7 +169,7 @@ describe('Pen', () => {
168
169
  expect(editor.history.redoStackSize).toBe(1);
169
170
 
170
171
  // Lifting the pointer up shouldn't clear the redo stack.
171
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(420, 340));
172
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(420, 340));
172
173
  expect(editor.history.redoStackSize).toBe(1);
173
174
  });
174
175
 
@@ -179,10 +180,10 @@ describe('Pen', () => {
179
180
  const penTool = editor.toolController.getMatchingTools(PenTool)[0];
180
181
  penTool.setStrokeFactory(makeFreehandLineBuilder);
181
182
 
182
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0.1, 0.1));
183
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0.1, 0.1));
183
184
  jest.advanceTimersByTime(100);
184
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(10.1, 10.1));
185
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(10.1, 10.1));
185
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(10.1, 10.1));
186
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(10.1, 10.1));
186
187
 
187
188
  const allElems = editor.image.getAllElements();
188
189
  expect(allElems).toHaveLength(1);
@@ -10,6 +10,7 @@ import SelectionTool from './SelectionTool';
10
10
  import createEditor from '../../testing/createEditor';
11
11
  import Pointer from '../../Pointer';
12
12
  import { Rect2 } from '../../lib';
13
+ import sendPenEvent from '../../testing/sendPenEvent';
13
14
 
14
15
  const getSelectionTool = (editor: Editor): SelectionTool => {
15
16
  return editor.toolController.getMatchingTools(SelectionTool)[0];
@@ -33,9 +34,9 @@ const createEditorWithSingleObjectSelection = (objectSize: number = 50) => {
33
34
  // Select the object
34
35
  const selectionTool = getSelectionTool(editor);
35
36
  selectionTool.setEnabled(true);
36
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0, 0));
37
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(10, 10));
38
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(5, 5));
37
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
38
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(10, 10));
39
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(5, 5));
39
40
 
40
41
  return { editor, testStroke, selectionTool };
41
42
  };
@@ -61,9 +62,9 @@ describe('SelectionTool', () => {
61
62
 
62
63
  const selectionTool = getSelectionTool(editor);
63
64
  selectionTool.setEnabled(true);
64
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(0, 0));
65
- editor.sendPenEvent(InputEvtType.PointerMoveEvt, Vec2.of(0.1, 0.1));
66
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(0.1, 0.1));
65
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
66
+ sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0.1, 0.1));
67
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0.1, 0.1));
67
68
 
68
69
  // Should surround the selected object (which has bbox = (0, 0, 1, 1))
69
70
  // with extra space.
@@ -125,8 +126,8 @@ describe('SelectionTool', () => {
125
126
  selectionTool.setEnabled(true);
126
127
 
127
128
  // Select the smaller rectangle
128
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(40, 40));
129
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(100, 100));
129
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(40, 40));
130
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(100, 100));
130
131
 
131
132
  expect(selectionTool.getSelectedObjects()).toHaveLength(1);
132
133
 
@@ -134,21 +135,21 @@ describe('SelectionTool', () => {
134
135
  editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'Shift');
135
136
 
136
137
  // Select the larger stroke.
137
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(200, 200));
138
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(600, 600));
138
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(200, 200));
139
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(600, 600));
139
140
 
140
141
  expect(selectionTool.getSelectedObjects()).toHaveLength(2);
141
142
 
142
143
  editor.sendKeyboardEvent(InputEvtType.KeyUpEvent, 'Shift');
143
144
 
144
145
  // Select the larger stroke without shift pressed
145
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(200, 200));
146
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(600, 600));
146
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(200, 200));
147
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(600, 600));
147
148
  expect(selectionTool.getSelectedObjects()).toHaveLength(1);
148
149
 
149
150
  // Select nothing
150
- editor.sendPenEvent(InputEvtType.PointerDownEvt, Vec2.of(200, 200));
151
- editor.sendPenEvent(InputEvtType.PointerUpEvt, Vec2.of(201, 201));
151
+ sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(200, 200));
152
+ sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(201, 201));
152
153
  expect(selectionTool.getSelectedObjects()).toHaveLength(0);
153
154
  });
154
155
 
@@ -379,24 +379,22 @@ export default class SelectionTool extends BaseTool {
379
379
 
380
380
  const exportViewport = new Viewport(() => {});
381
381
  exportViewport.updateScreenSize(Vec2.of(bbox.w, bbox.h));
382
- exportViewport.resetTransform(Mat33.translation(bbox.topLeft));
383
-
384
- const svgNameSpace = 'http://www.w3.org/2000/svg';
385
- const exportElem = document.createElementNS(svgNameSpace, 'svg');
382
+ exportViewport.resetTransform(Mat33.translation(bbox.topLeft.times(-1)));
386
383
 
387
384
  const sanitize = true;
388
- const renderer = new SVGRenderer(exportElem, exportViewport, sanitize);
385
+ const { element: svgExportElem, renderer: svgRenderer } = SVGRenderer.fromViewport(exportViewport, sanitize);
389
386
 
390
387
  const text: string[] = [];
391
388
  for (const elem of selectedElems) {
392
- elem.render(renderer);
389
+ elem.render(svgRenderer);
393
390
 
394
391
  if (elem instanceof TextComponent) {
395
392
  text.push(elem.getText());
396
393
  }
397
394
  }
398
395
 
399
- event.setData('image/svg+xml', exportElem.outerHTML);
396
+ event.setData('image/svg+xml', svgExportElem.outerHTML);
397
+ event.setData('text/html', svgExportElem.outerHTML);
400
398
  if (text.length > 0) {
401
399
  event.setData('text/plain', text.join('\n'));
402
400
  }