Skip to content

Commit

Permalink
feat(sidenav): add gestures to sidenav to allow dragging the sidenav
Browse files Browse the repository at this point in the history
  • Loading branch information
devversion committed Jan 1, 2016
1 parent f6c3b46 commit 550a200
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 1 deletion.
73 changes: 73 additions & 0 deletions src/components/sidenav/demoSidenavDragging/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

<div ng-controller="AppCtrl" layout="column" style="height:500px;" ng-cloak>

<section layout="row" flex>

<md-sidenav class="md-sidenav-left md-whiteframe-z2" md-component-id="left">

<md-toolbar class="md-theme-light">
<h1 class="md-toolbar-tools">Drag Enabled</h1>
</md-toolbar>
<md-content ng-controller="LeftCtrl" layout-padding>
<p>
This sidenav is able to be dragged. Through the drag gesture, it's possible to close the sidenav.
</p>
<md-button ng-click="close()" class="md-primary">
Close Sidenav Left
</md-button>
</md-content>

</md-sidenav>

<md-content flex layout-padding>

<div layout="column" layout-fill layout-align="top center">
<p>
You can simply close a sidenav by using a gesture. Simply drag the sidenav to close it.
</p>
<p>
The right sidenav is disabled for dragging.
</p>
<p>
The left sidenav allows dragging.
</p>

<div>
<md-button ng-click="toggleLeft()"
class="md-primary">
Toggle left
</md-button>
</div>

<div>
<md-button ng-click="toggleRight()"
ng-hide="isOpenRight()"
class="md-primary">
Toggle right
</md-button>
</div>
</div>

<div flex></div>

</md-content>

<md-sidenav class="md-sidenav-right md-whiteframe-z2" md-component-id="right" md-disable-drag="true">

<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">Drag Disabled</h1>
</md-toolbar>
<md-content layout-padding ng-controller="RightCtrl">
<md-button ng-click="close()" class="md-primary">
Close Sidenav Right
</md-button>
<p>
This sidenav disables the dragging gesture. It can be closed through the buttons.
</p>
</md-content>

</md-sidenav>

</section>

</div>
36 changes: 36 additions & 0 deletions src/components/sidenav/demoSidenavDragging/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
angular
.module('sidenavDemo1', ['ngMaterial'])
.controller('AppCtrl', function ($scope, $timeout, $mdSidenav, $log) {
$scope.toggleLeft = buildToggler('left');
$scope.toggleRight = buildToggler('right');
$scope.isOpenRight = function(){
return $mdSidenav('right').isOpen();
};

function buildToggler(navID) {
return function() {
$mdSidenav(navID)
.toggle()
.then(function () {
$log.debug("toggle " + navID + " is done");
});
}
}
})
.controller('LeftCtrl', function ($scope, $timeout, $mdSidenav, $log) {
$scope.close = function () {
$mdSidenav('left').close()
.then(function () {
$log.debug("close LEFT is done");
});

};
})
.controller('RightCtrl', function ($scope, $timeout, $mdSidenav, $log) {
$scope.close = function () {
$mdSidenav('right').close()
.then(function () {
$log.debug("close RIGHT is done");
});
};
});
99 changes: 98 additions & 1 deletion src/components/sidenav/sidenav.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ function SidenavFocusDirective() {
*
* @param {expression=} md-is-open A model bound to whether the sidenav is opened.
* @param {string=} md-component-id componentId to use with $mdSidenav service.
* @param {boolean=} md-disable-drag Disables the abbility to drag the sidenav
* @param {expression=} md-is-locked-open When this expression evalutes to true,
* the sidenav 'locks open': it falls into the content's flow instead
* of appearing over it. This overrides the `is-open` attribute.
Expand All @@ -208,7 +209,7 @@ function SidenavFocusDirective() {
* - `<md-sidenav md-is-locked-open="$mdMedia('min-width: 1000px')"></md-sidenav>`
* - `<md-sidenav md-is-locked-open="$mdMedia('sm')"></md-sidenav>` (locks open on small screens)
*/
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $compile, $parse, $log, $q, $document) {
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $mdGesture, $parse, $log, $q, $document, $timeout) {
return {
restrict: 'E',
scope: {
Expand Down Expand Up @@ -256,6 +257,8 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
scope.$watch(isLocked, updateIsLocked);
scope.$watch('isOpen', updateIsOpen);

// Enable dragging
if (!angular.isDefined(attr.mdDisableDrag) || !attr.mdDisableDrag) enableDragging();

// Publish special accessor for the Controller instance
sidenavCtrl.$toggleOpen = toggleOpen;
Expand Down Expand Up @@ -323,6 +326,100 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
}
}

function enableDragging() {
$mdGesture.register(element, 'drag', { horizontal: true });

element
.on('$md.dragstart', onDragStart)
.on('$md.drag', onDrag)
.on('$md.dragend', onDragEnd);

var style = getComputedStyle(element[0]);
var sidenavWidth = parseInt(style.width);
var isRightSidenav = element.hasClass('md-sidenav-right');
var accelerationBound = 10;

var dragCancelled = false;
var dragPercentage;
var lastOpenState;
var lastDistance = 0;
var isQuickDrag = false;

function onDragStart() {
if (element.hasClass('md-locked-open')) {
dragCancelled = true;
} else {
lastOpenState = scope.isOpen;
element.css($mdConstant.CSS.TRANSITION_DURATION, '0ms');
}
}

function onDrag(ev) {
if (dragCancelled) return;

if (!isQuickDrag) {
var distance = lastDistance - ev.pointer.distanceX;
if (isRightSidenav) {
isQuickDrag = distance <= -accelerationBound;
} else {
isQuickDrag = distance= accelerationBound;
}
} else {
if (isRightSidenav && lastDistance > ev.pointer.distanceX) {
isQuickDrag = false;
} else if (!isRightSidenav && lastDistance < ev.pointer.distanceX) {
isQuickDrag = false;
}
}

dragPercentage = Math.round((ev.pointer.distanceX / sidenavWidth) * 100);
if (!isRightSidenav) dragPercentage = 0 - dragPercentage;

if (dragPercentage > 100) dragPercentage = 100;
else if (dragPercentage < 0) dragPercentage = 0;

element.css($mdConstant.CSS.TRANSFORM, 'translate3d(-' + (isRightSidenav ? 100 - dragPercentage : dragPercentage) + '%,0,0)');
lastDistance = ev.pointer.distanceX;
}

function onDragEnd() {
if (dragCancelled) {
dragCancelled = false;
return;
}

var remainingPercentage = 100 - dragPercentage;
var animationTime = 4 * remainingPercentage;
var isOpen = dragPercentage > 50 || isQuickDrag;
if (isRightSidenav) isOpen = !isOpen;

element.css($mdConstant.CSS.TRANSITION_DURATION, animationTime + "ms");

element.css($mdConstant.CSS.TRANSFORM, 'translate3d(' + (isOpen ? -100 : 0) + '%,0,0)');

// Reset drag
lastDistance = 0;
isQuickDrag = false;
$timeout(onAnimationDone, animationTime, true, (isRightSidenav ? isOpen : !isOpen));
}

function onAnimationDone(isOpen) {
scope.isOpen = isOpen;
element.css($mdConstant.CSS.TRANSFORM, '');
element.css($mdConstant.CSS.TRANSITION_DURATION, '');

if (isOpen) {
if (!lastOpenState) {
$animate.enter(backdrop, element.parent());
}
element.removeClass('md-closed');
} else {
$animate.leave(backdrop);
element.addClass('md-closed');
}
}
}

/**
* Toggle the sideNav view and publish a promise to be resolved when
* the view animation finishes.
Expand Down

0 comments on commit 550a200

Please sign in to comment.