diff --git a/packages/slate-react/src/components/content.js b/packages/slate-react/src/components/content.js index 676e5db5d..1ab26638b 100644 --- a/packages/slate-react/src/components/content.js +++ b/packages/slate-react/src/components/content.js @@ -228,9 +228,16 @@ class Content extends React.Component { this.tmp.key++ } - // If the `onSelect` handler fires while the `isUpdatingSelection` flag is - // set it's a result of updating the selection manually, so skip it. - if (handler == 'onSelect' && this.tmp.isUpdatingSelection) { + // Ignore `onBlur`, `onFocus` and `onSelect` events generated + // programmatically while updating selection. + if ( + this.tmp.isUpdatingSelection && + ( + handler == 'onSelect' || + handler == 'onBlur' || + handler == 'onFocus' + ) + ) { return } diff --git a/packages/slate-react/src/components/void.js b/packages/slate-react/src/components/void.js index 69700a1f1..c135cd1c5 100644 --- a/packages/slate-react/src/components/void.js +++ b/packages/slate-react/src/components/void.js @@ -65,17 +65,31 @@ class Void extends React.Component { const Tag = node.kind == 'block' ? 'div' : 'span' const style = { height: '0', - color: 'transparent' + color: 'transparent', + outline: 'none' } + const spacer = ( + + {this.renderText()} + + ) this.debug('render', { props }) return ( - - {!readOnly && - {this.renderText()} - } - + + {readOnly ? null : spacer } + {children} diff --git a/packages/slate-react/src/plugins/before.js b/packages/slate-react/src/plugins/before.js index ab3157bd1..0dd00ddd9 100644 --- a/packages/slate-react/src/plugins/before.js +++ b/packages/slate-react/src/plugins/before.js @@ -5,6 +5,7 @@ import { findDOMNode } from 'react-dom' import HOTKEYS from '../constants/hotkeys' import { IS_FIREFOX, SUPPORTED_EVENTS } from '../constants/environment' +import findNode from '../utils/find-node' /** * Debug. @@ -60,12 +61,31 @@ function BeforePlugin() { if (isCopying) return true if (editor.props.readOnly) return true - // If the active element is still the editor, the blur event is due to the - // window itself being blurred (eg. when changing tabs) so we should ignore - // the event, since we want to maintain focus when returning. + const { state } = change + const focusTarget = event.relatedTarget + + // If focusTarget is null, the blur event is due to the window itself being + // blurred (eg. when changing tabs) so we should ignore the event, since we + // want to maintain focus when returning. + if (!focusTarget) return true + const el = findDOMNode(editor) - const window = getWindow(el) - if (window.document.activeElement == el) return true + + // The event should also 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 + + // 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, state).isVoid + ) { + return true + } debug('onBlur', { event }) } diff --git a/packages/slate-react/test/rendering/fixtures/custom-block-void.js b/packages/slate-react/test/rendering/fixtures/custom-block-void.js index f1a8bedf5..f84ea775d 100644 --- a/packages/slate-react/test/rendering/fixtures/custom-block-void.js +++ b/packages/slate-react/test/rendering/fixtures/custom-block-void.js @@ -23,13 +23,13 @@ export const state = ( export const output = `
-
-
+
+
-
+
diff --git a/packages/slate-react/test/rendering/fixtures/custom-inline-void.js b/packages/slate-react/test/rendering/fixtures/custom-inline-void.js index b7da3c6f4..c8f09bafc 100644 --- a/packages/slate-react/test/rendering/fixtures/custom-inline-void.js +++ b/packages/slate-react/test/rendering/fixtures/custom-inline-void.js @@ -31,13 +31,13 @@ export const output = ` - - + + - +