js-draw 1.10.0 → 1.11.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 (52) hide show
  1. package/dist/bundle.js +2 -2
  2. package/dist/cjs/commands/invertCommand.js +5 -0
  3. package/dist/cjs/components/AbstractComponent.d.ts +8 -0
  4. package/dist/cjs/components/AbstractComponent.js +28 -8
  5. package/dist/cjs/components/builders/types.d.ts +11 -0
  6. package/dist/cjs/toolbar/AbstractToolbar.d.ts +18 -2
  7. package/dist/cjs/toolbar/AbstractToolbar.js +46 -30
  8. package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -1
  9. package/dist/cjs/toolbar/widgets/ExitActionWidget.d.ts +12 -0
  10. package/dist/cjs/toolbar/widgets/ExitActionWidget.js +32 -0
  11. package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +4 -3
  12. package/dist/cjs/toolbar/widgets/HandToolWidget.js +24 -13
  13. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +1 -1
  14. package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
  15. package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
  16. package/dist/cjs/toolbar/widgets/layout/types.d.ts +1 -1
  17. package/dist/cjs/tools/Pen.js +5 -0
  18. package/dist/cjs/tools/SelectionTool/Selection.d.ts +4 -0
  19. package/dist/cjs/tools/SelectionTool/Selection.js +56 -12
  20. package/dist/cjs/tools/SelectionTool/SelectionTool.d.ts +1 -0
  21. package/dist/cjs/tools/SelectionTool/SelectionTool.js +19 -1
  22. package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +0 -1
  23. package/dist/cjs/tools/ToolSwitcherShortcut.js +0 -1
  24. package/dist/cjs/tools/keybindings.d.ts +1 -0
  25. package/dist/cjs/tools/keybindings.js +3 -1
  26. package/dist/cjs/version.js +1 -1
  27. package/dist/mjs/commands/invertCommand.mjs +5 -0
  28. package/dist/mjs/components/AbstractComponent.d.ts +8 -0
  29. package/dist/mjs/components/AbstractComponent.mjs +28 -8
  30. package/dist/mjs/components/builders/types.d.ts +11 -0
  31. package/dist/mjs/toolbar/AbstractToolbar.d.ts +18 -2
  32. package/dist/mjs/toolbar/AbstractToolbar.mjs +46 -30
  33. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -1
  34. package/dist/mjs/toolbar/widgets/ExitActionWidget.d.ts +12 -0
  35. package/dist/mjs/toolbar/widgets/ExitActionWidget.mjs +27 -0
  36. package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +4 -3
  37. package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +24 -13
  38. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +1 -1
  39. package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
  40. package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
  41. package/dist/mjs/toolbar/widgets/layout/types.d.ts +1 -1
  42. package/dist/mjs/tools/Pen.mjs +5 -0
  43. package/dist/mjs/tools/SelectionTool/Selection.d.ts +4 -0
  44. package/dist/mjs/tools/SelectionTool/Selection.mjs +56 -12
  45. package/dist/mjs/tools/SelectionTool/SelectionTool.d.ts +1 -0
  46. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +20 -2
  47. package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +0 -1
  48. package/dist/mjs/tools/ToolSwitcherShortcut.mjs +0 -1
  49. package/dist/mjs/tools/keybindings.d.ts +1 -0
  50. package/dist/mjs/tools/keybindings.mjs +2 -0
  51. package/dist/mjs/version.mjs +1 -1
  52. package/package.json +2 -2
@@ -11,6 +11,11 @@ const invertCommand = (command) => {
11
11
  if (command instanceof SerializableCommand_1.default) {
12
12
  // SerializableCommand that does the inverse of [command]
13
13
  return new class extends SerializableCommand_1.default {
14
+ constructor() {
15
+ super(...arguments);
16
+ // For debugging
17
+ this._command = command;
18
+ }
14
19
  serializeToJSON() {
15
20
  return command.serialize();
16
21
  }
@@ -116,6 +116,14 @@ export default abstract class AbstractComponent {
116
116
  protected abstract applyTransformation(affineTransfm: Mat33): void;
117
117
  transformBy(affineTransfm: Mat33): SerializableCommand;
118
118
  setZIndex(newZIndex: number): SerializableCommand;
119
+ /**
120
+ * Combines {@link transformBy} and {@link setZIndex} into a single command.
121
+ *
122
+ * @param newZIndex - The z-index this component should have after applying this command.
123
+ * @param originalZIndex - @internal The z-index the component should revert to after unapplying
124
+ * this command.
125
+ */
126
+ setZIndexAndTransformBy(affineTransfm: Mat33, newZIndex: number, originalZIndex?: number): SerializableCommand;
119
127
  isSelectable(): boolean;
120
128
  isBackground(): boolean;
121
129
  getProportionalRenderingTime(): number;
@@ -144,13 +144,25 @@ class AbstractComponent {
144
144
  }
145
145
  // Returns a command that, when applied, transforms this by [affineTransfm] and
146
146
  // updates the editor.
147
- // This also increases the element's z-index so that it is on top.
147
+ //
148
+ // The transformed component is also moved to the top (use {@link setZIndexAndTransformBy} to
149
+ // avoid this behavior).
148
150
  transformBy(affineTransfm) {
149
151
  return new AbstractComponent.TransformElementCommand(affineTransfm, this.getId(), this);
150
152
  }
151
153
  // Returns a command that updates this component's z-index.
152
154
  setZIndex(newZIndex) {
153
- return new AbstractComponent.TransformElementCommand(math_1.Mat33.identity, this.getId(), this, newZIndex, this.getZIndex());
155
+ return new AbstractComponent.TransformElementCommand(math_1.Mat33.identity, this.getId(), this, newZIndex);
156
+ }
157
+ /**
158
+ * Combines {@link transformBy} and {@link setZIndex} into a single command.
159
+ *
160
+ * @param newZIndex - The z-index this component should have after applying this command.
161
+ * @param originalZIndex - @internal The z-index the component should revert to after unapplying
162
+ * this command.
163
+ */
164
+ setZIndexAndTransformBy(affineTransfm, newZIndex, originalZIndex) {
165
+ return new AbstractComponent.TransformElementCommand(affineTransfm, this.getId(), this, newZIndex, originalZIndex);
154
166
  }
155
167
  // @returns true iff this component can be selected (e.g. by the selection tool.)
156
168
  isSelectable() {
@@ -221,8 +233,12 @@ class AbstractComponent {
221
233
  throw new Error(`Element with data ${json} cannot be deserialized.`);
222
234
  }
223
235
  const instance = this.deserializationCallbacks[json.name](json.data);
224
- instance.zIndex = json.zIndex;
225
236
  instance.id = json.id;
237
+ if (isFinite(json.zIndex)) {
238
+ instance.zIndex = json.zIndex;
239
+ // Ensure that new components will be added on top.
240
+ AbstractComponent.zIndexCounter = Math.max(AbstractComponent.zIndexCounter, instance.zIndex + 1);
241
+ }
226
242
  // TODO: What should we do with json.loadSaveData?
227
243
  // If we attach it to [instance], we create a potential security risk — loadSaveData
228
244
  // is often used to store unrecognised attributes so they can be preserved on output.
@@ -231,6 +247,7 @@ class AbstractComponent {
231
247
  }
232
248
  }
233
249
  // Topmost z-index
250
+ // TODO: Should be a property of the EditorImage.
234
251
  AbstractComponent.zIndexCounter = 0;
235
252
  AbstractComponent.deserializationCallbacks = {};
236
253
  AbstractComponent.transformElementCommandId = 'transform-element';
@@ -258,7 +275,7 @@ AbstractComponent.TransformElementCommand = (_a = class extends UnresolvedComman
258
275
  super.resolveComponent(image);
259
276
  this.origZIndex ??= this.component.getZIndex();
260
277
  }
261
- updateTransform(editor, newTransfm) {
278
+ updateTransform(editor, newTransfm, targetZIndex) {
262
279
  if (!this.component) {
263
280
  throw new Error('this.component is undefined or null!');
264
281
  }
@@ -270,7 +287,12 @@ AbstractComponent.TransformElementCommand = (_a = class extends UnresolvedComman
270
287
  hadParent = true;
271
288
  }
272
289
  this.component.applyTransformation(newTransfm);
290
+ this.component.zIndex = targetZIndex;
273
291
  this.component.lastChangedTime = (new Date()).getTime();
292
+ // Ensure that new components are automatically drawn above the current component.
293
+ if (targetZIndex >= AbstractComponent.zIndexCounter) {
294
+ AbstractComponent.zIndexCounter = targetZIndex + 1;
295
+ }
274
296
  // Add the element back to the document.
275
297
  if (hadParent) {
276
298
  EditorImage_1.default.addElement(this.component).apply(editor);
@@ -278,14 +300,12 @@ AbstractComponent.TransformElementCommand = (_a = class extends UnresolvedComman
278
300
  }
279
301
  apply(editor) {
280
302
  this.resolveComponent(editor.image);
281
- this.component.zIndex = this.targetZIndex;
282
- this.updateTransform(editor, this.affineTransfm);
303
+ this.updateTransform(editor, this.affineTransfm, this.targetZIndex);
283
304
  editor.queueRerender();
284
305
  }
285
306
  unapply(editor) {
286
307
  this.resolveComponent(editor.image);
287
- this.component.zIndex = this.origZIndex;
288
- this.updateTransform(editor, this.affineTransfm.inverse());
308
+ this.updateTransform(editor, this.affineTransfm.inverse(), this.origZIndex);
289
309
  editor.queueRerender();
290
310
  }
291
311
  description(_editor, localizationTable) {
@@ -7,6 +7,17 @@ export interface ComponentBuilder {
7
7
  getBBox(): Rect2;
8
8
  build(): AbstractComponent;
9
9
  preview(renderer: AbstractRenderer): void;
10
+ /**
11
+ * Called when the pen is stationary (or the user otherwise
12
+ * activates autocomplete). This might attempt to fit the user's
13
+ * drawing to a particular shape.
14
+ *
15
+ * The shape returned by this function may be ignored if it has
16
+ * an empty bounding box.
17
+ *
18
+ * Although this returns a Promise, it should return *as fast as
19
+ * possible*.
20
+ */
10
21
  autocorrectShape?: () => Promise<AbstractComponent | null>;
11
22
  addPoint(point: StrokeDataPoint): void;
12
23
  }
@@ -3,6 +3,7 @@ import { ToolbarLocalization } from './localization';
3
3
  import { ActionButtonIcon } from './types';
4
4
  import BaseWidget, { ToolbarWidgetTag } from './widgets/BaseWidget';
5
5
  import { DispatcherEventListener } from '../EventDispatcher';
6
+ import { BaseTool } from '../lib';
6
7
  export interface SpacerOptions {
7
8
  grow: number;
8
9
  minSize: string;
@@ -131,7 +132,7 @@ export default abstract class AbstractToolbar {
131
132
  /**
132
133
  * Adds an "Exit" button that, when clicked, calls `exitCallback`.
133
134
  *
134
- * **Note**: This is roughly equivalent to
135
+ * **Note**: This is *roughly* equivalent to
135
136
  * ```ts
136
137
  * toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
137
138
  * label: this.editor.localization.exit,
@@ -154,9 +155,24 @@ export default abstract class AbstractToolbar {
154
155
  */
155
156
  addUndoRedoButtons(undoFirst?: boolean): void;
156
157
  /**
157
- * Adds toolbar widgets based on the enabled tools.
158
+ * Adds widgets for pen/eraser/selection/text/pan-zoom primary tools.
159
+ *
160
+ * If `filter` returns `false` for a tool, no widget is added for that tool.
161
+ * See {@link addDefaultToolWidgets}
162
+ */
163
+ addWidgetsForPrimaryTools(filter?: (tool: BaseTool) => boolean): void;
164
+ /**
165
+ * Adds toolbar widgets based on the enabled tools, and additional tool-like
166
+ * buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}).
158
167
  */
159
168
  addDefaultToolWidgets(): void;
169
+ /**
170
+ * Adds widgets that don't correspond to tools, but do allow the user to control
171
+ * the editor in some way.
172
+ *
173
+ * By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}.
174
+ */
175
+ addDefaultEditorControlWidgets(): void;
160
176
  addDefaultActionButtons(): void;
161
177
  /**
162
178
  * Adds both the default tool widgets and action buttons.
@@ -35,6 +35,7 @@ const DocumentPropertiesWidget_1 = __importDefault(require("./widgets/DocumentPr
35
35
  const math_1 = require("@js-draw/math");
36
36
  const constants_1 = require("./constants");
37
37
  const SaveActionWidget_1 = __importDefault(require("./widgets/SaveActionWidget"));
38
+ const ExitActionWidget_1 = __importDefault(require("./widgets/ExitActionWidget"));
38
39
  class AbstractToolbar {
39
40
  /** @internal */
40
41
  constructor(editor, localizationTable = localization_1.defaultToolbarLocalization) {
@@ -297,14 +298,13 @@ class AbstractToolbar {
297
298
  */
298
299
  addSaveButton(saveCallback, labelOverride = {}) {
299
300
  const widget = new SaveActionWidget_1.default(this.editor, this.localizationTable, saveCallback, labelOverride);
300
- widget.setTags([BaseWidget_1.ToolbarWidgetTag.Save]);
301
301
  this.addWidget(widget);
302
302
  return widget;
303
303
  }
304
304
  /**
305
305
  * Adds an "Exit" button that, when clicked, calls `exitCallback`.
306
306
  *
307
- * **Note**: This is roughly equivalent to
307
+ * **Note**: This is *roughly* equivalent to
308
308
  * ```ts
309
309
  * toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
310
310
  * label: this.editor.localization.exit,
@@ -321,15 +321,9 @@ class AbstractToolbar {
321
321
  * @final
322
322
  */
323
323
  addExitButton(exitCallback, labelOverride = {}) {
324
- return this.addTaggedActionButton([BaseWidget_1.ToolbarWidgetTag.Exit], {
325
- label: this.editor.localization.exit,
326
- icon: this.editor.icons.makeCloseIcon(),
327
- ...labelOverride,
328
- }, () => {
329
- exitCallback();
330
- }, {
331
- autoDisableInReadOnlyEditors: false,
332
- });
324
+ const widget = new ExitActionWidget_1.default(this.editor, this.localizationTable, exitCallback, labelOverride);
325
+ this.addWidget(widget);
326
+ return widget;
333
327
  }
334
328
  /**
335
329
  * Adds undo and redo buttons that trigger the editor's built-in undo and redo
@@ -377,27 +371,49 @@ class AbstractToolbar {
377
371
  });
378
372
  }
379
373
  /**
380
- * Adds toolbar widgets based on the enabled tools.
374
+ * Adds widgets for pen/eraser/selection/text/pan-zoom primary tools.
375
+ *
376
+ * If `filter` returns `false` for a tool, no widget is added for that tool.
377
+ * See {@link addDefaultToolWidgets}
381
378
  */
382
- addDefaultToolWidgets() {
383
- const toolController = this.editor.toolController;
384
- for (const tool of toolController.getMatchingTools(Pen_1.default)) {
385
- const widget = new PenToolWidget_1.default(this.editor, tool, this.localizationTable);
386
- this.addWidget(widget);
387
- }
388
- for (const tool of toolController.getMatchingTools(Eraser_1.default)) {
389
- this.addWidget(new EraserToolWidget_1.default(this.editor, tool, this.localizationTable));
390
- }
391
- for (const tool of toolController.getMatchingTools(SelectionTool_1.default)) {
392
- this.addWidget(new SelectionToolWidget_1.default(this.editor, tool, this.localizationTable));
393
- }
394
- for (const tool of toolController.getMatchingTools(TextTool_1.default)) {
395
- this.addWidget(new TextToolWidget_1.default(this.editor, tool, this.localizationTable));
396
- }
397
- const panZoomTool = toolController.getMatchingTools(PanZoom_1.default)[0];
398
- if (panZoomTool) {
399
- this.addWidget(new HandToolWidget_1.default(this.editor, panZoomTool, this.localizationTable));
379
+ addWidgetsForPrimaryTools(filter) {
380
+ for (const tool of this.editor.toolController.getPrimaryTools()) {
381
+ if (filter && !filter?.(tool)) {
382
+ continue;
383
+ }
384
+ if (tool instanceof Pen_1.default) {
385
+ const widget = new PenToolWidget_1.default(this.editor, tool, this.localizationTable);
386
+ this.addWidget(widget);
387
+ }
388
+ else if (tool instanceof Eraser_1.default) {
389
+ this.addWidget(new EraserToolWidget_1.default(this.editor, tool, this.localizationTable));
390
+ }
391
+ else if (tool instanceof SelectionTool_1.default) {
392
+ this.addWidget(new SelectionToolWidget_1.default(this.editor, tool, this.localizationTable));
393
+ }
394
+ else if (tool instanceof TextTool_1.default) {
395
+ this.addWidget(new TextToolWidget_1.default(this.editor, tool, this.localizationTable));
396
+ }
397
+ else if (tool instanceof PanZoom_1.default) {
398
+ this.addWidget(new HandToolWidget_1.default(this.editor, tool, this.localizationTable));
399
+ }
400
400
  }
401
+ }
402
+ /**
403
+ * Adds toolbar widgets based on the enabled tools, and additional tool-like
404
+ * buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}).
405
+ */
406
+ addDefaultToolWidgets() {
407
+ this.addWidgetsForPrimaryTools();
408
+ this.addDefaultEditorControlWidgets();
409
+ }
410
+ /**
411
+ * Adds widgets that don't correspond to tools, but do allow the user to control
412
+ * the editor in some way.
413
+ *
414
+ * By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}.
415
+ */
416
+ addDefaultEditorControlWidgets() {
401
417
  this.addWidget(new DocumentPropertiesWidget_1.default(this.editor, this.localizationTable));
402
418
  this.addWidget(new InsertImageWidget_1.default(this.editor, this.localizationTable));
403
419
  }
@@ -53,7 +53,7 @@ class BaseWidget {
53
53
  this.layoutManager = defaultLayoutManager;
54
54
  this.icon = null;
55
55
  this.container = document.createElement('div');
56
- this.container.classList.add(`${constants_1.toolbarCSSPrefix}toolContainer`, `${constants_1.toolbarCSSPrefix}toolButtonContainer`);
56
+ this.container.classList.add(`${constants_1.toolbarCSSPrefix}toolContainer`, `${constants_1.toolbarCSSPrefix}toolButtonContainer`, `${constants_1.toolbarCSSPrefix}internalWidgetId--${id.replace(/[^a-zA-Z0-9_]/g, '-')}`);
57
57
  this.dropdownContent = document.createElement('div');
58
58
  __classPrivateFieldSet(this, _BaseWidget_hasDropdown, false, "f");
59
59
  this.button = document.createElement('div');
@@ -0,0 +1,12 @@
1
+ import { KeyPressEvent } from '../../inputEvents';
2
+ import Editor from '../../Editor';
3
+ import { ToolbarLocalization } from '../localization';
4
+ import ActionButtonWidget from './ActionButtonWidget';
5
+ import { ActionButtonIcon } from '../types';
6
+ declare class ExitActionWidget extends ActionButtonWidget {
7
+ constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>);
8
+ protected shouldAutoDisableInReadOnlyEditor(): boolean;
9
+ protected onKeyPress(event: KeyPressEvent): boolean;
10
+ mustBeInToplevelMenu(): boolean;
11
+ }
12
+ export default ExitActionWidget;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ActionButtonWidget_1 = __importDefault(require("./ActionButtonWidget"));
7
+ const BaseWidget_1 = require("./BaseWidget");
8
+ const keybindings_1 = require("./keybindings");
9
+ class ExitActionWidget extends ActionButtonWidget_1.default {
10
+ constructor(editor, localization, saveCallback, labelOverride = {}) {
11
+ super(editor, 'exit-button',
12
+ // Creates an icon
13
+ () => {
14
+ return labelOverride.icon ?? editor.icons.makeCloseIcon();
15
+ }, labelOverride.label ?? localization.exit, saveCallback);
16
+ this.setTags([BaseWidget_1.ToolbarWidgetTag.Exit]);
17
+ }
18
+ shouldAutoDisableInReadOnlyEditor() {
19
+ return false;
20
+ }
21
+ onKeyPress(event) {
22
+ if (this.editor.shortcuts.matchesShortcut(keybindings_1.exitKeyboardShortcut, event)) {
23
+ this.clickAction();
24
+ return true;
25
+ }
26
+ return super.onKeyPress(event);
27
+ }
28
+ mustBeInToplevelMenu() {
29
+ return true;
30
+ }
31
+ }
32
+ exports.default = ExitActionWidget;
@@ -4,11 +4,12 @@ import { ToolbarLocalization } from '../localization';
4
4
  import BaseToolWidget from './BaseToolWidget';
5
5
  import { SavedToolbuttonState } from './BaseWidget';
6
6
  export default class HandToolWidget extends BaseToolWidget {
7
- protected overridePanZoomTool: PanZoom;
8
7
  private allowTogglingBaseTool;
9
- constructor(editor: Editor, overridePanZoomTool: PanZoom, localizationTable: ToolbarLocalization);
10
- protected shouldAutoDisableInReadOnlyEditor(): boolean;
8
+ protected overridePanZoomTool: PanZoom;
9
+ constructor(editor: Editor, tool: PanZoom, localizationTable: ToolbarLocalization);
11
10
  private static getPrimaryHandTool;
11
+ private static getOverrideHandTool;
12
+ protected shouldAutoDisableInReadOnlyEditor(): boolean;
12
13
  protected getTitle(): string;
13
14
  protected createIcon(): Element;
14
15
  protected handleClick(): void;
@@ -124,34 +124,45 @@ class HandModeWidget extends BaseWidget_1.default {
124
124
  }
125
125
  class HandToolWidget extends BaseToolWidget_1.default {
126
126
  constructor(editor,
127
- // Pan zoom tool that overrides all other tools (enabling this tool for a device
128
- // causes that device to pan/zoom instead of interact with the primary tools)
129
- overridePanZoomTool, localizationTable) {
130
- const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController);
131
- const tool = primaryHandTool ?? overridePanZoomTool;
132
- super(editor, tool, 'hand-tool-widget', localizationTable);
133
- this.overridePanZoomTool = overridePanZoomTool;
127
+ // Can either be the primary pan/zoom tool (in the primary tools list) or
128
+ // the override pan/zoom tool.
129
+ // If the override pan/zoom tool, the primary will be gotten from the editor's
130
+ // tool controller.
131
+ // If the primary, the override will be gotten from the editor's tool controller.
132
+ tool, localizationTable) {
133
+ const isGivenToolPrimary = editor.toolController.getPrimaryTools().includes(tool);
134
+ const primaryTool = (isGivenToolPrimary ? tool : HandToolWidget.getPrimaryHandTool(editor.toolController))
135
+ ?? tool;
136
+ super(editor, primaryTool, 'hand-tool-widget', localizationTable);
137
+ this.overridePanZoomTool =
138
+ (isGivenToolPrimary ? HandToolWidget.getOverrideHandTool(editor.toolController) : tool)
139
+ ?? tool;
134
140
  // Only allow toggling a hand tool if we're using the primary hand tool and not the override
135
141
  // hand tool for this button.
136
- this.allowTogglingBaseTool = primaryHandTool !== null;
142
+ this.allowTogglingBaseTool = primaryTool !== null;
137
143
  // Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
138
144
  if (!this.allowTogglingBaseTool) {
139
145
  this.container.classList.add('dropdownShowable');
140
146
  }
141
147
  // Controls for the overriding hand tool.
142
- const touchPanningWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoom_1.PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
143
- const rotationLockWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoom_1.PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
148
+ const touchPanningWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoom_1.PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
149
+ const rotationLockWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoom_1.PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
144
150
  this.addSubWidget(touchPanningWidget);
145
151
  this.addSubWidget(rotationLockWidget);
146
152
  }
147
- shouldAutoDisableInReadOnlyEditor() {
148
- return false;
149
- }
150
153
  static getPrimaryHandTool(toolController) {
151
154
  const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom_1.default);
152
155
  const primaryPanZoomTool = primaryPanZoomToolList[0];
153
156
  return primaryPanZoomTool;
154
157
  }
158
+ static getOverrideHandTool(toolController) {
159
+ const panZoomToolList = toolController.getMatchingTools(PanZoom_1.default);
160
+ const panZoomTool = panZoomToolList[0];
161
+ return panZoomTool;
162
+ }
163
+ shouldAutoDisableInReadOnlyEditor() {
164
+ return false;
165
+ }
155
166
  getTitle() {
156
167
  return this.localizationTable.handTool;
157
168
  }
@@ -174,7 +174,7 @@ class InsertImageWidget extends BaseWidget_1.default {
174
174
  this.image?.reset();
175
175
  };
176
176
  this.statusView.replaceChildren(sizeText);
177
- const largeImageThreshold = 0.25; // MiB
177
+ const largeImageThreshold = 0.12; // MiB
178
178
  if (sizeInMiB > largeImageThreshold) {
179
179
  this.statusView.appendChild(decreaseSizeButton);
180
180
  }
@@ -1,3 +1,4 @@
1
1
  export declare const resizeImageToSelectionKeyboardShortcut = "jsdraw.toolbar.SelectionTool.resizeImageToSelection";
2
2
  export declare const selectStrokeTypeKeyboardShortcutIds: string[];
3
3
  export declare const saveKeyboardShortcut = "jsdraw.toolbar.SaveActionWidget.save";
4
+ export declare const exitKeyboardShortcut = "jsdraw.toolbar.ExitActionWidget.exit";
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.saveKeyboardShortcut = exports.selectStrokeTypeKeyboardShortcutIds = exports.resizeImageToSelectionKeyboardShortcut = void 0;
6
+ exports.exitKeyboardShortcut = exports.saveKeyboardShortcut = exports.selectStrokeTypeKeyboardShortcutIds = exports.resizeImageToSelectionKeyboardShortcut = void 0;
7
7
  const KeyboardShortcutManager_1 = __importDefault(require("../../shortcuts/KeyboardShortcutManager"));
8
8
  // Selection
9
9
  exports.resizeImageToSelectionKeyboardShortcut = 'jsdraw.toolbar.SelectionTool.resizeImageToSelection';
@@ -17,3 +17,6 @@ for (let i = 0; i < exports.selectStrokeTypeKeyboardShortcutIds.length; i++) {
17
17
  // Save
18
18
  exports.saveKeyboardShortcut = 'jsdraw.toolbar.SaveActionWidget.save';
19
19
  KeyboardShortcutManager_1.default.registerDefaultKeyboardShortcut(exports.saveKeyboardShortcut, ['ctrlOrMeta+KeyS'], 'Save');
20
+ // Exit
21
+ exports.exitKeyboardShortcut = 'jsdraw.toolbar.ExitActionWidget.exit';
22
+ KeyboardShortcutManager_1.default.registerDefaultKeyboardShortcut(exports.exitKeyboardShortcut, ['Alt+KeyQ'], 'Exit');
@@ -1,4 +1,4 @@
1
- import ReactiveValue from 'js-draw/src/util/ReactiveValue';
1
+ import ReactiveValue from '../../../util/ReactiveValue';
2
2
  /**
3
3
  * A class that manages whether/what content is shown for a widget.
4
4
  *
@@ -192,6 +192,11 @@ class Pen extends BaseTool_1.default {
192
192
  if (!this.builder || !correctedShape) {
193
193
  return;
194
194
  }
195
+ // Don't complete to empty shapes.
196
+ const bboxArea = correctedShape.getBBox().area;
197
+ if (bboxArea === 0 || !isFinite(bboxArea)) {
198
+ return;
199
+ }
195
200
  this.autocorrectedShape = correctedShape;
196
201
  this.lastAutocorrectedShape = correctedShape;
197
202
  this.previewStroke();
@@ -2,6 +2,7 @@
2
2
  * @internal
3
3
  * @packageDocumentation
4
4
  */
5
+ import SerializableCommand from '../../commands/SerializableCommand';
5
6
  import Editor from '../../Editor';
6
7
  import { Mat33, Rect2, Point2 } from '@js-draw/math';
7
8
  import Pointer from '../../Pointer';
@@ -36,7 +37,10 @@ export default class Selection {
36
37
  getScreenRegion(): Rect2;
37
38
  get screenRegionRotation(): number;
38
39
  setTransform(transform: Mat33, preview?: boolean): void;
40
+ private getDeltaZIndexToMoveSelectionToTop;
39
41
  finalizeTransform(): void | Promise<void>;
42
+ /** Sends all selected elements to the bottom of the visible image. */
43
+ sendToBack(): SerializableCommand | null;
40
44
  private static ApplyTransformationCommand;
41
45
  private previewTransformCmds;
42
46
  resolveToObjects(): boolean;