js-draw 1.27.2 → 1.28.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 (112) hide show
  1. package/README.md +1 -1
  2. package/dist/Editor.css +1 -1
  3. package/dist/bundle.js +28 -28
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/Editor.d.ts +7 -2
  6. package/dist/cjs/Editor.js +11 -5
  7. package/dist/cjs/SVGLoader/SVGLoader.d.ts +21 -0
  8. package/dist/cjs/SVGLoader/SVGLoader.js +74 -47
  9. package/dist/cjs/SVGLoader/SVGLoader.plugins.test.d.ts +1 -0
  10. package/dist/cjs/Viewport.js +2 -32
  11. package/dist/cjs/commands/Duplicate.d.ts +7 -4
  12. package/dist/cjs/commands/Duplicate.js +48 -7
  13. package/dist/cjs/commands/Duplicate.test.d.ts +1 -0
  14. package/dist/cjs/commands/Erase.d.ts +1 -1
  15. package/dist/cjs/commands/Erase.js +2 -2
  16. package/dist/cjs/commands/localization.d.ts +2 -2
  17. package/dist/cjs/commands/localization.js +2 -2
  18. package/dist/cjs/components/AbstractComponent.d.ts +7 -0
  19. package/dist/cjs/components/AbstractComponent.js +16 -2
  20. package/dist/cjs/components/Stroke.d.ts +21 -1
  21. package/dist/cjs/components/Stroke.js +29 -0
  22. package/dist/cjs/components/TextComponent.d.ts +2 -2
  23. package/dist/cjs/components/TextComponent.js +2 -2
  24. package/dist/cjs/image/EditorImage.d.ts +17 -9
  25. package/dist/cjs/image/EditorImage.js +33 -17
  26. package/dist/cjs/lib.d.ts +1 -1
  27. package/dist/cjs/localizations/de.js +2 -2
  28. package/dist/cjs/rendering/RenderingStyle.d.ts +7 -6
  29. package/dist/cjs/rendering/lib.d.ts +1 -1
  30. package/dist/cjs/rendering/renderers/AbstractRenderer.js +4 -0
  31. package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +9 -0
  32. package/dist/cjs/rendering/renderers/CanvasRenderer.js +14 -0
  33. package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +18 -0
  34. package/dist/cjs/rendering/renderers/SVGRenderer.js +21 -1
  35. package/dist/cjs/toolbar/utils/HelpDisplay.js +6 -4
  36. package/dist/cjs/toolbar/utils/localization.d.ts +1 -0
  37. package/dist/cjs/toolbar/utils/localization.js +1 -0
  38. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +1 -1
  39. package/dist/cjs/toolbar/widgets/InsertImageWidget/InsertImageWidget.js +1 -1
  40. package/dist/cjs/toolbar/widgets/components/makeGridSelector.js +1 -1
  41. package/dist/cjs/tools/Eraser.js +3 -3
  42. package/dist/cjs/tools/FindTool.js +1 -1
  43. package/dist/cjs/tools/PasteHandler.js +4 -1
  44. package/dist/cjs/tools/Pen.js +1 -1
  45. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.js +1 -1
  46. package/dist/cjs/tools/SelectionTool/Selection.js +23 -10
  47. package/dist/cjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.js +1 -1
  48. package/dist/cjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.js +1 -1
  49. package/dist/cjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.js +1 -1
  50. package/dist/cjs/tools/SelectionTool/SelectionTool.js +3 -2
  51. package/dist/cjs/tools/SoundUITool.js +1 -1
  52. package/dist/cjs/tools/TextTool.js +2 -2
  53. package/dist/cjs/util/assertions.d.ts +6 -0
  54. package/dist/cjs/util/assertions.js +18 -0
  55. package/dist/cjs/util/describeTransformation.d.ts +12 -0
  56. package/dist/cjs/util/describeTransformation.js +44 -0
  57. package/dist/cjs/version.js +1 -1
  58. package/dist/mjs/Editor.d.ts +7 -2
  59. package/dist/mjs/Editor.mjs +11 -5
  60. package/dist/mjs/SVGLoader/SVGLoader.d.ts +21 -0
  61. package/dist/mjs/SVGLoader/SVGLoader.mjs +74 -47
  62. package/dist/mjs/SVGLoader/SVGLoader.plugins.test.d.ts +1 -0
  63. package/dist/mjs/Viewport.mjs +2 -32
  64. package/dist/mjs/commands/Duplicate.d.ts +7 -4
  65. package/dist/mjs/commands/Duplicate.mjs +48 -7
  66. package/dist/mjs/commands/Duplicate.test.d.ts +1 -0
  67. package/dist/mjs/commands/Erase.d.ts +1 -1
  68. package/dist/mjs/commands/Erase.mjs +2 -2
  69. package/dist/mjs/commands/localization.d.ts +2 -2
  70. package/dist/mjs/commands/localization.mjs +2 -2
  71. package/dist/mjs/components/AbstractComponent.d.ts +7 -0
  72. package/dist/mjs/components/AbstractComponent.mjs +17 -3
  73. package/dist/mjs/components/Stroke.d.ts +21 -1
  74. package/dist/mjs/components/Stroke.mjs +31 -2
  75. package/dist/mjs/components/TextComponent.d.ts +2 -2
  76. package/dist/mjs/components/TextComponent.mjs +2 -2
  77. package/dist/mjs/image/EditorImage.d.ts +17 -9
  78. package/dist/mjs/image/EditorImage.mjs +33 -17
  79. package/dist/mjs/lib.d.ts +1 -1
  80. package/dist/mjs/localizations/de.mjs +2 -2
  81. package/dist/mjs/rendering/RenderingStyle.d.ts +7 -6
  82. package/dist/mjs/rendering/lib.d.ts +1 -1
  83. package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +4 -0
  84. package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +9 -0
  85. package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +14 -0
  86. package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +18 -0
  87. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +21 -1
  88. package/dist/mjs/toolbar/utils/HelpDisplay.mjs +6 -4
  89. package/dist/mjs/toolbar/utils/localization.d.ts +1 -0
  90. package/dist/mjs/toolbar/utils/localization.mjs +1 -0
  91. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +1 -1
  92. package/dist/mjs/toolbar/widgets/InsertImageWidget/InsertImageWidget.mjs +1 -1
  93. package/dist/mjs/toolbar/widgets/components/makeGridSelector.mjs +1 -1
  94. package/dist/mjs/tools/Eraser.mjs +3 -3
  95. package/dist/mjs/tools/FindTool.mjs +1 -1
  96. package/dist/mjs/tools/PasteHandler.mjs +4 -1
  97. package/dist/mjs/tools/Pen.mjs +1 -1
  98. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.mjs +1 -1
  99. package/dist/mjs/tools/SelectionTool/Selection.mjs +23 -10
  100. package/dist/mjs/tools/SelectionTool/SelectionBuilders/LassoSelectionBuilder.mjs +1 -1
  101. package/dist/mjs/tools/SelectionTool/SelectionBuilders/RectSelectionBuilder.mjs +1 -1
  102. package/dist/mjs/tools/SelectionTool/SelectionBuilders/SelectionBuilder.mjs +1 -1
  103. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +3 -2
  104. package/dist/mjs/tools/SoundUITool.mjs +1 -1
  105. package/dist/mjs/tools/TextTool.mjs +2 -2
  106. package/dist/mjs/util/assertions.d.ts +6 -0
  107. package/dist/mjs/util/assertions.mjs +16 -0
  108. package/dist/mjs/util/describeTransformation.d.ts +12 -0
  109. package/dist/mjs/util/describeTransformation.mjs +42 -0
  110. package/dist/mjs/version.mjs +1 -1
  111. package/package.json +4 -4
  112. package/src/toolbar/utils/HelpDisplay.scss +7 -1
@@ -22,7 +22,7 @@ export type EditorImageNotifier = EventDispatcher<EditorImageEventType, {
22
22
  */
23
23
  export type PreRenderComponentCallback = (component: AbstractComponent, componentsProcessed: number, totalComponents: number) => Promise<boolean>;
24
24
  /**
25
- * @summary Handles lookup/storage of elements in the image.
25
+ * Handles lookup/storage of elements in the image.
26
26
  *
27
27
  * `js-draw` images are made up of a collection of {@link AbstractComponent}s (which
28
28
  * includes {@link Stroke}s, {@link TextComponent}s, etc.). An `EditorImage`
@@ -30,9 +30,9 @@ export type PreRenderComponentCallback = (component: AbstractComponent, componen
30
30
  *
31
31
  * Here's how to do a few common operations:
32
32
  * - **Get all components in a {@link @js-draw/math!Rect2 | Rect2}**:
33
- * {@link EditorImage.getElementsIntersectingRegion}.
33
+ * {@link EditorImage.getComponentsIntersecting}.
34
34
  * - **Draw an `EditorImage` onto a canvas/SVG**: {@link EditorImage.render}.
35
- * - **Adding a new component**: {@link EditorImage.addElement}.
35
+ * - **Adding a new component**: {@link EditorImage.addComponent}.
36
36
  *
37
37
  * **Example**:
38
38
  * [[include:doc-pages/inline-examples/image-add-and-lookup.md]]
@@ -82,19 +82,23 @@ export default class EditorImage {
82
82
  * @returns all elements in the image, sorted by z-index (low to high).
83
83
  *
84
84
  * This can be slow for large images. If you only need all elemenst in part of the image,
85
- * consider using {@link getElementsIntersectingRegion} instead.
85
+ * consider using {@link getComponentsIntersecting} instead.
86
86
  *
87
87
  * **Note**: The result does not include background elements. See {@link getBackgroundComponents}.
88
88
  */
89
+ getAllComponents(): AbstractComponent[];
90
+ /** @deprecated in favor of {@link getAllComponents} */
89
91
  getAllElements(): AbstractComponent[];
90
92
  /** Returns the number of elements added to this image. @internal */
91
93
  estimateNumElements(): number;
94
+ /** @deprecated @see getComponentsIntersecting */
95
+ getElementsIntersectingRegion(region: Rect2, includeBackground?: boolean): AbstractComponent[];
92
96
  /**
93
97
  * @returns a list of `AbstractComponent`s intersecting `region`, sorted by increasing z-index.
94
98
  *
95
99
  * Components in the background layer are only included if `includeBackground` is `true`.
96
100
  */
97
- getElementsIntersectingRegion(region: Rect2, includeBackground?: boolean): AbstractComponent[];
101
+ getComponentsIntersecting(region: Rect2, includeBackground?: boolean): AbstractComponent[];
98
102
  /** Called whenever (just after) an element is completely removed. @internal */
99
103
  onDestroyElement(elem: AbstractComponent): void;
100
104
  /** Called just after an element is added. @internal */
@@ -105,7 +109,7 @@ export default class EditorImage {
105
109
  * @see {@link AbstractComponent.getId}
106
110
  */
107
111
  lookupElement(id: string): AbstractComponent | null;
108
- private addElementDirectly;
112
+ private addComponentDirectly;
109
113
  private removeElementDirectly;
110
114
  /**
111
115
  * Returns a command that adds the given element to the `EditorImage`.
@@ -118,10 +122,14 @@ export default class EditorImage {
118
122
  *
119
123
  * [[include:doc-pages/inline-examples/adding-a-stroke.md]]
120
124
  */
121
- static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
122
- /** @see EditorImage.addElement */
125
+ static addComponent(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
126
+ /** @see EditorImage.addComponent */
127
+ addComponent(component: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
128
+ /** Alias for {@link addComponent}. @deprecated Prefer `.addComponent` */
123
129
  addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
124
- private static AddElementCommand;
130
+ /** Alias for {@link addComponent}. @deprecated Prefer `.addComponent`. */
131
+ static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
132
+ private static AddComponentCommand;
125
133
  /**
126
134
  * @returns a `Viewport` for rendering the image when importing/exporting.
127
135
  */
@@ -21,7 +21,7 @@ export var EditorImageEventType;
21
21
  })(EditorImageEventType || (EditorImageEventType = {}));
22
22
  let debugMode = false;
23
23
  /**
24
- * @summary Handles lookup/storage of elements in the image.
24
+ * Handles lookup/storage of elements in the image.
25
25
  *
26
26
  * `js-draw` images are made up of a collection of {@link AbstractComponent}s (which
27
27
  * includes {@link Stroke}s, {@link TextComponent}s, etc.). An `EditorImage`
@@ -29,9 +29,9 @@ let debugMode = false;
29
29
  *
30
30
  * Here's how to do a few common operations:
31
31
  * - **Get all components in a {@link @js-draw/math!Rect2 | Rect2}**:
32
- * {@link EditorImage.getElementsIntersectingRegion}.
32
+ * {@link EditorImage.getComponentsIntersecting}.
33
33
  * - **Draw an `EditorImage` onto a canvas/SVG**: {@link EditorImage.render}.
34
- * - **Adding a new component**: {@link EditorImage.addElement}.
34
+ * - **Adding a new component**: {@link EditorImage.addComponent}.
35
35
  *
36
36
  * **Example**:
37
37
  * [[include:doc-pages/inline-examples/image-add-and-lookup.md]]
@@ -78,7 +78,7 @@ class EditorImage {
78
78
  const parent = this.findParent(elem);
79
79
  if (parent) {
80
80
  parent.remove();
81
- this.addElementDirectly(elem);
81
+ this.addComponentDirectly(elem);
82
82
  }
83
83
  }
84
84
  /** @internal */
@@ -133,25 +133,33 @@ class EditorImage {
133
133
  * @returns all elements in the image, sorted by z-index (low to high).
134
134
  *
135
135
  * This can be slow for large images. If you only need all elemenst in part of the image,
136
- * consider using {@link getElementsIntersectingRegion} instead.
136
+ * consider using {@link getComponentsIntersecting} instead.
137
137
  *
138
138
  * **Note**: The result does not include background elements. See {@link getBackgroundComponents}.
139
139
  */
140
- getAllElements() {
140
+ getAllComponents() {
141
141
  const leaves = this.root.getLeaves();
142
142
  sortLeavesByZIndex(leaves);
143
143
  return leaves.map((leaf) => leaf.getContent());
144
144
  }
145
+ /** @deprecated in favor of {@link getAllComponents} */
146
+ getAllElements() {
147
+ return this.getAllComponents();
148
+ }
145
149
  /** Returns the number of elements added to this image. @internal */
146
150
  estimateNumElements() {
147
151
  return this.componentCount;
148
152
  }
153
+ /** @deprecated @see getComponentsIntersecting */
154
+ getElementsIntersectingRegion(region, includeBackground = false) {
155
+ return this.getComponentsIntersecting(region, includeBackground);
156
+ }
149
157
  /**
150
158
  * @returns a list of `AbstractComponent`s intersecting `region`, sorted by increasing z-index.
151
159
  *
152
160
  * Components in the background layer are only included if `includeBackground` is `true`.
153
161
  */
154
- getElementsIntersectingRegion(region, includeBackground = false) {
162
+ getComponentsIntersecting(region, includeBackground = false) {
155
163
  let leaves = this.root.getLeavesIntersectingRegion(region);
156
164
  if (includeBackground) {
157
165
  leaves = leaves.concat(this.background.getLeavesIntersectingRegion(region));
@@ -179,7 +187,7 @@ class EditorImage {
179
187
  lookupElement(id) {
180
188
  return this.componentsById[id] ?? null;
181
189
  }
182
- addElementDirectly(elem) {
190
+ addComponentDirectly(elem) {
183
191
  // Because onAddToImage can affect the element's bounding box,
184
192
  // this needs to be called before parentTree.addLeaf.
185
193
  elem.onAddToImage(this);
@@ -210,12 +218,20 @@ class EditorImage {
210
218
  *
211
219
  * [[include:doc-pages/inline-examples/adding-a-stroke.md]]
212
220
  */
213
- static addElement(elem, applyByFlattening = false) {
214
- return new _a.AddElementCommand(elem, applyByFlattening);
221
+ static addComponent(elem, applyByFlattening = false) {
222
+ return new _a.AddComponentCommand(elem, applyByFlattening);
223
+ }
224
+ /** @see EditorImage.addComponent */
225
+ addComponent(component, applyByFlattening) {
226
+ return _a.addComponent(component, applyByFlattening);
215
227
  }
216
- /** @see EditorImage.addElement */
228
+ /** Alias for {@link addComponent}. @deprecated Prefer `.addComponent` */
217
229
  addElement(elem, applyByFlattening) {
218
- return _a.addElement(elem, applyByFlattening);
230
+ return this.addComponent(elem, applyByFlattening);
231
+ }
232
+ /** Alias for {@link addComponent}. @deprecated Prefer `.addComponent`. */
233
+ static addElement(elem, applyByFlattening = false) {
234
+ return this.addComponent(elem, applyByFlattening);
219
235
  }
220
236
  /**
221
237
  * @returns a `Viewport` for rendering the image when importing/exporting.
@@ -346,7 +362,7 @@ class EditorImage {
346
362
  }
347
363
  _a = EditorImage;
348
364
  // A Command that can access private [EditorImage] functionality
349
- EditorImage.AddElementCommand = (_b = class extends SerializableCommand {
365
+ EditorImage.AddComponentCommand = (_b = class extends SerializableCommand {
350
366
  // If [applyByFlattening], then the rendered content of this element
351
367
  // is present on the display's wet ink canvas. As such, no re-render is necessary
352
368
  // the first time this command is applied (the surfaces are joined instead).
@@ -366,7 +382,7 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand {
366
382
  }
367
383
  }
368
384
  apply(editor) {
369
- editor.image.addElementDirectly(this.element);
385
+ editor.image.addComponentDirectly(this.element);
370
386
  if (!this.applyByFlattening) {
371
387
  editor.queueRerender();
372
388
  }
@@ -380,7 +396,7 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand {
380
396
  editor.queueRerender();
381
397
  }
382
398
  description(_editor, localization) {
383
- return localization.addElementAction(this.element.description(localization));
399
+ return localization.addComponentAction(this.element.description(localization));
384
400
  }
385
401
  serializeToJSON() {
386
402
  return {
@@ -388,13 +404,13 @@ EditorImage.AddElementCommand = (_b = class extends SerializableCommand {
388
404
  };
389
405
  }
390
406
  },
391
- __setFunctionName(_b, "AddElementCommand"),
407
+ __setFunctionName(_b, "AddComponentCommand"),
392
408
  (() => {
393
409
  SerializableCommand.register('add-element', (json, editor) => {
394
410
  const id = json.elemData.id;
395
411
  const foundElem = editor.image.lookupElement(id);
396
412
  const elem = foundElem ?? AbstractComponent.deserialize(json.elemData);
397
- const result = new _a.AddElementCommand(elem);
413
+ const result = new _a.AddComponentCommand(elem);
398
414
  result.serializedElem = json.elemData;
399
415
  return result;
400
416
  });
package/dist/mjs/lib.d.ts CHANGED
@@ -20,7 +20,7 @@ export * from './types';
20
20
  export * from './inputEvents';
21
21
  export { default as getLocalizationTable, matchingLocalizationTable, } from './localizations/getLocalizationTable';
22
22
  export * from './localization';
23
- export { default as SVGLoader } from './SVGLoader/SVGLoader';
23
+ export { default as SVGLoader, SVGLoaderPlugin } from './SVGLoader/SVGLoader';
24
24
  export { default as Viewport } from './Viewport';
25
25
  export * from '@js-draw/math';
26
26
  export * from './components/lib';
@@ -65,9 +65,9 @@ const localization = {
65
65
  toolEnabledAnnouncement: (toolName) => `${toolName} aktiviert`,
66
66
  toolDisabledAnnouncement: (toolName) => `${toolName} deaktiviert`,
67
67
  updatedViewport: 'Transformierte Ansicht',
68
- transformedElements: (elemCount) => `${elemCount} Element${1 === elemCount ? '' : 'e'} transformiert`,
68
+ transformedElements: (elemCount, action) => `${elemCount} Element${1 === elemCount ? '' : 'e'} transformiert (${action})`,
69
69
  resizeOutputCommand: (newSize) => `Bildgröße auf ${newSize.w}x${newSize.h} geändert`,
70
- addElementAction: (componentDescription) => `${componentDescription} hinzugefügt`,
70
+ addComponentAction: (componentDescription) => `${componentDescription} hinzugefügt`,
71
71
  eraseAction: (elemDescription, countErased) => `${countErased} ${elemDescription} gelöscht`,
72
72
  duplicateAction: (elemDescription, countErased) => `${countErased} ${elemDescription} dupliziert`,
73
73
  inverseOf: (actionDescription) => `${actionDescription} umgekehrt`,
@@ -1,11 +1,12 @@
1
1
  import { Color4 } from '@js-draw/math';
2
- interface RenderingStyle {
2
+ export interface StrokeStyle {
3
+ readonly color: Color4;
4
+ /** Note: The stroke `width` is twice the stroke radius. */
5
+ readonly width: number;
6
+ }
7
+ export interface RenderingStyle {
3
8
  readonly fill: Color4;
4
- readonly stroke?: {
5
- readonly color: Color4;
6
- /** Note: The stroke `width` is twice the stroke radius. */
7
- readonly width: number;
8
- };
9
+ readonly stroke?: StrokeStyle;
9
10
  }
10
11
  export default RenderingStyle;
11
12
  export declare const cloneStyle: (style: RenderingStyle) => {
@@ -4,5 +4,5 @@ export { default as SVGRenderer } from './renderers/SVGRenderer';
4
4
  export { default as CanvasRenderer } from './renderers/CanvasRenderer';
5
5
  export { default as Display, RenderingMode } from './Display';
6
6
  export { default as TextRenderingStyle } from './TextRenderingStyle';
7
- export { default as RenderingStyle } from './RenderingStyle';
7
+ export { default as RenderingStyle, StrokeStyle as StrokeRenerdingStyle } from './RenderingStyle';
8
8
  export { pathToRenderable, pathFromRenderable, visualEquivalent as pathVisualEquivalent, default as RenderablePathSpec, } from './RenderablePathSpec';
@@ -140,6 +140,8 @@ export default class AbstractRenderer {
140
140
  this.selfTransform = transform;
141
141
  }
142
142
  pushTransform(transform) {
143
+ // Draw all pending paths that used the previous transform (if any).
144
+ this.flushPath();
143
145
  this.transformStack.push(this.selfTransform);
144
146
  this.setTransform(this.getCanvasToScreenTransform().rightMul(transform));
145
147
  }
@@ -147,6 +149,8 @@ export default class AbstractRenderer {
147
149
  if (this.transformStack.length === 0) {
148
150
  throw new Error('Unable to pop more transforms than have been pushed!');
149
151
  }
152
+ // Draw all pending paths that used the old transform (if any):
153
+ this.flushPath();
150
154
  this.setTransform(this.transformStack.pop() ?? null);
151
155
  }
152
156
  // Get the matrix that transforms a vector on the canvas to a vector on this'
@@ -42,6 +42,15 @@ export default class CanvasRenderer extends AbstractRenderer {
42
42
  private clipLevels;
43
43
  startObject(boundingBox: Rect2, clip?: boolean): void;
44
44
  endObject(): void;
45
+ /**
46
+ * Returns a reference to the underlying `CanvasRenderingContext2D`.
47
+ * This can be used to render custom content not supported by {@link AbstractRenderer}.
48
+ * However, such content won't support {@link SVGRenderer} or {@link TextOnlyRenderer}
49
+ * by default.
50
+ *
51
+ * Use with caution.
52
+ */
53
+ drawWithRawRenderingContext(callback: (ctx: CanvasRenderingContext2D) => void): void;
45
54
  drawPoints(...points: Point2[]): void;
46
55
  isTooSmallToRender(rect: Rect2): boolean;
47
56
  static fromViewport(exportViewport: Viewport, options?: {
@@ -208,6 +208,20 @@ export default class CanvasRenderer extends AbstractRenderer {
208
208
  this.ignoringObject = false;
209
209
  }
210
210
  }
211
+ /**
212
+ * Returns a reference to the underlying `CanvasRenderingContext2D`.
213
+ * This can be used to render custom content not supported by {@link AbstractRenderer}.
214
+ * However, such content won't support {@link SVGRenderer} or {@link TextOnlyRenderer}
215
+ * by default.
216
+ *
217
+ * Use with caution.
218
+ */
219
+ drawWithRawRenderingContext(callback) {
220
+ this.ctx.save();
221
+ this.transformBy(this.getCanvasToScreenTransform());
222
+ callback(this.ctx);
223
+ this.ctx.restore();
224
+ }
211
225
  // @internal
212
226
  drawPoints(...points) {
213
227
  const pointRadius = 10;
@@ -15,6 +15,9 @@ type FromViewportOptions = {
15
15
  */
16
16
  useViewBoxForPositioning?: boolean;
17
17
  };
18
+ type DrawWithSVGParentContext = {
19
+ sanitize: boolean;
20
+ };
18
21
  /**
19
22
  * Renders onto an `SVGElement`.
20
23
  *
@@ -57,7 +60,22 @@ export default class SVGRenderer extends AbstractRenderer {
57
60
  protected traceCubicBezierCurve(_controlPoint1: Point2, _controlPoint2: Point2, _endPoint: Point2): void;
58
61
  protected traceQuadraticBezierCurve(_controlPoint: Point2, _endPoint: Point2): void;
59
62
  drawPoints(...points: Point2[]): void;
63
+ /**
64
+ * Adds a **copy** of the given element directly to the container
65
+ * SVG element, **without applying transforms**.
66
+ *
67
+ * If `sanitize` is enabled, this does nothing.
68
+ */
60
69
  drawSVGElem(elem: SVGElement): void;
70
+ /**
71
+ * Allows rendering directly to the underlying SVG element. Rendered
72
+ * content is added to a `<g>` element that's passed as `parent` to `callback`.
73
+ *
74
+ * **Note**: Unlike {@link drawSVGElem}, this method can be used even if `sanitize` is `true`.
75
+ * In this case, it's the responsibility of `callback` to ensure that everything added
76
+ * to `parent` is safe to render.
77
+ */
78
+ drawWithSVGParent(callback: (parent: SVGGElement, context: DrawWithSVGParentContext) => void): void;
61
79
  isTooSmallToRender(_rect: Rect2): boolean;
62
80
  /**
63
81
  * Creates a new SVG element and `SVGRenerer` with `width`, `height`, `viewBox`,
@@ -341,7 +341,12 @@ export default class SVGRenderer extends AbstractRenderer {
341
341
  this.elem.appendChild(elem);
342
342
  });
343
343
  }
344
- // Renders a **copy** of the given element.
344
+ /**
345
+ * Adds a **copy** of the given element directly to the container
346
+ * SVG element, **without applying transforms**.
347
+ *
348
+ * If `sanitize` is enabled, this does nothing.
349
+ */
345
350
  drawSVGElem(elem) {
346
351
  if (this.sanitize) {
347
352
  return;
@@ -355,6 +360,21 @@ export default class SVGRenderer extends AbstractRenderer {
355
360
  this.elem.appendChild(elemToDraw);
356
361
  this.objectElems?.push(elemToDraw);
357
362
  }
363
+ /**
364
+ * Allows rendering directly to the underlying SVG element. Rendered
365
+ * content is added to a `<g>` element that's passed as `parent` to `callback`.
366
+ *
367
+ * **Note**: Unlike {@link drawSVGElem}, this method can be used even if `sanitize` is `true`.
368
+ * In this case, it's the responsibility of `callback` to ensure that everything added
369
+ * to `parent` is safe to render.
370
+ */
371
+ drawWithSVGParent(callback) {
372
+ const parent = document.createElementNS(svgNameSpace, 'g');
373
+ this.transformFrom(Mat33.identity, parent, true);
374
+ callback(parent, { sanitize: this.sanitize });
375
+ this.elem.appendChild(parent);
376
+ this.objectElems?.push(parent);
377
+ }
358
378
  isTooSmallToRender(_rect) {
359
379
  return false;
360
380
  }
@@ -134,6 +134,8 @@ const createHelpPage = (helpItems, onItemClick, onBackgroundClick, context) => {
134
134
  clonedElement.style.margin = '0';
135
135
  const clonedElementContainer = document.createElement('div');
136
136
  clonedElementContainer.classList.add('cloned-element-container');
137
+ clonedElementContainer.role = 'group';
138
+ clonedElementContainer.ariaLabel = context.localization.helpControlsAccessibilityLabel;
137
139
  clonedElementContainer.style.position = 'absolute';
138
140
  clonedElementContainer.style.left = `${targetBBox.topLeft.x}px`;
139
141
  clonedElementContainer.style.top = `${targetBBox.topLeft.y}px`;
@@ -152,11 +154,11 @@ const createHelpPage = (helpItems, onItemClick, onBackgroundClick, context) => {
152
154
  };
153
155
  const onItemChange = () => {
154
156
  const helpTextElement = document.createElement('div');
155
- helpTextElement.innerText = currentItem?.helpText ?? '';
157
+ helpTextElement.textContent = currentItem?.helpText ?? '';
156
158
  // For tests
157
159
  helpTextElement.classList.add('current-item-help');
158
160
  const navigationHelpElement = document.createElement('div');
159
- navigationHelpElement.innerText = context.localization.helpScreenNavigationHelp;
161
+ navigationHelpElement.textContent = context.localization.helpScreenNavigationHelp;
160
162
  navigationHelpElement.classList.add('navigation-help');
161
163
  textLabel.replaceChildren(helpTextElement, ...(currentItemIndex === 0 ? [navigationHelpElement] : []));
162
164
  updateClonedElementStates();
@@ -273,8 +275,8 @@ class HelpDisplay {
273
275
  navigationButtonContainer.classList.add('navigation-buttons');
274
276
  const nextButton = document.createElement('button');
275
277
  const previousButton = document.createElement('button');
276
- nextButton.innerText = this.context.localization.next;
277
- previousButton.innerText = this.context.localization.previous;
278
+ nextButton.textContent = this.context.localization.next;
279
+ previousButton.textContent = this.context.localization.previous;
278
280
  nextButton.classList.add('next');
279
281
  previousButton.classList.add('previous');
280
282
  const updateButtonVisibility = () => {
@@ -1,6 +1,7 @@
1
1
  export interface ToolbarUtilsLocalization {
2
2
  help: string;
3
3
  helpScreenNavigationHelp: string;
4
+ helpControlsAccessibilityLabel: string;
4
5
  helpHidden: string;
5
6
  next: string;
6
7
  previous: string;
@@ -5,4 +5,5 @@ export const defaultToolbarUtilsLocalization = {
5
5
  previous: 'Previous',
6
6
  close: 'Close',
7
7
  helpScreenNavigationHelp: 'Click on a control for more information.',
8
+ helpControlsAccessibilityLabel: 'Controls: Activate a control to show help.',
8
9
  };
@@ -62,7 +62,7 @@ class DocumentPropertiesWidget extends BaseWidget {
62
62
  setBackgroundType(backgroundType) {
63
63
  const prevBackgroundColor = this.editor.estimateBackgroundColor();
64
64
  const newBackground = new BackgroundComponent(backgroundType, prevBackgroundColor);
65
- const addBackgroundCommand = this.editor.image.addElement(newBackground);
65
+ const addBackgroundCommand = this.editor.image.addComponent(newBackground);
66
66
  return uniteCommands([this.removeBackgroundComponents(), addBackgroundCommand]);
67
67
  }
68
68
  /** Returns the type of the topmost background component */
@@ -270,7 +270,7 @@ class InsertImageWidget extends BaseWidget {
270
270
  const widthAdjustTransform = Mat33.scaling2D(originalWidth / newWidth);
271
271
  const commands = [];
272
272
  for (const component of newComponents) {
273
- commands.push(EditorImage.addElement(component), component.transformBy(originalTransform.rightMul(widthAdjustTransform)), component.setZIndex(editingImage.getZIndex()));
273
+ commands.push(EditorImage.addComponent(component), component.transformBy(originalTransform.rightMul(widthAdjustTransform)), component.setZIndex(editingImage.getZIndex()));
274
274
  }
275
275
  this.editor.dispatch(uniteCommands([...commands, eraseCommand]));
276
276
  selectionTools[0]?.setSelection(newComponents);
@@ -17,7 +17,7 @@ labelText, defaultId, choices) => {
17
17
  outerContainer.classList.add(`${toolbarCSSPrefix}grid-selector`);
18
18
  const selectedValue = MutableReactiveValue.fromInitialValue(defaultId);
19
19
  const menuContainer = document.createElement('div');
20
- menuContainer.setAttribute('role', 'menu');
20
+ menuContainer.role = 'group';
21
21
  menuContainer.id = `${toolbarCSSPrefix}-grid-select-id-${idCounter++}`;
22
22
  stopPropagationOfScrollingWheelEvents(menuContainer);
23
23
  const label = document.createElement('label');
@@ -139,7 +139,7 @@ export default class Eraser extends BaseTool {
139
139
  const line = new LineSegment2(this.lastPoint, currentPoint);
140
140
  const region = Rect2.union(line.bbox, eraserRect);
141
141
  const intersectingElems = this.editor.image
142
- .getElementsIntersectingRegion(region)
142
+ .getComponentsIntersecting(region)
143
143
  .filter((component) => {
144
144
  return component.intersects(line) || component.intersectsRect(eraserRect);
145
145
  });
@@ -178,7 +178,7 @@ export default class Eraser extends BaseTool {
178
178
  toAdd.push(...targetElem.withRegionErased(erasePath, this.editor.viewport));
179
179
  }
180
180
  const eraseCommand = new Erase(toErase);
181
- const newAddCommands = toAdd.map((elem) => EditorImage.addElement(elem));
181
+ const newAddCommands = toAdd.map((elem) => EditorImage.addComponent(elem));
182
182
  eraseCommand.apply(this.editor);
183
183
  newAddCommands.forEach((command) => command.apply(this.editor));
184
184
  const finalToErase = [];
@@ -234,7 +234,7 @@ export default class Eraser extends BaseTool {
234
234
  this.toRemove = this.toRemove.filter((other) => other !== item);
235
235
  }
236
236
  }
237
- commands.push(...[...this.toAdd].map((a) => EditorImage.addElement(a)));
237
+ commands.push(...[...this.toAdd].map((a) => EditorImage.addComponent(a)));
238
238
  this.addCommands = [];
239
239
  }
240
240
  if (this.eraseCommands.length > 0) {
@@ -22,7 +22,7 @@ export default class FindTool extends BaseTool {
22
22
  }
23
23
  getMatches(searchFor) {
24
24
  const lowerSearchFor = searchFor.toLocaleLowerCase();
25
- const matchingComponents = this.editor.image.getAllElements().filter((component) => {
25
+ const matchingComponents = this.editor.image.getAllComponents().filter((component) => {
26
26
  let text = '';
27
27
  if (component instanceof TextComponent) {
28
28
  text = component.getText();
@@ -71,7 +71,10 @@ export default class PasteHandler extends BaseTool {
71
71
  async doSVGPaste(data) {
72
72
  this.editor.showLoadingWarning(0);
73
73
  try {
74
- const loader = SVGLoader.fromString(data, true);
74
+ const loader = SVGLoader.fromString(data, {
75
+ sanitize: true,
76
+ plugins: this.editor.getCurrentSettings().svg?.loaderPlugins ?? [],
77
+ });
75
78
  const components = [];
76
79
  await loader.start((component) => {
77
80
  components.push(component);
@@ -213,7 +213,7 @@ export default class Pen extends BaseTool {
213
213
  this.editor.announceForAccessibility(this.editor.localization.autocorrectedTo(stroke.description(this.editor.localization)));
214
214
  }
215
215
  const canFlatten = true;
216
- const action = EditorImage.addElement(stroke, canFlatten);
216
+ const action = EditorImage.addComponent(stroke, canFlatten);
217
217
  this.editor.dispatch(action);
218
218
  }
219
219
  else {
@@ -17,7 +17,7 @@ export default class SelectAllShortcutHandler extends BaseTool {
17
17
  if (selectionTools.length > 0) {
18
18
  const selectionTool = selectionTools[0];
19
19
  selectionTool.setEnabled(true);
20
- selectionTool.setSelection(this.editor.image.getAllElements());
20
+ selectionTool.setSelection(this.editor.image.getAllComponents());
21
21
  return true;
22
22
  }
23
23
  }