js-draw 1.27.1 → 1.28.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/README.md +1 -1
- package/build-config.json +2 -1
- package/dist/Editor.css +1 -1
- package/dist/bundle.js +28 -28
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +7 -2
- package/dist/cjs/Editor.js +11 -5
- package/dist/cjs/SVGLoader/SVGLoader.d.ts +21 -0
- package/dist/cjs/SVGLoader/SVGLoader.js +74 -47
- package/dist/cjs/SVGLoader/SVGLoader.plugins.test.d.ts +1 -0
- package/dist/cjs/Viewport.js +2 -32
- package/dist/cjs/commands/Duplicate.d.ts +7 -4
- package/dist/cjs/commands/Duplicate.js +48 -7
- package/dist/cjs/commands/Duplicate.test.d.ts +1 -0
- package/dist/cjs/commands/Erase.d.ts +1 -1
- package/dist/cjs/commands/Erase.js +2 -2
- package/dist/cjs/commands/localization.d.ts +2 -2
- package/dist/cjs/commands/localization.js +2 -2
- package/dist/cjs/components/AbstractComponent.d.ts +7 -0
- package/dist/cjs/components/AbstractComponent.js +16 -2
- package/dist/cjs/components/Stroke.d.ts +21 -1
- package/dist/cjs/components/Stroke.js +29 -0
- package/dist/cjs/components/TextComponent.d.ts +2 -2
- package/dist/cjs/components/TextComponent.js +2 -2
- package/dist/cjs/components/builders/PolylineBuilder.js +1 -1
- package/dist/cjs/image/EditorImage.d.ts +17 -9
- package/dist/cjs/image/EditorImage.js +33 -17
- package/dist/cjs/lib.d.ts +1 -1
- package/dist/cjs/localizations/de.js +2 -2
- package/dist/cjs/rendering/RenderingStyle.d.ts +7 -6
- package/dist/cjs/rendering/lib.d.ts +1 -1
- package/dist/cjs/rendering/renderers/AbstractRenderer.js +4 -0
- package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +9 -0
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +14 -0
- package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +18 -0
- package/dist/cjs/rendering/renderers/SVGRenderer.js +21 -1
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +2 -2
- package/dist/cjs/toolbar/AbstractToolbar.js +2 -3
- package/dist/cjs/toolbar/DropdownToolbar.d.ts +1 -1
- package/dist/cjs/toolbar/DropdownToolbar.js +2 -3
- package/dist/cjs/toolbar/DropdownToolbar.test.d.ts +1 -0
- package/dist/cjs/toolbar/utils/HelpDisplay.js +6 -4
- package/dist/cjs/toolbar/utils/localization.d.ts +1 -0
- package/dist/cjs/toolbar/utils/localization.js +1 -0
- package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +1 -1
- package/dist/cjs/toolbar/widgets/InsertImageWidget/InsertImageWidget.js +1 -1
- package/dist/cjs/toolbar/widgets/components/makeGridSelector.js +1 -1
- package/dist/cjs/tools/Eraser.js +3 -3
- package/dist/cjs/tools/FindTool.js +1 -1
- package/dist/cjs/tools/PasteHandler.js +4 -1
- package/dist/cjs/tools/Pen.js +1 -1
- package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.js +1 -1
- package/dist/cjs/tools/SelectionTool/Selection.js +23 -10
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.js +1 -1
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.js +1 -1
- package/dist/cjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.js +1 -1
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +3 -2
- package/dist/cjs/tools/SoundUITool.js +1 -1
- package/dist/cjs/tools/TextTool.js +2 -2
- package/dist/cjs/util/assertions.d.ts +6 -0
- package/dist/cjs/util/assertions.js +18 -0
- package/dist/cjs/util/describeTransformation.d.ts +12 -0
- package/dist/cjs/util/describeTransformation.js +44 -0
- package/dist/cjs/version.js +2 -1
- package/dist/mjs/Editor.d.ts +7 -2
- package/dist/mjs/Editor.mjs +11 -5
- package/dist/mjs/SVGLoader/SVGLoader.d.ts +21 -0
- package/dist/mjs/SVGLoader/SVGLoader.mjs +74 -47
- package/dist/mjs/SVGLoader/SVGLoader.plugins.test.d.ts +1 -0
- package/dist/mjs/Viewport.mjs +2 -32
- package/dist/mjs/commands/Duplicate.d.ts +7 -4
- package/dist/mjs/commands/Duplicate.mjs +48 -7
- package/dist/mjs/commands/Duplicate.test.d.ts +1 -0
- package/dist/mjs/commands/Erase.d.ts +1 -1
- package/dist/mjs/commands/Erase.mjs +2 -2
- package/dist/mjs/commands/localization.d.ts +2 -2
- package/dist/mjs/commands/localization.mjs +2 -2
- package/dist/mjs/components/AbstractComponent.d.ts +7 -0
- package/dist/mjs/components/AbstractComponent.mjs +17 -3
- package/dist/mjs/components/Stroke.d.ts +21 -1
- package/dist/mjs/components/Stroke.mjs +31 -2
- package/dist/mjs/components/TextComponent.d.ts +2 -2
- package/dist/mjs/components/TextComponent.mjs +2 -2
- package/dist/mjs/components/builders/PolylineBuilder.mjs +1 -1
- package/dist/mjs/image/EditorImage.d.ts +17 -9
- package/dist/mjs/image/EditorImage.mjs +33 -17
- package/dist/mjs/lib.d.ts +1 -1
- package/dist/mjs/localizations/de.mjs +2 -2
- package/dist/mjs/rendering/RenderingStyle.d.ts +7 -6
- package/dist/mjs/rendering/lib.d.ts +1 -1
- package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +4 -0
- package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +9 -0
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +14 -0
- package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +18 -0
- package/dist/mjs/rendering/renderers/SVGRenderer.mjs +21 -1
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +2 -2
- package/dist/mjs/toolbar/AbstractToolbar.mjs +2 -3
- package/dist/mjs/toolbar/DropdownToolbar.d.ts +1 -1
- package/dist/mjs/toolbar/DropdownToolbar.mjs +2 -3
- package/dist/mjs/toolbar/DropdownToolbar.test.d.ts +1 -0
- package/dist/mjs/toolbar/utils/HelpDisplay.mjs +6 -4
- package/dist/mjs/toolbar/utils/localization.d.ts +1 -0
- package/dist/mjs/toolbar/utils/localization.mjs +1 -0
- package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +1 -1
- package/dist/mjs/toolbar/widgets/InsertImageWidget/InsertImageWidget.mjs +1 -1
- package/dist/mjs/toolbar/widgets/components/makeGridSelector.mjs +1 -1
- package/dist/mjs/tools/Eraser.mjs +3 -3
- package/dist/mjs/tools/FindTool.mjs +1 -1
- package/dist/mjs/tools/PasteHandler.mjs +4 -1
- package/dist/mjs/tools/Pen.mjs +1 -1
- package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.mjs +1 -1
- package/dist/mjs/tools/SelectionTool/Selection.mjs +23 -10
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.mjs +1 -1
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.mjs +1 -1
- package/dist/mjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.mjs +1 -1
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +3 -2
- package/dist/mjs/tools/SoundUITool.mjs +1 -1
- package/dist/mjs/tools/TextTool.mjs +2 -2
- package/dist/mjs/util/assertions.d.ts +6 -0
- package/dist/mjs/util/assertions.mjs +16 -0
- package/dist/mjs/util/describeTransformation.d.ts +12 -0
- package/dist/mjs/util/describeTransformation.mjs +42 -0
- package/dist/mjs/version.mjs +2 -1
- package/package.json +4 -4
- package/src/toolbar/utils/HelpDisplay.scss +7 -1
@@ -213,6 +213,20 @@ class CanvasRenderer extends AbstractRenderer_1.default {
|
|
213
213
|
this.ignoringObject = false;
|
214
214
|
}
|
215
215
|
}
|
216
|
+
/**
|
217
|
+
* Returns a reference to the underlying `CanvasRenderingContext2D`.
|
218
|
+
* This can be used to render custom content not supported by {@link AbstractRenderer}.
|
219
|
+
* However, such content won't support {@link SVGRenderer} or {@link TextOnlyRenderer}
|
220
|
+
* by default.
|
221
|
+
*
|
222
|
+
* Use with caution.
|
223
|
+
*/
|
224
|
+
drawWithRawRenderingContext(callback) {
|
225
|
+
this.ctx.save();
|
226
|
+
this.transformBy(this.getCanvasToScreenTransform());
|
227
|
+
callback(this.ctx);
|
228
|
+
this.ctx.restore();
|
229
|
+
}
|
216
230
|
// @internal
|
217
231
|
drawPoints(...points) {
|
218
232
|
const pointRadius = 10;
|
@@ -15,6 +15,9 @@ type FromViewportOptions = {
|
|
15
15
|
*/
|
16
16
|
useViewBoxForPositioning?: boolean;
|
17
17
|
};
|
18
|
+
type DrawWithSVGParentContext = {
|
19
|
+
sanitize: boolean;
|
20
|
+
};
|
18
21
|
/**
|
19
22
|
* Renders onto an `SVGElement`.
|
20
23
|
*
|
@@ -57,7 +60,22 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
57
60
|
protected traceCubicBezierCurve(_controlPoint1: Point2, _controlPoint2: Point2, _endPoint: Point2): void;
|
58
61
|
protected traceQuadraticBezierCurve(_controlPoint: Point2, _endPoint: Point2): void;
|
59
62
|
drawPoints(...points: Point2[]): void;
|
63
|
+
/**
|
64
|
+
* Adds a **copy** of the given element directly to the container
|
65
|
+
* SVG element, **without applying transforms**.
|
66
|
+
*
|
67
|
+
* If `sanitize` is enabled, this does nothing.
|
68
|
+
*/
|
60
69
|
drawSVGElem(elem: SVGElement): void;
|
70
|
+
/**
|
71
|
+
* Allows rendering directly to the underlying SVG element. Rendered
|
72
|
+
* content is added to a `<g>` element that's passed as `parent` to `callback`.
|
73
|
+
*
|
74
|
+
* **Note**: Unlike {@link drawSVGElem}, this method can be used even if `sanitize` is `true`.
|
75
|
+
* In this case, it's the responsibility of `callback` to ensure that everything added
|
76
|
+
* to `parent` is safe to render.
|
77
|
+
*/
|
78
|
+
drawWithSVGParent(callback: (parent: SVGGElement, context: DrawWithSVGParentContext) => void): void;
|
61
79
|
isTooSmallToRender(_rect: Rect2): boolean;
|
62
80
|
/**
|
63
81
|
* Creates a new SVG element and `SVGRenerer` with `width`, `height`, `viewBox`,
|
@@ -347,7 +347,12 @@ class SVGRenderer extends AbstractRenderer_1.default {
|
|
347
347
|
this.elem.appendChild(elem);
|
348
348
|
});
|
349
349
|
}
|
350
|
-
|
350
|
+
/**
|
351
|
+
* Adds a **copy** of the given element directly to the container
|
352
|
+
* SVG element, **without applying transforms**.
|
353
|
+
*
|
354
|
+
* If `sanitize` is enabled, this does nothing.
|
355
|
+
*/
|
351
356
|
drawSVGElem(elem) {
|
352
357
|
if (this.sanitize) {
|
353
358
|
return;
|
@@ -361,6 +366,21 @@ class SVGRenderer extends AbstractRenderer_1.default {
|
|
361
366
|
this.elem.appendChild(elemToDraw);
|
362
367
|
this.objectElems?.push(elemToDraw);
|
363
368
|
}
|
369
|
+
/**
|
370
|
+
* Allows rendering directly to the underlying SVG element. Rendered
|
371
|
+
* content is added to a `<g>` element that's passed as `parent` to `callback`.
|
372
|
+
*
|
373
|
+
* **Note**: Unlike {@link drawSVGElem}, this method can be used even if `sanitize` is `true`.
|
374
|
+
* In this case, it's the responsibility of `callback` to ensure that everything added
|
375
|
+
* to `parent` is safe to render.
|
376
|
+
*/
|
377
|
+
drawWithSVGParent(callback) {
|
378
|
+
const parent = document.createElementNS(svgNameSpace, 'g');
|
379
|
+
this.transformFrom(math_1.Mat33.identity, parent, true);
|
380
|
+
callback(parent, { sanitize: this.sanitize });
|
381
|
+
this.elem.appendChild(parent);
|
382
|
+
this.objectElems?.push(parent);
|
383
|
+
}
|
364
384
|
isTooSmallToRender(_rect) {
|
365
385
|
return false;
|
366
386
|
}
|
@@ -21,10 +21,10 @@ export type ToolbarActionButtonOptions = {
|
|
21
21
|
export default abstract class AbstractToolbar {
|
22
22
|
#private;
|
23
23
|
protected editor: Editor;
|
24
|
-
protected localizationTable: ToolbarLocalization;
|
25
24
|
private static colorisStarted;
|
25
|
+
protected localizationTable: ToolbarLocalization;
|
26
26
|
/** @internal */
|
27
|
-
constructor(editor: Editor, localizationTable
|
27
|
+
constructor(editor: Editor, localizationTable: ToolbarLocalization);
|
28
28
|
private closeColorPickerOverlay;
|
29
29
|
private setupCloseColorPickerOverlay;
|
30
30
|
setupColorPickers(): void;
|
@@ -17,7 +17,6 @@ var _AbstractToolbar_listeners, _AbstractToolbar_widgetsById, _AbstractToolbar_w
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
18
18
|
const types_1 = require("../types");
|
19
19
|
const coloris_1 = require("@melloware/coloris");
|
20
|
-
const localization_1 = require("./localization");
|
21
20
|
const SelectionTool_1 = __importDefault(require("../tools/SelectionTool/SelectionTool"));
|
22
21
|
const PanZoom_1 = __importDefault(require("../tools/PanZoom"));
|
23
22
|
const TextTool_1 = __importDefault(require("../tools/TextTool"));
|
@@ -44,14 +43,14 @@ const assertions_1 = require("../util/assertions");
|
|
44
43
|
*/
|
45
44
|
class AbstractToolbar {
|
46
45
|
/** @internal */
|
47
|
-
constructor(editor, localizationTable
|
46
|
+
constructor(editor, localizationTable) {
|
48
47
|
this.editor = editor;
|
49
|
-
this.localizationTable = localizationTable;
|
50
48
|
_AbstractToolbar_listeners.set(this, []);
|
51
49
|
_AbstractToolbar_widgetsById.set(this, {});
|
52
50
|
_AbstractToolbar_widgetList.set(this, []);
|
53
51
|
_AbstractToolbar_updateColoris.set(this, null);
|
54
52
|
this.closeColorPickerOverlay = null;
|
53
|
+
this.localizationTable = localizationTable ?? editor.localization;
|
55
54
|
if (!AbstractToolbar.colorisStarted) {
|
56
55
|
(0, coloris_1.init)();
|
57
56
|
AbstractToolbar.colorisStarted = true;
|
@@ -35,7 +35,7 @@ export default class DropdownToolbar extends AbstractToolbar {
|
|
35
35
|
private widgetOrderCounter;
|
36
36
|
private overflowWidget;
|
37
37
|
/** @internal */
|
38
|
-
constructor(editor: Editor, parent: HTMLElement, localizationTable
|
38
|
+
constructor(editor: Editor, parent: HTMLElement, localizationTable: ToolbarLocalization);
|
39
39
|
private reLayoutQueued;
|
40
40
|
private queueReLayout;
|
41
41
|
private reLayout;
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.makeDropdownToolbar = void 0;
|
7
|
-
const localization_1 = require("./localization");
|
8
7
|
const OverflowWidget_1 = __importDefault(require("./widgets/OverflowWidget"));
|
9
8
|
const AbstractToolbar_1 = __importDefault(require("./AbstractToolbar"));
|
10
9
|
const constants_1 = require("./constants");
|
@@ -35,12 +34,12 @@ const constants_1 = require("./constants");
|
|
35
34
|
* - {@link AbstractToolbar.addExitButton}
|
36
35
|
*/
|
37
36
|
const makeDropdownToolbar = (editor) => {
|
38
|
-
return new DropdownToolbar(editor, editor.getRootElement());
|
37
|
+
return new DropdownToolbar(editor, editor.getRootElement(), editor.localization);
|
39
38
|
};
|
40
39
|
exports.makeDropdownToolbar = makeDropdownToolbar;
|
41
40
|
class DropdownToolbar extends AbstractToolbar_1.default {
|
42
41
|
/** @internal */
|
43
|
-
constructor(editor, parent, localizationTable
|
42
|
+
constructor(editor, parent, localizationTable) {
|
44
43
|
super(editor, localizationTable);
|
45
44
|
// Flex-order of the next widget to be added.
|
46
45
|
this.widgetOrderCounter = 0;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -139,6 +139,8 @@ const createHelpPage = (helpItems, onItemClick, onBackgroundClick, context) => {
|
|
139
139
|
clonedElement.style.margin = '0';
|
140
140
|
const clonedElementContainer = document.createElement('div');
|
141
141
|
clonedElementContainer.classList.add('cloned-element-container');
|
142
|
+
clonedElementContainer.role = 'group';
|
143
|
+
clonedElementContainer.ariaLabel = context.localization.helpControlsAccessibilityLabel;
|
142
144
|
clonedElementContainer.style.position = 'absolute';
|
143
145
|
clonedElementContainer.style.left = `${targetBBox.topLeft.x}px`;
|
144
146
|
clonedElementContainer.style.top = `${targetBBox.topLeft.y}px`;
|
@@ -157,11 +159,11 @@ const createHelpPage = (helpItems, onItemClick, onBackgroundClick, context) => {
|
|
157
159
|
};
|
158
160
|
const onItemChange = () => {
|
159
161
|
const helpTextElement = document.createElement('div');
|
160
|
-
helpTextElement.
|
162
|
+
helpTextElement.textContent = currentItem?.helpText ?? '';
|
161
163
|
// For tests
|
162
164
|
helpTextElement.classList.add('current-item-help');
|
163
165
|
const navigationHelpElement = document.createElement('div');
|
164
|
-
navigationHelpElement.
|
166
|
+
navigationHelpElement.textContent = context.localization.helpScreenNavigationHelp;
|
165
167
|
navigationHelpElement.classList.add('navigation-help');
|
166
168
|
textLabel.replaceChildren(helpTextElement, ...(currentItemIndex === 0 ? [navigationHelpElement] : []));
|
167
169
|
updateClonedElementStates();
|
@@ -278,8 +280,8 @@ class HelpDisplay {
|
|
278
280
|
navigationButtonContainer.classList.add('navigation-buttons');
|
279
281
|
const nextButton = document.createElement('button');
|
280
282
|
const previousButton = document.createElement('button');
|
281
|
-
nextButton.
|
282
|
-
previousButton.
|
283
|
+
nextButton.textContent = this.context.localization.next;
|
284
|
+
previousButton.textContent = this.context.localization.previous;
|
283
285
|
nextButton.classList.add('next');
|
284
286
|
previousButton.classList.add('previous');
|
285
287
|
const updateButtonVisibility = () => {
|
@@ -100,7 +100,7 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
|
|
100
100
|
setBackgroundType(backgroundType) {
|
101
101
|
const prevBackgroundColor = this.editor.estimateBackgroundColor();
|
102
102
|
const newBackground = new BackgroundComponent_1.default(backgroundType, prevBackgroundColor);
|
103
|
-
const addBackgroundCommand = this.editor.image.
|
103
|
+
const addBackgroundCommand = this.editor.image.addComponent(newBackground);
|
104
104
|
return (0, uniteCommands_1.default)([this.removeBackgroundComponents(), addBackgroundCommand]);
|
105
105
|
}
|
106
106
|
/** Returns the type of the topmost background component */
|
@@ -275,7 +275,7 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
275
275
|
const widthAdjustTransform = math_1.Mat33.scaling2D(originalWidth / newWidth);
|
276
276
|
const commands = [];
|
277
277
|
for (const component of newComponents) {
|
278
|
-
commands.push(EditorImage_1.default.
|
278
|
+
commands.push(EditorImage_1.default.addComponent(component), component.transformBy(originalTransform.rightMul(widthAdjustTransform)), component.setZIndex(editingImage.getZIndex()));
|
279
279
|
}
|
280
280
|
this.editor.dispatch((0, uniteCommands_1.default)([...commands, eraseCommand]));
|
281
281
|
selectionTools[0]?.setSelection(newComponents);
|
@@ -22,7 +22,7 @@ labelText, defaultId, choices) => {
|
|
22
22
|
outerContainer.classList.add(`${constants_1.toolbarCSSPrefix}grid-selector`);
|
23
23
|
const selectedValue = ReactiveValue_1.MutableReactiveValue.fromInitialValue(defaultId);
|
24
24
|
const menuContainer = document.createElement('div');
|
25
|
-
menuContainer.
|
25
|
+
menuContainer.role = 'group';
|
26
26
|
menuContainer.id = `${constants_1.toolbarCSSPrefix}-grid-select-id-${idCounter++}`;
|
27
27
|
(0, stopPropagationOfScrollingWheelEvents_1.default)(menuContainer);
|
28
28
|
const label = document.createElement('label');
|
package/dist/cjs/tools/Eraser.js
CHANGED
@@ -145,7 +145,7 @@ class Eraser extends BaseTool_1.default {
|
|
145
145
|
const line = new math_1.LineSegment2(this.lastPoint, currentPoint);
|
146
146
|
const region = math_1.Rect2.union(line.bbox, eraserRect);
|
147
147
|
const intersectingElems = this.editor.image
|
148
|
-
.
|
148
|
+
.getComponentsIntersecting(region)
|
149
149
|
.filter((component) => {
|
150
150
|
return component.intersects(line) || component.intersectsRect(eraserRect);
|
151
151
|
});
|
@@ -184,7 +184,7 @@ class Eraser extends BaseTool_1.default {
|
|
184
184
|
toAdd.push(...targetElem.withRegionErased(erasePath, this.editor.viewport));
|
185
185
|
}
|
186
186
|
const eraseCommand = new Erase_1.default(toErase);
|
187
|
-
const newAddCommands = toAdd.map((elem) => EditorImage_1.default.
|
187
|
+
const newAddCommands = toAdd.map((elem) => EditorImage_1.default.addComponent(elem));
|
188
188
|
eraseCommand.apply(this.editor);
|
189
189
|
newAddCommands.forEach((command) => command.apply(this.editor));
|
190
190
|
const finalToErase = [];
|
@@ -240,7 +240,7 @@ class Eraser extends BaseTool_1.default {
|
|
240
240
|
this.toRemove = this.toRemove.filter((other) => other !== item);
|
241
241
|
}
|
242
242
|
}
|
243
|
-
commands.push(...[...this.toAdd].map((a) => EditorImage_1.default.
|
243
|
+
commands.push(...[...this.toAdd].map((a) => EditorImage_1.default.addComponent(a)));
|
244
244
|
this.addCommands = [];
|
245
245
|
}
|
246
246
|
if (this.eraseCommands.length > 0) {
|
@@ -27,7 +27,7 @@ class FindTool extends BaseTool_1.default {
|
|
27
27
|
}
|
28
28
|
getMatches(searchFor) {
|
29
29
|
const lowerSearchFor = searchFor.toLocaleLowerCase();
|
30
|
-
const matchingComponents = this.editor.image.
|
30
|
+
const matchingComponents = this.editor.image.getAllComponents().filter((component) => {
|
31
31
|
let text = '';
|
32
32
|
if (component instanceof TextComponent_1.default) {
|
33
33
|
text = component.getText();
|
@@ -76,7 +76,10 @@ class PasteHandler extends BaseTool_1.default {
|
|
76
76
|
async doSVGPaste(data) {
|
77
77
|
this.editor.showLoadingWarning(0);
|
78
78
|
try {
|
79
|
-
const loader = SVGLoader_1.default.fromString(data,
|
79
|
+
const loader = SVGLoader_1.default.fromString(data, {
|
80
|
+
sanitize: true,
|
81
|
+
plugins: this.editor.getCurrentSettings().svg?.loaderPlugins ?? [],
|
82
|
+
});
|
80
83
|
const components = [];
|
81
84
|
await loader.start((component) => {
|
82
85
|
components.push(component);
|
package/dist/cjs/tools/Pen.js
CHANGED
@@ -251,7 +251,7 @@ class Pen extends BaseTool_1.default {
|
|
251
251
|
this.editor.announceForAccessibility(this.editor.localization.autocorrectedTo(stroke.description(this.editor.localization)));
|
252
252
|
}
|
253
253
|
const canFlatten = true;
|
254
|
-
const action = EditorImage_1.default.
|
254
|
+
const action = EditorImage_1.default.addComponent(stroke, canFlatten);
|
255
255
|
this.editor.dispatch(action);
|
256
256
|
}
|
257
257
|
else {
|
@@ -22,7 +22,7 @@ class SelectAllShortcutHandler extends BaseTool_1.default {
|
|
22
22
|
if (selectionTools.length > 0) {
|
23
23
|
const selectionTool = selectionTools[0];
|
24
24
|
selectionTool.setEnabled(true);
|
25
|
-
selectionTool.setSelection(this.editor.image.
|
25
|
+
selectionTool.setSelection(this.editor.image.getAllComponents());
|
26
26
|
return true;
|
27
27
|
}
|
28
28
|
}
|
@@ -53,6 +53,8 @@ const types_1 = require("./types");
|
|
53
53
|
const EditorImage_1 = __importDefault(require("../../image/EditorImage"));
|
54
54
|
const uniteCommands_1 = __importDefault(require("../../commands/uniteCommands"));
|
55
55
|
const SelectionMenuShortcut_1 = __importDefault(require("./SelectionMenuShortcut"));
|
56
|
+
const assertions_1 = require("../../util/assertions");
|
57
|
+
const describeTransformation_1 = __importDefault(require("../../util/describeTransformation"));
|
56
58
|
const updateChunkSize = 100;
|
57
59
|
const maxPreviewElemCount = 500;
|
58
60
|
// @internal
|
@@ -186,7 +188,7 @@ class Selection {
|
|
186
188
|
return 0;
|
187
189
|
}
|
188
190
|
const selectedBottommostZIndex = this.selectedElems[0].getZIndex();
|
189
|
-
const visibleObjects = this.editor.image.
|
191
|
+
const visibleObjects = this.editor.image.getComponentsIntersecting(this.region);
|
190
192
|
const topMostVisibleZIndex = visibleObjects[visibleObjects.length - 1]?.getZIndex() ?? selectedBottommostZIndex;
|
191
193
|
const deltaZIndex = topMostVisibleZIndex + 1 - selectedBottommostZIndex;
|
192
194
|
return deltaZIndex;
|
@@ -205,13 +207,13 @@ class Selection {
|
|
205
207
|
// z-index of the just-transformed commands.
|
206
208
|
if (this.selectedElems.length > 0) {
|
207
209
|
const deltaZIndex = this.getDeltaZIndexToMoveSelectionToTop();
|
208
|
-
transformPromise = this.editor.dispatch(new _a.ApplyTransformationCommand(this, selectedElems, fullTransform, deltaZIndex));
|
210
|
+
transformPromise = this.editor.dispatch(new _a.ApplyTransformationCommand(this, selectedElems, this.originalRegion.center, fullTransform, deltaZIndex));
|
209
211
|
}
|
210
212
|
return transformPromise;
|
211
213
|
}
|
212
214
|
/** Sends all selected elements to the bottom of the visible image. */
|
213
215
|
sendToBack() {
|
214
|
-
const visibleObjects = this.editor.image.
|
216
|
+
const visibleObjects = this.editor.image.getComponentsIntersecting(this.editor.viewport.visibleRect);
|
215
217
|
// VisibleObjects and selectedElems should both be sorted by z-index
|
216
218
|
const lowestVisibleZIndex = visibleObjects[0]?.getZIndex() ?? 0;
|
217
219
|
const highestSelectedZIndex = this.selectedElems[this.selectedElems.length - 1]?.getZIndex() ?? 0;
|
@@ -338,7 +340,7 @@ class Selection {
|
|
338
340
|
// If we're making things visible and the selected object wasn't previously
|
339
341
|
// visible,
|
340
342
|
else if (!parent && this.removedFromImage[elem.getId()]) {
|
341
|
-
EditorImage_1.default.
|
343
|
+
EditorImage_1.default.addComponent(elem).apply(this.editor);
|
342
344
|
this.removedFromImage[elem.getId()] = false;
|
343
345
|
delete this.removedFromImage[elem.getId()];
|
344
346
|
}
|
@@ -476,14 +478,14 @@ class Selection {
|
|
476
478
|
// Don't update the selection's focus when redoing/undoing
|
477
479
|
const selectionToUpdate = null;
|
478
480
|
const deltaZIndex = this.getDeltaZIndexToMoveSelectionToTop();
|
479
|
-
tmpApplyCommand = new _a.ApplyTransformationCommand(selectionToUpdate, this.selectedElems, this.transform, deltaZIndex);
|
481
|
+
tmpApplyCommand = new _a.ApplyTransformationCommand(selectionToUpdate, this.selectedElems, this.region.center, this.transform, deltaZIndex);
|
480
482
|
// Transform to ensure that the duplicates are in the correct location
|
481
483
|
await tmpApplyCommand.apply(this.editor);
|
482
484
|
// Show items again
|
483
485
|
this.addRemoveSelectionFromImage(true);
|
484
486
|
// With the transformation applied, create the duplicates
|
485
487
|
command = (0, uniteCommands_1.default)(this.selectedElems.map((elem) => {
|
486
|
-
return EditorImage_1.default.
|
488
|
+
return EditorImage_1.default.addComponent(elem.clone());
|
487
489
|
}));
|
488
490
|
// Move the selected objects back to the correct location.
|
489
491
|
await tmpApplyCommand?.unapply(this.editor);
|
@@ -541,21 +543,31 @@ class Selection {
|
|
541
543
|
_a = Selection;
|
542
544
|
(() => {
|
543
545
|
SerializableCommand_1.default.register('selection-tool-transform', (json, _editor) => {
|
546
|
+
const rawTransformArray = json.transform;
|
547
|
+
const rawCenterArray = json.selectionCenter ?? [0, 0];
|
548
|
+
const rawElementIds = json.elems ?? [];
|
549
|
+
(0, assertions_1.assertIsNumberArray)(rawTransformArray);
|
550
|
+
(0, assertions_1.assertIsNumberArray)(rawCenterArray);
|
551
|
+
(0, assertions_1.assertIsStringArray)(rawElementIds);
|
544
552
|
// The selection box is lost when serializing/deserializing. No need to store box rotation
|
545
|
-
const fullTransform = new math_1.Mat33(...
|
546
|
-
const elemIds =
|
553
|
+
const fullTransform = new math_1.Mat33(...rawTransformArray);
|
554
|
+
const elemIds = rawElementIds;
|
547
555
|
const deltaZIndex = parseInt(json.deltaZIndex ?? 0);
|
548
|
-
|
556
|
+
const center = math_1.Vec2.of(rawCenterArray[0] ?? 0, rawCenterArray[1] ?? 0);
|
557
|
+
return new _a.ApplyTransformationCommand(null, elemIds, center, fullTransform, deltaZIndex);
|
549
558
|
});
|
550
559
|
})();
|
551
560
|
Selection.ApplyTransformationCommand = class extends SerializableCommand_1.default {
|
552
561
|
constructor(selection,
|
553
562
|
// If a `string[]`, selectedElems is a list of element IDs.
|
554
563
|
selectedElems,
|
564
|
+
// Information used to describe the transformation
|
565
|
+
selectionCenter,
|
555
566
|
// Full transformation used to transform elements.
|
556
567
|
fullTransform, deltaZIndex) {
|
557
568
|
super('selection-tool-transform');
|
558
569
|
this.selection = selection;
|
570
|
+
this.selectionCenter = selectionCenter;
|
559
571
|
this.fullTransform = fullTransform;
|
560
572
|
this.deltaZIndex = deltaZIndex;
|
561
573
|
const isIDList = (arr) => {
|
@@ -623,10 +635,11 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand_1.defau
|
|
623
635
|
elems: this.selectedElemIds,
|
624
636
|
transform: this.fullTransform.toArray(),
|
625
637
|
deltaZIndex: this.deltaZIndex,
|
638
|
+
selectionCenter: this.selectionCenter.asArray(),
|
626
639
|
};
|
627
640
|
}
|
628
641
|
description(_editor, localizationTable) {
|
629
|
-
return localizationTable.transformedElements(this.selectedElemIds.length);
|
642
|
+
return localizationTable.transformedElements(this.selectedElemIds.length, (0, describeTransformation_1.default)(this.selectionCenter, this.fullTransform, false, localizationTable));
|
630
643
|
}
|
631
644
|
};
|
632
645
|
exports.default = Selection;
|
@@ -38,7 +38,7 @@ class LassoSelectionBuilder extends SelectionBuilder_1.default {
|
|
38
38
|
resolveInternal(image) {
|
39
39
|
const path = this.previewPath();
|
40
40
|
const lines = path.polylineApproximation();
|
41
|
-
const candidates = image.
|
41
|
+
const candidates = image.getComponentsIntersecting(path.bbox);
|
42
42
|
const componentIsInSelection = (component) => {
|
43
43
|
if (path.closedContainsRect(component.getExactBBox())) {
|
44
44
|
return true;
|
@@ -20,7 +20,7 @@ class RectSelectionBuilder extends SelectionBuilder_1.default {
|
|
20
20
|
return math_1.Path.fromRect(this.rect);
|
21
21
|
}
|
22
22
|
resolveInternal(image) {
|
23
|
-
return image.
|
23
|
+
return image.getComponentsIntersecting(this.rect).filter((element) => {
|
24
24
|
// Filter out the case where the selection rectangle is completely contained
|
25
25
|
// within the element (and does not intersect it).
|
26
26
|
// This is useful, for example, if a very large stroke is used as the background
|
@@ -22,7 +22,7 @@ class SelectionBuilder {
|
|
22
22
|
if (isClick) {
|
23
23
|
const searchRegionSize = viewport.visibleRect.maxDimension / 200;
|
24
24
|
const minSizeBox = path.bbox.grownBy(searchRegionSize);
|
25
|
-
components = image.
|
25
|
+
components = image.getComponentsIntersecting(minSizeBox).filter((component) => {
|
26
26
|
return minSizeBox.containsRect(component.getBBox()) || component.intersectsRect(minSizeBox);
|
27
27
|
});
|
28
28
|
components = filterComponents(components);
|
@@ -259,7 +259,7 @@ class SelectionTool extends BaseTool_1.default {
|
|
259
259
|
return true;
|
260
260
|
}
|
261
261
|
else if (shortcucts.matchesShortcut(keybindings_1.selectAllKeyboardShortcut, event)) {
|
262
|
-
this.setSelection(this.editor.image.
|
262
|
+
this.setSelection(this.editor.image.getAllComponents());
|
263
263
|
return true;
|
264
264
|
}
|
265
265
|
else if (event.ctrlKey) {
|
@@ -462,7 +462,8 @@ class SelectionTool extends BaseTool_1.default {
|
|
462
462
|
this.handleOverlay.style.display = enabled ? 'block' : 'none';
|
463
463
|
if (enabled) {
|
464
464
|
this.handleOverlay.tabIndex = 0;
|
465
|
-
this.handleOverlay.
|
465
|
+
this.handleOverlay.role = 'group';
|
466
|
+
this.handleOverlay.ariaLabel = this.editor.localization.selectionToolKeyboardShortcuts;
|
466
467
|
}
|
467
468
|
else {
|
468
469
|
this.handleOverlay.tabIndex = -1;
|
@@ -156,7 +156,7 @@ class SoundUITool extends BaseTool_1.default {
|
|
156
156
|
this.soundFeedback?.setColor(this.editor.display.getColorAt(current.screenPos) ?? math_1.Color4.black);
|
157
157
|
const pointerMotionLine = new math_1.LineSegment2(this.lastPointerPos, current.canvasPos);
|
158
158
|
const collisions = this.editor.image
|
159
|
-
.
|
159
|
+
.getComponentsIntersecting(pointerMotionLine.bbox)
|
160
160
|
.filter((component) => component.intersects(pointerMotionLine));
|
161
161
|
this.lastPointerPos = current.canvasPos;
|
162
162
|
if (collisions.length > 0) {
|
@@ -103,7 +103,7 @@ class TextTool extends BaseTool_1.default {
|
|
103
103
|
const scrollCorrectionCanvas = this.editor.viewport.screenToCanvasTransform.transformVec3(scrollCorrectionScreen);
|
104
104
|
const scrollTransform = math_1.Mat33.translation(scrollCorrectionCanvas);
|
105
105
|
const textComponent = TextComponent_1.default.fromLines(content.split('\n'), scrollTransform.rightMul(this.contentTransform.get()), this.textStyle);
|
106
|
-
const action = EditorImage_1.default.
|
106
|
+
const action = EditorImage_1.default.addComponent(textComponent);
|
107
107
|
if (this.removeExistingCommand) {
|
108
108
|
// Unapply so that `removeExistingCommand` can be added to the undo stack.
|
109
109
|
this.removeExistingCommand.unapply(this.editor);
|
@@ -213,7 +213,7 @@ class TextTool extends BaseTool_1.default {
|
|
213
213
|
const canvasPos = current.canvasPos;
|
214
214
|
const halfTestRegionSize = math_1.Vec2.of(4, 4).times(this.editor.viewport.getSizeOfPixelOnCanvas());
|
215
215
|
const testRegion = math_1.Rect2.fromCorners(canvasPos.minus(halfTestRegionSize), canvasPos.plus(halfTestRegionSize));
|
216
|
-
const targetNodes = this.editor.image.
|
216
|
+
const targetNodes = this.editor.image.getComponentsIntersecting(testRegion);
|
217
217
|
let targetTextNodes = targetNodes.filter((node) => node instanceof TextComponent_1.default);
|
218
218
|
// Don't try to edit text nodes that contain the viewport (this allows us
|
219
219
|
// to zoom in on text nodes and add text on top of them.)
|
@@ -15,11 +15,17 @@ export declare function assertUnreachable(key: never): never;
|
|
15
15
|
* ```
|
16
16
|
*/
|
17
17
|
export declare function assertIsNumber(value: unknown, allowNaN?: boolean): asserts value is number;
|
18
|
+
/** Throws an `Error` if the given `value` is not a `string`. */
|
19
|
+
export declare function assertIsString(value: unknown): asserts value is string;
|
18
20
|
export declare function assertIsArray(values: unknown): asserts values is unknown[];
|
19
21
|
/**
|
20
22
|
* Throws if any of `values` is not of type number.
|
21
23
|
*/
|
22
24
|
export declare function assertIsNumberArray(values: unknown, allowNaN?: boolean): asserts values is number[];
|
25
|
+
/**
|
26
|
+
* Throws if any of `values` is not of type `string`.
|
27
|
+
*/
|
28
|
+
export declare function assertIsStringArray(values: unknown): asserts values is string[];
|
23
29
|
/**
|
24
30
|
* Throws an exception if `typeof value` is not a boolean.
|
25
31
|
*/
|
@@ -4,8 +4,10 @@
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
5
5
|
exports.assertUnreachable = assertUnreachable;
|
6
6
|
exports.assertIsNumber = assertIsNumber;
|
7
|
+
exports.assertIsString = assertIsString;
|
7
8
|
exports.assertIsArray = assertIsArray;
|
8
9
|
exports.assertIsNumberArray = assertIsNumberArray;
|
10
|
+
exports.assertIsStringArray = assertIsStringArray;
|
9
11
|
exports.assertIsBoolean = assertIsBoolean;
|
10
12
|
exports.assertTruthy = assertTruthy;
|
11
13
|
exports.assertIsObject = assertIsObject;
|
@@ -33,6 +35,12 @@ function assertIsNumber(value, allowNaN = false) {
|
|
33
35
|
throw new Error('Given value is not a number');
|
34
36
|
}
|
35
37
|
}
|
38
|
+
/** Throws an `Error` if the given `value` is not a `string`. */
|
39
|
+
function assertIsString(value) {
|
40
|
+
if (typeof value !== 'string') {
|
41
|
+
throw new Error('Given value is not a string');
|
42
|
+
}
|
43
|
+
}
|
36
44
|
function assertIsArray(values) {
|
37
45
|
if (!Array.isArray(values)) {
|
38
46
|
throw new Error('Asserting isArray: Given entity is not an array');
|
@@ -48,6 +56,16 @@ function assertIsNumberArray(values, allowNaN = false) {
|
|
48
56
|
assertIsNumber(value, allowNaN);
|
49
57
|
}
|
50
58
|
}
|
59
|
+
/**
|
60
|
+
* Throws if any of `values` is not of type `string`.
|
61
|
+
*/
|
62
|
+
function assertIsStringArray(values) {
|
63
|
+
assertIsArray(values);
|
64
|
+
assertIsNumber(values.length);
|
65
|
+
for (const value of values) {
|
66
|
+
assertIsString(value);
|
67
|
+
}
|
68
|
+
}
|
51
69
|
/**
|
52
70
|
* Throws an exception if `typeof value` is not a boolean.
|
53
71
|
*/
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Mat33, Vec2 } from '@js-draw/math';
|
2
|
+
interface Descriptions {
|
3
|
+
zoomedIn: string;
|
4
|
+
zoomedOut: string;
|
5
|
+
movedLeft: string;
|
6
|
+
movedRight: string;
|
7
|
+
movedUp: string;
|
8
|
+
movedDown: string;
|
9
|
+
rotatedBy: (deg: number) => string;
|
10
|
+
}
|
11
|
+
declare const describeTransformation: (origin: Vec2, transform: Mat33, invertDirections: boolean, localizationTable: Descriptions) => string;
|
12
|
+
export default describeTransformation;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const math_1 = require("@js-draw/math");
|
4
|
+
const describeTransformation = (
|
5
|
+
// The location of the object before being transformed
|
6
|
+
origin,
|
7
|
+
// The transformation
|
8
|
+
transform,
|
9
|
+
// If true, moving the object right, for example, reads as "moved left"
|
10
|
+
invertDirections, localizationTable) => {
|
11
|
+
// Describe the transformation's affect on the viewport (note that transformation transforms
|
12
|
+
// the **elements** within the viewport). Assumes the transformation only does rotation/scale/translation.
|
13
|
+
const linearTransformedVec = transform.transformVec3(math_1.Vec2.unitX);
|
14
|
+
const affineTransformedVec = transform.transformVec2(origin);
|
15
|
+
const scale = linearTransformedVec.magnitude();
|
16
|
+
const clockwiseRotation = -(180 / Math.PI) * linearTransformedVec.angle();
|
17
|
+
const translation = affineTransformedVec.minus(origin);
|
18
|
+
const result = [];
|
19
|
+
if (scale > 1.2) {
|
20
|
+
result.push(localizationTable.zoomedIn);
|
21
|
+
}
|
22
|
+
else if (scale < 0.8) {
|
23
|
+
result.push(localizationTable.zoomedOut);
|
24
|
+
}
|
25
|
+
if (Math.floor(Math.abs(clockwiseRotation)) > 0) {
|
26
|
+
const roundedRotation = Math.round(invertDirections ? -clockwiseRotation : clockwiseRotation);
|
27
|
+
result.push(localizationTable.rotatedBy(roundedRotation));
|
28
|
+
}
|
29
|
+
const minTranslation = 1e-4;
|
30
|
+
if (translation.x > minTranslation) {
|
31
|
+
result.push(invertDirections ? localizationTable.movedLeft : localizationTable.movedRight);
|
32
|
+
}
|
33
|
+
else if (translation.x < -minTranslation) {
|
34
|
+
result.push(invertDirections ? localizationTable.movedRight : localizationTable.movedLeft);
|
35
|
+
}
|
36
|
+
if (translation.y < -minTranslation) {
|
37
|
+
result.push(invertDirections ? localizationTable.movedDown : localizationTable.movedUp);
|
38
|
+
}
|
39
|
+
else if (translation.y > minTranslation) {
|
40
|
+
result.push(invertDirections ? localizationTable.movedUp : localizationTable.movedDown);
|
41
|
+
}
|
42
|
+
return result.join('; ');
|
43
|
+
};
|
44
|
+
exports.default = describeTransformation;
|