Skip to content

Commit

Permalink
Support Child Blocks
Browse files Browse the repository at this point in the history
Block registration API changes:

- Introduces `allowedParents` which lets one specify which block types a
  block can be nested into.
- Introduces `allowedPostTypes` which lets one specify which post types a
  block can be inserted into.
- Deprecates `isPrivate`. Instead, use `allowedPostTypes: false`.

Selector changes:

- `getInserterItems` now respects the above properties and is the source
  of truth for whether a block is insertable or not.
- Items returned by `getInserterItems` will include new `utility` and
  `frecency` attributes. `utility` indicates how useful we think inserting
  the block will be. This lets us show contextually useful blocks at the
  top of the Suggested tab.
- Deprecates `getSupportedBlockTypes`. `getInserterItems` will remove
  any blocks that are not supported.
- Deprecates `getFrecentInserterItems`. Instead, call `getInserterItems`
  and filter/sort by `frecency`.

Inserter changes:

- Both `inserter` and `inserter-with-shortcuts` have been changed to use
  `getInserterItems` and its new functionality.
  • Loading branch information
noisysocks committed Apr 24, 2018
1 parent f922df2 commit 458a0b7
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 556 deletions.
9 changes: 9 additions & 0 deletions blocks/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { get, isFunction, some } from 'lodash';
* WordPress dependencies
*/
import { applyFilters } from '@wordpress/hooks';
import { deprecated } from '@wordpress/utils';

/**
* Internal dependencies
Expand Down Expand Up @@ -160,6 +161,14 @@ export function registerBlockType( name, settings ) {
if ( ! settings.icon ) {
settings.icon = 'block-default';
}
if ( 'isPrivate' in settings ) {
deprecated( 'isPrivate', {
version: '2.9',
alternative: 'allowedPostTypes',
plugin: 'Gutenberg',
} );
settings.allowedPostTypes = !! settings.isPrivate;
}

return blocks[ name ] = settings;
}
Expand Down
2 changes: 1 addition & 1 deletion blocks/library/block/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const name = 'core/block';
export const settings = {
title: __( 'Shared Block' ),
category: 'shared',
isPrivate: true,
allowedPostTypes: false,

attributes: {
ref: {
Expand Down
27 changes: 19 additions & 8 deletions editor/components/inserter-with-shortcuts/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { filter, isEmpty } from 'lodash';
import { reject, isEmpty, orderBy, flow } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -17,18 +17,30 @@ import { withDispatch, withSelect } from '@wordpress/data';
*/
import './style.scss';

function filterItems( items ) {
return reject( items, ( item ) =>
item.name === getDefaultBlockName() && isEmpty( item.initialAttributes )
);
}

function orderItems( items ) {
return orderBy( items, [ 'utility', 'frecency' ], [ 'desc', 'desc' ] );
}

function limitItems( items ) {
return items.slice( 0, 3 );
}

function InserterWithShortcuts( { items, isLocked, onInsert } ) {
if ( isLocked ) {
return null;
}

const itemsWithoutDefaultBlock = filter( items, ( item ) =>
item.name !== getDefaultBlockName() || ! isEmpty( item.initialAttributes )
).slice( 0, 3 );
const bestItems = flow( filterItems, orderItems, limitItems )( items );

return (
<div className="editor-inserter-with-shortcuts">
{ itemsWithoutDefaultBlock.map( ( item ) => (
{ bestItems.map( ( item ) => (
<IconButton
key={ item.id }
className="editor-inserter-with-shortcuts__block"
Expand All @@ -53,10 +65,9 @@ export default compose(
};
} ),
withSelect( ( select, { allowedBlockTypes, rootUID } ) => {
const { getFrecentInserterItems, getSupportedBlocks } = select( 'core/editor' );
const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes );
const { getInserterItems } = select( 'core/editor' );
return {
items: getFrecentInserterItems( supportedBlocks, 4 ),
items: getInserterItems( allowedBlockTypes, rootUID ),
};
} ),
withDispatch( ( dispatch, ownProps ) => {
Expand Down
16 changes: 5 additions & 11 deletions editor/components/inserter/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* External dependencies
*/
import { isEmpty } from 'lodash';

/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -41,15 +36,15 @@ class Inserter extends Component {

render() {
const {
items,
position,
title,
children,
onInsertBlock,
hasSupportedBlocks,
isLocked,
} = this.props;

if ( ! hasSupportedBlocks || isLocked ) {
if ( items.length === 0 || isLocked ) {
return null;
}

Expand Down Expand Up @@ -79,7 +74,7 @@ class Inserter extends Component {
onClose();
};

return <InserterMenu onSelect={ onSelect } />;
return <InserterMenu items={ items } onSelect={ onSelect } />;
} }
/>
);
Expand All @@ -100,17 +95,16 @@ export default compose( [
getEditedPostAttribute,
getBlockInsertionPoint,
getSelectedBlock,
getSupportedBlocks,
getInserterItems,
} = select( 'core/editor' );

const insertionPoint = getBlockInsertionPoint();
const { rootUID } = insertionPoint;
const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes );
return {
title: getEditedPostAttribute( 'title' ),
insertionPoint,
selectedBlock: getSelectedBlock(),
hasSupportedBlocks: true === supportedBlocks || ! isEmpty( supportedBlocks ),
items: getInserterItems( allowedBlockTypes, rootUID ),
};
} ),
withDispatch( ( dispatch, ownProps ) => ( {
Expand Down
54 changes: 22 additions & 32 deletions editor/components/inserter/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
flow,
groupBy,
includes,
isEmpty,
orderBy,
pick,
some,
sortBy,
isEmpty,
} from 'lodash';

/**
Expand All @@ -24,9 +25,9 @@ import {
withInstanceId,
withSpokenMessages,
} from '@wordpress/components';
import { getCategories, isSharedBlock, withEditorSettings } from '@wordpress/blocks';
import { getCategories, isSharedBlock } from '@wordpress/blocks';
import { keycodes } from '@wordpress/utils';
import { withSelect, withDispatch } from '@wordpress/data';
import { withDispatch } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -49,6 +50,7 @@ export const searchItems = ( items, searchTerm ) => {
* Module constants
*/
const ARROWS = pick( keycodes, [ 'UP', 'DOWN', 'LEFT', 'RIGHT' ] );
const MAX_SUGGESTED_ITEMS = 9;

export class InserterMenu extends Component {
constructor() {
Expand Down Expand Up @@ -114,7 +116,7 @@ export class InserterMenu extends Component {
}

getItemsForTab( tab ) {
const { items, frecentItems } = this.props;
const { items } = this.props;

// If we're searching, use everything, otherwise just get the items visible in this tab
if ( this.state.filterValue ) {
Expand All @@ -124,7 +126,8 @@ export class InserterMenu extends Component {
let predicate;
switch ( tab ) {
case 'suggested':
return frecentItems;
predicate = ( item ) => item.utility > 0;
break;

case 'blocks':
predicate = ( item ) => item.category !== 'embed' && item.category !== 'shared';
Expand All @@ -144,7 +147,8 @@ export class InserterMenu extends Component {

sortItems( items ) {
if ( 'suggested' === this.state.tab && ! this.state.filterValue ) {
return items;
const sortedItems = orderBy( items, [ 'utility', 'frecency' ], [ 'desc', 'desc' ] );
return sortedItems.slice( 0, MAX_SUGGESTED_ITEMS );
}

const getCategoryIndex = ( item ) => {
Expand All @@ -158,6 +162,13 @@ export class InserterMenu extends Component {
return groupBy( items, ( item ) => item.category );
}

getVisibleItems( items ) {
return flow(
this.searchItems,
this.sortItems,
)( items );
}

getVisibleItemsByCategory( items ) {
return flow(
this.searchItems,
Expand Down Expand Up @@ -216,11 +227,6 @@ export class InserterMenu extends Component {
renderTabView( tab ) {
const itemsForTab = this.getItemsForTab( tab );

// If the Suggested tab is selected, don't render category headers
if ( 'suggested' === tab ) {
return this.renderItems( itemsForTab );
}

// If the Shared tab is selected and we have no results, display a friendly message
if ( 'shared' === tab && itemsForTab.length === 0 ) {
return (
Expand All @@ -230,6 +236,11 @@ export class InserterMenu extends Component {
);
}

// If the Suggested tab is selected, don't render category headers
if ( 'suggested' === tab ) {
return this.renderItems( this.getVisibleItems( itemsForTab ) );
}

const visibleItemsByCategory = this.getVisibleItemsByCategory( itemsForTab );

// If our results have only items from one category, don't render category headers
Expand Down Expand Up @@ -336,27 +347,6 @@ export class InserterMenu extends Component {
}

export default compose(
withEditorSettings( ( settings ) => {
const { allowedBlockTypes } = settings;

return {
allowedBlockTypes,
};
} ),
withSelect( ( select, { allowedBlockTypes } ) => {
const {
getBlockInsertionPoint,
getInserterItems,
getFrecentInserterItems,
getSupportedBlocks,
} = select( 'core/editor' );
const { rootUID } = getBlockInsertionPoint();
const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes );
return {
items: getInserterItems( supportedBlocks ),
frecentItems: getFrecentInserterItems( supportedBlocks ),
};
} ),
withDispatch( ( dispatch ) => ( {
fetchSharedBlocks: dispatch( 'core/editor' ).fetchSharedBlocks,
} ) ),
Expand Down
Loading

0 comments on commit 458a0b7

Please sign in to comment.