Skip to content

Commit

Permalink
[Patterns]: Browse Patterns in a Modal (#35773)
Browse files Browse the repository at this point in the history
* [Patterns]: Paterns explorer in modal

* add search results header info

* Update packages/block-editor/src/components/inserter/block-patterns-explorer/search-results.js

Co-authored-by: Miguel Fonseca <[email protected]>

* fix lint error

* small design changes

* Refactor patterns tab/explorer for performance

* Moving hover and focus styles to the preview container.

* Specifying a gap for the Grid component.

* Updating the button label to Explore and using the outlined style of button.

* Visual tweaks to the inserter modal

- No need for custom styles on the explore button.
- No need to separate the Featured tab on its own.
- Switching from 3 to 2 columns for the list.
- Increased the max-height and ensured no scrolling within the preview container.

* adjust min-height in quick inserter

* remove commented line

* Adjusting min/max height for previews in the modal and changing columns at breakpoints.

* More visual tweaks to the patterns list.

- Moving the margin to the bottom and increasing it to give the list some room to breathe.
- Removing an unnecessary position, background, and transition.
- Adjusting the title's padding to only apply to the top and adding a hover state.

* Increasing the space between the dropdown and the button to match the other spacing in the sidebar.

* Using the Heading component and adding a className to the list container div.

* Absolute positioning the explorer sidebar to allow for independent scrolling.

* hide explore button when inserter goes full width + small css fix for pattern item in quick inserter results

* address feedback

Co-authored-by: Miguel Fonseca <[email protected]>
Co-authored-by: Shaun Andrews <[email protected]>
  • Loading branch information
3 people authored and andrewserong committed Nov 7, 2021
1 parent 0914f2f commit 8c598f4
Show file tree
Hide file tree
Showing 11 changed files with 478 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,15 @@ import {
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import BlockPreview from '../block-preview';
import InserterDraggableBlocks from '../inserter-draggable-blocks';
import { store as blockEditorStore } from '../../store';

function BlockPattern( { isDraggable, pattern, onClick, composite } ) {
const { name, viewportWidth } = pattern;
const { blocks } = useSelect(
( select ) =>
select( blockEditorStore ).__experimentalGetParsedPattern( name ),
[ name ]
);
const { blocks, viewportWidth } = pattern;
const instanceId = useInstanceId( BlockPattern );
const descriptionId = `block-editor-block-patterns-list__item-description-${ instanceId }`;

Expand Down
34 changes: 22 additions & 12 deletions packages/block-editor/src/components/block-patterns-list/style.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.block-editor-block-patterns-list__list-item {
cursor: pointer;
margin-top: $grid-unit-20;
margin-bottom: $grid-unit-30;

&.is-placeholder {
min-height: 100px;
Expand All @@ -13,25 +13,35 @@

.block-editor-block-patterns-list__item {
height: 100%;
border-radius: $radius-block-ui;
transition: all 0.05s ease-in-out;
position: relative;
border: $border-width solid transparent;

&:hover {
.block-editor-block-preview__container {
display: flex;
align-items: center;
overflow: hidden;
border-radius: $radius-block-ui;
border: $border-width solid $gray-100;
}

.block-editor-block-patterns-list__item-title {
padding-top: $grid-unit-10;
font-size: 12px;
text-align: center;
}

&:hover .block-editor-block-preview__container {
border: $border-width solid var(--wp-admin-theme-color);
}

&:focus {
&:focus .block-editor-block-preview__container {
box-shadow: inset 0 0 0 1px $white, 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);

// Windows High Contrast mode will show this outline, but not the box-shadow.
outline: 2px solid transparent;
}
}

.block-editor-block-patterns-list__item-title {
padding: $grid-unit-05;
font-size: 12px;
text-align: center;

&:hover .block-editor-block-patterns-list__item-title,
&:focus .block-editor-block-patterns-list__item-title {
color: var(--wp-admin-theme-color);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* WordPress dependencies
*/
import { Modal } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import PatternExplorerSidebar from './sidebar';
import PatternList from './patterns-list';

function PatternsExplorer( { initialCategory, patternCategories } ) {
const [ filterValue, setFilterValue ] = useState( '' );
const [ selectedCategory, setSelectedCategory ] = useState(
initialCategory?.name
);
return (
<div className="block-editor-block-patterns-explorer">
<PatternExplorerSidebar
selectedCategory={ selectedCategory }
patternCategories={ patternCategories }
onClickCategory={ setSelectedCategory }
filterValue={ filterValue }
setFilterValue={ setFilterValue }
/>
<PatternList
filterValue={ filterValue }
selectedCategory={ selectedCategory }
patternCategories={ patternCategories }
/>
</div>
);
}

function PatternsExplorerModal( { onModalClose, ...restProps } ) {
return (
<Modal
title={ __( 'Patterns' ) }
closeLabel={ __( 'Close' ) }
onRequestClose={ onModalClose }
isFullScreen
>
<PatternsExplorer { ...restProps } />
</Modal>
);
}

export default PatternsExplorerModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* WordPress dependencies
*/
import { useMemo, useEffect } from '@wordpress/element';
import { _n, sprintf } from '@wordpress/i18n';
import { useDebounce, useAsyncList } from '@wordpress/compose';
import { __experimentalHeading as Heading } from '@wordpress/components';
import { speak } from '@wordpress/a11y';

/**
* Internal dependencies
*/
import BlockPatternsList from '../../block-patterns-list';
import InserterNoResults from '../no-results';
import useInsertionPoint from '../hooks/use-insertion-point';
import usePatternsState from '../hooks/use-patterns-state';
import InserterListbox from '../../inserter-listbox';
import { searchItems } from '../search-items';

const INITIAL_INSERTER_RESULTS = 2;

function PatternsListHeader( { filterValue, filteredBlockPatternsLength } ) {
if ( ! filterValue ) {
return null;
}
return (
<Heading
level={ 2 }
lineHeight={ '48px' }
className="block-editor-block-patterns-explorer__search-results-count"
>
{ sprintf(
/* translators: %d: number of patterns. %s: block pattern search query */
_n(
'%1$d pattern found for "%2$s"',
'%1$d patterns found for "%2$s"',
filteredBlockPatternsLength
),
filteredBlockPatternsLength,
filterValue
) }
</Heading>
);
}

function PatternList( { filterValue, selectedCategory, patternCategories } ) {
const debouncedSpeak = useDebounce( speak, 500 );
const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( {
shouldFocusBlock: true,
} );
const [ allPatterns, , onSelectBlockPattern ] = usePatternsState(
onInsertBlocks,
destinationRootClientId
);
const registeredPatternCategories = useMemo(
() =>
patternCategories.map(
( patternCategory ) => patternCategory.name
),
[ patternCategories ]
);

const filteredBlockPatterns = useMemo( () => {
if ( ! filterValue ) {
return allPatterns.filter( ( pattern ) =>
selectedCategory === 'uncategorized'
? ! pattern.categories?.length ||
pattern.categories.every(
( category ) =>
! registeredPatternCategories.includes(
category
)
)
: pattern.categories?.includes( selectedCategory )
);
}
return searchItems( allPatterns, filterValue );
}, [ filterValue, selectedCategory, allPatterns ] );

// Announce search results on change.
useEffect( () => {
if ( ! filterValue ) {
return;
}
const count = filteredBlockPatterns.length;
const resultsFoundMessage = sprintf(
/* translators: %d: number of results. */
_n( '%d result found.', '%d results found.', count ),
count
);
debouncedSpeak( resultsFoundMessage );
}, [ filterValue, debouncedSpeak ] );

const currentShownPatterns = useAsyncList( filteredBlockPatterns, {
step: INITIAL_INSERTER_RESULTS,
} );

const hasItems = !! filteredBlockPatterns?.length;
return (
<div className="block-editor-block-patterns-explorer__list">
{ hasItems && (
<PatternsListHeader
filterValue={ filterValue }
filteredBlockPatternsLength={ filteredBlockPatterns.length }
/>
) }
<InserterListbox>
{ ! hasItems && <InserterNoResults /> }
{ hasItems && (
<BlockPatternsList
shownPatterns={ currentShownPatterns }
blockPatterns={ filteredBlockPatterns }
onClickPattern={ onSelectBlockPattern }
isDraggable={ false }
/>
) }
</InserterListbox>
</div>
);
}

export default PatternList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* WordPress dependencies
*/
import { Button, SearchControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

function PatternCategoriesList( {
selectedCategory,
patternCategories,
onClickCategory,
} ) {
const baseClassName = 'block-editor-block-patterns-explorer__sidebar';
return (
<div className={ `${ baseClassName }__categories-list` }>
{ patternCategories.map( ( { name, label } ) => {
return (
<Button
key={ name }
label={ label }
className={ `${ baseClassName }__categories-list__item` }
isPressed={ selectedCategory === name }
onClick={ () => {
onClickCategory( name );
} }
>
{ label }
</Button>
);
} ) }
</div>
);
}

function PatternsExplorerSearch( { filterValue, setFilterValue } ) {
const baseClassName = 'block-editor-block-patterns-explorer__search';
return (
<div className={ baseClassName }>
<SearchControl
onChange={ setFilterValue }
value={ filterValue }
label={ __( 'Search for patterns' ) }
placeholder={ __( 'Search' ) }
/>
</div>
);
}

function PatternExplorerSidebar( {
selectedCategory,
patternCategories,
onClickCategory,
filterValue,
setFilterValue,
} ) {
const baseClassName = 'block-editor-block-patterns-explorer__sidebar';
return (
<div className={ baseClassName }>
<PatternsExplorerSearch
filterValue={ filterValue }
setFilterValue={ setFilterValue }
/>
{ ! filterValue && (
<PatternCategoriesList
selectedCategory={ selectedCategory }
patternCategories={ patternCategories }
onClickCategory={ onClickCategory }
/>
) }
</div>
);
}

export default PatternExplorerSidebar;
Loading

0 comments on commit 8c598f4

Please sign in to comment.