From 25cc5e8a9657f1d0731e6f11b4e44ad621bb3486 Mon Sep 17 00:00:00 2001 From: Marcy Sutton Date: Fri, 17 Apr 2015 13:02:09 -0700 Subject: [PATCH] fix(list): add ripple, inherit from .md-button Updates interaction styles of list controls to attach ripple on click of list items with proxy controls, and makes child primary & secondary icon buttons into md-button. This allows for correct dimensions and mouse/focus behavior to be extended to list controls. Closes #2395. --- src/components/button/button-theme.scss | 3 ++ src/components/list/list-theme.scss | 7 ++-- src/components/list/list.js | 43 ++++++++++++++++++++----- src/components/list/list.scss | 14 ++++++-- src/components/list/list.spec.js | 4 +-- src/core/services/ripple/ripple.js | 10 ++++++ 6 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/components/button/button-theme.scss b/src/components/button/button-theme.scss index df19563cede..1f0a859ed9d 100644 --- a/src/components/button/button-theme.scss +++ b/src/components/button/button-theme.scss @@ -12,6 +12,9 @@ a.md-button.md-THEME_NAME-theme, &.md-focused { background-color: '{{background-500-0.2}}'; } + &.md-icon-button:hover { + background-color: transparent; + } } &.md-fab { diff --git a/src/components/list/list-theme.scss b/src/components/list/list-theme.scss index 1966edfaa1f..3ca179f49d7 100644 --- a/src/components/list/list-theme.scss +++ b/src/components/list/list-theme.scss @@ -8,8 +8,7 @@ md-list.md-THEME_NAME-theme { color: '{{foreground-2}}'; } } - .md-proxy-focus.md-focused div.md-no-style, - .md-no-style:focus { + .md-proxy-focus.md-focused div.md-no-style { background-color: '{{background-100}}'; } @@ -25,5 +24,9 @@ md-list.md-THEME_NAME-theme { } md-list-item button { background-color: '{{background-color}}'; + + &.md-button:not([disabled]):hover { + background-color: '{{background-color}}'; + } } } diff --git a/src/components/list/list.js b/src/components/list/list.js index 43993146e83..1d6ae1d818b 100644 --- a/src/components/list/list.js +++ b/src/components/list/list.js @@ -10,6 +10,7 @@ angular.module('material.components.list', [ 'material.core' ]) + .controller('MdListController', MdListController) .directive('mdList', mdListDirective) .directive('mdListItem', mdListItemDirective); @@ -46,7 +47,6 @@ function mdListDirective($mdTheming) { } }; } - /** * @ngdoc directive * @name mdListItem @@ -67,10 +67,11 @@ function mdListDirective($mdTheming) { * * */ -function mdListItemDirective($mdAria, $mdConstant) { +function mdListItemDirective($mdAria, $mdConstant, $timeout) { var proxiedTypes = ['md-checkbox', 'md-switch']; return { restrict: 'E', + controller: 'MdListController', compile: function(tEl, tAttrs) { // Check for proxy controls (no ng-click on parent, and a control inside) var secondaryItem = tEl[0].querySelector('.md-secondary'); @@ -120,7 +121,7 @@ function mdListItemDirective($mdAria, $mdConstant) { container.append(tEl.contents()); tEl.addClass('md-proxy-focus'); } else { - container = angular.element(''); + container = angular.element('
'); container[0].setAttribute('ng-click', tEl[0].getAttribute('ng-click')); tEl[0].removeAttribute('ng-click'); container.children().eq(0).append(tEl.contents()); @@ -131,7 +132,7 @@ function mdListItemDirective($mdAria, $mdConstant) { if (secondaryItem && secondaryItem.hasAttribute('ng-click')) { $mdAria.expect(secondaryItem, 'aria-label'); - var buttonWrapper = angular.element(''); + var buttonWrapper = angular.element(''); buttonWrapper.attr('ng-click', secondaryItem.getAttribute('ng-click')); secondaryItem.removeAttribute('ng-click'); secondaryItem.setAttribute('tabindex', '-1'); @@ -157,7 +158,7 @@ function mdListItemDirective($mdAria, $mdConstant) { return postLink; - function postLink($scope, $element, $attr) { + function postLink($scope, $element, $attr, ctrl) { var proxies = []; @@ -167,8 +168,16 @@ function mdListItemDirective($mdAria, $mdConstant) { if ($element.hasClass('md-proxy-focus') && proxies.length) { angular.forEach(proxies, function(proxy) { proxy = angular.element(proxy); - proxy.on('focus', function() { - $element.addClass('md-focused'); + + $scope.mouseActive = false; + proxy.on('mousedown', function() { + $scope.mouseActive = true; + $timeout(function(){ + $scope.mouseActive = false; + }, 100); + }) + .on('focus', function() { + if ($scope.mouseActive === false) { $element.addClass('md-focused'); } proxy.on('blur', function proxyOnBlur() { $element.removeClass('md-focused'); proxy.off('blur', proxyOnBlur); @@ -189,6 +198,8 @@ function mdListItemDirective($mdAria, $mdConstant) { function computeClickable() { if (proxies.length || $element[0].firstElementChild.hasAttribute('ng-click')) { $element.addClass('md-clickable'); + + ctrl.attachRipple($scope, angular.element($element[0].querySelector('.md-no-style'))); } } @@ -213,7 +224,6 @@ function mdListItemDirective($mdAria, $mdConstant) { angular.forEach(proxies, function(proxy) { if (e.target !== proxy && !proxy.contains(e.target)) { angular.element(proxy).triggerHandler('click'); - proxy.focus(); } }); } @@ -223,4 +233,21 @@ function mdListItemDirective($mdAria, $mdConstant) { } }; } + +/* + * @private + * @ngdoc controller + * @name MdListController + * @module material.components.list + * + */ +function MdListController($scope, $element, $mdInkRipple) { + var ctrl = this; + ctrl.attachRipple = attachRipple; + + function attachRipple (scope, element) { + var options = {}; + $mdInkRipple.attachListControlBehavior(scope, element, options); + } +} })(); diff --git a/src/components/list/list.scss b/src/components/list/list.scss index 40f408ab307..facd3f75729 100644 --- a/src/components/list/list.scss +++ b/src/components/list/list.scss @@ -20,6 +20,7 @@ $list-item-primary-width: $baseline-grid * 7 !default; $list-item-primary-avatar-width: $baseline-grid * 5 !default; $list-item-primary-icon-width: $baseline-grid * 3 !default; $list-item-secondary-left-margin: $baseline-grid * 2 !default; +$list-item-secondary-button-width: $baseline-grid * 6 !default; $list-item-text-padding-top: $baseline-grid * 2 !default; $list-item-text-padding-bottom: $baseline-grid * 2.5 !default; $list-item-inset-divider-offset: 12 * $baseline-grid !default; @@ -40,6 +41,14 @@ md-list-item { position: relative; padding: $list-item-padding-vertical $list-item-padding-horizontal; flex: 1; + transition: background-color 0.15s linear; + + &.md-button { + height: inherit; + text-align: left; + text-transform: none; + width: 100%; + } &:focus { outline: none } @@ -130,11 +139,10 @@ md-list-item, md-list-item .md-list-item-inner { } button.md-button.md-secondary-container { - width: $list-item-primary-icon-width; + background-color: transparent; align-self: center; - margin-right: 0px; - box-sizing: content-box; border-radius: 50%; + margin: 0px; min-width: 0px; .md-ripple, .md-ripple-container { diff --git a/src/components/list/list.spec.js b/src/components/list/list.spec.js index cc9066a2597..7e887a65ed5 100644 --- a/src/components/list/list.spec.js +++ b/src/components/list/list.spec.js @@ -25,7 +25,7 @@ describe('mdListItem directive', function() { it('creates buttons when used with ng-click', function() { var listItem = setup('

Hello world

'); var firstChild = listItem.children()[0]; - expect(firstChild.nodeName).toBe('BUTTON'); + expect(firstChild.nodeName).toBe('MD-BUTTON'); expect(firstChild.childNodes[0].nodeName).toBe('DIV'); expect(firstChild.childNodes[0].childNodes[0].nodeName).toBe('P'); }); @@ -33,7 +33,7 @@ describe('mdListItem directive', function() { it('moves md-secondary items outside of the button', function() { var listItem = setup('

Hello World

'); var firstChild = listItem.children()[0]; - expect(firstChild.nodeName).toBe('BUTTON'); + expect(firstChild.nodeName).toBe('MD-BUTTON'); expect(firstChild.childNodes.length).toBe(1); var secondChild = listItem.children()[1]; expect(secondChild.nodeName).toBe('MD-BUTTON'); diff --git a/src/core/services/ripple/ripple.js b/src/core/services/ripple/ripple.js index 620693fe870..49346fdd298 100644 --- a/src/core/services/ripple/ripple.js +++ b/src/core/services/ripple/ripple.js @@ -27,6 +27,7 @@ function InkRippleService($window, $timeout) { attachButtonBehavior: attachButtonBehavior, attachCheckboxBehavior: attachCheckboxBehavior, attachTabBehavior: attachTabBehavior, + attachListControlBehavior: attachListControlBehavior, attach: attach }; @@ -56,6 +57,15 @@ function InkRippleService($window, $timeout) { }, options)); } + function attachListControlBehavior(scope, element, options) { + return attach(scope, element, angular.extend({ + center: false, + dimBackground: true, + outline: false, + rippleSize: 'full' + }, options)); + } + function attach(scope, element, options) { if (element.controller('mdNoInk')) return angular.noop;