Skip to content

Commit

Permalink
List block v2: Fix impossible to outdent multiple list items. (#41713)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta authored Jun 23, 2022
1 parent 1e8f02a commit a82f695
Showing 1 changed file with 49 additions and 21 deletions.
70 changes: 49 additions & 21 deletions packages/block-library/src/list-item/hooks/use-outdent-list-item.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/**
* External dependencies
*/
import { first, last } from 'lodash';

/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { cloneBlock } from '@wordpress/blocks';

/**
* Internal dependencies
Expand All @@ -23,19 +29,28 @@ export default function useOutdentListItem( clientId ) {
},
[ clientId ]
);
const { replaceBlocks, selectionChange } = useDispatch( blockEditorStore );
const { replaceBlocks, selectionChange, multiSelect } = useDispatch(
blockEditorStore
);
const {
getBlockRootClientId,
getBlockAttributes,
getBlock,
getBlockIndex,
getSelectionStart,
getSelectionEnd,
hasMultiSelection,
getMultiSelectedBlockClientIds,
} = useSelect( blockEditorStore );

return [
canOutdent,
useCallback( () => {
const _hasMultiSelection = hasMultiSelection();
const clientIds = _hasMultiSelection
? getMultiSelectedBlockClientIds()
: [ clientId ];

const selectionStart = getSelectionStart();
const selectionEnd = getSelectionEnd();

Expand All @@ -45,50 +60,63 @@ export default function useOutdentListItem( clientId ) {
const listItemParentAttributes =
getBlockAttributes( listItemParentId );

const index = getBlockIndex( clientId );
const firstIndex = getBlockIndex( first( clientIds ) );
const lastIndex = getBlockIndex( last( clientIds ) );
const siblingBlocks = getBlock( listParentId ).innerBlocks;
const previousSiblings = siblingBlocks.slice( 0, index );
const afterSiblings = siblingBlocks.slice( index + 1 );
const previousSiblings = siblingBlocks.slice( 0, firstIndex );
const afterSiblings = siblingBlocks.slice( lastIndex + 1 );

// Create a new parent list item block with just the siblings
// that existed before the child item being outdent.
// that existed before the first child item being outdent.
const newListItemParent = createListItem(
listItemParentAttributes,
listAttributes,
previousSiblings
);

const block = getBlock( clientId );
const childList = block.innerBlocks[ 0 ];
const lastBlock = getBlock( last( clientIds ) );
const childList = lastBlock.innerBlocks[ 0 ];
const childItems = childList?.innerBlocks || [];
const hasChildItems = !! childItems.length;

const newBlocksExcludingLast = clientIds
.slice( 0, -1 )
.map( ( _clientId ) => cloneBlock( getBlock( _clientId ) ) );

// Create a new list item block whose attributes are equal to the
// block being outdent and whose children are the children that it had (if any)
// last block being outdent and whose children are the children that it had (if any)
// followed by the siblings that existed after it.
const newItem = createListItem(
block.attributes,
const newLastItem = createListItem(
lastBlock.attributes,
hasChildItems ? childList.attributes : listAttributes,
[ ...childItems, ...afterSiblings ]
);

// Replace the parent list item block, with a new block containing
// the previous siblings, followed by another block containing after siblings
// in relation to the block being outdent.
// the previous siblings before the first block being outdent,
// followed by the blocks being outdent with the after siblings added
// as children of the last block.
replaceBlocks(
[ listItemParentId ],
[ newListItemParent, newItem ]
[ newListItemParent, ...newBlocksExcludingLast, newLastItem ]
);

// Restore the selection state.
selectionChange(
newItem.clientId,
selectionEnd.attributeKey,
selectionEnd.clientId === selectionStart.clientId
? selectionStart.offset
: selectionEnd.offset,
selectionEnd.offset
);
if ( ! _hasMultiSelection ) {
selectionChange(
newLastItem.clientId,
selectionEnd.attributeKey,
selectionEnd.clientId === selectionStart.clientId
? selectionStart.offset
: selectionEnd.offset,
selectionEnd.offset
);
} else {
multiSelect(
first( newBlocksExcludingLast ).clientId,
newLastItem.clientId
);
}
}, [ clientId ] ),
];
}

0 comments on commit a82f695

Please sign in to comment.