@tldraw/editor 4.5.0-canary.a6e749bd3e95 → 4.5.0-canary.b245c8ea38ef

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.
@@ -4268,6 +4268,11 @@ export declare abstract class Geometry2d {
4268
4268
  private _length?;
4269
4269
  get length(): number;
4270
4270
  getLength(_filters?: Geometry2dFilters): number;
4271
+ /**
4272
+ * Called after a hit test succeeds. Return `true` to reject the hit and allow
4273
+ * shapes behind this one to be selected instead (e.g. transparent image pixels).
4274
+ */
4275
+ ignoreHit(_point: VecLike): boolean;
4271
4276
  abstract getSvgPathData(first: boolean): string;
4272
4277
  }
4273
4278
 
@@ -8426,6 +8431,7 @@ export declare class TransformedGeometry2d extends Geometry2d {
8426
8431
  intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters): Vec[];
8427
8432
  intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters): VecLike[];
8428
8433
  intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters): VecLike[];
8434
+ ignoreHit(point: VecLike): boolean;
8429
8435
  transform(transform: MatModel, opts?: TransformedGeometry2dOptions): Geometry2d;
8430
8436
  getSvgPathData(): string;
8431
8437
  }
package/dist-cjs/index.js CHANGED
@@ -369,7 +369,7 @@ var import_LocalIndexedDb = require("./lib/utils/sync/LocalIndexedDb");
369
369
  var import_uniq = require("./lib/utils/uniq");
370
370
  (0, import_utils.registerTldrawLibraryVersion)(
371
371
  "@tldraw/editor",
372
- "4.5.0-canary.a6e749bd3e95",
372
+ "4.5.0-canary.b245c8ea38ef",
373
373
  "cjs"
374
374
  );
375
375
  //# sourceMappingURL=index.js.map
@@ -3990,6 +3990,9 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
3990
3990
  if (geometry.isClosed) {
3991
3991
  if (distance <= outerMargin || hitInside && distance <= 0 && distance > -innerMargin) {
3992
3992
  if (geometry.isFilled || isGroup && geometry.children[0].isFilled) {
3993
+ if (geometry.ignoreHit(pointInShapeSpace)) {
3994
+ continue;
3995
+ }
3993
3996
  return inMarginClosestToEdgeHit || shape;
3994
3997
  } else {
3995
3998
  if (this.getShapePageBounds(shape).contains(viewportPageBounds)) continue;
@@ -6725,38 +6728,10 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
6725
6728
  const bindingIdMap = new Map(
6726
6729
  preserveIds ? bindings.map((binding) => [binding.id, binding.id]) : bindings.map((binding) => [binding.id, (0, import_tlschema.createBindingId)()])
6727
6730
  );
6728
- let pasteParentId = this.getCurrentPageId();
6729
- let lowestDepth = Infinity;
6730
- let lowestAncestors = [];
6731
- for (const shape of this.getSelectedShapes()) {
6732
- if (lowestDepth === 0) break;
6733
- const isFrame = this.isShapeOfType(shape, "frame");
6734
- const ancestors = this.getShapeAncestors(shape);
6735
- if (isFrame) ancestors.push(shape);
6736
- const depth = isFrame ? ancestors.length + 1 : ancestors.length;
6737
- if (depth < lowestDepth) {
6738
- lowestDepth = depth;
6739
- lowestAncestors = ancestors;
6740
- pasteParentId = isFrame ? shape.id : shape.parentId;
6741
- } else if (depth === lowestDepth) {
6742
- if (lowestAncestors.length !== ancestors.length) {
6743
- throw Error(`Ancestors: ${lowestAncestors.length} !== ${ancestors.length}`);
6744
- }
6745
- if (lowestAncestors.length === 0) {
6746
- pasteParentId = currentPageId;
6747
- break;
6748
- } else {
6749
- pasteParentId = currentPageId;
6750
- for (let i = 0; i < lowestAncestors.length; i++) {
6751
- if (ancestors[i] !== lowestAncestors[i]) break;
6752
- pasteParentId = ancestors[i].id;
6753
- }
6754
- }
6755
- }
6756
- }
6731
+ let pasteParentId = currentPageId;
6732
+ const shapesById = new Map(shapes.map((s) => [s.id, s]));
6733
+ const rootShapesFromContent = (0, import_utils.compact)(rootShapeIds.map((id) => shapesById.get(id)));
6757
6734
  if (point) {
6758
- const shapesById = new Map(shapes.map((shape) => [shape.id, shape]));
6759
- const rootShapesFromContent = (0, import_utils.compact)(rootShapeIds.map((id) => shapesById.get(id)));
6760
6735
  if (rootShapesFromContent.length > 0) {
6761
6736
  const targetParent = this.getShapeAtPoint(point, {
6762
6737
  hitInside: true,
@@ -6764,38 +6739,55 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
6764
6739
  hitLocked: true,
6765
6740
  filter: (shape) => {
6766
6741
  const util = this.getShapeUtil(shape);
6767
- if (!util.canReceiveNewChildrenOfType) return false;
6768
6742
  return rootShapesFromContent.every(
6769
- (rootShape) => util.canReceiveNewChildrenOfType(shape, rootShape.type)
6743
+ (rootShape) => util.canReceiveNewChildrenOfType?.(shape, rootShape.type)
6770
6744
  );
6771
6745
  }
6772
6746
  });
6773
- pasteParentId = targetParent ? targetParent.id : currentPageId;
6774
- }
6775
- }
6776
- let isDuplicating = false;
6777
- if (!(0, import_tlschema.isPageId)(pasteParentId)) {
6778
- const parent = this.getShape(pasteParentId);
6779
- if (parent) {
6780
- if (!this.getViewportPageBounds().includes(this.getShapePageBounds(parent))) {
6781
- pasteParentId = currentPageId;
6782
- } else {
6783
- if (rootShapeIds.length === 1) {
6784
- const rootShape = shapes.find((s) => s.id === rootShapeIds[0]);
6785
- if (this.isShapeOfType(parent, "frame") && this.isShapeOfType(rootShape, "frame") && rootShape.props.w === parent?.props.w && rootShape.props.h === parent?.props.h) {
6786
- isDuplicating = true;
6747
+ pasteParentId = targetParent?.id ?? currentPageId;
6748
+ }
6749
+ } else if (!preservePosition) {
6750
+ const selectedShapes = this.getSelectedShapes();
6751
+ let selectedParent = null;
6752
+ const canAcceptAll = (candidate) => {
6753
+ const util = this.getShapeUtil(candidate);
6754
+ return rootShapesFromContent.every(
6755
+ (rs) => util.canReceiveNewChildrenOfType?.(candidate, rs.type)
6756
+ );
6757
+ };
6758
+ for (const shape of selectedShapes) {
6759
+ const candidate = canAcceptAll(shape) ? shape : this.findShapeAncestor(shape, canAcceptAll) ?? ((0, import_tlschema.isShapeId)(shape.parentId) ? this.getShape(shape.parentId) : null);
6760
+ if (!candidate) {
6761
+ selectedParent = null;
6762
+ break;
6763
+ }
6764
+ if (!selectedParent) {
6765
+ selectedParent = candidate;
6766
+ } else if (selectedParent.id !== candidate.id) {
6767
+ const spAncestors = this.getShapeAncestors(selectedParent);
6768
+ if (canAcceptAll(selectedParent)) spAncestors.push(selectedParent);
6769
+ const acceptingAncestors = spAncestors.filter(canAcceptAll);
6770
+ const candidateAncestorIds = /* @__PURE__ */ new Set([
6771
+ candidate.id,
6772
+ ...this.getShapeAncestors(candidate).map((a) => a.id)
6773
+ ]);
6774
+ let common = null;
6775
+ for (let i = acceptingAncestors.length - 1; i >= 0; i--) {
6776
+ if (candidateAncestorIds.has(acceptingAncestors[i].id)) {
6777
+ common = acceptingAncestors[i];
6778
+ break;
6787
6779
  }
6788
6780
  }
6781
+ selectedParent = common;
6782
+ if (!selectedParent) break;
6789
6783
  }
6790
- } else {
6791
- pasteParentId = currentPageId;
6792
6784
  }
6793
- }
6794
- if (!isDuplicating) {
6795
- isDuplicating = shapeIdMap.has(pasteParentId);
6796
- }
6797
- if (isDuplicating) {
6798
- pasteParentId = this.getShape(pasteParentId).parentId;
6785
+ if (selectedParent && shapeIdMap.has(selectedParent.id)) {
6786
+ selectedParent = null;
6787
+ }
6788
+ if (selectedParent) {
6789
+ pasteParentId = selectedParent.id;
6790
+ }
6799
6791
  }
6800
6792
  let index = this.getHighestIndexForParent(pasteParentId);
6801
6793
  const rootShapes = [];
@@ -6859,22 +6851,17 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
6859
6851
  })
6860
6852
  );
6861
6853
  this.run(() => {
6862
- if (assetsToCreate.length > 0) {
6863
- this.createAssets(assetsToCreate);
6864
- }
6854
+ if (assetsToCreate.length > 0) this.createAssets(assetsToCreate);
6865
6855
  this.createShapes(newShapes);
6866
6856
  this.createBindings(newBindings);
6867
- if (select) {
6868
- this.select(...rootShapes.map((s) => s.id));
6869
- }
6857
+ if (select) this.select(...rootShapes.map((s) => s.id));
6870
6858
  if (pasteParentId !== currentPageId) {
6871
6859
  this.reparentShapes(
6872
6860
  rootShapes.map((s) => s.id),
6873
6861
  pasteParentId
6874
6862
  );
6875
6863
  }
6876
- const newCreatedShapes = newShapes.map((s) => this.getShape(s.id));
6877
- const bounds = import_Box.Box.Common(newCreatedShapes.map((s) => this.getShapePageBounds(s)));
6864
+ const rootBounds = import_Box.Box.Common((0, import_utils.compact)(rootShapes.map((s) => this.getShapePageBounds(s.id))));
6878
6865
  if (point === void 0) {
6879
6866
  if (!(0, import_tlschema.isPageId)(pasteParentId)) {
6880
6867
  const shape = this.getShape(pasteParentId);
@@ -6882,40 +6869,44 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
6882
6869
  this.getShapePageTransform(shape),
6883
6870
  this.getShapeGeometry(shape).bounds.center
6884
6871
  );
6872
+ } else if (preservePosition) {
6873
+ point = rootBounds.center;
6885
6874
  } else {
6886
6875
  const viewportPageBounds = this.getViewportPageBounds();
6887
- if (preservePosition || viewportPageBounds.includes(import_Box.Box.From(bounds))) {
6888
- point = bounds.center;
6889
- } else {
6890
- point = viewportPageBounds.center;
6891
- }
6892
- }
6893
- }
6894
- if (rootShapes.length === 1) {
6895
- const onlyRoot = rootShapes[0];
6896
- if (this.isShapeOfType(onlyRoot, "frame")) {
6897
- while (this.getShapesAtPoint(point).some(
6898
- (shape) => this.isShapeOfType(shape, "frame") && shape.props.w === onlyRoot.props.w && shape.props.h === onlyRoot.props.h
6899
- )) {
6900
- point.x += bounds.w + 16;
6901
- }
6876
+ const anyOverlap = rootShapes.some((s) => {
6877
+ const b = this.getShapePageBounds(s.id);
6878
+ return b && viewportPageBounds.collides(b);
6879
+ });
6880
+ point = anyOverlap ? rootBounds.center : viewportPageBounds.center;
6902
6881
  }
6903
6882
  }
6904
6883
  const pageCenter = import_Box.Box.Common(
6905
6884
  (0, import_utils.compact)(rootShapes.map(({ id }) => this.getShapePageBounds(id)))
6906
6885
  ).center;
6907
6886
  const offset = import_Vec.Vec.Sub(point, pageCenter);
6908
- this.updateShapes(
6909
- rootShapes.map(({ id }) => {
6910
- const s = this.getShape(id);
6911
- const localRotation = this.getShapeParentTransform(id).decompose().rotation;
6912
- const localDelta = import_Vec.Vec.Rot(offset, -localRotation);
6913
- return { id: s.id, type: s.type, x: s.x + localDelta.x, y: s.y + localDelta.y };
6914
- })
6915
- );
6887
+ if (offset.x !== 0 || offset.y !== 0) {
6888
+ this.updateShapes(
6889
+ rootShapes.map(({ id }) => {
6890
+ const s = this.getShape(id);
6891
+ const localRotation = this.getShapeParentTransform(id).decompose().rotation;
6892
+ const localDelta = import_Vec.Vec.Rot(offset, -localRotation);
6893
+ return { id: s.id, type: s.type, x: s.x + localDelta.x, y: s.y + localDelta.y };
6894
+ })
6895
+ );
6896
+ }
6916
6897
  if ((0, import_tlschema.isPageId)(pasteParentId)) {
6917
6898
  const currentRootShapes = (0, import_utils.compact)(rootShapes.map((s) => this.getShape(s.id)));
6918
- const { reparenting } = (0, import_reparenting.getDroppedShapesToNewParents)(this, currentRootShapes);
6899
+ const { reparenting } = (0, import_reparenting.getDroppedShapesToNewParents)(
6900
+ this,
6901
+ currentRootShapes,
6902
+ (shape, parent) => {
6903
+ if (shapeIdMap.has(parent.id)) return false;
6904
+ const shapeBounds = this.getShapePageBounds(shape);
6905
+ const parentBounds = this.getShapePageBounds(parent);
6906
+ if (!shapeBounds || !parentBounds) return false;
6907
+ return parentBounds.containsPoint(shapeBounds.center);
6908
+ }
6909
+ );
6919
6910
  reparenting.forEach((childrenToReparent, newParentId) => {
6920
6911
  if (childrenToReparent.length === 0) return;
6921
6912
  this.reparentShapes(
@@ -6924,6 +6915,17 @@ class Editor extends (_a = import_eventemitter3.default, _getIsShapeHiddenCache_
6924
6915
  );
6925
6916
  });
6926
6917
  }
6918
+ const newShapeIdSet = new Set(newShapes.map((s) => s.id));
6919
+ const shapesToKickout = rootShapes.map((s) => s.id).filter((id) => {
6920
+ const shape = this.getShape(id);
6921
+ if (!shape) return false;
6922
+ if ((0, import_tlschema.isPageId)(shape.parentId)) return false;
6923
+ const children = this.getSortedChildIdsForParent(id);
6924
+ return !children.some((childId) => newShapeIdSet.has(childId));
6925
+ });
6926
+ if (shapesToKickout.length > 0) {
6927
+ (0, import_reparenting.kickoutOccludedShapes)(this, shapesToKickout);
6928
+ }
6927
6929
  });
6928
6930
  return this;
6929
6931
  }