diff --git a/wp-includes/css/buttons.css b/wp-includes/css/buttons.css index 34be74bf30..798343bb13 100644 --- a/wp-includes/css/buttons.css +++ b/wp-includes/css/buttons.css @@ -81,6 +81,14 @@ input[type="submit"]::-moz-focus-inner { padding: 0 8px 1px; } +.button.button-hero, +.button-group.button-hero .button { + font-size: 14px; + height: 46px; + line-height: 44px; + padding: 0 36px; +} + .button:active { outline: none; } @@ -294,7 +302,7 @@ input[type="submit"]::-moz-focus-inner { position: absolute; top: 100%; left: 0; - margin-top: 5px; + margin: 5px 0; padding: 0.8em 1em; border-radius: 3px; @@ -314,3 +322,8 @@ input[type="submit"]::-moz-focus-inner { left: auto; right: 0; } + +.dropdown-flip-y .dropdown { + top: auto; + bottom: 100%; +} \ No newline at end of file diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css index 766ada5a29..f73c28efb2 100644 --- a/wp-includes/css/media-views.css +++ b/wp-includes/css/media-views.css @@ -3,10 +3,10 @@ */ .media-modal { position: fixed; - top: 80px; - left: 60px; - right: 60px; - bottom: 60px; + top: 60px; + left: 40px; + right: 40px; + bottom: 40px; z-index: 125000; } @@ -21,6 +21,15 @@ z-index: 120000; } +.media-modal-backdrop div { + position: absolute; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; + border: 1px dashed rgba( 255, 255, 255, 0.5 ); +} + .media-modal-title, .media-modal-close { position: absolute; @@ -38,7 +47,7 @@ float: left; padding: 0; margin: 0; - font-size: 1.4em; + font-size: 16px; } .media-modal-close { @@ -74,12 +83,23 @@ .media-toolbar { position: absolute; top: 0; - left: 220px; + left: 0; right: 0; z-index: 100; - height: 50px; - padding: 0 10px; - border-bottom: 1px solid #dfdfdf; + height: 60px; + padding: 0 16px; + border: 0 solid #dfdfdf; +} + +.media-frame > .media-toolbar { + top: auto; + left: 200px; + bottom: 0; + border-width: 1px 0 0 0; +} + +.media-frame.hide-toolbar > .media-toolbar { + bottom: -61px; } .media-toolbar-primary { @@ -94,14 +114,14 @@ .media-toolbar-primary > .media-button-group { margin-left: 10px; float: left; - margin-top: 10px; + margin-top: 15px; } .media-toolbar-secondary > .media-button, .media-toolbar-secondary > .media-button-group { margin-right: 10px; float: left; - margin-top: 10px; + margin-top: 15px; } /** @@ -110,16 +130,21 @@ .media-sidebar { position: absolute; top: 0; - left: 0; - bottom: 0; - width: 219px; - z-index: 50; + right: 0; + bottom: 61px; + width: 247px; + padding: 0 16px; + z-index: 75; background: #f5f5f5; - border-right: 1px solid #dfdfdf; + border-left: 1px solid #dfdfdf; } .hide-sidebar .media-sidebar { - display: none; + right: -280px; +} + +.hide-toolbar .media-sidebar { + bottom: 0; } .media-sidebar .sidebar-title { @@ -146,32 +171,66 @@ padding-top: 5px; } +/** + * Menu + */ +.media-menu { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 199px; + margin: 0; + padding: 16px 0; + z-index: 200; + box-shadow: inset -6px 0 6px -6px rgba( 0, 0, 0, 0.4 ); +} + +.media-menu li { + position: relative; + padding: 4px 20px; + margin: 0; + line-height: 18px; + font-size: 14px; + color: #21759B; + text-shadow: 0 1px 0 #fff; +} + +.media-menu-item { + cursor: pointer; +} + +.media-menu li:hover { + background: rgba( 0, 0, 0, 0.04 ); +} + +.media-menu .active, +.media-menu .active:hover { + color: #333; + font-weight: bold; +} + +.media-menu .separator { + height: 0; + margin: 12px 20px; + padding: 0; + border-top: 1px solid #dfdfdf; + border-bottom: 1px solid #fff; +} + /** * Frame */ - -.media-frame .media-content, -.media-frame .media-toolbar, -.media-frame .media-sidebar { - -webkit-transition-property: left, right, top, bottom, margin; - -moz-transition-property: left, right, top, bottom, margin; - -ms-transition-property: left, right, top, bottom, margin; - -o-transition-property: left, right, top, bottom, margin; - transition-property: left, right, top, bottom, margin; - - -webkit-transition-duration: 0.2s; - -moz-transition-duration: 0.2s; - -ms-transition-duration: 0.2s; - -o-transition-duration: 0.2s; - transition-duration: 0.2s; +.media-frame { + overflow: hidden; } .media-frame .media-content { position: absolute; - top: 51px; - left: 220px; - right: 0; - bottom: 0; + top: 0; + left: 200px; + right: 280px; + bottom: 61px; height: auto; width: auto; margin: 0; @@ -179,7 +238,11 @@ } .media-frame.hide-sidebar .media-content { - left: 0; + right: 0; +} + +.media-frame.hide-toolbar .media-content { + bottom: 0; } .media-frame .media-toolbar .add-to-gallery { @@ -198,6 +261,14 @@ font-family: sans-serif; } +/** + * Attachments + */ +.attachments { + margin: 0; + padding-right: 16px; +} + /** * Attachment */ @@ -388,6 +459,28 @@ border-radius: 0; } + +/** + * Attachments Browser + */ +.media-frame .attachments-browser { + overflow: hidden; +} + +.attachments-browser .media-toolbar { + height: 50px; +} + +.attachments-browser .attachments { + position: absolute; + top: 50px; + left: 0; + right: 0; + bottom: 0; + overflow: auto; +} + + /** * Progress Bar */ @@ -445,10 +538,10 @@ .uploader-window-content { position: absolute; - top: 30px; - left: 30px; - right: 30px; - bottom: 30px; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; border: 1px dashed #fff; } @@ -463,7 +556,7 @@ -o-transform: translateY( -50% ); transform: translateY( -50% ); - font-size: 18px; + font-size: 20px; font-weight: 200; color: #fff; padding: 0; @@ -485,8 +578,24 @@ display: block; } -.uploader-inline { - display: none; +.media-content.uploader-inline { + margin: 20px; + padding: 20px; + border: 1px dashed #aaa; + text-align: center; +} + +.uploader-inline-content { + position: absolute; + top: 30%; + left: 0; + right: 0; +} + +.uploader-inline h3 { + font-size: 20px; + font-weight: 200; + margin-bottom: 1.6em; } .uploader-inline .media-progress-bar { @@ -497,23 +606,85 @@ display: block; } -.media-sidebar .uploader-inline { - display: block; - position: absolute; - left: 0; - right: 0; - bottom: 0; - height: 100px; - margin: 10px; - padding-top: 10px; - text-align: center; - border: 1px dashed #aaa; +.uploader-inline .browser { + display: inline-block !important; } -.media-sidebar .uploader-inline h3 { - font-weight: 200; - font-size: 16px; - margin: 10px 0; +/** + * Selection + */ + +.media-selection { + position: absolute; + top: 0; + left: 0; + right: 350px; + height: 60px; + padding: 0 0 0 16px; + overflow: hidden; + white-space: nowrap; +} + +.media-selection .selection-info { + display: inline-block; + height: 60px; + margin-right: 10px; + vertical-align: top; +} + +.media-selection.empty { + display: none; +} + +.media-selection .count { + display: block; + padding-top: 12px; + font-size: 14px; + line-height: 20px; + font-weight: bold; +} + +.media-selection .clear-selection { + display: block; + text-decoration: none; + line-height: 16px; +} + +.media-selection .attachments { + display: inline-block; + height: 60px; + margin-top: 5px; + overflow: hidden; + vertical-align: top; +} + +.media-selection .selected.attachment { + box-shadow: none; +} + +.media-selection .details.attachment { + box-shadow: + 0 0 0 1px #fff, + 0 0 0 3px #1e8cbe; +} + +.media-selection:after { + content: ''; + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 25px; + background-image: -webkit-gradient(linear, right top, right top, from( rgba( 255, 255, 255, 1 ) ), to( rgba( 255, 255, 255, 0 ) )); + background-image: -webkit-linear-gradient(right, rgba( 255, 255, 255, 1 ) , rgba( 255, 255, 255, 0 ) ); + background-image: -moz-linear-gradient(right, rgba( 255, 255, 255, 1 ) , rgba( 255, 255, 255, 0 ) ); + background-image: -o-linear-gradient(right, rgba( 255, 255, 255, 1 ) , rgba( 255, 255, 255, 0 ) ); + background-image: linear-gradient(to left, rgba( 255, 255, 255, 1 ) , rgba( 255, 255, 255, 0 ) ); +} + +.media-selection .attachment .filename { + display: none; } /** @@ -584,10 +755,6 @@ * Attachment Details */ -.attachment-details { - padding-top: 20px; -} - .attachment-details-preview { cursor: default; } diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js index 577b1d0e0a..70d5538bfe 100644 --- a/wp-includes/js/media-views.js +++ b/wp-includes/js/media-views.js @@ -83,15 +83,21 @@ state: function( id ) { var previous; - if ( id ) { - if ( previous = this.state() ) - previous.trigger('deactivate'); - this._state = id; - return this.state().trigger('activate'); - } + if ( ! id ) + return this._state ? this.get( this._state ) : null; - if ( this._state ) - return this.get( this._state ); + previous = this.state(); + + // Bail if we're trying to select the current state, or a state + // that does not exist. + if ( previous && id === previous.id || ! this.states.get( id ) ) + return; + + if ( previous ) + previous.trigger('deactivate'); + + this._state = id; + this.state().trigger('activate'); } }); @@ -107,9 +113,50 @@ }; }); + + // wp.media.controller.State + // --------------------------- + media.controller.State = Backbone.Model.extend({ + initialize: function() { + this.on( 'activate', this._activate, this ); + this.on( 'activate', this.activate, this ); + this.on( 'deactivate', this._deactivate, this ); + this.on( 'deactivate', this.deactivate, this ); + }, + + activate: function() {}, + _activate: function() { + this.active = true; + + this.menu(); + this.toolbar(); + this.sidebar(); + this.content(); + }, + + deactivate: function() {}, + _deactivate: function() { + this.active = false; + }, + + menu: function() { + var menu = this.get('menu'); + + if ( ! menu ) + return; + + this.frame.menu( menu ); + menu.select( this.id ); + }, + + toolbar: function() {}, + sidebar: function() {}, + content: function() {} + }); + // wp.media.controller.Library // --------------------------- - media.controller.Library = Backbone.Model.extend({ + media.controller.Library = media.controller.State.extend({ defaults: { id: 'library', multiple: false, @@ -133,15 +180,10 @@ if ( ! this.get('gutter') ) this.set( 'gutter', 8 ); - this.on( 'activate', this.activate, this ); - this.on( 'deactivate', this.deactivate, this ); + media.controller.State.prototype.initialize.apply( this, arguments ); }, activate: function() { - this.toolbar(); - this.sidebar(); - this.content(); - // If we're in a workflow that supports multiple attachments, // automatically select any uploading attachments. if ( this.get('multiple') ) @@ -153,6 +195,7 @@ deactivate: function() { var toolbar = this._postLibraryToolbar; + if ( toolbar ) this.get('selection').off( 'add remove', toolbar.visibility, toolbar ); @@ -184,30 +227,16 @@ }) ); this.details(); - frame.sidebar().add({ - search: new media.view.Search({ - controller: frame, - model: this.get('library').props, - priority: 20 - }), - - selection: new media.view.SelectionPreview({ - controller: frame, - collection: this.get('selection'), - priority: 40 - }) - }); }, content: function() { var frame = this.frame; // Content. - frame.content( new media.view.Attachments({ + frame.content( new media.view.AttachmentsBrowser({ controller: frame, collection: this.get('library'), - // The single `Attachment` view to be used in the `Attachments` view. - AttachmentView: media.view.Attachment.Library + model: this }).render() ); }, @@ -255,6 +284,65 @@ } }); + + // wp.media.controller.Upload + // --------------------------- + media.controller.Upload = media.controller.Library.extend({ + defaults: _.defaults({ + id: 'upload' + }, media.controller.Library.prototype.defaults ), + + initialize: function() { + var library = this.get('library'); + + // If a `library` attribute isn't provided, create a new + // `Attachments` collection that observes (and thereby receives + // all uploading) attachments. + if ( ! library ) { + library = new Attachments(); + library.props.set({ + orderby: 'date', + order: 'ASC' + }); + library.observe( wp.Uploader.queue ); + this.set( 'library', library ); + } + + media.controller.Library.prototype.initialize.apply( this, arguments ); + }, + + activate: function() { + this.get('library').on( 'add remove reset', this.refresh, this ); + media.controller.Library.prototype.activate.apply( this, arguments ); + this.refresh(); + }, + + deactivate: function() { + this.get('library').off( 'add remove reset', this.refresh, this ); + media.controller.Library.prototype.deactivate.apply( this, arguments ); + }, + + refresh: function() { + this.frame.$el.toggleClass( 'hide-sidebar hide-toolbar', ! this.get('library').length ); + this.content(); + }, + + content: function() { + var frame = this.frame, + upload; + + if ( this.get('library').length ) { + media.controller.Library.prototype.content.apply( this, arguments ); + } else { + upload = new media.view.UploaderInline({ + controller: frame + }).render(); + + frame.content( upload ); + } + } + }); + // wp.media.controller.Gallery // --------------------------- media.controller.Gallery = media.controller.Library.extend({ @@ -296,6 +384,7 @@ this.frame.content( new media.view.Attachments({ controller: this.frame, collection: this.get('library'), + model: this, sortable: true, // The single `Attachment` view to be used in the `Attachments` view. AttachmentView: media.view.Attachment.Gallery @@ -345,7 +434,7 @@ initialize: function() { _.defaults( this.options, { - state: 'library', + state: 'upload', title: '', selection: [], library: {}, @@ -361,12 +450,12 @@ }, render: function() { - var els = [ this.toolbar().el, this.sidebar().el, this.content().el ]; + var els = [ this.menu().el, this.content().el, this.sidebar().el, this.toolbar().el ]; if ( this.modal ) this.modal.render(); - // Detach any views that will be rebound to maintain event bidnings. + // Detach any views that will be rebound to maintain event bindings. this.$el.children().filter( els ).detach(); this.$el.empty().append( els ); @@ -389,7 +478,12 @@ }, createStates: function() { - var options = this.options; + var options = this.options, + menus = { + landing: new media.view.Menu.Landing({ + controller: this + }) + }; // Create the default `states` collection. this.states = new Backbone.Collection(); @@ -404,7 +498,12 @@ new media.controller.Library({ selection: options.selection, library: media.query( options.library ), - multiple: this.options.multiple + multiple: this.options.multiple, + menu: menus.landing + }), + new media.controller.Upload({ + multiple: this.options.multiple, + menu: menus.landing }), new media.controller.Gallery({ library: options.selection, @@ -419,7 +518,7 @@ createSubviews: function() { // Initialize a stub view for each subview region. - _.each(['toolbar','sidebar','content'], function( subview ) { + _.each(['menu','content','sidebar','toolbar'], function( subview ) { this[ '_' + subview ] = new Backbone.View({ tagName: 'div', className: 'media-' + subview @@ -450,15 +549,21 @@ _.extend( media.view.Frame.prototype, media.controller.StateMachine.prototype ); // Create methods to fetch and replace individual subviews. - _.each(['toolbar','sidebar','content'], function( subview ) { + _.each(['menu','content','sidebar','toolbar'], function( subview ) { media.view.Frame.prototype[ subview ] = function( view ) { var previous = this[ '_' + subview ]; if ( ! view ) return previous; + if ( view === previous ) + return; + view.$el.addClass( 'media-' + subview ); + // Remove the hide class. + this.$el.removeClass( 'hide-' + subview ); + if ( previous.destroy ) previous.destroy(); previous.undelegateEvents(); @@ -564,17 +669,12 @@ var uploader; this.controller = this.options.controller; - this.inline = new media.view.UploaderInline({ - controller: this.controller, - uploaderWindow: this - }).render(); - this.inline.$el.appendTo('body'); + this.$browser = $('').hide().appendTo('body'); uploader = this.options.uploader = _.defaults( this.options.uploader || {}, { - container: this.inline.$el, dropzone: this.$el, - browser: this.inline.$('.browser'), + browser: this.$browser, params: {} }); @@ -647,6 +747,9 @@ initialize: function() { this.controller = this.options.controller; + if ( ! this.options.$browser ) + this.options.$browser = this.controller.uploader.$browser; + // Track uploading attachments. wp.Uploader.queue.on( 'add remove reset change:percent', this.renderUploadProgress, this ); }, @@ -656,8 +759,17 @@ }, render: function() { + var $browser = this.options.$browser, + $placeholder; + this.renderUploadProgress(); this.$el.html( this.template( this.options ) ); + + $placeholder = this.$('.browser'); + $browser.text( $placeholder.text() ); + $browser[0].className = $placeholder[0].className; + $placeholder.replaceWith( $browser.show() ); + this.$bar = this.$('.media-progress-bar div'); return this; }, @@ -679,7 +791,6 @@ } }); - /** * wp.media.view.Toolbar */ @@ -765,6 +876,12 @@ controller = this.options.controller; this.options.items = { + selection: new media.view.Selection({ + controller: controller, + collection: selection, + priority: -40 + }).render(), + 'create-new-gallery': { style: 'primary', text: l10n.createNewGallery, @@ -777,7 +894,7 @@ 'insert-into-post': new media.view.ButtonGroup({ priority: 30, - classes: 'dropdown-flip-x', + classes: 'dropdown-flip-x dropdown-flip-y', buttons: [ { text: l10n.insertIntoPost, @@ -1033,19 +1150,19 @@ }); /** - * wp.media.view.Sidebar + * wp.media.view.PriorityList */ - media.view.Sidebar = Backbone.View.extend({ + + media.view.PriorityList = Backbone.View.extend({ tagName: 'div', - className: 'media-sidebar', - template: media.template('sidebar'), initialize: function() { this.controller = this.options.controller; this._views = {}; - if ( this.options.views ) - this.add( this.options.views, { silent: true }); + this.add( _.extend( {}, this.views, this.options.views ), { silent: true }); + delete this.views; + delete this.options.views; if ( ! this.options.silent ) this.render(); @@ -1060,18 +1177,7 @@ // Otherwise, `jQuery.html()` will unbind their events. $( els ).detach(); - this.$el.html( this.template({ - title: this.controller.state().get('title') || '', - uploader: this.controller.options.uploader - }) ); - - this.$('.sidebar-content').html( els ); - - if ( this.controller.uploader ) { - this.$el.append( this.controller.uploader.inline.$el ); - this.controller.uploader.refresh(); - } - + this.$el.html( els ); return this; }, @@ -1089,6 +1195,9 @@ return this; } + if ( ! (view instanceof Backbone.View) ) + view = this.toView( view, id, options ); + view.controller = view.controller || this.controller; this._views[ id ] = view; @@ -1106,9 +1215,94 @@ if ( ! options || ! options.silent ) this.render(); return this; + }, + + toView: function( options ) { + return new Backbone.View( options ); } }); + + /** + * wp.media.view.Menu + */ + media.view.Menu = media.view.PriorityList.extend({ + tagName: 'ul', + className: 'media-menu', + + toView: function( options, id ) { + options = options || {}; + options.id = id; + return new media.view.MenuItem( options ).render(); + }, + + select: function( id ) { + var view = this.get( id ); + + if ( ! view ) + return; + + this.deselect(); + view.$el.addClass('active'); + }, + + deselect: function() { + this.$el.children().removeClass('active'); + } + }); + + media.view.MenuItem = Backbone.View.extend({ + tagName: 'li', + className: 'media-menu-item', + + events: { + 'click': 'toState' + }, + + toState: function() { + this.controller.state( this.options.id ); + }, + + render: function() { + var options = this.options; + + if ( options.text ) + this.$el.text( options.text ); + else if ( options.html ) + this.$el.html( options.html ); + + return this; + } + }); + + media.view.Menu.Landing = media.view.Menu.extend({ + views: { + upload: { + text: l10n.uploadFilesTitle, + priority: 20 + }, + library: { + text: l10n.mediaLibraryTitle, + priority: 40 + }, + separateLibrary: new Backbone.View({ + className: 'separator', + priority: 60 + }), + embed: { + text: l10n.embedFromUrlTitle, + priority: 80 + } + } + }); + + /** + * wp.media.view.Sidebar + */ + media.view.Sidebar = media.view.PriorityList.extend({ + className: 'media-sidebar' + }); + /** * wp.media.view.Attachment */ @@ -1118,7 +1312,7 @@ template: media.template('attachment'), events: { - 'click .attachment-preview': 'toggleSelection', + 'mousedown .attachment-preview': 'toggleSelection', 'change .describe': 'describe' }, @@ -1322,29 +1516,47 @@ this.initSortable(); - this.controller.state().on( 'change:edge change:gutter', this.css, this ); + _.bindAll( this, 'css' ); + this.model.on( 'change:edge change:gutter', this.css, this ); + this._resizeCss = _.debounce( _.bind( this.css, this ), this.refreshSensitivity ); + $(window).on( 'resize.attachments', this._resizeCss ); this.css(); }, destroy: function() { this.collection.off( 'add remove reset', null, this ); - this.controller.state().off( 'change:edge change:gutter', this.css, this ); + this.model.off( 'change:edge change:gutter', this.css, this ); + $(window).off( 'resize.attachments', this._resizeCss ); }, css: function() { - var $css = $( '#' + this.el.id + '-css' ), - state = this.controller.state(); + var $css = $( '#' + this.el.id + '-css' ); if ( $css.length ) $css.remove(); media.view.Attachments.$head().append( this.template({ id: this.el.id, - edge: state.get('edge'), - gutter: state.get('gutter') + edge: this.edge(), + gutter: this.model.get('gutter') }) ); }, + edge: function() { + var edge = this.model.get('edge'), + gutter, width, columns; + + if ( ! this.$el.is(':visible') ) + return edge; + + + gutter = this.model.get('gutter') * 2; + width = this.$el.width() - gutter; + columns = Math.ceil( width / ( edge + gutter ) ); + edge = Math.floor( ( width - ( columns * gutter ) ) / columns ); + return edge; + }, + initSortable: function() { var collection = this.collection, from; @@ -1393,7 +1605,7 @@ render: function() { // If there are no elements, load some. if ( ! this.collection.length ) { - this.collection.more(); + this.collection.more().done( this.scroll ); this.$el.empty(); return this; } @@ -1410,6 +1622,7 @@ // Then, trigger the scroll event to check if we're within the // threshold to query for additional attachments. this.scroll(); + return this; }, @@ -1441,7 +1654,7 @@ return; if ( this.el.scrollHeight < this.el.scrollTop + ( this.el.clientHeight * this.options.refreshThreshold ) ) { - this.collection.more(); + this.collection.more().done( this.scroll ); } } }, { @@ -1482,6 +1695,54 @@ } }); + + + /** + * wp.media.view.AttachmentsBrowser + */ + media.view.AttachmentsBrowser = Backbone.View.extend({ + tagName: 'div', + className: 'attachments-browser', + + initialize: function() { + this.controller = this.options.controller; + + _.defaults( this.options, { + search: true, + upload: false, + total: true + }); + + this.toolbar = new media.view.Toolbar({ + controller: this.controller + }); + + if ( this.options.search ) { + this.toolbar.add( 'search', new media.view.Search({ + controller: this.controller, + model: this.collection.props, + priority: -40 + }) ); + } + + this.attachments = new media.view.Attachments({ + controller: this.controller, + collection: this.collection, + model: this.model, + sortable: this.options.sortable, + // The single `Attachment` view to be used in the `Attachments` view. + AttachmentView: media.view.Attachment.Library + }); + }, + + render: function() { + this.toolbar.$el.detach(); + this.attachments.$el.detach(); + this.$el.html([ this.toolbar.render().el, this.attachments.render().el ]); + return this; + } + }); + /** * wp.media.view.SelectionPreview */ @@ -1533,6 +1794,83 @@ } }); + /** + * wp.media.view.Selection + */ + media.view.Selection = Backbone.View.extend({ + tagName: 'div', + className: 'media-selection', + template: media.template('media-selection'), + + events: { + 'click .clear-selection': 'clear' + }, + + initialize: function() { + _.defaults( this.options, { + clearable: true + }); + + this.controller = this.options.controller; + this.attachments = new media.view.Attachments({ + controller: this.controller, + collection: this.collection, + sortable: true, + model: new Backbone.Model({ + edge: 40, + gutter: 5 + }), + + // The single `Attachment` view to be used in the `Attachments` view. + AttachmentView: media.view.Attachment.Selection + }); + + this.collection.on( 'add remove reset', this.refresh, this ); + }, + + destroy: function() { + this.collection.off( 'add remove reset', this.refresh, this ); + }, + + render: function() { + this.attachments.$el.detach(); + this.attachments.render(); + + this.$el.html( this.template( this.options ) ); + + this.$('.selection-view').replaceWith( this.attachments.$el ); + this.refresh(); + return this; + }, + + refresh: function() { + // If the selection hasn't been rendered, bail. + if ( ! this.$el.children().length ) + return; + + // If nothing is selected, display nothing. + this.$el.toggleClass( 'empty', ! this.collection.length ); + this.$('.count').text( this.collection.length + ' ' + l10n.selected ); + }, + + clear: function( event ) { + event.preventDefault(); + this.collection.clear(); + } + }); + + + /** + * wp.media.view.Attachment.Selection + */ + media.view.Attachment.Selection = media.view.Attachment.extend({ + // On click, just select the model, instead of removing the model from + // the selection. + toggleSelection: function() { + this.controller.state().get('selection').single( this.model ); + } + }); + /** * wp.media.view.Settings diff --git a/wp-includes/media.php b/wp-includes/media.php index 990d64ec9a..079aab574c 100644 --- a/wp-includes/media.php +++ b/wp-includes/media.php @@ -1300,7 +1300,9 @@ function wp_print_media_templates( $attachment ) {

<%- title %>

× -
+
+
+
- - + +