js-draw 0.14.0 → 0.15.1
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/.github/ISSUE_TEMPLATE/translation.yml +24 -0
- package/CHANGELOG.md +14 -1
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +4 -0
- package/dist/src/Color4.js +22 -0
- package/dist/src/Editor.d.ts +2 -1
- package/dist/src/Editor.js +10 -1
- package/dist/src/EditorImage.d.ts +1 -0
- package/dist/src/EditorImage.js +11 -0
- package/dist/src/commands/UnresolvedCommand.d.ts +14 -0
- package/dist/src/commands/UnresolvedCommand.js +22 -0
- package/dist/src/commands/uniteCommands.js +4 -2
- package/dist/src/components/AbstractComponent.d.ts +0 -1
- package/dist/src/components/AbstractComponent.js +36 -50
- package/dist/src/components/RestylableComponent.d.ts +24 -0
- package/dist/src/components/RestylableComponent.js +80 -0
- package/dist/src/components/Stroke.d.ts +8 -1
- package/dist/src/components/Stroke.js +49 -1
- package/dist/src/components/TextComponent.d.ts +10 -10
- package/dist/src/components/TextComponent.js +46 -13
- package/dist/src/components/lib.d.ts +2 -1
- package/dist/src/components/lib.js +2 -1
- package/dist/src/components/localization.d.ts +1 -0
- package/dist/src/components/localization.js +1 -0
- package/dist/src/rendering/TextRenderingStyle.d.ts +23 -0
- package/dist/src/rendering/TextRenderingStyle.js +20 -0
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +1 -1
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +1 -1
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +1 -1
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +1 -1
- package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +1 -1
- package/dist/src/toolbar/IconProvider.d.ts +2 -1
- package/dist/src/toolbar/IconProvider.js +10 -0
- package/dist/src/toolbar/localization.d.ts +1 -0
- package/dist/src/toolbar/localization.js +1 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +10 -4
- package/dist/src/toolbar/widgets/InsertImageWidget.js +2 -1
- package/dist/src/toolbar/widgets/SelectionToolWidget.js +77 -1
- package/dist/src/tools/Pen.js +2 -2
- package/dist/src/tools/SelectionTool/SelectAllShortcutHandler.d.ts +8 -0
- package/dist/src/tools/SelectionTool/SelectAllShortcutHandler.js +22 -0
- package/dist/src/tools/SelectionTool/Selection.js +1 -1
- package/dist/src/tools/SelectionTool/SelectionTool.js +7 -10
- package/dist/src/tools/TextTool.d.ts +1 -1
- package/dist/src/tools/ToolController.js +2 -0
- package/dist/src/tools/lib.d.ts +1 -0
- package/dist/src/tools/lib.js +1 -0
- package/dist/src/tools/localization.d.ts +1 -0
- package/dist/src/tools/localization.js +1 -0
- package/package.json +1 -1
- package/src/Color4.test.ts +4 -0
- package/src/Color4.ts +26 -0
- package/src/Editor.toSVG.test.ts +1 -1
- package/src/Editor.ts +12 -1
- package/src/EditorImage.ts +13 -0
- package/src/SVGLoader.ts +2 -1
- package/src/commands/UnresolvedCommand.ts +37 -0
- package/src/commands/uniteCommands.ts +5 -2
- package/src/components/AbstractComponent.transformBy.test.ts +22 -0
- package/src/components/AbstractComponent.ts +41 -59
- package/src/components/RestylableComponent.ts +142 -0
- package/src/components/Stroke.test.ts +68 -0
- package/src/components/Stroke.ts +68 -2
- package/src/components/TextComponent.test.ts +56 -2
- package/src/components/TextComponent.ts +63 -25
- package/src/components/lib.ts +4 -1
- package/src/components/localization.ts +3 -0
- package/src/math/Rect2.test.ts +18 -6
- package/src/rendering/TextRenderingStyle.ts +38 -0
- package/src/rendering/renderers/AbstractRenderer.ts +1 -1
- package/src/rendering/renderers/CanvasRenderer.ts +2 -1
- package/src/rendering/renderers/DummyRenderer.ts +1 -1
- package/src/rendering/renderers/SVGRenderer.ts +1 -1
- package/src/rendering/renderers/TextOnlyRenderer.ts +1 -1
- package/src/toolbar/IconProvider.ts +12 -1
- package/src/toolbar/localization.ts +2 -0
- package/src/toolbar/toolbar.css +7 -0
- package/src/toolbar/widgets/BaseWidget.ts +12 -4
- package/src/toolbar/widgets/InsertImageWidget.ts +2 -1
- package/src/toolbar/widgets/SelectionToolWidget.ts +95 -1
- package/src/tools/PanZoom.test.ts +2 -1
- package/src/tools/PasteHandler.ts +1 -1
- package/src/tools/Pen.ts +2 -2
- package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +28 -0
- package/src/tools/SelectionTool/Selection.ts +1 -1
- package/src/tools/SelectionTool/SelectionTool.ts +6 -9
- package/src/tools/TextTool.ts +2 -1
- package/src/tools/ToolController.ts +2 -0
- package/src/tools/lib.ts +1 -0
- package/src/tools/localization.ts +2 -0
@@ -7,4 +7,5 @@ export { default as AbstractComponent } from './AbstractComponent';
|
|
7
7
|
import Stroke from './Stroke';
|
8
8
|
import TextComponent from './TextComponent';
|
9
9
|
import ImageComponent from './ImageComponent';
|
10
|
-
|
10
|
+
import RestyleableComponent, { createRestyleComponentCommand } from './RestylableComponent';
|
11
|
+
export { Stroke, TextComponent as Text, RestyleableComponent, createRestyleComponentCommand, TextComponent, Stroke as StrokeComponent, ImageComponent, };
|
@@ -7,4 +7,5 @@ export { default as AbstractComponent } from './AbstractComponent';
|
|
7
7
|
import Stroke from './Stroke';
|
8
8
|
import TextComponent from './TextComponent';
|
9
9
|
import ImageComponent from './ImageComponent';
|
10
|
-
|
10
|
+
import { createRestyleComponentCommand } from './RestylableComponent';
|
11
|
+
export { Stroke, TextComponent as Text, createRestyleComponentCommand, TextComponent, Stroke as StrokeComponent, ImageComponent, };
|
@@ -2,6 +2,7 @@ export const defaultComponentLocalization = {
|
|
2
2
|
unlabeledImageNode: 'Unlabeled image node',
|
3
3
|
stroke: 'Stroke',
|
4
4
|
svgObject: 'SVG Object',
|
5
|
+
restyledElements: 'Restyled elements',
|
5
6
|
text: (text) => `Text object: ${text}`,
|
6
7
|
imageNode: (description) => `Image: ${description}`,
|
7
8
|
};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import RenderingStyle from './RenderingStyle';
|
2
|
+
export interface TextStyle {
|
3
|
+
size: number;
|
4
|
+
fontFamily: string;
|
5
|
+
fontWeight?: string;
|
6
|
+
fontVariant?: string;
|
7
|
+
renderingStyle: RenderingStyle;
|
8
|
+
}
|
9
|
+
export default TextStyle;
|
10
|
+
export declare const textStyleFromJSON: (json: any) => TextStyle;
|
11
|
+
export declare const textStyleToJSON: (style: TextStyle) => {
|
12
|
+
renderingStyle: {
|
13
|
+
fill: string;
|
14
|
+
stroke: {
|
15
|
+
color: string;
|
16
|
+
width: number;
|
17
|
+
} | undefined;
|
18
|
+
};
|
19
|
+
size: number;
|
20
|
+
fontFamily: string;
|
21
|
+
fontWeight?: string | undefined;
|
22
|
+
fontVariant?: string | undefined;
|
23
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { styleFromJSON, styleToJSON } from './RenderingStyle';
|
2
|
+
export const textStyleFromJSON = (json) => {
|
3
|
+
if (typeof json === 'string') {
|
4
|
+
json = JSON.parse(json);
|
5
|
+
}
|
6
|
+
if (typeof (json.fontFamily) !== 'string') {
|
7
|
+
throw new Error('Serialized textStyle missing string fontFamily attribute!');
|
8
|
+
}
|
9
|
+
const style = {
|
10
|
+
renderingStyle: styleFromJSON(json.renderingStyle),
|
11
|
+
size: json.size,
|
12
|
+
fontWeight: json.fontWeight,
|
13
|
+
fontVariant: json.fontVariant,
|
14
|
+
fontFamily: json.fontFamily,
|
15
|
+
};
|
16
|
+
return style;
|
17
|
+
};
|
18
|
+
export const textStyleToJSON = (style) => {
|
19
|
+
return Object.assign(Object.assign({}, style), { renderingStyle: styleToJSON(style.renderingStyle) });
|
20
|
+
};
|
@@ -1,11 +1,11 @@
|
|
1
1
|
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
2
|
-
import { TextStyle } from '../../components/TextComponent';
|
3
2
|
import Mat33 from '../../math/Mat33';
|
4
3
|
import Path, { PathCommand } from '../../math/Path';
|
5
4
|
import Rect2 from '../../math/Rect2';
|
6
5
|
import { Point2, Vec2 } from '../../math/Vec2';
|
7
6
|
import Viewport from '../../Viewport';
|
8
7
|
import RenderingStyle from '../RenderingStyle';
|
8
|
+
import TextStyle from '../TextRenderingStyle';
|
9
9
|
export interface RenderablePathSpec {
|
10
10
|
startPoint: Point2;
|
11
11
|
commands: PathCommand[];
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import { TextStyle } from '../../components/TextComponent';
|
2
1
|
import Mat33 from '../../math/Mat33';
|
3
2
|
import Rect2 from '../../math/Rect2';
|
4
3
|
import { Point2, Vec2 } from '../../math/Vec2';
|
5
4
|
import Vec3 from '../../math/Vec3';
|
6
5
|
import Viewport from '../../Viewport';
|
7
6
|
import RenderingStyle from '../RenderingStyle';
|
7
|
+
import TextStyle from '../TextRenderingStyle';
|
8
8
|
import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
|
9
9
|
export default class CanvasRenderer extends AbstractRenderer {
|
10
10
|
private ctx;
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import { TextStyle } from '../../components/TextComponent';
|
2
1
|
import Mat33 from '../../math/Mat33';
|
3
2
|
import Rect2 from '../../math/Rect2';
|
4
3
|
import { Point2, Vec2 } from '../../math/Vec2';
|
5
4
|
import Vec3 from '../../math/Vec3';
|
6
5
|
import Viewport from '../../Viewport';
|
7
6
|
import RenderingStyle from '../RenderingStyle';
|
7
|
+
import TextStyle from '../TextRenderingStyle';
|
8
8
|
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
9
9
|
export default class DummyRenderer extends AbstractRenderer {
|
10
10
|
clearedCount: number;
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
2
|
-
import { TextStyle } from '../../components/TextComponent';
|
3
2
|
import Mat33 from '../../math/Mat33';
|
4
3
|
import Rect2 from '../../math/Rect2';
|
5
4
|
import { Point2, Vec2 } from '../../math/Vec2';
|
6
5
|
import Viewport from '../../Viewport';
|
7
6
|
import RenderingStyle from '../RenderingStyle';
|
7
|
+
import TextStyle from '../TextRenderingStyle';
|
8
8
|
import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
|
9
9
|
export declare const renderedStylesheetId = "js-draw-style-sheet";
|
10
10
|
export default class SVGRenderer extends AbstractRenderer {
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import { TextStyle } from '../../components/TextComponent';
|
2
1
|
import Mat33 from '../../math/Mat33';
|
3
2
|
import Rect2 from '../../math/Rect2';
|
4
3
|
import Vec3 from '../../math/Vec3';
|
5
4
|
import Viewport from '../../Viewport';
|
6
5
|
import { TextRendererLocalization } from '../localization';
|
7
6
|
import RenderingStyle from '../RenderingStyle';
|
7
|
+
import TextStyle from '../TextRenderingStyle';
|
8
8
|
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
9
9
|
export default class TextOnlyRenderer extends AbstractRenderer {
|
10
10
|
private localizationTable;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
2
|
import { ComponentBuilderFactory } from '../components/builders/types';
|
3
|
-
import
|
3
|
+
import TextStyle from '../rendering/TextRenderingStyle';
|
4
4
|
import Pen from '../tools/Pen';
|
5
5
|
export type IconType = HTMLImageElement | SVGElement;
|
6
6
|
/**
|
@@ -51,6 +51,7 @@ export default class IconProvider {
|
|
51
51
|
makePenIcon(strokeSize: number, color: string | Color4, rounded?: boolean): IconType;
|
52
52
|
makeIconFromFactory(pen: Pen, factory: ComponentBuilderFactory): IconType;
|
53
53
|
makePipetteIcon(color?: Color4): IconType;
|
54
|
+
makeFormatSelectionIcon(): IconType;
|
54
55
|
makeResizeViewportIcon(): IconType;
|
55
56
|
makeDuplicateSelectionIcon(): IconType;
|
56
57
|
makePasteIcon(): IconType;
|
@@ -537,6 +537,16 @@ export default class IconProvider {
|
|
537
537
|
icon.setAttribute('viewBox', '0 0 100 100');
|
538
538
|
return icon;
|
539
539
|
}
|
540
|
+
makeFormatSelectionIcon() {
|
541
|
+
return this.makeIconFromPath(`
|
542
|
+
M 5 10
|
543
|
+
L 5 20 L 10 20 L 10 15 L 20 15 L 20 40 L 15 40 L 15 45 L 35 45 L 35 40 L 30 40 L 30 15 L 40 15 L 40 20 L 45 20 L 45 15 L 45 10 L 5 10 z
|
544
|
+
M 90 10 C 90 10 86.5 13.8 86 14 C 86 14 76.2 24.8 76 25 L 60 25 L 60 65 C 75 70 85 70 90 65 L 90 25 L 80 25 L 76.7 25 L 90 10 z
|
545
|
+
M 60 25 L 55 25 L 50 30 L 60 25 z
|
546
|
+
M 10 55 L 10 90 L 41 90 L 41 86 L 45 86 L 45 55 L 10 55 z
|
547
|
+
M 42 87 L 42 93 L 48 93 L 48 87 L 42 87 z
|
548
|
+
`);
|
549
|
+
}
|
540
550
|
makeResizeViewportIcon() {
|
541
551
|
return this.makeIconFromPath(`
|
542
552
|
M 75 5 75 10 90 10 90 25 95 25 95 5 75 5 z
|
@@ -88,24 +88,30 @@ export default class BaseWidget {
|
|
88
88
|
}
|
89
89
|
// If we didn't do anything with the event, send it to the editor.
|
90
90
|
if (!handled) {
|
91
|
-
this.editor.toolController.dispatchInputEvent({
|
91
|
+
handled = this.editor.toolController.dispatchInputEvent({
|
92
92
|
kind: InputEvtType.KeyPressEvent,
|
93
93
|
key: evt.key,
|
94
|
-
ctrlKey: evt.ctrlKey,
|
94
|
+
ctrlKey: evt.ctrlKey || evt.metaKey,
|
95
95
|
altKey: evt.altKey,
|
96
96
|
});
|
97
97
|
}
|
98
|
+
if (handled) {
|
99
|
+
evt.preventDefault();
|
100
|
+
}
|
98
101
|
};
|
99
102
|
button.onkeyup = evt => {
|
100
103
|
if (evt.key in clickTriggers) {
|
101
104
|
return;
|
102
105
|
}
|
103
|
-
this.editor.toolController.dispatchInputEvent({
|
106
|
+
const handled = this.editor.toolController.dispatchInputEvent({
|
104
107
|
kind: InputEvtType.KeyUpEvent,
|
105
108
|
key: evt.key,
|
106
|
-
ctrlKey: evt.ctrlKey,
|
109
|
+
ctrlKey: evt.ctrlKey || evt.metaKey,
|
107
110
|
altKey: evt.altKey,
|
108
111
|
});
|
112
|
+
if (handled) {
|
113
|
+
evt.preventDefault();
|
114
|
+
}
|
109
115
|
};
|
110
116
|
button.onclick = () => {
|
111
117
|
if (!this.disabled) {
|
@@ -10,7 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
10
10
|
import ImageComponent from '../../components/ImageComponent';
|
11
11
|
import Erase from '../../commands/Erase';
|
12
12
|
import EditorImage from '../../EditorImage';
|
13
|
-
import
|
13
|
+
import uniteCommands from '../../commands/uniteCommands';
|
14
|
+
import SelectionTool from '../../tools/SelectionTool/SelectionTool';
|
14
15
|
import Mat33 from '../../math/Mat33';
|
15
16
|
import fileToBase64 from '../../util/fileToBase64';
|
16
17
|
import ActionButtonWidget from './ActionButtonWidget';
|
@@ -7,9 +7,82 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8
8
|
});
|
9
9
|
};
|
10
|
+
import Color4 from '../../Color4';
|
11
|
+
import { isRestylableComponent } from '../../components/RestylableComponent';
|
12
|
+
import uniteCommands from '../../commands/uniteCommands';
|
10
13
|
import { EditorEventType } from '../../types';
|
14
|
+
import makeColorInput from '../makeColorInput';
|
11
15
|
import ActionButtonWidget from './ActionButtonWidget';
|
12
16
|
import BaseToolWidget from './BaseToolWidget';
|
17
|
+
import BaseWidget from './BaseWidget';
|
18
|
+
class RestyleSelectionWidget extends BaseWidget {
|
19
|
+
constructor(editor, selectionTool, localizationTable) {
|
20
|
+
super(editor, 'restyle-selection', localizationTable);
|
21
|
+
this.selectionTool = selectionTool;
|
22
|
+
this.updateFormatData = () => { };
|
23
|
+
// Allow showing the dropdown even if this widget isn't selected yet
|
24
|
+
this.container.classList.add('dropdownShowable');
|
25
|
+
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
26
|
+
if (toolEvt.kind !== EditorEventType.ToolUpdated) {
|
27
|
+
throw new Error('Invalid event type!');
|
28
|
+
}
|
29
|
+
if (toolEvt.tool === this.selectionTool) {
|
30
|
+
this.updateFormatData();
|
31
|
+
}
|
32
|
+
});
|
33
|
+
}
|
34
|
+
getTitle() {
|
35
|
+
return this.localizationTable.reformatSelection;
|
36
|
+
}
|
37
|
+
createIcon() {
|
38
|
+
return this.editor.icons.makeFormatSelectionIcon();
|
39
|
+
}
|
40
|
+
handleClick() {
|
41
|
+
this.setDropdownVisible(!this.isDropdownVisible());
|
42
|
+
}
|
43
|
+
fillDropdown(dropdown) {
|
44
|
+
const container = document.createElement('div');
|
45
|
+
const colorRow = document.createElement('div');
|
46
|
+
const colorLabel = document.createElement('label');
|
47
|
+
const [colorInput, colorInputContainer, setColorInputValue] = makeColorInput(this.editor, color => {
|
48
|
+
const selection = this.selectionTool.getSelection();
|
49
|
+
if (selection) {
|
50
|
+
const updateStyleCommands = [];
|
51
|
+
for (const elem of selection.getSelectedObjects()) {
|
52
|
+
if (isRestylableComponent(elem)) {
|
53
|
+
updateStyleCommands.push(elem.updateStyle({ color }));
|
54
|
+
}
|
55
|
+
}
|
56
|
+
const unitedCommand = uniteCommands(updateStyleCommands);
|
57
|
+
this.editor.dispatch(unitedCommand);
|
58
|
+
}
|
59
|
+
});
|
60
|
+
colorLabel.innerText = this.localizationTable.colorLabel;
|
61
|
+
this.updateFormatData = () => {
|
62
|
+
const selection = this.selectionTool.getSelection();
|
63
|
+
if (selection) {
|
64
|
+
colorInput.disabled = false;
|
65
|
+
const colors = [];
|
66
|
+
for (const elem of selection.getSelectedObjects()) {
|
67
|
+
if (isRestylableComponent(elem)) {
|
68
|
+
const color = elem.getStyle().color;
|
69
|
+
if (color) {
|
70
|
+
colors.push(color);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
setColorInputValue(Color4.average(colors));
|
75
|
+
}
|
76
|
+
else {
|
77
|
+
colorInput.disabled = true;
|
78
|
+
}
|
79
|
+
};
|
80
|
+
colorRow.replaceChildren(colorLabel, colorInputContainer);
|
81
|
+
container.replaceChildren(colorRow);
|
82
|
+
dropdown.replaceChildren(container);
|
83
|
+
return true;
|
84
|
+
}
|
85
|
+
}
|
13
86
|
export default class SelectionToolWidget extends BaseToolWidget {
|
14
87
|
constructor(editor, tool, localization) {
|
15
88
|
super(editor, tool, 'selection-tool-widget', localization);
|
@@ -26,13 +99,16 @@ export default class SelectionToolWidget extends BaseToolWidget {
|
|
26
99
|
const selection = this.tool.getSelection();
|
27
100
|
this.editor.dispatch(yield selection.duplicateSelectedObjects());
|
28
101
|
}), localization);
|
102
|
+
const restyleButton = new RestyleSelectionWidget(editor, this.tool, localization);
|
29
103
|
this.addSubWidget(resizeButton);
|
30
104
|
this.addSubWidget(deleteButton);
|
31
105
|
this.addSubWidget(duplicateButton);
|
106
|
+
this.addSubWidget(restyleButton);
|
32
107
|
const updateDisabled = (disabled) => {
|
33
108
|
resizeButton.setDisabled(disabled);
|
34
109
|
deleteButton.setDisabled(disabled);
|
35
110
|
duplicateButton.setDisabled(disabled);
|
111
|
+
restyleButton.setDisabled(disabled);
|
36
112
|
};
|
37
113
|
updateDisabled(true);
|
38
114
|
// Enable/disable actions based on whether items are selected
|
@@ -42,7 +118,7 @@ export default class SelectionToolWidget extends BaseToolWidget {
|
|
42
118
|
}
|
43
119
|
if (toolEvt.tool === this.tool) {
|
44
120
|
const selection = this.tool.getSelection();
|
45
|
-
const hasSelection = selection && selection.
|
121
|
+
const hasSelection = selection && selection.getSelectedItemCount() > 0;
|
46
122
|
updateDisabled(!hasSelection);
|
47
123
|
}
|
48
124
|
});
|
package/dist/src/tools/Pen.js
CHANGED
@@ -152,7 +152,7 @@ export default class Pen extends BaseTool {
|
|
152
152
|
this.setThickness(newThickness);
|
153
153
|
return true;
|
154
154
|
}
|
155
|
-
if (key === 'control') {
|
155
|
+
if (key === 'control' || key === 'meta') {
|
156
156
|
this.ctrlKeyPressed = true;
|
157
157
|
return true;
|
158
158
|
}
|
@@ -164,7 +164,7 @@ export default class Pen extends BaseTool {
|
|
164
164
|
}
|
165
165
|
onKeyUp({ key }) {
|
166
166
|
key = key.toLowerCase();
|
167
|
-
if (key === 'control') {
|
167
|
+
if (key === 'control' || key === 'meta') {
|
168
168
|
this.ctrlKeyPressed = false;
|
169
169
|
return true;
|
170
170
|
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import Editor from '../../Editor';
|
2
|
+
import { KeyPressEvent } from '../../types';
|
3
|
+
import BaseTool from '../BaseTool';
|
4
|
+
export default class SelectAllShortcutHandler extends BaseTool {
|
5
|
+
private editor;
|
6
|
+
constructor(editor: Editor);
|
7
|
+
onKeyPress({ key, ctrlKey }: KeyPressEvent): boolean;
|
8
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import BaseTool from '../BaseTool';
|
2
|
+
import SelectionTool from './SelectionTool';
|
3
|
+
// Handles ctrl+a: Select all
|
4
|
+
export default class SelectAllShortcutHandler extends BaseTool {
|
5
|
+
constructor(editor) {
|
6
|
+
super(editor.notifier, editor.localization.selectAllTool);
|
7
|
+
this.editor = editor;
|
8
|
+
}
|
9
|
+
// @internal
|
10
|
+
onKeyPress({ key, ctrlKey }) {
|
11
|
+
if (ctrlKey && key === 'a') {
|
12
|
+
const selectionTools = this.editor.toolController.getMatchingTools(SelectionTool);
|
13
|
+
if (selectionTools.length > 0) {
|
14
|
+
const selectionTool = selectionTools[0];
|
15
|
+
selectionTool.setEnabled(true);
|
16
|
+
selectionTool.setSelection(this.editor.image.getAllElements());
|
17
|
+
return true;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
return false;
|
21
|
+
}
|
22
|
+
}
|
@@ -440,7 +440,7 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
|
|
440
440
|
this.resolveToElems(editor);
|
441
441
|
(_b = this.selection) === null || _b === void 0 ? void 0 : _b.setTransform(this.fullTransform.inverse(), false);
|
442
442
|
(_c = this.selection) === null || _c === void 0 ? void 0 : _c.updateUI();
|
443
|
-
yield editor.asyncUnapplyCommands(this.transformCommands, updateChunkSize);
|
443
|
+
yield editor.asyncUnapplyCommands(this.transformCommands, updateChunkSize, true);
|
444
444
|
(_d = this.selection) === null || _d === void 0 ? void 0 : _d.setTransform(Mat33.identity);
|
445
445
|
(_e = this.selection) === null || _e === void 0 ? void 0 : _e.recomputeRegion();
|
446
446
|
(_f = this.selection) === null || _f === void 0 ? void 0 : _f.updateUI();
|
@@ -158,7 +158,7 @@ export default class SelectionTool extends BaseTool {
|
|
158
158
|
}
|
159
159
|
}
|
160
160
|
onGestureCancel() {
|
161
|
-
var _a, _b, _c;
|
161
|
+
var _a, _b, _c, _d;
|
162
162
|
if (this.selectionBoxHandlingEvt) {
|
163
163
|
(_a = this.selectionBox) === null || _a === void 0 ? void 0 : _a.onDragCancel();
|
164
164
|
}
|
@@ -167,11 +167,13 @@ export default class SelectionTool extends BaseTool {
|
|
167
167
|
(_b = this.selectionBox) === null || _b === void 0 ? void 0 : _b.cancelSelection();
|
168
168
|
this.selectionBox = this.prevSelectionBox;
|
169
169
|
(_c = this.selectionBox) === null || _c === void 0 ? void 0 : _c.addTo(this.handleOverlay);
|
170
|
+
(_d = this.selectionBox) === null || _d === void 0 ? void 0 : _d.recomputeRegion();
|
171
|
+
this.prevSelectionBox = null;
|
170
172
|
}
|
171
173
|
this.expandingSelectionBox = false;
|
172
174
|
}
|
173
175
|
onKeyPress(event) {
|
174
|
-
if (event.key === 'Control') {
|
176
|
+
if (event.key === 'Control' || event.key === 'Meta') {
|
175
177
|
this.ctrlKeyPressed = true;
|
176
178
|
return true;
|
177
179
|
}
|
@@ -181,8 +183,7 @@ export default class SelectionTool extends BaseTool {
|
|
181
183
|
return true;
|
182
184
|
}
|
183
185
|
else if (event.key === 'a' && event.ctrlKey) {
|
184
|
-
|
185
|
-
// Return early to prevent 'a' from moving the selection/view.
|
186
|
+
this.setSelection(this.editor.image.getAllElements());
|
186
187
|
return true;
|
187
188
|
}
|
188
189
|
else if (event.ctrlKey) {
|
@@ -268,7 +269,7 @@ export default class SelectionTool extends BaseTool {
|
|
268
269
|
return handled;
|
269
270
|
}
|
270
271
|
onKeyUp(evt) {
|
271
|
-
if (evt.key === 'Control') {
|
272
|
+
if (evt.key === 'Control' || evt.key === 'Meta') {
|
272
273
|
this.ctrlKeyPressed = false;
|
273
274
|
return true;
|
274
275
|
}
|
@@ -283,10 +284,6 @@ export default class SelectionTool extends BaseTool {
|
|
283
284
|
});
|
284
285
|
return true;
|
285
286
|
}
|
286
|
-
else if (evt.key === 'a') {
|
287
|
-
this.setSelection(this.editor.image.getAllElements());
|
288
|
-
return true;
|
289
|
-
}
|
290
287
|
}
|
291
288
|
if (this.selectionBox && SelectionTool.handleableKeys.some(key => key === evt.key)) {
|
292
289
|
this.selectionBox.finalizeTransform();
|
@@ -394,5 +391,5 @@ SelectionTool.handleableKeys = [
|
|
394
391
|
'e', 'j', 'ArrowDown',
|
395
392
|
'r', 'R',
|
396
393
|
'i', 'I', 'o', 'O',
|
397
|
-
'Control',
|
394
|
+
'Control', 'Meta',
|
398
395
|
];
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
|
-
import { TextStyle } from '../components/TextComponent';
|
3
2
|
import Editor from '../Editor';
|
4
3
|
import { PointerEvt } from '../types';
|
5
4
|
import BaseTool from './BaseTool';
|
6
5
|
import { ToolLocalization } from './localization';
|
6
|
+
import TextStyle from '../rendering/TextRenderingStyle';
|
7
7
|
export default class TextTool extends BaseTool {
|
8
8
|
private editor;
|
9
9
|
private localizationTable;
|
@@ -13,6 +13,7 @@ import PasteHandler from './PasteHandler';
|
|
13
13
|
import ToolbarShortcutHandler from './ToolbarShortcutHandler';
|
14
14
|
import { makePressureSensitiveFreehandLineBuilder } from '../components/builders/PressureSensitiveFreehandLineBuilder';
|
15
15
|
import FindTool from './FindTool';
|
16
|
+
import SelectAllShortcutHandler from './SelectionTool/SelectAllShortcutHandler';
|
16
17
|
export default class ToolController {
|
17
18
|
/** @internal */
|
18
19
|
constructor(editor, localization) {
|
@@ -43,6 +44,7 @@ export default class ToolController {
|
|
43
44
|
new ToolSwitcherShortcut(editor),
|
44
45
|
new FindTool(editor),
|
45
46
|
new PasteHandler(editor),
|
47
|
+
new SelectAllShortcutHandler(editor),
|
46
48
|
];
|
47
49
|
primaryTools.forEach(tool => tool.setToolGroup(primaryToolGroup));
|
48
50
|
panZoomTool.setEnabled(true);
|
package/dist/src/tools/lib.d.ts
CHANGED
@@ -10,6 +10,7 @@ export { default as PanZoomTool, PanZoomMode } from './PanZoom';
|
|
10
10
|
export { default as PenTool, PenStyle } from './Pen';
|
11
11
|
export { default as TextTool } from './TextTool';
|
12
12
|
export { default as SelectionTool } from './SelectionTool/SelectionTool';
|
13
|
+
export { default as SelectAllShortcutHandler } from './SelectionTool/SelectAllShortcutHandler';
|
13
14
|
export { default as EraserTool } from './Eraser';
|
14
15
|
export { default as PasteHandler } from './PasteHandler';
|
15
16
|
export { default as ToolbarShortcutHandler } from './ToolbarShortcutHandler';
|
package/dist/src/tools/lib.js
CHANGED
@@ -10,6 +10,7 @@ export { default as PanZoomTool, PanZoomMode } from './PanZoom';
|
|
10
10
|
export { default as PenTool } from './Pen';
|
11
11
|
export { default as TextTool } from './TextTool';
|
12
12
|
export { default as SelectionTool } from './SelectionTool/SelectionTool';
|
13
|
+
export { default as SelectAllShortcutHandler } from './SelectionTool/SelectAllShortcutHandler';
|
13
14
|
export { default as EraserTool } from './Eraser';
|
14
15
|
export { default as PasteHandler } from './PasteHandler';
|
15
16
|
export { default as ToolbarShortcutHandler } from './ToolbarShortcutHandler';
|
package/package.json
CHANGED
package/src/Color4.test.ts
CHANGED
package/src/Color4.ts
CHANGED
@@ -151,6 +151,32 @@ export default class Color4 {
|
|
151
151
|
);
|
152
152
|
}
|
153
153
|
|
154
|
+
/**
|
155
|
+
* @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
|
156
|
+
*/
|
157
|
+
public static average(colors: Color4[]) {
|
158
|
+
let averageA = 0;
|
159
|
+
let averageR = 0;
|
160
|
+
let averageG = 0;
|
161
|
+
let averageB = 0;
|
162
|
+
|
163
|
+
for (const color of colors) {
|
164
|
+
averageA += color.a;
|
165
|
+
averageR += color.r;
|
166
|
+
averageG += color.g;
|
167
|
+
averageB += color.b;
|
168
|
+
}
|
169
|
+
|
170
|
+
if (colors.length > 0) {
|
171
|
+
averageA /= colors.length;
|
172
|
+
averageR /= colors.length;
|
173
|
+
averageG /= colors.length;
|
174
|
+
averageB /= colors.length;
|
175
|
+
}
|
176
|
+
|
177
|
+
return new Color4(averageR, averageG, averageB, averageA);
|
178
|
+
}
|
179
|
+
|
154
180
|
private hexString: string|null = null;
|
155
181
|
|
156
182
|
/**
|
package/src/Editor.toSVG.test.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { TextStyle } from './components/TextComponent';
|
2
1
|
import { Color4, Mat33, Rect2, TextComponent, EditorImage, Vec2 } from './lib';
|
2
|
+
import TextStyle from './rendering/TextRenderingStyle';
|
3
3
|
import SVGLoader from './SVGLoader';
|
4
4
|
import createEditor from './testing/createEditor';
|
5
5
|
|
package/src/Editor.ts
CHANGED
@@ -704,8 +704,14 @@ export class Editor {
|
|
704
704
|
return this.asyncApplyOrUnapplyCommands(commands, true, chunkSize);
|
705
705
|
}
|
706
706
|
|
707
|
+
// If `unapplyInReverseOrder`, commands are reversed before unapplying.
|
707
708
|
// @see {@link #asyncApplyOrUnapplyCommands }
|
708
|
-
public asyncUnapplyCommands(commands: Command[], chunkSize: number) {
|
709
|
+
public asyncUnapplyCommands(commands: Command[], chunkSize: number, unapplyInReverseOrder: boolean = false) {
|
710
|
+
if (unapplyInReverseOrder) {
|
711
|
+
commands = [ ...commands ]; // copy
|
712
|
+
commands.reverse();
|
713
|
+
}
|
714
|
+
|
709
715
|
return this.asyncApplyOrUnapplyCommands(commands, false, chunkSize);
|
710
716
|
}
|
711
717
|
|
@@ -745,6 +751,11 @@ export class Editor {
|
|
745
751
|
});
|
746
752
|
}
|
747
753
|
|
754
|
+
// @internal
|
755
|
+
public isRerenderQueued() {
|
756
|
+
return this.rerenderQueued;
|
757
|
+
}
|
758
|
+
|
748
759
|
public rerender(showImageBounds: boolean = true) {
|
749
760
|
this.display.startRerender();
|
750
761
|
|