js-draw 0.17.2 → 0.17.4
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 +10 -1
- package/README.md +17 -8
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +62 -1
- package/dist/src/Editor.js +75 -24
- package/dist/src/EditorImage.d.ts +4 -2
- package/dist/src/EditorImage.js +4 -2
- package/dist/src/SVGLoader.d.ts +4 -0
- package/dist/src/SVGLoader.js +4 -0
- package/dist/src/components/lib.d.ts +2 -2
- package/dist/src/components/lib.js +2 -2
- package/dist/src/lib.d.ts +2 -1
- package/dist/src/lib.js +2 -1
- package/dist/src/rendering/lib.d.ts +2 -0
- package/dist/src/rendering/lib.js +2 -0
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +25 -0
- package/dist/src/rendering/renderers/CanvasRenderer.js +27 -0
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +15 -0
- package/dist/src/rendering/renderers/SVGRenderer.js +27 -1
- package/dist/src/testing/lib.d.ts +2 -0
- package/dist/src/testing/lib.js +2 -0
- package/dist/src/testing/sendPenEvent.d.ts +12 -0
- package/dist/src/testing/sendPenEvent.js +19 -0
- package/dist/src/testing/sendTouchEvent.d.ts +36 -0
- package/dist/src/testing/sendTouchEvent.js +36 -0
- package/dist/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +0 -1
- package/dist/src/toolbar/widgets/DocumentPropertiesWidget.js +5 -20
- package/dist/src/toolbar/widgets/PenToolWidget.js +1 -0
- package/dist/src/toolbar/widgets/TextToolWidget.js +4 -1
- package/dist/src/tools/SelectionTool/SelectionTool.js +5 -6
- package/package.json +1 -1
- package/src/Editor.ts +78 -28
- package/src/EditorImage.ts +4 -2
- package/src/SVGLoader.ts +4 -0
- package/src/components/lib.ts +2 -1
- package/src/lib.ts +2 -1
- package/src/rendering/lib.ts +2 -0
- package/src/rendering/renderers/CanvasRenderer.ts +27 -0
- package/src/rendering/renderers/SVGRenderer.ts +32 -1
- package/src/testing/lib.ts +3 -0
- package/src/testing/sendPenEvent.ts +31 -0
- package/src/testing/sendTouchEvent.ts +36 -1
- package/src/toolbar/toolbar.css +5 -0
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +5 -23
- package/src/toolbar/widgets/PenToolWidget.ts +1 -0
- package/src/toolbar/widgets/TextToolWidget.ts +4 -1
- package/src/tools/Eraser.test.ts +11 -10
- package/src/tools/PanZoom.test.ts +1 -1
- package/src/tools/Pen.test.ts +63 -62
- package/src/tools/SelectionTool/SelectionTool.test.ts +15 -14
- package/src/tools/SelectionTool/SelectionTool.ts +5 -7
package/dist/src/Editor.d.ts
CHANGED
@@ -46,6 +46,9 @@ export interface EditorSettings {
|
|
46
46
|
* // Do something with saveData...
|
47
47
|
* });
|
48
48
|
* ```
|
49
|
+
*
|
50
|
+
* See also
|
51
|
+
* [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/example/example.ts#L15).
|
49
52
|
*/
|
50
53
|
export declare class Editor {
|
51
54
|
private container;
|
@@ -123,6 +126,8 @@ export declare class Editor {
|
|
123
126
|
*
|
124
127
|
* // Add the default toolbar
|
125
128
|
* const toolbar = editor.addToolbar();
|
129
|
+
*
|
130
|
+
* // Add a save button
|
126
131
|
* toolbar.addActionButton({
|
127
132
|
* label: 'Save'
|
128
133
|
* icon: createSaveIcon(),
|
@@ -138,6 +143,7 @@ export declare class Editor {
|
|
138
143
|
*
|
139
144
|
* @example
|
140
145
|
* ```
|
146
|
+
* // Set the editor's height to 500px
|
141
147
|
* editor.getRootElement().style.height = '500px';
|
142
148
|
* ```
|
143
149
|
*/
|
@@ -146,6 +152,10 @@ export declare class Editor {
|
|
146
152
|
showLoadingWarning(fractionLoaded: number): void;
|
147
153
|
hideLoadingWarning(): void;
|
148
154
|
private previousAccessibilityAnnouncement;
|
155
|
+
/**
|
156
|
+
* Announce `message` for screen readers. If `message` is the same as the previous
|
157
|
+
* message, it is re-announced.
|
158
|
+
*/
|
149
159
|
announceForAccessibility(message: string): void;
|
150
160
|
/**
|
151
161
|
* Creates a toolbar. If `defaultLayout` is true, default buttons are used.
|
@@ -162,6 +172,25 @@ export declare class Editor {
|
|
162
172
|
handleHTMLPointerEvent(eventType: 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel', evt: PointerEvent): boolean;
|
163
173
|
private isEventSink;
|
164
174
|
private handlePaste;
|
175
|
+
/**
|
176
|
+
* Forward pointer events from `elem` to this editor. Such that right-click/right-click drag
|
177
|
+
* events are also forwarded, `elem`'s contextmenu is disabled.
|
178
|
+
*
|
179
|
+
* @example
|
180
|
+
* ```ts
|
181
|
+
* const overlay = document.createElement('div');
|
182
|
+
* editor.createHTMLOverlay(overlay);
|
183
|
+
*
|
184
|
+
* // Send all pointer events that don't have the control key pressed
|
185
|
+
* // to the editor.
|
186
|
+
* editor.handlePointerEventsFrom(overlay, (event) => {
|
187
|
+
* if (event.ctrlKey) {
|
188
|
+
* return false;
|
189
|
+
* }
|
190
|
+
* return true;
|
191
|
+
* });
|
192
|
+
* ```
|
193
|
+
*/
|
165
194
|
handlePointerEventsFrom(elem: HTMLElement, filter?: HTMLPointerEventFilter): void;
|
166
195
|
/** Adds event listners for keypresses to `elem` and forwards those events to the editor. */
|
167
196
|
handleKeyEventsFrom(elem: HTMLElement): void;
|
@@ -200,25 +229,52 @@ export declare class Editor {
|
|
200
229
|
* Schedule a re-render for some time in the near future. Does not schedule an additional
|
201
230
|
* re-render if a re-render is already queued.
|
202
231
|
*
|
203
|
-
* @returns a promise that resolves when
|
232
|
+
* @returns a promise that resolves when re-rendering has completed.
|
204
233
|
*/
|
205
234
|
queueRerender(): Promise<void>;
|
206
235
|
isRerenderQueued(): boolean;
|
236
|
+
/**
|
237
|
+
* Re-renders the entire image.
|
238
|
+
*
|
239
|
+
* @see {@link Editor.queueRerender}
|
240
|
+
*/
|
207
241
|
rerender(showImageBounds?: boolean): void;
|
208
242
|
/**
|
243
|
+
* Draws the given path onto the wet ink renderer. The given path will
|
244
|
+
* be displayed on top of the main image.
|
245
|
+
*
|
209
246
|
* @see {@link Display.getWetInkRenderer} {@link Display.flatten}
|
210
247
|
*/
|
211
248
|
drawWetInk(...path: RenderablePathSpec[]): void;
|
212
249
|
/**
|
250
|
+
* Clears the wet ink display.
|
251
|
+
*
|
213
252
|
* @see {@link Display.getWetInkRenderer}
|
214
253
|
*/
|
215
254
|
clearWetInk(): void;
|
255
|
+
/**
|
256
|
+
* Focuses the region used for text input/key commands.
|
257
|
+
*/
|
216
258
|
focus(): void;
|
259
|
+
/**
|
260
|
+
* Creates an element that will be positioned on top of the dry/wet ink
|
261
|
+
* renderers.
|
262
|
+
*
|
263
|
+
* This is useful for displaying content on top of the rendered content
|
264
|
+
* (e.g. a selection box).
|
265
|
+
*/
|
217
266
|
createHTMLOverlay(overlay: HTMLElement): {
|
218
267
|
remove: () => void;
|
219
268
|
};
|
220
269
|
addStyleSheet(content: string): HTMLStyleElement;
|
221
270
|
sendKeyboardEvent(eventType: InputEvtType.KeyPressEvent | InputEvtType.KeyUpEvent, key: string, ctrlKey?: boolean, altKey?: boolean): void;
|
271
|
+
/**
|
272
|
+
* Dispatch a pen event to the currently selected tool.
|
273
|
+
* Intended primarially for unit tests.
|
274
|
+
*
|
275
|
+
* @deprecated
|
276
|
+
* @see {@link sendPenEvent} {@link sendTouchEvent}
|
277
|
+
*/
|
222
278
|
sendPenEvent(eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]): void;
|
223
279
|
addAndCenterComponents(components: AbstractComponent[], selectComponents?: boolean): Promise<void>;
|
224
280
|
toDataURL(format?: 'image/png' | 'image/jpeg' | 'image/webp'): string;
|
@@ -234,6 +290,11 @@ export declare class Editor {
|
|
234
290
|
* Set the background color of the image.
|
235
291
|
*/
|
236
292
|
setBackgroundColor(color: Color4): Command;
|
293
|
+
/**
|
294
|
+
* @returns the average of the colors of all background components. Use this to get the current background
|
295
|
+
* color.
|
296
|
+
*/
|
297
|
+
estimateBackgroundColor(): Color4;
|
237
298
|
getImportExportRect(): Rect2;
|
238
299
|
setImportExportRect(imageRect: Rect2): Command;
|
239
300
|
/**
|
package/dist/src/Editor.js
CHANGED
@@ -32,6 +32,7 @@ import uniteCommands from './commands/uniteCommands';
|
|
32
32
|
import SelectionTool from './tools/SelectionTool/SelectionTool';
|
33
33
|
import Erase from './commands/Erase';
|
34
34
|
import ImageBackground, { BackgroundType } from './components/ImageBackground';
|
35
|
+
import sendPenEvent from './testing/sendPenEvent';
|
35
36
|
/**
|
36
37
|
* The main entrypoint for the full editor.
|
37
38
|
*
|
@@ -46,6 +47,9 @@ import ImageBackground, { BackgroundType } from './components/ImageBackground';
|
|
46
47
|
* // Do something with saveData...
|
47
48
|
* });
|
48
49
|
* ```
|
50
|
+
*
|
51
|
+
* See also
|
52
|
+
* [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/example/example.ts#L15).
|
49
53
|
*/
|
50
54
|
export class Editor {
|
51
55
|
/**
|
@@ -62,6 +66,8 @@ export class Editor {
|
|
62
66
|
*
|
63
67
|
* // Add the default toolbar
|
64
68
|
* const toolbar = editor.addToolbar();
|
69
|
+
*
|
70
|
+
* // Add a save button
|
65
71
|
* toolbar.addActionButton({
|
66
72
|
* label: 'Save'
|
67
73
|
* icon: createSaveIcon(),
|
@@ -156,6 +162,7 @@ export class Editor {
|
|
156
162
|
*
|
157
163
|
* @example
|
158
164
|
* ```
|
165
|
+
* // Set the editor's height to 500px
|
159
166
|
* editor.getRootElement().style.height = '500px';
|
160
167
|
* ```
|
161
168
|
*/
|
@@ -172,8 +179,10 @@ export class Editor {
|
|
172
179
|
this.loadingWarning.style.display = 'none';
|
173
180
|
this.announceForAccessibility(this.localization.doneLoading);
|
174
181
|
}
|
175
|
-
|
176
|
-
|
182
|
+
/**
|
183
|
+
* Announce `message` for screen readers. If `message` is the same as the previous
|
184
|
+
* message, it is re-announced.
|
185
|
+
*/
|
177
186
|
announceForAccessibility(message) {
|
178
187
|
// Force re-announcing an announcement if announced again.
|
179
188
|
if (message === this.previousAccessibilityAnnouncement) {
|
@@ -424,6 +433,25 @@ export class Editor {
|
|
424
433
|
}
|
425
434
|
});
|
426
435
|
}
|
436
|
+
/**
|
437
|
+
* Forward pointer events from `elem` to this editor. Such that right-click/right-click drag
|
438
|
+
* events are also forwarded, `elem`'s contextmenu is disabled.
|
439
|
+
*
|
440
|
+
* @example
|
441
|
+
* ```ts
|
442
|
+
* const overlay = document.createElement('div');
|
443
|
+
* editor.createHTMLOverlay(overlay);
|
444
|
+
*
|
445
|
+
* // Send all pointer events that don't have the control key pressed
|
446
|
+
* // to the editor.
|
447
|
+
* editor.handlePointerEventsFrom(overlay, (event) => {
|
448
|
+
* if (event.ctrlKey) {
|
449
|
+
* return false;
|
450
|
+
* }
|
451
|
+
* return true;
|
452
|
+
* });
|
453
|
+
* ```
|
454
|
+
*/
|
427
455
|
handlePointerEventsFrom(elem, filter) {
|
428
456
|
// May be required to prevent text selection on iOS/Safari:
|
429
457
|
// See https://stackoverflow.com/a/70992717/17055750
|
@@ -560,7 +588,7 @@ export class Editor {
|
|
560
588
|
* Schedule a re-render for some time in the near future. Does not schedule an additional
|
561
589
|
* re-render if a re-render is already queued.
|
562
590
|
*
|
563
|
-
* @returns a promise that resolves when
|
591
|
+
* @returns a promise that resolves when re-rendering has completed.
|
564
592
|
*/
|
565
593
|
queueRerender() {
|
566
594
|
if (!this.rerenderQueued) {
|
@@ -582,6 +610,11 @@ export class Editor {
|
|
582
610
|
isRerenderQueued() {
|
583
611
|
return this.rerenderQueued;
|
584
612
|
}
|
613
|
+
/**
|
614
|
+
* Re-renders the entire image.
|
615
|
+
*
|
616
|
+
* @see {@link Editor.queueRerender}
|
617
|
+
*/
|
585
618
|
rerender(showImageBounds = true) {
|
586
619
|
this.display.startRerender();
|
587
620
|
// Don't render if the display has zero size.
|
@@ -601,6 +634,9 @@ export class Editor {
|
|
601
634
|
this.nextRerenderListeners = [];
|
602
635
|
}
|
603
636
|
/**
|
637
|
+
* Draws the given path onto the wet ink renderer. The given path will
|
638
|
+
* be displayed on top of the main image.
|
639
|
+
*
|
604
640
|
* @see {@link Display.getWetInkRenderer} {@link Display.flatten}
|
605
641
|
*/
|
606
642
|
drawWetInk(...path) {
|
@@ -609,17 +645,26 @@ export class Editor {
|
|
609
645
|
}
|
610
646
|
}
|
611
647
|
/**
|
648
|
+
* Clears the wet ink display.
|
649
|
+
*
|
612
650
|
* @see {@link Display.getWetInkRenderer}
|
613
651
|
*/
|
614
652
|
clearWetInk() {
|
615
653
|
this.display.getWetInkRenderer().clear();
|
616
654
|
}
|
617
|
-
|
655
|
+
/**
|
656
|
+
* Focuses the region used for text input/key commands.
|
657
|
+
*/
|
618
658
|
focus() {
|
619
659
|
this.renderingRegion.focus();
|
620
660
|
}
|
621
|
-
|
622
|
-
|
661
|
+
/**
|
662
|
+
* Creates an element that will be positioned on top of the dry/wet ink
|
663
|
+
* renderers.
|
664
|
+
*
|
665
|
+
* This is useful for displaying content on top of the rendered content
|
666
|
+
* (e.g. a selection box).
|
667
|
+
*/
|
623
668
|
createHTMLOverlay(overlay) {
|
624
669
|
overlay.classList.add('overlay');
|
625
670
|
this.container.appendChild(overlay);
|
@@ -643,19 +688,17 @@ export class Editor {
|
|
643
688
|
altKey,
|
644
689
|
});
|
645
690
|
}
|
646
|
-
|
647
|
-
|
691
|
+
/**
|
692
|
+
* Dispatch a pen event to the currently selected tool.
|
693
|
+
* Intended primarially for unit tests.
|
694
|
+
*
|
695
|
+
* @deprecated
|
696
|
+
* @see {@link sendPenEvent} {@link sendTouchEvent}
|
697
|
+
*/
|
648
698
|
sendPenEvent(eventType, point,
|
649
699
|
// @deprecated
|
650
700
|
allPointers) {
|
651
|
-
|
652
|
-
this.toolController.dispatchInputEvent({
|
653
|
-
kind: eventType,
|
654
|
-
allPointers: allPointers !== null && allPointers !== void 0 ? allPointers : [
|
655
|
-
mainPointer,
|
656
|
-
],
|
657
|
-
current: mainPointer,
|
658
|
-
});
|
701
|
+
sendPenEvent(this, eventType, point, allPointers);
|
659
702
|
}
|
660
703
|
addAndCenterComponents(components, selectComponents = true) {
|
661
704
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -717,9 +760,8 @@ export class Editor {
|
|
717
760
|
}
|
718
761
|
toSVG() {
|
719
762
|
const importExportViewport = this.image.getImportExportViewport().getTemporaryClone();
|
720
|
-
const
|
721
|
-
const result =
|
722
|
-
const renderer = new SVGRenderer(result, importExportViewport);
|
763
|
+
const sanitize = false;
|
764
|
+
const { element: result, renderer } = SVGRenderer.fromViewport(importExportViewport, sanitize);
|
723
765
|
const origTransform = importExportViewport.canvasToScreenTransform;
|
724
766
|
// Render with (0,0) at (0,0) — we'll handle translation with
|
725
767
|
// the viewBox property.
|
@@ -731,11 +773,6 @@ export class Editor {
|
|
731
773
|
result.setAttribute('viewBox', [rect.x, rect.y, rect.w, rect.h].map(part => toRoundedString(part)).join(' '));
|
732
774
|
result.setAttribute('width', toRoundedString(rect.w));
|
733
775
|
result.setAttribute('height', toRoundedString(rect.h));
|
734
|
-
// Ensure the image can be identified as an SVG if downloaded.
|
735
|
-
// See https://jwatt.org/svg/authoring/
|
736
|
-
result.setAttribute('version', '1.1');
|
737
|
-
result.setAttribute('baseProfile', 'full');
|
738
|
-
result.setAttribute('xmlns', svgNameSpace);
|
739
776
|
return result;
|
740
777
|
}
|
741
778
|
/**
|
@@ -799,6 +836,20 @@ export class Editor {
|
|
799
836
|
return background.updateStyle({ color });
|
800
837
|
}
|
801
838
|
}
|
839
|
+
/**
|
840
|
+
* @returns the average of the colors of all background components. Use this to get the current background
|
841
|
+
* color.
|
842
|
+
*/
|
843
|
+
estimateBackgroundColor() {
|
844
|
+
var _a;
|
845
|
+
const backgroundColors = [];
|
846
|
+
for (const component of this.image.getBackgroundComponents()) {
|
847
|
+
if (component instanceof ImageBackground) {
|
848
|
+
backgroundColors.push((_a = component.getStyle().color) !== null && _a !== void 0 ? _a : Color4.transparent);
|
849
|
+
}
|
850
|
+
}
|
851
|
+
return Color4.average(backgroundColors);
|
852
|
+
}
|
802
853
|
// Returns the size of the visible region of the output SVG
|
803
854
|
getImportExportRect() {
|
804
855
|
return this.image.getImportExportViewport().visibleRect;
|
@@ -26,8 +26,10 @@ export default class EditorImage {
|
|
26
26
|
/** @internal */
|
27
27
|
renderWithCache(screenRenderer: AbstractRenderer, cache: RenderingCache, viewport: Viewport): void;
|
28
28
|
/**
|
29
|
-
* Renders all nodes visible from `viewport` (or all nodes if `viewport = null`)
|
30
|
-
*
|
29
|
+
* Renders all nodes visible from `viewport` (or all nodes if `viewport = null`).
|
30
|
+
*
|
31
|
+
* `viewport` is used to improve rendering performance. If given, it must match
|
32
|
+
* the viewport used by the `renderer` (if any).
|
31
33
|
*/
|
32
34
|
render(renderer: AbstractRenderer, viewport: Viewport | null): void;
|
33
35
|
/** Renders all nodes, even ones not within the viewport. @internal */
|
package/dist/src/EditorImage.js
CHANGED
@@ -67,8 +67,10 @@ export default class EditorImage {
|
|
67
67
|
cache.render(screenRenderer, this.root, viewport);
|
68
68
|
}
|
69
69
|
/**
|
70
|
-
* Renders all nodes visible from `viewport` (or all nodes if `viewport = null`)
|
71
|
-
*
|
70
|
+
* Renders all nodes visible from `viewport` (or all nodes if `viewport = null`).
|
71
|
+
*
|
72
|
+
* `viewport` is used to improve rendering performance. If given, it must match
|
73
|
+
* the viewport used by the `renderer` (if any).
|
72
74
|
*/
|
73
75
|
render(renderer, viewport) {
|
74
76
|
this.background.render(renderer, viewport === null || viewport === void 0 ? void 0 : viewport.visibleRect);
|
package/dist/src/SVGLoader.d.ts
CHANGED
@@ -36,6 +36,10 @@ export default class SVGLoader implements ImageLoader {
|
|
36
36
|
private getSourceAttrs;
|
37
37
|
start(onAddComponent: ComponentAddedListener, onProgress: OnProgressListener, onDetermineExportRect?: OnDetermineExportRectListener | null): Promise<void>;
|
38
38
|
/**
|
39
|
+
* Create an `SVGLoader` from the content of an SVG image. SVGs are loaded within a sandboxed
|
40
|
+
* iframe with `sandbox="allow-same-origin"`
|
41
|
+
* [thereby disabling JavaScript](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox).
|
42
|
+
*
|
39
43
|
* @see {@link Editor.loadFrom}
|
40
44
|
* @param text - Textual representation of the SVG (e.g. `<svg viewbox='...'>...</svg>`).
|
41
45
|
* @param sanitize - if `true`, don't store unknown attributes.
|
package/dist/src/SVGLoader.js
CHANGED
@@ -387,6 +387,10 @@ export default class SVGLoader {
|
|
387
387
|
});
|
388
388
|
}
|
389
389
|
/**
|
390
|
+
* Create an `SVGLoader` from the content of an SVG image. SVGs are loaded within a sandboxed
|
391
|
+
* iframe with `sandbox="allow-same-origin"`
|
392
|
+
* [thereby disabling JavaScript](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox).
|
393
|
+
*
|
390
394
|
* @see {@link Editor.loadFrom}
|
391
395
|
* @param text - Textual representation of the SVG (e.g. `<svg viewbox='...'>...</svg>`).
|
392
396
|
* @param sanitize - if `true`, don't store unknown attributes.
|
@@ -7,6 +7,6 @@ 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
|
-
import RestyleableComponent, { createRestyleComponentCommand } from './RestylableComponent';
|
10
|
+
import RestyleableComponent, { createRestyleComponentCommand, isRestylableComponent } from './RestylableComponent';
|
11
11
|
import ImageBackground from './ImageBackground';
|
12
|
-
export { Stroke, TextComponent as Text, RestyleableComponent, createRestyleComponentCommand, TextComponent, Stroke as StrokeComponent, ImageBackground as BackgroundComponent, ImageComponent, };
|
12
|
+
export { Stroke, TextComponent as Text, RestyleableComponent, createRestyleComponentCommand, isRestylableComponent, TextComponent, Stroke as StrokeComponent, ImageBackground as BackgroundComponent, ImageComponent, };
|
@@ -7,6 +7,6 @@ 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
|
-
import { createRestyleComponentCommand } from './RestylableComponent';
|
10
|
+
import { createRestyleComponentCommand, isRestylableComponent } from './RestylableComponent';
|
11
11
|
import ImageBackground from './ImageBackground';
|
12
|
-
export { Stroke, TextComponent as Text, createRestyleComponentCommand, TextComponent, Stroke as StrokeComponent, ImageBackground as BackgroundComponent, ImageComponent, };
|
12
|
+
export { Stroke, TextComponent as Text, createRestyleComponentCommand, isRestylableComponent, TextComponent, Stroke as StrokeComponent, ImageBackground as BackgroundComponent, ImageComponent, };
|
package/dist/src/lib.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* The main entrypoint for the NPM package. Everything exported by this file
|
3
|
-
* is available through the `js-draw` package.
|
3
|
+
* is available through the [`js-draw` package](https://www.npmjs.com/package/js-draw).
|
4
4
|
*
|
5
5
|
* @example
|
6
6
|
* ```
|
@@ -26,6 +26,7 @@ export * from './commands/lib';
|
|
26
26
|
export * from './tools/lib';
|
27
27
|
export * from './toolbar/lib';
|
28
28
|
export * from './rendering/lib';
|
29
|
+
export * from './testing/lib';
|
29
30
|
export { default as Pointer, PointerDevice } from './Pointer';
|
30
31
|
export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
|
31
32
|
export { default as UndoRedoHistory } from './UndoRedoHistory';
|
package/dist/src/lib.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* The main entrypoint for the NPM package. Everything exported by this file
|
3
|
-
* is available through the `js-draw` package.
|
3
|
+
* is available through the [`js-draw` package](https://www.npmjs.com/package/js-draw).
|
4
4
|
*
|
5
5
|
* @example
|
6
6
|
* ```
|
@@ -26,6 +26,7 @@ export * from './commands/lib';
|
|
26
26
|
export * from './tools/lib';
|
27
27
|
export * from './toolbar/lib';
|
28
28
|
export * from './rendering/lib';
|
29
|
+
export * from './testing/lib';
|
29
30
|
export { default as Pointer, PointerDevice } from './Pointer';
|
30
31
|
export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
|
31
32
|
export { default as UndoRedoHistory } from './UndoRedoHistory';
|
@@ -1,3 +1,5 @@
|
|
1
1
|
export { default as AbstractRenderer } from './renderers/AbstractRenderer';
|
2
2
|
export { default as DummyRenderer } from './renderers/DummyRenderer';
|
3
|
+
export { default as SVGRenderer } from './renderers/SVGRenderer';
|
4
|
+
export { default as CanvasRenderer } from './renderers/CanvasRenderer';
|
3
5
|
export { default as Display } from './Display';
|
@@ -1,3 +1,5 @@
|
|
1
1
|
export { default as AbstractRenderer } from './renderers/AbstractRenderer';
|
2
2
|
export { default as DummyRenderer } from './renderers/DummyRenderer';
|
3
|
+
export { default as SVGRenderer } from './renderers/SVGRenderer';
|
4
|
+
export { default as CanvasRenderer } from './renderers/CanvasRenderer';
|
3
5
|
export { default as Display } from './Display';
|
@@ -6,6 +6,26 @@ import Viewport from '../../Viewport';
|
|
6
6
|
import RenderingStyle from '../RenderingStyle';
|
7
7
|
import TextStyle from '../TextRenderingStyle';
|
8
8
|
import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
|
9
|
+
/**
|
10
|
+
* Renders onto a `CanvasRenderingContext2D`.
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```ts
|
14
|
+
* const editor = new Editor(document.body);
|
15
|
+
*
|
16
|
+
* const canvas = document.createElement('canvas');
|
17
|
+
* const ctx = canvas.getContext('2d');
|
18
|
+
*
|
19
|
+
* // Ensure that the canvas can fit the entire rendering
|
20
|
+
* const viewport = editor.image.getImportExportViewport();
|
21
|
+
* canvas.width = viewport.getScreenRectSize().x;
|
22
|
+
* canvas.height = viewport.getScreenRectSize().y;
|
23
|
+
*
|
24
|
+
* // Render editor.image onto the renderer
|
25
|
+
* const renderer = new CanvasRenderer(ctx, viewport);
|
26
|
+
* editor.image.render(renderer, viewport);
|
27
|
+
* ```
|
28
|
+
*/
|
9
29
|
export default class CanvasRenderer extends AbstractRenderer {
|
10
30
|
private ctx;
|
11
31
|
private ignoreObjectsAboveLevel;
|
@@ -14,6 +34,11 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
14
34
|
private minSquareCurveApproxDist;
|
15
35
|
private minRenderSizeAnyDimen;
|
16
36
|
private minRenderSizeBothDimens;
|
37
|
+
/**
|
38
|
+
* Creates a new `CanvasRenderer` that renders to the given rendering context.
|
39
|
+
* The `viewport` is used to determine the translation/rotation/scaling of the content
|
40
|
+
* to draw.
|
41
|
+
*/
|
17
42
|
constructor(ctx: CanvasRenderingContext2D, viewport: Viewport);
|
18
43
|
private transformBy;
|
19
44
|
canRenderFromWithoutDataLoss(other: AbstractRenderer): boolean;
|
@@ -3,7 +3,32 @@ import TextComponent from '../../components/TextComponent';
|
|
3
3
|
import Path from '../../math/Path';
|
4
4
|
import { Vec2 } from '../../math/Vec2';
|
5
5
|
import AbstractRenderer from './AbstractRenderer';
|
6
|
+
/**
|
7
|
+
* Renders onto a `CanvasRenderingContext2D`.
|
8
|
+
*
|
9
|
+
* @example
|
10
|
+
* ```ts
|
11
|
+
* const editor = new Editor(document.body);
|
12
|
+
*
|
13
|
+
* const canvas = document.createElement('canvas');
|
14
|
+
* const ctx = canvas.getContext('2d');
|
15
|
+
*
|
16
|
+
* // Ensure that the canvas can fit the entire rendering
|
17
|
+
* const viewport = editor.image.getImportExportViewport();
|
18
|
+
* canvas.width = viewport.getScreenRectSize().x;
|
19
|
+
* canvas.height = viewport.getScreenRectSize().y;
|
20
|
+
*
|
21
|
+
* // Render editor.image onto the renderer
|
22
|
+
* const renderer = new CanvasRenderer(ctx, viewport);
|
23
|
+
* editor.image.render(renderer, viewport);
|
24
|
+
* ```
|
25
|
+
*/
|
6
26
|
export default class CanvasRenderer extends AbstractRenderer {
|
27
|
+
/**
|
28
|
+
* Creates a new `CanvasRenderer` that renders to the given rendering context.
|
29
|
+
* The `viewport` is used to determine the translation/rotation/scaling of the content
|
30
|
+
* to draw.
|
31
|
+
*/
|
7
32
|
constructor(ctx, viewport) {
|
8
33
|
super(viewport);
|
9
34
|
this.ctx = ctx;
|
@@ -175,6 +200,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
175
200
|
this.ignoringObject = false;
|
176
201
|
}
|
177
202
|
}
|
203
|
+
// @internal
|
178
204
|
drawPoints(...points) {
|
179
205
|
const pointRadius = 10;
|
180
206
|
for (let i = 0; i < points.length; i++) {
|
@@ -191,6 +217,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
191
217
|
this.ctx.fillText(`${i}`, point.x, point.y, pointRadius * 2);
|
192
218
|
}
|
193
219
|
}
|
220
|
+
// @internal
|
194
221
|
isTooSmallToRender(rect) {
|
195
222
|
// Should we ignore all objects within this object's bbox?
|
196
223
|
const diagonal = this.getCanvasToScreenTransform().transformVec3(rect.size);
|
@@ -7,6 +7,11 @@ import RenderingStyle from '../RenderingStyle';
|
|
7
7
|
import TextStyle from '../TextRenderingStyle';
|
8
8
|
import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
|
9
9
|
export declare const renderedStylesheetId = "js-draw-style-sheet";
|
10
|
+
/**
|
11
|
+
* Renders onto an `SVGElement`.
|
12
|
+
*
|
13
|
+
* @see {@link Editor.toSVG}
|
14
|
+
*/
|
10
15
|
export default class SVGRenderer extends AbstractRenderer {
|
11
16
|
private elem;
|
12
17
|
private sanitize;
|
@@ -14,6 +19,12 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
14
19
|
private lastPathString;
|
15
20
|
private objectElems;
|
16
21
|
private overwrittenAttrs;
|
22
|
+
/**
|
23
|
+
* Creates a renderer that renders onto `elem`. If `sanitize`, don't render potentially untrusted data.
|
24
|
+
*
|
25
|
+
* `viewport` is used to determine the translation/rotation/scaling/output size of the rendered
|
26
|
+
* data.
|
27
|
+
*/
|
17
28
|
constructor(elem: SVGSVGElement, viewport: Viewport, sanitize?: boolean);
|
18
29
|
private addStyleSheet;
|
19
30
|
setRootSVGAttribute(name: string, value: string | null): void;
|
@@ -39,4 +50,8 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
39
50
|
drawPoints(...points: Point2[]): void;
|
40
51
|
drawSVGElem(elem: SVGElement): void;
|
41
52
|
isTooSmallToRender(_rect: Rect2): boolean;
|
53
|
+
static fromViewport(viewport: Viewport, sanitize?: boolean): {
|
54
|
+
element: SVGSVGElement;
|
55
|
+
renderer: SVGRenderer;
|
56
|
+
};
|
42
57
|
}
|
@@ -6,8 +6,18 @@ import { svgAttributesDataKey, svgStyleAttributesDataKey } from '../../SVGLoader
|
|
6
6
|
import AbstractRenderer from './AbstractRenderer';
|
7
7
|
export const renderedStylesheetId = 'js-draw-style-sheet';
|
8
8
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
9
|
+
/**
|
10
|
+
* Renders onto an `SVGElement`.
|
11
|
+
*
|
12
|
+
* @see {@link Editor.toSVG}
|
13
|
+
*/
|
9
14
|
export default class SVGRenderer extends AbstractRenderer {
|
10
|
-
|
15
|
+
/**
|
16
|
+
* Creates a renderer that renders onto `elem`. If `sanitize`, don't render potentially untrusted data.
|
17
|
+
*
|
18
|
+
* `viewport` is used to determine the translation/rotation/scaling/output size of the rendered
|
19
|
+
* data.
|
20
|
+
*/
|
11
21
|
constructor(elem, viewport, sanitize = false) {
|
12
22
|
super(viewport);
|
13
23
|
this.elem = elem;
|
@@ -275,4 +285,20 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
275
285
|
isTooSmallToRender(_rect) {
|
276
286
|
return false;
|
277
287
|
}
|
288
|
+
// Creates a new SVG element and SVGRenerer with attributes set for the given Viewport.
|
289
|
+
static fromViewport(viewport, sanitize = true) {
|
290
|
+
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
291
|
+
const result = document.createElementNS(svgNameSpace, 'svg');
|
292
|
+
const rect = viewport.getScreenRectSize();
|
293
|
+
// rect.x -> size of rect in x direction, rect.y -> size of rect in y direction.
|
294
|
+
result.setAttribute('viewBox', [0, 0, rect.x, rect.y].map(part => toRoundedString(part)).join(' '));
|
295
|
+
result.setAttribute('width', toRoundedString(rect.x));
|
296
|
+
result.setAttribute('height', toRoundedString(rect.y));
|
297
|
+
// Ensure the image can be identified as an SVG if downloaded.
|
298
|
+
// See https://jwatt.org/svg/authoring/
|
299
|
+
result.setAttribute('version', '1.1');
|
300
|
+
result.setAttribute('baseProfile', 'full');
|
301
|
+
result.setAttribute('xmlns', svgNameSpace);
|
302
|
+
return { element: result, renderer: new SVGRenderer(result, viewport, sanitize) };
|
303
|
+
}
|
278
304
|
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import Editor from '../Editor';
|
2
|
+
import { Point2 } from '../math/Vec2';
|
3
|
+
import Pointer from '../Pointer';
|
4
|
+
import { InputEvtType } from '../types';
|
5
|
+
/**
|
6
|
+
* Dispatch a pen event to the currently selected tool.
|
7
|
+
* Intended for unit tests.
|
8
|
+
*
|
9
|
+
* @see {@link sendTouchEvent}
|
10
|
+
*/
|
11
|
+
declare const sendPenEvent: (editor: Editor, eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]) => void;
|
12
|
+
export default sendPenEvent;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import Pointer from '../Pointer';
|
2
|
+
import { InputEvtType } from '../types';
|
3
|
+
/**
|
4
|
+
* Dispatch a pen event to the currently selected tool.
|
5
|
+
* Intended for unit tests.
|
6
|
+
*
|
7
|
+
* @see {@link sendTouchEvent}
|
8
|
+
*/
|
9
|
+
const sendPenEvent = (editor, eventType, point, allPointers) => {
|
10
|
+
const mainPointer = Pointer.ofCanvasPoint(point, eventType !== InputEvtType.PointerUpEvt, editor.viewport);
|
11
|
+
editor.toolController.dispatchInputEvent({
|
12
|
+
kind: eventType,
|
13
|
+
allPointers: allPointers !== null && allPointers !== void 0 ? allPointers : [
|
14
|
+
mainPointer,
|
15
|
+
],
|
16
|
+
current: mainPointer,
|
17
|
+
});
|
18
|
+
};
|
19
|
+
export default sendPenEvent;
|