js-draw 1.2.1 → 1.3.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 +30 -30
- package/dist/Editor.css +70 -4
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +73 -40
- package/dist/cjs/Editor.js +90 -24
- package/dist/cjs/EditorImage.d.ts +58 -6
- package/dist/cjs/EditorImage.js +336 -60
- package/dist/cjs/SVGLoader.d.ts +10 -4
- package/dist/cjs/SVGLoader.js +30 -10
- package/dist/cjs/UndoRedoHistory.d.ts +2 -2
- package/dist/cjs/UndoRedoHistory.js +4 -2
- package/dist/cjs/Viewport.d.ts +2 -1
- package/dist/cjs/Viewport.js +12 -3
- package/dist/cjs/commands/Command.d.ts +1 -0
- package/dist/cjs/commands/Command.js +1 -0
- package/dist/cjs/commands/Erase.js +1 -1
- package/dist/cjs/commands/SerializableCommand.d.ts +1 -1
- package/dist/cjs/commands/SerializableCommand.js +16 -2
- package/dist/cjs/commands/localization.d.ts +2 -0
- package/dist/cjs/commands/localization.js +2 -0
- package/dist/cjs/components/AbstractComponent.d.ts +38 -0
- package/dist/cjs/components/AbstractComponent.js +31 -0
- package/dist/cjs/components/BackgroundComponent.d.ts +10 -1
- package/dist/cjs/components/BackgroundComponent.js +60 -6
- package/dist/cjs/components/SVGGlobalAttributesObject.d.ts +2 -1
- package/dist/cjs/components/SVGGlobalAttributesObject.js +30 -1
- package/dist/cjs/components/Stroke.d.ts +1 -0
- package/dist/cjs/components/Stroke.js +44 -0
- package/dist/cjs/components/UnknownSVGObject.d.ts +2 -1
- package/dist/cjs/components/UnknownSVGObject.js +30 -1
- package/dist/cjs/components/lib.d.ts +2 -2
- package/dist/cjs/components/lib.js +15 -2
- package/dist/cjs/lib.d.ts +2 -45
- package/dist/cjs/lib.js +2 -45
- package/dist/cjs/rendering/RenderablePathSpec.d.ts +1 -0
- package/dist/cjs/rendering/RenderablePathSpec.js +1 -0
- package/dist/cjs/rendering/RenderingStyle.d.ts +1 -0
- package/dist/cjs/rendering/lib.d.ts +1 -0
- package/dist/cjs/rendering/lib.js +5 -1
- package/dist/cjs/rendering/renderers/AbstractRenderer.js +1 -1
- package/dist/cjs/shortcuts/KeyboardShortcutManager.d.ts +2 -2
- package/dist/cjs/shortcuts/KeyboardShortcutManager.js +2 -2
- package/dist/cjs/toolbar/localization.d.ts +1 -0
- package/dist/cjs/toolbar/localization.js +1 -0
- package/dist/cjs/toolbar/widgets/BaseWidget.js +5 -0
- package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +54 -25
- package/dist/cjs/toolbar/widgets/components/makeGridSelector.js +8 -0
- package/dist/cjs/tools/PanZoom.js +13 -8
- package/dist/cjs/tools/ScrollbarTool.d.ts +18 -0
- package/dist/cjs/tools/ScrollbarTool.js +85 -0
- package/dist/cjs/tools/SelectionTool/SelectionTool.selecting.test.d.ts +1 -0
- package/dist/cjs/tools/ToolController.js +2 -0
- package/dist/cjs/types.d.ts +3 -1
- package/dist/cjs/util/adjustEditorThemeForContrast.js +1 -0
- package/dist/cjs/util/assertions.d.ts +4 -0
- package/dist/cjs/util/assertions.js +12 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +73 -40
- package/dist/mjs/Editor.mjs +90 -24
- package/dist/mjs/EditorImage.d.ts +58 -6
- package/dist/mjs/EditorImage.mjs +313 -61
- package/dist/mjs/SVGLoader.d.ts +10 -4
- package/dist/mjs/SVGLoader.mjs +29 -9
- package/dist/mjs/UndoRedoHistory.d.ts +2 -2
- package/dist/mjs/UndoRedoHistory.mjs +4 -2
- package/dist/mjs/Viewport.d.ts +2 -1
- package/dist/mjs/Viewport.mjs +12 -3
- package/dist/mjs/commands/Command.d.ts +1 -0
- package/dist/mjs/commands/Command.mjs +1 -0
- package/dist/mjs/commands/Erase.mjs +1 -1
- package/dist/mjs/commands/SerializableCommand.d.ts +1 -1
- package/dist/mjs/commands/SerializableCommand.mjs +16 -2
- package/dist/mjs/commands/localization.d.ts +2 -0
- package/dist/mjs/commands/localization.mjs +2 -0
- package/dist/mjs/components/AbstractComponent.d.ts +38 -0
- package/dist/mjs/components/AbstractComponent.mjs +30 -0
- package/dist/mjs/components/BackgroundComponent.d.ts +10 -1
- package/dist/mjs/components/BackgroundComponent.mjs +37 -6
- package/dist/mjs/components/SVGGlobalAttributesObject.d.ts +2 -1
- package/dist/mjs/components/SVGGlobalAttributesObject.mjs +7 -1
- package/dist/mjs/components/Stroke.d.ts +1 -0
- package/dist/mjs/components/Stroke.mjs +44 -0
- package/dist/mjs/components/UnknownSVGObject.d.ts +2 -1
- package/dist/mjs/components/UnknownSVGObject.mjs +7 -1
- package/dist/mjs/components/lib.d.ts +2 -2
- package/dist/mjs/components/lib.mjs +2 -2
- package/dist/mjs/lib.d.ts +2 -45
- package/dist/mjs/lib.mjs +2 -45
- package/dist/mjs/rendering/RenderablePathSpec.d.ts +1 -0
- package/dist/mjs/rendering/RenderablePathSpec.mjs +1 -0
- package/dist/mjs/rendering/RenderingStyle.d.ts +1 -0
- package/dist/mjs/rendering/lib.d.ts +1 -0
- package/dist/mjs/rendering/lib.mjs +1 -0
- package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +1 -1
- package/dist/mjs/shortcuts/KeyboardShortcutManager.d.ts +2 -2
- package/dist/mjs/shortcuts/KeyboardShortcutManager.mjs +2 -2
- package/dist/mjs/toolbar/localization.d.ts +1 -0
- package/dist/mjs/toolbar/localization.mjs +1 -0
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +5 -0
- package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +54 -25
- package/dist/mjs/toolbar/widgets/components/makeGridSelector.mjs +8 -0
- package/dist/mjs/tools/PanZoom.mjs +13 -8
- package/dist/mjs/tools/ScrollbarTool.d.ts +18 -0
- package/dist/mjs/tools/ScrollbarTool.mjs +79 -0
- package/dist/mjs/tools/SelectionTool/SelectionTool.selecting.test.d.ts +1 -0
- package/dist/mjs/tools/ToolController.mjs +2 -0
- package/dist/mjs/types.d.ts +3 -1
- package/dist/mjs/util/adjustEditorThemeForContrast.mjs +1 -0
- package/dist/mjs/util/assertions.d.ts +4 -0
- package/dist/mjs/util/assertions.mjs +10 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +3 -4
- package/src/Editor.scss +8 -0
- package/src/dialogs/dialogs.scss +2 -1
- package/src/toolbar/AbstractToolbar.scss +3 -0
- package/src/toolbar/EdgeToolbar.scss +4 -1
- package/src/toolbar/widgets/DocumentPropertiesWidget.scss +12 -0
- package/src/toolbar/widgets/components/makeGridSelector.scss +6 -1
- package/src/tools/ScrollbarTool.scss +57 -0
- package/src/tools/{SoundUITool.css → SoundUITool.scss} +4 -0
- package/src/tools/tools.scss +2 -1
package/dist/cjs/EditorImage.js
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
2
25
|
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
3
26
|
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
4
27
|
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
@@ -8,13 +31,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
31
|
};
|
9
32
|
var _a, _b, _c;
|
10
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
11
|
-
exports.ImageNode = exports.EditorImageEventType = exports.sortLeavesByZIndex = void 0;
|
34
|
+
exports.RootImageNode = exports.ImageNode = exports.EditorImageEventType = exports.sortLeavesByZIndex = void 0;
|
12
35
|
const Viewport_1 = __importDefault(require("./Viewport"));
|
13
|
-
const AbstractComponent_1 =
|
36
|
+
const AbstractComponent_1 = __importStar(require("./components/AbstractComponent"));
|
14
37
|
const math_1 = require("@js-draw/math");
|
15
38
|
const SerializableCommand_1 = __importDefault(require("./commands/SerializableCommand"));
|
16
39
|
const EventDispatcher_1 = __importDefault(require("./EventDispatcher"));
|
17
40
|
const assertions_1 = require("./util/assertions");
|
41
|
+
const Command_1 = __importDefault(require("./commands/Command"));
|
18
42
|
// @internal Sort by z-index, low to high
|
19
43
|
const sortLeavesByZIndex = (leaves) => {
|
20
44
|
leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
|
@@ -23,23 +47,25 @@ exports.sortLeavesByZIndex = sortLeavesByZIndex;
|
|
23
47
|
var EditorImageEventType;
|
24
48
|
(function (EditorImageEventType) {
|
25
49
|
EditorImageEventType[EditorImageEventType["ExportViewportChanged"] = 0] = "ExportViewportChanged";
|
50
|
+
EditorImageEventType[EditorImageEventType["AutoresizeModeChanged"] = 1] = "AutoresizeModeChanged";
|
26
51
|
})(EditorImageEventType || (exports.EditorImageEventType = EditorImageEventType = {}));
|
52
|
+
const debugMode = false;
|
27
53
|
// Handles lookup/storage of elements in the image
|
28
54
|
class EditorImage {
|
29
55
|
// @internal
|
30
56
|
constructor() {
|
31
57
|
this.componentCount = 0;
|
32
|
-
this.
|
33
|
-
this.
|
58
|
+
this.settingExportRect = false;
|
59
|
+
this.root = new RootImageNode();
|
60
|
+
this.background = new RootImageNode();
|
34
61
|
this.componentsById = {};
|
35
62
|
this.notifier = new EventDispatcher_1.default();
|
36
63
|
this.importExportViewport = new Viewport_1.default(() => {
|
37
|
-
this.
|
38
|
-
image: this,
|
39
|
-
});
|
64
|
+
this.onExportViewportChanged();
|
40
65
|
});
|
41
66
|
// Default to a 500x500 image
|
42
67
|
this.importExportViewport.updateScreenSize(math_1.Vec2.of(500, 500));
|
68
|
+
this.shouldAutoresizeExportViewport = false;
|
43
69
|
}
|
44
70
|
// Returns all components that make up the background of this image. These
|
45
71
|
// components are rendered below all other components.
|
@@ -73,7 +99,13 @@ class EditorImage {
|
|
73
99
|
/** @internal */
|
74
100
|
renderWithCache(screenRenderer, cache, viewport) {
|
75
101
|
this.background.render(screenRenderer, viewport.visibleRect);
|
76
|
-
|
102
|
+
// If in debug mode, avoid rendering with cache to show additional debug information
|
103
|
+
if (!debugMode) {
|
104
|
+
cache.render(screenRenderer, this.root, viewport);
|
105
|
+
}
|
106
|
+
else {
|
107
|
+
this.root.render(screenRenderer, viewport.visibleRect);
|
108
|
+
}
|
77
109
|
}
|
78
110
|
/**
|
79
111
|
* Renders all nodes visible from `viewport` (or all nodes if `viewport = null`).
|
@@ -89,7 +121,11 @@ class EditorImage {
|
|
89
121
|
renderAll(renderer) {
|
90
122
|
this.render(renderer, null);
|
91
123
|
}
|
92
|
-
/**
|
124
|
+
/**
|
125
|
+
* @returns all elements in the image, sorted by z-index. This can be slow for large images.
|
126
|
+
*
|
127
|
+
* Does not include background elements. See {@link getBackgroundComponents}.
|
128
|
+
*/
|
93
129
|
getAllElements() {
|
94
130
|
const leaves = this.root.getLeaves();
|
95
131
|
(0, exports.sortLeavesByZIndex)(leaves);
|
@@ -100,15 +136,25 @@ class EditorImage {
|
|
100
136
|
return this.componentCount;
|
101
137
|
}
|
102
138
|
/** @returns a list of `AbstractComponent`s intersecting `region`, sorted by z-index. */
|
103
|
-
getElementsIntersectingRegion(region) {
|
104
|
-
|
139
|
+
getElementsIntersectingRegion(region, includeBackground = false) {
|
140
|
+
let leaves = this.root.getLeavesIntersectingRegion(region);
|
141
|
+
if (includeBackground) {
|
142
|
+
leaves = leaves.concat(this.background.getLeavesIntersectingRegion(region));
|
143
|
+
}
|
105
144
|
(0, exports.sortLeavesByZIndex)(leaves);
|
106
145
|
return leaves.map(leaf => leaf.getContent());
|
107
146
|
}
|
108
|
-
/** Called whenever an element is completely removed. @internal */
|
147
|
+
/** Called whenever (just after) an element is completely removed. @internal */
|
109
148
|
onDestroyElement(elem) {
|
110
149
|
this.componentCount--;
|
111
150
|
delete this.componentsById[elem.getId()];
|
151
|
+
this.autoresizeExportViewport();
|
152
|
+
}
|
153
|
+
/** Called just after an element is added. @internal */
|
154
|
+
onElementAdded(elem) {
|
155
|
+
this.componentCount++;
|
156
|
+
this.componentsById[elem.getId()] = elem;
|
157
|
+
this.autoresizeExportViewport();
|
112
158
|
}
|
113
159
|
/**
|
114
160
|
* @returns the AbstractComponent with `id`, if it exists.
|
@@ -119,13 +165,15 @@ class EditorImage {
|
|
119
165
|
return this.componentsById[id] ?? null;
|
120
166
|
}
|
121
167
|
addElementDirectly(elem) {
|
168
|
+
// Because onAddToImage can affect the element's bounding box,
|
169
|
+
// this needs to be called before parentTree.addLeaf.
|
122
170
|
elem.onAddToImage(this);
|
123
|
-
this.componentCount++;
|
124
|
-
this.componentsById[elem.getId()] = elem;
|
125
171
|
// If a background component, add to the background. Else,
|
126
172
|
// add to the normal component tree.
|
127
173
|
const parentTree = elem.isBackground() ? this.background : this.root;
|
128
|
-
|
174
|
+
const result = parentTree.addLeaf(elem);
|
175
|
+
this.onElementAdded(elem);
|
176
|
+
return result;
|
129
177
|
}
|
130
178
|
removeElementDirectly(element) {
|
131
179
|
const container = this.findParent(element);
|
@@ -156,11 +204,82 @@ class EditorImage {
|
|
156
204
|
getImportExportViewport() {
|
157
205
|
return this.importExportViewport;
|
158
206
|
}
|
207
|
+
/**
|
208
|
+
* @see {@link setImportExportRect}
|
209
|
+
*/
|
210
|
+
getImportExportRect() {
|
211
|
+
return this.getImportExportViewport().visibleRect;
|
212
|
+
}
|
213
|
+
/**
|
214
|
+
* Sets the import/export rectangle to the given `imageRect`. Disables
|
215
|
+
* autoresize (if it was previously enabled).
|
216
|
+
*/
|
159
217
|
setImportExportRect(imageRect) {
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
return
|
218
|
+
return EditorImage.SetImportExportRectCommand.of(this, imageRect, false);
|
219
|
+
}
|
220
|
+
getAutoresizeEnabled() {
|
221
|
+
return this.shouldAutoresizeExportViewport;
|
222
|
+
}
|
223
|
+
/** Returns a `Command` that sets whether the image should autoresize. */
|
224
|
+
setAutoresizeEnabled(autoresize) {
|
225
|
+
if (autoresize === this.shouldAutoresizeExportViewport) {
|
226
|
+
return Command_1.default.empty;
|
227
|
+
}
|
228
|
+
const newBBox = this.root.getBBox();
|
229
|
+
return EditorImage.SetImportExportRectCommand.of(this, newBBox, autoresize);
|
230
|
+
}
|
231
|
+
setAutoresizeEnabledDirectly(shouldAutoresize) {
|
232
|
+
if (shouldAutoresize !== this.shouldAutoresizeExportViewport) {
|
233
|
+
this.shouldAutoresizeExportViewport = shouldAutoresize;
|
234
|
+
this.notifier.dispatch(EditorImageEventType.AutoresizeModeChanged, {
|
235
|
+
image: this,
|
236
|
+
});
|
237
|
+
}
|
238
|
+
}
|
239
|
+
/** Updates the size/position of the viewport */
|
240
|
+
autoresizeExportViewport() {
|
241
|
+
// Only autoresize if in autoresize mode -- otherwise resizing the image
|
242
|
+
// should be done with undoable commands.
|
243
|
+
if (this.shouldAutoresizeExportViewport) {
|
244
|
+
this.setExportRectDirectly(this.root.getBBox());
|
245
|
+
}
|
246
|
+
}
|
247
|
+
/**
|
248
|
+
* Sets the import/export viewport directly, without returning a `Command`.
|
249
|
+
* As such, this is not undoable.
|
250
|
+
*
|
251
|
+
* See setImportExportRect
|
252
|
+
*
|
253
|
+
* Returns true if changes to the viewport were made (and thus
|
254
|
+
* ExportViewportChanged was fired.)
|
255
|
+
*/
|
256
|
+
setExportRectDirectly(newRect) {
|
257
|
+
const viewport = this.getImportExportViewport();
|
258
|
+
const lastSize = viewport.getScreenRectSize();
|
259
|
+
const lastTransform = viewport.canvasToScreenTransform;
|
260
|
+
const newTransform = math_1.Mat33.translation(newRect.topLeft.times(-1));
|
261
|
+
if (!lastSize.eq(newRect.size) || !lastTransform.eq(newTransform)) {
|
262
|
+
// Prevent the ExportViewportChanged event from being fired
|
263
|
+
// multiple times for the same viewport change: Set settingExportRect
|
264
|
+
// to true.
|
265
|
+
this.settingExportRect = true;
|
266
|
+
viewport.updateScreenSize(newRect.size);
|
267
|
+
viewport.resetTransform(newTransform);
|
268
|
+
this.settingExportRect = false;
|
269
|
+
this.onExportViewportChanged();
|
270
|
+
return true;
|
271
|
+
}
|
272
|
+
return false;
|
273
|
+
}
|
274
|
+
onExportViewportChanged() {
|
275
|
+
// Prevent firing duplicate events -- changes
|
276
|
+
// made by exportViewport.resetTransform may cause this method to be
|
277
|
+
// called.
|
278
|
+
if (!this.settingExportRect) {
|
279
|
+
this.notifier.dispatch(EditorImageEventType.ExportViewportChanged, {
|
280
|
+
image: this,
|
281
|
+
});
|
282
|
+
}
|
164
283
|
}
|
165
284
|
}
|
166
285
|
_a = EditorImage;
|
@@ -221,37 +340,57 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand_1.defaul
|
|
221
340
|
_b);
|
222
341
|
// Handles resizing the background import/export region of the image.
|
223
342
|
EditorImage.SetImportExportRectCommand = (_c = class extends SerializableCommand_1.default {
|
224
|
-
constructor(originalSize, originalTransform,
|
343
|
+
constructor(originalSize, originalTransform, originalAutoresize, newExportRect, newAutoresize) {
|
225
344
|
super(EditorImage.SetImportExportRectCommand.commandId);
|
226
345
|
this.originalSize = originalSize;
|
227
346
|
this.originalTransform = originalTransform;
|
228
|
-
this.
|
347
|
+
this.originalAutoresize = originalAutoresize;
|
348
|
+
this.newExportRect = newExportRect;
|
349
|
+
this.newAutoresize = newAutoresize;
|
350
|
+
}
|
351
|
+
// Uses `image` to store the original size/transform
|
352
|
+
static of(image, newExportRect, newAutoresize) {
|
353
|
+
const importExportViewport = image.getImportExportViewport();
|
354
|
+
const originalSize = importExportViewport.visibleRect.size;
|
355
|
+
const originalTransform = importExportViewport.canvasToScreenTransform;
|
356
|
+
const originalAutoresize = image.getAutoresizeEnabled();
|
357
|
+
return new EditorImage.SetImportExportRectCommand(originalSize, originalTransform, originalAutoresize, newExportRect, newAutoresize);
|
229
358
|
}
|
230
359
|
apply(editor) {
|
231
|
-
|
232
|
-
|
233
|
-
viewport.resetTransform(math_1.Mat33.translation(this.finalRect.topLeft.times(-1)));
|
360
|
+
editor.image.setAutoresizeEnabledDirectly(this.newAutoresize);
|
361
|
+
editor.image.setExportRectDirectly(this.newExportRect);
|
234
362
|
editor.queueRerender();
|
235
363
|
}
|
236
364
|
unapply(editor) {
|
237
365
|
const viewport = editor.image.getImportExportViewport();
|
366
|
+
editor.image.setAutoresizeEnabledDirectly(this.originalAutoresize);
|
238
367
|
viewport.updateScreenSize(this.originalSize);
|
239
368
|
viewport.resetTransform(this.originalTransform);
|
240
369
|
editor.queueRerender();
|
241
370
|
}
|
242
371
|
description(_editor, localization) {
|
243
|
-
|
372
|
+
if (this.newAutoresize !== this.originalAutoresize) {
|
373
|
+
if (this.newAutoresize) {
|
374
|
+
return localization.enabledAutoresizeOutputCommand;
|
375
|
+
}
|
376
|
+
else {
|
377
|
+
return localization.disabledAutoresizeOutputCommand;
|
378
|
+
}
|
379
|
+
}
|
380
|
+
return localization.resizeOutputCommand(this.newExportRect);
|
244
381
|
}
|
245
382
|
serializeToJSON() {
|
246
383
|
return {
|
247
384
|
originalSize: this.originalSize.xy,
|
248
385
|
originalTransform: this.originalTransform.toArray(),
|
249
386
|
newRegion: {
|
250
|
-
x: this.
|
251
|
-
y: this.
|
252
|
-
w: this.
|
253
|
-
h: this.
|
387
|
+
x: this.newExportRect.x,
|
388
|
+
y: this.newExportRect.y,
|
389
|
+
w: this.newExportRect.w,
|
390
|
+
h: this.newExportRect.h,
|
254
391
|
},
|
392
|
+
autoresize: this.newAutoresize,
|
393
|
+
originalAutoresize: this.originalAutoresize,
|
255
394
|
};
|
256
395
|
}
|
257
396
|
},
|
@@ -269,15 +408,22 @@ EditorImage.SetImportExportRectCommand = (_c = class extends SerializableCommand
|
|
269
408
|
json.newRegion.w,
|
270
409
|
json.newRegion.h,
|
271
410
|
]);
|
411
|
+
(0, assertions_1.assertIsBoolean)(json.autoresize ?? false);
|
412
|
+
(0, assertions_1.assertIsBoolean)(json.originalAutoresize ?? false);
|
272
413
|
const originalSize = math_1.Vec2.ofXY(json.originalSize);
|
273
414
|
const originalTransform = new math_1.Mat33(...json.originalTransform);
|
274
415
|
const finalRect = new math_1.Rect2(json.newRegion.x, json.newRegion.y, json.newRegion.w, json.newRegion.h);
|
275
|
-
|
416
|
+
const autoresize = json.autoresize ?? false;
|
417
|
+
const originalAutoresize = json.originalAutoresize ?? false;
|
418
|
+
return new EditorImage.SetImportExportRectCommand(originalSize, originalTransform, originalAutoresize, finalRect, autoresize);
|
276
419
|
});
|
277
420
|
})(),
|
278
421
|
_c);
|
279
422
|
exports.default = EditorImage;
|
280
|
-
/**
|
423
|
+
/**
|
424
|
+
* Part of the Editor's image. Does not handle fullscreen/invisible components.
|
425
|
+
* @internal
|
426
|
+
*/
|
281
427
|
class ImageNode {
|
282
428
|
constructor(parent = null) {
|
283
429
|
this.parent = parent;
|
@@ -299,9 +445,11 @@ class ImageNode {
|
|
299
445
|
getParent() {
|
300
446
|
return this.parent;
|
301
447
|
}
|
302
|
-
|
448
|
+
// Override this to change how children are considered within a given region.
|
449
|
+
getChildrenIntersectingRegion(region, isTooSmallFilter) {
|
303
450
|
return this.children.filter(child => {
|
304
|
-
|
451
|
+
const bbox = child.getBBox();
|
452
|
+
return !isTooSmallFilter?.(bbox) && bbox.intersects(region);
|
305
453
|
});
|
306
454
|
}
|
307
455
|
getChildrenOrSelfIntersectingRegion(region) {
|
@@ -311,29 +459,25 @@ class ImageNode {
|
|
311
459
|
return this.getChildrenIntersectingRegion(region);
|
312
460
|
}
|
313
461
|
// Returns a list of `ImageNode`s with content (and thus no children).
|
462
|
+
// Override getChildrenIntersectingRegion to customize how this method
|
463
|
+
// determines whether/which children are in `region`.
|
314
464
|
getLeavesIntersectingRegion(region, isTooSmall) {
|
315
465
|
const result = [];
|
316
|
-
let current;
|
317
466
|
const workList = [];
|
318
467
|
workList.push(this);
|
319
|
-
const toNext = () => {
|
320
|
-
current = undefined;
|
321
|
-
const next = workList.pop();
|
322
|
-
if (next && !isTooSmall?.(next.bbox)) {
|
323
|
-
current = next;
|
324
|
-
if (current.content !== null && current.getBBox().intersection(region)) {
|
325
|
-
result.push(current);
|
326
|
-
}
|
327
|
-
workList.push(...current.getChildrenIntersectingRegion(region));
|
328
|
-
}
|
329
|
-
};
|
330
468
|
while (workList.length > 0) {
|
331
|
-
|
469
|
+
const current = workList.pop();
|
470
|
+
if (current.content !== null) {
|
471
|
+
result.push(current);
|
472
|
+
}
|
473
|
+
workList.push(...current.getChildrenIntersectingRegion(region, isTooSmall));
|
332
474
|
}
|
333
475
|
return result;
|
334
476
|
}
|
335
477
|
// Returns the child of this with the target content or `null` if no
|
336
478
|
// such child exists.
|
479
|
+
//
|
480
|
+
// Note: Relies on all children to have valid bounding boxes.
|
337
481
|
getChildWithContent(target) {
|
338
482
|
const candidates = this.getLeavesIntersectingRegion(target.getBBox());
|
339
483
|
for (const candidate of candidates) {
|
@@ -397,12 +541,19 @@ class ImageNode {
|
|
397
541
|
result.rebalance();
|
398
542
|
return result;
|
399
543
|
}
|
400
|
-
const newNode =
|
544
|
+
const newNode = ImageNode.createLeafNode(this, leaf);
|
401
545
|
this.children.push(newNode);
|
402
|
-
newNode.content = leaf;
|
403
546
|
newNode.recomputeBBox(true);
|
404
547
|
return newNode;
|
405
548
|
}
|
549
|
+
// Creates a new leaf node with the given content.
|
550
|
+
// This only establishes the parent-child linking in one direction. Callers
|
551
|
+
// must add the resultant node to the list of children manually.
|
552
|
+
static createLeafNode(parent, content) {
|
553
|
+
const newNode = new ImageNode(parent);
|
554
|
+
newNode.content = content;
|
555
|
+
return newNode;
|
556
|
+
}
|
406
557
|
getBBox() {
|
407
558
|
return this.bbox;
|
408
559
|
}
|
@@ -418,9 +569,20 @@ class ImageNode {
|
|
418
569
|
this.bbox = math_1.Rect2.union(...this.children.map(child => child.getBBox()));
|
419
570
|
}
|
420
571
|
if (bubbleUp && !oldBBox.eq(this.bbox)) {
|
421
|
-
this.
|
572
|
+
if (!this.bbox.containsRect(oldBBox)) {
|
573
|
+
this.parent?.unionBBoxWith(this.bbox);
|
574
|
+
}
|
575
|
+
else {
|
576
|
+
this.parent?.recomputeBBox(true);
|
577
|
+
}
|
422
578
|
}
|
423
579
|
}
|
580
|
+
// Grows this' bounding box to also include `other`.
|
581
|
+
// Always bubbles up.
|
582
|
+
unionBBoxWith(other) {
|
583
|
+
this.bbox = this.bbox.union(other);
|
584
|
+
this.parent?.unionBBoxWith(other);
|
585
|
+
}
|
424
586
|
updateParents(recursive = false) {
|
425
587
|
for (const child of this.children) {
|
426
588
|
child.parent = this;
|
@@ -451,6 +613,19 @@ class ImageNode {
|
|
451
613
|
}
|
452
614
|
}
|
453
615
|
}
|
616
|
+
// Removes the parent-to-child link.
|
617
|
+
// Called internally by `.remove`
|
618
|
+
removeChild(child) {
|
619
|
+
const oldChildCount = this.children.length;
|
620
|
+
this.children = this.children.filter(node => {
|
621
|
+
return node !== child;
|
622
|
+
});
|
623
|
+
console.assert(this.children.length === oldChildCount - 1, `${oldChildCount - 1} ≠ ${this.children.length} after removing all nodes equal to ${child}. Nodes should only be removed once.`);
|
624
|
+
this.children.forEach(child => {
|
625
|
+
child.rebalance();
|
626
|
+
});
|
627
|
+
this.recomputeBBox(true);
|
628
|
+
}
|
454
629
|
// Remove this node and all of its children
|
455
630
|
remove() {
|
456
631
|
this.content?.onRemoveFromImage();
|
@@ -459,18 +634,10 @@ class ImageNode {
|
|
459
634
|
this.children = [];
|
460
635
|
return;
|
461
636
|
}
|
462
|
-
|
463
|
-
|
464
|
-
return node !== this;
|
465
|
-
});
|
466
|
-
console.assert(this.parent.children.length === oldChildCount - 1, `${oldChildCount - 1} ≠ ${this.parent.children.length} after removing all nodes equal to ${this}. Nodes should only be removed once.`);
|
467
|
-
this.parent.children.forEach(child => {
|
468
|
-
child.rebalance();
|
469
|
-
});
|
470
|
-
this.parent.recomputeBBox(true);
|
471
|
-
// Invalidate/disconnect this.
|
472
|
-
this.content = null;
|
637
|
+
this.parent.removeChild(this);
|
638
|
+
// Remove the child-to-parent link and invalid this
|
473
639
|
this.parent = null;
|
640
|
+
this.content = null;
|
474
641
|
this.children = [];
|
475
642
|
}
|
476
643
|
render(renderer, visibleRect) {
|
@@ -486,7 +653,116 @@ class ImageNode {
|
|
486
653
|
// Leaves by definition have content
|
487
654
|
leaf.getContent().render(renderer, visibleRect);
|
488
655
|
}
|
656
|
+
// Show debug information
|
657
|
+
if (debugMode && visibleRect) {
|
658
|
+
this.renderDebugBoundingBoxes(renderer, visibleRect);
|
659
|
+
}
|
660
|
+
}
|
661
|
+
// Debug only: Shows bounding boxes of this and all children.
|
662
|
+
renderDebugBoundingBoxes(renderer, visibleRect, depth = 0) {
|
663
|
+
const bbox = this.getBBox();
|
664
|
+
const pixelSize = 1 / (renderer.getSizeOfCanvasPixelOnScreen() || 1);
|
665
|
+
if (bbox.maxDimension < 3 * pixelSize || !bbox.intersects(visibleRect)) {
|
666
|
+
return;
|
667
|
+
}
|
668
|
+
// Render debug information for this
|
669
|
+
renderer.startObject(bbox);
|
670
|
+
// Different styling for leaf nodes
|
671
|
+
const isLeaf = !!this.content;
|
672
|
+
const fill = isLeaf ? math_1.Color4.ofRGBA(1, 0, 1, 0.4) : math_1.Color4.ofRGBA(0, 1, Math.sin(depth), 0.6);
|
673
|
+
const lineWidth = isLeaf ? 1 * pixelSize : 2 * pixelSize;
|
674
|
+
renderer.drawRect(bbox.intersection(visibleRect), lineWidth, { fill });
|
675
|
+
renderer.endObject();
|
676
|
+
// Render debug information for children
|
677
|
+
for (const child of this.children) {
|
678
|
+
child.renderDebugBoundingBoxes(renderer, visibleRect, depth + 1);
|
679
|
+
}
|
489
680
|
}
|
490
681
|
}
|
491
682
|
exports.ImageNode = ImageNode;
|
492
683
|
ImageNode.idCounter = 0;
|
684
|
+
/** An `ImageNode` that can properly handle fullscreen/data components. @internal */
|
685
|
+
class RootImageNode extends ImageNode {
|
686
|
+
constructor() {
|
687
|
+
super(...arguments);
|
688
|
+
// Nodes that will always take up the entire screen
|
689
|
+
this.fullscreenChildren = [];
|
690
|
+
// Nodes that will never be visible unless a full render is done.
|
691
|
+
this.dataComponents = [];
|
692
|
+
}
|
693
|
+
getChildrenIntersectingRegion(region, _isTooSmall) {
|
694
|
+
const result = super.getChildrenIntersectingRegion(region);
|
695
|
+
for (const node of this.fullscreenChildren) {
|
696
|
+
result.push(node);
|
697
|
+
}
|
698
|
+
return result;
|
699
|
+
}
|
700
|
+
getLeaves() {
|
701
|
+
const leaves = super.getLeaves();
|
702
|
+
// Add fullscreen/data components — this method should
|
703
|
+
// return *all* leaves.
|
704
|
+
return this.dataComponents.concat(this.fullscreenChildren, leaves);
|
705
|
+
}
|
706
|
+
removeChild(child) {
|
707
|
+
let removed = false;
|
708
|
+
const checkTargetChild = (component) => {
|
709
|
+
const isTarget = component === child;
|
710
|
+
removed ||= isTarget;
|
711
|
+
return !isTarget;
|
712
|
+
};
|
713
|
+
// Check whether the child is stored in the data/fullscreen
|
714
|
+
// component arrays first.
|
715
|
+
this.dataComponents = this.dataComponents
|
716
|
+
.filter(checkTargetChild);
|
717
|
+
this.fullscreenChildren = this.fullscreenChildren
|
718
|
+
.filter(checkTargetChild);
|
719
|
+
if (!removed) {
|
720
|
+
super.removeChild(child);
|
721
|
+
}
|
722
|
+
}
|
723
|
+
getChildWithContent(target) {
|
724
|
+
const searchExtendedChildren = () => {
|
725
|
+
// Search through all extended children
|
726
|
+
const candidates = this.fullscreenChildren.concat(this.dataComponents);
|
727
|
+
for (const candidate of candidates) {
|
728
|
+
if (candidate.getContent() === target) {
|
729
|
+
return candidate;
|
730
|
+
}
|
731
|
+
}
|
732
|
+
return null;
|
733
|
+
};
|
734
|
+
// If positioned as if a standard child, search using the superclass first.
|
735
|
+
// Because it could be mislabeled, also search the extended children if the superclass
|
736
|
+
// search fails.
|
737
|
+
if (target.getSizingMode() === AbstractComponent_1.ComponentSizingMode.BoundingBox) {
|
738
|
+
return super.getChildWithContent(target) ?? searchExtendedChildren();
|
739
|
+
}
|
740
|
+
// Fall back to the superclass -- it's possible that the component has
|
741
|
+
// changed labels.
|
742
|
+
return super.getChildWithContent(target) ?? searchExtendedChildren();
|
743
|
+
}
|
744
|
+
addLeaf(leafContent) {
|
745
|
+
const sizingMode = leafContent.getSizingMode();
|
746
|
+
if (sizingMode === AbstractComponent_1.ComponentSizingMode.BoundingBox) {
|
747
|
+
return super.addLeaf(leafContent);
|
748
|
+
}
|
749
|
+
else if (sizingMode === AbstractComponent_1.ComponentSizingMode.FillScreen) {
|
750
|
+
this.onContentChange();
|
751
|
+
const newNode = ImageNode.createLeafNode(this, leafContent);
|
752
|
+
this.fullscreenChildren.push(newNode);
|
753
|
+
return newNode;
|
754
|
+
}
|
755
|
+
else if (sizingMode === AbstractComponent_1.ComponentSizingMode.Anywhere) {
|
756
|
+
this.onContentChange();
|
757
|
+
const newNode = ImageNode.createLeafNode(this, leafContent);
|
758
|
+
this.dataComponents.push(newNode);
|
759
|
+
return newNode;
|
760
|
+
}
|
761
|
+
else {
|
762
|
+
const exhaustivenessCheck = sizingMode;
|
763
|
+
throw new Error(`Invalid sizing mode, ${sizingMode}`);
|
764
|
+
return exhaustivenessCheck;
|
765
|
+
}
|
766
|
+
}
|
767
|
+
}
|
768
|
+
exports.RootImageNode = RootImageNode;
|
package/dist/cjs/SVGLoader.d.ts
CHANGED
@@ -4,22 +4,28 @@ export declare const defaultSVGViewRect: Rect2;
|
|
4
4
|
export declare const svgAttributesDataKey = "svgAttrs";
|
5
5
|
export declare const svgStyleAttributesDataKey = "svgStyleAttrs";
|
6
6
|
export declare const svgLoaderAttributeContainerID = "svgContainerID";
|
7
|
+
export declare const svgLoaderAutoresizeClassName = "js-draw--autoresize";
|
7
8
|
export type SVGLoaderUnknownAttribute = [string, string];
|
8
9
|
export type SVGLoaderUnknownStyleAttribute = {
|
9
10
|
key: string;
|
10
11
|
value: string;
|
11
12
|
priority?: string;
|
12
13
|
};
|
14
|
+
export interface SVGLoaderOptions {
|
15
|
+
sanitize?: boolean;
|
16
|
+
disableUnknownObjectWarnings?: boolean;
|
17
|
+
}
|
13
18
|
export default class SVGLoader implements ImageLoader {
|
14
19
|
private source;
|
15
|
-
private onFinish
|
16
|
-
private readonly storeUnknown;
|
20
|
+
private onFinish;
|
17
21
|
private onAddComponent;
|
18
22
|
private onProgress;
|
19
23
|
private onDetermineExportRect;
|
20
24
|
private processedCount;
|
21
25
|
private totalToProcess;
|
22
26
|
private rootViewBox;
|
27
|
+
private readonly storeUnknown;
|
28
|
+
private readonly disableUnknownObjectWarnings;
|
23
29
|
private constructor();
|
24
30
|
private getStyle;
|
25
31
|
private strokeDataFromElem;
|
@@ -48,7 +54,7 @@ export default class SVGLoader implements ImageLoader {
|
|
48
54
|
*
|
49
55
|
* @see {@link Editor.loadFrom}
|
50
56
|
* @param text - Textual representation of the SVG (e.g. `<svg viewbox='...'>...</svg>`).
|
51
|
-
* @param
|
57
|
+
* @param options - if `true` or `false`, treated as the `sanitize` option -- don't store unknown attributes.
|
52
58
|
*/
|
53
|
-
static fromString(text: string,
|
59
|
+
static fromString(text: string, options?: Partial<SVGLoaderOptions> | boolean): SVGLoader;
|
54
60
|
}
|