Skip to content

Commit

Permalink
Block List: Use default Inserter for sibling insertion
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Oct 24, 2018
1 parent b022ad2 commit 9729b96
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 110 deletions.
32 changes: 15 additions & 17 deletions packages/editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,12 +371,12 @@ export class BlockListBlock extends Component {
isSelected,
isPartOfMultiSelection,
isFirstMultiSelected,
isLastMultiSelected,
isTypingWithinBlock,
isMultiSelecting,
hoverArea,
isEmptyDefaultBlock,
isMovable,
isPreviousBlockADefaultEmptyBlock,
isParentOfSelectedBlock,
isDraggable,
} = this.props;
Expand All @@ -403,11 +403,10 @@ export class BlockListBlock extends Component {
const shouldShowMobileToolbar = shouldAppearSelected;
const { error, dragging } = this.state;

// Insertion point can only be made visible when the side inserter is
// not present, and either the block is at the extent of a selection or
// is the first block in the top-level list rendering.
const shouldShowInsertionPoint = ( isPartOfMultiSelection && isFirst ) || ! isPartOfMultiSelection;
const canShowInBetweenInserter = ! isEmptyDefaultBlock && ! isPreviousBlockADefaultEmptyBlock;
// Insertion point can only be made visible if the block is at the
// the extent of a multi-selection, or not in a multi-selection.
const shouldShowInsertionPoint = ( isPartOfMultiSelection && isLastMultiSelected ) || ! isPartOfMultiSelection;
const canShowInBetweenInserter = ! isEmptyDefaultBlock;

// Generate the wrapper class names handling the different states of the block.
const wrapperClassName = classnames( 'editor-block-list__block', {
Expand Down Expand Up @@ -486,15 +485,6 @@ export class BlockListBlock extends Component {
] }
{ ...wrapperProps }
>
{ shouldShowInsertionPoint && (
<BlockInsertionPoint
clientId={ clientId }
rootClientId={ rootClientId }
layout={ layout }
canShowInserter={ canShowInBetweenInserter }
onInsert={ this.hideHoverEffects }
/>
) }
<BlockDropZone
index={ order }
clientId={ clientId }
Expand Down Expand Up @@ -570,6 +560,14 @@ export class BlockListBlock extends Component {
</div>
</Fragment>
) }
{ shouldShowInsertionPoint && (
<BlockInsertionPoint
clientId={ clientId }
rootClientId={ rootClientId }
layout={ layout }
canShowInserter={ canShowInBetweenInserter }
/>
) }
</IgnoreNestedEvents>
);
/* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */
Expand All @@ -585,6 +583,7 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId, isLargeV
isAncestorMultiSelected,
isBlockMultiSelected,
isFirstMultiSelectedBlock,
isLastMultiSelectedBlock,
isMultiSelecting,
isTyping,
getBlockIndex,
Expand All @@ -600,14 +599,14 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId, isLargeV
const { hasFixedToolbar, focusMode } = getEditorSettings();
const block = getBlock( clientId );
const previousBlockClientId = getPreviousBlockClientId( clientId );
const previousBlock = getBlock( previousBlockClientId );
const templateLock = getTemplateLock( rootClientId );
const isParentOfSelectedBlock = hasSelectedInnerBlock( clientId, true );

return {
nextBlockClientId: getNextBlockClientId( clientId ),
isPartOfMultiSelection: isBlockMultiSelected( clientId ) || isAncestorMultiSelected( clientId ),
isFirstMultiSelected: isFirstMultiSelectedBlock( clientId ),
isLastMultiSelected: isLastMultiSelectedBlock( clientId ),
isMultiSelecting: isMultiSelecting(),
// We only care about this prop when the block is selected
// Thus to avoid unnecessary rerenders we avoid updating the prop if the block is not selected.
Expand All @@ -618,7 +617,6 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId, isLargeV
isSelectionEnabled: isSelectionEnabled(),
initialPosition: getSelectedBlocksInitialCaretPosition(),
isEmptyDefaultBlock: block && isUnmodifiedDefaultBlock( block ),
isPreviousBlockADefaultEmptyBlock: previousBlock && isUnmodifiedDefaultBlock( previousBlock ),
isMovable: 'all' !== templateLock,
isLocked: !! templateLock,
isFocusMode: focusMode && isLargeViewport,
Expand Down
109 changes: 38 additions & 71 deletions packages/editor/src/components/block-list/insertion-point.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
import { Component } from '@wordpress/element';
import { IconButton } from '@wordpress/components';
import { withSelect, withDispatch } from '@wordpress/data';
import { ifCondition, compose } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import Inserter from '../inserter';

class BlockInsertionPoint extends Component {
constructor() {
Expand All @@ -22,14 +24,9 @@ class BlockInsertionPoint extends Component {

this.onBlurInserter = this.onBlurInserter.bind( this );
this.onFocusInserter = this.onFocusInserter.bind( this );
this.onClick = this.onClick.bind( this );
}

onFocusInserter( event ) {
// We stop propagation of the focus event to avoid selecting the current block
// While we're trying to insert a new block
event.stopPropagation();

onFocusInserter() {
this.setState( {
isInserterFocused: true,
} );
Expand All @@ -41,77 +38,47 @@ class BlockInsertionPoint extends Component {
} );
}

onClick() {
const { layout, rootClientId, index, ...props } = this.props;
props.insertDefaultBlock( { layout }, rootClientId, index );
props.startTyping();
this.onBlurInserter();
if ( props.onInsert ) {
this.props.onInsert();
}
}

render() {
const { isInserterFocused } = this.state;
const { showInsertionPoint, showInserter } = this.props;
const { showInsertionPoint, canShowInserter } = this.props;

return (
<div className="editor-block-list__insertion-point">
{ showInsertionPoint && <div className="editor-block-list__insertion-point-indicator" /> }
{ showInserter && (
<div className={ classnames( 'editor-block-list__insertion-point-inserter', { 'is-visible': isInserterFocused } ) }>
<IconButton
icon="insert"
className="editor-block-list__insertion-point-button"
onClick={ this.onClick }
label={ __( 'Insert block' ) }
onFocus={ this.onFocusInserter }
onBlur={ this.onBlurInserter }
/>
{ canShowInserter && (
<div
onFocus={ this.onFocusInserter }
onBlur={ this.onBlurInserter }
className={
classnames( 'editor-block-list__insertion-point-inserter', {
'is-visible': isInserterFocused,
} )
}
>
<Inserter />
</div>
) }
</div>
);
}
}
export default compose(
withSelect( ( select, { clientId, rootClientId, canShowInserter } ) => {
const {
canInsertBlockType,
getBlockIndex,
getBlockInsertionPoint,
getBlock,
isBlockInsertionPointVisible,
isTyping,
} = select( 'core/editor' );
const {
getDefaultBlockName,
} = select( 'core/blocks' );
const blockIndex = clientId ? getBlockIndex( clientId, rootClientId ) : -1;
const insertIndex = blockIndex;
const insertionPoint = getBlockInsertionPoint();
const block = clientId ? getBlock( clientId ) : null;
const showInsertionPoint = (
isBlockInsertionPointVisible() &&
insertionPoint.index === insertIndex &&
insertionPoint.rootClientId === rootClientId &&
( ! block || ! isUnmodifiedDefaultBlock( block ) )
);
export default withSelect( ( select, { clientId, rootClientId } ) => {
const {
getBlockIndex,
getBlockInsertionPoint,
getBlock,
isBlockInsertionPointVisible,
} = select( 'core/editor' );
const blockIndex = getBlockIndex( clientId, rootClientId );
const insertIndex = blockIndex + 1;
const insertionPoint = getBlockInsertionPoint();
const block = getBlock( clientId );
const showInsertionPoint = (
isBlockInsertionPointVisible() &&
insertionPoint.index === insertIndex &&
insertionPoint.rootClientId === rootClientId &&
! isUnmodifiedDefaultBlock( block )
);

const defaultBlockName = getDefaultBlockName();
return {
canInsertDefaultBlock: canInsertBlockType( defaultBlockName, rootClientId ),
showInserter: ! isTyping() && canShowInserter,
index: insertIndex,
showInsertionPoint,
};
} ),
ifCondition( ( { canInsertDefaultBlock } ) => canInsertDefaultBlock ),
withDispatch( ( dispatch ) => {
const { insertDefaultBlock, startTyping } = dispatch( 'core/editor' );
return {
insertDefaultBlock,
startTyping,
};
} )
)( BlockInsertionPoint );
return { showInsertionPoint };
} )( BlockInsertionPoint );
22 changes: 3 additions & 19 deletions packages/editor/src/components/block-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@
.editor-block-list__insertion-point {
position: relative;
z-index: z-index(".editor-block-list__insertion-point");
margin-top: -$block-padding;
margin-bottom: -$block-padding;
}

.editor-block-list__insertion-point-indicator {
Expand All @@ -640,7 +640,7 @@
justify-content: center;

// Show a clickable plus.
.editor-block-list__insertion-point-button {
.editor-inserter__toggle {
margin-top: -4px;
border-radius: 50%;
color: $blue-medium-focus;
Expand All @@ -663,30 +663,14 @@
}
}

// Don't show the sibling inserter before the selected block.
.edit-post-layout:not(.has-fixed-toolbar) {
// The child selector is necessary for this to work properly in nested contexts.
.is-selected > .editor-block-list__insertion-point > .editor-block-list__insertion-point-inserter {
opacity: 0;
pointer-events: none;

&:hover,
&.is-visible {
opacity: 1;
pointer-events: auto;
}
}
}

// This is the edge-to-edge hover area that contains the plus.
.editor-block-list__block {
> .editor-block-list__insertion-point {
position: absolute;
top: -$block-padding - $block-spacing / 2;
bottom: -$block-padding - $block-spacing / 2;

// Matches the whole empty space between two blocks.
height: $block-padding * 2;
bottom: auto;

// Go edge to edge on mobile.
left: 0;
Expand Down
20 changes: 17 additions & 3 deletions packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1014,15 +1014,29 @@ export function getLastMultiSelectedBlockClientId( state ) {
* specified client ID is the first block of the multi-selection set, or false
* otherwise.
*
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
*
* @return {boolean} Whether block is first in mult-selection.
* @return {boolean} Whether block is first in multi-selection.
*/
export function isFirstMultiSelectedBlock( state, clientId ) {
return getFirstMultiSelectedBlockClientId( state ) === clientId;
}

/**
* Returns true if a multi-selection exists, and the block corresponding to the
* specified client ID is the last block of the multi-selection set, or false
* otherwise.
*
* @param {Object} state Editor state.
* @param {string} clientId Block client ID.
*
* @return {boolean} Whether block is last in mult-selection.
*/
export function isLastMultiSelectedBlock( state, clientId ) {
return getLastMultiSelectedBlockClientId( state ) === clientId;
}

/**
* Returns true if the client ID occurs within the block multi-selection, or
* false otherwise.
Expand Down

0 comments on commit 9729b96

Please sign in to comment.