@rfkit/charts 1.10.2 → 1.10.4

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.
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type React from 'react';
6
6
  import { type ReactNode } from 'react';
7
- import { type RectBounds, type RectPosition } from '../../hooks/useDragSelect';
7
+ import { type DragType, type RectBounds, type RectPosition } from '../../hooks/useDragSelect';
8
8
  type DragSelectBoxVariant = 'contained' | 'projected';
9
9
  export interface DragSelectBoxProps {
10
10
  enabled: boolean;
@@ -15,7 +15,7 @@ export interface DragSelectBoxProps {
15
15
  maxSize?: number;
16
16
  snapThreshold?: number;
17
17
  onChange?: (rect: RectPosition) => void;
18
- onDragStart?: () => boolean | undefined;
18
+ onDragStart?: (dragType: Exclude<DragType, null>) => boolean | undefined;
19
19
  onDragEnd?: (rect: RectPosition) => void;
20
20
  children?: ReactNode;
21
21
  labels?: {
package/index.js CHANGED
@@ -505,10 +505,6 @@ var __webpack_modules__ = {
505
505
  inset-inline: 0;
506
506
  }
507
507
 
508
- [data-rfkit-charts-root] .inset-y-0 {
509
- inset-block: 0;
510
- }
511
-
512
508
  [data-rfkit-charts-root] .top-0 {
513
509
  top: 0;
514
510
  }
@@ -613,10 +609,6 @@ var __webpack_modules__ = {
613
609
  isolation: isolate;
614
610
  }
615
611
 
616
- [data-rfkit-charts-root] .z-0 {
617
- z-index: 0;
618
- }
619
-
620
612
  [data-rfkit-charts-root] .z-10 {
621
613
  z-index: 10;
622
614
  }
@@ -872,10 +864,6 @@ var __webpack_modules__ = {
872
864
  height: calc(var(--spacing) * 1.5);
873
865
  }
874
866
 
875
- [data-rfkit-charts-root] .h-2 {
876
- height: calc(var(--spacing) * 2);
877
- }
878
-
879
867
  [data-rfkit-charts-root] .h-2\\.5 {
880
868
  height: calc(var(--spacing) * 2.5);
881
869
  }
@@ -22531,16 +22519,26 @@ const resolveHeatmapSourceRange = (snapshot, bounds)=>{
22531
22519
  };
22532
22520
  };
22533
22521
  const resolveHeatmapSnapshotRangeData = (snapshot, range)=>snapshot.rawData.slice(range.startIndex, range.endIndex + 1).map((row)=>Array.from(row.slice(range.startCol, range.endCol + 1)));
22534
- const resolveHeatmapSourceRangeScreenRect = (snapshot, range)=>{
22522
+ const resolveHeatmapSourceRangeScreenRect = (snapshot, range)=>resolveHeatmapContinuousSourceRangeScreenRect(snapshot, {
22523
+ x: [
22524
+ Math.min(range.startCol, range.endCol),
22525
+ Math.max(range.startCol, range.endCol) + 1
22526
+ ],
22527
+ y: [
22528
+ Math.min(range.startIndex, range.endIndex),
22529
+ Math.max(range.startIndex, range.endIndex) + 1
22530
+ ]
22531
+ });
22532
+ const resolveHeatmapContinuousSourceRangeScreenRect = (snapshot, range)=>{
22535
22533
  const [viewportXStart, viewportXEnd] = snapshot.range.x;
22536
22534
  const [viewportYStart, viewportYEnd] = snapshot.range.y;
22537
22535
  const viewportXSpan = viewportXEnd - viewportXStart;
22538
22536
  const viewportYSpan = viewportYEnd - viewportYStart;
22539
22537
  if (viewportXSpan <= 0 || viewportYSpan <= 0) return null;
22540
- const sourceXStart = Math.min(range.startCol, range.endCol);
22541
- const sourceXEnd = Math.max(range.startCol, range.endCol) + 1;
22542
- const sourceYStart = Math.min(range.startIndex, range.endIndex);
22543
- const sourceYEnd = Math.max(range.startIndex, range.endIndex) + 1;
22538
+ const sourceXStart = Math.min(range.x[0], range.x[1]);
22539
+ const sourceXEnd = Math.max(range.x[0], range.x[1]);
22540
+ const sourceYStart = Math.min(range.y[0], range.y[1]);
22541
+ const sourceYEnd = Math.max(range.y[0], range.y[1]);
22544
22542
  return {
22545
22543
  leftPosition: (sourceXStart - viewportXStart) * 100 / viewportXSpan,
22546
22544
  width: (sourceXEnd - sourceXStart) * 100 / viewportXSpan,
@@ -22549,19 +22547,40 @@ const resolveHeatmapSourceRangeScreenRect = (snapshot, range)=>{
22549
22547
  };
22550
22548
  };
22551
22549
  const SOURCE_EDGE_EPSILON = 1e-7;
22552
- const resolveHeatmapProjectedSourceRange = (snapshot, bounds)=>{
22550
+ const resolveHeatmapProjectedContinuousSourceRange = (snapshot, bounds)=>{
22553
22551
  const { sourceSize } = snapshot;
22554
22552
  const [viewportXStart, viewportXEnd] = snapshot.range.x;
22555
22553
  const [viewportYStart, viewportYEnd] = snapshot.range.y;
22556
22554
  const viewportXSpan = viewportXEnd - viewportXStart;
22557
22555
  const viewportYSpan = viewportYEnd - viewportYStart;
22558
22556
  if (sourceSize.width <= 0 || sourceSize.height <= 0 || viewportXSpan <= 0 || viewportYSpan <= 0) return null;
22559
- const sourceXStart = viewportXStart + Math.min(bounds.startLeft, bounds.endLeft) * viewportXSpan / 100;
22560
- const sourceXEnd = viewportXStart + Math.max(bounds.startLeft, bounds.endLeft) * viewportXSpan / 100;
22557
+ const sourceXStart = viewport_clamp(viewportXStart + Math.min(bounds.startLeft, bounds.endLeft) * viewportXSpan / 100, 0, sourceSize.width);
22558
+ const sourceXEnd = viewport_clamp(viewportXStart + Math.max(bounds.startLeft, bounds.endLeft) * viewportXSpan / 100, 0, sourceSize.width);
22561
22559
  const sourceYAtStart = viewportYStart + (100 - bounds.startTop) * viewportYSpan / 100;
22562
22560
  const sourceYAtEnd = viewportYStart + (100 - bounds.endTop) * viewportYSpan / 100;
22563
- const sourceYStart = Math.min(sourceYAtStart, sourceYAtEnd);
22564
- const sourceYEnd = Math.max(sourceYAtStart, sourceYAtEnd);
22561
+ const sourceYStart = viewport_clamp(Math.min(sourceYAtStart, sourceYAtEnd), 0, sourceSize.height);
22562
+ const sourceYEnd = viewport_clamp(Math.max(sourceYAtStart, sourceYAtEnd), 0, sourceSize.height);
22563
+ return {
22564
+ x: [
22565
+ sourceXStart,
22566
+ sourceXEnd
22567
+ ],
22568
+ y: [
22569
+ sourceYStart,
22570
+ sourceYEnd
22571
+ ]
22572
+ };
22573
+ };
22574
+ const resolveHeatmapProjectedSourceRange = (snapshot, bounds)=>{
22575
+ const sourceRange = resolveHeatmapProjectedContinuousSourceRange(snapshot, bounds);
22576
+ if (!sourceRange) return null;
22577
+ return quantizeHeatmapContinuousSourceRange(snapshot, sourceRange);
22578
+ };
22579
+ const quantizeHeatmapContinuousSourceRange = (snapshot, sourceRange)=>{
22580
+ const { sourceSize } = snapshot;
22581
+ if (sourceSize.width <= 0 || sourceSize.height <= 0) return null;
22582
+ const [sourceXStart, sourceXEnd] = sourceRange.x;
22583
+ const [sourceYStart, sourceYEnd] = sourceRange.y;
22565
22584
  const startCol = viewport_clamp(Math.floor(sourceXStart + SOURCE_EDGE_EPSILON), 0, sourceSize.width - 1);
22566
22585
  const endCol = viewport_clamp(Math.max(startCol, Math.ceil(sourceXEnd - SOURCE_EDGE_EPSILON) - 1), startCol, sourceSize.width - 1);
22567
22586
  const startIndex = viewport_clamp(Math.floor(sourceYStart + SOURCE_EDGE_EPSILON), 0, sourceSize.height - 1);
@@ -22630,7 +22649,7 @@ const resolveHeatmapOverviewViewportRect = (snapshot)=>{
22630
22649
  height: (range.y[1] - range.y[0]) / sourceSize.height
22631
22650
  };
22632
22651
  };
22633
- const resolveHeatmapOverviewSourceRangeRect = (snapshot, range)=>{
22652
+ const resolveHeatmapOverviewContinuousSourceRangeRect = (snapshot, range)=>{
22634
22653
  const { sourceSize } = snapshot;
22635
22654
  if (sourceSize.width <= 0 || sourceSize.height <= 0) return {
22636
22655
  height: 0,
@@ -22638,22 +22657,22 @@ const resolveHeatmapOverviewSourceRangeRect = (snapshot, range)=>{
22638
22657
  top: 0,
22639
22658
  width: 0
22640
22659
  };
22641
- const startCol = viewport_clamp(Math.min(range.startCol, range.endCol), 0, sourceSize.width - 1);
22642
- const endCol = viewport_clamp(Math.max(range.startCol, range.endCol), startCol, sourceSize.width - 1);
22643
- const startIndex = viewport_clamp(Math.min(range.startIndex, range.endIndex), 0, sourceSize.height - 1);
22644
- const endIndex = viewport_clamp(Math.max(range.startIndex, range.endIndex), startIndex, sourceSize.height - 1);
22660
+ const xStart = viewport_clamp(Math.min(range.x[0], range.x[1]), 0, sourceSize.width);
22661
+ const xEnd = viewport_clamp(Math.max(range.x[0], range.x[1]), xStart, sourceSize.width);
22662
+ const yStart = viewport_clamp(Math.min(range.y[0], range.y[1]), 0, sourceSize.height);
22663
+ const yEnd = viewport_clamp(Math.max(range.y[0], range.y[1]), yStart, sourceSize.height);
22645
22664
  return {
22646
- left: startCol / sourceSize.width,
22647
- top: 1 - (endIndex + 1) / sourceSize.height,
22648
- width: (endCol + 1 - startCol) / sourceSize.width,
22649
- height: (endIndex + 1 - startIndex) / sourceSize.height
22665
+ left: xStart / sourceSize.width,
22666
+ top: 1 - yEnd / sourceSize.height,
22667
+ width: (xEnd - xStart) / sourceSize.width,
22668
+ height: (yEnd - yStart) / sourceSize.height
22650
22669
  };
22651
22670
  };
22652
- const isHeatmapSourceRangeVisibleInViewport = (snapshot, range)=>Math.min(range.startCol, range.endCol) < snapshot.range.x[1] && Math.max(range.startCol, range.endCol) >= snapshot.range.x[0] && Math.min(range.startIndex, range.endIndex) < snapshot.range.y[1] && Math.max(range.startIndex, range.endIndex) >= snapshot.range.y[0];
22653
- const isHeatmapOverviewSourcePointInRange = (range, point, tolerance = {
22671
+ const isHeatmapContinuousSourceRangeVisibleInViewport = (snapshot, range)=>Math.min(range.x[0], range.x[1]) < snapshot.range.x[1] && Math.max(range.x[0], range.x[1]) > snapshot.range.x[0] && Math.min(range.y[0], range.y[1]) < snapshot.range.y[1] && Math.max(range.y[0], range.y[1]) > snapshot.range.y[0];
22672
+ const isHeatmapOverviewSourcePointInContinuousRange = (range, point, tolerance = {
22654
22673
  x: 0,
22655
22674
  y: 0
22656
- })=>point.x >= Math.min(range.startCol, range.endCol) - tolerance.x && point.x <= Math.max(range.startCol, range.endCol) + 1 + tolerance.x && point.y >= Math.min(range.startIndex, range.endIndex) - tolerance.y && point.y <= Math.max(range.startIndex, range.endIndex) + 1 + tolerance.y;
22675
+ })=>point.x >= Math.min(range.x[0], range.x[1]) - tolerance.x && point.x <= Math.max(range.x[0], range.x[1]) + tolerance.x && point.y >= Math.min(range.y[0], range.y[1]) - tolerance.y && point.y <= Math.max(range.y[0], range.y[1]) + tolerance.y;
22657
22676
  const isHeatmapViewportFullRange = (snapshot)=>{
22658
22677
  const { range, sourceSize } = snapshot;
22659
22678
  if (sourceSize.width <= 0 || sourceSize.height <= 0) return true;
@@ -22666,9 +22685,9 @@ const calculateHeatmapOverviewCenteredRange = (snapshot, point)=>{
22666
22685
  y: centerRange(snapshot.range.y, snapshot.sourceSize.height, sourcePoint.y)
22667
22686
  };
22668
22687
  };
22669
- const calculateHeatmapOverviewSourceRangeCenteredRange = (snapshot, range)=>({
22670
- x: centerRange(snapshot.range.x, snapshot.sourceSize.width, (Math.min(range.startCol, range.endCol) + Math.max(range.startCol, range.endCol) + 1) / 2),
22671
- y: centerRange(snapshot.range.y, snapshot.sourceSize.height, (Math.min(range.startIndex, range.endIndex) + Math.max(range.startIndex, range.endIndex) + 1) / 2)
22688
+ const calculateHeatmapOverviewContinuousSourceRangeCenteredRange = (snapshot, range)=>({
22689
+ x: centerRange(snapshot.range.x, snapshot.sourceSize.width, (Math.min(range.x[0], range.x[1]) + Math.max(range.x[0], range.x[1])) / 2),
22690
+ y: centerRange(snapshot.range.y, snapshot.sourceSize.height, (Math.min(range.y[0], range.y[1]) + Math.max(range.y[0], range.y[1])) / 2)
22672
22691
  });
22673
22692
  const calculateHeatmapOverviewDragRange = (snapshot, point, grabOffset)=>{
22674
22693
  const sourcePoint = resolveHeatmapOverviewSourcePoint(snapshot, point);
@@ -24388,16 +24407,16 @@ function calculateAreaInfo({ endLeft, startLeft, startTop, endTop, frequencyForm
24388
24407
  return result;
24389
24408
  }
24390
24409
  const heatmapCaptureUpdate = (globalID, func)=>subscription_createSubscriptionManager(`heatmapCaptureUpdate-${globalID}`, '0', func, globalID);
24391
- const getHeatmapCaptureSourceRangeKey = (globalID)=>`heatmapCaptureSourceRange-${globalID}`;
24392
- const getHeatmapCaptureSourceRangeSubscriptionKey = (globalID)=>`${getHeatmapCaptureSourceRangeKey(globalID)}-update`;
24393
- const isSourceRangeEqual = (left, right)=>left === right || null !== left && null !== right && left.startCol === right.startCol && left.endCol === right.endCol && left.startIndex === right.startIndex && left.endIndex === right.endIndex;
24394
- const getHeatmapCaptureSourceRange = (globalID)=>subscription_openData(getHeatmapCaptureSourceRangeKey(globalID), void 0, null, globalID);
24395
- const setHeatmapCaptureSourceRange = (globalID, range)=>{
24396
- if (!globalID || isSourceRangeEqual(getHeatmapCaptureSourceRange(globalID), range)) return;
24397
- subscription_openData(getHeatmapCaptureSourceRangeKey(globalID), range, null, globalID);
24398
- subscription_createSubscriptionManager(getHeatmapCaptureSourceRangeSubscriptionKey(globalID))(range);
24399
- };
24400
- const subscribeHeatmapCaptureSourceRange = (globalID, func)=>subscription_createSubscriptionManager(getHeatmapCaptureSourceRangeSubscriptionKey(globalID), 'HeatmapOverview', func, globalID);
24410
+ const getHeatmapCaptureSelectionKey = (globalID)=>`heatmapCaptureSelection-${globalID}`;
24411
+ const getHeatmapCaptureSelectionSubscriptionKey = (globalID)=>`${getHeatmapCaptureSelectionKey(globalID)}-update`;
24412
+ const isSelectionEqual = (left, right)=>left === right || null !== left && null !== right && Math.abs(left.x[0] - right.x[0]) <= 1e-7 && Math.abs(left.x[1] - right.x[1]) <= 1e-7 && Math.abs(left.y[0] - right.y[0]) <= 1e-7 && Math.abs(left.y[1] - right.y[1]) <= 1e-7;
24413
+ const getHeatmapCaptureSelection = (globalID)=>subscription_openData(getHeatmapCaptureSelectionKey(globalID), void 0, null, globalID);
24414
+ const setHeatmapCaptureSelection = (globalID, selection)=>{
24415
+ if (!globalID || isSelectionEqual(getHeatmapCaptureSelection(globalID), selection)) return;
24416
+ subscription_openData(getHeatmapCaptureSelectionKey(globalID), selection, null, globalID);
24417
+ subscription_createSubscriptionManager(getHeatmapCaptureSelectionSubscriptionKey(globalID))(selection);
24418
+ };
24419
+ const subscribeHeatmapCaptureSelection = (globalID, func)=>subscription_createSubscriptionManager(getHeatmapCaptureSelectionSubscriptionKey(globalID), 'HeatmapOverview', func, globalID);
24401
24420
  const Area_COMPONENT_KEY = constants_ToolType.HeatmapCapture;
24402
24421
  const Area_Area = (props)=>{
24403
24422
  const { state: { heatmapCapture, axisX: { frequencyFormat, unit }, system, globalID } } = useStore_useStore();
@@ -25503,7 +25522,7 @@ const DragSelectBox = ({ enabled, defaultRect, minSize = 0, minWidth, minHeight,
25503
25522
  return nextRect;
25504
25523
  };
25505
25524
  const handleBoxMouseDown = (event, type, startRect)=>{
25506
- if (onDragStart?.() === false) return;
25525
+ if (onDragStart?.(type) === false) return;
25507
25526
  event.preventDefault();
25508
25527
  event.stopPropagation();
25509
25528
  handleMouseDown(event, type, startRect);
@@ -25698,12 +25717,191 @@ const DragSelectBox = ({ enabled, defaultRect, minSize = 0, minWidth, minHeight,
25698
25717
  });
25699
25718
  };
25700
25719
  const components_DragSelectBox = DragSelectBox;
25701
- const sliderInfo_PRECISION = 1e4;
25702
- const calculateHeatmapSliderInfo = ({ formatBandwidth, frequencyFormat, heatmapData, rect, sourceRangeOverride, waterfallSnapshot })=>{
25720
+ const sliderInfo_PRECISION = 1e6;
25721
+ const sliderInfo_clamp = (value1, min, max)=>Math.min(max, Math.max(min, value1));
25722
+ const resolveFrequencyExtent = (frequencyFormat)=>{
25723
+ if (!frequencyFormat) return null;
25724
+ const start = Number(frequencyFormat(0));
25725
+ const end = Number(frequencyFormat(100));
25726
+ if (!Number.isFinite(start) || !Number.isFinite(end) || start === end) return null;
25727
+ return [
25728
+ start,
25729
+ end
25730
+ ];
25731
+ };
25732
+ const resolveContinuousFrequencyFromSegments = (ratio, segments, edge)=>{
25733
+ if (!segments?.length) return null;
25734
+ const percent = 100 * sliderInfo_clamp(ratio, 0, 1);
25735
+ const lastIndex = segments.length - 1;
25736
+ for(let index = 0; index < segments.length; index += 1){
25737
+ const segment = segments[index];
25738
+ const progressStart = Math.min(segment.progress[0], segment.progress[1]);
25739
+ const progressEnd = Math.max(segment.progress[0], segment.progress[1]);
25740
+ const contains = 'start' === edge ? percent >= progressStart && (percent < progressEnd || index === lastIndex && percent <= progressEnd) : (percent > progressStart || 0 === index) && percent <= progressEnd;
25741
+ if (!contains) continue;
25742
+ const progressSpan = progressEnd - progressStart;
25743
+ const localRatio = progressSpan ? (percent - progressStart) / progressSpan : 0;
25744
+ return segment.start + (segment.stop - segment.start) * localRatio;
25745
+ }
25746
+ return null;
25747
+ };
25748
+ const resolveFrequencyFromRatio = (ratio, segments, frequencyFormat, edge)=>{
25749
+ const value1 = frequencyFormat?.(100 * sliderInfo_clamp(ratio, 0, 1));
25750
+ const frequency = void 0 === value1 ? Number.NaN : Number(value1);
25751
+ if (Number.isFinite(frequency)) return frequency;
25752
+ return resolveContinuousFrequencyFromSegments(ratio, segments, edge);
25753
+ };
25754
+ const resolveRatioFromFrequency = (frequency, segments, frequencyFormat)=>{
25755
+ if (segments?.length) {
25756
+ let nearest = null;
25757
+ for (const segment of segments){
25758
+ const frequencyStart = segment.start;
25759
+ const frequencyEnd = segment.stop;
25760
+ const frequencyMin = Math.min(frequencyStart, frequencyEnd);
25761
+ const frequencyMax = Math.max(frequencyStart, frequencyEnd);
25762
+ const progressStart = segment.progress[0] / 100;
25763
+ const progressEnd = segment.progress[1] / 100;
25764
+ if (frequency >= frequencyMin && frequency <= frequencyMax) {
25765
+ const frequencySpan = frequencyEnd - frequencyStart;
25766
+ const localRatio = frequencySpan ? (frequency - frequencyStart) / frequencySpan : 0;
25767
+ return sliderInfo_clamp(progressStart + (progressEnd - progressStart) * localRatio, 0, 1);
25768
+ }
25769
+ const candidates = [
25770
+ {
25771
+ distance: Math.abs(frequency - frequencyStart),
25772
+ ratio: progressStart
25773
+ },
25774
+ {
25775
+ distance: Math.abs(frequency - frequencyEnd),
25776
+ ratio: progressEnd
25777
+ }
25778
+ ];
25779
+ for (const candidate of candidates)if (!nearest || candidate.distance < nearest.distance) nearest = candidate;
25780
+ }
25781
+ if (nearest) return sliderInfo_clamp(nearest.ratio, 0, 1);
25782
+ }
25783
+ const extent = resolveFrequencyExtent(frequencyFormat);
25784
+ if (!extent) return null;
25785
+ return sliderInfo_clamp((frequency - extent[0]) / (extent[1] - extent[0]), 0, 1);
25786
+ };
25787
+ const resolveHeatmapSliderFrequencyRatioRange = ({ frequencyFormat, frequencyRange, segments })=>{
25788
+ const start = resolveRatioFromFrequency(frequencyRange[0], segments, frequencyFormat);
25789
+ const end = resolveRatioFromFrequency(frequencyRange[1], segments, frequencyFormat);
25790
+ if (null === start || null === end) return null;
25791
+ return [
25792
+ Math.min(start, end),
25793
+ Math.max(start, end)
25794
+ ];
25795
+ };
25796
+ const resolveFrequencyRangeFromRatios = (ratios, segments, frequencyFormat)=>{
25797
+ const start = resolveFrequencyFromRatio(ratios[0], segments, frequencyFormat, 'start');
25798
+ const end = resolveFrequencyFromRatio(ratios[1], segments, frequencyFormat, 'end');
25799
+ if (null === start || null === end) return null;
25800
+ return [
25801
+ Math.min(start, end),
25802
+ Math.max(start, end)
25803
+ ];
25804
+ };
25805
+ const resolveHeatmapSliderSelectionFromRect = ({ frequencyFormat, rect, segments, waterfallSnapshot })=>{
25806
+ const continuousRange = resolveHeatmapProjectedContinuousSourceRange(waterfallSnapshot, {
25807
+ startLeft: rect.leftPosition,
25808
+ endLeft: rect.leftPosition + rect.width,
25809
+ startTop: rect.position + rect.height,
25810
+ endTop: rect.position
25811
+ });
25812
+ if (!continuousRange || waterfallSnapshot.sourceSize.width <= 0) return null;
25813
+ const x = [
25814
+ continuousRange.x[0] / waterfallSnapshot.sourceSize.width,
25815
+ continuousRange.x[1] / waterfallSnapshot.sourceSize.width
25816
+ ];
25817
+ return {
25818
+ frequency: resolveFrequencyRangeFromRatios(x, segments, frequencyFormat),
25819
+ x,
25820
+ y: continuousRange.y
25821
+ };
25822
+ };
25823
+ const resolveHeatmapSliderSelectionFromInteraction = ({ dragType, frequencyFormat, rect, segments, startSelection, waterfallSnapshot })=>{
25824
+ const current = resolveHeatmapSliderSelectionFromRect({
25825
+ frequencyFormat,
25826
+ rect,
25827
+ segments,
25828
+ waterfallSnapshot
25829
+ });
25830
+ if (!current || !startSelection) return current;
25831
+ switch(dragType){
25832
+ case 'left':
25833
+ return {
25834
+ frequency: current.frequency && startSelection.frequency ? [
25835
+ current.frequency[0],
25836
+ startSelection.frequency[1]
25837
+ ] : current.frequency,
25838
+ x: [
25839
+ current.x[0],
25840
+ startSelection.x[1]
25841
+ ],
25842
+ y: startSelection.y
25843
+ };
25844
+ case 'right':
25845
+ return {
25846
+ frequency: current.frequency && startSelection.frequency ? [
25847
+ startSelection.frequency[0],
25848
+ current.frequency[1]
25849
+ ] : current.frequency,
25850
+ x: [
25851
+ startSelection.x[0],
25852
+ current.x[1]
25853
+ ],
25854
+ y: startSelection.y
25855
+ };
25856
+ case 'top':
25857
+ return {
25858
+ frequency: startSelection.frequency,
25859
+ x: startSelection.x,
25860
+ y: [
25861
+ startSelection.y[0],
25862
+ current.y[1]
25863
+ ]
25864
+ };
25865
+ case 'bottom':
25866
+ return {
25867
+ frequency: startSelection.frequency,
25868
+ x: startSelection.x,
25869
+ y: [
25870
+ current.y[0],
25871
+ startSelection.y[1]
25872
+ ]
25873
+ };
25874
+ case 'middle':
25875
+ return current;
25876
+ }
25877
+ };
25878
+ const resolveHeatmapSliderSelectionSourceRange = ({ selection, waterfallSnapshot })=>{
25879
+ const { sourceSize } = waterfallSnapshot;
25880
+ if (sourceSize.width <= 0 || sourceSize.height <= 0) return null;
25881
+ const xStart = sliderInfo_clamp(Math.min(selection.x[0], selection.x[1]), 0, 1);
25882
+ const xEnd = sliderInfo_clamp(Math.max(selection.x[0], selection.x[1]), xStart, 1);
25883
+ const yStart = sliderInfo_clamp(Math.min(selection.y[0], selection.y[1]), 0, sourceSize.height);
25884
+ const yEnd = sliderInfo_clamp(Math.max(selection.y[0], selection.y[1]), yStart, sourceSize.height);
25885
+ return {
25886
+ x: [
25887
+ xStart * sourceSize.width,
25888
+ xEnd * sourceSize.width
25889
+ ],
25890
+ y: [
25891
+ yStart,
25892
+ yEnd
25893
+ ]
25894
+ };
25895
+ };
25896
+ const calculateHeatmapSliderInfo = ({ formatBandwidth, frequencyFormat, heatmapData, rect, segments, selectionOverride, sourceRangeOverride, waterfallSnapshot })=>{
25703
25897
  const len = heatmapData.length;
25704
25898
  if (len <= 0) return null;
25705
25899
  const { position, height, leftPosition: left, width } = rect;
25706
- const sourceRange = sourceRangeOverride ?? (waterfallSnapshot ? resolveHeatmapProjectedSourceRange(waterfallSnapshot, {
25900
+ const continuousSourceRangeOverride = waterfallSnapshot && selectionOverride ? resolveHeatmapSliderSelectionSourceRange({
25901
+ selection: selectionOverride,
25902
+ waterfallSnapshot
25903
+ }) : null;
25904
+ const sourceRange = sourceRangeOverride ?? (waterfallSnapshot && continuousSourceRangeOverride ? quantizeHeatmapContinuousSourceRange(waterfallSnapshot, continuousSourceRangeOverride) : null) ?? (waterfallSnapshot ? resolveHeatmapProjectedSourceRange(waterfallSnapshot, {
25707
25905
  startLeft: left,
25708
25906
  endLeft: left + width,
25709
25907
  startTop: position + height,
@@ -25721,12 +25919,16 @@ const calculateHeatmapSliderInfo = ({ formatBandwidth, frequencyFormat, heatmapD
25721
25919
  if (!columnRange) return null;
25722
25920
  const { startCol, endCol } = columnRange;
25723
25921
  const sourceWidth = waterfallSnapshot?.sourceSize.width ?? 0;
25724
- const startLeft = sourceRangeOverride && sourceWidth > 0 ? 100 * startCol / sourceWidth : waterfallSnapshot ? resolveHeatmapSourceLeft(left, waterfallSnapshot) : left;
25725
- const endLeft = sourceRangeOverride && sourceWidth > 0 ? (endCol + 1) * 100 / sourceWidth : waterfallSnapshot ? resolveHeatmapSourceLeft(left + width, waterfallSnapshot) : left + width;
25726
- const startFrequencyValue = frequencyFormat?.(startLeft);
25727
- const endFrequencyValue = frequencyFormat?.(endLeft);
25728
- const startFrequency = startFrequencyValue ? Math.round(Number(startFrequencyValue) * sliderInfo_PRECISION) / sliderInfo_PRECISION : 0;
25729
- const endFrequency = endFrequencyValue ? Math.round(Number(endFrequencyValue) * sliderInfo_PRECISION) / sliderInfo_PRECISION : 0;
25922
+ const startLeft = selectionOverride ? 100 * selectionOverride.x[0] : sourceRangeOverride && sourceWidth > 0 ? 100 * startCol / sourceWidth : waterfallSnapshot ? resolveHeatmapSourceLeft(left, waterfallSnapshot) : left;
25923
+ const endLeft = selectionOverride ? 100 * selectionOverride.x[1] : sourceRangeOverride && sourceWidth > 0 ? (endCol + 1) * 100 / sourceWidth : waterfallSnapshot ? resolveHeatmapSourceLeft(left + width, waterfallSnapshot) : left + width;
25924
+ const fallbackFrequencyRange = resolveFrequencyRangeFromRatios([
25925
+ startLeft / 100,
25926
+ endLeft / 100
25927
+ ], segments, frequencyFormat);
25928
+ const startFrequencyValue = selectionOverride?.frequency?.[0] ?? fallbackFrequencyRange?.[0];
25929
+ const endFrequencyValue = selectionOverride?.frequency?.[1] ?? fallbackFrequencyRange?.[1];
25930
+ const startFrequency = Number.isFinite(startFrequencyValue) ? Math.round(Number(startFrequencyValue) * sliderInfo_PRECISION) / sliderInfo_PRECISION : 0;
25931
+ const endFrequency = Number.isFinite(endFrequencyValue) ? Math.round(Number(endFrequencyValue) * sliderInfo_PRECISION) / sliderInfo_PRECISION : 0;
25730
25932
  const bandwidth = Math.abs(endFrequency - startFrequency);
25731
25933
  return {
25732
25934
  startIndex,
@@ -25759,7 +25961,7 @@ const isRectEqual = (left, right)=>Math.abs(left.position - right.position) <= 0
25759
25961
  const isBoundsEqual = (left, right)=>Math.abs(left.top - right.top) <= 0.01 && Math.abs(left.bottom - right.bottom) <= 0.01 && Math.abs(left.left - right.left) <= 0.01 && Math.abs(left.right - right.right) <= 0.01;
25760
25962
  const isSliderInfoEqual = (left, right)=>left.startIndex === right.startIndex && left.endIndex === right.endIndex && left.startTimestamp === right.startTimestamp && left.endTimestamp === right.endTimestamp && left.duration === right.duration && left.startCol === right.startCol && left.endCol === right.endCol && left.startFrequency === right.startFrequency && left.startFrequencyFormat === right.startFrequencyFormat && left.endFrequency === right.endFrequency && left.endFrequencyFormat === right.endFrequencyFormat && left.bandwidth === right.bandwidth && left.bandwidthFormat === right.bandwidthFormat;
25761
25963
  const Slider = ({ id })=>{
25762
- const { state: { globalID, heatmapCapture: { type, show, display, interval, onChange }, axisX: { frequencyFormat, unit } } } = useStore_useStore();
25964
+ const { state: { globalID, heatmapCapture: { type, show, display, interval, onChange }, axisX: { frequencyFormat, unit }, segments } } = useStore_useStore();
25763
25965
  const canSlider = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>type === heatmap_HeatmapCaptureType.Slider && show && display, [
25764
25966
  type,
25765
25967
  show,
@@ -25771,8 +25973,14 @@ const Slider = ({ id })=>{
25771
25973
  const [currentRect, setCurrentRect] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(DEFAULT_RECT);
25772
25974
  const [interactionBounds, setInteractionBounds] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)();
25773
25975
  const currentRectRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(currentRect);
25976
+ const selectionRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
25977
+ const dragStartSelectionRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
25978
+ const dragStartRectRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
25979
+ const activeDragTypeRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
25774
25980
  const frequencyFormatRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(frequencyFormat);
25981
+ const segmentsRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(segments);
25775
25982
  frequencyFormatRef.current = frequencyFormat;
25983
+ segmentsRef.current = segments;
25776
25984
  const updateCurrentRect = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((rect)=>{
25777
25985
  currentRectRef.current = rect;
25778
25986
  setCurrentRect((current)=>isRectEqual(current, rect) ? current : rect);
@@ -25783,40 +25991,39 @@ const Slider = ({ id })=>{
25783
25991
  return isBoundsEqual(current, bounds) ? current : bounds;
25784
25992
  });
25785
25993
  }, []);
25994
+ const updateSelectionSnapshot = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((nextInfo)=>{
25995
+ const waterfallSnapshot = getHeatmapWaterfallSnapshot(globalID);
25996
+ const selection = selectionRef.current;
25997
+ const continuousRange = waterfallSnapshot && selection ? resolveHeatmapSliderSelectionSourceRange({
25998
+ selection,
25999
+ waterfallSnapshot
26000
+ }) : null;
26001
+ setHeatmapCaptureSelection(globalID, canSlider && nextInfo && continuousRange ? continuousRange : null);
26002
+ }, [
26003
+ canSlider,
26004
+ globalID
26005
+ ]);
25786
26006
  const updateInfo = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((nextInfo, notify)=>{
25787
26007
  infoRef.current = nextInfo;
25788
26008
  setInfo(nextInfo);
25789
- if (canSlider) setHeatmapCaptureSourceRange(globalID, {
25790
- endCol: nextInfo.endCol,
25791
- endIndex: nextInfo.endIndex,
25792
- startCol: nextInfo.startCol,
25793
- startIndex: nextInfo.startIndex
25794
- });
26009
+ updateSelectionSnapshot(nextInfo);
25795
26010
  if (notify) {
25796
26011
  notifiedRawDataRef.current = getHeatmapWaterfallSnapshot(globalID)?.rawData ?? null;
25797
26012
  onChange?.(nextInfo);
25798
26013
  }
25799
26014
  }, [
25800
- canSlider,
25801
26015
  globalID,
25802
- onChange
26016
+ onChange,
26017
+ updateSelectionSnapshot
25803
26018
  ]);
25804
26019
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
25805
- if (!globalID || !canSlider) {
25806
- if (globalID) setHeatmapCaptureSourceRange(globalID, null);
25807
- return;
25808
- }
25809
- const currentInfo = infoRef.current;
25810
- if (currentInfo) setHeatmapCaptureSourceRange(globalID, {
25811
- endCol: currentInfo.endCol,
25812
- endIndex: currentInfo.endIndex,
25813
- startCol: currentInfo.startCol,
25814
- startIndex: currentInfo.startIndex
25815
- });
25816
- return ()=>setHeatmapCaptureSourceRange(globalID, null);
26020
+ if (!globalID) return;
26021
+ updateSelectionSnapshot(canSlider ? infoRef.current ?? null : null);
26022
+ return ()=>setHeatmapCaptureSelection(globalID, null);
25817
26023
  }, [
25818
26024
  canSlider,
25819
- globalID
26025
+ globalID,
26026
+ updateSelectionSnapshot
25820
26027
  ]);
25821
26028
  const getHeatmapData = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)(()=>{
25822
26029
  const waterfallData = withDatabase(globalID).getAllRawData().waterfallData ?? [];
@@ -25827,20 +26034,42 @@ const Slider = ({ id })=>{
25827
26034
  }, [
25828
26035
  globalID
25829
26036
  ]);
25830
- const calculateSliderInfo = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((r, sourceRangeOverride)=>{
26037
+ const calculateSliderInfo = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((r, selectionOverride)=>{
25831
26038
  const [heatmapData] = getHeatmapData();
25832
26039
  return calculateHeatmapSliderInfo({
25833
26040
  formatBandwidth: getFrequencyToFixed,
25834
26041
  frequencyFormat: frequencyFormatRef.current,
25835
26042
  heatmapData,
25836
26043
  rect: r,
25837
- sourceRangeOverride,
26044
+ segments: segmentsRef.current,
26045
+ selectionOverride,
25838
26046
  waterfallSnapshot: getHeatmapWaterfallSnapshot(globalID)
25839
26047
  });
25840
26048
  }, [
25841
26049
  getHeatmapData,
25842
26050
  globalID
25843
26051
  ]);
26052
+ const updateSelection = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((rect, dragType)=>{
26053
+ const waterfallSnapshot = getHeatmapWaterfallSnapshot(globalID);
26054
+ if (!waterfallSnapshot) return null;
26055
+ const nextSelection = dragType ? resolveHeatmapSliderSelectionFromInteraction({
26056
+ dragType,
26057
+ frequencyFormat: frequencyFormatRef.current,
26058
+ rect,
26059
+ segments: segmentsRef.current,
26060
+ startSelection: dragStartSelectionRef.current,
26061
+ waterfallSnapshot
26062
+ }) : resolveHeatmapSliderSelectionFromRect({
26063
+ frequencyFormat: frequencyFormatRef.current,
26064
+ rect,
26065
+ segments: segmentsRef.current,
26066
+ waterfallSnapshot
26067
+ });
26068
+ if (nextSelection) selectionRef.current = nextSelection;
26069
+ return nextSelection;
26070
+ }, [
26071
+ globalID
26072
+ ]);
25844
26073
  const [updateKey, setUpdateKey] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(0);
25845
26074
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
25846
26075
  if (!id || !globalID) return;
@@ -25850,18 +26079,32 @@ const Slider = ({ id })=>{
25850
26079
  globalID
25851
26080
  ]);
25852
26081
  const handleChange = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((rect)=>{
26082
+ const startRect = dragStartRectRef.current;
26083
+ if (startRect && isRectEqual(startRect, rect)) return;
25853
26084
  updateCurrentRect(rect);
25854
- const newInfo = calculateSliderInfo(rect);
26085
+ const selection = updateSelection(rect, activeDragTypeRef.current);
26086
+ const newInfo = calculateSliderInfo(rect, selection ?? void 0);
25855
26087
  if (newInfo) updateInfo(newInfo, false);
25856
26088
  }, [
25857
26089
  calculateSliderInfo,
25858
26090
  updateCurrentRect,
25859
- updateInfo
26091
+ updateInfo,
26092
+ updateSelection
25860
26093
  ]);
25861
- const claimCaptureInteraction = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)(()=>compareEventPriority(id, constants_ToolType.HeatmapCapture), [
26094
+ const claimCaptureInteraction = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((dragType)=>{
26095
+ const claimed = compareEventPriority(id, constants_ToolType.HeatmapCapture);
26096
+ if (false === claimed) return false;
26097
+ activeDragTypeRef.current = dragType;
26098
+ dragStartSelectionRef.current = selectionRef.current;
26099
+ dragStartRectRef.current = currentRectRef.current;
26100
+ return claimed;
26101
+ }, [
25862
26102
  id
25863
26103
  ]);
25864
26104
  const releaseCaptureInteraction = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)(()=>{
26105
+ activeDragTypeRef.current = null;
26106
+ dragStartSelectionRef.current = null;
26107
+ dragStartRectRef.current = null;
25865
26108
  createMouseUpEventManager(getEID(id))();
25866
26109
  resetEventLevel(id, constants_ToolType.HeatmapCapture);
25867
26110
  }, [
@@ -25869,8 +26112,8 @@ const Slider = ({ id })=>{
25869
26112
  ]);
25870
26113
  const handleDragEnd = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((rect)=>{
25871
26114
  try {
25872
- updateCurrentRect(rect);
25873
- const newInfo = calculateSliderInfo(rect);
26115
+ const selection = selectionRef.current ?? updateSelection(rect, null);
26116
+ const newInfo = calculateSliderInfo(rect, selection ?? void 0);
25874
26117
  if (newInfo) updateInfo(newInfo, true);
25875
26118
  } finally{
25876
26119
  releaseCaptureInteraction();
@@ -25878,8 +26121,8 @@ const Slider = ({ id })=>{
25878
26121
  }, [
25879
26122
  calculateSliderInfo,
25880
26123
  releaseCaptureInteraction,
25881
- updateCurrentRect,
25882
- updateInfo
26124
+ updateInfo,
26125
+ updateSelection
25883
26126
  ]);
25884
26127
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>releaseCaptureInteraction, [
25885
26128
  canSlider,
@@ -25891,34 +26134,54 @@ const Slider = ({ id })=>{
25891
26134
  const waterfallSnapshot = getHeatmapWaterfallSnapshot(globalID);
25892
26135
  const currentInfo = infoRef.current;
25893
26136
  const rowsUnchanged = startIndex === currentInfo?.startIndex && endIndex === currentInfo.endIndex;
25894
- const frequenciesUnchanged = void 0 === startFrequency || void 0 === endFrequency ? true : Math.abs(startFrequency - (currentInfo?.startFrequency ?? NaN)) < 1e-4 && Math.abs(endFrequency - (currentInfo?.endFrequency ?? NaN)) < 1e-4;
26137
+ const currentFrequencyRange = selectionRef.current?.frequency;
26138
+ const frequenciesUnchanged = void 0 === startFrequency || void 0 === endFrequency ? true : Math.abs(Math.min(startFrequency, endFrequency) - (currentFrequencyRange?.[0] ?? Number.NaN)) <= 1e-7 && Math.abs(Math.max(startFrequency, endFrequency) - (currentFrequencyRange?.[1] ?? Number.NaN)) <= 1e-7;
25895
26139
  if (rowsUnchanged && frequenciesUnchanged) return;
25896
26140
  const activeRect = currentRectRef.current;
25897
26141
  if (waterfallSnapshot) {
26142
+ const { sourceSize } = waterfallSnapshot;
26143
+ if (sourceSize.width <= 0 || sourceSize.height <= 0) return;
25898
26144
  const validRows = len > 0 && startIndex >= 0 && endIndex < len && endIndex - startIndex >= Math.ceil(MIN_SIZE * len / 100);
25899
- const targetRange = {
25900
- startIndex: validRows ? Math.min(startIndex, endIndex) : currentInfo?.startIndex ?? waterfallSnapshot.range.y[0],
25901
- endIndex: validRows ? Math.max(startIndex, endIndex) : currentInfo?.endIndex ?? waterfallSnapshot.range.y[1] - 1,
25902
- startCol: currentInfo?.startCol ?? waterfallSnapshot.range.x[0],
25903
- endCol: currentInfo?.endCol ?? waterfallSnapshot.range.x[1] - 1
26145
+ const currentSelection = selectionRef.current ?? resolveHeatmapSliderSelectionFromRect({
26146
+ frequencyFormat: frequencyFormatRef.current,
26147
+ rect: activeRect,
26148
+ segments: segmentsRef.current,
26149
+ waterfallSnapshot
26150
+ });
26151
+ const externalFrequencyRange = void 0 !== startFrequency && void 0 !== endFrequency ? [
26152
+ Math.min(startFrequency, endFrequency),
26153
+ Math.max(startFrequency, endFrequency)
26154
+ ] : null;
26155
+ const externalFrequencyRatios = externalFrequencyRange ? resolveHeatmapSliderFrequencyRatioRange({
26156
+ frequencyFormat: frequencyFormatRef.current,
26157
+ frequencyRange: externalFrequencyRange,
26158
+ segments: segmentsRef.current
26159
+ }) : null;
26160
+ if (externalFrequencyRange && !externalFrequencyRatios) return;
26161
+ const targetSelection = {
26162
+ frequency: externalFrequencyRange ?? currentSelection?.frequency ?? null,
26163
+ x: externalFrequencyRatios ?? currentSelection?.x ?? [
26164
+ waterfallSnapshot.range.x[0] / sourceSize.width,
26165
+ waterfallSnapshot.range.x[1] / sourceSize.width
26166
+ ],
26167
+ y: validRows ? [
26168
+ Math.min(startIndex, endIndex),
26169
+ Math.max(startIndex, endIndex) + 1
26170
+ ] : currentSelection?.y ?? [
26171
+ currentInfo?.startIndex ?? waterfallSnapshot.range.y[0],
26172
+ (currentInfo?.endIndex ?? waterfallSnapshot.range.y[1] - 1) + 1
26173
+ ]
25904
26174
  };
25905
- if (void 0 !== startFrequency && void 0 !== endFrequency && frequencyFormat) {
25906
- const minFrequency = Number(frequencyFormat(0));
25907
- const maxFrequency = Number(frequencyFormat(100));
25908
- const frequencySpan = maxFrequency - minFrequency;
25909
- if (Number.isFinite(minFrequency) && Number.isFinite(maxFrequency) && frequencySpan > 0) {
25910
- const startPercent = Math.max(0, Math.min(100, (Math.min(startFrequency, endFrequency) - minFrequency) / frequencySpan * 100));
25911
- const endPercent = Math.max(startPercent, Math.min(100, (Math.max(startFrequency, endFrequency) - minFrequency) / frequencySpan * 100));
25912
- const sourceWidth = waterfallSnapshot.sourceSize.width;
25913
- targetRange.startCol = Math.max(0, Math.min(sourceWidth - 1, Math.floor(startPercent * sourceWidth / 100)));
25914
- targetRange.endCol = Math.max(targetRange.startCol, Math.min(sourceWidth - 1, Math.ceil(endPercent * sourceWidth / 100) - 1));
25915
- }
25916
- }
25917
- if (currentInfo?.startIndex === targetRange.startIndex && currentInfo.endIndex === targetRange.endIndex && currentInfo.startCol === targetRange.startCol && currentInfo.endCol === targetRange.endCol) return;
25918
- const nextRect = resolveHeatmapSourceRangeScreenRect(waterfallSnapshot, targetRange);
26175
+ const continuousRange = resolveHeatmapSliderSelectionSourceRange({
26176
+ selection: targetSelection,
26177
+ waterfallSnapshot
26178
+ });
26179
+ if (!continuousRange) return;
26180
+ const nextRect = resolveHeatmapContinuousSourceRangeScreenRect(waterfallSnapshot, continuousRange);
25919
26181
  if (!nextRect) return;
26182
+ selectionRef.current = targetSelection;
25920
26183
  updateCurrentRect(nextRect);
25921
- const newInfo = calculateSliderInfo(nextRect, targetRange);
26184
+ const newInfo = calculateSliderInfo(nextRect, targetSelection);
25922
26185
  if (newInfo) updateInfo(newInfo, true);
25923
26186
  return;
25924
26187
  }
@@ -25995,21 +26258,46 @@ const Slider = ({ id })=>{
25995
26258
  top: sourceRect.position
25996
26259
  } : void 0);
25997
26260
  } else updateInteractionBounds(void 0);
25998
- if (!waterfallSnapshot || !sourceInfo) {
26261
+ if (!waterfallSnapshot) {
25999
26262
  const newInfo = calculateSliderInfo(currentRectRef.current);
26000
26263
  if (newInfo) updateInfo(newInfo, true);
26001
26264
  return;
26002
26265
  }
26003
- const nextRect = resolveHeatmapSourceRangeScreenRect(waterfallSnapshot, sourceInfo);
26266
+ let selection = selectionRef.current ?? resolveHeatmapSliderSelectionFromRect({
26267
+ frequencyFormat: frequencyFormatRef.current,
26268
+ rect: currentRectRef.current,
26269
+ segments: segmentsRef.current,
26270
+ waterfallSnapshot
26271
+ });
26272
+ if (!selection) return;
26273
+ if (selection.frequency) {
26274
+ const x = resolveHeatmapSliderFrequencyRatioRange({
26275
+ frequencyFormat: frequencyFormatRef.current,
26276
+ frequencyRange: selection.frequency,
26277
+ segments: segmentsRef.current
26278
+ });
26279
+ if (x) selection = {
26280
+ ...selection,
26281
+ x
26282
+ };
26283
+ }
26284
+ selectionRef.current = selection;
26285
+ const continuousSourceRange = resolveHeatmapSliderSelectionSourceRange({
26286
+ selection,
26287
+ waterfallSnapshot
26288
+ });
26289
+ if (!continuousSourceRange) return;
26290
+ const nextRect = resolveHeatmapContinuousSourceRangeScreenRect(waterfallSnapshot, continuousSourceRange);
26004
26291
  if (nextRect) updateCurrentRect(nextRect);
26005
- const refreshedInfo = calculateSliderInfo(nextRect ?? currentRectRef.current, sourceInfo);
26292
+ const refreshedInfo = calculateSliderInfo(nextRect ?? currentRectRef.current, selection);
26006
26293
  if (!refreshedInfo) return;
26007
- const infoChanged = !isSliderInfoEqual(refreshedInfo, sourceInfo);
26294
+ const infoChanged = !sourceInfo || !isSliderInfoEqual(refreshedInfo, sourceInfo);
26008
26295
  const rawDataChanged = notifiedRawDataRef.current !== waterfallSnapshot.rawData;
26009
26296
  if (infoChanged || rawDataChanged) updateInfo(refreshedInfo, true);
26010
26297
  }, [
26011
26298
  updateKey,
26012
26299
  frequencyFormat,
26300
+ segments,
26013
26301
  canSlider,
26014
26302
  globalID,
26015
26303
  calculateSliderInfo,
@@ -26475,7 +26763,7 @@ const HeatmapOverview = /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react__.forw
26475
26763
  context.restore();
26476
26764
  const selectionRange = selectionRangeRef.current;
26477
26765
  if (!selectionRange) return;
26478
- const selectionRect = resolveHeatmapOverviewSourceRangeRect(snapshot, selectionRange);
26766
+ const selectionRect = resolveHeatmapOverviewContinuousSourceRangeRect(snapshot, selectionRange);
26479
26767
  const selectionLeft = selectionRect.left * canvas.width;
26480
26768
  const selectionTop = selectionRect.top * canvas.height;
26481
26769
  const selectionWidth = Math.max(selectionRect.width * canvas.width, 2 * lineWidth);
@@ -26584,12 +26872,12 @@ const HeatmapOverview = /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react__.forw
26584
26872
  updateOverlayColors
26585
26873
  ]);
26586
26874
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
26587
- const updateSelection = (range)=>{
26588
- selectionRangeRef.current = range;
26875
+ const updateSelection = (selection)=>{
26876
+ selectionRangeRef.current = selection;
26589
26877
  scheduleDraw(false);
26590
26878
  };
26591
- const unsubscribe = subscribeHeatmapCaptureSourceRange(globalID, updateSelection);
26592
- updateSelection(getHeatmapCaptureSourceRange(globalID));
26879
+ const unsubscribe = subscribeHeatmapCaptureSelection(globalID, updateSelection);
26880
+ updateSelection(getHeatmapCaptureSelection(globalID));
26593
26881
  return unsubscribe;
26594
26882
  }, [
26595
26883
  globalID,
@@ -26656,13 +26944,13 @@ const HeatmapOverview = /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react__.forw
26656
26944
  const sourcePoint = resolveHeatmapOverviewSourcePoint(snapshot, point);
26657
26945
  const pointInViewport = isHeatmapOverviewSourcePointInViewport(snapshot, sourcePoint);
26658
26946
  const selectionRange = selectionRangeRef.current;
26659
- if (!pointInViewport && selectionRange && !isHeatmapSourceRangeVisibleInViewport(snapshot, selectionRange)) {
26947
+ if (!pointInViewport && selectionRange && !isHeatmapContinuousSourceRangeVisibleInViewport(snapshot, selectionRange)) {
26660
26948
  const bounds = event.currentTarget.getBoundingClientRect();
26661
26949
  const tolerance = {
26662
26950
  x: bounds.width ? 6 * snapshot.sourceSize.width / bounds.width : 0,
26663
26951
  y: bounds.height ? 6 * snapshot.sourceSize.height / bounds.height : 0
26664
26952
  };
26665
- if (isHeatmapOverviewSourcePointInRange(selectionRange, sourcePoint, tolerance)) return void scheduleRange(calculateHeatmapOverviewSourceRangeCenteredRange(snapshot, selectionRange));
26953
+ if (isHeatmapOverviewSourcePointInContinuousRange(selectionRange, sourcePoint, tolerance)) return void scheduleRange(calculateHeatmapOverviewContinuousSourceRangeCenteredRange(snapshot, selectionRange));
26666
26954
  }
26667
26955
  if (!pointInViewport) return void scheduleRange(calculateHeatmapOverviewCenteredRange(snapshot, point));
26668
26956
  dragRef.current = {
@@ -33135,38 +33423,22 @@ const SignalSegments = /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react__.memo)
33135
33423
  style: style,
33136
33424
  children: signalPositions.map(({ item, key, position, color, tooltip })=>{
33137
33425
  const isActive = activeSignalKey === key;
33138
- const positionStyle = {
33426
+ const itemStyle = {
33139
33427
  left: position.left,
33140
33428
  width: position.width,
33141
- minWidth: '1px'
33429
+ minWidth: '1px',
33430
+ backgroundColor: color,
33431
+ height: isActive ? '100%' : '8px',
33432
+ opacity: isActive ? 0.5 : 1
33142
33433
  };
33143
- return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)(__WEBPACK_EXTERNAL_MODULE_react__.Fragment, {
33144
- children: [
33145
- isActive ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
33146
- "aria-hidden": "true",
33147
- className: "pointer-events-none absolute inset-y-0 z-0 box-border",
33148
- "data-testid": "signal-annotation-highlight",
33149
- style: {
33150
- ...positionStyle,
33151
- backgroundColor: `color-mix(in srgb, ${color} 14%, transparent)`,
33152
- borderLeft: `1px solid ${color}`,
33153
- borderRight: `1px solid ${color}`
33154
- }
33155
- }) : null,
33156
- /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
33157
- className: "pointer-events-none absolute top-0 flex h-2 min-w-px items-center justify-center overflow-hidden text-ellipsis whitespace-nowrap text-center text-xs text-foreground",
33158
- "data-signal-active": isActive ? 'true' : 'false',
33159
- "data-signal-key": key,
33160
- "data-signal-source": resolveSignalSource(item),
33161
- "data-testid": "signal-annotation",
33162
- style: {
33163
- ...positionStyle,
33164
- backgroundColor: color,
33165
- zIndex: isActive ? 2 : 1
33166
- },
33167
- title: tooltip
33168
- })
33169
- ]
33434
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
33435
+ className: "pointer-events-none absolute top-0 flex min-w-px items-center justify-center overflow-hidden text-ellipsis whitespace-nowrap text-center text-xs text-foreground",
33436
+ "data-signal-active": isActive ? 'true' : 'false',
33437
+ "data-signal-key": key,
33438
+ "data-signal-source": resolveSignalSource(item),
33439
+ "data-testid": "signal-annotation",
33440
+ style: itemStyle,
33441
+ title: tooltip
33170
33442
  }, `segment-${segmentIndex}-signal-${key}`);
33171
33443
  })
33172
33444
  }));
@@ -1,18 +1,53 @@
1
1
  import type { TimestampedFloat32Array, WaterfallSnapshot } from '@rfkit/spectrum-analyzer';
2
- import type { AxisXProps, HeatmapSlider2DData } from '../../../types';
3
- import { type HeatmapSourceRange } from '../viewport.ts';
2
+ import type { AxisXProps, HeatmapSlider2DData, SegmentsType } from '../../../types';
3
+ import { type HeatmapContinuousSourceRange, type HeatmapSourceRange } from '../viewport.ts';
4
+ export interface HeatmapSliderSelection {
5
+ /** 业务频率。外部输入保持原值,指针交互按 axisX.frequencyFormat 解析。 */
6
+ frequency: readonly [start: number, end: number] | null;
7
+ /** 相对完整频率域的连续位置,不受瀑布矩阵列密度影响。 */
8
+ x: readonly [start: number, end: number];
9
+ /** 完整瀑布源数据中的连续行边界。 */
10
+ y: readonly [start: number, end: number];
11
+ }
12
+ export type HeatmapSliderDragType = 'top' | 'bottom' | 'left' | 'right' | 'middle';
13
+ interface HeatmapSliderRect {
14
+ height: number;
15
+ leftPosition: number;
16
+ position: number;
17
+ width: number;
18
+ }
4
19
  interface SliderInfoParams {
5
20
  formatBandwidth: (bandwidth: number) => string;
6
21
  frequencyFormat: AxisXProps['frequencyFormat'];
7
22
  heatmapData: readonly TimestampedFloat32Array[];
8
- rect: {
9
- height: number;
10
- leftPosition: number;
11
- position: number;
12
- width: number;
13
- };
23
+ rect: HeatmapSliderRect;
24
+ segments?: SegmentsType;
25
+ selectionOverride?: Readonly<HeatmapSliderSelection>;
14
26
  sourceRangeOverride?: HeatmapSourceRange;
15
27
  waterfallSnapshot: Readonly<WaterfallSnapshot> | null;
16
28
  }
17
- export declare const calculateHeatmapSliderInfo: ({ formatBandwidth, frequencyFormat, heatmapData, rect, sourceRangeOverride, waterfallSnapshot }: SliderInfoParams) => HeatmapSlider2DData | null;
29
+ export declare const resolveHeatmapSliderFrequencyRatioRange: ({ frequencyFormat, frequencyRange, segments }: {
30
+ frequencyFormat: AxisXProps["frequencyFormat"];
31
+ frequencyRange: readonly [start: number, end: number];
32
+ segments?: SegmentsType;
33
+ }) => readonly [start: number, end: number] | null;
34
+ export declare const resolveHeatmapSliderSelectionFromRect: ({ frequencyFormat, rect, segments, waterfallSnapshot }: {
35
+ frequencyFormat: AxisXProps["frequencyFormat"];
36
+ rect: HeatmapSliderRect;
37
+ segments?: SegmentsType;
38
+ waterfallSnapshot: Readonly<WaterfallSnapshot>;
39
+ }) => HeatmapSliderSelection | null;
40
+ export declare const resolveHeatmapSliderSelectionFromInteraction: ({ dragType, frequencyFormat, rect, segments, startSelection, waterfallSnapshot }: {
41
+ dragType: HeatmapSliderDragType;
42
+ frequencyFormat: AxisXProps["frequencyFormat"];
43
+ rect: HeatmapSliderRect;
44
+ segments?: SegmentsType;
45
+ startSelection: Readonly<HeatmapSliderSelection> | null;
46
+ waterfallSnapshot: Readonly<WaterfallSnapshot>;
47
+ }) => HeatmapSliderSelection | null;
48
+ export declare const resolveHeatmapSliderSelectionSourceRange: ({ selection, waterfallSnapshot }: {
49
+ selection: Readonly<HeatmapSliderSelection>;
50
+ waterfallSnapshot: Readonly<WaterfallSnapshot>;
51
+ }) => HeatmapContinuousSourceRange | null;
52
+ export declare const calculateHeatmapSliderInfo: ({ formatBandwidth, frequencyFormat, heatmapData, rect, segments, selectionOverride, sourceRangeOverride, waterfallSnapshot }: SliderInfoParams) => HeatmapSlider2DData | null;
18
53
  export {};
@@ -1,5 +1,5 @@
1
1
  import type { AxisXProps, HeatmapAreaData, HeatmapCaptureProps } from '../../../types';
2
- import { type HeatmapSourceRange } from '../viewport';
2
+ import { type HeatmapContinuousSourceRange } from '../viewport';
3
3
  interface Coordinates {
4
4
  startLeft: number;
5
5
  endLeft: number;
@@ -27,7 +27,7 @@ export declare const defaultAreaInfoResult: {
27
27
  };
28
28
  export default function calculateAreaInfo({ endLeft, startLeft, startTop, endTop, frequencyFormat, globalID }: AreaInfoParams): HeatmapAreaData;
29
29
  export declare const heatmapCaptureUpdate: (globalID: string, func?: (props: Partial<HeatmapCaptureProps>) => void) => (...args: any[]) => void;
30
- export declare const getHeatmapCaptureSourceRange: (globalID: string) => Readonly<HeatmapSourceRange> | null;
31
- export declare const setHeatmapCaptureSourceRange: (globalID: string, range: Readonly<HeatmapSourceRange> | null) => void;
32
- export declare const subscribeHeatmapCaptureSourceRange: (globalID: string, func: (range: Readonly<HeatmapSourceRange> | null) => void) => (...args: any[]) => void;
30
+ export declare const getHeatmapCaptureSelection: (globalID: string) => Readonly<HeatmapContinuousSourceRange> | null;
31
+ export declare const setHeatmapCaptureSelection: (globalID: string, selection: Readonly<HeatmapContinuousSourceRange> | null) => void;
32
+ export declare const subscribeHeatmapCaptureSelection: (globalID: string, func: (selection: Readonly<HeatmapContinuousSourceRange> | null) => void) => (...args: any[]) => void;
33
33
  export {};
@@ -12,6 +12,10 @@ export interface HeatmapSourceRange {
12
12
  startCol: number;
13
13
  startIndex: number;
14
14
  }
15
+ export interface HeatmapContinuousSourceRange {
16
+ x: readonly [start: number, end: number];
17
+ y: readonly [start: number, end: number];
18
+ }
15
19
  export interface HeatmapSourceRangeScreenRect {
16
20
  height: number;
17
21
  leftPosition: number;
@@ -50,12 +54,20 @@ export declare const resolveHeatmapSourceRange: (snapshot: Readonly<WaterfallSna
50
54
  }) => HeatmapSourceRange | null;
51
55
  export declare const resolveHeatmapSnapshotRangeData: (snapshot: Readonly<WaterfallSnapshot>, range: HeatmapSourceRange) => number[][];
52
56
  export declare const resolveHeatmapSourceRangeScreenRect: (snapshot: Readonly<HeatmapViewportSnapshot>, range: HeatmapSourceRange) => HeatmapSourceRangeScreenRect | null;
57
+ export declare const resolveHeatmapContinuousSourceRangeScreenRect: (snapshot: Readonly<HeatmapViewportSnapshot>, range: Readonly<HeatmapContinuousSourceRange>) => HeatmapSourceRangeScreenRect | null;
58
+ export declare const resolveHeatmapProjectedContinuousSourceRange: (snapshot: Readonly<HeatmapViewportSnapshot>, bounds: {
59
+ endLeft: number;
60
+ endTop: number;
61
+ startLeft: number;
62
+ startTop: number;
63
+ }) => HeatmapContinuousSourceRange | null;
53
64
  export declare const resolveHeatmapProjectedSourceRange: (snapshot: Readonly<HeatmapViewportSnapshot>, bounds: {
54
65
  endLeft: number;
55
66
  endTop: number;
56
67
  startLeft: number;
57
68
  startTop: number;
58
69
  }) => HeatmapSourceRange | null;
70
+ export declare const quantizeHeatmapContinuousSourceRange: (snapshot: Readonly<HeatmapViewportSnapshot>, sourceRange: Readonly<HeatmapContinuousSourceRange>) => HeatmapSourceRange | null;
59
71
  export declare const calculateHeatmapWheelRange: (snapshot: Readonly<WaterfallSnapshot>, wheel: number, coord: {
60
72
  left: number;
61
73
  top: number;
@@ -65,14 +77,18 @@ export declare const resolveHeatmapOverviewSourcePoint: (snapshot: Readonly<Heat
65
77
  top: number;
66
78
  }) => HeatmapOverviewSourcePoint;
67
79
  export declare const resolveHeatmapOverviewViewportRect: (snapshot: Readonly<HeatmapViewportSnapshot>) => HeatmapOverviewViewportRect;
80
+ export declare const resolveHeatmapOverviewContinuousSourceRangeRect: (snapshot: Readonly<HeatmapViewportSnapshot>, range: Readonly<HeatmapContinuousSourceRange>) => HeatmapOverviewViewportRect;
68
81
  export declare const resolveHeatmapOverviewSourceRangeRect: (snapshot: Readonly<HeatmapViewportSnapshot>, range: HeatmapSourceRange) => HeatmapOverviewViewportRect;
82
+ export declare const isHeatmapContinuousSourceRangeVisibleInViewport: (snapshot: Readonly<HeatmapViewportSnapshot>, range: Readonly<HeatmapContinuousSourceRange>) => boolean;
69
83
  export declare const isHeatmapSourceRangeVisibleInViewport: (snapshot: Readonly<HeatmapViewportSnapshot>, range: HeatmapSourceRange) => boolean;
84
+ export declare const isHeatmapOverviewSourcePointInContinuousRange: (range: Readonly<HeatmapContinuousSourceRange>, point: HeatmapOverviewSourcePoint, tolerance?: HeatmapOverviewSourcePoint) => boolean;
70
85
  export declare const isHeatmapOverviewSourcePointInRange: (range: HeatmapSourceRange, point: HeatmapOverviewSourcePoint, tolerance?: HeatmapOverviewSourcePoint) => boolean;
71
86
  export declare const isHeatmapViewportFullRange: (snapshot: Readonly<HeatmapViewportSnapshot>) => boolean;
72
87
  export declare const calculateHeatmapOverviewCenteredRange: (snapshot: Readonly<HeatmapViewportSnapshot>, point: {
73
88
  left: number;
74
89
  top: number;
75
90
  }) => HeatmapViewportRange;
91
+ export declare const calculateHeatmapOverviewContinuousSourceRangeCenteredRange: (snapshot: Readonly<HeatmapViewportSnapshot>, range: Readonly<HeatmapContinuousSourceRange>) => HeatmapViewportRange;
76
92
  export declare const calculateHeatmapOverviewSourceRangeCenteredRange: (snapshot: Readonly<HeatmapViewportSnapshot>, range: HeatmapSourceRange) => HeatmapViewportRange;
77
93
  export declare const calculateHeatmapOverviewDragRange: (snapshot: Readonly<HeatmapViewportSnapshot>, point: {
78
94
  left: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rfkit/charts",
3
- "version": "1.10.2",
3
+ "version": "1.10.4",
4
4
  "type": "module",
5
5
  "description": "Chart components for wireless monitoring web applications",
6
6
  "exports": {