@tldraw/editor 4.3.0-canary.fd6b7f2a8adc → 4.3.0-next.2b3bfbba757b

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.
Files changed (48) hide show
  1. package/dist-cjs/index.d.ts +35 -55
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/editor/Editor.js +12 -1
  4. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  5. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  6. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  7. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  8. package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
  9. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  11. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
  12. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  13. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  14. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  15. package/dist-cjs/version.js +3 -3
  16. package/dist-cjs/version.js.map +1 -1
  17. package/dist-esm/index.d.mts +35 -55
  18. package/dist-esm/index.mjs +1 -1
  19. package/dist-esm/lib/editor/Editor.mjs +12 -1
  20. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  21. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  22. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  23. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  24. package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
  25. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  26. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  27. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
  28. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  29. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  30. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  31. package/dist-esm/version.mjs +3 -3
  32. package/dist-esm/version.mjs.map +1 -1
  33. package/package.json +10 -10
  34. package/src/lib/editor/Editor.test.ts +10 -10
  35. package/src/lib/editor/Editor.ts +59 -82
  36. package/src/lib/editor/bindings/BindingUtil.ts +9 -15
  37. package/src/lib/editor/derivations/bindingsIndex.ts +2 -2
  38. package/src/lib/editor/managers/FontManager/FontManager.test.ts +4 -14
  39. package/src/lib/editor/managers/SnapManager/SnapManager.ts +3 -3
  40. package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
  41. package/src/lib/editor/shapes/ShapeUtil.ts +8 -5
  42. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +3 -1
  43. package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +1 -2
  44. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +3 -3
  45. package/src/lib/exports/getSvgJsx.test.ts +19 -10
  46. package/src/lib/exports/getSvgJsx.tsx +5 -2
  47. package/src/lib/utils/reparenting.ts +5 -5
  48. package/src/version.ts +3 -3
@@ -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 { 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(s, 'group'))?.id ??\n\t\t\t\teditor.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 _shape = editor.getShape(shape)\n\tif (!_shape) return EMPTY_ARRAY\n\n\tconst pageTransform = editor.getShapePageTransform(shape)\n\tconst clipPath = editor.getShapeUtil(_shape.type).getClipPath?.(_shape)\n\n\tconst parentPageMaskVertices = clipPath ? pageTransform.applyToPoints(clipPath) : undefined\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(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(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(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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",
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 _shape = editor.getShape(shape)\n\tif (!_shape) return EMPTY_ARRAY\n\n\tconst pageTransform = editor.getShapePageTransform(shape)\n\tconst clipPath = editor.getShapeUtil(_shape.type).getClipPath?.(_shape)\n\n\tconst parentPageMaskVertices = clipPath ? pageTransform.applyToPoints(clipPath) : undefined\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
  }
@@ -1,8 +1,8 @@
1
- const version = "4.3.0-canary.fd6b7f2a8adc";
1
+ const version = "4.3.0-next.2b3bfbba757b";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2025-11-24T22:53:38.801Z",
5
- patch: "2025-11-24T22:53:38.801Z"
4
+ minor: "2025-11-25T08:43:48.918Z",
5
+ patch: "2025-11-25T08:43:48.918Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.fd6b7f2a8adc'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-11-24T22:53:38.801Z',\n\tpatch: '2025-11-24T22:53:38.801Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-next.2b3bfbba757b'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-11-25T08:43:48.918Z',\n\tpatch: '2025-11-25T08:43:48.918Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "tldraw infinite canvas SDK (editor).",
4
- "version": "4.3.0-canary.fd6b7f2a8adc",
4
+ "version": "4.3.0-next.2b3bfbba757b",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -47,15 +47,15 @@
47
47
  "context": "yarn run -T tsx ../../internal/scripts/context.ts"
48
48
  },
49
49
  "dependencies": {
50
- "@tiptap/core": "^3.6.2",
51
- "@tiptap/pm": "^3.6.2",
52
- "@tiptap/react": "^3.6.2",
53
- "@tldraw/state": "4.3.0-canary.fd6b7f2a8adc",
54
- "@tldraw/state-react": "4.3.0-canary.fd6b7f2a8adc",
55
- "@tldraw/store": "4.3.0-canary.fd6b7f2a8adc",
56
- "@tldraw/tlschema": "4.3.0-canary.fd6b7f2a8adc",
57
- "@tldraw/utils": "4.3.0-canary.fd6b7f2a8adc",
58
- "@tldraw/validate": "4.3.0-canary.fd6b7f2a8adc",
50
+ "@tiptap/core": "3.6.2",
51
+ "@tiptap/pm": "3.6.2",
52
+ "@tiptap/react": "3.6.2",
53
+ "@tldraw/state": "4.3.0-next.2b3bfbba757b",
54
+ "@tldraw/state-react": "4.3.0-next.2b3bfbba757b",
55
+ "@tldraw/store": "4.3.0-next.2b3bfbba757b",
56
+ "@tldraw/tlschema": "4.3.0-next.2b3bfbba757b",
57
+ "@tldraw/utils": "4.3.0-next.2b3bfbba757b",
58
+ "@tldraw/validate": "4.3.0-next.2b3bfbba757b",
59
59
  "@types/core-js": "^2.5.8",
60
60
  "@use-gesture/react": "^10.3.1",
61
61
  "classnames": "^2.5.1",
@@ -6,25 +6,25 @@ import {
6
6
  Rectangle2d,
7
7
  ShapeUtil,
8
8
  T,
9
- TLShape,
9
+ TLBaseShape,
10
10
  createShapeId,
11
11
  createTLStore,
12
12
  } from '../..'
13
13
  import { Editor } from './Editor'
14
14
  import { StateNode } from './tools/StateNode'
15
15
 
16
- const MY_CUSTOM_SHAPE_TYPE = 'my-custom-shape'
17
-
18
- declare module '@tldraw/tlschema' {
19
- export interface TLGlobalShapePropsMap {
20
- [MY_CUSTOM_SHAPE_TYPE]: { w: number; h: number; text: string | undefined; isFilled: boolean }
16
+ type ICustomShape = TLBaseShape<
17
+ 'my-custom-shape',
18
+ {
19
+ w: number
20
+ h: number
21
+ text: string | undefined
22
+ isFilled: boolean
21
23
  }
22
- }
23
-
24
- type ICustomShape = TLShape<typeof MY_CUSTOM_SHAPE_TYPE>
24
+ >
25
25
 
26
26
  class CustomShape extends ShapeUtil<ICustomShape> {
27
- static override type = MY_CUSTOM_SHAPE_TYPE
27
+ static override type = 'my-custom-shape' as const
28
28
  static override props: RecordProps<ICustomShape> = {
29
29
  w: T.number,
30
30
  h: T.number,
@@ -21,6 +21,7 @@ import {
21
21
  PageRecordType,
22
22
  StyleProp,
23
23
  StylePropValue,
24
+ TLArrowShape,
24
25
  TLAsset,
25
26
  TLAssetId,
26
27
  TLAssetPartial,
@@ -29,12 +30,12 @@ import {
29
30
  TLBindingId,
30
31
  TLBindingUpdate,
31
32
  TLCamera,
32
- TLCreateShapePartial,
33
33
  TLCursor,
34
34
  TLCursorType,
35
35
  TLDOCUMENT_ID,
36
36
  TLDocument,
37
37
  TLFrameShape,
38
+ TLGeoShape,
38
39
  TLGroupShape,
39
40
  TLHandle,
40
41
  TLINSTANCE_ID,
@@ -42,6 +43,7 @@ import {
42
43
  TLInstance,
43
44
  TLInstancePageState,
44
45
  TLInstancePresence,
46
+ TLNoteShape,
45
47
  TLPOINTER_ID,
46
48
  TLPage,
47
49
  TLPageId,
@@ -52,6 +54,8 @@ import {
52
54
  TLShapePartial,
53
55
  TLStore,
54
56
  TLStoreSnapshot,
57
+ TLUnknownBinding,
58
+ TLUnknownShape,
55
59
  TLVideoAsset,
56
60
  createBindingId,
57
61
  createShapeId,
@@ -443,7 +447,7 @@ export class Editor extends EventEmitter<TLEventMap> {
443
447
  let deletedBindings = new Map<TLBindingId, BindingOnDeleteOptions<any>>()
444
448
  const deletedShapeIds = new Set<TLShapeId>()
445
449
  const invalidParents = new Set<TLShapeId>()
446
- let invalidBindingTypes = new Set<TLBinding['type']>()
450
+ let invalidBindingTypes = new Set<string>()
447
451
  this.disposables.add(
448
452
  this.sideEffects.registerOperationCompleteHandler(() => {
449
453
  // this needs to be cleared here because further effects may delete more shapes
@@ -706,7 +710,7 @@ export class Editor extends EventEmitter<TLEventMap> {
706
710
  if (filtered.length > 0) {
707
711
  const commonGroupAncestor = this.findCommonAncestor(
708
712
  compact(filtered.map((id) => this.getShape(id))),
709
- (shape) => this.isShapeOfType(shape, 'group')
713
+ (shape) => this.isShapeOfType<TLGroupShape>(shape, 'group')
710
714
  )
711
715
 
712
716
  if (commonGroupAncestor) {
@@ -978,7 +982,7 @@ export class Editor extends EventEmitter<TLEventMap> {
978
982
  *
979
983
  * @public
980
984
  */
981
- shapeUtils: { readonly [K in string]?: ShapeUtil<TLShape> }
985
+ shapeUtils: { readonly [K in string]?: ShapeUtil<TLUnknownShape> }
982
986
 
983
987
  styleProps: { [key: string]: Map<StyleProp<any>, string> }
984
988
 
@@ -997,8 +1001,8 @@ export class Editor extends EventEmitter<TLEventMap> {
997
1001
  *
998
1002
  * @public
999
1003
  */
1000
- getShapeUtil<K extends TLShape['type']>(type: K): ShapeUtil<Extract<TLShape, { type: K }>>
1001
- getShapeUtil<S extends TLShape>(shape: S | TLShapePartial<S> | S['type']): ShapeUtil<S>
1004
+ getShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): ShapeUtil<S>
1005
+ getShapeUtil<S extends TLUnknownShape>(type: S['type']): ShapeUtil<S>
1002
1006
  getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T
1003
1007
  getShapeUtil(arg: string | { type: string }) {
1004
1008
  const type = typeof arg === 'string' ? arg : arg.type
@@ -1012,8 +1016,8 @@ export class Editor extends EventEmitter<TLEventMap> {
1012
1016
  *
1013
1017
  * @param shape - A shape, shape partial, or shape type.
1014
1018
  */
1015
- hasShapeUtil(shape: TLShape | TLShapePartial<TLShape>): boolean
1016
- hasShapeUtil(type: TLShape['type']): boolean
1019
+ hasShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): boolean
1020
+ hasShapeUtil<S extends TLUnknownShape>(type: S['type']): boolean
1017
1021
  hasShapeUtil<T extends ShapeUtil>(
1018
1022
  type: T extends ShapeUtil<infer R> ? R['type'] : string
1019
1023
  ): boolean
@@ -1028,7 +1032,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1028
1032
  *
1029
1033
  * @public
1030
1034
  */
1031
- bindingUtils: { readonly [K in string]?: BindingUtil<TLBinding> }
1035
+ bindingUtils: { readonly [K in string]?: BindingUtil<TLUnknownBinding> }
1032
1036
 
1033
1037
  /**
1034
1038
  * Get a binding util from a binding itself.
@@ -1045,8 +1049,8 @@ export class Editor extends EventEmitter<TLEventMap> {
1045
1049
  *
1046
1050
  * @public
1047
1051
  */
1048
- getBindingUtil<K extends TLBinding['type']>(type: K): BindingUtil<Extract<TLBinding, { type: K }>>
1049
- getBindingUtil<S extends TLBinding>(binding: S | { type: S['type'] }): BindingUtil<S>
1052
+ getBindingUtil<S extends TLUnknownBinding>(binding: S | { type: S['type'] }): BindingUtil<S>
1053
+ getBindingUtil<S extends TLUnknownBinding>(type: S['type']): BindingUtil<S>
1050
1054
  getBindingUtil<T extends BindingUtil>(
1051
1055
  type: T extends BindingUtil<infer R> ? R['type'] : string
1052
1056
  ): T
@@ -2216,7 +2220,7 @@ export class Editor extends EventEmitter<TLEventMap> {
2216
2220
  throw Error(`Editor.setFocusedGroup: Shape with id ${id} does not exist`)
2217
2221
  }
2218
2222
 
2219
- if (!this.isShapeOfType(shape, 'group')) {
2223
+ if (!this.isShapeOfType<TLGroupShape>(shape, 'group')) {
2220
2224
  throw Error(
2221
2225
  `Editor.setFocusedGroup: Cannot set focused group to shape of type ${shape.type}`
2222
2226
  )
@@ -2244,7 +2248,7 @@ export class Editor extends EventEmitter<TLEventMap> {
2244
2248
  if (focusedGroup) {
2245
2249
  // If we have a focused layer, look for an ancestor of the focused shape that is a group
2246
2250
  const match = this.findShapeAncestor(focusedGroup, (shape) =>
2247
- this.isShapeOfType(shape, 'group')
2251
+ this.isShapeOfType<TLGroupShape>(shape, 'group')
2248
2252
  )
2249
2253
  // If we have an ancestor that can become a focused layer, set it as the focused layer
2250
2254
  this.setFocusedGroup(match?.id ?? null)
@@ -5123,10 +5127,10 @@ export class Editor extends EventEmitter<TLEventMap> {
5123
5127
 
5124
5128
  // Check labels first
5125
5129
  if (
5126
- this.isShapeOfType(shape, 'frame') ||
5127
- ((this.isShapeOfType(shape, 'note') ||
5128
- this.isShapeOfType(shape, 'arrow') ||
5129
- (this.isShapeOfType(shape, 'geo') && shape.props.fill === 'none')) &&
5130
+ this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
5131
+ ((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
5132
+ this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
5133
+ (this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
5130
5134
  this.getShapeUtil(shape).getText(shape)?.trim())
5131
5135
  ) {
5132
5136
  for (const childGeometry of (geometry as Group2d).children) {
@@ -5136,7 +5140,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5136
5140
  }
5137
5141
  }
5138
5142
 
5139
- if (this.isShapeOfType(shape, 'frame')) {
5143
+ if (this.isShapeOfType<TLFrameShape>(shape, 'frame')) {
5140
5144
  // On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
5141
5145
  // this prevents clicks from passing through the body of a frame to shapes behind it.
5142
5146
 
@@ -5417,7 +5421,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5417
5421
  *
5418
5422
  * @example
5419
5423
  * ```ts
5420
- * const isArrowShape = isShapeOfType(someShape, 'arrow')
5424
+ * const isArrowShape = isShapeOfType<TLArrowShape>(someShape, 'arrow')
5421
5425
  * ```
5422
5426
  *
5423
5427
  * @param util - the TLShapeUtil constructor to test against
@@ -5425,16 +5429,15 @@ export class Editor extends EventEmitter<TLEventMap> {
5425
5429
  *
5426
5430
  * @public
5427
5431
  */
5428
- isShapeOfType<K extends TLShape['type']>(
5429
- shape: TLShape,
5430
- type: K
5431
- ): shape is Extract<TLShape, { type: K }>
5432
- isShapeOfType<T extends TLShape>(
5433
- shape: TLShape,
5432
+ isShapeOfType<T extends TLUnknownShape>(shape: TLUnknownShape, type: T['type']): shape is T
5433
+ isShapeOfType<T extends TLUnknownShape>(
5434
+ shapeId: TLUnknownShape['id'],
5435
+ type: T['type']
5436
+ ): shapeId is T['id']
5437
+ isShapeOfType<T extends TLUnknownShape>(
5438
+ arg: TLUnknownShape | TLUnknownShape['id'],
5434
5439
  type: T['type']
5435
- ): shape is Extract<TLShape, { type: T['type'] }>
5436
- isShapeOfType<T extends TLShape = TLShape>(shapeId: TLShapeId, type: T['type']): boolean
5437
- isShapeOfType(arg: TLShape | TLShapeId, type: TLShape['type']) {
5440
+ ) {
5438
5441
  const shape = typeof arg === 'string' ? this.getShape(arg) : arg
5439
5442
  if (!shape) return false
5440
5443
  return shape.type === type
@@ -5830,7 +5833,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5830
5833
 
5831
5834
  while (node) {
5832
5835
  if (
5833
- this.isShapeOfType(node, 'group') &&
5836
+ this.isShapeOfType<TLGroupShape>(node, 'group') &&
5834
5837
  focusedGroup?.id !== node.id &&
5835
5838
  !this.hasAncestor(focusedGroup, node.id) &&
5836
5839
  (filter?.(node) ?? true)
@@ -5872,15 +5875,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5872
5875
  * Get all bindings of a certain type _from_ a particular shape. These are the bindings whose
5873
5876
  * `fromId` matched the shape's ID.
5874
5877
  */
5875
- getBindingsFromShape<K extends TLBinding['type']>(
5876
- shape: TLShape | TLShapeId,
5877
- type: K
5878
- ): Extract<TLBinding, { type: K }>[]
5879
- getBindingsFromShape<Binding extends TLBinding = TLBinding>(
5880
- shape: TLShape | TLShapeId,
5881
- type: Binding['type']
5882
- ): Binding[]
5883
- getBindingsFromShape<Binding extends TLBinding = TLBinding>(
5878
+ getBindingsFromShape<Binding extends TLUnknownBinding = TLBinding>(
5884
5879
  shape: TLShape | TLShapeId,
5885
5880
  type: Binding['type']
5886
5881
  ): Binding[] {
@@ -5894,15 +5889,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5894
5889
  * Get all bindings of a certain type _to_ a particular shape. These are the bindings whose
5895
5890
  * `toId` matches the shape's ID.
5896
5891
  */
5897
- getBindingsToShape<K extends TLBinding['type']>(
5898
- shape: TLShape | TLShapeId,
5899
- type: K
5900
- ): Extract<TLBinding, { type: K }>[]
5901
- getBindingsToShape<Binding extends TLBinding = TLBinding>(
5902
- shape: TLShape | TLShapeId,
5903
- type: Binding['type']
5904
- ): Binding[]
5905
- getBindingsToShape<Binding extends TLBinding = TLBinding>(
5892
+ getBindingsToShape<Binding extends TLUnknownBinding = TLBinding>(
5906
5893
  shape: TLShape | TLShapeId,
5907
5894
  type: Binding['type']
5908
5895
  ): Binding[] {
@@ -5916,15 +5903,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5916
5903
  * Get all bindings involving a particular shape. This includes bindings where the shape is the
5917
5904
  * `fromId` or `toId`. If a type is provided, only bindings of that type are returned.
5918
5905
  */
5919
- getBindingsInvolvingShape<K extends TLBinding['type']>(
5920
- shape: TLShape | TLShapeId,
5921
- type: K
5922
- ): Extract<TLBinding, { type: K }>[]
5923
- getBindingsInvolvingShape<Binding extends TLBinding = TLBinding>(
5924
- shape: TLShape | TLShapeId,
5925
- type?: Binding['type']
5926
- ): Binding[]
5927
- getBindingsInvolvingShape<Binding extends TLBinding = TLBinding>(
5906
+ getBindingsInvolvingShape<Binding extends TLUnknownBinding = TLBinding>(
5928
5907
  shape: TLShape | TLShapeId,
5929
5908
  type?: Binding['type']
5930
5909
  ): Binding[] {
@@ -5946,7 +5925,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5946
5925
  if (!fromShape || !toShape) continue
5947
5926
  if (!this.canBindShapes({ fromShape, toShape, binding: partial })) continue
5948
5927
 
5949
- const util = this.getBindingUtil(partial.type)
5928
+ const util = this.getBindingUtil<TLUnknownBinding>(partial.type)
5950
5929
  const defaultProps = util.getDefaultProps()
5951
5930
  const binding = this.store.schema.types.binding.create({
5952
5931
  ...partial,
@@ -6051,7 +6030,7 @@ export class Editor extends EventEmitter<TLEventMap> {
6051
6030
  const toShapeType = typeof toShape === 'string' ? toShape : toShape.type
6052
6031
  const bindingType = typeof binding === 'string' ? binding : binding.type
6053
6032
 
6054
- const canBindOpts = { fromShapeType, toShapeType, bindingType } as const
6033
+ const canBindOpts = { fromShapeType, toShapeType, bindingType }
6055
6034
 
6056
6035
  if (fromShapeType === toShapeType) {
6057
6036
  return this.getShapeUtil(fromShapeType).canBind(canBindOpts)
@@ -6592,7 +6571,7 @@ export class Editor extends EventEmitter<TLEventMap> {
6592
6571
  const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)))
6593
6572
 
6594
6573
  for (const shape of shapesToFlipFirstPass) {
6595
- if (this.isShapeOfType(shape, 'group')) {
6574
+ if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
6596
6575
  const childrenOfGroups = compact(
6597
6576
  this.getSortedChildIdsForParent(shape.id).map((id) => this.getShape(id))
6598
6577
  )
@@ -7713,7 +7692,9 @@ export class Editor extends EventEmitter<TLEventMap> {
7713
7692
  *
7714
7693
  * @public
7715
7694
  */
7716
- canCreateShape(shape: OptionalKeys<TLShapePartial<TLShape>, 'id'> | TLShape['id']): boolean {
7695
+ canCreateShape<T extends TLUnknownShape>(
7696
+ shape: OptionalKeys<TLShapePartial<T>, 'id'> | T['id']
7697
+ ): boolean {
7717
7698
  return this.canCreateShapes([shape])
7718
7699
  }
7719
7700
 
@@ -7724,8 +7705,8 @@ export class Editor extends EventEmitter<TLEventMap> {
7724
7705
  *
7725
7706
  * @public
7726
7707
  */
7727
- canCreateShapes(
7728
- shapes: (TLShape['id'] | OptionalKeys<TLShapePartial<TLShape>, 'id'>)[]
7708
+ canCreateShapes<T extends TLUnknownShape>(
7709
+ shapes: (T['id'] | OptionalKeys<TLShapePartial<T>, 'id'>)[]
7729
7710
  ): boolean {
7730
7711
  return shapes.length + this.getCurrentPageShapeIds().size <= this.options.maxShapesPerPage
7731
7712
  }
@@ -7743,7 +7724,7 @@ export class Editor extends EventEmitter<TLEventMap> {
7743
7724
  *
7744
7725
  * @public
7745
7726
  */
7746
- createShape<TShape extends TLShape>(shape: TLCreateShapePartial<TShape>): this {
7727
+ createShape<T extends TLUnknownShape>(shape: OptionalKeys<TLShapePartial<T>, 'id'>): this {
7747
7728
  this.createShapes([shape])
7748
7729
  return this
7749
7730
  }
@@ -7761,7 +7742,7 @@ export class Editor extends EventEmitter<TLEventMap> {
7761
7742
  *
7762
7743
  * @public
7763
7744
  */
7764
- createShapes<TShape extends TLShape = TLShape>(shapes: TLCreateShapePartial<TShape>[]): this {
7745
+ createShapes<T extends TLUnknownShape>(shapes: OptionalKeys<TLShapePartial<T>, 'id'>[]): this {
7765
7746
  if (!Array.isArray(shapes)) {
7766
7747
  throw Error('Editor.createShapes: must provide an array of shapes or shape partials')
7767
7748
  }
@@ -8142,7 +8123,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8142
8123
  const highestIndex = shapesWithRootParent[shapesWithRootParent.length - 1]?.index
8143
8124
 
8144
8125
  this.run(() => {
8145
- this.createShapes([
8126
+ this.createShapes<TLGroupShape>([
8146
8127
  {
8147
8128
  id: groupId,
8148
8129
  type: 'group',
@@ -8212,7 +8193,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8212
8193
  const groups: TLGroupShape[] = []
8213
8194
 
8214
8195
  shapesToUngroup.forEach((shape) => {
8215
- if (this.isShapeOfType(shape, 'group')) {
8196
+ if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
8216
8197
  groups.push(shape)
8217
8198
  } else {
8218
8199
  idsToSelect.add(shape.id)
@@ -8258,7 +8239,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8258
8239
  *
8259
8240
  * @public
8260
8241
  */
8261
- updateShape<T extends TLShape = TLShape>(partial: TLShapePartial<T> | null | undefined) {
8242
+ updateShape<T extends TLUnknownShape>(partial: TLShapePartial<T> | null | undefined) {
8262
8243
  this.updateShapes([partial])
8263
8244
  return this
8264
8245
  }
@@ -8275,7 +8256,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8275
8256
  *
8276
8257
  * @public
8277
8258
  */
8278
- updateShapes<T extends TLShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
8259
+ updateShapes<T extends TLUnknownShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
8279
8260
  const compactedPartials: TLShapePartial<T>[] = Array(partials.length)
8280
8261
 
8281
8262
  for (let i = 0, n = partials.length; i < n; i++) {
@@ -8427,7 +8408,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8427
8408
  * @internal
8428
8409
  */
8429
8410
  private _extractSharedStyles(shape: TLShape, sharedStyleMap: SharedStyleMap) {
8430
- if (this.isShapeOfType(shape, 'group')) {
8411
+ if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
8431
8412
  // For groups, ignore the styles of the group shape and instead include the styles of the
8432
8413
  // group's children. These are the shapes that would have their styles changed if the
8433
8414
  // user called `setStyle` on the current selection.
@@ -8547,7 +8528,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8547
8528
  // For groups, ignore the opacity of the group shape and instead include
8548
8529
  // the opacity of the group's children. These are the shapes that would have
8549
8530
  // their opacity changed if the user called `setOpacity` on the current selection.
8550
- if (this.isShapeOfType(shape, 'group')) {
8531
+ if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
8551
8532
  for (const childId of this.getSortedChildIdsForParent(shape.id)) {
8552
8533
  addShape(childId)
8553
8534
  }
@@ -8608,7 +8589,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8608
8589
  // We can have many deep levels of grouped shape
8609
8590
  // Making a recursive function to look through all the levels
8610
8591
  const addShapeById = (shape: TLShape) => {
8611
- if (this.isShapeOfType(shape, 'group')) {
8592
+ if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
8612
8593
  const childIds = this.getSortedChildIdsForParent(shape)
8613
8594
  for (const childId of childIds) {
8614
8595
  addShapeById(this.getShape(childId)!)
@@ -8692,7 +8673,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8692
8673
  // We can have many deep levels of grouped shape
8693
8674
  // Making a recursive function to look through all the levels
8694
8675
  const addShapeById = (shape: TLShape) => {
8695
- if (this.isShapeOfType(shape, 'group')) {
8676
+ if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
8696
8677
  const childIds = this.getSortedChildIdsForParent(shape.id)
8697
8678
  for (const childId of childIds) {
8698
8679
  addShapeById(this.getShape(childId)!)
@@ -9117,7 +9098,7 @@ export class Editor extends EventEmitter<TLEventMap> {
9117
9098
  for (const shape of this.getSelectedShapes()) {
9118
9099
  if (lowestDepth === 0) break
9119
9100
 
9120
- const isFrame = this.isShapeOfType(shape, 'frame')
9101
+ const isFrame = this.isShapeOfType<TLFrameShape>(shape, 'frame')
9121
9102
  const ancestors = this.getShapeAncestors(shape)
9122
9103
  if (isFrame) ancestors.push(shape)
9123
9104
 
@@ -9156,8 +9137,8 @@ export class Editor extends EventEmitter<TLEventMap> {
9156
9137
  if (rootShapeIds.length === 1) {
9157
9138
  const rootShape = shapes.find((s) => s.id === rootShapeIds[0])!
9158
9139
  if (
9159
- this.isShapeOfType(parent, 'frame') &&
9160
- this.isShapeOfType(rootShape, 'frame') &&
9140
+ this.isShapeOfType<TLFrameShape>(parent, 'frame') &&
9141
+ this.isShapeOfType<TLFrameShape>(rootShape, 'frame') &&
9161
9142
  rootShape.props.w === parent?.props.w &&
9162
9143
  rootShape.props.h === parent?.props.h
9163
9144
  ) {
@@ -9332,11 +9313,11 @@ export class Editor extends EventEmitter<TLEventMap> {
9332
9313
  const onlyRoot = rootShapes[0] as TLFrameShape
9333
9314
  // If the old bounds are in the viewport...
9334
9315
  // todo: replace frame references with shapes that can accept children
9335
- if (this.isShapeOfType(onlyRoot, 'frame')) {
9316
+ if (this.isShapeOfType<TLFrameShape>(onlyRoot, 'frame')) {
9336
9317
  while (
9337
9318
  this.getShapesAtPoint(point).some(
9338
9319
  (shape) =>
9339
- this.isShapeOfType(shape, 'frame') &&
9320
+ this.isShapeOfType<TLFrameShape>(shape, 'frame') &&
9340
9321
  shape.props.w === onlyRoot.props.w &&
9341
9322
  shape.props.h === onlyRoot.props.h
9342
9323
  )
@@ -10251,7 +10232,6 @@ export class Editor extends EventEmitter<TLEventMap> {
10251
10232
  }
10252
10233
  }
10253
10234
 
10254
- this.emit('event', info)
10255
10235
  this.root.handleEvent(info)
10256
10236
  return
10257
10237
  }
@@ -10750,10 +10730,7 @@ function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
10750
10730
 
10751
10731
  function applyPartialToRecordWithProps<
10752
10732
  T extends UnknownRecord & { type: string; props: object; meta: object },
10753
- >(
10754
- prev: T,
10755
- partial?: T extends T ? Omit<Partial<T>, 'props'> & { props?: Partial<T['props']> } : never
10756
- ): T {
10733
+ >(prev: T, partial?: Partial<T> & { props?: Partial<T['props']> }): T {
10757
10734
  if (!partial) return prev
10758
10735
  let next = null as null | T
10759
10736
  const entries = Object.entries(partial)