@pipsend/charts 0.0.8 → 0.0.10

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 v0.0.8-dev+202510241619
3
+ * Pipsend Charts v0.0.10-dev+202510282321
4
4
  * Copyright (c) 2025 Pipsend
5
5
  * Licensed under MIT License
6
6
  * Built on TradingView Lightweight Charts™ (Apache 2.0)
@@ -6415,7 +6415,12 @@ class ChartModel {
6415
6415
  series._internal_destroy();
6416
6416
  }
6417
6417
  this._private__timeScale._internal_recalculateIndicesWithData();
6418
+ const paneCountBefore = this._private__panes.length;
6418
6419
  this._private__cleanupIfPaneIsEmpty(paneImpl);
6420
+ // If a pane was removed, trigger full update to refresh the layout
6421
+ if (this._private__panes.length < paneCountBefore) {
6422
+ this._internal_fullUpdate();
6423
+ }
6419
6424
  }
6420
6425
  _internal_moveSeriesToScale(series, targetScaleId) {
6421
6426
  const pane = ensureNotNull(this._internal_paneForSource(series));
@@ -16537,6 +16542,8 @@ class DraggablePriceLine {
16537
16542
  this._private__isDragging = false;
16538
16543
  this._private__container = null;
16539
16544
  this._private__unsubscribeHandlers = [];
16545
+ this._private__isHovering = false;
16546
+ this._private__originalColor = '';
16540
16547
  this._private__chart = chart;
16541
16548
  this._private__series = series;
16542
16549
  this._private__options = {
@@ -16552,6 +16559,7 @@ class DraggablePriceLine {
16552
16559
  }
16553
16560
  }
16554
16561
  _private__createPriceLine() {
16562
+ this._private__originalColor = this._private__options.color;
16555
16563
  this._private__priceLine = this._private__series.createPriceLine({
16556
16564
  price: this._private__options.price,
16557
16565
  color: this._private__options.color,
@@ -16567,8 +16575,12 @@ class DraggablePriceLine {
16567
16575
  return;
16568
16576
  this._private__container = chartElement;
16569
16577
  const handleMouseDown = (e) => {
16578
+ // Solo permitir drag desde el área del gráfico (no desde el price axis)
16579
+ if (!this._private__isInChartArea(e)) {
16580
+ return;
16581
+ }
16570
16582
  const price = this._private__getPriceFromY(e.clientY);
16571
- if (this._private__isNearLine(price)) {
16583
+ if (this._private__isOnLine(e, price)) {
16572
16584
  this._private__isDragging = true;
16573
16585
  this._private__container.style.cursor = 'ns-resize';
16574
16586
  if (this._private__options.onDragStart) {
@@ -16585,15 +16597,24 @@ class DraggablePriceLine {
16585
16597
  if (this._private__options.onDragMove) {
16586
16598
  this._private__options.onDragMove(newPrice);
16587
16599
  }
16600
+ // Prevenir que el gráfico se mueva mientras arrastramos la línea
16601
+ e.preventDefault();
16602
+ e.stopPropagation();
16588
16603
  }
16589
16604
  else {
16590
- // Show hover cursor
16605
+ // Solo mostrar hover si está en el área del gráfico
16606
+ if (!this._private__isInChartArea(e)) {
16607
+ this._private__setHoverState(false);
16608
+ return;
16609
+ }
16591
16610
  const price = this._private__getPriceFromY(e.clientY);
16592
- if (this._private__isNearLine(price)) {
16611
+ if (this._private__isOnLine(e, price)) {
16593
16612
  this._private__container.style.cursor = 'ns-resize';
16613
+ this._private__setHoverState(true);
16594
16614
  }
16595
16615
  else {
16596
16616
  this._private__container.style.cursor = 'default';
16617
+ this._private__setHoverState(false);
16597
16618
  }
16598
16619
  }
16599
16620
  };
@@ -16601,69 +16622,50 @@ class DraggablePriceLine {
16601
16622
  if (this._private__isDragging) {
16602
16623
  this._private__isDragging = false;
16603
16624
  this._private__container.style.cursor = 'default';
16625
+ this._private__setHoverState(false);
16604
16626
  if (this._private__options.onDragEnd) {
16605
16627
  this._private__options.onDragEnd(this._private__options.price);
16606
16628
  }
16607
16629
  }
16608
16630
  };
16631
+ const handleMouseLeave = (e) => {
16632
+ if (this._private__isDragging) {
16633
+ this._private__isDragging = false;
16634
+ this._private__setHoverState(false);
16635
+ if (this._private__options.onDragEnd) {
16636
+ this._private__options.onDragEnd(this._private__options.price);
16637
+ }
16638
+ }
16639
+ else {
16640
+ this._private__setHoverState(false);
16641
+ }
16642
+ };
16609
16643
  this._private__container.addEventListener('mousedown', handleMouseDown);
16610
16644
  this._private__container.addEventListener('mousemove', handleMouseMove);
16611
16645
  this._private__container.addEventListener('mouseup', handleMouseUp);
16612
- this._private__container.addEventListener('mouseleave', handleMouseUp);
16646
+ this._private__container.addEventListener('mouseleave', handleMouseLeave);
16613
16647
  // Store unsubscribe handlers
16614
16648
  this._private__unsubscribeHandlers.push(() => {
16615
16649
  this._private__container?.removeEventListener('mousedown', handleMouseDown);
16616
16650
  this._private__container?.removeEventListener('mousemove', handleMouseMove);
16617
16651
  this._private__container?.removeEventListener('mouseup', handleMouseUp);
16618
- this._private__container?.removeEventListener('mouseleave', handleMouseUp);
16652
+ this._private__container?.removeEventListener('mouseleave', handleMouseLeave);
16619
16653
  });
16620
16654
  }
16621
16655
  _private__getPriceFromY(clientY) {
16622
16656
  try {
16623
- // Get chart element and bounds
16657
+ // Usar la API del chart para convertir coordenada Y a precio
16658
+ // Esto garantiza que usamos exactamente la misma escala que el chart
16624
16659
  const chartElement = this._private__chart.chartElement?.() || document.querySelector('.tv-lightweight-charts');
16625
16660
  if (!chartElement)
16626
16661
  return this._private__options.price;
16627
16662
  const rect = chartElement.getBoundingClientRect();
16628
16663
  const relativeY = clientY - rect.top;
16629
- // Get visible logical range
16630
- const timeScale = this._private__chart.timeScale();
16631
- const visibleRange = timeScale.getVisibleLogicalRange();
16632
- if (!visibleRange)
16664
+ // Usar coordinateToPrice() - método oficial del chart
16665
+ const price = this._private__series.coordinateToPrice(relativeY);
16666
+ if (price === null) {
16633
16667
  return this._private__options.price;
16634
- // Get series data
16635
- const seriesData = this._private__series.data();
16636
- if (!seriesData || seriesData.length === 0)
16637
- return this._private__options.price;
16638
- // Calculate min/max price in visible range
16639
- const startIndex = Math.max(0, Math.floor(visibleRange.from));
16640
- const endIndex = Math.min(seriesData.length - 1, Math.ceil(visibleRange.to));
16641
- let minPrice = Infinity;
16642
- let maxPrice = -Infinity;
16643
- for (let i = startIndex; i <= endIndex; i++) {
16644
- const d = seriesData[i];
16645
- if ('high' in d && 'low' in d) {
16646
- minPrice = Math.min(minPrice, d.low);
16647
- maxPrice = Math.max(maxPrice, d.high);
16648
- }
16649
- else if ('close' in d) {
16650
- minPrice = Math.min(minPrice, d.close);
16651
- maxPrice = Math.max(maxPrice, d.close);
16652
- }
16653
- else if ('value' in d) {
16654
- minPrice = Math.min(minPrice, d.value);
16655
- maxPrice = Math.max(maxPrice, d.value);
16656
- }
16657
16668
  }
16658
- // Add 10% padding
16659
- const padding = (maxPrice - minPrice) * 0.1;
16660
- minPrice -= padding;
16661
- maxPrice += padding;
16662
- // Calculate price from Y position
16663
- const priceRange = maxPrice - minPrice;
16664
- const chartHeight = rect.height;
16665
- const pricePerPixel = priceRange / chartHeight;
16666
- const price = maxPrice - (relativeY * pricePerPixel);
16667
16669
  return price;
16668
16670
  }
16669
16671
  catch (error) {
@@ -16671,37 +16673,61 @@ class DraggablePriceLine {
16671
16673
  return this._private__options.price;
16672
16674
  }
16673
16675
  }
16674
- _private__isNearLine(price) {
16675
- // More generous threshold - 15 pixels in price space
16676
+ /**
16677
+ * Verifica si el mouse está EN la línea (no cerca, sino exactamente sobre ella)
16678
+ * Usa la API de coordenadas del chart para precisión consistente
16679
+ * @param e - Mouse event
16680
+ * @param price - Precio calculado desde la posición Y
16681
+ */
16682
+ _private__isOnLine(e, price) {
16676
16683
  const chartElement = this._private__chart.chartElement?.() || document.querySelector('.tv-lightweight-charts');
16677
16684
  if (!chartElement)
16678
16685
  return false;
16686
+ // Usar la API del chart para convertir precio a coordenada Y
16687
+ const lineCoordinate = this._private__series.priceToCoordinate(this._private__options.price);
16688
+ if (lineCoordinate === null)
16689
+ return false;
16690
+ // Obtener la coordenada Y del mouse relativa al chart
16679
16691
  const rect = chartElement.getBoundingClientRect();
16680
- const seriesData = this._private__series.data();
16681
- if (!seriesData || seriesData.length === 0)
16692
+ const mouseY = e.clientY - rect.top;
16693
+ // Threshold FIJO en píxeles (muy preciso y consistente)
16694
+ const pixelThreshold = 5; // 5 píxeles - siempre igual sin importar el zoom
16695
+ // Comparar directamente en píxeles (más preciso)
16696
+ return Math.abs(mouseY - lineCoordinate) <= pixelThreshold;
16697
+ }
16698
+ /**
16699
+ * Verifica si el mouse está en el área del gráfico (no en el price axis)
16700
+ */
16701
+ _private__isInChartArea(e) {
16702
+ if (!this._private__container)
16682
16703
  return false;
16683
- // Calculate pixel threshold based on visible range
16684
- let minPrice = Infinity;
16685
- let maxPrice = -Infinity;
16686
- for (const data of seriesData) {
16687
- const d = data;
16688
- if ('high' in d && 'low' in d) {
16689
- minPrice = Math.min(minPrice, d.low);
16690
- maxPrice = Math.max(maxPrice, d.high);
16691
- }
16692
- else if ('close' in d) {
16693
- minPrice = Math.min(minPrice, d.close);
16694
- maxPrice = Math.max(maxPrice, d.close);
16695
- }
16696
- else if ('value' in d) {
16697
- minPrice = Math.min(minPrice, d.value);
16698
- maxPrice = Math.max(maxPrice, d.value);
16699
- }
16704
+ // Obtener el ancho del price axis (usualmente ~60-80px desde la derecha)
16705
+ const rect = this._private__container.getBoundingClientRect();
16706
+ const priceAxisWidth = 80; // Ancho aproximado del eje de precios
16707
+ // El mouse debe estar ANTES del price axis (área del gráfico)
16708
+ const mouseX = e.clientX - rect.left;
16709
+ const chartAreaWidth = rect.width - priceAxisWidth;
16710
+ return mouseX < chartAreaWidth;
16711
+ }
16712
+ /**
16713
+ * Activa/desactiva el estado de hover (línea negra)
16714
+ */
16715
+ _private__setHoverState(isHovering) {
16716
+ if (this._private__isHovering === isHovering || !this._private__priceLine)
16717
+ return;
16718
+ this._private__isHovering = isHovering;
16719
+ if (isHovering) {
16720
+ // Cambiar a negro cuando está en hover
16721
+ this._private__priceLine.applyOptions({
16722
+ color: '#000000'
16723
+ });
16724
+ }
16725
+ else {
16726
+ // Restaurar color original
16727
+ this._private__priceLine.applyOptions({
16728
+ color: this._private__originalColor
16729
+ });
16700
16730
  }
16701
- const priceRange = maxPrice - minPrice;
16702
- const pixelThreshold = 15; // 15 pixels
16703
- const priceThreshold = (priceRange / rect.height) * pixelThreshold;
16704
- return Math.abs(price - this._private__options.price) < priceThreshold;
16705
16731
  }
16706
16732
  _private__updatePrice(newPrice) {
16707
16733
  this._private__options.price = newPrice;
@@ -16714,6 +16740,10 @@ class DraggablePriceLine {
16714
16740
  */
16715
16741
  applyOptions(options) {
16716
16742
  this._private__options = { ...this._private__options, ...options };
16743
+ // Actualizar color original si se proporciona
16744
+ if (options.color) {
16745
+ this._private__originalColor = options.color;
16746
+ }
16717
16747
  if (this._private__priceLine && options.price !== undefined) {
16718
16748
  this._private__priceLine.applyOptions({
16719
16749
  price: options.price,
@@ -16743,6 +16773,8 @@ class DraggablePriceLine {
16743
16773
  // Cleanup event listeners
16744
16774
  this._private__unsubscribeHandlers.forEach(handler => handler());
16745
16775
  this._private__unsubscribeHandlers = [];
16776
+ // Remove hover state
16777
+ this._private__setHoverState(false);
16746
16778
  // Remove price line
16747
16779
  if (this._private__priceLine) {
16748
16780
  this._private__series.removePriceLine(this._private__priceLine);
@@ -16875,49 +16907,23 @@ class InteractiveLineManager {
16875
16907
  }
16876
16908
  }
16877
16909
  _private__getPriceFromMouseEvent(e) {
16878
- if (!this._private__container)
16879
- return 0;
16880
- const rect = this._private__container.getBoundingClientRect();
16881
- const relativeY = e.clientY - rect.top;
16882
- // Get visible logical range
16883
- const timeScale = this._private__chart.timeScale();
16884
- const visibleRange = timeScale.getVisibleLogicalRange();
16885
- if (!visibleRange)
16886
- return 0;
16887
- // Get series data
16888
- const seriesData = this._private__series.data();
16889
- if (!seriesData || seriesData.length === 0)
16890
- return 0;
16891
- // Calculate min/max price in visible range
16892
- const startIndex = Math.max(0, Math.floor(visibleRange.from));
16893
- const endIndex = Math.min(seriesData.length - 1, Math.ceil(visibleRange.to));
16894
- let minPrice = Infinity;
16895
- let maxPrice = -Infinity;
16896
- for (let i = startIndex; i <= endIndex; i++) {
16897
- const d = seriesData[i];
16898
- if ('high' in d && 'low' in d) {
16899
- minPrice = Math.min(minPrice, d.low);
16900
- maxPrice = Math.max(maxPrice, d.high);
16901
- }
16902
- else if ('close' in d) {
16903
- minPrice = Math.min(minPrice, d.close);
16904
- maxPrice = Math.max(maxPrice, d.close);
16905
- }
16906
- else if ('value' in d) {
16907
- minPrice = Math.min(minPrice, d.value);
16908
- maxPrice = Math.max(maxPrice, d.value);
16910
+ try {
16911
+ if (!this._private__container)
16912
+ return 0;
16913
+ // Usar la misma API que usa el dragging para consistencia perfecta
16914
+ const rect = this._private__container.getBoundingClientRect();
16915
+ const relativeY = e.clientY - rect.top;
16916
+ // Usar coordinateToPrice() - método oficial del chart
16917
+ const price = this._private__series.coordinateToPrice(relativeY);
16918
+ if (price === null) {
16919
+ return 0;
16909
16920
  }
16921
+ return price;
16922
+ }
16923
+ catch (error) {
16924
+ console.error('Error getting price from mouse event:', error);
16925
+ return 0;
16910
16926
  }
16911
- // Add 10% padding
16912
- const padding = (maxPrice - minPrice) * 0.1;
16913
- minPrice -= padding;
16914
- maxPrice += padding;
16915
- // Calculate price from Y position
16916
- const priceRange = maxPrice - minPrice;
16917
- const chartHeight = rect.height;
16918
- const pricePerPixel = priceRange / chartHeight;
16919
- const price = maxPrice - (relativeY * pricePerPixel);
16920
- return price;
16921
16927
  }
16922
16928
  /**
16923
16929
  * Activa el modo de creación de líneas por clic
@@ -17078,7 +17084,7 @@ const customSeriesDefaultOptions = {
17078
17084
  * Returns the current version as a string. For example `'1.0.0'`.
17079
17085
  */
17080
17086
  function version() {
17081
- return "0.0.8-dev+202510241619";
17087
+ return "0.0.10-dev+202510282321";
17082
17088
  }
17083
17089
 
17084
17090
  export { areaSeries as AreaSeries, barSeries as BarSeries, baselineSeries as BaselineSeries, candlestickSeries as CandlestickSeries, ColorType, CrosshairMode, DraggablePriceLine, histogramSeries as HistogramSeries, InteractiveLineManager, LastPriceAnimationMode, lineSeries as LineSeries, LineStyle, LineType, MismatchDirection, PriceLineSource, PriceScaleMode, TickMarkType, TrackingModeExitMode, applyATR, applyBollingerBands, applyEMA, applyMACD, applyOBV, applyRSI, applySMA, applyStochastic, applyVolume, applyWMA, createChart, createChartEx, createImageWatermark, createInteractiveLineManager, createOptionsChart, createSeriesMarkers, createTextWatermark, createTradingLine, createUpDownMarkers, createYieldCurveChart, customSeriesDefaultOptions, defaultHorzScaleBehavior, isBusinessDay, isUTCTimestamp, setOBVVolumeData, setVolumeData, version };