possible to make sections in theme customizer sortable and saveable at publish button clicked?

We do have kirki(a theme customizer toolkit) sortable field. But i want to apply the same logic on the sections itself instead of creating another section with sortable fields.
I’m using options instead of theme mods, just for better visualization right now.
The things i tried so far:
in functions.php I have this code below to create an option row in option table:

$sortable_sections = get_option('sortable_sections');
  if(!isset($sortable_sections) OR empty($sortable_sections)){
  add_option('sortable_sections', array('eat', 'pray','love'));
}

The code to create sections in a file named kirki-config.php which is required in functions.php as below

$sortable_sections = get_option('sortable_sections');
foreach($sortable_sections as $sortable_section){
 Kirki::add_section( $sortable_section, array(
    'title'          => esc_attr__( strtoupper($sortable_section), 'airspace' ),
    'description'    => esc_attr__( 'Insert content', 'airspace' ),
    'panel'          => 'frontpage_panel',
) );
}

the above code creates three sections with id eat, pray, love respectively.

the code in js is as below:(file hooked to admin_enqueue_scripts)

jQuery( document ).ready(function($) {

  $('#sub-accordion-panel-frontpage_panel').sortable({
    items: '.control-section-kirki-default',
    axis : 'y',
    cursor: 'move,
   update: function(){
      //here i want the code which gets the updated reordered array and passed to a  
      //php file using .sortable('serialize'), only after clicking the publish
     // button. the problem is the publish button is disabled even after
    // sections got new positions.
   }
  });
});

and in php file i want code something like as below:

$new_array = $_POST['accordion-section'];
update_option('sortable_sections', $new_array);

So there are two steps which i’m having problem to finish.
1. enabling and disabling the publish button
2.after clicking the button js variable goes to php file and updates the option.

How to achieve it, and is there any better method to achieve it?

1 Answer
1

Simple tutorial – How to sort customizer sections in the panel and add order to theme.

1. You must use prefixes to do this because customizer don’t support custom classes.

  • ctmss_panel_ – for panels
  • ctmss_section_ – for sections
  • ctmss_hidden_ – for hidden sections that has a input with values

2. Add panel to the Customizer – add_panel()

$wp_customize->add_panel( 'ctmss_panel_panelname1', array(
    'title'    => esc_html__( 'My Panel', 'textdomain' ),
    'priority' => 150
) );

3. Add section with text input to save new sections order. Hide this section with CSS. add_section(), add_setting(), add_control()

$wp_customize->add_section( 'ctmss_hidden_sectionname1', array(
    'title'       => esc_html__( 'Section hidden', 'textdomain' ),
    'panel'       => 'ctmss_panel_panelname1',
    'priority'    => 1
) );

$wp_customize->add_setting(
    'ctmss_sections_order', array(
        'sanitize_callback' => 'wp_kses_post'
    )
);
$wp_customize->add_control(
    new WP_Customize_Control(
        $wp_customize,
        'sections_order',
        array(
            'settings' => 'ctmss_sections_order',
            'type'     => 'text',
            'label'    => esc_html__( 'Section layout', 'textdomain' ),
            'section'  => 'ctmss_hidden_sectionname1',
        )
    )
);

4. Create array() with available sections. If saved values is available get it, else set default sections and order.

$default_sections = array (
    'ctmss_section_sectionname1' => array (
        'title'       => esc_html__( 'Section 1', 'textdomain' ),
        'description' => esc_html__( 'Section 1 Description', 'textdomain' ),
    ),
    'ctmss_section_sectionname2' => array (
        'title'       => esc_html__( 'Section 2', 'textdomain' ),
        'description' => esc_html__( 'Section 2 Description', 'textdomain' ),
    ),
    'ctmss_section_sectionname3' => array (
        'title'       => esc_html__( 'Section 3', 'textdomain' ),
        'description' => esc_html__( 'Section 3 Description', 'textdomain' ),
    ),
    'ctmss_section_sectionname4' => array (
        'title'       => esc_html__( 'Section 4', 'textdomain' ),
        'description' => esc_html__( 'Section 4 Description', 'textdomain' ),
    ),
);

$sortable_sections = get_theme_mod('ctmss_sections_order');
if( !isset( $sortable_sections ) || empty( $sortable_sections ) ){
    set_theme_mod( 'ctmss_sections_order', implode(',', array_keys( $default_sections ) ) );
}
$sortable_sections = explode(',', $sortable_sections );

foreach( $sortable_sections as $sortable_section ){
    $wp_customize->add_section( $sortable_section, array(
        'title'       => $default_sections[$sortable_section]['title'],
        'description' => $default_sections[$sortable_section]['description'],
        'panel'       => 'ctmss_panel_panelname1'
    ) );
}

5. Add controls to the sections. Example:

$wp_customize->add_setting(
    'myprefix_section1_layout', array(
        'default'           => 'classic',
        'sanitize_callback' => 'wp_kses_post'
    )
);
$wp_customize->add_control(
    new WP_Customize_Control(
        $wp_customize,
        'section1_layout',
        array(
            'settings' => 'myprefix_section1_layout',
            'type'     => 'radio',
            'label'    => esc_html__( 'Section layout', 'textdomain' ),
            'section'  => 'ctmss_section_sectionname1',
            'choices'  => array(
                'classic'          => esc_html__( 'Classic', 'textdomain' ),
                'grid'             => esc_html__( 'Grid', 'textdomain' ),
                'list'             => esc_html__( 'List', 'textdomain' ),
            )
        )
    )
);

$wp_customize->add_setting(
    'myprefix_section2_layout', array(
        'default'           => 'classic',
        'sanitize_callback' => 'wp_kses_post'
    )
);
$wp_customize->add_control(
    new WP_Customize_Control(
        $wp_customize,
        'section2_layout',
        array(
            'settings' => 'myprefix_section2_layout',
            'type'     => 'radio',
            'label'    => esc_html__( 'Section layout', 'textdomain' ),
            'section'  => 'ctmss_section_sectionname2',
            'choices'  => array(
                'classic'          => esc_html__( 'Classic', 'textdomain' ),
                'grid'             => esc_html__( 'Grid', 'textdomain' ),
                'list'             => esc_html__( 'List', 'textdomain' ),
            )
        )
    )
);

$wp_customize->add_setting(
    'myprefix_section3_layout', array(
        'default'           => 'classic',
        'sanitize_callback' => 'wp_kses_post'
    )
);
$wp_customize->add_control(
    new WP_Customize_Control(
        $wp_customize,
        'section3_layout',
        array(
            'settings' => 'myprefix_section3_layout',
            'type'     => 'radio',
            'label'    => esc_html__( 'Section layout', 'textdomain' ),
            'section'  => 'ctmss_section_sectionname3',
            'choices'  => array(
                'classic'          => esc_html__( 'Classic', 'textdomain' ),
                'grid'             => esc_html__( 'Grid', 'textdomain' ),
                'list'             => esc_html__( 'List', 'textdomain' ),
            )
        )
    )
);

$wp_customize->add_setting(
    'myprefix_section4_layout', array(
        'default'           => 'classic',
        'sanitize_callback' => 'wp_kses_post'
    )
);
$wp_customize->add_control(
    new WP_Customize_Control(
        $wp_customize,
        'section4_layout',
        array(
            'settings' => 'myprefix_section4_layout',
            'type'     => 'radio',
            'label'    => esc_html__( 'Section layout', 'textdomain' ),
            'section'  => 'ctmss_section_sectionname4',
            'choices'  => array(
                'classic'          => esc_html__( 'Classic', 'textdomain' ),
                'grid'             => esc_html__( 'Grid', 'textdomain' ),
                'list'             => esc_html__( 'List', 'textdomain' ),
            )
        )
    )
);

6. Add styles and scripts.

function my_customizer_scripts() {
    wp_enqueue_script( 'my_customizer_js', trailingslashit( get_template_directory_uri() ) . 'assets/js/my-customizer.js', array(), '1.0', 'all' );
}
add_action('customize_controls_print_scripts', 'my_customizer_scripts');

function my_customizer_styles() {
    wp_enqueue_style( 'my_customizer_css', trailingslashit( get_template_directory_uri() ) . 'assets/css/my-customizer.css', array(), '1.0', 'all' );
}
add_action('customize_controls_print_styles', 'my_customizer_styles');

my-customizer.css

li[id*="ctmss_hidden_"],
ul[id*="ctmss_hidden_"],
.ctmss_hidden {
    display: none !important;
}

.ctmss_section .accordion-section-title:active {
    cursor: move !important;
}

my-customizer.js

jQuery( document ).ready( function($) {

    $('ul[id*="ctmss_panel_"]').addClass('ctmss_panel');
    $('ul.ctmss_panel').each( function() {
        if( 0 === $(this).length ){
            return true;
        }

        var panel = $(this),
            panelSectionHidden = panel.find('li[id*="ctmss_hidden_"]').attr('aria-owns');

        panel.find('li[id*="ctmss_section_"]').addClass('ctmss_section');
        panel.find('li[id*="ctmss_hidden_"]').addClass('ctmss_hidden');

        // Init sortable.
        panel.sortable( {
            item: 'li.ctmss_section',
            axis: 'y',

            // Update value when we stop sorting.
            stop: function() {
                updateValue();
            }
        });

        // Updates the sorting list.
        function updateValue() {
            var inputValues = panel.find( '.ctmss_section' ).map( function() {
                var id = $(this).attr('id'),
                    id = id.replace('accordion-section-','');

                return id;
            }).get().join(',');

            // Add the value to the hidden field
            $( '#' + panelSectionHidden ).find( '.customize-control-text input' ).prop( 'value', inputValues );

            // Important! Make sure to trigger change event so Customizer knows it has to save the field
            $( '#' + panelSectionHidden ).find( '.customize-control-text input' ).trigger('change');

            console.log( inputValues );
        }

    });

});

7. Add function to display sections.

if ( !function_exists( 'ctmss_get_sections' ) ):
    function ctmss_get_sections( $sections ) {

        $sections = explode(',', $sections);

        $output="";

        if ( empty( $sections ) ) {
            return $output;
        }

        foreach( $sections as $section ) {

            switch ( $section ) {

            case 'ctmss_section_sectionname1':
                $output .= '<div style="width: 100%; height: 200px; padding: 40px; background: #e1e1e1;">Section 1</div>';
                break;

            case 'ctmss_section_sectionname2':
                $output .= '<div style="width: 100%; height: 200px; padding: 40px; background: #e3e3e3;">Section 2</div>';
                break;

            case 'ctmss_section_sectionname3':
                $output .= '<div style="width: 100%; height: 200px; padding: 40px; background: #e5e5e5;">Section 3</div>';
                break;

            case 'ctmss_section_sectionname4':
                $output .= '<div style="width: 100%; height: 200px; padding: 40px; background: #e7e7e7;">Section 4</div>';
                break;

            default:
                break;
            }

        }

        return $output;

    }
endif;

8. Run your code where you want.

echo ctmss_get_sections( get_theme_mod('ctmss_sections_order') );

This solution works without Kirki but is easy to customize it.

Leave a Comment