Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 57bb422

Browse files
author
Umer Farooq
committed
feat(dropdown): add optional placement and respect bootstrap classes
Add an optional dropdown-placement attribute setting much like the tooltips'. If it is specified then bootstrap's dropup and dropdown-menu-right classes will be ignored. Updated positioning of the dropdown menu to always use the position service. Ensured that dropup and dropdown-menu-right will be respected for append-to and append-to-body dropdowns. Updated positioning service to return the element when looking for the offsetParent if the element is not statically positioned.
1 parent 57ed7e4 commit 57bb422

File tree

4 files changed

+174
-45
lines changed

4 files changed

+174
-45
lines changed

src/dropdown/docs/demo.html

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,129 @@ <h4>append-to vs. append-to-body vs. inline example</h4>
131131
</div>
132132
</div>
133133
</div>
134+
<!-- Placement use case -->
135+
<h4>Inline placement and positioning</h4>
136+
<div class="btn-group dropup" uib-dropdown >
137+
<button id="simple-btn-dropup" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
138+
Button dropup <span class="caret"></span>
139+
</button>
140+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-dropup">
141+
<li role="menuitem"><a href="#">Action</a></li>
142+
<li role="menuitem"><a href="#">Another action</a></li>
143+
<li role="menuitem"><a href="#">Something else here</a></li>
144+
<li class="divider"></li>
145+
<li role="menuitem"><a href="#">Separated link</a></li>
146+
</ul>
147+
</div>
148+
<div class="btn-group" uib-dropdown >
149+
<button id="simple-btn-menu-right" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
150+
Button dropdown-menu-right <span class="caret"></span>
151+
</button>
152+
<ul class="dropdown-menu dropdown-menu-right" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-menu-right">
153+
<li role="menuitem"><a href="#">Action</a></li>
154+
<li role="menuitem"><a href="#">Another action</a></li>
155+
<li role="menuitem"><a href="#">Something else here</a></li>
156+
<li class="divider"></li>
157+
<li role="menuitem"><a href="#">Separated link</a></li>
158+
</ul>
159+
</div>
160+
<div class="btn-group" uib-dropdown dropdown-placement="top-left">
161+
<button id="simple-btn-top-left" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
162+
Button top-left <span class="caret"></span>
163+
</button>
164+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-top-left">
165+
<li role="menuitem"><a href="#">Action</a></li>
166+
<li role="menuitem"><a href="#">Another action</a></li>
167+
<li role="menuitem"><a href="#">Something else here</a></li>
168+
<li class="divider"></li>
169+
<li role="menuitem"><a href="#">Separated link</a></li>
170+
</ul>
171+
</div>
172+
<div class="btn-group" uib-dropdown dropdown-placement="bottom-right">
173+
<button id="simple-btn-bottom-right" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
174+
Button bottom-right <span class="caret"></span>
175+
</button>
176+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-bottom-right">
177+
<li role="menuitem"><a href="#">Action</a></li>
178+
<li role="menuitem"><a href="#">Another action</a></li>
179+
<li role="menuitem"><a href="#">Something else here</a></li>
180+
<li class="divider"></li>
181+
<li role="menuitem"><a href="#">Separated link</a></li>
182+
</ul>
183+
</div>
184+
<div class="btn-group" uib-dropdown dropdown-placement="right-top">
185+
<button id="simple-btn-right-top" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
186+
Button right-top <span class="caret"></span>
187+
</button>
188+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-right-top">
189+
<li role="menuitem"><a href="#">Action</a></li>
190+
<li role="menuitem"><a href="#">Another action</a></li>
191+
<li role="menuitem"><a href="#">Something else here</a></li>
192+
<li class="divider"></li>
193+
<li role="menuitem"><a href="#">Separated link</a></li>
194+
</ul>
195+
</div>
196+
<h4>append-to-body placement and positioning</h4>
197+
<div class="btn-group dropup" uib-dropdown dropdown-append-to-body>
198+
<button id="simple-btn-dropup-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
199+
Button dropup <span class="caret"></span>
200+
</button>
201+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-dropup-append-to-body">
202+
<li role="menuitem"><a href="#">Action</a></li>
203+
<li role="menuitem"><a href="#">Another action</a></li>
204+
<li role="menuitem"><a href="#">Something else here</a></li>
205+
<li class="divider"></li>
206+
<li role="menuitem"><a href="#">Separated link</a></li>
207+
</ul>
208+
</div>
209+
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-append-to-body>
210+
<button id="simple-btn-menu-right-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
211+
Button dropdown-menu-right <span class="caret"></span>
212+
</button>
213+
<ul class="dropdown-menu dropdown-menu-right" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-menu-right-append-to-body">
214+
<li role="menuitem"><a href="#">Action</a></li>
215+
<li role="menuitem"><a href="#">Another action</a></li>
216+
<li role="menuitem"><a href="#">Something else here</a></li>
217+
<li class="divider"></li>
218+
<li role="menuitem"><a href="#">Separated link</a></li>
219+
</ul>
220+
</div>
221+
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-placement="top-left">
222+
<button id="simple-btn-top-left-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
223+
Button top-left <span class="caret"></span>
224+
</button>
225+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-top-left-append-to-body">
226+
<li role="menuitem"><a href="#">Action</a></li>
227+
<li role="menuitem"><a href="#">Another action</a></li>
228+
<li role="menuitem"><a href="#">Something else here</a></li>
229+
<li class="divider"></li>
230+
<li role="menuitem"><a href="#">Separated link</a></li>
231+
</ul>
232+
</div>
233+
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-placement="bottom-right">
234+
<button id="simple-btn-bottom-right-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
235+
Button bottom-right <span class="caret"></span>
236+
</button>
237+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-bottom-right-append-to-body">
238+
<li role="menuitem"><a href="#">Action</a></li>
239+
<li role="menuitem"><a href="#">Another action</a></li>
240+
<li role="menuitem"><a href="#">Something else here</a></li>
241+
<li class="divider"></li>
242+
<li role="menuitem"><a href="#">Separated link</a></li>
243+
</ul>
244+
</div>
245+
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-placement="right-top">
246+
<button id="simple-btn-right-top-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
247+
Button right-top <span class="caret"></span>
248+
</button>
249+
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-right-top-append-to-body">
250+
<li role="menuitem"><a href="#">Action</a></li>
251+
<li role="menuitem"><a href="#">Another action</a></li>
252+
<li role="menuitem"><a href="#">Something else here</a></li>
253+
<li class="divider"></li>
254+
<li role="menuitem"><a href="#">Separated link</a></li>
255+
</ul>
256+
</div>
134257

135258
<script type="text/ng-template" id="dropdown.html">
136259
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="button-template-url">

src/dropdown/docs/readme.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ Each of these parts need to be used as attribute directives.
4242
<small class="badge">$</small> -
4343
An optional expression called when the dropdown menu is opened or closed.
4444

45+
* `dropdown-placement`
46+
<small class="badge">C</small>
47+
_(Default: `bottom-left`, Config: `placement`)_ -
48+
If specified, bootstrap's dropup and dropdown-menu-right classes will be ignored. Passing in 'auto' separated by a space before the placement will enable auto positioning, e.g: "auto bottom-left". The dropdown will attempt to position the menu where it fits in the closest scrollable ancestor. Accepts:
49+
50+
* `top` - menu on top, horizontally centered on host element.
51+
* `top-left` - menu on top, left edge aligned with host element left edge.
52+
* `top-right` - menu on top, right edge aligned with host element right edge.
53+
* `bottom` - menu on bottom, horizontally centered on host element.
54+
* `bottom-left` - menu on bottom, left edge aligned with host element left edge.
55+
* `bottom-right` - menu on bottom, right edge aligned with host element right edge.
56+
* `left` - menu on left, vertically centered on host element.
57+
* `left-top` - menu on left, top edge aligned with host element top edge.
58+
* `left-bottom` - menu on left, bottom edge aligned with host element bottom edge.
59+
* `right` - menu on right, vertically centered on host element.
60+
* `right-top` - menu on right, top edge aligned with host element top edge.
61+
* `right-bottom` - menu on right, bottom edge aligned with host element bottom edge.
62+
63+
4564
### uib-dropdown-menu settings
4665

4766
* `template-url`

src/dropdown/dropdown.js

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
145145
setIsOpen = angular.noop,
146146
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
147147
appendToBody = false,
148+
appendToBodyPlacement = null,
148149
appendTo = null,
149150
keynavEnabled = false,
150151
selectedOption = null,
@@ -253,53 +254,34 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
253254
}
254255
};
255256

256-
scope.$watch('isOpen', function(isOpen, wasOpen) {
257-
if (appendTo && self.dropdownMenu) {
258-
var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
259-
css,
260-
rightalign,
261-
scrollbarPadding,
262-
scrollbarWidth = 0;
263-
264-
css = {
265-
top: pos.top + 'px',
266-
display: isOpen ? 'block' : 'none'
267-
};
268-
269-
rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
270-
if (!rightalign) {
271-
css.left = pos.left + 'px';
272-
css.right = 'auto';
273-
} else {
274-
css.left = 'auto';
275-
scrollbarPadding = $position.scrollbarPadding(appendTo);
276-
277-
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
278-
scrollbarWidth = scrollbarPadding.scrollbarWidth;
279-
}
280-
281-
css.right = window.innerWidth - scrollbarWidth -
282-
(pos.left + $element.prop('offsetWidth')) + 'px';
257+
function positionDropdownMenu(container) {
258+
if (!self.dropdownMenu) { return; }
259+
var placement = $attrs.dropdownPlacement;
260+
if (!placement) {
261+
var dropUp = container.hasClass('dropup') || $element.hasClass('dropup'),
262+
rightAlign = self.dropdownMenu.hasClass('dropdown-menu-right');
263+
placement = 'bottom-left';
264+
if (dropUp && rightAlign) {
265+
placement = 'top-right';
266+
} else if (dropUp) {
267+
placement = 'top-left';
268+
} else if (rightAlign) {
269+
placement = 'bottom-right';
283270
}
284-
285-
// Need to adjust our positioning to be relative to the appendTo container
286-
// if it's not the body element
287-
if (!appendToBody) {
288-
var appendOffset = $position.offset(appendTo);
289-
290-
css.top = pos.top - appendOffset.top + 'px';
291-
292-
if (!rightalign) {
293-
css.left = pos.left - appendOffset.left + 'px';
294-
} else {
295-
css.right = window.innerWidth -
296-
(pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
297-
}
298-
}
299-
300-
self.dropdownMenu.css(css);
301271
}
272+
self.dropdownMenu.css({ display: 'block' });
273+
var pos = $position.positionElements(appendToBody ? $element : container, self.dropdownMenu, placement, appendToBody),
274+
css;
275+
276+
css = {
277+
top: pos.top + 'px',
278+
left: pos.left + 'px',
279+
right: 'auto'
280+
};
281+
self.dropdownMenu.css(css);
282+
}
302283

284+
scope.$watch('isOpen', function(isOpen, wasOpen) {
303285
var openContainer = appendTo ? appendTo : $element;
304286
var dropdownOpenClass = appendTo ? appendToOpenClass : openClass;
305287
var hasOpenClass = openContainer.hasClass(dropdownOpenClass);
@@ -327,10 +309,12 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
327309
var newEl = dropdownElement;
328310
self.dropdownMenu.replaceWith(newEl);
329311
self.dropdownMenu = newEl;
312+
positionDropdownMenu(openContainer);
330313
$document.on('keydown', uibDropdownService.keybindFilter);
331314
});
332315
});
333316
} else {
317+
positionDropdownMenu(openContainer);
334318
$document.on('keydown', uibDropdownService.keybindFilter);
335319
}
336320

@@ -345,6 +329,9 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
345329
self.dropdownMenu.replaceWith(newEl);
346330
self.dropdownMenu = newEl;
347331
}
332+
if (self.dropdownMenu) {
333+
self.dropdownMenu.css({ display: 'none' });
334+
}
348335

349336
self.selectedOption = null;
350337
}

src/position/position.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ angular.module('ui.bootstrap.position', [])
6565
offsetParent: function(elem) {
6666
elem = this.getRawNode(elem);
6767

68-
var offsetParent = elem.offsetParent || $document[0].documentElement;
68+
var offsetParent = elem;
6969

7070
function isStaticPositioned(el) {
7171
return ($window.getComputedStyle(el).position || 'static') === 'static';

0 commit comments

Comments
 (0)