js-draw 1.24.1 → 1.25.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +15 -15
- package/dist/bundle.js +1 -1
- package/dist/cjs/Editor.d.ts +12 -0
- package/dist/cjs/Editor.js +1 -0
- package/dist/cjs/commands/invertCommand.test.d.ts +1 -0
- package/dist/cjs/components/TextComponent.d.ts +11 -0
- package/dist/cjs/components/TextComponent.js +14 -3
- package/dist/cjs/testing/fillHtmlInput.d.ts +6 -0
- package/dist/cjs/testing/fillHtmlInput.js +22 -0
- package/dist/cjs/testing/sendKeyPressRelease.d.ts +2 -2
- package/dist/cjs/testing/sendKeyPressRelease.js +15 -3
- package/dist/cjs/tools/PasteHandler.d.ts +1 -1
- package/dist/cjs/tools/PasteHandler.js +12 -4
- package/dist/cjs/tools/PasteHandler.test.d.ts +1 -0
- package/dist/cjs/tools/TextTool.js +4 -0
- package/dist/cjs/util/ClipboardHandler.js +23 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +12 -0
- package/dist/mjs/Editor.mjs +1 -0
- package/dist/mjs/commands/invertCommand.test.d.ts +1 -0
- package/dist/mjs/components/TextComponent.d.ts +11 -0
- package/dist/mjs/components/TextComponent.mjs +14 -3
- package/dist/mjs/testing/fillHtmlInput.d.ts +6 -0
- package/dist/mjs/testing/fillHtmlInput.mjs +17 -0
- package/dist/mjs/testing/sendKeyPressRelease.d.ts +2 -2
- package/dist/mjs/testing/sendKeyPressRelease.mjs +12 -3
- package/dist/mjs/tools/PasteHandler.d.ts +1 -1
- package/dist/mjs/tools/PasteHandler.mjs +12 -4
- package/dist/mjs/tools/PasteHandler.test.d.ts +1 -0
- package/dist/mjs/tools/TextTool.mjs +4 -0
- package/dist/mjs/util/ClipboardHandler.mjs +23 -1
- package/dist/mjs/version.mjs +1 -1
- package/package.json +4 -4
package/dist/cjs/Editor.d.ts
CHANGED
@@ -121,6 +121,18 @@ export interface EditorSettings {
|
|
121
121
|
*/
|
122
122
|
showImagePicker?: ShowCustomFilePickerCallback;
|
123
123
|
} | null;
|
124
|
+
/**
|
125
|
+
* Allows changing how js-draw interacts with the clipboard.
|
126
|
+
*
|
127
|
+
* **Note**: Even when a custom `clipboardApi` is specified, if a `ClipboardEvent` is available
|
128
|
+
* (e.g. from when a user pastes with ctrl+v), the `ClipboardEvent` will be preferred.
|
129
|
+
*/
|
130
|
+
clipboardApi: {
|
131
|
+
/** Called to read data to the clipboard. Keys in the result are MIME types. Values are the data associated with that type. */
|
132
|
+
read(): Promise<Map<string, Blob | string>>;
|
133
|
+
/** Called to write data to the clipboard. Keys in `data` are MIME types. Values are the data associated with that type. */
|
134
|
+
write(data: Map<string, Blob | Promise<Blob> | string>): void | Promise<void>;
|
135
|
+
} | null;
|
124
136
|
}
|
125
137
|
/**
|
126
138
|
* The main entrypoint for the full editor.
|
package/dist/cjs/Editor.js
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -76,6 +76,17 @@ export default class TextComponent extends AbstractComponent implements Restylea
|
|
76
76
|
private static getFontHeight;
|
77
77
|
private computeUntransformedBBoxOfPart;
|
78
78
|
private recomputeBBox;
|
79
|
+
/**
|
80
|
+
* Renders a TextComponent or a TextComponent child onto a `canvas`.
|
81
|
+
*
|
82
|
+
* `visibleRect` can be provided as a performance optimization. If not the top-level
|
83
|
+
* text node, `baseTransform` (specifies the transformation of the parent text component
|
84
|
+
* in canvas space) should also be provided.
|
85
|
+
*
|
86
|
+
* Note that passing a `baseTransform` is preferable to transforming `visibleRect`. At high
|
87
|
+
* zoom levels, transforming `visibleRect` by the inverse of the parent transform can lead to
|
88
|
+
* inaccuracy due to precision loss.
|
89
|
+
*/
|
79
90
|
private renderInternal;
|
80
91
|
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
81
92
|
getProportionalRenderingTime(): number;
|
@@ -154,11 +154,22 @@ class TextComponent extends AbstractComponent_1.default {
|
|
154
154
|
}
|
155
155
|
this.contentBBox = bbox ?? math_1.Rect2.empty;
|
156
156
|
}
|
157
|
-
|
157
|
+
/**
|
158
|
+
* Renders a TextComponent or a TextComponent child onto a `canvas`.
|
159
|
+
*
|
160
|
+
* `visibleRect` can be provided as a performance optimization. If not the top-level
|
161
|
+
* text node, `baseTransform` (specifies the transformation of the parent text component
|
162
|
+
* in canvas space) should also be provided.
|
163
|
+
*
|
164
|
+
* Note that passing a `baseTransform` is preferable to transforming `visibleRect`. At high
|
165
|
+
* zoom levels, transforming `visibleRect` by the inverse of the parent transform can lead to
|
166
|
+
* inaccuracy due to precision loss.
|
167
|
+
*/
|
168
|
+
renderInternal(canvas, visibleRect, baseTransform = math_1.Mat33.identity) {
|
158
169
|
const cursor = new TextComponent.TextCursor(this.transform, this.style);
|
159
170
|
for (const textObject of this.textObjects) {
|
160
171
|
const { transform, bbox } = cursor.update(textObject);
|
161
|
-
if (visibleRect && !visibleRect.intersects(bbox)) {
|
172
|
+
if (visibleRect && !visibleRect.intersects(bbox.transformedBoundingBox(baseTransform))) {
|
162
173
|
continue;
|
163
174
|
}
|
164
175
|
if (typeof textObject === 'string') {
|
@@ -166,7 +177,7 @@ class TextComponent extends AbstractComponent_1.default {
|
|
166
177
|
}
|
167
178
|
else {
|
168
179
|
canvas.pushTransform(transform);
|
169
|
-
textObject.renderInternal(canvas, visibleRect
|
180
|
+
textObject.renderInternal(canvas, visibleRect, baseTransform.rightMul(transform));
|
170
181
|
canvas.popTransform();
|
171
182
|
}
|
172
183
|
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const sendKeyPressRelease_1 = __importDefault(require("./sendKeyPressRelease"));
|
7
|
+
/** Sets the content of the given `input` or textarea to be `text`. */
|
8
|
+
const fillInput = (input, text, { clear = false } = {}) => {
|
9
|
+
const dispatchUpdate = () => {
|
10
|
+
input.dispatchEvent(new InputEvent('input'));
|
11
|
+
};
|
12
|
+
if (clear) {
|
13
|
+
input.value = '';
|
14
|
+
dispatchUpdate();
|
15
|
+
}
|
16
|
+
for (const character of text.split('')) {
|
17
|
+
input.value += character;
|
18
|
+
(0, sendKeyPressRelease_1.default)(input, character);
|
19
|
+
dispatchUpdate();
|
20
|
+
}
|
21
|
+
};
|
22
|
+
exports.default = fillInput;
|
@@ -1,3 +1,3 @@
|
|
1
|
-
import
|
2
|
-
declare const sendKeyPressRelease: (
|
1
|
+
import Editor from '../Editor';
|
2
|
+
declare const sendKeyPressRelease: (target: Editor | HTMLElement, key: string) => void;
|
3
3
|
export default sendKeyPressRelease;
|
@@ -1,8 +1,20 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const Editor_1 = __importDefault(require("../Editor"));
|
3
7
|
const inputEvents_1 = require("../inputEvents");
|
4
|
-
const
|
5
|
-
|
6
|
-
|
8
|
+
const guessKeyCodeFromKey_1 = __importDefault(require("../util/guessKeyCodeFromKey"));
|
9
|
+
const sendKeyPressRelease = (target, key) => {
|
10
|
+
if (target instanceof Editor_1.default) {
|
11
|
+
target.sendKeyboardEvent(inputEvents_1.InputEvtType.KeyPressEvent, key);
|
12
|
+
target.sendKeyboardEvent(inputEvents_1.InputEvtType.KeyUpEvent, key);
|
13
|
+
}
|
14
|
+
else {
|
15
|
+
const code = (0, guessKeyCodeFromKey_1.default)(key);
|
16
|
+
target.dispatchEvent(new KeyboardEvent('keydown', { key, code }));
|
17
|
+
target.dispatchEvent(new KeyboardEvent('keyup', { key, code }));
|
18
|
+
}
|
7
19
|
};
|
8
20
|
exports.default = sendKeyPressRelease;
|
@@ -15,7 +15,7 @@ import BaseTool from './BaseTool';
|
|
15
15
|
export default class PasteHandler extends BaseTool {
|
16
16
|
private editor;
|
17
17
|
constructor(editor: Editor);
|
18
|
-
onPaste(event: PasteEvent): boolean;
|
18
|
+
onPaste(event: PasteEvent, onComplete?: () => void): boolean;
|
19
19
|
private addComponentsFromPaste;
|
20
20
|
private doSVGPaste;
|
21
21
|
private doTextPaste;
|
@@ -26,12 +26,20 @@ class PasteHandler extends BaseTool_1.default {
|
|
26
26
|
this.editor = editor;
|
27
27
|
}
|
28
28
|
// @internal
|
29
|
-
onPaste(event) {
|
29
|
+
onPaste(event, onComplete) {
|
30
30
|
const mime = event.mime.toLowerCase();
|
31
31
|
const svgData = (() => {
|
32
32
|
if (mime === 'image/svg+xml') {
|
33
33
|
return event.data;
|
34
34
|
}
|
35
|
+
// In some environments, it isn't possible to write non-text data to the
|
36
|
+
// clipboard. To support these cases, auto-detect text/plain SVG data.
|
37
|
+
if (mime === 'text/plain') {
|
38
|
+
const trimmedData = event.data.trim();
|
39
|
+
if (trimmedData.startsWith('<svg') && trimmedData.endsWith('</svg>')) {
|
40
|
+
return trimmedData;
|
41
|
+
}
|
42
|
+
}
|
35
43
|
if (mime !== 'text/html') {
|
36
44
|
return false;
|
37
45
|
}
|
@@ -49,15 +57,15 @@ class PasteHandler extends BaseTool_1.default {
|
|
49
57
|
return event.data.substring(event.data.search(/<svg/i), svgEnd);
|
50
58
|
})();
|
51
59
|
if (svgData) {
|
52
|
-
void this.doSVGPaste(svgData);
|
60
|
+
void this.doSVGPaste(svgData).then(onComplete);
|
53
61
|
return true;
|
54
62
|
}
|
55
63
|
else if (mime === 'text/plain') {
|
56
|
-
void this.doTextPaste(event.data);
|
64
|
+
void this.doTextPaste(event.data).then(onComplete);
|
57
65
|
return true;
|
58
66
|
}
|
59
67
|
else if (mime === 'image/png' || mime === 'image/jpeg') {
|
60
|
-
void this.doImagePaste(event.data);
|
68
|
+
void this.doImagePaste(event.data).then(onComplete);
|
61
69
|
return true;
|
62
70
|
}
|
63
71
|
return false;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -174,6 +174,10 @@ class TextTool extends BaseTool_1.default {
|
|
174
174
|
}, 0);
|
175
175
|
};
|
176
176
|
this.textInputElem.onkeyup = (evt) => {
|
177
|
+
// In certain input modes, the <enter> key is used to select characters.
|
178
|
+
// When in this mode, prevent <enter> from submitting:
|
179
|
+
if (evt.isComposing)
|
180
|
+
return;
|
177
181
|
if (evt.key === 'Enter' && !evt.shiftKey) {
|
178
182
|
this.flushInput();
|
179
183
|
this.editor.focus();
|
@@ -75,6 +75,7 @@ class ClipboardHandler {
|
|
75
75
|
const supportedMIMEs = ['image/svg+xml', 'text/html', 'image/png', 'image/jpeg', 'text/plain'];
|
76
76
|
let files = [];
|
77
77
|
const textData = new Map();
|
78
|
+
const editorSettings = editor.getCurrentSettings();
|
78
79
|
if (hasEvent) {
|
79
80
|
// NOTE: On some browsers, .getData and .files must be used before any async operations.
|
80
81
|
files = [...clipboardData.files];
|
@@ -85,6 +86,21 @@ class ClipboardHandler {
|
|
85
86
|
}
|
86
87
|
}
|
87
88
|
}
|
89
|
+
else if (editorSettings.clipboardApi) {
|
90
|
+
const clipboardData = await editorSettings.clipboardApi.read();
|
91
|
+
for (const [type, data] of clipboardData.entries()) {
|
92
|
+
if (typeof data === 'string') {
|
93
|
+
textData.set(type, data);
|
94
|
+
}
|
95
|
+
else {
|
96
|
+
let blob = data;
|
97
|
+
if (blob.type !== type) {
|
98
|
+
blob = new Blob([blob], { type });
|
99
|
+
}
|
100
|
+
files.push(blob);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
88
104
|
else {
|
89
105
|
const clipboardData = await navigator.clipboard.read();
|
90
106
|
for (const item of clipboardData) {
|
@@ -238,7 +254,13 @@ class ClipboardHandler {
|
|
238
254
|
return navigator.clipboard.write([new ClipboardItem(browserMimeToData)]);
|
239
255
|
};
|
240
256
|
const supportsClipboardApi = typeof ClipboardItem !== 'undefined' && typeof navigator?.clipboard?.write !== 'undefined';
|
241
|
-
|
257
|
+
const prefersClipboardApi = !__classPrivateFieldGet(this, _ClipboardHandler_preferClipboardEvents, "f") && supportsClipboardApi && (hasNonTextMimeTypes || !event);
|
258
|
+
const editorSettings = this.editor.getCurrentSettings();
|
259
|
+
if (prefersClipboardApi && editorSettings.clipboardApi) {
|
260
|
+
const writeResult = editorSettings.clipboardApi.write(mimeToData);
|
261
|
+
return writeResult ?? Promise.resolve();
|
262
|
+
}
|
263
|
+
else if (prefersClipboardApi) {
|
242
264
|
let clipboardApiPromise = null;
|
243
265
|
const fallBackToCopyEvent = (reason) => {
|
244
266
|
console.warn('Unable to copy to the clipboard API. Future calls to .copy will use ClipboardEvents if possible.', reason);
|
package/dist/cjs/version.js
CHANGED
package/dist/mjs/Editor.d.ts
CHANGED
@@ -121,6 +121,18 @@ export interface EditorSettings {
|
|
121
121
|
*/
|
122
122
|
showImagePicker?: ShowCustomFilePickerCallback;
|
123
123
|
} | null;
|
124
|
+
/**
|
125
|
+
* Allows changing how js-draw interacts with the clipboard.
|
126
|
+
*
|
127
|
+
* **Note**: Even when a custom `clipboardApi` is specified, if a `ClipboardEvent` is available
|
128
|
+
* (e.g. from when a user pastes with ctrl+v), the `ClipboardEvent` will be preferred.
|
129
|
+
*/
|
130
|
+
clipboardApi: {
|
131
|
+
/** Called to read data to the clipboard. Keys in the result are MIME types. Values are the data associated with that type. */
|
132
|
+
read(): Promise<Map<string, Blob | string>>;
|
133
|
+
/** Called to write data to the clipboard. Keys in `data` are MIME types. Values are the data associated with that type. */
|
134
|
+
write(data: Map<string, Blob | Promise<Blob> | string>): void | Promise<void>;
|
135
|
+
} | null;
|
124
136
|
}
|
125
137
|
/**
|
126
138
|
* The main entrypoint for the full editor.
|
package/dist/mjs/Editor.mjs
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -76,6 +76,17 @@ export default class TextComponent extends AbstractComponent implements Restylea
|
|
76
76
|
private static getFontHeight;
|
77
77
|
private computeUntransformedBBoxOfPart;
|
78
78
|
private recomputeBBox;
|
79
|
+
/**
|
80
|
+
* Renders a TextComponent or a TextComponent child onto a `canvas`.
|
81
|
+
*
|
82
|
+
* `visibleRect` can be provided as a performance optimization. If not the top-level
|
83
|
+
* text node, `baseTransform` (specifies the transformation of the parent text component
|
84
|
+
* in canvas space) should also be provided.
|
85
|
+
*
|
86
|
+
* Note that passing a `baseTransform` is preferable to transforming `visibleRect`. At high
|
87
|
+
* zoom levels, transforming `visibleRect` by the inverse of the parent transform can lead to
|
88
|
+
* inaccuracy due to precision loss.
|
89
|
+
*/
|
79
90
|
private renderInternal;
|
80
91
|
render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
81
92
|
getProportionalRenderingTime(): number;
|
@@ -148,11 +148,22 @@ class TextComponent extends AbstractComponent {
|
|
148
148
|
}
|
149
149
|
this.contentBBox = bbox ?? Rect2.empty;
|
150
150
|
}
|
151
|
-
|
151
|
+
/**
|
152
|
+
* Renders a TextComponent or a TextComponent child onto a `canvas`.
|
153
|
+
*
|
154
|
+
* `visibleRect` can be provided as a performance optimization. If not the top-level
|
155
|
+
* text node, `baseTransform` (specifies the transformation of the parent text component
|
156
|
+
* in canvas space) should also be provided.
|
157
|
+
*
|
158
|
+
* Note that passing a `baseTransform` is preferable to transforming `visibleRect`. At high
|
159
|
+
* zoom levels, transforming `visibleRect` by the inverse of the parent transform can lead to
|
160
|
+
* inaccuracy due to precision loss.
|
161
|
+
*/
|
162
|
+
renderInternal(canvas, visibleRect, baseTransform = Mat33.identity) {
|
152
163
|
const cursor = new TextComponent.TextCursor(this.transform, this.style);
|
153
164
|
for (const textObject of this.textObjects) {
|
154
165
|
const { transform, bbox } = cursor.update(textObject);
|
155
|
-
if (visibleRect && !visibleRect.intersects(bbox)) {
|
166
|
+
if (visibleRect && !visibleRect.intersects(bbox.transformedBoundingBox(baseTransform))) {
|
156
167
|
continue;
|
157
168
|
}
|
158
169
|
if (typeof textObject === 'string') {
|
@@ -160,7 +171,7 @@ class TextComponent extends AbstractComponent {
|
|
160
171
|
}
|
161
172
|
else {
|
162
173
|
canvas.pushTransform(transform);
|
163
|
-
textObject.renderInternal(canvas, visibleRect
|
174
|
+
textObject.renderInternal(canvas, visibleRect, baseTransform.rightMul(transform));
|
164
175
|
canvas.popTransform();
|
165
176
|
}
|
166
177
|
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import sendKeyPressRelease from './sendKeyPressRelease.mjs';
|
2
|
+
/** Sets the content of the given `input` or textarea to be `text`. */
|
3
|
+
const fillInput = (input, text, { clear = false } = {}) => {
|
4
|
+
const dispatchUpdate = () => {
|
5
|
+
input.dispatchEvent(new InputEvent('input'));
|
6
|
+
};
|
7
|
+
if (clear) {
|
8
|
+
input.value = '';
|
9
|
+
dispatchUpdate();
|
10
|
+
}
|
11
|
+
for (const character of text.split('')) {
|
12
|
+
input.value += character;
|
13
|
+
sendKeyPressRelease(input, character);
|
14
|
+
dispatchUpdate();
|
15
|
+
}
|
16
|
+
};
|
17
|
+
export default fillInput;
|
@@ -1,3 +1,3 @@
|
|
1
|
-
import
|
2
|
-
declare const sendKeyPressRelease: (
|
1
|
+
import Editor from '../Editor';
|
2
|
+
declare const sendKeyPressRelease: (target: Editor | HTMLElement, key: string) => void;
|
3
3
|
export default sendKeyPressRelease;
|
@@ -1,6 +1,15 @@
|
|
1
|
+
import Editor from '../Editor.mjs';
|
1
2
|
import { InputEvtType } from '../inputEvents.mjs';
|
2
|
-
|
3
|
-
|
4
|
-
|
3
|
+
import guessKeyCodeFromKey from '../util/guessKeyCodeFromKey.mjs';
|
4
|
+
const sendKeyPressRelease = (target, key) => {
|
5
|
+
if (target instanceof Editor) {
|
6
|
+
target.sendKeyboardEvent(InputEvtType.KeyPressEvent, key);
|
7
|
+
target.sendKeyboardEvent(InputEvtType.KeyUpEvent, key);
|
8
|
+
}
|
9
|
+
else {
|
10
|
+
const code = guessKeyCodeFromKey(key);
|
11
|
+
target.dispatchEvent(new KeyboardEvent('keydown', { key, code }));
|
12
|
+
target.dispatchEvent(new KeyboardEvent('keyup', { key, code }));
|
13
|
+
}
|
5
14
|
};
|
6
15
|
export default sendKeyPressRelease;
|
@@ -15,7 +15,7 @@ import BaseTool from './BaseTool';
|
|
15
15
|
export default class PasteHandler extends BaseTool {
|
16
16
|
private editor;
|
17
17
|
constructor(editor: Editor);
|
18
|
-
onPaste(event: PasteEvent): boolean;
|
18
|
+
onPaste(event: PasteEvent, onComplete?: () => void): boolean;
|
19
19
|
private addComponentsFromPaste;
|
20
20
|
private doSVGPaste;
|
21
21
|
private doTextPaste;
|
@@ -21,12 +21,20 @@ export default class PasteHandler extends BaseTool {
|
|
21
21
|
this.editor = editor;
|
22
22
|
}
|
23
23
|
// @internal
|
24
|
-
onPaste(event) {
|
24
|
+
onPaste(event, onComplete) {
|
25
25
|
const mime = event.mime.toLowerCase();
|
26
26
|
const svgData = (() => {
|
27
27
|
if (mime === 'image/svg+xml') {
|
28
28
|
return event.data;
|
29
29
|
}
|
30
|
+
// In some environments, it isn't possible to write non-text data to the
|
31
|
+
// clipboard. To support these cases, auto-detect text/plain SVG data.
|
32
|
+
if (mime === 'text/plain') {
|
33
|
+
const trimmedData = event.data.trim();
|
34
|
+
if (trimmedData.startsWith('<svg') && trimmedData.endsWith('</svg>')) {
|
35
|
+
return trimmedData;
|
36
|
+
}
|
37
|
+
}
|
30
38
|
if (mime !== 'text/html') {
|
31
39
|
return false;
|
32
40
|
}
|
@@ -44,15 +52,15 @@ export default class PasteHandler extends BaseTool {
|
|
44
52
|
return event.data.substring(event.data.search(/<svg/i), svgEnd);
|
45
53
|
})();
|
46
54
|
if (svgData) {
|
47
|
-
void this.doSVGPaste(svgData);
|
55
|
+
void this.doSVGPaste(svgData).then(onComplete);
|
48
56
|
return true;
|
49
57
|
}
|
50
58
|
else if (mime === 'text/plain') {
|
51
|
-
void this.doTextPaste(event.data);
|
59
|
+
void this.doTextPaste(event.data).then(onComplete);
|
52
60
|
return true;
|
53
61
|
}
|
54
62
|
else if (mime === 'image/png' || mime === 'image/jpeg') {
|
55
|
-
void this.doImagePaste(event.data);
|
63
|
+
void this.doImagePaste(event.data).then(onComplete);
|
56
64
|
return true;
|
57
65
|
}
|
58
66
|
return false;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -169,6 +169,10 @@ export default class TextTool extends BaseTool {
|
|
169
169
|
}, 0);
|
170
170
|
};
|
171
171
|
this.textInputElem.onkeyup = (evt) => {
|
172
|
+
// In certain input modes, the <enter> key is used to select characters.
|
173
|
+
// When in this mode, prevent <enter> from submitting:
|
174
|
+
if (evt.isComposing)
|
175
|
+
return;
|
172
176
|
if (evt.key === 'Enter' && !evt.shiftKey) {
|
173
177
|
this.flushInput();
|
174
178
|
this.editor.focus();
|
@@ -70,6 +70,7 @@ class ClipboardHandler {
|
|
70
70
|
const supportedMIMEs = ['image/svg+xml', 'text/html', 'image/png', 'image/jpeg', 'text/plain'];
|
71
71
|
let files = [];
|
72
72
|
const textData = new Map();
|
73
|
+
const editorSettings = editor.getCurrentSettings();
|
73
74
|
if (hasEvent) {
|
74
75
|
// NOTE: On some browsers, .getData and .files must be used before any async operations.
|
75
76
|
files = [...clipboardData.files];
|
@@ -80,6 +81,21 @@ class ClipboardHandler {
|
|
80
81
|
}
|
81
82
|
}
|
82
83
|
}
|
84
|
+
else if (editorSettings.clipboardApi) {
|
85
|
+
const clipboardData = await editorSettings.clipboardApi.read();
|
86
|
+
for (const [type, data] of clipboardData.entries()) {
|
87
|
+
if (typeof data === 'string') {
|
88
|
+
textData.set(type, data);
|
89
|
+
}
|
90
|
+
else {
|
91
|
+
let blob = data;
|
92
|
+
if (blob.type !== type) {
|
93
|
+
blob = new Blob([blob], { type });
|
94
|
+
}
|
95
|
+
files.push(blob);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
83
99
|
else {
|
84
100
|
const clipboardData = await navigator.clipboard.read();
|
85
101
|
for (const item of clipboardData) {
|
@@ -233,7 +249,13 @@ class ClipboardHandler {
|
|
233
249
|
return navigator.clipboard.write([new ClipboardItem(browserMimeToData)]);
|
234
250
|
};
|
235
251
|
const supportsClipboardApi = typeof ClipboardItem !== 'undefined' && typeof navigator?.clipboard?.write !== 'undefined';
|
236
|
-
|
252
|
+
const prefersClipboardApi = !__classPrivateFieldGet(this, _ClipboardHandler_preferClipboardEvents, "f") && supportsClipboardApi && (hasNonTextMimeTypes || !event);
|
253
|
+
const editorSettings = this.editor.getCurrentSettings();
|
254
|
+
if (prefersClipboardApi && editorSettings.clipboardApi) {
|
255
|
+
const writeResult = editorSettings.clipboardApi.write(mimeToData);
|
256
|
+
return writeResult ?? Promise.resolve();
|
257
|
+
}
|
258
|
+
else if (prefersClipboardApi) {
|
237
259
|
let clipboardApiPromise = null;
|
238
260
|
const fallBackToCopyEvent = (reason) => {
|
239
261
|
console.warn('Unable to copy to the clipboard API. Future calls to .copy will use ClipboardEvents if possible.', reason);
|
package/dist/mjs/version.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.25.0",
|
4
4
|
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
|
5
5
|
"types": "./dist/mjs/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -64,11 +64,11 @@
|
|
64
64
|
"postpack": "ts-node tools/copyREADME.ts revert"
|
65
65
|
},
|
66
66
|
"dependencies": {
|
67
|
-
"@js-draw/math": "^1.
|
67
|
+
"@js-draw/math": "^1.25.0",
|
68
68
|
"@melloware/coloris": "0.22.0"
|
69
69
|
},
|
70
70
|
"devDependencies": {
|
71
|
-
"@js-draw/build-tool": "^1.
|
71
|
+
"@js-draw/build-tool": "^1.25.0",
|
72
72
|
"@types/jest": "29.5.5",
|
73
73
|
"@types/jsdom": "21.1.3"
|
74
74
|
},
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "8ecc7be6d8b1b00c25fe7d3ed6c5fee239451dfa"
|
90
90
|
}
|