gifted-charts-core 0.0.10 → 0.0.12

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gifted-charts-core",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Mathematical and logical utilities used by react-gifted-charts and react-native-gifted-charts",
5
5
  "main": "index.ts",
6
6
  "files": [
@@ -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 { animations } = props;
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 clone(props.data).map((item) => {
127
+ return nullishHandledData.map((item) => {
115
128
  item.value = item.value - (props.yAxisOffset ?? 0);
116
129
  return item;
117
130
  });
118
131
  }
119
- return props.data;
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 clone(props.data2).map((item) => {
145
+ return nullishHandledData.map((item) => {
127
146
  item.value = item.value - (props.yAxisOffset ?? 0);
128
147
  return item;
129
148
  });
130
149
  }
131
- return props.data2;
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 clone(props.data3).map((item) => {
163
+ return nullishHandledData.map((item) => {
139
164
  item.value = item.value - (props.yAxisOffset ?? 0);
140
165
  return item;
141
166
  });
142
167
  }
143
- return props.data3;
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 clone(props.data4).map((item) => {
181
+ return nullishHandledData.map((item) => {
151
182
  item.value = item.value - (props.yAxisOffset ?? 0);
152
183
  return item;
153
184
  });
154
185
  }
155
- return props.data4;
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 clone(props.data5).map((item) => {
199
+ return nullishHandledData.map((item) => {
163
200
  item.value = item.value - (props.yAxisOffset ?? 0);
164
201
  return item;
165
202
  });
166
203
  }
167
- return props.data5;
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
- const dataSet = props.dataSet;
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,11 +275,21 @@ 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 = props.lineSegments;
228
- const lineSegments2 = props.lineSegments2;
229
- const lineSegments3 = props.lineSegments3;
230
- const lineSegments4 = props.lineSegments4;
231
- const lineSegments5 = props.lineSegments5;
278
+ const lineSegments = !interpolateMissingValues
279
+ ? getLineSegmentsForMissingValues(props.data)
280
+ : props.lineSegments;
281
+ const lineSegments2 = !interpolateMissingValues
282
+ ? getLineSegmentsForMissingValues(props.data2)
283
+ : props.lineSegments2;
284
+ const lineSegments3 = !interpolateMissingValues
285
+ ? getLineSegmentsForMissingValues(props.data3)
286
+ : props.lineSegments3;
287
+ const lineSegments4 = !interpolateMissingValues
288
+ ? getLineSegmentsForMissingValues(props.data4)
289
+ : props.lineSegments4;
290
+ const lineSegments5 = !interpolateMissingValues
291
+ ? getLineSegmentsForMissingValues(props.data5)
292
+ : props.lineSegments5;
232
293
 
233
294
  const highlightedRange = props.highlightedRange;
234
295
 
@@ -242,7 +303,7 @@ export const useLineChart = (props: extendedLineChartPropsType) => {
242
303
  const endSpacing =
243
304
  props.endSpacing ?? (adjustToWidth ? 0 : LineDefaults.endSpacing);
244
305
 
245
- const thickness = props.thickness || LineDefaults.thickness;
306
+ const thickness = props.thickness ?? LineDefaults.thickness;
246
307
 
247
308
  const yAxisLabelWidth =
248
309
  props.yAxisLabelWidth ??
@@ -693,24 +754,27 @@ export const useLineChart = (props: extendedLineChartPropsType) => {
693
754
  );
694
755
  };
695
756
 
696
- const getNextPoint = (data, index, around) => {
757
+ const getNextPoint = (data, index, around, before) => {
697
758
  const isLast = index === data.length - 1;
698
- return isLast && !around
759
+ return isLast && !(around || before)
699
760
  ? " "
700
761
  : " L" +
701
- (getX(index) + (around ? (isLast ? 0 : spacing / 2) : spacing)) +
762
+ (getX(index) +
763
+ (around ? (isLast ? 0 : spacing / 2) : before ? 0 : spacing)) +
702
764
  " " +
703
765
  getY(data[index].value) +
704
766
  " ";
705
767
  };
706
768
  const getStepPath = (data, i) => {
707
769
  const around = edgePosition === EdgePosition.AROUND_DATA_POINT;
770
+ const before = edgePosition === EdgePosition.BEFORE_DATA_POINT;
708
771
  return (
709
772
  "L" +
710
- (getX(i) - (around && i > 0 ? spacing / 2 : 0)) +
773
+ (getX(i) -
774
+ (around && i > 0 ? spacing / 2 : before && i > 0 ? spacing : 0)) +
711
775
  " " +
712
776
  getY(data[i].value) +
713
- getNextPoint(data, i, around)
777
+ getNextPoint(data, i, around, before)
714
778
  );
715
779
  };
716
780
 
@@ -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 = {
@@ -234,7 +234,7 @@ export const LineDefaults = {
234
234
  stripWidth: 2,
235
235
  unFocusOnPressOut: true,
236
236
  delayBeforeUnFocus: 300,
237
- edgePosition: EdgePosition.AT_DATA_POINT,
237
+ edgePosition: EdgePosition.AFTER_DATA_POINT,
238
238
  endReachedOffset: defaultEndReachedOffset,
239
239
  };
240
240
 
@@ -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 && secondaryYAxis?.yAxisOffset) {
726
- return secondaryData?.map((item) => {
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 secondaryData;
742
+ return nullishHandledData;
732
743
  };
733
744
 
734
745
  export const getArrowProperty = (
@@ -1209,3 +1220,156 @@ 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[] | undefined => {
1344
+ if (!data?.length) return undefined;
1345
+ let i,
1346
+ n = data.length;
1347
+ const numericValuesLength = data.filter(
1348
+ (item) => typeof item.value === "number"
1349
+ ).length;
1350
+ if (!numericValuesLength) return undefined;
1351
+ const segments: LineSegment[] = [];
1352
+ for (i = 0; i < n; i++) {
1353
+ if (typeof data[i].value !== "number") {
1354
+ const nextValidInd = data
1355
+ .slice(i + 1, n)
1356
+ .findIndex((item) => typeof item.value === "number");
1357
+ if (nextValidInd === -1) {
1358
+ segments.push({
1359
+ startIndex: Math.max(i - 1, 0),
1360
+ endIndex: n,
1361
+ color: "transparent",
1362
+ });
1363
+ break;
1364
+ }
1365
+ const nextValidIndex = nextValidInd + i + 1;
1366
+ segments.push({
1367
+ startIndex: Math.max(i - 1, 0),
1368
+ endIndex: nextValidIndex,
1369
+ color: "transparent",
1370
+ });
1371
+ i = nextValidIndex;
1372
+ }
1373
+ }
1374
+ return segments;
1375
+ };
@@ -16,8 +16,9 @@ export enum CurveType {
16
16
  }
17
17
 
18
18
  export enum EdgePosition {
19
- AT_DATA_POINT,
19
+ AFTER_DATA_POINT,
20
20
  AROUND_DATA_POINT,
21
+ BEFORE_DATA_POINT,
21
22
  }
22
23
 
23
24
  export type RulesConfig = {