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

Commit ae31079

Browse files
committed
feat(dropdownToggle): support programmatic trigger & toggle callback
Closes #270 Closes #284 Closes #1447
1 parent 745246e commit ae31079

File tree

5 files changed

+310
-104
lines changed

5 files changed

+310
-104
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/docs/readme.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11

2-
DropdownToggle is a simple directive which will toggle a dropdown link on click. Simply put it on the `<a>` tag of the toggler-element, and it will find the nearest dropdown menu and toggle it when the `<a dropdown-toggle>` is clicked.
2+
Dropdown is a simple directive which will toggle a dropdown menu on click or programmatically.
3+
You can either use `is-open` to toggle or add inside a `<a dropdown-toggle>` element to toggle it when is clicked.
4+
There is also the `on-toggle(open)` optional expression fired when dropdown changes state.

src/dropdownToggle/dropdownToggle.js

+89-41
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,100 @@
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', closeDropdown);
13+
}
14+
15+
if ( openScope && openScope !== dropdownScope ) {
16+
openScope.isOpen = false;
17+
}
18+
19+
openScope = dropdownScope;
20+
};
21+
22+
this.close = function( dropdownScope ) {
23+
if ( openScope === dropdownScope ) {
24+
openScope = null;
25+
$document.unbind('click', closeDropdown);
26+
}
27+
};
28+
29+
var closeDropdown = function() {
30+
openScope.$apply(function() {
31+
openScope.isOpen = false;
32+
});
33+
};
34+
}])
35+
36+
.controller('DropdownController', ['$scope', '$attrs', 'dropdownConfig', 'dropdownService', function($scope, $attrs, dropdownConfig, dropdownService) {
37+
var self = this, openClass = dropdownConfig.openClass;
38+
39+
this.init = function( element ) {
40+
self.$element = element;
41+
$scope.isOpen = angular.isDefined($attrs.isOpen) ? $scope.$parent.$eval($attrs.isOpen) : false;
42+
};
43+
44+
this.toggle = function( open ) {
45+
return $scope.isOpen = arguments.length ? !!open : !$scope.isOpen;
46+
};
47+
48+
$scope.$watch('isOpen', function( value ) {
49+
self.$element.toggleClass( openClass, value );
50+
51+
if ( value ) {
52+
dropdownService.open( $scope );
53+
} else {
54+
dropdownService.close( $scope );
55+
}
56+
57+
$scope.onToggle({ open: !!value });
58+
});
59+
60+
$scope.$on('$locationChangeSuccess', function() {
61+
$scope.isOpen = false;
62+
});
63+
}])
64+
65+
.directive('dropdown', function() {
1866
return {
1967
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) {
68+
controller: 'DropdownController',
69+
scope: {
70+
isOpen: '=?',
71+
onToggle: '&'
72+
},
73+
link: function(scope, element, attrs, dropdownCtrl) {
74+
dropdownCtrl.init( element );
75+
}
76+
};
77+
})
2478

25-
var elementWasOpen = (element === openElement);
79+
.directive('dropdownToggle', function() {
80+
return {
81+
restrict: 'CA',
82+
require: '?^dropdown',
83+
link: function(scope, element, attrs, dropdownCtrl) {
84+
if ( !dropdownCtrl ) {
85+
return;
86+
}
2687

88+
element.bind('click', function(event) {
2789
event.preventDefault();
2890
event.stopPropagation();
2991

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);
92+
if ( !element.hasClass('disabled') && !element.prop('disabled') ) {
93+
scope.$apply(function() {
94+
dropdownCtrl.toggle();
95+
});
4896
}
4997
});
5098
}
5199
};
52-
}]);
100+
});

0 commit comments

Comments
 (0)