diff --git a/docs/src/pages/components/Sidenav.vue b/docs/src/pages/components/Sidenav.vue
index 21759f1..a03695a 100644
--- a/docs/src/pages/components/Sidenav.vue
+++ b/docs/src/pages/components/Sidenav.vue
@@ -7,6 +7,36 @@
+
+
+
+ Name
+ Type
+ Description
+
+
+
+
+
+ md-swipeable
+ Boolean
+ Enable the swipe functionality. Default false
+
+
+
+ md-swipe-threshold
+ Number
+ Set the initial threshold for the swipe when it's closed. Default 15
+
+
+
+ md-swipe-distance
+ Number
+ Set the swipe distance needed to open/close the sidenav. Default 100
+
+
+
+
@@ -102,7 +132,7 @@
Open console to see the events
-
+
Sidenav content
diff --git a/src/components/mdSidenav/mdSidenav.vue b/src/components/mdSidenav/mdSidenav.vue
index e678e21..b1e79c6 100644
--- a/src/components/mdSidenav/mdSidenav.vue
+++ b/src/components/mdSidenav/mdSidenav.vue
@@ -4,7 +4,7 @@
-
+
@@ -20,6 +20,17 @@
};
},
mixins: [theme],
+ props: {
+ mdSwipeable: Boolean,
+ mdSwipeThreshold: {
+ type: Number,
+ default: 15
+ },
+ mdSwipeDistance: {
+ type: Number,
+ default: 100
+ }
+ },
computed: {
classes() {
return this.mdVisible && 'md-active';
@@ -45,7 +56,80 @@
} else {
this.open();
}
+ },
+ isHorizontallyInside(positionX) {
+ return positionX > 0 && positionX < this.mountedRect.left + this.mountedRect.width;
+ },
+ isVerticallyInside(positionY) {
+ return positionY > 0 && positionY < this.mountedRect.top + this.mountedRect.height;
+ },
+ isFromStartWhenClosed(positionX) {
+ if (this.mdVisible) {
+ return true;
+ }
+
+ return positionX < this.mdSwipeThreshold;
+ },
+ handleTouchStart(event) {
+ const positionX = event.touches[0].clientX - this.mountedRect.left;
+ const positionY = event.touches[0].clientY - this.mountedRect.top;
+
+ if (
+ !this.isHorizontallyInside(positionX) ||
+ !this.isVerticallyInside(positionY) ||
+ !this.isFromStartWhenClosed(positionX)
+ ) {
+ return;
+ }
+
+ this.initialTouchPosition = positionX;
+ this.canMove = true;
+ },
+ handleTouchEnd() {
+ this.canMove = false;
+ this.initialTouchPosition = null;
+ },
+ handleTouchMove(event) {
+ if (!this.canMove) {
+ return;
+ }
+
+ const positionX = event.touches[0].clientX;
+
+ const difference = this.mdVisible
+ ? this.initialTouchPosition - positionX
+ : positionX - this.initialTouchPosition;
+
+ const action = this.mdVisible
+ ? 'close'
+ : 'open';
+
+ if (difference > this.mdSwipeDistance) {
+ this[action]();
+ }
}
+ },
+ mounted() {
+ if (!this.mdSwipeable) {
+ return;
+ }
+
+ this.mountedRect = this.$refs.backdrop.$el.getBoundingClientRect();
+ this.initialTouchPosition = null;
+ this.canMove = false;
+
+ document.addEventListener('touchstart', this.handleTouchStart);
+ document.addEventListener('touchend', this.handleTouchEnd);
+ document.addEventListener('touchmove', this.handleTouchMove);
+ },
+ beforeDestroy() {
+ if (!this.mdSwipeable) {
+ return;
+ }
+
+ document.removeEventListener('touchstart', this.handleTouchStart);
+ document.removeEventListener('touchend', this.handleTouchEnd);
+ document.removeEventListener('touchmove', this.handleTouchMove);
}
};