js-draw 0.1.12 → 0.2.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/.eslintrc.js +1 -0
- package/.firebaserc +5 -0
- package/.github/workflows/firebase-hosting-merge.yml +25 -0
- package/.github/workflows/firebase-hosting-pull-request.yml +22 -0
- package/.github/workflows/github-pages.yml +52 -0
- package/CHANGELOG.md +6 -0
- package/README.md +11 -6
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +19 -0
- package/dist/src/Color4.js +24 -3
- package/dist/src/Editor.d.ts +129 -2
- package/dist/src/Editor.js +94 -17
- package/dist/src/EditorImage.d.ts +7 -2
- package/dist/src/EditorImage.js +41 -25
- package/dist/src/EventDispatcher.d.ts +18 -0
- package/dist/src/EventDispatcher.js +19 -4
- package/dist/src/Pointer.js +3 -2
- package/dist/src/UndoRedoHistory.js +15 -2
- package/dist/src/Viewport.js +4 -1
- package/dist/src/bundle/bundled.d.ts +1 -2
- package/dist/src/bundle/bundled.js +1 -2
- package/dist/src/commands/Duplicate.d.ts +1 -1
- package/dist/src/commands/Duplicate.js +3 -4
- package/dist/src/commands/Erase.d.ts +1 -1
- package/dist/src/commands/Erase.js +6 -5
- package/dist/src/commands/SerializableCommand.d.ts +4 -5
- package/dist/src/commands/SerializableCommand.js +12 -4
- package/dist/src/commands/invertCommand.d.ts +4 -0
- package/dist/src/commands/invertCommand.js +44 -0
- package/dist/src/commands/lib.d.ts +6 -0
- package/dist/src/commands/lib.js +6 -0
- package/dist/src/commands/localization.d.ts +1 -0
- package/dist/src/commands/localization.js +1 -0
- package/dist/src/components/AbstractComponent.d.ts +13 -8
- package/dist/src/components/AbstractComponent.js +26 -15
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/src/components/SVGGlobalAttributesObject.js +7 -1
- package/dist/src/components/Stroke.d.ts +12 -2
- package/dist/src/components/Stroke.js +10 -7
- package/dist/src/components/Text.d.ts +2 -2
- package/dist/src/components/Text.js +6 -6
- package/dist/src/components/UnknownSVGObject.d.ts +1 -1
- package/dist/src/components/UnknownSVGObject.js +6 -1
- package/dist/src/components/lib.d.ts +4 -0
- package/dist/src/components/lib.js +4 -0
- package/dist/src/lib.d.ts +25 -0
- package/dist/src/lib.js +25 -0
- package/dist/src/math/Mat33.d.ts +47 -1
- package/dist/src/math/Mat33.js +48 -20
- package/dist/src/math/Path.js +3 -3
- package/dist/src/math/Rect2.d.ts +2 -2
- package/dist/src/math/Vec3.d.ts +62 -0
- package/dist/src/math/Vec3.js +62 -14
- package/dist/src/math/lib.d.ts +7 -0
- package/dist/src/math/lib.js +7 -0
- package/dist/src/math/rounding.js +1 -0
- package/dist/src/rendering/Display.d.ts +44 -0
- package/dist/src/rendering/Display.js +45 -6
- package/dist/src/rendering/caching/CacheRecord.d.ts +1 -0
- package/dist/src/rendering/caching/CacheRecord.js +3 -0
- package/dist/src/rendering/caching/CacheRecordManager.d.ts +4 -3
- package/dist/src/rendering/caching/CacheRecordManager.js +16 -4
- package/dist/src/rendering/caching/RenderingCache.d.ts +2 -3
- package/dist/src/rendering/caching/RenderingCache.js +9 -10
- package/dist/src/rendering/caching/types.d.ts +1 -3
- package/dist/src/rendering/renderers/CanvasRenderer.js +1 -1
- package/dist/src/toolbar/HTMLToolbar.js +1 -0
- package/dist/src/toolbar/makeColorInput.js +1 -1
- package/dist/src/toolbar/widgets/PenWidget.js +1 -0
- package/dist/src/tools/Pen.d.ts +1 -2
- package/dist/src/tools/Pen.js +8 -1
- package/dist/src/tools/PipetteTool.js +1 -0
- package/dist/src/tools/SelectionTool.js +45 -22
- package/dist/src/types.d.ts +17 -6
- package/dist/src/types.js +7 -5
- package/firebase.json +16 -0
- package/package.json +118 -101
- package/src/Color4.ts +23 -2
- package/src/Editor.ts +147 -25
- package/src/EditorImage.ts +45 -27
- package/src/EventDispatcher.ts +21 -6
- package/src/Pointer.ts +3 -2
- package/src/UndoRedoHistory.ts +18 -2
- package/src/Viewport.ts +5 -2
- package/src/bundle/bundled.ts +1 -2
- package/src/commands/Duplicate.ts +3 -4
- package/src/commands/Erase.ts +6 -5
- package/src/commands/SerializableCommand.ts +17 -9
- package/src/commands/invertCommand.ts +51 -0
- package/src/commands/lib.ts +14 -0
- package/src/commands/localization.ts +2 -0
- package/src/components/AbstractComponent.ts +31 -20
- package/src/components/SVGGlobalAttributesObject.ts +8 -1
- package/src/components/Stroke.test.ts +1 -1
- package/src/components/Stroke.ts +11 -7
- package/src/components/Text.ts +6 -7
- package/src/components/UnknownSVGObject.ts +7 -1
- package/src/components/lib.ts +9 -0
- package/src/lib.ts +28 -0
- package/src/math/Mat33.ts +48 -20
- package/src/math/Path.ts +3 -3
- package/src/math/Rect2.ts +2 -2
- package/src/math/Vec3.ts +62 -14
- package/src/math/lib.ts +15 -0
- package/src/math/rounding.ts +2 -0
- package/src/rendering/Display.ts +46 -6
- package/src/rendering/caching/CacheRecord.test.ts +1 -1
- package/src/rendering/caching/CacheRecord.ts +4 -0
- package/src/rendering/caching/CacheRecordManager.ts +33 -7
- package/src/rendering/caching/RenderingCache.ts +10 -15
- package/src/rendering/caching/types.ts +1 -6
- package/src/rendering/renderers/CanvasRenderer.ts +1 -1
- package/src/toolbar/HTMLToolbar.ts +1 -0
- package/src/toolbar/makeColorInput.ts +1 -1
- package/src/toolbar/widgets/PenWidget.ts +2 -0
- package/src/tools/PanZoom.ts +0 -1
- package/src/tools/Pen.ts +11 -2
- package/src/tools/PipetteTool.ts +2 -0
- package/src/tools/SelectionTool.ts +46 -18
- package/src/types.ts +19 -3
- package/tsconfig.json +4 -1
- package/typedoc.json +20 -0
package/dist/src/Color4.d.ts
CHANGED
@@ -1,15 +1,34 @@
|
|
1
1
|
export default class Color4 {
|
2
|
+
/** Red component. Should be in the range [0, 1]. */
|
2
3
|
readonly r: number;
|
4
|
+
/** Green component. `g` ∈ [0, 1] */
|
3
5
|
readonly g: number;
|
6
|
+
/** Blue component. `b` ∈ [0, 1] */
|
4
7
|
readonly b: number;
|
8
|
+
/** Alpha/transparent component. `a` ∈ [0, 1] */
|
5
9
|
readonly a: number;
|
6
10
|
private constructor();
|
11
|
+
/**
|
12
|
+
* Create a color from red, green, blue components. The color is fully opaque (`a = 1.0`).
|
13
|
+
*
|
14
|
+
* Each component should be in the range [0, 1].
|
15
|
+
*/
|
7
16
|
static ofRGB(red: number, green: number, blue: number): Color4;
|
8
17
|
static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4;
|
9
18
|
static fromHex(hexString: string): Color4;
|
19
|
+
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
10
20
|
static fromString(text: string): Color4;
|
21
|
+
/** @returns true if `this` and `other` are approximately equal. */
|
11
22
|
eq(other: Color4 | null | undefined): boolean;
|
12
23
|
private hexString;
|
24
|
+
/**
|
25
|
+
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
26
|
+
*
|
27
|
+
* @example
|
28
|
+
* ```
|
29
|
+
* Color4.red.toHexString(); // -> #ff0000ff
|
30
|
+
* ```
|
31
|
+
*/
|
13
32
|
toHexString(): string;
|
14
33
|
static transparent: Color4;
|
15
34
|
static red: Color4;
|
package/dist/src/Color4.js
CHANGED
@@ -1,12 +1,24 @@
|
|
1
1
|
export default class Color4 {
|
2
|
-
constructor(
|
2
|
+
constructor(
|
3
|
+
/** Red component. Should be in the range [0, 1]. */
|
4
|
+
r,
|
5
|
+
/** Green component. `g` ∈ [0, 1] */
|
6
|
+
g,
|
7
|
+
/** Blue component. `b` ∈ [0, 1] */
|
8
|
+
b,
|
9
|
+
/** Alpha/transparent component. `a` ∈ [0, 1] */
|
10
|
+
a) {
|
3
11
|
this.r = r;
|
4
12
|
this.g = g;
|
5
13
|
this.b = b;
|
6
14
|
this.a = a;
|
7
15
|
this.hexString = null;
|
8
16
|
}
|
9
|
-
|
17
|
+
/**
|
18
|
+
* Create a color from red, green, blue components. The color is fully opaque (`a = 1.0`).
|
19
|
+
*
|
20
|
+
* Each component should be in the range [0, 1].
|
21
|
+
*/
|
10
22
|
static ofRGB(red, green, blue) {
|
11
23
|
return Color4.ofRGBA(red, green, blue, 1.0);
|
12
24
|
}
|
@@ -46,7 +58,7 @@ export default class Color4 {
|
|
46
58
|
}
|
47
59
|
return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
|
48
60
|
}
|
49
|
-
|
61
|
+
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
50
62
|
static fromString(text) {
|
51
63
|
if (text.startsWith('#')) {
|
52
64
|
return Color4.fromHex(text);
|
@@ -67,12 +79,21 @@ export default class Color4 {
|
|
67
79
|
return Color4.ofRGBA(red, green, blue, alpha);
|
68
80
|
}
|
69
81
|
}
|
82
|
+
/** @returns true if `this` and `other` are approximately equal. */
|
70
83
|
eq(other) {
|
71
84
|
if (other == null) {
|
72
85
|
return false;
|
73
86
|
}
|
74
87
|
return this.toHexString() === other.toHexString();
|
75
88
|
}
|
89
|
+
/**
|
90
|
+
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
91
|
+
*
|
92
|
+
* @example
|
93
|
+
* ```
|
94
|
+
* Color4.red.toHexString(); // -> #ff0000ff
|
95
|
+
* ```
|
96
|
+
*/
|
76
97
|
toHexString() {
|
77
98
|
if (this.hexString) {
|
78
99
|
return this.hexString;
|
package/dist/src/Editor.d.ts
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
/**
|
2
|
+
* The main entrypoint for the full editor.
|
3
|
+
*
|
4
|
+
* @example
|
5
|
+
* To create an editor with a toolbar,
|
6
|
+
* ```
|
7
|
+
* const editor = new Editor(document.body);
|
8
|
+
*
|
9
|
+
* const toolbar = editor.addToolbar();
|
10
|
+
* toolbar.addActionButton('Save', () => {
|
11
|
+
* const saveData = editor.toSVG().outerHTML;
|
12
|
+
* // Do something with saveData...
|
13
|
+
* });
|
14
|
+
* ```
|
15
|
+
*
|
16
|
+
* @packageDocumentation
|
17
|
+
*/
|
1
18
|
import EditorImage from './EditorImage';
|
2
19
|
import ToolController from './tools/ToolController';
|
3
20
|
import { InputEvtType, EditorNotifier, ImageLoader } from './types';
|
@@ -12,39 +29,143 @@ import Pointer from './Pointer';
|
|
12
29
|
import Rect2 from './math/Rect2';
|
13
30
|
import { EditorLocalization } from './localization';
|
14
31
|
export interface EditorSettings {
|
32
|
+
/** Defaults to `RenderingMode.CanvasRenderer` */
|
15
33
|
renderingMode: RenderingMode;
|
34
|
+
/** Uses a default English localization if a translation is not given. */
|
16
35
|
localization: Partial<EditorLocalization>;
|
36
|
+
/**
|
37
|
+
* `true` if touchpad/mousewheel scrolling should scroll the editor instead of the document.
|
38
|
+
* This does not include pinch-zoom events.
|
39
|
+
* Defaults to true.
|
40
|
+
*/
|
17
41
|
wheelEventsEnabled: boolean | 'only-if-focused';
|
42
|
+
/** Minimum zoom fraction (e.g. 0.5 → 50% zoom). */
|
18
43
|
minZoom: number;
|
19
44
|
maxZoom: number;
|
20
45
|
}
|
21
46
|
export declare class Editor {
|
22
47
|
private container;
|
23
48
|
private renderingRegion;
|
24
|
-
history: UndoRedoHistory;
|
25
49
|
display: Display;
|
50
|
+
/**
|
51
|
+
* Handles undo/redo.
|
52
|
+
*
|
53
|
+
* @example
|
54
|
+
* ```
|
55
|
+
* const editor = new Editor(document.body);
|
56
|
+
*
|
57
|
+
* // Do something undoable.
|
58
|
+
* // ...
|
59
|
+
*
|
60
|
+
* // Undo the last action
|
61
|
+
* editor.history.undo();
|
62
|
+
* ```
|
63
|
+
*/
|
64
|
+
history: UndoRedoHistory;
|
65
|
+
/**
|
66
|
+
* Data structure for adding/removing/querying objects in the image.
|
67
|
+
*
|
68
|
+
* @example
|
69
|
+
* ```
|
70
|
+
* const editor = new Editor(document.body);
|
71
|
+
*
|
72
|
+
* // Create a path.
|
73
|
+
* const stroke = new Stroke([
|
74
|
+
* Path.fromString('M0,0 L30,30 z').toRenderable({ fill: Color4.black }),
|
75
|
+
* ]);
|
76
|
+
* const addElementCommand = editor.image.addElement(stroke);
|
77
|
+
*
|
78
|
+
* // Add the stroke to the editor
|
79
|
+
* editor.dispatch(addElementCommand);
|
80
|
+
* ```
|
81
|
+
*/
|
26
82
|
image: EditorImage;
|
83
|
+
/** Viewport for the exported/imported image. */
|
27
84
|
private importExportViewport;
|
85
|
+
/** @internal */
|
28
86
|
localization: EditorLocalization;
|
29
87
|
viewport: Viewport;
|
30
88
|
toolController: ToolController;
|
89
|
+
/**
|
90
|
+
* Global event dispatcher/subscriber.
|
91
|
+
* @see {@link types.EditorEventType}
|
92
|
+
*/
|
31
93
|
notifier: EditorNotifier;
|
32
94
|
private loadingWarning;
|
33
95
|
private accessibilityAnnounceArea;
|
34
96
|
private accessibilityControlArea;
|
35
97
|
private settings;
|
98
|
+
/**
|
99
|
+
* @example
|
100
|
+
* ```
|
101
|
+
* const container = document.body;
|
102
|
+
*
|
103
|
+
* // Create an editor
|
104
|
+
* const editor = new Editor(container, {
|
105
|
+
* // 2e-10 and 1e12 are the default values for minimum/maximum zoom.
|
106
|
+
* minZoom: 2e-10,
|
107
|
+
* maxZoom: 1e12,
|
108
|
+
* });
|
109
|
+
*
|
110
|
+
* // Add the default toolbar
|
111
|
+
* const toolbar = editor.addToolbar();
|
112
|
+
* toolbar.addActionButton({
|
113
|
+
* label: 'Save'
|
114
|
+
* icon: createSaveIcon(),
|
115
|
+
* }, () => {
|
116
|
+
* const saveData = editor.toSVG().outerHTML;
|
117
|
+
* // Do something with saveData
|
118
|
+
* });
|
119
|
+
* ```
|
120
|
+
*/
|
36
121
|
constructor(parent: HTMLElement, settings?: Partial<EditorSettings>);
|
122
|
+
/**
|
123
|
+
* @returns a reference to the editor's container.
|
124
|
+
*
|
125
|
+
* @example
|
126
|
+
* ```
|
127
|
+
* editor.getRootElement().style.height = '500px';
|
128
|
+
* ```
|
129
|
+
*/
|
37
130
|
getRootElement(): HTMLElement;
|
131
|
+
/** @param fractionLoaded - should be a number from 0 to 1, where 1 represents completely loaded. */
|
38
132
|
showLoadingWarning(fractionLoaded: number): void;
|
39
133
|
hideLoadingWarning(): void;
|
40
134
|
private previousAccessibilityAnnouncement;
|
41
135
|
announceForAccessibility(message: string): void;
|
136
|
+
/**
|
137
|
+
* Creates a toolbar. If `defaultLayout` is true, default buttons are used.
|
138
|
+
* @returns a reference to the toolbar.
|
139
|
+
*/
|
42
140
|
addToolbar(defaultLayout?: boolean): HTMLToolbar;
|
43
141
|
private registerListeners;
|
142
|
+
/** Adds event listners for keypresses to `elem` and forwards those events to the editor. */
|
44
143
|
handleKeyEventsFrom(elem: HTMLElement): void;
|
144
|
+
/** `apply` a command. `command` will be announced for accessibility. */
|
45
145
|
dispatch(command: Command, addToHistory?: boolean): void;
|
146
|
+
/**
|
147
|
+
* Dispatches a command without announcing it. By default, does not add to history.
|
148
|
+
* Use this to show finalized commands that don't need to have `announceForAccessibility`
|
149
|
+
* called.
|
150
|
+
*
|
151
|
+
* Prefer `command.apply(editor)` for incomplete commands. `dispatchNoAnnounce` may allow
|
152
|
+
* clients to listen for the application of commands (e.g. `SerializableCommand`s so they can
|
153
|
+
* be sent across the network), while `apply` does not.
|
154
|
+
*
|
155
|
+
* @example
|
156
|
+
* ```
|
157
|
+
* const addToHistory = false;
|
158
|
+
* editor.dispatchNoAnnounce(editor.viewport.zoomTo(someRectangle), addToHistory);
|
159
|
+
* ```
|
160
|
+
*/
|
46
161
|
dispatchNoAnnounce(command: Command, addToHistory?: boolean): void;
|
47
|
-
|
162
|
+
/**
|
163
|
+
* Apply a large transformation in chunks.
|
164
|
+
* If `apply` is `false`, the commands are unapplied.
|
165
|
+
* Triggers a re-render after each `updateChunkSize`-sized group of commands
|
166
|
+
* has been applied.
|
167
|
+
*/
|
168
|
+
asyncApplyOrUnapplyCommands(commands: Command[], apply: boolean, updateChunkSize: number): Promise<void>;
|
48
169
|
asyncApplyCommands(commands: Command[], chunkSize: number): Promise<void>;
|
49
170
|
asyncUnapplyCommands(commands: Command[], chunkSize: number): Promise<void>;
|
50
171
|
private announceUndoCallback;
|
@@ -64,6 +185,12 @@ export declare class Editor {
|
|
64
185
|
loadFrom(loader: ImageLoader): Promise<void>;
|
65
186
|
getImportExportRect(): Rect2;
|
66
187
|
setImportExportRect(imageRect: Rect2): Command;
|
188
|
+
/**
|
189
|
+
* Alias for loadFrom(SVGLoader.fromString).
|
190
|
+
*
|
191
|
+
* This is particularly useful when accessing a bundled version of the editor,
|
192
|
+
* where `SVGLoader.fromString` is unavailable.
|
193
|
+
*/
|
67
194
|
loadFromSVG(svgData: string): Promise<void>;
|
68
195
|
}
|
69
196
|
export default Editor;
|
package/dist/src/Editor.js
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
/**
|
2
|
+
* The main entrypoint for the full editor.
|
3
|
+
*
|
4
|
+
* @example
|
5
|
+
* To create an editor with a toolbar,
|
6
|
+
* ```
|
7
|
+
* const editor = new Editor(document.body);
|
8
|
+
*
|
9
|
+
* const toolbar = editor.addToolbar();
|
10
|
+
* toolbar.addActionButton('Save', () => {
|
11
|
+
* const saveData = editor.toSVG().outerHTML;
|
12
|
+
* // Do something with saveData...
|
13
|
+
* });
|
14
|
+
* ```
|
15
|
+
*
|
16
|
+
* @packageDocumentation
|
17
|
+
*/
|
1
18
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
2
19
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
3
20
|
return new (P || (P = Promise))(function (resolve, reject) {
|
@@ -24,7 +41,31 @@ import SVGLoader from './SVGLoader';
|
|
24
41
|
import Pointer from './Pointer';
|
25
42
|
import Mat33 from './math/Mat33';
|
26
43
|
import getLocalizationTable from './localizations/getLocalizationTable';
|
44
|
+
// { @inheritDoc Editor! }
|
27
45
|
export class Editor {
|
46
|
+
/**
|
47
|
+
* @example
|
48
|
+
* ```
|
49
|
+
* const container = document.body;
|
50
|
+
*
|
51
|
+
* // Create an editor
|
52
|
+
* const editor = new Editor(container, {
|
53
|
+
* // 2e-10 and 1e12 are the default values for minimum/maximum zoom.
|
54
|
+
* minZoom: 2e-10,
|
55
|
+
* maxZoom: 1e12,
|
56
|
+
* });
|
57
|
+
*
|
58
|
+
* // Add the default toolbar
|
59
|
+
* const toolbar = editor.addToolbar();
|
60
|
+
* toolbar.addActionButton({
|
61
|
+
* label: 'Save'
|
62
|
+
* icon: createSaveIcon(),
|
63
|
+
* }, () => {
|
64
|
+
* const saveData = editor.toSVG().outerHTML;
|
65
|
+
* // Do something with saveData
|
66
|
+
* });
|
67
|
+
* ```
|
68
|
+
*/
|
28
69
|
constructor(parent, settings = {}) {
|
29
70
|
var _a, _b, _c, _d;
|
30
71
|
this.previousAccessibilityAnnouncement = '';
|
@@ -96,13 +137,18 @@ export class Editor {
|
|
96
137
|
}
|
97
138
|
});
|
98
139
|
}
|
99
|
-
|
100
|
-
|
101
|
-
|
140
|
+
/**
|
141
|
+
* @returns a reference to the editor's container.
|
142
|
+
*
|
143
|
+
* @example
|
144
|
+
* ```
|
145
|
+
* editor.getRootElement().style.height = '500px';
|
146
|
+
* ```
|
147
|
+
*/
|
102
148
|
getRootElement() {
|
103
149
|
return this.container;
|
104
150
|
}
|
105
|
-
|
151
|
+
/** @param fractionLoaded - should be a number from 0 to 1, where 1 represents completely loaded. */
|
106
152
|
showLoadingWarning(fractionLoaded) {
|
107
153
|
const loadingPercent = Math.round(fractionLoaded * 100);
|
108
154
|
this.loadingWarning.innerText = this.localization.loading(loadingPercent);
|
@@ -112,6 +158,8 @@ export class Editor {
|
|
112
158
|
this.loadingWarning.style.display = 'none';
|
113
159
|
this.announceForAccessibility(this.localization.doneLoading);
|
114
160
|
}
|
161
|
+
// Announce `message` for screen readers. If `message` is the same as the previous
|
162
|
+
// message, it is re-announced.
|
115
163
|
announceForAccessibility(message) {
|
116
164
|
// Force re-announcing an announcement if announced again.
|
117
165
|
if (message === this.previousAccessibilityAnnouncement) {
|
@@ -120,6 +168,10 @@ export class Editor {
|
|
120
168
|
this.accessibilityAnnounceArea.innerText = message;
|
121
169
|
this.previousAccessibilityAnnouncement = message;
|
122
170
|
}
|
171
|
+
/**
|
172
|
+
* Creates a toolbar. If `defaultLayout` is true, default buttons are used.
|
173
|
+
* @returns a reference to the toolbar.
|
174
|
+
*/
|
123
175
|
addToolbar(defaultLayout = true) {
|
124
176
|
const toolbar = new HTMLToolbar(this, this.container, this.localization);
|
125
177
|
if (defaultLayout) {
|
@@ -254,8 +306,7 @@ export class Editor {
|
|
254
306
|
this.accessibilityControlArea.value = '';
|
255
307
|
});
|
256
308
|
}
|
257
|
-
|
258
|
-
// editor.
|
309
|
+
/** Adds event listners for keypresses to `elem` and forwards those events to the editor. */
|
259
310
|
handleKeyEventsFrom(elem) {
|
260
311
|
elem.addEventListener('keydown', evt => {
|
261
312
|
if (evt.key === 't' || evt.key === 'T') {
|
@@ -283,7 +334,7 @@ export class Editor {
|
|
283
334
|
}
|
284
335
|
});
|
285
336
|
}
|
286
|
-
|
337
|
+
/** `apply` a command. `command` will be announced for accessibility. */
|
287
338
|
dispatch(command, addToHistory = true) {
|
288
339
|
if (addToHistory) {
|
289
340
|
// .push applies [command] to this
|
@@ -294,7 +345,21 @@ export class Editor {
|
|
294
345
|
}
|
295
346
|
this.announceForAccessibility(command.description(this, this.localization));
|
296
347
|
}
|
297
|
-
|
348
|
+
/**
|
349
|
+
* Dispatches a command without announcing it. By default, does not add to history.
|
350
|
+
* Use this to show finalized commands that don't need to have `announceForAccessibility`
|
351
|
+
* called.
|
352
|
+
*
|
353
|
+
* Prefer `command.apply(editor)` for incomplete commands. `dispatchNoAnnounce` may allow
|
354
|
+
* clients to listen for the application of commands (e.g. `SerializableCommand`s so they can
|
355
|
+
* be sent across the network), while `apply` does not.
|
356
|
+
*
|
357
|
+
* @example
|
358
|
+
* ```
|
359
|
+
* const addToHistory = false;
|
360
|
+
* editor.dispatchNoAnnounce(editor.viewport.zoomTo(someRectangle), addToHistory);
|
361
|
+
* ```
|
362
|
+
*/
|
298
363
|
dispatchNoAnnounce(command, addToHistory = false) {
|
299
364
|
if (addToHistory) {
|
300
365
|
this.history.push(command);
|
@@ -303,10 +368,12 @@ export class Editor {
|
|
303
368
|
command.apply(this);
|
304
369
|
}
|
305
370
|
}
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
371
|
+
/**
|
372
|
+
* Apply a large transformation in chunks.
|
373
|
+
* If `apply` is `false`, the commands are unapplied.
|
374
|
+
* Triggers a re-render after each `updateChunkSize`-sized group of commands
|
375
|
+
* has been applied.
|
376
|
+
*/
|
310
377
|
asyncApplyOrUnapplyCommands(commands, apply, updateChunkSize) {
|
311
378
|
return __awaiter(this, void 0, void 0, function* () {
|
312
379
|
this.display.setDraftMode(true);
|
@@ -333,12 +400,16 @@ export class Editor {
|
|
333
400
|
this.hideLoadingWarning();
|
334
401
|
});
|
335
402
|
}
|
403
|
+
// @see {@link #asyncApplyOrUnapplyCommands }
|
336
404
|
asyncApplyCommands(commands, chunkSize) {
|
337
405
|
return this.asyncApplyOrUnapplyCommands(commands, true, chunkSize);
|
338
406
|
}
|
407
|
+
// @see {@link #asyncApplyOrUnapplyCommands }
|
339
408
|
asyncUnapplyCommands(commands, chunkSize) {
|
340
409
|
return this.asyncApplyOrUnapplyCommands(commands, false, chunkSize);
|
341
410
|
}
|
411
|
+
// Schedule a re-render for some time in the near future. Does not schedule an additional
|
412
|
+
// re-render if a re-render is already queued.
|
342
413
|
queueRerender() {
|
343
414
|
if (!this.rerenderQueued) {
|
344
415
|
this.rerenderQueued = true;
|
@@ -372,10 +443,12 @@ export class Editor {
|
|
372
443
|
clearWetInk() {
|
373
444
|
this.display.getWetInkRenderer().clear();
|
374
445
|
}
|
375
|
-
// Focuses the region used for text input
|
446
|
+
// Focuses the region used for text input/key commands.
|
376
447
|
focus() {
|
377
448
|
this.renderingRegion.focus();
|
378
449
|
}
|
450
|
+
// Creates an element that will be positioned on top of the dry/wet ink
|
451
|
+
// renderers.
|
379
452
|
createHTMLOverlay(overlay) {
|
380
453
|
overlay.classList.add('overlay');
|
381
454
|
this.container.appendChild(overlay);
|
@@ -390,7 +463,7 @@ export class Editor {
|
|
390
463
|
return styleSheet;
|
391
464
|
}
|
392
465
|
// Dispatch a pen event to the currently selected tool.
|
393
|
-
//
|
466
|
+
// Intended primarially for unit tests.
|
394
467
|
sendPenEvent(eventType, point, allPointers) {
|
395
468
|
const mainPointer = Pointer.ofCanvasPoint(point, eventType !== InputEvtType.PointerUpEvt, this.viewport);
|
396
469
|
this.toolController.dispatchInputEvent({
|
@@ -452,7 +525,7 @@ export class Editor {
|
|
452
525
|
getImportExportRect() {
|
453
526
|
return this.importExportViewport.visibleRect;
|
454
527
|
}
|
455
|
-
// Resize the output SVG
|
528
|
+
// Resize the output SVG to match `imageRect`.
|
456
529
|
setImportExportRect(imageRect) {
|
457
530
|
const origSize = this.importExportViewport.visibleRect.size;
|
458
531
|
const origTransform = this.importExportViewport.canvasToScreenTransform;
|
@@ -474,8 +547,12 @@ export class Editor {
|
|
474
547
|
}
|
475
548
|
};
|
476
549
|
}
|
477
|
-
|
478
|
-
|
550
|
+
/**
|
551
|
+
* Alias for loadFrom(SVGLoader.fromString).
|
552
|
+
*
|
553
|
+
* This is particularly useful when accessing a bundled version of the editor,
|
554
|
+
* where `SVGLoader.fromString` is unavailable.
|
555
|
+
*/
|
479
556
|
loadFromSVG(svgData) {
|
480
557
|
return __awaiter(this, void 0, void 0, function* () {
|
481
558
|
const loader = SVGLoader.fromString(svgData);
|
@@ -1,26 +1,31 @@
|
|
1
1
|
import AbstractRenderer from './rendering/renderers/AbstractRenderer';
|
2
|
-
import Command from './commands/Command';
|
3
2
|
import Viewport from './Viewport';
|
4
3
|
import AbstractComponent from './components/AbstractComponent';
|
5
4
|
import Rect2 from './math/Rect2';
|
6
5
|
import RenderingCache from './rendering/caching/RenderingCache';
|
6
|
+
import SerializableCommand from './commands/SerializableCommand';
|
7
7
|
export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
|
8
8
|
export default class EditorImage {
|
9
9
|
private root;
|
10
10
|
private componentsById;
|
11
11
|
constructor();
|
12
12
|
findParent(elem: AbstractComponent): ImageNode | null;
|
13
|
+
/** @internal */
|
13
14
|
renderWithCache(screenRenderer: AbstractRenderer, cache: RenderingCache, viewport: Viewport): void;
|
15
|
+
/** @internal */
|
14
16
|
render(renderer: AbstractRenderer, viewport: Viewport): void;
|
17
|
+
/** Renders all nodes, even ones not within the viewport. @internal */
|
15
18
|
renderAll(renderer: AbstractRenderer): void;
|
16
19
|
getElementsIntersectingRegion(region: Rect2): AbstractComponent[];
|
20
|
+
/** @internal */
|
17
21
|
onDestroyElement(elem: AbstractComponent): void;
|
18
22
|
lookupElement(id: string): AbstractComponent | null;
|
19
23
|
private addElementDirectly;
|
20
|
-
static addElement(elem: AbstractComponent, applyByFlattening?: boolean):
|
24
|
+
static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
21
25
|
private static AddElementCommand;
|
22
26
|
}
|
23
27
|
declare type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
28
|
+
/** Part of the Editor's image. @internal */
|
24
29
|
export declare class ImageNode {
|
25
30
|
private parent;
|
26
31
|
private content;
|
package/dist/src/EditorImage.js
CHANGED
@@ -2,11 +2,13 @@ var _a;
|
|
2
2
|
import AbstractComponent from './components/AbstractComponent';
|
3
3
|
import Rect2 from './math/Rect2';
|
4
4
|
import SerializableCommand from './commands/SerializableCommand';
|
5
|
+
// @internal
|
5
6
|
export const sortLeavesByZIndex = (leaves) => {
|
6
7
|
leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
|
7
8
|
};
|
8
9
|
// Handles lookup/storage of elements in the image
|
9
10
|
export default class EditorImage {
|
11
|
+
// @internal
|
10
12
|
constructor() {
|
11
13
|
this.root = new ImageNode();
|
12
14
|
this.componentsById = {};
|
@@ -21,13 +23,15 @@ export default class EditorImage {
|
|
21
23
|
}
|
22
24
|
return null;
|
23
25
|
}
|
26
|
+
/** @internal */
|
24
27
|
renderWithCache(screenRenderer, cache, viewport) {
|
25
28
|
cache.render(screenRenderer, this.root, viewport);
|
26
29
|
}
|
30
|
+
/** @internal */
|
27
31
|
render(renderer, viewport) {
|
28
32
|
this.root.render(renderer, viewport.visibleRect);
|
29
33
|
}
|
30
|
-
|
34
|
+
/** Renders all nodes, even ones not within the viewport. @internal */
|
31
35
|
renderAll(renderer) {
|
32
36
|
const leaves = this.root.getLeaves();
|
33
37
|
sortLeavesByZIndex(leaves);
|
@@ -40,6 +44,7 @@ export default class EditorImage {
|
|
40
44
|
sortLeavesByZIndex(leaves);
|
41
45
|
return leaves.map(leaf => leaf.getContent());
|
42
46
|
}
|
47
|
+
/** @internal */
|
43
48
|
onDestroyElement(elem) {
|
44
49
|
delete this.componentsById[elem.getId()];
|
45
50
|
}
|
@@ -83,24 +88,25 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
|
|
83
88
|
container === null || container === void 0 ? void 0 : container.remove();
|
84
89
|
editor.queueRerender();
|
85
90
|
}
|
86
|
-
description(
|
91
|
+
description(_editor, localization) {
|
87
92
|
return localization.addElementAction(this.element.description(localization));
|
88
93
|
}
|
89
|
-
|
90
|
-
return
|
94
|
+
serializeToJSON() {
|
95
|
+
return {
|
91
96
|
elemData: this.element.serialize(),
|
92
|
-
}
|
97
|
+
};
|
93
98
|
}
|
94
99
|
},
|
95
100
|
(() => {
|
96
|
-
SerializableCommand.register('add-element', (
|
97
|
-
const
|
98
|
-
const
|
101
|
+
SerializableCommand.register('add-element', (json, editor) => {
|
102
|
+
const id = json.elemData.id;
|
103
|
+
const foundElem = editor.image.lookupElement(id);
|
104
|
+
const elem = foundElem !== null && foundElem !== void 0 ? foundElem : AbstractComponent.deserialize(json.elemData);
|
99
105
|
return new EditorImage.AddElementCommand(elem);
|
100
106
|
});
|
101
107
|
})(),
|
102
108
|
_a);
|
103
|
-
|
109
|
+
/** Part of the Editor's image. @internal */
|
104
110
|
export class ImageNode {
|
105
111
|
constructor(parent = null) {
|
106
112
|
this.parent = parent;
|
@@ -136,16 +142,22 @@ export class ImageNode {
|
|
136
142
|
// Returns a list of `ImageNode`s with content (and thus no children).
|
137
143
|
getLeavesIntersectingRegion(region, isTooSmall) {
|
138
144
|
const result = [];
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
145
|
+
let current;
|
146
|
+
const workList = [];
|
147
|
+
workList.push(this);
|
148
|
+
const toNext = () => {
|
149
|
+
current = undefined;
|
150
|
+
const next = workList.pop();
|
151
|
+
if (next && !(isTooSmall === null || isTooSmall === void 0 ? void 0 : isTooSmall(next.bbox))) {
|
152
|
+
current = next;
|
153
|
+
if (current.content !== null && current.getBBox().intersection(region)) {
|
154
|
+
result.push(current);
|
155
|
+
}
|
156
|
+
workList.push(...current.getChildrenIntersectingRegion(region));
|
157
|
+
}
|
158
|
+
};
|
159
|
+
while (workList.length > 0) {
|
160
|
+
toNext();
|
149
161
|
}
|
150
162
|
return result;
|
151
163
|
}
|
@@ -180,13 +192,17 @@ export class ImageNode {
|
|
180
192
|
// share a parent.
|
181
193
|
const leafBBox = leaf.getBBox();
|
182
194
|
if (leafBBox.containsRect(this.getBBox())) {
|
183
|
-
// Create a node for this' children and for the new content..
|
184
195
|
const nodeForNewLeaf = new ImageNode(this);
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
196
|
+
if (this.children.length < this.targetChildCount) {
|
197
|
+
this.children.push(nodeForNewLeaf);
|
198
|
+
}
|
199
|
+
else {
|
200
|
+
const nodeForChildren = new ImageNode(this);
|
201
|
+
nodeForChildren.children = this.children;
|
202
|
+
this.children = [nodeForNewLeaf, nodeForChildren];
|
203
|
+
nodeForChildren.recomputeBBox(true);
|
204
|
+
nodeForChildren.updateParents();
|
205
|
+
}
|
190
206
|
return nodeForNewLeaf.addLeaf(leaf);
|
191
207
|
}
|
192
208
|
const containingNodes = this.children.filter(child => child.getBBox().containsRect(leafBBox));
|
@@ -1,3 +1,20 @@
|
|
1
|
+
/**
|
2
|
+
* Handles notifying listeners of events.
|
3
|
+
*
|
4
|
+
* `EventKeyType` is used to distinguish events (e.g. a `ClickEvent` vs a `TouchEvent`)
|
5
|
+
* while `EventMessageType` is the type of the data sent with an event (can be `void`).
|
6
|
+
*
|
7
|
+
* @example
|
8
|
+
* ```
|
9
|
+
* const dispatcher = new EventDispatcher<'event1'|'event2'|'event3', void>();
|
10
|
+
* dispatcher.on('event1', () => {
|
11
|
+
* console.log('Event 1 triggered.');
|
12
|
+
* });
|
13
|
+
* dispatcher.dispatch('event1');
|
14
|
+
* ```
|
15
|
+
*
|
16
|
+
* @packageDocumentation
|
17
|
+
*/
|
1
18
|
declare type CallbackHandler<EventType> = (data: EventType) => void;
|
2
19
|
export default class EventDispatcher<EventKeyType extends string | symbol | number, EventMessageType> {
|
3
20
|
private listeners;
|
@@ -6,6 +23,7 @@ export default class EventDispatcher<EventKeyType extends string | symbol | numb
|
|
6
23
|
on(eventName: EventKeyType, callback: CallbackHandler<EventMessageType>): {
|
7
24
|
remove: () => boolean;
|
8
25
|
};
|
26
|
+
/** Removes an event listener. This is equivalent to calling `.remove()` on the object returned by `.on`. */
|
9
27
|
off(eventName: EventKeyType, callback: CallbackHandler<EventMessageType>): void;
|
10
28
|
}
|
11
29
|
export {};
|