Make WordPress Core

Changeset 51006


Ignore:
Timestamp:
05/25/2021 05:41:11 PM (5 years ago)
Author:
joedolson
Message:

Menus: Add bulk delete for menu items.

Adds an option to bulk delete menu items from the core Navigation Menus screen, offering a considerable usability benefit when making significant changes to navigation menus.

Uses the bulk select pattern found in post and media lists for consistency with other core management screens, rather than the rapid delete pattern found in menus in the customizer.

Props wphound, welcher, melchoyce, maxpertici, audrasjb
Fixes #21603.

Location:
trunk/src
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/lib/nav-menu.js

    r50547 r51006  
    4545
    4646                        this.attachMenuEditListeners();
     47
     48                        this.attachBulkSelectButtonListeners();
     49                        this.attachMenuCheckBoxListeners();
     50                        this.attachMenuItemDeleteButton();
     51                        this.attachPendingMenuItemsListForDeletion();
    4752
    4853                        this.attachQuickSearchListeners();
    … …  
    863868                },
    864869
     870                /**
     871                 * Handle toggling bulk selection checkboxes for menu items.
     872                 *
     873                 * @since 5.8.0
     874                 */
     875                attachBulkSelectButtonListeners : function() {
     876                        var that = this;
     877
     878                        $('.bulk-select-switcher').on( 'change', function(){
     879                                if( this.checked ){
     880                                        $('.bulk-select-switcher').prop('checked',true);
     881                                        that.enableBulkSelection();
     882                                }else{
     883                                        $('.bulk-select-switcher').prop('checked',false);
     884                                        that.disableBulkSelection();
     885                                }
     886                        });
     887                },
     888
     889                /**
     890                 * Enable bulk selection checkboxes for menu items.
     891                 *
     892                 * @since 5.8.0
     893                 */
     894                enableBulkSelection : function() {
     895                        $('#menu-to-edit').addClass('bulk-selection');
     896                        $('#nav-menu-bulk-actions-top').addClass('bulk-selection');
     897                        $('#nav-menu-bulk-actions-bottom').addClass('bulk-selection');
     898
     899                        var checkbox = $('#menu-to-edit .menu-item-checkbox');
     900                        $.each( checkbox, function(){
     901                                $(this).prop('disabled',false);
     902                        });
     903                },
     904
     905                /**
     906                 * Disable bulk selection checkboxes for menu items.
     907                 *
     908                 * @since 5.8.0
     909                 */
     910                disableBulkSelection : function() {
     911                        $('#menu-to-edit').removeClass('bulk-selection');
     912                        $('#nav-menu-bulk-actions-top').removeClass('bulk-selection');
     913                        $('#nav-menu-bulk-actions-bottom').removeClass('bulk-selection');
     914
     915                        if ( $('.menu-items-delete').is('[aria-describedby="pending-menu-items-to-delete"]') ) {
     916                                $('.menu-items-delete').removeAttr( 'aria-describedby' );
     917                        }
     918
     919                        var checkbox = $('#menu-to-edit .menu-item-checkbox');
     920                        $.each( checkbox, function(){
     921                                $(this).prop('disabled',true).prop('checked',false);
     922                        });
     923                        $('.menu-items-delete').addClass('disabled');
     924                        $('#pending-menu-items-to-delete ul').empty();
     925                },
     926
     927                /**
     928                 * Listen for state changes on bulk action checkboxes.
     929                 *
     930                 * @since 5.8.0
     931                 */
     932                attachMenuCheckBoxListeners : function() {
     933                        var that = this;
     934
     935                        $( '#menu-to-edit' ).on( 'change', '.menu-item-checkbox', function(){
     936                                that.setRemoveSelectedButtonStatus();
     937                        });
     938                },
     939
     940                /**
     941                 * Create delete button to remove menu items from collection.
     942                 *
     943                 * @since 5.8.0
     944                 */
     945                attachMenuItemDeleteButton : function() {
     946                        var that = this;
     947
     948                        $( document ).on('click', '.menu-items-delete', function(e){
     949                                e.preventDefault();
     950
     951                                if( ! $(this).hasClass('disabled') ){
     952                                        $.each( $('.menu-item-checkbox:checked'), function( index, element ) {
     953                                                $( element ).parents( 'li' ).find( 'a.item-delete' ).trigger( 'click' );
     954                                        });
     955
     956                                        $('.menu-items-delete').addClass('disabled');
     957                                        $('.bulk-select-switcher').prop('checked',false);
     958
     959                                        var itemsPendingDeletion = '';
     960                                        var itemsPendingDeletionList = $('#pending-menu-items-to-delete ul li');
     961
     962                                        $.each( itemsPendingDeletionList, function( index, element ) {
     963                                                var itemName = $( element ).find( '.pending-menu-item-name' ).text();
     964                                                var itemSpeech = menus.menuItemDeletion.replace( '%s', itemName );
     965                                                itemsPendingDeletion += itemSpeech ;
     966                                                if( ( index + 1 ) < itemsPendingDeletionList.length ){
     967                                                        itemsPendingDeletion += ', ' ;
     968                                                }
     969                                        });
     970
     971                                        var deletionSpeech = menus.itemsDeleted.replace( '%s', itemsPendingDeletion );
     972                                        wp.a11y.speak( deletionSpeech, 'polite' );
     973                                        that.disableBulkSelection();
     974                                }
     975                        });
     976                },
     977
     978                /**
     979                 * List menu items awaiting deletion.
     980                 *
     981                 * @since 5.8.0
     982                 */
     983                attachPendingMenuItemsListForDeletion : function(){
     984                        $('#post-body-content').on( 'change', '.menu-item-checkbox', function(){
     985
     986                                if( ! $('.menu-items-delete').is('[aria-describedby="pending-menu-items-to-delete"]') ){ $('.menu-items-delete').attr( 'aria-describedby', 'pending-menu-items-to-delete' ); }
     987
     988                                var menuItemName = $(this).next().text();
     989                                var menuItemType = $(this).parent().next('.item-controls').find('.item-type').text();
     990                                var menuItemID = $(this).attr('data-menu-item-id') ;
     991
     992                                var ListedMenuItem = $('#pending-menu-items-to-delete ul').find('[data-menu-item-id=' + menuItemID + ']') ;
     993                                if( ListedMenuItem.length > 0 ){
     994                                        ListedMenuItem.remove();
     995                                }
     996                                if( this.checked === true ){
     997                                        $('#pending-menu-items-to-delete ul').append( '<li data-menu-item-id="' + menuItemID + '"><span class="pending-menu-item-name">' + menuItemName + '</span> <span class="pending-menu-item-type">(' + menuItemType + ')</span><span class="separator"></span></li>' );
     998                                }
     999
     1000                                $('#pending-menu-items-to-delete li .separator').html(', ');
     1001                                $('#pending-menu-items-to-delete li .separator').last().html('.');
     1002                        });
     1003                },
     1004
     1005                /**
     1006                 * Set status of bulk delete checkbox.
     1007                 *
     1008                 * @since 5.8.0
     1009                 */
     1010                setBulkDeleteCheckboxStatus : function(){
     1011
     1012                        var that = this;
     1013                        var checkbox = $('#menu-to-edit .menu-item-checkbox');
     1014
     1015                        $.each( checkbox, function(){
     1016
     1017                                if( $(this).prop('disabled') == true ){
     1018                                        $(this).prop( 'disabled', false );
     1019                                }else{
     1020                                        $(this).prop( 'disabled', true );
     1021                                }
     1022
     1023                                if( $(this).is(':checked')) {
     1024                                        $(this).prop('checked',false);
     1025                                }
     1026                        });
     1027
     1028                        that.setRemoveSelectedButtonStatus();
     1029                },
     1030
     1031                /**
     1032                 * Set status of menu items removal button.
     1033                 *
     1034                 * @since 5.8.0
     1035                 */
     1036                setRemoveSelectedButtonStatus : function(){
     1037
     1038                        var button = $('.menu-items-delete');
     1039
     1040                        if( $('.menu-item-checkbox:checked').length > 0 ) {
     1041                                button.removeClass('disabled');
     1042                        } else {
     1043                                button.addClass('disabled');
     1044                        }
     1045                },
     1046
    8651047                attachMenuSaveSubmitListeners : function() {
    8661048                        /*
    … …  
    9091091                                searchTimer = setTimeout( function() {
    9101092                                        api.updateQuickSearchResults( $this );
    911                                 }, 500 );
     1093                                }, 500 );
    9121094                        }).on( 'blur', '.quick-search', function() {
    9131095                                api.lastSearch = '';
  • trunk/src/wp-admin/css/nav-menus.css

    r50782 r51006  
    9696.menu-edit #post-body-content h3 {
    9797        margin: 1em 0 10px;
     98}
     99
     100#nav-menu-bulk-actions-top {
     101        margin: 1em 0;
     102}
     103
     104#nav-menu-bulk-actions-bottom {
     105        margin: 1em 0;
     106        margin: calc( 1em + 9px ) 0 ;
     107}
     108
     109.bulk-actions input.button {
     110        margin-right: 12px;
     111}
     112
     113.bulk-select-button {
     114        position: relative;
     115        display: inline-block;
     116        padding: 0 10px;
     117        font-size: 13px;
     118        line-height: 2.15384615;
     119        height: auto;
     120        min-height: 30px;
     121        background: #f6f7f7;
     122        vertical-align: top;
     123        border: 1px solid #dcdcde;
     124        margin: 0;
     125        cursor: pointer;
     126        border-radius: 3px;
     127        white-space: nowrap;
     128        box-sizing: border-box;
     129}
     130
     131.bulk-selection .bulk-select-button {
     132        color: #2271b1;
     133        border-color: #2271b1;
     134        background: #f6f7f7;
     135        vertical-align: top;
     136}
     137
     138#pending-menu-items-to-delete {
     139        display: none;
     140}
     141
     142.bulk-selection #pending-menu-items-to-delete {
     143        display: block;
     144        margin-top: 1em;
     145}
     146
     147#pending-menu-items-to-delete p {
     148        margin-bottom: 0;
     149}
     150
     151#pending-menu-items-to-delete ul {
     152        margin-top: 0;
     153        list-style: none;
     154}
     155
     156#pending-menu-items-to-delete ul li {
     157        display: inline;
     158}
     159
     160input.bulk-select-switcher + .bulk-select-button-label {
     161        vertical-align: inherit;
     162}
     163
     164label.bulk-select-button:hover,
     165label.bulk-select-button:active,
     166label.bulk-select-button:focus-within {
     167        background: #f0f0f1;
     168        border-color: #0a4b78;
     169        color: #0a4b78;
     170}
     171
     172input.bulk-select-switcher:focus + .bulk-select-button-label{
     173        color: #0a4b78;
     174}
     175
     176.bulk-actions input.menu-items-delete {
     177        -webkit-appearance: none;
     178        appearance: none;
     179        font-size: inherit;
     180        border: 0;
     181        line-height: 2.1em;
     182        background: none;
     183        cursor: pointer;
     184        text-decoration: underline;
     185        color: #b32d2e;
     186}
     187
     188.bulk-actions input.menu-items-delete:hover {
     189        color: #b32d2e;
     190        border: none;
     191}
     192
     193.bulk-actions input.menu-items-delete.disabled {
     194        cursor: default;
     195        color: #a7aaad;
     196        box-shadow: none;
    98197}
    99198
    … …  
    480579        /* @todo: responsive view. */
    481580        margin-right: 13em;
     581}
     582
     583.menu-item-handle .menu-item-checkbox {
     584        display: none;
     585}
     586
     587.bulk-selection .menu-item-handle .menu-item-checkbox {
     588        display: inline-block;
     589        margin-right: 6px;
    482590}
    483591
  • trunk/src/wp-admin/includes/class-walker-nav-menu-edit.php

    r50767 r51006  
    119119                        <div class="menu-item-bar">
    120120                                <div class="menu-item-handle">
    121                                         <span class="item-title"><span class="menu-item-title"><?php echo esc_html( $title ); ?></span> <span class="is-submenu" <?php echo $submenu_text; ?>><?php _e( 'sub item' ); ?></span></span>
     121                                        <label class="item-title" for="menu-item-checkbox-<?php echo $item_id; ?>"><input id="menu-item-checkbox-<?php echo $item_id; ?>" type="checkbox" class="menu-item-checkbox" data-menu-item-id="<?php echo $item_id; ?>" disabled="disabled"/><span class="menu-item-title"><?php echo esc_html( $title ); ?></span> <span class="is-submenu" <?php echo $submenu_text; ?>><?php _e( 'sub item' ); ?></span></label>
    122122                                        <span class="item-controls">
    123123                                                <span class="item-type"><?php echo esc_html( $item->type_label ); ?></span>
  • trunk/src/wp-admin/nav-menus.php

    r50938 r51006  
    486486        /* translators: 1: Item name, 2: Item position, 3: Parent item name. */
    487487        'subMenuFocus'            => __( '%1$s. Sub item number %2$d under %3$s.' ),
     488        /* translators: %s: Item name. */
     489        'menuItemDeletion'        => __( 'item %s' ),
     490        /* translators: %s: Item name. */
     491        'itemsDeleted'            => __( 'Deleted menu item: %s.' ),
    488492);
    489493wp_localize_script( 'nav-menu', 'menus', $nav_menus_l10n );
    … …  
    962966                                                <div id="post-body-content" class="wp-clearfix">
    963967                                                        <?php if ( ! $add_new_screen ) : ?>
    964 
    965968                                                                <?php
    966969                                                                $hide_style = '';
    … …  
    979982                                                                        <p><?php echo $starter_copy; ?></p>
    980983                                                                </div>
    981 
     984                                                                <div id="nav-menu-bulk-actions-top" class="bulk-actions">
     985                                                                        <label class="bulk-select-button" for="bulk-select-switcher-top">
     986                                                                                <input type="checkbox" id="bulk-select-switcher-top" name="bulk-select-switcher-top" class="bulk-select-switcher">
     987                                                                                <span class="bulk-select-button-label"><?php echo __( 'Bulk Select' ) ; ?></span>
     988                                                                        </label>
     989                                                                </div>
    982990                                                                <?php
    983991                                                                if ( isset( $edit_markup ) && ! is_wp_error( $edit_markup ) ) {
    … …  
    10051013                                                        }
    10061014                                                        ?>
     1015                                                        <div id="nav-menu-bulk-actions-bottom" class="bulk-actions">
     1016                                                                <label class="bulk-select-button" for="bulk-select-switcher-bottom">
     1017                                                                        <input type="checkbox" id="bulk-select-switcher-bottom" name="bulk-select-switcher-top" class="bulk-select-switcher">
     1018                                                                        <span class="bulk-select-button-label"><?php echo __( 'Bulk Select' ); ?></span>
     1019                                                                </label>
     1020                                                                <input type="button" class="deletion menu-items-delete disabled" value="<?php _e( 'Remove Selected Items' ) ; ?>">
     1021                                                                <div id="pending-menu-items-to-delete">
     1022                                                                        <p><?php _e( 'List of menu items selected for deletion:' ) ; ?></p>
     1023                                                                        <ul></ul>
     1024                                                                </div>
     1025                                                        </div>
    10071026                                                        <div class="menu-settings" <?php echo $no_menus_style; ?>>
    10081027                                                                <h3><?php _e( 'Menu Settings' ); ?></h3>
Note: See TracChangeset for help on using the changeset viewer.