@wordpress/block-library 6.0.12 → 6.0.16

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 (139) hide show
  1. package/build/group/edit.native.js +1 -1
  2. package/build/group/edit.native.js.map +1 -1
  3. package/build/group/index.js +3 -1
  4. package/build/group/index.js.map +1 -1
  5. package/build/image/image.js +30 -6
  6. package/build/image/image.js.map +1 -1
  7. package/build/navigation/deprecated.js +1 -3
  8. package/build/navigation/deprecated.js.map +1 -1
  9. package/build/navigation/edit/index.js +109 -43
  10. package/build/navigation/edit/index.js.map +1 -1
  11. package/build/navigation/edit/navigation-menu-selector.js +10 -3
  12. package/build/navigation/edit/navigation-menu-selector.js.map +1 -1
  13. package/build/navigation/edit/placeholder/index.js +15 -7
  14. package/build/navigation/edit/placeholder/index.js.map +1 -1
  15. package/build/navigation/edit/responsive-wrapper.js +9 -7
  16. package/build/navigation/edit/responsive-wrapper.js.map +1 -1
  17. package/build/navigation/edit/unsaved-inner-blocks.js +3 -11
  18. package/build/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  19. package/build/navigation/edit/use-navigation-notice.js +54 -0
  20. package/build/navigation/edit/use-navigation-notice.js.map +1 -0
  21. package/build/navigation/index.js +1 -2
  22. package/build/navigation/index.js.map +1 -1
  23. package/build/navigation/menu-items-to-blocks.js +4 -1
  24. package/build/navigation/menu-items-to-blocks.js.map +1 -1
  25. package/build/navigation/use-navigation-menu.js +9 -2
  26. package/build/navigation/use-navigation-menu.js.map +1 -1
  27. package/build/navigation/view.js +14 -9
  28. package/build/navigation/view.js.map +1 -1
  29. package/build/navigation-submenu/edit.js +1 -1
  30. package/build/navigation-submenu/edit.js.map +1 -1
  31. package/build/post-comments/index.js +1 -1
  32. package/build/post-template/edit.js +54 -13
  33. package/build/post-template/edit.js.map +1 -1
  34. package/build/site-logo/edit.js +9 -6
  35. package/build/site-logo/edit.js.map +1 -1
  36. package/build/site-logo/index.js +0 -3
  37. package/build/site-logo/index.js.map +1 -1
  38. package/build/template-part/edit/index.js +7 -1
  39. package/build/template-part/edit/index.js.map +1 -1
  40. package/build-module/group/edit.native.js +1 -1
  41. package/build-module/group/edit.native.js.map +1 -1
  42. package/build-module/group/index.js +3 -1
  43. package/build-module/group/index.js.map +1 -1
  44. package/build-module/image/image.js +31 -7
  45. package/build-module/image/image.js.map +1 -1
  46. package/build-module/navigation/deprecated.js +1 -3
  47. package/build-module/navigation/deprecated.js.map +1 -1
  48. package/build-module/navigation/edit/index.js +107 -44
  49. package/build-module/navigation/edit/index.js.map +1 -1
  50. package/build-module/navigation/edit/navigation-menu-selector.js +9 -3
  51. package/build-module/navigation/edit/navigation-menu-selector.js.map +1 -1
  52. package/build-module/navigation/edit/placeholder/index.js +15 -7
  53. package/build-module/navigation/edit/placeholder/index.js.map +1 -1
  54. package/build-module/navigation/edit/responsive-wrapper.js +9 -7
  55. package/build-module/navigation/edit/responsive-wrapper.js.map +1 -1
  56. package/build-module/navigation/edit/unsaved-inner-blocks.js +4 -13
  57. package/build-module/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  58. package/build-module/navigation/edit/use-navigation-notice.js +44 -0
  59. package/build-module/navigation/edit/use-navigation-notice.js.map +1 -0
  60. package/build-module/navigation/index.js +1 -2
  61. package/build-module/navigation/index.js.map +1 -1
  62. package/build-module/navigation/menu-items-to-blocks.js +3 -1
  63. package/build-module/navigation/menu-items-to-blocks.js.map +1 -1
  64. package/build-module/navigation/use-navigation-menu.js +9 -2
  65. package/build-module/navigation/use-navigation-menu.js.map +1 -1
  66. package/build-module/navigation/view.js +14 -9
  67. package/build-module/navigation/view.js.map +1 -1
  68. package/build-module/navigation-submenu/edit.js +1 -1
  69. package/build-module/navigation-submenu/edit.js.map +1 -1
  70. package/build-module/post-comments/index.js +1 -1
  71. package/build-module/post-template/edit.js +54 -15
  72. package/build-module/post-template/edit.js.map +1 -1
  73. package/build-module/site-logo/edit.js +9 -6
  74. package/build-module/site-logo/edit.js.map +1 -1
  75. package/build-module/site-logo/index.js +0 -3
  76. package/build-module/site-logo/index.js.map +1 -1
  77. package/build-module/template-part/edit/index.js +6 -1
  78. package/build-module/template-part/edit/index.js.map +1 -1
  79. package/build-style/common-rtl.css +10 -0
  80. package/build-style/common.css +10 -0
  81. package/build-style/editor-rtl.css +38 -11
  82. package/build-style/editor.css +38 -11
  83. package/build-style/image/editor-rtl.css +16 -0
  84. package/build-style/image/editor.css +16 -0
  85. package/build-style/navigation/editor-rtl.css +11 -0
  86. package/build-style/navigation/editor.css +11 -0
  87. package/build-style/navigation/style-rtl.css +56 -24
  88. package/build-style/navigation/style.css +56 -24
  89. package/build-style/post-comments/style-rtl.css +3 -1
  90. package/build-style/post-comments/style.css +3 -1
  91. package/build-style/post-comments-form/style-rtl.css +2 -2
  92. package/build-style/post-comments-form/style.css +2 -2
  93. package/build-style/site-logo/editor-rtl.css +1 -11
  94. package/build-style/site-logo/editor.css +1 -11
  95. package/build-style/site-logo/style-rtl.css +3 -1
  96. package/build-style/site-logo/style.css +3 -1
  97. package/build-style/style-rtl.css +74 -28
  98. package/build-style/style.css +74 -28
  99. package/package.json +14 -14
  100. package/src/common.scss +12 -0
  101. package/src/editor.scss +12 -0
  102. package/src/gallery/index.php +7 -8
  103. package/src/group/block.json +3 -1
  104. package/src/group/edit.native.js +1 -1
  105. package/src/home-link/index.php +1 -1
  106. package/src/image/editor.scss +18 -0
  107. package/src/image/image.js +32 -8
  108. package/src/image/index.php +1 -1
  109. package/src/navigation/block.json +1 -2
  110. package/src/navigation/deprecated.js +0 -2
  111. package/src/navigation/edit/index.js +166 -60
  112. package/src/navigation/edit/navigation-menu-selector.js +20 -6
  113. package/src/navigation/edit/placeholder/index.js +40 -24
  114. package/src/navigation/edit/responsive-wrapper.js +10 -7
  115. package/src/navigation/edit/unsaved-inner-blocks.js +13 -25
  116. package/src/navigation/edit/use-navigation-notice.js +37 -0
  117. package/src/navigation/editor.scss +14 -0
  118. package/src/navigation/index.php +19 -8
  119. package/src/navigation/menu-items-to-blocks.js +7 -1
  120. package/src/navigation/style.scss +82 -32
  121. package/src/navigation/use-navigation-menu.js +20 -0
  122. package/src/navigation/view.js +15 -9
  123. package/src/navigation-link/index.php +1 -1
  124. package/src/navigation-submenu/edit.js +1 -1
  125. package/src/navigation-submenu/index.php +9 -3
  126. package/src/page-list/index.php +9 -4
  127. package/src/post-comments/block.json +5 -1
  128. package/src/post-comments/index.php +17 -0
  129. package/src/post-comments/style.scss +7 -3
  130. package/src/post-comments-form/index.php +6 -4
  131. package/src/post-comments-form/style.scss +7 -5
  132. package/src/post-template/edit.js +54 -17
  133. package/src/site-logo/block.json +0 -3
  134. package/src/site-logo/edit.js +6 -5
  135. package/src/site-logo/editor.scss +1 -15
  136. package/src/site-logo/index.php +0 -4
  137. package/src/site-logo/style.scss +3 -1
  138. package/src/template-part/edit/index.js +6 -1
  139. package/src/template-part/index.php +5 -2
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import classnames from 'classnames';
5
+ import { noop } from 'lodash';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -26,7 +27,8 @@ import {
26
27
  Warning,
27
28
  } from '@wordpress/block-editor';
28
29
  import { EntityProvider, useEntityProp } from '@wordpress/core-data';
29
- import { useDispatch, useSelect } from '@wordpress/data';
30
+
31
+ import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
30
32
  import {
31
33
  PanelBody,
32
34
  ToggleControl,
@@ -51,6 +53,9 @@ import NavigationMenuSelector from './navigation-menu-selector';
51
53
  import NavigationMenuNameControl from './navigation-menu-name-control';
52
54
  import UnsavedInnerBlocks from './unsaved-inner-blocks';
53
55
  import NavigationMenuDeleteControl from './navigation-menu-delete-control';
56
+ import useNavigationNotice from './use-navigation-notice';
57
+
58
+ const EMPTY_ARRAY = [];
54
59
 
55
60
  function getComputedStyle( node ) {
56
61
  return node.ownerDocument.defaultView.getComputedStyle( node );
@@ -106,20 +111,32 @@ function Navigation( {
106
111
  openSubmenusOnClick,
107
112
  overlayMenu,
108
113
  showSubmenuIcon,
109
- layout: { justifyContent, orientation = 'horizontal' } = {},
114
+ layout: {
115
+ justifyContent,
116
+ orientation = 'horizontal',
117
+ flexWrap = 'wrap',
118
+ } = {},
110
119
  } = attributes;
111
120
 
112
- const [ areaMenu, setAreaMenu ] = useEntityProp(
113
- 'root',
114
- 'navigationArea',
115
- 'navigation',
116
- navigationArea
117
- );
121
+ let areaMenu,
122
+ setAreaMenu = noop;
123
+ // Navigation areas are deprecated and on their way out. Let's not perform
124
+ // the request unless we're in an environment where the endpoint exists.
125
+ if ( process.env.GUTENBERG_PHASE === 2 ) {
126
+ // eslint-disable-next-line react-hooks/rules-of-hooks
127
+ [ areaMenu, setAreaMenu ] = useEntityProp(
128
+ 'root',
129
+ 'navigationArea',
130
+ 'navigation',
131
+ navigationArea
132
+ );
133
+ }
118
134
 
119
135
  const navigationAreaMenu = areaMenu === 0 ? undefined : areaMenu;
120
136
 
121
137
  const ref = navigationArea ? navigationAreaMenu : attributes.ref;
122
138
 
139
+ const registry = useRegistry();
123
140
  const setRef = useCallback(
124
141
  ( postId ) => {
125
142
  setAttributes( { ref: postId } );
@@ -134,19 +151,41 @@ function Navigation( {
134
151
  `navigationMenu/${ ref }`
135
152
  );
136
153
 
137
- const { innerBlocks, isInnerBlockSelected } = useSelect(
154
+ const {
155
+ hasUncontrolledInnerBlocks,
156
+ uncontrolledInnerBlocks,
157
+ isInnerBlockSelected,
158
+ } = useSelect(
138
159
  ( select ) => {
139
- const { getBlocks, hasSelectedInnerBlock } = select(
160
+ const { getBlock, getBlocks, hasSelectedInnerBlock } = select(
140
161
  blockEditorStore
141
162
  );
163
+
164
+ // This relies on the fact that `getBlock` won't return controlled
165
+ // inner blocks, while `getBlocks` does. It might be more stable to
166
+ // introduce a selector like `getUncontrolledInnerBlocks`, just in
167
+ // case `getBlock` is fixed.
168
+ const _uncontrolledInnerBlocks = getBlock( clientId ).innerBlocks;
169
+ const _hasUncontrolledInnerBlocks =
170
+ _uncontrolledInnerBlocks?.length;
171
+ const _controlledInnerBlocks = _hasUncontrolledInnerBlocks
172
+ ? EMPTY_ARRAY
173
+ : getBlocks( clientId );
174
+ const innerBlocks = _hasUncontrolledInnerBlocks
175
+ ? _uncontrolledInnerBlocks
176
+ : _controlledInnerBlocks;
177
+
142
178
  return {
143
- innerBlocks: getBlocks( clientId ),
179
+ hasSubmenus: !! innerBlocks.find(
180
+ ( block ) => block.name === 'core/navigation-submenu'
181
+ ),
182
+ hasUncontrolledInnerBlocks: _hasUncontrolledInnerBlocks,
183
+ uncontrolledInnerBlocks: _uncontrolledInnerBlocks,
144
184
  isInnerBlockSelected: hasSelectedInnerBlock( clientId, true ),
145
185
  };
146
186
  },
147
187
  [ clientId ]
148
188
  );
149
- const hasExistingNavItems = !! innerBlocks.length;
150
189
  const {
151
190
  replaceInnerBlocks,
152
191
  selectBlock,
@@ -158,10 +197,10 @@ function Navigation( {
158
197
  setHasSavedUnsavedInnerBlocks,
159
198
  ] = useState( false );
160
199
 
161
- const isWithinUnassignedArea = navigationArea && ! ref;
200
+ const isWithinUnassignedArea = !! navigationArea && ! ref;
162
201
 
163
202
  const [ isPlaceholderShown, setIsPlaceholderShown ] = useState(
164
- ! hasExistingNavItems || isWithinUnassignedArea
203
+ ! hasUncontrolledInnerBlocks || isWithinUnassignedArea
165
204
  );
166
205
 
167
206
  const [ isResponsiveMenuOpen, setResponsiveMenuVisibility ] = useState(
@@ -175,6 +214,12 @@ function Navigation( {
175
214
  hasResolvedNavigationMenus,
176
215
  navigationMenus,
177
216
  navigationMenu,
217
+ canUserUpdateNavigationEntity,
218
+ hasResolvedCanUserUpdateNavigationEntity,
219
+ canUserDeleteNavigationEntity,
220
+ hasResolvedCanUserDeleteNavigationEntity,
221
+ canUserCreateNavigation,
222
+ hasResolvedCanUserCreateNavigation,
178
223
  } = useNavigationMenu( ref );
179
224
 
180
225
  const navRef = useRef();
@@ -192,6 +237,10 @@ function Navigation( {
192
237
  className: classnames( className, {
193
238
  'items-justified-right': justifyContent === 'right',
194
239
  'items-justified-space-between': justifyContent === 'space-between',
240
+ 'items-justified-left': justifyContent === 'left',
241
+ 'items-justified-center': justifyContent === 'center',
242
+ 'is-vertical': orientation === 'vertical',
243
+ 'no-wrap': flexWrap === 'nowrap',
195
244
  'is-responsive': 'never' !== overlayMenu,
196
245
  'has-text-color': !! textColor.color || !! textColor?.class,
197
246
  [ getColorClassName(
@@ -262,7 +311,7 @@ function Navigation( {
262
311
  setDetectedColor,
263
312
  setDetectedBackgroundColor
264
313
  );
265
- const subMenuElement = navRef.current.querySelector(
314
+ const subMenuElement = navRef.current?.querySelector(
266
315
  '[data-type="core/navigation-link"] [data-type="core/navigation-link"]'
267
316
  );
268
317
  if ( subMenuElement ) {
@@ -279,49 +328,105 @@ function Navigation( {
279
328
  setIsPlaceholderShown( ! isEntityAvailable );
280
329
  }, [ isEntityAvailable ] );
281
330
 
282
- // If the ref no longer exists the reset the inner blocks
283
- // to provide a clean slate.
331
+ const [ showCantEditNotice, hideCantEditNotice ] = useNavigationNotice( {
332
+ name: 'block-library/core/navigation/permissions/update',
333
+ message: __(
334
+ 'You do not have permission to edit this Menu. Any changes made will not be saved.'
335
+ ),
336
+ } );
337
+
338
+ const [ showCantCreateNotice, hideCantCreateNotice ] = useNavigationNotice(
339
+ {
340
+ name: 'block-library/core/navigation/permissions/create',
341
+ message: __(
342
+ 'You do not have permission to create Navigation Menus.'
343
+ ),
344
+ }
345
+ );
346
+
284
347
  useEffect( () => {
285
- if ( ref === undefined && innerBlocks.length > 0 ) {
286
- replaceInnerBlocks( clientId, [] );
348
+ if ( ! isSelected && ! isInnerBlockSelected ) {
349
+ hideCantEditNotice();
350
+ hideCantCreateNotice();
287
351
  }
288
- // innerBlocks are intentionally not listed as deps. This function is only concerned
289
- // with the snapshot from the time when ref became undefined.
290
- }, [ clientId, ref, innerBlocks ] );
291
352
 
292
- const startWithEmptyMenu = useCallback( () => {
293
- if ( navigationArea ) {
294
- setAreaMenu( 0 );
353
+ if ( isSelected || isInnerBlockSelected ) {
354
+ if (
355
+ hasResolvedCanUserUpdateNavigationEntity &&
356
+ ! canUserUpdateNavigationEntity
357
+ ) {
358
+ showCantEditNotice();
359
+ }
360
+
361
+ if (
362
+ ! ref &&
363
+ hasResolvedCanUserCreateNavigation &&
364
+ ! canUserCreateNavigation
365
+ ) {
366
+ showCantCreateNotice();
367
+ }
295
368
  }
296
- setAttributes( {
297
- ref: undefined,
298
- } );
369
+ }, [
370
+ isSelected,
371
+ isInnerBlockSelected,
372
+ canUserUpdateNavigationEntity,
373
+ hasResolvedCanUserUpdateNavigationEntity,
374
+ canUserCreateNavigation,
375
+ hasResolvedCanUserCreateNavigation,
376
+ ref,
377
+ ] );
299
378
 
300
- setIsPlaceholderShown( true );
301
- }, [ clientId ] );
379
+ const startWithEmptyMenu = useCallback( () => {
380
+ registry.batch( () => {
381
+ if ( navigationArea ) {
382
+ setAreaMenu( 0 );
383
+ }
384
+ setAttributes( {
385
+ ref: undefined,
386
+ } );
387
+ if ( ! ref ) {
388
+ replaceInnerBlocks( clientId, [] );
389
+ }
390
+ setIsPlaceholderShown( true );
391
+ } );
392
+ }, [ clientId, ref ] );
302
393
 
303
394
  // If the block has inner blocks, but no menu id, this was an older
304
395
  // navigation block added before the block used a wp_navigation entity.
305
396
  // Either this block was saved in the content or inserted by a pattern.
306
397
  // Consider this 'unsaved'. Offer an uncontrolled version of inner blocks,
307
398
  // that automatically saves the menu.
308
- const hasUnsavedBlocks =
309
- hasExistingNavItems && ! isEntityAvailable && ! isWithinUnassignedArea;
399
+ const hasUnsavedBlocks = hasUncontrolledInnerBlocks && ! isEntityAvailable;
310
400
  if ( hasUnsavedBlocks ) {
311
401
  return (
312
- <UnsavedInnerBlocks
313
- blockProps={ blockProps }
314
- blocks={ innerBlocks }
315
- clientId={ clientId }
316
- navigationMenus={ navigationMenus }
317
- hasSelection={ isSelected || isInnerBlockSelected }
318
- hasSavedUnsavedInnerBlocks={ hasSavedUnsavedInnerBlocks }
319
- onSave={ ( post ) => {
320
- setHasSavedUnsavedInnerBlocks( true );
321
- // Switch to using the wp_navigation entity.
322
- setRef( post.id );
323
- } }
324
- />
402
+ <nav { ...blockProps }>
403
+ <ResponsiveWrapper
404
+ id={ clientId }
405
+ onToggle={ setResponsiveMenuVisibility }
406
+ isOpen={ isResponsiveMenuOpen }
407
+ isResponsive={ 'never' !== overlayMenu }
408
+ isHiddenByDefault={ 'always' === overlayMenu }
409
+ classNames={ overlayClassnames }
410
+ styles={ overlayStyles }
411
+ >
412
+ <UnsavedInnerBlocks
413
+ blockProps={ blockProps }
414
+ blocks={ uncontrolledInnerBlocks }
415
+ clientId={ clientId }
416
+ navigationMenus={ navigationMenus }
417
+ hasSelection={ isSelected || isInnerBlockSelected }
418
+ hasSavedUnsavedInnerBlocks={
419
+ hasSavedUnsavedInnerBlocks
420
+ }
421
+ onSave={ ( post ) => {
422
+ // Set some state used as a guard to prevent the creation of multiple posts.
423
+ setHasSavedUnsavedInnerBlocks( true );
424
+ // Switch to using the wp_navigation entity.
425
+ setRef( post.id );
426
+ } }
427
+ />
428
+ </ResponsiveWrapper>
429
+ </nav>
325
430
  );
326
431
  }
327
432
 
@@ -374,6 +479,7 @@ function Navigation( {
374
479
  onClose();
375
480
  } }
376
481
  onCreateNew={ startWithEmptyMenu }
482
+ showCreate={ canUserCreateNavigation }
377
483
  />
378
484
  ) }
379
485
  </ToolbarDropdownMenu>
@@ -484,18 +590,16 @@ function Navigation( {
484
590
  </InspectorControls>
485
591
  { isEntityAvailable && (
486
592
  <InspectorControls __experimentalGroup="advanced">
487
- <NavigationMenuNameControl />
488
- <NavigationMenuDeleteControl
489
- onDelete={ () => {
490
- if ( navigationArea ) {
491
- setAreaMenu( 0 );
492
- }
493
- setAttributes( {
494
- ref: undefined,
495
- } );
496
- setIsPlaceholderShown( true );
497
- } }
498
- />
593
+ { hasResolvedCanUserUpdateNavigationEntity &&
594
+ canUserUpdateNavigationEntity && (
595
+ <NavigationMenuNameControl />
596
+ ) }
597
+ { hasResolvedCanUserDeleteNavigationEntity &&
598
+ canUserDeleteNavigationEntity && (
599
+ <NavigationMenuDeleteControl
600
+ onDelete={ startWithEmptyMenu }
601
+ />
602
+ ) }
499
603
  </InspectorControls>
500
604
  ) }
501
605
  <nav { ...blockProps }>
@@ -513,11 +617,13 @@ function Navigation( {
513
617
  hasResolvedNavigationMenus
514
618
  }
515
619
  clientId={ clientId }
620
+ canUserCreateNavigation={ canUserCreateNavigation }
516
621
  />
517
622
  ) }
518
- { ! isEntityAvailable && ! isPlaceholderShown && (
519
- <PlaceholderPreview isLoading />
520
- ) }
623
+ { ! hasResolvedCanUserCreateNavigation ||
624
+ ( ! isEntityAvailable && ! isPlaceholderShown && (
625
+ <PlaceholderPreview isLoading />
626
+ ) ) }
521
627
  { ! isPlaceholderShown && (
522
628
  <ResponsiveWrapper
523
629
  id={ clientId }
@@ -5,13 +5,18 @@ import { MenuGroup, MenuItem, MenuItemsChoice } from '@wordpress/components';
5
5
  import { useEntityId } from '@wordpress/core-data';
6
6
  import { __, sprintf } from '@wordpress/i18n';
7
7
  import { decodeEntities } from '@wordpress/html-entities';
8
+ import { addQueryArgs } from '@wordpress/url';
8
9
 
9
10
  /**
10
11
  * Internal dependencies
11
12
  */
12
13
  import useNavigationMenu from '../use-navigation-menu';
13
14
 
14
- export default function NavigationMenuSelector( { onSelect, onCreateNew } ) {
15
+ export default function NavigationMenuSelector( {
16
+ onSelect,
17
+ onCreateNew,
18
+ showCreate = false,
19
+ } ) {
15
20
  const { navigationMenus } = useNavigationMenu();
16
21
  const ref = useEntityId( 'postType', 'wp_navigation' );
17
22
 
@@ -41,11 +46,20 @@ export default function NavigationMenuSelector( { onSelect, onCreateNew } ) {
41
46
  } ) }
42
47
  />
43
48
  </MenuGroup>
44
- <MenuGroup>
45
- <MenuItem onClick={ onCreateNew }>
46
- { __( 'Create new menu' ) }
47
- </MenuItem>
48
- </MenuGroup>
49
+ { showCreate && (
50
+ <MenuGroup>
51
+ <MenuItem onClick={ onCreateNew }>
52
+ { __( 'Create new menu' ) }
53
+ </MenuItem>
54
+ <MenuItem
55
+ href={ addQueryArgs( 'edit.php', {
56
+ post_type: 'wp_navigation',
57
+ } ) }
58
+ >
59
+ { __( 'Manage menus' ) }
60
+ </MenuItem>
61
+ </MenuGroup>
62
+ ) }
49
63
  </>
50
64
  );
51
65
  }
@@ -31,6 +31,7 @@ const ExistingMenusDropdown = ( {
31
31
  onFinish,
32
32
  menus,
33
33
  onCreateFromMenu,
34
+ showClassicMenus = false,
34
35
  } ) => {
35
36
  const toggleProps = {
36
37
  variant: 'tertiary',
@@ -65,22 +66,24 @@ const ExistingMenusDropdown = ( {
65
66
  );
66
67
  } ) }
67
68
  </MenuGroup>
68
- <MenuGroup label={ __( 'Classic Menus' ) }>
69
- { menus?.map( ( menu ) => {
70
- return (
71
- <MenuItem
72
- onClick={ () => {
73
- setSelectedMenu( menu.id );
74
- onCreateFromMenu( menu.name );
75
- } }
76
- onClose={ onClose }
77
- key={ menu.id }
78
- >
79
- { decodeEntities( menu.name ) }
80
- </MenuItem>
81
- );
82
- } ) }
83
- </MenuGroup>
69
+ { showClassicMenus && (
70
+ <MenuGroup label={ __( 'Classic Menus' ) }>
71
+ { menus?.map( ( menu ) => {
72
+ return (
73
+ <MenuItem
74
+ onClick={ () => {
75
+ setSelectedMenu( menu.id );
76
+ onCreateFromMenu( menu.name );
77
+ } }
78
+ onClose={ onClose }
79
+ key={ menu.id }
80
+ >
81
+ { decodeEntities( menu.name ) }
82
+ </MenuItem>
83
+ );
84
+ } ) }
85
+ </MenuGroup>
86
+ ) }
84
87
  </>
85
88
  ) }
86
89
  </DropdownMenu>
@@ -92,6 +95,7 @@ export default function NavigationPlaceholder( {
92
95
  onFinish,
93
96
  canSwitchNavigationMenu,
94
97
  hasResolvedNavigationMenus,
98
+ canUserCreateNavigation = false,
95
99
  } ) {
96
100
  const [ selectedMenu, setSelectedMenu ] = useState();
97
101
  const [ isCreatingFromMenu, setIsCreatingFromMenu ] = useState( false );
@@ -102,6 +106,10 @@ export default function NavigationPlaceholder( {
102
106
  blocks,
103
107
  navigationMenuTitle = null
104
108
  ) => {
109
+ if ( ! canUserCreateNavigation ) {
110
+ return;
111
+ }
112
+
105
113
  const navigationMenu = await createNavigationMenu(
106
114
  navigationMenuTitle,
107
115
  blocks
@@ -176,8 +184,10 @@ export default function NavigationPlaceholder( {
176
184
  <Icon icon={ navigation } />{ ' ' }
177
185
  { __( 'Navigation' ) }
178
186
  </div>
187
+
179
188
  <hr />
180
- { hasMenus || navigationMenus.length ? (
189
+
190
+ { hasMenus || navigationMenus?.length ? (
181
191
  <>
182
192
  <ExistingMenusDropdown
183
193
  canSwitchNavigationMenu={
@@ -188,11 +198,14 @@ export default function NavigationPlaceholder( {
188
198
  onFinish={ onFinish }
189
199
  menus={ menus }
190
200
  onCreateFromMenu={ onCreateFromMenu }
201
+ showClassicMenus={
202
+ canUserCreateNavigation
203
+ }
191
204
  />
192
205
  <hr />
193
206
  </>
194
207
  ) : undefined }
195
- { hasPages ? (
208
+ { canUserCreateNavigation && hasPages ? (
196
209
  <>
197
210
  <Button
198
211
  variant="tertiary"
@@ -203,12 +216,15 @@ export default function NavigationPlaceholder( {
203
216
  <hr />
204
217
  </>
205
218
  ) : undefined }
206
- <Button
207
- variant="tertiary"
208
- onClick={ onCreateEmptyMenu }
209
- >
210
- { __( 'Start empty' ) }
211
- </Button>
219
+
220
+ { canUserCreateNavigation && (
221
+ <Button
222
+ variant="tertiary"
223
+ onClick={ onCreateEmptyMenu }
224
+ >
225
+ { __( 'Start empty' ) }
226
+ </Button>
227
+ ) }
212
228
  </div>
213
229
  </div>
214
230
  </Placeholder>
@@ -39,12 +39,20 @@ export default function ResponsiveWrapper( {
39
39
 
40
40
  const modalId = `${ id }-modal`;
41
41
 
42
+ const dialogProps = {
43
+ className: 'wp-block-navigation__responsive-dialog',
44
+ ...( isOpen && {
45
+ role: 'dialog',
46
+ 'aria-modal': true,
47
+ 'aria-label': __( 'Menu' ),
48
+ } ),
49
+ };
50
+
42
51
  return (
43
52
  <>
44
53
  { ! isOpen && (
45
54
  <Button
46
55
  aria-haspopup="true"
47
- aria-expanded={ isOpen }
48
56
  aria-label={ __( 'Open menu' ) }
49
57
  className={ openButtonClasses }
50
58
  onClick={ () => onToggle( true ) }
@@ -73,12 +81,7 @@ export default function ResponsiveWrapper( {
73
81
  className="wp-block-navigation__responsive-close"
74
82
  tabIndex="-1"
75
83
  >
76
- <div
77
- className="wp-block-navigation__responsive-dialog"
78
- role="dialog"
79
- aria-modal="true"
80
- aria-labelledby={ `${ modalId }-title` }
81
- >
84
+ <div { ...dialogProps }>
82
85
  <Button
83
86
  className="wp-block-navigation__responsive-container-close"
84
87
  aria-label={ __( 'Close menu' ) }
@@ -18,7 +18,6 @@ import { useContext, useEffect, useRef } from '@wordpress/element';
18
18
  import useNavigationMenu from '../use-navigation-menu';
19
19
  import useCreateNavigationMenu from './use-create-navigation-menu';
20
20
 
21
- const NOOP = () => {};
22
21
  const EMPTY_OBJECT = {};
23
22
  const DRAFT_MENU_PARAMS = [
24
23
  'postType',
@@ -41,13 +40,6 @@ export default function UnsavedInnerBlocks( {
41
40
 
42
41
  const innerBlocksProps = useInnerBlocksProps( blockProps, {
43
42
  renderAppender: hasSelection ? undefined : false,
44
-
45
- // Make the inner blocks 'controlled'. This allows the block to always
46
- // work with controlled inner blocks, smoothing out the switch to using
47
- // an entity.
48
- value: blocks,
49
- onChange: NOOP,
50
- onInput: NOOP,
51
43
  } );
52
44
 
53
45
  const {
@@ -125,22 +117,18 @@ export default function UnsavedInnerBlocks( {
125
117
  ] );
126
118
 
127
119
  return (
128
- <>
129
- <nav { ...blockProps }>
130
- <div className="wp-block-navigation__unsaved-changes">
131
- <Disabled
132
- className={ classnames(
133
- 'wp-block-navigation__unsaved-changes-overlay',
134
- {
135
- 'is-saving': hasSelection,
136
- }
137
- ) }
138
- >
139
- <div { ...innerBlocksProps } />
140
- </Disabled>
141
- { hasSelection && <Spinner /> }
142
- </div>
143
- </nav>
144
- </>
120
+ <div className="wp-block-navigation__unsaved-changes">
121
+ <Disabled
122
+ className={ classnames(
123
+ 'wp-block-navigation__unsaved-changes-overlay',
124
+ {
125
+ 'is-saving': hasSelection,
126
+ }
127
+ ) }
128
+ >
129
+ <div { ...innerBlocksProps } />
130
+ </Disabled>
131
+ { hasSelection && <Spinner /> }
132
+ </div>
145
133
  );
146
134
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useRef } from '@wordpress/element';
5
+ import { useDispatch } from '@wordpress/data';
6
+ import { store as noticeStore } from '@wordpress/notices';
7
+
8
+ function useNavigationNotice( { name, message } = {} ) {
9
+ const noticeRef = useRef();
10
+
11
+ const { createWarningNotice, removeNotice } = useDispatch( noticeStore );
12
+
13
+ const showNotice = () => {
14
+ if ( noticeRef.current ) {
15
+ return;
16
+ }
17
+
18
+ noticeRef.current = name;
19
+
20
+ createWarningNotice( message, {
21
+ id: noticeRef.current,
22
+ type: 'snackbar',
23
+ } );
24
+ };
25
+
26
+ const hideNotice = () => {
27
+ if ( ! noticeRef.current ) {
28
+ return;
29
+ }
30
+ removeNotice( noticeRef.current );
31
+ noticeRef.current = null;
32
+ };
33
+
34
+ return [ showNotice, hideNotice ];
35
+ }
36
+
37
+ export default useNavigationNotice;
@@ -96,6 +96,19 @@
96
96
  }
97
97
  }
98
98
 
99
+ // Show even when a child is selected. This is an edgecase just for navigation submenus.
100
+ .is-editing > .wp-block-navigation__submenu-container > .block-list-appender {
101
+ display: block;
102
+ position: static;
103
+ width: 100%;
104
+ }
105
+
106
+ // Hide when hovering.
107
+ .wp-block-navigation__submenu-container .block-list-appender {
108
+ display: none;
109
+ }
110
+
111
+
99
112
  /**
100
113
  * Colors Selector component
101
114
  */
@@ -359,6 +372,7 @@ $color-control-label-height: 20px;
359
372
  font-size: $default-font-size;
360
373
  font-family: $default-font;
361
374
  gap: $grid-unit-15 * 0.5;
375
+ align-items: center;
362
376
 
363
377
  // Margins.
364
378
  .components-dropdown,