浏览代码

Fix support for deep nesting

While deep nesting will not work on a standard `<select>`, we can
emulate it through data objects still, and just handle the display
of the data in the results.

This also means that the horrific deep-nested CSS is back to handle
the padding. I hope that will get fixed over time.

This also fixes one of the performance issues with adding array
data, as options are added at the very end instead of one by one.
Kevin Brown 10 年之前
父节点
当前提交
7f17291932

+ 19 - 0
dist/css/select2.css

@@ -218,6 +218,25 @@
   color: #999; }
 .select2-container--default .select2-results__option[aria-selected=true] {
   background-color: #ddd; }
+.select2-container--default .select2-results__option .select2-results__option {
+  padding-left: 1em; }
+  .select2-container--default .select2-results__option .select2-results__option .select2-results__group {
+    padding-left: 0; }
+  .select2-container--default .select2-results__option .select2-results__option .select2-results__option {
+    margin-left: -1em;
+    padding-left: 2em; }
+    .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+      margin-left: -2em;
+      padding-left: 3em; }
+      .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+        margin-left: -3em;
+        padding-left: 4em; }
+        .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+          margin-left: -4em;
+          padding-left: 5em; }
+          .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
+            margin-left: -5em;
+            padding-left: 6em; }
 .select2-container--default .select2-results__option--highlighted[aria-selected] {
   background-color: #5897fb;
   color: white; }

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


+ 26 - 9
dist/js/select2.amd.full.js

@@ -2324,9 +2324,19 @@ define('select2/data/select',[
   };
 
   SelectAdapter.prototype.option = function (data) {
-    var option = document.createElement('option');
+    var option;
 
-    option.value = data.id;
+    if (data.children) {
+      option = document.createElement('optgroup');
+      option.label = data.text;
+    } else {
+      option = document.createElement('option');
+      option.innerText = data.text;
+    }
+
+    if (data.id) {
+      option.value = data.id;
+    }
 
     if (data.disabled) {
       option.disabled = true;
@@ -2336,8 +2346,6 @@ define('select2/data/select',[
       option.selected = true;
     }
 
-    option.innerText = data.text;
-
     var $option = $(option);
 
     var normalizedData = this._normalizeItem(data);
@@ -2367,7 +2375,7 @@ define('select2/data/select',[
       };
     } else if ($option.is('optgroup')) {
       data = {
-        text: $option.attr('label'),
+        text: $option.prop('label'),
         children: []
       };
 
@@ -2444,7 +2452,7 @@ define('select2/data/array',[
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
-    this.convertToOptions(data);
+    $element.append(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2469,6 +2477,8 @@ define('select2/data/array',[
       return self.item($(this)).id;
     }).get();
 
+    var $options = [];
+
     // Filter out all items except for the one passed in the argument
     function onlyItem (item) {
       return function () {
@@ -2477,8 +2487,7 @@ define('select2/data/array',[
     }
 
     for (var d = 0; d < data.length; d++) {
-      var item = data[d];
-      item.id = item.id.toString();
+      var item = this._normalizeItem(data[d]);
 
       // Skip items which were pre-loaded, only merge the data
       if (existingIds.indexOf(item.id) >= 0) {
@@ -2496,8 +2505,16 @@ define('select2/data/array',[
 
       var $option = this.option(item);
 
-      this.$element.append($option);
+      if (item.children) {
+        var $children = this.convertToOptions(item.children);
+
+        $option.append($children);
+      }
+
+      $options.push($option);
     }
+
+    return $options;
   };
 
   return ArrayAdapter;

+ 26 - 9
dist/js/select2.amd.js

@@ -2324,9 +2324,19 @@ define('select2/data/select',[
   };
 
   SelectAdapter.prototype.option = function (data) {
-    var option = document.createElement('option');
+    var option;
 
-    option.value = data.id;
+    if (data.children) {
+      option = document.createElement('optgroup');
+      option.label = data.text;
+    } else {
+      option = document.createElement('option');
+      option.innerText = data.text;
+    }
+
+    if (data.id) {
+      option.value = data.id;
+    }
 
     if (data.disabled) {
       option.disabled = true;
@@ -2336,8 +2346,6 @@ define('select2/data/select',[
       option.selected = true;
     }
 
-    option.innerText = data.text;
-
     var $option = $(option);
 
     var normalizedData = this._normalizeItem(data);
@@ -2367,7 +2375,7 @@ define('select2/data/select',[
       };
     } else if ($option.is('optgroup')) {
       data = {
-        text: $option.attr('label'),
+        text: $option.prop('label'),
         children: []
       };
 
@@ -2444,7 +2452,7 @@ define('select2/data/array',[
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
-    this.convertToOptions(data);
+    $element.append(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2469,6 +2477,8 @@ define('select2/data/array',[
       return self.item($(this)).id;
     }).get();
 
+    var $options = [];
+
     // Filter out all items except for the one passed in the argument
     function onlyItem (item) {
       return function () {
@@ -2477,8 +2487,7 @@ define('select2/data/array',[
     }
 
     for (var d = 0; d < data.length; d++) {
-      var item = data[d];
-      item.id = item.id.toString();
+      var item = this._normalizeItem(data[d]);
 
       // Skip items which were pre-loaded, only merge the data
       if (existingIds.indexOf(item.id) >= 0) {
@@ -2496,8 +2505,16 @@ define('select2/data/array',[
 
       var $option = this.option(item);
 
-      this.$element.append($option);
+      if (item.children) {
+        var $children = this.convertToOptions(item.children);
+
+        $option.append($children);
+      }
+
+      $options.push($option);
     }
+
+    return $options;
   };
 
   return ArrayAdapter;

+ 26 - 9
dist/js/select2.full.js

@@ -11859,9 +11859,19 @@ define('select2/data/select',[
   };
 
   SelectAdapter.prototype.option = function (data) {
-    var option = document.createElement('option');
+    var option;
 
-    option.value = data.id;
+    if (data.children) {
+      option = document.createElement('optgroup');
+      option.label = data.text;
+    } else {
+      option = document.createElement('option');
+      option.innerText = data.text;
+    }
+
+    if (data.id) {
+      option.value = data.id;
+    }
 
     if (data.disabled) {
       option.disabled = true;
@@ -11871,8 +11881,6 @@ define('select2/data/select',[
       option.selected = true;
     }
 
-    option.innerText = data.text;
-
     var $option = $(option);
 
     var normalizedData = this._normalizeItem(data);
@@ -11902,7 +11910,7 @@ define('select2/data/select',[
       };
     } else if ($option.is('optgroup')) {
       data = {
-        text: $option.attr('label'),
+        text: $option.prop('label'),
         children: []
       };
 
@@ -11979,7 +11987,7 @@ define('select2/data/array',[
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
-    this.convertToOptions(data);
+    $element.append(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -12004,6 +12012,8 @@ define('select2/data/array',[
       return self.item($(this)).id;
     }).get();
 
+    var $options = [];
+
     // Filter out all items except for the one passed in the argument
     function onlyItem (item) {
       return function () {
@@ -12012,8 +12022,7 @@ define('select2/data/array',[
     }
 
     for (var d = 0; d < data.length; d++) {
-      var item = data[d];
-      item.id = item.id.toString();
+      var item = this._normalizeItem(data[d]);
 
       // Skip items which were pre-loaded, only merge the data
       if (existingIds.indexOf(item.id) >= 0) {
@@ -12031,8 +12040,16 @@ define('select2/data/array',[
 
       var $option = this.option(item);
 
-      this.$element.append($option);
+      if (item.children) {
+        var $children = this.convertToOptions(item.children);
+
+        $option.append($children);
+      }
+
+      $options.push($option);
     }
+
+    return $options;
   };
 
   return ArrayAdapter;

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


+ 26 - 9
dist/js/select2.js

@@ -2752,9 +2752,19 @@ define('select2/data/select',[
   };
 
   SelectAdapter.prototype.option = function (data) {
-    var option = document.createElement('option');
+    var option;
 
-    option.value = data.id;
+    if (data.children) {
+      option = document.createElement('optgroup');
+      option.label = data.text;
+    } else {
+      option = document.createElement('option');
+      option.innerText = data.text;
+    }
+
+    if (data.id) {
+      option.value = data.id;
+    }
 
     if (data.disabled) {
       option.disabled = true;
@@ -2764,8 +2774,6 @@ define('select2/data/select',[
       option.selected = true;
     }
 
-    option.innerText = data.text;
-
     var $option = $(option);
 
     var normalizedData = this._normalizeItem(data);
@@ -2795,7 +2803,7 @@ define('select2/data/select',[
       };
     } else if ($option.is('optgroup')) {
       data = {
-        text: $option.attr('label'),
+        text: $option.prop('label'),
         children: []
       };
 
@@ -2872,7 +2880,7 @@ define('select2/data/array',[
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
-    this.convertToOptions(data);
+    $element.append(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -2897,6 +2905,8 @@ define('select2/data/array',[
       return self.item($(this)).id;
     }).get();
 
+    var $options = [];
+
     // Filter out all items except for the one passed in the argument
     function onlyItem (item) {
       return function () {
@@ -2905,8 +2915,7 @@ define('select2/data/array',[
     }
 
     for (var d = 0; d < data.length; d++) {
-      var item = data[d];
-      item.id = item.id.toString();
+      var item = this._normalizeItem(data[d]);
 
       // Skip items which were pre-loaded, only merge the data
       if (existingIds.indexOf(item.id) >= 0) {
@@ -2924,8 +2933,16 @@ define('select2/data/array',[
 
       var $option = this.option(item);
 
-      this.$element.append($option);
+      if (item.children) {
+        var $children = this.convertToOptions(item.children);
+
+        $option.append($children);
+      }
+
+      $options.push($option);
     }
+
+    return $options;
   };
 
   return ArrayAdapter;

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


+ 13 - 4
src/js/select2/data/array.js

@@ -8,7 +8,7 @@ define([
 
     ArrayAdapter.__super__.constructor.call(this, $element, options);
 
-    this.convertToOptions(data);
+    $element.append(this.convertToOptions(data));
   }
 
   Utils.Extend(ArrayAdapter, SelectAdapter);
@@ -33,6 +33,8 @@ define([
       return self.item($(this)).id;
     }).get();
 
+    var $options = [];
+
     // Filter out all items except for the one passed in the argument
     function onlyItem (item) {
       return function () {
@@ -41,8 +43,7 @@ define([
     }
 
     for (var d = 0; d < data.length; d++) {
-      var item = data[d];
-      item.id = item.id.toString();
+      var item = this._normalizeItem(data[d]);
 
       // Skip items which were pre-loaded, only merge the data
       if (existingIds.indexOf(item.id) >= 0) {
@@ -60,8 +61,16 @@ define([
 
       var $option = this.option(item);
 
-      this.$element.append($option);
+      if (item.children) {
+        var $children = this.convertToOptions(item.children);
+
+        $option.append($children);
+      }
+
+      $options.push($option);
     }
+
+    return $options;
   };
 
   return ArrayAdapter;

+ 13 - 5
src/js/select2/data/select.js

@@ -148,9 +148,19 @@ define([
   };
 
   SelectAdapter.prototype.option = function (data) {
-    var option = document.createElement('option');
+    var option;
 
-    option.value = data.id;
+    if (data.children) {
+      option = document.createElement('optgroup');
+      option.label = data.text;
+    } else {
+      option = document.createElement('option');
+      option.innerText = data.text;
+    }
+
+    if (data.id) {
+      option.value = data.id;
+    }
 
     if (data.disabled) {
       option.disabled = true;
@@ -160,8 +170,6 @@ define([
       option.selected = true;
     }
 
-    option.innerText = data.text;
-
     var $option = $(option);
 
     var normalizedData = this._normalizeItem(data);
@@ -191,7 +199,7 @@ define([
       };
     } else if ($option.is('optgroup')) {
       data = {
-        text: $option.attr('label'),
+        text: $option.prop('label'),
         children: []
       };
 

+ 33 - 0
src/scss/theme/default/layout.scss

@@ -47,6 +47,39 @@
     &[aria-selected=true] {
       background-color: #ddd;
     }
+
+    .select2-results__option {
+      padding-left: 1em;
+
+      .select2-results__group {
+        padding-left: 0;
+      }
+
+      .select2-results__option {
+        margin-left: -1em;
+        padding-left: 2em;
+
+        .select2-results__option {
+          margin-left: -2em;
+          padding-left: 3em;
+
+          .select2-results__option {
+            margin-left: -3em;
+            padding-left: 4em;
+
+            .select2-results__option {
+              margin-left: -4em;
+              padding-left: 5em;
+
+              .select2-results__option {
+                margin-left: -5em;
+                padding-left: 6em;
+              }
+            }
+          }
+        }
+      }
+    }
   }
 
   .select2-results__option--highlighted[aria-selected] {

+ 57 - 0
tests/data/array-tests.js

@@ -21,6 +21,25 @@ var options = new Options({
   ]
 });
 
+var nestedOptions = new Options({
+  data: [
+    {
+      text: 'Default',
+      children: [
+        {
+          text: 'Next',
+          children: [
+            {
+              id: 'a',
+              text: 'Option'
+            }
+          ]
+        }
+      ]
+    }
+  ]
+});
+
 test('current gets default for single', function (assert) {
   var $select = $('#qunit-fixture .single');
 
@@ -186,3 +205,41 @@ test('option tags are automatically generated', function (assert) {
     'An <option> element should be created for each object'
   );
 });
+
+test('optgroup tags can also be generated', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var data = new ArrayData($select, nestedOptions);
+
+  assert.equal(
+    $select.find('option').length,
+    1,
+    'An <option> element should be created for the one selectable object'
+  );
+
+  assert.equal(
+    $select.find('optgroup').length,
+    2,
+    'An <optgroup> element should be created for the two with children'
+  );
+});
+
+test('optgroup tags have the right properties', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var data = new ArrayData($select, nestedOptions);
+
+  var $group = $select.children('optgroup');
+
+  assert.equal(
+    $group.prop('label'),
+    'Default',
+    'An `<optgroup>` label should match the text property'
+  );
+
+  assert.equal(
+    $group.children().length,
+    1,
+    'The <optgroup> should have one child under it'
+  );
+});

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