diff --git a/packages/ui-components/src/components/layout/tabs/Horizontal.vue b/packages/ui-components/src/components/layout/tabs/Horizontal.vue index e865e146b..486edd731 100644 --- a/packages/ui-components/src/components/layout/tabs/Horizontal.vue +++ b/packages/ui-components/src/components/layout/tabs/Horizontal.vue @@ -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(() => { - // 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(() => ({ + 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() })