js-draw 1.27.2 → 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/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/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/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 +1 -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/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/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 +1 -1
- package/package.json +4 -4
- package/src/toolbar/utils/HelpDisplay.scss +7 -1
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
const describeComponentList_1 = __importDefault(require("../components/util/describeComponentList"));
|
7
|
+
const assertions_1 = require("../util/assertions");
|
7
8
|
const Erase_1 = __importDefault(require("./Erase"));
|
8
9
|
const SerializableCommand_1 = __importDefault(require("./SerializableCommand"));
|
9
10
|
/**
|
@@ -16,7 +17,7 @@ const SerializableCommand_1 = __importDefault(require("./SerializableCommand"));
|
|
16
17
|
*
|
17
18
|
* // Find all elements intersecting the rectangle with top left (0,0) and
|
18
19
|
* // (width,height)=(100,100).
|
19
|
-
* const elems = editor.image.
|
20
|
+
* const elems = editor.image.getComponentsIntersecting(
|
20
21
|
* new Rect2(0, 0, 100, 100)
|
21
22
|
* );
|
22
23
|
*
|
@@ -27,13 +28,24 @@ const SerializableCommand_1 = __importDefault(require("./SerializableCommand"));
|
|
27
28
|
* editor.dispatch(duplicateElems);
|
28
29
|
* ```
|
29
30
|
*
|
30
|
-
* @see {@link Editor.dispatch} {@link EditorImage.
|
31
|
+
* @see {@link Editor.dispatch} {@link EditorImage.getComponentsIntersecting}
|
31
32
|
*/
|
32
33
|
class Duplicate extends SerializableCommand_1.default {
|
33
|
-
constructor(toDuplicate
|
34
|
+
constructor(toDuplicate,
|
35
|
+
// @internal -- IDs given to the duplicate elements
|
36
|
+
idsForDuplicates) {
|
34
37
|
super('duplicate');
|
35
38
|
this.toDuplicate = toDuplicate;
|
36
|
-
this.duplicates = toDuplicate.map((elem) =>
|
39
|
+
this.duplicates = toDuplicate.map((elem, idx) => {
|
40
|
+
// For collaborative editing, it's important for the clones to have
|
41
|
+
// the same IDs as the originals
|
42
|
+
if (idsForDuplicates && idsForDuplicates[idx]) {
|
43
|
+
return elem.cloneWithId(idsForDuplicates[idx]);
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
return elem.clone();
|
47
|
+
}
|
48
|
+
});
|
37
49
|
this.reverse = new Erase_1.default(this.duplicates);
|
38
50
|
}
|
39
51
|
apply(editor) {
|
@@ -52,13 +64,42 @@ class Duplicate extends SerializableCommand_1.default {
|
|
52
64
|
return localizationTable.duplicateAction((0, describeComponentList_1.default)(localizationTable, this.duplicates) ?? localizationTable.elements, this.duplicates.length);
|
53
65
|
}
|
54
66
|
serializeToJSON() {
|
55
|
-
return
|
67
|
+
return {
|
68
|
+
originalIds: this.toDuplicate.map((elem) => elem.getId()),
|
69
|
+
cloneIds: this.duplicates.map((elem) => elem.getId()),
|
70
|
+
};
|
56
71
|
}
|
57
72
|
}
|
58
73
|
(() => {
|
59
74
|
SerializableCommand_1.default.register('duplicate', (json, editor) => {
|
60
|
-
|
61
|
-
|
75
|
+
let originalIds;
|
76
|
+
let cloneIds;
|
77
|
+
// Compatibility with older editors
|
78
|
+
if (Array.isArray(json)) {
|
79
|
+
originalIds = json;
|
80
|
+
cloneIds = [];
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
originalIds = json.originalIds;
|
84
|
+
cloneIds = json.cloneIds;
|
85
|
+
}
|
86
|
+
(0, assertions_1.assertIsStringArray)(originalIds);
|
87
|
+
(0, assertions_1.assertIsStringArray)(cloneIds);
|
88
|
+
// Resolve to elements -- only keep the elements that can be found in the image.
|
89
|
+
const resolvedElements = [];
|
90
|
+
const filteredCloneIds = [];
|
91
|
+
for (let i = 0; i < originalIds.length; i++) {
|
92
|
+
const originalId = originalIds[i];
|
93
|
+
const foundElement = editor.image.lookupElement(originalId);
|
94
|
+
if (!foundElement) {
|
95
|
+
console.warn('Duplicate command: Could not find element with ID', originalId);
|
96
|
+
}
|
97
|
+
else {
|
98
|
+
filteredCloneIds.push(cloneIds[i]);
|
99
|
+
resolvedElements.push(foundElement);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
return new Duplicate(resolvedElements, filteredCloneIds);
|
62
103
|
});
|
63
104
|
})();
|
64
105
|
exports.default = Duplicate;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -31,7 +31,7 @@ import SerializableCommand from './SerializableCommand';
|
|
31
31
|
*
|
32
32
|
* // Find all elements intersecting the rectangle with top left (-10,-30) and
|
33
33
|
* // (width,height)=(50,100).
|
34
|
-
* const elems = editor.image.
|
34
|
+
* const elems = editor.image.getComponentsIntersecting(
|
35
35
|
* new Rect2(-10, -30, 50, 100)
|
36
36
|
* );
|
37
37
|
*
|
@@ -36,7 +36,7 @@ const SerializableCommand_1 = __importDefault(require("./SerializableCommand"));
|
|
36
36
|
*
|
37
37
|
* // Find all elements intersecting the rectangle with top left (-10,-30) and
|
38
38
|
* // (width,height)=(50,100).
|
39
|
-
* const elems = editor.image.
|
39
|
+
* const elems = editor.image.getComponentsIntersecting(
|
40
40
|
* new Rect2(-10, -30, 50, 100)
|
41
41
|
* );
|
42
42
|
*
|
@@ -68,7 +68,7 @@ class Erase extends SerializableCommand_1.default {
|
|
68
68
|
unapply(editor) {
|
69
69
|
for (const part of this.toRemove) {
|
70
70
|
if (!editor.image.findParent(part)) {
|
71
|
-
EditorImage_1.default.
|
71
|
+
EditorImage_1.default.addComponent(part).apply(editor);
|
72
72
|
}
|
73
73
|
}
|
74
74
|
this.applied = false;
|
@@ -11,11 +11,11 @@ export interface CommandLocalization {
|
|
11
11
|
duplicatedNoElements: string;
|
12
12
|
elements: string;
|
13
13
|
updatedViewport: string;
|
14
|
-
transformedElements: (elemCount: number) => string;
|
14
|
+
transformedElements: (elemCount: number, transformDescription: string) => string;
|
15
15
|
resizeOutputCommand: (newSize: Rect2) => string;
|
16
16
|
enabledAutoresizeOutputCommand: string;
|
17
17
|
disabledAutoresizeOutputCommand: string;
|
18
|
-
|
18
|
+
addComponentAction: (elemDescription: string) => string;
|
19
19
|
eraseAction: (elemDescription: string, numElems: number) => string;
|
20
20
|
duplicateAction: (elemDescription: string, count: number) => string;
|
21
21
|
inverseOf: (actionDescription: string) => string;
|
@@ -3,11 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.defaultCommandLocalization = void 0;
|
4
4
|
exports.defaultCommandLocalization = {
|
5
5
|
updatedViewport: 'Transformed Viewport',
|
6
|
-
transformedElements: (elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`,
|
6
|
+
transformedElements: (elemCount, action) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'} (${action})`,
|
7
7
|
resizeOutputCommand: (newSize) => `Resized image to ${newSize.w}x${newSize.h}`,
|
8
8
|
enabledAutoresizeOutputCommand: 'Enabled output autoresize',
|
9
9
|
disabledAutoresizeOutputCommand: 'Disabled output autoresize',
|
10
|
-
|
10
|
+
addComponentAction: (componentDescription) => `Added ${componentDescription}`,
|
11
11
|
eraseAction: (componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`,
|
12
12
|
duplicateAction: (componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`,
|
13
13
|
unionOf: (actionDescription, actionCount) => `Union: ${actionCount} ${actionDescription}`,
|
@@ -158,6 +158,13 @@ export default abstract class AbstractComponent {
|
|
158
158
|
abstract description(localizationTable: ImageComponentLocalization): string;
|
159
159
|
protected abstract createClone(): AbstractComponent;
|
160
160
|
clone(): AbstractComponent;
|
161
|
+
/**
|
162
|
+
* Creates a copy of this component with a particular `id`.
|
163
|
+
* This is used internally by {@link Duplicate} when deserializing.
|
164
|
+
*
|
165
|
+
* @internal -- users of the library shouldn't need this.
|
166
|
+
*/
|
167
|
+
cloneWithId(cloneId: string): AbstractComponent;
|
161
168
|
/**
|
162
169
|
* **Optional method**: Divides this component into sections roughly along the given path,
|
163
170
|
* removing parts that are roughly within `shape`.
|
@@ -13,6 +13,8 @@ const SerializableCommand_1 = __importDefault(require("../commands/SerializableC
|
|
13
13
|
const EditorImage_1 = __importDefault(require("../image/EditorImage"));
|
14
14
|
const math_1 = require("@js-draw/math");
|
15
15
|
const UnresolvedCommand_1 = __importDefault(require("../commands/UnresolvedCommand"));
|
16
|
+
const describeTransformation_1 = __importDefault(require("../util/describeTransformation"));
|
17
|
+
const assertions_1 = require("../util/assertions");
|
16
18
|
var ComponentSizingMode;
|
17
19
|
(function (ComponentSizingMode) {
|
18
20
|
/** The default. The compnent gets its size from its bounding box. */
|
@@ -207,6 +209,17 @@ class AbstractComponent {
|
|
207
209
|
}
|
208
210
|
return clone;
|
209
211
|
}
|
212
|
+
/**
|
213
|
+
* Creates a copy of this component with a particular `id`.
|
214
|
+
* This is used internally by {@link Duplicate} when deserializing.
|
215
|
+
*
|
216
|
+
* @internal -- users of the library shouldn't need this.
|
217
|
+
*/
|
218
|
+
cloneWithId(cloneId) {
|
219
|
+
const clone = this.clone();
|
220
|
+
clone.id = cloneId;
|
221
|
+
return clone;
|
222
|
+
}
|
210
223
|
// Convert the component to an object that can be passed to
|
211
224
|
// `JSON.stringify`.
|
212
225
|
//
|
@@ -250,6 +263,7 @@ class AbstractComponent {
|
|
250
263
|
if (AbstractComponent.isNotDeserializable(json)) {
|
251
264
|
throw new Error(`Element with data ${json} cannot be deserialized.`);
|
252
265
|
}
|
266
|
+
(0, assertions_1.assertIsString)(json.id);
|
253
267
|
const instance = this.deserializationCallbacks[json.name](json.data);
|
254
268
|
instance.id = json.id;
|
255
269
|
if (isFinite(json.zIndex)) {
|
@@ -313,7 +327,7 @@ AbstractComponent.TransformElementCommand = (_a = class extends UnresolvedComman
|
|
313
327
|
}
|
314
328
|
// Add the element back to the document.
|
315
329
|
if (hadParent) {
|
316
|
-
EditorImage_1.default.
|
330
|
+
EditorImage_1.default.addComponent(this.component).apply(editor);
|
317
331
|
}
|
318
332
|
}
|
319
333
|
apply(editor) {
|
@@ -327,7 +341,7 @@ AbstractComponent.TransformElementCommand = (_a = class extends UnresolvedComman
|
|
327
341
|
editor.queueRerender();
|
328
342
|
}
|
329
343
|
description(_editor, localizationTable) {
|
330
|
-
return localizationTable.transformedElements(1);
|
344
|
+
return localizationTable.transformedElements(1, (0, describeTransformation_1.default)(math_1.Vec2.zero, this.affineTransfm, false, localizationTable));
|
331
345
|
}
|
332
346
|
serializeToJSON() {
|
333
347
|
return {
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import SerializableCommand from '../commands/SerializableCommand';
|
2
|
-
import { Mat33, Path, Rect2, LineSegment2 } from '@js-draw/math';
|
2
|
+
import { Mat33, Path, Rect2, LineSegment2, Color4 } from '@js-draw/math';
|
3
3
|
import Editor from '../Editor';
|
4
4
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
5
|
+
import { StrokeStyle } from '../rendering/RenderingStyle';
|
5
6
|
import AbstractComponent from './AbstractComponent';
|
6
7
|
import { ImageComponentLocalization } from './localization';
|
7
8
|
import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
|
@@ -48,6 +49,25 @@ export default class Stroke extends AbstractComponent implements RestyleableComp
|
|
48
49
|
* ```
|
49
50
|
*/
|
50
51
|
constructor(parts: RenderablePathSpec[], initialZIndex?: number);
|
52
|
+
/**
|
53
|
+
* Creates a new `Stroke` from a {@link Path} and `style`. Strokes created
|
54
|
+
* with this method have transparent fill.
|
55
|
+
*
|
56
|
+
* Example:
|
57
|
+
* ```ts,runnable
|
58
|
+
* import { Editor, Stroke, Color4 } from 'js-draw';
|
59
|
+
* const editor = new Editor(document.body);
|
60
|
+
* ---visible---
|
61
|
+
* const stroke = Stroke.fromStroked('m0,0 l10,10', { width: 10, color: Color4.red });
|
62
|
+
* editor.dispatch(editor.image.addComponent(stroke));
|
63
|
+
* ```
|
64
|
+
* Notice that `path` can be a string that specifies an SVG path
|
65
|
+
*
|
66
|
+
* @see fromFilled
|
67
|
+
*/
|
68
|
+
static fromStroked(path: Path | string, style: StrokeStyle): Stroke;
|
69
|
+
/** @see fromStroked */
|
70
|
+
static fromFilled(path: Path | string, fill: Color4): Stroke;
|
51
71
|
getStyle(): ComponentStyle;
|
52
72
|
updateStyle(style: ComponentStyle): SerializableCommand;
|
53
73
|
forceStyle(style: ComponentStyle, editor: Editor | null): void;
|
@@ -74,6 +74,35 @@ class Stroke extends AbstractComponent_1.default {
|
|
74
74
|
}
|
75
75
|
this.contentBBox ??= math_1.Rect2.empty;
|
76
76
|
}
|
77
|
+
/**
|
78
|
+
* Creates a new `Stroke` from a {@link Path} and `style`. Strokes created
|
79
|
+
* with this method have transparent fill.
|
80
|
+
*
|
81
|
+
* Example:
|
82
|
+
* ```ts,runnable
|
83
|
+
* import { Editor, Stroke, Color4 } from 'js-draw';
|
84
|
+
* const editor = new Editor(document.body);
|
85
|
+
* ---visible---
|
86
|
+
* const stroke = Stroke.fromStroked('m0,0 l10,10', { width: 10, color: Color4.red });
|
87
|
+
* editor.dispatch(editor.image.addComponent(stroke));
|
88
|
+
* ```
|
89
|
+
* Notice that `path` can be a string that specifies an SVG path
|
90
|
+
*
|
91
|
+
* @see fromFilled
|
92
|
+
*/
|
93
|
+
static fromStroked(path, style) {
|
94
|
+
if (typeof path === 'string') {
|
95
|
+
path = math_1.Path.fromString(path);
|
96
|
+
}
|
97
|
+
return new Stroke([(0, RenderablePathSpec_1.pathToRenderable)(path, { fill: math_1.Color4.transparent, stroke: style })]);
|
98
|
+
}
|
99
|
+
/** @see fromStroked */
|
100
|
+
static fromFilled(path, fill) {
|
101
|
+
if (typeof path === 'string') {
|
102
|
+
path = math_1.Path.fromString(path);
|
103
|
+
}
|
104
|
+
return new Stroke([(0, RenderablePathSpec_1.pathToRenderable)(path, { fill })]);
|
105
|
+
}
|
77
106
|
getStyle() {
|
78
107
|
if (this.parts.length === 0) {
|
79
108
|
return {};
|
@@ -38,7 +38,7 @@ type TextElement = TextComponent | string;
|
|
38
38
|
* };
|
39
39
|
*
|
40
40
|
* editor.dispatch(
|
41
|
-
* editor.image.
|
41
|
+
* editor.image.addComponent(new TextComponent(['Hello, world'], positioning1, style)),
|
42
42
|
* );
|
43
43
|
*
|
44
44
|
*
|
@@ -49,7 +49,7 @@ type TextElement = TextComponent | string;
|
|
49
49
|
* // is placed directly after 'Test'.
|
50
50
|
* const positioning2 = Mat33.translation(Vec2.of(10, 50));
|
51
51
|
* editor.dispatch(
|
52
|
-
* editor.image.
|
52
|
+
* editor.image.addComponent(
|
53
53
|
* new TextComponent([ new TextComponent(['Test'], positioning1, style), '[Test]' ], positioning2, style)
|
54
54
|
* ),
|
55
55
|
* );
|
@@ -46,7 +46,7 @@ const defaultTextStyle = {
|
|
46
46
|
* };
|
47
47
|
*
|
48
48
|
* editor.dispatch(
|
49
|
-
* editor.image.
|
49
|
+
* editor.image.addComponent(new TextComponent(['Hello, world'], positioning1, style)),
|
50
50
|
* );
|
51
51
|
*
|
52
52
|
*
|
@@ -57,7 +57,7 @@ const defaultTextStyle = {
|
|
57
57
|
* // is placed directly after 'Test'.
|
58
58
|
* const positioning2 = Mat33.translation(Vec2.of(10, 50));
|
59
59
|
* editor.dispatch(
|
60
|
-
* editor.image.
|
60
|
+
* editor.image.addComponent(
|
61
61
|
* new TextComponent([ new TextComponent(['Test'], positioning1, style), '[Test]' ], positioning2, style)
|
62
62
|
* ),
|
63
63
|
* );
|
@@ -22,7 +22,7 @@ export type EditorImageNotifier = EventDispatcher<EditorImageEventType, {
|
|
22
22
|
*/
|
23
23
|
export type PreRenderComponentCallback = (component: AbstractComponent, componentsProcessed: number, totalComponents: number) => Promise<boolean>;
|
24
24
|
/**
|
25
|
-
*
|
25
|
+
* Handles lookup/storage of elements in the image.
|
26
26
|
*
|
27
27
|
* `js-draw` images are made up of a collection of {@link AbstractComponent}s (which
|
28
28
|
* includes {@link Stroke}s, {@link TextComponent}s, etc.). An `EditorImage`
|
@@ -30,9 +30,9 @@ export type PreRenderComponentCallback = (component: AbstractComponent, componen
|
|
30
30
|
*
|
31
31
|
* Here's how to do a few common operations:
|
32
32
|
* - **Get all components in a {@link @js-draw/math!Rect2 | Rect2}**:
|
33
|
-
* {@link EditorImage.
|
33
|
+
* {@link EditorImage.getComponentsIntersecting}.
|
34
34
|
* - **Draw an `EditorImage` onto a canvas/SVG**: {@link EditorImage.render}.
|
35
|
-
* - **Adding a new component**: {@link EditorImage.
|
35
|
+
* - **Adding a new component**: {@link EditorImage.addComponent}.
|
36
36
|
*
|
37
37
|
* **Example**:
|
38
38
|
* [[include:doc-pages/inline-examples/image-add-and-lookup.md]]
|
@@ -82,19 +82,23 @@ export default class EditorImage {
|
|
82
82
|
* @returns all elements in the image, sorted by z-index (low to high).
|
83
83
|
*
|
84
84
|
* This can be slow for large images. If you only need all elemenst in part of the image,
|
85
|
-
* consider using {@link
|
85
|
+
* consider using {@link getComponentsIntersecting} instead.
|
86
86
|
*
|
87
87
|
* **Note**: The result does not include background elements. See {@link getBackgroundComponents}.
|
88
88
|
*/
|
89
|
+
getAllComponents(): AbstractComponent[];
|
90
|
+
/** @deprecated in favor of {@link getAllComponents} */
|
89
91
|
getAllElements(): AbstractComponent[];
|
90
92
|
/** Returns the number of elements added to this image. @internal */
|
91
93
|
estimateNumElements(): number;
|
94
|
+
/** @deprecated @see getComponentsIntersecting */
|
95
|
+
getElementsIntersectingRegion(region: Rect2, includeBackground?: boolean): AbstractComponent[];
|
92
96
|
/**
|
93
97
|
* @returns a list of `AbstractComponent`s intersecting `region`, sorted by increasing z-index.
|
94
98
|
*
|
95
99
|
* Components in the background layer are only included if `includeBackground` is `true`.
|
96
100
|
*/
|
97
|
-
|
101
|
+
getComponentsIntersecting(region: Rect2, includeBackground?: boolean): AbstractComponent[];
|
98
102
|
/** Called whenever (just after) an element is completely removed. @internal */
|
99
103
|
onDestroyElement(elem: AbstractComponent): void;
|
100
104
|
/** Called just after an element is added. @internal */
|
@@ -105,7 +109,7 @@ export default class EditorImage {
|
|
105
109
|
* @see {@link AbstractComponent.getId}
|
106
110
|
*/
|
107
111
|
lookupElement(id: string): AbstractComponent | null;
|
108
|
-
private
|
112
|
+
private addComponentDirectly;
|
109
113
|
private removeElementDirectly;
|
110
114
|
/**
|
111
115
|
* Returns a command that adds the given element to the `EditorImage`.
|
@@ -118,10 +122,14 @@ export default class EditorImage {
|
|
118
122
|
*
|
119
123
|
* [[include:doc-pages/inline-examples/adding-a-stroke.md]]
|
120
124
|
*/
|
121
|
-
static
|
122
|
-
/** @see EditorImage.
|
125
|
+
static addComponent(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
126
|
+
/** @see EditorImage.addComponent */
|
127
|
+
addComponent(component: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
128
|
+
/** Alias for {@link addComponent}. @deprecated Prefer `.addComponent` */
|
123
129
|
addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
124
|
-
|
130
|
+
/** Alias for {@link addComponent}. @deprecated Prefer `.addComponent`. */
|
131
|
+
static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
132
|
+
private static AddComponentCommand;
|
125
133
|
/**
|
126
134
|
* @returns a `Viewport` for rendering the image when importing/exporting.
|
127
135
|
*/
|
@@ -61,7 +61,7 @@ var EditorImageEventType;
|
|
61
61
|
})(EditorImageEventType || (exports.EditorImageEventType = EditorImageEventType = {}));
|
62
62
|
let debugMode = false;
|
63
63
|
/**
|
64
|
-
*
|
64
|
+
* Handles lookup/storage of elements in the image.
|
65
65
|
*
|
66
66
|
* `js-draw` images are made up of a collection of {@link AbstractComponent}s (which
|
67
67
|
* includes {@link Stroke}s, {@link TextComponent}s, etc.). An `EditorImage`
|
@@ -69,9 +69,9 @@ let debugMode = false;
|
|
69
69
|
*
|
70
70
|
* Here's how to do a few common operations:
|
71
71
|
* - **Get all components in a {@link @js-draw/math!Rect2 | Rect2}**:
|
72
|
-
* {@link EditorImage.
|
72
|
+
* {@link EditorImage.getComponentsIntersecting}.
|
73
73
|
* - **Draw an `EditorImage` onto a canvas/SVG**: {@link EditorImage.render}.
|
74
|
-
* - **Adding a new component**: {@link EditorImage.
|
74
|
+
* - **Adding a new component**: {@link EditorImage.addComponent}.
|
75
75
|
*
|
76
76
|
* **Example**:
|
77
77
|
* [[include:doc-pages/inline-examples/image-add-and-lookup.md]]
|
@@ -118,7 +118,7 @@ class EditorImage {
|
|
118
118
|
const parent = this.findParent(elem);
|
119
119
|
if (parent) {
|
120
120
|
parent.remove();
|
121
|
-
this.
|
121
|
+
this.addComponentDirectly(elem);
|
122
122
|
}
|
123
123
|
}
|
124
124
|
/** @internal */
|
@@ -173,25 +173,33 @@ class EditorImage {
|
|
173
173
|
* @returns all elements in the image, sorted by z-index (low to high).
|
174
174
|
*
|
175
175
|
* This can be slow for large images. If you only need all elemenst in part of the image,
|
176
|
-
* consider using {@link
|
176
|
+
* consider using {@link getComponentsIntersecting} instead.
|
177
177
|
*
|
178
178
|
* **Note**: The result does not include background elements. See {@link getBackgroundComponents}.
|
179
179
|
*/
|
180
|
-
|
180
|
+
getAllComponents() {
|
181
181
|
const leaves = this.root.getLeaves();
|
182
182
|
(0, exports.sortLeavesByZIndex)(leaves);
|
183
183
|
return leaves.map((leaf) => leaf.getContent());
|
184
184
|
}
|
185
|
+
/** @deprecated in favor of {@link getAllComponents} */
|
186
|
+
getAllElements() {
|
187
|
+
return this.getAllComponents();
|
188
|
+
}
|
185
189
|
/** Returns the number of elements added to this image. @internal */
|
186
190
|
estimateNumElements() {
|
187
191
|
return this.componentCount;
|
188
192
|
}
|
193
|
+
/** @deprecated @see getComponentsIntersecting */
|
194
|
+
getElementsIntersectingRegion(region, includeBackground = false) {
|
195
|
+
return this.getComponentsIntersecting(region, includeBackground);
|
196
|
+
}
|
189
197
|
/**
|
190
198
|
* @returns a list of `AbstractComponent`s intersecting `region`, sorted by increasing z-index.
|
191
199
|
*
|
192
200
|
* Components in the background layer are only included if `includeBackground` is `true`.
|
193
201
|
*/
|
194
|
-
|
202
|
+
getComponentsIntersecting(region, includeBackground = false) {
|
195
203
|
let leaves = this.root.getLeavesIntersectingRegion(region);
|
196
204
|
if (includeBackground) {
|
197
205
|
leaves = leaves.concat(this.background.getLeavesIntersectingRegion(region));
|
@@ -219,7 +227,7 @@ class EditorImage {
|
|
219
227
|
lookupElement(id) {
|
220
228
|
return this.componentsById[id] ?? null;
|
221
229
|
}
|
222
|
-
|
230
|
+
addComponentDirectly(elem) {
|
223
231
|
// Because onAddToImage can affect the element's bounding box,
|
224
232
|
// this needs to be called before parentTree.addLeaf.
|
225
233
|
elem.onAddToImage(this);
|
@@ -250,12 +258,20 @@ class EditorImage {
|
|
250
258
|
*
|
251
259
|
* [[include:doc-pages/inline-examples/adding-a-stroke.md]]
|
252
260
|
*/
|
253
|
-
static
|
254
|
-
return new _a.
|
261
|
+
static addComponent(elem, applyByFlattening = false) {
|
262
|
+
return new _a.AddComponentCommand(elem, applyByFlattening);
|
263
|
+
}
|
264
|
+
/** @see EditorImage.addComponent */
|
265
|
+
addComponent(component, applyByFlattening) {
|
266
|
+
return _a.addComponent(component, applyByFlattening);
|
255
267
|
}
|
256
|
-
/** @
|
268
|
+
/** Alias for {@link addComponent}. @deprecated Prefer `.addComponent` */
|
257
269
|
addElement(elem, applyByFlattening) {
|
258
|
-
return
|
270
|
+
return this.addComponent(elem, applyByFlattening);
|
271
|
+
}
|
272
|
+
/** Alias for {@link addComponent}. @deprecated Prefer `.addComponent`. */
|
273
|
+
static addElement(elem, applyByFlattening = false) {
|
274
|
+
return this.addComponent(elem, applyByFlattening);
|
259
275
|
}
|
260
276
|
/**
|
261
277
|
* @returns a `Viewport` for rendering the image when importing/exporting.
|
@@ -386,7 +402,7 @@ class EditorImage {
|
|
386
402
|
}
|
387
403
|
_a = EditorImage;
|
388
404
|
// A Command that can access private [EditorImage] functionality
|
389
|
-
EditorImage.
|
405
|
+
EditorImage.AddComponentCommand = (_b = class extends SerializableCommand_1.default {
|
390
406
|
// If [applyByFlattening], then the rendered content of this element
|
391
407
|
// is present on the display's wet ink canvas. As such, no re-render is necessary
|
392
408
|
// the first time this command is applied (the surfaces are joined instead).
|
@@ -406,7 +422,7 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand_1.defaul
|
|
406
422
|
}
|
407
423
|
}
|
408
424
|
apply(editor) {
|
409
|
-
editor.image.
|
425
|
+
editor.image.addComponentDirectly(this.element);
|
410
426
|
if (!this.applyByFlattening) {
|
411
427
|
editor.queueRerender();
|
412
428
|
}
|
@@ -420,7 +436,7 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand_1.defaul
|
|
420
436
|
editor.queueRerender();
|
421
437
|
}
|
422
438
|
description(_editor, localization) {
|
423
|
-
return localization.
|
439
|
+
return localization.addComponentAction(this.element.description(localization));
|
424
440
|
}
|
425
441
|
serializeToJSON() {
|
426
442
|
return {
|
@@ -428,13 +444,13 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand_1.defaul
|
|
428
444
|
};
|
429
445
|
}
|
430
446
|
},
|
431
|
-
__setFunctionName(_b, "
|
447
|
+
__setFunctionName(_b, "AddComponentCommand"),
|
432
448
|
(() => {
|
433
449
|
SerializableCommand_1.default.register('add-element', (json, editor) => {
|
434
450
|
const id = json.elemData.id;
|
435
451
|
const foundElem = editor.image.lookupElement(id);
|
436
452
|
const elem = foundElem ?? AbstractComponent_1.default.deserialize(json.elemData);
|
437
|
-
const result = new _a.
|
453
|
+
const result = new _a.AddComponentCommand(elem);
|
438
454
|
result.serializedElem = json.elemData;
|
439
455
|
return result;
|
440
456
|
});
|
package/dist/cjs/lib.d.ts
CHANGED
@@ -20,7 +20,7 @@ export * from './types';
|
|
20
20
|
export * from './inputEvents';
|
21
21
|
export { default as getLocalizationTable, matchingLocalizationTable, } from './localizations/getLocalizationTable';
|
22
22
|
export * from './localization';
|
23
|
-
export { default as SVGLoader } from './SVGLoader/SVGLoader';
|
23
|
+
export { default as SVGLoader, SVGLoaderPlugin } from './SVGLoader/SVGLoader';
|
24
24
|
export { default as Viewport } from './Viewport';
|
25
25
|
export * from '@js-draw/math';
|
26
26
|
export * from './components/lib';
|
@@ -67,9 +67,9 @@ const localization = {
|
|
67
67
|
toolEnabledAnnouncement: (toolName) => `${toolName} aktiviert`,
|
68
68
|
toolDisabledAnnouncement: (toolName) => `${toolName} deaktiviert`,
|
69
69
|
updatedViewport: 'Transformierte Ansicht',
|
70
|
-
transformedElements: (elemCount) => `${elemCount} Element${1 === elemCount ? '' : 'e'} transformiert`,
|
70
|
+
transformedElements: (elemCount, action) => `${elemCount} Element${1 === elemCount ? '' : 'e'} transformiert (${action})`,
|
71
71
|
resizeOutputCommand: (newSize) => `Bildgröße auf ${newSize.w}x${newSize.h} geändert`,
|
72
|
-
|
72
|
+
addComponentAction: (componentDescription) => `${componentDescription} hinzugefügt`,
|
73
73
|
eraseAction: (elemDescription, countErased) => `${countErased} ${elemDescription} gelöscht`,
|
74
74
|
duplicateAction: (elemDescription, countErased) => `${countErased} ${elemDescription} dupliziert`,
|
75
75
|
inverseOf: (actionDescription) => `${actionDescription} umgekehrt`,
|
@@ -1,11 +1,12 @@
|
|
1
1
|
import { Color4 } from '@js-draw/math';
|
2
|
-
interface
|
2
|
+
export interface StrokeStyle {
|
3
|
+
readonly color: Color4;
|
4
|
+
/** Note: The stroke `width` is twice the stroke radius. */
|
5
|
+
readonly width: number;
|
6
|
+
}
|
7
|
+
export interface RenderingStyle {
|
3
8
|
readonly fill: Color4;
|
4
|
-
readonly stroke?:
|
5
|
-
readonly color: Color4;
|
6
|
-
/** Note: The stroke `width` is twice the stroke radius. */
|
7
|
-
readonly width: number;
|
8
|
-
};
|
9
|
+
readonly stroke?: StrokeStyle;
|
9
10
|
}
|
10
11
|
export default RenderingStyle;
|
11
12
|
export declare const cloneStyle: (style: RenderingStyle) => {
|
@@ -4,5 +4,5 @@ export { default as SVGRenderer } from './renderers/SVGRenderer';
|
|
4
4
|
export { default as CanvasRenderer } from './renderers/CanvasRenderer';
|
5
5
|
export { default as Display, RenderingMode } from './Display';
|
6
6
|
export { default as TextRenderingStyle } from './TextRenderingStyle';
|
7
|
-
export { default as RenderingStyle } from './RenderingStyle';
|
7
|
+
export { default as RenderingStyle, StrokeStyle as StrokeRenerdingStyle } from './RenderingStyle';
|
8
8
|
export { pathToRenderable, pathFromRenderable, visualEquivalent as pathVisualEquivalent, default as RenderablePathSpec, } from './RenderablePathSpec';
|
@@ -142,6 +142,8 @@ class AbstractRenderer {
|
|
142
142
|
this.selfTransform = transform;
|
143
143
|
}
|
144
144
|
pushTransform(transform) {
|
145
|
+
// Draw all pending paths that used the previous transform (if any).
|
146
|
+
this.flushPath();
|
145
147
|
this.transformStack.push(this.selfTransform);
|
146
148
|
this.setTransform(this.getCanvasToScreenTransform().rightMul(transform));
|
147
149
|
}
|
@@ -149,6 +151,8 @@ class AbstractRenderer {
|
|
149
151
|
if (this.transformStack.length === 0) {
|
150
152
|
throw new Error('Unable to pop more transforms than have been pushed!');
|
151
153
|
}
|
154
|
+
// Draw all pending paths that used the old transform (if any):
|
155
|
+
this.flushPath();
|
152
156
|
this.setTransform(this.transformStack.pop() ?? null);
|
153
157
|
}
|
154
158
|
// Get the matrix that transforms a vector on the canvas to a vector on this'
|
@@ -42,6 +42,15 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
42
42
|
private clipLevels;
|
43
43
|
startObject(boundingBox: Rect2, clip?: boolean): void;
|
44
44
|
endObject(): void;
|
45
|
+
/**
|
46
|
+
* Returns a reference to the underlying `CanvasRenderingContext2D`.
|
47
|
+
* This can be used to render custom content not supported by {@link AbstractRenderer}.
|
48
|
+
* However, such content won't support {@link SVGRenderer} or {@link TextOnlyRenderer}
|
49
|
+
* by default.
|
50
|
+
*
|
51
|
+
* Use with caution.
|
52
|
+
*/
|
53
|
+
drawWithRawRenderingContext(callback: (ctx: CanvasRenderingContext2D) => void): void;
|
45
54
|
drawPoints(...points: Point2[]): void;
|
46
55
|
isTooSmallToRender(rect: Rect2): boolean;
|
47
56
|
static fromViewport(exportViewport: Viewport, options?: {
|
@@ -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;
|