fix: remove scrollbar arrows from shapes + add collision slide-off
- Change inner div overflow from scroll to hidden, removing browser scrollbar arrows that appeared on every canvas shape - Add shape collision detection: shapes now slide off each other with an 8px gap instead of overlapping when dragged (pointer + touch) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0555b5fa7f
commit
15e6a9b9ba
|
|
@ -72,7 +72,7 @@ const styles = css`
|
|||
div {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
|
@ -249,6 +249,8 @@ export class FolkShape extends FolkElement {
|
|||
this.requestUpdate("rotation");
|
||||
}
|
||||
|
||||
static GAP = 8; // minimum gap between shapes
|
||||
|
||||
#highlighted = false;
|
||||
get highlighted() {
|
||||
return this.#highlighted;
|
||||
|
|
@ -348,6 +350,7 @@ export class FolkShape extends FolkElement {
|
|||
// Apply movement
|
||||
this.#rect.x += moveDelta.x;
|
||||
this.#rect.y += moveDelta.y;
|
||||
this.#resolveOverlaps(moveDelta.x, moveDelta.y);
|
||||
this.requestUpdate();
|
||||
this.#dispatchTransformEvent();
|
||||
}
|
||||
|
|
@ -442,6 +445,7 @@ export class FolkShape extends FolkElement {
|
|||
} else {
|
||||
this.x += moveDelta.x;
|
||||
this.y += moveDelta.y;
|
||||
this.#resolveOverlaps(moveDelta.x, moveDelta.y);
|
||||
}
|
||||
event.preventDefault();
|
||||
return;
|
||||
|
|
@ -603,6 +607,52 @@ export class FolkShape extends FolkElement {
|
|||
this.requestUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* After moving, push this shape away from any overlapping siblings.
|
||||
* Uses the direction of the move to decide which side to slide to.
|
||||
*/
|
||||
#resolveOverlaps(dx: number, dy: number) {
|
||||
const parent = this.parentElement;
|
||||
if (!parent) return;
|
||||
|
||||
const gap = FolkShape.GAP;
|
||||
const me = { x: this.x, y: this.y, w: this.width, h: this.height };
|
||||
|
||||
for (const sibling of parent.children) {
|
||||
if (sibling === this || !(sibling instanceof FolkShape)) continue;
|
||||
if (sibling.tagName.toLowerCase() === "folk-arrow") continue;
|
||||
|
||||
const other = { x: sibling.x, y: sibling.y, w: sibling.width, h: sibling.height };
|
||||
|
||||
// Check overlap (axis-aligned)
|
||||
const overlapX = me.x < other.x + other.w + gap && me.x + me.w + gap > other.x;
|
||||
const overlapY = me.y < other.y + other.h + gap && me.y + me.h + gap > other.y;
|
||||
|
||||
if (!overlapX || !overlapY) continue;
|
||||
|
||||
// Compute penetration depths from each side
|
||||
const pushRight = (other.x + other.w + gap) - me.x;
|
||||
const pushLeft = me.x + me.w + gap - other.x;
|
||||
const pushDown = (other.y + other.h + gap) - me.y;
|
||||
const pushUp = me.y + me.h + gap - other.y;
|
||||
|
||||
// Pick the axis with the smallest penetration, biased by move direction
|
||||
const minX = pushRight < pushLeft ? -pushRight : pushLeft;
|
||||
const minY = pushDown < pushUp ? -pushDown : pushUp;
|
||||
|
||||
if (Math.abs(minX) < Math.abs(minY)) {
|
||||
// Slide horizontally
|
||||
this.#rect.x += dx <= 0 ? -pushLeft : pushRight;
|
||||
} else {
|
||||
// Slide vertically
|
||||
this.#rect.y += dy <= 0 ? -pushUp : pushDown;
|
||||
}
|
||||
|
||||
me.x = this.#rect.x;
|
||||
me.y = this.#rect.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize shape to JSON for Automerge sync
|
||||
* Subclasses should override and call super.toJSON()
|
||||
|
|
|
|||
Loading…
Reference in New Issue