@tldraw/editor 4.5.0-next.dad6df23ab1f → 4.5.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 (38) hide show
  1. package/dist-cjs/index.d.ts +8 -0
  2. package/dist-cjs/index.js +2 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +6 -2
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +2 -3
  7. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  8. package/dist-cjs/lib/hooks/useCanvasEvents.js +3 -3
  9. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  10. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
  11. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  12. package/dist-cjs/lib/utils/dom.js +8 -4
  13. package/dist-cjs/lib/utils/dom.js.map +2 -2
  14. package/dist-cjs/version.js +3 -3
  15. package/dist-cjs/version.js.map +1 -1
  16. package/dist-esm/index.d.mts +8 -0
  17. package/dist-esm/index.mjs +3 -1
  18. package/dist-esm/index.mjs.map +2 -2
  19. package/dist-esm/lib/editor/Editor.mjs +6 -2
  20. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  21. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +2 -3
  22. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  23. package/dist-esm/lib/hooks/useCanvasEvents.mjs +9 -4
  24. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  25. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -3
  26. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  27. package/dist-esm/lib/utils/dom.mjs +8 -4
  28. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  29. package/dist-esm/version.mjs +3 -3
  30. package/dist-esm/version.mjs.map +1 -1
  31. package/package.json +7 -7
  32. package/src/index.ts +1 -0
  33. package/src/lib/editor/Editor.ts +9 -0
  34. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +3 -3
  35. package/src/lib/hooks/useCanvasEvents.ts +9 -5
  36. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -5
  37. package/src/lib/utils/dom.ts +16 -8
  38. package/src/version.ts +3 -3
@@ -81,9 +81,8 @@ class Pointing extends StateNode {
81
81
  const delta = new Vec(w / 2, h / 2);
82
82
  const parentTransform = this.editor.getShapeParentTransform(shape);
83
83
  if (parentTransform) delta.rot(-parentTransform.rotation());
84
- let scale = 1;
85
- if (this.editor.user.getIsDynamicResizeMode()) {
86
- scale = 1 / this.editor.getZoomLevel();
84
+ const scale = this.editor.getResizeScaleFactor();
85
+ if (scale !== 1) {
87
86
  w *= scale;
88
87
  h *= scale;
89
88
  delta.mul(scale);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts"],
4
- "sourcesContent": ["import { TLShape, createShapeId } from '@tldraw/tlschema'\nimport { structuredClone } from '@tldraw/utils'\nimport { Vec } from '../../../../primitives/Vec'\nimport type { Editor } from '../../../Editor'\nimport { TLBaseBoxShape } from '../../../shapes/BaseBoxShapeUtil'\nimport { TLPointerEventInfo } from '../../../types/event-types'\nimport { StateNode } from '../../StateNode'\nimport type { BaseBoxShapeTool } from '../BaseBoxShapeTool'\n\nexport class Pointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\tif (editor.inputs.getIsDragging()) {\n\t\t\tconst originPagePoint = editor.inputs.getOriginPagePoint()\n\n\t\t\tconst shapeType = (this.parent as BaseBoxShapeTool)!.shapeType\n\n\t\t\tconst id = createShapeId()\n\n\t\t\tconst creatingMarkId = editor.markHistoryStoppingPoint(`creating_box:${id}`)\n\t\t\tconst newPoint = maybeSnapToGrid(originPagePoint, editor)\n\n\t\t\t// Allow this to trigger the max shapes reached alert\n\t\t\tthis.editor.createShapes([\n\t\t\t\t{\n\t\t\t\t\tid,\n\t\t\t\t\ttype: shapeType,\n\t\t\t\t\tx: newPoint.x,\n\t\t\t\t\ty: newPoint.y,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\tw: 1,\n\t\t\t\t\t\th: 1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t])\n\t\t\tconst shape = editor.getShape(id)\n\t\t\tif (!shape) {\n\t\t\t\tthis.cancel()\n\t\t\t\treturn\n\t\t\t}\n\t\t\teditor.select(id)\n\n\t\t\tconst parent = this.parent as BaseBoxShapeTool\n\t\t\tthis.editor.setCurrentTool(\n\t\t\t\t'select.resizing',\n\t\t\t\t{\n\t\t\t\t\t...info,\n\t\t\t\t\ttarget: 'selection',\n\t\t\t\t\thandle: 'bottom_right',\n\t\t\t\t\tisCreating: true,\n\t\t\t\t\tcreatingMarkId,\n\t\t\t\t\tcreationCursorOffset: { x: 1, y: 1 },\n\t\t\t\t\tonInteractionEnd: this.parent.id,\n\t\t\t\t\tonCreate: parent.onCreate\n\t\t\t\t\t\t? (shape: TLShape | null) => parent.onCreate?.(shape)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t} /** satisfies ResizingInfo, defined in main tldraw package \uD83D\uDE27 */\n\t\t\t)\n\t\t}\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tcomplete() {\n\t\tconst originPagePoint = this.editor.inputs.getOriginPagePoint()\n\n\t\tconst shapeType = (this.parent as BaseBoxShapeTool)!.shapeType as TLBaseBoxShape['type']\n\n\t\tconst id = createShapeId()\n\n\t\tthis.editor.markHistoryStoppingPoint(`creating_box:${id}`)\n\n\t\t// Allow this to trigger the max shapes reached alert\n\t\t// todo: add scale here when dynamic size is enabled (is this still needed?)\n\t\tthis.editor.createShapes([\n\t\t\t{\n\t\t\t\tid,\n\t\t\t\ttype: shapeType,\n\t\t\t\tx: originPagePoint.x,\n\t\t\t\ty: originPagePoint.y,\n\t\t\t},\n\t\t])\n\n\t\tconst shape = this.editor.getShape<TLBaseBoxShape>(id)!\n\t\tif (!shape) {\n\t\t\tthis.cancel()\n\t\t\treturn\n\t\t}\n\n\t\tlet { w, h } = shape.props\n\t\tconst delta = new Vec(w / 2, h / 2)\n\t\tconst parentTransform = this.editor.getShapeParentTransform(shape)\n\t\tif (parentTransform) delta.rot(-parentTransform.rotation())\n\t\tlet scale = 1\n\n\t\tif (this.editor.user.getIsDynamicResizeMode()) {\n\t\t\tscale = 1 / this.editor.getZoomLevel()\n\t\t\tw *= scale\n\t\t\th *= scale\n\t\t\tdelta.mul(scale)\n\t\t}\n\n\t\tconst next = structuredClone(shape)\n\t\tconst newPoint = maybeSnapToGrid(new Vec(shape.x - delta.x, shape.y - delta.y), this.editor)\n\t\tnext.x = newPoint.x\n\t\tnext.y = newPoint.y\n\t\tnext.props.w = w\n\t\tnext.props.h = h\n\n\t\tif ('scale' in shape.props) {\n\t\t\t;(next as TLBaseBoxShape & { props: { scale: number } }).props.scale = scale\n\t\t}\n\n\t\tthis.editor.updateShape(next)\n\n\t\tthis.editor.setSelectedShapes([id])\n\n\t\tif (this.editor.getInstanceState().isToolLocked) {\n\t\t\tthis.parent.transition('idle')\n\t\t} else {\n\t\t\tthis.editor.setCurrentTool('select.idle')\n\t\t}\n\t}\n\n\tcancel() {\n\t\tthis.parent.transition('idle')\n\t}\n}\n\n/**\n * Checks if grid mode is enabled and snaps a point to the grid if so\n *\n * @public\n */\nexport function maybeSnapToGrid(point: Vec, editor: Editor): Vec {\n\tconst isGridMode = editor.getInstanceState().isGridMode\n\tconst gridSize = editor.getDocumentSettings().gridSize\n\tif (isGridMode) return point.clone().snapToGrid(gridSize)\n\treturn point.clone()\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { TLShape, createShapeId } from '@tldraw/tlschema'\nimport { structuredClone } from '@tldraw/utils'\nimport { Vec } from '../../../../primitives/Vec'\nimport type { Editor } from '../../../Editor'\nimport { TLBaseBoxShape } from '../../../shapes/BaseBoxShapeUtil'\nimport { TLPointerEventInfo } from '../../../types/event-types'\nimport { StateNode } from '../../StateNode'\nimport type { BaseBoxShapeTool } from '../BaseBoxShapeTool'\n\nexport class Pointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\tif (editor.inputs.getIsDragging()) {\n\t\t\tconst originPagePoint = editor.inputs.getOriginPagePoint()\n\n\t\t\tconst shapeType = (this.parent as BaseBoxShapeTool)!.shapeType\n\n\t\t\tconst id = createShapeId()\n\n\t\t\tconst creatingMarkId = editor.markHistoryStoppingPoint(`creating_box:${id}`)\n\t\t\tconst newPoint = maybeSnapToGrid(originPagePoint, editor)\n\n\t\t\t// Allow this to trigger the max shapes reached alert\n\t\t\tthis.editor.createShapes([\n\t\t\t\t{\n\t\t\t\t\tid,\n\t\t\t\t\ttype: shapeType,\n\t\t\t\t\tx: newPoint.x,\n\t\t\t\t\ty: newPoint.y,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\tw: 1,\n\t\t\t\t\t\th: 1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t])\n\t\t\tconst shape = editor.getShape(id)\n\t\t\tif (!shape) {\n\t\t\t\tthis.cancel()\n\t\t\t\treturn\n\t\t\t}\n\t\t\teditor.select(id)\n\n\t\t\tconst parent = this.parent as BaseBoxShapeTool\n\t\t\tthis.editor.setCurrentTool(\n\t\t\t\t'select.resizing',\n\t\t\t\t{\n\t\t\t\t\t...info,\n\t\t\t\t\ttarget: 'selection',\n\t\t\t\t\thandle: 'bottom_right',\n\t\t\t\t\tisCreating: true,\n\t\t\t\t\tcreatingMarkId,\n\t\t\t\t\tcreationCursorOffset: { x: 1, y: 1 },\n\t\t\t\t\tonInteractionEnd: this.parent.id,\n\t\t\t\t\tonCreate: parent.onCreate\n\t\t\t\t\t\t? (shape: TLShape | null) => parent.onCreate?.(shape)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t} /** satisfies ResizingInfo, defined in main tldraw package \uD83D\uDE27 */\n\t\t\t)\n\t\t}\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tcomplete() {\n\t\tconst originPagePoint = this.editor.inputs.getOriginPagePoint()\n\n\t\tconst shapeType = (this.parent as BaseBoxShapeTool)!.shapeType as TLBaseBoxShape['type']\n\n\t\tconst id = createShapeId()\n\n\t\tthis.editor.markHistoryStoppingPoint(`creating_box:${id}`)\n\n\t\t// Allow this to trigger the max shapes reached alert\n\t\t// todo: add scale here when dynamic size is enabled (is this still needed?)\n\t\tthis.editor.createShapes([\n\t\t\t{\n\t\t\t\tid,\n\t\t\t\ttype: shapeType,\n\t\t\t\tx: originPagePoint.x,\n\t\t\t\ty: originPagePoint.y,\n\t\t\t},\n\t\t])\n\n\t\tconst shape = this.editor.getShape<TLBaseBoxShape>(id)!\n\t\tif (!shape) {\n\t\t\tthis.cancel()\n\t\t\treturn\n\t\t}\n\n\t\tlet { w, h } = shape.props\n\t\tconst delta = new Vec(w / 2, h / 2)\n\t\tconst parentTransform = this.editor.getShapeParentTransform(shape)\n\t\tif (parentTransform) delta.rot(-parentTransform.rotation())\n\t\tconst scale = this.editor.getResizeScaleFactor()\n\n\t\t// A scale factor of 1 means dynamic sizing is not affecting this shape.\n\t\tif (scale !== 1) {\n\t\t\tw *= scale\n\t\t\th *= scale\n\t\t\tdelta.mul(scale)\n\t\t}\n\n\t\tconst next = structuredClone(shape)\n\t\tconst newPoint = maybeSnapToGrid(new Vec(shape.x - delta.x, shape.y - delta.y), this.editor)\n\t\tnext.x = newPoint.x\n\t\tnext.y = newPoint.y\n\t\tnext.props.w = w\n\t\tnext.props.h = h\n\n\t\tif ('scale' in shape.props) {\n\t\t\t;(next as TLBaseBoxShape & { props: { scale: number } }).props.scale = scale\n\t\t}\n\n\t\tthis.editor.updateShape(next)\n\n\t\tthis.editor.setSelectedShapes([id])\n\n\t\tif (this.editor.getInstanceState().isToolLocked) {\n\t\t\tthis.parent.transition('idle')\n\t\t} else {\n\t\t\tthis.editor.setCurrentTool('select.idle')\n\t\t}\n\t}\n\n\tcancel() {\n\t\tthis.parent.transition('idle')\n\t}\n}\n\n/**\n * Checks if grid mode is enabled and snaps a point to the grid if so\n *\n * @public\n */\nexport function maybeSnapToGrid(point: Vec, editor: Editor): Vec {\n\tconst isGridMode = editor.getInstanceState().isGridMode\n\tconst gridSize = editor.getDocumentSettings().gridSize\n\tif (isGridMode) return point.clone().snapToGrid(gridSize)\n\treturn point.clone()\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["shape"]
7
7
  }
@@ -2,7 +2,12 @@ import { useValue } from "@tldraw/state-react";
2
2
  import { useEffect, useMemo } from "react";
3
3
  import { RIGHT_MOUSE_BUTTON } from "../constants.mjs";
4
4
  import { tlenv } from "../globals/environment.mjs";
5
- import { preventDefault, releasePointerCapture, setPointerCapture } from "../utils/dom.mjs";
5
+ import {
6
+ elementShouldCaptureKeys,
7
+ preventDefault,
8
+ releasePointerCapture,
9
+ setPointerCapture
10
+ } from "../utils/dom.mjs";
6
11
  import { getPointerInfo } from "../utils/getPointerInfo.mjs";
7
12
  import { useEditor } from "./useEditor.mjs";
8
13
  function useCanvasEvents() {
@@ -63,12 +68,12 @@ function useCanvasEvents() {
63
68
  if (editor.wasEventAlreadyHandled(e)) return;
64
69
  editor.markEventAsHandled(e);
65
70
  if (!(e.target instanceof HTMLElement)) return;
66
- const editingShapeId = editor.getEditingShape()?.id;
71
+ const editingShapeId = editor.getEditingShapeId();
67
72
  if (
68
73
  // if the target is not inside the editing shape
69
74
  !(editingShapeId && e.target.closest(`[data-shape-id="${editingShapeId}"]`)) && // and the target is not an clickable element
70
- e.target.tagName !== "A" && // or a TextArea.tsx ?
71
- e.target.tagName !== "TEXTAREA" && !e.target.isContentEditable
75
+ e.target.tagName !== "A" && // and the target is not an editable element
76
+ !elementShouldCaptureKeys(e.target, false)
72
77
  ) {
73
78
  preventDefault(e);
74
79
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useCanvasEvents.ts"],
4
- "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport React, { useEffect, useMemo } from 'react'\nimport { RIGHT_MOUSE_BUTTON } from '../constants'\nimport { tlenv } from '../globals/environment'\nimport { preventDefault, releasePointerCapture, setPointerCapture } from '../utils/dom'\nimport { getPointerInfo } from '../utils/getPointerInfo'\nimport { useEditor } from './useEditor'\n\nexport function useCanvasEvents() {\n\tconst editor = useEditor()\n\tconst ownerDocument = editor.getContainer().ownerDocument\n\tconst currentTool = useValue('current tool', () => editor.getCurrentTool(), [editor])\n\n\tconst events = useMemo(\n\t\tfunction canvasEvents() {\n\t\t\tfunction onPointerDown(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\n\t\t\t\tif (e.button === RIGHT_MOUSE_BUTTON) {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\tname: 'right_click',\n\t\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 5) return\n\n\t\t\t\tsetPointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_down',\n\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerUp(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return\n\n\t\t\t\treleasePointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_up',\n\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerEnter(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tif (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return\n\t\t\t\tconst canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'\n\t\t\t\teditor.updateInstanceState({ isHoveringCanvas: canHover ? true : null })\n\t\t\t}\n\n\t\t\tfunction onPointerLeave(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tif (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return\n\t\t\t\tconst canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'\n\t\t\t\teditor.updateInstanceState({ isHoveringCanvas: canHover ? false : null })\n\t\t\t}\n\n\t\t\tfunction onTouchStart(e: React.TouchEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tfunction onTouchEnd(e: React.TouchEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\t// check that e.target is an HTMLElement\n\t\t\t\tif (!(e.target instanceof HTMLElement)) return\n\n\t\t\t\tconst editingShapeId = editor.getEditingShape()?.id\n\t\t\t\tif (\n\t\t\t\t\t// if the target is not inside the editing shape\n\t\t\t\t\t!(editingShapeId && e.target.closest(`[data-shape-id=\"${editingShapeId}\"]`)) &&\n\t\t\t\t\t// and the target is not an clickable element\n\t\t\t\t\te.target.tagName !== 'A' &&\n\t\t\t\t\t// or a TextArea.tsx ?\n\t\t\t\t\te.target.tagName !== 'TEXTAREA' &&\n\t\t\t\t\t!e.target.isContentEditable\n\t\t\t\t) {\n\t\t\t\t\tpreventDefault(e)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onDragOver(e: React.DragEvent<Element>) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tasync function onDrop(e: React.DragEvent<Element>) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tpreventDefault(e)\n\t\t\t\te.stopPropagation()\n\n\t\t\t\tconst pagePoint = editor.screenToPage({ x: e.clientX, y: e.clientY })\n\n\t\t\t\t// Call the custom onDropOnCanvas callback if provided\n\t\t\t\tif (editor.options.experimental__onDropOnCanvas) {\n\t\t\t\t\tconst handled = editor.options.experimental__onDropOnCanvas({\n\t\t\t\t\t\tpoint: pagePoint,\n\t\t\t\t\t\tevent: e,\n\t\t\t\t\t})\n\t\t\t\t\tif (handled) return\n\t\t\t\t}\n\n\t\t\t\tif (e.dataTransfer?.files?.length) {\n\t\t\t\t\tconst files = Array.from(e.dataTransfer.files)\n\n\t\t\t\t\tawait editor.putExternalContent({\n\t\t\t\t\t\ttype: 'files',\n\t\t\t\t\t\tfiles,\n\t\t\t\t\t\tpoint: pagePoint,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst url = e.dataTransfer.getData('url')\n\t\t\t\tif (url) {\n\t\t\t\t\tawait editor.putExternalContent({\n\t\t\t\t\t\ttype: 'url',\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tpoint: pagePoint,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onClick(e: React.MouseEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\te.stopPropagation()\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tonPointerDown,\n\t\t\t\tonPointerUp,\n\t\t\t\tonPointerEnter,\n\t\t\t\tonPointerLeave,\n\t\t\t\tonDragOver,\n\t\t\t\tonDrop,\n\t\t\t\tonTouchStart,\n\t\t\t\tonTouchEnd,\n\t\t\t\tonClick,\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n\n\t// onPointerMove is special: where we're only interested in the other events when they're\n\t// happening _on_ the canvas (as opposed to outside of it, or on UI floating over it), we want\n\t// the pointer position to be up to date regardless of whether it's over the tldraw canvas or\n\t// not. So instead of returning a listener to be attached to the canvas, we directly attach a\n\t// listener to the whole document instead.\n\tuseEffect(() => {\n\t\tlet lastX: number, lastY: number\n\n\t\tfunction onPointerMove(e: PointerEvent) {\n\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\teditor.markEventAsHandled(e)\n\n\t\t\tif (e.clientX === lastX && e.clientY === lastY) return\n\t\t\tlastX = e.clientX\n\t\t\tlastY = e.clientY\n\n\t\t\t// For tools that benefit from a higher fidelity of events,\n\t\t\t// we dispatch the coalesced events.\n\t\t\t// N.B. Sometimes getCoalescedEvents isn't present on iOS, ugh.\n\t\t\t// Specifically, in local mode (non-https) mode, iOS does not `useCoalescedEvents`\n\t\t\t// so it appears like the ink is working locally, when really it's just that `useCoalescedEvents`\n\t\t\t// is disabled. The intent here is to have `useCoalescedEvents` disabled for iOS.\n\t\t\tconst events =\n\t\t\t\t!tlenv.isIos && currentTool.useCoalescedEvents && e.getCoalescedEvents\n\t\t\t\t\t? e.getCoalescedEvents()\n\t\t\t\t\t: [e]\n\n\t\t\tfor (const singleEvent of events) {\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_move',\n\t\t\t\t\t...getPointerInfo(editor, singleEvent),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\townerDocument.body.addEventListener('pointermove', onPointerMove)\n\t\treturn () => {\n\t\t\townerDocument.body.removeEventListener('pointermove', onPointerMove)\n\t\t}\n\t}, [editor, currentTool, ownerDocument])\n\n\treturn events\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport React, { useEffect, useMemo } from 'react'\nimport { RIGHT_MOUSE_BUTTON } from '../constants'\nimport { tlenv } from '../globals/environment'\nimport {\n\telementShouldCaptureKeys,\n\tpreventDefault,\n\treleasePointerCapture,\n\tsetPointerCapture,\n} from '../utils/dom'\nimport { getPointerInfo } from '../utils/getPointerInfo'\nimport { useEditor } from './useEditor'\n\nexport function useCanvasEvents() {\n\tconst editor = useEditor()\n\tconst ownerDocument = editor.getContainer().ownerDocument\n\tconst currentTool = useValue('current tool', () => editor.getCurrentTool(), [editor])\n\n\tconst events = useMemo(\n\t\tfunction canvasEvents() {\n\t\t\tfunction onPointerDown(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\n\t\t\t\tif (e.button === RIGHT_MOUSE_BUTTON) {\n\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\t\tname: 'right_click',\n\t\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 5) return\n\n\t\t\t\tsetPointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_down',\n\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerUp(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tif (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return\n\n\t\t\t\treleasePointerCapture(e.currentTarget, e)\n\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_up',\n\t\t\t\t\t...getPointerInfo(editor, e),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tfunction onPointerEnter(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tif (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return\n\t\t\t\tconst canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'\n\t\t\t\teditor.updateInstanceState({ isHoveringCanvas: canHover ? true : null })\n\t\t\t}\n\n\t\t\tfunction onPointerLeave(e: React.PointerEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tif (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return\n\t\t\t\tconst canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'\n\t\t\t\teditor.updateInstanceState({ isHoveringCanvas: canHover ? false : null })\n\t\t\t}\n\n\t\t\tfunction onTouchStart(e: React.TouchEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tfunction onTouchEnd(e: React.TouchEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\t// check that e.target is an HTMLElement\n\t\t\t\tif (!(e.target instanceof HTMLElement)) return\n\n\t\t\t\tconst editingShapeId = editor.getEditingShapeId()\n\t\t\t\tif (\n\t\t\t\t\t// if the target is not inside the editing shape\n\t\t\t\t\t!(editingShapeId && e.target.closest(`[data-shape-id=\"${editingShapeId}\"]`)) &&\n\t\t\t\t\t// and the target is not an clickable element\n\t\t\t\t\te.target.tagName !== 'A' &&\n\t\t\t\t\t// and the target is not an editable element\n\t\t\t\t\t!elementShouldCaptureKeys(e.target, false)\n\t\t\t\t) {\n\t\t\t\t\tpreventDefault(e)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onDragOver(e: React.DragEvent<Element>) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\n\t\t\tasync function onDrop(e: React.DragEvent<Element>) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\tpreventDefault(e)\n\t\t\t\te.stopPropagation()\n\n\t\t\t\tconst pagePoint = editor.screenToPage({ x: e.clientX, y: e.clientY })\n\n\t\t\t\t// Call the custom onDropOnCanvas callback if provided\n\t\t\t\tif (editor.options.experimental__onDropOnCanvas) {\n\t\t\t\t\tconst handled = editor.options.experimental__onDropOnCanvas({\n\t\t\t\t\t\tpoint: pagePoint,\n\t\t\t\t\t\tevent: e,\n\t\t\t\t\t})\n\t\t\t\t\tif (handled) return\n\t\t\t\t}\n\n\t\t\t\tif (e.dataTransfer?.files?.length) {\n\t\t\t\t\tconst files = Array.from(e.dataTransfer.files)\n\n\t\t\t\t\tawait editor.putExternalContent({\n\t\t\t\t\t\ttype: 'files',\n\t\t\t\t\t\tfiles,\n\t\t\t\t\t\tpoint: pagePoint,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst url = e.dataTransfer.getData('url')\n\t\t\t\tif (url) {\n\t\t\t\t\tawait editor.putExternalContent({\n\t\t\t\t\t\ttype: 'url',\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tpoint: pagePoint,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onClick(e: React.MouseEvent) {\n\t\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\t\te.stopPropagation()\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tonPointerDown,\n\t\t\t\tonPointerUp,\n\t\t\t\tonPointerEnter,\n\t\t\t\tonPointerLeave,\n\t\t\t\tonDragOver,\n\t\t\t\tonDrop,\n\t\t\t\tonTouchStart,\n\t\t\t\tonTouchEnd,\n\t\t\t\tonClick,\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n\n\t// onPointerMove is special: where we're only interested in the other events when they're\n\t// happening _on_ the canvas (as opposed to outside of it, or on UI floating over it), we want\n\t// the pointer position to be up to date regardless of whether it's over the tldraw canvas or\n\t// not. So instead of returning a listener to be attached to the canvas, we directly attach a\n\t// listener to the whole document instead.\n\tuseEffect(() => {\n\t\tlet lastX: number, lastY: number\n\n\t\tfunction onPointerMove(e: PointerEvent) {\n\t\t\tif (editor.wasEventAlreadyHandled(e)) return\n\t\t\teditor.markEventAsHandled(e)\n\n\t\t\tif (e.clientX === lastX && e.clientY === lastY) return\n\t\t\tlastX = e.clientX\n\t\t\tlastY = e.clientY\n\n\t\t\t// For tools that benefit from a higher fidelity of events,\n\t\t\t// we dispatch the coalesced events.\n\t\t\t// N.B. Sometimes getCoalescedEvents isn't present on iOS, ugh.\n\t\t\t// Specifically, in local mode (non-https) mode, iOS does not `useCoalescedEvents`\n\t\t\t// so it appears like the ink is working locally, when really it's just that `useCoalescedEvents`\n\t\t\t// is disabled. The intent here is to have `useCoalescedEvents` disabled for iOS.\n\t\t\tconst events =\n\t\t\t\t!tlenv.isIos && currentTool.useCoalescedEvents && e.getCoalescedEvents\n\t\t\t\t\t? e.getCoalescedEvents()\n\t\t\t\t\t: [e]\n\n\t\t\tfor (const singleEvent of events) {\n\t\t\t\teditor.dispatch({\n\t\t\t\t\ttype: 'pointer',\n\t\t\t\t\ttarget: 'canvas',\n\t\t\t\t\tname: 'pointer_move',\n\t\t\t\t\t...getPointerInfo(editor, singleEvent),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\townerDocument.body.addEventListener('pointermove', onPointerMove)\n\t\treturn () => {\n\t\t\townerDocument.body.removeEventListener('pointermove', onPointerMove)\n\t\t}\n\t}, [editor, currentTool, ownerDocument])\n\n\treturn events\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["events"]
7
7
  }
@@ -1,7 +1,6 @@
1
1
  import { useEffect } from "react";
2
- import { preventDefault } from "../utils/dom.mjs";
2
+ import { elementShouldCaptureKeys, preventDefault } from "../utils/dom.mjs";
3
3
  import { useEditor } from "./useEditor.mjs";
4
- const IGNORED_TAGS = ["textarea", "input"];
5
4
  function useFixSafariDoubleTapZoomPencilEvents(ref) {
6
5
  const editor = useEditor();
7
6
  useEffect(() => {
@@ -11,7 +10,7 @@ function useFixSafariDoubleTapZoomPencilEvents(ref) {
11
10
  if (e instanceof PointerEvent && e.pointerType === "pen") {
12
11
  editor.markEventAsHandled(e);
13
12
  const { target } = e;
14
- if (IGNORED_TAGS.includes(target.tagName?.toLocaleLowerCase()) || target.isContentEditable || editor.isIn("select.editing_shape")) {
13
+ if (elementShouldCaptureKeys(target instanceof Element ? target : null, false) || editor.isIn("select.editing_shape")) {
15
14
  return;
16
15
  }
17
16
  preventDefault(e);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts"],
4
- "sourcesContent": ["import { useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useEditor } from './useEditor'\n\nconst IGNORED_TAGS = ['textarea', 'input']\n\n/**\n * When double tapping with the pencil in iOS, it enables a little zoom window in the UI. We don't\n * want this for drawing operations and can disable it by setting 'disableDoubleTapZoom' in the main\n * editor.\n */\nexport function useFixSafariDoubleTapZoomPencilEvents(ref: React.RefObject<HTMLElement | null>) {\n\tconst editor = useEditor()\n\n\tuseEffect(() => {\n\t\tconst elm = ref.current\n\n\t\tif (!elm) return\n\n\t\tconst handleEvent = (e: PointerEvent | TouchEvent) => {\n\t\t\tif (e instanceof PointerEvent && e.pointerType === 'pen') {\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\tconst { target } = e\n\n\t\t\t\t// Allow events to propagate if the app is editing a shape, or if the event is occurring in a text area or input\n\t\t\t\tif (\n\t\t\t\t\tIGNORED_TAGS.includes((target as Element).tagName?.toLocaleLowerCase()) ||\n\t\t\t\t\t(target as HTMLElement).isContentEditable ||\n\t\t\t\t\teditor.isIn('select.editing_shape')\n\t\t\t\t) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\t\t}\n\n\t\telm.addEventListener('touchstart', handleEvent)\n\t\telm.addEventListener('touchend', handleEvent)\n\t\treturn () => {\n\t\t\telm.removeEventListener('touchstart', handleEvent)\n\t\t\telm.removeEventListener('touchend', handleEvent)\n\t\t}\n\t}, [editor, ref])\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AAE1B,MAAM,eAAe,CAAC,YAAY,OAAO;AAOlC,SAAS,sCAAsC,KAA0C;AAC/F,QAAM,SAAS,UAAU;AAEzB,YAAU,MAAM;AACf,UAAM,MAAM,IAAI;AAEhB,QAAI,CAAC,IAAK;AAEV,UAAM,cAAc,CAAC,MAAiC;AACrD,UAAI,aAAa,gBAAgB,EAAE,gBAAgB,OAAO;AACzD,eAAO,mBAAmB,CAAC;AAC3B,cAAM,EAAE,OAAO,IAAI;AAGnB,YACC,aAAa,SAAU,OAAmB,SAAS,kBAAkB,CAAC,KACrE,OAAuB,qBACxB,OAAO,KAAK,sBAAsB,GACjC;AACD;AAAA,QACD;AAEA,uBAAe,CAAC;AAAA,MACjB;AAAA,IACD;AAEA,QAAI,iBAAiB,cAAc,WAAW;AAC9C,QAAI,iBAAiB,YAAY,WAAW;AAC5C,WAAO,MAAM;AACZ,UAAI,oBAAoB,cAAc,WAAW;AACjD,UAAI,oBAAoB,YAAY,WAAW;AAAA,IAChD;AAAA,EACD,GAAG,CAAC,QAAQ,GAAG,CAAC;AACjB;",
4
+ "sourcesContent": ["import { useEffect } from 'react'\nimport { elementShouldCaptureKeys, preventDefault } from '../utils/dom'\nimport { useEditor } from './useEditor'\n\n/**\n * When double tapping with the pencil in iOS, it enables a little zoom window in the UI. We don't\n * want this for drawing operations and can disable it by setting 'disableDoubleTapZoom' in the main\n * editor.\n */\nexport function useFixSafariDoubleTapZoomPencilEvents(ref: React.RefObject<HTMLElement | null>) {\n\tconst editor = useEditor()\n\n\tuseEffect(() => {\n\t\tconst elm = ref.current\n\n\t\tif (!elm) return\n\n\t\tconst handleEvent = (e: PointerEvent | TouchEvent) => {\n\t\t\tif (e instanceof PointerEvent && e.pointerType === 'pen') {\n\t\t\t\teditor.markEventAsHandled(e)\n\t\t\t\tconst { target } = e\n\n\t\t\t\t// Allow events to propagate if the app is editing a shape, or if the event is occurring in a text area or input\n\t\t\t\tif (\n\t\t\t\t\telementShouldCaptureKeys(target instanceof Element ? target : null, false) ||\n\t\t\t\t\teditor.isIn('select.editing_shape')\n\t\t\t\t) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tpreventDefault(e)\n\t\t\t}\n\t\t}\n\n\t\telm.addEventListener('touchstart', handleEvent)\n\t\telm.addEventListener('touchend', handleEvent)\n\t\treturn () => {\n\t\t\telm.removeEventListener('touchstart', handleEvent)\n\t\t\telm.removeEventListener('touchend', handleEvent)\n\t\t}\n\t}, [editor, ref])\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,0BAA0B,sBAAsB;AACzD,SAAS,iBAAiB;AAOnB,SAAS,sCAAsC,KAA0C;AAC/F,QAAM,SAAS,UAAU;AAEzB,YAAU,MAAM;AACf,UAAM,MAAM,IAAI;AAEhB,QAAI,CAAC,IAAK;AAEV,UAAM,cAAc,CAAC,MAAiC;AACrD,UAAI,aAAa,gBAAgB,EAAE,gBAAgB,OAAO;AACzD,eAAO,mBAAmB,CAAC;AAC3B,cAAM,EAAE,OAAO,IAAI;AAGnB,YACC,yBAAyB,kBAAkB,UAAU,SAAS,MAAM,KAAK,KACzE,OAAO,KAAK,sBAAsB,GACjC;AACD;AAAA,QACD;AAEA,uBAAe,CAAC;AAAA,MACjB;AAAA,IACD;AAEA,QAAI,iBAAiB,cAAc,WAAW;AAC9C,QAAI,iBAAiB,YAAY,WAAW;AAC5C,WAAO,MAAM;AACZ,UAAI,oBAAoB,cAAc,WAAW;AACjD,UAAI,oBAAoB,YAAY,WAAW;AAAA,IAChD;AAAA,EACD,GAAG,CAAC,QAAQ,GAAG,CAAC;AACjB;",
6
6
  "names": []
7
7
  }
@@ -40,13 +40,17 @@ const setStyleProperty = (elm, property, value) => {
40
40
  if (!elm) return;
41
41
  elm.style.setProperty(property, String(value));
42
42
  };
43
- function activeElementShouldCaptureKeys(ignoreButtons = false) {
44
- const { activeElement } = document;
45
- const elements = ignoreButtons ? ["input", "textarea"] : ["input", "select", "button", "textarea"];
46
- return !!(activeElement && (activeElement.isContentEditable || elements.indexOf(activeElement.tagName.toLowerCase()) > -1 || activeElement.classList.contains("tlui-slider__thumb")));
43
+ function elementShouldCaptureKeys(el, includeButtonsAndMenus = true) {
44
+ if (!el) return false;
45
+ const tagName = el.tagName.toLowerCase();
46
+ return el.isContentEditable || tagName === "input" || tagName === "textarea" || includeButtonsAndMenus && tagName === "select" || includeButtonsAndMenus && tagName === "button" || el.classList.contains("tlui-slider__thumb");
47
+ }
48
+ function activeElementShouldCaptureKeys(includeButtonsAndMenus = true) {
49
+ return elementShouldCaptureKeys(document.activeElement, includeButtonsAndMenus);
47
50
  }
48
51
  export {
49
52
  activeElementShouldCaptureKeys,
53
+ elementShouldCaptureKeys,
50
54
  loopToHtmlElement,
51
55
  preventDefault,
52
56
  releasePointerCapture,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/dom.ts"],
4
- "sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elements do not support pointerCapture in\nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm.nodeType === Node.ELEMENT_NODE) return elm as HTMLElement\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Because if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortunately it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/**\n * Calls `event.stopPropagation()`.\n *\n * @deprecated Use {@link Editor.markEventAsHandled} instead, or manually call `event.stopPropagation()` if\n * that's what you really want.\n *\n * @public\n */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, String(value))\n}\n\n/** @internal */\nexport function activeElementShouldCaptureKeys(ignoreButtons = false) {\n\tconst { activeElement } = document\n\tconst elements = ignoreButtons ? ['input', 'textarea'] : ['input', 'select', 'button', 'textarea']\n\treturn !!(\n\t\tactiveElement &&\n\t\t((activeElement as HTMLElement).isContentEditable ||\n\t\t\telements.indexOf(activeElement.tagName.toLowerCase()) > -1 ||\n\t\t\tactiveElement.classList.contains('tlui-slider__thumb'))\n\t)\n}\n"],
5
- "mappings": "AAeA,SAAS,YAAY,oCAAoC;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,IAAI,aAAa,KAAK,aAAc,QAAO;AAC/C,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAUO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,OAAO,KAAK,CAAC;AAC9C;AAGO,SAAS,+BAA+B,gBAAgB,OAAO;AACrE,QAAM,EAAE,cAAc,IAAI;AAC1B,QAAM,WAAW,gBAAgB,CAAC,SAAS,UAAU,IAAI,CAAC,SAAS,UAAU,UAAU,UAAU;AACjG,SAAO,CAAC,EACP,kBACE,cAA8B,qBAC/B,SAAS,QAAQ,cAAc,QAAQ,YAAY,CAAC,IAAI,MACxD,cAAc,UAAU,SAAS,oBAAoB;AAExD;",
4
+ "sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elements do not support pointerCapture in\nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm.nodeType === Node.ELEMENT_NODE) return elm as HTMLElement\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Because if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortunately it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/**\n * Calls `event.stopPropagation()`.\n *\n * @deprecated Use {@link Editor.markEventAsHandled} instead, or manually call `event.stopPropagation()` if\n * that's what you really want.\n *\n * @public\n */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, String(value))\n}\n\n/** @internal */\nexport function elementShouldCaptureKeys(el: Element | null, includeButtonsAndMenus = true) {\n\tif (!el) return false\n\n\tconst tagName = el.tagName.toLowerCase()\n\treturn (\n\t\t(el as HTMLElement).isContentEditable ||\n\t\ttagName === 'input' ||\n\t\ttagName === 'textarea' ||\n\t\t(includeButtonsAndMenus && tagName === 'select') ||\n\t\t(includeButtonsAndMenus && tagName === 'button') ||\n\t\tel.classList.contains('tlui-slider__thumb')\n\t)\n}\n\n/** @internal */\nexport function activeElementShouldCaptureKeys(includeButtonsAndMenus = true) {\n\treturn elementShouldCaptureKeys(document.activeElement, includeButtonsAndMenus)\n}\n"],
5
+ "mappings": "AAeA,SAAS,YAAY,oCAAoC;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,IAAI,aAAa,KAAK,aAAc,QAAO;AAC/C,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,WAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,6BAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAUO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,OAAO,KAAK,CAAC;AAC9C;AAGO,SAAS,yBAAyB,IAAoB,yBAAyB,MAAM;AAC3F,MAAI,CAAC,GAAI,QAAO;AAEhB,QAAM,UAAU,GAAG,QAAQ,YAAY;AACvC,SACE,GAAmB,qBACpB,YAAY,WACZ,YAAY,cACX,0BAA0B,YAAY,YACtC,0BAA0B,YAAY,YACvC,GAAG,UAAU,SAAS,oBAAoB;AAE5C;AAGO,SAAS,+BAA+B,yBAAyB,MAAM;AAC7E,SAAO,yBAAyB,SAAS,eAAe,sBAAsB;AAC/E;",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "4.5.0-next.dad6df23ab1f";
1
+ const version = "4.5.0";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2026-03-04T10:05:37.505Z",
5
- patch: "2026-03-04T10:05:37.505Z"
4
+ minor: "2026-03-18T11:05:13.340Z",
5
+ patch: "2026-03-18T11:05:13.340Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.5.0-next.dad6df23ab1f'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-03-04T10:05:37.505Z',\n\tpatch: '2026-03-04T10:05:37.505Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.5.0'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-03-18T11:05:13.340Z',\n\tpatch: '2026-03-18T11:05:13.340Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "tldraw infinite canvas SDK (editor).",
4
- "version": "4.5.0-next.dad6df23ab1f",
4
+ "version": "4.5.0",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -49,12 +49,12 @@
49
49
  "@tiptap/core": "^3.12.1",
50
50
  "@tiptap/pm": "^3.12.1",
51
51
  "@tiptap/react": "^3.12.1",
52
- "@tldraw/state": "4.5.0-next.dad6df23ab1f",
53
- "@tldraw/state-react": "4.5.0-next.dad6df23ab1f",
54
- "@tldraw/store": "4.5.0-next.dad6df23ab1f",
55
- "@tldraw/tlschema": "4.5.0-next.dad6df23ab1f",
56
- "@tldraw/utils": "4.5.0-next.dad6df23ab1f",
57
- "@tldraw/validate": "4.5.0-next.dad6df23ab1f",
52
+ "@tldraw/state": "4.5.0",
53
+ "@tldraw/state-react": "4.5.0",
54
+ "@tldraw/store": "4.5.0",
55
+ "@tldraw/tlschema": "4.5.0",
56
+ "@tldraw/utils": "4.5.0",
57
+ "@tldraw/validate": "4.5.0",
58
58
  "@use-gesture/react": "^10.3.1",
59
59
  "classnames": "^2.5.1",
60
60
  "eventemitter3": "^4.0.7",
package/src/index.ts CHANGED
@@ -447,6 +447,7 @@ export {
447
447
  } from './lib/utils/deepLinks'
448
448
  export {
449
449
  activeElementShouldCaptureKeys,
450
+ elementShouldCaptureKeys,
450
451
  loopToHtmlElement,
451
452
  preventDefault,
452
453
  releasePointerCapture,
@@ -2772,6 +2772,15 @@ export class Editor extends EventEmitter<TLEventMap> {
2772
2772
  return this.getCamera().z
2773
2773
  }
2774
2774
 
2775
+ /**
2776
+ * Get the scale factor used when creating or resizing shapes in dynamic size mode.
2777
+ *
2778
+ * @public
2779
+ */
2780
+ @computed getResizeScaleFactor() {
2781
+ return this.user.getIsDynamicResizeMode() ? 1 / this.getZoomLevel() : 1
2782
+ }
2783
+
2775
2784
  private _debouncedZoomLevel = atom('debounced zoom level', 1)
2776
2785
 
2777
2786
  /**
@@ -107,10 +107,10 @@ export class Pointing extends StateNode {
107
107
  const delta = new Vec(w / 2, h / 2)
108
108
  const parentTransform = this.editor.getShapeParentTransform(shape)
109
109
  if (parentTransform) delta.rot(-parentTransform.rotation())
110
- let scale = 1
110
+ const scale = this.editor.getResizeScaleFactor()
111
111
 
112
- if (this.editor.user.getIsDynamicResizeMode()) {
113
- scale = 1 / this.editor.getZoomLevel()
112
+ // A scale factor of 1 means dynamic sizing is not affecting this shape.
113
+ if (scale !== 1) {
114
114
  w *= scale
115
115
  h *= scale
116
116
  delta.mul(scale)
@@ -2,7 +2,12 @@ import { useValue } from '@tldraw/state-react'
2
2
  import React, { useEffect, useMemo } from 'react'
3
3
  import { RIGHT_MOUSE_BUTTON } from '../constants'
4
4
  import { tlenv } from '../globals/environment'
5
- import { preventDefault, releasePointerCapture, setPointerCapture } from '../utils/dom'
5
+ import {
6
+ elementShouldCaptureKeys,
7
+ preventDefault,
8
+ releasePointerCapture,
9
+ setPointerCapture,
10
+ } from '../utils/dom'
6
11
  import { getPointerInfo } from '../utils/getPointerInfo'
7
12
  import { useEditor } from './useEditor'
8
13
 
@@ -78,15 +83,14 @@ export function useCanvasEvents() {
78
83
  // check that e.target is an HTMLElement
79
84
  if (!(e.target instanceof HTMLElement)) return
80
85
 
81
- const editingShapeId = editor.getEditingShape()?.id
86
+ const editingShapeId = editor.getEditingShapeId()
82
87
  if (
83
88
  // if the target is not inside the editing shape
84
89
  !(editingShapeId && e.target.closest(`[data-shape-id="${editingShapeId}"]`)) &&
85
90
  // and the target is not an clickable element
86
91
  e.target.tagName !== 'A' &&
87
- // or a TextArea.tsx ?
88
- e.target.tagName !== 'TEXTAREA' &&
89
- !e.target.isContentEditable
92
+ // and the target is not an editable element
93
+ !elementShouldCaptureKeys(e.target, false)
90
94
  ) {
91
95
  preventDefault(e)
92
96
  }
@@ -1,9 +1,7 @@
1
1
  import { useEffect } from 'react'
2
- import { preventDefault } from '../utils/dom'
2
+ import { elementShouldCaptureKeys, preventDefault } from '../utils/dom'
3
3
  import { useEditor } from './useEditor'
4
4
 
5
- const IGNORED_TAGS = ['textarea', 'input']
6
-
7
5
  /**
8
6
  * When double tapping with the pencil in iOS, it enables a little zoom window in the UI. We don't
9
7
  * want this for drawing operations and can disable it by setting 'disableDoubleTapZoom' in the main
@@ -24,8 +22,7 @@ export function useFixSafariDoubleTapZoomPencilEvents(ref: React.RefObject<HTMLE
24
22
 
25
23
  // Allow events to propagate if the app is editing a shape, or if the event is occurring in a text area or input
26
24
  if (
27
- IGNORED_TAGS.includes((target as Element).tagName?.toLocaleLowerCase()) ||
28
- (target as HTMLElement).isContentEditable ||
25
+ elementShouldCaptureKeys(target instanceof Element ? target : null, false) ||
29
26
  editor.isIn('select.editing_shape')
30
27
  ) {
31
28
  return
@@ -98,13 +98,21 @@ export const setStyleProperty = (
98
98
  }
99
99
 
100
100
  /** @internal */
101
- export function activeElementShouldCaptureKeys(ignoreButtons = false) {
102
- const { activeElement } = document
103
- const elements = ignoreButtons ? ['input', 'textarea'] : ['input', 'select', 'button', 'textarea']
104
- return !!(
105
- activeElement &&
106
- ((activeElement as HTMLElement).isContentEditable ||
107
- elements.indexOf(activeElement.tagName.toLowerCase()) > -1 ||
108
- activeElement.classList.contains('tlui-slider__thumb'))
101
+ export function elementShouldCaptureKeys(el: Element | null, includeButtonsAndMenus = true) {
102
+ if (!el) return false
103
+
104
+ const tagName = el.tagName.toLowerCase()
105
+ return (
106
+ (el as HTMLElement).isContentEditable ||
107
+ tagName === 'input' ||
108
+ tagName === 'textarea' ||
109
+ (includeButtonsAndMenus && tagName === 'select') ||
110
+ (includeButtonsAndMenus && tagName === 'button') ||
111
+ el.classList.contains('tlui-slider__thumb')
109
112
  )
110
113
  }
114
+
115
+ /** @internal */
116
+ export function activeElementShouldCaptureKeys(includeButtonsAndMenus = true) {
117
+ return elementShouldCaptureKeys(document.activeElement, includeButtonsAndMenus)
118
+ }
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '4.5.0-next.dad6df23ab1f'
4
+ export const version = '4.5.0'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2026-03-04T10:05:37.505Z',
8
- patch: '2026-03-04T10:05:37.505Z',
7
+ minor: '2026-03-18T11:05:13.340Z',
8
+ patch: '2026-03-18T11:05:13.340Z',
9
9
  }