blob: 4b34591c23a4b5b67bcca1102ae91d0b358fcf49 [file]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useEffect, useMemo, useRef } from 'react';
import { Table } from 'antd';
import type { Column, Line, Pie, Scatter } from '@antv/g2plot';
import { VisualizationControls } from './VisualizationControls';
import { parseTableData, exportFile } from '@/utils';
import type { ParagraphConfigResult, ParagraphIResultsMsgItem, VisualizationMode } from '@zeppelin/sdk';
interface TableVisualizationProps {
result: ParagraphIResultsMsgItem;
config?: ParagraphConfigResult;
}
export const TableVisualization = ({ result, config }: TableVisualizationProps) => {
const [currentMode, setCurrentMode] = useState<VisualizationMode>(config?.graph.mode || 'table');
const chartRef = useRef<HTMLDivElement>(null);
const tableData = useMemo(() => parseTableData(result.data), [result.data]);
const handleExport = (type: 'csv' | 'xlsx') => {
if (tableData) {
exportFile(tableData, type);
}
};
const renderVisualization = () => {
if (!tableData || tableData.rows.length === 0) return null;
if (currentMode === 'table') {
const columns = tableData.columnNames.map((col, idx) => ({
title: col,
dataIndex: idx,
key: idx,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
render: (text: any) => text
}));
const dataSource = tableData.rows.map((row, idx) => ({
key: idx,
...row.reduce((acc, cell, cellIdx) => ({ ...acc, [cellIdx]: cell }), {})
}));
return (
<Table
columns={columns}
dataSource={dataSource}
size="small"
scroll={{ x: true }}
pagination={{ pageSize: 50 }}
/>
);
}
return <div ref={chartRef} style={{ height: 400 }}></div>;
};
useEffect(() => {
if (!chartRef.current || !tableData || tableData.rows.length === 0 || currentMode === 'table') return;
const data = tableData.rows.map((row, idx) => ({
category: row[0] || `Row ${idx + 1}`,
value: parseFloat(row[1] || '0') || 0,
x: idx,
y: parseFloat(row[1] || '0') || 0
}));
let chart: Column | Line | Pie | Scatter | null = null;
let cancelled = false;
import('@antv/g2plot').then(g2plot => {
if (cancelled || !chartRef.current) return;
switch (currentMode) {
case 'multiBarChart':
chart = new g2plot.Column(chartRef.current, {
data,
xField: 'category',
yField: 'value',
color: '#1890ff',
columnWidthRatio: 0.8
});
break;
case 'lineChart':
chart = new g2plot.Line(chartRef.current, {
data,
xField: 'category',
yField: 'value',
color: '#1890ff'
});
break;
case 'pieChart':
chart = new g2plot.Pie(chartRef.current, {
data,
angleField: 'value',
colorField: 'category'
});
break;
case 'scatterChart':
chart = new g2plot.Scatter(chartRef.current, {
data,
xField: 'x',
yField: 'y',
color: '#1890ff'
});
break;
case 'stackedAreaChart':
chart = new g2plot.Line(chartRef.current, {
data,
xField: 'category',
yField: 'value',
color: '#1890ff',
point: {
size: 3,
shape: 'circle'
},
lineStyle: {
lineWidth: 2
}
});
break;
}
if (chart) {
chart.render();
}
});
return () => {
cancelled = true;
if (chart) {
chart.destroy();
}
};
}, [currentMode, tableData]);
return (
<div>
<VisualizationControls currentMode={currentMode} onModeChange={setCurrentMode} onExport={handleExport} />
{renderVisualization()}
</div>
);
};