@wordpress/block-editor 12.10.0 → 12.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/build/components/block-patterns-list/index.js +9 -4
  2. package/build/components/block-patterns-list/index.js.map +1 -1
  3. package/build/components/block-patterns-paging/index.js +3 -1
  4. package/build/components/block-patterns-paging/index.js.map +1 -1
  5. package/build/components/inserter/block-patterns-explorer/patterns-list.js +19 -30
  6. package/build/components/inserter/block-patterns-explorer/patterns-list.js.map +1 -1
  7. package/build/components/inserter/block-patterns-explorer/sidebar.js +0 -15
  8. package/build/components/inserter/block-patterns-explorer/sidebar.js.map +1 -1
  9. package/build/components/inserter/block-patterns-filter.js +137 -0
  10. package/build/components/inserter/block-patterns-filter.js.map +1 -0
  11. package/build/components/inserter/block-patterns-tab.js +77 -37
  12. package/build/components/inserter/block-patterns-tab.js.map +1 -1
  13. package/build/components/inserter/hooks/use-patterns-paging.js +4 -0
  14. package/build/components/inserter/hooks/use-patterns-paging.js.map +1 -1
  15. package/build/components/inspector-controls-tabs/styles-tab.js +1 -1
  16. package/build/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  17. package/build/hooks/background.js +28 -23
  18. package/build/hooks/background.js.map +1 -1
  19. package/build/hooks/block-rename-ui.js +2 -1
  20. package/build/hooks/block-rename-ui.js.map +1 -1
  21. package/build/store/private-selectors.js +2 -3
  22. package/build/store/private-selectors.js.map +1 -1
  23. package/build-module/components/block-patterns-list/index.js +10 -5
  24. package/build-module/components/block-patterns-list/index.js.map +1 -1
  25. package/build-module/components/block-patterns-paging/index.js +3 -1
  26. package/build-module/components/block-patterns-paging/index.js.map +1 -1
  27. package/build-module/components/inserter/block-patterns-explorer/patterns-list.js +20 -31
  28. package/build-module/components/inserter/block-patterns-explorer/patterns-list.js.map +1 -1
  29. package/build-module/components/inserter/block-patterns-explorer/sidebar.js +0 -14
  30. package/build-module/components/inserter/block-patterns-explorer/sidebar.js.map +1 -1
  31. package/build-module/components/inserter/block-patterns-filter.js +128 -0
  32. package/build-module/components/inserter/block-patterns-filter.js.map +1 -0
  33. package/build-module/components/inserter/block-patterns-tab.js +75 -35
  34. package/build-module/components/inserter/block-patterns-tab.js.map +1 -1
  35. package/build-module/components/inserter/hooks/use-patterns-paging.js +5 -1
  36. package/build-module/components/inserter/hooks/use-patterns-paging.js.map +1 -1
  37. package/build-module/components/inspector-controls-tabs/styles-tab.js +1 -1
  38. package/build-module/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  39. package/build-module/hooks/background.js +30 -25
  40. package/build-module/hooks/background.js.map +1 -1
  41. package/build-module/hooks/block-rename-ui.js +2 -1
  42. package/build-module/hooks/block-rename-ui.js.map +1 -1
  43. package/build-module/store/private-selectors.js +2 -3
  44. package/build-module/store/private-selectors.js.map +1 -1
  45. package/build-style/style-rtl.css +53 -26
  46. package/build-style/style.css +53 -26
  47. package/package.json +32 -32
  48. package/src/components/block-patterns-list/index.js +21 -12
  49. package/src/components/block-patterns-paging/index.js +1 -1
  50. package/src/components/block-patterns-paging/style.scss +16 -13
  51. package/src/components/inserter/block-patterns-explorer/patterns-list.js +23 -67
  52. package/src/components/inserter/block-patterns-explorer/sidebar.js +0 -15
  53. package/src/components/inserter/block-patterns-filter.js +183 -0
  54. package/src/components/inserter/block-patterns-tab.js +106 -58
  55. package/src/components/inserter/hooks/use-patterns-paging.js +12 -1
  56. package/src/components/inserter/style.scss +13 -5
  57. package/src/components/inspector-controls-tabs/styles-tab.js +1 -1
  58. package/src/hooks/background.js +57 -46
  59. package/src/hooks/background.scss +36 -18
  60. package/src/hooks/block-rename-ui.js +1 -0
  61. package/src/store/private-selectors.js +7 -3
  62. package/build/components/inserter/block-patterns-source-filter.js +0 -54
  63. package/build/components/inserter/block-patterns-source-filter.js.map +0 -1
  64. package/build/components/inserter/block-patterns-sync-filter.js +0 -46
  65. package/build/components/inserter/block-patterns-sync-filter.js.map +0 -1
  66. package/build-module/components/inserter/block-patterns-source-filter.js +0 -44
  67. package/build-module/components/inserter/block-patterns-source-filter.js.map +0 -1
  68. package/build-module/components/inserter/block-patterns-sync-filter.js +0 -38
  69. package/build-module/components/inserter/block-patterns-sync-filter.js.map +0 -1
  70. package/src/components/inserter/block-patterns-source-filter.js +0 -40
  71. package/src/components/inserter/block-patterns-sync-filter.js +0 -35
@@ -17,31 +17,13 @@ import InserterListbox from '../../inserter-listbox';
17
17
  import { searchItems } from '../search-items';
18
18
  import BlockPatternsPaging from '../../block-patterns-paging';
19
19
  import usePatternsPaging from '../hooks/use-patterns-paging';
20
- import { allPatternsCategory, isPatternFiltered } from '../block-patterns-tab';
21
- import { BlockPatternsSyncFilter } from '../block-patterns-sync-filter';
22
- import {
23
- PATTERN_TYPES,
24
- PATTERN_SOURCE_FILTERS,
25
- } from '../block-patterns-source-filter';
26
-
27
- function PatternsListHeader( {
28
- filterValue,
29
- filteredBlockPatternsLength,
30
- selectedCategory,
31
- patternCategories,
32
- } ) {
20
+ import { allPatternsCategory, myPatternsCategory } from '../block-patterns-tab';
21
+
22
+ function PatternsListHeader( { filterValue, filteredBlockPatternsLength } ) {
33
23
  if ( ! filterValue ) {
34
24
  return null;
35
25
  }
36
- let filter = filterValue;
37
- if ( selectedCategory !== allPatternsCategory.name ) {
38
- const category = patternCategories.find(
39
- ( patternCategory ) => patternCategory.name === selectedCategory
40
- );
41
- if ( category ) {
42
- filter = `${ filter } - ${ category?.label }`;
43
- }
44
- }
26
+
45
27
  return (
46
28
  <Heading
47
29
  level={ 2 }
@@ -49,26 +31,19 @@ function PatternsListHeader( {
49
31
  className="block-editor-block-patterns-explorer__search-results-count"
50
32
  >
51
33
  { sprintf(
52
- /* translators: %d: number of patterns. %s: block pattern search query */
34
+ /* translators: %d: number of patterns. */
53
35
  _n(
54
- '%1$d pattern found for "%2$s"',
55
- '%1$d patterns found for "%2$s"',
36
+ '%d pattern found',
37
+ '%d patterns found',
56
38
  filteredBlockPatternsLength
57
39
  ),
58
- filteredBlockPatternsLength,
59
- filter
40
+ filteredBlockPatternsLength
60
41
  ) }
61
42
  </Heading>
62
43
  );
63
44
  }
64
45
 
65
- function PatternList( {
66
- searchValue,
67
- patternSourceFilter,
68
- selectedCategory,
69
- patternCategories,
70
- } ) {
71
- const [ patternSyncFilter, setPatternSyncFilter ] = useState( 'all' );
46
+ function PatternList( { searchValue, selectedCategory, patternCategories } ) {
72
47
  const container = useRef();
73
48
  const debouncedSpeak = useDebounce( speak, 500 );
74
49
  const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( {
@@ -89,20 +64,12 @@ function PatternList( {
89
64
 
90
65
  const filteredBlockPatterns = useMemo( () => {
91
66
  const filteredPatterns = patterns.filter( ( pattern ) => {
92
- if (
93
- isPatternFiltered(
94
- pattern,
95
- patternSourceFilter,
96
- patternSyncFilter
97
- )
98
- ) {
99
- return false;
100
- }
101
-
102
67
  if ( selectedCategory === allPatternsCategory.name ) {
103
68
  return true;
104
69
  }
105
-
70
+ if ( selectedCategory === myPatternsCategory.name && pattern.id ) {
71
+ return true;
72
+ }
106
73
  if ( selectedCategory === 'uncategorized' ) {
107
74
  const hasKnownCategory = pattern.categories.some(
108
75
  ( category ) =>
@@ -119,18 +86,12 @@ function PatternList( {
119
86
  return filteredPatterns;
120
87
  }
121
88
 
122
- return searchItems(
123
- filteredPatterns,
124
- searchValue,
125
- patternSourceFilter
126
- );
89
+ return searchItems( filteredPatterns, searchValue );
127
90
  }, [
128
91
  searchValue,
129
- patternSourceFilter,
130
92
  patterns,
131
93
  selectedCategory,
132
94
  registeredPatternCategories,
133
- patternSyncFilter,
134
95
  ] );
135
96
 
136
97
  // Announce search results on change.
@@ -150,10 +111,17 @@ function PatternList( {
150
111
  const pagingProps = usePatternsPaging(
151
112
  filteredBlockPatterns,
152
113
  selectedCategory,
153
- container,
154
- patternSourceFilter
114
+ container
155
115
  );
156
116
 
117
+ // Reset page when search value changes.
118
+ const [ previousSearchValue, setPreviousSearchValue ] =
119
+ useState( searchValue );
120
+ if ( searchValue !== previousSearchValue ) {
121
+ setPreviousSearchValue( searchValue );
122
+ pagingProps.changePage( 1 );
123
+ }
124
+
157
125
  const hasItems = !! filteredBlockPatterns?.length;
158
126
  return (
159
127
  <div
@@ -161,23 +129,11 @@ function PatternList( {
161
129
  ref={ container }
162
130
  >
163
131
  <PatternsListHeader
164
- filterValue={
165
- searchValue || PATTERN_SOURCE_FILTERS[ patternSourceFilter ]
166
- }
132
+ filterValue={ searchValue }
167
133
  filteredBlockPatternsLength={ filteredBlockPatterns.length }
168
- selectedCategory={ selectedCategory }
169
- patternCategories={ patternCategories }
170
134
  />
171
135
 
172
136
  <InserterListbox>
173
- { patternSourceFilter === PATTERN_TYPES.user &&
174
- ! searchValue && (
175
- <BlockPatternsSyncFilter
176
- patternSyncFilter={ patternSyncFilter }
177
- setPatternSyncFilter={ setPatternSyncFilter }
178
- />
179
- ) }
180
-
181
137
  { hasItems && (
182
138
  <BlockPatternsList
183
139
  shownPatterns={ pagingProps.categoryPatternsAsyncList }
@@ -4,12 +4,6 @@
4
4
  import { Button, SearchControl } from '@wordpress/components';
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
- /**
8
- * Internal dependencies
9
- */
10
- import { default as BlockPatternsSourceFilter } from '../block-patterns-source-filter';
11
- import { allPatternsCategory } from '../block-patterns-tab';
12
-
13
7
  function PatternCategoriesList( {
14
8
  selectedCategory,
15
9
  patternCategories,
@@ -56,8 +50,6 @@ function PatternExplorerSidebar( {
56
50
  selectedCategory,
57
51
  patternCategories,
58
52
  onClickCategory,
59
- patternSourceFilter,
60
- setPatternSourceFilter,
61
53
  searchValue,
62
54
  setSearchValue,
63
55
  } ) {
@@ -68,13 +60,6 @@ function PatternExplorerSidebar( {
68
60
  searchValue={ searchValue }
69
61
  setSearchValue={ setSearchValue }
70
62
  />
71
- <BlockPatternsSourceFilter
72
- value={ patternSourceFilter }
73
- onChange={ ( value ) => {
74
- setPatternSourceFilter( value );
75
- onClickCategory( allPatternsCategory.name );
76
- } }
77
- />
78
63
  { ! searchValue && (
79
64
  <PatternCategoriesList
80
65
  selectedCategory={ selectedCategory }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ SVG,
6
+ Path,
7
+ DropdownMenu,
8
+ MenuGroup,
9
+ MenuItemsChoice,
10
+ } from '@wordpress/components';
11
+ import { __ } from '@wordpress/i18n';
12
+ import { Icon } from '@wordpress/icons';
13
+ import { useMemo } from '@wordpress/element';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import { myPatternsCategory } from './block-patterns-tab';
19
+
20
+ export const PATTERN_TYPES = {
21
+ all: 'all',
22
+ synced: 'synced',
23
+ unsynced: 'unsynced',
24
+ user: 'user',
25
+ theme: 'theme',
26
+ directory: 'directory',
27
+ };
28
+
29
+ export const SYNC_TYPES = {
30
+ all: 'all',
31
+ full: 'fully',
32
+ unsynced: 'unsynced',
33
+ };
34
+
35
+ const getShouldDisableSyncFilter = ( sourceFilter ) =>
36
+ sourceFilter !== PATTERN_TYPES.all && sourceFilter !== PATTERN_TYPES.user;
37
+
38
+ const getShouldDisableNonUserSources = ( category ) => {
39
+ return category.name === myPatternsCategory.name;
40
+ };
41
+
42
+ export function BlockPatternsSyncFilter( {
43
+ setPatternSyncFilter,
44
+ setPatternSourceFilter,
45
+ patternSyncFilter,
46
+ patternSourceFilter,
47
+ scrollContainerRef,
48
+ category,
49
+ } ) {
50
+ // If the category is `myPatterns` then we need to set the source filter to `user`, but
51
+ // we do this by deriving from props rather than calling setPatternSourceFilter otherwise
52
+ // the user may be confused when switching to another category if the haven't explicity set
53
+ // this filter themselves.
54
+ const currentPatternSourceFilter =
55
+ category.name === myPatternsCategory.name
56
+ ? PATTERN_TYPES.user
57
+ : patternSourceFilter;
58
+
59
+ // We need to disable the sync filter option if the source filter is not 'all' or 'user'
60
+ // otherwise applying them will just result in no patterns being shown.
61
+ const shouldDisableSyncFilter = getShouldDisableSyncFilter(
62
+ currentPatternSourceFilter
63
+ );
64
+
65
+ // We also need to disable the directory and theme source filter options if the category
66
+ // is `myPatterns` otherwise applying them will also just result in no patterns being shown.
67
+ const shouldDisableNonUserSources =
68
+ getShouldDisableNonUserSources( category );
69
+
70
+ const patternSyncMenuOptions = useMemo(
71
+ () => [
72
+ { value: SYNC_TYPES.all, label: __( 'All' ) },
73
+ {
74
+ value: SYNC_TYPES.full,
75
+ label: __( 'Synced' ),
76
+ info: __( 'Updated everywhere' ),
77
+ disabled: shouldDisableSyncFilter,
78
+ },
79
+ {
80
+ value: SYNC_TYPES.unsynced,
81
+ label: __( 'Standard' ),
82
+ info: __( 'Edit freely' ),
83
+ disabled: shouldDisableSyncFilter,
84
+ },
85
+ ],
86
+ [ shouldDisableSyncFilter ]
87
+ );
88
+
89
+ const patternSourceMenuOptions = useMemo(
90
+ () => [
91
+ {
92
+ value: PATTERN_TYPES.all,
93
+ label: __( 'All' ),
94
+ disabled: shouldDisableNonUserSources,
95
+ },
96
+ {
97
+ value: PATTERN_TYPES.directory,
98
+ label: __( 'Directory' ),
99
+ info: __( 'Pattern directory & core' ),
100
+ disabled: shouldDisableNonUserSources,
101
+ },
102
+ {
103
+ value: PATTERN_TYPES.theme,
104
+ label: __( 'Theme' ),
105
+ info: __( 'Bundled with the theme' ),
106
+ disabled: shouldDisableNonUserSources,
107
+ },
108
+ {
109
+ value: PATTERN_TYPES.user,
110
+ label: __( 'User' ),
111
+ info: __( 'Custom created' ),
112
+ },
113
+ ],
114
+ [ shouldDisableNonUserSources ]
115
+ );
116
+
117
+ function handleSetSourceFilterChange( newSourceFilter ) {
118
+ setPatternSourceFilter( newSourceFilter );
119
+ if ( getShouldDisableSyncFilter( newSourceFilter ) ) {
120
+ setPatternSyncFilter( SYNC_TYPES.all );
121
+ }
122
+ }
123
+
124
+ return (
125
+ <>
126
+ <DropdownMenu
127
+ popoverProps={ {
128
+ placement: 'right-end',
129
+ } }
130
+ label="Filter patterns"
131
+ icon={
132
+ <Icon
133
+ icon={
134
+ <SVG
135
+ width="24"
136
+ height="24"
137
+ viewBox="0 0 24 24"
138
+ fill="none"
139
+ xmlns="http://www.w3.org/2000/svg"
140
+ >
141
+ <Path
142
+ d="M10 17.5H14V16H10V17.5ZM6 6V7.5H18V6H6ZM8 12.5H16V11H8V12.5Z"
143
+ fill="#1E1E1E"
144
+ />
145
+ </SVG>
146
+ }
147
+ />
148
+ }
149
+ >
150
+ { () => (
151
+ <>
152
+ <MenuGroup label={ __( 'Author' ) }>
153
+ <MenuItemsChoice
154
+ choices={ patternSourceMenuOptions }
155
+ onSelect={ ( value ) => {
156
+ handleSetSourceFilterChange( value );
157
+ scrollContainerRef.current?.scrollTo(
158
+ 0,
159
+ 0
160
+ );
161
+ } }
162
+ value={ currentPatternSourceFilter }
163
+ />
164
+ </MenuGroup>
165
+ <MenuGroup label={ __( 'Type' ) }>
166
+ <MenuItemsChoice
167
+ choices={ patternSyncMenuOptions }
168
+ onSelect={ ( value ) => {
169
+ setPatternSyncFilter( value );
170
+ scrollContainerRef.current?.scrollTo(
171
+ 0,
172
+ 0
173
+ );
174
+ } }
175
+ value={ patternSyncFilter }
176
+ />
177
+ </MenuGroup>
178
+ </>
179
+ ) }
180
+ </DropdownMenu>
181
+ </>
182
+ );
183
+ }
@@ -14,6 +14,9 @@ import {
14
14
  __experimentalItemGroup as ItemGroup,
15
15
  __experimentalItem as Item,
16
16
  __experimentalHStack as HStack,
17
+ __experimentalVStack as VStack,
18
+ __experimentalHeading as Heading,
19
+ __experimentalText as Text,
17
20
  FlexBlock,
18
21
  Button,
19
22
  } from '@wordpress/components';
@@ -28,48 +31,68 @@ import usePatternsState from './hooks/use-patterns-state';
28
31
  import BlockPatternList from '../block-patterns-list';
29
32
  import PatternsExplorerModal from './block-patterns-explorer/explorer';
30
33
  import MobileTabNavigation from './mobile-tab-navigation';
31
- import BlockPatternsPaging from '../block-patterns-paging';
32
34
  import usePatternsPaging from './hooks/use-patterns-paging';
33
- import {
34
- PATTERN_TYPES,
35
- default as BlockPatternsSourceFilter,
36
- } from './block-patterns-source-filter';
37
35
  import {
38
36
  BlockPatternsSyncFilter,
39
37
  SYNC_TYPES,
40
- } from './block-patterns-sync-filter';
38
+ PATTERN_TYPES,
39
+ } from './block-patterns-filter';
41
40
 
42
41
  const noop = () => {};
43
42
 
44
43
  export const allPatternsCategory = {
45
44
  name: 'allPatterns',
46
- label: __( 'All categories' ),
45
+ label: __( 'All Patterns' ),
46
+ };
47
+
48
+ export const myPatternsCategory = {
49
+ name: 'myPatterns',
50
+ label: __( 'My patterns' ),
47
51
  };
48
52
 
49
53
  export function isPatternFiltered( pattern, sourceFilter, syncFilter ) {
54
+ const isUserPattern = pattern.name.startsWith( 'core/block' );
55
+ const isDirectoryPattern =
56
+ pattern.source === 'core' ||
57
+ pattern.source?.startsWith( 'pattern-directory' );
58
+
59
+ // If theme source selected, filter out user created patterns and those from
60
+ // the core patterns directory.
50
61
  if (
51
62
  sourceFilter === PATTERN_TYPES.theme &&
52
- pattern.name.startsWith( 'core/block' )
63
+ ( isUserPattern || isDirectoryPattern )
64
+ ) {
65
+ return true;
66
+ }
67
+
68
+ // If the directory source is selected, filter out user created patterns
69
+ // and those bundled with the theme.
70
+ if (
71
+ sourceFilter === PATTERN_TYPES.directory &&
72
+ ( isUserPattern || ! isDirectoryPattern )
53
73
  ) {
54
74
  return true;
55
75
  }
76
+
77
+ // If user source selected, filter out theme patterns. Any pattern without
78
+ // an id wasn't created by a user.
56
79
  if ( sourceFilter === PATTERN_TYPES.user && ! pattern.id ) {
57
80
  return true;
58
81
  }
59
- if (
60
- sourceFilter === PATTERN_TYPES.user &&
61
- syncFilter === SYNC_TYPES.full &&
62
- pattern.syncStatus !== ''
63
- ) {
82
+
83
+ // Filter by sync status.
84
+ if ( syncFilter === SYNC_TYPES.full && pattern.syncStatus !== '' ) {
64
85
  return true;
65
86
  }
87
+
66
88
  if (
67
- sourceFilter === PATTERN_TYPES.user &&
68
89
  syncFilter === SYNC_TYPES.unsynced &&
69
- pattern.syncStatus !== 'unsynced'
90
+ pattern.syncStatus !== 'unsynced' &&
91
+ isUserPattern
70
92
  ) {
71
93
  return true;
72
94
  }
95
+
73
96
  return false;
74
97
  }
75
98
 
@@ -126,6 +149,9 @@ export function usePatternsCategories( rootClientId, sourceFilter = 'all' ) {
126
149
  label: _x( 'Uncategorized' ),
127
150
  } );
128
151
  }
152
+ if ( filteredPatterns.some( ( pattern ) => pattern.id ) ) {
153
+ categories.unshift( myPatternsCategory );
154
+ }
129
155
  if ( filteredPatterns.length > 0 ) {
130
156
  categories.unshift( {
131
157
  name: allPatternsCategory.name,
@@ -173,6 +199,7 @@ export function BlockPatternsCategoryDialog( {
173
199
  className="block-editor-inserter__patterns-category-dialog"
174
200
  >
175
201
  <BlockPatternsCategoryPanel
202
+ key={ category.name }
176
203
  rootClientId={ rootClientId }
177
204
  onInsert={ onInsert }
178
205
  onHover={ onHover }
@@ -190,26 +217,26 @@ export function BlockPatternsCategoryPanel( {
190
217
  onHover = noop,
191
218
  category,
192
219
  showTitlesAsTooltip,
193
- patternFilter,
194
220
  } ) {
195
221
  const [ allPatterns, , onClickPattern ] = usePatternsState(
196
222
  onInsert,
197
223
  rootClientId
198
224
  );
199
225
  const [ patternSyncFilter, setPatternSyncFilter ] = useState( 'all' );
226
+ const [ patternSourceFilter, setPatternSourceFilter ] = useState( 'all' );
200
227
 
201
228
  const availableCategories = usePatternsCategories(
202
229
  rootClientId,
203
- patternFilter
230
+ patternSourceFilter
204
231
  );
205
- const container = useRef();
232
+ const scrollContainerRef = useRef();
206
233
  const currentCategoryPatterns = useMemo(
207
234
  () =>
208
235
  allPatterns.filter( ( pattern ) => {
209
236
  if (
210
237
  isPatternFiltered(
211
238
  pattern,
212
- patternFilter,
239
+ patternSourceFilter,
213
240
  patternSyncFilter
214
241
  )
215
242
  ) {
@@ -219,6 +246,9 @@ export function BlockPatternsCategoryPanel( {
219
246
  if ( category.name === allPatternsCategory.name ) {
220
247
  return true;
221
248
  }
249
+ if ( category.name === myPatternsCategory.name && pattern.id ) {
250
+ return true;
251
+ }
222
252
  if ( category.name !== 'uncategorized' ) {
223
253
  return pattern.categories?.includes( category.name );
224
254
  }
@@ -239,7 +269,7 @@ export function BlockPatternsCategoryPanel( {
239
269
  allPatterns,
240
270
  availableCategories,
241
271
  category.name,
242
- patternFilter,
272
+ patternSourceFilter,
243
273
  patternSyncFilter,
244
274
  ]
245
275
  );
@@ -247,33 +277,66 @@ export function BlockPatternsCategoryPanel( {
247
277
  const pagingProps = usePatternsPaging(
248
278
  currentCategoryPatterns,
249
279
  category,
250
- container
280
+ scrollContainerRef
251
281
  );
282
+ const { changePage } = pagingProps;
252
283
 
253
284
  // Hide block pattern preview on unmount.
254
285
  // eslint-disable-next-line react-hooks/exhaustive-deps
255
286
  useEffect( () => () => onHover( null ), [] );
256
287
 
288
+ const onSetPatternSyncFilter = useCallback(
289
+ ( value ) => {
290
+ setPatternSyncFilter( value );
291
+ changePage( 1 );
292
+ },
293
+ [ setPatternSyncFilter, changePage ]
294
+ );
295
+ const onSetPatternSourceFilter = useCallback(
296
+ ( value ) => {
297
+ setPatternSourceFilter( value );
298
+ changePage( 1 );
299
+ },
300
+ [ setPatternSourceFilter, changePage ]
301
+ );
302
+
257
303
  return (
258
- <div
259
- className="block-editor-inserter__patterns-category-panel"
260
- ref={ container }
261
- >
262
- <div className="block-editor-inserter__patterns-category-panel-title">
263
- { category.label }
264
- </div>
265
- <p>{ category.description }</p>
266
- { patternFilter === PATTERN_TYPES.user && (
267
- <BlockPatternsSyncFilter
268
- patternSyncFilter={ patternSyncFilter }
269
- setPatternSyncFilter={ setPatternSyncFilter }
270
- />
271
- ) }
272
- { ! currentCategoryPatterns.length && (
273
- <div>{ __( 'No results found' ) }</div>
274
- ) }
304
+ <div className="block-editor-inserter__patterns-category-panel">
305
+ <VStack
306
+ spacing={ 2 }
307
+ className="block-editor-inserter__patterns-category-panel-header"
308
+ >
309
+ <HStack>
310
+ <FlexBlock>
311
+ <Heading level={ 4 } as="div">
312
+ { category.label }
313
+ </Heading>
314
+ </FlexBlock>
315
+ <BlockPatternsSyncFilter
316
+ patternSyncFilter={ patternSyncFilter }
317
+ patternSourceFilter={ patternSourceFilter }
318
+ setPatternSyncFilter={ onSetPatternSyncFilter }
319
+ setPatternSourceFilter={ onSetPatternSourceFilter }
320
+ scrollContainerRef={ scrollContainerRef }
321
+ category={ category }
322
+ />
323
+ </HStack>
324
+ { category.description && (
325
+ <Text>{ category.description }</Text>
326
+ ) }
327
+ { ! currentCategoryPatterns.length && (
328
+ <Text
329
+ variant="muted"
330
+ className="block-editor-inserter__patterns-category-no-results"
331
+ >
332
+ { __( 'No results found' ) }
333
+ </Text>
334
+ ) }
335
+ </VStack>
336
+
275
337
  { currentCategoryPatterns.length > 0 && (
276
338
  <BlockPatternList
339
+ ref={ scrollContainerRef }
277
340
  shownPatterns={ pagingProps.categoryPatternsAsyncList }
278
341
  blockPatterns={ pagingProps.categoryPatterns }
279
342
  onClickPattern={ onClickPattern }
@@ -283,12 +346,10 @@ export function BlockPatternsCategoryPanel( {
283
346
  category={ category.name }
284
347
  isDraggable
285
348
  showTitlesAsTooltip={ showTitlesAsTooltip }
286
- patternFilter={ patternFilter }
349
+ patternFilter={ patternSourceFilter }
350
+ pagingProps={ pagingProps }
287
351
  />
288
352
  ) }
289
- { pagingProps.numPages > 1 && (
290
- <BlockPatternsPaging { ...pagingProps } />
291
- ) }
292
353
  </div>
293
354
  );
294
355
  }
@@ -300,12 +361,8 @@ function BlockPatternsTabs( {
300
361
  rootClientId,
301
362
  } ) {
302
363
  const [ showPatternsExplorer, setShowPatternsExplorer ] = useState( false );
303
- const [ patternSourceFilter, setPatternSourceFilter ] = useState( 'all' );
304
364
 
305
- const categories = usePatternsCategories(
306
- rootClientId,
307
- patternSourceFilter
308
- );
365
+ const categories = usePatternsCategories( rootClientId );
309
366
 
310
367
  const initialCategory = selectedCategory || categories[ 0 ];
311
368
  const isMobile = useViewportMatch( 'medium', '<' );
@@ -317,23 +374,13 @@ function BlockPatternsTabs( {
317
374
  aria-label={ __( 'Block pattern categories' ) }
318
375
  className="block-editor-inserter__block-patterns-tabs"
319
376
  >
320
- <BlockPatternsSourceFilter
321
- value={ patternSourceFilter }
322
- onChange={ ( value ) => {
323
- setPatternSourceFilter( value );
324
- onSelectCategory( allPatternsCategory, value );
325
- } }
326
- />
327
377
  <ItemGroup role="list">
328
378
  { categories.map( ( category ) => (
329
379
  <Item
330
380
  role="listitem"
331
381
  key={ category.name }
332
382
  onClick={ () =>
333
- onSelectCategory(
334
- category,
335
- patternSourceFilter
336
- )
383
+ onSelectCategory( category )
337
384
  }
338
385
  className={
339
386
  category === selectedCategory
@@ -380,6 +427,7 @@ function BlockPatternsTabs( {
380
427
  <MobileTabNavigation categories={ categories }>
381
428
  { ( category ) => (
382
429
  <BlockPatternsCategoryPanel
430
+ key={ category.name }
383
431
  onInsert={ onInsert }
384
432
  rootClientId={ rootClientId }
385
433
  category={ category }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useMemo, useState } from '@wordpress/element';
4
+ import { useMemo, useState, useEffect } from '@wordpress/element';
5
5
  import { useAsyncList, usePrevious } from '@wordpress/compose';
6
6
  import { getScrollContainer } from '@wordpress/dom';
7
7
 
@@ -54,6 +54,17 @@ export default function usePatternsPaging(
54
54
 
55
55
  setCurrentPage( page );
56
56
  };
57
+
58
+ useEffect(
59
+ function scrollToTopOnCategoryChange() {
60
+ const scrollContainer = getScrollContainer(
61
+ scrollContainerRef?.current
62
+ );
63
+ scrollContainer?.scrollTo( 0, 0 );
64
+ },
65
+ [ currentCategory, scrollContainerRef ]
66
+ );
67
+
57
68
  return {
58
69
  totalItems,
59
70
  categoryPatterns,