Theme Installer: Fix sorting, counts, keyboard navigation; add prev/next to previews.

props matveb.
see #27055.


git-svn-id: https://develop.svn.wordpress.org/trunk@27937 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Nacin 2014-04-03 23:52:45 +00:00
parent e73e290712
commit b29ccb5656
3 changed files with 124 additions and 14 deletions

View File

@ -1289,8 +1289,11 @@ body.more-filters-opened.filters-applied .theme-browser {
margin: 0 5px; margin: 0 5px;
padding: 4px 8px; padding: 4px 8px;
} }
.more-filters-container .filtering-by a {
margin-left: 10px;
}
body.filters-applied .more-filters-container .filters-group, body.filters-applied .more-filters-container .filters-group,
body.filters-applied .more-filters-container a, body.filters-applied .more-filters-container a.button,
body.filters-applied .more-filters-container br { body.filters-applied .more-filters-container br {
display: none !important; display: none !important;
} }
@ -1509,7 +1512,7 @@ body.full-overlay-active {
margin: 0; margin: 0;
z-index: 10; z-index: 10;
overflow: auto; overflow: auto;
background: transparent; background: #eee;
border-right: none; border-right: none;
} }
@ -1672,6 +1675,16 @@ body.full-overlay-active {
display: none; display: none;
} }
.wp-full-overlay .theme-navigation {
padding: 10px 20px;
position: absolute;
bottom: 10px;
text-align: left;
}
.wp-full-overlay .theme-navigation .next-theme {
float: right;
}
/* Animations */ /* Animations */
.wp-full-overlay, .wp-full-overlay,
.wp-full-overlay-sidebar, .wp-full-overlay-sidebar,
@ -1750,7 +1763,7 @@ body.full-overlay-active {
.install-theme-info { .install-theme-info {
display: none; display: none;
padding: 10px 20px 20px; padding: 10px 20px 60px;
} }
.single-theme .install-theme-info { .single-theme .install-theme-info {

View File

@ -215,6 +215,8 @@ themes.Collection = Backbone.Collection.extend({
return collection; return collection;
}, },
count: false,
// Handles requests for more themes // Handles requests for more themes
// and caches results // and caches results
// //
@ -229,6 +231,10 @@ themes.Collection = Backbone.Collection.extend({
self = this, self = this,
query, isPaginated, count; query, isPaginated, count;
// Store current query request args
// for later use with the event `theme:end`
this.currentQuery.request = request;
// Search the query cache for matches. // Search the query cache for matches.
query = _.find( queries, function( query ) { query = _.find( queries, function( query ) {
return _.isEqual( query.request, request ); return _.isEqual( query.request, request );
@ -244,7 +250,7 @@ themes.Collection = Backbone.Collection.extend({
} }
// Otherwise, send a new API call and add it to the cache. // Otherwise, send a new API call and add it to the cache.
if ( ! query ) { if ( ! query && ! isPaginated ) {
query = this.apiCall( request ).done( function( data ) { query = this.apiCall( request ).done( function( data ) {
// Update the collection with the queried data. // Update the collection with the queried data.
self.reset( data.themes ); self.reset( data.themes );
@ -260,7 +266,7 @@ themes.Collection = Backbone.Collection.extend({
} }
// Store the results and the query request // Store the results and the query request
queries.push( { themes: data.themes, request: request } ); queries.push( { themes: data.themes, request: request, total: count } );
}).fail( function() { }).fail( function() {
self.trigger( 'query:fail' ); self.trigger( 'query:fail' );
}); });
@ -289,6 +295,14 @@ themes.Collection = Backbone.Collection.extend({
// Only trigger an update event since we already have the themes // Only trigger an update event since we already have the themes
// on our cached object // on our cached object
if ( _.isNumber( query.total ) ) {
this.count = query.total;
}
if ( ! query.total ) {
this.count = this.length;
}
this.reset( query.themes ); this.reset( query.themes );
this.trigger( 'update' ); this.trigger( 'update' );
} }
@ -305,9 +319,6 @@ themes.Collection = Backbone.Collection.extend({
// Send Ajax POST request to api.wordpress.org/themes // Send Ajax POST request to api.wordpress.org/themes
apiCall: function( request, paginated ) { apiCall: function( request, paginated ) {
// Store current query request args
// for later use with the event `theme:end`
this.currentQuery.request = request;
// Ajax request to .org API // Ajax request to .org API
return $.ajax({ return $.ajax({
@ -441,6 +452,9 @@ themes.view.Theme = wp.Backbone.View.extend({
}, },
preview: function( event ) { preview: function( event ) {
var self = this,
current;
// Bail if the user scrolled on a touch device // Bail if the user scrolled on a touch device
if ( this.touchDrag === true ) { if ( this.touchDrag === true ) {
return this.touchDrag = false; return this.touchDrag = false;
@ -460,12 +474,74 @@ themes.view.Theme = wp.Backbone.View.extend({
event = event || window.event; event = event || window.event;
// Set focus to current theme.
themes.focusedTheme = this.$el;
// Construct a new Preview view.
var preview = new themes.view.Preview({ var preview = new themes.view.Preview({
model: this.model model: this.model
}); });
// Render the view and append it.
preview.render(); preview.render();
$( 'div.wrap' ).append( preview.el ); $( 'div.wrap' ).append( preview.el );
// Listen to our preview object
// for `theme:next` and `theme:previous` events.
this.listenTo( preview, 'theme:next', function() {
// Keep local track of current theme model.
current = self.model;
// If we have ventured away from current model update the current model position.
if ( ! _.isUndefined( self.current ) ) {
current = self.current;
}
// Get previous theme model.
self.current = self.model.collection.at( self.model.collection.indexOf( current ) + 1 );
// If we have no more themes, bail.
if ( _.isUndefined( self.current ) ) {
return self.current = current;
}
// Construct a new Preview view.
preview = new themes.view.Preview({
model: self.current
});
// Render and append.
preview.render();
$( 'div.wrap' ).append( preview.el );
})
.listenTo( preview, 'theme:previous', function() {
// Keep track of current theme model.
current = self.model;
// If we have ventured away from current model update the current model position.
if ( ! _.isUndefined( self.current ) ) {
current = self.current;
}
// Get previous theme model.
self.current = self.model.collection.at( self.model.collection.indexOf( current ) - 1 );
// If we have no more themes, bail.
if ( _.isUndefined( self.current ) ) {
return;
}
// Construct a new Preview view.
preview = new themes.view.Preview({
model: self.current
});
// Render and append.
preview.render();
$( 'div.wrap' ).append( preview.el );
});
} }
}); });
@ -633,14 +709,16 @@ themes.view.Details = wp.Backbone.View.extend({
// Theme Preview view // Theme Preview view
// Set ups a modal overlay with the expanded theme data // Set ups a modal overlay with the expanded theme data
themes.view.Preview = wp.Backbone.View.extend({ themes.view.Preview = themes.view.Details.extend({
className: 'wp-full-overlay expanded', className: 'wp-full-overlay expanded',
el: '#theme-installer', el: '#theme-installer',
events: { events: {
'click .close-full-overlay': 'close', 'click .close-full-overlay': 'close',
'click .collapse-sidebar': 'collapse' 'click .collapse-sidebar': 'collapse',
'click .previous-theme': 'previousTheme',
'click .next-theme': 'nextTheme'
}, },
// The HTML template for the theme preview // The HTML template for the theme preview
@ -654,12 +732,18 @@ themes.view.Preview = wp.Backbone.View.extend({
this.$el.fadeIn( 200, function() { this.$el.fadeIn( 200, function() {
$( 'body' ).addClass( 'theme-installer-active full-overlay-active' ); $( 'body' ).addClass( 'theme-installer-active full-overlay-active' );
$( '.close-full-overlay' ).focus();
}); });
}, },
close: function() { close: function() {
this.$el.fadeOut( 200, function() { this.$el.fadeOut( 200, function() {
$( 'body' ).removeClass( 'theme-installer-active full-overlay-active' ); $( 'body' ).removeClass( 'theme-installer-active full-overlay-active' );
// Return focus to the theme div
if ( themes.focusedTheme ) {
themes.focusedTheme.focus();
}
}); });
themes.router.navigate( themes.router.baseUrl( '' ) ); themes.router.navigate( themes.router.baseUrl( '' ) );
@ -782,7 +866,7 @@ themes.view.Themes = wp.Backbone.View.extend({
} }
// Display a live theme count for the collection // Display a live theme count for the collection
this.count.text( this.collection.length ); this.count.text( this.collection.count ? this.collection.count : this.collection.length );
}, },
// Iterates through each instance of the collection // Iterates through each instance of the collection
@ -1143,6 +1227,9 @@ themes.view.InstallerSearch = themes.view.Search.extend({
request.tag = [ value.slice( 4 ) ]; request.tag = [ value.slice( 4 ) ];
} }
$( '.theme-section.current' ).removeClass( 'current' );
$( 'body' ).removeClass( 'more-filters-opened filters-applied' );
// Get the themes by sending Ajax POST request to api.wordpress.org/themes // Get the themes by sending Ajax POST request to api.wordpress.org/themes
// or searching the local cache // or searching the local cache
this.collection.query( request ); this.collection.query( request );
@ -1161,7 +1248,8 @@ themes.view.Installer = themes.view.Appearance.extend({
'click .apply-filters': 'addFilter', 'click .apply-filters': 'addFilter',
'click [type="checkbox"]': 'filtersChecked', 'click [type="checkbox"]': 'filtersChecked',
'click .clear-filters': 'clearFilters', 'click .clear-filters': 'clearFilters',
'click .feature-name': 'filterSection' 'click .feature-name': 'filterSection',
'click .filtering-by a': 'backToFilters'
}, },
// Handles all the rendering of the public theme directory // Handles all the rendering of the public theme directory
@ -1297,6 +1385,7 @@ themes.view.Installer = themes.view.Appearance.extend({
} }
$( 'body' ).addClass( 'filters-applied' ); $( 'body' ).addClass( 'filters-applied' );
$( '.theme-section.current' ).removeClass( 'current' );
filteringBy.empty(); filteringBy.empty();
_.each( tags, function( tag ) { _.each( tags, function( tag ) {
@ -1355,7 +1444,7 @@ themes.view.Installer = themes.view.Appearance.extend({
event.preventDefault(); event.preventDefault();
if ( $( 'body' ).hasClass( 'filters-applied' ) ) { if ( $( 'body' ).hasClass( 'filters-applied' ) ) {
return $( 'body' ).removeClass( 'filters-applied' ); return this.backToFilters();
} }
// If the filters section is opened and filters are checked // If the filters section is opened and filters are checked
@ -1384,6 +1473,10 @@ themes.view.Installer = themes.view.Appearance.extend({
$( item ).prop( 'checked', false ); $( item ).prop( 'checked', false );
return self.filtersChecked(); return self.filtersChecked();
}); });
},
backToFilters: function() {
$( 'body' ).removeClass( 'filters-applied' );
} }
}); });

View File

@ -145,11 +145,11 @@ include(ABSPATH . 'wp-admin/admin-header.php');
<div class="filtering-by"> <div class="filtering-by">
<span><?php _e( 'Filtering by:' ); ?></span> <span><?php _e( 'Filtering by:' ); ?></span>
<div class="tags"></div> <div class="tags"></div>
<a href="#"><?php _e( 'Edit' ); ?></a>
</div> </div>
</div> </div>
</div> </div>
<div class="theme-browser"></div> <div class="theme-browser"></div>
<div class="theme-overlay"></div>
<div id="theme-installer" class="wp-full-overlay expanded"></div> <div id="theme-installer" class="wp-full-overlay expanded"></div>
<p class="no-themes"><?php _e( 'No themes found. Try a different search.' ); ?></p> <p class="no-themes"><?php _e( 'No themes found. Try a different search.' ); ?></p>
@ -224,6 +224,10 @@ if ( $tab ) {
<span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span> <span class="collapse-sidebar-label"><?php _e( 'Collapse' ); ?></span>
<span class="collapse-sidebar-arrow"></span> <span class="collapse-sidebar-arrow"></span>
</a> </a>
<div class="theme-navigation">
<a class="previous-theme button" href="#"><?php _e( 'Previous' ); ?></a>
<a class="next-theme button" href="#"><?php _e( 'Next' ); ?></a>
</div>
</div> </div>
</div> </div>
<div class="wp-full-overlay-main"> <div class="wp-full-overlay-main">