Edit Image modal:

- Move all advanced options under a single “Show advanced options” toggle that mirrors the behavior and look-and-feel of the wplink modal.
- Switch to using <select> for the Size and Link To.
- Bring back the field for CSS Class for the image, but don’t incorporate the internally managed WordPress classes (size-, wp-image-, etc…).
- On larger screen sizes, float labels to the left. When the width drops below 900px, stack the label above the fields.
- Keep image at top on screen sizes where the two columns are stacked into a single column.
- Don't replace the nodes in the editor DOM so we don't stomp on any custom attributes that the user may have added via the Text editor or some other mechanism.
Props gcorne, see #27366

git-svn-id: https://develop.svn.wordpress.org/trunk@27898 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2014-04-02 01:53:56 +00:00
parent 5977c4332e
commit d4f99c2142
4 changed files with 296 additions and 204 deletions

View File

@ -270,7 +270,8 @@
width: 100%; width: 100%;
} }
.media-sidebar h3 { .media-sidebar h3,
.image-details h3 {
position: relative; position: relative;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
@ -1603,23 +1604,34 @@
.image-details .embed-media-settings { .image-details .embed-media-settings {
top: 0; top: 0;
overflow: visible; overflow: visible;
padding: 0;
} }
.image-details .column-settings { .image-details .column-settings {
width: 44%; background: #f3f3f3;
float: left; border-right: 1px solid #ddd;
margin-right: 20px; min-height: 100%;
width: 52%;
position: absolute;
top: 0;
left: 0;
}
.image-details .column-settings h3 {
margin: 20px;
padding-top: 20px;
border-top: 1px solid #ddd;
} }
.image-details .column-image { .image-details .column-image {
width: 53%; width: 48%;
float: left; position: absolute;
left: 52%;
top: 0;
} }
.image-details .column-image:after { .image-details .image {
content: ''; margin: 20px;
display: table;
clear: both;
} }
.image-details .image img { .image-details .image img {
@ -1627,6 +1639,29 @@
max-height: 500px; max-height: 500px;
} }
.image-details .advanced-toggle {
font-style: italic;
color: #666;
text-decoration: none;
margin: 20px;
display: block;
}
.image-details .advanced-toggle::after {
font: normal 20px/1 'dashicons';
speak: none;
vertical-align: top;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
content: '\f140';
display: inline-block;
margin-top: -2px;
}
.image-details .advanced-visible .advanced-toggle::after {
content: '\f142';
margin-top: 0;
}
.media-embed .thumbnail { .media-embed .thumbnail {
max-width: 100%; max-width: 100%;
@ -1661,14 +1696,19 @@
clear: both; clear: both;
} }
.image-details .setting { .image-details .embed-media-settings .setting {
float: none; float: none;
width: auto;
} }
.image-details .actions { .image-details .actions {
margin: 10px 0; margin: 10px 0;
} }
.image-details .hidden {
display: none;
}
.media-embed .setting input[type="text"], .media-embed .setting input[type="text"],
.media-embed .setting textarea { .media-embed .setting textarea {
display: block; display: block;
@ -1677,9 +1717,20 @@
margin: 1px 0; margin: 1px 0;
} }
.image-details .setting input[type="text"], .image-details .embed-media-settings .setting input[type="text"],
.image-details .setting textarea { .image-details .embed-media-settings .setting textarea {
max-width: inherit; max-width: inherit;
width: 70%;
}
.image-details .embed-media-settings .setting input.link-to-custom,
.image-details .embed-media-settings .link-target {
margin-left: 27%;
width: 70%;
}
.image-details .embed-media-settings .link-target {
margin-top: 24px;
} }
.media-embed .setting input.hidden { .media-embed .setting input.hidden {
@ -1694,6 +1745,14 @@
color: #666; color: #666;
} }
.image-details .embed-media-settings .setting span {
float: left;
width: 25%;
text-align: right;
margin: 8px 1% 0 1%;
line-height: 1.1;
}
.media-embed .setting .button-group { .media-embed .setting .button-group {
margin: 2px 0; margin: 2px 0;
} }
@ -1709,10 +1768,6 @@
margin-top: 10px; margin-top: 10px;
} }
.advanced .hidden {
display: none;
}
/* Drag & drop on the editor upload */ /* Drag & drop on the editor upload */
#wp-fullscreen-body .uploader-editor, #wp-fullscreen-body .uploader-editor,
.wp-editor-wrap .uploader-editor { .wp-editor-wrap .uploader-editor {
@ -1930,6 +1985,28 @@
right: 30px; right: 30px;
} }
.image-details .embed-media-settings .setting {
margin: 20px;
}
.image-details .embed-media-settings .setting span {
float: none;
text-align: left;
width: 100%;
margin-bottom: 4px;
}
.image-details .embed-media-settings .setting input.link-to-custom,
.image-details .embed-media-settings .setting input[type="text"],
.image-details .embed-media-settings .setting textarea {
width: 100%;
margin-left: 0;
}
.image-details .link-target {
width: 100%;
}
.media-selection { .media-selection {
min-width: 120px; min-width: 120px;
} }
@ -2137,10 +2214,14 @@
.image-details .column-settings, .image-details .column-settings,
.image-details .column-image { .image-details .column-image {
float: none; position: relative;
padding: 10px 0 20px 0;
left: 0;
width: 100%; width: 100%;
min-height: inherit;
} }
/* Gallery */ /* Gallery */
.media-frame.hide-router .media-frame-content { .media-frame.hide-router .media-frame-content {
top: 73px; top: 73px;

View File

@ -6059,15 +6059,13 @@
events: _.defaults( media.view.Settings.AttachmentDisplay.prototype.events, { events: _.defaults( media.view.Settings.AttachmentDisplay.prototype.events, {
'click .edit-attachment': 'editAttachment', 'click .edit-attachment': 'editAttachment',
'click .replace-attachment': 'replaceAttachment', 'click .replace-attachment': 'replaceAttachment',
'click .show-advanced': 'showAdvanced' 'click .advanced-toggle': 'toggleAdvanced'
} ), } ),
initialize: function() { initialize: function() {
// used in AttachmentDisplay.prototype.updateLinkTo // used in AttachmentDisplay.prototype.updateLinkTo
this.options.attachment = this.model.attachment; this.options.attachment = this.model.attachment;
if ( this.model.attachment ) {
this.listenTo( this.model, 'change:url', this.updateUrl ); this.listenTo( this.model, 'change:url', this.updateUrl );
this.listenTo( this.model, 'change:link', this.toggleLinkSettings ); this.listenTo( this.model, 'change:link', this.toggleLinkSettings );
}
media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments ); media.view.Settings.AttachmentDisplay.prototype.initialize.apply( this, arguments );
}, },
@ -6113,8 +6111,8 @@
}, },
updateUrl: function() { updateUrl: function() {
this.$( '.image img' ).attr( 'src', this.model.get('url' ) ); this.$( '.image img' ).attr( 'src', this.model.get( 'url' ) );
this.$( '.url' ).val( this.model.get('url' ) ); this.$( '.url' ).val( this.model.get( 'url' ) );
}, },
toggleLinkSettings: function() { toggleLinkSettings: function() {
@ -6125,11 +6123,16 @@
} }
}, },
showAdvanced: function( event ) { toggleAdvanced: function( event ) {
var $advanced = $( event.target ).closest( '.advanced' );
event.preventDefault(); event.preventDefault();
$( event.target ).closest('.advanced') if ( $advanced.hasClass('advanced-visible') ) {
.find( '.hidden' ).removeClass( 'hidden' ); $advanced.removeClass('advanced-visible');
$( event.target ).remove(); $advanced.find('div').addClass('hidden');
} else {
$advanced.addClass('advanced-visible');
$advanced.find('div').removeClass('hidden');
}
}, },
editAttachment: function( event ) { editAttachment: function( event ) {

View File

@ -114,7 +114,7 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
} }
function extractImageData( imageNode ) { function extractImageData( imageNode ) {
var classes, metadata, captionBlock, caption, link, var classes, extraClasses, metadata, captionBlock, caption, link, width, height,
dom = editor.dom; dom = editor.dom;
// default attributes // default attributes
@ -127,38 +127,44 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
caption: '', caption: '',
alt: '', alt: '',
align: 'none', align: 'none',
extraClasses: '',
link: false, link: false,
linkUrl: '', linkUrl: '',
linkClassName: '', linkClassName: '',
linkTargetBlank: false, linkTargetBlank: false,
linkRel: '', linkRel: '',
title: '', title: ''
className: ''
}; };
metadata.url = dom.getAttrib( imageNode, 'src' ); metadata.url = dom.getAttrib( imageNode, 'src' );
metadata.alt = dom.getAttrib( imageNode, 'alt' ); metadata.alt = dom.getAttrib( imageNode, 'alt' );
metadata.title = dom.getAttrib( imageNode, 'title' ); metadata.title = dom.getAttrib( imageNode, 'title' );
metadata.width = parseInt( dom.getAttrib( imageNode, 'width' ), 10 );
metadata.height = parseInt( dom.getAttrib( imageNode, 'height' ), 10 );
metadata.className = imageNode.className;
classes = metadata.className.split( ' ' ); width = dom.getAttrib( imageNode, 'width' ) || imageNode.width;
height = dom.getAttrib( imageNode, 'height' ) || imageNode.height;
metadata.width = parseInt( width, 10 );
metadata.height = parseInt( height, 10 );
classes = tinymce.explode( imageNode.className, ' ' );
extraClasses = [];
tinymce.each( classes, function( name ) { tinymce.each( classes, function( name ) {
if ( /^wp-image/.test( name ) ) { if ( /^wp-image/.test( name ) ) {
metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 ); metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 );
} } else if ( /^align/.test( name ) ) {
if ( /^align/.test( name ) ) {
metadata.align = name.replace( 'align', '' ); metadata.align = name.replace( 'align', '' );
} else if ( /^size/.test( name ) ) {
metadata.size = name.replace( 'size-', '' );
} else {
extraClasses.push( name );
} }
if ( /^size/.test( name ) ) {
metadata.size = name.replace( 'size-', '' );
}
} ); } );
metadata.extraClasses = extraClasses.join( ' ' );
// Extract caption // Extract caption
captionBlock = dom.getParents( imageNode, '.wp-caption' ); captionBlock = dom.getParents( imageNode, '.wp-caption' );
@ -194,68 +200,16 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
} }
function updateImage( imageNode, imageData ) { function updateImage( imageNode, imageData ) {
var className, width, node, html, captionNode, nodeToReplace, uid, editedImg, id; var classes, className, width, node, html, parent, wrap,
captionNode, dd, dl, id, attrs, linkAttrs,
dom = editor.dom;
if ( imageData.caption ) { classes = tinymce.explode( imageData.extraClasses, ' ' );
html = createImageAndLink( imageData, 'html' ); if ( ! classes ) {
classes = [];
width = parseInt( imageData.width, 10 );
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
width += 10;
} }
className = 'align' + imageData.align;
id = imageData.attachment_id ? 'id="attachment_'+ imageData.attachment_id +'" ' : '';
// should create a new function for generating the caption markup
html = '<dl ' + id + 'class="wp-caption '+ className +'" style="width: '+ width +'px">' +
'<dt class="wp-caption-dt">'+ html + '</dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>';
node = editor.dom.create( 'div', { 'class': 'mceTemp' }, html );
} else {
node = createImageAndLink( imageData, 'node' );
}
nodeToReplace = imageNode;
captionNode = editor.dom.getParent( imageNode, '.mceTemp' );
if ( captionNode ) {
nodeToReplace = captionNode;
} else {
if ( imageNode.parentNode.nodeName === 'A' ) {
nodeToReplace = imageNode.parentNode;
}
}
uid = editor.dom.uniqueId( 'wp_' );
editor.dom.setAttrib( node, 'data-wp-replace-id', uid );
editor.dom.replace( node, nodeToReplace );
// find the updated node
node = editor.dom.select( '[data-wp-replace-id="' + uid + '"]' )[0];
editor.dom.setAttrib( node, 'data-wp-replace-id', '' );
editor.nodeChanged();
editedImg = node.nodeName === 'IMG' ? node : editor.dom.select( 'img', node )[0];
if ( editedImg ) {
editor.selection.select( editedImg );
// refresh toolbar
addToolbar( editedImg );
}
}
function createImageAndLink( imageData, mode ) {
var classes = [],
attrs, linkAttrs;
mode = mode ? mode : 'node';
if ( ! imageData.caption ) { if ( ! imageData.caption ) {
classes.push( 'align' + imageData.align ); classes.push( 'align' + imageData.align );
} }
@ -269,45 +223,97 @@ tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
attrs = { attrs = {
src: imageData.url, src: imageData.url,
width: imageData.width, width: imageData.width || null,
height: imageData.height, height: imageData.height || null,
alt: imageData.alt, alt: imageData.alt,
title: imageData.title || null title: imageData.title || null,
'class': classes.join( ' ' ) || null
}; };
if ( classes.length ) { dom.setAttribs( imageNode, attrs );
attrs['class'] = classes.join( ' ' );
}
if ( imageData.linkUrl ) {
linkAttrs = { linkAttrs = {
href: imageData.linkUrl href: imageData.linkUrl,
rel: imageData.linkRel || null,
target: imageData.linkTargetBlank ? '_blank': null,
'class': imageData.linkClassName || null
}; };
if ( imageData.linkRel ) { if ( imageNode.parentNode.nodeName === 'A' ) {
linkAttrs.rel = imageData.linkRel; if ( imageData.linkUrl ) {
dom.setAttribs( imageNode.parentNode, linkAttrs );
} else {
dom.remove( imageNode.parentNode, true );
}
} else if ( imageData.linkUrl ) {
html = dom.createHTML( 'a', linkAttrs, dom.getOuterHTML( imageNode ) );
dom.outerHTML( imageNode, html );
} }
if ( imageData.linkTargetBlank ) { captionNode = editor.dom.getParent( imageNode, '.mceTemp' );
linkAttrs.target = '_blank';
if ( imageNode.parentNode.nodeName === 'A' ) {
node = imageNode.parentNode;
} else {
node = imageNode;
} }
if ( imageData.linkClassName ) { if ( imageData.caption ) {
linkAttrs['class'] = imageData.linkClassName; width = parseInt( imageData.width, 10 );
id = imageData.attachment_id ? 'attachment_' + imageData.attachment_id : null;
className = 'wp-caption align' + imageData.align;
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) {
width += 10;
} }
if ( mode === 'node' ) { if ( captionNode ) {
return editor.dom.create( 'a', linkAttrs, editor.dom.createHTML( 'img', attrs ) ); dl = dom.select( 'dl.wp-caption', captionNode );
} else if ( mode === 'html' ) {
return editor.dom.createHTML( 'a', linkAttrs, editor.dom.createHTML( 'img', attrs ) ); if ( dl.length ) {
dom.setAttribs( dl, {
id: id,
'class': className,
style: 'width: ' + width + 'px'
} );
} }
} else if ( mode === 'node' ) { dd = dom.select( '.wp-caption-dd', captionNode );
return editor.dom.create( 'img', attrs );
} else if ( mode === 'html' ) { if ( dd.length ) {
return editor.dom.createHTML( 'img', attrs ); dom.setHTML( dd[0], imageData.caption );
} }
} else {
id = id ? 'id="'+ id +'" ' : '';
// should create a new function for generating the caption markup
html = '<dl ' + id + 'class="' + className +'" style="width: '+ width +'px">' +
'<dt class="wp-caption-dt">' + dom.getOuterHTML( node ) + '</dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>';
if ( parent = dom.getParent( node, 'p' ) ) {
wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
dom.insertAfter( wrap, parent );
dom.remove( node );
if ( dom.isEmpty( parent ) ) {
dom.remove( parent );
}
} else {
dom.setOuterHTML( node, '<div class="mceTemp">' + html + '</div>' );
}
}
} else if ( captionNode ) {
// Remove the caption wrapper and place the image in new paragraph
parent = dom.create( 'p' );
captionNode.parentNode.insertBefore( parent, captionNode );
parent.appendChild( node );
dom.remove( captionNode );
}
editor.nodeChanged();
// refresh the toolbar
addToolbar( imageNode );
} }
function editImage( img ) { function editImage( img ) {

View File

@ -641,6 +641,18 @@ function wp_print_media_templates() {
<script type="text/html" id="tmpl-image-details"> <script type="text/html" id="tmpl-image-details">
<div class="media-embed"> <div class="media-embed">
<div class="embed-media-settings"> <div class="embed-media-settings">
<div class="column-image">
<div class="image">
<img src="{{ data.model.url }}" draggable="false" />
<# if ( data.attachment && window.imageEdit ) { #>
<div class="actions">
<input type="button" class="edit-attachment button" value="<?php esc_attr_e( 'Edit Original' ); ?>" />
<input type="button" class="replace-attachment button" value="<?php esc_attr_e( 'Replace' ); ?>" />
</div>
<# } #>
</div>
</div>
<div class="column-settings"> <div class="column-settings">
<?php <?php
/** This filter is documented in wp-admin/includes/media.php */ /** This filter is documented in wp-admin/includes/media.php */
@ -656,16 +668,7 @@ function wp_print_media_templates() {
<input type="text" data-setting="alt" value="{{ data.model.alt }}" /> <input type="text" data-setting="alt" value="{{ data.model.alt }}" />
</label> </label>
<div class="setting advanced"> <h3><?php _e( 'Display Settings' ); ?></h3>
<a class="show-advanced" href="#"><?php _e('show advanced'); ?></a>
<div class="hidden">
<label class="setting title-text">
<span><?php _e('Title Attribute'); ?></span>
<input type="text" data-setting="title" value="{{ data.model.title }}" />
</label>
</div>
</div>
<div class="setting align"> <div class="setting align">
<span><?php _e('Align'); ?></span> <span><?php _e('Align'); ?></span>
<div class="button-group button-large" data-setting="align"> <div class="button-group button-large" data-setting="align">
@ -685,9 +688,14 @@ function wp_print_media_templates() {
</div> </div>
<# if ( data.attachment ) { #> <# if ( data.attachment ) { #>
<div class="setting size"> <# if ( 'undefined' !== typeof data.attachment.sizes ) { #>
<label class="setting">
<span><?php _e('Size'); ?></span> <span><?php _e('Size'); ?></span>
<div class="button-group button-large" data-setting="size"> <select class="size" name="size"
data-setting="size"
<# if ( data.userSettings ) { #>
data-user-setting="imgsize"
<# } #>>
<?php <?php
/** This filter is documented in wp-admin/includes/media.php */ /** This filter is documented in wp-admin/includes/media.php */
$sizes = apply_filters( 'image_size_names_choose', array( $sizes = apply_filters( 'image_size_names_choose', array(
@ -698,74 +706,68 @@ function wp_print_media_templates() {
) ); ) );
foreach ( $sizes as $value => $name ) : ?> foreach ( $sizes as $value => $name ) : ?>
<# var size = data.attachment.sizes['<?php echo esc_js( $value ); ?>']; <#
var size = data.sizes['<?php echo esc_js( $value ); ?>'];
if ( size ) { #> if ( size ) { #>
<button class="button" value="<?php echo esc_attr( $value ); ?>"> <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $value, 'full' ); ?>>
<?php echo esc_html( $name ); ?> <?php echo esc_html( $name ); ?> &ndash; {{ size.width }} &times; {{ size.height }}
</button> </option>
<# } #> <# } #>
<?php endforeach; ?> <?php endforeach; ?>
</div> </select>
</div> </label>
<# } #>
<# } #> <# } #>
<div class="setting link-to"> <div class="setting link-to">
<span><?php _e('Link To'); ?></span> <span><?php _e('Link To'); ?></span>
<div class="button-group button-large" data-setting="link"> <select data-setting="link">
<# if ( data.attachment ) { #> <# if ( data.attachment ) { #>
<button class="button" value="file"> <option value="file">
<?php esc_attr_e('Media File'); ?> <?php esc_attr_e('Media File'); ?>
</button> </option>
<button class="button" value="post"> <option value="post">
<?php esc_attr_e('Attachment Page'); ?> <?php esc_attr_e('Attachment Page'); ?>
</button> </option>
<# } else { #> <# } else { #>
<button class="button" value="file"> <option value="file">
<?php esc_attr_e('Image URL'); ?> <?php esc_attr_e('Image URL'); ?>
</button> </option>
<# } #> <# } #>
<button class="button" value="custom"> <option value="custom">
<?php esc_attr_e('Custom URL'); ?> <?php esc_attr_e('Custom URL'); ?>
</button> </option>
<button class="button active" value="none"> <option value="none">
<?php esc_attr_e('None'); ?> <?php esc_attr_e('None'); ?>
</button> </option>
</div> </select>
<input type="text" class="link-to-custom" data-setting="linkUrl" /> <input type="text" class="link-to-custom" data-setting="linkUrl" />
</div> </div>
<div class="advanced">
<a class="advanced-toggle" href="#"><?php _e('Show advanced options'); ?></a>
<div class="setting link-settings"> <div class="hidden">
<label class="setting title-text">
<span><?php _e('Image Title Attribute'); ?></span>
<input type="text" data-setting="title" value="{{ data.model.title }}" />
</label>
<label class="setting extra-classes">
<span><?php _e('Image CSS Class'); ?></span>
<input type="text" data-setting="extraClasses" value="{{ data.model.extraClasses }}" />
</label>
<div class="setting link-target"> <div class="setting link-target">
<label><input type="checkbox" data-setting="linkTargetBlank" value="_blank" <# if ( data.model.linkTargetBlank ) { #>checked="checked"<# } #>><?php _e( 'Open link in a new window/tab' ); ?></label> <label><input type="checkbox" data-setting="linkTargetBlank" value="_blank" <# if ( data.model.linkTargetBlank ) { #>checked="checked"<# } #>><?php _e( 'Open link in a new window/tab' ); ?></label>
</div> </div>
<div class="advanced">
<a class="show-advanced" href="#"><?php _e('show advanced'); ?></a>
<div class="hidden">
<label class="setting link-rel"> <label class="setting link-rel">
<span><?php _e('Link Rel'); ?></span> <span><?php _e('Link Rel'); ?></span>
<input type="text" data-setting="linkRel" value="{{ data.model.linkClassName }}" /> <input type="text" data-setting="linkRel" value="{{ data.model.linkClassName }}" />
</label> </label>
<label class="setting link-class-name"> <label class="setting link-class-name">
<span><?php _e('CSS Class'); ?></span> <span><?php _e('Link CSS Class'); ?></span>
<input type="text" data-setting="linkClassName" value="{{ data.model.linkClassName }}" /> <input type="text" data-setting="linkClassName" value="{{ data.model.linkClassName }}" />
</label> </label>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="column-image">
<div class="image">
<img src="{{ data.model.url }}" draggable="false" />
</div>
<# if ( data.attachment && window.imageEdit ) { #>
<div class="actions">
<input type="button" class="edit-attachment button" value="<?php esc_attr_e( 'Edit Original' ); ?>" />
<input type="button" class="replace-attachment button" value="<?php esc_attr_e( 'Replace' ); ?>" />
</div>
<# } #>
</div>
</div> </div>
</div> </div>
</script> </script>