UNPKG

@wordpress/block-editor

Version:
173 lines (158 loc) 4.74 kB
/** * WordPress dependencies */ import { createContext, useContext } from '@wordpress/element'; /** * Internal dependencies */ import { orderBy } from '../../utils/sorting'; export const DEFAULT_BLOCK_LIST_CONTEXT = { scrollRef: null, blocksLayouts: { current: {} }, findBlockLayoutByClientId, getBlockLayoutsOrderedByYCoord, findBlockLayoutByPosition, updateBlocksLayouts, }; const Context = createContext( DEFAULT_BLOCK_LIST_CONTEXT ); const { Provider, Consumer } = Context; /** * Finds a block's layout data by position. * * @param {Object} data Blocks layouts object. * @param {Object} position Position to use for finding the block. * @param {number} position.x X coordinate. * @param {number} position.y Y coordinate. * * @return {Object|undefined} Found block layout data that matches the provided position. If none is found, `undefined` will be returned. */ function findBlockLayoutByPosition( data, position ) { // Only enabled for root level blocks return Object.values( data ).find( ( block ) => { return ( position.x >= block.x && position.x <= block.x + block.width && position.y >= block.y && position.y <= block.y + block.height ); } ); } /** * Finds a block's layout data by its client Id. * * @param {Object} data Blocks layouts object. * @param {string} clientId Block's clientId. * * @return {Object} Found block layout data. */ function findBlockLayoutByClientId( data, clientId ) { return Object.entries( data ).reduce( ( acc, entry ) => { const item = entry[ 1 ]; if ( acc ) { return acc; } if ( item?.clientId === clientId ) { return item; } if ( item?.innerBlocks && Object.keys( item.innerBlocks ).length > 0 ) { return findBlockLayoutByClientId( item.innerBlocks, clientId ); } return null; }, null ); } /** * Deletes the layout data of a block by its client Id. * * @param {Object} data Blocks layouts object. * @param {string} clientId Block's clientsId. * * @return {Object} Updated data object. */ export function deleteBlockLayoutByClientId( data, clientId ) { return Object.keys( data ).reduce( ( acc, key ) => { if ( key !== clientId ) { acc[ key ] = data[ key ]; } if ( data[ key ]?.innerBlocks && Object.keys( data[ key ].innerBlocks ).length > 0 ) { if ( acc[ key ] ) { acc[ key ].innerBlocks = deleteBlockLayoutByClientId( data[ key ].innerBlocks, clientId ); } } return acc; }, {} ); } /** * Orders the block's layout data by its Y coordinate. * * @param {Object} data Blocks layouts object. * * @return {Object} Blocks layouts object ordered by its Y coordinate. */ function getBlockLayoutsOrderedByYCoord( data ) { // Only enabled for root level blocks. return orderBy( Object.values( data ), 'y' ); } /** * Updates or deletes a block's layout data in the blocksLayouts object, * in case of deletion, the layout data is not required. * * @param {Object} blocksLayouts Blocks layouts object. * @param {Object} blockData Block's layout data to add or remove to/from the blockLayouts object. * @param {string} blockData.clientId Block's clientId. * @param {?string} blockData.rootClientId Optional. Block's rootClientId. * @param {?boolean} blockData.shouldRemove Optional. Flag to remove it from the blocksLayout list. * @param {number} blockData.width Block's width. * @param {number} blockData.height Block's height. * @param {number} blockData.x Block's x coordinate (relative to the parent). * @param {number} blockData.y Block's y coordinate (relative to the parent). */ function updateBlocksLayouts( blocksLayouts, blockData ) { const { clientId, rootClientId, shouldRemove, ...layoutProps } = blockData; if ( clientId && shouldRemove ) { blocksLayouts.current = deleteBlockLayoutByClientId( blocksLayouts.current, clientId ); return; } if ( clientId && ! rootClientId ) { blocksLayouts.current[ clientId ] = { clientId, rootClientId, ...layoutProps, innerBlocks: { ...blocksLayouts.current[ clientId ]?.innerBlocks, }, }; } else if ( clientId && rootClientId ) { const block = findBlockLayoutByClientId( blocksLayouts.current, rootClientId ); if ( block ) { block.innerBlocks[ clientId ] = { clientId, rootClientId, ...layoutProps, innerBlocks: { ...block.innerBlocks[ clientId ]?.innerBlocks, }, }; } } } export { Provider as BlockListProvider, Consumer as BlockListConsumer }; /** * Hook that returns the block list context. * * @return {Object} Block list context */ export const useBlockListContext = () => { return useContext( Context ); };