@wordpress/block-editor 10.2.0 → 10.3.0

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 (285) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +41 -0
  3. package/build/components/block-draggable/draggable-chip.js +4 -2
  4. package/build/components/block-draggable/draggable-chip.js.map +1 -1
  5. package/build/components/block-inspector/index.js +4 -4
  6. package/build/components/block-inspector/index.js.map +1 -1
  7. package/build/components/block-list/block-list-compact.native.js +1 -0
  8. package/build/components/block-list/block-list-compact.native.js.map +1 -1
  9. package/build/components/block-list/block.js +72 -14
  10. package/build/components/block-list/block.js.map +1 -1
  11. package/build/components/block-list/block.native.js +79 -12
  12. package/build/components/block-list/block.native.js.map +1 -1
  13. package/build/components/block-list/use-in-between-inserter.js +7 -23
  14. package/build/components/block-list/use-in-between-inserter.js.map +1 -1
  15. package/build/components/block-lock/modal.js +9 -6
  16. package/build/components/block-lock/modal.js.map +1 -1
  17. package/build/components/block-parent-selector/index.js +3 -3
  18. package/build/components/block-parent-selector/index.js.map +1 -1
  19. package/build/components/block-patterns-list/index.js +5 -4
  20. package/build/components/block-patterns-list/index.js.map +1 -1
  21. package/build/components/block-popover/drop-zone.js +85 -0
  22. package/build/components/block-popover/drop-zone.js.map +1 -0
  23. package/build/components/block-popover/index.js +2 -1
  24. package/build/components/block-popover/index.js.map +1 -1
  25. package/build/components/block-preview/auto.js +2 -2
  26. package/build/components/block-preview/auto.js.map +1 -1
  27. package/build/components/block-preview/index.js +6 -9
  28. package/build/components/block-preview/index.js.map +1 -1
  29. package/build/components/block-preview/live.js +3 -7
  30. package/build/components/block-preview/live.js.map +1 -1
  31. package/build/components/block-selection-clearer/index.js +9 -1
  32. package/build/components/block-selection-clearer/index.js.map +1 -1
  33. package/build/components/block-settings-menu/block-settings-dropdown.js +17 -11
  34. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  35. package/build/components/block-styles/index.js +18 -42
  36. package/build/components/block-styles/index.js.map +1 -1
  37. package/build/components/block-toolbar/index.js +4 -4
  38. package/build/components/block-toolbar/index.js.map +1 -1
  39. package/build/components/block-tools/back-compat.js +2 -1
  40. package/build/components/block-tools/back-compat.js.map +1 -1
  41. package/build/components/block-tools/insertion-point.js +50 -20
  42. package/build/components/block-tools/insertion-point.js.map +1 -1
  43. package/build/components/block-tools/selected-block-popover.js +15 -3
  44. package/build/components/block-tools/selected-block-popover.js.map +1 -1
  45. package/build/components/colors-gradients/control.js +1 -1
  46. package/build/components/colors-gradients/control.js.map +1 -1
  47. package/build/components/colors-gradients/panel-color-gradient-settings.js +2 -2
  48. package/build/components/colors-gradients/panel-color-gradient-settings.js.map +1 -1
  49. package/build/components/font-sizes/fluid-utils.js +256 -0
  50. package/build/components/font-sizes/fluid-utils.js.map +1 -0
  51. package/build/components/font-sizes/index.js +8 -0
  52. package/build/components/font-sizes/index.js.map +1 -1
  53. package/build/components/index.js +0 -9
  54. package/build/components/index.js.map +1 -1
  55. package/build/components/inner-blocks/use-inner-block-template-sync.js +3 -2
  56. package/build/components/inner-blocks/use-inner-block-template-sync.js.map +1 -1
  57. package/build/components/inserter/block-patterns-tab.js +151 -78
  58. package/build/components/inserter/block-patterns-tab.js.map +1 -1
  59. package/build/components/inserter/menu.js +14 -3
  60. package/build/components/inserter/menu.js.map +1 -1
  61. package/build/components/inserter-draggable-blocks/index.js +4 -2
  62. package/build/components/inserter-draggable-blocks/index.js.map +1 -1
  63. package/build/components/list-view/use-list-view-drop-zone.js +1 -14
  64. package/build/components/list-view/use-list-view-drop-zone.js.map +1 -1
  65. package/build/components/preview-options/index.js +2 -3
  66. package/build/components/preview-options/index.js.map +1 -1
  67. package/build/components/spacing-sizes-control/spacing-input-control.js +35 -5
  68. package/build/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
  69. package/build/components/use-block-drop-zone/index.js +98 -57
  70. package/build/components/use-block-drop-zone/index.js.map +1 -1
  71. package/build/components/use-on-block-drop/index.js +12 -12
  72. package/build/components/use-on-block-drop/index.js.map +1 -1
  73. package/build/components/use-on-block-drop/types.js +6 -0
  74. package/build/components/use-on-block-drop/types.js.map +1 -0
  75. package/build/hooks/align.js +1 -3
  76. package/build/hooks/align.js.map +1 -1
  77. package/build/hooks/align.native.js +1 -7
  78. package/build/hooks/align.native.js.map +1 -1
  79. package/build/hooks/font-size.js +60 -0
  80. package/build/hooks/font-size.js.map +1 -1
  81. package/build/hooks/margin.js +6 -5
  82. package/build/hooks/margin.js.map +1 -1
  83. package/build/hooks/padding.js +2 -1
  84. package/build/hooks/padding.js.map +1 -1
  85. package/build/hooks/style.js +126 -4
  86. package/build/hooks/style.js.map +1 -1
  87. package/build/hooks/use-typography-props.js +17 -3
  88. package/build/hooks/use-typography-props.js.map +1 -1
  89. package/build/hooks/utils.js +1 -1
  90. package/build/hooks/utils.js.map +1 -1
  91. package/build/store/actions.js +59 -45
  92. package/build/store/actions.js.map +1 -1
  93. package/build/store/defaults.js +3 -0
  94. package/build/store/defaults.js.map +1 -1
  95. package/build/store/reducer.js +31 -15
  96. package/build/store/reducer.js.map +1 -1
  97. package/build/utils/math.js +14 -0
  98. package/build/utils/math.js.map +1 -1
  99. package/build/utils/pre-parse-patterns.js +19 -2
  100. package/build/utils/pre-parse-patterns.js.map +1 -1
  101. package/build-module/components/block-draggable/draggable-chip.js +7 -3
  102. package/build-module/components/block-draggable/draggable-chip.js.map +1 -1
  103. package/build-module/components/block-inspector/index.js +4 -4
  104. package/build-module/components/block-inspector/index.js.map +1 -1
  105. package/build-module/components/block-list/block-list-compact.native.js +1 -0
  106. package/build-module/components/block-list/block-list-compact.native.js.map +1 -1
  107. package/build-module/components/block-list/block.js +72 -14
  108. package/build-module/components/block-list/block.js.map +1 -1
  109. package/build-module/components/block-list/block.native.js +80 -13
  110. package/build-module/components/block-list/block.native.js.map +1 -1
  111. package/build-module/components/block-list/use-in-between-inserter.js +8 -24
  112. package/build-module/components/block-list/use-in-between-inserter.js.map +1 -1
  113. package/build-module/components/block-lock/modal.js +10 -8
  114. package/build-module/components/block-lock/modal.js.map +1 -1
  115. package/build-module/components/block-parent-selector/index.js +3 -3
  116. package/build-module/components/block-parent-selector/index.js.map +1 -1
  117. package/build-module/components/block-patterns-list/index.js +5 -4
  118. package/build-module/components/block-patterns-list/index.js.map +1 -1
  119. package/build-module/components/block-popover/drop-zone.js +70 -0
  120. package/build-module/components/block-popover/drop-zone.js.map +1 -0
  121. package/build-module/components/block-popover/index.js +2 -1
  122. package/build-module/components/block-popover/index.js.map +1 -1
  123. package/build-module/components/block-preview/auto.js +1 -1
  124. package/build-module/components/block-preview/auto.js.map +1 -1
  125. package/build-module/components/block-preview/index.js +6 -9
  126. package/build-module/components/block-preview/index.js.map +1 -1
  127. package/build-module/components/block-preview/live.js +3 -6
  128. package/build-module/components/block-preview/live.js.map +1 -1
  129. package/build-module/components/block-selection-clearer/index.js +9 -1
  130. package/build-module/components/block-selection-clearer/index.js.map +1 -1
  131. package/build-module/components/block-settings-menu/block-settings-dropdown.js +18 -12
  132. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  133. package/build-module/components/block-styles/index.js +19 -44
  134. package/build-module/components/block-styles/index.js.map +1 -1
  135. package/build-module/components/block-toolbar/index.js +4 -4
  136. package/build-module/components/block-toolbar/index.js.map +1 -1
  137. package/build-module/components/block-tools/back-compat.js +2 -1
  138. package/build-module/components/block-tools/back-compat.js.map +1 -1
  139. package/build-module/components/block-tools/insertion-point.js +48 -20
  140. package/build-module/components/block-tools/insertion-point.js.map +1 -1
  141. package/build-module/components/block-tools/selected-block-popover.js +15 -3
  142. package/build-module/components/block-tools/selected-block-popover.js.map +1 -1
  143. package/build-module/components/colors-gradients/control.js +2 -2
  144. package/build-module/components/colors-gradients/control.js.map +1 -1
  145. package/build-module/components/colors-gradients/panel-color-gradient-settings.js +3 -3
  146. package/build-module/components/colors-gradients/panel-color-gradient-settings.js.map +1 -1
  147. package/build-module/components/font-sizes/fluid-utils.js +245 -0
  148. package/build-module/components/font-sizes/fluid-utils.js.map +1 -0
  149. package/build-module/components/font-sizes/index.js +1 -0
  150. package/build-module/components/font-sizes/index.js.map +1 -1
  151. package/build-module/components/index.js +0 -1
  152. package/build-module/components/index.js.map +1 -1
  153. package/build-module/components/inner-blocks/use-inner-block-template-sync.js +3 -2
  154. package/build-module/components/inner-blocks/use-inner-block-template-sync.js.map +1 -1
  155. package/build-module/components/inserter/block-patterns-tab.js +148 -81
  156. package/build-module/components/inserter/block-patterns-tab.js.map +1 -1
  157. package/build-module/components/inserter/menu.js +10 -3
  158. package/build-module/components/inserter/menu.js.map +1 -1
  159. package/build-module/components/inserter-draggable-blocks/index.js +4 -2
  160. package/build-module/components/inserter-draggable-blocks/index.js.map +1 -1
  161. package/build-module/components/list-view/use-list-view-drop-zone.js +1 -14
  162. package/build-module/components/list-view/use-list-view-drop-zone.js.map +1 -1
  163. package/build-module/components/preview-options/index.js +2 -3
  164. package/build-module/components/preview-options/index.js.map +1 -1
  165. package/build-module/components/spacing-sizes-control/spacing-input-control.js +34 -5
  166. package/build-module/components/spacing-sizes-control/spacing-input-control.js.map +1 -1
  167. package/build-module/components/use-block-drop-zone/index.js +98 -58
  168. package/build-module/components/use-block-drop-zone/index.js.map +1 -1
  169. package/build-module/components/use-on-block-drop/index.js +12 -12
  170. package/build-module/components/use-on-block-drop/index.js.map +1 -1
  171. package/build-module/components/use-on-block-drop/types.js +2 -0
  172. package/build-module/components/use-on-block-drop/types.js.map +1 -0
  173. package/build-module/hooks/align.js +1 -2
  174. package/build-module/hooks/align.js.map +1 -1
  175. package/build-module/hooks/align.native.js +1 -6
  176. package/build-module/hooks/align.native.js.map +1 -1
  177. package/build-module/hooks/font-size.js +59 -1
  178. package/build-module/hooks/font-size.js.map +1 -1
  179. package/build-module/hooks/margin.js +6 -5
  180. package/build-module/hooks/margin.js.map +1 -1
  181. package/build-module/hooks/padding.js +2 -1
  182. package/build-module/hooks/padding.js.map +1 -1
  183. package/build-module/hooks/style.js +124 -3
  184. package/build-module/hooks/style.js.map +1 -1
  185. package/build-module/hooks/use-typography-props.js +17 -4
  186. package/build-module/hooks/use-typography-props.js.map +1 -1
  187. package/build-module/hooks/utils.js +2 -2
  188. package/build-module/hooks/utils.js.map +1 -1
  189. package/build-module/store/actions.js +55 -42
  190. package/build-module/store/actions.js.map +1 -1
  191. package/build-module/store/defaults.js +3 -0
  192. package/build-module/store/defaults.js.map +1 -1
  193. package/build-module/store/reducer.js +32 -16
  194. package/build-module/store/reducer.js.map +1 -1
  195. package/build-module/utils/math.js +12 -0
  196. package/build-module/utils/math.js.map +1 -1
  197. package/build-module/utils/pre-parse-patterns.js +19 -2
  198. package/build-module/utils/pre-parse-patterns.js.map +1 -1
  199. package/build-style/style-rtl.css +243 -144
  200. package/build-style/style.css +243 -144
  201. package/package.json +28 -28
  202. package/src/components/block-breadcrumb/test/index.js +1 -1
  203. package/src/components/block-draggable/draggable-chip.js +4 -2
  204. package/src/components/block-inspector/index.js +8 -7
  205. package/src/components/block-list/block-list-compact.native.js +1 -0
  206. package/src/components/block-list/block.js +111 -7
  207. package/src/components/block-list/block.native.js +123 -9
  208. package/src/components/block-list/style.scss +93 -126
  209. package/src/components/block-list/use-in-between-inserter.js +8 -19
  210. package/src/components/block-lock/modal.js +12 -7
  211. package/src/components/block-mover/style.scss +0 -1
  212. package/src/components/block-parent-selector/index.js +3 -3
  213. package/src/components/block-patterns-list/index.js +9 -5
  214. package/src/components/block-patterns-list/style.scss +7 -3
  215. package/src/components/block-popover/README.md +8 -0
  216. package/src/components/block-popover/drop-zone.js +63 -0
  217. package/src/components/block-popover/index.js +2 -1
  218. package/src/components/block-popover/style.scss +17 -1
  219. package/src/components/block-preview/auto.js +1 -1
  220. package/src/components/block-preview/index.js +7 -8
  221. package/src/components/block-preview/live.js +2 -7
  222. package/src/components/block-preview/test/index.js +1 -7
  223. package/src/components/block-selection-clearer/index.js +7 -2
  224. package/src/components/block-selection-clearer/test/index.js +118 -0
  225. package/src/components/block-settings-menu/block-settings-dropdown.js +25 -11
  226. package/src/components/block-settings-menu/test/block-mode-toggle.js +17 -17
  227. package/src/components/block-styles/index.js +26 -49
  228. package/src/components/block-switcher/test/index.js +2 -2
  229. package/src/components/block-toolbar/index.js +4 -6
  230. package/src/components/block-toolbar/style.scss +38 -14
  231. package/src/components/block-tools/back-compat.js +1 -0
  232. package/src/components/block-tools/insertion-point.js +42 -17
  233. package/src/components/block-tools/selected-block-popover.js +14 -1
  234. package/src/components/button-block-appender/style.scss +4 -2
  235. package/src/components/color-palette/test/__snapshots__/control.js.snap +32 -52
  236. package/src/components/color-palette/test/control.js +11 -15
  237. package/src/components/colors-gradients/control.js +2 -2
  238. package/src/components/colors-gradients/panel-color-gradient-settings.js +3 -4
  239. package/src/components/colors-gradients/test/control.js +49 -77
  240. package/src/components/font-sizes/fluid-utils.js +296 -0
  241. package/src/components/font-sizes/index.js +1 -0
  242. package/src/components/font-sizes/test/fluid-utils.js +168 -0
  243. package/src/components/image-size-control/test/index.js +47 -60
  244. package/src/components/index.js +0 -1
  245. package/src/components/inner-blocks/use-inner-block-template-sync.js +3 -2
  246. package/src/components/inserter/block-patterns-tab.js +232 -98
  247. package/src/components/inserter/menu.js +15 -2
  248. package/src/components/inserter/style.scss +94 -9
  249. package/src/components/inserter/test/reusable-blocks-tab.js +6 -6
  250. package/src/components/inserter-draggable-blocks/index.js +12 -2
  251. package/src/components/inserter-list-item/style.scss +20 -1
  252. package/src/components/link-control/test/index.js +1 -1
  253. package/src/components/list-view/use-list-view-drop-zone.js +4 -18
  254. package/src/components/panel-color-settings/test/index.js +4 -4
  255. package/src/components/preview-options/index.js +2 -2
  256. package/src/components/preview-options/style.scss +1 -1
  257. package/src/components/provider/test/use-block-sync.js +131 -165
  258. package/src/components/responsive-block-control/test/index.js +4 -4
  259. package/src/components/spacing-sizes-control/spacing-input-control.js +16 -2
  260. package/src/components/spacing-sizes-control/style.scss +26 -19
  261. package/src/components/use-block-drop-zone/index.js +136 -79
  262. package/src/components/use-block-drop-zone/test/index.js +333 -81
  263. package/src/components/use-on-block-drop/index.js +11 -12
  264. package/src/components/use-on-block-drop/types.ts +1 -0
  265. package/src/hooks/align.js +3 -2
  266. package/src/hooks/align.native.js +5 -8
  267. package/src/hooks/font-size.js +75 -0
  268. package/src/hooks/margin.js +5 -4
  269. package/src/hooks/padding.js +1 -0
  270. package/src/hooks/style.js +122 -3
  271. package/src/hooks/test/style.js +206 -1
  272. package/src/hooks/test/use-typography-props.js +22 -0
  273. package/src/hooks/use-typography-props.js +18 -3
  274. package/src/hooks/utils.js +6 -2
  275. package/src/store/actions.js +20 -12
  276. package/src/store/defaults.js +3 -0
  277. package/src/store/reducer.js +31 -24
  278. package/src/store/test/actions.js +0 -9
  279. package/src/utils/math.js +17 -0
  280. package/src/utils/pre-parse-patterns.js +12 -7
  281. package/build/components/inserter/pattern-panel.js +0 -87
  282. package/build/components/inserter/pattern-panel.js.map +0 -1
  283. package/build-module/components/inserter/pattern-panel.js +0 -74
  284. package/build-module/components/inserter/pattern-panel.js.map +0 -1
  285. package/src/components/inserter/pattern-panel.js +0 -93
@@ -40,7 +40,7 @@ export default function useInnerBlockTemplateSync(
40
40
  templateLock,
41
41
  templateInsertUpdatesSelection
42
42
  ) {
43
- const { getSelectedBlocksInitialCaretPosition } =
43
+ const { getSelectedBlocksInitialCaretPosition, isBlockSelected } =
44
44
  useSelect( blockEditorStore );
45
45
  const { replaceInnerBlocks } = useDispatch( blockEditorStore );
46
46
  const innerBlocks = useSelect(
@@ -86,7 +86,8 @@ export default function useInnerBlockTemplateSync(
86
86
  nextBlocks,
87
87
  currentInnerBlocks.length === 0 &&
88
88
  templateInsertUpdatesSelection &&
89
- nextBlocks.length !== 0,
89
+ nextBlocks.length !== 0 &&
90
+ isBlockSelected( clientId ),
90
91
  // This ensures the "initialPosition" doesn't change when applying the template
91
92
  // If we're supposed to focus the block, we'll focus the first inner block
92
93
  // otherwise, we won't apply any auto-focus.
@@ -1,96 +1,37 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useMemo, useState, useCallback } from '@wordpress/element';
5
- import { _x } from '@wordpress/i18n';
6
- import { useAsyncList } from '@wordpress/compose';
4
+ import {
5
+ useMemo,
6
+ useState,
7
+ useCallback,
8
+ useRef,
9
+ useEffect,
10
+ } from '@wordpress/element';
11
+ import { _x, __, isRTL } from '@wordpress/i18n';
12
+ import { useAsyncList, useViewportMatch } from '@wordpress/compose';
13
+ import {
14
+ __experimentalItemGroup as ItemGroup,
15
+ __experimentalItem as Item,
16
+ __experimentalHStack as HStack,
17
+ __experimentalNavigatorProvider as NavigatorProvider,
18
+ __experimentalNavigatorScreen as NavigatorScreen,
19
+ __experimentalNavigatorButton as NavigatorButton,
20
+ __experimentalNavigatorBackButton as NavigatorBackButton,
21
+ FlexBlock,
22
+ Button,
23
+ } from '@wordpress/components';
24
+ import { Icon, chevronRight, chevronLeft } from '@wordpress/icons';
25
+ import { focus } from '@wordpress/dom';
7
26
 
8
27
  /**
9
28
  * Internal dependencies
10
29
  */
11
- import PatternInserterPanel from './pattern-panel';
12
30
  import usePatternsState from './hooks/use-patterns-state';
13
31
  import BlockPatternList from '../block-patterns-list';
14
32
  import PatternsExplorerModal from './block-patterns-explorer/explorer';
15
33
 
16
- function BlockPatternsCategory( {
17
- rootClientId,
18
- onInsert,
19
- selectedCategory,
20
- populatedCategories,
21
- } ) {
22
- const [ allPatterns, , onClick ] = usePatternsState(
23
- onInsert,
24
- rootClientId
25
- );
26
-
27
- const getPatternIndex = useCallback(
28
- ( pattern ) => {
29
- if ( ! pattern.categories?.length ) {
30
- return Infinity;
31
- }
32
- const indexedCategories = populatedCategories.reduce(
33
- ( accumulator, { name }, index ) => {
34
- accumulator[ name ] = index;
35
- return accumulator;
36
- },
37
- {}
38
- );
39
- return Math.min(
40
- ...pattern.categories.map( ( cat ) =>
41
- indexedCategories[ cat ] !== undefined
42
- ? indexedCategories[ cat ]
43
- : Infinity
44
- )
45
- );
46
- },
47
- [ populatedCategories ]
48
- );
49
-
50
- const currentCategoryPatterns = useMemo(
51
- () =>
52
- allPatterns.filter( ( pattern ) =>
53
- selectedCategory.name === 'uncategorized'
54
- ? getPatternIndex( pattern ) === Infinity
55
- : pattern.categories?.includes( selectedCategory.name )
56
- ),
57
- [ allPatterns, selectedCategory ]
58
- );
59
-
60
- // Ordering the patterns is important for the async rendering.
61
- const orderedPatterns = useMemo( () => {
62
- return currentCategoryPatterns.sort( ( a, b ) => {
63
- return getPatternIndex( a ) - getPatternIndex( b );
64
- } );
65
- }, [ currentCategoryPatterns, getPatternIndex ] );
66
-
67
- const currentShownPatterns = useAsyncList( orderedPatterns );
68
-
69
- if ( ! currentCategoryPatterns.length ) {
70
- return null;
71
- }
72
-
73
- return (
74
- <div className="block-editor-inserter__panel-content">
75
- <BlockPatternList
76
- shownPatterns={ currentShownPatterns }
77
- blockPatterns={ currentCategoryPatterns }
78
- onClickPattern={ onClick }
79
- label={ selectedCategory.label }
80
- orientation="vertical"
81
- isDraggable
82
- />
83
- </div>
84
- );
85
- }
86
-
87
- function BlockPatternsTabs( {
88
- rootClientId,
89
- onInsert,
90
- onClickCategory,
91
- selectedCategory,
92
- } ) {
93
- const [ showPatternsExplorer, setShowPatternsExplorer ] = useState( false );
34
+ function usePatternsCategories() {
94
35
  const [ allPatterns, allCategories ] = usePatternsState();
95
36
 
96
37
  const hasRegisteredCategory = useCallback(
@@ -138,30 +79,173 @@ function BlockPatternsTabs( {
138
79
  return categories;
139
80
  }, [ allPatterns, allCategories ] );
140
81
 
141
- const patternCategory = selectedCategory
142
- ? selectedCategory
143
- : populatedCategories[ 0 ];
82
+ return populatedCategories;
83
+ }
84
+
85
+ export function BlockPatternsCategoryDialog( {
86
+ rootClientId,
87
+ onInsert,
88
+ category,
89
+ } ) {
90
+ const container = useRef();
91
+
92
+ useEffect( () => {
93
+ const timeout = setTimeout( () => {
94
+ const [ firstTabbable ] = focus.tabbable.find( container.current );
95
+ firstTabbable?.focus();
96
+ } );
97
+ return () => clearTimeout( timeout );
98
+ }, [ category ] );
144
99
 
145
100
  return (
146
- <>
147
- <PatternInserterPanel
148
- selectedCategory={ patternCategory }
149
- patternCategories={ populatedCategories }
150
- onClickCategory={ onClickCategory }
151
- openPatternExplorer={ () => setShowPatternsExplorer( true ) }
101
+ <div
102
+ ref={ container }
103
+ className="block-editor-inserter__patterns-category-panel"
104
+ >
105
+ <BlockPatternsCategoryPanel
106
+ rootClientId={ rootClientId }
107
+ onInsert={ onInsert }
108
+ category={ category }
152
109
  />
153
- { ! showPatternsExplorer && (
154
- <BlockPatternsCategory
155
- rootClientId={ rootClientId }
110
+ </div>
111
+ );
112
+ }
113
+
114
+ export function BlockPatternsCategoryPanel( {
115
+ rootClientId,
116
+ onInsert,
117
+ category,
118
+ } ) {
119
+ const [ allPatterns, , onClick ] = usePatternsState(
120
+ onInsert,
121
+ rootClientId
122
+ );
123
+
124
+ const availableCategories = usePatternsCategories();
125
+ const currentCategoryPatterns = useMemo(
126
+ () =>
127
+ allPatterns.filter( ( pattern ) => {
128
+ if ( category.name !== 'uncategorized' ) {
129
+ return pattern.categories?.includes( category.name );
130
+ }
131
+
132
+ // The uncategorized category should show all the patterns without any category
133
+ // or with no available category.
134
+ const availablePatternCategories =
135
+ pattern.categories?.filter( ( cat ) =>
136
+ availableCategories.find(
137
+ ( availableCategory ) =>
138
+ availableCategory.name === cat
139
+ )
140
+ ) ?? [];
141
+
142
+ return availablePatternCategories.length === 0;
143
+ } ),
144
+ [ allPatterns, category ]
145
+ );
146
+
147
+ const currentShownPatterns = useAsyncList( currentCategoryPatterns );
148
+
149
+ if ( ! currentCategoryPatterns.length ) {
150
+ return null;
151
+ }
152
+
153
+ return (
154
+ <div>
155
+ <div className="block-editor-inserter__patterns-category-panel-title">
156
+ { category.label }
157
+ </div>
158
+ <p>{ category.description }</p>
159
+ <BlockPatternList
160
+ shownPatterns={ currentShownPatterns }
161
+ blockPatterns={ currentCategoryPatterns }
162
+ onClickPattern={ onClick }
163
+ label={ category.label }
164
+ orientation="vertical"
165
+ category={ category.label }
166
+ isDraggable
167
+ />
168
+ </div>
169
+ );
170
+ }
171
+
172
+ function BlockPatternsTabs( {
173
+ onSelectCategory,
174
+ selectedCategory,
175
+ onInsert,
176
+ rootClientId,
177
+ } ) {
178
+ const [ showPatternsExplorer, setShowPatternsExplorer ] = useState( false );
179
+ const categories = usePatternsCategories();
180
+ const isMobile = useViewportMatch( 'medium', '<' );
181
+
182
+ return (
183
+ <>
184
+ { ! isMobile && (
185
+ <div className="block-editor-inserter__block-patterns-tabs-container">
186
+ <nav aria-label={ __( 'Block pattern categories' ) }>
187
+ <ItemGroup
188
+ role="list"
189
+ className="block-editor-inserter__block-patterns-tabs"
190
+ >
191
+ { categories.map( ( category ) => (
192
+ <Item
193
+ role="listitem"
194
+ key={ category.name }
195
+ onClick={ () =>
196
+ onSelectCategory( category )
197
+ }
198
+ className={
199
+ category === selectedCategory
200
+ ? 'block-editor-inserter__patterns-category block-editor-inserter__patterns-selected-category'
201
+ : 'block-editor-inserter__patterns-category'
202
+ }
203
+ aria-label={ category.label }
204
+ aria-current={
205
+ category === selectedCategory
206
+ ? 'true'
207
+ : undefined
208
+ }
209
+ >
210
+ <HStack>
211
+ <FlexBlock>
212
+ { category.label }
213
+ </FlexBlock>
214
+ <Icon icon={ chevronRight } />
215
+ </HStack>
216
+ </Item>
217
+ ) ) }
218
+
219
+ <div
220
+ role="presentation"
221
+ className="block-editor-inserter__patterns-fill-space"
222
+ />
223
+
224
+ <div role="listitem">
225
+ <Button
226
+ className="block-editor-inserter__patterns-explore-button"
227
+ onClick={ () =>
228
+ setShowPatternsExplorer( true )
229
+ }
230
+ variant="secondary"
231
+ >
232
+ { __( 'Explore all patterns' ) }
233
+ </Button>
234
+ </div>
235
+ </ItemGroup>
236
+ </nav>
237
+ </div>
238
+ ) }
239
+ { isMobile && (
240
+ <BlockPatternsTabNavigation
156
241
  onInsert={ onInsert }
157
- selectedCategory={ patternCategory }
158
- populatedCategories={ populatedCategories }
242
+ rootClientId={ rootClientId }
159
243
  />
160
244
  ) }
161
245
  { showPatternsExplorer && (
162
246
  <PatternsExplorerModal
163
- initialCategory={ patternCategory }
164
- patternCategories={ populatedCategories }
247
+ initialCategory={ selectedCategory }
248
+ patternCategories={ categories }
165
249
  onModalClose={ () => setShowPatternsExplorer( false ) }
166
250
  />
167
251
  ) }
@@ -169,4 +253,54 @@ function BlockPatternsTabs( {
169
253
  );
170
254
  }
171
255
 
256
+ function BlockPatternsTabNavigation( { onInsert, rootClientId } ) {
257
+ const categories = usePatternsCategories();
258
+
259
+ return (
260
+ <NavigatorProvider initialPath="/">
261
+ <NavigatorScreen path="/">
262
+ <ItemGroup>
263
+ { categories.map( ( category ) => (
264
+ <NavigatorButton
265
+ key={ category.name }
266
+ path={ `/category/${ category.name }` }
267
+ as={ Item }
268
+ isAction
269
+ >
270
+ <HStack>
271
+ <FlexBlock>{ category.label }</FlexBlock>
272
+ <Icon
273
+ icon={
274
+ isRTL() ? chevronLeft : chevronRight
275
+ }
276
+ />
277
+ </HStack>
278
+ </NavigatorButton>
279
+ ) ) }
280
+ </ItemGroup>
281
+ </NavigatorScreen>
282
+
283
+ { categories.map( ( category ) => (
284
+ <NavigatorScreen
285
+ key={ category.name }
286
+ path={ `/category/${ category.name }` }
287
+ >
288
+ <NavigatorBackButton
289
+ icon={ isRTL() ? chevronRight : chevronLeft }
290
+ isSmall
291
+ aria-label={ __( 'Navigate to the categories list' ) }
292
+ >
293
+ { __( 'Back' ) }
294
+ </NavigatorBackButton>
295
+ <BlockPatternsCategoryPanel
296
+ category={ category }
297
+ rootClientId={ rootClientId }
298
+ onInsert={ onInsert }
299
+ />
300
+ </NavigatorScreen>
301
+ ) ) }
302
+ </NavigatorProvider>
303
+ );
304
+ }
305
+
172
306
  export default BlockPatternsTabs;
@@ -24,7 +24,9 @@ import { useSelect } from '@wordpress/data';
24
24
  import Tips from './tips';
25
25
  import InserterPreviewPanel from './preview-panel';
26
26
  import BlockTypesTab from './block-types-tab';
27
- import BlockPatternsTabs from './block-patterns-tab';
27
+ import BlockPatternsTabs, {
28
+ BlockPatternsCategoryDialog,
29
+ } from './block-patterns-tab';
28
30
  import ReusableBlocksTab from './reusable-blocks-tab';
29
31
  import InserterSearchResults from './search-results';
30
32
  import useInsertionPoint from './hooks/use-insertion-point';
@@ -52,6 +54,7 @@ function InserterMenu(
52
54
  const [ hoveredItem, setHoveredItem ] = useState( null );
53
55
  const [ selectedPatternCategory, setSelectedPatternCategory ] =
54
56
  useState( null );
57
+ const [ selectedTab, setSelectedTab ] = useState( null );
55
58
 
56
59
  const [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ] =
57
60
  useInsertionPoint( {
@@ -144,7 +147,7 @@ function InserterMenu(
144
147
  <BlockPatternsTabs
145
148
  rootClientId={ destinationRootClientId }
146
149
  onInsert={ onInsertPattern }
147
- onClickCategory={ onClickPatternCategory }
150
+ onSelectCategory={ onClickPatternCategory }
148
151
  selectedCategory={ selectedPatternCategory }
149
152
  />
150
153
  ),
@@ -186,6 +189,8 @@ function InserterMenu(
186
189
  },
187
190
  } ) );
188
191
 
192
+ const showPatternPanel =
193
+ selectedTab === 'patterns' && ! filterValue && selectedPatternCategory;
189
194
  const showAsTabs = ! filterValue && ( showPatterns || hasReusableBlocks );
190
195
 
191
196
  return (
@@ -228,6 +233,7 @@ function InserterMenu(
228
233
  showPatterns={ showPatterns }
229
234
  showReusableBlocks={ hasReusableBlocks }
230
235
  prioritizePatterns={ prioritizePatterns }
236
+ onSelect={ setSelectedTab }
231
237
  >
232
238
  { getCurrentTab }
233
239
  </InserterTabs>
@@ -241,6 +247,13 @@ function InserterMenu(
241
247
  { showInserterHelpPanel && hoveredItem && (
242
248
  <InserterPreviewPanel item={ hoveredItem } />
243
249
  ) }
250
+ { showPatternPanel && (
251
+ <BlockPatternsCategoryDialog
252
+ rootClientId={ destinationRootClientId }
253
+ onInsert={ onInsertPattern }
254
+ category={ selectedPatternCategory }
255
+ />
256
+ ) }
244
257
  </div>
245
258
  );
246
259
  }
@@ -22,14 +22,13 @@ $block-inserter-tabs-height: 44px;
22
22
  flex-direction: column;
23
23
  height: 100%;
24
24
  gap: $grid-unit-20;
25
- width: auto;
26
- @include break-medium {
27
- width: $block-inserter-width;
28
- }
29
-
30
25
  &.show-as-tabs {
31
26
  gap: 0;
32
27
  }
28
+
29
+ @include break-medium {
30
+ width: $block-inserter-width;
31
+ }
33
32
  }
34
33
 
35
34
  .block-editor-inserter__popover.is-quick {
@@ -148,10 +147,6 @@ $block-inserter-tabs-height: 44px;
148
147
  padding: $grid-unit-20 $grid-unit-20 0;
149
148
  }
150
149
 
151
- .block-editor-inserter__panel-header-patterns {
152
- padding: $grid-unit-20 $grid-unit-20 0;
153
- }
154
-
155
150
  .block-editor-inserter__panel-content {
156
151
  padding: $grid-unit-20;
157
152
  }
@@ -235,6 +230,92 @@ $block-inserter-tabs-height: 44px;
235
230
  }
236
231
  }
237
232
 
233
+ .block-editor-inserter__patterns-explore-button.components-button {
234
+ padding: $grid-unit-20;
235
+ justify-content: center;
236
+ margin-top: $grid-unit-20;
237
+ width: 100%;
238
+ }
239
+
240
+ .block-editor-inserter__patterns-selected-category.block-editor-inserter__patterns-selected-category {
241
+ color: var(--wp-admin-theme-color);
242
+ position: relative;
243
+
244
+ .components-flex-item {
245
+ filter: brightness(0.95);
246
+ }
247
+
248
+ svg {
249
+ fill: var(--wp-admin-theme-color);
250
+ }
251
+
252
+ &::after {
253
+ content: "";
254
+ position: absolute;
255
+ top: 0;
256
+ bottom: 0;
257
+ left: 0;
258
+ right: 0;
259
+ border-radius: $radius-block-ui;
260
+ opacity: 0.04;
261
+ background: var(--wp-admin-theme-color);
262
+ }
263
+ }
264
+
265
+ .block-editor-inserter__block-patterns-tabs-container {
266
+ height: 100%;
267
+ nav {
268
+ height: 100%;
269
+ }
270
+ }
271
+
272
+ .block-editor-inserter__block-patterns-tabs {
273
+ display: flex;
274
+ flex-direction: column;
275
+ padding: $grid-unit-20;
276
+ overflow-y: auto;
277
+ height: 100%;
278
+ }
279
+
280
+ .block-editor-inserter__patterns-fill-space {
281
+ flex-grow: 1;
282
+ }
283
+
284
+ .block-editor-inserter__patterns-category-panel {
285
+ background: $gray-100;
286
+ border-left: $border-width solid $gray-200;
287
+ border-right: $border-width solid $gray-200;
288
+ position: absolute;
289
+ padding: $grid-unit-40 $grid-unit-30;
290
+ top: 0;
291
+ left: 0;
292
+ height: 100%;
293
+ width: 100%;
294
+ overflow-y: auto;
295
+ scrollbar-gutter: stable both-edges;
296
+
297
+ @include break-medium {
298
+ left: 100%;
299
+ display: block;
300
+ width: 300px;
301
+ }
302
+
303
+ .block-editor-block-patterns-list {
304
+ margin-top: $grid-unit-30;
305
+ }
306
+
307
+ .block-editor-block-preview__container {
308
+ box-shadow: 0 15px 25px rgb(0 0 0 / 7%);
309
+ &:hover {
310
+ box-shadow: 0 0 0 2px $gray-900, 0 15px 25px rgb(0 0 0 / 7%);
311
+ }
312
+ }
313
+
314
+ .block-editor-block-patterns-list__item-title {
315
+ display: none;
316
+ }
317
+ }
318
+
238
319
  .block-editor-inserter__preview-content {
239
320
  min-height: $grid-unit-60 * 3;
240
321
  background: $gray-100;
@@ -388,3 +469,7 @@ $block-inserter-tabs-height: 44px;
388
469
  }
389
470
  }
390
471
  }
472
+
473
+ .block-editor-inserter__patterns-category-panel-title {
474
+ font-size: calc(1.25 * 13px);
475
+ }
@@ -41,14 +41,14 @@ function InserterBlockList( props ) {
41
41
  }
42
42
 
43
43
  const initializeAllClosedMenuState = ( propOverrides ) => {
44
- const result = render( <InserterBlockList { ...propOverrides } /> );
45
- const activeTabs = result.container.querySelectorAll(
44
+ const { container } = render( <InserterBlockList { ...propOverrides } /> );
45
+ const activeTabs = container.querySelectorAll(
46
46
  '.components-panel__body.is-opened button.components-panel__body-toggle'
47
47
  );
48
48
  activeTabs.forEach( ( tab ) => {
49
49
  fireEvent.click( tab );
50
50
  } );
51
- return result;
51
+ return container;
52
52
  };
53
53
 
54
54
  describe( 'InserterMenu', () => {
@@ -92,13 +92,13 @@ describe( 'InserterMenu', () => {
92
92
  } );
93
93
 
94
94
  it( 'should list reusable blocks', () => {
95
- const { container } = initializeAllClosedMenuState();
95
+ const container = initializeAllClosedMenuState();
96
96
  const blocks = container.querySelectorAll(
97
97
  '.block-editor-block-types-list__item-title'
98
98
  );
99
99
 
100
100
  expect( blocks ).toHaveLength( 1 );
101
- expect( blocks[ 0 ].textContent ).toBe( 'My reusable block' );
101
+ expect( blocks[ 0 ] ).toHaveTextContent( 'My reusable block' );
102
102
  } );
103
103
 
104
104
  it( 'should trim whitespace of search terms', () => {
@@ -111,6 +111,6 @@ describe( 'InserterMenu', () => {
111
111
  );
112
112
 
113
113
  expect( blocks ).toHaveLength( 1 );
114
- expect( blocks[ 0 ].textContent ).toBe( 'My reusable block' );
114
+ expect( blocks[ 0 ] ).toHaveTextContent( 'My reusable block' );
115
115
  } );
116
116
  } );
@@ -7,7 +7,13 @@ import { Draggable } from '@wordpress/components';
7
7
  */
8
8
  import BlockDraggableChip from '../block-draggable/draggable-chip';
9
9
 
10
- const InserterDraggableBlocks = ( { isEnabled, blocks, icon, children } ) => {
10
+ const InserterDraggableBlocks = ( {
11
+ isEnabled,
12
+ blocks,
13
+ icon,
14
+ children,
15
+ isPattern,
16
+ } ) => {
11
17
  const transferData = {
12
18
  type: 'inserter',
13
19
  blocks,
@@ -18,7 +24,11 @@ const InserterDraggableBlocks = ( { isEnabled, blocks, icon, children } ) => {
18
24
  __experimentalTransferDataType="wp-blocks"
19
25
  transferData={ transferData }
20
26
  __experimentalDragComponent={
21
- <BlockDraggableChip count={ blocks.length } icon={ icon } />
27
+ <BlockDraggableChip
28
+ count={ blocks.length }
29
+ icon={ icon }
30
+ isPattern={ isPattern }
31
+ />
22
32
  }
23
33
  >
24
34
  { ( { onDraggableStart, onDraggableEnd } ) => {
@@ -30,7 +30,26 @@
30
30
 
31
31
  &:not(:disabled) {
32
32
  &:hover {
33
- color: var(--wp-admin-theme-color) !important;
33
+ .block-editor-block-types-list__item-title {
34
+ color: var(--wp-admin-theme-color) !important;
35
+ filter: brightness(0.95);
36
+ }
37
+
38
+ svg {
39
+ color: var(--wp-admin-theme-color) !important;
40
+ }
41
+
42
+ &::after {
43
+ content: "";
44
+ position: absolute;
45
+ top: 0;
46
+ bottom: 0;
47
+ left: 0;
48
+ right: 0;
49
+ border-radius: $radius-block-ui;
50
+ opacity: 0.04;
51
+ background: var(--wp-admin-theme-color);
52
+ }
34
53
  }
35
54
 
36
55
  &:focus {
@@ -392,7 +392,7 @@ describe( 'Searching for a link', () => {
392
392
  fauxEntitySuggestions.length
393
393
  );
394
394
 
395
- expect( searchInput.getAttribute( 'aria-expanded' ) ).toBe( 'true' );
395
+ expect( searchInput ).toHaveAttribute( 'aria-expanded', 'true' );
396
396
 
397
397
  // Sanity check that a search suggestion shows up corresponding to the data.
398
398
  expect( searchResultElements[ 0 ] ).toHaveTextContent(