@pipsend/charts 2.3.4 → 2.3.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.
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * Pipsend Charts v2.3.4
3
+ * Pipsend Charts v2.3.11
4
4
  * Copyright (c) 2026 Pipsend
5
5
  * Licensed under MIT License
6
6
  * Built on TradingView Lightweight Charts™ (Apache 2.0)
@@ -2486,14 +2486,8 @@
2486
2486
  return this._private__styleGetter(this._private__findBar, this._private__series._internal_options(), barIndex, precomputedBars);
2487
2487
  }
2488
2488
  /**
2489
- * Like `barStyle`, but returns null instead of throwing when the bar is
2490
- * gone.
2491
- *
2492
- * Recolouring walks items that were laid out against the data as it was. A
2493
- * bar that has since disappeared is not an error there: the view is about
2494
- * to be rebuilt from the new data anyway. Letting it throw took down
2495
- * whatever asked for the repaint — including `setData`, which left the
2496
- * series without its data and the pane empty until the page was reloaded.
2489
+ * Like `barStyle`, but null instead of throwing when the bar is gone, which
2490
+ * a recolour of items laid out against older data can hit.
2497
2491
  */
2498
2492
  _internal_barStyleOrNull(barIndex, precomputedBars) {
2499
2493
  if (precomputedBars === undefined && this._private__series._internal_bars()._internal_valueAt(barIndex) === null) {
@@ -2895,7 +2889,10 @@
2895
2889
  commonRendererData._internal_additionalPaddingTop = additionalPadding;
2896
2890
  commonRendererData._internal_additionalPaddingBottom = additionalPadding;
2897
2891
  commonRendererData._internal_coordinate = data._internal_coordinate;
2898
- commonRendererData._internal_fixedCoordinate = data._internal_fixedCoordinate;
2892
+ // Re-runs at draw time, after the axis has moved the label clear of overlaps.
2893
+ if (data._internal_fixedCoordinate !== undefined) {
2894
+ commonRendererData._internal_fixedCoordinate = data._internal_fixedCoordinate;
2895
+ }
2899
2896
  axisRendererData._internal_text = data._internal_text;
2900
2897
  axisRendererData._internal_visible = data._internal_visible;
2901
2898
  axisRendererData._internal_tickVisible = data._internal_tickVisible;
@@ -3772,7 +3769,6 @@
3772
3769
  }
3773
3770
  }
3774
3771
 
3775
- const TICK_DENSITY = 2.5;
3776
3772
  class PriceTickMarkBuilder {
3777
3773
  constructor(priceScale, base, coordinateToLogicalFunc, logicalToCoordinateFunc) {
3778
3774
  this._private__marks = [];
@@ -3833,7 +3829,7 @@
3833
3829
  return this._private__priceScale._internal_fontSize();
3834
3830
  }
3835
3831
  _private__tickMarkHeight() {
3836
- return Math.ceil(this._private__fontHeight() * TICK_DENSITY);
3832
+ return Math.max(1, Math.ceil(this._private__fontHeight() * this._private__priceScale._internal_options().tickMarkSpacing));
3837
3833
  }
3838
3834
  _private__updateMarks(firstValue, span, high, low, minCoord, maxCoord) {
3839
3835
  const marks = this._private__marks;
@@ -3998,6 +3994,9 @@
3998
3994
  this._private__invalidateInternalHeightCache();
3999
3995
  this._private__marksCache = null;
4000
3996
  }
3997
+ if (options.tickMarkSpacing !== undefined) {
3998
+ this._private__marksCache = null;
3999
+ }
4001
4000
  }
4002
4001
  _internal_isAutoScale() {
4003
4002
  return this._private__options.autoScale;
@@ -5175,6 +5174,9 @@
5175
5174
  this._private__uniformDistribution = val;
5176
5175
  this._private__cache = null;
5177
5176
  }
5177
+ _internal_invalidate() {
5178
+ this._private__cache = null;
5179
+ }
5178
5180
  _internal_setTimeScalePoints(newPoints, firstChangedPointIndex) {
5179
5181
  this._private__removeMarksSinceIndex(firstChangedPointIndex);
5180
5182
  this._private__cache = null;
@@ -5193,7 +5195,7 @@
5193
5195
  });
5194
5196
  }
5195
5197
  }
5196
- _internal_build(spacing, maxWidth, checkIndicesForData, indicesWithDataMap, indicesWithDataId) {
5198
+ _internal_build(spacing, maxWidth, checkIndicesForData, indicesWithDataMap, indicesWithDataId, allows = () => true) {
5197
5199
  const maxIndexesPerMark = Math.ceil(maxWidth / spacing);
5198
5200
  if (this._private__cache === null ||
5199
5201
  this._private__cache._internal_maxIndexesPerMark !== maxIndexesPerMark ||
@@ -5202,7 +5204,7 @@
5202
5204
  this._private__cache = {
5203
5205
  _internal_indicesWithDataId: indicesWithDataId,
5204
5206
  _internal_checkIndicesForData: checkIndicesForData,
5205
- _internal_marks: this._private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap),
5207
+ _internal_marks: this._private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap, allows),
5206
5208
  _internal_maxIndexesPerMark: maxIndexesPerMark,
5207
5209
  };
5208
5210
  }
@@ -5226,7 +5228,7 @@
5226
5228
  this._private__marksByWeight.delete(weight);
5227
5229
  }
5228
5230
  }
5229
- _private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap) {
5231
+ _private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap, allows) {
5230
5232
  let marks = [];
5231
5233
  const canBeIncluded = (mark) => !checkIndicesForData || indicesWithDataMap.has(mark.index);
5232
5234
  for (const weight of Array.from(this._private__marksByWeight.keys()).sort((a, b) => b - a)) {
@@ -5244,6 +5246,11 @@
5244
5246
  let leftIndex = -Infinity;
5245
5247
  for (let i = 0; i < currentWeightLength; i++) {
5246
5248
  const mark = currentWeight[i];
5249
+ // Skipped before the fit check: a rejected mark must not count as one
5250
+ // that did not fit, which with uniform distribution drops its whole weight.
5251
+ if (!allows(mark, maxIndexesPerMark)) {
5252
+ continue;
5253
+ }
5247
5254
  const currentIndex = mark.index;
5248
5255
  // Determine indexes with which current index will be compared
5249
5256
  // All marks to the right is moved to new array
@@ -5550,7 +5557,7 @@
5550
5557
  const visibleBars = ensureNotNull(this._internal_visibleStrictRange());
5551
5558
  const firstBar = Math.max(visibleBars._internal_left(), visibleBars._internal_left() - indexPerLabel);
5552
5559
  const lastBar = Math.max(visibleBars._internal_right(), visibleBars._internal_right() - indexPerLabel);
5553
- const items = this._private__tickMarks._internal_build(spacing, maxLabelWidth, this._private__options.ignoreWhitespaceIndices, this._private__indicesWithData, this._private__indicesWithDataUpdateId);
5560
+ const items = this._private__tickMarks._internal_build(spacing, maxLabelWidth, this._private__options.ignoreWhitespaceIndices, this._private__indicesWithData, this._private__indicesWithDataUpdateId, (mark, barsPerMark) => this._private__horzScaleBehavior.allowsTickMark?.(mark, barsPerMark) ?? true);
5554
5561
  // according to indexPerLabel value this value means "earliest index which _might be_ used as the second label on time scale"
5555
5562
  const earliestIndexOfSecondLabel = this._private__firstIndex() + indexPerLabel;
5556
5563
  // according to indexPerLabel value this value means "earliest index which _might be_ used as the second last label on time scale"
@@ -5928,6 +5935,7 @@
5928
5935
  _private__invalidateTickMarks() {
5929
5936
  this._private__resetTimeMarksCache();
5930
5937
  this._private__formattedByWeight.clear();
5938
+ this._private__tickMarks._internal_invalidate();
5931
5939
  }
5932
5940
  _private__updateDateTimeFormatter() {
5933
5941
  this._private__horzScaleBehavior.updateFormatter(this._private__localizationOptions);
@@ -7042,6 +7050,14 @@
7042
7050
  const tp = item;
7043
7051
  return this._private__dateTimeFormatter._internal_format(new Date(tp._internal_timestamp * 1000));
7044
7052
  }
7053
+ allowsTickMark(tickMark, barsPerMark) {
7054
+ const filter = this._private__options.timeScale.tickMarkFilter;
7055
+ if (filter === undefined) {
7056
+ return true;
7057
+ }
7058
+ const tickMarkType = weightToTickMarkType(tickMark.weight, this._private__options.timeScale.timeVisible, this._private__options.timeScale.secondsVisible);
7059
+ return filter(tickMark.originalTime, tickMarkType, barsPerMark);
7060
+ }
7045
7061
  formatTickmark(tickMark, localizationOptions) {
7046
7062
  const tickMarkType = weightToTickMarkType(tickMark.weight, this._private__options.timeScale.timeVisible, this._private__options.timeScale.secondsVisible);
7047
7063
  const options = this._private__options.timeScale;
@@ -11446,9 +11462,6 @@
11446
11462
  _internal__updateOptions() {
11447
11463
  const colorer = this._internal__series._internal_barColorer();
11448
11464
  this._internal__items = this._internal__items.map((item) => {
11449
- // An item whose bar is gone keeps the colour it had. Throwing here
11450
- // took down whatever asked for the repaint — `setData` among them,
11451
- // which left the series without its data and the pane empty.
11452
11465
  const style = colorer._internal_barStyleOrNull(item._internal_time);
11453
11466
  return style === null ? item : { ...item, ...style };
11454
11467
  });
@@ -11751,6 +11764,7 @@
11751
11764
  },
11752
11765
  minimumWidth: 0,
11753
11766
  ensureEdgeTickMarksVisible: false,
11767
+ tickMarkSpacing: 2.5,
11754
11768
  };
11755
11769
 
11756
11770
  const timeScaleOptionsDefaults = {
@@ -12846,8 +12860,6 @@
12846
12860
  const colorer = this._internal__series._internal_barColorer();
12847
12861
  this._internal__items = this._internal__series._internal_bars()._internal_rows().map((row) => {
12848
12862
  const value = row._internal_value[3 /* PlotRowValueIndex.Close */];
12849
- // The row travels with the point: without it the colorer looks it up
12850
- // again by index, and that lookup could fail and take the repaint down.
12851
12863
  return this._internal__createRawItem(row._internal_index, value, colorer, row);
12852
12864
  });
12853
12865
  }
@@ -12870,10 +12882,10 @@
12870
12882
  if (point) {
12871
12883
  const style = styleGetter(renderingScope, point);
12872
12884
  if (style !== prevStyle) {
12873
- context.beginPath();
12874
12885
  if (prevStyle !== null) {
12875
12886
  context.fill();
12876
12887
  }
12888
+ context.beginPath();
12877
12889
  context.fillStyle = style;
12878
12890
  prevStyle = style;
12879
12891
  }
@@ -13812,9 +13824,6 @@
13812
13824
  }
13813
13825
  _internal__fillRawPoints() {
13814
13826
  const colorer = this._internal__series._internal_barColorer();
13815
- // The row goes to the colorer as it is. Asking for the style by index
13816
- // alone made it look the row up again, and that second lookup could come
13817
- // back empty and throw, taking the whole repaint down with it.
13818
13827
  this._internal__items = this._internal__series._internal_bars()._internal_rows().map((row) => this._internal__createRawItem(row._internal_index, row, colorer));
13819
13828
  }
13820
13829
  }
@@ -17885,7 +17894,7 @@
17885
17894
  hideSource: options.hideSource ?? true,
17886
17895
  };
17887
17896
  const sourceOptions = series.options();
17888
- // Same price scale and the same formatting as the source: the candles are a
17897
+ // Same price scale, formatting and axis labels as the source: the candles are a
17889
17898
  // restatement of that series, not a second instrument, so they must not
17890
17899
  // autoscale on their own axis or round to a different precision.
17891
17900
  const haSeries = chart.addSeries(candlestickSeries, {
@@ -17899,6 +17908,8 @@
17899
17908
  wickDownColor: resolved.wickDownColor,
17900
17909
  priceScaleId: sourceOptions.priceScaleId,
17901
17910
  priceFormat: sourceOptions.priceFormat,
17911
+ lastValueVisible: sourceOptions.lastValueVisible,
17912
+ priceLineVisible: sourceOptions.priceLineVisible,
17902
17913
  }, resolved.paneIndex);
17903
17914
  let restoreSourcePaint = null;
17904
17915
  const blankSource = (blank) => {
@@ -18100,12 +18111,14 @@
18100
18111
  const macdSeries = chart.addSeries(lineSeries, {
18101
18112
  color: resolved.colorMACD,
18102
18113
  }, paneIndex);
18114
+ // An index past the last pane opens a new one, so the rest follow MACD to where it landed.
18115
+ const macdPane = paneIndexOf(chart, macdSeries, paneIndex);
18103
18116
  const signalSeries = chart.addSeries(lineSeries, {
18104
18117
  color: resolved.colorSignal,
18105
- }, paneIndex);
18118
+ }, macdPane);
18106
18119
  const histogramSeries$1 = chart.addSeries(histogramSeries, {
18107
18120
  color: resolved.colorHistogramUp,
18108
- }, paneIndex);
18121
+ }, macdPane);
18109
18122
  // Calculate EMA
18110
18123
  const calculateEMA = (prices, period) => {
18111
18124
  const ema = [];
@@ -18237,13 +18250,13 @@
18237
18250
  // Stochastic is bounded to 0-100; pinning it keeps the pane readable
18238
18251
  autoscaleInfoProvider: boundedScale,
18239
18252
  }, paneIndex);
18253
+ // An index past the last pane opens a new one, so %D follows %K to where it landed.
18254
+ const scalePane = paneIndexOf(chart, kSeries, paneIndex);
18240
18255
  // Create %D line (slow - signal)
18241
18256
  const dSeries = chart.addSeries(lineSeries, {
18242
18257
  color: resolved.colorD,
18243
18258
  autoscaleInfoProvider: boundedScale,
18244
- }, paneIndex);
18245
- // The pane the series landed in, which is not always the one asked for.
18246
- const scalePane = paneIndexOf(chart, kSeries, paneIndex);
18259
+ }, scalePane);
18247
18260
  chart.priceScale(kSeries.options().priceScaleId ?? 'right', scalePane).applyOptions({
18248
18261
  scaleMargins: {
18249
18262
  top: 0.1,
@@ -18585,15 +18598,16 @@
18585
18598
  color: resolved.colorADX,
18586
18599
  autoscaleInfoProvider: boundedScale,
18587
18600
  }, paneIndex);
18601
+ // An index past the last pane opens a new one, so the DI lines follow ADX to where it landed.
18602
+ const scalePane = paneIndexOf(chart, adxSeries, paneIndex);
18588
18603
  const plusSeries = chart.addSeries(lineSeries, {
18589
18604
  color: resolved.colorPlusDI,
18590
18605
  autoscaleInfoProvider: boundedScale,
18591
- }, paneIndex);
18606
+ }, scalePane);
18592
18607
  const minusSeries = chart.addSeries(lineSeries, {
18593
18608
  color: resolved.colorMinusDI,
18594
18609
  autoscaleInfoProvider: boundedScale,
18595
- }, paneIndex);
18596
- const scalePane = paneIndexOf(chart, adxSeries, paneIndex);
18610
+ }, scalePane);
18597
18611
  chart.priceScale(adxSeries.options().priceScaleId ?? 'right', scalePane).applyOptions({
18598
18612
  scaleMargins: {
18599
18613
  top: 0.1,
@@ -19709,6 +19723,8 @@
19709
19723
  this._private__pointerId = null;
19710
19724
  this._private__pressOrigin = null;
19711
19725
  this._private__pressedLine = null;
19726
+ /** Whether the gesture in flight came from a finger rather than a mouse. */
19727
+ this._private__pressWasTouch = false;
19712
19728
  /** Distance from the pointer to the dragged line when the press landed. */
19713
19729
  this._private__grabOffset = 0;
19714
19730
  /** Latest pointer position in pane coordinates, or null when off the panes. */
@@ -19773,8 +19789,18 @@
19773
19789
  this._private__applyCursor();
19774
19790
  }
19775
19791
  _private__onPointerDown(event) {
19776
- const line = this._private__hoveredId === null ? null : this._private__lines.get(this._private__hoveredId) ?? null;
19792
+ // Hover belongs to the mouse. Touch leaves whatever the last press marked
19793
+ // still marked, so trusting it here grabbed that line again wherever the
19794
+ // next tap landed: every press resolves itself instead.
19795
+ this._private__pressWasTouch = event.pointerType !== 'mouse';
19796
+ let line = this._private__pressWasTouch || this._private__hoveredId === null
19797
+ ? null
19798
+ : this._private__lines.get(this._private__hoveredId) ?? null;
19799
+ if (line === null) {
19800
+ line = this._private__resolveHitAt(event);
19801
+ }
19777
19802
  if (!line) {
19803
+ this._private__clearTouchHover();
19778
19804
  return;
19779
19805
  }
19780
19806
  this._private__pressOrigin = { _internal_x: event.clientX, _internal_y: event.clientY };
@@ -19813,6 +19839,7 @@
19813
19839
  }
19814
19840
  _private__onPointerUp(event) {
19815
19841
  const dragged = this._private__dragging;
19842
+ const wasTouch = this._private__pressWasTouch;
19816
19843
  const pressed = this._private__pressedLine;
19817
19844
  const origin = this._private__pressOrigin;
19818
19845
  this._private__releasePointer();
@@ -19834,12 +19861,24 @@
19834
19861
  dragged.click();
19835
19862
  }
19836
19863
  }
19864
+ this._private__clearTouchHover(wasTouch);
19837
19865
  this._private__applyCursor();
19838
19866
  return;
19839
19867
  }
19840
19868
  if (pressed !== null && !moved && pressed.isClickable()) {
19841
19869
  pressed.click();
19842
19870
  }
19871
+ this._private__clearTouchHover(wasTouch);
19872
+ }
19873
+ /** A finger leaves nothing hovered behind it, the way lifting a mouse does. */
19874
+ _private__clearTouchHover(wasTouch = this._private__pressWasTouch) {
19875
+ if (!wasTouch || this._private__hoveredId === null) {
19876
+ return;
19877
+ }
19878
+ const line = this._private__lines.get(this._private__hoveredId) ?? null;
19879
+ this._private__setHovered(null);
19880
+ this._private__setHoveredPart(line, null);
19881
+ this._private__applyCursor();
19843
19882
  }
19844
19883
  /**
19845
19884
  * Abort an in-flight gesture: the line goes back to where it was and no
@@ -19885,9 +19924,38 @@
19885
19924
  return paneY === null ? null : line.priceAt(paneY - this._private__grabOffset);
19886
19925
  }
19887
19926
  /** The pointer's position inside the line's pane, in pane coordinates. */
19888
- _private__paneY(line, event) {
19927
+ /**
19928
+ * The hovered id comes from the crosshair, which touch never moves before it
19929
+ * presses, so the press resolves the target itself.
19930
+ */
19931
+ _private__resolveHitAt(event) {
19932
+ for (const line of this._private__lines.values()) {
19933
+ if (!line.hitTestAt) {
19934
+ continue;
19935
+ }
19936
+ const paneElement = this._private__paneElementOf(line);
19937
+ if (paneElement === null) {
19938
+ continue;
19939
+ }
19940
+ const rect = paneElement.getBoundingClientRect();
19941
+ // A fingertip is far coarser than a pointer, so its targets are widened.
19942
+ const slop = event.pointerType === 'mouse' ? 0 : 12 /* Constants.TouchSlopPx */;
19943
+ const hit = this._private__splitHit(line.hitTestAt(event.clientX - rect.left, event.clientY - rect.top, slop));
19944
+ if (hit === null) {
19945
+ continue;
19946
+ }
19947
+ this._private__setHovered(line.id());
19948
+ this._private__setHoveredPart(line, hit._internal_part);
19949
+ return line;
19950
+ }
19951
+ return null;
19952
+ }
19953
+ _private__paneElementOf(line) {
19889
19954
  const pane = this._private__chart.panes()[line.paneIndex()];
19890
- const paneElement = pane ? pane.getHTMLElement() : null;
19955
+ return pane ? pane.getHTMLElement() : null;
19956
+ }
19957
+ _private__paneY(line, event) {
19958
+ const paneElement = this._private__paneElementOf(line);
19891
19959
  if (paneElement) {
19892
19960
  return event.clientY - paneElement.getBoundingClientRect().top;
19893
19961
  }
@@ -20004,6 +20072,7 @@
20004
20072
  * line. Hit testing comes from the engine — the line's id travels as the price
20005
20073
  * line's `externalId` — so the hit area matches what is drawn.
20006
20074
  */
20075
+ const HIT_TOLERANCE_PX$1 = 8;
20007
20076
  class DraggablePriceLine {
20008
20077
  constructor(chart, series, options) {
20009
20078
  this._private__priceLine = null;
@@ -20056,6 +20125,14 @@
20056
20125
  color: hovered ? this._private__options.hoverColor : this._private__originalColor,
20057
20126
  });
20058
20127
  }
20128
+ /**
20129
+ * The engine draws this one, so there is no pane view to ask: a press within
20130
+ * the line's own grab strip counts as a hit.
20131
+ */
20132
+ hitTestAt(x, y, slop = 0) {
20133
+ const coord = this._private__series.priceToCoordinate(this._private__options.price);
20134
+ return coord !== null && Math.abs(y - coord) <= HIT_TOLERANCE_PX$1 + slop ? this._private__id : null;
20135
+ }
20059
20136
  priceAt(paneY) {
20060
20137
  const price = this._private__series.coordinateToPrice(paneY);
20061
20138
  return price === null ? null : price;
@@ -20598,22 +20675,27 @@
20598
20675
  _internal_setData(data) {
20599
20676
  this._private__data = data;
20600
20677
  }
20601
- _internal_hitTest(x, y) {
20678
+ /**
20679
+ * `slop` widens every target by the same amount, for input coarser than a
20680
+ * mouse pointer.
20681
+ */
20682
+ _internal_hitTest(x, y, slop = 0) {
20602
20683
  // The close button sits on top of the label, which sits on top of the
20603
20684
  // line, so the narrowest target is tested first.
20604
20685
  for (const layout of this._private__layouts) {
20605
- if (layout._internal_closable && x >= layout._internal_closeLeft && x <= layout._internal_closeRight &&
20606
- Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2) {
20686
+ if (layout._internal_closable && x >= layout._internal_closeLeft - slop && x <= layout._internal_closeRight + slop &&
20687
+ Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2 + slop) {
20607
20688
  return { _internal_level: layout._internal_level, _internal_part: 'close' };
20608
20689
  }
20609
20690
  }
20610
20691
  for (const layout of this._private__layouts) {
20611
- if (x >= layout._internal_pillLeft && x <= layout._internal_pillRight && Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2) {
20692
+ if (x >= layout._internal_pillLeft - slop && x <= layout._internal_pillRight + slop &&
20693
+ Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2 + slop) {
20612
20694
  return { _internal_level: layout._internal_level, _internal_part: 'body' };
20613
20695
  }
20614
20696
  }
20615
20697
  for (const layout of this._private__layouts) {
20616
- if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */) {
20698
+ if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */ + slop) {
20617
20699
  return { _internal_level: layout._internal_level, _internal_part: 'line' };
20618
20700
  }
20619
20701
  }
@@ -20850,8 +20932,8 @@
20850
20932
  zOrder() {
20851
20933
  return 'top';
20852
20934
  }
20853
- _internal_hitTest(x, y) {
20854
- return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y);
20935
+ _internal_hitTest(x, y, slop = 0) {
20936
+ return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y, slop);
20855
20937
  }
20856
20938
  }
20857
20939
 
@@ -21002,6 +21084,13 @@
21002
21084
  * of jumping to sit under it — which matters most when the label has been
21003
21085
  * fanned away from its own line.
21004
21086
  */
21087
+ hitTestAt(x, y, slop = 0) {
21088
+ const hit = this._private__paneView._internal_hitTest(x, y, slop);
21089
+ if (hit === null) {
21090
+ return null;
21091
+ }
21092
+ return this._private__levelOptions(hit._internal_level) === null ? null : `${this._private__id}#${hit._internal_level}:${hit._internal_part}`;
21093
+ }
21005
21094
  dragAnchorY() {
21006
21095
  if (this._private__dragLevel === null) {
21007
21096
  return null;
@@ -30072,6 +30161,7 @@
30072
30161
  this._private__mouseMoveHandler = null;
30073
30162
  this._private__pointerDownHandler = null;
30074
30163
  this._private__pointerUpHandler = null;
30164
+ this._private__pointerMoveHandler = null;
30075
30165
  this._private__pointerCancelHandler = null;
30076
30166
  this._private__lostCaptureHandler = null;
30077
30167
  this._private__windowPointerUpHandler = null;
@@ -30646,10 +30736,12 @@
30646
30736
  if (chartElement) {
30647
30737
  this._private__pointerDownHandler = (event) => this._private__handlePointerDown(event);
30648
30738
  this._private__pointerUpHandler = (event) => this._private__handlePointerUp(event);
30739
+ this._private__pointerMoveHandler = (event) => this._private__handlePointerMove(event);
30649
30740
  this._private__pointerCancelHandler = (event) => this._private__handlePointerUp(event);
30650
30741
  this._private__lostCaptureHandler = (event) => this._private__handleLostPointerCapture(event);
30651
30742
  chartElement.addEventListener('pointerdown', this._private__pointerDownHandler);
30652
30743
  chartElement.addEventListener('pointerup', this._private__pointerUpHandler);
30744
+ chartElement.addEventListener('pointermove', this._private__pointerMoveHandler);
30653
30745
  chartElement.addEventListener('pointercancel', this._private__pointerCancelHandler);
30654
30746
  chartElement.addEventListener('lostpointercapture', this._private__lostCaptureHandler);
30655
30747
  }
@@ -30684,6 +30776,10 @@
30684
30776
  chartElement.removeEventListener('pointerup', this._private__pointerUpHandler);
30685
30777
  this._private__pointerUpHandler = null;
30686
30778
  }
30779
+ if (this._private__pointerMoveHandler) {
30780
+ chartElement.removeEventListener('pointermove', this._private__pointerMoveHandler);
30781
+ this._private__pointerMoveHandler = null;
30782
+ }
30687
30783
  if (this._private__pointerCancelHandler) {
30688
30784
  chartElement.removeEventListener('pointercancel', this._private__pointerCancelHandler);
30689
30785
  this._private__pointerCancelHandler = null;
@@ -30833,6 +30929,36 @@
30833
30929
  // mouse events, and those are what drive the crosshair the drag reads.
30834
30930
  this._private__disableChartInteractions();
30835
30931
  }
30932
+ /**
30933
+ * The drag reads the crosshair, which touch never moves: its move handler
30934
+ * only tries to pan, and the drag has just turned panning off. So touch is
30935
+ * driven straight from the pointer instead.
30936
+ */
30937
+ _private__handlePointerMove(event) {
30938
+ if (!this._private__isDragging || this._private__draggedToolId === null || event.pointerType === 'mouse') {
30939
+ return;
30940
+ }
30941
+ if (this._private__pointerId !== null && event.pointerId !== this._private__pointerId) {
30942
+ return;
30943
+ }
30944
+ const tool = this._private__tools.get(this._private__draggedToolId);
30945
+ if (!tool) {
30946
+ return;
30947
+ }
30948
+ const paneElement = this._private__paneElementOf(tool);
30949
+ if (paneElement === null) {
30950
+ return;
30951
+ }
30952
+ const rect = paneElement.getBoundingClientRect();
30953
+ const series = tool.series();
30954
+ const price = series.coordinateToPrice((event.clientY - rect.top));
30955
+ const time = timeFromCoordinate(this._private__chart, series, event.clientX - rect.left);
30956
+ if (price === null || time === null) {
30957
+ return;
30958
+ }
30959
+ tool.updateDrag(time, price);
30960
+ event.preventDefault();
30961
+ }
30836
30962
  _private__handlePointerUp(event) {
30837
30963
  if (!this._private__isDragging || this._private__draggedToolId === null) {
30838
30964
  return;
@@ -31006,7 +31132,7 @@
31006
31132
  * Returns the current version as a string. For example `'1.0.0'`.
31007
31133
  */
31008
31134
  function version() {
31009
- return "2.3.4";
31135
+ return "2.3.11";
31010
31136
  }
31011
31137
 
31012
31138
  var PipsendChartsModule = /*#__PURE__*/Object.freeze({