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