Skip to content

Commit d6ff776

Browse files
authored
Some performance improvement and introducing multi line xaxis ticks, for line, bar and area charts, (#145)
* performance improvement for barChart scroll button extracted * performance improvement for line and are area chart * perf(charts): Optimize chart rendering by memoizing Y-axis This commit improves the rendering performance of the AreaChart, BarChart, and LineChart components by memoizing the Y-axis. The Y-axis for these charts is now wrapped in a `useMemo` hook to prevent unnecessary re-renders when unrelated props change. This results in a smoother experience, particularly for charts that are interactive or update frequently. Additionally, the `chartSyncID` has been removed as it is no longer required with the new implementation. * function changes * better colition detection and xtick * bar xtik colition detection * xAxis multi * feat(charts): Add useMaxLabelHeight hook for dynamic label height calculation This commit introduces a new hook, useMaxLabelHeight, which calculates the maximum height of labels in a chart based on the provided data and category key. The hook utilizes memoization for performance optimization and ensures accurate height measurement by creating temporary DOM elements. Additionally, it cleans up by removing unused code from useTransformKey. * feat(charts): Add XAxisTicks type for chart tick variants This commit introduces a new type, XAxisTickVariant, which defines the possible variants for X-axis ticks in charts. The type is exported from a new file, enhancing the type definitions available for chart components. * feat(charts): Export useMaxLabelHeight hook in charts hooks index This commit updates the charts hooks index file to export the useMaxLabelHeight hook, allowing for dynamic label height calculations to be utilized in other components. * fix(ThemeProvider): Update default theme reference to use themes.light This commit modifies the ThemeProvider component to reference themes.light instead of lightTheme for the default theme. This change ensures consistency with the updated theme structure. * feat(charts): Enhance AreaChart and LineChart with dynamic label height and X-axis tick improvements This commit introduces the useMaxLabelHeight hook in both AreaChart and LineChart components to dynamically calculate the maximum height of X-axis labels based on their content. The X-axis tick rendering has been updated to utilize this height for better spacing. Additionally, the XAxisTick component has been refactored to support different tick variants, improving the overall flexibility and appearance of the charts. Unused code related to X-axis position handling has been removed for cleaner implementation. * refactor(charts): Update chart height calculations and improve X-axis tick rendering This commit modifies the AreaChart and LineChart components to include the maximum label height in their height calculations, ensuring better spacing for dynamic content. The XAxisTick component has been updated to accept a customizable width for improved layout flexibility. Additionally, a new utility function, getCanvasContext, has been introduced to streamline font style application for canvas rendering, enhancing overall chart rendering accuracy. * refactor(charts): Improve BarChart utility functions and label width handling This commit refactors the BarChart utility functions by introducing the getCanvasContext utility for better font style management in X-axis tick rendering. It also updates the padding values for chart content and adjusts the label width handling in the useMaxLabelHeight hook to enhance layout consistency and visual spacing. * refactor(charts): Update chart components for multiLine tick variant and width handling This commit refactors the AreaChart, BarChart, and LineChart components to support a new "multiLine" tick variant for X-axis labels. It introduces the widthOfGroup calculation to improve label height management and adjusts the useMaxLabelHeight hook accordingly. The XAxisTick component is updated to accommodate the new variant and width properties, enhancing layout flexibility and visual consistency across charts. * refactor(charts): Enhance BarChart story data structure for improved readability This commit refactors the data structure in the BarChart stories to enhance readability by formatting the data entries with consistent indentation and spacing. This change improves the clarity of the data variations used in the stories, making it easier for developers to understand and modify the chart data. * refactor(charts): Simplify XAxisTick usage in chart components This commit refactors the AreaChart, BarChart, and LineChart components to streamline the usage of the XAxisTick component by removing the unnecessary chartVariant prop. The tick rendering is simplified for better readability and maintainability, enhancing the overall code structure across the chart components. * refactor(charts): Remove unused getCanvasContext utility from XAxisTick component This commit removes the unused getCanvasContext utility from the XAxisTick component, streamlining the code and improving maintainability. The change contributes to a cleaner implementation by eliminating unnecessary references, enhancing overall code clarity. * refactor(charts): Update XAxisTick component to support customizable label height This commit enhances the XAxisTick component by adding a new labelHeight prop, allowing for more flexible height management of X-axis ticks across AreaChart, BarChart, and LineChart components. The tick rendering in these charts has been updated to utilize this new prop, improving layout consistency and visual appearance. * refactor(charts): Standardize X-axis padding in AreaChart and LineChart components This commit introduces a new constant, X_AXIS_PADDING, to standardize the left and right padding for the X-axis in both AreaChart and LineChart components. This change enhances code maintainability and ensures consistent layout across chart components. * rename yaxis chart to yaxis * hide shell stories * fix type
1 parent 542b4a3 commit d6ff776

26 files changed

Lines changed: 662 additions & 503 deletions

File tree

js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx

Lines changed: 77 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
import clsx from "clsx";
2-
import { ChevronLeft, ChevronRight } from "lucide-react";
32
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
43
import { Area, AreaChart as RechartsAreaChart, XAxis, YAxis } from "recharts";
54
import { useId } from "../../../polyfills";
6-
import { IconButton } from "../../IconButton";
75
import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts";
86
import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext";
9-
import { useTransformedKeys } from "../hooks";
7+
import { useMaxLabelHeight, useTransformedKeys } from "../hooks";
108
import {
119
ActiveDot,
1210
cartesianGrid,
1311
CustomTooltipContent,
1412
DefaultLegend,
13+
ScrollButtonsHorizontal,
1514
SideBarTooltip,
1615
XAxisTick,
1716
YAxisTick,
1817
} from "../shared";
19-
import { LegendItem } from "../types";
18+
import { LegendItem, XAxisTickVariant } from "../types";
2019
import {
2120
findNearestSnapPosition,
2221
getOptimalXAxisTickFormatter,
2322
getSnapPositions,
2423
getWidthOfData,
25-
getXAxisTickPositionData,
24+
getWidthOfGroup,
2625
} from "../utils/AreaAndLine/AreaAndLineUtils";
2726
import { PaletteName, useChartPalette } from "../utils/PalletUtils";
2827
import {
@@ -45,6 +44,7 @@ export interface AreaChartProps<T extends AreaChartData> {
4544
theme?: PaletteName;
4645
customPalette?: string[];
4746
variant?: AreaChartVariant;
47+
tickVariant?: XAxisTickVariant;
4848
grid?: boolean;
4949
legend?: boolean;
5050
icons?: Partial<Record<keyof T[number], React.ComponentType>>;
@@ -58,13 +58,15 @@ export interface AreaChartProps<T extends AreaChartData> {
5858
}
5959

6060
const Y_AXIS_WIDTH = 40; // Width of Y-axis chart when shown
61+
const X_AXIS_PADDING = 36;
6162

6263
const AreaChartComponent = <T extends AreaChartData>({
6364
data,
6465
categoryKey,
6566
theme = "ocean",
6667
customPalette,
6768
variant = "natural",
69+
tickVariant = "multiLine",
6870
grid = true,
6971
icons = {},
7072
isAnimationActive = false,
@@ -80,6 +82,12 @@ const AreaChartComponent = <T extends AreaChartData>({
8082
return getDataKeys(data, categoryKey as string);
8183
}, [data, categoryKey]);
8284

85+
const widthOfGroup = useMemo(() => {
86+
return getWidthOfGroup(data);
87+
}, [data]);
88+
89+
const maxLabelHeight = useMaxLabelHeight(data, categoryKey as string, tickVariant, widthOfGroup);
90+
8391
const transformedKeys = useTransformedKeys(dataKeys);
8492

8593
const colors = useChartPalette({
@@ -125,19 +133,14 @@ const AreaChartComponent = <T extends AreaChartData>({
125133
}, [data]);
126134

127135
const chartHeight = useMemo(() => {
128-
return height ?? 296;
129-
}, [height]);
136+
return height ?? 296 + maxLabelHeight;
137+
}, [height, maxLabelHeight]);
130138

131139
// Calculate optimal tick formatter for collision detection and truncation
132140
const xAxisTickFormatter = useMemo(() => {
133141
return getOptimalXAxisTickFormatter(data, effectiveContainerWidth);
134142
}, [data, effectiveContainerWidth]);
135143

136-
// Calculate position data for X-axis tick offset handling
137-
const xAxisPositionData = useMemo(() => {
138-
return getXAxisTickPositionData(data, categoryKey as string);
139-
}, [data, categoryKey]);
140-
141144
// Check scroll boundaries
142145
const updateScrollState = useCallback(() => {
143146
if (mainContainerRef.current) {
@@ -225,8 +228,6 @@ const AreaChartComponent = <T extends AreaChartData>({
225228

226229
const id = useId();
227230

228-
const chartSyncID = useMemo(() => `area-chart-sync-${id}`, [id]);
229-
230231
const gradientID = useMemo(() => `area-chart-gradient-${id}`, [id]);
231232

232233
const onAreaClick = useCallback(
@@ -246,6 +247,51 @@ const AreaChartComponent = <T extends AreaChartData>({
246247
[dataKeys, colors],
247248
);
248249

250+
const yAxis = useMemo(() => {
251+
if (!showYAxis) {
252+
return null;
253+
}
254+
return (
255+
<div className="crayon-area-chart-y-axis-container">
256+
{/* Y-axis only chart - synchronized with main chart */}
257+
<RechartsAreaChart
258+
key={`y-axis-chart-${id}`}
259+
width={Y_AXIS_WIDTH}
260+
height={chartHeight}
261+
data={data}
262+
margin={{
263+
top: 20,
264+
bottom: maxLabelHeight, // this is required for to give space for x-axis
265+
left: 0,
266+
right: 0,
267+
}}
268+
>
269+
<YAxis
270+
width={Y_AXIS_WIDTH}
271+
tickLine={false}
272+
axisLine={false}
273+
tickFormatter={getYAxisTickFormatter()}
274+
tick={<YAxisTick />}
275+
/>
276+
{/* Invisible area to maintain scale synchronization */}
277+
{dataKeys.map((key) => {
278+
return (
279+
<Area
280+
key={`y-axis-${key}`}
281+
dataKey={key}
282+
type={variant}
283+
stroke="none"
284+
fill="transparent"
285+
fillOpacity={0}
286+
stackId="a"
287+
/>
288+
);
289+
})}
290+
</RechartsAreaChart>
291+
</div>
292+
);
293+
}, [showYAxis, chartHeight, data, dataKeys, variant, id, maxLabelHeight]);
294+
249295
return (
250296
<SideBarTooltipProvider
251297
isSideBarTooltipOpen={isSideBarTooltipOpen}
@@ -260,46 +306,8 @@ const AreaChartComponent = <T extends AreaChartData>({
260306
}}
261307
>
262308
<div className="crayon-area-chart-container-inner" ref={chartContainerRef}>
263-
{showYAxis && (
264-
<div className="crayon-area-chart-y-axis-container">
265-
{/* Y-axis only chart - synchronized with main chart */}
266-
<RechartsAreaChart
267-
key={`y-axis-chart-${id}`}
268-
width={Y_AXIS_WIDTH}
269-
height={chartHeight}
270-
data={data}
271-
margin={{
272-
top: 20,
273-
bottom: 32, // this is required for to give space for x-axis
274-
left: 0,
275-
right: 0,
276-
}}
277-
syncId={chartSyncID}
278-
>
279-
<YAxis
280-
width={Y_AXIS_WIDTH}
281-
tickLine={false}
282-
axisLine={false}
283-
tickFormatter={getYAxisTickFormatter()}
284-
tick={<YAxisTick />}
285-
/>
286-
{/* Invisible area to maintain scale synchronization */}
287-
{dataKeys.map((key) => {
288-
return (
289-
<Area
290-
key={`y-axis-${key}`}
291-
dataKey={key}
292-
type={variant}
293-
stroke="none"
294-
fill="transparent"
295-
fillOpacity={0}
296-
stackId="a"
297-
/>
298-
);
299-
})}
300-
</RechartsAreaChart>
301-
</div>
302-
)}
309+
{/* Y-axis of the chart */}
310+
{yAxis}
303311
<div className="crayon-area-chart-main-container" ref={mainContainerRef}>
304312
<ChartContainer
305313
config={chartConfig}
@@ -317,7 +325,6 @@ const AreaChartComponent = <T extends AreaChartData>({
317325
top: 20,
318326
bottom: 0,
319327
}}
320-
syncId={chartSyncID}
321328
onClick={onAreaClick}
322329
>
323330
{grid && cartesianGrid()}
@@ -328,17 +335,18 @@ const AreaChartComponent = <T extends AreaChartData>({
328335
textAnchor="middle"
329336
interval={0}
330337
tickFormatter={xAxisTickFormatter}
338+
height={maxLabelHeight}
331339
tick={
332340
<XAxisTick
333-
getPositionOffset={xAxisPositionData.getPositionOffset}
334-
isFirstTick={xAxisPositionData.isFirstTick}
335-
isLastTick={xAxisPositionData.isLastTick}
341+
variant={tickVariant}
342+
widthOfGroup={widthOfGroup}
343+
labelHeight={maxLabelHeight}
336344
/>
337345
}
338346
orientation="bottom"
339347
padding={{
340-
left: 25,
341-
right: 20,
348+
left: X_AXIS_PADDING,
349+
right: X_AXIS_PADDING,
342350
}}
343351
/>
344352

@@ -387,37 +395,15 @@ const AreaChartComponent = <T extends AreaChartData>({
387395
{isSideBarTooltipOpen && <SideBarTooltip height={chartHeight} />}
388396
</div>
389397
{/* if the data width is greater than the effective width, then show the scroll buttons */}
390-
{dataWidth > effectiveWidth && (
391-
<div className="crayon-area-chart-scroll-container">
392-
<IconButton
393-
className={clsx(
394-
"crayon-area-chart-scroll-button crayon-area-chart-scroll-button--left",
395-
{
396-
"crayon-area-chart-scroll-button--disabled": !canScrollLeft,
397-
},
398-
)}
399-
icon={<ChevronLeft />}
400-
variant="secondary"
401-
onClick={scrollLeft}
402-
size="extra-small"
403-
disabled={!canScrollLeft}
404-
/>
405-
<IconButton
406-
className={clsx(
407-
"crayon-area-chart-scroll-button crayon-area-chart-scroll-button--right",
408-
{
409-
"crayon-area-chart-scroll-button--disabled": !canScrollRight,
410-
"crayon-area-chart-scroll-button--SideBarTooltip": isSideBarTooltipOpen,
411-
},
412-
)}
413-
icon={<ChevronRight />}
414-
variant="secondary"
415-
size="extra-small"
416-
onClick={scrollRight}
417-
disabled={!canScrollRight}
418-
/>
419-
</div>
420-
)}
398+
<ScrollButtonsHorizontal
399+
dataWidth={dataWidth}
400+
effectiveWidth={effectiveWidth}
401+
canScrollLeft={canScrollLeft}
402+
canScrollRight={canScrollRight}
403+
isSideBarTooltipOpen={isSideBarTooltipOpen}
404+
onScrollLeft={scrollLeft}
405+
onScrollRight={scrollRight}
406+
/>
421407
{legend && (
422408
<DefaultLegend
423409
items={legendItems}

js/packages/react-ui/src/components/Charts/AreaChart/areaChart.scss

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,3 @@
2323
/* Hide scrollbar for IE and Edge */
2424
-ms-overflow-style: none;
2525
}
26-
27-
.crayon-area-chart-scroll-container {
28-
position: relative;
29-
}
30-
31-
button.crayon-area-chart-scroll-button {
32-
position: absolute;
33-
background-color: cssUtils.$bg-container;
34-
35-
&:hover {
36-
background-color: cssUtils.$bg-container;
37-
}
38-
39-
&--left {
40-
top: -15px;
41-
transform: translateY(-50%);
42-
left: 20px;
43-
}
44-
45-
&--right {
46-
top: -15px;
47-
transform: translateY(-50%);
48-
right: 0px;
49-
}
50-
51-
&--disabled {
52-
visibility: hidden;
53-
cursor: not-allowed;
54-
transition: visibility 0.1s linear;
55-
}
56-
57-
&--SideBarTooltip {
58-
top: -15px;
59-
transform: translateY(-50%);
60-
right: 185px;
61-
}
62-
}

0 commit comments

Comments
 (0)