Skip to content

Commit

Permalink
feat(dropdown): add optional placement and respect bootstrap classes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Umer Farooq committed Dec 12, 2016
1 parent 79ec214 commit 0557dfb
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 38 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "2.2.1-SNAPSHOT",
"version": "2.2.0+inin.1.3",
"homepage": "http://angular-ui.github.io/bootstrap/",
"dependencies": {},
"directories": {
Expand Down
123 changes: 123 additions & 0 deletions src/dropdown/docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,129 @@ <h4>append-to vs. append-to-body vs. inline example</h4>
</div>
</div>
</div>
<!-- Placement use case -->
<h4>Inline placement and positioning</h4>
<div class="btn-group dropup" uib-dropdown >
<button id="simple-btn-dropup" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button dropup <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-dropup">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown >
<button id="simple-btn-menu-right" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button dropdown-menu-right <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-menu-right">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-placement="top-left">
<button id="simple-btn-top-left" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button top-left <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-top-left">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-placement="bottom-right">
<button id="simple-btn-bottom-right" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button bottom-right <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-bottom-right">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-placement="right-top">
<button id="simple-btn-right-top" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button right-top <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-right-top">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<h4>append-to-body placement and positioning</h4>
<div class="btn-group dropup" uib-dropdown dropdown-append-to-body>
<button id="simple-btn-dropup-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button dropup <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-dropup-append-to-body">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-append-to-body>
<button id="simple-btn-menu-right-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button dropdown-menu-right <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-menu-right-append-to-body">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-placement="top-left">
<button id="simple-btn-top-left-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button top-left <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-top-left-append-to-body">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-placement="bottom-right">
<button id="simple-btn-bottom-right-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button bottom-right <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-bottom-right-append-to-body">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>
<div class="btn-group" uib-dropdown dropdown-append-to-body dropdown-placement="right-top">
<button id="simple-btn-right-top-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
Button right-top <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="simple-btn-right-top-append-to-body">
<li role="menuitem"><a href="#">Action</a></li>
<li role="menuitem"><a href="#">Another action</a></li>
<li role="menuitem"><a href="#">Something else here</a></li>
<li class="divider"></li>
<li role="menuitem"><a href="#">Separated link</a></li>
</ul>
</div>

<script type="text/ng-template" id="dropdown.html">
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="button-template-url">
Expand Down
19 changes: 19 additions & 0 deletions src/dropdown/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ Each of these parts need to be used as attribute directives.
<small class="badge">$</small> -
An optional expression called when the dropdown menu is opened or closed.

* `dropdown-placement`
<small class="badge">C</small>
_(Default: `auto bottom-left`, Config: `placement`)_ -
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:

* `top` - menu on top, horizontally centered on host element.
* `top-left` - menu on top, left edge aligned with host element left edge.
* `top-right` - menu on top, right edge aligned with host element right edge.
* `bottom` - menu on bottom, horizontally centered on host element.
* `bottom-left` - menu on bottom, left edge aligned with host element left edge.
* `bottom-right` - menu on bottom, right edge aligned with host element right edge.
* `left` - menu on left, vertically centered on host element.
* `left-top` - menu on left, top edge aligned with host element top edge.
* `left-bottom` - menu on left, bottom edge aligned with host element bottom edge.
* `right` - menu on right, vertically centered on host element.
* `right-top` - menu on right, top edge aligned with host element top edge.
* `right-bottom` - menu on right, bottom edge aligned with host element bottom edge.


### uib-dropdown-menu settings

* `template-url`
Expand Down
63 changes: 27 additions & 36 deletions src/dropdown/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
setIsOpen = angular.noop,
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
appendToBody = false,
appendToBodyPlacement = null,
appendTo = null,
keynavEnabled = false,
selectedOption = null,
Expand Down Expand Up @@ -201,49 +202,34 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
};

scope.$watch('isOpen', function(isOpen, wasOpen) {
if (appendTo && self.dropdownMenu) {
var dropUp = $element.hasClass('dropup');
var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
var placement = 'auto bottom-left';
if(dropUp && rightalign){
function positionDropdownMenu(container) {
if (!self.dropdownMenu) { return; }
var placement = $attrs.dropdownPlacement;
if (!placement) {
var dropUp = container.hasClass('dropup') || $element.hasClass('dropup'),
rightAlign = self.dropdownMenu.hasClass('dropdown-menu-right');
placement = 'auto bottom-left';
if (dropUp && rightAlign) {
placement = 'auto top-right';
} else if (dropUp) {
placement = 'auto top-left';
} else if (rightalign) {
} else if (rightAlign) {
placement = 'auto bottom-right';
}

var pos = $position.positionElements($element, self.dropdownMenu, placement, true),
css,
scrollbarPadding,
scrollbarWidth = 0;

css = {
top: pos.top + 'px',
left: pos.left + 'px',
right: 'auto',
display: isOpen ? 'block' : 'none'
};

// Need to adjust our positioning to be relative to the appendTo container
// if it's not the body element
if (!appendToBody) {
var appendOffset = $position.offset(appendTo);

css.top = pos.top - appendOffset.top + 'px';

if (!rightalign) {
css.left = pos.left - appendOffset.left + 'px';
} else {
css.right = window.innerWidth -
(pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
}
}

self.dropdownMenu.css(css);
}
self.dropdownMenu.css({ display: 'block' });
var pos = $position.positionElements(appendToBody ? $element : container, self.dropdownMenu, placement, appendToBody),
css;

css = {
top: pos.top + 'px',
left: pos.left + 'px',
right: 'auto'
};
self.dropdownMenu.css(css);
}

scope.$watch('isOpen', function(isOpen, wasOpen) {
var openContainer = appendTo ? appendTo : $element;
var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);

Expand All @@ -263,10 +249,12 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
var newEl = dropdownElement;
self.dropdownMenu.replaceWith(newEl);
self.dropdownMenu = newEl;
positionDropdownMenu(openContainer);
$document.on('keydown', uibDropdownService.keybindFilter);
});
});
} else {
positionDropdownMenu(openContainer);
$document.on('keydown', uibDropdownService.keybindFilter);
}

Expand All @@ -282,6 +270,9 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
self.dropdownMenu.replaceWith(newEl);
self.dropdownMenu = newEl;
}
if (self.dropdownMenu) {
self.dropdownMenu.css({ display: 'none' });
}

self.selectedOption = null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/position/position.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ angular.module('ui.bootstrap.position', [])
offsetParent: function(elem) {
elem = this.getRawNode(elem);

var offsetParent = elem.offsetParent || $document[0].documentElement;
var offsetParent = elem;

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

0 comments on commit 0557dfb

Please sign in to comment.