fix(fe): underline position on tab resize in Horizontal tabs

Replaces useElementSize with useResizeObserver to update the underline position and width when the active tab changes size, ensuring the underline stays correctly aligned even when tab content changes (e.g., badge count updates).
This commit is contained in:
andrewwallacespeckle
2025-07-04 13:10:16 +02:00
parent cf6d997b93
commit 34a366c705
@@ -98,7 +98,7 @@ import { isClient } from '@vueuse/core'
import { ArrowLongRightIcon, ArrowLongLeftIcon } from '@heroicons/vue/24/outline'
import type { Nullable } from '@speckle/shared'
import { throttle } from '#lodash'
import { useElementSize } from '@vueuse/core'
import { useResizeObserver } from '@vueuse/core'
import CommonBadge from '~~/src/components/common/Badge.vue'
const props = defineProps<{
@@ -113,7 +113,8 @@ const showLeftArrow = ref(false)
const showRightArrow = ref(false)
const isInitialSetup = ref(true)
const { width } = useElementSize(buttonContainer)
const underlineLeft = ref('0px')
const underlineWidth = ref('0px')
const buttonClass = computed(() => {
return (item: LayoutPageTabItem) => {
@@ -152,18 +153,18 @@ const activeItemRef = computed(() => {
return btns.find((b) => b.dataset['tabId'] === id) || null
})
const borderStyle = computed<CSSProperties>(() => {
// Using width in calculation to force dependency
return width.value
? {
left: `${activeItemRef.value?.offsetLeft || 0}px`,
width: `${activeItemRef.value?.clientWidth || 0}px`
}
: {
left: '0px',
width: '0px'
}
})
const borderStyle = computed<CSSProperties>(() => ({
left: underlineLeft.value,
width: underlineWidth.value
}))
const updateUnderline = () => {
const el = activeItemRef.value
if (!el) return
underlineLeft.value = `${el.offsetLeft}px`
underlineWidth.value = `${el.clientWidth}px`
}
const setActiveItem = (item: LayoutPageTabItem) => {
activeItem.value = item
@@ -232,6 +233,19 @@ watch(
}
)
// React to size changes of the active tab (e.g. when count badge disappears)
watch(
() => activeItemRef.value,
(el, _prev, onCleanup) => {
if (!el) return
updateUnderline()
const { stop } = useResizeObserver(el, updateUnderline)
onCleanup(stop)
},
{ immediate: true }
)
onBeforeUnmount(() => {
handleScroll.cancel()
})