lupine.components 1.1.41 → 1.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/component-pool/charts/area-chart-demo.tsx +0 -4
- package/src/component-pool/charts/area-chart.tsx +5 -2
- package/src/component-pool/charts/bar-chart-demo.tsx +1 -4
- package/src/component-pool/charts/bar-chart.tsx +5 -2
- package/src/component-pool/charts/chart-utils.ts +4 -3
- package/src/component-pool/charts/column-chart-demo.tsx +1 -4
- package/src/component-pool/charts/column-chart.tsx +5 -2
- package/src/component-pool/charts/donut-chart-demo.tsx +2 -5
- package/src/component-pool/charts/donut-chart.tsx +1 -23
- package/src/component-pool/charts/gauge-chart-demo.tsx +0 -4
- package/src/component-pool/charts/gauge-chart.tsx +5 -2
- package/src/component-pool/charts/line-chart-demo.tsx +0 -4
- package/src/component-pool/charts/line-chart.tsx +5 -2
- package/src/component-pool/charts/pie-chart-demo.tsx +4 -10
- package/src/component-pool/charts/pie-chart.tsx +78 -61
- package/src/component-pool/charts/radar-chart-demo.tsx +0 -3
- package/src/component-pool/charts/radar-chart.tsx +40 -5
- package/src/component-pool/charts/scatter-chart-demo.tsx +0 -3
- package/src/component-pool/charts/scatter-chart.tsx +5 -2
- package/src/component-pool/index.ts +1 -0
- package/src/component-pool/svg-graph/index.ts +1 -0
- package/src/component-pool/svg-graph/svg-graph-demo.tsx +59 -0
- package/src/component-pool/svg-graph/svg-graph.ts +166 -0
- package/src/component-pool/youtube-player/youtube-player-demo.tsx +4 -4
- package/src/component-pool/youtube-player/youtube-player.tsx +90 -14
- package/src/components/action-sheet-demo.tsx +12 -0
- package/src/components/action-sheet-theme.tsx +150 -0
- package/src/components/index.ts +1 -0
- package/src/components/menu-bar.tsx +15 -4
- package/src/components/menu-sidebar.tsx +24 -12
- package/src/components/message-box.tsx +18 -0
- package/src/components/popup-menu.tsx +4 -4
- package/src/components/tabs.tsx +9 -6
- package/src/demo/demo-about.tsx +1 -1
- package/src/demo/demo-frame-helper.tsx +8 -1
- package/src/demo/demo-registry.ts +2 -0
package/package.json
CHANGED
|
@@ -16,14 +16,12 @@ export const areaChartDemo: DemoStory<any> = {
|
|
|
16
16
|
args: {
|
|
17
17
|
title: 'Company Revenues',
|
|
18
18
|
width: '100%',
|
|
19
|
-
height: '350px',
|
|
20
19
|
showLegend: true,
|
|
21
20
|
curved: true,
|
|
22
21
|
},
|
|
23
22
|
argTypes: {
|
|
24
23
|
title: { control: 'text' },
|
|
25
24
|
width: { control: 'text' },
|
|
26
|
-
height: { control: 'text' },
|
|
27
25
|
showLegend: { control: 'boolean' },
|
|
28
26
|
curved: { control: 'boolean' },
|
|
29
27
|
},
|
|
@@ -70,7 +68,6 @@ export const areaChartDemo: DemoStory<any> = {
|
|
|
70
68
|
data={multiSeriesData}
|
|
71
69
|
title={args.title}
|
|
72
70
|
width={args.width}
|
|
73
|
-
height={args.height}
|
|
74
71
|
showLegend={args.showLegend}
|
|
75
72
|
curved={args.curved}
|
|
76
73
|
yAxisFormatter={(val) => '$' + val + 'M'}
|
|
@@ -84,7 +81,6 @@ export const areaChartDemo: DemoStory<any> = {
|
|
|
84
81
|
<AreaChart
|
|
85
82
|
data={multiSeriesData}
|
|
86
83
|
title='Straight Area Comparison'
|
|
87
|
-
height='300px'
|
|
88
84
|
showLegend={true}
|
|
89
85
|
curved={false}
|
|
90
86
|
yAxisFormatter={(val) => '$' + val + 'M'}
|
|
@@ -228,13 +228,16 @@ export const AreaChart = (props: AreaChartProps) => {
|
|
|
228
228
|
},
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
-
const
|
|
231
|
+
const ratio = props.aspectRatio ?? 16 / 9;
|
|
232
|
+
const paddingTop = `${(1 / ratio) * 100}%`;
|
|
233
|
+
|
|
234
|
+
const styleStr = `width: ${props.width || '100%'};`;
|
|
232
235
|
|
|
233
236
|
return (
|
|
234
237
|
<div ref={ref} class='&-container' style={styleStr}>
|
|
235
238
|
{props.title && <div class='chart-title'>{props.title}</div>}
|
|
236
239
|
|
|
237
|
-
<div style={{ flex: 1, position: 'relative', minHeight: 0 }}>{chartVar.node}</div>
|
|
240
|
+
<div style={{ flex: 1, position: 'relative', minHeight: 0, paddingTop }}>{chartVar.node}</div>
|
|
238
241
|
|
|
239
242
|
{showLegend && (
|
|
240
243
|
<div class='chart-legend'>
|
|
@@ -26,13 +26,11 @@ export const barChartDemo: DemoStory<any> = {
|
|
|
26
26
|
args: {
|
|
27
27
|
title: 'Top Economies',
|
|
28
28
|
width: '100%',
|
|
29
|
-
height: '350px',
|
|
30
29
|
showLegend: true,
|
|
31
30
|
},
|
|
32
31
|
argTypes: {
|
|
33
32
|
title: { control: 'text' },
|
|
34
33
|
width: { control: 'text' },
|
|
35
|
-
height: { control: 'text' },
|
|
36
34
|
showLegend: { control: 'boolean' },
|
|
37
35
|
},
|
|
38
36
|
render: (args: any) => {
|
|
@@ -78,7 +76,6 @@ export const barChartDemo: DemoStory<any> = {
|
|
|
78
76
|
data={singleSeriesData}
|
|
79
77
|
title={args.title}
|
|
80
78
|
width={args.width}
|
|
81
|
-
height={args.height}
|
|
82
79
|
showLegend={args.showLegend}
|
|
83
80
|
xAxisFormatter={(val) => '$' + val + 'T'}
|
|
84
81
|
/>
|
|
@@ -88,7 +85,7 @@ export const barChartDemo: DemoStory<any> = {
|
|
|
88
85
|
<section class='demo-section'>
|
|
89
86
|
<div class='section-title'>Multi-Series (Grouped Horizontal)</div>
|
|
90
87
|
<div class='chart-box'>
|
|
91
|
-
<BarChart data={multiSeriesData} title='Quarterly Comparison'
|
|
88
|
+
<BarChart data={multiSeriesData} title='Quarterly Comparison' showLegend={true} />
|
|
92
89
|
</div>
|
|
93
90
|
</section>
|
|
94
91
|
</div>
|
|
@@ -197,13 +197,16 @@ export const BarChart = (props: BarChartProps) => {
|
|
|
197
197
|
},
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
-
const
|
|
200
|
+
const ratio = props.aspectRatio ?? 16 / 9;
|
|
201
|
+
const paddingTop = `${(1 / ratio) * 100}%`;
|
|
202
|
+
|
|
203
|
+
const styleStr = `width: ${props.width || '100%'};`;
|
|
201
204
|
|
|
202
205
|
return (
|
|
203
206
|
<div ref={ref} class='&-container' style={styleStr}>
|
|
204
207
|
{props.title && <div class='chart-title'>{props.title}</div>}
|
|
205
208
|
|
|
206
|
-
<div style={{ flex: 1, position: 'relative', minHeight: 0 }}>{chartVar.node}</div>
|
|
209
|
+
<div style={{ flex: 1, position: 'relative', minHeight: 0, paddingTop }}>{chartVar.node}</div>
|
|
207
210
|
|
|
208
211
|
{showLegend && (
|
|
209
212
|
<div class='chart-legend'>
|
|
@@ -20,8 +20,9 @@ declare global {
|
|
|
20
20
|
export const chartCommonCss: CssProps = {
|
|
21
21
|
position: 'relative',
|
|
22
22
|
width: '100%',
|
|
23
|
-
height: '
|
|
24
|
-
minHeight: '
|
|
23
|
+
height: 'auto',
|
|
24
|
+
minHeight: '0', // Default min height
|
|
25
|
+
maxHeight: '100%',
|
|
25
26
|
display: 'flex',
|
|
26
27
|
flexDirection: 'column',
|
|
27
28
|
|
|
@@ -113,7 +114,7 @@ export type ChartData = {
|
|
|
113
114
|
export type BasicChartProps = {
|
|
114
115
|
title?: string;
|
|
115
116
|
data: ChartData;
|
|
116
|
-
|
|
117
|
+
aspectRatio?: number;
|
|
117
118
|
width?: string | number;
|
|
118
119
|
showLegend?: boolean;
|
|
119
120
|
};
|
|
@@ -27,13 +27,11 @@ export const columnChartDemo: DemoStory<any> = {
|
|
|
27
27
|
args: {
|
|
28
28
|
title: 'Monthly Revenue',
|
|
29
29
|
width: '100%',
|
|
30
|
-
height: '350px',
|
|
31
30
|
showLegend: true,
|
|
32
31
|
},
|
|
33
32
|
argTypes: {
|
|
34
33
|
title: { control: 'text' },
|
|
35
34
|
width: { control: 'text' },
|
|
36
|
-
height: { control: 'text' },
|
|
37
35
|
showLegend: { control: 'boolean' },
|
|
38
36
|
},
|
|
39
37
|
render: (args: any) => {
|
|
@@ -79,7 +77,6 @@ export const columnChartDemo: DemoStory<any> = {
|
|
|
79
77
|
data={singleSeriesData}
|
|
80
78
|
title={args.title}
|
|
81
79
|
width={args.width}
|
|
82
|
-
height={args.height}
|
|
83
80
|
showLegend={args.showLegend}
|
|
84
81
|
yAxisFormatter={(val) => '$' + val}
|
|
85
82
|
/>
|
|
@@ -89,7 +86,7 @@ export const columnChartDemo: DemoStory<any> = {
|
|
|
89
86
|
<section class='demo-section'>
|
|
90
87
|
<div class='section-title'>Multi-Series (Grouped)</div>
|
|
91
88
|
<div class='chart-box'>
|
|
92
|
-
<ColumnChart data={multiSeriesData} title='Quarterly Product Sales'
|
|
89
|
+
<ColumnChart data={multiSeriesData} title='Quarterly Product Sales' showLegend={true} />
|
|
93
90
|
</div>
|
|
94
91
|
</section>
|
|
95
92
|
</div>
|
|
@@ -204,13 +204,16 @@ export const ColumnChart = (props: ColumnChartProps) => {
|
|
|
204
204
|
},
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
-
const
|
|
207
|
+
const ratio = props.aspectRatio ?? 16 / 9;
|
|
208
|
+
const paddingTop = `${(1 / ratio) * 100}%`;
|
|
209
|
+
|
|
210
|
+
const styleStr = `width: ${props.width || '100%'};`;
|
|
208
211
|
|
|
209
212
|
return (
|
|
210
213
|
<div ref={ref} class='&-container' style={styleStr}>
|
|
211
214
|
{props.title && <div class='chart-title'>{props.title}</div>}
|
|
212
215
|
|
|
213
|
-
<div style={{ flex: 1, position: 'relative', minHeight: 0 }}>{chartVar.node}</div>
|
|
216
|
+
<div style={{ flex: 1, position: 'relative', minHeight: 0, paddingTop }}>{chartVar.node}</div>
|
|
214
217
|
|
|
215
218
|
{showLegend && (
|
|
216
219
|
<div class='chart-legend'>
|
|
@@ -18,14 +18,12 @@ export const donutChartDemo: DemoStory<any> = {
|
|
|
18
18
|
args: {
|
|
19
19
|
title: 'Traffic Sources',
|
|
20
20
|
width: '100%',
|
|
21
|
-
height: '300px',
|
|
22
21
|
showLegend: true,
|
|
23
22
|
innerRadiusRatio: 0.6,
|
|
24
23
|
},
|
|
25
24
|
argTypes: {
|
|
26
25
|
title: { control: 'text' },
|
|
27
26
|
width: { control: 'text' },
|
|
28
|
-
height: { control: 'text' },
|
|
29
27
|
showLegend: { control: 'boolean' },
|
|
30
28
|
innerRadiusRatio: { control: 'number' },
|
|
31
29
|
},
|
|
@@ -78,7 +76,6 @@ export const donutChartDemo: DemoStory<any> = {
|
|
|
78
76
|
data={sampleData}
|
|
79
77
|
title={args.title}
|
|
80
78
|
width={args.width}
|
|
81
|
-
height={args.height}
|
|
82
79
|
showLegend={args.showLegend}
|
|
83
80
|
innerRadiusRatio={args.innerRadiusRatio}
|
|
84
81
|
/>
|
|
@@ -89,10 +86,10 @@ export const donutChartDemo: DemoStory<any> = {
|
|
|
89
86
|
<div class='section-title'>Variations</div>
|
|
90
87
|
<div class='grid'>
|
|
91
88
|
<div class='chart-box'>
|
|
92
|
-
<DonutChart data={sampleData} title='Thin Ring' innerRadiusRatio={0.8}
|
|
89
|
+
<DonutChart data={sampleData} title='Thin Ring' innerRadiusRatio={0.8} />
|
|
93
90
|
</div>
|
|
94
91
|
<div class='chart-box'>
|
|
95
|
-
<DonutChart data={sampleData} title='Thick Ring' innerRadiusRatio={0.3}
|
|
92
|
+
<DonutChart data={sampleData} title='Thick Ring' innerRadiusRatio={0.3} />
|
|
96
93
|
</div>
|
|
97
94
|
</div>
|
|
98
95
|
</section>
|
|
@@ -2,27 +2,5 @@ import { PieChartProps, PieChart } from './pie-chart';
|
|
|
2
2
|
|
|
3
3
|
// Donut Chart is just a Pie Chart with an inner radius wrapper
|
|
4
4
|
export const DonutChart = (props: Omit<PieChartProps, 'innerRadiusRatio'> & { innerRadiusRatio?: number }) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return (
|
|
8
|
-
<div
|
|
9
|
-
style={{
|
|
10
|
-
flex: 1,
|
|
11
|
-
position: 'relative',
|
|
12
|
-
display: 'flex',
|
|
13
|
-
justifyContent: 'center',
|
|
14
|
-
alignItems: 'center',
|
|
15
|
-
minHeight: '0',
|
|
16
|
-
}}
|
|
17
|
-
>
|
|
18
|
-
<svg
|
|
19
|
-
className='chart-svg' // Changed 'class' to 'className' for React
|
|
20
|
-
style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', overflow: 'visible' }}
|
|
21
|
-
viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
|
|
22
|
-
preserveAspectRatio='xMidYMid meet'
|
|
23
|
-
>
|
|
24
|
-
<PieChart {...props} innerRadiusRatio={props.innerRadiusRatio ?? 0.6} />
|
|
25
|
-
</svg>
|
|
26
|
-
</div>
|
|
27
|
-
);
|
|
5
|
+
return <PieChart {...props} innerRadiusRatio={props.innerRadiusRatio ?? 0.6} />;
|
|
28
6
|
};
|
|
@@ -8,13 +8,11 @@ export const gaugeChartDemo: DemoStory<any> = {
|
|
|
8
8
|
args: {
|
|
9
9
|
title: 'Server Load',
|
|
10
10
|
width: '100%',
|
|
11
|
-
height: '350px',
|
|
12
11
|
value: 65,
|
|
13
12
|
},
|
|
14
13
|
argTypes: {
|
|
15
14
|
title: { control: 'text' },
|
|
16
15
|
width: { control: 'text' },
|
|
17
|
-
height: { control: 'text' },
|
|
18
16
|
value: { control: 'number' },
|
|
19
17
|
},
|
|
20
18
|
render: (args: any) => {
|
|
@@ -60,7 +58,6 @@ export const gaugeChartDemo: DemoStory<any> = {
|
|
|
60
58
|
liveValue.value = (
|
|
61
59
|
<GaugeChart
|
|
62
60
|
title='CPU Usage'
|
|
63
|
-
height='300px'
|
|
64
61
|
value={Math.round(currentValue)}
|
|
65
62
|
color='#e74c3c'
|
|
66
63
|
valueFormatter={(v) => v + '%'}
|
|
@@ -79,7 +76,6 @@ export const gaugeChartDemo: DemoStory<any> = {
|
|
|
79
76
|
<GaugeChart
|
|
80
77
|
title={args.title}
|
|
81
78
|
width={args.width}
|
|
82
|
-
height={args.height}
|
|
83
79
|
value={args.value}
|
|
84
80
|
valueFormatter={(v) => v + '%'}
|
|
85
81
|
/>
|
|
@@ -63,13 +63,16 @@ export const GaugeChart = (props: GaugeChartProps) => {
|
|
|
63
63
|
globalCssId,
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
const
|
|
66
|
+
const ratio = props.aspectRatio ?? 16 / 9;
|
|
67
|
+
const paddingTop = `${(1 / ratio) * 100}%`;
|
|
68
|
+
|
|
69
|
+
const styleStr = `width: ${props.width || '100%'};`;
|
|
67
70
|
|
|
68
71
|
return (
|
|
69
72
|
<div ref={ref} class='&-container' style={styleStr}>
|
|
70
73
|
{props.title && <div class='chart-title'>{props.title}</div>}
|
|
71
74
|
|
|
72
|
-
<div style={{ flex: 1, position: 'relative', display: 'flex', justifyContent: 'center', minHeight: 0 }}>
|
|
75
|
+
<div style={{ flex: 1, position: 'relative', display: 'flex', justifyContent: 'center', minHeight: 0, paddingTop }}>
|
|
73
76
|
<svg
|
|
74
77
|
class='chart-svg'
|
|
75
78
|
style={{
|
|
@@ -21,14 +21,12 @@ export const lineChartDemo: DemoStory<any> = {
|
|
|
21
21
|
args: {
|
|
22
22
|
title: 'Exchange Rates over Week',
|
|
23
23
|
width: '100%',
|
|
24
|
-
height: '350px',
|
|
25
24
|
showLegend: true,
|
|
26
25
|
curved: false,
|
|
27
26
|
},
|
|
28
27
|
argTypes: {
|
|
29
28
|
title: { control: 'text' },
|
|
30
29
|
width: { control: 'text' },
|
|
31
|
-
height: { control: 'text' },
|
|
32
30
|
showLegend: { control: 'boolean' },
|
|
33
31
|
curved: { control: 'boolean' },
|
|
34
32
|
},
|
|
@@ -75,7 +73,6 @@ export const lineChartDemo: DemoStory<any> = {
|
|
|
75
73
|
data={multiSeriesData}
|
|
76
74
|
title={args.title}
|
|
77
75
|
width={args.width}
|
|
78
|
-
height={args.height}
|
|
79
76
|
showLegend={args.showLegend}
|
|
80
77
|
curved={args.curved}
|
|
81
78
|
/>
|
|
@@ -88,7 +85,6 @@ export const lineChartDemo: DemoStory<any> = {
|
|
|
88
85
|
<LineChart
|
|
89
86
|
data={exchangeRatesData}
|
|
90
87
|
title='Crypto Value 2024'
|
|
91
|
-
height='300px'
|
|
92
88
|
showLegend={true}
|
|
93
89
|
curved={true}
|
|
94
90
|
/>
|
|
@@ -207,13 +207,16 @@ export const LineChart = (props: LineChartProps) => {
|
|
|
207
207
|
},
|
|
208
208
|
};
|
|
209
209
|
|
|
210
|
-
const
|
|
210
|
+
const ratio = props.aspectRatio ?? 16 / 9;
|
|
211
|
+
const paddingTop = `${(1 / ratio) * 100}%`;
|
|
212
|
+
|
|
213
|
+
const styleStr = `width: ${props.width || '100%'};`;
|
|
211
214
|
|
|
212
215
|
return (
|
|
213
216
|
<div ref={ref} class='&-container' style={styleStr}>
|
|
214
217
|
{props.title && <div class='chart-title'>{props.title}</div>}
|
|
215
218
|
|
|
216
|
-
<div style={{ flex: 1, position: 'relative', minHeight: 0 }}>{chartVar.node}</div>
|
|
219
|
+
<div style={{ flex: 1, position: 'relative', minHeight: 0, paddingTop }}>{chartVar.node}</div>
|
|
217
220
|
|
|
218
221
|
{showLegend && (
|
|
219
222
|
<div class='chart-legend'>
|
|
@@ -23,13 +23,11 @@ export const pieChartDemo: DemoStory<any> = {
|
|
|
23
23
|
args: {
|
|
24
24
|
title: 'Fruit Sales',
|
|
25
25
|
width: '100%',
|
|
26
|
-
height: '300px',
|
|
27
26
|
showLegend: true,
|
|
28
27
|
},
|
|
29
28
|
argTypes: {
|
|
30
29
|
title: { control: 'text' },
|
|
31
30
|
width: { control: 'text' },
|
|
32
|
-
height: { control: 'text' },
|
|
33
31
|
showLegend: { control: 'boolean' },
|
|
34
32
|
},
|
|
35
33
|
render: (args: any) => {
|
|
@@ -81,7 +79,6 @@ export const pieChartDemo: DemoStory<any> = {
|
|
|
81
79
|
data={sampleData}
|
|
82
80
|
title={args.title}
|
|
83
81
|
width={args.width}
|
|
84
|
-
height={args.height}
|
|
85
82
|
showLegend={args.showLegend}
|
|
86
83
|
/>
|
|
87
84
|
</div>
|
|
@@ -91,10 +88,10 @@ export const pieChartDemo: DemoStory<any> = {
|
|
|
91
88
|
<div class='section-title'>Variations</div>
|
|
92
89
|
<div class='grid'>
|
|
93
90
|
<div class='chart-box'>
|
|
94
|
-
<PieChart data={sampleData} title='No Legend' showLegend={false}
|
|
91
|
+
<PieChart data={sampleData} title='No Legend' showLegend={false} />
|
|
95
92
|
</div>
|
|
96
93
|
<div class='chart-box'>
|
|
97
|
-
<PieChart data={emptyData} title='Empty Data'
|
|
94
|
+
<PieChart data={emptyData} title='Empty Data' />
|
|
98
95
|
</div>
|
|
99
96
|
<div class='chart-box'>
|
|
100
97
|
<PieChart
|
|
@@ -103,8 +100,7 @@ export const pieChartDemo: DemoStory<any> = {
|
|
|
103
100
|
series: [{ name: 'Lone', data: [100] }],
|
|
104
101
|
}}
|
|
105
102
|
title='100% Value'
|
|
106
|
-
|
|
107
|
-
/>
|
|
103
|
+
/>
|
|
108
104
|
</div>
|
|
109
105
|
<div class='chart-box'>
|
|
110
106
|
<PieChart
|
|
@@ -113,8 +109,7 @@ export const pieChartDemo: DemoStory<any> = {
|
|
|
113
109
|
series: [{ name: 'Half', data: [50, 50] }],
|
|
114
110
|
}}
|
|
115
111
|
title='50/50 Split'
|
|
116
|
-
|
|
117
|
-
/>
|
|
112
|
+
/>
|
|
118
113
|
</div>
|
|
119
114
|
</div>
|
|
120
115
|
</section>
|
|
@@ -132,7 +127,6 @@ const data = {
|
|
|
132
127
|
data={data}
|
|
133
128
|
title="Fruit Sales"
|
|
134
129
|
width="100%"
|
|
135
|
-
height="300px"
|
|
136
130
|
showLegend={true}
|
|
137
131
|
/>
|
|
138
132
|
`,
|
|
@@ -13,78 +13,94 @@ export const PieChart = (props: PieChartProps) => {
|
|
|
13
13
|
const showLegend = props.showLegend !== false;
|
|
14
14
|
const innerRadiusRatio = props.innerRadiusRatio ?? 0;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!series || !series.data || series.data.length === 0) {
|
|
16
|
+
const seriesList = props.data.series;
|
|
17
|
+
if (!seriesList || seriesList.length === 0) {
|
|
19
18
|
return <div class='&-container'>No data</div>;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
const values = series.data;
|
|
23
|
-
const total = values.reduce((sum, val) => sum + val, 0);
|
|
24
|
-
|
|
25
21
|
// SVG viewBox settings
|
|
26
22
|
const viewBoxSize = 200;
|
|
27
23
|
const center = viewBoxSize / 2;
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
24
|
+
const maxRadius = (viewBoxSize / 2) * 0.9; // 90% of container to leave margin for stroke/anti-aliasing
|
|
25
|
+
|
|
26
|
+
const globalInnerRadius = maxRadius * innerRadiusRatio;
|
|
27
|
+
const numSeries = seriesList.length;
|
|
28
|
+
// If only 1 series and innerRadiusRatio is 0, ringThickness is maxRadius.
|
|
29
|
+
// If multiple series, we divide the available space (maxRadius - globalInnerRadius) among them.
|
|
30
|
+
const ringThickness = (maxRadius - globalInnerRadius) / numSeries;
|
|
31
|
+
|
|
32
|
+
const allSlices: any[] = [];
|
|
33
|
+
|
|
34
|
+
seriesList.forEach((series, seriesIndex) => {
|
|
35
|
+
const values = series.data;
|
|
36
|
+
if (!values || values.length === 0) return;
|
|
37
|
+
|
|
38
|
+
const total = values.reduce((sum, val) => sum + val, 0);
|
|
39
|
+
let currentAngle = 0;
|
|
40
|
+
|
|
41
|
+
// Outermost ring is series 0
|
|
42
|
+
const outerR = maxRadius - seriesIndex * ringThickness;
|
|
43
|
+
const innerR = outerR - ringThickness;
|
|
44
|
+
|
|
45
|
+
values.forEach((val, index) => {
|
|
46
|
+
// Prevent 0 area slices. Also handle case where 1 val is 100%
|
|
47
|
+
const ratio = total > 0 ? val / total : 0;
|
|
48
|
+
const angleDelta = ratio * 360;
|
|
49
|
+
|
|
50
|
+
// Fix for 100% slice (SVG arcs don't draw well at exactly 360)
|
|
51
|
+
const isFullCircle = angleDelta === 360;
|
|
52
|
+
const adjustedAngleDelta = isFullCircle ? 359.99 : angleDelta;
|
|
53
|
+
|
|
54
|
+
const startAngle = currentAngle;
|
|
55
|
+
const endAngle = currentAngle + adjustedAngleDelta;
|
|
56
|
+
currentAngle += adjustedAngleDelta;
|
|
57
|
+
|
|
58
|
+
const pathD = describeArc(center, center, outerR, startAngle, endAngle, innerR);
|
|
59
|
+
const color = getChartColor(index);
|
|
60
|
+
const label = props.data.labels[index] || `Item ${index + 1}`;
|
|
61
|
+
|
|
62
|
+
const handleMouseEnter = (e: any) => {
|
|
63
|
+
const percentage = (ratio * 100).toFixed(1) + '%';
|
|
64
|
+
Tooltip.show(
|
|
65
|
+
e,
|
|
66
|
+
<div>
|
|
67
|
+
<div style={{ fontWeight: 'bold' }}>{series.name ? `${series.name} - ` : ''}{label}</div>
|
|
68
|
+
<div>Value: {val}</div>
|
|
69
|
+
<div>Share: {percentage}</div>
|
|
70
|
+
</div>,
|
|
71
|
+
{ position: 'auto' }
|
|
72
|
+
);
|
|
73
|
+
e.target.style.opacity = 0.8;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const handleMouseLeave = (e: any) => {
|
|
77
|
+
Tooltip.hide();
|
|
78
|
+
e.target.style.opacity = 1;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
allSlices.push(
|
|
82
|
+
<path
|
|
83
|
+
class='chart-element'
|
|
84
|
+
d={pathD}
|
|
85
|
+
fill={color}
|
|
86
|
+
stroke='var(--primary-bg-color)' // border color between slices
|
|
87
|
+
stroke-width={isFullCircle ? '0' : '1'}
|
|
88
|
+
onMouseEnter={handleMouseEnter}
|
|
89
|
+
onMouseLeave={handleMouseLeave}
|
|
90
|
+
style={{ transition: 'opacity 0.2s' }}
|
|
91
|
+
/>
|
|
60
92
|
);
|
|
61
|
-
|
|
62
|
-
e.target.style.opacity = 0.8;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const handleMouseLeave = (e: any) => {
|
|
66
|
-
Tooltip.hide();
|
|
67
|
-
e.target.style.opacity = 1;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<path
|
|
72
|
-
class='chart-element'
|
|
73
|
-
d={pathD}
|
|
74
|
-
fill={color}
|
|
75
|
-
stroke='var(--primary-bg-color)' // border color between slices
|
|
76
|
-
stroke-width={isFullCircle ? '0' : '1'}
|
|
77
|
-
onMouseEnter={handleMouseEnter}
|
|
78
|
-
onMouseLeave={handleMouseLeave}
|
|
79
|
-
/>
|
|
80
|
-
);
|
|
93
|
+
});
|
|
81
94
|
});
|
|
82
95
|
|
|
83
96
|
const ref: RefProps = {
|
|
84
97
|
globalCssId,
|
|
85
98
|
};
|
|
86
99
|
|
|
87
|
-
const
|
|
100
|
+
const ratio = props.aspectRatio ?? 16 / 9;
|
|
101
|
+
const paddingTop = `${(1 / ratio) * 100}%`;
|
|
102
|
+
|
|
103
|
+
const styleStr = `width: ${props.width || '100%'}; min-height: 200px;`;
|
|
88
104
|
|
|
89
105
|
return (
|
|
90
106
|
<div ref={ref} class='&-container' style={styleStr}>
|
|
@@ -98,6 +114,7 @@ export const PieChart = (props: PieChartProps) => {
|
|
|
98
114
|
justifyContent: 'center',
|
|
99
115
|
alignItems: 'center',
|
|
100
116
|
minHeight: '0',
|
|
117
|
+
paddingTop,
|
|
101
118
|
}}
|
|
102
119
|
>
|
|
103
120
|
<svg
|
|
@@ -106,13 +123,13 @@ export const PieChart = (props: PieChartProps) => {
|
|
|
106
123
|
viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
|
|
107
124
|
preserveAspectRatio='xMidYMid meet'
|
|
108
125
|
>
|
|
109
|
-
<g>{
|
|
126
|
+
<g>{allSlices}</g>
|
|
110
127
|
</svg>
|
|
111
128
|
</div>
|
|
112
129
|
|
|
113
130
|
{showLegend && (
|
|
114
131
|
<div class='chart-legend'>
|
|
115
|
-
{
|
|
132
|
+
{props.data.labels.map((_, i) => (
|
|
116
133
|
<div class='legend-item'>
|
|
117
134
|
<div class='legend-color' style={{ backgroundColor: getChartColor(i) }} />
|
|
118
135
|
<div>{props.data.labels[i] || `Item ${i + 1}`}</div>
|
|
@@ -16,13 +16,11 @@ export const radarChartDemo: DemoStory<any> = {
|
|
|
16
16
|
args: {
|
|
17
17
|
title: 'Character Stats',
|
|
18
18
|
width: '100%',
|
|
19
|
-
height: '400px',
|
|
20
19
|
showLegend: true,
|
|
21
20
|
},
|
|
22
21
|
argTypes: {
|
|
23
22
|
title: { control: 'text' },
|
|
24
23
|
width: { control: 'text' },
|
|
25
|
-
height: { control: 'text' },
|
|
26
24
|
showLegend: { control: 'boolean' },
|
|
27
25
|
},
|
|
28
26
|
render: (args: any) => {
|
|
@@ -68,7 +66,6 @@ export const radarChartDemo: DemoStory<any> = {
|
|
|
68
66
|
data={statsData}
|
|
69
67
|
title={args.title}
|
|
70
68
|
width={args.width}
|
|
71
|
-
height={args.height}
|
|
72
69
|
showLegend={args.showLegend}
|
|
73
70
|
/>
|
|
74
71
|
</div>
|