3939 @push (' before_scripts' )
4040 <div class =" col-md-12 well repeatable-element row m-1 p-2" data-repeatable-identifier =" {{ $field [' name' ] } }" >
4141 @if (isset ($field [' fields' ]) && is_array ($field [' fields' ]) && count ($field [' fields' ]) )
42- <button type =" button" class =" close delete-element" ><span aria-hidden =" true" >×</span ></button >
42+ <div class =" controls" >
43+ <button type =" button" class =" close delete-element" ><span aria-hidden =" true" >×</span ></button >
44+ <button type =" button" class =" close move-element-up" >
45+ <svg viewBox =" 0 0 64 80" ><path d =" M46.8,36.7c-4.3-4.3-8.7-8.7-13-13c-1-1-2.6-1-3.5,0c-4.3,4.3-8.7,8.7-13,13c-2.3,2.3,1.3,5.8,3.5,3.5c4.3-4.3,8.7-8.7,13-13c-1.2,0-2.4,0-3.5,0c4.3,4.3,8.7,8.7,13,13C45.5,42.5,49,39,46.8,36.7L46.8,36.7z" /></svg >
46+ </button >
47+ <button type =" button" class =" close move-element-down" >
48+ <svg viewBox =" 0 0 64 80" ><path d =" M17.2,30.3c4.3,4.3,8.7,8.7,13,13c1,1,2.6,1,3.5,0c4.3-4.3,8.7-8.7,13-13c2.3-2.3-1.3-5.8-3.5-3.5c-4.3,4.3-8.7,8.7-13,13c1.2,0,2.4,0,3.5,0c-4.3-4.3-8.7-8.7-13-13C18.5,24.5,15,28,17.2,30.3L17.2,30.3z" /></svg >
49+ </button >
50+ </div >
4351 @foreach ($field [' fields' ] as $subfield )
4452 @php
4553 $subfield = $crud -> makeSureFieldHasNecessaryAttributes ($subfield );
7785 border-radius : 5px ;
7886 background-color : #f0f3f94f ;
7987 }
80- .container-repeatable-elements .delete-element {
81- z-index : 2 ;
88+ .container-repeatable-elements .controls {
89+ display : flex ;
90+ flex-direction : column ;
91+ align-content : center ;
8292 position : absolute !important ;
83- margin-left : -24px ;
84- margin-top : 0px ;
93+ left : -16px ;
94+ z-index : 2 ;
95+ }
96+
97+ .container-repeatable-elements .controls button {
8598 height : 30px ;
8699 width : 30px ;
87- border-radius : 15px ;
88- text-align : center ;
100+ border-radius : 50% ;
89101 background-color : #e8ebf0 !important ;
102+ margin-bottom : 2px ;
103+ overflow : hidden ;
104+ }
105+ .container-repeatable-elements .controls button .move-element-up ,
106+ .container-repeatable-elements .controls button .move-element-down {
107+ height : 24px ;
108+ width : 24px ;
109+ margin : 2px auto ;
110+ }
111+ .container-repeatable-elements .repeatable-element :first-of-type .move-element-up ,
112+ .container-repeatable-elements .repeatable-element :last-of-type .move-element-down {
113+ display : none ;
90114 }
91115 </style >
92116 @endpush
101125 */
102126 function repeatableInputToObj (container_name ) {
103127 var arr = [];
104- var obj = {};
105128
106129 var container = $ (' [data-repeatable-holder=' + container_name+ ' ]' );
107130
108131 container .find (' .well' ).each (function () {
109- $ (this ).find (' input, select, textarea' ).each (function () {
110- if ($ (this ).data (' repeatable-input-name' )) {
111- obj[$ (this ).data (' repeatable-input-name' )] = $ (this ).val ();
112- }
113- });
114- arr .push (obj);
115- obj = {};
132+ arr .push (repeatableElementToObj ($ (this )));
116133 });
117134
118135 return arr;
119136 }
120137
138+ /* *
139+ * Takes all inputs in a repeatable element and makes them an object.
140+ */
141+ function repeatableElementToObj (element ) {
142+ var obj = {};
143+
144+ element .find (' input, select, textarea' ).each (function () {
145+ if ($ (this ).data (' repeatable-input-name' )) {
146+ obj[$ (this ).data (' repeatable-input-name' )] = $ (this ).val ();
147+ }
148+ });
149+
150+ return obj;
151+ }
152+
121153 /* *
122154 * The method that initializes the javascript on this field type.
123155 */
@@ -188,7 +220,7 @@ function bpFieldInitRepeatableElement(element) {
188220 /* *
189221 * Adds a new field group to the repeatable input.
190222 */
191- function newRepeatableElement (container , field_group , values ) {
223+ function newRepeatableElement (container , field_group , values , position ) {
192224
193225 var field_name = container .data (' repeatable-identifier' );
194226 var new_field_group = field_group .clone ();
@@ -207,12 +239,40 @@ function newRepeatableElement(container, field_group, values) {
207239 // decrement the container current number of rows by -1
208240 updateRepeatableRowCount (container_holder, - 1 );
209241
210- $ (this ).parent ( ).remove ();
242+ $ (this ).closest ( ' .repeatable-element ' ).remove ();
211243
212244 // we reassure row numbers on delete
213245 setupElementRowsNumbers (container_holder);
214246 });
215247
248+ new_field_group .find (' .move-element-up, .move-element-down' ).click (function (){
249+ var $repeatableElement = $ (this ).closest (' .repeatable-element' );
250+
251+ // get existing values
252+ var values = repeatableElementToObj ($repeatableElement);
253+ var index = $repeatableElement .index ();
254+ index += $ (this ).is (' .move-element-up' ) ? - 1 : 1 ;
255+
256+ if (index < 0 ) return ;
257+
258+ // trigger delete for existing element
259+ new_field_group .find (' input, select, textarea' ).each (function (i , el ) {
260+ // we trigger this event so fields can intercept when they are beeing deleted from the page
261+ // implemented because of ckeditor instances that stayed around when deleted from page
262+ // introducing unwanted js errors and high memory usage.
263+ $ (el).trigger (' backpack_field.deleted' );
264+ });
265+
266+ // decrement the container current number of rows by -1
267+ updateRepeatableRowCount (container_holder, - 1 );
268+
269+ // remove element
270+ $repeatableElement .remove ();
271+
272+ // create new element with existing values in desired position
273+ newRepeatableElement (container, field_group, values, index);
274+ });
275+
216276 if (values != null ) {
217277 // set the value on field inputs, based on the JSON in the hidden input
218278 new_field_group .find (' input, select, textarea' ).each (function () {
@@ -237,7 +297,13 @@ function newRepeatableElement(container, field_group, values) {
237297 });
238298 }
239299 // we push the fields to the correct container in page.
240- container_holder .append (new_field_group);
300+ var $children = container_holder .children ();
301+
302+ if (! Number .isInteger (position) || $children .length - 1 < position) {
303+ container_holder .append (new_field_group);
304+ } else {
305+ container_holder .children ().eq (position).before (new_field_group);
306+ }
241307
242308 // after appending to the container we reassure row numbers
243309 setupElementRowsNumbers (container_holder);
@@ -248,6 +314,13 @@ function newRepeatableElement(container, field_group, values) {
248314 updateRepeatableRowCount (container_holder, 1 );
249315
250316 initializeFieldsWithJavascript (container_holder);
317+
318+ if (Number .isInteger (position)) {
319+ // Trigger change for elements that have moved
320+ new_field_group .find (' input, select, textarea' ).each (function (i , el ) {
321+ $ (el).trigger (' change' );
322+ });
323+ }
251324 }
252325
253326 // this function is responsible for managing rows numbers upon creation/deletion of elements
@@ -287,6 +360,9 @@ function updateRepeatableRowCount(container, amount) {
287360 // show or hide delete button
288361 container .find (' .delete-element' ).toggleClass (' d-none' , current_rows <= min_rows);
289362
363+ // show or hide move buttons
364+ container .find (' .move-element-up, .move-element-down' ).toggleClass (' d-none' , current_rows <= 1 );
365+
290366 // show or hide new item button
291367 container .parent ().parent ().find (' .add-repeatable-element-button' ).toggleClass (' d-none' , current_rows >= max_rows);
292368 }
0 commit comments