@@ -862,24 +862,34 @@ public function callback_repeater( $args ) {
862862 </div>
863863
864864 <script>
865- jQuery(document).ready(function($) {
866- var wrapper = $('#<?php echo esc_js ( $ args ['id ' ] ); ?> -wrapper');
867- var itemsContainer = wrapper.find('.<?php echo esc_js ( $ args ['id ' ] ); ?> -items');
868- var index = <?php echo esc_js ( (string ) count ( $ value ) ); ?> ;
865+ jQuery(document).ready(function($) {
866+ var wrapper = $('#<?php echo esc_js ( $ args ['id ' ] ); ?> -wrapper');
867+ var itemsContainer = wrapper.find('.<?php echo esc_js ( $ args ['id ' ] ); ?> -items');
868+ var index = <?php echo esc_js ( (string ) count ( $ value ) ); ?> ;
869+ var liveUpdateField = '<?php echo esc_js ( ! empty ( $ args ['live_update_field ' ] ) ? $ args ['live_update_field ' ] : 'name ' ); ?> ';
870+ var fallbackTitle = '<?php echo esc_js ( ! empty ( $ args ['new_item_text ' ] ) ? $ args ['new_item_text ' ] : $ this ->translation_strings ['repeater_new_item ' ] ); ?> ';
869871
870872 // Add Item
871- wrapper.on('click', '.add-item', function() {
872- var template = wrapper.find('.repeater-template').html();
873- template = template.replace(/{{INDEX}}/g, index);
874- itemsContainer.append(template);
875- index++;
876-
877- // Ensure the toggle icon for the new item is set to the collapsed state (▲)
878- itemsContainer.find('.repeater-item-header:last .toggle-icon').text('▲');
879-
880- // Ensure that .repeater-item-content is set to display:block
881- itemsContainer.find('.repeater-item-content:last').css('display', 'block');
882- });
873+ wrapper.on('click', '.add-item', function() {
874+ var template = wrapper.find('.repeater-template').html();
875+ var uniqueId = 'row_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
876+ template = template.replace(/{{INDEX}}/g, index);
877+ template = template.replace(/{{ROW_ID}}/g, uniqueId);
878+ itemsContainer.append(template);
879+ index++;
880+ var newItem = itemsContainer.find('.wz-repeater-item:last');
881+
882+ // Ensure the toggle icon for the new item is set to the collapsed state (▲)
883+ itemsContainer.find('.repeater-item-header:last .toggle-icon').text('▲');
884+
885+ // Ensure that .repeater-item-content is set to display:block
886+ itemsContainer.find('.repeater-item-content:last').css('display', 'block');
887+
888+ if (window.WZInitTomSelect) {
889+ window.WZInitTomSelect(newItem.get(0));
890+ }
891+ document.dispatchEvent(new CustomEvent('wz:repeater-item-added', { detail: { container: newItem.get(0) } }));
892+ });
883893
884894 // Remove Item
885895 wrapper.on('click', '.remove-item', function() {
@@ -923,20 +933,28 @@ public function callback_repeater( $args ) {
923933 }
924934 });
925935
926- // Reindex Items After Adding, Removing, or Moving
927- function reindexItems() {
928- itemsContainer.find('.wz-repeater-item').each(function(idx) {
936+ // Reindex Items After Adding, Removing, or Moving
937+ function reindexItems() {
938+ itemsContainer.find('.wz-repeater-item').each(function(idx) {
929939 $(this).find(':input').each(function() {
930940 var name = $(this).attr('name');
931- if (name) {
932- name = name.replace(/\[\d+\]/, '[' + idx + ']');
933- $(this).attr('name', name);
934- }
941+ if (name) {
942+ name = name.replace(/\[\d+\](?=\[(?:fields|row_id)\])/, '[' + idx + ']');
943+ $(this).attr('name', name);
944+ }
945+ });
935946 });
947+ }
948+
949+ // Live update repeater title when the specified field changes.
950+ wrapper.on('input', '.wz-repeater-item :input[name$="[fields][' + liveUpdateField + ']"]', function() {
951+ var $this = $(this);
952+ var newName = $this.val();
953+ var $repeaterTitle = $this.closest('.wz-repeater-item').find('.repeater-title');
954+ $repeaterTitle.text(newName || fallbackTitle);
936955 });
937- }
938- });
939- </script>
956+ });
957+ </script>
940958 <?php
941959 $ html = ob_get_clean ();
942960 $ html .= $ this ->get_field_description ( $ args );
@@ -953,18 +971,33 @@ function reindexItems() {
953971 * @param array|null $item Item data if exists.
954972 * @return void
955973 */
956- private function render_repeater_item ( $ args , $ index , $ item = null ) {
974+ public function render_repeater_item ( $ args , $ index , $ item = null ) {
957975 if ( empty ( $ args ['fields ' ] ) || ! is_array ( $ args ['fields ' ] ) ) {
958976 return ;
959977 }
960978
979+ $ fallback_title = ! empty ( $ args ['new_item_text ' ] ) ? $ args ['new_item_text ' ] : $ this ->translation_strings ['repeater_new_item ' ];
980+
981+ // Generate or retrieve unique row ID.
982+ $ item_id = '' ;
983+ if ( is_array ( $ item ) && isset ( $ item ['row_id ' ] ) ) {
984+ $ item_id = $ item ['row_id ' ];
985+ } elseif ( '{{INDEX}} ' !== $ index ) {
986+ // For existing items without row_id, generate a persistent one.
987+ $ item_id = 'row_ ' . md5 ( $ args ['id ' ] . '_ ' . $ index );
988+ } else {
989+ // For new items, use a placeholder that will be replaced.
990+ $ item_id = '{{ROW_ID}} ' ;
991+ }
992+
961993 ?>
962- <div class="wz-repeater-item">
963- <div class="repeater-item-header">
994+ <div class="wz-repeater-item" data-row-id="<?php echo esc_attr ( $ item_id ); ?> ">
995+ <input type="hidden" name="<?php echo esc_attr ( $ this ->settings_key ); ?> [<?php echo esc_attr ( $ args ['id ' ] ); ?> ][<?php echo esc_attr ( $ index ); ?> ][row_id]" value="<?php echo esc_attr ( $ item_id ); ?> " />
996+ <div class="repeater-item-header">
964997 <?php
965998 $ display_field = ! empty ( $ args ['live_update_field ' ] ) ? $ args ['live_update_field ' ] : 'name ' ;
966999 ?>
967- <span class="repeater-title"><?php echo esc_html ( ! empty ( $ item ['fields ' ][ $ display_field ] ) ? $ item ['fields ' ][ $ display_field ] : $ this -> translation_strings [ ' repeater_new_item ' ] ); ?> </span>
1000+ <span class="repeater-title"><?php echo esc_html ( ! empty ( $ item ['fields ' ][ $ display_field ] ) ? $ item ['fields ' ][ $ display_field ] : $ fallback_title ); ?> </span>
9681001 <span class="toggle-icon">▼</span>
9691002 </div>
9701003 <div class="repeater-item-content" style="display: none;">
@@ -1027,22 +1060,7 @@ private function render_repeater_item( $args, $index, $item = null ) {
10271060 </div>
10281061 </div>
10291062
1030- <script>
1031- jQuery(document).ready(function($) {
1032- var wrapper = $('#<?php echo esc_js ( $ args ['id ' ] ); ?> -wrapper');
1033- var itemsContainer = wrapper.find('.<?php echo esc_js ( $ args ['id ' ] ); ?> -items');
1034-
1035- // Live update repeater title when the specified field changes
1036- var liveUpdateField = '<?php echo esc_js ( ! empty ( $ args ['live_update_field ' ] ) ? $ args ['live_update_field ' ] : 'name ' ); ?> ';
1037- wrapper.on('input', '.wz-repeater-item input[name$="[fields][' + liveUpdateField + ']"]', function() {
1038- var $this = $(this);
1039- var newName = $this.val();
1040- var $repeaterTitle = $this.closest('.wz-repeater-item').find('.repeater-title');
1041- $repeaterTitle.text(newName || '<?php echo esc_js ( $ this ->translation_strings ['repeater_new_item ' ] ); ?> '); // Update title or set default if empty
1042- });
1043- });
1044- </script>
1045- <?php
1063+ <?php
10461064 }
10471065
10481066
0 commit comments