@pipsend/charts 2.3.13 → 2.3.14
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/dist/pipsend-charts.development.mjs +282 -78
- package/dist/pipsend-charts.production.mjs +2 -2
- package/dist/pipsend-charts.standalone.development.js +282 -78
- package/dist/pipsend-charts.standalone.development.mjs +282 -78
- package/dist/pipsend-charts.standalone.production.js +2 -2
- package/dist/pipsend-charts.standalone.production.mjs +2 -2
- package/dist/typings.d.ts +41 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @license
|
|
3
|
-
* Pipsend Charts v2.3.
|
|
3
|
+
* Pipsend Charts v2.3.14
|
|
4
4
|
* Copyright (c) 2026 Pipsend
|
|
5
5
|
* Licensed under MIT License
|
|
6
6
|
* Built on TradingView Lightweight Charts™ (Apache 2.0)
|
|
@@ -21644,6 +21644,13 @@
|
|
|
21644
21644
|
series() {
|
|
21645
21645
|
return this._series;
|
|
21646
21646
|
}
|
|
21647
|
+
/**
|
|
21648
|
+
* Box the selection toolbar anchors to, in pane pixels. Null falls back to
|
|
21649
|
+
* the serialized points, which cannot place a line across the whole pane.
|
|
21650
|
+
*/
|
|
21651
|
+
selectionBox() {
|
|
21652
|
+
return null;
|
|
21653
|
+
}
|
|
21647
21654
|
getState() {
|
|
21648
21655
|
return this._state;
|
|
21649
21656
|
}
|
|
@@ -22320,6 +22327,53 @@
|
|
|
22320
22327
|
return new TrendLineTool(context, options);
|
|
22321
22328
|
}
|
|
22322
22329
|
|
|
22330
|
+
const END_HANDLE_MARGIN = 10;
|
|
22331
|
+
/**
|
|
22332
|
+
* Hands the next `clicks` clicks on the chart to `onClick`, past the last bar
|
|
22333
|
+
* too. A press that grabbed another drawing does not count.
|
|
22334
|
+
*/
|
|
22335
|
+
function placeByClicks(chart, series, clicks, onClick) {
|
|
22336
|
+
let placed = 0;
|
|
22337
|
+
let handler = null;
|
|
22338
|
+
const stop = () => {
|
|
22339
|
+
if (handler !== null) {
|
|
22340
|
+
chart.unsubscribeClick(handler);
|
|
22341
|
+
handler = null;
|
|
22342
|
+
}
|
|
22343
|
+
};
|
|
22344
|
+
handler = (param) => {
|
|
22345
|
+
if (!param.point || consumeInputClaim(chart)) {
|
|
22346
|
+
return;
|
|
22347
|
+
}
|
|
22348
|
+
const price = series.coordinateToPrice(param.point.y);
|
|
22349
|
+
const time = timeFromCoordinate(chart, series, param.point.x);
|
|
22350
|
+
if (price === null || time === null) {
|
|
22351
|
+
return;
|
|
22352
|
+
}
|
|
22353
|
+
placed++;
|
|
22354
|
+
if (placed >= clicks) {
|
|
22355
|
+
stop();
|
|
22356
|
+
}
|
|
22357
|
+
onClick({ _internal_time: time, _internal_price: price, _internal_x: param.point.x, _internal_y: param.point.y }, placed - 1);
|
|
22358
|
+
};
|
|
22359
|
+
chart.subscribeClick(handler);
|
|
22360
|
+
return {
|
|
22361
|
+
_internal_pending: () => handler !== null,
|
|
22362
|
+
_internal_stop: stop,
|
|
22363
|
+
};
|
|
22364
|
+
}
|
|
22365
|
+
/** A fifth of the pane, and never less than a bar. */
|
|
22366
|
+
function defaultWidth(chart) {
|
|
22367
|
+
const timeScale = chart.timeScale();
|
|
22368
|
+
return Math.max(timeScale.width() / 5, timeScale.options().barSpacing);
|
|
22369
|
+
}
|
|
22370
|
+
/** Kept inside the pane: past it, the handles on the end hide under the price axis. */
|
|
22371
|
+
function placedEndX(chart, x) {
|
|
22372
|
+
const timeScale = chart.timeScale();
|
|
22373
|
+
const onScreen = Math.min(x + defaultWidth(chart), timeScale.width() - END_HANDLE_MARGIN);
|
|
22374
|
+
return Math.max(onScreen, x + timeScale.options().barSpacing);
|
|
22375
|
+
}
|
|
22376
|
+
|
|
22323
22377
|
class HorizontalLinePaneRenderer {
|
|
22324
22378
|
constructor(tool) {
|
|
22325
22379
|
this._private__tool = tool;
|
|
@@ -22360,6 +22414,7 @@
|
|
|
22360
22414
|
this._private__isDragging = false;
|
|
22361
22415
|
this._private__grab = null;
|
|
22362
22416
|
this._private__moveStartPrice = 0;
|
|
22417
|
+
this._private__placement = null;
|
|
22363
22418
|
this._private__hLineOptions = {
|
|
22364
22419
|
lineColor: '#2962ff',
|
|
22365
22420
|
lineWidth: 2,
|
|
@@ -22372,6 +22427,26 @@
|
|
|
22372
22427
|
};
|
|
22373
22428
|
this._registerOptions(this._private__hLineOptions);
|
|
22374
22429
|
this._private__createPrimitive();
|
|
22430
|
+
if (this._private__hLineOptions.interactive) {
|
|
22431
|
+
this._private__placement = placeByClicks(this._chart, this._series, 1, (point) => this._private__placeAt(point));
|
|
22432
|
+
}
|
|
22433
|
+
}
|
|
22434
|
+
_private__placeAt(point) {
|
|
22435
|
+
this._private__hLineOptions.price = point._internal_price;
|
|
22436
|
+
if (this._private__hLineOptions.startTime !== undefined) {
|
|
22437
|
+
this._private__hLineOptions.startTime = point._internal_time;
|
|
22438
|
+
}
|
|
22439
|
+
this._private__requestUpdate();
|
|
22440
|
+
}
|
|
22441
|
+
isComplete() {
|
|
22442
|
+
return !this._private__placement?._internal_pending();
|
|
22443
|
+
}
|
|
22444
|
+
selectionBox() {
|
|
22445
|
+
const y = this._private__hLineOptions.price === undefined ? null : this._series.priceToCoordinate(this._private__hLineOptions.price);
|
|
22446
|
+
if (y === null) {
|
|
22447
|
+
return null;
|
|
22448
|
+
}
|
|
22449
|
+
return { left: this._private__startX(), right: this._chart.timeScale().width(), top: y, bottom: y };
|
|
22375
22450
|
}
|
|
22376
22451
|
_private__createPrimitive() {
|
|
22377
22452
|
this._private__paneView = this._private__createPaneView();
|
|
@@ -22409,7 +22484,7 @@
|
|
|
22409
22484
|
return coordinateFromTime(this._chart, this._series, this._private__hLineOptions.startTime) ?? 0;
|
|
22410
22485
|
}
|
|
22411
22486
|
drawToCanvas(ctx, horizontalPixelRatio, verticalPixelRatio) {
|
|
22412
|
-
if (this._private__hLineOptions.price === undefined)
|
|
22487
|
+
if (!this.isComplete() || this._private__hLineOptions.price === undefined)
|
|
22413
22488
|
return;
|
|
22414
22489
|
const y = (this._series.priceToCoordinate(this._private__hLineOptions.price) ?? 0) * verticalPixelRatio;
|
|
22415
22490
|
const chartWidth = this._chart.chartElement().clientWidth * horizontalPixelRatio;
|
|
@@ -22503,7 +22578,7 @@
|
|
|
22503
22578
|
return this.hitTest(x, y) ? toolHoveredItem(this.id, 'body', false) : null;
|
|
22504
22579
|
}
|
|
22505
22580
|
hitTest(x, y) {
|
|
22506
|
-
if (this._private__hLineOptions.price === undefined) {
|
|
22581
|
+
if (!this.isComplete() || this._private__hLineOptions.price === undefined) {
|
|
22507
22582
|
return false;
|
|
22508
22583
|
}
|
|
22509
22584
|
const lineY = this._series.priceToCoordinate(this._private__hLineOptions.price) ?? 0;
|
|
@@ -22578,6 +22653,7 @@
|
|
|
22578
22653
|
this._private__requestUpdate();
|
|
22579
22654
|
}
|
|
22580
22655
|
remove() {
|
|
22656
|
+
this._private__placement?._internal_stop();
|
|
22581
22657
|
if (this._private__primitive) {
|
|
22582
22658
|
this._series.detachPrimitive(this._private__primitive);
|
|
22583
22659
|
this._private__primitive = null;
|
|
@@ -22637,6 +22713,7 @@
|
|
|
22637
22713
|
this._private__grab = null;
|
|
22638
22714
|
this._private__moveStartTime = 0;
|
|
22639
22715
|
this._private__moveStartPrice = 0;
|
|
22716
|
+
this._private__placement = null;
|
|
22640
22717
|
this._private__crossOptions = {
|
|
22641
22718
|
lineColor: '#787b86',
|
|
22642
22719
|
lineWidth: 1,
|
|
@@ -22646,6 +22723,16 @@
|
|
|
22646
22723
|
};
|
|
22647
22724
|
this._registerOptions(this._private__crossOptions);
|
|
22648
22725
|
this._private__createPrimitive();
|
|
22726
|
+
if (this._private__crossOptions.interactive) {
|
|
22727
|
+
this._private__placement = placeByClicks(this._chart, this._series, 1, (point) => {
|
|
22728
|
+
this._private__crossOptions.time = point._internal_time;
|
|
22729
|
+
this._private__crossOptions.price = point._internal_price;
|
|
22730
|
+
this._private__requestUpdate();
|
|
22731
|
+
});
|
|
22732
|
+
}
|
|
22733
|
+
}
|
|
22734
|
+
isComplete() {
|
|
22735
|
+
return !this._private__placement?._internal_pending();
|
|
22649
22736
|
}
|
|
22650
22737
|
_private__createPrimitive() {
|
|
22651
22738
|
this._private__paneView = new CrossLinePaneView(this);
|
|
@@ -22673,7 +22760,7 @@
|
|
|
22673
22760
|
this._private__requestUpdate();
|
|
22674
22761
|
}
|
|
22675
22762
|
_private__coordinates() {
|
|
22676
|
-
if (this._private__crossOptions.time === undefined || this._private__crossOptions.price === undefined) {
|
|
22763
|
+
if (!this.isComplete() || this._private__crossOptions.time === undefined || this._private__crossOptions.price === undefined) {
|
|
22677
22764
|
return null;
|
|
22678
22765
|
}
|
|
22679
22766
|
const x = coordinateFromTime(this._chart, this._series, this._private__crossOptions.time);
|
|
@@ -22796,6 +22883,7 @@
|
|
|
22796
22883
|
return this._private__isHovered;
|
|
22797
22884
|
}
|
|
22798
22885
|
remove() {
|
|
22886
|
+
this._private__placement?._internal_stop();
|
|
22799
22887
|
if (this._private__primitive) {
|
|
22800
22888
|
try {
|
|
22801
22889
|
this._series.detachPrimitive(this._private__primitive);
|
|
@@ -24218,6 +24306,7 @@
|
|
|
24218
24306
|
this._private__isDragging = false;
|
|
24219
24307
|
this._private__grab = null;
|
|
24220
24308
|
this._private__moveStartTime = 0;
|
|
24309
|
+
this._private__placement = null;
|
|
24221
24310
|
this._private__vLineOptions = {
|
|
24222
24311
|
lineColor: '#787b86',
|
|
24223
24312
|
lineWidth: 1,
|
|
@@ -24229,6 +24318,29 @@
|
|
|
24229
24318
|
};
|
|
24230
24319
|
this._registerOptions(this._private__vLineOptions);
|
|
24231
24320
|
this._private__createPrimitive();
|
|
24321
|
+
if (this._private__vLineOptions.interactive) {
|
|
24322
|
+
this._private__placement = placeByClicks(this._chart, this._series, 1, (point) => {
|
|
24323
|
+
this._private__vLineOptions.time = point._internal_time;
|
|
24324
|
+
this._private__requestUpdate();
|
|
24325
|
+
});
|
|
24326
|
+
}
|
|
24327
|
+
}
|
|
24328
|
+
isComplete() {
|
|
24329
|
+
return !this._private__placement?._internal_pending();
|
|
24330
|
+
}
|
|
24331
|
+
selectionBox() {
|
|
24332
|
+
const x = coordinateFromTime(this._chart, this._series, this._private__vLineOptions.time);
|
|
24333
|
+
if (x === null) {
|
|
24334
|
+
return null;
|
|
24335
|
+
}
|
|
24336
|
+
let middle;
|
|
24337
|
+
try {
|
|
24338
|
+
middle = this._series.getPane().getHeight() / 2;
|
|
24339
|
+
}
|
|
24340
|
+
catch (error) {
|
|
24341
|
+
return null;
|
|
24342
|
+
}
|
|
24343
|
+
return { left: x, right: x, top: middle, bottom: middle };
|
|
24232
24344
|
}
|
|
24233
24345
|
_private__createPrimitive() {
|
|
24234
24346
|
this._private__paneView = this._private__createPaneView();
|
|
@@ -24259,7 +24371,7 @@
|
|
|
24259
24371
|
return new VerticalLinePaneView(this);
|
|
24260
24372
|
}
|
|
24261
24373
|
drawToCanvas(ctx, horizontalPixelRatio, verticalPixelRatio) {
|
|
24262
|
-
if (this._private__vLineOptions.time === undefined)
|
|
24374
|
+
if (!this.isComplete() || this._private__vLineOptions.time === undefined)
|
|
24263
24375
|
return;
|
|
24264
24376
|
const x = (coordinateFromTime(this._chart, this._series, this._private__vLineOptions.time) ?? 0) * horizontalPixelRatio;
|
|
24265
24377
|
const chartHeight = this._chart.chartElement().clientHeight * verticalPixelRatio;
|
|
@@ -24346,7 +24458,7 @@
|
|
|
24346
24458
|
return this.hitTest(x, y) ? toolHoveredItem(this.id, 'body', false) : null;
|
|
24347
24459
|
}
|
|
24348
24460
|
hitTest(x, y) {
|
|
24349
|
-
if (this._private__vLineOptions.time === undefined) {
|
|
24461
|
+
if (!this.isComplete() || this._private__vLineOptions.time === undefined) {
|
|
24350
24462
|
return false;
|
|
24351
24463
|
}
|
|
24352
24464
|
const lineX = coordinateFromTime(this._chart, this._series, this._private__vLineOptions.time) ?? 0;
|
|
@@ -24416,6 +24528,7 @@
|
|
|
24416
24528
|
this._private__requestUpdate();
|
|
24417
24529
|
}
|
|
24418
24530
|
remove() {
|
|
24531
|
+
this._private__placement?._internal_stop();
|
|
24419
24532
|
if (this._private__primitive) {
|
|
24420
24533
|
this._series.detachPrimitive(this._private__primitive);
|
|
24421
24534
|
this._private__primitive = null;
|
|
@@ -25137,6 +25250,7 @@
|
|
|
25137
25250
|
this._private__isHovered = false;
|
|
25138
25251
|
this._private__isDragging = false;
|
|
25139
25252
|
this._private__dragTarget = null;
|
|
25253
|
+
this._private__placement = null;
|
|
25140
25254
|
this._private__fibOptions = {
|
|
25141
25255
|
levels: [
|
|
25142
25256
|
{ level: 0, color: '#787B86', label: '0%' },
|
|
@@ -25152,6 +25266,20 @@
|
|
|
25152
25266
|
...options,
|
|
25153
25267
|
};
|
|
25154
25268
|
this._private__createPrimitive();
|
|
25269
|
+
if (this._private__fibOptions.interactive) {
|
|
25270
|
+
this._private__placement = placeByClicks(this._chart, this._series, 3, (point, index) => {
|
|
25271
|
+
const placed = { time: point._internal_time, price: point._internal_price };
|
|
25272
|
+
if (index === 0) {
|
|
25273
|
+
this.setPoint1(placed);
|
|
25274
|
+
}
|
|
25275
|
+
else if (index === 1) {
|
|
25276
|
+
this.setPoint2(placed);
|
|
25277
|
+
}
|
|
25278
|
+
else {
|
|
25279
|
+
this.setPoint3(placed);
|
|
25280
|
+
}
|
|
25281
|
+
});
|
|
25282
|
+
}
|
|
25155
25283
|
}
|
|
25156
25284
|
_private__createPrimitive() {
|
|
25157
25285
|
this._private__paneView = this._private__createPaneView();
|
|
@@ -25209,7 +25337,7 @@
|
|
|
25209
25337
|
this._private__requestUpdate();
|
|
25210
25338
|
}
|
|
25211
25339
|
isComplete() {
|
|
25212
|
-
return !!(this._private__fibOptions.point1 && this._private__fibOptions.point2 && this._private__fibOptions.point3);
|
|
25340
|
+
return !this._private__placement?._internal_pending() && !!(this._private__fibOptions.point1 && this._private__fibOptions.point2 && this._private__fibOptions.point3);
|
|
25213
25341
|
}
|
|
25214
25342
|
setColor(color) {
|
|
25215
25343
|
this._private__fibOptions.lineColor = color;
|
|
@@ -25504,6 +25632,7 @@
|
|
|
25504
25632
|
this._private__requestUpdate();
|
|
25505
25633
|
}
|
|
25506
25634
|
remove() {
|
|
25635
|
+
this._private__placement?._internal_stop();
|
|
25507
25636
|
if (this._private__primitive) {
|
|
25508
25637
|
this._series.detachPrimitive(this._private__primitive);
|
|
25509
25638
|
this._private__primitive = null;
|
|
@@ -28069,6 +28198,7 @@
|
|
|
28069
28198
|
this._private__moveStart = null;
|
|
28070
28199
|
this._private__isEditing = false;
|
|
28071
28200
|
this._private__clickOutsideHandler = null;
|
|
28201
|
+
this._private__placement = null;
|
|
28072
28202
|
this._private__textOptions = {
|
|
28073
28203
|
text: 'Double-click to edit',
|
|
28074
28204
|
fontSize: 14,
|
|
@@ -28089,6 +28219,14 @@
|
|
|
28089
28219
|
this._registerOptions(this._private__textOptions);
|
|
28090
28220
|
this._private__createPrimitive();
|
|
28091
28221
|
this._private__setupClickOutsideHandler();
|
|
28222
|
+
if (this._private__textOptions.interactive) {
|
|
28223
|
+
this._private__placement = placeByClicks(this._chart, this._series, 1, (point) => {
|
|
28224
|
+
this.setPosition(point._internal_time, point._internal_price);
|
|
28225
|
+
if (this._private__textOptions.onComplete) {
|
|
28226
|
+
this._private__textOptions.onComplete();
|
|
28227
|
+
}
|
|
28228
|
+
});
|
|
28229
|
+
}
|
|
28092
28230
|
}
|
|
28093
28231
|
_private__setupClickOutsideHandler() {
|
|
28094
28232
|
// Set up click handler to finish editing when clicking outside
|
|
@@ -28174,7 +28312,7 @@
|
|
|
28174
28312
|
this._private__requestUpdate();
|
|
28175
28313
|
}
|
|
28176
28314
|
isComplete() {
|
|
28177
|
-
return !!(this._private__textOptions.time && this._private__textOptions.price !== undefined);
|
|
28315
|
+
return !this._private__placement?._internal_pending() && !!(this._private__textOptions.time && this._private__textOptions.price !== undefined);
|
|
28178
28316
|
}
|
|
28179
28317
|
setColor(color) {
|
|
28180
28318
|
this._private__textOptions.textColor = color;
|
|
@@ -28457,6 +28595,7 @@
|
|
|
28457
28595
|
};
|
|
28458
28596
|
}
|
|
28459
28597
|
remove() {
|
|
28598
|
+
this._private__placement?._internal_stop();
|
|
28460
28599
|
// Clean up click handler
|
|
28461
28600
|
if (this._private__clickOutsideHandler) {
|
|
28462
28601
|
this._chart.unsubscribeClick(this._private__clickOutsideHandler);
|
|
@@ -29315,6 +29454,7 @@
|
|
|
29315
29454
|
this._private__paneView = null;
|
|
29316
29455
|
this._private__isSelected = false;
|
|
29317
29456
|
this._private__isHovered = false;
|
|
29457
|
+
this._private__placement = null;
|
|
29318
29458
|
this._private__isDragging = false;
|
|
29319
29459
|
this._private__dragTarget = null;
|
|
29320
29460
|
this._private__grab = null;
|
|
@@ -29337,6 +29477,32 @@
|
|
|
29337
29477
|
// Create primitive and setup
|
|
29338
29478
|
this._private__createPrimitive();
|
|
29339
29479
|
this._private__updateMetrics();
|
|
29480
|
+
if (this._private__positionOptions.interactive) {
|
|
29481
|
+
this._private__placement = placeByClicks(this._chart, this._series, 1, (point) => this._private__placeAt(point));
|
|
29482
|
+
}
|
|
29483
|
+
}
|
|
29484
|
+
_private__placeAt(point) {
|
|
29485
|
+
const options = this._private__positionOptions;
|
|
29486
|
+
const shift = point._internal_price - options.entryPrice;
|
|
29487
|
+
options.entryPrice = point._internal_price;
|
|
29488
|
+
options.entryTime = point._internal_time;
|
|
29489
|
+
if (options.stopLoss !== undefined) {
|
|
29490
|
+
options.stopLoss += shift;
|
|
29491
|
+
}
|
|
29492
|
+
if (options.takeProfit !== undefined) {
|
|
29493
|
+
options.takeProfit += shift;
|
|
29494
|
+
}
|
|
29495
|
+
options.endTime = timeFromCoordinate(this._chart, this._series, placedEndX(this._chart, point._internal_x)) ?? undefined;
|
|
29496
|
+
this._private__updateMetrics();
|
|
29497
|
+
this._private__requestUpdate();
|
|
29498
|
+
}
|
|
29499
|
+
isComplete() {
|
|
29500
|
+
return !this._private__placement?._internal_pending();
|
|
29501
|
+
}
|
|
29502
|
+
_private__span() {
|
|
29503
|
+
const entryX = coordinateFromTime(this._chart, this._series, this._private__positionOptions.entryTime) ?? 0;
|
|
29504
|
+
const endX = coordinateFromTime(this._chart, this._series, this._private__positionOptions.endTime) ?? entryX + defaultWidth(this._chart);
|
|
29505
|
+
return { _internal_entryX: entryX, _internal_endX: endX };
|
|
29340
29506
|
}
|
|
29341
29507
|
_private__createPrimitive() {
|
|
29342
29508
|
// Create and cache the paneView once
|
|
@@ -29368,6 +29534,9 @@
|
|
|
29368
29534
|
return new PositionPaneView(this);
|
|
29369
29535
|
}
|
|
29370
29536
|
drawToCanvas(ctx, horizontalPixelRatio, verticalPixelRatio) {
|
|
29537
|
+
if (!this.isComplete()) {
|
|
29538
|
+
return;
|
|
29539
|
+
}
|
|
29371
29540
|
// Draw profit rectangle (green)
|
|
29372
29541
|
if (this._private__profitRect && this._private__positionOptions.takeProfit) {
|
|
29373
29542
|
const timeScale = this._chart.timeScale();
|
|
@@ -29401,16 +29570,9 @@
|
|
|
29401
29570
|
}
|
|
29402
29571
|
}
|
|
29403
29572
|
_private__drawRectangle(ctx, rect, horizontalPixelRatio, verticalPixelRatio, timeScale) {
|
|
29404
|
-
const
|
|
29405
|
-
|
|
29406
|
-
|
|
29407
|
-
if (this._private__positionOptions.endTime) {
|
|
29408
|
-
endX = (coordinateFromTime(this._chart, this._series, this._private__positionOptions.endTime) ?? 0) * horizontalPixelRatio;
|
|
29409
|
-
}
|
|
29410
|
-
else {
|
|
29411
|
-
const chartWidth = this._chart.chartElement().clientWidth * horizontalPixelRatio;
|
|
29412
|
-
endX = chartWidth;
|
|
29413
|
-
}
|
|
29573
|
+
const span = this._private__span();
|
|
29574
|
+
const entryX = span._internal_entryX * horizontalPixelRatio;
|
|
29575
|
+
const endX = span._internal_endX * horizontalPixelRatio;
|
|
29414
29576
|
const y1 = (this._series.priceToCoordinate(rect._internal_price1) ?? 0) * verticalPixelRatio;
|
|
29415
29577
|
const y2 = (this._series.priceToCoordinate(rect._internal_price2) ?? 0) * verticalPixelRatio;
|
|
29416
29578
|
// Draw filled rectangle with semi-transparent fill
|
|
@@ -29425,16 +29587,10 @@
|
|
|
29425
29587
|
ctx.stroke();
|
|
29426
29588
|
}
|
|
29427
29589
|
_private__drawEntryLine(ctx, horizontalPixelRatio, verticalPixelRatio, timeScale) {
|
|
29428
|
-
const
|
|
29590
|
+
const span = this._private__span();
|
|
29591
|
+
const entryX = span._internal_entryX * horizontalPixelRatio;
|
|
29592
|
+
const endX = span._internal_endX * horizontalPixelRatio;
|
|
29429
29593
|
const entryY = (this._series.priceToCoordinate(this._private__positionOptions.entryPrice) ?? 0) * verticalPixelRatio;
|
|
29430
|
-
let endX;
|
|
29431
|
-
if (this._private__positionOptions.endTime) {
|
|
29432
|
-
endX = (coordinateFromTime(this._chart, this._series, this._private__positionOptions.endTime) ?? 0) * horizontalPixelRatio;
|
|
29433
|
-
}
|
|
29434
|
-
else {
|
|
29435
|
-
const chartWidth = this._chart.chartElement().clientWidth * horizontalPixelRatio;
|
|
29436
|
-
endX = chartWidth;
|
|
29437
|
-
}
|
|
29438
29594
|
// Draw horizontal entry line (divider)
|
|
29439
29595
|
const lineColor = this._private__positionOptions.entryLineColor;
|
|
29440
29596
|
ctx.strokeStyle = lineColor;
|
|
@@ -29548,9 +29704,11 @@
|
|
|
29548
29704
|
const padding = 6 * horizontalPixelRatio;
|
|
29549
29705
|
const labelWidth = textMetrics.width + padding * 2;
|
|
29550
29706
|
const labelHeight = 20 * horizontalPixelRatio;
|
|
29551
|
-
//
|
|
29707
|
+
// Outside the box, so a label wider than it does not cover the handles on its edge
|
|
29708
|
+
const entryY = (this._series.priceToCoordinate(this._private__positionOptions.entryPrice) ?? 0) * verticalPixelRatio;
|
|
29709
|
+
const gap = 3 * verticalPixelRatio;
|
|
29552
29710
|
const labelX = entryX + 8 * horizontalPixelRatio;
|
|
29553
|
-
const labelY = y - labelHeight
|
|
29711
|
+
const labelY = y <= entryY ? y - labelHeight - gap : y + gap;
|
|
29554
29712
|
// Draw label background
|
|
29555
29713
|
ctx.fillStyle = rect._internal_color;
|
|
29556
29714
|
ctx.fillRect(labelX, labelY, labelWidth, labelHeight);
|
|
@@ -29558,10 +29716,12 @@
|
|
|
29558
29716
|
ctx.fillStyle = '#ffffff';
|
|
29559
29717
|
ctx.textAlign = 'left';
|
|
29560
29718
|
ctx.textBaseline = 'middle';
|
|
29561
|
-
ctx.fillText(labelText, labelX + padding,
|
|
29719
|
+
ctx.fillText(labelText, labelX + padding, labelY + labelHeight / 2);
|
|
29562
29720
|
}
|
|
29563
29721
|
_private__drawEndpoint(ctx, price, color, horizontalPixelRatio, verticalPixelRatio, timeScale) {
|
|
29564
|
-
const
|
|
29722
|
+
const span = this._private__span();
|
|
29723
|
+
const entryX = span._internal_entryX * horizontalPixelRatio;
|
|
29724
|
+
const endX = span._internal_endX * horizontalPixelRatio;
|
|
29565
29725
|
const y = (this._series.priceToCoordinate(price) ?? 0) * verticalPixelRatio;
|
|
29566
29726
|
// Draw circle endpoint at entry
|
|
29567
29727
|
const circleColor = color;
|
|
@@ -29573,14 +29733,6 @@
|
|
|
29573
29733
|
ctx.fill();
|
|
29574
29734
|
ctx.stroke();
|
|
29575
29735
|
// Draw square endpoint at the right edge
|
|
29576
|
-
let endX;
|
|
29577
|
-
if (this._private__positionOptions.endTime) {
|
|
29578
|
-
endX = (coordinateFromTime(this._chart, this._series, this._private__positionOptions.endTime) ?? 0) * horizontalPixelRatio;
|
|
29579
|
-
}
|
|
29580
|
-
else {
|
|
29581
|
-
const chartWidth = this._chart.chartElement().clientWidth * horizontalPixelRatio;
|
|
29582
|
-
endX = chartWidth - 10 * horizontalPixelRatio;
|
|
29583
|
-
}
|
|
29584
29736
|
const size = 8 * horizontalPixelRatio;
|
|
29585
29737
|
const squareColor = color;
|
|
29586
29738
|
ctx.fillStyle = squareColor;
|
|
@@ -29701,18 +29853,12 @@
|
|
|
29701
29853
|
return this.hitTest(x, y) ? toolHoveredItem(this.id, 'body', false) : null;
|
|
29702
29854
|
}
|
|
29703
29855
|
hitTest(x, y) {
|
|
29704
|
-
if (!this._private__positionOptions.stopLoss && !this._private__positionOptions.takeProfit) {
|
|
29856
|
+
if (!this.isComplete() || (!this._private__positionOptions.stopLoss && !this._private__positionOptions.takeProfit)) {
|
|
29705
29857
|
return false;
|
|
29706
29858
|
}
|
|
29707
|
-
const
|
|
29708
|
-
|
|
29709
|
-
|
|
29710
|
-
endX = coordinateFromTime(this._chart, this._series, this._private__positionOptions.endTime) ?? 0;
|
|
29711
|
-
}
|
|
29712
|
-
else {
|
|
29713
|
-
const chartWidth = this._chart.chartElement().clientWidth;
|
|
29714
|
-
endX = chartWidth;
|
|
29715
|
-
}
|
|
29859
|
+
const { _internal_entryX: entryX, _internal_endX: endX } = this._private__span();
|
|
29860
|
+
const left = Math.min(entryX, endX);
|
|
29861
|
+
const right = Math.max(entryX, endX);
|
|
29716
29862
|
// Check if inside the entire Position Tool area (rectangles)
|
|
29717
29863
|
if (this._private__positionOptions.takeProfit && this._private__positionOptions.stopLoss) {
|
|
29718
29864
|
const tpY = this._series.priceToCoordinate(this._private__positionOptions.takeProfit) ?? 0;
|
|
@@ -29720,7 +29866,7 @@
|
|
|
29720
29866
|
const minY = Math.min(tpY, slY);
|
|
29721
29867
|
const maxY = Math.max(tpY, slY);
|
|
29722
29868
|
// Check if inside the rectangle bounds (entire tool area)
|
|
29723
|
-
if (x >=
|
|
29869
|
+
if (x >= left && x <= right && y >= minY && y <= maxY) {
|
|
29724
29870
|
return true;
|
|
29725
29871
|
}
|
|
29726
29872
|
// Also check entry line area
|
|
@@ -29732,15 +29878,12 @@
|
|
|
29732
29878
|
return false;
|
|
29733
29879
|
}
|
|
29734
29880
|
hitTestEndpoint(x, y) {
|
|
29735
|
-
|
|
29736
|
-
|
|
29737
|
-
if (this._private__positionOptions.endTime) {
|
|
29738
|
-
endX = coordinateFromTime(this._chart, this._series, this._private__positionOptions.endTime) ?? 0;
|
|
29739
|
-
}
|
|
29740
|
-
else {
|
|
29741
|
-
const chartWidth = this._chart.chartElement().clientWidth;
|
|
29742
|
-
endX = chartWidth;
|
|
29881
|
+
if (!this.isComplete()) {
|
|
29882
|
+
return null;
|
|
29743
29883
|
}
|
|
29884
|
+
const { _internal_entryX: entryX, _internal_endX: endX } = this._private__span();
|
|
29885
|
+
const left = Math.min(entryX, endX);
|
|
29886
|
+
const right = Math.max(entryX, endX);
|
|
29744
29887
|
const tolerance = 10;
|
|
29745
29888
|
// Check TP left endpoint (circle)
|
|
29746
29889
|
if (this._private__positionOptions.takeProfit) {
|
|
@@ -29777,7 +29920,7 @@
|
|
|
29777
29920
|
// Check entry line (middle divider line) - higher priority than 'move'
|
|
29778
29921
|
const entryY = this._series.priceToCoordinate(this._private__positionOptions.entryPrice) ?? 0;
|
|
29779
29922
|
const lineTolerance = 8;
|
|
29780
|
-
if (x >=
|
|
29923
|
+
if (x >= left && x <= right && Math.abs(y - entryY) <= lineTolerance) {
|
|
29781
29924
|
return 'entry';
|
|
29782
29925
|
}
|
|
29783
29926
|
// Check if clicking on the body (rectangles) for moving entire tool
|
|
@@ -29787,7 +29930,7 @@
|
|
|
29787
29930
|
const minY = Math.min(tpY, slY);
|
|
29788
29931
|
const maxY = Math.max(tpY, slY);
|
|
29789
29932
|
// Check if inside the rectangle bounds
|
|
29790
|
-
if (x >=
|
|
29933
|
+
if (x >= left && x <= right && y >= minY && y <= maxY) {
|
|
29791
29934
|
return 'move';
|
|
29792
29935
|
}
|
|
29793
29936
|
}
|
|
@@ -29887,6 +30030,7 @@
|
|
|
29887
30030
|
this._private__requestUpdate();
|
|
29888
30031
|
}
|
|
29889
30032
|
remove() {
|
|
30033
|
+
this._private__placement?._internal_stop();
|
|
29890
30034
|
if (this._private__primitive) {
|
|
29891
30035
|
this._series.detachPrimitive(this._private__primitive);
|
|
29892
30036
|
this._private__primitive = null;
|
|
@@ -29933,6 +30077,7 @@
|
|
|
29933
30077
|
}
|
|
29934
30078
|
}
|
|
29935
30079
|
|
|
30080
|
+
const TOP_PADDING = 50;
|
|
29936
30081
|
class GanttTool extends DrawingToolBase {
|
|
29937
30082
|
constructor(context, options) {
|
|
29938
30083
|
super(context, 'gantt', options);
|
|
@@ -29946,6 +30091,7 @@
|
|
|
29946
30091
|
this._private__grab = null;
|
|
29947
30092
|
this._private__moveStart = null;
|
|
29948
30093
|
this._private__hoveredSegmentIndex = -1;
|
|
30094
|
+
this._private__placement = null;
|
|
29949
30095
|
this._private__ganttOptions = {
|
|
29950
30096
|
segments: [],
|
|
29951
30097
|
rowHeight: 40,
|
|
@@ -29958,6 +30104,43 @@
|
|
|
29958
30104
|
};
|
|
29959
30105
|
this._registerOptions(this._private__ganttOptions);
|
|
29960
30106
|
this._private__createPrimitive();
|
|
30107
|
+
if (this._private__ganttOptions.interactive) {
|
|
30108
|
+
this._private__placement = placeByClicks(this._chart, this._series, 1, (point) => this._private__placeAt(point));
|
|
30109
|
+
}
|
|
30110
|
+
}
|
|
30111
|
+
_private__placeAt(point) {
|
|
30112
|
+
const segments = this._private__ganttOptions.segments ?? [];
|
|
30113
|
+
const endTime = timeFromCoordinate(this._chart, this._series, placedEndX(this._chart, point._internal_x)) ?? point._internal_time;
|
|
30114
|
+
const rowPitch = this._private__ganttOptions.rowHeight + this._private__ganttOptions.rowSpacing;
|
|
30115
|
+
const row = Math.max(0, Math.floor((point._internal_y - TOP_PADDING) / rowPitch));
|
|
30116
|
+
const shift = segments.length > 0 ? row - segments[0].level : 0;
|
|
30117
|
+
for (const segment of segments) {
|
|
30118
|
+
segment.startTime = point._internal_time;
|
|
30119
|
+
segment.endTime = endTime;
|
|
30120
|
+
segment.level = Math.max(0, segment.level + shift);
|
|
30121
|
+
}
|
|
30122
|
+
this._private__requestUpdate();
|
|
30123
|
+
}
|
|
30124
|
+
isComplete() {
|
|
30125
|
+
return !this._private__placement?._internal_pending();
|
|
30126
|
+
}
|
|
30127
|
+
selectionBox() {
|
|
30128
|
+
const segments = this._private__ganttOptions.segments ?? [];
|
|
30129
|
+
if (segments.length === 0) {
|
|
30130
|
+
return null;
|
|
30131
|
+
}
|
|
30132
|
+
const rowHeight = this._private__ganttOptions.rowHeight;
|
|
30133
|
+
const pitch = rowHeight + this._private__ganttOptions.rowSpacing;
|
|
30134
|
+
// Typed as the public shape: an inferred one gets its keys renamed by the build
|
|
30135
|
+
const box = { left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity };
|
|
30136
|
+
for (const segment of segments) {
|
|
30137
|
+
const top = TOP_PADDING + segment.level * pitch;
|
|
30138
|
+
box.left = Math.min(box.left, coordinateFromTime(this._chart, this._series, segment.startTime) ?? 0);
|
|
30139
|
+
box.right = Math.max(box.right, coordinateFromTime(this._chart, this._series, segment.endTime) ?? 0);
|
|
30140
|
+
box.top = Math.min(box.top, top);
|
|
30141
|
+
box.bottom = Math.max(box.bottom, top + rowHeight);
|
|
30142
|
+
}
|
|
30143
|
+
return box;
|
|
29961
30144
|
}
|
|
29962
30145
|
_private__createPrimitive() {
|
|
29963
30146
|
this._private__paneView = this._private__createPaneView();
|
|
@@ -29988,11 +30171,10 @@
|
|
|
29988
30171
|
return new GanttPaneView(this);
|
|
29989
30172
|
}
|
|
29990
30173
|
drawToCanvas(ctx, horizontalPixelRatio, verticalPixelRatio) {
|
|
29991
|
-
if (!this._private__ganttOptions.segments || this._private__ganttOptions.segments.length === 0)
|
|
30174
|
+
if (!this.isComplete() || !this._private__ganttOptions.segments || this._private__ganttOptions.segments.length === 0)
|
|
29992
30175
|
return;
|
|
29993
30176
|
// Position gantt at top of chart with padding
|
|
29994
|
-
const
|
|
29995
|
-
const baseY = topPadding * verticalPixelRatio;
|
|
30177
|
+
const baseY = TOP_PADDING * verticalPixelRatio;
|
|
29996
30178
|
const rowHeight = this._private__ganttOptions.rowHeight * verticalPixelRatio;
|
|
29997
30179
|
const rowSpacing = this._private__ganttOptions.rowSpacing * verticalPixelRatio;
|
|
29998
30180
|
// Draw grid lines if enabled
|
|
@@ -30171,11 +30353,10 @@
|
|
|
30171
30353
|
this._private__moveStart = null;
|
|
30172
30354
|
}
|
|
30173
30355
|
hitTest(x, y) {
|
|
30174
|
-
if (!this._private__ganttOptions.segments || this._private__ganttOptions.segments.length === 0) {
|
|
30356
|
+
if (!this.isComplete() || !this._private__ganttOptions.segments || this._private__ganttOptions.segments.length === 0) {
|
|
30175
30357
|
return false;
|
|
30176
30358
|
}
|
|
30177
|
-
const
|
|
30178
|
-
const baseY = topPadding;
|
|
30359
|
+
const baseY = TOP_PADDING;
|
|
30179
30360
|
const rowHeight = this._private__ganttOptions.rowHeight;
|
|
30180
30361
|
const rowSpacing = this._private__ganttOptions.rowSpacing;
|
|
30181
30362
|
// Check each segment
|
|
@@ -30229,6 +30410,7 @@
|
|
|
30229
30410
|
this._private__requestUpdate();
|
|
30230
30411
|
}
|
|
30231
30412
|
remove() {
|
|
30413
|
+
this._private__placement?._internal_stop();
|
|
30232
30414
|
if (this._private__primitive) {
|
|
30233
30415
|
this._series.detachPrimitive(this._private__primitive);
|
|
30234
30416
|
this._private__primitive = null;
|
|
@@ -30364,6 +30546,7 @@
|
|
|
30364
30546
|
}
|
|
30365
30547
|
this._private__completionCheck = setTimeout(() => {
|
|
30366
30548
|
this._private__completionCheck = null;
|
|
30549
|
+
const completed = [];
|
|
30367
30550
|
for (const id of Array.from(this._private__pendingToolIds)) {
|
|
30368
30551
|
const tool = this._private__tools.get(id);
|
|
30369
30552
|
if (!tool) {
|
|
@@ -30372,9 +30555,15 @@
|
|
|
30372
30555
|
}
|
|
30373
30556
|
if (tool.isComplete()) {
|
|
30374
30557
|
this._private__pendingToolIds.delete(id);
|
|
30375
|
-
|
|
30558
|
+
completed.push(id);
|
|
30376
30559
|
}
|
|
30377
30560
|
}
|
|
30561
|
+
if (completed.length > 0) {
|
|
30562
|
+
this.recordChange();
|
|
30563
|
+
}
|
|
30564
|
+
for (const id of completed) {
|
|
30565
|
+
this._private__drawingCompleted._internal_fire(id);
|
|
30566
|
+
}
|
|
30378
30567
|
}, 0);
|
|
30379
30568
|
}
|
|
30380
30569
|
/**
|
|
@@ -30434,12 +30623,16 @@
|
|
|
30434
30623
|
if (this._private__restoring) {
|
|
30435
30624
|
return;
|
|
30436
30625
|
}
|
|
30626
|
+
const next = this.serializeAll();
|
|
30627
|
+
if (JSON.stringify(next) === JSON.stringify(this._private__committed)) {
|
|
30628
|
+
return;
|
|
30629
|
+
}
|
|
30437
30630
|
this._private__past.push(this._private__committed);
|
|
30438
30631
|
if (this._private__past.length > UNDO_HISTORY_LIMIT) {
|
|
30439
30632
|
this._private__past.shift();
|
|
30440
30633
|
}
|
|
30441
30634
|
this._private__future = [];
|
|
30442
|
-
this._private__committed =
|
|
30635
|
+
this._private__committed = next;
|
|
30443
30636
|
}
|
|
30444
30637
|
canUndo() {
|
|
30445
30638
|
return this._private__past.length > 0;
|
|
@@ -30495,11 +30688,25 @@
|
|
|
30495
30688
|
const tool = this._private__tools.get(this._private__selectedToolId);
|
|
30496
30689
|
return tool ? this._private__boundsOf(tool) : null;
|
|
30497
30690
|
}
|
|
30691
|
+
/** Relative to the chart element; the tool's box is relative to its pane. */
|
|
30692
|
+
_private__boundsOf(tool) {
|
|
30693
|
+
const box = tool.selectionBox() ?? this._private__boxFromPoints(tool);
|
|
30694
|
+
if (box === null) {
|
|
30695
|
+
return null;
|
|
30696
|
+
}
|
|
30697
|
+
const offset = this._private__paneOffsetOf(tool);
|
|
30698
|
+
return {
|
|
30699
|
+
left: box.left + offset._internal_x,
|
|
30700
|
+
right: box.right + offset._internal_x,
|
|
30701
|
+
top: box.top + offset._internal_y,
|
|
30702
|
+
bottom: box.bottom + offset._internal_y,
|
|
30703
|
+
};
|
|
30704
|
+
}
|
|
30498
30705
|
/**
|
|
30499
30706
|
* Bounds come from the tool's own serialized points, so this works for every
|
|
30500
30707
|
* tool without each one having to report a box.
|
|
30501
30708
|
*/
|
|
30502
|
-
|
|
30709
|
+
_private__boxFromPoints(tool) {
|
|
30503
30710
|
const series = tool.series();
|
|
30504
30711
|
const points = tool.serialize().points;
|
|
30505
30712
|
let left = Number.POSITIVE_INFINITY;
|
|
@@ -30522,13 +30729,7 @@
|
|
|
30522
30729
|
if (!found) {
|
|
30523
30730
|
return null;
|
|
30524
30731
|
}
|
|
30525
|
-
|
|
30526
|
-
return {
|
|
30527
|
-
left: left + offset._internal_x,
|
|
30528
|
-
right: right + offset._internal_x,
|
|
30529
|
-
top: top + offset._internal_y,
|
|
30530
|
-
bottom: bottom + offset._internal_y,
|
|
30531
|
-
};
|
|
30732
|
+
return { left, top, right, bottom };
|
|
30532
30733
|
}
|
|
30533
30734
|
/** Panes below the first one are offset inside the chart element. */
|
|
30534
30735
|
_private__paneOffsetOf(tool) {
|
|
@@ -30612,7 +30813,10 @@
|
|
|
30612
30813
|
serializeAll() {
|
|
30613
30814
|
const result = [];
|
|
30614
30815
|
for (const tool of this._private__tools.values()) {
|
|
30615
|
-
|
|
30816
|
+
// Not drawn yet: undo would bring it back as a finished drawing
|
|
30817
|
+
if (tool.isComplete()) {
|
|
30818
|
+
result.push(tool.serialize());
|
|
30819
|
+
}
|
|
30616
30820
|
}
|
|
30617
30821
|
return result;
|
|
30618
30822
|
}
|
|
@@ -31237,7 +31441,7 @@
|
|
|
31237
31441
|
* Returns the current version as a string. For example `'1.0.0'`.
|
|
31238
31442
|
*/
|
|
31239
31443
|
function version() {
|
|
31240
|
-
return "2.3.
|
|
31444
|
+
return "2.3.14";
|
|
31241
31445
|
}
|
|
31242
31446
|
|
|
31243
31447
|
var PipsendChartsModule = /*#__PURE__*/Object.freeze({
|