Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: Supports StatisticsAnalysis of the current canvas data, and adds highlighting interaction #650

Merged
merged 10 commits into from
Jan 2, 2025
109 changes: 87 additions & 22 deletions packages/studio-explore/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import {
} from '@graphscope/studio-graph';

import { ToogleLeftButton, ToogleRightButton } from './components/ToggleButton';
import { Connection, FetchGraph, Searchbar, Statistics, ClusterAnalysis, Next } from './components';
import { Connection, FetchGraph, Searchbar, Statistics, ClusterAnalysis, Next, Overview } from './components';
import { BgColorsOutlined, BarChartOutlined } from '@ant-design/icons';
import { Divider, Flex, theme } from 'antd';
import { Divider, Flex, theme, Segmented, Tabs } from 'antd';
import { getDefaultServices } from './services';

interface ExploreProps {
Expand All @@ -48,9 +48,8 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
const { id } = props;
const [services, setServices] = useState(props.services || getDefaultServices());

const onQuery = async () => {};

const containerRef = useRef<HTMLDivElement | null>(null);
const { token } = theme.useToken();

return (
<div ref={containerRef} style={{ position: 'absolute', top: '0px', left: '0px', bottom: '0px', right: '0px' }}>
Expand All @@ -62,37 +61,101 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
<SegmentedTabs
queryKey="left"
tableHeight={60}
tabStyle={{
marginBottom: '0px',
}}
block
items={[
{
key: 'StyleSetting',
key: 'Statistics',
label: (
<Flex vertical gap={0} align="center" style={{ paddingTop: '6px' }}>
<BgColorsOutlined style={{ fontSize: 17 }} />
StyleSetting
<BarChartOutlined style={{ fontSize: 17 }} />
Statistics
</Flex>
),
children: <StyleSetting />,
children: (
<Tabs
// queryKey="Statistics"
// block
indicator={{ size: origin => origin - 0, align: 'center' }}
items={[
{
key: 'current',
label: (
<Flex vertical gap={0} align="center">
Current Canvas
</Flex>
),
children: <Statistics />,
},
{
key: 'global',
label: (
<Flex vertical gap={0} align="center">
Global
</Flex>
),
children: <Overview />,
},
]}
/>
),
},

{
key: 'Statistics',
key: 'Analysis',
label: (
<Flex vertical gap={0} align="center" style={{ paddingTop: '6px' }}>
<BarChartOutlined style={{ fontSize: 17 }} />
Statistics
<Icons.Cluster />
Analysis
</Flex>
),
children: <Statistics />,
children: (
<SegmentedTabs
queryKey="analysis"
block
items={[
{
key: 'Style',
label: (
<Flex vertical gap={0} align="center">
Style
</Flex>
),
children: <StyleSetting />,
},
{
key: 'Layout',
label: (
<Flex vertical gap={0} align="center">
Layout
</Flex>
),
children: <StyleSetting />,
},
{
key: 'Cluster',
label: (
<Flex vertical gap={0} align="center">
Cluster
</Flex>
),
children: <ClusterAnalysis />,
},
]}
/>
),
},
{
key: 'ClusterAnalysis',
key: 'StyleSetting',
label: (
<Flex vertical gap={0} align="center" style={{ paddingTop: '6px' }}>
<Icons.Cluster />
ClusterAnalysis
<BgColorsOutlined style={{ fontSize: 17 }} />
Report
</Flex>
),
children: <ClusterAnalysis />,
children: <StyleSetting />,
},
]}
></SegmentedTabs>
Expand Down Expand Up @@ -128,8 +191,10 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
}
autoResize={false}
leftSideStyle={{
width: '360px',
boxShadow: 'rgba(0, 0, 0, 0.19) 0px 4px 12px',
width: '400px',
boxShadow: token.boxShadow,
marginRight: '0px',
// borderRadius: token.borderRadius,
overflow: 'scroll',
}}
rightSideStyle={{
Expand All @@ -138,8 +203,8 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
overflowY: 'scroll',
}}
defaultCollapsed={{
leftSide: true,
rightSide: false,
leftSide: false,
rightSide: true,
}}
>
{/* <Prepare data={data} schema={schema} graphId={graphId} /> */}
Expand Down Expand Up @@ -168,12 +233,12 @@ const Explore: React.FunctionComponent<ExploreProps> = props => {
</ContextMenu>
<Toolbar
direction="horizontal"
style={{ position: 'absolute', top: '20px', left: '80px', width: 500 }}
style={{ position: 'absolute', top: '12px', left: '80px', width: 500 }}
noSpace
>
<Searchbar />
</Toolbar>
<Toolbar style={{ position: 'absolute', top: '20px', left: '20px', right: 'unset' }}>
<Toolbar style={{ position: 'absolute', top: '12px', left: '12px', right: 'unset' }}>
<Connection />
<Divider style={{ margin: '0px' }} />
<ToogleLeftButton />
Expand Down
23 changes: 22 additions & 1 deletion packages/studio-explore/src/components/ChartView/BarChart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useRef } from 'react';
import { Chart } from '@antv/g2';
import { theme } from 'antd';

interface IBarChartProps {
data: Record<string, any>;
Expand All @@ -13,6 +14,7 @@ interface IBarChartProps {

const BarChart: React.FunctionComponent<IBarChartProps> = props => {
const { data, xField, yField, style = {}, onClick, options = {} } = props;
const { token } = theme.useToken();

const { height = 200, padding = [20, 80] } = style;
const ChartContainerRef = useRef(null);
Expand All @@ -29,12 +31,31 @@ const BarChart: React.FunctionComponent<IBarChartProps> = props => {

chart.current.options({
data,
style: { radius: 4 },
style: { radius: 4, fill: token.colorPrimary },
type: 'interval',
axis: {
y: false,
x: {
labelAutoRotate: true,
labelAutoHide: true,
},
},
encode: {
x: xField,
y: yField,
},
labels: [
{
text: yField,
fill: token.colorPrimary,
dy: -15,
transform: [
{
type: 'overlapHide',
},
],
},
],
transform: [{ type: 'stackY' }],
interaction: { elementSelect: { single: true } },
state: { selected: { fill: '#1d6c63' }, unselected: { opacity: 0.99 } },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import Statistics from '../Statistics';
import Overview from '../Overview';
import { useContext } from '@graphscope/studio-graph';
import { Illustration } from '@graphscope/studio-components';
import { Flex, Typography } from 'antd';
Expand All @@ -20,7 +20,7 @@ const GlobalStatistics: React.FunctionComponent<IGlobalStatisticsProps> = props
What is the next exploration? Let's take a look at the global view of graph data to find some inspiration.
</Typography.Text>
</Flex>
<Statistics />
<Overview />
</Flex>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const InspectNeighbor = props => {
// });

// };
console.log(label, 'render...label');

return (
<Flex vertical gap={12}>
<Flex align="center" gap={12}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useEffect, useState } from 'react';
import { Select, Space, Flex, Card, Button, Typography, Skeleton } from 'antd';

import { useContext, IQueryStatement } from '@graphscope/studio-graph';
import { getPropertyOptions } from './utils';
import { BarChartOutlined, DeleteOutlined } from '@ant-design/icons';
import Chart from '../../ChartView/index';

import { Illustration } from '@graphscope/studio-components';
export interface IQueryPropertyStatics {
id: 'queryPropertyStatics';
query: (property: string) => Promise<{ [key: string]: any }>;
}

interface ITableViewProps {
property?: string;
onRemove: () => void;
}
const ChartView: React.FunctionComponent<ITableViewProps> = props => {
const { onRemove } = props;
const { store, updateStore } = useContext();
const { getService, schema } = store;
const options = getPropertyOptions(schema);

const [state, setState] = useState<{
data: { [key: string]: any };
isLoading: boolean;
property?: string;
}>({
data: [],
isLoading: false,
property: props.property,
});
const { data, isLoading, property } = state;

useEffect(() => {
(async () => {
if (!property) {
return;
}
setState(preState => {
return {
...preState,
isLoading: true,
};
});

try {
const data = await getService<IQueryPropertyStatics>('queryPropertyStatics')(property);
setState(preState => {
return {
...preState,
data: data,
isLoading: false,
};
});
} catch (error) {
console.log('error', error);
setState(preState => {
return {
...preState,
isLoading: false,
};
});
}
})();
}, [property]);

const handleChange = value => {
setState(preState => {
return {
...preState,
property: value,
};
});
};

const handleChartClick = async e => {
if (!property) {
return;
}
const queryCypher = getService<IQueryStatement>('queryStatement');
const data = await queryCypher(
`
MATCH(a)
WHERE a.${property}='${e.data.data[property]}'
return a
`,
);
updateStore(draft => {
draft.data = data;
draft.source = data;
});
};

return (
<Card
title={
<Select
allowClear
variant="borderless"
onChange={handleChange}
defaultValue={property}
style={{ width: '160px' }}
options={options}
placeholder="Select property"
/>
}
extra={
<Space size={0}>
<Button type="text" icon={<BarChartOutlined />} />
<Button type="text" icon={<DeleteOutlined />} onClick={onRemove} />
</Space>
}
styles={{
header: {
padding: '0px 8px',
minHeight: '36px',
fontSize: '14px',
fontWeight: 400,
},
body: {
padding: '4px',
height: '210px',
},
}}
>
{isLoading && <Skeleton active style={{ padding: '24px' }} />}
{!isLoading &&
(property ? (
<Chart data={data} xField={property} yField="counts" onClick={handleChartClick} />
) : (
<Flex vertical justify="center" align="center">
<Illustration.Charts style={{ height: '160px', width: '160px' }} />
<Typography.Text type="secondary" style={{ textAlign: 'center' }}>
Select the properties you're interested in
</Typography.Text>
</Flex>
))}
</Card>
);
};

export default ChartView;
Loading
Loading