From fb28cd466321285f5c94bfdb5da50b1e1fb6de67 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 5 Oct 2017 02:21:22 +0000 Subject: [PATCH] Customize: Improve the menu creation flow. Often, folks run into two issues when they create new menus: they click "Add a Menu" thinking it will add a new page to their menu, or they forget to assign their new menu to a location, and then wonder why it doesn't show up on their site. This commit rearranges the order of items in the menu panel, and updates the flow for creating a menu by breaking it up into steps. Additionally, more help text has been added to guide people through the process of creating a menu. Also adds default `type` lookups for Panel and Section instances. See #30741. Props bpayton, obenland, westonruter, celloexpessions, afercia, melchoyce, zoonini, michelleweber. Fixes #40104. git-svn-id: https://develop.svn.wordpress.org/trunk@41768 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/customize-controls.css | 12 +- src/wp-admin/css/customize-nav-menus.css | 82 ++- src/wp-admin/js/customize-controls.js | 39 +- src/wp-admin/js/customize-nav-menus.js | 513 +++++++++++------- .../class-wp-customize-control.php | 10 +- .../class-wp-customize-manager.php | 3 +- .../class-wp-customize-nav-menus.php | 126 +++-- .../class-wp-customize-section.php | 3 - ...wp-customize-nav-menu-auto-add-control.php | 11 +- .../class-wp-customize-nav-menu-control.php | 56 +- ...p-customize-nav-menu-locations-control.php | 70 +++ ...ass-wp-customize-nav-menu-name-control.php | 11 +- .../class-wp-customize-nav-menus-panel.php | 4 + .../qunit/wp-admin/js/customize-nav-menus.js | 2 +- 14 files changed, 592 insertions(+), 350 deletions(-) create mode 100644 src/wp-includes/customize/class-wp-customize-nav-menu-locations-control.php diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index aa43287616..767d092049 100644 --- a/src/wp-admin/css/customize-controls.css +++ b/src/wp-admin/css/customize-controls.css @@ -557,11 +557,16 @@ body.trashing #publish-settings { border-bottom-color: #ddd; } -#customize-theme-controls .control-panel-content .control-section:nth-child(2), -#customize-theme-controls .control-panel-nav_menus .control-section:nth-child(3) { +#customize-theme-controls .control-panel-content:not(.control-panel-nav_menus) .control-section:nth-child(2), +#customize-theme-controls .control-panel-nav_menus .control-section-nav_menu, +#customize-theme-controls .control-section-nav_menu_locations .accordion-section-title { border-top: 1px solid #ddd; } +#customize-theme-controls .control-panel-nav_menus .control-section-nav_menu + .control-section-nav_menu { + border-top: none; +} + #customize-theme-controls > ul { margin: 0; } @@ -661,7 +666,8 @@ body.trashing #publish-settings { position: static; } -.customize-section-description-container { +.customize-section-description-container, +.control-section-nav_menu .customize-section-description-container { margin-bottom: 15px; } diff --git a/src/wp-admin/css/customize-nav-menus.css b/src/wp-admin/css/customize-nav-menus.css index a2a811f721..37da06f5a2 100644 --- a/src/wp-admin/css/customize-nav-menus.css +++ b/src/wp-admin/css/customize-nav-menus.css @@ -1,10 +1,22 @@ #customize-theme-controls #accordion-section-menu_locations { position: relative; - margin-bottom: 15px; + margin-top: 15px; } #customize-theme-controls #accordion-section-menu_locations > .accordion-section-title { border-bottom-color: #ddd; + margin-top: 15px; +} + +#customize-theme-controls .customize-section-title-nav_menus-heading, +#customize-theme-controls .customize-section-title-menu_locations-heading, +#customize-theme-controls .customize-section-title-menu_locations-description { + padding: 0 12px 0 12px; +} + +#customize-theme-controls .customize-control-description.customize-section-title-menu_locations-description { + /* Override the default italic style for control descriptions */ + font-style: normal; } .menu-in-location, @@ -27,6 +39,23 @@ line-height: 28px; } +#customize-controls .customize-control-nav_menu_name { + margin-bottom: 12px; +} + +.customize-control-nav_menu_name p:last-of-type { + margin-bottom: 0; +} + +#customize-new-menu-submit { + float: right; + min-width: 85px; +} + +#customize-new-menu-submit-description { + margin: 0; +} + .wp-customizer .menu-item-bar .menu-item-handle, .wp-customizer .menu-item-settings, .wp-customizer .menu-item-settings .description-thin { @@ -175,12 +204,25 @@ border-top: none; } -.menu-settings .customize-control-checkbox label { +.wp-customizer .menu-location-settings { + margin-top: 12px; + border-top: none; +} + +.wp-customizer .control-section-nav_menu .menu-location-settings { + margin-top: 24px; + border-top: 1px solid #ddd; + padding-top: 12px; +} + +.menu-settings .customize-control-checkbox label, +.menu-location-settings .customize-control-checkbox label { line-height: 1; } /* @todo update selector or potentially remove */ -.menu-settings .customize-control.customize-control-checkbox { +.menu-settings .customize-control.customize-control-checkbox, +.menu-location-settings .customize-control.customize-control-checkbox { margin-bottom: 8px; /* Override collapsing at smaller viewports. */ } @@ -738,27 +780,16 @@ body.adding-menu-items #customize-preview iframe { /* @todo update selector */ #accordion-section-add_menu { margin: 15px 12px; - overflow: hidden; + text-align: right; } -.new-menu-section-content { - display: none; - padding: 15px 0 0 0; - clear: both; +#accordion-section-add_menu h3, +#accordion-section-add_menu .customize-add-menu-button { + margin: 0; } -/* @todo update selector */ -#accordion-section-add_menu .accordion-section-title { - padding-left: 45px; -} - -/* @todo update selector */ -#accordion-section-add_menu .accordion-section-title:before { - font: normal 20px/1 dashicons; - position: absolute; - top: 12px; - left: 14px; - content: "\f132"; +#accordion-section-add_menu .customize-add-menu-button { + font-weight: normal; } #create-new-menu-submit { @@ -772,6 +803,10 @@ body.adding-menu-items #customize-preview iframe { width: 100%; } +.assigned-menu-locations-title p { + margin: 0 0 8px 0; +} + li.assigned-to-menu-location .menu-delete-item { display: none; } @@ -808,7 +843,12 @@ li.assigned-to-menu-location .add-new-menu-item { margin-bottom: 0; } -.customize-control-nav_menu { +.customize-control-nav_menu .new-menu-item-invitation { + margin-top: 0; + margin-bottom: 0; +} + +.customize-control-nav_menu .customize-control-nav_menu-buttons { margin-top: 12px; } diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 010848d257..1656c4565d 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -1234,8 +1234,21 @@ * @param {object} [options.params] - Deprecated wrapper for the above properties. */ initialize: function ( id, options ) { - var section = this; - Container.prototype.initialize.call( section, id, options ); + var section = this, params; + params = options.params || options; + + // Look up the type if one was not supplied. + if ( ! params.type ) { + _.find( api.sectionConstructor, function( Constructor, type ) { + if ( Constructor === section.constructor ) { + params.type = type; + return true; + } + return false; + } ); + } + + Container.prototype.initialize.call( section, id, params ); section.id = id; section.panel = new api.Value(); @@ -2507,8 +2520,22 @@ * @param {object} [options.params] - Deprecated wrapper for the above properties. */ initialize: function ( id, options ) { - var panel = this; - Container.prototype.initialize.call( panel, id, options ); + var panel = this, params; + params = options.params || options; + + // Look up the type if one was not supplied. + if ( ! params.type ) { + _.find( api.panelConstructor, function( Constructor, type ) { + if ( Constructor === panel.constructor ) { + params.type = type; + return true; + } + return false; + } ); + } + + Container.prototype.initialize.call( panel, id, params ); + panel.embed(); panel.deferred.embedded.done( function () { panel.ready(); @@ -3095,7 +3122,7 @@ * @param {string} [options.active=true] - Whether the control is active. * @param {string} options.section - The ID of the section the control belongs to. * @param {mixed} [options.setting] - The ID of the main setting or an instance of this setting. - * @param {mixed} options.settings - An object with keys (e.g. default) that maps to setting IDs or Setting/Value objects, or an array of setting IDs or Setting/Value objects. + * @param {mixed} options.settings - An object with keys (e.g. default) that maps to setting IDs or Setting/Value objects, or an array of setting IDs or Setting/Value objects. * @param {mixed} options.settings.default - The ID of the setting the control relates to. * @param {string} options.settings.data - @todo Is this used? * @param {string} options.label - Label. @@ -3107,6 +3134,8 @@ defaultActiveArguments: { duration: 'fast', completeCallback: $.noop }, defaults: { + label: '', + description: '', active: true, priority: 10 }, diff --git a/src/wp-admin/js/customize-nav-menus.js b/src/wp-admin/js/customize-nav-menus.js index 258e1bb376..d0b73c59cd 100644 --- a/src/wp-admin/js/customize-nav-menus.js +++ b/src/wp-admin/js/customize-nav-menus.js @@ -811,6 +811,18 @@ panel.container.find( '.hide-column-tog' ).click( function() { panel.saveManageColumnsState(); }); + + // Wait until after construction to patch the UI + _.defer( function () { + + panel.contentContainer.find( '#accordion-section-menu_locations' ).prepend( + wp.template( 'nav-menu-locations-header' )( api.Menus.data ) + ); + + panel.contentContainer.find( '#accordion-section-add_menu .accordion-section-title' ).replaceWith( + wp.template( 'nav-menu-create-menu-section-title' ) + ); + } ); }, /** @@ -961,7 +973,16 @@ }, populateControls: function() { - var section = this, menuNameControlId, menuAutoAddControlId, menuControl, menuNameControl, menuAutoAddControl; + var section = this, + menuNameControlId, + menuLocationsControlId, + menuAutoAddControlId, + menuDeleteControlId, + menuControl, + menuNameControl, + menuLocationsControl, + menuAutoAddControl, + menuDeleteControl; // Add the control for managing the menu name. menuNameControlId = section.id + '[name]'; @@ -996,6 +1017,22 @@ menuControl.active.set( true ); } + // Add the menu locations control. + menuLocationsControlId = section.id + '[locations]'; + menuLocationsControl = api.control( menuLocationsControlId ); + if ( ! menuLocationsControl ) { + menuLocationsControl = new api.controlConstructor.nav_menu_locations( menuLocationsControlId, { + section: section.id, + priority: 999, + settings: { + 'default': section.id + }, + menu_id: section.params.menu_id + } ); + api.control.add( menuLocationsControl.id, menuLocationsControl ); + menuControl.active.set( true ); + } + // Add the control for managing the menu auto_add. menuAutoAddControlId = section.id + '[auto_add]'; menuAutoAddControl = api.control( menuAutoAddControlId ); @@ -1004,7 +1041,7 @@ type: 'nav_menu_auto_add', label: '', section: section.id, - priority: 999, + priority: 1000, settings: { 'default': section.id } @@ -1013,6 +1050,25 @@ menuAutoAddControl.active.set( true ); } + // Add the control for deleting the menu + menuDeleteControlId = section.id + '[delete]'; + menuDeleteControl = api.control( menuDeleteControlId ); + if ( ! menuDeleteControl ) { + menuDeleteControl = new api.Control( menuDeleteControlId, { + section: section.id, + priority: 1001, + templateId: 'nav-menu-delete-button' + } ); + api.control.add( menuDeleteControl.id, menuDeleteControl ); + menuDeleteControl.active.set( true ); + menuDeleteControl.deferred.embedded.done( function () { + menuDeleteControl.container.find( 'button' ).on( 'click', function() { + var menuId = section.params.menu_id; + var menuControl = api.Menus.getMenuControl( menuId ); + menuControl.setting.set( false ); + }); + } ); + } }, /** @@ -1093,7 +1149,6 @@ * wp.customize.Menus.NewMenuSection * * Customizer section for new menus. - * Note that 'new_menu' must match the WP_Customize_New_Menu_Section::$type. * * @constructor * @augments wp.customize.Section @@ -1106,51 +1161,164 @@ * @since 4.3.0 */ attachEvents: function() { - var section = this; - this.container.on( 'click', '.add-menu-toggle', function() { - if ( section.expanded() ) { - section.collapse(); - } else { - section.expand(); - } + var section = this, + container = section.container, + contentContainer = section.contentContainer; + + /* + * We have to manually handle section expanded because we do not + * apply the `accordion-section-title` class to this button-driven section. + */ + container.on( 'click', '.customize-add-menu-button', function() { + section.expand(); }); + + contentContainer.on( 'keydown', '.menu-name-field', function( event ) { + if ( 13 === event.which ) { // Enter. + section.submit(); + } + } ); + contentContainer.on( 'click', '#customize-new-menu-submit', function( event ) { + section.submit(); + event.stopPropagation(); + event.preventDefault(); + } ); + + api.Section.prototype.attachEvents.apply( this, arguments ); }, /** - * Update UI to reflect expanded state. + * Set up the control. * - * @since 4.1.0 - * - * @param {Boolean} expanded + * @since 4.9.0 */ - onChangeExpanded: function( expanded ) { + ready: function() { + this.populateControls(); + }, + + /** + * Create the controls for this section. + * + * @since 4.9.0 + */ + populateControls: function() { var section = this, - button = section.container.find( '.add-menu-toggle' ), - content = section.contentContainer, - customizer = section.headContainer.closest( '.wp-full-overlay-sidebar-content' ); - if ( expanded ) { - button.addClass( 'open' ); - button.attr( 'aria-expanded', 'true' ); - content.slideDown( 'fast', function() { - customizer.scrollTop( customizer.height() ); - }); - } else { - button.removeClass( 'open' ); - button.attr( 'aria-expanded', 'false' ); - content.slideUp( 'fast' ); - content.find( '.menu-name-field' ).removeClass( 'invalid' ); + menuNameControlId, + menuLocationsControlId, + newMenuSubmitControlId, + menuNameControl, + menuLocationsControl, + newMenuSubmitControl; + + menuNameControlId = section.id + '[name]'; + menuNameControl = api.control( menuNameControlId ); + if ( ! menuNameControl ) { + menuNameControl = new api.controlConstructor.nav_menu_name( menuNameControlId, { + label: api.Menus.data.l10n.menuNameLabel, + description: api.Menus.data.l10n.newMenuNameDescription, + section: section.id, + priority: 0 + } ); + api.control.add( menuNameControl.id, menuNameControl ); + menuNameControl.active.set( true ); + } + + menuLocationsControlId = section.id + '[locations]'; + menuLocationsControl = api.control( menuLocationsControlId ); + if ( ! menuLocationsControl ) { + menuLocationsControl = new api.controlConstructor.nav_menu_locations( menuLocationsControlId, { + section: section.id, + priority: 1, + menu_id: '' + } ); + api.control.add( menuLocationsControlId, menuLocationsControl ); + menuLocationsControl.active.set( true ); + } + + newMenuSubmitControlId = section.id + '[submit]'; + newMenuSubmitControl = api.control( newMenuSubmitControlId ); + if ( !newMenuSubmitControl ) { + newMenuSubmitControl = new api.Control( newMenuSubmitControlId, { + section: section.id, + priority: 1, + templateId: 'nav-menu-submit-new-button' + } ); + api.control.add( newMenuSubmitControlId, newMenuSubmitControl ); + newMenuSubmitControl.active.set( true ); } }, /** - * Find the content element. + * Create the new menu with name and location supplied by the user. * - * @since 4.7.0 - * - * @returns {jQuery} Content UL element. + * @since 4.9.0 */ - getContent: function() { - return this.container.find( 'ul:first' ); + submit: function() { + var section = this, + contentContainer = section.contentContainer, + nameInput = contentContainer.find( '.menu-name-field' ).first(), + name = nameInput.val(), + menuSection, + customizeId, + placeholderId = api.Menus.generatePlaceholderAutoIncrementId(); + + if ( ! name ) { + nameInput.addClass( 'invalid' ); + nameInput.focus(); + return; + } + + customizeId = 'nav_menu[' + String( placeholderId ) + ']'; + + // Register the menu control setting. + api.create( customizeId, customizeId, {}, { + type: 'nav_menu', + transport: api.Menus.data.settingTransport, + previewer: api.previewer + } ); + api( customizeId ).set( $.extend( + {}, + api.Menus.data.defaultSettingValues.nav_menu, + { + name: name + } + ) ); + + /* + * Add the menu section (and its controls). + * Note that this will automatically create the required controls + * inside via the Section's ready method. + */ + menuSection = new api.Menus.MenuSection( customizeId, { + panel: 'nav_menus', + title: displayNavMenuName( name ), + customizeAction: api.Menus.data.l10n.customizingMenus, + priority: 10, + menu_id: placeholderId + } ); + api.section.add( customizeId, menuSection ); + + // Clear name field. + nameInput.val( '' ); + nameInput.removeClass( 'invalid' ); + + contentContainer.find( '.assigned-menu-location input[type=checkbox]' ).each( function() { + var checkbox = $( this ), + navMenuLocationSetting; + + if ( checkbox.prop( 'checked' ) ) { + navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' ); + navMenuLocationSetting.set( placeholderId ); + + // Reset state for next new menu + checkbox.prop( 'checked', false ); + } + } ); + + wp.a11y.speak( api.Menus.data.l10n.menuAdded ); + + // Focus on the new menu section. + api.section( customizeId ).focus(); // @todo should we focus on the new menu's control and open the add-items panel? Thinking user flow... } }); @@ -1512,6 +1680,8 @@ wp.a11y.speak( api.Menus.data.l10n.itemDeleted ); $adjacentFocusTarget.focus(); // keyboard accessibility } ); + + control.setting.set( false ); } ); }, @@ -2034,45 +2204,88 @@ api.Menus.MenuNameControl = api.Control.extend({ ready: function() { - var control = this, - settingValue = control.setting(); + var control = this; - /* - * Since the control is not registered in PHP, we need to prevent the - * preview's sending of the activeControls to result in this control - * being deactivated. - */ - control.active.validate = function() { - var value, section = api.section( control.section() ); - if ( section ) { - value = section.active(); - } else { - value = false; - } - return value; - }; - - control.nameElement = new api.Element( control.container.find( '.menu-name-field' ) ); - - control.nameElement.bind(function( value ) { + if ( control.setting ) { var settingValue = control.setting(); - if ( settingValue && settingValue.name !== value ) { - settingValue = _.clone( settingValue ); - settingValue.name = value; - control.setting.set( settingValue ); - } - }); - if ( settingValue ) { - control.nameElement.set( settingValue.name ); - } - control.setting.bind(function( object ) { - if ( object ) { - control.nameElement.set( object.name ); + control.nameElement = new api.Element( control.container.find( '.menu-name-field' ) ); + + control.nameElement.bind(function( value ) { + var settingValue = control.setting(); + if ( settingValue && settingValue.name !== value ) { + settingValue = _.clone( settingValue ); + settingValue.name = value; + control.setting.set( settingValue ); + } + }); + if ( settingValue ) { + control.nameElement.set( settingValue.name ); } + + control.setting.bind(function( object ) { + if ( object ) { + control.nameElement.set( object.name ); + } + }); + } + } + }); + + /** + * wp.customize.Menus.MenuLocationsControl + * + * Customizer control for a nav menu's locations. + * + * @since 4.9.0 + * @constructor + * @augments wp.customize.Control + */ + api.Menus.MenuLocationsControl = api.Control.extend({ + + /** + * Set up the control. + * + * @since 4.9.0 + */ + ready: function () { + var control = this; + + control.container.find( '.assigned-menu-location' ).each(function() { + var container = $( this ), + checkbox = container.find( 'input[type=checkbox]' ), + element = new api.Element( checkbox ), + navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' ), + isNewMenu = control.params.menu_id === '', + updateCheckbox = isNewMenu ? _.noop : function( checked ) { + element.set( checked ); + }, + updateSetting = isNewMenu ? _.noop : function( checked ) { + navMenuLocationSetting.set( checked ? control.params.menu_id : 0 ); + }, + updateSelectedMenuLabel = function( selectedMenuId ) { + var menuSetting = api( 'nav_menu[' + String( selectedMenuId ) + ']' ); + if ( ! selectedMenuId || ! menuSetting || ! menuSetting() ) { + container.find( '.theme-location-set' ).hide(); + } else { + container.find( '.theme-location-set' ).show().find( 'span' ).text( displayNavMenuName( menuSetting().name ) ); + } + }; + + updateCheckbox( navMenuLocationSetting.get() === control.params.menu_id ); + + checkbox.on( 'change', function() { + // Note: We can't use element.bind( function( checked ){ ... } ) here because it will trigger a change as well. + updateSetting( this.checked ); + } ); + + navMenuLocationSetting.bind( function( selectedMenuId ) { + updateCheckbox( selectedMenuId === control.params.menu_id ); + updateSelectedMenuLabel( selectedMenuId ); + } ); + updateSelectedMenuLabel( navMenuLocationSetting.get() ); }); } - }); /** @@ -2180,7 +2393,6 @@ } ); this._setupAddition(); - this._setupLocations(); this._setupTitle(); // Add menu to Custom Menu widgets. @@ -2210,6 +2422,15 @@ select.append( new Option( name, menuId ) ); } } + + /* + * Wait for menu items to be added. + * Ideally, we'd bind to an event indicating construction is complete, + * but deferring appears to be the best option today. + */ + _.defer( function () { + control.updateInvitationVisibility(); + } ); }, /** @@ -2235,11 +2456,6 @@ }); } } ); - - control.container.find( '.menu-delete-item .button-link-delete' ).on( 'click', function( event ) { - event.preventDefault(); - control.setting.set( false ); - }); }, /** @@ -2393,43 +2609,6 @@ widgetTemplate.find( 'option[value=' + String( menuId ) + ']' ).remove(); }, - // Setup theme location checkboxes. - _setupLocations: function() { - var control = this; - - control.container.find( '.assigned-menu-location' ).each(function() { - var container = $( this ), - checkbox = container.find( 'input[type=checkbox]' ), - element, - updateSelectedMenuLabel, - navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' ); - - updateSelectedMenuLabel = function( selectedMenuId ) { - var menuSetting = api( 'nav_menu[' + String( selectedMenuId ) + ']' ); - if ( ! selectedMenuId || ! menuSetting || ! menuSetting() ) { - container.find( '.theme-location-set' ).hide(); - } else { - container.find( '.theme-location-set' ).show().find( 'span' ).text( displayNavMenuName( menuSetting().name ) ); - } - }; - - element = new api.Element( checkbox ); - element.set( navMenuLocationSetting.get() === control.params.menu_id ); - - checkbox.on( 'change', function() { - // Note: We can't use element.bind( function( checked ){ ... } ) here because it will trigger a change as well. - navMenuLocationSetting.set( this.checked ? control.params.menu_id : 0 ); - } ); - - navMenuLocationSetting.bind(function( selectedMenuId ) { - element.set( selectedMenuId === control.params.menu_id ); - updateSelectedMenuLabel( selectedMenuId ); - }); - updateSelectedMenuLabel( navMenuLocationSetting.get() ); - - }); - }, - /** * Update Section Title as menu name is changed. */ @@ -2607,6 +2786,7 @@ currentAbsolutePosition: 0 } ); + menuControl.updateInvitationVisibility( menuItemControls ); menuControl.container.find( '.reorder-toggle' ).toggle( menuItemControls.length > 1 ); }, @@ -2681,103 +2861,22 @@ wp.a11y.speak( api.Menus.data.l10n.itemAdded ); return menuItemControl; + }, + + /** + * Show an invitation to add new menu items when there are no menu items. + * + * @since 4.9.0 + * + * @param {wp.customize.controlConstructor.nav_menu_item[]} optionalMenuItemControls + */ + updateInvitationVisibility: function ( optionalMenuItemControls ) { + var menuItemControls = optionalMenuItemControls || this.getMenuItemControls(); + + this.container.find( '.new-menu-item-invitation' ).toggle( menuItemControls.length === 0 ); } } ); - /** - * wp.customize.Menus.NewMenuControl - * - * Customizer control for creating new menus and handling deletion of existing menus. - * Note that 'new_menu' must match the WP_Customize_New_Menu_Control::$type. - * - * @constructor - * @augments wp.customize.Control - */ - api.Menus.NewMenuControl = api.Control.extend({ - /** - * Set up the control. - */ - ready: function() { - this._bindHandlers(); - }, - - _bindHandlers: function() { - var self = this, - name = $( '#customize-control-new_menu_name input' ), - submit = $( '#create-new-menu-submit' ); - name.on( 'keydown', function( event ) { - if ( 13 === event.which ) { // Enter. - self.submit(); - } - } ); - submit.on( 'click', function( event ) { - self.submit(); - event.stopPropagation(); - event.preventDefault(); - } ); - }, - - /** - * Create the new menu with the name supplied. - */ - submit: function() { - - var control = this, - container = control.container.closest( '.accordion-section-new-menu' ), - nameInput = container.find( '.menu-name-field' ).first(), - name = nameInput.val(), - menuSection, - customizeId, - placeholderId = api.Menus.generatePlaceholderAutoIncrementId(); - - if ( ! name ) { - nameInput.addClass( 'invalid' ); - nameInput.focus(); - return; - } - - customizeId = 'nav_menu[' + String( placeholderId ) + ']'; - - // Register the menu control setting. - api.create( customizeId, customizeId, {}, { - type: 'nav_menu', - transport: api.Menus.data.settingTransport, - previewer: api.previewer - } ); - api( customizeId ).set( $.extend( - {}, - api.Menus.data.defaultSettingValues.nav_menu, - { - name: name - } - ) ); - - /* - * Add the menu section (and its controls). - * Note that this will automatically create the required controls - * inside via the Section's ready method. - */ - menuSection = new api.Menus.MenuSection( customizeId, { - panel: 'nav_menus', - title: displayNavMenuName( name ), - customizeAction: api.Menus.data.l10n.customizingMenus, - type: 'nav_menu', - priority: 10, - menu_id: placeholderId - } ); - api.section.add( menuSection ); - - // Clear name field. - nameInput.val( '' ); - nameInput.removeClass( 'invalid' ); - - wp.a11y.speak( api.Menus.data.l10n.menuAdded ); - - // Focus on the new menu section. - api.section( customizeId ).focus(); // @todo should we focus on the new menu's control and open the add-items panel? Thinking user flow... - } - }); - /** * Extends wp.customize.controlConstructor with control constructor for * menu_location, menu_item, nav_menu, and new_menu. @@ -2787,8 +2886,8 @@ nav_menu_item: api.Menus.MenuItemControl, nav_menu: api.Menus.MenuControl, nav_menu_name: api.Menus.MenuNameControl, - nav_menu_auto_add: api.Menus.MenuAutoAddControl, - new_menu: api.Menus.NewMenuControl + nav_menu_locations: api.Menus.MenuLocationsControl, + nav_menu_auto_add: api.Menus.MenuAutoAddControl }); /** diff --git a/src/wp-includes/class-wp-customize-control.php b/src/wp-includes/class-wp-customize-control.php index e856f71ad2..5f60b81c04 100644 --- a/src/wp-includes/class-wp-customize-control.php +++ b/src/wp-includes/class-wp-customize-control.php @@ -769,16 +769,16 @@ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location */ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' ); +/** + * WP_Customize_Nav_Menu_Locations_Control class. + */ +require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-locations-control.php' ); + /** * WP_Customize_Nav_Menu_Auto_Add_Control class. */ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' ); -/** - * WP_Customize_New_Menu_Control class. - */ -require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' ); - /** * WP_Customize_Date_Time_Control class. */ diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 6dd4fae3e7..1f9847f4ad 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -315,8 +315,8 @@ final class WP_Customize_Manager { require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' ); + require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-locations-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' ); - require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); @@ -324,7 +324,6 @@ final class WP_Customize_Manager { require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' ); - require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-section.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-custom-css-setting.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' ); diff --git a/src/wp-includes/class-wp-customize-nav-menus.php b/src/wp-includes/class-wp-customize-nav-menus.php index 06228b2b74..ed039507ad 100644 --- a/src/wp-includes/class-wp-customize-nav-menus.php +++ b/src/wp-includes/class-wp-customize-nav-menus.php @@ -395,39 +395,49 @@ final class WP_Customize_Nav_Menus { $temp_nav_menu_setting = new WP_Customize_Nav_Menu_Setting( $this->manager, 'nav_menu[-1]' ); $temp_nav_menu_item_setting = new WP_Customize_Nav_Menu_Item_Setting( $this->manager, 'nav_menu_item[-1]' ); + $num_locations = count( get_registered_nav_menus() ); + if ( 1 === $num_locations ) { + $locations_description = __( 'Your theme can display menus in one location.' ); + } else { + /* translators: %s: number of menu locations */ + $locations_description = sprintf( _n( 'Your theme can display menus in %s location.', 'Your theme can display menus in %s locations.', $num_locations ), number_format_i18n( $num_locations ) ); + } + // Pass data to JS. $settings = array( 'allMenus' => wp_get_nav_menus(), 'itemTypes' => $this->available_item_types(), 'l10n' => array( - 'untitled' => _x( '(no label)', 'missing menu item navigation label' ), - 'unnamed' => _x( '(unnamed)', 'Missing menu name.' ), - 'custom_label' => __( 'Custom Link' ), - 'page_label' => get_post_type_object( 'page' )->labels->singular_name, - /* translators: %s: menu location */ - 'menuLocation' => _x( '(Currently set to: %s)', 'menu' ), - 'menuNameLabel' => __( 'Menu Name' ), - 'itemAdded' => __( 'Menu item added' ), - 'itemDeleted' => __( 'Menu item deleted' ), - 'menuAdded' => __( 'Menu created' ), - 'menuDeleted' => __( 'Menu deleted' ), - 'movedUp' => __( 'Menu item moved up' ), - 'movedDown' => __( 'Menu item moved down' ), - 'movedLeft' => __( 'Menu item moved out of submenu' ), - 'movedRight' => __( 'Menu item is now a sub-item' ), + 'untitled' => _x( '(no label)', 'missing menu item navigation label' ), + 'unnamed' => _x( '(unnamed)', 'Missing menu name.' ), + 'custom_label' => __( 'Custom Link' ), + 'page_label' => get_post_type_object( 'page' )->labels->singular_name, + /* translators: %s: menu location */ + 'menuLocation' => _x( '(Currently set to: %s)', 'menu' ), + 'locationsDescription' => $locations_description, + 'menuNameLabel' => __( 'Menu Name' ), + 'newMenuNameDescription' => __( 'If your theme has multiple menus, giving them clear names will help you manage them.' ), + 'itemAdded' => __( 'Menu item added' ), + 'itemDeleted' => __( 'Menu item deleted' ), + 'menuAdded' => __( 'Menu created' ), + 'menuDeleted' => __( 'Menu deleted' ), + 'movedUp' => __( 'Menu item moved up' ), + 'movedDown' => __( 'Menu item moved down' ), + 'movedLeft' => __( 'Menu item moved out of submenu' ), + 'movedRight' => __( 'Menu item is now a sub-item' ), /* translators: ▸ is the unicode right-pointing triangle, and %s is the section title in the Customizer */ - 'customizingMenus' => sprintf( __( 'Customizing ▸ %s' ), esc_html( $this->manager->get_panel( 'nav_menus' )->title ) ), + 'customizingMenus' => sprintf( __( 'Customizing ▸ %s' ), esc_html( $this->manager->get_panel( 'nav_menus' )->title ) ), /* translators: %s: title of menu item which is invalid */ - 'invalidTitleTpl' => __( '%s (Invalid)' ), + 'invalidTitleTpl' => __( '%s (Invalid)' ), /* translators: %s: title of menu item in draft status */ - 'pendingTitleTpl' => __( '%s (Pending)' ), - 'itemsFound' => __( 'Number of items found: %d' ), - 'itemsFoundMore' => __( 'Additional items found: %d' ), - 'itemsLoadingMore' => __( 'Loading more results... please wait.' ), - 'reorderModeOn' => __( 'Reorder mode enabled' ), - 'reorderModeOff' => __( 'Reorder mode closed' ), - 'reorderLabelOn' => esc_attr__( 'Reorder menu items' ), - 'reorderLabelOff' => esc_attr__( 'Close reorder mode' ), + 'pendingTitleTpl' => __( '%s (Pending)' ), + 'itemsFound' => __( 'Number of items found: %d' ), + 'itemsFoundMore' => __( 'Additional items found: %d' ), + 'itemsLoadingMore' => __( 'Loading more results... please wait.' ), + 'reorderModeOn' => __( 'Reorder mode enabled' ), + 'reorderModeOff' => __( 'Reorder mode closed' ), + 'reorderLabelOn' => esc_attr__( 'Reorder menu items' ), + 'reorderLabelOff' => esc_attr__( 'Close reorder mode' ), ), 'settingTransport' => 'postMessage', 'phpIntMax' => PHP_INT_MAX, @@ -537,6 +547,7 @@ final class WP_Customize_Nav_Menus { $this->manager->register_panel_type( 'WP_Customize_Nav_Menus_Panel' ); $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Control' ); $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Name_Control' ); + $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Locations_Control' ); $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Auto_Add_Control' ); $this->manager->register_control_type( 'WP_Customize_Nav_Menu_Item_Control' ); @@ -558,23 +569,24 @@ final class WP_Customize_Nav_Menus { // Menu locations. $locations = get_registered_nav_menus(); - $num_locations = count( array_keys( $locations ) ); + $num_locations = count( $locations ); if ( 1 == $num_locations ) { - $description = '

' . __( 'Your theme supports one menu. Select which menu you would like to use.' ) . '

'; + $description = '

' . __( 'Your theme can display menus in one location. Select which menu you would like to use.' ) . '

'; } else { /* translators: %s: number of menu locations */ - $description = '

' . sprintf( _n( 'Your theme supports %s menu. Select which menu appears in each location.', 'Your theme supports %s menus. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) ) . '

'; + $description = '

' . sprintf( _n( 'Your theme can display menus in %s location. Select which menu you would like to use.', 'Your theme can display menus in %s locations. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) ) . '

'; } + if ( current_theme_supports( 'widgets' ) ) { /* translators: URL to the widgets panel of the customizer */ - $description .= '

' . sprintf( __( 'You can also place menus in widget areas with the “Custom Menu” widget.' ), "javascript:wp.customize.panel( 'widgets' ).focus();" ) . '

'; + $description .= '

' . sprintf( __( 'If your theme has widget areas, you can also add menus there. Visit the Widgets panel and add a “Custom Menu widget” to display a menu in a sidebar or footer.' ), "javascript:wp.customize.panel( 'widgets' ).focus();" ) . '

'; } $this->manager->add_section( 'menu_locations', array( - 'title' => __( 'Menu Locations' ), - 'panel' => 'nav_menus', - 'priority' => 5, - 'description' => $description, + 'title' => __( 'View All Locations' ), + 'panel' => 'nav_menus', + 'priority' => 30, + 'description' => $description ) ); $choices = array( '0' => __( '— Select —' ) ); @@ -667,27 +679,13 @@ final class WP_Customize_Nav_Menus { } // Add the add-new-menu section and controls. - $this->manager->add_section( new WP_Customize_New_Menu_Section( $this->manager, 'add_menu', array( - 'title' => __( 'Add a Menu' ), + $this->manager->add_section( 'add_menu', array( + 'type' => 'new_menu', + 'title' => __( 'New Menu' ), 'panel' => 'nav_menus', - 'priority' => 999, - ) ) ); - - $this->manager->add_control( 'new_menu_name', array( - 'label' => __( 'New menu name' ), - 'section' => 'add_menu', - 'type' => 'text', - 'settings' => array(), - 'input_attrs' => array( - 'class' => 'menu-name-field', - ), + 'priority' => 20, ) ); - $this->manager->add_control( new WP_Customize_New_Menu_Control( $this->manager, 'create_new_menu', array( - 'section' => 'add_menu', - 'settings' => array(), - ) ) ); - $this->manager->add_setting( new WP_Customize_Filter_Setting( $this->manager, 'nav_menus_created_posts', array( 'transport' => 'postMessage', 'type' => 'option', // To prevent theme prefix in changeset. @@ -925,6 +923,32 @@ final class WP_Customize_Nav_Menus { ?> + + + + + + + + + <# var elementId = _.uniqueId( 'customize-nav-menu-auto-add-control-' ); #> - + + + + - <# var elementId; #> - - -

- - - - - - + + +

+ <# var elementId; #> + + + <# if ( data.description ) { #> +

{{ data.description }}

+ <# } #> render_screen_options(); ?> + +