@tldraw/editor 3.16.0-canary.5f82fb812214 → 3.16.0-canary.5f8d98bccb38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +13 -101
- package/dist-cjs/index.js +3 -5
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +1 -5
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +3 -99
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +10 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +4 -1
- package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +110 -35
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +36 -3
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +68 -6
- package/dist-cjs/lib/license/Watermark.js.map +3 -3
- package/dist-cjs/lib/primitives/Vec.js +0 -4
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +26 -18
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +3 -0
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +2 -35
- package/dist-cjs/lib/utils/reparenting.js.map +3 -3
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +13 -101
- package/dist-esm/index.mjs +3 -5
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +1 -5
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +3 -99
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +10 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +111 -36
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +36 -4
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +68 -6
- package/dist-esm/lib/license/Watermark.mjs.map +3 -3
- package/dist-esm/lib/primitives/Vec.mjs +0 -4
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +29 -19
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +3 -0
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +3 -40
- package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +8 -3
- package/package.json +7 -7
- package/src/index.ts +1 -9
- package/src/lib/TldrawEditor.tsx +1 -13
- package/src/lib/editor/Editor.ts +1 -125
- package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +11 -0
- package/src/lib/editor/types/misc-types.ts +0 -6
- package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
- package/src/lib/license/LicenseManager.test.ts +643 -387
- package/src/lib/license/LicenseManager.ts +156 -44
- package/src/lib/license/LicenseProvider.tsx +69 -5
- package/src/lib/license/Watermark.tsx +73 -6
- package/src/lib/primitives/Vec.ts +0 -5
- package/src/lib/primitives/geometry/Geometry2d.ts +49 -19
- package/src/lib/primitives/geometry/Group2d.ts +4 -0
- package/src/lib/utils/reparenting.ts +3 -69
- package/src/version.ts +3 -3
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { EMPTY_ARRAY } from "@tldraw/state";
|
|
2
2
|
import { compact, getIndexAbove, getIndexBetween } from "@tldraw/utils";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
intersectPolygonPolygon,
|
|
6
|
-
polygonIntersectsPolyline,
|
|
7
|
-
polygonsIntersect
|
|
8
|
-
} from "../primitives/intersect.mjs";
|
|
9
|
-
import { pointInPolygon } from "../primitives/utils.mjs";
|
|
3
|
+
import { intersectPolygonPolygon } from "../primitives/intersect.mjs";
|
|
10
4
|
function kickoutOccludedShapes(editor, shapeIds, opts) {
|
|
11
5
|
const parentsToCheck = /* @__PURE__ */ new Set();
|
|
12
6
|
for (const id of shapeIds) {
|
|
@@ -102,39 +96,9 @@ function getOverlappingShapes(editor, shape, otherShapes) {
|
|
|
102
96
|
if (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false;
|
|
103
97
|
const parentPolygonInShapeShape = editor.getShapePageTransform(childId).clone().invert().applyToPoints(parentPagePolygon);
|
|
104
98
|
const geometry = editor.getShapeGeometry(childId);
|
|
105
|
-
return
|
|
99
|
+
return geometry.overlapsPolygon(parentPolygonInShapeShape);
|
|
106
100
|
});
|
|
107
101
|
}
|
|
108
|
-
function doesGeometryOverlapPolygon(geometry, parentCornersInShapeSpace) {
|
|
109
|
-
if (geometry instanceof Group2d) {
|
|
110
|
-
return geometry.children.some(
|
|
111
|
-
(childGeometry) => doesGeometryOverlapPolygon(childGeometry, parentCornersInShapeSpace)
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
const { vertices, center, isFilled, isEmptyLabel, isClosed } = geometry;
|
|
115
|
-
if (isEmptyLabel) return false;
|
|
116
|
-
if (vertices.some((v) => pointInPolygon(v, parentCornersInShapeSpace))) {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
if (isClosed) {
|
|
120
|
-
if (isFilled) {
|
|
121
|
-
if (pointInPolygon(center, parentCornersInShapeSpace)) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
if (parentCornersInShapeSpace.every((v) => pointInPolygon(v, vertices))) {
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (polygonsIntersect(parentCornersInShapeSpace, vertices)) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
if (polygonIntersectsPolyline(parentCornersInShapeSpace, vertices)) {
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
102
|
function getDroppedShapesToNewParents(editor, shapes, cb) {
|
|
139
103
|
const shapesToActuallyCheck = new Set(shapes);
|
|
140
104
|
const movingGroups = /* @__PURE__ */ new Set();
|
|
@@ -187,7 +151,7 @@ function getDroppedShapesToNewParents(editor, shapes, cb) {
|
|
|
187
151
|
if (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck;
|
|
188
152
|
if (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck;
|
|
189
153
|
const parentPolygonInShapeSpace = editor.getShapePageTransform(shape).clone().invert().applyToPoints(parentPagePolygon);
|
|
190
|
-
if (
|
|
154
|
+
if (editor.getShapeGeometry(shape).overlapsPolygon(parentPolygonInShapeSpace)) {
|
|
191
155
|
if (!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type))
|
|
192
156
|
continue shapeCheck;
|
|
193
157
|
if (shape.parentId !== parentShape.id) {
|
|
@@ -209,7 +173,6 @@ function getDroppedShapesToNewParents(editor, shapes, cb) {
|
|
|
209
173
|
};
|
|
210
174
|
}
|
|
211
175
|
export {
|
|
212
|
-
doesGeometryOverlapPolygon,
|
|
213
176
|
getDroppedShapesToNewParents,
|
|
214
177
|
kickoutOccludedShapes
|
|
215
178
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/reparenting.ts"],
|
|
4
|
-
"sourcesContent": ["import { EMPTY_ARRAY } from '@tldraw/state'\nimport { TLGroupShape, TLParentId, TLShape, TLShapeId } from '@tldraw/tlschema'\nimport { IndexKey, compact, getIndexAbove, getIndexBetween } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { Vec } from '../primitives/Vec'\nimport { Geometry2d } from '../primitives/geometry/Geometry2d'\nimport { Group2d } from '../primitives/geometry/Group2d'\nimport {\n\tintersectPolygonPolygon,\n\tpolygonIntersectsPolyline,\n\tpolygonsIntersect,\n} from '../primitives/intersect'\nimport { pointInPolygon } from '../primitives/utils'\n\n/**\n * Reparents shapes that are no longer contained within their parent shapes.\n * todo: rename me to something more descriptive, like `reparentOccludedShapes` or `reparentAutoDroppedShapes`\n *\n * @param editor - The editor instance.\n * @param shapeIds - The IDs of the shapes to reparent.\n * @param opts - Optional options, including a callback to filter out certain parents, such as when removing a frame.\n *\n * @public\n */\nexport function kickoutOccludedShapes(\n\teditor: Editor,\n\tshapeIds: TLShapeId[],\n\topts?: { filter?(parent: TLShape): boolean }\n) {\n\tconst parentsToCheck = new Set<TLShape>()\n\n\tfor (const id of shapeIds) {\n\t\tconst shape = editor.getShape(id)\n\n\t\tif (!shape) continue\n\t\tparentsToCheck.add(shape)\n\n\t\tconst parent = editor.getShape(shape.parentId)\n\t\tif (!parent) continue\n\t\tparentsToCheck.add(parent)\n\t}\n\n\t// Check all of the parents and gather up parents who have lost children\n\tconst parentsToLostChildren = new Map<TLShape, TLShapeId[]>()\n\n\tfor (const parent of parentsToCheck) {\n\t\tconst childIds = editor.getSortedChildIdsForParent(parent)\n\t\tif (opts?.filter && !opts.filter(parent)) {\n\t\t\t// If the shape is filtered out, we kick out all of its children\n\t\t\tparentsToLostChildren.set(parent, childIds)\n\t\t} else {\n\t\t\tconst overlappingChildren = getOverlappingShapes(editor, parent.id, childIds)\n\t\t\tif (overlappingChildren.length < childIds.length) {\n\t\t\t\tparentsToLostChildren.set(\n\t\t\t\t\tparent,\n\t\t\t\t\tchildIds.filter((id) => !overlappingChildren.includes(id))\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get all of the shapes on the current page, sorted by their index\n\tconst sortedShapeIds = editor.getCurrentPageShapesSorted().map((s) => s.id)\n\n\tconst parentsToNewChildren: Record<\n\t\tTLParentId,\n\t\t{ parentId: TLParentId; shapeIds: TLShapeId[]; index?: IndexKey }\n\t> = {}\n\n\tfor (const [prevParent, lostChildrenIds] of parentsToLostChildren) {\n\t\tconst lostChildren = compact(lostChildrenIds.map((id) => editor.getShape(id)))\n\n\t\t// Don't fall \"up\" into frames in front of the shape\n\t\t// if (pageShapes.indexOf(shape) < frameSortPosition) continue shapeCheck\n\n\t\t// Otherwise, we have no next dropping shape under the cursor, so go find\n\t\t// all the frames on the page where the moving shapes will fall into\n\t\tconst { reparenting, remainingShapesToReparent } = getDroppedShapesToNewParents(\n\t\t\teditor,\n\t\t\tlostChildren,\n\t\t\t(shape, maybeNewParent) => {\n\t\t\t\t// If we're filtering out a potential parent, don't reparent shapes to the filtered out shape\n\t\t\t\tif (opts?.filter && !opts.filter(maybeNewParent)) return false\n\t\t\t\treturn (\n\t\t\t\t\tmaybeNewParent.id !== prevParent.id &&\n\t\t\t\t\tsortedShapeIds.indexOf(maybeNewParent.id) < sortedShapeIds.indexOf(shape.id)\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\n\t\treparenting.forEach((childrenToReparent, newParentId) => {\n\t\t\tif (childrenToReparent.length === 0) return\n\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\tshapeIds: [],\n\t\t\t\t}\n\t\t\t}\n\t\t\tparentsToNewChildren[newParentId].shapeIds.push(...childrenToReparent.map((s) => s.id))\n\t\t})\n\n\t\t// Reparent the rest to the page (or containing group)\n\t\tif (remainingShapesToReparent.size > 0) {\n\t\t\t// The remaining shapes are going to be reparented to the old parent's containing group, if there was one, or else to the page\n\t\t\tconst newParentId =\n\t\t\t\teditor.findShapeAncestor(prevParent, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))\n\t\t\t\t\t?.id ?? editor.getCurrentPageId()\n\n\t\t\tremainingShapesToReparent.forEach((shape) => {\n\t\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\t\tlet insertIndexKey: IndexKey | undefined\n\n\t\t\t\t\tconst oldParentSiblingIds = editor.getSortedChildIdsForParent(newParentId)\n\t\t\t\t\tconst oldParentIndex = oldParentSiblingIds.indexOf(prevParent.id)\n\t\t\t\t\tif (oldParentIndex > -1) {\n\t\t\t\t\t\t// If the old parent is a direct child of the new parent, then we'll add them above the old parent but below the next sibling.\n\t\t\t\t\t\tconst siblingsIndexAbove = oldParentSiblingIds[oldParentIndex + 1]\n\t\t\t\t\t\tconst indexKeyAbove = siblingsIndexAbove\n\t\t\t\t\t\t\t? editor.getShape(siblingsIndexAbove)!.index\n\t\t\t\t\t\t\t: getIndexAbove(prevParent.index)\n\t\t\t\t\t\tinsertIndexKey = getIndexBetween(prevParent.index, indexKeyAbove)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If the old parent is not a direct child of the new parent, then we'll add them to the \"top\" of the new parent's children.\n\t\t\t\t\t\t// This is done automatically if we leave the index undefined, so let's do that.\n\t\t\t\t\t}\n\n\t\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\t\tshapeIds: [],\n\t\t\t\t\t\tindex: insertIndexKey,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tparentsToNewChildren[newParentId].shapeIds.push(shape.id)\n\t\t\t})\n\t\t}\n\t}\n\n\teditor.run(() => {\n\t\tObject.values(parentsToNewChildren).forEach(({ parentId, shapeIds, index }) => {\n\t\t\tif (shapeIds.length === 0) return\n\t\t\t// Before we reparent, sort the new shape ids by their place in the original absolute order on the page\n\t\t\tshapeIds.sort((a, b) => (sortedShapeIds.indexOf(a) < sortedShapeIds.indexOf(b) ? -1 : 1))\n\t\t\teditor.reparentShapes(shapeIds, parentId, index)\n\t\t})\n\t})\n}\n\n/**\n * Get the shapes that overlap with a given shape.\n *\n * @param editor - The editor instance.\n * @param shape - The shapes or shape IDs to check against.\n * @param otherShapes - The shapes or shape IDs to check for overlap.\n * @returns An array of shapes or shape IDs that overlap with the given shape.\n */\nfunction getOverlappingShapes<T extends TLShape[] | TLShapeId[]>(\n\teditor: Editor,\n\tshape: T[number],\n\totherShapes: T\n) {\n\tif (otherShapes.length === 0) {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\tconst parentPageBounds = editor.getShapePageBounds(shape)\n\tif (!parentPageBounds) return EMPTY_ARRAY\n\n\tconst parentGeometry = editor.getShapeGeometry(shape)\n\tconst parentPageTransform = editor.getShapePageTransform(shape)\n\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\n\tconst parentPageMaskVertices = editor.getShapeMask(shape)\n\tconst parentPagePolygon = parentPageMaskVertices\n\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t: parentPageCorners\n\n\tif (!parentPagePolygon) return EMPTY_ARRAY\n\n\treturn otherShapes.filter((childId) => {\n\t\tconst shapePageBounds = editor.getShapePageBounds(childId)\n\t\tif (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false\n\n\t\tconst parentPolygonInShapeShape = editor\n\t\t\t.getShapePageTransform(childId)\n\t\t\t.clone()\n\t\t\t.invert()\n\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\tconst geometry = editor.getShapeGeometry(childId)\n\n\t\treturn doesGeometryOverlapPolygon(geometry, parentPolygonInShapeShape)\n\t})\n}\n\n/**\n * @public\n */\nexport function doesGeometryOverlapPolygon(\n\tgeometry: Geometry2d,\n\tparentCornersInShapeSpace: Vec[]\n): boolean {\n\t// If the child is a group, check if any of its children overlap the box\n\tif (geometry instanceof Group2d) {\n\t\treturn geometry.children.some((childGeometry) =>\n\t\t\tdoesGeometryOverlapPolygon(childGeometry, parentCornersInShapeSpace)\n\t\t)\n\t}\n\n\t// Otherwise, check if the geometry overlaps the box\n\tconst { vertices, center, isFilled, isEmptyLabel, isClosed } = geometry\n\n\t// We'll do things in order of cheapest to most expensive checks\n\n\t// Skip empty labels\n\tif (isEmptyLabel) return false\n\n\t// If any of the shape's vertices are inside the occluder, it's inside\n\tif (vertices.some((v) => pointInPolygon(v, parentCornersInShapeSpace))) {\n\t\treturn true\n\t}\n\n\t// If the shape is filled and closed and its center is inside the parent, it's inside\n\tif (isClosed) {\n\t\tif (isFilled) {\n\t\t\t// If closed and filled, check if the center is inside the parent\n\t\t\tif (pointInPolygon(center, parentCornersInShapeSpace)) {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// ..then, slightly more expensive check, see the shape covers the entire parent but not its center\n\t\t\tif (parentCornersInShapeSpace.every((v) => pointInPolygon(v, vertices))) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\t// If any the shape's vertices intersect the edge of the occluder, it's inside.\n\t\t// for example when a rotated rectangle is moved over the corner of a parent rectangle\n\t\t// If the child shape is closed, intersect as a polygon\n\t\tif (polygonsIntersect(parentCornersInShapeSpace, vertices)) {\n\t\t\treturn true\n\t\t}\n\t} else {\n\t\t// if the child shape is not closed, intersect as a polyline\n\t\tif (polygonIntersectsPolyline(parentCornersInShapeSpace, vertices)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// If none of the above checks passed, the shape is outside the parent\n\treturn false\n}\n\n/**\n * Get the shapes that will be reparented to new parents when the shapes are dropped.\n *\n * @param editor - The editor instance.\n * @param shapes - The shapes to check.\n * @param cb - A callback to filter out certain shapes.\n * @returns An object with the shapes that will be reparented to new parents and the shapes that will be reparented to the page or their ancestral group.\n *\n * @public\n */\nexport function getDroppedShapesToNewParents(\n\teditor: Editor,\n\tshapes: Set<TLShape> | TLShape[],\n\tcb?: (shape: TLShape, parent: TLShape) => boolean\n) {\n\tconst shapesToActuallyCheck = new Set<TLShape>(shapes)\n\tconst movingGroups = new Set<TLGroupShape>()\n\n\tfor (const shape of shapes) {\n\t\tconst parent = editor.getShapeParent(shape)\n\t\tif (parent && editor.isShapeOfType<TLGroupShape>(parent, 'group')) {\n\t\t\tif (!movingGroups.has(parent)) {\n\t\t\t\tmovingGroups.add(parent)\n\t\t\t}\n\t\t}\n\t}\n\n\t// If all of a group's children are moving, then move the group instead\n\tfor (const movingGroup of movingGroups) {\n\t\tconst children = compact(\n\t\t\teditor.getSortedChildIdsForParent(movingGroup).map((id) => editor.getShape(id))\n\t\t)\n\t\tfor (const child of children) {\n\t\t\tshapesToActuallyCheck.delete(child)\n\t\t}\n\t\tshapesToActuallyCheck.add(movingGroup)\n\t}\n\n\t// this could be cached and passed in\n\tconst shapeGroupIds = new Map<TLShapeId, TLShapeId | undefined>()\n\n\tconst reparenting = new Map<TLShapeId, TLShape[]>()\n\n\tconst remainingShapesToReparent = new Set(shapesToActuallyCheck)\n\n\tconst potentialParentShapes = editor\n\t\t.getCurrentPageShapesSorted()\n\t\t// filter out any shapes that aren't frames or that are included among the provided shapes\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\teditor.getShapeUtil(s).canReceiveNewChildrenOfType?.(s, s.type) &&\n\t\t\t\t!remainingShapesToReparent.has(s)\n\t\t)\n\n\tparentCheck: for (let i = potentialParentShapes.length - 1; i >= 0; i--) {\n\t\tconst parentShape = potentialParentShapes[i]\n\t\tconst parentShapeContainingGroupId = editor.findShapeAncestor(parentShape, (s) =>\n\t\t\teditor.isShapeOfType<TLGroupShape>(s, 'group')\n\t\t)?.id\n\n\t\tconst parentGeometry = editor.getShapeGeometry(parentShape)\n\t\tconst parentPageTransform = editor.getShapePageTransform(parentShape)\n\t\tconst parentPageMaskVertices = editor.getShapeMask(parentShape)\n\t\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\t\tconst parentPagePolygon = parentPageMaskVertices\n\t\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t\t: parentPageCorners\n\n\t\tif (!parentPagePolygon) continue parentCheck\n\n\t\tconst childrenToReparent = []\n\n\t\t// For each of the dropping shapes...\n\t\tshapeCheck: for (const shape of remainingShapesToReparent) {\n\t\t\t// Don't reparent a frame to itself\n\t\t\tif (parentShape.id === shape.id) continue shapeCheck\n\n\t\t\t// Use the callback to filter out certain shapes\n\t\t\tif (cb && !cb(shape, parentShape)) continue shapeCheck\n\n\t\t\tif (!shapeGroupIds.has(shape.id)) {\n\t\t\t\tshapeGroupIds.set(\n\t\t\t\t\tshape.id,\n\t\t\t\t\teditor.findShapeAncestor(shape, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))?.id\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst shapeGroupId = shapeGroupIds.get(shape.id)\n\n\t\t\t// Are the shape and the parent part of different groups?\n\t\t\tif (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck\n\n\t\t\t// Is the shape is actually the ancestor of the parent?\n\t\t\tif (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck\n\n\t\t\t// Convert the parent polygon to the shape's space\n\t\t\tconst parentPolygonInShapeSpace = editor\n\t\t\t\t.getShapePageTransform(shape)\n\t\t\t\t.clone()\n\t\t\t\t.invert()\n\t\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\t\t// If the shape overlaps the parent polygon, reparent it to that parent\n\t\t\tif (doesGeometryOverlapPolygon(editor.getShapeGeometry(shape), parentPolygonInShapeSpace)) {\n\t\t\t\t// Use the util to check if the shape can be reparented to the parent\n\t\t\t\tif (\n\t\t\t\t\t!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type)\n\t\t\t\t)\n\t\t\t\t\tcontinue shapeCheck\n\n\t\t\t\tif (shape.parentId !== parentShape.id) {\n\t\t\t\t\tchildrenToReparent.push(shape)\n\t\t\t\t}\n\t\t\t\tremainingShapesToReparent.delete(shape)\n\t\t\t\tcontinue shapeCheck\n\t\t\t}\n\t\t}\n\n\t\tif (childrenToReparent.length) {\n\t\t\treparenting.set(parentShape.id, childrenToReparent)\n\t\t}\n\t}\n\n\treturn {\n\t\t// these are the shapes that will be reparented to new parents\n\t\treparenting,\n\t\t// these are the shapes that will be reparented to the page or their ancestral group\n\t\tremainingShapesToReparent,\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,mBAAmB;AAE5B,SAAmB,SAAS,eAAe,uBAAuB;
|
|
4
|
+
"sourcesContent": ["import { EMPTY_ARRAY } from '@tldraw/state'\nimport { TLGroupShape, TLParentId, TLShape, TLShapeId } from '@tldraw/tlschema'\nimport { IndexKey, compact, getIndexAbove, getIndexBetween } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { intersectPolygonPolygon } from '../primitives/intersect'\n\n/**\n * Reparents shapes that are no longer contained within their parent shapes.\n * todo: rename me to something more descriptive, like `reparentOccludedShapes` or `reparentAutoDroppedShapes`\n *\n * @param editor - The editor instance.\n * @param shapeIds - The IDs of the shapes to reparent.\n * @param opts - Optional options, including a callback to filter out certain parents, such as when removing a frame.\n *\n * @public\n */\nexport function kickoutOccludedShapes(\n\teditor: Editor,\n\tshapeIds: TLShapeId[],\n\topts?: { filter?(parent: TLShape): boolean }\n) {\n\tconst parentsToCheck = new Set<TLShape>()\n\n\tfor (const id of shapeIds) {\n\t\tconst shape = editor.getShape(id)\n\n\t\tif (!shape) continue\n\t\tparentsToCheck.add(shape)\n\n\t\tconst parent = editor.getShape(shape.parentId)\n\t\tif (!parent) continue\n\t\tparentsToCheck.add(parent)\n\t}\n\n\t// Check all of the parents and gather up parents who have lost children\n\tconst parentsToLostChildren = new Map<TLShape, TLShapeId[]>()\n\n\tfor (const parent of parentsToCheck) {\n\t\tconst childIds = editor.getSortedChildIdsForParent(parent)\n\t\tif (opts?.filter && !opts.filter(parent)) {\n\t\t\t// If the shape is filtered out, we kick out all of its children\n\t\t\tparentsToLostChildren.set(parent, childIds)\n\t\t} else {\n\t\t\tconst overlappingChildren = getOverlappingShapes(editor, parent.id, childIds)\n\t\t\tif (overlappingChildren.length < childIds.length) {\n\t\t\t\tparentsToLostChildren.set(\n\t\t\t\t\tparent,\n\t\t\t\t\tchildIds.filter((id) => !overlappingChildren.includes(id))\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get all of the shapes on the current page, sorted by their index\n\tconst sortedShapeIds = editor.getCurrentPageShapesSorted().map((s) => s.id)\n\n\tconst parentsToNewChildren: Record<\n\t\tTLParentId,\n\t\t{ parentId: TLParentId; shapeIds: TLShapeId[]; index?: IndexKey }\n\t> = {}\n\n\tfor (const [prevParent, lostChildrenIds] of parentsToLostChildren) {\n\t\tconst lostChildren = compact(lostChildrenIds.map((id) => editor.getShape(id)))\n\n\t\t// Don't fall \"up\" into frames in front of the shape\n\t\t// if (pageShapes.indexOf(shape) < frameSortPosition) continue shapeCheck\n\n\t\t// Otherwise, we have no next dropping shape under the cursor, so go find\n\t\t// all the frames on the page where the moving shapes will fall into\n\t\tconst { reparenting, remainingShapesToReparent } = getDroppedShapesToNewParents(\n\t\t\teditor,\n\t\t\tlostChildren,\n\t\t\t(shape, maybeNewParent) => {\n\t\t\t\t// If we're filtering out a potential parent, don't reparent shapes to the filtered out shape\n\t\t\t\tif (opts?.filter && !opts.filter(maybeNewParent)) return false\n\t\t\t\treturn (\n\t\t\t\t\tmaybeNewParent.id !== prevParent.id &&\n\t\t\t\t\tsortedShapeIds.indexOf(maybeNewParent.id) < sortedShapeIds.indexOf(shape.id)\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\n\t\treparenting.forEach((childrenToReparent, newParentId) => {\n\t\t\tif (childrenToReparent.length === 0) return\n\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\tshapeIds: [],\n\t\t\t\t}\n\t\t\t}\n\t\t\tparentsToNewChildren[newParentId].shapeIds.push(...childrenToReparent.map((s) => s.id))\n\t\t})\n\n\t\t// Reparent the rest to the page (or containing group)\n\t\tif (remainingShapesToReparent.size > 0) {\n\t\t\t// The remaining shapes are going to be reparented to the old parent's containing group, if there was one, or else to the page\n\t\t\tconst newParentId =\n\t\t\t\teditor.findShapeAncestor(prevParent, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))\n\t\t\t\t\t?.id ?? editor.getCurrentPageId()\n\n\t\t\tremainingShapesToReparent.forEach((shape) => {\n\t\t\t\tif (!parentsToNewChildren[newParentId]) {\n\t\t\t\t\tlet insertIndexKey: IndexKey | undefined\n\n\t\t\t\t\tconst oldParentSiblingIds = editor.getSortedChildIdsForParent(newParentId)\n\t\t\t\t\tconst oldParentIndex = oldParentSiblingIds.indexOf(prevParent.id)\n\t\t\t\t\tif (oldParentIndex > -1) {\n\t\t\t\t\t\t// If the old parent is a direct child of the new parent, then we'll add them above the old parent but below the next sibling.\n\t\t\t\t\t\tconst siblingsIndexAbove = oldParentSiblingIds[oldParentIndex + 1]\n\t\t\t\t\t\tconst indexKeyAbove = siblingsIndexAbove\n\t\t\t\t\t\t\t? editor.getShape(siblingsIndexAbove)!.index\n\t\t\t\t\t\t\t: getIndexAbove(prevParent.index)\n\t\t\t\t\t\tinsertIndexKey = getIndexBetween(prevParent.index, indexKeyAbove)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If the old parent is not a direct child of the new parent, then we'll add them to the \"top\" of the new parent's children.\n\t\t\t\t\t\t// This is done automatically if we leave the index undefined, so let's do that.\n\t\t\t\t\t}\n\n\t\t\t\t\tparentsToNewChildren[newParentId] = {\n\t\t\t\t\t\tparentId: newParentId,\n\t\t\t\t\t\tshapeIds: [],\n\t\t\t\t\t\tindex: insertIndexKey,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tparentsToNewChildren[newParentId].shapeIds.push(shape.id)\n\t\t\t})\n\t\t}\n\t}\n\n\teditor.run(() => {\n\t\tObject.values(parentsToNewChildren).forEach(({ parentId, shapeIds, index }) => {\n\t\t\tif (shapeIds.length === 0) return\n\t\t\t// Before we reparent, sort the new shape ids by their place in the original absolute order on the page\n\t\t\tshapeIds.sort((a, b) => (sortedShapeIds.indexOf(a) < sortedShapeIds.indexOf(b) ? -1 : 1))\n\t\t\teditor.reparentShapes(shapeIds, parentId, index)\n\t\t})\n\t})\n}\n\n/**\n * Get the shapes that overlap with a given shape.\n *\n * @param editor - The editor instance.\n * @param shape - The shapes or shape IDs to check against.\n * @param otherShapes - The shapes or shape IDs to check for overlap.\n * @returns An array of shapes or shape IDs that overlap with the given shape.\n */\nfunction getOverlappingShapes<T extends TLShape[] | TLShapeId[]>(\n\teditor: Editor,\n\tshape: T[number],\n\totherShapes: T\n) {\n\tif (otherShapes.length === 0) {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\tconst parentPageBounds = editor.getShapePageBounds(shape)\n\tif (!parentPageBounds) return EMPTY_ARRAY\n\n\tconst parentGeometry = editor.getShapeGeometry(shape)\n\tconst parentPageTransform = editor.getShapePageTransform(shape)\n\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\n\tconst parentPageMaskVertices = editor.getShapeMask(shape)\n\tconst parentPagePolygon = parentPageMaskVertices\n\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t: parentPageCorners\n\n\tif (!parentPagePolygon) return EMPTY_ARRAY\n\n\treturn otherShapes.filter((childId) => {\n\t\tconst shapePageBounds = editor.getShapePageBounds(childId)\n\t\tif (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false\n\n\t\tconst parentPolygonInShapeShape = editor\n\t\t\t.getShapePageTransform(childId)\n\t\t\t.clone()\n\t\t\t.invert()\n\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\tconst geometry = editor.getShapeGeometry(childId)\n\n\t\treturn geometry.overlapsPolygon(parentPolygonInShapeShape)\n\t})\n}\n\n/**\n * Get the shapes that will be reparented to new parents when the shapes are dropped.\n *\n * @param editor - The editor instance.\n * @param shapes - The shapes to check.\n * @param cb - A callback to filter out certain shapes.\n * @returns An object with the shapes that will be reparented to new parents and the shapes that will be reparented to the page or their ancestral group.\n *\n * @public\n */\nexport function getDroppedShapesToNewParents(\n\teditor: Editor,\n\tshapes: Set<TLShape> | TLShape[],\n\tcb?: (shape: TLShape, parent: TLShape) => boolean\n) {\n\tconst shapesToActuallyCheck = new Set<TLShape>(shapes)\n\tconst movingGroups = new Set<TLGroupShape>()\n\n\tfor (const shape of shapes) {\n\t\tconst parent = editor.getShapeParent(shape)\n\t\tif (parent && editor.isShapeOfType<TLGroupShape>(parent, 'group')) {\n\t\t\tif (!movingGroups.has(parent)) {\n\t\t\t\tmovingGroups.add(parent)\n\t\t\t}\n\t\t}\n\t}\n\n\t// If all of a group's children are moving, then move the group instead\n\tfor (const movingGroup of movingGroups) {\n\t\tconst children = compact(\n\t\t\teditor.getSortedChildIdsForParent(movingGroup).map((id) => editor.getShape(id))\n\t\t)\n\t\tfor (const child of children) {\n\t\t\tshapesToActuallyCheck.delete(child)\n\t\t}\n\t\tshapesToActuallyCheck.add(movingGroup)\n\t}\n\n\t// this could be cached and passed in\n\tconst shapeGroupIds = new Map<TLShapeId, TLShapeId | undefined>()\n\n\tconst reparenting = new Map<TLShapeId, TLShape[]>()\n\n\tconst remainingShapesToReparent = new Set(shapesToActuallyCheck)\n\n\tconst potentialParentShapes = editor\n\t\t.getCurrentPageShapesSorted()\n\t\t// filter out any shapes that aren't frames or that are included among the provided shapes\n\t\t.filter(\n\t\t\t(s) =>\n\t\t\t\teditor.getShapeUtil(s).canReceiveNewChildrenOfType?.(s, s.type) &&\n\t\t\t\t!remainingShapesToReparent.has(s)\n\t\t)\n\n\tparentCheck: for (let i = potentialParentShapes.length - 1; i >= 0; i--) {\n\t\tconst parentShape = potentialParentShapes[i]\n\t\tconst parentShapeContainingGroupId = editor.findShapeAncestor(parentShape, (s) =>\n\t\t\teditor.isShapeOfType<TLGroupShape>(s, 'group')\n\t\t)?.id\n\n\t\tconst parentGeometry = editor.getShapeGeometry(parentShape)\n\t\tconst parentPageTransform = editor.getShapePageTransform(parentShape)\n\t\tconst parentPageMaskVertices = editor.getShapeMask(parentShape)\n\t\tconst parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices)\n\t\tconst parentPagePolygon = parentPageMaskVertices\n\t\t\t? intersectPolygonPolygon(parentPageMaskVertices, parentPageCorners)\n\t\t\t: parentPageCorners\n\n\t\tif (!parentPagePolygon) continue parentCheck\n\n\t\tconst childrenToReparent = []\n\n\t\t// For each of the dropping shapes...\n\t\tshapeCheck: for (const shape of remainingShapesToReparent) {\n\t\t\t// Don't reparent a frame to itself\n\t\t\tif (parentShape.id === shape.id) continue shapeCheck\n\n\t\t\t// Use the callback to filter out certain shapes\n\t\t\tif (cb && !cb(shape, parentShape)) continue shapeCheck\n\n\t\t\tif (!shapeGroupIds.has(shape.id)) {\n\t\t\t\tshapeGroupIds.set(\n\t\t\t\t\tshape.id,\n\t\t\t\t\teditor.findShapeAncestor(shape, (s) => editor.isShapeOfType<TLGroupShape>(s, 'group'))?.id\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst shapeGroupId = shapeGroupIds.get(shape.id)\n\n\t\t\t// Are the shape and the parent part of different groups?\n\t\t\tif (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck\n\n\t\t\t// Is the shape is actually the ancestor of the parent?\n\t\t\tif (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck\n\n\t\t\t// Convert the parent polygon to the shape's space\n\t\t\tconst parentPolygonInShapeSpace = editor\n\t\t\t\t.getShapePageTransform(shape)\n\t\t\t\t.clone()\n\t\t\t\t.invert()\n\t\t\t\t.applyToPoints(parentPagePolygon)\n\n\t\t\t// If the shape overlaps the parent polygon, reparent it to that parent\n\t\t\tif (editor.getShapeGeometry(shape).overlapsPolygon(parentPolygonInShapeSpace)) {\n\t\t\t\t// Use the util to check if the shape can be reparented to the parent\n\t\t\t\tif (\n\t\t\t\t\t!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type)\n\t\t\t\t)\n\t\t\t\t\tcontinue shapeCheck\n\n\t\t\t\tif (shape.parentId !== parentShape.id) {\n\t\t\t\t\tchildrenToReparent.push(shape)\n\t\t\t\t}\n\t\t\t\tremainingShapesToReparent.delete(shape)\n\t\t\t\tcontinue shapeCheck\n\t\t\t}\n\t\t}\n\n\t\tif (childrenToReparent.length) {\n\t\t\treparenting.set(parentShape.id, childrenToReparent)\n\t\t}\n\t}\n\n\treturn {\n\t\t// these are the shapes that will be reparented to new parents\n\t\treparenting,\n\t\t// these are the shapes that will be reparented to the page or their ancestral group\n\t\tremainingShapesToReparent,\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["shapeIds"]
|
|
7
7
|
}
|
package/dist-esm/version.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "3.16.0-canary.
|
|
1
|
+
const version = "3.16.0-canary.5f8d98bccb38";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2024-09-13T14:36:29.063Z",
|
|
4
|
-
minor: "2025-
|
|
5
|
-
patch: "2025-
|
|
4
|
+
minor: "2025-09-11T07:12:59.993Z",
|
|
5
|
+
patch: "2025-09-11T07:12:59.993Z"
|
|
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.16.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.16.0-canary.5f8d98bccb38'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-11T07:12:59.993Z',\n\tpatch: '2025-09-11T07:12:59.993Z',\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
|
@@ -689,11 +689,17 @@ input,
|
|
|
689
689
|
}
|
|
690
690
|
|
|
691
691
|
.tl-text-measure {
|
|
692
|
-
position: absolute;
|
|
693
692
|
z-index: var(--tl-layer-canvas-hidden);
|
|
693
|
+
opacity: 0;
|
|
694
|
+
visibility: hidden;
|
|
695
|
+
|
|
696
|
+
/* pointer-events: all; */
|
|
697
|
+
/* opacity: 1; */
|
|
698
|
+
/* z-index: 99999; */
|
|
699
|
+
|
|
700
|
+
position: absolute;
|
|
694
701
|
top: 0px;
|
|
695
702
|
left: 0px;
|
|
696
|
-
opacity: 0;
|
|
697
703
|
width: max-content;
|
|
698
704
|
box-sizing: border-box;
|
|
699
705
|
pointer-events: none;
|
|
@@ -704,7 +710,6 @@ input,
|
|
|
704
710
|
border: none;
|
|
705
711
|
user-select: none;
|
|
706
712
|
contain: style paint;
|
|
707
|
-
visibility: hidden;
|
|
708
713
|
/* N.B. This property, while discouraged ("intended for Document Type Definition (DTD) designers") is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs. */
|
|
709
714
|
unicode-bidi: plaintext;
|
|
710
715
|
-webkit-user-select: none;
|
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": "3.16.0-canary.
|
|
4
|
+
"version": "3.16.0-canary.5f8d98bccb38",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@tiptap/core": "^2.9.1",
|
|
51
51
|
"@tiptap/pm": "^2.9.1",
|
|
52
52
|
"@tiptap/react": "^2.9.1",
|
|
53
|
-
"@tldraw/state": "3.16.0-canary.
|
|
54
|
-
"@tldraw/state-react": "3.16.0-canary.
|
|
55
|
-
"@tldraw/store": "3.16.0-canary.
|
|
56
|
-
"@tldraw/tlschema": "3.16.0-canary.
|
|
57
|
-
"@tldraw/utils": "3.16.0-canary.
|
|
58
|
-
"@tldraw/validate": "3.16.0-canary.
|
|
53
|
+
"@tldraw/state": "3.16.0-canary.5f8d98bccb38",
|
|
54
|
+
"@tldraw/state-react": "3.16.0-canary.5f8d98bccb38",
|
|
55
|
+
"@tldraw/store": "3.16.0-canary.5f8d98bccb38",
|
|
56
|
+
"@tldraw/tlschema": "3.16.0-canary.5f8d98bccb38",
|
|
57
|
+
"@tldraw/utils": "3.16.0-canary.5f8d98bccb38",
|
|
58
|
+
"@tldraw/validate": "3.16.0-canary.5f8d98bccb38",
|
|
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
|
@@ -268,7 +268,6 @@ export {
|
|
|
268
268
|
type TLGetShapeAtPointOptions,
|
|
269
269
|
type TLImageExportOptions,
|
|
270
270
|
type TLSvgExportOptions,
|
|
271
|
-
type TLSvgOptions,
|
|
272
271
|
type TLUpdatePointerOptions,
|
|
273
272
|
} from './lib/editor/types/misc-types'
|
|
274
273
|
export {
|
|
@@ -335,6 +334,7 @@ export {
|
|
|
335
334
|
type TestEnvironment,
|
|
336
335
|
type ValidLicenseKeyResult,
|
|
337
336
|
} from './lib/license/LicenseManager'
|
|
337
|
+
export { LICENSE_TIMEOUT } from './lib/license/LicenseProvider'
|
|
338
338
|
export { defaultTldrawOptions, type TldrawOptions } from './lib/options'
|
|
339
339
|
export {
|
|
340
340
|
Box,
|
|
@@ -486,14 +486,6 @@ export { type TLStoreWithStatus } from './lib/utils/sync/StoreWithStatus'
|
|
|
486
486
|
export { uniq } from './lib/utils/uniq'
|
|
487
487
|
export { openWindow } from './lib/utils/window-open'
|
|
488
488
|
|
|
489
|
-
/**
|
|
490
|
-
* @deprecated Licensing is now enabled in the tldraw SDK.
|
|
491
|
-
* @public */
|
|
492
|
-
export function debugEnableLicensing() {
|
|
493
|
-
// noop
|
|
494
|
-
return
|
|
495
|
-
}
|
|
496
|
-
|
|
497
489
|
registerTldrawLibraryVersion(
|
|
498
490
|
(globalThis as any).TLDRAW_LIBRARY_NAME,
|
|
499
491
|
(globalThis as any).TLDRAW_LIBRARY_VERSION,
|
package/src/lib/TldrawEditor.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MigrationSequence, Store } from '@tldraw/store'
|
|
2
2
|
import { TLShape, TLStore, TLStoreSnapshot } from '@tldraw/tlschema'
|
|
3
3
|
import { annotateError, Required } from '@tldraw/utils'
|
|
4
|
+
import classNames from 'classnames'
|
|
4
5
|
import React, {
|
|
5
6
|
memo,
|
|
6
7
|
ReactNode,
|
|
@@ -12,8 +13,6 @@ import React, {
|
|
|
12
13
|
useState,
|
|
13
14
|
useSyncExternalStore,
|
|
14
15
|
} from 'react'
|
|
15
|
-
|
|
16
|
-
import classNames from 'classnames'
|
|
17
16
|
import { version } from '../version'
|
|
18
17
|
import { DefaultErrorFallback } from './components/default-components/DefaultErrorFallback'
|
|
19
18
|
import { OptionalErrorBoundary } from './components/ErrorBoundary'
|
|
@@ -189,13 +188,6 @@ export interface TldrawEditorBaseProps {
|
|
|
189
188
|
*/
|
|
190
189
|
deepLinks?: true | TLDeepLinkOptions
|
|
191
190
|
|
|
192
|
-
/**
|
|
193
|
-
* Predicate for whether or not a shape should be hidden.
|
|
194
|
-
*
|
|
195
|
-
* @deprecated Use {@link TldrawEditorBaseProps#getShapeVisibility} instead.
|
|
196
|
-
*/
|
|
197
|
-
isShapeHidden?(shape: TLShape, editor: Editor): boolean
|
|
198
|
-
|
|
199
191
|
/**
|
|
200
192
|
* Provides a way to hide shapes.
|
|
201
193
|
*
|
|
@@ -412,8 +404,6 @@ function TldrawEditorWithReadyStore({
|
|
|
412
404
|
options,
|
|
413
405
|
licenseKey,
|
|
414
406
|
deepLinks: _deepLinks,
|
|
415
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
416
|
-
isShapeHidden,
|
|
417
407
|
getShapeVisibility,
|
|
418
408
|
assetUrls,
|
|
419
409
|
}: Required<
|
|
@@ -473,7 +463,6 @@ function TldrawEditorWithReadyStore({
|
|
|
473
463
|
textOptions,
|
|
474
464
|
options,
|
|
475
465
|
licenseKey,
|
|
476
|
-
isShapeHidden,
|
|
477
466
|
getShapeVisibility,
|
|
478
467
|
fontAssetUrls: assetUrls?.fonts,
|
|
479
468
|
})
|
|
@@ -509,7 +498,6 @@ function TldrawEditorWithReadyStore({
|
|
|
509
498
|
user,
|
|
510
499
|
setEditor,
|
|
511
500
|
licenseKey,
|
|
512
|
-
isShapeHidden,
|
|
513
501
|
getShapeVisibility,
|
|
514
502
|
textOptions,
|
|
515
503
|
assetUrls,
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -116,7 +116,6 @@ import {
|
|
|
116
116
|
} from '../constants'
|
|
117
117
|
import { exportToSvg } from '../exports/exportToSvg'
|
|
118
118
|
import { getSvgAsImage } from '../exports/getSvgAsImage'
|
|
119
|
-
import { tlenv } from '../globals/environment'
|
|
120
119
|
import { tlmenus } from '../globals/menus'
|
|
121
120
|
import { tltime } from '../globals/time'
|
|
122
121
|
import { TldrawOptions, defaultTldrawOptions } from '../options'
|
|
@@ -244,16 +243,6 @@ export interface TLEditorOptions {
|
|
|
244
243
|
options?: Partial<TldrawOptions>
|
|
245
244
|
licenseKey?: string
|
|
246
245
|
fontAssetUrls?: { [key: string]: string | undefined }
|
|
247
|
-
/**
|
|
248
|
-
* A predicate that should return true if the given shape should be hidden.
|
|
249
|
-
*
|
|
250
|
-
* @deprecated Use {@link Editor#getShapeVisibility} instead.
|
|
251
|
-
*
|
|
252
|
-
* @param shape - The shape to check.
|
|
253
|
-
* @param editor - The editor instance.
|
|
254
|
-
*/
|
|
255
|
-
isShapeHidden?(shape: TLShape, editor: Editor): boolean
|
|
256
|
-
|
|
257
246
|
/**
|
|
258
247
|
* Provides a way to hide shapes.
|
|
259
248
|
*
|
|
@@ -309,21 +298,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
309
298
|
autoFocus,
|
|
310
299
|
inferDarkMode,
|
|
311
300
|
options,
|
|
312
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
313
|
-
isShapeHidden,
|
|
314
301
|
getShapeVisibility,
|
|
315
302
|
fontAssetUrls,
|
|
316
303
|
}: TLEditorOptions) {
|
|
317
304
|
super()
|
|
318
|
-
assert(
|
|
319
|
-
!(isShapeHidden && getShapeVisibility),
|
|
320
|
-
'Cannot use both isShapeHidden and getShapeVisibility'
|
|
321
|
-
)
|
|
322
305
|
|
|
323
|
-
this._getShapeVisibility =
|
|
324
|
-
? // eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
325
|
-
(shape: TLShape, editor: Editor) => (isShapeHidden(shape, editor) ? 'hidden' : 'inherit')
|
|
326
|
-
: getShapeVisibility
|
|
306
|
+
this._getShapeVisibility = getShapeVisibility
|
|
327
307
|
|
|
328
308
|
this.options = { ...defaultTldrawOptions, ...options }
|
|
329
309
|
|
|
@@ -907,14 +887,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
907
887
|
*/
|
|
908
888
|
readonly fonts: FontManager
|
|
909
889
|
|
|
910
|
-
/**
|
|
911
|
-
* A manager for the editor's environment.
|
|
912
|
-
*
|
|
913
|
-
* @deprecated This is deprecated and will be removed in a future version. Use the `tlenv` global export instead.
|
|
914
|
-
* @public
|
|
915
|
-
*/
|
|
916
|
-
readonly environment = tlenv
|
|
917
|
-
|
|
918
890
|
/**
|
|
919
891
|
* A manager for the editor's scribbles.
|
|
920
892
|
*
|
|
@@ -1119,35 +1091,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1119
1091
|
return this.history.getNumRedos() > 0
|
|
1120
1092
|
}
|
|
1121
1093
|
|
|
1122
|
-
/**
|
|
1123
|
-
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1124
|
-
* any redos.
|
|
1125
|
-
*
|
|
1126
|
-
* @example
|
|
1127
|
-
* ```ts
|
|
1128
|
-
* editor.mark()
|
|
1129
|
-
* editor.mark('flip shapes')
|
|
1130
|
-
* ```
|
|
1131
|
-
*
|
|
1132
|
-
* @param markId - The mark's id, usually the reason for adding the mark.
|
|
1133
|
-
*
|
|
1134
|
-
* @public
|
|
1135
|
-
* @deprecated use {@link Editor.markHistoryStoppingPoint} instead
|
|
1136
|
-
*/
|
|
1137
|
-
mark(markId?: string): this {
|
|
1138
|
-
if (typeof markId === 'string') {
|
|
1139
|
-
console.warn(
|
|
1140
|
-
`[tldraw] \`editor.history.mark("${markId}")\` is deprecated. Please use \`const myMarkId = editor.markHistoryStoppingPoint()\` instead.`
|
|
1141
|
-
)
|
|
1142
|
-
} else {
|
|
1143
|
-
console.warn(
|
|
1144
|
-
'[tldraw] `editor.mark()` is deprecated. Use `editor.markHistoryStoppingPoint()` instead.'
|
|
1145
|
-
)
|
|
1146
|
-
}
|
|
1147
|
-
this.history._mark(markId ?? uniqueId())
|
|
1148
|
-
return this
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
1094
|
/**
|
|
1152
1095
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1153
1096
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1272,13 +1215,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1272
1215
|
return this
|
|
1273
1216
|
}
|
|
1274
1217
|
|
|
1275
|
-
/**
|
|
1276
|
-
* @deprecated Use `Editor.run` instead.
|
|
1277
|
-
*/
|
|
1278
|
-
batch(fn: () => void, opts?: TLEditorRunOptions): this {
|
|
1279
|
-
return this.run(fn, opts)
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
1218
|
/* --------------------- Errors --------------------- */
|
|
1283
1219
|
|
|
1284
1220
|
/** @internal */
|
|
@@ -1580,54 +1516,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1580
1516
|
|
|
1581
1517
|
menus = tlmenus.forContext(this.contextId)
|
|
1582
1518
|
|
|
1583
|
-
/**
|
|
1584
|
-
* @deprecated Use `editor.menus.getOpenMenus` instead.
|
|
1585
|
-
*
|
|
1586
|
-
* @public
|
|
1587
|
-
*/
|
|
1588
|
-
@computed getOpenMenus(): string[] {
|
|
1589
|
-
return this.menus.getOpenMenus()
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
/**
|
|
1593
|
-
* @deprecated Use `editor.menus.addOpenMenu` instead.
|
|
1594
|
-
*
|
|
1595
|
-
* @public
|
|
1596
|
-
*/
|
|
1597
|
-
addOpenMenu(id: string): this {
|
|
1598
|
-
this.menus.addOpenMenu(id)
|
|
1599
|
-
return this
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
/**
|
|
1603
|
-
* @deprecated Use `editor.menus.deleteOpenMenu` instead.
|
|
1604
|
-
*
|
|
1605
|
-
* @public
|
|
1606
|
-
*/
|
|
1607
|
-
deleteOpenMenu(id: string): this {
|
|
1608
|
-
this.menus.deleteOpenMenu(id)
|
|
1609
|
-
return this
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
/**
|
|
1613
|
-
* @deprecated Use `editor.menus.clearOpenMenus` instead.
|
|
1614
|
-
*
|
|
1615
|
-
* @public
|
|
1616
|
-
*/
|
|
1617
|
-
clearOpenMenus(): this {
|
|
1618
|
-
this.menus.clearOpenMenus()
|
|
1619
|
-
return this
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
/**
|
|
1623
|
-
* @deprecated Use `editor.menus.hasAnyOpenMenus` instead.
|
|
1624
|
-
*
|
|
1625
|
-
* @public
|
|
1626
|
-
*/
|
|
1627
|
-
@computed getIsMenuOpen(): boolean {
|
|
1628
|
-
return this.menus.hasAnyOpenMenus()
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
1519
|
/* --------------------- Cursor --------------------- */
|
|
1632
1520
|
|
|
1633
1521
|
/**
|
|
@@ -5839,11 +5727,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5839
5727
|
return shapeIds
|
|
5840
5728
|
}
|
|
5841
5729
|
|
|
5842
|
-
/** @deprecated Use {@link Editor.getDraggingOverShape} instead */
|
|
5843
|
-
getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
|
|
5844
|
-
return this.getDraggingOverShape(point, droppingShapes)
|
|
5845
|
-
}
|
|
5846
|
-
|
|
5847
5730
|
/**
|
|
5848
5731
|
* Get the shape that some shapes should be dropped on at a given point.
|
|
5849
5732
|
*
|
|
@@ -9459,13 +9342,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9459
9342
|
}
|
|
9460
9343
|
}
|
|
9461
9344
|
|
|
9462
|
-
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
|
|
9463
|
-
async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
|
|
9464
|
-
const result = await this.getSvgElement(shapes, opts)
|
|
9465
|
-
if (!result) return undefined
|
|
9466
|
-
return result.svg
|
|
9467
|
-
}
|
|
9468
|
-
|
|
9469
9345
|
/**
|
|
9470
9346
|
* Get an exported image of the given shapes.
|
|
9471
9347
|
*
|
|
@@ -7,6 +7,12 @@ function fromScratch(editor: Editor): Set<TLShapeId> {
|
|
|
7
7
|
const viewportPageBounds = editor.getViewportPageBounds()
|
|
8
8
|
const notVisibleShapes = new Set<TLShapeId>()
|
|
9
9
|
shapesIds.forEach((id) => {
|
|
10
|
+
const shape = editor.getShape(id)
|
|
11
|
+
if (!shape) return
|
|
12
|
+
|
|
13
|
+
const canCull = editor.getShapeUtil(shape.type).canCull(shape)
|
|
14
|
+
if (!canCull) return
|
|
15
|
+
|
|
10
16
|
// If the shape is fully outside of the viewport page bounds, add it to the set.
|
|
11
17
|
// We'll ignore masks here, since they're more expensive to compute and the overhead is not worth it.
|
|
12
18
|
const pageBounds = editor.getShapePageBounds(id)
|
|
@@ -283,6 +283,17 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
283
283
|
return true
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Whether this shape can be culled. By default, shapes are culled for
|
|
288
|
+
* performance reasons when they are outside of the viewport. Culled shapes are still rendered
|
|
289
|
+
* to the DOM, but have their `display` property set to `none`.
|
|
290
|
+
*
|
|
291
|
+
* @param shape - The shape.
|
|
292
|
+
*/
|
|
293
|
+
canCull(_shape: Shape): boolean {
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
|
|
286
297
|
/**
|
|
287
298
|
* Does this shape provide a background for its children? If this is true,
|
|
288
299
|
* then any children with a `renderBackground` method will have their
|
|
@@ -72,12 +72,6 @@ export interface TLImageExportOptions extends TLSvgExportOptions {
|
|
|
72
72
|
format?: TLExportType
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
/**
|
|
76
|
-
* @public
|
|
77
|
-
* @deprecated use {@link TLImageExportOptions} instead
|
|
78
|
-
*/
|
|
79
|
-
export type TLSvgOptions = TLImageExportOptions
|
|
80
|
-
|
|
81
75
|
/** @public */
|
|
82
76
|
export interface TLCameraMoveOptions {
|
|
83
77
|
/** Whether to move the camera immediately, rather than on the next tick. */
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { RefObject, useEffect } from 'react'
|
|
2
2
|
import { preventDefault } from '../utils/dom'
|
|
3
3
|
import { useContainer } from './useContainer'
|
|
4
|
+
import { useMaybeEditor } from './useEditor'
|
|
4
5
|
|
|
5
6
|
/** @public */
|
|
6
7
|
export function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement>) {
|
|
7
8
|
if (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')
|
|
8
9
|
const container = useContainer()
|
|
10
|
+
const editor = useMaybeEditor()
|
|
9
11
|
|
|
10
12
|
useEffect(() => {
|
|
11
13
|
function onMouseOver(e: MouseEvent) {
|
|
14
|
+
if (!editor?.getInstanceState().isFocused) return
|
|
12
15
|
if ((e as any).isSpecialRedispatchedEvent) return
|
|
13
16
|
preventDefault(e)
|
|
14
17
|
const cvs = container.querySelector('.tl-canvas')
|
|
@@ -25,5 +28,5 @@ export function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement>) {
|
|
|
25
28
|
return () => {
|
|
26
29
|
elm.removeEventListener('mouseover', onMouseOver)
|
|
27
30
|
}
|
|
28
|
-
}, [container, ref])
|
|
31
|
+
}, [container, editor, ref])
|
|
29
32
|
}
|