js-draw 1.0.1 → 1.1.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.
- package/LICENSE +21 -0
- package/dist/Editor.css +1 -0
- package/dist/bundle.js +1 -1
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +9 -13
- package/dist/cjs/toolbar/AbstractToolbar.js +14 -19
- package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
- package/dist/cjs/toolbar/widgets/SaveActionWidget.js +26 -0
- package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +9 -13
- package/dist/mjs/toolbar/AbstractToolbar.mjs +14 -19
- package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
- package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +21 -0
- package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
- package/dist/mjs/version.mjs +1 -1
- package/docs/img/readme-images/js-draw.jpg +0 -0
- package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
- package/package.json +5 -4
- package/src/toolbar/EdgeToolbar.scss +1 -0
- package/dist-test/test_imports/package-lock.json +0 -13
- package/dist-test/test_imports/package.json +0 -12
- package/dist-test/test_imports/test-imports.js +0 -11
- package/dist-test/test_imports/test-require.cjs +0 -14
- package/src/Editor.loadFrom.test.ts +0 -24
- package/src/Editor.test.ts +0 -107
- package/src/Editor.toSVG.test.ts +0 -294
- package/src/Editor.ts +0 -1443
- package/src/EditorImage.test.ts +0 -117
- package/src/EditorImage.ts +0 -609
- package/src/EventDispatcher.test.ts +0 -123
- package/src/EventDispatcher.ts +0 -72
- package/src/Pointer.ts +0 -183
- package/src/SVGLoader.test.ts +0 -114
- package/src/SVGLoader.ts +0 -672
- package/src/UndoRedoHistory.test.ts +0 -34
- package/src/UndoRedoHistory.ts +0 -102
- package/src/Viewport.ts +0 -322
- package/src/bundle/bundled.ts +0 -7
- package/src/commands/Command.ts +0 -45
- package/src/commands/Duplicate.ts +0 -75
- package/src/commands/Erase.ts +0 -95
- package/src/commands/SerializableCommand.ts +0 -49
- package/src/commands/UnresolvedCommand.ts +0 -37
- package/src/commands/invertCommand.ts +0 -58
- package/src/commands/lib.ts +0 -16
- package/src/commands/localization.ts +0 -47
- package/src/commands/uniteCommands.test.ts +0 -23
- package/src/commands/uniteCommands.ts +0 -140
- package/src/components/AbstractComponent.transformBy.test.ts +0 -23
- package/src/components/AbstractComponent.ts +0 -383
- package/src/components/BackgroundComponent.test.ts +0 -44
- package/src/components/BackgroundComponent.ts +0 -348
- package/src/components/ImageComponent.ts +0 -176
- package/src/components/RestylableComponent.ts +0 -161
- package/src/components/SVGGlobalAttributesObject.ts +0 -79
- package/src/components/Stroke.test.ts +0 -137
- package/src/components/Stroke.ts +0 -294
- package/src/components/TextComponent.test.ts +0 -202
- package/src/components/TextComponent.ts +0 -429
- package/src/components/UnknownSVGObject.test.ts +0 -10
- package/src/components/UnknownSVGObject.ts +0 -60
- package/src/components/builders/ArrowBuilder.ts +0 -106
- package/src/components/builders/CircleBuilder.ts +0 -100
- package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
- package/src/components/builders/FreehandLineBuilder.ts +0 -210
- package/src/components/builders/LineBuilder.ts +0 -77
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
- package/src/components/builders/RectangleBuilder.ts +0 -73
- package/src/components/builders/types.ts +0 -15
- package/src/components/lib.ts +0 -31
- package/src/components/localization.ts +0 -24
- package/src/components/util/StrokeSmoother.ts +0 -302
- package/src/components/util/describeComponentList.ts +0 -18
- package/src/dialogs/makeAboutDialog.ts +0 -82
- package/src/inputEvents.ts +0 -143
- package/src/lib.ts +0 -91
- package/src/localization.ts +0 -34
- package/src/localizations/de.ts +0 -146
- package/src/localizations/en.ts +0 -8
- package/src/localizations/es.ts +0 -74
- package/src/localizations/getLocalizationTable.test.ts +0 -27
- package/src/localizations/getLocalizationTable.ts +0 -74
- package/src/rendering/Display.ts +0 -247
- package/src/rendering/RenderablePathSpec.ts +0 -88
- package/src/rendering/RenderingStyle.test.ts +0 -68
- package/src/rendering/RenderingStyle.ts +0 -55
- package/src/rendering/TextRenderingStyle.ts +0 -55
- package/src/rendering/caching/CacheRecord.test.ts +0 -48
- package/src/rendering/caching/CacheRecord.ts +0 -76
- package/src/rendering/caching/CacheRecordManager.ts +0 -71
- package/src/rendering/caching/RenderingCache.test.ts +0 -43
- package/src/rendering/caching/RenderingCache.ts +0 -66
- package/src/rendering/caching/RenderingCacheNode.ts +0 -404
- package/src/rendering/caching/testUtils.ts +0 -35
- package/src/rendering/caching/types.ts +0 -34
- package/src/rendering/lib.ts +0 -8
- package/src/rendering/localization.ts +0 -20
- package/src/rendering/renderers/AbstractRenderer.ts +0 -232
- package/src/rendering/renderers/CanvasRenderer.ts +0 -312
- package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
- package/src/rendering/renderers/DummyRenderer.ts +0 -142
- package/src/rendering/renderers/SVGRenderer.ts +0 -434
- package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
- package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
- package/src/shortcuts/KeyBinding.test.ts +0 -61
- package/src/shortcuts/KeyBinding.ts +0 -257
- package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
- package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
- package/src/shortcuts/lib.ts +0 -3
- package/src/testing/createEditor.ts +0 -11
- package/src/testing/getUniquePointerId.ts +0 -18
- package/src/testing/lib.ts +0 -3
- package/src/testing/sendPenEvent.ts +0 -36
- package/src/testing/sendTouchEvent.ts +0 -71
- package/src/toolbar/AbstractToolbar.ts +0 -542
- package/src/toolbar/DropdownToolbar.ts +0 -220
- package/src/toolbar/EdgeToolbar.test.ts +0 -54
- package/src/toolbar/EdgeToolbar.ts +0 -543
- package/src/toolbar/IconProvider.ts +0 -861
- package/src/toolbar/constants.ts +0 -1
- package/src/toolbar/lib.ts +0 -6
- package/src/toolbar/localization.ts +0 -136
- package/src/toolbar/types.ts +0 -13
- package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
- package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
- package/src/toolbar/widgets/BaseWidget.ts +0 -495
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
- package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
- package/src/toolbar/widgets/HandToolWidget.ts +0 -239
- package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
- package/src/toolbar/widgets/OverflowWidget.ts +0 -92
- package/src/toolbar/widgets/PenToolWidget.ts +0 -369
- package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
- package/src/toolbar/widgets/TextToolWidget.ts +0 -149
- package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
- package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
- package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
- package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
- package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
- package/src/toolbar/widgets/keybindings.ts +0 -19
- package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
- package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
- package/src/toolbar/widgets/layout/types.ts +0 -74
- package/src/toolbar/widgets/lib.ts +0 -13
- package/src/tools/BaseTool.ts +0 -169
- package/src/tools/Eraser.test.ts +0 -103
- package/src/tools/Eraser.ts +0 -173
- package/src/tools/FindTool.test.ts +0 -67
- package/src/tools/FindTool.ts +0 -153
- package/src/tools/InputFilter/FunctionMapper.ts +0 -17
- package/src/tools/InputFilter/InputMapper.ts +0 -41
- package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
- package/src/tools/InputFilter/InputPipeline.ts +0 -34
- package/src/tools/InputFilter/InputStabilizer.ts +0 -254
- package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
- package/src/tools/PanZoom.test.ts +0 -339
- package/src/tools/PanZoom.ts +0 -525
- package/src/tools/PasteHandler.ts +0 -94
- package/src/tools/Pen.test.ts +0 -260
- package/src/tools/Pen.ts +0 -284
- package/src/tools/PipetteTool.ts +0 -84
- package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
- package/src/tools/SelectionTool/Selection.ts +0 -647
- package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
- package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
- package/src/tools/SelectionTool/SelectionTool.ts +0 -510
- package/src/tools/SelectionTool/TransformMode.ts +0 -112
- package/src/tools/SelectionTool/types.ts +0 -11
- package/src/tools/SoundUITool.ts +0 -221
- package/src/tools/TextTool.ts +0 -339
- package/src/tools/ToolController.ts +0 -224
- package/src/tools/ToolEnabledGroup.ts +0 -14
- package/src/tools/ToolSwitcherShortcut.ts +0 -39
- package/src/tools/ToolbarShortcutHandler.ts +0 -39
- package/src/tools/UndoRedoShortcut.test.ts +0 -62
- package/src/tools/UndoRedoShortcut.ts +0 -24
- package/src/tools/keybindings.ts +0 -85
- package/src/tools/lib.ts +0 -22
- package/src/tools/localization.ts +0 -76
- package/src/types.ts +0 -151
- package/src/util/ReactiveValue.test.ts +0 -168
- package/src/util/ReactiveValue.ts +0 -241
- package/src/util/assertions.ts +0 -55
- package/src/util/fileToBase64.ts +0 -18
- package/src/util/guessKeyCodeFromKey.ts +0 -36
- package/src/util/listPrefixMatch.ts +0 -19
- package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
- package/src/util/untilNextAnimationFrame.ts +0 -9
- package/src/util/waitForAll.ts +0 -18
- package/src/util/waitForTimeout.ts +0 -9
- package/src/version.test.ts +0 -12
- package/src/version.ts +0 -3
- package/tools/allLocales.js +0 -4
- 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
|
-
});
|