1
0
mirror of synced 2024-11-29 16:16:02 +03:00

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:
Kevin Brown 2015-01-09 20:34:49 -05:00
parent 5c71e2da50
commit 7f17291932
12 changed files with 243 additions and 49 deletions

19
dist/css/select2.css vendored
View File

@ -218,6 +218,25 @@
color: #999; } color: #999; }
.select2-container--default .select2-results__option[aria-selected=true] { .select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd; } 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] { .select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb; background-color: #5897fb;
color: white; } color: white; }

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

35
dist/js/select2.js vendored
View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@ -47,6 +47,39 @@
&[aria-selected=true] { &[aria-selected=true] {
background-color: #ddd; 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] { .select2-results__option--highlighted[aria-selected] {

View File

@ -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) { test('current gets default for single', function (assert) {
var $select = $('#qunit-fixture .single'); 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' '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'
);
});