[feat] Add rectangular hover area tracking for queue overlay

Uses useMouse + useElementBounding from VueUse to detect hover over the
entire rectangular bounding box of the queue area (actionbar + overlay).

This approach is needed because QueueProgressOverlay has pointer-events-none
on its wrapper, which would create "holes" in hover detection with standard
mouseenter/mouseleave events.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Benjamin Lu
2025-11-24 13:05:22 -08:00
parent 72124a9fb0
commit 8fba5f8914
2 changed files with 28 additions and 5 deletions

View File

@@ -4,7 +4,7 @@
<SubgraphBreadcrumb />
</div>
<div class="mx-1 flex flex-col items-end gap-1">
<div ref="queueAreaRef" class="mx-1 flex flex-col items-end gap-1">
<div
class="actionbar-container pointer-events-auto flex h-12 items-center rounded-lg border border-interface-stroke px-2 shadow-interface"
>
@@ -40,12 +40,16 @@
<CurrentUserButton v-if="isLoggedIn" class="shrink-0" />
<LoginButton v-else-if="isDesktop" />
</div>
<QueueProgressOverlay v-model:expanded="isQueueOverlayExpanded" />
<QueueProgressOverlay
v-model:expanded="isQueueOverlayExpanded"
:external-hovered="isQueueAreaHovered"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useElementBounding, useMouse } from '@vueuse/core'
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -68,6 +72,20 @@ const { isLoggedIn } = useCurrentUser()
const isDesktop = isElectron()
const { t } = useI18n()
const isQueueOverlayExpanded = ref(false)
// Track hover over the rectangular bounding box of the queue area
// Using mouse position + element bounds instead of mouseenter/mouseleave
// because QueueProgressOverlay has pointer-events-none on its wrapper
const queueAreaRef = ref<HTMLElement | null>(null)
const { x: mouseX, y: mouseY } = useMouse()
const { left, top, right, bottom } = useElementBounding(queueAreaRef)
const isQueueAreaHovered = computed(
() =>
mouseX.value >= left.value &&
mouseX.value <= right.value &&
mouseY.value >= top.value &&
mouseY.value <= bottom.value
)
const queueStore = useQueueStore()
const queuedCount = computed(() => queueStore.pendingTasks.length)
const queueHistoryTooltipConfig = computed(() =>

View File

@@ -6,8 +6,8 @@
<div
class="pointer-events-auto flex w-[350px] min-w-[310px] max-h-[60vh] flex-col overflow-hidden rounded-lg border font-inter transition-colors duration-200 ease-in-out"
:class="containerClass"
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
@mouseenter="isHoveredInternal = true"
@mouseleave="isHoveredInternal = false"
>
<!-- Expanded state -->
<QueueOverlayExpanded
@@ -87,6 +87,8 @@ type OverlayState = 'hidden' | 'empty' | 'active' | 'expanded'
const props = defineProps<{
expanded?: boolean
/** External hover state from parent container */
externalHovered?: boolean
}>()
const emit = defineEmits<{
@@ -109,7 +111,10 @@ const {
totalProgressStyle,
currentNodeProgressStyle
} = useQueueProgress()
const isHovered = ref(false)
const isHoveredInternal = ref(false)
const isHovered = computed(
() => isHoveredInternal.value || props.externalHovered
)
const internalExpanded = ref(false)
const isExpanded = computed({
get: () =>