@tldraw/editor 4.3.0-canary.ef37ae623ce8 → 4.3.0-canary.f3d38f27687a
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 +13 -0
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +20 -1
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/globals/environment.js +45 -9
- package/dist-cjs/lib/globals/environment.js.map +2 -2
- package/dist-cjs/lib/hooks/useCoarsePointer.js +14 -29
- package/dist-cjs/lib/hooks/useCoarsePointer.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 +13 -0
- package/dist-esm/index.mjs +3 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +20 -1
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/globals/environment.mjs +45 -9
- package/dist-esm/lib/globals/environment.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCoarsePointer.mjs +15 -30
- package/dist-esm/lib/hooks/useCoarsePointer.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/index.ts +1 -1
- package/src/lib/editor/Editor.ts +25 -1
- package/src/lib/globals/environment.ts +65 -10
- package/src/lib/hooks/useCoarsePointer.ts +16 -59
- package/src/version.ts +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { atom } from "@tldraw/state";
|
|
1
2
|
const tlenv = {
|
|
2
3
|
isSafari: false,
|
|
3
4
|
isIos: false,
|
|
@@ -8,16 +9,51 @@ const tlenv = {
|
|
|
8
9
|
isDarwin: false,
|
|
9
10
|
hasCanvasSupport: false
|
|
10
11
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
let isForcedFinePointer = false;
|
|
13
|
+
if (typeof window !== "undefined") {
|
|
14
|
+
if ("navigator" in window) {
|
|
15
|
+
tlenv.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
16
|
+
tlenv.isIos = !!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i);
|
|
17
|
+
tlenv.isChromeForIos = /crios.*safari/i.test(navigator.userAgent);
|
|
18
|
+
tlenv.isFirefox = /firefox/i.test(navigator.userAgent);
|
|
19
|
+
tlenv.isAndroid = /android/i.test(navigator.userAgent);
|
|
20
|
+
tlenv.isDarwin = window.navigator.userAgent.toLowerCase().indexOf("mac") > -1;
|
|
21
|
+
}
|
|
22
|
+
tlenv.hasCanvasSupport = "Promise" in window && "HTMLCanvasElement" in window;
|
|
23
|
+
isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos;
|
|
24
|
+
}
|
|
25
|
+
const tlenvReactive = atom("tlenvReactive", {
|
|
26
|
+
// Whether the user's device has a coarse pointer. This is dynamic on many systems, especially
|
|
27
|
+
// on touch-screen laptops, which will become "coarse" if the user touches the screen.
|
|
28
|
+
// See https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/pointer#coarse
|
|
29
|
+
isCoarsePointer: false
|
|
30
|
+
});
|
|
31
|
+
if (typeof window !== "undefined" && !isForcedFinePointer) {
|
|
32
|
+
const mql = window.matchMedia && window.matchMedia("(any-pointer: coarse)");
|
|
33
|
+
const isCurrentCoarsePointer = () => tlenvReactive.__unsafe__getWithoutCapture().isCoarsePointer;
|
|
34
|
+
if (mql) {
|
|
35
|
+
const updateIsCoarsePointer = () => {
|
|
36
|
+
const isCoarsePointer = mql.matches;
|
|
37
|
+
if (isCoarsePointer !== isCurrentCoarsePointer()) {
|
|
38
|
+
tlenvReactive.update((prev) => ({ ...prev, isCoarsePointer }));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
updateIsCoarsePointer();
|
|
42
|
+
mql.addEventListener("change", updateIsCoarsePointer);
|
|
43
|
+
}
|
|
44
|
+
window.addEventListener(
|
|
45
|
+
"pointerdown",
|
|
46
|
+
(e) => {
|
|
47
|
+
const isCoarseEvent = e.pointerType !== "mouse";
|
|
48
|
+
if (isCoarseEvent !== isCurrentCoarsePointer()) {
|
|
49
|
+
tlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarseEvent }));
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{ capture: true }
|
|
53
|
+
);
|
|
19
54
|
}
|
|
20
55
|
export {
|
|
21
|
-
tlenv
|
|
56
|
+
tlenv,
|
|
57
|
+
tlenvReactive
|
|
22
58
|
};
|
|
23
59
|
//# sourceMappingURL=environment.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/globals/environment.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * An object that contains information about the current device and environment.\n *\n * @public\n */\nconst tlenv = {\n\tisSafari: false,\n\tisIos: false,\n\tisChromeForIos: false,\n\tisFirefox: false,\n\tisAndroid: false,\n\tisWebview: false,\n\tisDarwin: false,\n\thasCanvasSupport: false,\n}\n\nif (typeof window !== 'undefined'
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { atom } from '@tldraw/state'\n\n/**\n * An object that contains information about the current device and environment.\n * This object is not reactive and will not update automatically when the environment changes,\n * so only include values that are fixed, such as the user's browser and operating system.\n *\n * @public\n */\nconst tlenv = {\n\tisSafari: false,\n\tisIos: false,\n\tisChromeForIos: false,\n\tisFirefox: false,\n\tisAndroid: false,\n\tisWebview: false,\n\tisDarwin: false,\n\thasCanvasSupport: false,\n}\n\nlet isForcedFinePointer = false\n\nif (typeof window !== 'undefined') {\n\tif ('navigator' in window) {\n\t\ttlenv.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\t\ttlenv.isIos = !!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i)\n\t\ttlenv.isChromeForIos = /crios.*safari/i.test(navigator.userAgent)\n\t\ttlenv.isFirefox = /firefox/i.test(navigator.userAgent)\n\t\ttlenv.isAndroid = /android/i.test(navigator.userAgent)\n\t\ttlenv.isDarwin = window.navigator.userAgent.toLowerCase().indexOf('mac') > -1\n\t}\n\ttlenv.hasCanvasSupport = 'Promise' in window && 'HTMLCanvasElement' in window\n\tisForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos\n}\n\n/**\n * An atom that contains information about the current device and environment.\n * This object is reactive and will update automatically when the environment changes.\n * Use it for values that may change over time, such as the pointer type.\n *\n * @public\n */\nconst tlenvReactive = atom('tlenvReactive', {\n\t// Whether the user's device has a coarse pointer. This is dynamic on many systems, especially\n\t// on touch-screen laptops, which will become \"coarse\" if the user touches the screen.\n\t// See https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/pointer#coarse\n\tisCoarsePointer: false,\n})\n\nif (typeof window !== 'undefined' && !isForcedFinePointer) {\n\tconst mql = window.matchMedia && window.matchMedia('(any-pointer: coarse)')\n\n\tconst isCurrentCoarsePointer = () => tlenvReactive.__unsafe__getWithoutCapture().isCoarsePointer\n\n\tif (mql) {\n\t\t// 1. Update the coarse pointer automatically when the media query changes\n\t\tconst updateIsCoarsePointer = () => {\n\t\t\tconst isCoarsePointer = mql.matches\n\t\t\tif (isCoarsePointer !== isCurrentCoarsePointer()) {\n\t\t\t\ttlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarsePointer }))\n\t\t\t}\n\t\t}\n\t\tupdateIsCoarsePointer()\n\t\tmql.addEventListener('change', updateIsCoarsePointer)\n\t}\n\n\t// 2. Also update the coarse pointer state when a pointer down event occurs. We need `capture: true`\n\t// here because the tldraw component itself stops propagation on pointer events it receives.\n\twindow.addEventListener(\n\t\t'pointerdown',\n\t\t(e: PointerEvent) => {\n\t\t\t// when the user interacts with a mouse, we assume they have a fine pointer.\n\t\t\t// otherwise, we assume they have a coarse pointer.\n\t\t\tconst isCoarseEvent = e.pointerType !== 'mouse'\n\t\t\tif (isCoarseEvent !== isCurrentCoarsePointer()) {\n\t\t\t\ttlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarseEvent }))\n\t\t\t}\n\t\t},\n\t\t{ capture: true }\n\t)\n}\n\nexport { tlenv, tlenvReactive }\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY;AASrB,MAAM,QAAQ;AAAA,EACb,UAAU;AAAA,EACV,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,kBAAkB;AACnB;AAEA,IAAI,sBAAsB;AAE1B,IAAI,OAAO,WAAW,aAAa;AAClC,MAAI,eAAe,QAAQ;AAC1B,UAAM,WAAW,iCAAiC,KAAK,UAAU,SAAS;AAC1E,UAAM,QAAQ,CAAC,CAAC,UAAU,UAAU,MAAM,OAAO,KAAK,CAAC,CAAC,UAAU,UAAU,MAAM,SAAS;AAC3F,UAAM,iBAAiB,iBAAiB,KAAK,UAAU,SAAS;AAChE,UAAM,YAAY,WAAW,KAAK,UAAU,SAAS;AACrD,UAAM,YAAY,WAAW,KAAK,UAAU,SAAS;AACrD,UAAM,WAAW,OAAO,UAAU,UAAU,YAAY,EAAE,QAAQ,KAAK,IAAI;AAAA,EAC5E;AACA,QAAM,mBAAmB,aAAa,UAAU,uBAAuB;AACvE,wBAAsB,MAAM,aAAa,CAAC,MAAM,aAAa,CAAC,MAAM;AACrE;AASA,MAAM,gBAAgB,KAAK,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAI3C,iBAAiB;AAClB,CAAC;AAED,IAAI,OAAO,WAAW,eAAe,CAAC,qBAAqB;AAC1D,QAAM,MAAM,OAAO,cAAc,OAAO,WAAW,uBAAuB;AAE1E,QAAM,yBAAyB,MAAM,cAAc,4BAA4B,EAAE;AAEjF,MAAI,KAAK;AAER,UAAM,wBAAwB,MAAM;AACnC,YAAM,kBAAkB,IAAI;AAC5B,UAAI,oBAAoB,uBAAuB,GAAG;AACjD,sBAAc,OAAO,CAAC,UAAU,EAAE,GAAG,MAAM,gBAAiC,EAAE;AAAA,MAC/E;AAAA,IACD;AACA,0BAAsB;AACtB,QAAI,iBAAiB,UAAU,qBAAqB;AAAA,EACrD;AAIA,SAAO;AAAA,IACN;AAAA,IACA,CAAC,MAAoB;AAGpB,YAAM,gBAAgB,EAAE,gBAAgB;AACxC,UAAI,kBAAkB,uBAAuB,GAAG;AAC/C,sBAAc,OAAO,CAAC,UAAU,EAAE,GAAG,MAAM,iBAAiB,cAAc,EAAE;AAAA,MAC7E;AAAA,IACD;AAAA,IACA,EAAE,SAAS,KAAK;AAAA,EACjB;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,36 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { unsafe__withoutCapture } from "@tldraw/state";
|
|
2
|
+
import { useReactor } from "@tldraw/state-react";
|
|
3
|
+
import { tlenvReactive } from "../globals/environment.mjs";
|
|
3
4
|
import { useEditor } from "./useEditor.mjs";
|
|
4
5
|
function useCoarsePointer() {
|
|
5
6
|
const editor = useEditor();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const next = isForcedFinePointer ? false : mql.matches;
|
|
19
|
-
if (isCoarse !== next) return;
|
|
20
|
-
isCoarse = next;
|
|
21
|
-
editor.updateInstanceState({ isCoarsePointer: next });
|
|
22
|
-
};
|
|
23
|
-
if (mql) {
|
|
24
|
-
mql.addEventListener("change", handleMediaQueryChange);
|
|
25
|
-
handleMediaQueryChange();
|
|
26
|
-
}
|
|
27
|
-
return () => {
|
|
28
|
-
window.removeEventListener("pointerdown", handlePointerDown, { capture: true });
|
|
29
|
-
if (mql) {
|
|
30
|
-
mql.removeEventListener("change", handleMediaQueryChange);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}, [editor]);
|
|
7
|
+
useReactor(
|
|
8
|
+
"coarse pointer change",
|
|
9
|
+
() => {
|
|
10
|
+
const isCoarsePointer = tlenvReactive.get().isCoarsePointer;
|
|
11
|
+
const isInstanceStateCoarsePointer = unsafe__withoutCapture(
|
|
12
|
+
() => editor.getInstanceState().isCoarsePointer
|
|
13
|
+
);
|
|
14
|
+
if (isCoarsePointer === isInstanceStateCoarsePointer) return;
|
|
15
|
+
editor.updateInstanceState({ isCoarsePointer });
|
|
16
|
+
},
|
|
17
|
+
[editor]
|
|
18
|
+
);
|
|
34
19
|
}
|
|
35
20
|
export {
|
|
36
21
|
useCoarsePointer
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/hooks/useCoarsePointer.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { unsafe__withoutCapture } from '@tldraw/state'\nimport { useReactor } from '@tldraw/state-react'\nimport { tlenvReactive } from '../globals/environment'\nimport { useEditor } from './useEditor'\n\n/** @internal */\nexport function useCoarsePointer() {\n\tconst editor = useEditor()\n\n\t// When the coarse pointer state changes, update the instance state\n\tuseReactor(\n\t\t'coarse pointer change',\n\t\t() => {\n\t\t\tconst isCoarsePointer = tlenvReactive.get().isCoarsePointer\n\t\t\tconst isInstanceStateCoarsePointer = unsafe__withoutCapture(\n\t\t\t\t() => editor.getInstanceState().isCoarsePointer\n\t\t\t)\n\t\t\tif (isCoarsePointer === isInstanceStateCoarsePointer) return\n\t\t\teditor.updateInstanceState({ isCoarsePointer: isCoarsePointer })\n\t\t},\n\t\t[editor]\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAGnB,SAAS,mBAAmB;AAClC,QAAM,SAAS,UAAU;AAGzB;AAAA,IACC;AAAA,IACA,MAAM;AACL,YAAM,kBAAkB,cAAc,IAAI,EAAE;AAC5C,YAAM,+BAA+B;AAAA,QACpC,MAAM,OAAO,iBAAiB,EAAE;AAAA,MACjC;AACA,UAAI,oBAAoB,6BAA8B;AACtD,aAAO,oBAAoB,EAAE,gBAAiC,CAAC;AAAA,IAChE;AAAA,IACA,CAAC,MAAM;AAAA,EACR;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "4.3.0-canary.
|
|
1
|
+
const version = "4.3.0-canary.f3d38f27687a";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2025-09-18T14:39:22.803Z",
|
|
4
|
-
minor: "2025-12-
|
|
5
|
-
patch: "2025-12-
|
|
4
|
+
minor: "2025-12-05T23:07:00.406Z",
|
|
5
|
+
patch: "2025-12-05T23:07:00.406Z"
|
|
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 = '4.3.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 = '4.3.0-canary.f3d38f27687a'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-05T23:07:00.406Z',\n\tpatch: '2025-12-05T23:07:00.406Z',\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.3.0-canary.
|
|
4
|
+
"version": "4.3.0-canary.f3d38f27687a",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@tiptap/core": "^3.6.2",
|
|
51
51
|
"@tiptap/pm": "^3.6.2",
|
|
52
52
|
"@tiptap/react": "^3.6.2",
|
|
53
|
-
"@tldraw/state": "4.3.0-canary.
|
|
54
|
-
"@tldraw/state-react": "4.3.0-canary.
|
|
55
|
-
"@tldraw/store": "4.3.0-canary.
|
|
56
|
-
"@tldraw/tlschema": "4.3.0-canary.
|
|
57
|
-
"@tldraw/utils": "4.3.0-canary.
|
|
58
|
-
"@tldraw/validate": "4.3.0-canary.
|
|
53
|
+
"@tldraw/state": "4.3.0-canary.f3d38f27687a",
|
|
54
|
+
"@tldraw/state-react": "4.3.0-canary.f3d38f27687a",
|
|
55
|
+
"@tldraw/store": "4.3.0-canary.f3d38f27687a",
|
|
56
|
+
"@tldraw/tlschema": "4.3.0-canary.f3d38f27687a",
|
|
57
|
+
"@tldraw/utils": "4.3.0-canary.f3d38f27687a",
|
|
58
|
+
"@tldraw/validate": "4.3.0-canary.f3d38f27687a",
|
|
59
59
|
"@types/core-js": "^2.5.8",
|
|
60
60
|
"@use-gesture/react": "^10.3.1",
|
|
61
61
|
"classnames": "^2.5.1",
|
package/src/index.ts
CHANGED
|
@@ -282,7 +282,7 @@ export {
|
|
|
282
282
|
type SvgExportDef,
|
|
283
283
|
} from './lib/editor/types/SvgExportContext'
|
|
284
284
|
export { getSvgAsImage } from './lib/exports/getSvgAsImage'
|
|
285
|
-
export { tlenv } from './lib/globals/environment'
|
|
285
|
+
export { tlenv, tlenvReactive } from './lib/globals/environment'
|
|
286
286
|
export { tlmenus } from './lib/globals/menus'
|
|
287
287
|
export { tltime } from './lib/globals/time'
|
|
288
288
|
export {
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -9149,6 +9149,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9149
9149
|
}
|
|
9150
9150
|
}
|
|
9151
9151
|
|
|
9152
|
+
if (point) {
|
|
9153
|
+
const shapesById = new Map<TLShapeId, TLShape>(shapes.map((shape) => [shape.id, shape]))
|
|
9154
|
+
const rootShapesFromContent = compact(rootShapeIds.map((id) => shapesById.get(id)))
|
|
9155
|
+
if (rootShapesFromContent.length > 0) {
|
|
9156
|
+
const targetParent = this.getShapeAtPoint(point, {
|
|
9157
|
+
hitInside: true,
|
|
9158
|
+
hitFrameInside: true,
|
|
9159
|
+
hitLocked: true,
|
|
9160
|
+
filter: (shape) => {
|
|
9161
|
+
const util = this.getShapeUtil(shape)
|
|
9162
|
+
if (!util.canReceiveNewChildrenOfType) return false
|
|
9163
|
+
return rootShapesFromContent.every((rootShape) =>
|
|
9164
|
+
util.canReceiveNewChildrenOfType!(shape, rootShape.type)
|
|
9165
|
+
)
|
|
9166
|
+
},
|
|
9167
|
+
})
|
|
9168
|
+
|
|
9169
|
+
// When pasting at a specific point (e.g. paste-at-cursor) prefer the
|
|
9170
|
+
// parent under the pointer so that we don't keep using the original
|
|
9171
|
+
// selection's parent (which can keep shapes clipped inside frames).
|
|
9172
|
+
pasteParentId = targetParent ? targetParent.id : currentPageId
|
|
9173
|
+
}
|
|
9174
|
+
}
|
|
9175
|
+
|
|
9152
9176
|
let isDuplicating = false
|
|
9153
9177
|
|
|
9154
9178
|
if (!isPageId(pasteParentId)) {
|
|
@@ -10255,8 +10279,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10255
10279
|
}
|
|
10256
10280
|
}
|
|
10257
10281
|
|
|
10258
|
-
this.emit('event', info)
|
|
10259
10282
|
this.root.handleEvent(info)
|
|
10283
|
+
this.emit('event', info)
|
|
10260
10284
|
return
|
|
10261
10285
|
}
|
|
10262
10286
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { atom } from '@tldraw/state'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* An object that contains information about the current device and environment.
|
|
5
|
+
* This object is not reactive and will not update automatically when the environment changes,
|
|
6
|
+
* so only include values that are fixed, such as the user's browser and operating system.
|
|
3
7
|
*
|
|
4
8
|
* @public
|
|
5
9
|
*/
|
|
@@ -14,15 +18,66 @@ const tlenv = {
|
|
|
14
18
|
hasCanvasSupport: false,
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
let isForcedFinePointer = false
|
|
22
|
+
|
|
23
|
+
if (typeof window !== 'undefined') {
|
|
24
|
+
if ('navigator' in window) {
|
|
25
|
+
tlenv.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
|
26
|
+
tlenv.isIos = !!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i)
|
|
27
|
+
tlenv.isChromeForIos = /crios.*safari/i.test(navigator.userAgent)
|
|
28
|
+
tlenv.isFirefox = /firefox/i.test(navigator.userAgent)
|
|
29
|
+
tlenv.isAndroid = /android/i.test(navigator.userAgent)
|
|
30
|
+
tlenv.isDarwin = window.navigator.userAgent.toLowerCase().indexOf('mac') > -1
|
|
31
|
+
}
|
|
32
|
+
tlenv.hasCanvasSupport = 'Promise' in window && 'HTMLCanvasElement' in window
|
|
33
|
+
isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* An atom that contains information about the current device and environment.
|
|
38
|
+
* This object is reactive and will update automatically when the environment changes.
|
|
39
|
+
* Use it for values that may change over time, such as the pointer type.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
const tlenvReactive = atom('tlenvReactive', {
|
|
44
|
+
// Whether the user's device has a coarse pointer. This is dynamic on many systems, especially
|
|
45
|
+
// on touch-screen laptops, which will become "coarse" if the user touches the screen.
|
|
46
|
+
// See https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/pointer#coarse
|
|
47
|
+
isCoarsePointer: false,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
if (typeof window !== 'undefined' && !isForcedFinePointer) {
|
|
51
|
+
const mql = window.matchMedia && window.matchMedia('(any-pointer: coarse)')
|
|
52
|
+
|
|
53
|
+
const isCurrentCoarsePointer = () => tlenvReactive.__unsafe__getWithoutCapture().isCoarsePointer
|
|
54
|
+
|
|
55
|
+
if (mql) {
|
|
56
|
+
// 1. Update the coarse pointer automatically when the media query changes
|
|
57
|
+
const updateIsCoarsePointer = () => {
|
|
58
|
+
const isCoarsePointer = mql.matches
|
|
59
|
+
if (isCoarsePointer !== isCurrentCoarsePointer()) {
|
|
60
|
+
tlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarsePointer }))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
updateIsCoarsePointer()
|
|
64
|
+
mql.addEventListener('change', updateIsCoarsePointer)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Also update the coarse pointer state when a pointer down event occurs. We need `capture: true`
|
|
68
|
+
// here because the tldraw component itself stops propagation on pointer events it receives.
|
|
69
|
+
window.addEventListener(
|
|
70
|
+
'pointerdown',
|
|
71
|
+
(e: PointerEvent) => {
|
|
72
|
+
// when the user interacts with a mouse, we assume they have a fine pointer.
|
|
73
|
+
// otherwise, we assume they have a coarse pointer.
|
|
74
|
+
const isCoarseEvent = e.pointerType !== 'mouse'
|
|
75
|
+
if (isCoarseEvent !== isCurrentCoarsePointer()) {
|
|
76
|
+
tlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarseEvent }))
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{ capture: true }
|
|
80
|
+
)
|
|
26
81
|
}
|
|
27
82
|
|
|
28
|
-
export { tlenv }
|
|
83
|
+
export { tlenv, tlenvReactive }
|
|
@@ -1,66 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { unsafe__withoutCapture } from '@tldraw/state'
|
|
2
|
+
import { useReactor } from '@tldraw/state-react'
|
|
3
|
+
import { tlenvReactive } from '../globals/environment'
|
|
3
4
|
import { useEditor } from './useEditor'
|
|
4
5
|
|
|
5
6
|
/** @internal */
|
|
6
7
|
export function useCoarsePointer() {
|
|
7
8
|
const editor = useEditor()
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
editor.updateInstanceState({ isCoarsePointer: isCoarseEvent })
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// we need `capture: true` here because the tldraw component itself stops propagation on
|
|
26
|
-
// pointer events it receives.
|
|
27
|
-
window.addEventListener('pointerdown', handlePointerDown, { capture: true })
|
|
28
|
-
|
|
29
|
-
// 2.
|
|
30
|
-
// We can also use the media query to detect / set the initial pointer type
|
|
31
|
-
// and update the state if the pointer type changes.
|
|
32
|
-
|
|
33
|
-
// We want the touch / mouse events to run even if the browser does not
|
|
34
|
-
// support matchMedia. We'll have to handle the media query changes
|
|
35
|
-
// conditionally in the code below.
|
|
36
|
-
const mql = window.matchMedia && window.matchMedia('(any-pointer: coarse)')
|
|
37
|
-
|
|
38
|
-
// This is a workaround for a Firefox bug where we don't correctly
|
|
39
|
-
// detect coarse VS fine pointer. For now, let's assume that you have a fine
|
|
40
|
-
// pointer if you're on Firefox on desktop.
|
|
41
|
-
const isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos
|
|
42
|
-
|
|
43
|
-
const handleMediaQueryChange = () => {
|
|
44
|
-
const next = isForcedFinePointer ? false : mql.matches // get the value from the media query
|
|
45
|
-
if (isCoarse !== next) return // bail if the value hasn't changed
|
|
46
|
-
isCoarse = next // update the local value
|
|
47
|
-
editor.updateInstanceState({ isCoarsePointer: next }) // update the value in state
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (mql) {
|
|
51
|
-
// set up the listener
|
|
52
|
-
mql.addEventListener('change', handleMediaQueryChange)
|
|
53
|
-
|
|
54
|
-
// and run the handler once to set the initial value
|
|
55
|
-
handleMediaQueryChange()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return () => {
|
|
59
|
-
window.removeEventListener('pointerdown', handlePointerDown, { capture: true })
|
|
60
|
-
|
|
61
|
-
if (mql) {
|
|
62
|
-
mql.removeEventListener('change', handleMediaQueryChange)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}, [editor])
|
|
10
|
+
// When the coarse pointer state changes, update the instance state
|
|
11
|
+
useReactor(
|
|
12
|
+
'coarse pointer change',
|
|
13
|
+
() => {
|
|
14
|
+
const isCoarsePointer = tlenvReactive.get().isCoarsePointer
|
|
15
|
+
const isInstanceStateCoarsePointer = unsafe__withoutCapture(
|
|
16
|
+
() => editor.getInstanceState().isCoarsePointer
|
|
17
|
+
)
|
|
18
|
+
if (isCoarsePointer === isInstanceStateCoarsePointer) return
|
|
19
|
+
editor.updateInstanceState({ isCoarsePointer: isCoarsePointer })
|
|
20
|
+
},
|
|
21
|
+
[editor]
|
|
22
|
+
)
|
|
66
23
|
}
|
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.3.0-canary.
|
|
4
|
+
export const version = '4.3.0-canary.f3d38f27687a'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2025-09-18T14:39:22.803Z',
|
|
7
|
-
minor: '2025-12-
|
|
8
|
-
patch: '2025-12-
|
|
7
|
+
minor: '2025-12-05T23:07:00.406Z',
|
|
8
|
+
patch: '2025-12-05T23:07:00.406Z',
|
|
9
9
|
}
|