Skip to content

Commit e9624d5

Browse files
authored
fix: Update the dots container dynamically when dimensions change (#17)
1 parent b148774 commit e9624d5

File tree

8 files changed

+229
-117
lines changed

8 files changed

+229
-117
lines changed

src/lib/dot-matrix/Chart.tsx

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,24 @@
11
import React, { useMemo } from "react";
22

3-
import { getNumberOfDots, getStyles, hasOverlapping } from "./utils/utils";
4-
import {
5-
DEFAULT_COLUMNS,
6-
DEFAULT_DOT_WIDTH,
7-
DEFAULT_ROWS,
8-
Elements
9-
} from "./constants";
10-
import { ChartProps, DataPointType } from "./types";
3+
import { getGradient, getStyles } from "./utils/utils";
4+
import { DEFAULT_COLUMNS, DEFAULT_DOT_WIDTH, Elements } from "./constants";
5+
import { ChartProps, DotsType } from "./types";
116
import classes from "./styles.module.scss";
127

138
const Chart = (props: ChartProps): JSX.Element => {
149
const {
1510
dimensions = {},
1611
styles,
1712
dotsToBeMapped,
18-
fractionalDots,
19-
total,
2013
width,
2114
spaceBetweenDots
2215
} = props;
2316

24-
const { rows = DEFAULT_ROWS, columns = DEFAULT_COLUMNS } = dimensions;
17+
const { columns = DEFAULT_COLUMNS } = dimensions;
2518

2619
const dotWidth = useMemo(
2720
() => (width ? width / columns - spaceBetweenDots : DEFAULT_DOT_WIDTH),
28-
[width]
21+
[width, columns, spaceBetweenDots]
2922
);
3023

3124
return (
@@ -36,42 +29,33 @@ const Chart = (props: ChartProps): JSX.Element => {
3629
...getStyles(Elements.DotsContainer, styles)
3730
}}
3831
>
39-
{dotsToBeMapped?.map((dataItem: DataPointType, rowIndex: number) => (
40-
<React.Fragment key={`${dataItem.name}-${rowIndex}`}>
41-
{dataItem &&
42-
[...Array(getNumberOfDots(dataItem, rows, columns, total))].map(
43-
(item: null, columnIndex: number) => (
44-
<div
45-
id="dot-matrix-dots"
46-
key={`${dataItem.name}-${rowIndex}-${columnIndex}`}
47-
>
48-
{hasOverlapping(fractionalDots, rowIndex, columnIndex) ? (
49-
<div
50-
className={classes.eachDot}
51-
style={{
52-
backgroundImage: `linear-gradient(to right, ${
53-
dotsToBeMapped[rowIndex - 1].color
54-
} 20%, ${dataItem?.color} 50%)`,
55-
width: `${dotWidth}px`,
56-
height: `${dotWidth}px`,
57-
...getStyles(Elements.Dot, styles)
58-
}}
59-
/>
60-
) : (
61-
<div
62-
className={classes.eachDot}
63-
style={{
64-
backgroundColor: dataItem?.color,
65-
width: `${dotWidth}px`,
66-
height: `${dotWidth}px`,
67-
...getStyles(Elements.Dot, styles)
68-
}}
69-
id={`each-category-${rowIndex}-${columnIndex}`}
70-
/>
71-
)}
72-
</div>
73-
)
74-
)}
32+
{dotsToBeMapped?.map((dataItem: DotsType, rowIndex: number) => (
33+
<React.Fragment key={`${dataItem.id}-${rowIndex}`}>
34+
{[...Array(dataItem.count)].map((item: null, columnIndex: number) => (
35+
<div
36+
id="dot-matrix-dots"
37+
key={`${dataItem.id}-${rowIndex}-${columnIndex}`}
38+
>
39+
<div
40+
className={classes.eachDot}
41+
style={{
42+
backgroundColor: dataItem?.color,
43+
backgroundImage:
44+
Array.isArray(dataItem.gradientColors) &&
45+
Array.isArray(dataItem.gradientPercentage)
46+
? getGradient(
47+
dataItem.gradientColors,
48+
dataItem.gradientPercentage
49+
)
50+
: "",
51+
width: `${dotWidth}px`,
52+
height: `${dotWidth}px`,
53+
...getStyles(Elements.Dot, styles)
54+
}}
55+
id={`each-category-${rowIndex}-${columnIndex}`}
56+
/>
57+
</div>
58+
))}
7559
</React.Fragment>
7660
))}
7761
</div>

src/lib/dot-matrix/DotMatrix.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22

33
import { useDotMatrix } from "./custom-hooks/useDotMatrix";
4-
import { getLegendPosition, getStyles } from "./utils/utils";
4+
import { getLegendPosition, getStyles, getUniqueDots } from "./utils/utils";
55
import {
66
DEFAULT_COLUMNS,
77
DEFAULT_GAP,
@@ -30,13 +30,13 @@ const DotMatrix = (props: DotMatrixPropType): JSX.Element => {
3030

3131
const width = useChartContainerWidth("dots-container", [
3232
showLegend,
33-
legendPosition
33+
legendPosition,
34+
dimensions.rows || DEFAULT_ROWS,
35+
dimensions.columns || DEFAULT_COLUMNS,
36+
spaceBetweenDots
3437
]);
3538

36-
const [dotsToBeMapped, totalDots, fractionalDots] = useDotMatrix(
37-
dataPoints,
38-
dimensions
39-
);
39+
const dotsToBeMapped = useDotMatrix(dataPoints, dimensions);
4040

4141
return (
4242
<div className={classes.container}>
@@ -52,13 +52,20 @@ const DotMatrix = (props: DotMatrixPropType): JSX.Element => {
5252
styles={styles}
5353
dimensions={dimensions}
5454
dotsToBeMapped={dotsToBeMapped}
55-
total={totalDots}
5655
width={width}
57-
fractionalDots={fractionalDots}
5856
spaceBetweenDots={spaceBetweenDots}
5957
/>
6058
</div>
61-
{showLegend && <Legend styles={styles} data={dotsToBeMapped} />}
59+
{showLegend && (
60+
<Legend
61+
styles={styles}
62+
data={getUniqueDots(dotsToBeMapped).filter(
63+
(dots) =>
64+
dots.color ||
65+
(dots?.gradientColors && dots.gradientColors.length > 0)
66+
)}
67+
/>
68+
)}
6269
</div>
6370
</div>
6471
);

src/lib/dot-matrix/Legend.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22

33
import { getStyles } from "./utils/utils";
4-
import { DataPointType, LegendProps } from "./types";
4+
import { DotsType, LegendProps } from "./types";
55
import { Elements } from "./constants";
66
import classes from "./styles.module.scss";
77

@@ -14,12 +14,12 @@ const Legend = (props: LegendProps): JSX.Element => {
1414
className={classes.legends}
1515
style={{ ...getStyles(Elements.LegendContainer, styles) }}
1616
>
17-
{data?.map((point: DataPointType, index) => (
17+
{data?.map((point: DotsType, index) => (
1818
<div className={classes.legend} key={`${point.name}-${index}`}>
1919
<div
2020
className={classes.legendDot}
2121
style={{
22-
backgroundColor: point.color,
22+
backgroundColor: point.color || (point.gradientColors && point.gradientColors[0]),
2323
...getStyles(Elements.LegendDot, styles)
2424
}}
2525
/>
@@ -35,4 +35,4 @@ const Legend = (props: LegendProps): JSX.Element => {
3535
);
3636
};
3737

38-
export default Legend;
38+
export default Legend;

src/lib/dot-matrix/custom-hooks/useChartContainerWidth.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,16 @@ import { findContainerWidth } from "../utils/utils";
55

66
const useChartContainerWidth = (
77
id: string,
8-
dependencyArray: Array<boolean | string>
8+
dependencyArray: Array<boolean | string | number>
99
): number => {
1010
const [width, setWidth] = useState<number>(DEFAULT_DOT_CONTAINER_WIDTH);
1111

12-
useEffect(() => {
13-
updateContainerWidth();
14-
}, []);
15-
1612
useEffect(() => {
1713
updateContainerWidth();
1814
}, [...dependencyArray]);
1915

2016
useEffect(() => {
21-
if (typeof window !== undefined) {
17+
if (typeof window !== "undefined") {
2218
window.addEventListener("resize", updateContainerWidth);
2319

2420
return () => {
@@ -29,8 +25,10 @@ const useChartContainerWidth = (
2925

3026
const updateContainerWidth = (): void => {
3127
let widthValue;
32-
if (typeof window !== undefined) widthValue = findContainerWidth(id);
33-
if (widthValue) setWidth(widthValue);
28+
if (typeof window !== "undefined") {
29+
widthValue = findContainerWidth(id);
30+
setWidth(widthValue);
31+
}
3432
};
3533
return width;
3634
};
Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { useMemo } from "react";
22

3-
import { DataPointType, DimensionProp } from "../types";
3+
import { DataPointType, DimensionProp, DotsType } from "../types";
44
import { COLOR_PALETTE, DEFAULT_COLUMNS, DEFAULT_ROWS } from "../constants";
5-
import { isColorAlreadyUsed } from "../utils/utils";
5+
import {
6+
isColorAlreadyUsed,
7+
isDecimal,
8+
mergeAndSortById
9+
} from "../utils/utils";
610

711
export const useDotMatrix = (
812
dataPoints: DataPointType[],
913
dimensions: DimensionProp
10-
): [DataPointType[], number, number[]] => {
11-
const [dotsToBeMapped, totalDots] = useMemo(() => {
14+
): DotsType[] => {
15+
const [dotsWithColor, totalDots] = useMemo(() => {
1216
const dotMatrixData: DataPointType[] = [];
1317
let totalCount = 0;
1418
if (dataPoints) {
@@ -25,23 +29,71 @@ export const useDotMatrix = (
2529
dotMatrixData.push({ ...point, color });
2630
});
2731
}
28-
return [dotMatrixData, totalCount]
32+
return [dotMatrixData, totalCount];
2933
}, [dataPoints]);
3034

31-
// Calculates fractional part of a category based on the provided data points
32-
// relative to the total number of dots and dimension
33-
const fractionalDots: number[] = useMemo(() => {
34-
const fractionalParts: Array<number> = [];
35+
//finding the number of dots to be rendered relative to the total number of dots and dimension
36+
const dots = useMemo(() => {
37+
const completeDots: number[] = [];
3538
if (totalDots) {
36-
dotsToBeMapped?.forEach((point: DataPointType) => {
39+
dotsWithColor?.forEach((point: DataPointType) => {
3740
const { rows = DEFAULT_ROWS, columns = DEFAULT_COLUMNS } = dimensions;
3841
const pointPercentage = point.count / totalDots;
3942
const dotsCount = pointPercentage * rows * columns;
40-
const dotFraction = dotsCount - Math.floor(dotsCount);
41-
fractionalParts?.push(dotFraction);
43+
completeDots.push(dotsCount);
4244
});
4345
}
44-
return fractionalParts;
45-
}, [totalDots]);
46-
return [dotsToBeMapped, totalDots, fractionalDots];
47-
};
46+
return completeDots;
47+
}, [totalDots, dimensions.columns, dimensions.rows]);
48+
49+
const dotsToBeMapped = useMemo(() => {
50+
//Calculating the dots with gradient and subtracting the gradient part from the total number of dots
51+
const currentDots = [...dots];
52+
const gradientDots: DotsType[] = [];
53+
for (let i = 0; i < currentDots.length - 1; i++) {
54+
if (isDecimal(currentDots[i])) {
55+
const roundedCurrentDotCount = Math.floor(currentDots[i]);
56+
let remainingDecimal =
57+
1 - (currentDots[i] - roundedCurrentDotCount);
58+
const gradientColors = [dotsWithColor[i].color];
59+
const percentage = [currentDots[i] - roundedCurrentDotCount];
60+
let j = i + 1;
61+
while (remainingDecimal !== 0 && j < currentDots.length) {
62+
if (currentDots[j] >= remainingDecimal) {
63+
percentage.push(remainingDecimal);
64+
currentDots[j] = currentDots[j] - remainingDecimal;
65+
remainingDecimal = 0;
66+
} else {
67+
remainingDecimal = remainingDecimal - dots[j];
68+
percentage.push(currentDots[j] - Math.floor(currentDots[j]));
69+
currentDots[j] = 0;
70+
}
71+
currentDots[i] = roundedCurrentDotCount;
72+
gradientColors.push(dotsWithColor[j].color);
73+
j++;
74+
}
75+
gradientDots.push({
76+
id: i,
77+
count: 1,
78+
name: dotsWithColor[i].name,
79+
gradientColors: gradientColors,
80+
gradientPercentage: percentage
81+
});
82+
}
83+
}
84+
//Calculating the remaining dots with single color
85+
const singleDots: DotsType[] = [];
86+
for (let i = 0; i < currentDots.length; i++) {
87+
singleDots.push({
88+
id: i,
89+
name: dotsWithColor[i].name,
90+
count: Math.round(currentDots[i]),
91+
color: dotsWithColor[i].color
92+
});
93+
}
94+
//merging both arrays and sorting it with respect to id
95+
return mergeAndSortById(gradientDots, singleDots);
96+
}, [dataPoints, dimensions.rows, dimensions.columns]);
97+
98+
return dotsToBeMapped;
99+
};

src/lib/dot-matrix/types.d.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ export interface DataPointType {
66
export interface ChartProps {
77
dimensions?: DimensionProp;
88
styles: StyleProp;
9-
dotsToBeMapped: DataPointType[];
10-
fractionalDots: number[];
11-
total: number;
9+
dotsToBeMapped: DotsType[];
1210
width: number;
1311
spaceBetweenDots: number;
1412
}
@@ -29,7 +27,7 @@ export type StyleProp = {
2927

3028
export interface LegendProps {
3129
styles: StyleProp;
32-
data: DataPointType[];
30+
data: DotsType[];
3331
}
3432

3533
export interface DotMatrixPropType {
@@ -52,3 +50,12 @@ export interface DotMatrixPropType {
5250
| "bottom-end";
5351
styles?: StyleProp;
5452
}
53+
54+
export interface DotsType {
55+
id: number;
56+
name?: string;
57+
count?: number;
58+
color?: string;
59+
gradientColors?: (string | undefined)[];
60+
gradientPercentage?: number[];
61+
}

0 commit comments

Comments
 (0)