소스 검색

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'
+  );
+});

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.