ui: fix sankey session filtering
This commit is contained in:
parent
06bad31a7d
commit
3e722ea5ba
1 changed files with 63 additions and 31 deletions
|
|
@ -82,7 +82,8 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
title={
|
title={
|
||||||
<div className="flex items-center relative">
|
<div className="flex items-center relative">
|
||||||
<InfoCircleOutlined className="hidden md:inline-block mr-1" />
|
<InfoCircleOutlined className="hidden md:inline-block mr-1" />
|
||||||
Set a start or end point to visualize the journey. If set, try adjusting filters.
|
Set a start or end point to visualize the journey. If set, try
|
||||||
|
adjusting filters.
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
show={true}
|
show={true}
|
||||||
|
|
@ -90,13 +91,11 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const [finalNodeCount, setFinalNodeCount] = React.useState(data.nodes.length);
|
const [finalNodeCount, setFinalNodeCount] = React.useState(data.nodes.length);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!chartRef.current) return;
|
if (!chartRef.current) return;
|
||||||
|
|
||||||
|
|
||||||
const startNodes = data.nodes.filter((n) => n.depth === 0);
|
const startNodes = data.nodes.filter((n) => n.depth === 0);
|
||||||
let finalNodes = data.nodes;
|
let finalNodes = data.nodes;
|
||||||
let finalLinks = data.links;
|
let finalLinks = data.links;
|
||||||
|
|
@ -112,24 +111,21 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
finalLinks = subLinks;
|
finalLinks = subLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const chart = echarts.init(chartRef.current);
|
const chart = echarts.init(chartRef.current);
|
||||||
|
|
||||||
|
|
||||||
const maxDepth = 4;
|
const maxDepth = 4;
|
||||||
const filteredNodes = finalNodes.filter((n) => (n.depth ?? 0) <= maxDepth);
|
const filteredNodes = finalNodes.filter((n) => (n.depth ?? 0) <= maxDepth);
|
||||||
const filteredLinks = finalLinks.filter((l) => {
|
const filteredLinks = finalLinks.filter((l) => {
|
||||||
const sourceNode = finalNodes.find((n) => n.id === l.source);
|
const sourceNode = finalNodes.find((n) => n.id === l.source);
|
||||||
const targetNode = finalNodes.find((n) => n.id === l.target);
|
const targetNode = finalNodes.find((n) => n.id === l.target);
|
||||||
return (
|
return (
|
||||||
(sourceNode?.depth ?? 0) <= maxDepth && (targetNode?.depth ?? 0) <= maxDepth
|
(sourceNode?.depth ?? 0) <= maxDepth &&
|
||||||
|
(targetNode?.depth ?? 0) <= maxDepth
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
setFinalNodeCount(filteredNodes.length);
|
setFinalNodeCount(filteredNodes.length);
|
||||||
|
|
||||||
|
|
||||||
const echartNodes = filteredNodes
|
const echartNodes = filteredNodes
|
||||||
.map((n) => {
|
.map((n) => {
|
||||||
let computedName = getNodeName(n.eventType || 'Minor Paths', n.name);
|
let computedName = getNodeName(n.eventType || 'Minor Paths', n.name);
|
||||||
|
|
@ -153,9 +149,10 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
|
|
||||||
if (a.depth === b.depth) {
|
if (a.depth === b.depth) {
|
||||||
return getEventPriority(a.type || '') - getEventPriority(b.type || '');
|
return (
|
||||||
|
getEventPriority(a.type || '') - getEventPriority(b.type || '')
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return (a.depth as number) - (b.depth as number);
|
return (a.depth as number) - (b.depth as number);
|
||||||
}
|
}
|
||||||
|
|
@ -171,12 +168,10 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
|
|
||||||
if (echartNodes.length === 0) return;
|
if (echartNodes.length === 0) return;
|
||||||
|
|
||||||
|
|
||||||
const startNodeValue = echartLinks
|
const startNodeValue = echartLinks
|
||||||
.filter((link) => link.source === 0)
|
.filter((link) => link.source === 0)
|
||||||
.reduce((sum, link) => sum + link.value, 0);
|
.reduce((sum, link) => sum + link.value, 0);
|
||||||
|
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
|
@ -206,7 +201,7 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
textBorderColor: 'transparent',
|
textBorderColor: 'transparent',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
overflow: 'truncate',
|
overflow: 'truncate',
|
||||||
maxWidth: 30,
|
maxWidth: 30,
|
||||||
distance: 3,
|
distance: 3,
|
||||||
offset: [-20, 0],
|
offset: [-20, 0],
|
||||||
formatter: function (params: any) {
|
formatter: function (params: any) {
|
||||||
|
|
@ -226,7 +221,7 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: '#333',
|
color: '#333',
|
||||||
overflow: 'truncate',
|
overflow: 'truncate',
|
||||||
paddingBottom:'.5rem',
|
paddingBottom: '.5rem',
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
@ -262,10 +257,8 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
chart.setOption(option);
|
chart.setOption(option);
|
||||||
|
|
||||||
|
|
||||||
function getUpstreamNodes(nodeIdx: number, visited = new Set<number>()) {
|
function getUpstreamNodes(nodeIdx: number, visited = new Set<number>()) {
|
||||||
if (visited.has(nodeIdx)) return;
|
if (visited.has(nodeIdx)) return;
|
||||||
visited.add(nodeIdx);
|
visited.add(nodeIdx);
|
||||||
|
|
@ -353,21 +346,60 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
chart.on('click', function (params: any) {
|
chart.on('click', function (params: any) {
|
||||||
if (!onChartClick) return;
|
if (!onChartClick) return;
|
||||||
|
const unsupported = ['other', 'drop'];
|
||||||
|
|
||||||
if (params.dataType === 'node') {
|
if (params.dataType === 'node') {
|
||||||
const nodeIndex = params.dataIndex;
|
const node = params.data;
|
||||||
const node = filteredNodes[nodeIndex];
|
const filters = []
|
||||||
onChartClick([{ node }]);
|
if (node && node.type) {
|
||||||
|
const type = node.type.toLowerCase();
|
||||||
|
if (unsupported.includes(type)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filters.push({
|
||||||
|
operator: 'is',
|
||||||
|
type: type,
|
||||||
|
value: [node.name],
|
||||||
|
isEvent: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onChartClick?.(filters);
|
||||||
} else if (params.dataType === 'edge') {
|
} else if (params.dataType === 'edge') {
|
||||||
const linkIndex = params.dataIndex;
|
const linkIndex = params.dataIndex;
|
||||||
const link = filteredLinks[linkIndex];
|
const link = filteredLinks[linkIndex];
|
||||||
onChartClick([{ link }]);
|
|
||||||
|
const firstNode = data.nodes.find(n => n.id === link.source)
|
||||||
|
const lastNode = data.nodes.find(n => n.id === link.target)
|
||||||
|
const firstNodeType = firstNode?.eventType?.toLowerCase() ?? 'location';
|
||||||
|
const lastNodeType = lastNode?.eventType?.toLowerCase() ?? 'location';
|
||||||
|
if (unsupported.includes(firstNodeType) || unsupported.includes(lastNodeType)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const filters = [];
|
||||||
|
if (firstNode) {
|
||||||
|
filters.push({
|
||||||
|
operator: 'is',
|
||||||
|
type: firstNodeType,
|
||||||
|
value: [firstNode.name],
|
||||||
|
isEvent: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastNode) {
|
||||||
|
filters.push({
|
||||||
|
operator: 'is',
|
||||||
|
type: lastNodeType,
|
||||||
|
value: [lastNode.name],
|
||||||
|
isEvent: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartClick?.(filters);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const ro = new ResizeObserver(() => chart.resize());
|
const ro = new ResizeObserver(() => chart.resize());
|
||||||
ro.observe(chartRef.current);
|
ro.observe(chartRef.current);
|
||||||
|
|
||||||
|
|
@ -377,14 +409,9 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
};
|
};
|
||||||
}, [data, height, onChartClick]);
|
}, [data, height, onChartClick]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let containerStyle: React.CSSProperties;
|
let containerStyle: React.CSSProperties;
|
||||||
if (isUngrouped) {
|
if (isUngrouped) {
|
||||||
|
const dynamicMinHeight = finalNodeCount * 15;
|
||||||
|
|
||||||
const dynamicMinHeight = finalNodeCount * 15;
|
|
||||||
containerStyle = {
|
containerStyle = {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
minHeight: dynamicMinHeight,
|
minHeight: dynamicMinHeight,
|
||||||
|
|
@ -392,14 +419,19 @@ const EChartsSankey: React.FC<Props> = (props) => {
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
containerStyle = {
|
containerStyle = {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height,
|
height,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div ref={chartRef} style={containerStyle} className='min-w-[600px] overflow-scroll' />;
|
return (
|
||||||
|
<div
|
||||||
|
ref={chartRef}
|
||||||
|
style={containerStyle}
|
||||||
|
className="min-w-[600px] overflow-scroll"
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EChartsSankey;
|
export default EChartsSankey;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue