Support

Account

Home Forums Feature Requests Drag Flexible Content layouts between parent Repeaters

Solving

Drag Flexible Content layouts between parent Repeaters

  • I have a Field Group where I have a Repeater, within which is a Flexible Content field. Here is a visual representation of what I’ve got:

    - Repeater Field #1
      - Flexible Content Field #1
         - Flexible Content Layout #1
         - Flexible Content Layout #2
    - Repeater Field #2
      - Flexible Content Field #2
         - Flexible Content Layout #3

    Currently I cannot drag Flexible Content layouts between separate Flexible Content fields. In the above visual representation, I’d like to be able to drag ‘Flexible Content Layout #2’ into ‘Flexible Content Field #2’.

    Obviously this should only be possible to drag between Flexible Content Fields of the same type.

    I’ve been playing with hacking this myself using jQuery sortable with limited success. I can reorder in the UI, but the elements are not saving to the database on post update.

    This would make a huge difference to my using of the plugin.

    Cheers!

    Simon

  • Hi @sfoxe

    Thanks for the suggestion.

    I’ll pass it to the plugin author and hope he can implement it in the future.

    Cheers!

  • This feature would be outstanding for flexible content. I’ve created a page builder plugin that has drag/drop & resizable columns. Dragging between them would be a major improvement. Thanks for such a great plugin!

  • Hi guys

    This is a great idea.

    It should be possible by adding the following to the sortable JS args for the flexible content initialization function:

    
    connectWith: '.acf-flexible-content .values',
    

    This is untested, but the connectWith param should allow you to move from one sortable to another. The input’s name attributes should also allow for this to work..

    Please test and report back with any issues / results

  • I added that code to the correct .sortable() call in acf-pro-input.js and yes, you can move elements between flexible content containers now, but when saving, the element that was moved gets deleted from the page entirely.

    When I inspect the DOM, I see that the element that was moved has the same data-id as another element in the container that it was moved to. I’m guess that’s the conflict here.

    Help on this one would be killer. Thanks!

  • Absolutely the same feature I’m searching for.
    But I suggest a structure like this:

    # Repeater (row)
    # Repeater ( column )
    # Flexible content ( content )

    So you can develop modules only for the Flexible content ( content ) and use each of these in each column to get something like a Visual Composer.

    Clients like how VC works, but in frontend they pull a lot of sh*t, nested divs like a mirror in a mirror, and something more developer friendly would be an epic win for ACF users.

    Like an ACF visual composer, maybe a “grid” field ???
    But with customisable classes ( i need to set my bootstrap classes ), however will be useless like site origin page builder.

    Some guys are trying to do that: https://bitbucket.org/dachcom/dachcom-page-designer but hmmm you can’t drag&drop outside the same repeater / fc

    Moreover, keep in mind that when you create a flexible content with lot of modules, your pc take off from the desktop with fans at 20000 rpm like a shuttle, and the browser show an alert like “hey, are you sure let this script run till tomorrow” ?

    Let’s join the forces and find a workaround,
    cheers guys

  • We are working on a page builder. It’s looking good so far, but we are still missing this feature — drag and drop between repeaters and flexible content fields.

    I’ve tried a few different tests that allow me to move field layouts from one flexible content field to another, but I still haven’t figured out how to get it to save correctly.

    Initially, I thought it might be related to a conflicting data-id on the layout wrapper, but a subsequent test proved that theory wrong. There must be a conflict with the way the fields are saved. It’s also possible that there’s a conflict with the sortstop function that fires after the element gets moved.

    I’ll continue to debug and post updates. Elliot, if you’re reading this and have more insights, I’d be happy to have your help.

  • Ok, a little closer, I was able to get the field save in the new flexible content field that it gets moved to. However, the field’s value does not get saved.

    I modified the code in the .acf-hidden input so that the page element’s layout gets updated in this format:

    acf[column_field_key][1][elements_field_key][2][acf_fc_layout]

    I changed the column number from 0 to 1 to move the page element from the first column to the second.

  • Success! I had wrote a script that modifies the name attribute of elements to match the new column and element locations. It’s hooked into the sortstop action. This code should work on most any setup with only two substitutions, the [data-name=”elements”] and [data-layout=”columns”] attributes are specific to my setup. It uses a nested flexible content field structure where the columns are a layout under the primary flexible content field. Elements is another flexible content field that’s part of the column layout.

    // Handle Dragging Between Columns
    function GetSubstringIndex(str, substring, n) {
        var times = 0, index = null;
    
        while (times < n && index !== -1) {
            index = str.indexOf(substring, index+1);
            times++;
        }
    
        return index;
    }
    acf.add_action('sortstop', function ($el) {
        if ($($el).parent('[data-name="elements"] > .acf-input > .acf-flexible-content > .values').length) {
            var column_num = $($el).closest('[data-layout="column"]').attr('data-id');
            $($el).find('[name^="acf[field_"]').each(function(){
                var field_name = $(this).attr('name');   
                var index_location = field_name.indexOf(']')+2;
                var new_name = field_name.substr(0, index_location) + column_num + field_name.substr(index_location+1)
                $(this).attr('name', new_name);
            });
            $($el).closest('[data-name="elements"]').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index){
                $(this).find('[name^="acf[field_"]').each(function(){
                    var field_name = $(this).attr('name');   
                    var index_location = GetSubstringIndex(field_name,']', 3)+2;
                    var new_name = field_name.substr(0, index_location) + index + field_name.substr(index_location+1);
                    $(this).attr('name', new_name);
                });
            });
        }
    });
  • Hi fullsteamlabs,
    Thanks for your code, could you please tell me what changes I have to make if I need to move between repeater fields.. but for repeater field as well..

    So something like this:

    Main repeater field A..time
       repeater field1..name..title..
       repeater field2..name..title..
    Main repeater field B..time
       repeater field1..name..title..
       repeater field2..name..title..
    

    So need to move repeater field1..name..title.. to Main repeater field A..time.

    Thanks

  • woody, were you able to get the connectWith parameter to work for jQuery sortable on your repeater? I think that’s the first step. If you can target the right selector there, you’ll be able to move layouts between repeaters in the editor. However, you won’t be able to save them successfully until you’ve modified the code further.

    Start by adding the connectWith attribute to the sortable() function for repeaters in /advanced-custom-fields-pro/pro/assets/js/acf-pro-input.min.js

  • Hi fullsteamlabs,
    Thanks for the helpful code. I’ve placed the js now.
    I’d like to see if you can provide an example of the hierarchy of acf fields you used to make that work?

  • redmeesblue, I’ve got the following structure:

    Editor (Flexible Content) > Elements (Flexible Content) > Page Element (various field layouts)

    The first level, the editor, uses a flexible content element with one layout that serves as the container for the page elements. Then, the elements field inside it uses a flexible content field that contains the page elements as layouts.

    I’ve done some more complex UI customization to enable modal windows and resizable containers, but I think you can still get the dragging to work without all that.

  • EDIT: It didn’t, see later post

    Thanks so much!

    It worked out if the box. The complete code is below (nothing new, just combined for easy copy&paste). This greatly enhances the use of ACF:

    Add to functions.php:

    
    // Handle Dragging Between Columns
    
    function my_acf_input_admin_footer() {
    
        ?>
    
            <script type="text/javascript">
            
            (function($) {
    
                $( ".values" ).sortable({
                  connectWith: ".values"
                })
    
            })(jQuery); 
    
            function GetSubstringIndex(str, substring, n) {
                var times = 0, index = null;
    
                while (times < n && index !== -1) {
                    index = str.indexOf(substring, index+1);
                    times++;
                }
    
                return index;
            }
            
            acf.add_action('sortstop', function ($el) {
                if ($($el).parent('[data-name="elements"] > .acf-input > .acf-flexible-content > .values').length) {
                    var column_num = $($el).closest('[data-layout="column"]').attr('data-id');
                    $($el).find('[name^="acf[field_"]').each(function(){
                        var field_name = $(this).attr('name');   
                        var index_location = field_name.indexOf(']')+2;
                        var new_name = field_name.substr(0, index_location) + column_num + field_name.substr(index_location+1)
                        $(this).attr('name', new_name);
                    });
                    $($el).closest('[data-name="elements"]').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index){
                        $(this).find('[name^="acf[field_"]').each(function(){
                            var field_name = $(this).attr('name');   
                            var index_location = GetSubstringIndex(field_name,']', 3)+2;
                            var new_name = field_name.substr(0, index_location) + index + field_name.substr(index_location+1);
                            $(this).attr('name', new_name);
                        });
                    });
                }
            });        
    
            </script>
    
        <?php
    
    }
    
    add_action('acf/input/admin_footer', 'my_acf_input_admin_footer');
    
  • For some reason I haven’t been able to get the values to save by doing this.
    I’m guessing I’ve created the fields incorrectly – could anyone give an example structure of their fields, or even an export of the fields you are using? That would be really helpful!

  • I have a repeater of a flexible content:

    repeater > flexible content

  • Thanks for that Script!
    It works great, but I have problems with saving the dragged layouts.
    All layouts, which were dragged to the other fields, are lost after saving the post.
    Have/had somebody the same issue?

    I changed the JS to this for just dragging the layouts between the reusable fields:

    
    connectWith: [
        '[data-layout="1_col"] .values',
        '[data-layout="2_col"] .values'
    ]
    

    I created a field group for all my modules. This field is reused in the columns as an flexible content field:

    - flexible content for modules (using this fieldgroup for reusable field group)
      - text
      - image
      - ...
    

    in the main groups:

    
    - flexible content (1 col)
      - col: reusable field group (flexible content)
    - flexible content (2 cols)
      - first col: reusable field group (flexible content)
      - second col: reusable field group (flexible content)
    

  • Hi,

    I realized that I had to actually change it quite a bit to fit my situation (not just copy&paste as I wrote earlier, sorry!)

    This one works in the following situation:

    repeater “columns” > flexible content “element” > layouts

    Or in words: I have a repeater named “columns” which has one subfield of type “flexible content” named “element” which has several layouts. Each column can have several Elements of different layouts, for example Image, Text-Block, etc.

    Overview of the changes
    – attached “sortstop” and “sortstart” events again
    – changed several selectors
    – re-initialise tinymce after intercolumn drag

    This is not good code, but it works for now. Would be great to have such a thing in ACF.

    
    function my_acf_input_admin_footer() {
    
        ?>
    
            <script type="text/javascript">
            
            (function($) {
    
               acf.add_action('ready', function( $el ){
                   
                   $( ".values" ).sortable({
                     connectWith: ".values",
    
                     start: function(event, ui) {
                        acf.do_action('sortstart', ui.item, ui.placeholder);              
                      },     
    
                     stop: function(event, ui) {
                        acf.do_action('sortstop', ui.item, ui.placeholder);    
    
                        $(this).find('.mce-tinymce').each(function(){
                            tinyMCE.execCommand( 'mceRemoveControl', true, $(this).attr('id') );
                            tinyMCE.execCommand( 'mceAddControl', true, $(this).attr('id') );
                            //$(this).sortable("refresh");
                        });                    
                      },                 
                   })
                   
               });
    
                acf.add_action('sortstop', function ($el) {
                    if ($($el).parents('[data-name="columns"] > .acf-input > .acf-repeater').length) {
                        var column_num = $($el).closest('.acf-row').attr('data-id');
                        $($el).find('[name^="acf[field_"]').each(function(){
                            var field_name = $(this).attr('name');   
                            var index_location = field_name.indexOf(']')+2;
                            var new_name = field_name.substr(0, index_location) + column_num + field_name.substr(index_location+1)
                            $(this).attr('name', new_name);
                        });
                        $($el).closest('[data-name="element"]').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index){
                            $(this).find('[name^="acf[field_"]').each(function(){
                                var field_name = $(this).attr('name');   
                                var index_location = GetSubstringIndex(field_name,']', 3)+2;
                                var new_name = field_name.substr(0, index_location) + index + field_name.substr(index_location+1);
                                $(this).attr('name', new_name);
                            });
                        });
                    }
                });      
    
            })(jQuery); 
    
            function GetSubstringIndex(str, substring, n) {
                var times = 0, index = null;
    
                while (times < n && index !== -1) {
                    index = str.indexOf(substring, index+1);
                    times++;
                }
    
                return index;
            }  
    
            </script>
    
        <?php
    
    }
    
    add_action('acf/input/admin_footer', 'my_acf_input_admin_footer');
    
  • Does anyone know how to get this working with the latest version of the plugin(5.6.8)?

    All the field names and keys are different in the latest version compared to the above solution. I have attempted to substitute and modify the above solution but i don’t have an older version of the plugin to compare the elements with and Im getting a little lost.

    My example is not as complex as @retani , I’m simply trying to copy one flexible layout to another flexible field. The layout Im trying to copy contains a significant amount of fields and being able to copy that layout into existing flexible fields is going to extremely cost and time effective.

    – Flexible Field 1
    — Flexible Layout 1
    – Flexible Field 2
    — Flexible Layout 2

    ->

    – Flexible Field 1
    — Flexible Layout 1
    — Flexible Layout 2
    – Flexible Field 2

    Anyone got any ides? or even some pseudocode to assist?

  • Hi
    Here is my version!

    • Plugin: Advanced Custom Fields PRO Version 5.7.13
    • Field-Structure: ‘repeater’ > ‘flexible_content’

    Big Thanks To Retani

    
    		function acfDragNDropFlexibleLayoutsBetweenRepeaters() {
    ?>
    			<script type="text/javascript">
    				
    				(function($) {
    
    					acf.add_action('ready', function($el){
    						$(".values").sortable({
    							connectWith: ".values",
    							start: function(event, ui) {
    								acf.do_action('sortstart', ui.item, ui.placeholder);
    							},
    							stop: function(event, ui) {
    								acf.do_action('sortstop', ui.item, ui.placeholder);
    								$(this).find('.mce-tinymce').each(function() {
    									tinyMCE.execCommand('mceRemoveControl', true, $(this).attr('id'));
    									tinyMCE.execCommand('mceAddControl', true, $(this).attr('id'));
    								});
    							}
    						});
    					});
    
    					acf.add_action('sortstop', function ($el) {
    
    						// check if the dropped element is within a repeater field
    							if($($el).parents('.acf-input > .acf-repeater').length) {
    
    								// get column_num from closest acf-row
    									var column_num = $($el).closest('.acf-row').attr('data-id');
    
    								// loop all (input) fields within dropped element and change / fix name
    									$($el).find('[name^="acf[field_"]').each(function() {
    										var field_name 		= 	$(this).attr('name');
    										var index_location 	= 	field_name.indexOf(']')+2;
    										var new_name 		= 	field_name.substr(0, index_location) + column_num + field_name.substr(index_location+1);
    										$(this).attr('name', new_name);
    									});
    
    								// get closest flexible-content-field and loop all layouts within this flexible-content-field
    									$($el).closest('.acf-field.acf-field-flexible-content').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index) {
    
    										// update order number
    											$(this).find('.acf-fc-layout-order:first').html(index+1);
    										
    										// loop all (input) fields within dropped element and change / fix name
    											$(this).find('[name^="acf[field_"]').each(function() {
    												var field_name 		= 	$(this).attr('name');
    												var index_location 	= 	GetSubstringIndex(field_name,']', 3)+2;
    												var new_name 		= 	field_name.substr(0, index_location) + index + field_name.substr(index_location+1);
    												$(this).attr('name', new_name);
    											});
    
    										// click already selected buttons to trigger conditional logics
    											$(this).find('.acf-button-group label.selected').trigger('click');
    									});
    							}
    					});
    
    				})(jQuery); 
    
    				function GetSubstringIndex(str, substring, n) {
    					var times = 0, index = null;
    					while (times < n && index !== -1) {
    						index = str.indexOf(substring, index+1);
    						times++;
    					}
    					return index;
    				}
    
    			</script>
    <?php
    		}
    
    		add_action('acf/input/admin_footer', 'acfDragNDropFlexibleLayoutsBetweenRepeaters');
    
  • New version, works with 5.8.7:

    // acfDragNDropFlexibleLayoutsBetweenRepeaters
    // orginal from https://support.advancedcustomfields.com/forums/topic/drag-flexible-content-layouts-between-parent-repeaters/
    add_action('acf/input/admin_footer', function () {
        ?>
        <script type="text/javascript">
    
            (function($) {
    
                acf.add_action('ready', function($el){
                    $(".values").sortable({
                        connectWith: ".values",
                        start: function(event, ui) {
                            acf.do_action('sortstart', ui.item, ui.placeholder);
                        },
                        stop: function(event, ui) {
                            acf.do_action('sortstop', ui.item, ui.placeholder);
                            $(this).find('.mce-tinymce').each(function() {
                                tinyMCE.execCommand('mceRemoveControl', true, $(this).attr('id'));
                                tinyMCE.execCommand('mceAddControl', true, $(this).attr('id'));
                            });
                        }
                    });
                });
    
                acf.add_action('sortstop', function ($el) {
    
                    // check if the dropped element is within a repeater field
                    if($($el).parents('.acf-input > .acf-repeater').length) {
    
                        // get column_num from closest acf-row
                        var column_num = $($el).closest('.acf-row').attr('data-id');
    
                        // loop all (input) fields within dropped element and change / fix name
                        $($el).find('[name^="acf[field_"]').each(function() {
                            var field_name 		= 	$(this).attr('name');
                            field_name          =   field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
                            field_name[1]       =   '[' + column_num + ']'; // set the new row name
                            var new_name        =   'acf' + field_name.join('');
                            $(this).attr('name', new_name);
                        });
    
                        // get closest flexible-content-field and loop all layouts within this flexible-content-field
                        $($el).closest('.acf-field.acf-field-flexible-content').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index) {
    
                            // update order number
                            $(this).find('.acf-fc-layout-order:first').html(index+1);
    
                            // loop all (input) fields within dropped element and change / fix name
                            $(this).find('[name^="acf[field_"]').each(function() {
                                var field_name 		= 	$(this).attr('name');
                                field_name          =   field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
                                var tempIndex       =   parseInt(field_name[3].match(/([0-9]+)/g)); // hacky code
                                field_name[3].replace(tempIndex, tempIndex+1); // set the new index
                                var new_name        =   'acf' + field_name.join('');
                                $(this).attr('name', new_name);
                            });
    
                            // click already selected buttons to trigger conditional logics
                            $(this).find('.acf-button-group label.selected').trigger('click');
                        });
                    }
                });
    
            })(jQuery);
    
            function GetSubstringIndex(str, substring, n) {
                var times = 0, index = null;
                while (times < n && index !== -1) {
                    index = str.indexOf(substring, index+1);
                    times++;
                }
                return index;
            }
    
        </script>
        <?php
    });
  • ACF Version 5.8.7

    // acfDragNDropFlexibleLayoutsBetweenRepeaters
    // orginal from https://support.advancedcustomfields.com/forums/topic/drag-flexible-content-layouts-between-parent-repeaters/
    add_action('acf/input/admin_footer', function () {
        ?>
        <script type="text/javascript">
    
            (function($) {
    
                acf.add_action('ready', function($el){
                    $(".values").sortable({
                        connectWith: ".values",
                        start: function(event, ui) {
                            acf.do_action('sortstart', ui.item, ui.placeholder);
                        },
                        stop: function(event, ui) {
                            acf.do_action('sortstop', ui.item, ui.placeholder);
                            $(this).find('.mce-tinymce').each(function() {
                                tinyMCE.execCommand('mceRemoveControl', true, $(this).attr('id'));
                                tinyMCE.execCommand('mceAddControl', true, $(this).attr('id'));
                            });
                        }
                    });
                });
    
                acf.add_action('sortstop', function ($el) {
    
                    // check if the dropped element is within a repeater field
                    if($($el).parents('.acf-input > .acf-repeater').length) {
    
                        // get column_num from closest acf-row
                        var column_num = $($el).closest('.acf-row').attr('data-id');
    
                        // loop all (input) fields within dropped element and change / fix name
                        $($el).find('[name^="acf[field_"]').each(function() {
                            var field_name 		= 	$(this).attr('name');
                            field_name          =   field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
                            field_name[1]       =   '[' + column_num + ']'; // set the new row name
                            var new_name        =   'acf' + field_name.join('');
                            $(this).attr('name', new_name);
                        });
    
                        // get closest flexible-content-field and loop all layouts within this flexible-content-field
                        $($el).closest('.acf-field.acf-field-flexible-content').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index) {
    
                            // update order number
                            $(this).find('.acf-fc-layout-order:first').html(index+1);
    
                            // loop all (input) fields within dropped element and change / fix name
                            $(this).find('[name^="acf[field_"]').each(function() {
                                var field_name 		= 	$(this).attr('name');
                                field_name          =   field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
                                var tempIndex       =   parseInt(field_name[3].match(/([0-9]+)/g)); // hacky code
                                field_name[3]       =   field_name[3].replace(tempIndex, index); // set the new index
                                var new_name        =   'acf' + field_name.join('');
                                $(this).attr('name', new_name);
                            });
    
                            // click already selected buttons to trigger conditional logics
                            $(this).find('.acf-button-group label.selected').trigger('click');
                        });
                    }
                });
    
            })(jQuery);
    
            function GetSubstringIndex(str, substring, n) {
                var times = 0, index = null;
                while (times < n && index !== -1) {
                    index = str.indexOf(substring, index+1);
                    times++;
                }
                return index;
            }
    
        </script>
        <?php
    });
  • This reply has been marked as private.
  • This solution is working for me (ACF 5.8.7):

    // acf drag n drop flexible layouts between repeaters
    add_action('acf/input/admin_footer', function () {
        ?>
        <script type="text/javascript">
    
            (function($) {
    
                acf.add_action('ready', function($el){
                    $(".values").sortable({
                        connectWith: ".values",
                        start: function(event, ui) {
                            acf.do_action('sortstart', ui.item, ui.placeholder);
                        },
                        stop: function(event, ui) {
                            acf.do_action('sortstop', ui.item, ui.placeholder);
                            $(this).find('.mce-tinymce').each(function() {
                                tinyMCE.execCommand('mceRemoveControl', true, $(this).attr('id'));
                                tinyMCE.execCommand('mceAddControl', true, $(this).attr('id'));
                            });
                        }
                    });
                });
    
                acf.add_action('sortstop', function ($el) {
    
                    // check if the dropped element is within a repeater field
                    if($($el).parents('.acf-input > .acf-repeater').length) {
    
                        // get column_num from closest acf-row
                        var column_num = $($el).closest('.acf-row').attr('data-id');
    
                        // loop all (input) fields within dropped element and change / fix name
                        $($el).find('[name^="acf[field_"]').each(function() {
                            var field_name 		= 	$(this).attr('name');
                            field_name          =   field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
                            field_name[1]       =   '[' + column_num + ']'; // set the new row name
                            var new_name        =   'acf' + field_name.join('');
                            $(this).attr('name', new_name);
                        });
    
                        // get closest flexible-content-field and loop all layouts within this flexible-content-field
                        $($el).closest('.acf-field.acf-field-flexible-content').find('.acf-input > .acf-flexible-content > .values > .layout').each(function(index) {
    
                            // update order number
                            $(this).find('.acf-fc-layout-order:first').html(index+1);
    
                            // loop all (input) fields within dropped element and change / fix name
                            $(this).find('[name^="acf[field_"]').each(function() {
                                var field_name 		= 	$(this).attr('name');
                                field_name          =   field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
                                var tempIndex       =   parseInt(field_name[3].match(/([0-9]+)/g)); // hacky code
                                field_name[3]       =   field_name[3].replace(tempIndex, index); // set the new index
                                var new_name        =   'acf' + field_name.join('');
                                $(this).attr('name', new_name);
                            });
    
                            // click already selected buttons to trigger conditional logics
                            $(this).find('.acf-button-group label.selected').trigger('click');
                        });
                    }
                });
    
            })(jQuery);
    
        </script>
        <?php
    });
  • Hello, I’ve fixed @nerd-attack solution for drag&drop also arrays like gallery field etc.

    
    // acf drag n drop flexible layouts between repeaters
    add_action('acf/input/admin_footer', function () {
        ?>
        <script type="text/javascript">
    
            (function($) {
    
                acf.add_action('ready', function($el){
                    $(".values").sortable({
                        connectWith: ".values",
                        start: function(event, ui) {
                            acf.do_action('sortstart', ui.item, ui.placeholder);
                        },
                        stop: function(event, ui) {
                            acf.do_action('sortstop', ui.item, ui.placeholder);
                            $(this).find('.mce-tinymce').each(function() {
                                tinyMCE.execCommand('mceRemoveControl', true, $(this).attr('id'));
                                tinyMCE.execCommand('mceAddControl', true, $(this).attr('id'));
                            });
                        }
                    });
                });
    
                acf.add_action('sortstop', function ($el) {
    
    		// check if the dropped element is within a repeater field
    		if ($($el).parents('.acf-input > .acf-repeater').length) {
    
    			// get column_num from closest acf-row
    			var column_num = $($el).closest('.acf-row').attr('data-id');
    
    			// loop all (input) fields within dropped element and change / fix name
    			$($el).find('[name^="acf[field_"]').each(function () {
    				var field_name = $(this).attr('name');
    				field_name = field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
    				field_name[1] = '[' + column_num + ']'; // set the new row name
    				var new_name = 'acf' + field_name.join('');
    				$(this).attr('name', new_name);
    			});
    
    			// get closest flexible-content-field and loop all layouts within this flexible-content-field
    			$($el).closest('.acf-field.acf-field-flexible-content').find('.acf-input > .acf-flexible-content > .values > .layout').each(function (index) {
    
    				// update order number
    				$(this).find('.acf-fc-layout-order:first').html(index + 1);
    
    				// loop all (input) fields within dropped element and change / fix name
    				$(this).find('[name^="acf[field_"]').each(function () {
    					var field_name = $(this).attr('name');
    					var is_array = field_name.endsWith('[]')
    					field_name = field_name.match(/\[([a-zA-Z0-9_-]+\])/g); // split name attribute
    					var tempIndex = parseInt(field_name[3].match(/([0-9]+)/g)); // hacky code
    					field_name[3] = field_name[3].replace(tempIndex, index); // set the new index
    					var new_name = 'acf' + field_name.join('');
    
    					$(this).attr('name', new_name + is_array ? '[]' : '');
    				});
    
    				// click already selected buttons to trigger conditional logics
    				$(this).find('.acf-button-group label.selected').trigger('click');
    			});
    		}
    	});
    
            })(jQuery);
    
        </script>
        <?php
    });
    
Viewing 25 posts - 1 through 25 (of 28 total)

The topic ‘Drag Flexible Content layouts between parent Repeaters’ is closed to new replies.