Adds Fit to content option for frames. This resizes the frames so that
the whole content fits. It also adds 50px padding on all sides so that
the content does not touch the frame's borders.
https://github.com/tldraw/tldraw/assets/2523721/b2f86e31-7dfb-495f-ac31-f1e0125e0af1https://github.com/tldraw/tldraw/assets/2523721/e0a73d25-ac9f-4a35-a1fd-4aed7a5b151cFixes#1407
### 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. Add some shapes.
2. Add a frame that encloses those shapes.
3. Right click on the frame and choose `Fit to content`
4. The frame should resize to fit all the children with some padding on
all sides of the frame.
- [x] Unit Tests
- [ ] End to end tests
### Release Notes
- Add Fit to content option to the context menu for frames. This resizes
the frames to correctly fit all their content.
---------
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This is an attempt at #1989. The big issue there is when `shapeUtils`
change when you're relying on tldraw to provide you with the store
instead of providing your own. Our `useTLStore` component had a bug
where it would rely on effects & a ref to detect when its options had
changed whilst still scheduling updates. Fresh opts would come in, but
they'd be different from the ones in the ref, so we'd schedule an
update, so the opts would come in again, but they'd still be different
as we hadn't run effects yet, and we'd schedule an update again (and so
on).
This diff fixes that by storing the previous opts in state instead of a
ref, so they're updating in lockstep with the store itself. this
prevents the update loop.
There are still situations where we can get into loops if the developer
is passing in custom tools, shapeUtils, or components but not memoising
them or defining them outside of react. As a DX improvement, we do some
auto-memoisation of these values using shallow equality to help with
this issue.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
- [x] Unit Tests
### Release Notes
- Fixes a bug that could cause crashes due to a re-render loop with HMR
#1989
- Add simple frame removing - it just drops the frame and parent
children to frames parent.
- Select children after removing the frame.
- Add children to the frame if we resize the frame so that it encloses
them.
Describe what your pull request does. If appropriate, add GIFs or images
showing the before and after.
### 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. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
follow up to #2189
adds runtime warnings for deprecated fields. cleans up remaining fields
and usages. Adds a lint rule to prevent access to deprecated fields.
Adds a lint rule to prevent using getters.
### Change Type
- [x] `patch` — Bug fix
This PR adds a custom tool example, the `Screenshot Tool`.
It demonstrates how a user can create a custom tool together with custom
tool UI.
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Use the screenshot example
### Release Notes
- adds ScreenshotTool custom tool example
- improvements and new exports related to copying and exporting images /
files
- loosens up types around icons and translations
- moving `StateNode.isActive` into an atom
- adding `Editor.path`
This PR extracts some improvements from #2198 into a separate PR.
### Release Notes
- adds computed `StateNode.getPath`
- adds computed StateNode.getCurrent`
- adds computed StateNode.getIsActive`
- adds computed `Editor.getPath()`
- makes transition's second property optional
### Change Type
- [x] `minor` — New feature
### Test Plan
- [x] Unit Tests
- [x] End to end tests
Follow up to #2189 and #2202
### Change Type
- [x] `patch` — Bug fix
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
This PR replaces the `.value` getter for the atom with `.get()`
### Change Type
- [x] `major` — Breaking change
---------
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
This improves how zooming works when we zoom in an inactive window. With
this change you should zoom towards the pointer position, while before
it zoomed towards the last known pointer position before the window
became inactive.
Fixes#2165
Before
https://github.com/tldraw/tldraw/assets/2523721/50018782-533a-43bb-88a5-21fc4419b723
After
https://github.com/tldraw/tldraw/assets/2523721/c3859f84-ef56-4db8-96b9-50a2de060507
### Change Type
- [x] `patch` — Bug fix
- [ ] `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. Open the tldraw editor.
2. Click away from the browser window so that it's not longer active.
3. Hover over the browser window and start zooming.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Improves zooming for inactive windows.
This PR adds two new component overrides to the editor's `components`
slot. They are:
- `<OnTheCanvas/>`, which renders inside of the html layer that scales
and translates with the camera
- `<InFrontOfTheCanvas/>`, which renders in front of the canvas but
behind any UI elements, and which does not scale / pan with the camera.
![Kapture 2023-11-06 at 12 19
15](https://github.com/tldraw/tldraw/assets/23072548/51c0421d-8b39-48b5-9b8a-c717253c3423)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. See the "on the canvas" example.
### Release Notes
- [editor] Adds two new components, `OnTheCanvas` and
`InFrontOfTheCanvas`.
This PR adds support for multiple scribbles at the same time. It
prevents the sudden disappearance of existing scribbles when new ones
are added. It simplifies the management of scribbles by moving the
scribble manager to the editor.
![Kapture 2023-10-29 at 10 17
48](https://github.com/tldraw/tldraw/assets/23072548/23089047-6247-4714-bb79-c4972370140f)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Use the eraser, scribble select, and laser pointer tools
- [x] Unit Tests
### Release Notes
- [feature] multi scribbles
This PR passes the initial shape to the onHandleChange method. It makes
it a bit easier to work with handles in custom shapes.
### Change Type
- [ ] `patch` — Bug fix
- [ ] `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]
- [x] `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
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
This PR removes some calls to `findLast`, as this is not in all browsers
yet.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Drag and drop.
2. Create a shape inside of a frame
- [x] Unit Tests
This PR:
1. Adds a `renderingOnly` option to the `Editor.getShapeAtPoint` method.
When true, the method will only hit test against rendering shapes
(shapes that are inside of the current `renderingBounds`) rather than
all shapes on the canvas.
2. Includes some low level improvements to the way that edges find their
nearest point.
3. Includes a fix to circle geometry that could produce NaN values
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Check whether hovering shapes still works as you would expect.
- [x] Unit Tests
### Release Notes
- Improve perf for hovering shapes / shape hit tests
This PR:
- removes feature flags for people menu, highlighter shape
- removes debugging for cursors
- adds a debug flag for hiding shapes
- changes Canvas to use `useValue` rather than `track`
- removes the default background color on `tl-background`
- in the editor components, makes `Background` null by default
### Change Type
- [x] `minor` — New feature
This PR restores the controlled nature of focus. Focus allows keyboard
shortcuts and other interactions to occur. The editor's focus should
always / entirely be controlled via the autoFocus prop or by manually
setting `editor.instanceState.isFocused`.
Design note: I'm starting to think that focus is the wrong abstraction,
and that we should instead use a kind of "disabled" state for editors
that the user isn't interacting with directly. In a page where multiple
editors exit (e.g. a notion page), a developer could switch from
disabled to enabled using a first interaction.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
- [x] End to end tests
This PR prevents certain shapes from being edited while in readonly
mode. It adds `ShapeUtil.canEditInReadOnly` to allow developers to opt
in to editing shapes. It's currently applied only to embed shapes.
### Change Type
- [x] `major`
### Test Plan
1. In a readonly mode, try to edit text / sticky notes / arrow labels
via double click / enter. You should not be able to edit them.
2. Try to edit an embed. You should be able to edit it.
### Release Notes
- Prevent editing text shapes in readonly mode.
This PR fixes some cases where, if a member function (like `onEnter`)
was not an arrow function, it would not run.
### Change Type
- [x] `patch` — Bug fix
This PR makes it so that user preferences can be in a 'null' state,
where we use the default values and/or infer from the system
preferences.
Before this PR it was impossible to allow a user to change their locale
via their system config rather than selecting an explicit value in the
tldraw editor menu. Similarly, it was impossible to adapt to changes in
the user's system preferences for dark/light mode.
That's because we saved the full user preference values the first time
the user loaded tldraw, and the only way for them to change after that
is by saving new values.
After this PR, if a value is `null` we will use the 'default' version of
it, which can be inferred based on the user's system preferences in the
case of dark mode, locale, and animation speed. Then if the user changes
their system config and refreshes the page their changes should be
picked up by tldraw where they previously wouldn't have been.
Dark mode inference is opt-in by setting a prop `inferDarkMode: true` on
the `Editor` instance (and the `<Tldraw />` components), because we
don't want it to be a surprise for existing library users.
### Change Type
- [ ] `patch` — Bug fix
- [ ] `minor` — New feature
- [x] `major` — Breaking change
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
This PR fixes the text label placement for geo shapes. (It also fixes
the way an ellipse renders when set to dash or dotted).
There's still the slightest offset of the text label's outline when you
begin editing. Maybe we should keep the indicator instead?
### Change Type
- [x] `patch` — Bug fix
### Test Plan
Create a hexagon shape
hit enter to type
indicator is offset, text label is no longer offset
---------
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
Currently, the highlighter shape uses a single 0-width line for its
geometry, same as the draw tool. For the draw tool this works ok - the
visual line is thin enough that unless you zoom right in, it's hard to
find areas where the hover should trigger but isn't. As the highlighter
tool is much thicker though, it's relatively easy to find those areas.
The fix is for the geometry to represent the line including its thick
stroke, instead of at 0-width. There are two possible approaches here:
1. Update the polyline geometry to allow passing a stroke width.
2. Instead of a polyline, make the highlighter shape be a polygon that
traces _around_ the stroke
1 is the more accurate approach, but is hard to fit into our geometry
system. Our geometry is based around two primitives: `getVertices` which
returns an array of points around the shape, and `nearestPoint` which
returns the nearest point on the geometry to a vector we pass in. We can
account for a stroke in `nearestPoint` pretty easily, including it in
`getVertices` is hard - we'd have to expand the vertices and handle line
join/caps etc. Just making the change in `nearestPoint` does fix the
issue here, but i'm not sure about the knock-on effect elsewhere and
don't really want to introduce 1-off hacks into the core geometry
system.
2 actually means addressing the same hard problem around outlining
strokes as 1, but it lets us do it in a more tightly-scoped one-off
change just to the highlighter shape, instead of trying to come up with
a generic solution for the whole geometry system. This is the approach
I've taken in this diff. We outline the stroke using perfect-freehand,
which works pretty well but produces inaccurate results at edge-cases,
particularly when a line rapidly changes direction:
![Kapture 2023-09-19 at 13 45
01](https://github.com/tldraw/tldraw/assets/1489520/1593ac5c-e7db-4360-b97d-ba66cdfb5498)
I think that given this is scoped to just the highlighter shape and is
imo an improvement over the stroke issue from before, it's a reasonable
solution for now. If we want to in the future we could implement real
non-freehand-based outlining.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Create a highlight shape
2. Zoom in
3. Make sure you can interact with the shape at its edges instead of
right in the center
This PR adds the source items from a paste event to the data shared with
external content handlers. This allows developers to customize the way
certain content is handled.
For example, pasting text sometimes incudes additional clipboard items,
such as the HTML representation of that text. We wouldn't want to create
two shapes—one for the text and one for the HTML—so we still treat this
as a single text paste. The `registerExternalContentHandler` API allows
a developer to change how that text is handled, and the new `sources`
API will now allow the developer to take into consideration all of the
items that were on the clipboard.
![Kapture 2023-09-19 at 12 25
52](https://github.com/tldraw/tldraw/assets/23072548/fa976320-cfec-4921-b481-10cae0d4043e)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Try the external content source example.
2. Paste text that includes HTML (e.g. from VS Code)
### Release Notes
- [editor / tldraw] add `sources` to `TLExternalContent`
This PR:
- adds `canSnap` as a property to handle and ignores snapping when
dragging a handle that does not have `canSnap` set to true. Arrows no
longer snap.
- adds `isLabel` to Geometry2d
- fixes selection on empty text labels
- fixes vertices / snapping for empty text labels
### Change Type
- [x] `minor` — New feature
### Test Plan
- [x] Unit Tests
Add `Store.migrateSnapshot`, another surface API alongside getSnapshot
and loadSnapshot.
### Change Type
- [x] `minor` — New feature
### Release Notes
- [editor] add `Store.migrateSnapshot`
This PR:
- adds a `snapshot` prop to the <Tldraw> component. It does basically
the same thing as calling `loadSnapshot` after creating the store, but
happens before the editor actually loads.
- adds a largeish example (including a JSON snapshot) to the examples
We have some very complex ways of juggling serialized data between
multiplayer, file formats, and the snapshot APIs. I'd like to see these
simplified, or at least for our documentation to reflect a narrow subset
of all the options available.
The most common questions seem to be:
Q: How do I serialize data?
A: Via the `Editor.getSnapshot()` method
Q: How do I restore serialized data?
A: Via the `Editor.loadSnapshot()` method OR via the `<Tldraw>`
component's `snapshot` prop
The store has an `initialData` constructor prop, however this is quite
complex as the store also requires a schema class instance with which to
migrate the data. In our components (<Tldraw> and <TldrawEditor>) we
were also accepting `initialData`, however we weren't accepting a
schema, and either way I think it's unrealistic to also expect users to
create schemas themselves and pass those in.
AFAIK the `initialData` prop is only used in the file loading, which is
a good example of how complex it looks like to create a schema and
migrate data outside of the components.
### Change Type
- [x] `minor` — New feature
This PR fixes zero width or height on Geometry2d bounds. It adds the
`zeroFix` helper to the `Box2d` class.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Create a straight line
2. Create a straight arrow that binds to the straight line
- [x] Unit Tests
### Release Notes
- Fix bug with straight lines / arrows
This PR fixes some creative use of CSS in setting the radius property of
various SVGs. While this use is supported in all browsers, it was
confusing CSS processors. Moving these out of CSS and into JavaScript
seems to be a pretty minor trade. Closes
https://github.com/tldraw/tldraw/issues/1775.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Ensure that borders and handles adjust their radii correctly when
zoomed in or out.
This PR updates the way that styles are changed. It splits `setStyle`
and `setOpacity` into `setStyleForNext Shape` and
`setOpacityForNextShape` and `setStyleForSelectedShapes` and
`setOpacityForSelectedShapes`. It fixes the issue with setting one style
re-setting other styles.
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Set styles when shapes are not selected.
2. Set styles when shapes are selected.
3. Set styles when shapes are selected and the selected tool is not
select.
- [x] Unit Tests
This PR includes further UX improvements to selection.
- clicking inside of a hollow shape will no longer select it on pointer
up
- clicking a shape's filled label will select it on pointer down
- clicking a shape's empty label will select it on pointer up
- clicking and dragging a selected arrow is now better limited to its
body, not its bounds
- arrows will no longer bind to labels
### Text labels
A big change here relates to text labels. Previously, we had listeners
set on the text label elements; I've removed these and we now check the
actual label bounds geometry for a hit. For geo shapes, this geometry is
now placed correctly based on the alignment / vertical alignment of the
label.
- Clicking on a label with text in it will select the shape on pointer
down.
- Clicking on an empty text label will select the shape on pointer up.
## Hollow shapes
Previously, shapes with `fill: none` were also being selected on pointer
up. I've removed that logic because it was producing wrong-feeling
selections too often. We now select these shapes only when clicking on
the label (as mentioned above) or when clicking on the edges of the
shape. This is in line with the original behavior (currently on
tldraw.com, prior to the earlier PR that updated selection logic).
## Arrows
Arrows still hit the inside of hollow shapes, using the "smallest
hovered" logic previously used for pointer-up selection on hollow
shapes. They also now correctly do so while ignoring text labels.
### Change Type
- [x] `minor` — New feature
### Test Plan
1. try selecting geo shapes, nested geo shapes, arrows and shapes with
labels or without labels
- [x] Unit Tests
This PR cleans up some APIs around the editor's current page state:
- `setEditingShapeId` -> `setEditingShape`
- `setHoveredShapeId` -> `setHoveredShape`
- `setCroppingShapeId` -> `setCroppingShape`
- `setFocusedGroupId` -> `setFocusedGroup`
- `setErasingShapeIds` -> `setErasingShapes`
- `setHintingShapeIds` -> `setHintingShapes`
It also adds some additional computed getters, e.g.
`Editor.croppingShape`.
It also adds some errors around `setCroppingShape`.
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
This PR:
- improves the logic for computing `renderingShapes`
- improves the handling of side effects related to cropping
We might use the same side effect logic to edit / re-edit shapes, though
this may be more complicated with inputs that steal focus.
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Crop an image
2. Change the crop
3. Stop cropping
4. Undo — you should be cropping again!
5. Undo until you're not cropping anymore
6. Redo until you're cropping again
7. etc.
- [x] Unit Tests
This PR fixes our page to screen conversion.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Drop an image onto the screen while the camera is panned and zoomed.
- [x] Unit Tests
This PR:
- adds history options to several commands in order to allow them to
support squashing and ephemeral data (previously, these commands had
boolean values for squashing / ephemeral)
It also:
- changes `markId` to return the editor instance rather than the mark id
passed into the command
- removes `focus` and `blur` commands
- changes `createPage` parameters
- unifies `animateShape` / `animateShapes` options
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
This PR:
- supports client configuration of the rendering bounds via
`Editor.renderingBoundsMargin`
- no longer culls selected shapes
- restores rendering shape tests accidentally removed in #1786
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Select shapes, scroll quickly to see if they get culled
- [x] Unit Tests
### Release Notes
- [editor] add `Editor.renderingBoundsMargin`
This PR updates camera APIs:
- removes animateCamera
- adds animation support to setCamera
- makes camera commands accept points rather than an x/y
- `centerOnPoint`
- `pageToScreen`
- `screenToPoint`
- `pan`
- `setCamera`
- makes `zoomToBounds` accept a `Box2d` rather than x/y/w/h
- removes the `getBoundingClientRects` call from `getPointerInfo`
- removes the resize observer from `useScreenBounds`, uses an interval
instead when focused
A big (unexpected) improvement here is that `getBoundingClientRects` was
being called on every pointer move. This is a relatively expensive call
(it forces reflow) which could impact interactions. It's now called at
most once per second, and we could probably improve on that too if we
needed by only updating while in the select state.
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Try the multiple editors example after scrolling / resizing
2. Use the camera commands (zoom in, etc)
- [x] Unit Tests
### Release Notes
- (editor) improve camera commands