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.
This commit is contained in:
parent
5c71e2da50
commit
7f17291932
19
dist/css/select2.css
vendored
19
dist/css/select2.css
vendored
@ -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; }
|
||||
|
2
dist/css/select2.min.css
vendored
2
dist/css/select2.min.css
vendored
File diff suppressed because one or more lines are too long
33
dist/js/select2.amd.full.js
vendored
33
dist/js/select2.amd.full.js
vendored
@ -2324,9 +2324,19 @@ define('select2/data/select',[
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option = document.createElement('option');
|
||||
var option;
|
||||
|
||||
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;
|
||||
|
33
dist/js/select2.amd.js
vendored
33
dist/js/select2.amd.js
vendored
@ -2324,9 +2324,19 @@ define('select2/data/select',[
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option = document.createElement('option');
|
||||
var option;
|
||||
|
||||
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;
|
||||
|
33
dist/js/select2.full.js
vendored
33
dist/js/select2.full.js
vendored
@ -11859,9 +11859,19 @@ define('select2/data/select',[
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option = document.createElement('option');
|
||||
var option;
|
||||
|
||||
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;
|
||||
|
2
dist/js/select2.full.min.js
vendored
2
dist/js/select2.full.min.js
vendored
File diff suppressed because one or more lines are too long
33
dist/js/select2.js
vendored
33
dist/js/select2.js
vendored
@ -2752,9 +2752,19 @@ define('select2/data/select',[
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option = document.createElement('option');
|
||||
var option;
|
||||
|
||||
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;
|
||||
|
4
dist/js/select2.min.js
vendored
4
dist/js/select2.min.js
vendored
File diff suppressed because one or more lines are too long
17
src/js/select2/data/array.js
vendored
17
src/js/select2/data/array.js
vendored
@ -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;
|
||||
|
16
src/js/select2/data/select.js
vendored
16
src/js/select2/data/select.js
vendored
@ -148,9 +148,19 @@ define([
|
||||
};
|
||||
|
||||
SelectAdapter.prototype.option = function (data) {
|
||||
var option = document.createElement('option');
|
||||
var option;
|
||||
|
||||
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: []
|
||||
};
|
||||
|
||||
|
@ -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] {
|
||||
|
@ -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'
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user