forked from cloudscape-design/chart-components
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchart-pie-internal.tsx
More file actions
146 lines (132 loc) · 6.08 KB
/
chart-pie-internal.tsx
File metadata and controls
146 lines (132 loc) · 6.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { forwardRef, useImperativeHandle, useRef } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import type Highcharts from "highcharts";
import { useControllableState } from "@cloudscape-design/component-toolkit";
import { InternalCoreChart } from "../core/chart-core";
import { CoreChartProps } from "../core/interfaces";
import { getOptionsId } from "../core/utils";
import { InternalBaseComponentProps } from "../internal/base-component/use-base-component";
import * as Styles from "../internal/chart-styles";
import { fireNonCancelableEvent } from "../internal/events";
import { SomeRequired, Writeable } from "../internal/utils/utils";
import { useInnerArea } from "./chart-inner-area";
import { PieChartProps } from "./interfaces";
import testClasses from "./test-classes/styles.css.js";
interface InternalPieChartProps extends InternalBaseComponentProps, Omit<PieChartProps, "series"> {
series: readonly PieChartProps.SeriesOptions[];
tooltip: SomeRequired<PieChartProps.TooltipOptions, "enabled" | "size">;
legend: SomeRequired<PieChartProps.LegendOptions, "enabled">;
}
export const InternalPieChart = forwardRef(
({ series: originalSeries, tooltip, ...props }: InternalPieChartProps, ref: React.Ref<PieChartProps.Ref>) => {
const apiRef = useRef<null | CoreChartProps.ChartAPI>(null);
// When visibleSegments and onVisibleSegmentsChange are provided - the segments visibility can be controlled from the outside.
// Otherwise - the component handles segments visibility using its internal state.
useControllableState(props.visibleSegments, props.onVisibleSegmentsChange, undefined, {
componentName: "PieChart",
propertyName: "visibleSegments",
changeHandlerName: "onVisibleSegmentsChange",
});
const allSegmentIds = originalSeries.flatMap((s) => s.data.map((d) => getOptionsId(d)));
const onVisibleSegmentsChange: CoreChartProps["onVisibleItemsChange"] = ({ detail: { items } }) => {
const visibleSegments = items.filter((i) => i.visible).map((i) => i.id);
fireNonCancelableEvent(props.onVisibleSegmentsChange, { visibleSegments });
};
// Converting donut series to Highcharts pie series.
const series: Highcharts.SeriesOptionsType[] = originalSeries.map((s) => {
const data = s.data as Writeable<PieChartProps.PieSegmentOptions[]>;
const style = s.type === "pie" ? Styles.pieSeries : Styles.donutSeries;
return { ...s, type: "pie", data, ...style };
});
// Pie chart imperative API.
useImperativeHandle(ref, () => ({
setVisibleSegments: (visibleSegmentsIds) => apiRef.current?.setItemsVisible(visibleSegmentsIds),
showAllSegments: () => apiRef.current?.setItemsVisible(allSegmentIds),
}));
// Get inner area title and description for donut chart.
const innerArea = useInnerArea(originalSeries, props);
// We convert pie tooltip options to the core chart's getTooltipContent callback,
// ensuring no internal types are exposed to the consumer-defined render functions.
const getTooltipContent: CoreChartProps["getTooltipContent"] = () => {
const transformDetailsProps = ({
point,
}: CoreChartProps.TooltipDetailsProps): PieChartProps.TooltipDetailsRenderProps => {
return {
totalValue: point.total ?? 0,
segmentValue: point.y ?? 0,
segmentId: getOptionsId(point.options),
segmentName: point.name ?? "",
};
};
const transformSlotProps = (props: CoreChartProps.TooltipSlotProps): PieChartProps.TooltipDetailsRenderProps => {
const point = props.items[0].point;
return transformDetailsProps({ point, hideTooltip: props.hideTooltip });
};
return {
header: tooltip?.header ? (props) => tooltip.header!(transformSlotProps(props)) : undefined,
details: tooltip?.details ? (props) => tooltip.details!(transformDetailsProps(props)) : undefined,
body: tooltip?.body ? (props) => tooltip.body!(transformSlotProps(props)) : undefined,
footer: tooltip?.footer ? (props) => tooltip.footer!(transformSlotProps(props)) : undefined,
};
};
// Convert pie segment props to Highcharts segment data labels.
const segmentDataLabels:
| Highcharts.SeriesPieDataLabelsOptionsObject
| Highcharts.SeriesPieDataLabelsOptionsObject[] = {
position: "left",
formatter() {
const { segmentTitle, segmentDescription } = props;
const segmentProps = {
// For pie series, we expect y, total, and name to be always present, and y to be non-null for any visible segment.
// However, these are optional in Highcharts types, so we need to do a fallback.
totalValue: this.total ?? 0,
segmentValue: this.y ?? 0,
segmentId: this.options.id,
segmentName: this.options.name ?? "",
};
const title = segmentTitle ? segmentTitle(segmentProps) : this.name;
const description = segmentDescription?.(segmentProps);
if (title || description) {
return renderToStaticMarkup(
<text>
{title ? <tspan>{title}</tspan> : null}
<br />
{description ? <tspan style={Styles.segmentDescriptionCss}>{description}</tspan> : null}
</text>,
);
}
return null;
},
useHTML: false,
};
const highchartsOptions: Highcharts.Options = {
chart: {
events: {
render(event) {
innerArea.onChartRender.call(this, event);
},
},
},
plotOptions: {
pie: {
dataLabels: segmentDataLabels,
},
},
series,
};
return (
<InternalCoreChart
{...props}
callback={(api) => (apiRef.current = api)}
options={highchartsOptions}
tooltip={tooltip}
getTooltipContent={getTooltipContent}
visibleItems={props.visibleSegments}
onVisibleItemsChange={onVisibleSegmentsChange}
className={testClasses.root}
/>
);
},
);