<style scoped> .slotholder { height: 100vh; width: 180px; display: block; float: left; overflow: hidden; } .global-menu { width: 180px; /* background: #221f2b; */ background: #221f2b; margin: 0; padding: 0; box-sizing: border-box; display: block; position: fixed; z-index: 900; top: 0; left: 0; bottom: 0; } .menu-logo-display { width: 27px; margin: 5px 0 0 55px; display: inline-block; height: auto; } .menu-item { color: #fff; padding: 9px 10px; display: inline-block; width: 100%; font-size: 1.1em; box-sizing: border-box; } .menu-item i.icon { margin-right: 10px; } .sub { padding-left: 20px; } .menu-section {} .menu-section + .menu-section { /* border-top: 1px solid #534c68; */ border-top: 1px solid #534c68e3; } .menu-button { cursor: pointer; } .menu-button:hover { background-color: #534c68; text-decoration: none; } .router-link-active { background-color: #534c68; } .shade { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.7); z-index: 899; cursor: pointer; } .top-menu-bar { /*color: var(--text_color);*/ /*width: 100%;*/ position: fixed; bottom: 0; left: 0; right: 0; z-index: 999; background-color: var(--small_element_bg_color); /*padding: 5px 1rem 5px;*/ display: flex; justify-content: space-around; width: 100vw; border-top: 1px solid var(--dark_border_color); display: flex; margin: 0; padding: 0; } .place-holder { width: 100%; /*height: 40px;*/ height: 0; } .logo-display { width: 27px; height: auto; } .version-display { position: absolute; bottom: 0; left: -20px; right: 0; height: 30px; padding: 5px 0; text-align: center; color: #8c80ae; cursor: pointer; background-color: var(--menu-background); } .mobile-button { padding: 5px 0 0; margin: 0; cursor: pointer; font-size: 0.6em; color: var(--menu-text); text-align: center; flex-basis: 100%; line-height: 1.8em; } .mobile-button + .mobile-button { border-left: 1px solid var(--dark_border_color); } .mobile-button i { font-size: 2em; margin: 0 auto; padding: 0; width: 100%; } .mobile-button svg { margin: 0 46% 0; display: inline-block; width: 15px; } .mobile-button:active, .mobile-button:focus, .mobile-button:hover { text-decoration: none; } .mobile-button.active { background-color: transparent; } .single-line-text { width: calc(100%); /*margin: 5px 10px;*/ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: inline-block; } .faded { color: var(--dark_border_color); } </style> <template> <div> <div class="place-holder" v-if="collapsed && !menuOpen"></div> <!-- collapsed menu, appears as a bar --> <div class="top-menu-bar" v-if="(collapsed || mobile) && !menuOpen"> <!-- logo --> <router-link v-if="loggedIn" class="mobile-button" exact-active-class="active" to="/notes" v-on:click.native="emitReloadEvent()"> <logo class="logo-display" color="var(--main-accent)" /> Notes </router-link> <!-- new note --> <div v-if="loggedIn" class="mobile-button"> <span v-if="!disableNewNote" @click="createNote"> <i class="green plus icon"></i> New Note </span> <span v-if="disableNewNote"> <i class="grey plus icon"></i> Working </span> </div> <!-- open straight to note --> <router-link v-if="loggedIn && $store.getters.totals && $store.getters.totals['quickNote']" exact-active-class="active" class="mobile-button" :to="`/notes/open/${$store.getters.totals['quickNote']}`"> <i class="green sticky note outline icon"></i> Scratch Pad </router-link> <!-- create new and redirect to new note id --> <a v-if="loggedIn && $store.getters.totals && !$store.getters.totals['quickNote']" v-on:click="newQuickNote()" exact-active-class="active" class="mobile-button"> <i class="green sticky note outline icon"></i> Scratch Pad </a> <router-link v-if="loggedIn" class="mobile-button" exact-active-class="active" to="/attachments"> <i class="green open folder outline icon"></i> Files </router-link> <!-- menu --> <div class="mobile-button" v-on:click="collapseMenu"> <i class="green link bars icon" ></i> Menu </div> </div> <div class="shade" v-if="mobile && !collapsed" v-on:click="collapseMenu"></div> <div class="slotholder" v-if="!collapsed && !mobile"> </div> <div class="global-menu" v-if="!collapsed" v-on:click="menuClicked"> <div class="menu-section" v-on:click="collapseMenu"> <i class="white angle left icon"></i> <logo class="menu-logo-display" color="var(--main-accent)" /> </div> <div class="menu-section" v-if="loggedIn"> <div v-if="!disableNewNote" @click="createNote" class="menu-item menu-item menu-button"> <div class="ui green fluid compact button"> <i class="plus icon"></i>New Note </div> </div> <div v-if="disableNewNote" class="menu-item menu-item menu-button"> <div class="ui basic fluid compact button"> <i class="plus loading icon"></i>New Note </div> </div> </div> <div class="menu-section" v-if="loggedIn"> <router-link exact-active-class="active" class="menu-item menu-button" to="/notes" v-on:click.native="emitReloadEvent()"> <i class="file outline icon"></i>Notes <counter v-if="$store.getters.totals && $store.getters.totals['totalNotes']" class="float-right" number-id="totalNotes" /> </router-link> <div> <div class="menu-item menu-button sub" v-on:click="updateFastFilters(3)" v-if="$store.getters.totals && ($store.getters.totals['sharedToNotes'] > 0 || $store.getters.totals['sharedFromNotes'] > 0)"> <i class="grey paper plane outline icon"></i>Shared <counter v-if="$store.getters.totals && $store.getters.totals['sharedToNotes']" class="float-right" number-id="sharedToNotes" /> </div> <div class="menu-item menu-button sub" v-on:click="updateFastFilters(2)" v-if="$store.getters.totals && $store.getters.totals['archivedNotes'] > 0"> <i class="grey archive icon"></i>Archived <counter v-if="$store.getters.totals && $store.getters.totals['archivedNotes']" class="float-right" number-id="archivedNotes" /> </div> <div class="menu-item menu-button sub" v-on:click="updateFastFilters(4)" v-if="$store.getters.totals && $store.getters.totals['trashedNotes'] > 0"> <i class="grey trash alternate outline icon"></i>Trashed <counter v-if="$store.getters.totals && $store.getters.totals['trashedNotes']" class="float-right" number-id="trashedNotes" /> </div> <!-- <div class="menu-item sub">Show Only <i class="caret down icon"></i></div> --> <!-- <div v-on:click="updateFastFilters(0)" class="menu-item menu-button sub"><i class="grey linkify icon"></i>Links</div> --> <!-- <div v-on:click="updateFastFilters(1)" class="menu-item menu-button sub"><i class="grey tags icon"></i>Tags</div> --> </div> </div> <div class="menu-section" v-if="loggedIn && $store.getters.totals && $store.getters.totals['totalFiles']"> <router-link class="menu-item menu-button" exact-active-class="active" to="/attachments"> <i class="open folder outline icon"></i>Files <counter class="float-right" number-id="totalFiles" /> </router-link> </div> <div class="menu-section" v-if="loggedIn"> <!-- open straight to note --> <router-link v-if="loggedIn && $store.getters.totals && $store.getters.totals['quickNote']" exact-active-class="active" class="menu-item menu-button" :to="`/notes/open/${$store.getters.totals['quickNote']}`"> <i class="sticky note outline icon"></i>Scratch Pad </router-link> <!-- create new and redirect to new note id --> <a v-if="loggedIn && $store.getters.totals && !$store.getters.totals['quickNote']" v-on:click="newQuickNote()" exact-active-class="active" class="menu-item menu-button"> <i class="sticky note outline icon"></i>Scratch Pad </a> </div> <div class="menu-section" v-if="!loggedIn"> <router-link v-if="!loggedIn" class="menu-item menu-button" exact-active-class="active" to="/"> <i class="home icon"></i>Welcome </router-link> <router-link exact-active-class="active" class="menu-item menu-button" to="/login"> <i class="plug icon"></i>Login </router-link> </div> <div class="menu-section"> <div v-on:click="toggleNightMode" class="menu-item menu-button"> <span v-if="$store.getters.getIsNightMode == 0"> <i class="moon outline icon"></i>Black Theme</span> <span v-if="$store.getters.getIsNightMode == 1"> <i class="moon outline icon"></i>Light Theme</span> </div> </div> <div class="menu-section" v-if="loggedIn"> <router-link class="menu-item menu-button" exact-active-class="active" to="/settings"> <i class="cog icon"></i>Settings </router-link> </div> <div class="menu-section" v-if="loggedIn"> <router-link class="menu-item menu-button" exact-active-class="active" to="/metrictrack"> <i class="calendar check outlin icon"></i>Metric Track </router-link> </div> <div class="menu-section"> <router-link class="menu-item menu-button" exact-active-class="active" to="/help"> <i class="question circle outline icon"></i>Help </router-link> </div> <div class="menu-section" v-if="loggedIn"> <div class="menu-item menu-button" v-on:click="logout()"> <i class="log out icon"></i>Log Out </div> </div> <!-- Tags --> <div class="menu-section" v-if="gotTags()"> <div class="menu-item"> <i class="green tags icon"></i> Tags </div> </div> <div v-if="gotTags()"> <div class="menu-section" v-for="(data, tag) in $store.getters.totals['tags']"> <router-link class="menu-item menu-button" :to="`/search/tags/${tag}`"> <span class="single-line-text"> <!-- <i class="small grey tag icon"></i> --> <span class="float-right">{{ data.uses }}</span> <span class="faded"> #</span> {{ tag }}</span> </router-link> </div> </div> <div v-on:click="reloadPage" class="version-display" v-if="version != 0" > <i :class="`${getVersionIcon()} icon`"></i> {{ version }} </div> </div> </div> </template> <script> import axios from 'axios' export default { components: { 'search-input': require('@/components/SearchInput.vue').default, 'counter':require('@/components/AnimatedCounterComponent.vue').default, 'logo':require('@/components/LogoComponent.vue').default, }, data: function(){ return { version: '0', username: '', collapsed: false, mobile: false, disableNewNote: false, menuOpen: true, userIcon: true, resizeDebounce: null, } }, beforeMount(){ window.addEventListener('resize', this.resizeEventHandler) }, beforeDestroy(){ window.removeEventListener('resize', this.resizeEventHandler) }, mounted: function(){ this.mobile = this.$store.getters.getIsUserOnMobile this.collapsed = this.$store.getters.getIsUserOnMobile if(this.mobile){ this.menuOpen = false } if(this.loggedIn){ this.$store.dispatch('fetchAndUpdateUserTotals') this.version = localStorage.getItem('currentVersion') } this.resizeEventHandler() //Trigger resize event }, computed: { loggedIn () { //Map logged in from state return this.$store.getters.getLoggedIn }, }, methods: { gotTags(){ if(this.loggedIn && this.$store.getters.totals && this.$store.getters.totals.tags && Object.keys(this.$store.getters.totals.tags).length ){ return true } return false }, logout() { this.$router.push('/') axios.post('/api/user/logout') setTimeout(() => { this.$store.commit('destroyLoginToken') this.$bus.$emit('notification', 'Logged Out') }, 200) }, newQuickNote(){ axios.post('/api/quick-note/get') .then( ({data}) => { this.$router.push({'path':'/notes/open/'+data.noteId}) }) }, resizeEventHandler(e) { clearTimeout(this.resizeDebounce) this.resizeDebounce = setTimeout(() => { this.mobile = false this.menuOpen = false this.collapsed = false if(window.innerWidth < 700){ this.collapsed = true this.mobile = true } }, 100) }, menuClicked(){ //Collapse menu when item is clicked in mobile if(this.mobile && !this.collapsed){ this.collapsed = true this.menuOpen = false } }, collapseMenu(){ this.collapsed = !this.collapsed if(!this.collapsed){ this.menuOpen = true } else { this.menuOpen = false } }, createNote(event){ this.disableNewNote = true axios.post('/api/note/create', {title:''}) .then(response => { if(response.data && response.data.id){ //Push new note to url and it will open this.$router.push('/notes/open/'+response.data.id) this.disableNewNote = false } }) .catch(error => { this.$bus.$emit('notification', 'Failed to create note') }) }, toggleNightMode(){ this.$store.commit('toggleNightMode') }, ucWords(str){ return (str + '') .replace(/^(.)|\s+(.)/g, function ($1) { return $1.toUpperCase() }) }, emitReloadEvent(){ //Reloads note page to initial state this.$bus.$emit('note_reload') }, updateFastFilters(filterIndex){ //A little hacky, brings user to notes page then filters on click if(this.$route.name != 'Note Page'){ this.$router.push('/notes') setTimeout( () => { this.$bus.$emit('update_fast_filters', filterIndex) }, 500 ) } else { this.$bus.$emit('update_fast_filters', filterIndex) } }, reloadPage(){ location.reload(true) }, getVersionIcon(){ if(!this.version){ return 'radiation alternate' } const icons = ['cat','crow','dog','dove','dragon','fish','frog','hippo','horse','kiwi bird','otter','spider', 'smile', 'robot', 'hat wizard', 'microchip', 'atom', 'grin tongue squint', 'radiation', 'ghost', 'dna', 'burn', 'brain', 'moon', 'torii gate'] const index = ( parseInt(String(this.version).replace(/\./g,'')) % (icons.length)) return icons[index] } } } </script>