@tldraw/editor 5.4.0-canary.e10ff229bb14 → 5.4.0-next.09eca5dee30d
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.js +1 -1
- package/dist-cjs/lib/editor/Editor.js +14 -17
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +3 -7
- package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +21 -63
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/normalizeWheel.js +0 -2
- package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/editor/Editor.mjs +14 -17
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +3 -7
- package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +21 -63
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/normalizeWheel.mjs +0 -2
- package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/lib/editor/Editor.ts +24 -25
- package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +3 -7
- package/src/lib/primitives/geometry/Group2d.ts +27 -63
- package/src/lib/utils/normalizeWheel.ts +0 -6
- package/src/version.ts +3 -3
- package/src/lib/utils/normalizeWheel.test.ts +0 -39
|
@@ -19,13 +19,9 @@ function deriveShapeIdsInCurrentPage(store, getCurrentPageId) {
|
|
|
19
19
|
function fromScratch() {
|
|
20
20
|
const currentPageId = getCurrentPageId();
|
|
21
21
|
lastPageId = currentPageId;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
result.add(id);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return result;
|
|
22
|
+
return new Set(
|
|
23
|
+
[...shapesIndex.get()].filter((id) => isShapeInPage(store, currentPageId, store.get(id)))
|
|
24
|
+
);
|
|
29
25
|
}
|
|
30
26
|
return computed("_shapeIdsInCurrentPage", (prevValue, lastComputedEpoch) => {
|
|
31
27
|
if (isUninitialized(prevValue)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/derivations/shapeIdsInCurrentPage.ts"],
|
|
4
|
-
"sourcesContent": ["import { computed, isUninitialized, RESET_VALUE, withDiff } from '@tldraw/state'\nimport { IncrementalSetConstructor } from '@tldraw/store'\nimport {\n\tisPageId,\n\tisShape,\n\tisShapeId,\n\tTLPageId,\n\tTLShape,\n\tTLShapeId,\n\tTLStore,\n} from '@tldraw/tlschema'\n\n/**\n * Get whether a shape is in the current page.\n *\n * @param store - The tldraw store.\n * @param pageId - The id of the page to check.\n * @param shape - The the shape to check.\n */\nconst isShapeInPage = (store: TLStore, pageId: TLPageId, shape: TLShape): boolean => {\n\twhile (!isPageId(shape.parentId)) {\n\t\tconst parent = store.get(shape.parentId)\n\t\tif (!parent) return false\n\t\tshape = parent\n\t}\n\n\treturn shape.parentId === pageId\n}\n\n/**\n * A derivation that returns a list of shape ids in the current page.\n *\n * @param store - The tldraw store.\n * @param getCurrentPageId - A function that returns the current page id.\n */\nexport function deriveShapeIdsInCurrentPage(store: TLStore, getCurrentPageId: () => TLPageId) {\n\tconst shapesIndex = store.query.ids('shape')\n\tlet lastPageId: null | TLPageId = null\n\tfunction fromScratch() {\n\t\tconst currentPageId = getCurrentPageId()\n\t\tlastPageId = currentPageId\n\t\
|
|
5
|
-
"mappings": "AAAA,SAAS,UAAU,iBAAiB,aAAa,gBAAgB;AACjE,SAAS,iCAAiC;AAC1C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAKM;AASP,MAAM,gBAAgB,CAAC,OAAgB,QAAkB,UAA4B;AACpF,SAAO,CAAC,SAAS,MAAM,QAAQ,GAAG;AACjC,UAAM,SAAS,MAAM,IAAI,MAAM,QAAQ;AACvC,QAAI,CAAC,OAAQ,QAAO;AACpB,YAAQ;AAAA,EACT;AAEA,SAAO,MAAM,aAAa;AAC3B;AAQO,SAAS,4BAA4B,OAAgB,kBAAkC;AAC7F,QAAM,cAAc,MAAM,MAAM,IAAI,OAAO;AAC3C,MAAI,aAA8B;AAClC,WAAS,cAAc;AACtB,UAAM,gBAAgB,iBAAiB;AACvC,iBAAa;AACb,
|
|
4
|
+
"sourcesContent": ["import { computed, isUninitialized, RESET_VALUE, withDiff } from '@tldraw/state'\nimport { IncrementalSetConstructor } from '@tldraw/store'\nimport {\n\tisPageId,\n\tisShape,\n\tisShapeId,\n\tTLPageId,\n\tTLShape,\n\tTLShapeId,\n\tTLStore,\n} from '@tldraw/tlschema'\n\n/**\n * Get whether a shape is in the current page.\n *\n * @param store - The tldraw store.\n * @param pageId - The id of the page to check.\n * @param shape - The the shape to check.\n */\nconst isShapeInPage = (store: TLStore, pageId: TLPageId, shape: TLShape): boolean => {\n\twhile (!isPageId(shape.parentId)) {\n\t\tconst parent = store.get(shape.parentId)\n\t\tif (!parent) return false\n\t\tshape = parent\n\t}\n\n\treturn shape.parentId === pageId\n}\n\n/**\n * A derivation that returns a list of shape ids in the current page.\n *\n * @param store - The tldraw store.\n * @param getCurrentPageId - A function that returns the current page id.\n */\nexport function deriveShapeIdsInCurrentPage(store: TLStore, getCurrentPageId: () => TLPageId) {\n\tconst shapesIndex = store.query.ids('shape')\n\tlet lastPageId: null | TLPageId = null\n\tfunction fromScratch() {\n\t\tconst currentPageId = getCurrentPageId()\n\t\tlastPageId = currentPageId\n\t\treturn new Set(\n\t\t\t[...shapesIndex.get()].filter((id) => isShapeInPage(store, currentPageId, store.get(id)!))\n\t\t)\n\t}\n\treturn computed<Set<TLShapeId>>('_shapeIdsInCurrentPage', (prevValue, lastComputedEpoch) => {\n\t\tif (isUninitialized(prevValue)) {\n\t\t\treturn fromScratch()\n\t\t}\n\n\t\tconst currentPageId = getCurrentPageId()\n\n\t\tif (currentPageId !== lastPageId) {\n\t\t\treturn fromScratch()\n\t\t}\n\n\t\tconst diff = store.history.getDiffSince(lastComputedEpoch)\n\n\t\tif (diff === RESET_VALUE) {\n\t\t\treturn fromScratch()\n\t\t}\n\n\t\tconst builder = new IncrementalSetConstructor<TLShapeId>(\n\t\t\tprevValue\n\t\t) as IncrementalSetConstructor<TLShapeId>\n\n\t\tfor (const changes of diff) {\n\t\t\tfor (const record of Object.values(changes.added)) {\n\t\t\t\tif (isShape(record) && isShapeInPage(store, currentPageId, record)) {\n\t\t\t\t\tbuilder.add(record.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const [_from, to] of Object.values(changes.updated)) {\n\t\t\t\tif (isShape(to)) {\n\t\t\t\t\tif (isShapeInPage(store, currentPageId, to)) {\n\t\t\t\t\t\tbuilder.add(to.id)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuilder.remove(to.id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const id of Object.keys(changes.removed)) {\n\t\t\t\tif (isShapeId(id)) {\n\t\t\t\t\tbuilder.remove(id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst result = builder.get()\n\t\tif (!result) {\n\t\t\treturn prevValue\n\t\t}\n\n\t\treturn withDiff(result.value, result.diff)\n\t})\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,UAAU,iBAAiB,aAAa,gBAAgB;AACjE,SAAS,iCAAiC;AAC1C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAKM;AASP,MAAM,gBAAgB,CAAC,OAAgB,QAAkB,UAA4B;AACpF,SAAO,CAAC,SAAS,MAAM,QAAQ,GAAG;AACjC,UAAM,SAAS,MAAM,IAAI,MAAM,QAAQ;AACvC,QAAI,CAAC,OAAQ,QAAO;AACpB,YAAQ;AAAA,EACT;AAEA,SAAO,MAAM,aAAa;AAC3B;AAQO,SAAS,4BAA4B,OAAgB,kBAAkC;AAC7F,QAAM,cAAc,MAAM,MAAM,IAAI,OAAO;AAC3C,MAAI,aAA8B;AAClC,WAAS,cAAc;AACtB,UAAM,gBAAgB,iBAAiB;AACvC,iBAAa;AACb,WAAO,IAAI;AAAA,MACV,CAAC,GAAG,YAAY,IAAI,CAAC,EAAE,OAAO,CAAC,OAAO,cAAc,OAAO,eAAe,MAAM,IAAI,EAAE,CAAE,CAAC;AAAA,IAC1F;AAAA,EACD;AACA,SAAO,SAAyB,0BAA0B,CAAC,WAAW,sBAAsB;AAC3F,QAAI,gBAAgB,SAAS,GAAG;AAC/B,aAAO,YAAY;AAAA,IACpB;AAEA,UAAM,gBAAgB,iBAAiB;AAEvC,QAAI,kBAAkB,YAAY;AACjC,aAAO,YAAY;AAAA,IACpB;AAEA,UAAM,OAAO,MAAM,QAAQ,aAAa,iBAAiB;AAEzD,QAAI,SAAS,aAAa;AACzB,aAAO,YAAY;AAAA,IACpB;AAEA,UAAM,UAAU,IAAI;AAAA,MACnB;AAAA,IACD;AAEA,eAAW,WAAW,MAAM;AAC3B,iBAAW,UAAU,OAAO,OAAO,QAAQ,KAAK,GAAG;AAClD,YAAI,QAAQ,MAAM,KAAK,cAAc,OAAO,eAAe,MAAM,GAAG;AACnE,kBAAQ,IAAI,OAAO,EAAE;AAAA,QACtB;AAAA,MACD;AAEA,iBAAW,CAAC,OAAO,EAAE,KAAK,OAAO,OAAO,QAAQ,OAAO,GAAG;AACzD,YAAI,QAAQ,EAAE,GAAG;AAChB,cAAI,cAAc,OAAO,eAAe,EAAE,GAAG;AAC5C,oBAAQ,IAAI,GAAG,EAAE;AAAA,UAClB,OAAO;AACN,oBAAQ,OAAO,GAAG,EAAE;AAAA,UACrB;AAAA,QACD;AAAA,MACD;AAEA,iBAAW,MAAM,OAAO,KAAK,QAAQ,OAAO,GAAG;AAC9C,YAAI,UAAU,EAAE,GAAG;AAClB,kBAAQ,OAAO,EAAE;AAAA,QAClB;AAAA,MACD;AAAA,IACD;AAEA,UAAM,SAAS,QAAQ,IAAI;AAC3B,QAAI,CAAC,QAAQ;AACZ,aAAO;AAAA,IACR;AAEA,WAAO,SAAS,OAAO,OAAO,OAAO,IAAI;AAAA,EAC1C,CAAC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EMPTY_ARRAY } from "@tldraw/state";
|
|
1
2
|
import { assert, invLerp, lerp } from "@tldraw/utils";
|
|
2
3
|
import { Box } from "../Box.mjs";
|
|
3
4
|
import { Vec } from "../Vec.mjs";
|
|
@@ -23,15 +24,7 @@ class Group2d extends Geometry2d {
|
|
|
23
24
|
}
|
|
24
25
|
getVertices(filters) {
|
|
25
26
|
if (this.isExcludedByFilter(filters)) return [];
|
|
26
|
-
|
|
27
|
-
for (const child of this.children) {
|
|
28
|
-
if (child.isExcludedByFilter(filters)) continue;
|
|
29
|
-
const childVertices = child.getVertices(filters);
|
|
30
|
-
for (let i = 0, n = childVertices.length; i < n; i++) {
|
|
31
|
-
vertices.push(childVertices[i]);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return vertices;
|
|
27
|
+
return this.children.filter((c) => !c.isExcludedByFilter(filters)).flatMap((c) => c.getVertices(filters));
|
|
35
28
|
}
|
|
36
29
|
nearestPoint(point, filters) {
|
|
37
30
|
let dist = Infinity;
|
|
@@ -66,73 +59,38 @@ class Group2d extends Geometry2d {
|
|
|
66
59
|
return smallestDistance;
|
|
67
60
|
}
|
|
68
61
|
hitTestPoint(point, margin, hitInside, filters = Geometry2dFilters.EXCLUDE_LABELS) {
|
|
69
|
-
|
|
70
|
-
if (child.isExcludedByFilter(filters)) continue;
|
|
71
|
-
if (child.hitTestPoint(point, margin, hitInside)) return true;
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
62
|
+
return !!this.children.filter((c) => !c.isExcludedByFilter(filters)).find((c) => c.hitTestPoint(point, margin, hitInside));
|
|
74
63
|
}
|
|
75
64
|
hitTestLineSegment(A, B, zoom, filters = Geometry2dFilters.EXCLUDE_LABELS) {
|
|
76
|
-
|
|
77
|
-
if (child.isExcludedByFilter(filters)) continue;
|
|
78
|
-
if (child.hitTestLineSegment(A, B, zoom)) return true;
|
|
79
|
-
}
|
|
80
|
-
return false;
|
|
65
|
+
return !!this.children.filter((c) => !c.isExcludedByFilter(filters)).find((c) => c.hitTestLineSegment(A, B, zoom));
|
|
81
66
|
}
|
|
82
67
|
intersectLineSegment(A, B, filters) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
88
|
-
result.push(hits[i]);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return result;
|
|
68
|
+
return this.children.flatMap((child) => {
|
|
69
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY;
|
|
70
|
+
return child.intersectLineSegment(A, B, filters);
|
|
71
|
+
});
|
|
92
72
|
}
|
|
93
73
|
intersectCircle(center, radius, filters) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
99
|
-
result.push(hits[i]);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return result;
|
|
74
|
+
return this.children.flatMap((child) => {
|
|
75
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY;
|
|
76
|
+
return child.intersectCircle(center, radius, filters);
|
|
77
|
+
});
|
|
103
78
|
}
|
|
104
79
|
getBoundsVertices() {
|
|
105
80
|
if (this.excludeFromShapeBounds) return [];
|
|
106
|
-
|
|
107
|
-
for (const child of this.children) {
|
|
108
|
-
const childVertices = child.getBoundsVertices();
|
|
109
|
-
for (let i = 0, n = childVertices.length; i < n; i++) {
|
|
110
|
-
vertices.push(childVertices[i]);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return vertices;
|
|
81
|
+
return this.children.flatMap((child) => child.getBoundsVertices());
|
|
114
82
|
}
|
|
115
83
|
intersectPolygon(polygon, filters) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
121
|
-
result.push(hits[i]);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return result;
|
|
84
|
+
return this.children.flatMap((child) => {
|
|
85
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY;
|
|
86
|
+
return child.intersectPolygon(polygon, filters);
|
|
87
|
+
});
|
|
125
88
|
}
|
|
126
89
|
intersectPolyline(polyline, filters) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
132
|
-
result.push(hits[i]);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return result;
|
|
90
|
+
return this.children.flatMap((child) => {
|
|
91
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY;
|
|
92
|
+
return child.intersectPolyline(polyline, filters);
|
|
93
|
+
});
|
|
136
94
|
}
|
|
137
95
|
interpolateAlongEdge(t, filters) {
|
|
138
96
|
const totalLength = this.getLength(filters);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/primitives/geometry/Group2d.ts"],
|
|
4
|
-
"sourcesContent": ["import { assert, invLerp, lerp } from '@tldraw/utils'\nimport { Box } from '../Box'\nimport { Mat } from '../Mat'\nimport { Vec, VecLike } from '../Vec'\nimport { Geometry2d, Geometry2dFilters, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Group2d extends Geometry2d {\n\tchildren: Geometry2d[] = []\n\tignoredChildren: Geometry2d[] = []\n\n\tconstructor(\n\t\tconfig: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & {\n\t\t\tchildren: Geometry2d[]\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true, isFilled: false })\n\n\t\tconst addChildren = (children: Geometry2d[]) => {\n\t\t\tfor (const child of children) {\n\t\t\t\tif (child instanceof Group2d) {\n\t\t\t\t\taddChildren(child.children)\n\t\t\t\t} else if (child.ignore) {\n\t\t\t\t\tthis.ignoredChildren.push(child)\n\t\t\t\t} else {\n\t\t\t\t\tthis.children.push(child)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\taddChildren(config.children)\n\n\t\tif (this.children.length === 0) throw Error('Group2d must have at least one child')\n\t}\n\n\toverride getVertices(filters: Geometry2dFilters): Vec[] {\n\t\tif (this.isExcludedByFilter(filters)) return []\n\t\
|
|
5
|
-
"mappings": "AAAA,SAAS,QAAQ,SAAS,YAAY;AACtC,SAAS,WAAW;AAEpB,SAAS,WAAoB;AAC7B,SAAS,YAAY,yBAA4C;AAG1D,MAAM,gBAAgB,WAAW;AAAA,EACvC,WAAyB,CAAC;AAAA,EAC1B,kBAAgC,CAAC;AAAA,EAEjC,YACC,QAGC;AACD,UAAM,EAAE,GAAG,QAAQ,UAAU,MAAM,UAAU,MAAM,CAAC;AAEpD,UAAM,cAAc,CAAC,aAA2B;AAC/C,iBAAW,SAAS,UAAU;AAC7B,YAAI,iBAAiB,SAAS;AAC7B,sBAAY,MAAM,QAAQ;AAAA,QAC3B,WAAW,MAAM,QAAQ;AACxB,eAAK,gBAAgB,KAAK,KAAK;AAAA,QAChC,OAAO;AACN,eAAK,SAAS,KAAK,KAAK;AAAA,QACzB;AAAA,MACD;AAAA,IACD;AAEA,gBAAY,OAAO,QAAQ;AAE3B,QAAI,KAAK,SAAS,WAAW,EAAG,OAAM,MAAM,sCAAsC;AAAA,EACnF;AAAA,EAES,YAAY,SAAmC;AACvD,QAAI,KAAK,mBAAmB,OAAO,EAAG,QAAO,CAAC;AAC9C,
|
|
4
|
+
"sourcesContent": ["import { EMPTY_ARRAY } from '@tldraw/state'\nimport { assert, invLerp, lerp } from '@tldraw/utils'\nimport { Box } from '../Box'\nimport { Mat } from '../Mat'\nimport { Vec, VecLike } from '../Vec'\nimport { Geometry2d, Geometry2dFilters, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Group2d extends Geometry2d {\n\tchildren: Geometry2d[] = []\n\tignoredChildren: Geometry2d[] = []\n\n\tconstructor(\n\t\tconfig: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & {\n\t\t\tchildren: Geometry2d[]\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true, isFilled: false })\n\n\t\tconst addChildren = (children: Geometry2d[]) => {\n\t\t\tfor (const child of children) {\n\t\t\t\tif (child instanceof Group2d) {\n\t\t\t\t\taddChildren(child.children)\n\t\t\t\t} else if (child.ignore) {\n\t\t\t\t\tthis.ignoredChildren.push(child)\n\t\t\t\t} else {\n\t\t\t\t\tthis.children.push(child)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\taddChildren(config.children)\n\n\t\tif (this.children.length === 0) throw Error('Group2d must have at least one child')\n\t}\n\n\toverride getVertices(filters: Geometry2dFilters): Vec[] {\n\t\tif (this.isExcludedByFilter(filters)) return []\n\t\treturn this.children\n\t\t\t.filter((c) => !c.isExcludedByFilter(filters))\n\t\t\t.flatMap((c) => c.getVertices(filters))\n\t}\n\n\toverride nearestPoint(point: VecLike, filters?: Geometry2dFilters): Vec {\n\t\tlet dist = Infinity\n\t\tlet nearest: Vec | undefined\n\n\t\tconst { children } = this\n\n\t\tif (children.length === 0) {\n\t\t\tthrow Error('no children')\n\t\t}\n\n\t\tlet p: Vec\n\t\tlet d: number\n\t\tfor (const child of children) {\n\t\t\tif (child.isExcludedByFilter(filters)) continue\n\t\t\tp = child.nearestPoint(point, filters)\n\t\t\td = Vec.Dist2(p, point)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = p\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\toverride distanceToPoint(point: VecLike, hitInside = false, filters?: Geometry2dFilters) {\n\t\tlet smallestDistance = Infinity\n\t\tfor (const child of this.children) {\n\t\t\tif (child.isExcludedByFilter(filters)) continue\n\t\t\tconst distance = child.distanceToPoint(point, hitInside, filters)\n\t\t\tif (distance < smallestDistance) {\n\t\t\t\tsmallestDistance = distance\n\t\t\t}\n\t\t}\n\t\treturn smallestDistance\n\t}\n\n\toverride hitTestPoint(\n\t\tpoint: VecLike,\n\t\tmargin: number,\n\t\thitInside: boolean,\n\t\tfilters = Geometry2dFilters.EXCLUDE_LABELS\n\t): boolean {\n\t\treturn !!this.children\n\t\t\t.filter((c) => !c.isExcludedByFilter(filters))\n\t\t\t.find((c) => c.hitTestPoint(point, margin, hitInside))\n\t}\n\n\toverride hitTestLineSegment(\n\t\tA: VecLike,\n\t\tB: VecLike,\n\t\tzoom: number,\n\t\tfilters = Geometry2dFilters.EXCLUDE_LABELS\n\t): boolean {\n\t\treturn !!this.children\n\t\t\t.filter((c) => !c.isExcludedByFilter(filters))\n\t\t\t.find((c) => c.hitTestLineSegment(A, B, zoom))\n\t}\n\n\toverride intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\treturn this.children.flatMap((child) => {\n\t\t\tif (child.isExcludedByFilter(filters)) return EMPTY_ARRAY\n\t\t\treturn child.intersectLineSegment(A, B, filters)\n\t\t})\n\t}\n\n\toverride intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {\n\t\treturn this.children.flatMap((child) => {\n\t\t\tif (child.isExcludedByFilter(filters)) return EMPTY_ARRAY\n\t\t\treturn child.intersectCircle(center, radius, filters)\n\t\t})\n\t}\n\n\toverride getBoundsVertices(): Vec[] {\n\t\tif (this.excludeFromShapeBounds) return []\n\t\treturn this.children.flatMap((child) => child.getBoundsVertices())\n\t}\n\n\toverride intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters) {\n\t\treturn this.children.flatMap((child) => {\n\t\t\tif (child.isExcludedByFilter(filters)) return EMPTY_ARRAY\n\t\t\treturn child.intersectPolygon(polygon, filters)\n\t\t})\n\t}\n\n\toverride intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters) {\n\t\treturn this.children.flatMap((child) => {\n\t\t\tif (child.isExcludedByFilter(filters)) return EMPTY_ARRAY\n\t\t\treturn child.intersectPolyline(polyline, filters)\n\t\t})\n\t}\n\n\toverride interpolateAlongEdge(t: number, filters?: Geometry2dFilters): Vec {\n\t\tconst totalLength = this.getLength(filters)\n\n\t\tconst distanceToTravel = t * totalLength\n\t\tlet distanceTraveled = 0\n\t\tfor (const child of this.children) {\n\t\t\tif (child.isExcludedByFilter(filters)) continue\n\t\t\tconst childLength = child.length\n\t\t\tconst newDistanceTraveled = distanceTraveled + childLength\n\t\t\tif (newDistanceTraveled >= distanceToTravel) {\n\t\t\t\treturn child.interpolateAlongEdge(\n\t\t\t\t\tinvLerp(distanceTraveled, newDistanceTraveled, distanceToTravel),\n\t\t\t\t\tfilters\n\t\t\t\t)\n\t\t\t}\n\t\t\tdistanceTraveled = newDistanceTraveled\n\t\t}\n\n\t\treturn this.children[this.children.length - 1].interpolateAlongEdge(1, filters)\n\t}\n\n\toverride uninterpolateAlongEdge(point: VecLike, filters?: Geometry2dFilters): number {\n\t\tconst totalLength = this.getLength(filters)\n\n\t\tlet closestChild = null\n\t\tlet closestDistance = Infinity\n\t\tlet distanceTraveled = 0\n\n\t\tfor (const child of this.children) {\n\t\t\tif (child.isExcludedByFilter(filters)) continue\n\t\t\tconst childLength = child.getLength(filters)\n\t\t\tconst newDistanceTraveled = distanceTraveled + childLength\n\n\t\t\tconst distance = child.distanceToPoint(point, false, filters)\n\t\t\tif (distance < closestDistance) {\n\t\t\t\tclosestDistance = distance\n\t\t\t\tclosestChild = {\n\t\t\t\t\tstartLength: distanceTraveled,\n\t\t\t\t\tendLength: newDistanceTraveled,\n\t\t\t\t\tchild,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdistanceTraveled = newDistanceTraveled\n\t\t}\n\n\t\tassert(closestChild)\n\n\t\tconst normalizedDistanceInChild = closestChild.child.uninterpolateAlongEdge(point, filters)\n\t\tconst childTLength = lerp(\n\t\t\tclosestChild.startLength,\n\t\t\tclosestChild.endLength,\n\t\t\tnormalizedDistanceInChild\n\t\t)\n\t\treturn childTLength / totalLength\n\t}\n\n\toverride transform(transform: Mat): Geometry2d {\n\t\treturn new Group2d({\n\t\t\tchildren: this.children.map((c) => c.transform(transform)),\n\t\t\tisLabel: this.isLabel,\n\t\t\tdebugColor: this.debugColor,\n\t\t\tignore: this.ignore,\n\t\t})\n\t}\n\n\tgetArea() {\n\t\t// todo: this is a temporary solution, assuming that the first child defines the group size; we would want to flatten the group and then find the area of the hull polygon\n\t\treturn this.children[0].area\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\t\tfor (const child of this.children) {\n\t\t\tpath += child.toSimpleSvgPath()\n\t\t}\n\n\t\tconst corners = Box.FromPoints(this.boundsVertices).corners\n\t\t// draw just a few pixels around each corner, e.g. an L shape for the bottom left\n\n\t\tfor (let i = 0, n = corners.length; i < n; i++) {\n\t\t\tconst corner = corners[i]\n\t\t\tconst prevCorner = corners[(i - 1 + n) % n]\n\t\t\tconst prevDist = corner.dist(prevCorner)\n\t\t\tconst nextCorner = corners[(i + 1) % n]\n\t\t\tconst nextDist = corner.dist(nextCorner)\n\n\t\t\tconst A = corner.clone().lrp(prevCorner, 4 / prevDist)\n\t\t\tconst B = corner\n\t\t\tconst C = corner.clone().lrp(nextCorner, 4 / nextDist)\n\n\t\t\tpath += `M${A.x},${A.y} L${B.x},${B.y} L${C.x},${C.y} `\n\t\t}\n\t\treturn path\n\t}\n\n\tgetLength(filters?: Geometry2dFilters): number {\n\t\tlet length = 0\n\t\tfor (const child of this.children) {\n\t\t\tif (child.isExcludedByFilter(filters)) continue\n\t\t\tlength += child.length\n\t\t}\n\t\treturn length\n\t}\n\n\tgetSvgPathData(): string {\n\t\treturn this.children.map((c, i) => (c.isLabel ? '' : c.getSvgPathData(i === 0))).join(' ')\n\t}\n\n\toverlapsPolygon(polygon: VecLike[]): boolean {\n\t\treturn this.children.some((child) => child.overlapsPolygon(polygon))\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,8 +2,6 @@ import { tlenv } from "../globals/environment.mjs";
|
|
|
2
2
|
const MAX_ZOOM_STEP = 10;
|
|
3
3
|
function normalizeWheel(event) {
|
|
4
4
|
let { deltaY, deltaX } = event;
|
|
5
|
-
if (!Number.isFinite(deltaX)) deltaX = 0;
|
|
6
|
-
if (!Number.isFinite(deltaY)) deltaY = 0;
|
|
7
5
|
let deltaZ = 0;
|
|
8
6
|
if (event.ctrlKey || event.altKey || event.metaKey) {
|
|
9
7
|
deltaZ = (Math.abs(deltaY) > MAX_ZOOM_STEP ? MAX_ZOOM_STEP * Math.sign(deltaY) : deltaY) / 100;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/normalizeWheel.ts"],
|
|
4
|
-
"sourcesContent": ["import { tlenv } from '../globals/environment'\n\nconst MAX_ZOOM_STEP = 10\n\n/**\n * Normalizes a wheel event, so that the delta values are consistent across different browsers or devices. Adapted from https://stackoverflow.com/a/13650579.\n * @param event - The wheel event to normalize.\n * @returns The normalized wheel event.\n * @internal */\nexport function normalizeWheel(event: WheelEvent | React.WheelEvent<HTMLElement>) {\n\tlet { deltaY, deltaX } = event\n\
|
|
5
|
-
"mappings": "AAAA,SAAS,aAAa;AAEtB,MAAM,gBAAgB;AAOf,SAAS,eAAe,OAAmD;AACjF,MAAI,EAAE,QAAQ,OAAO,IAAI;
|
|
4
|
+
"sourcesContent": ["import { tlenv } from '../globals/environment'\n\nconst MAX_ZOOM_STEP = 10\n\n/**\n * Normalizes a wheel event, so that the delta values are consistent across different browsers or devices. Adapted from https://stackoverflow.com/a/13650579.\n * @param event - The wheel event to normalize.\n * @returns The normalized wheel event.\n * @internal */\nexport function normalizeWheel(event: WheelEvent | React.WheelEvent<HTMLElement>) {\n\tlet { deltaY, deltaX } = event\n\tlet deltaZ = 0\n\n\t// wheeling\n\tif (event.ctrlKey || event.altKey || event.metaKey) {\n\t\tdeltaZ = (Math.abs(deltaY) > MAX_ZOOM_STEP ? MAX_ZOOM_STEP * Math.sign(deltaY) : deltaY) / 100\n\t} else {\n\t\tif (event.shiftKey && !tlenv.isDarwin && !tlenv.isIos) {\n\t\t\tdeltaX = deltaY\n\t\t\tdeltaY = 0\n\t\t}\n\t}\n\n\treturn { x: -deltaX, y: -deltaY, z: -deltaZ }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,aAAa;AAEtB,MAAM,gBAAgB;AAOf,SAAS,eAAe,OAAmD;AACjF,MAAI,EAAE,QAAQ,OAAO,IAAI;AACzB,MAAI,SAAS;AAGb,MAAI,MAAM,WAAW,MAAM,UAAU,MAAM,SAAS;AACnD,cAAU,KAAK,IAAI,MAAM,IAAI,gBAAgB,gBAAgB,KAAK,KAAK,MAAM,IAAI,UAAU;AAAA,EAC5F,OAAO;AACN,QAAI,MAAM,YAAY,CAAC,MAAM,YAAY,CAAC,MAAM,OAAO;AACtD,eAAS;AACT,eAAS;AAAA,IACV;AAAA,EACD;AAEA,SAAO,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,QAAQ,GAAG,CAAC,OAAO;AAC7C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "5.4.0-
|
|
1
|
+
const version = "5.4.0-next.09eca5dee30d";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2026-05-06T16:28:18.473Z",
|
|
4
|
-
minor: "2026-08-
|
|
5
|
-
patch: "2026-08-
|
|
4
|
+
minor: "2026-08-10T10:12:59.188Z",
|
|
5
|
+
patch: "2026-08-10T10:12:59.188Z"
|
|
6
6
|
};
|
|
7
7
|
export {
|
|
8
8
|
publishDates,
|
package/dist-esm/version.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '5.4.0-
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '5.4.0-next.09eca5dee30d'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-08-10T10:12:59.188Z',\n\tpatch: '2026-08-10T10:12:59.188Z',\n}\n"],
|
|
5
5
|
"mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
3
|
"description": "tldraw infinite canvas SDK (editor).",
|
|
4
|
-
"version": "5.4.0-
|
|
4
|
+
"version": "5.4.0-next.09eca5dee30d",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"@tiptap/core": "^3.12.1",
|
|
50
50
|
"@tiptap/pm": "^3.12.1",
|
|
51
51
|
"@tiptap/react": "^3.12.1",
|
|
52
|
-
"@tldraw/state": "5.4.0-
|
|
53
|
-
"@tldraw/state-react": "5.4.0-
|
|
54
|
-
"@tldraw/store": "5.4.0-
|
|
55
|
-
"@tldraw/tlschema": "5.4.0-
|
|
56
|
-
"@tldraw/utils": "5.4.0-
|
|
57
|
-
"@tldraw/validate": "5.4.0-
|
|
52
|
+
"@tldraw/state": "5.4.0-next.09eca5dee30d",
|
|
53
|
+
"@tldraw/state-react": "5.4.0-next.09eca5dee30d",
|
|
54
|
+
"@tldraw/store": "5.4.0-next.09eca5dee30d",
|
|
55
|
+
"@tldraw/tlschema": "5.4.0-next.09eca5dee30d",
|
|
56
|
+
"@tldraw/utils": "5.4.0-next.09eca5dee30d",
|
|
57
|
+
"@tldraw/validate": "5.4.0-next.09eca5dee30d",
|
|
58
58
|
"classnames": "^2.5.1",
|
|
59
59
|
"eventemitter3": "^4.0.7",
|
|
60
60
|
"idb": "^7.1.1",
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -5862,7 +5862,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5862
5862
|
const margin = this.options.hitTestMargin / this.getZoomLevel()
|
|
5863
5863
|
const sortedShapes = this.getCurrentPageShapesSorted()
|
|
5864
5864
|
|
|
5865
|
-
// iterate from the top (highest z-index) to find the top-most matching shape
|
|
5866
5865
|
for (let i = sortedShapes.length - 1; i >= 0; i--) {
|
|
5867
5866
|
const shape = sortedShapes[i]
|
|
5868
5867
|
if (shape.type === 'group') continue
|
|
@@ -5912,25 +5911,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5912
5911
|
const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin())
|
|
5913
5912
|
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin)
|
|
5914
5913
|
|
|
5915
|
-
const shapesToCheck =
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
const shape = shapesToCheck[i]
|
|
5914
|
+
const shapesToCheck = (
|
|
5915
|
+
opts.renderingOnly
|
|
5916
|
+
? this.getCurrentPageRenderingShapesSorted()
|
|
5917
|
+
: this.getCurrentPageShapesSorted()
|
|
5918
|
+
).filter((shape) => {
|
|
5921
5919
|
// Frame-like shapes have labels positioned above the shape (outside bounds), so always include them
|
|
5922
|
-
if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape))
|
|
5920
|
+
if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
|
|
5921
|
+
|
|
5923
5922
|
if (
|
|
5924
5923
|
(shape.isLocked && !hitLocked) ||
|
|
5925
5924
|
this.isShapeHidden(shape) ||
|
|
5926
5925
|
this.isShapeOfType(shape, 'group')
|
|
5927
|
-
)
|
|
5928
|
-
|
|
5929
|
-
}
|
|
5926
|
+
)
|
|
5927
|
+
return false
|
|
5930
5928
|
const pageMask = this.getShapeMask(shape)
|
|
5931
|
-
if (pageMask && !pointInPolygon(point, pageMask))
|
|
5932
|
-
if (filter && !filter(shape))
|
|
5929
|
+
if (pageMask && !pointInPolygon(point, pageMask)) return false
|
|
5930
|
+
if (filter && !filter(shape)) return false
|
|
5931
|
+
return true
|
|
5932
|
+
})
|
|
5933
5933
|
|
|
5934
|
+
for (let i = shapesToCheck.length - 1; i >= 0; i--) {
|
|
5935
|
+
const shape = shapesToCheck[i]
|
|
5934
5936
|
const geometry = this.getShapeGeometry(shape)
|
|
5935
5937
|
const isGroup = geometry instanceof Group2d
|
|
5936
5938
|
|
|
@@ -6112,18 +6114,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6112
6114
|
const margin = opts.margin ?? 0
|
|
6113
6115
|
const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin)
|
|
6114
6116
|
|
|
6115
|
-
// Get all page shapes in z-index order and filter to candidates that pass isPointInShape
|
|
6116
|
-
// Frame-like shapes are always checked because their labels can be outside their bounds
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
if (this.isPointInShape(shape, point, opts)) result.push(shape)
|
|
6125
|
-
}
|
|
6126
|
-
return result
|
|
6117
|
+
// Get all page shapes in z-index order and filter to candidates that pass isPointInShape
|
|
6118
|
+
// Frame-like shapes are always checked because their labels can be outside their bounds
|
|
6119
|
+
return this.getCurrentPageShapesSorted()
|
|
6120
|
+
.filter((shape) => {
|
|
6121
|
+
if (this.isShapeHidden(shape)) return false
|
|
6122
|
+
if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
|
|
6123
|
+
return this.isPointInShape(shape, point, opts)
|
|
6124
|
+
})
|
|
6125
|
+
.reverse()
|
|
6127
6126
|
}
|
|
6128
6127
|
|
|
6129
6128
|
/**
|
|
@@ -39,13 +39,9 @@ export function deriveShapeIdsInCurrentPage(store: TLStore, getCurrentPageId: ()
|
|
|
39
39
|
function fromScratch() {
|
|
40
40
|
const currentPageId = getCurrentPageId()
|
|
41
41
|
lastPageId = currentPageId
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
result.add(id)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return result
|
|
42
|
+
return new Set(
|
|
43
|
+
[...shapesIndex.get()].filter((id) => isShapeInPage(store, currentPageId, store.get(id)!))
|
|
44
|
+
)
|
|
49
45
|
}
|
|
50
46
|
return computed<Set<TLShapeId>>('_shapeIdsInCurrentPage', (prevValue, lastComputedEpoch) => {
|
|
51
47
|
if (isUninitialized(prevValue)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EMPTY_ARRAY } from '@tldraw/state'
|
|
1
2
|
import { assert, invLerp, lerp } from '@tldraw/utils'
|
|
2
3
|
import { Box } from '../Box'
|
|
3
4
|
import { Mat } from '../Mat'
|
|
@@ -35,15 +36,9 @@ export class Group2d extends Geometry2d {
|
|
|
35
36
|
|
|
36
37
|
override getVertices(filters: Geometry2dFilters): Vec[] {
|
|
37
38
|
if (this.isExcludedByFilter(filters)) return []
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const childVertices = child.getVertices(filters)
|
|
42
|
-
for (let i = 0, n = childVertices.length; i < n; i++) {
|
|
43
|
-
vertices.push(childVertices[i])
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return vertices
|
|
39
|
+
return this.children
|
|
40
|
+
.filter((c) => !c.isExcludedByFilter(filters))
|
|
41
|
+
.flatMap((c) => c.getVertices(filters))
|
|
47
42
|
}
|
|
48
43
|
|
|
49
44
|
override nearestPoint(point: VecLike, filters?: Geometry2dFilters): Vec {
|
|
@@ -89,11 +84,9 @@ export class Group2d extends Geometry2d {
|
|
|
89
84
|
hitInside: boolean,
|
|
90
85
|
filters = Geometry2dFilters.EXCLUDE_LABELS
|
|
91
86
|
): boolean {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
return false
|
|
87
|
+
return !!this.children
|
|
88
|
+
.filter((c) => !c.isExcludedByFilter(filters))
|
|
89
|
+
.find((c) => c.hitTestPoint(point, margin, hitInside))
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
override hitTestLineSegment(
|
|
@@ -102,71 +95,42 @@ export class Group2d extends Geometry2d {
|
|
|
102
95
|
zoom: number,
|
|
103
96
|
filters = Geometry2dFilters.EXCLUDE_LABELS
|
|
104
97
|
): boolean {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
return false
|
|
98
|
+
return !!this.children
|
|
99
|
+
.filter((c) => !c.isExcludedByFilter(filters))
|
|
100
|
+
.find((c) => c.hitTestLineSegment(A, B, zoom))
|
|
110
101
|
}
|
|
111
102
|
|
|
112
103
|
override intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
118
|
-
result.push(hits[i])
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return result
|
|
104
|
+
return this.children.flatMap((child) => {
|
|
105
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
|
|
106
|
+
return child.intersectLineSegment(A, B, filters)
|
|
107
|
+
})
|
|
122
108
|
}
|
|
123
109
|
|
|
124
110
|
override intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
130
|
-
result.push(hits[i])
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return result
|
|
111
|
+
return this.children.flatMap((child) => {
|
|
112
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
|
|
113
|
+
return child.intersectCircle(center, radius, filters)
|
|
114
|
+
})
|
|
134
115
|
}
|
|
135
116
|
|
|
136
117
|
override getBoundsVertices(): Vec[] {
|
|
137
118
|
if (this.excludeFromShapeBounds) return []
|
|
138
|
-
|
|
139
|
-
for (const child of this.children) {
|
|
140
|
-
const childVertices = child.getBoundsVertices()
|
|
141
|
-
for (let i = 0, n = childVertices.length; i < n; i++) {
|
|
142
|
-
vertices.push(childVertices[i])
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return vertices
|
|
119
|
+
return this.children.flatMap((child) => child.getBoundsVertices())
|
|
146
120
|
}
|
|
147
121
|
|
|
148
122
|
override intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
154
|
-
result.push(hits[i])
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return result
|
|
123
|
+
return this.children.flatMap((child) => {
|
|
124
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
|
|
125
|
+
return child.intersectPolygon(polygon, filters)
|
|
126
|
+
})
|
|
158
127
|
}
|
|
159
128
|
|
|
160
129
|
override intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
for (let i = 0, n = hits.length; i < n; i++) {
|
|
166
|
-
result.push(hits[i])
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return result
|
|
130
|
+
return this.children.flatMap((child) => {
|
|
131
|
+
if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
|
|
132
|
+
return child.intersectPolyline(polyline, filters)
|
|
133
|
+
})
|
|
170
134
|
}
|
|
171
135
|
|
|
172
136
|
override interpolateAlongEdge(t: number, filters?: Geometry2dFilters): Vec {
|
|
@@ -9,12 +9,6 @@ const MAX_ZOOM_STEP = 10
|
|
|
9
9
|
* @internal */
|
|
10
10
|
export function normalizeWheel(event: WheelEvent | React.WheelEvent<HTMLElement>) {
|
|
11
11
|
let { deltaY, deltaX } = event
|
|
12
|
-
|
|
13
|
-
// Coerce non-finite deltas to no movement so they can't propagate into the camera and
|
|
14
|
-
// crash validation. Also covers deltaZ below, which is derived from deltaY.
|
|
15
|
-
if (!Number.isFinite(deltaX)) deltaX = 0
|
|
16
|
-
if (!Number.isFinite(deltaY)) deltaY = 0
|
|
17
|
-
|
|
18
12
|
let deltaZ = 0
|
|
19
13
|
|
|
20
14
|
// wheeling
|
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 = '5.4.0-
|
|
4
|
+
export const version = '5.4.0-next.09eca5dee30d'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2026-05-06T16:28:18.473Z',
|
|
7
|
-
minor: '2026-08-
|
|
8
|
-
patch: '2026-08-
|
|
7
|
+
minor: '2026-08-10T10:12:59.188Z',
|
|
8
|
+
patch: '2026-08-10T10:12:59.188Z',
|
|
9
9
|
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { normalizeWheel } from './normalizeWheel'
|
|
2
|
-
|
|
3
|
-
function wheelEvent(props: Partial<WheelEvent>): WheelEvent {
|
|
4
|
-
return {
|
|
5
|
-
deltaX: 0,
|
|
6
|
-
deltaY: 0,
|
|
7
|
-
ctrlKey: false,
|
|
8
|
-
altKey: false,
|
|
9
|
-
metaKey: false,
|
|
10
|
-
shiftKey: false,
|
|
11
|
-
...props,
|
|
12
|
-
} as WheelEvent
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe('normalizeWheel', () => {
|
|
16
|
-
it('negates finite deltas for panning', () => {
|
|
17
|
-
const { x, y } = normalizeWheel(wheelEvent({ deltaX: 5, deltaY: 10 }))
|
|
18
|
-
expect(x).toBe(-5)
|
|
19
|
-
expect(y).toBe(-10)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('coerces a non-finite deltaX to zero movement', () => {
|
|
23
|
-
const { x, y } = normalizeWheel(wheelEvent({ deltaX: NaN, deltaY: 10 }))
|
|
24
|
-
expect(x === 0).toBe(true)
|
|
25
|
-
expect(y).toBe(-10)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('coerces a non-finite deltaY to zero movement', () => {
|
|
29
|
-
const { x, y } = normalizeWheel(wheelEvent({ deltaX: 5, deltaY: Infinity }))
|
|
30
|
-
expect(x).toBe(-5)
|
|
31
|
-
expect(y === 0).toBe(true)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('does not produce a non-finite zoom delta when wheeling with a bad deltaY', () => {
|
|
35
|
-
const { z } = normalizeWheel(wheelEvent({ deltaY: NaN, ctrlKey: true }))
|
|
36
|
-
expect(Number.isFinite(z)).toBe(true)
|
|
37
|
-
expect(z === 0).toBe(true)
|
|
38
|
-
})
|
|
39
|
-
})
|