recharts#AreaChart TypeScript Examples
The following examples show how to use
recharts#AreaChart.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From vvs-ui with GNU General Public License v3.0 | 5 votes |
LineChart = ({ data, setHoverValue, setHoverDate }: LineChartProps) => {
const { theme } = useTheme()
if (!data || data.length === 0) {
return <LineChartLoader />
}
return (
<ResponsiveContainer>
<AreaChart
data={data}
width={300}
height={308}
margin={{
top: 5,
right: 15,
left: 0,
bottom: 5,
}}
onMouseLeave={() => {
if (setHoverDate) setHoverDate(undefined)
if (setHoverValue) setHoverValue(undefined)
}}
>
<XAxis
dataKey="time"
axisLine={false}
tickLine={false}
tickFormatter={(time) => format(time, 'dd')}
minTickGap={10}
/>
<YAxis
dataKey="value"
tickCount={6}
scale="linear"
axisLine={false}
tickLine={false}
fontSize="12px"
tickFormatter={(val) => `$${formatAmount(val)}`}
orientation="right"
tick={{ dx: 10, fill: theme.colors.textSubtle }}
/>
<Tooltip
cursor={{ stroke: theme.colors.primary }}
contentStyle={{ display: 'none' }}
formatter={(tooltipValue, name, props) => (
<HoverUpdater payload={props.payload} setHoverValue={setHoverValue} setHoverDate={setHoverDate} />
)}
/>
<Area
dataKey="value"
type="monotone"
stroke={theme.colors.primary}
fill={theme.colors.lightBlue}
strokeWidth={2}
/>
</AreaChart>
</ResponsiveContainer>
)
}
Example #2
Source File: Question.tsx From project-loved-web with MIT License | 5 votes |
function ComparingChart({ answers, comparingStatistic }: ComparingChartProps) {
const intl = useIntl();
const colors = useColors();
const xAxisProps: XAxisProps = {
dataKey: 'statistic',
interval: 'preserveStartEnd',
stroke: colors.content,
tick: { fill: colors.content },
tickLine: { stroke: colors.content },
};
if (comparingStatistic === 'rank') {
xAxisProps.domain = ['dataMin', 'dataMax'];
xAxisProps.scale = 'log';
xAxisProps.tickFormatter = (value) => intl.formatNumber(value);
xAxisProps.type = 'number';
}
return (
<AreaChart data={answers} width={700} height={175}>
<defs>
<linearGradient id='answerColor' x1='0' y1='0' x2='0' y2='1'>
<stop offset='0%' stopColor={colors['rating-2']} stopOpacity={0.9} />
<stop offset='20%' stopColor={colors['rating-1']} stopOpacity={0.8} />
<stop offset='40%' stopColor={colors['rating-0']} stopOpacity={0.7} />
<stop offset='60%' stopColor={colors['rating--1']} stopOpacity={0.6} />
<stop offset='80%' stopColor={colors['rating--2']} stopOpacity={0.5} />
<stop offset='100%' stopColor={colors['rating--2']} stopOpacity={0} />
</linearGradient>
</defs>
<XAxis {...xAxisProps} />
<YAxis dataKey='average' domain={[0, 5]} hide />
<Area
type='monotone'
dataKey='average'
stroke={colors.content}
fillOpacity={1}
fill='url(#answerColor)'
/>
</AreaChart>
);
}
Example #3
Source File: index.tsx From glide-frontend with GNU General Public License v3.0 | 5 votes |
LineChart = ({ data, setHoverValue, setHoverDate }: LineChartProps) => {
const { theme } = useTheme()
if (!data || data.length === 0) {
return <LineChartLoader />
}
return (
<ResponsiveContainer>
<AreaChart
data={data}
width={300}
height={308}
margin={{
top: 5,
right: 15,
left: 0,
bottom: 5,
}}
onMouseLeave={() => {
if (setHoverDate) setHoverDate(undefined)
if (setHoverValue) setHoverValue(undefined)
}}
>
<defs>
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={theme.colors.inputSecondary} stopOpacity={0.5} />
<stop offset="100%" stopColor={theme.colors.secondary} stopOpacity={0} />
</linearGradient>
</defs>
<XAxis
dataKey="time"
axisLine={false}
tickLine={false}
tickFormatter={(time) => format(time, 'dd')}
minTickGap={10}
/>
<YAxis
dataKey="value"
tickCount={6}
scale="linear"
axisLine={false}
tickLine={false}
fontSize="12px"
tickFormatter={(val) => `$${formatAmount(val)}`}
orientation="right"
tick={{ dx: 10, fill: theme.colors.textSubtle }}
/>
<Tooltip
cursor={{ stroke: theme.colors.secondary }}
contentStyle={{ display: 'none' }}
formatter={(tooltipValue, name, props) => (
<HoverUpdater payload={props.payload} setHoverValue={setHoverValue} setHoverDate={setHoverDate} />
)}
/>
<Area dataKey="value" type="monotone" stroke={theme.colors.secondary} fill="url(#gradient)" strokeWidth={2} />
</AreaChart>
</ResponsiveContainer>
)
}
Example #4
Source File: ChartDetailsSinglestat.tsx From kubenav with MIT License | 5 votes |
ChartDetailsSinglestat: React.FunctionComponent<IChartDetailsSinglestatProps> = ({
unit,
results,
}: IChartDetailsSinglestatProps) => {
const context = useContext<IContext>(AppContext);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const series: any = [];
for (let i = 0; i < results.length; i++) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = [];
for (let j = 0; j < results[i].values.length; j++) {
data.push({
time: results[i].values[j][0],
value: parseFloat(results[i].values[j][1]),
});
}
series.push({ name: results[i].label, data: data });
}
return (
<IonRow
style={{
fontSize: '20px',
fontWeight: 500,
color: isDarkMode(context.settings.theme)
? isPlatform('ios')
? '#ffffff'
: '#dbdbdb'
: isPlatform('ios')
? '#000000'
: '#262626',
height: '100px',
width: '100%',
}}
>
{series.length > 0 ? (
<div
style={{
margin: 'auto',
left: 0,
right: 0,
top: 40,
position: 'absolute',
display: 'flex',
justifyContent: 'center',
zIndex: 1000,
}}
>
{series[0].data[series[0].data.length - 1].value.toFixed(2)} {unit ? unit : ''}
</div>
) : null}
<IonCol style={{ padding: '0px' }}>
<ResponsiveContainer>
<AreaChart>
{series.map((serie, index) => (
<Area
key={index}
dataKey="value"
// NOTE: https://github.com/recharts/recharts/issues/2487
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data={serie.data}
name={serie.name}
stroke="#326ce5"
fill="#326ce5"
fillOpacity={0.2}
/>
))}
</AreaChart>
</ResponsiveContainer>
</IonCol>
</IonRow>
);
}
Example #5
Source File: OrderChart.tsx From ra-enterprise-demo with MIT License | 5 votes |
OrderChart: FC<{ orders?: Order[] }> = ({ orders }) => {
const translate = useTranslate();
if (!orders) return null;
return (
<Card>
<CardHeader title={translate('pos.dashboard.month_history')} />
<CardContent>
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<AreaChart data={getRevenuePerDay(orders)}>
<defs>
<linearGradient
id="colorUv"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor="#8884d8"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="#8884d8"
stopOpacity={0}
/>
</linearGradient>
</defs>
<XAxis
dataKey="date"
name="Date"
type="number"
scale="time"
domain={[
addDays(aMonthAgo, 1).getTime(),
new Date().getTime(),
]}
tickFormatter={dateFormatter}
/>
<YAxis dataKey="total" name="Revenue" unit="€" />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip
cursor={{ strokeDasharray: '3 3' }}
formatter={(value): string =>
new Intl.NumberFormat(undefined, {
style: 'currency',
currency: 'USD',
}).format(value as any)
}
labelFormatter={(label: any): string =>
dateFormatter(label)
}
/>
<Area
type="monotone"
dataKey="total"
stroke="#8884d8"
strokeWidth={2}
fill="url(#colorUv)"
/>
</AreaChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
);
}
Example #6
Source File: ClaimGraph.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 5 votes |
ClaimGraph: FC = () => {
const rewardsEarned = useRewardsEarned()
const data = useMemo<DataType[]>(() => {
return [
{
mta: 0,
ordering: 0,
},
{
mta: rewardsEarned?.rewards ?? 0,
ordering: 1,
},
]
}, [rewardsEarned])
return (
<Container>
<ResponsiveContainer width="100%" aspect={1.75}>
<AreaChart data={data}>
<defs>
<linearGradient id="area" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={Color.blue} stopOpacity={0.5} />
<stop offset="95%" stopColor={Color.blue} stopOpacity={0} />
</linearGradient>
</defs>
<XAxis
dataKey="ordering"
tickFormatter={ordering => (ordering === 0 ? 'Last claim' : 'Now')}
axisLine={false}
padding={{ left: 16 }}
tickLine={false}
/>
<YAxis tickCount={2} tickFormatter={m => `${m}`} axisLine={false} padding={{ bottom: 16 }} tickLine={false} width={32} />
<Tooltip
cursor
label=""
labelFormatter={w => (w === 0 ? 'Last claim' : 'Available to claim')}
formatter={mta => `${(mta as number).toFixed(2)} MTA`}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
<Area type="monotone" name={'Earned: '} dataKey="mta" stroke={Color.blue} strokeWidth={2} fill="url(#area)" />
</AreaChart>
</ResponsiveContainer>
</Container>
)
}
Example #7
Source File: StakeGraph.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 5 votes |
StakeGraph: FC = () => {
const { data: tokenData } = useStakedTokenQuery()
const weightedTimestamp = tokenData?.stakedToken?.accounts?.[0]?.balance?.weightedTimestamp
const data = generateData(weightedTimestamp)
const ticks = removeDuplicatesBy(x => x.multiplier, data).map(v => v.week)
return (
<Container>
<ResponsiveContainer width="100%" aspect={1.75}>
<AreaChart data={data}>
<defs>
<linearGradient id="area" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={Color.blue} stopOpacity={0.5} />
<stop offset="95%" stopColor={Color.blue} stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="week" tickFormatter={w => `${w / WEEK}`} axisLine={false} padding={{ left: 16 }} tickLine={false} ticks={ticks} />
<YAxis
domain={['dataMin', 'dataMax']}
tickCount={4}
tickFormatter={m => `${m}x`}
axisLine={false}
padding={{ bottom: 16 }}
tickLine={false}
width={32}
/>
<Tooltip
cursor
labelFormatter={week => `+${(week as number) / WEEK} weeks`}
formatter={multiplier => multiplier}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
<Area type="stepAfter" name={`multiplier: `} dataKey="multiplier" stroke={Color.blue} strokeWidth={2} fill="url(#area)" />
</AreaChart>
</ResponsiveContainer>
</Container>
)
}
Example #8
Source File: WithdrawGraph.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 5 votes |
WithdrawGraph: FC = () => {
const { data } = useStakedTokenQuery() ?? {}
const weightedTimestamp = data?.stakedToken?.accounts?.[0]?.balance?.weightedTimestamp ?? nowUnix
const graphData = useMemo(() => {
const weeksStaked = (nowUnix - weightedTimestamp) / WEEK
const data = generateData(weeksStaked)
const ticks = [...new Set(data.map(d => d.week))]
return { data, ticks }
}, [weightedTimestamp])
return (
<Container>
<ResponsiveContainer width="100%" aspect={1.75}>
<AreaChart data={graphData.data}>
<defs>
<linearGradient id="area" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={Color.blue} stopOpacity={0.5} />
<stop offset="95%" stopColor={Color.blue} stopOpacity={0} />
</linearGradient>
</defs>
<XAxis
dataKey="week"
tickFormatter={w => `${w / WEEK}`}
axisLine={false}
padding={{ left: 16 }}
tickLine={false}
ticks={graphData.ticks}
/>
<YAxis tickCount={2} tickFormatter={m => `${m}%`} axisLine={false} padding={{ bottom: 16 }} tickLine={false} width={24} />
<Tooltip
cursor
labelFormatter={week => `+${(week as number) / WEEK} weeks`}
formatter={fee => `${(fee as number).toFixed(4)}%`}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
<Area type="monotone" name={'Fee: '} dataKey="fee" stroke={Color.blue} strokeWidth={2} fill="url(#area)" />
</AreaChart>
</ResponsiveContainer>
</Container>
)
}
Example #9
Source File: STResults.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
STResults = ({ classes, results, start }: ISTResults) => {
const [jsonView, setJsonView] = useState<boolean>(false);
const finalRes = results[results.length - 1] || [];
const getServers: STServer[] = get(finalRes, "GETStats.servers", []) || [];
const putServers: STServer[] = get(finalRes, "PUTStats.servers", []) || [];
const getThroughput = get(finalRes, "GETStats.throughputPerSec", 0);
const getObjects = get(finalRes, "GETStats.objectsPerSec", 0);
const putThroughput = get(finalRes, "PUTStats.throughputPerSec", 0);
const putObjects = get(finalRes, "PUTStats.objectsPerSec", 0);
let statJoin: IndvServerMetric[] = [];
getServers.forEach((item) => {
const hostName = item.endpoint;
const putMetric = putServers.find((item) => item.endpoint === hostName);
let itemJoin: IndvServerMetric = {
getUnit: "-",
getValue: "N/A",
host: item.endpoint,
putUnit: "-",
putValue: "N/A",
};
if (item.err && item.err !== "") {
itemJoin.getError = item.err;
itemJoin.getUnit = "-";
itemJoin.getValue = "N/A";
} else {
const niceGet = calculateBytes(item.throughputPerSec.toString());
itemJoin.getUnit = niceGet.unit;
itemJoin.getValue = niceGet.total.toString();
}
if (putMetric) {
if (putMetric.err && putMetric.err !== "") {
itemJoin.putError = putMetric.err;
itemJoin.putUnit = "-";
itemJoin.putValue = "N/A";
} else {
const nicePut = calculateBytes(putMetric.throughputPerSec.toString());
itemJoin.putUnit = nicePut.unit;
itemJoin.putValue = nicePut.total.toString();
}
}
statJoin.push(itemJoin);
});
const downloadResults = () => {
const date = new Date();
let element = document.createElement("a");
element.setAttribute(
"href",
"data:text/plain;charset=utf-8," + JSON.stringify(finalRes)
);
element.setAttribute(
"download",
`speedtest_results-${date.toISOString()}.log`
);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const toggleJSONView = () => {
setJsonView(!jsonView);
};
const finalResJSON = finalRes ? JSON.stringify(finalRes, null, 4) : "";
const clnMetrics = cleanMetrics(results);
return (
<Fragment>
<Grid container className={classes.objectGeneral}>
<Grid item xs={12} md={6} lg={6}>
<Grid container className={classes.objectGeneral}>
<Grid item xs={12} md={6} lg={6}>
<SpeedTestUnit
icon={
<div className={classes.download}>
<DownloadStatIcon />
</div>
}
title={"GET"}
throughput={getThroughput}
objects={getObjects}
/>
</Grid>
<Grid item xs={12} md={6} lg={6}>
<SpeedTestUnit
icon={
<div className={classes.upload}>
<UploadStatIcon />
</div>
}
title={"PUT"}
throughput={putThroughput}
objects={putObjects}
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6} lg={6}>
<ResponsiveContainer width="99%">
<AreaChart data={clnMetrics}>
<defs>
<linearGradient id="colorPut" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#2781B0" stopOpacity={0.9} />
<stop offset="95%" stopColor="#fff" stopOpacity={0} />
</linearGradient>
<linearGradient id="colorGet" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#4CCB92" stopOpacity={0.9} />
<stop offset="95%" stopColor="#fff" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid
strokeDasharray={"0 0"}
strokeWidth={1}
strokeOpacity={0.5}
stroke={"#F1F1F1"}
vertical={false}
/>
<Area
type="monotone"
dataKey={"get"}
stroke={"#4CCB92"}
fill={"url(#colorGet)"}
fillOpacity={0.3}
strokeWidth={2}
dot={false}
/>
<Area
type="monotone"
dataKey={"put"}
stroke={"#2781B0"}
fill={"url(#colorPut)"}
fillOpacity={0.3}
strokeWidth={2}
dot={false}
/>
</AreaChart>
</ResponsiveContainer>
</Grid>
</Grid>
<br />
{clnMetrics.length > 1 && (
<Fragment>
<Grid container>
<Grid item xs={12} md={6} className={classes.descriptorLabel}>
{start ? (
<Fragment>Preliminar Results:</Fragment>
) : (
<Fragment>
{jsonView ? "JSON Results:" : "Detailed Results:"}
</Fragment>
)}
</Grid>
<Grid item xs={12} md={6} className={classes.actionButtons}>
{!start && (
<Fragment>
<BoxIconButton
aria-label="Download"
onClick={downloadResults}
size="large"
>
<DownloadIcon />
</BoxIconButton>
<BoxIconButton
aria-label="Download"
onClick={toggleJSONView}
size="large"
>
<JSONIcon />
</BoxIconButton>
</Fragment>
)}
</Grid>
</Grid>
<Grid container className={classes.resultsContainer}>
{jsonView ? (
<Fragment>
<CodeMirrorWrapper
value={finalResJSON}
readOnly
onBeforeChange={() => {}}
/>
</Fragment>
) : (
<Fragment>
<Grid
item
xs={12}
sm={12}
md={1}
lg={1}
className={classes.resultsIcon}
alignItems={"flex-end"}
>
<ComputerLineIcon width={45} />
</Grid>
<Grid
item
xs={12}
sm={6}
md={3}
lg={2}
className={classes.detailedItem}
>
Nodes: <strong>{finalRes.servers}</strong>
</Grid>
<Grid
item
xs={12}
sm={6}
md={3}
lg={2}
className={classes.detailedItem}
>
Drives: <strong>{finalRes.disks}</strong>
</Grid>
<Grid
item
xs={12}
sm={6}
md={3}
lg={2}
className={classes.detailedItem}
>
Concurrent: <strong>{finalRes.concurrent}</strong>
</Grid>
<Grid
item
xs={12}
sm={12}
md={12}
lg={5}
className={classes.detailedVersion}
>
<span className={classes.versionIcon}>
<VersionIcon />
</span>{" "}
MinIO VERSION <strong>{finalRes.version}</strong>
</Grid>
<Grid item xs={12} className={classes.tableOverflow}>
<table
className={classes.serversTable}
cellSpacing={0}
cellPadding={0}
>
<thead>
<tr>
<th colSpan={2}>Servers</th>
<th>GET</th>
<th>PUT</th>
</tr>
</thead>
<tbody>
{statJoin.map((stats, index) => (
<tr key={`storage-${index.toString()}`}>
<td className={classes.serverIcon}>
<StorageIcon />
</td>
<td className={classes.serverHost}>{stats.host}</td>
{stats.getError && stats.getError !== "" ? (
<td>{stats.getError}</td>
) : (
<Fragment>
<td className={classes.serverValue}>
{prettyNumber(parseFloat(stats.getValue))}
{stats.getUnit}/s.
</td>
</Fragment>
)}
{stats.putError && stats.putError !== "" ? (
<td>{stats.putError}</td>
) : (
<Fragment>
<td className={classes.serverValue}>
{prettyNumber(parseFloat(stats.putValue))}
{stats.putUnit}/s.
</td>
</Fragment>
)}
</tr>
))}
</tbody>
</table>
</Grid>
</Fragment>
)}
</Grid>
</Fragment>
)}
</Fragment>
);
}
Example #10
Source File: LiquidityChart.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 4 votes |
Chart: FC<{
aggregateMetrics: {
type: string
enabled: boolean
label: string
color: string
}[]
}> = ({ aggregateMetrics }) => {
const poolAddress = useSelectedFeederPoolAddress()
const data = useTotalLiquidity(poolAddress)
const dateFilter = useDateFilter()
const { metrics } = useMetricsState()
return (
<RechartsContainer>
{data.length < 2 && <NoData>No data yet</NoData>}
<ResponsiveContainer aspect={2} debounce={1} width="99%">
<AreaChart margin={{ top: 40, right: 0, bottom: 0, left: 0 }} barCategoryGap={1} data={data.length > 1 ? data : []}>
<defs>
{aggregateMetrics.map(({ type, color }) => (
<linearGradient id={type} key={type} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color} stopOpacity={0.5} />
<stop offset="95%" stopColor={color} stopOpacity={0} />
</linearGradient>
))}
</defs>
<XAxis
dataKey="timestamp"
axisLine={false}
xAxisId={0}
height={0}
tick={false}
tickFormatter={timestamp => (timestamp ? format(timestamp * 1000, periodFormatMapping[dateFilter.period]) : '')}
/>
<YAxis
type="number"
orientation="left"
tickFormatter={toK}
axisLine={false}
interval="preserveEnd"
yAxisId={0}
tick={false}
width={0}
/>
<Tooltip
cursor
labelFormatter={timestamp => format((timestamp as number) * 1000, 'yyyy-MM-dd HH:mm')}
formatter={toK as never}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
{metrics.map(({ color, type, label, enabled }) => (
<Area
animationDuration={100}
key={type}
type="monotone"
hide={!enabled}
dataKey={type}
name={`${label} `}
opacity={1}
dot={false}
yAxisId={0}
stroke={color}
strokeWidth={2}
fill={`url(#${type})`}
/>
))}
</AreaChart>
</ResponsiveContainer>
</RechartsContainer>
)
}
Example #11
Source File: ClaimGraph.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 4 votes |
ClaimGraph: FC = () => {
const rewardStreams = useRewardStreams()
const chart = useMemo<Chart>(() => {
if (!rewardStreams) return { maxY: 0, groups: [] }
// Filter for selected types
const filtered = rewardStreams.chartData
// Group into ranges
const ranges = filtered
.map(({ t }) => t)
.reduce<[number, number][]>((prev, x, idx) => {
if (idx === 0) return [[x, x]]
const last = prev[prev.length - 1]
// More than 2 months? Too much of a gap, make a new group
// Otherwise the chart is hard to read
// TODO needs more logic here... if I didn't claim for more than that
// time since I started earning, it won't show correctly
// if (x - last[1] > 5184000) {
// return [...prev, [x, x]];
// }
return [...prev.slice(0, -1), [last[0], x]]
}, [])
// Find the max Y value to use a common scale
const maxY = filtered.reduce(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(prev, { t, ...datum }) => Math.max(prev, ...(Object.values(datum) as number[])),
0,
)
return {
maxY,
groups: ranges
.map(range => {
const data = filtered.filter(datum => datum.t >= range[0] && datum.t <= range[1])
const types = Array.from(
new Set(
data.reduce<StreamType[]>(
(prev, datum) => [
...prev,
...Object.entries(datum)
.filter(([k, v]) => k !== 't' && v)
.map(([k]) => k as unknown as StreamType),
],
[],
),
),
)
return {
range,
data,
types,
}
})
.filter(group => group.data.length > 1),
}
}, [rewardStreams])
if (!rewardStreams) return null
return (
<ChartContainer key={chart.groups.length}>
{chart.groups.map(({ data, types, range }) => (
<ResponsiveContainer maxHeight={200} aspect={3} key={range[0]}>
<AreaChart data={data} margin={{ top: 20, left: 0, right: 0, bottom: 0 }}>
<defs>
{Object.keys(dataTypes).map(streamType => (
<linearGradient id={`type-${streamType}`} key={streamType} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={rewardsColorMapping[streamType as unknown as StreamType].fill1} />
<stop offset="95%" stopColor={rewardsColorMapping[streamType as unknown as StreamType].fill2} />
</linearGradient>
))}
</defs>
<Tooltip
cursor
labelFormatter={tooltipFormatter}
formatter={toK as never}
separator=" "
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
<XAxis
scale="time"
interval="preserveStartEnd"
domain={[range[0], 'auto']}
type="number"
dataKey="t"
tickFormatter={xAxisFormatter}
stroke={Color.greyTransparent}
padding={{ left: 40, right: 80 }}
/>
<YAxis
type="number"
domain={[0, chart.maxY]}
orientation="left"
tickFormatter={toK}
axisLine={false}
interval={100}
// interval="preserveEnd"
tick={false}
/>
<ReferenceLine x={rewardStreams?.currentTime} stroke={Color.greyTransparent}>
<Label position="insideTopRight" value="Unclaimed" fontSize={14} dx={6} dy={-20} />
</ReferenceLine>
{rewardStreams?.nextUnlock && (
<ReferenceLine x={rewardStreams.nextUnlock} stroke={Color.greyTransparent}>
<Label position="insideTopLeft" value="Next unlock" fontSize={14} dy={-20} dx={-6} />
</ReferenceLine>
)}
{rewardStreams && (
<ReferenceLine x={rewardStreams.previewStream.start} stroke={Color.greyTransparent}>
<Label position="insideTopLeft" value="New unlock" fontSize={14} />
</ReferenceLine>
)}
{types.flatMap(streamType =>
(dataTypes[streamType].subTypes ?? [streamType]).map(subType => (
<Area
key={subType}
dataKey={subType}
name={dataTypes[subType].label}
dot={false}
strokeWidth={0}
stroke={rewardsColorMapping[subType].point}
stackId={1}
fill={`url(#type-${subType})`}
fillOpacity={1}
/>
)),
)}
</AreaChart>
</ResponsiveContainer>
))}
</ChartContainer>
)
}
Example #12
Source File: VolumeChart.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 4 votes |
Chart: FC = () => {
const dateFilter = useDateFilter()
const data = useVolumeMetrics()
const { metrics } = useMetricsState()
return (
<RechartsContainer>
{data && data.length ? (
<ResponsiveContainer aspect={2}>
<AreaChart margin={{ top: 0, right: 16, bottom: 16, left: 16 }} barCategoryGap={1} data={data}>
<defs>
{volumeMetrics.map(({ type, color }) => (
<linearGradient id={type} key={type} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color} stopOpacity={0.5} />
<stop offset="95%" stopColor={color} stopOpacity={0} />
</linearGradient>
))}
</defs>
<XAxis
dataKey="timestamp"
axisLine={false}
xAxisId={0}
tickSize={12}
padding={{ left: 16 }}
minTickGap={16}
tickLine
tickFormatter={timestamp => (timestamp ? format(timestamp * 1000, periodFormatMapping[dateFilter.period]) : '')}
/>
<YAxis
type="number"
orientation="left"
tickFormatter={toK}
axisLine={false}
tickLine
tickSize={12}
padding={{ bottom: 16 }}
interval="preserveEnd"
minTickGap={8}
yAxisId={0}
/>
<YAxis
type="number"
hide={!metrics.find(m => m.type === TransactionType.MassetPaidFee && m.enabled)}
orientation="right"
tickFormatter={toK}
axisLine={false}
tickLine
tickSize={12}
name="Fees"
padding={{ bottom: 16 }}
interval="preserveEnd"
minTickGap={8}
yAxisId={1}
/>
<Tooltip
cursor
labelFormatter={timestamp => format((timestamp as number) * 1000, 'yyyy-MM-dd HH:mm')}
formatter={toK as never}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
{metrics.map(({ color, type, label, enabled }) => (
<Area
isAnimationActive={false}
key={type}
type="monotone"
hide={!enabled}
dataKey={type}
name={`${label} `}
opacity={1}
dot={false}
yAxisId={type === TransactionType.MassetPaidFee ? 1 : 0}
stroke={color}
strokeWidth={2}
fill={`url(#${type})`}
/>
))}
</AreaChart>
</ResponsiveContainer>
) : (
<ThemedSkeleton height={270} />
)}
</RechartsContainer>
)
}
Example #13
Source File: DailyApys.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 4 votes |
DailyApysChart: FC<{
shimmerHeight?: number
tick?: boolean
marginTop?: number
aspect?: number
className?: string
color?: string
strokeWidth?: number
hoverEnabled?: boolean
}> = ({ shimmerHeight = 270, tick, className, marginTop, color, aspect = 2, strokeWidth = 2, hoverEnabled = true }) => {
const dateFilter = useDateFilter()
const { DailyApy, UtilisationRate } = useMetrics<MetricTypes>()
const blockTimes = useBlockTimesForDates(dateFilter.dates)
const savingsContractState = useSelectedSavingsContractState()
const dailyApys = useDailyApysForBlockTimes(savingsContractState?.address, blockTimes)
if (dailyApys.some(value => value.dailyAPY > 1000) || dailyApys.every(value => value.dailyAPY === 0)) {
return <NoData className={className}>No data available yet</NoData>
}
return (
<RechartsContainer className={className}>
{dailyApys && dailyApys.length ? (
<ResponsiveContainer aspect={aspect} debounce={1} width="99%">
<AreaChart
margin={tick ? { top: marginTop, right: 16, bottom: 16, left: 16 } : { top: marginTop, right: 0, bottom: 0, left: 0 }}
barCategoryGap={1}
data={dailyApys}
>
{hoverEnabled && (
<defs>
<linearGradient id="utilisationRate" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={Color.blackTransparent} stopOpacity={0.5} />
<stop offset="95%" stopColor={Color.blackTransparent} stopOpacity={0} />
</linearGradient>
<linearGradient id="dailyAPY" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color ?? Color.gold} stopOpacity={0.5} />
<stop offset="95%" stopColor={color ?? Color.gold} stopOpacity={0} />
</linearGradient>
</defs>
)}
<XAxis
dataKey="timestamp"
axisLine={false}
tick={tick}
xAxisId={0}
tickSize={12}
padding={tick ? { left: 16 } : { left: 0 }}
minTickGap={16}
tickLine
height={!tick ? 0 : undefined}
tickFormatter={(timestamp: number) => (timestamp ? format(timestamp * 1000, periodFormatMapping[dateFilter.period]) : '')}
/>
<YAxis
type="number"
orientation="left"
tickFormatter={formatApy}
axisLine={false}
tick={tick}
tickLine
tickSize={12}
padding={tick ? { bottom: 16 } : { bottom: 0 }}
interval="preserveEnd"
minTickGap={8}
yAxisId={0}
width={!tick ? 0 : undefined}
/>
{hoverEnabled && (
<Tooltip
cursor
labelFormatter={timestamp => format((timestamp as number) * 1000, 'yyyy-MM-dd HH:mm')}
formatter={formatApy as never}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
)}
<Area
hide={!UtilisationRate.enabled}
strokeWidth={strokeWidth}
type="monotone"
dataKey="utilisationRate"
name="SAVE Utilisation "
opacity={1}
fill="url(#utilisationRate)"
yAxisId={0}
stroke={Color.blackTransparent}
/>
<Area
hide={!DailyApy.enabled}
strokeWidth={strokeWidth}
type="monotone"
dataKey="dailyAPY"
name="Daily APY "
opacity={1}
yAxisId={0}
fill="url(#dailyAPY)"
stroke={color ?? DailyApy.color}
/>
</AreaChart>
</ResponsiveContainer>
) : (
<ThemedSkeleton height={shimmerHeight} />
)}
</RechartsContainer>
)
}
Example #14
Source File: AggregateChart.tsx From mStable-apps with GNU Lesser General Public License v3.0 | 4 votes |
Chart: FC<{
aggregateMetrics: {
type: string
enabled: boolean
label: string
color: string
}[]
}> = ({ aggregateMetrics }) => {
const data = useAggregateMetrics()
const dateFilter = useDateFilter()
const { metrics } = useMetricsState()
return (
<RechartsContainer>
{data && data.length ? (
<ResponsiveContainer aspect={2}>
<AreaChart margin={{ top: 0, right: 16, bottom: 16, left: 16 }} barCategoryGap={1} data={data}>
<defs>
{aggregateMetrics.map(({ type, color }) => (
<linearGradient id={type} key={type} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color} stopOpacity={0.5} />
<stop offset="95%" stopColor={color} stopOpacity={0} />
</linearGradient>
))}
</defs>
<XAxis
dataKey="timestamp"
axisLine={false}
xAxisId={0}
tickSize={12}
padding={{ left: 16 }}
minTickGap={16}
tickLine
tickFormatter={timestamp => (timestamp ? format(timestamp * 1000, periodFormatMapping[dateFilter.period]) : '')}
/>
<YAxis
type="number"
orientation="left"
tickFormatter={toK}
axisLine={false}
tickLine
tickSize={12}
padding={{ bottom: 16 }}
interval="preserveEnd"
minTickGap={8}
yAxisId={0}
/>
<Tooltip
cursor
labelFormatter={timestamp => format((timestamp as number) * 1000, 'yyyy-MM-dd HH:mm')}
formatter={toK as never}
separator=""
contentStyle={{
fontSize: '14px',
padding: '8px',
background: 'rgba(255, 255, 255, 0.8)',
textAlign: 'right',
border: 'none',
borderRadius: '4px',
color: Color.black,
}}
wrapperStyle={{
top: 0,
left: 0,
}}
/>
{metrics.map(({ color, type, label, enabled }) => (
<Area
isAnimationActive={false}
key={type}
type="monotone"
hide={!enabled}
dataKey={type}
name={`${label} `}
opacity={1}
dot={false}
yAxisId={0}
stroke={color}
strokeWidth={2}
fill={`url(#${type})`}
/>
))}
</AreaChart>
</ResponsiveContainer>
) : (
<ThemedSkeleton height={270} />
)}
</RechartsContainer>
)
}
Example #15
Source File: ArtistListeningRepartition.tsx From your_spotify with GNU General Public License v3.0 | 4 votes |
export default function ArtistListeningRepartition({ className }: ArtistListeningRepartitionProps) {
const { interval } = useSelector(selectRawIntervalDetail);
const results = useAPI(api.mostListenedArtist, interval.start, interval.end, interval.timesplit);
const resultsWithCount = useMemo(
() =>
results?.map((res) => ({
_id: res._id,
artists: res.artists.reduce<Record<string, number>>((acc, curr, idx) => {
acc[curr.id] = res.counts[idx];
return acc;
}, {}),
})),
[results],
);
const allArtists = useMemo(() => {
const all: Record<string, Artist> = {};
results?.forEach((res) => {
res.artists.forEach((art) => {
if (!(art.id in all)) {
all[art.id] = art;
}
});
});
return all;
}, [results]);
const data = useMemo(() => {
if (!resultsWithCount) {
return [];
}
const d = resultsWithCount.map((curr, idx) => {
const obj: { x: number; _id: DateId } & any = {
x: idx,
_id: curr._id as DateId,
};
const total = Object.values(curr.artists).reduce((acc, count) => acc + count, 0);
Object.values(allArtists).forEach((art) => {
obj[art.id] = (curr.artists[art.id] ?? 0) / total;
});
return obj;
}, []);
return buildXYDataObjSpread(d, Object.keys(allArtists), interval.start, interval.end, false);
}, [allArtists, interval, resultsWithCount]);
const tooltipLabelFormatter = useRawTooltipLabelFormatter(formatXAxisDateTooltip, false);
const tooltipValueFormatter = useCallback(
(value: number, label: string) => {
if (value === 0) {
return [<span />];
}
return [`${allArtists[label].name}: ${Math.floor(value * 1000) / 10}%`];
},
[allArtists],
);
const tooltipSorter = useCallback((a: any) => {
return -a.payload[a.dataKey];
}, []);
const formatX = useFormatXAxis(data);
if (!results) {
return <LoadingImplementedChart title="Artist listening distribution" className={className} />;
}
return (
<ChartCard title="Artist listening distribution" className={className}>
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={data}>
<XAxis dataKey="x" tickFormatter={formatX} style={{ fontWeight: 'bold' }} />
<YAxis domain={[0, 1]} tickFormatter={formatYAxis} />
<Tooltip
formatter={tooltipValueFormatter}
labelFormatter={tooltipLabelFormatter}
wrapperStyle={{ zIndex: 1000 }}
contentStyle={{ background: 'var(--background)' }}
itemSorter={tooltipSorter}
/>
{Object.values(allArtists).map((art, idx) => (
<Area
type="monotone"
dataKey={art.id}
key={art.id}
stackId={-1}
stroke={getColor(idx)}
fill={getColor(idx)}
/>
))}
</AreaChart>
</ResponsiveContainer>
</ChartCard>
);
}
Example #16
Source File: LinearGraphWidget.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
LinearGraphWidget = ({
classes,
title,
timeStart,
timeEnd,
propLoading,
panelItem,
apiPrefix,
hideYAxis = false,
areaWidget = false,
yAxisFormatter = (item: string) => item,
xAxisFormatter = (item: string) => item,
zoomActivated = false,
}: ILinearGraphWidget) => {
const dispatch = useDispatch();
const [loading, setLoading] = useState<boolean>(true);
const [data, setData] = useState<object[]>([]);
const [dataMax, setDataMax] = useState<number>(0);
const [result, setResult] = useState<IDashboardPanel | null>(null);
useEffect(() => {
if (propLoading) {
setLoading(true);
}
}, [propLoading]);
useEffect(() => {
if (loading) {
let stepCalc = 0;
if (timeStart !== null && timeEnd !== null) {
const secondsInPeriod = timeEnd.unix() - timeStart.unix();
const periods = Math.floor(secondsInPeriod / 60);
stepCalc = periods < 1 ? 15 : periods;
}
api
.invoke(
"GET",
`/api/v1/${apiPrefix}/info/widgets/${
panelItem.id
}/?step=${stepCalc}&${
timeStart !== null ? `&start=${timeStart.unix()}` : ""
}${timeStart !== null && timeEnd !== null ? "&" : ""}${
timeEnd !== null ? `end=${timeEnd.unix()}` : ""
}`
)
.then((res: any) => {
const widgetsWithValue = widgetDetailsToPanel(res, panelItem);
setData(widgetsWithValue.data);
setResult(widgetsWithValue);
setLoading(false);
let maxVal = 0;
for (const dp of widgetsWithValue.data) {
for (const key in dp) {
if (key === "name") {
continue;
}
let val = parseInt(dp[key]);
if (isNaN(val)) {
val = 0;
}
if (maxVal < val) {
maxVal = val;
}
}
}
setDataMax(maxVal);
})
.catch((err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
setLoading(false);
});
}
}, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]);
let intervalCount = Math.floor(data.length / 5);
const linearConfiguration = result
? (result?.widgetConfiguration as ILinearGraphConfiguration[])
: [];
const CustomizedDot = (prop: any) => {
const { cx, cy, index } = prop;
if (index % 3 !== 0) {
return null;
}
return <circle cx={cx} cy={cy} r={3} strokeWidth={0} fill="#07264A" />;
};
const theme = useTheme();
const biggerThanMd = useMediaQuery(theme.breakpoints.up("md"));
return (
<Box className={zoomActivated ? "" : classes.singleValueContainer}>
{!zoomActivated && (
<div className={classes.titleContainer}>
{title} <ExpandGraphLink panelItem={panelItem} />
</div>
)}
<Box
sx={
zoomActivated
? { flexDirection: "column" }
: {
height: "100%",
display: "grid",
gridTemplateColumns: {
md: "1fr 1fr",
sm: "1fr",
},
}
}
style={areaWidget ? { gridTemplateColumns: "1fr" } : {}}
>
{loading && <Loader className={classes.loadingAlign} />}
{!loading && (
<React.Fragment>
<div
className={
zoomActivated ? classes.zoomChartCont : classes.chartCont
}
>
<ResponsiveContainer width="99%">
<AreaChart
data={data}
margin={{
top: 5,
right: 20,
left: hideYAxis ? 20 : 5,
bottom: 0,
}}
>
{areaWidget && (
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#2781B0" stopOpacity={1} />
<stop
offset="100%"
stopColor="#ffffff"
stopOpacity={0}
/>
<stop
offset="95%"
stopColor="#ffffff"
stopOpacity={0.8}
/>
</linearGradient>
</defs>
)}
<CartesianGrid
strokeDasharray={areaWidget ? "2 2" : "5 5"}
strokeWidth={1}
strokeOpacity={1}
stroke={"#eee0e0"}
vertical={!areaWidget}
/>
<XAxis
dataKey="name"
tickFormatter={(value: any) => xAxisFormatter(value)}
interval={intervalCount}
tick={{
fontSize: "68%",
fontWeight: "normal",
color: "#404143",
}}
tickCount={10}
stroke={"#082045"}
/>
<YAxis
type={"number"}
domain={[0, dataMax * 1.1]}
hide={hideYAxis}
tickFormatter={(value: any) => yAxisFormatter(value)}
tick={{
fontSize: "68%",
fontWeight: "normal",
color: "#404143",
}}
stroke={"#082045"}
/>
{linearConfiguration.map((section, index) => {
return (
<Area
key={`area-${section.dataKey}-${index.toString()}`}
type="monotone"
dataKey={section.dataKey}
isAnimationActive={false}
stroke={!areaWidget ? section.lineColor : "#D7E5F8"}
fill={areaWidget ? "url(#colorUv)" : section.fillColor}
fillOpacity={areaWidget ? 0.65 : 0}
strokeWidth={!areaWidget ? 3 : 0}
strokeLinecap={"round"}
dot={areaWidget ? <CustomizedDot /> : false}
/>
);
})}
<Tooltip
content={
<LineChartTooltip
linearConfiguration={linearConfiguration}
yAxisFormatter={yAxisFormatter}
/>
}
wrapperStyle={{
zIndex: 5000,
}}
/>
</AreaChart>
</ResponsiveContainer>
</div>
{!areaWidget && (
<Fragment>
{zoomActivated && (
<Fragment>
<strong>Series</strong>
<br />
<br />
</Fragment>
)}
{biggerThanMd && (
<div className={classes.legendChart}>
{linearConfiguration.map((section, index) => {
return (
<div
className={classes.singleLegendContainer}
key={`legend-${section.keyLabel}-${index.toString()}`}
>
<div
className={classes.colorContainer}
style={{ backgroundColor: section.lineColor }}
/>
<div className={classes.legendLabel}>
{section.keyLabel}
</div>
</div>
);
})}
</div>
)}
</Fragment>
)}
</React.Fragment>
)}
</Box>
</Box>
);
}
Example #17
Source File: LiveMetricsItem.tsx From kubenav with MIT License | 4 votes |
LiveMetricsItem: React.FunctionComponent<ILiveMetricsItemProps> = ({
show,
hide,
item,
}: ILiveMetricsItemProps) => {
const context = useContext<IContext>(AppContext);
const [metrics, setMetrics] = useState<ILiveMetric[]>([]);
const fetchMetrics = async () => {
try {
if (show) {
const data: IPodMetrics = await kubernetesRequest(
'GET',
`/apis/metrics.k8s.io/v1beta1/namespaces/${
item.metadata && item.metadata.namespace ? item.metadata.namespace : ''
}/pods/${item.metadata && item.metadata.name ? item.metadata.name : ''}`,
'',
context.settings,
await context.kubernetesAuthWrapper(''),
);
const containerMetrics: ILiveMetric[] = [];
if (data.containers) {
for (const container of data.containers) {
containerMetrics.push({
timestamp: 0,
cpu: parseInt(formatResourceValue('cpu', container?.usage?.cpu || '0')),
memory: parseInt(formatResourceValue('memory', container?.usage?.memory || '0')),
});
}
}
setMetrics((prevMetrics) => {
if (prevMetrics.length > 14) {
return [
...prevMetrics.slice(-14),
{
timestamp: Math.floor(new Date().getTime() / 1000),
cpu: containerMetrics.map((m) => m.cpu).reduce(reducer),
memory: containerMetrics.map((m) => m.memory).reduce(reducer),
},
];
} else {
return [
...prevMetrics,
{
timestamp: Math.floor(new Date().getTime() / 1000),
cpu: containerMetrics.map((m) => m.cpu).reduce(reducer),
memory: containerMetrics.map((m) => m.memory).reduce(reducer),
},
];
}
});
}
} catch (err) {
throw err;
}
};
const formatTime = (time: number): string => {
const d = new Date(time * 1000);
return `${('0' + d.getHours()).slice(-2)}:${('0' + d.getMinutes()).slice(-2)}:${('0' + d.getSeconds()).slice(-2)}`;
};
useEffect(() => {
const timer = setTimeout(() => {
fetchMetrics();
}, 3000);
return () => clearTimeout(timer);
});
return (
<React.Fragment>
<IonModal isOpen={show} onDidDismiss={hide}>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton onClick={hide}>
<IonIcon slot="icon-only" icon={close} />
</IonButton>
</IonButtons>
<IonTitle>{item.metadata ? item.metadata.name : ''}</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonCard>
<IonCardHeader>
<IonCardTitle>CPU Usage</IonCardTitle>
</IonCardHeader>
<IonCardContent>
<div style={{ height: '300px', width: '100%' }}>
{metrics.length > 0 && (
<ResponsiveContainer>
<AreaChart data={metrics}>
<XAxis
dataKey="timestamp"
scale="time"
type="number"
domain={['dataMin', 'dataMax']}
tickFormatter={formatTime}
/>
<YAxis dataKey="cpu" unit="m" />
<Area
dataKey="cpu"
// NOTE: https://github.com/recharts/recharts/issues/2487
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data={metrics}
name="CPU Usage"
stroke="#326ce5"
fill="#326ce5"
fillOpacity={0.2}
isAnimationActive={false}
/>
</AreaChart>
</ResponsiveContainer>
)}
</div>
</IonCardContent>
</IonCard>
<IonCard>
<IonCardHeader>
<IonCardTitle>Memory Usage</IonCardTitle>
</IonCardHeader>
<IonCardContent>
<div style={{ height: '300px', width: '100%' }}>
{metrics.length > 0 && (
<ResponsiveContainer>
<AreaChart data={metrics}>
<XAxis
dataKey="timestamp"
scale="time"
type="number"
domain={['dataMin', 'dataMax']}
tickFormatter={formatTime}
/>
<YAxis dataKey="memory" unit="Mi" />
<Area
dataKey="memory"
// NOTE: https://github.com/recharts/recharts/issues/2487
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data={metrics}
name="Memory Usage"
stroke="#326ce5"
fill="#326ce5"
fillOpacity={0.2}
isAnimationActive={false}
/>
</AreaChart>
</ResponsiveContainer>
)}
</div>
</IonCardContent>
</IonCard>
</IonContent>
</IonModal>
</React.Fragment>
);
}
Example #18
Source File: ChartDetailsArea.tsx From kubenav with MIT License | 4 votes |
ChartDetailsArea: React.FunctionComponent<IChartDetailsAreaProps> = ({
unit,
timeDiff,
results,
}: IChartDetailsAreaProps) => {
const context = useContext<IContext>(AppContext);
const [selected, setSelected] = useState<string>('');
// formatTime is use to formate the shown values for the x axis. If the user selected a time span lower then 24h we
// are showing the "hh:mm" for timespans larger then 24h we are showing "MM/DD hh:mm".
const formatTime = (time: number): string => {
const d = new Date(time * 1000);
if (timeDiff >= 86400) {
return `${('0' + (d.getMonth() + 1)).slice(-2)}/${('0' + d.getDate()).slice(-2)} ${('0' + d.getHours()).slice(
-2,
)}:${('0' + d.getMinutes()).slice(-2)}`;
} else {
return `${('0' + d.getHours()).slice(-2)}:${('0' + d.getMinutes()).slice(-2)}`;
}
};
// The series variables contains the transformed Prometheus results. This is necessary, because the returned formate
// from Prometheus can not directly used with Recharts.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const series: any = [];
for (let i = 0; i < results.length; i++) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = [];
for (let j = 0; j < results[i].values.length; j++) {
data.push({
time: results[i].values[j][0],
value: parseFloat(results[i].values[j][1]),
});
}
series.push({ name: results[i].label, data: data });
}
return (
<IonRow style={{ height: '300px', width: '100%' }}>
<IonCol style={{ padding: '0px' }}>
<ResponsiveContainer>
<AreaChart data={series[0].data}>
<XAxis
dataKey="time"
scale="time"
type="number"
domain={['dataMin', 'dataMax']}
tickFormatter={formatTime}
/>
<YAxis dataKey="value" unit={unit} />
<Legend
height={40}
wrapperStyle={{ overflowY: 'auto' }}
onClick={(e) => (selected === e.payload.name ? setSelected('') : setSelected(e.payload.name))}
/>
{!isPlatform('hybrid') ? (
<Tooltip
cursor={{ stroke: '#949494', strokeWidth: 2 }}
contentStyle={
isDarkMode(context.settings.theme)
? isPlatform('ios')
? { backgroundColor: '1c1c1c', borderColor: '#949494' }
: { backgroundColor: '#1A1B1E', borderColor: '#949494' }
: { backgroundColor: '#ffffff', borderColor: '#949494' }
}
formatter={(value) => {
return `${value.toFixed(5)} ${unit ? unit : ''}`;
}}
labelFormatter={formatTime}
/>
) : null}
{selected === ''
? series.map((serie, index) => (
<Area
key={index}
dataKey="value"
// NOTE: https://github.com/recharts/recharts/issues/2487
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data={serie.data}
name={serie.name}
stroke={getColor(index, isDarkMode(context.settings.theme))}
fill={getColor(index, isDarkMode(context.settings.theme))}
fillOpacity={0.2}
/>
))
: series
.filter((serie) => serie.name === selected)
.map((serie, index) => (
<Area
key={index}
dataKey="value"
// NOTE: https://github.com/recharts/recharts/issues/2487
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data={serie.data}
name={serie.name}
stroke={getColor(
series.findIndex((s) => s.name === selected),
isDarkMode(context.settings.theme),
)}
fill={getColor(
series.findIndex((s) => s.name === selected),
isDarkMode(context.settings.theme),
)}
fillOpacity={0.2}
/>
))}
</AreaChart>
</ResponsiveContainer>
</IonCol>
</IonRow>
);
}
Example #19
Source File: CostOverviewBreakdownChart.tsx From backstage with Apache License 2.0 | 4 votes |
CostOverviewBreakdownChart = ({
costBreakdown,
}: CostOverviewBreakdownChartProps) => {
const theme = useTheme<CostInsightsTheme>();
const classes = useStyles(theme);
const lastCompleteBillingDate = useLastCompleteBillingDate();
const { duration } = useFilters(mapFiltersToProps);
const [isExpanded, setExpanded] = useState(false);
if (!costBreakdown) {
return null;
}
const flattenedAggregation = costBreakdown
.map(cost => cost.aggregation)
.flat();
const totalCost = aggregationSum(flattenedAggregation);
const previousPeriodTotal = getPreviousPeriodTotalCost(
flattenedAggregation,
duration,
lastCompleteBillingDate,
);
const currentPeriodTotal = totalCost - previousPeriodTotal;
const canExpand = costBreakdown.length >= 8;
const otherCategoryIds: string[] = [];
const breakdownsByDate = costBreakdown.reduce(
(breakdownByDate, breakdown) => {
const breakdownTotal = aggregationSum(breakdown.aggregation);
// Group breakdown items with less than 10% of the total cost into "Other" category if needed
const isOtherCategory =
canExpand && breakdownTotal < totalCost * LOW_COST_THRESHOLD;
const updatedBreakdownByDate = { ...breakdownByDate };
if (isOtherCategory) {
otherCategoryIds.push(breakdown.id);
}
breakdown.aggregation.forEach(curAggregation => {
const costsForDate = updatedBreakdownByDate[curAggregation.date] || {};
updatedBreakdownByDate[curAggregation.date] = {
...costsForDate,
[breakdown.id]:
(costsForDate[breakdown.id] || 0) + curAggregation.amount,
};
});
return updatedBreakdownByDate;
},
{} as Record<string, Record<string, number>>,
);
const chartData: Record<string, number>[] = Object.keys(breakdownsByDate).map(
date => {
const costsForDate = Object.keys(breakdownsByDate[date]).reduce(
(dateCosts, breakdown) => {
// Group costs for items that belong to 'Other' in the chart.
const cost = breakdownsByDate[date][breakdown];
const breakdownCost =
!isExpanded && otherCategoryIds.includes(breakdown)
? { Other: (dateCosts.Other || 0) + cost }
: { [breakdown]: cost };
return { ...dateCosts, ...breakdownCost };
},
{} as Record<string, number>,
);
return {
...costsForDate,
date: Date.parse(date),
};
},
);
const sortedBreakdowns = costBreakdown.sort(
(a, b) => aggregationSum(a.aggregation) - aggregationSum(b.aggregation),
);
const renderAreas = () => {
const separatedBreakdowns = sortedBreakdowns
// Check that the breakdown is a separate group and hasn't been added to 'Other'
.filter(
breakdown =>
breakdown.id !== 'Other' && !otherCategoryIds.includes(breakdown.id),
)
.map(breakdown => breakdown.id);
// Keep 'Other' category at the bottom of the stack
const breakdownsToDisplay = isExpanded
? sortedBreakdowns.map(breakdown => breakdown.id)
: ['Other', ...separatedBreakdowns];
return breakdownsToDisplay.map((breakdown, i) => {
// Logic to handle case where there are more items than data viz colors.
const color =
theme.palette.dataViz[
(breakdownsToDisplay.length - 1 - i) %
(theme.palette.dataViz.length - 1)
];
return (
<Area
key={breakdown}
dataKey={breakdown}
isAnimationActive={false}
stackId="1"
stroke={color}
fill={color}
onClick={() => setExpanded(true)}
style={{
cursor: breakdown === 'Other' && !isExpanded ? 'pointer' : null,
}}
/>
);
});
};
const tooltipRenderer: ContentRenderer<TooltipProps> = ({
label,
payload = [],
}) => {
if (isInvalid({ label, payload })) return null;
const date =
typeof label === 'number'
? DateTime.fromMillis(label)
: DateTime.fromISO(label!);
const dateTitle = date.toUTC().toFormat(DEFAULT_DATE_FORMAT);
const items = payload.map(p => ({
label: p.dataKey as string,
value: formatGraphValue(p.value as number),
fill: p.fill!,
}));
const expandText = (
<Box>
<Divider
style={{
backgroundColor: emphasize(theme.palette.divider, 1),
margin: '10px 0',
}}
/>
<Box display="flex" justifyContent="space-between" alignItems="center">
<FullScreenIcon />
<Typography>Click to expand</Typography>
</Box>
</Box>
);
return (
<Tooltip title={dateTitle}>
{items.reverse().map((item, index) => (
<TooltipItem key={`${item.label}-${index}`} item={item} />
))}
{canExpand && !isExpanded ? expandText : null}
</Tooltip>
);
};
const options: Partial<BarChartLegendOptions> = {
previousName: formatPeriod(duration, lastCompleteBillingDate, false),
currentName: formatPeriod(duration, lastCompleteBillingDate, true),
hideMarker: true,
};
return (
<Box display="flex" flexDirection="column">
<Box display="flex" flexDirection="row">
<BarChartLegend
costStart={previousPeriodTotal}
costEnd={currentPeriodTotal}
options={options}
/>
</Box>
<ResponsiveContainer
width={classes.container.width}
height={classes.container.height}
>
<AreaChart
data={chartData}
margin={{
top: 16,
right: 30,
bottom: 40,
}}
>
<CartesianGrid stroke={classes.cartesianGrid.stroke} />
<XAxis
dataKey="date"
domain={['dataMin', 'dataMax']}
tickFormatter={overviewGraphTickFormatter}
tickCount={6}
type="number"
stroke={classes.axis.fill}
/>
<YAxis
domain={[() => 0, 'dataMax']}
tick={{ fill: classes.axis.fill }}
tickFormatter={formatGraphValue}
width={classes.yAxis.width}
/>
{renderAreas()}
<RechartsTooltip content={tooltipRenderer} animationDuration={100} />
</AreaChart>
</ResponsiveContainer>
</Box>
);
}