Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show inserter next to empty text block #565

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions blocks/editable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default class Editable extends wp.element.Component {
onSetup( editor ) {
this.editor = editor;
editor.on( 'init', this.onInit );
editor.on( 'focusout', this.onChange );
editor.on( 'change', this.onChange );
editor.on( 'NewBlock', this.onNewBlock );
editor.on( 'focusin', this.onFocus );
editor.on( 'nodechange', this.onNodeChange );
Expand Down Expand Up @@ -130,8 +130,13 @@ export default class Editable extends wp.element.Component {
}

this.savedContent = this.getContent();
this.editor.save();
this.props.onChange( this.savedContent );

// Save contents to the element, but avoid events since by default the
// save function will incur another `change` event
this.editor.save( {
no_events: true,
} );
}

getRelativePosition( node ) {
Expand Down
4 changes: 0 additions & 4 deletions blocks/library/text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ registerBlock( 'core/text', {
content: children(),
},

defaultAttributes: {
content: <p />,
},

merge( attributes, attributesToMerge ) {
return {
content: wp.element.concatChildren( attributes.content, attributesToMerge.content ),
Expand Down
13 changes: 11 additions & 2 deletions editor/inserter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import clickOutside from 'react-click-outside';
import { connect } from 'react-redux';
import classnames from 'classnames';

/**
* WordPress dependencies
Expand Down Expand Up @@ -65,10 +66,11 @@ class Inserter extends wp.element.Component {

render() {
const { opened } = this.state;
const { position } = this.props;
const { position, className } = this.props;
const classes = classnames( 'editor-inserter', className );

return (
<div className="editor-inserter">
<div className={ classes }>
<IconButton
icon="insert"
label={ wp.i18n.__( 'Insert block' ) }
Expand Down Expand Up @@ -96,5 +98,12 @@ export default connect(
block: wp.blocks.createBlock( slug ),
} );
},
} ),
// In `mergeProps`, we flip the order to enable a rendering component to
// override the default `onInsertBlock` behavior
( stateProps, dispatchProps, ownProps ) => ( {
...stateProps,
...dispatchProps,
...ownProps,
} )
)( clickOutside( Inserter ) );
23 changes: 21 additions & 2 deletions editor/modes/visual-editor/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@ import { partial } from 'lodash';
/**
* WordPress dependencies
*/
import { createBlock } from 'blocks';
import Toolbar from 'components/toolbar';

/**
* Internal dependencies
*/
import BlockMover from '../../block-mover';
import BlockSwitcher from '../../block-switcher';
import Inserter from '../../inserter';
import {
getPreviousBlock,
getBlock,
getBlockFocus,
getBlockOrder,
isNewBlock,
isBlockHovered,
isBlockSelected,
isTypingInBlock,
Expand All @@ -36,6 +39,7 @@ class VisualEditorBlock extends wp.element.Component {
this.maybeStartTyping = this.maybeStartTyping.bind( this );
this.removeOnBackspace = this.removeOnBackspace.bind( this );
this.mergeWithPrevious = this.mergeWithPrevious.bind( this );
this.replaceNewBlock = this.replaceNewBlock.bind( this );
this.previousOffset = null;
}

Expand Down Expand Up @@ -146,6 +150,13 @@ class VisualEditorBlock extends wp.element.Component {
);
}

replaceNewBlock( slug ) {
// When choosing block from inserter for a new empty block, override
// insert behavior to replace current block instead
const { uid, replaceBlocks } = this.props;
replaceBlocks( [ uid ], [ createBlock( slug ) ] );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the newly created block borrow the uid from the replaced block to stay selected?

}

componentDidUpdate( prevProps ) {
if ( this.previousOffset ) {
window.scrollTo(
Expand Down Expand Up @@ -180,7 +191,7 @@ class VisualEditorBlock extends wp.element.Component {
return null;
}

const { isHovered, isSelected, isTyping, focus } = this.props;
const { isHovered, isSelected, isNew, isTyping, focus } = this.props;
const className = classnames( 'editor-visual-editor__block', {
'is-selected': isSelected && ! isTyping,
'is-hovered': isHovered,
Expand Down Expand Up @@ -212,7 +223,14 @@ class VisualEditorBlock extends wp.element.Component {
tabIndex="0"
{ ...wrapperProps }
>
{ ( ( isSelected && ! isTyping ) || isHovered ) && <BlockMover uid={ block.uid } /> }
{ isNew && isSelected && (
<Inserter
onInsertBlock={ this.replaceNewBlock }
className="editor-visual-editor__empty-block-inserter" />
) }
{ ! isNew && ( ( isSelected && ! isTyping ) || isHovered ) && (
<BlockMover uid={ block.uid } />
) }
{ isSelected && ! isTyping &&
<div className="editor-visual-editor__block-controls">
<BlockSwitcher uid={ block.uid } />
Expand Down Expand Up @@ -260,6 +278,7 @@ export default connect(
block: getBlock( state, ownProps.uid ),
isSelected: isBlockSelected( state, ownProps.uid ),
isHovered: isBlockHovered( state, ownProps.uid ),
isNew: isNewBlock( state, ownProps.uid ),
focus: getBlockFocus( state, ownProps.uid ),
isTyping: isTypingInBlock( state, ownProps.uid ),
order: getBlockOrder( state, ownProps.uid ),
Expand Down
4 changes: 3 additions & 1 deletion editor/modes/visual-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ function VisualEditor( { blocks } ) {
{ blocks.map( ( uid ) => (
<VisualEditorBlock key={ uid } uid={ uid } />
) ) }
<Inserter position="top right" />
<Inserter
position="top right"
className="editor-visual-editor__inserter" />
</div>
);
}
Expand Down
15 changes: 13 additions & 2 deletions editor/modes/visual-editor/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
}

/* "Hassle-free full bleed" from CSS Tricks */
.editor-visual-editor > *:not( [data-align="wide"] ) {
.editor-post-title,
.editor-visual-editor__block:not( [data-align="wide"] ) {
max-width: $visual-editor-max-width;
margin-left: auto;
margin-right: auto;
Expand Down Expand Up @@ -107,6 +108,16 @@
display: inline-flex;
}

.editor-visual-editor .editor-inserter {
.editor-visual-editor__empty-block-inserter {
position: absolute;
top: 10px;
left: -10px;

&:not( :hover ) {
opacity: 0.8;
}
}

.editor-visual-editor__inserter {
margin: $item-spacing $item-spacing $item-spacing calc( 50% - #{ $visual-editor-max-width / 2 } ); // account for full-width trick
}
26 changes: 26 additions & 0 deletions editor/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ export function getBlockOrder( state, uid ) {
return state.editor.blockOrder.indexOf( uid );
}

/**
* Returns true if the block is empty, null if the block is not known, or null
* otherwise.
*
* @param {Object} state Current application state
* @param {string} uid Block UID
* @return {?Boolean} Whether block is empty, or null if unknown
*/
export function isNewBlock( state, uid ) {
const block = getBlock( state, uid );
if ( ! block ) {
return null;
}

// A block is considered new if it's a text block without content. Usually
// we'd avoid engrained knowledge of specific block types, but the behavior
// of text as the default new inserted block is a special case. Regardless,
// that we abstract this behind a generic selector enables us to refactor
// in the future to one with fewer specific implementation details.

return (
'core/text' === block.blockType &&
! block.attributes.content
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this it will work but I prefer your idea here to compute the isNew flag:

#798 (comment)


export function isFirstBlock( state, uid ) {
return first( state.editor.blockOrder ) === uid;
}
Expand Down
47 changes: 47 additions & 0 deletions editor/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getBlocks,
getBlockUids,
getBlockOrder,
isNewBlock,
isFirstBlock,
isLastBlock,
getPreviousBlock,
Expand Down Expand Up @@ -267,6 +268,52 @@ describe( 'selectors', () => {
} );
} );

describe( 'isNewBlock()', () => {
it( 'returns null if unknown', () => {
const state = {
editor: {
blocksByUid: {},
},
};

expect( isNewBlock( state, 23 ) ).to.be.null();
} );

it( 'returns true if new', () => {
const state = {
editor: {
blocksByUid: {
23: {
blockType: 'core/text',
attributes: {
content: undefined,
},
},
},
},
};

expect( isNewBlock( state, 23 ) ).to.be.true();
} );

it( 'returns false if not new', () => {
const state = {
editor: {
blocksByUid: {
23: {
blockType: 'core/text',
attributes: {
content: {},
},
},
},
},
};

expect( isNewBlock( state, 23 ) ).to.be.false();
} );
} );

describe( 'isFirstBlock', () => {
it( 'should return true when the block is first', () => {
const state = {
Expand Down