<template> <aside id="sidebar" ref="sidebar"> <div class="scrollable-area"> <div class="logo-container"> <img :src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg.svg`" class="logo" alt="The Lounge" role="presentation" /> <img :src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg-inverted.svg`" class="logo-inverted" alt="The Lounge" role="presentation" /> <span v-if="isDevelopment" title="The Lounge has been built in development mode" :style="{ backgroundColor: '#ff9e18', color: '#000', padding: '2px', borderRadius: '4px', fontSize: '12px', }" >DEVELOPER</span > </div> <NetworkList /> </div> <footer id="footer"> <span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Connect to network" ><router-link to="/connect" tag="button" active-class="active" :class="['icon', 'connect']" aria-label="Connect to network" role="tab" aria-controls="connect" :aria-selected="$route.name === 'Connect'" /></span> <span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Settings" ><router-link to="/settings" tag="button" active-class="active" :class="['icon', 'settings']" aria-label="Settings" role="tab" aria-controls="settings" :aria-selected="$route.name === 'Settings'" /></span> <span class="tooltipped tooltipped-n tooltipped-no-touch" :aria-label=" $store.state.serverConfiguration.isUpdateAvailable ? 'Help\n(update available)' : 'Help' " ><router-link to="/help" tag="button" active-class="active" :class="[ 'icon', 'help', {notified: $store.state.serverConfiguration.isUpdateAvailable}, ]" aria-label="Help" role="tab" aria-controls="help" :aria-selected="$route.name === 'Help'" /></span> </footer> </aside> </template> <script> import NetworkList from "./NetworkList.vue"; export default { name: "Sidebar", components: { NetworkList, }, props: { overlay: HTMLElement, }, data() { return { isDevelopment: process.env.NODE_ENV !== "production", }; }, 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.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.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: { isPublic: () => document.body.classList.contains("public"), }, }; </script>