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
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.assertIsNumberArray = exports.assertIsNumber = exports.assertUnreachable = void 0;
|
3
|
+
exports.assertIsBoolean = exports.assertIsNumberArray = exports.assertIsNumber = exports.assertUnreachable = void 0;
|
4
4
|
/**
|
5
5
|
* Compile-time assertion that a branch of code is unreachable.
|
6
6
|
* @internal
|
@@ -49,3 +49,14 @@ const assertIsNumberArray = (values, allowNaN = false) => {
|
|
49
49
|
return true;
|
50
50
|
};
|
51
51
|
exports.assertIsNumberArray = assertIsNumberArray;
|
52
|
+
/**
|
53
|
+
* Throws an exception if `typeof value` is not a boolean.
|
54
|
+
*/
|
55
|
+
const assertIsBoolean = (value) => {
|
56
|
+
if (typeof value !== 'boolean') {
|
57
|
+
throw new Error('Given value is not a boolean');
|
58
|
+
// return false;
|
59
|
+
}
|
60
|
+
return true;
|
61
|
+
};
|
62
|
+
exports.assertIsBoolean = assertIsBoolean;
|
package/dist/cjs/version.js
CHANGED
package/dist/mjs/Editor.d.ts
CHANGED
@@ -16,7 +16,13 @@ import KeyBinding from './shortcuts/KeyBinding';
|
|
16
16
|
import AbstractToolbar from './toolbar/AbstractToolbar';
|
17
17
|
import RenderablePathSpec from './rendering/RenderablePathSpec';
|
18
18
|
import { AboutDialogEntry } from './dialogs/makeAboutDialog';
|
19
|
-
/**
|
19
|
+
/**
|
20
|
+
* Provides settings to an instance of an editor. See the Editor {@link Editor.constructor}.
|
21
|
+
*
|
22
|
+
* ## Example
|
23
|
+
*
|
24
|
+
* [[include:doc-pages/inline-examples/settings-example-1.md]]
|
25
|
+
*/
|
20
26
|
export interface EditorSettings {
|
21
27
|
/** Defaults to `RenderingMode.CanvasRenderer` */
|
22
28
|
renderingMode: RenderingMode;
|
@@ -36,41 +42,21 @@ export interface EditorSettings {
|
|
36
42
|
* Overrides for keyboard shortcuts. For example,
|
37
43
|
* ```ts
|
38
44
|
* {
|
39
|
-
* 'some.shortcut.id': [
|
45
|
+
* 'some.shortcut.id': [ KeyBinding.keyboardShortcutFromString('ctrl+a') ],
|
40
46
|
* 'another.shortcut.id': [ ]
|
41
47
|
* }
|
42
48
|
* ```
|
43
49
|
* where shortcut IDs map to lists of associated keybindings.
|
50
|
+
*
|
51
|
+
* @see
|
52
|
+
* - {@link KeyBinding}
|
53
|
+
* - {@link KeyboardShortcutManager}
|
44
54
|
*/
|
45
55
|
keyboardShortcutOverrides: Record<string, Array<KeyBinding>>;
|
46
56
|
/**
|
47
57
|
* Provides a set of icons for the editor.
|
48
58
|
*
|
49
59
|
* See, for example, the `@js-draw/material-icons` package.
|
50
|
-
*
|
51
|
-
* @example
|
52
|
-
* ```ts,runnable
|
53
|
-
* import * as jsdraw from 'js-draw';
|
54
|
-
* import MaterialIconProvider from '@js-draw/material-icons';
|
55
|
-
* import 'js-draw/styles';
|
56
|
-
*
|
57
|
-
* const settings: Partial<jsdraw.EditorSettings> = {
|
58
|
-
* // Default to material icons
|
59
|
-
* iconProvider: new MaterialIconProvider(),
|
60
|
-
*
|
61
|
-
* // Only scroll the editor if it's focused.
|
62
|
-
* wheelEventsEnabled: 'only-if-focused',
|
63
|
-
* };
|
64
|
-
*
|
65
|
-
* // Add an editor to the document, using the above settings
|
66
|
-
* const editor = new jsdraw.Editor(document.body, settings);
|
67
|
-
*
|
68
|
-
* // Add a toolbar to the editor
|
69
|
-
* const toolbar = jsdraw.makeEdgeToolbar(editor);
|
70
|
-
*
|
71
|
-
* // Add the default tool items
|
72
|
-
* toolbar.addDefaults();
|
73
|
-
* ```
|
74
60
|
*/
|
75
61
|
iconProvider: IconProvider;
|
76
62
|
/**
|
@@ -81,20 +67,22 @@ export interface EditorSettings {
|
|
81
67
|
/**
|
82
68
|
* The main entrypoint for the full editor.
|
83
69
|
*
|
84
|
-
*
|
70
|
+
* ## Example
|
85
71
|
* To create an editor with a toolbar,
|
86
|
-
* ```
|
72
|
+
* ```ts,runnable
|
73
|
+
* import { Editor } from 'js-draw';
|
74
|
+
*
|
87
75
|
* const editor = new Editor(document.body);
|
88
76
|
*
|
89
77
|
* const toolbar = editor.addToolbar();
|
90
|
-
* toolbar.
|
78
|
+
* toolbar.addSaveButton(() => {
|
91
79
|
* const saveData = editor.toSVG().outerHTML;
|
92
80
|
* // Do something with saveData...
|
93
81
|
* });
|
94
82
|
* ```
|
95
83
|
*
|
96
84
|
* See also
|
97
|
-
* [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/demo/example.ts
|
85
|
+
* [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/demo/example.ts).
|
98
86
|
*/
|
99
87
|
export declare class Editor {
|
100
88
|
private container;
|
@@ -120,12 +108,13 @@ export declare class Editor {
|
|
120
108
|
* Data structure for adding/removing/querying objects in the image.
|
121
109
|
*
|
122
110
|
* @example
|
123
|
-
* ```
|
111
|
+
* ```ts,runnable
|
112
|
+
* import { Editor, Stroke, Path, Color4, pathToRenderable } from 'js-draw';
|
124
113
|
* const editor = new Editor(document.body);
|
125
114
|
*
|
126
115
|
* // Create a path.
|
127
116
|
* const stroke = new Stroke([
|
128
|
-
* Path.fromString('M0,0
|
117
|
+
* pathToRenderable(Path.fromString('M0,0 L100,100 L300,30 z'), { fill: Color4.red }),
|
129
118
|
* ]);
|
130
119
|
* const addElementCommand = editor.image.addElement(stroke);
|
131
120
|
*
|
@@ -166,7 +155,9 @@ export declare class Editor {
|
|
166
155
|
private settings;
|
167
156
|
/**
|
168
157
|
* @example
|
169
|
-
* ```
|
158
|
+
* ```ts,runnable
|
159
|
+
* import { Editor } from 'js-draw';
|
160
|
+
*
|
170
161
|
* const container = document.body;
|
171
162
|
*
|
172
163
|
* // Create an editor
|
@@ -179,13 +170,16 @@ export declare class Editor {
|
|
179
170
|
* // Add the default toolbar
|
180
171
|
* const toolbar = editor.addToolbar();
|
181
172
|
*
|
182
|
-
*
|
173
|
+
* const createCustomIcon = () => {
|
174
|
+
* // Create/return an icon here.
|
175
|
+
* };
|
176
|
+
*
|
177
|
+
* // Add a custom button
|
183
178
|
* toolbar.addActionButton({
|
184
|
-
* label: '
|
185
|
-
* icon:
|
179
|
+
* label: 'Custom Button'
|
180
|
+
* icon: createCustomIcon(),
|
186
181
|
* }, () => {
|
187
|
-
*
|
188
|
-
* // Do something with saveData
|
182
|
+
* // Do something here
|
189
183
|
* });
|
190
184
|
* ```
|
191
185
|
*/
|
@@ -330,6 +324,9 @@ export declare class Editor {
|
|
330
324
|
/**
|
331
325
|
* Clears the wet ink display.
|
332
326
|
*
|
327
|
+
* The wet ink display can be used by the currently active tool to display a preview
|
328
|
+
* of an in-progress action.
|
329
|
+
*
|
333
330
|
* @see {@link Display.getWetInkRenderer}
|
334
331
|
*/
|
335
332
|
clearWetInk(): void;
|
@@ -341,12 +338,19 @@ export declare class Editor {
|
|
341
338
|
* Creates an element that will be positioned on top of the dry/wet ink
|
342
339
|
* renderers.
|
343
340
|
*
|
341
|
+
* So as not to change the position of other overlays, `overlay` should either
|
342
|
+
* be styled to have 0 height or have `position: absolute`.
|
343
|
+
*
|
344
344
|
* This is useful for displaying content on top of the rendered content
|
345
345
|
* (e.g. a selection box).
|
346
346
|
*/
|
347
347
|
createHTMLOverlay(overlay: HTMLElement): {
|
348
348
|
remove: () => void;
|
349
349
|
};
|
350
|
+
/**
|
351
|
+
* Creates a CSS stylesheet with `content` and applies it to the document
|
352
|
+
* (and thus, to this editor).
|
353
|
+
*/
|
350
354
|
addStyleSheet(content: string): HTMLStyleElement;
|
351
355
|
/**
|
352
356
|
* Dispatch a keyboard event to the currently selected tool.
|
@@ -370,7 +374,21 @@ export declare class Editor {
|
|
370
374
|
*/
|
371
375
|
sendPenEvent(eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]): void;
|
372
376
|
addAndCenterComponents(components: AbstractComponent[], selectComponents?: boolean): Promise<void>;
|
377
|
+
/**
|
378
|
+
* Get a data URL (e.g. as produced by `HTMLCanvasElement::toDataURL`).
|
379
|
+
* If `format` is not `image/png`, a PNG image URL may still be returned (as in the
|
380
|
+
* case of `HTMLCanvasElement::toDataURL`).
|
381
|
+
*
|
382
|
+
* The export resolution is the same as the size of the drawing canvas, unless `outputSize`
|
383
|
+
* is given.
|
384
|
+
*/
|
373
385
|
toDataURL(format?: 'image/png' | 'image/jpeg' | 'image/webp', outputSize?: Vec2): string;
|
386
|
+
/**
|
387
|
+
* Converts the editor's content into an SVG image.
|
388
|
+
*
|
389
|
+
* @see
|
390
|
+
* {@link SVGRenderer}
|
391
|
+
*/
|
374
392
|
toSVG(): SVGElement;
|
375
393
|
/**
|
376
394
|
* Load editor data from an `ImageLoader` (e.g. an {@link SVGLoader}).
|
@@ -393,8 +411,23 @@ export declare class Editor {
|
|
393
411
|
/**
|
394
412
|
* Alias for `loadFrom(SVGLoader.fromString)`.
|
395
413
|
*
|
396
|
-
*
|
397
|
-
*
|
414
|
+
* @example
|
415
|
+
* ```ts,runnable
|
416
|
+
* import {Editor} from 'js-draw';
|
417
|
+
* const editor = new Editor(document.body);
|
418
|
+
*
|
419
|
+
* ---visible---
|
420
|
+
* await editor.loadFromSVG(`
|
421
|
+
* <svg viewBox="5 23 52 30" width="52" height="16" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
422
|
+
* <text style="
|
423
|
+
* transform: matrix(0.181846, 0.1, 0, 0.181846, 11.4, 33.2);
|
424
|
+
* font-family: serif;
|
425
|
+
* font-size: 32px;
|
426
|
+
* fill: rgb(100, 140, 61);
|
427
|
+
* ">An SVG image!</text>
|
428
|
+
* </svg>
|
429
|
+
* `);
|
430
|
+
* ```
|
398
431
|
*/
|
399
432
|
loadFromSVG(svgData: string, sanitize?: boolean): Promise<void>;
|
400
433
|
private closeAboutDialog;
|
package/dist/mjs/Editor.mjs
CHANGED
@@ -8,7 +8,7 @@ import EventDispatcher from './EventDispatcher.mjs';
|
|
8
8
|
import { Vec2, Vec3, Color4, Mat33, toRoundedString } from '@js-draw/math';
|
9
9
|
import Display, { RenderingMode } from './rendering/Display.mjs';
|
10
10
|
import SVGRenderer from './rendering/renderers/SVGRenderer.mjs';
|
11
|
-
import SVGLoader from './SVGLoader.mjs';
|
11
|
+
import SVGLoader, { svgLoaderAutoresizeClassName } from './SVGLoader.mjs';
|
12
12
|
import Pointer from './Pointer.mjs';
|
13
13
|
import getLocalizationTable from './localizations/getLocalizationTable.mjs';
|
14
14
|
import IconProvider from './toolbar/IconProvider.mjs';
|
@@ -29,25 +29,29 @@ import version from './version.mjs';
|
|
29
29
|
/**
|
30
30
|
* The main entrypoint for the full editor.
|
31
31
|
*
|
32
|
-
*
|
32
|
+
* ## Example
|
33
33
|
* To create an editor with a toolbar,
|
34
|
-
* ```
|
34
|
+
* ```ts,runnable
|
35
|
+
* import { Editor } from 'js-draw';
|
36
|
+
*
|
35
37
|
* const editor = new Editor(document.body);
|
36
38
|
*
|
37
39
|
* const toolbar = editor.addToolbar();
|
38
|
-
* toolbar.
|
40
|
+
* toolbar.addSaveButton(() => {
|
39
41
|
* const saveData = editor.toSVG().outerHTML;
|
40
42
|
* // Do something with saveData...
|
41
43
|
* });
|
42
44
|
* ```
|
43
45
|
*
|
44
46
|
* See also
|
45
|
-
* [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/demo/example.ts
|
47
|
+
* [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/demo/example.ts).
|
46
48
|
*/
|
47
49
|
export class Editor {
|
48
50
|
/**
|
49
51
|
* @example
|
50
|
-
* ```
|
52
|
+
* ```ts,runnable
|
53
|
+
* import { Editor } from 'js-draw';
|
54
|
+
*
|
51
55
|
* const container = document.body;
|
52
56
|
*
|
53
57
|
* // Create an editor
|
@@ -60,13 +64,16 @@ export class Editor {
|
|
60
64
|
* // Add the default toolbar
|
61
65
|
* const toolbar = editor.addToolbar();
|
62
66
|
*
|
63
|
-
*
|
67
|
+
* const createCustomIcon = () => {
|
68
|
+
* // Create/return an icon here.
|
69
|
+
* };
|
70
|
+
*
|
71
|
+
* // Add a custom button
|
64
72
|
* toolbar.addActionButton({
|
65
|
-
* label: '
|
66
|
-
* icon:
|
73
|
+
* label: 'Custom Button'
|
74
|
+
* icon: createCustomIcon(),
|
67
75
|
* }, () => {
|
68
|
-
*
|
69
|
-
* // Do something with saveData
|
76
|
+
* // Do something here
|
70
77
|
* });
|
71
78
|
* ```
|
72
79
|
*/
|
@@ -99,6 +106,10 @@ export class Editor {
|
|
99
106
|
iconProvider: settings.iconProvider ?? new IconProvider(),
|
100
107
|
notices: [],
|
101
108
|
};
|
109
|
+
// Validate settings
|
110
|
+
if (this.settings.minZoom > this.settings.maxZoom) {
|
111
|
+
throw new Error('Minimum zoom must be lesser than maximum zoom!');
|
112
|
+
}
|
102
113
|
this.icons = this.settings.iconProvider;
|
103
114
|
this.shortcuts = new KeyboardShortcutManager(this.settings.keyboardShortcutOverrides);
|
104
115
|
this.container = document.createElement('div');
|
@@ -153,6 +164,10 @@ export class Editor {
|
|
153
164
|
if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
|
154
165
|
resetTransform = evt.oldTransform;
|
155
166
|
}
|
167
|
+
else {
|
168
|
+
// If 1x zoom isn't acceptable, try a zoom between the minimum and maximum.
|
169
|
+
resetTransform = Mat33.scaling2D((this.settings.minZoom + this.settings.maxZoom) / 2);
|
170
|
+
}
|
156
171
|
this.viewport.resetTransform(resetTransform);
|
157
172
|
}
|
158
173
|
}
|
@@ -206,6 +221,7 @@ export class Editor {
|
|
206
221
|
registerListeners() {
|
207
222
|
this.handlePointerEventsFrom(this.renderingRegion);
|
208
223
|
this.handleKeyEventsFrom(this.renderingRegion);
|
224
|
+
this.handlePointerEventsFrom(this.accessibilityAnnounceArea);
|
209
225
|
this.container.addEventListener('wheel', evt => {
|
210
226
|
let delta = Vec3.of(evt.deltaX, evt.deltaY, evt.deltaZ);
|
211
227
|
// Process wheel events if the ctrl key is down, even if disabled -- we do want to handle
|
@@ -282,6 +298,8 @@ export class Editor {
|
|
282
298
|
// still fill the screen.
|
283
299
|
this.container.style.setProperty('--editor-current-width-px', `${this.container.clientWidth}px`);
|
284
300
|
this.container.style.setProperty('--editor-current-height-px', `${this.container.clientHeight}px`);
|
301
|
+
this.container.style.setProperty('--editor-current-display-width-px', `${this.renderingRegion.clientWidth}px`);
|
302
|
+
this.container.style.setProperty('--editor-current-display-height-px', `${this.renderingRegion.clientHeight}px`);
|
285
303
|
}
|
286
304
|
getPointerList() {
|
287
305
|
const nowTime = performance.now();
|
@@ -702,12 +720,12 @@ export class Editor {
|
|
702
720
|
this.display.setDraftMode(false);
|
703
721
|
this.hideLoadingWarning();
|
704
722
|
}
|
705
|
-
// @see {@link
|
723
|
+
// @see {@link asyncApplyOrUnapplyCommands }
|
706
724
|
asyncApplyCommands(commands, chunkSize) {
|
707
725
|
return this.asyncApplyOrUnapplyCommands(commands, true, chunkSize);
|
708
726
|
}
|
709
727
|
// If `unapplyInReverseOrder`, commands are reversed before unapplying.
|
710
|
-
// @see {@link
|
728
|
+
// @see {@link asyncApplyOrUnapplyCommands }
|
711
729
|
asyncUnapplyCommands(commands, chunkSize, unapplyInReverseOrder = false) {
|
712
730
|
if (unapplyInReverseOrder) {
|
713
731
|
commands = [...commands]; // copy
|
@@ -754,8 +772,8 @@ export class Editor {
|
|
754
772
|
}
|
755
773
|
const renderer = this.display.getDryInkRenderer();
|
756
774
|
this.image.renderWithCache(renderer, this.display.getCache(), this.viewport);
|
757
|
-
|
758
|
-
|
775
|
+
// Draw a rectangle around the region that will be visible on save
|
776
|
+
if (showImageBounds && !this.image.getAutoresizeEnabled()) {
|
759
777
|
const exportRectFill = { fill: Color4.fromHex('#44444455') };
|
760
778
|
const exportRectStrokeWidth = 5 * this.viewport.getSizeOfPixelOnCanvas();
|
761
779
|
renderer.drawRect(this.getImportExportRect(), exportRectStrokeWidth, exportRectFill);
|
@@ -778,6 +796,9 @@ export class Editor {
|
|
778
796
|
/**
|
779
797
|
* Clears the wet ink display.
|
780
798
|
*
|
799
|
+
* The wet ink display can be used by the currently active tool to display a preview
|
800
|
+
* of an in-progress action.
|
801
|
+
*
|
781
802
|
* @see {@link Display.getWetInkRenderer}
|
782
803
|
*/
|
783
804
|
clearWetInk() {
|
@@ -793,16 +814,23 @@ export class Editor {
|
|
793
814
|
* Creates an element that will be positioned on top of the dry/wet ink
|
794
815
|
* renderers.
|
795
816
|
*
|
817
|
+
* So as not to change the position of other overlays, `overlay` should either
|
818
|
+
* be styled to have 0 height or have `position: absolute`.
|
819
|
+
*
|
796
820
|
* This is useful for displaying content on top of the rendered content
|
797
821
|
* (e.g. a selection box).
|
798
822
|
*/
|
799
823
|
createHTMLOverlay(overlay) {
|
800
|
-
overlay.classList.add('overlay');
|
824
|
+
overlay.classList.add('overlay', 'js-draw-editor-overlay');
|
801
825
|
this.container.appendChild(overlay);
|
802
826
|
return {
|
803
827
|
remove: () => overlay.remove(),
|
804
828
|
};
|
805
829
|
}
|
830
|
+
/**
|
831
|
+
* Creates a CSS stylesheet with `content` and applies it to the document
|
832
|
+
* (and thus, to this editor).
|
833
|
+
*/
|
806
834
|
addStyleSheet(content) {
|
807
835
|
const styleSheet = document.createElement('style');
|
808
836
|
styleSheet.innerText = content;
|
@@ -883,11 +911,14 @@ export class Editor {
|
|
883
911
|
}
|
884
912
|
}
|
885
913
|
}
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
914
|
+
/**
|
915
|
+
* Get a data URL (e.g. as produced by `HTMLCanvasElement::toDataURL`).
|
916
|
+
* If `format` is not `image/png`, a PNG image URL may still be returned (as in the
|
917
|
+
* case of `HTMLCanvasElement::toDataURL`).
|
918
|
+
*
|
919
|
+
* The export resolution is the same as the size of the drawing canvas, unless `outputSize`
|
920
|
+
* is given.
|
921
|
+
*/
|
891
922
|
toDataURL(format = 'image/png', outputSize) {
|
892
923
|
const canvas = document.createElement('canvas');
|
893
924
|
const importExportViewport = this.image.getImportExportViewport();
|
@@ -904,6 +935,12 @@ export class Editor {
|
|
904
935
|
const dataURL = canvas.toDataURL(format);
|
905
936
|
return dataURL;
|
906
937
|
}
|
938
|
+
/**
|
939
|
+
* Converts the editor's content into an SVG image.
|
940
|
+
*
|
941
|
+
* @see
|
942
|
+
* {@link SVGRenderer}
|
943
|
+
*/
|
907
944
|
toSVG() {
|
908
945
|
const importExportViewport = this.image.getImportExportViewport().getTemporaryClone();
|
909
946
|
const sanitize = false;
|
@@ -919,6 +956,12 @@ export class Editor {
|
|
919
956
|
result.setAttribute('viewBox', [rect.x, rect.y, rect.w, rect.h].map(part => toRoundedString(part)).join(' '));
|
920
957
|
result.setAttribute('width', toRoundedString(rect.w));
|
921
958
|
result.setAttribute('height', toRoundedString(rect.h));
|
959
|
+
if (this.image.getAutoresizeEnabled()) {
|
960
|
+
result.classList.add(svgLoaderAutoresizeClassName);
|
961
|
+
}
|
962
|
+
else {
|
963
|
+
result.classList.remove(svgLoaderAutoresizeClassName);
|
964
|
+
}
|
922
965
|
return result;
|
923
966
|
}
|
924
967
|
/**
|
@@ -931,6 +974,7 @@ export class Editor {
|
|
931
974
|
this.display.setDraftMode(true);
|
932
975
|
const originalBackgrounds = this.image.getBackgroundComponents();
|
933
976
|
const eraseBackgroundCommand = new Erase(originalBackgrounds);
|
977
|
+
let autoresizeEnabled = false;
|
934
978
|
await loader.start(async (component) => {
|
935
979
|
await this.dispatchNoAnnounce(EditorImage.addElement(component));
|
936
980
|
}, (countProcessed, totalToProcess) => {
|
@@ -940,10 +984,17 @@ export class Editor {
|
|
940
984
|
return untilNextAnimationFrame();
|
941
985
|
}
|
942
986
|
return null;
|
943
|
-
}, (importExportRect) => {
|
987
|
+
}, (importExportRect, options) => {
|
944
988
|
this.dispatchNoAnnounce(this.setImportExportRect(importExportRect), false);
|
945
989
|
this.dispatchNoAnnounce(this.viewport.zoomTo(importExportRect), false);
|
990
|
+
if (options) {
|
991
|
+
autoresizeEnabled = options.autoresize;
|
992
|
+
}
|
946
993
|
});
|
994
|
+
// TODO: Move this call into the callback above. Currently, this would cause
|
995
|
+
// decrease in performance as the main background would be repeatedly added
|
996
|
+
// and removed from the editor every time another component is added.
|
997
|
+
this.dispatchNoAnnounce(this.image.setAutoresizeEnabled(autoresizeEnabled), false);
|
947
998
|
// Ensure that we don't have multiple overlapping BackgroundComponents. Remove
|
948
999
|
// old BackgroundComponents.
|
949
1000
|
// Overlapping BackgroundComponents may cause changing the background color to
|
@@ -1004,8 +1055,23 @@ export class Editor {
|
|
1004
1055
|
/**
|
1005
1056
|
* Alias for `loadFrom(SVGLoader.fromString)`.
|
1006
1057
|
*
|
1007
|
-
*
|
1008
|
-
*
|
1058
|
+
* @example
|
1059
|
+
* ```ts,runnable
|
1060
|
+
* import {Editor} from 'js-draw';
|
1061
|
+
* const editor = new Editor(document.body);
|
1062
|
+
*
|
1063
|
+
* ---visible---
|
1064
|
+
* await editor.loadFromSVG(`
|
1065
|
+
* <svg viewBox="5 23 52 30" width="52" height="16" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
1066
|
+
* <text style="
|
1067
|
+
* transform: matrix(0.181846, 0.1, 0, 0.181846, 11.4, 33.2);
|
1068
|
+
* font-family: serif;
|
1069
|
+
* font-size: 32px;
|
1070
|
+
* fill: rgb(100, 140, 61);
|
1071
|
+
* ">An SVG image!</text>
|
1072
|
+
* </svg>
|
1073
|
+
* `);
|
1074
|
+
* ```
|
1009
1075
|
*/
|
1010
1076
|
async loadFromSVG(svgData, sanitize = false) {
|
1011
1077
|
const loader = SVGLoader.fromString(svgData, sanitize);
|
@@ -5,9 +5,11 @@ import { Rect2 } from '@js-draw/math';
|
|
5
5
|
import RenderingCache from './rendering/caching/RenderingCache';
|
6
6
|
import SerializableCommand from './commands/SerializableCommand';
|
7
7
|
import EventDispatcher from './EventDispatcher';
|
8
|
+
import Command from './commands/Command';
|
8
9
|
export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
|
9
10
|
export declare enum EditorImageEventType {
|
10
|
-
ExportViewportChanged = 0
|
11
|
+
ExportViewportChanged = 0,
|
12
|
+
AutoresizeModeChanged = 1
|
11
13
|
}
|
12
14
|
export type EditorImageNotifier = EventDispatcher<EditorImageEventType, {
|
13
15
|
image: EditorImage;
|
@@ -19,6 +21,7 @@ export default class EditorImage {
|
|
19
21
|
private componentCount;
|
20
22
|
/** Viewport for the exported/imported image. */
|
21
23
|
private importExportViewport;
|
24
|
+
private shouldAutoresizeExportViewport;
|
22
25
|
readonly notifier: EditorImageNotifier;
|
23
26
|
constructor();
|
24
27
|
getBackgroundComponents(): AbstractComponent[];
|
@@ -35,14 +38,20 @@ export default class EditorImage {
|
|
35
38
|
render(renderer: AbstractRenderer, viewport: Viewport | null): void;
|
36
39
|
/** Renders all nodes, even ones not within the viewport. @internal */
|
37
40
|
renderAll(renderer: AbstractRenderer): void;
|
38
|
-
/**
|
41
|
+
/**
|
42
|
+
* @returns all elements in the image, sorted by z-index. This can be slow for large images.
|
43
|
+
*
|
44
|
+
* Does not include background elements. See {@link getBackgroundComponents}.
|
45
|
+
*/
|
39
46
|
getAllElements(): AbstractComponent[];
|
40
47
|
/** Returns the number of elements added to this image. @internal */
|
41
48
|
estimateNumElements(): number;
|
42
49
|
/** @returns a list of `AbstractComponent`s intersecting `region`, sorted by z-index. */
|
43
|
-
getElementsIntersectingRegion(region: Rect2): AbstractComponent[];
|
44
|
-
/** Called whenever an element is completely removed. @internal */
|
50
|
+
getElementsIntersectingRegion(region: Rect2, includeBackground?: boolean): AbstractComponent[];
|
51
|
+
/** Called whenever (just after) an element is completely removed. @internal */
|
45
52
|
onDestroyElement(elem: AbstractComponent): void;
|
53
|
+
/** Called just after an element is added. @internal */
|
54
|
+
private onElementAdded;
|
46
55
|
/**
|
47
56
|
* @returns the AbstractComponent with `id`, if it exists.
|
48
57
|
*
|
@@ -66,11 +75,40 @@ export default class EditorImage {
|
|
66
75
|
* @returns a `Viewport` for rendering the image when importing/exporting.
|
67
76
|
*/
|
68
77
|
getImportExportViewport(): Viewport;
|
78
|
+
/**
|
79
|
+
* @see {@link setImportExportRect}
|
80
|
+
*/
|
81
|
+
getImportExportRect(): Rect2;
|
82
|
+
/**
|
83
|
+
* Sets the import/export rectangle to the given `imageRect`. Disables
|
84
|
+
* autoresize (if it was previously enabled).
|
85
|
+
*/
|
69
86
|
setImportExportRect(imageRect: Rect2): SerializableCommand;
|
87
|
+
getAutoresizeEnabled(): boolean;
|
88
|
+
/** Returns a `Command` that sets whether the image should autoresize. */
|
89
|
+
setAutoresizeEnabled(autoresize: boolean): Command;
|
90
|
+
private setAutoresizeEnabledDirectly;
|
91
|
+
/** Updates the size/position of the viewport */
|
92
|
+
private autoresizeExportViewport;
|
93
|
+
private settingExportRect;
|
94
|
+
/**
|
95
|
+
* Sets the import/export viewport directly, without returning a `Command`.
|
96
|
+
* As such, this is not undoable.
|
97
|
+
*
|
98
|
+
* See setImportExportRect
|
99
|
+
*
|
100
|
+
* Returns true if changes to the viewport were made (and thus
|
101
|
+
* ExportViewportChanged was fired.)
|
102
|
+
*/
|
103
|
+
private setExportRectDirectly;
|
104
|
+
private onExportViewportChanged;
|
70
105
|
private static SetImportExportRectCommand;
|
71
106
|
}
|
72
107
|
type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
73
|
-
/**
|
108
|
+
/**
|
109
|
+
* Part of the Editor's image. Does not handle fullscreen/invisible components.
|
110
|
+
* @internal
|
111
|
+
*/
|
74
112
|
export declare class ImageNode {
|
75
113
|
private parent;
|
76
114
|
private content;
|
@@ -84,17 +122,31 @@ export declare class ImageNode {
|
|
84
122
|
onContentChange(): void;
|
85
123
|
getContent(): AbstractComponent | null;
|
86
124
|
getParent(): ImageNode | null;
|
87
|
-
|
125
|
+
protected getChildrenIntersectingRegion(region: Rect2, isTooSmallFilter?: TooSmallToRenderCheck): ImageNode[];
|
88
126
|
getChildrenOrSelfIntersectingRegion(region: Rect2): ImageNode[];
|
89
127
|
getLeavesIntersectingRegion(region: Rect2, isTooSmall?: TooSmallToRenderCheck): ImageNode[];
|
90
128
|
getChildWithContent(target: AbstractComponent): ImageNode | null;
|
91
129
|
getLeaves(): ImageNode[];
|
92
130
|
addLeaf(leaf: AbstractComponent): ImageNode;
|
131
|
+
protected static createLeafNode(parent: ImageNode, content: AbstractComponent): ImageNode;
|
93
132
|
getBBox(): Rect2;
|
94
133
|
recomputeBBox(bubbleUp: boolean): void;
|
134
|
+
private unionBBoxWith;
|
95
135
|
private updateParents;
|
96
136
|
private rebalance;
|
137
|
+
protected removeChild(child: ImageNode): void;
|
97
138
|
remove(): void;
|
98
139
|
render(renderer: AbstractRenderer, visibleRect?: Rect2): void;
|
140
|
+
renderDebugBoundingBoxes(renderer: AbstractRenderer, visibleRect: Rect2, depth?: number): void;
|
141
|
+
}
|
142
|
+
/** An `ImageNode` that can properly handle fullscreen/data components. @internal */
|
143
|
+
export declare class RootImageNode extends ImageNode {
|
144
|
+
private fullscreenChildren;
|
145
|
+
private dataComponents;
|
146
|
+
protected getChildrenIntersectingRegion(region: Rect2, _isTooSmall?: TooSmallToRenderCheck): ImageNode[];
|
147
|
+
getLeaves(): ImageNode[];
|
148
|
+
removeChild(child: ImageNode): void;
|
149
|
+
getChildWithContent(target: AbstractComponent): ImageNode | null;
|
150
|
+
addLeaf(leafContent: AbstractComponent): ImageNode;
|
99
151
|
}
|
100
152
|
export {};
|