Skip to content

Commit

Permalink
Add block variations to the slash inserter
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Jun 30, 2020
1 parent dc4c0d0 commit ae66586
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 273 deletions.
178 changes: 88 additions & 90 deletions packages/block-editor/src/autocompleters/block.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,38 @@
/**
* External dependencies
*/
import { once } from 'lodash';
import { noop, map } from 'lodash';

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

/**
* Internal dependencies
*/
import { searchBlockItems } from '../components/inserter/search-items';
import useBlockTypesState from '../components/inserter/hooks/use-block-types-state';
import { includeVariationsInInserterItems } from '../components/inserter/utils';
import BlockIcon from '../components/block-icon';

/** @typedef {import('@wordpress/block-editor').WPEditorInserterItem} WPEditorInserterItem */

/** @typedef {import('@wordpress/components').WPCompleter} WPCompleter */

/**
* Returns the client ID of the parent where a newly inserted block would be
* placed.
*
* @return {string} Client ID of the parent where a newly inserted block would
* be placed.
*/
function defaultGetBlockInsertionParentClientId() {
return select( 'core/block-editor' ).getBlockInsertionPoint().rootClientId;
}

/**
* Returns the inserter items for the specified parent block.
*
* @param {string} rootClientId Client ID of the block for which to retrieve
* inserter items.
*
* @return {Array<WPEditorInserterItem>} The inserter items for the specified
* parent.
*/
function defaultGetInserterItems( rootClientId ) {
return select( 'core/block-editor' ).getInserterItems( rootClientId );
}

/**
* Returns the name of the currently selected block.
*
* @return {string?} The name of the currently selected block or `null` if no
* block is selected.
*/
function defaultGetSelectedBlockName() {
const { getSelectedBlockClientId, getBlockName } = select(
'core/block-editor'
const createBlocksFromInnerBlocksTemplate = ( innerBlocksTemplate ) => {
return map(
innerBlocksTemplate,
( [ name, attributes, innerBlocks = [] ] ) =>
createBlock(
name,
attributes,
createBlocksFromInnerBlocksTemplate( innerBlocks )
)
);
const selectedBlockClientId = getSelectedBlockClientId();
return selectedBlockClientId ? getBlockName( selectedBlockClientId ) : null;
}
};

/**
* Triggers a fetch of reusable blocks, once.
*
* TODO: Reusable blocks fetching should be reimplemented as a core-data entity
* resolver, not relying on `core/editor` (see #7119). The implementation here
* is imperfect in that the options result will not await the completion of the
* fetch request and thus will not include any reusable blocks. This has always
* been true, but relied upon the fact the user would be delayed in typing an
* autocompleter search query. Once implemented using resolvers, the status of
* this request could be subscribed to as part of a promised return value using
* the result of `hasFinishedResolution`. There is currently reliable way to
* determine that a reusable blocks fetch request has completed.
*
* @return {Promise} Promise resolving once reusable blocks fetched.
*/
const fetchReusableBlocks = once( () => {
const { __experimentalFetchReusableBlocks } = select(
'core/block-editor'
).getSettings();
/** @typedef {import('@wordpress/block-editor').WPEditorInserterItem} WPEditorInserterItem */

if ( __experimentalFetchReusableBlocks ) {
__experimentalFetchReusableBlocks();
}
} );
/** @typedef {import('@wordpress/components').WPCompleter} WPCompleter */

/**
* Creates a blocks repeater for replacing the current block with a selected block type.
Expand All @@ -91,46 +44,91 @@ const fetchReusableBlocks = once( () => {
*
* @return {WPCompleter} A blocks completer.
*/
export function createBlockCompleter( {
// Allow store-based selectors to be overridden for unit test.
getBlockInsertionParentClientId = defaultGetBlockInsertionParentClientId,
getInserterItems = defaultGetInserterItems,
getSelectedBlockName = defaultGetSelectedBlockName,
} = {} ) {
function createBlockCompleter() {
return {
name: 'blocks',
className: 'block-editor-autocompleters__block',
triggerPrefix: '/',
options() {
fetchReusableBlocks();

const selectedBlockName = getSelectedBlockName();
return getInserterItems( getBlockInsertionParentClientId() ).filter(
// Avoid offering to replace the current block with a block of the same type.
( inserterItem ) => selectedBlockName !== inserterItem.name
useItems( filterValue ) {
const { rootClientId, selectedBlockName } = useSelect(
( select ) => {
const {
getSelectedBlockClientId,
getBlockName,
getBlockInsertionPoint,
} = select( 'core/block-editor' );
const selectedBlockClientId = getSelectedBlockClientId();
return {
selectedBlockName: selectedBlockClientId
? getBlockName( selectedBlockClientId )
: null,
rootClientId: getBlockInsertionPoint().rootClientId,
};
},
[]
);
},
getOptionKeywords( inserterItem ) {
const { title, keywords = [], category } = inserterItem;
return [ category, ...keywords, title ];
},
getOptionLabel( inserterItem ) {
const { icon, title } = inserterItem;
return [ <BlockIcon key="icon" icon={ icon } showColors />, title ];
const [ items, categories, collections ] = useBlockTypesState(
rootClientId,
noop
);

const filteredItems = useMemo( () => {
return searchBlockItems(
items,
categories,
collections,
filterValue
).filter( ( item ) => item.name !== selectedBlockName );
}, [
filterValue,
selectedBlockName,
items,
categories,
collections,
] );

const options = useMemo(
() =>
includeVariationsInInserterItems( filteredItems ).map(
( blockItem ) => {
const { title, icon, isDisabled } = blockItem;
return {
key: `block-${ blockItem.id }`,
value: blockItem,
label: (
<>
<BlockIcon
key="icon"
icon={ icon }
showColors
/>
{ title }
</>
),
isDisabled,
};
}
),
[ filteredItems ]
);

return [ options ];
},
allowContext( before, after ) {
return ! ( /\S/.test( before ) || /\S/.test( after ) );
},
getOptionCompletion( inserterItem ) {
const { name, initialAttributes } = inserterItem;
const { name, initialAttributes, innerBlocks } = inserterItem;
return {
action: 'replace',
value: createBlock( name, initialAttributes ),
value: createBlock(
name,
initialAttributes,
createBlocksFromInnerBlocksTemplate( innerBlocks )
),
};
},
isOptionDisabled( inserterItem ) {
return inserterItem.isDisabled;
},
};
}

Expand Down
146 changes: 0 additions & 146 deletions packages/block-editor/src/autocompleters/test/block.js

This file was deleted.

Loading

0 comments on commit ae66586

Please sign in to comment.