@tldraw/editor 3.13.0-canary.2449ca610c41 → 3.13.0-canary.2928a5d81c67
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 +109 -111
- package/dist-cjs/index.js +7 -22
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +12 -8
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +27 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +17 -11
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +40 -15
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager.js +10 -0
- package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgJsx.js +12 -3
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +16 -15
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +16 -0
- package/dist-cjs/lib/primitives/Box.js.map +2 -2
- package/dist-cjs/lib/primitives/Mat.js +1 -1
- package/dist-cjs/lib/primitives/Mat.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +20 -0
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Edge2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +91 -20
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +55 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Polyline2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +2 -2
- package/dist-cjs/lib/utils/areShapesContentEqual.js +25 -0
- package/dist-cjs/lib/utils/areShapesContentEqual.js.map +7 -0
- package/dist-cjs/lib/utils/debug-flags.js +5 -2
- package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
- package/dist-cjs/lib/utils/nearestMultiple.js +34 -0
- package/dist-cjs/lib/utils/nearestMultiple.js.map +7 -0
- package/dist-cjs/lib/utils/rotation.js +5 -5
- package/dist-cjs/lib/utils/rotation.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 +109 -111
- package/dist-esm/index.mjs +9 -41
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +12 -8
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +27 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +17 -11
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +40 -15
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager.mjs +10 -0
- package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +12 -3
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +16 -15
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +16 -0
- package/dist-esm/lib/primitives/Box.mjs.map +2 -2
- package/dist-esm/lib/primitives/Mat.mjs +1 -1
- package/dist-esm/lib/primitives/Mat.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +20 -0
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs +1 -1
- package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +92 -21
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +55 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Polyline2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +2 -2
- package/dist-esm/lib/utils/areShapesContentEqual.mjs +5 -0
- package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +7 -0
- package/dist-esm/lib/utils/debug-flags.mjs +5 -2
- package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
- package/dist-esm/lib/utils/nearestMultiple.mjs +14 -0
- package/dist-esm/lib/utils/nearestMultiple.mjs.map +7 -0
- package/dist-esm/lib/utils/rotation.mjs +5 -5
- package/dist-esm/lib/utils/rotation.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +34 -4
- package/package.json +7 -7
- package/src/index.ts +16 -31
- package/src/lib/components/Shape.tsx +14 -10
- package/src/lib/components/default-components/DefaultCanvas.tsx +32 -2
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +17 -8
- package/src/lib/editor/Editor.test.ts +1 -1
- package/src/lib/editor/Editor.ts +40 -15
- package/src/lib/editor/managers/SnapManager/HandleSnaps.ts +0 -1
- package/src/lib/editor/managers/TextManager.ts +12 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +22 -2
- package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +9 -9
- package/src/lib/exports/getSvgJsx.tsx +16 -7
- package/src/lib/hooks/useEditorComponents.tsx +32 -28
- package/src/lib/primitives/Box.ts +20 -0
- package/src/lib/primitives/Mat.ts +5 -4
- package/src/lib/primitives/Vec.ts +23 -0
- package/src/lib/primitives/geometry/Arc2d.ts +5 -5
- package/src/lib/primitives/geometry/Circle2d.ts +4 -4
- package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -4
- package/src/lib/primitives/geometry/CubicSpline2d.ts +3 -3
- package/src/lib/primitives/geometry/Edge2d.ts +3 -3
- package/src/lib/primitives/geometry/Ellipse2d.ts +3 -3
- package/src/lib/primitives/geometry/Geometry2d.test.ts +42 -0
- package/src/lib/primitives/geometry/Geometry2d.ts +123 -35
- package/src/lib/primitives/geometry/Group2d.ts +70 -7
- package/src/lib/primitives/geometry/Point2d.ts +2 -2
- package/src/lib/primitives/geometry/Polyline2d.ts +3 -3
- package/src/lib/primitives/geometry/Stadium2d.ts +3 -3
- package/src/lib/test/currentToolIdMask.test.ts +1 -1
- package/src/lib/test/user.test.ts +1 -1
- package/src/lib/utils/areShapesContentEqual.ts +4 -0
- package/src/lib/utils/debug-flags.ts +7 -2
- package/src/lib/utils/nearestMultiple.ts +13 -0
- package/src/lib/utils/rotation.ts +8 -6
- package/src/lib/utils/sync/LocalIndexedDb.test.ts +1 -1
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +1 -1
- package/src/version.ts +3 -3
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function gcd(a, b) {
|
|
2
|
+
return b === 0 ? a : gcd(b, a % b);
|
|
3
|
+
}
|
|
4
|
+
function nearestMultiple(float) {
|
|
5
|
+
const decimal = float.toString().split(".")[1];
|
|
6
|
+
if (!decimal) return 1;
|
|
7
|
+
const denominator = Math.pow(10, decimal.length);
|
|
8
|
+
const numerator = parseInt(decimal, 10);
|
|
9
|
+
return denominator / gcd(numerator, denominator);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
nearestMultiple
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=nearestMultiple.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/utils/nearestMultiple.ts"],
|
|
4
|
+
"sourcesContent": ["// Euclidean algorithm to find the GCD\nfunction gcd(a: number, b: number): number {\n\treturn b === 0 ? a : gcd(b, a % b)\n}\n\n// Returns the lowest value that the given number can be multiplied by to reach an integer\nexport function nearestMultiple(float: number) {\n\tconst decimal = float.toString().split('.')[1]\n\tif (!decimal) return 1\n\tconst denominator = Math.pow(10, decimal.length)\n\tconst numerator = parseInt(decimal, 10)\n\treturn denominator / gcd(numerator, denominator)\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,IAAI,GAAW,GAAmB;AAC1C,SAAO,MAAM,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;AAClC;AAGO,SAAS,gBAAgB,OAAe;AAC9C,QAAM,UAAU,MAAM,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC;AAC7C,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,cAAc,KAAK,IAAI,IAAI,QAAQ,MAAM;AAC/C,QAAM,YAAY,SAAS,SAAS,EAAE;AACtC,SAAO,cAAc,IAAI,WAAW,WAAW;AAChD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -13,10 +13,10 @@ function getRotationSnapshot({
|
|
|
13
13
|
if (!rotatedPageBounds) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
const
|
|
16
|
+
const initialPageCenter = rotatedPageBounds.center.clone().rotWith(rotatedPageBounds.point, rotation);
|
|
17
17
|
return {
|
|
18
|
-
|
|
19
|
-
initialCursorAngle:
|
|
18
|
+
initialPageCenter,
|
|
19
|
+
initialCursorAngle: initialPageCenter.angle(editor.inputs.originPagePoint),
|
|
20
20
|
initialShapesRotation: rotation,
|
|
21
21
|
shapeSnapshots: shapes.map((shape) => ({
|
|
22
22
|
shape,
|
|
@@ -31,11 +31,11 @@ function applyRotationToSnapshotShapes({
|
|
|
31
31
|
stage,
|
|
32
32
|
centerOverride
|
|
33
33
|
}) {
|
|
34
|
-
const {
|
|
34
|
+
const { initialPageCenter, shapeSnapshots } = snapshot;
|
|
35
35
|
editor.updateShapes(
|
|
36
36
|
shapeSnapshots.map(({ shape, initialPagePoint }) => {
|
|
37
37
|
const parentTransform = isShapeId(shape.parentId) ? editor.getShapePageTransform(shape.parentId) : Mat.Identity();
|
|
38
|
-
const newPagePoint = Vec.RotWith(initialPagePoint, centerOverride ??
|
|
38
|
+
const newPagePoint = Vec.RotWith(initialPagePoint, centerOverride ?? initialPageCenter, delta);
|
|
39
39
|
const newLocalPoint = Mat.applyToPoint(
|
|
40
40
|
// use the current parent transform in case it has moved/resized since the start
|
|
41
41
|
// (e.g. if rotating a shape at the edge of a group)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/rotation.ts"],
|
|
4
|
-
"sourcesContent": ["import { isShapeId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'\nimport { compact } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { Mat } from '../primitives/Mat'\nimport { canonicalizeRotation } from '../primitives/utils'\nimport { Vec, VecLike } from '../primitives/Vec'\n\n/** @internal */\nexport function getRotationSnapshot({\n\teditor,\n\tids,\n}: {\n\teditor: Editor\n\tids: TLShapeId[]\n}): TLRotationSnapshot | null {\n\tconst shapes = compact(ids.map((id) => editor.getShape(id)))\n\tconst rotation = editor.getShapesSharedRotation(ids)\n\tconst rotatedPageBounds = editor.getShapesRotatedPageBounds(ids)\n\n\t// todo: this assumes we're rotating the selected shapes\n\t// if we try to rotate shapes that aren't selected, this\n\t// will produce the wrong results\n\n\t// Return null if there are no selected shapes\n\tif (!rotatedPageBounds) {\n\t\treturn null\n\t}\n\n\tconst
|
|
5
|
-
"mappings": "AAAA,SAAS,iBAAqD;AAC9D,SAAS,eAAe;AAExB,SAAS,WAAW;AACpB,SAAS,4BAA4B;AACrC,SAAS,WAAoB;AAGtB,SAAS,oBAAoB;AAAA,EACnC;AAAA,EACA;AACD,GAG8B;AAC7B,QAAM,SAAS,QAAQ,IAAI,IAAI,CAAC,OAAO,OAAO,SAAS,EAAE,CAAC,CAAC;AAC3D,QAAM,WAAW,OAAO,wBAAwB,GAAG;AACnD,QAAM,oBAAoB,OAAO,2BAA2B,GAAG;AAO/D,MAAI,CAAC,mBAAmB;AACvB,WAAO;AAAA,EACR;AAEA,QAAM,
|
|
4
|
+
"sourcesContent": ["import { isShapeId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'\nimport { compact } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { Mat } from '../primitives/Mat'\nimport { canonicalizeRotation } from '../primitives/utils'\nimport { Vec, VecLike } from '../primitives/Vec'\n\n/** @internal */\nexport function getRotationSnapshot({\n\teditor,\n\tids,\n}: {\n\teditor: Editor\n\tids: TLShapeId[]\n}): TLRotationSnapshot | null {\n\tconst shapes = compact(ids.map((id) => editor.getShape(id)))\n\tconst rotation = editor.getShapesSharedRotation(ids)\n\tconst rotatedPageBounds = editor.getShapesRotatedPageBounds(ids)\n\n\t// todo: this assumes we're rotating the selected shapes\n\t// if we try to rotate shapes that aren't selected, this\n\t// will produce the wrong results\n\n\t// Return null if there are no selected shapes\n\tif (!rotatedPageBounds) {\n\t\treturn null\n\t}\n\n\tconst initialPageCenter = rotatedPageBounds.center\n\t\t.clone()\n\t\t.rotWith(rotatedPageBounds.point, rotation)\n\n\treturn {\n\t\tinitialPageCenter,\n\t\tinitialCursorAngle: initialPageCenter.angle(editor.inputs.originPagePoint),\n\t\tinitialShapesRotation: rotation,\n\t\tshapeSnapshots: shapes.map((shape) => ({\n\t\t\tshape,\n\t\t\tinitialPagePoint: editor.getShapePageTransform(shape.id)!.point(),\n\t\t})),\n\t}\n}\n\n/**\n * @internal\n **/\nexport interface TLRotationSnapshot {\n\tinitialPageCenter: Vec\n\tinitialCursorAngle: number\n\tinitialShapesRotation: number\n\tshapeSnapshots: {\n\t\tshape: TLShape\n\t\tinitialPagePoint: Vec\n\t}[]\n}\n\n/** @internal */\nexport function applyRotationToSnapshotShapes({\n\tdelta,\n\teditor,\n\tsnapshot,\n\tstage,\n\tcenterOverride,\n}: {\n\tdelta: number\n\tsnapshot: TLRotationSnapshot\n\teditor: Editor\n\tstage: 'start' | 'update' | 'end' | 'one-off'\n\tcenterOverride?: VecLike\n}) {\n\tconst { initialPageCenter, shapeSnapshots } = snapshot\n\n\teditor.updateShapes(\n\t\tshapeSnapshots.map(({ shape, initialPagePoint }) => {\n\t\t\t// We need to both rotate each shape individually and rotate the shapes\n\t\t\t// around the pivot point (the average center of all rotating shapes.)\n\n\t\t\tconst parentTransform = isShapeId(shape.parentId)\n\t\t\t\t? editor.getShapePageTransform(shape.parentId)!\n\t\t\t\t: Mat.Identity()\n\n\t\t\tconst newPagePoint = Vec.RotWith(initialPagePoint, centerOverride ?? initialPageCenter, delta)\n\n\t\t\tconst newLocalPoint = Mat.applyToPoint(\n\t\t\t\t// use the current parent transform in case it has moved/resized since the start\n\t\t\t\t// (e.g. if rotating a shape at the edge of a group)\n\t\t\t\tMat.Inverse(parentTransform),\n\t\t\t\tnewPagePoint\n\t\t\t)\n\t\t\tconst newRotation = canonicalizeRotation(shape.rotation + delta)\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx: newLocalPoint.x,\n\t\t\t\ty: newLocalPoint.y,\n\t\t\t\trotation: newRotation,\n\t\t\t}\n\t\t})\n\t)\n\n\t// Handle change\n\n\tconst changes: TLShapePartial[] = []\n\n\tshapeSnapshots.forEach(({ shape }) => {\n\t\tconst current = editor.getShape(shape.id)\n\t\tif (!current) return\n\t\tconst util = editor.getShapeUtil(shape)\n\n\t\tif (stage === 'start' || stage === 'one-off') {\n\t\t\tconst changeStart = util.onRotateStart?.(shape)\n\t\t\tif (changeStart) changes.push(changeStart)\n\t\t}\n\n\t\tconst changeUpdate = util.onRotate?.(shape, current)\n\t\tif (changeUpdate) changes.push(changeUpdate)\n\n\t\tif (stage === 'end' || stage === 'one-off') {\n\t\t\tconst changeEnd = util.onRotateEnd?.(shape, current)\n\t\t\tif (changeEnd) changes.push(changeEnd)\n\t\t}\n\t})\n\n\tif (changes.length > 0) {\n\t\teditor.updateShapes(changes)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAqD;AAC9D,SAAS,eAAe;AAExB,SAAS,WAAW;AACpB,SAAS,4BAA4B;AACrC,SAAS,WAAoB;AAGtB,SAAS,oBAAoB;AAAA,EACnC;AAAA,EACA;AACD,GAG8B;AAC7B,QAAM,SAAS,QAAQ,IAAI,IAAI,CAAC,OAAO,OAAO,SAAS,EAAE,CAAC,CAAC;AAC3D,QAAM,WAAW,OAAO,wBAAwB,GAAG;AACnD,QAAM,oBAAoB,OAAO,2BAA2B,GAAG;AAO/D,MAAI,CAAC,mBAAmB;AACvB,WAAO;AAAA,EACR;AAEA,QAAM,oBAAoB,kBAAkB,OAC1C,MAAM,EACN,QAAQ,kBAAkB,OAAO,QAAQ;AAE3C,SAAO;AAAA,IACN;AAAA,IACA,oBAAoB,kBAAkB,MAAM,OAAO,OAAO,eAAe;AAAA,IACzE,uBAAuB;AAAA,IACvB,gBAAgB,OAAO,IAAI,CAAC,WAAW;AAAA,MACtC;AAAA,MACA,kBAAkB,OAAO,sBAAsB,MAAM,EAAE,EAAG,MAAM;AAAA,IACjE,EAAE;AAAA,EACH;AACD;AAgBO,SAAS,8BAA8B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAMG;AACF,QAAM,EAAE,mBAAmB,eAAe,IAAI;AAE9C,SAAO;AAAA,IACN,eAAe,IAAI,CAAC,EAAE,OAAO,iBAAiB,MAAM;AAInD,YAAM,kBAAkB,UAAU,MAAM,QAAQ,IAC7C,OAAO,sBAAsB,MAAM,QAAQ,IAC3C,IAAI,SAAS;AAEhB,YAAM,eAAe,IAAI,QAAQ,kBAAkB,kBAAkB,mBAAmB,KAAK;AAE7F,YAAM,gBAAgB,IAAI;AAAA;AAAA;AAAA,QAGzB,IAAI,QAAQ,eAAe;AAAA,QAC3B;AAAA,MACD;AACA,YAAM,cAAc,qBAAqB,MAAM,WAAW,KAAK;AAE/D,aAAO;AAAA,QACN,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,GAAG,cAAc;AAAA,QACjB,GAAG,cAAc;AAAA,QACjB,UAAU;AAAA,MACX;AAAA,IACD,CAAC;AAAA,EACF;AAIA,QAAM,UAA4B,CAAC;AAEnC,iBAAe,QAAQ,CAAC,EAAE,MAAM,MAAM;AACrC,UAAM,UAAU,OAAO,SAAS,MAAM,EAAE;AACxC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,OAAO,aAAa,KAAK;AAEtC,QAAI,UAAU,WAAW,UAAU,WAAW;AAC7C,YAAM,cAAc,KAAK,gBAAgB,KAAK;AAC9C,UAAI,YAAa,SAAQ,KAAK,WAAW;AAAA,IAC1C;AAEA,UAAM,eAAe,KAAK,WAAW,OAAO,OAAO;AACnD,QAAI,aAAc,SAAQ,KAAK,YAAY;AAE3C,QAAI,UAAU,SAAS,UAAU,WAAW;AAC3C,YAAM,YAAY,KAAK,cAAc,OAAO,OAAO;AACnD,UAAI,UAAW,SAAQ,KAAK,SAAS;AAAA,IACtC;AAAA,EACD,CAAC;AAED,MAAI,QAAQ,SAAS,GAAG;AACvB,WAAO,aAAa,OAAO;AAAA,EAC5B;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "3.13.0-canary.
|
|
1
|
+
const version = "3.13.0-canary.2928a5d81c67";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-
|
|
5
|
-
patch: "2025-
|
|
4
|
+
minor: "2025-05-09T10:38:30.040Z",
|
|
5
|
+
patch: "2025-05-09T10:38:30.040Z"
|
|
6
6
|
};
|
|
7
7
|
export {
|
|
8
8
|
publishDates,
|
package/dist-esm/version.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.13.0-canary.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.13.0-canary.2928a5d81c67'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-09T10:38:30.040Z',\n\tpatch: '2025-05-09T10:38:30.040Z',\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
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
/* User handles need to be above selection edges / corners, matters for sticky note clone handles */
|
|
45
45
|
--layer-overlays-user-handles: 105;
|
|
46
46
|
--layer-overlays-user-indicator-hint: 110;
|
|
47
|
+
--layer-overlays-custom: 115;
|
|
47
48
|
--layer-overlays-collaborator-cursor-hint: 120;
|
|
48
49
|
--layer-overlays-collaborator-cursor: 130;
|
|
49
50
|
|
|
@@ -472,6 +473,10 @@ input,
|
|
|
472
473
|
stroke-width: calc(2.5px * var(--tl-scale));
|
|
473
474
|
}
|
|
474
475
|
|
|
476
|
+
.tl-custom-overlays {
|
|
477
|
+
z-index: var(--layer-overlays-custom);
|
|
478
|
+
}
|
|
479
|
+
|
|
475
480
|
/* behind collaborator cursor */
|
|
476
481
|
.tl-collaborator__cursor-hint {
|
|
477
482
|
z-index: var(--layer-overlays-collaborator-cursor-hint);
|
|
@@ -595,6 +600,31 @@ input,
|
|
|
595
600
|
}
|
|
596
601
|
}
|
|
597
602
|
|
|
603
|
+
/* --------------------- Arrow Hints -------------------- */
|
|
604
|
+
|
|
605
|
+
.tl-arrow-hint-handle {
|
|
606
|
+
fill: var(--color-selected-contrast);
|
|
607
|
+
stroke: var(--color-selection-stroke);
|
|
608
|
+
stroke-width: calc(1.5px * var(--tl-scale));
|
|
609
|
+
r: calc(4px * var(--tl-scale));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.tl-arrow-hint-snap {
|
|
613
|
+
stroke: transparent;
|
|
614
|
+
fill: var(--color-selection-fill);
|
|
615
|
+
r: calc(12px * var(--tl-scale));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.tl-arrow-hint-snap__none,
|
|
619
|
+
.tl-arrow-hint-snap__center,
|
|
620
|
+
.tl-arrow-hint-snap__axis {
|
|
621
|
+
display: none;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.tl-arrow-hint-snap__edge {
|
|
625
|
+
r: calc(8px * var(--tl-scale));
|
|
626
|
+
}
|
|
627
|
+
|
|
598
628
|
/* ------------------ Bounds Detail ----------------- */
|
|
599
629
|
|
|
600
630
|
.tl-image,
|
|
@@ -964,6 +994,8 @@ input,
|
|
|
964
994
|
|
|
965
995
|
.tl-rich-text p {
|
|
966
996
|
margin: 0;
|
|
997
|
+
/* Depending on the extensions, <p> tags can be empty, without a <br />. */
|
|
998
|
+
min-height: 1lh;
|
|
967
999
|
}
|
|
968
1000
|
|
|
969
1001
|
.tl-rich-text ul,
|
|
@@ -971,6 +1003,8 @@ input,
|
|
|
971
1003
|
text-align: left;
|
|
972
1004
|
margin: 0;
|
|
973
1005
|
padding-left: 3.25ch;
|
|
1006
|
+
/* Some resets, like Tailwind, nix the list styling. */
|
|
1007
|
+
list-style: revert;
|
|
974
1008
|
}
|
|
975
1009
|
|
|
976
1010
|
.tl-rich-text ol:has(> li:nth-child(10)) {
|
|
@@ -1348,10 +1382,6 @@ input,
|
|
|
1348
1382
|
opacity: 0;
|
|
1349
1383
|
}
|
|
1350
1384
|
|
|
1351
|
-
.tl-arrow-label[data-isediting='true'] > .tl-arrow-label__inner {
|
|
1352
|
-
background-color: var(--color-background);
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
1385
|
.tl-arrow-label__inner {
|
|
1356
1386
|
border-radius: var(--radius-1);
|
|
1357
1387
|
box-sizing: content-box;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
3
|
"description": "A tiny little drawing app (editor).",
|
|
4
|
-
"version": "3.13.0-canary.
|
|
4
|
+
"version": "3.13.0-canary.2928a5d81c67",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"@tiptap/core": "^2.9.1",
|
|
49
49
|
"@tiptap/pm": "^2.9.1",
|
|
50
50
|
"@tiptap/react": "^2.9.1",
|
|
51
|
-
"@tldraw/state": "3.13.0-canary.
|
|
52
|
-
"@tldraw/state-react": "3.13.0-canary.
|
|
53
|
-
"@tldraw/store": "3.13.0-canary.
|
|
54
|
-
"@tldraw/tlschema": "3.13.0-canary.
|
|
55
|
-
"@tldraw/utils": "3.13.0-canary.
|
|
56
|
-
"@tldraw/validate": "3.13.0-canary.
|
|
51
|
+
"@tldraw/state": "3.13.0-canary.2928a5d81c67",
|
|
52
|
+
"@tldraw/state-react": "3.13.0-canary.2928a5d81c67",
|
|
53
|
+
"@tldraw/store": "3.13.0-canary.2928a5d81c67",
|
|
54
|
+
"@tldraw/tlschema": "3.13.0-canary.2928a5d81c67",
|
|
55
|
+
"@tldraw/utils": "3.13.0-canary.2928a5d81c67",
|
|
56
|
+
"@tldraw/validate": "3.13.0-canary.2928a5d81c67",
|
|
57
57
|
"@types/core-js": "^2.5.8",
|
|
58
58
|
"@use-gesture/react": "^10.3.1",
|
|
59
59
|
"classnames": "^2.5.1",
|
package/src/index.ts
CHANGED
|
@@ -4,37 +4,11 @@ import 'core-js/stable/array/flat-map.js'
|
|
|
4
4
|
import 'core-js/stable/array/flat.js'
|
|
5
5
|
import 'core-js/stable/string/at.js'
|
|
6
6
|
import 'core-js/stable/string/replace-all.js'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
react,
|
|
13
|
-
transact,
|
|
14
|
-
transaction,
|
|
15
|
-
whyAmIRunning,
|
|
16
|
-
type Atom,
|
|
17
|
-
type Signal,
|
|
18
|
-
} from '@tldraw/state'
|
|
19
|
-
export {
|
|
20
|
-
track,
|
|
21
|
-
useAtom,
|
|
22
|
-
useComputed,
|
|
23
|
-
useQuickReactor,
|
|
24
|
-
useReactor,
|
|
25
|
-
useStateTracking,
|
|
26
|
-
useValue,
|
|
27
|
-
} from '@tldraw/state-react'
|
|
28
|
-
export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
|
|
29
|
-
export {
|
|
30
|
-
getFontsFromRichText,
|
|
31
|
-
type RichTextFontVisitor,
|
|
32
|
-
type RichTextFontVisitorState,
|
|
33
|
-
type TLTextOptions,
|
|
34
|
-
type TiptapEditor,
|
|
35
|
-
type TiptapNode,
|
|
36
|
-
} from './lib/utils/richText'
|
|
37
|
-
export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line local/no-export-star
|
|
9
|
+
export * from '@tldraw/state'
|
|
10
|
+
// eslint-disable-next-line local/no-export-star
|
|
11
|
+
export * from '@tldraw/state-react'
|
|
38
12
|
// eslint-disable-next-line local/no-export-star
|
|
39
13
|
export * from '@tldraw/store'
|
|
40
14
|
// eslint-disable-next-line local/no-export-star
|
|
@@ -43,6 +17,7 @@ export * from '@tldraw/tlschema'
|
|
|
43
17
|
export * from '@tldraw/utils'
|
|
44
18
|
// eslint-disable-next-line local/no-export-star
|
|
45
19
|
export * from '@tldraw/validate'
|
|
20
|
+
|
|
46
21
|
export {
|
|
47
22
|
ErrorScreen,
|
|
48
23
|
LoadingScreen,
|
|
@@ -212,6 +187,7 @@ export {
|
|
|
212
187
|
export { GroupShapeUtil } from './lib/editor/shapes/group/GroupShapeUtil'
|
|
213
188
|
export { getPerfectDashProps } from './lib/editor/shapes/shared/getPerfectDashProps'
|
|
214
189
|
export { resizeBox, type ResizeBoxOptions } from './lib/editor/shapes/shared/resizeBox'
|
|
190
|
+
export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
|
|
215
191
|
export { BaseBoxShapeTool } from './lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool'
|
|
216
192
|
export { maybeSnapToGrid } from './lib/editor/tools/BaseBoxShapeTool/children/Pointing'
|
|
217
193
|
export { StateNode, type TLStateNodeConstructor } from './lib/editor/tools/StateNode'
|
|
@@ -459,12 +435,21 @@ export { hardResetEditor } from './lib/utils/hardResetEditor'
|
|
|
459
435
|
export { isAccelKey } from './lib/utils/keyboard'
|
|
460
436
|
export { normalizeWheel } from './lib/utils/normalizeWheel'
|
|
461
437
|
export { refreshPage } from './lib/utils/refreshPage'
|
|
438
|
+
export {
|
|
439
|
+
getFontsFromRichText,
|
|
440
|
+
type RichTextFontVisitor,
|
|
441
|
+
type RichTextFontVisitorState,
|
|
442
|
+
type TLTextOptions,
|
|
443
|
+
type TiptapEditor,
|
|
444
|
+
type TiptapNode,
|
|
445
|
+
} from './lib/utils/richText'
|
|
462
446
|
export {
|
|
463
447
|
applyRotationToSnapshotShapes,
|
|
464
448
|
getRotationSnapshot,
|
|
465
449
|
type TLRotationSnapshot,
|
|
466
450
|
} from './lib/utils/rotation'
|
|
467
451
|
export { runtime, setRuntimeOverrides } from './lib/utils/runtime'
|
|
452
|
+
export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
|
|
468
453
|
export { type TLStoreWithStatus } from './lib/utils/sync/StoreWithStatus'
|
|
469
454
|
export { hardReset } from './lib/utils/sync/hardReset'
|
|
470
455
|
export { uniq } from './lib/utils/uniq'
|
|
@@ -6,6 +6,7 @@ import { ShapeUtil } from '../editor/shapes/ShapeUtil'
|
|
|
6
6
|
import { useEditor } from '../hooks/useEditor'
|
|
7
7
|
import { useEditorComponents } from '../hooks/useEditorComponents'
|
|
8
8
|
import { Mat } from '../primitives/Mat'
|
|
9
|
+
import { areShapesContentEqual } from '../utils/areShapesContentEqual'
|
|
9
10
|
import { setStyleProperty } from '../utils/dom'
|
|
10
11
|
import { OptionalErrorBoundary } from './ErrorBoundary'
|
|
11
12
|
|
|
@@ -27,6 +28,7 @@ export const Shape = memo(function Shape({
|
|
|
27
28
|
index,
|
|
28
29
|
backgroundIndex,
|
|
29
30
|
opacity,
|
|
31
|
+
dprMultiple,
|
|
30
32
|
}: {
|
|
31
33
|
id: TLShapeId
|
|
32
34
|
shape: TLShape
|
|
@@ -34,6 +36,7 @@ export const Shape = memo(function Shape({
|
|
|
34
36
|
index: number
|
|
35
37
|
backgroundIndex: number
|
|
36
38
|
opacity: number
|
|
39
|
+
dprMultiple: number
|
|
37
40
|
}) {
|
|
38
41
|
const editor = useEditor()
|
|
39
42
|
|
|
@@ -88,14 +91,18 @@ export const Shape = memo(function Shape({
|
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
// Width / Height
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
// We round the shape width and height up to the nearest multiple of dprMultiple
|
|
95
|
+
// to avoid the browser making miscalculations when applying the transform.
|
|
96
|
+
const widthRemainder = bounds.w % dprMultiple
|
|
97
|
+
const heightRemainder = bounds.h % dprMultiple
|
|
98
|
+
const width = widthRemainder === 0 ? bounds.w : bounds.w + (dprMultiple - widthRemainder)
|
|
99
|
+
const height = heightRemainder === 0 ? bounds.h : bounds.h + (dprMultiple - heightRemainder)
|
|
93
100
|
|
|
94
101
|
if (width !== prev.width || height !== prev.height) {
|
|
95
|
-
setStyleProperty(containerRef.current, 'width', width + 'px')
|
|
96
|
-
setStyleProperty(containerRef.current, 'height', height + 'px')
|
|
97
|
-
setStyleProperty(bgContainerRef.current, 'width', width + 'px')
|
|
98
|
-
setStyleProperty(bgContainerRef.current, 'height', height + 'px')
|
|
102
|
+
setStyleProperty(containerRef.current, 'width', Math.max(width, dprMultiple) + 'px')
|
|
103
|
+
setStyleProperty(containerRef.current, 'height', Math.max(height, dprMultiple) + 'px')
|
|
104
|
+
setStyleProperty(bgContainerRef.current, 'width', Math.max(width, dprMultiple) + 'px')
|
|
105
|
+
setStyleProperty(bgContainerRef.current, 'height', Math.max(height, dprMultiple) + 'px')
|
|
99
106
|
prev.width = width
|
|
100
107
|
prev.height = height
|
|
101
108
|
}
|
|
@@ -184,10 +191,7 @@ export const InnerShape = memo(
|
|
|
184
191
|
[util, shape.id]
|
|
185
192
|
)
|
|
186
193
|
},
|
|
187
|
-
(prev, next) =>
|
|
188
|
-
prev.shape.props === next.shape.props &&
|
|
189
|
-
prev.shape.meta === next.shape.meta &&
|
|
190
|
-
prev.util === next.util
|
|
194
|
+
(prev, next) => areShapesContentEqual(prev.shape, next.shape) && prev.util === next.util
|
|
191
195
|
)
|
|
192
196
|
|
|
193
197
|
export const InnerShapeBackground = memo(
|
|
@@ -22,6 +22,7 @@ import { Vec } from '../../primitives/Vec'
|
|
|
22
22
|
import { toDomPrecision } from '../../primitives/utils'
|
|
23
23
|
import { debugFlags } from '../../utils/debug-flags'
|
|
24
24
|
import { setStyleProperty } from '../../utils/dom'
|
|
25
|
+
import { nearestMultiple } from '../../utils/nearestMultiple'
|
|
25
26
|
import { GeometryDebuggingView } from '../GeometryDebuggingView'
|
|
26
27
|
import { LiveCollaborators } from '../LiveCollaborators'
|
|
27
28
|
import { MenuClickCapture } from '../MenuClickCapture'
|
|
@@ -168,6 +169,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|
|
168
169
|
<SnapIndicatorWrapper />
|
|
169
170
|
<SelectionForegroundWrapper />
|
|
170
171
|
<HandlesWrapper />
|
|
172
|
+
<OverlaysWrapper />
|
|
171
173
|
<LiveCollaborators />
|
|
172
174
|
</div>
|
|
173
175
|
</div>
|
|
@@ -372,14 +374,33 @@ function HandleWrapper({
|
|
|
372
374
|
)
|
|
373
375
|
}
|
|
374
376
|
|
|
377
|
+
function OverlaysWrapper() {
|
|
378
|
+
const { Overlays } = useEditorComponents()
|
|
379
|
+
if (!Overlays) return null
|
|
380
|
+
return (
|
|
381
|
+
<div className="tl-custom-overlays tl-overlays__item">
|
|
382
|
+
<Overlays />
|
|
383
|
+
</div>
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
|
|
375
387
|
function ShapesWithSVGs() {
|
|
376
388
|
const editor = useEditor()
|
|
377
389
|
|
|
378
390
|
const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
|
|
379
391
|
|
|
392
|
+
const dprMultiple = useValue(
|
|
393
|
+
'dpr multiple',
|
|
394
|
+
() =>
|
|
395
|
+
// dprMultiple is the smallest number we can multiply dpr by to get an integer
|
|
396
|
+
// it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
|
|
397
|
+
nearestMultiple(Math.floor(editor.getInstanceState().devicePixelRatio * 100) / 100),
|
|
398
|
+
[editor]
|
|
399
|
+
)
|
|
400
|
+
|
|
380
401
|
return renderingShapes.map((result) => (
|
|
381
402
|
<Fragment key={result.id + '_fragment'}>
|
|
382
|
-
<Shape {...result} />
|
|
403
|
+
<Shape {...result} dprMultiple={dprMultiple} />
|
|
383
404
|
<DebugSvgCopy id={result.id} mode="iframe" />
|
|
384
405
|
</Fragment>
|
|
385
406
|
))
|
|
@@ -414,10 +435,19 @@ function ShapesToDisplay() {
|
|
|
414
435
|
|
|
415
436
|
const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
|
|
416
437
|
|
|
438
|
+
const dprMultiple = useValue(
|
|
439
|
+
'dpr multiple',
|
|
440
|
+
() =>
|
|
441
|
+
// dprMultiple is the smallest number we can multiply dpr by to get an integer
|
|
442
|
+
// it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
|
|
443
|
+
nearestMultiple(Math.floor(editor.getInstanceState().devicePixelRatio * 100) / 100),
|
|
444
|
+
[editor]
|
|
445
|
+
)
|
|
446
|
+
|
|
417
447
|
return (
|
|
418
448
|
<>
|
|
419
449
|
{renderingShapes.map((result) => (
|
|
420
|
-
<Shape key={result.id + '_shape'} {...result} />
|
|
450
|
+
<Shape key={result.id + '_shape'} {...result} dprMultiple={dprMultiple} />
|
|
421
451
|
))}
|
|
422
452
|
{tlenv.isSafari && <ReflowIfNeeded />}
|
|
423
453
|
</>
|
|
@@ -9,13 +9,21 @@ import { useEditorComponents } from '../../hooks/useEditorComponents'
|
|
|
9
9
|
import { OptionalErrorBoundary } from '../ErrorBoundary'
|
|
10
10
|
|
|
11
11
|
// need an extra layer of indirection here to allow hooks to be used inside the indicator render
|
|
12
|
-
const EvenInnererIndicator = memo(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
const EvenInnererIndicator = memo(
|
|
13
|
+
({ shape, util }: { shape: TLShape; util: ShapeUtil<any> }) => {
|
|
14
|
+
return useStateTracking('Indicator: ' + shape.type, () =>
|
|
15
|
+
// always fetch the latest shape from the store even if the props/meta have not changed, to avoid
|
|
16
|
+
// calling the render method with stale data.
|
|
17
|
+
util.indicator(util.editor.store.unsafeGetWithoutCapture(shape.id) as TLShape)
|
|
18
|
+
)
|
|
19
|
+
},
|
|
20
|
+
(prevProps, nextProps) => {
|
|
21
|
+
return (
|
|
22
|
+
prevProps.shape.props === nextProps.shape.props &&
|
|
23
|
+
prevProps.shape.meta === nextProps.shape.meta
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
)
|
|
19
27
|
|
|
20
28
|
const InnerIndicator = memo(({ editor, id }: { editor: Editor; id: TLShapeId }) => {
|
|
21
29
|
const shape = useValue('shape for indicator', () => editor.store.get(id), [editor, id])
|
|
@@ -61,13 +69,14 @@ export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
|
|
|
61
69
|
useQuickReactor(
|
|
62
70
|
'indicator transform',
|
|
63
71
|
() => {
|
|
72
|
+
if (hidden) return
|
|
64
73
|
const elm = rIndicator.current
|
|
65
74
|
if (!elm) return
|
|
66
75
|
const pageTransform = editor.getShapePageTransform(shapeId)
|
|
67
76
|
if (!pageTransform) return
|
|
68
77
|
elm.style.setProperty('transform', pageTransform.toCssString())
|
|
69
78
|
},
|
|
70
|
-
[editor, shapeId]
|
|
79
|
+
[editor, shapeId, hidden]
|
|
71
80
|
)
|
|
72
81
|
|
|
73
82
|
useLayoutEffect(() => {
|
|
@@ -52,7 +52,7 @@ beforeEach(() => {
|
|
|
52
52
|
shapeUtils: [CustomShape],
|
|
53
53
|
bindingUtils: [],
|
|
54
54
|
tools: [],
|
|
55
|
-
store: createTLStore({ shapeUtils: [CustomShape] }),
|
|
55
|
+
store: createTLStore({ shapeUtils: [CustomShape], bindingUtils: [] }),
|
|
56
56
|
getContainer: () => document.body,
|
|
57
57
|
})
|
|
58
58
|
editor.setCameraOptions({ isLocked: true })
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -129,6 +129,7 @@ import { Group2d } from '../primitives/geometry/Group2d'
|
|
|
129
129
|
import { intersectPolygonPolygon } from '../primitives/intersect'
|
|
130
130
|
import { PI, approximately, areAnglesCompatible, clamp, pointInPolygon } from '../primitives/utils'
|
|
131
131
|
import { ReadonlySharedStyleMap, SharedStyle, SharedStyleMap } from '../utils/SharedStylesMap'
|
|
132
|
+
import { areShapesContentEqual } from '../utils/areShapesContentEqual'
|
|
132
133
|
import { dataUrlToFile } from '../utils/assets'
|
|
133
134
|
import { debugFlags } from '../utils/debug-flags'
|
|
134
135
|
import {
|
|
@@ -325,7 +326,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
325
326
|
this.options = { ...defaultTldrawOptions, ...options }
|
|
326
327
|
|
|
327
328
|
this.store = store
|
|
328
|
-
this.disposables.add(this.store.dispose.bind(this.store))
|
|
329
329
|
this.history = new HistoryManager<TLRecord>({
|
|
330
330
|
store,
|
|
331
331
|
annotateError: (error) => {
|
|
@@ -955,6 +955,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
955
955
|
dispose() {
|
|
956
956
|
this.disposables.forEach((dispose) => dispose())
|
|
957
957
|
this.disposables.clear()
|
|
958
|
+
this.store.dispose()
|
|
958
959
|
this.isDisposed = true
|
|
959
960
|
}
|
|
960
961
|
|
|
@@ -2275,13 +2276,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2275
2276
|
setEditingShape(shape: TLShapeId | TLShape | null): this {
|
|
2276
2277
|
const id = typeof shape === 'string' ? shape : (shape?.id ?? null)
|
|
2277
2278
|
this.setRichTextEditor(null)
|
|
2278
|
-
|
|
2279
|
+
const prevEditingShapeId = this.getEditingShapeId()
|
|
2280
|
+
if (id !== prevEditingShapeId) {
|
|
2279
2281
|
if (id) {
|
|
2280
2282
|
const shape = this.getShape(id)
|
|
2281
2283
|
if (shape && this.getShapeUtil(shape).canEdit(shape)) {
|
|
2282
2284
|
this.run(
|
|
2283
2285
|
() => {
|
|
2284
2286
|
this._updateCurrentPageState({ editingShapeId: id })
|
|
2287
|
+
if (prevEditingShapeId) {
|
|
2288
|
+
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2289
|
+
if (prevEditingShape) {
|
|
2290
|
+
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
this.getShapeUtil(shape).onEditStart?.(shape)
|
|
2285
2294
|
},
|
|
2286
2295
|
{ history: 'ignore' }
|
|
2287
2296
|
)
|
|
@@ -2294,6 +2303,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
2294
2303
|
() => {
|
|
2295
2304
|
this._updateCurrentPageState({ editingShapeId: null })
|
|
2296
2305
|
this._currentRichTextEditor.set(null)
|
|
2306
|
+
if (prevEditingShapeId) {
|
|
2307
|
+
const prevEditingShape = this.getShape(prevEditingShapeId)
|
|
2308
|
+
if (prevEditingShape) {
|
|
2309
|
+
this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape)
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2297
2312
|
},
|
|
2298
2313
|
{ history: 'ignore' }
|
|
2299
2314
|
)
|
|
@@ -4574,7 +4589,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4574
4589
|
this.fonts.trackFontsForShape(shape)
|
|
4575
4590
|
return this.getShapeUtil(shape).getGeometry(shape, opts)
|
|
4576
4591
|
},
|
|
4577
|
-
{ areRecordsEqual:
|
|
4592
|
+
{ areRecordsEqual: areShapesContentEqual }
|
|
4578
4593
|
)
|
|
4579
4594
|
}
|
|
4580
4595
|
return this._shapeGeometryCaches[context].get(
|
|
@@ -4622,9 +4637,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4622
4637
|
|
|
4623
4638
|
/** @internal */
|
|
4624
4639
|
@computed private _getShapeHandlesCache(): ComputedCache<TLHandle[] | undefined, TLShape> {
|
|
4625
|
-
return this.store.createComputedCache(
|
|
4626
|
-
|
|
4627
|
-
|
|
4640
|
+
return this.store.createComputedCache(
|
|
4641
|
+
'handles',
|
|
4642
|
+
(shape) => {
|
|
4643
|
+
return this.getShapeUtil(shape).getHandles?.(shape)
|
|
4644
|
+
},
|
|
4645
|
+
{
|
|
4646
|
+
areRecordsEqual: areShapesContentEqual,
|
|
4647
|
+
}
|
|
4648
|
+
)
|
|
4628
4649
|
}
|
|
4629
4650
|
|
|
4630
4651
|
/**
|
|
@@ -5845,9 +5866,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5845
5866
|
@computed
|
|
5846
5867
|
private _getBindingsIndexCache() {
|
|
5847
5868
|
const index = bindingsIndex(this)
|
|
5848
|
-
return this.store.createComputedCache<TLBinding[], TLShape>(
|
|
5849
|
-
|
|
5850
|
-
|
|
5869
|
+
return this.store.createComputedCache<TLBinding[], TLShape>(
|
|
5870
|
+
'bindingsIndex',
|
|
5871
|
+
(shape) => {
|
|
5872
|
+
return index.get().get(shape.id)
|
|
5873
|
+
},
|
|
5874
|
+
// we can ignore the shape equality check here because the index is
|
|
5875
|
+
// computed incrementally based on what bindings are in the store
|
|
5876
|
+
{ areRecordsEqual: () => true }
|
|
5877
|
+
)
|
|
5851
5878
|
}
|
|
5852
5879
|
|
|
5853
5880
|
/**
|
|
@@ -10214,7 +10241,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10214
10241
|
|
|
10215
10242
|
// If the camera behavior is "zoom" and the ctrl key is pressed, then pan;
|
|
10216
10243
|
// If the camera behavior is "pan" and the ctrl key is not pressed, then zoom
|
|
10217
|
-
if (
|
|
10244
|
+
if (info.ctrlKey) behavior = wheelBehavior === 'pan' ? 'zoom' : 'pan'
|
|
10218
10245
|
|
|
10219
10246
|
switch (behavior) {
|
|
10220
10247
|
case 'zoom': {
|
|
@@ -10330,12 +10357,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10330
10357
|
if (this.inputs.isPanning && this.inputs.isPointing) {
|
|
10331
10358
|
// Handle spacebar / middle mouse button panning
|
|
10332
10359
|
const { currentScreenPoint, previousScreenPoint } = this.inputs
|
|
10333
|
-
const { panSpeed } = cameraOptions
|
|
10334
10360
|
const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
|
|
10335
|
-
this.setCamera(
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
)
|
|
10361
|
+
this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
|
|
10362
|
+
immediate: true,
|
|
10363
|
+
})
|
|
10339
10364
|
this.maybeTrackPerformance('Panning')
|
|
10340
10365
|
return
|
|
10341
10366
|
}
|