UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

217 lines (216 loc) • 10.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var reparenting_exports = {}; __export(reparenting_exports, { getDroppedShapesToNewParents: () => getDroppedShapesToNewParents, kickoutOccludedShapes: () => kickoutOccludedShapes }); module.exports = __toCommonJS(reparenting_exports); var import_state = require("@tldraw/state"); var import_utils = require("@tldraw/utils"); var import_intersect = require("../primitives/intersect"); function kickoutOccludedShapes(editor, shapeIds, opts) { const parentsToCheck = /* @__PURE__ */ new Set(); for (const id of shapeIds) { const shape = editor.getShape(id); if (!shape) continue; parentsToCheck.add(shape); const parent = editor.getShape(shape.parentId); if (!parent) continue; parentsToCheck.add(parent); } const parentsToLostChildren = /* @__PURE__ */ new Map(); for (const parent of parentsToCheck) { const childIds = editor.getSortedChildIdsForParent(parent); if (opts?.filter && !opts.filter(parent)) { parentsToLostChildren.set(parent, childIds); } else { const overlappingChildren = getOverlappingShapes(editor, parent.id, childIds); if (overlappingChildren.length < childIds.length) { const parentUtil = editor.getShapeUtil(parent); const lostChildIds = childIds.filter((id) => { if (overlappingChildren.includes(id)) return false; const child = editor.getShape(id); if (!child) return false; return parentUtil.canRemoveChildrenOfType(parent, child.type); }); if (lostChildIds.length > 0) { parentsToLostChildren.set(parent, lostChildIds); } } } } const sortedShapeIds = editor.getCurrentPageShapesSorted().map((s) => s.id); const parentsToNewChildren = {}; for (const [prevParent, lostChildrenIds] of parentsToLostChildren) { const lostChildren = (0, import_utils.compact)(lostChildrenIds.map((id) => editor.getShape(id))); const { reparenting, remainingShapesToReparent } = getDroppedShapesToNewParents( editor, lostChildren, (shape, maybeNewParent) => { if (opts?.filter && !opts.filter(maybeNewParent)) return false; return maybeNewParent.id !== prevParent.id && sortedShapeIds.indexOf(maybeNewParent.id) < sortedShapeIds.indexOf(shape.id); } ); reparenting.forEach((childrenToReparent, newParentId) => { if (childrenToReparent.length === 0) return; if (!parentsToNewChildren[newParentId]) { parentsToNewChildren[newParentId] = { parentId: newParentId, shapeIds: [] }; } parentsToNewChildren[newParentId].shapeIds.push(...childrenToReparent.map((s) => s.id)); }); if (remainingShapesToReparent.size > 0) { const newParentId = editor.findShapeAncestor(prevParent, (s) => editor.isShapeOfType(s, "group"))?.id ?? editor.getCurrentPageId(); remainingShapesToReparent.forEach((shape) => { if (!parentsToNewChildren[newParentId]) { let insertIndexKey; const oldParentSiblingIds = editor.getSortedChildIdsForParent(newParentId); const oldParentIndex = oldParentSiblingIds.indexOf(prevParent.id); if (oldParentIndex > -1) { const siblingsIndexAbove = oldParentSiblingIds[oldParentIndex + 1]; const siblingAboveIndex = siblingsIndexAbove ? editor.getShape(siblingsIndexAbove).index : void 0; const indexKeyAbove = siblingAboveIndex && siblingAboveIndex > prevParent.index ? siblingAboveIndex : (0, import_utils.getIndexAbove)(prevParent.index); insertIndexKey = (0, import_utils.getIndexBetween)(prevParent.index, indexKeyAbove); } else { } parentsToNewChildren[newParentId] = { parentId: newParentId, shapeIds: [], index: insertIndexKey }; } parentsToNewChildren[newParentId].shapeIds.push(shape.id); }); } } editor.run(() => { Object.values(parentsToNewChildren).forEach(({ parentId, shapeIds: shapeIds2, index }) => { if (shapeIds2.length === 0) return; shapeIds2.sort((a, b) => sortedShapeIds.indexOf(a) < sortedShapeIds.indexOf(b) ? -1 : 1); editor.reparentShapes(shapeIds2, parentId, index); }); }); } function getOverlappingShapes(editor, shape, otherShapes) { if (otherShapes.length === 0) { return import_state.EMPTY_ARRAY; } const parentPageBounds = editor.getShapePageBounds(shape); if (!parentPageBounds) return import_state.EMPTY_ARRAY; const _shape = editor.getShape(shape); if (!_shape) return import_state.EMPTY_ARRAY; const parentGeometry = editor.getShapeGeometry(shape); const parentPageTransform = editor.getShapePageTransform(shape); const parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices); const clipPath = editor.getShapeUtil(_shape.type).getClipPath?.(_shape); const parentPageMaskVertices = clipPath ? parentPageTransform.applyToPoints(clipPath) : void 0; const parentPagePolygon = parentPageMaskVertices ? (0, import_intersect.intersectPolygonPolygon)(parentPageMaskVertices, parentPageCorners) : parentPageCorners; if (!parentPagePolygon) return import_state.EMPTY_ARRAY; return otherShapes.filter((childId) => { const shapePageBounds = editor.getShapePageBounds(childId); if (!shapePageBounds || !parentPageBounds.includes(shapePageBounds)) return false; const parentPolygonInShapeShape = editor.getShapePageTransform(childId).clone().invert().applyToPoints(parentPagePolygon); const geometry = editor.getShapeGeometry(childId); return geometry.overlapsPolygon(parentPolygonInShapeShape); }); } function getDroppedShapesToNewParents(editor, shapes, cb) { const shapesToActuallyCheck = new Set(shapes); const movingGroups = /* @__PURE__ */ new Set(); for (const shape of shapes) { const parent = editor.getShapeParent(shape); if (parent && editor.isShapeOfType(parent, "group")) { if (!movingGroups.has(parent)) { movingGroups.add(parent); } } } for (const movingGroup of movingGroups) { const children = (0, import_utils.compact)( editor.getSortedChildIdsForParent(movingGroup).map((id) => editor.getShape(id)) ); for (const child of children) { shapesToActuallyCheck.delete(child); } shapesToActuallyCheck.add(movingGroup); } const shapeGroupIds = /* @__PURE__ */ new Map(); const reparenting = /* @__PURE__ */ new Map(); const remainingShapesToReparent = new Set(shapesToActuallyCheck); const potentialParentShapes = editor.getCurrentPageShapesSorted().filter((parentShape) => { if (remainingShapesToReparent.has(parentShape)) return false; const parentUtil = editor.getShapeUtil(parentShape); for (const childShape of remainingShapesToReparent) { if (parentUtil.canReceiveNewChildrenOfType(parentShape, childShape.type)) { return true; } } return false; }); parentCheck: for (let i = potentialParentShapes.length - 1; i >= 0; i--) { const parentShape = potentialParentShapes[i]; const parentShapeContainingGroupId = editor.findShapeAncestor( parentShape, (s) => editor.isShapeOfType(s, "group") )?.id; const parentGeometry = editor.getShapeGeometry(parentShape); const parentPageTransform = editor.getShapePageTransform(parentShape); const parentPageMaskVertices = editor.getShapeMask(parentShape); const parentPageCorners = parentPageTransform.applyToPoints(parentGeometry.vertices); const parentPagePolygon = parentPageMaskVertices ? (0, import_intersect.intersectPolygonPolygon)(parentPageMaskVertices, parentPageCorners) : parentPageCorners; if (!parentPagePolygon) continue parentCheck; const childrenToReparent = []; shapeCheck: for (const shape of remainingShapesToReparent) { if (parentShape.id === shape.id) continue shapeCheck; if (cb && !cb(shape, parentShape)) continue shapeCheck; if (!shapeGroupIds.has(shape.id)) { shapeGroupIds.set( shape.id, editor.findShapeAncestor(shape, (s) => editor.isShapeOfType(s, "group"))?.id ); } const shapeGroupId = shapeGroupIds.get(shape.id); if (shapeGroupId !== parentShapeContainingGroupId) continue shapeCheck; if (editor.findShapeAncestor(parentShape, (s) => shape.id === s.id)) continue shapeCheck; const parentPolygonInShapeSpace = editor.getShapePageTransform(shape).clone().invert().applyToPoints(parentPagePolygon); if (editor.getShapeGeometry(shape).overlapsPolygon(parentPolygonInShapeSpace)) { if (!editor.getShapeUtil(parentShape).canReceiveNewChildrenOfType?.(parentShape, shape.type)) continue shapeCheck; if (shape.parentId !== parentShape.id) { childrenToReparent.push(shape); } remainingShapesToReparent.delete(shape); continue shapeCheck; } } if (childrenToReparent.length) { reparenting.set(parentShape.id, childrenToReparent); } } return { // these are the shapes that will be reparented to new parents reparenting, // these are the shapes that will be reparented to the page or their ancestral group remainingShapesToReparent }; } //# sourceMappingURL=reparenting.js.map