Ensure seamless toggling between selected labels and the complete graph

This commit is contained in:
yangdx
2025-03-12 08:52:28 +08:00
parent fff2ef87f0
commit ee0700d288
3 changed files with 39 additions and 23 deletions

View File

@@ -50,9 +50,17 @@ const GraphLabels = () => {
[getSearchEngine] [getSearchEngine]
) )
const setQueryLabel = useCallback((label: string) => { const setQueryLabel = useCallback((newLabel: string) => {
if (label.startsWith('And ') && label.endsWith(' others')) return if (newLabel.startsWith('And ') && newLabel.endsWith(' others')) return
useSettingsStore.getState().setQueryLabel(label)
const currentLabel = useSettingsStore.getState().queryLabel
// When selecting the same label (except '*'), switch to '*'
if (newLabel === currentLabel && newLabel !== '*') {
useSettingsStore.getState().setQueryLabel('*')
} else {
useSettingsStore.getState().setQueryLabel(newLabel)
}
}, []) }, [])
return ( return (
@@ -70,6 +78,7 @@ const GraphLabels = () => {
placeholder="Search labels..." placeholder="Search labels..."
value={label !== null ? label : ''} value={label !== null ? label : ''}
onChange={setQueryLabel} onChange={setQueryLabel}
clearable={false} // Prevent clearing value on reselect
/> />
) )
} }

View File

@@ -1,5 +1,5 @@
import Graph, { DirectedGraph } from 'graphology' import Graph, { DirectedGraph } from 'graphology'
import { useCallback, useEffect } from 'react' import { useCallback, useEffect, useRef } from 'react'
import { randomColor, errorMessage } from '@/lib/utils' import { randomColor, errorMessage } from '@/lib/utils'
import * as Constants from '@/lib/constants' import * as Constants from '@/lib/constants'
import { useGraphStore, RawGraph } from '@/stores/graph' import { useGraphStore, RawGraph } from '@/stores/graph'
@@ -161,9 +161,9 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
return graph return graph
} }
const lastQueryLabel = { label: '', maxQueryDepth: 0, minDegree: 0 }
const useLightrangeGraph = () => { const useLightrangeGraph = () => {
// Use useRef to maintain lastQueryLabel state between renders
const lastQueryLabelRef = useRef({ label: '', maxQueryDepth: 0, minDegree: 0 })
const queryLabel = useSettingsStore.use.queryLabel() const queryLabel = useSettingsStore.use.queryLabel()
const rawGraph = useGraphStore.use.rawGraph() const rawGraph = useGraphStore.use.rawGraph()
const sigmaGraph = useGraphStore.use.sigmaGraph() const sigmaGraph = useGraphStore.use.sigmaGraph()
@@ -188,37 +188,44 @@ const useLightrangeGraph = () => {
if (queryLabel) { if (queryLabel) {
// Always fetch data for "*" label // Always fetch data for "*" label
// For other labels, only fetch when parameters change // For other labels, only fetch when parameters change
const shouldUpdate = queryLabel === '*' || const shouldUpdate = true;
lastQueryLabel.label !== queryLabel ||
lastQueryLabel.maxQueryDepth !== maxQueryDepth ||
lastQueryLabel.minDegree !== minDegree;
if (shouldUpdate) { if (shouldUpdate) {
lastQueryLabel.label = queryLabel lastQueryLabelRef.current = {
lastQueryLabel.maxQueryDepth = maxQueryDepth label: queryLabel,
lastQueryLabel.minDegree = minDegree maxQueryDepth,
minDegree
}
const state = useGraphStore.getState()
state.reset()
fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => { fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => {
// console.debug('Query label: ' + queryLabel) const state = useGraphStore.getState()
state.setSigmaGraph(createSigmaGraph(data)) const newSigmaGraph = createSigmaGraph(data)
data?.buildDynamicMap() data?.buildDynamicMap()
// Update all graph data at once to minimize UI flicker
state.clearSelection()
state.setMoveToSelectedNode(false)
state.setSigmaGraph(newSigmaGraph)
state.setRawGraph(data) state.setRawGraph(data)
// Extract labels from graph data // Extract labels from graph data
if (data) { if (data) {
const labelSet = new Set<string>(['*']) const labelSet = new Set<string>();
for (const node of data.nodes) { for (const node of data.nodes) {
if (node.labels && Array.isArray(node.labels)) { if (node.labels && Array.isArray(node.labels)) {
for (const label of node.labels) { for (const label of node.labels) {
labelSet.add(label) if (label !== '*') { // filter out label "*"
labelSet.add(label);
} }
} }
} }
state.setGraphLabels(Array.from(labelSet).sort()) }
// Put * on top of other labels
const sortedLabels = Array.from(labelSet).sort();
state.setGraphLabels(['*', ...sortedLabels]);
} else { } else {
state.setGraphLabels(['*']) // Ensure * is there eventhough there is no graph data
state.setGraphLabels(['*']);
} }
}) })
} }

View File

@@ -93,7 +93,7 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
rawGraph: null, rawGraph: null,
sigmaGraph: null, sigmaGraph: null,
graphLabels: [], graphLabels: ['*'],
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
set({ selectedNode: nodeId, moveToSelectedNode }), set({ selectedNode: nodeId, moveToSelectedNode }),
@@ -115,7 +115,7 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
focusedEdge: null, focusedEdge: null,
rawGraph: null, rawGraph: null,
sigmaGraph: null, sigmaGraph: null,
graphLabels: [], graphLabels: ['*'],
moveToSelectedNode: false moveToSelectedNode: false
}), }),