gifted-charts-core 0.0.10 → 0.0.11
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/LineChart/index.ts +76 -20
- package/src/LineChart/types.ts +4 -0
- package/src/utils/constants.ts +1 -1
- package/src/utils/index.tsx +167 -4
- package/src/utils/types.ts +2 -1
package/package.json
CHANGED
package/src/LineChart/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
getAxesAndRulesProps,
|
|
18
18
|
getCurvePathWithSegments,
|
|
19
19
|
getExtendedContainerHeightWithPadding,
|
|
20
|
+
getInterpolatedData,
|
|
21
|
+
getLineSegmentsForMissingValues,
|
|
20
22
|
getMaxValue,
|
|
21
23
|
getNoOfSections,
|
|
22
24
|
getPathWithHighlight,
|
|
@@ -32,7 +34,12 @@ interface extendedLineChartPropsType extends LineChartPropsType {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export const useLineChart = (props: extendedLineChartPropsType) => {
|
|
35
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
animations,
|
|
39
|
+
showDataPointsForMissingValues,
|
|
40
|
+
interpolateMissingValues = true,
|
|
41
|
+
onlyPositive,
|
|
42
|
+
} = props;
|
|
36
43
|
const curvature = props.curvature ?? LineDefaults.curvature;
|
|
37
44
|
const curveType = props.curveType ?? LineDefaults.curveType;
|
|
38
45
|
const [scrollX, setScrollX] = useState(0);
|
|
@@ -110,70 +117,114 @@ export const useLineChart = (props: extendedLineChartPropsType) => {
|
|
|
110
117
|
if (!props.data) {
|
|
111
118
|
return [];
|
|
112
119
|
}
|
|
120
|
+
const nullishHandledData = getInterpolatedData(
|
|
121
|
+
props.data,
|
|
122
|
+
showDataPointsForMissingValues,
|
|
123
|
+
interpolateMissingValues,
|
|
124
|
+
onlyPositive
|
|
125
|
+
);
|
|
113
126
|
if (props.yAxisOffset) {
|
|
114
|
-
return
|
|
127
|
+
return nullishHandledData.map((item) => {
|
|
115
128
|
item.value = item.value - (props.yAxisOffset ?? 0);
|
|
116
129
|
return item;
|
|
117
130
|
});
|
|
118
131
|
}
|
|
119
|
-
return
|
|
132
|
+
return nullishHandledData;
|
|
120
133
|
}, [props.yAxisOffset, props.data]);
|
|
121
134
|
const data2 = useMemo(() => {
|
|
122
135
|
if (!props.data2) {
|
|
123
136
|
return [];
|
|
124
137
|
}
|
|
138
|
+
const nullishHandledData = getInterpolatedData(
|
|
139
|
+
props.data2,
|
|
140
|
+
showDataPointsForMissingValues,
|
|
141
|
+
interpolateMissingValues,
|
|
142
|
+
onlyPositive
|
|
143
|
+
);
|
|
125
144
|
if (props.yAxisOffset) {
|
|
126
|
-
return
|
|
145
|
+
return nullishHandledData.map((item) => {
|
|
127
146
|
item.value = item.value - (props.yAxisOffset ?? 0);
|
|
128
147
|
return item;
|
|
129
148
|
});
|
|
130
149
|
}
|
|
131
|
-
return
|
|
150
|
+
return nullishHandledData;
|
|
132
151
|
}, [props.yAxisOffset, props.data2]);
|
|
133
152
|
const data3 = useMemo(() => {
|
|
134
153
|
if (!props.data3) {
|
|
135
154
|
return [];
|
|
136
155
|
}
|
|
156
|
+
const nullishHandledData = getInterpolatedData(
|
|
157
|
+
props.data3,
|
|
158
|
+
showDataPointsForMissingValues,
|
|
159
|
+
interpolateMissingValues,
|
|
160
|
+
onlyPositive
|
|
161
|
+
);
|
|
137
162
|
if (props.yAxisOffset) {
|
|
138
|
-
return
|
|
163
|
+
return nullishHandledData.map((item) => {
|
|
139
164
|
item.value = item.value - (props.yAxisOffset ?? 0);
|
|
140
165
|
return item;
|
|
141
166
|
});
|
|
142
167
|
}
|
|
143
|
-
return
|
|
168
|
+
return nullishHandledData;
|
|
144
169
|
}, [props.yAxisOffset, props.data3]);
|
|
145
170
|
const data4 = useMemo(() => {
|
|
146
171
|
if (!props.data4) {
|
|
147
172
|
return [];
|
|
148
173
|
}
|
|
174
|
+
const nullishHandledData = getInterpolatedData(
|
|
175
|
+
props.data4,
|
|
176
|
+
showDataPointsForMissingValues,
|
|
177
|
+
interpolateMissingValues,
|
|
178
|
+
onlyPositive
|
|
179
|
+
);
|
|
149
180
|
if (props.yAxisOffset) {
|
|
150
|
-
return
|
|
181
|
+
return nullishHandledData.map((item) => {
|
|
151
182
|
item.value = item.value - (props.yAxisOffset ?? 0);
|
|
152
183
|
return item;
|
|
153
184
|
});
|
|
154
185
|
}
|
|
155
|
-
return
|
|
186
|
+
return nullishHandledData;
|
|
156
187
|
}, [props.yAxisOffset, props.data4]);
|
|
157
188
|
const data5 = useMemo(() => {
|
|
158
189
|
if (!props.data5) {
|
|
159
190
|
return [];
|
|
160
191
|
}
|
|
192
|
+
const nullishHandledData = getInterpolatedData(
|
|
193
|
+
props.data5,
|
|
194
|
+
showDataPointsForMissingValues,
|
|
195
|
+
interpolateMissingValues,
|
|
196
|
+
onlyPositive
|
|
197
|
+
);
|
|
161
198
|
if (props.yAxisOffset) {
|
|
162
|
-
return
|
|
199
|
+
return nullishHandledData.map((item) => {
|
|
163
200
|
item.value = item.value - (props.yAxisOffset ?? 0);
|
|
164
201
|
return item;
|
|
165
202
|
});
|
|
166
203
|
}
|
|
167
|
-
return
|
|
204
|
+
return nullishHandledData;
|
|
168
205
|
}, [props.yAxisOffset, props.data5]);
|
|
169
206
|
|
|
170
207
|
const secondaryData =
|
|
171
208
|
getSecondaryDataWithOffsetIncluded(
|
|
172
209
|
props.secondaryData,
|
|
173
|
-
props.secondaryYAxis
|
|
210
|
+
props.secondaryYAxis,
|
|
211
|
+
showDataPointsForMissingValues,
|
|
212
|
+
interpolateMissingValues,
|
|
213
|
+
onlyPositive
|
|
174
214
|
) || [];
|
|
175
215
|
|
|
176
|
-
|
|
216
|
+
let dataSet = props.dataSet;
|
|
217
|
+
if (dataSet?.length) {
|
|
218
|
+
dataSet = dataSet.map((dataSetItem) => ({
|
|
219
|
+
...dataSetItem,
|
|
220
|
+
data: getInterpolatedData(
|
|
221
|
+
dataSetItem.data,
|
|
222
|
+
showDataPointsForMissingValues,
|
|
223
|
+
interpolateMissingValues,
|
|
224
|
+
onlyPositive
|
|
225
|
+
),
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
177
228
|
const data0 = useMemo(() => {
|
|
178
229
|
if (props.yAxisOffset) {
|
|
179
230
|
return dataSet?.[0]?.data;
|
|
@@ -224,7 +275,9 @@ export const useLineChart = (props: extendedLineChartPropsType) => {
|
|
|
224
275
|
const startIndex5 = props.startIndex5 || 0;
|
|
225
276
|
const endIndex5 = props.endIndex5 ?? data5.length - 1;
|
|
226
277
|
|
|
227
|
-
const lineSegments =
|
|
278
|
+
const lineSegments = !interpolateMissingValues
|
|
279
|
+
? getLineSegmentsForMissingValues(data)
|
|
280
|
+
: props.lineSegments;
|
|
228
281
|
const lineSegments2 = props.lineSegments2;
|
|
229
282
|
const lineSegments3 = props.lineSegments3;
|
|
230
283
|
const lineSegments4 = props.lineSegments4;
|
|
@@ -242,7 +295,7 @@ export const useLineChart = (props: extendedLineChartPropsType) => {
|
|
|
242
295
|
const endSpacing =
|
|
243
296
|
props.endSpacing ?? (adjustToWidth ? 0 : LineDefaults.endSpacing);
|
|
244
297
|
|
|
245
|
-
const thickness = props.thickness
|
|
298
|
+
const thickness = props.thickness ?? LineDefaults.thickness;
|
|
246
299
|
|
|
247
300
|
const yAxisLabelWidth =
|
|
248
301
|
props.yAxisLabelWidth ??
|
|
@@ -693,24 +746,27 @@ export const useLineChart = (props: extendedLineChartPropsType) => {
|
|
|
693
746
|
);
|
|
694
747
|
};
|
|
695
748
|
|
|
696
|
-
const getNextPoint = (data, index, around) => {
|
|
749
|
+
const getNextPoint = (data, index, around, before) => {
|
|
697
750
|
const isLast = index === data.length - 1;
|
|
698
|
-
return isLast && !around
|
|
751
|
+
return isLast && !(around || before)
|
|
699
752
|
? " "
|
|
700
753
|
: " L" +
|
|
701
|
-
(getX(index) +
|
|
754
|
+
(getX(index) +
|
|
755
|
+
(around ? (isLast ? 0 : spacing / 2) : before ? 0 : spacing)) +
|
|
702
756
|
" " +
|
|
703
757
|
getY(data[index].value) +
|
|
704
758
|
" ";
|
|
705
759
|
};
|
|
706
760
|
const getStepPath = (data, i) => {
|
|
707
761
|
const around = edgePosition === EdgePosition.AROUND_DATA_POINT;
|
|
762
|
+
const before = edgePosition === EdgePosition.BEFORE_DATA_POINT;
|
|
708
763
|
return (
|
|
709
764
|
"L" +
|
|
710
|
-
(getX(i) -
|
|
765
|
+
(getX(i) -
|
|
766
|
+
(around && i > 0 ? spacing / 2 : before && i > 0 ? spacing : 0)) +
|
|
711
767
|
" " +
|
|
712
768
|
getY(data[i].value) +
|
|
713
|
-
getNextPoint(data, i, around)
|
|
769
|
+
getNextPoint(data, i, around, before)
|
|
714
770
|
);
|
|
715
771
|
};
|
|
716
772
|
|
package/src/LineChart/types.ts
CHANGED
|
@@ -319,6 +319,10 @@ export type LineChartPropsType = {
|
|
|
319
319
|
onEndReached?: () => void;
|
|
320
320
|
onStartReached?: () => void;
|
|
321
321
|
endReachedOffset?: number;
|
|
322
|
+
|
|
323
|
+
showDataPointsForMissingValues?: boolean;
|
|
324
|
+
interpolateMissingValues?: boolean;
|
|
325
|
+
onlyPositive?: boolean;
|
|
322
326
|
};
|
|
323
327
|
|
|
324
328
|
export type lineDataItem = {
|
package/src/utils/constants.ts
CHANGED
|
@@ -234,7 +234,7 @@ export const LineDefaults = {
|
|
|
234
234
|
stripWidth: 2,
|
|
235
235
|
unFocusOnPressOut: true,
|
|
236
236
|
delayBeforeUnFocus: 300,
|
|
237
|
-
edgePosition: EdgePosition.
|
|
237
|
+
edgePosition: EdgePosition.AFTER_DATA_POINT,
|
|
238
238
|
endReachedOffset: defaultEndReachedOffset,
|
|
239
239
|
};
|
|
240
240
|
|
package/src/utils/index.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { lineDataItem } from "../LineChart/types";
|
|
1
2
|
import {
|
|
2
3
|
AxesAndRulesDefaults,
|
|
3
4
|
BarDefaults,
|
|
@@ -720,15 +721,25 @@ export const getExtendedContainerHeightWithPadding = (
|
|
|
720
721
|
|
|
721
722
|
export const getSecondaryDataWithOffsetIncluded = (
|
|
722
723
|
secondaryData?: any,
|
|
723
|
-
secondaryYAxis?: any
|
|
724
|
+
secondaryYAxis?: any,
|
|
725
|
+
showDataPointsForMissingValues?: boolean,
|
|
726
|
+
interpolateMissingValues?: boolean,
|
|
727
|
+
onlyPositive?: boolean
|
|
724
728
|
) => {
|
|
725
|
-
if (secondaryData
|
|
726
|
-
|
|
729
|
+
if (!secondaryData) return secondaryData;
|
|
730
|
+
const nullishHandledData = getInterpolatedData(
|
|
731
|
+
secondaryData,
|
|
732
|
+
showDataPointsForMissingValues,
|
|
733
|
+
interpolateMissingValues,
|
|
734
|
+
onlyPositive
|
|
735
|
+
);
|
|
736
|
+
if (secondaryYAxis?.yAxisOffset) {
|
|
737
|
+
return nullishHandledData.map((item) => {
|
|
727
738
|
item.value = item.value - (secondaryYAxis?.yAxisOffset ?? 0);
|
|
728
739
|
return item;
|
|
729
740
|
});
|
|
730
741
|
}
|
|
731
|
-
return
|
|
742
|
+
return nullishHandledData;
|
|
732
743
|
};
|
|
733
744
|
|
|
734
745
|
export const getArrowProperty = (
|
|
@@ -1209,3 +1220,155 @@ export const getBarWidth = (
|
|
|
1209
1220
|
}
|
|
1210
1221
|
return localBarWidth;
|
|
1211
1222
|
};
|
|
1223
|
+
|
|
1224
|
+
export const getInterpolatedData = (
|
|
1225
|
+
dataParam: lineDataItem[],
|
|
1226
|
+
showDataPointsForMissingValues?: boolean,
|
|
1227
|
+
interpolateMissingValues?: boolean,
|
|
1228
|
+
onlyPositive?: boolean
|
|
1229
|
+
): lineDataItem[] => {
|
|
1230
|
+
if (!interpolateMissingValues) {
|
|
1231
|
+
return dataParam.map((item) => {
|
|
1232
|
+
if (typeof item.value !== "number") {
|
|
1233
|
+
if (showDataPointsForMissingValues) return { ...item, value: 0 };
|
|
1234
|
+
return { ...item, value: 0, hideDataPoint: true };
|
|
1235
|
+
}
|
|
1236
|
+
return item;
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
if (!interpolateMissingValues) return dataParam;
|
|
1240
|
+
const data = clone(dataParam);
|
|
1241
|
+
const n = data.length;
|
|
1242
|
+
|
|
1243
|
+
/************** PRE-PROCESSING **************/
|
|
1244
|
+
let numericValue;
|
|
1245
|
+
const numericValuesLength = data.filter((item) => {
|
|
1246
|
+
const isNum = typeof item.value === "number";
|
|
1247
|
+
if (isNum) {
|
|
1248
|
+
numericValue = item.value;
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
return false;
|
|
1252
|
+
}).length;
|
|
1253
|
+
|
|
1254
|
+
if (!numericValuesLength) return [];
|
|
1255
|
+
|
|
1256
|
+
if (numericValuesLength === 1) {
|
|
1257
|
+
data.forEach((item) => {
|
|
1258
|
+
if (!showDataPointsForMissingValues && typeof item.value !== "number") {
|
|
1259
|
+
item.hideDataPoint = true;
|
|
1260
|
+
}
|
|
1261
|
+
item.value = numericValue;
|
|
1262
|
+
});
|
|
1263
|
+
return data;
|
|
1264
|
+
}
|
|
1265
|
+
/**********************************************************************/
|
|
1266
|
+
|
|
1267
|
+
data.forEach((item, index) => {
|
|
1268
|
+
if (typeof item.value === "number") return;
|
|
1269
|
+
// Cut the line in 2 halves-> pre and post
|
|
1270
|
+
// Now there are 4 possibilities-
|
|
1271
|
+
// 1. Both pre and post have valid values
|
|
1272
|
+
// 2. Only pre has valid value
|
|
1273
|
+
// 3. Only post has valid value
|
|
1274
|
+
// 4. None has valid value -> this is already handled in preprocessing
|
|
1275
|
+
|
|
1276
|
+
const pre = data.slice(0, index);
|
|
1277
|
+
const post = data.slice(index + 1, n);
|
|
1278
|
+
|
|
1279
|
+
const preValidIndex = pre.findLastIndex(
|
|
1280
|
+
(item) => typeof item.value === "number"
|
|
1281
|
+
);
|
|
1282
|
+
const postValidInd = post.findIndex(
|
|
1283
|
+
(item) => typeof item.value === "number"
|
|
1284
|
+
);
|
|
1285
|
+
const postValidIndex = postValidInd + index + 1;
|
|
1286
|
+
|
|
1287
|
+
let count, step;
|
|
1288
|
+
|
|
1289
|
+
// 1. Both pre and post have valid values
|
|
1290
|
+
if (preValidIndex !== -1 && postValidInd !== -1) {
|
|
1291
|
+
count = postValidIndex - preValidIndex;
|
|
1292
|
+
step = (data[postValidIndex].value - data[preValidIndex].value) / count;
|
|
1293
|
+
data[index].value =
|
|
1294
|
+
data[preValidIndex].value + step * (index - preValidIndex);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// 2. Only pre has valid value
|
|
1298
|
+
else if (preValidIndex !== -1 && postValidInd === -1) {
|
|
1299
|
+
// Now there are 2 possibilities-
|
|
1300
|
+
// 1. There's only 1 valid value in the pre -> this is already handled in preprocessing
|
|
1301
|
+
// 2. There are more than valid values in pre
|
|
1302
|
+
const secondPre = data.slice(0, preValidIndex);
|
|
1303
|
+
const secondPreIndex = secondPre.findLastIndex(
|
|
1304
|
+
(item) => typeof item.value === "number"
|
|
1305
|
+
);
|
|
1306
|
+
|
|
1307
|
+
count = preValidIndex - secondPreIndex;
|
|
1308
|
+
step = (data[secondPreIndex].value - data[preValidIndex].value) / count;
|
|
1309
|
+
data[index].value =
|
|
1310
|
+
data[preValidIndex].value - step * (index - preValidIndex);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// 3. Only post has valid value
|
|
1314
|
+
else if (preValidIndex === -1 && postValidInd !== -1) {
|
|
1315
|
+
// Now there are 2 possibilities-
|
|
1316
|
+
// 1. There's only 1 valid value in the post -> this is already handled in preprocessing
|
|
1317
|
+
// 2. There are more than valid values in post
|
|
1318
|
+
|
|
1319
|
+
const secondPost = data.slice(postValidIndex + 1, n);
|
|
1320
|
+
const secondPostInd = secondPost.findIndex(
|
|
1321
|
+
(item) => typeof item.value === "number"
|
|
1322
|
+
);
|
|
1323
|
+
const secondPostIndex = secondPostInd + postValidIndex + 1;
|
|
1324
|
+
|
|
1325
|
+
count = secondPostIndex - postValidIndex;
|
|
1326
|
+
step = (data[secondPostIndex].value - data[postValidIndex].value) / count;
|
|
1327
|
+
data[index].value =
|
|
1328
|
+
data[postValidIndex].value - step * (postValidIndex - index);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// hide data point (since it is interpolated)
|
|
1332
|
+
if (!showDataPointsForMissingValues) {
|
|
1333
|
+
item.hideDataPoint = true;
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
return onlyPositive
|
|
1337
|
+
? data.map((item) => ({ ...item, value: Math.max(item.value, 0) }))
|
|
1338
|
+
: data;
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
export const getLineSegmentsForMissingValues = (
|
|
1342
|
+
data: lineDataItem[]
|
|
1343
|
+
): LineSegment[] => {
|
|
1344
|
+
let i,
|
|
1345
|
+
n = data.length;
|
|
1346
|
+
const numericValuesLength = data.filter(
|
|
1347
|
+
(item) => typeof item.value === "number"
|
|
1348
|
+
).length;
|
|
1349
|
+
if (!numericValuesLength) return [];
|
|
1350
|
+
const segments: LineSegment[] = [];
|
|
1351
|
+
for (i = 0; i < n; i++) {
|
|
1352
|
+
if (typeof data[i].value !== "number") {
|
|
1353
|
+
const nextValidInd = data
|
|
1354
|
+
.slice(i + 1, n)
|
|
1355
|
+
.findIndex((item) => typeof item.value === "number");
|
|
1356
|
+
if (nextValidInd === -1) {
|
|
1357
|
+
segments.push({
|
|
1358
|
+
startIndex: Math.max(i - 1, 0),
|
|
1359
|
+
endIndex: n,
|
|
1360
|
+
color: "transparent",
|
|
1361
|
+
});
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
const nextValidIndex = nextValidInd + i + 1;
|
|
1365
|
+
segments.push({
|
|
1366
|
+
startIndex: Math.max(i - 1, 0),
|
|
1367
|
+
endIndex: nextValidIndex,
|
|
1368
|
+
color: "transparent",
|
|
1369
|
+
});
|
|
1370
|
+
i = nextValidIndex;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return segments;
|
|
1374
|
+
};
|