diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-admin/js/customize-nav-menus.js --- a/wp/wp-admin/js/customize-nav-menus.js Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-admin/js/customize-nav-menus.js Tue Dec 15 13:49:49 2020 +0100 @@ -95,7 +95,7 @@ * @since 4.7.0 * @alias wp.customize.Menus.insertAutoDraftPost * - * @param {object} params - Parameters for the draft post to create. + * @param {Object} params - Parameters for the draft post to create. * @param {string} params.post_type - Post type to add. * @param {string} params.post_title - Post title to use. * @return {jQuery.promise} Promise resolved with the added post. @@ -203,9 +203,11 @@ _.bindAll( this, 'close' ); - // If the available menu items panel is open and the customize controls are - // interacted with (other than an item being deleted), then close the - // available menu items panel. Also close on back button click. + /* + * If the available menu items panel is open and the customize controls + * are interacted with (other than an item being deleted), then close + * the available menu items panel. Also close on back button click. + */ $( '#customize-controls, .customize-section-back' ).on( 'click keydown', function( e ) { var isDeleteBtn = $( e.target ).is( '.item-delete, .item-delete *' ), isAddNewBtn = $( e.target ).is( '.add-new-menu-item, .add-new-menu-item *' ); @@ -214,9 +216,9 @@ } } ); - // Clear the search results and trigger a `keyup` event to fire a new search. + // Clear the search results and trigger an `input` event to fire a new search. this.$clearResults.on( 'click', function() { - self.$search.val( '' ).focus().trigger( 'keyup' ); + self.$search.val( '' ).focus().trigger( 'input' ); } ); this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() { @@ -252,7 +254,7 @@ } }); - // Close the panel if the URL in the preview changes + // Close the panel if the URL in the preview changes. api.previewer.bind( 'url', this.close ); self.delegateEvents(); @@ -385,9 +387,9 @@ * @since 4.7.0 Changed function signature to take list of item types instead of single type/object. * @access private * - * @param {Array.} itemTypes List of objects containing type and key. + * @param {Array.} itemTypes List of objects containing type and key. * @param {string} deprecated Formerly the object parameter. - * @returns {void} + * @return {void} */ loadItems: function( itemTypes, deprecated ) { var self = this, _itemTypes, requestItemTypes = [], params, request, itemTemplate, availableMenuItemContainers = {}; @@ -496,7 +498,7 @@ // Submit handler for keypress and click on menu item. _submit: function( event ) { - // Only proceed with keypress if it is Enter or Spacebar + // Only proceed with keypress if it is Enter or Spacebar. if ( 'keypress' === event.type && ( 13 !== event.which && 32 !== event.which ) ) { return; } @@ -584,7 +586,7 @@ this.currentMenuControl.addItemToMenu( menuItem ); // Reset the custom link form. - itemUrl.val( 'http://' ); + itemUrl.val( '' ).attr( 'placeholder', 'https://' ); itemName.val( '' ); }, @@ -595,7 +597,7 @@ * @private * * @param {jQuery.Event} event Event. - * @returns {void} + * @return {void} */ _submitNew: function( event ) { var container; @@ -621,7 +623,7 @@ * @private * * @param {jQuery} container - * @returns {void} + * @return {void} */ submitNew: function( container ) { var panel = this, @@ -717,7 +719,7 @@ this.$search.focus(); }, - // Closes the panel + // Closes the panel. close: function( options ) { options = options || {}; @@ -731,7 +733,7 @@ $( 'body' ).removeClass( 'adding-menu-items' ); $( '#available-menu-items .menu-item-handle.item-added' ).removeClass( 'item-added' ); - this.$search.val( '' ).trigger( 'keyup' ); + this.$search.val( '' ).trigger( 'input' ); }, // Add a few keyboard enhancements to the panel. @@ -741,7 +743,7 @@ isBackTab = ( 9 === event.which && event.shiftKey ), isSearchFocused = $( event.target ).is( this.$search ); - // If enter pressed but nothing entered, don't do anything + // If enter pressed but nothing entered, don't do anything. if ( isEnter && ! this.$search.val() ) { return; } @@ -781,7 +783,7 @@ } event.preventDefault(); - // Hide description + // Hide description. if ( content.not( ':hidden' ) ) { content.slideUp( 'fast' ); help.attr( 'aria-expanded', 'false' ); @@ -802,7 +804,7 @@ return false; } ); - // Help toggle + // Help toggle. help.on( 'click keydown', function( event ) { if ( api.utils.isKeydownButNotEnterEvent( event ) ) { return; @@ -843,7 +845,7 @@ * @since 4.3.0 * @private * - * @returns {void} + * @return {void} */ saveManageColumnsState: _.debounce( function() { var panel = this; @@ -877,7 +879,7 @@ * @since 4.3.0 * @private * - * @returns {Array} Fields (columns) that are hidden. + * @return {Array} Fields (columns) that are hidden. */ hidden: function() { return $( '.hide-column-tog' ).not( ':checked' ).map( function() { @@ -903,7 +905,7 @@ * * @since 4.3.0 * - * @param {String} id + * @param {string} id * @param {Object} options */ initialize: function( id, options ) { @@ -973,7 +975,7 @@ * Update the active field class for the content container for a given checkbox toggle. * * @this {jQuery} - * @returns {void} + * @return {void} */ handleFieldActiveToggle = function() { var className = 'field-' + $( this ).val() + '-active'; @@ -1062,7 +1064,7 @@ menuAutoAddControl.active.set( true ); } - // Add the control for deleting the menu + // Add the control for deleting the menu. menuDeleteControlId = section.id + '[delete]'; menuDeleteControl = api.control( menuDeleteControlId ); if ( ! menuDeleteControl ) { @@ -1126,7 +1128,7 @@ wpNavMenu.menuList = section.contentContainer; wpNavMenu.targetList = wpNavMenu.menuList; - // Add attributes needed by wpNavMenu + // Add attributes needed by wpNavMenu. $( '#menu-to-edit' ).removeAttr( 'id' ); wpNavMenu.menuList.attr( 'id', 'menu-to-edit' ).addClass( 'menu' ); @@ -1145,7 +1147,8 @@ wpNavMenu.initSortables(); // Depends on menu-to-edit ID being set above. section.deferred.initSortables.resolve( wpNavMenu.menuList ); // Now MenuControl can extend the sortable. - // @todo Note that wp.customize.reflowPaneContents() is debounced, so this immediate change will show a slight flicker while priorities get updated. + // @todo Note that wp.customize.reflowPaneContents() is debounced, + // so this immediate change will show a slight flicker while priorities get updated. api.control( 'nav_menu[' + String( section.params.menu_id ) + ']' ).reflowMenuItems(); } if ( _.isFunction( completeCallback ) ) { @@ -1166,7 +1169,7 @@ * * @since 4.9.0 * - * @returns {void} + * @return {void} */ highlightNewItemButton: function() { api.utils.highlightButton( this.contentContainer.find( '.add-new-menu-item' ), { delay: 2000 } ); @@ -1179,7 +1182,7 @@ * @since 4.9.0 * * @param {string} [name=''] Nav menu name. - * @returns {wp.customize.Menus.MenuSection} Added nav menu. + * @return {wp.customize.Menus.MenuSection} Added nav menu. */ api.Menus.createNavMenu = function createNavMenu( name ) { var customizeId, placeholderId, setting; @@ -1263,7 +1266,7 @@ * Get number of non-deleted nav menus. * * @since 4.9.0 - * @returns {number} Count. + * @return {number} Count. */ function getNavMenuCount() { var count = 0; @@ -1279,7 +1282,7 @@ * Update visibility of notice to prompt users to create menus. * * @since 4.9.0 - * @returns {void} + * @return {void} */ function updateNoticeVisibility() { container.find( '.add-new-menu-notice' ).prop( 'hidden', getNavMenuCount() > 0 ); @@ -1290,7 +1293,7 @@ * * @since 4.9.0 * @param {wp.customize.Setting} setting - Added setting. - * @returns {void} + * @return {void} */ function addChangeEventListener( setting ) { if ( navMenuSettingPattern.test( setting.id ) ) { @@ -1304,7 +1307,7 @@ * * @since 4.9.0 * @param {wp.customize.Setting} setting - Removed setting. - * @returns {void} + * @return {void} */ function removeChangeEventListener( setting ) { if ( navMenuSettingPattern.test( setting.id ) ) { @@ -1415,7 +1418,7 @@ navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' ); navMenuLocationSetting.set( menuSection.params.menu_id ); - // Reset state for next new menu + // Reset state for next new menu. checkbox.prop( 'checked', false ); } } ); @@ -1439,7 +1442,7 @@ * @since 4.9.0 * * @param {string|null} locationId - The ID of the location to select. `null` clears all selections. - * @returns {void} + * @return {void} */ selectDefaultLocation: function( locationId ) { var locationControl = api.control( this.id + '[locations]' ), @@ -1698,7 +1701,8 @@ control.elements.classes = new api.Element( control.container.find( '.edit-menu-item-classes' ) ); control.elements.xfn = new api.Element( control.container.find( '.edit-menu-item-xfn' ) ); control.elements.description = new api.Element( control.container.find( '.edit-menu-item-description' ) ); - // @todo allow other elements, added by plugins, to be automatically picked up here; allow additional values to be added to setting array. + // @todo Allow other elements, added by plugins, to be automatically picked up here; + // allow additional values to be added to setting array. _.each( control.elements, function( element, property ) { element.bind(function( value ) { @@ -1790,8 +1794,12 @@ $removeBtn = control.container.find( '.item-delete' ); $removeBtn.on( 'click', function() { - // Find an adjacent element to add focus to when this menu item goes away - var addingItems = true, $adjacentFocusTarget, $next, $prev; + // Find an adjacent element to add focus to when this menu item goes away. + var addingItems = true, $adjacentFocusTarget, $next, $prev, + instanceCounter = 0, // Instance count of the menu item deleted. + deleteItemOriginalItemId = control.params.original_item_id, + addedItems = control.getMenuControl().$sectionContent.find( '.menu-item' ), + availableMenuItem; if ( ! $( 'body' ).hasClass( 'adding-menu-items' ) ) { addingItems = false; @@ -1808,10 +1816,43 @@ $adjacentFocusTarget = control.container.nextAll( '.customize-control-nav_menu' ).find( '.add-new-menu-item' ).first(); } + /* + * If the menu item deleted is the only of its instance left, + * remove the check icon of this menu item in the right panel. + */ + _.each( addedItems, function( addedItem ) { + var menuItemId, menuItemControl, matches; + + // This is because menu item that's deleted is just hidden. + if ( ! $( addedItem ).is( ':visible' ) ) { + return; + } + + matches = addedItem.getAttribute( 'id' ).match( /^customize-control-nav_menu_item-(-?\d+)$/, '' ); + if ( ! matches ) { + return; + } + + menuItemId = parseInt( matches[1], 10 ); + menuItemControl = api.control( 'nav_menu_item[' + String( menuItemId ) + ']' ); + + // Check for duplicate menu items. + if ( menuItemControl && deleteItemOriginalItemId == menuItemControl.params.original_item_id ) { + instanceCounter++; + } + } ); + + if ( instanceCounter <= 1 ) { + // Revert the check icon to add icon. + availableMenuItem = $( '#menu-item-tpl-' + control.params.original_item_id ); + availableMenuItem.removeClass( 'selected' ); + availableMenuItem.find( '.menu-item-handle' ).removeClass( 'item-added' ); + } + control.container.slideUp( function() { control.setting.set( false ); wp.a11y.speak( api.Menus.data.l10n.itemDeleted ); - $adjacentFocusTarget.focus(); // keyboard accessibility + $adjacentFocusTarget.focus(); // Keyboard accessibility. } ); control.setting.set( false ); @@ -1870,7 +1911,7 @@ /** * - * @returns {number} + * @return {number} */ getDepth: function() { var control = this, setting = control.setting(), depth = 0; @@ -1921,7 +1962,6 @@ control.params.target = settingValue.target; control.params.attr_title = settingValue.attr_title; control.params.classes = _.isArray( settingValue.classes ) ? settingValue.classes.join( ' ' ) : settingValue.classes; - control.params.attr_title = settingValue.attr_title; control.params.xfn = settingValue.xfn; control.params.description = settingValue.description; control.params.parent = settingValue.menu_item_parent; @@ -1963,7 +2003,7 @@ * * @param {Boolean} expanded * @param {Object} [params] - * @returns {Boolean} false if state already applied + * @return {Boolean} False if state already applied. */ _toggleExpanded: api.Section.prototype._toggleExpanded, @@ -1971,7 +2011,7 @@ * @since 4.6.0 * * @param {Object} [params] - * @returns {Boolean} false if already expanded + * @return {Boolean} False if already expanded. */ expand: api.Section.prototype.expand, @@ -1991,7 +2031,7 @@ * @since 4.6.0 * * @param {Object} [params] - * @returns {Boolean} false if already collapsed + * @return {Boolean} False if already collapsed. */ collapse: api.Section.prototype.collapse, @@ -2172,7 +2212,7 @@ * * @private * - * @param {Number} offset 1|-1 + * @param {number} offset 1|-1 */ _changePosition: function( offset ) { var control = this, @@ -2232,7 +2272,7 @@ * * @private * - * @param {Number} offset 1|-1 + * @param {number} offset 1|-1 */ _changeDepth: function( offset ) { if ( 1 !== offset && -1 !== offset ) { @@ -2429,7 +2469,7 @@ * @since 4.9.0 * * @param {Object.} selections - A map of location selections. - * @returns {void} + * @return {void} */ setSelections: function( selections ) { this.container.find( '.menu-location' ).each( function( i, checkboxNode ) { @@ -2615,7 +2655,7 @@ * Notice that the UI aspects here are handled by wpNavMenu.initSortables() * which is called in MenuSection.onChangeExpanded() * - * @param {object} menuList - The element that has sortable(). + * @param {Object} menuList - The element that has sortable(). */ _setupSortable: function( menuList ) { var control = this; @@ -2779,13 +2819,13 @@ action = sectionTitle.find( '.customize-action' ), name = displayNavMenuName( menu.name ); - // Update the control title + // Update the control title. controlTitle.text( name ); if ( location.length ) { location.appendTo( controlTitle ); } - // Update the section title + // Update the section title. sectionTitle.text( name ); if ( action.length ) { action.prependTo( sectionTitle ); @@ -2814,7 +2854,7 @@ /** * Enable/disable the reordering UI * - * @param {Boolean} showOrHide to enable/disable reordering + * @param {boolean} showOrHide to enable/disable reordering */ toggleReordering: function( showOrHide ) { var addNewItemBtn = this.container.find( '.add-new-menu-item' ), @@ -2954,11 +2994,12 @@ /** * Add a new item to this menu. * - * @param {object} item - Value for the nav_menu_item setting to be created. - * @returns {wp.customize.Menus.controlConstructor.nav_menu_item} The newly-created nav_menu_item control instance. + * @param {Object} item - Value for the nav_menu_item setting to be created. + * @return {wp.customize.Menus.controlConstructor.nav_menu_item} The newly-created nav_menu_item control instance. */ addItemToMenu: function( item ) { - var menuControl = this, customizeId, settingArgs, setting, menuItemControl, placeholderId, position = 0, priority = 10; + var menuControl = this, customizeId, settingArgs, setting, menuItemControl, placeholderId, position = 0, priority = 10, + originalItemId = item.id || ''; _.each( menuControl.getMenuItemControls(), function( control ) { if ( false === control.setting() ) { @@ -2982,7 +3023,7 @@ position: position } ); - delete item.id; // only used by Backbone + delete item.id; // Only used by Backbone. placeholderId = api.Menus.generatePlaceholderAutoIncrementId(); customizeId = 'nav_menu_item[' + String( placeholderId ) + ']'; @@ -3002,7 +3043,8 @@ settings: { 'default': customizeId }, - menu_item_id: placeholderId + menu_item_id: placeholderId, + original_item_id: originalItemId } ); api.control.add( menuItemControl ); @@ -3028,83 +3070,6 @@ } } ); - api.Menus.NewMenuControl = api.Control.extend(/** @lends wp.customize.Menus.NewMenuControl.prototype */{ - - /** - * 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. - * - * @constructs wp.customize.Menus.NewMenuControl - * @augments wp.customize.Control - * - * @deprecated 4.9.0 This class is no longer used due to new menu creation UX. - */ - initialize: function() { - if ( 'undefined' !== typeof console && console.warn ) { - console.warn( '[DEPRECATED] wp.customize.NewMenuControl will be removed. Please use wp.customize.Menus.createNavMenu() instead.' ); - } - api.Control.prototype.initialize.apply( this, arguments ); - }, - - /** - * Set up the control. - * - * @deprecated 4.9.0 - */ - 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. - * - * @deprecated 4.9.0 - */ - submit: function() { - - var control = this, - container = control.container.closest( '.accordion-section-new-menu' ), - nameInput = container.find( '.menu-name-field' ).first(), - name = nameInput.val(), - menuSection; - - if ( ! name ) { - nameInput.addClass( 'invalid' ); - nameInput.focus(); - return; - } - - menuSection = api.Menus.createNavMenu( name ); - - // Clear name field. - nameInput.val( '' ); - nameInput.removeClass( 'invalid' ); - - wp.a11y.speak( api.Menus.data.l10n.menuAdded ); - - // Focus on the new menu section. - menuSection.focus(); - } - }); - /** * Extends wp.customize.controlConstructor with control constructor for * menu_location, menu_item, nav_menu, and new_menu. @@ -3114,7 +3079,6 @@ nav_menu_item: api.Menus.MenuItemControl, nav_menu: api.Menus.MenuControl, nav_menu_name: api.Menus.MenuNameControl, - new_menu: api.Menus.NewMenuControl, // @todo Remove in a future release. See #42364. nav_menu_locations: api.Menus.MenuLocationsControl, nav_menu_auto_add: api.Menus.MenuAutoAddControl }); @@ -3171,9 +3135,9 @@ * * @alias wp.customize.Menus.applySavedData * - * @param {object} data - * @param {array} data.nav_menu_updates - * @param {array} data.nav_menu_item_updates + * @param {Object} data + * @param {Array} data.nav_menu_updates + * @param {Array} data.nav_menu_item_updates */ api.Menus.applySavedData = function( data ) { @@ -3430,7 +3394,7 @@ * @alias wp.customize.Menus.getMenuItemControl * * @param {string} menuItemId - * @return {object|null} + * @return {Object|null} */ api.Menus.getMenuItemControl = function( menuItemId ) { return api.control( menuItemIdToSettingId( menuItemId ) ); @@ -3439,7 +3403,7 @@ /** * @alias wp.customize.Menus~menuItemIdToSettingId * - * @param {String} menuItemId + * @param {string} menuItemId */ function menuItemIdToSettingId( menuItemId ) { return 'nav_menu_item[' + menuItemId + ']'; @@ -3452,7 +3416,7 @@ * @alias wp.customize.Menus~displayNavMenuName * * @param {string} name - * @returns {string} + * @return {string} */ function displayNavMenuName( name ) { name = name || '';