js-draw 1.3.1 → 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 (158) 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/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 +1 -1
  13. package/dist/cjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  14. package/dist/cjs/{EditorImage.js → image/EditorImage.js} +51 -7
  15. package/dist/cjs/image/export/editorImageToSVG.d.ts +8 -0
  16. package/dist/cjs/image/export/editorImageToSVG.js +49 -0
  17. package/dist/cjs/image/export/setExportedSVGSize.d.ts +6 -0
  18. package/dist/cjs/image/export/setExportedSVGSize.js +25 -0
  19. package/dist/cjs/image/lib.d.ts +1 -0
  20. package/dist/cjs/image/lib.js +8 -0
  21. package/dist/cjs/lib.d.ts +1 -1
  22. package/dist/cjs/lib.js +2 -3
  23. package/dist/cjs/localizations/comments.d.ts +6 -0
  24. package/dist/cjs/localizations/comments.js +10 -0
  25. package/dist/cjs/localizations/es.js +68 -48
  26. package/dist/cjs/rendering/caching/RenderingCache.d.ts +1 -1
  27. package/dist/cjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  28. package/dist/cjs/rendering/caching/RenderingCacheNode.js +4 -3
  29. package/dist/cjs/toolbar/AbstractToolbar.d.ts +11 -3
  30. package/dist/cjs/toolbar/AbstractToolbar.js +20 -6
  31. package/dist/cjs/toolbar/EdgeToolbar.js +5 -6
  32. package/dist/cjs/toolbar/IconProvider.d.ts +1 -0
  33. package/dist/cjs/toolbar/IconProvider.js +43 -0
  34. package/dist/cjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  35. package/dist/cjs/toolbar/widgets/ActionButtonWidget.js +19 -1
  36. package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  37. package/dist/cjs/toolbar/widgets/BaseToolWidget.js +3 -0
  38. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  39. package/dist/cjs/toolbar/widgets/BaseWidget.js +30 -2
  40. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +1 -1
  41. package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  42. package/dist/cjs/toolbar/widgets/HandToolWidget.js +6 -0
  43. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +1 -1
  44. package/dist/cjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  45. package/dist/cjs/toolbar/widgets/OverflowWidget.js +3 -0
  46. package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  47. package/dist/cjs/toolbar/widgets/SaveActionWidget.js +3 -0
  48. package/dist/cjs/tools/BaseTool.d.ts +3 -0
  49. package/dist/cjs/tools/BaseTool.js +13 -2
  50. package/dist/cjs/tools/FindTool.d.ts +1 -0
  51. package/dist/cjs/tools/FindTool.js +4 -1
  52. package/dist/cjs/tools/PanZoom.d.ts +1 -0
  53. package/dist/cjs/tools/PanZoom.js +4 -0
  54. package/dist/cjs/tools/Pen.d.ts +0 -1
  55. package/dist/cjs/tools/Pen.js +1 -4
  56. package/dist/cjs/tools/PipetteTool.d.ts +1 -0
  57. package/dist/cjs/tools/PipetteTool.js +3 -0
  58. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  59. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.js +3 -0
  60. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -0
  61. package/dist/cjs/tools/SelectionTool/Selection.js +44 -8
  62. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  63. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +26 -8
  64. package/dist/cjs/tools/SelectionTool/SelectionTool.js +5 -0
  65. package/dist/cjs/tools/SoundUITool.d.ts +1 -0
  66. package/dist/cjs/tools/SoundUITool.js +4 -1
  67. package/dist/cjs/tools/TextTool.js +2 -2
  68. package/dist/cjs/tools/ToolController.d.ts +2 -0
  69. package/dist/cjs/tools/ToolController.js +13 -2
  70. package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  71. package/dist/cjs/tools/ToolSwitcherShortcut.js +3 -0
  72. package/dist/cjs/types.d.ts +9 -4
  73. package/dist/cjs/types.js +4 -3
  74. package/dist/cjs/util/ReactiveValue.d.ts +1 -1
  75. package/dist/cjs/util/ReactiveValue.js +2 -2
  76. package/dist/cjs/version.js +1 -1
  77. package/dist/mjs/Editor.d.ts +36 -3
  78. package/dist/mjs/Editor.mjs +64 -27
  79. package/dist/mjs/Editor.toSVGAsync.test.d.ts +1 -0
  80. package/dist/mjs/commands/Erase.mjs +1 -1
  81. package/dist/mjs/commands/UnresolvedCommand.d.ts +1 -1
  82. package/dist/mjs/components/AbstractComponent.d.ts +1 -1
  83. package/dist/mjs/components/AbstractComponent.mjs +1 -1
  84. package/dist/mjs/components/BackgroundComponent.d.ts +1 -1
  85. package/dist/mjs/components/BackgroundComponent.mjs +1 -1
  86. package/dist/mjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  87. package/dist/mjs/{EditorImage.mjs → image/EditorImage.mjs} +51 -7
  88. package/dist/mjs/image/EditorImage.test.d.ts +1 -0
  89. package/dist/mjs/image/export/editorImageToSVG.d.ts +8 -0
  90. package/dist/mjs/image/export/editorImageToSVG.mjs +41 -0
  91. package/dist/mjs/image/export/setExportedSVGSize.d.ts +6 -0
  92. package/dist/mjs/image/export/setExportedSVGSize.mjs +23 -0
  93. package/dist/mjs/image/lib.d.ts +1 -0
  94. package/dist/mjs/image/lib.mjs +1 -0
  95. package/dist/mjs/lib.d.ts +1 -1
  96. package/dist/mjs/lib.mjs +1 -1
  97. package/dist/mjs/localizations/comments.d.ts +6 -0
  98. package/dist/mjs/localizations/comments.mjs +8 -0
  99. package/dist/mjs/localizations/es.mjs +68 -48
  100. package/dist/mjs/rendering/caching/RenderingCache.d.ts +1 -1
  101. package/dist/mjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  102. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +4 -3
  103. package/dist/mjs/toolbar/AbstractToolbar.d.ts +11 -3
  104. package/dist/mjs/toolbar/AbstractToolbar.mjs +20 -6
  105. package/dist/mjs/toolbar/EdgeToolbar.mjs +5 -6
  106. package/dist/mjs/toolbar/IconProvider.d.ts +1 -0
  107. package/dist/mjs/toolbar/IconProvider.mjs +43 -0
  108. package/dist/mjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  109. package/dist/mjs/toolbar/widgets/ActionButtonWidget.mjs +21 -2
  110. package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  111. package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +3 -0
  112. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  113. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +30 -2
  114. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +1 -1
  115. package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  116. package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +6 -0
  117. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +1 -1
  118. package/dist/mjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  119. package/dist/mjs/toolbar/widgets/OverflowWidget.mjs +3 -0
  120. package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  121. package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +3 -0
  122. package/dist/mjs/tools/BaseTool.d.ts +3 -0
  123. package/dist/mjs/tools/BaseTool.mjs +13 -2
  124. package/dist/mjs/tools/FindTool.d.ts +1 -0
  125. package/dist/mjs/tools/FindTool.mjs +4 -1
  126. package/dist/mjs/tools/PanZoom.d.ts +1 -0
  127. package/dist/mjs/tools/PanZoom.mjs +4 -0
  128. package/dist/mjs/tools/Pen.d.ts +0 -1
  129. package/dist/mjs/tools/Pen.mjs +1 -4
  130. package/dist/mjs/tools/PipetteTool.d.ts +1 -0
  131. package/dist/mjs/tools/PipetteTool.mjs +3 -0
  132. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  133. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.mjs +3 -0
  134. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -0
  135. package/dist/mjs/tools/SelectionTool/Selection.mjs +45 -9
  136. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  137. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +25 -7
  138. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +5 -0
  139. package/dist/mjs/tools/SoundUITool.d.ts +1 -0
  140. package/dist/mjs/tools/SoundUITool.mjs +4 -1
  141. package/dist/mjs/tools/TextTool.mjs +2 -2
  142. package/dist/mjs/tools/ToolController.d.ts +2 -0
  143. package/dist/mjs/tools/ToolController.mjs +13 -2
  144. package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  145. package/dist/mjs/tools/ToolSwitcherShortcut.mjs +3 -0
  146. package/dist/mjs/types.d.ts +9 -4
  147. package/dist/mjs/types.mjs +4 -3
  148. package/dist/mjs/util/ReactiveValue.d.ts +1 -1
  149. package/dist/mjs/util/ReactiveValue.mjs +2 -2
  150. package/dist/mjs/version.mjs +1 -1
  151. package/package.json +5 -5
  152. package/src/Editor.scss +6 -0
  153. package/src/toolbar/EdgeToolbar.scss +19 -2
  154. package/src/tools/SelectionTool/SelectionTool.scss +74 -0
  155. package/src/tools/tools.scss +1 -1
  156. package/src/tools/SelectionTool/SelectionTool.css +0 -35
  157. /package/dist/cjs/{EditorImage.test.d.ts → Editor.toSVGAsync.test.d.ts} +0 -0
  158. /package/dist/{mjs → cjs/image}/EditorImage.test.d.ts +0 -0
@@ -3,13 +3,13 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
3
3
  return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
4
4
  };
5
5
  var _a, _b, _c;
6
- import Viewport from './Viewport.mjs';
7
- import AbstractComponent, { ComponentSizingMode } from './components/AbstractComponent.mjs';
6
+ import Viewport from '../Viewport.mjs';
7
+ import AbstractComponent, { ComponentSizingMode } from '../components/AbstractComponent.mjs';
8
8
  import { Rect2, Vec2, Mat33, Color4 } from '@js-draw/math';
9
- import SerializableCommand from './commands/SerializableCommand.mjs';
10
- import EventDispatcher from './EventDispatcher.mjs';
11
- import { assertIsBoolean, assertIsNumber, assertIsNumberArray } from './util/assertions.mjs';
12
- import Command from './commands/Command.mjs';
9
+ import SerializableCommand from '../commands/SerializableCommand.mjs';
10
+ import EventDispatcher from '../EventDispatcher.mjs';
11
+ import { assertIsBoolean, assertIsNumber, assertIsNumberArray } from '../util/assertions.mjs';
12
+ import Command from '../commands/Command.mjs';
13
13
  // @internal Sort by z-index, low to high
14
14
  export const sortLeavesByZIndex = (leaves) => {
15
15
  leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
@@ -87,7 +87,27 @@ class EditorImage {
87
87
  this.background.render(renderer, viewport?.visibleRect);
88
88
  this.root.render(renderer, viewport?.visibleRect);
89
89
  }
90
- /** Renders all nodes, even ones not within the viewport. @internal */
90
+ /**
91
+ * Like {@link renderAll}, but can be stopped early and paused.
92
+ *
93
+ * **Note**: If the image is being edited during an async rendering, there is no
94
+ * guarantee that all nodes will be rendered correctly (some may be missing).
95
+ *
96
+ * @internal
97
+ */
98
+ async renderAllAsync(renderer, preRenderComponent) {
99
+ const stoppedEarly = !(await this.background.renderAllAsync(renderer, preRenderComponent));
100
+ if (!stoppedEarly) {
101
+ return await this.root.renderAllAsync(renderer, preRenderComponent);
102
+ }
103
+ return false;
104
+ }
105
+ /**
106
+ * Renders all nodes, even ones not within the viewport.
107
+ *
108
+ * This can be slow for large images
109
+ * @internal
110
+ */
91
111
  renderAll(renderer) {
92
112
  this.render(renderer, null);
93
113
  }
@@ -610,6 +630,30 @@ export class ImageNode {
610
630
  this.content = null;
611
631
  this.children = [];
612
632
  }
633
+ // Creates a (potentially incomplete) async rendering of this image.
634
+ // Returns false if stopped early
635
+ async renderAllAsync(renderer,
636
+ // Used to pause/stop the renderer process
637
+ preRenderComponent) {
638
+ const leaves = this.getLeaves();
639
+ sortLeavesByZIndex(leaves);
640
+ const totalLeaves = leaves.length;
641
+ for (let leafIndex = 0; leafIndex < totalLeaves; leafIndex++) {
642
+ const leaf = leaves[leafIndex];
643
+ const component = leaf.getContent();
644
+ // Even though leaf was originally a leaf, it might not be any longer --
645
+ // rendering is async and the tree can change during that time.
646
+ if (!component) {
647
+ continue;
648
+ }
649
+ const shouldContinue = await preRenderComponent(component, leafIndex, totalLeaves);
650
+ if (!shouldContinue) {
651
+ return false;
652
+ }
653
+ component.render(renderer, undefined);
654
+ }
655
+ return true;
656
+ }
613
657
  render(renderer, visibleRect) {
614
658
  let leaves;
615
659
  if (visibleRect) {
@@ -0,0 +1 @@
1
+ export {};
@@ -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,41 @@
1
+ import { Mat33 } from '@js-draw/math';
2
+ import SVGRenderer from '../../rendering/renderers/SVGRenderer.mjs';
3
+ import { svgLoaderAutoresizeClassName } from '../../SVGLoader.mjs';
4
+ import setExportedSVGSize from './setExportedSVGSize.mjs';
5
+ const toSVGInternal = (image, renderFunction, options) => {
6
+ const importExportViewport = image.getImportExportViewport().getTemporaryClone();
7
+ const { element: result, renderer } = SVGRenderer.fromViewport(importExportViewport, options.sanitize ?? false);
8
+ const origTransform = importExportViewport.canvasToScreenTransform;
9
+ // Render with (0,0) at (0,0) — we'll handle translation with
10
+ // the viewBox property.
11
+ importExportViewport.resetTransform(Mat33.identity);
12
+ // Use a callback rather than async/await to allow this function to create
13
+ // both sync and async render functions
14
+ renderFunction(renderer, () => {
15
+ importExportViewport.resetTransform(origTransform);
16
+ if (image.getAutoresizeEnabled()) {
17
+ result.classList.add(svgLoaderAutoresizeClassName);
18
+ }
19
+ else {
20
+ result.classList.remove(svgLoaderAutoresizeClassName);
21
+ }
22
+ setExportedSVGSize(result, importExportViewport, options);
23
+ return result;
24
+ });
25
+ return result;
26
+ };
27
+ export const editorImageToSVGSync = (image, options) => {
28
+ return toSVGInternal(image, (renderer, onComplete) => {
29
+ image.renderAll(renderer);
30
+ onComplete();
31
+ }, options);
32
+ };
33
+ export const editorImageToSVGAsync = (image, preRenderComponent, options) => {
34
+ return new Promise(resolve => {
35
+ toSVGInternal(image, async (renderer, onComplete) => {
36
+ await image.renderAllAsync(renderer, preRenderComponent);
37
+ const result = onComplete();
38
+ resolve(result);
39
+ }, options);
40
+ });
41
+ };
@@ -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,23 @@
1
+ import { toRoundedString } from '@js-draw/math';
2
+ // @internal
3
+ const setExportedSVGSize = (svg, viewport, options) => {
4
+ // Just show the main region
5
+ const rect = viewport.visibleRect;
6
+ svg.setAttribute('viewBox', [rect.x, rect.y, rect.w, rect.h].map(part => toRoundedString(part)).join(' '));
7
+ // Adjust the width/height as necessary
8
+ let width = rect.w;
9
+ let height = rect.h;
10
+ if (options?.minDimension && width < options.minDimension) {
11
+ const newWidth = options.minDimension;
12
+ height *= newWidth / (width || 1);
13
+ width = newWidth;
14
+ }
15
+ if (options?.minDimension && height < options.minDimension) {
16
+ const newHeight = options.minDimension;
17
+ width *= newHeight / (height || 1);
18
+ height = newHeight;
19
+ }
20
+ svg.setAttribute('width', toRoundedString(width));
21
+ svg.setAttribute('height', toRoundedString(height));
22
+ };
23
+ export default setExportedSVGSize;
@@ -0,0 +1 @@
1
+ export { default as EditorImage } from './EditorImage';
@@ -0,0 +1 @@
1
+ export { default as EditorImage } from './EditorImage.mjs';
package/dist/mjs/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/mjs/lib.mjs CHANGED
@@ -15,7 +15,7 @@
15
15
  * @packageDocumentation
16
16
  */
17
17
  import Editor from './Editor.mjs';
18
- export { default as EditorImage } from './EditorImage.mjs';
18
+ export * from './image/lib.mjs';
19
19
  export * from './types.mjs';
20
20
  export * from './inputEvents.mjs';
21
21
  export { default as getLocalizationTable, matchingLocalizationTable } from './localizations/getLocalizationTable.mjs';
@@ -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,8 @@
1
+ /**
2
+ * Comments to help translators create translations.
3
+ */
4
+ const comments = {
5
+ dragAndDropHereOrBrowse: 'Uses {{curly braces}} to denote bold text',
6
+ closeSidebar: 'Currently used as an accessibilty label',
7
+ };
8
+ export default comments;
@@ -1,68 +1,88 @@
1
1
  import { defaultEditorLocalization } from '../localization.mjs';
2
- // A partial Spanish localization.
2
+ // A partial Spanish localization, created with /scripts/markdownTranslationFormToTs.ts
3
3
  const localization = {
4
4
  ...defaultEditorLocalization,
5
- // Strings for the main editor interface
6
- // (see src/localization.ts)
7
- loading: (percentage) => `Cargando: ${percentage}%...`,
8
- imageEditor: 'Editor de dibujos',
9
- undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`,
10
- redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`,
11
- undo: 'Deshace',
12
- redo: 'Rehace',
13
- // Strings for the toolbar
14
- // (see src/toolbar/localization.ts)
15
5
  pen: 'Lapiz',
16
6
  eraser: 'Borrador',
17
7
  select: 'Selecciona',
18
- thicknessLabel: 'Tamaño',
19
- colorLabel: 'Color',
20
- doneLoading: 'El cargado terminó',
21
- fontLabel: 'Fuente: ',
22
- anyDevicePanning: 'Mover la pantalla con todo dispotivo',
23
- touchPanning: 'Mover la pantalla con un dedo',
24
- touchPanTool: 'Instrumento de mover la pantalla con un dedo',
25
- outlinedRectanglePen: 'Rectángulo con nada más que un borde',
26
- filledRectanglePen: 'Rectángulo sin borde',
27
- linePen: 'Línea',
28
- arrowPen: 'Flecha',
29
- roundedTipPen: 'Lapiz Redondeado',
30
- selectPenTip: 'Forma de dibuja',
31
8
  handTool: 'Mover',
32
- zoom: 'Zoom',
9
+ image: 'Imagen',
10
+ chooseFile: 'Seleccionar archivo',
11
+ cancel: 'Cancelar',
33
12
  resetView: 'Reiniciar vista',
13
+ thicknessLabel: 'Tamaño',
14
+ fontLabel: 'Fuente:',
15
+ textSize: 'Tamaño',
34
16
  resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado',
35
17
  deleteSelection: 'Borra la selección',
36
18
  duplicateSelection: 'Duplica la selección',
19
+ exit: 'Salir',
20
+ save: 'Guardar',
21
+ undo: 'Deshace',
22
+ redo: 'Rehace',
23
+ selectPenTip: 'Punta',
24
+ selectShape: 'Forma',
37
25
  pickColorFromScreen: 'Selecciona un color de la pantalla',
38
26
  clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color',
39
- dropdownShown(toolName) {
40
- return `Menú por ${toolName} es visible`;
41
- },
42
- dropdownHidden: function (toolName) {
43
- return `Menú por ${toolName} fue ocultado`;
44
- },
45
- colorChangedAnnouncement: function (color) {
46
- return `Color fue cambiado a ${color}`;
47
- },
48
- keyboardPanZoom: 'Mover la pantalla con el teclado',
49
- penTool: function (penId) {
50
- return `Lapiz ${penId}`;
51
- },
27
+ documentProperties: 'Fondo',
28
+ backgroundColor: 'Color de fondo',
29
+ imageWidthOption: 'Ancho',
30
+ imageHeightOption: 'Alto',
31
+ toggleOverflow: 'Más',
32
+ touchPanning: 'Mover la pantalla con un dedo',
33
+ roundedTipPen: 'Lapiz Redondeado',
34
+ arrowPen: 'Flecha',
35
+ linePen: 'Línea',
36
+ outlinedRectanglePen: 'Rectángulo delineado',
37
+ filledRectanglePen: 'Rectángulo sin borde',
38
+ lockRotation: 'Bloquea rotación',
39
+ paste: 'Pegar',
40
+ closeSidebar: (toolName) => `Close sidebar for ${toolName}`,
41
+ dropdownShown: (toolName) => `Menú por ${toolName} es visible`,
42
+ dropdownHidden: (toolName) => { return `Menú por ${toolName} fue ocultado`; },
43
+ zoomLevel: (zoomPercent) => `Zoom: ${zoomPercent}%`,
44
+ colorChangedAnnouncement: (color) => { return `Color fue cambiado a ${color}`; },
45
+ imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`,
46
+ imageLoadError: (message) => `Error cargando imagen: ${message}`,
47
+ penTool: (penId) => { return `Lapiz ${penId}`; },
52
48
  selectionTool: 'Selecciona',
53
49
  eraserTool: 'Borrador',
50
+ touchPanTool: 'Instrumento de mover la pantalla con un dedo',
51
+ pipetteTool: 'Seleccione un color de la pantalla',
52
+ keyboardPanZoom: 'Mover la pantalla con el teclado',
54
53
  textTool: 'Texto',
55
54
  enterTextToInsert: 'Entra texto',
56
- textSize: 'Tamaño',
55
+ findLabel: 'Buscar',
56
+ toNextMatch: 'Próxima',
57
+ closeDialog: 'Cerrar',
58
+ focusedFoundText: (matchIdx, totalMatches) => `Viewing match ${matchIdx} of ${totalMatches}`,
59
+ anyDevicePanning: 'Mover la pantalla con todo dispotivo',
60
+ copied: (count, description) => `Copied ${count} ${description}`,
61
+ pasted: (count, description) => `Pasted ${count} ${description}`,
62
+ toolEnabledAnnouncement: (toolName) => `${toolName} enabled`,
63
+ toolDisabledAnnouncement: (toolName) => `${toolName} disabled`,
64
+ transformedElements: (elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`,
65
+ resizeOutputCommand: (newSize) => `Resized image to ${newSize.w}x${newSize.h}`,
66
+ addElementAction: (componentDescription) => `Added ${componentDescription}`,
67
+ eraseAction: (componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`,
68
+ duplicateAction: (componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`,
69
+ unionOf: (actionDescription, actionCount) => `Union: ${actionCount} ${actionDescription}`,
70
+ inverseOf: (actionDescription) => `Inverse of ${actionDescription}`,
71
+ rotatedBy: (degrees) => `Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}`,
72
+ selectedElements: (count) => `Selected ${count} element${count === 1 ? '' : 's'}`,
73
+ filledBackgroundWithColor: (color) => `Filled background (${color})`,
74
+ text: (text) => `Text object: ${text}`,
75
+ imageNode: (label) => `Image: ${label}`,
76
+ restyledElement: (elementDescription) => `Restyled ${elementDescription}`,
77
+ pathNodeCount: (count) => `There are ${count} visible path objects.`,
78
+ textNodeCount: (count) => `There are ${count} visible text nodes.`,
79
+ imageNodeCount: (nodeCount) => `There are ${nodeCount} visible image nodes.`,
80
+ textNode: (content) => `Text: ${content}`,
57
81
  rerenderAsText: 'Redibuja la pantalla al texto',
58
- lockRotation: 'Bloquea rotación',
59
- image: 'Imagen',
60
- imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`,
61
- imageLoadError: (message) => `Error cargando imagen: ${message}`,
62
- toggleOverflow: 'Más',
63
- documentProperties: 'Fondo',
64
- imageWidthOption: 'Ancho',
65
- imageHeightOption: 'Alto',
66
- backgroundColor: 'Color de fondo: '
82
+ loading: (percentage) => `Cargando: ${percentage}%...`,
83
+ imageEditor: 'Editor de dibujos',
84
+ doneLoading: 'El cargado terminó',
85
+ undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`,
86
+ redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`,
67
87
  };
68
88
  export 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,5 +1,5 @@
1
1
  // A cache record with sub-nodes.
2
- import { sortLeavesByZIndex } from '../../EditorImage.mjs';
2
+ import { sortLeavesByZIndex } from '../../image/EditorImage.mjs';
3
3
  import { Rect2, Color4 } from '@js-draw/math';
4
4
  // 3x3 divisions for each node.
5
5
  const cacheDivisionSize = 3;
@@ -42,11 +42,12 @@ export default class RenderingCacheNode {
42
42
  // Generates children, if missing.
43
43
  generateChildren() {
44
44
  if (this.instantiatedChildren.length === 0) {
45
- const childRects = this.region.divideIntoGrid(cacheDivisionSize, cacheDivisionSize);
46
- if (this.region.size.x === 0 || this.region.size.y === 0) {
45
+ if (this.region.size.x / cacheDivisionSize === 0 || this.region.size.y / cacheDivisionSize === 0) {
47
46
  console.warn('Cache element has zero size! Not generating children.');
48
47
  return;
49
48
  }
49
+ const childRects = this.region.divideIntoGrid(cacheDivisionSize, cacheDivisionSize);
50
+ console.assert(childRects.length === cacheDivisionSize * cacheDivisionSize, 'Warning: divideIntoGrid created the wrong number of subrectangles!');
50
51
  for (const rect of childRects) {
51
52
  const child = new RenderingCacheNode(rect, this.cacheState);
52
53
  child.parent = this;
@@ -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
  *
@@ -230,7 +230,15 @@ class AbstractToolbar {
230
230
  * @see
231
231
  * {@link addActionButton}
232
232
  */
233
- makeActionButton(title, command, mustBeToplevel = true) {
233
+ makeActionButton(title, command, options = true) {
234
+ // Parse options
235
+ if (typeof options === 'boolean') {
236
+ options = {
237
+ mustBeToplevel: options,
238
+ };
239
+ }
240
+ const mustBeToplevel = options.mustBeToplevel ?? true;
241
+ const autoDisableInReadOnlyEditors = options.autoDisableInReadOnlyEditors ?? true;
234
242
  const titleString = typeof title === 'string' ? title : title.label;
235
243
  const widgetId = 'action-button';
236
244
  const makeIcon = () => {
@@ -239,16 +247,20 @@ class AbstractToolbar {
239
247
  }
240
248
  return title.icon;
241
249
  };
242
- const widget = new ActionButtonWidget(this.editor, widgetId, makeIcon, titleString, command, this.editor.localization, mustBeToplevel);
250
+ const widget = new ActionButtonWidget(this.editor, widgetId, makeIcon, titleString, command, this.editor.localization, mustBeToplevel, autoDisableInReadOnlyEditors);
243
251
  return widget;
244
252
  }
245
253
  /**
246
254
  * Adds an action button with `title` to this toolbar (or to the given `parent` element).
247
255
  *
256
+ * `options` can either be an object with properties `mustBeToplevel` and/or
257
+ * `autoDisableInReadOnlyEditors` or a boolean value. If a boolean, it is interpreted
258
+ * as being the value of `mustBeToplevel`.
259
+ *
248
260
  * @return The added button.
249
261
  */
250
- addActionButton(title, command, mustBeToplevel = true) {
251
- const widget = this.makeActionButton(title, command, mustBeToplevel);
262
+ addActionButton(title, command, options = true) {
263
+ const widget = this.makeActionButton(title, command, options);
252
264
  this.addWidget(widget);
253
265
  return widget;
254
266
  }
@@ -256,8 +268,8 @@ class AbstractToolbar {
256
268
  * Like {@link addActionButton}, except associates `tags` with the button that allow
257
269
  * different toolbar styles to give the button tag-dependent styles.
258
270
  */
259
- addTaggedActionButton(tags, title, command, mustBeToplevel = true) {
260
- const widget = this.makeActionButton(title, command, mustBeToplevel);
271
+ addTaggedActionButton(tags, title, command, options = true) {
272
+ const widget = this.makeActionButton(title, command, options);
261
273
  widget.setTags(tags);
262
274
  this.addWidget(widget);
263
275
  return widget;
@@ -303,6 +315,8 @@ class AbstractToolbar {
303
315
  icon: this.editor.icons.makeCloseIcon(),
304
316
  }, () => {
305
317
  exitCallback();
318
+ }, {
319
+ autoDisableInReadOnlyEditors: false,
306
320
  });
307
321
  }
308
322
  /**
@@ -191,7 +191,6 @@ export default class EdgeToolbar extends AbstractToolbar {
191
191
  };
192
192
  const actionRowBBox = this.toolbarActionRow.getBoundingClientRect();
193
193
  const toolbarRowBBox = this.toolbarToolRow.getBoundingClientRect();
194
- const inSameRow = actionRowBBox.y === toolbarRowBBox.y;
195
194
  const onDifferentRows = actionRowBBox.y + actionRowBBox.height <= toolbarRowBBox.y;
196
195
  if (onDifferentRows) {
197
196
  this.toolbarContainer.classList.remove('one-row');
@@ -201,11 +200,11 @@ export default class EdgeToolbar extends AbstractToolbar {
201
200
  }
202
201
  if (this.toolbarToolRow.clientWidth < this.toolbarToolRow.scrollWidth) {
203
202
  this.toolbarToolRow.classList.add('has-scroll');
204
- // If both button areas are in the same row, don't change the padding --
205
- // it could lead to an endless loop of reseize events.
206
- if (!inSameRow) {
207
- setExtraPadding();
208
- }
203
+ // Note: This can potentially change the size of the tool row.
204
+ // Because this is run inside of a ResizeObserver callback, special
205
+ // care must be taken to ensure that this change doesn't re-trigger
206
+ // the resize observer.
207
+ setExtraPadding();
209
208
  }
210
209
  else {
211
210
  this.toolbarToolRow.classList.remove('has-scroll', 'extra-padding');
@@ -38,6 +38,7 @@ export default class IconProvider {
38
38
  makeDropdownIcon(): IconElemType;
39
39
  makeEraserIcon(eraserSize?: number): IconElemType;
40
40
  makeSelectionIcon(): IconElemType;
41
+ makeRotateIcon(): IconElemType;
41
42
  makeHandToolIcon(): IconElemType;
42
43
  makeTouchPanningIcon(): IconElemType;
43
44
  /** Unused by js-draw. @deprecated */
@@ -164,6 +164,49 @@ class IconProvider {
164
164
  icon.setAttribute('viewBox', '0 0 100 100');
165
165
  return icon;
166
166
  }
167
+ makeRotateIcon() {
168
+ const icon = document.createElementNS(svgNamespace, 'svg');
169
+ icon.innerHTML = `
170
+ <defs>
171
+ <marker
172
+ id="arrow-marker"
173
+ viewBox="0 0 10 10"
174
+ refX="3" refY="5"
175
+ markerWidth="3" markerHeight="3"
176
+ orient="auto-start-reverse"
177
+ >
178
+ <path
179
+ d="M0,0 L8,5 L0,10z"
180
+ fill="var(--icon-color)"
181
+ />
182
+ </marker>
183
+ </defs>
184
+
185
+ <path
186
+ marker-start="url(#arrow-marker)"
187
+ d="
188
+ M20,20
189
+ A30,30 0 1 1 80 80
190
+ "
191
+ fill="none"
192
+ stroke="var(--icon-color)"
193
+ stroke-width="12"
194
+ />
195
+ <path
196
+ d="
197
+ M80,80
198
+ A30,30 0 1 1 20 20
199
+ "
200
+ fill="none"
201
+ stroke="var(--icon-color)"
202
+ stroke-width="12"
203
+ stroke-dasharray="30 10 20 10 20 10 10"
204
+ style="stroke-linecap: butt;"
205
+ />
206
+ `;
207
+ icon.setAttribute('viewBox', '-5 -5 110 110');
208
+ return icon;
209
+ }
167
210
  makeHandToolIcon() {
168
211
  const fill = 'none';
169
212
  const strokeColor = 'var(--icon-color)';
@@ -2,11 +2,13 @@ import Editor from '../../Editor';
2
2
  import { ToolbarLocalization } from '../localization';
3
3
  import BaseWidget from './BaseWidget';
4
4
  export default class ActionButtonWidget extends BaseWidget {
5
+ #private;
5
6
  protected makeIcon: () => Element | null;
6
7
  protected title: string;
7
8
  protected clickAction: () => void;
8
9
  protected mustBeToplevel: boolean;
9
- constructor(editor: Editor, id: string, makeIcon: () => Element | null, title: string, clickAction: () => void, localizationTable?: ToolbarLocalization, mustBeToplevel?: boolean);
10
+ constructor(editor: Editor, id: string, makeIcon: () => Element | null, title: string, clickAction: () => void, localizationTable?: ToolbarLocalization, mustBeToplevel?: boolean, autoDisableInReadOnlyEditors?: boolean);
11
+ protected shouldAutoDisableInReadOnlyEditor(): boolean;
10
12
  protected handleClick(): void;
11
13
  protected getTitle(): string;
12
14
  protected createIcon(): Element | null;
@@ -1,11 +1,28 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ 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");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ 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");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _ActionButtonWidget_autoDisableInReadOnlyEditors;
1
13
  import BaseWidget from './BaseWidget.mjs';
2
- export default class ActionButtonWidget extends BaseWidget {
3
- constructor(editor, id, makeIcon, title, clickAction, localizationTable, mustBeToplevel = false) {
14
+ class ActionButtonWidget extends BaseWidget {
15
+ constructor(editor, id, makeIcon, title, clickAction, localizationTable, mustBeToplevel = false, autoDisableInReadOnlyEditors = true) {
4
16
  super(editor, id, localizationTable);
5
17
  this.makeIcon = makeIcon;
6
18
  this.title = title;
7
19
  this.clickAction = clickAction;
8
20
  this.mustBeToplevel = mustBeToplevel;
21
+ _ActionButtonWidget_autoDisableInReadOnlyEditors.set(this, void 0);
22
+ __classPrivateFieldSet(this, _ActionButtonWidget_autoDisableInReadOnlyEditors, autoDisableInReadOnlyEditors, "f");
23
+ }
24
+ shouldAutoDisableInReadOnlyEditor() {
25
+ return __classPrivateFieldGet(this, _ActionButtonWidget_autoDisableInReadOnlyEditors, "f");
9
26
  }
10
27
  handleClick() {
11
28
  this.clickAction();
@@ -23,3 +40,5 @@ export default class ActionButtonWidget extends BaseWidget {
23
40
  return this.mustBeToplevel;
24
41
  }
25
42
  }
43
+ _ActionButtonWidget_autoDisableInReadOnlyEditors = new WeakMap();
44
+ export default ActionButtonWidget;
@@ -6,6 +6,7 @@ import BaseWidget from './BaseWidget';
6
6
  export default abstract class BaseToolWidget extends BaseWidget {
7
7
  protected targetTool: BaseTool;
8
8
  constructor(editor: Editor, targetTool: BaseTool, id: string, localizationTable?: ToolbarLocalization);
9
+ protected shouldAutoDisableInReadOnlyEditor(): boolean;
9
10
  protected handleClick(): void;
10
11
  protected onKeyPress(event: KeyPressEvent): boolean;
11
12
  addTo(parent: HTMLElement): HTMLElement;
@@ -34,6 +34,9 @@ export default class BaseToolWidget extends BaseWidget {
34
34
  }
35
35
  });
36
36
  }
37
+ shouldAutoDisableInReadOnlyEditor() {
38
+ return !this.targetTool.canReceiveInputInReadOnlyEditor();
39
+ }
37
40
  handleClick() {
38
41
  if (this.hasDropdown) {
39
42
  if (!this.targetTool.isEnabled()) {