js-draw 1.4.1 → 1.6.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 +10 -1
- package/dist/Editor.css +13 -12
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +8 -0
- package/dist/cjs/Editor.js +32 -9
- package/dist/cjs/SVGLoader.d.ts +6 -0
- package/dist/cjs/SVGLoader.js +100 -56
- package/dist/cjs/commands/Erase.d.ts +7 -1
- package/dist/cjs/commands/Erase.js +13 -4
- package/dist/cjs/commands/invertCommand.js +1 -1
- package/dist/cjs/components/Stroke.d.ts +1 -1
- package/dist/cjs/components/Stroke.js +1 -1
- package/dist/cjs/image/EditorImage.js +1 -1
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +13 -4
- package/dist/cjs/toolbar/AbstractToolbar.js +18 -5
- package/dist/cjs/toolbar/AbstractToolbar.test.d.ts +1 -0
- package/dist/cjs/toolbar/EdgeToolbar.js +2 -2
- package/dist/cjs/toolbar/widgets/BaseToolWidget.js +4 -13
- package/dist/cjs/toolbar/widgets/BaseToolWidget.test.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +5 -1
- package/dist/cjs/toolbar/widgets/BaseWidget.js +45 -28
- package/dist/cjs/toolbar/widgets/BaseWidget.test.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +2 -1
- package/dist/cjs/toolbar/widgets/SaveActionWidget.js +6 -2
- package/dist/cjs/tools/BaseTool.js +1 -0
- package/dist/cjs/tools/SelectionTool/Selection.js +4 -2
- package/dist/cjs/tools/ToolController.d.ts +23 -0
- package/dist/cjs/tools/ToolController.js +65 -4
- package/dist/cjs/tools/ToolController.test.d.ts +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +8 -0
- package/dist/mjs/Editor.mjs +32 -9
- package/dist/mjs/SVGLoader.d.ts +6 -0
- package/dist/mjs/SVGLoader.mjs +99 -55
- package/dist/mjs/commands/Erase.d.ts +7 -1
- package/dist/mjs/commands/Erase.mjs +13 -4
- package/dist/mjs/commands/invertCommand.mjs +1 -1
- package/dist/mjs/components/Stroke.d.ts +1 -1
- package/dist/mjs/components/Stroke.mjs +1 -1
- package/dist/mjs/image/EditorImage.mjs +1 -1
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +13 -4
- package/dist/mjs/toolbar/AbstractToolbar.mjs +18 -5
- package/dist/mjs/toolbar/AbstractToolbar.test.d.ts +1 -0
- package/dist/mjs/toolbar/EdgeToolbar.mjs +2 -2
- package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +4 -13
- package/dist/mjs/toolbar/widgets/BaseToolWidget.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +5 -1
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +45 -28
- package/dist/mjs/toolbar/widgets/BaseWidget.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +2 -1
- package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +6 -2
- package/dist/mjs/tools/BaseTool.mjs +1 -0
- package/dist/mjs/tools/SelectionTool/Selection.mjs +4 -2
- package/dist/mjs/tools/ToolController.d.ts +23 -0
- package/dist/mjs/tools/ToolController.mjs +65 -4
- package/dist/mjs/tools/ToolController.test.d.ts +1 -0
- package/dist/mjs/version.mjs +1 -1
- package/docs/img/readme-images/logo.svg +1 -0
- package/package.json +3 -3
- package/src/Editor.scss +4 -2
- package/src/toolbar/EdgeToolbar.scss +7 -4
- package/src/tools/SelectionTool/SelectionTool.scss +2 -2
@@ -58,7 +58,7 @@ class EditorImage {
|
|
58
58
|
this.settingExportRect = false;
|
59
59
|
this.root = new RootImageNode();
|
60
60
|
this.background = new RootImageNode();
|
61
|
-
this.componentsById =
|
61
|
+
this.componentsById = Object.create(null);
|
62
62
|
this.notifier = new EventDispatcher_1.default();
|
63
63
|
this.importExportViewport = new Viewport_1.default(() => {
|
64
64
|
this.onExportViewportChanged();
|
@@ -124,24 +124,30 @@ export default abstract class AbstractToolbar {
|
|
124
124
|
* toolbar.addDefaults();
|
125
125
|
* toolbar.addSaveButton(() => alert('save clicked!'));
|
126
126
|
* ```
|
127
|
+
*
|
128
|
+
* `labelOverride` can optionally be used to change the `label` or `icon` of the button.
|
127
129
|
*/
|
128
|
-
addSaveButton(saveCallback: () => void): BaseWidget;
|
130
|
+
addSaveButton(saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>): BaseWidget;
|
129
131
|
/**
|
130
132
|
* Adds an "Exit" button that, when clicked, calls `exitCallback`.
|
131
133
|
*
|
132
|
-
* **Note**: This is equivalent to
|
134
|
+
* **Note**: This is roughly equivalent to
|
133
135
|
* ```ts
|
134
136
|
* toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
|
135
137
|
* label: this.editor.localization.exit,
|
136
138
|
* icon: this.editor.icons.makeCloseIcon(),
|
139
|
+
*
|
140
|
+
* // labelOverride can be used to override label or icon.
|
141
|
+
* ...labelOverride,
|
137
142
|
* }, () => {
|
138
143
|
* exitCallback();
|
139
144
|
* });
|
140
145
|
* ```
|
146
|
+
* with some additional configuration.
|
141
147
|
*
|
142
148
|
* @final
|
143
149
|
*/
|
144
|
-
addExitButton(exitCallback: () => void): BaseWidget;
|
150
|
+
addExitButton(exitCallback: () => void, labelOverride?: Partial<ActionButtonIcon>): BaseWidget;
|
145
151
|
/**
|
146
152
|
* Adds undo and redo buttons that trigger the editor's built-in undo and redo
|
147
153
|
* functionality.
|
@@ -156,7 +162,10 @@ export default abstract class AbstractToolbar {
|
|
156
162
|
* Adds both the default tool widgets and action buttons.
|
157
163
|
*/
|
158
164
|
abstract addDefaults(): void;
|
159
|
-
/**
|
165
|
+
/**
|
166
|
+
* Remove this toolbar from its container and clean up listeners.
|
167
|
+
* This should only be called **once** for a given toolbar.
|
168
|
+
*/
|
160
169
|
remove(): void;
|
161
170
|
/**
|
162
171
|
* Removes `listener` when {@link remove} is called.
|
@@ -292,9 +292,11 @@ class AbstractToolbar {
|
|
292
292
|
* toolbar.addDefaults();
|
293
293
|
* toolbar.addSaveButton(() => alert('save clicked!'));
|
294
294
|
* ```
|
295
|
+
*
|
296
|
+
* `labelOverride` can optionally be used to change the `label` or `icon` of the button.
|
295
297
|
*/
|
296
|
-
addSaveButton(saveCallback) {
|
297
|
-
const widget = new SaveActionWidget_1.default(this.editor, this.localizationTable, saveCallback);
|
298
|
+
addSaveButton(saveCallback, labelOverride = {}) {
|
299
|
+
const widget = new SaveActionWidget_1.default(this.editor, this.localizationTable, saveCallback, labelOverride);
|
298
300
|
widget.setTags([BaseWidget_1.ToolbarWidgetTag.Save]);
|
299
301
|
this.addWidget(widget);
|
300
302
|
return widget;
|
@@ -302,22 +304,27 @@ class AbstractToolbar {
|
|
302
304
|
/**
|
303
305
|
* Adds an "Exit" button that, when clicked, calls `exitCallback`.
|
304
306
|
*
|
305
|
-
* **Note**: This is equivalent to
|
307
|
+
* **Note**: This is roughly equivalent to
|
306
308
|
* ```ts
|
307
309
|
* toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
|
308
310
|
* label: this.editor.localization.exit,
|
309
311
|
* icon: this.editor.icons.makeCloseIcon(),
|
312
|
+
*
|
313
|
+
* // labelOverride can be used to override label or icon.
|
314
|
+
* ...labelOverride,
|
310
315
|
* }, () => {
|
311
316
|
* exitCallback();
|
312
317
|
* });
|
313
318
|
* ```
|
319
|
+
* with some additional configuration.
|
314
320
|
*
|
315
321
|
* @final
|
316
322
|
*/
|
317
|
-
addExitButton(exitCallback) {
|
323
|
+
addExitButton(exitCallback, labelOverride = {}) {
|
318
324
|
return this.addTaggedActionButton([BaseWidget_1.ToolbarWidgetTag.Exit], {
|
319
325
|
label: this.editor.localization.exit,
|
320
326
|
icon: this.editor.icons.makeCloseIcon(),
|
327
|
+
...labelOverride,
|
321
328
|
}, () => {
|
322
329
|
exitCallback();
|
323
330
|
}, {
|
@@ -397,7 +404,10 @@ class AbstractToolbar {
|
|
397
404
|
addDefaultActionButtons() {
|
398
405
|
this.addUndoRedoButtons();
|
399
406
|
}
|
400
|
-
/**
|
407
|
+
/**
|
408
|
+
* Remove this toolbar from its container and clean up listeners.
|
409
|
+
* This should only be called **once** for a given toolbar.
|
410
|
+
*/
|
401
411
|
remove() {
|
402
412
|
this.closeColorPickerOverlay?.remove();
|
403
413
|
for (const listener of __classPrivateFieldGet(this, _AbstractToolbar_listeners, "f")) {
|
@@ -405,6 +415,9 @@ class AbstractToolbar {
|
|
405
415
|
}
|
406
416
|
__classPrivateFieldSet(this, _AbstractToolbar_listeners, [], "f");
|
407
417
|
this.onRemove();
|
418
|
+
for (const widget of __classPrivateFieldGet(this, _AbstractToolbar_widgetList, "f")) {
|
419
|
+
widget.remove();
|
420
|
+
}
|
408
421
|
}
|
409
422
|
/**
|
410
423
|
* Removes `listener` when {@link remove} is called.
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -235,11 +235,11 @@ class EdgeToolbar extends AbstractToolbar_1.default {
|
|
235
235
|
widget.removeCSSClassFromContainer('label-right');
|
236
236
|
if (tags.includes(BaseWidget_1.ToolbarWidgetTag.Save)) {
|
237
237
|
widget.addCSSClassToContainer('label-inline');
|
238
|
-
widget.addCSSClassToContainer('label-
|
238
|
+
widget.addCSSClassToContainer('label-left');
|
239
239
|
}
|
240
240
|
if (tags.includes(BaseWidget_1.ToolbarWidgetTag.Exit)) {
|
241
241
|
widget.addCSSClassToContainer('label-inline');
|
242
|
-
widget.addCSSClassToContainer('label-
|
242
|
+
widget.addCSSClassToContainer('label-right');
|
243
243
|
}
|
244
244
|
}
|
245
245
|
addWidgetInternal(widget) {
|
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
const types_1 = require("../../types");
|
7
6
|
const BaseWidget_1 = __importDefault(require("./BaseWidget"));
|
8
7
|
const constants_1 = require("../constants");
|
9
8
|
const isToolWidgetFocused = () => {
|
@@ -14,11 +13,8 @@ class BaseToolWidget extends BaseWidget_1.default {
|
|
14
13
|
constructor(editor, targetTool, id, localizationTable) {
|
15
14
|
super(editor, id, localizationTable);
|
16
15
|
this.targetTool = targetTool;
|
17
|
-
|
18
|
-
if (
|
19
|
-
throw new Error('Incorrect event type! (Expected ToolEnabled)');
|
20
|
-
}
|
21
|
-
if (toolEvt.tool === targetTool) {
|
16
|
+
this.targetTool.enabledValue().onUpdateAndNow(enabled => {
|
17
|
+
if (enabled) {
|
22
18
|
this.setSelected(true);
|
23
19
|
// Transfer focus to the current button, only if another toolbar button is
|
24
20
|
// focused.
|
@@ -28,12 +24,7 @@ class BaseToolWidget extends BaseWidget_1.default {
|
|
28
24
|
this.focus();
|
29
25
|
}
|
30
26
|
}
|
31
|
-
|
32
|
-
editor.notifier.on(types_1.EditorEventType.ToolDisabled, toolEvt => {
|
33
|
-
if (toolEvt.kind !== types_1.EditorEventType.ToolDisabled) {
|
34
|
-
throw new Error('Incorrect event type! (Expected ToolDisabled)');
|
35
|
-
}
|
36
|
-
if (toolEvt.tool === targetTool) {
|
27
|
+
else {
|
37
28
|
this.setSelected(false);
|
38
29
|
this.setDropdownVisible(false);
|
39
30
|
}
|
@@ -57,7 +48,7 @@ class BaseToolWidget extends BaseWidget_1.default {
|
|
57
48
|
}
|
58
49
|
}
|
59
50
|
onKeyPress(event) {
|
60
|
-
if (this.isSelected() && event.
|
51
|
+
if (this.isSelected() && event.code === 'Space' && this.hasDropdown) {
|
61
52
|
this.handleClick();
|
62
53
|
return true;
|
63
54
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -81,13 +81,17 @@ export default abstract class BaseWidget {
|
|
81
81
|
* @internal
|
82
82
|
*/
|
83
83
|
addTo(parent: HTMLElement): HTMLElement;
|
84
|
+
/**
|
85
|
+
* Remove this. This allows the widget to be added to a toolbar again
|
86
|
+
* in the future using {@link addTo}.
|
87
|
+
*/
|
88
|
+
remove(): void;
|
84
89
|
focus(): void;
|
85
90
|
/**
|
86
91
|
* @internal
|
87
92
|
*/
|
88
93
|
addCSSClassToContainer(className: string): void;
|
89
94
|
removeCSSClassFromContainer(className: string): void;
|
90
|
-
remove(): void;
|
91
95
|
protected updateIcon(): void;
|
92
96
|
setDisabled(disabled: boolean): void;
|
93
97
|
setSelected(selected: boolean): void;
|
@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
13
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
15
15
|
};
|
16
|
-
var _BaseWidget_hasDropdown, _BaseWidget_disabledDueToReadOnlyEditor, _BaseWidget_tags,
|
16
|
+
var _BaseWidget_instances, _BaseWidget_hasDropdown, _BaseWidget_disabledDueToReadOnlyEditor, _BaseWidget_tags, _BaseWidget_removeEditorListeners, _BaseWidget_addEditorListeners;
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
18
18
|
exports.ToolbarWidgetTag = void 0;
|
19
19
|
const ToolbarShortcutHandler_1 = __importDefault(require("../../tools/ToolbarShortcutHandler"));
|
@@ -32,6 +32,7 @@ var ToolbarWidgetTag;
|
|
32
32
|
})(ToolbarWidgetTag || (exports.ToolbarWidgetTag = ToolbarWidgetTag = {}));
|
33
33
|
class BaseWidget {
|
34
34
|
constructor(editor, id, localizationTable) {
|
35
|
+
_BaseWidget_instances.add(this);
|
35
36
|
this.editor = editor;
|
36
37
|
this.id = id;
|
37
38
|
this.dropdown = null;
|
@@ -44,8 +45,7 @@ class BaseWidget {
|
|
44
45
|
// Maps subWidget IDs to subWidgets.
|
45
46
|
this.subWidgets = {};
|
46
47
|
this.toplevel = true;
|
47
|
-
|
48
|
-
_BaseWidget_readOnlyListener.set(this, null);
|
48
|
+
_BaseWidget_removeEditorListeners.set(this, null);
|
49
49
|
this.localizationTable = localizationTable ?? editor.localization;
|
50
50
|
// Default layout manager
|
51
51
|
const defaultLayoutManager = new DropdownLayoutManager_1.default((text) => this.editor.announceForAccessibility(text), this.localizationTable);
|
@@ -66,12 +66,6 @@ class BaseWidget {
|
|
66
66
|
this.button.oncontextmenu = event => {
|
67
67
|
event.preventDefault();
|
68
68
|
};
|
69
|
-
const toolbarShortcutHandlers = this.editor.toolController.getMatchingTools(ToolbarShortcutHandler_1.default);
|
70
|
-
// If the onKeyPress function has been extended and the editor is configured to send keypress events to
|
71
|
-
// toolbar widgets,
|
72
|
-
if (toolbarShortcutHandlers.length > 0 && this.onKeyPress !== BaseWidget.prototype.onKeyPress) {
|
73
|
-
toolbarShortcutHandlers[0].registerListener(event => this.onKeyPress(event));
|
74
|
-
}
|
75
69
|
}
|
76
70
|
/**
|
77
71
|
* Should return a constant true or false value. If true (the default),
|
@@ -274,22 +268,18 @@ class BaseWidget {
|
|
274
268
|
if (this.container.parentElement) {
|
275
269
|
this.container.remove();
|
276
270
|
}
|
277
|
-
|
278
|
-
if (readOnly && this.shouldAutoDisableInReadOnlyEditor() && !this.disabled) {
|
279
|
-
this.setDisabled(true);
|
280
|
-
__classPrivateFieldSet(this, _BaseWidget_disabledDueToReadOnlyEditor, true, "f");
|
281
|
-
if (__classPrivateFieldGet(this, _BaseWidget_hasDropdown, "f")) {
|
282
|
-
this.dropdown?.requestHide();
|
283
|
-
}
|
284
|
-
}
|
285
|
-
else if (!readOnly && __classPrivateFieldGet(this, _BaseWidget_disabledDueToReadOnlyEditor, "f")) {
|
286
|
-
__classPrivateFieldSet(this, _BaseWidget_disabledDueToReadOnlyEditor, false, "f");
|
287
|
-
this.setDisabled(false);
|
288
|
-
}
|
289
|
-
}), "f");
|
271
|
+
__classPrivateFieldGet(this, _BaseWidget_instances, "m", _BaseWidget_addEditorListeners).call(this);
|
290
272
|
parent.appendChild(this.container);
|
291
273
|
return this.container;
|
292
274
|
}
|
275
|
+
/**
|
276
|
+
* Remove this. This allows the widget to be added to a toolbar again
|
277
|
+
* in the future using {@link addTo}.
|
278
|
+
*/
|
279
|
+
remove() {
|
280
|
+
this.container.remove();
|
281
|
+
__classPrivateFieldGet(this, _BaseWidget_removeEditorListeners, "f")?.call(this);
|
282
|
+
}
|
293
283
|
focus() {
|
294
284
|
this.button.focus();
|
295
285
|
}
|
@@ -302,11 +292,6 @@ class BaseWidget {
|
|
302
292
|
removeCSSClassFromContainer(className) {
|
303
293
|
this.container.classList.remove(className);
|
304
294
|
}
|
305
|
-
remove() {
|
306
|
-
this.container.remove();
|
307
|
-
__classPrivateFieldGet(this, _BaseWidget_readOnlyListener, "f")?.remove();
|
308
|
-
__classPrivateFieldSet(this, _BaseWidget_readOnlyListener, null, "f");
|
309
|
-
}
|
310
295
|
updateIcon() {
|
311
296
|
let newIcon = this.createIcon();
|
312
297
|
if (!newIcon) {
|
@@ -443,5 +428,37 @@ class BaseWidget {
|
|
443
428
|
}
|
444
429
|
}
|
445
430
|
}
|
446
|
-
_BaseWidget_hasDropdown = new WeakMap(), _BaseWidget_disabledDueToReadOnlyEditor = new WeakMap(), _BaseWidget_tags = new WeakMap(),
|
431
|
+
_BaseWidget_hasDropdown = new WeakMap(), _BaseWidget_disabledDueToReadOnlyEditor = new WeakMap(), _BaseWidget_tags = new WeakMap(), _BaseWidget_removeEditorListeners = new WeakMap(), _BaseWidget_instances = new WeakSet(), _BaseWidget_addEditorListeners = function _BaseWidget_addEditorListeners() {
|
432
|
+
__classPrivateFieldGet(this, _BaseWidget_removeEditorListeners, "f")?.call(this);
|
433
|
+
const toolbarShortcutHandlers = this.editor.toolController.getMatchingTools(ToolbarShortcutHandler_1.default);
|
434
|
+
let removeKeyPressListener = null;
|
435
|
+
// If the onKeyPress function has been extended and the editor is configured to send keypress events to
|
436
|
+
// toolbar widgets,
|
437
|
+
if (toolbarShortcutHandlers.length > 0 && this.onKeyPress !== BaseWidget.prototype.onKeyPress) {
|
438
|
+
const keyPressListener = (event) => this.onKeyPress(event);
|
439
|
+
const handler = toolbarShortcutHandlers[0];
|
440
|
+
handler.registerListener(keyPressListener);
|
441
|
+
removeKeyPressListener = () => {
|
442
|
+
handler.removeListener(keyPressListener);
|
443
|
+
};
|
444
|
+
}
|
445
|
+
const readOnlyListener = this.editor.isReadOnlyReactiveValue().onUpdateAndNow(readOnly => {
|
446
|
+
if (readOnly && this.shouldAutoDisableInReadOnlyEditor() && !this.disabled) {
|
447
|
+
this.setDisabled(true);
|
448
|
+
__classPrivateFieldSet(this, _BaseWidget_disabledDueToReadOnlyEditor, true, "f");
|
449
|
+
if (__classPrivateFieldGet(this, _BaseWidget_hasDropdown, "f")) {
|
450
|
+
this.dropdown?.requestHide();
|
451
|
+
}
|
452
|
+
}
|
453
|
+
else if (!readOnly && __classPrivateFieldGet(this, _BaseWidget_disabledDueToReadOnlyEditor, "f")) {
|
454
|
+
__classPrivateFieldSet(this, _BaseWidget_disabledDueToReadOnlyEditor, false, "f");
|
455
|
+
this.setDisabled(false);
|
456
|
+
}
|
457
|
+
});
|
458
|
+
__classPrivateFieldSet(this, _BaseWidget_removeEditorListeners, () => {
|
459
|
+
readOnlyListener.remove();
|
460
|
+
removeKeyPressListener?.();
|
461
|
+
__classPrivateFieldSet(this, _BaseWidget_removeEditorListeners, null, "f");
|
462
|
+
}, "f");
|
463
|
+
};
|
447
464
|
exports.default = BaseWidget;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -2,8 +2,9 @@ import { KeyPressEvent } from '../../inputEvents';
|
|
2
2
|
import Editor from '../../Editor';
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
4
|
import ActionButtonWidget from './ActionButtonWidget';
|
5
|
+
import { ActionButtonIcon } from '../types';
|
5
6
|
declare class SaveActionWidget extends ActionButtonWidget {
|
6
|
-
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void);
|
7
|
+
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>);
|
7
8
|
protected shouldAutoDisableInReadOnlyEditor(): boolean;
|
8
9
|
protected onKeyPress(event: KeyPressEvent): boolean;
|
9
10
|
mustBeInToplevelMenu(): boolean;
|
@@ -7,8 +7,12 @@ const ActionButtonWidget_1 = __importDefault(require("./ActionButtonWidget"));
|
|
7
7
|
const BaseWidget_1 = require("./BaseWidget");
|
8
8
|
const keybindings_1 = require("./keybindings");
|
9
9
|
class SaveActionWidget extends ActionButtonWidget_1.default {
|
10
|
-
constructor(editor, localization, saveCallback) {
|
11
|
-
super(editor, 'save-button',
|
10
|
+
constructor(editor, localization, saveCallback, labelOverride = {}) {
|
11
|
+
super(editor, 'save-button',
|
12
|
+
// Creates an icon
|
13
|
+
() => {
|
14
|
+
return labelOverride.icon ?? editor.icons.makeSaveIcon();
|
15
|
+
}, labelOverride.label ?? localization.save, saveCallback);
|
12
16
|
this.setTags([BaseWidget_1.ToolbarWidgetTag.Save]);
|
13
17
|
}
|
14
18
|
shouldAutoDisableInReadOnlyEditor() {
|
@@ -167,6 +167,7 @@ class BaseTool {
|
|
167
167
|
onDestroy() {
|
168
168
|
__classPrivateFieldGet(this, _BaseTool_readOnlyEditorChangeListener, "f")?.remove();
|
169
169
|
__classPrivateFieldSet(this, _BaseTool_readOnlyEditorChangeListener, null, "f");
|
170
|
+
__classPrivateFieldSet(this, _BaseTool_group, null, "f");
|
170
171
|
}
|
171
172
|
}
|
172
173
|
_BaseTool_enabled = new WeakMap(), _BaseTool_group = new WeakMap(), _BaseTool_inputMapper = new WeakMap(), _BaseTool_readOnlyEditorChangeListener = new WeakMap();
|
@@ -157,8 +157,10 @@ class Selection {
|
|
157
157
|
// Reset for the next drag
|
158
158
|
this.originalRegion = this.originalRegion.transformedBoundingBox(this.transform);
|
159
159
|
this.transform = math_1.Mat33.identity;
|
160
|
-
// Make the commands undo-able
|
161
|
-
|
160
|
+
// Make the commands undo-able, but only if the transform is non-empty.
|
161
|
+
if (!fullTransform.eq(math_1.Mat33.identity)) {
|
162
|
+
await this.editor.dispatch(new Selection.ApplyTransformationCommand(this, selectedElems, fullTransform));
|
163
|
+
}
|
162
164
|
// Clear renderings of any in-progress transformations
|
163
165
|
const wetInkRenderer = this.editor.display.getWetInkRenderer();
|
164
166
|
wetInkRenderer.clear();
|
@@ -13,9 +13,32 @@ export default class ToolController implements InputEventListener {
|
|
13
13
|
/** @internal */
|
14
14
|
constructor(editor: Editor, localization: ToolLocalization);
|
15
15
|
setTools(tools: BaseTool[], primaryToolGroup?: ToolEnabledGroup): void;
|
16
|
+
/**
|
17
|
+
* Add a tool that acts like one of the primary tools (only one primary tool can be enabled at a time).
|
18
|
+
*
|
19
|
+
* If the tool is already added to this, the tool is converted to a primary tool.
|
20
|
+
*
|
21
|
+
* This should be called before creating the app's toolbar.
|
22
|
+
*/
|
16
23
|
addPrimaryTool(tool: BaseTool): void;
|
17
24
|
getPrimaryTools(): BaseTool[];
|
18
25
|
addTool(tool: BaseTool): void;
|
26
|
+
/**
|
27
|
+
* Removes **and destroys** all tools in `tools` from this.
|
28
|
+
*/
|
29
|
+
removeAndDestroyTools(tools: BaseTool[]): void;
|
30
|
+
private insertTools;
|
31
|
+
/**
|
32
|
+
* Removes a tool from this' tool list and replaces it with `replaceWith`.
|
33
|
+
*
|
34
|
+
* If any of `toolsToInsert` have already been added to this, the tools are
|
35
|
+
* moved.
|
36
|
+
*
|
37
|
+
* This should be called before creating the editor's toolbar.
|
38
|
+
*/
|
39
|
+
insertToolsAfter(insertAfter: BaseTool, toolsToInsert: BaseTool[]): void;
|
40
|
+
/** @see {@link insertToolsAfter} */
|
41
|
+
insertToolsBefore(insertBefore: BaseTool, toolsToInsert: BaseTool[]): void;
|
19
42
|
private onEventInternal;
|
20
43
|
/** Alias for {@link dispatchInputEvent}. */
|
21
44
|
onEvent(event: InputEvt): boolean;
|
@@ -115,14 +115,21 @@ class ToolController {
|
|
115
115
|
this.tools = tools;
|
116
116
|
this.primaryToolGroup = primaryToolGroup ?? new ToolEnabledGroup_1.default();
|
117
117
|
}
|
118
|
-
|
119
|
-
|
118
|
+
/**
|
119
|
+
* Add a tool that acts like one of the primary tools (only one primary tool can be enabled at a time).
|
120
|
+
*
|
121
|
+
* If the tool is already added to this, the tool is converted to a primary tool.
|
122
|
+
*
|
123
|
+
* This should be called before creating the app's toolbar.
|
124
|
+
*/
|
120
125
|
addPrimaryTool(tool) {
|
121
126
|
tool.setToolGroup(this.primaryToolGroup);
|
122
127
|
if (tool.isEnabled()) {
|
123
128
|
this.primaryToolGroup.notifyEnabled(tool);
|
124
129
|
}
|
125
|
-
this.
|
130
|
+
if (!this.tools.includes(tool)) {
|
131
|
+
this.addTool(tool);
|
132
|
+
}
|
126
133
|
}
|
127
134
|
getPrimaryTools() {
|
128
135
|
return this.tools.filter(tool => {
|
@@ -131,8 +138,62 @@ class ToolController {
|
|
131
138
|
}
|
132
139
|
// Add a tool to the end of this' tool list (the added tool receives events after tools already added to this).
|
133
140
|
// This should be called before creating the app's toolbar.
|
141
|
+
//
|
142
|
+
// A tool should only be added once.
|
134
143
|
addTool(tool) {
|
135
|
-
|
144
|
+
// Only add if not already present.
|
145
|
+
if (!this.tools.includes(tool)) {
|
146
|
+
this.tools.push(tool);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
/**
|
150
|
+
* Removes **and destroys** all tools in `tools` from this.
|
151
|
+
*/
|
152
|
+
removeAndDestroyTools(tools) {
|
153
|
+
const newTools = [];
|
154
|
+
for (const tool of this.tools) {
|
155
|
+
if (tools.includes(tool)) {
|
156
|
+
if (this.activeTool === tool) {
|
157
|
+
this.activeTool = null;
|
158
|
+
}
|
159
|
+
tool.onDestroy();
|
160
|
+
}
|
161
|
+
else {
|
162
|
+
newTools.push(tool);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
this.tools = newTools;
|
166
|
+
}
|
167
|
+
insertTools(insertNear, toolsToInsert, mode) {
|
168
|
+
this.tools = this.tools.filter(tool => !toolsToInsert.includes(tool));
|
169
|
+
const newTools = [];
|
170
|
+
for (const tool of this.tools) {
|
171
|
+
if (mode === 'after') {
|
172
|
+
newTools.push(tool);
|
173
|
+
}
|
174
|
+
if (tool === insertNear) {
|
175
|
+
newTools.push(...toolsToInsert);
|
176
|
+
}
|
177
|
+
if (mode === 'before') {
|
178
|
+
newTools.push(tool);
|
179
|
+
}
|
180
|
+
}
|
181
|
+
this.tools = newTools;
|
182
|
+
}
|
183
|
+
/**
|
184
|
+
* Removes a tool from this' tool list and replaces it with `replaceWith`.
|
185
|
+
*
|
186
|
+
* If any of `toolsToInsert` have already been added to this, the tools are
|
187
|
+
* moved.
|
188
|
+
*
|
189
|
+
* This should be called before creating the editor's toolbar.
|
190
|
+
*/
|
191
|
+
insertToolsAfter(insertAfter, toolsToInsert) {
|
192
|
+
this.insertTools(insertAfter, toolsToInsert, 'after');
|
193
|
+
}
|
194
|
+
/** @see {@link insertToolsAfter} */
|
195
|
+
insertToolsBefore(insertBefore, toolsToInsert) {
|
196
|
+
this.insertTools(insertBefore, toolsToInsert, 'before');
|
136
197
|
}
|
137
198
|
// @internal use `dispatchEvent` rather than calling `onEvent` directly.
|
138
199
|
onEventInternal(event) {
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/dist/cjs/version.js
CHANGED
package/dist/mjs/Editor.d.ts
CHANGED
@@ -64,6 +64,14 @@ export interface EditorSettings {
|
|
64
64
|
* Additional messages to show in the "about" dialog.
|
65
65
|
*/
|
66
66
|
notices: AboutDialogEntry[];
|
67
|
+
/**
|
68
|
+
* Information about the app/website js-draw is running within
|
69
|
+
* to show at the beginning of the about dialog.
|
70
|
+
*/
|
71
|
+
appInfo: {
|
72
|
+
name: string;
|
73
|
+
version?: string;
|
74
|
+
} | null;
|
67
75
|
}
|
68
76
|
/**
|
69
77
|
* The main entrypoint for the full editor.
|
package/dist/mjs/Editor.mjs
CHANGED
@@ -106,6 +106,7 @@ export class Editor {
|
|
106
106
|
keyboardShortcutOverrides: settings.keyboardShortcutOverrides ?? {},
|
107
107
|
iconProvider: settings.iconProvider ?? new IconProvider(),
|
108
108
|
notices: [],
|
109
|
+
appInfo: settings.appInfo ? { ...settings.appInfo } : null,
|
109
110
|
};
|
110
111
|
// Validate settings
|
111
112
|
if (this.settings.minZoom > this.settings.maxZoom) {
|
@@ -644,12 +645,16 @@ export class Editor {
|
|
644
645
|
htmlEvent.preventDefault();
|
645
646
|
}
|
646
647
|
});
|
647
|
-
elem.addEventListener('
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
648
|
+
elem.addEventListener('focusout', (event) => {
|
649
|
+
const stillHasFocus = event.relatedTarget && elem.contains(event.relatedTarget);
|
650
|
+
if (!stillHasFocus) {
|
651
|
+
for (const event of keysDown) {
|
652
|
+
this.toolController.dispatchInputEvent({
|
653
|
+
...event,
|
654
|
+
kind: InputEvtType.KeyUpEvent,
|
655
|
+
});
|
656
|
+
}
|
657
|
+
keysDown = [];
|
653
658
|
}
|
654
659
|
});
|
655
660
|
// Allow drop.
|
@@ -1116,16 +1121,34 @@ export class Editor {
|
|
1116
1121
|
showAboutDialog() {
|
1117
1122
|
const iconLicenseText = this.icons.licenseInfo();
|
1118
1123
|
const notices = [];
|
1124
|
+
if (this.settings.appInfo) {
|
1125
|
+
const versionLines = [];
|
1126
|
+
if (this.settings.appInfo.version) {
|
1127
|
+
versionLines.push(`v${this.settings.appInfo.version}`, '');
|
1128
|
+
}
|
1129
|
+
notices.push({
|
1130
|
+
heading: `${this.settings.appInfo.name}`,
|
1131
|
+
text: [
|
1132
|
+
...versionLines,
|
1133
|
+
`Image editor library: js-draw v${version.number}.`,
|
1134
|
+
].join('\n'),
|
1135
|
+
});
|
1136
|
+
}
|
1137
|
+
else {
|
1138
|
+
notices.push({
|
1139
|
+
heading: 'js-draw',
|
1140
|
+
text: `v${version.number}`,
|
1141
|
+
});
|
1142
|
+
}
|
1119
1143
|
notices.push({
|
1120
|
-
heading: '
|
1144
|
+
heading: 'Developer information',
|
1121
1145
|
text: [
|
1122
|
-
`v${version.number}`,
|
1123
|
-
'',
|
1124
1146
|
'Image debug information (from when this dialog was opened):',
|
1125
1147
|
` ${this.viewport.getScaleFactor()}x zoom, ${180 / Math.PI * this.viewport.getRotationAngle()} rotation`,
|
1126
1148
|
` ${this.image.estimateNumElements()} components`,
|
1127
1149
|
` ${this.getImportExportRect().w}x${this.getImportExportRect().h} size`,
|
1128
1150
|
].join('\n'),
|
1151
|
+
minimized: true,
|
1129
1152
|
});
|
1130
1153
|
notices.push({
|
1131
1154
|
heading: 'Libraries',
|
package/dist/mjs/SVGLoader.d.ts
CHANGED
@@ -11,9 +11,14 @@ export type SVGLoaderUnknownStyleAttribute = {
|
|
11
11
|
value: string;
|
12
12
|
priority?: string;
|
13
13
|
};
|
14
|
+
export declare enum SVGLoaderLoadMethod {
|
15
|
+
IFrame = "iframe",
|
16
|
+
DOMParser = "domparser"
|
17
|
+
}
|
14
18
|
export interface SVGLoaderOptions {
|
15
19
|
sanitize?: boolean;
|
16
20
|
disableUnknownObjectWarnings?: boolean;
|
21
|
+
loadMethod?: 'iframe' | 'domparser';
|
17
22
|
}
|
18
23
|
export default class SVGLoader implements ImageLoader {
|
19
24
|
private source;
|
@@ -32,6 +37,7 @@ export default class SVGLoader implements ImageLoader {
|
|
32
37
|
private attachUnrecognisedAttrs;
|
33
38
|
private addPath;
|
34
39
|
private addBackground;
|
40
|
+
private getComputedStyle;
|
35
41
|
private getTransform;
|
36
42
|
private makeText;
|
37
43
|
private addText;
|