Skip to content

Commit

Permalink
Inserter: Add block pattern category selection dropdown in main inser…
Browse files Browse the repository at this point in the history
…ter (#24954)

* Add dropdown for patterns in the main inserter to select and view one specific category at a time.

* Remove the viewing: text, and increase the target size of the dropdown button.

* Fix scss lint errors.

* Fix the lazy loading of patterns when specific categories are selected.

* Make sure quick inserter searches work for patterns and select all categories for the search.

* switch to filtering patterns in component rather than hook

* Improve performance of pattern search with large pattern sets

* Also change filtered patterns if pattern array changes

* Fix casing error

* Add uncategorized back

* Switch to native select control

* Prevent duplicate 'uncategorized' categories being added

* remove unused arg

* Design cleanup.

* Switch to useMemo to filter patterns instead of storing in component state

* remove empty categories

* Remove arguments that are no longer used.

Co-authored-by: Glen Davies <[email protected]>
  • Loading branch information
apeatling and Glen Davies authored Sep 16, 2020
1 parent 3c7a5aa commit a2419d0
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 53 deletions.
141 changes: 89 additions & 52 deletions packages/block-editor/src/components/inserter/block-patterns-tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,30 @@ import { fromPairs } from 'lodash';
/**
* WordPress dependencies
*/
import { useMemo, useCallback } from '@wordpress/element';
import { useMemo, useCallback, useEffect } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import { useAsyncList } from '@wordpress/compose';

/**
* Internal dependencies
*/
import InserterPanel from './panel';
import PatternInserterPanel from './pattern-panel';
import { searchItems } from './search-items';
import InserterNoResults from './no-results';
import usePatternsState from './hooks/use-patterns-state';
import BlockPatternList from '../block-patterns-list';

function BlockPatternsSearchResults( { filterValue, onInsert } ) {
const [ patterns, , onClick ] = usePatternsState( onInsert );
const currentShownPatterns = useAsyncList( patterns );
const [ allPatterns, , onClick ] = usePatternsState( onInsert );

const filteredPatterns = useMemo(
() => searchItems( patterns, filterValue ),
[ filterValue, patterns ]
() => searchItems( allPatterns, filterValue ),
[ filterValue, allPatterns ]
);

const currentShownPatterns = useAsyncList( filteredPatterns );

if ( filterValue ) {
return !! filteredPatterns.length ? (
<InserterPanel title={ __( 'Search Results' ) }>
Expand All @@ -43,91 +45,126 @@ function BlockPatternsSearchResults( { filterValue, onInsert } ) {
}
}

function BlockPatternsPerCategories( { onInsert } ) {
const [ patterns, categories, onClick ] = usePatternsState( onInsert );
function BlockPatternsCategory( {
onInsert,
selectedCategory,
onClickCategory,
} ) {
const [ allPatterns, allCategories, onClick ] = usePatternsState(
onInsert
);

// Remove any empty categories
const populatedCategories = useMemo(
() =>
allCategories.filter( ( category ) =>
allPatterns.some( ( pattern ) =>
pattern.categories.includes( category.name )
)
),
[ allPatterns, allCategories ]
);

const patternCategory = selectedCategory
? selectedCategory
: populatedCategories[ 0 ];

useEffect( () => {
if (
allPatterns.some(
( pattern ) => getPatternIndex( pattern ) === Infinity
) &&
! populatedCategories.find(
( category ) => category.name === 'uncategorized'
)
) {
populatedCategories.push( {
name: 'uncategorized',
label: _x( 'Uncategorized' ),
} );
}
}, [ populatedCategories, allPatterns ] );

const getPatternIndex = useCallback(
( pattern ) => {
if ( ! pattern.categories || ! pattern.categories.length ) {
return Infinity;
}
const indexedCategories = fromPairs(
categories.map( ( { name }, index ) => [ name, index ] )
populatedCategories.map( ( { name }, index ) => [
name,
index,
] )
);
return Math.min(
...pattern.categories.map( ( category ) =>
indexedCategories[ category ] !== undefined
? indexedCategories[ category ]
...pattern.categories.map( ( cat ) =>
indexedCategories[ cat ] !== undefined
? indexedCategories[ cat ]
: Infinity
)
);
},
[ categories ]
[ populatedCategories ]
);

// Ordering the patterns per category is important for the async rendering.
const currentCategoryPatterns = useMemo(
() =>
allPatterns.filter( ( pattern ) =>
patternCategory.name === 'uncategorized'
? getPatternIndex( pattern ) === Infinity
: pattern.categories &&
pattern.categories.includes( patternCategory.name )
),
[ allPatterns, patternCategory ]
);

// Ordering the patterns is important for the async rendering.
const orderedPatterns = useMemo( () => {
return patterns.sort( ( a, b ) => {
return currentCategoryPatterns.sort( ( a, b ) => {
return getPatternIndex( a ) - getPatternIndex( b );
} );
}, [ patterns, getPatternIndex ] );
}, [ currentCategoryPatterns, getPatternIndex ] );

const currentShownPatterns = useAsyncList( orderedPatterns );

// Uncategorized Patterns
const uncategorizedPatterns = useMemo(
() =>
patterns.filter(
( pattern ) => getPatternIndex( pattern ) === Infinity
),
[ patterns, getPatternIndex ]
);

return (
<>
{ categories.map( ( patternCategory ) => {
const categoryPatterns = patterns.filter(
( pattern ) =>
pattern.categories &&
pattern.categories.includes( patternCategory.name )
);
return (
!! categoryPatterns.length && (
<InserterPanel
key={ patternCategory.name }
title={ patternCategory.label }
>
<BlockPatternList
shownPatterns={ currentShownPatterns }
blockPatterns={ categoryPatterns }
onClickPattern={ onClick }
/>
</InserterPanel>
)
);
} ) }

{ !! uncategorizedPatterns.length && (
<InserterPanel title={ _x( 'Uncategorized' ) }>
{ !! currentCategoryPatterns.length && (
<PatternInserterPanel
key={ patternCategory.name }
title={ patternCategory.title }
selectedCategory={ patternCategory }
patternCategories={ populatedCategories }
onClickCategory={ onClickCategory }
>
<BlockPatternList
shownPatterns={ currentShownPatterns }
blockPatterns={ uncategorizedPatterns }
blockPatterns={ currentCategoryPatterns }
onClickPattern={ onClick }
/>
</InserterPanel>
</PatternInserterPanel>
) }
</>
);
}

function BlockPatternsTabs( { onInsert, filterValue } ) {
function BlockPatternsTabs( {
onInsert,
onClickCategory,
filterValue,
selectedCategory,
} ) {
return filterValue ? (
<BlockPatternsSearchResults
onInsert={ onInsert }
filterValue={ filterValue }
/>
) : (
<BlockPatternsPerCategories onInsert={ onInsert } />
<BlockPatternsCategory
selectedCategory={ selectedCategory }
onInsert={ onInsert }
onClickCategory={ onClickCategory }
/>
);
}

Expand Down
10 changes: 10 additions & 0 deletions packages/block-editor/src/components/inserter/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ function InserterMenu( {
const [ activeTab, setActiveTab ] = useState( 'blocks' );
const [ filterValue, setFilterValue ] = useState( '' );
const [ hoveredItem, setHoveredItem ] = useState( null );
const [ selectedPatternCategory, setSelectedPatternCategory ] = useState(
null
);

const [
destinationRootClientId,
onInsertBlocks,
Expand Down Expand Up @@ -83,6 +87,10 @@ function InserterMenu( {
setHoveredItem( item );
};

const onClickPatternCategory = ( patternCategory ) => {
setSelectedPatternCategory( patternCategory );
};

const blocksTab = (
<>
<div className="block-editor-inserter__block-list">
Expand All @@ -109,6 +117,8 @@ function InserterMenu( {
<BlockPatternsTabs
onInsert={ onInsertPattern }
filterValue={ filterValue }
onClickCategory={ onClickPatternCategory }
selectedCategory={ selectedPatternCategory }
/>
);

Expand Down
65 changes: 65 additions & 0 deletions packages/block-editor/src/components/inserter/pattern-panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

function PatternInserterPanel( {
selectedCategory,
patternCategories,
onClickCategory,
children,
} ) {
const categoryOptions = () => {
const options = [];

patternCategories.map( ( patternCategory ) => {
return options.push( {
value: patternCategory.name,
label: patternCategory.label,
} );
} );

return options;
};

const onChangeSelect = ( selected ) => {
onClickCategory(
patternCategories.find(
( patternCategory ) => selected === patternCategory.name
)
);
};

const getPanelHeaderClassName = () => {
return classnames(
'block-editor-inserter__panel-header',
'block-editor-inserter__panel-header-patterns'
);
};

return (
<>
<div className={ getPanelHeaderClassName() }>
<SelectControl
className="block-editor-inserter__panel-dropdown"
label={ __( 'Filter patterns' ) }
hideLabelFromVision
value={ selectedCategory.name }
onChange={ onChangeSelect }
options={ categoryOptions() }
/>
</div>
<div className="block-editor-inserter__panel-content">
{ children }
</div>
</>
);
}

export default PatternInserterPanel;
16 changes: 15 additions & 1 deletion packages/block-editor/src/components/inserter/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,32 @@ $block-inserter-tabs-height: 44px;
padding: $grid-unit-20 $grid-unit-20 0;
}

.block-editor-inserter__panel-header-patterns {
padding: $grid-unit-20 $grid-unit-20 0 $grid-unit-10;
}

.block-editor-inserter__panel-content {
padding: $grid-unit-20;
}

.block-editor-inserter__panel-title {
.block-editor-inserter__panel-title,
.block-editor-inserter__panel-title button,
.components-custom-select-control__menu li {
margin: 0 $grid-unit-15 0 0;
color: $gray-700;
text-transform: uppercase;
font-size: 11px;
font-weight: 500;
}

.block-editor-inserter__panel-dropdown select.components-select-control__input.components-select-control__input.components-select-control__input {
line-height: 1.2;
}

.block-editor-inserter__panel-dropdown select {
border: none;
}

.block-editor-inserter__block-list {
flex-grow: 1;
position: relative;
Expand Down

0 comments on commit a2419d0

Please sign in to comment.