All files / web/src/composables/node useNodeInfoPopup.ts

0% Statements 0/73
0% Branches 0/1
0% Functions 0/1
0% Lines 0/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99                                                                                                                                                                                                     
import { ref, nextTick } from 'vue'
import type { PopupType } from '@/components/nodes/NodeInfoPopup.vue'
import { useExecutionStore } from '@/stores/executionStore'
 
/**
 * Composable for node info popup state management
 *
 * Creates popup state for a node. Should be called once in BaseNode
 * and shared with child components via provide/inject.
 *
 * State lifecycle is tied to the node component - automatically cleaned
 * up when the node unmounts.
 */
export function useNodeInfoPopup(nodeId: string) {
  const executionStore = useExecutionStore()
 
  const popupVisible = ref(false)
  const popupType = ref<PopupType>('time')
  const popupPosition = ref({ x: 0, y: 0 })
  const activeTab = ref<PopupType | null>(null)
 
  const nodeResult = () => executionStore.nodeResults.get(nodeId) || null
 
  const hasInput = () => {
    const result = nodeResult()
    return result?.input !== undefined && result.input !== null
  }
 
  const hasOutput = () => {
    const result = nodeResult()
    return result?.output !== undefined && result.output !== null
  }
 
  const hasExecutionTime = () => {
    const result = nodeResult()
    return result?.executionTime !== undefined
  }
 
  const calculatePosition = (event: MouseEvent) => {
    const target = event.currentTarget as HTMLElement
    const rect = target.getBoundingClientRect()
 
    return {
      x: rect.left + rect.width / 2 - 100,
      y: rect.bottom + 4
    }
  }
 
  const showPopup = async (event: MouseEvent, type: PopupType) => {
    if (popupVisible.value && popupType.value === type) {
      popupVisible.value = false
      activeTab.value = null
      return
    }
 
    popupType.value = type
    popupPosition.value = calculatePosition(event)
    popupVisible.value = false
 
    await nextTick()
    popupVisible.value = true
    activeTab.value = type
  }
 
  const showTimePopup = (event: MouseEvent) => {
    if (!hasExecutionTime()) return
    showPopup(event, 'time')
  }
 
  const showInputPopup = (event: MouseEvent) => {
    if (!hasInput()) return
    showPopup(event, 'input')
  }
 
  const showOutputPopup = (event: MouseEvent) => {
    if (!hasOutput()) return
    showPopup(event, 'output')
  }
 
  const closePopup = () => {
    popupVisible.value = false
    activeTab.value = null
  }
 
  return {
    popupVisible,
    popupType,
    popupPosition,
    nodeResult,
    activeTab,
    hasInput,
    hasOutput,
    hasExecutionTime,
    showTimePopup,
    showInputPopup,
    showOutputPopup,
    closePopup
  }
}