js-draw 0.17.0 → 0.17.2
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 +14 -1
- package/dist/bundle.js +1 -1
- package/dist/src/EditorImage.d.ts +6 -6
- package/dist/src/EditorImage.js +83 -42
- package/dist/src/UndoRedoHistory.js +1 -1
- package/dist/src/components/TextComponent.d.ts +13 -1
- package/dist/src/components/TextComponent.js +13 -5
- package/dist/src/rendering/RenderingStyle.d.ts +7 -0
- package/dist/src/rendering/RenderingStyle.js +6 -0
- package/dist/src/rendering/TextRenderingStyle.d.ts +13 -0
- package/dist/src/rendering/TextRenderingStyle.js +4 -1
- package/dist/src/toolbar/HTMLToolbar.js +14 -3
- package/dist/src/tools/FindTool.js +2 -1
- package/dist/src/util/assertions.d.ts +22 -0
- package/dist/src/util/assertions.js +42 -3
- package/package.json +16 -16
- package/src/EditorImage.test.ts +33 -0
- package/src/EditorImage.ts +87 -35
- package/src/UndoRedoHistory.test.ts +1 -1
- package/src/UndoRedoHistory.ts +1 -1
- package/src/components/TextComponent.test.ts +26 -1
- package/src/components/TextComponent.ts +18 -8
- package/src/rendering/RenderingStyle.ts +9 -0
- package/src/rendering/TextRenderingStyle.ts +8 -1
- package/src/toolbar/HTMLToolbar.ts +17 -4
- package/src/tools/FindTool.ts +2 -1
- package/src/util/assertions.ts +51 -3
@@ -5,7 +5,6 @@ import Rect2 from './math/Rect2';
|
|
5
5
|
import RenderingCache from './rendering/caching/RenderingCache';
|
6
6
|
import SerializableCommand from './commands/SerializableCommand';
|
7
7
|
import EventDispatcher from './EventDispatcher';
|
8
|
-
import Command from './commands/Command';
|
9
8
|
export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
|
10
9
|
export declare enum EditorImageEventType {
|
11
10
|
ExportViewportChanged = 0
|
@@ -21,11 +20,6 @@ export default class EditorImage {
|
|
21
20
|
private importExportViewport;
|
22
21
|
readonly notifier: EditorImageNotifier;
|
23
22
|
constructor();
|
24
|
-
/**
|
25
|
-
* @returns a `Viewport` for rendering the image when importing/exporting.
|
26
|
-
*/
|
27
|
-
getImportExportViewport(): Viewport;
|
28
|
-
setImportExportRect(imageRect: Rect2): Command;
|
29
23
|
getBackgroundComponents(): AbstractComponent[];
|
30
24
|
findParent(elem: AbstractComponent): ImageNode | null;
|
31
25
|
queueRerenderOf(elem: AbstractComponent): void;
|
@@ -63,6 +57,12 @@ export default class EditorImage {
|
|
63
57
|
/** @see EditorImage.addElement */
|
64
58
|
addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
65
59
|
private static AddElementCommand;
|
60
|
+
/**
|
61
|
+
* @returns a `Viewport` for rendering the image when importing/exporting.
|
62
|
+
*/
|
63
|
+
getImportExportViewport(): Viewport;
|
64
|
+
setImportExportRect(imageRect: Rect2): SerializableCommand;
|
65
|
+
private static SetImportExportRectCommand;
|
66
66
|
}
|
67
67
|
type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
68
68
|
/** Part of the Editor's image. @internal */
|
package/dist/src/EditorImage.js
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
var _a;
|
1
|
+
var _a, _b, _c;
|
2
2
|
import Viewport from './Viewport';
|
3
3
|
import AbstractComponent from './components/AbstractComponent';
|
4
4
|
import Rect2 from './math/Rect2';
|
5
5
|
import SerializableCommand from './commands/SerializableCommand';
|
6
6
|
import EventDispatcher from './EventDispatcher';
|
7
7
|
import { Vec2 } from './math/Vec2';
|
8
|
-
import Command from './commands/Command';
|
9
8
|
import Mat33 from './math/Mat33';
|
9
|
+
import { assertIsNumber, assertIsNumberArray } from './util/assertions';
|
10
10
|
// @internal Sort by z-index, low to high
|
11
11
|
export const sortLeavesByZIndex = (leaves) => {
|
12
12
|
leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
|
@@ -31,34 +31,6 @@ export default class EditorImage {
|
|
31
31
|
// Default to a 500x500 image
|
32
32
|
this.importExportViewport.updateScreenSize(Vec2.of(500, 500));
|
33
33
|
}
|
34
|
-
/**
|
35
|
-
* @returns a `Viewport` for rendering the image when importing/exporting.
|
36
|
-
*/
|
37
|
-
getImportExportViewport() {
|
38
|
-
return this.importExportViewport;
|
39
|
-
}
|
40
|
-
setImportExportRect(imageRect) {
|
41
|
-
const importExportViewport = this.getImportExportViewport();
|
42
|
-
const origSize = importExportViewport.visibleRect.size;
|
43
|
-
const origTransform = importExportViewport.canvasToScreenTransform;
|
44
|
-
return new class extends Command {
|
45
|
-
apply(editor) {
|
46
|
-
const viewport = editor.image.getImportExportViewport();
|
47
|
-
viewport.updateScreenSize(imageRect.size);
|
48
|
-
viewport.resetTransform(Mat33.translation(imageRect.topLeft.times(-1)));
|
49
|
-
editor.queueRerender();
|
50
|
-
}
|
51
|
-
unapply(editor) {
|
52
|
-
const viewport = editor.image.getImportExportViewport();
|
53
|
-
viewport.updateScreenSize(origSize);
|
54
|
-
viewport.resetTransform(origTransform);
|
55
|
-
editor.queueRerender();
|
56
|
-
}
|
57
|
-
description(_editor, localizationTable) {
|
58
|
-
return localizationTable.resizeOutputCommand(imageRect);
|
59
|
-
}
|
60
|
-
};
|
61
|
-
}
|
62
34
|
// Returns all components that make up the background of this image. These
|
63
35
|
// components are rendered below all other components.
|
64
36
|
getBackgroundComponents() {
|
@@ -75,8 +47,8 @@ export default class EditorImage {
|
|
75
47
|
}
|
76
48
|
// Returns the parent of the given element, if it exists.
|
77
49
|
findParent(elem) {
|
78
|
-
var
|
79
|
-
return (
|
50
|
+
var _b;
|
51
|
+
return (_b = this.background.getChildWithContent(elem)) !== null && _b !== void 0 ? _b : this.root.getChildWithContent(elem);
|
80
52
|
}
|
81
53
|
// Forces a re-render of `elem` when the image is next re-rendered as a whole.
|
82
54
|
// Does nothing if `elem` is not in this.
|
@@ -128,8 +100,8 @@ export default class EditorImage {
|
|
128
100
|
* @see {@link AbstractComponent.getId}
|
129
101
|
*/
|
130
102
|
lookupElement(id) {
|
131
|
-
var
|
132
|
-
return (
|
103
|
+
var _b;
|
104
|
+
return (_b = this.componentsById[id]) !== null && _b !== void 0 ? _b : null;
|
133
105
|
}
|
134
106
|
addElementDirectly(elem) {
|
135
107
|
elem.onAddToImage(this);
|
@@ -162,9 +134,22 @@ export default class EditorImage {
|
|
162
134
|
addElement(elem, applyByFlattening) {
|
163
135
|
return EditorImage.addElement(elem, applyByFlattening);
|
164
136
|
}
|
137
|
+
/**
|
138
|
+
* @returns a `Viewport` for rendering the image when importing/exporting.
|
139
|
+
*/
|
140
|
+
getImportExportViewport() {
|
141
|
+
return this.importExportViewport;
|
142
|
+
}
|
143
|
+
setImportExportRect(imageRect) {
|
144
|
+
const importExportViewport = this.getImportExportViewport();
|
145
|
+
const origSize = importExportViewport.visibleRect.size;
|
146
|
+
const origTransform = importExportViewport.canvasToScreenTransform;
|
147
|
+
return new EditorImage.SetImportExportRectCommand(origSize, origTransform, imageRect);
|
148
|
+
}
|
165
149
|
}
|
150
|
+
_a = EditorImage;
|
166
151
|
// A Command that can access private [EditorImage] functionality
|
167
|
-
EditorImage.AddElementCommand = (
|
152
|
+
EditorImage.AddElementCommand = (_b = class extends SerializableCommand {
|
168
153
|
// If [applyByFlattening], then the rendered content of this element
|
169
154
|
// is present on the display's wet ink canvas. As such, no re-render is necessary
|
170
155
|
// the first time this command is applied (the surfaces are joined instead).
|
@@ -201,9 +186,9 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
|
|
201
186
|
return localization.addElementAction(this.element.description(localization));
|
202
187
|
}
|
203
188
|
serializeToJSON() {
|
204
|
-
var
|
189
|
+
var _b;
|
205
190
|
return {
|
206
|
-
elemData: (
|
191
|
+
elemData: (_b = this.serializedElem) !== null && _b !== void 0 ? _b : this.element.serialize(),
|
207
192
|
};
|
208
193
|
}
|
209
194
|
},
|
@@ -217,7 +202,63 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
|
|
217
202
|
return result;
|
218
203
|
});
|
219
204
|
})(),
|
220
|
-
|
205
|
+
_b);
|
206
|
+
// Handles resizing the background import/export region of the image.
|
207
|
+
EditorImage.SetImportExportRectCommand = (_c = class extends SerializableCommand {
|
208
|
+
constructor(originalSize, originalTransform, finalRect) {
|
209
|
+
super(EditorImage.SetImportExportRectCommand.commandId);
|
210
|
+
this.originalSize = originalSize;
|
211
|
+
this.originalTransform = originalTransform;
|
212
|
+
this.finalRect = finalRect;
|
213
|
+
}
|
214
|
+
apply(editor) {
|
215
|
+
const viewport = editor.image.getImportExportViewport();
|
216
|
+
viewport.updateScreenSize(this.finalRect.size);
|
217
|
+
viewport.resetTransform(Mat33.translation(this.finalRect.topLeft.times(-1)));
|
218
|
+
editor.queueRerender();
|
219
|
+
}
|
220
|
+
unapply(editor) {
|
221
|
+
const viewport = editor.image.getImportExportViewport();
|
222
|
+
viewport.updateScreenSize(this.originalSize);
|
223
|
+
viewport.resetTransform(this.originalTransform);
|
224
|
+
editor.queueRerender();
|
225
|
+
}
|
226
|
+
description(_editor, localization) {
|
227
|
+
return localization.resizeOutputCommand(this.finalRect);
|
228
|
+
}
|
229
|
+
serializeToJSON() {
|
230
|
+
return {
|
231
|
+
originalSize: this.originalSize.xy,
|
232
|
+
originalTransform: this.originalTransform.toArray(),
|
233
|
+
newRegion: {
|
234
|
+
x: this.finalRect.x,
|
235
|
+
y: this.finalRect.y,
|
236
|
+
w: this.finalRect.w,
|
237
|
+
h: this.finalRect.h,
|
238
|
+
},
|
239
|
+
};
|
240
|
+
}
|
241
|
+
},
|
242
|
+
_c.commandId = 'set-import-export-rect',
|
243
|
+
(() => {
|
244
|
+
const commandId = _c.commandId;
|
245
|
+
SerializableCommand.register(commandId, (json, _editor) => {
|
246
|
+
assertIsNumber(json.originalSize.x);
|
247
|
+
assertIsNumber(json.originalSize.y);
|
248
|
+
assertIsNumberArray(json.originalTransform);
|
249
|
+
assertIsNumberArray([
|
250
|
+
json.newRegion.x,
|
251
|
+
json.newRegion.y,
|
252
|
+
json.newRegion.w,
|
253
|
+
json.newRegion.h,
|
254
|
+
]);
|
255
|
+
const originalSize = Vec2.ofXY(json.originalSize);
|
256
|
+
const originalTransform = new Mat33(...json.originalTransform);
|
257
|
+
const finalRect = new Rect2(json.newRegion.x, json.newRegion.y, json.newRegion.w, json.newRegion.h);
|
258
|
+
return new EditorImage.SetImportExportRectCommand(originalSize, originalTransform, finalRect);
|
259
|
+
});
|
260
|
+
})(),
|
261
|
+
_c);
|
221
262
|
/** Part of the Editor's image. @internal */
|
222
263
|
export class ImageNode {
|
223
264
|
constructor(parent = null) {
|
@@ -351,7 +392,7 @@ export class ImageNode {
|
|
351
392
|
// this' ancestors bounding boxes. This also re-computes this' bounding box
|
352
393
|
// in the z-direction (z-indicies).
|
353
394
|
recomputeBBox(bubbleUp) {
|
354
|
-
var
|
395
|
+
var _b;
|
355
396
|
const oldBBox = this.bbox;
|
356
397
|
if (this.content !== null) {
|
357
398
|
this.bbox = this.content.getBBox();
|
@@ -360,7 +401,7 @@ export class ImageNode {
|
|
360
401
|
this.bbox = Rect2.union(...this.children.map(child => child.getBBox()));
|
361
402
|
}
|
362
403
|
if (bubbleUp && !oldBBox.eq(this.bbox)) {
|
363
|
-
(
|
404
|
+
(_b = this.parent) === null || _b === void 0 ? void 0 : _b.recomputeBBox(true);
|
364
405
|
}
|
365
406
|
}
|
366
407
|
updateParents(recursive = false) {
|
@@ -395,8 +436,8 @@ export class ImageNode {
|
|
395
436
|
}
|
396
437
|
// Remove this node and all of its children
|
397
438
|
remove() {
|
398
|
-
var
|
399
|
-
(
|
439
|
+
var _b;
|
440
|
+
(_b = this.content) === null || _b === void 0 ? void 0 : _b.onRemoveFromImage();
|
400
441
|
if (!this.parent) {
|
401
442
|
this.content = null;
|
402
443
|
this.children = [];
|
@@ -43,7 +43,7 @@ class UndoRedoHistory {
|
|
43
43
|
}
|
44
44
|
__classPrivateFieldSet(this, _UndoRedoHistory_redoStack, [], "f");
|
45
45
|
if (__classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length > this.maxUndoRedoStackSize) {
|
46
|
-
const removeAtOnceCount =
|
46
|
+
const removeAtOnceCount = Math.ceil(this.maxUndoRedoStackSize / 100);
|
47
47
|
const removedElements = __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").splice(0, removeAtOnceCount);
|
48
48
|
removedElements.forEach(elem => elem.onDrop(this.editor));
|
49
49
|
}
|
@@ -28,7 +28,19 @@ export default class TextComponent extends AbstractComponent implements Restylea
|
|
28
28
|
getStyle(): ComponentStyle;
|
29
29
|
updateStyle(style: ComponentStyle): SerializableCommand;
|
30
30
|
forceStyle(style: ComponentStyle, editor: Editor | null): void;
|
31
|
-
getTextStyle():
|
31
|
+
getTextStyle(): {
|
32
|
+
renderingStyle: {
|
33
|
+
fill: import("../Color4").default;
|
34
|
+
stroke: {
|
35
|
+
color: import("../Color4").default;
|
36
|
+
width: number;
|
37
|
+
} | undefined;
|
38
|
+
};
|
39
|
+
size: number;
|
40
|
+
fontFamily: string;
|
41
|
+
fontWeight?: string | undefined;
|
42
|
+
fontVariant?: string | undefined;
|
43
|
+
};
|
32
44
|
getBaselinePos(): import("../lib").Vec3;
|
33
45
|
getTransform(): Mat33;
|
34
46
|
protected applyTransformation(affineTransfm: Mat33): void;
|
@@ -2,7 +2,7 @@ import LineSegment2 from '../math/LineSegment2';
|
|
2
2
|
import Mat33 from '../math/Mat33';
|
3
3
|
import Rect2 from '../math/Rect2';
|
4
4
|
import { Vec2 } from '../math/Vec2';
|
5
|
-
import { textStyleFromJSON, textStyleToJSON } from '../rendering/TextRenderingStyle';
|
5
|
+
import { cloneTextStyle, textStyleFromJSON, textStyleToJSON } from '../rendering/TextRenderingStyle';
|
6
6
|
import AbstractComponent from './AbstractComponent';
|
7
7
|
import { createRestyleComponentCommand } from './RestylableComponent';
|
8
8
|
const componentTypeId = 'text';
|
@@ -132,10 +132,10 @@ export default class TextComponent extends AbstractComponent {
|
|
132
132
|
}
|
133
133
|
forceStyle(style, editor) {
|
134
134
|
if (style.textStyle) {
|
135
|
-
this.style = style.textStyle;
|
135
|
+
this.style = cloneTextStyle(style.textStyle);
|
136
136
|
}
|
137
137
|
else if (style.color) {
|
138
|
-
this.style
|
138
|
+
this.style = Object.assign(Object.assign({}, this.style), { renderingStyle: Object.assign(Object.assign({}, this.style.renderingStyle), { fill: style.color }) });
|
139
139
|
}
|
140
140
|
else {
|
141
141
|
return;
|
@@ -152,7 +152,7 @@ export default class TextComponent extends AbstractComponent {
|
|
152
152
|
}
|
153
153
|
// See this.getStyle
|
154
154
|
getTextStyle() {
|
155
|
-
return this.style;
|
155
|
+
return cloneTextStyle(this.style);
|
156
156
|
}
|
157
157
|
getBaselinePos() {
|
158
158
|
return this.transform.transformVec2(Vec2.zero);
|
@@ -165,7 +165,15 @@ export default class TextComponent extends AbstractComponent {
|
|
165
165
|
this.recomputeBBox();
|
166
166
|
}
|
167
167
|
createClone() {
|
168
|
-
|
168
|
+
const clonedTextObjects = this.textObjects.map(obj => {
|
169
|
+
if (typeof obj === 'string') {
|
170
|
+
return obj;
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
return obj.createClone();
|
174
|
+
}
|
175
|
+
});
|
176
|
+
return new TextComponent(clonedTextObjects, this.transform, this.style);
|
169
177
|
}
|
170
178
|
getText() {
|
171
179
|
const result = [];
|
@@ -7,6 +7,13 @@ interface RenderingStyle {
|
|
7
7
|
};
|
8
8
|
}
|
9
9
|
export default RenderingStyle;
|
10
|
+
export declare const cloneStyle: (style: RenderingStyle) => {
|
11
|
+
fill: Color4;
|
12
|
+
stroke: {
|
13
|
+
color: Color4;
|
14
|
+
width: number;
|
15
|
+
} | undefined;
|
16
|
+
};
|
10
17
|
export declare const stylesEqual: (a: RenderingStyle, b: RenderingStyle) => boolean;
|
11
18
|
export declare const styleToJSON: (style: RenderingStyle) => {
|
12
19
|
fill: string;
|
@@ -1,4 +1,10 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
|
+
export const cloneStyle = (style) => {
|
3
|
+
return {
|
4
|
+
fill: style.fill,
|
5
|
+
stroke: style.stroke ? Object.assign({}, style.stroke) : undefined,
|
6
|
+
};
|
7
|
+
};
|
2
8
|
export const stylesEqual = (a, b) => {
|
3
9
|
var _a, _b, _c, _d, _e, _f;
|
4
10
|
const result = a === b || (a.fill.eq(b.fill)
|
@@ -7,6 +7,19 @@ export interface TextStyle {
|
|
7
7
|
renderingStyle: RenderingStyle;
|
8
8
|
}
|
9
9
|
export default TextStyle;
|
10
|
+
export declare const cloneTextStyle: (style: TextStyle) => {
|
11
|
+
renderingStyle: {
|
12
|
+
fill: import("../Color4").default;
|
13
|
+
stroke: {
|
14
|
+
color: import("../Color4").default;
|
15
|
+
width: number;
|
16
|
+
} | undefined;
|
17
|
+
};
|
18
|
+
size: number;
|
19
|
+
fontFamily: string;
|
20
|
+
fontWeight?: string | undefined;
|
21
|
+
fontVariant?: string | undefined;
|
22
|
+
};
|
10
23
|
export declare const textStyleFromJSON: (json: any) => TextStyle;
|
11
24
|
export declare const textStyleToJSON: (style: TextStyle) => {
|
12
25
|
renderingStyle: {
|
@@ -1,4 +1,7 @@
|
|
1
|
-
import { styleFromJSON, styleToJSON } from './RenderingStyle';
|
1
|
+
import { cloneStyle, styleFromJSON, styleToJSON } from './RenderingStyle';
|
2
|
+
export const cloneTextStyle = (style) => {
|
3
|
+
return Object.assign(Object.assign({}, style), { renderingStyle: cloneStyle(style.renderingStyle) });
|
4
|
+
};
|
2
5
|
export const textStyleFromJSON = (json) => {
|
3
6
|
if (typeof json === 'string') {
|
4
7
|
json = JSON.parse(json);
|
@@ -133,6 +133,16 @@ export default class HTMLToolbar {
|
|
133
133
|
}
|
134
134
|
return totalWidth;
|
135
135
|
};
|
136
|
+
// Returns true if there is enough empty space to move the first child
|
137
|
+
// from the overflow menu to the main menu.
|
138
|
+
const canRemoveFirstChildFromOverflow = (freeSpaceInMainMenu) => {
|
139
|
+
var _a, _b;
|
140
|
+
const overflowChildren = (_b = (_a = this.overflowWidget) === null || _a === void 0 ? void 0 : _a.getChildWidgets()) !== null && _b !== void 0 ? _b : [];
|
141
|
+
if (overflowChildren.length === 0) {
|
142
|
+
return false;
|
143
|
+
}
|
144
|
+
return overflowChildren[0].getButtonWidth() <= freeSpaceInMainMenu;
|
145
|
+
};
|
136
146
|
let overflowWidgetsWidth = getTotalWidth(this.overflowWidget.getChildWidgets());
|
137
147
|
let shownWidgetWidth = getTotalWidth(this.widgetList) - overflowWidgetsWidth;
|
138
148
|
let availableWidth = this.container.clientWidth * 0.87;
|
@@ -143,7 +153,8 @@ export default class HTMLToolbar {
|
|
143
153
|
availableWidth *= 1.75;
|
144
154
|
}
|
145
155
|
let updatedChildren = false;
|
146
|
-
|
156
|
+
// If we can remove at least one child from the overflow menu,
|
157
|
+
if (canRemoveFirstChildFromOverflow(availableWidth - shownWidgetWidth)) {
|
147
158
|
// Move widgets to the main menu.
|
148
159
|
const overflowChildren = this.overflowWidget.clearChildren();
|
149
160
|
for (const child of overflowChildren) {
|
@@ -153,13 +164,11 @@ export default class HTMLToolbar {
|
|
153
164
|
shownWidgetWidth += child.getButtonWidth();
|
154
165
|
}
|
155
166
|
}
|
156
|
-
this.overflowWidget.setHidden(true);
|
157
167
|
overflowWidgetsWidth = 0;
|
158
168
|
updatedChildren = true;
|
159
169
|
}
|
160
170
|
if (shownWidgetWidth >= availableWidth) {
|
161
171
|
// Move widgets to the overflow menu.
|
162
|
-
this.overflowWidget.setHidden(false);
|
163
172
|
// Start with the rightmost widget, move to the leftmost
|
164
173
|
for (let i = this.widgetList.length - 1; i >= 0 && shownWidgetWidth >= availableWidth; i--) {
|
165
174
|
const child = this.widgetList[i];
|
@@ -173,6 +182,8 @@ export default class HTMLToolbar {
|
|
173
182
|
}
|
174
183
|
updatedChildren = true;
|
175
184
|
}
|
185
|
+
// Hide/show the overflow widget.
|
186
|
+
this.overflowWidget.setHidden(this.overflowWidget.getChildWidgets().length === 0);
|
176
187
|
if (updatedChildren) {
|
177
188
|
this.setupColorPickers();
|
178
189
|
}
|
@@ -29,7 +29,8 @@ export default class FindTool extends BaseTool {
|
|
29
29
|
matchIdx = matches.length + matchIdx;
|
30
30
|
}
|
31
31
|
if (matchIdx < matches.length) {
|
32
|
-
|
32
|
+
const undoable = false;
|
33
|
+
this.editor.dispatch(this.editor.viewport.zoomTo(matches[matchIdx], true, true), undoable);
|
33
34
|
this.editor.announceForAccessibility(this.editor.localization.focusedFoundText(matchIdx + 1, matches.length));
|
34
35
|
}
|
35
36
|
}
|
@@ -1 +1,23 @@
|
|
1
|
+
/**
|
2
|
+
* Compile-time assertion that a branch of code is unreachable.
|
3
|
+
* @internal
|
4
|
+
*/
|
1
5
|
export declare const assertUnreachable: (key: never) => never;
|
6
|
+
/**
|
7
|
+
* Throws an exception if the typeof given value is not a number or `value` is NaN.
|
8
|
+
*
|
9
|
+
* @example
|
10
|
+
* ```ts
|
11
|
+
* const foo: unknown = 3;
|
12
|
+
* assertIsNumber(foo);
|
13
|
+
*
|
14
|
+
* assertIsNumber('hello, world'); // throws an Error.
|
15
|
+
* ```
|
16
|
+
*
|
17
|
+
*
|
18
|
+
*/
|
19
|
+
export declare const assertIsNumber: (value: any, allowNaN?: boolean) => value is number;
|
20
|
+
/**
|
21
|
+
* Throws if any of `values` is not of type number.
|
22
|
+
*/
|
23
|
+
export declare const assertIsNumberArray: (values: any[], allowNaN?: boolean) => values is number[];
|
@@ -1,6 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
/**
|
2
|
+
* Compile-time assertion that a branch of code is unreachable.
|
3
|
+
* @internal
|
4
|
+
*/
|
4
5
|
export const assertUnreachable = (key) => {
|
6
|
+
// See https://stackoverflow.com/a/39419171/17055750
|
5
7
|
throw new Error(`Should be unreachable. Key: ${key}.`);
|
6
8
|
};
|
9
|
+
/**
|
10
|
+
* Throws an exception if the typeof given value is not a number or `value` is NaN.
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```ts
|
14
|
+
* const foo: unknown = 3;
|
15
|
+
* assertIsNumber(foo);
|
16
|
+
*
|
17
|
+
* assertIsNumber('hello, world'); // throws an Error.
|
18
|
+
* ```
|
19
|
+
*
|
20
|
+
*
|
21
|
+
*/
|
22
|
+
export const assertIsNumber = (value, allowNaN = false) => {
|
23
|
+
if (typeof value !== 'number' || (!allowNaN && isNaN(value))) {
|
24
|
+
throw new Error('Given value is not a number');
|
25
|
+
// return false;
|
26
|
+
}
|
27
|
+
return true;
|
28
|
+
};
|
29
|
+
/**
|
30
|
+
* Throws if any of `values` is not of type number.
|
31
|
+
*/
|
32
|
+
export const assertIsNumberArray = (values, allowNaN = false) => {
|
33
|
+
if (typeof values !== 'object') {
|
34
|
+
throw new Error('Asserting isNumberArray: Given entity is not an array');
|
35
|
+
}
|
36
|
+
if (!assertIsNumber(values['length'])) {
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
for (const value of values) {
|
40
|
+
if (!assertIsNumber(value, allowNaN)) {
|
41
|
+
return false;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
return true;
|
45
|
+
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "0.17.
|
3
|
+
"version": "0.17.2",
|
4
4
|
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
|
5
5
|
"main": "./dist/src/lib.d.ts",
|
6
6
|
"types": "./dist/src/lib.js",
|
@@ -78,31 +78,31 @@
|
|
78
78
|
"postpack": "pinst --enable"
|
79
79
|
},
|
80
80
|
"dependencies": {
|
81
|
-
"@melloware/coloris": "^0.
|
82
|
-
"bezier-js": "^6.1.
|
81
|
+
"@melloware/coloris": "^0.18.0",
|
82
|
+
"bezier-js": "^6.1.3"
|
83
83
|
},
|
84
84
|
"devDependencies": {
|
85
85
|
"@types/bezier-js": "^4.1.0",
|
86
|
-
"@types/jest": "^29.
|
87
|
-
"@types/jsdom": "^
|
88
|
-
"@types/node": "^18.
|
89
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
90
|
-
"@typescript-eslint/parser": "^5.
|
86
|
+
"@types/jest": "^29.4.0",
|
87
|
+
"@types/jsdom": "^21.1.0",
|
88
|
+
"@types/node": "^18.14.0",
|
89
|
+
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
90
|
+
"@typescript-eslint/parser": "^5.53.0",
|
91
91
|
"css-loader": "^6.7.3",
|
92
|
-
"eslint": "^8.
|
92
|
+
"eslint": "^8.34.0",
|
93
93
|
"husky": "^8.0.3",
|
94
|
-
"jest": "^29.3
|
95
|
-
"jest-environment-jsdom": "^29.3
|
96
|
-
"jsdom": "^
|
97
|
-
"lint-staged": "^13.1.
|
94
|
+
"jest": "^29.4.3",
|
95
|
+
"jest-environment-jsdom": "^29.4.3",
|
96
|
+
"jsdom": "^21.1.0",
|
97
|
+
"lint-staged": "^13.1.2",
|
98
98
|
"pinst": "^3.0.0",
|
99
99
|
"style-loader": "^3.3.1",
|
100
100
|
"terser-webpack-plugin": "^5.3.6",
|
101
|
-
"ts-jest": "^29.0.
|
101
|
+
"ts-jest": "^29.0.5",
|
102
102
|
"ts-loader": "^9.4.2",
|
103
103
|
"ts-node": "^10.9.1",
|
104
|
-
"typedoc": "^0.23.
|
105
|
-
"typescript": "^4.9.
|
104
|
+
"typedoc": "^0.23.25",
|
105
|
+
"typescript": "^4.9.5",
|
106
106
|
"webpack": "^5.75.0"
|
107
107
|
},
|
108
108
|
"bugs": {
|
package/src/EditorImage.test.ts
CHANGED
@@ -6,6 +6,9 @@ import Color4 from './Color4';
|
|
6
6
|
import DummyRenderer from './rendering/renderers/DummyRenderer';
|
7
7
|
import createEditor from './testing/createEditor';
|
8
8
|
import RenderingStyle from './rendering/RenderingStyle';
|
9
|
+
import Rect2 from './math/Rect2';
|
10
|
+
import Mat33 from './math/Mat33';
|
11
|
+
import { SerializableCommand } from './lib';
|
9
12
|
|
10
13
|
describe('EditorImage', () => {
|
11
14
|
const testStroke = new Stroke([
|
@@ -84,4 +87,34 @@ describe('EditorImage', () => {
|
|
84
87
|
expect(firstParent?.getParent()).toStrictEqual(secondParent?.getParent());
|
85
88
|
expect(firstParent?.getParent()?.getParent()).toBeNull();
|
86
89
|
});
|
90
|
+
|
91
|
+
it('setImportExportRect should return a serializable command', () => {
|
92
|
+
const editor = createEditor();
|
93
|
+
const image = editor.image;
|
94
|
+
|
95
|
+
const originalRect = editor.getImportExportRect();
|
96
|
+
const newRect = new Rect2(3, 4, 5, 6);
|
97
|
+
const command = image.setImportExportRect(newRect);
|
98
|
+
expect(command.serialize().data).toMatchObject({
|
99
|
+
originalSize: originalRect.size.xy,
|
100
|
+
originalTransform: Mat33.identity.toArray(),
|
101
|
+
newRegion: {
|
102
|
+
x: 3,
|
103
|
+
y: 4,
|
104
|
+
w: 5,
|
105
|
+
h: 6,
|
106
|
+
},
|
107
|
+
});
|
108
|
+
|
109
|
+
expect(editor.getImportExportRect()).objEq(originalRect);
|
110
|
+
command.apply(editor);
|
111
|
+
expect(editor.getImportExportRect()).objEq(newRect);
|
112
|
+
|
113
|
+
const deserializedCommand = SerializableCommand.deserialize(command.serialize(), editor);
|
114
|
+
|
115
|
+
deserializedCommand.unapply(editor);
|
116
|
+
expect(editor.getImportExportRect()).objEq(originalRect);
|
117
|
+
deserializedCommand.apply(editor);
|
118
|
+
expect(editor.getImportExportRect()).objEq(newRect);
|
119
|
+
});
|
87
120
|
});
|