js-draw 1.16.1 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. package/README.md +70 -10
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundledStyles.js +1 -1
  4. package/dist/cjs/Editor.d.ts +75 -7
  5. package/dist/cjs/Editor.js +93 -90
  6. package/dist/cjs/Pointer.d.ts +2 -1
  7. package/dist/cjs/Pointer.js +9 -2
  8. package/dist/cjs/commands/localization.d.ts +1 -0
  9. package/dist/cjs/commands/localization.js +1 -0
  10. package/dist/cjs/commands/uniteCommands.d.ts +5 -1
  11. package/dist/cjs/commands/uniteCommands.js +33 -7
  12. package/dist/cjs/components/AbstractComponent.d.ts +17 -5
  13. package/dist/cjs/components/AbstractComponent.js +15 -15
  14. package/dist/cjs/components/Stroke.d.ts +4 -1
  15. package/dist/cjs/components/Stroke.js +158 -2
  16. package/dist/cjs/components/TextComponent.d.ts +36 -1
  17. package/dist/cjs/components/TextComponent.js +39 -1
  18. package/dist/cjs/components/builders/ArrowBuilder.js +1 -1
  19. package/dist/cjs/components/builders/PolylineBuilder.d.ts +35 -0
  20. package/dist/cjs/components/builders/PolylineBuilder.js +122 -0
  21. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
  22. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +44 -51
  23. package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +1 -1
  24. package/dist/cjs/components/lib.d.ts +1 -0
  25. package/dist/cjs/components/lib.js +3 -1
  26. package/dist/cjs/components/util/StrokeSmoother.js +4 -4
  27. package/dist/cjs/image/EditorImage.d.ts +4 -1
  28. package/dist/cjs/image/EditorImage.js +5 -2
  29. package/dist/cjs/inputEvents.d.ts +11 -1
  30. package/dist/cjs/localizations/comments.d.ts +3 -0
  31. package/dist/cjs/localizations/comments.js +3 -0
  32. package/dist/cjs/localizations/de.js +1 -3
  33. package/dist/cjs/localizations/es.js +3 -3
  34. package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
  35. package/dist/cjs/rendering/renderers/CanvasRenderer.js +16 -0
  36. package/dist/cjs/rendering/renderers/SVGRenderer.js +1 -1
  37. package/dist/cjs/testing/createEditor.d.ts +2 -2
  38. package/dist/cjs/testing/createEditor.js +2 -2
  39. package/dist/cjs/toolbar/IconProvider.d.ts +9 -4
  40. package/dist/cjs/toolbar/IconProvider.js +21 -7
  41. package/dist/cjs/toolbar/localization.d.ts +6 -1
  42. package/dist/cjs/toolbar/localization.js +7 -2
  43. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +24 -1
  44. package/dist/cjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
  45. package/dist/cjs/toolbar/widgets/EraserToolWidget.js +45 -5
  46. package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
  47. package/dist/cjs/toolbar/widgets/PenToolWidget.js +17 -4
  48. package/dist/cjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
  49. package/dist/cjs/toolbar/widgets/keybindings.js +1 -1
  50. package/dist/cjs/tools/Eraser.d.ts +24 -4
  51. package/dist/cjs/tools/Eraser.js +108 -21
  52. package/dist/cjs/tools/InputFilter/InputStabilizer.js +3 -3
  53. package/dist/cjs/tools/PasteHandler.js +35 -10
  54. package/dist/cjs/tools/Pen.js +2 -2
  55. package/dist/cjs/tools/SelectionTool/SelectionTool.js +23 -4
  56. package/dist/cjs/tools/SelectionTool/ToPointerAutoscroller.js +1 -1
  57. package/dist/cjs/tools/ToolController.d.ts +17 -1
  58. package/dist/cjs/tools/ToolController.js +21 -8
  59. package/dist/cjs/tools/lib.d.ts +1 -4
  60. package/dist/cjs/tools/lib.js +2 -4
  61. package/dist/cjs/tools/localization.d.ts +2 -2
  62. package/dist/cjs/tools/localization.js +2 -2
  63. package/dist/cjs/util/ClipboardHandler.d.ts +27 -0
  64. package/dist/cjs/util/ClipboardHandler.js +205 -0
  65. package/dist/cjs/util/ClipboardHandler.test.d.ts +1 -0
  66. package/dist/cjs/version.d.ts +5 -0
  67. package/dist/cjs/version.js +6 -1
  68. package/dist/mjs/Editor.d.ts +75 -7
  69. package/dist/mjs/Editor.mjs +93 -90
  70. package/dist/mjs/Pointer.d.ts +2 -1
  71. package/dist/mjs/Pointer.mjs +9 -2
  72. package/dist/mjs/commands/localization.d.ts +1 -0
  73. package/dist/mjs/commands/localization.mjs +1 -0
  74. package/dist/mjs/commands/uniteCommands.d.ts +5 -1
  75. package/dist/mjs/commands/uniteCommands.mjs +33 -7
  76. package/dist/mjs/components/AbstractComponent.d.ts +17 -5
  77. package/dist/mjs/components/AbstractComponent.mjs +15 -15
  78. package/dist/mjs/components/Stroke.d.ts +4 -1
  79. package/dist/mjs/components/Stroke.mjs +159 -3
  80. package/dist/mjs/components/TextComponent.d.ts +36 -1
  81. package/dist/mjs/components/TextComponent.mjs +40 -2
  82. package/dist/mjs/components/builders/ArrowBuilder.mjs +1 -1
  83. package/dist/mjs/components/builders/PolylineBuilder.d.ts +35 -0
  84. package/dist/mjs/components/builders/PolylineBuilder.mjs +115 -0
  85. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
  86. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +45 -52
  87. package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +1 -1
  88. package/dist/mjs/components/lib.d.ts +1 -0
  89. package/dist/mjs/components/lib.mjs +1 -0
  90. package/dist/mjs/components/util/StrokeSmoother.mjs +4 -4
  91. package/dist/mjs/image/EditorImage.d.ts +4 -1
  92. package/dist/mjs/image/EditorImage.mjs +5 -2
  93. package/dist/mjs/inputEvents.d.ts +11 -1
  94. package/dist/mjs/localizations/comments.d.ts +3 -0
  95. package/dist/mjs/localizations/comments.mjs +3 -0
  96. package/dist/mjs/localizations/de.mjs +1 -3
  97. package/dist/mjs/localizations/es.mjs +3 -3
  98. package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
  99. package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +16 -0
  100. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +1 -1
  101. package/dist/mjs/testing/createEditor.d.ts +2 -2
  102. package/dist/mjs/testing/createEditor.mjs +2 -2
  103. package/dist/mjs/toolbar/IconProvider.d.ts +9 -4
  104. package/dist/mjs/toolbar/IconProvider.mjs +21 -7
  105. package/dist/mjs/toolbar/localization.d.ts +6 -1
  106. package/dist/mjs/toolbar/localization.mjs +7 -2
  107. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +24 -1
  108. package/dist/mjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
  109. package/dist/mjs/toolbar/widgets/EraserToolWidget.mjs +47 -6
  110. package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
  111. package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +17 -4
  112. package/dist/mjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
  113. package/dist/mjs/toolbar/widgets/keybindings.mjs +1 -1
  114. package/dist/mjs/tools/Eraser.d.ts +24 -4
  115. package/dist/mjs/tools/Eraser.mjs +108 -22
  116. package/dist/mjs/tools/InputFilter/InputStabilizer.mjs +3 -3
  117. package/dist/mjs/tools/PasteHandler.mjs +35 -10
  118. package/dist/mjs/tools/Pen.mjs +2 -2
  119. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +23 -4
  120. package/dist/mjs/tools/SelectionTool/ToPointerAutoscroller.mjs +1 -1
  121. package/dist/mjs/tools/ToolController.d.ts +17 -1
  122. package/dist/mjs/tools/ToolController.mjs +21 -8
  123. package/dist/mjs/tools/lib.d.ts +1 -4
  124. package/dist/mjs/tools/lib.mjs +1 -4
  125. package/dist/mjs/tools/localization.d.ts +2 -2
  126. package/dist/mjs/tools/localization.mjs +2 -2
  127. package/dist/mjs/util/ClipboardHandler.d.ts +27 -0
  128. package/dist/mjs/util/ClipboardHandler.mjs +200 -0
  129. package/dist/mjs/util/ClipboardHandler.test.d.ts +1 -0
  130. package/dist/mjs/version.d.ts +5 -0
  131. package/dist/mjs/version.mjs +6 -1
  132. package/package.json +6 -6
@@ -8,6 +8,7 @@ const math_1 = require("@js-draw/math");
8
8
  const types_1 = require("../../types");
9
9
  const Viewport_1 = __importDefault(require("../../Viewport"));
10
10
  const BaseTool_1 = __importDefault(require("../BaseTool"));
11
+ const CanvasRenderer_1 = __importDefault(require("../../rendering/renderers/CanvasRenderer"));
11
12
  const SVGRenderer_1 = __importDefault(require("../../rendering/renderers/SVGRenderer"));
12
13
  const Selection_1 = __importDefault(require("./Selection"));
13
14
  const TextComponent_1 = __importDefault(require("../../components/TextComponent"));
@@ -376,19 +377,37 @@ class SelectionTool extends BaseTool_1.default {
376
377
  return false;
377
378
  }
378
379
  const exportViewport = new Viewport_1.default(() => { });
379
- exportViewport.updateScreenSize(math_1.Vec2.of(bbox.w, bbox.h));
380
- exportViewport.resetTransform(math_1.Mat33.translation(bbox.topLeft.times(-1)));
381
- const sanitize = true;
382
- const { element: svgExportElem, renderer: svgRenderer } = SVGRenderer_1.default.fromViewport(exportViewport, sanitize);
380
+ const selectionScreenSize = this.selectionBox.getScreenRegion().size.times(this.editor.display.getDevicePixelRatio());
381
+ // Update the viewport to have screen size roughly equal to the size of the selection box
382
+ let scaleFactor = selectionScreenSize.maximumEntryMagnitude() / (bbox.size.maximumEntryMagnitude() || 1);
383
+ // Round to a nearby power of two
384
+ scaleFactor = Math.pow(2, Math.ceil(Math.log2(scaleFactor)));
385
+ exportViewport.updateScreenSize(bbox.size.times(scaleFactor));
386
+ exportViewport.resetTransform(math_1.Mat33.scaling2D(scaleFactor)
387
+ // Move the selection onto the screen
388
+ .rightMul(math_1.Mat33.translation(bbox.topLeft.times(-1))));
389
+ const { element: svgExportElem, renderer: svgRenderer } = SVGRenderer_1.default.fromViewport(exportViewport, { sanitize: true, useViewBoxForPositioning: true });
390
+ const { element: canvas, renderer: canvasRenderer } = CanvasRenderer_1.default.fromViewport(exportViewport, { maxCanvasDimen: 4096 });
383
391
  const text = [];
384
392
  for (const elem of selectedElems) {
385
393
  elem.render(svgRenderer);
394
+ elem.render(canvasRenderer);
386
395
  if (elem instanceof TextComponent_1.default) {
387
396
  text.push(elem.getText());
388
397
  }
389
398
  }
390
399
  event.setData('image/svg+xml', svgExportElem.outerHTML);
391
400
  event.setData('text/html', svgExportElem.outerHTML);
401
+ event.setData('image/png', new Promise((resolve, reject) => {
402
+ canvas.toBlob((blob) => {
403
+ if (blob) {
404
+ resolve(blob);
405
+ }
406
+ else {
407
+ reject('Failed to convert canvas to blob.');
408
+ }
409
+ }, 'image/png');
410
+ }));
392
411
  if (text.length > 0) {
393
412
  event.setData('text/plain', text.join('\n'));
394
413
  }
@@ -28,7 +28,7 @@ class ToPointerAutoscroller {
28
28
  return math_1.Vec2.zero;
29
29
  }
30
30
  const closestEdgePoint = autoscrollBoundary.getClosestPointOnBoundaryTo(screenPoint);
31
- const distToEdge = closestEdgePoint.minus(screenPoint).magnitude();
31
+ const distToEdge = closestEdgePoint.distanceTo(screenPoint);
32
32
  const toEdge = closestEdgePoint.minus(screenPoint);
33
33
  // Go faster for points further away from the boundary.
34
34
  const maximumScaleFactor = 1.25;
@@ -12,6 +12,12 @@ export default class ToolController implements InputEventListener {
12
12
  private isEditorReadOnly;
13
13
  /** @internal */
14
14
  constructor(editor: Editor, localization: ToolLocalization);
15
+ /**
16
+ * Replaces the current set of tools with `tools`. This should only be done before
17
+ * the creation of the app's toolbar (if using `AbstractToolbar`).
18
+ *
19
+ * If no `primaryToolGroup` is given, an empty one will be created.
20
+ */
15
21
  setTools(tools: BaseTool[], primaryToolGroup?: ToolEnabledGroup): void;
16
22
  /**
17
23
  * Add a tool that acts like one of the primary tools (only one primary tool can be enabled at a time).
@@ -22,7 +28,17 @@ export default class ToolController implements InputEventListener {
22
28
  */
23
29
  addPrimaryTool(tool: BaseTool): void;
24
30
  getPrimaryTools(): BaseTool[];
25
- addTool(tool: BaseTool): void;
31
+ /**
32
+ * Add a tool to the end of this' tool list (the added tool receives events after tools already added to this).
33
+ * This should be called before creating the app's toolbar.
34
+ *
35
+ * If `options.addToFront`, the tool is added to the beginning of this' tool list.
36
+ *
37
+ * Does nothing if the tool is already present.
38
+ */
39
+ addTool(tool: BaseTool, options?: {
40
+ addToFront: boolean;
41
+ }): void;
26
42
  /**
27
43
  * Removes **and destroys** all tools in `tools` from this.
28
44
  */
@@ -109,8 +109,12 @@ class ToolController {
109
109
  });
110
110
  this.activeTool = null;
111
111
  }
112
- // Replaces the current set of tools with `tools`. This should only be done before
113
- // the creation of the app's toolbar (if using `AbstractToolbar`).
112
+ /**
113
+ * Replaces the current set of tools with `tools`. This should only be done before
114
+ * the creation of the app's toolbar (if using `AbstractToolbar`).
115
+ *
116
+ * If no `primaryToolGroup` is given, an empty one will be created.
117
+ */
114
118
  setTools(tools, primaryToolGroup) {
115
119
  this.tools = tools;
116
120
  this.primaryToolGroup = primaryToolGroup ?? new ToolEnabledGroup_1.default();
@@ -136,14 +140,23 @@ class ToolController {
136
140
  return tool.getToolGroup() === this.primaryToolGroup;
137
141
  });
138
142
  }
139
- // Add a tool to the end of this' tool list (the added tool receives events after tools already added to this).
140
- // This should be called before creating the app's toolbar.
141
- //
142
- // A tool should only be added once.
143
- addTool(tool) {
143
+ /**
144
+ * Add a tool to the end of this' tool list (the added tool receives events after tools already added to this).
145
+ * This should be called before creating the app's toolbar.
146
+ *
147
+ * If `options.addToFront`, the tool is added to the beginning of this' tool list.
148
+ *
149
+ * Does nothing if the tool is already present.
150
+ */
151
+ addTool(tool, options) {
144
152
  // Only add if not already present.
145
153
  if (!this.tools.includes(tool)) {
146
- this.tools.push(tool);
154
+ if (options?.addToFront) {
155
+ this.tools.splice(0, 0, tool);
156
+ }
157
+ else {
158
+ this.tools.push(tool);
159
+ }
147
160
  }
148
161
  }
149
162
  /**
@@ -1,6 +1,3 @@
1
- /**
2
- * @packageDocumentation
3
- */
4
1
  export { default as BaseTool } from './BaseTool';
5
2
  export { default as ToolController } from './ToolController';
6
3
  export { default as ToolEnabledGroup } from './ToolEnabledGroup';
@@ -11,7 +8,7 @@ export { default as PenTool, PenStyle } from './Pen';
11
8
  export { default as TextTool } from './TextTool';
12
9
  export { default as SelectionTool } from './SelectionTool/SelectionTool';
13
10
  export { default as SelectAllShortcutHandler } from './SelectionTool/SelectAllShortcutHandler';
14
- export { default as EraserTool } from './Eraser';
11
+ export { default as EraserTool, EraserMode } from './Eraser';
15
12
  export { default as PasteHandler } from './PasteHandler';
16
13
  export { default as SoundUITool } from './SoundUITool';
17
14
  export { default as ToolbarShortcutHandler } from './ToolbarShortcutHandler';
@@ -1,12 +1,9 @@
1
1
  "use strict";
2
- /**
3
- * @packageDocumentation
4
- */
5
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
6
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
7
4
  };
8
5
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ToolbarShortcutHandler = exports.SoundUITool = exports.PasteHandler = exports.EraserTool = exports.SelectAllShortcutHandler = exports.SelectionTool = exports.TextTool = exports.PenTool = exports.PanZoomMode = exports.PanZoomTool = exports.ToolSwitcherShortcut = exports.UndoRedoShortcut = exports.ToolEnabledGroup = exports.ToolController = exports.BaseTool = void 0;
6
+ exports.ToolbarShortcutHandler = exports.SoundUITool = exports.PasteHandler = exports.EraserMode = exports.EraserTool = exports.SelectAllShortcutHandler = exports.SelectionTool = exports.TextTool = exports.PenTool = exports.PanZoomMode = exports.PanZoomTool = exports.ToolSwitcherShortcut = exports.UndoRedoShortcut = exports.ToolEnabledGroup = exports.ToolController = exports.BaseTool = void 0;
10
7
  var BaseTool_1 = require("./BaseTool");
11
8
  Object.defineProperty(exports, "BaseTool", { enumerable: true, get: function () { return __importDefault(BaseTool_1).default; } });
12
9
  var ToolController_1 = require("./ToolController");
@@ -30,6 +27,7 @@ var SelectAllShortcutHandler_1 = require("./SelectionTool/SelectAllShortcutHandl
30
27
  Object.defineProperty(exports, "SelectAllShortcutHandler", { enumerable: true, get: function () { return __importDefault(SelectAllShortcutHandler_1).default; } });
31
28
  var Eraser_1 = require("./Eraser");
32
29
  Object.defineProperty(exports, "EraserTool", { enumerable: true, get: function () { return __importDefault(Eraser_1).default; } });
30
+ Object.defineProperty(exports, "EraserMode", { enumerable: true, get: function () { return Eraser_1.EraserMode; } });
33
31
  var PasteHandler_1 = require("./PasteHandler");
34
32
  Object.defineProperty(exports, "PasteHandler", { enumerable: true, get: function () { return __importDefault(PasteHandler_1).default; } });
35
33
  var SoundUITool_1 = require("./SoundUITool");
@@ -26,8 +26,8 @@ export interface ToolLocalization {
26
26
  findDialogHidden: string;
27
27
  focusedFoundText: (currentMatchNumber: number, totalMatches: number) => string;
28
28
  anyDevicePanning: string;
29
- copied: (count: number, description: string) => string;
30
- pasted: (count: number, description: string) => string;
29
+ copied: (count: number) => string;
30
+ pasted: (count: number) => string;
31
31
  toolEnabledAnnouncement: (toolName: string) => string;
32
32
  toolDisabledAnnouncement: (toolName: string) => string;
33
33
  }
@@ -29,8 +29,8 @@ exports.defaultToolLocalization = {
29
29
  findDialogHidden: 'Find dialog hidden',
30
30
  focusedFoundText: (matchIdx, totalMatches) => `Viewing match ${matchIdx} of ${totalMatches}`,
31
31
  anyDevicePanning: 'Any device panning',
32
- copied: (count, description) => `Copied ${count} ${description}`,
33
- pasted: (count, description) => `Pasted ${count} ${description}`,
32
+ copied: (count) => `Copied ${count} item(s)`,
33
+ pasted: (count) => `Pasted ${count} item(s)`,
34
34
  toolEnabledAnnouncement: (toolName) => `${toolName} enabled`,
35
35
  toolDisabledAnnouncement: (toolName) => `${toolName} disabled`,
36
36
  };
@@ -0,0 +1,27 @@
1
+ import { Editor } from '../Editor';
2
+ /**
3
+ * Handles conversion between the browser clipboard APIs and internal
4
+ * js-draw clipboard events.
5
+ */
6
+ export default class ClipboardHandler {
7
+ #private;
8
+ private editor;
9
+ constructor(editor: Editor);
10
+ /**
11
+ * Pastes data from the clipboard into the editor associated with
12
+ * this handler.
13
+ *
14
+ * @param event Optional -- a clipboard/drag event. If not provided,
15
+ * `navigator.clipboard` will be used instead.
16
+ * @returns true if the paste event was handled by the editor.
17
+ */
18
+ paste(event?: DragEvent | ClipboardEvent): Promise<boolean>;
19
+ /**
20
+ * Copies text from the editor associated with this.
21
+ *
22
+ * Even if `event` is provided, the `navigator.clipboard` API may be used if image data
23
+ * is to be copied. This is done because `ClipboardEvent`s seem to not support attaching
24
+ * images.
25
+ */
26
+ copy(event?: ClipboardEvent): Promise<void>;
27
+ }
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ var _ClipboardHandler_preferClipboardEvents;
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const inputEvents_1 = require("../inputEvents");
19
+ const fileToBase64Url_1 = __importDefault(require("./fileToBase64Url"));
20
+ const isTextMimeType = (mime) =>
21
+ // +xml: Handles image/svg+xml
22
+ mime.endsWith('+xml') || mime.startsWith('text/');
23
+ /**
24
+ * Handles conversion between the browser clipboard APIs and internal
25
+ * js-draw clipboard events.
26
+ */
27
+ class ClipboardHandler {
28
+ constructor(editor) {
29
+ this.editor = editor;
30
+ _ClipboardHandler_preferClipboardEvents.set(this, false);
31
+ }
32
+ /**
33
+ * Pastes data from the clipboard into the editor associated with
34
+ * this handler.
35
+ *
36
+ * @param event Optional -- a clipboard/drag event. If not provided,
37
+ * `navigator.clipboard` will be used instead.
38
+ * @returns true if the paste event was handled by the editor.
39
+ */
40
+ async paste(event) {
41
+ const editor = this.editor;
42
+ const clipboardData = event?.dataTransfer ?? event?.clipboardData ?? null;
43
+ const hasEvent = !!clipboardData;
44
+ const sendPasteEvent = (mime, data) => {
45
+ return data && editor.toolController.dispatchInputEvent({
46
+ kind: inputEvents_1.InputEvtType.PasteEvent,
47
+ mime,
48
+ data,
49
+ });
50
+ };
51
+ // Listed in order of precedence
52
+ const supportedMIMEs = [
53
+ 'image/svg+xml',
54
+ 'text/html',
55
+ 'image/png',
56
+ 'image/jpeg',
57
+ 'text/plain',
58
+ ];
59
+ let files = [];
60
+ const textData = new Map();
61
+ if (hasEvent) {
62
+ // NOTE: On some browsers, .getData and .files must be used before any async operations.
63
+ files = [...clipboardData.files];
64
+ for (const mime of supportedMIMEs) {
65
+ const data = clipboardData.getData(mime);
66
+ if (data) {
67
+ textData.set(mime, data);
68
+ }
69
+ }
70
+ }
71
+ else {
72
+ const clipboardData = await navigator.clipboard.read();
73
+ for (const item of clipboardData) {
74
+ for (const mime of item.types) {
75
+ if (supportedMIMEs.includes(mime)) {
76
+ files.push(await item.getType(mime));
77
+ }
78
+ }
79
+ }
80
+ }
81
+ // Returns true if handled
82
+ const handleMIME = async (mime) => {
83
+ const isTextFormat = isTextMimeType(mime);
84
+ if (isTextFormat) {
85
+ const data = textData.get(mime);
86
+ if (sendPasteEvent(mime, data)) {
87
+ event?.preventDefault();
88
+ return true;
89
+ }
90
+ }
91
+ for (const file of files) {
92
+ const fileType = file.type.toLowerCase();
93
+ if (fileType !== mime) {
94
+ continue;
95
+ }
96
+ if (isTextFormat) {
97
+ const text = await file.text();
98
+ if (sendPasteEvent(mime, text)) {
99
+ event?.preventDefault();
100
+ return true;
101
+ }
102
+ }
103
+ else {
104
+ editor.showLoadingWarning(0);
105
+ const onprogress = (evt) => {
106
+ editor.showLoadingWarning(evt.loaded / evt.total);
107
+ };
108
+ try {
109
+ const data = await (0, fileToBase64Url_1.default)(file, { onprogress });
110
+ if (sendPasteEvent(mime, data)) {
111
+ event?.preventDefault();
112
+ editor.hideLoadingWarning();
113
+ return true;
114
+ }
115
+ }
116
+ catch (e) {
117
+ console.error('Error reading image:', e);
118
+ }
119
+ editor.hideLoadingWarning();
120
+ }
121
+ }
122
+ return false;
123
+ };
124
+ for (const mime of supportedMIMEs) {
125
+ if (await handleMIME(mime)) {
126
+ return true;
127
+ }
128
+ }
129
+ return false;
130
+ }
131
+ /**
132
+ * Copies text from the editor associated with this.
133
+ *
134
+ * Even if `event` is provided, the `navigator.clipboard` API may be used if image data
135
+ * is to be copied. This is done because `ClipboardEvent`s seem to not support attaching
136
+ * images.
137
+ */
138
+ copy(event) {
139
+ const mimeToData = Object.create(null);
140
+ if (this.editor.toolController.dispatchInputEvent({
141
+ kind: inputEvents_1.InputEvtType.CopyEvent,
142
+ setData: (mime, data) => {
143
+ mimeToData[mime] = data;
144
+ },
145
+ })) {
146
+ event?.preventDefault();
147
+ }
148
+ const mimeTypes = Object.keys(mimeToData);
149
+ const hasNonTextMimeTypes = mimeTypes.some(mime => !isTextMimeType(mime));
150
+ const copyToEvent = () => {
151
+ if (!event) {
152
+ throw new Error('Unable to paste -- no event provided.');
153
+ }
154
+ for (const key in mimeToData) {
155
+ const value = mimeToData[key];
156
+ if (typeof value === 'string') {
157
+ event.clipboardData?.setData(key, value);
158
+ }
159
+ }
160
+ };
161
+ const copyToClipboardApi = () => {
162
+ const mappedMimeToData = Object.create(null);
163
+ const mimeMapping = {
164
+ // image/svg+xml is unsupported in Chrome.
165
+ 'image/svg+xml': 'text/html',
166
+ };
167
+ for (const key in mimeToData) {
168
+ const data = mimeToData[key];
169
+ const mappedKey = mimeMapping[key] || key;
170
+ if (typeof data === 'string') {
171
+ mappedMimeToData[mappedKey] = new Blob([new TextEncoder().encode(data)], { type: mappedKey });
172
+ }
173
+ else {
174
+ mappedMimeToData[mappedKey] = data;
175
+ }
176
+ }
177
+ return navigator.clipboard.write([new ClipboardItem(mappedMimeToData)]);
178
+ };
179
+ const supportsClipboardApi = (typeof ClipboardItem !== 'undefined'
180
+ && typeof navigator?.clipboard?.write !== 'undefined');
181
+ if (!__classPrivateFieldGet(this, _ClipboardHandler_preferClipboardEvents, "f") && supportsClipboardApi && (hasNonTextMimeTypes || !event)) {
182
+ let clipboardApiPromise = null;
183
+ const fallBackToCopyEvent = (reason) => {
184
+ console.warn('Unable to copy to the clipboard API. Future calls to .copy will use ClipboardEvents if possible.', reason);
185
+ __classPrivateFieldSet(this, _ClipboardHandler_preferClipboardEvents, true, "f");
186
+ copyToEvent();
187
+ };
188
+ try {
189
+ clipboardApiPromise = copyToClipboardApi();
190
+ }
191
+ catch (error) {
192
+ fallBackToCopyEvent(error);
193
+ }
194
+ if (clipboardApiPromise) {
195
+ return clipboardApiPromise.catch(fallBackToCopyEvent);
196
+ }
197
+ }
198
+ else {
199
+ copyToEvent();
200
+ }
201
+ return Promise.resolve();
202
+ }
203
+ }
204
+ _ClipboardHandler_preferClipboardEvents = new WeakMap();
205
+ exports.default = ClipboardHandler;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Contains the current version of the library -- used
3
+ * internaly (e.g. for documentation).
4
+ * @internal
5
+ */
1
6
  declare const _default: {
2
7
  number: string;
3
8
  };
@@ -1,5 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Contains the current version of the library -- used
5
+ * internaly (e.g. for documentation).
6
+ * @internal
7
+ */
3
8
  exports.default = {
4
- number: '1.16.1',
9
+ number: '1.18.0',
5
10
  };
@@ -11,12 +11,14 @@ import Pointer from './Pointer';
11
11
  import { EditorLocalization } from './localization';
12
12
  import IconProvider from './toolbar/IconProvider';
13
13
  import AbstractComponent from './components/AbstractComponent';
14
+ import { BackgroundType } from './components/BackgroundComponent';
14
15
  import KeyboardShortcutManager from './shortcuts/KeyboardShortcutManager';
15
16
  import KeyBinding from './shortcuts/KeyBinding';
16
17
  import AbstractToolbar from './toolbar/AbstractToolbar';
17
18
  import RenderablePathSpec from './rendering/RenderablePathSpec';
18
19
  import { AboutDialogEntry } from './dialogs/makeAboutDialog';
19
20
  import ReactiveValue, { MutableReactiveValue } from './util/ReactiveValue';
21
+ import { PenTypeRecord } from './toolbar/widgets/PenToolWidget';
20
22
  /**
21
23
  * Provides settings to an instance of an editor. See the Editor {@link Editor.constructor}.
22
24
  *
@@ -73,6 +75,33 @@ export interface EditorSettings {
73
75
  description?: string;
74
76
  version?: string;
75
77
  } | null;
78
+ /**
79
+ * Configures the default pen tools.
80
+ *
81
+ * **Example**:
82
+ * [[include:doc-pages/inline-examples/editor-settings-polyline-pen.md]]
83
+ */
84
+ pens: {
85
+ /**
86
+ * Additional pen types that can be selected in a toolbar.
87
+ */
88
+ additionalPenTypes?: readonly Readonly<PenTypeRecord>[];
89
+ /**
90
+ * Should return `true` if a pen type should be shown in the toolbar.
91
+ *
92
+ * @example
93
+ * ```ts,runnable
94
+ * import {Editor} from 'js-draw';
95
+ * const editor = new Editor(document.body, {
96
+ * // Only allow selecting the polyline pen from the toolbar.
97
+ * pens: { filterPenTypes: p => p.id === 'polyline-pen' },
98
+ * });
99
+ * editor.addToolbar();
100
+ * ```
101
+ * Notice that this setting only affects the toolbar GUI.
102
+ */
103
+ filterPenTypes?: (penType: PenTypeRecord) => boolean;
104
+ } | null;
76
105
  }
77
106
  /**
78
107
  * The main entrypoint for the full editor.
@@ -92,7 +121,7 @@ export interface EditorSettings {
92
121
  * ```
93
122
  *
94
123
  * See also
95
- * [`docs/example/example.ts`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/demo/example.ts).
124
+ * * [`examples.md`](https://github.com/personalizedrefrigerator/js-draw/blob/main/docs/examples.md).
96
125
  */
97
126
  export declare class Editor {
98
127
  private container;
@@ -195,7 +224,7 @@ export declare class Editor {
195
224
  private accessibilityControlArea;
196
225
  private eventListenerTargets;
197
226
  private readOnly;
198
- private settings;
227
+ private readonly settings;
199
228
  /**
200
229
  * @example
201
230
  * ```ts,runnable
@@ -227,6 +256,12 @@ export declare class Editor {
227
256
  * ```
228
257
  */
229
258
  constructor(parent: HTMLElement, settings?: Partial<EditorSettings>);
259
+ /**
260
+ * @returns a shallow copy of the current settings of the editor.
261
+ *
262
+ * Do not modify.
263
+ */
264
+ getCurrentSettings(): Readonly<EditorSettings>;
230
265
  /**
231
266
  * @returns a reference to the editor's container.
232
267
  *
@@ -254,7 +289,7 @@ export declare class Editor {
254
289
  private registerListeners;
255
290
  private updateEditorSizeVariables;
256
291
  /** @internal */
257
- protected handleHTMLWheelEvent(event: WheelEvent): boolean | undefined;
292
+ handleHTMLWheelEvent(event: WheelEvent): boolean | undefined;
258
293
  private pointers;
259
294
  private getPointerList;
260
295
  /**
@@ -273,7 +308,7 @@ export declare class Editor {
273
308
  /** @internal */
274
309
  protected handleDrop(evt: DragEvent | ClipboardEvent): Promise<void>;
275
310
  /** @internal */
276
- protected handlePaste(evt: DragEvent | ClipboardEvent): Promise<void>;
311
+ protected handlePaste(evt: DragEvent | ClipboardEvent): Promise<boolean | undefined>;
277
312
  /**
278
313
  * Forward pointer events from `elem` to this editor. Such that right-click/right-click drag
279
314
  * events are also forwarded, `elem`'s contextmenu is disabled.
@@ -317,9 +352,9 @@ export declare class Editor {
317
352
  remove: () => void;
318
353
  };
319
354
  /** @internal */
320
- protected handleHTMLKeyDownEvent(htmlEvent: KeyboardEvent): boolean;
355
+ handleHTMLKeyDownEvent(htmlEvent: KeyboardEvent): boolean;
321
356
  /** @internal */
322
- protected handleHTMLKeyUpEvent(htmlEvent: KeyboardEvent): boolean;
357
+ handleHTMLKeyUpEvent(htmlEvent: KeyboardEvent): boolean;
323
358
  /**
324
359
  * Adds event listners for keypresses (and drop events) on `elem` and forwards those
325
360
  * events to the editor.
@@ -444,8 +479,11 @@ export declare class Editor {
444
479
  * This is a convenience method that creates **and applies** a single command.
445
480
  *
446
481
  * If `selectComponents` is true (the default), the components are selected.
482
+ *
483
+ * `actionDescription`, if given, should be a screenreader-friendly description of the
484
+ * reason components were added (e.g. "pasted").
447
485
  */
448
- addAndCenterComponents(components: AbstractComponent[], selectComponents?: boolean): Promise<void>;
486
+ addAndCenterComponents(components: AbstractComponent[], selectComponents?: boolean, actionDescription?: string): Promise<void>;
449
487
  /**
450
488
  * Get a data URL (e.g. as produced by `HTMLCanvasElement::toDataURL`).
451
489
  * If `format` is not `image/png`, a PNG image URL may still be returned (as in the
@@ -493,8 +531,38 @@ export declare class Editor {
493
531
  */
494
532
  loadFrom(loader: ImageLoader): Promise<void>;
495
533
  private getTopmostBackgroundComponent;
534
+ /**
535
+ * This is a convenience method for adding or updating the {@link BackgroundComponent}
536
+ * and {@link EditorImage.setAutoresizeEnabled} for the current image.
537
+ *
538
+ * If there are multiple {@link BackgroundComponent}s in the image, this only modifies
539
+ * the topmost such element.
540
+ *
541
+ * **Example**:
542
+ * ```ts,runnable
543
+ * import { Editor, Color4, BackgroundComponentBackgroundType } from 'js-draw';
544
+ * const editor = new Editor(document.body);
545
+ * editor.dispatch(editor.setBackgroundStyle({
546
+ * color: Color4.orange,
547
+ * type: BackgroundComponentBackgroundType.Grid,
548
+ * autoresize: true,
549
+ * }));
550
+ * ```
551
+ *
552
+ * To change the background size, see {@link EditorImage.setImportExportRect}.
553
+ */
554
+ setBackgroundStyle(style: {
555
+ color?: Color4;
556
+ type?: BackgroundType;
557
+ autoresize?: boolean;
558
+ }): Command;
496
559
  /**
497
560
  * Set the background color of the image.
561
+ *
562
+ * This is a convenience method for adding or updating the {@link BackgroundComponent}
563
+ * for the current image.
564
+ *
565
+ * @see {@link setBackgroundStyle}
498
566
  */
499
567
  setBackgroundColor(color: Color4): Command;
500
568
  /**