Skip to content

Commit dcf0414

Browse files
add radar chart and treemap (#73)
1 parent 80fbf10 commit dcf0414

File tree

7 files changed

+330
-1
lines changed

7 files changed

+330
-1
lines changed

ai-verify-shared-library/packages/charts/README.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The following charts are available
1212
* [AreaChart](#areachart)
1313
* [PieChart](#piechart)
1414
* [ScatterChart](#scatterchart)
15+
* [Treemap](#treemap)
1516

1617
## Chart Data Format
1718
Chart data should be in format of array of objects.
@@ -81,6 +82,139 @@ const data03 = [
8182
{ x: 150, y: 400 },
8283
{ x: 110, y: 280 },
8384
];
85+
86+
const data04 = [
87+
{
88+
name: 'axis',
89+
children: [
90+
{ name: 'Axes', size: 1302 },
91+
{ name: 'Axis', size: 24593 },
92+
{ name: 'AxisGridLine', size: 652 },
93+
{ name: 'AxisLabel', size: 636 },
94+
{ name: 'CartesianAxes', size: 6703 },
95+
],
96+
},
97+
{
98+
name: 'controls',
99+
children: [
100+
{ name: 'AnchorControl', size: 2138 },
101+
{ name: 'ClickControl', size: 3824 },
102+
{ name: 'Control', size: 1353 },
103+
{ name: 'ControlList', size: 4665 },
104+
{ name: 'DragControl', size: 2649 },
105+
{ name: 'ExpandControl', size: 2832 },
106+
{ name: 'HoverControl', size: 4896 },
107+
{ name: 'IControl', size: 763 },
108+
{ name: 'PanZoomControl', size: 5222 },
109+
{ name: 'SelectionControl', size: 7862 },
110+
{ name: 'TooltipControl', size: 8435 },
111+
],
112+
},
113+
{
114+
name: 'data',
115+
children: [
116+
{ name: 'Data', size: 20544 },
117+
{ name: 'DataList', size: 19788 },
118+
{ name: 'DataSprite', size: 10349 },
119+
{ name: 'EdgeSprite', size: 3301 },
120+
{ name: 'NodeSprite', size: 19382 },
121+
{
122+
name: 'render',
123+
children: [
124+
{ name: 'ArrowType', size: 698 },
125+
{ name: 'EdgeRenderer', size: 5569 },
126+
{ name: 'IRenderer', size: 353 },
127+
{ name: 'ShapeRenderer', size: 2247 },
128+
],
129+
},
130+
{ name: 'ScaleBinding', size: 11275 },
131+
{ name: 'Tree', size: 7147 },
132+
{ name: 'TreeBuilder', size: 9930 },
133+
],
134+
},
135+
{
136+
name: 'events',
137+
children: [
138+
{ name: 'DataEvent', size: 7313 },
139+
{ name: 'SelectionEvent', size: 6880 },
140+
{ name: 'TooltipEvent', size: 3701 },
141+
{ name: 'VisualizationEvent', size: 2117 },
142+
],
143+
},
144+
{
145+
name: 'legend',
146+
children: [
147+
{ name: 'Legend', size: 20859 },
148+
{ name: 'LegendItem', size: 4614 },
149+
{ name: 'LegendRange', size: 10530 },
150+
],
151+
},
152+
{
153+
name: 'operator',
154+
children: [
155+
{
156+
name: 'distortion',
157+
children: [
158+
{ name: 'BifocalDistortion', size: 4461 },
159+
{ name: 'Distortion', size: 6314 },
160+
{ name: 'FisheyeDistortion', size: 3444 },
161+
],
162+
},
163+
{
164+
name: 'encoder',
165+
children: [
166+
{ name: 'ColorEncoder', size: 3179 },
167+
{ name: 'Encoder', size: 4060 },
168+
{ name: 'PropertyEncoder', size: 4138 },
169+
{ name: 'ShapeEncoder', size: 1690 },
170+
{ name: 'SizeEncoder', size: 1830 },
171+
],
172+
},
173+
{
174+
name: 'filter',
175+
children: [
176+
{ name: 'FisheyeTreeFilter', size: 5219 },
177+
{ name: 'GraphDistanceFilter', size: 3165 },
178+
{ name: 'VisibilityFilter', size: 3509 },
179+
],
180+
},
181+
{ name: 'IOperator', size: 1286 },
182+
{
183+
name: 'label',
184+
children: [
185+
{ name: 'Labeler', size: 9956 },
186+
{ name: 'RadialLabeler', size: 3899 },
187+
{ name: 'StackedAreaLabeler', size: 3202 },
188+
],
189+
},
190+
{
191+
name: 'layout',
192+
children: [
193+
{ name: 'AxisLayout', size: 6725 },
194+
{ name: 'BundledEdgeRouter', size: 3727 },
195+
{ name: 'CircleLayout', size: 9317 },
196+
{ name: 'CirclePackingLayout', size: 12003 },
197+
{ name: 'DendrogramLayout', size: 4853 },
198+
{ name: 'ForceDirectedLayout', size: 8411 },
199+
{ name: 'IcicleTreeLayout', size: 4864 },
200+
{ name: 'IndentedTreeLayout', size: 3174 },
201+
{ name: 'Layout', size: 7881 },
202+
{ name: 'NodeLinkTreeLayout', size: 12870 },
203+
{ name: 'PieLayout', size: 2728 },
204+
{ name: 'RadialTreeLayout', size: 12348 },
205+
{ name: 'RandomLayout', size: 870 },
206+
{ name: 'StackedAreaLayout', size: 9121 },
207+
{ name: 'TreeMapLayout', size: 9191 },
208+
],
209+
},
210+
{ name: 'Operator', size: 2490 },
211+
{ name: 'OperatorList', size: 5248 },
212+
{ name: 'OperatorSequence', size: 4190 },
213+
{ name: 'OperatorSwitch', size: 2581 },
214+
{ name: 'SortOperator', size: 2023 },
215+
],
216+
},
217+
];
84218
```
85219

86220
# BarChart
@@ -353,3 +487,72 @@ Each object in the source data must contain a "x" and "y" fields.
353487
scatters={[{ data:data03 }]}
354488
/>
355489
```
490+
491+
# Treemap
492+
```
493+
import { Treemap } from 'ai-verify-shared-library/charts'
494+
```
495+
496+
## **Treemap** Properties
497+
498+
| Property | Type | Required | Description |
499+
| -------- | ---- | -------- | ----------- |
500+
| data | array of object | Yes | Source Data |
501+
| dataKey | string | Yes | Key of data for this bar |
502+
| aspectRatio | number | No | The treemap will try to keep every single rectangle's aspect ratio near the aspectRatio given |
503+
| colors | array of string | No | List of colors used to fill the reactangle for each branch of the tree |
504+
| hideNonLeaf | boolean | No | Whether to hide labels in non-leaf nodes, default false |
505+
| nonLeafStyle | object | No | customize the CSS style for labels in non-leaf nodes |
506+
| leafStyle | object | No | customize the CSS style for labels in leaf nodes |
507+
508+
## Examples
509+
510+
**Treemap**
511+
512+
![Treemap](images/treemap_1.png)
513+
```
514+
<Treemap data={data04} dataKey="size" aspectRatio={4 / 3} />
515+
```
516+
517+
# RadarChart
518+
```
519+
import { RadarChart } from 'ai-verify-shared-library/charts'
520+
```
521+
522+
## Source Data
523+
Each object in the source data must contain a "name" field to label each pie
524+
525+
## **RadarChart** Properties
526+
527+
| Property | Type | Required | Description |
528+
| -------- | ---- | -------- | ----------- |
529+
| data | array of object | Yes | Source Data |
530+
| radars | array of Radar object | Yes | Array of Radar definitions |
531+
| chartProps | object | No | Override chart properties, see https://recharts.org/en-US/api/RadarChart |
532+
| polarAngleAxis | object | Yes | Override XAxis properties, see https://recharts.org/en-US/api/PolarAngleAxis |
533+
| polarRadiusAxis | object | No | Override YAxis properties, see https://recharts.org/en-US/api/PolarRadiusAxis |
534+
| legendProps | object | No | Override Legend properties, see https://recharts.org/en-US/api/Legend |
535+
| hideLegend | boolean | No | If set to true, will not display the chart legend |
536+
537+
## **Radar** Properties
538+
539+
| Property | Type | Required | Description |
540+
| -------- | ---- | -------- | ----------- |
541+
| name | string | Yes | Name for this radar |
542+
| dataKey | string | Yes | Key of data for this radar |
543+
| fill | string | No | Color of radar fill in css hex code |
544+
| stroke | string | No | Color of radar stroke in css hex code |
545+
| legendType | 'line' \| 'plainline' \| 'square' \| 'rect' \| 'circle' \| 'cross' \| 'diamond' \| 'square' \| 'star' \| 'triangle' \| 'wye' \| 'none' | No | Type of icon in legend, defaults to 'none' |
546+
547+
548+
## Examples
549+
550+
**Simple RadarChart**
551+
552+
![Simple RadarChart](images/radarchart_1.png)
553+
```
554+
<RadarChart
555+
data={data02} polarAngleAxis={{ dataKey:'name' }}
556+
radars={[ { name:'Value', dataKey:'value' } ]}
557+
/>
558+
```
Loading
Loading

ai-verify-shared-library/packages/charts/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ export { default as BarChart, BarChartProps } from './barChart';
22
export { default as LineChart, LineChartProps } from './lineChart';
33
export { default as AreaChart, AreaChartProps } from './areaChart';
44
export { default as PieChart, PieChartProps, renderCustomizedPieLabel } from './pieChart';
5-
export { default as ScatterChart, ScatterChartProps } from './scatterChart';
5+
export { default as ScatterChart, ScatterChartProps } from './scatterChart';
6+
export { default as Treemap, TreemapProps } from './treemap';
7+
export { default as RadarChart, RadarChartProps } from './radarChart';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { RadarChart, Radar, RadarProps, PolarGrid, PolarAngleAxis, PolarAngleAxisProps, PolarRadiusAxis, PolarRadiusAxisProps, Legend, ResponsiveContainer } from 'recharts';
3+
import { BaseCategoryChartProps } from './types/charts';
4+
import { getColor } from './chartUtils';
5+
6+
type RadarProps2 = Omit<RadarProps, 'ref'>;
7+
type PolarAngleAxisProps2 = Omit<PolarAngleAxisProps, 'ref'>;
8+
type PolarRadiusAxisProps2 = Omit<PolarRadiusAxisProps, 'ref'>;
9+
10+
export interface RadarChartProps extends BaseCategoryChartProps {
11+
radars: RadarProps2[];
12+
polarAngleAxis?: PolarAngleAxisProps2;
13+
polarRadiusAxis?: PolarRadiusAxisProps2;
14+
}
15+
16+
export default function MyPieChart({ radars, data, chartProps, legendProps, hideLegend=false, polarAngleAxis, polarRadiusAxis }: RadarChartProps) {
17+
return (
18+
<ResponsiveContainer width="100%" height="100%">
19+
<RadarChart cx="50%" cy="50%" outerRadius="80%" data={data} {...chartProps}>
20+
<PolarGrid />
21+
<PolarAngleAxis {...polarAngleAxis} />
22+
<PolarRadiusAxis {...polarRadiusAxis} />
23+
{!hideLegend && <Legend {...legendProps} />}
24+
{radars.map((radar, index) => (
25+
<Radar key={`radar-${index}`} fill={getColor(index)} stroke={getColor(index)} fillOpacity={0.6} isAnimationActive={false} {...radar} />
26+
))}
27+
</RadarChart>
28+
</ResponsiveContainer>
29+
);
30+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React from 'react';
2+
import { Treemap, ResponsiveContainer } from 'recharts';
3+
import { Colors } from './chartUtils';
4+
import { Props } from 'recharts/types/chart/Treemap';
5+
6+
export const FONT_FAMILY ="Inter";
7+
8+
export const styles = {
9+
textBlock: {
10+
width: '100%',
11+
height: '100%',
12+
display: 'flex',
13+
},
14+
text: {
15+
color: 'white',
16+
whitespace: 'nowrap',
17+
overflow: 'hidden',
18+
textOverflow: 'ellipsis',
19+
}
20+
}
21+
22+
export function CustomizedContent ({ root, depth, x, y, width, height, index, children, colors, name, hideNonLeaf, nonLeafStyle, leafStyle }: any) {
23+
if (width <= 10 || height <= 20)
24+
return null;
25+
return (
26+
<g>
27+
<rect
28+
x={x}
29+
y={y}
30+
width={width}
31+
height={height}
32+
style={{
33+
fill: depth < 2 ? colors[Math.floor((index / root.children.length) * 6)] : '#ffffff00',
34+
stroke: '#fff',
35+
strokeWidth: 2 / (depth + 1e-10),
36+
strokeOpacity: 1 / (depth + 1e-10),
37+
}}
38+
/>
39+
<switch>
40+
<foreignObject x={x} y={y} width={width} height={height}>
41+
<div xmlns="http://www.w3.org/1999/xhtml" style={{ width, height, margin:0, padding:'3px' }}>
42+
{!children && (
43+
<div style={{ ...styles.textBlock, justifyContent:'center', alignItems:'center' }}>
44+
<div style={{ ...styles.text, fontSize:13, ...leafStyle }}>{name}</div>
45+
</div>
46+
)}
47+
{children && !hideNonLeaf && (
48+
<div style={{ ...styles.textBlock, justifyContent:'flex-start', alignItems:'flex-start' }}>
49+
<div style={{ ...styles.text, textDecoration:'underline', fontSize:13, ...nonLeafStyle }}>{name}</div>
50+
</div>
51+
)}
52+
</div>
53+
</foreignObject>
54+
{!children && (
55+
<text x={x + width / 2} y={y + height / 2 + 7} textAnchor="middle" fill="#fff" fontSize={13} font-family={FONT_FAMILY}>
56+
{name}
57+
</text>
58+
)}
59+
{children && !hideNonLeaf && (
60+
<text x={x + 4} y={y + 18} fill="#fff" fontSize={16} fillOpacity={0.9} font-family={FONT_FAMILY}>
61+
{name}
62+
</text>
63+
)}
64+
</switch>
65+
</g>
66+
);
67+
}
68+
69+
export interface TreemapProps extends Props {
70+
colors?: string[];
71+
leafStyle?: React.CSSProperties;
72+
hideNonLeaf?: boolean;
73+
nonLeafStyle?: React.CSSProperties;
74+
}
75+
76+
export default function MyTreemapChart({ data, colors=Colors, hideNonLeaf=false, nonLeafStyle={}, leafStyle={}, ...props }: TreemapProps) {
77+
return (
78+
<ResponsiveContainer width="100%" height="100%">
79+
<Treemap
80+
data={data}
81+
isAnimationActive={false}
82+
content={<CustomizedContent colors={colors} hideNonLeaf={hideNonLeaf} nonLeafStyle={nonLeafStyle} leafStyle={leafStyle} />}
83+
{...props}
84+
/>
85+
</ResponsiveContainer>
86+
);
87+
}

ai-verify-shared-library/packages/charts/src/types/charts.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,10 @@ export interface BaseCartesianChartProps {
4040
legendProps?: LegendProps;
4141
hideLegend?: boolean;
4242
}
43+
44+
export interface BaseCategoryChartProps {
45+
data: any[];
46+
chartProps: CategoricalChartProps;
47+
legendProps?: LegendProps;
48+
hideLegend?: boolean;
49+
}

0 commit comments

Comments
 (0)