js-draw 1.3.1 → 1.4.1

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 (170) hide show
  1. package/README.md +2 -2
  2. package/dist/Editor.css +55 -13
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/Editor.d.ts +36 -3
  6. package/dist/cjs/Editor.js +63 -26
  7. package/dist/cjs/commands/Erase.js +1 -1
  8. package/dist/cjs/commands/UnresolvedCommand.d.ts +1 -1
  9. package/dist/cjs/components/AbstractComponent.d.ts +1 -1
  10. package/dist/cjs/components/AbstractComponent.js +1 -1
  11. package/dist/cjs/components/BackgroundComponent.d.ts +1 -1
  12. package/dist/cjs/components/BackgroundComponent.js +13 -7
  13. package/dist/cjs/components/SVGGlobalAttributesObject.d.ts +2 -2
  14. package/dist/cjs/components/SVGGlobalAttributesObject.js +10 -14
  15. package/dist/cjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  16. package/dist/cjs/{EditorImage.js → image/EditorImage.js} +51 -7
  17. package/dist/cjs/image/export/adjustExportedSVGSize.d.ts +6 -0
  18. package/dist/cjs/image/export/adjustExportedSVGSize.js +22 -0
  19. package/dist/cjs/image/export/editorImageToSVG.d.ts +8 -0
  20. package/dist/cjs/image/export/editorImageToSVG.js +63 -0
  21. package/dist/cjs/image/lib.d.ts +1 -0
  22. package/dist/cjs/image/lib.js +8 -0
  23. package/dist/cjs/lib.d.ts +1 -1
  24. package/dist/cjs/lib.js +2 -3
  25. package/dist/cjs/localizations/comments.d.ts +6 -0
  26. package/dist/cjs/localizations/comments.js +10 -0
  27. package/dist/cjs/localizations/es.js +68 -48
  28. package/dist/cjs/rendering/caching/RenderingCache.d.ts +1 -1
  29. package/dist/cjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  30. package/dist/cjs/rendering/caching/RenderingCacheNode.js +4 -3
  31. package/dist/cjs/rendering/renderers/AbstractRenderer.d.ts +1 -0
  32. package/dist/cjs/rendering/renderers/AbstractRenderer.js +8 -0
  33. package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +28 -1
  34. package/dist/cjs/rendering/renderers/SVGRenderer.js +58 -7
  35. package/dist/cjs/toolbar/AbstractToolbar.d.ts +11 -3
  36. package/dist/cjs/toolbar/AbstractToolbar.js +20 -6
  37. package/dist/cjs/toolbar/EdgeToolbar.js +5 -6
  38. package/dist/cjs/toolbar/IconProvider.d.ts +1 -0
  39. package/dist/cjs/toolbar/IconProvider.js +43 -0
  40. package/dist/cjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  41. package/dist/cjs/toolbar/widgets/ActionButtonWidget.js +19 -1
  42. package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  43. package/dist/cjs/toolbar/widgets/BaseToolWidget.js +3 -0
  44. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  45. package/dist/cjs/toolbar/widgets/BaseWidget.js +30 -2
  46. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +1 -1
  47. package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  48. package/dist/cjs/toolbar/widgets/HandToolWidget.js +6 -0
  49. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +1 -1
  50. package/dist/cjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  51. package/dist/cjs/toolbar/widgets/OverflowWidget.js +3 -0
  52. package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  53. package/dist/cjs/toolbar/widgets/SaveActionWidget.js +3 -0
  54. package/dist/cjs/tools/BaseTool.d.ts +3 -0
  55. package/dist/cjs/tools/BaseTool.js +13 -2
  56. package/dist/cjs/tools/FindTool.d.ts +1 -0
  57. package/dist/cjs/tools/FindTool.js +4 -1
  58. package/dist/cjs/tools/PanZoom.d.ts +1 -0
  59. package/dist/cjs/tools/PanZoom.js +4 -0
  60. package/dist/cjs/tools/Pen.d.ts +0 -1
  61. package/dist/cjs/tools/Pen.js +1 -4
  62. package/dist/cjs/tools/PipetteTool.d.ts +1 -0
  63. package/dist/cjs/tools/PipetteTool.js +3 -0
  64. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  65. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.js +3 -0
  66. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -0
  67. package/dist/cjs/tools/SelectionTool/Selection.js +44 -8
  68. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  69. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +26 -8
  70. package/dist/cjs/tools/SelectionTool/SelectionTool.js +5 -0
  71. package/dist/cjs/tools/SoundUITool.d.ts +1 -0
  72. package/dist/cjs/tools/SoundUITool.js +4 -1
  73. package/dist/cjs/tools/TextTool.js +2 -2
  74. package/dist/cjs/tools/ToolController.d.ts +2 -0
  75. package/dist/cjs/tools/ToolController.js +13 -2
  76. package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  77. package/dist/cjs/tools/ToolSwitcherShortcut.js +3 -0
  78. package/dist/cjs/types.d.ts +9 -4
  79. package/dist/cjs/types.js +4 -3
  80. package/dist/cjs/util/ReactiveValue.d.ts +1 -1
  81. package/dist/cjs/util/ReactiveValue.js +2 -2
  82. package/dist/cjs/version.js +1 -1
  83. package/dist/mjs/Editor.d.ts +36 -3
  84. package/dist/mjs/Editor.mjs +64 -27
  85. package/dist/mjs/Editor.toSVGAsync.test.d.ts +1 -0
  86. package/dist/mjs/commands/Erase.mjs +1 -1
  87. package/dist/mjs/commands/UnresolvedCommand.d.ts +1 -1
  88. package/dist/mjs/components/AbstractComponent.d.ts +1 -1
  89. package/dist/mjs/components/AbstractComponent.mjs +1 -1
  90. package/dist/mjs/components/BackgroundComponent.d.ts +1 -1
  91. package/dist/mjs/components/BackgroundComponent.mjs +13 -7
  92. package/dist/mjs/components/SVGGlobalAttributesObject.d.ts +2 -2
  93. package/dist/mjs/components/SVGGlobalAttributesObject.mjs +10 -14
  94. package/dist/mjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  95. package/dist/mjs/{EditorImage.mjs → image/EditorImage.mjs} +51 -7
  96. package/dist/mjs/image/EditorImage.test.d.ts +1 -0
  97. package/dist/mjs/image/export/adjustExportedSVGSize.d.ts +6 -0
  98. package/dist/mjs/image/export/adjustExportedSVGSize.mjs +20 -0
  99. package/dist/mjs/image/export/editorImageToSVG.d.ts +8 -0
  100. package/dist/mjs/image/export/editorImageToSVG.mjs +55 -0
  101. package/dist/mjs/image/lib.d.ts +1 -0
  102. package/dist/mjs/image/lib.mjs +1 -0
  103. package/dist/mjs/lib.d.ts +1 -1
  104. package/dist/mjs/lib.mjs +1 -1
  105. package/dist/mjs/localizations/comments.d.ts +6 -0
  106. package/dist/mjs/localizations/comments.mjs +8 -0
  107. package/dist/mjs/localizations/es.mjs +68 -48
  108. package/dist/mjs/rendering/caching/RenderingCache.d.ts +1 -1
  109. package/dist/mjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  110. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +4 -3
  111. package/dist/mjs/rendering/renderers/AbstractRenderer.d.ts +1 -0
  112. package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +8 -0
  113. package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +28 -1
  114. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +58 -7
  115. package/dist/mjs/toolbar/AbstractToolbar.d.ts +11 -3
  116. package/dist/mjs/toolbar/AbstractToolbar.mjs +20 -6
  117. package/dist/mjs/toolbar/EdgeToolbar.mjs +5 -6
  118. package/dist/mjs/toolbar/IconProvider.d.ts +1 -0
  119. package/dist/mjs/toolbar/IconProvider.mjs +43 -0
  120. package/dist/mjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  121. package/dist/mjs/toolbar/widgets/ActionButtonWidget.mjs +21 -2
  122. package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  123. package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +3 -0
  124. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  125. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +30 -2
  126. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +1 -1
  127. package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  128. package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +6 -0
  129. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +1 -1
  130. package/dist/mjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  131. package/dist/mjs/toolbar/widgets/OverflowWidget.mjs +3 -0
  132. package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  133. package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +3 -0
  134. package/dist/mjs/tools/BaseTool.d.ts +3 -0
  135. package/dist/mjs/tools/BaseTool.mjs +13 -2
  136. package/dist/mjs/tools/FindTool.d.ts +1 -0
  137. package/dist/mjs/tools/FindTool.mjs +4 -1
  138. package/dist/mjs/tools/PanZoom.d.ts +1 -0
  139. package/dist/mjs/tools/PanZoom.mjs +4 -0
  140. package/dist/mjs/tools/Pen.d.ts +0 -1
  141. package/dist/mjs/tools/Pen.mjs +1 -4
  142. package/dist/mjs/tools/PipetteTool.d.ts +1 -0
  143. package/dist/mjs/tools/PipetteTool.mjs +3 -0
  144. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  145. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.mjs +3 -0
  146. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -0
  147. package/dist/mjs/tools/SelectionTool/Selection.mjs +45 -9
  148. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  149. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +25 -7
  150. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +5 -0
  151. package/dist/mjs/tools/SoundUITool.d.ts +1 -0
  152. package/dist/mjs/tools/SoundUITool.mjs +4 -1
  153. package/dist/mjs/tools/TextTool.mjs +2 -2
  154. package/dist/mjs/tools/ToolController.d.ts +2 -0
  155. package/dist/mjs/tools/ToolController.mjs +13 -2
  156. package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  157. package/dist/mjs/tools/ToolSwitcherShortcut.mjs +3 -0
  158. package/dist/mjs/types.d.ts +9 -4
  159. package/dist/mjs/types.mjs +4 -3
  160. package/dist/mjs/util/ReactiveValue.d.ts +1 -1
  161. package/dist/mjs/util/ReactiveValue.mjs +2 -2
  162. package/dist/mjs/version.mjs +1 -1
  163. package/package.json +5 -5
  164. package/src/Editor.scss +6 -0
  165. package/src/toolbar/EdgeToolbar.scss +19 -2
  166. package/src/tools/SelectionTool/SelectionTool.scss +74 -0
  167. package/src/tools/tools.scss +1 -1
  168. package/src/tools/SelectionTool/SelectionTool.css +0 -35
  169. /package/dist/cjs/{EditorImage.test.d.ts → Editor.toSVGAsync.test.d.ts} +0 -0
  170. /package/dist/{mjs → cjs/image}/EditorImage.test.d.ts +0 -0
@@ -40,13 +40,16 @@ const Erase_1 = __importDefault(require("../../commands/Erase"));
40
40
  const Duplicate_1 = __importDefault(require("../../commands/Duplicate"));
41
41
  const TransformMode_1 = require("./TransformMode");
42
42
  const types_1 = require("./types");
43
- const EditorImage_1 = __importDefault(require("../../EditorImage"));
43
+ const EditorImage_1 = __importDefault(require("../../image/EditorImage"));
44
44
  const updateChunkSize = 100;
45
45
  const maxPreviewElemCount = 500;
46
46
  // @internal
47
47
  class Selection {
48
48
  constructor(startPoint, editor) {
49
49
  this.editor = editor;
50
+ // The last-computed bounding box of selected content
51
+ // @see getTightBoundingBox
52
+ this.selectionTightBoundingBox = null;
50
53
  this.transform = math_1.Mat33.identity;
51
54
  this.selectedElems = [];
52
55
  this.hasParent = true;
@@ -65,10 +68,23 @@ class Selection {
65
68
  this.backgroundElem = document.createElement('div');
66
69
  this.backgroundElem.classList.add(`${SelectionTool_1.cssPrefix}selection-background`);
67
70
  this.container.appendChild(this.backgroundElem);
68
- const resizeHorizontalHandle = new SelectionHandle_1.default(SelectionHandle_1.HandleShape.Square, math_1.Vec2.of(1, 0.5), this, this.editor.viewport, (startPoint) => this.transformers.resize.onDragStart(startPoint, types_1.ResizeMode.HorizontalOnly), (currentPoint) => this.transformers.resize.onDragUpdate(currentPoint), () => this.transformers.resize.onDragEnd());
69
- const resizeVerticalHandle = new SelectionHandle_1.default(SelectionHandle_1.HandleShape.Square, math_1.Vec2.of(0.5, 1), this, this.editor.viewport, (startPoint) => this.transformers.resize.onDragStart(startPoint, types_1.ResizeMode.VerticalOnly), (currentPoint) => this.transformers.resize.onDragUpdate(currentPoint), () => this.transformers.resize.onDragEnd());
70
- const resizeBothHandle = new SelectionHandle_1.default(SelectionHandle_1.HandleShape.Square, math_1.Vec2.of(1, 1), this, this.editor.viewport, (startPoint) => this.transformers.resize.onDragStart(startPoint, types_1.ResizeMode.Both), (currentPoint) => this.transformers.resize.onDragUpdate(currentPoint), () => this.transformers.resize.onDragEnd());
71
- const rotationHandle = new SelectionHandle_1.default(SelectionHandle_1.HandleShape.Circle, math_1.Vec2.of(0.5, 0), this, this.editor.viewport, (startPoint) => this.transformers.rotate.onDragStart(startPoint), (currentPoint) => this.transformers.rotate.onDragUpdate(currentPoint), () => this.transformers.rotate.onDragEnd());
71
+ const resizeHorizontalHandle = new SelectionHandle_1.default({
72
+ action: SelectionHandle_1.HandleAction.ResizeX,
73
+ side: math_1.Vec2.of(1, 0.5),
74
+ }, this, this.editor.viewport, (startPoint) => this.transformers.resize.onDragStart(startPoint, types_1.ResizeMode.HorizontalOnly), (currentPoint) => this.transformers.resize.onDragUpdate(currentPoint), () => this.transformers.resize.onDragEnd());
75
+ const resizeVerticalHandle = new SelectionHandle_1.default({
76
+ action: SelectionHandle_1.HandleAction.ResizeY,
77
+ side: math_1.Vec2.of(0.5, 1),
78
+ }, this, this.editor.viewport, (startPoint) => this.transformers.resize.onDragStart(startPoint, types_1.ResizeMode.VerticalOnly), (currentPoint) => this.transformers.resize.onDragUpdate(currentPoint), () => this.transformers.resize.onDragEnd());
79
+ const resizeBothHandle = new SelectionHandle_1.default({
80
+ action: SelectionHandle_1.HandleAction.ResizeXY,
81
+ side: math_1.Vec2.of(1, 1),
82
+ }, this, this.editor.viewport, (startPoint) => this.transformers.resize.onDragStart(startPoint, types_1.ResizeMode.Both), (currentPoint) => this.transformers.resize.onDragUpdate(currentPoint), () => this.transformers.resize.onDragEnd());
83
+ const rotationHandle = new SelectionHandle_1.default({
84
+ action: SelectionHandle_1.HandleAction.Rotate,
85
+ side: math_1.Vec2.of(0.5, 0),
86
+ icon: this.editor.icons.makeRotateIcon(),
87
+ }, this, this.editor.viewport, (startPoint) => this.transformers.rotate.onDragStart(startPoint), (currentPoint) => this.transformers.rotate.onDragUpdate(currentPoint), () => this.transformers.rotate.onDragEnd());
72
88
  this.handles = [
73
89
  resizeBothHandle,
74
90
  resizeHorizontalHandle,
@@ -195,18 +211,26 @@ class Selection {
195
211
  // Returns false if the selection is empty.
196
212
  recomputeRegion() {
197
213
  const newRegion = this.computeTightBoundingBox();
214
+ this.selectionTightBoundingBox = newRegion;
198
215
  if (!newRegion) {
199
216
  this.cancelSelection();
200
217
  return false;
201
218
  }
202
219
  this.originalRegion = newRegion;
220
+ this.padRegion();
221
+ return true;
222
+ }
223
+ // Applies padding to the current region if it is too small.
224
+ // @internal
225
+ padRegion() {
226
+ const sourceRegion = this.selectionTightBoundingBox ?? this.originalRegion;
203
227
  const minSize = this.getMinCanvasSize();
204
- if (this.originalRegion.w < minSize || this.originalRegion.h < minSize) {
228
+ if (sourceRegion.w < minSize || sourceRegion.h < minSize) {
205
229
  // Add padding
206
230
  const padding = minSize / 2;
207
- this.originalRegion = math_1.Rect2.bboxOf(this.originalRegion.corners, padding);
231
+ this.originalRegion = math_1.Rect2.bboxOf(sourceRegion.corners, padding);
232
+ this.updateUI();
208
233
  }
209
- return true;
210
234
  }
211
235
  getMinCanvasSize() {
212
236
  const canvasHandleSize = SelectionHandle_1.handleSize / this.editor.viewport.getScaleFactor();
@@ -230,6 +254,14 @@ class Selection {
230
254
  const rotationDeg = this.screenRegionRotation * 180 / Math.PI;
231
255
  this.backgroundElem.style.transform = `rotate(${rotationDeg}deg)`;
232
256
  this.backgroundElem.style.transformOrigin = 'center';
257
+ // If closer to perpendicular, apply different CSS
258
+ const perpendicularClassName = `${SelectionTool_1.cssPrefix}rotated-near-perpendicular`;
259
+ if (Math.abs(Math.sin(this.screenRegionRotation)) > 0.5) {
260
+ this.container.classList.add(perpendicularClassName);
261
+ }
262
+ else {
263
+ this.container.classList.remove(perpendicularClassName);
264
+ }
233
265
  for (const handle of this.handles) {
234
266
  handle.updatePosition();
235
267
  }
@@ -401,6 +433,7 @@ class Selection {
401
433
  }
402
434
  setToPoint(point) {
403
435
  this.originalRegion = this.originalRegion.grownToPoint(point);
436
+ this.selectionTightBoundingBox = null;
404
437
  this.updateUI();
405
438
  }
406
439
  cancelSelection() {
@@ -408,12 +441,15 @@ class Selection {
408
441
  this.container.remove();
409
442
  }
410
443
  this.originalRegion = math_1.Rect2.empty;
444
+ this.selectionTightBoundingBox = null;
411
445
  this.hasParent = false;
412
446
  }
413
447
  setSelectedObjects(objects, bbox) {
414
448
  this.addRemoveSelectionFromImage(true);
415
449
  this.originalRegion = bbox;
450
+ this.selectionTightBoundingBox = bbox;
416
451
  this.selectedElems = objects.filter(object => object.isSelectable());
452
+ this.padRegion();
417
453
  this.updateUI();
418
454
  }
419
455
  getSelectedObjects() {
@@ -2,17 +2,23 @@ import { Point2, Vec2 } from '@js-draw/math';
2
2
  import Selection from './Selection';
3
3
  import Pointer from '../../Pointer';
4
4
  import Viewport from '../../Viewport';
5
- export declare enum HandleShape {
6
- Circle = 0,
7
- Square = 1
5
+ export declare enum HandleAction {
6
+ ResizeXY = "resize-xy",
7
+ Rotate = "rotate",
8
+ ResizeX = "resize-x",
9
+ ResizeY = "resize-y"
10
+ }
11
+ export interface HandlePresentation {
12
+ side: Vec2;
13
+ icon?: Element;
14
+ action: HandleAction;
8
15
  }
9
16
  export declare const handleSize = 30;
10
17
  export type DragStartCallback = (startPoint: Point2) => void;
11
18
  export type DragUpdateCallback = (canvasPoint: Point2) => void;
12
19
  export type DragEndCallback = () => void;
13
20
  export default class SelectionHandle {
14
- readonly shape: HandleShape;
15
- private readonly parentSide;
21
+ readonly presentation: HandlePresentation;
16
22
  private readonly parent;
17
23
  private readonly viewport;
18
24
  private readonly onDragStart;
@@ -20,7 +26,9 @@ export default class SelectionHandle {
20
26
  private readonly onDragEnd;
21
27
  private element;
22
28
  private snapToGrid;
23
- constructor(shape: HandleShape, parentSide: Vec2, parent: Selection, viewport: Viewport, onDragStart: DragStartCallback, onDragUpdate: DragUpdateCallback, onDragEnd: DragEndCallback);
29
+ private shape;
30
+ private parentSide;
31
+ constructor(presentation: HandlePresentation, parent: Selection, viewport: Viewport, onDragStart: DragStartCallback, onDragUpdate: DragUpdateCallback, onDragEnd: DragEndCallback);
24
32
  /**
25
33
  * Adds this to `container`, where `conatiner` should be the background/selection
26
34
  * element visible on the screen.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleSize = exports.HandleShape = void 0;
3
+ exports.handleSize = exports.HandleAction = void 0;
4
4
  const assertions_1 = require("../../util/assertions");
5
5
  const math_1 = require("@js-draw/math");
6
6
  const SelectionTool_1 = require("./SelectionTool");
@@ -8,12 +8,18 @@ var HandleShape;
8
8
  (function (HandleShape) {
9
9
  HandleShape[HandleShape["Circle"] = 0] = "Circle";
10
10
  HandleShape[HandleShape["Square"] = 1] = "Square";
11
- })(HandleShape || (exports.HandleShape = HandleShape = {}));
11
+ })(HandleShape || (HandleShape = {}));
12
+ var HandleAction;
13
+ (function (HandleAction) {
14
+ HandleAction["ResizeXY"] = "resize-xy";
15
+ HandleAction["Rotate"] = "rotate";
16
+ HandleAction["ResizeX"] = "resize-x";
17
+ HandleAction["ResizeY"] = "resize-y";
18
+ })(HandleAction || (exports.HandleAction = HandleAction = {}));
12
19
  exports.handleSize = 30;
13
20
  class SelectionHandle {
14
- constructor(shape, parentSide, parent, viewport, onDragStart, onDragUpdate, onDragEnd) {
15
- this.shape = shape;
16
- this.parentSide = parentSide;
21
+ constructor(presentation, parent, viewport, onDragStart, onDragUpdate, onDragEnd) {
22
+ this.presentation = presentation;
17
23
  this.parent = parent;
18
24
  this.viewport = viewport;
19
25
  this.onDragStart = onDragStart;
@@ -21,8 +27,20 @@ class SelectionHandle {
21
27
  this.onDragEnd = onDragEnd;
22
28
  this.dragLastPos = null;
23
29
  this.element = document.createElement('div');
24
- this.element.classList.add(`${SelectionTool_1.cssPrefix}handle`);
25
- switch (shape) {
30
+ this.element.classList.add(`${SelectionTool_1.cssPrefix}handle`, `${SelectionTool_1.cssPrefix}${presentation.action}`);
31
+ this.parentSide = presentation.side;
32
+ const icon = presentation.icon;
33
+ if (icon) {
34
+ this.element.appendChild(icon);
35
+ icon.classList.add('icon');
36
+ }
37
+ if (presentation.action === HandleAction.Rotate) {
38
+ this.shape = HandleShape.Circle;
39
+ }
40
+ else {
41
+ this.shape = HandleShape.Square;
42
+ }
43
+ switch (this.shape) {
26
44
  case HandleShape.Circle:
27
45
  this.element.classList.add(`${SelectionTool_1.cssPrefix}circle`);
28
46
  break;
@@ -30,7 +48,7 @@ class SelectionHandle {
30
48
  this.element.classList.add(`${SelectionTool_1.cssPrefix}square`);
31
49
  break;
32
50
  default:
33
- (0, assertions_1.assertUnreachable)(shape);
51
+ (0, assertions_1.assertUnreachable)(this.shape);
34
52
  }
35
53
  this.updatePosition();
36
54
  }
@@ -33,6 +33,11 @@ class SelectionTool extends BaseTool_1.default {
33
33
  // The selection box could be using the wet ink display if its transformation
34
34
  // hasn't been finalized yet. Clear before updating the UI.
35
35
  this.editor.clearWetInk();
36
+ // If not currently selecting, ensure that the selection box
37
+ // is large enough.
38
+ if (!this.expandingSelectionBox) {
39
+ this.selectionBox?.padRegion();
40
+ }
36
41
  this.selectionBox?.updateUI();
37
42
  });
38
43
  this.editor.handleKeyEventsFrom(this.handleOverlay);
@@ -14,6 +14,7 @@ export default class SoundUITool extends BaseTool {
14
14
  private toggleButton;
15
15
  private toggleButtonContainer;
16
16
  constructor(editor: Editor, description: string);
17
+ canReceiveInputInReadOnlyEditor(): boolean;
17
18
  private updateToggleButtonText;
18
19
  setEnabled(enabled: boolean): void;
19
20
  onKeyPress(event: KeyPressEvent): boolean;
@@ -105,6 +105,9 @@ class SoundUITool extends BaseTool_1.default {
105
105
  this.updateToggleButtonText();
106
106
  editor.createHTMLOverlay(this.toggleButtonContainer);
107
107
  }
108
+ canReceiveInputInReadOnlyEditor() {
109
+ return true;
110
+ }
108
111
  updateToggleButtonText() {
109
112
  const containerEnabledClass = 'sound-ui-tool-enabled';
110
113
  if (this.isEnabled()) {
@@ -118,7 +121,7 @@ class SoundUITool extends BaseTool_1.default {
118
121
  }
119
122
  setEnabled(enabled) {
120
123
  super.setEnabled(enabled);
121
- if (!enabled) {
124
+ if (!this.isEnabled()) {
122
125
  this.soundFeedback?.close();
123
126
  this.soundFeedback = null;
124
127
  }
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const TextComponent_1 = __importDefault(require("../components/TextComponent"));
7
- const EditorImage_1 = __importDefault(require("../EditorImage"));
7
+ const EditorImage_1 = __importDefault(require("../image/EditorImage"));
8
8
  const math_1 = require("@js-draw/math");
9
9
  const Pointer_1 = require("../Pointer");
10
10
  const types_1 = require("../types");
@@ -193,7 +193,7 @@ class TextTool extends BaseTool_1.default {
193
193
  }
194
194
  setEnabled(enabled) {
195
195
  super.setEnabled(enabled);
196
- if (!enabled) {
196
+ if (!this.isEnabled()) {
197
197
  this.flushInput();
198
198
  }
199
199
  this.textEditOverlay.style.display = enabled ? 'block' : 'none';
@@ -9,6 +9,7 @@ export default class ToolController implements InputEventListener {
9
9
  private activeTool;
10
10
  private primaryToolGroup;
11
11
  private inputPipeline;
12
+ private isEditorReadOnly;
12
13
  /** @internal */
13
14
  constructor(editor: Editor, localization: ToolLocalization);
14
15
  setTools(tools: BaseTool[], primaryToolGroup?: ToolEnabledGroup): void;
@@ -29,4 +30,5 @@ export default class ToolController implements InputEventListener {
29
30
  */
30
31
  addInputMapper(mapper: InputMapper): void;
31
32
  getMatchingTools<Type extends BaseTool>(type: new (...args: any[]) => Type): Type[];
33
+ onEditorDestroyed(): void;
32
34
  }
@@ -51,6 +51,7 @@ class ToolController {
51
51
  /** @internal */
52
52
  constructor(editor, localization) {
53
53
  this.activeTool = null;
54
+ this.isEditorReadOnly = editor.isReadOnlyReactiveValue();
54
55
  this.inputPipeline = new InputPipeline_1.default();
55
56
  this.inputPipeline.setEmitListener(event => this.onEventInternal(event));
56
57
  const primaryToolGroup = new ToolEnabledGroup_1.default();
@@ -135,6 +136,10 @@ class ToolController {
135
136
  }
136
137
  // @internal use `dispatchEvent` rather than calling `onEvent` directly.
137
138
  onEventInternal(event) {
139
+ const isEditorReadOnly = this.isEditorReadOnly.get();
140
+ const canToolReceiveInput = (tool) => {
141
+ return tool.isEnabled() && (!isEditorReadOnly || tool.canReceiveInputInReadOnlyEditor());
142
+ };
138
143
  let handled = false;
139
144
  if (event.kind === inputEvents_1.InputEvtType.PointerDownEvt) {
140
145
  let canOnlySendToActiveTool = false;
@@ -145,7 +150,7 @@ class ToolController {
145
150
  if (canOnlySendToActiveTool && tool !== this.activeTool) {
146
151
  continue;
147
152
  }
148
- if (tool.isEnabled() && tool.onEvent(event)) {
153
+ if (canToolReceiveInput(tool) && tool.onEvent(event)) {
149
154
  if (this.activeTool !== tool) {
150
155
  this.activeTool?.onEvent({ kind: inputEvents_1.InputEvtType.GestureCancelEvt });
151
156
  }
@@ -179,7 +184,7 @@ class ToolController {
179
184
  }
180
185
  else {
181
186
  for (const tool of this.tools) {
182
- if (!tool.isEnabled()) {
187
+ if (!canToolReceiveInput(tool)) {
183
188
  continue;
184
189
  }
185
190
  handled = tool.onEvent(event);
@@ -213,5 +218,11 @@ class ToolController {
213
218
  getMatchingTools(type) {
214
219
  return this.tools.filter(tool => tool instanceof type);
215
220
  }
221
+ // @internal
222
+ onEditorDestroyed() {
223
+ for (const tool of this.tools) {
224
+ tool.onDestroy();
225
+ }
226
+ }
216
227
  }
217
228
  exports.default = ToolController;
@@ -12,5 +12,6 @@ import BaseTool from './BaseTool';
12
12
  export default class ToolSwitcherShortcut extends BaseTool {
13
13
  private editor;
14
14
  constructor(editor: Editor);
15
+ canReceiveInputInReadOnlyEditor(): boolean;
15
16
  onKeyPress({ key }: KeyPressEvent): boolean;
16
17
  }
@@ -17,6 +17,9 @@ class ToolSwitcherShortcut extends BaseTool_1.default {
17
17
  super(editor.notifier, editor.localization.changeTool);
18
18
  this.editor = editor;
19
19
  }
20
+ canReceiveInputInReadOnlyEditor() {
21
+ return true;
22
+ }
20
23
  // @internal
21
24
  onKeyPress({ key }) {
22
25
  const toolController = this.editor.toolController;
@@ -16,12 +16,13 @@ export declare enum EditorEventType {
16
16
  ViewportChanged = 7,
17
17
  DisplayResized = 8,
18
18
  SelectionUpdated = 9,
19
+ ReadOnlyModeToggled = 10,
19
20
  /** @internal */
20
- ColorPickerToggled = 10,
21
+ ColorPickerToggled = 11,
21
22
  /** @internal */
22
- ColorPickerColorSelected = 11,
23
+ ColorPickerColorSelected = 12,
23
24
  /** @deprecated @internal */
24
- ToolbarDropdownShown = 12
25
+ ToolbarDropdownShown = 13
25
26
  }
26
27
  export declare enum UndoEventType {
27
28
  CommandDone = 0,
@@ -66,6 +67,10 @@ export interface SelectionUpdated {
66
67
  readonly selectedComponents: AbstractComponent[];
67
68
  readonly tool: BaseTool;
68
69
  }
70
+ export interface ReadOnlyToggled {
71
+ readonly kind: EditorEventType.ReadOnlyModeToggled;
72
+ readonly editorIsReadOnly: boolean;
73
+ }
69
74
  export interface ColorPickerToggled {
70
75
  readonly kind: EditorEventType.ColorPickerToggled;
71
76
  readonly open: boolean;
@@ -79,7 +84,7 @@ export interface ToolbarDropdownShownEvent {
79
84
  readonly fromToplevelDropdown: boolean;
80
85
  readonly layoutManager: WidgetContentLayoutManager;
81
86
  }
82
- export type EditorEventDataType = EditorToolEvent | EditorObjectEvent | EditorViewportChangedEvent | DisplayResizedEvent | EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent | SelectionUpdated | ColorPickerToggled | ColorPickerColorSelected | ToolbarDropdownShownEvent;
87
+ export type EditorEventDataType = EditorToolEvent | EditorObjectEvent | EditorViewportChangedEvent | DisplayResizedEvent | EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent | SelectionUpdated | ReadOnlyToggled | ColorPickerToggled | ColorPickerColorSelected | ToolbarDropdownShownEvent;
83
88
  export type OnProgressListener = (amountProcessed: number, totalToProcess: number) => Promise<void> | null | void;
84
89
  export type ComponentAddedListener = (component: AbstractComponent) => Promise<void> | void;
85
90
  export type OnDetermineExportRectListener = (exportRect: Rect2, options?: {
package/dist/cjs/types.js CHANGED
@@ -14,12 +14,13 @@ var EditorEventType;
14
14
  EditorEventType[EditorEventType["ViewportChanged"] = 7] = "ViewportChanged";
15
15
  EditorEventType[EditorEventType["DisplayResized"] = 8] = "DisplayResized";
16
16
  EditorEventType[EditorEventType["SelectionUpdated"] = 9] = "SelectionUpdated";
17
+ EditorEventType[EditorEventType["ReadOnlyModeToggled"] = 10] = "ReadOnlyModeToggled";
17
18
  /** @internal */
18
- EditorEventType[EditorEventType["ColorPickerToggled"] = 10] = "ColorPickerToggled";
19
+ EditorEventType[EditorEventType["ColorPickerToggled"] = 11] = "ColorPickerToggled";
19
20
  /** @internal */
20
- EditorEventType[EditorEventType["ColorPickerColorSelected"] = 11] = "ColorPickerColorSelected";
21
+ EditorEventType[EditorEventType["ColorPickerColorSelected"] = 12] = "ColorPickerColorSelected";
21
22
  /** @deprecated @internal */
22
- EditorEventType[EditorEventType["ToolbarDropdownShown"] = 12] = "ToolbarDropdownShown";
23
+ EditorEventType[EditorEventType["ToolbarDropdownShown"] = 13] = "ToolbarDropdownShown";
23
24
  })(EditorEventType || (exports.EditorEventType = EditorEventType = {}));
24
25
  // Types of `EditorUndoStackUpdated` events.
25
26
  var UndoEventType;
@@ -55,7 +55,7 @@ export declare abstract class ReactiveValue<T> {
55
55
  }
56
56
  export declare abstract class MutableReactiveValue<T> extends ReactiveValue<T> {
57
57
  /**
58
- * Changes the value of this and fires all update listeners.
58
+ * Changes the value of this and, if different, fires all update listeners.
59
59
  *
60
60
  * @see {@link onUpdate}
61
61
  */
@@ -59,7 +59,7 @@ class ReactiveValue {
59
59
  */
60
60
  static fromCallback(callback, sourceValues) {
61
61
  const result = new ReactiveValueImpl(callback());
62
- const resultRef = window.WeakRef ? new WeakRef(result) : { deref: () => result };
62
+ const resultRef = window.WeakRef ? new window.WeakRef(result) : { deref: () => result };
63
63
  for (const value of sourceValues) {
64
64
  const listener = value.onUpdate(() => {
65
65
  // Use resultRef to allow `result` to be garbage collected
@@ -98,7 +98,7 @@ exports.ReactiveValue = ReactiveValue;
98
98
  class MutableReactiveValue extends ReactiveValue {
99
99
  static fromProperty(sourceValue, propertyName) {
100
100
  const child = ReactiveValue.fromInitialValue(sourceValue.get()[propertyName]);
101
- const childRef = window.WeakRef ? new WeakRef(child) : { deref: () => child };
101
+ const childRef = window.WeakRef ? new window.WeakRef(child) : { deref: () => child };
102
102
  // When the source is updated...
103
103
  const sourceListener = sourceValue.onUpdate(newValue => {
104
104
  const childValue = childRef.deref();
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = {
4
- number: '1.3.1',
4
+ number: '1.4.1',
5
5
  };
@@ -1,4 +1,4 @@
1
- import EditorImage from './EditorImage';
1
+ import EditorImage from './image/EditorImage';
2
2
  import ToolController from './tools/ToolController';
3
3
  import { EditorNotifier, ImageLoader } from './types';
4
4
  import { HTMLPointerEventFilter, InputEvtType } from './inputEvents';
@@ -16,6 +16,7 @@ import KeyBinding from './shortcuts/KeyBinding';
16
16
  import AbstractToolbar from './toolbar/AbstractToolbar';
17
17
  import RenderablePathSpec from './rendering/RenderablePathSpec';
18
18
  import { AboutDialogEntry } from './dialogs/makeAboutDialog';
19
+ import ReactiveValue, { MutableReactiveValue } from './util/ReactiveValue';
19
20
  /**
20
21
  * Provides settings to an instance of an editor. See the Editor {@link Editor.constructor}.
21
22
  *
@@ -152,6 +153,7 @@ export declare class Editor {
152
153
  private accessibilityAnnounceArea;
153
154
  private accessibilityControlArea;
154
155
  private eventListenerTargets;
156
+ private readOnly;
155
157
  private settings;
156
158
  /**
157
159
  * @example
@@ -269,6 +271,13 @@ export declare class Editor {
269
271
  * passed to the editor.
270
272
  */
271
273
  handleKeyEventsFrom(elem: HTMLElement, filter?: (event: KeyboardEvent) => boolean): void;
274
+ /**
275
+ * Attempts to prevent **user-triggered** events from modifying
276
+ * the content of the image.
277
+ */
278
+ setReadOnly(readOnly: boolean): void;
279
+ isReadOnlyReactiveValue(): ReactiveValue<boolean>;
280
+ isReadOnly(): MutableReactiveValue<boolean>;
272
281
  /** `apply` a command. `command` will be announced for accessibility. */
273
282
  dispatch(command: Command, addToHistory?: boolean): void | Promise<void>;
274
283
  /**
@@ -386,10 +395,31 @@ export declare class Editor {
386
395
  /**
387
396
  * Converts the editor's content into an SVG image.
388
397
  *
398
+ * If the output SVG has width or height less than `options.minDimension`, its size
399
+ * will be increased.
400
+ *
389
401
  * @see
390
402
  * {@link SVGRenderer}
391
403
  */
392
- toSVG(): SVGElement;
404
+ toSVG(options?: {
405
+ minDimension?: number;
406
+ }): SVGElement;
407
+ /**
408
+ * Converts the editor's content into an SVG image in an asynchronous,
409
+ * but **potentially lossy** way.
410
+ *
411
+ * **Warning**: If the image is being edited during an async rendering, edited components
412
+ * may not be rendered.
413
+ *
414
+ * Like {@link toSVG}, but can be configured to briefly pause after processing every
415
+ * `pauseAfterCount` items. This can prevent the editor from becoming unresponsive
416
+ * when saving very large images.
417
+ */
418
+ toSVGAsync(options?: {
419
+ minDimension?: number;
420
+ pauseAfterCount?: number;
421
+ onProgress?: (processedCountInLayer: number, totalToProcessInLayer: number) => Promise<void | boolean>;
422
+ }): Promise<SVGElement>;
393
423
  /**
394
424
  * Load editor data from an `ImageLoader` (e.g. an {@link SVGLoader}).
395
425
  *
@@ -435,7 +465,10 @@ export declare class Editor {
435
465
  * Shows an information dialog with legal notices.
436
466
  */
437
467
  showAboutDialog(): void;
438
- /** Removes and destroys the editor */
468
+ /**
469
+ * Removes and **destroys** the editor. The editor cannot be added to a parent
470
+ * again after calling this method.
471
+ */
439
472
  remove(): void;
440
473
  }
441
474
  export default Editor;