This PR replaces our current minimap implementation with one that uses
WebGL
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### 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>
This PR makes a small improvement to the way we measure distances.
(Often we measure distances multiple times per frame per shape on the
screen). In many cases, we compare a minimum distance. This makes those
checks faster by avoiding a square root.
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `improvement` — Improving existing features
### Release Notes
- Improve performance of minimum distance checks.
This PR tweaks the logic of _when_ we update the viewport screen bounds.
Previously, we updated every one second in order to capture any changes
to the viewport's screen position. In this PR, we _check_ every one
second and update the screen bounds if the viewport's screen position
has actually changed. Since we also update the rendering shapes when
this happens, it would cause the rendering / culling shapes to update
while the camera was moving.
I've also removed the "maximum time before we start culling shapes", as
this wasn't very useful and could also cause frames to start dropping
without recovering.
https://github.com/tldraw/tldraw/assets/23072548/9f474481-30c9-49b4-a009-66775ca6a0c1
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Zoom and pan around
2. Culled shapes should only update when you stop moving the camera.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Improve performance of the canvas when many shapes are present.
## Migration path
1. If any of your shapes implement `toSvg` for exports, you'll need to
replace your implementation with a new version that returns JSX (it's a
react component) instead of manually constructing SVG DOM nodes
2. `editor.getSvg` is deprecated. It still works, but will be going away
in a future release. If you still need SVGs as DOM elements rather than
strings, use `new DOMParser().parseFromString(svgString,
'image/svg+xml').firstElementChild`
## The change in detail
At the moment, our SVG exports very carefully try to recreate the
visuals of our shapes by manually constructing SVG DOM nodes. On its own
this is really painful, but it also results in a lot of duplicated logic
between the `component` and `getSvg` methods of shape utils.
In #3020, we looked at using string concatenation & DOMParser to make
this a bit less painful. This works, but requires specifying namespaces
everywhere, is still pretty painful (no syntax highlighting or
formatting), and still results in all that duplicated logic.
I briefly experimented with creating my own version of the javascript
language that let you embed XML like syntax directly. I was going to
call it EXTREME JAVASCRIPT or XJS for short, but then I noticed that we
already wrote the whole of tldraw in this thing called react and a (imo
much worse named) version of the javascript xml thing already existed.
Given the entire library already depends on react, what would it look
like if we just used react directly for these exports? Turns out things
get a lot simpler! Take a look at lmk what you think
This diff was intended as a proof of concept, but is actually pretty
close to being landable. The main thing is that here, I've deliberately
leant into this being a big breaking change to see just how much code we
could delete (turns out: lots). We could if we wanted to make this
without making it a breaking change at all, but it would add back a lot
of complexity on our side and run a fair bit slower
---------
Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
This diff mostly adds an image annotator example, but also has a couple
of drive-by changes:
- Added a 'use-cases' category to the examples app for this style of
mini-app
- Add `editor.pageToViewport`, which is like `editor.pageToScreen` but
works with viewport coordinates (better for `InFrontOfTheCanvas` stuff)
- Prevent the chrome side-swipe-to-go-back thing in the examples app
Some cool features of the image annotator:
- The image cannot be unlocked, and cannot have shapes places behind it
- I still need to work out a way of removing the context menu though
- Anything you place outside the bounds of the image (and therefore
outside the bounds of the export) will be greyed out
- You can't change pages
- unless you find the "move to page" action... need to fix that
- The camera is constrained! It'll keep the image roughly centered on
the screen. If you pick a very long thin image, you can only scroll
vertically. If you pick a very big one, it'll default it to a reasonable
size.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [x] `docs` — Changes to the documentation, examples, or templates.
<!-- ❗ Please select a 'Type' label ❗️ -->
- [x] `feature` — New feature
When we went from overrides-based to component based UI customisation
APIs, we didn't do the toolbar because it had some significant extra
complexity around overflowing the contents of the menu into the
dropdown. This is really hard to do at render-time with react - you
can't introspect what a component will return to move some of it into an
overflow.
Instead, this diff runs that logic in a `useLayoutEffect` - we render
all the items into both the main toolbar and the overflow menu, then in
the effect (or if the rendered components change) we use CSS to remove
the items we don't need, check which was last active, etc. Originally, I
wasn't really into this approach - but i've actually found it to work
super well and be very reliable.
### Change Type
- [x] `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. Test the toolbar at many different sizes with many different 'active
tools'
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
We use `children: any` in a bunch of places, but the proper type for
these is `ReactNode`. This diff fixes those.
### Change Type
- [x] `patch` — Bug fix
This one is a roundup of superficial changes, apologies for having them
in a single PR.
This PR:
- does some chair re-arranging for one of our hotter paths related to
updating shapes
- changes our type exports for editor components
- adds shape indicator to editor components
- moves canvas to be an editor component
- fixes a CSS bug with hinted buttons
- fixes CSS bugs with the menus
- fixes bad imports in examples
### Change Type
- [x] `major`
This PR adds the `TldrawImage` component that displays a tldraw snapshot
as an SVG image.
![2024-02-15 at 12 29 52 - Coral
Cod](https://github.com/tldraw/tldraw/assets/15892272/14140e9e-7d6d-4dd3-88a3-86a6786325c5)
## Why
We've seen requests for this kind of thing from users. eg: GitBook, and
on discord:
<img width="710" alt="image"
src="https://github.com/tldraw/tldraw/assets/15892272/3d3a3e9d-66b9-42e7-81de-a70aa7165bdc">
The component provides a way to do that.
This PR also untangles various bits of editor state from image
exporting, which makes it easier for library users to export images more
agnostically. (ie: they can now export any shapes on any page in any
theme. previously, they had to change the user's state to do that).
## What else
- This PR also adds an **Image snapshot** example to demonstrate the new
component.
- We now pass an `isDarkMode` property to the `toSvg` method (inside the
`ctx` argument). This means that `toSvg` doesn't have to rely on editor
state anymore. I updated all our `toSvg` methods to use it.
- See code comments for more info.
## Any issues?
When you toggle to editing mode in the new example, text measurements
are initially wrong (until you edit the size of a text shape). Click on
the text shape to see how its indicator is wrong. Not sure why this is,
or if it's even related. Does it ring a bell with anyone? If not, I'll
take a closer look. (fixed, see comments --steve)
## Future work
Now that we've untangled image exporting from editor state, we could
expose some more helpful helpers for making this easier.
Fixes tld-2122
### Change Type
- [x] `minor` — New feature
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. Open the **Image snapshot** example.
2. Try editing the image, saving the image, and making sure the image
updates.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Dev: Added the `TldrawImage` component.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR refactors our menu systems and provides an interface to hide or
replace individual user interface elements.
# Background
Previously, we've had two types of overrides:
- "schema" overrides that would allow insertion or replacement of items
in the different menus
- "component" overrides that would replace components in the editor's
user interface
This PR is an attempt to unify the two and to provide for additional
cases where the "schema-based" user interface had begun to break down.
# Approach
This PR makes no attempt to change the `actions` or `tools`
overrides—the current system seems to be correct for those because they
are not reactive. The challenge with the other ui schemas is that they
_are_ reactive, and thus the overrides both need to a) be fed in from
outside of the editor as props, and b) react to changes from the editor,
which is an impossible situation.
The new approach is to use React to declare menu items. (Surprise!)
```tsx
function CustomHelpMenuContent() {
return (
<>
<DefaultHelpMenuContent />
<TldrawUiMenuGroup id="custom stuff">
<TldrawUiMenuItem
id="about"
label="Like my posts"
icon="external-link"
readonlyOk
onSelect={() => {
window.open('https://x.com/tldraw', '_blank')
}}
/>
</TldrawUiMenuGroup>
</>
)
}
const components: TLComponents = {
HelpMenuContent: CustomHelpMenuContent,
}
export default function CustomHelpMenuContentExample() {
return (
<div className="tldraw__editor">
<Tldraw components={components} />
</div>
)
}
```
We use a `components` prop with the combined editor and ui components.
- [ ] Create a "layout" component?
- [ ] Make UI components more isolated? If possible, they shouldn't
depend on styles outside of themselves, so that they can be used in
other layouts. Maybe we wait on this because I'm feeling a slippery
slope toward presumptions about configurability.
- [ ] OTOH maybe we go hard and consider these things as separate
components, even packages, with their own interfaces for customizability
/ configurability, just go all the way with it, and see what that looks
like.
# Pros
Top line: you can customize tldraw's user interface in a MUCH more
granular / powerful way than before.
It solves a case where menu items could not be made stateful from
outside of the editor context, and provides the option to do things in
the menus that we couldn't allow previously with the "schema-based"
approach.
It also may (who knows) be more performant because we can locate the
state inside of the components for individual buttons and groups,
instead of all at the top level above the "schema". Because items /
groups decide their own state, we don't have to have big checks on how
many items are selected, or whether we have a flippable state. Items and
groups themselves are allowed to re-build as part of the regular React
lifecycle. Menus aren't constantly being rebuilt, if that were ever an
issue.
Menu items can be shared between different menu types. We'll are
sometimes able to re-use items between, for example, the menu and the
context menu and the actions menu.
Our overrides no longer mutate anything, so there's less weird searching
and finding.
# Cons
This approach can make customization menu contents significantly more
complex, as an end user would need to re-declare most of a menu in order
to make any change to it. Luckily a user can add things to the top or
bottom of the context menu fairly easily. (And who knows, folks may
actually want to do deep customization, and this allows for it.)
It's more code. We are shipping more react components, basically one for
each menu item / group.
Currently this PR does not export the subcomponents, i.e. menu items. If
we do want to export these, then heaven help us, it's going to be a
_lot_ of exports.
# Progress
- [x] Context menu
- [x] Main menu
- [x] Zoom menu
- [x] Help menu
- [x] Actions menu
- [x] Keyboard shortcuts menu
- [x] Quick actions in main menu? (new)
- [x] Helper buttons? (new)
- [x] Debug Menu
And potentially
- [x] Toolbar
- [x] Style menu
- [ ] Share zone
- [x] Navigation zone
- [ ] Other zones
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. use the context menu
2. use the custom context menu example
3. use cursor chat in the context menu
- [x] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
(pending landing on: "Going to wait to land this one until the Google
SEO 'soft 404' validation finishes. I want to make sure we're testing
separate things.")
- removes Loading text
- adds sitemap to try to get Google to play nice
### Change Type
- [x] `patch` — Bug fix
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR changes the way that viewport bounds are calculated by using the
canvas element as the source of truth, rather than the container. This
allows for cases where the canvas is not the same dimensions as the
component. (Given the way our UI and context works, there are cases
where this is desired, i.e. toolbars and other items overlaid on top of
the canvas area).
The editor's `getContainer` is now only used for the text measurement.
It would be good to get that out somehow.
# Pros
We can inset the canvas
# Cons
We can no longer imperatively call `updateScreenBounds`, as we need to
provide those bounds externally.
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Use the examples, including the new inset canvas example.
- [x] Unit Tests
### Release Notes
- Changes the source of truth for the viewport page bounds to be the
canvas instead.
Taking the opportunity for some last-minute low-consequence breaking
changes before 2.0, this diff does some prep work for adding a new
snapping API by making the distinction between the two types of snapping
a bit clearer and cleaning up some naming.
- `SnapManager` has had most of the actual snapping logic moved into two
properties: `shapeBounds` (for snapping shape bounds on translate and
resize) and `handles` (for snapping handles)
- `SnapLine`s are renamed to `SnapIndicator`s. The 'line' name was a bit
confusing because not all of these indicators are lines (the new vertex
snap type will be a single point)
I'm not too worried about this being a breaking change as it touches an
area of the API that I'd be very surprised if more than a couple of
people were using.
### Change Type
- [x] `major` — Breaking change
### Test Plan
- No user-facing changes.
### Release Notes
- `SnapLine`s are now called `SnapIndicator`s
- Snapping methods moved from `editor.snaps` to
`editor.snaps.shapeBounds` and `editor.snaps.handles` depending on the
type of snapping you're trying to do.
This PR fixes some bugs related to our coordinate systems. These bugs
would appear when the editor was not full screen.
![Kapture 2024-02-04 at 11 53
37](https://github.com/tldraw/tldraw/assets/23072548/9c2199f3-b34d-4fe1-a3e5-d0c65fe5a11e)
In short, we were being inconsistent with whether the
`currentScreenPoint` was relative to the top left corner of the
component or the top left corner of the page that contained the
component.
Here's the actual system:
<img width="898" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/c49b3686-aeb6-4164-a55d-8639d40290a1">
The `viewportPageBounds` describes the bounds of the component within
the browser's space. Its `x` and `y` describe the delta in browser space
between the top left corner of the **page** and the component. This is
not effected by scrolling.
The use's `screenPoint` describes the user's cursor's location relative
to the `viewportPageBounds`. Its `x` and `y` describe the delta in
browser space between the top left corner of the **component** and the
cursor.
While this is a bug fix, I'm marking it as major as apps may be
depending on the previous (broken) behavior.
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Zoom in, out, and pinch on an editor that isn't full screen.
2. Zoom in, out, and pinch on an editor that is full screen.
3. Drag and scroll on an editor that isn't full screen.
4. Drag and scroll on an editor that is full screen.
- [x] Unit Tests
### Release Notes
- Fixed bugs with `getViewportScreenCenter` that could effect zooming
and pinching on editors that aren't full screen
In a previous PR, we improved logic for skipping virtual handles if a
handle's neighbor is too close to it. This PR improves this logic
further by testing against all other vertex handles, not just the
neighbors; and skipping tests against handles that are not vertices
(such as arrow text labels).
This PR also:
- removes some static cursors from `useCursors`
- adds a tiny bit of hover to text labels to differentiate them when
hovered
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Create an arrow with a text label
2. Select the arrow
3. The arrow's middle handle should be selectable and in front of the
text label
### Release Notes
- Fixed a bug with virtual / create handle visibility.
This PR fixes a bug in the wheel event that added the container offset
to the pointer location.
![Kapture 2024-01-26 at 13 54
20](https://github.com/tldraw/tldraw/assets/23072548/20cdc0ed-4a43-4b11-9261-9e6aecfd3292)
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. start dragging
2. use the mousewheel / trackpad to move the camera
### Release Notes
- Fixed a bug with the mouse wheel effecting the pointer location when
the editor was not full screen
This PR fixes a few bugs with the "comma as pointer" feature.
In tldraw, the `,` key can be used as a replacement for "pointer down"
and "pointer up". This is most useful on laptops with trackpads that
make dragging inconvenient. (See
https://github.com/tldraw/tldraw/issues/2550).
Previously, the canvas had to be focused in order for the comma key to
work. If you clicked on a menu item and then pressed comma, it would not
product a pointer event until you first clicked on the canvas. This is
now fixed by moving the listener out of the `useDocumentEvents` and into
`useKeyboardShortcuts`.
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Click the canvas.
2. Use the comma key to control pointer down / up.
3. Click a shape tool on the toolbar.
4. Move your mouse over the canvas.
5. Press the comma key. It should produce a dot / shape / etc
### Release Notes
- Improve comma key as a replacement for pointer down / pointer up.
This essentially reverts the change from #1858 – it seems to be no
longer necessary after we applied the transforms to each overlay item
individually rather than applying a single transform to the outer
container.
This fixes an issue where at certain zoom levels, overlay elements would
disappear when their parent div/svg (that we use for positioning) went
offscreen while their overflowing contents (the stuff you could see) did
not.
todos before merging
- [ ] test on android and ios
- [ ] test on windows
### Change Type
- [x] `patch` — Bug fix
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Release Notes
- removes the internal `useDprMultiple` hook
I standardized the definition of the `useEditor hook` by changing it
from an `arrow function` to a `regular function`, in line with other
editor-related hooks.
### 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
1. Add a step-by-step description of how to test your PR here.
2.
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- Add a brief release note for your PR here.
We probably don't want to add the listener here.
### 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
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
Adds a `LoadingScreen` override option.
Resolves https://github.com/tldraw/tldraw/issues/2269
### 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
- Allow users to customize the connecting screen.
occuring -> occurring
### Change Type
- [ ] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [x] `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.
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
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 PR fixes a bug where a render to the popover component would cause
the menu to never close.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Open the quick actions menu with two shapes selected
2. Click the group button
3. Click on the canvas
The menu should close.
### Release Notes
- Fix actions menu not closing when clicking the canvas after grouping
items via the actions menu.
Follow up to #2149 to make sure it works when tldraw is not mounted at
0,0 in document space. Try it out in the 'multiple' examples
### Change Type
- [x] `patch` — Bug fix
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`.
Seems Safari only added `MediaQueryList: change event` in version 14.
This is a workaround for older versions.
https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/change_event
Reported here
https://discord.com/channels/859816885297741824/926464446694580275/1166028196832092282
### 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
### Release Notes
- Fixes an issue with `addEventListener` on MediaQueryList object in old
versions of Safari.
This PR fixes a bug that causes menus not to close correctly when open.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. On mobile, open the menu.
2. Tap the canvas—it should close the panel.
3. Open the mobile style panel.
4. Tap the canvas—it should close the panel.
5. Open the mobile style panel.
6. Tap the mobile style panel button—it should close the panel.
7. On mobile, long press to open the context menu
### Release Notes
- [fix] bug with menus
Before the geometry change, we'd rely on the browser to tell us which
element was hovered, which meant that when the pointer left the canvas
we'd automatically clear the hovered shape.
Currently, we don't know whether the pointer is over the canvas or not -
so we keep showing the hover indicator for the last shape you had your
pointer over.
This diff adds an `isHoveringCanvas` prop to the instance state (true,
false, or null if the current pointer doesn't support hovering) that we
can use to track this and disable the hover indicator appropriately.
![Kapture 2023-10-05 at 12 00
00](https://github.com/tldraw/tldraw/assets/1489520/236b9459-878b-47e2-bcaa-10d245581347)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Create some shapes that go below the UI
2. Move the mouse from the shape to the UI
3. Hover indicator should disappear
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
There are a lot of cases (eg when interacting with the ui) where the
editor loses focus. This check was preventing us from updating the
viewport screen bounds when appropriate. This function is throttled and
pretty cheap anyway (if the viewport is equal its a no-op) so let's just
remove the condition
### Change Type
- [x] `patch` — Bug fix
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 the escape key from exiting full screen when a shape is
being edited or when shapes are selected. Basically, if the select tool
is going to use the escape key, then don't let it be used for its normal
purpose.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Open a firefox full screen tab.
2. Start editing a text shape.
3. Press escape. It should stop editing.
4. Press escape again. It should deselect the shape.
5. Press escape again. It should exit full screen mode.
This PR improves pinch events on touch screens.
It tries to avoid zooming unless we're sure that the user is zooming (or
at least, that they've started zooming). It removes behavior that snaps
the pinch to a certain zoom (e.g. 100%, 50%).
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. On iPad, try to two-finger pan without zooming.
2. Perform a zoom.
3. Perform a two-finger pan, then a zoom.
### Release Notes
- Improve pinch gesture events.
This PR removes the automatic focus events from the editor.
The `autoFocus` prop is now true by default. When true, the editor will
begin in a focused state (`editor.instanceState.isFocused` will be
`true`) and the component will respond to keyboard shortcuts and other
interactions. When false, the editor will begin in an unfocused state
and not respond to keyboard interactions.
**It's now up to the developer** using the component to update
`isFocused` themselves. There's no predictable way to do that on our
side, so we leave it to the developer to decide when to turn on or off
focus for a container (for example, using an intersection observer to
"unfocus" components that are off screen).
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Open the multiple editors example.
2. Click to focus each editor.
3. Use the keyboard shortcuts to check that the correct editor is
focused.
4. Start editing a shape, then select the other editor. The first
editing shape should complete.
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [editor] Make autofocus default, remove automatic blur / focus events.
---------
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>