diff --git a/src/components/button/button.js b/src/components/button/button.js index 903bac90099..0f1c0712e10 100644 --- a/src/components/button/button.js +++ b/src/components/button/button.js @@ -106,9 +106,9 @@ function MdButtonDirective($mdInkRipple, $mdTheming, $mdAria, $timeout) { }, 100); }) .on('focus', function() { - if(scope.mouseActive === false) element.addClass('focus'); + if(scope.mouseActive === false) { element.addClass('md-focused'); } }) - .on('blur', function() { element.removeClass('focus'); }); + .on('blur', function() { element.removeClass('md-focused'); }); } } diff --git a/src/components/button/button.scss b/src/components/button/button.scss index 0a0c997cb37..499992f0ab9 100644 --- a/src/components/button/button.scss +++ b/src/components/button/button.scss @@ -150,7 +150,7 @@ $icon-button-margin: 0.600rem !default; &:not([disabled]) { &.md-raised, &.md-fab { - &:focus { + &.md-focused { @extend .md-shadow-bottom-z-1; } &:active { @@ -165,7 +165,7 @@ $icon-button-margin: 0.600rem !default; .md-button.md-fab-top-right { transform: translate3d(0, $button-fab-toast-offset, 0); &:not([disabled]) { - &.focus, + &.md-focused, &:hover { transform: translate3d(0, $button-fab-toast-offset - 1, 0); } @@ -177,7 +177,7 @@ $icon-button-margin: 0.600rem !default; .md-button.md-fab-bottom-right { transform: translate3d(0, -$button-fab-toast-offset, 0); &:not([disabled]) { - &.focus, + &.md-focused, &:hover { transform: translate3d(0, -$button-fab-toast-offset - 1, 0); } @@ -185,7 +185,6 @@ $icon-button-margin: 0.600rem !default; } } - .md-button-group { display: flex; flex: 1; diff --git a/src/components/button/button.spec.js b/src/components/button/button.spec.js index 5f81b9e4742..7689ea76960 100644 --- a/src/components/button/button.spec.js +++ b/src/components/button/button.spec.js @@ -38,16 +38,16 @@ describe('md-button', function() { var button = $compile('')($rootScope.$new()); $rootScope.$apply(); button.triggerHandler('mousedown'); - expect(button[0]).not.toHaveClass('focus'); + expect(button[0]).not.toHaveClass('md-focused'); })); it('should set focus state on focus and remove on blur', inject(function ($compile, $rootScope){ var button = $compile('')($rootScope.$new()); $rootScope.$apply(); button.triggerHandler('focus'); - expect(button[0]).toHaveClass('focus'); + expect(button[0]).toHaveClass('md-focused'); button.triggerHandler('blur'); - expect(button[0]).not.toHaveClass('focus'); + expect(button[0]).not.toHaveClass('md-focused'); })); describe('with href or ng-href', function() { diff --git a/src/components/checkbox/checkbox-theme.scss b/src/components/checkbox/checkbox-theme.scss index 34bd2b87836..09a01e4188a 100644 --- a/src/components/checkbox/checkbox-theme.scss +++ b/src/components/checkbox/checkbox-theme.scss @@ -6,6 +6,9 @@ md-checkbox.md-THEME_NAME-theme { &.md-checked .md-ripple { color: '{{background-600}}'; } + &.md-checked.md-focused .md-container:before { + background-color: '{{accent-color-0.26}}'; + } .md-icon { border-color: '{{foreground-2}}'; @@ -34,6 +37,10 @@ md-checkbox.md-THEME_NAME-theme { background-color: '{{primary-color-0.87}}'; } + &.md-checked.md-focused .md-container:before { + background-color: '{{primary-color-0.26}}'; + } + &.md-checked .md-icon:after { border-color: '{{background-200}}'; } @@ -43,14 +50,15 @@ md-checkbox.md-THEME_NAME-theme { .md-ripple { color: '{{warn-600}}'; } - .md-icon { border-color: '{{foreground-2}}'; } &.md-checked .md-icon { background-color: '{{warn-color-0.87}}'; } - + &.md-checked.md-focused:not([disabled]) .md-container:before { + background-color: '{{warn-color-0.26}}'; + } &.md-checked .md-icon:after { border-color: '{{background-200}}'; } diff --git a/src/components/checkbox/checkbox.js b/src/components/checkbox/checkbox.js index ed5de4a881f..928b16260ee 100644 --- a/src/components/checkbox/checkbox.js +++ b/src/components/checkbox/checkbox.js @@ -50,7 +50,7 @@ angular.module('material.components.checkbox', [ * * */ -function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, $mdTheming, $mdUtil) { +function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, $mdTheming, $mdUtil, $timeout) { inputDirective = inputDirective[0]; var CHECKED_CSS = 'md-checked'; @@ -98,13 +98,28 @@ function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant, 0: {} }, attr, [ngModelCtrl]); - element.on('click', listener) - .on('keypress', keypressHandler); + scope.mouseActive = false; + element + .on('click', listener) + .on('keypress', keypressHandler) + .on('mousedown', function() { + scope.mouseActive = true; + $timeout(function(){ + scope.mouseActive = false; + }, 100); + }) + .on('focus', function() { + if(scope.mouseActive === false) { element.addClass('md-focused'); } + }) + .on('blur', function() { element.removeClass('md-focused'); }); + ngModelCtrl.$render = render; function keypressHandler(ev) { - if(ev.which === $mdConstant.KEY_CODE.SPACE || ev.which === $mdConstant.KEY_CODE.ENTER) { + var keyCode = ev.which || ev.keyCode; + if (keyCode === $mdConstant.KEY_CODE.SPACE || keyCode === $mdConstant.KEY_CODE.ENTER) { ev.preventDefault(); + if (!element.hasClass('md-focused')) { element.addClass('md-focused'); } listener(ev); } } diff --git a/src/components/checkbox/checkbox.scss b/src/components/checkbox/checkbox.scss index dc120583e2b..95533488a80 100644 --- a/src/components/checkbox/checkbox.scss +++ b/src/components/checkbox/checkbox.scss @@ -16,6 +16,21 @@ md-checkbox { box-sizing: border-box; } + &.md-focused:not([disabled]) { + .md-container:before { + left: -8px; + top: -8px; + right: -8px; + bottom: -8px; + } + + &:not(.md-checked) { + .md-container:before { + background-color: rgba(0, 0, 0, 0.12); + } + } + } + .md-container { position: relative; top: 4px; @@ -23,6 +38,21 @@ md-checkbox { width: $checkbox-width; height: $checkbox-height; + &:before { + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all 0.5s; + width: auto; + } + &:after { content: ''; position: absolute; @@ -65,11 +95,6 @@ md-checkbox { cursor: no-drop; } - // focus - &:focus .md-label:not(:empty) { - border-color: black; - } - &.md-checked .md-icon:after { transform: rotate(45deg); diff --git a/src/components/checkbox/checkbox.spec.js b/src/components/checkbox/checkbox.spec.js index 964b2a7087b..b1ada47dba7 100644 --- a/src/components/checkbox/checkbox.spec.js +++ b/src/components/checkbox/checkbox.spec.js @@ -67,8 +67,33 @@ describe('mdCheckbox', function() { checkbox.triggerHandler('click'); expect($rootScope.blue).toBe(true); + })); + it('should not set focus state on mousedown', inject(function($compile, $rootScope) { + var checkbox = $compile('')($rootScope.$new()); + $rootScope.$apply(); + checkbox.triggerHandler('mousedown'); + expect(checkbox[0]).not.toHaveClass('md-focused'); + })); + it('should set focus state on focus and remove on blur', inject(function($compile, $rootScope) { + var checkbox = $compile('')($rootScope.$new()); + $rootScope.$apply(); + checkbox.triggerHandler('focus'); + expect(checkbox[0]).toHaveClass('md-focused'); + checkbox.triggerHandler('blur'); + expect(checkbox[0]).not.toHaveClass('md-focused'); + })); + + it('should set focus state on keyboard interaction after clicking', inject(function($compile, $rootScope, $mdConstant) { + var checkbox = $compile('')($rootScope.$new()); + $rootScope.$apply(); + checkbox.triggerHandler('mousedown'); + checkbox.triggerHandler({ + type: 'keypress', + keyCode: $mdConstant.KEY_CODE.SPACE + }); + expect(checkbox[0]).toHaveClass('md-focused'); })); describe('ng core checkbox tests', function() { diff --git a/src/components/list/list-theme.scss b/src/components/list/list-theme.scss index 51b5a03bf02..1966edfaa1f 100644 --- a/src/components/list/list-theme.scss +++ b/src/components/list/list-theme.scss @@ -9,7 +9,6 @@ md-list.md-THEME_NAME-theme { } } .md-proxy-focus.md-focused div.md-no-style, - .md-secondary:focus, .md-no-style:focus { background-color: '{{background-100}}'; } diff --git a/src/components/radioButton/radio-button-theme.scss b/src/components/radioButton/radio-button-theme.scss index 42db7332dca..48581543b35 100644 --- a/src/components/radioButton/radio-button-theme.scss +++ b/src/components/radioButton/radio-button-theme.scss @@ -80,6 +80,14 @@ md-radio-button.md-THEME_NAME-theme { } -md-radio-group.md-THEME_NAME-theme:focus:not(:empty) { - border-color: '{{foreground-1}}'; +md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) { + .md-checked .md-container:before { + background-color: '{{accent-color-0.26}}'; + } + .md-checked:not([disabled]).md-primary .md-container:before { + background-color: '{{primary-color-0.26}}'; + } + .md-checked.md-primary .md-container:before { + background-color: '{{warn-color-0.26}}'; + } } diff --git a/src/components/radioButton/radio-button.scss b/src/components/radioButton/radio-button.scss index f65a6ff40e9..b08ddf93ded 100644 --- a/src/components/radioButton/radio-button.scss +++ b/src/components/radioButton/radio-button.scss @@ -35,6 +35,21 @@ md-radio-button, left: -$radio-width; top: -$radio-width; } + + &:before { + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all 0.5s; + width: auto; + } } @@ -81,14 +96,17 @@ md-radio-button, } md-radio-group { - border: 1px dotted transparent; - display: block; - outline: none; -} - - -.radioButtondemoBasicUsage md-radio-group { - border: none; + &:focus { + outline: none; + } + &.md-focused { + .md-checked .md-container:before { + left: -8px; + top: -8px; + right: -8px; + bottom: -8px; + } + } } @media screen and (-ms-high-contrast: active) { diff --git a/src/components/radioButton/radioButton.js b/src/components/radioButton/radioButton.js index 91810bd1a80..00a7612114a 100644 --- a/src/components/radioButton/radioButton.js +++ b/src/components/radioButton/radioButton.js @@ -53,7 +53,7 @@ angular.module('material.components.radioButton', [ * * */ -function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming) { +function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) { RadioGroupController.prototype = createRadioGroupControllerProto(); return { @@ -68,18 +68,25 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming) { var rgCtrl = ctrls[0]; var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); + function setFocus() { + if (!element.hasClass('md-focused')) { element.addClass('md-focused'); } + } + function keydownListener(ev) { - switch(ev.keyCode) { + var keyCode = ev.which || ev.keyCode; + switch(keyCode) { case $mdConstant.KEY_CODE.LEFT_ARROW: case $mdConstant.KEY_CODE.UP_ARROW: ev.preventDefault(); rgCtrl.selectPrevious(); + setFocus(); break; case $mdConstant.KEY_CODE.RIGHT_ARROW: case $mdConstant.KEY_CODE.DOWN_ARROW: ev.preventDefault(); rgCtrl.selectNext(); + setFocus(); break; case $mdConstant.KEY_CODE.ENTER: @@ -93,12 +100,22 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming) { rgCtrl.init(ngModelCtrl); + scope.mouseActive = false; element.attr({ 'role': 'radiogroup', 'tabIndex': element.attr('tabindex') || '0' }) - .on('keydown', keydownListener); - + .on('keydown', keydownListener) + .on('mousedown', function(event) { + scope.mouseActive = true; + $timeout(function() { + scope.mouseActive = false; + }, 100); + }) + .on('focus', function() { + if(scope.mouseActive === false) { rgCtrl.$element.addClass('md-focused'); } + }) + .on('blur', function() { rgCtrl.$element.removeClass('md-focused'); }); } function RadioGroupController($element) { diff --git a/src/components/radioButton/radioButton.spec.js b/src/components/radioButton/radioButton.spec.js index 183ebd9c34b..0cee983330c 100644 --- a/src/components/radioButton/radioButton.spec.js +++ b/src/components/radioButton/radioButton.spec.js @@ -112,6 +112,42 @@ describe('radioButton', function() { expect($rootScope.color).toEqual('green'); })); + it('should not set focus state on mousedown', inject(function($compile, $rootScope) { + var element = $compile('' + + '' + + '' + + '')($rootScope); + $rootScope.$apply(); + element.triggerHandler('mousedown'); + expect(element[0]).not.toHaveClass('md-focused'); + })); + + it('should set focus state on focus and remove on blur', inject(function($compile, $rootScope) { + var element = $compile('' + + '' + + '' + + '')($rootScope); + $rootScope.$apply(); + element.triggerHandler('focus'); + expect(element[0]).toHaveClass('md-focused'); + element.triggerHandler('blur'); + expect(element[0]).not.toHaveClass('md-focused'); + })); + + it('should set focus state on keyboard interaction after clicking', inject(function($compile, $rootScope, $mdConstant) { + var element = $compile('' + + '' + + '' + + '')($rootScope); + $rootScope.$apply(); + element.triggerHandler('mousedown'); + element.triggerHandler({ + type: 'keydown', + keyCode: $mdConstant.KEY_CODE.DOWN_ARROW + }); + expect(element[0]).toHaveClass('md-focused'); + })); + describe('ng core radio button tests', function() { it('should noop with no model', inject(function($compile, $rootScope) { diff --git a/src/components/switch/demoBasicUsage/index.html b/src/components/switch/demoBasicUsage/index.html index e3c3e321517..f6229810410 100644 --- a/src/components/switch/demoBasicUsage/index.html +++ b/src/components/switch/demoBasicUsage/index.html @@ -15,12 +15,11 @@ Switch (Disabled, Active) - + Switch (md-primary): No Ink - - - - Switch 5 message: {{ message }} + + + Switch 6 message: {{ message }} diff --git a/src/components/switch/demoBasicUsage/script.js b/src/components/switch/demoBasicUsage/script.js index 1e6c4405578..1ddae820000 100644 --- a/src/components/switch/demoBasicUsage/script.js +++ b/src/components/switch/demoBasicUsage/script.js @@ -2,10 +2,11 @@ angular.module('switchDemo1', ['ngMaterial']) .controller('SwitchDemoCtrl', function($scope) { $scope.data = { cb1: true, - cb4: true + cb4: true, + cb5: false }; - - $scope.onChange = function(cbState){ - $scope.message = "The switch is now: " + cbState; + + $scope.onChange = function(cbState) { + $scope.message = "The switch is now: " + cbState; }; }); diff --git a/src/components/switch/switch-theme.scss b/src/components/switch/switch-theme.scss index f0098526f4b..f5f3dcbf43d 100644 --- a/src/components/switch/switch-theme.scss +++ b/src/components/switch/switch-theme.scss @@ -13,6 +13,9 @@ md-switch.md-THEME_NAME-theme { .md-bar { background-color: '{{accent-color-0.5}}'; } + &.md-focused .md-thumb:before { + background-color: '{{accent-color-0.26}}'; + } &.md-primary { .md-thumb { @@ -21,6 +24,9 @@ md-switch.md-THEME_NAME-theme { .md-bar { background-color: '{{primary-color-0.5}}'; } + &.md-focused .md-thumb:before { + background-color: '{{primary-color-0.26}}'; + } } &.md-warn { @@ -30,6 +36,9 @@ md-switch.md-THEME_NAME-theme { .md-bar { background-color: '{{warn-color-0.5}}'; } + &.md-focused .md-thumb:before { + background-color: '{{warn-color-0.26}}'; + } } } @@ -41,12 +50,4 @@ md-switch.md-THEME_NAME-theme { background-color: '{{foreground-4}}'; } } - - &:focus { - .md-label:not(:empty) { - border-color: '{{foreground-1}}'; - border-style: dotted; - } - } - } diff --git a/src/components/switch/switch.scss b/src/components/switch/switch.scss index 84ed60f34a0..878b62f7b83 100644 --- a/src/components/switch/switch.scss +++ b/src/components/switch/switch.scss @@ -31,6 +31,21 @@ md-switch { } } + &.md-focused:not([disabled]) { + .md-thumb:before { + left: -8px; + top: -8px; + right: -8px; + bottom: -8px; + } + + &:not(.md-checked) { + .md-thumb:before { + background-color: rgba(0, 0, 0, 0.12); + } + } + } + .md-label { border-color: transparent; border-width: 0px; @@ -68,6 +83,21 @@ md-switch { border-radius: 50%; box-shadow: $whiteframe-shadow-z1; + &:before { + background-color: transparent; + border-radius: 50%; + content: ''; + position: absolute; + display: block; + height: auto; + left: 0; + top: 0; + right: 0; + bottom: 0; + transition: all 0.5s; + width: auto; + } + .md-ripple-container { position: absolute; display: block;