Remove focus management (#1953)

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>
This commit is contained in:
Steve Ruiz 2023-10-02 12:29:54 +01:00 committed by GitHub
parent 3fa7dd359d
commit da33179a31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 196 additions and 195 deletions

View file

@ -1,56 +1,15 @@
import { debounce } from '@tldraw/utils'
import { useLayoutEffect } from 'react'
import { useContainer } from './useContainer'
import { useEditor } from './useEditor'
/** @internal */
export function useFocusEvents(autoFocus: boolean) {
const editor = useEditor()
const container = useContainer()
useLayoutEffect(() => {
if (!container) return
// We need to debounce this because when focus changes, the body
// becomes focused for a brief moment. Debouncing means that we
// check only when focus stops changing: when it settles, what
// has it settled on? If it's settled on the container or something
// inside of the container, then focus or preserve the current focus;
// if not, then turn off focus. Turning off focus is a trigger to
// also turn off keyboard shortcuts and other things.
const updateFocus = debounce(() => {
const { activeElement } = document
const { isFocused: wasFocused } = editor.instanceState
const isFocused =
document.hasFocus() && (container === activeElement || container.contains(activeElement))
if (wasFocused !== isFocused) {
editor.updateInstanceState({ isFocused })
editor.updateViewportScreenBounds()
if (!isFocused) {
// When losing focus, run complete() to ensure that any interacts end
editor.complete()
}
}
}, 32)
container.addEventListener('focusin', updateFocus)
container.addEventListener('focus', updateFocus)
container.addEventListener('focusout', updateFocus)
container.addEventListener('blur', updateFocus)
return () => {
container.removeEventListener('focusin', updateFocus)
container.removeEventListener('focus', updateFocus)
container.removeEventListener('focusout', updateFocus)
container.removeEventListener('blur', updateFocus)
}
}, [container, editor])
useLayoutEffect(() => {
if (autoFocus) {
if (autoFocus && !editor.instanceState.isFocused) {
editor.updateInstanceState({ isFocused: true })
editor.getContainer().focus()
} else if (editor.instanceState.isFocused) {
editor.updateInstanceState({ isFocused: false })
}
}, [editor, autoFocus])
}