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.
Files changed (33) hide show
  1. package/README.md +15 -15
  2. package/dist/bundle.js +1 -1
  3. package/dist/cjs/Editor.d.ts +12 -0
  4. package/dist/cjs/Editor.js +1 -0
  5. package/dist/cjs/commands/invertCommand.test.d.ts +1 -0
  6. package/dist/cjs/components/TextComponent.d.ts +11 -0
  7. package/dist/cjs/components/TextComponent.js +14 -3
  8. package/dist/cjs/testing/fillHtmlInput.d.ts +6 -0
  9. package/dist/cjs/testing/fillHtmlInput.js +22 -0
  10. package/dist/cjs/testing/sendKeyPressRelease.d.ts +2 -2
  11. package/dist/cjs/testing/sendKeyPressRelease.js +15 -3
  12. package/dist/cjs/tools/PasteHandler.d.ts +1 -1
  13. package/dist/cjs/tools/PasteHandler.js +12 -4
  14. package/dist/cjs/tools/PasteHandler.test.d.ts +1 -0
  15. package/dist/cjs/tools/TextTool.js +4 -0
  16. package/dist/cjs/util/ClipboardHandler.js +23 -1
  17. package/dist/cjs/version.js +1 -1
  18. package/dist/mjs/Editor.d.ts +12 -0
  19. package/dist/mjs/Editor.mjs +1 -0
  20. package/dist/mjs/commands/invertCommand.test.d.ts +1 -0
  21. package/dist/mjs/components/TextComponent.d.ts +11 -0
  22. package/dist/mjs/components/TextComponent.mjs +14 -3
  23. package/dist/mjs/testing/fillHtmlInput.d.ts +6 -0
  24. package/dist/mjs/testing/fillHtmlInput.mjs +17 -0
  25. package/dist/mjs/testing/sendKeyPressRelease.d.ts +2 -2
  26. package/dist/mjs/testing/sendKeyPressRelease.mjs +12 -3
  27. package/dist/mjs/tools/PasteHandler.d.ts +1 -1
  28. package/dist/mjs/tools/PasteHandler.mjs +12 -4
  29. package/dist/mjs/tools/PasteHandler.test.d.ts +1 -0
  30. package/dist/mjs/tools/TextTool.mjs +4 -0
  31. package/dist/mjs/util/ClipboardHandler.mjs +23 -1
  32. package/dist/mjs/version.mjs +1 -1
  33. package/package.json +4 -4
@@ -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.
@@ -159,6 +159,7 @@ class Editor {
159
159
  image: {
160
160
  showImagePicker: settings.image?.showImagePicker ?? undefined,
161
161
  },
162
+ clipboardApi: settings.clipboardApi ?? null,
162
163
  };
163
164
  // Validate settings
164
165
  if (this.settings.minZoom > this.settings.maxZoom) {
@@ -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
- renderInternal(canvas, visibleRect) {
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?.transformedBoundingBox(transform.inverse()));
180
+ textObject.renderInternal(canvas, visibleRect, baseTransform.rightMul(transform));
170
181
  canvas.popTransform();
171
182
  }
172
183
  }
@@ -0,0 +1,6 @@
1
+ interface Options {
2
+ clear?: boolean;
3
+ }
4
+ /** Sets the content of the given `input` or textarea to be `text`. */
5
+ declare const fillInput: (input: HTMLInputElement | HTMLTextAreaElement, text: string, { clear }?: Options) => void;
6
+ export default fillInput;
@@ -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 type Editor from '../Editor';
2
- declare const sendKeyPressRelease: (editor: Editor, key: string) => void;
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 sendKeyPressRelease = (editor, key) => {
5
- editor.sendKeyboardEvent(inputEvents_1.InputEvtType.KeyPressEvent, key);
6
- editor.sendKeyboardEvent(inputEvents_1.InputEvtType.KeyUpEvent, key);
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
- if (!__classPrivateFieldGet(this, _ClipboardHandler_preferClipboardEvents, "f") && supportsClipboardApi && (hasNonTextMimeTypes || !event)) {
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);
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  * @internal
7
7
  */
8
8
  exports.default = {
9
- number: '1.24.1',
9
+ number: '1.25.0',
10
10
  };
@@ -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.
@@ -120,6 +120,7 @@ export class Editor {
120
120
  image: {
121
121
  showImagePicker: settings.image?.showImagePicker ?? undefined,
122
122
  },
123
+ clipboardApi: settings.clipboardApi ?? null,
123
124
  };
124
125
  // Validate settings
125
126
  if (this.settings.minZoom > this.settings.maxZoom) {
@@ -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
- renderInternal(canvas, visibleRect) {
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?.transformedBoundingBox(transform.inverse()));
174
+ textObject.renderInternal(canvas, visibleRect, baseTransform.rightMul(transform));
164
175
  canvas.popTransform();
165
176
  }
166
177
  }
@@ -0,0 +1,6 @@
1
+ interface Options {
2
+ clear?: boolean;
3
+ }
4
+ /** Sets the content of the given `input` or textarea to be `text`. */
5
+ declare const fillInput: (input: HTMLInputElement | HTMLTextAreaElement, text: string, { clear }?: Options) => void;
6
+ export default fillInput;
@@ -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 type Editor from '../Editor';
2
- declare const sendKeyPressRelease: (editor: Editor, key: string) => void;
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
- const sendKeyPressRelease = (editor, key) => {
3
- editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, key);
4
- editor.sendKeyboardEvent(InputEvtType.KeyUpEvent, key);
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
- if (!__classPrivateFieldGet(this, _ClipboardHandler_preferClipboardEvents, "f") && supportsClipboardApi && (hasNonTextMimeTypes || !event)) {
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);
@@ -4,5 +4,5 @@
4
4
  * @internal
5
5
  */
6
6
  export default {
7
- number: '1.24.1',
7
+ number: '1.25.0',
8
8
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.24.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.24.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.24.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": "ef847374748e32d6d96d993a2236a99d9109a32c"
89
+ "gitHead": "8ecc7be6d8b1b00c25fe7d3ed6c5fee239451dfa"
90
90
  }