Move slideout menu logic to Vue.

This commit is contained in:
Richard Lewis 2019-08-05 15:24:44 +03:00 committed by Pavel Djundik
parent ee92de0ff7
commit 2b602ca333
5 changed files with 107 additions and 122 deletions

View File

@ -49,6 +49,7 @@ export default {
return { return {
notified: this.$store.state.isNotified, notified: this.$store.state.isNotified,
"menu-open": this.$store.state.sidebarOpen, "menu-open": this.$store.state.sidebarOpen,
"menu-dragging": this.$store.state.sidebarDragging,
"userlist-open": this.$store.state.userlistOpen, "userlist-open": this.$store.state.userlistOpen,
}; };
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<aside id="sidebar"> <aside id="sidebar" ref="sidebar">
<div class="scrollable-area"> <div class="scrollable-area">
<div class="logo-container"> <div class="logo-container">
<img <img
@ -63,7 +63,7 @@
/></span> /></span>
</footer> </footer>
</aside> </aside>
<div id="sidebar-overlay" @click="$root.setSidebar(false)" /> <div id="sidebar-overlay" ref="overlay" @click="$root.setSidebar(false)" />
</div> </div>
</template> </template>
@ -79,6 +79,106 @@ export default {
activeChannel: Object, activeChannel: Object,
networks: Array, networks: Array,
}, },
mounted() {
this.touchStartPos = null;
this.touchCurPos = null;
this.touchStartTime = 0;
this.menuWidth = 0;
this.menuIsMoving = false;
this.menuIsAbsolute = false;
this.onTouchStart = (e) => {
this.touchStartPos = this.touchCurPos = e.touches.item(0);
if (e.touches.length !== 1) {
this.onTouchEnd();
return;
}
const styles = window.getComputedStyle(this.$refs.sidebar);
this.menuWidth = parseFloat(styles.width);
this.menuIsAbsolute = styles.position === "absolute";
if (!this.$store.state.sidebarOpen || this.touchStartPos.screenX > this.menuWidth) {
this.touchStartTime = Date.now();
document.body.addEventListener("touchmove", this.onTouchMove, {passive: true});
document.body.addEventListener("touchend", this.onTouchEnd, {passive: true});
}
};
this.onTouchMove = (e) => {
const touch = (this.touchCurPos = e.touches.item(0));
let distX = touch.screenX - this.touchStartPos.screenX;
const distY = touch.screenY - this.touchStartPos.screenY;
if (!this.menuIsMoving) {
// tan(45°) is 1. Gestures in 0°-45° (< 1) are considered horizontal, so
// menu must be open; gestures in 45°-90° (>1) are considered vertical, so
// chat windows must be scrolled.
if (Math.abs(distY / distX) >= 1) {
this.onTouchEnd();
return;
}
const devicePixelRatio = window.devicePixelRatio || 2;
if (Math.abs(distX) > devicePixelRatio) {
this.$store.commit("sidebarDragging", true);
this.menuIsMoving = true;
}
}
// Do not animate the menu on desktop view
if (!this.menuIsAbsolute) {
return;
}
if (this.$store.state.sidebarOpen) {
distX += this.menuWidth;
}
if (distX > this.menuWidth) {
distX = this.menuWidth;
} else if (distX < 0) {
distX = 0;
}
this.$refs.sidebar.style.transform = "translate3d(" + distX + "px, 0, 0)";
this.$refs.overlay.style.opacity = distX / this.menuWidth;
};
this.onTouchEnd = () => {
const diff = this.touchCurPos.screenX - this.touchStartPos.screenX;
const absDiff = Math.abs(diff);
if (
absDiff > this.menuWidth / 2 ||
(Date.now() - this.touchStartTime < 180 && absDiff > 50)
) {
this.toggle(diff > 0);
}
document.body.removeEventListener("touchmove", this.onTouchMove);
document.body.removeEventListener("touchend", this.onTouchEnd);
this.$store.commit("sidebarDragging", false);
this.$refs.sidebar.style.transform = null;
this.$refs.overlay.style.opacity = null;
this.touchStartPos = null;
this.touchCurPos = null;
this.touchStartTime = 0;
this.menuIsMoving = false;
};
this.toggle = (state) => {
this.$store.commit("sidebarOpen", state);
};
document.body.addEventListener("touchstart", this.onTouchStart, {passive: true});
},
methods: { methods: {
isPublic: () => document.body.classList.contains("public"), isPublic: () => document.body.classList.contains("public"),
}, },

View File

@ -1,115 +0,0 @@
"use strict";
class SlideoutMenu {
enable() {
this.viewport = document.getElementById("viewport");
this.menu = document.getElementById("sidebar");
this.sidebarOverlay = document.getElementById("sidebar-overlay");
this.touchStartPos = null;
this.touchCurPos = null;
this.touchStartTime = 0;
this.menuWidth = 0;
this.menuIsOpen = false;
this.menuIsMoving = false;
this.menuIsAbsolute = false;
this.onTouchStart = (e) => {
this.touchStartPos = this.touchCurPos = e.touches.item(0);
if (e.touches.length !== 1) {
this.onTouchEnd();
return;
}
const styles = window.getComputedStyle(this.menu);
this.menuWidth = parseFloat(styles.width);
this.menuIsAbsolute = styles.position === "absolute";
if (!this.menuIsOpen || this.touchStartPos.screenX > this.menuWidth) {
this.touchStartTime = Date.now();
document.body.addEventListener("touchmove", this.onTouchMove, {passive: true});
document.body.addEventListener("touchend", this.onTouchEnd, {passive: true});
}
};
this.onTouchMove = (e) => {
const touch = (this.touchCurPos = e.touches.item(0));
let distX = touch.screenX - this.touchStartPos.screenX;
const distY = touch.screenY - this.touchStartPos.screenY;
if (!this.menuIsMoving) {
// tan(45°) is 1. Gestures in 0°-45° (< 1) are considered horizontal, so
// menu must be open; gestures in 45°-90° (>1) are considered vertical, so
// chat windows must be scrolled.
if (Math.abs(distY / distX) >= 1) {
this.onTouchEnd();
return;
}
const devicePixelRatio = window.devicePixelRatio || 2;
if (Math.abs(distX) > devicePixelRatio) {
this.viewport.classList.toggle("menu-dragging", true);
this.menuIsMoving = true;
}
}
// Do not animate the menu on desktop view
if (!this.menuIsAbsolute) {
return;
}
if (this.menuIsOpen) {
distX += this.menuWidth;
}
if (distX > this.menuWidth) {
distX = this.menuWidth;
} else if (distX < 0) {
distX = 0;
}
this.menu.style.transform = "translate3d(" + distX + "px, 0, 0)";
this.sidebarOverlay.style.opacity = distX / this.menuWidth;
};
this.onTouchEnd = () => {
const diff = this.touchCurPos.screenX - this.touchStartPos.screenX;
const absDiff = Math.abs(diff);
if (
absDiff > this.menuWidth / 2 ||
(Date.now() - this.touchStartTime < 180 && absDiff > 50)
) {
this.toggle(diff > 0);
}
document.body.removeEventListener("touchmove", this.onTouchMove);
document.body.removeEventListener("touchend", this.onTouchEnd);
this.viewport.classList.toggle("menu-dragging", false);
this.menu.style.transform = null;
this.sidebarOverlay.style.opacity = null;
this.touchStartPos = null;
this.touchCurPos = null;
this.touchStartTime = 0;
this.menuIsMoving = false;
};
document.body.addEventListener("touchstart", this.onTouchStart, {passive: true});
}
toggle(state) {
this.menuIsOpen = state;
this.viewport.classList.toggle("menu-open", state);
}
isOpen() {
return this.menuIsOpen;
}
}
module.exports = new SlideoutMenu();

View File

@ -11,6 +11,7 @@ export default new Vuex.Store({
activeWindow: null, activeWindow: null,
sessions: [], sessions: [],
sidebarOpen: false, sidebarOpen: false,
sidebarDragging: false,
userlistOpen: storage.get("thelounge.state.userlist") !== "false", userlistOpen: storage.get("thelounge.state.userlist") !== "false",
}, },
mutations: { mutations: {
@ -32,6 +33,9 @@ export default new Vuex.Store({
sidebarOpen(state, payload) { sidebarOpen(state, payload) {
state.sidebarOpen = payload; state.sidebarOpen = payload;
}, },
sidebarDragging(state, payload) {
state.sidebarDragging = payload;
},
userlistOpen(state, payload) { userlistOpen(state, payload) {
state.userlistOpen = payload; state.userlistOpen = payload;
}, },

View File

@ -7,7 +7,6 @@ const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
const localetime = require("./libs/handlebars/localetime"); const localetime = require("./libs/handlebars/localetime");
const friendlysize = require("./libs/handlebars/friendlysize"); const friendlysize = require("./libs/handlebars/friendlysize");
const colorClass = require("./libs/handlebars/colorClass"); const colorClass = require("./libs/handlebars/colorClass");
const slideoutMenu = require("../js/slideout");
const storage = require("./localStorage"); const storage = require("./localStorage");
Vue.filter("localetime", localetime); Vue.filter("localetime", localetime);
@ -54,15 +53,11 @@ const vueApp = new Vue({
onSocketInit() { onSocketInit() {
this.initialized = true; this.initialized = true;
this.$store.commit("isConnected", true); this.$store.commit("isConnected", true);
// TODO: handle slideut in vue
slideoutMenu.enable();
}, },
setSidebar(state) { setSidebar(state) {
const utils = require("./utils"); const utils = require("./utils");
this.$store.commit("sidebarOpen", state); this.$store.commit("sidebarOpen", state);
slideoutMenu.toggle(false);
if (window.outerWidth > utils.mobileViewportPixels) { if (window.outerWidth > utils.mobileViewportPixels) {
storage.set("thelounge.state.sidebar", state); storage.set("thelounge.state.sidebar", state);