-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[List v2]: Indent multiple list items (#40991)
* [List v2]: Indent multiple list items * simplify logic
- Loading branch information
1 parent
ed1f17d
commit db7b40e
Showing
2 changed files
with
51 additions
and
64 deletions.
There are no files selected for viewing
113 changes: 50 additions & 63 deletions
113
packages/block-library/src/list-item/hooks/use-indent-list-item.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,76 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { first } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useCallback } from '@wordpress/element'; | ||
import { useSelect, useDispatch } from '@wordpress/data'; | ||
import { store as blockEditorStore } from '@wordpress/block-editor'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { createListItem } from '../utils'; | ||
import { createBlock, cloneBlock } from '@wordpress/blocks'; | ||
|
||
export default function useIndentListItem( clientId ) { | ||
const { canIndent } = useSelect( | ||
( innerSelect ) => { | ||
const { getBlockIndex } = innerSelect( blockEditorStore ); | ||
return { | ||
canIndent: getBlockIndex( clientId ) > 0, | ||
}; | ||
}, | ||
const canIndent = useSelect( | ||
( select ) => select( blockEditorStore ).getBlockIndex( clientId ) > 0, | ||
[ clientId ] | ||
); | ||
const { replaceBlocks, selectionChange } = useDispatch( blockEditorStore ); | ||
const { replaceBlocks, selectionChange, multiSelect } = useDispatch( | ||
blockEditorStore | ||
); | ||
const { | ||
getBlockRootClientId, | ||
getBlock, | ||
getBlockOrder, | ||
getPreviousBlockClientId, | ||
getSelectionStart, | ||
getSelectionEnd, | ||
getBlockIndex, | ||
hasMultiSelection, | ||
getMultiSelectedBlockClientIds, | ||
} = useSelect( blockEditorStore ); | ||
|
||
return [ | ||
canIndent, | ||
useCallback( () => { | ||
const _hasMultiSelection = hasMultiSelection(); | ||
const clientIds = _hasMultiSelection | ||
? getMultiSelectedBlockClientIds() | ||
: [ clientId ]; | ||
const clonedBlocks = clientIds.map( ( _clientId ) => | ||
cloneBlock( getBlock( _clientId ) ) | ||
); | ||
const previousSiblingId = getPreviousBlockClientId( clientId ); | ||
const newListItem = cloneBlock( getBlock( previousSiblingId ) ); | ||
// If the sibling has no innerBlocks, create a new `list` block. | ||
if ( ! newListItem.innerBlocks?.length ) { | ||
newListItem.innerBlocks = [ createBlock( 'core/list' ) ]; | ||
} | ||
// A list item usually has one `list`, but it's possible to have | ||
// more. So we need to preserve the previous `list` blocks and | ||
// merge the new blocks to the last `list`. | ||
newListItem.innerBlocks[ | ||
newListItem.innerBlocks.length - 1 | ||
].innerBlocks.push( ...clonedBlocks ); | ||
|
||
// We get the selection start/end here, because when | ||
// we replace blocks, the selection is updated too. | ||
const selectionStart = getSelectionStart(); | ||
const selectionEnd = getSelectionEnd(); | ||
|
||
const parentId = getBlockRootClientId( clientId ); | ||
const previousSiblingId = getBlockOrder( parentId )[ | ||
getBlockIndex( clientId ) - 1 | ||
]; | ||
const previousSibling = getBlock( previousSiblingId ); | ||
const previousSiblingChildren = | ||
first( previousSibling.innerBlocks )?.innerBlocks || []; | ||
const previousSiblingListAttributes = | ||
first( previousSibling.innerBlocks )?.attributes || {}; | ||
const block = getBlock( clientId ); | ||
|
||
const childListAttributes = first( block.innerBlocks )?.attributes; | ||
const childItemBlocks = | ||
first( block.innerBlocks )?.innerBlocks || []; | ||
|
||
const newBlock = createListItem( | ||
block.attributes, | ||
childListAttributes, | ||
childItemBlocks | ||
); | ||
// Replace the previous sibling of the block being indented and the indented block, | ||
// Replace the previous sibling of the block being indented and the indented blocks, | ||
// with a new block whose attributes are equal to the ones of the previous sibling and | ||
// whose descendants are the children of the previous sibling, followed by the indented block. | ||
// whose descendants are the children of the previous sibling, followed by the indented blocks. | ||
replaceBlocks( | ||
[ previousSiblingId, clientId ], | ||
[ | ||
createListItem( | ||
previousSibling.attributes, | ||
previousSiblingListAttributes, | ||
[ ...previousSiblingChildren, newBlock ] | ||
), | ||
] | ||
); | ||
|
||
// Restore the selection state. | ||
selectionChange( | ||
newBlock.clientId, | ||
selectionEnd.attributeKey, | ||
selectionEnd.clientId === selectionStart.clientId | ||
? selectionStart.offset | ||
: selectionEnd.offset, | ||
selectionEnd.offset | ||
[ previousSiblingId, ...clientIds ], | ||
[ newListItem ] | ||
); | ||
if ( ! _hasMultiSelection ) { | ||
selectionChange( | ||
clonedBlocks[ 0 ].clientId, | ||
selectionEnd.attributeKey, | ||
selectionEnd.clientId === selectionStart.clientId | ||
? selectionStart.offset | ||
: selectionEnd.offset, | ||
selectionEnd.offset | ||
); | ||
} else { | ||
multiSelect( | ||
clonedBlocks[ 0 ].clientId, | ||
clonedBlocks[ clonedBlocks.length - 1 ].clientId | ||
); | ||
} | ||
}, [ clientId ] ), | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters