hyperprop-charting-library 0.1.138 → 0.1.140
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/hyperprop-charting-library.cjs +641 -50
- package/dist/hyperprop-charting-library.d.ts +131 -1
- package/dist/hyperprop-charting-library.js +641 -50
- package/dist/index.cjs +641 -50
- package/dist/index.d.cts +131 -1
- package/dist/index.d.ts +131 -1
- package/dist/index.js +641 -50
- package/docs/API.md +106 -0
- package/package.json +1 -1
|
@@ -2821,6 +2821,9 @@ var DEFAULT_OPTIONS = {
|
|
|
2821
2821
|
doubleClickAction: "reset",
|
|
2822
2822
|
keyboard: { enabled: true, panBars: 1, deleteSelectedDrawing: true },
|
|
2823
2823
|
accessibility: { label: "Price chart", description: "", focusable: true },
|
|
2824
|
+
downsampling: { enabled: true, thresholdPx: 1.5 },
|
|
2825
|
+
touch: { hitToleranceScale: 2.2, longPressTooltip: true, longPressMs: 400 },
|
|
2826
|
+
datafeedOptions: { prefetchThresholdBars: 150, cooldownMs: 1200, chunkBars: 1500 },
|
|
2824
2827
|
crosshair: DEFAULT_CROSSHAIR_OPTIONS,
|
|
2825
2828
|
grid: DEFAULT_GRID_OPTIONS,
|
|
2826
2829
|
watermark: DEFAULT_WATERMARK_OPTIONS,
|
|
@@ -2871,6 +2874,18 @@ var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
|
2871
2874
|
...baseOptions.accessibility,
|
|
2872
2875
|
...options.accessibility ?? {}
|
|
2873
2876
|
},
|
|
2877
|
+
downsampling: {
|
|
2878
|
+
...baseOptions.downsampling,
|
|
2879
|
+
...options.downsampling ?? {}
|
|
2880
|
+
},
|
|
2881
|
+
touch: {
|
|
2882
|
+
...baseOptions.touch,
|
|
2883
|
+
...options.touch ?? {}
|
|
2884
|
+
},
|
|
2885
|
+
datafeedOptions: {
|
|
2886
|
+
...baseOptions.datafeedOptions,
|
|
2887
|
+
...options.datafeedOptions ?? {}
|
|
2888
|
+
},
|
|
2874
2889
|
crosshair: {
|
|
2875
2890
|
...baseOptions.crosshair,
|
|
2876
2891
|
...options.crosshair ?? {}
|
|
@@ -3045,6 +3060,7 @@ function createChart(element, options = {}) {
|
|
|
3045
3060
|
let drawingDoubleClickHandler = null;
|
|
3046
3061
|
let drawingEditTextHandler = null;
|
|
3047
3062
|
let selectionChangeHandler = null;
|
|
3063
|
+
let longPressHandler = null;
|
|
3048
3064
|
let contextMenuHandler = null;
|
|
3049
3065
|
let keyboardShortcutHandler = null;
|
|
3050
3066
|
let lastSelectionSignature = null;
|
|
@@ -3746,6 +3762,150 @@ function createChart(element, options = {}) {
|
|
|
3746
3762
|
heikinAshiFingerprint = fingerprint;
|
|
3747
3763
|
return result;
|
|
3748
3764
|
};
|
|
3765
|
+
let compareSeriesStates = [];
|
|
3766
|
+
const prepareCompareSeries = (options2) => {
|
|
3767
|
+
const rows = [];
|
|
3768
|
+
for (const point of options2.data ?? []) {
|
|
3769
|
+
const t = new Date(point.t).getTime();
|
|
3770
|
+
const c = Number(point.c);
|
|
3771
|
+
if (Number.isFinite(t) && Number.isFinite(c)) rows.push({ t, c });
|
|
3772
|
+
}
|
|
3773
|
+
rows.sort((a, b) => a.t - b.t);
|
|
3774
|
+
return {
|
|
3775
|
+
options: options2,
|
|
3776
|
+
times: rows.map((row) => row.t),
|
|
3777
|
+
closes: rows.map((row) => row.c),
|
|
3778
|
+
aligned: null,
|
|
3779
|
+
alignedFingerprint: ""
|
|
3780
|
+
};
|
|
3781
|
+
};
|
|
3782
|
+
const mainDataFingerprint = () => {
|
|
3783
|
+
const last = data[data.length - 1];
|
|
3784
|
+
return last ? `${data.length}|${data[0].time.getTime()}|${last.time.getTime()}` : "empty";
|
|
3785
|
+
};
|
|
3786
|
+
const getAlignedCompareValues = (state) => {
|
|
3787
|
+
const fingerprint = `${mainDataFingerprint()}|${state.times.length}|${state.times[state.times.length - 1] ?? 0}`;
|
|
3788
|
+
if (state.aligned && state.alignedFingerprint === fingerprint) {
|
|
3789
|
+
return state.aligned;
|
|
3790
|
+
}
|
|
3791
|
+
const aligned = new Float64Array(data.length).fill(Number.NaN);
|
|
3792
|
+
let cursor = 0;
|
|
3793
|
+
let lastValue = Number.NaN;
|
|
3794
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
3795
|
+
const barTime = data[i].time.getTime();
|
|
3796
|
+
while (cursor < state.times.length && state.times[cursor] <= barTime) {
|
|
3797
|
+
lastValue = state.closes[cursor];
|
|
3798
|
+
cursor += 1;
|
|
3799
|
+
}
|
|
3800
|
+
aligned[i] = lastValue;
|
|
3801
|
+
}
|
|
3802
|
+
state.aligned = aligned;
|
|
3803
|
+
state.alignedFingerprint = fingerprint;
|
|
3804
|
+
return aligned;
|
|
3805
|
+
};
|
|
3806
|
+
const visibleCompareSeries = () => compareSeriesStates.filter((state) => state.options.visible !== false && state.times.length > 0);
|
|
3807
|
+
const setCompareSeries = (series) => {
|
|
3808
|
+
compareSeriesStates = (series ?? []).filter((entry) => entry && entry.id).map(prepareCompareSeries);
|
|
3809
|
+
scheduleDraw({ updateAutoScale: true });
|
|
3810
|
+
};
|
|
3811
|
+
const addCompareSeries = (series) => {
|
|
3812
|
+
if (!series?.id) return;
|
|
3813
|
+
compareSeriesStates = [...compareSeriesStates.filter((state) => state.options.id !== series.id), prepareCompareSeries(series)];
|
|
3814
|
+
scheduleDraw({ updateAutoScale: true });
|
|
3815
|
+
};
|
|
3816
|
+
const removeCompareSeries = (id) => {
|
|
3817
|
+
const next = compareSeriesStates.filter((state) => state.options.id !== id);
|
|
3818
|
+
if (next.length === compareSeriesStates.length) return;
|
|
3819
|
+
compareSeriesStates = next;
|
|
3820
|
+
scheduleDraw({ updateAutoScale: true });
|
|
3821
|
+
};
|
|
3822
|
+
const getCompareSeries = () => compareSeriesStates.map((state) => state.options);
|
|
3823
|
+
let datafeed = null;
|
|
3824
|
+
let datafeedUnsubscribe = null;
|
|
3825
|
+
let datafeedLoading = false;
|
|
3826
|
+
let datafeedExhausted = false;
|
|
3827
|
+
let datafeedLastRequestAt = 0;
|
|
3828
|
+
const estimateBarStepMs = () => {
|
|
3829
|
+
if (mergedOptions.timeStepMs > 0) return mergedOptions.timeStepMs;
|
|
3830
|
+
if (data.length < 2) return 6e4;
|
|
3831
|
+
const span = data[data.length - 1].time.getTime() - data[0].time.getTime();
|
|
3832
|
+
return Math.max(1e3, span / (data.length - 1));
|
|
3833
|
+
};
|
|
3834
|
+
const requestDatafeedBars = async (request) => {
|
|
3835
|
+
if (!datafeed) return [];
|
|
3836
|
+
try {
|
|
3837
|
+
return await datafeed.getBars(request) ?? [];
|
|
3838
|
+
} catch (error) {
|
|
3839
|
+
datafeed.onError?.(error, request);
|
|
3840
|
+
return [];
|
|
3841
|
+
}
|
|
3842
|
+
};
|
|
3843
|
+
const maybeLoadOlderHistory = () => {
|
|
3844
|
+
if (!datafeed || datafeedLoading || datafeedExhausted || data.length === 0) return;
|
|
3845
|
+
const options2 = mergedOptions.datafeedOptions ?? DEFAULT_OPTIONS.datafeedOptions;
|
|
3846
|
+
const threshold = options2?.prefetchThresholdBars ?? 150;
|
|
3847
|
+
const cooldown = options2?.cooldownMs ?? 1200;
|
|
3848
|
+
if (Date.now() - datafeedLastRequestAt < cooldown) return;
|
|
3849
|
+
if (xCenter - xSpan / 2 > threshold) return;
|
|
3850
|
+
const stepMs = estimateBarStepMs();
|
|
3851
|
+
const chunk = Math.max(10, options2?.chunkBars ?? 1500);
|
|
3852
|
+
const toMs = data[0].time.getTime();
|
|
3853
|
+
datafeedLoading = true;
|
|
3854
|
+
datafeedLastRequestAt = Date.now();
|
|
3855
|
+
void requestDatafeedBars({
|
|
3856
|
+
fromMs: toMs - chunk * stepMs,
|
|
3857
|
+
toMs,
|
|
3858
|
+
countBack: chunk,
|
|
3859
|
+
firstRequest: false
|
|
3860
|
+
}).then((bars) => {
|
|
3861
|
+
const older = bars.filter((bar) => new Date(bar.t).getTime() < toMs);
|
|
3862
|
+
if (older.length === 0) {
|
|
3863
|
+
datafeedExhausted = true;
|
|
3864
|
+
return;
|
|
3865
|
+
}
|
|
3866
|
+
const merged = [...older, ...data.map((bar) => ({
|
|
3867
|
+
t: bar.time.toISOString(),
|
|
3868
|
+
o: bar.o,
|
|
3869
|
+
h: bar.h,
|
|
3870
|
+
l: bar.l,
|
|
3871
|
+
c: bar.c,
|
|
3872
|
+
...bar.v === void 0 ? {} : { v: bar.v }
|
|
3873
|
+
}))];
|
|
3874
|
+
setData(merged);
|
|
3875
|
+
}).finally(() => {
|
|
3876
|
+
datafeedLoading = false;
|
|
3877
|
+
});
|
|
3878
|
+
};
|
|
3879
|
+
const setDatafeed = async (nextDatafeed) => {
|
|
3880
|
+
datafeedUnsubscribe?.();
|
|
3881
|
+
datafeedUnsubscribe = null;
|
|
3882
|
+
datafeed = nextDatafeed;
|
|
3883
|
+
datafeedExhausted = false;
|
|
3884
|
+
datafeedLoading = false;
|
|
3885
|
+
datafeedLastRequestAt = 0;
|
|
3886
|
+
if (!nextDatafeed) return;
|
|
3887
|
+
await nextDatafeed.onReady?.();
|
|
3888
|
+
if (datafeed !== nextDatafeed) return;
|
|
3889
|
+
const stepMs = estimateBarStepMs();
|
|
3890
|
+
const countBack = Math.max(50, Math.round(mergedOptions.initialVisibleBars * 3));
|
|
3891
|
+
const toMs = Date.now();
|
|
3892
|
+
datafeedLoading = true;
|
|
3893
|
+
const bars = await requestDatafeedBars({
|
|
3894
|
+
fromMs: toMs - countBack * stepMs,
|
|
3895
|
+
toMs,
|
|
3896
|
+
countBack,
|
|
3897
|
+
firstRequest: true
|
|
3898
|
+
});
|
|
3899
|
+
datafeedLoading = false;
|
|
3900
|
+
if (datafeed !== nextDatafeed) return;
|
|
3901
|
+
if (bars.length > 0) {
|
|
3902
|
+
setData(bars);
|
|
3903
|
+
}
|
|
3904
|
+
const unsubscribe = nextDatafeed.subscribeBars?.((bar) => {
|
|
3905
|
+
if (datafeed === nextDatafeed) upsertBar(bar);
|
|
3906
|
+
});
|
|
3907
|
+
datafeedUnsubscribe = typeof unsubscribe === "function" ? unsubscribe : null;
|
|
3908
|
+
};
|
|
3749
3909
|
const formatHoverTimeLabel = (time, mode) => {
|
|
3750
3910
|
if (mode === "time") {
|
|
3751
3911
|
return time.toLocaleTimeString(void 0, {
|
|
@@ -4351,6 +4511,31 @@ function createChart(element, options = {}) {
|
|
|
4351
4511
|
const chartType = mergedOptions.chartType;
|
|
4352
4512
|
const seriesData = chartType === "heikin-ashi" ? getHeikinAshiData() : data;
|
|
4353
4513
|
const isLineStyleChart = chartType === "line" || chartType === "area" || chartType === "baseline";
|
|
4514
|
+
const comparePlans = [];
|
|
4515
|
+
for (const state of visibleCompareSeries()) {
|
|
4516
|
+
const aligned = getAlignedCompareValues(state);
|
|
4517
|
+
let anchorIndex = -1;
|
|
4518
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
4519
|
+
if (Number.isFinite(aligned[index] ?? Number.NaN)) {
|
|
4520
|
+
anchorIndex = index;
|
|
4521
|
+
break;
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
if (anchorIndex < 0) continue;
|
|
4525
|
+
const baseCompare = aligned[anchorIndex];
|
|
4526
|
+
const baseMain = seriesData[anchorIndex]?.c ?? 0;
|
|
4527
|
+
const usePercent = (state.options.scale ?? "percent") !== "price";
|
|
4528
|
+
const toPrice = usePercent && baseCompare > 0 ? (value) => baseMain * value / baseCompare : (value) => value;
|
|
4529
|
+
let lastNativeValue = Number.NaN;
|
|
4530
|
+
for (let index = endIndex; index >= startIndex; index -= 1) {
|
|
4531
|
+
const value = aligned[index];
|
|
4532
|
+
if (value !== void 0 && Number.isFinite(value)) {
|
|
4533
|
+
lastNativeValue = value;
|
|
4534
|
+
break;
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
comparePlans.push({ state, aligned, toPrice, usePercent, baseCompare, lastNativeValue });
|
|
4538
|
+
}
|
|
4354
4539
|
const skipLatestIndex = mergedOptions.autoScaleIgnoreLatestCandle && data.length > 1 ? data.length - 1 : -1;
|
|
4355
4540
|
const scanCandleRange = (from, to) => {
|
|
4356
4541
|
let min = Number.POSITIVE_INFINITY;
|
|
@@ -4396,6 +4581,23 @@ function createChart(element, options = {}) {
|
|
|
4396
4581
|
maxPrice = Math.max(maxPrice, weightedMax);
|
|
4397
4582
|
}
|
|
4398
4583
|
}
|
|
4584
|
+
for (const plan of comparePlans) {
|
|
4585
|
+
if (plan.state.options.includeInAutoScale === false) continue;
|
|
4586
|
+
let compareMin = Number.POSITIVE_INFINITY;
|
|
4587
|
+
let compareMax = Number.NEGATIVE_INFINITY;
|
|
4588
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
4589
|
+
const value = plan.aligned[index];
|
|
4590
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
4591
|
+
const price = plan.toPrice(value);
|
|
4592
|
+
if (!Number.isFinite(price)) continue;
|
|
4593
|
+
if (price < compareMin) compareMin = price;
|
|
4594
|
+
if (price > compareMax) compareMax = price;
|
|
4595
|
+
}
|
|
4596
|
+
if (compareMin <= compareMax) {
|
|
4597
|
+
minPrice = Math.min(minPrice, compareMin);
|
|
4598
|
+
maxPrice = Math.max(maxPrice, compareMax);
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4399
4601
|
const priceRange = maxPrice - minPrice || 1;
|
|
4400
4602
|
const autoMin = minPrice - priceRange * 0.08;
|
|
4401
4603
|
const autoMax = maxPrice + priceRange * 0.08;
|
|
@@ -5507,7 +5709,106 @@ function createChart(element, options = {}) {
|
|
|
5507
5709
|
}
|
|
5508
5710
|
return data[index]?.v;
|
|
5509
5711
|
};
|
|
5510
|
-
|
|
5712
|
+
const downsamplingOptions = mergedOptions.downsampling ?? DEFAULT_OPTIONS.downsampling;
|
|
5713
|
+
const downsampleThresholdPx = Math.max(0.2, downsamplingOptions?.thresholdPx ?? 1.5);
|
|
5714
|
+
const shouldDownsample = (downsamplingOptions?.enabled ?? true) && candleSpacing < downsampleThresholdPx && endIndex - startIndex > 2;
|
|
5715
|
+
const seriesColumns = [];
|
|
5716
|
+
if (shouldDownsample) {
|
|
5717
|
+
let current = null;
|
|
5718
|
+
let currentColumn = Number.NaN;
|
|
5719
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
5720
|
+
const point = seriesData[index];
|
|
5721
|
+
if (!point) continue;
|
|
5722
|
+
const column = Math.round(chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth);
|
|
5723
|
+
if (!current || column !== currentColumn) {
|
|
5724
|
+
currentColumn = column;
|
|
5725
|
+
current = {
|
|
5726
|
+
x: column,
|
|
5727
|
+
open: point.o,
|
|
5728
|
+
high: point.h,
|
|
5729
|
+
low: point.l,
|
|
5730
|
+
close: point.c,
|
|
5731
|
+
minClose: point.c,
|
|
5732
|
+
maxClose: point.c,
|
|
5733
|
+
minCloseIndex: index,
|
|
5734
|
+
maxCloseIndex: index
|
|
5735
|
+
};
|
|
5736
|
+
seriesColumns.push(current);
|
|
5737
|
+
continue;
|
|
5738
|
+
}
|
|
5739
|
+
if (point.h > current.high) current.high = point.h;
|
|
5740
|
+
if (point.l < current.low) current.low = point.l;
|
|
5741
|
+
current.close = point.c;
|
|
5742
|
+
if (point.c < current.minClose) {
|
|
5743
|
+
current.minClose = point.c;
|
|
5744
|
+
current.minCloseIndex = index;
|
|
5745
|
+
}
|
|
5746
|
+
if (point.c > current.maxClose) {
|
|
5747
|
+
current.maxClose = point.c;
|
|
5748
|
+
current.maxCloseIndex = index;
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
if (shouldDownsample && !isLineStyleChart) {
|
|
5753
|
+
const upColumns = new Path2D();
|
|
5754
|
+
const downColumns = new Path2D();
|
|
5755
|
+
for (const column of seriesColumns) {
|
|
5756
|
+
const path = column.close >= column.open ? upColumns : downColumns;
|
|
5757
|
+
const x = column.x + 0.5;
|
|
5758
|
+
const top = crisp(yFromPrice(column.high));
|
|
5759
|
+
const bottom = crisp(yFromPrice(column.low));
|
|
5760
|
+
path.moveTo(x, top);
|
|
5761
|
+
path.lineTo(x, bottom === top ? bottom + 1 : bottom);
|
|
5762
|
+
}
|
|
5763
|
+
ctx.lineWidth = 1;
|
|
5764
|
+
ctx.strokeStyle = mergedOptions.upColor;
|
|
5765
|
+
ctx.stroke(upColumns);
|
|
5766
|
+
ctx.strokeStyle = mergedOptions.downColor;
|
|
5767
|
+
ctx.stroke(downColumns);
|
|
5768
|
+
} else if (shouldDownsample && isLineStyleChart) {
|
|
5769
|
+
const seriesLineWidth = Math.max(1, mergedOptions.lineWidth);
|
|
5770
|
+
const linePath = new Path2D();
|
|
5771
|
+
let started = false;
|
|
5772
|
+
let firstX = 0;
|
|
5773
|
+
let lastX = 0;
|
|
5774
|
+
for (const column of seriesColumns) {
|
|
5775
|
+
const lowFirst = column.minCloseIndex <= column.maxCloseIndex;
|
|
5776
|
+
const first = lowFirst ? column.minClose : column.maxClose;
|
|
5777
|
+
const second = lowFirst ? column.maxClose : column.minClose;
|
|
5778
|
+
const x = column.x;
|
|
5779
|
+
if (!started) {
|
|
5780
|
+
linePath.moveTo(x, yFromPrice(first));
|
|
5781
|
+
firstX = x;
|
|
5782
|
+
started = true;
|
|
5783
|
+
} else {
|
|
5784
|
+
linePath.lineTo(x, yFromPrice(first));
|
|
5785
|
+
}
|
|
5786
|
+
if (second !== first) {
|
|
5787
|
+
linePath.lineTo(x, yFromPrice(second));
|
|
5788
|
+
}
|
|
5789
|
+
lastX = x;
|
|
5790
|
+
}
|
|
5791
|
+
if (started) {
|
|
5792
|
+
if (chartType === "area" || chartType === "baseline") {
|
|
5793
|
+
const baseY = chartType === "baseline" ? yFromPrice(mergedOptions.baselinePrice ?? (yMin + yMax) / 2) : chartBottom;
|
|
5794
|
+
const fillPath = new Path2D(linePath);
|
|
5795
|
+
fillPath.lineTo(lastX, baseY);
|
|
5796
|
+
fillPath.lineTo(firstX, baseY);
|
|
5797
|
+
fillPath.closePath();
|
|
5798
|
+
ctx.save();
|
|
5799
|
+
ctx.globalAlpha = Math.max(0, Math.min(1, mergedOptions.areaFillOpacity));
|
|
5800
|
+
ctx.fillStyle = chartType === "baseline" ? mergedOptions.upColor : mergedOptions.lineColor;
|
|
5801
|
+
ctx.fill(fillPath);
|
|
5802
|
+
ctx.restore();
|
|
5803
|
+
}
|
|
5804
|
+
ctx.save();
|
|
5805
|
+
ctx.strokeStyle = chartType === "baseline" ? mergedOptions.upColor : mergedOptions.lineColor;
|
|
5806
|
+
ctx.lineWidth = seriesLineWidth;
|
|
5807
|
+
ctx.lineJoin = "round";
|
|
5808
|
+
ctx.stroke(linePath);
|
|
5809
|
+
ctx.restore();
|
|
5810
|
+
}
|
|
5811
|
+
} else if (isLineStyleChart) {
|
|
5511
5812
|
const seriesLineWidth = Math.max(1, mergedOptions.lineWidth);
|
|
5512
5813
|
const linePath = new Path2D();
|
|
5513
5814
|
let started = false;
|
|
@@ -5673,6 +5974,105 @@ function createChart(element, options = {}) {
|
|
|
5673
5974
|
ctx.stroke(downHollowBodies);
|
|
5674
5975
|
}
|
|
5675
5976
|
}
|
|
5977
|
+
const compareAxisTags = [];
|
|
5978
|
+
for (const plan of comparePlans) {
|
|
5979
|
+
const color = plan.state.options.color ?? "#f59e0b";
|
|
5980
|
+
const lineWidth = Math.max(1, plan.state.options.lineWidth ?? 1.5);
|
|
5981
|
+
const path = new Path2D();
|
|
5982
|
+
let started = false;
|
|
5983
|
+
let firstX = 0;
|
|
5984
|
+
let lastX = 0;
|
|
5985
|
+
let lastY = 0;
|
|
5986
|
+
let lastPrice = Number.NaN;
|
|
5987
|
+
const emit = (x, y) => {
|
|
5988
|
+
if (!started) {
|
|
5989
|
+
path.moveTo(x, y);
|
|
5990
|
+
firstX = x;
|
|
5991
|
+
started = true;
|
|
5992
|
+
} else {
|
|
5993
|
+
path.lineTo(x, y);
|
|
5994
|
+
}
|
|
5995
|
+
lastX = x;
|
|
5996
|
+
lastY = y;
|
|
5997
|
+
};
|
|
5998
|
+
const xForIndex = (index) => chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
5999
|
+
if (shouldDownsample) {
|
|
6000
|
+
let column = Number.NaN;
|
|
6001
|
+
let minValue = 0;
|
|
6002
|
+
let maxValue = 0;
|
|
6003
|
+
let minIndex = 0;
|
|
6004
|
+
let maxIndex = 0;
|
|
6005
|
+
let hasColumn = false;
|
|
6006
|
+
const flushColumn = () => {
|
|
6007
|
+
if (!hasColumn) return;
|
|
6008
|
+
const lowFirst = minIndex <= maxIndex;
|
|
6009
|
+
const first = plan.toPrice(lowFirst ? minValue : maxValue);
|
|
6010
|
+
const second = plan.toPrice(lowFirst ? maxValue : minValue);
|
|
6011
|
+
if (Number.isFinite(first)) emit(column, yFromPrice(first));
|
|
6012
|
+
if (Number.isFinite(second) && second !== first) emit(column, yFromPrice(second));
|
|
6013
|
+
lastPrice = plan.toPrice(maxIndex >= minIndex ? maxValue : minValue);
|
|
6014
|
+
};
|
|
6015
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
6016
|
+
const value = plan.aligned[index];
|
|
6017
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
6018
|
+
const nextColumn = Math.round(xForIndex(index));
|
|
6019
|
+
if (!hasColumn || nextColumn !== column) {
|
|
6020
|
+
flushColumn();
|
|
6021
|
+
column = nextColumn;
|
|
6022
|
+
minValue = value;
|
|
6023
|
+
maxValue = value;
|
|
6024
|
+
minIndex = index;
|
|
6025
|
+
maxIndex = index;
|
|
6026
|
+
hasColumn = true;
|
|
6027
|
+
continue;
|
|
6028
|
+
}
|
|
6029
|
+
if (value < minValue) {
|
|
6030
|
+
minValue = value;
|
|
6031
|
+
minIndex = index;
|
|
6032
|
+
}
|
|
6033
|
+
if (value > maxValue) {
|
|
6034
|
+
maxValue = value;
|
|
6035
|
+
maxIndex = index;
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
flushColumn();
|
|
6039
|
+
} else {
|
|
6040
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
6041
|
+
const value = plan.aligned[index];
|
|
6042
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
6043
|
+
const price = plan.toPrice(value);
|
|
6044
|
+
if (!Number.isFinite(price)) continue;
|
|
6045
|
+
lastPrice = price;
|
|
6046
|
+
emit(xForIndex(index), yFromPrice(price));
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
if (!started) continue;
|
|
6050
|
+
if (plan.state.options.style === "area") {
|
|
6051
|
+
const fillPath = new Path2D(path);
|
|
6052
|
+
fillPath.lineTo(lastX, chartBottom);
|
|
6053
|
+
fillPath.lineTo(firstX, chartBottom);
|
|
6054
|
+
fillPath.closePath();
|
|
6055
|
+
ctx.save();
|
|
6056
|
+
ctx.globalAlpha = Math.max(0, Math.min(1, mergedOptions.areaFillOpacity)) * 0.6;
|
|
6057
|
+
ctx.fillStyle = color;
|
|
6058
|
+
ctx.fill(fillPath);
|
|
6059
|
+
ctx.restore();
|
|
6060
|
+
}
|
|
6061
|
+
ctx.save();
|
|
6062
|
+
ctx.strokeStyle = color;
|
|
6063
|
+
ctx.lineWidth = lineWidth;
|
|
6064
|
+
ctx.lineJoin = "round";
|
|
6065
|
+
ctx.stroke(path);
|
|
6066
|
+
ctx.restore();
|
|
6067
|
+
if (Number.isFinite(lastPrice)) {
|
|
6068
|
+
const label = plan.state.options.label ?? plan.state.options.id;
|
|
6069
|
+
const nativeLast = plan.lastNativeValue;
|
|
6070
|
+
const usePercentTag = plan.usePercent && plan.baseCompare > 0 && Number.isFinite(nativeLast);
|
|
6071
|
+
const changePct = usePercentTag ? (nativeLast / plan.baseCompare - 1) * 100 : 0;
|
|
6072
|
+
const shortText = usePercentTag ? `${changePct >= 0 ? "+" : ""}${changePct.toFixed(2)}%` : formatPrice(nativeLast);
|
|
6073
|
+
compareAxisTags.push({ y: lastY, text: `${label} ${shortText}`, shortText, color });
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
5676
6076
|
const activeOverlayIndicators = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
5677
6077
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
5678
6078
|
).sort((a, b) => a.indicator.zIndex - b.indicator.zIndex);
|
|
@@ -6447,6 +6847,35 @@ function createChart(element, options = {}) {
|
|
|
6447
6847
|
drawText(countdownText, chartRight + 6, (fullChartBottom + height) / 2, "left", "middle", xAxis.textColor);
|
|
6448
6848
|
ctx.font = prevFont;
|
|
6449
6849
|
}
|
|
6850
|
+
if (compareAxisTags.length > 0 && width - chartRight > 0) {
|
|
6851
|
+
const prevFont = ctx.font;
|
|
6852
|
+
const tagFontSize = Math.max(9, (resolvedAxis.fontSize ?? 12) - 2);
|
|
6853
|
+
ctx.font = `${tagFontSize}px ${mergedOptions.fontFamily}`;
|
|
6854
|
+
ctx.textBaseline = "middle";
|
|
6855
|
+
ctx.textAlign = "left";
|
|
6856
|
+
const placed = [];
|
|
6857
|
+
const tagHeight = tagFontSize + 6;
|
|
6858
|
+
const gutterWidth = width - chartRight - 4;
|
|
6859
|
+
for (const tag of [...compareAxisTags].sort((a, b) => a.y - b.y)) {
|
|
6860
|
+
const text = measureTextWidth(tag.text) + 10 <= gutterWidth ? tag.text : tag.shortText;
|
|
6861
|
+
const tagWidth = Math.min(gutterWidth, measureTextWidth(text) + 10);
|
|
6862
|
+
if (tagWidth <= 0) continue;
|
|
6863
|
+
let tagY = clamp(tag.y - tagHeight / 2, chartTop, chartBottom - tagHeight);
|
|
6864
|
+
for (const slot of placed) {
|
|
6865
|
+
if (tagY < slot.bottom + 2 && tagY + tagHeight > slot.top - 2) {
|
|
6866
|
+
tagY = slot.bottom + 2;
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
placed.push({ top: tagY, bottom: tagY + tagHeight });
|
|
6870
|
+
ctx.save();
|
|
6871
|
+
ctx.fillStyle = tag.color;
|
|
6872
|
+
fillRoundedRect(chartRight + 2, Math.round(tagY), Math.round(tagWidth), tagHeight, 3);
|
|
6873
|
+
ctx.fillStyle = labelTextColorOn(tag.color, "#ffffff");
|
|
6874
|
+
ctx.fillText(text, chartRight + 7, Math.round(tagY + tagHeight / 2));
|
|
6875
|
+
ctx.restore();
|
|
6876
|
+
}
|
|
6877
|
+
ctx.font = prevFont;
|
|
6878
|
+
}
|
|
6450
6879
|
if (baseCtx) {
|
|
6451
6880
|
if (baseCanvas.width !== canvas.width || baseCanvas.height !== canvas.height) {
|
|
6452
6881
|
baseCanvas.width = canvas.width;
|
|
@@ -6471,6 +6900,7 @@ function createChart(element, options = {}) {
|
|
|
6471
6900
|
};
|
|
6472
6901
|
paintCrosshairOverlay();
|
|
6473
6902
|
emitSelectionChangeIfMoved();
|
|
6903
|
+
maybeLoadOlderHistory();
|
|
6474
6904
|
};
|
|
6475
6905
|
const paintCrosshairOverlay = () => {
|
|
6476
6906
|
crosshairPriceActionRegion = null;
|
|
@@ -6663,6 +7093,67 @@ function createChart(element, options = {}) {
|
|
|
6663
7093
|
drawText(timeText, timeX + labelPaddingX, timeY + timeHeight / 2, "left", "middle", labelTextColor);
|
|
6664
7094
|
}
|
|
6665
7095
|
}
|
|
7096
|
+
if (longPressState && (mergedOptions.touch?.longPressTooltip ?? true)) {
|
|
7097
|
+
const index = indexFromCanvasX(cx);
|
|
7098
|
+
const bar = index === null ? void 0 : data[index];
|
|
7099
|
+
if (bar) {
|
|
7100
|
+
const barTime = getTimeForIndex(index);
|
|
7101
|
+
const changeAbs = bar.c - bar.o;
|
|
7102
|
+
const changePct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
|
|
7103
|
+
const rows = [
|
|
7104
|
+
["O", formatPrice(bar.o), null],
|
|
7105
|
+
["H", formatPrice(bar.h), null],
|
|
7106
|
+
["L", formatPrice(bar.l), null],
|
|
7107
|
+
["C", formatPrice(bar.c), null],
|
|
7108
|
+
[
|
|
7109
|
+
"Chg",
|
|
7110
|
+
`${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${changePct.toFixed(2)}%)`,
|
|
7111
|
+
changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor
|
|
7112
|
+
]
|
|
7113
|
+
];
|
|
7114
|
+
if (bar.v !== void 0) {
|
|
7115
|
+
rows.push([
|
|
7116
|
+
"Vol",
|
|
7117
|
+
bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v)),
|
|
7118
|
+
null
|
|
7119
|
+
]);
|
|
7120
|
+
}
|
|
7121
|
+
const prevFont = ctx.font;
|
|
7122
|
+
const fontSize = 11;
|
|
7123
|
+
ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
|
|
7124
|
+
const headerText = barTime ? formatHoverTimeLabel(barTime, "auto") : "";
|
|
7125
|
+
const rowGap = 3;
|
|
7126
|
+
const padding = 8;
|
|
7127
|
+
const labelColumn = 26;
|
|
7128
|
+
const contentWidth = Math.max(
|
|
7129
|
+
measureTextWidth(headerText),
|
|
7130
|
+
...rows.map(([, value]) => labelColumn + measureTextWidth(value))
|
|
7131
|
+
);
|
|
7132
|
+
const boxWidth = Math.ceil(contentWidth) + padding * 2;
|
|
7133
|
+
const boxHeight = padding * 2 + (fontSize + rowGap) * (rows.length + (headerText ? 1 : 0)) - rowGap;
|
|
7134
|
+
const fingerGap = 26;
|
|
7135
|
+
const boxX = cx + fingerGap + boxWidth <= chartRight ? cx + fingerGap : Math.max(chartLeft + 4, cx - fingerGap - boxWidth);
|
|
7136
|
+
const boxY = clamp(cy - boxHeight - fingerGap, chartTop + 4, chartBottom - boxHeight - 4);
|
|
7137
|
+
ctx.save();
|
|
7138
|
+
ctx.fillStyle = "rgba(16,17,20,0.94)";
|
|
7139
|
+
fillRoundedRect(Math.round(boxX), Math.round(boxY), boxWidth, boxHeight, 6);
|
|
7140
|
+
ctx.strokeStyle = "rgba(255,255,255,0.14)";
|
|
7141
|
+
ctx.lineWidth = 1;
|
|
7142
|
+
ctx.strokeRect(Math.round(boxX) + 0.5, Math.round(boxY) + 0.5, boxWidth - 1, boxHeight - 1);
|
|
7143
|
+
let textY = boxY + padding + fontSize / 2;
|
|
7144
|
+
if (headerText) {
|
|
7145
|
+
drawText(headerText, boxX + padding, textY, "left", "middle", "rgba(255,255,255,0.55)");
|
|
7146
|
+
textY += fontSize + rowGap;
|
|
7147
|
+
}
|
|
7148
|
+
for (const [label, value, valueColor] of rows) {
|
|
7149
|
+
drawText(label, boxX + padding, textY, "left", "middle", "rgba(255,255,255,0.5)");
|
|
7150
|
+
drawText(value, boxX + padding + labelColumn, textY, "left", "middle", valueColor ?? "#e6e8ee");
|
|
7151
|
+
textY += fontSize + rowGap;
|
|
7152
|
+
}
|
|
7153
|
+
ctx.restore();
|
|
7154
|
+
ctx.font = prevFont;
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
6666
7157
|
};
|
|
6667
7158
|
const drawOverlayOnly = () => {
|
|
6668
7159
|
if (!baseCtx || !overlayLayout || baseCanvas.width !== canvas.width || baseCanvas.height !== canvas.height || baseCanvas.width === 0) {
|
|
@@ -7172,6 +7663,7 @@ function createChart(element, options = {}) {
|
|
|
7172
7663
|
return Math.hypot(x - (x1 + t * dx), y - (y1 + t * dy));
|
|
7173
7664
|
};
|
|
7174
7665
|
const getDrawingHit = (x, y) => {
|
|
7666
|
+
const hitTolerance = touchInputActive ? Math.max(1, mergedOptions.touch?.hitToleranceScale ?? 2.2) : 1;
|
|
7175
7667
|
for (let index = drawings.length - 1; index >= 0; index -= 1) {
|
|
7176
7668
|
const drawing = drawings[index];
|
|
7177
7669
|
if (!drawing?.visible) continue;
|
|
@@ -7180,10 +7672,10 @@ function createChart(element, options = {}) {
|
|
|
7180
7672
|
if (!point) continue;
|
|
7181
7673
|
const lineY = canvasYFromDrawingPrice(point.price);
|
|
7182
7674
|
const handleX = drawState ? drawState.chartRight - 64 : 0;
|
|
7183
|
-
if (Math.hypot(x - handleX, y - lineY) <= 8) {
|
|
7675
|
+
if (Math.hypot(x - handleX, y - lineY) <= 8 * hitTolerance) {
|
|
7184
7676
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7185
7677
|
}
|
|
7186
|
-
if (Math.abs(y - lineY) <= 7) {
|
|
7678
|
+
if (Math.abs(y - lineY) <= 7 * hitTolerance) {
|
|
7187
7679
|
return { drawing, target: "line" };
|
|
7188
7680
|
}
|
|
7189
7681
|
} else if (drawing.type === "vertical-line") {
|
|
@@ -7191,10 +7683,10 @@ function createChart(element, options = {}) {
|
|
|
7191
7683
|
if (!point) continue;
|
|
7192
7684
|
const lineX = canvasXFromDrawingPoint(point);
|
|
7193
7685
|
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
7194
|
-
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
7686
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8 * hitTolerance) {
|
|
7195
7687
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7196
7688
|
}
|
|
7197
|
-
if (Math.abs(x - lineX) <= 7) {
|
|
7689
|
+
if (Math.abs(x - lineX) <= 7 * hitTolerance) {
|
|
7198
7690
|
return { drawing, target: "line" };
|
|
7199
7691
|
}
|
|
7200
7692
|
} else if (drawing.type === "trendline") {
|
|
@@ -7205,13 +7697,13 @@ function createChart(element, options = {}) {
|
|
|
7205
7697
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7206
7698
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7207
7699
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7208
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7700
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7209
7701
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7210
7702
|
}
|
|
7211
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7703
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7212
7704
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7213
7705
|
}
|
|
7214
|
-
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
7706
|
+
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
|
|
7215
7707
|
return { drawing, target: "line" };
|
|
7216
7708
|
}
|
|
7217
7709
|
} else if (drawing.type === "ray") {
|
|
@@ -7222,10 +7714,10 @@ function createChart(element, options = {}) {
|
|
|
7222
7714
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7223
7715
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7224
7716
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7225
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7717
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7226
7718
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7227
7719
|
}
|
|
7228
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7720
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7229
7721
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7230
7722
|
}
|
|
7231
7723
|
const dx = x2 - x1;
|
|
@@ -7235,7 +7727,7 @@ function createChart(element, options = {}) {
|
|
|
7235
7727
|
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
7236
7728
|
const projX = x1 + t * dx;
|
|
7237
7729
|
const projY = y1 + t * dy;
|
|
7238
|
-
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
7730
|
+
if (Math.hypot(x - projX, y - projY) <= 6 * hitTolerance) {
|
|
7239
7731
|
return { drawing, target: "line" };
|
|
7240
7732
|
}
|
|
7241
7733
|
}
|
|
@@ -7247,17 +7739,17 @@ function createChart(element, options = {}) {
|
|
|
7247
7739
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7248
7740
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7249
7741
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7250
|
-
if (Math.hypot(x - x1, y - y1) <= 9) {
|
|
7742
|
+
if (Math.hypot(x - x1, y - y1) <= 9 * hitTolerance) {
|
|
7251
7743
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7252
7744
|
}
|
|
7253
|
-
if (Math.hypot(x - x2, y - y2) <= 9) {
|
|
7745
|
+
if (Math.hypot(x - x2, y - y2) <= 9 * hitTolerance) {
|
|
7254
7746
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7255
7747
|
}
|
|
7256
7748
|
const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
7257
7749
|
for (const ratio of fibRatios) {
|
|
7258
7750
|
const price = first.price + (second.price - first.price) * (1 - ratio);
|
|
7259
7751
|
const lineY = canvasYFromDrawingPrice(price);
|
|
7260
|
-
if (Math.abs(y - lineY) <= 5) {
|
|
7752
|
+
if (Math.abs(y - lineY) <= 5 * hitTolerance) {
|
|
7261
7753
|
return { drawing, target: "line" };
|
|
7262
7754
|
}
|
|
7263
7755
|
}
|
|
@@ -7270,30 +7762,30 @@ function createChart(element, options = {}) {
|
|
|
7270
7762
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7271
7763
|
const x1 = canvasXFromDrawingPoint(p1);
|
|
7272
7764
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7273
|
-
if (Math.hypot(x - x0, y - y0) <= 8) {
|
|
7765
|
+
if (Math.hypot(x - x0, y - y0) <= 8 * hitTolerance) {
|
|
7274
7766
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7275
7767
|
}
|
|
7276
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7768
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7277
7769
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7278
7770
|
}
|
|
7279
7771
|
if (p2) {
|
|
7280
7772
|
const x2 = canvasXFromDrawingPoint(p2);
|
|
7281
7773
|
const y2 = canvasYFromDrawingPrice(p2.price);
|
|
7282
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7774
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7283
7775
|
return { drawing, target: "handle", pointIndex: 2 };
|
|
7284
7776
|
}
|
|
7285
7777
|
const move = p1.price - p0.price;
|
|
7286
7778
|
const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
|
|
7287
7779
|
for (const ratio of fibExtRatios) {
|
|
7288
7780
|
const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
|
|
7289
|
-
if (Math.abs(y - lineY) <= 5) {
|
|
7781
|
+
if (Math.abs(y - lineY) <= 5 * hitTolerance) {
|
|
7290
7782
|
return { drawing, target: "line" };
|
|
7291
7783
|
}
|
|
7292
7784
|
}
|
|
7293
|
-
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 || distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
7785
|
+
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
|
|
7294
7786
|
return { drawing, target: "line" };
|
|
7295
7787
|
}
|
|
7296
|
-
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6) {
|
|
7788
|
+
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance) {
|
|
7297
7789
|
return { drawing, target: "line" };
|
|
7298
7790
|
}
|
|
7299
7791
|
} else if (drawing.type === "long-position" || drawing.type === "short-position") {
|
|
@@ -7307,10 +7799,10 @@ function createChart(element, options = {}) {
|
|
|
7307
7799
|
const entryY = canvasYFromDrawingPrice(entry.price);
|
|
7308
7800
|
const targetY = canvasYFromDrawingPrice(target.price);
|
|
7309
7801
|
const stopY = canvasYFromDrawingPrice(stop.price);
|
|
7310
|
-
if (Math.hypot(x - leftX, y - targetY) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7311
|
-
if (Math.hypot(x - leftX, y - entryY) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7312
|
-
if (Math.hypot(x - leftX, y - stopY) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
7313
|
-
if (Math.hypot(x - rightX, y - entryY) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
7802
|
+
if (Math.hypot(x - leftX, y - targetY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7803
|
+
if (Math.hypot(x - leftX, y - entryY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7804
|
+
if (Math.hypot(x - leftX, y - stopY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7805
|
+
if (Math.hypot(x - rightX, y - entryY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 3 };
|
|
7314
7806
|
const x0 = Math.min(leftX, rightX);
|
|
7315
7807
|
const x1 = Math.max(leftX, rightX);
|
|
7316
7808
|
const yTop = Math.min(targetY, stopY, entryY);
|
|
@@ -7326,10 +7818,10 @@ function createChart(element, options = {}) {
|
|
|
7326
7818
|
const px1 = canvasXFromDrawingPoint(p1);
|
|
7327
7819
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7328
7820
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7329
|
-
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7330
|
-
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7331
|
-
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
7332
|
-
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
7821
|
+
if (Math.hypot(x - px0, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7822
|
+
if (Math.hypot(x - px1, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7823
|
+
if (Math.hypot(x - px0, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7824
|
+
if (Math.hypot(x - px1, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 3 };
|
|
7333
7825
|
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
7334
7826
|
return { drawing, target: "line" };
|
|
7335
7827
|
}
|
|
@@ -7341,16 +7833,16 @@ function createChart(element, options = {}) {
|
|
|
7341
7833
|
const px1 = canvasXFromDrawingPoint(p1);
|
|
7342
7834
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7343
7835
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7344
|
-
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7345
|
-
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7346
|
-
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
7347
|
-
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
7836
|
+
if (Math.hypot(x - px0, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7837
|
+
if (Math.hypot(x - px1, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7838
|
+
if (Math.hypot(x - px0, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7839
|
+
if (Math.hypot(x - px1, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 3 };
|
|
7348
7840
|
const cx = (px0 + px1) / 2;
|
|
7349
7841
|
const cy = (y0 + y1) / 2;
|
|
7350
7842
|
const rx = Math.max(1, Math.abs(px1 - px0) / 2);
|
|
7351
7843
|
const ry = Math.max(1, Math.abs(y1 - y0) / 2);
|
|
7352
7844
|
const norm = ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2;
|
|
7353
|
-
if (norm <= 1) {
|
|
7845
|
+
if (norm <= 1 * hitTolerance) {
|
|
7354
7846
|
return { drawing, target: "line" };
|
|
7355
7847
|
}
|
|
7356
7848
|
} else if (drawing.type === "parallel-channel") {
|
|
@@ -7362,18 +7854,18 @@ function createChart(element, options = {}) {
|
|
|
7362
7854
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7363
7855
|
const x1 = canvasXFromDrawingPoint(p1);
|
|
7364
7856
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7365
|
-
if (Math.hypot(x - x0, y - y0) <= 8) return { drawing, target: "handle", pointIndex: 0 };
|
|
7366
|
-
if (Math.hypot(x - x1, y - y1) <= 8) return { drawing, target: "handle", pointIndex: 1 };
|
|
7857
|
+
if (Math.hypot(x - x0, y - y0) <= 8 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7858
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7367
7859
|
if (p2) {
|
|
7368
7860
|
const x2 = canvasXFromDrawingPoint(p2);
|
|
7369
7861
|
const y2 = canvasYFromDrawingPrice(p2.price);
|
|
7370
|
-
if (Math.hypot(x - x2, y - y2) <= 8) return { drawing, target: "handle", pointIndex: 2 };
|
|
7862
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7371
7863
|
const indexSpan = p1.index - p0.index;
|
|
7372
7864
|
const slope = indexSpan !== 0 ? (p1.price - p0.price) / indexSpan : 0;
|
|
7373
7865
|
const offset = p2.price - (p0.price + slope * (p2.index - p0.index));
|
|
7374
7866
|
const q0y = canvasYFromDrawingPrice(p0.price + offset);
|
|
7375
7867
|
const q1y = canvasYFromDrawingPrice(p1.price + offset);
|
|
7376
|
-
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 || distanceToSegment(x, y, x0, q0y, x1, q1y) <= 6) {
|
|
7868
|
+
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x0, q0y, x1, q1y) <= 6 * hitTolerance) {
|
|
7377
7869
|
return { drawing, target: "line" };
|
|
7378
7870
|
}
|
|
7379
7871
|
const minX = Math.min(x0, x1);
|
|
@@ -7386,7 +7878,7 @@ function createChart(element, options = {}) {
|
|
|
7386
7878
|
return { drawing, target: "line" };
|
|
7387
7879
|
}
|
|
7388
7880
|
}
|
|
7389
|
-
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6) {
|
|
7881
|
+
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance) {
|
|
7390
7882
|
return { drawing, target: "line" };
|
|
7391
7883
|
}
|
|
7392
7884
|
} else if (drawing.type === "arrow") {
|
|
@@ -7397,13 +7889,13 @@ function createChart(element, options = {}) {
|
|
|
7397
7889
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7398
7890
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7399
7891
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7400
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7892
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7401
7893
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7402
7894
|
}
|
|
7403
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7895
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7404
7896
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7405
7897
|
}
|
|
7406
|
-
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
7898
|
+
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
|
|
7407
7899
|
return { drawing, target: "line" };
|
|
7408
7900
|
}
|
|
7409
7901
|
} else if (drawing.type === "brush") {
|
|
@@ -7415,7 +7907,7 @@ function createChart(element, options = {}) {
|
|
|
7415
7907
|
for (let i = 1; i < pts.length; i += 1) {
|
|
7416
7908
|
const nextX = canvasXFromDrawingPoint(pts[i]);
|
|
7417
7909
|
const nextY = canvasYFromDrawingPrice(pts[i].price);
|
|
7418
|
-
if (distanceToSegment(x, y, prevX, prevY, nextX, nextY) <= 7) {
|
|
7910
|
+
if (distanceToSegment(x, y, prevX, prevY, nextX, nextY) <= 7 * hitTolerance) {
|
|
7419
7911
|
hitBody = true;
|
|
7420
7912
|
break;
|
|
7421
7913
|
}
|
|
@@ -7433,8 +7925,8 @@ function createChart(element, options = {}) {
|
|
|
7433
7925
|
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
7434
7926
|
const bx = canvasXFromDrawingPoint(boxPoint);
|
|
7435
7927
|
const by = canvasYFromDrawingPrice(boxPoint.price);
|
|
7436
|
-
if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7437
|
-
if (Math.hypot(x - bx, y - by) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7928
|
+
if (Math.hypot(x - ax, y - ay) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7929
|
+
if (Math.hypot(x - bx, y - by) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7438
7930
|
const fontSize = Math.max(6, drawing.fontSize);
|
|
7439
7931
|
const prevFont = ctx.font;
|
|
7440
7932
|
ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -7450,7 +7942,7 @@ function createChart(element, options = {}) {
|
|
|
7450
7942
|
if (x >= bx && x <= bx + blockW && y >= by && y <= by + blockH) {
|
|
7451
7943
|
return { drawing, target: "line" };
|
|
7452
7944
|
}
|
|
7453
|
-
if (distanceToSegment(x, y, bx + blockW / 2, by + blockH / 2, ax, ay) <= 5) {
|
|
7945
|
+
if (distanceToSegment(x, y, bx + blockW / 2, by + blockH / 2, ax, ay) <= 5 * hitTolerance) {
|
|
7454
7946
|
return { drawing, target: "line" };
|
|
7455
7947
|
}
|
|
7456
7948
|
} else if (drawing.type === "price-range") {
|
|
@@ -7461,8 +7953,8 @@ function createChart(element, options = {}) {
|
|
|
7461
7953
|
const px1 = canvasXFromDrawingPoint(p1);
|
|
7462
7954
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7463
7955
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7464
|
-
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7465
|
-
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7956
|
+
if (Math.hypot(x - px0, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7957
|
+
if (Math.hypot(x - px1, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7466
7958
|
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
7467
7959
|
return { drawing, target: "line" };
|
|
7468
7960
|
}
|
|
@@ -7471,7 +7963,7 @@ function createChart(element, options = {}) {
|
|
|
7471
7963
|
if (!anchor) continue;
|
|
7472
7964
|
const ax = canvasXFromDrawingPoint(anchor);
|
|
7473
7965
|
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
7474
|
-
if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7966
|
+
if (Math.hypot(x - ax, y - ay) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7475
7967
|
const fontSize = Math.max(6, drawing.fontSize);
|
|
7476
7968
|
const prevFont = ctx.font;
|
|
7477
7969
|
ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -7566,8 +8058,9 @@ function createChart(element, options = {}) {
|
|
|
7566
8058
|
if (!drawState || x < drawState.chartLeft || x > drawState.chartRight) {
|
|
7567
8059
|
return null;
|
|
7568
8060
|
}
|
|
8061
|
+
const dividerTolerance = touchInputActive ? 4 * Math.max(1, mergedOptions.touch?.hitToleranceScale ?? 2.2) : 4;
|
|
7569
8062
|
for (const pane of paneLayoutInfos) {
|
|
7570
|
-
if (pane.visible && Math.abs(y - pane.top) <=
|
|
8063
|
+
if (pane.visible && Math.abs(y - pane.top) <= dividerTolerance) {
|
|
7571
8064
|
return { id: pane.id, heightPx: pane.bottom - pane.top };
|
|
7572
8065
|
}
|
|
7573
8066
|
}
|
|
@@ -8060,7 +8553,43 @@ function createChart(element, options = {}) {
|
|
|
8060
8553
|
let lastPointerX = 0;
|
|
8061
8554
|
let lastPointerY = 0;
|
|
8062
8555
|
let activePointerId = null;
|
|
8556
|
+
let touchInputActive = false;
|
|
8063
8557
|
const touchPointers = /* @__PURE__ */ new Map();
|
|
8558
|
+
let longPressTimerId = null;
|
|
8559
|
+
let longPressState = null;
|
|
8560
|
+
const cancelLongPressTimer = () => {
|
|
8561
|
+
if (longPressTimerId !== null) {
|
|
8562
|
+
clearTimeout(longPressTimerId);
|
|
8563
|
+
longPressTimerId = null;
|
|
8564
|
+
}
|
|
8565
|
+
};
|
|
8566
|
+
const emitLongPress = (x, y, scrubbing) => {
|
|
8567
|
+
if (!longPressHandler) return;
|
|
8568
|
+
const rect = canvas.getBoundingClientRect();
|
|
8569
|
+
const index = indexFromCanvasX(x);
|
|
8570
|
+
const barTime = index === null ? null : getTimeForIndex(index);
|
|
8571
|
+
const bar = index === null ? void 0 : data[index];
|
|
8572
|
+
longPressHandler({
|
|
8573
|
+
x,
|
|
8574
|
+
y,
|
|
8575
|
+
clientX: rect.left + x,
|
|
8576
|
+
clientY: rect.top + y,
|
|
8577
|
+
scrubbing,
|
|
8578
|
+
...getHitRegion(x, y) === "plot" ? { price: roundToPricePrecision(priceFromCanvasY(y)) } : {},
|
|
8579
|
+
...index === null ? {} : { index },
|
|
8580
|
+
...barTime ? { time: barTime.toISOString() } : {},
|
|
8581
|
+
...bar ? {
|
|
8582
|
+
point: {
|
|
8583
|
+
t: bar.time.toISOString(),
|
|
8584
|
+
o: bar.o,
|
|
8585
|
+
h: bar.h,
|
|
8586
|
+
l: bar.l,
|
|
8587
|
+
c: bar.c,
|
|
8588
|
+
...bar.v === void 0 ? {} : { v: bar.v }
|
|
8589
|
+
}
|
|
8590
|
+
} : {}
|
|
8591
|
+
});
|
|
8592
|
+
};
|
|
8064
8593
|
let pinchZoomState = null;
|
|
8065
8594
|
const getTouchPair = () => {
|
|
8066
8595
|
const points = Array.from(touchPointers.values());
|
|
@@ -8126,7 +8655,8 @@ function createChart(element, options = {}) {
|
|
|
8126
8655
|
}
|
|
8127
8656
|
};
|
|
8128
8657
|
const onPointerDown = (event) => {
|
|
8129
|
-
|
|
8658
|
+
touchInputActive = event.pointerType === "touch" || event.pointerType === "pen";
|
|
8659
|
+
if (touchInputActive) {
|
|
8130
8660
|
event.preventDefault();
|
|
8131
8661
|
}
|
|
8132
8662
|
if (mergedOptions.accessibility?.focusable !== false && document.activeElement !== canvas) {
|
|
@@ -8143,9 +8673,25 @@ function createChart(element, options = {}) {
|
|
|
8143
8673
|
capturePointer(event.pointerId);
|
|
8144
8674
|
const touchPair = getTouchPair();
|
|
8145
8675
|
if (touchPair) {
|
|
8676
|
+
cancelLongPressTimer();
|
|
8677
|
+
longPressState = null;
|
|
8146
8678
|
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
8147
8679
|
return;
|
|
8148
8680
|
}
|
|
8681
|
+
cancelLongPressTimer();
|
|
8682
|
+
longPressState = null;
|
|
8683
|
+
const touchOptions = mergedOptions.touch ?? {};
|
|
8684
|
+
const wantsTooltip = touchOptions.longPressTooltip !== false || longPressHandler !== null;
|
|
8685
|
+
if (wantsTooltip && !activeDrawingTool && !draftDrawing && getHitRegion(point.x, point.y) === "plot") {
|
|
8686
|
+
const pointerId = event.pointerId;
|
|
8687
|
+
longPressTimerId = window.setTimeout(() => {
|
|
8688
|
+
longPressTimerId = null;
|
|
8689
|
+
longPressState = { pointerId, x: point.x, y: point.y };
|
|
8690
|
+
setCrosshairPoint(point);
|
|
8691
|
+
scheduleDraw({ overlayOnly: true });
|
|
8692
|
+
emitLongPress(point.x, point.y, false);
|
|
8693
|
+
}, Math.max(120, touchOptions.longPressMs ?? 400));
|
|
8694
|
+
}
|
|
8149
8695
|
}
|
|
8150
8696
|
if (measureState) {
|
|
8151
8697
|
if (measureState.dragging) {
|
|
@@ -8311,7 +8857,10 @@ function createChart(element, options = {}) {
|
|
|
8311
8857
|
};
|
|
8312
8858
|
const onPointerMove = (event) => {
|
|
8313
8859
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
8860
|
+
touchInputActive = true;
|
|
8314
8861
|
event.preventDefault();
|
|
8862
|
+
} else if (event.pointerType === "mouse") {
|
|
8863
|
+
touchInputActive = false;
|
|
8315
8864
|
}
|
|
8316
8865
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
8317
8866
|
shiftKeyActive = event.shiftKey;
|
|
@@ -8320,6 +8869,8 @@ function createChart(element, options = {}) {
|
|
|
8320
8869
|
touchPointers.set(event.pointerId, point);
|
|
8321
8870
|
const touchPair = getTouchPair();
|
|
8322
8871
|
if (touchPair) {
|
|
8872
|
+
cancelLongPressTimer();
|
|
8873
|
+
longPressState = null;
|
|
8323
8874
|
if (!pinchZoomState) {
|
|
8324
8875
|
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
8325
8876
|
}
|
|
@@ -8329,6 +8880,21 @@ function createChart(element, options = {}) {
|
|
|
8329
8880
|
if (pinchZoomState) {
|
|
8330
8881
|
return;
|
|
8331
8882
|
}
|
|
8883
|
+
if (longPressState && longPressState.pointerId === event.pointerId) {
|
|
8884
|
+
longPressState = { ...longPressState, x: point.x, y: point.y };
|
|
8885
|
+
setCrosshairPoint(point);
|
|
8886
|
+
emitCrosshairMove(point.x, point.y, getHitRegion(point.x, point.y) === "plot" ? "plot" : "plot");
|
|
8887
|
+
scheduleDraw({ overlayOnly: true });
|
|
8888
|
+
emitLongPress(point.x, point.y, true);
|
|
8889
|
+
return;
|
|
8890
|
+
}
|
|
8891
|
+
if (longPressTimerId !== null) {
|
|
8892
|
+
const dx = point.x - (pointerDownInfo?.x ?? point.x);
|
|
8893
|
+
const dy = point.y - (pointerDownInfo?.y ?? point.y);
|
|
8894
|
+
if (dx * dx + dy * dy > 36) {
|
|
8895
|
+
cancelLongPressTimer();
|
|
8896
|
+
}
|
|
8897
|
+
}
|
|
8332
8898
|
}
|
|
8333
8899
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
8334
8900
|
const dx = point.x - pointerDownInfo.x;
|
|
@@ -8592,6 +9158,18 @@ function createChart(element, options = {}) {
|
|
|
8592
9158
|
lastPointerY = point.y;
|
|
8593
9159
|
};
|
|
8594
9160
|
const endPointerDrag = (event) => {
|
|
9161
|
+
cancelLongPressTimer();
|
|
9162
|
+
if (longPressState && (!event || event.pointerId === longPressState.pointerId)) {
|
|
9163
|
+
longPressState = null;
|
|
9164
|
+
touchPointers.delete(event?.pointerId ?? -1);
|
|
9165
|
+
setCrosshairPoint(null);
|
|
9166
|
+
isDragging = false;
|
|
9167
|
+
dragMode = null;
|
|
9168
|
+
activePointerId = null;
|
|
9169
|
+
pointerDownInfo = null;
|
|
9170
|
+
scheduleDraw({ overlayOnly: true });
|
|
9171
|
+
return;
|
|
9172
|
+
}
|
|
8595
9173
|
if (event?.pointerType === "touch") {
|
|
8596
9174
|
touchPointers.delete(event.pointerId);
|
|
8597
9175
|
if (pinchZoomState) {
|
|
@@ -9408,6 +9986,9 @@ function createChart(element, options = {}) {
|
|
|
9408
9986
|
const onKeyboardShortcut = (handler) => {
|
|
9409
9987
|
keyboardShortcutHandler = handler;
|
|
9410
9988
|
};
|
|
9989
|
+
const onLongPress = (handler) => {
|
|
9990
|
+
longPressHandler = handler;
|
|
9991
|
+
};
|
|
9411
9992
|
const focus = () => {
|
|
9412
9993
|
canvas.focus({ preventScroll: true });
|
|
9413
9994
|
};
|
|
@@ -9461,6 +10042,10 @@ function createChart(element, options = {}) {
|
|
|
9461
10042
|
return canvas.toDataURL(options2.type ?? "image/png", options2.quality);
|
|
9462
10043
|
};
|
|
9463
10044
|
const destroy = () => {
|
|
10045
|
+
cancelLongPressTimer();
|
|
10046
|
+
datafeedUnsubscribe?.();
|
|
10047
|
+
datafeedUnsubscribe = null;
|
|
10048
|
+
datafeed = null;
|
|
9464
10049
|
cancelKineticPan();
|
|
9465
10050
|
if (smoothingRafId !== null) {
|
|
9466
10051
|
cancelAnimationFrame(smoothingRafId);
|
|
@@ -9548,6 +10133,12 @@ function createChart(element, options = {}) {
|
|
|
9548
10133
|
getMagnetMode,
|
|
9549
10134
|
setPriceScale,
|
|
9550
10135
|
getPriceScale,
|
|
10136
|
+
setCompareSeries,
|
|
10137
|
+
addCompareSeries,
|
|
10138
|
+
removeCompareSeries,
|
|
10139
|
+
getCompareSeries,
|
|
10140
|
+
setDatafeed,
|
|
10141
|
+
onLongPress,
|
|
9551
10142
|
cancelDrawing,
|
|
9552
10143
|
setTradeMarkers,
|
|
9553
10144
|
setSelectedDrawing,
|