fix: replace legacy timeouts with v-bind in navtab component (#5343)

* fix: dont use timeouts for navtabs

* fix: remove nexttick
This commit is contained in:
Calum H.
2026-02-09 15:24:39 +00:00
committed by GitHub
parent e80d7730ca
commit dd4b054d95

View File

@@ -55,7 +55,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Component } from 'vue' import type { Component } from 'vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
const route = useNativeRoute() const route = useNativeRoute()
@@ -104,6 +104,9 @@ const subpageSelected = ref(false)
const sliderReady = ref(false) // Slider is positioned and should be visible const sliderReady = ref(false) // Slider is positioned and should be visible
const transitionsEnabled = ref(false) // CSS transitions should apply (after first paint) const transitionsEnabled = ref(false) // CSS transitions should apply (after first paint)
// Stagger delays for the trailing edges of the slider animation
const sliderDelays = ref({ left: '0ms', top: '0ms', right: '0ms', bottom: '0ms' })
const filteredLinks = computed(() => props.links.filter((link) => link.shown ?? true)) const filteredLinks = computed(() => props.links.filter((link) => link.shown ?? true))
const sliderStyle = computed(() => ({ const sliderStyle = computed(() => ({
@@ -114,6 +117,11 @@ const sliderStyle = computed(() => ({
opacity: sliderReady.value && currentActiveIndex.value !== -1 ? 1 : 0, opacity: sliderReady.value && currentActiveIndex.value !== -1 ? 1 : 0,
})) }))
const leftDelay = computed(() => sliderDelays.value.left)
const rightDelay = computed(() => sliderDelays.value.right)
const topDelay = computed(() => sliderDelays.value.top)
const bottomDelay = computed(() => sliderDelays.value.bottom)
const isActiveAndNotSubpage = computed( const isActiveAndNotSubpage = computed(
() => (index: number) => currentActiveIndex.value === index && !subpageSelected.value, () => (index: number) => currentActiveIndex.value === index && !subpageSelected.value,
) )
@@ -229,25 +237,20 @@ function animateSliderTo(newPosition: {
right: number right: number
bottom: number bottom: number
}) { }) {
const STAGGER_DELAY = 200 const STAGGER_DELAY = '200ms'
// Horizontal animation - lead with the direction of movement // Set stagger delays: leading edge moves immediately, trailing edge is delayed
if (newPosition.left < sliderLeft.value) { sliderDelays.value = {
sliderLeft.value = newPosition.left left: newPosition.left < sliderLeft.value ? '0ms' : STAGGER_DELAY,
setTimeout(() => (sliderRight.value = newPosition.right), STAGGER_DELAY) right: newPosition.left < sliderLeft.value ? STAGGER_DELAY : '0ms',
} else { top: newPosition.top < sliderTop.value ? '0ms' : STAGGER_DELAY,
sliderRight.value = newPosition.right bottom: newPosition.top < sliderTop.value ? STAGGER_DELAY : '0ms',
setTimeout(() => (sliderLeft.value = newPosition.left), STAGGER_DELAY)
} }
// Vertical animation - lead with the direction of movement sliderLeft.value = newPosition.left
if (newPosition.top < sliderTop.value) { sliderRight.value = newPosition.right
sliderTop.value = newPosition.top sliderTop.value = newPosition.top
setTimeout(() => (sliderBottom.value = newPosition.bottom), STAGGER_DELAY) sliderBottom.value = newPosition.bottom
} else {
sliderBottom.value = newPosition.bottom
setTimeout(() => (sliderTop.value = newPosition.top), STAGGER_DELAY)
}
} }
function updateActiveTab() { function updateActiveTab() {
@@ -256,7 +259,7 @@ function updateActiveTab() {
subpageSelected.value = isSubpage subpageSelected.value = isSubpage
if (index !== -1) { if (index !== -1) {
nextTick(positionSlider) positionSlider()
} else { } else {
sliderLeft.value = 0 sliderLeft.value = 0
sliderRight.value = 0 sliderRight.value = 0
@@ -293,7 +296,10 @@ watch(() => props.links, updateActiveTab, { deep: true })
<style scoped> <style scoped>
.navtabs-transition { .navtabs-transition {
transition: transition:
all 150ms cubic-bezier(0.4, 0, 0.2, 1), left 150ms cubic-bezier(0.4, 0, 0.2, 1) v-bind(leftDelay),
right 150ms cubic-bezier(0.4, 0, 0.2, 1) v-bind(rightDelay),
top 150ms cubic-bezier(0.4, 0, 0.2, 1) v-bind(topDelay),
bottom 150ms cubic-bezier(0.4, 0, 0.2, 1) v-bind(bottomDelay),
opacity 250ms cubic-bezier(0.5, 0, 0.2, 1) 50ms; opacity 250ms cubic-bezier(0.5, 0, 0.2, 1) 50ms;
} }