@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)
@@ -2485,14 +2485,8 @@ class SeriesBarColorer {
2485
2485
  return this._private__styleGetter(this._private__findBar, this._private__series._internal_options(), barIndex, precomputedBars);
2486
2486
  }
2487
2487
  /**
2488
- * Like `barStyle`, but returns null instead of throwing when the bar is
2489
- * gone.
2490
- *
2491
- * Recolouring walks items that were laid out against the data as it was. A
2492
- * bar that has since disappeared is not an error there: the view is about
2493
- * to be rebuilt from the new data anyway. Letting it throw took down
2494
- * whatever asked for the repaint — including `setData`, which left the
2495
- * series without its data and the pane empty until the page was reloaded.
2488
+ * Like `barStyle`, but null instead of throwing when the bar is gone, which
2489
+ * a recolour of items laid out against older data can hit.
2496
2490
  */
2497
2491
  _internal_barStyleOrNull(barIndex, precomputedBars) {
2498
2492
  if (precomputedBars === undefined && this._private__series._internal_bars()._internal_valueAt(barIndex) === null) {
@@ -2894,7 +2888,10 @@ class SeriesPrimitivePriceAxisViewWrapper extends PriceAxisView {
2894
2888
  commonRendererData._internal_additionalPaddingTop = additionalPadding;
2895
2889
  commonRendererData._internal_additionalPaddingBottom = additionalPadding;
2896
2890
  commonRendererData._internal_coordinate = data._internal_coordinate;
2897
- commonRendererData._internal_fixedCoordinate = data._internal_fixedCoordinate;
2891
+ // Re-runs at draw time, after the axis has moved the label clear of overlaps.
2892
+ if (data._internal_fixedCoordinate !== undefined) {
2893
+ commonRendererData._internal_fixedCoordinate = data._internal_fixedCoordinate;
2894
+ }
2898
2895
  axisRendererData._internal_text = data._internal_text;
2899
2896
  axisRendererData._internal_visible = data._internal_visible;
2900
2897
  axisRendererData._internal_tickVisible = data._internal_tickVisible;
@@ -3771,7 +3768,6 @@ class PriceTickSpanCalculator {
3771
3768
  }
3772
3769
  }
3773
3770
 
3774
- const TICK_DENSITY = 2.5;
3775
3771
  class PriceTickMarkBuilder {
3776
3772
  constructor(priceScale, base, coordinateToLogicalFunc, logicalToCoordinateFunc) {
3777
3773
  this._private__marks = [];
@@ -3832,7 +3828,7 @@ class PriceTickMarkBuilder {
3832
3828
  return this._private__priceScale._internal_fontSize();
3833
3829
  }
3834
3830
  _private__tickMarkHeight() {
3835
- return Math.ceil(this._private__fontHeight() * TICK_DENSITY);
3831
+ return Math.max(1, Math.ceil(this._private__fontHeight() * this._private__priceScale._internal_options().tickMarkSpacing));
3836
3832
  }
3837
3833
  _private__updateMarks(firstValue, span, high, low, minCoord, maxCoord) {
3838
3834
  const marks = this._private__marks;
@@ -3997,6 +3993,9 @@ class PriceScale {
3997
3993
  this._private__invalidateInternalHeightCache();
3998
3994
  this._private__marksCache = null;
3999
3995
  }
3996
+ if (options.tickMarkSpacing !== undefined) {
3997
+ this._private__marksCache = null;
3998
+ }
4000
3999
  }
4001
4000
  _internal_isAutoScale() {
4002
4001
  return this._private__options.autoScale;
@@ -5174,6 +5173,9 @@ class TickMarks {
5174
5173
  this._private__uniformDistribution = val;
5175
5174
  this._private__cache = null;
5176
5175
  }
5176
+ _internal_invalidate() {
5177
+ this._private__cache = null;
5178
+ }
5177
5179
  _internal_setTimeScalePoints(newPoints, firstChangedPointIndex) {
5178
5180
  this._private__removeMarksSinceIndex(firstChangedPointIndex);
5179
5181
  this._private__cache = null;
@@ -5192,7 +5194,7 @@ class TickMarks {
5192
5194
  });
5193
5195
  }
5194
5196
  }
5195
- _internal_build(spacing, maxWidth, checkIndicesForData, indicesWithDataMap, indicesWithDataId) {
5197
+ _internal_build(spacing, maxWidth, checkIndicesForData, indicesWithDataMap, indicesWithDataId, allows = () => true) {
5196
5198
  const maxIndexesPerMark = Math.ceil(maxWidth / spacing);
5197
5199
  if (this._private__cache === null ||
5198
5200
  this._private__cache._internal_maxIndexesPerMark !== maxIndexesPerMark ||
@@ -5201,7 +5203,7 @@ class TickMarks {
5201
5203
  this._private__cache = {
5202
5204
  _internal_indicesWithDataId: indicesWithDataId,
5203
5205
  _internal_checkIndicesForData: checkIndicesForData,
5204
- _internal_marks: this._private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap),
5206
+ _internal_marks: this._private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap, allows),
5205
5207
  _internal_maxIndexesPerMark: maxIndexesPerMark,
5206
5208
  };
5207
5209
  }
@@ -5225,7 +5227,7 @@ class TickMarks {
5225
5227
  this._private__marksByWeight.delete(weight);
5226
5228
  }
5227
5229
  }
5228
- _private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap) {
5230
+ _private__buildMarksImpl(maxIndexesPerMark, checkIndicesForData, indicesWithDataMap, allows) {
5229
5231
  let marks = [];
5230
5232
  const canBeIncluded = (mark) => !checkIndicesForData || indicesWithDataMap.has(mark.index);
5231
5233
  for (const weight of Array.from(this._private__marksByWeight.keys()).sort((a, b) => b - a)) {
@@ -5243,6 +5245,11 @@ class TickMarks {
5243
5245
  let leftIndex = -Infinity;
5244
5246
  for (let i = 0; i < currentWeightLength; i++) {
5245
5247
  const mark = currentWeight[i];
5248
+ // Skipped before the fit check: a rejected mark must not count as one
5249
+ // that did not fit, which with uniform distribution drops its whole weight.
5250
+ if (!allows(mark, maxIndexesPerMark)) {
5251
+ continue;
5252
+ }
5246
5253
  const currentIndex = mark.index;
5247
5254
  // Determine indexes with which current index will be compared
5248
5255
  // All marks to the right is moved to new array
@@ -5549,7 +5556,7 @@ class TimeScale {
5549
5556
  const visibleBars = ensureNotNull(this._internal_visibleStrictRange());
5550
5557
  const firstBar = Math.max(visibleBars._internal_left(), visibleBars._internal_left() - indexPerLabel);
5551
5558
  const lastBar = Math.max(visibleBars._internal_right(), visibleBars._internal_right() - indexPerLabel);
5552
- const items = this._private__tickMarks._internal_build(spacing, maxLabelWidth, this._private__options.ignoreWhitespaceIndices, this._private__indicesWithData, this._private__indicesWithDataUpdateId);
5559
+ 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);
5553
5560
  // according to indexPerLabel value this value means "earliest index which _might be_ used as the second label on time scale"
5554
5561
  const earliestIndexOfSecondLabel = this._private__firstIndex() + indexPerLabel;
5555
5562
  // according to indexPerLabel value this value means "earliest index which _might be_ used as the second last label on time scale"
@@ -5927,6 +5934,7 @@ class TimeScale {
5927
5934
  _private__invalidateTickMarks() {
5928
5935
  this._private__resetTimeMarksCache();
5929
5936
  this._private__formattedByWeight.clear();
5937
+ this._private__tickMarks._internal_invalidate();
5930
5938
  }
5931
5939
  _private__updateDateTimeFormatter() {
5932
5940
  this._private__horzScaleBehavior.updateFormatter(this._private__localizationOptions);
@@ -7041,6 +7049,14 @@ class HorzScaleBehaviorTime {
7041
7049
  const tp = item;
7042
7050
  return this._private__dateTimeFormatter._internal_format(new Date(tp._internal_timestamp * 1000));
7043
7051
  }
7052
+ allowsTickMark(tickMark, barsPerMark) {
7053
+ const filter = this._private__options.timeScale.tickMarkFilter;
7054
+ if (filter === undefined) {
7055
+ return true;
7056
+ }
7057
+ const tickMarkType = weightToTickMarkType(tickMark.weight, this._private__options.timeScale.timeVisible, this._private__options.timeScale.secondsVisible);
7058
+ return filter(tickMark.originalTime, tickMarkType, barsPerMark);
7059
+ }
7044
7060
  formatTickmark(tickMark, localizationOptions) {
7045
7061
  const tickMarkType = weightToTickMarkType(tickMark.weight, this._private__options.timeScale.timeVisible, this._private__options.timeScale.secondsVisible);
7046
7062
  const options = this._private__options.timeScale;
@@ -11050,9 +11066,6 @@ class SeriesPaneViewBase {
11050
11066
  _internal__updateOptions() {
11051
11067
  const colorer = this._internal__series._internal_barColorer();
11052
11068
  this._internal__items = this._internal__items.map((item) => {
11053
- // An item whose bar is gone keeps the colour it had. Throwing here
11054
- // took down whatever asked for the repaint — `setData` among them,
11055
- // which left the series without its data and the pane empty.
11056
11069
  const style = colorer._internal_barStyleOrNull(item._internal_time);
11057
11070
  return style === null ? item : { ...item, ...style };
11058
11071
  });
@@ -11355,6 +11368,7 @@ const priceScaleOptionsDefaults = {
11355
11368
  },
11356
11369
  minimumWidth: 0,
11357
11370
  ensureEdgeTickMarksVisible: false,
11371
+ tickMarkSpacing: 2.5,
11358
11372
  };
11359
11373
 
11360
11374
  const timeScaleOptionsDefaults = {
@@ -12450,8 +12464,6 @@ class LinePaneViewBase extends SeriesPaneViewBase {
12450
12464
  const colorer = this._internal__series._internal_barColorer();
12451
12465
  this._internal__items = this._internal__series._internal_bars()._internal_rows().map((row) => {
12452
12466
  const value = row._internal_value[3 /* PlotRowValueIndex.Close */];
12453
- // The row travels with the point: without it the colorer looks it up
12454
- // again by index, and that lookup could fail and take the repaint down.
12455
12467
  return this._internal__createRawItem(row._internal_index, value, colorer, row);
12456
12468
  });
12457
12469
  }
@@ -12474,10 +12486,10 @@ styleGetter) {
12474
12486
  if (point) {
12475
12487
  const style = styleGetter(renderingScope, point);
12476
12488
  if (style !== prevStyle) {
12477
- context.beginPath();
12478
12489
  if (prevStyle !== null) {
12479
12490
  context.fill();
12480
12491
  }
12492
+ context.beginPath();
12481
12493
  context.fillStyle = style;
12482
12494
  prevStyle = style;
12483
12495
  }
@@ -13416,9 +13428,6 @@ class BarsPaneViewBase extends SeriesPaneViewBase {
13416
13428
  }
13417
13429
  _internal__fillRawPoints() {
13418
13430
  const colorer = this._internal__series._internal_barColorer();
13419
- // The row goes to the colorer as it is. Asking for the style by index
13420
- // alone made it look the row up again, and that second lookup could come
13421
- // back empty and throw, taking the whole repaint down with it.
13422
13431
  this._internal__items = this._internal__series._internal_bars()._internal_rows().map((row) => this._internal__createRawItem(row._internal_index, row, colorer));
13423
13432
  }
13424
13433
  }
@@ -17489,7 +17498,7 @@ function applyHeikinAshi(series, chart, options = {}) {
17489
17498
  hideSource: options.hideSource ?? true,
17490
17499
  };
17491
17500
  const sourceOptions = series.options();
17492
- // Same price scale and the same formatting as the source: the candles are a
17501
+ // Same price scale, formatting and axis labels as the source: the candles are a
17493
17502
  // restatement of that series, not a second instrument, so they must not
17494
17503
  // autoscale on their own axis or round to a different precision.
17495
17504
  const haSeries = chart.addSeries(candlestickSeries, {
@@ -17503,6 +17512,8 @@ function applyHeikinAshi(series, chart, options = {}) {
17503
17512
  wickDownColor: resolved.wickDownColor,
17504
17513
  priceScaleId: sourceOptions.priceScaleId,
17505
17514
  priceFormat: sourceOptions.priceFormat,
17515
+ lastValueVisible: sourceOptions.lastValueVisible,
17516
+ priceLineVisible: sourceOptions.priceLineVisible,
17506
17517
  }, resolved.paneIndex);
17507
17518
  let restoreSourcePaint = null;
17508
17519
  const blankSource = (blank) => {
@@ -17704,12 +17715,14 @@ function applyMACD(series, chart, options = {}) {
17704
17715
  const macdSeries = chart.addSeries(lineSeries, {
17705
17716
  color: resolved.colorMACD,
17706
17717
  }, paneIndex);
17718
+ // An index past the last pane opens a new one, so the rest follow MACD to where it landed.
17719
+ const macdPane = paneIndexOf(chart, macdSeries, paneIndex);
17707
17720
  const signalSeries = chart.addSeries(lineSeries, {
17708
17721
  color: resolved.colorSignal,
17709
- }, paneIndex);
17722
+ }, macdPane);
17710
17723
  const histogramSeries$1 = chart.addSeries(histogramSeries, {
17711
17724
  color: resolved.colorHistogramUp,
17712
- }, paneIndex);
17725
+ }, macdPane);
17713
17726
  // Calculate EMA
17714
17727
  const calculateEMA = (prices, period) => {
17715
17728
  const ema = [];
@@ -17841,13 +17854,13 @@ function applyStochastic(series, chart, options = {}) {
17841
17854
  // Stochastic is bounded to 0-100; pinning it keeps the pane readable
17842
17855
  autoscaleInfoProvider: boundedScale,
17843
17856
  }, paneIndex);
17857
+ // An index past the last pane opens a new one, so %D follows %K to where it landed.
17858
+ const scalePane = paneIndexOf(chart, kSeries, paneIndex);
17844
17859
  // Create %D line (slow - signal)
17845
17860
  const dSeries = chart.addSeries(lineSeries, {
17846
17861
  color: resolved.colorD,
17847
17862
  autoscaleInfoProvider: boundedScale,
17848
- }, paneIndex);
17849
- // The pane the series landed in, which is not always the one asked for.
17850
- const scalePane = paneIndexOf(chart, kSeries, paneIndex);
17863
+ }, scalePane);
17851
17864
  chart.priceScale(kSeries.options().priceScaleId ?? 'right', scalePane).applyOptions({
17852
17865
  scaleMargins: {
17853
17866
  top: 0.1,
@@ -18189,15 +18202,16 @@ function applyADX(series, chart, options = {}) {
18189
18202
  color: resolved.colorADX,
18190
18203
  autoscaleInfoProvider: boundedScale,
18191
18204
  }, paneIndex);
18205
+ // An index past the last pane opens a new one, so the DI lines follow ADX to where it landed.
18206
+ const scalePane = paneIndexOf(chart, adxSeries, paneIndex);
18192
18207
  const plusSeries = chart.addSeries(lineSeries, {
18193
18208
  color: resolved.colorPlusDI,
18194
18209
  autoscaleInfoProvider: boundedScale,
18195
- }, paneIndex);
18210
+ }, scalePane);
18196
18211
  const minusSeries = chart.addSeries(lineSeries, {
18197
18212
  color: resolved.colorMinusDI,
18198
18213
  autoscaleInfoProvider: boundedScale,
18199
- }, paneIndex);
18200
- const scalePane = paneIndexOf(chart, adxSeries, paneIndex);
18214
+ }, scalePane);
18201
18215
  chart.priceScale(adxSeries.options().priceScaleId ?? 'right', scalePane).applyOptions({
18202
18216
  scaleMargins: {
18203
18217
  top: 0.1,
@@ -19313,6 +19327,8 @@ class TradingLineController {
19313
19327
  this._private__pointerId = null;
19314
19328
  this._private__pressOrigin = null;
19315
19329
  this._private__pressedLine = null;
19330
+ /** Whether the gesture in flight came from a finger rather than a mouse. */
19331
+ this._private__pressWasTouch = false;
19316
19332
  /** Distance from the pointer to the dragged line when the press landed. */
19317
19333
  this._private__grabOffset = 0;
19318
19334
  /** Latest pointer position in pane coordinates, or null when off the panes. */
@@ -19377,8 +19393,18 @@ class TradingLineController {
19377
19393
  this._private__applyCursor();
19378
19394
  }
19379
19395
  _private__onPointerDown(event) {
19380
- const line = this._private__hoveredId === null ? null : this._private__lines.get(this._private__hoveredId) ?? null;
19396
+ // Hover belongs to the mouse. Touch leaves whatever the last press marked
19397
+ // still marked, so trusting it here grabbed that line again wherever the
19398
+ // next tap landed: every press resolves itself instead.
19399
+ this._private__pressWasTouch = event.pointerType !== 'mouse';
19400
+ let line = this._private__pressWasTouch || this._private__hoveredId === null
19401
+ ? null
19402
+ : this._private__lines.get(this._private__hoveredId) ?? null;
19403
+ if (line === null) {
19404
+ line = this._private__resolveHitAt(event);
19405
+ }
19381
19406
  if (!line) {
19407
+ this._private__clearTouchHover();
19382
19408
  return;
19383
19409
  }
19384
19410
  this._private__pressOrigin = { _internal_x: event.clientX, _internal_y: event.clientY };
@@ -19417,6 +19443,7 @@ class TradingLineController {
19417
19443
  }
19418
19444
  _private__onPointerUp(event) {
19419
19445
  const dragged = this._private__dragging;
19446
+ const wasTouch = this._private__pressWasTouch;
19420
19447
  const pressed = this._private__pressedLine;
19421
19448
  const origin = this._private__pressOrigin;
19422
19449
  this._private__releasePointer();
@@ -19438,12 +19465,24 @@ class TradingLineController {
19438
19465
  dragged.click();
19439
19466
  }
19440
19467
  }
19468
+ this._private__clearTouchHover(wasTouch);
19441
19469
  this._private__applyCursor();
19442
19470
  return;
19443
19471
  }
19444
19472
  if (pressed !== null && !moved && pressed.isClickable()) {
19445
19473
  pressed.click();
19446
19474
  }
19475
+ this._private__clearTouchHover(wasTouch);
19476
+ }
19477
+ /** A finger leaves nothing hovered behind it, the way lifting a mouse does. */
19478
+ _private__clearTouchHover(wasTouch = this._private__pressWasTouch) {
19479
+ if (!wasTouch || this._private__hoveredId === null) {
19480
+ return;
19481
+ }
19482
+ const line = this._private__lines.get(this._private__hoveredId) ?? null;
19483
+ this._private__setHovered(null);
19484
+ this._private__setHoveredPart(line, null);
19485
+ this._private__applyCursor();
19447
19486
  }
19448
19487
  /**
19449
19488
  * Abort an in-flight gesture: the line goes back to where it was and no
@@ -19489,9 +19528,38 @@ class TradingLineController {
19489
19528
  return paneY === null ? null : line.priceAt(paneY - this._private__grabOffset);
19490
19529
  }
19491
19530
  /** The pointer's position inside the line's pane, in pane coordinates. */
19492
- _private__paneY(line, event) {
19531
+ /**
19532
+ * The hovered id comes from the crosshair, which touch never moves before it
19533
+ * presses, so the press resolves the target itself.
19534
+ */
19535
+ _private__resolveHitAt(event) {
19536
+ for (const line of this._private__lines.values()) {
19537
+ if (!line.hitTestAt) {
19538
+ continue;
19539
+ }
19540
+ const paneElement = this._private__paneElementOf(line);
19541
+ if (paneElement === null) {
19542
+ continue;
19543
+ }
19544
+ const rect = paneElement.getBoundingClientRect();
19545
+ // A fingertip is far coarser than a pointer, so its targets are widened.
19546
+ const slop = event.pointerType === 'mouse' ? 0 : 12 /* Constants.TouchSlopPx */;
19547
+ const hit = this._private__splitHit(line.hitTestAt(event.clientX - rect.left, event.clientY - rect.top, slop));
19548
+ if (hit === null) {
19549
+ continue;
19550
+ }
19551
+ this._private__setHovered(line.id());
19552
+ this._private__setHoveredPart(line, hit._internal_part);
19553
+ return line;
19554
+ }
19555
+ return null;
19556
+ }
19557
+ _private__paneElementOf(line) {
19493
19558
  const pane = this._private__chart.panes()[line.paneIndex()];
19494
- const paneElement = pane ? pane.getHTMLElement() : null;
19559
+ return pane ? pane.getHTMLElement() : null;
19560
+ }
19561
+ _private__paneY(line, event) {
19562
+ const paneElement = this._private__paneElementOf(line);
19495
19563
  if (paneElement) {
19496
19564
  return event.clientY - paneElement.getBoundingClientRect().top;
19497
19565
  }
@@ -19608,6 +19676,7 @@ let nextLineId = 0;
19608
19676
  * line. Hit testing comes from the engine — the line's id travels as the price
19609
19677
  * line's `externalId` — so the hit area matches what is drawn.
19610
19678
  */
19679
+ const HIT_TOLERANCE_PX$1 = 8;
19611
19680
  class DraggablePriceLine {
19612
19681
  constructor(chart, series, options) {
19613
19682
  this._private__priceLine = null;
@@ -19660,6 +19729,14 @@ class DraggablePriceLine {
19660
19729
  color: hovered ? this._private__options.hoverColor : this._private__originalColor,
19661
19730
  });
19662
19731
  }
19732
+ /**
19733
+ * The engine draws this one, so there is no pane view to ask: a press within
19734
+ * the line's own grab strip counts as a hit.
19735
+ */
19736
+ hitTestAt(x, y, slop = 0) {
19737
+ const coord = this._private__series.priceToCoordinate(this._private__options.price);
19738
+ return coord !== null && Math.abs(y - coord) <= HIT_TOLERANCE_PX$1 + slop ? this._private__id : null;
19739
+ }
19663
19740
  priceAt(paneY) {
19664
19741
  const price = this._private__series.coordinateToPrice(paneY);
19665
19742
  return price === null ? null : price;
@@ -20202,22 +20279,27 @@ class PositionLineRenderer {
20202
20279
  _internal_setData(data) {
20203
20280
  this._private__data = data;
20204
20281
  }
20205
- _internal_hitTest(x, y) {
20282
+ /**
20283
+ * `slop` widens every target by the same amount, for input coarser than a
20284
+ * mouse pointer.
20285
+ */
20286
+ _internal_hitTest(x, y, slop = 0) {
20206
20287
  // The close button sits on top of the label, which sits on top of the
20207
20288
  // line, so the narrowest target is tested first.
20208
20289
  for (const layout of this._private__layouts) {
20209
- if (layout._internal_closable && x >= layout._internal_closeLeft && x <= layout._internal_closeRight &&
20210
- Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2) {
20290
+ if (layout._internal_closable && x >= layout._internal_closeLeft - slop && x <= layout._internal_closeRight + slop &&
20291
+ Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2 + slop) {
20211
20292
  return { _internal_level: layout._internal_level, _internal_part: 'close' };
20212
20293
  }
20213
20294
  }
20214
20295
  for (const layout of this._private__layouts) {
20215
- if (x >= layout._internal_pillLeft && x <= layout._internal_pillRight && Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2) {
20296
+ if (x >= layout._internal_pillLeft - slop && x <= layout._internal_pillRight + slop &&
20297
+ Math.abs(y - layout._internal_labelY) <= layout._internal_height / 2 + slop) {
20216
20298
  return { _internal_level: layout._internal_level, _internal_part: 'body' };
20217
20299
  }
20218
20300
  }
20219
20301
  for (const layout of this._private__layouts) {
20220
- if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */) {
20302
+ if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */ + slop) {
20221
20303
  return { _internal_level: layout._internal_level, _internal_part: 'line' };
20222
20304
  }
20223
20305
  }
@@ -20454,8 +20536,8 @@ class PositionLinePaneView {
20454
20536
  zOrder() {
20455
20537
  return 'top';
20456
20538
  }
20457
- _internal_hitTest(x, y) {
20458
- return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y);
20539
+ _internal_hitTest(x, y, slop = 0) {
20540
+ return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y, slop);
20459
20541
  }
20460
20542
  }
20461
20543
 
@@ -20606,6 +20688,13 @@ class PositionLine {
20606
20688
  * of jumping to sit under it — which matters most when the label has been
20607
20689
  * fanned away from its own line.
20608
20690
  */
20691
+ hitTestAt(x, y, slop = 0) {
20692
+ const hit = this._private__paneView._internal_hitTest(x, y, slop);
20693
+ if (hit === null) {
20694
+ return null;
20695
+ }
20696
+ return this._private__levelOptions(hit._internal_level) === null ? null : `${this._private__id}#${hit._internal_level}:${hit._internal_part}`;
20697
+ }
20609
20698
  dragAnchorY() {
20610
20699
  if (this._private__dragLevel === null) {
20611
20700
  return null;
@@ -29676,6 +29765,7 @@ class DrawingToolsManager {
29676
29765
  this._private__mouseMoveHandler = null;
29677
29766
  this._private__pointerDownHandler = null;
29678
29767
  this._private__pointerUpHandler = null;
29768
+ this._private__pointerMoveHandler = null;
29679
29769
  this._private__pointerCancelHandler = null;
29680
29770
  this._private__lostCaptureHandler = null;
29681
29771
  this._private__windowPointerUpHandler = null;
@@ -30250,10 +30340,12 @@ class DrawingToolsManager {
30250
30340
  if (chartElement) {
30251
30341
  this._private__pointerDownHandler = (event) => this._private__handlePointerDown(event);
30252
30342
  this._private__pointerUpHandler = (event) => this._private__handlePointerUp(event);
30343
+ this._private__pointerMoveHandler = (event) => this._private__handlePointerMove(event);
30253
30344
  this._private__pointerCancelHandler = (event) => this._private__handlePointerUp(event);
30254
30345
  this._private__lostCaptureHandler = (event) => this._private__handleLostPointerCapture(event);
30255
30346
  chartElement.addEventListener('pointerdown', this._private__pointerDownHandler);
30256
30347
  chartElement.addEventListener('pointerup', this._private__pointerUpHandler);
30348
+ chartElement.addEventListener('pointermove', this._private__pointerMoveHandler);
30257
30349
  chartElement.addEventListener('pointercancel', this._private__pointerCancelHandler);
30258
30350
  chartElement.addEventListener('lostpointercapture', this._private__lostCaptureHandler);
30259
30351
  }
@@ -30288,6 +30380,10 @@ class DrawingToolsManager {
30288
30380
  chartElement.removeEventListener('pointerup', this._private__pointerUpHandler);
30289
30381
  this._private__pointerUpHandler = null;
30290
30382
  }
30383
+ if (this._private__pointerMoveHandler) {
30384
+ chartElement.removeEventListener('pointermove', this._private__pointerMoveHandler);
30385
+ this._private__pointerMoveHandler = null;
30386
+ }
30291
30387
  if (this._private__pointerCancelHandler) {
30292
30388
  chartElement.removeEventListener('pointercancel', this._private__pointerCancelHandler);
30293
30389
  this._private__pointerCancelHandler = null;
@@ -30437,6 +30533,36 @@ class DrawingToolsManager {
30437
30533
  // mouse events, and those are what drive the crosshair the drag reads.
30438
30534
  this._private__disableChartInteractions();
30439
30535
  }
30536
+ /**
30537
+ * The drag reads the crosshair, which touch never moves: its move handler
30538
+ * only tries to pan, and the drag has just turned panning off. So touch is
30539
+ * driven straight from the pointer instead.
30540
+ */
30541
+ _private__handlePointerMove(event) {
30542
+ if (!this._private__isDragging || this._private__draggedToolId === null || event.pointerType === 'mouse') {
30543
+ return;
30544
+ }
30545
+ if (this._private__pointerId !== null && event.pointerId !== this._private__pointerId) {
30546
+ return;
30547
+ }
30548
+ const tool = this._private__tools.get(this._private__draggedToolId);
30549
+ if (!tool) {
30550
+ return;
30551
+ }
30552
+ const paneElement = this._private__paneElementOf(tool);
30553
+ if (paneElement === null) {
30554
+ return;
30555
+ }
30556
+ const rect = paneElement.getBoundingClientRect();
30557
+ const series = tool.series();
30558
+ const price = series.coordinateToPrice((event.clientY - rect.top));
30559
+ const time = timeFromCoordinate(this._private__chart, series, event.clientX - rect.left);
30560
+ if (price === null || time === null) {
30561
+ return;
30562
+ }
30563
+ tool.updateDrag(time, price);
30564
+ event.preventDefault();
30565
+ }
30440
30566
  _private__handlePointerUp(event) {
30441
30567
  if (!this._private__isDragging || this._private__draggedToolId === null) {
30442
30568
  return;
@@ -30610,7 +30736,7 @@ const customSeriesDefaultOptions = {
30610
30736
  * Returns the current version as a string. For example `'1.0.0'`.
30611
30737
  */
30612
30738
  function version() {
30613
- return "2.3.4";
30739
+ return "2.3.11";
30614
30740
  }
30615
30741
 
30616
30742
  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 };