Media grid, round 2. Expect much more to come.

* Instead of a sidebar for details, utilize a modal. The modal experience allows for a larger preview, editing images, audio/video previews, and previous/next navigation, like the theme browser. Think of it as an attachment browser.
* Show some details in the grid view to more easily distinguish items.

props ericlewis, wonderboymusic, JerrySarcastic. see #24716.

Built from https://develop.svn.wordpress.org/trunk@28993


git-svn-id: http://core.svn.wordpress.org/trunk@28781 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Helen Hou-Sandí 2014-07-04 03:39:15 +00:00
parent 0546378ce8
commit 63f9cbd02a
12 changed files with 1297 additions and 69 deletions

View File

@ -24,16 +24,8 @@ if ( 'grid' === $mode ) {
wp_enqueue_media();
wp_enqueue_script( 'media-grid' );
wp_enqueue_script( 'media' );
require_once( ABSPATH . 'wp-admin/admin-header.php' );
?><div class="view-switch media-grid-view-switch">
<a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list">
<img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/>
</a>
<a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current">
<img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/>
</a>
</div><?php
include( ABSPATH . 'wp-admin/admin-footer.php' );
exit;
}

View File

@ -750,6 +750,30 @@
max-height: 100%;
}
.attachment-preview.type-audio .thumbnail,
.attachment-preview.type-video .thumbnail {
z-index: 1;
margin: 5%;
max-width: 90%;
max-height: 90%;
}
.media-frame-content .attachment-preview.type-audio .icon,
.media-frame-content .attachment-preview.type-video .icon {
z-index: 2;
background: #f1f1f1;
position: relative;
padding: 0;
top: 15%;
right: auto;
left: auto;
}
.attachment-preview.type-audio .filename,
.attachment-preview.type-video .filename {
z-index: 3;
}
.attachment-preview .thumbnail:after {
content: '';
display: block;
@ -909,6 +933,22 @@
border-radius: 0;
}
.attachment .data-fields {
margin: 5px 0 0;
}
.attachment .data-field {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: block;
line-height: 19px;
height: 19px;
text-align: right;
width: 90%;
margin: 0 5%;
}
/**
* Attachments Browser
*/
@ -924,6 +964,10 @@
height: 50px;
}
.attachments-browser.hide-sidebar .media-toolbar {
left: 0;
}
.attachments-browser .media-toolbar-primary > .media-button,
.attachments-browser .media-toolbar-primary > .media-button-group,
.attachments-browser .media-toolbar-secondary > .media-button,
@ -942,6 +986,92 @@
outline: none;
}
.inline-toolbar {
position: absolute;
top: 0;
right: 0;
display: none;
z-index: 100;
}
.inline-toolbar .remove {
display: none;
}
.active-video .inline-toolbar .remove {
display: inline-block;
}
.attachment:hover .inline-toolbar {
display: block;
}
.inline-toolbar div,
.inline-toolbar .inline-media-control {
display: inline-block;
margin-top: 4px;
margin-right: 4px;
padding: 2px;
width: 20px;
height: 20px;
box-shadow: 0 1px 3px rgba(0,0,0,0.5);
background-color: #000;
background-color: rgba(0,0,0,0.9);
cursor: pointer;
color: white;
font-size: 20px;
}
.ie8 .inline-toolbar div,
.ie7 .inline-toolbar div {
display: inline;
padding: 0;
}
.inline-media-control span {
display: block;
width: 16px;
height: 16px;
margin: 2px;
background: url(/wp-includes/js/mediaelement/controls.png) 0 0 no-repeat;
}
.inline-media-control.active span {
margin: 2px;
background-position: 0 -16px;
}
.inline-media-control.paused span {
margin: 2px;
background-position: 0 0;
}
audio#inline-media-node {
display: none;
}
video#inline-media-node {
position: relative;
z-index: 5;
top: 0;
right: 0;
}
.inline-video-wrap {
width: 100%;
height: auto;
position: absolute;
z-index: 5;
background: #000;
padding: 10px 0 5px;
top: 0;
right: 0;
}
.attachments-browser.hide-sidebar .attachments {
left: 0;
}
.attachments-browser .instructions {
display: inline-block;
margin-top: 16px;
@ -2388,11 +2518,11 @@
line-height: 29px;
}
.media-grid-view-switch {
position: fixed;
left: 10px;
top: 44px;
z-index: 300;
.media-grid-view .view-switch {
display: inline-block;
float: none;
margin-top: 13px;
vertical-align: middle;
}
/**
@ -2427,7 +2557,221 @@
display: none;
}
/**
* Copied styles from the Add theme toolbar.
*
* This should be OOCSS'd so both use a shared selector.
*/
.media-grid-view .media-toolbar {
background: #fff;
-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
color: #555;
display: inline-block;
font-size: 13px;
padding: 0 20px;
position: relative;
width: 100%;
}
/**
* The left and right buttons are copied from the expanded theme details modal.
*
* This should be OOCSS'd so both use a shared selector.
*/
.edit-attachment-frame .edit-media-header .left,
.edit-attachment-frame .edit-media-header .right {
cursor: pointer;
color: #777;
background-color: transparent;
height: 48px;
width: 54px;
float: right;
text-align: center;
border: 0;
border-left: 1px solid #ddd;
}
.edit-attachment-frame .edit-media-header .right:before,
.edit-attachment-frame .edit-media-header .left:before {
font: normal 20px/50px 'dashicons' !important;
display: inline;
font-weight: 300;
}
.edit-attachment-frame .edit-media-header .left:before {
content: '\f340';
}
.edit-attachment-frame .edit-media-header .right:before {
content: '\f344';
}
.edit-attachment-frame .edit-media-header .left.disabled,
.edit-attachment-frame .edit-media-header .right.disabled,
.edit-attachment-frame .edit-media-header .left.disabled:hover,
.edit-attachment-frame .edit-media-header .right.disabled:hover {
color: #ccc;
background: inherit;
cursor: inherit;
}
.edit-attachment-frame .edit-media-header .close:hover,
.edit-attachment-frame .edit-media-header .right:hover,
.edit-attachment-frame .edit-media-header .left:hover,
.edit-attachment-frame .edit-media-header .close:focus,
.edit-attachment-frame .edit-media-header .right:focus,
.edit-attachment-frame .edit-media-header .left:focus {
background: #0074a2;
color: #fff;
}
.edit-attachment-frame .media-frame-content,
.edit-attachment-frame .media-frame-router {
right: 0;
}
/* Hiding this for the moment instead of removing it from the template. */
.edit-attachment-frame h3 {
display: none;
}
.edit-attachment-frame .attachment-details {
position: absolute;
overflow: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.edit-attachment-frame .attachment-info {
border-bottom: 0;
border-left: 1px solid #ddd;
bottom: 0;
position: absolute;
top: 0;
right: 0;
margin-bottom: 0;
padding: 2% 4%;
left: 50%;
}
.edit-attachment-frame .attachment-info .thumbnail {
max-width: none;
max-height: none;
}
.edit-attachment-frame .attachment-info .thumbnail-image img {
margin: 0;
}
.edit-attachment-frame .attachment-info .thumbnail-image:after {
-webkit-box-shadow: none;
box-shadow: none;
}
.edit-attachment-frame .attachment-info .thumbnail img {
max-width: none;
max-height: 50%;
}
.edit-attachment-frame .attachment-info .details {
float: none;
}
.edit-attachment-frame .wp-media-wrapper {
margin-top: 20px;
}
.edit-attachment-frame .attachment-fields {
bottom: 0;
padding: 2% 4%;
position: absolute;
top: 0;
right: 50%;
left: 0;
}
.edit-attachment-frame .attachment-fields .setting {
display: block;
float: right;
width: 100%;
margin: 1px 0;
}
.edit-attachment-frame .attachment-fields .setting label {
display: block;
}
.edit-attachment-frame .attachment-fields .setting .link-to-custom {
margin: 3px 0;
}
.edit-attachment-frame .attachment-fields .setting .name {
min-width: 30%;
margin-left: 4%;
font-size: 12px;
text-align: left;
}
.edit-attachment-frame .attachment-fields .setting select {
max-width: 65%;
}
.edit-attachment-frame .attachment-fields .setting input[type="checkbox"],
.edit-attachment-frame .attachment-fields .field input[type="checkbox"] {
width: 16px;
float: none;
margin: 8px 3px 0;
padding: 0;
}
.edit-attachment-frame .attachment-fields .setting span {
float: right;
min-height: 22px;
padding-top: 8px;
line-height: 16px;
font-weight: normal;
color: #666;
}
.edit-attachment-frame .attachment-fields .setting input[type="text"],
.edit-attachment-frame .attachment-fields .setting input[type="password"],
.edit-attachment-frame .attachment-fields .setting input[type="number"],
.edit-attachment-frame .attachment-fields .setting input[type="search"],
.edit-attachment-frame .attachment-fields .setting input[type="email"],
.edit-attachment-frame .attachment-fields .setting input[type="url"],
.edit-attachment-frame .attachment-fields .setting textarea,
.edit-attachment-frame .attachment-fields .setting .value {
margin: 1px;
width: 65%;
float: left;
padding: 6px 8px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.edit-attachment-frame .attachment-fields .setting textarea {
height: 62px;
resize: vertical;
}
.edit-attachment-frame .attachment-fields select {
margin-top: 3px;
}
.media-grid-view.hide-router .media-frame-title {
box-shadow: none;
}
.media-grid-view .media-frame-content {
background-color: transparent;
bottom: 40px;
}
@media screen and (max-width: 782px) {

File diff suppressed because one or more lines are too long

View File

@ -750,6 +750,30 @@
max-height: 100%;
}
.attachment-preview.type-audio .thumbnail,
.attachment-preview.type-video .thumbnail {
z-index: 1;
margin: 5%;
max-width: 90%;
max-height: 90%;
}
.media-frame-content .attachment-preview.type-audio .icon,
.media-frame-content .attachment-preview.type-video .icon {
z-index: 2;
background: #f1f1f1;
position: relative;
padding: 0;
top: 15%;
left: auto;
right: auto;
}
.attachment-preview.type-audio .filename,
.attachment-preview.type-video .filename {
z-index: 3;
}
.attachment-preview .thumbnail:after {
content: '';
display: block;
@ -909,6 +933,22 @@
border-radius: 0;
}
.attachment .data-fields {
margin: 5px 0 0;
}
.attachment .data-field {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: block;
line-height: 19px;
height: 19px;
text-align: left;
width: 90%;
margin: 0 5%;
}
/**
* Attachments Browser
*/
@ -924,6 +964,10 @@
height: 50px;
}
.attachments-browser.hide-sidebar .media-toolbar {
right: 0;
}
.attachments-browser .media-toolbar-primary > .media-button,
.attachments-browser .media-toolbar-primary > .media-button-group,
.attachments-browser .media-toolbar-secondary > .media-button,
@ -942,6 +986,92 @@
outline: none;
}
.inline-toolbar {
position: absolute;
top: 0;
left: 0;
display: none;
z-index: 100;
}
.inline-toolbar .remove {
display: none;
}
.active-video .inline-toolbar .remove {
display: inline-block;
}
.attachment:hover .inline-toolbar {
display: block;
}
.inline-toolbar div,
.inline-toolbar .inline-media-control {
display: inline-block;
margin-top: 4px;
margin-left: 4px;
padding: 2px;
width: 20px;
height: 20px;
box-shadow: 0 1px 3px rgba(0,0,0,0.5);
background-color: #000;
background-color: rgba(0,0,0,0.9);
cursor: pointer;
color: white;
font-size: 20px;
}
.ie8 .inline-toolbar div,
.ie7 .inline-toolbar div {
display: inline;
padding: 0;
}
.inline-media-control span {
display: block;
width: 16px;
height: 16px;
margin: 2px;
background: url(/wp-includes/js/mediaelement/controls.png) 0 0 no-repeat;
}
.inline-media-control.active span {
margin: 2px;
background-position: 0 -16px;
}
.inline-media-control.paused span {
margin: 2px;
background-position: 0 0;
}
audio#inline-media-node {
display: none;
}
video#inline-media-node {
position: relative;
z-index: 5;
top: 0;
left: 0;
}
.inline-video-wrap {
width: 100%;
height: auto;
position: absolute;
z-index: 5;
background: #000;
padding: 10px 0 5px;
top: 0;
left: 0;
}
.attachments-browser.hide-sidebar .attachments {
right: 0;
}
.attachments-browser .instructions {
display: inline-block;
margin-top: 16px;
@ -2388,11 +2518,11 @@
line-height: 29px;
}
.media-grid-view-switch {
position: fixed;
right: 10px;
top: 44px;
z-index: 300;
.media-grid-view .view-switch {
display: inline-block;
float: none;
margin-top: 13px;
vertical-align: middle;
}
/**
@ -2427,7 +2557,221 @@
display: none;
}
/**
* Copied styles from the Add theme toolbar.
*
* This should be OOCSS'd so both use a shared selector.
*/
.media-grid-view .media-toolbar {
background: #fff;
-webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
color: #555;
display: inline-block;
font-size: 13px;
padding: 0 20px;
position: relative;
width: 100%;
}
/**
* The left and right buttons are copied from the expanded theme details modal.
*
* This should be OOCSS'd so both use a shared selector.
*/
.edit-attachment-frame .edit-media-header .left,
.edit-attachment-frame .edit-media-header .right {
cursor: pointer;
color: #777;
background-color: transparent;
height: 48px;
width: 54px;
float: left;
text-align: center;
border: 0;
border-right: 1px solid #ddd;
}
.edit-attachment-frame .edit-media-header .right:before,
.edit-attachment-frame .edit-media-header .left:before {
font: normal 20px/50px 'dashicons' !important;
display: inline;
font-weight: 300;
}
.edit-attachment-frame .edit-media-header .left:before {
content: '\f340';
}
.edit-attachment-frame .edit-media-header .right:before {
content: '\f344';
}
.edit-attachment-frame .edit-media-header .left.disabled,
.edit-attachment-frame .edit-media-header .right.disabled,
.edit-attachment-frame .edit-media-header .left.disabled:hover,
.edit-attachment-frame .edit-media-header .right.disabled:hover {
color: #ccc;
background: inherit;
cursor: inherit;
}
.edit-attachment-frame .edit-media-header .close:hover,
.edit-attachment-frame .edit-media-header .right:hover,
.edit-attachment-frame .edit-media-header .left:hover,
.edit-attachment-frame .edit-media-header .close:focus,
.edit-attachment-frame .edit-media-header .right:focus,
.edit-attachment-frame .edit-media-header .left:focus {
background: #0074a2;
color: #fff;
}
.edit-attachment-frame .media-frame-content,
.edit-attachment-frame .media-frame-router {
left: 0;
}
/* Hiding this for the moment instead of removing it from the template. */
.edit-attachment-frame h3 {
display: none;
}
.edit-attachment-frame .attachment-details {
position: absolute;
overflow: auto;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
.edit-attachment-frame .attachment-info {
border-bottom: 0;
border-right: 1px solid #ddd;
bottom: 0;
position: absolute;
top: 0;
left: 0;
margin-bottom: 0;
padding: 2% 4%;
right: 50%;
}
.edit-attachment-frame .attachment-info .thumbnail {
max-width: none;
max-height: none;
}
.edit-attachment-frame .attachment-info .thumbnail-image img {
margin: 0;
}
.edit-attachment-frame .attachment-info .thumbnail-image:after {
-webkit-box-shadow: none;
box-shadow: none;
}
.edit-attachment-frame .attachment-info .thumbnail img {
max-width: none;
max-height: 50%;
}
.edit-attachment-frame .attachment-info .details {
float: none;
}
.edit-attachment-frame .wp-media-wrapper {
margin-top: 20px;
}
.edit-attachment-frame .attachment-fields {
bottom: 0;
padding: 2% 4%;
position: absolute;
top: 0;
left: 50%;
right: 0;
}
.edit-attachment-frame .attachment-fields .setting {
display: block;
float: left;
width: 100%;
margin: 1px 0;
}
.edit-attachment-frame .attachment-fields .setting label {
display: block;
}
.edit-attachment-frame .attachment-fields .setting .link-to-custom {
margin: 3px 0;
}
.edit-attachment-frame .attachment-fields .setting .name {
min-width: 30%;
margin-right: 4%;
font-size: 12px;
text-align: right;
}
.edit-attachment-frame .attachment-fields .setting select {
max-width: 65%;
}
.edit-attachment-frame .attachment-fields .setting input[type="checkbox"],
.edit-attachment-frame .attachment-fields .field input[type="checkbox"] {
width: 16px;
float: none;
margin: 8px 3px 0;
padding: 0;
}
.edit-attachment-frame .attachment-fields .setting span {
float: left;
min-height: 22px;
padding-top: 8px;
line-height: 16px;
font-weight: normal;
color: #666;
}
.edit-attachment-frame .attachment-fields .setting input[type="text"],
.edit-attachment-frame .attachment-fields .setting input[type="password"],
.edit-attachment-frame .attachment-fields .setting input[type="number"],
.edit-attachment-frame .attachment-fields .setting input[type="search"],
.edit-attachment-frame .attachment-fields .setting input[type="email"],
.edit-attachment-frame .attachment-fields .setting input[type="url"],
.edit-attachment-frame .attachment-fields .setting textarea,
.edit-attachment-frame .attachment-fields .setting .value {
margin: 1px;
width: 65%;
float: right;
padding: 6px 8px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.edit-attachment-frame .attachment-fields .setting textarea {
height: 62px;
resize: vertical;
}
.edit-attachment-frame .attachment-fields select {
margin-top: 3px;
}
.media-grid-view.hide-router .media-frame-title {
box-shadow: none;
}
.media-grid-view .media-frame-content {
background-color: transparent;
bottom: 40px;
}
@media screen and (max-width: 782px) {

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,17 @@
}
},
removeAllPlayers: function() {
var p;
if ( window.mejs && window.mejs.players ) {
for ( p in window.mejs.players ) {
window.mejs.players[p].pause();
this.removePlayer( window.mejs.players[p] );
}
}
},
/**
* Pauses the current object's instances of MediaElementPlayer
*/

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(function( $, _, Backbone, wp ) {
(function($, _, Backbone, wp) {
var media = wp.media, l10n;
// Link any localized strings.
@ -9,6 +9,96 @@
delete l10n.settings;
}
/**
* A state for editing (cropping, etc.) an image.
*
* @constructor
* @augments wp.media.controller.State
* @augments Backbone.Model
*/
media.controller.EditImageNoFrame = media.controller._State.extend({
defaults: {
id: 'edit-attachment',
title: l10n.editImage,
// Region mode defaults.
menu: false,
router: 'edit-metadata',
content: 'edit-metadata',
toolbar: 'toolbar',
url: ''
},
initialize: function() {
media.controller._State.prototype.initialize.apply( this, arguments );
},
activate: function() {
this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
},
_postActivate: function() {
this._content();
this._router();
},
deactivate: function() {
this.stopListening( this.frame );
},
toolbar: function() {
var frame = this.frame,
lastState = frame.lastState(),
previous = lastState && lastState.id;
frame.toolbar.set( new media.view.Toolbar({
controller: frame,
items: {
back: {
style: 'primary',
text: l10n.back,
priority: 20,
click: function() {
if ( previous ) {
frame.setState( previous );
} else {
frame.close();
}
}
}
}
}) );
},
/**
* @access private
*/
_router: function() {
var router = this.frame.router,
mode = this.get('router'),
view;
this.frame.$el.toggleClass( 'hide-router', ! mode );
if ( ! mode ) {
return;
}
this.frame.router.render( mode );
view = router.get();
if ( view && view.select ) {
view.select( this.frame.content.mode() );
}
},
_content: function() {
var mode = this.get( 'content' );
if ( mode ) {
this.frame[ 'content' ].render( mode );
}
}
});
/**
* wp.media.view.MediaFrame.Manage
*
@ -36,7 +126,8 @@
library: {},
multiple: false,
state: 'library',
uploader: true
uploader: true,
mode: [ 'grid', 'edit' ]
});
// Ensure core and media grid view UI is enabled.
@ -111,15 +202,56 @@
router: false,
content: 'browse',
filterable: 'mime-types'
}),
new media.controller.EditImage( { model: options.editImage } )
})
]);
},
bindHandlers: function() {
this.on( 'content:create:browse', this.browseContent, this );
this.on( 'content:render:edit-image', this.editImageContent, this );
// Handle a frame-level event for editing an attachment.
this.on( 'edit:attachment', this.editAttachment, this );
this.on( 'edit:attachment:next', this.editNextAttachment, this );
this.on( 'edit:attachment:previous', this.editPreviousAttachment, this );
},
editPreviousAttachment: function( currentModel ) {
var library = this.state().get('library'),
currentModelIndex = library.indexOf( currentModel );
this.trigger( 'edit:attachment', library.at( currentModelIndex - 1 ) );
},
editNextAttachment: function( currentModel ) {
var library = this.state().get('library'),
currentModelIndex = library.indexOf( currentModel );
this.trigger( 'edit:attachment', library.at( currentModelIndex + 1 ) );
},
/**
* Open the Edit Attachment modal.
*/
editAttachment: function( model ) {
var library = this.state().get('library'), hasPrevious, hasNext;
if ( library.indexOf( model ) > 0 ) {
hasPrevious = true;
}
else {
hasPrevious = false;
}
if ( library.indexOf( model ) < library.length - 1 ) {
hasNext = true;
}
else {
hasNext = false;
}
new media.view.Frame.EditAttachment({
hasPrevious: hasPrevious,
hasNext: hasNext,
model: model,
gridController: this
});
},
/**
@ -143,6 +275,7 @@
display: state.get('displaySettings'),
dragInfo: state.get('dragInfo'),
bulkEdit: true,
sidebar: false,
suggestedWidth: state.get('suggestedWidth'),
suggestedHeight: state.get('suggestedHeight'),
@ -162,5 +295,194 @@
}
});
}( jQuery, _, Backbone, wp ));
media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({
template: wp.template( 'attachment-details-two-column' ),
initialize: function() {
this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
this.model.on( 'change:sizes change:uploading', this.render, this );
this.model.on( 'change:title', this._syncTitle, this );
this.model.on( 'change:caption', this._syncCaption, this );
this.model.on( 'change:percent', this.progress, this );
// Update the selection.
this.model.on( 'add', this.select, this );
this.model.on( 'remove', this.deselect, this );
},
render: function() {
media.view.Attachment.Details.prototype.render.apply( this, arguments );
media.mixin.removeAllPlayers();
$( 'audio, video', this.$el ).each( function (i, elem) {
var el = media.view.MediaDetails.prepareSrc( elem );
new MediaElementPlayer( el, media.mixin.mejsSettings );
} );
}
});
/**
* A frame for editing the details of a specific media item.
*
* Opens in a modal by default.
*
* Requires an attachment model to be passed in the options hash under `model`.
*/
media.view.Frame.EditAttachment = media.view.Frame.extend({
className: 'edit-attachment-frame',
template: media.template( 'edit-attachment-frame' ),
regions: [ 'router', 'content' ],
events: {
'click': 'collapse',
'click .delete-media-item': 'deleteMediaItem',
'click .left': 'previousMediaItem',
'click .right': 'nextMediaItem'
},
initialize: function( options ) {
var self = this;
media.view.Frame.prototype.initialize.apply( this, arguments );
_.defaults( this.options, {
modal: true,
state: 'edit-attachment'
});
this.createStates();
this.on( 'content:render:edit-metadata', this.editMetadataContent, this );
this.on( 'content:render:edit-image', this.editImageContentUgh, this );
// Only need a tab to Edit Image for images.
if ( this.model.get( 'type' ) === 'image' ) {
this.on( 'router:create', this.createRouter, this );
this.on( 'router:render', this.browseRouter, this );
}
// Initialize modal container view.
if ( this.options.modal ) {
this.modal = new media.view.Modal({
controller: this,
title: this.options.title
});
// Completely destroy the modal DOM element when closing it.
this.modal.close = function() {
self.modal.remove();
};
this.modal.content( this );
this.modal.open();
}
},
/**
* Add the default states to the frame.
*/
createStates: function() {
this.states.add([
new media.controller.EditImageNoFrame( { model: this.model } )
]);
},
/**
* @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
*/
render: function() {
// Activate the default state if no active state exists.
if ( ! this.state() && this.options.state ) {
this.setState( this.options.state );
}
/**
* call 'render' directly on the parent class
*/
return media.view.Frame.prototype.render.apply( this, arguments );
},
/**
* Content region rendering callback for the `edit-metadata` mode.
*/
editMetadataContent: function() {
var view = new media.view.Attachment.Details.TwoColumn({
controller: this,
model: this.model
});
this.content.set( view );
},
/**
* For some reason the view doesn't exist in the DOM yet, don't have the
* patience to track this down right now.
*/
editImageContentUgh: function() {
_.defer( _.bind( this.editImageContent, this ) );
},
/**
* Render the EditImage view into the frame's content region.
*/
editImageContent: function() {
var view = new media.view.EditImage( { model: this.model, controller: this } ).render();
this.content.set( view );
// after creating the wrapper view, load the actual editor via an ajax call
view.loadEditor();
},
/**
* Create the router view.
*
* @param {Object} router
* @this wp.media.controller.Region
*/
createRouter: function( router ) {
router.view = new media.view.Router({
controller: this
});
},
/**
* Router rendering callback.
*
* @param media.view.Router view Instantiated in this.createRouter()
*/
browseRouter: function( view ) {
view.set({
'edit-metadata': {
text: 'Edit Metadata',
priority: 20
},
'edit-image': {
text: 'Edit Image',
priority: 40
}
});
},
/**
* Click handler to switch to the previous media item.
*/
previousMediaItem: function() {
if ( ! this.options.hasPrevious )
return;
this.modal.close();
this.options.gridController.trigger( 'edit:attachment:previous', this.model );
},
/**
* Click handler to switch to the next media item.
*/
nextMediaItem: function() {
if ( ! this.options.hasNext )
return;
this.modal.close();
this.options.gridController.trigger( 'edit:attachment:next', this.model );
}
});
}(jQuery, _, Backbone, wp));

File diff suppressed because one or more lines are too long

View File

@ -337,16 +337,11 @@
});
/**
* wp.media.controller.State
*
* A state is a step in a workflow that when set will trigger the controllers
* for the regions to be updated as specified in the frame. This is the base
* class that the various states used in wp.media extend.
*
* @constructor
* @augments Backbone.Model
* A more abstracted state, because media.controller.State expects
* specific regions (menu, title, etc.) to exist on the frame, which do not
* exist in media.view.Frame.EditAttachment.
*/
media.controller.State = Backbone.Model.extend({
media.controller._State = Backbone.Model.extend({
constructor: function() {
this.on( 'activate', this._preActivate, this );
this.on( 'activate', this.activate, this );
@ -354,13 +349,11 @@
this.on( 'deactivate', this._deactivate, this );
this.on( 'deactivate', this.deactivate, this );
this.on( 'reset', this.reset, this );
this.on( 'ready', this._ready, this );
this.on( 'ready', this.ready, this );
/**
* Call parent constructor with passed arguments
*/
Backbone.Model.apply( this, arguments );
this.on( 'change:menu', this._updateMenu, this );
},
/**
@ -382,15 +375,55 @@
/**
* @access private
*/
_ready: function() {
this._updateMenu();
_preActivate: function() {
this.active = true;
},
/**
* @access private
*/
_preActivate: function() {
this.active = true;
_postActivate: function() {},
/**
* @access private
*/
_deactivate: function() {
this.active = false;
}
});
/**
* wp.media.controller.State
*
* A state is a step in a workflow that when set will trigger the controllers
* for the regions to be updated as specified in the frame. This is the base
* class that the various states used in wp.media extend.
*
* @constructor
* @augments Backbone.Model
*/
media.controller.State = media.controller._State.extend({
constructor: function() {
this.on( 'activate', this._preActivate, this );
this.on( 'activate', this.activate, this );
this.on( 'activate', this._postActivate, this );
this.on( 'deactivate', this._deactivate, this );
this.on( 'deactivate', this.deactivate, this );
this.on( 'reset', this.reset, this );
this.on( 'ready', this._ready, this );
this.on( 'ready', this.ready, this );
/**
* Call parent constructor with passed arguments
*/
Backbone.Model.apply( this, arguments );
this.on( 'change:menu', this._updateMenu, this );
},
/**
* @access private
*/
_ready: function() {
this._updateMenu();
},
/**
* @access private
*/
@ -1758,7 +1791,8 @@
_.defaults( this.options, {
title: '',
modal: true,
uploader: true
uploader: true,
mode: ['select']
});
// Ensure core UI is enabled.
@ -4530,7 +4564,7 @@
var selection = this.options.selection;
this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
this.model.on( 'change:sizes change:uploading', this.render, this );
this.model.on( 'change', this.render, this );
this.model.on( 'change:title', this._syncTitle, this );
this.model.on( 'change:caption', this._syncCaption, this );
this.model.on( 'change:percent', this.progress, this );
@ -4583,7 +4617,7 @@
compat: false,
alt: '',
description: ''
});
}, this.options );
options.buttons = this.buttons;
options.describe = this.controller.state().get('describe');
@ -4633,11 +4667,17 @@
*/
toggleSelectionHandler: function( event ) {
var method;
// Catch enter and space events
if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
return;
}
// In the grid view, bubble up an edit:attachment event to the controller.
if ( _.contains( this.controller.options.mode, 'grid' ) ) {
this.controller.trigger( 'edit:attachment', this.model );
return;
}
if ( event.shiftKey ) {
method = 'between';
} else if ( event.ctrlKey || event.metaKey ) {
@ -5168,10 +5208,11 @@
*/
createAttachmentView: function( attachment ) {
var view = new this.options.AttachmentView({
controller: this.controller,
model: attachment,
collection: this.collection,
selection: this.options.selection
controller: this.controller,
model: attachment,
collection: this.collection,
selection: this.options.selection,
showAttachmentFields: this.options.showAttachmentFields
});
return this._viewsByCid[ attachment.cid ] = view;
@ -5468,7 +5509,6 @@
}
});
/**
* wp.media.view.AttachmentsBrowser
*
@ -5486,13 +5526,18 @@
filters: false,
search: true,
display: false,
sidebar: true,
showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ),
AttachmentView: media.view.Attachment.Library
});
this.createToolbar();
this.updateContent();
this.createSidebar();
if ( this.options.sidebar ) {
this.createSidebar();
} else {
this.$el.addClass( 'hide-sidebar' );
}
this.collection.on( 'add remove reset', this.updateContent, this );
},
@ -5517,6 +5562,20 @@
this.views.add( this.toolbar );
// Feels odd to bring the global media library switcher into the Attachment
// browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
// which the controller can tap into and add this view?
if ( _.contains( this.controller.options.mode, 'grid' ) ) {
var libraryViewSwitcherConstructor = media.View.extend({
className: 'view-switch media-grid-view-switch',
template: media.template( 'media-library-view-switcher')
});
this.toolbar.set( 'libraryViewSwitcher', new libraryViewSwitcherConstructor({
controller: this.controller,
priority: -90
}).render() );
}
filters = this.options.filters;
if ( 'uploaded' === filters ) {
FiltersConstructor = media.view.AttachmentFilters.Uploaded;
@ -5611,11 +5670,12 @@
this.removeContent();
this.attachments = new media.view.Attachments({
controller: this.controller,
collection: this.collection,
selection: this.options.selection,
model: this.model,
sortable: this.options.sortable,
controller: this.controller,
collection: this.collection,
selection: this.options.selection,
model: this.model,
sortable: this.options.sortable,
showAttachmentFields: this.options.showAttachmentFields,
// The single `Attachment` view to be used in the `Attachments` view.
AttachmentView: this.options.AttachmentView

File diff suppressed because one or more lines are too long

View File

@ -220,6 +220,15 @@ function wp_print_media_templates() {
</div>
</script>
<script type="text/html" id="tmpl-media-library-view-switcher">
<a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list">
<img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/>
</a>
<a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current">
<img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/>
</a>
</script>
<script type="text/html" id="tmpl-uploader-status">
<h3><?php _e( 'Uploading' ); ?></h3>
<a class="upload-dismiss-errors" href="#"><?php _e('Dismiss Errors'); ?></a>
@ -241,7 +250,128 @@ function wp_print_media_templates() {
<span class="upload-error-message">{{ data.message }}</span>
</script>
<script type="text/html" id="tmpl-edit-attachment-frame">
<div class="edit-media-header">
<button class="left dashicons dashicons-no<# if ( ! data.hasPrevious ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit previous media item' ); ?></span></button>
<button class="right dashicons dashicons-no<# if ( ! data.hasNext ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit next media item' ); ?></span></button>
</div>
<div class="media-frame-router"></div>
<div class="media-frame-content"></div>
<div class="media-frame-toolbar"></div>
</script>
<script type="text/html" id="tmpl-attachment-details-two-column">
<h3>
<?php _e('Attachment Details'); ?>
<span class="settings-save-status">
<span class="spinner"></span>
<span class="saved"><?php esc_html_e('Saved.'); ?></span>
</span>
</h3>
<div class="attachment-info">
<div class="thumbnail thumbnail-{{ data.type }}">
<# if ( data.uploading ) { #>
<div class="media-progress-bar"><div></div></div>
<# } else if ( 'image' === data.type ) { #>
<img src="{{ data.sizes.full.url }}" draggable="false" />
<# } else { #>
<img src="{{ data.icon }}" class="icon" draggable="false" />
<# } #>
</div>
<div class="details">
<div class="filename">{{ data.filename }}</div>
<div class="uploaded">{{ data.dateFormatted }}</div>
<div class="file-size">{{ data.filesizeHumanReadable }}</div>
<# if ( 'image' === data.type && ! data.uploading ) { #>
<# if ( data.width && data.height ) { #>
<div class="dimensions">{{ data.width }} &times; {{ data.height }}</div>
<# } #>
<# if ( data.can.save ) { #>
<a class="edit-attachment" href="{{ data.editLink }}&amp;image-editor" target="_blank"><?php _e( 'Edit Image' ); ?></a>
<a class="refresh-attachment" href="#"><?php _e( 'Refresh' ); ?></a>
<# } #>
<# } #>
<# if ( data.fileLength ) { #>
<div class="file-length"><?php _e( 'Length:' ); ?> {{ data.fileLength }}</div>
<# } #>
<# if ( ! data.uploading && data.can.remove ) { #>
<?php if ( MEDIA_TRASH ): ?>
<a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a>
<?php else: ?>
<a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a>
<?php endif; ?>
<# } #>
<div class="compat-meta">
<# if ( data.compat && data.compat.meta ) { #>
{{{ data.compat.meta }}}
<# } #>
</div>
</div>
<# if ( 'audio' === data.type ) { #>
<div class="wp-media-wrapper">
<audio style="visibility: hidden" controls class="wp-audio-shortcode" width="100%" preload="none">
<source type="{{ data.mime }}" src="{{ data.url }}"/>
</audio>
</div>
<# } else if ( 'video' === data.type ) { #>
<div style="max-width: 100%; width: {{ data.width }}px" class="wp-media-wrapper">
<video controls class="wp-video-shortcode" preload="metadata"
width="{{ data.width }}" height="{{ data.height }}"
<# if ( data.image && data.image.src !== data.icon ) { #>poster="{{ data.image.src }}"<# } #>>
<source type="{{ data.mime }}" src="{{ data.url }}"/>
</video>
</div>
<# } #>
</div>
<div class="attachment-fields">
<label class="setting" data-setting="url">
<span class="name"><?php _e('URL'); ?></span>
<input type="text" value="{{ data.url }}" readonly />
</label>
<# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #>
<label class="setting" data-setting="title">
<span class="name"><?php _e('Title'); ?></span>
<input type="text" value="{{ data.title }}" {{ maybeReadOnly }} />
</label>
<label class="setting" data-setting="caption">
<span class="name"><?php _e('Caption'); ?></span>
<textarea {{ maybeReadOnly }}>{{ data.caption }}</textarea>
</label>
<# if ( 'image' === data.type ) { #>
<label class="setting" data-setting="alt">
<span class="name"><?php _e('Alt Text'); ?></span>
<input type="text" value="{{ data.alt }}" {{ maybeReadOnly }} />
</label>
<# } #>
<label class="setting" data-setting="description">
<span class="name"><?php _e('Description'); ?></span>
<textarea {{ maybeReadOnly }}>{{ data.description }}</textarea>
</label>
<label class="setting">
<span class="name"><?php _e( 'Uploaded By' ); ?></span>
<span class="value">{{ data.authorName }}</span>
</label>
<# if ( data.uploadedTo ) { #>
<label class="setting">
<span class="name"><?php _e('Uploaded To'); ?></span>
<span class="value"><a href="{{ data.uploadedToLink }}">{{ data.uploadedToTitle }}</a></span>
</label>
<# } #>
</div>
</script>
<script type="text/html" id="tmpl-attachment">
<# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
<div class="inline-toolbar">
<div class="dashicons dashicons-edit edit edit-media"></div>
</div>
<# } #>
<div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}">
<# if ( data.uploading ) { #>
<div class="media-progress-bar"><div></div></div>
@ -251,13 +381,15 @@ function wp_print_media_templates() {
<img src="{{ data.size.url }}" draggable="false" alt="" />
</div>
</div>
<# } else { #>
<# } else {
if ( data.thumb && data.thumb.src && data.thumb.src !== data.icon ) {
#><img src="{{ data.thumb.src }}" class="thumbnail" draggable="false" /><#
} #>
<img src="{{ data.icon }}" class="icon" draggable="false" />
<div class="filename">
<div>{{ data.filename }}</div>
</div>
<# } #>
<# if ( data.buttons.close ) { #>
<a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a>
<# } #>
@ -268,8 +400,8 @@ function wp_print_media_templates() {
</div>
<#
var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly';
if ( data.describe ) { #>
<# if ( 'image' === data.type ) { #>
if ( data.describe ) {
if ( 'image' === data.type ) { #>
<input type="text" value="{{ data.caption }}" class="describe" data-setting="caption"
placeholder="<?php esc_attr_e('Caption this image&hellip;'); ?>" {{ maybeReadOnly }} />
<# } else { #>
@ -281,8 +413,31 @@ function wp_print_media_templates() {
<# } else { #>
placeholder="<?php esc_attr_e('Describe this media file&hellip;'); ?>"
<# } #> {{ maybeReadOnly }} />
<# } #>
<# }
}
if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
<div class="data-fields">
<# _.each( data.showAttachmentFields, function( field ) { #>
<div class="data-field data-{{ field }}"><#
if ( 'uploadedTo' === field ) {
if ( data[field] ) {
#><?php _e( 'Uploaded To:' ) ?><#
} else {
#><?php _e( 'Unattached' ) ?><#
}
} else if ( 'title' === field && ! data[ field ] ) {
#><?php _e( '(No title)' ) ?><#
}
if ( data[ field ] ) {
#>{{ data[ field ] }}<#
}
#></div>
<# }); #>
</div>
<# } #>
</script>
<script type="text/html" id="tmpl-attachment-details">