diff --git a/frontend/src/App.vue b/frontend/src/App.vue index be63baf..b7a3b21 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,25 +1,6 @@ <template> <div class="app-container"> - <header v-if="user"> - <div class="wrapper"> - <nav> - <li class="main"> - <RouterLink to="/"> - <font-awesome-icon icon="map-marker-alt" /> GPSTracker - </RouterLink> - </li> - - <div class="spacer" /> - - <li class="right"> - <RouterLink to="/logout"> - <font-awesome-icon icon="sign-out-alt" /> - <span class="logout-text">Logout</span> - </RouterLink> - </li> - </nav> - </div> - </header> + <Header :user="user" /> <div class="body"> <Loading v-if="loading" /> @@ -35,13 +16,19 @@ import { RouterLink, RouterView } from 'vue-router' import { type Optional } from './models/Types'; import Api from './mixins/Api.vue'; +import Dropdowns from './mixins/Dropdowns.vue'; +import Header from './components/Header.vue'; import Loading from './elements/Loading.vue'; import Messages from './components/Messages.vue' import User from './models/User'; export default { - mixins: [Api], + mixins: [ + Api, + Dropdowns, + ], components: { + Header, Loading, Messages, RouterLink, @@ -57,6 +44,7 @@ export default { async mounted() { this.loading = true + this.installDropdownHandler() try { const auth = await this.fetchUser() @@ -93,8 +81,6 @@ export default { <style lang="scss" scoped> @use "@/styles/common.scss" as *; -$header-height: 3rem; - .app-container { width: 100%; height: 100vh; diff --git a/frontend/src/components/Header.vue b/frontend/src/components/Header.vue new file mode 100644 index 0000000..33fd443 --- /dev/null +++ b/frontend/src/components/Header.vue @@ -0,0 +1,132 @@ +<template> + <header v-if="user"> + <div class="wrapper"> + <nav> + <li class="main"> + <RouterLink :to="{ name: 'home', hash: $route.hash }"> + <font-awesome-icon icon="map-marker-alt" /> GPSTracker + </RouterLink> + </li> + + <div class="spacer" /> + + <li class="right"> + <Dropdown> + <template #button> + <button class="options" title="Options"> + <font-awesome-icon icon="user" /> + </button> + </template> + + <DropdownItem> + <RouterLink to="/devices"> + <font-awesome-icon icon="mobile-alt" /> + <span class="item-text">Devices</span> + </RouterLink> + </DropdownItem> + + <DropdownItem> + <RouterLink to="/logout"> + <font-awesome-icon icon="sign-out-alt" /> + <span class="item-text">Logout</span> + </RouterLink> + </DropdownItem> + </Dropdown> + </li> + </nav> + </div> + </header> +</template> + +<script lang="ts"> +import { RouterLink } from 'vue-router' + +import { type Optional } from '../models/Types'; +import Dropdown from './Dropdown.vue'; +import DropdownItem from './DropdownItem.vue'; +import User from '../models/User'; + +export default { + components: { + Dropdown, + DropdownItem, + RouterLink, + }, + + props: { + user: { + type: Object as () => Optional<User>, + required: true, + }, + }, +} +</script> + +<style lang="scss" scoped> +@use "@/styles/common.scss" as *; + +header { + width: 100%; + height: $header-height; + margin-bottom: 0.2rem; + padding: 0.5rem 0; + box-shadow: 0 0 0.5rem 0 rgba(0, 0, 0, 0.5); + + nav { + display: flex; + align-items: center; + + li { + display: inline-block; + list-style: none; + border-radius: 0.25rem; + + &.main { + font-size: 1.25rem; + margin-left: 0.5rem; + + :deep(a) { + &:hover { + background: none; + font-size: 1.5rem; + } + } + + &:hover { + margin-top: -0.25rem; + } + } + + &:not(.main) { + :deep(a) { + color: var(--color-text); + text-decoration: none; + } + } + + &.right { + margin-right: 0.5rem; + } + + .logout-text { + @include mobile { + display: none; + } + } + } + + .spacer { + flex: 1; + } + } + + button { + background: none; + border: none; + cursor: pointer; + font-size: 1.25rem; + margin-right: 0.5rem; + padding: 0.25rem; + } +} +</style> diff --git a/frontend/src/components/Map.vue b/frontend/src/components/Map.vue index 102ce3e..84cfd08 100644 --- a/frontend/src/components/Map.vue +++ b/frontend/src/components/Map.vue @@ -440,15 +440,6 @@ main { box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.5); } -@keyframes unroll { - from { - transform: translateY(7.5em); - } - to { - transform: translateY(0); - } -} - :deep(.ol-viewport) { .ol-attribution { position: absolute !important; diff --git a/frontend/src/styles/common.scss b/frontend/src/styles/common.scss index 1b34d6f..12045a8 100644 --- a/frontend/src/styles/common.scss +++ b/frontend/src/styles/common.scss @@ -4,6 +4,8 @@ $screen-sm: 768px; $screen-md: 992px; $screen-lg: 1200px; +$header-height: 3.5rem; + // @media utilities for common screen sizes @mixin mobile { @media (max-width: $screen-sm) { @@ -112,3 +114,12 @@ button { opacity: 0; } } + +@keyframes unroll { + from { + transform: translateY(7.5em); + } + to { + transform: translateY(0); + } +}