Skip to content

Commit

Permalink
InnerBlocks: Support insert before/after the block actions (#59162)
Browse files Browse the repository at this point in the history
* Don't call selectors during the render; subscribe to the store instead
* Get blocks on demand
* Update 'insertBeforeBlock' and 'insertAfterBlock' actions
* A handler 'attributesToCopy' definitions

Unlinked contributors: mapk, gwwar.

Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: noisysocks <noisysocks@git.wordpress.org>
Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: shaunandrews <shaunandrews@git.wordpress.org>
Co-authored-by: richtabor <richtabor@git.wordpress.org>
  • Loading branch information
7 people authored Feb 22, 2024
1 parent 255ed23 commit efe7d2e
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 72 deletions.
4 changes: 2 additions & 2 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1263,15 +1263,15 @@ Action that hides the insertion point.

### insertAfterBlock

Action that inserts an empty block after a given block.
Action that inserts a default block after a given block.

_Parameters_

- _clientId_ `string`:

### insertBeforeBlock

Action that inserts an empty block before a given block.
Action that inserts a default block before a given block.

_Parameters_

Expand Down
104 changes: 57 additions & 47 deletions packages/block-editor/src/components/block-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,55 @@ export default function BlockActions( {
children,
__experimentalUpdateSelection: updateSelection,
} ) {
const {
canInsertBlockType,
getBlockRootClientId,
getBlocksByClientId,
canMoveBlocks,
canRemoveBlocks,
} = useSelect( blockEditorStore );
const { getDefaultBlockName, getGroupingBlockName } =
useSelect( blocksStore );

const blocks = getBlocksByClientId( clientIds );
const rootClientId = getBlockRootClientId( clientIds[ 0 ] );

const canCopyStyles = blocks.every( ( block ) => {
return (
!! block &&
( hasBlockSupport( block.name, 'color' ) ||
hasBlockSupport( block.name, 'typography' ) )
);
} );

const canDuplicate = blocks.every( ( block ) => {
return (
!! block &&
hasBlockSupport( block.name, 'multiple', true ) &&
canInsertBlockType( block.name, rootClientId )
);
} );

const canInsertDefaultBlock = canInsertBlockType(
getDefaultBlockName(),
rootClientId
const selected = useSelect(
( select ) => {
const {
canInsertBlockType,
getBlockRootClientId,
getBlocksByClientId,
getDirectInsertBlock,
canMoveBlocks,
canRemoveBlocks,
} = select( blockEditorStore );

const blocks = getBlocksByClientId( clientIds );
const rootClientId = getBlockRootClientId( clientIds[ 0 ] );
const canInsertDefaultBlock = canInsertBlockType(
getDefaultBlockName(),
rootClientId
);
const directInsertBlock = rootClientId
? getDirectInsertBlock( rootClientId )
: null;

return {
canMove: canMoveBlocks( clientIds, rootClientId ),
canRemove: canRemoveBlocks( clientIds, rootClientId ),
canInsertBlock: canInsertDefaultBlock || !! directInsertBlock,
canCopyStyles: blocks.every( ( block ) => {
return (
!! block &&
( hasBlockSupport( block.name, 'color' ) ||
hasBlockSupport( block.name, 'typography' ) )
);
} ),
canDuplicate: blocks.every( ( block ) => {
return (
!! block &&
hasBlockSupport( block.name, 'multiple', true ) &&
canInsertBlockType( block.name, rootClientId )
);
} ),
};
},
[ clientIds, getDefaultBlockName ]
);
const { getBlocksByClientId, getBlocks } = useSelect( blockEditorStore );

const canMove = canMoveBlocks( clientIds, rootClientId );
const canRemove = canRemoveBlocks( clientIds, rootClientId );
const { canMove, canRemove, canInsertBlock, canCopyStyles, canDuplicate } =
selected;

const {
removeBlocks,
Expand All @@ -75,11 +88,9 @@ export default function BlockActions( {
return children( {
canCopyStyles,
canDuplicate,
canInsertDefaultBlock,
canInsertBlock,
canMove,
canRemove,
rootClientId,
blocks,
onDuplicate() {
return duplicateBlocks( clientIds, updateSelection );
},
Expand All @@ -104,44 +115,43 @@ export default function BlockActions( {
setBlockMovingClientId( clientIds[ 0 ] );
},
onGroup() {
if ( ! blocks.length ) {
if ( ! clientIds.length ) {
return;
}

const groupingBlockName = getGroupingBlockName();

// Activate the `transform` on `core/group` which does the conversion.
const newBlocks = switchToBlockType( blocks, groupingBlockName );
const newBlocks = switchToBlockType(
getBlocksByClientId( clientIds ),
groupingBlockName
);

if ( ! newBlocks ) {
return;
}
replaceBlocks( clientIds, newBlocks );
},
onUngroup() {
if ( ! blocks.length ) {
if ( ! clientIds.length ) {
return;
}

const innerBlocks = blocks[ 0 ].innerBlocks;

const innerBlocks = getBlocks( clientIds[ 0 ] );
if ( ! innerBlocks.length ) {
return;
}

replaceBlocks( clientIds, innerBlocks );
},
onCopy() {
const selectedBlockClientIds = blocks.map(
( { clientId } ) => clientId
);
if ( blocks.length === 1 ) {
flashBlock( selectedBlockClientIds[ 0 ] );
if ( clientIds.length === 1 ) {
flashBlock( clientIds[ 0 ] );
}
notifyCopy( 'copy', selectedBlockClientIds );
notifyCopy( 'copy', clientIds );
},
async onPasteStyles() {
await pasteStyles( blocks );
await pasteStyles( getBlocksByClientId( clientIds ) );
},
} );
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ const POPOVER_PROPS = {
placement: 'bottom-start',
};

function CopyMenuItem( { blocks, onCopy, label } ) {
const ref = useCopyToClipboard( () => serialize( blocks ), onCopy );
function CopyMenuItem( { clientIds, onCopy, label } ) {
const { getBlocksByClientId } = useSelect( blockEditorStore );
const ref = useCopyToClipboard(
() => serialize( getBlocksByClientId( clientIds ) ),
onCopy
);
const copyMenuItemLabel = label ? label : __( 'Copy' );
return <MenuItem ref={ ref }>{ copyMenuItemLabel }</MenuItem>;
}
Expand Down Expand Up @@ -198,7 +202,7 @@ export function BlockSettingsDropdown( {
{ ( {
canCopyStyles,
canDuplicate,
canInsertDefaultBlock,
canInsertBlock,
canMove,
canRemove,
onDuplicate,
Expand All @@ -208,7 +212,6 @@ export function BlockSettingsDropdown( {
onCopy,
onPasteStyles,
onMoveTo,
blocks,
} ) => (
<DropdownMenu
icon={ moreVertical }
Expand Down Expand Up @@ -245,7 +248,7 @@ export function BlockSettingsDropdown( {
'core/block-editor/insert-after',
event
) &&
canInsertDefaultBlock
canInsertBlock
) {
event.preventDefault();
setOpenedBlockSettingsMenu( undefined );
Expand All @@ -255,7 +258,7 @@ export function BlockSettingsDropdown( {
'core/block-editor/insert-before',
event
) &&
canInsertDefaultBlock
canInsertBlock
) {
event.preventDefault();
setOpenedBlockSettingsMenu( undefined );
Expand Down Expand Up @@ -286,7 +289,7 @@ export function BlockSettingsDropdown( {
/>
) }
<CopyMenuItem
blocks={ blocks }
clientIds={ clientIds }
onCopy={ onCopy }
/>
{ canDuplicate && (
Expand All @@ -301,7 +304,7 @@ export function BlockSettingsDropdown( {
{ __( 'Duplicate' ) }
</MenuItem>
) }
{ canInsertDefaultBlock && (
{ canInsertBlock && (
<>
<MenuItem
onClick={ pipe(
Expand All @@ -327,7 +330,7 @@ export function BlockSettingsDropdown( {
{ canCopyStyles && (
<MenuGroup>
<CopyMenuItem
blocks={ blocks }
clientIds={ clientIds }
onCopy={ onCopy }
label={ __( 'Copy styles' ) }
/>
Expand Down
68 changes: 54 additions & 14 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1539,7 +1539,7 @@ export const duplicateBlocks =
};

/**
* Action that inserts an empty block before a given block.
* Action that inserts a default block before a given block.
*
* @param {string} clientId
*/
Expand All @@ -1555,16 +1555,34 @@ export const insertBeforeBlock =
return;
}

const firstSelectedIndex = select.getBlockIndex( clientId );
return dispatch.insertDefaultBlock(
{},
rootClientId,
firstSelectedIndex
);
const blockIndex = select.getBlockIndex( clientId );
const directInsertBlock = rootClientId
? select.getDirectInsertBlock( rootClientId )
: null;

if ( ! directInsertBlock ) {
return dispatch.insertDefaultBlock( {}, rootClientId, blockIndex );
}

const copiedAttributes = {};
if ( directInsertBlock.attributesToCopy ) {
const attributes = select.getBlockAttributes( clientId );
directInsertBlock.attributesToCopy.forEach( ( key ) => {
if ( attributes[ key ] ) {
copiedAttributes[ key ] = attributes[ key ];
}
} );
}

const block = createBlock( directInsertBlock.name, {
...directInsertBlock.attributes,
...copiedAttributes,
} );
return dispatch.insertBlock( block, blockIndex, rootClientId );
};

/**
* Action that inserts an empty block after a given block.
* Action that inserts a default block after a given block.
*
* @param {string} clientId
*/
Expand All @@ -1580,12 +1598,34 @@ export const insertAfterBlock =
return;
}

const firstSelectedIndex = select.getBlockIndex( clientId );
return dispatch.insertDefaultBlock(
{},
rootClientId,
firstSelectedIndex + 1
);
const blockIndex = select.getBlockIndex( clientId );
const directInsertBlock = rootClientId
? select.getDirectInsertBlock( rootClientId )
: null;

if ( ! directInsertBlock ) {
return dispatch.insertDefaultBlock(
{},
rootClientId,
blockIndex + 1
);
}

const copiedAttributes = {};
if ( directInsertBlock.attributesToCopy ) {
const attributes = select.getBlockAttributes( clientId );
directInsertBlock.attributesToCopy.forEach( ( key ) => {
if ( attributes[ key ] ) {
copiedAttributes[ key ] = attributes[ key ];
}
} );
}

const block = createBlock( directInsertBlock.name, {
...directInsertBlock.attributes,
...copiedAttributes,
} );
return dispatch.insertBlock( block, blockIndex + 1, rootClientId );
};

/**
Expand Down

0 comments on commit efe7d2e

Please sign in to comment.