Files
speckle-server/packages/frontend-2/components/header/NavUserMenu.vue
T
andrewwallacespeckle c2a95b484f refactor(ui-components): define and use new font styles (#2524)
* New Text Styles. Initial FE2 changes

* More fe2 styling classes

* Minor update

* Minor update

* Fix build

* More updates for discussion

* More styling updates

* Minor updates to inputs

* More text updates

* More font class swapping

* Revert dui3 changes

* Confirmed Lineheights

* Add story files for new text styles

* Minor copy changes

* Minor typo

* andrew/web-1371-misalignment-in-account-dropdown

* andrew/web-1374-settings-text-styles-are-not-right

* andrew/web-1375-nav-texts-should-be-14px

* andrew/web-1376-decrease-size-of-versions-header

* andrew/web-1377-version-card-title

* semibold>medium

* Measure mode

* Changes from PR

* Tweaked nav menu

* Revert prose change. Add prose-sm

---------

Co-authored-by: Mike Tasset <mike.tasset@gmail.com>
2024-07-30 15:06:48 +01:00

208 lines
7.4 KiB
Vue

<template>
<div>
<Menu as="div" class="flex items-center">
<MenuButton :id="menuButtonId" v-slot="{ open: userOpen }">
<span class="sr-only">Open user menu</span>
<UserAvatar v-if="!userOpen" size="lg" :user="activeUser" hover-effect />
<UserAvatar v-else size="lg" hover-effect>
<XMarkIcon class="w-5 h-5" />
</UserAvatar>
</MenuButton>
<Transition
enter-active-class="transition ease-out duration-200"
enter-from-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-from-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<MenuItems
class="absolute right-4 top-14 w-56 origin-top-right bg-foundation outline outline-1 outline-primary-muted rounded-md shadow-lg overflow-hidden"
>
<div class="border-b border-outline-3 py-1 mb-1">
<MenuItem v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-primary cursor-pointer transition mx-1 rounded'
]"
target="_blank"
external
:href="connectorsPageUrl"
>
Connector downloads
</NuxtLink>
</MenuItem>
</div>
<MenuItem v-if="activeUser" v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-foreground cursor-pointer transition mx-1 rounded'
]"
@click="toggleSettingsDialog(settingsQueries.user.profile)"
>
Settings
</NuxtLink>
</MenuItem>
<MenuItem v-if="isAdmin" v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-foreground cursor-pointer transition mx-1 rounded'
]"
@click="toggleSettingsDialog(settingsQueries.server.general)"
>
Server settings
</NuxtLink>
</MenuItem>
<MenuItem v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-foreground cursor-pointer transition mx-1 rounded'
]"
@click="toggleTheme"
>
{{ isDarkTheme ? 'Light mode' : 'Dark mode' }}
</NuxtLink>
</MenuItem>
<MenuItem v-if="activeUser && !isGuest" v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-foreground cursor-pointer transition mx-1 rounded'
]"
@click="toggleInviteDialog"
>
Invite to Speckle
</NuxtLink>
</MenuItem>
<MenuItem v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-foreground cursor-pointer transition mx-1 rounded'
]"
target="_blank"
to="https://docs.google.com/forms/d/e/1FAIpQLSeTOU8i0KwpgBG7ONimsh4YMqvLKZfSRhWEOz4W0MyjQ1lfAQ/viewform"
external
>
Feedback
</NuxtLink>
</MenuItem>
<div class="border-t border-outline-3 py-1 mt-1">
<MenuItem v-if="activeUser" v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'text-body-sm flex px-2 py-1.5 text-foreground cursor-pointer transition mx-1 rounded'
]"
@click="logout"
>
Log out
</NuxtLink>
</MenuItem>
<MenuItem v-if="!activeUser && loginUrl" v-slot="{ active }">
<NuxtLink
:class="[
active ? 'bg-highlight-1' : '',
'flex px-2 py-1.5 text-sm text-foreground cursor-pointer transition mx-1 rounded'
]"
:to="loginUrl"
>
Log in
</NuxtLink>
</MenuItem>
<div v-if="version" class="border-t border-outline-3 py-1 mt-1">
<MenuItem>
<div class="px-3 pt-1 text-tiny text-foreground-2">
Version {{ version }}
</div>
</MenuItem>
</div>
</div>
</MenuItems>
</Transition>
</Menu>
<SettingsServerUserInviteDialog v-model:open="showInviteDialog" />
<SettingsDialog
v-model:open="showSettingsDialog"
v-model:target-menu-item="settingsDialogTarget"
/>
</div>
</template>
<script setup lang="ts">
import { isString } from 'lodash'
import { useBreakpoints } from '@vueuse/core'
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
import { XMarkIcon } from '@heroicons/vue/24/outline'
import { Roles } from '@speckle/shared'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import { useAuthManager } from '~~/lib/auth/composables/auth'
import { useTheme } from '~~/lib/core/composables/theme'
import { connectorsPageUrl, settingsQueries } from '~/lib/common/helpers/route'
import type { RouteLocationRaw } from 'vue-router'
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
import { useServerInfo } from '~/lib/core/composables/server'
defineProps<{
loginUrl?: RouteLocationRaw
}>()
const route = useRoute()
const { logout } = useAuthManager()
const { activeUser, isGuest } = useActiveUser()
const { isDarkTheme, toggleTheme } = useTheme()
const router = useRouter()
const { triggerNotification } = useGlobalToast()
const { serverInfo } = useServerInfo()
const showInviteDialog = ref(false)
const showSettingsDialog = ref(false)
const settingsDialogTarget = ref<string | null>(null)
const menuButtonId = useId()
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isMobile = breakpoints.smaller('md')
const version = computed(() => serverInfo.value?.version)
const isAdmin = computed(() => activeUser.value?.role === Roles.Server.Admin)
const toggleInviteDialog = () => {
showInviteDialog.value = true
}
const toggleSettingsDialog = (target: string) => {
showSettingsDialog.value = true
// On mobile open the modal but dont set the target
settingsDialogTarget.value = !isMobile.value ? target : null
}
const deleteSettingsQuery = (): void => {
const currentQueryParams = { ...route.query }
delete currentQueryParams.settings
router.push({ query: currentQueryParams })
}
onMounted(() => {
const settingsQuery = route.query?.settings
if (settingsQuery && isString(settingsQuery)) {
if (settingsQuery.includes('server') && !isAdmin.value) {
triggerNotification({
type: ToastNotificationType.Danger,
title: "You don't have access to server settings"
})
return
}
showSettingsDialog.value = true
settingsDialogTarget.value = settingsQuery
deleteSettingsQuery()
}
})
</script>