js-draw 1.0.1 → 1.0.2

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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/cjs/version.js +1 -1
  4. package/dist/mjs/version.mjs +1 -1
  5. package/docs/img/readme-images/js-draw.jpg +0 -0
  6. package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
  7. package/package.json +5 -4
  8. package/dist-test/test_imports/package-lock.json +0 -13
  9. package/dist-test/test_imports/package.json +0 -12
  10. package/dist-test/test_imports/test-imports.js +0 -11
  11. package/dist-test/test_imports/test-require.cjs +0 -14
  12. package/src/Editor.loadFrom.test.ts +0 -24
  13. package/src/Editor.test.ts +0 -107
  14. package/src/Editor.toSVG.test.ts +0 -294
  15. package/src/Editor.ts +0 -1443
  16. package/src/EditorImage.test.ts +0 -117
  17. package/src/EditorImage.ts +0 -609
  18. package/src/EventDispatcher.test.ts +0 -123
  19. package/src/EventDispatcher.ts +0 -72
  20. package/src/Pointer.ts +0 -183
  21. package/src/SVGLoader.test.ts +0 -114
  22. package/src/SVGLoader.ts +0 -672
  23. package/src/UndoRedoHistory.test.ts +0 -34
  24. package/src/UndoRedoHistory.ts +0 -102
  25. package/src/Viewport.ts +0 -322
  26. package/src/bundle/bundled.ts +0 -7
  27. package/src/commands/Command.ts +0 -45
  28. package/src/commands/Duplicate.ts +0 -75
  29. package/src/commands/Erase.ts +0 -95
  30. package/src/commands/SerializableCommand.ts +0 -49
  31. package/src/commands/UnresolvedCommand.ts +0 -37
  32. package/src/commands/invertCommand.ts +0 -58
  33. package/src/commands/lib.ts +0 -16
  34. package/src/commands/localization.ts +0 -47
  35. package/src/commands/uniteCommands.test.ts +0 -23
  36. package/src/commands/uniteCommands.ts +0 -140
  37. package/src/components/AbstractComponent.transformBy.test.ts +0 -23
  38. package/src/components/AbstractComponent.ts +0 -383
  39. package/src/components/BackgroundComponent.test.ts +0 -44
  40. package/src/components/BackgroundComponent.ts +0 -348
  41. package/src/components/ImageComponent.ts +0 -176
  42. package/src/components/RestylableComponent.ts +0 -161
  43. package/src/components/SVGGlobalAttributesObject.ts +0 -79
  44. package/src/components/Stroke.test.ts +0 -137
  45. package/src/components/Stroke.ts +0 -294
  46. package/src/components/TextComponent.test.ts +0 -202
  47. package/src/components/TextComponent.ts +0 -429
  48. package/src/components/UnknownSVGObject.test.ts +0 -10
  49. package/src/components/UnknownSVGObject.ts +0 -60
  50. package/src/components/builders/ArrowBuilder.ts +0 -106
  51. package/src/components/builders/CircleBuilder.ts +0 -100
  52. package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
  53. package/src/components/builders/FreehandLineBuilder.ts +0 -210
  54. package/src/components/builders/LineBuilder.ts +0 -77
  55. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
  56. package/src/components/builders/RectangleBuilder.ts +0 -73
  57. package/src/components/builders/types.ts +0 -15
  58. package/src/components/lib.ts +0 -31
  59. package/src/components/localization.ts +0 -24
  60. package/src/components/util/StrokeSmoother.ts +0 -302
  61. package/src/components/util/describeComponentList.ts +0 -18
  62. package/src/dialogs/makeAboutDialog.ts +0 -82
  63. package/src/inputEvents.ts +0 -143
  64. package/src/lib.ts +0 -91
  65. package/src/localization.ts +0 -34
  66. package/src/localizations/de.ts +0 -146
  67. package/src/localizations/en.ts +0 -8
  68. package/src/localizations/es.ts +0 -74
  69. package/src/localizations/getLocalizationTable.test.ts +0 -27
  70. package/src/localizations/getLocalizationTable.ts +0 -74
  71. package/src/rendering/Display.ts +0 -247
  72. package/src/rendering/RenderablePathSpec.ts +0 -88
  73. package/src/rendering/RenderingStyle.test.ts +0 -68
  74. package/src/rendering/RenderingStyle.ts +0 -55
  75. package/src/rendering/TextRenderingStyle.ts +0 -55
  76. package/src/rendering/caching/CacheRecord.test.ts +0 -48
  77. package/src/rendering/caching/CacheRecord.ts +0 -76
  78. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  79. package/src/rendering/caching/RenderingCache.test.ts +0 -43
  80. package/src/rendering/caching/RenderingCache.ts +0 -66
  81. package/src/rendering/caching/RenderingCacheNode.ts +0 -404
  82. package/src/rendering/caching/testUtils.ts +0 -35
  83. package/src/rendering/caching/types.ts +0 -34
  84. package/src/rendering/lib.ts +0 -8
  85. package/src/rendering/localization.ts +0 -20
  86. package/src/rendering/renderers/AbstractRenderer.ts +0 -232
  87. package/src/rendering/renderers/CanvasRenderer.ts +0 -312
  88. package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
  89. package/src/rendering/renderers/DummyRenderer.ts +0 -142
  90. package/src/rendering/renderers/SVGRenderer.ts +0 -434
  91. package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
  92. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
  93. package/src/shortcuts/KeyBinding.test.ts +0 -61
  94. package/src/shortcuts/KeyBinding.ts +0 -257
  95. package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
  96. package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
  97. package/src/shortcuts/lib.ts +0 -3
  98. package/src/testing/createEditor.ts +0 -11
  99. package/src/testing/getUniquePointerId.ts +0 -18
  100. package/src/testing/lib.ts +0 -3
  101. package/src/testing/sendPenEvent.ts +0 -36
  102. package/src/testing/sendTouchEvent.ts +0 -71
  103. package/src/toolbar/AbstractToolbar.ts +0 -542
  104. package/src/toolbar/DropdownToolbar.ts +0 -220
  105. package/src/toolbar/EdgeToolbar.test.ts +0 -54
  106. package/src/toolbar/EdgeToolbar.ts +0 -543
  107. package/src/toolbar/IconProvider.ts +0 -861
  108. package/src/toolbar/constants.ts +0 -1
  109. package/src/toolbar/lib.ts +0 -6
  110. package/src/toolbar/localization.ts +0 -136
  111. package/src/toolbar/types.ts +0 -13
  112. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  113. package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
  114. package/src/toolbar/widgets/BaseWidget.ts +0 -495
  115. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
  116. package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
  117. package/src/toolbar/widgets/HandToolWidget.ts +0 -239
  118. package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
  119. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  120. package/src/toolbar/widgets/PenToolWidget.ts +0 -369
  121. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
  122. package/src/toolbar/widgets/TextToolWidget.ts +0 -149
  123. package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
  124. package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
  125. package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
  126. package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
  127. package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
  128. package/src/toolbar/widgets/keybindings.ts +0 -19
  129. package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
  130. package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
  131. package/src/toolbar/widgets/layout/types.ts +0 -74
  132. package/src/toolbar/widgets/lib.ts +0 -13
  133. package/src/tools/BaseTool.ts +0 -169
  134. package/src/tools/Eraser.test.ts +0 -103
  135. package/src/tools/Eraser.ts +0 -173
  136. package/src/tools/FindTool.test.ts +0 -67
  137. package/src/tools/FindTool.ts +0 -153
  138. package/src/tools/InputFilter/FunctionMapper.ts +0 -17
  139. package/src/tools/InputFilter/InputMapper.ts +0 -41
  140. package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
  141. package/src/tools/InputFilter/InputPipeline.ts +0 -34
  142. package/src/tools/InputFilter/InputStabilizer.ts +0 -254
  143. package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
  144. package/src/tools/PanZoom.test.ts +0 -339
  145. package/src/tools/PanZoom.ts +0 -525
  146. package/src/tools/PasteHandler.ts +0 -94
  147. package/src/tools/Pen.test.ts +0 -260
  148. package/src/tools/Pen.ts +0 -284
  149. package/src/tools/PipetteTool.ts +0 -84
  150. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
  151. package/src/tools/SelectionTool/Selection.ts +0 -647
  152. package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
  153. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
  154. package/src/tools/SelectionTool/SelectionTool.ts +0 -510
  155. package/src/tools/SelectionTool/TransformMode.ts +0 -112
  156. package/src/tools/SelectionTool/types.ts +0 -11
  157. package/src/tools/SoundUITool.ts +0 -221
  158. package/src/tools/TextTool.ts +0 -339
  159. package/src/tools/ToolController.ts +0 -224
  160. package/src/tools/ToolEnabledGroup.ts +0 -14
  161. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  162. package/src/tools/ToolbarShortcutHandler.ts +0 -39
  163. package/src/tools/UndoRedoShortcut.test.ts +0 -62
  164. package/src/tools/UndoRedoShortcut.ts +0 -24
  165. package/src/tools/keybindings.ts +0 -85
  166. package/src/tools/lib.ts +0 -22
  167. package/src/tools/localization.ts +0 -76
  168. package/src/types.ts +0 -151
  169. package/src/util/ReactiveValue.test.ts +0 -168
  170. package/src/util/ReactiveValue.ts +0 -241
  171. package/src/util/assertions.ts +0 -55
  172. package/src/util/fileToBase64.ts +0 -18
  173. package/src/util/guessKeyCodeFromKey.ts +0 -36
  174. package/src/util/listPrefixMatch.ts +0 -19
  175. package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
  176. package/src/util/untilNextAnimationFrame.ts +0 -9
  177. package/src/util/waitForAll.ts +0 -18
  178. package/src/util/waitForTimeout.ts +0 -9
  179. package/src/version.test.ts +0 -12
  180. package/src/version.ts +0 -3
  181. package/tools/allLocales.js +0 -4
  182. package/tools/copyREADME.ts +0 -62
@@ -1,142 +0,0 @@
1
- import { assertUnreachable } from '../../util/assertions';
2
- import { Point2, Rect2, Vec2 } from '@js-draw/math';
3
- import { cssPrefix } from './SelectionTool';
4
- import Selection from './Selection';
5
- import Pointer from '../../Pointer';
6
- import Viewport from '../../Viewport';
7
-
8
- export enum HandleShape {
9
- Circle,
10
- Square,
11
- }
12
-
13
- export const handleSize = 30;
14
-
15
- // `startPoint` is in screen coordinates
16
- export type DragStartCallback = (startPoint: Point2)=>void;
17
- export type DragUpdateCallback = (canvasPoint: Point2)=> void;
18
- export type DragEndCallback = ()=> void;
19
-
20
- export default class SelectionHandle {
21
- private element: HTMLElement;
22
- private snapToGrid: boolean;
23
-
24
- public constructor(
25
- readonly shape: HandleShape,
26
- private readonly parentSide: Vec2,
27
- private readonly parent: Selection,
28
- private readonly viewport: Viewport,
29
-
30
- private readonly onDragStart: DragStartCallback,
31
- private readonly onDragUpdate: DragUpdateCallback,
32
- private readonly onDragEnd: DragEndCallback,
33
- ) {
34
- this.element = document.createElement('div');
35
- this.element.classList.add(`${cssPrefix}handle`);
36
-
37
- switch (shape) {
38
- case HandleShape.Circle:
39
- this.element.classList.add(`${cssPrefix}circle`);
40
- break;
41
- case HandleShape.Square:
42
- this.element.classList.add(`${cssPrefix}square`);
43
- break;
44
- default:
45
- assertUnreachable(shape);
46
- }
47
-
48
- this.updatePosition();
49
- }
50
-
51
- /**
52
- * Adds this to `container`, where `conatiner` should be the background/selection
53
- * element visible on the screen.
54
- */
55
- public addTo(container: HTMLElement) {
56
- container.appendChild(this.element);
57
- }
58
-
59
- /**
60
- * Returns this handle's bounding box relative to the top left of the
61
- * selection box.
62
- */
63
- private getBBoxParentCoords() {
64
- const parentRect = this.parent.screenRegion;
65
- const size = Vec2.of(handleSize, handleSize);
66
- const topLeft = parentRect.size.scale(this.parentSide)
67
- // Center
68
- .minus(size.times(1/2));
69
-
70
- return new Rect2(topLeft.x, topLeft.y, size.x, size.y);
71
- }
72
-
73
- /** @returns this handle's bounding box relative to the canvas. */
74
- private getBBoxCanvasCoords() {
75
- const parentRect = this.parent.region;
76
- const size = Vec2.of(handleSize, handleSize).times(1/this.viewport.getScaleFactor());
77
-
78
- const topLeftFromParent = parentRect.size.scale(this.parentSide).minus(size.times(0.5));
79
-
80
- return new Rect2(topLeftFromParent.x, topLeftFromParent.y, size.x, size.y).translatedBy(parentRect.topLeft);
81
- }
82
-
83
- /**
84
- * Moves the HTML representation of this to the location matching its internal representation.
85
- */
86
- public updatePosition() {
87
- const bbox = this.getBBoxParentCoords();
88
-
89
- // Position within the selection box.
90
- this.element.style.marginLeft = `${bbox.topLeft.x}px`;
91
- this.element.style.marginTop = `${bbox.topLeft.y}px`;
92
- this.element.style.width = `${bbox.w}px`;
93
- this.element.style.height = `${bbox.h}px`;
94
- }
95
-
96
- /** @returns true iff `point` (in editor **canvas** coordinates) is in this. */
97
- public containsPoint(point: Point2) {
98
- const bbox = this.getBBoxCanvasCoords();
99
- const delta = point.minus(bbox.center);
100
-
101
- // Should have same x and y radius
102
- const radius = bbox.size.x / 2;
103
-
104
- let result;
105
- if (this.shape === HandleShape.Circle) {
106
- result = delta.magnitude() <= radius;
107
- } else {
108
- result = Math.abs(delta.x) <= radius && Math.abs(delta.y) <= radius;
109
- }
110
-
111
- return result;
112
- }
113
-
114
- private dragLastPos: Vec2|null = null;
115
- public handleDragStart(pointer: Pointer) {
116
- this.onDragStart(pointer.canvasPos);
117
- this.dragLastPos = pointer.canvasPos;
118
- }
119
-
120
- public handleDragUpdate(pointer: Pointer) {
121
- if (!this.dragLastPos) {
122
- return;
123
- }
124
-
125
- this.onDragUpdate(pointer.canvasPos);
126
- }
127
-
128
- public handleDragEnd() {
129
- if (!this.dragLastPos) {
130
- return;
131
- }
132
- this.onDragEnd();
133
- }
134
-
135
- public setSnapToGrid(snap: boolean) {
136
- this.snapToGrid = snap;
137
- }
138
-
139
- public isSnappingToGrid() {
140
- return this.snapToGrid;
141
- }
142
- }
@@ -1,370 +0,0 @@
1
- import Stroke from '../../components/Stroke';
2
- import Editor from '../../Editor';
3
- import EditorImage from '../../EditorImage';
4
- import { InputEvtType } from '../../inputEvents';
5
- import Selection from './Selection';
6
- import SelectionTool from './SelectionTool';
7
- import createEditor from '../../testing/createEditor';
8
- import Pointer from '../../Pointer';
9
- import { Rect2, Vec2, Path, Color4 } from '@js-draw/math';
10
- import sendPenEvent from '../../testing/sendPenEvent';
11
- import { pathToRenderable } from '../../rendering/RenderablePathSpec';
12
- import { EditorEventType } from '../../types';
13
-
14
- const getSelectionTool = (editor: Editor): SelectionTool => {
15
- return editor.toolController.getMatchingTools(SelectionTool)[0];
16
- };
17
-
18
- const createSquareStroke = (size: number = 1) => {
19
- const testStroke = new Stroke([
20
- // A filled square
21
- pathToRenderable(Path.fromString(`M0,0 L${size},0 L${size},${size} L0,${size} Z`), { fill: Color4.blue }),
22
- ]);
23
- const addTestStrokeCommand = EditorImage.addElement(testStroke);
24
-
25
- return { testStroke, addTestStrokeCommand };
26
- };
27
-
28
- const createEditorWithSingleObjectSelection = (objectSize: number = 50) => {
29
- const { testStroke, addTestStrokeCommand } = createSquareStroke(objectSize);
30
- const editor = createEditor();
31
- editor.dispatch(addTestStrokeCommand);
32
-
33
- // Select the object
34
- const selectionTool = getSelectionTool(editor);
35
- selectionTool.setEnabled(true);
36
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
37
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(10, 10));
38
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(5, 5));
39
-
40
- return { editor, testStroke, selectionTool };
41
- };
42
-
43
- const dragSelection = (editor: Editor, selection: Selection, startPt: Vec2, endPt: Vec2) => {
44
- selection.onDragStart(Pointer.ofCanvasPoint(startPt, true, editor.viewport));
45
- jest.advanceTimersByTime(100);
46
-
47
- selection.onDragUpdate(Pointer.ofCanvasPoint(endPt, true, editor.viewport));
48
- jest.advanceTimersByTime(100);
49
-
50
- selection.onDragEnd();
51
- };
52
-
53
- describe('SelectionTool', () => {
54
- it('selection should shrink/grow to bounding box of selected objects', () => {
55
- const { addTestStrokeCommand } = createSquareStroke();
56
-
57
- const editor = createEditor();
58
- editor.dispatch(addTestStrokeCommand);
59
-
60
- const selectionTool = getSelectionTool(editor);
61
- selectionTool.setEnabled(true);
62
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
63
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0.1, 0.1));
64
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0.1, 0.1));
65
-
66
- // Should surround the selected object (which has bbox = (0, 0, 1, 1))
67
- // with extra space.
68
- const paddingSize = selectionTool.getSelection()!.getMinCanvasSize();
69
- expect(selectionTool.getSelection()!.region).toMatchObject({
70
- x: -paddingSize / 2,
71
- y: -paddingSize / 2,
72
- w: paddingSize + 1,
73
- h: paddingSize + 1,
74
- });
75
- });
76
-
77
- it('sending keyboard events to the selected region should move selected items', () => {
78
- const { editor, selectionTool, testStroke } = createEditorWithSingleObjectSelection(50);
79
- const selection = selectionTool.getSelection();
80
- expect(selection).not.toBeNull();
81
-
82
- // Drag the object
83
- // (d => move right (d is from WASD controls.))
84
- editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'd');
85
- editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'd');
86
- editor.sendKeyboardEvent(InputEvtType.KeyUpEvent, 'd');
87
-
88
- expect(testStroke.getBBox().topLeft.x).toBeGreaterThan(5);
89
-
90
- editor.history.undo();
91
-
92
- expect(testStroke.getBBox().topLeft).toMatchObject({
93
- x: 0,
94
- y: 0,
95
- });
96
- });
97
-
98
- it('moving the selection with a keyboard should move the view to keep the selection in view', () => {
99
- const { editor, selectionTool } = createEditorWithSingleObjectSelection(50);
100
-
101
- const selection = selectionTool.getSelection();
102
- if (selection === null) {
103
- // Throw to allow TypeScript's non-null checker to understand that selection
104
- // must be non-null after this.
105
- throw new Error('Selection should be non-null.');
106
- }
107
-
108
- editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'a');
109
- editor.sendKeyboardEvent(InputEvtType.KeyUpEvent, 'a');
110
- expect(editor.viewport.visibleRect.containsPoint(selection.region.center)).toBe(true);
111
- });
112
-
113
- it('shift+click should expand an existing selection', () => {
114
- const { addTestStrokeCommand: stroke1Command } = createSquareStroke(50);
115
- const { addTestStrokeCommand: stroke2Command } = createSquareStroke(500);
116
-
117
- const editor = createEditor();
118
- editor.dispatch(stroke1Command);
119
- editor.dispatch(stroke2Command);
120
-
121
- // Select the first stroke
122
- const selectionTool = getSelectionTool(editor);
123
- selectionTool.setEnabled(true);
124
-
125
- // Select the smaller rectangle
126
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(40, 40));
127
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(100, 100));
128
-
129
- expect(selectionTool.getSelectedObjects()).toHaveLength(1);
130
-
131
- // Shift key down.
132
- editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'Shift');
133
-
134
- // Select the larger stroke.
135
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(200, 200));
136
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(600, 600));
137
-
138
- expect(selectionTool.getSelectedObjects()).toHaveLength(2);
139
-
140
- editor.sendKeyboardEvent(InputEvtType.KeyUpEvent, 'Shift');
141
-
142
- // Select the larger stroke without shift pressed
143
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(600, 600));
144
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(200, 200));
145
- expect(selectionTool.getSelectedObjects()).toHaveLength(1);
146
-
147
- // Select nothing
148
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(2000, 200));
149
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(2001, 201));
150
- expect(selectionTool.getSelectedObjects()).toHaveLength(0);
151
- });
152
-
153
- it('should remove the selection from the document while dragging', () => {
154
- const { editor, selectionTool } = createEditorWithSingleObjectSelection(50);
155
-
156
- const selection = selectionTool.getSelection()!;
157
- selection.onDragStart(Pointer.ofCanvasPoint(Vec2.of(0, 0), true, editor.viewport));
158
- jest.advanceTimersByTime(100);
159
- selection.onDragUpdate(Pointer.ofCanvasPoint(Vec2.of(20, 0), true, editor.viewport));
160
- jest.advanceTimersByTime(100);
161
-
162
- // Expect the selection to not be in the image while dragging
163
- expect(editor.image.getAllElements()).toHaveLength(0);
164
-
165
- selection.onDragEnd();
166
-
167
- expect(editor.image.getAllElements()).toHaveLength(1);
168
- });
169
-
170
- it('should drag objects horizontally', () => {
171
- const { editor, selectionTool, testStroke } = createEditorWithSingleObjectSelection(50);
172
-
173
- expect(editor.image.findParent(testStroke)).not.toBeNull();
174
- expect(testStroke.getBBox().topLeft).objEq(Vec2.of(0, 0));
175
-
176
- const selection = selectionTool.getSelection()!;
177
- dragSelection(editor, selection, Vec2.of(0, 0), Vec2.of(10, 0));
178
-
179
- expect(editor.image.findParent(testStroke)).not.toBeNull();
180
- expect(testStroke.getBBox().topLeft).objEq(Vec2.of(10, 0));
181
- });
182
-
183
- it('should round changes in objects positions when dragging', () => {
184
- const { editor, selectionTool, testStroke } = createEditorWithSingleObjectSelection(50);
185
-
186
- expect(editor.image.findParent(testStroke)).not.toBeNull();
187
- expect(testStroke.getBBox().topLeft).objEq(Vec2.of(0, 0));
188
-
189
- const selection = selectionTool.getSelection()!;
190
- dragSelection(editor, selection, Vec2.of(0, 0), Vec2.of(9.999, 0));
191
-
192
- expect(editor.image.findParent(testStroke)).not.toBeNull();
193
- expect(testStroke.getBBox().topLeft).objEq(Vec2.of(10, 0));
194
- });
195
-
196
- it('rotation handle should rotate the selection', () => {
197
- const { addTestStrokeCommand: strokeCommand } = createSquareStroke(50);
198
-
199
- const editor = createEditor();
200
- editor.dispatch(strokeCommand);
201
-
202
- // Select the first stroke
203
- const selectionTool = getSelectionTool(editor);
204
- selectionTool.setEnabled(true);
205
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(40, 40));
206
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(100, 100));
207
-
208
- // Drag the rotate handle, which should be located halfway across
209
- // the top of the selection box
210
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(25, 0));
211
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(30, 0));
212
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0, 25));
213
-
214
- // Rotate 45 degrees:
215
- // Drag start (resize circle)
216
- // ↓
217
- // .---o---x ← y=0, drag end
218
- // | |
219
- // | |
220
- // | |
221
- // .-------.
222
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(50, 0));
223
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(50, 0));
224
-
225
- expect(selectionTool.getSelectedObjects()).toHaveLength(1);
226
-
227
- // Deselect & add the item back to the editor
228
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(1250, 0));
229
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(1250, 0));
230
-
231
- expect(selectionTool.getSelectedObjects()).toHaveLength(0);
232
-
233
- const imageStrokes = editor.image.getAllElements();
234
- expect(imageStrokes).toHaveLength(1);
235
-
236
- const transformedStroke = imageStrokes[0] as Stroke;
237
- const strokePoints = transformedStroke.getPath().polylineApproximation().map(line => line.p1);
238
-
239
- // One point should now be just above the center of the square:
240
- // . ←
241
- // . .
242
- // .
243
- //
244
- expect(strokePoints.filter(point => point.eq(Vec2.of(Math.hypot(25, 0), 0)))).toHaveLength(1);
245
- });
246
-
247
- it('dragCancel should return a selection to its original position', () => {
248
- const { editor, selectionTool, testStroke } = createEditorWithSingleObjectSelection(150);
249
-
250
- const selection = selectionTool.getSelection()!;
251
- expect(testStroke.getBBox().topLeft).objEq(Vec2.zero);
252
-
253
- selection.onDragStart(Pointer.ofCanvasPoint(Vec2.of(10, 0), true, editor.viewport));
254
- jest.advanceTimersByTime(100);
255
- selection.onDragUpdate(Pointer.ofCanvasPoint(Vec2.of(200, 10), true, editor.viewport));
256
- jest.advanceTimersByTime(100);
257
- selection.onDragCancel();
258
-
259
- expect(testStroke.getBBox().topLeft).objEq(Vec2.zero);
260
- expect(editor.image.findParent(testStroke)).not.toBeNull();
261
- });
262
-
263
- it('duplicateSelectedObjects should duplicate a selection while dragging', async () => {
264
- const { editor, selectionTool, testStroke } = createEditorWithSingleObjectSelection(150);
265
-
266
- const selection = selectionTool.getSelection()!;
267
- selection.onDragStart(Pointer.ofCanvasPoint(Vec2.of(0, 0), true, editor.viewport));
268
- jest.advanceTimersByTime(100);
269
- selection.onDragUpdate(Pointer.ofCanvasPoint(Vec2.of(20, 0), true, editor.viewport));
270
-
271
- // The selection should not be in the document while dragging
272
- expect(editor.image.findParent(testStroke)).toBeNull();
273
-
274
- await editor.dispatch(await selection.duplicateSelectedObjects());
275
- jest.advanceTimersByTime(100);
276
-
277
- // The duplicate stroke should be added to the document, but the original should not.
278
- expect(editor.image.findParent(testStroke)).toBeNull();
279
-
280
- const allObjectsInImage = editor.image.getAllElements();
281
- expect(allObjectsInImage).toHaveLength(1);
282
-
283
- const duplicateObject = allObjectsInImage[0];
284
-
285
- // The duplicate stroke should be translated
286
- expect(duplicateObject.getBBox()).objEq(new Rect2(20, 0, 150, 150));
287
-
288
- // The duplicate stroke should be selected.
289
- expect(selection.getSelectedObjects()).toHaveLength(1);
290
-
291
- // The test stroke should not be added to the document
292
- // (esp if we continue dragging)
293
- selection.onDragUpdate(Pointer.ofCanvasPoint(Vec2.of(30, 10), true, editor.viewport));
294
- jest.advanceTimersByTime(100);
295
-
296
- expect(editor.image.findParent(testStroke)).toBeNull();
297
-
298
- // The test stroke should be translated when we finish dragging.
299
- selection.onDragEnd();
300
-
301
- expect(editor.image.findParent(testStroke)).not.toBeNull();
302
- expect(testStroke.getBBox()).objEq(new Rect2(30, 10, 150, 150));
303
- });
304
-
305
- it('should only fire the SelectionChanged event if the selection changed', () => {
306
- const { editor, selectionTool, testStroke } = createEditorWithSingleObjectSelection(150);
307
-
308
- selectionTool.clearSelection();
309
-
310
- const updatedListener = jest.fn();
311
- editor.notifier.on(EditorEventType.SelectionUpdated, updatedListener);
312
-
313
- expect(updatedListener).toHaveBeenCalledTimes(0);
314
-
315
- selectionTool.setEnabled(true);
316
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
317
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(10, 10));
318
-
319
- // Should not be notified until the selection ends
320
- expect(updatedListener).toHaveBeenCalledTimes(0);
321
-
322
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(5, 5));
323
-
324
- expect(updatedListener).toHaveBeenCalledTimes(1);
325
- expect(updatedListener).toHaveBeenLastCalledWith({
326
- kind: EditorEventType.SelectionUpdated,
327
- tool: selectionTool,
328
- selectedComponents: [ testStroke ]
329
- });
330
-
331
- // Selecting the same content should not re-fire the listener
332
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
333
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(10, 10));
334
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(5, 5));
335
-
336
- expect(updatedListener).toHaveBeenCalledTimes(1);
337
-
338
- // ...but selecting a different item should.
339
- const secondStroke = createSquareStroke(3000); // Large to ensure that we don't drag the selection instead.
340
- editor.dispatch(secondStroke.addTestStrokeCommand);
341
-
342
- expect(updatedListener).toHaveBeenCalledTimes(1);
343
-
344
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(2999, 2999));
345
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(3001, 3001));
346
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(3002, 3002));
347
-
348
- expect(updatedListener).toHaveBeenCalledTimes(2);
349
- expect(updatedListener).toHaveBeenLastCalledWith({
350
- kind: EditorEventType.SelectionUpdated,
351
- tool: selectionTool,
352
- selectedComponents: [ secondStroke.testStroke ],
353
- });
354
- });
355
-
356
- it('should remove the selection box after ending an empty selection', () => {
357
- const { editor, selectionTool } = createEditorWithSingleObjectSelection(150);
358
-
359
- // Should have a selection when objects are selected
360
- expect(selectionTool.getSelection()).not.toBe(null);
361
-
362
- // Select nothing
363
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(2999, 2999));
364
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(3001, 3001));
365
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(3002, 3002));
366
-
367
- // Should not have a selection after setting the selection to contain no objects
368
- expect(selectionTool.getSelection()).toBe(null);
369
- });
370
- });