js-draw 1.25.0 → 1.27.1
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +1 -1
- package/dist/Editor.css +1 -1935
- package/dist/bundle.js +478 -4
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +0 -2
- package/dist/cjs/Editor.js +1 -1
- package/dist/cjs/bundle/bundled.js +2 -1
- package/dist/cjs/components/AbstractComponent.d.ts +15 -0
- package/dist/cjs/components/AbstractComponent.js +16 -0
- package/dist/cjs/components/Stroke.d.ts +1 -0
- package/dist/cjs/components/Stroke.js +7 -0
- package/dist/cjs/image/EditorImage.d.ts +2 -1
- package/dist/cjs/image/EditorImage.js +21 -6
- package/dist/cjs/toolbar/AbstractToolbar.js +9 -2
- package/dist/cjs/toolbar/IconProvider.d.ts +2 -1
- package/dist/cjs/toolbar/IconProvider.js +18 -8
- package/dist/cjs/toolbar/localization.d.ts +2 -0
- package/dist/cjs/toolbar/localization.js +2 -0
- package/dist/cjs/toolbar/widgets/BaseWidget.js +6 -1
- package/dist/cjs/toolbar/widgets/HandToolWidget.js +3 -3
- package/dist/cjs/toolbar/widgets/SelectionToolWidget.d.ts +7 -0
- package/dist/cjs/toolbar/widgets/SelectionToolWidget.js +109 -28
- package/dist/cjs/toolbar/widgets/components/makeButtonGrid.d.ts +17 -0
- package/dist/cjs/toolbar/widgets/components/makeButtonGrid.js +40 -0
- package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -3
- package/dist/cjs/tools/SelectionTool/Selection.js +30 -46
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.d.ts +17 -0
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.js +67 -0
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.d.ts +13 -0
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.js +33 -0
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.d.ts +15 -0
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.js +39 -0
- package/dist/cjs/tools/SelectionTool/SelectionMenuShortcut.d.ts +3 -1
- package/dist/cjs/tools/SelectionTool/SelectionMenuShortcut.js +13 -4
- package/dist/cjs/tools/SelectionTool/SelectionTool.d.ts +10 -2
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +68 -55
- package/dist/cjs/tools/SelectionTool/types.d.ts +4 -0
- package/dist/cjs/tools/SelectionTool/types.js +6 -1
- package/dist/cjs/tools/TextTool.js +5 -2
- package/dist/cjs/tools/lib.d.ts +1 -1
- package/dist/cjs/tools/lib.js +2 -1
- package/dist/cjs/util/ReactiveValue.js +2 -6
- package/dist/cjs/util/assertions.d.ts +7 -6
- package/dist/cjs/util/assertions.js +35 -29
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +0 -2
- package/dist/mjs/Editor.mjs +1 -1
- package/dist/mjs/bundle/bundled.mjs +2 -1
- package/dist/mjs/components/AbstractComponent.d.ts +15 -0
- package/dist/mjs/components/AbstractComponent.mjs +16 -0
- package/dist/mjs/components/Stroke.d.ts +1 -0
- package/dist/mjs/components/Stroke.mjs +7 -0
- package/dist/mjs/image/EditorImage.d.ts +2 -1
- package/dist/mjs/image/EditorImage.mjs +21 -6
- package/dist/mjs/toolbar/AbstractToolbar.mjs +9 -2
- package/dist/mjs/toolbar/IconProvider.d.ts +2 -1
- package/dist/mjs/toolbar/IconProvider.mjs +18 -8
- package/dist/mjs/toolbar/localization.d.ts +2 -0
- package/dist/mjs/toolbar/localization.mjs +2 -0
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +6 -1
- package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +3 -3
- package/dist/mjs/toolbar/widgets/SelectionToolWidget.d.ts +7 -0
- package/dist/mjs/toolbar/widgets/SelectionToolWidget.mjs +109 -28
- package/dist/mjs/toolbar/widgets/components/makeButtonGrid.d.ts +17 -0
- package/dist/mjs/toolbar/widgets/components/makeButtonGrid.mjs +35 -0
- package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -3
- package/dist/mjs/tools/SelectionTool/Selection.mjs +30 -46
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.d.ts +17 -0
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.mjs +61 -0
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.d.ts +13 -0
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.mjs +27 -0
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.d.ts +15 -0
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.mjs +36 -0
- package/dist/mjs/tools/SelectionTool/SelectionMenuShortcut.d.ts +3 -1
- package/dist/mjs/tools/SelectionTool/SelectionMenuShortcut.mjs +13 -4
- package/dist/mjs/tools/SelectionTool/SelectionTool.d.ts +10 -2
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +68 -55
- package/dist/mjs/tools/SelectionTool/types.d.ts +4 -0
- package/dist/mjs/tools/SelectionTool/types.mjs +5 -0
- package/dist/mjs/tools/TextTool.mjs +5 -2
- package/dist/mjs/tools/lib.d.ts +1 -1
- package/dist/mjs/tools/lib.mjs +1 -1
- package/dist/mjs/util/ReactiveValue.mjs +2 -6
- package/dist/mjs/util/assertions.d.ts +7 -6
- package/dist/mjs/util/assertions.mjs +28 -24
- package/dist/mjs/version.mjs +1 -1
- package/package.json +4 -4
- package/src/toolbar/EdgeToolbar.scss +6 -1
- package/src/toolbar/widgets/components/components.scss +1 -0
- package/src/toolbar/widgets/components/makeButtonGrid.scss +25 -0
- package/src/tools/SelectionTool/SelectionTool.scss +12 -1
- package/src/tools/util/createMenuOverlay.scss +5 -3
@@ -4,15 +4,17 @@ const math_1 = require("@js-draw/math");
|
|
4
4
|
const SelectionTool_1 = require("./SelectionTool");
|
5
5
|
const verticalOffset = 40;
|
6
6
|
class SelectionMenuShortcut {
|
7
|
-
constructor(parent, viewport, showContextMenu, localization) {
|
7
|
+
constructor(parent, viewport, icon, showContextMenu, localization) {
|
8
8
|
this.parent = parent;
|
9
9
|
this.viewport = viewport;
|
10
|
+
this.icon = icon;
|
10
11
|
this.localization = localization;
|
11
12
|
this.lastDragPointer = null;
|
12
13
|
this.element = document.createElement('div');
|
13
14
|
this.element.classList.add(`${SelectionTool_1.cssPrefix}handle`, `${SelectionTool_1.cssPrefix}selection-menu`);
|
14
15
|
this.element.style.setProperty('--vertical-offset', `${verticalOffset}px`);
|
15
16
|
this.onClick = () => {
|
17
|
+
this.button?.focus({ preventScroll: true });
|
16
18
|
const anchor = this.getBBoxCanvasCoords().center;
|
17
19
|
showContextMenu(anchor);
|
18
20
|
};
|
@@ -21,16 +23,22 @@ class SelectionMenuShortcut {
|
|
21
23
|
}
|
22
24
|
initUI() {
|
23
25
|
const button = document.createElement('button');
|
24
|
-
|
26
|
+
this.icon.classList.add('icon');
|
27
|
+
button.replaceChildren(this.icon);
|
25
28
|
button.ariaLabel = this.localization.selectionMenu__show;
|
26
29
|
button.title = button.ariaLabel;
|
30
|
+
this.button = button;
|
27
31
|
// To prevent editor event handlers from conflicting with those for the button,
|
28
32
|
// don't register a [click] handler. An onclick handler can be fired incorrectly
|
29
33
|
// in this case (in Chrome) after onClick is fired in onDragEnd, leading to a double
|
30
34
|
// on-click action.
|
31
35
|
button.onkeydown = (event) => {
|
32
|
-
if (event.key === 'Enter')
|
36
|
+
if (event.key === 'Enter') {
|
37
|
+
// .preventDefault prevents [Enter] from activating the first item in the
|
38
|
+
// selection menu.
|
39
|
+
event.preventDefault();
|
33
40
|
this.onClick();
|
41
|
+
}
|
34
42
|
};
|
35
43
|
this.element.appendChild(button);
|
36
44
|
// Update the bounding box of this in response to the new button.
|
@@ -60,7 +68,8 @@ class SelectionMenuShortcut {
|
|
60
68
|
const contentCanvasSize = this.getElementScreenSize().times(toCanvasScale);
|
61
69
|
const handleSizeCanvas = verticalOffset / this.viewport.getScaleFactor();
|
62
70
|
const topLeft = math_1.Vec2.of(parentCanvasRect.x, parentCanvasRect.y - handleSizeCanvas);
|
63
|
-
|
71
|
+
const minSize = math_1.Vec2.of(48, 48).times(toCanvasScale);
|
72
|
+
return new math_1.Rect2(topLeft.x, topLeft.y, contentCanvasSize.x, contentCanvasSize.y).grownToSize(minSize);
|
64
73
|
}
|
65
74
|
updatePosition() {
|
66
75
|
const bbox = this.getBBoxParentCoords();
|
@@ -3,13 +3,18 @@ import Editor from '../../Editor';
|
|
3
3
|
import { ContextMenuEvt, CopyEvent, KeyPressEvent, KeyUpEvent, PointerEvt } from '../../inputEvents';
|
4
4
|
import BaseTool from '../BaseTool';
|
5
5
|
import Selection from './Selection';
|
6
|
+
import { MutableReactiveValue } from '../../util/ReactiveValue';
|
7
|
+
import { SelectionMode } from './types';
|
6
8
|
export declare const cssPrefix = "selection-tool-";
|
9
|
+
export { SelectionMode };
|
7
10
|
export default class SelectionTool extends BaseTool {
|
8
11
|
private editor;
|
12
|
+
readonly modeValue: MutableReactiveValue<SelectionMode>;
|
13
|
+
private selectionBuilder;
|
9
14
|
private handleOverlay;
|
10
15
|
private prevSelectionBox;
|
11
16
|
private selectionBox;
|
12
|
-
private
|
17
|
+
private removeSelectionScheduled;
|
13
18
|
private startPoint;
|
14
19
|
private expandingSelectionBox;
|
15
20
|
private shiftKeyPressed;
|
@@ -17,8 +22,8 @@ export default class SelectionTool extends BaseTool {
|
|
17
22
|
private lastPointer;
|
18
23
|
private autoscroller;
|
19
24
|
constructor(editor: Editor, description: string);
|
25
|
+
private getSelectionColor;
|
20
26
|
private makeSelectionBox;
|
21
|
-
private snapSelectionToGrid;
|
22
27
|
private showContextMenu;
|
23
28
|
onContextMenu(event: ContextMenuEvt): boolean;
|
24
29
|
private selectionBoxHandlingEvt;
|
@@ -36,7 +41,10 @@ export default class SelectionTool extends BaseTool {
|
|
36
41
|
onCopy(event: CopyEvent): boolean;
|
37
42
|
setEnabled(enabled: boolean): void;
|
38
43
|
getSelection(): Selection | null;
|
44
|
+
/** @returns true if the selection is currently being created by the user. */
|
45
|
+
isSelecting(): boolean;
|
39
46
|
getSelectedObjects(): AbstractComponent[];
|
40
47
|
setSelection(objects: AbstractComponent[]): void;
|
48
|
+
private clearSelectionNoUpdateEvent;
|
41
49
|
clearSelection(): void;
|
42
50
|
}
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.cssPrefix = void 0;
|
6
|
+
exports.SelectionMode = exports.cssPrefix = void 0;
|
7
7
|
const math_1 = require("@js-draw/math");
|
8
8
|
const types_1 = require("../../types");
|
9
9
|
const Viewport_1 = __importDefault(require("../../Viewport"));
|
@@ -15,6 +15,11 @@ const TextComponent_1 = __importDefault(require("../../components/TextComponent"
|
|
15
15
|
const keybindings_1 = require("../keybindings");
|
16
16
|
const ToPointerAutoscroller_1 = __importDefault(require("./ToPointerAutoscroller"));
|
17
17
|
const showSelectionContextMenu_1 = __importDefault(require("./util/showSelectionContextMenu"));
|
18
|
+
const ReactiveValue_1 = require("../../util/ReactiveValue");
|
19
|
+
const types_2 = require("./types");
|
20
|
+
Object.defineProperty(exports, "SelectionMode", { enumerable: true, get: function () { return types_2.SelectionMode; } });
|
21
|
+
const LassoSelectionBuilder_1 = __importDefault(require("./SelectionBuilders/LassoSelectionBuilder"));
|
22
|
+
const RectSelectionBuilder_1 = __importDefault(require("./SelectionBuilders/RectSelectionBuilder"));
|
18
23
|
exports.cssPrefix = 'selection-tool-';
|
19
24
|
// Allows users to select/transform portions of the `EditorImage`.
|
20
25
|
// With respect to `extend`ing, `SelectionTool` is not stable.
|
@@ -25,7 +30,7 @@ class SelectionTool extends BaseTool_1.default {
|
|
25
30
|
// True if clearing and recreating the selectionBox has been deferred. This is used to prevent the selection
|
26
31
|
// from vanishing on pointerdown events that are intended to form other gestures (e.g. long press) that would
|
27
32
|
// ultimately restore the selection.
|
28
|
-
this.
|
33
|
+
this.removeSelectionScheduled = false;
|
29
34
|
this.startPoint = null; // canvas position
|
30
35
|
this.expandingSelectionBox = false;
|
31
36
|
this.shiftKeyPressed = false;
|
@@ -39,6 +44,13 @@ class SelectionTool extends BaseTool_1.default {
|
|
39
44
|
// Whether the last keypress corresponded to an action that didn't transform the
|
40
45
|
// selection (and thus does not need to be finalized on onKeyUp).
|
41
46
|
this.hasUnfinalizedTransformFromKeyPress = false;
|
47
|
+
this.modeValue = ReactiveValue_1.MutableReactiveValue.fromInitialValue(types_2.SelectionMode.Rectangle);
|
48
|
+
this.modeValue.onUpdate(() => {
|
49
|
+
this.editor.notifier.dispatch(types_1.EditorEventType.ToolUpdated, {
|
50
|
+
kind: types_1.EditorEventType.ToolUpdated,
|
51
|
+
tool: this,
|
52
|
+
});
|
53
|
+
});
|
42
54
|
this.autoscroller = new ToPointerAutoscroller_1.default(editor.viewport, (scrollBy) => {
|
43
55
|
editor.dispatch(Viewport_1.default.transformBy(math_1.Mat33.translation(scrollBy)), false);
|
44
56
|
// Update the selection box/content to match the new viewport.
|
@@ -67,26 +79,19 @@ class SelectionTool extends BaseTool_1.default {
|
|
67
79
|
this.editor.handleKeyEventsFrom(this.handleOverlay);
|
68
80
|
this.editor.handlePointerEventsFrom(this.handleOverlay);
|
69
81
|
}
|
70
|
-
|
82
|
+
getSelectionColor() {
|
83
|
+
const colorString = getComputedStyle(this.handleOverlay).getPropertyValue('--selection-background-color');
|
84
|
+
return math_1.Color4.fromString(colorString).withAlpha(0.5);
|
85
|
+
}
|
86
|
+
makeSelectionBox(selectedObjects) {
|
71
87
|
this.prevSelectionBox = this.selectionBox;
|
72
|
-
this.selectionBox = new Selection_1.default(
|
88
|
+
this.selectionBox = new Selection_1.default(selectedObjects, this.editor, this.showContextMenu);
|
73
89
|
if (!this.expandingSelectionBox) {
|
74
90
|
// Remove any previous selection rects
|
75
91
|
this.prevSelectionBox?.cancelSelection();
|
76
92
|
}
|
77
93
|
this.selectionBox.addTo(this.handleOverlay);
|
78
94
|
}
|
79
|
-
snapSelectionToGrid() {
|
80
|
-
if (!this.selectionBox)
|
81
|
-
throw new Error('No selection to snap!');
|
82
|
-
// Snap the top left corner of what we have selected.
|
83
|
-
const topLeftOfBBox = this.selectionBox.computeTightBoundingBox().topLeft;
|
84
|
-
const snappedTopLeft = this.editor.viewport.snapToGrid(topLeftOfBBox);
|
85
|
-
const snapDelta = snappedTopLeft.minus(topLeftOfBBox);
|
86
|
-
const oldTransform = this.selectionBox.getTransform();
|
87
|
-
this.selectionBox.setTransform(oldTransform.rightMul(math_1.Mat33.translation(snapDelta)));
|
88
|
-
this.selectionBox.finalizeTransform();
|
89
|
-
}
|
90
95
|
onContextMenu(event) {
|
91
96
|
const canShowSelectionMenu = this.selectionBox
|
92
97
|
?.getScreenRegion()
|
@@ -105,7 +110,7 @@ class SelectionTool extends BaseTool_1.default {
|
|
105
110
|
let transforming = false;
|
106
111
|
if (this.selectionBox) {
|
107
112
|
if (snapToGrid) {
|
108
|
-
this.
|
113
|
+
this.selectionBox.snapSelectedObjectsToGrid();
|
109
114
|
}
|
110
115
|
const dragStartResult = this.selectionBox.onDragStart(current);
|
111
116
|
if (dragStartResult) {
|
@@ -117,7 +122,13 @@ class SelectionTool extends BaseTool_1.default {
|
|
117
122
|
if (!transforming) {
|
118
123
|
// Shift key: Combine the new and old selection boxes at the end of the gesture.
|
119
124
|
this.expandingSelectionBox = this.shiftKeyPressed;
|
120
|
-
this.
|
125
|
+
this.removeSelectionScheduled = !this.expandingSelectionBox;
|
126
|
+
if (this.modeValue.get() === types_2.SelectionMode.Lasso) {
|
127
|
+
this.selectionBuilder = new LassoSelectionBuilder_1.default(current.canvasPos, this.editor.viewport);
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
this.selectionBuilder = new RectSelectionBuilder_1.default(current.canvasPos);
|
131
|
+
}
|
121
132
|
}
|
122
133
|
else {
|
123
134
|
// Only autoscroll if we're transforming an existing selection
|
@@ -132,13 +143,12 @@ class SelectionTool extends BaseTool_1.default {
|
|
132
143
|
}
|
133
144
|
onMainPointerUpdated(currentPointer) {
|
134
145
|
this.lastPointer = currentPointer;
|
135
|
-
if (this.
|
136
|
-
this.
|
137
|
-
this.
|
138
|
-
this.selectionBox
|
146
|
+
if (this.removeSelectionScheduled) {
|
147
|
+
this.removeSelectionScheduled = false;
|
148
|
+
this.handleOverlay.replaceChildren();
|
149
|
+
this.prevSelectionBox = this.selectionBox;
|
150
|
+
this.selectionBox = null;
|
139
151
|
}
|
140
|
-
if (!this.selectionBox)
|
141
|
-
return;
|
142
152
|
this.autoscroller.onPointerMove(currentPointer.screenPos);
|
143
153
|
if (!this.expandingSelectionBox && this.shiftKeyPressed && this.startPoint) {
|
144
154
|
const screenPos = this.editor.viewport.canvasToScreen(this.startPoint);
|
@@ -148,47 +158,46 @@ class SelectionTool extends BaseTool_1.default {
|
|
148
158
|
currentPointer = currentPointer.snappedToGrid(this.editor.viewport);
|
149
159
|
}
|
150
160
|
if (this.selectionBoxHandlingEvt) {
|
151
|
-
this.selectionBox
|
161
|
+
this.selectionBox?.onDragUpdate(currentPointer);
|
152
162
|
}
|
153
163
|
else {
|
154
|
-
this.
|
164
|
+
this.selectionBuilder?.onPointerMove(currentPointer.canvasPos);
|
165
|
+
this.editor.clearWetInk();
|
166
|
+
this.selectionBuilder?.render(this.editor.display.getWetInkRenderer(), this.getSelectionColor());
|
155
167
|
}
|
156
168
|
}
|
157
169
|
onPointerUp(event) {
|
158
170
|
this.onMainPointerUpdated(event.current);
|
159
171
|
this.autoscroller.stop();
|
160
|
-
if (
|
161
|
-
|
162
|
-
this.selectionBox.setHandlesVisible(true);
|
163
|
-
// Were we expanding the previous selection?
|
164
|
-
if (this.expandingSelectionBox && this.prevSelectionBox) {
|
165
|
-
// If so, finish expanding.
|
166
|
-
this.expandingSelectionBox = false;
|
167
|
-
this.selectionBox.resolveToObjects();
|
168
|
-
this.setSelection([
|
169
|
-
...this.selectionBox.getSelectedObjects(),
|
170
|
-
...this.prevSelectionBox.getSelectedObjects(),
|
171
|
-
]);
|
172
|
+
if (this.selectionBoxHandlingEvt) {
|
173
|
+
this.selectionBox?.onDragEnd();
|
172
174
|
}
|
173
|
-
else {
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
175
|
+
else if (this.selectionBuilder) {
|
176
|
+
const newSelection = this.selectionBuilder.resolve(this.editor.image, this.editor.viewport);
|
177
|
+
this.selectionBuilder = null;
|
178
|
+
this.editor.clearWetInk();
|
179
|
+
if (this.expandingSelectionBox && this.selectionBox) {
|
180
|
+
this.setSelection([...this.selectionBox.getSelectedObjects(), ...newSelection]);
|
178
181
|
}
|
179
182
|
else {
|
180
|
-
this.
|
183
|
+
this.setSelection(newSelection);
|
181
184
|
}
|
182
|
-
this.selectionBoxHandlingEvt = false;
|
183
|
-
this.lastPointer = null;
|
184
185
|
}
|
186
|
+
this.expandingSelectionBox = false;
|
187
|
+
this.removeSelectionScheduled = false;
|
188
|
+
this.selectionBoxHandlingEvt = false;
|
189
|
+
this.lastPointer = null;
|
185
190
|
}
|
186
191
|
onGestureCancel() {
|
192
|
+
if (this.selectionBuilder) {
|
193
|
+
this.selectionBuilder = null;
|
194
|
+
this.editor.clearWetInk();
|
195
|
+
}
|
187
196
|
this.autoscroller.stop();
|
188
197
|
if (this.selectionBoxHandlingEvt) {
|
189
198
|
this.selectionBox?.onDragCancel();
|
190
199
|
}
|
191
|
-
else if (!this.
|
200
|
+
else if (!this.removeSelectionScheduled) {
|
192
201
|
// Revert to the previous selection, if any.
|
193
202
|
this.selectionBox?.cancelSelection();
|
194
203
|
this.selectionBox = this.prevSelectionBox;
|
@@ -196,7 +205,7 @@ class SelectionTool extends BaseTool_1.default {
|
|
196
205
|
this.selectionBox?.recomputeRegion();
|
197
206
|
this.prevSelectionBox = null;
|
198
207
|
}
|
199
|
-
this.
|
208
|
+
this.removeSelectionScheduled = false;
|
200
209
|
this.expandingSelectionBox = false;
|
201
210
|
this.lastPointer = null;
|
202
211
|
this.selectionBoxHandlingEvt = false;
|
@@ -464,6 +473,10 @@ class SelectionTool extends BaseTool_1.default {
|
|
464
473
|
getSelection() {
|
465
474
|
return this.selectionBox;
|
466
475
|
}
|
476
|
+
/** @returns true if the selection is currently being created by the user. */
|
477
|
+
isSelecting() {
|
478
|
+
return !!this.selectionBuilder;
|
479
|
+
}
|
467
480
|
getSelectedObjects() {
|
468
481
|
return this.selectionBox?.getSelectedObjects() ?? [];
|
469
482
|
}
|
@@ -489,20 +502,20 @@ class SelectionTool extends BaseTool_1.default {
|
|
489
502
|
bbox = object.getBBox();
|
490
503
|
}
|
491
504
|
}
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
this.clearSelection();
|
496
|
-
if (!this.selectionBox) {
|
497
|
-
this.makeSelectionBox(bbox.topLeft);
|
505
|
+
this.clearSelectionNoUpdateEvent();
|
506
|
+
if (bbox) {
|
507
|
+
this.makeSelectionBox(objects);
|
498
508
|
}
|
499
|
-
this.selectionBox.setSelectedObjects(objects, bbox);
|
500
509
|
this.onSelectionUpdated();
|
501
510
|
}
|
502
|
-
clearSelection
|
511
|
+
// Equivalent to .clearSelection, but does not dispatch an update event
|
512
|
+
clearSelectionNoUpdateEvent() {
|
503
513
|
this.handleOverlay.replaceChildren();
|
504
514
|
this.prevSelectionBox = this.selectionBox;
|
505
515
|
this.selectionBox = null;
|
516
|
+
}
|
517
|
+
clearSelection() {
|
518
|
+
this.clearSelectionNoUpdateEvent();
|
506
519
|
this.onSelectionUpdated();
|
507
520
|
}
|
508
521
|
}
|
@@ -1,6 +1,11 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.TransformMode = exports.ResizeMode = void 0;
|
3
|
+
exports.TransformMode = exports.ResizeMode = exports.SelectionMode = void 0;
|
4
|
+
var SelectionMode;
|
5
|
+
(function (SelectionMode) {
|
6
|
+
SelectionMode["Lasso"] = "lasso";
|
7
|
+
SelectionMode["Rectangle"] = "rect";
|
8
|
+
})(SelectionMode || (exports.SelectionMode = SelectionMode = {}));
|
4
9
|
var ResizeMode;
|
5
10
|
(function (ResizeMode) {
|
6
11
|
ResizeMode[ResizeMode["Both"] = 0] = "Both";
|
@@ -163,12 +163,15 @@ class TextTool extends BaseTool_1.default {
|
|
163
163
|
}
|
164
164
|
};
|
165
165
|
this.textInputElem.onblur = () => {
|
166
|
+
const input = this.textInputElem;
|
166
167
|
// Delay removing the input -- flushInput may be called within a blur()
|
167
168
|
// event handler
|
168
169
|
const removeInput = false;
|
169
|
-
const input = this.textInputElem;
|
170
170
|
this.flushInput(removeInput);
|
171
171
|
this.textInputElem = null;
|
172
|
+
if (input) {
|
173
|
+
input.classList.add('-hiding');
|
174
|
+
}
|
172
175
|
setTimeout(() => {
|
173
176
|
input?.remove();
|
174
177
|
}, 0);
|
@@ -208,7 +211,7 @@ class TextTool extends BaseTool_1.default {
|
|
208
211
|
if (allPointers.length === 1) {
|
209
212
|
// Are we clicking on a text node?
|
210
213
|
const canvasPos = current.canvasPos;
|
211
|
-
const halfTestRegionSize = math_1.Vec2.of(
|
214
|
+
const halfTestRegionSize = math_1.Vec2.of(4, 4).times(this.editor.viewport.getSizeOfPixelOnCanvas());
|
212
215
|
const testRegion = math_1.Rect2.fromCorners(canvasPos.minus(halfTestRegionSize), canvasPos.plus(halfTestRegionSize));
|
213
216
|
const targetNodes = this.editor.image.getElementsIntersectingRegion(testRegion);
|
214
217
|
let targetTextNodes = targetNodes.filter((node) => node instanceof TextComponent_1.default);
|
package/dist/cjs/tools/lib.d.ts
CHANGED
@@ -7,7 +7,7 @@ export { default as ToolSwitcherShortcut } from './ToolSwitcherShortcut';
|
|
7
7
|
export { default as PanZoomTool, PanZoomMode } from './PanZoom';
|
8
8
|
export { default as PenTool, PenStyle } from './Pen';
|
9
9
|
export { default as TextTool } from './TextTool';
|
10
|
-
export { default as SelectionTool } from './SelectionTool/SelectionTool';
|
10
|
+
export { default as SelectionTool, SelectionMode } from './SelectionTool/SelectionTool';
|
11
11
|
export { default as SelectAllShortcutHandler } from './SelectionTool/SelectAllShortcutHandler';
|
12
12
|
export { default as EraserTool, EraserMode } from './Eraser';
|
13
13
|
export { default as PasteHandler } from './PasteHandler';
|
package/dist/cjs/tools/lib.js
CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.ToolbarShortcutHandler = exports.SoundUITool = exports.PasteHandler = exports.EraserMode = exports.EraserTool = exports.SelectAllShortcutHandler = exports.SelectionTool = exports.TextTool = exports.PenTool = exports.PanZoomMode = exports.PanZoomTool = exports.ToolSwitcherShortcut = exports.UndoRedoShortcut = exports.ToolEnabledGroup = exports.ToolController = exports.BaseTool = exports.InputMapper = void 0;
|
6
|
+
exports.ToolbarShortcutHandler = exports.SoundUITool = exports.PasteHandler = exports.EraserMode = exports.EraserTool = exports.SelectAllShortcutHandler = exports.SelectionMode = exports.SelectionTool = exports.TextTool = exports.PenTool = exports.PanZoomMode = exports.PanZoomTool = exports.ToolSwitcherShortcut = exports.UndoRedoShortcut = exports.ToolEnabledGroup = exports.ToolController = exports.BaseTool = exports.InputMapper = void 0;
|
7
7
|
var InputMapper_1 = require("./InputFilter/InputMapper");
|
8
8
|
Object.defineProperty(exports, "InputMapper", { enumerable: true, get: function () { return __importDefault(InputMapper_1).default; } });
|
9
9
|
var BaseTool_1 = require("./BaseTool");
|
@@ -25,6 +25,7 @@ var TextTool_1 = require("./TextTool");
|
|
25
25
|
Object.defineProperty(exports, "TextTool", { enumerable: true, get: function () { return __importDefault(TextTool_1).default; } });
|
26
26
|
var SelectionTool_1 = require("./SelectionTool/SelectionTool");
|
27
27
|
Object.defineProperty(exports, "SelectionTool", { enumerable: true, get: function () { return __importDefault(SelectionTool_1).default; } });
|
28
|
+
Object.defineProperty(exports, "SelectionMode", { enumerable: true, get: function () { return SelectionTool_1.SelectionMode; } });
|
28
29
|
var SelectAllShortcutHandler_1 = require("./SelectionTool/SelectAllShortcutHandler");
|
29
30
|
Object.defineProperty(exports, "SelectAllShortcutHandler", { enumerable: true, get: function () { return __importDefault(SelectAllShortcutHandler_1).default; } });
|
30
31
|
var Eraser_1 = require("./Eraser");
|
@@ -72,9 +72,7 @@ class ReactiveValue {
|
|
72
72
|
*/
|
73
73
|
static fromCallback(callback, sourceValues) {
|
74
74
|
const result = new ReactiveValueImpl(callback());
|
75
|
-
const resultRef =
|
76
|
-
? new window.WeakRef(result)
|
77
|
-
: { deref: () => result };
|
75
|
+
const resultRef = typeof WeakRef !== 'undefined' ? new WeakRef(result) : { deref: () => result };
|
78
76
|
for (const value of sourceValues) {
|
79
77
|
const listener = value.onUpdate(() => {
|
80
78
|
// Use resultRef to allow `result` to be garbage collected
|
@@ -118,9 +116,7 @@ exports.ReactiveValue = ReactiveValue;
|
|
118
116
|
class MutableReactiveValue extends ReactiveValue {
|
119
117
|
static fromProperty(sourceValue, propertyName) {
|
120
118
|
const child = ReactiveValue.fromInitialValue(sourceValue.get()[propertyName]);
|
121
|
-
const childRef =
|
122
|
-
? new window.WeakRef(child)
|
123
|
-
: { deref: () => child };
|
119
|
+
const childRef = typeof WeakRef !== 'undefined' ? new WeakRef(child) : { deref: () => child };
|
124
120
|
// When the source is updated...
|
125
121
|
const sourceListener = sourceValue.onUpdate((newValue) => {
|
126
122
|
const childValue = childRef.deref();
|
@@ -2,7 +2,7 @@
|
|
2
2
|
* Compile-time assertion that a branch of code is unreachable.
|
3
3
|
* @internal
|
4
4
|
*/
|
5
|
-
export declare
|
5
|
+
export declare function assertUnreachable(key: never): never;
|
6
6
|
/**
|
7
7
|
* Throws an exception if the typeof given value is not a number or `value` is NaN.
|
8
8
|
*
|
@@ -13,15 +13,16 @@ export declare const assertUnreachable: (key: never) => never;
|
|
13
13
|
*
|
14
14
|
* assertIsNumber('hello, world'); // throws an Error.
|
15
15
|
* ```
|
16
|
-
*
|
17
|
-
*
|
18
16
|
*/
|
19
|
-
export declare
|
17
|
+
export declare function assertIsNumber(value: unknown, allowNaN?: boolean): asserts value is number;
|
18
|
+
export declare function assertIsArray(values: unknown): asserts values is unknown[];
|
20
19
|
/**
|
21
20
|
* Throws if any of `values` is not of type number.
|
22
21
|
*/
|
23
|
-
export declare
|
22
|
+
export declare function assertIsNumberArray(values: unknown, allowNaN?: boolean): asserts values is number[];
|
24
23
|
/**
|
25
24
|
* Throws an exception if `typeof value` is not a boolean.
|
26
25
|
*/
|
27
|
-
export declare
|
26
|
+
export declare function assertIsBoolean(value: unknown): asserts value is boolean;
|
27
|
+
export declare function assertTruthy<T>(value: T | null | undefined | false | 0): asserts value is T;
|
28
|
+
export declare function assertIsObject(value: unknown): asserts value is Record<string, unknown>;
|
@@ -1,15 +1,22 @@
|
|
1
1
|
"use strict";
|
2
|
+
// Note: Arrow functions cannot be used for type assertions. See
|
3
|
+
// https://github.com/microsoft/TypeScript/issues/34523
|
2
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.
|
5
|
+
exports.assertUnreachable = assertUnreachable;
|
6
|
+
exports.assertIsNumber = assertIsNumber;
|
7
|
+
exports.assertIsArray = assertIsArray;
|
8
|
+
exports.assertIsNumberArray = assertIsNumberArray;
|
9
|
+
exports.assertIsBoolean = assertIsBoolean;
|
10
|
+
exports.assertTruthy = assertTruthy;
|
11
|
+
exports.assertIsObject = assertIsObject;
|
4
12
|
/**
|
5
13
|
* Compile-time assertion that a branch of code is unreachable.
|
6
14
|
* @internal
|
7
15
|
*/
|
8
|
-
|
16
|
+
function assertUnreachable(key) {
|
9
17
|
// See https://stackoverflow.com/a/39419171/17055750
|
10
18
|
throw new Error(`Should be unreachable. Key: ${key}.`);
|
11
|
-
}
|
12
|
-
exports.assertUnreachable = assertUnreachable;
|
19
|
+
}
|
13
20
|
/**
|
14
21
|
* Throws an exception if the typeof given value is not a number or `value` is NaN.
|
15
22
|
*
|
@@ -20,43 +27,42 @@ exports.assertUnreachable = assertUnreachable;
|
|
20
27
|
*
|
21
28
|
* assertIsNumber('hello, world'); // throws an Error.
|
22
29
|
* ```
|
23
|
-
*
|
24
|
-
*
|
25
30
|
*/
|
26
|
-
|
31
|
+
function assertIsNumber(value, allowNaN = false) {
|
27
32
|
if (typeof value !== 'number' || (!allowNaN && isNaN(value))) {
|
28
33
|
throw new Error('Given value is not a number');
|
29
|
-
// return false;
|
30
34
|
}
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
}
|
36
|
+
function assertIsArray(values) {
|
37
|
+
if (!Array.isArray(values)) {
|
38
|
+
throw new Error('Asserting isArray: Given entity is not an array');
|
39
|
+
}
|
40
|
+
}
|
34
41
|
/**
|
35
42
|
* Throws if any of `values` is not of type number.
|
36
43
|
*/
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
}
|
41
|
-
if (!(0, exports.assertIsNumber)(values['length'])) {
|
42
|
-
return false;
|
43
|
-
}
|
44
|
+
function assertIsNumberArray(values, allowNaN = false) {
|
45
|
+
assertIsArray(values);
|
46
|
+
assertIsNumber(values.length);
|
44
47
|
for (const value of values) {
|
45
|
-
|
46
|
-
return false;
|
47
|
-
}
|
48
|
+
assertIsNumber(value, allowNaN);
|
48
49
|
}
|
49
|
-
|
50
|
-
};
|
51
|
-
exports.assertIsNumberArray = assertIsNumberArray;
|
50
|
+
}
|
52
51
|
/**
|
53
52
|
* Throws an exception if `typeof value` is not a boolean.
|
54
53
|
*/
|
55
|
-
|
54
|
+
function assertIsBoolean(value) {
|
56
55
|
if (typeof value !== 'boolean') {
|
57
56
|
throw new Error('Given value is not a boolean');
|
58
|
-
// return false;
|
59
57
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
}
|
59
|
+
function assertTruthy(value) {
|
60
|
+
if (!value) {
|
61
|
+
throw new Error(`${JSON.stringify(value)} is not truthy`);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
function assertIsObject(value) {
|
65
|
+
if (typeof value !== 'object') {
|
66
|
+
throw new Error(`AssertIsObject: Given entity is not an object (type = ${typeof value})`);
|
67
|
+
}
|
68
|
+
}
|
package/dist/cjs/version.js
CHANGED
package/dist/mjs/Editor.d.ts
CHANGED
@@ -116,8 +116,6 @@ export interface EditorSettings {
|
|
116
116
|
*
|
117
117
|
* If not given, the default file picker shown by a [file input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)
|
118
118
|
* is shown.
|
119
|
-
*
|
120
|
-
* @beta -- API may change between minor releases.
|
121
119
|
*/
|
122
120
|
showImagePicker?: ShowCustomFilePickerCallback;
|
123
121
|
} | null;
|
package/dist/mjs/Editor.mjs
CHANGED
@@ -4,6 +4,7 @@ import { LineSegment2, Mat33, Path, Rect2 } from '@js-draw/math';
|
|
4
4
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
5
5
|
import { ImageComponentLocalization } from './localization';
|
6
6
|
import Viewport from '../Viewport';
|
7
|
+
import { Point2 } from '@js-draw/math';
|
7
8
|
export type LoadSaveData = string[] | Record<symbol, string | number>;
|
8
9
|
export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
|
9
10
|
export type DeserializeCallback = (data: string) => AbstractComponent;
|
@@ -27,6 +28,12 @@ export declare enum ComponentSizingMode {
|
|
27
28
|
}
|
28
29
|
/**
|
29
30
|
* A base class for everything that can be added to an {@link EditorImage}.
|
31
|
+
*
|
32
|
+
* In addition to the `abstract` methods, there are a few methods that should be
|
33
|
+
* overridden when creating a selectable/erasable subclass:
|
34
|
+
* - {@link keyPoints}: Overriding this may improve how the component interacts with the selection tool.
|
35
|
+
* - {@link withRegionErased}: Override/implement this to allow the component to be partially erased
|
36
|
+
* by the partial stroke eraser.
|
30
37
|
*/
|
31
38
|
export default abstract class AbstractComponent {
|
32
39
|
private readonly componentKind;
|
@@ -113,6 +120,14 @@ export default abstract class AbstractComponent {
|
|
113
120
|
* this function.
|
114
121
|
*/
|
115
122
|
intersectsRect(rect: Rect2): boolean;
|
123
|
+
/**
|
124
|
+
* Returns a selection of points within this object. Each contiguous section
|
125
|
+
* of this object should have a point in the returned array.
|
126
|
+
*
|
127
|
+
* Subclasses should override this method if the center of the bounding box is
|
128
|
+
* not contained within the object.
|
129
|
+
*/
|
130
|
+
keyPoints(): Point2[];
|
116
131
|
isSelectable(): boolean;
|
117
132
|
isBackground(): boolean;
|
118
133
|
getProportionalRenderingTime(): number;
|