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
@@ -96,34 +96,45 @@ class HandModeWidget extends BaseWidget {
96
96
  }
97
97
  export default class HandToolWidget extends BaseToolWidget {
98
98
  constructor(editor,
99
- // Pan zoom tool that overrides all other tools (enabling this tool for a device
100
- // causes that device to pan/zoom instead of interact with the primary tools)
101
- overridePanZoomTool, localizationTable) {
102
- const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController);
103
- const tool = primaryHandTool ?? overridePanZoomTool;
104
- super(editor, tool, 'hand-tool-widget', localizationTable);
105
- this.overridePanZoomTool = overridePanZoomTool;
99
+ // Can either be the primary pan/zoom tool (in the primary tools list) or
100
+ // the override pan/zoom tool.
101
+ // If the override pan/zoom tool, the primary will be gotten from the editor's
102
+ // tool controller.
103
+ // If the primary, the override will be gotten from the editor's tool controller.
104
+ tool, localizationTable) {
105
+ const isGivenToolPrimary = editor.toolController.getPrimaryTools().includes(tool);
106
+ const primaryTool = (isGivenToolPrimary ? tool : HandToolWidget.getPrimaryHandTool(editor.toolController))
107
+ ?? tool;
108
+ super(editor, primaryTool, 'hand-tool-widget', localizationTable);
109
+ this.overridePanZoomTool =
110
+ (isGivenToolPrimary ? HandToolWidget.getOverrideHandTool(editor.toolController) : tool)
111
+ ?? tool;
106
112
  // Only allow toggling a hand tool if we're using the primary hand tool and not the override
107
113
  // hand tool for this button.
108
- this.allowTogglingBaseTool = primaryHandTool !== null;
114
+ this.allowTogglingBaseTool = primaryTool !== null;
109
115
  // Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
110
116
  if (!this.allowTogglingBaseTool) {
111
117
  this.container.classList.add('dropdownShowable');
112
118
  }
113
119
  // Controls for the overriding hand tool.
114
- const touchPanningWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
115
- const rotationLockWidget = new HandModeWidget(editor, overridePanZoomTool, PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
120
+ const touchPanningWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
121
+ const rotationLockWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
116
122
  this.addSubWidget(touchPanningWidget);
117
123
  this.addSubWidget(rotationLockWidget);
118
124
  }
119
- shouldAutoDisableInReadOnlyEditor() {
120
- return false;
121
- }
122
125
  static getPrimaryHandTool(toolController) {
123
126
  const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom);
124
127
  const primaryPanZoomTool = primaryPanZoomToolList[0];
125
128
  return primaryPanZoomTool;
126
129
  }
130
+ static getOverrideHandTool(toolController) {
131
+ const panZoomToolList = toolController.getMatchingTools(PanZoom);
132
+ const panZoomTool = panZoomToolList[0];
133
+ return panZoomTool;
134
+ }
135
+ shouldAutoDisableInReadOnlyEditor() {
136
+ return false;
137
+ }
127
138
  getTitle() {
128
139
  return this.localizationTable.handTool;
129
140
  }
@@ -169,7 +169,7 @@ class InsertImageWidget extends BaseWidget {
169
169
  this.image?.reset();
170
170
  };
171
171
  this.statusView.replaceChildren(sizeText);
172
- const largeImageThreshold = 0.25; // MiB
172
+ const largeImageThreshold = 0.12; // MiB
173
173
  if (sizeInMiB > largeImageThreshold) {
174
174
  this.statusView.appendChild(decreaseSizeButton);
175
175
  }
@@ -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";
@@ -11,3 +11,6 @@ for (let i = 0; i < selectStrokeTypeKeyboardShortcutIds.length; i++) {
11
11
  // Save
12
12
  export const saveKeyboardShortcut = 'jsdraw.toolbar.SaveActionWidget.save';
13
13
  KeyboardShortcutManager.registerDefaultKeyboardShortcut(saveKeyboardShortcut, ['ctrlOrMeta+KeyS'], 'Save');
14
+ // Exit
15
+ export const exitKeyboardShortcut = 'jsdraw.toolbar.ExitActionWidget.exit';
16
+ KeyboardShortcutManager.registerDefaultKeyboardShortcut(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
  *
@@ -187,6 +187,11 @@ export default class Pen extends BaseTool {
187
187
  if (!this.builder || !correctedShape) {
188
188
  return;
189
189
  }
190
+ // Don't complete to empty shapes.
191
+ const bboxArea = correctedShape.getBBox().area;
192
+ if (bboxArea === 0 || !isFinite(bboxArea)) {
193
+ return;
194
+ }
190
195
  this.autocorrectedShape = correctedShape;
191
196
  this.lastAutocorrectedShape = correctedShape;
192
197
  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;
@@ -13,6 +13,7 @@ import Duplicate from '../../commands/Duplicate.mjs';
13
13
  import { DragTransformer, ResizeTransformer, RotateTransformer } from './TransformMode.mjs';
14
14
  import { ResizeMode } from './types.mjs';
15
15
  import EditorImage from '../../image/EditorImage.mjs';
16
+ import uniteCommands from '../../commands/uniteCommands.mjs';
16
17
  const updateChunkSize = 100;
17
18
  const maxPreviewElemCount = 500;
18
19
  // @internal
@@ -23,6 +24,7 @@ class Selection {
23
24
  // @see getTightBoundingBox
24
25
  this.selectionTightBoundingBox = null;
25
26
  this.transform = Mat33.identity;
27
+ // invariant: sorted by increasing z-index
26
28
  this.selectedElems = [];
27
29
  this.hasParent = true;
28
30
  // Maps IDs to whether we removed the component from the image
@@ -133,6 +135,16 @@ class Selection {
133
135
  this.previewTransformCmds();
134
136
  }
135
137
  }
138
+ getDeltaZIndexToMoveSelectionToTop() {
139
+ if (this.selectedElems.length === 0) {
140
+ return 0;
141
+ }
142
+ const selectedBottommostZIndex = this.selectedElems[0].getZIndex();
143
+ const visibleObjects = this.editor.image.getElementsIntersectingRegion(this.region);
144
+ const topMostVisibleZIndex = visibleObjects[visibleObjects.length - 1]?.getZIndex() ?? selectedBottommostZIndex;
145
+ const deltaZIndex = (topMostVisibleZIndex + 1) - selectedBottommostZIndex;
146
+ return deltaZIndex;
147
+ }
136
148
  // Applies the current transformation to the selection
137
149
  finalizeTransform() {
138
150
  const fullTransform = this.transform;
@@ -141,17 +153,35 @@ class Selection {
141
153
  this.originalRegion = this.originalRegion.transformedBoundingBox(this.transform);
142
154
  this.transform = Mat33.identity;
143
155
  this.scrollTo();
156
+ let transformPromise = undefined;
144
157
  // Make the commands undo-able.
145
158
  // Don't check for non-empty transforms because this breaks changing the
146
159
  // z-index of the just-transformed commands.
147
- //
148
- // TODO: Check whether the selectedElems are already all toplevel.
149
- const transformPromise = this.editor.dispatch(new _a.ApplyTransformationCommand(this, selectedElems, fullTransform));
160
+ if (this.selectedElems.length > 0) {
161
+ const deltaZIndex = this.getDeltaZIndexToMoveSelectionToTop();
162
+ transformPromise = this.editor.dispatch(new _a.ApplyTransformationCommand(this, selectedElems, fullTransform, deltaZIndex));
163
+ }
150
164
  // Clear renderings of any in-progress transformations
151
165
  const wetInkRenderer = this.editor.display.getWetInkRenderer();
152
166
  wetInkRenderer.clear();
153
167
  return transformPromise;
154
168
  }
169
+ /** Sends all selected elements to the bottom of the visible image. */
170
+ sendToBack() {
171
+ const visibleObjects = this.editor.image.getElementsIntersectingRegion(this.editor.viewport.visibleRect);
172
+ // VisibleObjects and selectedElems should both be sorted by z-index
173
+ const lowestVisibleZIndex = visibleObjects[0]?.getZIndex() ?? 0;
174
+ const highestSelectedZIndex = this.selectedElems[this.selectedElems.length - 1]?.getZIndex() ?? 0;
175
+ const targetHighestZIndex = lowestVisibleZIndex - 1;
176
+ const deltaZIndex = targetHighestZIndex - highestSelectedZIndex;
177
+ if (deltaZIndex !== 0) {
178
+ const commands = this.selectedElems.map(elem => {
179
+ return elem.setZIndex(elem.getZIndex() + deltaZIndex);
180
+ });
181
+ return uniteCommands(commands, updateChunkSize);
182
+ }
183
+ return null;
184
+ }
155
185
  // Preview the effects of the current transformation on the selection
156
186
  previewTransformCmds() {
157
187
  if (this.selectedElems.length === 0) {
@@ -165,7 +195,7 @@ class Selection {
165
195
  const wetInkRenderer = this.editor.display.getWetInkRenderer();
166
196
  wetInkRenderer.clear();
167
197
  wetInkRenderer.pushTransform(this.transform);
168
- const viewportVisibleRect = this.editor.viewport.visibleRect;
198
+ const viewportVisibleRect = this.editor.viewport.visibleRect.union(this.region);
169
199
  const visibleRect = viewportVisibleRect.transformedBoundingBox(this.transform.inverse());
170
200
  for (const elem of this.selectedElems) {
171
201
  elem.render(wetInkRenderer, visibleRect);
@@ -411,7 +441,8 @@ class Selection {
411
441
  if (wasTransforming) {
412
442
  // Don't update the selection's focus when redoing/undoing
413
443
  const selectionToUpdate = null;
414
- tmpApplyCommand = new _a.ApplyTransformationCommand(selectionToUpdate, this.selectedElems, this.transform);
444
+ const deltaZIndex = this.getDeltaZIndexToMoveSelectionToTop();
445
+ tmpApplyCommand = new _a.ApplyTransformationCommand(selectionToUpdate, this.selectedElems, this.transform, deltaZIndex);
415
446
  // Transform to ensure that the duplicates are in the correct location
416
447
  await tmpApplyCommand.apply(this.editor);
417
448
  // Show items again
@@ -452,6 +483,8 @@ class Selection {
452
483
  this.originalRegion = bbox;
453
484
  this.selectionTightBoundingBox = bbox;
454
485
  this.selectedElems = objects.filter(object => object.isSelectable());
486
+ // Enforce increasing z-index invariant
487
+ this.selectedElems.sort((a, b) => a.getZIndex() - b.getZIndex());
455
488
  this.padRegion();
456
489
  this.updateUI();
457
490
  }
@@ -465,7 +498,8 @@ _a = Selection;
465
498
  // The selection box is lost when serializing/deserializing. No need to store box rotation
466
499
  const fullTransform = new Mat33(...json.transform);
467
500
  const elemIds = (json.elems ?? []);
468
- return new _a.ApplyTransformationCommand(null, elemIds, fullTransform);
501
+ const deltaZIndex = parseInt(json.deltaZIndex ?? 0);
502
+ return new _a.ApplyTransformationCommand(null, elemIds, fullTransform, deltaZIndex);
469
503
  });
470
504
  })();
471
505
  Selection.ApplyTransformationCommand = class extends SerializableCommand {
@@ -473,10 +507,11 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
473
507
  // If a `string[]`, selectedElems is a list of element IDs.
474
508
  selectedElems,
475
509
  // Full transformation used to transform elements.
476
- fullTransform) {
510
+ fullTransform, deltaZIndex) {
477
511
  super('selection-tool-transform');
478
512
  this.selection = selection;
479
513
  this.fullTransform = fullTransform;
514
+ this.deltaZIndex = deltaZIndex;
480
515
  const isIDList = (arr) => {
481
516
  return typeof arr[0] === 'string';
482
517
  };
@@ -487,11 +522,11 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
487
522
  else {
488
523
  this.selectedElemIds = selectedElems.map(elem => elem.getId());
489
524
  this.transformCommands = selectedElems.map(elem => {
490
- return elem.transformBy(this.fullTransform);
525
+ return elem.setZIndexAndTransformBy(this.fullTransform, elem.getZIndex() + deltaZIndex);
491
526
  });
492
527
  }
493
528
  }
494
- resolveToElems(editor) {
529
+ resolveToElems(editor, isUndoing) {
495
530
  if (this.transformCommands) {
496
531
  return;
497
532
  }
@@ -500,11 +535,19 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
500
535
  if (!elem) {
501
536
  throw new Error(`Unable to find element with ID, ${id}.`);
502
537
  }
503
- return elem.transformBy(this.fullTransform);
538
+ let originalZIndex = elem.getZIndex();
539
+ let targetZIndex = elem.getZIndex() + this.deltaZIndex;
540
+ // If the command has already been applied, the element should currently
541
+ // have the target z-index.
542
+ if (isUndoing) {
543
+ targetZIndex = elem.getZIndex();
544
+ originalZIndex = elem.getZIndex() - this.deltaZIndex;
545
+ }
546
+ return elem.setZIndexAndTransformBy(this.fullTransform, targetZIndex, originalZIndex);
504
547
  });
505
548
  }
506
549
  async apply(editor) {
507
- this.resolveToElems(editor);
550
+ this.resolveToElems(editor, false);
508
551
  this.selection?.setTransform(this.fullTransform, false);
509
552
  this.selection?.updateUI();
510
553
  await editor.asyncApplyCommands(this.transformCommands, updateChunkSize);
@@ -513,7 +556,7 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
513
556
  this.selection?.updateUI();
514
557
  }
515
558
  async unapply(editor) {
516
- this.resolveToElems(editor);
559
+ this.resolveToElems(editor, true);
517
560
  this.selection?.setTransform(this.fullTransform.inverse(), false);
518
561
  this.selection?.updateUI();
519
562
  await editor.asyncUnapplyCommands(this.transformCommands, updateChunkSize, true);
@@ -525,6 +568,7 @@ Selection.ApplyTransformationCommand = class extends SerializableCommand {
525
568
  return {
526
569
  elems: this.selectedElemIds,
527
570
  transform: this.fullTransform.toArray(),
571
+ deltaZIndex: this.deltaZIndex,
528
572
  };
529
573
  }
530
574
  description(_editor, localizationTable) {
@@ -28,6 +28,7 @@ export default class SelectionTool extends BaseTool {
28
28
  private onSelectionUpdated;
29
29
  private zoomToSelection;
30
30
  private static handleableKeys;
31
+ private hasUnfinalizedTransformFromKeyPress;
31
32
  onKeyPress(event: KeyPressEvent): boolean;
32
33
  onKeyUp(evt: KeyUpEvent): boolean;
33
34
  onCopy(event: CopyEvent): boolean;
@@ -5,7 +5,7 @@ import BaseTool from '../BaseTool.mjs';
5
5
  import SVGRenderer from '../../rendering/renderers/SVGRenderer.mjs';
6
6
  import Selection from './Selection.mjs';
7
7
  import TextComponent from '../../components/TextComponent.mjs';
8
- import { duplicateSelectionShortcut, selectAllKeyboardShortcut, snapToGridKeyboardShortcutId } from '../keybindings.mjs';
8
+ import { duplicateSelectionShortcut, selectAllKeyboardShortcut, sendToBackSelectionShortcut, snapToGridKeyboardShortcutId } from '../keybindings.mjs';
9
9
  import ToPointerAutoscroller from './ToPointerAutoscroller.mjs';
10
10
  export const cssPrefix = 'selection-tool-';
11
11
  // Allows users to select/transform portions of the `EditorImage`.
@@ -21,6 +21,9 @@ class SelectionTool extends BaseTool {
21
21
  this.lastPointer = null;
22
22
  this.selectionBoxHandlingEvt = false;
23
23
  this.lastSelectedObjects = [];
24
+ // Whether the last keypress corresponded to an action that didn't transform the
25
+ // selection (and thus does not need to be finalized on onKeyUp).
26
+ this.hasUnfinalizedTransformFromKeyPress = false;
24
27
  this.autoscroller = new ToPointerAutoscroller(editor.viewport, (scrollBy) => {
25
28
  editor.dispatch(Viewport.transformBy(Mat33.translation(scrollBy)), false);
26
29
  // Update the selection box/content to match the new viewport.
@@ -213,7 +216,8 @@ class SelectionTool extends BaseTool {
213
216
  this.snapToGrid = true;
214
217
  return true;
215
218
  }
216
- if (this.selectionBox && shortcucts.matchesShortcut(duplicateSelectionShortcut, event)) {
219
+ if (this.selectionBox && (shortcucts.matchesShortcut(duplicateSelectionShortcut, event)
220
+ || shortcucts.matchesShortcut(sendToBackSelectionShortcut, event))) {
217
221
  // Handle duplication on key up — we don't want to accidentally duplicate
218
222
  // many times.
219
223
  return true;
@@ -297,6 +301,8 @@ class SelectionTool extends BaseTool {
297
301
  const oldTransform = this.selectionBox.getTransform();
298
302
  this.selectionBox.setTransform(oldTransform.rightMul(transform));
299
303
  this.selectionBox.scrollTo();
304
+ // The transformation needs to be finalized at some point (on key up)
305
+ this.hasUnfinalizedTransformFromKeyPress = true;
300
306
  }
301
307
  if (this.selectionBox && !handled && (event.key === 'Delete' || event.key === 'Backspace')) {
302
308
  this.editor.dispatch(this.selectionBox.deleteSelectedObjects());
@@ -322,12 +328,24 @@ class SelectionTool extends BaseTool {
322
328
  });
323
329
  return true;
324
330
  }
331
+ if (this.selectionBox && shortcucts.matchesShortcut(sendToBackSelectionShortcut, evt)) {
332
+ const sendToBackCommand = this.selectionBox.sendToBack();
333
+ if (sendToBackCommand) {
334
+ this.editor.dispatch(sendToBackCommand);
335
+ }
336
+ return true;
337
+ }
325
338
  if (evt.key === 'Shift') {
326
339
  this.shiftKeyPressed = false;
327
340
  return true;
328
341
  }
342
+ // If we don't need to finalize the transform
343
+ if (!this.hasUnfinalizedTransformFromKeyPress) {
344
+ return true;
345
+ }
329
346
  if (this.selectionBox && SelectionTool.handleableKeys.some(key => key === evt.key)) {
330
347
  this.selectionBox.finalizeTransform();
348
+ this.hasUnfinalizedTransformFromKeyPress = false;
331
349
  return true;
332
350
  }
333
351
  return false;
@@ -7,7 +7,6 @@ import BaseTool from './BaseTool';
7
7
  *
8
8
  * This is in the default set of {@link ToolController} tools.
9
9
  *
10
- * @deprecated This may be replaced in the future.
11
10
  */
12
11
  export default class ToolSwitcherShortcut extends BaseTool {
13
12
  private editor;
@@ -5,7 +5,6 @@ import BaseTool from './BaseTool.mjs';
5
5
  *
6
6
  * This is in the default set of {@link ToolController} tools.
7
7
  *
8
- * @deprecated This may be replaced in the future.
9
8
  */
10
9
  export default class ToolSwitcherShortcut extends BaseTool {
11
10
  constructor(editor) {
@@ -15,3 +15,4 @@ export declare const zoomInKeyboardShortcutId = "jsdraw.tools.PanZoom.zoomIn";
15
15
  export declare const zoomOutKeyboardShortcutId = "jsdraw.tools.PanZoom.zoomOut";
16
16
  export declare const selectAllKeyboardShortcut = "jsdraw.tools.SelectionTool.selectAll";
17
17
  export declare const duplicateSelectionShortcut = "jsdraw.tools.SelectionTool.duplicateSelection";
18
+ export declare const sendToBackSelectionShortcut = "jsdraw.tools.SelectionTool.sendToBack";
@@ -39,3 +39,5 @@ export const selectAllKeyboardShortcut = 'jsdraw.tools.SelectionTool.selectAll';
39
39
  KeyboardShortcutManager.registerDefaultKeyboardShortcut(selectAllKeyboardShortcut, ['CtrlOrMeta+KeyA'], 'Select all');
40
40
  export const duplicateSelectionShortcut = 'jsdraw.tools.SelectionTool.duplicateSelection';
41
41
  KeyboardShortcutManager.registerDefaultKeyboardShortcut(duplicateSelectionShortcut, ['CtrlOrMeta+KeyD'], 'Duplicate selection');
42
+ export const sendToBackSelectionShortcut = 'jsdraw.tools.SelectionTool.sendToBack';
43
+ KeyboardShortcutManager.registerDefaultKeyboardShortcut(sendToBackSelectionShortcut, ['End'], 'Send to back');
@@ -1,3 +1,3 @@
1
1
  export default {
2
- number: '1.10.0',
2
+ number: '1.11.0',
3
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -86,5 +86,5 @@
86
86
  "freehand",
87
87
  "svg"
88
88
  ],
89
- "gitHead": "ccf1d0634e902c731fcd794df11cd001c3a30585"
89
+ "gitHead": "01fc3dc7bdbc9f456705bf08d9c30b4549122d97"
90
90
  }