Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 71 additions & 44 deletions src/components/topbar/WorkflowTabs.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<div
ref="containerRef"
class="workflow-tabs-container flex h-full max-w-full flex-auto flex-row overflow-hidden"
:class="{ 'workflow-tabs-container-desktop': isDesktop }"
>
Expand All @@ -13,7 +14,6 @@
@mousedown="whileMouseDown($event, () => scroll(-1))"
/>
<ScrollPanel
ref="scrollPanelRef"
class="no-drag overflow-hidden"
:pt:content="{
class: 'p-0 w-full flex',
Expand Down Expand Up @@ -74,6 +74,7 @@ import ContextMenu from 'primevue/contextmenu'
import ScrollPanel from 'primevue/scrollpanel'
import SelectButton from 'primevue/selectbutton'
import { computed, nextTick, onUpdated, ref, watch } from 'vue'
import type { WatchStopHandle } from 'vue'
import { useI18n } from 'vue-i18n'

import WorkflowTab from '@/components/topbar/WorkflowTab.vue'
Expand Down Expand Up @@ -108,7 +109,7 @@ const workflowService = useWorkflowService()

const rightClickedTab = ref<WorkflowOption | undefined>()
const menu = ref()
const scrollPanelRef = ref()
const containerRef = ref<HTMLElement | null>(null)
const showOverflowArrows = ref(false)
const leftArrowEnabled = ref(false)
const rightArrowEnabled = ref(false)
Expand Down Expand Up @@ -221,26 +222,34 @@ const handleWheel = (event: WheelEvent) => {
})
}

const scrollContent = computed(
() =>
(containerRef.value?.querySelector(
'.p-scrollpanel-content'
) as HTMLElement | null) ?? null
)

const scroll = (direction: number) => {
const scrollElement = scrollPanelRef.value.$el.querySelector(
'.p-scrollpanel-content'
) as HTMLElement
scrollElement.scrollBy({ left: direction * 20 })
const el = scrollContent.value
if (!el) return
el.scrollBy({ left: direction * 20 })
}

const ensureActiveTabVisible = async () => {
const ensureActiveTabVisible = async (
options: { waitForDom?: boolean } = {}
) => {
if (!selectedWorkflow.value) return

await nextTick()
if (options.waitForDom !== false) {
await nextTick()
}

const scrollPanelElement = scrollPanelRef.value?.$el as
| HTMLElement
| undefined
if (!scrollPanelElement) return
const containerElement = containerRef.value
if (!containerElement) return

const activeTabElement = scrollPanelElement.querySelector(
const activeTabElement = containerElement.querySelector(
'.p-togglebutton-checked'
) as HTMLElement | null
)
if (!activeTabElement) return

activeTabElement.scrollIntoView({ block: 'nearest', inline: 'nearest' })
Expand All @@ -255,38 +264,56 @@ watch(
{ immediate: true }
)

const scrollContent = computed(
() =>
scrollPanelRef.value?.$el.querySelector(
'.p-scrollpanel-content'
) as HTMLElement
)
let overflowObserver: ReturnType<typeof useOverflowObserver> | null = null
let overflowWatch: ReturnType<typeof watch> | null = null
watch(scrollContent, (value) => {
const scrollState = useScroll(value)

watch(scrollState.arrivedState, () => {
leftArrowEnabled.value = !scrollState.arrivedState.left
rightArrowEnabled.value = !scrollState.arrivedState.right
})
let stopArrivedWatch: WatchStopHandle | null = null
let stopOverflowWatch: WatchStopHandle | null = null

overflowObserver?.dispose()
overflowWatch?.stop()
overflowObserver = useOverflowObserver(value)
overflowWatch = watch(
overflowObserver.isOverflowing,
(value) => {
showOverflowArrows.value = value
void nextTick(() => {
// Force a new check after arrows are updated
scrollState.measure()
void ensureActiveTabVisible()
})
},
{ immediate: true }
)
})
watch(
scrollContent,
(el, _prev, onCleanup) => {
stopArrivedWatch?.()
stopOverflowWatch?.()
overflowObserver?.dispose()

if (!el) return

const scrollState = useScroll(el)

stopArrivedWatch = watch(
[
() => scrollState.arrivedState.left,
() => scrollState.arrivedState.right
],
([atLeft, atRight]) => {
leftArrowEnabled.value = !atLeft
rightArrowEnabled.value = !atRight
},
{ immediate: true }
)

overflowObserver = useOverflowObserver(el)
stopOverflowWatch = watch(
overflowObserver.isOverflowing,
(isOverflow) => {
showOverflowArrows.value = isOverflow
if (!isOverflow) return
void nextTick(() => {
// Force a new check after arrows are updated
scrollState.measure()
void ensureActiveTabVisible({ waitForDom: false })
})
},
{ immediate: true }
)

onCleanup(() => {
stopArrivedWatch?.()
stopOverflowWatch?.()
overflowObserver?.dispose()
})
},
{ immediate: true }
)

onUpdated(() => {
if (!overflowObserver?.disposed.value) {
Expand Down
Loading