浏览代码

Added inline searching for multiple selects

This adds a search box to the main container for multiple select
boxes, similar to the what the old version had. This can still be
swapped out such that the search box displays above the results
like it previously did.

The placeholder for multiple selects will also now use the input
attribute of the search box instead of generating a custom element.

This also changes the selector for the search container to
`.select2-search`, which is consistent with the old class and
reduces the complexity of the selectors.

This fixes an issue with how decorators worked, where the constructor
from the parent class would clobber the custom constructor that is
generated for the new decorated class. This has been fixed by
excluding the constructor from the list of fields which are
transferred when decorating classes.
Kevin Brown 10 年之前
父节点
当前提交
217cd4cfd0

+ 18 - 12
dist/css/select2.css

@@ -26,10 +26,16 @@
   user-select: none;
   -webkit-user-select: none; }
   .select2-container .selection .multiple-select .rendered-selection {
-    display: block;
+    display: inline-block;
     overflow: hidden;
     padding-left: 8px;
     text-overflow: ellipsis; }
+.select2-container .select2-search-inline {
+  float: left; }
+  .select2-container .select2-search-inline input {
+    border: none;
+    font-size: 100%;
+    margin-top: 5px; }
 
 .select2-container .dropdown {
   background-color: white;
@@ -41,12 +47,6 @@
   left: -100000px;
   width: 100%;
   z-index: 100; }
-  .select2-container .dropdown .search {
-    display: block;
-    padding: 4px; }
-    .select2-container .dropdown .search input {
-      padding: 4px;
-      width: 100%; }
   .select2-container .dropdown .results {
     display: block; }
     .select2-container .dropdown .results .options {
@@ -59,6 +59,12 @@
         -webkit-user-select: none; }
         .select2-container .dropdown .results .options .option[aria-selected] {
           cursor: pointer; }
+.select2-container .select2-search {
+  display: block;
+  padding: 4px; }
+  .select2-container .select2-search input {
+    padding: 4px;
+    width: 100%; }
 .select2-container.open .dropdown {
   border-top: none;
   border-top-left-radius: 0;
@@ -81,10 +87,10 @@
   .select2-container.select2-theme-default .selection .multiple-select .rendered-selection {
     list-style: none;
     margin: 0;
-    padding: 5px;
-    padding-bottom: 0; }
+    padding: 0 5px; }
     .select2-container.select2-theme-default .selection .multiple-select .rendered-selection .placeholder {
       color: #999;
+      margin-top: 5px;
       float: left; }
     .select2-container.select2-theme-default .selection .multiple-select .rendered-selection .choice {
       background-color: #e4e4e4;
@@ -92,7 +98,7 @@
       border-radius: 4px;
       float: left;
       margin-right: 5px;
-      margin-bottom: 5px;
+      margin-top: 5px;
       padding: 0 5px; }
       .select2-container.select2-theme-default .selection .multiple-select .rendered-selection .choice .remove {
         color: #999;
@@ -104,7 +110,7 @@
 .select2-container.select2-theme-default.open .selection .single-select, .select2-container.select2-theme-default.open .selection .multiple-select {
   border-bottom-left-radius: 0;
   border-bottom-right-radius: 0; }
-.select2-container.select2-theme-default .dropdown .search input {
+.select2-container.select2-theme-default .select2-search input {
   border: 1px solid #aaa; }
 .select2-container.select2-theme-default .dropdown .results > .options {
   max-height: 200px;
@@ -154,7 +160,7 @@
 .select2-container.select2-theme-classic .dropdown {
   background-color: white;
   border-top: none; }
-  .select2-container.select2-theme-classic .dropdown .search input {
+  .select2-container.select2-theme-classic .dropdown .select2-search input {
     border: 1px solid #aaa;
     outline: 0; }
   .select2-container.select2-theme-classic .dropdown .results > .options {

文件差异内容过多而无法显示
+ 0 - 0
dist/css/select2.min.css


+ 125 - 10
dist/js/select2.amd.full.js

@@ -33,6 +33,10 @@ window.$ = window.$ || {};(function() { if ($ && $.fn && $.fn.select2 && $.fn.se
         continue;
       }
 
+      if (methodName === 'constructor') {
+        continue;
+      }
+
       methods.push(methodName);
     }
 
@@ -913,6 +917,15 @@ define('select2/selection/placeholder',[
     return placeholder;
   };
 
+  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+    var $placeholder = this.selectionContainer();
+
+    $placeholder.html(this.display(placeholder));
+    $placeholder.addClass('placeholder').removeClass('choice');
+
+    return $placeholder;
+  };
+
   Placeholder.prototype.update = function (decorated, data) {
     var singlePlaceholder = (
       data.length == 1 && data[0].id != this.placeholder.id
@@ -925,10 +938,7 @@ define('select2/selection/placeholder',[
 
     this.clear();
 
-    var $placeholder = this.selectionContainer();
-
-    $placeholder.html(this.display(this.placeholder));
-    $placeholder.addClass('placeholder').removeClass('choice');
+    var $placeholder = this.createPlaceholder(this.placeholder);
 
     this.$selection.find('.rendered-selection').append($placeholder);
   };
@@ -936,6 +946,95 @@ define('select2/selection/placeholder',[
   return Placeholder;
 });
 
+define('select2/selection/search',[
+  '../utils'
+], function (Utils) {
+  function Search (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  Search.prototype.render = function (decorated) {
+    var $search = $(
+      '<li class="select2-search-inline">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
+      '</li>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    var $rendered = decorated.call(this);
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    this.$search.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+
+    this.$search.on('keyup', function (evt) {
+      self.handleSearch(evt);
+    });
+
+    container.on('open', function () {
+      self.$search.attr('tabindex', 0);
+
+      self.$search.focus();
+    });
+
+    container.on('close', function () {
+      self.$search.attr('tabindex', -1);
+
+      self.$search.val('');
+    });
+
+    this.$search.off('keydown').on('keydown', function (evt) {
+      evt.stopPropagation();
+
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+  };
+
+  Search.prototype.createPlaceholder = function (decorated, placeholder) {
+    this.$search.attr('placeholder', placeholder.text);
+  };
+
+  Search.prototype.update = function (decorated, data) {
+    this.$search.attr('placeholder', '');
+
+    decorated.call(this, data);
+
+    this.$selection.find('.rendered-selection').append(this.$searchContainer);
+  };
+
+  Search.prototype.handleSearch = function (evt) {
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.showSearch = function (_, params) {
+    return true;
+  };
+
+  return Search;
+});
+
 define('select2/translation',[
 
 ], function () {
@@ -2404,8 +2503,8 @@ define('select2/dropdown/search',[
     var $rendered = decorated.call(this);
 
     var $search = $(
-      '<span class="search">' +
-        '<input type="search" name="search" tabindex="-1" role="textbox" />' +
+      '<span class="select2-search">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
       '</span>'
     );
 
@@ -2659,6 +2758,7 @@ define('select2/defaults',[
   './selection/single',
   './selection/multiple',
   './selection/placeholder',
+  './selection/search',
 
   './utils',
   './translation',
@@ -2677,10 +2777,10 @@ define('select2/defaults',[
 
   './i18n/en'
 ], function ($, ResultsList,
-             SingleSelection, MultipleSelection, Placeholder,
+             SingleSelection, MultipleSelection, Placeholder, SelectionSearch,
              Utils, Translation, DIACRITICS,
              SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
-             Dropdown, Search, HidePlaceholder, InfiniteScroll,
+             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              EnglishTranslation) {
   function Defaults () {
     this.reset();
@@ -2730,9 +2830,13 @@ define('select2/defaults',[
     }
 
     if (options.dropdownAdapter == null) {
-      var SearchableDropdown = Utils.Decorate(Dropdown, Search);
+      if (options.multiple) {
+        options.dropdownAdapter = Dropdown;
+      } else {
+        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
 
-      options.dropdownAdapter = SearchableDropdown;
+        options.dropdownAdapter = SearchableDropdown;
+      }
     }
 
     if (options.selectionAdapter == null) {
@@ -2749,6 +2853,13 @@ define('select2/defaults',[
           Placeholder
         );
       }
+
+      if (options.multiple) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          SelectionSearch
+        );
+      }
     }
 
     if (typeof options.language === 'string') {
@@ -3073,6 +3184,10 @@ define('select2/core',[
       self.close();
     });
 
+    this.selection.on('query', function (params) {
+      self.trigger('query', params);
+    });
+
     this.selection.on('keypress', function (e) {
       self.trigger('keypress', e);
     });

+ 125 - 10
dist/js/select2.amd.js

@@ -33,6 +33,10 @@ window.$ = window.$ || {};(function() { if ($ && $.fn && $.fn.select2 && $.fn.se
         continue;
       }
 
+      if (methodName === 'constructor') {
+        continue;
+      }
+
       methods.push(methodName);
     }
 
@@ -913,6 +917,15 @@ define('select2/selection/placeholder',[
     return placeholder;
   };
 
+  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+    var $placeholder = this.selectionContainer();
+
+    $placeholder.html(this.display(placeholder));
+    $placeholder.addClass('placeholder').removeClass('choice');
+
+    return $placeholder;
+  };
+
   Placeholder.prototype.update = function (decorated, data) {
     var singlePlaceholder = (
       data.length == 1 && data[0].id != this.placeholder.id
@@ -925,10 +938,7 @@ define('select2/selection/placeholder',[
 
     this.clear();
 
-    var $placeholder = this.selectionContainer();
-
-    $placeholder.html(this.display(this.placeholder));
-    $placeholder.addClass('placeholder').removeClass('choice');
+    var $placeholder = this.createPlaceholder(this.placeholder);
 
     this.$selection.find('.rendered-selection').append($placeholder);
   };
@@ -936,6 +946,95 @@ define('select2/selection/placeholder',[
   return Placeholder;
 });
 
+define('select2/selection/search',[
+  '../utils'
+], function (Utils) {
+  function Search (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  Search.prototype.render = function (decorated) {
+    var $search = $(
+      '<li class="select2-search-inline">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
+      '</li>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    var $rendered = decorated.call(this);
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    this.$search.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+
+    this.$search.on('keyup', function (evt) {
+      self.handleSearch(evt);
+    });
+
+    container.on('open', function () {
+      self.$search.attr('tabindex', 0);
+
+      self.$search.focus();
+    });
+
+    container.on('close', function () {
+      self.$search.attr('tabindex', -1);
+
+      self.$search.val('');
+    });
+
+    this.$search.off('keydown').on('keydown', function (evt) {
+      evt.stopPropagation();
+
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+  };
+
+  Search.prototype.createPlaceholder = function (decorated, placeholder) {
+    this.$search.attr('placeholder', placeholder.text);
+  };
+
+  Search.prototype.update = function (decorated, data) {
+    this.$search.attr('placeholder', '');
+
+    decorated.call(this, data);
+
+    this.$selection.find('.rendered-selection').append(this.$searchContainer);
+  };
+
+  Search.prototype.handleSearch = function (evt) {
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.showSearch = function (_, params) {
+    return true;
+  };
+
+  return Search;
+});
+
 define('select2/translation',[
 
 ], function () {
@@ -2404,8 +2503,8 @@ define('select2/dropdown/search',[
     var $rendered = decorated.call(this);
 
     var $search = $(
-      '<span class="search">' +
-        '<input type="search" name="search" tabindex="-1" role="textbox" />' +
+      '<span class="select2-search">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
       '</span>'
     );
 
@@ -2659,6 +2758,7 @@ define('select2/defaults',[
   './selection/single',
   './selection/multiple',
   './selection/placeholder',
+  './selection/search',
 
   './utils',
   './translation',
@@ -2677,10 +2777,10 @@ define('select2/defaults',[
 
   './i18n/en'
 ], function ($, ResultsList,
-             SingleSelection, MultipleSelection, Placeholder,
+             SingleSelection, MultipleSelection, Placeholder, SelectionSearch,
              Utils, Translation, DIACRITICS,
              SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
-             Dropdown, Search, HidePlaceholder, InfiniteScroll,
+             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              EnglishTranslation) {
   function Defaults () {
     this.reset();
@@ -2730,9 +2830,13 @@ define('select2/defaults',[
     }
 
     if (options.dropdownAdapter == null) {
-      var SearchableDropdown = Utils.Decorate(Dropdown, Search);
+      if (options.multiple) {
+        options.dropdownAdapter = Dropdown;
+      } else {
+        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
 
-      options.dropdownAdapter = SearchableDropdown;
+        options.dropdownAdapter = SearchableDropdown;
+      }
     }
 
     if (options.selectionAdapter == null) {
@@ -2749,6 +2853,13 @@ define('select2/defaults',[
           Placeholder
         );
       }
+
+      if (options.multiple) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          SelectionSearch
+        );
+      }
     }
 
     if (typeof options.language === 'string') {
@@ -3073,6 +3184,10 @@ define('select2/core',[
       self.close();
     });
 
+    this.selection.on('query', function (params) {
+      self.trigger('query', params);
+    });
+
     this.selection.on('keypress', function (e) {
       self.trigger('keypress', e);
     });

+ 125 - 10
dist/js/select2.full.js

@@ -9568,6 +9568,10 @@ define('select2/utils',[], function () {
         continue;
       }
 
+      if (methodName === 'constructor') {
+        continue;
+      }
+
       methods.push(methodName);
     }
 
@@ -10448,6 +10452,15 @@ define('select2/selection/placeholder',[
     return placeholder;
   };
 
+  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+    var $placeholder = this.selectionContainer();
+
+    $placeholder.html(this.display(placeholder));
+    $placeholder.addClass('placeholder').removeClass('choice');
+
+    return $placeholder;
+  };
+
   Placeholder.prototype.update = function (decorated, data) {
     var singlePlaceholder = (
       data.length == 1 && data[0].id != this.placeholder.id
@@ -10460,10 +10473,7 @@ define('select2/selection/placeholder',[
 
     this.clear();
 
-    var $placeholder = this.selectionContainer();
-
-    $placeholder.html(this.display(this.placeholder));
-    $placeholder.addClass('placeholder').removeClass('choice');
+    var $placeholder = this.createPlaceholder(this.placeholder);
 
     this.$selection.find('.rendered-selection').append($placeholder);
   };
@@ -10471,6 +10481,95 @@ define('select2/selection/placeholder',[
   return Placeholder;
 });
 
+define('select2/selection/search',[
+  '../utils'
+], function (Utils) {
+  function Search (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  Search.prototype.render = function (decorated) {
+    var $search = $(
+      '<li class="select2-search-inline">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
+      '</li>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    var $rendered = decorated.call(this);
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    this.$search.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+
+    this.$search.on('keyup', function (evt) {
+      self.handleSearch(evt);
+    });
+
+    container.on('open', function () {
+      self.$search.attr('tabindex', 0);
+
+      self.$search.focus();
+    });
+
+    container.on('close', function () {
+      self.$search.attr('tabindex', -1);
+
+      self.$search.val('');
+    });
+
+    this.$search.off('keydown').on('keydown', function (evt) {
+      evt.stopPropagation();
+
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+  };
+
+  Search.prototype.createPlaceholder = function (decorated, placeholder) {
+    this.$search.attr('placeholder', placeholder.text);
+  };
+
+  Search.prototype.update = function (decorated, data) {
+    this.$search.attr('placeholder', '');
+
+    decorated.call(this, data);
+
+    this.$selection.find('.rendered-selection').append(this.$searchContainer);
+  };
+
+  Search.prototype.handleSearch = function (evt) {
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.showSearch = function (_, params) {
+    return true;
+  };
+
+  return Search;
+});
+
 define('select2/translation',[
 
 ], function () {
@@ -11939,8 +12038,8 @@ define('select2/dropdown/search',[
     var $rendered = decorated.call(this);
 
     var $search = $(
-      '<span class="search">' +
-        '<input type="search" name="search" tabindex="-1" role="textbox" />' +
+      '<span class="select2-search">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
       '</span>'
     );
 
@@ -12194,6 +12293,7 @@ define('select2/defaults',[
   './selection/single',
   './selection/multiple',
   './selection/placeholder',
+  './selection/search',
 
   './utils',
   './translation',
@@ -12212,10 +12312,10 @@ define('select2/defaults',[
 
   './i18n/en'
 ], function ($, ResultsList,
-             SingleSelection, MultipleSelection, Placeholder,
+             SingleSelection, MultipleSelection, Placeholder, SelectionSearch,
              Utils, Translation, DIACRITICS,
              SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
-             Dropdown, Search, HidePlaceholder, InfiniteScroll,
+             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              EnglishTranslation) {
   function Defaults () {
     this.reset();
@@ -12265,9 +12365,13 @@ define('select2/defaults',[
     }
 
     if (options.dropdownAdapter == null) {
-      var SearchableDropdown = Utils.Decorate(Dropdown, Search);
+      if (options.multiple) {
+        options.dropdownAdapter = Dropdown;
+      } else {
+        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
 
-      options.dropdownAdapter = SearchableDropdown;
+        options.dropdownAdapter = SearchableDropdown;
+      }
     }
 
     if (options.selectionAdapter == null) {
@@ -12284,6 +12388,13 @@ define('select2/defaults',[
           Placeholder
         );
       }
+
+      if (options.multiple) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          SelectionSearch
+        );
+      }
     }
 
     if (typeof options.language === 'string') {
@@ -12608,6 +12719,10 @@ define('select2/core',[
       self.close();
     });
 
+    this.selection.on('query', function (params) {
+      self.trigger('query', params);
+    });
+
     this.selection.on('keypress', function (e) {
       self.trigger('keypress', e);
     });

文件差异内容过多而无法显示
+ 0 - 0
dist/js/select2.full.min.js


+ 125 - 10
dist/js/select2.js

@@ -461,6 +461,10 @@ define('select2/utils',[], function () {
         continue;
       }
 
+      if (methodName === 'constructor') {
+        continue;
+      }
+
       methods.push(methodName);
     }
 
@@ -1341,6 +1345,15 @@ define('select2/selection/placeholder',[
     return placeholder;
   };
 
+  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+    var $placeholder = this.selectionContainer();
+
+    $placeholder.html(this.display(placeholder));
+    $placeholder.addClass('placeholder').removeClass('choice');
+
+    return $placeholder;
+  };
+
   Placeholder.prototype.update = function (decorated, data) {
     var singlePlaceholder = (
       data.length == 1 && data[0].id != this.placeholder.id
@@ -1353,10 +1366,7 @@ define('select2/selection/placeholder',[
 
     this.clear();
 
-    var $placeholder = this.selectionContainer();
-
-    $placeholder.html(this.display(this.placeholder));
-    $placeholder.addClass('placeholder').removeClass('choice');
+    var $placeholder = this.createPlaceholder(this.placeholder);
 
     this.$selection.find('.rendered-selection').append($placeholder);
   };
@@ -1364,6 +1374,95 @@ define('select2/selection/placeholder',[
   return Placeholder;
 });
 
+define('select2/selection/search',[
+  '../utils'
+], function (Utils) {
+  function Search (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  Search.prototype.render = function (decorated) {
+    var $search = $(
+      '<li class="select2-search-inline">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
+      '</li>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    var $rendered = decorated.call(this);
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    this.$search.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+
+    this.$search.on('keyup', function (evt) {
+      self.handleSearch(evt);
+    });
+
+    container.on('open', function () {
+      self.$search.attr('tabindex', 0);
+
+      self.$search.focus();
+    });
+
+    container.on('close', function () {
+      self.$search.attr('tabindex', -1);
+
+      self.$search.val('');
+    });
+
+    this.$search.off('keydown').on('keydown', function (evt) {
+      evt.stopPropagation();
+
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+  };
+
+  Search.prototype.createPlaceholder = function (decorated, placeholder) {
+    this.$search.attr('placeholder', placeholder.text);
+  };
+
+  Search.prototype.update = function (decorated, data) {
+    this.$search.attr('placeholder', '');
+
+    decorated.call(this, data);
+
+    this.$selection.find('.rendered-selection').append(this.$searchContainer);
+  };
+
+  Search.prototype.handleSearch = function (evt) {
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.showSearch = function (_, params) {
+    return true;
+  };
+
+  return Search;
+});
+
 define('select2/translation',[
 
 ], function () {
@@ -2832,8 +2931,8 @@ define('select2/dropdown/search',[
     var $rendered = decorated.call(this);
 
     var $search = $(
-      '<span class="search">' +
-        '<input type="search" name="search" tabindex="-1" role="textbox" />' +
+      '<span class="select2-search">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
       '</span>'
     );
 
@@ -3087,6 +3186,7 @@ define('select2/defaults',[
   './selection/single',
   './selection/multiple',
   './selection/placeholder',
+  './selection/search',
 
   './utils',
   './translation',
@@ -3105,10 +3205,10 @@ define('select2/defaults',[
 
   './i18n/en'
 ], function ($, ResultsList,
-             SingleSelection, MultipleSelection, Placeholder,
+             SingleSelection, MultipleSelection, Placeholder, SelectionSearch,
              Utils, Translation, DIACRITICS,
              SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
-             Dropdown, Search, HidePlaceholder, InfiniteScroll,
+             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              EnglishTranslation) {
   function Defaults () {
     this.reset();
@@ -3158,9 +3258,13 @@ define('select2/defaults',[
     }
 
     if (options.dropdownAdapter == null) {
-      var SearchableDropdown = Utils.Decorate(Dropdown, Search);
+      if (options.multiple) {
+        options.dropdownAdapter = Dropdown;
+      } else {
+        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
 
-      options.dropdownAdapter = SearchableDropdown;
+        options.dropdownAdapter = SearchableDropdown;
+      }
     }
 
     if (options.selectionAdapter == null) {
@@ -3177,6 +3281,13 @@ define('select2/defaults',[
           Placeholder
         );
       }
+
+      if (options.multiple) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          SelectionSearch
+        );
+      }
     }
 
     if (typeof options.language === 'string') {
@@ -3501,6 +3612,10 @@ define('select2/core',[
       self.close();
     });
 
+    this.selection.on('query', function (params) {
+      self.trigger('query', params);
+    });
+
     this.selection.on('keypress', function (e) {
       self.trigger('keypress', e);
     });

文件差异内容过多而无法显示
+ 0 - 0
dist/js/select2.min.js


+ 4 - 0
src/js/select2/core.js

@@ -182,6 +182,10 @@ define([
       self.close();
     });
 
+    this.selection.on('query', function (params) {
+      self.trigger('query', params);
+    });
+
     this.selection.on('keypress', function (e) {
       self.trigger('keypress', e);
     });

+ 16 - 4
src/js/select2/defaults.js

@@ -5,6 +5,7 @@ define([
   './selection/single',
   './selection/multiple',
   './selection/placeholder',
+  './selection/search',
 
   './utils',
   './translation',
@@ -23,10 +24,10 @@ define([
 
   './i18n/en'
 ], function ($, ResultsList,
-             SingleSelection, MultipleSelection, Placeholder,
+             SingleSelection, MultipleSelection, Placeholder, SelectionSearch,
              Utils, Translation, DIACRITICS,
              SelectData, ArrayData, AjaxData, Tags, MinimumInputLength,
-             Dropdown, Search, HidePlaceholder, InfiniteScroll,
+             Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
              EnglishTranslation) {
   function Defaults () {
     this.reset();
@@ -76,9 +77,13 @@ define([
     }
 
     if (options.dropdownAdapter == null) {
-      var SearchableDropdown = Utils.Decorate(Dropdown, Search);
+      if (options.multiple) {
+        options.dropdownAdapter = Dropdown;
+      } else {
+        var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
 
-      options.dropdownAdapter = SearchableDropdown;
+        options.dropdownAdapter = SearchableDropdown;
+      }
     }
 
     if (options.selectionAdapter == null) {
@@ -95,6 +100,13 @@ define([
           Placeholder
         );
       }
+
+      if (options.multiple) {
+        options.selectionAdapter = Utils.Decorate(
+          options.selectionAdapter,
+          SelectionSearch
+        );
+      }
     }
 
     if (typeof options.language === 'string') {

+ 2 - 2
src/js/select2/dropdown/search.js

@@ -7,8 +7,8 @@ define([
     var $rendered = decorated.call(this);
 
     var $search = $(
-      '<span class="search">' +
-        '<input type="search" name="search" tabindex="-1" role="textbox" />' +
+      '<span class="select2-search">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
       '</span>'
     );
 

+ 10 - 4
src/js/select2/selection/placeholder.js

@@ -18,6 +18,15 @@ define([
     return placeholder;
   };
 
+  Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
+    var $placeholder = this.selectionContainer();
+
+    $placeholder.html(this.display(placeholder));
+    $placeholder.addClass('placeholder').removeClass('choice');
+
+    return $placeholder;
+  };
+
   Placeholder.prototype.update = function (decorated, data) {
     var singlePlaceholder = (
       data.length == 1 && data[0].id != this.placeholder.id
@@ -30,10 +39,7 @@ define([
 
     this.clear();
 
-    var $placeholder = this.selectionContainer();
-
-    $placeholder.html(this.display(this.placeholder));
-    $placeholder.addClass('placeholder').removeClass('choice');
+    var $placeholder = this.createPlaceholder(this.placeholder);
 
     this.$selection.find('.rendered-selection').append($placeholder);
   };

+ 88 - 0
src/js/select2/selection/search.js

@@ -0,0 +1,88 @@
+define([
+  '../utils'
+], function (Utils) {
+  function Search (decorated, $element, options) {
+    decorated.call(this, $element, options);
+  }
+
+  Search.prototype.render = function (decorated) {
+    var $search = $(
+      '<li class="select2-search-inline">' +
+        '<input type="search" tabindex="-1" role="textbox" />' +
+      '</li>'
+    );
+
+    this.$searchContainer = $search;
+    this.$search = $search.find('input');
+
+    var $rendered = decorated.call(this);
+
+    return $rendered;
+  };
+
+  Search.prototype.bind = function (decorated, container, $container) {
+    var self = this;
+
+    decorated.call(this, container, $container);
+
+    this.$search.on('keydown', function (evt) {
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+
+    this.$search.on('keyup', function (evt) {
+      self.handleSearch(evt);
+    });
+
+    container.on('open', function () {
+      self.$search.attr('tabindex', 0);
+
+      self.$search.focus();
+    });
+
+    container.on('close', function () {
+      self.$search.attr('tabindex', -1);
+
+      self.$search.val('');
+    });
+
+    this.$search.off('keydown').on('keydown', function (evt) {
+      evt.stopPropagation();
+
+      self.trigger('keypress', evt);
+
+      self._keyUpPrevented = evt.isDefaultPrevented();
+    });
+  };
+
+  Search.prototype.createPlaceholder = function (decorated, placeholder) {
+    this.$search.attr('placeholder', placeholder.text);
+  };
+
+  Search.prototype.update = function (decorated, data) {
+    this.$search.attr('placeholder', '');
+
+    decorated.call(this, data);
+
+    this.$selection.find('.rendered-selection').append(this.$searchContainer);
+  };
+
+  Search.prototype.handleSearch = function (evt) {
+    if (!this._keyUpPrevented) {
+      var input = this.$search.val();
+
+      this.trigger('query', {
+        term: input
+      });
+    }
+
+    this._keyUpPrevented = false;
+  };
+
+  Search.prototype.showSearch = function (_, params) {
+    return true;
+  };
+
+  return Search;
+});

+ 4 - 0
src/js/select2/utils.js

@@ -33,6 +33,10 @@ define([], function () {
         continue;
       }
 
+      if (methodName === 'constructor') {
+        continue;
+      }
+
       methods.push(methodName);
     }
 

+ 10 - 10
src/scss/_dropdown.scss

@@ -16,16 +16,6 @@
 
     z-index: 100;
 
-    .search {
-      display: block;
-      padding: 4px;
-
-      input {
-        padding: 4px;
-        width: 100%;
-      }
-    }
-
     .results {
       display: block;
 
@@ -48,6 +38,16 @@
     }
   }
 
+  .select2-search {
+    display: block;
+    padding: 4px;
+
+    input {
+      padding: 4px;
+      width: 100%;
+    }
+  }
+
   &.open .dropdown {
     border-top: none;
     border-top-left-radius: 0;

+ 11 - 1
src/scss/_multiple.scss

@@ -11,10 +11,20 @@
     -webkit-user-select: none;
 
     .rendered-selection {
-      display: block;
+      display: inline-block;
       overflow: hidden;
       padding-left: 8px;
       text-overflow: ellipsis;
     }
   }
+
+  .select2-search-inline {
+    float: left;
+
+    input {
+      border: none;
+      font-size: 100%;
+      margin-top: 5px;
+    }
+  }
 }

+ 1 - 1
src/scss/theme/classic/layout.scss

@@ -49,7 +49,7 @@
     background-color: $dropdown-bg-color;
     border-top: none;
 
-    .search {
+    .select2-search {
       input {
         border: 1px solid $border-color;
         outline: 0;

+ 9 - 8
src/scss/theme/default/layout.scss

@@ -23,12 +23,13 @@
       .rendered-selection {
         list-style: none;
         margin: 0;
-        padding: 5px;
-        padding-bottom: 0;
+        padding: 0 5px;
 
         .placeholder {
           color: #999;
 
+          margin-top: 5px;
+
           float: left;
         }
 
@@ -41,7 +42,7 @@
           float: left;
 
           margin-right: 5px;
-          margin-bottom: 5px;
+          margin-top: 5px;
           padding: 0 5px;
 
           .remove {
@@ -70,13 +71,13 @@
     }
   }
 
-  .dropdown {
-    .search {
-      input {
-        border: 1px solid #aaa;
-      }
+  .select2-search {
+    input {
+      border: 1px solid #aaa;
     }
+  }
 
+  .dropdown {
     .results {
       &> .options {
         max-height: 200px;

+ 52 - 0
tests/utils/decorator-tests.js

@@ -135,3 +135,55 @@ test('inherited - constructor', function (assert) {
   assert.ok(inst.called);
   assert.ok(inst.inherited);
 });
+
+test('inherited - three levels', function (assert) {
+  function BaseClass (testArgument) {
+    this.baseCalled = true;
+    this.baseTestArgument = testArgument;
+  }
+
+  BaseClass.prototype.test = function (a) {
+    return a + 'c';
+  };
+
+  function MiddleClass (decorated, testArgument) {
+    this.middleCalled = true;
+    this.middleTestArgument = testArgument;
+
+    decorated.call(this, testArgument);
+  }
+
+  MiddleClass.prototype.test = function (decorated, a) {
+    return decorated.call(this, a + 'b');
+  };
+
+  function DecoratorClass (decorated, testArgument) {
+    this.decoratorCalled = true;
+    this.decoratorTestArgument = testArgument;
+
+    decorated.call(this, testArgument);
+  }
+
+  DecoratorClass.prototype.test = function (decorated, a) {
+    return decorated.call(this, a + 'a');
+  };
+
+  var DecoratedClass = Utils.Decorate(
+    Utils.Decorate(BaseClass, MiddleClass),
+    DecoratorClass
+  );
+
+  var inst = new DecoratedClass('test');
+
+  assert.ok(inst.baseCalled, 'The base class contructor was called');
+  assert.ok(inst.middleCalled, 'The middle class constructor was called');
+  assert.ok(inst.decoratorCalled, 'The decorator constructor was called');
+
+  assert.strictEqual(inst.baseTestArgument, 'test');
+  assert.strictEqual(inst.middleTestArgument, 'test');
+  assert.strictEqual(inst.decoratorTestArgument, 'test');
+
+  var out = inst.test('test');
+
+  assert.strictEqual(out, 'testabc');
+});

部分文件因为文件数量过多而无法显示