js-draw 0.16.0 → 0.17.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/CHANGELOG.md +15 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.js +2 -1
- package/dist/src/EditorImage.js +12 -5
- package/dist/src/SVGLoader.js +6 -0
- package/dist/src/UndoRedoHistory.d.ts +2 -3
- package/dist/src/UndoRedoHistory.js +37 -20
- package/dist/src/components/RestylableComponent.js +2 -2
- package/dist/src/components/localization.d.ts +1 -1
- package/dist/src/components/localization.js +1 -1
- package/dist/src/lib.d.ts +1 -0
- package/dist/src/lib.js +1 -0
- package/dist/src/toolbar/HTMLToolbar.d.ts +1 -0
- package/dist/src/toolbar/HTMLToolbar.js +4 -1
- package/dist/src/toolbar/widgets/OverflowWidget.js +6 -0
- package/dist/src/tools/SelectionTool/SelectionTool.js +1 -1
- package/dist/src/types.d.ts +7 -0
- package/dist/src/types.js +7 -0
- package/package.json +1 -1
- package/src/Editor.css +4 -0
- package/src/Editor.ts +3 -1
- package/src/EditorImage.ts +11 -6
- package/src/SVGLoader.test.ts +57 -0
- package/src/SVGLoader.ts +7 -0
- package/src/UndoRedoHistory.ts +28 -23
- package/src/components/RestylableComponent.ts +2 -2
- package/src/components/localization.ts +2 -2
- package/src/lib.ts +1 -0
- package/src/toolbar/HTMLToolbar.ts +6 -1
- package/src/toolbar/toolbar.css +7 -1
- package/src/toolbar/widgets/OverflowWidget.css +20 -2
- package/src/toolbar/widgets/OverflowWidget.ts +9 -0
- package/src/tools/SelectionTool/SelectionTool.ts +1 -1
- package/src/types.ts +11 -1
package/dist/src/Editor.js
CHANGED
@@ -503,11 +503,12 @@ export class Editor {
|
|
503
503
|
* ```
|
504
504
|
*/
|
505
505
|
dispatchNoAnnounce(command, addToHistory = false) {
|
506
|
+
const result = command.apply(this);
|
506
507
|
if (addToHistory) {
|
507
508
|
const apply = false; // Don't double-apply
|
508
509
|
this.history.push(command, apply);
|
509
510
|
}
|
510
|
-
return
|
511
|
+
return result;
|
511
512
|
}
|
512
513
|
/**
|
513
514
|
* Apply a large transformation in chunks.
|
package/dist/src/EditorImage.js
CHANGED
@@ -172,9 +172,13 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
|
|
172
172
|
super('add-element');
|
173
173
|
this.element = element;
|
174
174
|
this.applyByFlattening = applyByFlattening;
|
175
|
-
|
176
|
-
//
|
177
|
-
this
|
175
|
+
this.serializedElem = null;
|
176
|
+
// FIXME: The serialized version of this command may be inaccurate if this is
|
177
|
+
// serialized when this command is not on the top of the undo stack.
|
178
|
+
//
|
179
|
+
// Caching the element's serialized data leads to additional memory usage *and*
|
180
|
+
// sometimes incorrect behavior in collaborative editing.
|
181
|
+
this.serializedElem = null;
|
178
182
|
if (isNaN(element.getBBox().area)) {
|
179
183
|
throw new Error('Elements in the image cannot have NaN bounding boxes');
|
180
184
|
}
|
@@ -197,8 +201,9 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
|
|
197
201
|
return localization.addElementAction(this.element.description(localization));
|
198
202
|
}
|
199
203
|
serializeToJSON() {
|
204
|
+
var _a;
|
200
205
|
return {
|
201
|
-
elemData: this.serializedElem,
|
206
|
+
elemData: (_a = this.serializedElem) !== null && _a !== void 0 ? _a : this.element.serialize(),
|
202
207
|
};
|
203
208
|
}
|
204
209
|
},
|
@@ -207,7 +212,9 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
|
|
207
212
|
const id = json.elemData.id;
|
208
213
|
const foundElem = editor.image.lookupElement(id);
|
209
214
|
const elem = foundElem !== null && foundElem !== void 0 ? foundElem : AbstractComponent.deserialize(json.elemData);
|
210
|
-
|
215
|
+
const result = new EditorImage.AddElementCommand(elem);
|
216
|
+
result.serializedElem = json.elemData;
|
217
|
+
return result;
|
211
218
|
});
|
212
219
|
})(),
|
213
220
|
_a);
|
package/dist/src/SVGLoader.js
CHANGED
@@ -215,6 +215,12 @@ export default class SVGLoader {
|
|
215
215
|
// In some environments, computedStyles.fontSize can be increased by the system.
|
216
216
|
// Thus, to prevent text from growing on load/save, prefer .style.fontSize.
|
217
217
|
let fontSizeMatch = fontSizeExp.exec(elem.style.fontSize);
|
218
|
+
if (!fontSizeMatch && elem.tagName.toLowerCase() === 'tspan' && elem.parentElement) {
|
219
|
+
// Try to inherit the font size of the parent text element.
|
220
|
+
fontSizeMatch = fontSizeExp.exec(elem.parentElement.style.fontSize);
|
221
|
+
}
|
222
|
+
// If we still couldn't find a font size, try to use computedStyles (which can be
|
223
|
+
// wrong).
|
218
224
|
if (!fontSizeMatch) {
|
219
225
|
fontSizeMatch = fontSizeExp.exec(computedStyles.fontSize);
|
220
226
|
}
|
@@ -3,12 +3,11 @@ import Command from './commands/Command';
|
|
3
3
|
type AnnounceRedoCallback = (command: Command) => void;
|
4
4
|
type AnnounceUndoCallback = (command: Command) => void;
|
5
5
|
declare class UndoRedoHistory {
|
6
|
+
#private;
|
6
7
|
private readonly editor;
|
7
8
|
private announceRedoCallback;
|
8
9
|
private announceUndoCallback;
|
9
|
-
private
|
10
|
-
private redoStack;
|
11
|
-
private maxUndoRedoStackSize;
|
10
|
+
private readonly maxUndoRedoStackSize;
|
12
11
|
constructor(editor: Editor, announceRedoCallback: AnnounceRedoCallback, announceUndoCallback: AnnounceUndoCallback);
|
13
12
|
private fireUpdateEvent;
|
14
13
|
push(command: Command, apply?: boolean): void;
|
@@ -1,19 +1,35 @@
|
|
1
|
-
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
6
|
+
};
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
|
+
};
|
12
|
+
var _UndoRedoHistory_undoStack, _UndoRedoHistory_redoStack;
|
13
|
+
import { EditorEventType, UndoEventType } from './types';
|
2
14
|
class UndoRedoHistory {
|
3
15
|
// @internal
|
4
16
|
constructor(editor, announceRedoCallback, announceUndoCallback) {
|
5
17
|
this.editor = editor;
|
6
18
|
this.announceRedoCallback = announceRedoCallback;
|
7
19
|
this.announceUndoCallback = announceUndoCallback;
|
20
|
+
_UndoRedoHistory_undoStack.set(this, void 0);
|
21
|
+
_UndoRedoHistory_redoStack.set(this, void 0);
|
8
22
|
this.maxUndoRedoStackSize = 700;
|
9
|
-
this
|
10
|
-
this
|
23
|
+
__classPrivateFieldSet(this, _UndoRedoHistory_undoStack, [], "f");
|
24
|
+
__classPrivateFieldSet(this, _UndoRedoHistory_redoStack, [], "f");
|
11
25
|
}
|
12
|
-
fireUpdateEvent() {
|
26
|
+
fireUpdateEvent(stackUpdateType, triggeringCommand) {
|
13
27
|
this.editor.notifier.dispatch(EditorEventType.UndoRedoStackUpdated, {
|
14
28
|
kind: EditorEventType.UndoRedoStackUpdated,
|
15
|
-
undoStackSize: this.
|
16
|
-
redoStackSize: this.
|
29
|
+
undoStackSize: __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length,
|
30
|
+
redoStackSize: __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").length,
|
31
|
+
command: triggeringCommand,
|
32
|
+
stackUpdateType,
|
17
33
|
});
|
18
34
|
}
|
19
35
|
// Adds the given command to this and applies it to the editor.
|
@@ -21,17 +37,17 @@ class UndoRedoHistory {
|
|
21
37
|
if (apply) {
|
22
38
|
command.apply(this.editor);
|
23
39
|
}
|
24
|
-
this.
|
25
|
-
for (const elem of this
|
40
|
+
__classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").push(command);
|
41
|
+
for (const elem of __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f")) {
|
26
42
|
elem.onDrop(this.editor);
|
27
43
|
}
|
28
|
-
this
|
29
|
-
if (this.
|
44
|
+
__classPrivateFieldSet(this, _UndoRedoHistory_redoStack, [], "f");
|
45
|
+
if (__classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length > this.maxUndoRedoStackSize) {
|
30
46
|
const removeAtOnceCount = 10;
|
31
|
-
const removedElements = this.
|
47
|
+
const removedElements = __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").splice(0, removeAtOnceCount);
|
32
48
|
removedElements.forEach(elem => elem.onDrop(this.editor));
|
33
49
|
}
|
34
|
-
this.fireUpdateEvent();
|
50
|
+
this.fireUpdateEvent(UndoEventType.CommandDone, command);
|
35
51
|
this.editor.notifier.dispatch(EditorEventType.CommandDone, {
|
36
52
|
kind: EditorEventType.CommandDone,
|
37
53
|
command,
|
@@ -39,12 +55,12 @@ class UndoRedoHistory {
|
|
39
55
|
}
|
40
56
|
// Remove the last command from this' undo stack and apply it.
|
41
57
|
undo() {
|
42
|
-
const command = this.
|
58
|
+
const command = __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").pop();
|
43
59
|
if (command) {
|
44
|
-
this.
|
60
|
+
__classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").push(command);
|
45
61
|
command.unapply(this.editor);
|
46
62
|
this.announceUndoCallback(command);
|
47
|
-
this.fireUpdateEvent();
|
63
|
+
this.fireUpdateEvent(UndoEventType.CommandUndone, command);
|
48
64
|
this.editor.notifier.dispatch(EditorEventType.CommandUndone, {
|
49
65
|
kind: EditorEventType.CommandUndone,
|
50
66
|
command,
|
@@ -52,12 +68,12 @@ class UndoRedoHistory {
|
|
52
68
|
}
|
53
69
|
}
|
54
70
|
redo() {
|
55
|
-
const command = this.
|
71
|
+
const command = __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").pop();
|
56
72
|
if (command) {
|
57
|
-
this.
|
73
|
+
__classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").push(command);
|
58
74
|
command.apply(this.editor);
|
59
75
|
this.announceRedoCallback(command);
|
60
|
-
this.fireUpdateEvent();
|
76
|
+
this.fireUpdateEvent(UndoEventType.CommandRedone, command);
|
61
77
|
this.editor.notifier.dispatch(EditorEventType.CommandDone, {
|
62
78
|
kind: EditorEventType.CommandDone,
|
63
79
|
command,
|
@@ -65,10 +81,11 @@ class UndoRedoHistory {
|
|
65
81
|
}
|
66
82
|
}
|
67
83
|
get undoStackSize() {
|
68
|
-
return this.
|
84
|
+
return __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length;
|
69
85
|
}
|
70
86
|
get redoStackSize() {
|
71
|
-
return this.
|
87
|
+
return __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").length;
|
72
88
|
}
|
73
89
|
}
|
90
|
+
_UndoRedoHistory_undoStack = new WeakMap(), _UndoRedoHistory_redoStack = new WeakMap();
|
74
91
|
export default UndoRedoHistory;
|
@@ -56,8 +56,8 @@ class DefaultRestyleComponentCommand extends UnresolvedSerializableCommand {
|
|
56
56
|
unapply(editor) {
|
57
57
|
this.getComponent(editor).forceStyle(this.originalStyle, editor);
|
58
58
|
}
|
59
|
-
description(
|
60
|
-
return localizationTable.
|
59
|
+
description(editor, localizationTable) {
|
60
|
+
return localizationTable.restyledElement(this.getComponent(editor).description(localizationTable));
|
61
61
|
}
|
62
62
|
serializeToJSON() {
|
63
63
|
return {
|
@@ -6,6 +6,6 @@ export interface ImageComponentLocalization {
|
|
6
6
|
svgObject: string;
|
7
7
|
emptyBackground: string;
|
8
8
|
filledBackgroundWithColor: (color: string) => string;
|
9
|
-
|
9
|
+
restyledElement: (elementDescription: string) => string;
|
10
10
|
}
|
11
11
|
export declare const defaultComponentLocalization: ImageComponentLocalization;
|
@@ -2,9 +2,9 @@ export const defaultComponentLocalization = {
|
|
2
2
|
unlabeledImageNode: 'Unlabeled image node',
|
3
3
|
stroke: 'Stroke',
|
4
4
|
svgObject: 'SVG Object',
|
5
|
-
restyledElements: 'Restyled elements',
|
6
5
|
emptyBackground: 'Empty background',
|
7
6
|
filledBackgroundWithColor: (color) => `Filled background (${color})`,
|
8
7
|
text: (text) => `Text object: ${text}`,
|
9
8
|
imageNode: (description) => `Image: ${description}`,
|
9
|
+
restyledElement: (elementDescription) => `Restyled ${elementDescription}`,
|
10
10
|
};
|
package/dist/src/lib.d.ts
CHANGED
@@ -28,5 +28,6 @@ export * from './toolbar/lib';
|
|
28
28
|
export * from './rendering/lib';
|
29
29
|
export { default as Pointer, PointerDevice } from './Pointer';
|
30
30
|
export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
|
31
|
+
export { default as UndoRedoHistory } from './UndoRedoHistory';
|
31
32
|
export { Editor, EditorSettings };
|
32
33
|
export default Editor;
|
package/dist/src/lib.js
CHANGED
@@ -28,5 +28,6 @@ export * from './toolbar/lib';
|
|
28
28
|
export * from './rendering/lib';
|
29
29
|
export { default as Pointer, PointerDevice } from './Pointer';
|
30
30
|
export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
|
31
|
+
export { default as UndoRedoHistory } from './UndoRedoHistory';
|
31
32
|
export { Editor };
|
32
33
|
export default Editor;
|
@@ -23,6 +23,8 @@ export default class HTMLToolbar {
|
|
23
23
|
this.editor = editor;
|
24
24
|
this.localizationTable = localizationTable;
|
25
25
|
this.listeners = [];
|
26
|
+
// Flex-order of the next widget to be added.
|
27
|
+
this.widgetOrderCounter = 0;
|
26
28
|
this.widgetsById = {};
|
27
29
|
this.widgetList = [];
|
28
30
|
// Widget to toggle overflow menu.
|
@@ -197,7 +199,7 @@ export default class HTMLToolbar {
|
|
197
199
|
this.setupColorPickers();
|
198
200
|
// Ensure that the widget gets displayed in the correct
|
199
201
|
// place in the toolbar, even if it's removed and re-added.
|
200
|
-
container.style.order = `${this.
|
202
|
+
container.style.order = `${this.widgetOrderCounter++}`;
|
201
203
|
this.queueReLayout();
|
202
204
|
}
|
203
205
|
/**
|
@@ -233,6 +235,7 @@ export default class HTMLToolbar {
|
|
233
235
|
if (options.maxSize) {
|
234
236
|
spacer.style.maxWidth = options.maxSize;
|
235
237
|
}
|
238
|
+
spacer.style.order = `${this.widgetOrderCounter++}`;
|
236
239
|
this.container.appendChild(spacer);
|
237
240
|
}
|
238
241
|
serializeState() {
|
@@ -4,6 +4,7 @@ export default class OverflowWidget extends BaseWidget {
|
|
4
4
|
var _a;
|
5
5
|
super(editor, 'overflow-widget', localizationTable);
|
6
6
|
this.overflowChildren = [];
|
7
|
+
this.container.classList.add('toolbar-overflow-widget');
|
7
8
|
// Make the dropdown openable
|
8
9
|
this.container.classList.add('dropdownShowable');
|
9
10
|
(_a = this.overflowContainer) !== null && _a !== void 0 ? _a : (this.overflowContainer = document.createElement('div'));
|
@@ -32,6 +33,7 @@ export default class OverflowWidget extends BaseWidget {
|
|
32
33
|
*/
|
33
34
|
clearChildren() {
|
34
35
|
this.overflowContainer.replaceChildren();
|
36
|
+
this.container.classList.remove('horizontal');
|
35
37
|
const overflowChildren = this.overflowChildren;
|
36
38
|
this.overflowChildren = [];
|
37
39
|
return overflowChildren;
|
@@ -56,6 +58,10 @@ export default class OverflowWidget extends BaseWidget {
|
|
56
58
|
this.overflowChildren.push(widget);
|
57
59
|
widget.addTo(this.overflowContainer);
|
58
60
|
widget.setIsToplevel(false);
|
61
|
+
// Switch to a horizontal layout if enough children
|
62
|
+
if (this.overflowChildren.length > 2) {
|
63
|
+
this.container.classList.add('horizontal');
|
64
|
+
}
|
59
65
|
}
|
60
66
|
// This always returns false.
|
61
67
|
// Don't try to move the overflow menu to itself.
|
package/dist/src/types.d.ts
CHANGED
@@ -85,6 +85,11 @@ export declare enum EditorEventType {
|
|
85
85
|
ColorPickerColorSelected = 10,
|
86
86
|
ToolbarDropdownShown = 11
|
87
87
|
}
|
88
|
+
export declare enum UndoEventType {
|
89
|
+
CommandDone = 0,
|
90
|
+
CommandUndone = 1,
|
91
|
+
CommandRedone = 2
|
92
|
+
}
|
88
93
|
type EditorToolEventType = EditorEventType.ToolEnabled | EditorEventType.ToolDisabled | EditorEventType.ToolUpdated;
|
89
94
|
export interface EditorToolEvent {
|
90
95
|
readonly kind: EditorToolEventType;
|
@@ -107,6 +112,8 @@ export interface EditorUndoStackUpdated {
|
|
107
112
|
readonly kind: EditorEventType.UndoRedoStackUpdated;
|
108
113
|
readonly undoStackSize: number;
|
109
114
|
readonly redoStackSize: number;
|
115
|
+
readonly command?: Command;
|
116
|
+
readonly stackUpdateType: UndoEventType;
|
110
117
|
}
|
111
118
|
export interface CommandDoneEvent {
|
112
119
|
readonly kind: EditorEventType.CommandDone;
|
package/dist/src/types.js
CHANGED
@@ -26,3 +26,10 @@ export var EditorEventType;
|
|
26
26
|
EditorEventType[EditorEventType["ColorPickerColorSelected"] = 10] = "ColorPickerColorSelected";
|
27
27
|
EditorEventType[EditorEventType["ToolbarDropdownShown"] = 11] = "ToolbarDropdownShown";
|
28
28
|
})(EditorEventType || (EditorEventType = {}));
|
29
|
+
// Types of `EditorUndoStackUpdated` events.
|
30
|
+
export var UndoEventType;
|
31
|
+
(function (UndoEventType) {
|
32
|
+
UndoEventType[UndoEventType["CommandDone"] = 0] = "CommandDone";
|
33
|
+
UndoEventType[UndoEventType["CommandUndone"] = 1] = "CommandUndone";
|
34
|
+
UndoEventType[UndoEventType["CommandRedone"] = 2] = "CommandRedone";
|
35
|
+
})(UndoEventType || (UndoEventType = {}));
|
package/package.json
CHANGED
package/src/Editor.css
CHANGED
package/src/Editor.ts
CHANGED
@@ -663,12 +663,14 @@ export class Editor {
|
|
663
663
|
* ```
|
664
664
|
*/
|
665
665
|
public dispatchNoAnnounce(command: Command, addToHistory: boolean = false) {
|
666
|
+
const result = command.apply(this);
|
667
|
+
|
666
668
|
if (addToHistory) {
|
667
669
|
const apply = false; // Don't double-apply
|
668
670
|
this.history.push(command, apply);
|
669
671
|
}
|
670
672
|
|
671
|
-
return
|
673
|
+
return result;
|
672
674
|
}
|
673
675
|
|
674
676
|
/**
|
package/src/EditorImage.ts
CHANGED
@@ -211,7 +211,7 @@ export default class EditorImage {
|
|
211
211
|
|
212
212
|
// A Command that can access private [EditorImage] functionality
|
213
213
|
private static AddElementCommand = class extends SerializableCommand {
|
214
|
-
private serializedElem: any;
|
214
|
+
private serializedElem: any|null = null;
|
215
215
|
|
216
216
|
// If [applyByFlattening], then the rendered content of this element
|
217
217
|
// is present on the display's wet ink canvas. As such, no re-render is necessary
|
@@ -222,9 +222,12 @@ export default class EditorImage {
|
|
222
222
|
) {
|
223
223
|
super('add-element');
|
224
224
|
|
225
|
-
//
|
226
|
-
//
|
227
|
-
|
225
|
+
// FIXME: The serialized version of this command may be inaccurate if this is
|
226
|
+
// serialized when this command is not on the top of the undo stack.
|
227
|
+
//
|
228
|
+
// Caching the element's serialized data leads to additional memory usage *and*
|
229
|
+
// sometimes incorrect behavior in collaborative editing.
|
230
|
+
this.serializedElem = null;
|
228
231
|
|
229
232
|
if (isNaN(element.getBBox().area)) {
|
230
233
|
throw new Error('Elements in the image cannot have NaN bounding boxes');
|
@@ -253,7 +256,7 @@ export default class EditorImage {
|
|
253
256
|
|
254
257
|
protected serializeToJSON() {
|
255
258
|
return {
|
256
|
-
elemData: this.serializedElem,
|
259
|
+
elemData: this.serializedElem ?? this.element.serialize(),
|
257
260
|
};
|
258
261
|
}
|
259
262
|
|
@@ -262,7 +265,9 @@ export default class EditorImage {
|
|
262
265
|
const id = json.elemData.id;
|
263
266
|
const foundElem = editor.image.lookupElement(id);
|
264
267
|
const elem = foundElem ?? AbstractComponent.deserialize(json.elemData);
|
265
|
-
|
268
|
+
const result = new EditorImage.AddElementCommand(elem);
|
269
|
+
result.serializedElem = json.elemData;
|
270
|
+
return result;
|
266
271
|
});
|
267
272
|
}
|
268
273
|
};
|
package/src/SVGLoader.test.ts
CHANGED
@@ -54,4 +54,61 @@ describe('SVGLoader', () => {
|
|
54
54
|
expect(elem.getBBox().topLeft.x).toBe(0);
|
55
55
|
expect(elem.getBBox().h).toBeGreaterThan(200);
|
56
56
|
});
|
57
|
+
|
58
|
+
it('tspans without specified font-sizes should inherit their font size from their parent element', async () => {
|
59
|
+
const editor = createEditor();
|
60
|
+
await editor.loadFrom(SVGLoader.fromString(`
|
61
|
+
<svg>
|
62
|
+
<text style='font-size: 22px;'>
|
63
|
+
Testing...
|
64
|
+
<tspan>Test 2...</tspan>
|
65
|
+
<tspan>Test 3...</tspan>
|
66
|
+
<tspan style='font-size: 3px;'>Test 4...</tspan>
|
67
|
+
</text>
|
68
|
+
</svg>
|
69
|
+
`, true));
|
70
|
+
const elem = editor.image
|
71
|
+
.getAllElements()
|
72
|
+
.filter(elem => elem instanceof TextComponent)[0] as TextComponent;
|
73
|
+
expect(elem).not.toBeNull();
|
74
|
+
|
75
|
+
// Ensure each child object has the correct size
|
76
|
+
expect(elem.serialize().data).toMatchObject({
|
77
|
+
'textObjects': [
|
78
|
+
{ },
|
79
|
+
{
|
80
|
+
'json':
|
81
|
+
{
|
82
|
+
'textObjects': [{ 'text': 'Test 2...' }],
|
83
|
+
'style': {
|
84
|
+
'size': 22,
|
85
|
+
}
|
86
|
+
}
|
87
|
+
},
|
88
|
+
{ },
|
89
|
+
{
|
90
|
+
'json': {
|
91
|
+
'textObjects': [{ 'text': 'Test 3...' }],
|
92
|
+
'style': {
|
93
|
+
'size': 22
|
94
|
+
}
|
95
|
+
}
|
96
|
+
},
|
97
|
+
{ },
|
98
|
+
{
|
99
|
+
'json': {
|
100
|
+
'textObjects': [{ 'text': 'Test 4...' }],
|
101
|
+
'style': {
|
102
|
+
'size': 3,
|
103
|
+
}
|
104
|
+
}
|
105
|
+
},
|
106
|
+
{ }
|
107
|
+
],
|
108
|
+
|
109
|
+
'style': {
|
110
|
+
'size': 22,
|
111
|
+
}
|
112
|
+
});
|
113
|
+
});
|
57
114
|
});
|
package/src/SVGLoader.ts
CHANGED
@@ -257,6 +257,13 @@ export default class SVGLoader implements ImageLoader {
|
|
257
257
|
// In some environments, computedStyles.fontSize can be increased by the system.
|
258
258
|
// Thus, to prevent text from growing on load/save, prefer .style.fontSize.
|
259
259
|
let fontSizeMatch = fontSizeExp.exec(elem.style.fontSize);
|
260
|
+
if (!fontSizeMatch && elem.tagName.toLowerCase() === 'tspan' && elem.parentElement) {
|
261
|
+
// Try to inherit the font size of the parent text element.
|
262
|
+
fontSizeMatch = fontSizeExp.exec(elem.parentElement.style.fontSize);
|
263
|
+
}
|
264
|
+
|
265
|
+
// If we still couldn't find a font size, try to use computedStyles (which can be
|
266
|
+
// wrong).
|
260
267
|
if (!fontSizeMatch) {
|
261
268
|
fontSizeMatch = fontSizeExp.exec(computedStyles.fontSize);
|
262
269
|
}
|
package/src/UndoRedoHistory.ts
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
import Editor from './Editor';
|
2
2
|
import Command from './commands/Command';
|
3
|
-
import { EditorEventType } from './types';
|
3
|
+
import { EditorEventType, UndoEventType } from './types';
|
4
4
|
|
5
5
|
type AnnounceRedoCallback = (command: Command)=>void;
|
6
6
|
type AnnounceUndoCallback = (command: Command)=>void;
|
7
7
|
|
8
8
|
class UndoRedoHistory {
|
9
|
-
|
10
|
-
|
9
|
+
#undoStack: Command[];
|
10
|
+
#redoStack: Command[];
|
11
11
|
|
12
|
-
private maxUndoRedoStackSize: number = 700;
|
12
|
+
private readonly maxUndoRedoStackSize: number = 700;
|
13
13
|
|
14
14
|
// @internal
|
15
15
|
public constructor(
|
@@ -17,15 +17,20 @@ class UndoRedoHistory {
|
|
17
17
|
private announceRedoCallback: AnnounceRedoCallback,
|
18
18
|
private announceUndoCallback: AnnounceUndoCallback,
|
19
19
|
) {
|
20
|
-
this
|
21
|
-
this
|
20
|
+
this.#undoStack = [];
|
21
|
+
this.#redoStack = [];
|
22
22
|
}
|
23
23
|
|
24
|
-
private fireUpdateEvent(
|
24
|
+
private fireUpdateEvent(
|
25
|
+
stackUpdateType: UndoEventType, triggeringCommand: Command
|
26
|
+
) {
|
25
27
|
this.editor.notifier.dispatch(EditorEventType.UndoRedoStackUpdated, {
|
26
28
|
kind: EditorEventType.UndoRedoStackUpdated,
|
27
|
-
undoStackSize: this
|
28
|
-
redoStackSize: this
|
29
|
+
undoStackSize: this.#undoStack.length,
|
30
|
+
redoStackSize: this.#redoStack.length,
|
31
|
+
|
32
|
+
command: triggeringCommand,
|
33
|
+
stackUpdateType,
|
29
34
|
});
|
30
35
|
}
|
31
36
|
|
@@ -34,20 +39,20 @@ class UndoRedoHistory {
|
|
34
39
|
if (apply) {
|
35
40
|
command.apply(this.editor);
|
36
41
|
}
|
37
|
-
this
|
42
|
+
this.#undoStack.push(command);
|
38
43
|
|
39
|
-
for (const elem of this
|
44
|
+
for (const elem of this.#redoStack) {
|
40
45
|
elem.onDrop(this.editor);
|
41
46
|
}
|
42
|
-
this
|
47
|
+
this.#redoStack = [];
|
43
48
|
|
44
|
-
if (this
|
49
|
+
if (this.#undoStack.length > this.maxUndoRedoStackSize) {
|
45
50
|
const removeAtOnceCount = 10;
|
46
|
-
const removedElements = this
|
51
|
+
const removedElements = this.#undoStack.splice(0, removeAtOnceCount);
|
47
52
|
removedElements.forEach(elem => elem.onDrop(this.editor));
|
48
53
|
}
|
49
54
|
|
50
|
-
this.fireUpdateEvent();
|
55
|
+
this.fireUpdateEvent(UndoEventType.CommandDone, command);
|
51
56
|
this.editor.notifier.dispatch(EditorEventType.CommandDone, {
|
52
57
|
kind: EditorEventType.CommandDone,
|
53
58
|
command,
|
@@ -56,13 +61,13 @@ class UndoRedoHistory {
|
|
56
61
|
|
57
62
|
// Remove the last command from this' undo stack and apply it.
|
58
63
|
public undo() {
|
59
|
-
const command = this
|
64
|
+
const command = this.#undoStack.pop();
|
60
65
|
if (command) {
|
61
|
-
this
|
66
|
+
this.#redoStack.push(command);
|
62
67
|
command.unapply(this.editor);
|
63
68
|
this.announceUndoCallback(command);
|
64
69
|
|
65
|
-
this.fireUpdateEvent();
|
70
|
+
this.fireUpdateEvent(UndoEventType.CommandUndone, command);
|
66
71
|
this.editor.notifier.dispatch(EditorEventType.CommandUndone, {
|
67
72
|
kind: EditorEventType.CommandUndone,
|
68
73
|
command,
|
@@ -71,13 +76,13 @@ class UndoRedoHistory {
|
|
71
76
|
}
|
72
77
|
|
73
78
|
public redo() {
|
74
|
-
const command = this
|
79
|
+
const command = this.#redoStack.pop();
|
75
80
|
if (command) {
|
76
|
-
this
|
81
|
+
this.#undoStack.push(command);
|
77
82
|
command.apply(this.editor);
|
78
83
|
this.announceRedoCallback(command);
|
79
84
|
|
80
|
-
this.fireUpdateEvent();
|
85
|
+
this.fireUpdateEvent(UndoEventType.CommandRedone, command);
|
81
86
|
this.editor.notifier.dispatch(EditorEventType.CommandDone, {
|
82
87
|
kind: EditorEventType.CommandDone,
|
83
88
|
command,
|
@@ -86,11 +91,11 @@ class UndoRedoHistory {
|
|
86
91
|
}
|
87
92
|
|
88
93
|
public get undoStackSize(): number {
|
89
|
-
return this
|
94
|
+
return this.#undoStack.length;
|
90
95
|
}
|
91
96
|
|
92
97
|
public get redoStackSize(): number {
|
93
|
-
return this
|
98
|
+
return this.#redoStack.length;
|
94
99
|
}
|
95
100
|
}
|
96
101
|
|
@@ -111,8 +111,8 @@ class DefaultRestyleComponentCommand extends UnresolvedSerializableCommand {
|
|
111
111
|
this.getComponent(editor).forceStyle(this.originalStyle, editor);
|
112
112
|
}
|
113
113
|
|
114
|
-
public description(
|
115
|
-
return localizationTable.
|
114
|
+
public description(editor: Editor, localizationTable: EditorLocalization): string {
|
115
|
+
return localizationTable.restyledElement(this.getComponent(editor).description(localizationTable));
|
116
116
|
}
|
117
117
|
|
118
118
|
protected serializeToJSON() {
|