feat: add click interactivity to Plotly 3D scatter chart
Enable users to click on points in the vector space visualization to open the chunk viewer modal, providing a more direct interaction method alongside the existing "Show Chunk" button. Implementation details: - Register plotly_click event handler in renderPlot() after chart creation - Add handlePlotClick() method to process click events - Use point index mapping to access full result object from this.results - Add loading guard in viewChunk() to prevent concurrent chunk loading - Add cursor styling: pointer for result points, default for query point - Add beforeDestroy() lifecycle hook to cleanup event handlers Features: - Both interaction methods work: click chart points OR "Show Chunk" button - Only result points (trace 0) are clickable, query point (trace 1) ignored - Pointer cursor on hover indicates clickable points - Loading state prevents rapid clicks from causing issues - Memory leak prevention through proper event handler cleanup Technical approach: - Uses index mapping (not data duplication) for efficiency - Results and coordinates arrays have guaranteed 1:1 mapping from API - Event handler re-registered on each chart re-render - CSS-based cursor styling (more performant than JS hover handlers) Testing: - ESLint validation passed - Follows Vue 2.7 component property order conventions - Compatible with existing chunk viewer modal
This commit is contained in:
Vendored
+57
@@ -490,6 +490,13 @@ export default {
|
||||
return this.results.filter(r => (r.score || 0) >= threshold)
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
// Clean up Plotly event handlers to prevent memory leaks
|
||||
const plotDiv = document.getElementById('viz-plot')
|
||||
if (plotDiv && plotDiv.on) {
|
||||
plotDiv.removeAllListeners('plotly_click')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleDocType(docTypeId, checked) {
|
||||
if (checked && !this.selectedDocTypes.includes(docTypeId)) {
|
||||
@@ -733,6 +740,12 @@ export default {
|
||||
}
|
||||
|
||||
Plotly.newPlot('viz-plot', traces, layout, config)
|
||||
|
||||
// Register click event handler for result points
|
||||
const plotDiv = document.getElementById('viz-plot')
|
||||
if (plotDiv) {
|
||||
plotDiv.on('plotly_click', this.handlePlotClick)
|
||||
}
|
||||
},
|
||||
|
||||
updatePlot() {
|
||||
@@ -751,6 +764,11 @@ export default {
|
||||
},
|
||||
|
||||
async viewChunk(result) {
|
||||
// Guard against concurrent loading
|
||||
if (this.viewerLoading) {
|
||||
return
|
||||
}
|
||||
|
||||
this.showViewer = true
|
||||
this.viewerLoading = true
|
||||
this.viewerTitle = result.title || 'Chunk Viewer'
|
||||
@@ -828,6 +846,35 @@ export default {
|
||||
this.showViewer = false
|
||||
this.pdfTotalPages = 0
|
||||
},
|
||||
|
||||
handlePlotClick(eventData) {
|
||||
// Only handle clicks on trace 0 (document results)
|
||||
// Trace 1 is the query point - ignore clicks on it
|
||||
if (!eventData.points || eventData.points.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const point = eventData.points[0]
|
||||
const traceIndex = point.curveNumber // 0 = documents, 1 = query
|
||||
const pointIndex = point.pointNumber // Index in trace data
|
||||
|
||||
// Ignore clicks on query point (trace 1)
|
||||
if (traceIndex !== 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Access full result object using pointIndex
|
||||
// Results array is 1:1 with coordinates array (guaranteed by API)
|
||||
const result = this.results[pointIndex]
|
||||
|
||||
if (!result) {
|
||||
console.warn('Click handler: result not found for index', pointIndex)
|
||||
return
|
||||
}
|
||||
|
||||
// Call existing viewChunk method
|
||||
this.viewChunk(result)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -955,6 +1002,16 @@ export default {
|
||||
#viz-plot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
// Pointer cursor for clickable result points (trace 0)
|
||||
:deep(.scatterlayer .trace:first-child .point) {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
// Default cursor for query point (trace 1)
|
||||
:deep(.scatterlayer .trace:nth-child(2) .point) {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Results
|
||||
|
||||
Reference in New Issue
Block a user