Make WordPress Core

Changeset 57713


Ignore:
Timestamp:
02/26/2024 08:16:09 PM (2 months ago)
Author:
jorbin
Message:

Site icon: Polish up Site Icon on the general settings screen.

This fixes a number of issues, chief among them:

  • Updates to the site title are reflected in the preview.
  • Improve alt text for preview
  • Make string describing site icon more succinct.
  • Add inline documentation to JavaScript

Props kebbet, jorbin, swissspidy, afercia, mukesh27, alexstine, jameskoster, andraganescu.
Fixes #54370.

Location:
trunk/src
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/admin/site-icon.js

    r57602 r57713  
    1 (function($) {
    2     var frame;
    3 
    4     function calculateImageSelectOptions ( attachment ) {
    5         var realWidth  = attachment.get( 'width' ),
     1/**
     2 * Handle the site icon setting in options-general.php.
     3 *
     4 * @since 6.5.0
     5 * @output wp-admin/js/site-icon.js
     6 */
     7
     8/* global jQuery, wp */
     9
     10( function ( $ ) {
     11    var $chooseButton = $( '#choose-from-library-button' ),
     12        $iconPreview = $( '#site-icon-preview' ),
     13        $browserIconPreview = $( '#browser-icon-preview' ),
     14        $appIconPreview = $( '#app-icon-preview' ),
     15        $hiddenDataField = $( '#site_icon_hidden_field' ),
     16        $removeButton = $( '#js-remove-site-icon' ),
     17        frame;
     18
     19    /**
     20     * Calculate image selection options based on the attachment dimensions.
     21     *
     22     * @since 6.5.0
     23     *
     24     * @param {Object} attachment The attachment object representing the image.
     25     * @return {Object} The image selection options.
     26     */
     27    function calculateImageSelectOptions( attachment ) {
     28        var realWidth = attachment.get( 'width' ),
    629            realHeight = attachment.get( 'height' ),
    730            xInit = 512,
    831            yInit = 512,
    932            ratio = xInit / yInit,
    10             xImg  = xInit,
    11             yImg  = yInit,
    12             x1, y1, imgSelectOptions;
     33            xImg = xInit,
     34            yImg = yInit,
     35            x1,
     36            y1,
     37            imgSelectOptions;
    1338
    1439        if ( realWidth / realHeight > ratio ) {
     
    3661            y1: y1,
    3762            x2: xInit + x1,
    38             y2: yInit + y1
     63            y2: yInit + y1,
    3964        };
    4065
     
    4267    }
    4368
    44     $( function() {
    45         // Build the choose from library frame.
    46         $( '#choose-from-library-link' ).on( 'click', function() {
    47             var $el = $(this);
    48 
    49             // Create the media frame.
    50             frame = wp.media({
    51                 button: {
    52                     // Set the text of the button.
    53                     text: $el.data('update'),
    54                     // Don't close, we might need to crop.
    55                     close: false
    56                 },
    57                 states: [
    58                     new wp.media.controller.Library({
    59                         title: $el.data( 'choose' ),
    60                         library: wp.media.query({ type: 'image' }),
    61                         date: false,
    62                         suggestedWidth: $el.data( 'size' ),
    63                         suggestedHeight: $el.data( 'size' )
    64                     }),
    65                     new wp.media.controller.SiteIconCropper({
    66                         control: {
    67                             params: {
    68                                 width: $el.data( 'size' ),
    69                                 height: $el.data( 'size' )
    70                             }
     69    /**
     70     * Initializes the media frame for selecting or cropping an image.
     71     *
     72     * @since 6.5.0
     73     */
     74    $chooseButton.on( 'click', function () {
     75        var $el = $( this );
     76
     77        // Create the media frame.
     78        frame = wp.media( {
     79            button: {
     80                // Set the text of the button.
     81                text: $el.data( 'update' ),
     82
     83                // Don't close, we might need to crop.
     84                close: false,
     85            },
     86            states: [
     87                new wp.media.controller.Library( {
     88                    title: $el.data( 'choose-text' ),
     89                    library: wp.media.query( { type: 'image' } ),
     90                    date: false,
     91                    suggestedWidth: $el.data( 'size' ),
     92                    suggestedHeight: $el.data( 'size' ),
     93                } ),
     94                new wp.media.controller.SiteIconCropper( {
     95                    control: {
     96                        params: {
     97                            width: $el.data( 'size' ),
     98                            height: $el.data( 'size' ),
    7199                        },
    72                         imgSelectOptions: calculateImageSelectOptions
    73                     })
    74                 ]
    75             });
    76 
    77             frame.on( 'cropped', function( attachment) {
    78                 $( '#site_icon_hidden_field' ).val(attachment.id);
    79                 switchToUpdate(attachment.url);
     100                    },
     101                    imgSelectOptions: calculateImageSelectOptions,
     102                } ),
     103            ],
     104        } );
     105
     106        frame.on( 'cropped', function ( attachment ) {
     107            $hiddenDataField.val( attachment.id );
     108            switchToUpdate( attachment );
     109            frame.close();
     110
     111            // Start over with a frame that is so fresh and so clean clean.
     112            frame = null;
     113        } );
     114
     115        // When an image is selected, run a callback.
     116        frame.on( 'select', function () {
     117            // Grab the selected attachment.
     118            var attachment = frame.state().get( 'selection' ).first();
     119
     120            if (
     121                attachment.attributes.height === $el.data( 'size' ) &&
     122                $el.data( 'size' ) === attachment.attributes.width
     123            ) {
     124                switchToUpdate( attachment.attributes );
    80125                frame.close();
    81                 // Start over with a frame that is so fresh and so clean clean.
    82                 frame = null;
    83             });
    84 
    85             // When an image is selected, run a callback.
    86             frame.on( 'select', function() {
    87                 // Grab the selected attachment.
    88                 var attachment = frame.state().get('selection').first();
    89 
    90                 if ( attachment.attributes.height === $el.data('size') && $el.data('size') === attachment.attributes.width ) {
    91                     // Set the value of the hidden input to the attachment id.
    92                     $( '#site_icon_hidden_field').val(attachment.id);
    93                     switchToUpdate(attachment.attributes.url);
    94                     frame.close();
    95                 } else {
    96                     frame.setState( 'cropper' );
    97                 }
    98             });
    99 
    100             frame.open();
    101         });
    102     });
    103 
    104     function switchToUpdate( url ){
    105         // Set site-icon-img src to the url and remove the hidden class.
    106         $( '#site-icon-preview').find('img').not('.browser-preview').each( function(i, img ){
    107             $(img).attr('src', url );
    108         });
    109         $( '#site-icon-preview' ).removeClass( 'hidden' );
    110         // Remove hidden class from remove.
    111         $( '#js-remove-site-icon' ).removeClass( 'hidden' );
    112         // If the button is not in the update state, swap the classes.
    113         if( $( '#choose-from-library-link' ).attr( 'data-state' ) !== '1' ){
    114             var classes = $( '#choose-from-library-link' ).attr( 'class' );
    115             $( '#choose-from-library-link' ).attr( 'class', $( '#choose-from-library-link' ).attr('data-alt-classes') );
    116             $( '#choose-from-library-link' ).attr( 'data-alt-classes', classes );
    117             $( '#choose-from-library-link' ).attr( 'data-state', '1' );
     126
     127                // Set the value of the hidden input to the attachment id.
     128                $hiddenDataField.val( attachment.id );
     129            } else {
     130                frame.setState( 'cropper' );
     131            }
     132        } );
     133
     134        frame.open();
     135    } );
     136
     137    /**
     138     * Update the UI when a site icon is selected.
     139     *
     140     * @since 6.5.0
     141     *
     142     * @param {array} attributes The attributes for the attachment.
     143     */
     144    function switchToUpdate( attributes ) {
     145        var i18nAppAlternativeString, i18nBrowserAlternativeString;
     146
     147        if ( attributes.alt ) {
     148            i18nAppAlternativeString = wp.i18n.sprintf(
     149                /* translators: %s: The selected image alt text. */
     150                wp.i18n.__( 'App icon preview: Current image: %s' ),
     151                attributes.alt
     152            );
     153            i18nBrowserAlternativeString = wp.i18n.sprintf(
     154                /* translators: %s: The selected image alt text. */
     155                wp.i18n.__( 'Browser icon preview: Current image: %s' ),
     156                attributes.alt
     157            );
     158        } else {
     159            i18nAppAlternativeString = wp.i18n.sprintf(
     160                /* translators: %s: The selected image filename. */
     161                wp.i18n.__(
     162                    'App icon preview: The current image has no alternative text. The file name is: %s'
     163                ),
     164                attributes.filename
     165            );
     166            i18nBrowserAlternativeString = wp.i18n.sprintf(
     167                /* translators: %s: The selected image filename. */
     168                wp.i18n.__(
     169                    'Browser icon preview: The current image has no alternative text. The file name is: %s'
     170                ),
     171                attributes.filename
     172            );
    118173        }
    119174
    120         // swap the text of the button
    121         $( '#choose-from-library-link' ).text( $( '#choose-from-library-link' ).attr( 'data-update-text' ) );
     175        // Set site-icon-img src and alternative text to app icon preview.
     176        $appIconPreview.attr( {
     177            src: attributes.url,
     178            alt: i18nAppAlternativeString,
     179        } );
     180
     181        // Set site-icon-img src and alternative text to browser preview.
     182        $browserIconPreview.attr( {
     183            src: attributes.url,
     184            alt: i18nBrowserAlternativeString,
     185        } );
     186
     187        // Remove hidden class from icon preview div and remove button.
     188        $iconPreview.removeClass( 'hidden' );
     189        $removeButton.removeClass( 'hidden' );
     190
     191        // If the choose button is not in the update state, swap the classes.
     192        if ( $chooseButton.attr( 'data-state' ) !== '1' ) {
     193            $chooseButton.attr( {
     194                class: $chooseButton.attr( 'data-alt-classes' ),
     195                'data-alt-classes': $chooseButton.attr( 'class' ),
     196                'data-state': '1',
     197            } );
     198        }
     199
     200        // Swap the text of the choose button.
     201        $chooseButton.text( $chooseButton.attr( 'data-update-text' ) );
    122202    }
    123203
    124     $( '#js-remove-site-icon' ).on( 'click', function() {
    125         $( '#site_icon_hidden_field' ).val( 'false' );
    126         $( '#site-icon-preview' ).toggleClass( 'hidden' );
     204    /**
     205     * Handles the click event of the remove button.
     206     *
     207     * @since 6.5.0
     208     */
     209    $removeButton.on( 'click', function () {
     210        $hiddenDataField.val( 'false' );
    127211        $( this ).toggleClass( 'hidden' );
    128 
    129         var classes = $( '#choose-from-library-link' ).attr( 'class' );
    130         $( '#choose-from-library-link' ).attr( 'class', $( '#choose-from-library-link' ).attr( 'data-alt-classes' ) );
    131         $( '#choose-from-library-link' ).attr( 'data-alt-classes', classes );
    132 
    133         // Swap the text of the button.
    134         $( '#choose-from-library-link' ).text( $( '#choose-from-library-link' ).attr( 'data-choose-text' ) );
    135         // Set the state of the button so it can be changed on new icon.
    136         $( '#choose-from-library-link' ).attr( 'data-state', '');
    137     });
    138 }(jQuery));
     212        $iconPreview.toggleClass( 'hidden' );
     213        $browserIconPreview.attr( {
     214            src: '',
     215            alt: '',
     216        } );
     217        $appIconPreview.attr( {
     218            src: '',
     219            alt: '',
     220        } );
     221
     222        /**
     223         * Resets state to the button, for correct visual style and state.
     224         * Updates the text of the button.
     225         * Sets focus state to the button.
     226         */
     227        $chooseButton
     228            .attr( {
     229                class: $chooseButton.attr( 'data-alt-classes' ),
     230                'data-alt-classes': $chooseButton.attr( 'class' ),
     231                'data-state': '',
     232            } )
     233            .text( $chooseButton.attr( 'data-choose-text' ) )
     234            .trigger( 'focus' );
     235    } );
     236} )( jQuery );
  • trunk/src/wp-admin/css/forms.css

    r57602 r57713  
    790790}
    791791
    792 .button-add-site-icon{
     792.button-add-site-icon {
    793793    width: 100%;
    794794    cursor: pointer;
     
    802802
    803803.button-add-site-icon:focus,
    804 .button-add-site-icon:hover{
    805     background: white;
    806 }
    807 
    808 .site-icon-section .favicon-preview{
     804.button-add-site-icon:hover {
     805    background: #fff;
     806}
     807
     808.site-icon-section .favicon-preview {
    809809    float: left;
    810810}
    811 .site-icon-section .app-icon-preview{
     811.site-icon-section .app-icon-preview {
    812812    float: left;
    813813    margin: 0 20px;
    814814}
    815815
    816 .site-icon-section .site-icon-preview img{
     816.site-icon-section .site-icon-preview img {
    817817    max-width: 100%;
    818818}
    819819
    820 .button-ad-site-icon:focus{
     820.button-add-site-icon:focus {
    821821    background-color: #fff;
    822822    border-color: #3582c4;
  • trunk/src/wp-admin/css/site-icon.css

    r57618 r57713  
    88    position: relative;
    99    max-width: 180px;
    10     float: left;
    1110}
    1211
  • trunk/src/wp-admin/includes/options.php

    r56176 r57713  
    3737    jQuery( function($) {
    3838        var $siteName = $( '#wp-admin-bar-site-name' ).children( 'a' ).first(),
     39            $siteIconPreview = $('#site-icon-preview-site-title'),
    3940            homeURL = ( <?php echo wp_json_encode( get_home_url() ); ?> || '' ).replace( /^(https?:\/\/)?(www\.)?/, '' );
    4041
     
    4849
    4950            $siteName.text( title );
     51            $siteIconPreview.text( title );
    5052        });
    5153
  • trunk/src/wp-admin/options-general.php

    r57618 r57713  
    108108    $classes_for_upload_button = 'upload-button button-add-media button-add-site-icon';
    109109    $classes_for_update_button = 'button';
    110 
    111     $classes_for_avatar = 'avatar avatar-150';
     110    $classes_for_wrapper       = '';
     111
    112112    if ( has_site_icon() ) {
    113         $classes_for_avatar          .= ' has-site-icon';
     113        $classes_for_wrapper         .= ' has-site-icon';
    114114        $classes_for_button           = $classes_for_update_button;
    115115        $classes_for_button_on_change = $classes_for_upload_button;
    116116    } else {
    117         $classes_for_avatar          .= ' hidden';
     117        $classes_for_wrapper         .= ' hidden';
    118118        $classes_for_button           = $classes_for_upload_button;
    119119        $classes_for_button_on_change = $classes_for_update_button;
    120120    }
    121121
    122 
    123     ?>
    124     <div id="site-icon-preview" class="site-icon-preview wp-clearfix <?php echo esc_attr( $classes_for_avatar ); ?>">
     122    // Handle alt text for site icon on page load.
     123    $site_icon_id           = (int) get_option( 'site_icon' );
     124    $app_icon_alt_value     = '';
     125    $browser_icon_alt_value = '';
     126
     127    if ( $site_icon_id ) {
     128        $img_alt            = get_post_meta( $site_icon_id, '_wp_attachment_image_alt', true );
     129        $filename           = wp_basename( get_site_icon_url() );
     130        $app_icon_alt_value = sprintf(
     131            /* translators: %s: The selected image filename. */
     132            __( 'App icon preview: The current image has no alternative text. The file name is: %s' ),
     133            $filename
     134        );
     135
     136        $browser_icon_alt_value = sprintf(
     137            /* translators: %s: The selected image filename. */
     138            __( 'Browser icon preview: The current image has no alternative text. The file name is: %s' ),
     139            $filename
     140        );
     141
     142        if ( $img_alt ) {
     143            $app_icon_alt_value = sprintf(
     144                /* translators: %s: The selected image alt text. */
     145                __( 'App icon preview: Current image: %s' ),
     146                $img_alt
     147            );
     148
     149            $browser_icon_alt_value = sprintf(
     150                /* translators: %s: The selected image alt text. */
     151                __( 'Browser icon preview: Current image: %s' ),
     152                $img_alt
     153            );
     154        }
     155    }
     156    ?>
     157
     158
     159    <div id="site-icon-preview" class="site-icon-preview wp-clearfix settings-page-preview <?php echo esc_attr( $classes_for_wrapper ); ?>">
    125160        <div class="favicon-preview">
    126161            <img src="<?php echo esc_url( admin_url( 'images/' . ( is_rtl() ? 'browser-rtl.png' : 'browser.png' ) ) ); ?>" class="browser-preview" width="182" alt="">
    127162            <div class="favicon">
    128                 <img src="<?php site_icon_url(); ?>" alt="<?php esc_attr_e( 'Preview as a browser icon' ); ?>">
     163                <img id="browser-icon-preview" src="<?php site_icon_url(); ?>" alt="<?php echo esc_attr( $browser_icon_alt_value ); ?>">
    129164            </div>
    130             <span class="browser-title" aria-hidden="true"><?php echo get_bloginfo( 'name' ); ?></span>
     165            <span id="site-icon-preview-site-title" class="browser-title" aria-hidden="true"><?php bloginfo( 'name' ); ?></span>
    131166        </div>
    132         <img class="app-icon-preview" src="<?php site_icon_url(); ?>" alt="<?php esc_attr_e( 'Preview as an app icon' ); ?>">
     167        <img id="app-icon-preview" class="app-icon-preview" src="<?php site_icon_url(); ?>" alt="<?php echo esc_attr( $app_icon_alt_value ); ?>">
    133168    </div>
     169
    134170    <input type="hidden" name="site_icon" id="site_icon_hidden_field" value="<?php form_option( 'site_icon' ); ?>" />
    135171    <div class="action-buttons">
    136172        <button type="button"
    137             id="choose-from-library-link"
     173            id="choose-from-library-button"
    138174            type="button"
    139175            class="<?php echo esc_attr( $classes_for_button ); ?>"
     
    162198
    163199    <p class="description">
    164         <?php _e( 'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. Upload one here!' ); ?>
    165     </p>
    166     <p class="description">
    167200        <?php
    168201            /* translators: %s: Site Icon size in pixels. */
    169             printf( __( 'Site Icons should be square and at least %s pixels.' ), '<strong>512 &times; 512</strong>' );
     202            printf( __( 'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. They should be square and at least %s pixels.' ), '<code>512 &times; 512</code>' );
    170203        ?>
    171204    </p>
  • trunk/src/wp-includes/script-loader.php

    r57699 r57713  
    850850    $scripts->add( 'wp-lists', "/wp-includes/js/wp-lists$suffix.js", array( 'wp-ajax-response', 'jquery-color' ), false, 1 );
    851851
    852     $scripts->add( 'site-icon', '/wp-admin/js/site-icon.js', array( 'jquery', 'jcrop' ), false, 1 );
     852    $scripts->add( 'site-icon', '/wp-admin/js/site-icon.js', array( 'jquery' ), false, 1 );
     853    $scripts->set_translations( 'site-icon' );
    853854
    854855    // WordPress no longer uses or bundles Prototype or script.aculo.us. These are now pulled from an external source.
Note: See TracChangeset for help on using the changeset viewer.