2 * Angular Material Design
3 * https://github.com/angular/material
7 (function( window, angular, undefined ){
12 * @name material.components.list
16 angular.module('material.components.list', [
19 .controller('MdListController', MdListController)
20 .directive('mdList', mdListDirective)
21 .directive('mdListItem', mdListItemDirective);
26 * @module material.components.list
31 * The `<md-list>` directive is a list container for 1..n `<md-list-item>` tags.
36 * <md-list-item class="md-2-line" ng-repeat="item in todos">
37 * <md-checkbox ng-model="item.done"></md-checkbox>
38 * <div class="md-list-item-text">
39 * <h3>{{item.title}}</h3>
40 * <p>{{item.description}}</p>
47 function mdListDirective($mdTheming) {
50 compile: function(tEl) {
51 tEl[0].setAttribute('role', 'list');
56 mdListDirective.$inject = ["$mdTheming"];
60 * @module material.components.list
65 * The `<md-list-item>` directive is a container intended for row items in a `<md-list>` container.
71 * Item content in list
77 function mdListItemDirective($mdAria, $mdConstant, $timeout) {
78 var proxiedTypes = ['md-checkbox', 'md-switch'];
81 controller: 'MdListController',
82 compile: function(tEl, tAttrs) {
83 // Check for proxy controls (no ng-click on parent, and a control inside)
84 var secondaryItem = tEl[0].querySelector('.md-secondary');
85 var hasProxiedElement;
88 tEl[0].setAttribute('role', 'listitem');
90 if (!tAttrs.ngClick) {
91 for (var i = 0, type; type = proxiedTypes[i]; ++i) {
92 if (proxyElement = tEl[0].querySelector(type)) {
93 hasProxiedElement = true;
97 if (hasProxiedElement) {
99 } else if (!tEl[0].querySelector('md-button')) {
100 tEl.addClass('md-no-proxy');
108 function setupToggleAria() {
109 var toggleTypes = ['md-switch', 'md-checkbox'];
112 for (var i = 0, toggleType; toggleType = toggleTypes[i]; ++i) {
113 if (toggle = tEl.find(toggleType)[0]) {
114 if (!toggle.hasAttribute('aria-label')) {
115 var p = tEl.find('p')[0];
117 toggle.setAttribute('aria-label', 'Toggle ' + p.textContent);
123 function wrapIn(type) {
126 container = angular.element('<div class="md-no-style md-list-item-inner">');
127 container.append(tEl.contents());
128 tEl.addClass('md-proxy-focus');
130 container = angular.element('<md-button class="md-no-style"><div class="md-list-item-inner"></div></md-button>');
131 var copiedAttrs = ['ng-click', 'aria-label', 'ng-disabled'];
132 angular.forEach(copiedAttrs, function(attr) {
133 if (tEl[0].hasAttribute(attr)) {
134 container[0].setAttribute(attr, tEl[0].getAttribute(attr));
135 tEl[0].removeAttribute(attr);
138 container.children().eq(0).append(tEl.contents());
141 tEl[0].setAttribute('tabindex', '-1');
142 tEl.append(container);
144 if (secondaryItem && secondaryItem.hasAttribute('ng-click')) {
145 $mdAria.expect(secondaryItem, 'aria-label');
146 var buttonWrapper = angular.element('<md-button class="md-secondary-container md-icon-button">');
147 buttonWrapper.attr('ng-click', secondaryItem.getAttribute('ng-click'));
148 secondaryItem.removeAttribute('ng-click');
149 secondaryItem.setAttribute('tabindex', '-1');
150 secondaryItem.classList.remove('md-secondary');
151 buttonWrapper.append(secondaryItem);
152 secondaryItem = buttonWrapper[0];
155 // Check for a secondary item and move it outside
156 if ( secondaryItem && (
157 secondaryItem.hasAttribute('ng-click') ||
159 isProxiedElement(secondaryItem) )
161 tEl.addClass('md-with-secondary');
162 tEl.append(secondaryItem);
166 function isProxiedElement(el) {
167 return proxiedTypes.indexOf(el.nodeName.toLowerCase()) != -1;
172 function postLink($scope, $element, $attr, ctrl) {
175 firstChild = $element[0].firstElementChild,
176 hasClick = firstChild && firstChild.hasAttribute('ng-click');
181 if ($element.hasClass('md-proxy-focus') && proxies.length) {
182 angular.forEach(proxies, function(proxy) {
183 proxy = angular.element(proxy);
185 $scope.mouseActive = false;
186 proxy.on('mousedown', function() {
187 $scope.mouseActive = true;
189 $scope.mouseActive = false;
192 .on('focus', function() {
193 if ($scope.mouseActive === false) { $element.addClass('md-focused'); }
194 proxy.on('blur', function proxyOnBlur() {
195 $element.removeClass('md-focused');
196 proxy.off('blur', proxyOnBlur);
202 function computeProxies() {
203 var children = $element.children();
204 if (children.length && !children[0].hasAttribute('ng-click')) {
205 angular.forEach(proxiedTypes, function(type) {
206 angular.forEach(firstChild.querySelectorAll(type), function(child) {
212 function computeClickable() {
213 if (proxies.length || hasClick) {
214 $element.addClass('md-clickable');
216 ctrl.attachRipple($scope, angular.element($element[0].querySelector('.md-no-style')));
220 if (!hasClick && !proxies.length) {
221 firstChild && firstChild.addEventListener('keypress', function(e) {
222 if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA') {
223 var keyCode = e.which || e.keyCode;
224 if (keyCode == $mdConstant.KEY_CODE.SPACE) {
235 $element.off('click');
236 $element.off('keypress');
238 if (proxies.length && firstChild) {
239 $element.children().eq(0).on('click', function(e) {
240 if (firstChild.contains(e.target)) {
241 angular.forEach(proxies, function(proxy) {
242 if (e.target !== proxy && !proxy.contains(e.target)) {
243 angular.element(proxy).triggerHandler('click');
253 mdListItemDirective.$inject = ["$mdAria", "$mdConstant", "$timeout"];
258 * @name MdListController
259 * @module material.components.list
262 function MdListController($scope, $element, $mdListInkRipple) {
264 ctrl.attachRipple = attachRipple;
266 function attachRipple (scope, element) {
268 $mdListInkRipple.attach(scope, element, options);
271 MdListController.$inject = ["$scope", "$element", "$mdListInkRipple"];
274 })(window, window.angular);