@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.
package/README.md CHANGED
@@ -61,7 +61,9 @@ Explore interactive examples to see Pipsend Charts in action:
61
61
 
62
62
  ### Screenshots
63
63
 
64
- > **Note:** Add screenshots of your charts here to showcase the visual capabilities
64
+ ![PipsendCharts Clean](image.png)
65
+ ![PipsendCharts Indicators](image-1.png)
66
+ ![PipsendCharts Trading Lines](image-2.png)
65
67
 
66
68
  ```
67
69
  📊 Candlestick Chart with SMA & EMA
@@ -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)
@@ -6417,7 +6417,12 @@ class ChartModel {
6417
6417
  series._internal_destroy();
6418
6418
  }
6419
6419
  this._private__timeScale._internal_recalculateIndicesWithData();
6420
+ const paneCountBefore = this._private__panes.length;
6420
6421
  this._private__cleanupIfPaneIsEmpty(paneImpl);
6422
+ // If a pane was removed, trigger full update to refresh the layout
6423
+ if (this._private__panes.length < paneCountBefore) {
6424
+ this._internal_fullUpdate();
6425
+ }
6421
6426
  }
6422
6427
  _internal_moveSeriesToScale(series, targetScaleId) {
6423
6428
  const pane = ensureNotNull(this._internal_paneForSource(series));
@@ -16144,6 +16149,8 @@ class DraggablePriceLine {
16144
16149
  this._private__isDragging = false;
16145
16150
  this._private__container = null;
16146
16151
  this._private__unsubscribeHandlers = [];
16152
+ this._private__isHovering = false;
16153
+ this._private__originalColor = '';
16147
16154
  this._private__chart = chart;
16148
16155
  this._private__series = series;
16149
16156
  this._private__options = {
@@ -16159,6 +16166,7 @@ class DraggablePriceLine {
16159
16166
  }
16160
16167
  }
16161
16168
  _private__createPriceLine() {
16169
+ this._private__originalColor = this._private__options.color;
16162
16170
  this._private__priceLine = this._private__series.createPriceLine({
16163
16171
  price: this._private__options.price,
16164
16172
  color: this._private__options.color,
@@ -16174,8 +16182,12 @@ class DraggablePriceLine {
16174
16182
  return;
16175
16183
  this._private__container = chartElement;
16176
16184
  const handleMouseDown = (e) => {
16185
+ // Solo permitir drag desde el área del gráfico (no desde el price axis)
16186
+ if (!this._private__isInChartArea(e)) {
16187
+ return;
16188
+ }
16177
16189
  const price = this._private__getPriceFromY(e.clientY);
16178
- if (this._private__isNearLine(price)) {
16190
+ if (this._private__isOnLine(e, price)) {
16179
16191
  this._private__isDragging = true;
16180
16192
  this._private__container.style.cursor = 'ns-resize';
16181
16193
  if (this._private__options.onDragStart) {
@@ -16192,15 +16204,24 @@ class DraggablePriceLine {
16192
16204
  if (this._private__options.onDragMove) {
16193
16205
  this._private__options.onDragMove(newPrice);
16194
16206
  }
16207
+ // Prevenir que el gráfico se mueva mientras arrastramos la línea
16208
+ e.preventDefault();
16209
+ e.stopPropagation();
16195
16210
  }
16196
16211
  else {
16197
- // Show hover cursor
16212
+ // Solo mostrar hover si está en el área del gráfico
16213
+ if (!this._private__isInChartArea(e)) {
16214
+ this._private__setHoverState(false);
16215
+ return;
16216
+ }
16198
16217
  const price = this._private__getPriceFromY(e.clientY);
16199
- if (this._private__isNearLine(price)) {
16218
+ if (this._private__isOnLine(e, price)) {
16200
16219
  this._private__container.style.cursor = 'ns-resize';
16220
+ this._private__setHoverState(true);
16201
16221
  }
16202
16222
  else {
16203
16223
  this._private__container.style.cursor = 'default';
16224
+ this._private__setHoverState(false);
16204
16225
  }
16205
16226
  }
16206
16227
  };
@@ -16208,69 +16229,50 @@ class DraggablePriceLine {
16208
16229
  if (this._private__isDragging) {
16209
16230
  this._private__isDragging = false;
16210
16231
  this._private__container.style.cursor = 'default';
16232
+ this._private__setHoverState(false);
16211
16233
  if (this._private__options.onDragEnd) {
16212
16234
  this._private__options.onDragEnd(this._private__options.price);
16213
16235
  }
16214
16236
  }
16215
16237
  };
16238
+ const handleMouseLeave = (e) => {
16239
+ if (this._private__isDragging) {
16240
+ this._private__isDragging = false;
16241
+ this._private__setHoverState(false);
16242
+ if (this._private__options.onDragEnd) {
16243
+ this._private__options.onDragEnd(this._private__options.price);
16244
+ }
16245
+ }
16246
+ else {
16247
+ this._private__setHoverState(false);
16248
+ }
16249
+ };
16216
16250
  this._private__container.addEventListener('mousedown', handleMouseDown);
16217
16251
  this._private__container.addEventListener('mousemove', handleMouseMove);
16218
16252
  this._private__container.addEventListener('mouseup', handleMouseUp);
16219
- this._private__container.addEventListener('mouseleave', handleMouseUp);
16253
+ this._private__container.addEventListener('mouseleave', handleMouseLeave);
16220
16254
  // Store unsubscribe handlers
16221
16255
  this._private__unsubscribeHandlers.push(() => {
16222
16256
  this._private__container?.removeEventListener('mousedown', handleMouseDown);
16223
16257
  this._private__container?.removeEventListener('mousemove', handleMouseMove);
16224
16258
  this._private__container?.removeEventListener('mouseup', handleMouseUp);
16225
- this._private__container?.removeEventListener('mouseleave', handleMouseUp);
16259
+ this._private__container?.removeEventListener('mouseleave', handleMouseLeave);
16226
16260
  });
16227
16261
  }
16228
16262
  _private__getPriceFromY(clientY) {
16229
16263
  try {
16230
- // Get chart element and bounds
16264
+ // Usar la API del chart para convertir coordenada Y a precio
16265
+ // Esto garantiza que usamos exactamente la misma escala que el chart
16231
16266
  const chartElement = this._private__chart.chartElement?.() || document.querySelector('.tv-lightweight-charts');
16232
16267
  if (!chartElement)
16233
16268
  return this._private__options.price;
16234
16269
  const rect = chartElement.getBoundingClientRect();
16235
16270
  const relativeY = clientY - rect.top;
16236
- // Get visible logical range
16237
- const timeScale = this._private__chart.timeScale();
16238
- const visibleRange = timeScale.getVisibleLogicalRange();
16239
- if (!visibleRange)
16271
+ // Usar coordinateToPrice() - método oficial del chart
16272
+ const price = this._private__series.coordinateToPrice(relativeY);
16273
+ if (price === null) {
16240
16274
  return this._private__options.price;
16241
- // Get series data
16242
- const seriesData = this._private__series.data();
16243
- if (!seriesData || seriesData.length === 0)
16244
- return this._private__options.price;
16245
- // Calculate min/max price in visible range
16246
- const startIndex = Math.max(0, Math.floor(visibleRange.from));
16247
- const endIndex = Math.min(seriesData.length - 1, Math.ceil(visibleRange.to));
16248
- let minPrice = Infinity;
16249
- let maxPrice = -Infinity;
16250
- for (let i = startIndex; i <= endIndex; i++) {
16251
- const d = seriesData[i];
16252
- if ('high' in d && 'low' in d) {
16253
- minPrice = Math.min(minPrice, d.low);
16254
- maxPrice = Math.max(maxPrice, d.high);
16255
- }
16256
- else if ('close' in d) {
16257
- minPrice = Math.min(minPrice, d.close);
16258
- maxPrice = Math.max(maxPrice, d.close);
16259
- }
16260
- else if ('value' in d) {
16261
- minPrice = Math.min(minPrice, d.value);
16262
- maxPrice = Math.max(maxPrice, d.value);
16263
- }
16264
16275
  }
16265
- // Add 10% padding
16266
- const padding = (maxPrice - minPrice) * 0.1;
16267
- minPrice -= padding;
16268
- maxPrice += padding;
16269
- // Calculate price from Y position
16270
- const priceRange = maxPrice - minPrice;
16271
- const chartHeight = rect.height;
16272
- const pricePerPixel = priceRange / chartHeight;
16273
- const price = maxPrice - (relativeY * pricePerPixel);
16274
16276
  return price;
16275
16277
  }
16276
16278
  catch (error) {
@@ -16278,37 +16280,61 @@ class DraggablePriceLine {
16278
16280
  return this._private__options.price;
16279
16281
  }
16280
16282
  }
16281
- _private__isNearLine(price) {
16282
- // More generous threshold - 15 pixels in price space
16283
+ /**
16284
+ * Verifica si el mouse está EN la línea (no cerca, sino exactamente sobre ella)
16285
+ * Usa la API de coordenadas del chart para precisión consistente
16286
+ * @param e - Mouse event
16287
+ * @param price - Precio calculado desde la posición Y
16288
+ */
16289
+ _private__isOnLine(e, price) {
16283
16290
  const chartElement = this._private__chart.chartElement?.() || document.querySelector('.tv-lightweight-charts');
16284
16291
  if (!chartElement)
16285
16292
  return false;
16293
+ // Usar la API del chart para convertir precio a coordenada Y
16294
+ const lineCoordinate = this._private__series.priceToCoordinate(this._private__options.price);
16295
+ if (lineCoordinate === null)
16296
+ return false;
16297
+ // Obtener la coordenada Y del mouse relativa al chart
16286
16298
  const rect = chartElement.getBoundingClientRect();
16287
- const seriesData = this._private__series.data();
16288
- if (!seriesData || seriesData.length === 0)
16299
+ const mouseY = e.clientY - rect.top;
16300
+ // Threshold FIJO en píxeles (muy preciso y consistente)
16301
+ const pixelThreshold = 5; // 5 píxeles - siempre igual sin importar el zoom
16302
+ // Comparar directamente en píxeles (más preciso)
16303
+ return Math.abs(mouseY - lineCoordinate) <= pixelThreshold;
16304
+ }
16305
+ /**
16306
+ * Verifica si el mouse está en el área del gráfico (no en el price axis)
16307
+ */
16308
+ _private__isInChartArea(e) {
16309
+ if (!this._private__container)
16289
16310
  return false;
16290
- // Calculate pixel threshold based on visible range
16291
- let minPrice = Infinity;
16292
- let maxPrice = -Infinity;
16293
- for (const data of seriesData) {
16294
- const d = data;
16295
- if ('high' in d && 'low' in d) {
16296
- minPrice = Math.min(minPrice, d.low);
16297
- maxPrice = Math.max(maxPrice, d.high);
16298
- }
16299
- else if ('close' in d) {
16300
- minPrice = Math.min(minPrice, d.close);
16301
- maxPrice = Math.max(maxPrice, d.close);
16302
- }
16303
- else if ('value' in d) {
16304
- minPrice = Math.min(minPrice, d.value);
16305
- maxPrice = Math.max(maxPrice, d.value);
16306
- }
16311
+ // Obtener el ancho del price axis (usualmente ~60-80px desde la derecha)
16312
+ const rect = this._private__container.getBoundingClientRect();
16313
+ const priceAxisWidth = 80; // Ancho aproximado del eje de precios
16314
+ // El mouse debe estar ANTES del price axis (área del gráfico)
16315
+ const mouseX = e.clientX - rect.left;
16316
+ const chartAreaWidth = rect.width - priceAxisWidth;
16317
+ return mouseX < chartAreaWidth;
16318
+ }
16319
+ /**
16320
+ * Activa/desactiva el estado de hover (línea negra)
16321
+ */
16322
+ _private__setHoverState(isHovering) {
16323
+ if (this._private__isHovering === isHovering || !this._private__priceLine)
16324
+ return;
16325
+ this._private__isHovering = isHovering;
16326
+ if (isHovering) {
16327
+ // Cambiar a negro cuando está en hover
16328
+ this._private__priceLine.applyOptions({
16329
+ color: '#000000'
16330
+ });
16331
+ }
16332
+ else {
16333
+ // Restaurar color original
16334
+ this._private__priceLine.applyOptions({
16335
+ color: this._private__originalColor
16336
+ });
16307
16337
  }
16308
- const priceRange = maxPrice - minPrice;
16309
- const pixelThreshold = 15; // 15 pixels
16310
- const priceThreshold = (priceRange / rect.height) * pixelThreshold;
16311
- return Math.abs(price - this._private__options.price) < priceThreshold;
16312
16338
  }
16313
16339
  _private__updatePrice(newPrice) {
16314
16340
  this._private__options.price = newPrice;
@@ -16321,6 +16347,10 @@ class DraggablePriceLine {
16321
16347
  */
16322
16348
  applyOptions(options) {
16323
16349
  this._private__options = { ...this._private__options, ...options };
16350
+ // Actualizar color original si se proporciona
16351
+ if (options.color) {
16352
+ this._private__originalColor = options.color;
16353
+ }
16324
16354
  if (this._private__priceLine && options.price !== undefined) {
16325
16355
  this._private__priceLine.applyOptions({
16326
16356
  price: options.price,
@@ -16350,6 +16380,8 @@ class DraggablePriceLine {
16350
16380
  // Cleanup event listeners
16351
16381
  this._private__unsubscribeHandlers.forEach(handler => handler());
16352
16382
  this._private__unsubscribeHandlers = [];
16383
+ // Remove hover state
16384
+ this._private__setHoverState(false);
16353
16385
  // Remove price line
16354
16386
  if (this._private__priceLine) {
16355
16387
  this._private__series.removePriceLine(this._private__priceLine);
@@ -16482,49 +16514,23 @@ class InteractiveLineManager {
16482
16514
  }
16483
16515
  }
16484
16516
  _private__getPriceFromMouseEvent(e) {
16485
- if (!this._private__container)
16486
- return 0;
16487
- const rect = this._private__container.getBoundingClientRect();
16488
- const relativeY = e.clientY - rect.top;
16489
- // Get visible logical range
16490
- const timeScale = this._private__chart.timeScale();
16491
- const visibleRange = timeScale.getVisibleLogicalRange();
16492
- if (!visibleRange)
16493
- return 0;
16494
- // Get series data
16495
- const seriesData = this._private__series.data();
16496
- if (!seriesData || seriesData.length === 0)
16497
- return 0;
16498
- // Calculate min/max price in visible range
16499
- const startIndex = Math.max(0, Math.floor(visibleRange.from));
16500
- const endIndex = Math.min(seriesData.length - 1, Math.ceil(visibleRange.to));
16501
- let minPrice = Infinity;
16502
- let maxPrice = -Infinity;
16503
- for (let i = startIndex; i <= endIndex; i++) {
16504
- const d = seriesData[i];
16505
- if ('high' in d && 'low' in d) {
16506
- minPrice = Math.min(minPrice, d.low);
16507
- maxPrice = Math.max(maxPrice, d.high);
16508
- }
16509
- else if ('close' in d) {
16510
- minPrice = Math.min(minPrice, d.close);
16511
- maxPrice = Math.max(maxPrice, d.close);
16512
- }
16513
- else if ('value' in d) {
16514
- minPrice = Math.min(minPrice, d.value);
16515
- maxPrice = Math.max(maxPrice, d.value);
16517
+ try {
16518
+ if (!this._private__container)
16519
+ return 0;
16520
+ // Usar la misma API que usa el dragging para consistencia perfecta
16521
+ const rect = this._private__container.getBoundingClientRect();
16522
+ const relativeY = e.clientY - rect.top;
16523
+ // Usar coordinateToPrice() - método oficial del chart
16524
+ const price = this._private__series.coordinateToPrice(relativeY);
16525
+ if (price === null) {
16526
+ return 0;
16516
16527
  }
16528
+ return price;
16529
+ }
16530
+ catch (error) {
16531
+ console.error('Error getting price from mouse event:', error);
16532
+ return 0;
16517
16533
  }
16518
- // Add 10% padding
16519
- const padding = (maxPrice - minPrice) * 0.1;
16520
- minPrice -= padding;
16521
- maxPrice += padding;
16522
- // Calculate price from Y position
16523
- const priceRange = maxPrice - minPrice;
16524
- const chartHeight = rect.height;
16525
- const pricePerPixel = priceRange / chartHeight;
16526
- const price = maxPrice - (relativeY * pricePerPixel);
16527
- return price;
16528
16534
  }
16529
16535
  /**
16530
16536
  * Activa el modo de creación de líneas por clic
@@ -16685,7 +16691,7 @@ const customSeriesDefaultOptions = {
16685
16691
  * Returns the current version as a string. For example `'1.0.0'`.
16686
16692
  */
16687
16693
  function version() {
16688
- return "0.0.8-dev+202510241619";
16694
+ return "0.0.10-dev+202510282321";
16689
16695
  }
16690
16696
 
16691
16697
  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 };