js-draw 0.11.3 → 0.13.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 (91) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/src/Color4.d.ts +13 -0
  4. package/dist/src/Color4.js +17 -0
  5. package/dist/src/Editor.d.ts +33 -18
  6. package/dist/src/Editor.js +26 -24
  7. package/dist/src/EditorImage.d.ts +12 -0
  8. package/dist/src/EditorImage.js +12 -0
  9. package/dist/src/Pointer.d.ts +1 -0
  10. package/dist/src/Pointer.js +8 -0
  11. package/dist/src/SVGLoader.d.ts +5 -0
  12. package/dist/src/SVGLoader.js +49 -36
  13. package/dist/src/Viewport.d.ts +30 -1
  14. package/dist/src/Viewport.js +39 -9
  15. package/dist/src/commands/invertCommand.js +1 -1
  16. package/dist/src/components/AbstractComponent.d.ts +20 -0
  17. package/dist/src/components/AbstractComponent.js +32 -2
  18. package/dist/src/lib.d.ts +6 -3
  19. package/dist/src/lib.js +4 -1
  20. package/dist/src/math/Mat33.d.ts +1 -1
  21. package/dist/src/math/Mat33.js +1 -1
  22. package/dist/src/rendering/Display.d.ts +9 -11
  23. package/dist/src/rendering/Display.js +12 -14
  24. package/dist/src/rendering/lib.d.ts +3 -0
  25. package/dist/src/rendering/lib.js +3 -0
  26. package/dist/src/rendering/renderers/DummyRenderer.js +2 -2
  27. package/dist/src/rendering/renderers/SVGRenderer.js +4 -0
  28. package/dist/src/toolbar/HTMLToolbar.d.ts +51 -0
  29. package/dist/src/toolbar/HTMLToolbar.js +63 -5
  30. package/dist/src/toolbar/IconProvider.d.ts +2 -2
  31. package/dist/src/toolbar/IconProvider.js +123 -35
  32. package/dist/src/toolbar/widgets/EraserToolWidget.d.ts +8 -1
  33. package/dist/src/toolbar/widgets/EraserToolWidget.js +45 -4
  34. package/dist/src/toolbar/widgets/PenToolWidget.js +2 -2
  35. package/dist/src/toolbar/widgets/SelectionToolWidget.js +12 -3
  36. package/dist/src/tools/Eraser.d.ts +10 -1
  37. package/dist/src/tools/Eraser.js +65 -13
  38. package/dist/src/tools/PanZoom.js +1 -1
  39. package/dist/src/tools/PasteHandler.d.ts +11 -4
  40. package/dist/src/tools/PasteHandler.js +12 -5
  41. package/dist/src/tools/Pen.d.ts +7 -2
  42. package/dist/src/tools/Pen.js +39 -6
  43. package/dist/src/tools/SelectionTool/Selection.d.ts +4 -1
  44. package/dist/src/tools/SelectionTool/Selection.js +64 -27
  45. package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -0
  46. package/dist/src/tools/SelectionTool/SelectionHandle.js +6 -0
  47. package/dist/src/tools/SelectionTool/SelectionTool.d.ts +3 -1
  48. package/dist/src/tools/SelectionTool/SelectionTool.js +56 -16
  49. package/dist/src/tools/TextTool.js +10 -6
  50. package/dist/src/tools/ToolSwitcherShortcut.d.ts +8 -0
  51. package/dist/src/tools/ToolSwitcherShortcut.js +9 -3
  52. package/dist/src/tools/UndoRedoShortcut.js +2 -4
  53. package/dist/src/types.d.ts +2 -2
  54. package/package.json +2 -2
  55. package/src/Color4.test.ts +11 -0
  56. package/src/Color4.ts +23 -0
  57. package/src/Editor.ts +39 -26
  58. package/src/EditorImage.ts +12 -0
  59. package/src/Pointer.ts +19 -0
  60. package/src/SVGLoader.ts +20 -15
  61. package/src/Viewport.ts +50 -11
  62. package/src/commands/invertCommand.ts +1 -1
  63. package/src/components/AbstractComponent.ts +52 -2
  64. package/src/lib.ts +6 -3
  65. package/src/math/Mat33.ts +1 -1
  66. package/src/rendering/Display.ts +12 -15
  67. package/src/rendering/RenderingStyle.ts +1 -1
  68. package/src/rendering/lib.ts +4 -0
  69. package/src/rendering/renderers/DummyRenderer.ts +2 -3
  70. package/src/rendering/renderers/SVGRenderer.ts +4 -0
  71. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -1
  72. package/src/toolbar/HTMLToolbar.ts +81 -5
  73. package/src/toolbar/IconProvider.ts +132 -37
  74. package/src/toolbar/widgets/EraserToolWidget.ts +64 -5
  75. package/src/toolbar/widgets/PenToolWidget.ts +2 -2
  76. package/src/toolbar/widgets/SelectionToolWidget.ts +2 -2
  77. package/src/tools/Eraser.test.ts +79 -0
  78. package/src/tools/Eraser.ts +81 -17
  79. package/src/tools/PanZoom.ts +1 -1
  80. package/src/tools/PasteHandler.ts +12 -6
  81. package/src/tools/Pen.test.ts +44 -1
  82. package/src/tools/Pen.ts +53 -8
  83. package/src/tools/SelectionTool/Selection.ts +73 -23
  84. package/src/tools/SelectionTool/SelectionHandle.ts +9 -0
  85. package/src/tools/SelectionTool/SelectionTool.test.ts +138 -21
  86. package/src/tools/SelectionTool/SelectionTool.ts +70 -16
  87. package/src/tools/TextTool.ts +14 -8
  88. package/src/tools/ToolSwitcherShortcut.ts +10 -5
  89. package/src/tools/UndoRedoShortcut.ts +2 -5
  90. package/src/types.ts +2 -2
  91. package/typedoc.json +2 -2
package/src/Viewport.ts CHANGED
@@ -92,26 +92,30 @@ export class Viewport {
92
92
  this.screenRect = this.screenRect.resizedTo(screenSize);
93
93
  }
94
94
 
95
- // Get the screen's visible region transformed into canvas space.
95
+ /** Get the screen's visible region transformed into canvas space. */
96
96
  public get visibleRect(): Rect2 {
97
97
  return this.screenRect.transformedBoundingBox(this.inverseTransform);
98
98
  }
99
99
 
100
- // the given point, but in canvas coordinates
100
+ /** @returns the given point, but in canvas coordinates */
101
101
  public screenToCanvas(screenPoint: Point2): Point2 {
102
102
  return this.inverseTransform.transformVec2(screenPoint);
103
103
  }
104
104
 
105
+ /** @returns the given point transformed into screen coordinates. */
105
106
  public canvasToScreen(canvasPoint: Point2): Point2 {
106
107
  return this.transform.transformVec2(canvasPoint);
107
108
  }
108
109
 
110
+ /** @returns a command that transforms the canvas by `transform`. */
109
111
  public static transformBy(transform: Mat33): ViewportTransform {
110
112
  return new Viewport.ViewportTransform(transform);
111
113
  }
112
114
 
113
- // Updates the transformation directly. Using `transformBy` is preferred.
114
- // [newTransform] should map from canvas coordinates to screen coordinates.
115
+ /**
116
+ * Updates the transformation directly. Using `transformBy` is preferred.
117
+ * @param newTransform - should map from canvas coordinates to screen coordinates.
118
+ */
115
119
  public resetTransform(newTransform: Mat33 = Mat33.identity) {
116
120
  const oldTransform = this.transform;
117
121
  this.transform = newTransform;
@@ -131,29 +135,64 @@ export class Viewport {
131
135
  return this.transform;
132
136
  }
133
137
 
134
- public getResolution(): Vec2 {
138
+ /** @returns the size of the visible region in pixels. */
139
+ public getScreenRectSize(): Vec2 {
135
140
  return this.screenRect.size;
136
141
  }
137
142
 
138
- // Returns the amount a vector on the canvas is scaled to become a vector on the screen.
143
+ /** Alias for `getScreenRectSize`. @deprecated */
144
+ public getResolution() {
145
+ return this.getScreenRectSize();
146
+ }
147
+
148
+ /** @returns the amount a vector on the canvas is scaled to become a vector on the screen. */
139
149
  public getScaleFactor(): number {
140
150
  // Use transformVec3 to avoid translating the vector
141
151
  return this.transform.transformVec3(Vec3.unitX).magnitude();
142
152
  }
143
153
 
144
- // Returns the size of one screen pixel in canvas units.
154
+ /**
155
+ * @returns `getScaleFactor()` rounded to the nearest power of 10.
156
+ * For example, if `getScaleFactor()` returns 101, `getScaleFactorToNearestPowerOfTen()`
157
+ * should return `100` because `100` is the nearest power of 10 to 101.
158
+ */
159
+ public getScaleFactorToNearestPowerOfTen() {
160
+ const scaleFactor = this.getScaleFactor();
161
+ return Math.pow(10, Math.round(Math.log10(scaleFactor)));
162
+ }
163
+
164
+ public snapToGrid(canvasPos: Point2) {
165
+ const snapCoordinate = (coordinate: number) => {
166
+ const scaleFactor = this.getScaleFactorToNearestPowerOfTen();
167
+ const roundFactor = scaleFactor / 100;
168
+ const snapped = Math.round(coordinate * roundFactor) / roundFactor;
169
+
170
+ return snapped;
171
+ };
172
+
173
+ const snappedCanvasPos = Vec2.of(
174
+ snapCoordinate(canvasPos.x), snapCoordinate(canvasPos.y)
175
+ );
176
+ return snappedCanvasPos;
177
+ }
178
+
179
+ /** Returns the size of one screen pixel in canvas units. */
145
180
  public getSizeOfPixelOnCanvas(): number {
146
181
  return 1/this.getScaleFactor();
147
182
  }
148
183
 
149
- // Returns the angle of the canvas in radians.
150
- // This is the angle by which the canvas is rotated relative to the screen.
184
+ /**
185
+ * @returns the angle of the canvas in radians.
186
+ * This is the angle by which the canvas is rotated relative to the screen.
187
+ */
151
188
  public getRotationAngle(): number {
152
189
  return this.transform.transformVec3(Vec3.unitX).angle();
153
190
  }
154
191
 
155
- // Rounds the given `point` to a multiple of 10 such that it is within `tolerance` of
156
- // its original location. This is useful for preparing data for base-10 conversion.
192
+ /**
193
+ * Rounds the given `point` to a multiple of 10 such that it is within `tolerance` of
194
+ * its original location. This is useful for preparing data for base-10 conversion.
195
+ */
157
196
  public static roundPoint<T extends Point2|number>(
158
197
  point: T, tolerance: number,
159
198
  ): PointDataType<T>;
@@ -3,7 +3,7 @@ import { EditorLocalization } from '../localization';
3
3
  import Command from './Command';
4
4
  import SerializableCommand from './SerializableCommand';
5
5
 
6
- // Returns a command taht does the opposite of the given command --- `result.apply()` calls
6
+ // Returns a command that does the opposite of the given command --- `result.apply()` calls
7
7
  // `command.unapply()` and `result.unapply()` calls `command.apply()`.
8
8
  const invertCommand = <T extends Command> (command: T): T extends SerializableCommand ? SerializableCommand : Command => {
9
9
  if (command instanceof SerializableCommand) {
@@ -13,9 +13,20 @@ export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
13
13
  export type DeserializeCallback = (data: string)=>AbstractComponent;
14
14
  type ComponentId = string;
15
15
 
16
+ /**
17
+ * A base class for everything that can be added to an {@link EditorImage}.
18
+ */
16
19
  export default abstract class AbstractComponent {
20
+ // The timestamp (milliseconds) at which the component was
21
+ // last changed (i.e. created/translated).
22
+ // @deprecated
17
23
  protected lastChangedTime: number;
24
+
25
+ // The bounding box of this component.
26
+ // {@link getBBox}, by default, returns `contentBBox`.
27
+ // This must be set by components.
18
28
  protected abstract contentBBox: Rect2;
29
+
19
30
  private zIndex: number;
20
31
  private id: string;
21
32
 
@@ -38,7 +49,7 @@ export default abstract class AbstractComponent {
38
49
  }
39
50
 
40
51
  // Returns a unique ID for this element.
41
- // @see { @link EditorImage!default.lookupElement }
52
+ // @see { @link lib!EditorImage.lookupElement }
42
53
  public getId() {
43
54
  return this.id;
44
55
  }
@@ -55,14 +66,22 @@ export default abstract class AbstractComponent {
55
66
  this.deserializationCallbacks[componentKind] = deserialize ?? null;
56
67
  }
57
68
 
58
- // Get and manage data attached by a loader.
69
+ // Stores data attached by a loader.
59
70
  private loadSaveData: LoadSaveDataTable = {};
71
+
72
+ /**
73
+ * Attach data that can be used while exporting the component (e.g. to SVG).
74
+ *
75
+ * This is intended for use by a {@link ImageLoader}.
76
+ */
60
77
  public attachLoadSaveData(key: string, data: LoadSaveData) {
61
78
  if (!this.loadSaveData[key]) {
62
79
  this.loadSaveData[key] = [];
63
80
  }
64
81
  this.loadSaveData[key].push(data);
65
82
  }
83
+
84
+ /** See {@link attachLoadSaveData} */
66
85
  public getLoadSaveData(): LoadSaveDataTable {
67
86
  return this.loadSaveData;
68
87
  }
@@ -71,13 +90,39 @@ export default abstract class AbstractComponent {
71
90
  return this.zIndex;
72
91
  }
73
92
 
93
+ /** @returns the bounding box of */
74
94
  public getBBox(): Rect2 {
75
95
  return this.contentBBox;
76
96
  }
77
97
 
78
98
  public abstract render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
99
+
100
+ /** @return true if `lineSegment` intersects this component. */
79
101
  public abstract intersects(lineSegment: LineSegment2): boolean;
80
102
 
103
+ /**
104
+ * @returns true if this component intersects `rect` -- it is entirely contained
105
+ * within the rectangle or one of the rectangle's edges intersects this component.
106
+ */
107
+ public intersectsRect(rect: Rect2): boolean {
108
+ // If this component intersects rect,
109
+ // it is either contained entirely within rect or intersects one of rect's edges.
110
+
111
+ // If contained within,
112
+ if (rect.containsRect(this.getBBox())) {
113
+ return true;
114
+ }
115
+
116
+ // Calculated bounding boxes can be slightly larger than their actual contents' bounding box.
117
+ // As such, test with more lines than just the rect's edges.
118
+ const testLines = [];
119
+ for (const subregion of rect.divideIntoGrid(2, 2)) {
120
+ testLines.push(...subregion.getEdges());
121
+ }
122
+
123
+ return testLines.some(edge => this.intersects(edge));
124
+ }
125
+
81
126
  // Return null iff this object cannot be safely serialized/deserialized.
82
127
  protected abstract serializeToJSON(): any[]|Record<string, any>|number|string|null;
83
128
 
@@ -181,6 +226,7 @@ export default abstract class AbstractComponent {
181
226
  }
182
227
 
183
228
  this.component.applyTransformation(newTransfm);
229
+ this.component.lastChangedTime = (new Date()).getTime();
184
230
 
185
231
  // Add the element back to the document.
186
232
  if (hadParent) {
@@ -231,6 +277,10 @@ export default abstract class AbstractComponent {
231
277
  }
232
278
  };
233
279
 
280
+ /**
281
+ * @return a description that could be read by a screen reader
282
+ * (e.g. when adding/erasing the component)
283
+ */
234
284
  public abstract description(localizationTable: ImageComponentLocalization): string;
235
285
 
236
286
  // Component-specific implementation of {@link clone}.
package/src/lib.ts CHANGED
@@ -8,25 +8,28 @@
8
8
  * ```
9
9
  *
10
10
  * @see
11
- * {@link Editor!}
11
+ * {@link Editor}
12
12
  *
13
13
  * @packageDocumentation
14
14
  */
15
15
 
16
- import Editor from './Editor';
16
+ import Editor, { EditorSettings } from './Editor';
17
17
  export { default as EditorImage } from './EditorImage';
18
18
  export * from './types';
19
19
  export { default as getLocalizationTable } from './localizations/getLocalizationTable';
20
20
  export * from './localization';
21
21
 
22
22
  export { default as Color4 } from './Color4';
23
+ export { default as SVGLoader } from './SVGLoader';
24
+ export { default as Viewport } from './Viewport';
23
25
  export * from './math/lib';
24
26
  export * from './components/lib';
25
27
  export * from './commands/lib';
26
28
  export * from './tools/lib';
27
29
  export * from './toolbar/lib';
30
+ export * from './rendering/lib';
28
31
  export { default as Pointer, PointerDevice } from './Pointer';
29
32
  export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
30
33
 
31
- export { Editor };
34
+ export { Editor, EditorSettings };
32
35
  export default Editor;
package/src/math/Mat33.ts CHANGED
@@ -338,7 +338,7 @@ export default class Mat33 {
338
338
  return result.rightMul(Mat33.translation(center.times(-1)));
339
339
  }
340
340
 
341
- /** @see {@link !fromCSSMatrix} */
341
+ /** @see {@link fromCSSMatrix} */
342
342
  public toCSSMatrix(): string {
343
343
  return `matrix(${this.a1},${this.b1},${this.a2},${this.b2},${this.a3},${this.b3})`;
344
344
  }
@@ -1,18 +1,3 @@
1
- /**
2
- * Handles `HTMLCanvasElement`s (or other drawing surfaces if being used) used to display the editor's contents.
3
- *
4
- * @example
5
- * ```
6
- * const editor = new Editor(document.body);
7
- * const w = editor.display.width;
8
- * const h = editor.display.height;
9
- * const center = Vec2.of(w / 2, h / 2);
10
- * const colorAtCenter = editor.display.getColorAt(center);
11
- * ```
12
- *
13
- * @packageDocumentation
14
- */
15
-
16
1
  import AbstractRenderer from './renderers/AbstractRenderer';
17
2
  import CanvasRenderer from './renderers/CanvasRenderer';
18
3
  import { Editor } from '../Editor';
@@ -29,6 +14,18 @@ export enum RenderingMode {
29
14
  // SVGRenderer is not supported by the main display
30
15
  }
31
16
 
17
+ /**
18
+ * Handles `HTMLCanvasElement`s (or other drawing surfaces if being used) used to display the editor's contents.
19
+ *
20
+ * @example
21
+ * ```
22
+ * const editor = new Editor(document.body);
23
+ * const w = editor.display.width;
24
+ * const h = editor.display.height;
25
+ * const center = Vec2.of(w / 2, h / 2);
26
+ * const colorAtCenter = editor.display.getColorAt(center);
27
+ * ```
28
+ */
32
29
  export default class Display {
33
30
  private dryInkRenderer: AbstractRenderer;
34
31
  private wetInkRenderer: AbstractRenderer;
@@ -13,7 +13,7 @@ export default RenderingStyle;
13
13
  export const stylesEqual = (a: RenderingStyle, b: RenderingStyle): boolean => {
14
14
  const result = a === b || (a.fill.eq(b.fill)
15
15
  && (a.stroke == undefined) === (b.stroke == undefined)
16
- && (a.stroke?.color?.eq(b.stroke?.color) ?? true)
16
+ && (a.stroke?.color?.eq(b.stroke?.color) ?? true)
17
17
  && a.stroke?.width === b.stroke?.width);
18
18
 
19
19
  // Map undefined/null -> false
@@ -0,0 +1,4 @@
1
+
2
+ export { default as AbstractRenderer } from './renderers/AbstractRenderer';
3
+ export { default as DummyRenderer } from './renderers/DummyRenderer';
4
+ export { default as Display } from './Display';
@@ -1,5 +1,3 @@
1
- // Renderer that outputs nothing. Useful for automated tests.
2
-
3
1
  import { TextStyle } from '../../components/TextComponent';
4
2
  import Mat33 from '../../math/Mat33';
5
3
  import Rect2 from '../../math/Rect2';
@@ -9,6 +7,7 @@ import Viewport from '../../Viewport';
9
7
  import RenderingStyle from '../RenderingStyle';
10
8
  import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
11
9
 
10
+ // Renderer that outputs almost nothing. Useful for automated tests.
12
11
  export default class DummyRenderer extends AbstractRenderer {
13
12
  // Variables that track the state of what's been rendered
14
13
  public clearedCount: number = 0;
@@ -28,7 +27,7 @@ export default class DummyRenderer extends AbstractRenderer {
28
27
 
29
28
  public displaySize(): Vec2 {
30
29
  // Do we have a stored viewport size?
31
- const viewportSize = this.getViewport().getResolution();
30
+ const viewportSize = this.getViewport().getScreenRectSize();
32
31
 
33
32
  // Don't use a 0x0 viewport — DummyRenderer is often used
34
33
  // for tests that run without a display, so pretend we have a
@@ -38,6 +38,10 @@ export default class SVGRenderer extends AbstractRenderer {
38
38
  stroke-linecap: round;
39
39
  stroke-linejoin: round;
40
40
  }
41
+
42
+ text {
43
+ white-space: pre;
44
+ }
41
45
  `.replace(/\s+/g, '');
42
46
  styleSheet.setAttribute('id', renderedStylesheetId);
43
47
  this.elem.appendChild(styleSheet);
@@ -9,7 +9,6 @@ import RenderingStyle from '../RenderingStyle';
9
9
  import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
10
10
 
11
11
  // Outputs a description of what was rendered.
12
-
13
12
  export default class TextOnlyRenderer extends AbstractRenderer {
14
13
  private descriptionBuilder: string[] = [];
15
14
  private pathCount: number = 0;
@@ -16,12 +16,25 @@ import SelectionToolWidget from './widgets/SelectionToolWidget';
16
16
  import TextToolWidget from './widgets/TextToolWidget';
17
17
  import HandToolWidget from './widgets/HandToolWidget';
18
18
  import BaseWidget from './widgets/BaseWidget';
19
- import { ActionButtonWidget, InsertImageWidget } from './lib';
19
+ import ActionButtonWidget from './widgets/ActionButtonWidget';
20
+ import InsertImageWidget from './widgets/InsertImageWidget';
20
21
 
21
22
  export const toolbarCSSPrefix = 'toolbar-';
22
23
 
23
24
  type UpdateColorisCallback = ()=>void;
24
25
 
26
+ interface SpacerOptions {
27
+ // Defaults to 0. If a non-zero number, determines the rate at which the
28
+ // spacer should grow (like flexGrow).
29
+ grow: number;
30
+
31
+ // Minimum size (e.g. "23px")
32
+ minSize: string;
33
+
34
+ // Maximum size (e.g. "50px")
35
+ maxSize: string;
36
+ }
37
+
25
38
  export default class HTMLToolbar {
26
39
  private container: HTMLElement;
27
40
 
@@ -121,8 +134,17 @@ export default class HTMLToolbar {
121
134
  });
122
135
  }
123
136
 
124
- // Adds an `ActionButtonWidget` or `BaseToolWidget`. The widget should not have already have a parent
125
- // (i.e. its `addTo` method should not have been called).
137
+ /**
138
+ * Adds an `ActionButtonWidget` or `BaseToolWidget`. The widget should not have already have a parent
139
+ * (i.e. its `addTo` method should not have been called).
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const toolbar = editor.addToolbar();
144
+ * const insertImageWidget = new InsertImageWidget(editor);
145
+ * toolbar.addWidget(insertImageWidget);
146
+ * ```
147
+ */
126
148
  public addWidget(widget: BaseWidget) {
127
149
  // Prevent name collisions
128
150
  const id = widget.getUniqueIdIn(this.widgets);
@@ -135,6 +157,46 @@ export default class HTMLToolbar {
135
157
  this.setupColorPickers();
136
158
  }
137
159
 
160
+ /**
161
+ * Adds a spacer.
162
+ *
163
+ * @example
164
+ * Adding a save button that moves to the very right edge of the toolbar
165
+ * while keeping the other buttons centered:
166
+ * ```ts
167
+ * const toolbar = editor.addToolbar(false);
168
+ *
169
+ * toolbar.addSpacer({ grow: 1 });
170
+ * toolbar.addDefaults();
171
+ * toolbar.addSpacer({ grow: 1 });
172
+ *
173
+ * toolbar.addActionButton({
174
+ * label: 'Save',
175
+ * icon: editor.icons.makeSaveIcon(),
176
+ * }, () => {
177
+ * saveCallback();
178
+ * });
179
+ * ```
180
+ */
181
+ public addSpacer(options: Partial<SpacerOptions> = {}) {
182
+ const spacer = document.createElement('div');
183
+ spacer.classList.add(`${toolbarCSSPrefix}spacer`);
184
+
185
+ if (options.grow) {
186
+ spacer.style.flexGrow = `${options.grow}`;
187
+ }
188
+
189
+ if (options.minSize) {
190
+ spacer.style.minWidth = options.minSize;
191
+ }
192
+
193
+ if (options.maxSize) {
194
+ spacer.style.maxWidth = options.maxSize;
195
+ }
196
+
197
+ this.container.appendChild(spacer);
198
+ }
199
+
138
200
  public serializeState(): string {
139
201
  const result: Record<string, any> = {};
140
202
 
@@ -145,8 +207,10 @@ export default class HTMLToolbar {
145
207
  return JSON.stringify(result);
146
208
  }
147
209
 
148
- // Deserialize toolbar widgets from the given state.
149
- // Assumes that toolbar widgets are in the same order as when state was serialized.
210
+ /**
211
+ * Deserialize toolbar widgets from the given state.
212
+ * Assumes that toolbar widgets are in the same order as when state was serialized.
213
+ */
150
214
  public deserializeState(state: string) {
151
215
  const data = JSON.parse(state);
152
216
 
@@ -247,4 +311,16 @@ export default class HTMLToolbar {
247
311
  public addDefaultActionButtons() {
248
312
  this.addUndoRedoButtons();
249
313
  }
314
+
315
+ /**
316
+ * Adds both the default tool widgets and action buttons. Equivalent to
317
+ * ```ts
318
+ * toolbar.addDefaultToolWidgets();
319
+ * toolbar.addDefaultActionButtons();
320
+ * ```
321
+ */
322
+ public addDefaults() {
323
+ this.addDefaultToolWidgets();
324
+ this.addDefaultActionButtons();
325
+ }
250
326
  }