[fix] Focus events (actually) (#2015)

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 commit is contained in:
Steve Ruiz 2023-10-04 10:01:48 +01:00 committed by GitHub
parent 6b19d70a9e
commit d715fa3a2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 236 additions and 122 deletions

View file

@ -7,25 +7,26 @@ export function useFocusEvents(autoFocus: boolean) {
const editor = useEditor()
const container = useContainer()
useLayoutEffect(() => {
function handleFocus() {
if (autoFocus) {
// When autoFocus is true, update the editor state to be focused
// unless it's already focused
if (!editor.instanceState.isFocused) {
editor.updateInstanceState({ isFocused: true })
}
}
container.addEventListener('focus', handleFocus)
container.addEventListener('pointerdown', handleFocus)
if (autoFocus && !editor.instanceState.isFocused) {
editor.updateInstanceState({ isFocused: true })
container.focus()
} else if (editor.instanceState.isFocused) {
editor.updateInstanceState({ isFocused: false })
}
return () => {
container.removeEventListener('focus', handleFocus)
container.removeEventListener('pointerdown', handleFocus)
// Note: Focus is also handled by the side effect manager in tldraw.
// Importantly, if a user manually sets isFocused to true (or if it
// changes for any reason from false to true), the side effect manager
// in tldraw will also take care of the focus. However, it may be that
// on first mount the editor already has isFocused: true in the model,
// so we also need to focus it here just to be sure.
editor.getContainer().focus()
} else {
// When autoFocus is false, update the editor state to be not focused
// unless it's already not focused
if (editor.instanceState.isFocused) {
editor.updateInstanceState({ isFocused: false })
}
}
}, [editor, container, autoFocus])
}