this.search( this.terms );
}
- // If search is blank, show all themes
- // Useful for resetting the views when you clean the input
+ // If search is blank, set all the widgets as they matched the search to reset the views.
if ( this.terms === '' ) {
this.each( function ( widget ) {
widget.set( 'search_matched', true );
events: {
'input #widgets-search': 'search',
'keyup #widgets-search': 'search',
- 'change #widgets-search': 'search',
- 'search #widgets-search': 'search',
'focus .widget-tpl' : 'focus',
'click .widget-tpl' : '_submit',
'keypress .widget-tpl' : '_submit',
// Cache sidebar control which has opened panel
currentSidebarControl: null,
$search: null,
+ $clearResults: null,
+ searchMatchesCount: null,
initialize: function() {
var self = this;
this.$search = $( '#widgets-search' );
+ this.$clearResults = this.$el.find( '.clear-results' );
+
_.bindAll( this, 'close' );
this.listenTo( this.collection, 'change', this.updateList );
this.updateList();
+ // Set the initial search count to the number of available widgets.
+ this.searchMatchesCount = this.collection.length;
+
// If the available widgets panel is open and the customize controls are
// interacted with (i.e. available widgets panel is blurred) then close the
// available widgets panel. Also close on back button click.
}
} );
+ // Clear the search results and trigger a `keyup` event to fire a new search.
+ this.$clearResults.on( 'click', function() {
+ self.$search.val( '' ).focus().trigger( 'keyup' );
+ } );
+
// Close the panel if the URL in the preview changes
api.previewer.bind( 'url', this.close );
},
var firstVisible;
this.collection.doSearch( event.target.value );
+ // Update the search matches count.
+ this.updateSearchMatchesCount();
+ // Announce how many search results.
+ this.announceSearchMatches();
// Remove a widget from being selected if it is no longer visible
if ( this.selected && ! this.selected.is( ':visible' ) ) {
this.select( firstVisible );
}
}
+
+ // Toggle the clear search results button.
+ if ( '' !== event.target.value ) {
+ this.$clearResults.addClass( 'is-visible' );
+ } else if ( '' === event.target.value ) {
+ this.$clearResults.removeClass( 'is-visible' );
+ }
+
+ // Set a CSS class on the search container when there are no search results.
+ if ( ! this.searchMatchesCount ) {
+ this.$el.addClass( 'no-widgets-found' );
+ } else {
+ this.$el.removeClass( 'no-widgets-found' );
+ }
},
+ // Update the count of the available widgets that have the `search_matched` attribute.
+ updateSearchMatchesCount: function() {
+ this.searchMatchesCount = this.collection.where({ search_matched: true }).length;
+ },
+
+ // Send a message to the aria-live region to announce how many search results.
+ announceSearchMatches: _.debounce( function() {
+ var message = l10n.widgetsFound.replace( '%d', this.searchMatchesCount ) ;
+
+ if ( ! this.searchMatchesCount ) {
+ message = l10n.noWidgetsFound;
+ }
+
+ wp.a11y.speak( message );
+ }, 500 ),
+
// Changes visibility of available widgets
updateList: function() {
this.collection.each( function( widget ) {
args = $.extend( {}, control.defaultExpandedArguments, args );
control.onChangeExpanded( expanded, args );
});
+ control.altNotice = true;
api.Control.prototype.initialize.call( control, id, options );
},
$saveBtn = this.container.find( '.widget-control-save' );
$saveBtn.val( l10n.saveBtnLabel );
$saveBtn.attr( 'title', l10n.saveBtnTooltip );
- $saveBtn.removeClass( 'button-primary' ).addClass( 'button-secondary' );
+ $saveBtn.removeClass( 'button-primary' );
$saveBtn.on( 'click', function( e ) {
e.preventDefault();
self.updateWidget( { disable_form: true } ); // @todo disable_form is unused?
params.action = 'update-widget';
params.wp_customize = 'on';
params.nonce = api.settings.nonce['update-widget'];
- params.theme = api.settings.theme.stylesheet;
+ params.customize_theme = api.settings.theme.stylesheet;
params.customized = wp.customize.previewer.query().customized;
data = $.param( params );
* @param {Object} args merged on top of this.defaultActiveArguments
*/
onChangeExpanded: function ( expanded, args ) {
- var self = this, $widget, $inside, complete, prevComplete;
+ var self = this, $widget, $inside, complete, prevComplete, expandControl;
self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI.
if ( expanded ) {
$widget = this.container.find( 'div.widget:first' );
$inside = $widget.find( '.widget-inside:first' );
- if ( expanded ) {
-
- if ( self.section() && api.section( self.section() ) ) {
- self.expandControlSection();
- }
+ expandControl = function() {
// Close all other widget controls before expanding this one
api.control.each( function( otherControl ) {
self.container.trigger( 'expand' );
self.container.addClass( 'expanding' );
+ };
+
+ if ( expanded ) {
+ if ( api.section.has( self.section() ) ) {
+ api.section( self.section() ).expand( {
+ completeCallback: expandControl
+ } );
+ } else {
+ expandControl();
+ }
} else {
complete = function() {
/**
* Get the widget_form Customize controls associated with the current sidebar.
*
- * @since 3.9
+ * @since 3.9.0
* @return {wp.customize.controlConstructor.widget_form[]}
*/
getWidgetFormControls: function() {
return foundControl;
};
+ /**
+ * Initialize Edit Menu button in Nav Menu widget.
+ */
+ $( document ).on( 'widget-added', function( event, widgetContainer ) {
+ var parsedWidgetId, widgetControl, navMenuSelect, editMenuButton;
+ parsedWidgetId = parseWidgetId( widgetContainer.find( '> .widget-inside > .form > .widget-id' ).val() );
+ if ( 'nav_menu' !== parsedWidgetId.id_base ) {
+ return;
+ }
+ widgetControl = api.control( 'widget_nav_menu[' + String( parsedWidgetId.number ) + ']' );
+ if ( ! widgetControl ) {
+ return;
+ }
+ navMenuSelect = widgetContainer.find( 'select[name*="nav_menu"]' );
+ editMenuButton = widgetContainer.find( '.edit-selected-nav-menu > button' );
+ if ( 0 === navMenuSelect.length || 0 === editMenuButton.length ) {
+ return;
+ }
+ navMenuSelect.on( 'change', function() {
+ if ( api.section.has( 'nav_menu[' + navMenuSelect.val() + ']' ) ) {
+ editMenuButton.parent().show();
+ } else {
+ editMenuButton.parent().hide();
+ }
+ });
+ editMenuButton.on( 'click', function() {
+ var section = api.section( 'nav_menu[' + navMenuSelect.val() + ']' );
+ if ( section ) {
+ focusConstructWithBreadcrumb( section, widgetControl );
+ }
+ } );
+ } );
+
+ /**
+ * Focus (expand) one construct and then focus on another construct after the first is collapsed.
+ *
+ * This overrides the back button to serve the purpose of breadcrumb navigation.
+ *
+ * @param {wp.customize.Section|wp.customize.Panel|wp.customize.Control} focusConstruct - The object to initially focus.
+ * @param {wp.customize.Section|wp.customize.Panel|wp.customize.Control} returnConstruct - The object to return focus.
+ */
+ function focusConstructWithBreadcrumb( focusConstruct, returnConstruct ) {
+ focusConstruct.focus();
+ function onceCollapsed( isExpanded ) {
+ if ( ! isExpanded ) {
+ focusConstruct.expanded.unbind( onceCollapsed );
+ returnConstruct.focus();
+ }
+ }
+ focusConstruct.expanded.bind( onceCollapsed );
+ }
+
/**
* @param {String} widgetId
* @returns {Object}