Um widget no Customizer pode ser de "uso único" (ou seja, desativado após a adição de 1 instância)?

8

Estou em uma missão noturna para criar um widget de uso único personalizado .

Assim que uma instância for adicionada a um painel da barra lateral no Customizer , seu controle no painel Widgets disponíveis deverá ser exibido como desativado (ou, como alternativa, desaparecer completamente).

É assim que ficaria (observe o widget visualmente "desativado" à direita):

…

O widget personalizado está registrado e tudo, mas estou preso ao requisito de uso único.

Especificações

  • Escala não é um problema. Não há problema em assumir apenas 1 barra lateral registrada. A abordagem pode ser limitar o uso do widget a 1 por barra lateral ou a 1 por todas as barras laterais registradas - ambas seriam igualmente boas por enquanto.
  • A página Widgets no back-end não é um problema. Isso não é necessário para funcionar igualmente bem na página Widgets , em Aparência, no back-end. Ele só tem que funcionar no Customizer.

Questões

  1. Cerca de 250 anos atrás, todos os widgets costumavam ser de "uso único". Alguém conhece uma maneira legítima de trazer esses tempos de volta e fazer com que um widget personalizado seja usado apenas 1x por meio da API do Widget?
  2. Se não (o que eu suponho depois de ter pesquisado uma boa quantidade de arquivos principais), provavelmente aderia a uma abordagem baseada em CSS (eventos-ponteiro, sobreposição de pseudo-elemento, qualquer que seja). Alguma alma amável ajudaria meu conhecimento muito limitado do Customizer / JavaScript com uma abordagem básica sobre como adicionar / remover uma classe CSS dedicada ao controle de widget no painel "disponível" (o da direita) assim que uma instância do referido widget tiver foi adicionado / removido ao painel da barra lateral?

O que eu tentei até agora

  • Pesquise vários arquivos principais.
  • Leia isto e isto , mas nenhum deles parece prático.
  • Consertou com eventos jQuery widget-added, widget-updatede widget-synced, mas perca um evento para “Widget excluído”.

Agradecemos toneladas de antecedência!


Atualização: ainda não coloquei minha prova de conceito em um repositório público, faria, é claro.

Solução

Embrulhei a solução gentilmente compartilhada pelo kraftner abaixo em um plug-in de prova de conceito no GitHub .

glueckpress
fonte
Você também pode usar a API do widget clássico em vez de WP_Widget, por exemplo: gist.github.com/westonruter/7141599
Weston Ruter

Respostas:

5

Aproximação

Então, eu olhei para isso e minha abordagem é a seguinte:

  1. Ao iniciar o personalizador, percorra todas as barras laterais e desative o widget assim que encontrar algum uso dele.
  2. Sempre que uma barra lateral for alterada, faça isso novamente.

aviso Legal

Isso tem as seguintes limitações:

  1. Esta é a primeira vez que brinquei com a API JS do Customizer. Então, talvez eu esteja fazendo coisas ineficientes aqui, mas ei, funciona;)
  2. Só se preocupa com o Personalizador (conforme indicado na pergunta)
  3. Não faz nenhum tipo de validação no servidor. Ele apenas oculta a interface do usuário; portanto, se você estiver preocupado com alguém que a contorne, isso é incompleto / inseguro.
  4. Desativar o widget é global - qualquer uso em qualquer barra lateral desativa o widget globalmente.

O código

Agora que terminamos o aviso, vejamos o código:

(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 );

Nota lateral: use a customize_controls_enqueue_scriptsação para adicionar o script.


Você provavelmente poderia estender isso para limitá-lo a trabalhar uma base por barra lateral em vez de globalmente. Eu diria para ouvir a ativação de uma barra lateral e depois contar os widgets nessa barra lateral. Mas não encontrei tempo para analisar isso também.

kraftner
fonte
Fantástico! Estou sem palavras, isso funciona como um encanto e resolve meu caso completamente. Não esperaria um exemplo completo de trabalho, muito obrigado!
glueckpress
1
Boas perguntas (explicações claras, capturas de tela, provas de esforço) merecem respostas completas. Deixando esse comentário como uma dica para outras pessoas, como obter respostas adequadas para suas perguntas. ;)
kraftner