js-draw 1.23.1 → 1.24.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/dist/Editor.css +21 -0
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundledStyles.js +1 -1
  4. package/dist/cjs/Editor.d.ts +7 -1
  5. package/dist/cjs/Editor.js +71 -9
  6. package/dist/cjs/SVGLoader/SVGLoader.js +17 -7
  7. package/dist/cjs/Viewport.d.ts +2 -0
  8. package/dist/cjs/Viewport.js +0 -1
  9. package/dist/cjs/components/AbstractComponent.d.ts +1 -1
  10. package/dist/cjs/components/BackgroundComponent.js +17 -7
  11. package/dist/cjs/components/SVGGlobalAttributesObject.js +17 -7
  12. package/dist/cjs/components/UnknownSVGObject.js +17 -7
  13. package/dist/cjs/components/lib.js +17 -7
  14. package/dist/cjs/image/EditorImage.js +17 -7
  15. package/dist/cjs/testing/sendPenEvent.js +17 -7
  16. package/dist/cjs/testing/sendTouchEvent.js +17 -7
  17. package/dist/cjs/toolbar/EdgeToolbar.d.ts +1 -1
  18. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +17 -7
  19. package/dist/cjs/toolbar/widgets/HandToolWidget.js +17 -7
  20. package/dist/cjs/tools/InputFilter/ContextMenuRecognizer.js +17 -7
  21. package/dist/cjs/tools/Pen.js +17 -7
  22. package/dist/cjs/tools/SelectionTool/Selection.js +17 -7
  23. package/dist/cjs/tools/SelectionTool/SelectionMenuShortcut.js +1 -1
  24. package/dist/cjs/tools/SelectionTool/util/makeClipboardErrorHandlers.d.ts +2 -2
  25. package/dist/cjs/tools/SelectionTool/util/makeClipboardErrorHandlers.js +1 -1
  26. package/dist/cjs/tools/SoundUITool.js +1 -1
  27. package/dist/cjs/tools/TextTool.d.ts +4 -4
  28. package/dist/cjs/tools/TextTool.js +45 -51
  29. package/dist/cjs/tools/ToolController.js +17 -7
  30. package/dist/cjs/tools/UndoRedoShortcut.js +2 -2
  31. package/dist/cjs/util/ClipboardHandler.js +1 -0
  32. package/dist/cjs/version.js +1 -1
  33. package/dist/mjs/Editor.d.ts +7 -1
  34. package/dist/mjs/Editor.mjs +54 -2
  35. package/dist/mjs/Viewport.d.ts +2 -0
  36. package/dist/mjs/Viewport.mjs +0 -1
  37. package/dist/mjs/components/AbstractComponent.d.ts +1 -1
  38. package/dist/mjs/toolbar/EdgeToolbar.d.ts +1 -1
  39. package/dist/mjs/tools/SelectionTool/SelectionMenuShortcut.mjs +1 -1
  40. package/dist/mjs/tools/SelectionTool/util/makeClipboardErrorHandlers.d.ts +2 -2
  41. package/dist/mjs/tools/SelectionTool/util/makeClipboardErrorHandlers.mjs +1 -1
  42. package/dist/mjs/tools/SoundUITool.mjs +1 -1
  43. package/dist/mjs/tools/TextTool.d.ts +4 -4
  44. package/dist/mjs/tools/TextTool.mjs +45 -51
  45. package/dist/mjs/tools/UndoRedoShortcut.mjs +2 -2
  46. package/dist/mjs/util/ClipboardHandler.mjs +1 -0
  47. package/dist/mjs/version.mjs +1 -1
  48. package/package.json +4 -4
  49. package/src/Editor.scss +31 -0
@@ -25,7 +25,7 @@ import guessKeyCodeFromKey from './util/guessKeyCodeFromKey.mjs';
25
25
  import makeAboutDialog from './dialogs/makeAboutDialog.mjs';
26
26
  import version from './version.mjs';
27
27
  import { editorImageToSVGSync, editorImageToSVGAsync } from './image/export/editorImageToSVG.mjs';
28
- import { MutableReactiveValue } from './util/ReactiveValue.mjs';
28
+ import ReactiveValue, { MutableReactiveValue } from './util/ReactiveValue.mjs';
29
29
  import listenForKeyboardEventsFrom from './util/listenForKeyboardEventsFrom.mjs';
30
30
  import mitLicenseAttribution from './util/mitLicenseAttribution.mjs';
31
31
  import ClipboardHandler from './util/ClipboardHandler.mjs';
@@ -473,7 +473,7 @@ export class Editor {
473
473
  /** @internal */
474
474
  async handleDrop(evt) {
475
475
  evt.preventDefault();
476
- this.handlePaste(evt);
476
+ await this.handlePaste(evt);
477
477
  }
478
478
  /** @internal */
479
479
  async handlePaste(evt) {
@@ -884,6 +884,58 @@ export class Editor {
884
884
  remove: () => overlay.remove(),
885
885
  };
886
886
  }
887
+ /**
888
+ * Anchors the given `element` to the canvas with a given position/transformation in canvas space.
889
+ */
890
+ anchorElementToCanvas(element, canvasTransform) {
891
+ if (canvasTransform instanceof Mat33) {
892
+ canvasTransform = ReactiveValue.fromImmutable(canvasTransform);
893
+ }
894
+ // The element hierarchy looks like this:
895
+ // overlay > contentWrapper > content
896
+ //
897
+ // Both contentWrapper and overlay are present to:
898
+ // 1. overlay: Positions the content at the top left of the viewport. The overlay
899
+ // has `height: 0` to allow other overlays to also be aligned with the viewport's
900
+ // top left.
901
+ // 2. contentWrapper: Has the same width/height as the editor's visible region and
902
+ // has `overflow: hidden`. This prevents the anchored element from being visible
903
+ // when not in the visible region of the canvas.
904
+ const overlay = document.createElement('div');
905
+ overlay.classList.add('anchored-element-overlay');
906
+ const contentWrapper = document.createElement('div');
907
+ contentWrapper.classList.add('content-wrapper');
908
+ element.classList.add('content');
909
+ // Updates CSS variables that specify the position/rotation/scale of the content.
910
+ const updateElementPositioning = () => {
911
+ const transform = canvasTransform.get();
912
+ const canvasRotation = transform.transformVec3(Vec2.unitX).angle();
913
+ const screenRotation = canvasRotation + this.viewport.getRotationAngle();
914
+ const screenTransform = this.viewport.canvasToScreenTransform.rightMul(canvasTransform.get());
915
+ overlay.style.setProperty('--full-transform', screenTransform.toCSSMatrix());
916
+ const translation = screenTransform.transformVec2(Vec2.zero);
917
+ overlay.style.setProperty('--position-x', `${translation.x}px`);
918
+ overlay.style.setProperty('--position-y', `${translation.y}px`);
919
+ overlay.style.setProperty('--rotation', `${(screenRotation * 180) / Math.PI}deg`);
920
+ overlay.style.setProperty('--scale', `${screenTransform.getScaleFactor()}`);
921
+ };
922
+ updateElementPositioning();
923
+ // The anchored element needs to be updated both when the user moves the canvas
924
+ // and when the anchored element's transform changes.
925
+ const updateListener = canvasTransform.onUpdate(updateElementPositioning);
926
+ const viewportListener = this.notifier.on(EditorEventType.ViewportChanged, updateElementPositioning);
927
+ contentWrapper.appendChild(element);
928
+ overlay.appendChild(contentWrapper);
929
+ overlay.classList.add('overlay', 'js-draw-editor-overlay');
930
+ this.renderingRegion.insertAdjacentElement('afterend', overlay);
931
+ return {
932
+ remove: () => {
933
+ overlay.remove();
934
+ updateListener.remove();
935
+ viewportListener.remove();
936
+ },
937
+ };
938
+ }
887
939
  /**
888
940
  * Creates a CSS stylesheet with `content` and applies it to the document
889
941
  * (and thus, to this editor).
@@ -9,7 +9,9 @@ type TransformChangeCallback = (oldTransform: Mat33, newTransform: Mat33) => voi
9
9
  export declare class Viewport {
10
10
  private onTransformChangeCallback;
11
11
  private static ViewportTransform;
12
+ /** Converts from canvas to screen coordinates */
12
13
  private transform;
14
+ /** Converts from screen to canvas coordinates */
13
15
  private inverseTransform;
14
16
  private screenRect;
15
17
  constructor(onTransformChangeCallback: TransformChangeCallback);
@@ -127,7 +127,6 @@ export class Viewport {
127
127
  }
128
128
  // The separate function type definition seems necessary here.
129
129
  // See https://stackoverflow.com/a/58163623/17055750.
130
- // eslint-disable-next-line no-dupe-class-members
131
130
  static roundPoint(point, tolerance) {
132
131
  const scaleFactor = 10 ** Math.floor(Math.log10(tolerance));
133
132
  const roundComponent = (component) => {
@@ -163,5 +163,5 @@ export default abstract class AbstractComponent {
163
163
  data: string | number | any[] | Record<string, any>;
164
164
  };
165
165
  private static isNotDeserializable;
166
- static deserialize(json: string | any): AbstractComponent;
166
+ static deserialize(json: any): AbstractComponent;
167
167
  }
@@ -49,7 +49,7 @@ export default class EdgeToolbar extends AbstractToolbar {
49
49
  constructor(editor: Editor, parent: HTMLElement, localizationTable: ToolbarLocalization);
50
50
  private listenForVisibilityChanges;
51
51
  private onToolbarRowResize;
52
- addSpacer(_options?: Partial<SpacerOptions> | undefined): void;
52
+ addSpacer(_options?: Partial<SpacerOptions>): void;
53
53
  addUndoRedoButtons(): void;
54
54
  addDefaults(): void;
55
55
  private updateWidgetCSSClasses;
@@ -10,7 +10,7 @@ export default class SelectionMenuShortcut {
10
10
  this.element = document.createElement('div');
11
11
  this.element.classList.add(`${cssPrefix}handle`, `${cssPrefix}selection-menu`);
12
12
  this.element.style.setProperty('--vertical-offset', `${verticalOffset}px`);
13
- this.onClick = async () => {
13
+ this.onClick = () => {
14
14
  const anchor = this.getBBoxCanvasCoords().center;
15
15
  showContextMenu(anchor);
16
16
  };
@@ -1,6 +1,6 @@
1
1
  import Editor from '../../../Editor';
2
2
  declare const makeClipboardErrorHandlers: (editor: Editor) => {
3
- onCopyError(error: Error | unknown): Promise<void>;
4
- onPasteError(error: Error | unknown): void;
3
+ onCopyError(error: unknown): void;
4
+ onPasteError(error: unknown): void;
5
5
  };
6
6
  export default makeClipboardErrorHandlers;
@@ -16,7 +16,7 @@ const makeClipboardErrorHandlers = (editor) => {
16
16
  return dialog;
17
17
  };
18
18
  return {
19
- async onCopyError(error) {
19
+ onCopyError(error) {
20
20
  const dialog = makeErrorDialog(error);
21
21
  const textboxLabel = document.createElement('label');
22
22
  textboxLabel.textContent = editor.localization.copyPasteError__copyRetry;
@@ -73,7 +73,7 @@ class SoundFeedback {
73
73
  this.boundaryGain.gain.linearRampToValueAtTime(0, this.ctx.currentTime + 0.25);
74
74
  }
75
75
  close() {
76
- this.ctx.close();
76
+ void this.ctx.close();
77
77
  this.closed = true;
78
78
  }
79
79
  }
@@ -5,23 +5,22 @@ import BaseTool from './BaseTool';
5
5
  import { ToolLocalization } from './localization';
6
6
  import TextRenderingStyle from '../rendering/TextRenderingStyle';
7
7
  import { MutableReactiveValue } from '../util/ReactiveValue';
8
+ /** A tool that allows users to enter and edit text. */
8
9
  export default class TextTool extends BaseTool {
9
10
  private editor;
10
11
  private localizationTable;
11
12
  private textStyleValue;
12
13
  private textStyle;
14
+ private anchorControl;
15
+ private contentTransform;
13
16
  private textEditOverlay;
14
17
  private textInputElem;
15
- private textTargetPosition;
16
18
  private textMeasuringCtx;
17
- private textRotation;
18
- private textScale;
19
19
  private removeExistingCommand;
20
20
  constructor(editor: Editor, description: string, localizationTable: ToolLocalization);
21
21
  private initTextMeasuringCanvas;
22
22
  private getTextAscent;
23
23
  private flushInput;
24
- private getTextScaleMatrix;
25
24
  private updateTextInput;
26
25
  private startTextInput;
27
26
  setEnabled(enabled: boolean): void;
@@ -33,4 +32,5 @@ export default class TextTool extends BaseTool {
33
32
  getTextStyle(): TextRenderingStyle;
34
33
  getStyleValue(): MutableReactiveValue<TextRenderingStyle>;
35
34
  private setTextStyle;
35
+ onDestroy(): void;
36
36
  }
@@ -8,15 +8,14 @@ import Erase from '../commands/Erase.mjs';
8
8
  import uniteCommands from '../commands/uniteCommands.mjs';
9
9
  import { ReactiveValue } from '../util/ReactiveValue.mjs';
10
10
  const overlayCSSClass = 'textEditorOverlay';
11
+ /** A tool that allows users to enter and edit text. */
11
12
  export default class TextTool extends BaseTool {
12
13
  constructor(editor, description, localizationTable) {
13
14
  super(editor.notifier, description);
14
15
  this.editor = editor;
15
16
  this.localizationTable = localizationTable;
16
17
  this.textInputElem = null;
17
- this.textTargetPosition = null;
18
18
  this.textMeasuringCtx = null;
19
- this.textScale = Vec2.of(1, 1);
20
19
  this.removeExistingCommand = null;
21
20
  const editorFonts = editor.getCurrentSettings().text?.fonts ?? [];
22
21
  this.textStyleValue = ReactiveValue.fromInitialValue({
@@ -34,18 +33,10 @@ export default class TextTool extends BaseTool {
34
33
  tool: this,
35
34
  });
36
35
  });
36
+ this.contentTransform = ReactiveValue.fromInitialValue(Mat33.identity);
37
37
  this.textEditOverlay = document.createElement('div');
38
38
  this.textEditOverlay.classList.add(overlayCSSClass);
39
39
  this.editor.addStyleSheet(`
40
- .${overlayCSSClass} {
41
- height: 0;
42
- overflow: visible;
43
-
44
- /* Allows absolutely-positioned textareas to scroll with
45
- the containing overlay. */
46
- position: relative;
47
- }
48
-
49
40
  .${overlayCSSClass} textarea {
50
41
  background-color: rgba(0, 0, 0, 0);
51
42
 
@@ -61,8 +52,7 @@ export default class TextTool extends BaseTool {
61
52
  min-height: 1.1em;
62
53
  }
63
54
  `);
64
- this.editor.createHTMLOverlay(this.textEditOverlay);
65
- this.editor.notifier.on(EditorEventType.ViewportChanged, () => this.updateTextInput());
55
+ this.anchorControl = this.editor.anchorElementToCanvas(this.textEditOverlay, this.contentTransform);
66
56
  }
67
57
  initTextMeasuringCanvas() {
68
58
  this.textMeasuringCtx ??= document.createElement('canvas').getContext('2d');
@@ -82,24 +72,32 @@ export default class TextTool extends BaseTool {
82
72
  // If [removeInput], the HTML input element is removed. Otherwise, its value
83
73
  // is cleared.
84
74
  flushInput(removeInput = true) {
85
- if (this.textInputElem && this.textTargetPosition) {
86
- const content = this.textInputElem.value.trimEnd();
87
- this.textInputElem.value = '';
88
- if (removeInput) {
89
- // In some browsers, .remove() triggers a .blur event (synchronously).
90
- // Clear this.textInputElem before removal
91
- const input = this.textInputElem;
92
- this.textInputElem = null;
93
- input.remove();
94
- }
95
- if (content === '') {
96
- return;
97
- }
98
- const textTransform = Mat33.translation(this.textTargetPosition)
99
- .rightMul(this.getTextScaleMatrix())
100
- .rightMul(Mat33.scaling2D(this.editor.viewport.getSizeOfPixelOnCanvas()))
101
- .rightMul(Mat33.zRotation(this.textRotation));
102
- const textComponent = TextComponent.fromLines(content.split('\n'), textTransform, this.textStyle);
75
+ if (!this.textInputElem)
76
+ return;
77
+ // Determine the scroll first -- removing the input (and other DOM changes)
78
+ // also change the scroll.
79
+ const scrollingRegion = this.textEditOverlay.parentElement;
80
+ const containerScroll = Vec2.of(scrollingRegion?.scrollLeft ?? 0, scrollingRegion?.scrollTop ?? 0);
81
+ const content = this.textInputElem.value.trimEnd();
82
+ this.textInputElem.value = '';
83
+ if (removeInput) {
84
+ // In some browsers, .remove() triggers a .blur event (synchronously).
85
+ // Clear this.textInputElem before removal
86
+ const input = this.textInputElem;
87
+ this.textInputElem = null;
88
+ input.remove();
89
+ }
90
+ if (content !== '') {
91
+ // When the text is long, it can cause its container to scroll so that the
92
+ // editing caret is in view.
93
+ // So that the text added to the document is in the same position as the text
94
+ // shown in the editor, account for this scroll when computing the transform:
95
+ const scrollCorrectionScreen = containerScroll.times(-1);
96
+ // Uses .transformVec3 to avoid also translating the scroll correction (treating
97
+ // it as a point):
98
+ const scrollCorrectionCanvas = this.editor.viewport.screenToCanvasTransform.transformVec3(scrollCorrectionScreen);
99
+ const scrollTransform = Mat33.translation(scrollCorrectionCanvas);
100
+ const textComponent = TextComponent.fromLines(content.split('\n'), scrollTransform.rightMul(this.contentTransform.get()), this.textStyle);
103
101
  const action = EditorImage.addElement(textComponent);
104
102
  if (this.removeExistingCommand) {
105
103
  // Unapply so that `removeExistingCommand` can be added to the undo stack.
@@ -112,16 +110,10 @@ export default class TextTool extends BaseTool {
112
110
  }
113
111
  }
114
112
  }
115
- getTextScaleMatrix() {
116
- return Mat33.scaling2D(this.textScale.times(1 / this.editor.viewport.getSizeOfPixelOnCanvas()));
117
- }
118
113
  updateTextInput() {
119
- if (!this.textInputElem || !this.textTargetPosition) {
120
- this.textInputElem?.remove();
114
+ if (!this.textInputElem) {
121
115
  return;
122
116
  }
123
- const viewport = this.editor.viewport;
124
- const textScreenPos = viewport.canvasToScreen(this.textTargetPosition);
125
117
  this.textInputElem.placeholder = this.localizationTable.enterTextToInsert;
126
118
  this.textInputElem.style.fontFamily = this.textStyle.fontFamily;
127
119
  this.textInputElem.style.fontStyle = this.textStyle.fontStyle ?? '';
@@ -129,9 +121,6 @@ export default class TextTool extends BaseTool {
129
121
  this.textInputElem.style.fontWeight = this.textStyle.fontWeight ?? '';
130
122
  this.textInputElem.style.fontSize = `${this.textStyle.size}px`;
131
123
  this.textInputElem.style.color = this.textStyle.renderingStyle.fill.toHexString();
132
- this.textInputElem.style.position = 'absolute';
133
- this.textInputElem.style.left = `${textScreenPos.x}px`;
134
- this.textInputElem.style.top = `${textScreenPos.y}px`;
135
124
  this.textInputElem.style.margin = '0';
136
125
  this.textInputElem.style.width = `${this.textInputElem.scrollWidth}px`;
137
126
  this.textInputElem.style.height = `${this.textInputElem.scrollHeight}px`;
@@ -140,9 +129,7 @@ export default class TextTool extends BaseTool {
140
129
  const tallText = 'Testing!';
141
130
  const ascent = this.getTextAscent(tallText, this.textStyle);
142
131
  const vertAdjust = ascent;
143
- const rotation = this.textRotation + viewport.getRotationAngle();
144
- const scale = this.getTextScaleMatrix();
145
- this.textInputElem.style.transform = `${scale.toCSSMatrix()} rotate(${(rotation * 180) / Math.PI}deg) translate(0, ${-vertAdjust}px)`;
132
+ this.textInputElem.style.transform = `translate(0, ${-vertAdjust}px)`;
146
133
  this.textInputElem.style.transformOrigin = 'top left';
147
134
  // Match the line height of default rendered text.
148
135
  const lineHeight = Math.floor(this.textStyle.size);
@@ -153,9 +140,14 @@ export default class TextTool extends BaseTool {
153
140
  this.textInputElem = document.createElement('textarea');
154
141
  this.textInputElem.value = initialText;
155
142
  this.textInputElem.style.display = 'inline-block';
156
- this.textTargetPosition = this.editor.viewport.roundPoint(textCanvasPos);
157
- this.textRotation = -this.editor.viewport.getRotationAngle();
158
- this.textScale = Vec2.of(1, 1).times(this.editor.viewport.getSizeOfPixelOnCanvas());
143
+ const textTargetPosition = this.editor.viewport.roundPoint(textCanvasPos);
144
+ const textRotation = -this.editor.viewport.getRotationAngle();
145
+ const textScale = Vec2.of(1, 1).times(this.editor.viewport.getSizeOfPixelOnCanvas());
146
+ this.contentTransform.set(
147
+ // Scale, then rotate, then translate:
148
+ Mat33.translation(textTargetPosition)
149
+ .rightMul(Mat33.zRotation(textRotation))
150
+ .rightMul(Mat33.scaling2D(textScale)));
159
151
  this.updateTextInput();
160
152
  // Update the input size/position/etc. after the placeHolder has had time to appear.
161
153
  setTimeout(() => this.updateTextInput(), 0);
@@ -224,10 +216,7 @@ export default class TextTool extends BaseTool {
224
216
  this.removeExistingCommand = new Erase([targetNode]);
225
217
  this.removeExistingCommand.apply(this.editor);
226
218
  this.startTextInput(targetNode.getBaselinePos(), targetNode.getText());
227
- const transform = targetNode.getTransform();
228
- this.textRotation = transform.transformVec3(Vec2.unitX).angle();
229
- const scaleFactor = transform.transformVec3(Vec2.unitX).magnitude();
230
- this.textScale = Vec2.of(1, 1).times(scaleFactor);
219
+ this.contentTransform.set(targetNode.getTransform());
231
220
  this.updateTextInput();
232
221
  }
233
222
  else {
@@ -278,4 +267,9 @@ export default class TextTool extends BaseTool {
278
267
  setTextStyle(style) {
279
268
  this.textStyleValue.set(style);
280
269
  }
270
+ // @internal
271
+ onDestroy() {
272
+ super.onDestroy();
273
+ this.anchorControl.remove();
274
+ }
281
275
  }
@@ -9,11 +9,11 @@ export default class UndoRedoShortcut extends BaseTool {
9
9
  // @internal
10
10
  onKeyPress(event) {
11
11
  if (this.editor.shortcuts.matchesShortcut(undoKeyboardShortcutId, event)) {
12
- this.editor.history.undo();
12
+ void this.editor.history.undo();
13
13
  return true;
14
14
  }
15
15
  else if (this.editor.shortcuts.matchesShortcut(redoKeyboardShortcutId, event)) {
16
- this.editor.history.redo();
16
+ void this.editor.history.redo();
17
17
  return true;
18
18
  }
19
19
  return false;
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-redundant-type-constituents -- Used for clarity */
1
2
  var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
3
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
4
  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");
@@ -4,5 +4,5 @@
4
4
  * @internal
5
5
  */
6
6
  export default {
7
- number: '1.23.1',
7
+ number: '1.24.1',
8
8
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.23.1",
3
+ "version": "1.24.1",
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",
@@ -64,11 +64,11 @@
64
64
  "postpack": "ts-node tools/copyREADME.ts revert"
65
65
  },
66
66
  "dependencies": {
67
- "@js-draw/math": "^1.23.1",
67
+ "@js-draw/math": "^1.24.1",
68
68
  "@melloware/coloris": "0.22.0"
69
69
  },
70
70
  "devDependencies": {
71
- "@js-draw/build-tool": "^1.23.1",
71
+ "@js-draw/build-tool": "^1.24.1",
72
72
  "@types/jest": "29.5.5",
73
73
  "@types/jsdom": "21.1.3"
74
74
  },
@@ -86,5 +86,5 @@
86
86
  "freehand",
87
87
  "svg"
88
88
  ],
89
- "gitHead": "e0bb3336d5f3a94533c823906778d39a4880f4cf"
89
+ "gitHead": "ef847374748e32d6d96d993a2236a99d9109a32c"
90
90
  }
package/src/Editor.scss CHANGED
@@ -165,6 +165,37 @@
165
165
  z-index: 5;
166
166
  }
167
167
 
168
+ // See Editor.anchorElementToCanvas
169
+ .imageEditorContainer .anchored-element-overlay {
170
+ overflow: visible;
171
+ height: 0;
172
+
173
+ > .content-wrapper {
174
+ width: var(--editor-current-display-width-px);
175
+ height: var(--editor-current-display-height-px);
176
+ overflow: hidden;
177
+ // Display 'position: absolute' children relative to this.
178
+ position: relative;
179
+
180
+ // Disable pointer events: If the parent (or the container) has
181
+ // captured pointers and the container is removed, this prevents
182
+ // us from receiving the following events (e.g. in Firefox).
183
+ pointer-events: none;
184
+
185
+ > .content {
186
+ position: absolute;
187
+ left: var(--position-x);
188
+ top: var(--position-y);
189
+ transform: scale(var(--scale)) rotate(var(--rotation));
190
+ transform-origin: left top;
191
+ margin: 0;
192
+
193
+ // We *do* want pointer events for the positioned content.
194
+ pointer-events: all;
195
+ }
196
+ }
197
+ }
198
+
168
199
  // TODO: Apply this change during a future major release.
169
200
  // So as not to change the position of other overlays, all overlays should have
170
201
  // 0 height.