diff --git a/wp-admin/includes/meta-boxes.php b/wp-admin/includes/meta-boxes.php index 2aee5aa2d0..51e38b60cb 100644 --- a/wp-admin/includes/meta-boxes.php +++ b/wp-admin/includes/meta-boxes.php @@ -1018,7 +1018,8 @@ function post_thumbnail_meta_box( $post ) { var $element = $('#select-featured-image'), $thumbnailId = $element.find('input[name="thumbnail_id"]'), title = '', - workflow, selection, setFeaturedImage; + update = '', + frame, selection, setFeaturedImage; setFeaturedImage = function( thumbnailId ) { $element.find('img').remove(); @@ -1029,41 +1030,52 @@ function post_thumbnail_meta_box( $post ) { $element.on( 'click', '.choose, img', function( event ) { event.preventDefault(); - if ( ! workflow ) { - workflow = wp.media({ + if ( ! frame ) { + frame = wp.media({ title: title, library: { type: 'image' } }); - selection = workflow.state().get('selection'); + frame.toolbar( new wp.media.view.Toolbar({ + controller: frame, + items: { + update: { + style: 'primary', + text: update, + priority: 40, - selection.on( 'add', function( model ) { - var sizes = model.get('sizes'), - size; + click: function() { + var selection = frame.state().get('selection'), + model = selection.first(), + sizes = model.get('sizes'), + size; - setFeaturedImage( model.id ); + setFeaturedImage( model.id ); - // @todo: might need a size hierarchy equivalent. - if ( sizes ) - size = sizes['post-thumbnail'] || sizes.medium; + // @todo: might need a size hierarchy equivalent. + if ( sizes ) + size = sizes['post-thumbnail'] || sizes.medium; - // @todo: Need a better way of accessing full size - // data besides just calling toJSON(). - size = size || model.toJSON(); + // @todo: Need a better way of accessing full size + // data besides just calling toJSON(). + size = size || model.toJSON(); - workflow.close(); - selection.clear(); + frame.close(); + selection.clear(); - $( '', { - src: size.url, - width: size.width - }).prependTo( $element ); - }); + $( '', { + src: size.url, + width: size.width + }).prependTo( $element ); + } + } + } + }) ); } - workflow.open(); + frame.open(); }); $element.on( 'click', '.remove', function( event ) { diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css index 0f22658c48..1f3e92cc57 100644 --- a/wp-includes/css/media-views.css +++ b/wp-includes/css/media-views.css @@ -72,9 +72,12 @@ * Toolbar */ .media-toolbar { - position: relative; - z-index: 50; - height: 60px; + position: absolute; + top: 0; + left: 220px; + right: 0; + z-index: 100; + height: 50px; padding: 0 10px; border-bottom: 1px solid #dfdfdf; } @@ -91,22 +94,64 @@ .media-toolbar-primary > .media-button-group { margin-left: 10px; float: left; - margin-top: 16px; + margin-top: 10px; } .media-toolbar-secondary > .media-button, .media-toolbar-secondary > .media-button-group { margin-right: 10px; float: left; - margin-top: 16px; + margin-top: 10px; +} + +/** + * Sidebar + */ +.media-sidebar { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 219px; + z-index: 50; + background: #f5f5f5; + border-right: 1px solid #dfdfdf; +} + +.hide-sidebar .media-sidebar { + display: none; +} + +.media-sidebar .sidebar-title { + font-weight: 200; + font-size: 20px; + margin: 0; + padding: 12px 10px 10px; + line-height: 28px; + /*border-bottom: 1px solid #dfdfdf;*/ +} + +.media-sidebar .sidebar-content { + padding: 0 10px; +} + +.media-sidebar .search { + display: block; + width: 100%; +} + +.media-sidebar .selection-preview { + display: block; + padding-top: 5px; } /** * Frame */ -.media-frame .attachments, -.media-frame .media-toolbar { +.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; @@ -120,35 +165,41 @@ transition-duration: 0.2s; } -.media-frame .attachments { +.media-frame .media-content { position: absolute; - top: 61px; - left: 0; + top: 51px; + left: 220px; right: 0; bottom: 0; height: auto; width: auto; + overflow: auto; } -.media-frame.hide-toolbar .attachments { - top: 0; -} - -.media-frame .media-toolbar { - margin-top: 0; -} - -.media-frame.hide-toolbar .media-toolbar { - margin-top: -61px; +.media-frame.hide-sidebar .media-content { + left: 0; } .media-frame .media-toolbar .add-to-gallery { display: none; } + +/** + * Search + */ +.media-frame .search { + margin-top: 11px; + padding: 4px; + line-height: 18px; + font-size: 13px; + color: #464646; + font-family: sans-serif; +} + /** * Attachments */ -.attachments { +/*.attachments { position: relative; width: 100%; height: 100%; @@ -173,16 +224,6 @@ font-weight: 200; } -.attachments-header .search { - float: right; - margin-top: 11px; - padding: 4px; - line-height: 18px; - font-size: 13px; - color: #464646; - font-family: sans-serif; -} - .attachments ul { position: absolute; top: 50px; @@ -191,7 +232,7 @@ bottom: 0; overflow: auto; margin: 0 0 20px; -} +}*/ /** * Attachment @@ -401,7 +442,6 @@ bottom: 0; background: rgba( 0, 86, 132, 0.9 ); - /*z-index: -200;*/ z-index: 250000; display: none; text-align: center; @@ -414,10 +454,6 @@ transition: opacity 250ms; } -/*.drag-over .uploader-window { - z-index: 250000; -}*/ - .uploader-window-content { position: absolute; top: 30px; diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js index c994d76119..4efd28c715 100644 --- a/wp-includes/js/media-views.js +++ b/wp-includes/js/media-views.js @@ -113,7 +113,8 @@ defaults: { id: 'library', multiple: false, - describe: false + describe: false, + title: l10n.mediaLibrary }, initialize: function() { @@ -130,25 +131,41 @@ var frame = this.frame, toolbar; + // Toolbar. toolbar = this._postLibraryToolbar = new media.view.Toolbar.PostLibrary({ controller: frame, - selection: this.get('selection') + state: this }); frame.toolbar( toolbar ); this.get('selection').on( 'add remove', toolbar.visibility, toolbar ); + // Sidebar. + frame.sidebar( new media.view.Sidebar({ + controller: frame, + views: { + 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. frame.content( new media.view.Attachments({ - directions: this.get('multiple') ? l10n.selectMediaMultiple : l10n.selectMediaSingular, controller: frame, collection: this.get('library'), // The single `Attachment` view to be used in the `Attachments` view. AttachmentView: media.view.Attachment.Library }).render() ); - if ( ! this.get('selection').length ) - frame.$el.addClass('hide-toolbar'); - // If we're in a workflow that supports multiple attachments, // automatically select any uploading attachments. if ( this.get('multiple') ) @@ -173,7 +190,8 @@ defaults: { id: 'gallery', multiple: true, - describe: true + describe: true, + title: l10n.createGallery }, initialize: function() { @@ -186,14 +204,19 @@ activate: function() { var frame = this.frame; + // Toolbar. frame.toolbar( new media.view.Toolbar.Gallery({ controller: frame, - editing: this.get('editing'), - selection: this.get('selection') + state: this }) ); + // Sidebar. + frame.sidebar( new media.view.Sidebar({ + controller: frame + }).render() ); + + // Content. frame.content( new media.view.Attachments({ - directions: 'Gallery time!', controller: frame, collection: this.get('selection'), sortable: true, @@ -245,7 +268,7 @@ }, render: function() { - var els = [ this.sidebar().el, this.toolbar().el, this.content().el ]; + var els = [ this.toolbar().el, this.sidebar().el, this.content().el ]; if ( this.modal ) this.modal.render(); @@ -634,16 +657,11 @@ // --------------------------------- media.view.Toolbar.PostLibrary = media.view.Toolbar.extend({ initialize: function() { - var selection = this.options.selection, + var state = this.options.state, + selection = state.get('selection'), controller = this.options.controller; this.options.items = { - 'selection-preview': new media.view.SelectionPreview({ - controller: controller, - collection: selection, - priority: -40 - }), - 'create-new-gallery': { style: 'primary', text: l10n.createNewGallery, @@ -662,7 +680,7 @@ text: l10n.insertIntoPost, click: function() { controller.close(); - controller.state().trigger( 'insert', selection ); + state.trigger( 'insert', selection ); selection.clear(); } }, @@ -698,16 +716,16 @@ }; media.view.Toolbar.prototype.initialize.apply( this, arguments ); + this.visibility(); }, visibility: function() { - var selection = this.options.selection, + var state = this.options.state, + selection = state.get('selection'), controller = this.options.controller, count = selection.length, showGallery; - controller.$el.toggleClass( 'hide-toolbar', ! count ); - // Check if every attachment in the selection is an image. showGallery = count > 1 && selection.all( function( attachment ) { return 'image' === attachment.get('type'); @@ -718,6 +736,8 @@ _.each( insert.buttons, function( button ) { button.model.set( 'style', showGallery ? '' : 'primary' ); }); + + _.first( insert.buttons ).model.set( 'disabled', ! count ); } }); @@ -725,8 +745,9 @@ // ----------------------------- media.view.Toolbar.Gallery = media.view.Toolbar.extend({ initialize: function() { - var editing = this.options.editing, - selection = this.options.selection, + var state = this.options.state, + editing = state.get('editing'), + selection = state.get('selection'), controller = this.options.controller; this.options.items = { @@ -736,7 +757,7 @@ priority: 40, click: function() { controller.close(); - controller.state().trigger( 'update', selection ); + state.trigger( 'update', selection ); selection.clear(); controller.state('library'); } @@ -769,9 +790,10 @@ }, defaults: { - text: '', - style: '', - size: 'large' + text: '', + style: '', + size: 'large', + disabled: false }, initialize: function() { @@ -796,17 +818,19 @@ }, render: function() { - var classes = [ 'button', this.className ]; + var classes = [ 'button', this.className ], + model = this.model.toJSON(); - if ( this.model.get('style') ) - classes.push( 'button-' + this.model.get('style') ); + if ( model.style ) + classes.push( 'button-' + model.style ); - if ( this.model.get('size') ) - classes.push( 'button-' + this.model.get('size') ); + if ( model.size ) + classes.push( 'button-' + model.size ); classes = _.uniq( classes.concat( this.options.classes ) ); this.el.className = classes.join(' '); + this.$el.attr( 'disabled', model.disabled ); // Detach the dropdown. if ( this.options.dropdown ) @@ -822,7 +846,7 @@ click: function( event ) { event.preventDefault(); - if ( this.options.click ) + if ( this.options.click && ! this.model.get('disabled') ) this.options.click.apply( this, arguments ); } }); @@ -854,6 +878,70 @@ } }); + /** + * wp.media.view.Sidebar + */ + media.view.Sidebar = 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 }).render(); + }, + + render: function() { + var els = _( this._views ).chain().sortBy( function( view ) { + return view.options.priority || 10; + }).pluck('el').value(); + + // Make sure to detach the elements we want to reuse. + // 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 ); + + return this; + }, + + add: function( id, view, options ) { + // Accept an object with an `id` : `view` mapping. + if ( _.isObject( id ) ) { + _.each( id, function( view, id ) { + this.add( id, view, options ); + }, this ); + return this; + } + + view.controller = view.controller || this.controller; + + this._views[ id ] = view; + if ( ! options || ! options.silent ) + this.render(); + return this; + }, + + get: function( id ) { + return this._views[ id ]; + }, + + remove: function( id, options ) { + delete this._views[ id ]; + if ( ! options || ! options.silent ) + this.render(); + return this; + } + }); + /** * wp.media.view.Attachment */ @@ -1068,12 +1156,11 @@ * wp.media.view.Attachments */ media.view.Attachments = Backbone.View.extend({ - tagName: 'div', + tagName: 'ul', className: 'attachments', - template: media.template('attachments'), events: { - 'keyup .search': 'search' + 'scroll': 'scroll' }, initialize: function() { @@ -1092,13 +1179,10 @@ }, this ); }, this ); - this.collection.on( 'reset', this.refresh, this ); - - this.$list = $('