@spider-analyzer/timeline 5.0.3 → 5.0.5

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": "@spider-analyzer/timeline",
3
- "version": "5.0.3",
3
+ "version": "5.0.5",
4
4
  "description": "React graphical component to display metric over time with a time selection feature.",
5
5
  "author": "Thibaut Raballand <spider.analyzer@gmail.com> (https://spider-analyzer.io)",
6
6
  "license": "MIT",
package/src/TimeLine.tsx CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  useCallback,
4
4
  useEffect,
5
5
  useImperativeHandle,
6
+ useMemo,
6
7
  useRef,
7
8
  useState,
8
9
  } from 'react';
@@ -188,9 +189,19 @@ const TimeLineInner = forwardRef<TimeLineHandle, any>(function TimeLine(props, r
188
189
  xAxis.clamp(true);
189
190
  xAxisRef.current = xAxis;
190
191
 
191
- const ticks = xAxis.ticks(_floor(histoWidth / p.xAxis.spaceBetweenTicks));
192
+ const tickCount = _floor(histoWidth / p.xAxis.spaceBetweenTicks);
193
+ const ticks = xAxis.ticks(tickCount);
194
+ // intervalMs should be domainSpan/(visibleTickCount*barsBetweenTicks).
195
+ // Use ticks[1]-ticks[0] when d3 gives us >= 2 "nice" ticks (preserves
196
+ // the original intent of snapping to minute/hour-aligned intervals);
197
+ // otherwise fall back to domainSpan/tickCount so we never regress to
198
+ // smallestResolution on the very first render (when tick generation
199
+ // can be momentarily sparse).
200
+ const tickIntervalMs = ticks.length >= 2
201
+ ? ticks[1].getTime() - ticks[0].getTime()
202
+ : (+domain.max - +domain.min) / Math.max(tickCount, 1);
192
203
  const intervalMs = _max([
193
- _round(moment(ticks[1]).diff(moment(ticks[0])) / p.xAxis.barsBetweenTicks),
204
+ _round(tickIntervalMs / p.xAxis.barsBetweenTicks),
194
205
  propsRef.current.smallestResolution.asMilliseconds(),
195
206
  ]) as number;
196
207
 
@@ -887,27 +898,66 @@ const TimeLine = forwardRef<TimeLineHandle, any>(function TimeLineWrapper(props,
887
898
  else if (ret) apply(ret);
888
899
  }, [props.onLoadDefaultDomain, props.onDomainChange, zone]);
889
900
 
890
- // Build the moment-typed props the inner expects. Apply the same
891
- // defaults the inner destructures locally so that ref-path accesses
892
- // (propsRef.current.xAxis.spaceBetweenTicks etc.) never trip on an
893
- // undefined consumer prop.
901
+ // Normalize incoming props into moment-typed values the inner expects.
902
+ // MEMOIZE on the raw consumer reference so that a parent re-render
903
+ // with an unchanged `histo` (etc.) doesn't hand the inner a fresh
904
+ // object otherwise the inner's `histo !== prev` effect fires every
905
+ // render → setState → re-render → infinite loop (observed in
906
+ // Network-View: x-axis never stabilizes, loader never clears).
907
+ const timeSpanMoments = useMemo(
908
+ () => (props.timeSpan ? timeSpanToMoments(props.timeSpan, zone) : undefined),
909
+ [props.timeSpan, zone],
910
+ );
911
+ const maxDomainMoments = useMemo(
912
+ () => (props.maxDomain ? domainToMoments(props.maxDomain, zone) : undefined),
913
+ [props.maxDomain, zone],
914
+ );
915
+ const histoMoments = useMemo(
916
+ () => (props.histo?.items
917
+ ? { ...props.histo, items: props.histo.items.map((it: any) => ({ ...it, time: toMoment(it.time, zone) })) }
918
+ : props.histo),
919
+ [props.histo, zone],
920
+ );
921
+ const qualityMoments = useMemo(
922
+ () => (props.quality?.items
923
+ ? { ...props.quality, items: props.quality.items.map((it: any) => ({ ...it, time: toMoment(it.time, zone) })) }
924
+ : props.quality),
925
+ [props.quality, zone],
926
+ );
927
+ const biggestVisibleDomainDur = useMemo(
928
+ () => (props.biggestVisibleDomain != null ? toDuration(props.biggestVisibleDomain) : undefined),
929
+ [props.biggestVisibleDomain],
930
+ );
931
+ const biggestTimeSpanDur = useMemo(
932
+ () => (props.biggestTimeSpan != null ? toDuration(props.biggestTimeSpan) : undefined),
933
+ [props.biggestTimeSpan],
934
+ );
935
+ const smallestResolutionDur = useMemo(
936
+ () => (props.smallestResolution != null ? toDuration(props.smallestResolution) : undefined),
937
+ [props.smallestResolution],
938
+ );
939
+ const xAxisMerged = useMemo(
940
+ () => ({ ...xAxisDefault, ...(props.xAxis ?? {}) }),
941
+ [props.xAxis],
942
+ );
943
+ const marginMerged = useMemo(
944
+ () => ({ ...marginDefault, ...(props.margin ?? {}) }),
945
+ [props.margin],
946
+ );
947
+
894
948
  const innerProps: any = {
895
949
  ...props,
896
- xAxis: { ...xAxisDefault, ...(props.xAxis ?? {}) },
950
+ xAxis: xAxisMerged,
897
951
  yAxis: props.yAxis ?? {},
898
- margin: { ...marginDefault, ...(props.margin ?? {}) },
952
+ margin: marginMerged,
899
953
  domains: stack,
900
- timeSpan: props.timeSpan ? timeSpanToMoments(props.timeSpan, zone) : undefined,
901
- maxDomain: props.maxDomain ? domainToMoments(props.maxDomain, zone) : undefined,
902
- histo: props.histo?.items
903
- ? { ...props.histo, items: props.histo.items.map((it: any) => ({ ...it, time: toMoment(it.time, zone) })) }
904
- : props.histo,
905
- quality: props.quality?.items
906
- ? { ...props.quality, items: props.quality.items.map((it: any) => ({ ...it, time: toMoment(it.time, zone) })) }
907
- : props.quality,
908
- biggestVisibleDomain: props.biggestVisibleDomain != null ? toDuration(props.biggestVisibleDomain) : undefined,
909
- biggestTimeSpan: props.biggestTimeSpan != null ? toDuration(props.biggestTimeSpan) : undefined,
910
- smallestResolution: props.smallestResolution != null ? toDuration(props.smallestResolution) : undefined,
954
+ timeSpan: timeSpanMoments,
955
+ maxDomain: maxDomainMoments,
956
+ histo: histoMoments,
957
+ quality: qualityMoments,
958
+ biggestVisibleDomain: biggestVisibleDomainDur,
959
+ biggestTimeSpan: biggestTimeSpanDur,
960
+ smallestResolution: smallestResolutionDur,
911
961
  onUpdateDomains: handleUpdateDomains,
912
962
  onLoadDefaultDomain: handleLoadDefault,
913
963
  // onLoadHisto: object shape + Date instants