CRM-14996 - Support multi-valued fields for state/country chain-select
authorColeman Watts <coleman@civicrm.org>
Thu, 21 Aug 2014 19:45:38 +0000 (20:45 +0100)
committerColeman Watts <coleman@civicrm.org>
Thu, 21 Aug 2014 20:26:39 +0000 (21:26 +0100)
When selecting more than one, it groups the results into option groups for better usability.
Applies select2 to all chain-select fields to avoid any push-me/pull-me in future patches to the js
(e.g. fixing the code to work for normal selects and breaking select2 and vice-versa).

CRM/Contact/Form/Search/Criteria.php
CRM/Core/Page/AJAX/Location.php
css/civicrm.css
templates/CRM/common/stateCountry.tpl

index 1aee74b7443552576964216f8c03b93821f83747..598cd72b80a9fc9ce2390ca461e1194cdc4064b5 100644 (file)
@@ -337,11 +337,11 @@ class CRM_Contact_Form_Search_Criteria {
             //if not setdefault any country
             $selectElements = CRM_Core_PseudoConstant::$select();
           }
-          $element = $form->add('select', $name, $title, $selectElements, FALSE, array('class' => 'crm-select2'));
+          $element = $form->add('select', $name, $title, $selectElements);
         }
         elseif ($select == 'country') {
           $selectElements = array('' => ts('- any -')) + CRM_Core_PseudoConstant::$select();
-          $element = $form->add('select', $name, $title, $selectElements, FALSE, array('class' => 'crm-select2'));
+          $element = $form->add('select', $name, $title, $selectElements);
         }
         elseif ($select == 'county') {
           if ( array_key_exists('state_province', $formValues) && !CRM_Utils_System::isNull($formValues['state_province'])) {
@@ -350,7 +350,7 @@ class CRM_Contact_Form_Search_Criteria {
           else {
             $selectElements = array('' => ts('- any -'));
           }
-          $element = $form->add('select', $name, $title, $selectElements, FALSE, array('class' => 'crm-select2'));
+          $element = $form->add('select', $name, $title, $selectElements);
         }
         else {
           $selectElements = array('' => ts('- any -')) + CRM_Core_PseudoConstant::$select();
index 3782674a2a9baa141cf8f094e1fdfb13d94e7391..d9e7023a97c56406f4154ca99300ccfa1579536d 100644 (file)
@@ -203,43 +203,62 @@ class CRM_Core_Page_AJAX_Location {
     if (empty($_GET['_value'])) {
       CRM_Utils_System::civiExit();
     }
+    $countries = (array) $_GET['_value'];
+    $elements = array();
+    $list = &$elements;
+    foreach ($countries as $val) {
+      $result = CRM_Core_PseudoConstant::stateProvinceForCountry($val);
 
-    $result = CRM_Core_PseudoConstant::stateProvinceForCountry($_GET['_value']);
-
-    $elements = array(array(
-      'name' => $result ? ts('- select a state -') : ts('- N/A -'),
-      'value' => '',
-    ));
-    foreach ($result as $id => $name) {
-      $elements[] = array(
-        'name' => $name,
-        'value' => $id,
-      );
+      // Option-groups for multiple countries
+      if ($result && count($countries) > 1) {
+        $elements[] = array(
+          'name' => CRM_Core_PseudoConstant::country($val, FALSE),
+          'children' => array(),
+        );
+        $list = &$elements[count($elements)-1]['children'];
+      }
+      foreach ($result as $id => $name) {
+        $list[] = array(
+          'name' => $name,
+          'value' => $id,
+        );
+      }
     }
-
-    echo json_encode($elements);
+    $placeholder = array(array('value' => '', 'name' => $elements ? ts('- select -') : ts('- N/A -')));
+    echo json_encode(array_merge($placeholder, $elements));
     CRM_Utils_System::civiExit();
   }
 
   static function jqCounty() {
+    $elements = array();
     if (!isset($_GET['_value']) || CRM_Utils_System::isNull($_GET['_value'])) {
       $elements = array(
         array('name' => ts('(choose state first)'), 'value' => '')
       );
     }
     else {
-      $result = CRM_Core_PseudoConstant::countyForState($_GET['_value']);
+      $states = (array) $_GET['_value'];
+      $list = &$elements;
+      foreach ($states as $val) {
+        $result = CRM_Core_PseudoConstant::countyForState($val);
 
-      $elements = array(array(
-        'name' => $result ? ts('- select -') : ts('- N/A -'),
-        'value' => '',
-      ));
-      foreach ($result as $id => $name) {
-        $elements[] = array(
-          'name' => $name,
-          'value' => $id,
-        );
+        // Option-groups for multiple countries
+        if ($result && count($states) > 1) {
+          $elements[] = array(
+            'name' => CRM_Core_PseudoConstant::stateProvince($val, FALSE),
+            'children' => array(),
+          );
+          $list = &$elements[count($elements)-1]['children'];
+        }
+        foreach ($result as $id => $name) {
+          $list[] = array(
+            'name' => $name,
+            'value' => $id,
+          );
+        }
       }
+      $placeholder = array(array('value' => '', 'name' => $elements ? ts('- select -') : ts('- N/A -')));
+      $elements = array_merge($placeholder, $elements);
     }
 
     echo json_encode($elements);
index 8e7d0ddaae61511174e7081a05ea7857195db0f5..50090250f93a7933e21c987b12441dd7dc089de3 100644 (file)
@@ -3628,8 +3628,8 @@ div.m ul#civicrm-menu,
 .crm-select2 {
   width: 15em;
 }
-.crm-container .select2-container {
-  min-width: 6em !important;
+.crm-container div.select2-container {
+  min-width: 6em;
   font-size: 11px;
 }
 /* Add arrow icon to multi-selects */
index 90f7274d5842ea8475e752288f8d0d90fe1d3261..b16e8a47c0b46b6e6d309edfd88ebf63f2dab2a4 100644 (file)
 <script type="text/javascript">
   {literal}
   CRM.$(function($) {
+    var $form = $('form.{/literal}{$form.formClass}{literal}');
     function chainSelect(e) {
       var info = $(this).data('chainSelect');
       var val = info.target.val();
       var multiple = info.target.attr('multiple');
       var placeholder = $(this).val() ? "{/literal}{ts escape='js'}Loading{/ts}{literal}..." : info.placeholder;
-      !multiple && info.target.html('<option value="">' + placeholder + '</option>');
+      if (multiple) {
+        info.target.html('').prop('disabled', true).crmSelect2({placeholder: placeholder});
+      }
+      else {
+        info.target.html('<option value="">' + placeholder + '</option>').prop('disabled', true).crmSelect2();
+      }
       if ($(this).val()) {
-        if (multiple) {
+        $.getJSON(info.callback, {_value: $(this).val()}, function(data) {
           var options = '';
-          $.each($(this).val(), function(index, value) {
-            $.getJSON(info.callback, {_value: value}, function(data) {
-              $.each(data, function() {
-                if (this.value) {
-                  options += '<option value="' + this.value + '">' + this.name + '</option>';
-                }
-              });
-              info.target.html(options).val(val).trigger('change');
-            });
-          });
-        }
-        else {
-          $.getJSON(info.callback, {_value: $(this).val()}, function(data) {
-            var options = '';
+          function buildOptions(data) {
             $.each(data, function() {
-              if (this.name) {
+              if (this.children) {
+                options += '<optgroup label="' + this.name + '">';
+                buildOptions(this.children);
+                options += '</optgroup>';
+              }
+              else if (this.value || !multiple) {
                 options += '<option value="' + this.value + '">' + this.name + '</option>';
               }
+              else {
+                info.target.crmSelect2({placeholder: this.name});
+              }
             });
-            info.target.html(options).val(val).trigger('change');
-          });
-        }
+          }
+          buildOptions(data);
+          info.target.html(options).val(val).prop('disabled', false).trigger('change');
+        });
+      }
+      else {
+        info.target.trigger('change');
       }
     }
+    function initField(selector) {
+      return $(selector, $form).css('min-width', '20em').crmSelect2();
+    }
     {/literal}
     {foreach from=$config->stateCountryMap item=stateCountryMap}
-      {if $stateCountryMap.country && $stateCountryMap.state_province}
-        $('select[name="{$stateCountryMap.country}"], #{$stateCountryMap.country}').data('chainSelect', {ldelim}
-          callback: CRM.url('civicrm/ajax/jqState'),
-          target: $('select[name="{$stateCountryMap.state_province}"], #{$stateCountryMap.state_province}'),
-          placeholder: "{ts escape='js'}(choose country first){/ts}"
-          {rdelim}).on('change', chainSelect);
-      {/if}
       {if $stateCountryMap.state_province && $stateCountryMap.county}
-        $('select[name="{$stateCountryMap.state_province}"], #{$stateCountryMap.state_province}').data('chainSelect', {ldelim}
+        $('select[name="{$stateCountryMap.state_province}"], select#{$stateCountryMap.state_province}', $form).data('chainSelect', {ldelim}
           callback: CRM.url('civicrm/ajax/jqCounty'),
-          target: $('select[name="{$stateCountryMap.county}"], #{$stateCountryMap.county}'),
+          target: initField('select[name="{$stateCountryMap.county}"], #{$stateCountryMap.county}'),
           placeholder: "{ts escape='js'}(choose state first){/ts}"
         {rdelim}).on('change',  chainSelect);
       {/if}
+      {if $stateCountryMap.country && $stateCountryMap.state_province}
+        initField('select[name="{$stateCountryMap.country}"], select#{$stateCountryMap.country}').data('chainSelect', {ldelim}
+          callback: CRM.url('civicrm/ajax/jqState'),
+          target: initField('select[name="{$stateCountryMap.state_province}"], #{$stateCountryMap.state_province}'),
+          placeholder: "{ts escape='js'}(choose country first){/ts}"
+        {rdelim}).on('change', chainSelect).change();
+      {/if}
     {/foreach}
     {literal}
   });