@wordpress/block-library
Version:
Block library for the WordPress editor.
142 lines (126 loc) • 3.97 kB
JavaScript
/**
* WordPress dependencies
*/
import { useRegistry, useDispatch, useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { getDefaultBlockName, switchToBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import useOutdentListItem from './use-outdent-list-item';
import { name as listItemName } from '../block.json';
export default function useMerge( clientId ) {
const registry = useRegistry();
const {
getPreviousBlockClientId,
getNextBlockClientId,
getBlockOrder,
getBlockRootClientId,
getBlockName,
getBlock,
} = useSelect( blockEditorStore );
const { mergeBlocks, moveBlocksToPosition, replaceBlock, selectBlock } =
useDispatch( blockEditorStore );
const [ , outdentListItem ] = useOutdentListItem( clientId );
function getTrailingId( id ) {
const order = getBlockOrder( id );
if ( ! order.length ) {
return id;
}
return getTrailingId( order[ order.length - 1 ] );
}
function getParentListItemId( id ) {
const listId = getBlockRootClientId( id );
const parentListItemId = getBlockRootClientId( listId );
if ( ! parentListItemId ) return;
if ( getBlockName( parentListItemId ) !== listItemName ) return;
return parentListItemId;
}
/**
* Return the next list item with respect to the given list item. If none,
* return the next list item of the parent list item if it exists.
*
* @param {string} id A list item client ID.
* @return {string?} The client ID of the next list item.
*/
function _getNextId( id ) {
const next = getNextBlockClientId( id );
if ( next ) return next;
const parentListItemId = getParentListItemId( id );
if ( ! parentListItemId ) return;
return _getNextId( parentListItemId );
}
/**
* Given a client ID, return the client ID of the list item on the next
* line, regardless of indentation level.
*
* @param {string} id The client ID of the current list item.
* @return {string?} The client ID of the next list item.
*/
function getNextId( id ) {
const order = getBlockOrder( id );
// If the list item does not have a nested list, return the next list
// item.
if ( ! order.length ) {
return _getNextId( id );
}
// Get the first list item in the nested list.
return getBlockOrder( order[ 0 ] )[ 0 ];
}
function switchToDefaultBlockType( forward ) {
const rootClientId = getBlockRootClientId( clientId );
const replacement = switchToBlockType(
getBlock( rootClientId ),
getDefaultBlockName()
);
const indexToSelect = forward ? replacement.length - 1 : 0;
const initialPosition = forward ? -1 : 0;
registry.batch( () => {
replaceBlock( rootClientId, replacement );
selectBlock(
replacement[ indexToSelect ].clientId,
initialPosition
);
} );
}
return ( forward ) => {
if ( forward ) {
const nextBlockClientId = getNextId( clientId );
if ( ! nextBlockClientId ) {
switchToDefaultBlockType( forward );
return;
}
if ( getParentListItemId( nextBlockClientId ) ) {
outdentListItem( nextBlockClientId );
} else {
registry.batch( () => {
moveBlocksToPosition(
getBlockOrder( nextBlockClientId ),
nextBlockClientId,
getPreviousBlockClientId( nextBlockClientId )
);
mergeBlocks( clientId, nextBlockClientId );
} );
}
} else {
// Merging is only done from the top level. For lowel levels, the
// list item is outdented instead.
const previousBlockClientId = getPreviousBlockClientId( clientId );
if ( getParentListItemId( clientId ) ) {
outdentListItem( clientId );
} else if ( previousBlockClientId ) {
const trailingId = getTrailingId( previousBlockClientId );
registry.batch( () => {
moveBlocksToPosition(
getBlockOrder( clientId ),
clientId,
previousBlockClientId
);
mergeBlocks( trailingId, clientId );
} );
} else {
switchToDefaultBlockType( forward );
}
}
};
}