angular-ui-bootstrap
Version:
Native AngularJS (Angular) directives for Bootstrap
146 lines (131 loc) • 5.08 kB
JavaScript
angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex'])
.constant('uibAccordionConfig', {
closeOthers: true
})
.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
// This array keeps track of the accordion groups
this.groups = [];
// Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
this.closeOthers = function(openGroup) {
var closeOthers = angular.isDefined($attrs.closeOthers) ?
$scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
if (closeOthers) {
angular.forEach(this.groups, function(group) {
if (group !== openGroup) {
group.isOpen = false;
}
});
}
};
// This is called from the accordion-group directive to add itself to the accordion
this.addGroup = function(groupScope) {
var that = this;
this.groups.push(groupScope);
groupScope.$on('$destroy', function(event) {
that.removeGroup(groupScope);
});
};
// This is called from the accordion-group directive when to remove itself
this.removeGroup = function(group) {
var index = this.groups.indexOf(group);
if (index !== -1) {
this.groups.splice(index, 1);
}
};
}])
// The accordion directive simply sets up the directive controller
// and adds an accordion CSS class to itself element.
.directive('uibAccordion', function() {
return {
controller: 'UibAccordionController',
controllerAs: 'accordion',
transclude: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/accordion/accordion.html';
}
};
})
// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
.directive('uibAccordionGroup', function() {
return {
require: '^uibAccordion', // We need this directive to be inside an accordion
transclude: true, // It transcludes the contents of the directive into the template
restrict: 'A',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
},
scope: {
heading: '@', // Interpolate the heading attribute onto this scope
panelClass: '@?', // Ditto with panelClass
isOpen: '=?',
isDisabled: '=?'
},
controller: function() {
this.setHeading = function(element) {
this.heading = element;
};
},
link: function(scope, element, attrs, accordionCtrl) {
element.addClass('panel');
accordionCtrl.addGroup(scope);
scope.openClass = attrs.openClass || 'panel-open';
scope.panelClass = attrs.panelClass || 'panel-default';
scope.$watch('isOpen', function(value) {
element.toggleClass(scope.openClass, !!value);
if (value) {
accordionCtrl.closeOthers(scope);
}
});
scope.toggleOpen = function($event) {
if (!scope.isDisabled) {
if (!$event || $event.which === 32) {
scope.isOpen = !scope.isOpen;
}
}
};
var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
scope.headingId = id + '-tab';
scope.panelId = id + '-panel';
}
};
})
// Use accordion-heading below an accordion-group to provide a heading containing HTML
.directive('uibAccordionHeading', function() {
return {
transclude: true, // Grab the contents to be used as the heading
template: '', // In effect remove this element!
replace: true,
require: '^uibAccordionGroup',
link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
// Pass the heading to the accordion-group controller
// so that it can be transcluded into the right place in the template
// [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
}
};
})
// Use in the accordion-group template to indicate where you want the heading to be transcluded
// You must provide the property on the accordion-group controller that will hold the transcluded element
.directive('uibAccordionTransclude', function() {
return {
require: '^uibAccordionGroup',
link: function(scope, element, attrs, controller) {
scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
if (heading) {
var elem = angular.element(element[0].querySelector(getHeaderSelectors()));
elem.html('');
elem.append(heading);
}
});
}
};
function getHeaderSelectors() {
return 'uib-accordion-header,' +
'data-uib-accordion-header,' +
'x-uib-accordion-header,' +
'uib\\:accordion-header,' +
'[uib-accordion-header],' +
'[data-uib-accordion-header],' +
'[x-uib-accordion-header]';
}
});