js-draw 1.3.0 → 1.4.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 (164) hide show
  1. package/README.md +1 -1
  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/SVGLoader.js +37 -22
  8. package/dist/cjs/commands/Erase.js +1 -1
  9. package/dist/cjs/commands/UnresolvedCommand.d.ts +1 -1
  10. package/dist/cjs/components/AbstractComponent.d.ts +1 -1
  11. package/dist/cjs/components/AbstractComponent.js +1 -1
  12. package/dist/cjs/components/BackgroundComponent.d.ts +1 -1
  13. package/dist/cjs/components/BackgroundComponent.js +3 -2
  14. package/dist/cjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  15. package/dist/cjs/{EditorImage.js → image/EditorImage.js} +51 -7
  16. package/dist/cjs/image/export/editorImageToSVG.d.ts +8 -0
  17. package/dist/cjs/image/export/editorImageToSVG.js +49 -0
  18. package/dist/cjs/image/export/setExportedSVGSize.d.ts +6 -0
  19. package/dist/cjs/image/export/setExportedSVGSize.js +25 -0
  20. package/dist/cjs/image/lib.d.ts +1 -0
  21. package/dist/cjs/image/lib.js +8 -0
  22. package/dist/cjs/lib.d.ts +1 -1
  23. package/dist/cjs/lib.js +2 -3
  24. package/dist/cjs/localizations/comments.d.ts +6 -0
  25. package/dist/cjs/localizations/comments.js +10 -0
  26. package/dist/cjs/localizations/es.js +68 -48
  27. package/dist/cjs/rendering/caching/RenderingCache.d.ts +1 -1
  28. package/dist/cjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  29. package/dist/cjs/rendering/caching/RenderingCacheNode.js +4 -3
  30. package/dist/cjs/rendering/renderers/SVGRenderer.js +8 -19
  31. package/dist/cjs/rendering/renderers/SVGRenderer.test.d.ts +1 -0
  32. package/dist/cjs/toolbar/AbstractToolbar.d.ts +11 -3
  33. package/dist/cjs/toolbar/AbstractToolbar.js +20 -6
  34. package/dist/cjs/toolbar/EdgeToolbar.js +5 -6
  35. package/dist/cjs/toolbar/IconProvider.d.ts +1 -0
  36. package/dist/cjs/toolbar/IconProvider.js +43 -0
  37. package/dist/cjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  38. package/dist/cjs/toolbar/widgets/ActionButtonWidget.js +19 -1
  39. package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  40. package/dist/cjs/toolbar/widgets/BaseToolWidget.js +3 -0
  41. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  42. package/dist/cjs/toolbar/widgets/BaseWidget.js +30 -2
  43. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +1 -1
  44. package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  45. package/dist/cjs/toolbar/widgets/HandToolWidget.js +6 -0
  46. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +1 -1
  47. package/dist/cjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  48. package/dist/cjs/toolbar/widgets/OverflowWidget.js +3 -0
  49. package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  50. package/dist/cjs/toolbar/widgets/SaveActionWidget.js +3 -0
  51. package/dist/cjs/tools/BaseTool.d.ts +3 -0
  52. package/dist/cjs/tools/BaseTool.js +13 -2
  53. package/dist/cjs/tools/FindTool.d.ts +1 -0
  54. package/dist/cjs/tools/FindTool.js +4 -1
  55. package/dist/cjs/tools/PanZoom.d.ts +1 -0
  56. package/dist/cjs/tools/PanZoom.js +4 -0
  57. package/dist/cjs/tools/Pen.d.ts +0 -1
  58. package/dist/cjs/tools/Pen.js +1 -4
  59. package/dist/cjs/tools/PipetteTool.d.ts +1 -0
  60. package/dist/cjs/tools/PipetteTool.js +3 -0
  61. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  62. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.js +3 -0
  63. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -0
  64. package/dist/cjs/tools/SelectionTool/Selection.js +44 -8
  65. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  66. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +26 -8
  67. package/dist/cjs/tools/SelectionTool/SelectionTool.js +5 -0
  68. package/dist/cjs/tools/SoundUITool.d.ts +1 -0
  69. package/dist/cjs/tools/SoundUITool.js +4 -1
  70. package/dist/cjs/tools/TextTool.js +2 -2
  71. package/dist/cjs/tools/ToolController.d.ts +2 -0
  72. package/dist/cjs/tools/ToolController.js +13 -2
  73. package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  74. package/dist/cjs/tools/ToolSwitcherShortcut.js +3 -0
  75. package/dist/cjs/types.d.ts +9 -4
  76. package/dist/cjs/types.js +4 -3
  77. package/dist/cjs/util/ReactiveValue.d.ts +1 -1
  78. package/dist/cjs/util/ReactiveValue.js +2 -2
  79. package/dist/cjs/version.js +1 -1
  80. package/dist/mjs/Editor.d.ts +36 -3
  81. package/dist/mjs/Editor.mjs +64 -27
  82. package/dist/mjs/Editor.toSVGAsync.test.d.ts +1 -0
  83. package/dist/mjs/SVGLoader.mjs +37 -22
  84. package/dist/mjs/commands/Erase.mjs +1 -1
  85. package/dist/mjs/commands/UnresolvedCommand.d.ts +1 -1
  86. package/dist/mjs/components/AbstractComponent.d.ts +1 -1
  87. package/dist/mjs/components/AbstractComponent.mjs +1 -1
  88. package/dist/mjs/components/BackgroundComponent.d.ts +1 -1
  89. package/dist/mjs/components/BackgroundComponent.mjs +3 -2
  90. package/dist/mjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  91. package/dist/mjs/{EditorImage.mjs → image/EditorImage.mjs} +51 -7
  92. package/dist/mjs/image/EditorImage.test.d.ts +1 -0
  93. package/dist/mjs/image/export/editorImageToSVG.d.ts +8 -0
  94. package/dist/mjs/image/export/editorImageToSVG.mjs +41 -0
  95. package/dist/mjs/image/export/setExportedSVGSize.d.ts +6 -0
  96. package/dist/mjs/image/export/setExportedSVGSize.mjs +23 -0
  97. package/dist/mjs/image/lib.d.ts +1 -0
  98. package/dist/mjs/image/lib.mjs +1 -0
  99. package/dist/mjs/lib.d.ts +1 -1
  100. package/dist/mjs/lib.mjs +1 -1
  101. package/dist/mjs/localizations/comments.d.ts +6 -0
  102. package/dist/mjs/localizations/comments.mjs +8 -0
  103. package/dist/mjs/localizations/es.mjs +68 -48
  104. package/dist/mjs/rendering/caching/RenderingCache.d.ts +1 -1
  105. package/dist/mjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  106. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +4 -3
  107. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +8 -19
  108. package/dist/mjs/rendering/renderers/SVGRenderer.test.d.ts +1 -0
  109. package/dist/mjs/toolbar/AbstractToolbar.d.ts +11 -3
  110. package/dist/mjs/toolbar/AbstractToolbar.mjs +20 -6
  111. package/dist/mjs/toolbar/EdgeToolbar.mjs +5 -6
  112. package/dist/mjs/toolbar/IconProvider.d.ts +1 -0
  113. package/dist/mjs/toolbar/IconProvider.mjs +43 -0
  114. package/dist/mjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  115. package/dist/mjs/toolbar/widgets/ActionButtonWidget.mjs +21 -2
  116. package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  117. package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +3 -0
  118. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  119. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +30 -2
  120. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +1 -1
  121. package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  122. package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +6 -0
  123. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +1 -1
  124. package/dist/mjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  125. package/dist/mjs/toolbar/widgets/OverflowWidget.mjs +3 -0
  126. package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  127. package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +3 -0
  128. package/dist/mjs/tools/BaseTool.d.ts +3 -0
  129. package/dist/mjs/tools/BaseTool.mjs +13 -2
  130. package/dist/mjs/tools/FindTool.d.ts +1 -0
  131. package/dist/mjs/tools/FindTool.mjs +4 -1
  132. package/dist/mjs/tools/PanZoom.d.ts +1 -0
  133. package/dist/mjs/tools/PanZoom.mjs +4 -0
  134. package/dist/mjs/tools/Pen.d.ts +0 -1
  135. package/dist/mjs/tools/Pen.mjs +1 -4
  136. package/dist/mjs/tools/PipetteTool.d.ts +1 -0
  137. package/dist/mjs/tools/PipetteTool.mjs +3 -0
  138. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  139. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.mjs +3 -0
  140. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -0
  141. package/dist/mjs/tools/SelectionTool/Selection.mjs +45 -9
  142. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  143. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +25 -7
  144. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +5 -0
  145. package/dist/mjs/tools/SoundUITool.d.ts +1 -0
  146. package/dist/mjs/tools/SoundUITool.mjs +4 -1
  147. package/dist/mjs/tools/TextTool.mjs +2 -2
  148. package/dist/mjs/tools/ToolController.d.ts +2 -0
  149. package/dist/mjs/tools/ToolController.mjs +13 -2
  150. package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  151. package/dist/mjs/tools/ToolSwitcherShortcut.mjs +3 -0
  152. package/dist/mjs/types.d.ts +9 -4
  153. package/dist/mjs/types.mjs +4 -3
  154. package/dist/mjs/util/ReactiveValue.d.ts +1 -1
  155. package/dist/mjs/util/ReactiveValue.mjs +2 -2
  156. package/dist/mjs/version.mjs +1 -1
  157. package/package.json +5 -5
  158. package/src/Editor.scss +6 -0
  159. package/src/toolbar/EdgeToolbar.scss +19 -2
  160. package/src/tools/SelectionTool/SelectionTool.scss +74 -0
  161. package/src/tools/tools.scss +1 -1
  162. package/src/tools/SelectionTool/SelectionTool.css +0 -35
  163. /package/dist/cjs/{EditorImage.test.d.ts → Editor.toSVGAsync.test.d.ts} +0 -0
  164. /package/dist/{mjs → cjs/image}/EditorImage.test.d.ts +0 -0
@@ -1,11 +1,11 @@
1
- import AbstractRenderer from './rendering/renderers/AbstractRenderer';
2
- import Viewport from './Viewport';
3
- import AbstractComponent from './components/AbstractComponent';
1
+ import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
2
+ import Viewport from '../Viewport';
3
+ import AbstractComponent from '../components/AbstractComponent';
4
4
  import { Rect2 } from '@js-draw/math';
5
- import RenderingCache from './rendering/caching/RenderingCache';
6
- import SerializableCommand from './commands/SerializableCommand';
7
- import EventDispatcher from './EventDispatcher';
8
- import Command from './commands/Command';
5
+ import RenderingCache from '../rendering/caching/RenderingCache';
6
+ import SerializableCommand from '../commands/SerializableCommand';
7
+ import EventDispatcher from '../EventDispatcher';
8
+ import Command from '../commands/Command';
9
9
  export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
10
10
  export declare enum EditorImageEventType {
11
11
  ExportViewportChanged = 0,
@@ -14,6 +14,13 @@ export declare enum EditorImageEventType {
14
14
  export type EditorImageNotifier = EventDispatcher<EditorImageEventType, {
15
15
  image: EditorImage;
16
16
  }>;
17
+ /**
18
+ * A callback used to
19
+ * 1. pause the render process
20
+ * 2. observe progress through `componentsProcessed` and `totalComponents`
21
+ * 3. stop the render process early by returning `false`.
22
+ */
23
+ export type PreRenderComponentCallback = (component: AbstractComponent, componentsProcessed: number, totalComponents: number) => Promise<boolean>;
17
24
  export default class EditorImage {
18
25
  private root;
19
26
  private background;
@@ -36,7 +43,21 @@ export default class EditorImage {
36
43
  * the viewport used by the `renderer` (if any).
37
44
  */
38
45
  render(renderer: AbstractRenderer, viewport: Viewport | null): void;
39
- /** Renders all nodes, even ones not within the viewport. @internal */
46
+ /**
47
+ * Like {@link renderAll}, but can be stopped early and paused.
48
+ *
49
+ * **Note**: If the image is being edited during an async rendering, there is no
50
+ * guarantee that all nodes will be rendered correctly (some may be missing).
51
+ *
52
+ * @internal
53
+ */
54
+ renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>;
55
+ /**
56
+ * Renders all nodes, even ones not within the viewport.
57
+ *
58
+ * This can be slow for large images
59
+ * @internal
60
+ */
40
61
  renderAll(renderer: AbstractRenderer): void;
41
62
  /**
42
63
  * @returns all elements in the image, sorted by z-index. This can be slow for large images.
@@ -136,6 +157,7 @@ export declare class ImageNode {
136
157
  private rebalance;
137
158
  protected removeChild(child: ImageNode): void;
138
159
  remove(): void;
160
+ renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>;
139
161
  render(renderer: AbstractRenderer, visibleRect?: Rect2): void;
140
162
  renderDebugBoundingBoxes(renderer: AbstractRenderer, visibleRect: Rect2, depth?: number): void;
141
163
  }
@@ -32,13 +32,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32
32
  var _a, _b, _c;
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.RootImageNode = exports.ImageNode = exports.EditorImageEventType = exports.sortLeavesByZIndex = void 0;
35
- const Viewport_1 = __importDefault(require("./Viewport"));
36
- const AbstractComponent_1 = __importStar(require("./components/AbstractComponent"));
35
+ const Viewport_1 = __importDefault(require("../Viewport"));
36
+ const AbstractComponent_1 = __importStar(require("../components/AbstractComponent"));
37
37
  const math_1 = require("@js-draw/math");
38
- const SerializableCommand_1 = __importDefault(require("./commands/SerializableCommand"));
39
- const EventDispatcher_1 = __importDefault(require("./EventDispatcher"));
40
- const assertions_1 = require("./util/assertions");
41
- const Command_1 = __importDefault(require("./commands/Command"));
38
+ const SerializableCommand_1 = __importDefault(require("../commands/SerializableCommand"));
39
+ const EventDispatcher_1 = __importDefault(require("../EventDispatcher"));
40
+ const assertions_1 = require("../util/assertions");
41
+ const Command_1 = __importDefault(require("../commands/Command"));
42
42
  // @internal Sort by z-index, low to high
43
43
  const sortLeavesByZIndex = (leaves) => {
44
44
  leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
@@ -117,7 +117,27 @@ class EditorImage {
117
117
  this.background.render(renderer, viewport?.visibleRect);
118
118
  this.root.render(renderer, viewport?.visibleRect);
119
119
  }
120
- /** Renders all nodes, even ones not within the viewport. @internal */
120
+ /**
121
+ * Like {@link renderAll}, but can be stopped early and paused.
122
+ *
123
+ * **Note**: If the image is being edited during an async rendering, there is no
124
+ * guarantee that all nodes will be rendered correctly (some may be missing).
125
+ *
126
+ * @internal
127
+ */
128
+ async renderAllAsync(renderer, preRenderComponent) {
129
+ const stoppedEarly = !(await this.background.renderAllAsync(renderer, preRenderComponent));
130
+ if (!stoppedEarly) {
131
+ return await this.root.renderAllAsync(renderer, preRenderComponent);
132
+ }
133
+ return false;
134
+ }
135
+ /**
136
+ * Renders all nodes, even ones not within the viewport.
137
+ *
138
+ * This can be slow for large images
139
+ * @internal
140
+ */
121
141
  renderAll(renderer) {
122
142
  this.render(renderer, null);
123
143
  }
@@ -640,6 +660,30 @@ class ImageNode {
640
660
  this.content = null;
641
661
  this.children = [];
642
662
  }
663
+ // Creates a (potentially incomplete) async rendering of this image.
664
+ // Returns false if stopped early
665
+ async renderAllAsync(renderer,
666
+ // Used to pause/stop the renderer process
667
+ preRenderComponent) {
668
+ const leaves = this.getLeaves();
669
+ (0, exports.sortLeavesByZIndex)(leaves);
670
+ const totalLeaves = leaves.length;
671
+ for (let leafIndex = 0; leafIndex < totalLeaves; leafIndex++) {
672
+ const leaf = leaves[leafIndex];
673
+ const component = leaf.getContent();
674
+ // Even though leaf was originally a leaf, it might not be any longer --
675
+ // rendering is async and the tree can change during that time.
676
+ if (!component) {
677
+ continue;
678
+ }
679
+ const shouldContinue = await preRenderComponent(component, leafIndex, totalLeaves);
680
+ if (!shouldContinue) {
681
+ return false;
682
+ }
683
+ component.render(renderer, undefined);
684
+ }
685
+ return true;
686
+ }
643
687
  render(renderer, visibleRect) {
644
688
  let leaves;
645
689
  if (visibleRect) {
@@ -0,0 +1,8 @@
1
+ import EditorImage, { PreRenderComponentCallback } from '../EditorImage';
2
+ import { SVGSizingOptions } from './setExportedSVGSize';
3
+ export interface SVGExportOptions extends SVGSizingOptions {
4
+ sanitize?: boolean;
5
+ minDimension?: number;
6
+ }
7
+ export declare const editorImageToSVGSync: (image: EditorImage, options: SVGExportOptions) => SVGSVGElement;
8
+ export declare const editorImageToSVGAsync: (image: EditorImage, preRenderComponent: PreRenderComponentCallback, options: SVGExportOptions) => Promise<SVGElement>;
@@ -0,0 +1,49 @@
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
+ exports.editorImageToSVGAsync = exports.editorImageToSVGSync = void 0;
7
+ const math_1 = require("@js-draw/math");
8
+ const SVGRenderer_1 = __importDefault(require("../../rendering/renderers/SVGRenderer"));
9
+ const SVGLoader_1 = require("../../SVGLoader");
10
+ const setExportedSVGSize_1 = __importDefault(require("./setExportedSVGSize"));
11
+ const toSVGInternal = (image, renderFunction, options) => {
12
+ const importExportViewport = image.getImportExportViewport().getTemporaryClone();
13
+ const { element: result, renderer } = SVGRenderer_1.default.fromViewport(importExportViewport, options.sanitize ?? false);
14
+ const origTransform = importExportViewport.canvasToScreenTransform;
15
+ // Render with (0,0) at (0,0) — we'll handle translation with
16
+ // the viewBox property.
17
+ importExportViewport.resetTransform(math_1.Mat33.identity);
18
+ // Use a callback rather than async/await to allow this function to create
19
+ // both sync and async render functions
20
+ renderFunction(renderer, () => {
21
+ importExportViewport.resetTransform(origTransform);
22
+ if (image.getAutoresizeEnabled()) {
23
+ result.classList.add(SVGLoader_1.svgLoaderAutoresizeClassName);
24
+ }
25
+ else {
26
+ result.classList.remove(SVGLoader_1.svgLoaderAutoresizeClassName);
27
+ }
28
+ (0, setExportedSVGSize_1.default)(result, importExportViewport, options);
29
+ return result;
30
+ });
31
+ return result;
32
+ };
33
+ const editorImageToSVGSync = (image, options) => {
34
+ return toSVGInternal(image, (renderer, onComplete) => {
35
+ image.renderAll(renderer);
36
+ onComplete();
37
+ }, options);
38
+ };
39
+ exports.editorImageToSVGSync = editorImageToSVGSync;
40
+ const editorImageToSVGAsync = (image, preRenderComponent, options) => {
41
+ return new Promise(resolve => {
42
+ toSVGInternal(image, async (renderer, onComplete) => {
43
+ await image.renderAllAsync(renderer, preRenderComponent);
44
+ const result = onComplete();
45
+ resolve(result);
46
+ }, options);
47
+ });
48
+ };
49
+ exports.editorImageToSVGAsync = editorImageToSVGAsync;
@@ -0,0 +1,6 @@
1
+ import Viewport from '../../Viewport';
2
+ export type SVGSizingOptions = {
3
+ minDimension?: number;
4
+ };
5
+ declare const setExportedSVGSize: (svg: SVGElement, viewport: Viewport, options: SVGSizingOptions) => void;
6
+ export default setExportedSVGSize;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const math_1 = require("@js-draw/math");
4
+ // @internal
5
+ const setExportedSVGSize = (svg, viewport, options) => {
6
+ // Just show the main region
7
+ const rect = viewport.visibleRect;
8
+ svg.setAttribute('viewBox', [rect.x, rect.y, rect.w, rect.h].map(part => (0, math_1.toRoundedString)(part)).join(' '));
9
+ // Adjust the width/height as necessary
10
+ let width = rect.w;
11
+ let height = rect.h;
12
+ if (options?.minDimension && width < options.minDimension) {
13
+ const newWidth = options.minDimension;
14
+ height *= newWidth / (width || 1);
15
+ width = newWidth;
16
+ }
17
+ if (options?.minDimension && height < options.minDimension) {
18
+ const newHeight = options.minDimension;
19
+ width *= newHeight / (height || 1);
20
+ height = newHeight;
21
+ }
22
+ svg.setAttribute('width', (0, math_1.toRoundedString)(width));
23
+ svg.setAttribute('height', (0, math_1.toRoundedString)(height));
24
+ };
25
+ exports.default = setExportedSVGSize;
@@ -0,0 +1 @@
1
+ export { default as EditorImage } from './EditorImage';
@@ -0,0 +1,8 @@
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
+ exports.EditorImage = void 0;
7
+ var EditorImage_1 = require("./EditorImage");
8
+ Object.defineProperty(exports, "EditorImage", { enumerable: true, get: function () { return __importDefault(EditorImage_1).default; } });
package/dist/cjs/lib.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * @packageDocumentation
16
16
  */
17
17
  import Editor, { EditorSettings } from './Editor';
18
- export { default as EditorImage } from './EditorImage';
18
+ export * from './image/lib';
19
19
  export * from './types';
20
20
  export * from './inputEvents';
21
21
  export { default as getLocalizationTable, matchingLocalizationTable } from './localizations/getLocalizationTable';
package/dist/cjs/lib.js CHANGED
@@ -33,11 +33,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.HTMLToolbar = exports.AbstractToolbar = exports.Editor = exports.__js_draw__version = exports.UndoRedoHistory = exports.PointerDevice = exports.Pointer = exports.EventDispatcher = exports.Viewport = exports.SVGLoader = exports.matchingLocalizationTable = exports.getLocalizationTable = exports.EditorImage = void 0;
36
+ exports.HTMLToolbar = exports.AbstractToolbar = exports.Editor = exports.__js_draw__version = exports.UndoRedoHistory = exports.PointerDevice = exports.Pointer = exports.EventDispatcher = exports.Viewport = exports.SVGLoader = exports.matchingLocalizationTable = exports.getLocalizationTable = void 0;
37
37
  const Editor_1 = __importDefault(require("./Editor"));
38
38
  exports.Editor = Editor_1.default;
39
- var EditorImage_1 = require("./EditorImage");
40
- Object.defineProperty(exports, "EditorImage", { enumerable: true, get: function () { return __importDefault(EditorImage_1).default; } });
39
+ __exportStar(require("./image/lib"), exports);
41
40
  __exportStar(require("./types"), exports);
42
41
  __exportStar(require("./inputEvents"), exports);
43
42
  var getLocalizationTable_1 = require("./localizations/getLocalizationTable");
@@ -0,0 +1,6 @@
1
+ import { EditorLocalization } from '../localization';
2
+ /**
3
+ * Comments to help translators create translations.
4
+ */
5
+ declare const comments: Partial<Record<keyof EditorLocalization, string>>;
6
+ export default comments;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Comments to help translators create translations.
5
+ */
6
+ const comments = {
7
+ dragAndDropHereOrBrowse: 'Uses {{curly braces}} to denote bold text',
8
+ closeSidebar: 'Currently used as an accessibilty label',
9
+ };
10
+ exports.default = comments;
@@ -1,70 +1,90 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const localization_1 = require("../localization");
4
- // A partial Spanish localization.
4
+ // A partial Spanish localization, created with /scripts/markdownTranslationFormToTs.ts
5
5
  const localization = {
6
6
  ...localization_1.defaultEditorLocalization,
7
- // Strings for the main editor interface
8
- // (see src/localization.ts)
9
- loading: (percentage) => `Cargando: ${percentage}%...`,
10
- imageEditor: 'Editor de dibujos',
11
- undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`,
12
- redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`,
13
- undo: 'Deshace',
14
- redo: 'Rehace',
15
- // Strings for the toolbar
16
- // (see src/toolbar/localization.ts)
17
7
  pen: 'Lapiz',
18
8
  eraser: 'Borrador',
19
9
  select: 'Selecciona',
20
- thicknessLabel: 'Tamaño',
21
- colorLabel: 'Color',
22
- doneLoading: 'El cargado terminó',
23
- fontLabel: 'Fuente: ',
24
- anyDevicePanning: 'Mover la pantalla con todo dispotivo',
25
- touchPanning: 'Mover la pantalla con un dedo',
26
- touchPanTool: 'Instrumento de mover la pantalla con un dedo',
27
- outlinedRectanglePen: 'Rectángulo con nada más que un borde',
28
- filledRectanglePen: 'Rectángulo sin borde',
29
- linePen: 'Línea',
30
- arrowPen: 'Flecha',
31
- roundedTipPen: 'Lapiz Redondeado',
32
- selectPenTip: 'Forma de dibuja',
33
10
  handTool: 'Mover',
34
- zoom: 'Zoom',
11
+ image: 'Imagen',
12
+ chooseFile: 'Seleccionar archivo',
13
+ cancel: 'Cancelar',
35
14
  resetView: 'Reiniciar vista',
15
+ thicknessLabel: 'Tamaño',
16
+ fontLabel: 'Fuente:',
17
+ textSize: 'Tamaño',
36
18
  resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado',
37
19
  deleteSelection: 'Borra la selección',
38
20
  duplicateSelection: 'Duplica la selección',
21
+ exit: 'Salir',
22
+ save: 'Guardar',
23
+ undo: 'Deshace',
24
+ redo: 'Rehace',
25
+ selectPenTip: 'Punta',
26
+ selectShape: 'Forma',
39
27
  pickColorFromScreen: 'Selecciona un color de la pantalla',
40
28
  clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color',
41
- dropdownShown(toolName) {
42
- return `Menú por ${toolName} es visible`;
43
- },
44
- dropdownHidden: function (toolName) {
45
- return `Menú por ${toolName} fue ocultado`;
46
- },
47
- colorChangedAnnouncement: function (color) {
48
- return `Color fue cambiado a ${color}`;
49
- },
50
- keyboardPanZoom: 'Mover la pantalla con el teclado',
51
- penTool: function (penId) {
52
- return `Lapiz ${penId}`;
53
- },
29
+ documentProperties: 'Fondo',
30
+ backgroundColor: 'Color de fondo',
31
+ imageWidthOption: 'Ancho',
32
+ imageHeightOption: 'Alto',
33
+ toggleOverflow: 'Más',
34
+ touchPanning: 'Mover la pantalla con un dedo',
35
+ roundedTipPen: 'Lapiz Redondeado',
36
+ arrowPen: 'Flecha',
37
+ linePen: 'Línea',
38
+ outlinedRectanglePen: 'Rectángulo delineado',
39
+ filledRectanglePen: 'Rectángulo sin borde',
40
+ lockRotation: 'Bloquea rotación',
41
+ paste: 'Pegar',
42
+ closeSidebar: (toolName) => `Close sidebar for ${toolName}`,
43
+ dropdownShown: (toolName) => `Menú por ${toolName} es visible`,
44
+ dropdownHidden: (toolName) => { return `Menú por ${toolName} fue ocultado`; },
45
+ zoomLevel: (zoomPercent) => `Zoom: ${zoomPercent}%`,
46
+ colorChangedAnnouncement: (color) => { return `Color fue cambiado a ${color}`; },
47
+ imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`,
48
+ imageLoadError: (message) => `Error cargando imagen: ${message}`,
49
+ penTool: (penId) => { return `Lapiz ${penId}`; },
54
50
  selectionTool: 'Selecciona',
55
51
  eraserTool: 'Borrador',
52
+ touchPanTool: 'Instrumento de mover la pantalla con un dedo',
53
+ pipetteTool: 'Seleccione un color de la pantalla',
54
+ keyboardPanZoom: 'Mover la pantalla con el teclado',
56
55
  textTool: 'Texto',
57
56
  enterTextToInsert: 'Entra texto',
58
- textSize: 'Tamaño',
57
+ findLabel: 'Buscar',
58
+ toNextMatch: 'Próxima',
59
+ closeDialog: 'Cerrar',
60
+ focusedFoundText: (matchIdx, totalMatches) => `Viewing match ${matchIdx} of ${totalMatches}`,
61
+ anyDevicePanning: 'Mover la pantalla con todo dispotivo',
62
+ copied: (count, description) => `Copied ${count} ${description}`,
63
+ pasted: (count, description) => `Pasted ${count} ${description}`,
64
+ toolEnabledAnnouncement: (toolName) => `${toolName} enabled`,
65
+ toolDisabledAnnouncement: (toolName) => `${toolName} disabled`,
66
+ transformedElements: (elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`,
67
+ resizeOutputCommand: (newSize) => `Resized image to ${newSize.w}x${newSize.h}`,
68
+ addElementAction: (componentDescription) => `Added ${componentDescription}`,
69
+ eraseAction: (componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`,
70
+ duplicateAction: (componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`,
71
+ unionOf: (actionDescription, actionCount) => `Union: ${actionCount} ${actionDescription}`,
72
+ inverseOf: (actionDescription) => `Inverse of ${actionDescription}`,
73
+ rotatedBy: (degrees) => `Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}`,
74
+ selectedElements: (count) => `Selected ${count} element${count === 1 ? '' : 's'}`,
75
+ filledBackgroundWithColor: (color) => `Filled background (${color})`,
76
+ text: (text) => `Text object: ${text}`,
77
+ imageNode: (label) => `Image: ${label}`,
78
+ restyledElement: (elementDescription) => `Restyled ${elementDescription}`,
79
+ pathNodeCount: (count) => `There are ${count} visible path objects.`,
80
+ textNodeCount: (count) => `There are ${count} visible text nodes.`,
81
+ imageNodeCount: (nodeCount) => `There are ${nodeCount} visible image nodes.`,
82
+ textNode: (content) => `Text: ${content}`,
59
83
  rerenderAsText: 'Redibuja la pantalla al texto',
60
- lockRotation: 'Bloquea rotación',
61
- image: 'Imagen',
62
- imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`,
63
- imageLoadError: (message) => `Error cargando imagen: ${message}`,
64
- toggleOverflow: 'Más',
65
- documentProperties: 'Fondo',
66
- imageWidthOption: 'Ancho',
67
- imageHeightOption: 'Alto',
68
- backgroundColor: 'Color de fondo: '
84
+ loading: (percentage) => `Cargando: ${percentage}%...`,
85
+ imageEditor: 'Editor de dibujos',
86
+ doneLoading: 'El cargado terminó',
87
+ undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`,
88
+ redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`,
69
89
  };
70
90
  exports.default = localization;
@@ -1,4 +1,4 @@
1
- import { ImageNode } from '../../EditorImage';
1
+ import { ImageNode } from '../../image/EditorImage';
2
2
  import Viewport from '../../Viewport';
3
3
  import AbstractRenderer from '../renderers/AbstractRenderer';
4
4
  import { CacheProps } from './types';
@@ -1,4 +1,4 @@
1
- import { ImageNode } from '../../EditorImage';
1
+ import { ImageNode } from '../../image/EditorImage';
2
2
  import { Rect2 } from '@js-draw/math';
3
3
  import Viewport from '../../Viewport';
4
4
  import AbstractRenderer from '../renderers/AbstractRenderer';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // A cache record with sub-nodes.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const EditorImage_1 = require("../../EditorImage");
4
+ const EditorImage_1 = require("../../image/EditorImage");
5
5
  const math_1 = require("@js-draw/math");
6
6
  // 3x3 divisions for each node.
7
7
  const cacheDivisionSize = 3;
@@ -44,11 +44,12 @@ class RenderingCacheNode {
44
44
  // Generates children, if missing.
45
45
  generateChildren() {
46
46
  if (this.instantiatedChildren.length === 0) {
47
- const childRects = this.region.divideIntoGrid(cacheDivisionSize, cacheDivisionSize);
48
- if (this.region.size.x === 0 || this.region.size.y === 0) {
47
+ if (this.region.size.x / cacheDivisionSize === 0 || this.region.size.y / cacheDivisionSize === 0) {
49
48
  console.warn('Cache element has zero size! Not generating children.');
50
49
  return;
51
50
  }
51
+ const childRects = this.region.divideIntoGrid(cacheDivisionSize, cacheDivisionSize);
52
+ console.assert(childRects.length === cacheDivisionSize * cacheDivisionSize, 'Warning: divideIntoGrid created the wrong number of subrectangles!');
52
53
  for (const rect of childRects) {
53
54
  const child = new RenderingCacheNode(rect, this.cacheState);
54
55
  child.parent = this;
@@ -135,26 +135,18 @@ class SVGRenderer extends AbstractRenderer_1.default {
135
135
  }
136
136
  // Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
137
137
  // Otherwise, just uses a `matrix`.
138
- transformFrom(elemTransform, elem, inCanvasSpace = false, setXY = true) {
139
- let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
140
- const translation = transform.transformVec2(math_1.Vec2.zero);
141
- if (setXY) {
142
- transform = transform.rightMul(math_1.Mat33.translation(translation.times(-1)));
143
- }
138
+ transformFrom(elemTransform, elem, inCanvasSpace = false) {
139
+ const transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
144
140
  if (!transform.eq(math_1.Mat33.identity)) {
145
- elem.style.transform = `matrix(
146
- ${transform.a1}, ${transform.b1},
147
- ${transform.a2}, ${transform.b2},
148
- ${transform.a3}, ${transform.b3}
149
- )`;
141
+ const matrixString = transform.toCSSMatrix();
142
+ elem.style.transform = matrixString;
143
+ // Most browsers round the components of CSS transforms.
144
+ // Include a higher precision copy of the element's transform.
145
+ elem.setAttribute('data-highp-transform', matrixString);
150
146
  }
151
147
  else {
152
148
  elem.style.transform = '';
153
149
  }
154
- if (setXY) {
155
- elem.setAttribute('x', `${(0, math_1.toRoundedString)(translation.x)}`);
156
- elem.setAttribute('y', `${(0, math_1.toRoundedString)(translation.y)}`);
157
- }
158
150
  }
159
151
  drawText(text, transform, style) {
160
152
  const applyTextStyles = (elem, style) => {
@@ -190,10 +182,7 @@ class SVGRenderer extends AbstractRenderer_1.default {
190
182
  if (!this.textContainer) {
191
183
  const container = document.createElementNS(svgNameSpace, 'text');
192
184
  container.appendChild(document.createTextNode(text));
193
- // Don't set .x/.y properties (just use .style.transform).
194
- // Child nodes aren't translated by .x/.y properties, but are by .style.transform.
195
- const setXY = false;
196
- this.transformFrom(transform, container, true, setXY);
185
+ this.transformFrom(transform, container, true);
197
186
  applyTextStyles(container, style);
198
187
  this.elem.appendChild(container);
199
188
  this.objectElems?.push(container);
@@ -0,0 +1 @@
1
+ export {};
@@ -8,6 +8,10 @@ export interface SpacerOptions {
8
8
  minSize: string;
9
9
  maxSize: string;
10
10
  }
11
+ export type ToolbarActionButtonOptions = {
12
+ mustBeToplevel?: boolean;
13
+ autoDisableInReadOnlyEditors?: boolean;
14
+ };
11
15
  export default abstract class AbstractToolbar {
12
16
  #private;
13
17
  protected editor: Editor;
@@ -91,18 +95,22 @@ export default abstract class AbstractToolbar {
91
95
  * @see
92
96
  * {@link addActionButton}
93
97
  */
94
- protected makeActionButton(title: string | ActionButtonIcon, command: () => void, mustBeToplevel?: boolean): BaseWidget;
98
+ protected makeActionButton(title: string | ActionButtonIcon, command: () => void, options?: ToolbarActionButtonOptions | boolean): BaseWidget;
95
99
  /**
96
100
  * Adds an action button with `title` to this toolbar (or to the given `parent` element).
97
101
  *
102
+ * `options` can either be an object with properties `mustBeToplevel` and/or
103
+ * `autoDisableInReadOnlyEditors` or a boolean value. If a boolean, it is interpreted
104
+ * as being the value of `mustBeToplevel`.
105
+ *
98
106
  * @return The added button.
99
107
  */
100
- addActionButton(title: string | ActionButtonIcon, command: () => void, mustBeToplevel?: boolean): BaseWidget;
108
+ addActionButton(title: string | ActionButtonIcon, command: () => void, options?: ToolbarActionButtonOptions | boolean): BaseWidget;
101
109
  /**
102
110
  * Like {@link addActionButton}, except associates `tags` with the button that allow
103
111
  * different toolbar styles to give the button tag-dependent styles.
104
112
  */
105
- addTaggedActionButton(tags: (ToolbarWidgetTag | string)[], title: string | ActionButtonIcon, command: () => void, mustBeToplevel?: boolean): BaseWidget;
113
+ addTaggedActionButton(tags: (ToolbarWidgetTag | string)[], title: string | ActionButtonIcon, command: () => void, options?: ToolbarActionButtonOptions | boolean): BaseWidget;
106
114
  /**
107
115
  * Adds a save button that, when clicked, calls `saveCallback`.
108
116
  *
@@ -235,7 +235,15 @@ class AbstractToolbar {
235
235
  * @see
236
236
  * {@link addActionButton}
237
237
  */
238
- makeActionButton(title, command, mustBeToplevel = true) {
238
+ makeActionButton(title, command, options = true) {
239
+ // Parse options
240
+ if (typeof options === 'boolean') {
241
+ options = {
242
+ mustBeToplevel: options,
243
+ };
244
+ }
245
+ const mustBeToplevel = options.mustBeToplevel ?? true;
246
+ const autoDisableInReadOnlyEditors = options.autoDisableInReadOnlyEditors ?? true;
239
247
  const titleString = typeof title === 'string' ? title : title.label;
240
248
  const widgetId = 'action-button';
241
249
  const makeIcon = () => {
@@ -244,16 +252,20 @@ class AbstractToolbar {
244
252
  }
245
253
  return title.icon;
246
254
  };
247
- const widget = new ActionButtonWidget_1.default(this.editor, widgetId, makeIcon, titleString, command, this.editor.localization, mustBeToplevel);
255
+ const widget = new ActionButtonWidget_1.default(this.editor, widgetId, makeIcon, titleString, command, this.editor.localization, mustBeToplevel, autoDisableInReadOnlyEditors);
248
256
  return widget;
249
257
  }
250
258
  /**
251
259
  * Adds an action button with `title` to this toolbar (or to the given `parent` element).
252
260
  *
261
+ * `options` can either be an object with properties `mustBeToplevel` and/or
262
+ * `autoDisableInReadOnlyEditors` or a boolean value. If a boolean, it is interpreted
263
+ * as being the value of `mustBeToplevel`.
264
+ *
253
265
  * @return The added button.
254
266
  */
255
- addActionButton(title, command, mustBeToplevel = true) {
256
- const widget = this.makeActionButton(title, command, mustBeToplevel);
267
+ addActionButton(title, command, options = true) {
268
+ const widget = this.makeActionButton(title, command, options);
257
269
  this.addWidget(widget);
258
270
  return widget;
259
271
  }
@@ -261,8 +273,8 @@ class AbstractToolbar {
261
273
  * Like {@link addActionButton}, except associates `tags` with the button that allow
262
274
  * different toolbar styles to give the button tag-dependent styles.
263
275
  */
264
- addTaggedActionButton(tags, title, command, mustBeToplevel = true) {
265
- const widget = this.makeActionButton(title, command, mustBeToplevel);
276
+ addTaggedActionButton(tags, title, command, options = true) {
277
+ const widget = this.makeActionButton(title, command, options);
266
278
  widget.setTags(tags);
267
279
  this.addWidget(widget);
268
280
  return widget;
@@ -308,6 +320,8 @@ class AbstractToolbar {
308
320
  icon: this.editor.icons.makeCloseIcon(),
309
321
  }, () => {
310
322
  exitCallback();
323
+ }, {
324
+ autoDisableInReadOnlyEditors: false,
311
325
  });
312
326
  }
313
327
  /**