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
|
@@ -2853,6 +2853,9 @@ var DEFAULT_OPTIONS = {
|
|
|
2853
2853
|
doubleClickAction: "reset",
|
|
2854
2854
|
keyboard: { enabled: true, panBars: 1, deleteSelectedDrawing: true },
|
|
2855
2855
|
accessibility: { label: "Price chart", description: "", focusable: true },
|
|
2856
|
+
downsampling: { enabled: true, thresholdPx: 1.5 },
|
|
2857
|
+
touch: { hitToleranceScale: 2.2, longPressTooltip: true, longPressMs: 400 },
|
|
2858
|
+
datafeedOptions: { prefetchThresholdBars: 150, cooldownMs: 1200, chunkBars: 1500 },
|
|
2856
2859
|
crosshair: DEFAULT_CROSSHAIR_OPTIONS,
|
|
2857
2860
|
grid: DEFAULT_GRID_OPTIONS,
|
|
2858
2861
|
watermark: DEFAULT_WATERMARK_OPTIONS,
|
|
@@ -2903,6 +2906,18 @@ var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
|
2903
2906
|
...baseOptions.accessibility,
|
|
2904
2907
|
...options.accessibility ?? {}
|
|
2905
2908
|
},
|
|
2909
|
+
downsampling: {
|
|
2910
|
+
...baseOptions.downsampling,
|
|
2911
|
+
...options.downsampling ?? {}
|
|
2912
|
+
},
|
|
2913
|
+
touch: {
|
|
2914
|
+
...baseOptions.touch,
|
|
2915
|
+
...options.touch ?? {}
|
|
2916
|
+
},
|
|
2917
|
+
datafeedOptions: {
|
|
2918
|
+
...baseOptions.datafeedOptions,
|
|
2919
|
+
...options.datafeedOptions ?? {}
|
|
2920
|
+
},
|
|
2906
2921
|
crosshair: {
|
|
2907
2922
|
...baseOptions.crosshair,
|
|
2908
2923
|
...options.crosshair ?? {}
|
|
@@ -3077,6 +3092,7 @@ function createChart(element, options = {}) {
|
|
|
3077
3092
|
let drawingDoubleClickHandler = null;
|
|
3078
3093
|
let drawingEditTextHandler = null;
|
|
3079
3094
|
let selectionChangeHandler = null;
|
|
3095
|
+
let longPressHandler = null;
|
|
3080
3096
|
let contextMenuHandler = null;
|
|
3081
3097
|
let keyboardShortcutHandler = null;
|
|
3082
3098
|
let lastSelectionSignature = null;
|
|
@@ -3778,6 +3794,150 @@ function createChart(element, options = {}) {
|
|
|
3778
3794
|
heikinAshiFingerprint = fingerprint;
|
|
3779
3795
|
return result;
|
|
3780
3796
|
};
|
|
3797
|
+
let compareSeriesStates = [];
|
|
3798
|
+
const prepareCompareSeries = (options2) => {
|
|
3799
|
+
const rows = [];
|
|
3800
|
+
for (const point of options2.data ?? []) {
|
|
3801
|
+
const t = new Date(point.t).getTime();
|
|
3802
|
+
const c = Number(point.c);
|
|
3803
|
+
if (Number.isFinite(t) && Number.isFinite(c)) rows.push({ t, c });
|
|
3804
|
+
}
|
|
3805
|
+
rows.sort((a, b) => a.t - b.t);
|
|
3806
|
+
return {
|
|
3807
|
+
options: options2,
|
|
3808
|
+
times: rows.map((row) => row.t),
|
|
3809
|
+
closes: rows.map((row) => row.c),
|
|
3810
|
+
aligned: null,
|
|
3811
|
+
alignedFingerprint: ""
|
|
3812
|
+
};
|
|
3813
|
+
};
|
|
3814
|
+
const mainDataFingerprint = () => {
|
|
3815
|
+
const last = data[data.length - 1];
|
|
3816
|
+
return last ? `${data.length}|${data[0].time.getTime()}|${last.time.getTime()}` : "empty";
|
|
3817
|
+
};
|
|
3818
|
+
const getAlignedCompareValues = (state) => {
|
|
3819
|
+
const fingerprint = `${mainDataFingerprint()}|${state.times.length}|${state.times[state.times.length - 1] ?? 0}`;
|
|
3820
|
+
if (state.aligned && state.alignedFingerprint === fingerprint) {
|
|
3821
|
+
return state.aligned;
|
|
3822
|
+
}
|
|
3823
|
+
const aligned = new Float64Array(data.length).fill(Number.NaN);
|
|
3824
|
+
let cursor = 0;
|
|
3825
|
+
let lastValue = Number.NaN;
|
|
3826
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
3827
|
+
const barTime = data[i].time.getTime();
|
|
3828
|
+
while (cursor < state.times.length && state.times[cursor] <= barTime) {
|
|
3829
|
+
lastValue = state.closes[cursor];
|
|
3830
|
+
cursor += 1;
|
|
3831
|
+
}
|
|
3832
|
+
aligned[i] = lastValue;
|
|
3833
|
+
}
|
|
3834
|
+
state.aligned = aligned;
|
|
3835
|
+
state.alignedFingerprint = fingerprint;
|
|
3836
|
+
return aligned;
|
|
3837
|
+
};
|
|
3838
|
+
const visibleCompareSeries = () => compareSeriesStates.filter((state) => state.options.visible !== false && state.times.length > 0);
|
|
3839
|
+
const setCompareSeries = (series) => {
|
|
3840
|
+
compareSeriesStates = (series ?? []).filter((entry) => entry && entry.id).map(prepareCompareSeries);
|
|
3841
|
+
scheduleDraw({ updateAutoScale: true });
|
|
3842
|
+
};
|
|
3843
|
+
const addCompareSeries = (series) => {
|
|
3844
|
+
if (!series?.id) return;
|
|
3845
|
+
compareSeriesStates = [...compareSeriesStates.filter((state) => state.options.id !== series.id), prepareCompareSeries(series)];
|
|
3846
|
+
scheduleDraw({ updateAutoScale: true });
|
|
3847
|
+
};
|
|
3848
|
+
const removeCompareSeries = (id) => {
|
|
3849
|
+
const next = compareSeriesStates.filter((state) => state.options.id !== id);
|
|
3850
|
+
if (next.length === compareSeriesStates.length) return;
|
|
3851
|
+
compareSeriesStates = next;
|
|
3852
|
+
scheduleDraw({ updateAutoScale: true });
|
|
3853
|
+
};
|
|
3854
|
+
const getCompareSeries = () => compareSeriesStates.map((state) => state.options);
|
|
3855
|
+
let datafeed = null;
|
|
3856
|
+
let datafeedUnsubscribe = null;
|
|
3857
|
+
let datafeedLoading = false;
|
|
3858
|
+
let datafeedExhausted = false;
|
|
3859
|
+
let datafeedLastRequestAt = 0;
|
|
3860
|
+
const estimateBarStepMs = () => {
|
|
3861
|
+
if (mergedOptions.timeStepMs > 0) return mergedOptions.timeStepMs;
|
|
3862
|
+
if (data.length < 2) return 6e4;
|
|
3863
|
+
const span = data[data.length - 1].time.getTime() - data[0].time.getTime();
|
|
3864
|
+
return Math.max(1e3, span / (data.length - 1));
|
|
3865
|
+
};
|
|
3866
|
+
const requestDatafeedBars = async (request) => {
|
|
3867
|
+
if (!datafeed) return [];
|
|
3868
|
+
try {
|
|
3869
|
+
return await datafeed.getBars(request) ?? [];
|
|
3870
|
+
} catch (error) {
|
|
3871
|
+
datafeed.onError?.(error, request);
|
|
3872
|
+
return [];
|
|
3873
|
+
}
|
|
3874
|
+
};
|
|
3875
|
+
const maybeLoadOlderHistory = () => {
|
|
3876
|
+
if (!datafeed || datafeedLoading || datafeedExhausted || data.length === 0) return;
|
|
3877
|
+
const options2 = mergedOptions.datafeedOptions ?? DEFAULT_OPTIONS.datafeedOptions;
|
|
3878
|
+
const threshold = options2?.prefetchThresholdBars ?? 150;
|
|
3879
|
+
const cooldown = options2?.cooldownMs ?? 1200;
|
|
3880
|
+
if (Date.now() - datafeedLastRequestAt < cooldown) return;
|
|
3881
|
+
if (xCenter - xSpan / 2 > threshold) return;
|
|
3882
|
+
const stepMs = estimateBarStepMs();
|
|
3883
|
+
const chunk = Math.max(10, options2?.chunkBars ?? 1500);
|
|
3884
|
+
const toMs = data[0].time.getTime();
|
|
3885
|
+
datafeedLoading = true;
|
|
3886
|
+
datafeedLastRequestAt = Date.now();
|
|
3887
|
+
void requestDatafeedBars({
|
|
3888
|
+
fromMs: toMs - chunk * stepMs,
|
|
3889
|
+
toMs,
|
|
3890
|
+
countBack: chunk,
|
|
3891
|
+
firstRequest: false
|
|
3892
|
+
}).then((bars) => {
|
|
3893
|
+
const older = bars.filter((bar) => new Date(bar.t).getTime() < toMs);
|
|
3894
|
+
if (older.length === 0) {
|
|
3895
|
+
datafeedExhausted = true;
|
|
3896
|
+
return;
|
|
3897
|
+
}
|
|
3898
|
+
const merged = [...older, ...data.map((bar) => ({
|
|
3899
|
+
t: bar.time.toISOString(),
|
|
3900
|
+
o: bar.o,
|
|
3901
|
+
h: bar.h,
|
|
3902
|
+
l: bar.l,
|
|
3903
|
+
c: bar.c,
|
|
3904
|
+
...bar.v === void 0 ? {} : { v: bar.v }
|
|
3905
|
+
}))];
|
|
3906
|
+
setData(merged);
|
|
3907
|
+
}).finally(() => {
|
|
3908
|
+
datafeedLoading = false;
|
|
3909
|
+
});
|
|
3910
|
+
};
|
|
3911
|
+
const setDatafeed = async (nextDatafeed) => {
|
|
3912
|
+
datafeedUnsubscribe?.();
|
|
3913
|
+
datafeedUnsubscribe = null;
|
|
3914
|
+
datafeed = nextDatafeed;
|
|
3915
|
+
datafeedExhausted = false;
|
|
3916
|
+
datafeedLoading = false;
|
|
3917
|
+
datafeedLastRequestAt = 0;
|
|
3918
|
+
if (!nextDatafeed) return;
|
|
3919
|
+
await nextDatafeed.onReady?.();
|
|
3920
|
+
if (datafeed !== nextDatafeed) return;
|
|
3921
|
+
const stepMs = estimateBarStepMs();
|
|
3922
|
+
const countBack = Math.max(50, Math.round(mergedOptions.initialVisibleBars * 3));
|
|
3923
|
+
const toMs = Date.now();
|
|
3924
|
+
datafeedLoading = true;
|
|
3925
|
+
const bars = await requestDatafeedBars({
|
|
3926
|
+
fromMs: toMs - countBack * stepMs,
|
|
3927
|
+
toMs,
|
|
3928
|
+
countBack,
|
|
3929
|
+
firstRequest: true
|
|
3930
|
+
});
|
|
3931
|
+
datafeedLoading = false;
|
|
3932
|
+
if (datafeed !== nextDatafeed) return;
|
|
3933
|
+
if (bars.length > 0) {
|
|
3934
|
+
setData(bars);
|
|
3935
|
+
}
|
|
3936
|
+
const unsubscribe = nextDatafeed.subscribeBars?.((bar) => {
|
|
3937
|
+
if (datafeed === nextDatafeed) upsertBar(bar);
|
|
3938
|
+
});
|
|
3939
|
+
datafeedUnsubscribe = typeof unsubscribe === "function" ? unsubscribe : null;
|
|
3940
|
+
};
|
|
3781
3941
|
const formatHoverTimeLabel = (time, mode) => {
|
|
3782
3942
|
if (mode === "time") {
|
|
3783
3943
|
return time.toLocaleTimeString(void 0, {
|
|
@@ -4383,6 +4543,31 @@ function createChart(element, options = {}) {
|
|
|
4383
4543
|
const chartType = mergedOptions.chartType;
|
|
4384
4544
|
const seriesData = chartType === "heikin-ashi" ? getHeikinAshiData() : data;
|
|
4385
4545
|
const isLineStyleChart = chartType === "line" || chartType === "area" || chartType === "baseline";
|
|
4546
|
+
const comparePlans = [];
|
|
4547
|
+
for (const state of visibleCompareSeries()) {
|
|
4548
|
+
const aligned = getAlignedCompareValues(state);
|
|
4549
|
+
let anchorIndex = -1;
|
|
4550
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
4551
|
+
if (Number.isFinite(aligned[index] ?? Number.NaN)) {
|
|
4552
|
+
anchorIndex = index;
|
|
4553
|
+
break;
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
if (anchorIndex < 0) continue;
|
|
4557
|
+
const baseCompare = aligned[anchorIndex];
|
|
4558
|
+
const baseMain = seriesData[anchorIndex]?.c ?? 0;
|
|
4559
|
+
const usePercent = (state.options.scale ?? "percent") !== "price";
|
|
4560
|
+
const toPrice = usePercent && baseCompare > 0 ? (value) => baseMain * value / baseCompare : (value) => value;
|
|
4561
|
+
let lastNativeValue = Number.NaN;
|
|
4562
|
+
for (let index = endIndex; index >= startIndex; index -= 1) {
|
|
4563
|
+
const value = aligned[index];
|
|
4564
|
+
if (value !== void 0 && Number.isFinite(value)) {
|
|
4565
|
+
lastNativeValue = value;
|
|
4566
|
+
break;
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
comparePlans.push({ state, aligned, toPrice, usePercent, baseCompare, lastNativeValue });
|
|
4570
|
+
}
|
|
4386
4571
|
const skipLatestIndex = mergedOptions.autoScaleIgnoreLatestCandle && data.length > 1 ? data.length - 1 : -1;
|
|
4387
4572
|
const scanCandleRange = (from, to) => {
|
|
4388
4573
|
let min = Number.POSITIVE_INFINITY;
|
|
@@ -4428,6 +4613,23 @@ function createChart(element, options = {}) {
|
|
|
4428
4613
|
maxPrice = Math.max(maxPrice, weightedMax);
|
|
4429
4614
|
}
|
|
4430
4615
|
}
|
|
4616
|
+
for (const plan of comparePlans) {
|
|
4617
|
+
if (plan.state.options.includeInAutoScale === false) continue;
|
|
4618
|
+
let compareMin = Number.POSITIVE_INFINITY;
|
|
4619
|
+
let compareMax = Number.NEGATIVE_INFINITY;
|
|
4620
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
4621
|
+
const value = plan.aligned[index];
|
|
4622
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
4623
|
+
const price = plan.toPrice(value);
|
|
4624
|
+
if (!Number.isFinite(price)) continue;
|
|
4625
|
+
if (price < compareMin) compareMin = price;
|
|
4626
|
+
if (price > compareMax) compareMax = price;
|
|
4627
|
+
}
|
|
4628
|
+
if (compareMin <= compareMax) {
|
|
4629
|
+
minPrice = Math.min(minPrice, compareMin);
|
|
4630
|
+
maxPrice = Math.max(maxPrice, compareMax);
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4431
4633
|
const priceRange = maxPrice - minPrice || 1;
|
|
4432
4634
|
const autoMin = minPrice - priceRange * 0.08;
|
|
4433
4635
|
const autoMax = maxPrice + priceRange * 0.08;
|
|
@@ -5539,7 +5741,106 @@ function createChart(element, options = {}) {
|
|
|
5539
5741
|
}
|
|
5540
5742
|
return data[index]?.v;
|
|
5541
5743
|
};
|
|
5542
|
-
|
|
5744
|
+
const downsamplingOptions = mergedOptions.downsampling ?? DEFAULT_OPTIONS.downsampling;
|
|
5745
|
+
const downsampleThresholdPx = Math.max(0.2, downsamplingOptions?.thresholdPx ?? 1.5);
|
|
5746
|
+
const shouldDownsample = (downsamplingOptions?.enabled ?? true) && candleSpacing < downsampleThresholdPx && endIndex - startIndex > 2;
|
|
5747
|
+
const seriesColumns = [];
|
|
5748
|
+
if (shouldDownsample) {
|
|
5749
|
+
let current = null;
|
|
5750
|
+
let currentColumn = Number.NaN;
|
|
5751
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
5752
|
+
const point = seriesData[index];
|
|
5753
|
+
if (!point) continue;
|
|
5754
|
+
const column = Math.round(chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth);
|
|
5755
|
+
if (!current || column !== currentColumn) {
|
|
5756
|
+
currentColumn = column;
|
|
5757
|
+
current = {
|
|
5758
|
+
x: column,
|
|
5759
|
+
open: point.o,
|
|
5760
|
+
high: point.h,
|
|
5761
|
+
low: point.l,
|
|
5762
|
+
close: point.c,
|
|
5763
|
+
minClose: point.c,
|
|
5764
|
+
maxClose: point.c,
|
|
5765
|
+
minCloseIndex: index,
|
|
5766
|
+
maxCloseIndex: index
|
|
5767
|
+
};
|
|
5768
|
+
seriesColumns.push(current);
|
|
5769
|
+
continue;
|
|
5770
|
+
}
|
|
5771
|
+
if (point.h > current.high) current.high = point.h;
|
|
5772
|
+
if (point.l < current.low) current.low = point.l;
|
|
5773
|
+
current.close = point.c;
|
|
5774
|
+
if (point.c < current.minClose) {
|
|
5775
|
+
current.minClose = point.c;
|
|
5776
|
+
current.minCloseIndex = index;
|
|
5777
|
+
}
|
|
5778
|
+
if (point.c > current.maxClose) {
|
|
5779
|
+
current.maxClose = point.c;
|
|
5780
|
+
current.maxCloseIndex = index;
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
if (shouldDownsample && !isLineStyleChart) {
|
|
5785
|
+
const upColumns = new Path2D();
|
|
5786
|
+
const downColumns = new Path2D();
|
|
5787
|
+
for (const column of seriesColumns) {
|
|
5788
|
+
const path = column.close >= column.open ? upColumns : downColumns;
|
|
5789
|
+
const x = column.x + 0.5;
|
|
5790
|
+
const top = crisp(yFromPrice(column.high));
|
|
5791
|
+
const bottom = crisp(yFromPrice(column.low));
|
|
5792
|
+
path.moveTo(x, top);
|
|
5793
|
+
path.lineTo(x, bottom === top ? bottom + 1 : bottom);
|
|
5794
|
+
}
|
|
5795
|
+
ctx.lineWidth = 1;
|
|
5796
|
+
ctx.strokeStyle = mergedOptions.upColor;
|
|
5797
|
+
ctx.stroke(upColumns);
|
|
5798
|
+
ctx.strokeStyle = mergedOptions.downColor;
|
|
5799
|
+
ctx.stroke(downColumns);
|
|
5800
|
+
} else if (shouldDownsample && isLineStyleChart) {
|
|
5801
|
+
const seriesLineWidth = Math.max(1, mergedOptions.lineWidth);
|
|
5802
|
+
const linePath = new Path2D();
|
|
5803
|
+
let started = false;
|
|
5804
|
+
let firstX = 0;
|
|
5805
|
+
let lastX = 0;
|
|
5806
|
+
for (const column of seriesColumns) {
|
|
5807
|
+
const lowFirst = column.minCloseIndex <= column.maxCloseIndex;
|
|
5808
|
+
const first = lowFirst ? column.minClose : column.maxClose;
|
|
5809
|
+
const second = lowFirst ? column.maxClose : column.minClose;
|
|
5810
|
+
const x = column.x;
|
|
5811
|
+
if (!started) {
|
|
5812
|
+
linePath.moveTo(x, yFromPrice(first));
|
|
5813
|
+
firstX = x;
|
|
5814
|
+
started = true;
|
|
5815
|
+
} else {
|
|
5816
|
+
linePath.lineTo(x, yFromPrice(first));
|
|
5817
|
+
}
|
|
5818
|
+
if (second !== first) {
|
|
5819
|
+
linePath.lineTo(x, yFromPrice(second));
|
|
5820
|
+
}
|
|
5821
|
+
lastX = x;
|
|
5822
|
+
}
|
|
5823
|
+
if (started) {
|
|
5824
|
+
if (chartType === "area" || chartType === "baseline") {
|
|
5825
|
+
const baseY = chartType === "baseline" ? yFromPrice(mergedOptions.baselinePrice ?? (yMin + yMax) / 2) : chartBottom;
|
|
5826
|
+
const fillPath = new Path2D(linePath);
|
|
5827
|
+
fillPath.lineTo(lastX, baseY);
|
|
5828
|
+
fillPath.lineTo(firstX, baseY);
|
|
5829
|
+
fillPath.closePath();
|
|
5830
|
+
ctx.save();
|
|
5831
|
+
ctx.globalAlpha = Math.max(0, Math.min(1, mergedOptions.areaFillOpacity));
|
|
5832
|
+
ctx.fillStyle = chartType === "baseline" ? mergedOptions.upColor : mergedOptions.lineColor;
|
|
5833
|
+
ctx.fill(fillPath);
|
|
5834
|
+
ctx.restore();
|
|
5835
|
+
}
|
|
5836
|
+
ctx.save();
|
|
5837
|
+
ctx.strokeStyle = chartType === "baseline" ? mergedOptions.upColor : mergedOptions.lineColor;
|
|
5838
|
+
ctx.lineWidth = seriesLineWidth;
|
|
5839
|
+
ctx.lineJoin = "round";
|
|
5840
|
+
ctx.stroke(linePath);
|
|
5841
|
+
ctx.restore();
|
|
5842
|
+
}
|
|
5843
|
+
} else if (isLineStyleChart) {
|
|
5543
5844
|
const seriesLineWidth = Math.max(1, mergedOptions.lineWidth);
|
|
5544
5845
|
const linePath = new Path2D();
|
|
5545
5846
|
let started = false;
|
|
@@ -5705,6 +6006,105 @@ function createChart(element, options = {}) {
|
|
|
5705
6006
|
ctx.stroke(downHollowBodies);
|
|
5706
6007
|
}
|
|
5707
6008
|
}
|
|
6009
|
+
const compareAxisTags = [];
|
|
6010
|
+
for (const plan of comparePlans) {
|
|
6011
|
+
const color = plan.state.options.color ?? "#f59e0b";
|
|
6012
|
+
const lineWidth = Math.max(1, plan.state.options.lineWidth ?? 1.5);
|
|
6013
|
+
const path = new Path2D();
|
|
6014
|
+
let started = false;
|
|
6015
|
+
let firstX = 0;
|
|
6016
|
+
let lastX = 0;
|
|
6017
|
+
let lastY = 0;
|
|
6018
|
+
let lastPrice = Number.NaN;
|
|
6019
|
+
const emit = (x, y) => {
|
|
6020
|
+
if (!started) {
|
|
6021
|
+
path.moveTo(x, y);
|
|
6022
|
+
firstX = x;
|
|
6023
|
+
started = true;
|
|
6024
|
+
} else {
|
|
6025
|
+
path.lineTo(x, y);
|
|
6026
|
+
}
|
|
6027
|
+
lastX = x;
|
|
6028
|
+
lastY = y;
|
|
6029
|
+
};
|
|
6030
|
+
const xForIndex = (index) => chartLeft + (index + 0.5 - xStart) / xSpan * chartWidth;
|
|
6031
|
+
if (shouldDownsample) {
|
|
6032
|
+
let column = Number.NaN;
|
|
6033
|
+
let minValue = 0;
|
|
6034
|
+
let maxValue = 0;
|
|
6035
|
+
let minIndex = 0;
|
|
6036
|
+
let maxIndex = 0;
|
|
6037
|
+
let hasColumn = false;
|
|
6038
|
+
const flushColumn = () => {
|
|
6039
|
+
if (!hasColumn) return;
|
|
6040
|
+
const lowFirst = minIndex <= maxIndex;
|
|
6041
|
+
const first = plan.toPrice(lowFirst ? minValue : maxValue);
|
|
6042
|
+
const second = plan.toPrice(lowFirst ? maxValue : minValue);
|
|
6043
|
+
if (Number.isFinite(first)) emit(column, yFromPrice(first));
|
|
6044
|
+
if (Number.isFinite(second) && second !== first) emit(column, yFromPrice(second));
|
|
6045
|
+
lastPrice = plan.toPrice(maxIndex >= minIndex ? maxValue : minValue);
|
|
6046
|
+
};
|
|
6047
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
6048
|
+
const value = plan.aligned[index];
|
|
6049
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
6050
|
+
const nextColumn = Math.round(xForIndex(index));
|
|
6051
|
+
if (!hasColumn || nextColumn !== column) {
|
|
6052
|
+
flushColumn();
|
|
6053
|
+
column = nextColumn;
|
|
6054
|
+
minValue = value;
|
|
6055
|
+
maxValue = value;
|
|
6056
|
+
minIndex = index;
|
|
6057
|
+
maxIndex = index;
|
|
6058
|
+
hasColumn = true;
|
|
6059
|
+
continue;
|
|
6060
|
+
}
|
|
6061
|
+
if (value < minValue) {
|
|
6062
|
+
minValue = value;
|
|
6063
|
+
minIndex = index;
|
|
6064
|
+
}
|
|
6065
|
+
if (value > maxValue) {
|
|
6066
|
+
maxValue = value;
|
|
6067
|
+
maxIndex = index;
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
flushColumn();
|
|
6071
|
+
} else {
|
|
6072
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
6073
|
+
const value = plan.aligned[index];
|
|
6074
|
+
if (value === void 0 || !Number.isFinite(value)) continue;
|
|
6075
|
+
const price = plan.toPrice(value);
|
|
6076
|
+
if (!Number.isFinite(price)) continue;
|
|
6077
|
+
lastPrice = price;
|
|
6078
|
+
emit(xForIndex(index), yFromPrice(price));
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
if (!started) continue;
|
|
6082
|
+
if (plan.state.options.style === "area") {
|
|
6083
|
+
const fillPath = new Path2D(path);
|
|
6084
|
+
fillPath.lineTo(lastX, chartBottom);
|
|
6085
|
+
fillPath.lineTo(firstX, chartBottom);
|
|
6086
|
+
fillPath.closePath();
|
|
6087
|
+
ctx.save();
|
|
6088
|
+
ctx.globalAlpha = Math.max(0, Math.min(1, mergedOptions.areaFillOpacity)) * 0.6;
|
|
6089
|
+
ctx.fillStyle = color;
|
|
6090
|
+
ctx.fill(fillPath);
|
|
6091
|
+
ctx.restore();
|
|
6092
|
+
}
|
|
6093
|
+
ctx.save();
|
|
6094
|
+
ctx.strokeStyle = color;
|
|
6095
|
+
ctx.lineWidth = lineWidth;
|
|
6096
|
+
ctx.lineJoin = "round";
|
|
6097
|
+
ctx.stroke(path);
|
|
6098
|
+
ctx.restore();
|
|
6099
|
+
if (Number.isFinite(lastPrice)) {
|
|
6100
|
+
const label = plan.state.options.label ?? plan.state.options.id;
|
|
6101
|
+
const nativeLast = plan.lastNativeValue;
|
|
6102
|
+
const usePercentTag = plan.usePercent && plan.baseCompare > 0 && Number.isFinite(nativeLast);
|
|
6103
|
+
const changePct = usePercentTag ? (nativeLast / plan.baseCompare - 1) * 100 : 0;
|
|
6104
|
+
const shortText = usePercentTag ? `${changePct >= 0 ? "+" : ""}${changePct.toFixed(2)}%` : formatPrice(nativeLast);
|
|
6105
|
+
compareAxisTags.push({ y: lastY, text: `${label} ${shortText}`, shortText, color });
|
|
6106
|
+
}
|
|
6107
|
+
}
|
|
5708
6108
|
const activeOverlayIndicators = indicators.filter((indicator) => indicator.visible).map((indicator) => ({ indicator, plugin: indicatorRegistry.get(indicator.type) })).filter(
|
|
5709
6109
|
(value) => value.plugin !== void 0 && (value.indicator.pane ?? value.plugin.pane ?? "overlay") === "overlay"
|
|
5710
6110
|
).sort((a, b) => a.indicator.zIndex - b.indicator.zIndex);
|
|
@@ -6479,6 +6879,35 @@ function createChart(element, options = {}) {
|
|
|
6479
6879
|
drawText(countdownText, chartRight + 6, (fullChartBottom + height) / 2, "left", "middle", xAxis.textColor);
|
|
6480
6880
|
ctx.font = prevFont;
|
|
6481
6881
|
}
|
|
6882
|
+
if (compareAxisTags.length > 0 && width - chartRight > 0) {
|
|
6883
|
+
const prevFont = ctx.font;
|
|
6884
|
+
const tagFontSize = Math.max(9, (resolvedAxis.fontSize ?? 12) - 2);
|
|
6885
|
+
ctx.font = `${tagFontSize}px ${mergedOptions.fontFamily}`;
|
|
6886
|
+
ctx.textBaseline = "middle";
|
|
6887
|
+
ctx.textAlign = "left";
|
|
6888
|
+
const placed = [];
|
|
6889
|
+
const tagHeight = tagFontSize + 6;
|
|
6890
|
+
const gutterWidth = width - chartRight - 4;
|
|
6891
|
+
for (const tag of [...compareAxisTags].sort((a, b) => a.y - b.y)) {
|
|
6892
|
+
const text = measureTextWidth(tag.text) + 10 <= gutterWidth ? tag.text : tag.shortText;
|
|
6893
|
+
const tagWidth = Math.min(gutterWidth, measureTextWidth(text) + 10);
|
|
6894
|
+
if (tagWidth <= 0) continue;
|
|
6895
|
+
let tagY = clamp(tag.y - tagHeight / 2, chartTop, chartBottom - tagHeight);
|
|
6896
|
+
for (const slot of placed) {
|
|
6897
|
+
if (tagY < slot.bottom + 2 && tagY + tagHeight > slot.top - 2) {
|
|
6898
|
+
tagY = slot.bottom + 2;
|
|
6899
|
+
}
|
|
6900
|
+
}
|
|
6901
|
+
placed.push({ top: tagY, bottom: tagY + tagHeight });
|
|
6902
|
+
ctx.save();
|
|
6903
|
+
ctx.fillStyle = tag.color;
|
|
6904
|
+
fillRoundedRect(chartRight + 2, Math.round(tagY), Math.round(tagWidth), tagHeight, 3);
|
|
6905
|
+
ctx.fillStyle = labelTextColorOn(tag.color, "#ffffff");
|
|
6906
|
+
ctx.fillText(text, chartRight + 7, Math.round(tagY + tagHeight / 2));
|
|
6907
|
+
ctx.restore();
|
|
6908
|
+
}
|
|
6909
|
+
ctx.font = prevFont;
|
|
6910
|
+
}
|
|
6482
6911
|
if (baseCtx) {
|
|
6483
6912
|
if (baseCanvas.width !== canvas.width || baseCanvas.height !== canvas.height) {
|
|
6484
6913
|
baseCanvas.width = canvas.width;
|
|
@@ -6503,6 +6932,7 @@ function createChart(element, options = {}) {
|
|
|
6503
6932
|
};
|
|
6504
6933
|
paintCrosshairOverlay();
|
|
6505
6934
|
emitSelectionChangeIfMoved();
|
|
6935
|
+
maybeLoadOlderHistory();
|
|
6506
6936
|
};
|
|
6507
6937
|
const paintCrosshairOverlay = () => {
|
|
6508
6938
|
crosshairPriceActionRegion = null;
|
|
@@ -6695,6 +7125,67 @@ function createChart(element, options = {}) {
|
|
|
6695
7125
|
drawText(timeText, timeX + labelPaddingX, timeY + timeHeight / 2, "left", "middle", labelTextColor);
|
|
6696
7126
|
}
|
|
6697
7127
|
}
|
|
7128
|
+
if (longPressState && (mergedOptions.touch?.longPressTooltip ?? true)) {
|
|
7129
|
+
const index = indexFromCanvasX(cx);
|
|
7130
|
+
const bar = index === null ? void 0 : data[index];
|
|
7131
|
+
if (bar) {
|
|
7132
|
+
const barTime = getTimeForIndex(index);
|
|
7133
|
+
const changeAbs = bar.c - bar.o;
|
|
7134
|
+
const changePct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
|
|
7135
|
+
const rows = [
|
|
7136
|
+
["O", formatPrice(bar.o), null],
|
|
7137
|
+
["H", formatPrice(bar.h), null],
|
|
7138
|
+
["L", formatPrice(bar.l), null],
|
|
7139
|
+
["C", formatPrice(bar.c), null],
|
|
7140
|
+
[
|
|
7141
|
+
"Chg",
|
|
7142
|
+
`${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${changePct.toFixed(2)}%)`,
|
|
7143
|
+
changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor
|
|
7144
|
+
]
|
|
7145
|
+
];
|
|
7146
|
+
if (bar.v !== void 0) {
|
|
7147
|
+
rows.push([
|
|
7148
|
+
"Vol",
|
|
7149
|
+
bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v)),
|
|
7150
|
+
null
|
|
7151
|
+
]);
|
|
7152
|
+
}
|
|
7153
|
+
const prevFont = ctx.font;
|
|
7154
|
+
const fontSize = 11;
|
|
7155
|
+
ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
|
|
7156
|
+
const headerText = barTime ? formatHoverTimeLabel(barTime, "auto") : "";
|
|
7157
|
+
const rowGap = 3;
|
|
7158
|
+
const padding = 8;
|
|
7159
|
+
const labelColumn = 26;
|
|
7160
|
+
const contentWidth = Math.max(
|
|
7161
|
+
measureTextWidth(headerText),
|
|
7162
|
+
...rows.map(([, value]) => labelColumn + measureTextWidth(value))
|
|
7163
|
+
);
|
|
7164
|
+
const boxWidth = Math.ceil(contentWidth) + padding * 2;
|
|
7165
|
+
const boxHeight = padding * 2 + (fontSize + rowGap) * (rows.length + (headerText ? 1 : 0)) - rowGap;
|
|
7166
|
+
const fingerGap = 26;
|
|
7167
|
+
const boxX = cx + fingerGap + boxWidth <= chartRight ? cx + fingerGap : Math.max(chartLeft + 4, cx - fingerGap - boxWidth);
|
|
7168
|
+
const boxY = clamp(cy - boxHeight - fingerGap, chartTop + 4, chartBottom - boxHeight - 4);
|
|
7169
|
+
ctx.save();
|
|
7170
|
+
ctx.fillStyle = "rgba(16,17,20,0.94)";
|
|
7171
|
+
fillRoundedRect(Math.round(boxX), Math.round(boxY), boxWidth, boxHeight, 6);
|
|
7172
|
+
ctx.strokeStyle = "rgba(255,255,255,0.14)";
|
|
7173
|
+
ctx.lineWidth = 1;
|
|
7174
|
+
ctx.strokeRect(Math.round(boxX) + 0.5, Math.round(boxY) + 0.5, boxWidth - 1, boxHeight - 1);
|
|
7175
|
+
let textY = boxY + padding + fontSize / 2;
|
|
7176
|
+
if (headerText) {
|
|
7177
|
+
drawText(headerText, boxX + padding, textY, "left", "middle", "rgba(255,255,255,0.55)");
|
|
7178
|
+
textY += fontSize + rowGap;
|
|
7179
|
+
}
|
|
7180
|
+
for (const [label, value, valueColor] of rows) {
|
|
7181
|
+
drawText(label, boxX + padding, textY, "left", "middle", "rgba(255,255,255,0.5)");
|
|
7182
|
+
drawText(value, boxX + padding + labelColumn, textY, "left", "middle", valueColor ?? "#e6e8ee");
|
|
7183
|
+
textY += fontSize + rowGap;
|
|
7184
|
+
}
|
|
7185
|
+
ctx.restore();
|
|
7186
|
+
ctx.font = prevFont;
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
6698
7189
|
};
|
|
6699
7190
|
const drawOverlayOnly = () => {
|
|
6700
7191
|
if (!baseCtx || !overlayLayout || baseCanvas.width !== canvas.width || baseCanvas.height !== canvas.height || baseCanvas.width === 0) {
|
|
@@ -7204,6 +7695,7 @@ function createChart(element, options = {}) {
|
|
|
7204
7695
|
return Math.hypot(x - (x1 + t * dx), y - (y1 + t * dy));
|
|
7205
7696
|
};
|
|
7206
7697
|
const getDrawingHit = (x, y) => {
|
|
7698
|
+
const hitTolerance = touchInputActive ? Math.max(1, mergedOptions.touch?.hitToleranceScale ?? 2.2) : 1;
|
|
7207
7699
|
for (let index = drawings.length - 1; index >= 0; index -= 1) {
|
|
7208
7700
|
const drawing = drawings[index];
|
|
7209
7701
|
if (!drawing?.visible) continue;
|
|
@@ -7212,10 +7704,10 @@ function createChart(element, options = {}) {
|
|
|
7212
7704
|
if (!point) continue;
|
|
7213
7705
|
const lineY = canvasYFromDrawingPrice(point.price);
|
|
7214
7706
|
const handleX = drawState ? drawState.chartRight - 64 : 0;
|
|
7215
|
-
if (Math.hypot(x - handleX, y - lineY) <= 8) {
|
|
7707
|
+
if (Math.hypot(x - handleX, y - lineY) <= 8 * hitTolerance) {
|
|
7216
7708
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7217
7709
|
}
|
|
7218
|
-
if (Math.abs(y - lineY) <= 7) {
|
|
7710
|
+
if (Math.abs(y - lineY) <= 7 * hitTolerance) {
|
|
7219
7711
|
return { drawing, target: "line" };
|
|
7220
7712
|
}
|
|
7221
7713
|
} else if (drawing.type === "vertical-line") {
|
|
@@ -7223,10 +7715,10 @@ function createChart(element, options = {}) {
|
|
|
7223
7715
|
if (!point) continue;
|
|
7224
7716
|
const lineX = canvasXFromDrawingPoint(point);
|
|
7225
7717
|
const handleY = drawState ? (drawState.chartTop + drawState.chartBottom) / 2 : 0;
|
|
7226
|
-
if (Math.hypot(x - lineX, y - handleY) <= 8) {
|
|
7718
|
+
if (Math.hypot(x - lineX, y - handleY) <= 8 * hitTolerance) {
|
|
7227
7719
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7228
7720
|
}
|
|
7229
|
-
if (Math.abs(x - lineX) <= 7) {
|
|
7721
|
+
if (Math.abs(x - lineX) <= 7 * hitTolerance) {
|
|
7230
7722
|
return { drawing, target: "line" };
|
|
7231
7723
|
}
|
|
7232
7724
|
} else if (drawing.type === "trendline") {
|
|
@@ -7237,13 +7729,13 @@ function createChart(element, options = {}) {
|
|
|
7237
7729
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7238
7730
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7239
7731
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7240
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7732
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7241
7733
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7242
7734
|
}
|
|
7243
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7735
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7244
7736
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7245
7737
|
}
|
|
7246
|
-
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
7738
|
+
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
|
|
7247
7739
|
return { drawing, target: "line" };
|
|
7248
7740
|
}
|
|
7249
7741
|
} else if (drawing.type === "ray") {
|
|
@@ -7254,10 +7746,10 @@ function createChart(element, options = {}) {
|
|
|
7254
7746
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7255
7747
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7256
7748
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7257
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7749
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7258
7750
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7259
7751
|
}
|
|
7260
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7752
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7261
7753
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7262
7754
|
}
|
|
7263
7755
|
const dx = x2 - x1;
|
|
@@ -7267,7 +7759,7 @@ function createChart(element, options = {}) {
|
|
|
7267
7759
|
const t = Math.max(0, ((x - x1) * dx + (y - y1) * dy) / lengthSq);
|
|
7268
7760
|
const projX = x1 + t * dx;
|
|
7269
7761
|
const projY = y1 + t * dy;
|
|
7270
|
-
if (Math.hypot(x - projX, y - projY) <= 6) {
|
|
7762
|
+
if (Math.hypot(x - projX, y - projY) <= 6 * hitTolerance) {
|
|
7271
7763
|
return { drawing, target: "line" };
|
|
7272
7764
|
}
|
|
7273
7765
|
}
|
|
@@ -7279,17 +7771,17 @@ function createChart(element, options = {}) {
|
|
|
7279
7771
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7280
7772
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7281
7773
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7282
|
-
if (Math.hypot(x - x1, y - y1) <= 9) {
|
|
7774
|
+
if (Math.hypot(x - x1, y - y1) <= 9 * hitTolerance) {
|
|
7283
7775
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7284
7776
|
}
|
|
7285
|
-
if (Math.hypot(x - x2, y - y2) <= 9) {
|
|
7777
|
+
if (Math.hypot(x - x2, y - y2) <= 9 * hitTolerance) {
|
|
7286
7778
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7287
7779
|
}
|
|
7288
7780
|
const fibRatios = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618];
|
|
7289
7781
|
for (const ratio of fibRatios) {
|
|
7290
7782
|
const price = first.price + (second.price - first.price) * (1 - ratio);
|
|
7291
7783
|
const lineY = canvasYFromDrawingPrice(price);
|
|
7292
|
-
if (Math.abs(y - lineY) <= 5) {
|
|
7784
|
+
if (Math.abs(y - lineY) <= 5 * hitTolerance) {
|
|
7293
7785
|
return { drawing, target: "line" };
|
|
7294
7786
|
}
|
|
7295
7787
|
}
|
|
@@ -7302,30 +7794,30 @@ function createChart(element, options = {}) {
|
|
|
7302
7794
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7303
7795
|
const x1 = canvasXFromDrawingPoint(p1);
|
|
7304
7796
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7305
|
-
if (Math.hypot(x - x0, y - y0) <= 8) {
|
|
7797
|
+
if (Math.hypot(x - x0, y - y0) <= 8 * hitTolerance) {
|
|
7306
7798
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7307
7799
|
}
|
|
7308
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7800
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7309
7801
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7310
7802
|
}
|
|
7311
7803
|
if (p2) {
|
|
7312
7804
|
const x2 = canvasXFromDrawingPoint(p2);
|
|
7313
7805
|
const y2 = canvasYFromDrawingPrice(p2.price);
|
|
7314
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7806
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7315
7807
|
return { drawing, target: "handle", pointIndex: 2 };
|
|
7316
7808
|
}
|
|
7317
7809
|
const move = p1.price - p0.price;
|
|
7318
7810
|
const fibExtRatios = [0, 0.382, 0.5, 0.618, 1, 1.272, 1.618, 2.618];
|
|
7319
7811
|
for (const ratio of fibExtRatios) {
|
|
7320
7812
|
const lineY = canvasYFromDrawingPrice(p2.price + move * ratio);
|
|
7321
|
-
if (Math.abs(y - lineY) <= 5) {
|
|
7813
|
+
if (Math.abs(y - lineY) <= 5 * hitTolerance) {
|
|
7322
7814
|
return { drawing, target: "line" };
|
|
7323
7815
|
}
|
|
7324
7816
|
}
|
|
7325
|
-
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 || distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
7817
|
+
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
|
|
7326
7818
|
return { drawing, target: "line" };
|
|
7327
7819
|
}
|
|
7328
|
-
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6) {
|
|
7820
|
+
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance) {
|
|
7329
7821
|
return { drawing, target: "line" };
|
|
7330
7822
|
}
|
|
7331
7823
|
} else if (drawing.type === "long-position" || drawing.type === "short-position") {
|
|
@@ -7339,10 +7831,10 @@ function createChart(element, options = {}) {
|
|
|
7339
7831
|
const entryY = canvasYFromDrawingPrice(entry.price);
|
|
7340
7832
|
const targetY = canvasYFromDrawingPrice(target.price);
|
|
7341
7833
|
const stopY = canvasYFromDrawingPrice(stop.price);
|
|
7342
|
-
if (Math.hypot(x - leftX, y - targetY) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7343
|
-
if (Math.hypot(x - leftX, y - entryY) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7344
|
-
if (Math.hypot(x - leftX, y - stopY) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
7345
|
-
if (Math.hypot(x - rightX, y - entryY) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
7834
|
+
if (Math.hypot(x - leftX, y - targetY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7835
|
+
if (Math.hypot(x - leftX, y - entryY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7836
|
+
if (Math.hypot(x - leftX, y - stopY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7837
|
+
if (Math.hypot(x - rightX, y - entryY) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 3 };
|
|
7346
7838
|
const x0 = Math.min(leftX, rightX);
|
|
7347
7839
|
const x1 = Math.max(leftX, rightX);
|
|
7348
7840
|
const yTop = Math.min(targetY, stopY, entryY);
|
|
@@ -7358,10 +7850,10 @@ function createChart(element, options = {}) {
|
|
|
7358
7850
|
const px1 = canvasXFromDrawingPoint(p1);
|
|
7359
7851
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7360
7852
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7361
|
-
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7362
|
-
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7363
|
-
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
7364
|
-
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
7853
|
+
if (Math.hypot(x - px0, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7854
|
+
if (Math.hypot(x - px1, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7855
|
+
if (Math.hypot(x - px0, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7856
|
+
if (Math.hypot(x - px1, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 3 };
|
|
7365
7857
|
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
7366
7858
|
return { drawing, target: "line" };
|
|
7367
7859
|
}
|
|
@@ -7373,16 +7865,16 @@ function createChart(element, options = {}) {
|
|
|
7373
7865
|
const px1 = canvasXFromDrawingPoint(p1);
|
|
7374
7866
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7375
7867
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7376
|
-
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7377
|
-
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7378
|
-
if (Math.hypot(x - px0, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 2 };
|
|
7379
|
-
if (Math.hypot(x - px1, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 3 };
|
|
7868
|
+
if (Math.hypot(x - px0, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7869
|
+
if (Math.hypot(x - px1, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7870
|
+
if (Math.hypot(x - px0, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7871
|
+
if (Math.hypot(x - px1, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 3 };
|
|
7380
7872
|
const cx = (px0 + px1) / 2;
|
|
7381
7873
|
const cy = (y0 + y1) / 2;
|
|
7382
7874
|
const rx = Math.max(1, Math.abs(px1 - px0) / 2);
|
|
7383
7875
|
const ry = Math.max(1, Math.abs(y1 - y0) / 2);
|
|
7384
7876
|
const norm = ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2;
|
|
7385
|
-
if (norm <= 1) {
|
|
7877
|
+
if (norm <= 1 * hitTolerance) {
|
|
7386
7878
|
return { drawing, target: "line" };
|
|
7387
7879
|
}
|
|
7388
7880
|
} else if (drawing.type === "parallel-channel") {
|
|
@@ -7394,18 +7886,18 @@ function createChart(element, options = {}) {
|
|
|
7394
7886
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7395
7887
|
const x1 = canvasXFromDrawingPoint(p1);
|
|
7396
7888
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7397
|
-
if (Math.hypot(x - x0, y - y0) <= 8) return { drawing, target: "handle", pointIndex: 0 };
|
|
7398
|
-
if (Math.hypot(x - x1, y - y1) <= 8) return { drawing, target: "handle", pointIndex: 1 };
|
|
7889
|
+
if (Math.hypot(x - x0, y - y0) <= 8 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7890
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7399
7891
|
if (p2) {
|
|
7400
7892
|
const x2 = canvasXFromDrawingPoint(p2);
|
|
7401
7893
|
const y2 = canvasYFromDrawingPrice(p2.price);
|
|
7402
|
-
if (Math.hypot(x - x2, y - y2) <= 8) return { drawing, target: "handle", pointIndex: 2 };
|
|
7894
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) return { drawing, target: "handle", pointIndex: 2 };
|
|
7403
7895
|
const indexSpan = p1.index - p0.index;
|
|
7404
7896
|
const slope = indexSpan !== 0 ? (p1.price - p0.price) / indexSpan : 0;
|
|
7405
7897
|
const offset = p2.price - (p0.price + slope * (p2.index - p0.index));
|
|
7406
7898
|
const q0y = canvasYFromDrawingPrice(p0.price + offset);
|
|
7407
7899
|
const q1y = canvasYFromDrawingPrice(p1.price + offset);
|
|
7408
|
-
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 || distanceToSegment(x, y, x0, q0y, x1, q1y) <= 6) {
|
|
7900
|
+
if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance || distanceToSegment(x, y, x0, q0y, x1, q1y) <= 6 * hitTolerance) {
|
|
7409
7901
|
return { drawing, target: "line" };
|
|
7410
7902
|
}
|
|
7411
7903
|
const minX = Math.min(x0, x1);
|
|
@@ -7418,7 +7910,7 @@ function createChart(element, options = {}) {
|
|
|
7418
7910
|
return { drawing, target: "line" };
|
|
7419
7911
|
}
|
|
7420
7912
|
}
|
|
7421
|
-
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6) {
|
|
7913
|
+
} else if (distanceToSegment(x, y, x0, y0, x1, y1) <= 6 * hitTolerance) {
|
|
7422
7914
|
return { drawing, target: "line" };
|
|
7423
7915
|
}
|
|
7424
7916
|
} else if (drawing.type === "arrow") {
|
|
@@ -7429,13 +7921,13 @@ function createChart(element, options = {}) {
|
|
|
7429
7921
|
const y1 = canvasYFromDrawingPrice(first.price);
|
|
7430
7922
|
const x2 = canvasXFromDrawingPoint(second);
|
|
7431
7923
|
const y2 = canvasYFromDrawingPrice(second.price);
|
|
7432
|
-
if (Math.hypot(x - x1, y - y1) <= 8) {
|
|
7924
|
+
if (Math.hypot(x - x1, y - y1) <= 8 * hitTolerance) {
|
|
7433
7925
|
return { drawing, target: "handle", pointIndex: 0 };
|
|
7434
7926
|
}
|
|
7435
|
-
if (Math.hypot(x - x2, y - y2) <= 8) {
|
|
7927
|
+
if (Math.hypot(x - x2, y - y2) <= 8 * hitTolerance) {
|
|
7436
7928
|
return { drawing, target: "handle", pointIndex: 1 };
|
|
7437
7929
|
}
|
|
7438
|
-
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
7930
|
+
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6 * hitTolerance) {
|
|
7439
7931
|
return { drawing, target: "line" };
|
|
7440
7932
|
}
|
|
7441
7933
|
} else if (drawing.type === "brush") {
|
|
@@ -7447,7 +7939,7 @@ function createChart(element, options = {}) {
|
|
|
7447
7939
|
for (let i = 1; i < pts.length; i += 1) {
|
|
7448
7940
|
const nextX = canvasXFromDrawingPoint(pts[i]);
|
|
7449
7941
|
const nextY = canvasYFromDrawingPrice(pts[i].price);
|
|
7450
|
-
if (distanceToSegment(x, y, prevX, prevY, nextX, nextY) <= 7) {
|
|
7942
|
+
if (distanceToSegment(x, y, prevX, prevY, nextX, nextY) <= 7 * hitTolerance) {
|
|
7451
7943
|
hitBody = true;
|
|
7452
7944
|
break;
|
|
7453
7945
|
}
|
|
@@ -7465,8 +7957,8 @@ function createChart(element, options = {}) {
|
|
|
7465
7957
|
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
7466
7958
|
const bx = canvasXFromDrawingPoint(boxPoint);
|
|
7467
7959
|
const by = canvasYFromDrawingPrice(boxPoint.price);
|
|
7468
|
-
if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7469
|
-
if (Math.hypot(x - bx, y - by) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7960
|
+
if (Math.hypot(x - ax, y - ay) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7961
|
+
if (Math.hypot(x - bx, y - by) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7470
7962
|
const fontSize = Math.max(6, drawing.fontSize);
|
|
7471
7963
|
const prevFont = ctx.font;
|
|
7472
7964
|
ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -7482,7 +7974,7 @@ function createChart(element, options = {}) {
|
|
|
7482
7974
|
if (x >= bx && x <= bx + blockW && y >= by && y <= by + blockH) {
|
|
7483
7975
|
return { drawing, target: "line" };
|
|
7484
7976
|
}
|
|
7485
|
-
if (distanceToSegment(x, y, bx + blockW / 2, by + blockH / 2, ax, ay) <= 5) {
|
|
7977
|
+
if (distanceToSegment(x, y, bx + blockW / 2, by + blockH / 2, ax, ay) <= 5 * hitTolerance) {
|
|
7486
7978
|
return { drawing, target: "line" };
|
|
7487
7979
|
}
|
|
7488
7980
|
} else if (drawing.type === "price-range") {
|
|
@@ -7493,8 +7985,8 @@ function createChart(element, options = {}) {
|
|
|
7493
7985
|
const px1 = canvasXFromDrawingPoint(p1);
|
|
7494
7986
|
const y0 = canvasYFromDrawingPrice(p0.price);
|
|
7495
7987
|
const y1 = canvasYFromDrawingPrice(p1.price);
|
|
7496
|
-
if (Math.hypot(x - px0, y - y0) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7497
|
-
if (Math.hypot(x - px1, y - y1) <= 9) return { drawing, target: "handle", pointIndex: 1 };
|
|
7988
|
+
if (Math.hypot(x - px0, y - y0) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7989
|
+
if (Math.hypot(x - px1, y - y1) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 1 };
|
|
7498
7990
|
if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
|
|
7499
7991
|
return { drawing, target: "line" };
|
|
7500
7992
|
}
|
|
@@ -7503,7 +7995,7 @@ function createChart(element, options = {}) {
|
|
|
7503
7995
|
if (!anchor) continue;
|
|
7504
7996
|
const ax = canvasXFromDrawingPoint(anchor);
|
|
7505
7997
|
const ay = canvasYFromDrawingPrice(anchor.price);
|
|
7506
|
-
if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
|
|
7998
|
+
if (Math.hypot(x - ax, y - ay) <= 9 * hitTolerance) return { drawing, target: "handle", pointIndex: 0 };
|
|
7507
7999
|
const fontSize = Math.max(6, drawing.fontSize);
|
|
7508
8000
|
const prevFont = ctx.font;
|
|
7509
8001
|
ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
|
|
@@ -7598,8 +8090,9 @@ function createChart(element, options = {}) {
|
|
|
7598
8090
|
if (!drawState || x < drawState.chartLeft || x > drawState.chartRight) {
|
|
7599
8091
|
return null;
|
|
7600
8092
|
}
|
|
8093
|
+
const dividerTolerance = touchInputActive ? 4 * Math.max(1, mergedOptions.touch?.hitToleranceScale ?? 2.2) : 4;
|
|
7601
8094
|
for (const pane of paneLayoutInfos) {
|
|
7602
|
-
if (pane.visible && Math.abs(y - pane.top) <=
|
|
8095
|
+
if (pane.visible && Math.abs(y - pane.top) <= dividerTolerance) {
|
|
7603
8096
|
return { id: pane.id, heightPx: pane.bottom - pane.top };
|
|
7604
8097
|
}
|
|
7605
8098
|
}
|
|
@@ -8092,7 +8585,43 @@ function createChart(element, options = {}) {
|
|
|
8092
8585
|
let lastPointerX = 0;
|
|
8093
8586
|
let lastPointerY = 0;
|
|
8094
8587
|
let activePointerId = null;
|
|
8588
|
+
let touchInputActive = false;
|
|
8095
8589
|
const touchPointers = /* @__PURE__ */ new Map();
|
|
8590
|
+
let longPressTimerId = null;
|
|
8591
|
+
let longPressState = null;
|
|
8592
|
+
const cancelLongPressTimer = () => {
|
|
8593
|
+
if (longPressTimerId !== null) {
|
|
8594
|
+
clearTimeout(longPressTimerId);
|
|
8595
|
+
longPressTimerId = null;
|
|
8596
|
+
}
|
|
8597
|
+
};
|
|
8598
|
+
const emitLongPress = (x, y, scrubbing) => {
|
|
8599
|
+
if (!longPressHandler) return;
|
|
8600
|
+
const rect = canvas.getBoundingClientRect();
|
|
8601
|
+
const index = indexFromCanvasX(x);
|
|
8602
|
+
const barTime = index === null ? null : getTimeForIndex(index);
|
|
8603
|
+
const bar = index === null ? void 0 : data[index];
|
|
8604
|
+
longPressHandler({
|
|
8605
|
+
x,
|
|
8606
|
+
y,
|
|
8607
|
+
clientX: rect.left + x,
|
|
8608
|
+
clientY: rect.top + y,
|
|
8609
|
+
scrubbing,
|
|
8610
|
+
...getHitRegion(x, y) === "plot" ? { price: roundToPricePrecision(priceFromCanvasY(y)) } : {},
|
|
8611
|
+
...index === null ? {} : { index },
|
|
8612
|
+
...barTime ? { time: barTime.toISOString() } : {},
|
|
8613
|
+
...bar ? {
|
|
8614
|
+
point: {
|
|
8615
|
+
t: bar.time.toISOString(),
|
|
8616
|
+
o: bar.o,
|
|
8617
|
+
h: bar.h,
|
|
8618
|
+
l: bar.l,
|
|
8619
|
+
c: bar.c,
|
|
8620
|
+
...bar.v === void 0 ? {} : { v: bar.v }
|
|
8621
|
+
}
|
|
8622
|
+
} : {}
|
|
8623
|
+
});
|
|
8624
|
+
};
|
|
8096
8625
|
let pinchZoomState = null;
|
|
8097
8626
|
const getTouchPair = () => {
|
|
8098
8627
|
const points = Array.from(touchPointers.values());
|
|
@@ -8158,7 +8687,8 @@ function createChart(element, options = {}) {
|
|
|
8158
8687
|
}
|
|
8159
8688
|
};
|
|
8160
8689
|
const onPointerDown = (event) => {
|
|
8161
|
-
|
|
8690
|
+
touchInputActive = event.pointerType === "touch" || event.pointerType === "pen";
|
|
8691
|
+
if (touchInputActive) {
|
|
8162
8692
|
event.preventDefault();
|
|
8163
8693
|
}
|
|
8164
8694
|
if (mergedOptions.accessibility?.focusable !== false && document.activeElement !== canvas) {
|
|
@@ -8175,9 +8705,25 @@ function createChart(element, options = {}) {
|
|
|
8175
8705
|
capturePointer(event.pointerId);
|
|
8176
8706
|
const touchPair = getTouchPair();
|
|
8177
8707
|
if (touchPair) {
|
|
8708
|
+
cancelLongPressTimer();
|
|
8709
|
+
longPressState = null;
|
|
8178
8710
|
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
8179
8711
|
return;
|
|
8180
8712
|
}
|
|
8713
|
+
cancelLongPressTimer();
|
|
8714
|
+
longPressState = null;
|
|
8715
|
+
const touchOptions = mergedOptions.touch ?? {};
|
|
8716
|
+
const wantsTooltip = touchOptions.longPressTooltip !== false || longPressHandler !== null;
|
|
8717
|
+
if (wantsTooltip && !activeDrawingTool && !draftDrawing && getHitRegion(point.x, point.y) === "plot") {
|
|
8718
|
+
const pointerId = event.pointerId;
|
|
8719
|
+
longPressTimerId = window.setTimeout(() => {
|
|
8720
|
+
longPressTimerId = null;
|
|
8721
|
+
longPressState = { pointerId, x: point.x, y: point.y };
|
|
8722
|
+
setCrosshairPoint(point);
|
|
8723
|
+
scheduleDraw({ overlayOnly: true });
|
|
8724
|
+
emitLongPress(point.x, point.y, false);
|
|
8725
|
+
}, Math.max(120, touchOptions.longPressMs ?? 400));
|
|
8726
|
+
}
|
|
8181
8727
|
}
|
|
8182
8728
|
if (measureState) {
|
|
8183
8729
|
if (measureState.dragging) {
|
|
@@ -8343,7 +8889,10 @@ function createChart(element, options = {}) {
|
|
|
8343
8889
|
};
|
|
8344
8890
|
const onPointerMove = (event) => {
|
|
8345
8891
|
if (event.pointerType === "touch" || event.pointerType === "pen") {
|
|
8892
|
+
touchInputActive = true;
|
|
8346
8893
|
event.preventDefault();
|
|
8894
|
+
} else if (event.pointerType === "mouse") {
|
|
8895
|
+
touchInputActive = false;
|
|
8347
8896
|
}
|
|
8348
8897
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
8349
8898
|
shiftKeyActive = event.shiftKey;
|
|
@@ -8352,6 +8901,8 @@ function createChart(element, options = {}) {
|
|
|
8352
8901
|
touchPointers.set(event.pointerId, point);
|
|
8353
8902
|
const touchPair = getTouchPair();
|
|
8354
8903
|
if (touchPair) {
|
|
8904
|
+
cancelLongPressTimer();
|
|
8905
|
+
longPressState = null;
|
|
8355
8906
|
if (!pinchZoomState) {
|
|
8356
8907
|
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
8357
8908
|
}
|
|
@@ -8361,6 +8912,21 @@ function createChart(element, options = {}) {
|
|
|
8361
8912
|
if (pinchZoomState) {
|
|
8362
8913
|
return;
|
|
8363
8914
|
}
|
|
8915
|
+
if (longPressState && longPressState.pointerId === event.pointerId) {
|
|
8916
|
+
longPressState = { ...longPressState, x: point.x, y: point.y };
|
|
8917
|
+
setCrosshairPoint(point);
|
|
8918
|
+
emitCrosshairMove(point.x, point.y, getHitRegion(point.x, point.y) === "plot" ? "plot" : "plot");
|
|
8919
|
+
scheduleDraw({ overlayOnly: true });
|
|
8920
|
+
emitLongPress(point.x, point.y, true);
|
|
8921
|
+
return;
|
|
8922
|
+
}
|
|
8923
|
+
if (longPressTimerId !== null) {
|
|
8924
|
+
const dx = point.x - (pointerDownInfo?.x ?? point.x);
|
|
8925
|
+
const dy = point.y - (pointerDownInfo?.y ?? point.y);
|
|
8926
|
+
if (dx * dx + dy * dy > 36) {
|
|
8927
|
+
cancelLongPressTimer();
|
|
8928
|
+
}
|
|
8929
|
+
}
|
|
8364
8930
|
}
|
|
8365
8931
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
8366
8932
|
const dx = point.x - pointerDownInfo.x;
|
|
@@ -8624,6 +9190,18 @@ function createChart(element, options = {}) {
|
|
|
8624
9190
|
lastPointerY = point.y;
|
|
8625
9191
|
};
|
|
8626
9192
|
const endPointerDrag = (event) => {
|
|
9193
|
+
cancelLongPressTimer();
|
|
9194
|
+
if (longPressState && (!event || event.pointerId === longPressState.pointerId)) {
|
|
9195
|
+
longPressState = null;
|
|
9196
|
+
touchPointers.delete(event?.pointerId ?? -1);
|
|
9197
|
+
setCrosshairPoint(null);
|
|
9198
|
+
isDragging = false;
|
|
9199
|
+
dragMode = null;
|
|
9200
|
+
activePointerId = null;
|
|
9201
|
+
pointerDownInfo = null;
|
|
9202
|
+
scheduleDraw({ overlayOnly: true });
|
|
9203
|
+
return;
|
|
9204
|
+
}
|
|
8627
9205
|
if (event?.pointerType === "touch") {
|
|
8628
9206
|
touchPointers.delete(event.pointerId);
|
|
8629
9207
|
if (pinchZoomState) {
|
|
@@ -9440,6 +10018,9 @@ function createChart(element, options = {}) {
|
|
|
9440
10018
|
const onKeyboardShortcut = (handler) => {
|
|
9441
10019
|
keyboardShortcutHandler = handler;
|
|
9442
10020
|
};
|
|
10021
|
+
const onLongPress = (handler) => {
|
|
10022
|
+
longPressHandler = handler;
|
|
10023
|
+
};
|
|
9443
10024
|
const focus = () => {
|
|
9444
10025
|
canvas.focus({ preventScroll: true });
|
|
9445
10026
|
};
|
|
@@ -9493,6 +10074,10 @@ function createChart(element, options = {}) {
|
|
|
9493
10074
|
return canvas.toDataURL(options2.type ?? "image/png", options2.quality);
|
|
9494
10075
|
};
|
|
9495
10076
|
const destroy = () => {
|
|
10077
|
+
cancelLongPressTimer();
|
|
10078
|
+
datafeedUnsubscribe?.();
|
|
10079
|
+
datafeedUnsubscribe = null;
|
|
10080
|
+
datafeed = null;
|
|
9496
10081
|
cancelKineticPan();
|
|
9497
10082
|
if (smoothingRafId !== null) {
|
|
9498
10083
|
cancelAnimationFrame(smoothingRafId);
|
|
@@ -9580,6 +10165,12 @@ function createChart(element, options = {}) {
|
|
|
9580
10165
|
getMagnetMode,
|
|
9581
10166
|
setPriceScale,
|
|
9582
10167
|
getPriceScale,
|
|
10168
|
+
setCompareSeries,
|
|
10169
|
+
addCompareSeries,
|
|
10170
|
+
removeCompareSeries,
|
|
10171
|
+
getCompareSeries,
|
|
10172
|
+
setDatafeed,
|
|
10173
|
+
onLongPress,
|
|
9583
10174
|
cancelDrawing,
|
|
9584
10175
|
setTradeMarkers,
|
|
9585
10176
|
setSelectedDrawing,
|