js-draw 1.2.2 → 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 +29 -29
- package/dist/Editor.css +65 -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/lib.d.ts +2 -45
- package/dist/cjs/lib.js +2 -45
- package/dist/cjs/rendering/RenderingStyle.d.ts +1 -0
- 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/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/lib.d.ts +2 -45
- package/dist/mjs/lib.mjs +2 -45
- package/dist/mjs/rendering/RenderingStyle.d.ts +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/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/EdgeToolbar.scss +4 -1
- package/src/toolbar/widgets/DocumentPropertiesWidget.scss +12 -0
- package/src/toolbar/widgets/components/makeGridSelector.scss +1 -1
- package/src/tools/ScrollbarTool.scss +57 -0
- package/src/tools/{SoundUITool.css → SoundUITool.scss} +4 -0
- package/src/tools/tools.scss +2 -1
@@ -11,8 +11,8 @@ declare class UndoRedoHistory {
|
|
11
11
|
constructor(editor: Editor, announceRedoCallback: AnnounceRedoCallback, announceUndoCallback: AnnounceUndoCallback);
|
12
12
|
private fireUpdateEvent;
|
13
13
|
push(command: Command, apply?: boolean): void;
|
14
|
-
undo(): void
|
15
|
-
redo(): void
|
14
|
+
undo(): void | Promise<void>;
|
15
|
+
redo(): void | Promise<void>;
|
16
16
|
get undoStackSize(): number;
|
17
17
|
get redoStackSize(): number;
|
18
18
|
}
|
@@ -58,26 +58,28 @@ class UndoRedoHistory {
|
|
58
58
|
const command = __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").pop();
|
59
59
|
if (command) {
|
60
60
|
__classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").push(command);
|
61
|
-
command.unapply(this.editor);
|
61
|
+
const result = command.unapply(this.editor);
|
62
62
|
this.announceUndoCallback(command);
|
63
63
|
this.fireUpdateEvent(UndoEventType.CommandUndone, command);
|
64
64
|
this.editor.notifier.dispatch(EditorEventType.CommandUndone, {
|
65
65
|
kind: EditorEventType.CommandUndone,
|
66
66
|
command,
|
67
67
|
});
|
68
|
+
return result;
|
68
69
|
}
|
69
70
|
}
|
70
71
|
redo() {
|
71
72
|
const command = __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").pop();
|
72
73
|
if (command) {
|
73
74
|
__classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").push(command);
|
74
|
-
command.apply(this.editor);
|
75
|
+
const result = command.apply(this.editor);
|
75
76
|
this.announceRedoCallback(command);
|
76
77
|
this.fireUpdateEvent(UndoEventType.CommandRedone, command);
|
77
78
|
this.editor.notifier.dispatch(EditorEventType.CommandDone, {
|
78
79
|
kind: EditorEventType.CommandDone,
|
79
80
|
command,
|
80
81
|
});
|
82
|
+
return result;
|
81
83
|
}
|
82
84
|
}
|
83
85
|
get undoStackSize() {
|
package/dist/mjs/Viewport.d.ts
CHANGED
@@ -18,6 +18,7 @@ export declare class Viewport {
|
|
18
18
|
* useful when rendering with a temporarily different viewport.
|
19
19
|
*/
|
20
20
|
getTemporaryClone(): Viewport;
|
21
|
+
/** Resizes the screen rect to the given size. @internal */
|
21
22
|
updateScreenSize(screenSize: Vec2): void;
|
22
23
|
/** Get the screen's visible region transformed into canvas space. */
|
23
24
|
get visibleRect(): Rect2;
|
@@ -34,7 +35,7 @@ export declare class Viewport {
|
|
34
35
|
resetTransform(newTransform?: Mat33): void;
|
35
36
|
get screenToCanvasTransform(): Mat33;
|
36
37
|
get canvasToScreenTransform(): Mat33;
|
37
|
-
/** @returns the size of the visible region in pixels. */
|
38
|
+
/** @returns the size of the visible region in pixels (screen units). */
|
38
39
|
getScreenRectSize(): Vec2;
|
39
40
|
/** Alias for `getScreenRectSize`. @deprecated */
|
40
41
|
getResolution(): Vec3;
|
package/dist/mjs/Viewport.mjs
CHANGED
@@ -32,7 +32,7 @@ export class Viewport {
|
|
32
32
|
result.screenRect = this.screenRect;
|
33
33
|
return result;
|
34
34
|
}
|
35
|
-
|
35
|
+
/** Resizes the screen rect to the given size. @internal */
|
36
36
|
updateScreenSize(screenSize) {
|
37
37
|
this.screenRect = this.screenRect.resizedTo(screenSize);
|
38
38
|
}
|
@@ -68,7 +68,7 @@ export class Viewport {
|
|
68
68
|
get canvasToScreenTransform() {
|
69
69
|
return this.transform;
|
70
70
|
}
|
71
|
-
/** @returns the size of the visible region in pixels. */
|
71
|
+
/** @returns the size of the visible region in pixels (screen units). */
|
72
72
|
getScreenRectSize() {
|
73
73
|
return this.screenRect.size;
|
74
74
|
}
|
@@ -150,8 +150,17 @@ export class Viewport {
|
|
150
150
|
// Computes and returns an affine transformation that makes `toMakeVisible` visible and roughly centered on the screen.
|
151
151
|
computeZoomToTransform(toMakeVisible, allowZoomIn = true, allowZoomOut = true) {
|
152
152
|
let transform = Mat33.identity;
|
153
|
+
// Invalid size? (Would divide by zero)
|
153
154
|
if (toMakeVisible.w === 0 || toMakeVisible.h === 0) {
|
154
|
-
|
155
|
+
// Create a new rectangle with a valid size
|
156
|
+
let newSize = Math.max(toMakeVisible.w, toMakeVisible.h);
|
157
|
+
// Choose a reasonable default size, but don't zoom.
|
158
|
+
if (newSize === 0) {
|
159
|
+
newSize = 50;
|
160
|
+
allowZoomIn = false;
|
161
|
+
allowZoomOut = false;
|
162
|
+
}
|
163
|
+
toMakeVisible = new Rect2(toMakeVisible.x, toMakeVisible.y, newSize, newSize);
|
155
164
|
}
|
156
165
|
if (isNaN(toMakeVisible.size.magnitude())) {
|
157
166
|
throw new Error(`${toMakeVisible.toString()} rectangle has NaN size! Cannot zoom to!`);
|
@@ -5,6 +5,7 @@ export declare abstract class Command {
|
|
5
5
|
abstract unapply(editor: Editor): Promise<void> | void;
|
6
6
|
onDrop(_editor: Editor): void;
|
7
7
|
abstract description(editor: Editor, localizationTable: EditorLocalization): string;
|
8
|
+
/** @deprecated Use {@link uniteCommands} */
|
8
9
|
static union(a: Command, b: Command): Command;
|
9
10
|
static readonly empty: {
|
10
11
|
description(_editor: Editor, _localizationTable: EditorLocalization): string;
|
@@ -32,8 +32,8 @@ class Erase extends SerializableCommand {
|
|
32
32
|
for (const part of this.toRemove) {
|
33
33
|
const parent = editor.image.findParent(part);
|
34
34
|
if (parent) {
|
35
|
-
editor.image.onDestroyElement(part);
|
36
35
|
parent.remove();
|
36
|
+
editor.image.onDestroyElement(part);
|
37
37
|
}
|
38
38
|
}
|
39
39
|
this.applied = true;
|
@@ -2,7 +2,7 @@ import Editor from '../Editor';
|
|
2
2
|
import Command from './Command';
|
3
3
|
export type DeserializationCallback = (data: Record<string, any> | any[], editor: Editor) => SerializableCommand;
|
4
4
|
export default abstract class SerializableCommand extends Command {
|
5
|
-
private
|
5
|
+
#private;
|
6
6
|
constructor(commandTypeId: string);
|
7
7
|
protected abstract serializeToJSON(): string | Record<string, any> | any[];
|
8
8
|
private static deserializationCallbacks;
|
@@ -1,11 +1,24 @@
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
6
|
+
};
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
|
+
};
|
12
|
+
var _SerializableCommand_commandTypeId;
|
1
13
|
import Command from './Command.mjs';
|
2
14
|
class SerializableCommand extends Command {
|
3
15
|
constructor(commandTypeId) {
|
4
16
|
super();
|
5
|
-
this
|
17
|
+
_SerializableCommand_commandTypeId.set(this, void 0);
|
6
18
|
if (!(commandTypeId in SerializableCommand.deserializationCallbacks)) {
|
7
19
|
throw new Error(`Command ${commandTypeId} must have a registered deserialization callback. To do this, call SerializableCommand.register.`);
|
8
20
|
}
|
21
|
+
__classPrivateFieldSet(this, _SerializableCommand_commandTypeId, commandTypeId, "f");
|
9
22
|
}
|
10
23
|
// Convert this command to an object that can be passed to `JSON.stringify`.
|
11
24
|
//
|
@@ -14,7 +27,7 @@ class SerializableCommand extends Command {
|
|
14
27
|
serialize() {
|
15
28
|
return {
|
16
29
|
data: this.serializeToJSON(),
|
17
|
-
commandType: this
|
30
|
+
commandType: __classPrivateFieldGet(this, _SerializableCommand_commandTypeId, "f"),
|
18
31
|
};
|
19
32
|
}
|
20
33
|
// Convert a `string` containing JSON data (or the output of `JSON.parse`) into a
|
@@ -33,5 +46,6 @@ class SerializableCommand extends Command {
|
|
33
46
|
SerializableCommand.deserializationCallbacks[commandTypeId] = deserialize;
|
34
47
|
}
|
35
48
|
}
|
49
|
+
_SerializableCommand_commandTypeId = new WeakMap();
|
36
50
|
SerializableCommand.deserializationCallbacks = {};
|
37
51
|
export default SerializableCommand;
|
@@ -13,6 +13,8 @@ export interface CommandLocalization {
|
|
13
13
|
updatedViewport: string;
|
14
14
|
transformedElements: (elemCount: number) => string;
|
15
15
|
resizeOutputCommand: (newSize: Rect2) => string;
|
16
|
+
enabledAutoresizeOutputCommand: string;
|
17
|
+
disabledAutoresizeOutputCommand: string;
|
16
18
|
addElementAction: (elemDescription: string) => string;
|
17
19
|
eraseAction: (elemDescription: string, numElems: number) => string;
|
18
20
|
duplicateAction: (elemDescription: string, count: number) => string;
|
@@ -2,6 +2,8 @@ export const defaultCommandLocalization = {
|
|
2
2
|
updatedViewport: 'Transformed Viewport',
|
3
3
|
transformedElements: (elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`,
|
4
4
|
resizeOutputCommand: (newSize) => `Resized image to ${newSize.w}x${newSize.h}`,
|
5
|
+
enabledAutoresizeOutputCommand: 'Enabled output autoresize',
|
6
|
+
disabledAutoresizeOutputCommand: 'Disabled output autoresize',
|
5
7
|
addElementAction: (componentDescription) => `Added ${componentDescription}`,
|
6
8
|
eraseAction: (componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`,
|
7
9
|
duplicateAction: (componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`,
|
@@ -6,12 +6,41 @@ import { ImageComponentLocalization } from './localization';
|
|
6
6
|
export type LoadSaveData = (string[] | Record<symbol, string | number>);
|
7
7
|
export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
|
8
8
|
export type DeserializeCallback = (data: string) => AbstractComponent;
|
9
|
+
export declare enum ComponentSizingMode {
|
10
|
+
/** The default. The compnent gets its size from its bounding box. */
|
11
|
+
BoundingBox = 0,
|
12
|
+
/** Causes the component to fill the entire visible region of the screen */
|
13
|
+
FillScreen = 1,
|
14
|
+
/**
|
15
|
+
* Displays the component anywhere (arbitrary location) on the
|
16
|
+
* canvas. (Ignoring the bounding box).
|
17
|
+
*
|
18
|
+
* These components may be ignored unless a full render is done.
|
19
|
+
*
|
20
|
+
* Intended for compnents that need to be rendered on a full export,
|
21
|
+
* but won't be visible to the user.
|
22
|
+
*
|
23
|
+
* For example, a metadata component.
|
24
|
+
*/
|
25
|
+
Anywhere = 2
|
26
|
+
}
|
9
27
|
/**
|
10
28
|
* A base class for everything that can be added to an {@link EditorImage}.
|
11
29
|
*/
|
12
30
|
export default abstract class AbstractComponent {
|
13
31
|
private readonly componentKind;
|
14
32
|
protected lastChangedTime: number;
|
33
|
+
/**
|
34
|
+
* The bounding box of this component.
|
35
|
+
* {@link getBBox}, by default, returns `contentBBox`.
|
36
|
+
* This must be set by components.
|
37
|
+
*
|
38
|
+
* If this changes, {@link EditorImage.queueRerenderOf} should be called for
|
39
|
+
* this object (provided that this object has been added to the editor.)
|
40
|
+
*
|
41
|
+
* **Note**: This value is ignored if {@link getSizingMode} returns `FillScreen`
|
42
|
+
* or `FillImage`.
|
43
|
+
*/
|
15
44
|
protected abstract contentBBox: Rect2;
|
16
45
|
private zIndex;
|
17
46
|
private id;
|
@@ -39,6 +68,15 @@ export default abstract class AbstractComponent {
|
|
39
68
|
* @returns the bounding box of this. Unlike `getBBox`, this should **not** be a rough estimate.
|
40
69
|
*/
|
41
70
|
getExactBBox(): Rect2;
|
71
|
+
/**
|
72
|
+
* Returns information about how this component should be displayed
|
73
|
+
* (e.g. fill the screen or get its size from {@link getBBox}).
|
74
|
+
*
|
75
|
+
* {@link EditorImage.queueRerenderOf} must be called to apply changes to
|
76
|
+
* the output of this method if this component has already been added to an
|
77
|
+
* {@link EditorImage}.
|
78
|
+
*/
|
79
|
+
getSizingMode(): ComponentSizingMode;
|
42
80
|
/** Called when this component is added to the given image. */
|
43
81
|
onAddToImage(_image: EditorImage): void;
|
44
82
|
onRemoveFromImage(): void;
|
@@ -7,6 +7,25 @@ import SerializableCommand from '../commands/SerializableCommand.mjs';
|
|
7
7
|
import EditorImage from '../EditorImage.mjs';
|
8
8
|
import { Mat33 } from '@js-draw/math';
|
9
9
|
import UnresolvedSerializableCommand from '../commands/UnresolvedCommand.mjs';
|
10
|
+
export var ComponentSizingMode;
|
11
|
+
(function (ComponentSizingMode) {
|
12
|
+
/** The default. The compnent gets its size from its bounding box. */
|
13
|
+
ComponentSizingMode[ComponentSizingMode["BoundingBox"] = 0] = "BoundingBox";
|
14
|
+
/** Causes the component to fill the entire visible region of the screen */
|
15
|
+
ComponentSizingMode[ComponentSizingMode["FillScreen"] = 1] = "FillScreen";
|
16
|
+
/**
|
17
|
+
* Displays the component anywhere (arbitrary location) on the
|
18
|
+
* canvas. (Ignoring the bounding box).
|
19
|
+
*
|
20
|
+
* These components may be ignored unless a full render is done.
|
21
|
+
*
|
22
|
+
* Intended for compnents that need to be rendered on a full export,
|
23
|
+
* but won't be visible to the user.
|
24
|
+
*
|
25
|
+
* For example, a metadata component.
|
26
|
+
*/
|
27
|
+
ComponentSizingMode[ComponentSizingMode["Anywhere"] = 2] = "Anywhere";
|
28
|
+
})(ComponentSizingMode || (ComponentSizingMode = {}));
|
10
29
|
/**
|
11
30
|
* A base class for everything that can be added to an {@link EditorImage}.
|
12
31
|
*/
|
@@ -72,6 +91,17 @@ class AbstractComponent {
|
|
72
91
|
getExactBBox() {
|
73
92
|
return this.getBBox();
|
74
93
|
}
|
94
|
+
/**
|
95
|
+
* Returns information about how this component should be displayed
|
96
|
+
* (e.g. fill the screen or get its size from {@link getBBox}).
|
97
|
+
*
|
98
|
+
* {@link EditorImage.queueRerenderOf} must be called to apply changes to
|
99
|
+
* the output of this method if this component has already been added to an
|
100
|
+
* {@link EditorImage}.
|
101
|
+
*/
|
102
|
+
getSizingMode() {
|
103
|
+
return ComponentSizingMode.BoundingBox;
|
104
|
+
}
|
75
105
|
/** Called when this component is added to the given image. */
|
76
106
|
onAddToImage(_image) { }
|
77
107
|
onRemoveFromImage() { }
|
@@ -3,7 +3,7 @@ import EditorImage from '../EditorImage';
|
|
3
3
|
import SerializableCommand from '../commands/SerializableCommand';
|
4
4
|
import { LineSegment2, Mat33, Rect2, Color4 } from '@js-draw/math';
|
5
5
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
6
|
-
import AbstractComponent from './AbstractComponent';
|
6
|
+
import AbstractComponent, { ComponentSizingMode } from './AbstractComponent';
|
7
7
|
import { ImageComponentLocalization } from './localization';
|
8
8
|
import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
|
9
9
|
export declare enum BackgroundType {
|
@@ -24,6 +24,8 @@ export default class BackgroundComponent extends AbstractComponent implements Re
|
|
24
24
|
private mainColor;
|
25
25
|
protected contentBBox: Rect2;
|
26
26
|
private viewportSizeChangeListener;
|
27
|
+
private autoresizeChangedListener;
|
28
|
+
private fillsScreen;
|
27
29
|
private gridSize;
|
28
30
|
private gridStrokeWidth;
|
29
31
|
private secondaryColor;
|
@@ -41,10 +43,17 @@ export default class BackgroundComponent extends AbstractComponent implements Re
|
|
41
43
|
onRemoveFromImage(): void;
|
42
44
|
private recomputeBBox;
|
43
45
|
private generateGridPath;
|
46
|
+
/**
|
47
|
+
* @returns this background's bounding box if the screen size is taken into
|
48
|
+
* account (which may be necessary if this component is configured to fill the
|
49
|
+
* entire screen).
|
50
|
+
*/
|
51
|
+
private getFullBoundingBox;
|
44
52
|
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
45
53
|
intersects(lineSegment: LineSegment2): boolean;
|
46
54
|
isSelectable(): boolean;
|
47
55
|
isBackground(): boolean;
|
56
|
+
getSizingMode(): ComponentSizingMode;
|
48
57
|
protected serializeToJSON(): {
|
49
58
|
mainColor: string;
|
50
59
|
secondaryColor: string | undefined;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { EditorImageEventType } from '../EditorImage.mjs';
|
2
2
|
import { Rect2, Color4, toRoundedString, Path, PathCommandType, Vec2 } from '@js-draw/math';
|
3
|
-
import AbstractComponent from './AbstractComponent.mjs';
|
3
|
+
import AbstractComponent, { ComponentSizingMode } from './AbstractComponent.mjs';
|
4
4
|
import { createRestyleComponentCommand } from './RestylableComponent.mjs';
|
5
5
|
import Viewport from '../Viewport.mjs';
|
6
6
|
import { pathToRenderable } from '../rendering/RenderablePathSpec.mjs';
|
@@ -28,6 +28,10 @@ export default class BackgroundComponent extends AbstractComponent {
|
|
28
28
|
this.backgroundType = backgroundType;
|
29
29
|
this.mainColor = mainColor;
|
30
30
|
this.viewportSizeChangeListener = null;
|
31
|
+
this.autoresizeChangedListener = null;
|
32
|
+
// Whether the background should grow/shrink to match the screen size,
|
33
|
+
// rather than being clipped to the image boundaries.
|
34
|
+
this.fillsScreen = false;
|
31
35
|
this.gridSize = Viewport.getGridSize(2);
|
32
36
|
this.gridStrokeWidth = 0.7;
|
33
37
|
this.secondaryColor = null;
|
@@ -102,22 +106,37 @@ export default class BackgroundComponent extends AbstractComponent {
|
|
102
106
|
this.viewportSizeChangeListener = image.notifier.on(EditorImageEventType.ExportViewportChanged, () => {
|
103
107
|
this.recomputeBBox(image);
|
104
108
|
});
|
109
|
+
this.autoresizeChangedListener = image.notifier.on(EditorImageEventType.AutoresizeModeChanged, () => {
|
110
|
+
this.recomputeBBox(image);
|
111
|
+
});
|
105
112
|
this.recomputeBBox(image);
|
106
113
|
}
|
107
114
|
onRemoveFromImage() {
|
108
115
|
this.viewportSizeChangeListener?.remove();
|
116
|
+
this.autoresizeChangedListener?.remove();
|
109
117
|
this.viewportSizeChangeListener = null;
|
118
|
+
this.autoresizeChangedListener = null;
|
110
119
|
}
|
111
120
|
recomputeBBox(image) {
|
112
121
|
const importExportRect = image.getImportExportViewport().visibleRect;
|
122
|
+
let needsRerender = false;
|
113
123
|
if (!this.contentBBox.eq(importExportRect)) {
|
114
124
|
this.contentBBox = importExportRect;
|
115
|
-
|
125
|
+
needsRerender = true;
|
126
|
+
}
|
127
|
+
const imageAutoresizes = image.getAutoresizeEnabled();
|
128
|
+
if (imageAutoresizes !== this.fillsScreen) {
|
129
|
+
this.fillsScreen = imageAutoresizes;
|
130
|
+
needsRerender = true;
|
131
|
+
}
|
132
|
+
if (needsRerender) {
|
133
|
+
// Re-renders this if already added to the EditorImage.
|
116
134
|
image.queueRerenderOf(this);
|
117
135
|
}
|
118
136
|
}
|
119
137
|
generateGridPath(visibleRect) {
|
120
|
-
const
|
138
|
+
const contentBBox = this.getFullBoundingBox(visibleRect);
|
139
|
+
const targetRect = visibleRect?.grownBy(this.gridStrokeWidth)?.intersection(contentBBox) ?? contentBBox;
|
121
140
|
const roundDownToGrid = (coord) => Math.floor(coord / this.gridSize) * this.gridSize;
|
122
141
|
const roundUpToGrid = (coord) => Math.ceil(coord / this.gridSize) * this.gridSize;
|
123
142
|
const startY = roundUpToGrid(targetRect.y);
|
@@ -158,24 +177,33 @@ export default class BackgroundComponent extends AbstractComponent {
|
|
158
177
|
}
|
159
178
|
return new Path(startPoint, result);
|
160
179
|
}
|
180
|
+
/**
|
181
|
+
* @returns this background's bounding box if the screen size is taken into
|
182
|
+
* account (which may be necessary if this component is configured to fill the
|
183
|
+
* entire screen).
|
184
|
+
*/
|
185
|
+
getFullBoundingBox(visibleRect) {
|
186
|
+
return (this.fillsScreen ? visibleRect : this.contentBBox) ?? this.contentBBox;
|
187
|
+
}
|
161
188
|
render(canvas, visibleRect) {
|
162
189
|
if (this.backgroundType === BackgroundType.None) {
|
163
190
|
return;
|
164
191
|
}
|
165
192
|
const clip = this.backgroundType === BackgroundType.Grid;
|
166
|
-
|
193
|
+
const contentBBox = this.getFullBoundingBox(visibleRect);
|
194
|
+
canvas.startObject(contentBBox, clip);
|
167
195
|
if (this.backgroundType === BackgroundType.SolidColor || this.backgroundType === BackgroundType.Grid) {
|
168
196
|
// If the rectangle for this region contains the visible rect,
|
169
197
|
// we can fill the entire visible rectangle (which may be more efficient than
|
170
198
|
// filling the entire region for this.)
|
171
199
|
if (visibleRect) {
|
172
|
-
const intersection = visibleRect.intersection(
|
200
|
+
const intersection = visibleRect.intersection(contentBBox);
|
173
201
|
if (intersection) {
|
174
202
|
canvas.fillRect(intersection, this.mainColor);
|
175
203
|
}
|
176
204
|
}
|
177
205
|
else {
|
178
|
-
canvas.fillRect(
|
206
|
+
canvas.fillRect(contentBBox, this.mainColor);
|
179
207
|
}
|
180
208
|
}
|
181
209
|
if (this.backgroundType === BackgroundType.Grid) {
|
@@ -213,6 +241,9 @@ export default class BackgroundComponent extends AbstractComponent {
|
|
213
241
|
isBackground() {
|
214
242
|
return true;
|
215
243
|
}
|
244
|
+
getSizingMode() {
|
245
|
+
return this.fillsScreen ? ComponentSizingMode.FillScreen : ComponentSizingMode.BoundingBox;
|
246
|
+
}
|
216
247
|
serializeToJSON() {
|
217
248
|
return {
|
218
249
|
mainColor: this.mainColor.toHexString(),
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { LineSegment2, Mat33, Rect2 } from '@js-draw/math';
|
2
2
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
3
|
-
import AbstractComponent from './AbstractComponent';
|
3
|
+
import AbstractComponent, { ComponentSizingMode } from './AbstractComponent';
|
4
4
|
import { ImageComponentLocalization } from './localization';
|
5
5
|
type GlobalAttrsList = Array<[string, string | null]>;
|
6
6
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
@@ -11,6 +11,7 @@ export default class SVGGlobalAttributesObject extends AbstractComponent {
|
|
11
11
|
intersects(_lineSegment: LineSegment2): boolean;
|
12
12
|
protected applyTransformation(_affineTransfm: Mat33): void;
|
13
13
|
isSelectable(): boolean;
|
14
|
+
getSizingMode(): ComponentSizingMode;
|
14
15
|
protected createClone(): SVGGlobalAttributesObject;
|
15
16
|
description(localization: ImageComponentLocalization): string;
|
16
17
|
protected serializeToJSON(): string | null;
|
@@ -6,7 +6,7 @@
|
|
6
6
|
//
|
7
7
|
import { Rect2 } from '@js-draw/math';
|
8
8
|
import SVGRenderer from '../rendering/renderers/SVGRenderer.mjs';
|
9
|
-
import AbstractComponent from './AbstractComponent.mjs';
|
9
|
+
import AbstractComponent, { ComponentSizingMode } from './AbstractComponent.mjs';
|
10
10
|
const componentKind = 'svg-global-attributes';
|
11
11
|
// Stores global SVG attributes (e.g. namespace identifiers.)
|
12
12
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
@@ -32,6 +32,12 @@ export default class SVGGlobalAttributesObject extends AbstractComponent {
|
|
32
32
|
isSelectable() {
|
33
33
|
return false;
|
34
34
|
}
|
35
|
+
getSizingMode() {
|
36
|
+
// This component can be shown anywhere (it won't be
|
37
|
+
// visible to the user, it just needs to be saved with
|
38
|
+
// the image).
|
39
|
+
return ComponentSizingMode.Anywhere;
|
40
|
+
}
|
35
41
|
createClone() {
|
36
42
|
return new SVGGlobalAttributesObject(this.attrs);
|
37
43
|
}
|
@@ -48,6 +48,7 @@ export default class Stroke extends AbstractComponent implements RestyleableComp
|
|
48
48
|
updateStyle(style: ComponentStyle): SerializableCommand;
|
49
49
|
forceStyle(style: ComponentStyle, editor: Editor | null): void;
|
50
50
|
intersects(line: LineSegment2): boolean;
|
51
|
+
intersectsRect(rect: Rect2): boolean;
|
51
52
|
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
52
53
|
getProportionalRenderingTime(): number;
|
53
54
|
private bboxForPart;
|
@@ -122,6 +122,50 @@ export default class Stroke extends AbstractComponent {
|
|
122
122
|
}
|
123
123
|
return false;
|
124
124
|
}
|
125
|
+
intersectsRect(rect) {
|
126
|
+
// AbstractComponent::intersectsRect can be inexact for strokes with non-zero
|
127
|
+
// stroke radius (has many false negatives). As such, additional checks are
|
128
|
+
// done here, before passing to the superclass.
|
129
|
+
if (!rect.intersects(this.getBBox())) {
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
// The following check only checks for the positive case:
|
133
|
+
// Sample a set of points that are known to be within each part of this
|
134
|
+
// stroke. For example, the points marked with an "x" below:
|
135
|
+
// ___________________
|
136
|
+
// / \
|
137
|
+
// | x x |
|
138
|
+
// \_____________ |
|
139
|
+
// | x |
|
140
|
+
// \_____/
|
141
|
+
//
|
142
|
+
// Because we don't want the following case to result in selection,
|
143
|
+
// __________________
|
144
|
+
// /.___. \
|
145
|
+
// || x | x | <- /* The
|
146
|
+
// |·---· | .___.
|
147
|
+
// \____________ | | |
|
148
|
+
// | x | ·---·
|
149
|
+
// \_____/ denotes the input rectangle */
|
150
|
+
//
|
151
|
+
// we need to ensure that the rectangle intersects each point **and** the
|
152
|
+
// edge of the rectangle.
|
153
|
+
for (const part of this.parts) {
|
154
|
+
// As such, we need to shrink the input rectangle to verify that the original,
|
155
|
+
// unshrunken rectangle would have intersected the edge of the stroke if it
|
156
|
+
// intersects a point within the stroke.
|
157
|
+
const interiorRect = rect.grownBy(-(part.style.stroke?.width ?? 0));
|
158
|
+
if (interiorRect.area === 0) {
|
159
|
+
continue;
|
160
|
+
}
|
161
|
+
for (const point of part.path.startEndPoints()) {
|
162
|
+
if (interiorRect.containsPoint(point)) {
|
163
|
+
return true;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
return super.intersectsRect(rect);
|
168
|
+
}
|
125
169
|
render(canvas, visibleRect) {
|
126
170
|
canvas.startObject(this.getBBox());
|
127
171
|
for (const part of this.parts) {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { LineSegment2, Mat33, Rect2 } from '@js-draw/math';
|
2
2
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
3
|
-
import AbstractComponent from './AbstractComponent';
|
3
|
+
import AbstractComponent, { ComponentSizingMode } from './AbstractComponent';
|
4
4
|
import { ImageComponentLocalization } from './localization';
|
5
5
|
export default class UnknownSVGObject extends AbstractComponent {
|
6
6
|
private svgObject;
|
@@ -10,6 +10,7 @@ export default class UnknownSVGObject extends AbstractComponent {
|
|
10
10
|
intersects(lineSegment: LineSegment2): boolean;
|
11
11
|
protected applyTransformation(_affineTransfm: Mat33): void;
|
12
12
|
isSelectable(): boolean;
|
13
|
+
getSizingMode(): ComponentSizingMode;
|
13
14
|
protected createClone(): AbstractComponent;
|
14
15
|
description(localization: ImageComponentLocalization): string;
|
15
16
|
protected serializeToJSON(): string | null;
|
@@ -5,7 +5,7 @@
|
|
5
5
|
//
|
6
6
|
import { Rect2 } from '@js-draw/math';
|
7
7
|
import SVGRenderer from '../rendering/renderers/SVGRenderer.mjs';
|
8
|
-
import AbstractComponent from './AbstractComponent.mjs';
|
8
|
+
import AbstractComponent, { ComponentSizingMode } from './AbstractComponent.mjs';
|
9
9
|
const componentId = 'unknown-svg-object';
|
10
10
|
export default class UnknownSVGObject extends AbstractComponent {
|
11
11
|
constructor(svgObject) {
|
@@ -30,6 +30,12 @@ export default class UnknownSVGObject extends AbstractComponent {
|
|
30
30
|
isSelectable() {
|
31
31
|
return false;
|
32
32
|
}
|
33
|
+
getSizingMode() {
|
34
|
+
// This component can be shown anywhere (it won't be
|
35
|
+
// visible to the user, it just needs to be saved with
|
36
|
+
// the image).
|
37
|
+
return ComponentSizingMode.Anywhere;
|
38
|
+
}
|
33
39
|
createClone() {
|
34
40
|
return new UnknownSVGObject(this.svgObject.cloneNode(true));
|
35
41
|
}
|
package/dist/mjs/lib.d.ts
CHANGED
@@ -2,52 +2,9 @@
|
|
2
2
|
* The main entrypoint for the NPM package. Everything exported by this file
|
3
3
|
* is available through the [`js-draw` package](https://www.npmjs.com/package/js-draw).
|
4
4
|
*
|
5
|
-
*
|
6
|
-
* ```ts,runnable
|
7
|
-
* import { Editor, Vec3, Mat33, ToolbarWidgetTag } from 'js-draw';
|
5
|
+
* ## Example
|
8
6
|
*
|
9
|
-
*
|
10
|
-
* import { MaterialIconProvider } from '@js-draw/material-icons';
|
11
|
-
*
|
12
|
-
* // Apply js-draw CSS
|
13
|
-
* import 'js-draw/styles';
|
14
|
-
* // If your bundler doesn't support the above, try
|
15
|
-
* // import 'js-draw/bundledStyles';
|
16
|
-
*
|
17
|
-
* (async () => {
|
18
|
-
* const editor = new Editor(document.body, {
|
19
|
-
* iconProvider: new MaterialIconProvider(),
|
20
|
-
* });
|
21
|
-
* const toolbar = editor.addToolbar();
|
22
|
-
*
|
23
|
-
* // Increases the minimum height of the editor
|
24
|
-
* editor.getRootElement().style.height = '600px';
|
25
|
-
*
|
26
|
-
* // Loads from SVG data
|
27
|
-
* await editor.loadFromSVG(`
|
28
|
-
* <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
29
|
-
* <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
|
30
|
-
* <path d="M500,500L500,0L0,0L0,500L500,500" fill="#aaa" class="js-draw-image-background"></path>
|
31
|
-
* <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: #111;">Testing...</text>
|
32
|
-
* </svg>
|
33
|
-
* `);
|
34
|
-
*
|
35
|
-
* // Adding tags to a toolbar button allows different styles to be applied.
|
36
|
-
* // Also see addActionButton.
|
37
|
-
* const buttonLabels = [ ToolbarWidgetTag.Save ];
|
38
|
-
*
|
39
|
-
* toolbar.addSaveButton(() => {
|
40
|
-
* const saveData = editor.toSVG().outerHTML;
|
41
|
-
*
|
42
|
-
* // Do something with saveData
|
43
|
-
* });
|
44
|
-
*
|
45
|
-
* toolbar.addExitButton(() => {
|
46
|
-
* // Save/confirm exiting here?
|
47
|
-
* editor.remove();
|
48
|
-
* });
|
49
|
-
* })();
|
50
|
-
* ```
|
7
|
+
* [[include:doc-pages/inline-examples/main-js-draw-example.md]]
|
51
8
|
*
|
52
9
|
* @see
|
53
10
|
* - {@link Editor}
|