Selaa lähdekoodia

feat(form): add sortable option for multiple choice widget

Amine Zaghdoudi 10 vuotta sitten
vanhempi
commit
2ac5db96da

+ 53 - 0
Form/Extension/ChoiceTypeExtension.php

@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Sonata package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Form\Extension;
+
+use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\FormView;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+/**
+ * Class ChoiceTypeExtension
+ *
+ * @package Sonata\AdminBundle\Form\Extension
+ *
+ * @author Amine Zaghdoudi <amine.zaghdoudi@ekino.com>
+ */
+class ChoiceTypeExtension extends AbstractTypeExtension
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function setDefaultOptions(OptionsResolverInterface $resolver)
+    {
+        $resolver->setOptional(array('sortable'));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function buildView(FormView $view, FormInterface $form, array $options)
+    {
+        $view->vars['sortable'] = array_key_exists('sortable', $options) && $options['sortable'];
+    }
+
+    /**
+     * Returns the name of the type being extended.
+     *
+     * @return string The name of the type being extended
+     */
+    public function getExtendedType()
+    {
+        return 'choice';
+    }
+}

+ 4 - 0
Resources/config/form_types.xml

@@ -49,6 +49,10 @@
             <tag name="form.type_extension" alias="form" />
         </service>
 
+        <service id="sonata.admin.form.extension.choice" class="Sonata\AdminBundle\Form\Extension\ChoiceTypeExtension">
+            <tag name="form.type_extension" alias="choice" />
+        </service>
+
         <!-- Form Filter Type -->
         <service id="sonata.admin.form.filter.type.number" class="Sonata\AdminBundle\Form\Type\Filter\NumberType">
             <tag name="form.type" alias="sonata_type_filter_number" />

+ 13 - 0
Resources/doc/reference/form_types.rst

@@ -511,6 +511,19 @@ General
         <?php
         $form->add('status', null, array('label' => false);
 
+ChoiceType
+^^^^^^^^^^
+
+- ``sortable``: This option can be added for multiple choice widget to activate select2 sortable.
+
+.. code-block:: php
+
+        <?php
+        $form->add('multiChoices', 'choice', array(
+            'multiple' => true,
+            'sortable' => true
+        );
+
 .. _`Symfony field types`: http://symfony.com/doc/current/book/forms.html#built-in-field-types
 .. _`Symfony choice Field Type docs`: http://symfony.com/doc/current/reference/forms/types/choice.html
 .. _`Symfony PropertyPath`: http://api.symfony.com/2.0/Symfony/Component/Form/Util/PropertyPath.html

+ 42 - 0
Resources/public/Admin.js

@@ -444,5 +444,47 @@ var Admin = {
         Admin.log('[core|setup_tree_view] setup tree view', subject);
 
         jQuery('ul.js-treeview', subject).treeView();
+    },
+
+    /**
+     * Setup sortable multiple select2
+     */
+    setup_sortable_select2: function(subject, data) {
+        Admin.log('[core|setup_sortable_select2] configure sortable Select2 on', subject);
+
+        var transformedData = [];
+        for (var i = 0 ; i < data.length ; i++) {
+            transformedData[i] = {id: data[i].data, text: data[i].label};
+        }
+
+        subject.select2({
+            data:     transformedData,
+            multiple: true
+        });
+
+        subject.select2("container").find("ul.select2-choices").sortable({
+            containment: 'parent',
+            start: function () {
+                subject.select2("onSortStart");
+            },
+            update: function () {
+                subject.select2("onSortEnd");
+            }
+        });
+
+        // On form submit, transform value to match what is expected by server
+        subject.parents('form:first').submit(function (event) {
+            var values   = subject.val().split(',');
+            var baseName = subject.attr('name');
+            baseName = baseName.substring(0, baseName.length-1);
+            for (var i=0; i<values.length; i++) {
+                jQuery('<input>')
+                    .attr('type', 'hidden')
+                    .attr('name', baseName+i+']')
+                    .val(values[i])
+                    .appendTo(subject.parents('form:first'));
+            }
+            subject.remove();
+        });
     }
 };

+ 34 - 19
Resources/views/Form/form_admin_fields.html.twig

@@ -131,26 +131,30 @@ file that was distributed with this source code.
     {% if sonata_admin.admin and not sonata_admin.admin.getConfigurationPool().getOption('use_select2') %}
         {% set attr = attr|merge({'class': attr.class|default('') ~ ' form-control'}) %}
     {% endif %}
-    <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
-        {% if empty_value is not none %}
-            <option value=""{% if required and value is empty %} selected="selected"{% endif %}>
-                {% if not sonata_admin.admin %}
-                    {{- empty_value|trans({}, translation_domain) -}}
-                {% else %}
-                    {{- empty_value|trans({}, sonata_admin.field_description.translationDomain) -}}
-                {% endif%}
-            </option>
-        {% endif %}
-        {% if preferred_choices|length > 0 %}
-            {% set options = preferred_choices %}
-            {{ block('choice_widget_options') }}
-            {% if choices|length > 0 %}
-                <option disabled="disabled">{{ separator }}</option>
+    {% if sortable and multiple %}
+        {{ block('sonata_type_choice_multiple_sortable') }}
+    {% else %}
+        <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %} >
+            {% if empty_value is not none %}
+                <option value=""{% if required and value is empty %} selected="selected"{% endif %}>
+                    {% if not sonata_admin.admin %}
+                        {{- empty_value|trans({}, translation_domain) -}}
+                    {% else %}
+                        {{- empty_value|trans({}, sonata_admin.field_description.translationDomain) -}}
+                    {% endif%}
+                </option>
             {% endif %}
-        {% endif %}
-        {% set options = choices %}
-        {{ block('choice_widget_options') }}
-    </select>
+            {% if preferred_choices|length > 0 %}
+                {% set options = preferred_choices %}
+                {{ block('choice_widget_options') }}
+                {% if choices|length > 0 %}
+                    <option disabled="disabled">{{ separator }}</option>
+                {% endif %}
+            {% endif %}
+            {% set options = choices %}
+            {{ block('choice_widget_options') }}
+        </select>
+    {% endif %}
 {% endspaceless %}
 {% endblock choice_widget_collapsed %}
 
@@ -316,3 +320,14 @@ file that was distributed with this source code.
 
     </script>
 {% endblock %}
+
+{%  block sonata_type_choice_multiple_sortable %}
+    <input type="hidden" name="{{ full_name }}" id="{{ id }}" value="{{ value|join(',') }}" />
+    <script>
+        jQuery(document).ready(function() {
+            var $target = jQuery('#{{ id }}');
+
+            Admin.setup_sortable_select2($target, {{ form.vars.choices|json_encode|raw }});
+        });
+    </script>
+{% endblock %}

+ 54 - 0
Tests/Form/Extension/ChoiceTypeExtensionTest.php

@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Sonata package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Tests\Form\Extension;
+
+use Sonata\AdminBundle\Form\Extension\ChoiceTypeExtension;
+use Symfony\Component\Form\Forms;
+
+class ChoiceTypeExtensionTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setup()
+    {
+        $this->factory = Forms::createFormFactoryBuilder()
+            ->addTypeExtension(new ChoiceTypeExtension())
+            ->getFormFactory();
+    }
+
+    public function testExtendedType()
+    {
+        $extension = new ChoiceTypeExtension();
+
+        $this->assertEquals('choice', $extension->getExtendedType());
+    }
+
+    public function testDefaultOptionsWithSortable()
+    {
+        $view = $this->factory
+            ->create('choice', null, array(
+                'sortable' => true,
+            ))
+            ->createView();
+
+        $this->assertTrue(isset($view->vars['sortable']));
+        $this->assertTrue($view->vars['sortable']);
+    }
+
+    public function testDefaultOptionsWithoutSortable()
+    {
+        $view = $this->factory
+            ->create('choice', null, array())
+            ->createView();
+
+        $this->assertTrue(isset($view->vars['sortable']));
+        $this->assertFalse($view->vars['sortable']);
+    }
+}