@wordpress/block-library 7.16.0 → 7.17.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 (235) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/build/archives/edit.js +1 -0
  3. package/build/archives/edit.js.map +1 -1
  4. package/build/comment-template/edit.js +40 -32
  5. package/build/comment-template/edit.js.map +1 -1
  6. package/build/comments/edit/placeholder.js +1 -4
  7. package/build/comments/edit/placeholder.js.map +1 -1
  8. package/build/comments/index.js +5 -1
  9. package/build/comments/index.js.map +1 -1
  10. package/build/comments-title/edit.js +18 -1
  11. package/build/comments-title/edit.js.map +1 -1
  12. package/build/cover/edit/index.js +2 -2
  13. package/build/cover/edit/index.js.map +1 -1
  14. package/build/cover/edit/use-cover-is-dark.js +8 -7
  15. package/build/cover/edit/use-cover-is-dark.js.map +1 -1
  16. package/build/embed/variations.js +1 -1
  17. package/build/embed/variations.js.map +1 -1
  18. package/build/gallery/transforms.js +3 -3
  19. package/build/gallery/transforms.js.map +1 -1
  20. package/build/gallery/v1/edit.js +1 -1
  21. package/build/gallery/v1/edit.js.map +1 -1
  22. package/build/image/image.js +34 -10
  23. package/build/image/image.js.map +1 -1
  24. package/build/image/transforms.js +1 -7
  25. package/build/image/transforms.js.map +1 -1
  26. package/build/list/transforms.js +9 -1
  27. package/build/list/transforms.js.map +1 -1
  28. package/build/list-item/edit.js +3 -2
  29. package/build/list-item/edit.js.map +1 -1
  30. package/build/list-item/edit.native.js +3 -2
  31. package/build/list-item/edit.native.js.map +1 -1
  32. package/build/list-item/hooks/use-merge.js +19 -22
  33. package/build/list-item/hooks/use-merge.js.map +1 -1
  34. package/build/list-item/hooks/use-outdent-list-item.js +19 -3
  35. package/build/list-item/hooks/use-outdent-list-item.js.map +1 -1
  36. package/build/list-item/index.js +14 -1
  37. package/build/list-item/index.js.map +1 -1
  38. package/build/list-item/utils.js +14 -1
  39. package/build/list-item/utils.js.map +1 -1
  40. package/build/navigation/edit/index.js +35 -22
  41. package/build/navigation/edit/index.js.map +1 -1
  42. package/build/navigation/edit/inner-blocks.js +3 -5
  43. package/build/navigation/edit/inner-blocks.js.map +1 -1
  44. package/build/navigation/edit/use-convert-classic-menu-to-block-menu.js +25 -8
  45. package/build/navigation/edit/use-convert-classic-menu-to-block-menu.js.map +1 -1
  46. package/build/navigation/edit/use-create-navigation-menu.js +1 -1
  47. package/build/navigation/edit/use-create-navigation-menu.js.map +1 -1
  48. package/build/navigation/index.js +4 -1
  49. package/build/navigation/index.js.map +1 -1
  50. package/build/navigation-link/edit.js +1 -1
  51. package/build/navigation-link/edit.js.map +1 -1
  52. package/build/page-list/index.js +1 -5
  53. package/build/page-list/index.js.map +1 -1
  54. package/build/paragraph/edit.js +3 -11
  55. package/build/paragraph/edit.js.map +1 -1
  56. package/build/pattern/edit.js +11 -4
  57. package/build/pattern/edit.js.map +1 -1
  58. package/build/post-comments-form/form.js +1 -2
  59. package/build/post-comments-form/form.js.map +1 -1
  60. package/build/query/edit/inspector-controls/index.js +2 -2
  61. package/build/query/edit/inspector-controls/index.js.map +1 -1
  62. package/build/query/edit/inspector-controls/taxonomy-controls.js +79 -104
  63. package/build/query/edit/inspector-controls/taxonomy-controls.js.map +1 -1
  64. package/build/search/edit.js +2 -1
  65. package/build/search/edit.js.map +1 -1
  66. package/build/site-logo/edit.js +6 -9
  67. package/build/site-logo/edit.js.map +1 -1
  68. package/build/table/state.js +1 -1
  69. package/build/table/state.js.map +1 -1
  70. package/build/table-of-contents/edit.js +1 -4
  71. package/build/table-of-contents/edit.js.map +1 -1
  72. package/build/tag-cloud/edit.js +1 -1
  73. package/build/tag-cloud/edit.js.map +1 -1
  74. package/build/tag-cloud/index.js +8 -0
  75. package/build/tag-cloud/index.js.map +1 -1
  76. package/build-module/archives/edit.js +1 -0
  77. package/build-module/archives/edit.js.map +1 -1
  78. package/build-module/comment-template/edit.js +38 -30
  79. package/build-module/comment-template/edit.js.map +1 -1
  80. package/build-module/comments/edit/placeholder.js +1 -3
  81. package/build-module/comments/edit/placeholder.js.map +1 -1
  82. package/build-module/comments/index.js +5 -1
  83. package/build-module/comments/index.js.map +1 -1
  84. package/build-module/comments-title/edit.js +18 -2
  85. package/build-module/comments-title/edit.js.map +1 -1
  86. package/build-module/cover/edit/index.js +2 -2
  87. package/build-module/cover/edit/index.js.map +1 -1
  88. package/build-module/cover/edit/use-cover-is-dark.js +7 -7
  89. package/build-module/cover/edit/use-cover-is-dark.js.map +1 -1
  90. package/build-module/embed/variations.js +1 -1
  91. package/build-module/embed/variations.js.map +1 -1
  92. package/build-module/gallery/transforms.js +4 -4
  93. package/build-module/gallery/transforms.js.map +1 -1
  94. package/build-module/gallery/v1/edit.js +2 -2
  95. package/build-module/gallery/v1/edit.js.map +1 -1
  96. package/build-module/image/image.js +35 -11
  97. package/build-module/image/image.js.map +1 -1
  98. package/build-module/image/transforms.js +1 -6
  99. package/build-module/image/transforms.js.map +1 -1
  100. package/build-module/list/transforms.js +9 -1
  101. package/build-module/list/transforms.js.map +1 -1
  102. package/build-module/list-item/edit.js +3 -2
  103. package/build-module/list-item/edit.js.map +1 -1
  104. package/build-module/list-item/edit.native.js +3 -2
  105. package/build-module/list-item/edit.native.js.map +1 -1
  106. package/build-module/list-item/hooks/use-merge.js +19 -21
  107. package/build-module/list-item/hooks/use-merge.js.map +1 -1
  108. package/build-module/list-item/hooks/use-outdent-list-item.js +19 -3
  109. package/build-module/list-item/hooks/use-outdent-list-item.js.map +1 -1
  110. package/build-module/list-item/index.js +14 -1
  111. package/build-module/list-item/index.js.map +1 -1
  112. package/build-module/list-item/utils.js +14 -1
  113. package/build-module/list-item/utils.js.map +1 -1
  114. package/build-module/navigation/edit/index.js +35 -22
  115. package/build-module/navigation/edit/index.js.map +1 -1
  116. package/build-module/navigation/edit/inner-blocks.js +3 -5
  117. package/build-module/navigation/edit/inner-blocks.js.map +1 -1
  118. package/build-module/navigation/edit/use-convert-classic-menu-to-block-menu.js +25 -8
  119. package/build-module/navigation/edit/use-convert-classic-menu-to-block-menu.js.map +1 -1
  120. package/build-module/navigation/edit/use-create-navigation-menu.js +1 -1
  121. package/build-module/navigation/edit/use-create-navigation-menu.js.map +1 -1
  122. package/build-module/navigation/index.js +4 -1
  123. package/build-module/navigation/index.js.map +1 -1
  124. package/build-module/navigation-link/edit.js +1 -1
  125. package/build-module/navigation-link/edit.js.map +1 -1
  126. package/build-module/page-list/index.js +1 -5
  127. package/build-module/page-list/index.js.map +1 -1
  128. package/build-module/paragraph/edit.js +3 -10
  129. package/build-module/paragraph/edit.js.map +1 -1
  130. package/build-module/pattern/edit.js +10 -3
  131. package/build-module/pattern/edit.js.map +1 -1
  132. package/build-module/post-comments-form/form.js +2 -3
  133. package/build-module/post-comments-form/form.js.map +1 -1
  134. package/build-module/query/edit/inspector-controls/index.js +4 -4
  135. package/build-module/query/edit/inspector-controls/index.js.map +1 -1
  136. package/build-module/query/edit/inspector-controls/taxonomy-controls.js +81 -102
  137. package/build-module/query/edit/inspector-controls/taxonomy-controls.js.map +1 -1
  138. package/build-module/search/edit.js +3 -2
  139. package/build-module/search/edit.js.map +1 -1
  140. package/build-module/site-logo/edit.js +6 -9
  141. package/build-module/site-logo/edit.js.map +1 -1
  142. package/build-module/table/state.js +2 -2
  143. package/build-module/table/state.js.map +1 -1
  144. package/build-module/table-of-contents/edit.js +1 -3
  145. package/build-module/table-of-contents/edit.js.map +1 -1
  146. package/build-module/tag-cloud/edit.js +1 -1
  147. package/build-module/tag-cloud/edit.js.map +1 -1
  148. package/build-module/tag-cloud/index.js +8 -0
  149. package/build-module/tag-cloud/index.js.map +1 -1
  150. package/build-style/archives/style-rtl.css +4 -0
  151. package/build-style/archives/style.css +4 -0
  152. package/build-style/comments/editor-rtl.css +1 -0
  153. package/build-style/comments/editor.css +1 -0
  154. package/build-style/comments/style-rtl.css +1 -0
  155. package/build-style/comments/style.css +1 -0
  156. package/build-style/cover/editor-rtl.css +4 -0
  157. package/build-style/cover/editor.css +4 -0
  158. package/build-style/editor-rtl.css +9 -17
  159. package/build-style/editor.css +9 -17
  160. package/build-style/group/editor-rtl.css +1 -0
  161. package/build-style/group/editor.css +1 -0
  162. package/build-style/image/editor-rtl.css +1 -1
  163. package/build-style/image/editor.css +1 -1
  164. package/build-style/navigation/editor-rtl.css +1 -0
  165. package/build-style/navigation/editor.css +1 -0
  166. package/build-style/paragraph/editor-rtl.css +0 -16
  167. package/build-style/paragraph/editor.css +0 -16
  168. package/build-style/site-logo/editor-rtl.css +1 -0
  169. package/build-style/site-logo/editor.css +1 -0
  170. package/build-style/style-rtl.css +8 -0
  171. package/build-style/style.css +8 -0
  172. package/build-style/tag-cloud/style-rtl.css +3 -0
  173. package/build-style/tag-cloud/style.css +3 -0
  174. package/package.json +28 -28
  175. package/src/archives/edit.js +1 -0
  176. package/src/archives/style.scss +5 -0
  177. package/src/avatar/index.php +1 -1
  178. package/src/code/test/__snapshots__/edit.native.js.snap +13 -0
  179. package/src/code/test/edit.native.js +46 -32
  180. package/src/comment-template/edit.js +47 -34
  181. package/src/comments/block.json +5 -1
  182. package/src/comments/edit/placeholder.js +1 -7
  183. package/src/comments/style.scss +2 -0
  184. package/src/comments-title/edit.js +24 -1
  185. package/src/cover/edit/index.js +2 -7
  186. package/src/cover/edit/use-cover-is-dark.js +11 -11
  187. package/src/cover/editor.scss +7 -0
  188. package/src/embed/variations.js +1 -1
  189. package/src/gallery/transforms.js +8 -5
  190. package/src/gallery/v1/edit.js +2 -2
  191. package/src/group/editor.scss +1 -0
  192. package/src/image/editor.scss +4 -1
  193. package/src/image/image.js +59 -29
  194. package/src/image/transforms.js +1 -7
  195. package/src/list/test/edit.native.js +102 -3
  196. package/src/list/transforms.js +11 -0
  197. package/src/list-item/block.json +14 -1
  198. package/src/list-item/edit.js +2 -1
  199. package/src/list-item/edit.native.js +2 -1
  200. package/src/list-item/hooks/use-merge.js +4 -23
  201. package/src/list-item/hooks/use-outdent-list-item.js +6 -2
  202. package/src/navigation/block.json +4 -1
  203. package/src/navigation/edit/index.js +49 -23
  204. package/src/navigation/edit/inner-blocks.js +2 -5
  205. package/src/navigation/edit/use-convert-classic-menu-to-block-menu.js +32 -5
  206. package/src/navigation/edit/use-create-navigation-menu.js +2 -5
  207. package/src/navigation/editor.scss +1 -0
  208. package/src/navigation/index.php +103 -6
  209. package/src/navigation-link/edit.js +1 -1
  210. package/src/navigation-link/index.php +8 -1
  211. package/src/navigation-submenu/index.php +8 -1
  212. package/src/page-list/block.json +1 -5
  213. package/src/page-list/index.php +8 -6
  214. package/src/paragraph/edit.js +1 -14
  215. package/src/paragraph/editor.scss +0 -20
  216. package/src/pattern/edit.js +10 -3
  217. package/src/post-comments-form/form.js +2 -3
  218. package/src/query/edit/inspector-controls/index.js +4 -3
  219. package/src/query/edit/inspector-controls/taxonomy-controls.js +82 -87
  220. package/src/rss/index.php +3 -3
  221. package/src/search/edit.js +6 -1
  222. package/src/search/index.php +21 -13
  223. package/src/site-logo/edit.js +10 -8
  224. package/src/site-logo/editor.scss +1 -0
  225. package/src/table/state.js +2 -2
  226. package/src/table-of-contents/edit.js +1 -3
  227. package/src/tag-cloud/block.json +8 -0
  228. package/src/tag-cloud/edit.js +1 -1
  229. package/src/tag-cloud/style.scss +3 -0
  230. package/src/template-part/index.php +4 -0
  231. package/build/paragraph/drop-zone.js +0 -99
  232. package/build/paragraph/drop-zone.js.map +0 -1
  233. package/build-module/paragraph/drop-zone.js +0 -88
  234. package/build-module/paragraph/drop-zone.js.map +0 -1
  235. package/src/paragraph/drop-zone.js +0 -105
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import {
5
5
  changeTextOfRichText,
6
+ changeAndSelectTextOfRichText,
6
7
  fireEvent,
7
8
  getEditorHtml,
8
9
  initializeEditor,
@@ -18,6 +19,7 @@ import {
18
19
  */
19
20
  import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks';
20
21
  import { registerCoreBlocks } from '@wordpress/block-library';
22
+ import { BACKSPACE } from '@wordpress/keycodes';
21
23
 
22
24
  describe( 'List block', () => {
23
25
  beforeAll( () => {
@@ -210,7 +212,7 @@ describe( 'List block', () => {
210
212
  <!-- /wp:list-item -->
211
213
  <!-- wp:list-item -->
212
214
  <li>Item 2</li>
213
- <!-- /wp:list-item -->
215
+ <!-- /wp:list-item -->
214
216
  <!-- wp:list-item -->
215
217
  <li>Item 3</li>
216
218
  <!-- /wp:list-item --></ul>
@@ -238,7 +240,7 @@ describe( 'List block', () => {
238
240
  <!-- /wp:list-item -->
239
241
  <!-- wp:list-item -->
240
242
  <li>Item 2</li>
241
- <!-- /wp:list-item -->
243
+ <!-- /wp:list-item -->
242
244
  <!-- wp:list-item -->
243
245
  <li>Item 3</li>
244
246
  <!-- /wp:list-item --></ul>
@@ -277,7 +279,7 @@ describe( 'List block', () => {
277
279
  <!-- /wp:list-item -->
278
280
  <!-- wp:list-item -->
279
281
  <li>Item 2</li>
280
- <!-- /wp:list-item -->
282
+ <!-- /wp:list-item -->
281
283
  <!-- wp:list-item -->
282
284
  <li>Item 3</li>
283
285
  <!-- /wp:list-item --></ul>
@@ -311,4 +313,101 @@ describe( 'List block', () => {
311
313
 
312
314
  expect( getEditorHtml() ).toMatchSnapshot();
313
315
  } );
316
+
317
+ it( 'merges with other lists', async () => {
318
+ const initialHtml = `<!-- wp:list -->
319
+ <ul><!-- wp:list-item -->
320
+ <li>One</li><!-- /wp:list-item --></ul>
321
+ <!-- /wp:list --><!-- wp:list -->
322
+ <ul><!-- wp:list-item -->
323
+ <li>Two</li><!-- /wp:list-item --></ul>
324
+ <!-- /wp:list -->`;
325
+
326
+ const screen = await initializeEditor( {
327
+ initialHtml,
328
+ } );
329
+
330
+ // Select List block
331
+ const listBlock = screen.getByA11yLabel( /List Block\. Row 2/ );
332
+ fireEvent.press( listBlock );
333
+
334
+ // Select List Item block
335
+ const listItemBlock = within( listBlock ).getByA11yLabel(
336
+ /List item Block\. Row 1/
337
+ );
338
+ fireEvent.press( listItemBlock );
339
+
340
+ // With cursor positioned at the beginning of the first List Item, press
341
+ // backward delete
342
+ const listItemField =
343
+ within( listItemBlock ).getByA11yLabel( /Text input. .*Two.*/ );
344
+ changeAndSelectTextOfRichText( listItemField, 'Two' );
345
+ fireEvent( listItemField, 'onKeyDown', {
346
+ nativeEvent: {},
347
+ preventDefault() {},
348
+ keyCode: BACKSPACE,
349
+ } );
350
+
351
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
352
+ "<!-- wp:list -->
353
+ <ul><!-- wp:list-item -->
354
+ <li>One</li>
355
+ <!-- /wp:list-item -->
356
+
357
+ <!-- wp:list-item -->
358
+ <li>Two</li>
359
+ <!-- /wp:list-item --></ul>
360
+ <!-- /wp:list -->"
361
+ ` );
362
+ } );
363
+
364
+ it( 'unwraps list items when attempting to merge with non-list block', async () => {
365
+ const initialHtml = `<!-- wp:paragraph -->
366
+ <p>A quick brown fox.</p>
367
+ <!-- /wp:paragraph -->
368
+ <!-- wp:list -->
369
+ <ul><!-- wp:list-item -->
370
+ <li>One</li><!-- /wp:list-item --><!-- wp:list-item -->
371
+ <li>Two</li><!-- /wp:list-item --></ul>
372
+ <!-- /wp:list -->`;
373
+
374
+ const screen = await initializeEditor( {
375
+ initialHtml,
376
+ } );
377
+
378
+ // Select List block
379
+ const listBlock = screen.getByA11yLabel( /List Block\. Row 2/ );
380
+ fireEvent.press( listBlock );
381
+
382
+ // Select List Item block
383
+ const listItemBlock = within( listBlock ).getByA11yLabel(
384
+ /List item Block\. Row 1/
385
+ );
386
+ fireEvent.press( listItemBlock );
387
+
388
+ // With cursor positioned at the beginning of the first List Item, press
389
+ // backward delete
390
+ const listItemField =
391
+ within( listItemBlock ).getByA11yLabel( /Text input. .*One.*/ );
392
+ changeAndSelectTextOfRichText( listItemField, 'One' );
393
+ fireEvent( listItemField, 'onKeyDown', {
394
+ nativeEvent: {},
395
+ preventDefault() {},
396
+ keyCode: BACKSPACE,
397
+ } );
398
+
399
+ expect( getEditorHtml() ).toMatchInlineSnapshot( `
400
+ "<!-- wp:paragraph -->
401
+ <p>A quick brown fox.</p>
402
+ <!-- /wp:paragraph -->
403
+
404
+ <!-- wp:paragraph -->
405
+ <p>One</p>
406
+ <!-- /wp:paragraph -->
407
+
408
+ <!-- wp:paragraph -->
409
+ <p>Two</p>
410
+ <!-- /wp:paragraph -->"
411
+ ` );
412
+ } );
314
413
  } );
@@ -114,6 +114,17 @@ const transforms = {
114
114
  );
115
115
  },
116
116
  } ) ),
117
+ {
118
+ type: 'block',
119
+ blocks: [ '*' ],
120
+ transform: ( _attributes, childBlocks ) => {
121
+ return getListContentFlat( childBlocks ).map( ( content ) =>
122
+ createBlock( 'core/paragraph', {
123
+ content,
124
+ } )
125
+ );
126
+ },
127
+ },
117
128
  ],
118
129
  };
119
130
 
@@ -21,6 +21,19 @@
21
21
  },
22
22
  "supports": {
23
23
  "className": false,
24
- "__experimentalSelector": "li"
24
+ "__experimentalSelector": "li",
25
+ "typography": {
26
+ "fontSize": true,
27
+ "lineHeight": true,
28
+ "__experimentalFontFamily": true,
29
+ "__experimentalFontWeight": true,
30
+ "__experimentalFontStyle": true,
31
+ "__experimentalTextTransform": true,
32
+ "__experimentalTextDecoration": true,
33
+ "__experimentalLetterSpacing": true,
34
+ "__experimentalDefaultControls": {
35
+ "fontSize": true
36
+ }
37
+ }
25
38
  }
26
39
  }
@@ -60,6 +60,7 @@ export default function ListItemEdit( {
60
60
  setAttributes,
61
61
  onReplace,
62
62
  clientId,
63
+ mergeBlocks,
63
64
  } ) {
64
65
  const { placeholder, content } = attributes;
65
66
  const blockProps = useBlockProps( { ref: useCopy( clientId ) } );
@@ -70,7 +71,7 @@ export default function ListItemEdit( {
70
71
  const useEnterRef = useEnter( { content, clientId } );
71
72
  const useSpaceRef = useSpace( clientId );
72
73
  const onSplit = useSplit( clientId );
73
- const onMerge = useMerge( clientId );
74
+ const onMerge = useMerge( clientId, mergeBlocks );
74
75
  return (
75
76
  <>
76
77
  <li { ...innerBlocksProps }>
@@ -35,6 +35,7 @@ export default function ListItemEdit( {
35
35
  onReplace,
36
36
  clientId,
37
37
  style,
38
+ mergeBlocks,
38
39
  } ) {
39
40
  const [ contentWidth, setContentWidth ] = useState();
40
41
  const { placeholder, content } = attributes;
@@ -119,7 +120,7 @@ export default function ListItemEdit( {
119
120
  const preventDefault = useRef( false );
120
121
  const { onEnter } = useEnter( { content, clientId }, preventDefault );
121
122
  const onSplit = useSplit( clientId );
122
- const onMerge = useMerge( clientId );
123
+ const onMerge = useMerge( clientId, mergeBlocks );
123
124
  const onSplitList = useCallback(
124
125
  ( value ) => {
125
126
  if ( ! preventDefault.current ) {
@@ -3,7 +3,6 @@
3
3
  */
4
4
  import { useRegistry, useDispatch, useSelect } from '@wordpress/data';
5
5
  import { store as blockEditorStore } from '@wordpress/block-editor';
6
- import { getDefaultBlockName, switchToBlockType } from '@wordpress/blocks';
7
6
 
8
7
  /**
9
8
  * Internal dependencies
@@ -12,7 +11,7 @@ import useOutdentListItem from './use-outdent-list-item';
12
11
 
13
12
  import { name as listItemName } from '../block.json';
14
13
 
15
- export default function useMerge( clientId ) {
14
+ export default function useMerge( clientId, onMerge ) {
16
15
  const registry = useRegistry();
17
16
  const {
18
17
  getPreviousBlockClientId,
@@ -20,9 +19,8 @@ export default function useMerge( clientId ) {
20
19
  getBlockOrder,
21
20
  getBlockRootClientId,
22
21
  getBlockName,
23
- getBlock,
24
22
  } = useSelect( blockEditorStore );
25
- const { mergeBlocks, moveBlocksToPosition, replaceBlock, selectBlock } =
23
+ const { mergeBlocks, moveBlocksToPosition } =
26
24
  useDispatch( blockEditorStore );
27
25
  const [ , outdentListItem ] = useOutdentListItem( clientId );
28
26
 
@@ -79,29 +77,12 @@ export default function useMerge( clientId ) {
79
77
  return getBlockOrder( order[ 0 ] )[ 0 ];
80
78
  }
81
79
 
82
- function switchToDefaultBlockType( forward ) {
83
- const rootClientId = getBlockRootClientId( clientId );
84
- const replacement = switchToBlockType(
85
- getBlock( rootClientId ),
86
- getDefaultBlockName()
87
- );
88
- const indexToSelect = forward ? replacement.length - 1 : 0;
89
- const initialPosition = forward ? -1 : 0;
90
- registry.batch( () => {
91
- replaceBlock( rootClientId, replacement );
92
- selectBlock(
93
- replacement[ indexToSelect ].clientId,
94
- initialPosition
95
- );
96
- } );
97
- }
98
-
99
80
  return ( forward ) => {
100
81
  if ( forward ) {
101
82
  const nextBlockClientId = getNextId( clientId );
102
83
 
103
84
  if ( ! nextBlockClientId ) {
104
- switchToDefaultBlockType( forward );
85
+ onMerge( forward );
105
86
  return;
106
87
  }
107
88
 
@@ -134,7 +115,7 @@ export default function useMerge( clientId ) {
134
115
  mergeBlocks( trailingId, clientId );
135
116
  } );
136
117
  } else {
137
- switchToDefaultBlockType( forward );
118
+ onMerge( forward );
138
119
  }
139
120
  }
140
121
  };
@@ -20,12 +20,16 @@ export default function useOutdentListItem( clientId ) {
20
20
  const registry = useRegistry();
21
21
  const { canOutdent } = useSelect(
22
22
  ( innerSelect ) => {
23
- const { getBlockRootClientId } = innerSelect( blockEditorStore );
23
+ const { getBlockRootClientId, getBlockName } =
24
+ innerSelect( blockEditorStore );
24
25
  const grandParentId = getBlockRootClientId(
25
26
  getBlockRootClientId( clientId )
26
27
  );
28
+ const grandParentName = getBlockName( grandParentId );
29
+ const isListItem = grandParentName === listItemName;
30
+
27
31
  return {
28
- canOutdent: !! grandParentId,
32
+ canOutdent: isListItem,
29
33
  };
30
34
  },
31
35
  [ clientId ]
@@ -67,6 +67,10 @@
67
67
  "maxNestingLevel": {
68
68
  "type": "number",
69
69
  "default": 5
70
+ },
71
+ "templateLock": {
72
+ "type": [ "string", "boolean" ],
73
+ "enum": [ "all", "insert", "contentOnly", false ]
70
74
  }
71
75
  },
72
76
  "providesContext": {
@@ -88,7 +92,6 @@
88
92
  },
89
93
  "supports": {
90
94
  "align": [ "wide", "full" ],
91
- "anchor": true,
92
95
  "html": false,
93
96
  "inserter": true,
94
97
  "typography": {
@@ -86,6 +86,7 @@ function Navigation( {
86
86
  openSubmenusOnClick,
87
87
  overlayMenu,
88
88
  showSubmenuIcon,
89
+ templateLock,
89
90
  layout: {
90
91
  justifyContent,
91
92
  orientation = 'horizontal',
@@ -107,7 +108,7 @@ function Navigation( {
107
108
 
108
109
  // Preload classic menus, so that they don't suddenly pop-in when viewing
109
110
  // the Select Menu dropdown.
110
- useNavigationEntities();
111
+ const { menus: classicMenus } = useNavigationEntities();
111
112
 
112
113
  const [ showNavigationMenuStatusNotice, hideNavigationMenuStatusNotice ] =
113
114
  useNavigationNotice( {
@@ -216,6 +217,20 @@ function Navigation( {
216
217
  const navMenuResolvedButMissing =
217
218
  hasResolvedNavigationMenus && isNavigationMenuMissing;
218
219
 
220
+ const {
221
+ convert: convertClassicMenu,
222
+ status: classicMenuConversionStatus,
223
+ error: classicMenuConversionError,
224
+ } = useConvertClassicToBlockMenu( clientId );
225
+
226
+ const isConvertingClassicMenu =
227
+ classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_PENDING;
228
+
229
+ // Only autofallback to published menus.
230
+ const fallbackNavigationMenus = navigationMenus?.filter(
231
+ ( menu ) => menu.status === 'publish'
232
+ );
233
+
219
234
  // Attempt to retrieve and prioritize any existing navigation menu unless:
220
235
  // - the are uncontrolled inner blocks already present in the block.
221
236
  // - the user is creating a new menu.
@@ -228,23 +243,17 @@ function Navigation( {
228
243
  hasUncontrolledInnerBlocks ||
229
244
  isCreatingNavigationMenu ||
230
245
  ref ||
231
- ! navigationMenus?.length
246
+ ! fallbackNavigationMenus?.length
232
247
  ) {
233
248
  return;
234
249
  }
235
250
 
236
- navigationMenus.sort( ( menuA, menuB ) => {
251
+ fallbackNavigationMenus.sort( ( menuA, menuB ) => {
237
252
  const menuADate = new Date( menuA.date );
238
253
  const menuBDate = new Date( menuB.date );
239
254
  return menuADate.getTime() < menuBDate.getTime();
240
255
  } );
241
256
 
242
- // Only autofallback to published menus.
243
- const fallbackNavigationMenus = navigationMenus.filter(
244
- ( menu ) => menu.status === 'publish'
245
- );
246
- if ( fallbackNavigationMenus.length === 0 ) return;
247
-
248
257
  /**
249
258
  * This fallback displays (both in editor and on front)
250
259
  * a list of pages only if no menu (user assigned or
@@ -256,16 +265,26 @@ function Navigation( {
256
265
  setRef( fallbackNavigationMenus[ 0 ].id );
257
266
  }, [ navigationMenus ] );
258
267
 
259
- const navRef = useRef();
268
+ useEffect( () => {
269
+ if (
270
+ ! hasResolvedNavigationMenus ||
271
+ isConvertingClassicMenu ||
272
+ fallbackNavigationMenus?.length > 0 ||
273
+ classicMenus?.length !== 1
274
+ ) {
275
+ return false;
276
+ }
260
277
 
261
- const {
262
- convert: convertClassicMenu,
263
- status: classicMenuConversionStatus,
264
- error: classicMenuConversionError,
265
- } = useConvertClassicToBlockMenu( clientId );
278
+ // If there's non fallback navigation menus and
279
+ // only one classic menu then create a new navigation menu based on it.
280
+ convertClassicMenu(
281
+ classicMenus[ 0 ].id,
282
+ classicMenus[ 0 ].name,
283
+ 'publish'
284
+ );
285
+ }, [ hasResolvedNavigationMenus ] );
266
286
 
267
- const isConvertingClassicMenu =
268
- classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_PENDING;
287
+ const navRef = useRef();
269
288
 
270
289
  // The standard HTML5 tag for the block wrapper.
271
290
  const TagName = 'nav';
@@ -280,10 +299,11 @@ function Navigation( {
280
299
  ! isCreatingNavigationMenu &&
281
300
  ! isConvertingClassicMenu &&
282
301
  hasResolvedNavigationMenus &&
302
+ classicMenus?.length === 0 &&
283
303
  ! hasUncontrolledInnerBlocks;
284
304
 
285
305
  useEffect( () => {
286
- if ( isPlaceholder && ! ref ) {
306
+ if ( isPlaceholder ) {
287
307
  /**
288
308
  * this fallback only displays (both in editor and on front)
289
309
  * the list of pages block if no menu is available as a fallback.
@@ -476,7 +496,7 @@ function Navigation( {
476
496
  );
477
497
  } catch {
478
498
  showMenuAutoPublishDraftNotice(
479
- __( 'Error ocurred while publishing the navigation menu.' )
499
+ __( 'Error occurred while publishing the navigation menu.' )
480
500
  );
481
501
  }
482
502
  }, [ isDraftNavigationMenu, navigationMenu ] );
@@ -642,7 +662,8 @@ function Navigation( {
642
662
  onSelectClassicMenu={ async ( classicMenu ) => {
643
663
  const navMenu = await convertClassicMenu(
644
664
  classicMenu.id,
645
- classicMenu.name
665
+ classicMenu.name,
666
+ 'draft'
646
667
  );
647
668
  if ( navMenu ) {
648
669
  handleUpdateMenu( navMenu.id, {
@@ -686,6 +707,7 @@ function Navigation( {
686
707
  <UnsavedInnerBlocks
687
708
  blocks={ uncontrolledInnerBlocks }
688
709
  clientId={ clientId }
710
+ templateLock={ templateLock }
689
711
  navigationMenus={ navigationMenus }
690
712
  hasSelection={ isSelected || isInnerBlockSelected }
691
713
  hasSavedUnsavedInnerBlocks={
@@ -723,7 +745,8 @@ function Navigation( {
723
745
  onSelectClassicMenu={ async ( classicMenu ) => {
724
746
  const navMenu = await convertClassicMenu(
725
747
  classicMenu.id,
726
- classicMenu.name
748
+ classicMenu.name,
749
+ 'draft'
727
750
  );
728
751
  if ( navMenu ) {
729
752
  handleUpdateMenu( navMenu.id, {
@@ -808,7 +831,8 @@ function Navigation( {
808
831
  onSelectClassicMenu={ async ( classicMenu ) => {
809
832
  const navMenu = await convertClassicMenu(
810
833
  classicMenu.id,
811
- classicMenu.name
834
+ classicMenu.name,
835
+ 'draft'
812
836
  );
813
837
  if ( navMenu ) {
814
838
  handleUpdateMenu( navMenu.id, {
@@ -836,7 +860,8 @@ function Navigation( {
836
860
  onSelectClassicMenu={ async ( classicMenu ) => {
837
861
  const navMenu = await convertClassicMenu(
838
862
  classicMenu.id,
839
- classicMenu.name
863
+ classicMenu.name,
864
+ 'draft'
840
865
  );
841
866
  if ( navMenu ) {
842
867
  handleUpdateMenu( navMenu.id, {
@@ -921,6 +946,7 @@ function Navigation( {
921
946
  hasCustomPlaceholder={
922
947
  !! CustomPlaceholder
923
948
  }
949
+ templateLock={ templateLock }
924
950
  orientation={ orientation }
925
951
  />
926
952
  ) }
@@ -40,6 +40,7 @@ export default function NavigationInnerBlocks( {
40
40
  clientId,
41
41
  hasCustomPlaceholder,
42
42
  orientation,
43
+ templateLock,
43
44
  } ) {
44
45
  const {
45
46
  isImmediateParentOfSelectedBlock,
@@ -115,6 +116,7 @@ export default function NavigationInnerBlocks( {
115
116
  __experimentalDefaultBlock: DEFAULT_BLOCK,
116
117
  __experimentalDirectInsert: shouldDirectInsert,
117
118
  orientation,
119
+ templateLock,
118
120
 
119
121
  // As an exception to other blocks which feature nesting, show
120
122
  // the block appender even when a child block is selected.
@@ -129,11 +131,6 @@ export default function NavigationInnerBlocks( {
129
131
  parentOrChildHasSelection
130
132
  ? InnerBlocks.ButtonBlockAppender
131
133
  : false,
132
-
133
- // Template lock set to false here so that the Nav
134
- // Block on the experimental menus screen does not
135
- // inherit templateLock={ 'all' }.
136
- templateLock: false,
137
134
  __experimentalLayout: LAYOUT,
138
135
  placeholder: showPlaceholder ? placeholder : undefined,
139
136
  }
@@ -17,6 +17,10 @@ export const CLASSIC_MENU_CONVERSION_ERROR = 'error';
17
17
  export const CLASSIC_MENU_CONVERSION_PENDING = 'pending';
18
18
  export const CLASSIC_MENU_CONVERSION_IDLE = 'idle';
19
19
 
20
+ // This is needed to ensure that multiple components using this hook
21
+ // do not import the same classic menu twice.
22
+ let classicMenuBeingConvertedId = null;
23
+
20
24
  function useConvertClassicToBlockMenu( clientId ) {
21
25
  /*
22
26
  * The wp_navigation post is created as a draft so the changes on the frontend and
@@ -32,7 +36,11 @@ function useConvertClassicToBlockMenu( clientId ) {
32
36
  const [ status, setStatus ] = useState( CLASSIC_MENU_CONVERSION_IDLE );
33
37
  const [ error, setError ] = useState( null );
34
38
 
35
- async function convertClassicMenuToBlockMenu( menuId, menuName ) {
39
+ async function convertClassicMenuToBlockMenu(
40
+ menuId,
41
+ menuName,
42
+ postStatus = 'publish'
43
+ ) {
36
44
  let navigationMenu;
37
45
  let classicMenuItems;
38
46
 
@@ -76,7 +84,8 @@ function useConvertClassicToBlockMenu( clientId ) {
76
84
  try {
77
85
  navigationMenu = await createNavigationMenu(
78
86
  menuName,
79
- innerBlocks
87
+ innerBlocks,
88
+ postStatus
80
89
  );
81
90
 
82
91
  /**
@@ -91,7 +100,7 @@ function useConvertClassicToBlockMenu( clientId ) {
91
100
  'wp_navigation',
92
101
  navigationMenu.id,
93
102
  {
94
- status: 'publish',
103
+ status: postStatus,
95
104
  },
96
105
  { throwOnError: true }
97
106
  );
@@ -111,7 +120,15 @@ function useConvertClassicToBlockMenu( clientId ) {
111
120
  return navigationMenu;
112
121
  }
113
122
 
114
- const convert = useCallback( async ( menuId, menuName ) => {
123
+ const convert = useCallback( async ( menuId, menuName, postStatus ) => {
124
+ // Check whether this classic menu is being imported already.
125
+ if ( classicMenuBeingConvertedId === menuId ) {
126
+ return;
127
+ }
128
+
129
+ // Set the ID for the currently importing classic menu.
130
+ classicMenuBeingConvertedId = menuId;
131
+
115
132
  if ( ! menuId || ! menuName ) {
116
133
  setError( 'Unable to convert menu. Missing menu details.' );
117
134
  setStatus( CLASSIC_MENU_CONVERSION_ERROR );
@@ -121,15 +138,25 @@ function useConvertClassicToBlockMenu( clientId ) {
121
138
  setStatus( CLASSIC_MENU_CONVERSION_PENDING );
122
139
  setError( null );
123
140
 
124
- return await convertClassicMenuToBlockMenu( menuId, menuName )
141
+ return await convertClassicMenuToBlockMenu(
142
+ menuId,
143
+ menuName,
144
+ postStatus
145
+ )
125
146
  .then( ( navigationMenu ) => {
126
147
  setStatus( CLASSIC_MENU_CONVERSION_SUCCESS );
148
+ // Reset the ID for the currently importing classic menu.
149
+ classicMenuBeingConvertedId = null;
127
150
  return navigationMenu;
128
151
  } )
129
152
  .catch( ( err ) => {
130
153
  setError( err?.message );
154
+ // Reset the ID for the currently importing classic menu.
131
155
  setStatus( CLASSIC_MENU_CONVERSION_ERROR );
132
156
 
157
+ // Reset the ID for the currently importing classic menu.
158
+ classicMenuBeingConvertedId = null;
159
+
133
160
  // Rethrow error for debugging.
134
161
  throw new Error(
135
162
  sprintf(
@@ -16,10 +16,7 @@ export const CREATE_NAVIGATION_MENU_ERROR = 'error';
16
16
  export const CREATE_NAVIGATION_MENU_PENDING = 'pending';
17
17
  export const CREATE_NAVIGATION_MENU_IDLE = 'idle';
18
18
 
19
- export default function useCreateNavigationMenu(
20
- clientId,
21
- postStatus = 'publish'
22
- ) {
19
+ export default function useCreateNavigationMenu( clientId ) {
23
20
  const [ status, setStatus ] = useState( CREATE_NAVIGATION_MENU_IDLE );
24
21
  const [ value, setValue ] = useState( null );
25
22
  const [ error, setError ] = useState( null );
@@ -30,7 +27,7 @@ export default function useCreateNavigationMenu(
30
27
  // This callback uses data from the two placeholder steps and only creates
31
28
  // a new navigation menu when the user completes the final step.
32
29
  const create = useCallback(
33
- async ( title = null, blocks = [] ) => {
30
+ async ( title = null, blocks = [], postStatus ) => {
34
31
  // Guard against creating Navigations without a title.
35
32
  // Note you can pass no title, but if one is passed it must be
36
33
  // a string otherwise the title may end up being empty.
@@ -291,6 +291,7 @@ $color-control-label-height: 20px;
291
291
  bottom: 0;
292
292
  left: 0;
293
293
  pointer-events: none;
294
+ border: $border-width dashed currentColor;
294
295
  @include placeholder-style();
295
296
 
296
297
  // Inherit border radius from style variations.