[fix] iframe losing focus on pointer down (#1848)

This PR fixes a bug that would cause an interactive iframe (e.g. a
youtube video) to lose its editing state once clicked.

### Change Type

- [x] `patch` — Bug fix

### Test Plan

1. Create an interactive iframe.
2. Begin editing.
3. Click inside of the iframe
This commit is contained in:
Steve Ruiz 2023-09-09 10:41:06 +01:00 committed by GitHub
parent 48a1bb4d88
commit 20704ea417
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 66 deletions

View file

@ -0,0 +1,56 @@
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) {
editor.getContainer().focus()
}
}, [editor, autoFocus])
}