Selaa lähdekoodia

Add back *css* options through an adapter

The old functionality where classes were directly copied to the
container can be done by setting `dropdownCssClass: ':all:'` when
initializing Select2.

This closes https://github.com/select2/select2/issues/2879.
Kevin Brown 10 vuotta sitten
vanhempi
commit
f3f1b31958

+ 4 - 1
Gruntfile.js

@@ -10,9 +10,12 @@ module.exports = function (grunt) {
   fullIncludes = [
     'jquery',
 
-    'select2/compat/matcher',
+    'select2/compat/containerCss',
+    'select2/compat/dropdownCss',
+
     'select2/compat/initSelection',
     'select2/compat/inputData',
+    'select2/compat/matcher',
     'select2/compat/query',
 
     'select2/dropdown/attachContainer',

+ 209 - 25
dist/js/select2.full.js

@@ -4428,6 +4428,19 @@ S2.define('select2/defaults',[
         );
       }
 
+      if (
+        options.dropdownCssClass != null ||
+        options.dropdownCss != null ||
+        options.adaptDropdownCssClass != null
+      ) {
+        var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
+
+        options.dropdownAdapter = Utils.Decorate(
+          options.dropdownAdapter,
+          DropdownCSS
+        );
+      }
+
       options.dropdownAdapter = Utils.Decorate(
         options.dropdownAdapter,
         AttachBody
@@ -4463,6 +4476,19 @@ S2.define('select2/defaults',[
         );
       }
 
+      if (
+        options.containerCssClass != null ||
+        options.containerCss != null ||
+        options.adaptContainerCssClass != null
+      ) {
+        var ContainerCSS = require(options.amdBase + 'compat/containerCss');
+
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          ContainerCSS
+        );
+      }
+
       options.selectionAdapter = Utils.Decorate(
         options.selectionAdapter,
         EventRelay
@@ -5283,47 +5309,162 @@ S2.define('select2/core',[
   return Select2;
 });
 
-S2.define('select2/compat/matcher',[
+S2.define('select2/compat/utils',[
   'jquery'
 ], function ($) {
-  function oldMatcher (matcher) {
-    function wrappedMatcher (params, data) {
-      var match = $.extend(true, {}, data);
+  function syncCssClasses ($dest, $src, adapter) {
+    var classes, replacements = [], adapted;
 
-      if (params.term == null || $.trim(params.term) === '') {
-        return match;
-      }
+    classes = $.trim($dest.attr('class'));
 
-      if (data.children) {
-        for (var c = data.children.length - 1; c >= 0; c--) {
-          var child = data.children[c];
+    if (classes) {
+      classes = '' + classes; // for IE which returns object
 
-          // Check if the child object matches
-          // The old matcher returned a boolean true or false
-          var doesMatch = matcher(params.term, child.text, child);
+      $(classes.split(/\s+/)).each(function () {
+        // Save all Select2 classes
+        if (this.indexOf('select2-') === 0) {
+          replacements.push(this);
+        }
+      });
+    }
 
-          // If the child didn't match, pop it off
-          if (!doesMatch) {
-            match.children.splice(c, 1);
+    classes = $.trim($src.attr('class'));
+
+    if (classes) {
+      classes = '' + classes; // for IE which returns object
+
+      $(classes.split(/\s+/)).each(function () {
+        // Only adapt non-Select2 classes
+        if (this.indexOf('select2-') !== 0) {
+          adapted = adapter(this);
+
+          if (adapted != null) {
+            replacements.push(adapted);
           }
         }
+      });
+    }
 
-        if (match.children.length > 0) {
-          return match;
+    $dest.attr('class', replacements.join(' '));
+  }
+
+  return {
+    syncCssClasses: syncCssClasses
+  };
+});
+
+S2.define('select2/compat/containerCss',[
+  'jquery',
+  './utils'
+], function ($, CompatUtils) {
+  // No-op CSS adapter that discards all classes by default
+  function _containerAdapter (clazz) {
+    return null;
+  }
+
+  function ContainerCSS () { }
+
+  ContainerCSS.prototype.render = function (decorated) {
+    var $container = decorated.call(this);
+
+    var containerCssClass = this.options.get('containerCssClass') || '';
+
+    if ($.isFunction(containerCssClass)) {
+      containerCssClass = containerCssClass(this.$element);
+    }
+
+    var containerCssAdapter = this.options.get('adaptContainerCssClass');
+    containerCssAdapter = containerCssAdapter || _containerAdapter;
+
+    if (containerCssClass.indexOf(':all:') !== -1) {
+      containerCssClass = containerCssClass.replace(':all', '');
+
+      var _cssAdapter = containerCssAdapter;
+
+      containerCssAdapter = function (clazz) {
+        var adapted = _cssAdapter(clazz);
+
+        if (adapted != null) {
+          // Append the old one along with the adapted one
+          return adapted + ' ' + clazz;
         }
-      }
 
-      if (matcher(params.term, data.text, data)) {
-        return match;
-      }
+        return clazz;
+      };
+    }
 
-      return null;
+    var containerCss = this.options.get('containerCss') || {};
+
+    if ($.isFunction(containerCss)) {
+      containerCss = containerCss(this.$element);
     }
 
-    return wrappedMatcher;
+    CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter);
+
+    $container.css(containerCss);
+    $container.addClass(containerCssClass);
+
+    return $container;
+  };
+
+  return ContainerCSS;
+});
+
+S2.define('select2/compat/dropdownCss',[
+  'jquery',
+  './utils'
+], function ($, CompatUtils) {
+  // No-op CSS adapter that discards all classes by default
+  function _dropdownAdapter (clazz) {
+    return null;
   }
 
-  return oldMatcher;
+  function DropdownCSS () { }
+
+  DropdownCSS.prototype.render = function (decorated) {
+    var $dropdown = decorated.call(this);
+
+    var dropdownCssClass = this.options.get('dropdownCssClass') || '';
+
+    if ($.isFunction(dropdownCssClass)) {
+      dropdownCssClass = dropdownCssClass(this.$element);
+    }
+
+    var dropdownCssAdapter = this.options.get('adaptDropdownCssClass');
+    dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter;
+
+    if (dropdownCssClass.indexOf(':all:') !== -1) {
+      dropdownCssClass = dropdownCssClass.replace(':all', '');
+
+      var _cssAdapter = dropdownCssAdapter;
+
+      dropdownCssAdapter = function (clazz) {
+        var adapted = _cssAdapter(clazz);
+
+        if (adapted != null) {
+          // Append the old one along with the adapted one
+          return adapted + ' ' + clazz;
+        }
+
+        return clazz;
+      };
+    }
+
+    var dropdownCss = this.options.get('dropdownCss') || {};
+
+    if ($.isFunction(dropdownCss)) {
+      dropdownCss = dropdownCss(this.$element);
+    }
+
+    CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter);
+
+    $dropdown.css(dropdownCss);
+    $dropdown.addClass(dropdownCssClass);
+
+    return $dropdown;
+  };
+
+  return DropdownCSS;
 });
 
 S2.define('select2/compat/initSelection',[
@@ -5497,6 +5638,49 @@ S2.define('select2/compat/inputData',[
   return InputData;
 });
 
+S2.define('select2/compat/matcher',[
+  'jquery'
+], function ($) {
+  function oldMatcher (matcher) {
+    function wrappedMatcher (params, data) {
+      var match = $.extend(true, {}, data);
+
+      if (params.term == null || $.trim(params.term) === '') {
+        return match;
+      }
+
+      if (data.children) {
+        for (var c = data.children.length - 1; c >= 0; c--) {
+          var child = data.children[c];
+
+          // Check if the child object matches
+          // The old matcher returned a boolean true or false
+          var doesMatch = matcher(params.term, child.text, child);
+
+          // If the child didn't match, pop it off
+          if (!doesMatch) {
+            match.children.splice(c, 1);
+          }
+        }
+
+        if (match.children.length > 0) {
+          return match;
+        }
+      }
+
+      if (matcher(params.term, data.text, data)) {
+        return match;
+      }
+
+      return null;
+    }
+
+    return wrappedMatcher;
+  }
+
+  return oldMatcher;
+});
+
 S2.define('select2/compat/query',[
 
 ], function () {

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/js/select2.full.min.js


+ 26 - 0
dist/js/select2.js

@@ -4428,6 +4428,19 @@ S2.define('select2/defaults',[
         );
       }
 
+      if (
+        options.dropdownCssClass != null ||
+        options.dropdownCss != null ||
+        options.adaptDropdownCssClass != null
+      ) {
+        var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
+
+        options.dropdownAdapter = Utils.Decorate(
+          options.dropdownAdapter,
+          DropdownCSS
+        );
+      }
+
       options.dropdownAdapter = Utils.Decorate(
         options.dropdownAdapter,
         AttachBody
@@ -4463,6 +4476,19 @@ S2.define('select2/defaults',[
         );
       }
 
+      if (
+        options.containerCssClass != null ||
+        options.containerCss != null ||
+        options.adaptContainerCssClass != null
+      ) {
+        var ContainerCSS = require(options.amdBase + 'compat/containerCss');
+
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          ContainerCSS
+        );
+      }
+
       options.selectionAdapter = Utils.Decorate(
         options.selectionAdapter,
         EventRelay

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/js/select2.min.js


+ 56 - 0
src/js/select2/compat/containerCss.js

@@ -0,0 +1,56 @@
+define([
+  'jquery',
+  './utils'
+], function ($, CompatUtils) {
+  // No-op CSS adapter that discards all classes by default
+  function _containerAdapter (clazz) {
+    return null;
+  }
+
+  function ContainerCSS () { }
+
+  ContainerCSS.prototype.render = function (decorated) {
+    var $container = decorated.call(this);
+
+    var containerCssClass = this.options.get('containerCssClass') || '';
+
+    if ($.isFunction(containerCssClass)) {
+      containerCssClass = containerCssClass(this.$element);
+    }
+
+    var containerCssAdapter = this.options.get('adaptContainerCssClass');
+    containerCssAdapter = containerCssAdapter || _containerAdapter;
+
+    if (containerCssClass.indexOf(':all:') !== -1) {
+      containerCssClass = containerCssClass.replace(':all', '');
+
+      var _cssAdapter = containerCssAdapter;
+
+      containerCssAdapter = function (clazz) {
+        var adapted = _cssAdapter(clazz);
+
+        if (adapted != null) {
+          // Append the old one along with the adapted one
+          return adapted + ' ' + clazz;
+        }
+
+        return clazz;
+      };
+    }
+
+    var containerCss = this.options.get('containerCss') || {};
+
+    if ($.isFunction(containerCss)) {
+      containerCss = containerCss(this.$element);
+    }
+
+    CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter);
+
+    $container.css(containerCss);
+    $container.addClass(containerCssClass);
+
+    return $container;
+  };
+
+  return ContainerCSS;
+});

+ 56 - 0
src/js/select2/compat/dropdownCss.js

@@ -0,0 +1,56 @@
+define([
+  'jquery',
+  './utils'
+], function ($, CompatUtils) {
+  // No-op CSS adapter that discards all classes by default
+  function _dropdownAdapter (clazz) {
+    return null;
+  }
+
+  function DropdownCSS () { }
+
+  DropdownCSS.prototype.render = function (decorated) {
+    var $dropdown = decorated.call(this);
+
+    var dropdownCssClass = this.options.get('dropdownCssClass') || '';
+
+    if ($.isFunction(dropdownCssClass)) {
+      dropdownCssClass = dropdownCssClass(this.$element);
+    }
+
+    var dropdownCssAdapter = this.options.get('adaptDropdownCssClass');
+    dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter;
+
+    if (dropdownCssClass.indexOf(':all:') !== -1) {
+      dropdownCssClass = dropdownCssClass.replace(':all', '');
+
+      var _cssAdapter = dropdownCssAdapter;
+
+      dropdownCssAdapter = function (clazz) {
+        var adapted = _cssAdapter(clazz);
+
+        if (adapted != null) {
+          // Append the old one along with the adapted one
+          return adapted + ' ' + clazz;
+        }
+
+        return clazz;
+      };
+    }
+
+    var dropdownCss = this.options.get('dropdownCss') || {};
+
+    if ($.isFunction(dropdownCss)) {
+      dropdownCss = dropdownCss(this.$element);
+    }
+
+    CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter);
+
+    $dropdown.css(dropdownCss);
+    $dropdown.addClass(dropdownCssClass);
+
+    return $dropdown;
+  };
+
+  return DropdownCSS;
+});

+ 43 - 0
src/js/select2/compat/utils.js

@@ -0,0 +1,43 @@
+define([
+  'jquery'
+], function ($) {
+  function syncCssClasses ($dest, $src, adapter) {
+    var classes, replacements = [], adapted;
+
+    classes = $.trim($dest.attr('class'));
+
+    if (classes) {
+      classes = '' + classes; // for IE which returns object
+
+      $(classes.split(/\s+/)).each(function () {
+        // Save all Select2 classes
+        if (this.indexOf('select2-') === 0) {
+          replacements.push(this);
+        }
+      });
+    }
+
+    classes = $.trim($src.attr('class'));
+
+    if (classes) {
+      classes = '' + classes; // for IE which returns object
+
+      $(classes.split(/\s+/)).each(function () {
+        // Only adapt non-Select2 classes
+        if (this.indexOf('select2-') !== 0) {
+          adapted = adapter(this);
+
+          if (adapted != null) {
+            replacements.push(adapted);
+          }
+        }
+      });
+    }
+
+    $dest.attr('class', replacements.join(' '));
+  }
+
+  return {
+    syncCssClasses: syncCssClasses
+  };
+});

+ 26 - 0
src/js/select2/defaults.js

@@ -165,6 +165,19 @@ define([
         );
       }
 
+      if (
+        options.dropdownCssClass != null ||
+        options.dropdownCss != null ||
+        options.adaptDropdownCssClass != null
+      ) {
+        var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
+
+        options.dropdownAdapter = Utils.Decorate(
+          options.dropdownAdapter,
+          DropdownCSS
+        );
+      }
+
       options.dropdownAdapter = Utils.Decorate(
         options.dropdownAdapter,
         AttachBody
@@ -200,6 +213,19 @@ define([
         );
       }
 
+      if (
+        options.containerCssClass != null ||
+        options.containerCss != null ||
+        options.adaptContainerCssClass != null
+      ) {
+        var ContainerCSS = require(options.amdBase + 'compat/containerCss');
+
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          ContainerCSS
+        );
+      }
+
       options.selectionAdapter = Utils.Decorate(
         options.selectionAdapter,
         EventRelay

+ 104 - 0
tests/dropdown/dropdownCss-tests.js

@@ -0,0 +1,104 @@
+module('Dropdown - dropdownCssClass compatibility');
+
+var $ = require('jquery');
+var Utils = require('select2/utils');
+var Options = require('select2/options');
+
+var Dropdown = require('select2/dropdown');
+var DropdownCSS = Utils.Decorate(
+  Dropdown,
+  require('select2/compat/dropdownCss')
+);
+
+test('all classes will be copied if :all: is used', function (assert) {
+  var $element = $('<select class="test copy works"></select>');
+  var options = new Options({
+    dropdownCssClass: ':all:'
+  });
+
+  var select = new DropdownCSS($element, options);
+  var $dropdown = select.render();
+
+  assert.ok($dropdown.hasClass('test'));
+  assert.ok($dropdown.hasClass('copy'));
+  assert.ok($dropdown.hasClass('works'));
+  assert.ok(!$dropdown.hasClass(':all:'));
+});
+
+test(':all: can be used with other classes', function (assert) {
+  var $element = $('<select class="test copy works"></select>');
+  var options = new Options({
+    dropdownCssClass: ':all: other'
+  });
+
+  var select = new DropdownCSS($element, options);
+  var $dropdown = select.render();
+
+  assert.ok($dropdown.hasClass('test'));
+  assert.ok($dropdown.hasClass('copy'));
+  assert.ok($dropdown.hasClass('works'));
+  assert.ok($dropdown.hasClass('other'));
+  assert.ok(!$dropdown.hasClass(':all:'));
+});
+
+test('classes can be passed in as a string', function (assert) {
+  var $element = $('<select class="test copy works"></select>');
+  var options = new Options({
+    dropdownCssClass: 'other'
+  });
+
+  var select = new DropdownCSS($element, options);
+  var $dropdown = select.render();
+
+  assert.ok($dropdown.hasClass('other'));
+});
+
+test('a function can be used based on the element', function (assert){
+  var $element = $('<select class="test"></select>');
+  var options = new Options({
+    dropdownCssClass: function ($element) {
+      return 'function';
+    }
+  });
+
+  var select = new DropdownCSS($element, options);
+  var $dropdown = select.render();
+
+  assert.ok($dropdown.hasClass('function'));
+  assert.ok(!$dropdown.hasClass('test'));
+});
+
+test(':all: works around custom adapters', function (assert) {
+  var $element = $('<select class="test"></select>');
+  var options = new Options({
+    dropdownCssClass: ':all: something',
+    adaptDropdownCssClass: function (clazz) {
+      return clazz + '-modified';
+    }
+  });
+
+  var select = new DropdownCSS($element, options);
+  var $dropdown = select.render();
+
+  assert.ok($dropdown.hasClass('something'));
+
+  assert.ok($dropdown.hasClass('test'));
+  assert.ok($dropdown.hasClass('test-modified'));
+});
+
+module('Dropdown - adaptDropdownCss compatibility');
+
+test('only return when adapted', function (assert) {
+  var $element = $('<select class="original"></select>');
+  var options = new Options({
+    adaptDropdownCssClass: function (clazz) {
+      return 'modified';
+    }
+  });
+
+  var select = new DropdownCSS($element, options);
+  var $dropdown = select.render();
+
+  assert.ok(!$dropdown.hasClass('original'));
+  assert.ok($dropdown.hasClass('modified'));
+});

+ 104 - 0
tests/selection/containerCss-tests.js

@@ -0,0 +1,104 @@
+module('Dropdown - containerCssClass compatibility');
+
+var $ = require('jquery');
+var Utils = require('select2/utils');
+var Options = require('select2/options');
+
+var SingleSelection = require('select2/selection/single');
+var ContainerCSS = Utils.Decorate(
+  SingleSelection,
+  require('select2/compat/containerCss')
+);
+
+test('all classes will be copied if :all: is used', function (assert) {
+  var $element = $('<select class="test copy works"></select>');
+  var options = new Options({
+    containerCssClass: ':all:'
+  });
+
+  var select = new ContainerCSS($element, options);
+  var $container = select.render();
+
+  assert.ok($container.hasClass('test'));
+  assert.ok($container.hasClass('copy'));
+  assert.ok($container.hasClass('works'));
+  assert.ok(!$container.hasClass(':all:'));
+});
+
+test(':all: can be used with other classes', function (assert) {
+  var $element = $('<select class="test copy works"></select>');
+  var options = new Options({
+    containerCssClass: ':all: other'
+  });
+
+  var select = new ContainerCSS($element, options);
+  var $container = select.render();
+
+  assert.ok($container.hasClass('test'));
+  assert.ok($container.hasClass('copy'));
+  assert.ok($container.hasClass('works'));
+  assert.ok($container.hasClass('other'));
+  assert.ok(!$container.hasClass(':all:'));
+});
+
+test('classes can be passed in as a string', function (assert) {
+  var $element = $('<select class="test copy works"></select>');
+  var options = new Options({
+    containerCssClass: 'other'
+  });
+
+  var select = new ContainerCSS($element, options);
+  var $container = select.render();
+
+  assert.ok($container.hasClass('other'));
+});
+
+test('a function can be used based on the element', function (assert){
+  var $element = $('<select class="test"></select>');
+  var options = new Options({
+    containerCssClass: function ($element) {
+      return 'function';
+    }
+  });
+
+  var select = new ContainerCSS($element, options);
+  var $container = select.render();
+
+  assert.ok($container.hasClass('function'));
+  assert.ok(!$container.hasClass('test'));
+});
+
+test(':all: works around custom adapters', function (assert) {
+  var $element = $('<select class="test"></select>');
+  var options = new Options({
+    containerCssClass: ':all: something',
+    adaptContainerCssClass: function (clazz) {
+      return clazz + '-modified';
+    }
+  });
+
+  var select = new ContainerCSS($element, options);
+  var $container = select.render();
+
+  assert.ok($container.hasClass('something'));
+
+  assert.ok($container.hasClass('test'));
+  assert.ok($container.hasClass('test-modified'));
+});
+
+module('Selection - adaptContainerCss compatibility');
+
+test('only return when adapted', function (assert) {
+  var $element = $('<select class="original"></select>');
+  var options = new Options({
+    adaptContainerCssClass: function (clazz) {
+      return 'modified';
+    }
+  });
+
+  var select = new ContainerCSS($element, options);
+  var $container = select.render();
+
+  assert.ok(!$container.hasClass('original'));
+  assert.ok($container.hasClass('modified'));
+});

+ 2 - 0
tests/unit.html

@@ -66,6 +66,7 @@
     <script src="data/maximumSelectionLength-tests.js" type="text/javascript"></script>
     <script src="data/minimumInputLength-tests.js" type="text/javascript"></script>
 
+    <script src="dropdown/dropdownCss-tests.js" type="text/javascript"></script>
     <script src="dropdown/selectOnClose-tests.js" type="text/javascript"></script>
     <script src="dropdown/stopPropagation-tests.js" type="text/javascript"></script>
 
@@ -75,6 +76,7 @@
     <script src="options/width-tests.js" type="text/javascript"></script>
 
     <script src="selection/allowClear-tests.js" type="text/javascript"></script>
+    <script src="selection/containerCss-tests.js" type="text/javascript"></script>
     <script src="selection/multiple-tests.js" type="text/javascript"></script>
     <script src="selection/placeholder-tests.js" type="text/javascript"></script>
     <script src="selection/single-tests.js" type="text/javascript"></script>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä