Media: library grid view - improve browser history support.
Set view state properly when navigating history using the browser back/next button in the media library (grid view). Correctly handle navigating, search, image detail view and image edit mode. Also handle bookmarking/reloading. Props kucrut, joemcgill, afercia. Fixes #31846. git-svn-id: https://develop.svn.wordpress.org/trunk@41021 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
34c3d7d044
commit
2d5a59194d
@ -50,8 +50,10 @@ media.view.DeleteSelectedPermanentlyButton = require( './views/button/delete-sel
|
||||
*/
|
||||
var Router = Backbone.Router.extend({
|
||||
routes: {
|
||||
'upload.php?item=:slug': 'showItem',
|
||||
'upload.php?search=:query': 'search'
|
||||
'upload.php?item=:slug&mode=edit': 'editItem',
|
||||
'upload.php?item=:slug': 'showItem',
|
||||
'upload.php?search=:query': 'search',
|
||||
'upload.php': 'reset'
|
||||
},
|
||||
|
||||
// Map routes against the page URL
|
||||
@ -59,6 +61,14 @@ var Router = Backbone.Router.extend({
|
||||
return 'upload.php' + url;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
var frame = wp.media.frames.edit;
|
||||
|
||||
if ( frame ) {
|
||||
frame.close();
|
||||
}
|
||||
},
|
||||
|
||||
// Respond to the search route by filling the search field and trigggering the input event
|
||||
search: function( query ) {
|
||||
jQuery( '#media-search-input' ).val( query ).trigger( 'input' );
|
||||
@ -67,21 +77,30 @@ var Router = Backbone.Router.extend({
|
||||
// Show the modal with a specific item
|
||||
showItem: function( query ) {
|
||||
var media = wp.media,
|
||||
library = media.frame.state().get('library'),
|
||||
frame = media.frames.browse,
|
||||
library = frame.state().get('library'),
|
||||
item;
|
||||
|
||||
// Trigger the media frame to open the correct item
|
||||
item = library.findWhere( { id: parseInt( query, 10 ) } );
|
||||
item.set( 'skipHistory', true );
|
||||
|
||||
if ( item ) {
|
||||
media.frame.trigger( 'edit:attachment', item );
|
||||
frame.trigger( 'edit:attachment', item );
|
||||
} else {
|
||||
item = media.attachment( query );
|
||||
media.frame.listenTo( item, 'change', function( model ) {
|
||||
media.frame.stopListening( item );
|
||||
media.frame.trigger( 'edit:attachment', model );
|
||||
frame.listenTo( item, 'change', function( model ) {
|
||||
frame.stopListening( item );
|
||||
frame.trigger( 'edit:attachment', model );
|
||||
} );
|
||||
item.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
// Show the modal in edit mode with a specific item.
|
||||
editItem: function( query ) {
|
||||
this.showItem( query );
|
||||
wp.media.frames.edit.content.mode( 'edit-details' );
|
||||
}
|
||||
});
|
||||
|
||||
@ -107,8 +126,16 @@ var Details = wp.media.view.Attachment.Details,
|
||||
TwoColumn = Details.extend({
|
||||
template: wp.template( 'attachment-details-two-column' ),
|
||||
|
||||
initialize: function() {
|
||||
this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) );
|
||||
|
||||
Details.prototype.initialize.apply( this, arguments );
|
||||
},
|
||||
|
||||
editAttachment: function( event ) {
|
||||
event.preventDefault();
|
||||
if ( event ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.controller.content.mode( 'edit-image' );
|
||||
},
|
||||
|
||||
@ -401,13 +428,19 @@ EditAttachments = MediaFrame.extend({
|
||||
// Bind default title creation.
|
||||
this.on( 'title:create:default', this.createTitle, this );
|
||||
|
||||
// Close the modal if the attachment is deleted.
|
||||
this.listenTo( this.model, 'change:status destroy', this.close, this );
|
||||
|
||||
this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
|
||||
this.on( 'content:create:edit-image', this.editImageMode, this );
|
||||
this.on( 'content:render:edit-image', this.editImageModeRender, this );
|
||||
this.on( 'refresh', this.rerender, this );
|
||||
this.on( 'close', this.detach );
|
||||
|
||||
this.bindModelHandlers();
|
||||
this.listenTo( this.gridRouter, 'route:search', this.close, this );
|
||||
},
|
||||
|
||||
bindModelHandlers: function() {
|
||||
// Close the modal if the attachment is deleted.
|
||||
this.listenTo( this.model, 'change:status destroy', this.close, this );
|
||||
},
|
||||
|
||||
createModal: function() {
|
||||
@ -424,7 +457,6 @@ EditAttachments = MediaFrame.extend({
|
||||
|
||||
// Completely destroy the modal DOM element when closing it.
|
||||
this.modal.on( 'close', _.bind( function() {
|
||||
this.modal.remove();
|
||||
$( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
|
||||
// Restore the original focus item if possible
|
||||
$( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).focus();
|
||||
@ -442,7 +474,10 @@ EditAttachments = MediaFrame.extend({
|
||||
*/
|
||||
createStates: function() {
|
||||
this.states.add([
|
||||
new wp.media.controller.EditAttachmentMetadata( { model: this.model } )
|
||||
new wp.media.controller.EditAttachmentMetadata({
|
||||
model: this.model,
|
||||
library: this.library
|
||||
})
|
||||
]);
|
||||
},
|
||||
|
||||
@ -467,8 +502,8 @@ EditAttachments = MediaFrame.extend({
|
||||
model: this.model
|
||||
}) );
|
||||
|
||||
// Update browser url when navigating media details
|
||||
if ( this.model ) {
|
||||
// Update browser url when navigating media details, except on load.
|
||||
if ( this.model && ! this.model.get( 'skipHistory' ) ) {
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) );
|
||||
}
|
||||
},
|
||||
@ -494,6 +529,9 @@ EditAttachments = MediaFrame.extend({
|
||||
frame: this,
|
||||
controller: editImageController
|
||||
} );
|
||||
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) );
|
||||
|
||||
},
|
||||
|
||||
editImageModeRender: function( view ) {
|
||||
@ -508,7 +546,13 @@ EditAttachments = MediaFrame.extend({
|
||||
/**
|
||||
* Rerender the view.
|
||||
*/
|
||||
rerender: function() {
|
||||
rerender: function( model ) {
|
||||
this.stopListening( this.model );
|
||||
|
||||
this.model = model;
|
||||
|
||||
this.bindModelHandlers();
|
||||
|
||||
// Only rerender the `content` region.
|
||||
if ( this.content.mode() !== 'edit-metadata' ) {
|
||||
this.content.mode( 'edit-metadata' );
|
||||
@ -527,8 +571,7 @@ EditAttachments = MediaFrame.extend({
|
||||
this.$( '.left' ).blur();
|
||||
return;
|
||||
}
|
||||
this.model = this.library.at( this.getCurrentIndex() - 1 );
|
||||
this.rerender();
|
||||
this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) );
|
||||
this.$( '.left' ).focus();
|
||||
},
|
||||
|
||||
@ -540,8 +583,7 @@ EditAttachments = MediaFrame.extend({
|
||||
this.$( '.right' ).blur();
|
||||
return;
|
||||
}
|
||||
this.model = this.library.at( this.getCurrentIndex() + 1 );
|
||||
this.rerender();
|
||||
this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) );
|
||||
this.$( '.right' ).focus();
|
||||
},
|
||||
|
||||
@ -576,7 +618,9 @@ EditAttachments = MediaFrame.extend({
|
||||
},
|
||||
|
||||
resetRoute: function() {
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( '' ) );
|
||||
var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(),
|
||||
url = '' !== searchTerm ? '?search=' + searchTerm : '';
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
|
||||
}
|
||||
});
|
||||
|
||||
@ -666,38 +710,42 @@ Manage = MediaFrame.extend({
|
||||
this.bindRegionModeHandlers();
|
||||
this.render();
|
||||
this.bindSearchHandler();
|
||||
|
||||
wp.media.frames.browse = this;
|
||||
},
|
||||
|
||||
bindSearchHandler: function() {
|
||||
var search = this.$( '#media-search-input' ),
|
||||
currentSearch = this.options.container.data( 'search' ),
|
||||
searchView = this.browserView.toolbar.get( 'search' ).$el,
|
||||
listMode = this.$( '.view-list' ),
|
||||
|
||||
input = _.debounce( function (e) {
|
||||
input = _.throttle( function (e) {
|
||||
var val = $( e.currentTarget ).val(),
|
||||
url = '';
|
||||
|
||||
if ( val ) {
|
||||
url += '?search=' + val;
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
|
||||
}
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( url ) );
|
||||
}, 1000 );
|
||||
|
||||
// Update the URL when entering search string (at most once per second)
|
||||
search.on( 'input', _.bind( input, this ) );
|
||||
searchView.val( currentSearch ).trigger( 'input' );
|
||||
|
||||
this.gridRouter.on( 'route:search', function () {
|
||||
var href = window.location.href;
|
||||
if ( href.indexOf( 'mode=' ) > -1 ) {
|
||||
href = href.replace( /mode=[^&]+/g, 'mode=list' );
|
||||
} else {
|
||||
href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
|
||||
}
|
||||
href = href.replace( 'search=', 's=' );
|
||||
listMode.prop( 'href', href );
|
||||
} );
|
||||
this.gridRouter
|
||||
.on( 'route:search', function () {
|
||||
var href = window.location.href;
|
||||
if ( href.indexOf( 'mode=' ) > -1 ) {
|
||||
href = href.replace( /mode=[^&]+/g, 'mode=list' );
|
||||
} else {
|
||||
href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
|
||||
}
|
||||
href = href.replace( 'search=', 's=' );
|
||||
listMode.prop( 'href', href );
|
||||
})
|
||||
.on( 'route:reset', function() {
|
||||
searchView.val( '' ).trigger( 'input' );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -789,12 +837,16 @@ Manage = MediaFrame.extend({
|
||||
*/
|
||||
openEditAttachmentModal: function( model ) {
|
||||
// Create a new EditAttachment frame, passing along the library and the attachment model.
|
||||
wp.media( {
|
||||
frame: 'edit-attachments',
|
||||
controller: this,
|
||||
library: this.state().get('library'),
|
||||
model: model
|
||||
} );
|
||||
if ( wp.media.frames.edit ) {
|
||||
wp.media.frames.edit.open().trigger( 'refresh', model );
|
||||
} else {
|
||||
wp.media.frames.edit = wp.media( {
|
||||
frame: 'edit-attachments',
|
||||
controller: this,
|
||||
library: this.state().get('library'),
|
||||
model: model
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -8,8 +8,10 @@
|
||||
*/
|
||||
var Router = Backbone.Router.extend({
|
||||
routes: {
|
||||
'upload.php?item=:slug': 'showItem',
|
||||
'upload.php?search=:query': 'search'
|
||||
'upload.php?item=:slug&mode=edit': 'editItem',
|
||||
'upload.php?item=:slug': 'showItem',
|
||||
'upload.php?search=:query': 'search',
|
||||
'upload.php': 'reset'
|
||||
},
|
||||
|
||||
// Map routes against the page URL
|
||||
@ -17,6 +19,14 @@ var Router = Backbone.Router.extend({
|
||||
return 'upload.php' + url;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
var frame = wp.media.frames.edit;
|
||||
|
||||
if ( frame ) {
|
||||
frame.close();
|
||||
}
|
||||
},
|
||||
|
||||
// Respond to the search route by filling the search field and trigggering the input event
|
||||
search: function( query ) {
|
||||
jQuery( '#media-search-input' ).val( query ).trigger( 'input' );
|
||||
@ -25,21 +35,30 @@ var Router = Backbone.Router.extend({
|
||||
// Show the modal with a specific item
|
||||
showItem: function( query ) {
|
||||
var media = wp.media,
|
||||
library = media.frame.state().get('library'),
|
||||
frame = media.frames.browse,
|
||||
library = frame.state().get('library'),
|
||||
item;
|
||||
|
||||
// Trigger the media frame to open the correct item
|
||||
item = library.findWhere( { id: parseInt( query, 10 ) } );
|
||||
item.set( 'skipHistory', true );
|
||||
|
||||
if ( item ) {
|
||||
media.frame.trigger( 'edit:attachment', item );
|
||||
frame.trigger( 'edit:attachment', item );
|
||||
} else {
|
||||
item = media.attachment( query );
|
||||
media.frame.listenTo( item, 'change', function( model ) {
|
||||
media.frame.stopListening( item );
|
||||
media.frame.trigger( 'edit:attachment', model );
|
||||
frame.listenTo( item, 'change', function( model ) {
|
||||
frame.stopListening( item );
|
||||
frame.trigger( 'edit:attachment', model );
|
||||
} );
|
||||
item.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
// Show the modal in edit mode with a specific item.
|
||||
editItem: function( query ) {
|
||||
this.showItem( query );
|
||||
wp.media.frames.edit.content.mode( 'edit-details' );
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -17,8 +17,16 @@ var Details = wp.media.view.Attachment.Details,
|
||||
TwoColumn = Details.extend({
|
||||
template: wp.template( 'attachment-details-two-column' ),
|
||||
|
||||
initialize: function() {
|
||||
this.controller.on( 'content:activate:edit-details', _.bind( this.editAttachment, this ) );
|
||||
|
||||
Details.prototype.initialize.apply( this, arguments );
|
||||
},
|
||||
|
||||
editAttachment: function( event ) {
|
||||
event.preventDefault();
|
||||
if ( event ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.controller.content.mode( 'edit-image' );
|
||||
},
|
||||
|
||||
|
@ -59,13 +59,19 @@ EditAttachments = MediaFrame.extend({
|
||||
// Bind default title creation.
|
||||
this.on( 'title:create:default', this.createTitle, this );
|
||||
|
||||
// Close the modal if the attachment is deleted.
|
||||
this.listenTo( this.model, 'change:status destroy', this.close, this );
|
||||
|
||||
this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
|
||||
this.on( 'content:create:edit-image', this.editImageMode, this );
|
||||
this.on( 'content:render:edit-image', this.editImageModeRender, this );
|
||||
this.on( 'refresh', this.rerender, this );
|
||||
this.on( 'close', this.detach );
|
||||
|
||||
this.bindModelHandlers();
|
||||
this.listenTo( this.gridRouter, 'route:search', this.close, this );
|
||||
},
|
||||
|
||||
bindModelHandlers: function() {
|
||||
// Close the modal if the attachment is deleted.
|
||||
this.listenTo( this.model, 'change:status destroy', this.close, this );
|
||||
},
|
||||
|
||||
createModal: function() {
|
||||
@ -82,7 +88,6 @@ EditAttachments = MediaFrame.extend({
|
||||
|
||||
// Completely destroy the modal DOM element when closing it.
|
||||
this.modal.on( 'close', _.bind( function() {
|
||||
this.modal.remove();
|
||||
$( 'body' ).off( 'keydown.media-modal' ); /* remove the keydown event */
|
||||
// Restore the original focus item if possible
|
||||
$( 'li.attachment[data-id="' + this.model.get( 'id' ) +'"]' ).focus();
|
||||
@ -100,7 +105,10 @@ EditAttachments = MediaFrame.extend({
|
||||
*/
|
||||
createStates: function() {
|
||||
this.states.add([
|
||||
new wp.media.controller.EditAttachmentMetadata( { model: this.model } )
|
||||
new wp.media.controller.EditAttachmentMetadata({
|
||||
model: this.model,
|
||||
library: this.library
|
||||
})
|
||||
]);
|
||||
},
|
||||
|
||||
@ -125,8 +133,8 @@ EditAttachments = MediaFrame.extend({
|
||||
model: this.model
|
||||
}) );
|
||||
|
||||
// Update browser url when navigating media details
|
||||
if ( this.model ) {
|
||||
// Update browser url when navigating media details, except on load.
|
||||
if ( this.model && ! this.model.get( 'skipHistory' ) ) {
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id ) );
|
||||
}
|
||||
},
|
||||
@ -152,6 +160,9 @@ EditAttachments = MediaFrame.extend({
|
||||
frame: this,
|
||||
controller: editImageController
|
||||
} );
|
||||
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( '?item=' + this.model.id + '&mode=edit' ) );
|
||||
|
||||
},
|
||||
|
||||
editImageModeRender: function( view ) {
|
||||
@ -166,7 +177,13 @@ EditAttachments = MediaFrame.extend({
|
||||
/**
|
||||
* Rerender the view.
|
||||
*/
|
||||
rerender: function() {
|
||||
rerender: function( model ) {
|
||||
this.stopListening( this.model );
|
||||
|
||||
this.model = model;
|
||||
|
||||
this.bindModelHandlers();
|
||||
|
||||
// Only rerender the `content` region.
|
||||
if ( this.content.mode() !== 'edit-metadata' ) {
|
||||
this.content.mode( 'edit-metadata' );
|
||||
@ -185,8 +202,7 @@ EditAttachments = MediaFrame.extend({
|
||||
this.$( '.left' ).blur();
|
||||
return;
|
||||
}
|
||||
this.model = this.library.at( this.getCurrentIndex() - 1 );
|
||||
this.rerender();
|
||||
this.trigger( 'refresh', this.library.at( this.getCurrentIndex() - 1 ) );
|
||||
this.$( '.left' ).focus();
|
||||
},
|
||||
|
||||
@ -198,8 +214,7 @@ EditAttachments = MediaFrame.extend({
|
||||
this.$( '.right' ).blur();
|
||||
return;
|
||||
}
|
||||
this.model = this.library.at( this.getCurrentIndex() + 1 );
|
||||
this.rerender();
|
||||
this.trigger( 'refresh', this.library.at( this.getCurrentIndex() + 1 ) );
|
||||
this.$( '.right' ).focus();
|
||||
},
|
||||
|
||||
@ -234,7 +249,9 @@ EditAttachments = MediaFrame.extend({
|
||||
},
|
||||
|
||||
resetRoute: function() {
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( '' ) );
|
||||
var searchTerm = this.controller.browserView.toolbar.get( 'search' ).$el.val(),
|
||||
url = '' !== searchTerm ? '?search=' + searchTerm : '';
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -81,38 +81,42 @@ Manage = MediaFrame.extend({
|
||||
this.bindRegionModeHandlers();
|
||||
this.render();
|
||||
this.bindSearchHandler();
|
||||
|
||||
wp.media.frames.browse = this;
|
||||
},
|
||||
|
||||
bindSearchHandler: function() {
|
||||
var search = this.$( '#media-search-input' ),
|
||||
currentSearch = this.options.container.data( 'search' ),
|
||||
searchView = this.browserView.toolbar.get( 'search' ).$el,
|
||||
listMode = this.$( '.view-list' ),
|
||||
|
||||
input = _.debounce( function (e) {
|
||||
input = _.throttle( function (e) {
|
||||
var val = $( e.currentTarget ).val(),
|
||||
url = '';
|
||||
|
||||
if ( val ) {
|
||||
url += '?search=' + val;
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( url ), { replace: true } );
|
||||
}
|
||||
this.gridRouter.navigate( this.gridRouter.baseUrl( url ) );
|
||||
}, 1000 );
|
||||
|
||||
// Update the URL when entering search string (at most once per second)
|
||||
search.on( 'input', _.bind( input, this ) );
|
||||
searchView.val( currentSearch ).trigger( 'input' );
|
||||
|
||||
this.gridRouter.on( 'route:search', function () {
|
||||
var href = window.location.href;
|
||||
if ( href.indexOf( 'mode=' ) > -1 ) {
|
||||
href = href.replace( /mode=[^&]+/g, 'mode=list' );
|
||||
} else {
|
||||
href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
|
||||
}
|
||||
href = href.replace( 'search=', 's=' );
|
||||
listMode.prop( 'href', href );
|
||||
} );
|
||||
this.gridRouter
|
||||
.on( 'route:search', function () {
|
||||
var href = window.location.href;
|
||||
if ( href.indexOf( 'mode=' ) > -1 ) {
|
||||
href = href.replace( /mode=[^&]+/g, 'mode=list' );
|
||||
} else {
|
||||
href += href.indexOf( '?' ) > -1 ? '&mode=list' : '?mode=list';
|
||||
}
|
||||
href = href.replace( 'search=', 's=' );
|
||||
listMode.prop( 'href', href );
|
||||
})
|
||||
.on( 'route:reset', function() {
|
||||
searchView.val( '' ).trigger( 'input' );
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -204,12 +208,16 @@ Manage = MediaFrame.extend({
|
||||
*/
|
||||
openEditAttachmentModal: function( model ) {
|
||||
// Create a new EditAttachment frame, passing along the library and the attachment model.
|
||||
wp.media( {
|
||||
frame: 'edit-attachments',
|
||||
controller: this,
|
||||
library: this.state().get('library'),
|
||||
model: model
|
||||
} );
|
||||
if ( wp.media.frames.edit ) {
|
||||
wp.media.frames.edit.open().trigger( 'refresh', model );
|
||||
} else {
|
||||
wp.media.frames.edit = wp.media( {
|
||||
frame: 'edit-attachments',
|
||||
controller: this,
|
||||
library: this.state().get('library'),
|
||||
model: model
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user