@pipsend/charts 1.2.0 → 1.2.2

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 v1.2.0
3
+ * Pipsend Charts v1.2.1
4
4
  * Copyright (c) 2026 Pipsend
5
5
  * Licensed under MIT License
6
6
  * Built on TradingView Lightweight Charts™ (Apache 2.0)
@@ -17047,8 +17047,9 @@ function setVolumeProfileData(indicator, data) {
17047
17047
  indicator.setVolumeData(volumeMap);
17048
17048
  }
17049
17049
 
17050
+ const CLICK_TOLERANCE_PX = 4;
17050
17051
  /**
17051
- * Draggable Price Line - Línea de precio que se puede arrastrar con el mouse
17052
+ * Draggable Price Line - Línea de precio que se puede arrastrar y/o hacer click
17052
17053
  */
17053
17054
  class DraggablePriceLine {
17054
17055
  constructor(chart, series, options) {
@@ -17058,6 +17059,8 @@ class DraggablePriceLine {
17058
17059
  this._private__unsubscribeHandlers = [];
17059
17060
  this._private__isHovering = false;
17060
17061
  this._private__originalColor = '';
17062
+ this._private__mouseDownPos = null;
17063
+ this._private__mouseDownOnLine = false;
17061
17064
  this._private__chart = chart;
17062
17065
  this._private__series = series;
17063
17066
  this._private__options = {
@@ -17068,10 +17071,25 @@ class DraggablePriceLine {
17068
17071
  ...options,
17069
17072
  };
17070
17073
  this._private__createPriceLine();
17071
- if (this._private__options.draggable) {
17072
- this._private__setupDragHandlers();
17074
+ // Set up handlers if line is interactive (draggable or clickable)
17075
+ if (this._private__options.draggable || this._private__options.onClick) {
17076
+ this._private__setupInteractionHandlers();
17073
17077
  }
17074
17078
  }
17079
+ /**
17080
+ * Resolve the effective title: formatTitle(price) wins over the static title.
17081
+ */
17082
+ _private__resolveTitle() {
17083
+ if (this._private__options.formatTitle) {
17084
+ try {
17085
+ return this._private__options.formatTitle(this._private__options.price);
17086
+ }
17087
+ catch (error) {
17088
+ console.error('Error in formatTitle:', error);
17089
+ }
17090
+ }
17091
+ return this._private__options.title || '';
17092
+ }
17075
17093
  _private__createPriceLine() {
17076
17094
  this._private__originalColor = this._private__options.color;
17077
17095
  this._private__priceLine = this._private__series.createPriceLine({
@@ -17080,29 +17098,36 @@ class DraggablePriceLine {
17080
17098
  lineWidth: this._private__options.lineWidth,
17081
17099
  lineStyle: this._private__options.lineStyle,
17082
17100
  axisLabelVisible: this._private__options.axisLabelVisible,
17083
- title: this._private__options.title || '',
17101
+ title: this._private__resolveTitle(),
17084
17102
  });
17085
17103
  }
17086
- _private__setupDragHandlers() {
17104
+ _private__setupInteractionHandlers() {
17087
17105
  const chartElement = this._private__chart.chartElement?.() || document.querySelector('.tv-lightweight-charts');
17088
17106
  if (!chartElement)
17089
17107
  return;
17090
17108
  this._private__container = chartElement;
17091
17109
  const handleMouseDown = (e) => {
17092
- // Solo permitir drag desde el área del gráfico (no desde el price axis)
17110
+ // Solo permitir interacción desde el área del gráfico (no desde el price axis)
17093
17111
  if (!this._private__isInChartArea(e)) {
17094
17112
  return;
17095
17113
  }
17096
17114
  const price = this._private__getPriceFromY(e.clientY);
17097
- if (this._private__isOnLine(e, price)) {
17115
+ if (!this._private__isOnLine(e, price)) {
17116
+ return;
17117
+ }
17118
+ // Track press for click vs drag detection
17119
+ this._private__mouseDownPos = { _internal_x: e.clientX, _internal_y: e.clientY };
17120
+ this._private__mouseDownOnLine = true;
17121
+ // Only start drag if line is draggable
17122
+ if (this._private__options.draggable) {
17098
17123
  this._private__isDragging = true;
17099
17124
  this._private__container.style.cursor = 'ns-resize';
17100
17125
  if (this._private__options.onDragStart) {
17101
17126
  this._private__options.onDragStart(this._private__options.price);
17102
17127
  }
17103
- e.preventDefault();
17104
- e.stopPropagation();
17105
17128
  }
17129
+ e.preventDefault();
17130
+ e.stopPropagation();
17106
17131
  };
17107
17132
  const handleMouseMove = (e) => {
17108
17133
  if (this._private__isDragging) {
@@ -17114,32 +17139,65 @@ class DraggablePriceLine {
17114
17139
  // Prevenir que el gráfico se mueva mientras arrastramos la línea
17115
17140
  e.preventDefault();
17116
17141
  e.stopPropagation();
17142
+ return;
17117
17143
  }
17118
- else {
17119
- // Solo mostrar hover si está en el área del gráfico
17120
- if (!this._private__isInChartArea(e)) {
17121
- this._private__setHoverState(false);
17122
- return;
17123
- }
17124
- const price = this._private__getPriceFromY(e.clientY);
17125
- if (this._private__isOnLine(e, price)) {
17126
- this._private__container.style.cursor = 'ns-resize';
17127
- this._private__setHoverState(true);
17128
- }
17129
- else {
17144
+ // Hover state — only mutate cursor for transitions involving THIS line.
17145
+ // Multiple lines can share the same chart element listener (one per line).
17146
+ // Blindly resetting the cursor here would clobber another line's drag/hover.
17147
+ if (!this._private__isInChartArea(e)) {
17148
+ if (this._private__isHovering) {
17130
17149
  this._private__container.style.cursor = 'default';
17131
17150
  this._private__setHoverState(false);
17132
17151
  }
17152
+ return;
17153
+ }
17154
+ const price = this._private__getPriceFromY(e.clientY);
17155
+ const onThisLine = this._private__isOnLine(e, price);
17156
+ if (onThisLine) {
17157
+ this._private__container.style.cursor = this._private__options.draggable ? 'ns-resize' : 'pointer';
17158
+ this._private__setHoverState(true);
17159
+ }
17160
+ else if (this._private__isHovering) {
17161
+ // Only revert when actually leaving — don't override another line's cursor.
17162
+ this._private__container.style.cursor = 'default';
17163
+ this._private__setHoverState(false);
17133
17164
  }
17134
17165
  };
17135
17166
  const handleMouseUp = (e) => {
17136
- if (this._private__isDragging) {
17137
- this._private__isDragging = false;
17167
+ const wasDragging = this._private__isDragging;
17168
+ const downOnLine = this._private__mouseDownOnLine;
17169
+ const downPos = this._private__mouseDownPos;
17170
+ // Reset state regardless of outcome
17171
+ this._private__isDragging = false;
17172
+ this._private__mouseDownOnLine = false;
17173
+ this._private__mouseDownPos = null;
17174
+ if (wasDragging) {
17175
+ // Drag-end path
17138
17176
  this._private__container.style.cursor = 'default';
17139
17177
  this._private__setHoverState(false);
17140
- if (this._private__options.onDragEnd) {
17178
+ const movedFromDown = downPos &&
17179
+ (Math.abs(e.clientX - downPos._internal_x) > CLICK_TOLERANCE_PX ||
17180
+ Math.abs(e.clientY - downPos._internal_y) > CLICK_TOLERANCE_PX);
17181
+ // If barely moved, treat as click instead of drag
17182
+ if (!movedFromDown && this._private__options.onClick) {
17183
+ this._private__options.onClick(this._private__options.price);
17184
+ }
17185
+ else if (this._private__options.onDragEnd) {
17141
17186
  this._private__options.onDragEnd(this._private__options.price);
17142
17187
  }
17188
+ return;
17189
+ }
17190
+ // Pure click path (non-draggable line or draggable that didn't start drag)
17191
+ if (downOnLine && downPos && this._private__options.onClick) {
17192
+ const moved = Math.abs(e.clientX - downPos._internal_x) > CLICK_TOLERANCE_PX ||
17193
+ Math.abs(e.clientY - downPos._internal_y) > CLICK_TOLERANCE_PX;
17194
+ if (!moved) {
17195
+ // Confirm release was still on the line area
17196
+ const price = this._private__getPriceFromY(e.clientY);
17197
+ if (this._private__isOnLine(e, price)) {
17198
+ this._private__options.onClick(this._private__options.price);
17199
+ }
17200
+ }
17143
17201
  }
17144
17202
  };
17145
17203
  const handleMouseLeave = (e) => {
@@ -17150,9 +17208,12 @@ class DraggablePriceLine {
17150
17208
  this._private__options.onDragEnd(this._private__options.price);
17151
17209
  }
17152
17210
  }
17153
- else {
17211
+ else if (this._private__isHovering) {
17212
+ this._private__container.style.cursor = 'default';
17154
17213
  this._private__setHoverState(false);
17155
17214
  }
17215
+ this._private__mouseDownOnLine = false;
17216
+ this._private__mouseDownPos = null;
17156
17217
  };
17157
17218
  this._private__container.addEventListener('mousedown', handleMouseDown);
17158
17219
  this._private__container.addEventListener('mousemove', handleMouseMove);
@@ -17246,11 +17307,16 @@ class DraggablePriceLine {
17246
17307
  _private__updatePrice(newPrice) {
17247
17308
  this._private__options.price = newPrice;
17248
17309
  if (this._private__priceLine) {
17249
- this._private__priceLine.applyOptions({ price: newPrice });
17310
+ // Re-resolve title so formatTitle reflects the new price.
17311
+ this._private__priceLine.applyOptions({
17312
+ price: newPrice,
17313
+ title: this._private__resolveTitle(),
17314
+ });
17250
17315
  }
17251
17316
  }
17252
17317
  /**
17253
- * Update the price line options
17318
+ * Update the price line options. Any subset of options can be passed.
17319
+ * Title is recomputed from `formatTitle` if available, otherwise from `title`.
17254
17320
  */
17255
17321
  applyOptions(options) {
17256
17322
  this._private__options = { ...this._private__options, ...options };
@@ -17258,14 +17324,36 @@ class DraggablePriceLine {
17258
17324
  if (options.color) {
17259
17325
  this._private__originalColor = options.color;
17260
17326
  }
17261
- if (this._private__priceLine && options.price !== undefined) {
17262
- this._private__priceLine.applyOptions({
17263
- price: options.price,
17264
- color: options.color,
17265
- lineWidth: options.lineWidth,
17266
- lineStyle: options.lineStyle,
17267
- title: options.title,
17268
- });
17327
+ if (!this._private__priceLine)
17328
+ return;
17329
+ // Apply visual options. Any field not in `options` is left untouched on the
17330
+ // underlying price line, except `title` which we always re-resolve so that
17331
+ // formatTitle stays in sync with whatever changed.
17332
+ const patch = {
17333
+ title: this._private__resolveTitle(),
17334
+ };
17335
+ if (options.price !== undefined)
17336
+ patch.price = options.price;
17337
+ if (options.color !== undefined)
17338
+ patch.color = options.color;
17339
+ if (options.lineWidth !== undefined)
17340
+ patch.lineWidth = options.lineWidth;
17341
+ if (options.lineStyle !== undefined)
17342
+ patch.lineStyle = options.lineStyle;
17343
+ this._private__priceLine.applyOptions(patch);
17344
+ }
17345
+ /**
17346
+ * Set the title directly. Use this for updates that don't depend on the
17347
+ * line's price (e.g. live PnL on a fixed entry line). Bypasses formatTitle.
17348
+ */
17349
+ setTitle(text) {
17350
+ this._private__options.title = text;
17351
+ // If a formatter is set we still write the static title to options, but
17352
+ // the visual title comes from the formatter on the next price update.
17353
+ // To make this call effective immediately, we override the rendered title
17354
+ // here unconditionally.
17355
+ if (this._private__priceLine) {
17356
+ this._private__priceLine.applyOptions({ title: text });
17269
17357
  }
17270
17358
  }
17271
17359
  /**
@@ -17275,7 +17363,7 @@ class DraggablePriceLine {
17275
17363
  return this._private__options.price;
17276
17364
  }
17277
17365
  /**
17278
- * Set price programmatically
17366
+ * Set price programmatically. Triggers formatTitle if configured.
17279
17367
  */
17280
17368
  setPrice(price) {
17281
17369
  this._private__updatePrice(price);
@@ -17397,6 +17485,8 @@ function createTradingLine(series, chart, options) {
17397
17485
  onDragStart: options.onDragStart,
17398
17486
  onDragMove: options.onDragMove,
17399
17487
  onDragEnd: options.onDragEnd,
17488
+ onClick: options.onClick,
17489
+ formatTitle: options.formatTitle,
17400
17490
  };
17401
17491
  return new DraggablePriceLine(chart, series, lineOptions);
17402
17492
  }
@@ -24467,7 +24557,7 @@ const customSeriesDefaultOptions = {
24467
24557
  * Returns the current version as a string. For example `'1.0.0'`.
24468
24558
  */
24469
24559
  function version() {
24470
- return "1.2.0";
24560
+ return "1.2.1";
24471
24561
  }
24472
24562
 
24473
24563
  export { areaSeries as AreaSeries, ArrowTool, barSeries as BarSeries, baselineSeries as BaselineSeries, BrushTool, candlestickSeries as CandlestickSeries, CircleTool, ColorType, 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, PositionCalculator, PositionMarkersManager, PositionTool, PriceLineSource, PriceScaleMode, RectangleTool, RulerCalculator, RulerTool, STANDARD_FIBONACCI_RATIOS, TextTool, TickMarkType, TrackingModeExitMode, TradingSessionsIndicator, TrendLineTool, VerticalLineTool, VolumeProfileIndicator, applyATR, applyBollingerBands, applyEMA, applyMACD, applyOBV, applyRSI, applySMA, applyStochastic, applyVolume, applyWMA, calculatePipConfig, createArrowTool, createBrushTool, createChart, createChartEx, createCircleTool, createFibonacciExtensionTool, createFibonacciFanTool, createFibonacciTool, createGanttTool, createHorizontalLineTool, createImageWatermark, createInteractiveLineManager, createOptionsChart, createPositionMarkers, createPositionTool, createRectangleTool, createRulerTool, createSeriesMarkers, createTextTool, createTextWatermark, createTradingLine, createTradingSessionsIndicator, createTrendLineTool, createUpDownMarkers, createVerticalLineTool, createVolumeProfileIndicator, createYieldCurveChart, customSeriesDefaultOptions, defaultHorzScaleBehavior, getPipConfig, isBusinessDay, isUTCTimestamp, setOBVVolumeData, setVolumeData, setVolumeProfileData, version };