feat(stage-layouts): align account dropdown with onboarding (#2439)

This commit is contained in:
Rin
2026-09-02 16:26:29 +08:00
committed by GitHub
parent 6697cb4c87
commit 1e37c054bf
@@ -1,10 +1,17 @@
<script setup lang="ts">
import { signOut } from '@proj-airi/stage-ui/libs/auth'
import { useAuthStore } from '@proj-airi/stage-ui/stores/auth'
import { Avatar } from '@proj-airi/ui'
import { onClickOutside } from '@vueuse/core'
import { AnimatedContent, Avatar } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { computed, ref } from 'vue'
import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuRoot,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from 'reka-ui'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
@@ -14,14 +21,8 @@ const { t } = useI18n()
const userName = computed(() => user.value?.name)
const userAvatar = computed(() => user.value?.image)
const showDropdown = ref(false)
const dropdownRef = ref(null)
const formattedCredits = computed(() => credits.value.toLocaleString())
onClickOutside(dropdownRef, () => {
showDropdown.value = false
})
</script>
<template>
@@ -52,98 +53,139 @@ onClickOutside(dropdownRef, () => {
</template>
<!-- Authenticated: Avatar Dropdown -->
<div v-else ref="dropdownRef" class="relative">
<button
type="button"
class="flex items-center gap-2 border-2 border-neutral-100/60 rounded-full bg-neutral-50/70 p-1 pl-1 pr-3 backdrop-blur-md transition dark:border-neutral-800/30 dark:bg-neutral-800/70 hover:bg-neutral-100 dark:hover:bg-neutral-800"
:class="{ 'ring-2 ring-primary-500/20': showDropdown }"
aria-haspopup="true"
:aria-label="userName || t('settings.pages.account.title')"
:aria-expanded="showDropdown ? 'true' : 'false'"
@click="showDropdown = !showDropdown"
>
<Avatar
:src="userAvatar"
<DropdownMenuRoot v-else>
<DropdownMenuTrigger as-child>
<button
type="button"
:aria-label="userName || t('settings.pages.account.title')"
:class="[
'h-7 w-7 rounded-full',
'bg-neutral-200 text-neutral-500 dark:bg-neutral-700 dark:text-neutral-400',
'group flex items-center gap-2 rounded-full border-2 p-1 pl-1 pr-3 outline-none backdrop-blur-md',
'border-neutral-100/60 bg-neutral-50/70 dark:border-neutral-800/30 dark:bg-neutral-800/70',
'hover:bg-neutral-100 data-[state=open]:bg-neutral-100',
'dark:hover:bg-neutral-800 dark:data-[state=open]:bg-neutral-800',
'data-[state=open]:ring-2 data-[state=open]:ring-primary-500/20',
'transition-colors duration-200 ease-in-out',
]"
/>
<span v-if="userName" class="max-w-[100px] truncate text-sm text-neutral-700 font-medium hidden sm:block dark:text-neutral-200">
{{ userName }}
</span>
<div
class="i-solar:alt-arrow-down-linear text-neutral-400 transition-transform duration-200"
:class="{ 'rotate-180': showDropdown }"
/>
</button>
<Transition
enter-active-class="transition duration-200 ease-out"
enter-from-class="translate-y-1 opacity-0"
enter-to-class="translate-y-0 opacity-100"
leave-active-class="transition duration-150 ease-in"
leave-from-class="translate-y-0 opacity-100"
leave-to-class="translate-y-1 opacity-0"
>
<div
v-if="showDropdown"
class="absolute right-0 top-full z-50 mt-2 w-60 origin-top-right border border-neutral-200/60 rounded-xl bg-white/90 p-1 shadow-xl backdrop-blur-xl divide-y divide-neutral-100 dark:border-neutral-800/60 dark:bg-neutral-900/90 dark:divide-neutral-800"
>
<div class="px-3 py-2">
<p class="text-xs text-neutral-500 dark:text-neutral-400">
Signed in as
</p>
<p class="truncate text-sm text-neutral-900 font-medium dark:text-white">
{{ userName }}
</p>
<div class="mt-1 flex items-center gap-1.5 text-xs text-primary-600 font-medium dark:text-primary-400">
<div class="i-solar:battery-charge-bold-duotone text-sm" />
<span>{{ formattedCredits }} Flux</span>
<Avatar
:src="userAvatar"
:class="[
'h-7 w-7 rounded-full',
'bg-neutral-200 text-neutral-500 dark:bg-neutral-700 dark:text-neutral-400',
]"
/>
<span
v-if="userName"
:class="[
'max-w-[100px] hidden truncate text-sm font-medium sm:block',
'text-neutral-700 dark:text-neutral-200',
]"
>
{{ userName }}
</span>
<div
:class="[
'i-solar:alt-arrow-down-linear text-neutral-400',
'transition-transform duration-200',
'group-data-[state=open]:rotate-180',
]"
/>
</button>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent
as-child
align="end"
side="bottom"
:side-offset="6"
:class="[
'z-10000 w-60 rounded-xl border p-1 shadow-lg outline-none backdrop-blur-md',
'border-neutral-100/80 bg-neutral-100/80 text-neutral-700',
'dark:border-neutral-800/60 dark:bg-neutral-800/80 dark:text-neutral-100',
]"
>
<AnimatedContent>
<div class="px-3 py-2">
<p class="text-xs text-neutral-500 dark:text-neutral-400">
Signed in as
</p>
<p class="truncate text-sm text-neutral-900 font-medium dark:text-white">
{{ userName }}
</p>
<div class="mt-1 flex items-center gap-1.5 text-xs text-primary-600 font-medium dark:text-primary-400">
<div class="i-solar:battery-charge-bold-duotone text-sm" />
<span>{{ formattedCredits }} Flux</span>
</div>
</div>
</div>
<div class="py-1">
<RouterLink
to="/settings/account"
class="group w-full flex items-center gap-3 rounded-lg px-3 py-2 text-sm text-neutral-700 transition hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
@click="showDropdown = false"
>
<div class="i-solar:user-id-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
Profile
</RouterLink>
<DropdownMenuSeparator :class="['mx-2 my-1 h-px bg-neutral-200/80 dark:bg-neutral-700/80']" />
<RouterLink
to="/settings/flux"
class="group w-full flex items-center gap-3 rounded-lg px-3 py-2 text-sm text-neutral-700 transition hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
@click="showDropdown = false"
>
<div class="i-solar:battery-charge-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
Flux
</RouterLink>
<DropdownMenuItem as-child>
<RouterLink
to="/settings/account"
:class="[
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
'text-sm leading-none outline-none text-neutral-700 dark:text-neutral-200',
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
'transition-colors duration-150 ease-in-out',
]"
>
<div class="i-solar:user-id-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
Profile
</RouterLink>
</DropdownMenuItem>
<RouterLink
to="/settings"
class="group w-full flex items-center gap-3 rounded-lg px-3 py-2 text-sm text-neutral-700 transition hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
@click="showDropdown = false"
>
<div class="i-solar:settings-minimalistic-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
Settings
</RouterLink>
</div>
<DropdownMenuItem as-child>
<RouterLink
to="/settings/flux"
:class="[
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
'text-sm leading-none outline-none text-neutral-700 dark:text-neutral-200',
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
'transition-colors duration-150 ease-in-out',
]"
>
<div class="i-solar:battery-charge-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
Flux
</RouterLink>
</DropdownMenuItem>
<div class="py-1">
<button
class="group w-full flex items-center gap-3 rounded-lg px-3 py-2 text-sm text-red-600 transition hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
@click="signOut"
>
<div class="i-solar:logout-3-bold-duotone text-lg transition group-hover:text-red-600 dark:group-hover:text-red-400" />
Sign out
</button>
</div>
</div>
</Transition>
</div>
<DropdownMenuItem as-child>
<RouterLink
to="/settings"
:class="[
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
'text-sm leading-none outline-none text-neutral-700 dark:text-neutral-200',
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
'transition-colors duration-150 ease-in-out',
]"
>
<div class="i-solar:settings-minimalistic-bold-duotone text-lg text-neutral-400 transition group-hover:text-primary-500" />
Settings
</RouterLink>
</DropdownMenuItem>
<DropdownMenuSeparator :class="['mx-2 my-1 h-px bg-neutral-200/80 dark:bg-neutral-700/80']" />
<DropdownMenuItem as-child>
<button
:class="[
'group w-full flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2',
'text-sm leading-none outline-none text-red-600 dark:text-red-400',
'data-[highlighted]:bg-red-50 dark:data-[highlighted]:bg-red-900/20',
'transition-colors duration-150 ease-in-out',
]"
@click="signOut"
>
<div class="i-solar:logout-3-bold-duotone text-lg transition group-hover:text-red-600 dark:group-hover:text-red-400" />
Sign out
</button>
</DropdownMenuItem>
</AnimatedContent>
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenuRoot>
</div>
</template>