diff --git a/src/js/_enqueues/lib/nav-menu.js b/src/js/_enqueues/lib/nav-menu.js
index 79917c8447f1a..7b6298fe69d65 100644
--- a/src/js/_enqueues/lib/nav-menu.js
+++ b/src/js/_enqueues/lib/nav-menu.js
@@ -47,6 +47,7 @@
this.attachBulkSelectButtonListeners();
this.attachMenuCheckBoxListeners();
+ this.attachMenuItemMoveButton();
this.attachMenuItemDeleteButton();
this.attachPendingMenuItemsListForDeletion();
@@ -518,6 +519,7 @@
api.refreshAdvancedAccessibility();
thisItem.updateParentDropdown();
thisItem.updateOrderDropdown();
+ api.updateBulkMoveControls();
if ( a11ySpeech ) {
wp.a11y.speak( a11ySpeech );
@@ -616,6 +618,7 @@
menuItem.insertAfter( menuItems.eq( menuItemNewPosition ) ).updateParentMenuItemDBId().updateParentDropdown().updateOrderDropdown();
+ api.updateBulkMoveControls();
api.registerChange();
api.refreshKeyboardAccessibility();
api.refreshAdvancedAccessibility();
@@ -661,6 +664,7 @@
menuItem.detach().insertAfter( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
}
+ api.updateBulkMoveControls();
api.registerChange();
api.refreshKeyboardAccessibility();
api.refreshAdvancedAccessibility();
@@ -975,6 +979,7 @@
ui.item.updateParentDropdown();
ui.item.updateOrderDropdown();
api.refreshAdvancedAccessibilityOfItem( ui.item.find( 'a.item-edit' ) );
+ api.updateBulkMoveControls();
},
change: function(e, ui) {
// Make sure the placeholder is inside the menu.
@@ -1213,6 +1218,7 @@
$( '.menu-items-delete' ).addClass( 'disabled' );
$( '#pending-menu-items-to-delete ul' ).empty();
+ this.updateBulkMoveControls();
},
/**
@@ -1225,9 +1231,246 @@
$( '#menu-to-edit' ).on( 'change', '.menu-item-checkbox', function() {
that.setRemoveSelectedButtonStatus();
+ that.updateBulkMoveControls();
});
},
+ /**
+ * Get the topmost selected menu items.
+ *
+ * A selected child is omitted when its parent is also selected, because
+ * moving the parent already moves the entire subtree.
+ *
+ * @since 7.2.0
+ *
+ * @return {jQuery} Selected menu items without selected ancestors.
+ */
+ getSelectedMenuItems : function() {
+ var selectedMenuItems = $(),
+ selectedDepth = -1;
+
+ api.menuList.children( '.menu-item' ).each( function() {
+ var menuItem = $( this ),
+ depth = menuItem.menuItemDepth();
+
+ if ( depth <= selectedDepth ) {
+ selectedDepth = -1;
+ }
+
+ if ( -1 === selectedDepth && menuItem.find( '.menu-item-checkbox' ).prop( 'checked' ) ) {
+ selectedMenuItems = selectedMenuItems.add( menuItem );
+ selectedDepth = depth;
+ }
+ });
+
+ return selectedMenuItems;
+ },
+
+ /**
+ * Get the greatest depth below any selected root.
+ *
+ * @since 7.2.0
+ *
+ * @param {jQuery} selectedMenuItems Selected root menu items.
+ * @return {number} Greatest relative child depth.
+ */
+ getSelectedMenuItemsMaxChildDepth : function( selectedMenuItems ) {
+ var maxChildDepth = 0;
+
+ selectedMenuItems.each( function() {
+ var menuItem = $( this ),
+ depth = menuItem.menuItemDepth();
+
+ menuItem.childMenuItems().each( function() {
+ maxChildDepth = Math.max( maxChildDepth, $( this ).menuItemDepth() - depth );
+ });
+ });
+
+ return maxChildDepth;
+ },
+
+ /**
+ * Populate and set the state of the bulk move controls.
+ *
+ * @since 7.2.0
+ */
+ updateBulkMoveControls : function() {
+ var menuItems = api.menuList.children( '.menu-item' ),
+ selectedMenuItems = api.getSelectedMenuItems(),
+ excludedMenuItems = selectedMenuItems.add( selectedMenuItems.childMenuItems() ),
+ maxChildDepth = api.getSelectedMenuItemsMaxChildDepth( selectedMenuItems ),
+ fieldset = $( '#bulk-menu-items-move' ),
+ parentDropdown = $( '#bulk-menu-item-parent' ),
+ positionDropdown = $( '#bulk-menu-item-position' ),
+ selectOption = parentDropdown.find( 'option' ).first().clone();
+
+ fieldset.prop( 'disabled', ! selectedMenuItems.length );
+ parentDropdown.empty().append( selectOption );
+ positionDropdown.val( '' ).prop( 'disabled', true );
+ $( '.menu-items-move' ).prop( 'disabled', true );
+
+ if ( ! selectedMenuItems.length ) {
+ return;
+ }
+
+ parentDropdown.append(
+ $( '