js-draw 0.16.1 → 0.17.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.
@@ -503,11 +503,12 @@ export class Editor {
503
503
  * ```
504
504
  */
505
505
  dispatchNoAnnounce(command, addToHistory = false) {
506
+ const result = command.apply(this);
506
507
  if (addToHistory) {
507
508
  const apply = false; // Don't double-apply
508
509
  this.history.push(command, apply);
509
510
  }
510
- return command.apply(this);
511
+ return result;
511
512
  }
512
513
  /**
513
514
  * Apply a large transformation in chunks.
@@ -172,9 +172,13 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
172
172
  super('add-element');
173
173
  this.element = element;
174
174
  this.applyByFlattening = applyByFlattening;
175
- // Store the element's serialization --- .serializeToJSON may be called on this
176
- // even when this is not at the top of the undo/redo stack.
177
- this.serializedElem = element.serialize();
175
+ this.serializedElem = null;
176
+ // FIXME: The serialized version of this command may be inaccurate if this is
177
+ // serialized when this command is not on the top of the undo stack.
178
+ //
179
+ // Caching the element's serialized data leads to additional memory usage *and*
180
+ // sometimes incorrect behavior in collaborative editing.
181
+ this.serializedElem = null;
178
182
  if (isNaN(element.getBBox().area)) {
179
183
  throw new Error('Elements in the image cannot have NaN bounding boxes');
180
184
  }
@@ -197,8 +201,9 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
197
201
  return localization.addElementAction(this.element.description(localization));
198
202
  }
199
203
  serializeToJSON() {
204
+ var _a;
200
205
  return {
201
- elemData: this.serializedElem,
206
+ elemData: (_a = this.serializedElem) !== null && _a !== void 0 ? _a : this.element.serialize(),
202
207
  };
203
208
  }
204
209
  },
@@ -207,7 +212,9 @@ EditorImage.AddElementCommand = (_a = class extends SerializableCommand {
207
212
  const id = json.elemData.id;
208
213
  const foundElem = editor.image.lookupElement(id);
209
214
  const elem = foundElem !== null && foundElem !== void 0 ? foundElem : AbstractComponent.deserialize(json.elemData);
210
- return new EditorImage.AddElementCommand(elem);
215
+ const result = new EditorImage.AddElementCommand(elem);
216
+ result.serializedElem = json.elemData;
217
+ return result;
211
218
  });
212
219
  })(),
213
220
  _a);
@@ -3,12 +3,11 @@ import Command from './commands/Command';
3
3
  type AnnounceRedoCallback = (command: Command) => void;
4
4
  type AnnounceUndoCallback = (command: Command) => void;
5
5
  declare class UndoRedoHistory {
6
+ #private;
6
7
  private readonly editor;
7
8
  private announceRedoCallback;
8
9
  private announceUndoCallback;
9
- private undoStack;
10
- private redoStack;
11
- private maxUndoRedoStackSize;
10
+ private readonly maxUndoRedoStackSize;
12
11
  constructor(editor: Editor, announceRedoCallback: AnnounceRedoCallback, announceUndoCallback: AnnounceUndoCallback);
13
12
  private fireUpdateEvent;
14
13
  push(command: Command, apply?: boolean): void;
@@ -1,19 +1,35 @@
1
- import { EditorEventType } from './types';
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 _UndoRedoHistory_undoStack, _UndoRedoHistory_redoStack;
13
+ import { EditorEventType, UndoEventType } from './types';
2
14
  class UndoRedoHistory {
3
15
  // @internal
4
16
  constructor(editor, announceRedoCallback, announceUndoCallback) {
5
17
  this.editor = editor;
6
18
  this.announceRedoCallback = announceRedoCallback;
7
19
  this.announceUndoCallback = announceUndoCallback;
20
+ _UndoRedoHistory_undoStack.set(this, void 0);
21
+ _UndoRedoHistory_redoStack.set(this, void 0);
8
22
  this.maxUndoRedoStackSize = 700;
9
- this.undoStack = [];
10
- this.redoStack = [];
23
+ __classPrivateFieldSet(this, _UndoRedoHistory_undoStack, [], "f");
24
+ __classPrivateFieldSet(this, _UndoRedoHistory_redoStack, [], "f");
11
25
  }
12
- fireUpdateEvent() {
26
+ fireUpdateEvent(stackUpdateType, triggeringCommand) {
13
27
  this.editor.notifier.dispatch(EditorEventType.UndoRedoStackUpdated, {
14
28
  kind: EditorEventType.UndoRedoStackUpdated,
15
- undoStackSize: this.undoStack.length,
16
- redoStackSize: this.redoStack.length,
29
+ undoStackSize: __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length,
30
+ redoStackSize: __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").length,
31
+ command: triggeringCommand,
32
+ stackUpdateType,
17
33
  });
18
34
  }
19
35
  // Adds the given command to this and applies it to the editor.
@@ -21,17 +37,17 @@ class UndoRedoHistory {
21
37
  if (apply) {
22
38
  command.apply(this.editor);
23
39
  }
24
- this.undoStack.push(command);
25
- for (const elem of this.redoStack) {
40
+ __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").push(command);
41
+ for (const elem of __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f")) {
26
42
  elem.onDrop(this.editor);
27
43
  }
28
- this.redoStack = [];
29
- if (this.undoStack.length > this.maxUndoRedoStackSize) {
44
+ __classPrivateFieldSet(this, _UndoRedoHistory_redoStack, [], "f");
45
+ if (__classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length > this.maxUndoRedoStackSize) {
30
46
  const removeAtOnceCount = 10;
31
- const removedElements = this.undoStack.splice(0, removeAtOnceCount);
47
+ const removedElements = __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").splice(0, removeAtOnceCount);
32
48
  removedElements.forEach(elem => elem.onDrop(this.editor));
33
49
  }
34
- this.fireUpdateEvent();
50
+ this.fireUpdateEvent(UndoEventType.CommandDone, command);
35
51
  this.editor.notifier.dispatch(EditorEventType.CommandDone, {
36
52
  kind: EditorEventType.CommandDone,
37
53
  command,
@@ -39,12 +55,12 @@ class UndoRedoHistory {
39
55
  }
40
56
  // Remove the last command from this' undo stack and apply it.
41
57
  undo() {
42
- const command = this.undoStack.pop();
58
+ const command = __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").pop();
43
59
  if (command) {
44
- this.redoStack.push(command);
60
+ __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").push(command);
45
61
  command.unapply(this.editor);
46
62
  this.announceUndoCallback(command);
47
- this.fireUpdateEvent();
63
+ this.fireUpdateEvent(UndoEventType.CommandUndone, command);
48
64
  this.editor.notifier.dispatch(EditorEventType.CommandUndone, {
49
65
  kind: EditorEventType.CommandUndone,
50
66
  command,
@@ -52,12 +68,12 @@ class UndoRedoHistory {
52
68
  }
53
69
  }
54
70
  redo() {
55
- const command = this.redoStack.pop();
71
+ const command = __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").pop();
56
72
  if (command) {
57
- this.undoStack.push(command);
73
+ __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").push(command);
58
74
  command.apply(this.editor);
59
75
  this.announceRedoCallback(command);
60
- this.fireUpdateEvent();
76
+ this.fireUpdateEvent(UndoEventType.CommandRedone, command);
61
77
  this.editor.notifier.dispatch(EditorEventType.CommandDone, {
62
78
  kind: EditorEventType.CommandDone,
63
79
  command,
@@ -65,10 +81,11 @@ class UndoRedoHistory {
65
81
  }
66
82
  }
67
83
  get undoStackSize() {
68
- return this.undoStack.length;
84
+ return __classPrivateFieldGet(this, _UndoRedoHistory_undoStack, "f").length;
69
85
  }
70
86
  get redoStackSize() {
71
- return this.redoStack.length;
87
+ return __classPrivateFieldGet(this, _UndoRedoHistory_redoStack, "f").length;
72
88
  }
73
89
  }
90
+ _UndoRedoHistory_undoStack = new WeakMap(), _UndoRedoHistory_redoStack = new WeakMap();
74
91
  export default UndoRedoHistory;
@@ -56,8 +56,8 @@ class DefaultRestyleComponentCommand extends UnresolvedSerializableCommand {
56
56
  unapply(editor) {
57
57
  this.getComponent(editor).forceStyle(this.originalStyle, editor);
58
58
  }
59
- description(_editor, localizationTable) {
60
- return localizationTable.restyledElements;
59
+ description(editor, localizationTable) {
60
+ return localizationTable.restyledElement(this.getComponent(editor).description(localizationTable));
61
61
  }
62
62
  serializeToJSON() {
63
63
  return {
@@ -6,6 +6,6 @@ export interface ImageComponentLocalization {
6
6
  svgObject: string;
7
7
  emptyBackground: string;
8
8
  filledBackgroundWithColor: (color: string) => string;
9
- restyledElements: string;
9
+ restyledElement: (elementDescription: string) => string;
10
10
  }
11
11
  export declare const defaultComponentLocalization: ImageComponentLocalization;
@@ -2,9 +2,9 @@ export const defaultComponentLocalization = {
2
2
  unlabeledImageNode: 'Unlabeled image node',
3
3
  stroke: 'Stroke',
4
4
  svgObject: 'SVG Object',
5
- restyledElements: 'Restyled elements',
6
5
  emptyBackground: 'Empty background',
7
6
  filledBackgroundWithColor: (color) => `Filled background (${color})`,
8
7
  text: (text) => `Text object: ${text}`,
9
8
  imageNode: (description) => `Image: ${description}`,
9
+ restyledElement: (elementDescription) => `Restyled ${elementDescription}`,
10
10
  };
package/dist/src/lib.d.ts CHANGED
@@ -28,5 +28,6 @@ export * from './toolbar/lib';
28
28
  export * from './rendering/lib';
29
29
  export { default as Pointer, PointerDevice } from './Pointer';
30
30
  export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
31
+ export { default as UndoRedoHistory } from './UndoRedoHistory';
31
32
  export { Editor, EditorSettings };
32
33
  export default Editor;
package/dist/src/lib.js CHANGED
@@ -28,5 +28,6 @@ export * from './toolbar/lib';
28
28
  export * from './rendering/lib';
29
29
  export { default as Pointer, PointerDevice } from './Pointer';
30
30
  export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
31
+ export { default as UndoRedoHistory } from './UndoRedoHistory';
31
32
  export { Editor };
32
33
  export default Editor;
@@ -4,6 +4,7 @@ export default class OverflowWidget extends BaseWidget {
4
4
  var _a;
5
5
  super(editor, 'overflow-widget', localizationTable);
6
6
  this.overflowChildren = [];
7
+ this.container.classList.add('toolbar-overflow-widget');
7
8
  // Make the dropdown openable
8
9
  this.container.classList.add('dropdownShowable');
9
10
  (_a = this.overflowContainer) !== null && _a !== void 0 ? _a : (this.overflowContainer = document.createElement('div'));
@@ -32,6 +33,7 @@ export default class OverflowWidget extends BaseWidget {
32
33
  */
33
34
  clearChildren() {
34
35
  this.overflowContainer.replaceChildren();
36
+ this.container.classList.remove('horizontal');
35
37
  const overflowChildren = this.overflowChildren;
36
38
  this.overflowChildren = [];
37
39
  return overflowChildren;
@@ -56,6 +58,10 @@ export default class OverflowWidget extends BaseWidget {
56
58
  this.overflowChildren.push(widget);
57
59
  widget.addTo(this.overflowContainer);
58
60
  widget.setIsToplevel(false);
61
+ // Switch to a horizontal layout if enough children
62
+ if (this.overflowChildren.length > 2) {
63
+ this.container.classList.add('horizontal');
64
+ }
59
65
  }
60
66
  // This always returns false.
61
67
  // Don't try to move the overflow menu to itself.
@@ -85,6 +85,11 @@ export declare enum EditorEventType {
85
85
  ColorPickerColorSelected = 10,
86
86
  ToolbarDropdownShown = 11
87
87
  }
88
+ export declare enum UndoEventType {
89
+ CommandDone = 0,
90
+ CommandUndone = 1,
91
+ CommandRedone = 2
92
+ }
88
93
  type EditorToolEventType = EditorEventType.ToolEnabled | EditorEventType.ToolDisabled | EditorEventType.ToolUpdated;
89
94
  export interface EditorToolEvent {
90
95
  readonly kind: EditorToolEventType;
@@ -107,6 +112,8 @@ export interface EditorUndoStackUpdated {
107
112
  readonly kind: EditorEventType.UndoRedoStackUpdated;
108
113
  readonly undoStackSize: number;
109
114
  readonly redoStackSize: number;
115
+ readonly command?: Command;
116
+ readonly stackUpdateType: UndoEventType;
110
117
  }
111
118
  export interface CommandDoneEvent {
112
119
  readonly kind: EditorEventType.CommandDone;
package/dist/src/types.js CHANGED
@@ -26,3 +26,10 @@ export var EditorEventType;
26
26
  EditorEventType[EditorEventType["ColorPickerColorSelected"] = 10] = "ColorPickerColorSelected";
27
27
  EditorEventType[EditorEventType["ToolbarDropdownShown"] = 11] = "ToolbarDropdownShown";
28
28
  })(EditorEventType || (EditorEventType = {}));
29
+ // Types of `EditorUndoStackUpdated` events.
30
+ export var UndoEventType;
31
+ (function (UndoEventType) {
32
+ UndoEventType[UndoEventType["CommandDone"] = 0] = "CommandDone";
33
+ UndoEventType[UndoEventType["CommandUndone"] = 1] = "CommandUndone";
34
+ UndoEventType[UndoEventType["CommandRedone"] = 2] = "CommandRedone";
35
+ })(UndoEventType || (UndoEventType = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "main": "./dist/src/lib.d.ts",
6
6
  "types": "./dist/src/lib.js",
package/src/Editor.ts CHANGED
@@ -663,12 +663,14 @@ export class Editor {
663
663
  * ```
664
664
  */
665
665
  public dispatchNoAnnounce(command: Command, addToHistory: boolean = false) {
666
+ const result = command.apply(this);
667
+
666
668
  if (addToHistory) {
667
669
  const apply = false; // Don't double-apply
668
670
  this.history.push(command, apply);
669
671
  }
670
672
 
671
- return command.apply(this);
673
+ return result;
672
674
  }
673
675
 
674
676
  /**
@@ -211,7 +211,7 @@ export default class EditorImage {
211
211
 
212
212
  // A Command that can access private [EditorImage] functionality
213
213
  private static AddElementCommand = class extends SerializableCommand {
214
- private serializedElem: any;
214
+ private serializedElem: any|null = null;
215
215
 
216
216
  // If [applyByFlattening], then the rendered content of this element
217
217
  // is present on the display's wet ink canvas. As such, no re-render is necessary
@@ -222,9 +222,12 @@ export default class EditorImage {
222
222
  ) {
223
223
  super('add-element');
224
224
 
225
- // Store the element's serialization --- .serializeToJSON may be called on this
226
- // even when this is not at the top of the undo/redo stack.
227
- this.serializedElem = element.serialize();
225
+ // FIXME: The serialized version of this command may be inaccurate if this is
226
+ // serialized when this command is not on the top of the undo stack.
227
+ //
228
+ // Caching the element's serialized data leads to additional memory usage *and*
229
+ // sometimes incorrect behavior in collaborative editing.
230
+ this.serializedElem = null;
228
231
 
229
232
  if (isNaN(element.getBBox().area)) {
230
233
  throw new Error('Elements in the image cannot have NaN bounding boxes');
@@ -253,7 +256,7 @@ export default class EditorImage {
253
256
 
254
257
  protected serializeToJSON() {
255
258
  return {
256
- elemData: this.serializedElem,
259
+ elemData: this.serializedElem ?? this.element.serialize(),
257
260
  };
258
261
  }
259
262
 
@@ -262,7 +265,9 @@ export default class EditorImage {
262
265
  const id = json.elemData.id;
263
266
  const foundElem = editor.image.lookupElement(id);
264
267
  const elem = foundElem ?? AbstractComponent.deserialize(json.elemData);
265
- return new EditorImage.AddElementCommand(elem);
268
+ const result = new EditorImage.AddElementCommand(elem);
269
+ result.serializedElem = json.elemData;
270
+ return result;
266
271
  });
267
272
  }
268
273
  };
@@ -1,15 +1,15 @@
1
1
  import Editor from './Editor';
2
2
  import Command from './commands/Command';
3
- import { EditorEventType } from './types';
3
+ import { EditorEventType, UndoEventType } from './types';
4
4
 
5
5
  type AnnounceRedoCallback = (command: Command)=>void;
6
6
  type AnnounceUndoCallback = (command: Command)=>void;
7
7
 
8
8
  class UndoRedoHistory {
9
- private undoStack: Command[];
10
- private redoStack: Command[];
9
+ #undoStack: Command[];
10
+ #redoStack: Command[];
11
11
 
12
- private maxUndoRedoStackSize: number = 700;
12
+ private readonly maxUndoRedoStackSize: number = 700;
13
13
 
14
14
  // @internal
15
15
  public constructor(
@@ -17,15 +17,20 @@ class UndoRedoHistory {
17
17
  private announceRedoCallback: AnnounceRedoCallback,
18
18
  private announceUndoCallback: AnnounceUndoCallback,
19
19
  ) {
20
- this.undoStack = [];
21
- this.redoStack = [];
20
+ this.#undoStack = [];
21
+ this.#redoStack = [];
22
22
  }
23
23
 
24
- private fireUpdateEvent() {
24
+ private fireUpdateEvent(
25
+ stackUpdateType: UndoEventType, triggeringCommand: Command
26
+ ) {
25
27
  this.editor.notifier.dispatch(EditorEventType.UndoRedoStackUpdated, {
26
28
  kind: EditorEventType.UndoRedoStackUpdated,
27
- undoStackSize: this.undoStack.length,
28
- redoStackSize: this.redoStack.length,
29
+ undoStackSize: this.#undoStack.length,
30
+ redoStackSize: this.#redoStack.length,
31
+
32
+ command: triggeringCommand,
33
+ stackUpdateType,
29
34
  });
30
35
  }
31
36
 
@@ -34,20 +39,20 @@ class UndoRedoHistory {
34
39
  if (apply) {
35
40
  command.apply(this.editor);
36
41
  }
37
- this.undoStack.push(command);
42
+ this.#undoStack.push(command);
38
43
 
39
- for (const elem of this.redoStack) {
44
+ for (const elem of this.#redoStack) {
40
45
  elem.onDrop(this.editor);
41
46
  }
42
- this.redoStack = [];
47
+ this.#redoStack = [];
43
48
 
44
- if (this.undoStack.length > this.maxUndoRedoStackSize) {
49
+ if (this.#undoStack.length > this.maxUndoRedoStackSize) {
45
50
  const removeAtOnceCount = 10;
46
- const removedElements = this.undoStack.splice(0, removeAtOnceCount);
51
+ const removedElements = this.#undoStack.splice(0, removeAtOnceCount);
47
52
  removedElements.forEach(elem => elem.onDrop(this.editor));
48
53
  }
49
54
 
50
- this.fireUpdateEvent();
55
+ this.fireUpdateEvent(UndoEventType.CommandDone, command);
51
56
  this.editor.notifier.dispatch(EditorEventType.CommandDone, {
52
57
  kind: EditorEventType.CommandDone,
53
58
  command,
@@ -56,13 +61,13 @@ class UndoRedoHistory {
56
61
 
57
62
  // Remove the last command from this' undo stack and apply it.
58
63
  public undo() {
59
- const command = this.undoStack.pop();
64
+ const command = this.#undoStack.pop();
60
65
  if (command) {
61
- this.redoStack.push(command);
66
+ this.#redoStack.push(command);
62
67
  command.unapply(this.editor);
63
68
  this.announceUndoCallback(command);
64
69
 
65
- this.fireUpdateEvent();
70
+ this.fireUpdateEvent(UndoEventType.CommandUndone, command);
66
71
  this.editor.notifier.dispatch(EditorEventType.CommandUndone, {
67
72
  kind: EditorEventType.CommandUndone,
68
73
  command,
@@ -71,13 +76,13 @@ class UndoRedoHistory {
71
76
  }
72
77
 
73
78
  public redo() {
74
- const command = this.redoStack.pop();
79
+ const command = this.#redoStack.pop();
75
80
  if (command) {
76
- this.undoStack.push(command);
81
+ this.#undoStack.push(command);
77
82
  command.apply(this.editor);
78
83
  this.announceRedoCallback(command);
79
84
 
80
- this.fireUpdateEvent();
85
+ this.fireUpdateEvent(UndoEventType.CommandRedone, command);
81
86
  this.editor.notifier.dispatch(EditorEventType.CommandDone, {
82
87
  kind: EditorEventType.CommandDone,
83
88
  command,
@@ -86,11 +91,11 @@ class UndoRedoHistory {
86
91
  }
87
92
 
88
93
  public get undoStackSize(): number {
89
- return this.undoStack.length;
94
+ return this.#undoStack.length;
90
95
  }
91
96
 
92
97
  public get redoStackSize(): number {
93
- return this.redoStack.length;
98
+ return this.#redoStack.length;
94
99
  }
95
100
  }
96
101
 
@@ -111,8 +111,8 @@ class DefaultRestyleComponentCommand extends UnresolvedSerializableCommand {
111
111
  this.getComponent(editor).forceStyle(this.originalStyle, editor);
112
112
  }
113
113
 
114
- public description(_editor: Editor, localizationTable: EditorLocalization): string {
115
- return localizationTable.restyledElements;
114
+ public description(editor: Editor, localizationTable: EditorLocalization): string {
115
+ return localizationTable.restyledElement(this.getComponent(editor).description(localizationTable));
116
116
  }
117
117
 
118
118
  protected serializeToJSON() {
@@ -7,16 +7,16 @@ export interface ImageComponentLocalization {
7
7
  emptyBackground: string;
8
8
  filledBackgroundWithColor: (color: string)=> string;
9
9
 
10
- restyledElements: string;
10
+ restyledElement: (elementDescription: string) => string;
11
11
  }
12
12
 
13
13
  export const defaultComponentLocalization: ImageComponentLocalization = {
14
14
  unlabeledImageNode: 'Unlabeled image node',
15
15
  stroke: 'Stroke',
16
16
  svgObject: 'SVG Object',
17
- restyledElements: 'Restyled elements',
18
17
  emptyBackground: 'Empty background',
19
18
  filledBackgroundWithColor: (color) => `Filled background (${color})`,
20
19
  text: (text) => `Text object: ${text}`,
21
20
  imageNode: (description: string) => `Image: ${description}`,
21
+ restyledElement: (elementDescription: string) => `Restyled ${elementDescription}`,
22
22
  };
package/src/lib.ts CHANGED
@@ -30,6 +30,7 @@ export * from './toolbar/lib';
30
30
  export * from './rendering/lib';
31
31
  export { default as Pointer, PointerDevice } from './Pointer';
32
32
  export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
33
+ export { default as UndoRedoHistory } from './UndoRedoHistory';
33
34
 
34
35
  export { Editor, EditorSettings };
35
36
  export default Editor;
@@ -18,6 +18,8 @@
18
18
  flex-direction: row;
19
19
  justify-content: center;
20
20
 
21
+ --toolbar-button-height: min(20vh, 60px);
22
+
21
23
  /* Display above selection dialogs, etc. */
22
24
  z-index: 2;
23
25
 
@@ -30,7 +32,7 @@
30
32
  .toolbar-root > .toolbar-button {
31
33
  width: min-content;
32
34
  white-space: pre;
33
- height: min(20vh, 60px);
35
+ height: var(--toolbar-button-height);
34
36
  }
35
37
 
36
38
  .toolbar-dropdown .toolbar-button > .toolbar-icon {
@@ -76,6 +78,10 @@
76
78
  font-size: 1em;
77
79
  }
78
80
 
81
+ .toolbar-button > label {
82
+ cursor: inherit;
83
+ }
84
+
79
85
  .toolbar-dropdown > .toolbar-toolContainer > button,
80
86
  .toolbar-dropdown > .toolbar-toolContainer > .toolbar-button {
81
87
  width: 6em;
@@ -3,7 +3,25 @@
3
3
  display: flex;
4
4
  flex-direction: column;
5
5
  flex-wrap: wrap;
6
+ justify-content: center;
7
+ }
6
8
 
7
- overflow-x: auto;
8
- max-height: 100%;
9
+ .toolbar-overflow-widget-overflow-list > .toolbar-toolContainer > .toolbar-button {
10
+ height: var(--toolbar-button-height);
11
+ }
12
+
13
+ .toolbar-overflow-widget.horizontal .toolbar-overflow-widget-overflow-list {
14
+ flex-direction: row;
15
+ }
16
+
17
+ .toolbar-overflow-widget.horizontal > .toolbar-dropdown {
18
+ max-width: 100%;
19
+ left: 15px;
20
+ right: 15px;
21
+
22
+ /* Override the default transform and margin-left */
23
+ margin-left: 0 !important;
24
+ transform: none !important;
25
+
26
+ padding: 4px;
9
27
  }
@@ -10,6 +10,9 @@ export default class OverflowWidget extends BaseWidget {
10
10
  public constructor(editor: Editor, localizationTable?: ToolbarLocalization) {
11
11
  super(editor, 'overflow-widget', localizationTable);
12
12
 
13
+
14
+ this.container.classList.add('toolbar-overflow-widget');
15
+
13
16
  // Make the dropdown openable
14
17
  this.container.classList.add('dropdownShowable');
15
18
  this.overflowContainer ??= document.createElement('div');
@@ -44,6 +47,7 @@ export default class OverflowWidget extends BaseWidget {
44
47
  */
45
48
  public clearChildren(): BaseWidget[] {
46
49
  this.overflowContainer.replaceChildren();
50
+ this.container.classList.remove('horizontal');
47
51
 
48
52
  const overflowChildren = this.overflowChildren;
49
53
  this.overflowChildren = [];
@@ -73,6 +77,11 @@ export default class OverflowWidget extends BaseWidget {
73
77
  this.overflowChildren.push(widget);
74
78
  widget.addTo(this.overflowContainer);
75
79
  widget.setIsToplevel(false);
80
+
81
+ // Switch to a horizontal layout if enough children
82
+ if (this.overflowChildren.length > 2) {
83
+ this.container.classList.add('horizontal');
84
+ }
76
85
  }
77
86
 
78
87
  // This always returns false.
package/src/types.ts CHANGED
@@ -111,7 +111,6 @@ export type InputEvt = KeyPressEvent | KeyUpEvent | WheelEvt | GestureCancelEvt
111
111
 
112
112
  export type EditorNotifier = EventDispatcher<EditorEventType, EditorEventDataType>;
113
113
 
114
-
115
114
  export enum EditorEventType {
116
115
  ToolEnabled,
117
116
  ToolDisabled,
@@ -130,6 +129,13 @@ export enum EditorEventType {
130
129
  ToolbarDropdownShown,
131
130
  }
132
131
 
132
+ // Types of `EditorUndoStackUpdated` events.
133
+ export enum UndoEventType {
134
+ CommandDone,
135
+ CommandUndone,
136
+ CommandRedone,
137
+ }
138
+
133
139
  type EditorToolEventType = EditorEventType.ToolEnabled
134
140
  | EditorEventType.ToolDisabled
135
141
  | EditorEventType.ToolUpdated;
@@ -159,8 +165,12 @@ export interface DisplayResizedEvent {
159
165
 
160
166
  export interface EditorUndoStackUpdated {
161
167
  readonly kind: EditorEventType.UndoRedoStackUpdated;
168
+
162
169
  readonly undoStackSize: number;
163
170
  readonly redoStackSize: number;
171
+
172
+ readonly command?: Command;
173
+ readonly stackUpdateType: UndoEventType;
164
174
  }
165
175
 
166
176
  export interface CommandDoneEvent {