Add drag and drop support to InlinePanel

pull/12716/head
Thibaud Colas 2025-01-07 10:31:58 +00:00
rodzic 1c3f84f9c2
commit fe9409f99c
4 zmienionych plików z 57 dodań i 0 usunięć

Wyświetl plik

@ -1,4 +1,5 @@
import $ from 'jquery';
import Sortable from 'sortablejs';
import { initCollapsiblePanels } from '../../includes/panels';
import { ExpandingFormset } from '../ExpandingFormset';
@ -19,6 +20,14 @@ export class InlinePanel extends ExpandingFormset {
super(opts.formsetPrefix, opts);
this.formsElt = $('#' + opts.formsetPrefix + '-FORMS');
if (this.opts.canOrder) {
this.sortable = Sortable.create(this.formsElt.get(0), {
handle: '[data-inline-panel-child-drag]',
animation: 200,
onEnd: this.handleDragEnd.bind(this),
});
}
for (let i = 0; i < this.formCount; i += 1) {
const childPrefix = this.opts.emptyChildFormPrefix.replace(
/__prefix__/g,
@ -315,4 +324,24 @@ export class InlinePanel extends ExpandingFormset {
}),
);
}
/**
* Update fields based on the current DOM order.
*/
updateOrderValues() {
const forms = this.formsElt.children(':not(.deleted)');
forms.each((index, form) => {
const prefix = form.id.replace('inline_child_', '');
const orderInput = $(form).find(`[name="${prefix}-ORDER"]`);
orderInput.val(index + 1);
});
}
handleDragEnd(e) {
const { oldIndex, newIndex } = e;
if (oldIndex !== newIndex) {
this.updateOrderValues();
this.updateControlStates();
}
}
}

Wyświetl plik

@ -30,6 +30,7 @@ describe('InlinePanel', () => {
<p>Form for inline child</p>
<button type="button" data-inline-panel-child-move-up>Move up</button>
<button type="button" data-inline-panel-child-move-down>Move down</button>
<button type="button" data-inline-panel-child-drag>Drag</button>
<button type="button" id="id_${childPrefix}-DELETE-button">Delete</button>
<input type="hidden" name="${childPrefix}-ORDER" id="id_${childPrefix}-ORDER">
<input type="hidden" name="${childPrefix}-DELETE" id="id_${childPrefix}-DELETE">
@ -116,4 +117,29 @@ describe('InlinePanel', () => {
expect(handleRemovedEvent).toHaveBeenCalledTimes(1);
});
it('updates order values after drag-and-drop', () => {
const addBtn = document.getElementById('id_person_cafe_relationship-ADD');
addBtn.click();
addBtn.click();
// Simulate drag-and-drop by manually moving an element.
const forms = document.querySelectorAll(
'[data-inline-panel-child]:not(.deleted)',
);
forms[0].parentElement.insertBefore(forms[0], forms[2]);
panel.handleDragEnd({ oldIndex: 0, newIndex: 2 });
expect(
Array.from(
document.querySelectorAll(
'[data-inline-panel-child]:not(.deleted) [name$="-ORDER"]',
),
).map((field) => [field.name, field.value]),
).toEqual([
['person_cafe_relationship-2-ORDER', '1'],
['person_cafe_relationship-1-ORDER', '2'],
['person_cafe_relationship-3-ORDER', '3'],
]);
});
});

Wyświetl plik

@ -7,6 +7,7 @@
{% if can_order %}
<button type="button" class="button button--icon text-replace white" data-inline-panel-child-move-up title="{% trans 'Move up' %}">{% icon name="arrow-up" %}</button>
<button type="button" class="button button--icon text-replace white" data-inline-panel-child-move-down title="{% trans 'Move down' %}">{% icon name="arrow-down" %}</button>
<button type="button" class="button button--icon text-replace white" data-inline-panel-child-drag title="{% trans 'Drag' %}">{% icon name="grip" %}</button>
{% endif %}
<button type="button" class="button button--icon text-replace white" id="{{ child.form.DELETE.id_for_label }}-button" title="{% trans 'Delete' %}">{% icon name="bin" %}</button>
{% endfragment %}

Wyświetl plik

@ -7,6 +7,7 @@
{% fragment as header_controls %}
<button type="button" class="button button--icon text-replace" data-inline-panel-child-move-up>{% icon name="arrow-up" %}{% trans "Move up" %}</button>
<button type="button" class="button button--icon text-replace" data-inline-panel-child-move-down>{% icon name="arrow-down" %}{% trans "Move down" %}</button>
<button type="button" class="button button--icon text-replace" data-inline-panel-child-drag>{% icon name="grip" %}{% trans "Drag" %}</button>
<button type="button" class="button button--icon text-replace" id="{{ form.DELETE.id_for_label }}-button">{% icon name="bin" %}{% trans "Delete" %}</button>
{% endfragment %}