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
|
@@ -369,26 +369,45 @@ var getSeriesFingerprint = (data) => {
|
|
|
369
369
|
var withCachedSeries = (key, data, compute) => {
|
|
370
370
|
const fingerprint = getSeriesFingerprint(data);
|
|
371
371
|
const existing = builtInSeriesCache.get(key);
|
|
372
|
-
|
|
373
|
-
|
|
372
|
+
const shape = getSeriesShape(data);
|
|
373
|
+
if (existing) {
|
|
374
|
+
const unchanged = existing.fingerprint === fingerprint;
|
|
375
|
+
const liveTickWithinThrottle = !unchanged && indicatorLiveThrottleMs > 0 && existing.shape === shape && Date.now() - existing.computedAt < indicatorLiveThrottleMs;
|
|
376
|
+
if (unchanged || liveTickWithinThrottle) {
|
|
377
|
+
return existing.values;
|
|
378
|
+
}
|
|
374
379
|
}
|
|
375
380
|
const values = compute();
|
|
376
|
-
builtInSeriesCache.set(key, { fingerprint, values });
|
|
381
|
+
builtInSeriesCache.set(key, { fingerprint, values, shape, computedAt: Date.now() });
|
|
377
382
|
return values;
|
|
378
383
|
};
|
|
379
384
|
var builtInComputationCache = /* @__PURE__ */ new Map();
|
|
385
|
+
var indicatorLiveThrottleMs = 80;
|
|
386
|
+
var setIndicatorLiveThrottleMs = (ms) => {
|
|
387
|
+
indicatorLiveThrottleMs = Math.max(0, ms);
|
|
388
|
+
};
|
|
389
|
+
var getSeriesShape = (data) => {
|
|
390
|
+
const length = data.length;
|
|
391
|
+
const last = length > 0 ? data[length - 1] : void 0;
|
|
392
|
+
return last ? `${length}|${last.time.getTime()}` : "empty";
|
|
393
|
+
};
|
|
380
394
|
var COMPUTATION_CACHE_MAX_ENTRIES = 128;
|
|
381
395
|
var withCachedComputation = (key, data, compute) => {
|
|
382
396
|
const fingerprint = getSeriesFingerprint(data);
|
|
383
397
|
const existing = builtInComputationCache.get(key);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
398
|
+
const shape = getSeriesShape(data);
|
|
399
|
+
if (existing) {
|
|
400
|
+
const unchanged = existing.fingerprint === fingerprint;
|
|
401
|
+
const liveTickWithinThrottle = !unchanged && indicatorLiveThrottleMs > 0 && existing.shape === shape && Date.now() - existing.computedAt < indicatorLiveThrottleMs;
|
|
402
|
+
if (unchanged || liveTickWithinThrottle) {
|
|
403
|
+
builtInComputationCache.delete(key);
|
|
404
|
+
builtInComputationCache.set(key, existing);
|
|
405
|
+
return existing.value;
|
|
406
|
+
}
|
|
388
407
|
}
|
|
389
408
|
const value = compute();
|
|
390
409
|
builtInComputationCache.delete(key);
|
|
391
|
-
builtInComputationCache.set(key, { fingerprint, value });
|
|
410
|
+
builtInComputationCache.set(key, { fingerprint, value, shape, computedAt: Date.now() });
|
|
392
411
|
while (builtInComputationCache.size > COMPUTATION_CACHE_MAX_ENTRIES) {
|
|
393
412
|
const oldest = builtInComputationCache.keys().next().value;
|
|
394
413
|
if (oldest === void 0) break;
|
|
@@ -2659,44 +2678,6 @@ var compileScriptIndicator = (definition) => {
|
|
|
2659
2678
|
|
|
2660
2679
|
// src/themes.ts
|
|
2661
2680
|
var CHART_THEMES = {
|
|
2662
|
-
dark: {
|
|
2663
|
-
name: "dark",
|
|
2664
|
-
background: "#0f0f0f",
|
|
2665
|
-
upColor: "#089981",
|
|
2666
|
-
downColor: "#f23645",
|
|
2667
|
-
lineColor: "#2962ff",
|
|
2668
|
-
gridColor: "#3c3c3c",
|
|
2669
|
-
gridOpacity: 0.5,
|
|
2670
|
-
axisTextColor: "#dbdbdb",
|
|
2671
|
-
axisLineColor: "#424242",
|
|
2672
|
-
crosshairColor: "#9598a1",
|
|
2673
|
-
crosshairLabelBackground: "#3c3c3c",
|
|
2674
|
-
crosshairLabelText: "#dbdbdb",
|
|
2675
|
-
crosshairLabelBorder: "#5d5d5d",
|
|
2676
|
-
watermarkColor: "rgba(219,219,219,0.06)",
|
|
2677
|
-
indicatorTextColor: "#b2b5be",
|
|
2678
|
-
labelTextColor: "#ffffff",
|
|
2679
|
-
labelBackground: "#1a1a1a"
|
|
2680
|
-
},
|
|
2681
|
-
light: {
|
|
2682
|
-
name: "light",
|
|
2683
|
-
background: "#ffffff",
|
|
2684
|
-
upColor: "#089981",
|
|
2685
|
-
downColor: "#f23645",
|
|
2686
|
-
lineColor: "#2962ff",
|
|
2687
|
-
gridColor: "#d5d5d5",
|
|
2688
|
-
gridOpacity: 0.9,
|
|
2689
|
-
axisTextColor: "#0f0f0f",
|
|
2690
|
-
axisLineColor: "#c3c3c3",
|
|
2691
|
-
crosshairColor: "#787b86",
|
|
2692
|
-
crosshairLabelBackground: "#4b4b4b",
|
|
2693
|
-
crosshairLabelText: "#ffffff",
|
|
2694
|
-
crosshairLabelBorder: "#878787",
|
|
2695
|
-
watermarkColor: "rgba(15,15,15,0.06)",
|
|
2696
|
-
indicatorTextColor: "#4b4b4b",
|
|
2697
|
-
labelTextColor: "#ffffff",
|
|
2698
|
-
labelBackground: "#f0f3fa"
|
|
2699
|
-
},
|
|
2700
2681
|
midnight: {
|
|
2701
2682
|
name: "midnight",
|
|
2702
2683
|
background: "#171717",
|
|
@@ -2716,6 +2697,63 @@ var CHART_THEMES = {
|
|
|
2716
2697
|
labelTextColor: "#dbeafe",
|
|
2717
2698
|
labelBackground: "#0b1220"
|
|
2718
2699
|
},
|
|
2700
|
+
light: {
|
|
2701
|
+
name: "light",
|
|
2702
|
+
background: "#ffffff",
|
|
2703
|
+
upColor: "#22ab94",
|
|
2704
|
+
downColor: "#f23645",
|
|
2705
|
+
lineColor: "#2563eb",
|
|
2706
|
+
gridColor: "#e0e3eb",
|
|
2707
|
+
gridOpacity: 0.9,
|
|
2708
|
+
axisTextColor: "#1f2430",
|
|
2709
|
+
axisLineColor: "#c8ccd6",
|
|
2710
|
+
crosshairColor: "#6b7280",
|
|
2711
|
+
crosshairLabelBackground: "#2a2e39",
|
|
2712
|
+
crosshairLabelText: "#ffffff",
|
|
2713
|
+
crosshairLabelBorder: "#6b7280",
|
|
2714
|
+
watermarkColor: "rgba(19,23,34,0.06)",
|
|
2715
|
+
indicatorTextColor: "#4b5563",
|
|
2716
|
+
labelTextColor: "#ffffff",
|
|
2717
|
+
labelBackground: "#f1f3f8"
|
|
2718
|
+
},
|
|
2719
|
+
brand: {
|
|
2720
|
+
name: "brand",
|
|
2721
|
+
background: "#0b1220",
|
|
2722
|
+
upColor: "#22ab94",
|
|
2723
|
+
downColor: "#f23645",
|
|
2724
|
+
lineColor: "#253cff",
|
|
2725
|
+
gridColor: "#1b2740",
|
|
2726
|
+
gridOpacity: 0.6,
|
|
2727
|
+
axisTextColor: "#c3cee8",
|
|
2728
|
+
axisLineColor: "#34405c",
|
|
2729
|
+
crosshairColor: "#6e85ff",
|
|
2730
|
+
crosshairLabelBackground: "#253cff",
|
|
2731
|
+
crosshairLabelText: "#ffffff",
|
|
2732
|
+
crosshairLabelBorder: "#6e85ff",
|
|
2733
|
+
watermarkColor: "rgba(37,60,255,0.08)",
|
|
2734
|
+
indicatorTextColor: "#a9b8dc",
|
|
2735
|
+
labelTextColor: "#ffffff",
|
|
2736
|
+
labelBackground: "#101a2e"
|
|
2737
|
+
},
|
|
2738
|
+
dark: {
|
|
2739
|
+
name: "dark",
|
|
2740
|
+
background: "#0f0f0f",
|
|
2741
|
+
upColor: "#089981",
|
|
2742
|
+
downColor: "#f23645",
|
|
2743
|
+
lineColor: "#2962ff",
|
|
2744
|
+
gridColor: "#3c3c3c",
|
|
2745
|
+
gridOpacity: 0.5,
|
|
2746
|
+
axisTextColor: "#dbdbdb",
|
|
2747
|
+
axisLineColor: "#424242",
|
|
2748
|
+
crosshairColor: "#9598a1",
|
|
2749
|
+
crosshairLabelBackground: "#3c3c3c",
|
|
2750
|
+
crosshairLabelText: "#dbdbdb",
|
|
2751
|
+
crosshairLabelBorder: "#5d5d5d",
|
|
2752
|
+
watermarkColor: "rgba(219,219,219,0.06)",
|
|
2753
|
+
indicatorTextColor: "#b2b5be",
|
|
2754
|
+
labelTextColor: "#ffffff",
|
|
2755
|
+
labelBackground: "#1a1a1a"
|
|
2756
|
+
},
|
|
2719
2757
|
"high-contrast": {
|
|
2720
2758
|
name: "high-contrast",
|
|
2721
2759
|
background: "#000000",
|
|
@@ -2737,7 +2775,7 @@ var CHART_THEMES = {
|
|
|
2737
2775
|
}
|
|
2738
2776
|
};
|
|
2739
2777
|
var CHART_THEME_NAMES = Object.keys(CHART_THEMES);
|
|
2740
|
-
var resolveTheme = (theme) => typeof theme === "string" ? CHART_THEMES[theme] ?? CHART_THEMES.
|
|
2778
|
+
var resolveTheme = (theme) => typeof theme === "string" ? CHART_THEMES[theme] ?? CHART_THEMES.midnight : theme;
|
|
2741
2779
|
|
|
2742
2780
|
// src/options.ts
|
|
2743
2781
|
var DEFAULT_GRID_OPTIONS = {
|
|
@@ -2939,7 +2977,23 @@ var DEFAULT_OPTIONS = {
|
|
|
2939
2977
|
accessibility: { label: "Price chart", description: "", focusable: true },
|
|
2940
2978
|
downsampling: { enabled: true, thresholdPx: 1.5 },
|
|
2941
2979
|
touch: { hitToleranceScale: 2.2, longPressTooltip: true, longPressMs: 400 },
|
|
2980
|
+
dataLine: {
|
|
2981
|
+
visible: false,
|
|
2982
|
+
symbol: "",
|
|
2983
|
+
interval: "",
|
|
2984
|
+
exchange: "",
|
|
2985
|
+
showOhlc: true,
|
|
2986
|
+
showChange: true,
|
|
2987
|
+
showVolume: false,
|
|
2988
|
+
details: [],
|
|
2989
|
+
statusColor: "",
|
|
2990
|
+
fontSize: 0,
|
|
2991
|
+
symbolColor: "",
|
|
2992
|
+
textColor: ""
|
|
2993
|
+
},
|
|
2942
2994
|
datafeedOptions: { prefetchThresholdBars: 150, cooldownMs: 1200, chunkBars: 1500 },
|
|
2995
|
+
animation: { viewportTransitions: true, durationMs: 260 },
|
|
2996
|
+
indicatorUpdate: { liveThrottleMs: 80 },
|
|
2943
2997
|
crosshair: DEFAULT_CROSSHAIR_OPTIONS,
|
|
2944
2998
|
grid: DEFAULT_GRID_OPTIONS,
|
|
2945
2999
|
watermark: DEFAULT_WATERMARK_OPTIONS,
|
|
@@ -2998,6 +3052,18 @@ var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
|
2998
3052
|
...baseOptions.touch,
|
|
2999
3053
|
...options.touch ?? {}
|
|
3000
3054
|
},
|
|
3055
|
+
dataLine: {
|
|
3056
|
+
...baseOptions.dataLine,
|
|
3057
|
+
...options.dataLine ?? {}
|
|
3058
|
+
},
|
|
3059
|
+
animation: {
|
|
3060
|
+
...baseOptions.animation,
|
|
3061
|
+
...options.animation ?? {}
|
|
3062
|
+
},
|
|
3063
|
+
indicatorUpdate: {
|
|
3064
|
+
...baseOptions.indicatorUpdate,
|
|
3065
|
+
...options.indicatorUpdate ?? {}
|
|
3066
|
+
},
|
|
3001
3067
|
datafeedOptions: {
|
|
3002
3068
|
...baseOptions.datafeedOptions,
|
|
3003
3069
|
...options.datafeedOptions ?? {}
|
|
@@ -3257,6 +3323,7 @@ function createChart(element, options = {}) {
|
|
|
3257
3323
|
let crosshairPoint = null;
|
|
3258
3324
|
let doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
3259
3325
|
let doubleClickAction = mergedOptions.doubleClickAction;
|
|
3326
|
+
setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
|
|
3260
3327
|
let noOverlappingLineLabels = DEFAULT_LABELS_OPTIONS.noOverlapping;
|
|
3261
3328
|
let rightAxisLabelSlots = [];
|
|
3262
3329
|
let plotLabelSlots = [];
|
|
@@ -3498,21 +3565,69 @@ function createChart(element, options = {}) {
|
|
|
3498
3565
|
Math.min(highCenter, count + maxPanBars)
|
|
3499
3566
|
);
|
|
3500
3567
|
};
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
if (
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3568
|
+
let viewportTweenRaf = null;
|
|
3569
|
+
const cancelViewportTween = () => {
|
|
3570
|
+
if (viewportTweenRaf !== null) {
|
|
3571
|
+
cancelAnimationFrame(viewportTweenRaf);
|
|
3572
|
+
viewportTweenRaf = null;
|
|
3573
|
+
}
|
|
3574
|
+
};
|
|
3575
|
+
const prefersReducedMotion = () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3576
|
+
const animateViewportTo = (targetCenter, targetSpan, onComplete) => {
|
|
3577
|
+
const animation = mergedOptions.animation ?? DEFAULT_OPTIONS.animation;
|
|
3578
|
+
const duration = Math.max(0, animation?.durationMs ?? 260);
|
|
3579
|
+
const startCenter = xCenter;
|
|
3580
|
+
const startSpan = xSpan;
|
|
3581
|
+
const centerDelta = targetCenter - startCenter;
|
|
3582
|
+
const spanRatio = startSpan > 0 ? targetSpan / startSpan : 1;
|
|
3583
|
+
const negligible = Math.abs(centerDelta) < 0.5 && Math.abs(spanRatio - 1) < 0.01;
|
|
3584
|
+
if (animation?.viewportTransitions === false || duration === 0 || negligible || prefersReducedMotion() || typeof requestAnimationFrame !== "function") {
|
|
3585
|
+
xCenter = targetCenter;
|
|
3586
|
+
xSpan = targetSpan;
|
|
3587
|
+
clampXViewport();
|
|
3588
|
+
onComplete?.();
|
|
3589
|
+
emitViewportChange();
|
|
3590
|
+
scheduleDraw();
|
|
3591
|
+
return false;
|
|
3507
3592
|
}
|
|
3593
|
+
cancelViewportTween();
|
|
3594
|
+
const startedAt = performance.now();
|
|
3595
|
+
const step = (now) => {
|
|
3596
|
+
const t = clamp((now - startedAt) / duration, 0, 1);
|
|
3597
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
3598
|
+
xCenter = startCenter + centerDelta * eased;
|
|
3599
|
+
xSpan = startSpan * Math.pow(spanRatio, eased);
|
|
3600
|
+
clampXViewport();
|
|
3601
|
+
scheduleDraw();
|
|
3602
|
+
if (t < 1) {
|
|
3603
|
+
viewportTweenRaf = requestAnimationFrame(step);
|
|
3604
|
+
return;
|
|
3605
|
+
}
|
|
3606
|
+
viewportTweenRaf = null;
|
|
3607
|
+
xCenter = targetCenter;
|
|
3608
|
+
xSpan = targetSpan;
|
|
3609
|
+
clampXViewport();
|
|
3610
|
+
onComplete?.();
|
|
3611
|
+
emitViewportChange();
|
|
3612
|
+
scheduleDraw();
|
|
3613
|
+
};
|
|
3614
|
+
viewportTweenRaf = requestAnimationFrame(step);
|
|
3615
|
+
return true;
|
|
3616
|
+
};
|
|
3617
|
+
const getFitXTarget = () => {
|
|
3618
|
+
const count = data.length;
|
|
3619
|
+
if (count === 0) return { center: 0, span: 60 };
|
|
3508
3620
|
const maxSpan = Math.min(maxVisibleBars, Math.max(minVisibleBars, count + maxPanBars * 2));
|
|
3509
|
-
const
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
}
|
|
3514
|
-
|
|
3515
|
-
|
|
3621
|
+
const span = clamp(Math.floor(mergedOptions.initialVisibleBars), minVisibleBars, maxSpan);
|
|
3622
|
+
return {
|
|
3623
|
+
center: mergedOptions.initialViewport === "center" ? count / 2 : count - span / 2 + rightEdgePaddingBars,
|
|
3624
|
+
span
|
|
3625
|
+
};
|
|
3626
|
+
};
|
|
3627
|
+
const fitXViewport = () => {
|
|
3628
|
+
const target = getFitXTarget();
|
|
3629
|
+
xSpan = target.span;
|
|
3630
|
+
xCenter = target.center;
|
|
3516
3631
|
clampXViewport();
|
|
3517
3632
|
};
|
|
3518
3633
|
const getYBounds = () => {
|
|
@@ -6909,6 +7024,73 @@ function createChart(element, options = {}) {
|
|
|
6909
7024
|
for (const orderLine of orderLines) {
|
|
6910
7025
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
6911
7026
|
}
|
|
7027
|
+
let dataLineBottom = chartTop;
|
|
7028
|
+
const dataLine = mergedOptions.dataLine;
|
|
7029
|
+
if (dataLine?.visible && data.length > 0) {
|
|
7030
|
+
const hoverIndex = crosshairPoint && getHitRegion(crosshairPoint.x, crosshairPoint.y) === "plot" ? indexFromCanvasX(crosshairPoint.x) : null;
|
|
7031
|
+
const barIndex = hoverIndex !== null && hoverIndex >= 0 && hoverIndex < data.length ? hoverIndex : data.length - 1;
|
|
7032
|
+
const bar = data[barIndex];
|
|
7033
|
+
if (bar) {
|
|
7034
|
+
const prevFont = ctx.font;
|
|
7035
|
+
const fontSize = Math.max(9, dataLine.fontSize || axis.fontSize);
|
|
7036
|
+
ctx.font = `${fontSize}px ${mergedOptions.fontFamily}`;
|
|
7037
|
+
ctx.textBaseline = "top";
|
|
7038
|
+
ctx.textAlign = "left";
|
|
7039
|
+
const symbolColor = dataLine.symbolColor || axis.textColor;
|
|
7040
|
+
const textColor = dataLine.textColor || labels.indicatorTextColor;
|
|
7041
|
+
const changeAbs = bar.c - bar.o;
|
|
7042
|
+
const changeColor = changeAbs >= 0 ? mergedOptions.upColor : mergedOptions.downColor;
|
|
7043
|
+
const rowY = chartTop + 6;
|
|
7044
|
+
let cursorX = chartLeft + 10;
|
|
7045
|
+
if (dataLine.statusColor) {
|
|
7046
|
+
const dotRadius = Math.max(2.5, fontSize / 4);
|
|
7047
|
+
ctx.fillStyle = dataLine.statusColor;
|
|
7048
|
+
ctx.beginPath();
|
|
7049
|
+
ctx.arc(cursorX + dotRadius, rowY + fontSize / 2, dotRadius, 0, Math.PI * 2);
|
|
7050
|
+
ctx.fill();
|
|
7051
|
+
cursorX += dotRadius * 2 + 6;
|
|
7052
|
+
}
|
|
7053
|
+
const headline = [dataLine.symbol, dataLine.interval, dataLine.exchange].filter((part) => part && part.length > 0).join(" \xB7 ");
|
|
7054
|
+
if (headline) {
|
|
7055
|
+
ctx.fillStyle = symbolColor;
|
|
7056
|
+
ctx.fillText(headline, cursorX, rowY);
|
|
7057
|
+
cursorX += measureTextWidth(headline) + 10;
|
|
7058
|
+
}
|
|
7059
|
+
if (dataLine.showOhlc !== false) {
|
|
7060
|
+
const ohlc = `O${formatPrice(bar.o)} H${formatPrice(bar.h)} L${formatPrice(bar.l)} C${formatPrice(bar.c)}`;
|
|
7061
|
+
ctx.fillStyle = changeColor;
|
|
7062
|
+
ctx.fillText(ohlc, cursorX, rowY);
|
|
7063
|
+
cursorX += measureTextWidth(ohlc) + 8;
|
|
7064
|
+
}
|
|
7065
|
+
if (dataLine.showChange !== false) {
|
|
7066
|
+
const pct = bar.o !== 0 ? changeAbs / bar.o * 100 : 0;
|
|
7067
|
+
const changeText = `${changeAbs >= 0 ? "+" : ""}${formatPrice(changeAbs)} (${changeAbs >= 0 ? "+" : ""}${pct.toFixed(2)}%)`;
|
|
7068
|
+
ctx.fillStyle = changeColor;
|
|
7069
|
+
ctx.fillText(changeText, cursorX, rowY);
|
|
7070
|
+
cursorX += measureTextWidth(changeText) + 10;
|
|
7071
|
+
}
|
|
7072
|
+
if (dataLine.showVolume && bar.v !== void 0) {
|
|
7073
|
+
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))}`;
|
|
7074
|
+
ctx.fillStyle = textColor;
|
|
7075
|
+
ctx.fillText(volumeText, cursorX, rowY);
|
|
7076
|
+
cursorX += measureTextWidth(volumeText) + 10;
|
|
7077
|
+
}
|
|
7078
|
+
for (const detail of dataLine.details ?? []) {
|
|
7079
|
+
const text = detail.label ? `${detail.label} ${detail.value}` : detail.value;
|
|
7080
|
+
const width2 = measureTextWidth(text);
|
|
7081
|
+
if (cursorX + width2 > chartRight - 4) break;
|
|
7082
|
+
if (detail.background) {
|
|
7083
|
+
ctx.fillStyle = detail.background;
|
|
7084
|
+
fillRoundedRect(cursorX - 4, rowY - 2, width2 + 8, fontSize + 4, 3);
|
|
7085
|
+
}
|
|
7086
|
+
ctx.fillStyle = detail.color || textColor;
|
|
7087
|
+
ctx.fillText(text, cursorX, rowY);
|
|
7088
|
+
cursorX += width2 + 12;
|
|
7089
|
+
}
|
|
7090
|
+
dataLineBottom = rowY + fontSize + 4;
|
|
7091
|
+
ctx.font = prevFont;
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
6912
7094
|
if (labels.visible && (labels.showIndicatorNames || labels.showIndicatorValues)) {
|
|
6913
7095
|
const isLegendInputValue = (value) => {
|
|
6914
7096
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
@@ -6937,7 +7119,7 @@ function createChart(element, options = {}) {
|
|
|
6937
7119
|
const isRight = position === "top-right" || position === "bottom-right";
|
|
6938
7120
|
const isBottom = position === "bottom-left" || position === "bottom-right";
|
|
6939
7121
|
const legendX = isRight ? chartRight - offsetX : chartLeft + offsetX;
|
|
6940
|
-
const legendY = isBottom ? chartBottom - offsetY : chartTop + offsetY;
|
|
7122
|
+
const legendY = isBottom ? chartBottom - offsetY : Math.max(chartTop + offsetY, dataLineBottom);
|
|
6941
7123
|
drawText(legendText, legendX, legendY, isRight ? "right" : "left", isBottom ? "bottom" : "top", labels.indicatorTextColor);
|
|
6942
7124
|
ctx.font = prevFont;
|
|
6943
7125
|
}
|
|
@@ -7478,27 +7660,24 @@ function createChart(element, options = {}) {
|
|
|
7478
7660
|
scheduleDraw();
|
|
7479
7661
|
};
|
|
7480
7662
|
const fitContent = () => {
|
|
7481
|
-
|
|
7482
|
-
updateFollowLatest(true);
|
|
7483
|
-
emitViewportChange();
|
|
7484
|
-
scheduleDraw();
|
|
7663
|
+
const target = getFitXTarget();
|
|
7664
|
+
animateViewportTo(target.center, target.span, () => updateFollowLatest(true));
|
|
7485
7665
|
};
|
|
7486
7666
|
const resetViewport = () => {
|
|
7487
7667
|
cancelKineticPan();
|
|
7488
|
-
|
|
7668
|
+
const target = getFitXTarget();
|
|
7489
7669
|
resetYViewport();
|
|
7490
|
-
updateFollowLatest(true);
|
|
7491
|
-
emitViewportChange();
|
|
7492
|
-
scheduleDraw();
|
|
7670
|
+
animateViewportTo(target.center, target.span, () => updateFollowLatest(true));
|
|
7493
7671
|
};
|
|
7494
7672
|
const isFollowingLatest = () => followLatest;
|
|
7495
7673
|
const setFollowingLatest = (follow) => {
|
|
7496
7674
|
if (follow && data.length > 0) {
|
|
7497
7675
|
cancelKineticPan();
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7676
|
+
animateViewportTo(
|
|
7677
|
+
data.length - xSpan / 2 + rightEdgePaddingBars,
|
|
7678
|
+
xSpan,
|
|
7679
|
+
() => updateFollowLatest(true)
|
|
7680
|
+
);
|
|
7502
7681
|
} else {
|
|
7503
7682
|
updateFollowLatest(follow);
|
|
7504
7683
|
}
|
|
@@ -8781,6 +8960,7 @@ function createChart(element, options = {}) {
|
|
|
8781
8960
|
canvas.focus({ preventScroll: true });
|
|
8782
8961
|
}
|
|
8783
8962
|
cancelKineticPan();
|
|
8963
|
+
cancelViewportTween();
|
|
8784
8964
|
panVelocitySamples.length = 0;
|
|
8785
8965
|
magnetModifierActive = event.metaKey || event.ctrlKey;
|
|
8786
8966
|
shiftKeyActive = event.shiftKey;
|
|
@@ -9445,6 +9625,7 @@ function createChart(element, options = {}) {
|
|
|
9445
9625
|
return;
|
|
9446
9626
|
}
|
|
9447
9627
|
cancelKineticPan();
|
|
9628
|
+
cancelViewportTween();
|
|
9448
9629
|
const point = getCanvasPoint(event);
|
|
9449
9630
|
const region = getHitRegion(point.x, point.y);
|
|
9450
9631
|
if (region === "outside") {
|
|
@@ -9722,6 +9903,7 @@ function createChart(element, options = {}) {
|
|
|
9722
9903
|
resetRightMarginCache();
|
|
9723
9904
|
doubleClickEnabled = mergedOptions.doubleClickEnabled;
|
|
9724
9905
|
doubleClickAction = mergedOptions.doubleClickAction;
|
|
9906
|
+
setIndicatorLiveThrottleMs(mergedOptions.indicatorUpdate?.liveThrottleMs ?? 80);
|
|
9725
9907
|
applyAccessibilityAttributes();
|
|
9726
9908
|
const isTickerSmoothingEnabled = mergedOptions.tickerLine?.smoothing ?? false;
|
|
9727
9909
|
if (!isTickerSmoothingEnabled) {
|
|
@@ -10200,6 +10382,7 @@ function createChart(element, options = {}) {
|
|
|
10200
10382
|
return canvas.toDataURL(options2.type ?? "image/png", options2.quality);
|
|
10201
10383
|
};
|
|
10202
10384
|
const destroy = () => {
|
|
10385
|
+
cancelViewportTween();
|
|
10203
10386
|
cancelLongPressTimer();
|
|
10204
10387
|
datafeedUnsubscribe?.();
|
|
10205
10388
|
datafeedUnsubscribe = null;
|
|
@@ -83,6 +83,9 @@ interface ChartOptions {
|
|
|
83
83
|
downsampling?: DownsamplingOptions;
|
|
84
84
|
touch?: TouchOptions;
|
|
85
85
|
datafeedOptions?: DatafeedOptions;
|
|
86
|
+
dataLine?: DataLineOptions;
|
|
87
|
+
animation?: AnimationOptions;
|
|
88
|
+
indicatorUpdate?: IndicatorUpdateOptions;
|
|
86
89
|
crosshair?: CrosshairOptions;
|
|
87
90
|
grid?: GridOptions;
|
|
88
91
|
watermark?: WatermarkOptions;
|
|
@@ -635,7 +638,68 @@ interface ChartTheme {
|
|
|
635
638
|
/** Background for preview labels and pills. */
|
|
636
639
|
labelBackground: string;
|
|
637
640
|
}
|
|
638
|
-
type ChartThemeName = "
|
|
641
|
+
type ChartThemeName = "midnight" | "light" | "brand" | "dark" | "high-contrast";
|
|
642
|
+
/**
|
|
643
|
+
* The symbol/OHLC line drawn inside the top-left of the plot, like
|
|
644
|
+
* TradingView's data line. Values follow the crosshair while hovering and fall
|
|
645
|
+
* back to the latest bar otherwise. Opt-in, because hosts that already render
|
|
646
|
+
* their own DOM overlay shouldn't suddenly get two.
|
|
647
|
+
*/
|
|
648
|
+
interface DataLineOptions {
|
|
649
|
+
/** Default false. */
|
|
650
|
+
visible?: boolean;
|
|
651
|
+
/** e.g. "NQU6" — drawn first, in the emphasis color. */
|
|
652
|
+
symbol?: string;
|
|
653
|
+
/** e.g. "1h". */
|
|
654
|
+
interval?: string;
|
|
655
|
+
/** e.g. "CME". */
|
|
656
|
+
exchange?: string;
|
|
657
|
+
/** O/H/L/C values for the hovered (or latest) bar. Default true. */
|
|
658
|
+
showOhlc?: boolean;
|
|
659
|
+
/** Absolute and percent change of that bar, colored by direction. Default true. */
|
|
660
|
+
showChange?: boolean;
|
|
661
|
+
/** Bar volume. Default false. */
|
|
662
|
+
showVolume?: boolean;
|
|
663
|
+
/**
|
|
664
|
+
* Extra chips after the values — session, currency, a "Closed" badge. Each
|
|
665
|
+
* renders as `label value` with an optional color and pill background.
|
|
666
|
+
*/
|
|
667
|
+
details?: Array<{
|
|
668
|
+
label?: string;
|
|
669
|
+
value: string;
|
|
670
|
+
color?: string;
|
|
671
|
+
background?: string;
|
|
672
|
+
}>;
|
|
673
|
+
/** Colored dot before the symbol, e.g. green when the market is open. */
|
|
674
|
+
statusColor?: string;
|
|
675
|
+
fontSize?: number;
|
|
676
|
+
/** Symbol color. Defaults to the axis text color. */
|
|
677
|
+
symbolColor?: string;
|
|
678
|
+
/** Everything after the symbol. Defaults to the indicator legend color. */
|
|
679
|
+
textColor?: string;
|
|
680
|
+
}
|
|
681
|
+
interface IndicatorUpdateOptions {
|
|
682
|
+
/**
|
|
683
|
+
* While only the forming bar is ticking, recompute indicator series at most
|
|
684
|
+
* this often (ms) rather than on every frame. Values are always exact — this
|
|
685
|
+
* changes how often the live bar's output refreshes, not how it's computed,
|
|
686
|
+
* and anything structural (a bar closing, history loading, inputs changing)
|
|
687
|
+
* recomputes immediately. Default 80; set 0 to recompute every frame.
|
|
688
|
+
*/
|
|
689
|
+
liveThrottleMs?: number;
|
|
690
|
+
}
|
|
691
|
+
interface AnimationOptions {
|
|
692
|
+
/**
|
|
693
|
+
* Ease the big viewport jumps — reset, fit-all, scroll-to-realtime — instead
|
|
694
|
+
* of teleporting, so it stays obvious which way the chart moved. Continuous
|
|
695
|
+
* gestures (drag, wheel, pinch) are never animated; they already follow the
|
|
696
|
+
* hand. Default true, and always skipped when the OS asks for reduced
|
|
697
|
+
* motion.
|
|
698
|
+
*/
|
|
699
|
+
viewportTransitions?: boolean;
|
|
700
|
+
/** Transition length in ms. Default 260. */
|
|
701
|
+
durationMs?: number;
|
|
702
|
+
}
|
|
639
703
|
/** Touch ergonomics. Fingers are blunter than a mouse cursor. */
|
|
640
704
|
interface TouchOptions {
|
|
641
705
|
/**
|
|
@@ -1066,16 +1130,16 @@ declare const compileScriptIndicator: (definition: ScriptIndicatorDefinition) =>
|
|
|
1066
1130
|
/**
|
|
1067
1131
|
* Built-in palettes.
|
|
1068
1132
|
*
|
|
1069
|
-
* `
|
|
1070
|
-
*
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1133
|
+
* `midnight` is the long-standing default and `light` is its counterpart on a
|
|
1134
|
+
* white surface — same candle colors, so switching surfaces doesn't change how
|
|
1135
|
+
* you read the tape. `brand` tints everything toward Hyperprop blue, `dark` is
|
|
1136
|
+
* a neutral near-black for embedders who want no tint at all, and
|
|
1137
|
+
* `high-contrast` trades subtlety for legibility on projectors and for
|
|
1138
|
+
* low-vision users.
|
|
1075
1139
|
*/
|
|
1076
1140
|
declare const CHART_THEMES: Record<ChartThemeName, ChartTheme>;
|
|
1077
1141
|
declare const CHART_THEME_NAMES: ChartThemeName[];
|
|
1078
1142
|
|
|
1079
1143
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
1080
1144
|
|
|
1081
|
-
export { type AccessibilityOptions, type AxisOptions, type BuiltInIndicatorInfo, CHART_THEMES, CHART_THEME_NAMES, type ChartClickEvent, type ChartContextMenuEvent, type ChartContextMenuRegion, type ChartDatafeed, type ChartInstance, type ChartKeyboardAction, type ChartKeyboardShortcutEvent, type ChartLongPressEvent, type ChartOptions, type ChartSavedState, type ChartTheme, type ChartThemeName, type ChartType, type CompareSeriesOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DatafeedBarsRequest, type DatafeedOptions, type DownsamplingOptions, type DrawingBounds, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingSelectionChangeEvent, type DrawingToolType, FIB_DEFAULT_PALETTE, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneActionEvent, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneHeightChangeEvent, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type KeyboardOptions, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, POSITION_DEFAULT_COLORS, type PriceLineOptions, type PriceScaleMode, type PriceScaleOptions, SCRIPT_SOURCE_INPUT_VALUES, type ScriptComputeResult, type ScriptIndicatorDefinition, type ScriptIndicatorInputDef, type ScriptInputHelper, type ScriptInputOptions, type ScriptPlotSpec, type TickerLineOptions, type TouchOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, compileScriptIndicator, createChart, extractScriptInputs, validateScriptSource };
|
|
1145
|
+
export { type AccessibilityOptions, type AnimationOptions, type AxisOptions, type BuiltInIndicatorInfo, CHART_THEMES, CHART_THEME_NAMES, type ChartClickEvent, type ChartContextMenuEvent, type ChartContextMenuRegion, type ChartDatafeed, type ChartInstance, type ChartKeyboardAction, type ChartKeyboardShortcutEvent, type ChartLongPressEvent, type ChartOptions, type ChartSavedState, type ChartTheme, type ChartThemeName, type ChartType, type CompareSeriesOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type DataLineOptions, type DatafeedBarsRequest, type DatafeedOptions, type DownsamplingOptions, type DrawingBounds, type DrawingDefaults, type DrawingHoverEvent, type DrawingObjectOptions, type DrawingPoint, type DrawingSelectEvent, type DrawingSelectionChangeEvent, type DrawingToolType, FIB_DEFAULT_PALETTE, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPaneActionEvent, type IndicatorPaneAxisOptions, type IndicatorPaneGuideLine, type IndicatorPaneHeightChangeEvent, type IndicatorPaneRenderInfo, type IndicatorPaneValue, type IndicatorPaneValueLabel, type IndicatorPlugin, type IndicatorRenderContext, type IndicatorUpdateOptions, type KeyboardOptions, type LabelsOptions, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, POSITION_DEFAULT_COLORS, type PriceLineOptions, type PriceScaleMode, type PriceScaleOptions, SCRIPT_SOURCE_INPUT_VALUES, type ScriptComputeResult, type ScriptIndicatorDefinition, type ScriptIndicatorInputDef, type ScriptInputHelper, type ScriptInputOptions, type ScriptPlotSpec, type TickerLineOptions, type TouchOptions, type TradeMarkerOptions, type ViewportState, type WatermarkOptions, compileScriptIndicator, createChart, extractScriptInputs, validateScriptSource };
|