Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract inserter state into reusable hooks #22710

Merged
merged 2 commits into from
Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 9 additions & 63 deletions packages/block-editor/src/components/inserter/block-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import { __, _x, _n, sprintf } from '@wordpress/i18n';
import { withSpokenMessages } from '@wordpress/components';
import { addQueryArgs } from '@wordpress/url';
import { controlsRepeat } from '@wordpress/icons';
import { speak } from '@wordpress/a11y';
import { createBlock } from '@wordpress/blocks';
import { useMemo, useEffect } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
Expand All @@ -33,19 +31,7 @@ import __experimentalInserterMenuExtension from '../inserter-menu-extension';
import { searchBlockItems } from './search-items';
import InserterPanel from './panel';
import InserterNoResults from './no-results';

// Copied over from the Columns block. It seems like it should become part of public API.
const createBlocksFromInnerBlocksTemplate = ( innerBlocksTemplate ) => {
return map(
innerBlocksTemplate,
( [ name, attributes, innerBlocks = [] ] ) =>
createBlock(
name,
attributes,
createBlocksFromInnerBlocksTemplate( innerBlocks )
)
);
};
import useBlockTypesState from './hooks/use-block-types-state';

const getBlockNamespace = ( item ) => item.name.split( '/' )[ 0 ];

Expand All @@ -55,64 +41,24 @@ export function InserterBlockList( {
rootClientId,
onInsert,
onHover,
__experimentalSelectBlockOnInsert: selectBlockOnInsert,
filterValue,
debouncedSpeak,
} ) {
const {
categories,
collections,
items,
rootChildBlocks,
fetchReusableBlocks,
} = useSelect(
const [ items, categories, collections, onSelectItem ] = useBlockTypesState(
rootClientId,
onInsert
);
const rootChildBlocks = useSelect(
( select ) => {
const { getInserterItems, getBlockName, getSettings } = select(
'core/block-editor'
);
const {
getCategories,
getCollections,
getChildBlockNames,
} = select( 'core/blocks' );
const { getBlockName } = select( 'core/block-editor' );
const { getChildBlockNames } = select( 'core/blocks' );
const rootBlockName = getBlockName( rootClientId );
const { __experimentalFetchReusableBlocks } = getSettings();

return {
categories: getCategories(),
collections: getCollections(),
rootChildBlocks: getChildBlockNames( rootBlockName ),
items: getInserterItems( rootClientId ),
fetchReusableBlocks: __experimentalFetchReusableBlocks,
};
return getChildBlockNames( rootBlockName );
},
[ rootClientId ]
);

// Fetch resuable blocks on mount
useEffect( () => {
if ( fetchReusableBlocks ) {
fetchReusableBlocks();
}
}, [] );

const onSelectItem = ( item ) => {
const { name, title, initialAttributes, innerBlocks } = item;
const insertedBlock = createBlock(
name,
initialAttributes,
createBlocksFromInnerBlocksTemplate( innerBlocks )
);

onInsert( insertedBlock );

if ( ! selectBlockOnInsert ) {
// translators: %s: the name of the block that has been added
const message = sprintf( __( '%s block added' ), title );
speak( message );
}
};

const filteredItems = useMemo( () => {
return searchBlockItems( items, categories, collections, filterValue );
}, [ filterValue, items, categories, collections ] );
Expand Down
37 changes: 4 additions & 33 deletions packages/block-editor/src/components/inserter/block-patterns.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
/**
* External dependencies
*/
import { map, fromPairs } from 'lodash';
import { fromPairs } from 'lodash';

/**
* WordPress dependencies
*/
import { useMemo, useCallback } from '@wordpress/element';
import { parse, cloneBlock } from '@wordpress/blocks';
import { useDispatch, useSelect } from '@wordpress/data';
import { parse } from '@wordpress/blocks';
import { ENTER, SPACE } from '@wordpress/keycodes';
import { __, sprintf, _x } from '@wordpress/i18n';
import { __, _x } from '@wordpress/i18n';

/**
* Internal dependencies
Expand All @@ -20,35 +19,7 @@ import useAsyncList from './use-async-list';
import InserterPanel from './panel';
import { searchItems } from './search-items';
import InserterNoResults from './no-results';

const usePatternsState = ( onInsert ) => {
const { patternCategories, patterns } = useSelect( ( select ) => {
const {
__experimentalBlockPatterns,
__experimentalBlockPatternCategories,
} = select( 'core/block-editor' ).getSettings();
return {
patterns: __experimentalBlockPatterns,
patternCategories: __experimentalBlockPatternCategories,
};
}, [] );
const { createSuccessNotice } = useDispatch( 'core/notices' );
const onClickPattern = useCallback( ( pattern, blocks ) => {
onInsert( map( blocks, ( block ) => cloneBlock( block ) ) );
createSuccessNotice(
sprintf(
/* translators: %s: block pattern title. */
__( 'Pattern "%s" inserted.' ),
pattern.title
),
{
type: 'snackbar',
}
);
}, [] );

return [ patterns, patternCategories, onClickPattern ];
};
import usePatternsState from './hooks/use-patterns-state';

function BlockPattern( { pattern, onClick } ) {
const { content, viewportWidth } = pattern;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* External dependencies
*/
import { map } from 'lodash';
youknowriad marked this conversation as resolved.
Show resolved Hide resolved

/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { createBlock } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';

// Copied over from the Columns block. It seems like it should become part of public API.
const createBlocksFromInnerBlocksTemplate = ( innerBlocksTemplate ) => {
return map(
innerBlocksTemplate,
( [ name, attributes, innerBlocks = [] ] ) =>
createBlock(
name,
attributes,
createBlocksFromInnerBlocksTemplate( innerBlocks )
)
);
};

/**
* Retrieves the block types inserter state.
*
* @param {string=} rootClientId Insertion's root client ID.
* @param {Function} onInsert function called when inserter a list of blocks.
* @return {Array} Returns the block types state. (block types, categories, collections, onSelect handler)
*/
const useBlockTypesState = ( rootClientId, onInsert ) => {
const { categories, collections, items, fetchReusableBlocks } = useSelect(
( select ) => {
const { getInserterItems, getSettings } = select(
'core/block-editor'
);
const { getCategories, getCollections } = select( 'core/blocks' );
const { __experimentalFetchReusableBlocks } = getSettings();

return {
categories: getCategories(),
collections: getCollections(),
items: getInserterItems( rootClientId ),
fetchReusableBlocks: __experimentalFetchReusableBlocks,
};
},
[ rootClientId ]
);

// Fetch resuable blocks on mount
useEffect( () => {
if ( fetchReusableBlocks ) {
fetchReusableBlocks();
}
}, [] );

const onSelectItem = ( { name, initialAttributes, innerBlocks } ) => {
const insertedBlock = createBlock(
name,
initialAttributes,
createBlocksFromInnerBlocksTemplate( innerBlocks )
);

onInsert( insertedBlock );
};

return [ items, categories, collections, onSelectItem ];
};

export default useBlockTypesState;
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* External dependencies
*/
import { pick } from 'lodash';

/**
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
import { _n } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';

/**
* @typedef WPInserterConfig
*
* @property {string=} rootClientId Inserter Root Client ID.
* @property {string=} clientId Inserter Client ID.
* @property {boolean} isAppender Whether the inserter is an appender or not.
* @property {boolean} selectBlockOnInsert Whether the block should be selected on insert.
*/

/**
* Returns the insertion point state given the inserter config.
*
* @param {WPInserterConfig} config Inserter Config.
* @return {Array} Insertion Point State (rootClientID, onInsertBlocks and onToggle).
*/
function useInsertionPoint( {
rootClientId,
clientId,
isAppender,
selectBlockOnInsert,
} ) {
const {
destinationRootClientId,
getSelectedBlock,
getBlockIndex,
getBlockSelectionEnd,
getBlockOrder,
} = useSelect(
( select ) => {
const {
getSettings,
getBlockRootClientId,
getBlockSelectionEnd: _getBlockSelectionEnd,
} = select( 'core/block-editor' );

let destRootClientId = rootClientId;
if ( ! destRootClientId && ! clientId && ! isAppender ) {
const end = _getBlockSelectionEnd();
if ( end ) {
destRootClientId = getBlockRootClientId( end );
}
}
return {
hasPatterns: !! getSettings().__experimentalBlockPatterns
?.length,
destinationRootClientId: destRootClientId,
...pick( select( 'core/block-editor' ), [
'getSelectedBlock',
'getBlockIndex',
'getBlockSelectionEnd',
'getBlockOrder',
] ),
};
},
[ isAppender, clientId, rootClientId ]
);
const {
replaceBlocks,
insertBlocks,
showInsertionPoint,
hideInsertionPoint,
} = useDispatch( 'core/block-editor' );

function getInsertionIndex() {
// If the clientId is defined, we insert at the position of the block.
if ( clientId ) {
return getBlockIndex( clientId, destinationRootClientId );
}

// If there a selected block, we insert after the selected block.
const end = getBlockSelectionEnd();
if ( ! isAppender && end ) {
return getBlockIndex( end, destinationRootClientId ) + 1;
}

// Otherwise, we insert at the end of the current rootClientId
return getBlockOrder( destinationRootClientId ).length;
}

const onInsertBlocks = ( blocks ) => {
const selectedBlock = getSelectedBlock();
if (
! isAppender &&
selectedBlock &&
isUnmodifiedDefaultBlock( selectedBlock )
) {
replaceBlocks( selectedBlock.clientId, blocks );
} else {
insertBlocks(
blocks,
getInsertionIndex(),
destinationRootClientId,
selectBlockOnInsert
);
}

if ( ! selectBlockOnInsert ) {
// translators: %d: the name of the block that has been added
const message = _n(
'%d block added.',
'%d blocks added',
blocks.length
);
speak( message );
}
};

const onToggleInsertionPoint = ( show ) => {
if ( show ) {
const index = getInsertionIndex();
showInsertionPoint( destinationRootClientId, index );
} else {
hideInsertionPoint();
}
};

return [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ];
}

export default useInsertionPoint;
Loading