fix(journey): preview graph nodes on hover
This commit is contained in:
@@ -50,6 +50,7 @@ const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
const canvasWrapRef = ref<HTMLElement | null>(null)
|
||||
const selectedId = ref('')
|
||||
const hoverId = ref('')
|
||||
const hoverPosition = ref({ x: 0, y: 0 })
|
||||
const hoverCategory = ref('')
|
||||
const playing = ref(false)
|
||||
const playbackIndex = ref(-1)
|
||||
@@ -77,6 +78,15 @@ const activePointers = new Map<number, { x: number; y: number }>()
|
||||
const nodes = computed(() => data.value?.graph.nodes || [])
|
||||
const edges = computed(() => data.value?.graph.edges || [])
|
||||
const selectedNode = computed(() => nodes.value.find(node => node.id === selectedId.value) || null)
|
||||
const hoverNode = computed(() => visibleSceneNodes.value.find(node => node.id === hoverId.value) || null)
|
||||
const hoverPreviewStyle = computed(() => {
|
||||
const wrap = canvasWrapRef.value
|
||||
const maxWidth = Math.min(300, Math.max(0, (wrap?.clientWidth || 0) - 16))
|
||||
return {
|
||||
left: `${Math.max(8, Math.min(hoverPosition.value.x + 16, (wrap?.clientWidth || 0) - maxWidth - 8))}px`,
|
||||
top: `${Math.max(8, Math.min(hoverPosition.value.y + 16, (wrap?.clientHeight || 0) - 168))}px`,
|
||||
}
|
||||
})
|
||||
const playbackNodes = computed(() =>
|
||||
nodes.value
|
||||
.map((node, index) => ({ node, index }))
|
||||
@@ -467,6 +477,7 @@ function pointerPosition(event: MouseEvent | PointerEvent | WheelEvent): { x: nu
|
||||
}
|
||||
|
||||
function handlePointerDown(event: PointerEvent) {
|
||||
hoverId.value = ''
|
||||
activePointers.set(event.pointerId, { x: event.clientX, y: event.clientY })
|
||||
if (activePointers.size === 2) {
|
||||
const pointers = [...activePointers.values()]
|
||||
@@ -485,9 +496,13 @@ function handlePointerMove(event: PointerEvent) {
|
||||
if (activePointers.has(event.pointerId)) {
|
||||
activePointers.set(event.pointerId, { x: event.clientX, y: event.clientY })
|
||||
}
|
||||
const pos = pointerPosition(event)
|
||||
const hit = hitTest(pos.x, pos.y)
|
||||
hoverId.value = hit?.id || ''
|
||||
if (!dragging && activePointers.size === 0 && event.pointerType !== 'touch') {
|
||||
const pos = pointerPosition(event)
|
||||
hoverId.value = hitTest(pos.x, pos.y)?.id || ''
|
||||
hoverPosition.value = pos
|
||||
} else {
|
||||
hoverId.value = ''
|
||||
}
|
||||
|
||||
if (activePointers.size >= 2) {
|
||||
const pointers = [...activePointers.values()]
|
||||
@@ -536,6 +551,7 @@ function handleClick(event: MouseEvent) {
|
||||
|
||||
function handleWheel(event: WheelEvent) {
|
||||
event.preventDefault()
|
||||
hoverId.value = ''
|
||||
zoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom + (event.deltaY > 0 ? -0.08 : 0.08)))
|
||||
}
|
||||
|
||||
@@ -674,9 +690,14 @@ watch(sceneNodes, () => {
|
||||
@pointermove="handlePointerMove"
|
||||
@pointerup="handlePointerUp"
|
||||
@pointercancel="handlePointerUp"
|
||||
@pointerleave="hoverId = ''"
|
||||
@click="handleClick"
|
||||
@wheel="handleWheel"
|
||||
/>
|
||||
<div v-if="hoverNode" class="node-preview" :style="hoverPreviewStyle" role="tooltip">
|
||||
<strong>{{ detailTitle(hoverNode) }}</strong>
|
||||
<p v-if="isMemoryNode(hoverNode) && memoryText(hoverNode)">{{ memoryText(hoverNode) }}</p>
|
||||
</div>
|
||||
<div class="galaxy-hud">
|
||||
<span>{{ data?.profile || '-' }}</span>
|
||||
<span>{{ visibleSceneNodes.length }} {{ t('journey.nodes') }}</span>
|
||||
@@ -828,6 +849,35 @@ watch(sceneNodes, () => {
|
||||
}
|
||||
}
|
||||
|
||||
.node-preview {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
box-sizing: border-box;
|
||||
width: min(300px, calc(100% - 16px));
|
||||
max-height: 152px;
|
||||
overflow: hidden;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: $radius-md;
|
||||
background: $bg-secondary;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
|
||||
color: $text-primary;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
overflow-wrap: anywhere;
|
||||
pointer-events: none;
|
||||
|
||||
strong { display: block; }
|
||||
p {
|
||||
margin: 4px 0 0;
|
||||
white-space: pre-wrap;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 5;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.galaxy-hud {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
|
||||
Reference in New Issue
Block a user