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( + $( '