From 8f68e2095e752b65e6a4b1da347ae1ca4318a53e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 30 Sep 2021 17:03:01 +0200
Subject: [PATCH] fields: possibility to order items of Item(s)Field (#57251)

---
 wcs/fields.py                        |  1 +
 wcs/qommon/form.py                   |  4 ++-
 wcs/qommon/static/css/dc2/admin.scss |  3 +-
 wcs/qommon/static/js/widget_list.js  | 49 ++++++++++++++++++----------
 4 files changed, 37 insertions(+), 20 deletions(-)

diff --git a/wcs/fields.py b/wcs/fields.py
index 0408d4e3..e1d791c1 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -1793,6 +1793,7 @@ class ItemFieldMixin:
             element_kwargs={'render_br': False, 'size': 50},
             add_element_label=_('Add item'),
             attrs={'data-dynamic-display-child-of': 'data_mode', 'data-dynamic-display-value': 'simple-list'},
+            extra_css_class='sortable',
         )
         form.add(
             data_sources.DataSourceSelectionWidget,
diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py
index eed14554..18f15a75 100644
--- a/wcs/qommon/form.py
+++ b/wcs/qommon/form.py
@@ -1680,6 +1680,8 @@ class WidgetList(quixote.form.widget.WidgetList):
         if add_element_label == 'Add row':
             add_element_label = str(_('Add row'))
 
+        self.extra_css_class = kwargs.pop('extra_css_class', None)
+
         CompositeWidget.__init__(self, name, value=value, **kwargs)
         self.element_type = element_type
         self.element_kwargs = element_kwargs or {}
@@ -1727,7 +1729,7 @@ class WidgetList(quixote.form.widget.WidgetList):
         self.element_names.append(name)
 
     def add_media(self):
-        get_response().add_javascript(['jquery.js', 'widget_list.js'])
+        get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'widget_list.js'])
 
     def transfer_form_value(self, request):
         for widget in self.get_widgets():
diff --git a/wcs/qommon/static/css/dc2/admin.scss b/wcs/qommon/static/css/dc2/admin.scss
index 55e78a33..f48945c2 100644
--- a/wcs/qommon/static/css/dc2/admin.scss
+++ b/wcs/qommon/static/css/dc2/admin.scss
@@ -33,7 +33,8 @@ ul.biglist {
 	padding: 0.5em 0;
 }
 
-ul.biglist span.handle {
+ul.biglist span.handle,
+.widget span.handle {
 	cursor: move;
 	display: inline-block;
 	padding: 0.5ex;
diff --git a/wcs/qommon/static/js/widget_list.js b/wcs/qommon/static/js/widget_list.js
index 74ad168b..93c6ca8b 100644
--- a/wcs/qommon/static/js/widget_list.js
+++ b/wcs/qommon/static/js/widget_list.js
@@ -21,7 +21,6 @@ function prepare_widget_list_elements() {
                      */
                     return true;
                 }
-
                 if ($(row).find('[name$=add_element], [data-dynamic-display-value]').length > 0) {
                     /* this has complex widgets, don't use
                      * javascript in that case */
@@ -32,24 +31,19 @@ function prepare_widget_list_elements() {
                 var new_row = $(row).clone();
 
                 /* fix up form element name */
-                $(new_row).find('[name^=' + prefix + ']').each(
-                    function() {
-                        var cur_name = $(this).attr('name');
-                        var pos = cur_name.indexOf('element', prefix.length) + 7; // 7 == len(element)
-                        var index = cur_name.substring(pos, cur_name.length);
-                        var after_pos = Math.max(index.indexOf('-'), index.indexOf('$'));
-                        var after = '';
-                        if (after_pos != -1) {
-                            after = index.substring(after_pos, index.length);
-                        }
-                        var new_name = cur_name.substring(0, cur_name.length-index.length) +
-                            (parseInt(index)+1) + after;
-                        $(this).attr('name', new_name);
-                        if ($(this).attr('type') == 'text') {
-                            $(this).attr('value', '');
-                        }
+                if ($('[name^=' + prefix + ']', new_row).length) {
+                    var $element = $('[name^=' + prefix + ']', new_row);
+                    if ($element.attr('type') == 'text') {
+                        $element.attr('value', '');
                     }
-                );
+                    var cur_name = $element.attr('name');
+                    var pos = cur_name.indexOf('element', prefix.length) + 7; // 7 == len(element)
+                    var index = cur_name.substring(pos, cur_name.length);
+                    var element_regex = RegExp(`\\$element(\\d)`, 'g');
+                    index = parseInt(index) + 1;
+                    $(new_row).html($(new_row).html().replace(element_regex, `$element${index}`));
+                    $(new_row).attr('data-widget-name', $(new_row).attr('data-widget-name').replace(element_regex, `$element${index}`));
+                }
                 if (name_attr == 'to$add_element') {
                   /* replace 1st list element by an empty label, as it's used
                    * to remove an actual selection */
@@ -65,6 +59,25 @@ function prepare_widget_list_elements() {
                 return false;
             }
         );
+
+        $('.widget.sortable input').each(function () {
+            $('<span class="handle">⣿</span>').insertBefore($(this));
+        });
+        $('.widget.sortable .content').sortable({
+            handle: '.handle',
+            start: function(event, ui) {
+                $('.widget.StringWidget input', $(this)).each(function () {
+                    $(this).attr('value', $(this).val());  // save potential new values before the move
+                });
+            },
+            update : function(event, ui) {
+                $('.widget.StringWidget', $(this)).each(function (index) {
+                    var element_regex = RegExp(`\\$element(\\d)`, 'g');
+                    $(this).html($(this).html().replace(element_regex, `$element${index}`));
+                    $(this).attr('data-widget-name', $(this).attr('data-widget-name').replace(element_regex, `$element${index}`));
+                });
+            },
+        });
     }
 
 $(document).ready(prepare_widget_list_elements);
-- 
2.33.0

