js-draw 1.9.1 → 1.11.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/dist/Editor.css +48 -1
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +41 -0
- package/dist/cjs/Editor.js +9 -0
- package/dist/cjs/Pointer.js +1 -1
- package/dist/cjs/commands/Erase.d.ts +22 -2
- package/dist/cjs/commands/Erase.js +22 -2
- package/dist/cjs/commands/invertCommand.js +5 -0
- package/dist/cjs/commands/uniteCommands.d.ts +36 -0
- package/dist/cjs/commands/uniteCommands.js +36 -0
- package/dist/cjs/components/AbstractComponent.d.ts +8 -0
- package/dist/cjs/components/AbstractComponent.js +28 -8
- package/dist/cjs/components/ImageComponent.d.ts +12 -0
- package/dist/cjs/components/ImageComponent.js +16 -9
- package/dist/cjs/components/Stroke.d.ts +16 -2
- package/dist/cjs/components/Stroke.js +17 -1
- package/dist/cjs/components/builders/ArrowBuilder.js +3 -3
- package/dist/cjs/components/builders/CircleBuilder.js +3 -3
- package/dist/cjs/components/builders/FreehandLineBuilder.js +3 -3
- package/dist/cjs/components/builders/LineBuilder.js +3 -3
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +3 -3
- package/dist/cjs/components/builders/RectangleBuilder.js +5 -6
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +168 -0
- package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
- package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.js +46 -0
- package/dist/cjs/components/builders/types.d.ts +12 -0
- package/dist/cjs/image/EditorImage.d.ts +32 -1
- package/dist/cjs/image/EditorImage.js +32 -1
- package/dist/cjs/rendering/RenderablePathSpec.d.ts +5 -1
- package/dist/cjs/rendering/RenderablePathSpec.js +4 -0
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +18 -2
- package/dist/cjs/toolbar/AbstractToolbar.js +46 -30
- package/dist/cjs/toolbar/IconProvider.d.ts +2 -0
- package/dist/cjs/toolbar/IconProvider.js +17 -0
- package/dist/cjs/toolbar/localization.d.ts +3 -0
- package/dist/cjs/toolbar/localization.js +4 -1
- package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -1
- package/dist/cjs/toolbar/widgets/ExitActionWidget.d.ts +12 -0
- package/dist/cjs/toolbar/widgets/ExitActionWidget.js +32 -0
- package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +4 -3
- package/dist/cjs/toolbar/widgets/HandToolWidget.js +24 -13
- package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
- package/dist/cjs/toolbar/widgets/InsertImageWidget.js +102 -22
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +50 -20
- package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
- package/dist/cjs/toolbar/widgets/layout/types.d.ts +1 -1
- package/dist/cjs/tools/Pen.d.ts +9 -0
- package/dist/cjs/tools/Pen.js +82 -3
- package/dist/cjs/tools/SelectionTool/Selection.d.ts +4 -0
- package/dist/cjs/tools/SelectionTool/Selection.js +56 -12
- package/dist/cjs/tools/SelectionTool/SelectionTool.d.ts +1 -0
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +19 -1
- package/dist/cjs/tools/TextTool.js +5 -1
- package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +0 -1
- package/dist/cjs/tools/ToolSwitcherShortcut.js +0 -1
- package/dist/cjs/tools/keybindings.d.ts +1 -0
- package/dist/cjs/tools/keybindings.js +3 -1
- package/dist/cjs/tools/util/StationaryPenDetector.d.ts +22 -0
- package/dist/cjs/tools/util/StationaryPenDetector.js +95 -0
- package/dist/cjs/util/ReactiveValue.d.ts +2 -0
- package/dist/cjs/util/ReactiveValue.js +2 -0
- package/dist/cjs/util/lib.d.ts +1 -0
- package/dist/cjs/util/lib.js +4 -1
- package/dist/cjs/util/waitForImageLoaded.d.ts +2 -0
- package/dist/cjs/util/waitForImageLoaded.js +12 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +41 -0
- package/dist/mjs/Editor.mjs +9 -0
- package/dist/mjs/Pointer.mjs +1 -1
- package/dist/mjs/commands/Erase.d.ts +22 -2
- package/dist/mjs/commands/Erase.mjs +22 -2
- package/dist/mjs/commands/invertCommand.mjs +5 -0
- package/dist/mjs/commands/uniteCommands.d.ts +36 -0
- package/dist/mjs/commands/uniteCommands.mjs +36 -0
- package/dist/mjs/components/AbstractComponent.d.ts +8 -0
- package/dist/mjs/components/AbstractComponent.mjs +28 -8
- package/dist/mjs/components/ImageComponent.d.ts +12 -0
- package/dist/mjs/components/ImageComponent.mjs +16 -9
- package/dist/mjs/components/Stroke.d.ts +16 -2
- package/dist/mjs/components/Stroke.mjs +17 -1
- package/dist/mjs/components/builders/ArrowBuilder.mjs +3 -2
- package/dist/mjs/components/builders/CircleBuilder.mjs +3 -2
- package/dist/mjs/components/builders/FreehandLineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/LineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/RectangleBuilder.mjs +5 -4
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +166 -0
- package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
- package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.mjs +44 -0
- package/dist/mjs/components/builders/types.d.ts +12 -0
- package/dist/mjs/image/EditorImage.d.ts +32 -1
- package/dist/mjs/image/EditorImage.mjs +32 -1
- package/dist/mjs/rendering/RenderablePathSpec.d.ts +5 -1
- package/dist/mjs/rendering/RenderablePathSpec.mjs +4 -0
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +18 -2
- package/dist/mjs/toolbar/AbstractToolbar.mjs +46 -30
- package/dist/mjs/toolbar/IconProvider.d.ts +2 -0
- package/dist/mjs/toolbar/IconProvider.mjs +17 -0
- package/dist/mjs/toolbar/localization.d.ts +3 -0
- package/dist/mjs/toolbar/localization.mjs +4 -1
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -1
- package/dist/mjs/toolbar/widgets/ExitActionWidget.d.ts +12 -0
- package/dist/mjs/toolbar/widgets/ExitActionWidget.mjs +27 -0
- package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +4 -3
- package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +24 -13
- package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
- package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +102 -22
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +50 -20
- package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
- package/dist/mjs/toolbar/widgets/layout/types.d.ts +1 -1
- package/dist/mjs/tools/Pen.d.ts +9 -0
- package/dist/mjs/tools/Pen.mjs +82 -3
- package/dist/mjs/tools/SelectionTool/Selection.d.ts +4 -0
- package/dist/mjs/tools/SelectionTool/Selection.mjs +56 -12
- package/dist/mjs/tools/SelectionTool/SelectionTool.d.ts +1 -0
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +20 -2
- package/dist/mjs/tools/TextTool.mjs +5 -1
- package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +0 -1
- package/dist/mjs/tools/ToolSwitcherShortcut.mjs +0 -1
- package/dist/mjs/tools/keybindings.d.ts +1 -0
- package/dist/mjs/tools/keybindings.mjs +2 -0
- package/dist/mjs/tools/util/StationaryPenDetector.d.ts +22 -0
- package/dist/mjs/tools/util/StationaryPenDetector.mjs +92 -0
- package/dist/mjs/util/ReactiveValue.d.ts +2 -0
- package/dist/mjs/util/ReactiveValue.mjs +2 -0
- package/dist/mjs/util/lib.d.ts +1 -0
- package/dist/mjs/util/lib.mjs +1 -0
- package/dist/mjs/util/waitForImageLoaded.d.ts +2 -0
- package/dist/mjs/util/waitForImageLoaded.mjs +10 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +3 -3
- package/src/Editor.scss +7 -0
- package/src/toolbar/AbstractToolbar.scss +20 -0
- package/src/toolbar/toolbar.scss +1 -1
- package/src/toolbar/widgets/InsertImageWidget.scss +6 -1
- package/src/toolbar/widgets/PenToolWidget.scss +33 -0
- package/src/tools/SelectionTool/SelectionTool.scss +6 -0
- package/src/toolbar/widgets/PenToolWidget.css +0 -2
@@ -35,6 +35,7 @@ const DocumentPropertiesWidget_1 = __importDefault(require("./widgets/DocumentPr
|
|
35
35
|
const math_1 = require("@js-draw/math");
|
36
36
|
const constants_1 = require("./constants");
|
37
37
|
const SaveActionWidget_1 = __importDefault(require("./widgets/SaveActionWidget"));
|
38
|
+
const ExitActionWidget_1 = __importDefault(require("./widgets/ExitActionWidget"));
|
38
39
|
class AbstractToolbar {
|
39
40
|
/** @internal */
|
40
41
|
constructor(editor, localizationTable = localization_1.defaultToolbarLocalization) {
|
@@ -297,14 +298,13 @@ class AbstractToolbar {
|
|
297
298
|
*/
|
298
299
|
addSaveButton(saveCallback, labelOverride = {}) {
|
299
300
|
const widget = new SaveActionWidget_1.default(this.editor, this.localizationTable, saveCallback, labelOverride);
|
300
|
-
widget.setTags([BaseWidget_1.ToolbarWidgetTag.Save]);
|
301
301
|
this.addWidget(widget);
|
302
302
|
return widget;
|
303
303
|
}
|
304
304
|
/**
|
305
305
|
* Adds an "Exit" button that, when clicked, calls `exitCallback`.
|
306
306
|
*
|
307
|
-
* **Note**: This is roughly equivalent to
|
307
|
+
* **Note**: This is *roughly* equivalent to
|
308
308
|
* ```ts
|
309
309
|
* toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
|
310
310
|
* label: this.editor.localization.exit,
|
@@ -321,15 +321,9 @@ class AbstractToolbar {
|
|
321
321
|
* @final
|
322
322
|
*/
|
323
323
|
addExitButton(exitCallback, labelOverride = {}) {
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
...labelOverride,
|
328
|
-
}, () => {
|
329
|
-
exitCallback();
|
330
|
-
}, {
|
331
|
-
autoDisableInReadOnlyEditors: false,
|
332
|
-
});
|
324
|
+
const widget = new ExitActionWidget_1.default(this.editor, this.localizationTable, exitCallback, labelOverride);
|
325
|
+
this.addWidget(widget);
|
326
|
+
return widget;
|
333
327
|
}
|
334
328
|
/**
|
335
329
|
* Adds undo and redo buttons that trigger the editor's built-in undo and redo
|
@@ -377,27 +371,49 @@ class AbstractToolbar {
|
|
377
371
|
});
|
378
372
|
}
|
379
373
|
/**
|
380
|
-
* Adds
|
374
|
+
* Adds widgets for pen/eraser/selection/text/pan-zoom primary tools.
|
375
|
+
*
|
376
|
+
* If `filter` returns `false` for a tool, no widget is added for that tool.
|
377
|
+
* See {@link addDefaultToolWidgets}
|
381
378
|
*/
|
382
|
-
|
383
|
-
const
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
379
|
+
addWidgetsForPrimaryTools(filter) {
|
380
|
+
for (const tool of this.editor.toolController.getPrimaryTools()) {
|
381
|
+
if (filter && !filter?.(tool)) {
|
382
|
+
continue;
|
383
|
+
}
|
384
|
+
if (tool instanceof Pen_1.default) {
|
385
|
+
const widget = new PenToolWidget_1.default(this.editor, tool, this.localizationTable);
|
386
|
+
this.addWidget(widget);
|
387
|
+
}
|
388
|
+
else if (tool instanceof Eraser_1.default) {
|
389
|
+
this.addWidget(new EraserToolWidget_1.default(this.editor, tool, this.localizationTable));
|
390
|
+
}
|
391
|
+
else if (tool instanceof SelectionTool_1.default) {
|
392
|
+
this.addWidget(new SelectionToolWidget_1.default(this.editor, tool, this.localizationTable));
|
393
|
+
}
|
394
|
+
else if (tool instanceof TextTool_1.default) {
|
395
|
+
this.addWidget(new TextToolWidget_1.default(this.editor, tool, this.localizationTable));
|
396
|
+
}
|
397
|
+
else if (tool instanceof PanZoom_1.default) {
|
398
|
+
this.addWidget(new HandToolWidget_1.default(this.editor, tool, this.localizationTable));
|
399
|
+
}
|
400
400
|
}
|
401
|
+
}
|
402
|
+
/**
|
403
|
+
* Adds toolbar widgets based on the enabled tools, and additional tool-like
|
404
|
+
* buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}).
|
405
|
+
*/
|
406
|
+
addDefaultToolWidgets() {
|
407
|
+
this.addWidgetsForPrimaryTools();
|
408
|
+
this.addDefaultEditorControlWidgets();
|
409
|
+
}
|
410
|
+
/**
|
411
|
+
* Adds widgets that don't correspond to tools, but do allow the user to control
|
412
|
+
* the editor in some way.
|
413
|
+
*
|
414
|
+
* By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}.
|
415
|
+
*/
|
416
|
+
addDefaultEditorControlWidgets() {
|
401
417
|
this.addWidget(new DocumentPropertiesWidget_1.default(this.editor, this.localizationTable));
|
402
418
|
this.addWidget(new InsertImageWidget_1.default(this.editor, this.localizationTable));
|
403
419
|
}
|
@@ -51,6 +51,8 @@ export default class IconProvider {
|
|
51
51
|
makePenIcon(penStyle: PenStyle): IconElemType;
|
52
52
|
makeIconFromFactory(penStyle: PenStyle): IconElemType;
|
53
53
|
makePipetteIcon(color?: Color4): IconElemType;
|
54
|
+
makeShapeAutocorrectIcon(): IconElemType;
|
55
|
+
makeStrokeSmoothingIcon(): IconElemType;
|
54
56
|
/** Unused. @deprecated */
|
55
57
|
makeFormatSelectionIcon(): IconElemType;
|
56
58
|
makeResizeImageToSelectionIcon(): IconElemType;
|
@@ -649,6 +649,23 @@ class IconProvider {
|
|
649
649
|
icon.setAttribute('viewBox', '5 -40 140 140');
|
650
650
|
return icon;
|
651
651
|
}
|
652
|
+
makeShapeAutocorrectIcon() {
|
653
|
+
const fill = 'none';
|
654
|
+
const strokeColor = 'var(--icon-color)';
|
655
|
+
return this.makeIconFromPath(`
|
656
|
+
m 79.129476,33.847107 9.967823,-0.03218 v 55 h -55 l 0.03218,-9.96782
|
657
|
+
M 71.1,40.8 a 30,30 0 0 1 -30,30 30,30 0 0 1 -30,-30 30,30 0 0 1 30,-30 30,30 0 0 1 30,30 L 71.1,40.8
|
658
|
+
M 34.1,58.8 v -25 h 25 v 0
|
659
|
+
`, fill, strokeColor, '7px');
|
660
|
+
}
|
661
|
+
makeStrokeSmoothingIcon() {
|
662
|
+
const fill = 'none';
|
663
|
+
const strokeColor = 'var(--icon-color)';
|
664
|
+
return this.makeIconFromPath(`
|
665
|
+
m 31,83.2 c -50,0 30,-65 -20,-65
|
666
|
+
M 75,17.3 40,59.7 38.2,77.6 55.5,72.4 90.5,30 Z
|
667
|
+
`, fill, strokeColor, '7px');
|
668
|
+
}
|
652
669
|
/** Unused. @deprecated */
|
653
670
|
makeFormatSelectionIcon() {
|
654
671
|
return this.makeIconFromPath(`
|
@@ -10,6 +10,8 @@ export interface ToolbarLocalization {
|
|
10
10
|
arrowPen: string;
|
11
11
|
image: string;
|
12
12
|
inputAltText: string;
|
13
|
+
decreaseImageSize: string;
|
14
|
+
resetImage: string;
|
13
15
|
chooseFile: string;
|
14
16
|
dragAndDropHereOrBrowse: string;
|
15
17
|
cancel: string;
|
@@ -48,6 +50,7 @@ export interface ToolbarLocalization {
|
|
48
50
|
toggleOverflow: string;
|
49
51
|
about: string;
|
50
52
|
inputStabilization: string;
|
53
|
+
strokeAutocorrect: string;
|
51
54
|
errorImageHasZeroSize: string;
|
52
55
|
closeSidebar: (toolName: string) => string;
|
53
56
|
dropdownShown: (toolName: string) => string;
|
@@ -10,6 +10,8 @@ exports.defaultToolbarLocalization = {
|
|
10
10
|
image: 'Image',
|
11
11
|
reformatSelection: 'Format selection',
|
12
12
|
inputAltText: 'Alt text',
|
13
|
+
decreaseImageSize: 'Decrease size',
|
14
|
+
resetImage: 'Reset',
|
13
15
|
chooseFile: 'Choose file',
|
14
16
|
dragAndDropHereOrBrowse: 'Drag and drop here\nor\n{{browse}}',
|
15
17
|
submit: 'Submit',
|
@@ -40,7 +42,8 @@ exports.defaultToolbarLocalization = {
|
|
40
42
|
enableAutoresizeOption: 'Auto-resize',
|
41
43
|
toggleOverflow: 'More',
|
42
44
|
about: 'About',
|
43
|
-
inputStabilization: '
|
45
|
+
inputStabilization: 'Stabilization',
|
46
|
+
strokeAutocorrect: 'Autocorrect',
|
44
47
|
touchPanning: 'Touchscreen panning',
|
45
48
|
roundedTipPen: 'Round',
|
46
49
|
flatTipPen: 'Flat',
|
@@ -53,7 +53,7 @@ class BaseWidget {
|
|
53
53
|
this.layoutManager = defaultLayoutManager;
|
54
54
|
this.icon = null;
|
55
55
|
this.container = document.createElement('div');
|
56
|
-
this.container.classList.add(`${constants_1.toolbarCSSPrefix}toolContainer`, `${constants_1.toolbarCSSPrefix}toolButtonContainer`);
|
56
|
+
this.container.classList.add(`${constants_1.toolbarCSSPrefix}toolContainer`, `${constants_1.toolbarCSSPrefix}toolButtonContainer`, `${constants_1.toolbarCSSPrefix}internalWidgetId--${id.replace(/[^a-zA-Z0-9_]/g, '-')}`);
|
57
57
|
this.dropdownContent = document.createElement('div');
|
58
58
|
__classPrivateFieldSet(this, _BaseWidget_hasDropdown, false, "f");
|
59
59
|
this.button = document.createElement('div');
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { KeyPressEvent } from '../../inputEvents';
|
2
|
+
import Editor from '../../Editor';
|
3
|
+
import { ToolbarLocalization } from '../localization';
|
4
|
+
import ActionButtonWidget from './ActionButtonWidget';
|
5
|
+
import { ActionButtonIcon } from '../types';
|
6
|
+
declare class ExitActionWidget extends ActionButtonWidget {
|
7
|
+
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>);
|
8
|
+
protected shouldAutoDisableInReadOnlyEditor(): boolean;
|
9
|
+
protected onKeyPress(event: KeyPressEvent): boolean;
|
10
|
+
mustBeInToplevelMenu(): boolean;
|
11
|
+
}
|
12
|
+
export default ExitActionWidget;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const ActionButtonWidget_1 = __importDefault(require("./ActionButtonWidget"));
|
7
|
+
const BaseWidget_1 = require("./BaseWidget");
|
8
|
+
const keybindings_1 = require("./keybindings");
|
9
|
+
class ExitActionWidget extends ActionButtonWidget_1.default {
|
10
|
+
constructor(editor, localization, saveCallback, labelOverride = {}) {
|
11
|
+
super(editor, 'exit-button',
|
12
|
+
// Creates an icon
|
13
|
+
() => {
|
14
|
+
return labelOverride.icon ?? editor.icons.makeCloseIcon();
|
15
|
+
}, labelOverride.label ?? localization.exit, saveCallback);
|
16
|
+
this.setTags([BaseWidget_1.ToolbarWidgetTag.Exit]);
|
17
|
+
}
|
18
|
+
shouldAutoDisableInReadOnlyEditor() {
|
19
|
+
return false;
|
20
|
+
}
|
21
|
+
onKeyPress(event) {
|
22
|
+
if (this.editor.shortcuts.matchesShortcut(keybindings_1.exitKeyboardShortcut, event)) {
|
23
|
+
this.clickAction();
|
24
|
+
return true;
|
25
|
+
}
|
26
|
+
return super.onKeyPress(event);
|
27
|
+
}
|
28
|
+
mustBeInToplevelMenu() {
|
29
|
+
return true;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
exports.default = ExitActionWidget;
|
@@ -4,11 +4,12 @@ import { ToolbarLocalization } from '../localization';
|
|
4
4
|
import BaseToolWidget from './BaseToolWidget';
|
5
5
|
import { SavedToolbuttonState } from './BaseWidget';
|
6
6
|
export default class HandToolWidget extends BaseToolWidget {
|
7
|
-
protected overridePanZoomTool: PanZoom;
|
8
7
|
private allowTogglingBaseTool;
|
9
|
-
|
10
|
-
|
8
|
+
protected overridePanZoomTool: PanZoom;
|
9
|
+
constructor(editor: Editor, tool: PanZoom, localizationTable: ToolbarLocalization);
|
11
10
|
private static getPrimaryHandTool;
|
11
|
+
private static getOverrideHandTool;
|
12
|
+
protected shouldAutoDisableInReadOnlyEditor(): boolean;
|
12
13
|
protected getTitle(): string;
|
13
14
|
protected createIcon(): Element;
|
14
15
|
protected handleClick(): void;
|
@@ -124,34 +124,45 @@ class HandModeWidget extends BaseWidget_1.default {
|
|
124
124
|
}
|
125
125
|
class HandToolWidget extends BaseToolWidget_1.default {
|
126
126
|
constructor(editor,
|
127
|
-
//
|
128
|
-
//
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
127
|
+
// Can either be the primary pan/zoom tool (in the primary tools list) or
|
128
|
+
// the override pan/zoom tool.
|
129
|
+
// If the override pan/zoom tool, the primary will be gotten from the editor's
|
130
|
+
// tool controller.
|
131
|
+
// If the primary, the override will be gotten from the editor's tool controller.
|
132
|
+
tool, localizationTable) {
|
133
|
+
const isGivenToolPrimary = editor.toolController.getPrimaryTools().includes(tool);
|
134
|
+
const primaryTool = (isGivenToolPrimary ? tool : HandToolWidget.getPrimaryHandTool(editor.toolController))
|
135
|
+
?? tool;
|
136
|
+
super(editor, primaryTool, 'hand-tool-widget', localizationTable);
|
137
|
+
this.overridePanZoomTool =
|
138
|
+
(isGivenToolPrimary ? HandToolWidget.getOverrideHandTool(editor.toolController) : tool)
|
139
|
+
?? tool;
|
134
140
|
// Only allow toggling a hand tool if we're using the primary hand tool and not the override
|
135
141
|
// hand tool for this button.
|
136
|
-
this.allowTogglingBaseTool =
|
142
|
+
this.allowTogglingBaseTool = primaryTool !== null;
|
137
143
|
// Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
|
138
144
|
if (!this.allowTogglingBaseTool) {
|
139
145
|
this.container.classList.add('dropdownShowable');
|
140
146
|
}
|
141
147
|
// Controls for the overriding hand tool.
|
142
|
-
const touchPanningWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoom_1.PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
|
143
|
-
const rotationLockWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoom_1.PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
|
148
|
+
const touchPanningWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoom_1.PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
|
149
|
+
const rotationLockWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoom_1.PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
|
144
150
|
this.addSubWidget(touchPanningWidget);
|
145
151
|
this.addSubWidget(rotationLockWidget);
|
146
152
|
}
|
147
|
-
shouldAutoDisableInReadOnlyEditor() {
|
148
|
-
return false;
|
149
|
-
}
|
150
153
|
static getPrimaryHandTool(toolController) {
|
151
154
|
const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom_1.default);
|
152
155
|
const primaryPanZoomTool = primaryPanZoomToolList[0];
|
153
156
|
return primaryPanZoomTool;
|
154
157
|
}
|
158
|
+
static getOverrideHandTool(toolController) {
|
159
|
+
const panZoomToolList = toolController.getMatchingTools(PanZoom_1.default);
|
160
|
+
const panZoomTool = panZoomToolList[0];
|
161
|
+
return panZoomTool;
|
162
|
+
}
|
163
|
+
shouldAutoDisableInReadOnlyEditor() {
|
164
|
+
return false;
|
165
|
+
}
|
155
166
|
getTitle() {
|
156
167
|
return this.localizationTable.handTool;
|
157
168
|
}
|
@@ -3,10 +3,10 @@ import { ToolbarLocalization } from '../localization';
|
|
3
3
|
import BaseWidget from './BaseWidget';
|
4
4
|
export default class InsertImageWidget extends BaseWidget {
|
5
5
|
private imagePreview;
|
6
|
+
private image;
|
6
7
|
private selectedFiles;
|
7
8
|
private imageAltTextInput;
|
8
9
|
private statusView;
|
9
|
-
private imageBase64URL;
|
10
10
|
private submitButton;
|
11
11
|
constructor(editor: Editor, localization?: ToolbarLocalization);
|
12
12
|
protected getTitle(): string;
|
@@ -15,6 +15,7 @@ export default class InsertImageWidget extends BaseWidget {
|
|
15
15
|
protected handleClick(): void;
|
16
16
|
private static nextInputId;
|
17
17
|
protected fillDropdown(dropdown: HTMLElement): boolean;
|
18
|
+
private onImageDataUpdate;
|
18
19
|
private hideDialog;
|
19
20
|
private updateImageSizeDisplay;
|
20
21
|
private updateInputs;
|
@@ -14,10 +14,48 @@ const BaseWidget_1 = __importDefault(require("./BaseWidget"));
|
|
14
14
|
const types_1 = require("../../types");
|
15
15
|
const constants_1 = require("../constants");
|
16
16
|
const makeFileInput_1 = __importDefault(require("./components/makeFileInput"));
|
17
|
+
class ImageWrapper {
|
18
|
+
constructor(imageBase64Url, preview, onUrlUpdate) {
|
19
|
+
this.imageBase64Url = imageBase64Url;
|
20
|
+
this.preview = preview;
|
21
|
+
this.onUrlUpdate = onUrlUpdate;
|
22
|
+
this.originalSrc = imageBase64Url;
|
23
|
+
preview.src = imageBase64Url;
|
24
|
+
}
|
25
|
+
updateImageData(base64DataUrl) {
|
26
|
+
this.preview.src = base64DataUrl;
|
27
|
+
this.imageBase64Url = base64DataUrl;
|
28
|
+
this.onUrlUpdate();
|
29
|
+
}
|
30
|
+
decreaseSize(resizeFactor = 3 / 4) {
|
31
|
+
const canvas = document.createElement('canvas');
|
32
|
+
canvas.width = this.preview.naturalWidth * resizeFactor;
|
33
|
+
canvas.height = this.preview.naturalHeight * resizeFactor;
|
34
|
+
const ctx = canvas.getContext('2d');
|
35
|
+
ctx?.drawImage(this.preview, 0, 0, canvas.width, canvas.height);
|
36
|
+
// JPEG can be much smaller than PNG for the same image size. Prefer it if
|
37
|
+
// the image is already a JPEG.
|
38
|
+
const format = this.originalSrc?.startsWith('data:image/jpeg;') ? 'image/jpeg' : 'image/png';
|
39
|
+
this.updateImageData(canvas.toDataURL(format));
|
40
|
+
}
|
41
|
+
reset() {
|
42
|
+
this.updateImageData(this.originalSrc);
|
43
|
+
}
|
44
|
+
isChanged() {
|
45
|
+
return this.imageBase64Url !== this.originalSrc;
|
46
|
+
}
|
47
|
+
getBase64Url() {
|
48
|
+
return this.imageBase64Url;
|
49
|
+
}
|
50
|
+
static fromSrcAndPreview(initialBase64Src, preview, onUrlUpdate) {
|
51
|
+
return new ImageWrapper(initialBase64Src, preview, onUrlUpdate);
|
52
|
+
}
|
53
|
+
}
|
17
54
|
class InsertImageWidget extends BaseWidget_1.default {
|
18
55
|
constructor(editor, localization) {
|
19
56
|
localization ??= editor.localization;
|
20
57
|
super(editor, 'insert-image-widget', localization);
|
58
|
+
this.image = null;
|
21
59
|
// Make the dropdown showable
|
22
60
|
this.container.classList.add('dropdownShowable');
|
23
61
|
editor.notifier.on(types_1.EditorEventType.SelectionUpdated, event => {
|
@@ -51,6 +89,7 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
51
89
|
this.statusView = document.createElement('div');
|
52
90
|
const actionButtonRow = document.createElement('div');
|
53
91
|
actionButtonRow.classList.add('action-button-row');
|
92
|
+
this.statusView.classList.add('insert-image-image-status-view');
|
54
93
|
this.submitButton = document.createElement('button');
|
55
94
|
this.selectedFiles = selectedFiles;
|
56
95
|
this.imageAltTextInput = document.createElement('input');
|
@@ -65,9 +104,8 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
65
104
|
this.submitButton.innerText = this.localizationTable.submit;
|
66
105
|
this.selectedFiles.onUpdateAndNow(async (files) => {
|
67
106
|
if (files.length === 0) {
|
68
|
-
this.
|
69
|
-
this.
|
70
|
-
this.submitButton.style.display = 'none';
|
107
|
+
this.image = null;
|
108
|
+
this.onImageDataUpdate();
|
71
109
|
return;
|
72
110
|
}
|
73
111
|
this.imagePreview.style.display = 'block';
|
@@ -79,18 +117,13 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
79
117
|
catch (e) {
|
80
118
|
this.statusView.innerText = this.localizationTable.imageLoadError(e);
|
81
119
|
}
|
82
|
-
this.imageBase64URL = data;
|
83
120
|
if (data) {
|
84
|
-
this.
|
85
|
-
this.submitButton.disabled = false;
|
86
|
-
this.submitButton.style.display = '';
|
87
|
-
this.updateImageSizeDisplay();
|
121
|
+
this.image = ImageWrapper.fromSrcAndPreview(data, this.imagePreview, () => this.onImageDataUpdate());
|
88
122
|
}
|
89
123
|
else {
|
90
|
-
this.
|
91
|
-
this.submitButton.style.display = 'none';
|
92
|
-
this.statusView.innerText = '';
|
124
|
+
this.image = null;
|
93
125
|
}
|
126
|
+
this.onImageDataUpdate();
|
94
127
|
});
|
95
128
|
altTextRow.replaceChildren(imageAltTextLabel, this.imageAltTextInput);
|
96
129
|
actionButtonRow.replaceChildren(this.submitButton);
|
@@ -98,11 +131,27 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
98
131
|
dropdown.replaceChildren(container);
|
99
132
|
return true;
|
100
133
|
}
|
134
|
+
onImageDataUpdate() {
|
135
|
+
const base64Data = this.image?.getBase64Url();
|
136
|
+
if (base64Data) {
|
137
|
+
this.submitButton.disabled = false;
|
138
|
+
this.submitButton.style.display = '';
|
139
|
+
this.imagePreview.style.display = '';
|
140
|
+
this.updateImageSizeDisplay();
|
141
|
+
}
|
142
|
+
else {
|
143
|
+
this.submitButton.disabled = true;
|
144
|
+
this.submitButton.style.display = 'none';
|
145
|
+
this.statusView.innerText = '';
|
146
|
+
this.imagePreview.style.display = 'none';
|
147
|
+
this.submitButton.disabled = true;
|
148
|
+
}
|
149
|
+
}
|
101
150
|
hideDialog() {
|
102
151
|
this.setDropdownVisible(false);
|
103
152
|
}
|
104
153
|
updateImageSizeDisplay() {
|
105
|
-
const imageData = this.
|
154
|
+
const imageData = this.image?.getBase64Url() ?? '';
|
106
155
|
const sizeInKiB = imageData.length / 1024;
|
107
156
|
const sizeInMiB = sizeInKiB / 1024;
|
108
157
|
let units = 'KiB';
|
@@ -111,7 +160,27 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
111
160
|
size = sizeInMiB;
|
112
161
|
units = 'MiB';
|
113
162
|
}
|
114
|
-
|
163
|
+
const sizeText = document.createElement('span');
|
164
|
+
sizeText.innerText = this.localizationTable.imageSize(Math.round(size), units);
|
165
|
+
// Add a button to allow decreasing the size of large images.
|
166
|
+
const decreaseSizeButton = document.createElement('button');
|
167
|
+
decreaseSizeButton.innerText = this.localizationTable.decreaseImageSize;
|
168
|
+
decreaseSizeButton.onclick = () => {
|
169
|
+
this.image?.decreaseSize();
|
170
|
+
};
|
171
|
+
const resetSizeButton = document.createElement('button');
|
172
|
+
resetSizeButton.innerText = this.localizationTable.resetImage;
|
173
|
+
resetSizeButton.onclick = () => {
|
174
|
+
this.image?.reset();
|
175
|
+
};
|
176
|
+
this.statusView.replaceChildren(sizeText);
|
177
|
+
const largeImageThreshold = 0.12; // MiB
|
178
|
+
if (sizeInMiB > largeImageThreshold) {
|
179
|
+
this.statusView.appendChild(decreaseSizeButton);
|
180
|
+
}
|
181
|
+
else if (this.image?.isChanged()) {
|
182
|
+
this.statusView.appendChild(resetSizeButton);
|
183
|
+
}
|
115
184
|
}
|
116
185
|
updateInputs() {
|
117
186
|
const resetInputs = () => {
|
@@ -131,11 +200,8 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
131
200
|
if (selectedObjects.length === 1 && selectedObjects[0] instanceof ImageComponent_1.default) {
|
132
201
|
editingImage = selectedObjects[0];
|
133
202
|
this.imageAltTextInput.value = editingImage.getAltText() ?? '';
|
134
|
-
this.
|
135
|
-
this.
|
136
|
-
this.imageBase64URL = editingImage.getURL();
|
137
|
-
this.imagePreview.src = this.imageBase64URL;
|
138
|
-
this.updateImageSizeDisplay();
|
203
|
+
this.image = ImageWrapper.fromSrcAndPreview(editingImage.getURL(), this.imagePreview, () => this.onImageDataUpdate());
|
204
|
+
this.onImageDataUpdate();
|
139
205
|
}
|
140
206
|
else if (selectedObjects.length > 0) {
|
141
207
|
// If not, clear the selection.
|
@@ -149,13 +215,21 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
149
215
|
}
|
150
216
|
};
|
151
217
|
this.submitButton.onclick = async () => {
|
152
|
-
if (!this.
|
218
|
+
if (!this.image) {
|
153
219
|
return;
|
154
220
|
}
|
155
221
|
const image = new Image();
|
156
|
-
image.src = this.
|
222
|
+
image.src = this.image.getBase64Url();
|
157
223
|
image.setAttribute('alt', this.imageAltTextInput.value);
|
158
|
-
|
224
|
+
let component;
|
225
|
+
try {
|
226
|
+
component = await ImageComponent_1.default.fromImage(image, math_1.Mat33.identity);
|
227
|
+
}
|
228
|
+
catch (error) {
|
229
|
+
console.error('Error loading image', error);
|
230
|
+
this.statusView.innerText = this.localizationTable.imageLoadError(error);
|
231
|
+
return;
|
232
|
+
}
|
159
233
|
if (component.getBBox().area === 0) {
|
160
234
|
this.statusView.innerText = this.localizationTable.errorImageHasZeroSize;
|
161
235
|
return;
|
@@ -163,9 +237,15 @@ class InsertImageWidget extends BaseWidget_1.default {
|
|
163
237
|
this.hideDialog();
|
164
238
|
if (editingImage) {
|
165
239
|
const eraseCommand = new Erase_1.default([editingImage]);
|
240
|
+
// Try to preserve the original width
|
241
|
+
const originalTransform = editingImage.getTransformation();
|
242
|
+
// || 1: Prevent division by zero
|
243
|
+
const originalWidth = editingImage.getBBox().width || 1;
|
244
|
+
const newWidth = component.getBBox().transformedBoundingBox(originalTransform).width || 1;
|
245
|
+
const widthAdjustTransform = math_1.Mat33.scaling2D(originalWidth / newWidth);
|
166
246
|
await this.editor.dispatch((0, uniteCommands_1.default)([
|
167
247
|
EditorImage_1.default.addElement(component),
|
168
|
-
component.transformBy(
|
248
|
+
component.transformBy(originalTransform.rightMul(widthAdjustTransform)),
|
169
249
|
component.setZIndex(editingImage.getZIndex()),
|
170
250
|
eraseCommand,
|
171
251
|
]));
|
@@ -24,8 +24,7 @@ export default class PenToolWidget extends BaseToolWidget {
|
|
24
24
|
private createIconForRecord;
|
25
25
|
protected createIcon(): Element;
|
26
26
|
private createPenTypeSelector;
|
27
|
-
|
28
|
-
protected createStabilizationOption(): {
|
27
|
+
protected createStrokeCorrectionOptions(): {
|
29
28
|
update: () => void;
|
30
29
|
addTo: (parent: HTMLElement) => void;
|
31
30
|
};
|