Skip to content

Commit 7f1628f

Browse files
committed
Add an append-to-body attribute to the <ui-select-choices> directive that moves the dropdown element to the end of the document body before opening it, thereby solving problems with the dropdown being displayed below elements that follow the <ui-select> element in the document. This implementation is modeled after the typeahead-append-to-body support from UI Bootstrap. See angular-ui#41 (and quite a few dupes).
1 parent da159d4 commit 7f1628f

File tree

6 files changed

+74
-12
lines changed

6 files changed

+74
-12
lines changed

src/bootstrap/choices.tpl.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<ul class="ui-select-choices ui-select-choices-content dropdown-menu"
2+
style="display: block"
23
role="listbox"
3-
ng-show="$select.items.length > 0">
4+
ng-show="$select.open">
45
<li class="ui-select-choices-group" id="ui-select-choices-{{ $select.generatedId }}" >
56
<div class="divider" ng-show="$select.isGrouped && $index > 0"></div>
67
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label dropdown-header" ng-bind="$group.name"></div>

src/common.css

+6-6
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
}
109109

110110
/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */
111-
.ui-select-bootstrap > .ui-select-choices {
111+
.ui-select-choices {
112112
width: 100%;
113113
height: auto;
114114
max-height: 200px;
@@ -163,7 +163,7 @@
163163
border-right: 1px solid #428bca;
164164
}
165165

166-
.ui-select-bootstrap .ui-select-choices-row>a {
166+
.ui-select-choices-row>a {
167167
display: block;
168168
padding: 3px 20px;
169169
clear: both;
@@ -173,21 +173,21 @@
173173
white-space: nowrap;
174174
}
175175

176-
.ui-select-bootstrap .ui-select-choices-row>a:hover, .ui-select-bootstrap .ui-select-choices-row>a:focus {
176+
.ui-select-choices-row>a:hover, .ui-select-choices-row>a:focus {
177177
text-decoration: none;
178178
color: #262626;
179179
background-color: #f5f5f5;
180180
}
181181

182-
.ui-select-bootstrap .ui-select-choices-row.active>a {
182+
.ui-select-choices-row.active>a {
183183
color: #fff;
184184
text-decoration: none;
185185
outline: 0;
186186
background-color: #428bca;
187187
}
188188

189-
.ui-select-bootstrap .ui-select-choices-row.disabled>a,
190-
.ui-select-bootstrap .ui-select-choices-row.active.disabled>a {
189+
.ui-select-choices-row.disabled>a,
190+
.ui-select-choices-row.active.disabled>a {
191191
color: #777;
192192
cursor: not-allowed;
193193
background-color: #fff;

src/common.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,25 @@ var uis = angular.module('ui.select', [])
133133
return function(matchItem, query) {
134134
return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class="ui-select-highlight">$&</span>') : matchItem;
135135
};
136-
});
136+
})
137137

138+
/**
139+
* A read-only equivalent of jQuery's offset function: http://api.jquery.com/offset/
140+
*
141+
* Taken from AngularUI Bootstrap Position:
142+
* See https://github.com/angular-ui/bootstrap/blob/master/src/position/position.js#L70
143+
*/
144+
.factory('uisOffset',
145+
['$document', '$window',
146+
function ($document, $window) {
147+
148+
return function(element) {
149+
var boundingClientRect = element[0].getBoundingClientRect();
150+
return {
151+
width: boundingClientRect.width || element.prop('offsetWidth'),
152+
height: boundingClientRect.height || element.prop('offsetHeight'),
153+
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
154+
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
155+
};
156+
};
157+
}]);

src/select2/choices.tpl.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<ul class="ui-select-choices ui-select-choices-content select2-results">
1+
<ul class="ui-select-choices ui-select-choices-content select2-results"
2+
ng-style="{top: position.top+'px', left: position.left+'px'}">
23
<li class="ui-select-choices-group" ng-class="{'select2-result-with-children': $select.choiceGrouped($group) }">
34
<div ng-show="$select.choiceGrouped($group)" class="ui-select-choices-group-label select2-result-label" ng-bind="$group.name"></div>
45
<ul role="listbox"

src/selectize/choices.tpl.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<div ng-show="$select.open" class="ui-select-choices selectize-dropdown single">
1+
<div ng-show="$select.open" class="ui-select-choices selectize-dropdown single"
2+
ng-style="{top: position.top+'px', left: position.left+'px'}">
23
<div class="ui-select-choices-content selectize-dropdown-content">
34
<div class="ui-select-choices-group optgroup" role="listbox">
45
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label optgroup-header" ng-bind="$group.name"></div>

src/uiSelectChoicesDirective.js

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
uis.directive('uiSelectChoices',
2-
['uiSelectConfig', 'RepeatParser', 'uiSelectMinErr', '$compile',
3-
function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile) {
2+
['uiSelectConfig', 'RepeatParser', 'uiSelectMinErr', 'uisOffset', '$compile', '$document',
3+
function(uiSelectConfig, RepeatParser, uiSelectMinErr, uisOffset, $compile, $document) {
44

55
return {
66
restrict: 'EA',
@@ -21,6 +21,7 @@ uis.directive('uiSelectChoices',
2121

2222
// var repeat = RepeatParser.parse(attrs.repeat);
2323
var groupByExp = attrs.groupBy;
24+
var appendToBody = scope.$eval(attrs.appendToBody);
2425

2526
$select.parseRepeatAttr(attrs.repeat, groupByExp); //Result ready at $select.parserResult
2627

@@ -60,6 +61,44 @@ uis.directive('uiSelectChoices',
6061
var refreshDelay = scope.$eval(attrs.refreshDelay);
6162
$select.refreshDelay = refreshDelay !== undefined ? refreshDelay : uiSelectConfig.refreshDelay;
6263
});
64+
65+
if (appendToBody) {
66+
scope.$watch('$select.open', function(isOpen) {
67+
if (isOpen) {
68+
positionDropdown();
69+
} else {
70+
resetDropdown();
71+
}
72+
});
73+
}
74+
75+
// Hold on to a reference to the .ui-select-container element for appendToBody support
76+
var parent = element.parent();
77+
78+
function positionDropdown() {
79+
if (!appendToBody) {
80+
return;
81+
}
82+
83+
// Move the dropdown element to the end of the body
84+
$document.find('body').append(element);
85+
86+
var position = uisOffset(parent);
87+
element[0].style.left = position.left + 'px';
88+
element[0].style.top = position.top + parent[0].offsetHeight + 'px';
89+
element[0].style.width = parent[0].offsetWidth + 'px';
90+
}
91+
92+
function resetDropdown() {
93+
if (!appendToBody) {
94+
return;
95+
}
96+
parent.append(element);
97+
element[0].style.left = '';
98+
element[0].style.top = '';
99+
element[0].style.width = '';
100+
}
101+
63102
};
64103
}
65104
};

0 commit comments

Comments
 (0)