From 65c1ff84537b77b2be25d399c059b565fd0b2443 Mon Sep 17 00:00:00 2001 From: Igor Ribeiro Date: Tue, 7 Feb 2017 00:37:47 -0200 Subject: [PATCH] enable swipe to open or close sidenav (#429) * implement swipe on Sidenav * update the docs * prefix props with md- and update from destroyed to beforeDestroy * refactor to clarify logic --- docs/src/pages/components/Sidenav.vue | 32 +++++++++- src/components/mdSidenav/mdSidenav.vue | 86 +++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) 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); } };