From ec0ee0164cbdb2df7519aeb005ae478358691d3e Mon Sep 17 00:00:00 2001 From: Daryl Koopersmith Date: Thu, 6 Sep 2012 07:46:15 +0000 Subject: [PATCH] Adds UI for media modal toolbars, buttons, and the selected item(s) status. Currently uses actions for inserting media into a post as an example (hence the raw text). To test a workflow that supports multiple selection, run the following in your browser's JavaScript console: wp.media({ multiple: true }); see #21390, #21808. git-svn-id: https://develop.svn.wordpress.org/trunk@21769 602fd350-edb4-49c9-b593-d223f7449a82 --- wp-includes/css/media-views.css | 163 +++++++++++++++++++++++++++- wp-includes/js/media-views.js | 182 +++++++++++++++++++++++++++++++- wp-includes/media.php | 11 ++ 3 files changed, 351 insertions(+), 5 deletions(-) diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css index de1b4ed34e..a362628bca 100644 --- a/wp-includes/css/media-views.css +++ b/wp-includes/css/media-views.css @@ -53,6 +53,37 @@ overflow: auto; } +/** + * Toolbar + */ +.media-toolbar { + position: relative; + z-index: 50; + height: 60px; + border-bottom: 1px solid #dfdfdf; +} + +.media-toolbar-primary { + float: right; +} + +.media-toolbar-secondary { + float: left; +} + +.media-toolbar .media-button { + float: left; + margin-top: 19px; +} + +.media-toolbar-primary .media-button { + margin-left: 10px; +} + +.media-toolbar-secondary .media-button { + margin-right: 10px; +} + /** * Workspace */ @@ -98,6 +129,7 @@ width: auto; right: 0; border-color: #83B4D8; + box-shadow: 0 0 0 10px #fff; } .existing-attachments { @@ -106,12 +138,56 @@ left: 200px; right: 0; bottom: 0; + margin: 0 20px; +} + +.media-workspace .attachments, +.media-workspace .media-toolbar { + -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-workspace .attachments { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + height: auto; + width: auto; +} + +.media-workspace.with-toolbar .attachments { + top: 61px; +} + +.media-workspace .media-toolbar { + margin-top: -61px; +} + +.media-workspace.with-toolbar .media-toolbar { + margin-top: 0; +} + +.media-workspace .media-toolbar .add-to-gallery, +.media-workspace .media-toolbar .create-new-gallery { + display: none; } /** * Attachments */ .attachments { + position: relative; width: 100%; height: 100%; } @@ -128,15 +204,15 @@ .attachments-header h3 { float: left; margin: 0; - padding: 0 0 0 10px; + padding: 0; line-height: 50px; font-size: 18px; font-weight: 200; } .attachments-header input { float: right; - margin-top: 10px; - margin-right: 10px; + margin-top: 12px; + line-height: 18px; } .attachments ul { @@ -146,7 +222,7 @@ right: 0; bottom: 0; overflow: auto; - margin: 0 10px 20px; + margin: 0 -10px 20px; } /** @@ -171,6 +247,21 @@ border-color: #21759b; } +.attachment.selected:after { + content: '\2713'; + display: block; + height: 24px; + width: 24px; + position: absolute; + top: 0; + left: 0; + line-height: 24px; + font-size: 18px; + text-align: center; + color: #fff; + background: #21759b; +} + .attachment-thumbnail { position: absolute; top: 0; @@ -275,4 +366,68 @@ .uploading .upload-attachments .media-progress-bar { display: block; +} + +/** + * Selection Preview + */ + +.selection-preview { + position: relative; + height: 60px; + overflow: hidden; +} + +.selected-img { + float: left; + position: relative; + margin-right: 14px; +} + +.selection-preview img { + max-width: 40px; + max-height: 40px; + float: left; + margin-top: 6px; + margin-left: 1px; + border: 2px solid white; + box-shadow: + 0 0 0 1px #ccc, + 3px 3px 0 0 #fff, + 3px 3px 0 1px #ccc, + 6px 6px 0 0 #fff, + 6px 6px 0 1px #ccc; +} + +.selection-preview .selected-count-1 img { + margin-top: 8px; + box-shadow: 0 0 0 1px #ccc; +} + +.selection-preview .selected-count-2 img { + margin-top: 7px; + box-shadow: + 0 0 0 1px #ccc, + 3px 3px 0 0 #fff, + 3px 3px 0 1px #ccc; +} + +.selection-preview .count { + position: absolute; + bottom: 0; + right: 0; + height: 16px; + min-width: 8px; + padding: 0 4px; + font-size: 12px; + text-align: center; + font-weight: bold; + color: #999; + background: #fff; + box-shadow: -1px -1px 2px -1px rgba( 0, 0, 0, 0.2 ); +} + +.selection-preview .clear-selection { + float: left; + line-height: 60px; } \ No newline at end of file diff --git a/wp-includes/js/media-views.js b/wp-includes/js/media-views.js index d1c9b0650c..30136b3b1f 100644 --- a/wp-includes/js/media-views.js +++ b/wp-includes/js/media-views.js @@ -138,6 +138,102 @@ } }); + /** + * wp.media.view.Toolbar + */ + media.view.Toolbar = Backbone.View.extend({ + tagName: 'div', + className: 'media-toolbar', + + initialize: function() { + this._views = {}; + this.$primary = $('
').prependTo( this.$el ); + this.$secondary = $('
').prependTo( this.$el ); + + if ( this.options.items ) { + _.each( this.options.items, function( view, id ) { + this.add( id, view, { silent: true } ); + }, this ); + this.render(); + } + }, + + render: function() { + var views = _.chain( this._views ).sortBy( function( view ) { + return view.options.priority || 10; + }).groupBy( function( view ) { + return ( view.options.priority || 10 ) > 0 ? 'primary' : 'secondary'; + }).value(); + + // Make sure to detach the elements we want to reuse. + // Otherwise, `jQuery.html()` will unbind their events. + $( _.pluck( this._views, 'el' ) ).detach(); + this.$primary.html( _.pluck( views.primary, 'el' ) ); + this.$secondary.html( _.pluck( views.secondary, 'el' ) ); + + return this; + }, + + add: function( id, view, options ) { + if ( ! ( view instanceof Backbone.View ) ) { + view.classes = [ id ].concat( view.classes || [] ); + view = new media.view.Button( view ).render(); + } + + this._views[ id ] = view; + if ( ! options || ! options.silent ) + this.render(); + return this; + }, + + remove: function( id, options ) { + delete this._views[ id ]; + if ( ! options || ! options.silent ) + this.render(); + return this; + } + }); + + + /** + * wp.media.view.Button + */ + media.view.Button = Backbone.View.extend({ + tagName: 'a', + className: 'media-button', + attributes: { href: '#' }, + + events: { + 'click': 'click' + }, + + initialize: function() { + _.defaults( this.options, { + style: 'secondary', + text: '', + classes: [] + }); + }, + + render: function() { + var classes = [ this.className ]; + + if ( this.options.style ) + classes.push( 'button-' + this.options.style ); + + classes = classes.concat( this.options.classes ); + this.el.className = classes.join(' '); + this.$el.text( this.options.text ); + return this; + }, + + click: function( event ) { + event.preventDefault(); + if ( this.options.click ) + this.options.click.apply( this, arguments ); + } + }); + /** * wp.media.view.Workspace */ @@ -159,6 +255,12 @@ uploader: {} }); + this.$content = $('
'); + + // If this supports multiple attachments, initialize the sample toolbar view. + if ( this.controller.get('multiple') ) + this.initToolbarView(); + this.attachmentsView = new media.view.Attachments({ controller: this.controller, directions: 'Select stuff.', @@ -167,7 +269,6 @@ }) }); - this.$content = $('
'); this.$content.append( this.attachmentsView.$el ); // Track uploading attachments. @@ -242,6 +343,42 @@ file.attachment.destroy(); } }, this.options.uploader ) ); + }, + + // Initializes the toolbar view. Currently uses defaults set for + // inserting media into a post. This should be pulled out into the + // appropriate workflow when the time comes, but is currently here + // to test multiple selections. + initToolbarView: function() { + this.toolbarView = new media.view.Toolbar({ + items: { + 'selection-preview': new media.view.SelectionPreview({ + controller: this.controller, + collection: this.controller.selection, + priority: -40 + }), + 'insert-into-post': { + style: 'primary', + text: 'Insert into post', + priority: 40 + }, + 'create-new-gallery': { + style: 'primary', + text: 'Create a new gallery', + priority: 30 + }, + 'add-to-gallery': { + text: 'Add to gallery', + priority: 20 + } + } + }); + + this.controller.selection.on( 'add remove', function() { + this.$el.toggleClass( 'with-toolbar', !! this.controller.selection.length ); + }, this ); + + this.$content.append( this.toolbarView.$el ); } }); @@ -426,4 +563,47 @@ this.$el.removeClass('selected'); } }); + + /** + * wp.media.view.SelectionPreview + */ + media.view.SelectionPreview = Backbone.View.extend({ + tagName: 'div', + className: 'selection-preview', + template: media.template('media-selection-preview'), + + events: { + 'click .clear-selection': 'clear' + }, + + initialize: function() { + this.controller = this.options.controller; + this.collection.on( 'add change:url remove', this.render, this ); + this.render(); + }, + + render: function() { + var options = {}, + first, sizes, amount; + + // If nothing is selected, display nothing. + if ( ! this.collection.length ) { + this.$el.empty(); + return this; + } + + options.count = this.collection.length; + first = this.collection.first(); + sizes = first.get('sizes'); + options.thumbnail = ( sizes && sizes.thumbnail ) ? sizes.thumbnail.url : first.get('url'); + + this.$el.html( this.template( options ) ); + return this; + }, + + clear: function( event ) { + event.preventDefault(); + this.collection.clear(); + } + }); }(jQuery)); \ No newline at end of file diff --git a/wp-includes/media.php b/wp-includes/media.php index 8839fd9151..a23e02c96d 100644 --- a/wp-includes/media.php +++ b/wp-includes/media.php @@ -1649,5 +1649,16 @@ function wp_print_media_templates( $attachment ) {
+ +