hyperprop-charting-library 0.1.141 → 0.1.143
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 +256 -73
- package/dist/hyperprop-charting-library.d.ts +72 -8
- package/dist/hyperprop-charting-library.js +256 -73
- package/dist/index.cjs +256 -73
- package/dist/index.d.cts +72 -8
- package/dist/index.d.ts +72 -8
- package/dist/index.js +256 -73
- package/docs/API.md +92 -0
- package/package.json +1 -1
|
@@ -335,26 +335,45 @@ var getSeriesFingerprint = (data) => {
|
|
|
335
335
|
var withCachedSeries = (key, data, compute) => {
|
|
336
336
|
const fingerprint = getSeriesFingerprint(data);
|
|
337
337
|
const existing = builtInSeriesCache.get(key);
|
|
338
|
-
|
|
339
|
-
|
|
338
|
+
const shape = getSeriesShape(data);
|
|
339
|
+
if (existing) {
|
|
340
|
+
const unchanged = existing.fingerprint === fingerprint;
|
|
341
|
+
const liveTickWithinThrottle = !unchanged && indicatorLiveThrottleMs > 0 && existing.shape === shape && Date.now() - existing.computedAt < indicatorLiveThrottleMs;
|
|
342
|
+
if (unchanged || liveTickWithinThrottle) {
|
|
343
|
+
return existing.values;
|
|
344
|
+
}
|
|
340
345
|
}
|
|
341
346
|
const values = compute();
|
|
342
|
-
builtInSeriesCache.set(key, { fingerprint, values });
|
|
347
|
+
builtInSeriesCache.set(key, { fingerprint, values, shape, computedAt: Date.now() });
|
|
343
348
|
return values;
|
|
344
349
|
};
|
|
345
350
|
var builtInComputationCache = /* @__PURE__ */ new Map();
|
|
351
|
+
var indicatorLiveThrottleMs = 80;
|
|
352
|
+
var setIndicatorLiveThrottleMs = (ms) => {
|
|
353
|
+
indicatorLiveThrottleMs = Math.max(0, ms);
|
|
354
|
+
};
|
|
355
|
+
var getSeriesShape = (data) => {
|
|
356
|
+
const length = data.length;
|
|
357
|
+
const last = length > 0 ? data[length - 1] : void 0;
|
|
358
|
+
return last ? `${length}|${last.time.getTime()}` : "empty";
|
|
359
|
+
};
|
|
346
360
|
var COMPUTATION_CACHE_MAX_ENTRIES = 128;
|
|
347
361
|
var withCachedComputation = (key, data, compute) => {
|
|
348
362
|
const fingerprint = getSeriesFingerprint(data);
|
|
349
363
|
const existing = builtInComputationCache.get(key);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
364
|
+
const shape = getSeriesShape(data);
|
|
365
|
+
if (existing) {
|
|
366
|
+
const unchanged = existing.fingerprint === fingerprint;
|
|
367
|
+
const liveTickWithinThrottle = !unchanged && indicatorLiveThrottleMs > 0 && existing.shape === shape && Date.now() - existing.computedAt < indicatorLiveThrottleMs;
|
|
368
|
+
if (unchanged || liveTickWithinThrottle) {
|
|
369
|
+
builtInComputationCache.delete(key);
|
|
370
|
+
builtInComputationCache.set(key, existing);
|
|
371
|
+
return existing.value;
|
|
372
|
+
}
|
|
354
373
|
}
|
|
355
374
|
const value = compute();
|
|
356
375
|
builtInComputationCache.delete(key);
|
|
357
|
-
builtInComputationCache.set(key, { fingerprint, value });
|
|
376
|
+
builtInComputationCache.set(key, { fingerprint, value, shape, computedAt: Date.now() });
|
|
358
377
|
while (builtInComputationCache.size > COMPUTATION_CACHE_MAX_ENTRIES) {
|
|
359
378
|
const oldest = builtInComputationCache.keys().next().value;
|
|
360
379
|
if (oldest === void 0) break;
|
|
@@ -2625,44 +2644,6 @@ var compileScriptIndicator = (definition) => {
|
|
|
2625
2644
|
|
|
2626
2645
|
// src/themes.ts
|
|
2627
2646
|
var CHART_THEMES = {
|
|
2628
|
-
dark: {
|
|
2629
|
-
name: "dark",
|
|
2630
|
-
background: "#0f0f0f",
|
|
2631
|
-
upColor: "#089981",
|
|
2632
|
-
downColor: "#f23645",
|
|
2633
|
-
lineColor: "#2962ff",
|
|
2634
|
-
gridColor: "#3c3c3c",
|
|
2635
|
-
gridOpacity: 0.5,
|
|
2636
|
-
axisTextColor: "#dbdbdb",
|
|
2637
|
-
axisLineColor: "#424242",
|
|
2638
|
-
crosshairColor: "#9598a1",
|
|
2639
|
-
crosshairLabelBackground: "#3c3c3c",
|
|
2640
|
-
crosshairLabelText: "#dbdbdb",
|
|
2641
|
-
crosshairLabelBorder: "#5d5d5d",
|
|
2642
|
-
watermarkColor: "rgba(219,219,219,0.06)",
|
|
2643
|
-
indicatorTextColor: "#b2b5be",
|
|
2644
|
-
labelTextColor: "#ffffff",
|
|
2645
|
-
labelBackground: "#1a1a1a"
|
|
2646
|
-
},
|
|
2647
|
-
light: {
|
|
2648
|
-
name: "light",
|
|
2649
|
-
background: "#ffffff",
|
|
2650
|
-
upColor: "#089981",
|
|
2651
|
-
downColor: "#f23645",
|
|
2652
|
-
lineColor: "#2962ff",
|
|
2653
|
-
gridColor: "#d5d5d5",
|
|
2654
|
-
gridOpacity: 0.9,
|
|
2655
|
-
axisTextColor: "#0f0f0f",
|
|
2656
|
-
axisLineColor: "#c3c3c3",
|
|
2657
|
-
crosshairColor: "#787b86",
|
|
2658
|
-
crosshairLabelBackground: "#4b4b4b",
|
|
2659
|
-
crosshairLabelText: "#ffffff",
|
|
2660
|
-
crosshairLabelBorder: "#878787",
|
|
2661
|
-
watermarkColor: "rgba(15,15,15,0.06)",
|
|
2662
|
-
indicatorTextColor: "#4b4b4b",
|
|
2663
|
-
labelTextColor: "#ffffff",
|
|
2664
|
-
labelBackground: "#f0f3fa"
|
|
2665
|
-
},
|
|
2666
2647
|
midnight: {
|
|
2667
2648
|
name: "midnight",
|
|
2668
2649
|
background: "#171717",
|
|
@@ -2682,6 +2663,63 @@ var CHART_THEMES = {
|
|
|
2682
2663
|
labelTextColor: "#dbeafe",
|
|
2683
2664
|
labelBackground: "#0b1220"
|
|
2684
2665
|
},
|
|
2666
|
+
light: {
|
|
2667
|
+
name: "light",
|
|
2668
|
+
background: "#ffffff",
|
|
2669
|
+
upColor: "#22ab94",
|
|
2670
|
+
downColor: "#f23645",
|
|
2671
|
+
lineColor: "#2563eb",
|
|
2672
|
+
gridColor: "#e0e3eb",
|
|
2673
|
+
gridOpacity: 0.9,
|
|
2674
|
+
axisTextColor: "#1f2430",
|
|
2675
|
+
axisLineColor: "#c8ccd6",
|
|
2676
|
+
crosshairColor: "#6b7280",
|
|
2677
|
+
crosshairLabelBackground: "#2a2e39",
|
|
2678
|
+
crosshairLabelText: "#ffffff",
|
|
2679
|
+
crosshairLabelBorder: "#6b7280",
|
|
2680
|
+
watermarkColor: "rgba(19,23,34,0.06)",
|
|
2681
|
+
indicatorTextColor: "#4b5563",
|
|
2682
|
+
labelTextColor: "#ffffff",
|
|
2683
|
+
labelBackground: "#f1f3f8"
|
|
2684
|
+
},
|
|
2685
|
+
brand: {
|
|
2686
|
+
name: "brand",
|
|
2687
|
+
background: "#0b1220",
|
|
2688
|
+
upColor: "#22ab94",
|
|
2689
|
+
downColor: "#f23645",
|
|
2690
|
+
lineColor: "#253cff",
|
|
2691
|
+
gridColor: "#1b2740",
|
|
2692
|
+
gridOpacity: 0.6,
|
|
2693
|
+
axisTextColor: "#c3cee8",
|
|
2694
|
+
axisLineColor: "#34405c",
|
|
2695
|
+
crosshairColor: "#6e85ff",
|
|
2696
|
+
crosshairLabelBackground: "#253cff",
|
|
2697
|
+
crosshairLabelText: "#ffffff",
|
|
2698
|
+
crosshairLabelBorder: "#6e85ff",
|
|
2699
|
+
watermarkColor: "rgba(37,60,255,0.08)",
|
|
2700
|
+
indicatorTextColor: "#a9b8dc",
|
|
2701
|
+
labelTextColor: "#ffffff",
|
|
2702
|
+
labelBackground: "#101a2e"
|
|
2703
|
+
},
|
|
2704
|
+
dark: {
|
|
2705
|
+
name: "dark",
|
|
2706
|
+
background: "#0f0f0f",
|
|
2707
|
+
upColor: "#089981",
|
|
2708
|
+
downColor: "#f23645",
|
|
2709
|
+
lineColor: "#2962ff",
|
|
2710
|
+
gridColor: "#3c3c3c",
|
|
2711
|
+
gridOpacity: 0.5,
|
|
2712
|
+
axisTextColor: "#dbdbdb",
|
|
2713
|
+
axisLineColor: "#424242",
|
|
2714
|
+
crosshairColor: "#9598a1",
|
|
2715
|
+
crosshairLabelBackground: "#3c3c3c",
|
|
2716
|
+
crosshairLabelText: "#dbdbdb",
|
|
2717
|
+
crosshairLabelBorder: "#5d5d5d",
|
|
2718
|
+
watermarkColor: "rgba(219,219,219,0.06)",
|
|
2719
|
+
indicatorTextColor: "#b2b5be",
|
|
2720
|
+
labelTextColor: "#ffffff",
|
|
2721
|
+
labelBackground: "#1a1a1a"
|
|
2722
|
+
},
|
|
2685
2723
|
"high-contrast": {
|
|
2686
2724
|
name: "high-contrast",
|
|
2687
2725
|
background: "#000000",
|
|
@@ -2703,7 +2741,7 @@ var CHART_THEMES = {
|
|
|
2703
2741
|
}
|
|
2704
2742
|
};
|
|
2705
2743
|
var CHART_THEME_NAMES = Object.keys(CHART_THEMES);
|
|
2706
|
-
var resolveTheme = (theme) => typeof theme === "string" ? CHART_THEMES[theme] ?? CHART_THEMES.
|
|
2744
|
+
var resolveTheme = (theme) => typeof theme === "string" ? CHART_THEMES[theme] ?? CHART_THEMES.midnight : theme;
|
|
2707
2745
|
|
|
2708
2746
|
// src/options.ts
|
|
2709
2747
|
var DEFAULT_GRID_OPTIONS = {
|
|
@@ -2905,7 +2943,23 @@ var DEFAULT_OPTIONS = {
|
|
|
2905
2943
|
accessibility: { label: "Price chart", description: "", focusable: true },
|
|
2906
2944
|
downsampling: { enabled: true, thresholdPx: 1.5 },
|
|
2907
2945
|
touch: { hitToleranceScale: 2.2, longPressTooltip: true, longPressMs: 400 },
|
|
2946
|
+
dataLine: {
|
|
2947
|
+
visible: false,
|
|
2948
|
+
symbol: "",
|
|
2949
|
+
interval: "",
|
|
2950
|
+
exchange: "",
|
|
2951
|
+
showOhlc: true,
|
|
2952
|
+
showChange: true,
|
|
2953
|
+
showVolume: false,
|
|
2954
|
+
details: [],
|
|
2955
|
+
statusColor: "",
|
|
2956
|
+
fontSize: 0,
|
|
2957
|
+
symbolColor: "",
|
|
2958
|
+
textColor: ""
|
|
2959
|
+
},
|
|
2908
2960
|
datafeedOptions: { prefetchThresholdBars: 150, cooldownMs: 1200, chunkBars: 1500 },
|
|
2961
|
+
animation: { viewportTransitions: true, durationMs: 260 },
|
|
2962
|
+
indicatorUpdate: { liveThrottleMs: 80 },
|
|
2909
2963
|
crosshair: DEFAULT_CROSSHAIR_OPTIONS,
|
|
2910
2964
|
grid: DEFAULT_GRID_OPTIONS,
|
|
2911
2965
|
watermark: DEFAULT_WATERMARK_OPTIONS,
|
|
@@ -2964,6 +3018,18 @@ var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
|
2964
3018
|
...baseOptions.touch,
|
|
2965
3019
|
...options.touch ?? {}
|
|
2966
3020
|
},
|
|
3021
|
+
dataLine: {
|
|
3022
|
+
...baseOptions.dataLine,
|
|
3023
|
+
...options.dataLine ?? {}
|
|
3024
|
+
},
|
|
3025
|
+
animation: {
|
|
3026
|
+
...baseOptions.animation,
|
|
3027
|
+
...options.animation ?? {}
|
|
3028
|
+
},
|
|
3029
|
+
indicatorUpdate: {
|
|
3030
|
+
...baseOptions.indicatorUpdate,
|
|
3031
|
+
...options.indicatorUpdate ?? {}
|
|
3032
|
+
},
|
|
2967
3033
|
datafeedOptions: {
|
|
2968
3034
|
...baseOptions.datafeedOptions,
|
|
2969
3035
|
...options.datafeedOptions ?? {}
|
|
@@ -3223,6 +3289,7 @@ function createChart(element, options = {}) {
|
|
|
3223
3289
|
let crosshairPoint = null;
|
|
3224
3290
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
3225
3291
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
3292
|
+
setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
|
|
3226
3293
|
let noOverlappingLineLabels = DEFAULT_LABELS_OPTIONS.noOverlapping;
|
|
3227
3294
|
let rightAxisLabelSlots = [];
|
|
3228
3295
|
let plotLabelSlots = [];
|
|
@@ -3464,21 +3531,69 @@ function createChart(element, options = {}) {
|
|
|
3464
3531
|
Math.min(highCenter, count + maxPanBars)
|
|
3465
3532
|
);
|
|
3466
3533
|
};
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
if (
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3534
|
+
let viewportTweenRaf = null;
|
|
3535
|
+
const cancelViewportTween = () => {
|
|
3536
|
+
if (viewportTweenRaf !== null) {
|
|
3537
|
+
cancelAnimationFrame(viewportTweenRaf);
|
|
3538
|
+
viewportTweenRaf = null;
|
|
3539
|
+
}
|
|
3540
|
+
};
|
|
3541
|
+
const prefersReducedMotion = () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3542
|
+
const animateViewportTo = (targetCenter, targetSpan, onComplete) => {
|
|
3543
|
+
const animation = mergedOptions.animation ?? DEFAULT_OPTIONS.animation;
|
|
3544
|
+
const duration = Math.max(0, animation?.durationMs ?? 260);
|
|
3545
|
+
const startCenter = xCenter;
|
|
3546
|
+
const startSpan = xSpan;
|
|
3547
|
+
const centerDelta = targetCenter - startCenter;
|
|
3548
|
+
const spanRatio = startSpan > 0 ? targetSpan / startSpan : 1;
|
|
3549
|
+
const negligible = Math.abs(centerDelta) < 0.5 && Math.abs(spanRatio - 1) < 0.01;
|
|
3550
|
+
if (animation?.viewportTransitions === false || duration === 0 || negligible || prefersReducedMotion() || typeof requestAnimationFrame !== "function") {
|
|
3551
|
+
xCenter = targetCenter;
|
|
3552
|
+
xSpan = targetSpan;
|
|
3553
|
+
clampXViewport();
|
|
3554
|
+
onComplete?.();
|
|
3555
|
+
emitViewportChange();
|
|
3556
|
+
scheduleDraw();
|
|
3557
|
+
return false;
|
|
3473
3558
|
}
|
|
3559
|
+
cancelViewportTween();
|
|
3560
|
+
const startedAt = performance.now();
|
|
3561
|
+
const step = (now) => {
|
|
3562
|
+
const t = clamp((now - startedAt) / duration, 0, 1);
|
|
3563
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
3564
|
+
xCenter = startCenter + centerDelta * eased;
|
|
3565
|
+
xSpan = startSpan * Math.pow(spanRatio, eased);
|
|
3566
|
+
clampXViewport();
|
|
3567
|
+
scheduleDraw();
|
|
3568
|
+
if (t < 1) {
|
|
3569
|
+
viewportTweenRaf = requestAnimationFrame(step);
|
|
3570
|
+
return;
|
|
3571
|
+
}
|
|
3572
|
+
viewportTweenRaf = null;
|
|
3573
|
+
xCenter = targetCenter;
|
|
3574
|
+
xSpan = targetSpan;
|
|
3575
|
+
clampXViewport();
|
|
3576
|
+
onComplete?.();
|
|
3577
|
+
emitViewportChange();
|
|
3578
|
+
scheduleDraw();
|
|
3579
|
+
};
|
|
3580
|
+
viewportTweenRaf = requestAnimationFrame(step);
|
|
3581
|
+
return true;
|
|
3582
|
+
};
|
|
3583
|
+
const getFitXTarget = () => {
|
|
3584
|
+
const count = data.length;
|
|
3585
|
+
if (count === 0) return { center: 0, span: 60 };
|
|
3474
3586
|
const maxSpan = Math.min(maxVisibleBars, Math.max(minVisibleBars, count + maxPanBars * 2));
|
|
3475
|
-
const
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
}
|
|
3480
|
-
|
|
3481
|
-
|
|
3587
|
+
const span = clamp(Math.floor(mergedOptions.initialVisibleBars), minVisibleBars, maxSpan);
|
|
3588
|
+
return {
|
|
3589
|
+
center: mergedOptions.initialViewport === "center" ? count / 2 : count - span / 2 + rightEdgePaddingBars,
|
|
3590
|
+
span
|
|
3591
|
+
};
|
|
3592
|
+
};
|
|
3593
|
+
const fitXViewport = () => {
|
|
3594
|
+
const target = getFitXTarget();
|
|
3595
|
+
xSpan = target.span;
|
|
3596
|
+
xCenter = target.center;
|
|
3482
3597
|
clampXViewport();
|
|
3483
3598
|
};
|
|
3484
3599
|
const getYBounds = () => {
|
|
@@ -6875,6 +6990,73 @@ function createChart(element, options = {}) {
|
|
|
6875
6990
|
for (const orderLine of orderLines) {
|
|
6876
6991
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
6877
6992
|
}
|
|
6993
|
+
let dataLineBottom = chartTop;
|
|
6994
|
+
const dataLine = mergedOptions.dataLine;
|
|
6995
|
+
if (dataLine?.visible && data.length > 0) {
|
|
6996
|
+
const hoverIndex = crosshairPoint && getHitRegion(crosshairPoint.x, crosshairPoint.y) === "plot" ? indexFromCanvasX(crosshairPoint.x) : null;
|
|
6997
|
+
const barIndex = hoverIndex !== null && hoverIndex >= 0 && hoverIndex < data.length ? hoverIndex : data.length - 1;
|
|
6998
|
+
const bar = data[barIndex];
|
|
6999
|
+
if (bar) {
|
|
7000
|
+
const prevFont = ctx.font;
|
|
7001
|
+
const fontSize = Math.max(9, dataLine.fontSize || axis.fontSize);
|
|
7002
|
+
ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
|
|
7003
|
+
ctx.textBaseline = "top";
|
|
7004
|
+
ctx.textAlign = "left";
|
|
7005
|
+
const symbolColor = dataLine.symbolColor || axis.textColor;
|
|
7006
|
+
const textColor = dataLine.textColor || labels.indicatorTextColor;
|
|
7007
|
+
const changeAbs = bar.c - bar.o;
|
|
7008
|
+
const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
|
|
7009
|
+
const rowY = chartTop + 6;
|
|
7010
|
+
let cursorX = chartLeft + 10;
|
|
7011
|
+
if (dataLine.statusColor) {
|
|
7012
|
+
const dotRadius = Math.max(2.5, fontSize / 4);
|
|
7013
|
+
ctx.fillStyle = dataLine.statusColor;
|
|
7014
|
+
ctx.beginPath();
|
|
7015
|
+
ctx.arc(cursorX + dotRadius, rowY + fontSize / 2, dotRadius, 0, Math.PI * 2);
|
|
7016
|
+
ctx.fill();
|
|
7017
|
+
cursorX += dotRadius * 2 + 6;
|
|
7018
|
+
}
|
|
7019
|
+
const headline = [dataLine.symbol, dataLine.interval, dataLine.exchange].filter((part) => part && part.length > 0).join(" \xB7 ");
|
|
7020
|
+
if (headline) {
|
|
7021
|
+
ctx.fillStyle = symbolColor;
|
|
7022
|
+
ctx.fillText(headline, cursorX, rowY);
|
|
7023
|
+
cursorX += measureTextWidth(headline) + 10;
|
|
7024
|
+
}
|
|
7025
|
+
if (dataLine.showOhlc !== false) {
|
|
7026
|
+
const ohlc = `O${formatPrice(bar.o)} H${formatPrice(bar.h)} L${formatPrice(bar.l)} C${formatPrice(bar.c)}`;
|
|
7027
|
+
ctx.fillStyle = changeColor;
|
|
7028
|
+
ctx.fillText(ohlc, cursorX, rowY);
|
|
7029
|
+
cursorX += measureTextWidth(ohlc) + 8;
|
|
7030
|
+
}
|
|
7031
|
+
if (dataLine.showChange !== false) {
|
|
7032
|
+
const pct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
|
|
7033
|
+
const changeText = `${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${pct.toFixed(2)}%)`;
|
|
7034
|
+
ctx.fillStyle = changeColor;
|
|
7035
|
+
ctx.fillText(changeText, cursorX, rowY);
|
|
7036
|
+
cursorX += measureTextWidth(changeText) + 10;
|
|
7037
|
+
}
|
|
7038
|
+
if (dataLine.showVolume && bar.v !== void 0) {
|
|
7039
|
+
const volumeText = `Vol ${bar.v >= 1e6 ? `${(bar.v / 1e6).toFixed(2)}M` : bar.v >= 1e3 ? `${(bar.v / 1e3).toFixed(1)}K` : String(Math.round(bar.v))}`;
|
|
7040
|
+
ctx.fillStyle = textColor;
|
|
7041
|
+
ctx.fillText(volumeText, cursorX, rowY);
|
|
7042
|
+
cursorX += measureTextWidth(volumeText) + 10;
|
|
7043
|
+
}
|
|
7044
|
+
for (const detail of dataLine.details ?? []) {
|
|
7045
|
+
const text = detail.label ? `${detail.label} ${detail.value}` : detail.value;
|
|
7046
|
+
const width2 = measureTextWidth(text);
|
|
7047
|
+
if (cursorX + width2 > chartRight - 4) break;
|
|
7048
|
+
if (detail.background) {
|
|
7049
|
+
ctx.fillStyle = detail.background;
|
|
7050
|
+
fillRoundedRect(cursorX - 4, rowY - 2, width2 + 8, fontSize + 4, 3);
|
|
7051
|
+
}
|
|
7052
|
+
ctx.fillStyle = detail.color || textColor;
|
|
7053
|
+
ctx.fillText(text, cursorX, rowY);
|
|
7054
|
+
cursorX += width2 + 12;
|
|
7055
|
+
}
|
|
7056
|
+
dataLineBottom = rowY + fontSize + 4;
|
|
7057
|
+
ctx.font = prevFont;
|
|
7058
|
+
}
|
|
7059
|
+
}
|
|
6878
7060
|
if (labels.visible && (labels.showIndicatorNames || labels.showIndicatorValues)) {
|
|
6879
7061
|
const isLegendInputValue = (value) => {
|
|
6880
7062
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
@@ -6903,7 +7085,7 @@ function createChart(element, options = {}) {
|
|
|
6903
7085
|
const isRight = position === "top-right" || position === "bottom-right";
|
|
6904
7086
|
const isBottom = position === "bottom-left" || position === "bottom-right";
|
|
6905
7087
|
const legendX = isRight ? chartRight - offsetX : chartLeft + offsetX;
|
|
6906
|
-
const legendY = isBottom ? chartBottom - offsetY : chartTop + offsetY;
|
|
7088
|
+
const legendY = isBottom ? chartBottom - offsetY : Math.max(chartTop + offsetY, dataLineBottom);
|
|
6907
7089
|
drawText(legendText, legendX, legendY, isRight ? "right" : "left", isBottom ? "bottom" : "top", labels.indicatorTextColor);
|
|
6908
7090
|
ctx.font = prevFont;
|
|
6909
7091
|
}
|
|
@@ -7444,27 +7626,24 @@ function createChart(element, options = {}) {
|
|
|
7444
7626
|
scheduleDraw();
|
|
7445
7627
|
};
|
|
7446
7628
|
const fitContent = () => {
|
|
7447
|
-
|
|
7448
|
-
updateFollowLatest(true);
|
|
7449
|
-
emitViewportChange();
|
|
7450
|
-
scheduleDraw();
|
|
7629
|
+
const target = getFitXTarget();
|
|
7630
|
+
animateViewportTo(target.center, target.span, () => updateFollowLatest(true));
|
|
7451
7631
|
};
|
|
7452
7632
|
const resetViewport = () => {
|
|
7453
7633
|
cancelKineticPan();
|
|
7454
|
-
|
|
7634
|
+
const target = getFitXTarget();
|
|
7455
7635
|
resetYViewport();
|
|
7456
|
-
updateFollowLatest(true);
|
|
7457
|
-
emitViewportChange();
|
|
7458
|
-
scheduleDraw();
|
|
7636
|
+
animateViewportTo(target.center, target.span, () => updateFollowLatest(true));
|
|
7459
7637
|
};
|
|
7460
7638
|
const isFollowingLatest = () => followLatest;
|
|
7461
7639
|
const setFollowingLatest = (follow) => {
|
|
7462
7640
|
if (follow && data.length > 0) {
|
|
7463
7641
|
cancelKineticPan();
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7642
|
+
animateViewportTo(
|
|
7643
|
+
data.length - xSpan / 2 + rightEdgePaddingBars,
|
|
7644
|
+
xSpan,
|
|
7645
|
+
() => updateFollowLatest(true)
|
|
7646
|
+
);
|
|
7468
7647
|
} else {
|
|
7469
7648
|
updateFollowLatest(follow);
|
|
7470
7649
|
}
|
|
@@ -8747,6 +8926,7 @@ function createChart(element, options = {}) {
|
|
|
8747
8926
|
canvas.focus({ preventScroll: true });
|
|
8748
8927
|
}
|
|
8749
8928
|
cancelKineticPan();
|
|
8929
|
+
cancelViewportTween();
|
|
8750
8930
|
panVelocitySamples.length = 0;
|
|
8751
8931
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
8752
8932
|
shiftKeyActive = event.shiftKey;
|
|
@@ -9411,6 +9591,7 @@ function createChart(element, options = {}) {
|
|
|
9411
9591
|
return;
|
|
9412
9592
|
}
|
|
9413
9593
|
cancelKineticPan();
|
|
9594
|
+
cancelViewportTween();
|
|
9414
9595
|
const point = getCanvasPoint(event);
|
|
9415
9596
|
const region = getHitRegion(point.x, point.y);
|
|
9416
9597
|
if (region === "outside") {
|
|
@@ -9688,6 +9869,7 @@ function createChart(element, options = {}) {
|
|
|
9688
9869
|
resetRightMarginCache();
|
|
9689
9870
|
doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
9690
9871
|
doubleClickAction = mergedOptions.doubleClickAction;
|
|
9872
|
+
setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
|
|
9691
9873
|
applyAccessibilityAttributes();
|
|
9692
9874
|
const isTickerSmoothingEnabled = mergedOptions.tickerLine?.smoothing ?? false;
|
|
9693
9875
|
if (!isTickerSmoothingEnabled) {
|
|
@@ -10166,6 +10348,7 @@ function createChart(element, options = {}) {
|
|
|
10166
10348
|
return canvas.toDataURL(options2.type ?? "image/png", options2.quality);
|
|
10167
10349
|
};
|
|
10168
10350
|
const destroy = () => {
|
|
10351
|
+
cancelViewportTween();
|
|
10169
10352
|
cancelLongPressTimer();
|
|
10170
10353
|
datafeedUnsubscribe?.();
|
|
10171
10354
|
datafeedUnsubscribe = null;
|