MainHeader.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <nav
  3. class="nav is-info"
  4. :class="{ transparent, 'hide-logged-out': !loggedIn && hideLoggedOut }"
  5. >
  6. <div class="nav-left">
  7. <router-link v-if="!hideLogo" class="nav-item is-brand" to="/">
  8. <img
  9. :src="siteSettings.logo_white"
  10. :alt="siteSettings.sitename || `Musare`"
  11. />
  12. </router-link>
  13. </div>
  14. <span
  15. v-if="loggedIn || !hideLoggedOut"
  16. class="nav-toggle"
  17. :class="{ 'is-active': isMobile }"
  18. tabindex="0"
  19. @click="isMobile = !isMobile"
  20. @keyup.enter="isMobile = !isMobile"
  21. >
  22. <span />
  23. <span />
  24. <span />
  25. </span>
  26. <div class="nav-right nav-menu" :class="{ 'is-active': isMobile }">
  27. <div
  28. class="nav-item"
  29. id="nightmode-toggle"
  30. @click="toggleNightmode()"
  31. >
  32. <span
  33. :class="{
  34. 'material-icons': true,
  35. 'night-mode-toggle': true,
  36. 'night-mode-on': localNightmode
  37. }"
  38. :content="`${
  39. localNightmode ? 'Disable' : 'Enable'
  40. } Nightmode`"
  41. v-tippy
  42. >
  43. {{ localNightmode ? "dark_mode" : "light_mode" }}
  44. </span>
  45. <span class="night-mode-label">Toggle Nightmode</span>
  46. </div>
  47. <span v-if="loggedIn" class="grouped">
  48. <router-link
  49. v-if="role === 'admin'"
  50. class="nav-item admin"
  51. to="/admin"
  52. >
  53. <strong>Admin</strong>
  54. </router-link>
  55. <router-link
  56. class="nav-item"
  57. :to="{
  58. name: 'profile',
  59. params: { username }
  60. }"
  61. >
  62. Profile
  63. </router-link>
  64. <router-link class="nav-item" to="/settings"
  65. >Settings</router-link
  66. >
  67. <a class="nav-item" @click="logout()">Logout</a>
  68. </span>
  69. <span v-if="!loggedIn && !hideLoggedOut" class="grouped">
  70. <a class="nav-item" @click="openModal('login')">Login</a>
  71. <a
  72. v-if="!siteSettings.registrationDisabled"
  73. class="nav-item"
  74. @click="openModal('register')"
  75. >Register</a
  76. >
  77. </span>
  78. </div>
  79. <christmas-lights
  80. v-if="siteSettings.christmas"
  81. :lights="Math.min(Math.max(Math.floor(windowWidth / 175), 5), 15)"
  82. />
  83. </nav>
  84. </template>
  85. <script>
  86. import Toast from "toasters";
  87. import { mapState, mapGetters, mapActions } from "vuex";
  88. import { defineAsyncComponent } from "vue";
  89. export default {
  90. components: {
  91. ChristmasLights: defineAsyncComponent(() =>
  92. import("@/components/ChristmasLights.vue")
  93. )
  94. },
  95. props: {
  96. hideLogo: { type: Boolean, default: false },
  97. transparent: { type: Boolean, default: false },
  98. hideLoggedOut: { type: Boolean, default: false }
  99. },
  100. data() {
  101. return {
  102. localNightmode: false,
  103. isMobile: false,
  104. frontendDomain: "",
  105. siteSettings: {
  106. logo: "",
  107. sitename: "",
  108. christmas: false,
  109. registrationDisabled: false
  110. },
  111. windowWidth: 0
  112. };
  113. },
  114. computed: {
  115. ...mapState({
  116. modals: state => state.modalVisibility.modals.header,
  117. role: state => state.user.auth.role,
  118. loggedIn: state => state.user.auth.loggedIn,
  119. username: state => state.user.auth.username,
  120. nightmode: state => state.user.preferences.nightmode
  121. }),
  122. ...mapGetters({
  123. socket: "websockets/getSocket"
  124. })
  125. },
  126. watch: {
  127. nightmode(nightmode) {
  128. if (this.localNightmode !== nightmode)
  129. this.toggleNightmode(nightmode);
  130. }
  131. },
  132. async mounted() {
  133. this.localNightmode = JSON.parse(localStorage.getItem("nightmode"));
  134. if (this.localNightmode === null) this.localNightmode = false;
  135. this.frontendDomain = await lofig.get("frontendDomain");
  136. this.siteSettings = await lofig.get("siteSettings");
  137. this.$nextTick(() => {
  138. this.onResize();
  139. window.addEventListener("resize", this.onResize);
  140. });
  141. },
  142. methods: {
  143. toggleNightmode(toggle) {
  144. this.localNightmode = toggle || !this.localNightmode;
  145. localStorage.setItem("nightmode", this.localNightmode);
  146. if (this.loggedIn) {
  147. this.socket.dispatch(
  148. "users.updatePreferences",
  149. { nightmode: this.localNightmode },
  150. res => {
  151. if (res.status !== "success") new Toast(res.message);
  152. }
  153. );
  154. }
  155. this.changeNightmode(this.localNightmode);
  156. },
  157. onResize() {
  158. this.windowWidth = window.innerWidth;
  159. },
  160. ...mapActions("modalVisibility", ["openModal"]),
  161. ...mapActions("user/auth", ["logout"]),
  162. ...mapActions("user/preferences", ["changeNightmode"])
  163. }
  164. };
  165. </script>
  166. <style lang="less" scoped>
  167. .night-mode {
  168. .nav {
  169. background-color: var(--dark-grey-3) !important;
  170. }
  171. @media screen and (max-width: 768px) {
  172. .nav:not(.hide-logged-out) .nav-menu {
  173. background-color: var(--dark-grey-3) !important;
  174. }
  175. }
  176. .nav-item {
  177. color: var(--light-grey-2) !important;
  178. }
  179. }
  180. .nav {
  181. flex-shrink: 0;
  182. display: flex;
  183. position: relative;
  184. background-color: var(--primary-color);
  185. height: 64px;
  186. z-index: 2;
  187. &.transparent {
  188. background-color: transparent !important;
  189. }
  190. .nav-left,
  191. .nav-right {
  192. flex: 1;
  193. display: flex;
  194. }
  195. .nav-right {
  196. justify-content: flex-end;
  197. }
  198. a.nav-item.is-tab:hover {
  199. border-bottom: none;
  200. border-top: solid 1px var(--white);
  201. padding-top: 9px;
  202. }
  203. .nav-toggle {
  204. height: 64px;
  205. width: 50px;
  206. position: relative;
  207. background-color: transparent;
  208. display: none;
  209. position: relative;
  210. cursor: pointer;
  211. &.is-active {
  212. span:nth-child(1) {
  213. margin-left: -5px;
  214. transform: rotate(45deg);
  215. transform-origin: left top;
  216. }
  217. span:nth-child(2) {
  218. opacity: 0;
  219. }
  220. span:nth-child(3) {
  221. margin-left: -5px;
  222. transform: rotate(-45deg);
  223. transform-origin: left bottom;
  224. }
  225. }
  226. span {
  227. background-color: var(--white);
  228. display: block;
  229. height: 1px;
  230. left: 50%;
  231. margin-left: -7px;
  232. position: absolute;
  233. top: 50%;
  234. width: 15px;
  235. transition: none 86ms ease-out;
  236. transition-property: opacity, transform;
  237. &:nth-child(1) {
  238. margin-top: -6px;
  239. }
  240. &:nth-child(2) {
  241. margin-top: -1px;
  242. }
  243. &:nth-child(3) {
  244. margin-top: 4px;
  245. }
  246. }
  247. }
  248. .nav-item {
  249. font-size: 17px;
  250. color: var(--white);
  251. border-top: 0;
  252. display: flex;
  253. align-items: center;
  254. padding: 10px;
  255. cursor: pointer;
  256. &:hover,
  257. &:focus {
  258. color: var(--white);
  259. }
  260. &.is-brand {
  261. font-size: 2.1rem !important;
  262. line-height: 38px !important;
  263. padding: 0 20px;
  264. font-family: Pacifico, cursive;
  265. display: flex;
  266. align-items: center;
  267. img {
  268. max-height: 38px;
  269. color: var(--primary-color);
  270. user-select: none;
  271. -webkit-user-drag: none;
  272. }
  273. }
  274. .night-mode-label {
  275. display: none;
  276. }
  277. }
  278. }
  279. .grouped {
  280. margin: 0;
  281. display: flex;
  282. text-decoration: none;
  283. .nav-item {
  284. &:hover,
  285. &:focus {
  286. border-top: 1px solid var(--white);
  287. height: calc(100% - 1px);
  288. }
  289. }
  290. }
  291. @media screen and (max-width: 768px) {
  292. .nav:not(.hide-logged-out) {
  293. .nav-toggle {
  294. display: block !important;
  295. }
  296. .nav-menu {
  297. display: none !important;
  298. box-shadow: @box-shadow-dropdown;
  299. left: 0;
  300. right: 0;
  301. top: 100%;
  302. position: absolute;
  303. background: var(--white);
  304. }
  305. .nav-menu.is-active {
  306. display: flex !important;
  307. flex-direction: column-reverse;
  308. .nav-item {
  309. color: var(--dark-grey-2);
  310. &:hover {
  311. color: var(--dark-grey-2);
  312. }
  313. .night-mode-label {
  314. display: inline;
  315. margin-left: 5px;
  316. }
  317. }
  318. }
  319. .nav-menu {
  320. .grouped {
  321. flex-direction: column;
  322. }
  323. .nav-item {
  324. padding: 10px 20px;
  325. &:hover,
  326. &:focus {
  327. border-top: 0;
  328. height: unset;
  329. }
  330. }
  331. }
  332. }
  333. }
  334. </style>