2 * Angular Material Design
3 * https://github.com/angular/material
7 goog.provide('ng.material.components.list');
8 goog.require('ng.material.core');
11 * @name material.components.list
15 angular.module('material.components.list', [
18 .controller('MdListController', MdListController)
19 .directive('mdList', mdListDirective)
20 .directive('mdListItem', mdListItemDirective);
25 * @module material.components.list
30 * The `<md-list>` directive is a list container for 1..n `<md-list-item>` tags.
35 * <md-list-item class="md-2-line" ng-repeat="item in todos">
36 * <md-checkbox ng-model="item.done"></md-checkbox>
37 * <div class="md-list-item-text">
38 * <h3>{{item.title}}</h3>
39 * <p>{{item.description}}</p>
46 function mdListDirective($mdTheming) {
49 compile: function(tEl) {
50 tEl[0].setAttribute('role', 'list');
55 mdListDirective.$inject = ["$mdTheming"];
59 * @module material.components.list
64 * The `<md-list-item>` directive is a container intended for row items in a `<md-list>` container.
70 * Item content in list
76 function mdListItemDirective($mdAria, $mdConstant, $timeout) {
77 var proxiedTypes = ['md-checkbox', 'md-switch'];
80 controller: 'MdListController',
81 compile: function(tEl, tAttrs) {
82 // Check for proxy controls (no ng-click on parent, and a control inside)
83 var secondaryItem = tEl[0].querySelector('.md-secondary');
84 var hasProxiedElement;
87 tEl[0].setAttribute('role', 'listitem');
89 if (!tAttrs.ngClick) {
90 for (var i = 0, type; type = proxiedTypes[i]; ++i) {
91 if (proxyElement = tEl[0].querySelector(type)) {
92 hasProxiedElement = true;
96 if (hasProxiedElement) {
98 } else if (!tEl[0].querySelector('md-button')) {
99 tEl.addClass('md-no-proxy');
107 function setupToggleAria() {
108 var toggleTypes = ['md-switch', 'md-checkbox'];
111 for (var i = 0, toggleType; toggleType = toggleTypes[i]; ++i) {
112 if (toggle = tEl.find(toggleType)[0]) {
113 if (!toggle.hasAttribute('aria-label')) {
114 var p = tEl.find('p')[0];
116 toggle.setAttribute('aria-label', 'Toggle ' + p.textContent);
122 function wrapIn(type) {
125 container = angular.element('<div class="md-no-style md-list-item-inner">');
126 container.append(tEl.contents());
127 tEl.addClass('md-proxy-focus');
129 container = angular.element('<md-button class="md-no-style"><div class="md-list-item-inner"></div></md-button>');
130 var copiedAttrs = ['ng-click', 'aria-label', 'ng-disabled'];
131 angular.forEach(copiedAttrs, function(attr) {
132 if (tEl[0].hasAttribute(attr)) {
133 container[0].setAttribute(attr, tEl[0].getAttribute(attr));
134 tEl[0].removeAttribute(attr);
137 container.children().eq(0).append(tEl.contents());
140 tEl[0].setAttribute('tabindex', '-1');
141 tEl.append(container);
143 if (secondaryItem && secondaryItem.hasAttribute('ng-click')) {
144 $mdAria.expect(secondaryItem, 'aria-label');
145 var buttonWrapper = angular.element('<md-button class="md-secondary-container md-icon-button">');
146 buttonWrapper.attr('ng-click', secondaryItem.getAttribute('ng-click'));
147 secondaryItem.removeAttribute('ng-click');
148 secondaryItem.setAttribute('tabindex', '-1');
149 secondaryItem.classList.remove('md-secondary');
150 buttonWrapper.append(secondaryItem);
151 secondaryItem = buttonWrapper[0];
154 // Check for a secondary item and move it outside
155 if ( secondaryItem && (
156 secondaryItem.hasAttribute('ng-click') ||
158 isProxiedElement(secondaryItem) )
160 tEl.addClass('md-with-secondary');
161 tEl.append(secondaryItem);
165 function isProxiedElement(el) {
166 return proxiedTypes.indexOf(el.nodeName.toLowerCase()) != -1;
171 function postLink($scope, $element, $attr, ctrl) {
174 firstChild = $element[0].firstElementChild,
175 hasClick = firstChild && firstChild.hasAttribute('ng-click');
180 if ($element.hasClass('md-proxy-focus') && proxies.length) {
181 angular.forEach(proxies, function(proxy) {
182 proxy = angular.element(proxy);
184 $scope.mouseActive = false;
185 proxy.on('mousedown', function() {
186 $scope.mouseActive = true;
188 $scope.mouseActive = false;
191 .on('focus', function() {
192 if ($scope.mouseActive === false) { $element.addClass('md-focused'); }
193 proxy.on('blur', function proxyOnBlur() {
194 $element.removeClass('md-focused');
195 proxy.off('blur', proxyOnBlur);
201 function computeProxies() {
202 var children = $element.children();
203 if (children.length && !children[0].hasAttribute('ng-click')) {
204 angular.forEach(proxiedTypes, function(type) {
205 angular.forEach(firstChild.querySelectorAll(type), function(child) {
211 function computeClickable() {
212 if (proxies.length || hasClick) {
213 $element.addClass('md-clickable');
215 ctrl.attachRipple($scope, angular.element($element[0].querySelector('.md-no-style')));
219 if (!hasClick && !proxies.length) {
220 firstChild && firstChild.addEventListener('keypress', function(e) {
221 if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA') {
222 var keyCode = e.which || e.keyCode;
223 if (keyCode == $mdConstant.KEY_CODE.SPACE) {
234 $element.off('click');
235 $element.off('keypress');
237 if (proxies.length && firstChild) {
238 $element.children().eq(0).on('click', function(e) {
239 if (firstChild.contains(e.target)) {
240 angular.forEach(proxies, function(proxy) {
241 if (e.target !== proxy && !proxy.contains(e.target)) {
242 angular.element(proxy).triggerHandler('click');
252 mdListItemDirective.$inject = ["$mdAria", "$mdConstant", "$timeout"];
257 * @name MdListController
258 * @module material.components.list
261 function MdListController($scope, $element, $mdListInkRipple) {
263 ctrl.attachRipple = attachRipple;
265 function attachRipple (scope, element) {
267 $mdListInkRipple.attach(scope, element, options);
270 MdListController.$inject = ["$scope", "$element", "$mdListInkRipple"];
273 ng.material.components.list = angular.module("material.components.list");