WordPress.org

Make WordPress Core

Changeset 22321


Ignore:
Timestamp:
10/29/2012 06:56:23 AM (7 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.