diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss
index 7987e1b4b5b4dc..f999d3bb01dec0 100644
--- a/packages/base-styles/_z-index.scss
+++ b/packages/base-styles/_z-index.scss
@@ -189,6 +189,8 @@ $z-layers: (
".edit-site-layout__hub": 3,
".edit-site-layout__header": 2,
".edit-site-page-header": 2,
+ ".edit-site-patterns__header": 2,
+ ".edit-site-patterns__grid-pagination": 2,
".edit-site-layout__canvas-container": 2,
".edit-site-layout__sidebar": 1,
".edit-site-layout__canvas-container.is-resizing::after": 100,
diff --git a/packages/edit-site/src/components/page-patterns/grid.js b/packages/edit-site/src/components/page-patterns/grid.js
index 47bcdc8a1f768e..32fa6ebe55b39d 100644
--- a/packages/edit-site/src/components/page-patterns/grid.js
+++ b/packages/edit-site/src/components/page-patterns/grid.js
@@ -1,137 +1,22 @@
-/**
- * WordPress dependencies
- */
-import {
- __experimentalHStack as HStack,
- __experimentalText as Text,
- Button,
-} from '@wordpress/components';
-import { useRef, useState, useMemo } from '@wordpress/element';
-import { __, _x, _n, sprintf } from '@wordpress/i18n';
-import { useAsyncList } from '@wordpress/compose';
-
/**
* Internal dependencies
*/
import GridItem from './grid-item';
-const PAGE_SIZE = 20;
-
-function Pagination( { currentPage, numPages, changePage, totalItems } ) {
- return (
-
-
- {
- // translators: %s: Total number of patterns.
- sprintf(
- // translators: %s: Total number of patterns.
- _n( '%s item', '%s items', totalItems ),
- totalItems
- )
- }
-
-
-
-
-
-
- { sprintf(
- // translators: %1$s: Current page number, %2$s: Total number of pages.
- _x( '%1$s of %2$s', 'paging' ),
- currentPage,
- numPages
- ) }
-
-
-
-
-
-
- );
-}
-
export default function Grid( { categoryId, items, ...props } ) {
- const [ currentPage, setCurrentPage ] = useState( 1 );
- const gridRef = useRef();
- const totalItems = items.length;
- const pageIndex = currentPage - 1;
-
- const list = useMemo(
- () =>
- items.slice(
- pageIndex * PAGE_SIZE,
- pageIndex * PAGE_SIZE + PAGE_SIZE
- ),
- [ pageIndex, items ]
- );
-
- const asyncList = useAsyncList( list, { step: 10 } );
-
- if ( ! list?.length ) {
+ if ( ! items?.length ) {
return null;
}
- const numPages = Math.ceil( items.length / PAGE_SIZE );
- const changePage = ( page ) => {
- const scrollContainer = document.querySelector( '.edit-site-patterns' );
- scrollContainer?.scrollTo( 0, 0 );
-
- setCurrentPage( page );
- };
-
return (
- <>
-
- { asyncList.map( ( item ) => (
-
- ) ) }
-
- { numPages > 1 && (
-
+ { items.map( ( item ) => (
+
- ) }
- >
+ ) ) }
+
);
}
diff --git a/packages/edit-site/src/components/page-patterns/pagination.js b/packages/edit-site/src/components/page-patterns/pagination.js
new file mode 100644
index 00000000000000..702e24cd31b7f0
--- /dev/null
+++ b/packages/edit-site/src/components/page-patterns/pagination.js
@@ -0,0 +1,80 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ __experimentalHStack as HStack,
+ __experimentalText as Text,
+ Button,
+} from '@wordpress/components';
+import { __, _x, _n, sprintf } from '@wordpress/i18n';
+
+export default function Pagination( {
+ currentPage,
+ numPages,
+ changePage,
+ totalItems,
+} ) {
+ return (
+
+
+ {
+ // translators: %s: Total number of patterns.
+ sprintf(
+ // translators: %s: Total number of patterns.
+ _n( '%s item', '%s items', totalItems ),
+ totalItems
+ )
+ }
+
+
+
+
+
+
+ { sprintf(
+ // translators: %1$s: Current page number, %2$s: Total number of pages.
+ _x( '%1$s of %2$s', 'paging' ),
+ currentPage,
+ numPages
+ ) }
+
+
+
+
+
+
+ );
+}
diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js
index 69bbcff7723002..406999bee09c35 100644
--- a/packages/edit-site/src/components/page-patterns/patterns-list.js
+++ b/packages/edit-site/src/components/page-patterns/patterns-list.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { useState, useDeferredValue, useId } from '@wordpress/element';
+import { useState, useDeferredValue, useId, useMemo } from '@wordpress/element';
import {
SearchControl,
__experimentalVStack as VStack,
@@ -15,7 +15,7 @@ import {
import { __, isRTL } from '@wordpress/i18n';
import { chevronLeft, chevronRight } from '@wordpress/icons';
import { privateApis as routerPrivateApis } from '@wordpress/router';
-import { useViewportMatch } from '@wordpress/compose';
+import { useAsyncList, useViewportMatch } from '@wordpress/compose';
/**
* Internal dependencies
@@ -28,6 +28,7 @@ import SidebarButton from '../sidebar-button';
import useDebouncedInput from '../../utils/use-debounced-input';
import { unlock } from '../../lock-unlock';
import { SYNC_TYPES, USER_PATTERN_CATEGORY } from './utils';
+import Pagination from './pagination';
const { useLocation, useHistory } = unlock( routerPrivateApis );
@@ -47,6 +48,8 @@ const SYNC_DESCRIPTIONS = {
),
};
+const PAGE_SIZE = 20;
+
export default function PatternsList( { categoryId, type } ) {
const location = useLocation();
const history = useHistory();
@@ -56,6 +59,8 @@ export default function PatternsList( { categoryId, type } ) {
const deferredFilterValue = useDeferredValue( delayedFilterValue );
const [ syncFilter, setSyncFilter ] = useState( 'all' );
+ const [ currentPage, setCurrentPage ] = useState( 1 );
+
const deferredSyncedFilter = useDeferredValue( syncFilter );
const { patterns, isResolving } = usePatterns(
@@ -78,85 +83,124 @@ export default function PatternsList( { categoryId, type } ) {
const title = SYNC_FILTERS[ syncFilter ];
const description = SYNC_DESCRIPTIONS[ syncFilter ];
+ const totalItems = patterns.length;
+ const pageIndex = currentPage - 1;
+ const numPages = Math.ceil( patterns.length / PAGE_SIZE );
+
+ const list = useMemo(
+ () =>
+ patterns.slice(
+ pageIndex * PAGE_SIZE,
+ pageIndex * PAGE_SIZE + PAGE_SIZE
+ ),
+ [ pageIndex, patterns ]
+ );
+
+ const asyncList = useAsyncList( list, { step: 10 } );
+
+ const changePage = ( page ) => {
+ const scrollContainer = document.querySelector( '.edit-site-patterns' );
+ scrollContainer?.scrollTo( 0, 0 );
+
+ setCurrentPage( page );
+ };
+
return (
-
-
-
-
- { isMobileViewport && (
- {
- // Go back in history if we came from the Patterns page.
- // Otherwise push a stack onto the history.
- if ( location.state?.backPath === '/patterns' ) {
- history.back();
- } else {
- history.push( { path: '/patterns' } );
- }
- } }
- />
+ <>
+
+
+
+ { isMobileViewport && (
+ {
+ // Go back in history if we came from the Patterns page.
+ // Otherwise push a stack onto the history.
+ if (
+ location.state?.backPath === '/patterns'
+ ) {
+ history.back();
+ } else {
+ history.push( { path: '/patterns' } );
+ }
+ } }
+ />
+ ) }
+
+ setFilterValue( value ) }
+ placeholder={ __( 'Search patterns' ) }
+ label={ __( 'Search patterns' ) }
+ value={ filterValue }
+ __nextHasNoMarginBottom
+ />
+
+ { categoryId === USER_PATTERN_CATEGORY && (
+ setSyncFilter( value ) }
+ __nextHasNoMarginBottom
+ >
+ { Object.entries( SYNC_FILTERS ).map(
+ ( [ key, label ] ) => (
+
+ )
+ ) }
+
+ ) }
+
+
+
+ { syncFilter !== 'all' && (
+
+
+ { title }
+
+ { description ? (
+
+ { description }
+
+ ) : null }
+
) }
-
- setFilterValue( value ) }
- placeholder={ __( 'Search patterns' ) }
- label={ __( 'Search patterns' ) }
- value={ filterValue }
- __nextHasNoMarginBottom
+ { hasPatterns && (
+
-
- { categoryId === USER_PATTERN_CATEGORY && (
- setSyncFilter( value ) }
- __nextHasNoMarginBottom
- >
- { Object.entries( SYNC_FILTERS ).map(
- ( [ key, label ] ) => (
-
- )
- ) }
-
) }
-
- { syncFilter !== 'all' && (
-
-
- { title }
-
- { description ? (
-
- { description }
-
- ) : null }
-
- ) }
- { hasPatterns && (
- }
+
+ { numPages > 1 && (
+
) }
- { ! isResolving && ! hasPatterns && }
-
+ >
);
}
diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss
index d1fbedb141f701..ae35bcb86137b0 100644
--- a/packages/edit-site/src/components/page-patterns/style.scss
+++ b/packages/edit-site/src/components/page-patterns/style.scss
@@ -1,8 +1,19 @@
.edit-site-patterns {
- border: 1px solid $gray-800;
+ border-left: 1px solid $gray-800;
background: none;
margin: $header-height 0 0;
border-radius: 0;
+ padding: 0;
+ overflow-x: auto;
+
+ .edit-site-page-content {
+ height: 100%;
+ position: relative;
+ padding: 0;
+ display: flex;
+ flex-flow: column;
+ }
+
.components-base-control {
width: 100%;
@include break-medium {
@@ -63,16 +74,25 @@
}
.edit-site-patterns__grid-pagination {
- width: fit-content;
+ border-top: 1px solid $gray-800;
+ background: $gray-900;
+ padding: $grid-unit-30 $grid-unit-40;
+ position: sticky;
+ bottom: 0;
+ z-index: z-index(".edit-site-patterns__grid-pagination");
+
.components-button.is-tertiary {
width: $button-size-compact;
height: $button-size-compact;
color: $gray-100;
background-color: $gray-800;
+ justify-content: center;
+
&:disabled {
color: $gray-600;
background: none;
}
+
&:hover:not(:disabled) {
background-color: $gray-700;
}
@@ -80,6 +100,19 @@
}
}
+.edit-site-patterns__header {
+ position: sticky;
+ top: 0;
+ background: $gray-900;
+ padding: $grid-unit-40 $grid-unit-40 $grid-unit-20;
+ z-index: z-index(".edit-site-patterns__header");
+}
+
+.edit-site-patterns__section {
+ padding: $grid-unit-30 $grid-unit-40;
+ flex: 1;
+}
+
.edit-site-patterns__section-header {
.screen-reader-shortcut:focus {
top: 0;
@@ -90,11 +123,8 @@
display: grid;
grid-template-columns: 1fr;
gap: $grid-unit-40;
- // Small top padding required to avoid cutting off the visible outline
- // when hovering items.
- padding-top: $border-width-focus-fallback;
margin-top: 0;
- margin-bottom: $grid-unit-40;
+ margin-bottom: 0;
@include break-large {
grid-template-columns: 1fr 1fr;
}