forked from Mirrors/elk
feat: avoid navigation sidebar layout shifts while loading
This commit is contained in:
parent
a2da1b2ee1
commit
9ccc24e845
4 changed files with 48 additions and 22 deletions
|
@ -4,26 +4,22 @@ const { notifications } = useNotifications()
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav sm:px3 sm:py4 flex="~ col gap2" text-size-base leading-normal md:text-lg>
|
<nav sm:px3 sm:py4 flex="~ col gap2" text-size-base leading-normal md:text-lg>
|
||||||
<template v-if="isMastoInitialised && currentUser">
|
<NavSideItem :text="$t('nav_side.home')" to="/home" icon="i-ri:home-5-line" />
|
||||||
<NavSideItem :text="$t('nav_side.home')" to="/home" icon="i-ri:home-5-line" />
|
<NavSideItem :text="$t('nav_side.notifications')" to="/notifications" icon="i-ri:notification-4-line" :user-only="true">
|
||||||
<NavSideItem :text="$t('nav_side.notifications')" to="/notifications" icon="i-ri:notification-4-line">
|
<template #icon>
|
||||||
<template #icon>
|
<div flex relative>
|
||||||
<div flex relative>
|
<div class="i-ri:notification-4-line" md:text-size-inherit text-xl />
|
||||||
<div class="i-ri:notification-4-line" md:text-size-inherit text-xl />
|
<div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
|
||||||
<div v-if="notifications" class="top-[-0.3rem] right-[-0.3rem]" absolute font-bold rounded-full h-4 w-4 text-xs bg-primary text-inverted flex items-center justify-center>
|
{{ notifications < 10 ? notifications : '•' }}
|
||||||
{{ notifications < 10 ? notifications : '•' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</NavSideItem>
|
</template>
|
||||||
</template>
|
</NavSideItem>
|
||||||
<NavSideItem :text="$t('nav_side.explore')" :to="`/${currentServer}/explore`" icon="i-ri:hashtag" />
|
<NavSideItem :text="$t('nav_side.explore')" :to="`/${currentServer}/explore`" icon="i-ri:hashtag" />
|
||||||
<NavSideItem :text="$t('nav_side.local')" :to="`/${currentServer}/public/local`" icon="i-ri:group-2-line " />
|
<NavSideItem :text="$t('nav_side.local')" :to="`/${currentServer}/public/local`" icon="i-ri:group-2-line " />
|
||||||
<NavSideItem :text="$t('nav_side.federated')" :to="`/${currentServer}/public`" icon="i-ri:earth-line" />
|
<NavSideItem :text="$t('nav_side.federated')" :to="`/${currentServer}/public`" icon="i-ri:earth-line" />
|
||||||
<template v-if="isMastoInitialised && currentUser">
|
<NavSideItem :text="$t('nav_side.conversations')" to="/conversations" icon="i-ri:at-line" :user-only="true" />
|
||||||
<NavSideItem :text="$t('nav_side.conversations')" to="/conversations" icon="i-ri:at-line" />
|
<NavSideItem :text="$t('nav_side.favourites')" to="/favourites" icon="i-ri:heart-3-line" :user-only="true" />
|
||||||
<NavSideItem :text="$t('nav_side.favourites')" to="/favourites" icon="i-ri:heart-3-line" />
|
<NavSideItem :text="$t('nav_side.bookmarks')" to="/bookmarks" icon="i-ri:bookmark-line " :user-only="true" />
|
||||||
<NavSideItem :text="$t('nav_side.bookmarks')" to="/bookmarks" icon="i-ri:bookmark-line " />
|
|
||||||
</template>
|
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
text?: string
|
text?: string
|
||||||
icon: string
|
icon: string
|
||||||
to: string | Record<string, string>
|
to: string | Record<string, string>
|
||||||
}>()
|
userOnly?: boolean
|
||||||
|
}>(), {
|
||||||
|
userOnly: false,
|
||||||
|
})
|
||||||
|
|
||||||
defineSlots<{
|
defineSlots<{
|
||||||
icon: {}
|
icon: {}
|
||||||
|
@ -22,10 +25,26 @@ useCommand({
|
||||||
router.push(props.to)
|
router.push(props.to)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let activeClass = $ref('text-primary')
|
||||||
|
watch(isMastoInitialised, async () => {
|
||||||
|
if (!props.userOnly) {
|
||||||
|
// TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active
|
||||||
|
// we don't have currentServer defined until later
|
||||||
|
activeClass = ''
|
||||||
|
await nextTick()
|
||||||
|
activeClass = 'text-primary'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items
|
||||||
|
// when we know there is no user.
|
||||||
|
const noUserDisable = computed(() => !isMastoInitialised.value || (props.userOnly && !currentUser.value))
|
||||||
|
const noUserVisual = computed(() => isMastoInitialised.value && props.userOnly && !currentUser.value)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink :to="to" :active-class="isMastoInitialised ? 'text-primary' : ''" group focus:outline-none @click="$scrollToTop">
|
<NuxtLink :to="to" :disabled="noUserDisable" :class="noUserVisual ? 'op25 pointer-events-none ' : ''" :active-class="activeClass" group focus:outline-none @click="$scrollToTop">
|
||||||
<CommonTooltip :disabled="!isMediumScreen" :content="text" placement="right">
|
<CommonTooltip :disabled="!isMediumScreen" :content="text" placement="right">
|
||||||
<div flex w-fit px2 mx3 lg:mx0 lg:px5 py2 gap4 items-center transition-100 rounded-full group-hover:bg-active group-focus-visible:ring="2 current">
|
<div flex w-fit px2 mx3 lg:mx0 lg:px5 py2 gap4 items-center transition-100 rounded-full group-hover:bg-active group-focus-visible:ring="2 current">
|
||||||
<slot name="icon">
|
<slot name="icon">
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
|
<script setup>
|
||||||
|
const disabled = computed(() => !isMastoInitialised.value || !currentUser.value)
|
||||||
|
const disabledVisual = computed(() => isMastoInitialised.value && !currentUser.value)
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button color-primary btn-outline rounded-full ml-7 lg:ml-3 w-9 lg:w-auto font-bold py2 lg:py4 flex="~ gap2 center" @click="openPublishDialog()">
|
<button
|
||||||
|
color-primary rounded-full ml-7 lg:ml-3 w-9 lg:w-auto font-bold py2 lg:py4 flex="~ gap2 center"
|
||||||
|
cursor-pointer disabled:pointer-events-none
|
||||||
|
text-primary border-1 border-primary
|
||||||
|
:disabled="disabled" :class="disabledVisual ? 'op25' : 'hover:bg-primary hover:text-inverted'"
|
||||||
|
@click="openPublishDialog()"
|
||||||
|
>
|
||||||
<div i-ri:quill-pen-line />
|
<div i-ri:quill-pen-line />
|
||||||
<span hidden lg:block>{{ $t('action.compose') }}</span>
|
<span hidden lg:block>{{ $t('action.compose') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<div flex="~ col" overflow-y-auto justify-between h-full>
|
<div flex="~ col" overflow-y-auto justify-between h-full>
|
||||||
<div flex flex-col>
|
<div flex flex-col>
|
||||||
<NavSide />
|
<NavSide />
|
||||||
<PublishButton v-if="isMastoInitialised && currentUser" m5 />
|
<PublishButton m5 />
|
||||||
</div>
|
</div>
|
||||||
<div flex flex-col>
|
<div flex flex-col>
|
||||||
<UserSignInEntry v-if="isMastoInitialised && !currentUser" sm:hidden />
|
<UserSignInEntry v-if="isMastoInitialised && !currentUser" sm:hidden />
|
||||||
|
|
Loading…
Reference in a new issue