Skip to content

Commit

Permalink
added transforms, fixed onMerge
Browse files Browse the repository at this point in the history
  • Loading branch information
mxkae committed Jan 9, 2024
1 parent 9c28238 commit 6f69bec
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 178 deletions.
23 changes: 15 additions & 8 deletions src/block/icon-list-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import { TextStyles } from './style'
import { getUseSvgDef } from '../icon-list-new/util'
import {
useOutdentListItem,
convertToListItems,
useIndentListItem,
useOutdentListItem,
useMerge,
useOnSplit,
useCopy,
} from './util'

/**
Expand Down Expand Up @@ -53,8 +55,8 @@ const Edit = props => {
attributes,
clientId,
isSelected,
onRemove,
onReplace,
mergeBlocks,
context,
className,
setAttributes,
Expand Down Expand Up @@ -101,18 +103,17 @@ const Edit = props => {

const onSplit = useOnSplit( clientId, attributes )

const onMerge = useMerge( blockContext, clientId, attributes.text )

//TODO: move cursor to adjacent blocks without double press of arrow keys
const onMerge = useMerge( clientId, mergeBlocks )

const blockProps = useBlockProps( {
ref: useCopy( clientId ),
blockHoverClass: props.blockHoverClass,
clientId: props.clientId,
attributes: props.attributes,
className: blockClassNames,
blockTag: 'li',
renderHtmlTag: false,
tabindex: '-1',
tabIndex: '-1',
} )

const { ref, ...innerBlocksProps } = useInnerBlocksProps( blockProps, {
Expand Down Expand Up @@ -188,9 +189,15 @@ const Edit = props => {
tagName="span"
className={ textClassNames }
onSplit={ onSplit }
onRemove={ onRemove }
onMerge={ onMerge }
onReplace={ onReplace }
onReplace={ onReplace
? ( blocks, ...args ) => {
onReplace(
convertToListItems( blocks ),
...args
)
}
: undefined }
/>
</div>
{ innerBlocksProps.children }
Expand Down
7 changes: 7 additions & 0 deletions src/block/icon-list-item/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@ export const settings = {
supports: {
anchor: true,
align: true,
__experimentalSelector: 'li',
},
example,
edit,
save,
merge( attributes, attributesToMerge ) {
return {
...attributes,
text: attributes.text + attributesToMerge.text,
}
},
}
215 changes: 169 additions & 46 deletions src/block/icon-list-item/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,47 @@
/**
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose'
import { useCallback } from '@wordpress/element'
import {
useSelect, useDispatch, useRegistry,
useSelect, useDispatch, useRegistry, select,
} from '@wordpress/data'
import { createBlock, cloneBlock } from '@wordpress/blocks'
import {
createBlock, cloneBlock, switchToBlockType,
} from '@wordpress/blocks'
import { store as blockEditorStore } from '@wordpress/block-editor'
import { useBlockContext } from '~stackable/hooks'

function convertBlockToList( block ) {
const list = switchToBlockType( block, 'stackable/icon-list-new' )
if ( list ) {
return list
}
const paragraph = switchToBlockType( block, 'core/paragraph' )
if ( ! paragraph ) {
return null
}
return switchToBlockType( paragraph, 'stackable/icon-list-new' )
}

export function convertToListItems( blocks ) {
const listItems = []

for ( let block of blocks ) {
if ( block.name === 'stackable/icon-list-item' ) {
listItems.push( block )
} else if ( block.name === 'stackable/icon-list-new' ) {
listItems.push( ...block.innerBlocks )
} else if ( ( block = convertBlockToList( block ) ) ) {
for ( const { innerBlocks } of block ) {
listItems.push( ...innerBlocks )
}
}
}

return listItems
}

export const useOutdentListItem = ( blockContext, clientId ) => {
const {
parentBlock,
Expand Down Expand Up @@ -106,64 +140,119 @@ export const useIndentListItem = ( blockContext, clientId ) => {
}, [ blockContext, clientId ] )
}

export const useMerge = ( blockContext, clientId, text ) => {
const {
parentBlock,
previousBlock,
hasInnerBlocks,
innerBlocks,
} = blockContext
// Modified useMerge from gutenberg list item hooks.
export const useMerge = ( clientId, onMerge ) => {
const blockContext = useBlockContext( clientId )
const { previousBlock } = blockContext

const registry = useRegistry()
const outdentListItem = useOutdentListItem( blockContext, clientId )

const {
updateBlockAttributes,
removeBlock,
moveBlocksToPosition,
} =
useDispatch( 'core/block-editor' )
const { mergeBlocks, moveBlocksToPosition } = useDispatch( 'core/block-editor' )

const {
getBlockAttributes,
} = useSelect( 'core/block-editor' )
const { getBlockOrder, getBlockRootClientId } = useSelect( 'core/block-editor' )

const getParentListItemId = id => {
const { parentBlock: parentListBlock } = select( 'stackable/block-context' ).getBlockContext( id )
const { parentBlock: parentIconListItemBlock } = select( 'stackable/block-context' ).getBlockContext( parentListBlock.clientId )

if ( parentIconListItemBlock?.name !== 'stackable/icon-list-item' ) {
return
}

let blockToMerge = previousBlock
let willOutdent = false
const { parentBlock: iconListItemParentBlock } = useBlockContext( parentBlock.clientId )
if ( ! previousBlock || iconListItemParentBlock?.name === 'stackable/icon-list-item' ) {
willOutdent = true
return parentIconListItemBlock.clientId
}
const { hasInnerBlocks: previousHasInnerBlocks, innerBlocks: previousInnerBlocks } = useBlockContext( previousBlock?.clientId )
if ( previousHasInnerBlocks ) {
// Get the last icon list block of the preceding icon list item.
const lastIconList = previousInnerBlocks[ previousInnerBlocks.length - 1 ]
// Get the last icon list item block.
blockToMerge = lastIconList.innerBlocks[ lastIconList.innerBlocks.length - 1 ]

const getTrailingId = id => {
const order = getBlockOrder( id )

if ( ! order.length ) {
return id
}

return getTrailingId( order[ order.length - 1 ] )
}

return useCallback( () => {
registry.batch( () => {
if ( willOutdent ) {
outdentListItem()
return
}
/**
* 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 { nextBlock: next } = select( 'stackable/block-context' ).getBlockContext( id )
if ( next ) {
return next.clientId
}
const parentListItemId = getParentListItemId( id )
if ( ! parentListItemId ) {
return
}
return _getNextId( parentListItemId )
}

const currentAttributes = getBlockAttributes( blockToMerge.clientId )
/**
* 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 )
}

// eslint-disable-next-line stackable/no-update-block-attributes
updateBlockAttributes(
blockToMerge.clientId,
{ text: currentAttributes.text + text }
)
// Get the first list item in the nested list.
return getBlockOrder( order[ 0 ] )[ 0 ]
}

if ( hasInnerBlocks ) {
const clientIds = innerBlocks.map( block => block.clientId )
moveBlocksToPosition( clientIds, clientId, blockToMerge.clientId )
return useCallback( forward => {
function mergeWithNested( clientIdA, clientIdB ) {
registry.batch( () => {
const [ nestedListClientId ] = getBlockOrder( clientIdB )
// Move any nested list items to the previous list item.
if ( nestedListClientId ) {
moveBlocksToPosition(
getBlockOrder( nestedListClientId ),
nestedListClientId,
getBlockRootClientId( clientIdA )
)
}
mergeBlocks( clientIdA, clientIdB )
} )
}

if ( forward ) {
const nextBlockClientId = getNextId( clientId )

if ( ! nextBlockClientId ) {
onMerge( forward )
return
}

removeBlock( clientId )
} )
if ( getParentListItemId( nextBlockClientId ) ) {
outdentListItem( nextBlockClientId )
} else {
mergeWithNested( clientId, nextBlockClientId )
}
} else {
const previousBlockClientId = previousBlock?.clientId
if ( getParentListItemId( clientId ) ) {
// Outdent nested lists.
outdentListItem()
} else if ( previousBlockClientId ) {
const trailingId = getTrailingId( previousBlockClientId )
mergeWithNested( trailingId, clientId )
} else {
onMerge( forward )
}
}
}, [ blockContext, clientId ] )
}

Expand Down Expand Up @@ -198,3 +287,37 @@ export const useOnSplit = ( clientId, attributes ) => {
return newBlock
}, [ clientId, attributes ] )
}

export const useCopy = clientId => {
const {
getBlockRootClientId, getBlockName, getBlockAttributes,
} =
useSelect( blockEditorStore )

return useRefEffect( node => {
function onCopy( event ) {
// The event propagates through all nested lists, so don't override
// when copying nested list items.
if ( event.clipboardData.getData( '__unstableWrapperBlockName' ) ) {
return
}

const rootClientId = getBlockRootClientId( clientId )
event.clipboardData.setData(
'__unstableWrapperBlockName',
getBlockName( rootClientId )
)
event.clipboardData.setData(
'__unstableWrapperBlockAttributes',
JSON.stringify( getBlockAttributes( rootClientId ) )
)
}

node.addEventListener( 'copy', onCopy )
node.addEventListener( 'cut', onCopy )
return () => {
node.removeEventListener( 'copy', onCopy )
node.removeEventListener( 'cut', onCopy )
}
}, [] )
}
14 changes: 5 additions & 9 deletions src/block/icon-list-new/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import metadata from './block.json'
import schema from './schema'
import example from './example'
import deprecated from './deprecated'
import transforms from './transforms'

/**
* WordPress dependencies
Expand All @@ -25,18 +26,13 @@ export const settings = {
supports: {
anchor: true,
spacing: true,
__unstablePasteTextInline: true,
__experimentalSelector: 'ol,ul',
__experimentalOnMerge: true,
},
example,
deprecated,
edit,
save,
merge( attributes, attributesToMerge ) {
// Make sure that the selection is always at the end of the text.
// @see https://github.com/WordPress/gutenberg/blob/3da717b8d0ac7d7821fc6d0475695ccf3ae2829f/packages/block-library/src/paragraph/index.js
return {
text:
( attributes.text || '' ) +
( attributesToMerge.text || '' ),
}
},
transforms,
}
1 change: 0 additions & 1 deletion src/block/icon-list-new/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ export const attributes = ( version = VERSION ) => {
ConditionalDisplay.addAttributes( attrObject )
Typography.addAttributes( attrObject, 'ul,ol', {
hasTextTag: false,
multilineWrapperTags: [ 'ol', 'ul' ],
} )
MarginBottom.addAttributes( attrObject )

Expand Down
Loading

0 comments on commit 6f69bec

Please sign in to comment.