瀏覽代碼

Merge pull request #2914 from smzchaudary/select2-ng

add maximum selection length option
Kevin Brown 10 年之前
父節點
當前提交
dc0ebd35d5

+ 43 - 2
dist/js/select2.amd.full.js

@@ -2852,6 +2852,38 @@ define('select2/data/maximumInputLength',[
   return MaximumInputLength;
 });
 
+define('select2/data/maximumSelectionLength',[
+
+], function (){
+  function MaximumSelectionLength (decorated, $e, options) {
+    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumSelectionLength.prototype.query =
+    function (decorated, params, callback) {
+      var self = this;
+
+      this.current(function (currentData) {
+        var count = currentData != null ? currentData.length : 0;
+        if (self.maximumSelectionLength > 0 &&
+          count >= self.maximumSelectionLength) {
+          self.trigger('results:message', {
+            message: 'maximumSelected',
+            args: {
+              maximum: self.maximumSelectionLength
+            }
+          });
+          return;
+        }
+        decorated.call(self, params, callback);
+      });
+  };
+
+  return MaximumSelectionLength;
+});
+
 define('select2/dropdown',[
   './utils'
 ], function (Utils) {
@@ -3403,6 +3435,7 @@ define('select2/defaults',[
   './data/tokenizer',
   './data/minimumInputLength',
   './data/maximumInputLength',
+  './data/maximumSelectionLength',
 
   './dropdown',
   './dropdown/search',
@@ -3421,7 +3454,7 @@ define('select2/defaults',[
              Utils, Translation, DIACRITICS,
 
              SelectData, ArrayData, AjaxData, Tags, Tokenizer,
-             MinimumInputLength, MaximumInputLength,
+             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
 
              Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              AttachBody, MinimumResultsForSearch, SelectOnClose,
@@ -3457,6 +3490,13 @@ define('select2/defaults',[
         );
       }
 
+      if (options.maximumSelectionLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumSelectionLength
+        );
+      }
+
       if (options.tags != null) {
         options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
       }
@@ -3570,7 +3610,7 @@ define('select2/defaults',[
           language = Translation.loadPath(name);
         } catch (e) {
           // If we couldn't load it, check if it wasn't the full path
-          name = this.get('amdTranslationBase') + name;
+          name = this.defaults.amdLanguageBase + name;
           language = Translation.loadPath(name);
         }
 
@@ -3647,6 +3687,7 @@ define('select2/defaults',[
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
+      maximumSelectionLength: 0,
       minimumResultsForSearch: 0,
       selectOnClose: false,
       sorter: function (data) {

+ 43 - 2
dist/js/select2.amd.js

@@ -2852,6 +2852,38 @@ define('select2/data/maximumInputLength',[
   return MaximumInputLength;
 });
 
+define('select2/data/maximumSelectionLength',[
+
+], function (){
+  function MaximumSelectionLength (decorated, $e, options) {
+    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumSelectionLength.prototype.query =
+    function (decorated, params, callback) {
+      var self = this;
+
+      this.current(function (currentData) {
+        var count = currentData != null ? currentData.length : 0;
+        if (self.maximumSelectionLength > 0 &&
+          count >= self.maximumSelectionLength) {
+          self.trigger('results:message', {
+            message: 'maximumSelected',
+            args: {
+              maximum: self.maximumSelectionLength
+            }
+          });
+          return;
+        }
+        decorated.call(self, params, callback);
+      });
+  };
+
+  return MaximumSelectionLength;
+});
+
 define('select2/dropdown',[
   './utils'
 ], function (Utils) {
@@ -3403,6 +3435,7 @@ define('select2/defaults',[
   './data/tokenizer',
   './data/minimumInputLength',
   './data/maximumInputLength',
+  './data/maximumSelectionLength',
 
   './dropdown',
   './dropdown/search',
@@ -3421,7 +3454,7 @@ define('select2/defaults',[
              Utils, Translation, DIACRITICS,
 
              SelectData, ArrayData, AjaxData, Tags, Tokenizer,
-             MinimumInputLength, MaximumInputLength,
+             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
 
              Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              AttachBody, MinimumResultsForSearch, SelectOnClose,
@@ -3457,6 +3490,13 @@ define('select2/defaults',[
         );
       }
 
+      if (options.maximumSelectionLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumSelectionLength
+        );
+      }
+
       if (options.tags != null) {
         options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
       }
@@ -3570,7 +3610,7 @@ define('select2/defaults',[
           language = Translation.loadPath(name);
         } catch (e) {
           // If we couldn't load it, check if it wasn't the full path
-          name = this.get('amdTranslationBase') + name;
+          name = this.defaults.amdLanguageBase + name;
           language = Translation.loadPath(name);
         }
 
@@ -3647,6 +3687,7 @@ define('select2/defaults',[
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
+      maximumSelectionLength: 0,
       minimumResultsForSearch: 0,
       selectOnClose: false,
       sorter: function (data) {

+ 43 - 2
dist/js/select2.full.js

@@ -12387,6 +12387,38 @@ define('select2/data/maximumInputLength',[
   return MaximumInputLength;
 });
 
+define('select2/data/maximumSelectionLength',[
+
+], function (){
+  function MaximumSelectionLength (decorated, $e, options) {
+    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumSelectionLength.prototype.query =
+    function (decorated, params, callback) {
+      var self = this;
+
+      this.current(function (currentData) {
+        var count = currentData != null ? currentData.length : 0;
+        if (self.maximumSelectionLength > 0 &&
+          count >= self.maximumSelectionLength) {
+          self.trigger('results:message', {
+            message: 'maximumSelected',
+            args: {
+              maximum: self.maximumSelectionLength
+            }
+          });
+          return;
+        }
+        decorated.call(self, params, callback);
+      });
+  };
+
+  return MaximumSelectionLength;
+});
+
 define('select2/dropdown',[
   './utils'
 ], function (Utils) {
@@ -12938,6 +12970,7 @@ define('select2/defaults',[
   './data/tokenizer',
   './data/minimumInputLength',
   './data/maximumInputLength',
+  './data/maximumSelectionLength',
 
   './dropdown',
   './dropdown/search',
@@ -12956,7 +12989,7 @@ define('select2/defaults',[
              Utils, Translation, DIACRITICS,
 
              SelectData, ArrayData, AjaxData, Tags, Tokenizer,
-             MinimumInputLength, MaximumInputLength,
+             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
 
              Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              AttachBody, MinimumResultsForSearch, SelectOnClose,
@@ -12992,6 +13025,13 @@ define('select2/defaults',[
         );
       }
 
+      if (options.maximumSelectionLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumSelectionLength
+        );
+      }
+
       if (options.tags != null) {
         options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
       }
@@ -13105,7 +13145,7 @@ define('select2/defaults',[
           language = Translation.loadPath(name);
         } catch (e) {
           // If we couldn't load it, check if it wasn't the full path
-          name = this.get('amdTranslationBase') + name;
+          name = this.defaults.amdLanguageBase + name;
           language = Translation.loadPath(name);
         }
 
@@ -13182,6 +13222,7 @@ define('select2/defaults',[
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
+      maximumSelectionLength: 0,
       minimumResultsForSearch: 0,
       selectOnClose: false,
       sorter: function (data) {

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.full.min.js


+ 43 - 2
dist/js/select2.js

@@ -3280,6 +3280,38 @@ define('select2/data/maximumInputLength',[
   return MaximumInputLength;
 });
 
+define('select2/data/maximumSelectionLength',[
+
+], function (){
+  function MaximumSelectionLength (decorated, $e, options) {
+    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumSelectionLength.prototype.query =
+    function (decorated, params, callback) {
+      var self = this;
+
+      this.current(function (currentData) {
+        var count = currentData != null ? currentData.length : 0;
+        if (self.maximumSelectionLength > 0 &&
+          count >= self.maximumSelectionLength) {
+          self.trigger('results:message', {
+            message: 'maximumSelected',
+            args: {
+              maximum: self.maximumSelectionLength
+            }
+          });
+          return;
+        }
+        decorated.call(self, params, callback);
+      });
+  };
+
+  return MaximumSelectionLength;
+});
+
 define('select2/dropdown',[
   './utils'
 ], function (Utils) {
@@ -3831,6 +3863,7 @@ define('select2/defaults',[
   './data/tokenizer',
   './data/minimumInputLength',
   './data/maximumInputLength',
+  './data/maximumSelectionLength',
 
   './dropdown',
   './dropdown/search',
@@ -3849,7 +3882,7 @@ define('select2/defaults',[
              Utils, Translation, DIACRITICS,
 
              SelectData, ArrayData, AjaxData, Tags, Tokenizer,
-             MinimumInputLength, MaximumInputLength,
+             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
 
              Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              AttachBody, MinimumResultsForSearch, SelectOnClose,
@@ -3885,6 +3918,13 @@ define('select2/defaults',[
         );
       }
 
+      if (options.maximumSelectionLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumSelectionLength
+        );
+      }
+
       if (options.tags != null) {
         options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
       }
@@ -3998,7 +4038,7 @@ define('select2/defaults',[
           language = Translation.loadPath(name);
         } catch (e) {
           // If we couldn't load it, check if it wasn't the full path
-          name = this.get('amdTranslationBase') + name;
+          name = this.defaults.amdLanguageBase + name;
           language = Translation.loadPath(name);
         }
 
@@ -4075,6 +4115,7 @@ define('select2/defaults',[
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
+      maximumSelectionLength: 0,
       minimumResultsForSearch: 0,
       selectOnClose: false,
       sorter: function (data) {

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.min.js


+ 27 - 1
docs/examples.html

@@ -392,6 +392,28 @@ $(".js-programmatic-destroy").on("click", function () { $example.select2("destro
 
 $(".js-programmatic-multi-set-val").on("click", function () { $exampleMulti.val(["CA", "AL"]).trigger("change"); });
 $(".js-programmatic-multi-clear").on("click", function () { $exampleMulti.val(null).trigger("change"); });
+</script>
+    </div>
+  </section>
+  <section id="multiple_max" class="row">
+    <div class="col-md-4">
+      <h1>Limiting the number of selections</h1>
+      <p>Select2 multi-value select boxes can set restrictions regarding the maximum number of options selected.
+        The select below is declared with the <code>multiple</code> attribute with <code>maxSelectionLength</code> in the select2 options</p>
+
+      <p>
+        <select class="js-example-basic-multiple-limit js-states form-control" multiple="multiple"></select>
+      </p>
+    </div>
+    <div class="col-md-8">
+      <h2>Example code</h2>
+
+      <pre data-fill-from=".js-code-multiple-limit"></pre>
+
+<script type="text/x-example-code" class="js-code-multiple-limit">
+$(".js-example-basic-multiple-limit").select2({
+  maximumSelectionLength: 2
+  });
 </script>
     </div>
   </section>
@@ -812,6 +834,7 @@ $.fn.select2.amd.require(
     function (Select2, Utils, oldMatcher) {
   var $basicSingle = $(".js-example-basic-single");
   var $basicMultiple = $(".js-example-basic-multiple");
+  var $limitMultiple = $(".js-example-basic-multiple-limit");
 
   var $dataArray = $(".js-example-data-array");
   var $dataArraySelected = $(".js-example-data-array-selected");
@@ -830,7 +853,10 @@ $.fn.select2.amd.require(
   var $language = $(".js-example-language");
 
   $basicSingle.select2();
-  $basicMultiple.select2()
+  $basicMultiple.select2();
+  $limitMultiple.select2({
+    maximumSelectionLength: 2
+  });
 
   $dataArray.select2({
     data: data

+ 31 - 0
src/js/select2/data/maximumSelectionLength.js

@@ -0,0 +1,31 @@
+define([
+
+], function (){
+  function MaximumSelectionLength (decorated, $e, options) {
+    this.maximumSelectionLength = options.get('maximumSelectionLength');
+
+    decorated.call(this, $e, options);
+  }
+
+  MaximumSelectionLength.prototype.query =
+    function (decorated, params, callback) {
+      var self = this;
+
+      this.current(function (currentData) {
+        var count = currentData != null ? currentData.length : 0;
+        if (self.maximumSelectionLength > 0 &&
+          count >= self.maximumSelectionLength) {
+          self.trigger('results:message', {
+            message: 'maximumSelected',
+            args: {
+              maximum: self.maximumSelectionLength
+            }
+          });
+          return;
+        }
+        decorated.call(self, params, callback);
+      });
+  };
+
+  return MaximumSelectionLength;
+});

+ 11 - 2
src/js/select2/defaults.js

@@ -20,6 +20,7 @@ define([
   './data/tokenizer',
   './data/minimumInputLength',
   './data/maximumInputLength',
+  './data/maximumSelectionLength',
 
   './dropdown',
   './dropdown/search',
@@ -38,7 +39,7 @@ define([
              Utils, Translation, DIACRITICS,
 
              SelectData, ArrayData, AjaxData, Tags, Tokenizer,
-             MinimumInputLength, MaximumInputLength,
+             MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
 
              Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              AttachBody, MinimumResultsForSearch, SelectOnClose,
@@ -74,6 +75,13 @@ define([
         );
       }
 
+      if (options.maximumSelectionLength > 0) {
+        options.dataAdapter = Utils.Decorate(
+          options.dataAdapter,
+          MaximumSelectionLength
+        );
+      }
+
       if (options.tags != null) {
         options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
       }
@@ -187,7 +195,7 @@ define([
           language = Translation.loadPath(name);
         } catch (e) {
           // If we couldn't load it, check if it wasn't the full path
-          name = this.get('amdTranslationBase') + name;
+          name = this.defaults.amdLanguageBase + name;
           language = Translation.loadPath(name);
         }
 
@@ -264,6 +272,7 @@ define([
       matcher: matcher,
       minimumInputLength: 0,
       maximumInputLength: 0,
+      maximumSelectionLength: 0,
       minimumResultsForSearch: 0,
       selectOnClose: false,
       sorter: function (data) {

+ 199 - 0
tests/data/maximumSelectionLength-tests.js

@@ -0,0 +1,199 @@
+module('Data adapters - Maximum selection length');
+
+var MaximumSelectionLength = require('select2/data/maximumSelectionLength');
+
+var $ = require('jquery');
+var Options = require('select2/options');
+var Utils = require('select2/utils');
+
+function StubData () {
+  this.called = false;
+  this.currentData = [];
+}
+
+StubData.prototype.current = function (callback) {
+  callback(this.currentData);
+};
+
+StubData.prototype.val = function (val) {
+  this.currentData.push(val);
+};
+
+StubData.prototype.query = function (params, callback) {
+  this.called = true;
+};
+
+var MaximumData = Utils.Decorate(StubData, MaximumSelectionLength);
+
+test('0 never displays the notice', function (assert) {
+  var zeroOptions = new Options({
+    maximumSelectionLength: 0
+  });
+
+  var data = new MaximumData(null, zeroOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, zeroOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.val('1');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, zeroOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.val('1');
+  data.val('2');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+});
+
+test('< 0 never displays the notice', function (assert) {
+  var negativeOptions = new Options({
+    maximumSelectionLength: -1
+  });
+
+  var data = new MaximumData(null, negativeOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, negativeOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.val('1');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, negativeOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.val('1');
+  data.val('2');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+});
+
+test('triggers when >= 1 selection' , function (assert) {
+  var maxOfOneOptions = new Options({
+    maximumSelectionLength: 1
+  });
+  var data = new MaximumData(null, maxOfOneOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, maxOfOneOptions);
+
+  data.trigger = function () {
+    assert.ok(true, 'The event should be triggered.');
+  };
+
+  data.val('1');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(!data.called);
+
+});
+
+test('triggers when >= 2 selections' , function (assert) {
+  var maxOfTwoOptions = new Options({
+    maximumSelectionLength: 2
+  });
+  var data = new MaximumData(null, maxOfTwoOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, maxOfTwoOptions);
+
+  data.trigger = function () {
+    assert.ok(false, 'No events should be triggered');
+  };
+
+  data.val('1');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(data.called);
+
+  data = new MaximumData(null, maxOfTwoOptions);
+
+  data.trigger = function () {
+    assert.ok(true, 'The event should be triggered.');
+  };
+
+  data.val('1');
+  data.val('2');
+
+  data.query({
+    term: ''
+  });
+
+  assert.ok(!data.called);
+
+});

+ 19 - 0
tests/data/maximumSelectionLength.html

@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+  <head>
+    <link rel="stylesheet" href="../vendor/qunit-1.14.0.css" type="text/css" />
+    <link rel="stylesheet" href="../../dist/css/select2.css" type="text/css" />
+  </head>
+  <body>
+    <div id="qunit"></div>
+    <div id="qunit-fixture">
+    </div>
+
+    <script src="../vendor/qunit-1.14.0.js" type="text/javascript"></script>
+    <script src="../../vendor/almond-0.2.9.js" type="text/javascript"></script>
+    <script src="../../vendor/jquery-2.1.0.js" type="text/javascript"></script>
+    <script src="../../dist/js/select2.amd.js" type="text/javascript"></script>
+
+    <script src="maximumSelectionLength-tests.js" type="text/javascript"></script>
+  </body>
+</html>

Some files were not shown because too many files changed in this diff