@pipsend/charts 0.0.8 → 0.0.9
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 +3 -1
- package/dist/pipsend-charts.development.mjs +116 -110
- package/dist/pipsend-charts.production.mjs +2 -2
- package/dist/pipsend-charts.standalone.development.js +116 -110
- package/dist/pipsend-charts.standalone.development.mjs +116 -110
- package/dist/pipsend-charts.standalone.production.js +2 -2
- package/dist/pipsend-charts.standalone.production.mjs +2 -2
- package/dist/typings.d.ts +17 -1
- package/package.json +1 -1
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
|
-
|
|
64
|
+

|
|
65
|
+

|
|
66
|
+

|
|
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.
|
|
3
|
+
* Pipsend Charts v0.0.9-dev+202510282317
|
|
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.
|
|
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
|
-
//
|
|
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.
|
|
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',
|
|
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',
|
|
16259
|
+
this._private__container?.removeEventListener('mouseleave', handleMouseLeave);
|
|
16226
16260
|
});
|
|
16227
16261
|
}
|
|
16228
16262
|
_private__getPriceFromY(clientY) {
|
|
16229
16263
|
try {
|
|
16230
|
-
//
|
|
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
|
-
//
|
|
16237
|
-
const
|
|
16238
|
-
|
|
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
|
-
|
|
16282
|
-
|
|
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
|
|
16288
|
-
|
|
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
|
-
//
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
16304
|
-
|
|
16305
|
-
|
|
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
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
|
|
16490
|
-
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
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.
|
|
16694
|
+
return "0.0.9-dev+202510282317";
|
|
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 };
|