f745781056
This PR removes the `tool` parameter from the `defineShape` function. It's an opinionated change that I think we should at the very least consider. ## What's the context? Currently, you can add **tools** (aka state nodes) to your state chart in two different ways: 1. Passing them to the `<Tldraw>` component with the `tools` attribute. 2. As part of a shape definition's `tool` property, which you then pass to the `<Tldraw>` component with the `shapes` attribute. This is what (1) looks like: ```jsx import { MyTool } from "./MyTool" function Example() { return <Tldraw tools={[MyTool]} /> } ``` This is what (2) looks like: ```jsx import { MyTool } from "./MyTool" import { MyShapeUtil, myShapeProps } from "./MyShape" const MyShapeDefinition = defineShape("my-shape", { util: MyShapeUtil, props: myShapeProps, tool: MyTool, }) function Example() { return <Tldraw shapes={[MyShapeDefinition]} /> } ``` Clearly, (1) is better for when you want to add *just a tool*, that doesn't have an associated shape. And (2) is better for when you want to add *both* a tool and an associated shape. ## Why change it? I think we should remove method (2). Because I think that it adds a few complications. #### Does it help? I don't think that it helps to streamline the process of coupling shapes and tools. You still need to remember to add your tool. Seeing as `tool` is optional on the shape definition (rightly so), it doesn't prompt you to do it. #### What's easier to explain? I think it's easier to just have to explain _one method_. It would take longer to explain two methods, and it complicates the concepts involved. Seeing as there's not a big benefit to one method over the other, the added explanation wouldn't be a good trade-off. #### What happens if I use both? It's unclear to the user what would happen if they use both methods. Do we know what the intended behaviour of this would be? I think this will happen often. ```jsx import { MyTool } from "./MyTool" import { MyShapeUtil, myShapeProps } from "./MyShape" const MyShapeDefinition = defineShape("my-shape", { util: MyShapeUtil, props: myShapeProps, tool: MyTool, }) function Example() { return <Tldraw tools={[MyTool]} shapes={[MyShapeDefinition]} /> } ``` #### Does it fit my shape/tool? Many shapes are coupled closely with one tool. But some shapes would involve multiple tools. And some tools would involve multiple shapes. For example, you might first add a tool and a shape that go nicely together, so you use method (2). But two months later, you decide that you want another tool to be able to make this shape too. Now you've inserted your related tools in two different places, unless you refactor. Alternatively, you might want to add some more functionality to your tool, so that it can make multiple types of shapes. Instead of refactoring the existing shape, you want to create an entirely new shape, to keep your new code separate. Should you add the `tool` property to the new shape as well? What would happen if you did/didn't? What happens if you later disable the original shape? Would you need to move the `tool` property from there to the newer shape? It would be a lot simpler to just have the tool in your list of tools, instead of having them tangled up with shapes. #### Plugins? We've been considering moving towards some sort of 'plugins' system in the future, that could collect together shapes, tools, and other stuff. I think that a more complete concept of a 'plugin' would be the best place to collect together shapes, and tools — not on the shape itself. ### Change Type - [x] `major` — Breaking change [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Try using all of the app's tools, making sure they still work. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - [dev] Removed the `tool` property from `defineShape` |
||
---|---|---|
.. | ||
examples | ||
vscode |