From f12e179e24e441fabffea5982467409c4464de03 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 12 May 2017 08:46:21 +0100 Subject: [PATCH] Blocks: Handle forward backspace --- blocks/editable/index.js | 29 +++++++++++++++++++++++-- blocks/library/heading/index.js | 4 ++-- blocks/library/quote/index.js | 4 ++-- blocks/library/text/index.js | 4 ++-- editor/modes/visual-editor/block.js | 21 ++++++++++++------ editor/selectors.js | 5 +++++ editor/test/selectors.js | 33 +++++++++++++++++++++++++++++ 7 files changed, 86 insertions(+), 14 deletions(-) diff --git a/blocks/editable/index.js b/blocks/editable/index.js index ec0ae2fb5c2b68..9c0ac6afc12d41 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -20,6 +20,7 @@ import FormatToolbar from './format-toolbar'; import TinyMCE from './tinymce'; const KEYCODE_BACKSPACE = 8; +const KEYCODE_DELETE = 46; const alignmentMap = { alignleft: 'left', @@ -177,10 +178,34 @@ export default class Editable extends wp.element.Component { return true; } + isEndOfEditor() { + const range = this.editor.selection.getRng(); + if ( range.endOffset !== range.endContainer.textContent.length || ! range.collapsed ) { + return false; + } + const start = range.endContainer; + const body = this.editor.getBody(); + let element = start; + while ( element !== body ) { + const child = element; + element = element.parentNode; + if ( element.lastChild !== child ) { + return false; + } + } + return true; + } + onKeyDown( event ) { - if ( this.props.onMerge && event.keyCode === KEYCODE_BACKSPACE && this.isStartOfEditor() ) { + if ( + this.props.onMerge && ( + ( event.keyCode === KEYCODE_BACKSPACE && this.isStartOfEditor() ) || + ( event.keyCode === KEYCODE_DELETE && this.isEndOfEditor() ) + ) + ) { + const forward = event.keyCode === KEYCODE_DELETE; this.onChange(); - this.props.onMerge( this.editor.getContent() ); + this.props.onMerge( forward ); event.preventDefault(); event.stopImmediatePropagation(); } diff --git a/blocks/library/heading/index.js b/blocks/library/heading/index.js index a8181d868ab43b..6f5ae5cfd1f2dd 100644 --- a/blocks/library/heading/index.js +++ b/blocks/library/heading/index.js @@ -79,7 +79,7 @@ registerBlock( 'core/heading', { }; }, - edit( { attributes, setAttributes, focus, setFocus, mergeWithPrevious } ) { + edit( { attributes, setAttributes, focus, setFocus, mergeBlocks } ) { const { content, nodeName = 'H2' } = attributes; return ( @@ -89,7 +89,7 @@ registerBlock( 'core/heading', { focus={ focus } onFocus={ setFocus } onChange={ ( value ) => setAttributes( { content: value } ) } - onMerge={ mergeWithPrevious } + onMerge={ mergeBlocks } inline /> ); diff --git a/blocks/library/quote/index.js b/blocks/library/quote/index.js index 5105caa0164c45..f8f84d70ec5133 100644 --- a/blocks/library/quote/index.js +++ b/blocks/library/quote/index.js @@ -82,7 +82,7 @@ registerBlock( 'core/quote', { ], }, - edit( { attributes, setAttributes, focus, setFocus, mergeWithPrevious } ) { + edit( { attributes, setAttributes, focus, setFocus, mergeBlocks } ) { const { value, citation, style = 1 } = attributes; const focusedEditable = focus ? focus.editable || 'value' : null; @@ -97,7 +97,7 @@ registerBlock( 'core/quote', { } focus={ focusedEditable === 'value' ? focus : null } onFocus={ () => setFocus( { editable: 'value' } ) } - onMerge={ mergeWithPrevious } + onMerge={ mergeBlocks } showAlignments /> { ( ( citation && citation.length > 0 ) || !! focus ) && ( diff --git a/blocks/library/text/index.js b/blocks/library/text/index.js index 870d260cefe7eb..af23cb5df122f1 100644 --- a/blocks/library/text/index.js +++ b/blocks/library/text/index.js @@ -27,7 +27,7 @@ registerBlock( 'core/text', { }; }, - edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, mergeWithPrevious } ) { + edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, mergeBlocks } ) { const { content } = attributes; return ( @@ -46,7 +46,7 @@ registerBlock( 'core/text', { content: after, } ) ); } } - onMerge={ mergeWithPrevious } + onMerge={ mergeBlocks } showAlignments /> ); diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js index b5d4eb2f863969..0decf7a12973ea 100644 --- a/editor/modes/visual-editor/block.js +++ b/editor/modes/visual-editor/block.js @@ -19,6 +19,7 @@ import BlockSwitcher from '../../block-switcher'; import { focusBlock, mergeBlocks } from '../../actions'; import { getPreviousBlock, + getNextBlock, getBlock, getBlockFocus, getBlockOrder, @@ -36,7 +37,7 @@ class VisualEditorBlock extends wp.element.Component { this.maybeHover = this.maybeHover.bind( this ); this.maybeStartTyping = this.maybeStartTyping.bind( this ); this.removeOnBackspace = this.removeOnBackspace.bind( this ); - this.mergeWithPrevious = this.mergeWithPrevious.bind( this ); + this.mergeBlocks = this.mergeBlocks.bind( this ); this.previousOffset = null; } @@ -101,15 +102,22 @@ class VisualEditorBlock extends wp.element.Component { } } - mergeWithPrevious() { - const { block, previousBlock, onMerge } = this.props; + mergeBlocks( forward = false ) { + const { block, previousBlock, nextBlock, onMerge } = this.props; // Do nothing when it's the first block - if ( ! previousBlock ) { + if ( + ( ! forward && ! previousBlock ) || + ( forward && ! nextBlock ) + ) { return; } - onMerge( previousBlock, block ); + if ( forward ) { + onMerge( block, nextBlock ); + } else { + onMerge( previousBlock, block ); + } } componentDidUpdate( prevProps ) { @@ -200,7 +208,7 @@ class VisualEditorBlock extends wp.element.Component { setAttributes={ this.setAttributes } insertBlockAfter={ onInsertAfter } setFocus={ partial( onFocus, block.uid ) } - mergeWithPrevious={ this.mergeWithPrevious } + mergeBlocks={ this.mergeBlocks } /> @@ -213,6 +221,7 @@ export default connect( ( state, ownProps ) => { return { previousBlock: getPreviousBlock( state, ownProps.uid ), + nextBlock: getNextBlock( state, ownProps.uid ), block: getBlock( state, ownProps.uid ), isSelected: isBlockSelected( state, ownProps.uid ), isHovered: isBlockHovered( state, ownProps.uid ), diff --git a/editor/selectors.js b/editor/selectors.js index 7932d5b4f08b6a..537e180a01bd9d 100644 --- a/editor/selectors.js +++ b/editor/selectors.js @@ -68,6 +68,11 @@ export function getPreviousBlock( state, uid ) { return state.editor.blocksByUid[ state.editor.blockOrder[ order - 1 ] ] || null; } +export function getNextBlock( state, uid ) { + const order = getBlockOrder( state, uid ); + return state.editor.blocksByUid[ state.editor.blockOrder[ order + 1 ] ] || null; +} + export function isBlockSelected( state, uid ) { return state.selectedBlock.uid === uid; } diff --git a/editor/test/selectors.js b/editor/test/selectors.js index fe31e4108e1b63..6a133921858591 100644 --- a/editor/test/selectors.js +++ b/editor/test/selectors.js @@ -22,6 +22,7 @@ import { isFirstBlock, isLastBlock, getPreviousBlock, + getNextBlock, isBlockSelected, isBlockHovered, getBlockFocus, @@ -322,6 +323,38 @@ describe( 'selectors', () => { } ); } ); + describe( 'getNextBlock', () => { + it( 'should return the following block', () => { + const state = { + editor: { + blocksByUid: { + 23: { uid: 23, blockType: 'core/heading' }, + 123: { uid: 123, blockType: 'core/text' }, + }, + blockOrder: [ 123, 23 ], + }, + }; + + expect( getNextBlock( state, 123 ) ).to.eql( + { uid: 23, blockType: 'core/heading' }, + ); + } ); + + it( 'should return null for the last block', () => { + const state = { + editor: { + blocksByUid: { + 23: { uid: 23, blockType: 'core/heading' }, + 123: { uid: 123, blockType: 'core/text' }, + }, + blockOrder: [ 123, 23 ], + }, + }; + + expect( getNextBlock( state, 23 ) ).to.be.null(); + } ); + } ); + describe( 'isBlockSelected', () => { it( 'should return true if the block is selected', () => { const state = {