js-draw 0.9.3 → 0.10.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/.firebase/hosting.ZG9jcw.cache +338 -0
- package/.github/ISSUE_TEMPLATE/translation.yml +9 -1
- package/CHANGELOG.md +8 -0
- package/build_tools/buildTranslationTemplate.ts +6 -4
- package/dist/build_tools/buildTranslationTemplate.js +5 -4
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +1 -0
- package/dist/src/Color4.js +34 -15
- package/dist/src/Editor.d.ts +2 -2
- package/dist/src/Editor.js +2 -3
- package/dist/src/EditorImage.d.ts +1 -1
- package/dist/src/EventDispatcher.d.ts +1 -1
- package/dist/src/SVGLoader.d.ts +2 -2
- package/dist/src/SVGLoader.js +25 -11
- package/dist/src/UndoRedoHistory.d.ts +2 -2
- package/dist/src/Viewport.d.ts +1 -1
- package/dist/src/commands/SerializableCommand.d.ts +1 -1
- package/dist/src/components/AbstractComponent.d.ts +3 -3
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/src/components/builders/FreehandLineBuilder.js +3 -3
- package/dist/src/components/builders/PressureSensitiveFreehandLineBuilder.js +1 -1
- package/dist/src/components/builders/types.d.ts +1 -1
- package/dist/src/components/util/StrokeSmoother.d.ts +1 -1
- package/dist/src/math/Mat33.d.ts +1 -1
- package/dist/src/math/Path.d.ts +1 -1
- package/dist/src/math/Vec2.d.ts +2 -2
- package/dist/src/rendering/caching/testUtils.d.ts +1 -1
- package/dist/src/rendering/caching/types.d.ts +2 -2
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +1 -0
- package/dist/src/rendering/renderers/SVGRenderer.js +23 -6
- package/dist/src/toolbar/HTMLToolbar.d.ts +6 -1
- package/dist/src/toolbar/HTMLToolbar.js +24 -27
- package/dist/src/toolbar/IconProvider.d.ts +1 -1
- package/dist/src/toolbar/makeColorInput.d.ts +2 -2
- package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +3 -3
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +2 -2
- package/dist/src/toolbar/widgets/BaseWidget.js +9 -4
- package/dist/src/tools/BaseTool.js +4 -4
- package/dist/src/tools/PanZoom.d.ts +5 -1
- package/dist/src/tools/PanZoom.js +108 -10
- package/dist/src/tools/PipetteTool.d.ts +1 -1
- package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -3
- package/dist/src/tools/SelectionTool/SelectionHandle.js +1 -1
- package/dist/src/tools/ToolbarShortcutHandler.d.ts +1 -1
- package/dist/src/types.d.ts +8 -8
- package/dist/src/{language → util}/assertions.d.ts +0 -0
- package/dist/src/{language → util}/assertions.js +1 -0
- package/dist/src/util/untilNextAnimationFrame.d.ts +3 -0
- package/dist/src/util/untilNextAnimationFrame.js +7 -0
- package/package.json +1 -1
- package/src/Color4.test.ts +7 -0
- package/src/Color4.ts +47 -18
- package/src/Editor.toSVG.test.ts +84 -0
- package/src/Editor.ts +2 -3
- package/src/SVGLoader.ts +26 -10
- package/src/components/builders/FreehandLineBuilder.ts +3 -3
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +1 -1
- package/src/rendering/renderers/SVGRenderer.ts +23 -5
- package/src/toolbar/HTMLToolbar.ts +33 -30
- package/src/toolbar/widgets/ActionButtonWidget.ts +2 -2
- package/src/toolbar/widgets/BaseWidget.ts +9 -4
- package/src/tools/PanZoom.ts +124 -7
- package/src/tools/SelectionTool/SelectionHandle.ts +1 -1
- package/src/{language → util}/assertions.ts +1 -0
- package/src/util/untilNextAnimationFrame.ts +9 -0
package/dist/src/Color4.d.ts
CHANGED
package/dist/src/Color4.js
CHANGED
@@ -63,24 +63,40 @@ export default class Color4 {
|
|
63
63
|
if (text.startsWith('#')) {
|
64
64
|
return Color4.fromHex(text);
|
65
65
|
}
|
66
|
-
|
66
|
+
if (text === 'none' || text === 'transparent') {
|
67
67
|
return Color4.transparent;
|
68
68
|
}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
const
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
69
|
+
// rgba?: Match both rgb and rgba strings.
|
70
|
+
// ([,0-9.]+): Match any string of only numeric, '.' and ',' characters.
|
71
|
+
const rgbRegex = /^rgba?\(([,0-9.]+)\)$/i;
|
72
|
+
const rgbMatch = text.replace(/\s*/g, '').match(rgbRegex);
|
73
|
+
if (rgbMatch) {
|
74
|
+
const componentsListStr = rgbMatch[1];
|
75
|
+
const componentsList = JSON.parse(`[ ${componentsListStr} ]`);
|
76
|
+
if (componentsList.length === 3) {
|
77
|
+
return Color4.ofRGB(componentsList[0] / 255, componentsList[1] / 255, componentsList[2] / 255);
|
78
|
+
}
|
79
|
+
else if (componentsList.length === 4) {
|
80
|
+
return Color4.ofRGBA(componentsList[0] / 255, componentsList[1] / 255, componentsList[2] / 255, componentsList[3]);
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
throw new Error(`RGB string, ${text}, has wrong number of components: ${componentsList.length}`);
|
84
|
+
}
|
83
85
|
}
|
86
|
+
// Otherwise, try to use an HTMLCanvasElement to determine the color.
|
87
|
+
// Note: We may be unable to create an HTMLCanvasElement if running as a unit test.
|
88
|
+
const canvas = document.createElement('canvas');
|
89
|
+
canvas.width = 1;
|
90
|
+
canvas.height = 1;
|
91
|
+
const ctx = canvas.getContext('2d');
|
92
|
+
ctx.fillStyle = text;
|
93
|
+
ctx.fillRect(0, 0, 1, 1);
|
94
|
+
const data = ctx.getImageData(0, 0, 1, 1);
|
95
|
+
const red = data.data[0] / 255;
|
96
|
+
const green = data.data[1] / 255;
|
97
|
+
const blue = data.data[2] / 255;
|
98
|
+
const alpha = data.data[3] / 255;
|
99
|
+
return Color4.ofRGBA(red, green, blue, alpha);
|
84
100
|
}
|
85
101
|
/** @returns true if `this` and `other` are approximately equal. */
|
86
102
|
eq(other) {
|
@@ -118,6 +134,9 @@ export default class Color4 {
|
|
118
134
|
this.hexString = `#${red}${green}${blue}${alpha}`;
|
119
135
|
return this.hexString;
|
120
136
|
}
|
137
|
+
toString() {
|
138
|
+
return this.toHexString();
|
139
|
+
}
|
121
140
|
}
|
122
141
|
Color4.transparent = Color4.ofRGBA(0, 0, 0, 0);
|
123
142
|
Color4.red = Color4.ofRGB(1.0, 0.0, 0.0);
|
package/dist/src/Editor.d.ts
CHANGED
@@ -29,8 +29,8 @@ import Pointer from './Pointer';
|
|
29
29
|
import Rect2 from './math/Rect2';
|
30
30
|
import { EditorLocalization } from './localization';
|
31
31
|
import IconProvider from './toolbar/IconProvider';
|
32
|
-
type HTMLPointerEventType = 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel';
|
33
|
-
type HTMLPointerEventFilter = (eventName: HTMLPointerEventType, event: PointerEvent) => boolean;
|
32
|
+
declare type HTMLPointerEventType = 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel';
|
33
|
+
declare type HTMLPointerEventFilter = (eventName: HTMLPointerEventType, event: PointerEvent) => boolean;
|
34
34
|
export interface EditorSettings {
|
35
35
|
/** Defaults to `RenderingMode.CanvasRenderer` */
|
36
36
|
renderingMode: RenderingMode;
|
package/dist/src/Editor.js
CHANGED
@@ -44,6 +44,7 @@ import getLocalizationTable from './localizations/getLocalizationTable';
|
|
44
44
|
import IconProvider from './toolbar/IconProvider';
|
45
45
|
import { toRoundedString } from './math/rounding';
|
46
46
|
import CanvasRenderer from './rendering/renderers/CanvasRenderer';
|
47
|
+
import untilNextAnimationFrame from './util/untilNextAnimationFrame';
|
47
48
|
// { @inheritDoc Editor! }
|
48
49
|
export class Editor {
|
49
50
|
/**
|
@@ -676,9 +677,7 @@ export class Editor {
|
|
676
677
|
if (countProcessed % 500 === 0) {
|
677
678
|
this.showLoadingWarning(countProcessed / totalToProcess);
|
678
679
|
this.rerender();
|
679
|
-
return
|
680
|
-
requestAnimationFrame(() => resolve());
|
681
|
-
});
|
680
|
+
return untilNextAnimationFrame();
|
682
681
|
}
|
683
682
|
return null;
|
684
683
|
}, (importExportRect) => {
|
@@ -27,7 +27,7 @@ export default class EditorImage {
|
|
27
27
|
static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
28
28
|
private static AddElementCommand;
|
29
29
|
}
|
30
|
-
type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
30
|
+
declare type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
31
31
|
/** Part of the Editor's image. @internal */
|
32
32
|
export declare class ImageNode {
|
33
33
|
private parent;
|
@@ -15,7 +15,7 @@
|
|
15
15
|
*
|
16
16
|
* @packageDocumentation
|
17
17
|
*/
|
18
|
-
type CallbackHandler<EventType> = (data: EventType) => void;
|
18
|
+
declare type CallbackHandler<EventType> = (data: EventType) => void;
|
19
19
|
export default class EventDispatcher<EventKeyType extends string | symbol | number, EventMessageType> {
|
20
20
|
private listeners;
|
21
21
|
constructor();
|
package/dist/src/SVGLoader.d.ts
CHANGED
@@ -3,8 +3,8 @@ import { ComponentAddedListener, ImageLoader, OnDetermineExportRectListener, OnP
|
|
3
3
|
export declare const defaultSVGViewRect: Rect2;
|
4
4
|
export declare const svgAttributesDataKey = "svgAttrs";
|
5
5
|
export declare const svgStyleAttributesDataKey = "svgStyleAttrs";
|
6
|
-
export type SVGLoaderUnknownAttribute = [string, string];
|
7
|
-
export type SVGLoaderUnknownStyleAttribute = {
|
6
|
+
export declare type SVGLoaderUnknownAttribute = [string, string];
|
7
|
+
export declare type SVGLoaderUnknownStyleAttribute = {
|
8
8
|
key: string;
|
9
9
|
value: string;
|
10
10
|
priority?: string;
|
package/dist/src/SVGLoader.js
CHANGED
@@ -34,12 +34,14 @@ export default class SVGLoader {
|
|
34
34
|
this.processedCount = 0;
|
35
35
|
this.totalToProcess = 0;
|
36
36
|
}
|
37
|
-
|
38
|
-
|
37
|
+
// If [computedStyles] is given, it is preferred to directly accessing node's style object.
|
38
|
+
getStyle(node, computedStyles) {
|
39
|
+
var _a, _b, _c, _d, _f, _g;
|
39
40
|
const style = {
|
40
41
|
fill: Color4.transparent,
|
41
42
|
};
|
42
|
-
|
43
|
+
// If possible, use computedStyles (allows property inheritance).
|
44
|
+
const fillAttribute = (_b = (_a = node.getAttribute('fill')) !== null && _a !== void 0 ? _a : computedStyles === null || computedStyles === void 0 ? void 0 : computedStyles.fill) !== null && _b !== void 0 ? _b : node.style.fill;
|
43
45
|
if (fillAttribute) {
|
44
46
|
try {
|
45
47
|
style.fill = Color4.fromString(fillAttribute);
|
@@ -48,18 +50,21 @@ export default class SVGLoader {
|
|
48
50
|
console.error('Unknown fill color,', fillAttribute);
|
49
51
|
}
|
50
52
|
}
|
51
|
-
const strokeAttribute = (
|
52
|
-
const strokeWidthAttr = (
|
53
|
-
if (strokeAttribute) {
|
53
|
+
const strokeAttribute = (_d = (_c = node.getAttribute('stroke')) !== null && _c !== void 0 ? _c : computedStyles === null || computedStyles === void 0 ? void 0 : computedStyles.stroke) !== null && _d !== void 0 ? _d : node.style.stroke;
|
54
|
+
const strokeWidthAttr = (_g = (_f = node.getAttribute('stroke-width')) !== null && _f !== void 0 ? _f : computedStyles === null || computedStyles === void 0 ? void 0 : computedStyles.strokeWidth) !== null && _g !== void 0 ? _g : node.style.strokeWidth;
|
55
|
+
if (strokeAttribute && strokeWidthAttr) {
|
54
56
|
try {
|
55
57
|
let width = parseFloat(strokeWidthAttr !== null && strokeWidthAttr !== void 0 ? strokeWidthAttr : '1');
|
56
58
|
if (!isFinite(width)) {
|
57
59
|
width = 0;
|
58
60
|
}
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
const strokeColor = Color4.fromString(strokeAttribute);
|
62
|
+
if (strokeColor.a > 0) {
|
63
|
+
style.stroke = {
|
64
|
+
width,
|
65
|
+
color: strokeColor,
|
66
|
+
};
|
67
|
+
}
|
63
68
|
}
|
64
69
|
catch (e) {
|
65
70
|
console.error('Error parsing stroke data:', e);
|
@@ -188,6 +193,10 @@ export default class SVGLoader {
|
|
188
193
|
throw new Error(`Unrecognized text child node: ${child}.`);
|
189
194
|
}
|
190
195
|
}
|
196
|
+
// If no content, the content is an empty string.
|
197
|
+
if (contentList.length === 0) {
|
198
|
+
contentList.push('');
|
199
|
+
}
|
191
200
|
// Compute styles.
|
192
201
|
const computedStyles = window.getComputedStyle(elem);
|
193
202
|
const fontSizeMatch = /^([-0-9.e]+)px/i.exec(computedStyles.fontSize);
|
@@ -204,7 +213,7 @@ export default class SVGLoader {
|
|
204
213
|
const style = {
|
205
214
|
size: fontSize,
|
206
215
|
fontFamily: computedStyles.fontFamily || elem.style.fontFamily || 'sans-serif',
|
207
|
-
renderingStyle: this.getStyle(elem),
|
216
|
+
renderingStyle: this.getStyle(elem, computedStyles),
|
208
217
|
};
|
209
218
|
const supportedAttrs = [];
|
210
219
|
const transform = this.getTransform(elem, supportedAttrs, computedStyles);
|
@@ -297,6 +306,9 @@ export default class SVGLoader {
|
|
297
306
|
this.updateViewBox(node);
|
298
307
|
this.updateSVGAttrs(node);
|
299
308
|
break;
|
309
|
+
case 'style':
|
310
|
+
this.addUnknownNode(node);
|
311
|
+
break;
|
300
312
|
default:
|
301
313
|
console.warn('Unknown SVG element,', node);
|
302
314
|
if (!(node instanceof SVGElement)) {
|
@@ -361,6 +373,8 @@ export default class SVGLoader {
|
|
361
373
|
<html>
|
362
374
|
<head>
|
363
375
|
<title>SVG Loading Sandbox</title>
|
376
|
+
<meta name='viewport' conent='width=device-width,initial-scale=1.0'/>
|
377
|
+
<meta charset='utf-8'/>
|
364
378
|
</head>
|
365
379
|
<body>
|
366
380
|
<script>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import Editor from './Editor';
|
2
2
|
import Command from './commands/Command';
|
3
|
-
type AnnounceRedoCallback = (command: Command) => void;
|
4
|
-
type AnnounceUndoCallback = (command: Command) => void;
|
3
|
+
declare type AnnounceRedoCallback = (command: Command) => void;
|
4
|
+
declare type AnnounceUndoCallback = (command: Command) => void;
|
5
5
|
declare class UndoRedoHistory {
|
6
6
|
private readonly editor;
|
7
7
|
private announceRedoCallback;
|
package/dist/src/Viewport.d.ts
CHANGED
@@ -4,7 +4,7 @@ import Rect2 from './math/Rect2';
|
|
4
4
|
import { Point2, Vec2 } from './math/Vec2';
|
5
5
|
import { StrokeDataPoint } from './types';
|
6
6
|
import { EditorNotifier } from './types';
|
7
|
-
type PointDataType<T extends Point2 | StrokeDataPoint | number> = T extends Point2 ? Point2 : number;
|
7
|
+
declare type PointDataType<T extends Point2 | StrokeDataPoint | number> = T extends Point2 ? Point2 : number;
|
8
8
|
export declare abstract class ViewportTransform extends Command {
|
9
9
|
abstract readonly transform: Mat33;
|
10
10
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Editor from '../Editor';
|
2
2
|
import Command from './Command';
|
3
|
-
export type DeserializationCallback = (data: Record<string, any> | any[], editor: Editor) => SerializableCommand;
|
3
|
+
export declare type DeserializationCallback = (data: Record<string, any> | any[], editor: Editor) => SerializableCommand;
|
4
4
|
export default abstract class SerializableCommand extends Command {
|
5
5
|
private commandTypeId;
|
6
6
|
constructor(commandTypeId: string);
|
@@ -4,9 +4,9 @@ import Mat33 from '../math/Mat33';
|
|
4
4
|
import Rect2 from '../math/Rect2';
|
5
5
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
|
-
export type LoadSaveData = (string[] | Record<symbol, string | number>);
|
8
|
-
export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
|
9
|
-
export type DeserializeCallback = (data: string) => AbstractComponent;
|
7
|
+
export declare type LoadSaveData = (string[] | Record<symbol, string | number>);
|
8
|
+
export declare type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
|
9
|
+
export declare type DeserializeCallback = (data: string) => AbstractComponent;
|
10
10
|
export default abstract class AbstractComponent {
|
11
11
|
private readonly componentKind;
|
12
12
|
protected lastChangedTime: number;
|
@@ -4,7 +4,7 @@ import Rect2 from '../math/Rect2';
|
|
4
4
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
5
5
|
import AbstractComponent from './AbstractComponent';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
|
-
type GlobalAttrsList = Array<[string, string | null]>;
|
7
|
+
declare type GlobalAttrsList = Array<[string, string | null]>;
|
8
8
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
9
9
|
private readonly attrs;
|
10
10
|
protected contentBBox: Rect2;
|
@@ -33,7 +33,7 @@ export default class FreehandLineBuilder {
|
|
33
33
|
fill: Color4.transparent,
|
34
34
|
stroke: {
|
35
35
|
color: this.startPoint.color,
|
36
|
-
width: this.roundDistance(this.averageWidth),
|
36
|
+
width: this.roundDistance(this.averageWidth / 2),
|
37
37
|
}
|
38
38
|
};
|
39
39
|
}
|
@@ -77,7 +77,7 @@ export default class FreehandLineBuilder {
|
|
77
77
|
return this.previewStroke();
|
78
78
|
}
|
79
79
|
getMinFit() {
|
80
|
-
let minFit = Math.min(this.minFitAllowed, this.averageWidth /
|
80
|
+
let minFit = Math.min(this.minFitAllowed, this.averageWidth / 5);
|
81
81
|
if (minFit < 1e-10) {
|
82
82
|
minFit = this.minFitAllowed;
|
83
83
|
}
|
@@ -98,7 +98,7 @@ export default class FreehandLineBuilder {
|
|
98
98
|
if (!this.isFirstSegment) {
|
99
99
|
return [];
|
100
100
|
}
|
101
|
-
const width = Viewport.roundPoint(this.startPoint.width /
|
101
|
+
const width = Viewport.roundPoint(this.startPoint.width / 9, Math.min(this.minFitAllowed, this.startPoint.width / 5));
|
102
102
|
const center = this.roundPoint(this.startPoint.pos);
|
103
103
|
// Start on the right, cycle clockwise:
|
104
104
|
// |
|
@@ -141,7 +141,7 @@ export default class PressureSensitiveFreehandLineBuilder {
|
|
141
141
|
return this.previewStroke();
|
142
142
|
}
|
143
143
|
roundPoint(point) {
|
144
|
-
let minFit = Math.min(this.minFitAllowed, this.curveStartWidth /
|
144
|
+
let minFit = Math.min(this.minFitAllowed, this.curveStartWidth / 3);
|
145
145
|
if (minFit < 1e-10) {
|
146
146
|
minFit = this.minFitAllowed;
|
147
147
|
}
|
@@ -9,4 +9,4 @@ export interface ComponentBuilder {
|
|
9
9
|
preview(renderer: AbstractRenderer): void;
|
10
10
|
addPoint(point: StrokeDataPoint): void;
|
11
11
|
}
|
12
|
-
export type ComponentBuilderFactory = (startPoint: StrokeDataPoint, viewport: Viewport) => ComponentBuilder;
|
12
|
+
export declare type ComponentBuilderFactory = (startPoint: StrokeDataPoint, viewport: Viewport) => ComponentBuilder;
|
@@ -8,7 +8,7 @@ export interface Curve {
|
|
8
8
|
endWidth: number;
|
9
9
|
endPoint: Vec2;
|
10
10
|
}
|
11
|
-
type OnCurveAddedCallback = (curve: Curve | null) => void;
|
11
|
+
declare type OnCurveAddedCallback = (curve: Curve | null) => void;
|
12
12
|
export declare class StrokeSmoother {
|
13
13
|
private startPoint;
|
14
14
|
private minFitAllowed;
|
package/dist/src/math/Mat33.d.ts
CHANGED
package/dist/src/math/Path.d.ts
CHANGED
@@ -30,7 +30,7 @@ export interface MoveToPathCommand {
|
|
30
30
|
kind: PathCommandType.MoveTo;
|
31
31
|
point: Point2;
|
32
32
|
}
|
33
|
-
export type PathCommand = CubicBezierPathCommand | LinePathCommand | QuadraticBezierPathCommand | MoveToPathCommand;
|
33
|
+
export declare type PathCommand = CubicBezierPathCommand | LinePathCommand | QuadraticBezierPathCommand | MoveToPathCommand;
|
34
34
|
interface IntersectionResult {
|
35
35
|
curve: LineSegment2 | Bezier;
|
36
36
|
parameterValue: number;
|
package/dist/src/math/Vec2.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import DummyRenderer from '../renderers/DummyRenderer';
|
2
2
|
import RenderingCache from './RenderingCache';
|
3
3
|
import { CacheProps } from './types';
|
4
|
-
type RenderAllocCallback = (renderer: DummyRenderer) => void;
|
4
|
+
declare type RenderAllocCallback = (renderer: DummyRenderer) => void;
|
5
5
|
export declare const createCache: (onRenderAlloc?: RenderAllocCallback, cacheOptions?: Partial<CacheProps>) => {
|
6
6
|
cache: RenderingCache;
|
7
7
|
editor: import("../../Editor").Editor;
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { Vec2 } from '../../math/Vec2';
|
2
2
|
import AbstractRenderer from '../renderers/AbstractRenderer';
|
3
3
|
import { CacheRecordManager } from './CacheRecordManager';
|
4
|
-
export type CacheAddress = number;
|
5
|
-
export type BeforeDeallocCallback = () => void;
|
4
|
+
export declare type CacheAddress = number;
|
5
|
+
export declare type BeforeDeallocCallback = () => void;
|
6
6
|
export interface CacheProps {
|
7
7
|
createRenderer(): AbstractRenderer;
|
8
8
|
isOfCorrectType(renderer: AbstractRenderer): boolean;
|
@@ -24,6 +24,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
24
24
|
private transformFrom;
|
25
25
|
private textContainer;
|
26
26
|
private textContainerTransform;
|
27
|
+
private textParentStyle;
|
27
28
|
drawText(text: string, transform: Mat33, style: TextStyle): void;
|
28
29
|
drawImage(image: RenderableImage): void;
|
29
30
|
startObject(boundingBox: Rect2): void;
|
@@ -18,6 +18,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
18
18
|
this.overwrittenAttrs = {};
|
19
19
|
this.textContainer = null;
|
20
20
|
this.textContainerTransform = null;
|
21
|
+
this.textParentStyle = null;
|
21
22
|
this.clear();
|
22
23
|
this.addStyleSheet();
|
23
24
|
}
|
@@ -130,12 +131,26 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
130
131
|
drawText(text, transform, style) {
|
131
132
|
var _a;
|
132
133
|
const applyTextStyles = (elem, style) => {
|
133
|
-
var _a, _b;
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
134
|
+
var _a, _b, _c, _d, _e, _f;
|
135
|
+
if (style.fontFamily !== ((_a = this.textParentStyle) === null || _a === void 0 ? void 0 : _a.fontFamily)) {
|
136
|
+
elem.style.fontFamily = style.fontFamily;
|
137
|
+
}
|
138
|
+
if (style.fontVariant !== ((_b = this.textParentStyle) === null || _b === void 0 ? void 0 : _b.fontVariant)) {
|
139
|
+
elem.style.fontVariant = (_c = style.fontVariant) !== null && _c !== void 0 ? _c : '';
|
140
|
+
}
|
141
|
+
if (style.fontWeight !== ((_d = this.textParentStyle) === null || _d === void 0 ? void 0 : _d.fontWeight)) {
|
142
|
+
elem.style.fontWeight = (_e = style.fontWeight) !== null && _e !== void 0 ? _e : '';
|
143
|
+
}
|
144
|
+
if (style.size !== ((_f = this.textParentStyle) === null || _f === void 0 ? void 0 : _f.size)) {
|
145
|
+
elem.style.fontSize = style.size + 'px';
|
146
|
+
}
|
147
|
+
const fillString = style.renderingStyle.fill.toHexString();
|
148
|
+
// TODO: Uncomment at some future major version release --- currently causes incompatibility due
|
149
|
+
// to an SVG parsing bug in older versions.
|
150
|
+
//const parentFillString = this.textParentStyle?.renderingStyle?.fill?.toHexString();
|
151
|
+
//if (fillString !== parentFillString) {
|
152
|
+
elem.style.fill = fillString;
|
153
|
+
//}
|
139
154
|
if (style.renderingStyle.stroke) {
|
140
155
|
const strokeStyle = style.renderingStyle.stroke;
|
141
156
|
elem.style.stroke = strokeStyle.color.toHexString();
|
@@ -156,6 +171,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
156
171
|
if (this.objectLevel > 0) {
|
157
172
|
this.textContainer = container;
|
158
173
|
this.textContainerTransform = transform;
|
174
|
+
this.textParentStyle = style;
|
159
175
|
}
|
160
176
|
}
|
161
177
|
else {
|
@@ -188,6 +204,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
188
204
|
this.lastPathString = [];
|
189
205
|
this.lastPathStyle = null;
|
190
206
|
this.textContainer = null;
|
207
|
+
this.textParentStyle = null;
|
191
208
|
this.objectElems = [];
|
192
209
|
}
|
193
210
|
endObject(loaderData) {
|
@@ -16,7 +16,12 @@ export default class HTMLToolbar {
|
|
16
16
|
addWidget(widget: BaseWidget): void;
|
17
17
|
serializeState(): string;
|
18
18
|
deserializeState(state: string): void;
|
19
|
-
|
19
|
+
/**
|
20
|
+
* Adds an action button with `title` to this toolbar (or to the given `parent` element).
|
21
|
+
*
|
22
|
+
* @return The added button.
|
23
|
+
*/
|
24
|
+
addActionButton(title: string | ActionButtonIcon, command: () => void): BaseWidget;
|
20
25
|
addUndoRedoButtons(): void;
|
21
26
|
addDefaultToolWidgets(): void;
|
22
27
|
addDefaultActionButtons(): void;
|
@@ -12,6 +12,7 @@ import EraserWidget from './widgets/EraserToolWidget';
|
|
12
12
|
import SelectionToolWidget from './widgets/SelectionToolWidget';
|
13
13
|
import TextToolWidget from './widgets/TextToolWidget';
|
14
14
|
import HandToolWidget from './widgets/HandToolWidget';
|
15
|
+
import { ActionButtonWidget } from './lib';
|
15
16
|
export const toolbarCSSPrefix = 'toolbar-';
|
16
17
|
export default class HTMLToolbar {
|
17
18
|
/** @internal */
|
@@ -122,49 +123,45 @@ export default class HTMLToolbar {
|
|
122
123
|
this.widgets[widgetId].deserializeFrom(data[widgetId]);
|
123
124
|
}
|
124
125
|
}
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
return button;
|
126
|
+
/**
|
127
|
+
* Adds an action button with `title` to this toolbar (or to the given `parent` element).
|
128
|
+
*
|
129
|
+
* @return The added button.
|
130
|
+
*/
|
131
|
+
addActionButton(title, command) {
|
132
|
+
const titleString = typeof title === 'string' ? title : title.label;
|
133
|
+
const widgetId = 'action-button';
|
134
|
+
const makeIcon = () => {
|
135
|
+
if (typeof title === 'string') {
|
136
|
+
return null;
|
137
|
+
}
|
138
|
+
return title.icon;
|
139
|
+
};
|
140
|
+
const widget = new ActionButtonWidget(this.editor, widgetId, makeIcon, titleString, command, this.editor.localization);
|
141
|
+
this.addWidget(widget);
|
142
|
+
return widget;
|
143
143
|
}
|
144
144
|
addUndoRedoButtons() {
|
145
|
-
const undoRedoGroup = document.createElement('div');
|
146
|
-
undoRedoGroup.classList.add(`${toolbarCSSPrefix}buttonGroup`);
|
147
145
|
const undoButton = this.addActionButton({
|
148
146
|
label: this.localizationTable.undo,
|
149
147
|
icon: this.editor.icons.makeUndoIcon()
|
150
148
|
}, () => {
|
151
149
|
this.editor.history.undo();
|
152
|
-
}
|
150
|
+
});
|
153
151
|
const redoButton = this.addActionButton({
|
154
152
|
label: this.localizationTable.redo,
|
155
153
|
icon: this.editor.icons.makeRedoIcon(),
|
156
154
|
}, () => {
|
157
155
|
this.editor.history.redo();
|
158
|
-
}
|
159
|
-
|
160
|
-
|
161
|
-
redoButton.disabled = true;
|
156
|
+
});
|
157
|
+
undoButton.setDisabled(true);
|
158
|
+
redoButton.setDisabled(true);
|
162
159
|
this.editor.notifier.on(EditorEventType.UndoRedoStackUpdated, event => {
|
163
160
|
if (event.kind !== EditorEventType.UndoRedoStackUpdated) {
|
164
161
|
throw new Error('Wrong event type!');
|
165
162
|
}
|
166
|
-
undoButton.
|
167
|
-
redoButton.
|
163
|
+
undoButton.setDisabled(event.undoStackSize === 0);
|
164
|
+
redoButton.setDisabled(event.redoStackSize === 0);
|
168
165
|
});
|
169
166
|
}
|
170
167
|
addDefaultToolWidgets() {
|
@@ -2,7 +2,7 @@ import Color4 from '../Color4';
|
|
2
2
|
import { ComponentBuilderFactory } from '../components/builders/types';
|
3
3
|
import { TextStyle } from '../components/TextComponent';
|
4
4
|
import Pen from '../tools/Pen';
|
5
|
-
type IconType = SVGSVGElement | HTMLImageElement;
|
5
|
+
declare type IconType = SVGSVGElement | HTMLImageElement;
|
6
6
|
export default class IconProvider {
|
7
7
|
makeUndoIcon(): IconType;
|
8
8
|
makeRedoIcon(mirror?: boolean): IconType;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
2
|
import Editor from '../Editor';
|
3
|
-
type OnColorChangeListener = (color: Color4) => void;
|
4
|
-
type SetColorCallback = (color: Color4 | string) => void;
|
3
|
+
declare type OnColorChangeListener = (color: Color4) => void;
|
4
|
+
declare type SetColorCallback = (color: Color4 | string) => void;
|
5
5
|
export declare const makeColorInput: (editor: Editor, onColorChange: OnColorChangeListener) => [HTMLInputElement, HTMLElement, SetColorCallback];
|
6
6
|
export default makeColorInput;
|
@@ -2,12 +2,12 @@ import Editor from '../../Editor';
|
|
2
2
|
import { ToolbarLocalization } from '../localization';
|
3
3
|
import BaseWidget from './BaseWidget';
|
4
4
|
export default class ActionButtonWidget extends BaseWidget {
|
5
|
-
protected makeIcon: () => Element;
|
5
|
+
protected makeIcon: () => Element | null;
|
6
6
|
protected title: string;
|
7
7
|
protected clickAction: () => void;
|
8
|
-
constructor(editor: Editor, id: string, makeIcon: () => Element, title: string, clickAction: () => void, localizationTable?: ToolbarLocalization);
|
8
|
+
constructor(editor: Editor, id: string, makeIcon: () => Element | null, title: string, clickAction: () => void, localizationTable?: ToolbarLocalization);
|
9
9
|
protected handleClick(): void;
|
10
10
|
protected getTitle(): string;
|
11
|
-
protected createIcon(): Element;
|
11
|
+
protected createIcon(): Element | null;
|
12
12
|
protected fillDropdown(_dropdown: HTMLElement): boolean;
|
13
13
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import Editor from '../../Editor';
|
2
2
|
import { KeyPressEvent } from '../../types';
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
|
-
export type SavedToolbuttonState = Record<string, any>;
|
4
|
+
export declare type SavedToolbuttonState = Record<string, any>;
|
5
5
|
export default abstract class BaseWidget {
|
6
6
|
#private;
|
7
7
|
protected editor: Editor;
|
@@ -29,7 +29,7 @@ export default abstract class BaseWidget {
|
|
29
29
|
*/
|
30
30
|
getUniqueIdIn(container: Record<string, BaseWidget>): string;
|
31
31
|
protected abstract getTitle(): string;
|
32
|
-
protected abstract createIcon(): Element;
|
32
|
+
protected abstract createIcon(): Element | null;
|
33
33
|
protected fillDropdown(dropdown: HTMLElement): boolean;
|
34
34
|
protected setupActionBtnClickListener(button: HTMLElement): void;
|
35
35
|
protected onKeyPress(_event: KeyPressEvent): boolean;
|
@@ -156,11 +156,16 @@ export default class BaseWidget {
|
|
156
156
|
parent.appendChild(this.container);
|
157
157
|
}
|
158
158
|
updateIcon() {
|
159
|
-
var _a;
|
159
|
+
var _a, _b;
|
160
160
|
const newIcon = this.createIcon();
|
161
|
-
|
162
|
-
|
163
|
-
|
161
|
+
if (newIcon) {
|
162
|
+
(_a = this.icon) === null || _a === void 0 ? void 0 : _a.replaceWith(newIcon);
|
163
|
+
this.icon = newIcon;
|
164
|
+
this.icon.classList.add(`${toolbarCSSPrefix}icon`);
|
165
|
+
}
|
166
|
+
else {
|
167
|
+
(_b = this.icon) === null || _b === void 0 ? void 0 : _b.remove();
|
168
|
+
}
|
164
169
|
}
|
165
170
|
setDisabled(disabled) {
|
166
171
|
this.disabled = disabled;
|