js-draw 1.24.1 → 1.25.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/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
|
}
|