@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)
@@ -2483,14 +2483,8 @@ class SeriesBarColorer {
2483
2483
  return this._private__styleGetter(this._private__findBar, this._private__series._internal_options(), barIndex, precomputedBars);
2484
2484
  }
2485
2485
  /**
2486
- * Like `barStyle`, but returns null instead of throwing when the bar is
2487
- * gone.
2488
- *
2489
- * Recolouring walks items that were laid out against the data as it was. A
2490
- * bar that has since disappeared is not an error there: the view is about
2491
- * to be rebuilt from the new data anyway. Letting it throw took down
2492
- * whatever asked for the repaint — including `setData`, which left the
2493
- * series without its data and the pane empty until the page was reloaded.
2486
+ * Like `barStyle`, but null instead of throwing when the bar is gone, which
2487
+ * a recolour of items laid out against older data can hit.
2494
2488
  */
2495
2489
  _internal_barStyleOrNull(barIndex, precomputedBars) {
2496
2490
  if (precomputedBars === undefined && this._private__series._internal_bars()._internal_valueAt(barIndex) === null) {
@@ -2892,7 +2886,10 @@ class SeriesPrimitivePriceAxisViewWrapper extends PriceAxisView {
2892
2886
  commonRendererData._internal_additionalPaddingTop = additionalPadding;
2893
2887
  commonRendererData._internal_additionalPaddingBottom = additionalPadding;
2894
2888
  commonRendererData._internal_coordinate = data._internal_coordinate;
2895
- commonRendererData._internal_fixedCoordinate = data._internal_fixedCoordinate;
2889
+ // Re-runs at draw time, after the axis has moved the label clear of overlaps.
2890
+ if (data._internal_fixedCoordinate !== undefined) {
2891
+ commonRendererData._internal_fixedCoordinate = data._internal_fixedCoordinate;
2892
+ }
2896
2893
  axisRendererData._internal_text = data._internal_text;
2897
2894
  axisRendererData._internal_visible = data._internal_visible;
2898
2895
  axisRendererData._internal_tickVisible = data._internal_tickVisible;
@@ -3769,7 +3766,6 @@ class PriceTickSpanCalculator {
3769
3766
  }
3770
3767
  }
3771
3768
 
3772
- const TICK_DENSITY = 2.5;
3773
3769
  class PriceTickMarkBuilder {
3774
3770
  constructor(priceScale, base, coordinateToLogicalFunc, logicalToCoordinateFunc) {
3775
3771
  this._private__marks = [];
@@ -3830,7 +3826,7 @@ class PriceTickMarkBuilder {
3830
3826
  return this._private__priceScale._internal_fontSize();
3831
3827
  }
3832
3828
  _private__tickMarkHeight() {
3833
- return Math.ceil(this._private__fontHeight() * TICK_DENSITY);
3829
+ return Math.max(1, Math.ceil(this._private__fontHeight() * this._private__priceScale._internal_options().tickMarkSpacing));
3834
3830
  }
3835
3831
  _private__updateMarks(firstValue, span, high, low, minCoord, maxCoord) {
3836
3832
  const marks = this._private__marks;
@@ -3995,6 +3991,9 @@ class PriceScale {
3995
3991
  this._private__invalidateInternalHeightCache();
3996
3992
  this._private__marksCache = null;
3997
3993
  }
3994
+ if (options.tickMarkSpacing !== undefined) {
3995
+ this._private__marksCache = null;
3996
+ }
3998
3997
  }
3999
3998
  _internal_isAutoScale() {
4000
3999
  return this._private__options.autoScale;
@@ -5172,6 +5171,9 @@ class TickMarks {
5172
5171
  this._private__uniformDistribution = val;
5173
5172
  this._private__cache = null;
5174
5173
  }
5174
+ _internal_invalidate() {
5175
+ this._private__cache = null;
5176
+ }
5175
5177
  _internal_setTimeScalePoints(newPoints, firstChangedPointIndex) {
5176
5178
  this._private__removeMarksSinceIndex(firstChangedPointIndex);
5177
5179
  this._private__cache = null;
@@ -5190,7 +5192,7 @@ class TickMarks {
5190
5192
  });
5191
5193
  }
5192
5194
  }
5193
- _internal_build(spacing, maxWidth, checkIndicesForData, indicesWithDataMap, indicesWithDataId) {
5195
+ _internal_build(spacing, maxWidth, checkIndicesForData, indicesWithDataMap, indicesWithDataId, allows = () => true) {
5194
5196
  const maxIndexesPerMark = Math.ceil(maxWidth / spacing);
5195
5197
  if (this._private__cache === null ||
5196
5198
  this._private__cache._internal_maxIndexesPerMark !== maxIndexesPerMark ||
@@ -5199,7 +5201,7 @@ class TickMarks {
5199
5201
  this._private__cache = {
5200
5202
  _internal_indicesWithDataId: indicesWithDataId,
5201
5203
  _internal_checkIndicesForData: checkIndicesForData,
5202
- _internal_marks: this._private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap),
5204
+ _internal_marks: this._private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap, allows),
5203
5205
  _internal_maxIndexesPerMark: maxIndexesPerMark,
5204
5206
  };
5205
5207
  }
@@ -5223,7 +5225,7 @@ class TickMarks {
5223
5225
  this._private__marksByWeight.delete(weight);
5224
5226
  }
5225
5227
  }
5226
- _private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap) {
5228
+ _private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap, allows) {
5227
5229
  let marks = [];
5228
5230
  const canBeIncluded = (mark) => !checkIndicesForData || indicesWithDataMap.has(mark.index);
5229
5231
  for (const weight of Array.from(this._private__marksByWeight.keys()).sort((a, b) => b - a)) {
@@ -5241,6 +5243,11 @@ class TickMarks {
5241
5243
  let leftIndex = -Infinity;
5242
5244
  for (let i = 0; i < currentWeightLength; i++) {
5243
5245
  const mark = currentWeight[i];
5246
+ // Skipped before the fit check: a rejected mark must not count as one
5247
+ // that did not fit, which with uniform distribution drops its whole weight.
5248
+ if (!allows(mark, maxIndexesPerMark)) {
5249
+ continue;
5250
+ }
5244
5251
  const currentIndex = mark.index;
5245
5252
  // Determine indexes with which current index will be compared
5246
5253
  // All marks to the right is moved to new array
@@ -5547,7 +5554,7 @@ class TimeScale {
5547
5554
  const visibleBars = ensureNotNull(this._internal_visibleStrictRange());
5548
5555
  const firstBar = Math.max(visibleBars._internal_left(), visibleBars._internal_left() - indexPerLabel);
5549
5556
  const lastBar = Math.max(visibleBars._internal_right(), visibleBars._internal_right() - indexPerLabel);
5550
- const items = this._private__tickMarks._internal_build(spacing, maxLabelWidth, this._private__options.ignoreWhitespaceIndices, this._private__indicesWithData, this._private__indicesWithDataUpdateId);
5557
+ 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);
5551
5558
  // according to indexPerLabel value this value means "earliest index which _might be_ used as the second label on time scale"
5552
5559
  const earliestIndexOfSecondLabel = this._private__firstIndex() + indexPerLabel;
5553
5560
  // according to indexPerLabel value this value means "earliest index which _might be_ used as the second last label on time scale"
@@ -5925,6 +5932,7 @@ class TimeScale {
5925
5932
  _private__invalidateTickMarks() {
5926
5933
  this._private__resetTimeMarksCache();
5927
5934
  this._private__formattedByWeight.clear();
5935
+ this._private__tickMarks._internal_invalidate();
5928
5936
  }
5929
5937
  _private__updateDateTimeFormatter() {
5930
5938
  this._private__horzScaleBehavior.updateFormatter(this._private__localizationOptions);
@@ -7039,6 +7047,14 @@ class HorzScaleBehaviorTime {
7039
7047
  const tp = item;
7040
7048
  return this._private__dateTimeFormatter._internal_format(new Date(tp._internal_timestamp * 1000));
7041
7049
  }
7050
+ allowsTickMark(tickMark, barsPerMark) {
7051
+ const filter = this._private__options.timeScale.tickMarkFilter;
7052
+ if (filter === undefined) {
7053
+ return true;
7054
+ }
7055
+ const tickMarkType = weightToTickMarkType(tickMark.weight, this._private__options.timeScale.timeVisible, this._private__options.timeScale.secondsVisible);
7056
+ return filter(tickMark.originalTime, tickMarkType, barsPerMark);
7057
+ }
7042
7058
  formatTickmark(tickMark, localizationOptions) {
7043
7059
  const tickMarkType = weightToTickMarkType(tickMark.weight, this._private__options.timeScale.timeVisible, this._private__options.timeScale.secondsVisible);
7044
7060
  const options = this._private__options.timeScale;
@@ -11443,9 +11459,6 @@ class SeriesPaneViewBase {
11443
11459
  _internal__updateOptions() {
11444
11460
  const colorer = this._internal__series._internal_barColorer();
11445
11461
  this._internal__items = this._internal__items.map((item) => {
11446
- // An item whose bar is gone keeps the colour it had. Throwing here
11447
- // took down whatever asked for the repaint — `setData` among them,
11448
- // which left the series without its data and the pane empty.
11449
11462
  const style = colorer._internal_barStyleOrNull(item._internal_time);
11450
11463
  return style === null ? item : { ...item, ...style };
11451
11464
  });
@@ -11748,6 +11761,7 @@ const priceScaleOptionsDefaults = {
11748
11761
  },
11749
11762
  minimumWidth: 0,
11750
11763
  ensureEdgeTickMarksVisible: false,
11764
+ tickMarkSpacing: 2.5,
11751
11765
  };
11752
11766
 
11753
11767
  const timeScaleOptionsDefaults = {
@@ -12843,8 +12857,6 @@ class LinePaneViewBase extends SeriesPaneViewBase {
12843
12857
  const colorer = this._internal__series._internal_barColorer();
12844
12858
  this._internal__items = this._internal__series._internal_bars()._internal_rows().map((row) => {
12845
12859
  const value = row._internal_value[3 /* PlotRowValueIndex.Close */];
12846
- // The row travels with the point: without it the colorer looks it up
12847
- // again by index, and that lookup could fail and take the repaint down.
12848
12860
  return this._internal__createRawItem(row._internal_index, value, colorer, row);
12849
12861
  });
12850
12862
  }
@@ -12867,10 +12879,10 @@ styleGetter) {
12867
12879
  if (point) {
12868
12880
  const style = styleGetter(renderingScope, point);
12869
12881
  if (style !== prevStyle) {
12870
- context.beginPath();
12871
12882
  if (prevStyle !== null) {
12872
12883
  context.fill();
12873
12884
  }
12885
+ context.beginPath();
12874
12886
  context.fillStyle = style;
12875
12887
  prevStyle = style;
12876
12888
  }
@@ -13809,9 +13821,6 @@ class BarsPaneViewBase extends SeriesPaneViewBase {
13809
13821
  }
13810
13822
  _internal__fillRawPoints() {
13811
13823
  const colorer = this._internal__series._internal_barColorer();
13812
- // The row goes to the colorer as it is. Asking for the style by index
13813
- // alone made it look the row up again, and that second lookup could come
13814
- // back empty and throw, taking the whole repaint down with it.
13815
13824
  this._internal__items = this._internal__series._internal_bars()._internal_rows().map((row) => this._internal__createRawItem(row._internal_index, row, colorer));
13816
13825
  }
13817
13826
  }
@@ -17882,7 +17891,7 @@ function applyHeikinAshi(series, chart, options = {}) {
17882
17891
  hideSource: options.hideSource ?? true,
17883
17892
  };
17884
17893
  const sourceOptions = series.options();
17885
- // Same price scale and the same formatting as the source: the candles are a
17894
+ // Same price scale, formatting and axis labels as the source: the candles are a
17886
17895
  // restatement of that series, not a second instrument, so they must not
17887
17896
  // autoscale on their own axis or round to a different precision.
17888
17897
  const haSeries = chart.addSeries(candlestickSeries, {
@@ -17896,6 +17905,8 @@ function applyHeikinAshi(series, chart, options = {}) {
17896
17905
  wickDownColor: resolved.wickDownColor,
17897
17906
  priceScaleId: sourceOptions.priceScaleId,
17898
17907
  priceFormat: sourceOptions.priceFormat,
17908
+ lastValueVisible: sourceOptions.lastValueVisible,
17909
+ priceLineVisible: sourceOptions.priceLineVisible,
17899
17910
  }, resolved.paneIndex);
17900
17911
  let restoreSourcePaint = null;
17901
17912
  const blankSource = (blank) => {
@@ -18097,12 +18108,14 @@ function applyMACD(series, chart, options = {}) {
18097
18108
  const macdSeries = chart.addSeries(lineSeries, {
18098
18109
  color: resolved.colorMACD,
18099
18110
  }, paneIndex);
18111
+ // An index past the last pane opens a new one, so the rest follow MACD to where it landed.
18112
+ const macdPane = paneIndexOf(chart, macdSeries, paneIndex);
18100
18113
  const signalSeries = chart.addSeries(lineSeries, {
18101
18114
  color: resolved.colorSignal,
18102
- }, paneIndex);
18115
+ }, macdPane);
18103
18116
  const histogramSeries$1 = chart.addSeries(histogramSeries, {
18104
18117
  color: resolved.colorHistogramUp,
18105
- }, paneIndex);
18118
+ }, macdPane);
18106
18119
  // Calculate EMA
18107
18120
  const calculateEMA = (prices, period) => {
18108
18121
  const ema = [];
@@ -18234,13 +18247,13 @@ function applyStochastic(series, chart, options = {}) {
18234
18247
  // Stochastic is bounded to 0-100; pinning it keeps the pane readable
18235
18248
  autoscaleInfoProvider: boundedScale,
18236
18249
  }, paneIndex);
18250
+ // An index past the last pane opens a new one, so %D follows %K to where it landed.
18251
+ const scalePane = paneIndexOf(chart, kSeries, paneIndex);
18237
18252
  // Create %D line (slow - signal)
18238
18253
  const dSeries = chart.addSeries(lineSeries, {
18239
18254
  color: resolved.colorD,
18240
18255
  autoscaleInfoProvider: boundedScale,
18241
- }, paneIndex);
18242
- // The pane the series landed in, which is not always the one asked for.
18243
- const scalePane = paneIndexOf(chart, kSeries, paneIndex);
18256
+ }, scalePane);
18244
18257
  chart.priceScale(kSeries.options().priceScaleId ?? 'right', scalePane).applyOptions({
18245
18258
  scaleMargins: {
18246
18259
  top: 0.1,
@@ -18582,15 +18595,16 @@ function applyADX(series, chart, options = {}) {
18582
18595
  color: resolved.colorADX,
18583
18596
  autoscaleInfoProvider: boundedScale,
18584
18597
  }, paneIndex);
18598
+ // An index past the last pane opens a new one, so the DI lines follow ADX to where it landed.
18599
+ const scalePane = paneIndexOf(chart, adxSeries, paneIndex);
18585
18600
  const plusSeries = chart.addSeries(lineSeries, {
18586
18601
  color: resolved.colorPlusDI,
18587
18602
  autoscaleInfoProvider: boundedScale,
18588
- }, paneIndex);
18603
+ }, scalePane);
18589
18604
  const minusSeries = chart.addSeries(lineSeries, {
18590
18605
  color: resolved.colorMinusDI,
18591
18606
  autoscaleInfoProvider: boundedScale,
18592
- }, paneIndex);
18593
- const scalePane = paneIndexOf(chart, adxSeries, paneIndex);
18607
+ }, scalePane);
18594
18608
  chart.priceScale(adxSeries.options().priceScaleId ?? 'right', scalePane).applyOptions({
18595
18609
  scaleMargins: {
18596
18610
  top: 0.1,
@@ -19706,6 +19720,8 @@ class TradingLineController {
19706
19720
  this._private__pointerId = null;
19707
19721
  this._private__pressOrigin = null;
19708
19722
  this._private__pressedLine = null;
19723
+ /** Whether the gesture in flight came from a finger rather than a mouse. */
19724
+ this._private__pressWasTouch = false;
19709
19725
  /** Distance from the pointer to the dragged line when the press landed. */
19710
19726
  this._private__grabOffset = 0;
19711
19727
  /** Latest pointer position in pane coordinates, or null when off the panes. */
@@ -19770,8 +19786,18 @@ class TradingLineController {
19770
19786
  this._private__applyCursor();
19771
19787
  }
19772
19788
  _private__onPointerDown(event) {
19773
- const line = this._private__hoveredId === null ? null : this._private__lines.get(this._private__hoveredId) ?? null;
19789
+ // Hover belongs to the mouse. Touch leaves whatever the last press marked
19790
+ // still marked, so trusting it here grabbed that line again wherever the
19791
+ // next tap landed: every press resolves itself instead.
19792
+ this._private__pressWasTouch = event.pointerType !== 'mouse';
19793
+ let line = this._private__pressWasTouch || this._private__hoveredId === null
19794
+ ? null
19795
+ : this._private__lines.get(this._private__hoveredId) ?? null;
19796
+ if (line === null) {
19797
+ line = this._private__resolveHitAt(event);
19798
+ }
19774
19799
  if (!line) {
19800
+ this._private__clearTouchHover();
19775
19801
  return;
19776
19802
  }
19777
19803
  this._private__pressOrigin = { _internal_x: event.clientX, _internal_y: event.clientY };
@@ -19810,6 +19836,7 @@ class TradingLineController {
19810
19836
  }
19811
19837
  _private__onPointerUp(event) {
19812
19838
  const dragged = this._private__dragging;
19839
+ const wasTouch = this._private__pressWasTouch;
19813
19840
  const pressed = this._private__pressedLine;
19814
19841
  const origin = this._private__pressOrigin;
19815
19842
  this._private__releasePointer();
@@ -19831,12 +19858,24 @@ class TradingLineController {
19831
19858
  dragged.click();
19832
19859
  }
19833
19860
  }
19861
+ this._private__clearTouchHover(wasTouch);
19834
19862
  this._private__applyCursor();
19835
19863
  return;
19836
19864
  }
19837
19865
  if (pressed !== null && !moved && pressed.isClickable()) {
19838
19866
  pressed.click();
19839
19867
  }
19868
+ this._private__clearTouchHover(wasTouch);
19869
+ }
19870
+ /** A finger leaves nothing hovered behind it, the way lifting a mouse does. */
19871
+ _private__clearTouchHover(wasTouch = this._private__pressWasTouch) {
19872
+ if (!wasTouch || this._private__hoveredId === null) {
19873
+ return;
19874
+ }
19875
+ const line = this._private__lines.get(this._private__hoveredId) ?? null;
19876
+ this._private__setHovered(null);
19877
+ this._private__setHoveredPart(line, null);
19878
+ this._private__applyCursor();
19840
19879
  }
19841
19880
  /**
19842
19881
  * Abort an in-flight gesture: the line goes back to where it was and no
@@ -19882,9 +19921,38 @@ class TradingLineController {
19882
19921
  return paneY === null ? null : line.priceAt(paneY - this._private__grabOffset);
19883
19922
  }
19884
19923
  /** The pointer's position inside the line's pane, in pane coordinates. */
19885
- _private__paneY(line, event) {
19924
+ /**
19925
+ * The hovered id comes from the crosshair, which touch never moves before it
19926
+ * presses, so the press resolves the target itself.
19927
+ */
19928
+ _private__resolveHitAt(event) {
19929
+ for (const line of this._private__lines.values()) {
19930
+ if (!line.hitTestAt) {
19931
+ continue;
19932
+ }
19933
+ const paneElement = this._private__paneElementOf(line);
19934
+ if (paneElement === null) {
19935
+ continue;
19936
+ }
19937
+ const rect = paneElement.getBoundingClientRect();
19938
+ // A fingertip is far coarser than a pointer, so its targets are widened.
19939
+ const slop = event.pointerType === 'mouse' ? 0 : 12 /* Constants.TouchSlopPx */;
19940
+ const hit = this._private__splitHit(line.hitTestAt(event.clientX - rect.left, event.clientY - rect.top, slop));
19941
+ if (hit === null) {
19942
+ continue;
19943
+ }
19944
+ this._private__setHovered(line.id());
19945
+ this._private__setHoveredPart(line, hit._internal_part);
19946
+ return line;
19947
+ }
19948
+ return null;
19949
+ }
19950
+ _private__paneElementOf(line) {
19886
19951
  const pane = this._private__chart.panes()[line.paneIndex()];
19887
- const paneElement = pane ? pane.getHTMLElement() : null;
19952
+ return pane ? pane.getHTMLElement() : null;
19953
+ }
19954
+ _private__paneY(line, event) {
19955
+ const paneElement = this._private__paneElementOf(line);
19888
19956
  if (paneElement) {
19889
19957
  return event.clientY - paneElement.getBoundingClientRect().top;
19890
19958
  }
@@ -20001,6 +20069,7 @@ let nextLineId = 0;
20001
20069
  * line. Hit testing comes from the engine — the line's id travels as the price
20002
20070
  * line's `externalId` — so the hit area matches what is drawn.
20003
20071
  */
20072
+ const HIT_TOLERANCE_PX$1 = 8;
20004
20073
  class DraggablePriceLine {
20005
20074
  constructor(chart, series, options) {
20006
20075
  this._private__priceLine = null;
@@ -20053,6 +20122,14 @@ class DraggablePriceLine {
20053
20122
  color: hovered ? this._private__options.hoverColor : this._private__originalColor,
20054
20123
  });
20055
20124
  }
20125
+ /**
20126
+ * The engine draws this one, so there is no pane view to ask: a press within
20127
+ * the line's own grab strip counts as a hit.
20128
+ */
20129
+ hitTestAt(x, y, slop = 0) {
20130
+ const coord = this._private__series.priceToCoordinate(this._private__options.price);
20131
+ return coord !== null && Math.abs(y - coord) <= HIT_TOLERANCE_PX$1 + slop ? this._private__id : null;
20132
+ }
20056
20133
  priceAt(paneY) {
20057
20134
  const price = this._private__series.coordinateToPrice(paneY);
20058
20135
  return price === null ? null : price;
@@ -20595,22 +20672,27 @@ class PositionLineRenderer {
20595
20672
  _internal_setData(data) {
20596
20673
  this._private__data = data;
20597
20674
  }
20598
- _internal_hitTest(x, y) {
20675
+ /**
20676
+ * `slop` widens every target by the same amount, for input coarser than a
20677
+ * mouse pointer.
20678
+ */
20679
+ _internal_hitTest(x, y, slop = 0) {
20599
20680
  // The close button sits on top of the label, which sits on top of the
20600
20681
  // line, so the narrowest target is tested first.
20601
20682
  for (const layout of this._private__layouts) {
20602
- if (layout._internal_closable && x >= layout._internal_closeLeft && x <= layout._internal_closeRight &&
20603
- Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2) {
20683
+ if (layout._internal_closable && x >= layout._internal_closeLeft - slop && x <= layout._internal_closeRight + slop &&
20684
+ Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2 + slop) {
20604
20685
  return { _internal_level: layout._internal_level, _internal_part: 'close' };
20605
20686
  }
20606
20687
  }
20607
20688
  for (const layout of this._private__layouts) {
20608
- if (x >= layout._internal_pillLeft && x <= layout._internal_pillRight && Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2) {
20689
+ if (x >= layout._internal_pillLeft - slop && x <= layout._internal_pillRight + slop &&
20690
+ Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2 + slop) {
20609
20691
  return { _internal_level: layout._internal_level, _internal_part: 'body' };
20610
20692
  }
20611
20693
  }
20612
20694
  for (const layout of this._private__layouts) {
20613
- if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */) {
20695
+ if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */ + slop) {
20614
20696
  return { _internal_level: layout._internal_level, _internal_part: 'line' };
20615
20697
  }
20616
20698
  }
@@ -20847,8 +20929,8 @@ class PositionLinePaneView {
20847
20929
  zOrder() {
20848
20930
  return 'top';
20849
20931
  }
20850
- _internal_hitTest(x, y) {
20851
- return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y);
20932
+ _internal_hitTest(x, y, slop = 0) {
20933
+ return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y, slop);
20852
20934
  }
20853
20935
  }
20854
20936
 
@@ -20999,6 +21081,13 @@ class PositionLine {
20999
21081
  * of jumping to sit under it — which matters most when the label has been
21000
21082
  * fanned away from its own line.
21001
21083
  */
21084
+ hitTestAt(x, y, slop = 0) {
21085
+ const hit = this._private__paneView._internal_hitTest(x, y, slop);
21086
+ if (hit === null) {
21087
+ return null;
21088
+ }
21089
+ return this._private__levelOptions(hit._internal_level) === null ? null : `${this._private__id}#${hit._internal_level}:${hit._internal_part}`;
21090
+ }
21002
21091
  dragAnchorY() {
21003
21092
  if (this._private__dragLevel === null) {
21004
21093
  return null;
@@ -30069,6 +30158,7 @@ class DrawingToolsManager {
30069
30158
  this._private__mouseMoveHandler = null;
30070
30159
  this._private__pointerDownHandler = null;
30071
30160
  this._private__pointerUpHandler = null;
30161
+ this._private__pointerMoveHandler = null;
30072
30162
  this._private__pointerCancelHandler = null;
30073
30163
  this._private__lostCaptureHandler = null;
30074
30164
  this._private__windowPointerUpHandler = null;
@@ -30643,10 +30733,12 @@ class DrawingToolsManager {
30643
30733
  if (chartElement) {
30644
30734
  this._private__pointerDownHandler = (event) => this._private__handlePointerDown(event);
30645
30735
  this._private__pointerUpHandler = (event) => this._private__handlePointerUp(event);
30736
+ this._private__pointerMoveHandler = (event) => this._private__handlePointerMove(event);
30646
30737
  this._private__pointerCancelHandler = (event) => this._private__handlePointerUp(event);
30647
30738
  this._private__lostCaptureHandler = (event) => this._private__handleLostPointerCapture(event);
30648
30739
  chartElement.addEventListener('pointerdown', this._private__pointerDownHandler);
30649
30740
  chartElement.addEventListener('pointerup', this._private__pointerUpHandler);
30741
+ chartElement.addEventListener('pointermove', this._private__pointerMoveHandler);
30650
30742
  chartElement.addEventListener('pointercancel', this._private__pointerCancelHandler);
30651
30743
  chartElement.addEventListener('lostpointercapture', this._private__lostCaptureHandler);
30652
30744
  }
@@ -30681,6 +30773,10 @@ class DrawingToolsManager {
30681
30773
  chartElement.removeEventListener('pointerup', this._private__pointerUpHandler);
30682
30774
  this._private__pointerUpHandler = null;
30683
30775
  }
30776
+ if (this._private__pointerMoveHandler) {
30777
+ chartElement.removeEventListener('pointermove', this._private__pointerMoveHandler);
30778
+ this._private__pointerMoveHandler = null;
30779
+ }
30684
30780
  if (this._private__pointerCancelHandler) {
30685
30781
  chartElement.removeEventListener('pointercancel', this._private__pointerCancelHandler);
30686
30782
  this._private__pointerCancelHandler = null;
@@ -30830,6 +30926,36 @@ class DrawingToolsManager {
30830
30926
  // mouse events, and those are what drive the crosshair the drag reads.
30831
30927
  this._private__disableChartInteractions();
30832
30928
  }
30929
+ /**
30930
+ * The drag reads the crosshair, which touch never moves: its move handler
30931
+ * only tries to pan, and the drag has just turned panning off. So touch is
30932
+ * driven straight from the pointer instead.
30933
+ */
30934
+ _private__handlePointerMove(event) {
30935
+ if (!this._private__isDragging || this._private__draggedToolId === null || event.pointerType === 'mouse') {
30936
+ return;
30937
+ }
30938
+ if (this._private__pointerId !== null && event.pointerId !== this._private__pointerId) {
30939
+ return;
30940
+ }
30941
+ const tool = this._private__tools.get(this._private__draggedToolId);
30942
+ if (!tool) {
30943
+ return;
30944
+ }
30945
+ const paneElement = this._private__paneElementOf(tool);
30946
+ if (paneElement === null) {
30947
+ return;
30948
+ }
30949
+ const rect = paneElement.getBoundingClientRect();
30950
+ const series = tool.series();
30951
+ const price = series.coordinateToPrice((event.clientY - rect.top));
30952
+ const time = timeFromCoordinate(this._private__chart, series, event.clientX - rect.left);
30953
+ if (price === null || time === null) {
30954
+ return;
30955
+ }
30956
+ tool.updateDrag(time, price);
30957
+ event.preventDefault();
30958
+ }
30833
30959
  _private__handlePointerUp(event) {
30834
30960
  if (!this._private__isDragging || this._private__draggedToolId === null) {
30835
30961
  return;
@@ -31003,7 +31129,7 @@ const customSeriesDefaultOptions = {
31003
31129
  * Returns the current version as a string. For example `'1.0.0'`.
31004
31130
  */
31005
31131
  function version() {
31006
- return "2.3.4";
31132
+ return "2.3.11";
31007
31133
  }
31008
31134
 
31009
31135
  export { areaSeries as AreaSeries, ArrowTool, barSeries as BarSeries, baselineSeries as BaselineSeries, BrushTool, candlestickSeries as CandlestickSeries, CircleTool, ColorType, CrossLineTool, CrosshairMode, DEFAULT_SESSIONS, DraggablePriceLine, DrawingToolsManager, EXTENDED_FIBONACCI_RATIOS, FibonacciCalculator, FibonacciExtensionTool, FibonacciFanTool, FibonacciTool, GanttTool, histogramSeries as HistogramSeries, HorizontalLineTool, InteractiveLineManager, LastPriceAnimationMode, lineSeries as LineSeries, LineStyle, LineType, MismatchDirection, ParallelChannelTool, PitchforkTool, PolygonTool, PositionCalculator, PositionLine, PositionMarkersManager, PositionTool, PriceLineSource, PriceScaleMode, RectangleTool, RulerCalculator, RulerTool, STANDARD_FIBONACCI_RATIOS, TextTool, TickMarkType, TrackingModeExitMode, TradingSessionsIndicator, TrendLineTool, VerticalLineTool, VolumeProfileIndicator, applyADX, applyATR, applyBollingerBands, applyCCI, applyDonchianChannels, applyEMA, applyHeikinAshi, applyIchimoku, applyKeltnerChannels, applyMACD, applyMFI, applyOBV, applyParabolicSAR, applyPivotPoints, applyRSI, applySMA, applyStochastic, applySuperTrend, applyVWAP, applyVolume, applyWMA, applyWilliamsR, calculateHeikinAshi, calculatePipConfig, coordinateFromTime, createArrowTool, createBrushTool, createChart, createChartEx, createCircleTool, createCrossLineTool, createFibonacciExtensionTool, createFibonacciFanTool, createFibonacciTool, createGanttTool, createHorizontalLineTool, createImageWatermark, createInteractiveLineManager, createOptionsChart, createParallelChannelTool, createPitchforkTool, createPolygonTool, createPositionLine, createPositionMarkers, createPositionTool, createRectangleTool, createRulerTool, createSeriesMarkers, createTextTool, createTextWatermark, createTradingLine, createTradingSessionsIndicator, createTrendLineTool, createUpDownMarkers, createVerticalLineTool, createVolumeProfileIndicator, createYieldCurveChart, customSeriesDefaultOptions, defaultHorzScaleBehavior, getPipConfig, hideSeriesPaint, isBusinessDay, isUTCTimestamp, setSeriesVolumes, setVolumeProfileData, timeFromCoordinate, version };