Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 7f45663

Browse files
committed
feat(interaction): added service to detect last interaction
Sidenav want's to restore focus after the pane gets closed, this should only happen for keyboard interactions. The service got created to use that feature for other components too Fixes #5563 References #5583 feat(interaction): add test for interaction which fakes a keyboard input and validates it test(interaction): add interaction spec for mousedown event + formats 4 spaces to 2 style(interaction): fix styling of the previous commits
1 parent 5ae3d4c commit 7f45663

File tree

4 files changed

+90
-3
lines changed

4 files changed

+90
-3
lines changed

src/components/sidenav/sidenav.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function SidenavFocusDirective() {
208208
* - `<md-sidenav md-is-locked-open="$mdMedia('min-width: 1000px')"></md-sidenav>`
209209
* - `<md-sidenav md-is-locked-open="$mdMedia('sm')"></md-sidenav>` (locks open on small screens)
210210
*/
211-
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $compile, $parse, $log, $q, $document) {
211+
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $mdInteraction, $animate, $compile, $parse, $log, $q, $document) {
212212
return {
213213
restrict: 'E',
214214
scope: {
@@ -227,6 +227,7 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
227227
*/
228228
function postLink(scope, element, attr, sidenavCtrl) {
229229
var lastParentOverFlow;
230+
var triggeringInteractionType;
230231
var triggeringElement = null;
231232
var promise = $q.when(true);
232233

@@ -289,6 +290,7 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
289290
if ( isOpen ) {
290291
// Capture upon opening..
291292
triggeringElement = $document[0].activeElement;
293+
triggeringInteractionType = $mdInteraction.getLastInteractionType();
292294
}
293295

294296
disableParentScroll(isOpen);
@@ -344,9 +346,9 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
344346
// When the current `updateIsOpen()` animation finishes
345347
promise.then(function(result) {
346348

347-
if ( !scope.isOpen ) {
349+
if ( !scope.isOpen && triggeringElement && triggeringInteractionType && triggeringInteractionType === 'keyboard') {
348350
// reset focus to originating element (if available) upon close
349-
triggeringElement && triggeringElement.focus();
351+
triggeringElement.focus();
350352
triggeringElement = null;
351353
}
352354

src/core/core.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ angular
77
'ngAnimate',
88
'material.core.animate',
99
'material.core.layout',
10+
'material.core.interaction',
1011
'material.core.gestures',
1112
'material.core.theming'
1213
])
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
angular
2+
.module('material.core.interaction', [])
3+
.service('$mdInteraction', MdInteractionService);
4+
5+
function MdInteractionService($timeout) {
6+
var body = angular.element(document.body);
7+
var _mouseEvent = window.MSPointerEvent ? 'MSPointerDown' : window.PointerEvent ? 'pointerdown' : 'mousedown';
8+
var buffer = false;
9+
var timer;
10+
var lastInteractionType;
11+
var inputMap = {
12+
'keydown': 'keyboard',
13+
'mousedown': 'mouse',
14+
'mouseenter': 'mouse',
15+
'touchstart': 'touch',
16+
'pointerdown': 'pointer',
17+
'MSPointerDown': 'pointer'
18+
};
19+
var pointerMap = {
20+
2: 'touch',
21+
3: 'touch',
22+
4: 'mouse'
23+
};
24+
25+
function onInput(event) {
26+
if (buffer) return;
27+
var type = inputMap[event.type];
28+
if (type === 'pointer') {
29+
type = (typeof event.pointerType === 'number') ? pointerMap[event.pointerType] : event.pointerType;
30+
}
31+
lastInteractionType = type;
32+
}
33+
34+
function onBufferInput(event) {
35+
$timeout.cancel(timer);
36+
37+
onInput(event);
38+
buffer = true;
39+
40+
timer = $timeout(function() {
41+
buffer = false;
42+
}, 1000);
43+
}
44+
45+
body.on('keydown', onInput);
46+
body.on(_mouseEvent, onInput);
47+
body.on('mouseenter', onInput);
48+
if ('ontouchstart' in document.documentElement) body.on('touchstart', onBufferInput);
49+
50+
/**
51+
* Gets the last interaction type triggered by body.
52+
* Possible values for return are `mouse`, `keyboard` and `touch`
53+
* @returns {string}
54+
*/
55+
this.getLastInteractionType = function() {
56+
return lastInteractionType;
57+
}
58+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
describe("$mdInteraction", function() {
2+
beforeEach(module('material.core'));
3+
4+
describe("last interaction type", function() {
5+
6+
it("imitates a basic keyboard interaction and checks it", inject(function($mdInteraction) {
7+
8+
var event = document.createEvent('Event');
9+
event.keyCode = 37;
10+
event.initEvent('keydown', false, true);
11+
document.body.dispatchEvent(event);
12+
13+
expect($mdInteraction.getLastInteractionType()).toBe('keyboard');
14+
}));
15+
16+
it("dispatches a mousedown event on the document body and checks it", inject(function($mdInteraction) {
17+
18+
var event = document.createEvent("MouseEvent");
19+
event.initMouseEvent("mousedown", true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null);
20+
document.body.dispatchEvent(event);
21+
22+
expect($mdInteraction.getLastInteractionType()).toBe("mouse");
23+
}));
24+
25+
});
26+
});

0 commit comments

Comments
 (0)