@tldraw/editor 4.3.0-canary.c7096a59bf3b → 4.3.0-canary.d039f3a1ab8f
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 +54 -2
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +3 -3
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +43 -4
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
- package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.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/lib/hooks/useZoomCss.js +4 -8
- package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
- package/dist-cjs/lib/options.js +3 -1
- package/dist-cjs/lib/options.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 +54 -2
- package/dist-esm/index.mjs +3 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +3 -3
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +43 -4
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
- package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.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/lib/hooks/useZoomCss.mjs +4 -8
- package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +3 -1
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +8 -4
- package/package.json +7 -7
- package/src/index.ts +1 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +3 -3
- package/src/lib/editor/Editor.ts +74 -5
- package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
- package/src/lib/globals/environment.ts +65 -10
- package/src/lib/hooks/useCoarsePointer.ts +16 -59
- package/src/lib/hooks/useZoomCss.ts +3 -8
- package/src/lib/options.ts +13 -0
- package/src/version.ts +3 -3
|
@@ -4,7 +4,7 @@ import { useEditor } from "../../../hooks/useEditor.mjs";
|
|
|
4
4
|
import { getPerfectDashProps } from "../shared/getPerfectDashProps.mjs";
|
|
5
5
|
function DashedOutlineBox({ bounds, className }) {
|
|
6
6
|
const editor = useEditor();
|
|
7
|
-
const zoomLevel = useValue("zoom level", () => editor.
|
|
7
|
+
const zoomLevel = useValue("zoom level", () => editor.getEfficientZoomLevel(), [editor]);
|
|
8
8
|
return /* @__PURE__ */ jsx("g", { className, pointerEvents: "none", strokeLinecap: "round", strokeLinejoin: "round", children: bounds.sides.map((side, i) => {
|
|
9
9
|
const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(
|
|
10
10
|
side[0].dist(side[1]),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/shapes/group/DashedOutlineBox.tsx"],
|
|
4
|
-
"sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { useEditor } from '../../../hooks/useEditor'\nimport { Box } from '../../../primitives/Box'\nimport { getPerfectDashProps } from '../shared/getPerfectDashProps'\n\nexport function DashedOutlineBox({ bounds, className }: { bounds: Box; className: string }) {\n\tconst editor = useEditor()\n\n\tconst zoomLevel = useValue('zoom level', () => editor.
|
|
5
|
-
"mappings": "AAuBK;AAvBL,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAE1B,SAAS,2BAA2B;AAE7B,SAAS,iBAAiB,EAAE,QAAQ,UAAU,GAAuC;AAC3F,QAAM,SAAS,UAAU;AAEzB,QAAM,YAAY,SAAS,cAAc,MAAM,OAAO,
|
|
4
|
+
"sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { useEditor } from '../../../hooks/useEditor'\nimport { Box } from '../../../primitives/Box'\nimport { getPerfectDashProps } from '../shared/getPerfectDashProps'\n\nexport function DashedOutlineBox({ bounds, className }: { bounds: Box; className: string }) {\n\tconst editor = useEditor()\n\n\tconst zoomLevel = useValue('zoom level', () => editor.getEfficientZoomLevel(), [editor])\n\n\treturn (\n\t\t<g className={className} pointerEvents=\"none\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n\t\t\t{bounds.sides.map((side, i) => {\n\t\t\t\tconst { strokeDasharray, strokeDashoffset } = getPerfectDashProps(\n\t\t\t\t\tside[0].dist(side[1]),\n\t\t\t\t\t1 / zoomLevel,\n\t\t\t\t\t{\n\t\t\t\t\t\tstyle: 'dashed',\n\t\t\t\t\t\tlengthRatio: 4,\n\t\t\t\t\t}\n\t\t\t\t)\n\n\t\t\t\treturn (\n\t\t\t\t\t<line\n\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\tx1={side[0].x}\n\t\t\t\t\t\ty1={side[0].y}\n\t\t\t\t\t\tx2={side[1].x}\n\t\t\t\t\t\ty2={side[1].y}\n\t\t\t\t\t\tstrokeDasharray={strokeDasharray}\n\t\t\t\t\t\tstrokeDashoffset={strokeDashoffset}\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t})}\n\t\t</g>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAuBK;AAvBL,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAE1B,SAAS,2BAA2B;AAE7B,SAAS,iBAAiB,EAAE,QAAQ,UAAU,GAAuC;AAC3F,QAAM,SAAS,UAAU;AAEzB,QAAM,YAAY,SAAS,cAAc,MAAM,OAAO,sBAAsB,GAAG,CAAC,MAAM,CAAC;AAEvF,SACC,oBAAC,OAAE,WAAsB,eAAc,QAAO,eAAc,SAAQ,gBAAe,SACjF,iBAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC9B,UAAM,EAAE,iBAAiB,iBAAiB,IAAI;AAAA,MAC7C,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;AAAA,MACpB,IAAI;AAAA,MACJ;AAAA,QACC,OAAO;AAAA,QACP,aAAa;AAAA,MACd;AAAA,IACD;AAEA,WACC;AAAA,MAAC;AAAA;AAAA,QAEA,IAAI,KAAK,CAAC,EAAE;AAAA,QACZ,IAAI,KAAK,CAAC,EAAE;AAAA,QACZ,IAAI,KAAK,CAAC,EAAE;AAAA,QACZ,IAAI,KAAK,CAAC,EAAE;AAAA,QACZ;AAAA,QACA;AAAA;AAAA,MANK;AAAA,IAON;AAAA,EAEF,CAAC,GACF;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -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
|
}
|
|
@@ -9,14 +9,10 @@ function useZoomCss() {
|
|
|
9
9
|
React.useEffect(() => {
|
|
10
10
|
const setScale = (s) => container.style.setProperty("--tl-zoom", s.toString());
|
|
11
11
|
const setScaleDebounced = debounce(setScale, 100);
|
|
12
|
-
const scheduler = new EffectScheduler(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} else {
|
|
17
|
-
setScaleDebounced(editor.getZoomLevel());
|
|
18
|
-
}
|
|
19
|
-
});
|
|
12
|
+
const scheduler = new EffectScheduler(
|
|
13
|
+
"useZoomCss",
|
|
14
|
+
() => setScale(editor.getEfficientZoomLevel())
|
|
15
|
+
);
|
|
20
16
|
scheduler.attach();
|
|
21
17
|
scheduler.execute();
|
|
22
18
|
return () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/hooks/useZoomCss.ts"],
|
|
4
|
-
"sourcesContent": ["import { EffectScheduler } from '@tldraw/state'\nimport { debounce } from '@tldraw/utils'\nimport * as React from 'react'\nimport { useContainer } from './useContainer'\nimport { useEditor } from './useEditor'\n\nexport function useZoomCss() {\n\tconst editor = useEditor()\n\tconst container = useContainer()\n\n\tReact.useEffect(() => {\n\t\tconst setScale = (s: number) => container.style.setProperty('--tl-zoom', s.toString())\n\t\tconst setScaleDebounced = debounce(setScale, 100)\n\n\t\tconst scheduler = new EffectScheduler('useZoomCss', ()
|
|
5
|
-
"mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAEnB,SAAS,aAAa;AAC5B,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,aAAa;AAE/B,QAAM,UAAU,MAAM;AACrB,UAAM,WAAW,CAAC,MAAc,UAAU,MAAM,YAAY,aAAa,EAAE,SAAS,CAAC;AACrF,UAAM,oBAAoB,SAAS,UAAU,GAAG;AAEhD,UAAM,YAAY,IAAI
|
|
4
|
+
"sourcesContent": ["import { EffectScheduler } from '@tldraw/state'\nimport { debounce } from '@tldraw/utils'\nimport * as React from 'react'\nimport { useContainer } from './useContainer'\nimport { useEditor } from './useEditor'\n\nexport function useZoomCss() {\n\tconst editor = useEditor()\n\tconst container = useContainer()\n\n\tReact.useEffect(() => {\n\t\tconst setScale = (s: number) => container.style.setProperty('--tl-zoom', s.toString())\n\t\tconst setScaleDebounced = debounce(setScale, 100)\n\n\t\tconst scheduler = new EffectScheduler('useZoomCss', () =>\n\t\t\tsetScale(editor.getEfficientZoomLevel())\n\t\t)\n\n\t\tscheduler.attach()\n\t\tscheduler.execute()\n\n\t\treturn () => {\n\t\t\tscheduler.detach()\n\t\t\tsetScaleDebounced.cancel()\n\t\t}\n\t}, [editor, container])\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAEnB,SAAS,aAAa;AAC5B,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,aAAa;AAE/B,QAAM,UAAU,MAAM;AACrB,UAAM,WAAW,CAAC,MAAc,UAAU,MAAM,YAAY,aAAa,EAAE,SAAS,CAAC;AACrF,UAAM,oBAAoB,SAAS,UAAU,GAAG;AAEhD,UAAM,YAAY,IAAI;AAAA,MAAgB;AAAA,MAAc,MACnD,SAAS,OAAO,sBAAsB,CAAC;AAAA,IACxC;AAEA,cAAU,OAAO;AACjB,cAAU,QAAQ;AAElB,WAAO,MAAM;AACZ,gBAAU,OAAO;AACjB,wBAAkB,OAAO;AAAA,IAC1B;AAAA,EACD,GAAG,CAAC,QAAQ,SAAS,CAAC;AACvB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/lib/options.mjs
CHANGED
|
@@ -51,7 +51,9 @@ const defaultTldrawOptions = {
|
|
|
51
51
|
exportProvider: Fragment,
|
|
52
52
|
enableToolbarKeyboardShortcuts: true,
|
|
53
53
|
maxFontsToLoadBeforeRender: Infinity,
|
|
54
|
-
nonce: void 0
|
|
54
|
+
nonce: void 0,
|
|
55
|
+
debouncedZoom: true,
|
|
56
|
+
debouncedZoomThreshold: 500
|
|
55
57
|
};
|
|
56
58
|
export {
|
|
57
59
|
defaultTldrawOptions
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/options.ts"],
|
|
4
|
-
"sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly uiDragDistanceSquared: number\n\treadonly uiCoarseDragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\treadonly tooltipDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n\t/**\n\t * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.\n\t */\n\treadonly enableToolbarKeyboardShortcuts: boolean\n\t/**\n\t * The maximum number of fonts that will be loaded while blocking the main rendering of the\n\t * canvas. If there are more than this number of fonts needed, we'll just show the canvas right\n\t * away and let the fonts load in in the background.\n\t */\n\treadonly maxFontsToLoadBeforeRender: number\n\t/**\n\t * If you have a CSP policy that blocks inline styles, you can use this prop to provide a\n\t * nonce to use in the editor's styles.\n\t */\n\treadonly nonce: string | undefined\n\t/**\n\t * Branding name of the app, currently only used for adding aria-label for the application.\n\t */\n\treadonly branding?: string\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tuiDragDistanceSquared: 16, // 4 squared\n\t// it's really easy to accidentally drag from the toolbar on mobile, so we use a much larger\n\t// threshold than usual here to try and prevent accidental drags.\n\tuiCoarseDragDistanceSquared: 625, // 25 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttooltipDelayMs: 700,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n\tenableToolbarKeyboardShortcuts: true,\n\tmaxFontsToLoadBeforeRender: Infinity,\n\tnonce: undefined,\n} as const satisfies TldrawOptions\n"],
|
|
5
|
-
"mappings": "AAAA,SAAwB,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly uiDragDistanceSquared: number\n\treadonly uiCoarseDragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\treadonly tooltipDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n\t/**\n\t * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.\n\t */\n\treadonly enableToolbarKeyboardShortcuts: boolean\n\t/**\n\t * The maximum number of fonts that will be loaded while blocking the main rendering of the\n\t * canvas. If there are more than this number of fonts needed, we'll just show the canvas right\n\t * away and let the fonts load in in the background.\n\t */\n\treadonly maxFontsToLoadBeforeRender: number\n\t/**\n\t * If you have a CSP policy that blocks inline styles, you can use this prop to provide a\n\t * nonce to use in the editor's styles.\n\t */\n\treadonly nonce: string | undefined\n\t/**\n\t * Branding name of the app, currently only used for adding aria-label for the application.\n\t */\n\treadonly branding?: string\n\t/**\n\t * Whether to use debounced zoom level for certain rendering optimizations. When true,\n\t * `editor.getDebouncedZoomLevel()` returns a cached zoom value while the camera is moving,\n\t * reducing re-renders. When false, it always returns the current zoom level.\n\t */\n\treadonly debouncedZoom: boolean\n\t/**\n\t * The number of shapes that must be on the page for the debounced zoom level to be used.\n\t * Defaults to 300 shapes.\n\t */\n\treadonly debouncedZoomThreshold: number\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tuiDragDistanceSquared: 16, // 4 squared\n\t// it's really easy to accidentally drag from the toolbar on mobile, so we use a much larger\n\t// threshold than usual here to try and prevent accidental drags.\n\tuiCoarseDragDistanceSquared: 625, // 25 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttooltipDelayMs: 700,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n\tenableToolbarKeyboardShortcuts: true,\n\tmaxFontsToLoadBeforeRender: Infinity,\n\tnonce: undefined,\n\tdebouncedZoom: true,\n\tdebouncedZoomThreshold: 500,\n} as const satisfies TldrawOptions\n"],
|
|
5
|
+
"mappings": "AAAA,SAAwB,gBAAgB;AAuGjC,MAAM,uBAAuB;AAAA,EACnC,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,uBAAuB;AAAA;AAAA;AAAA;AAAA,EAGvB,6BAA6B;AAAA;AAAA,EAC7B,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,WAAW;AAAA,IACV,EAAE,KAAK,IAAI,KAAK,MAAM,MAAM,GAAG;AAAA,IAC/B,EAAE,KAAK,MAAM,KAAK,OAAO,MAAM,GAAG;AAAA,IAClC,EAAE,KAAK,MAAM,KAAK,GAAG,MAAM,EAAE;AAAA,IAC7B,EAAE,KAAK,KAAK,KAAK,KAAK,MAAM,EAAE;AAAA,EAC/B;AAAA,EACA,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,iCAAiC;AAAA,EACjC,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,gBAAgB;AAAA,EAChB,gCAAgC;AAAA,EAChC,4BAA4B;AAAA,EAC5B,OAAO;AAAA,EACP,eAAe;AAAA,EACf,wBAAwB;AACzB;",
|
|
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.d039f3a1ab8f";
|
|
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-08T08:33:39.283Z",
|
|
5
|
+
patch: "2025-12-08T08:33:39.283Z"
|
|
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.d039f3a1ab8f'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-08T08:33:39.283Z',\n\tpatch: '2025-12-08T08:33:39.283Z',\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/editor.css
CHANGED
|
@@ -607,7 +607,6 @@ input,
|
|
|
607
607
|
pointer-events: all;
|
|
608
608
|
white-space: pre-wrap;
|
|
609
609
|
overflow-wrap: break-word;
|
|
610
|
-
text-shadow: var(--tl-text-outline);
|
|
611
610
|
}
|
|
612
611
|
|
|
613
612
|
.tl-text-wrapper[data-font='draw'] {
|
|
@@ -770,7 +769,6 @@ input,
|
|
|
770
769
|
justify-content: center;
|
|
771
770
|
align-items: center;
|
|
772
771
|
color: var(--tl-color-text);
|
|
773
|
-
text-shadow: var(--tl-text-outline);
|
|
774
772
|
line-height: inherit;
|
|
775
773
|
position: absolute;
|
|
776
774
|
inset: 0px;
|
|
@@ -970,6 +968,14 @@ input,
|
|
|
970
968
|
display: block;
|
|
971
969
|
}
|
|
972
970
|
|
|
971
|
+
.tl-text__outline {
|
|
972
|
+
text-shadow: var(--tl-text-outline);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.tl-text__no-outline {
|
|
976
|
+
text-shadow: none;
|
|
977
|
+
}
|
|
978
|
+
|
|
973
979
|
/* --------------------- Loading -------------------- */
|
|
974
980
|
|
|
975
981
|
.tl-loading {
|
|
@@ -1217,7 +1223,6 @@ input,
|
|
|
1217
1223
|
align-items: center;
|
|
1218
1224
|
text-align: center;
|
|
1219
1225
|
color: var(--tl-color-text);
|
|
1220
|
-
text-shadow: var(--tl-text-outline);
|
|
1221
1226
|
}
|
|
1222
1227
|
|
|
1223
1228
|
.tl-shape[data-shape-type='arrow'] .tl-text-label__inner {
|
|
@@ -1446,7 +1451,6 @@ input,
|
|
|
1446
1451
|
}
|
|
1447
1452
|
|
|
1448
1453
|
.tl-note__container > .tl-text-label {
|
|
1449
|
-
text-shadow: none;
|
|
1450
1454
|
color: currentColor;
|
|
1451
1455
|
}
|
|
1452
1456
|
|
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.d039f3a1ab8f",
|
|
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.d039f3a1ab8f",
|
|
54
|
+
"@tldraw/state-react": "4.3.0-canary.d039f3a1ab8f",
|
|
55
|
+
"@tldraw/store": "4.3.0-canary.d039f3a1ab8f",
|
|
56
|
+
"@tldraw/tlschema": "4.3.0-canary.d039f3a1ab8f",
|
|
57
|
+
"@tldraw/utils": "4.3.0-canary.d039f3a1ab8f",
|
|
58
|
+
"@tldraw/validate": "4.3.0-canary.d039f3a1ab8f",
|
|
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 {
|
|
@@ -210,7 +210,7 @@ function GridWrapper() {
|
|
|
210
210
|
function ScribbleWrapper() {
|
|
211
211
|
const editor = useEditor()
|
|
212
212
|
const scribbles = useValue('scribbles', () => editor.getInstanceState().scribbles, [editor])
|
|
213
|
-
const zoomLevel = useValue('zoomLevel', () => editor.
|
|
213
|
+
const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
|
|
214
214
|
const { Scribble } = useEditorComponents()
|
|
215
215
|
|
|
216
216
|
if (!(Scribble && scribbles.length)) return null
|
|
@@ -243,7 +243,7 @@ function ZoomBrushWrapper() {
|
|
|
243
243
|
function SnapIndicatorWrapper() {
|
|
244
244
|
const editor = useEditor()
|
|
245
245
|
const lines = useValue('snapLines', () => editor.snaps.getIndicators(), [editor])
|
|
246
|
-
const zoomLevel = useValue('zoomLevel', () => editor.
|
|
246
|
+
const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
|
|
247
247
|
const { SnapIndicator } = useEditorComponents()
|
|
248
248
|
|
|
249
249
|
if (!(SnapIndicator && lines.length > 0)) return null
|
|
@@ -284,7 +284,7 @@ function HandlesWrapperInner({ shapeId }: { shapeId: TLShapeId }) {
|
|
|
284
284
|
const editor = useEditor()
|
|
285
285
|
const { Handles } = useEditorComponents()
|
|
286
286
|
|
|
287
|
-
const zoomLevel = useValue('zoomLevel', () => editor.
|
|
287
|
+
const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
|
|
288
288
|
|
|
289
289
|
const isCoarse = useValue('coarse pointer', () => editor.getInstanceState().isCoarsePointer, [
|
|
290
290
|
editor,
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -2670,6 +2670,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2670
2670
|
return this.getCamera().z
|
|
2671
2671
|
}
|
|
2672
2672
|
|
|
2673
|
+
private _debouncedZoomLevel = atom('debounced zoom level', 1)
|
|
2674
|
+
|
|
2675
|
+
/**
|
|
2676
|
+
* Get the debounced zoom level. When the camera is moving, this returns the zoom level
|
|
2677
|
+
* from when the camera started moving rather than the current zoom level. This can be
|
|
2678
|
+
* used to avoid expensive re-renders during camera movements.
|
|
2679
|
+
*
|
|
2680
|
+
* This behavior is controlled by the `useDebouncedZoom` option. When `useDebouncedZoom`
|
|
2681
|
+
* is `false`, this method always returns the current zoom level.
|
|
2682
|
+
*
|
|
2683
|
+
* @public
|
|
2684
|
+
*/
|
|
2685
|
+
@computed getDebouncedZoomLevel() {
|
|
2686
|
+
if (this.options.debouncedZoom) {
|
|
2687
|
+
if (this.getCameraState() === 'idle') {
|
|
2688
|
+
return this.getZoomLevel()
|
|
2689
|
+
} else {
|
|
2690
|
+
return this._debouncedZoomLevel.get()
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
return this.getZoomLevel()
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
@computed private _getAboveDebouncedZoomThreshold() {
|
|
2698
|
+
return this.getCurrentPageShapeIds().size > this.options.debouncedZoomThreshold
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
/**
|
|
2702
|
+
* Get the efficient zoom level. This returns the current zoom level if there are less than 300 shapes on the page,
|
|
2703
|
+
* otherwise it returns the debounced zoom level. This can be used to avoid expensive re-renders during camera movements.
|
|
2704
|
+
*
|
|
2705
|
+
* @public
|
|
2706
|
+
* @example
|
|
2707
|
+
* ```ts
|
|
2708
|
+
* editor.getEfficientZoomLevel()
|
|
2709
|
+
* ```
|
|
2710
|
+
*
|
|
2711
|
+
* @public
|
|
2712
|
+
*/
|
|
2713
|
+
@computed getEfficientZoomLevel() {
|
|
2714
|
+
return this._getAboveDebouncedZoomThreshold()
|
|
2715
|
+
? this.getDebouncedZoomLevel()
|
|
2716
|
+
: this.getZoomLevel()
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2673
2719
|
/**
|
|
2674
2720
|
* Get the camera's initial or reset zoom level.
|
|
2675
2721
|
*
|
|
@@ -3649,8 +3695,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3649
3695
|
}
|
|
3650
3696
|
}
|
|
3651
3697
|
|
|
3652
|
-
this._tickCameraState()
|
|
3653
|
-
|
|
3654
3698
|
return this
|
|
3655
3699
|
}
|
|
3656
3700
|
|
|
@@ -4056,18 +4100,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4056
4100
|
// box just for rendering, and we only update after the camera stops moving.
|
|
4057
4101
|
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
|
4058
4102
|
private _cameraStateTimeoutRemaining = 0
|
|
4059
|
-
_decayCameraStateTimeout(elapsed: number) {
|
|
4103
|
+
private _decayCameraStateTimeout(elapsed: number) {
|
|
4060
4104
|
this._cameraStateTimeoutRemaining -= elapsed
|
|
4061
4105
|
if (this._cameraStateTimeoutRemaining > 0) return
|
|
4062
4106
|
this.off('tick', this._decayCameraStateTimeout)
|
|
4063
4107
|
this._cameraState.set('idle')
|
|
4064
4108
|
}
|
|
4065
|
-
_tickCameraState() {
|
|
4109
|
+
private _tickCameraState() {
|
|
4066
4110
|
// always reset the timeout
|
|
4067
4111
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimeoutMs
|
|
4068
4112
|
// If the state is idle, then start the tick
|
|
4069
4113
|
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
|
4070
4114
|
this._cameraState.set('moving')
|
|
4115
|
+
this._debouncedZoomLevel.set(unsafe__withoutCapture(() => this.getCamera().z))
|
|
4071
4116
|
this.on('tick', this._decayCameraStateTimeout)
|
|
4072
4117
|
}
|
|
4073
4118
|
|
|
@@ -9149,6 +9194,30 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9149
9194
|
}
|
|
9150
9195
|
}
|
|
9151
9196
|
|
|
9197
|
+
if (point) {
|
|
9198
|
+
const shapesById = new Map<TLShapeId, TLShape>(shapes.map((shape) => [shape.id, shape]))
|
|
9199
|
+
const rootShapesFromContent = compact(rootShapeIds.map((id) => shapesById.get(id)))
|
|
9200
|
+
if (rootShapesFromContent.length > 0) {
|
|
9201
|
+
const targetParent = this.getShapeAtPoint(point, {
|
|
9202
|
+
hitInside: true,
|
|
9203
|
+
hitFrameInside: true,
|
|
9204
|
+
hitLocked: true,
|
|
9205
|
+
filter: (shape) => {
|
|
9206
|
+
const util = this.getShapeUtil(shape)
|
|
9207
|
+
if (!util.canReceiveNewChildrenOfType) return false
|
|
9208
|
+
return rootShapesFromContent.every((rootShape) =>
|
|
9209
|
+
util.canReceiveNewChildrenOfType!(shape, rootShape.type)
|
|
9210
|
+
)
|
|
9211
|
+
},
|
|
9212
|
+
})
|
|
9213
|
+
|
|
9214
|
+
// When pasting at a specific point (e.g. paste-at-cursor) prefer the
|
|
9215
|
+
// parent under the pointer so that we don't keep using the original
|
|
9216
|
+
// selection's parent (which can keep shapes clipped inside frames).
|
|
9217
|
+
pasteParentId = targetParent ? targetParent.id : currentPageId
|
|
9218
|
+
}
|
|
9219
|
+
}
|
|
9220
|
+
|
|
9152
9221
|
let isDuplicating = false
|
|
9153
9222
|
|
|
9154
9223
|
if (!isPageId(pasteParentId)) {
|
|
@@ -10255,8 +10324,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10255
10324
|
}
|
|
10256
10325
|
}
|
|
10257
10326
|
|
|
10258
|
-
this.emit('event', info)
|
|
10259
10327
|
this.root.handleEvent(info)
|
|
10328
|
+
this.emit('event', info)
|
|
10260
10329
|
return
|
|
10261
10330
|
}
|
|
10262
10331
|
|
|
@@ -6,7 +6,7 @@ import { getPerfectDashProps } from '../shared/getPerfectDashProps'
|
|
|
6
6
|
export function DashedOutlineBox({ bounds, className }: { bounds: Box; className: string }) {
|
|
7
7
|
const editor = useEditor()
|
|
8
8
|
|
|
9
|
-
const zoomLevel = useValue('zoom level', () => editor.
|
|
9
|
+
const zoomLevel = useValue('zoom level', () => editor.getEfficientZoomLevel(), [editor])
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
12
|
<g className={className} pointerEvents="none" strokeLinecap="round" strokeLinejoin="round">
|