Index: /trunk/src/wp-includes/css/media-views.css =================================================================== --- /trunk/src/wp-includes/css/media-views.css (revision 29483) +++ /trunk/src/wp-includes/css/media-views.css (revision 29484) @@ -730,4 +730,15 @@ -moz-box-sizing: border-box; box-sizing: border-box; + opacity: 1; + -webkit-transition: opacity 250ms; + transition: opacity 250ms; +} + +.media-frame.mode-select .attachment { + opacity: 0.65; +} + +.media-frame.mode-select .attachment.selected { + opacity: 1; } @@ -742,4 +753,13 @@ } +.media-frame.mode-grid .attachment:focus { + -webkit-box-shadow: + inset 0 0 0 6px #f1f1f1, + inset 0 0 1px 7px #5b9dd9; + box-shadow: + inset 0 0 0 6px #f1f1f1, + inset 0 0 1px 7px #5b9dd9; +} + .selected.attachment { -webkit-box-shadow: @@ -748,4 +768,13 @@ box-shadow: inset 0 0 0 5px #fff, + inset 0 0 0 7px #ccc; +} + +.media-frame.mode-grid .selected.attachment { + -webkit-box-shadow: + inset 0 0 0 6px #f1f1f1, + inset 0 0 0 7px #ccc; + box-shadow: + inset 0 0 0 6px #f1f1f1, inset 0 0 0 7px #ccc; } @@ -923,6 +952,5 @@ .selected.attachment:focus, -.attachment.details, -.media-frame.mode-grid .selected.attachment { +.attachment.details { -webkit-box-shadow: inset 0 0 0 3px #fff, @@ -930,4 +958,13 @@ box-shadow: inset 0 0 0 3px #fff, + inset 0 0 0 7px #1e8cbe; +} + +.media-frame.mode-grid .selected.attachment:focus { + -webkit-box-shadow: + inset 0 0 0 3px #f1f1f1, + inset 0 0 0 7px #1e8cbe; + box-shadow: + inset 0 0 0 3px #f1f1f1, inset 0 0 0 7px #1e8cbe; } @@ -945,12 +982,4 @@ } -.media-frame.mode-grid .attachment .check { - display: block; -} - -.media-frame.mode-grid .attachment .check div { - background-position: 21px 0; -} - .attachment.details .check div, .media-frame.mode-grid .attachment.selected .check div { @@ -998,5 +1027,5 @@ .attachments-browser .media-toolbar-secondary > .media-button, .attachments-browser .media-toolbar-secondary > .media-button-group { - margin-top: 10px; + margin: 11px 0; } @@ -2454,5 +2483,4 @@ /* Regions we don't use at all */ .media-frame.mode-grid .media-frame-title, -.media-frame.mode-grid .media-frame-toolbar, .media-frame.mode-grid .media-frame-router, .media-frame.mode-grid .media-frame-menu { @@ -2486,4 +2514,8 @@ .media-frame.mode-grid .attachments { + padding: 2px; +} + +.media-frame.mode-select .attachments { padding: 2px; } @@ -2512,4 +2544,12 @@ } +.media-frame.mode-select .attachments-browser .media-toolbar.fixed { + position: fixed; + top: 28px; + left: 182px; + right: 20px; + width: auto; +} + .media-frame.mode-grid input[type="search"] { margin: 1px; @@ -2538,4 +2578,8 @@ .media-frame.mode-grid .spinner { margin-top: 15px; +} + +.attachments-browser .media-toolbar-secondary > .select-mode-toggle-button { + margin-right: 10px; } Index: /trunk/src/wp-includes/js/media-grid.js =================================================================== --- /trunk/src/wp-includes/js/media-grid.js (revision 29483) +++ /trunk/src/wp-includes/js/media-grid.js (revision 29484) @@ -63,8 +63,11 @@ state: 'library', uploader: true, - mode: [ 'grid' ] + mode: [ 'grid', 'edit' ] }); - $(document).on( 'click', '.add-new-h2', _.bind( this.addNewClickHandler, this ) ); + this.$window = $( window ); + this.$adminBar = $( '#wpadminbar' ); + this.$window.on( 'scroll', _.debounce( _.bind( this.fixPosition, this ), 15 ) ); + $( document ).on( 'click', '.add-new-h2', _.bind( this.addNewClickHandler, this ) ); // Ensure core and media grid view UI is enabled. @@ -97,4 +100,6 @@ media.view.MediaFrame.prototype.initialize.apply( this, arguments ); + this.on( 'all', function () { console.log( arguments ); } ); + // Append the frame view directly the supplied container. this.$el.appendTo( this.options.container ); @@ -131,4 +136,5 @@ title: options.title, content: 'browse', + toolbar: 'select', contentUserSetting: false, filterable: 'all' @@ -145,4 +151,19 @@ // Handle a frame-level event for editing an attachment. this.on( 'edit:attachment', this.openEditAttachmentModal, this ); + }, + + fixPosition: function() { + var $browser; + if ( ! this.isModeActive( 'select' ) ) { + return; + } + + $browser = this.$('.attachments-browser'); + + if ( $browser.offset().top < this.$window.scrollTop() + this.$adminBar.height() ) { + $browser.find('.media-toolbar').addClass( 'fixed' ); + } else { + $browser.find('.media-toolbar').removeClass( 'fixed' ); + } }, @@ -543,124 +564,58 @@ }); - /** - * Controller for bulk selection. - */ - media.view.BulkSelection = media.View.extend({ - className: 'bulk-select', - - initialize: function() { - this.model = new Backbone.Model({ - currentAction: '' - - }); - - this.views.add( new media.view.Label({ - value: l10n.bulkActionsLabel, - attributes: { - 'for': 'bulk-select-dropdown' - } - }) ); - - this.views.add( - new media.view.BulkSelectionActionDropdown({ - controller: this - }) - ); - - this.views.add( - new media.view.BulkSelectionActionButton({ - disabled: true, - text: l10n.apply, - controller: this - }) - ); - } - }); - - /** - * Bulk Selection dropdown view. - * - * @constructor - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ - media.view.BulkSelectionActionDropdown = media.View.extend({ - tagName: 'select', - id: 'bulk-select-dropdown', - + media.view.SelectModeToggleButton = media.view.Button.extend({ initialize: function() { media.view.Button.prototype.initialize.apply( this, arguments ); - this.listenTo( this.controller.controller.state().get( 'selection' ), 'add remove reset', _.bind( this.enabled, this ) ); - this.$el.append( $('').val( '' ).html( l10n.bulkActions ) ) - .append( $('').val( 'delete' ).html( l10n.deletePermanently ) ); - this.$el.prop( 'disabled', true ); - this.$el.on( 'change', _.bind( this.changeHandler, this ) ); - }, - - /** - * Change handler for the dropdown. - * - * Sets the bulk selection controller's currentAction. - */ - changeHandler: function() { - this.controller.model.set( { 'currentAction': this.$el.val() } ); - }, - - /** - * Enable or disable the dropdown if attachments have been selected. - */ - enabled: function() { - var disabled = ! this.controller.controller.state().get('selection').length; - this.$el.prop( 'disabled', disabled ); - } - }); - - /** - * Bulk Selection dropdown view. - * - * @constructor - * - * @augments wp.media.view.Button - * @augments wp.media.View - * @augments wp.Backbone.View - * @augments Backbone.View - */ - media.view.BulkSelectionActionButton = media.view.Button.extend({ - tagName: 'button', - + this.listenTo( this.controller, 'select:activate select:deactivate', this.toggleBulkEditHandler ); + }, + + click: function() { + media.view.Button.prototype.click.apply( this, arguments ); + if ( this.controller.isModeActive( 'select' ) ) { + this.controller.deactivateMode( 'select' ).activateMode( 'edit' ); + } else { + this.controller.deactivateMode( 'edit' ).activateMode( 'select' ); + } + }, + + render: function() { + media.view.Button.prototype.render.apply( this, arguments ); + this.$el.addClass( 'select-mode-toggle-button' ); + return this; + }, + + toggleBulkEditHandler: function() { + var toolbar = this.controller.content.get().toolbar, children; + + children = toolbar.$( '.media-toolbar-secondary > *, .media-toolbar-primary > *'); + + if ( this.controller.isModeActive( 'select' ) ) { + this.model.set( 'text', l10n.cancelSelection ); + children.not( '.delete-selected-button' ).hide(); + toolbar.$( '.select-mode-toggle-button' ).show(); + toolbar.$( '.delete-selected-button' ).removeClass( 'hidden' ); + } else { + this.model.set( 'text', l10n.bulkSelect ); + toolbar.$( '.delete-selected-button' ).addClass( 'hidden' ); + children.not( '.spinner, .delete-selected-button' ).show(); + this.controller.state().get( 'selection' ).reset(); + } + } + }); + + media.view.DeleteSelectedButton = media.view.Button.extend({ initialize: function() { media.view.Button.prototype.initialize.apply( this, arguments ); - - this.listenTo( this.controller.model, 'change', this.enabled, this ); - this.listenTo( this.controller.controller.state().get( 'selection' ), 'add remove reset', _.bind( this.enabled, this ) ); - }, - /** - * Button click handler. - */ - click: function() { - var selection = this.controller.controller.state().get('selection'); - media.view.Button.prototype.click.apply( this, arguments ); - - if ( 'delete' === this.controller.model.get( 'currentAction' ) ) { - // Currently assumes delete is the only action - if ( confirm( l10n.warnBulkDelete ) ) { - while ( selection.length > 0 ) { - selection.at(0).destroy(); - } - } - } - - this.enabled(); - }, - /** - * Enable or disable the button depending if a bulk action is selected - * in the bulk select dropdown, and if attachments have been selected. - */ - enabled: function() { - var currentAction = this.controller.model.get( 'currentAction' ), - selection = this.controller.controller.state().get('selection'), - disabled = ! currentAction || ! selection.length; - this.$el.prop( 'disabled', disabled ); + this.listenTo( this.controller, 'selection:toggle', this.toggleDisabled ); + }, + + toggleDisabled: function() { + this.$el.attr( 'disabled', ! this.controller.state().get( 'selection' ).length ); + }, + + render: function() { + media.view.Button.prototype.render.apply( this, arguments ); + this.$el.addClass( 'delete-selected-button hidden' ); + return this; } }); Index: /trunk/src/wp-includes/js/media-views.js =================================================================== --- /trunk/src/wp-includes/js/media-views.js (revision 29483) +++ /trunk/src/wp-includes/js/media-views.js (revision 29484) @@ -1793,5 +1793,5 @@ // Bail if the mode isn't active. if ( ! this.isModeActive( mode ) ) { - return; + return this; } this.activeModes.remove( this.activeModes.where( { id: mode } ) ); @@ -4822,10 +4822,16 @@ // In the grid view, bubble up an edit:attachment event to the controller. if ( this.controller.isModeActive( 'grid' ) ) { - // Pass the current target to restore focus when closing - this.controller.trigger( 'edit:attachment', this.model, event.currentTarget ); - - // Don't scroll the view and don't attempt to submit anything. - event.stopPropagation(); - return; + if ( this.controller.isModeActive( 'edit' ) ) { + // Pass the current target to restore focus when closing + this.controller.trigger( 'edit:attachment', this.model, event.currentTarget ); + + // Don't scroll the view and don't attempt to submit anything. + event.stopPropagation(); + return; + } + + if ( this.controller.isModeActive( 'select' ) ) { + method = 'toggle'; + } } @@ -4839,4 +4845,6 @@ method: method }); + + this.controller.trigger( 'selection:toggle' ); // Don't scroll the view and don't attempt to submit anything. @@ -5785,10 +5793,4 @@ }).render() ); - // BulkSelection is a