@tldraw/editor 3.16.0-canary.940daaae9993 → 3.16.0-canary.98c4676431ae
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.
- package/dist-cjs/index.d.ts +42 -4
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/TldrawEditor.js +0 -2
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +11 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +35 -2
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +4 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +19 -16
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +5 -5
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
- package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useHandleEvents.js +6 -6
- package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useSelectionEvents.js +8 -8
- package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +24 -4
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +97 -90
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js +2 -3
- package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +42 -4
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/TldrawEditor.mjs +0 -2
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +11 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +35 -2
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +4 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +20 -22
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +6 -6
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +1 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useHandleEvents.mjs +6 -6
- package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useSelectionEvents.mjs +9 -14
- package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +24 -4
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +98 -91
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs +2 -3
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/lib/TldrawEditor.tsx +0 -2
- package/src/lib/components/default-components/DefaultCanvas.tsx +7 -1
- package/src/lib/editor/Editor.test.ts +90 -0
- package/src/lib/editor/Editor.ts +45 -2
- package/src/lib/editor/managers/FocusManager/FocusManager.ts +6 -2
- package/src/lib/hooks/useCanvasEvents.ts +20 -20
- package/src/lib/hooks/useDocumentEvents.ts +6 -6
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
- package/src/lib/hooks/useGestureEvents.ts +2 -2
- package/src/lib/hooks/useHandleEvents.ts +6 -6
- package/src/lib/hooks/useSelectionEvents.ts +9 -14
- package/src/lib/license/LicenseManager.test.ts +78 -2
- package/src/lib/license/LicenseManager.ts +31 -5
- package/src/lib/license/Watermark.tsx +100 -92
- package/src/lib/test/InFrontOfTheCanvas.test.tsx +187 -0
- package/src/lib/utils/dom.test.ts +103 -0
- package/src/lib/utils/dom.ts +8 -1
- package/src/lib/utils/getPointerInfo.ts +3 -2
- package/src/version.ts +3 -3
|
@@ -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 elemnets 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 React from 'react'\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 * Beacuase 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 unfortuantly 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
|
|
5
|
-
"mappings": "AAgBA,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;
|
|
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 elemnets 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 React from 'react'\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 * Beacuase 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 unfortuantly 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, value as string)\n}\n\n/** @internal */\nexport function activeElementShouldCaptureKeys(allowButtons = false) {\n\tconst { activeElement } = document\n\tconst elements = allowButtons ? ['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": "AAgBA,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,KAAe;AAChD;AAGO,SAAS,+BAA+B,eAAe,OAAO;AACpE,QAAM,EAAE,cAAc,IAAI;AAC1B,QAAM,WAAW,eAAe,CAAC,SAAS,UAAU,IAAI,CAAC,SAAS,UAAU,UAAU,UAAU;AAChG,SAAO,CAAC,EACP,kBACE,cAA8B,qBAC/B,SAAS,QAAQ,cAAc,QAAQ,YAAY,CAAC,IAAI,MACxD,cAAc,UAAU,SAAS,oBAAoB;AAExD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/getPointerInfo.ts"],
|
|
4
|
-
"sourcesContent": ["import { isAccelKey } from './keyboard'\n\n/** @public */\nexport function getPointerInfo(e: React.PointerEvent | PointerEvent) {\n\
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { Editor } from '../editor/Editor'\nimport { isAccelKey } from './keyboard'\n\n/** @public */\nexport function getPointerInfo(editor: Editor, e: React.PointerEvent | PointerEvent) {\n\teditor.markEventAsHandled(e)\n\n\treturn {\n\t\tpoint: {\n\t\t\tx: e.clientX,\n\t\t\ty: e.clientY,\n\t\t\tz: e.pressure,\n\t\t},\n\t\tshiftKey: e.shiftKey,\n\t\taltKey: e.altKey,\n\t\tctrlKey: e.metaKey || e.ctrlKey,\n\t\tmetaKey: e.metaKey,\n\t\taccelKey: isAccelKey(e),\n\t\tpointerId: e.pointerId,\n\t\tbutton: e.button,\n\t\tisPen: e.pointerType === 'pen',\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,kBAAkB;AAGpB,SAAS,eAAe,QAAgB,GAAsC;AACpF,SAAO,mBAAmB,CAAC;AAE3B,SAAO;AAAA,IACN,OAAO;AAAA,MACN,GAAG,EAAE;AAAA,MACL,GAAG,EAAE;AAAA,MACL,GAAG,EAAE;AAAA,IACN;AAAA,IACA,UAAU,EAAE;AAAA,IACZ,QAAQ,EAAE;AAAA,IACV,SAAS,EAAE,WAAW,EAAE;AAAA,IACxB,SAAS,EAAE;AAAA,IACX,UAAU,WAAW,CAAC;AAAA,IACtB,WAAW,EAAE;AAAA,IACb,QAAQ,EAAE;AAAA,IACV,OAAO,EAAE,gBAAgB;AAAA,EAC1B;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "3.16.0-canary.
|
|
1
|
+
const version = "3.16.0-canary.98c4676431ae";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-09-
|
|
5
|
-
patch: "2025-09-
|
|
4
|
+
minor: "2025-09-18T10:38:47.113Z",
|
|
5
|
+
patch: "2025-09-18T10:38:47.113Z"
|
|
6
6
|
};
|
|
7
7
|
export {
|
|
8
8
|
publishDates,
|
package/dist-esm/version.mjs.map
CHANGED
|
@@ -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 = '3.16.0-canary.
|
|
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 = '3.16.0-canary.98c4676431ae'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-18T10:38:47.113Z',\n\tpatch: '2025-09-18T10:38:47.113Z',\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": "3.16.0-canary.
|
|
4
|
+
"version": "3.16.0-canary.98c4676431ae",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@tiptap/core": "^2.9.1",
|
|
51
51
|
"@tiptap/pm": "^2.9.1",
|
|
52
52
|
"@tiptap/react": "^2.9.1",
|
|
53
|
-
"@tldraw/state": "3.16.0-canary.
|
|
54
|
-
"@tldraw/state-react": "3.16.0-canary.
|
|
55
|
-
"@tldraw/store": "3.16.0-canary.
|
|
56
|
-
"@tldraw/tlschema": "3.16.0-canary.
|
|
57
|
-
"@tldraw/utils": "3.16.0-canary.
|
|
58
|
-
"@tldraw/validate": "3.16.0-canary.
|
|
53
|
+
"@tldraw/state": "3.16.0-canary.98c4676431ae",
|
|
54
|
+
"@tldraw/state-react": "3.16.0-canary.98c4676431ae",
|
|
55
|
+
"@tldraw/store": "3.16.0-canary.98c4676431ae",
|
|
56
|
+
"@tldraw/tlschema": "3.16.0-canary.98c4676431ae",
|
|
57
|
+
"@tldraw/utils": "3.16.0-canary.98c4676431ae",
|
|
58
|
+
"@tldraw/validate": "3.16.0-canary.98c4676431ae",
|
|
59
59
|
"@types/core-js": "^2.5.8",
|
|
60
60
|
"@use-gesture/react": "^10.3.1",
|
|
61
61
|
"classnames": "^2.5.1",
|
package/src/lib/TldrawEditor.tsx
CHANGED
|
@@ -44,7 +44,6 @@ import { LicenseProvider } from './license/LicenseProvider'
|
|
|
44
44
|
import { Watermark } from './license/Watermark'
|
|
45
45
|
import { TldrawOptions } from './options'
|
|
46
46
|
import { TLDeepLinkOptions } from './utils/deepLinks'
|
|
47
|
-
import { stopEventPropagation } from './utils/dom'
|
|
48
47
|
import { TLTextOptions } from './utils/richText'
|
|
49
48
|
import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
|
|
50
49
|
|
|
@@ -275,7 +274,6 @@ export const TldrawEditor = memo(function TldrawEditor({
|
|
|
275
274
|
data-tldraw={version}
|
|
276
275
|
draggable={false}
|
|
277
276
|
className={classNames(`${TL_CONTAINER_CLASS} tl-theme__light`, className)}
|
|
278
|
-
onPointerDown={stopEventPropagation}
|
|
279
277
|
tabIndex={-1}
|
|
280
278
|
role="application"
|
|
281
279
|
aria-label={_options?.branding ?? 'tldraw'}
|
|
@@ -172,7 +172,13 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|
|
172
172
|
<LiveCollaborators />
|
|
173
173
|
</div>
|
|
174
174
|
</div>
|
|
175
|
-
<div
|
|
175
|
+
<div
|
|
176
|
+
className="tl-canvas__in-front"
|
|
177
|
+
onPointerDown={editor.markEventAsHandled}
|
|
178
|
+
onPointerUp={editor.markEventAsHandled}
|
|
179
|
+
onTouchStart={editor.markEventAsHandled}
|
|
180
|
+
onTouchEnd={editor.markEventAsHandled}
|
|
181
|
+
>
|
|
176
182
|
<InFrontOfTheCanvasWrapper />
|
|
177
183
|
</div>
|
|
178
184
|
<MovingCameraHitTestBlocker />
|
|
@@ -833,3 +833,93 @@ describe('selectAll', () => {
|
|
|
833
833
|
setSelectedShapesSpy.mockRestore()
|
|
834
834
|
})
|
|
835
835
|
})
|
|
836
|
+
|
|
837
|
+
describe('putExternalContent', () => {
|
|
838
|
+
let mockHandler: any
|
|
839
|
+
|
|
840
|
+
beforeEach(() => {
|
|
841
|
+
mockHandler = vi.fn()
|
|
842
|
+
editor.registerExternalContentHandler('text', mockHandler)
|
|
843
|
+
})
|
|
844
|
+
|
|
845
|
+
it('calls external content handler when not readonly', async () => {
|
|
846
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
847
|
+
|
|
848
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
849
|
+
await editor.putExternalContent(info)
|
|
850
|
+
|
|
851
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
it('does not call external content handler when readonly', async () => {
|
|
855
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
856
|
+
|
|
857
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
858
|
+
await editor.putExternalContent(info)
|
|
859
|
+
|
|
860
|
+
expect(mockHandler).not.toHaveBeenCalled()
|
|
861
|
+
})
|
|
862
|
+
|
|
863
|
+
it('calls external content handler when readonly but force is true', async () => {
|
|
864
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
865
|
+
|
|
866
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
867
|
+
await editor.putExternalContent(info, { force: true })
|
|
868
|
+
|
|
869
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
it('calls external content handler when force is false and not readonly', async () => {
|
|
873
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
874
|
+
|
|
875
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
876
|
+
await editor.putExternalContent(info, { force: false })
|
|
877
|
+
|
|
878
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
879
|
+
})
|
|
880
|
+
})
|
|
881
|
+
|
|
882
|
+
describe('replaceExternalContent', () => {
|
|
883
|
+
let mockHandler: any
|
|
884
|
+
|
|
885
|
+
beforeEach(() => {
|
|
886
|
+
mockHandler = vi.fn()
|
|
887
|
+
editor.registerExternalContentHandler('text', mockHandler)
|
|
888
|
+
})
|
|
889
|
+
|
|
890
|
+
it('calls external content handler when not readonly', async () => {
|
|
891
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
892
|
+
|
|
893
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
894
|
+
await editor.replaceExternalContent(info)
|
|
895
|
+
|
|
896
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
897
|
+
})
|
|
898
|
+
|
|
899
|
+
it('does not call external content handler when readonly', async () => {
|
|
900
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
901
|
+
|
|
902
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
903
|
+
await editor.replaceExternalContent(info)
|
|
904
|
+
|
|
905
|
+
expect(mockHandler).not.toHaveBeenCalled()
|
|
906
|
+
})
|
|
907
|
+
|
|
908
|
+
it('calls external content handler when readonly but force is true', async () => {
|
|
909
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
910
|
+
|
|
911
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
912
|
+
await editor.replaceExternalContent(info, { force: true })
|
|
913
|
+
|
|
914
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
915
|
+
})
|
|
916
|
+
|
|
917
|
+
it('calls external content handler when force is false and not readonly', async () => {
|
|
918
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
919
|
+
|
|
920
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
921
|
+
await editor.replaceExternalContent(info, { force: false })
|
|
922
|
+
|
|
923
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
924
|
+
})
|
|
925
|
+
})
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -343,6 +343,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
343
343
|
this.root = new NewRoot(this)
|
|
344
344
|
this.root.children = {}
|
|
345
345
|
|
|
346
|
+
this.markEventAsHandled = this.markEventAsHandled.bind(this)
|
|
347
|
+
|
|
346
348
|
const allShapeUtils = checkShapesAndAddCore(shapeUtils)
|
|
347
349
|
|
|
348
350
|
const _shapeUtils = {} as Record<string, ShapeUtil<any>>
|
|
@@ -8833,8 +8835,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8833
8835
|
* Handle external content, such as files, urls, embeds, or plain text which has been put into the app, for example by pasting external text or dropping external images onto canvas.
|
|
8834
8836
|
*
|
|
8835
8837
|
* @param info - Info about the external content.
|
|
8838
|
+
* @param opts - Options for handling external content, including force flag to bypass readonly checks.
|
|
8836
8839
|
*/
|
|
8837
|
-
async putExternalContent<E>(
|
|
8840
|
+
async putExternalContent<E>(
|
|
8841
|
+
info: TLExternalContent<E>,
|
|
8842
|
+
opts = {} as { force?: boolean }
|
|
8843
|
+
): Promise<void> {
|
|
8844
|
+
if (!opts.force && this.getIsReadonly()) return
|
|
8838
8845
|
return this.externalContentHandlers[info.type]?.(info as any)
|
|
8839
8846
|
}
|
|
8840
8847
|
|
|
@@ -8842,8 +8849,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8842
8849
|
* Handle replacing external content.
|
|
8843
8850
|
*
|
|
8844
8851
|
* @param info - Info about the external content.
|
|
8852
|
+
* @param opts - Options for handling external content, including force flag to bypass readonly checks.
|
|
8845
8853
|
*/
|
|
8846
|
-
async replaceExternalContent<E>(
|
|
8854
|
+
async replaceExternalContent<E>(
|
|
8855
|
+
info: TLExternalContent<E>,
|
|
8856
|
+
opts = {} as { force?: boolean }
|
|
8857
|
+
): Promise<void> {
|
|
8858
|
+
if (!opts.force && this.getIsReadonly()) return
|
|
8847
8859
|
return this.externalContentHandlers[info.type]?.(info as any)
|
|
8848
8860
|
}
|
|
8849
8861
|
|
|
@@ -10087,6 +10099,37 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10087
10099
|
/** @internal */
|
|
10088
10100
|
private performanceTrackerTimeout = -1 as any
|
|
10089
10101
|
|
|
10102
|
+
/** @internal */
|
|
10103
|
+
private handledEvents = new WeakSet<Event>()
|
|
10104
|
+
|
|
10105
|
+
/**
|
|
10106
|
+
* In tldraw, events are sometimes handled by multiple components. For example, the shapes might
|
|
10107
|
+
* have events, but the canvas handles events too. The way that the canvas handles events can
|
|
10108
|
+
* interfere with the with the shapes event handlers - for example, it calls `.preventDefault()`
|
|
10109
|
+
* on `pointerDown`, which also prevents `click` events from firing on the shapes.
|
|
10110
|
+
*
|
|
10111
|
+
* You can use `.stopPropagation()` to prevent the event from propagating to the rest of the
|
|
10112
|
+
* DOM, but that can impact non-tldraw event handlers set up elsewhere. By using
|
|
10113
|
+
* `markEventAsHandled`, you'll stop other parts of tldraw from handling the event without
|
|
10114
|
+
* impacting other, non-tldraw event handlers. See also {@link Editor.wasEventAlreadyHandled}.
|
|
10115
|
+
*
|
|
10116
|
+
* @public
|
|
10117
|
+
*/
|
|
10118
|
+
markEventAsHandled(e: Event | { nativeEvent: Event }) {
|
|
10119
|
+
const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e
|
|
10120
|
+
this.handledEvents.add(nativeEvent)
|
|
10121
|
+
}
|
|
10122
|
+
|
|
10123
|
+
/**
|
|
10124
|
+
* Checks if an event has already been handled. See {@link Editor.markEventAsHandled}.
|
|
10125
|
+
*
|
|
10126
|
+
* @public
|
|
10127
|
+
*/
|
|
10128
|
+
wasEventAlreadyHandled(e: Event | { nativeEvent: Event }) {
|
|
10129
|
+
const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e
|
|
10130
|
+
return this.handledEvents.has(nativeEvent)
|
|
10131
|
+
}
|
|
10132
|
+
|
|
10090
10133
|
/**
|
|
10091
10134
|
* Dispatch an event to the editor.
|
|
10092
10135
|
*
|
|
@@ -58,8 +58,12 @@ export class FocusManager {
|
|
|
58
58
|
|
|
59
59
|
private handleKeyDown(keyEvent: KeyboardEvent) {
|
|
60
60
|
const container = this.editor.getContainer()
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const activeEl = document.activeElement
|
|
62
|
+
// Edit mode should remove the focus ring, however if the active element's
|
|
63
|
+
// parent is the contextual toolbar, then allow it.
|
|
64
|
+
if (this.editor.isIn('select.editing_shape') && !activeEl?.closest('.tlui-contextual-toolbar'))
|
|
65
|
+
return
|
|
66
|
+
if (activeEl === container && this.editor.getSelectedShapeIds().length > 0) return
|
|
63
67
|
if (['Tab', 'ArrowUp', 'ArrowDown'].includes(keyEvent.key)) {
|
|
64
68
|
container.classList.remove('tl-container__no-focus-ring')
|
|
65
69
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { useValue } from '@tldraw/state-react'
|
|
2
2
|
import React, { useEffect, useMemo } from 'react'
|
|
3
3
|
import { RIGHT_MOUSE_BUTTON } from '../constants'
|
|
4
|
-
import {
|
|
5
|
-
preventDefault,
|
|
6
|
-
releasePointerCapture,
|
|
7
|
-
setPointerCapture,
|
|
8
|
-
stopEventPropagation,
|
|
9
|
-
} from '../utils/dom'
|
|
4
|
+
import { preventDefault, releasePointerCapture, setPointerCapture } from '../utils/dom'
|
|
10
5
|
import { getPointerInfo } from '../utils/getPointerInfo'
|
|
11
6
|
import { useEditor } from './useEditor'
|
|
12
7
|
|
|
@@ -17,14 +12,14 @@ export function useCanvasEvents() {
|
|
|
17
12
|
const events = useMemo(
|
|
18
13
|
function canvasEvents() {
|
|
19
14
|
function onPointerDown(e: React.PointerEvent) {
|
|
20
|
-
if ((e
|
|
15
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
21
16
|
|
|
22
17
|
if (e.button === RIGHT_MOUSE_BUTTON) {
|
|
23
18
|
editor.dispatch({
|
|
24
19
|
type: 'pointer',
|
|
25
20
|
target: 'canvas',
|
|
26
21
|
name: 'right_click',
|
|
27
|
-
...getPointerInfo(e),
|
|
22
|
+
...getPointerInfo(editor, e),
|
|
28
23
|
})
|
|
29
24
|
return
|
|
30
25
|
}
|
|
@@ -37,12 +32,12 @@ export function useCanvasEvents() {
|
|
|
37
32
|
type: 'pointer',
|
|
38
33
|
target: 'canvas',
|
|
39
34
|
name: 'pointer_down',
|
|
40
|
-
...getPointerInfo(e),
|
|
35
|
+
...getPointerInfo(editor, e),
|
|
41
36
|
})
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
function onPointerUp(e: React.PointerEvent) {
|
|
45
|
-
if ((e
|
|
40
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
46
41
|
if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
|
|
47
42
|
|
|
48
43
|
releasePointerCapture(e.currentTarget, e)
|
|
@@ -51,31 +46,33 @@ export function useCanvasEvents() {
|
|
|
51
46
|
type: 'pointer',
|
|
52
47
|
target: 'canvas',
|
|
53
48
|
name: 'pointer_up',
|
|
54
|
-
...getPointerInfo(e),
|
|
49
|
+
...getPointerInfo(editor, e),
|
|
55
50
|
})
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
function onPointerEnter(e: React.PointerEvent) {
|
|
59
|
-
if ((e
|
|
54
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
60
55
|
if (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return
|
|
61
56
|
const canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'
|
|
62
57
|
editor.updateInstanceState({ isHoveringCanvas: canHover ? true : null })
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
function onPointerLeave(e: React.PointerEvent) {
|
|
66
|
-
if ((e
|
|
61
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
67
62
|
if (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return
|
|
68
63
|
const canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'
|
|
69
64
|
editor.updateInstanceState({ isHoveringCanvas: canHover ? false : null })
|
|
70
65
|
}
|
|
71
66
|
|
|
72
67
|
function onTouchStart(e: React.TouchEvent) {
|
|
73
|
-
|
|
68
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
69
|
+
editor.markEventAsHandled(e)
|
|
74
70
|
preventDefault(e)
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
function onTouchEnd(e: React.TouchEvent) {
|
|
78
|
-
|
|
74
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
75
|
+
editor.markEventAsHandled(e)
|
|
79
76
|
// check that e.target is an HTMLElement
|
|
80
77
|
if (!(e.target instanceof HTMLElement)) return
|
|
81
78
|
|
|
@@ -94,12 +91,14 @@ export function useCanvasEvents() {
|
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
function onDragOver(e: React.DragEvent<Element>) {
|
|
94
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
97
95
|
preventDefault(e)
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
async function onDrop(e: React.DragEvent<Element>) {
|
|
99
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
101
100
|
preventDefault(e)
|
|
102
|
-
|
|
101
|
+
e.stopPropagation()
|
|
103
102
|
|
|
104
103
|
if (e.dataTransfer?.files?.length) {
|
|
105
104
|
const files = Array.from(e.dataTransfer.files)
|
|
@@ -124,7 +123,8 @@ export function useCanvasEvents() {
|
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
function onClick(e: React.MouseEvent) {
|
|
127
|
-
|
|
126
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
127
|
+
e.stopPropagation()
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return {
|
|
@@ -151,8 +151,8 @@ export function useCanvasEvents() {
|
|
|
151
151
|
let lastX: number, lastY: number
|
|
152
152
|
|
|
153
153
|
function onPointerMove(e: PointerEvent) {
|
|
154
|
-
if ((e
|
|
155
|
-
|
|
154
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
155
|
+
editor.markEventAsHandled(e)
|
|
156
156
|
|
|
157
157
|
if (e.clientX === lastX && e.clientY === lastY) return
|
|
158
158
|
lastX = e.clientX
|
|
@@ -168,7 +168,7 @@ export function useCanvasEvents() {
|
|
|
168
168
|
type: 'pointer',
|
|
169
169
|
target: 'canvas',
|
|
170
170
|
name: 'pointer_move',
|
|
171
|
-
...getPointerInfo(singleEvent),
|
|
171
|
+
...getPointerInfo(editor, singleEvent),
|
|
172
172
|
})
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -2,7 +2,7 @@ import { useValue } from '@tldraw/state-react'
|
|
|
2
2
|
import { useEffect } from 'react'
|
|
3
3
|
import { Editor } from '../editor/Editor'
|
|
4
4
|
import { TLKeyboardEventInfo } from '../editor/types/event-types'
|
|
5
|
-
import { activeElementShouldCaptureKeys, preventDefault
|
|
5
|
+
import { activeElementShouldCaptureKeys, preventDefault } from '../utils/dom'
|
|
6
6
|
import { isAccelKey } from '../utils/keyboard'
|
|
7
7
|
import { useContainer } from './useContainer'
|
|
8
8
|
import { useEditor } from './useEditor'
|
|
@@ -29,7 +29,7 @@ export function useDocumentEvents() {
|
|
|
29
29
|
// re-dispatched, which would lead to an infinite loop.
|
|
30
30
|
if ((e as any).isSpecialRedispatchedEvent) return
|
|
31
31
|
preventDefault(e)
|
|
32
|
-
|
|
32
|
+
e.stopPropagation()
|
|
33
33
|
const cvs = container.querySelector('.tl-canvas')
|
|
34
34
|
if (!cvs) return
|
|
35
35
|
const newEvent = new DragEvent(e.type, e)
|
|
@@ -103,8 +103,8 @@ export function useDocumentEvents() {
|
|
|
103
103
|
preventDefault(e)
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
if ((e
|
|
107
|
-
|
|
106
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
107
|
+
editor.markEventAsHandled(e)
|
|
108
108
|
const hasSelectedShapes = !!editor.getSelectedShapeIds().length
|
|
109
109
|
|
|
110
110
|
switch (e.key) {
|
|
@@ -211,8 +211,8 @@ export function useDocumentEvents() {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
const handleKeyUp = (e: KeyboardEvent) => {
|
|
214
|
-
if ((e
|
|
215
|
-
|
|
214
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
215
|
+
editor.markEventAsHandled(e)
|
|
216
216
|
|
|
217
217
|
if (areShortcutsDisabled(editor)) {
|
|
218
218
|
return
|
|
@@ -19,7 +19,7 @@ export function useFixSafariDoubleTapZoomPencilEvents(ref: React.RefObject<HTMLE
|
|
|
19
19
|
|
|
20
20
|
const handleEvent = (e: PointerEvent | TouchEvent) => {
|
|
21
21
|
if (e instanceof PointerEvent && e.pointerType === 'pen') {
|
|
22
|
-
|
|
22
|
+
editor.markEventAsHandled(e)
|
|
23
23
|
const { target } = e
|
|
24
24
|
|
|
25
25
|
// Allow events to propagate if the app is editing a shape, or if the event is occurring in a text area or input
|
|
@@ -3,7 +3,7 @@ import { createUseGesture, pinchAction, wheelAction } from '@use-gesture/react'
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { TLWheelEventInfo } from '../editor/types/event-types'
|
|
5
5
|
import { Vec } from '../primitives/Vec'
|
|
6
|
-
import { preventDefault
|
|
6
|
+
import { preventDefault } from '../utils/dom'
|
|
7
7
|
import { isAccelKey } from '../utils/keyboard'
|
|
8
8
|
import { normalizeWheel } from '../utils/normalizeWheel'
|
|
9
9
|
import { useEditor } from './useEditor'
|
|
@@ -113,7 +113,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
preventDefault(event)
|
|
116
|
-
|
|
116
|
+
event.stopPropagation()
|
|
117
117
|
const delta = normalizeWheel(event)
|
|
118
118
|
|
|
119
119
|
if (delta.x === 0 && delta.y === 0) return
|
|
@@ -16,7 +16,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
16
16
|
|
|
17
17
|
return React.useMemo(() => {
|
|
18
18
|
const onPointerDown = (e: React.PointerEvent) => {
|
|
19
|
-
if ((e
|
|
19
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
20
20
|
|
|
21
21
|
// Must set pointer capture on an HTML element!
|
|
22
22
|
const target = loopToHtmlElement(e.currentTarget)
|
|
@@ -32,7 +32,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
32
32
|
handle,
|
|
33
33
|
shape,
|
|
34
34
|
name: 'pointer_down',
|
|
35
|
-
...getPointerInfo(e),
|
|
35
|
+
...getPointerInfo(editor, e),
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -40,7 +40,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
40
40
|
let lastX: number, lastY: number
|
|
41
41
|
|
|
42
42
|
const onPointerMove = (e: React.PointerEvent) => {
|
|
43
|
-
if ((e
|
|
43
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
44
44
|
if (e.clientX === lastX && e.clientY === lastY) return
|
|
45
45
|
lastX = e.clientX
|
|
46
46
|
lastY = e.clientY
|
|
@@ -55,12 +55,12 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
55
55
|
handle,
|
|
56
56
|
shape,
|
|
57
57
|
name: 'pointer_move',
|
|
58
|
-
...getPointerInfo(e),
|
|
58
|
+
...getPointerInfo(editor, e),
|
|
59
59
|
})
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const onPointerUp = (e: React.PointerEvent) => {
|
|
63
|
-
if ((e
|
|
63
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
64
64
|
|
|
65
65
|
const target = loopToHtmlElement(e.currentTarget)
|
|
66
66
|
releasePointerCapture(target, e)
|
|
@@ -75,7 +75,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
75
75
|
handle,
|
|
76
76
|
shape,
|
|
77
77
|
name: 'pointer_up',
|
|
78
|
-
...getPointerInfo(e),
|
|
78
|
+
...getPointerInfo(editor, e),
|
|
79
79
|
})
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react'
|
|
2
2
|
import { RIGHT_MOUSE_BUTTON } from '../constants'
|
|
3
3
|
import { TLSelectionHandle } from '../editor/types/selection-types'
|
|
4
|
-
import {
|
|
5
|
-
loopToHtmlElement,
|
|
6
|
-
releasePointerCapture,
|
|
7
|
-
setPointerCapture,
|
|
8
|
-
stopEventPropagation,
|
|
9
|
-
} from '../utils/dom'
|
|
4
|
+
import { loopToHtmlElement, releasePointerCapture, setPointerCapture } from '../utils/dom'
|
|
10
5
|
import { getPointerInfo } from '../utils/getPointerInfo'
|
|
11
6
|
import { useEditor } from './useEditor'
|
|
12
7
|
|
|
@@ -17,7 +12,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
17
12
|
const events = useMemo(
|
|
18
13
|
function selectionEvents() {
|
|
19
14
|
const onPointerDown: React.PointerEventHandler = (e) => {
|
|
20
|
-
if ((e
|
|
15
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
21
16
|
|
|
22
17
|
if (e.button === RIGHT_MOUSE_BUTTON) {
|
|
23
18
|
editor.dispatch({
|
|
@@ -25,7 +20,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
25
20
|
target: 'selection',
|
|
26
21
|
handle,
|
|
27
22
|
name: 'right_click',
|
|
28
|
-
...getPointerInfo(e),
|
|
23
|
+
...getPointerInfo(editor, e),
|
|
29
24
|
})
|
|
30
25
|
return
|
|
31
26
|
}
|
|
@@ -52,16 +47,16 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
52
47
|
type: 'pointer',
|
|
53
48
|
target: 'selection',
|
|
54
49
|
handle,
|
|
55
|
-
...getPointerInfo(e),
|
|
50
|
+
...getPointerInfo(editor, e),
|
|
56
51
|
})
|
|
57
|
-
|
|
52
|
+
editor.markEventAsHandled(e)
|
|
58
53
|
}
|
|
59
54
|
|
|
60
55
|
// Track the last screen point
|
|
61
56
|
let lastX: number, lastY: number
|
|
62
57
|
|
|
63
58
|
function onPointerMove(e: React.PointerEvent) {
|
|
64
|
-
if ((e
|
|
59
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
65
60
|
if (e.button !== 0) return
|
|
66
61
|
if (e.clientX === lastX && e.clientY === lastY) return
|
|
67
62
|
lastX = e.clientX
|
|
@@ -72,12 +67,12 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
72
67
|
type: 'pointer',
|
|
73
68
|
target: 'selection',
|
|
74
69
|
handle,
|
|
75
|
-
...getPointerInfo(e),
|
|
70
|
+
...getPointerInfo(editor, e),
|
|
76
71
|
})
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
const onPointerUp: React.PointerEventHandler = (e) => {
|
|
80
|
-
if ((e
|
|
75
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
81
76
|
if (e.button !== 0) return
|
|
82
77
|
|
|
83
78
|
editor.dispatch({
|
|
@@ -85,7 +80,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
85
80
|
type: 'pointer',
|
|
86
81
|
target: 'selection',
|
|
87
82
|
handle,
|
|
88
|
-
...getPointerInfo(e),
|
|
83
|
+
...getPointerInfo(editor, e),
|
|
89
84
|
})
|
|
90
85
|
}
|
|
91
86
|
|