@tldraw/editor 5.1.0 → 5.2.0-canary.4a316fdfb2bb
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 +8 -10
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +4 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +3 -3
- package/dist-cjs/lib/components/default-components/DefaultLoadingScreen.js +2 -2
- package/dist-cjs/lib/components/default-components/DefaultLoadingScreen.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeErrorFallback.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeErrorFallback.js.map +3 -3
- package/dist-cjs/lib/components/default-components/DefaultSvgDefs.js +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSvgDefs.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +23 -2
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js +2 -2
- package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js +2 -2
- package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +8 -58
- package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/event-types.js +0 -2
- package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +3 -1
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +2 -2
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +5 -3
- package/dist-cjs/lib/utils/dom.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 +8 -10
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +4 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/DefaultLoadingScreen.mjs +2 -2
- package/dist-esm/lib/components/default-components/DefaultLoadingScreen.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeErrorFallback.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultShapeErrorFallback.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/DefaultSvgDefs.mjs +2 -2
- package/dist-esm/lib/components/default-components/DefaultSvgDefs.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +23 -2
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +2 -2
- package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +2 -2
- package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +8 -58
- package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/editor/types/event-types.mjs +0 -2
- package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +3 -1
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +2 -2
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +5 -3
- package/dist-esm/lib/utils/dom.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/components/default-components/DefaultErrorFallback.tsx +4 -1
- package/src/lib/components/default-components/DefaultLoadingScreen.tsx +1 -1
- package/src/lib/components/default-components/DefaultShapeErrorFallback.tsx +4 -3
- package/src/lib/components/default-components/DefaultSvgDefs.tsx +1 -1
- package/src/lib/editor/Editor.ts +39 -6
- package/src/lib/editor/derivations/bindingsIndex.ts +1 -1
- package/src/lib/editor/derivations/parentsToChildren.ts +1 -1
- package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +1 -1
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +54 -74
- package/src/lib/editor/managers/ClickManager/ClickManager.ts +15 -65
- package/src/lib/editor/tools/StateNode.ts +0 -2
- package/src/lib/editor/types/event-types.ts +2 -6
- package/src/lib/license/LicenseProvider.tsx +3 -1
- package/src/lib/primitives/utils.ts +1 -1
- package/src/lib/utils/dom.ts +5 -3
- package/src/version.ts +3 -3
|
@@ -36,11 +36,13 @@ function releasePointerCapture(element, event) {
|
|
|
36
36
|
console.warn("releasePointerCapture called on element:", element, event);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
function stopEventPropagation(e) {
|
|
40
|
+
return e.stopPropagation();
|
|
41
|
+
}
|
|
42
|
+
function setStyleProperty(elm, property, value) {
|
|
41
43
|
if (!elm) return;
|
|
42
44
|
elm.style.setProperty(property, String(value));
|
|
43
|
-
}
|
|
45
|
+
}
|
|
44
46
|
function elementShouldCaptureKeys(el, includeButtonsAndMenus = true) {
|
|
45
47
|
if (!el) return false;
|
|
46
48
|
const tagName = el.tagName.toLowerCase();
|
|
@@ -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\tif ('cancelable' in event && !event.cancelable) return\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
|
|
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,MAAI,gBAAgB,SAAS,CAAC,MAAM,WAAY;AAChD,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,
|
|
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\tif ('cancelable' in event && !event.cancelable) return\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 function stopEventPropagation(e: any) {\n\treturn e.stopPropagation()\n}\n\n/** @internal */\nexport function 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/**\n * Returns the global `document`. Use this instead of bare `document` to satisfy lint rules.\n *\n * When you have a DOM node or editor instance, prefer the scoped versions instead:\n * - `getOwnerDocument(node)` \u2013 the document that owns a specific DOM node\n * - `editor.getContainerDocument()` \u2013 the document where the editor is mounted\n *\n * @internal\n */\nexport function getGlobalDocument(): Document {\n\t// eslint-disable-next-line no-restricted-globals\n\tif (typeof document !== 'undefined') return document\n\treturn globalThis.document\n}\n\n/**\n * Returns the global `window`. Use this instead of bare `window` to satisfy lint rules.\n *\n * When you have a DOM node or editor instance, prefer the scoped versions instead:\n * - `getOwnerWindow(node)` \u2013 the window that owns a specific DOM node\n * - `editor.getContainerWindow()` \u2013 the window where the editor is mounted\n *\n * @internal\n */\nexport function getGlobalWindow(): Window & typeof globalThis {\n\tif (typeof window !== 'undefined') return window as Window & typeof globalThis\n\treturn globalThis as Window & typeof globalThis\n}\n\n/** @internal */\nexport function activeElementShouldCaptureKeys(includeButtonsAndMenus = true, doc?: Document) {\n\treturn elementShouldCaptureKeys(\n\t\t(doc ?? getGlobalDocument()).activeElement,\n\t\tincludeButtonsAndMenus\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,MAAI,gBAAgB,SAAS,CAAC,MAAM,WAAY;AAChD,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,SAAS,qBAAqB,GAAQ;AAC5C,SAAO,EAAE,gBAAgB;AAC1B;AAGO,SAAS,iBACf,KACA,UACA,OACC;AACD,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;AAWO,SAAS,oBAA8B;AAE7C,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,SAAO,WAAW;AACnB;AAWO,SAAS,kBAA8C;AAC7D,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO;AACR;AAGO,SAAS,+BAA+B,yBAAyB,MAAM,KAAgB;AAC7F,SAAO;AAAA,KACL,OAAO,kBAAkB,GAAG;AAAA,IAC7B;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "5.
|
|
1
|
+
const version = "5.2.0-canary.4a316fdfb2bb";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2026-05-06T16:28:18.473Z",
|
|
4
|
-
minor: "2026-06-
|
|
5
|
-
patch: "2026-06-
|
|
4
|
+
minor: "2026-06-05T11:56:57.032Z",
|
|
5
|
+
patch: "2026-06-05T11:56:57.032Z"
|
|
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 = '5.
|
|
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 = '5.2.0-canary.4a316fdfb2bb'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-06-05T11:56:57.032Z',\n\tpatch: '2026-06-05T11:56:57.032Z',\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": "5.
|
|
4
|
+
"version": "5.2.0-canary.4a316fdfb2bb",
|
|
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": "5.
|
|
53
|
-
"@tldraw/state-react": "5.
|
|
54
|
-
"@tldraw/store": "5.
|
|
55
|
-
"@tldraw/tlschema": "5.
|
|
56
|
-
"@tldraw/utils": "5.
|
|
57
|
-
"@tldraw/validate": "5.
|
|
52
|
+
"@tldraw/state": "5.2.0-canary.4a316fdfb2bb",
|
|
53
|
+
"@tldraw/state-react": "5.2.0-canary.4a316fdfb2bb",
|
|
54
|
+
"@tldraw/store": "5.2.0-canary.4a316fdfb2bb",
|
|
55
|
+
"@tldraw/tlschema": "5.2.0-canary.4a316fdfb2bb",
|
|
56
|
+
"@tldraw/utils": "5.2.0-canary.4a316fdfb2bb",
|
|
57
|
+
"@tldraw/validate": "5.2.0-canary.4a316fdfb2bb",
|
|
58
58
|
"classnames": "^2.5.1",
|
|
59
59
|
"eventemitter3": "^4.0.7",
|
|
60
60
|
"idb": "^7.1.1",
|
|
@@ -16,7 +16,10 @@ const BASE_ERROR_URL = 'https://github.com/tldraw/tldraw/issues/new'
|
|
|
16
16
|
export type TLErrorFallbackComponent = ComponentType<{ error: unknown; editor?: Editor }>
|
|
17
17
|
|
|
18
18
|
/** @public @react */
|
|
19
|
-
export const DefaultErrorFallback: TLErrorFallbackComponent = ({
|
|
19
|
+
export const DefaultErrorFallback: TLErrorFallbackComponent = function DefaultErrorFallback({
|
|
20
|
+
error,
|
|
21
|
+
editor,
|
|
22
|
+
}) {
|
|
20
23
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
21
24
|
const [shouldShowError, setShouldShowError] = useState(process.env.NODE_ENV === 'development')
|
|
22
25
|
const [didCopy, setDidCopy] = useState(false)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEditorComponents } from '../../hooks/EditorComponentsContext'
|
|
2
2
|
|
|
3
3
|
/** @public @react */
|
|
4
|
-
export
|
|
4
|
+
export function DefaultLoadingScreen() {
|
|
5
5
|
const { Spinner } = useEditorComponents()
|
|
6
6
|
return (
|
|
7
7
|
<div className="tl-loading" aria-busy="true" tabIndex={0}>
|
|
@@ -4,6 +4,7 @@ import { ComponentType } from 'react'
|
|
|
4
4
|
export type TLShapeErrorFallbackComponent = ComponentType<{ error: any }>
|
|
5
5
|
|
|
6
6
|
/** @internal */
|
|
7
|
-
export const DefaultShapeErrorFallback: TLShapeErrorFallbackComponent =
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export const DefaultShapeErrorFallback: TLShapeErrorFallbackComponent =
|
|
8
|
+
function DefaultShapeErrorFallback() {
|
|
9
|
+
return <div className="tl-shape-error-boundary" />
|
|
10
|
+
}
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -10768,6 +10768,16 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10768
10768
|
/** @internal */
|
|
10769
10769
|
private _selectedShapeIdsAtPointerDown: TLShapeId[] = []
|
|
10770
10770
|
|
|
10771
|
+
/**
|
|
10772
|
+
* Whether `_selectedShapeIdsAtPointerDown` holds a pre-gesture selection
|
|
10773
|
+
* captured by a `pointer_down` (the touch path) that a following pinch
|
|
10774
|
+
* should restore. False when no pointer_down preceded the pinch (the
|
|
10775
|
+
* Safari trackpad path uses gesture events), in which case `pinch_start`
|
|
10776
|
+
* captures the live selection instead.
|
|
10777
|
+
* @internal
|
|
10778
|
+
*/
|
|
10779
|
+
private _didCaptureSelectionAtPointerDown = false
|
|
10780
|
+
|
|
10771
10781
|
/** @internal */
|
|
10772
10782
|
private _longPressTimeout = -1 as any
|
|
10773
10783
|
|
|
@@ -10933,16 +10943,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10933
10943
|
if (inputs.getIsPinching()) return
|
|
10934
10944
|
|
|
10935
10945
|
if (!inputs.getIsEditing()) {
|
|
10936
|
-
//
|
|
10937
|
-
//
|
|
10938
|
-
//
|
|
10939
|
-
|
|
10946
|
+
// If a pointer_down already captured the pre-gesture selection,
|
|
10947
|
+
// keep it: on touch, the first finger's pointer_down can change
|
|
10948
|
+
// the selection before the second finger starts the pinch, and we
|
|
10949
|
+
// want to restore the selection from before that change. When no
|
|
10950
|
+
// pointer_down preceded the pinch (Safari delivers trackpad pinches
|
|
10951
|
+
// as gesture events), capture the live selection now.
|
|
10952
|
+
if (!this._didCaptureSelectionAtPointerDown) {
|
|
10953
|
+
this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds]
|
|
10954
|
+
}
|
|
10940
10955
|
|
|
10941
10956
|
this._didPinch = true
|
|
10942
10957
|
|
|
10943
10958
|
inputs.setIsPinching(true)
|
|
10944
10959
|
|
|
10945
10960
|
this.interrupt()
|
|
10961
|
+
|
|
10962
|
+
// If the first finger changed the selection, roll it back now rather
|
|
10963
|
+
// than waiting for the pinch to end, so the pre-gesture selection is
|
|
10964
|
+
// what's shown during the pinch.
|
|
10965
|
+
if (this._didCaptureSelectionAtPointerDown) {
|
|
10966
|
+
this.setSelectedShapes(this._selectedShapeIdsAtPointerDown)
|
|
10967
|
+
}
|
|
10946
10968
|
}
|
|
10947
10969
|
|
|
10948
10970
|
this.emit('event', info)
|
|
@@ -10994,6 +11016,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10994
11016
|
const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this
|
|
10995
11017
|
this.setSelectedShapes(this._selectedShapeIdsAtPointerDown)
|
|
10996
11018
|
this._selectedShapeIdsAtPointerDown = []
|
|
11019
|
+
this._didCaptureSelectionAtPointerDown = false
|
|
10997
11020
|
|
|
10998
11021
|
if (this._didPinch) {
|
|
10999
11022
|
this._didPinch = false
|
|
@@ -11122,8 +11145,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
11122
11145
|
}, this.options.longPressDurationMs)
|
|
11123
11146
|
}
|
|
11124
11147
|
|
|
11125
|
-
// Save the selected ids at
|
|
11126
|
-
|
|
11148
|
+
// Save the selected ids at the start of an interaction so a pinch can
|
|
11149
|
+
// restore the pre-gesture selection. Only capture on the first pointer:
|
|
11150
|
+
// on touch, the second finger's pointer_down arrives after the first
|
|
11151
|
+
// has already changed the selection, and we want the earlier snapshot.
|
|
11152
|
+
// Cleared on pointer_up / pinch_end.
|
|
11153
|
+
if (!this._didCaptureSelectionAtPointerDown) {
|
|
11154
|
+
this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds()
|
|
11155
|
+
this._didCaptureSelectionAtPointerDown = true
|
|
11156
|
+
}
|
|
11127
11157
|
|
|
11128
11158
|
// Firefox bug fix...
|
|
11129
11159
|
// If it's a left-mouse-click, we store the pointer id for later user
|
|
@@ -11243,6 +11273,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
11243
11273
|
if (this.inputs.getIsRightPointing() && !this.inputs.getIsPanning()) {
|
|
11244
11274
|
this.inputs.setIsRightPointing(false)
|
|
11245
11275
|
this._selectedShapeIdsAtPointerDown = []
|
|
11276
|
+
this._didCaptureSelectionAtPointerDown = false
|
|
11246
11277
|
break // fall through to state chart dispatch as right_click
|
|
11247
11278
|
}
|
|
11248
11279
|
|
|
@@ -11289,6 +11320,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
11289
11320
|
this.slideCamera({ speed: slideSpeed, direction: slideDirection })
|
|
11290
11321
|
}
|
|
11291
11322
|
this._selectedShapeIdsAtPointerDown = []
|
|
11323
|
+
this._didCaptureSelectionAtPointerDown = false
|
|
11292
11324
|
return this
|
|
11293
11325
|
}
|
|
11294
11326
|
}
|
|
@@ -11307,6 +11339,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
11307
11339
|
// Clear the stashed selection so the next pinch captures fresh state.
|
|
11308
11340
|
// This fixes Safari pinch zoom restoring outdated selections.
|
|
11309
11341
|
this._selectedShapeIdsAtPointerDown = []
|
|
11342
|
+
this._didCaptureSelectionAtPointerDown = false
|
|
11310
11343
|
|
|
11311
11344
|
break
|
|
11312
11345
|
}
|
|
@@ -29,7 +29,7 @@ function fromScratch(bindingsQuery: Computed<TLBinding[], unknown>) {
|
|
|
29
29
|
return shapesToBindings
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export
|
|
32
|
+
export function bindingsIndex(editor: Editor): Computed<TLBindingsIndex> {
|
|
33
33
|
const { store } = editor
|
|
34
34
|
const bindingsHistory = store.query.filterHistory('binding')
|
|
35
35
|
const bindingsQuery = store.query.records('binding')
|
|
@@ -22,7 +22,7 @@ function fromScratch(
|
|
|
22
22
|
return result
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export
|
|
25
|
+
export function parentsToChildren(store: TLStore) {
|
|
26
26
|
const shapeIdsQuery = store.query.ids<'shape'>('shape')
|
|
27
27
|
const shapeHistory = store.query.filterHistory('shape')
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ const isShapeInPage = (store: TLStore, pageId: TLPageId, shape: TLShape): boolea
|
|
|
33
33
|
* @param store - The tldraw store.
|
|
34
34
|
* @param getCurrentPageId - A function that returns the current page id.
|
|
35
35
|
*/
|
|
36
|
-
export
|
|
36
|
+
export function deriveShapeIdsInCurrentPage(store: TLStore, getCurrentPageId: () => TLPageId) {
|
|
37
37
|
const shapesIndex = store.query.ids('shape')
|
|
38
38
|
let lastPageId: null | TLPageId = null
|
|
39
39
|
function fromScratch() {
|
|
@@ -122,7 +122,7 @@ describe('ClickManager', () => {
|
|
|
122
122
|
expect(result.type).toBe('click')
|
|
123
123
|
expect(result.name).toBe('double_click')
|
|
124
124
|
expect(result.phase).toBe('down')
|
|
125
|
-
expect(clickManager.clickState).toBe('
|
|
125
|
+
expect(clickManager.clickState).toBe('pendingOverflow')
|
|
126
126
|
})
|
|
127
127
|
|
|
128
128
|
it('should generate double_click up event on pointer_up after double_click down', () => {
|
|
@@ -139,12 +139,34 @@ describe('ClickManager', () => {
|
|
|
139
139
|
expect(result.phase).toBe('up')
|
|
140
140
|
})
|
|
141
141
|
|
|
142
|
-
it('should dispatch double_click settle event after timeout in
|
|
142
|
+
it('should dispatch double_click settle-down event after timeout in pendingOverflow (pointer held)', () => {
|
|
143
143
|
const firstDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
144
144
|
const secondDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
145
145
|
|
|
146
146
|
clickManager.handlePointerEvent(firstDown)
|
|
147
147
|
clickManager.handlePointerEvent(secondDown)
|
|
148
|
+
// no pointer_up between or after — pointer is still down at settle time
|
|
149
|
+
|
|
150
|
+
vi.advanceTimersByTime(350)
|
|
151
|
+
|
|
152
|
+
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
153
|
+
expect.objectContaining({
|
|
154
|
+
type: 'click',
|
|
155
|
+
name: 'double_click',
|
|
156
|
+
phase: 'settle-down',
|
|
157
|
+
})
|
|
158
|
+
)
|
|
159
|
+
expect(clickManager.clickState).toBe('idle')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should dispatch double_click settle-up event after timeout in pendingOverflow (pointer released)', () => {
|
|
163
|
+
const down = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
164
|
+
const up = createPointerEvent('pointer_up', { x: 100, y: 100 })
|
|
165
|
+
|
|
166
|
+
clickManager.handlePointerEvent(down)
|
|
167
|
+
clickManager.handlePointerEvent(up)
|
|
168
|
+
clickManager.handlePointerEvent(down)
|
|
169
|
+
clickManager.handlePointerEvent(up)
|
|
148
170
|
|
|
149
171
|
vi.advanceTimersByTime(350)
|
|
150
172
|
|
|
@@ -152,124 +174,84 @@ describe('ClickManager', () => {
|
|
|
152
174
|
expect.objectContaining({
|
|
153
175
|
type: 'click',
|
|
154
176
|
name: 'double_click',
|
|
155
|
-
phase: 'settle',
|
|
177
|
+
phase: 'settle-up',
|
|
156
178
|
})
|
|
157
179
|
)
|
|
158
180
|
expect(clickManager.clickState).toBe('idle')
|
|
159
181
|
})
|
|
160
182
|
})
|
|
161
183
|
|
|
162
|
-
describe('
|
|
163
|
-
it('should
|
|
184
|
+
describe('overflow click handling', () => {
|
|
185
|
+
it('should enter overflow on the third pointer_down without emitting another click', () => {
|
|
164
186
|
const firstDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
165
187
|
const secondDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
166
188
|
const thirdDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
167
189
|
|
|
168
190
|
clickManager.handlePointerEvent(firstDown)
|
|
169
191
|
clickManager.handlePointerEvent(secondDown)
|
|
170
|
-
const result = clickManager.handlePointerEvent(thirdDown)
|
|
192
|
+
const result = clickManager.handlePointerEvent(thirdDown)
|
|
171
193
|
|
|
172
|
-
expect(result
|
|
173
|
-
expect(
|
|
174
|
-
expect(result.phase).toBe('down')
|
|
175
|
-
expect(clickManager.clickState).toBe('pendingQuadruple')
|
|
194
|
+
expect(result).toBe(thirdDown)
|
|
195
|
+
expect(clickManager.clickState).toBe('overflow')
|
|
176
196
|
})
|
|
177
197
|
|
|
178
|
-
it('should
|
|
198
|
+
it('should keep overflow active on further clicks', () => {
|
|
179
199
|
const pointerDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
180
200
|
|
|
181
201
|
clickManager.handlePointerEvent(pointerDown) // first
|
|
182
202
|
clickManager.handlePointerEvent(pointerDown) // second (double_click)
|
|
183
|
-
clickManager.handlePointerEvent(pointerDown) // third (
|
|
184
|
-
const result = clickManager.handlePointerEvent(pointerDown)
|
|
185
|
-
|
|
186
|
-
expect(result.type).toBe('click')
|
|
187
|
-
expect(result.name).toBe('quadruple_click')
|
|
188
|
-
expect(result.phase).toBe('down')
|
|
189
|
-
expect(clickManager.clickState).toBe('pendingOverflow')
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
it('should handle overflow state after quadruple click', () => {
|
|
193
|
-
const pointerDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
194
|
-
|
|
195
|
-
clickManager.handlePointerEvent(pointerDown) // first
|
|
196
|
-
clickManager.handlePointerEvent(pointerDown) // second
|
|
197
|
-
clickManager.handlePointerEvent(pointerDown) // third
|
|
198
|
-
clickManager.handlePointerEvent(pointerDown) // fourth
|
|
199
|
-
const result = clickManager.handlePointerEvent(pointerDown) // fifth
|
|
203
|
+
clickManager.handlePointerEvent(pointerDown) // third (overflow)
|
|
204
|
+
const result = clickManager.handlePointerEvent(pointerDown) // fourth
|
|
200
205
|
|
|
201
206
|
expect(result).toBe(pointerDown)
|
|
202
207
|
expect(clickManager.clickState).toBe('overflow')
|
|
203
208
|
})
|
|
204
209
|
|
|
205
|
-
it('should
|
|
206
|
-
const pointerDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
207
|
-
const pointerUp = createPointerEvent('pointer_up', { x: 100, y: 100 })
|
|
208
|
-
|
|
209
|
-
clickManager.handlePointerEvent(pointerDown) // first
|
|
210
|
-
clickManager.handlePointerEvent(pointerDown) // second
|
|
211
|
-
clickManager.handlePointerEvent(pointerDown) // third
|
|
212
|
-
const result = clickManager.handlePointerEvent(pointerUp) as TLClickEventInfo
|
|
213
|
-
|
|
214
|
-
expect(result.type).toBe('click')
|
|
215
|
-
expect(result.name).toBe('triple_click')
|
|
216
|
-
expect(result.phase).toBe('up')
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('should generate quadruple_click up event on pointer_up after quadruple_click down', () => {
|
|
210
|
+
it('should not emit double_click up events while in overflow', () => {
|
|
220
211
|
const pointerDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
221
212
|
const pointerUp = createPointerEvent('pointer_up', { x: 100, y: 100 })
|
|
222
213
|
|
|
223
214
|
clickManager.handlePointerEvent(pointerDown) // first
|
|
224
215
|
clickManager.handlePointerEvent(pointerDown) // second
|
|
225
216
|
clickManager.handlePointerEvent(pointerDown) // third
|
|
226
|
-
clickManager.handlePointerEvent(
|
|
227
|
-
const result = clickManager.handlePointerEvent(pointerUp) as TLClickEventInfo
|
|
217
|
+
const result = clickManager.handlePointerEvent(pointerUp)
|
|
228
218
|
|
|
229
|
-
expect(result
|
|
230
|
-
expect(
|
|
231
|
-
expect(result.phase).toBe('up')
|
|
219
|
+
expect(result).toBe(pointerUp)
|
|
220
|
+
expect(clickManager.clickState).toBe('overflow')
|
|
232
221
|
})
|
|
233
|
-
})
|
|
234
222
|
|
|
235
|
-
|
|
236
|
-
it('should dispatch triple_click settle event after timeout in pendingQuadruple', () => {
|
|
223
|
+
it('should return to idle after overflow timeout without dispatching a settle event', () => {
|
|
237
224
|
const pointerDown = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
238
225
|
|
|
239
226
|
clickManager.handlePointerEvent(pointerDown) // first
|
|
240
227
|
clickManager.handlePointerEvent(pointerDown) // second
|
|
241
|
-
clickManager.handlePointerEvent(pointerDown) // third
|
|
228
|
+
clickManager.handlePointerEvent(pointerDown) // third -> overflow
|
|
242
229
|
|
|
243
230
|
vi.advanceTimersByTime(350)
|
|
244
231
|
|
|
245
|
-
expect(editor.dispatch).
|
|
246
|
-
expect.objectContaining({
|
|
247
|
-
type: 'click',
|
|
248
|
-
name: 'triple_click',
|
|
249
|
-
phase: 'settle',
|
|
250
|
-
})
|
|
251
|
-
)
|
|
232
|
+
expect(editor.dispatch).not.toHaveBeenCalled()
|
|
252
233
|
expect(clickManager.clickState).toBe('idle')
|
|
253
234
|
})
|
|
235
|
+
})
|
|
254
236
|
|
|
255
|
-
|
|
256
|
-
|
|
237
|
+
describe('timeout behavior and settle events', () => {
|
|
238
|
+
it('should track press/release state across the pending window (settle-down then release → settle-up)', () => {
|
|
239
|
+
const down = createPointerEvent('pointer_down', { x: 100, y: 100 })
|
|
240
|
+
const up = createPointerEvent('pointer_up', { x: 100, y: 100 })
|
|
257
241
|
|
|
258
|
-
clickManager.handlePointerEvent(
|
|
259
|
-
clickManager.handlePointerEvent(
|
|
260
|
-
clickManager.handlePointerEvent(
|
|
261
|
-
clickManager.handlePointerEvent(
|
|
242
|
+
clickManager.handlePointerEvent(down)
|
|
243
|
+
clickManager.handlePointerEvent(up)
|
|
244
|
+
clickManager.handlePointerEvent(down) // second press — pointer is down...
|
|
245
|
+
clickManager.handlePointerEvent(up) // ...but released before timeout
|
|
262
246
|
|
|
263
247
|
vi.advanceTimersByTime(350)
|
|
264
248
|
|
|
265
249
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
266
250
|
expect.objectContaining({
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
phase: 'settle',
|
|
251
|
+
name: 'double_click',
|
|
252
|
+
phase: 'settle-up',
|
|
270
253
|
})
|
|
271
254
|
)
|
|
272
|
-
expect(clickManager.clickState).toBe('idle')
|
|
273
255
|
})
|
|
274
256
|
|
|
275
257
|
it('should use different timeout durations for different states', () => {
|
|
@@ -316,7 +298,7 @@ describe('ClickManager', () => {
|
|
|
316
298
|
|
|
317
299
|
expect(result.type).toBe('click')
|
|
318
300
|
expect(result.name).toBe('double_click')
|
|
319
|
-
expect(clickManager.clickState).toBe('
|
|
301
|
+
expect(clickManager.clickState).toBe('pendingOverflow')
|
|
320
302
|
})
|
|
321
303
|
})
|
|
322
304
|
|
|
@@ -396,7 +378,7 @@ describe('ClickManager', () => {
|
|
|
396
378
|
|
|
397
379
|
clickManager.handlePointerEvent(pointerDown)
|
|
398
380
|
clickManager.handlePointerEvent(pointerDown) // double click
|
|
399
|
-
expect(clickManager.clickState).toBe('
|
|
381
|
+
expect(clickManager.clickState).toBe('pendingOverflow')
|
|
400
382
|
|
|
401
383
|
clickManager.cancelDoubleClickTimeout()
|
|
402
384
|
|
|
@@ -416,9 +398,7 @@ describe('ClickManager', () => {
|
|
|
416
398
|
// Get to overflow state
|
|
417
399
|
clickManager.handlePointerEvent(pointerDown) // 1
|
|
418
400
|
clickManager.handlePointerEvent(pointerDown) // 2
|
|
419
|
-
clickManager.handlePointerEvent(pointerDown) // 3
|
|
420
|
-
clickManager.handlePointerEvent(pointerDown) // 4
|
|
421
|
-
clickManager.handlePointerEvent(pointerDown) // 5 -> overflow
|
|
401
|
+
clickManager.handlePointerEvent(pointerDown) // 3 -> overflow
|
|
422
402
|
|
|
423
403
|
expect(clickManager.clickState).toBe('overflow')
|
|
424
404
|
|
|
@@ -4,13 +4,7 @@ import type { Editor } from '../../Editor'
|
|
|
4
4
|
import { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
|
-
export type TLClickState =
|
|
8
|
-
| 'idle'
|
|
9
|
-
| 'pendingDouble'
|
|
10
|
-
| 'pendingTriple'
|
|
11
|
-
| 'pendingQuadruple'
|
|
12
|
-
| 'pendingOverflow'
|
|
13
|
-
| 'overflow'
|
|
7
|
+
export type TLClickState = 'idle' | 'pendingDouble' | 'pendingOverflow' | 'overflow'
|
|
14
8
|
|
|
15
9
|
const MAX_CLICK_DISTANCE = 40
|
|
16
10
|
|
|
@@ -26,6 +20,8 @@ export class ClickManager {
|
|
|
26
20
|
|
|
27
21
|
private _previousScreenPoint?: Vec
|
|
28
22
|
|
|
23
|
+
private _isPressingWhilePending = false
|
|
24
|
+
|
|
29
25
|
@bind
|
|
30
26
|
_getClickTimeout(state: TLClickState, id = uniqueId()) {
|
|
31
27
|
this._clickId = id
|
|
@@ -34,30 +30,12 @@ export class ClickManager {
|
|
|
34
30
|
() => {
|
|
35
31
|
if (this._clickState === state && this._clickId === id) {
|
|
36
32
|
switch (this._clickState) {
|
|
37
|
-
case 'pendingTriple': {
|
|
38
|
-
this.editor.dispatch({
|
|
39
|
-
...this.lastPointerInfo,
|
|
40
|
-
type: 'click',
|
|
41
|
-
name: 'double_click',
|
|
42
|
-
phase: 'settle',
|
|
43
|
-
})
|
|
44
|
-
break
|
|
45
|
-
}
|
|
46
|
-
case 'pendingQuadruple': {
|
|
47
|
-
this.editor.dispatch({
|
|
48
|
-
...this.lastPointerInfo,
|
|
49
|
-
type: 'click',
|
|
50
|
-
name: 'triple_click',
|
|
51
|
-
phase: 'settle',
|
|
52
|
-
})
|
|
53
|
-
break
|
|
54
|
-
}
|
|
55
33
|
case 'pendingOverflow': {
|
|
56
34
|
this.editor.dispatch({
|
|
57
35
|
...this.lastPointerInfo,
|
|
58
36
|
type: 'click',
|
|
59
|
-
name: '
|
|
60
|
-
phase: 'settle',
|
|
37
|
+
name: 'double_click',
|
|
38
|
+
phase: this._isPressingWhilePending ? 'settle-down' : 'settle-up',
|
|
61
39
|
})
|
|
62
40
|
break
|
|
63
41
|
}
|
|
@@ -100,6 +78,8 @@ export class ClickManager {
|
|
|
100
78
|
if (!this._clickState) return info
|
|
101
79
|
this._clickScreenPoint = Vec.From(info.point)
|
|
102
80
|
|
|
81
|
+
this._isPressingWhilePending = true
|
|
82
|
+
|
|
103
83
|
if (
|
|
104
84
|
this._previousScreenPoint &&
|
|
105
85
|
Vec.Dist2(this._previousScreenPoint, this._clickScreenPoint) > MAX_CLICK_DISTANCE ** 2
|
|
@@ -113,32 +93,12 @@ export class ClickManager {
|
|
|
113
93
|
|
|
114
94
|
switch (this._clickState) {
|
|
115
95
|
case 'pendingDouble': {
|
|
116
|
-
this._clickState = 'pendingTriple'
|
|
117
|
-
this._clickTimeout = this._getClickTimeout(this._clickState)
|
|
118
|
-
return {
|
|
119
|
-
...info,
|
|
120
|
-
type: 'click',
|
|
121
|
-
name: 'double_click',
|
|
122
|
-
phase: 'down',
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
case 'pendingTriple': {
|
|
126
|
-
this._clickState = 'pendingQuadruple'
|
|
127
|
-
this._clickTimeout = this._getClickTimeout(this._clickState)
|
|
128
|
-
return {
|
|
129
|
-
...info,
|
|
130
|
-
type: 'click',
|
|
131
|
-
name: 'triple_click',
|
|
132
|
-
phase: 'down',
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
case 'pendingQuadruple': {
|
|
136
96
|
this._clickState = 'pendingOverflow'
|
|
137
97
|
this._clickTimeout = this._getClickTimeout(this._clickState)
|
|
138
98
|
return {
|
|
139
99
|
...info,
|
|
140
100
|
type: 'click',
|
|
141
|
-
name: '
|
|
101
|
+
name: 'double_click',
|
|
142
102
|
phase: 'down',
|
|
143
103
|
}
|
|
144
104
|
}
|
|
@@ -159,30 +119,17 @@ export class ClickManager {
|
|
|
159
119
|
}
|
|
160
120
|
case 'pointer_up': {
|
|
161
121
|
if (!this._clickState) return info
|
|
122
|
+
|
|
162
123
|
this._clickScreenPoint = Vec.From(info.point)
|
|
163
124
|
|
|
125
|
+
this._isPressingWhilePending = false
|
|
126
|
+
|
|
164
127
|
switch (this._clickState) {
|
|
165
|
-
case 'pendingTriple': {
|
|
166
|
-
return {
|
|
167
|
-
...this.lastPointerInfo,
|
|
168
|
-
type: 'click',
|
|
169
|
-
name: 'double_click',
|
|
170
|
-
phase: 'up',
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
case 'pendingQuadruple': {
|
|
174
|
-
return {
|
|
175
|
-
...this.lastPointerInfo,
|
|
176
|
-
type: 'click',
|
|
177
|
-
name: 'triple_click',
|
|
178
|
-
phase: 'up',
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
128
|
case 'pendingOverflow': {
|
|
182
129
|
return {
|
|
183
130
|
...this.lastPointerInfo,
|
|
184
131
|
type: 'click',
|
|
185
|
-
name: '
|
|
132
|
+
name: 'double_click',
|
|
186
133
|
phase: 'up',
|
|
187
134
|
}
|
|
188
135
|
}
|
|
@@ -219,5 +166,8 @@ export class ClickManager {
|
|
|
219
166
|
cancelDoubleClickTimeout() {
|
|
220
167
|
this._clickTimeout = clearTimeout(this._clickTimeout)
|
|
221
168
|
this._clickState = 'idle'
|
|
169
|
+
// when a double click is cancelled, we are no longer pending any further
|
|
170
|
+
// clicks, so we set this to false even if the user is still pressing
|
|
171
|
+
this._isPressingWhilePending = false
|
|
222
172
|
}
|
|
223
173
|
}
|