tldraw/packages/editor
Steve Ruiz 01bc73e750
Editor.run, locked shapes improvements (#4042)
This PR:
- creates `Editor.run` (previously `Editor.batch`)
- deprecates `Editor.batch`
- introduces a `ignoreShapeLock` option top the `Editor.run` method that
allows the editor to update and delete locked shapes
- fixes a bug with `updateShapes` that allowed updating locked shapes
- fixes a bug with `ungroupShapes` that allowed ungrouping locked shapes
- makes `Editor.history` private
- adds `Editor.squashToMark`
- adds `Editor.clearHistory`
- removes `History.ignore`
- removes `History.onBatchComplete`
- makes `_updateCurrentPageState` private

```ts
editor.run(() => {
  editor.updateShape({ ...myLockedShape })
  editor.deleteShape(myLockedShape)
}, { ignoreShapeLock: true })
```

It also:

## How it works

Normally `updateShape`/`updateShapes` and `deleteShape`/`deleteShapes`
do not effect locked shapes.

```ts
const myLockedShape = editor.getShape(myShapeId)!

// no change from update
editor.updateShape({ ...myLockedShape, x: 100 })
expect(editor.getShape(myShapeId)).toMatchObject(myLockedShape)

// no change from delete
editor.deleteShapes([myLockedShape])
expect(editor.getShape(myShapeId)).toMatchObject(myLockedShape)
```

The new `run` method adds the option to ignore shape lock.

```ts
const myLockedShape = editor.getShape(myShapeId)!

// update works
editor.run(() => { editor.updateShape({ ...myLockedShape, x: 100 }) }, { ignoreShapeLock: true })
expect(editor.getShape(myShapeId)).toMatchObject({ ...myLockedShape, x: 100 })

// delete works
editor.run(() => { editor.deleteShapes([myLockedShape]), { ignoreShapeLock: true })
expect(editor.getShape(myShapeId)).toBeUndefined()
```

## History changes

This is a related but not entirely related change in this PR.

Previously, we had a few ways to run code that ignored the history.

- `editor.history.ignore(() => { ... })`
- `editor.batch(() => { ... }, { history: "ignore" })`
- `editor.history.batch(() => { ... }, { history: "ignore" })`
- `editor.updateCurrentPageState(() => { ... }, { history: "ignore" })`

We now have one way to run code that ignores history:

- `editor.run(() => { ... }, { history: "ignore" })`

## Design notes

We want a user to be able to update or delete locked shapes
programmatically.

### Callback vs. method options?

We could have added a `{ force: boolean }` property to the
`updateShapes` / `deleteShapes` methods, however there are places where
those methods are called from other methods (such as
`distributeShapes`). If we wanted to make these work, we would have also
had to provide a `force` option / bag to those methods.

Using a wrapper callback allows for "regular" tldraw editor code to work
while allowing for updates and deletes.

### Interaction logic?

We don't want this change to effect any of our interaction logic.

A lot of our interaction logic depends on identifying which shapes are
locked and which shapes aren't. For example, clicking on a locked shape
will go to the `pointing_canvas` state rather than the `pointing_shape`.
This PR has no effect on that part of the library.

It only effects the updateShapes and deleteShapes methods. As an example
of this, when `_force` is set to true by default, the only tests that
should fail are in `lockedShapes.test.ts`. The "user land" experience of
locked shapes is identical to what it is now.

### Change type

- [x] `bugfix`
- [ ] `improvement`
- [x] `feature`
- [x] `api`
- [ ] `other`

### Test plan

1. Create a shape
2. Lock it
3. From the console, update it
4. From the console, delete it

- [x] Unit tests

### Release notes

- SDK: Adds `Editor.force()` to permit updating / deleting locked shapes
- Fixed a bug that would allow locked shapes to be updated
programmatically
- Fixed a bug that would allow locked group shapes to be ungrouped
programmatically

---------

Co-authored-by: alex <alex@dytry.ch>
2024-07-15 14:10:09 +00:00
..
assets/watermarks sdk: wires up tldraw to have licensing mechanisms (#4021) 2024-07-11 11:49:18 +00:00
src Editor.run, locked shapes improvements (#4042) 2024-07-15 14:10:09 +00:00
api-extractor.json transfer-out: transfer out 2023-04-25 12:01:25 +01:00
api-report.md Editor.run, locked shapes improvements (#4042) 2024-07-15 14:10:09 +00:00
CHANGELOG.md Update CHANGELOG.md [skip ci] 2024-06-25 13:27:57 +00:00
editor.css Flip images (#4113) 2024-07-09 11:01:03 +00:00
LICENSE.md Change licenses to tldraw (#2167) 2023-12-19 10:41:01 +00:00
modules.d.ts sdk: wires up tldraw to have licensing mechanisms (#4021) 2024-07-11 11:49:18 +00:00
package.json Split @tldraw/state into @tldraw/state and @tldraw/state-react (#4170) 2024-07-15 11:18:59 +00:00
README.md Update READMEs, add form link (#3741) 2024-05-12 20:48:07 +00:00
setupTests.js React-powered SVG exports (#3117) 2024-03-25 14:16:55 +00:00
tsconfig.json Split @tldraw/state into @tldraw/state and @tldraw/state-react (#4170) 2024-07-15 11:18:59 +00:00

tldraw/tldraw

Distributions

You can find tldraw on npm here.

Contribution

Please see our contributing guide. Found a bug? Please submit an issue.

License

The tldraw source code and its distributions are provided under the tldraw license. This license does not permit commercial use. To purchase a commercial license or learn more, please fill out this form.

Trademarks

Copyright (c) 2023-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our trademark guidelines for info on acceptable usage.

Contact

Find us on Twitter/X at @tldraw.

Community

Have questions, comments or feedback? Join our discord or start a discussion. For the latest news and release notes, check out our Substack.