Allow snapping of shapes to the frame when dragging inside the frame. (#2520)

Allows you to snap to frames when dragging inside them.


https://github.com/tldraw/tldraw/assets/2523721/41816b9b-5969-416d-af15-77b8f102ad21

Resolves #2471 

### Change Type

- [ ] `patch` — Bug fix
- [x] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Test Plan

1. Create a frame.
2. Add some shape inside.
3. Drag the shapes while holding `cmd` or turning on always snap. You
should be able to snap to the edges and the centre of the frame.

- [ ] Unit Tests
- [ ] End to end tests

### Release Notes

- Adds snapping to frames when dragging shapes inside a frame.
This commit is contained in:
Mitja Bezenšek 2024-01-19 16:17:33 +01:00 committed by GitHub
parent 12fe9d9c4e
commit be927df935
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 3 deletions

View file

@ -1,5 +1,13 @@
import { atom, computed, EMPTY_ARRAY } from '@tldraw/state'
import { TLGroupShape, TLParentId, TLShape, TLShapeId, VecModel } from '@tldraw/tlschema'
import {
isShapeId,
TLFrameShape,
TLGroupShape,
TLParentId,
TLShape,
TLShapeId,
VecModel,
} from '@tldraw/tlschema'
import { dedupe, deepCopy } from '@tldraw/utils'
import {
Box,
@ -244,6 +252,16 @@ export class SnapManager {
const snappableShapes: GapNode[] = []
const collectSnappableShapesFromParent = (parentId: TLParentId) => {
if (isShapeId(parentId)) {
const parent = editor.getShape(parentId)
if (parent && editor.isShapeOfType<TLFrameShape>(parent, 'frame')) {
snappableShapes.push({
id: parentId,
pageBounds: editor.getShapePageBounds(parentId)!,
isClosed: editor.getShapeGeometry(parent).isClosed,
})
}
}
const sortedChildIds = editor.getSortedChildIdsForParent(parentId)
for (const childId of sortedChildIds) {
// Skip any selected ids

View file

@ -492,12 +492,19 @@ describe('frame shapes', () => {
editor.setCurrentTool('select')
editor.pointerDown(150, 150, innerBoxId).pointerMove(150, 50).pointerMove(150, 148)
editor.keyDown('Control')
expect(editor.snaps.getLines()).toHaveLength(0)
let shapes = editor.snaps.getSnappableShapes()
// We can snap to the parent frame
expect(shapes).toHaveLength(1)
expect(shapes[0].id).toBe(frameId)
// move shape inside the frame to make sure it snaps in there
editor.reparentShapes([outerBoxId], frameId).pointerMove(150, 149, { ctrlKey: true })
expect(editor.snaps.getLines()).toHaveLength(1)
shapes = editor.snaps.getSnappableShapes()
expect(shapes).toHaveLength(2)
const ids = new Set(shapes.map((s) => s.id))
expect(ids).toContain(frameId)
expect(ids).toContain(outerBoxId)
})
it('masks its children', () => {