WordPress.org

Make WordPress Core

Changeset 29484


Ignore:
Timestamp:
08/13/2014 10:44:48 PM (5 years ago)
Author:
wonderboymusic
Message:

Media Grid: add Bulk Selection mode for deleting attachments.

  • Toolbar is sticky when select mode is active
  • Selection is toggled when clicking an attachment preview
  • Unselected attachments fade out, selected fade in.

See #28842.

Location:
trunk/src/wp-includes
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/css/media-views.css

    r29463 r29484  
    730730    -moz-box-sizing: border-box;
    731731    box-sizing: border-box;
     732    opacity: 1;
     733    -webkit-transition: opacity 250ms;
     734    transition: opacity 250ms;
     735}
     736
     737.media-frame.mode-select .attachment {
     738    opacity: 0.65;
     739}
     740
     741.media-frame.mode-select .attachment.selected {
     742    opacity: 1;
    732743}
    733744
     
    742753}
    743754
     755.media-frame.mode-grid .attachment:focus {
     756    -webkit-box-shadow:
     757        inset 0 0 0 6px #f1f1f1,
     758        inset 0 0 1px 7px #5b9dd9;
     759    box-shadow:
     760        inset 0 0 0 6px #f1f1f1,
     761        inset 0 0 1px 7px #5b9dd9;
     762}
     763
    744764.selected.attachment {
    745765    -webkit-box-shadow:
     
    748768    box-shadow:
    749769        inset 0 0 0 5px #fff,
     770        inset 0 0 0 7px #ccc;
     771}
     772
     773.media-frame.mode-grid .selected.attachment {
     774    -webkit-box-shadow:
     775        inset 0 0 0 6px #f1f1f1,
     776        inset 0 0 0 7px #ccc;
     777    box-shadow:
     778        inset 0 0 0 6px #f1f1f1,
    750779        inset 0 0 0 7px #ccc;
    751780}
     
    923952
    924953.selected.attachment:focus,
    925 .attachment.details,
    926 .media-frame.mode-grid .selected.attachment {
     954.attachment.details {
    927955    -webkit-box-shadow:
    928956        inset 0 0 0 3px #fff,
     
    930958    box-shadow:
    931959        inset 0 0 0 3px #fff,
     960        inset 0 0 0 7px #1e8cbe;
     961}
     962
     963.media-frame.mode-grid .selected.attachment:focus {
     964    -webkit-box-shadow:
     965        inset 0 0 0 3px #f1f1f1,
     966        inset 0 0 0 7px #1e8cbe;
     967    box-shadow:
     968        inset 0 0 0 3px #f1f1f1,
    932969        inset 0 0 0 7px #1e8cbe;
    933970}
     
    945982}
    946983
    947 .media-frame.mode-grid .attachment .check {
    948     display: block;
    949 }
    950 
    951 .media-frame.mode-grid .attachment .check div {
    952     background-position: 21px 0;
    953 }
    954 
    955984.attachment.details .check div,
    956985.media-frame.mode-grid .attachment.selected .check div {
     
    9981027.attachments-browser .media-toolbar-secondary > .media-button,
    9991028.attachments-browser .media-toolbar-secondary > .media-button-group {
    1000     margin-top: 10px;
     1029    margin: 11px 0;
    10011030}
    10021031
     
    24542483/* Regions we don't use at all */
    24552484.media-frame.mode-grid .media-frame-title,
    2456 .media-frame.mode-grid .media-frame-toolbar,
    24572485.media-frame.mode-grid .media-frame-router,
    24582486.media-frame.mode-grid .media-frame-menu {
     
    24862514
    24872515.media-frame.mode-grid .attachments {
     2516    padding: 2px;
     2517}
     2518
     2519.media-frame.mode-select .attachments {
    24882520    padding: 2px;
    24892521}
     
    25122544}
    25132545
     2546.media-frame.mode-select .attachments-browser .media-toolbar.fixed {
     2547    position: fixed;
     2548    top: 28px;
     2549    left: 182px;
     2550    right: 20px;
     2551    width: auto;
     2552}
     2553
    25142554.media-frame.mode-grid input[type="search"] {
    25152555    margin: 1px;
     
    25382578.media-frame.mode-grid .spinner {
    25392579    margin-top: 15px;
     2580}
     2581
     2582.attachments-browser .media-toolbar-secondary > .select-mode-toggle-button {
     2583    margin-right: 10px;
    25402584}
    25412585
  • trunk/src/wp-includes/js/media-grid.js

    r29483 r29484  
    6363                state:     'library',
    6464                uploader:  true,
    65                 mode:      [ 'grid' ]
     65                mode:      [ 'grid', 'edit' ]
    6666            });
    6767
    68             $(document).on( 'click', '.add-new-h2', _.bind( this.addNewClickHandler, this ) );
     68            this.$window = $( window );
     69            this.$adminBar = $( '#wpadminbar' );
     70            this.$window.on( 'scroll', _.debounce( _.bind( this.fixPosition, this ), 15 ) );
     71            $( document ).on( 'click', '.add-new-h2', _.bind( this.addNewClickHandler, this ) );
    6972
    7073            // Ensure core and media grid view UI is enabled.
     
    97100            media.view.MediaFrame.prototype.initialize.apply( this, arguments );
    98101
     102            this.on( 'all', function () { console.log( arguments ); } );
     103
    99104            // Append the frame view directly the supplied container.
    100105            this.$el.appendTo( this.options.container );
     
    131136                    title:              options.title,
    132137                    content:            'browse',
     138                    toolbar:            'select',
    133139                    contentUserSetting: false,
    134140                    filterable:         'all'
     
    145151            // Handle a frame-level event for editing an attachment.
    146152            this.on( 'edit:attachment', this.openEditAttachmentModal, this );
     153        },
     154
     155        fixPosition: function() {
     156            var $browser;
     157            if ( ! this.isModeActive( 'select' ) ) {
     158                return;
     159            }
     160
     161            $browser = this.$('.attachments-browser');
     162
     163            if ( $browser.offset().top < this.$window.scrollTop() + this.$adminBar.height() ) {
     164                $browser.find('.media-toolbar').addClass( 'fixed' );
     165            } else {
     166                $browser.find('.media-toolbar').removeClass( 'fixed' );
     167            }
    147168        },
    148169
     
    543564    });
    544565
    545     /**
    546      * Controller for bulk selection.
    547      */
    548     media.view.BulkSelection = media.View.extend({
    549         className: 'bulk-select',
    550 
    551         initialize: function() {
    552             this.model = new Backbone.Model({
    553                 currentAction: ''
    554 
    555             });
    556 
    557             this.views.add( new media.view.Label({
    558                 value: l10n.bulkActionsLabel,
    559                 attributes: {
    560                     'for': 'bulk-select-dropdown'
    561                 }
    562             }) );
    563 
    564             this.views.add(
    565                 new media.view.BulkSelectionActionDropdown({
    566                     controller: this
    567                 })
    568             );
    569 
    570             this.views.add(
    571                 new media.view.BulkSelectionActionButton({
    572                     disabled:   true,
    573                     text:       l10n.apply,
    574                     controller: this
    575                 })
    576             );
    577         }
    578     });
    579 
    580     /**
    581      * Bulk Selection dropdown view.
    582      *
    583      * @constructor
    584      * @augments wp.media.View
    585      * @augments wp.Backbone.View
    586      * @augments Backbone.View
    587      */
    588     media.view.BulkSelectionActionDropdown = media.View.extend({
    589         tagName: 'select',
    590         id:      'bulk-select-dropdown',
    591 
     566    media.view.SelectModeToggleButton = media.view.Button.extend({
    592567        initialize: function() {
    593568            media.view.Button.prototype.initialize.apply( this, arguments );
    594             this.listenTo( this.controller.controller.state().get( 'selection' ), 'add remove reset', _.bind( this.enabled, this ) );
    595             this.$el.append( $('<option></option>').val( '' ).html( l10n.bulkActions ) )
    596                 .append( $('<option></option>').val( 'delete' ).html( l10n.deletePermanently ) );
    597             this.$el.prop( 'disabled', true );
    598             this.$el.on( 'change', _.bind( this.changeHandler, this ) );
    599         },
    600 
    601         /**
    602          * Change handler for the dropdown.
    603          *
    604          * Sets the bulk selection controller's currentAction.
    605          */
    606         changeHandler: function() {
    607             this.controller.model.set( { 'currentAction': this.$el.val() } );
    608         },
    609 
    610         /**
    611          * Enable or disable the dropdown if attachments have been selected.
    612          */
    613         enabled: function() {
    614             var disabled = ! this.controller.controller.state().get('selection').length;
    615             this.$el.prop( 'disabled', disabled );
    616         }
    617     });
    618 
    619     /**
    620      * Bulk Selection dropdown view.
    621      *
    622      * @constructor
    623      *
    624      * @augments wp.media.view.Button
    625      * @augments wp.media.View
    626      * @augments wp.Backbone.View
    627      * @augments Backbone.View
    628      */
    629     media.view.BulkSelectionActionButton = media.view.Button.extend({
    630         tagName: 'button',
    631 
     569            this.listenTo( this.controller, 'select:activate select:deactivate', this.toggleBulkEditHandler );
     570        },
     571
     572        click: function() {
     573            media.view.Button.prototype.click.apply( this, arguments );
     574            if ( this.controller.isModeActive( 'select' ) ) {
     575                this.controller.deactivateMode( 'select' ).activateMode( 'edit' );
     576            } else {
     577                this.controller.deactivateMode( 'edit' ).activateMode( 'select' );
     578            }
     579        },
     580
     581        render: function() {
     582            media.view.Button.prototype.render.apply( this, arguments );
     583            this.$el.addClass( 'select-mode-toggle-button' );
     584            return this;
     585        },
     586
     587        toggleBulkEditHandler: function() {
     588            var toolbar = this.controller.content.get().toolbar, children;
     589
     590            children = toolbar.$( '.media-toolbar-secondary > *, .media-toolbar-primary > *');
     591
     592            if ( this.controller.isModeActive( 'select' ) ) {
     593                this.model.set( 'text', l10n.cancelSelection );
     594                children.not( '.delete-selected-button' ).hide();
     595                toolbar.$( '.select-mode-toggle-button' ).show();
     596                toolbar.$( '.delete-selected-button' ).removeClass( 'hidden' );
     597            } else {
     598                this.model.set( 'text', l10n.bulkSelect );
     599                toolbar.$( '.delete-selected-button' ).addClass( 'hidden' );
     600                children.not( '.spinner, .delete-selected-button' ).show();
     601                this.controller.state().get( 'selection' ).reset();
     602            }
     603        }
     604    });
     605
     606    media.view.DeleteSelectedButton = media.view.Button.extend({
    632607        initialize: function() {
    633608            media.view.Button.prototype.initialize.apply( this, arguments );
    634 
    635             this.listenTo( this.controller.model, 'change', this.enabled, this );
    636             this.listenTo( this.controller.controller.state().get( 'selection' ), 'add remove reset', _.bind( this.enabled, this ) );
    637         },
    638         /**
    639          * Button click handler.
    640          */
    641         click: function() {
    642             var selection = this.controller.controller.state().get('selection');
    643             media.view.Button.prototype.click.apply( this, arguments );
    644 
    645             if ( 'delete' === this.controller.model.get( 'currentAction' ) ) {
    646                 // Currently assumes delete is the only action
    647                 if ( confirm( l10n.warnBulkDelete ) ) {
    648                     while ( selection.length > 0 ) {
    649                         selection.at(0).destroy();
    650                     }
    651                 }
    652             }
    653 
    654             this.enabled();
    655         },
    656         /**
    657          * Enable or disable the button depending if a bulk action is selected
    658          * in the bulk select dropdown, and if attachments have been selected.
    659          */
    660         enabled: function() {
    661             var currentAction = this.controller.model.get( 'currentAction' ),
    662                 selection = this.controller.controller.state().get('selection'),
    663                 disabled = ! currentAction || ! selection.length;
    664             this.$el.prop( 'disabled', disabled );
     609            this.listenTo( this.controller, 'selection:toggle', this.toggleDisabled );
     610        },
     611
     612        toggleDisabled: function() {
     613            this.$el.attr( 'disabled', ! this.controller.state().get( 'selection' ).length );
     614        },
     615
     616        render: function() {
     617            media.view.Button.prototype.render.apply( this, arguments );
     618            this.$el.addClass( 'delete-selected-button hidden' );
     619            return this;
    665620        }
    666621    });
  • trunk/src/wp-includes/js/media-views.js

    r29479 r29484  
    17931793            // Bail if the mode isn't active.
    17941794            if ( ! this.isModeActive( mode ) ) {
    1795                 return;
     1795                return this;
    17961796            }
    17971797            this.activeModes.remove( this.activeModes.where( { id: mode } ) );
     
    48224822            // In the grid view, bubble up an edit:attachment event to the controller.
    48234823            if ( this.controller.isModeActive( 'grid' ) ) {
    4824                 // Pass the current target to restore focus when closing
    4825                 this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
    4826 
    4827                 // Don't scroll the view and don't attempt to submit anything.
    4828                 event.stopPropagation();
    4829                 return;
     4824                if ( this.controller.isModeActive( 'edit' ) ) {
     4825                    // Pass the current target to restore focus when closing
     4826                    this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
     4827
     4828                    // Don't scroll the view and don't attempt to submit anything.
     4829                    event.stopPropagation();
     4830                    return;
     4831                }
     4832
     4833                if ( this.controller.isModeActive( 'select' ) ) {
     4834                    method = 'toggle';
     4835                }
    48304836            }
    48314837
     
    48394845                method: method
    48404846            });
     4847
     4848            this.controller.trigger( 'selection:toggle' );
    48414849
    48424850            // Don't scroll the view and don't attempt to submit anything.
     
    57855793                }).render() );
    57865794
    5787                 // BulkSelection is a <div> with subviews, including screen reader text
    5788                 this.toolbar.set( 'bulkSelection', new media.view.BulkSelection({
    5789                     controller: this.controller,
    5790                     priority: -70
    5791                 }).render() );
    5792 
    57935795                // DateFilter is a <select>, screen reader text needs to be rendered before
    57945796                this.toolbar.set( 'dateFilterLabel', new media.view.Label({
     
    58035805                    model:      this.collection.props,
    58045806                    priority: -75
     5807                }).render() );
     5808
     5809                // BulkSelection is a <div> with subviews, including screen reader text
     5810                this.toolbar.set( 'selectModeToggleButton', new media.view.SelectModeToggleButton({
     5811                    text: l10n.bulkSelect,
     5812                    controller: this.controller,
     5813                    priority: -70
     5814                }).render() );
     5815
     5816                this.toolbar.set( 'deleteSelectedButton', new media.view.DeleteSelectedButton({
     5817                    style: 'primary',
     5818                    disabled: true,
     5819                    text:  l10n.deleteSelected,
     5820                    controller: this.controller,
     5821                    priority: -60,
     5822                    click: function() {
     5823                        while ( this.controller.state().get( 'selection' ).length > 0 ) {
     5824                            this.controller.state().get( 'selection' ).at( 0 ).destroy();
     5825                        }
     5826                    }
    58055827                }).render() );
    58065828            }
  • trunk/src/wp-includes/media.php

    r29457 r29484  
    29372937        'bulkActions'            => __( 'Bulk Actions' ),
    29382938        'bulkActionsLabel'       => __( 'Select bulk action' ),
     2939        'bulkSelect'             => __( 'Bulk Select' ),
     2940        'cancelSelection'        => __( 'Cancel Selection' ),
     2941        'deleteSelected'         => __( 'Delete Selected' ),
    29392942        'deletePermanently'      => __( 'Delete Permanently' ),
    29402943        'apply'                  => __( 'Apply' ),
Note: See TracChangeset for help on using the changeset viewer.