Skip to content

Commit 0da9de7

Browse files
committed
Merge branch 'nbolender-reorder-repeatable'
# Conflicts: # package-lock.json # package.json
2 parents d7d4cff + 33d5760 commit 0da9de7

File tree

1 file changed

+94
-18
lines changed

1 file changed

+94
-18
lines changed

src/resources/views/crud/fields/repeatable.blade.php

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,15 @@
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);
@@ -77,16 +85,32 @@
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
@@ -101,23 +125,31 @@
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

Comments
 (0)