Make WordPress Core

Changeset 22321


Ignore:
Timestamp:
10/29/2012 06:56:23 AM (14 years ago)
Author:
koopersmith
Message:

Add a sidebar to the media modal.

  • Adds wp.media.view.Sidebar, to aid in rendering the sidebar.
  • Removes the directions from the Attachments view and shifts search into a separate view (wp.mce.view.Search) that can be relocated at will. This also serves to simplify the Attachments view by removing the nested list and $list parameters.
  • Show the toolbar on the featured image workflow, effectively requiring confirmation before closing the dialog.

see #21390, #21776, #21808.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/includes/meta-boxes.php

    r22320 r22321  
    10191019                        $thumbnailId = $element.find('input[name="thumbnail_id"]'),
    10201020                        title        = '<?php _e( "Choose a Featured Image" ); ?>',
    1021                         workflow, selection, setFeaturedImage;
     1021                        update       = '<?php _e( "Update Featured Image" ); ?>',
     1022                        frame, selection, setFeaturedImage;
    10221023
    10231024                setFeaturedImage = function( thumbnailId ) {
    … …  
    10301031                        event.preventDefault();
    10311032
    1032                         if ( ! workflow ) {
    1033                                 workflow = wp.media({
     1033                        if ( ! frame ) {
     1034                                frame = wp.media({
    10341035                                        title:   title,
    10351036                                        library: {
    … …  
    10381039                                });
    10391040
    1040                                 selection = workflow.state().get('selection');
    1041 
    1042                                 selection.on( 'add', function( model ) {
    1043                                         var sizes = model.get('sizes'),
    1044                                                 size;
    1045 
    1046                                         setFeaturedImage( model.id );
    1047 
    1048                                         // @todo: might need a size hierarchy equivalent.
    1049                                         if ( sizes )
    1050                                                 size = sizes['post-thumbnail'] || sizes.medium;
    1051 
    1052                                         // @todo: Need a better way of accessing full size
    1053                                         // data besides just calling toJSON().
    1054                                         size = size || model.toJSON();
    1055 
    1056                                         workflow.close();
    1057                                         selection.clear();
    1058 
    1059                                         $( '<img />', {
    1060                                                 src:    size.url,
    1061                                                 width:  size.width
    1062                                         }).prependTo( $element );
    1063                                 });
     1041                                frame.toolbar( new wp.media.view.Toolbar({
     1042                                        controller: frame,
     1043                                        items: {
     1044                                                update: {
     1045                                                        style:    'primary',
     1046                                                        text:     update,
     1047                                                        priority: 40,
     1048
     1049                                                        click: function() {
     1050                                                                var selection = frame.state().get('selection'),
     1051                                                                        model = selection.first(),
     1052                                                                        sizes = model.get('sizes'),
     1053                                                                        size;
     1054
     1055                                                                setFeaturedImage( model.id );
     1056
     1057                                                                // @todo: might need a size hierarchy equivalent.
     1058                                                                if ( sizes )
     1059                                                                        size = sizes['post-thumbnail'] || sizes.medium;
     1060
     1061                                                                // @todo: Need a better way of accessing full size
     1062                                                                // data besides just calling toJSON().
     1063                                                                size = size || model.toJSON();
     1064
     1065                                                                frame.close();
     1066                                                                selection.clear();
     1067
     1068                                                                $( '<img />', {
     1069                                                                        src:    size.url,
     1070                                                                        width:  size.width
     1071                                                                }).prependTo( $element );
     1072                                                        }
     1073                                                }
     1074                                        }
     1075                                }) );
    10641076                        }
    10651077
    1066                         workflow.open();
     1078                        frame.open();
    10671079                });
    10681080
  • trunk/wp-includes/css/media-views.css

    r22320 r22321  
    7373 */
    7474.media-toolbar {
    75         position: relative;
    76         z-index: 50;
    77         height: 60px;
     75        position: absolute;
     76        top: 0;
     77        left: 220px;
     78        right: 0;
     79        z-index: 100;
     80        height: 50px;
    7881        padding: 0 10px;
    7982        border-bottom: 1px solid #dfdfdf;
    … …  
    9295        margin-left: 10px;
    9396        float: left;
    94         margin-top: 16px;
     97        margin-top: 10px;
    9598}
    9699
    … …  
    99102        margin-right: 10px;
    100103        float: left;
    101         margin-top: 16px;
     104        margin-top: 10px;
     105}
     106
     107/**
     108 * Sidebar
     109 */
     110.media-sidebar {
     111        position: absolute;
     112        top: 0;
     113        left: 0;
     114        bottom: 0;
     115        width: 219px;
     116        z-index: 50;
     117        background: #f5f5f5;
     118        border-right: 1px solid #dfdfdf;
     119}
     120
     121.hide-sidebar .media-sidebar {
     122        display: none;
     123}
     124
     125.media-sidebar .sidebar-title {
     126        font-weight: 200;
     127        font-size: 20px;
     128        margin: 0;
     129        padding: 12px 10px 10px;
     130        line-height: 28px;
     131        /*border-bottom: 1px solid #dfdfdf;*/
     132}
     133
     134.media-sidebar .sidebar-content {
     135        padding: 0 10px;
     136}
     137
     138.media-sidebar .search {
     139        display: block;
     140        width: 100%;
     141}
     142
     143.media-sidebar .selection-preview {
     144        display: block;
     145        padding-top: 5px;
    102146}
    103147
    … …  
    106150 */
    107151
    108 .media-frame .attachments,
    109 .media-frame .media-toolbar {
     152.media-frame .media-content,
     153.media-frame .media-toolbar,
     154.media-frame .media-sidebar {
    110155        -webkit-transition-property: left, right, top, bottom, margin;
    111156        -moz-transition-property:    left, right, top, bottom, margin;
    … …  
    121166}
    122167
    123 .media-frame .attachments {
    124         position: absolute;
    125         top: 61px;
    126         left: 0;
     168.media-frame .media-content {
     169        position: absolute;
     170        top: 51px;
     171        left: 220px;
    127172        right: 0;
    128173        bottom: 0;
    129174        height: auto;
    130175        width: auto;
    131 }
    132 
    133 .media-frame.hide-toolbar .attachments {
    134         top: 0;
    135 }
    136 
    137 .media-frame .media-toolbar {
    138         margin-top: 0;
    139 }
    140 
    141 .media-frame.hide-toolbar .media-toolbar {
    142         margin-top: -61px;
     176        overflow: auto;
     177}
     178
     179.media-frame.hide-sidebar .media-content {
     180        left: 0;
    143181}
    144182
    … …  
    146184        display: none;
    147185}
    148 /**
    149  * Attachments
    150  */
    151 .attachments {
    152         position: relative;
    153         width: 100%;
    154         height: 100%;
    155 }
    156 
    157 .attachments-header {
    158         position: absolute;
    159         top: 0;
    160         left: 0;
    161         right: 0;
    162         height: 50px;
    163         padding: 0 10px;
    164         background: #fff;
    165 }
    166 
    167 .attachments-header h3 {
    168         float: left;
    169         margin: 0;
    170         padding: 0;
    171         line-height: 50px;
    172         font-size: 18px;
    173         font-weight: 200;
    174 }
    175 
    176 .attachments-header .search {
    177         float: right;
     186
     187/**
     188 * Search
     189 */
     190.media-frame .search {
    178191        margin-top: 11px;
    179192        padding: 4px;
    … …  
    184197}
    185198
     199/**
     200 * Attachments
     201 */
     202/*.attachments {
     203        position: relative;
     204        width: 100%;
     205        height: 100%;
     206}
     207
     208.attachments-header {
     209        position: absolute;
     210        top: 0;
     211        left: 0;
     212        right: 0;
     213        height: 50px;
     214        padding: 0 10px;
     215        background: #fff;
     216}
     217
     218.attachments-header h3 {
     219        float: left;
     220        margin: 0;
     221        padding: 0;
     222        line-height: 50px;
     223        font-size: 18px;
     224        font-weight: 200;
     225}
     226
    186227.attachments ul {
    187228        position: absolute;
    … …  
    192233        overflow: auto;
    193234        margin: 0 0 20px;
    194 }
     235}*/
    195236
    196237/**
    … …  
    402443        background: rgba( 0, 86, 132, 0.9 );
    403444
    404         /*z-index: -200;*/
    405445        z-index: 250000;
    406446        display: none;
    … …  
    414454        transition:         opacity 250ms;
    415455}
    416 
    417 /*.drag-over .uploader-window {
    418         z-index: 250000;
    419 }*/
    420456
    421457.uploader-window-content {
  • trunk/wp-includes/js/media-views.js

    r22320 r22321  
    114114                        id:       'library',
    115115                        multiple: false,
    116                         describe: false
     116                        describe: false,
     117                        title:    l10n.mediaLibrary
    117118                },
    118119
    … …  
    131132                                toolbar;
    132133
     134                        // Toolbar.
    133135                        toolbar = this._postLibraryToolbar = new media.view.Toolbar.PostLibrary({
    134136                                controller: frame,
    135                                 selection:  this.get('selection')
     137                                state:      this
    136138                        });
    137139
    … …  
    139141                        this.get('selection').on( 'add remove', toolbar.visibility, toolbar );
    140142
     143                        // Sidebar.
     144                        frame.sidebar( new media.view.Sidebar({
     145                                controller: frame,
     146                                views: {
     147                                        search: new media.view.Search({
     148                                                controller: frame,
     149                                                model:      this.get('library').props,
     150                                                priority:   20
     151                                        }),
     152
     153                                        selection: new media.view.SelectionPreview({
     154                                                controller: frame,
     155                                                collection: this.get('selection'),
     156                                                priority:   40
     157                                        })
     158                                }
     159                        }) );
     160
     161                        // Content.
    141162                        frame.content( new media.view.Attachments({
    142                                 directions: this.get('multiple') ? l10n.selectMediaMultiple : l10n.selectMediaSingular,
    143163                                controller: frame,
    144164                                collection: this.get('library'),
    … …  
    146166                                AttachmentView: media.view.Attachment.Library
    147167                        }).render() );
    148 
    149                         if ( ! this.get('selection').length )
    150                                 frame.$el.addClass('hide-toolbar');
    151168
    152169                        // If we're in a workflow that supports multiple attachments,
    … …  
    174191                        id:         'gallery',
    175192                        multiple:   true,
    176                         describe:   true
     193                        describe:   true,
     194                        title:      l10n.createGallery
    177195                },
    178196
    … …  
    187205                        var frame = this.frame;
    188206
     207                        // Toolbar.
    189208                        frame.toolbar( new media.view.Toolbar.Gallery({
    190209                                controller: frame,
    191                                 editing:    this.get('editing'),
    192                                 selection:  this.get('selection')
     210                                state:      this
    193211                        }) );
    194212
     213                        // Sidebar.
     214                        frame.sidebar( new media.view.Sidebar({
     215                                controller: frame
     216                        }).render() );
     217
     218                        // Content.
    195219                        frame.content( new media.view.Attachments({
    196                                 directions: 'Gallery time!',
    197220                                controller: frame,
    198221                                collection: this.get('selection'),
    … …  
    246269
    247270                render: function() {
    248                         var els = [ this.sidebar().el, this.toolbar().el, this.content().el ];
     271                        var els = [ this.toolbar().el, this.sidebar().el, this.content().el ];
    249272
    250273                        if ( this.modal )
    … …  
    635658        media.view.Toolbar.PostLibrary = media.view.Toolbar.extend({
    636659                initialize: function() {
    637                         var selection = this.options.selection,
     660                        var state = this.options.state,
     661                                selection = state.get('selection'),
    638662                                controller = this.options.controller;
    639663
    640664                        this.options.items = {
    641                                 'selection-preview': new media.view.SelectionPreview({
    642                                         controller: controller,
    643                                         collection: selection,
    644                                         priority: -40
    645                                 }),
    646 
    647665                                'create-new-gallery': {
    648666                                        style:    'primary',
    … …  
    663681                                                        click: function() {
    664682                                                                controller.close();
    665                                                                 controller.state().trigger( 'insert', selection );
     683                                                                state.trigger( 'insert', selection );
    666684                                                                selection.clear();
    667685                                                        }
    … …  
    699717
    700718                        media.view.Toolbar.prototype.initialize.apply( this, arguments );
     719                        this.visibility();
    701720                },
    702721
    703722                visibility: function() {
    704                         var selection = this.options.selection,
     723                        var state = this.options.state,
     724                                selection = state.get('selection'),
    705725                                controller = this.options.controller,
    706726                                count = selection.length,
    707727                                showGallery;
    708 
    709                         controller.$el.toggleClass( 'hide-toolbar', ! count );
    710728
    711729                        // Check if every attachment in the selection is an image.
    … …  
    719737                                button.model.set( 'style', showGallery ? '' : 'primary' );
    720738                        });
     739
     740                        _.first( insert.buttons ).model.set( 'disabled', ! count );
    721741                }
    722742        });
    … …  
    726746        media.view.Toolbar.Gallery = media.view.Toolbar.extend({
    727747                initialize: function() {
    728                         var editing = this.options.editing,
    729                                 selection = this.options.selection,
     748                        var state = this.options.state,
     749                                editing = state.get('editing'),
     750                                selection = state.get('selection'),
    730751                                controller = this.options.controller;
    731752
    … …  
    737758                                        click:    function() {
    738759                                                controller.close();
    739                                                 controller.state().trigger( 'update', selection );
     760                                                state.trigger( 'update', selection );
    740761                                                selection.clear();
    741762                                                controller.state('library');
    … …  
    770791
    771792                defaults: {
    772                         text:  '',
    773                         style: '',
    774                         size:  'large'
     793                        text:     '',
     794                        style:    '',
     795                        size:     'large',
     796                        disabled: false
    775797                },
    776798
    … …  
    797819
    798820                render: function() {
    799                         var classes = [ 'button', this.className ];
    800 
    801                         if ( this.model.get('style') )
    802                                 classes.push( 'button-' + this.model.get('style') );
    803 
    804                         if ( this.model.get('size') )
    805                                 classes.push( 'button-' + this.model.get('size') );
     821                        var classes = [ 'button', this.className ],
     822                                model = this.model.toJSON();
     823
     824                        if ( model.style )
     825                                classes.push( 'button-' + model.style );
     826
     827                        if ( model.size )
     828                                classes.push( 'button-' + model.size );
    806829
    807830                        classes = _.uniq( classes.concat( this.options.classes ) );
    808831                        this.el.className = classes.join(' ');
    809832
     833                        this.$el.attr( 'disabled', model.disabled );
    810834
    811835                        // Detach the dropdown.
    … …  
    823847                click: function( event ) {
    824848                        event.preventDefault();
    825                         if ( this.options.click )
     849                        if ( this.options.click && ! this.model.get('disabled') )
    826850                                this.options.click.apply( this, arguments );
    827851                }
    … …  
    851875                render: function() {
    852876                        this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() );
     877                        return this;
     878                }
     879        });
     880
     881        /**
     882         * wp.media.view.Sidebar
     883         */
     884        media.view.Sidebar = Backbone.View.extend({
     885                tagName:   'div',
     886                className: 'media-sidebar',
     887                template:  media.template('sidebar'),
     888
     889                initialize: function() {
     890                        this.controller = this.options.controller;
     891                        this._views     = {};
     892
     893                        if ( this.options.views )
     894                                this.add( this.options.views, { silent: true }).render();
     895                },
     896
     897                render: function() {
     898                        var els = _( this._views ).chain().sortBy( function( view ) {
     899                                        return view.options.priority || 10;
     900                                }).pluck('el').value();
     901
     902                        // Make sure to detach the elements we want to reuse.
     903                        // Otherwise, `jQuery.html()` will unbind their events.
     904                        $( els ).detach();
     905
     906                        this.$el.html( this.template({
     907                                title:    this.controller.state().get('title') || '',
     908                                uploader: this.controller.options.uploader
     909                        }) );
     910
     911                        this.$('.sidebar-content').html( els );
     912
     913                        return this;
     914                },
     915
     916                add: function( id, view, options ) {
     917                        // Accept an object with an `id` : `view` mapping.
     918                        if ( _.isObject( id ) ) {
     919                                _.each( id, function( view, id ) {
     920                                        this.add( id, view, options );
     921                                }, this );
     922                                return this;
     923                        }
     924
     925                        view.controller = view.controller || this.controller;
     926
     927                        this._views[ id ] = view;
     928                        if ( ! options || ! options.silent )
     929                                this.render();
     930                        return this;
     931                },
     932
     933                get: function( id ) {
     934                        return this._views[ id ];
     935                },
     936
     937                remove: function( id, options ) {
     938                        delete this._views[ id ];
     939                        if ( ! options || ! options.silent )
     940                                this.render();
    853941                        return this;
    854942                }
    … …  
    10691157         */
    10701158        media.view.Attachments = Backbone.View.extend({
    1071                 tagName:   'div',
     1159                tagName:   'ul',
    10721160                className: 'attachments',
    1073                 template:  media.template('attachments'),
    10741161
    10751162                events: {
    1076                         'keyup .search': 'search'
     1163                        'scroll': 'scroll'
    10771164                },
    10781165
    … …  
    10931180                        }, this );
    10941181
    1095                         this.collection.on( 'reset', this.refresh, this );
    1096 
    1097                         this.$list = $('<ul />');
    1098                         this.list  = this.$list[0];
    1099 
     1182                        this.collection.on( 'reset', this.render, this );
     1183
     1184                        // Throttle the scroll handler.
    11001185                        this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
    1101                         this.$list.on( 'scroll.attachments', this.scroll );
    11021186
    11031187                        this.initSortable();
    … …  
    11111195                                return;
    11121196
    1113                         this.$list.sortable({
     1197                        this.$el.sortable({
    11141198                                // If the `collection` has a `comparator`, disable sorting.
    11151199                                disabled: !! collection.comparator,
    … …  
    11171201                                // Prevent attachments from being dragged outside the bounding
    11181202                                // box of the list.
    1119                                 containment: this.$list,
     1203                                containment: this.$el,
    11201204
    11211205                                // Change the position of the attachment as soon as the
    … …  
    11451229                        // check to see if we have a `comparator`. If so, disable sorting.
    11461230                        collection.props.on( 'change:orderby', function() {
    1147                                 this.$list.sortable( 'option', 'disabled', !! collection.comparator );
     1231                                this.$el.sortable( 'option', 'disabled', !! collection.comparator );
    11481232                        }, this );
    11491233                },
    11501234
    11511235                render: function() {
    1152                         // Detach the list from the DOM to prevent event removal.
    1153                         this.$list.detach();
    1154 
    1155                         this.$el.html( this.template( this.options ) ).append( this.$list );
    1156                         this.refresh();
    1157                         return this;
    1158                 },
    1159 
    1160                 refresh: function() {
    11611236                        // If there are no elements, load some.
    11621237                        if ( ! this.collection.length ) {
    11631238                                this.collection.more();
    1164                                 this.$list.empty();
     1239                                this.$el.empty();
    11651240                                return this;
    11661241                        }
    … …  
    11681243                        // Otherwise, create all of the Attachment views, and replace
    11691244                        // the list in a single DOM operation.
    1170                         this.$list.html( this.collection.map( function( attachment ) {
     1245                        this.$el.html( this.collection.map( function( attachment ) {
    11711246                                return new this.options.AttachmentView({
    11721247                                        controller: this.controller,
    … …  
    11891264                        }).render();
    11901265
    1191                         children = this.$list.children();
     1266                        children = this.$el.children();
    11921267
    11931268                        if ( children.length > index )
    11941269                                children.eq( index ).before( view.$el );
    11951270                        else
    1196                                 this.$list.append( view.$el );
     1271                                this.$el.append( view.$el );
    11971272                },
    11981273
    11991274                remove: function( attachment, index ) {
    1200                         var children = this.$list.children();
     1275                        var children = this.$el.children();
    12011276                        if ( children.length )
    12021277                                children.eq( index ).detach();
    … …  
    12051280                scroll: function( event ) {
    12061281                        // @todo: is this still necessary?
    1207                         if ( ! this.$list.is(':visible') )
     1282                        if ( ! this.$el.is(':visible') )
    12081283                                return;
    12091284
    1210                         if ( this.list.scrollHeight < this.list.scrollTop + ( this.list.clientHeight * this.options.refreshThreshold ) ) {
     1285                        if ( this.el.scrollHeight < this.el.scrollTop + ( this.el.clientHeight * this.options.refreshThreshold ) ) {
    12111286                                this.collection.more();
    12121287                        }
     1288                }
     1289        });
     1290
     1291        /**
     1292         * wp.media.view.Search
     1293         */
     1294        media.view.Search = Backbone.View.extend({
     1295                tagName:   'input',
     1296                className: 'search',
     1297
     1298                attributes: {
     1299                        type:        'text',
     1300                        placeholder: l10n.search
     1301                },
     1302
     1303                events: {
     1304                        'keyup': 'search'
     1305                },
     1306
     1307                render: function() {
     1308                        this.el.value = this.model.escape('search');
     1309                        return this;
    12131310                },
    12141311
    12151312                search: function( event ) {
    1216                         var props = this.collection.props;
    1217 
    12181313                        if ( event.target.value )
    1219                                 props.set( 'search', event.target.value );
     1314                                this.model.set( 'search', event.target.value );
    12201315                        else
    1221                                 props.unset('search');
     1316                                this.model.unset('search');
    12221317                }
    12231318        });
  • trunk/wp-includes/media.php

    r22320 r22321  
    13101310        </script>
    13111311
    1312         <script type="text/html" id="tmpl-attachments">
    1313                 <div class="attachments-header">
    1314                         <h3><%- directions %></h3>
    1315                         <input class="search" type="text" placeholder="<?php esc_attr_e('Search'); ?>" />
    1316                 </div>
     1312        <script type="text/html" id="tmpl-sidebar">
     1313                <h2 class="sidebar-title"><%- title %></h2>
     1314                <div class="sidebar-content"></div>
    13171315        </script>
    13181316
  • trunk/wp-includes/script-loader.php

    r22236 r22321  
    324324        did_action( 'init' ) && $scripts->localize( 'media-views', '_wpMediaViewsL10n', array(
    325325                // Generic
    326                 'insertMedia'           => __( 'Insert Media' ),
    327                 'selectMediaSingular'   => __( 'Select a media file:' ),
    328                 'selectMediaMultiple'   => __( 'Select one or more media files:' ),
     326                'insertMedia' => __( 'Insert Media' ),
     327                'search'      => __( 'Search' ),
    329328
    330329                // Library
     330                'mediaLibrary'          => __( 'Media Library' ),
    331331                'createNewGallery'      => __( 'Create a new gallery' ),
    332332                'insertIntoPost'        => __( 'Insert into post' ),
    … …  
    334334
    335335                // Gallery
     336                'createGallery'          => __( 'Create Gallery' ),
    336337                'returnToLibrary'        => __( 'Return to media library' ),
    337338                'continueEditingGallery' => __( 'Continue editing gallery' ),
Note: See TracChangeset for help on using the changeset viewer.