Skip to content

Commit 905835c

Browse files
committed
feat(dropdown): support programatic trigger, toggle callback & esc key
Closes angular-ui#270 Closes angular-ui#284 Closes angular-ui#1447 Closes angular-ui#1558
1 parent a33bac9 commit 905835c

File tree

4 files changed

+297
-100
lines changed

4 files changed

+297
-100
lines changed

src/dropdownToggle/docs/demo.html

+50-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,50 @@
1-
<li class="dropdown" ng-controller="DropdownCtrl">
2-
<a class="dropdown-toggle">
3-
Click me for a dropdown, yo!
4-
</a>
5-
<ul class="dropdown-menu">
6-
<li ng-repeat="choice in items">
7-
<a>{{choice}}</a>
8-
</li>
9-
</ul>
10-
</li>
1+
2+
<div ng-controller="DropdownCtrl">
3+
<!-- Simple dropdown -->
4+
<span class="dropdown" on-toggle="toggled(open)">
5+
<a class="dropdown-toggle">
6+
Click me for a dropdown, yo!
7+
</a>
8+
<ul class="dropdown-menu">
9+
<li ng-repeat="choice in items">
10+
<a>{{choice}}</a>
11+
</li>
12+
</ul>
13+
</span>
14+
15+
<!-- Single button -->
16+
<div class="btn-group" dropdown is-open="status.isopen">
17+
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
18+
Button dropdown <span class="caret"></span>
19+
</button>
20+
<ul class="dropdown-menu" role="menu">
21+
<li><a href="#">Action</a></li>
22+
<li><a href="#">Another action</a></li>
23+
<li><a href="#">Something else here</a></li>
24+
<li class="divider"></li>
25+
<li><a href="#">Separated link</a></li>
26+
</ul>
27+
</div>
28+
29+
<!-- Split button -->
30+
<div class="btn-group" dropdown>
31+
<button type="button" class="btn btn-danger">Action</button>
32+
<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown">
33+
<span class="caret"></span>
34+
<span class="sr-only">Split button!</span>
35+
</button>
36+
<ul class="dropdown-menu" role="menu">
37+
<li><a href="#">Action</a></li>
38+
<li><a href="#">Another action</a></li>
39+
<li><a href="#">Something else here</a></li>
40+
<li class="divider"></li>
41+
<li><a href="#">Separated link</a></li>
42+
</ul>
43+
</div>
44+
45+
<hr />
46+
<p>
47+
<button type="button" class="btn btn-default btn-sm" ng-click="toggleDropdown($event)">Toggle button dropdown</button>
48+
</p>
49+
50+
</div>

src/dropdownToggle/docs/demo.js

+14
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,18 @@ function DropdownCtrl($scope) {
44
'And another choice for you.',
55
'but wait! A third!'
66
];
7+
8+
$scope.status = {
9+
isopen: false
10+
};
11+
12+
$scope.toggled = function(open) {
13+
console.log('Dropdown is now: ', open);
14+
};
15+
16+
$scope.toggleDropdown = function($event) {
17+
$event.preventDefault();
18+
$event.stopPropagation();
19+
$scope.status.isopen = !$scope.status.isopen;
20+
};
721
}

src/dropdownToggle/dropdownToggle.js

+98-43
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,107 @@
1-
/*
2-
* dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
3-
* @restrict class or attribute
4-
* @example:
5-
<li class="dropdown">
6-
<a class="dropdown-toggle">My Dropdown Menu</a>
7-
<ul class="dropdown-menu">
8-
<li ng-repeat="choice in dropChoices">
9-
<a ng-href="{{choice.href}}">{{choice.text}}</a>
10-
</li>
11-
</ul>
12-
</li>
13-
*/
14-
15-
angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', function ($document) {
16-
var openElement = null,
17-
closeMenu = angular.noop;
1+
angular.module('ui.bootstrap.dropdownToggle', [])
2+
3+
.constant('dropdownConfig', {
4+
openClass: 'open'
5+
})
6+
7+
.service('dropdownService', ['$document', function($document) {
8+
var self = this, openScope = null;
9+
10+
this.open = function( dropdownScope ) {
11+
if ( !openScope ) {
12+
$document.bind('click', documentClickBind);
13+
$document.bind('keydown', escKeyBind);
14+
}
15+
16+
if ( openScope && openScope !== dropdownScope ) {
17+
openScope.isOpen = false;
18+
}
19+
20+
openScope = dropdownScope;
21+
};
22+
23+
this.close = function( dropdownScope ) {
24+
if ( openScope === dropdownScope ) {
25+
openScope = null;
26+
$document.unbind('click', documentClickBind);
27+
$document.unbind('keydown', escKeyBind);
28+
}
29+
};
30+
31+
var documentClickBind = function() {
32+
openScope.$apply(function() {
33+
openScope.isOpen = false;
34+
});
35+
};
36+
37+
var escKeyBind = function( evt ) {
38+
if ( evt.which === 27 ) {
39+
openScope.$apply(function() {
40+
openScope.isOpen = false;
41+
});
42+
}
43+
};
44+
}])
45+
46+
.controller('DropdownController', ['$scope', '$attrs', 'dropdownConfig', 'dropdownService', function($scope, $attrs, dropdownConfig, dropdownService) {
47+
var self = this, openClass = dropdownConfig.openClass;
48+
49+
this.init = function( element ) {
50+
self.$element = element;
51+
$scope.isOpen = angular.isDefined($attrs.isOpen) ? $scope.$parent.$eval($attrs.isOpen) : false;
52+
};
53+
54+
this.toggle = function() {
55+
$scope.isOpen = !$scope.isOpen;
56+
};
57+
58+
$scope.$watch('isOpen', function( value ) {
59+
self.$element.toggleClass( openClass, value );
60+
61+
if ( value ) {
62+
dropdownService.open( $scope );
63+
} else {
64+
dropdownService.close( $scope );
65+
}
66+
67+
$scope.onToggle({ open: !!value });
68+
});
69+
70+
$scope.$on('$locationChangeSuccess', function() {
71+
$scope.isOpen = false;
72+
});
73+
}])
74+
75+
.directive('dropdown', function() {
1876
return {
1977
restrict: 'CA',
20-
link: function(scope, element, attrs) {
21-
scope.$on('$locationChangeSuccess', function() { closeMenu(); });
22-
element.parent().bind('click', function() { closeMenu(); });
23-
element.bind('click', function (event) {
78+
controller: 'DropdownController',
79+
scope: {
80+
isOpen: '=?',
81+
onToggle: '&'
82+
},
83+
link: function(scope, element, attrs, dropdownCtrl) {
84+
dropdownCtrl.init( element );
85+
}
86+
};
87+
})
2488

25-
var elementWasOpen = (element === openElement);
89+
.directive('dropdownToggle', function() {
90+
return {
91+
restrict: 'CA',
92+
require: '?^dropdown',
93+
link: function(scope, element, attrs, dropdownCtrl) {
94+
if ( !dropdownCtrl ) {
95+
return;
96+
}
2697

98+
element.bind('click', function(event) {
2799
event.preventDefault();
28100
event.stopPropagation();
29-
30-
if (!!openElement) {
31-
closeMenu();
32-
}
33-
34-
if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
35-
element.parent().addClass('open');
36-
openElement = element;
37-
closeMenu = function (event) {
38-
if (event) {
39-
event.preventDefault();
40-
event.stopPropagation();
41-
}
42-
$document.unbind('click', closeMenu);
43-
element.parent().removeClass('open');
44-
closeMenu = angular.noop;
45-
openElement = null;
46-
};
47-
$document.bind('click', closeMenu);
48-
}
101+
scope.$apply(function() {
102+
dropdownCtrl.toggle();
103+
});
49104
});
50105
}
51106
};
52-
}]);
107+
});

0 commit comments

Comments
 (0)