Approach
So I’ve looked into this and my approach is this:
- When launching the customizer go through all sidebars and disable the widget as soon as you find any usage of it
- Whenever a sidebar is changed do that again.
Disclaimer
This has the following limitations:
- This is the first time I’ve dabbled in playing with the JS API of the Customizer. So I might be doing inefficient stuff here, but hey, it works 😉
- Only cares about the Customizer (as stated in the question)
- Doesn’t do any kind of server-side validation. It merely hides the UI, so if you’re worried about someone circumventing the UI this is incomplete/insecure.
- Disabling the widget is global – any use in any sidebar disables the widget globally.
The code
Now that we have the Disclaimer done let’s look at the code:
(function() {
wp.customize.bind( 'ready', function() {
var api = wp.customize,
widgetId = 'foo_widget',
widget = wp.customize.Widgets.availableWidgets.findWhere( { id_base: widgetId } );
/**
* Counts how often a widget is used based on an array of Widget IDs.
*
* @param widgetIds
* @returns {number}
*/
var countWidgetUses = function( widgetIds ){
var widgetUsedCount = 0;
widgetIds.forEach(function(id){
if( id.indexOf( widgetId ) == 0 ){
widgetUsedCount++;
}
});
return widgetUsedCount;
};
var isSidebar = function( setting ) {
return (
0 === setting.id.indexOf( 'sidebars_widgets[' )
&&
setting.id !== 'sidebars_widgets[wp_inactive_widgets]'
);
};
var updateState = function(){
//Enable by default...
widget.set('is_disabled', false );
api.each( function( setting ) {
if ( isSidebar( setting ) ) {
//...and disable as soon as we encounter any usage of the widget.
if( countWidgetUses( setting.get() ) > 0 ) widget.set('is_disabled', true );
}
} );
};
/**
* Listen to changes to any sidebar.
*/
api.each( function( setting ) {
if ( isSidebar( setting ) ) {
setting.bind( updateState );
}
} );
updateState();
});
})( jQuery );
Sidenote: Use the customize_controls_enqueue_scripts
action to add the script.
You could probably extend this to limit it to work an a per sidebar base instead of globally. I’d say listen to the activation of a sidebar and then count the widgets in that sidebar. But I didn’t find time to look into that as well.