js-draw 0.15.2 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.github/ISSUE_TEMPLATE/translation.yml +56 -0
  2. package/CHANGELOG.md +12 -0
  3. package/dist/bundle.js +1 -1
  4. package/dist/src/Editor.d.ts +11 -0
  5. package/dist/src/Editor.js +52 -4
  6. package/dist/src/EditorImage.d.ts +11 -11
  7. package/dist/src/EditorImage.js +54 -18
  8. package/dist/src/SVGLoader.js +6 -0
  9. package/dist/src/Viewport.d.ts +5 -0
  10. package/dist/src/Viewport.js +11 -0
  11. package/dist/src/components/AbstractComponent.d.ts +1 -0
  12. package/dist/src/components/AbstractComponent.js +5 -0
  13. package/dist/src/components/ImageBackground.d.ts +2 -1
  14. package/dist/src/components/ImageBackground.js +8 -1
  15. package/dist/src/localizations/es.js +1 -1
  16. package/dist/src/toolbar/HTMLToolbar.d.ts +26 -2
  17. package/dist/src/toolbar/HTMLToolbar.js +130 -15
  18. package/dist/src/toolbar/IconProvider.d.ts +2 -0
  19. package/dist/src/toolbar/IconProvider.js +44 -0
  20. package/dist/src/toolbar/localization.d.ts +5 -0
  21. package/dist/src/toolbar/localization.js +5 -0
  22. package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  23. package/dist/src/toolbar/widgets/ActionButtonWidget.js +5 -1
  24. package/dist/src/toolbar/widgets/BaseToolWidget.d.ts +1 -1
  25. package/dist/src/toolbar/widgets/BaseToolWidget.js +2 -1
  26. package/dist/src/toolbar/widgets/BaseWidget.d.ts +7 -2
  27. package/dist/src/toolbar/widgets/BaseWidget.js +23 -1
  28. package/dist/src/toolbar/widgets/DocumentPropertiesWidget.d.ts +19 -0
  29. package/dist/src/toolbar/widgets/DocumentPropertiesWidget.js +135 -0
  30. package/dist/src/toolbar/widgets/OverflowWidget.d.ts +25 -0
  31. package/dist/src/toolbar/widgets/OverflowWidget.js +65 -0
  32. package/dist/src/toolbar/widgets/lib.d.ts +1 -0
  33. package/dist/src/toolbar/widgets/lib.js +1 -0
  34. package/dist/src/tools/PasteHandler.js +2 -2
  35. package/dist/src/tools/SelectionTool/Selection.d.ts +2 -1
  36. package/dist/src/tools/SelectionTool/Selection.js +2 -1
  37. package/dist/src/tools/SelectionTool/SelectionTool.js +1 -1
  38. package/package.json +1 -1
  39. package/src/Editor.css +4 -0
  40. package/src/Editor.loadFrom.test.ts +24 -0
  41. package/src/Editor.ts +59 -4
  42. package/src/EditorImage.ts +66 -23
  43. package/src/SVGLoader.test.ts +57 -0
  44. package/src/SVGLoader.ts +7 -0
  45. package/src/Viewport.ts +13 -0
  46. package/src/components/AbstractComponent.ts +6 -0
  47. package/src/components/ImageBackground.test.ts +35 -0
  48. package/src/components/ImageBackground.ts +10 -1
  49. package/src/localizations/es.ts +8 -0
  50. package/src/math/Mat33.test.ts +30 -5
  51. package/src/rendering/renderers/CanvasRenderer.ts +1 -1
  52. package/src/toolbar/HTMLToolbar.ts +169 -16
  53. package/src/toolbar/IconProvider.ts +46 -0
  54. package/src/toolbar/localization.ts +10 -0
  55. package/src/toolbar/toolbar.css +2 -0
  56. package/src/toolbar/widgets/ActionButtonWidget.ts +5 -0
  57. package/src/toolbar/widgets/BaseToolWidget.ts +3 -1
  58. package/src/toolbar/widgets/BaseWidget.ts +34 -2
  59. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +185 -0
  60. package/src/toolbar/widgets/OverflowWidget.css +9 -0
  61. package/src/toolbar/widgets/OverflowWidget.ts +83 -0
  62. package/src/toolbar/widgets/lib.ts +2 -1
  63. package/src/tools/PasteHandler.ts +3 -2
  64. package/src/tools/SelectionTool/Selection.ts +2 -1
  65. package/src/tools/SelectionTool/SelectionTool.ts +1 -1
@@ -8,6 +8,7 @@ import { Point2 } from './math/Vec2';
8
8
  import HTMLToolbar from './toolbar/HTMLToolbar';
9
9
  import { RenderablePathSpec } from './rendering/renderers/AbstractRenderer';
10
10
  import Display, { RenderingMode } from './rendering/Display';
11
+ import Color4 from './Color4';
11
12
  import Pointer from './Pointer';
12
13
  import Rect2 from './math/Rect2';
13
14
  import { EditorLocalization } from './localization';
@@ -222,7 +223,17 @@ export declare class Editor {
222
223
  addAndCenterComponents(components: AbstractComponent[], selectComponents?: boolean): Promise<void>;
223
224
  toDataURL(format?: 'image/png' | 'image/jpeg' | 'image/webp'): string;
224
225
  toSVG(): SVGElement;
226
+ /**
227
+ * Load editor data from an `ImageLoader` (e.g. an {@link SVGLoader}).
228
+ *
229
+ * @see loadFromSVG
230
+ */
225
231
  loadFrom(loader: ImageLoader): Promise<void>;
232
+ private getTopmostBackgroundComponent;
233
+ /**
234
+ * Set the background color of the image.
235
+ */
236
+ setBackgroundColor(color: Color4): Command;
226
237
  getImportExportRect(): Rect2;
227
238
  setImportExportRect(imageRect: Rect2): Command;
228
239
  /**
@@ -30,6 +30,8 @@ import untilNextAnimationFrame from './util/untilNextAnimationFrame';
30
30
  import fileToBase64 from './util/fileToBase64';
31
31
  import uniteCommands from './commands/uniteCommands';
32
32
  import SelectionTool from './tools/SelectionTool/SelectionTool';
33
+ import Erase from './commands/Erase';
34
+ import ImageBackground, { BackgroundType } from './components/ImageBackground';
33
35
  /**
34
36
  * The main entrypoint for the full editor.
35
37
  *
@@ -233,14 +235,21 @@ export class Editor {
233
235
  });
234
236
  this.notifier.on(EditorEventType.DisplayResized, _event => {
235
237
  this.viewport.updateScreenSize(Vec2.of(this.display.width, this.display.height));
238
+ this.queueRerender();
236
239
  });
237
- window.addEventListener('resize', () => {
240
+ const handleResize = () => {
238
241
  this.notifier.dispatch(EditorEventType.DisplayResized, {
239
242
  kind: EditorEventType.DisplayResized,
240
243
  newSize: Vec2.of(this.display.width, this.display.height),
241
244
  });
242
- this.queueRerender();
243
- });
245
+ };
246
+ if ('ResizeObserver' in window) {
247
+ const resizeObserver = new ResizeObserver(handleResize);
248
+ resizeObserver.observe(this.container);
249
+ }
250
+ else {
251
+ addEventListener('resize', handleResize);
252
+ }
244
253
  this.accessibilityControlArea.addEventListener('input', () => {
245
254
  this.accessibilityControlArea.value = '';
246
255
  });
@@ -706,7 +715,7 @@ export class Editor {
706
715
  return dataURL;
707
716
  }
708
717
  toSVG() {
709
- const importExportViewport = this.image.getImportExportViewport();
718
+ const importExportViewport = this.image.getImportExportViewport().getTemporaryClone();
710
719
  const svgNameSpace = 'http://www.w3.org/2000/svg';
711
720
  const result = document.createElementNS(svgNameSpace, 'svg');
712
721
  const renderer = new SVGRenderer(result, importExportViewport);
@@ -728,10 +737,17 @@ export class Editor {
728
737
  result.setAttribute('xmlns', svgNameSpace);
729
738
  return result;
730
739
  }
740
+ /**
741
+ * Load editor data from an `ImageLoader` (e.g. an {@link SVGLoader}).
742
+ *
743
+ * @see loadFromSVG
744
+ */
731
745
  loadFrom(loader) {
732
746
  return __awaiter(this, void 0, void 0, function* () {
733
747
  this.showLoadingWarning(0);
734
748
  this.display.setDraftMode(true);
749
+ const originalBackgrounds = this.image.getBackgroundComponents();
750
+ const eraseBackgroundCommand = new Erase(originalBackgrounds);
735
751
  yield loader.start((component) => __awaiter(this, void 0, void 0, function* () {
736
752
  yield this.dispatchNoAnnounce(EditorImage.addElement(component));
737
753
  }), (countProcessed, totalToProcess) => {
@@ -745,11 +761,43 @@ export class Editor {
745
761
  this.dispatchNoAnnounce(this.setImportExportRect(importExportRect), false);
746
762
  this.dispatchNoAnnounce(this.viewport.zoomTo(importExportRect), false);
747
763
  });
764
+ // Ensure that we don't have multiple overlapping BackgroundComponents. Remove
765
+ // old BackgroundComponents.
766
+ // Overlapping BackgroundComponents may cause changing the background color to
767
+ // not work properly.
768
+ if (this.image.getBackgroundComponents().length !== originalBackgrounds.length) {
769
+ yield this.dispatchNoAnnounce(eraseBackgroundCommand);
770
+ }
748
771
  this.hideLoadingWarning();
749
772
  this.display.setDraftMode(false);
750
773
  this.queueRerender();
751
774
  });
752
775
  }
776
+ getTopmostBackgroundComponent() {
777
+ let background = null;
778
+ // Find a background component, if one exists.
779
+ // Use the last (topmost) background component if there are multiple.
780
+ for (const component of this.image.getBackgroundComponents()) {
781
+ if (component instanceof ImageBackground) {
782
+ background = component;
783
+ }
784
+ }
785
+ return background;
786
+ }
787
+ /**
788
+ * Set the background color of the image.
789
+ */
790
+ setBackgroundColor(color) {
791
+ let background = this.getTopmostBackgroundComponent();
792
+ if (!background) {
793
+ const backgroundType = color.eq(Color4.transparent) ? BackgroundType.None : BackgroundType.SolidColor;
794
+ background = new ImageBackground(backgroundType, color);
795
+ return this.image.addElement(background);
796
+ }
797
+ else {
798
+ return background.updateStyle({ color });
799
+ }
800
+ }
753
801
  // Returns the size of the visible region of the output SVG
754
802
  getImportExportRect() {
755
803
  return this.image.getImportExportViewport().visibleRect;
@@ -1,12 +1,11 @@
1
- import Editor from './Editor';
2
1
  import AbstractRenderer from './rendering/renderers/AbstractRenderer';
3
2
  import Viewport from './Viewport';
4
3
  import AbstractComponent from './components/AbstractComponent';
5
4
  import Rect2 from './math/Rect2';
6
- import { EditorLocalization } from './localization';
7
5
  import RenderingCache from './rendering/caching/RenderingCache';
8
6
  import SerializableCommand from './commands/SerializableCommand';
9
7
  import EventDispatcher from './EventDispatcher';
8
+ import Command from './commands/Command';
10
9
  export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
11
10
  export declare enum EditorImageEventType {
12
11
  ExportViewportChanged = 0
@@ -16,6 +15,7 @@ export type EditorImageNotifier = EventDispatcher<EditorImageEventType, {
16
15
  }>;
17
16
  export default class EditorImage {
18
17
  private root;
18
+ private background;
19
19
  private componentsById;
20
20
  /** Viewport for the exported/imported image. */
21
21
  private importExportViewport;
@@ -25,18 +25,17 @@ export default class EditorImage {
25
25
  * @returns a `Viewport` for rendering the image when importing/exporting.
26
26
  */
27
27
  getImportExportViewport(): Viewport;
28
- setImportExportRect(imageRect: Rect2): {
29
- apply(editor: Editor): void;
30
- unapply(editor: Editor): void;
31
- description(_editor: Editor, localizationTable: EditorLocalization): string;
32
- onDrop(_editor: Editor): void;
33
- };
28
+ setImportExportRect(imageRect: Rect2): Command;
29
+ getBackgroundComponents(): AbstractComponent[];
34
30
  findParent(elem: AbstractComponent): ImageNode | null;
35
31
  queueRerenderOf(elem: AbstractComponent): void;
36
32
  /** @internal */
37
33
  renderWithCache(screenRenderer: AbstractRenderer, cache: RenderingCache, viewport: Viewport): void;
38
- /** @internal */
39
- render(renderer: AbstractRenderer, viewport: Viewport): void;
34
+ /**
35
+ * Renders all nodes visible from `viewport` (or all nodes if `viewport = null`)
36
+ * @internal
37
+ */
38
+ render(renderer: AbstractRenderer, viewport: Viewport | null): void;
40
39
  /** Renders all nodes, even ones not within the viewport. @internal */
41
40
  renderAll(renderer: AbstractRenderer): void;
42
41
  /** @returns all elements in the image, sorted by z-index. This can be slow for large images. */
@@ -83,6 +82,7 @@ export declare class ImageNode {
83
82
  private getChildrenIntersectingRegion;
84
83
  getChildrenOrSelfIntersectingRegion(region: Rect2): ImageNode[];
85
84
  getLeavesIntersectingRegion(region: Rect2, isTooSmall?: TooSmallToRenderCheck): ImageNode[];
85
+ getChildWithContent(target: AbstractComponent): ImageNode | null;
86
86
  getLeaves(): ImageNode[];
87
87
  addLeaf(leaf: AbstractComponent): ImageNode;
88
88
  getBBox(): Rect2;
@@ -90,6 +90,6 @@ export declare class ImageNode {
90
90
  private updateParents;
91
91
  private rebalance;
92
92
  remove(): void;
93
- render(renderer: AbstractRenderer, visibleRect: Rect2): void;
93
+ render(renderer: AbstractRenderer, visibleRect?: Rect2): void;
94
94
  }
95
95
  export {};
@@ -20,6 +20,7 @@ export default class EditorImage {
20
20
  // @internal
21
21
  constructor() {
22
22
  this.root = new ImageNode();
23
+ this.background = new ImageNode();
23
24
  this.componentsById = {};
24
25
  this.notifier = new EventDispatcher();
25
26
  this.importExportViewport = new Viewport(() => {
@@ -58,15 +59,24 @@ export default class EditorImage {
58
59
  }
59
60
  };
60
61
  }
61
- // Returns the parent of the given element, if it exists.
62
- findParent(elem) {
63
- const candidates = this.root.getLeavesIntersectingRegion(elem.getBBox());
64
- for (const candidate of candidates) {
65
- if (candidate.getContent() === elem) {
66
- return candidate;
62
+ // Returns all components that make up the background of this image. These
63
+ // components are rendered below all other components.
64
+ getBackgroundComponents() {
65
+ const result = [];
66
+ const leaves = this.background.getLeaves();
67
+ sortLeavesByZIndex(leaves);
68
+ for (const leaf of leaves) {
69
+ const content = leaf.getContent();
70
+ if (content) {
71
+ result.push(content);
67
72
  }
68
73
  }
69
- return null;
74
+ return result;
75
+ }
76
+ // Returns the parent of the given element, if it exists.
77
+ findParent(elem) {
78
+ var _a;
79
+ return (_a = this.background.getChildWithContent(elem)) !== null && _a !== void 0 ? _a : this.root.getChildWithContent(elem);
70
80
  }
71
81
  // Forces a re-render of `elem` when the image is next re-rendered as a whole.
72
82
  // Does nothing if `elem` is not in this.
@@ -81,19 +91,20 @@ export default class EditorImage {
81
91
  }
82
92
  /** @internal */
83
93
  renderWithCache(screenRenderer, cache, viewport) {
94
+ this.background.render(screenRenderer, viewport.visibleRect);
84
95
  cache.render(screenRenderer, this.root, viewport);
85
96
  }
86
- /** @internal */
97
+ /**
98
+ * Renders all nodes visible from `viewport` (or all nodes if `viewport = null`)
99
+ * @internal
100
+ */
87
101
  render(renderer, viewport) {
88
- this.root.render(renderer, viewport.visibleRect);
102
+ this.background.render(renderer, viewport === null || viewport === void 0 ? void 0 : viewport.visibleRect);
103
+ this.root.render(renderer, viewport === null || viewport === void 0 ? void 0 : viewport.visibleRect);
89
104
  }
90
105
  /** Renders all nodes, even ones not within the viewport. @internal */
91
106
  renderAll(renderer) {
92
- const leaves = this.root.getLeaves();
93
- sortLeavesByZIndex(leaves);
94
- for (const leaf of leaves) {
95
- leaf.getContent().render(renderer, leaf.getBBox());
96
- }
107
+ this.render(renderer, null);
97
108
  }
98
109
  /** @returns all elements in the image, sorted by z-index. This can be slow for large images. */
99
110
  getAllElements() {
@@ -123,11 +134,19 @@ export default class EditorImage {
123
134
  addElementDirectly(elem) {
124
135
  elem.onAddToImage(this);
125
136
  this.componentsById[elem.getId()] = elem;
126
- return this.root.addLeaf(elem);
137
+ // If a background component, add to the background. Else,
138
+ // add to the normal component tree.
139
+ const parentTree = elem.isBackground() ? this.background : this.root;
140
+ return parentTree.addLeaf(elem);
127
141
  }
128
142
  removeElementDirectly(element) {
129
143
  const container = this.findParent(element);
130
144
  container === null || container === void 0 ? void 0 : container.remove();
145
+ if (container) {
146
+ this.onDestroyElement(element);
147
+ return true;
148
+ }
149
+ return false;
131
150
  }
132
151
  /**
133
152
  * Returns a command that adds the given element to the `EditorImage`.
@@ -140,7 +159,7 @@ export default class EditorImage {
140
159
  return new EditorImage.AddElementCommand(elem, applyByFlattening);
141
160
  }
142
161
  /** @see EditorImage.addElement */
143
- addElement(elem, applyByFlattening = true) {
162
+ addElement(elem, applyByFlattening) {
144
163
  return EditorImage.addElement(elem, applyByFlattening);
145
164
  }
146
165
  }
@@ -247,6 +266,17 @@ export class ImageNode {
247
266
  }
248
267
  return result;
249
268
  }
269
+ // Returns the child of this with the target content or `null` if no
270
+ // such child exists.
271
+ getChildWithContent(target) {
272
+ const candidates = this.getLeavesIntersectingRegion(target.getBBox());
273
+ for (const candidate of candidates) {
274
+ if (candidate.getContent() === target) {
275
+ return candidate;
276
+ }
277
+ }
278
+ return null;
279
+ }
250
280
  // Returns a list of leaves with this as an ancestor.
251
281
  // Like getLeavesInRegion, but does not check whether ancestors are in a given rectangle
252
282
  getLeaves() {
@@ -359,12 +389,12 @@ export class ImageNode {
359
389
  // Remove this node and all of its children
360
390
  remove() {
361
391
  var _a;
392
+ (_a = this.content) === null || _a === void 0 ? void 0 : _a.onRemoveFromImage();
362
393
  if (!this.parent) {
363
394
  this.content = null;
364
395
  this.children = [];
365
396
  return;
366
397
  }
367
- (_a = this.content) === null || _a === void 0 ? void 0 : _a.onRemoveFromImage();
368
398
  const oldChildCount = this.parent.children.length;
369
399
  this.parent.children = this.parent.children.filter(node => {
370
400
  return node !== this;
@@ -380,7 +410,13 @@ export class ImageNode {
380
410
  this.children = [];
381
411
  }
382
412
  render(renderer, visibleRect) {
383
- const leaves = this.getLeavesIntersectingRegion(visibleRect, rect => renderer.isTooSmallToRender(rect));
413
+ let leaves;
414
+ if (visibleRect) {
415
+ leaves = this.getLeavesIntersectingRegion(visibleRect, rect => renderer.isTooSmallToRender(rect));
416
+ }
417
+ else {
418
+ leaves = this.getLeaves();
419
+ }
384
420
  sortLeavesByZIndex(leaves);
385
421
  for (const leaf of leaves) {
386
422
  // Leaves by definition have content
@@ -215,6 +215,12 @@ export default class SVGLoader {
215
215
  // In some environments, computedStyles.fontSize can be increased by the system.
216
216
  // Thus, to prevent text from growing on load/save, prefer .style.fontSize.
217
217
  let fontSizeMatch = fontSizeExp.exec(elem.style.fontSize);
218
+ if (!fontSizeMatch && elem.tagName.toLowerCase() === 'tspan' && elem.parentElement) {
219
+ // Try to inherit the font size of the parent text element.
220
+ fontSizeMatch = fontSizeExp.exec(elem.parentElement.style.fontSize);
221
+ }
222
+ // If we still couldn't find a font size, try to use computedStyles (which can be
223
+ // wrong).
218
224
  if (!fontSizeMatch) {
219
225
  fontSizeMatch = fontSizeExp.exec(computedStyles.fontSize);
220
226
  }
@@ -16,6 +16,11 @@ export declare class Viewport {
16
16
  private inverseTransform;
17
17
  private screenRect;
18
18
  constructor(onTransformChangeCallback: TransformChangeCallback);
19
+ /**
20
+ * @returns a temporary copy of `this` that does not notify when modified. This is
21
+ * useful when rendering with a temporarily different viewport.
22
+ */
23
+ getTemporaryClone(): Viewport;
19
24
  updateScreenSize(screenSize: Vec2): void;
20
25
  /** Get the screen's visible region transformed into canvas space. */
21
26
  get visibleRect(): Rect2;
@@ -24,6 +24,17 @@ export class Viewport {
24
24
  this.resetTransform(Mat33.identity);
25
25
  this.screenRect = Rect2.empty;
26
26
  }
27
+ /**
28
+ * @returns a temporary copy of `this` that does not notify when modified. This is
29
+ * useful when rendering with a temporarily different viewport.
30
+ */
31
+ getTemporaryClone() {
32
+ const result = new Viewport(() => { });
33
+ result.transform = this.transform;
34
+ result.inverseTransform = this.inverseTransform;
35
+ result.screenRect = this.screenRect;
36
+ return result;
37
+ }
27
38
  // @internal
28
39
  updateScreenSize(screenSize) {
29
40
  this.screenRect = this.screenRect.resizedTo(screenSize);
@@ -50,6 +50,7 @@ export default abstract class AbstractComponent {
50
50
  transformBy(affineTransfm: Mat33): SerializableCommand;
51
51
  setZIndex(newZIndex: number): SerializableCommand;
52
52
  isSelectable(): boolean;
53
+ isBackground(): boolean;
53
54
  getProportionalRenderingTime(): number;
54
55
  private static transformElementCommandId;
55
56
  private static TransformElementCommand;
@@ -94,6 +94,11 @@ export default class AbstractComponent {
94
94
  isSelectable() {
95
95
  return true;
96
96
  }
97
+ // @returns true iff this component should be added to the background, rather than the
98
+ // foreground of the image.
99
+ isBackground() {
100
+ return false;
101
+ }
97
102
  // @returns an approximation of the proportional time it takes to render this component.
98
103
  // This is intended to be a rough estimate, but, for example, a stroke with two points sould have
99
104
  // a renderingWeight approximately twice that of a stroke with one point.
@@ -23,13 +23,14 @@ export default class ImageBackground extends AbstractComponent implements Restyl
23
23
  constructor(backgroundType: BackgroundType, mainColor: Color4);
24
24
  getStyle(): ComponentStyle;
25
25
  updateStyle(style: ComponentStyle): SerializableCommand;
26
- forceStyle(style: ComponentStyle, _editor: Editor | null): void;
26
+ forceStyle(style: ComponentStyle, editor: Editor | null): void;
27
27
  onAddToImage(image: EditorImage): void;
28
28
  onRemoveFromImage(): void;
29
29
  private recomputeBBox;
30
30
  render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
31
31
  intersects(lineSegment: LineSegment2): boolean;
32
32
  isSelectable(): boolean;
33
+ isBackground(): boolean;
33
34
  protected serializeToJSON(): {
34
35
  mainColor: string;
35
36
  backgroundType: BackgroundType;
@@ -33,7 +33,7 @@ export default class ImageBackground extends AbstractComponent {
33
33
  return createRestyleComponentCommand(this.getStyle(), style, this);
34
34
  }
35
35
  // @internal
36
- forceStyle(style, _editor) {
36
+ forceStyle(style, editor) {
37
37
  const fill = style.color;
38
38
  if (!fill) {
39
39
  return;
@@ -45,6 +45,10 @@ export default class ImageBackground extends AbstractComponent {
45
45
  else {
46
46
  this.backgroundType = BackgroundType.SolidColor;
47
47
  }
48
+ if (editor) {
49
+ editor.image.queueRerenderOf(this);
50
+ editor.queueRerender();
51
+ }
48
52
  }
49
53
  onAddToImage(image) {
50
54
  if (this.viewportSizeChangeListener) {
@@ -96,6 +100,9 @@ export default class ImageBackground extends AbstractComponent {
96
100
  isSelectable() {
97
101
  return false;
98
102
  }
103
+ isBackground() {
104
+ return true;
105
+ }
99
106
  serializeToJSON() {
100
107
  return {
101
108
  mainColor: this.mainColor.toHexString(),
@@ -14,5 +14,5 @@ const localization = Object.assign(Object.assign({}, defaultEditorLocalization),
14
14
  return `Color fue cambiado a ${color}`;
15
15
  }, keyboardPanZoom: 'Mover la pantalla con el teclado', penTool: function (penId) {
16
16
  return `Lapiz ${penId}`;
17
- }, selectionTool: 'Selecciona', eraserTool: 'Borrador', textTool: 'Texto', enterTextToInsert: 'Entra texto', rerenderAsText: 'Redibuja la pantalla al texto', image: 'Imagen', imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`, imageLoadError: (message) => `Error cargando imagen: ${message}` });
17
+ }, selectionTool: 'Selecciona', eraserTool: 'Borrador', textTool: 'Texto', enterTextToInsert: 'Entra texto', textSize: 'Tamaño', rerenderAsText: 'Redibuja la pantalla al texto', lockRotation: 'Bloquea rotación', image: 'Imagen', imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`, imageLoadError: (message) => `Error cargando imagen: ${message}`, toggleOverflow: 'Más', documentProperties: 'Fondo', imageWidthOption: 'Ancho: ', imageHeightOption: 'Alto: ', backgroundColor: 'Color de fondo: ' });
18
18
  export default localization;
@@ -12,12 +12,20 @@ export default class HTMLToolbar {
12
12
  private editor;
13
13
  private localizationTable;
14
14
  private container;
15
- private widgets;
15
+ private resizeObserver;
16
+ private listeners;
17
+ private widgetOrderCounter;
18
+ private widgetsById;
19
+ private widgetList;
20
+ private overflowWidget;
16
21
  private static colorisStarted;
17
22
  private updateColoris;
18
23
  /** @internal */
19
24
  constructor(editor: Editor, parent: HTMLElement, localizationTable?: ToolbarLocalization);
20
25
  setupColorPickers(): void;
26
+ private reLayoutQueued;
27
+ private queueReLayout;
28
+ private reLayout;
21
29
  /**
22
30
  * Adds an `ActionButtonWidget` or `BaseToolWidget`. The widget should not have already have a parent
23
31
  * (i.e. its `addTo` method should not have been called).
@@ -63,17 +71,33 @@ export default class HTMLToolbar {
63
71
  *
64
72
  * @return The added button.
65
73
  */
66
- addActionButton(title: string | ActionButtonIcon, command: () => void): BaseWidget;
74
+ addActionButton(title: string | ActionButtonIcon, command: () => void, mustBeToplevel?: boolean): BaseWidget;
67
75
  addUndoRedoButtons(): void;
68
76
  addDefaultToolWidgets(): void;
69
77
  addDefaultActionButtons(): void;
78
+ /**
79
+ * Adds a widget that toggles the overflow menu. Call `addOverflowWidget` to ensure
80
+ * that this widget is in the correct space (if shown).
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * toolbar.addDefaultToolWidgets();
85
+ * toolbar.addOverflowWidget();
86
+ * toolbar.addDefaultActionButtons();
87
+ * ```
88
+ * shows the overflow widget between the default tool widgets and the default action buttons,
89
+ * if shown.
90
+ */
91
+ addOverflowWidget(): void;
70
92
  /**
71
93
  * Adds both the default tool widgets and action buttons. Equivalent to
72
94
  * ```ts
73
95
  * toolbar.addDefaultToolWidgets();
96
+ * toolbar.addOverflowWidget();
74
97
  * toolbar.addDefaultActionButtons();
75
98
  * ```
76
99
  */
77
100
  addDefaults(): void;
101
+ remove(): void;
78
102
  }
79
103
  export {};