diff --git a/packages/block-editor/src/components/block-patterns-list/index.js b/packages/block-editor/src/components/block-patterns-list/index.js index 302fe731d2187..3ac0cc7018b3d 100644 --- a/packages/block-editor/src/components/block-patterns-list/index.js +++ b/packages/block-editor/src/components/block-patterns-list/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useState } from '@wordpress/element'; +import { useState, forwardRef } from '@wordpress/element'; import { VisuallyHidden, __unstableComposite as Composite, @@ -24,6 +24,7 @@ import { Icon, symbol } from '@wordpress/icons'; */ import BlockPreview from '../block-preview'; import InserterDraggableBlocks from '../inserter-draggable-blocks'; +import BlockPatternsPaging from '../block-patterns-paging'; const WithToolTip = ( { showTooltip, title, children } ) => { if ( showTooltip ) { @@ -140,16 +141,20 @@ function BlockPatternPlaceholder() { ); } -function BlockPatternList( { - isDraggable, - blockPatterns, - shownPatterns, - onHover, - onClickPattern, - orientation, - label = __( 'Block Patterns' ), - showTitlesAsTooltip, -} ) { +function BlockPatternList( + { + isDraggable, + blockPatterns, + shownPatterns, + onHover, + onClickPattern, + orientation, + label = __( 'Block Patterns' ), + showTitlesAsTooltip, + pagingProps, + }, + ref +) { const composite = useCompositeState( { orientation } ); return ( { blockPatterns.map( ( pattern ) => { const isShown = shownPatterns.includes( pattern ); @@ -174,8 +180,11 @@ function BlockPatternList( { ); } ) } + { pagingProps && pagingProps.numPages > 1 && ( + + ) } ); } -export default BlockPatternList; +export default forwardRef( BlockPatternList ); diff --git a/packages/block-editor/src/components/block-patterns-paging/index.js b/packages/block-editor/src/components/block-patterns-paging/index.js index 610ff304e186e..8900ac3808262 100644 --- a/packages/block-editor/src/components/block-patterns-paging/index.js +++ b/packages/block-editor/src/components/block-patterns-paging/index.js @@ -16,7 +16,7 @@ export default function Pagination( { totalItems, } ) { return ( - + { // translators: %s: Total number of patterns. diff --git a/packages/block-editor/src/components/block-patterns-paging/style.scss b/packages/block-editor/src/components/block-patterns-paging/style.scss index 7de651f1511b6..ce57f96cd327a 100644 --- a/packages/block-editor/src/components/block-patterns-paging/style.scss +++ b/packages/block-editor/src/components/block-patterns-paging/style.scss @@ -1,20 +1,23 @@ -.block-editor-patterns__grid-pagination { - border-top: 1px solid $gray-800; - padding: $grid-unit-05; +.block-editor-patterns__grid-pagination-wrapper { - .components-button.is-tertiary { - width: auto; - height: $button-size-compact; + .block-editor-patterns__grid-pagination { + border-top: 1px solid $gray-800; + padding: $grid-unit-05; justify-content: center; + .components-button.is-tertiary { + width: auto; + height: $button-size-compact; + justify-content: center; - &:disabled { - color: $gray-600; - background: none; - } + &:disabled { + color: $gray-600; + background: none; + } - &:hover:not(:disabled) { - color: $white; - background-color: $gray-700; + &:hover:not(:disabled) { + color: $white; + background-color: $gray-700; + } } } } diff --git a/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js b/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js index 32f52dd5c3818..84cb80f5d5971 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js +++ b/packages/block-editor/src/components/inserter/block-patterns-explorer/patterns-list.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useMemo, useEffect, useRef, useState } from '@wordpress/element'; +import { useMemo, useEffect, useRef } from '@wordpress/element'; import { _n, sprintf } from '@wordpress/i18n'; import { useDebounce } from '@wordpress/compose'; import { __experimentalHeading as Heading } from '@wordpress/components'; @@ -17,31 +17,13 @@ import InserterListbox from '../../inserter-listbox'; import { searchItems } from '../search-items'; import BlockPatternsPaging from '../../block-patterns-paging'; import usePatternsPaging from '../hooks/use-patterns-paging'; -import { allPatternsCategory, isPatternFiltered } from '../block-patterns-tab'; -import { BlockPatternsSyncFilter } from '../block-patterns-sync-filter'; -import { - PATTERN_TYPES, - PATTERN_SOURCE_FILTERS, -} from '../block-patterns-source-filter'; - -function PatternsListHeader( { - filterValue, - filteredBlockPatternsLength, - selectedCategory, - patternCategories, -} ) { +import { allPatternsCategory } from '../block-patterns-tab'; + +function PatternsListHeader( { filterValue, filteredBlockPatternsLength } ) { if ( ! filterValue ) { return null; } - let filter = filterValue; - if ( selectedCategory !== allPatternsCategory.name ) { - const category = patternCategories.find( - ( patternCategory ) => patternCategory.name === selectedCategory - ); - if ( category ) { - filter = `${ filter } - ${ category?.label }`; - } - } + return ( { sprintf( - /* translators: %d: number of patterns. %s: block pattern search query */ + /* translators: %d: number of patterns. */ _n( - '%1$d pattern found for "%2$s"', - '%1$d patterns found for "%2$s"', + '%d pattern found', + '%d patterns found', filteredBlockPatternsLength ), - filteredBlockPatternsLength, - filter + filteredBlockPatternsLength ) } ); } -function PatternList( { - searchValue, - patternSourceFilter, - selectedCategory, - patternCategories, -} ) { - const [ patternSyncFilter, setPatternSyncFilter ] = useState( 'all' ); +function PatternList( { searchValue, selectedCategory, patternCategories } ) { const container = useRef(); const debouncedSpeak = useDebounce( speak, 500 ); const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( { @@ -89,16 +64,6 @@ function PatternList( { const filteredBlockPatterns = useMemo( () => { const filteredPatterns = patterns.filter( ( pattern ) => { - if ( - isPatternFiltered( - pattern, - patternSourceFilter, - patternSyncFilter - ) - ) { - return false; - } - if ( selectedCategory === allPatternsCategory.name ) { return true; } @@ -119,18 +84,12 @@ function PatternList( { return filteredPatterns; } - return searchItems( - filteredPatterns, - searchValue, - patternSourceFilter - ); + return searchItems( filteredPatterns, searchValue ); }, [ searchValue, - patternSourceFilter, patterns, selectedCategory, registeredPatternCategories, - patternSyncFilter, ] ); // Announce search results on change. @@ -150,8 +109,7 @@ function PatternList( { const pagingProps = usePatternsPaging( filteredBlockPatterns, selectedCategory, - container, - patternSourceFilter + container ); const hasItems = !! filteredBlockPatterns?.length; @@ -161,23 +119,11 @@ function PatternList( { ref={ container } > - { patternSourceFilter === PATTERN_TYPES.user && - ! searchValue && ( - - ) } - { hasItems && ( - { - setPatternSourceFilter( value ); - onClickCategory( allPatternsCategory.name ); - } } - /> { ! searchValue && ( + sourceFilter !== PATTERN_TYPES.all && sourceFilter !== PATTERN_TYPES.user; + +export function BlockPatternsSyncFilter( { + setPatternSyncFilter, + setPatternSourceFilter, + patternSyncFilter, + patternSourceFilter, + scrollContainerRef, +} ) { + // We need to disable the sync filter option if the source filter is not 'all' or 'user' + // otherwise applying them will just result in no patterns being shown. + const shouldDisableSyncFilter = + getShouldDisableSyncFilter( patternSourceFilter ); + + const patternSyncMenuOptions = useMemo( + () => [ + { value: SYNC_TYPES.all, label: __( 'All' ) }, + { + value: SYNC_TYPES.full, + label: __( 'Synced' ), + info: __( 'Updated everywhere' ), + disabled: shouldDisableSyncFilter, + }, + { + value: SYNC_TYPES.unsynced, + label: __( 'Standard' ), + info: __( 'Edit freely' ), + disabled: shouldDisableSyncFilter, + }, + ], + [ shouldDisableSyncFilter ] + ); + + function handleSetSourceFilterChange( newSourceFilter ) { + setPatternSourceFilter( newSourceFilter ); + if ( getShouldDisableSyncFilter( newSourceFilter ) ) { + setPatternSyncFilter( SYNC_TYPES.all ); + } + } + + return ( + <> + + + + } + /> + } + > + { () => ( + <> + + { + handleSetSourceFilterChange( value ); + scrollContainerRef.current?.scrollTo( + 0, + 0 + ); + } } + value={ patternSourceFilter } + /> + + + { + setPatternSyncFilter( value ); + scrollContainerRef.current?.scrollTo( + 0, + 0 + ); + } } + value={ patternSyncFilter } + /> + + + ) } + + + ); +} diff --git a/packages/block-editor/src/components/inserter/block-patterns-source-filter.js b/packages/block-editor/src/components/inserter/block-patterns-source-filter.js deleted file mode 100644 index 35f48e0ee96a1..0000000000000 --- a/packages/block-editor/src/components/inserter/block-patterns-source-filter.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { SelectControl } from '@wordpress/components'; - -export const PATTERN_TYPES = { - synced: 'synced', - unsynced: 'unsynced', - user: 'user', - theme: 'theme', -}; - -const patternSourceOptions = [ - { value: 'all', label: __( 'All pattern types' ) }, - { value: PATTERN_TYPES.theme, label: __( 'Theme patterns' ) }, - { value: PATTERN_TYPES.user, label: __( 'My patterns' ) }, -]; - -export const PATTERN_SOURCE_FILTERS = patternSourceOptions.reduce( - ( patternSourceFilters, { value, label } ) => { - patternSourceFilters[ value ] = label; - return patternSourceFilters; - }, - {} -); - -export default function BlockPatternsSourceFilter( { onChange, value } ) { - return ( - - ); -} diff --git a/packages/block-editor/src/components/inserter/block-patterns-sync-filter.js b/packages/block-editor/src/components/inserter/block-patterns-sync-filter.js deleted file mode 100644 index 456c8bde27045..0000000000000 --- a/packages/block-editor/src/components/inserter/block-patterns-sync-filter.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * WordPress dependencies - */ -import { SelectControl } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -export const SYNC_TYPES = { - full: 'fully', - unsynced: 'unsynced', -}; - -const patternSyncOptions = [ - { value: 'all', label: __( 'All' ) }, - { value: SYNC_TYPES.full, label: __( 'Synced' ) }, - { value: SYNC_TYPES.unsynced, label: __( 'Standard' ) }, -]; - -export function BlockPatternsSyncFilter( { - setPatternSyncFilter, - patternSyncFilter, -} ) { - return ( - - ); -} diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js index 37192df28a23b..926a9fa9203d3 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -14,6 +14,9 @@ import { __experimentalItemGroup as ItemGroup, __experimentalItem as Item, __experimentalHStack as HStack, + __experimentalVStack as VStack, + __experimentalHeading as Heading, + __experimentalText as Text, FlexBlock, Button, } from '@wordpress/components'; @@ -28,48 +31,63 @@ import usePatternsState from './hooks/use-patterns-state'; import BlockPatternList from '../block-patterns-list'; import PatternsExplorerModal from './block-patterns-explorer/explorer'; import MobileTabNavigation from './mobile-tab-navigation'; -import BlockPatternsPaging from '../block-patterns-paging'; import usePatternsPaging from './hooks/use-patterns-paging'; -import { - PATTERN_TYPES, - default as BlockPatternsSourceFilter, -} from './block-patterns-source-filter'; import { BlockPatternsSyncFilter, SYNC_TYPES, -} from './block-patterns-sync-filter'; + PATTERN_TYPES, +} from './block-patterns-filter'; const noop = () => {}; export const allPatternsCategory = { name: 'allPatterns', - label: __( 'All categories' ), + label: __( 'All Patterns' ), }; export function isPatternFiltered( pattern, sourceFilter, syncFilter ) { + const isUserPattern = pattern.name.startsWith( 'core/block' ); + const isDirectoryPattern = + pattern.source === 'core' || + pattern.source?.startsWith( 'pattern-directory' ); + + // If theme source selected, filter out user created patterns and those from + // the core patterns directory. if ( sourceFilter === PATTERN_TYPES.theme && - pattern.name.startsWith( 'core/block' ) + ( isUserPattern || isDirectoryPattern ) + ) { + return true; + } + + // If the directory source is selected, filter out user created patterns + // and those bundled with the theme. + if ( + sourceFilter === PATTERN_TYPES.directory && + ( isUserPattern || ! isDirectoryPattern ) ) { return true; } + + // If user source selected, filter out theme patterns. Any pattern without + // an id wasn't created by a user. if ( sourceFilter === PATTERN_TYPES.user && ! pattern.id ) { return true; } - if ( - sourceFilter === PATTERN_TYPES.user && - syncFilter === SYNC_TYPES.full && - pattern.syncStatus !== '' - ) { + + // Filter by sync status. + if ( syncFilter === SYNC_TYPES.full && pattern.syncStatus !== '' ) { return true; } + if ( - sourceFilter === PATTERN_TYPES.user && syncFilter === SYNC_TYPES.unsynced && - pattern.syncStatus !== 'unsynced' + pattern.syncStatus !== 'unsynced' && + isUserPattern ) { return true; } + return false; } @@ -190,26 +208,26 @@ export function BlockPatternsCategoryPanel( { onHover = noop, category, showTitlesAsTooltip, - patternFilter, } ) { const [ allPatterns, , onClickPattern ] = usePatternsState( onInsert, rootClientId ); const [ patternSyncFilter, setPatternSyncFilter ] = useState( 'all' ); + const [ patternSourceFilter, setPatternSourceFilter ] = useState( 'all' ); const availableCategories = usePatternsCategories( rootClientId, - patternFilter + patternSourceFilter ); - const container = useRef(); + const scrollContainerRef = useRef(); const currentCategoryPatterns = useMemo( () => allPatterns.filter( ( pattern ) => { if ( isPatternFiltered( pattern, - patternFilter, + patternSourceFilter, patternSyncFilter ) ) { @@ -239,7 +257,7 @@ export function BlockPatternsCategoryPanel( { allPatterns, availableCategories, category.name, - patternFilter, + patternSourceFilter, patternSyncFilter, ] ); @@ -247,7 +265,7 @@ export function BlockPatternsCategoryPanel( { const pagingProps = usePatternsPaging( currentCategoryPatterns, category, - container + scrollContainerRef ); // Hide block pattern preview on unmount. @@ -255,25 +273,41 @@ export function BlockPatternsCategoryPanel( { useEffect( () => () => onHover( null ), [] ); return ( -
-
- { category.label } -
-

{ category.description }

- { patternFilter === PATTERN_TYPES.user && ( - - ) } - { ! currentCategoryPatterns.length && ( -
{ __( 'No results found' ) }
- ) } +
+ + + + + { category.label } + + + + + { category.description && ( + { category.description } + ) } + { ! currentCategoryPatterns.length && ( + + { __( 'No results found' ) } + + ) } + + { currentCategoryPatterns.length > 0 && ( ) } - { pagingProps.numPages > 1 && ( - - ) }
); } @@ -300,12 +332,8 @@ function BlockPatternsTabs( { rootClientId, } ) { const [ showPatternsExplorer, setShowPatternsExplorer ] = useState( false ); - const [ patternSourceFilter, setPatternSourceFilter ] = useState( 'all' ); - const categories = usePatternsCategories( - rootClientId, - patternSourceFilter - ); + const categories = usePatternsCategories( rootClientId ); const initialCategory = selectedCategory || categories[ 0 ]; const isMobile = useViewportMatch( 'medium', '<' ); @@ -317,23 +345,13 @@ function BlockPatternsTabs( { aria-label={ __( 'Block pattern categories' ) } className="block-editor-inserter__block-patterns-tabs" > - { - setPatternSourceFilter( value ); - onSelectCategory( allPatternsCategory, value ); - } } - /> { categories.map( ( category ) => ( - onSelectCategory( - category, - patternSourceFilter - ) + onSelectCategory( category ) } className={ category === selectedCategory diff --git a/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js b/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js index 02df0f2ca053e..931796acceeb0 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js +++ b/packages/block-editor/src/components/inserter/hooks/use-patterns-paging.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useMemo, useState } from '@wordpress/element'; +import { useMemo, useState, useEffect } from '@wordpress/element'; import { useAsyncList, usePrevious } from '@wordpress/compose'; import { getScrollContainer } from '@wordpress/dom'; @@ -54,6 +54,17 @@ export default function usePatternsPaging( setCurrentPage( page ); }; + + useEffect( + function scrollToTopOnCategoryChange() { + const scrollContainer = getScrollContainer( + scrollContainerRef?.current + ); + scrollContainer?.scrollTo( 0, 0 ); + }, + [ currentCategory, scrollContainerRef ] + ); + return { totalItems, categoryPatterns, diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index 756a5f7000a04..2528139e8f30f 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -299,13 +299,10 @@ $block-inserter-tabs-height: 44px; border-left: $border-width solid $gray-200; border-right: $border-width solid $gray-200; position: absolute; - padding: $grid-unit-40 $grid-unit-30; top: 0; left: 0; height: 100%; width: 100%; - overflow-y: auto; - scrollbar-gutter: stable both-edges; @include break-medium { left: 100%; @@ -314,7 +311,10 @@ $block-inserter-tabs-height: 44px; } .block-editor-block-patterns-list { - margin-top: $grid-unit-30; + overflow-y: auto; + flex-grow: 1; + height: 100%; + padding: $grid-unit-40 $grid-unit-30; } } @@ -331,10 +331,18 @@ $block-inserter-tabs-height: 44px; .block-editor-inserter__patterns-category-panel { padding: 0 $grid-unit-20; - + display: flex; + flex-direction: column; + height: 100%; @include break-medium { padding: 0; } + .block-editor-inserter__patterns-category-panel-header { + padding: 16px $grid-unit-30; + } + .block-editor-inserter__patterns-category-no-results { + margin-top: $grid-unit-30; + } } .block-editor-inserter__preview-content {