From ce2b06af144aa818bde360294d9b93c2284ca6bd Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Mon, 30 Oct 2017 11:48:13 -0700 Subject: [PATCH] fix onBlur logic to prevent unfocus on window unfocus --- packages/slate-react/src/plugins/before.js | 51 ++++++++++++++-------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/packages/slate-react/src/plugins/before.js b/packages/slate-react/src/plugins/before.js index 991ccf037..272fbe1cf 100644 --- a/packages/slate-react/src/plugins/before.js +++ b/packages/slate-react/src/plugins/before.js @@ -27,6 +27,7 @@ const debug = Debug('slate:before') */ function BeforePlugin() { + let activeElement = null let compositionCount = 0 let isComposing = false let isCopying = false @@ -69,28 +70,34 @@ function BeforePlugin() { if (editor.props.readOnly) return true const { value } = change - const focusTarget = event.relatedTarget + const { relatedTarget, target } = event + const window = getWindow(target) - // focusTarget may be null when window itself being blurred - // (eg. when changing tabs), or when user clicks on elements - // without`tabindex` attribute. - if (focusTarget) { + // COMPAT: If the current `activeElement` is still the previous one, this is + // due to the window being blurred when the tab itself becomes unfocused, so + // we want to abort early to allow to editor to stay focused when the tab + // becomes focused again. + if (activeElement == window.document.activeElement) return true + + // COMPAT: The `relatedTarget` can be null when the new focus target is not + // a "focusable" element (eg. a `
` without `tabindex` set). + if (relatedTarget) { const el = findDOMNode(editor) - // The event should be ignored if the focus returns to the editor from an - // embedded editable element (eg. an input element inside a void node). - if (focusTarget == el) return true - // when the focus moved from the editor to a void node spacer... - if (focusTarget.hasAttribute('data-slate-spacer')) return true + // COMPAT: The event should be ignored if the focus is returning to the + // editor from an embedded editable element (eg. an element inside + // a void node). + if (relatedTarget == el) return true - // or to an editable element inside the editor but not into a void node - // (eg. a list item of the check list example). - if ( - el.contains(focusTarget) && - !findNode(focusTarget, value).isVoid - ) { - return true - } + // COMPAT: The event should be ignored if the focus is moving from the + // editor to inside a void node's spacer element. + if (relatedTarget.hasAttribute('data-slate-spacer')) return true + + // COMPAT: The event should be ignored if the focus is moving to a non- + // editable section of an element that isn't a void node (eg. a list item + // of the check list example). + const node = findNode(relatedTarget, value) + if (el.contains(relatedTarget) && node && !node.isVoid) return true } debug('onBlur', { event }) @@ -323,6 +330,10 @@ function BeforePlugin() { const el = findDOMNode(editor) + // Save the new `activeElement`. + const window = getWindow(event.target) + activeElement = window.document.activeElement + // COMPAT: If the editor has nested editable elements, the focus can go to // those elements. In Firefox, this must be prevented because it results in // issues with keyboard navigation. (2017/03/30) @@ -407,6 +418,10 @@ function BeforePlugin() { if (isComposing) return true if (editor.props.readOnly) return true + // Save the new `activeElement`. + const window = getWindow(event.target) + activeElement = window.document.activeElement + debug('onSelect', { event }) }