@wordpress/block-library 9.40.1 → 9.40.2-next.v.202602271551.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 (208) hide show
  1. package/build/accordion/view.cjs +0 -34
  2. package/build/accordion/view.cjs.map +2 -2
  3. package/build/icon/block.json +9 -1
  4. package/build/icon/components/custom-inserter/icon-grid.cjs +1 -1
  5. package/build/icon/components/custom-inserter/icon-grid.cjs.map +2 -2
  6. package/build/icon/components/custom-inserter/index.cjs +1 -1
  7. package/build/icon/components/custom-inserter/index.cjs.map +2 -2
  8. package/build/icon/edit.cjs +15 -7
  9. package/build/icon/edit.cjs.map +2 -2
  10. package/build/image/edit.cjs +1 -1
  11. package/build/image/edit.cjs.map +2 -2
  12. package/build/navigation/edit/index.cjs +4 -2
  13. package/build/navigation/edit/index.cjs.map +3 -3
  14. package/build/navigation/edit/leaf-more-menu.cjs +68 -6
  15. package/build/navigation/edit/leaf-more-menu.cjs.map +3 -3
  16. package/build/navigation/edit/menu-inspector-controls.cjs +20 -91
  17. package/build/navigation/edit/menu-inspector-controls.cjs.map +3 -3
  18. package/build/navigation/edit/navigation-link-ui.cjs +97 -0
  19. package/build/navigation/edit/navigation-link-ui.cjs.map +7 -0
  20. package/build/navigation/edit/navigation-list-view-header.cjs +86 -0
  21. package/build/navigation/edit/navigation-list-view-header.cjs.map +7 -0
  22. package/build/navigation/edit/navigation-menu-selector.cjs +4 -2
  23. package/build/navigation/edit/navigation-menu-selector.cjs.map +3 -3
  24. package/build/navigation/edit/placeholder/index.cjs +2 -2
  25. package/build/navigation/edit/placeholder/index.cjs.map +3 -3
  26. package/build/navigation-link/shared/controls.cjs +29 -52
  27. package/build/navigation-link/shared/controls.cjs.map +3 -3
  28. package/build/navigation-link/shared/use-link-preview.cjs +7 -8
  29. package/build/navigation-link/shared/use-link-preview.cjs.map +2 -2
  30. package/build/navigation-overlay-close/edit.cjs +2 -3
  31. package/build/navigation-overlay-close/edit.cjs.map +2 -2
  32. package/build/page-list-item/edit.cjs +6 -3
  33. package/build/page-list-item/edit.cjs.map +2 -2
  34. package/build/post-navigation-link/block.json +1 -3
  35. package/build/post-navigation-link/deprecated.cjs +100 -0
  36. package/build/post-navigation-link/deprecated.cjs.map +7 -0
  37. package/build/post-navigation-link/edit.cjs +2 -36
  38. package/build/post-navigation-link/edit.cjs.map +3 -3
  39. package/build/post-navigation-link/index.cjs +2 -0
  40. package/build/post-navigation-link/index.cjs.map +3 -3
  41. package/build/post-title/block.json +1 -3
  42. package/build/post-title/deprecated.cjs +82 -1
  43. package/build/post-title/deprecated.cjs.map +3 -3
  44. package/build/post-title/edit.cjs +10 -36
  45. package/build/post-title/edit.cjs.map +3 -3
  46. package/build/pullquote/deprecated.cjs +6 -6
  47. package/build/pullquote/deprecated.cjs.map +2 -2
  48. package/build/query/block.json +1 -2
  49. package/build/query-title/block.json +1 -3
  50. package/build/query-title/deprecated.cjs +70 -1
  51. package/build/query-title/deprecated.cjs.map +3 -3
  52. package/build/query-title/edit.cjs +17 -35
  53. package/build/query-title/edit.cjs.map +3 -3
  54. package/build/site-tagline/block.json +6 -4
  55. package/build/site-tagline/deprecated.cjs +66 -1
  56. package/build/site-tagline/deprecated.cjs.map +3 -3
  57. package/build/site-tagline/edit.cjs +14 -28
  58. package/build/site-tagline/edit.cjs.map +3 -3
  59. package/build/site-title/block.json +1 -3
  60. package/build/site-title/deprecated.cjs +79 -1
  61. package/build/site-title/deprecated.cjs.map +3 -3
  62. package/build/site-title/edit.cjs +14 -30
  63. package/build/site-title/edit.cjs.map +3 -3
  64. package/build/tabs-menu-item/block.json +1 -26
  65. package/build/tabs-menu-item/controls.cjs +2 -100
  66. package/build/tabs-menu-item/controls.cjs.map +3 -3
  67. package/build/tabs-menu-item/edit.cjs +6 -65
  68. package/build/tabs-menu-item/edit.cjs.map +2 -2
  69. package/build/tabs-menu-item/save.cjs +1 -15
  70. package/build/tabs-menu-item/save.cjs.map +2 -2
  71. package/build-module/accordion/view.mjs +1 -35
  72. package/build-module/accordion/view.mjs.map +2 -2
  73. package/build-module/icon/block.json +9 -1
  74. package/build-module/icon/components/custom-inserter/icon-grid.mjs +1 -1
  75. package/build-module/icon/components/custom-inserter/icon-grid.mjs.map +2 -2
  76. package/build-module/icon/components/custom-inserter/index.mjs +1 -1
  77. package/build-module/icon/components/custom-inserter/index.mjs.map +2 -2
  78. package/build-module/icon/edit.mjs +15 -7
  79. package/build-module/icon/edit.mjs.map +2 -2
  80. package/build-module/image/edit.mjs +1 -1
  81. package/build-module/image/edit.mjs.map +2 -2
  82. package/build-module/navigation/edit/index.mjs +4 -2
  83. package/build-module/navigation/edit/index.mjs.map +2 -2
  84. package/build-module/navigation/edit/leaf-more-menu.mjs +73 -7
  85. package/build-module/navigation/edit/leaf-more-menu.mjs.map +2 -2
  86. package/build-module/navigation/edit/menu-inspector-controls.mjs +21 -101
  87. package/build-module/navigation/edit/menu-inspector-controls.mjs.map +2 -2
  88. package/build-module/navigation/edit/navigation-link-ui.mjs +76 -0
  89. package/build-module/navigation/edit/navigation-link-ui.mjs.map +7 -0
  90. package/build-module/navigation/edit/navigation-list-view-header.mjs +58 -0
  91. package/build-module/navigation/edit/navigation-list-view-header.mjs.map +7 -0
  92. package/build-module/navigation/edit/navigation-menu-selector.mjs +5 -3
  93. package/build-module/navigation/edit/navigation-menu-selector.mjs.map +2 -2
  94. package/build-module/navigation/edit/placeholder/index.mjs +2 -2
  95. package/build-module/navigation/edit/placeholder/index.mjs.map +2 -2
  96. package/build-module/navigation-link/shared/controls.mjs +29 -53
  97. package/build-module/navigation-link/shared/controls.mjs.map +2 -2
  98. package/build-module/navigation-link/shared/use-link-preview.mjs +7 -8
  99. package/build-module/navigation-link/shared/use-link-preview.mjs.map +2 -2
  100. package/build-module/navigation-overlay-close/edit.mjs +2 -3
  101. package/build-module/navigation-overlay-close/edit.mjs.map +2 -2
  102. package/build-module/page-list-item/edit.mjs +6 -3
  103. package/build-module/page-list-item/edit.mjs.map +2 -2
  104. package/build-module/post-navigation-link/block.json +1 -3
  105. package/build-module/post-navigation-link/deprecated.mjs +69 -0
  106. package/build-module/post-navigation-link/deprecated.mjs.map +7 -0
  107. package/build-module/post-navigation-link/edit.mjs +3 -30
  108. package/build-module/post-navigation-link/edit.mjs.map +2 -2
  109. package/build-module/post-navigation-link/index.mjs +2 -0
  110. package/build-module/post-navigation-link/index.mjs.map +2 -2
  111. package/build-module/post-title/block.json +1 -3
  112. package/build-module/post-title/deprecated.mjs +82 -1
  113. package/build-module/post-title/deprecated.mjs.map +2 -2
  114. package/build-module/post-title/edit.mjs +10 -27
  115. package/build-module/post-title/edit.mjs.map +2 -2
  116. package/build-module/pullquote/deprecated.mjs +6 -6
  117. package/build-module/pullquote/deprecated.mjs.map +2 -2
  118. package/build-module/query/block.json +1 -2
  119. package/build-module/query-title/block.json +1 -3
  120. package/build-module/query-title/deprecated.mjs +70 -1
  121. package/build-module/query-title/deprecated.mjs.map +2 -2
  122. package/build-module/query-title/edit.mjs +17 -36
  123. package/build-module/query-title/edit.mjs.map +2 -2
  124. package/build-module/site-tagline/block.json +6 -4
  125. package/build-module/site-tagline/deprecated.mjs +66 -1
  126. package/build-module/site-tagline/deprecated.mjs.map +2 -2
  127. package/build-module/site-tagline/edit.mjs +14 -29
  128. package/build-module/site-tagline/edit.mjs.map +2 -2
  129. package/build-module/site-title/block.json +1 -3
  130. package/build-module/site-title/deprecated.mjs +79 -1
  131. package/build-module/site-title/deprecated.mjs.map +2 -2
  132. package/build-module/site-title/edit.mjs +14 -31
  133. package/build-module/site-title/edit.mjs.map +2 -2
  134. package/build-module/tabs-menu-item/block.json +1 -26
  135. package/build-module/tabs-menu-item/controls.mjs +3 -104
  136. package/build-module/tabs-menu-item/controls.mjs.map +2 -2
  137. package/build-module/tabs-menu-item/edit.mjs +6 -66
  138. package/build-module/tabs-menu-item/edit.mjs.map +2 -2
  139. package/build-module/tabs-menu-item/save.mjs +1 -15
  140. package/build-module/tabs-menu-item/save.mjs.map +2 -2
  141. package/build-style/editor-rtl.css +10 -5
  142. package/build-style/editor.css +10 -5
  143. package/build-style/navigation-link/editor-rtl.css +10 -0
  144. package/build-style/navigation-link/editor.css +10 -0
  145. package/build-style/style-rtl.css +3 -12
  146. package/build-style/style.css +3 -12
  147. package/build-style/tabs-menu-item/editor-rtl.css +0 -5
  148. package/build-style/tabs-menu-item/editor.css +0 -5
  149. package/build-style/tabs-menu-item/style-rtl.css +3 -12
  150. package/build-style/tabs-menu-item/style.css +3 -12
  151. package/package.json +38 -38
  152. package/src/accordion/view.js +1 -44
  153. package/src/accordion-item/index.php +0 -1
  154. package/src/cover/index.php +4 -4
  155. package/src/icon/block.json +9 -1
  156. package/src/icon/components/custom-inserter/icon-grid.js +1 -1
  157. package/src/icon/components/custom-inserter/index.js +1 -1
  158. package/src/icon/edit.js +20 -10
  159. package/src/icon/index.php +1 -3
  160. package/src/image/edit.js +1 -1
  161. package/src/image/index.php +1 -4
  162. package/src/navigation/edit/index.js +4 -2
  163. package/src/navigation/edit/leaf-more-menu.js +86 -11
  164. package/src/navigation/edit/menu-inspector-controls.js +23 -142
  165. package/src/navigation/edit/navigation-link-ui.js +115 -0
  166. package/src/navigation/edit/navigation-list-view-header.js +62 -0
  167. package/src/navigation/edit/navigation-menu-selector.js +5 -3
  168. package/src/navigation/edit/placeholder/index.js +3 -2
  169. package/src/navigation/edit/test/navigation-menu-selector.js +23 -20
  170. package/src/navigation-link/editor.scss +18 -0
  171. package/src/navigation-link/shared/controls.js +35 -62
  172. package/src/navigation-link/shared/test/controls.js +5 -5
  173. package/src/navigation-link/shared/test/use-link-preview.test.js +10 -1
  174. package/src/navigation-link/shared/use-link-preview.js +13 -11
  175. package/src/navigation-overlay-close/edit.js +4 -3
  176. package/src/page-list/index.php +1 -1
  177. package/src/page-list-item/edit.js +8 -7
  178. package/src/post-featured-image/index.php +2 -4
  179. package/src/post-navigation-link/block.json +1 -3
  180. package/src/post-navigation-link/deprecated.js +72 -0
  181. package/src/post-navigation-link/edit.js +2 -35
  182. package/src/post-navigation-link/index.js +2 -0
  183. package/src/post-title/block.json +1 -3
  184. package/src/post-title/deprecated.js +86 -1
  185. package/src/post-title/edit.js +2 -18
  186. package/src/pullquote/deprecated.js +3 -3
  187. package/src/query/block.json +1 -2
  188. package/src/query-title/block.json +1 -3
  189. package/src/query-title/deprecated.js +74 -1
  190. package/src/query-title/edit.js +11 -27
  191. package/src/query-title/index.php +1 -1
  192. package/src/site-tagline/block.json +6 -4
  193. package/src/site-tagline/deprecated.js +70 -1
  194. package/src/site-tagline/edit.js +9 -22
  195. package/src/site-title/block.json +1 -3
  196. package/src/site-title/deprecated.js +83 -1
  197. package/src/site-title/edit.js +9 -22
  198. package/src/tabs-menu-item/block.json +1 -26
  199. package/src/tabs-menu-item/controls.js +0 -108
  200. package/src/tabs-menu-item/edit.js +6 -79
  201. package/src/tabs-menu-item/editor.scss +0 -6
  202. package/src/tabs-menu-item/save.js +1 -26
  203. package/src/tabs-menu-item/style.scss +3 -14
  204. package/build/navigation/use-navigation-entities.cjs +0 -67
  205. package/build/navigation/use-navigation-entities.cjs.map +0 -7
  206. package/build-module/navigation/use-navigation-entities.mjs +0 -46
  207. package/build-module/navigation/use-navigation-entities.mjs.map +0 -7
  208. package/src/navigation/use-navigation-entities.js +0 -72
@@ -6,12 +6,7 @@ import {
6
6
  InspectorControls,
7
7
  store as blockEditorStore,
8
8
  } from '@wordpress/block-editor';
9
- import {
10
- PanelBody,
11
- Spinner,
12
- __experimentalHStack as HStack,
13
- __experimentalHeading as Heading,
14
- } from '@wordpress/components';
9
+ import { PanelBody, Spinner } from '@wordpress/components';
15
10
  import { useSelect, useDispatch } from '@wordpress/data';
16
11
  import { __, sprintf } from '@wordpress/i18n';
17
12
  import { useContext } from '@wordpress/element';
@@ -24,121 +19,18 @@ import { unlock } from '../../lock-unlock';
24
19
  import DeletedNavigationWarning from './deleted-navigation-warning';
25
20
  import useNavigationMenu from '../use-navigation-menu';
26
21
  import LeafMoreMenu from './leaf-more-menu';
27
- import {
28
- LinkUI,
29
- updateAttributes,
30
- useEntityBinding,
31
- } from '../../navigation-link/shared';
22
+ import { NavigationLinkUI } from './navigation-link-ui';
23
+ import NavigationListViewHeader from './navigation-list-view-header';
32
24
 
33
25
  const actionLabel =
34
26
  /* translators: %s: The name of a menu. */ __( "Switch to '%s'" );
35
- const BLOCKS_WITH_LINK_UI_SUPPORT = [
36
- 'core/navigation-link',
37
- 'core/navigation-submenu',
38
- ];
39
27
  const {
40
28
  PrivateListView,
41
- useBlockDisplayTitle,
42
29
  PrivateBlockContext,
43
30
  useListViewPanelState,
31
+ useBlockDisplayTitle,
44
32
  } = unlock( blockEditorPrivateApis );
45
33
 
46
- function AdditionalBlockContent( { block, insertedBlock, setInsertedBlock } ) {
47
- const { updateBlockAttributes, removeBlock } =
48
- useDispatch( blockEditorStore );
49
-
50
- const supportsLinkControls = BLOCKS_WITH_LINK_UI_SUPPORT?.includes(
51
- insertedBlock?.name
52
- );
53
- const blockWasJustInserted = insertedBlock?.clientId === block.clientId;
54
- const showLinkControls = supportsLinkControls && blockWasJustInserted;
55
-
56
- // Get binding utilities for the inserted block
57
- const { createBinding, clearBinding } = useEntityBinding( {
58
- clientId: insertedBlock?.clientId,
59
- attributes: insertedBlock?.attributes || {},
60
- } );
61
-
62
- if ( ! showLinkControls ) {
63
- return null;
64
- }
65
-
66
- /**
67
- * Cleanup function for auto-inserted Navigation Link blocks.
68
- *
69
- * Removes the block if it has no URL and clears the inserted block state.
70
- * This ensures consistent cleanup behavior across different contexts.
71
- */
72
- const cleanupInsertedBlock = () => {
73
- // Prevent automatic block selection when removing blocks in list view context
74
- // This avoids focus stealing that would close the list view and switch to canvas
75
- const shouldAutoSelectBlock = false;
76
-
77
- // Follows the exact same pattern as Navigation Link block's onClose handler
78
- // If there is no URL then remove the auto-inserted block to avoid empty blocks
79
- if ( ! insertedBlock?.attributes?.url && insertedBlock?.clientId ) {
80
- // Remove the block entirely to avoid poor UX
81
- // This matches the Navigation Link block's behavior
82
- removeBlock( insertedBlock.clientId, shouldAutoSelectBlock );
83
- }
84
- setInsertedBlock( null );
85
- };
86
-
87
- const setInsertedBlockAttributes =
88
- ( _insertedBlockClientId ) => ( _updatedAttributes ) => {
89
- if ( ! _insertedBlockClientId ) {
90
- return;
91
- }
92
- updateBlockAttributes( _insertedBlockClientId, _updatedAttributes );
93
- };
94
-
95
- // Wrapper function to clean up original block when a new block is selected
96
- const handleSetInsertedBlock = ( newBlock ) => {
97
- // Prevent automatic block selection when removing blocks in list view context
98
- // This avoids focus stealing that would close the list view and switch to canvas
99
- const shouldAutoSelectBlock = false;
100
-
101
- // If we have an existing inserted block and a new block is being set,
102
- // remove the original block to avoid duplicates
103
- if ( insertedBlock?.clientId && newBlock ) {
104
- removeBlock( insertedBlock.clientId, shouldAutoSelectBlock );
105
- }
106
- setInsertedBlock( newBlock );
107
- };
108
-
109
- return (
110
- <LinkUI
111
- clientId={ insertedBlock?.clientId }
112
- link={ insertedBlock?.attributes }
113
- onBlockInsert={ handleSetInsertedBlock }
114
- onClose={ () => {
115
- // Use cleanup function
116
- cleanupInsertedBlock();
117
- } }
118
- onChange={ ( updatedValue ) => {
119
- // updateAttributes determines the final state and returns metadata
120
- const { isEntityLink, attributes: updatedAttributes } =
121
- updateAttributes(
122
- updatedValue,
123
- setInsertedBlockAttributes( insertedBlock?.clientId ),
124
- insertedBlock?.attributes
125
- );
126
-
127
- // Handle URL binding based on the final computed state
128
- // Only create bindings for entity links (posts, pages, taxonomies)
129
- // Never create bindings for custom links (manual URLs)
130
- if ( isEntityLink ) {
131
- createBinding( updatedAttributes );
132
- } else {
133
- clearBinding();
134
- }
135
-
136
- setInsertedBlock( null );
137
- } }
138
- />
139
- );
140
- }
141
-
142
34
  const MainContent = ( {
143
35
  clientId,
144
36
  currentMenuId,
@@ -194,7 +86,7 @@ const MainContent = ( {
194
86
  description={ description }
195
87
  showAppender
196
88
  blockSettingsMenu={ LeafMoreMenu }
197
- additionalBlockContent={ AdditionalBlockContent }
89
+ additionalBlockContent={ NavigationLinkUI }
198
90
  onSelect={ openListViewContentPanel }
199
91
  />
200
92
  </div>
@@ -232,34 +124,23 @@ const MenuInspectorControls = ( props ) => {
232
124
  return (
233
125
  <InspectorControls group="list">
234
126
  <PanelBody title={ null }>
235
- <HStack className="wp-block-navigation-off-canvas-editor__header">
236
- <Heading
237
- className="wp-block-navigation-off-canvas-editor__title"
238
- level={ 2 }
239
- >
240
- { blockTitle }
241
- </Heading>
242
- { blockEditingMode === 'default' && (
243
- <NavigationMenuSelector
244
- currentMenuId={ currentMenuId }
245
- onSelectClassicMenu={ onSelectClassicMenu }
246
- onSelectNavigationMenu={
247
- onSelectNavigationMenu
248
- }
249
- onCreateNew={ onCreateNew }
250
- createNavigationMenuIsSuccess={
251
- createNavigationMenuIsSuccess
252
- }
253
- createNavigationMenuIsError={
254
- createNavigationMenuIsError
255
- }
256
- actionLabel={ actionLabel }
257
- isManageMenusButtonDisabled={
258
- isManageMenusButtonDisabled
259
- }
260
- />
261
- ) }
262
- </HStack>
127
+ <NavigationListViewHeader
128
+ clientId={ clientId }
129
+ blockEditingMode={ blockEditingMode }
130
+ currentMenuId={ currentMenuId }
131
+ onSelectClassicMenu={ onSelectClassicMenu }
132
+ onSelectNavigationMenu={ onSelectNavigationMenu }
133
+ onCreateNew={ onCreateNew }
134
+ createNavigationMenuIsSuccess={
135
+ createNavigationMenuIsSuccess
136
+ }
137
+ createNavigationMenuIsError={
138
+ createNavigationMenuIsError
139
+ }
140
+ isManageMenusButtonDisabled={
141
+ isManageMenusButtonDisabled
142
+ }
143
+ />
263
144
  <MainContent
264
145
  { ...props }
265
146
  expandRevision={ expandRevision }
@@ -273,7 +154,7 @@ const MenuInspectorControls = ( props ) => {
273
154
  return (
274
155
  <InspectorControls group="list">
275
156
  <PanelBody
276
- title={ __( 'Navigation' ) }
157
+ title={ blockTitle }
277
158
  opened={ isOpened }
278
159
  onToggle={ handleToggle }
279
160
  >
@@ -0,0 +1,115 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { store as blockEditorStore } from '@wordpress/block-editor';
5
+ import { useDispatch } from '@wordpress/data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import {
11
+ LinkUI,
12
+ updateAttributes,
13
+ useEntityBinding,
14
+ } from '../../navigation-link/shared';
15
+
16
+ const BLOCKS_WITH_LINK_UI_SUPPORT = [
17
+ 'core/navigation-link',
18
+ 'core/navigation-submenu',
19
+ ];
20
+
21
+ export function NavigationLinkUI( { block, insertedBlock, setInsertedBlock } ) {
22
+ const { updateBlockAttributes, removeBlock } =
23
+ useDispatch( blockEditorStore );
24
+
25
+ const supportsLinkControls = BLOCKS_WITH_LINK_UI_SUPPORT?.includes(
26
+ insertedBlock?.name
27
+ );
28
+ const blockWasJustInserted = insertedBlock?.clientId === block.clientId;
29
+ const showLinkControls = supportsLinkControls && blockWasJustInserted;
30
+
31
+ // Get binding utilities for the inserted block
32
+ const { createBinding, clearBinding } = useEntityBinding( {
33
+ clientId: insertedBlock?.clientId,
34
+ attributes: insertedBlock?.attributes || {},
35
+ } );
36
+
37
+ if ( ! showLinkControls ) {
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * Cleanup function for auto-inserted Navigation Link blocks.
43
+ *
44
+ * Removes the block if it has no URL and clears the inserted block state.
45
+ * This ensures consistent cleanup behavior across different contexts.
46
+ */
47
+ const cleanupInsertedBlock = () => {
48
+ // Prevent automatic block selection when removing blocks in list view context
49
+ // This avoids focus stealing that would close the list view and switch to canvas
50
+ const shouldAutoSelectBlock = false;
51
+
52
+ // Follows the exact same pattern as Navigation Link block's onClose handler
53
+ // If there is no URL then remove the auto-inserted block to avoid empty blocks
54
+ if ( ! insertedBlock?.attributes?.url && insertedBlock?.clientId ) {
55
+ // Remove the block entirely to avoid poor UX
56
+ // This matches the Navigation Link block's behavior
57
+ removeBlock( insertedBlock.clientId, shouldAutoSelectBlock );
58
+ }
59
+ setInsertedBlock( null );
60
+ };
61
+
62
+ const setInsertedBlockAttributes =
63
+ ( _insertedBlockClientId ) => ( _updatedAttributes ) => {
64
+ if ( ! _insertedBlockClientId ) {
65
+ return;
66
+ }
67
+ updateBlockAttributes( _insertedBlockClientId, _updatedAttributes );
68
+ };
69
+
70
+ // Wrapper function to clean up original block when a new block is selected
71
+ const handleSetInsertedBlock = ( newBlock ) => {
72
+ // Prevent automatic block selection when removing blocks in list view context
73
+ // This avoids focus stealing that would close the list view and switch to canvas
74
+ const shouldAutoSelectBlock = false;
75
+
76
+ // If we have an existing inserted block and a new block is being set,
77
+ // remove the original block to avoid duplicates
78
+ if ( insertedBlock?.clientId && newBlock ) {
79
+ removeBlock( insertedBlock.clientId, shouldAutoSelectBlock );
80
+ }
81
+ setInsertedBlock( newBlock );
82
+ };
83
+
84
+ return (
85
+ <LinkUI
86
+ clientId={ insertedBlock?.clientId }
87
+ link={ insertedBlock?.attributes }
88
+ onBlockInsert={ handleSetInsertedBlock }
89
+ onClose={ () => {
90
+ // Use cleanup function
91
+ cleanupInsertedBlock();
92
+ } }
93
+ onChange={ ( updatedValue ) => {
94
+ // updateAttributes determines the final state and returns metadata
95
+ const { isEntityLink, attributes: updatedAttributes } =
96
+ updateAttributes(
97
+ updatedValue,
98
+ setInsertedBlockAttributes( insertedBlock?.clientId ),
99
+ insertedBlock?.attributes
100
+ );
101
+
102
+ // Handle URL binding based on the final computed state
103
+ // Only create bindings for entity links (posts, pages, taxonomies)
104
+ // Never create bindings for custom links (manual URLs)
105
+ if ( isEntityLink ) {
106
+ createBinding( updatedAttributes );
107
+ } else {
108
+ clearBinding();
109
+ }
110
+
111
+ setInsertedBlock( null );
112
+ } }
113
+ />
114
+ );
115
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ __experimentalHStack as HStack,
6
+ __experimentalHeading as Heading,
7
+ } from '@wordpress/components';
8
+ import { __ } from '@wordpress/i18n';
9
+ import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import NavigationMenuSelector from './navigation-menu-selector';
15
+ import { unlock } from '../../lock-unlock';
16
+
17
+ const { useBlockDisplayTitle } = unlock( blockEditorPrivateApis );
18
+
19
+ const actionLabel =
20
+ /* translators: %s: The name of a menu. */ __( "Switch to '%s'" );
21
+
22
+ export default function NavigationListViewHeader( {
23
+ clientId,
24
+ blockEditingMode,
25
+ currentMenuId,
26
+ onSelectClassicMenu,
27
+ onSelectNavigationMenu,
28
+ onCreateNew,
29
+ createNavigationMenuIsSuccess,
30
+ createNavigationMenuIsError,
31
+ isManageMenusButtonDisabled,
32
+ } ) {
33
+ const blockTitle = useBlockDisplayTitle( {
34
+ clientId,
35
+ context: 'list-view',
36
+ } );
37
+
38
+ return (
39
+ <HStack className="wp-block-navigation-off-canvas-editor__header">
40
+ <Heading
41
+ className="wp-block-navigation-off-canvas-editor__title"
42
+ level={ 2 }
43
+ >
44
+ { blockTitle }
45
+ </Heading>
46
+ { blockEditingMode === 'default' && (
47
+ <NavigationMenuSelector
48
+ currentMenuId={ currentMenuId }
49
+ onSelectClassicMenu={ onSelectClassicMenu }
50
+ onSelectNavigationMenu={ onSelectNavigationMenu }
51
+ onCreateNew={ onCreateNew }
52
+ createNavigationMenuIsSuccess={
53
+ createNavigationMenuIsSuccess
54
+ }
55
+ createNavigationMenuIsError={ createNavigationMenuIsError }
56
+ actionLabel={ actionLabel }
57
+ isManageMenusButtonDisabled={ isManageMenusButtonDisabled }
58
+ />
59
+ ) }
60
+ </HStack>
61
+ );
62
+ }
@@ -11,13 +11,12 @@ import { moreVertical } from '@wordpress/icons';
11
11
  import { __, sprintf } from '@wordpress/i18n';
12
12
  import { decodeEntities } from '@wordpress/html-entities';
13
13
  import { useEffect, useMemo, useState } from '@wordpress/element';
14
- import { useEntityProp } from '@wordpress/core-data';
14
+ import { useEntityRecords, useEntityProp } from '@wordpress/core-data';
15
15
 
16
16
  /**
17
17
  * Internal dependencies
18
18
  */
19
19
  import useNavigationMenu from '../use-navigation-menu';
20
- import useNavigationEntities from '../use-navigation-entities';
21
20
 
22
21
  function buildMenuLabel( title, id, status ) {
23
22
  if ( ! title ) {
@@ -53,7 +52,10 @@ function NavigationMenuSelector( {
53
52
 
54
53
  actionLabel = actionLabel || createActionLabel;
55
54
 
56
- const { menus: classicMenus } = useNavigationEntities();
55
+ const { records: classicMenus } = useEntityRecords( 'root', 'menu', {
56
+ per_page: -1,
57
+ context: 'view',
58
+ } );
57
59
 
58
60
  const {
59
61
  navigationMenus,
@@ -6,11 +6,11 @@ import { __ } from '@wordpress/i18n';
6
6
  import { navigation, Icon } from '@wordpress/icons';
7
7
  import { speak } from '@wordpress/a11y';
8
8
  import { useEffect } from '@wordpress/element';
9
+ import { useEntityRecords } from '@wordpress/core-data';
9
10
 
10
11
  /**
11
12
  * Internal dependencies
12
13
  */
13
- import useNavigationEntities from '../../use-navigation-entities';
14
14
  import PlaceholderPreview from './placeholder-preview';
15
15
  import NavigationMenuSelector from '../navigation-menu-selector';
16
16
 
@@ -24,7 +24,8 @@ export default function NavigationPlaceholder( {
24
24
  onSelectClassicMenu,
25
25
  onCreateEmpty,
26
26
  } ) {
27
- const { isResolvingMenus, hasResolvedMenus } = useNavigationEntities();
27
+ const { isResolving: isResolvingMenus, hasResolved: hasResolvedMenus } =
28
+ useEntityRecords( 'root', 'menu', { per_page: -1, context: 'view' } );
28
29
 
29
30
  useEffect( () => {
30
31
  if ( ! isSelected ) {
@@ -4,12 +4,16 @@
4
4
  import { render, screen } from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
 
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { useEntityRecords } from '@wordpress/core-data';
11
+
7
12
  /**
8
13
  * Internal dependencies
9
14
  */
10
15
  import NavigationMenuSelector from '../navigation-menu-selector';
11
16
  import useNavigationMenu from '../../use-navigation-menu';
12
- import useNavigationEntities from '../../use-navigation-entities';
13
17
 
14
18
  jest.mock( '../../use-navigation-menu', () => {
15
19
  // This allows us to tweak the returned value on each test.
@@ -17,14 +21,13 @@ jest.mock( '../../use-navigation-menu', () => {
17
21
  return mock;
18
22
  } );
19
23
 
20
- jest.mock( '../../use-navigation-entities', () => {
21
- // This allows us to tweak the returned value on each test.
22
- const mock = jest.fn();
23
- return mock;
24
- } );
24
+ jest.mock( '@wordpress/core-data', () => ( {
25
+ ...jest.requireActual( '@wordpress/core-data' ),
26
+ useEntityRecords: jest.fn(),
27
+ } ) );
25
28
 
26
- useNavigationEntities.mockReturnValue( {
27
- menus: [],
29
+ useEntityRecords.mockReturnValue( {
30
+ records: [],
28
31
  } );
29
32
 
30
33
  const navigationMenu1 = {
@@ -485,8 +488,8 @@ describe( 'NavigationMenuSelector', () => {
485
488
  it( 'should not show classic menus if there are no classic menus', async () => {
486
489
  const user = userEvent.setup();
487
490
 
488
- useNavigationEntities.mockReturnValue( {
489
- menus: [],
491
+ useEntityRecords.mockReturnValue( {
492
+ records: [],
490
493
  } );
491
494
 
492
495
  render( <NavigationMenuSelector /> );
@@ -507,8 +510,8 @@ describe( 'NavigationMenuSelector', () => {
507
510
  canUserCreateNavigationMenus: false,
508
511
  } );
509
512
 
510
- useNavigationEntities.mockReturnValue( {
511
- menus: classicMenusFixture,
513
+ useEntityRecords.mockReturnValue( {
514
+ records: classicMenusFixture,
512
515
  } );
513
516
 
514
517
  render( <NavigationMenuSelector /> );
@@ -529,8 +532,8 @@ describe( 'NavigationMenuSelector', () => {
529
532
  canUserCreateNavigationMenus: true,
530
533
  } );
531
534
 
532
- useNavigationEntities.mockReturnValue( {
533
- menus: classicMenusFixture,
535
+ useEntityRecords.mockReturnValue( {
536
+ records: classicMenusFixture,
534
537
  } );
535
538
 
536
539
  render( <NavigationMenuSelector /> );
@@ -566,8 +569,8 @@ describe( 'NavigationMenuSelector', () => {
566
569
  canUserCreateNavigationMenus: true,
567
570
  } );
568
571
 
569
- useNavigationEntities.mockReturnValue( {
570
- menus: classicMenusFixture,
572
+ useEntityRecords.mockReturnValue( {
573
+ records: classicMenusFixture,
571
574
  } );
572
575
 
573
576
  const { rerender } = render(
@@ -598,8 +601,8 @@ describe( 'NavigationMenuSelector', () => {
598
601
  canUserCreateNavigationMenus: true,
599
602
  } );
600
603
 
601
- useNavigationEntities.mockReturnValue( {
602
- menus: classicMenusFixture,
604
+ useEntityRecords.mockReturnValue( {
605
+ records: classicMenusFixture,
603
606
  } );
604
607
 
605
608
  rerender(
@@ -631,8 +634,8 @@ describe( 'NavigationMenuSelector', () => {
631
634
  canUserCreateNavigationMenus: true,
632
635
  } );
633
636
 
634
- useNavigationEntities.mockReturnValue( {
635
- menus: classicMenusFixture,
637
+ useEntityRecords.mockReturnValue( {
638
+ records: classicMenusFixture,
636
639
  } );
637
640
 
638
641
  // Simulate the menu being created and component being re-rendered.
@@ -150,3 +150,21 @@
150
150
  .navigation-link-control__error-text {
151
151
  color: $alert-red;
152
152
  }
153
+
154
+ .navigation-link-to__action-button {
155
+ grid-column: auto;
156
+ // When this is the last button AND in an odd position among its class
157
+ // siblings, it's unpaired — span the full grid row.
158
+ &:nth-last-child(1 of #{&}):nth-child(odd of #{&}) {
159
+ grid-column: 1 / -1;
160
+ }
161
+
162
+ // Artificially increase specificity to override `justify-content` styles.
163
+ // Not ideal, but it shouldn't be necessary once switching to the new `Button`
164
+ // component from `@wordpress/ui`.
165
+ &#{&}#{&} {
166
+ justify-content: center;
167
+ }
168
+
169
+ }
170
+
@@ -5,7 +5,6 @@ import {
5
5
  Button,
6
6
  __experimentalToolsPanel as ToolsPanel,
7
7
  __experimentalToolsPanelItem as ToolsPanelItem,
8
- __experimentalHStack as HStack,
9
8
  CheckboxControl,
10
9
  TextControl,
11
10
  TextareaControl,
@@ -150,7 +149,7 @@ export function Controls( {
150
149
 
151
150
  // Check if URL is viewable (not hash link or other relative path like ./ or ../)
152
151
  const isViewableUrl =
153
- url &&
152
+ !! url &&
154
153
  ( ! isHashLink( url ) ||
155
154
  ( isRelativePath( url ) && ! url.startsWith( '/' ) ) );
156
155
 
@@ -158,11 +157,6 @@ export function Controls( {
158
157
  const viewUrl =
159
158
  isViewableUrl && url.startsWith( '/' ) && homeUrl ? homeUrl + url : url;
160
159
 
161
- const entityTypeName = getEntityTypeName(
162
- attributes.type,
163
- attributes.kind
164
- );
165
-
166
160
  return (
167
161
  <ToolsPanel
168
162
  label={ __( 'Settings' ) }
@@ -220,61 +214,6 @@ export function Controls( {
220
214
  help={ helpText ? helpText : undefined }
221
215
  />
222
216
  </ToolsPanelItem>
223
-
224
- { url && (
225
- <HStack
226
- className="navigation-link-to__actions"
227
- alignment="left"
228
- justify="left"
229
- style={ { gridColumn: '1 / -1' } }
230
- >
231
- { hasUrlBinding &&
232
- isBoundEntityAvailable &&
233
- entityRecord?.id &&
234
- attributes.kind === 'post-type' &&
235
- onNavigateToEntityRecord && (
236
- <Button
237
- size="compact"
238
- variant="secondary"
239
- onClick={ () => {
240
- onNavigateToEntityRecord( {
241
- postId: entityRecord.id,
242
- postType: attributes.type,
243
- } );
244
- } }
245
- __next40pxDefaultSize
246
- >
247
- { sprintf(
248
- /* translators: %s: entity type (e.g., "page", "post", "category") */
249
- __( 'Edit %s' ),
250
- entityTypeName
251
- ) }
252
- </Button>
253
- ) }
254
- { isViewableUrl && (
255
- <Button
256
- size="compact"
257
- variant="secondary"
258
- href={ viewUrl }
259
- target="_blank"
260
- icon={ external }
261
- iconPosition="right"
262
- __next40pxDefaultSize
263
- >
264
- { sprintf(
265
- /* translators: %s: entity type (e.g., "page", "post", "category") or "link" for external links */
266
- __( 'View %s' ),
267
- attributes.kind &&
268
- attributes.type &&
269
- attributes.kind !== 'custom'
270
- ? entityTypeName
271
- : __( 'link' )
272
- ) }
273
- </Button>
274
- ) }
275
- </HStack>
276
- ) }
277
-
278
217
  <ToolsPanelItem
279
218
  hasValue={ () => !! opensInNewTab }
280
219
  label={ __( 'Open in new tab' ) }
@@ -291,6 +230,40 @@ export function Controls( {
291
230
  }
292
231
  />
293
232
  </ToolsPanelItem>
233
+
234
+ { !! url &&
235
+ hasUrlBinding &&
236
+ isBoundEntityAvailable &&
237
+ entityRecord?.id &&
238
+ attributes.kind === 'post-type' &&
239
+ onNavigateToEntityRecord && (
240
+ <Button
241
+ variant="secondary"
242
+ onClick={ () => {
243
+ onNavigateToEntityRecord( {
244
+ postId: entityRecord.id,
245
+ postType: attributes.type,
246
+ } );
247
+ } }
248
+ __next40pxDefaultSize
249
+ className="navigation-link-to__action-button"
250
+ >
251
+ { __( 'Edit' ) }
252
+ </Button>
253
+ ) }
254
+ { isViewableUrl && (
255
+ <Button
256
+ variant="secondary"
257
+ href={ viewUrl }
258
+ target="_blank"
259
+ icon={ external }
260
+ iconPosition="right"
261
+ __next40pxDefaultSize
262
+ className="navigation-link-to__action-button"
263
+ >
264
+ { __( 'View' ) }
265
+ </Button>
266
+ ) }
294
267
  </>
295
268
  ) }
296
269