@tranding-custom-chart/renderer-canvas 0.0.0-stage → 2.0.0
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/README.md +40 -2
- package/dist/canvas-engine.d.ts +82 -0
- package/dist/canvas-engine.d.ts.map +1 -0
- package/dist/canvas-engine.js +594 -0
- package/dist/dpi.d.ts +31 -0
- package/dist/dpi.d.ts.map +1 -0
- package/dist/dpi.js +65 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/renderers/bar-renderer.d.ts +26 -0
- package/dist/renderers/bar-renderer.d.ts.map +1 -0
- package/dist/renderers/bar-renderer.js +45 -0
- package/dist/renderers/baseline-renderer.d.ts +20 -0
- package/dist/renderers/baseline-renderer.d.ts.map +1 -0
- package/dist/renderers/baseline-renderer.js +101 -0
- package/dist/renderers/box-plot-renderer.d.ts +14 -0
- package/dist/renderers/box-plot-renderer.d.ts.map +1 -0
- package/dist/renderers/box-plot-renderer.js +76 -0
- package/dist/renderers/bubble-renderer.d.ts +14 -0
- package/dist/renderers/bubble-renderer.d.ts.map +1 -0
- package/dist/renderers/bubble-renderer.js +37 -0
- package/dist/renderers/candlestick-renderer.d.ts +18 -0
- package/dist/renderers/candlestick-renderer.d.ts.map +1 -0
- package/dist/renderers/candlestick-renderer.js +87 -0
- package/dist/renderers/columns-renderer.d.ts +21 -0
- package/dist/renderers/columns-renderer.d.ts.map +1 -0
- package/dist/renderers/columns-renderer.js +35 -0
- package/dist/renderers/crosshair-renderer.d.ts +19 -0
- package/dist/renderers/crosshair-renderer.d.ts.map +1 -0
- package/dist/renderers/crosshair-renderer.js +71 -0
- package/dist/renderers/curved-line-renderer.d.ts +17 -0
- package/dist/renderers/curved-line-renderer.d.ts.map +1 -0
- package/dist/renderers/curved-line-renderer.js +46 -0
- package/dist/renderers/drawing-renderer.d.ts +19 -0
- package/dist/renderers/drawing-renderer.d.ts.map +1 -0
- package/dist/renderers/drawing-renderer.js +487 -0
- package/dist/renderers/grid-renderer.d.ts +26 -0
- package/dist/renderers/grid-renderer.d.ts.map +1 -0
- package/dist/renderers/grid-renderer.js +103 -0
- package/dist/renderers/heatmap-renderer.d.ts +16 -0
- package/dist/renderers/heatmap-renderer.d.ts.map +1 -0
- package/dist/renderers/heatmap-renderer.js +80 -0
- package/dist/renderers/high-low-renderer.d.ts +19 -0
- package/dist/renderers/high-low-renderer.d.ts.map +1 -0
- package/dist/renderers/high-low-renderer.js +28 -0
- package/dist/renderers/hlc-area-renderer.d.ts +22 -0
- package/dist/renderers/hlc-area-renderer.d.ts.map +1 -0
- package/dist/renderers/hlc-area-renderer.js +72 -0
- package/dist/renderers/hollow-candlestick-renderer.d.ts +26 -0
- package/dist/renderers/hollow-candlestick-renderer.d.ts.map +1 -0
- package/dist/renderers/hollow-candlestick-renderer.js +71 -0
- package/dist/renderers/horizontal-bar-renderer.d.ts +25 -0
- package/dist/renderers/horizontal-bar-renderer.d.ts.map +1 -0
- package/dist/renderers/horizontal-bar-renderer.js +52 -0
- package/dist/renderers/last-price-renderer.d.ts +17 -0
- package/dist/renderers/last-price-renderer.d.ts.map +1 -0
- package/dist/renderers/last-price-renderer.js +46 -0
- package/dist/renderers/line-renderer.d.ts +23 -0
- package/dist/renderers/line-renderer.d.ts.map +1 -0
- package/dist/renderers/line-renderer.js +80 -0
- package/dist/renderers/line-with-markers-renderer.d.ts +13 -0
- package/dist/renderers/line-with-markers-renderer.d.ts.map +1 -0
- package/dist/renderers/line-with-markers-renderer.js +35 -0
- package/dist/renderers/marker-renderer.d.ts +16 -0
- package/dist/renderers/marker-renderer.d.ts.map +1 -0
- package/dist/renderers/marker-renderer.js +91 -0
- package/dist/renderers/mixed-renderer.d.ts +18 -0
- package/dist/renderers/mixed-renderer.d.ts.map +1 -0
- package/dist/renderers/mixed-renderer.js +123 -0
- package/dist/renderers/ohlc-bar-renderer.d.ts +21 -0
- package/dist/renderers/ohlc-bar-renderer.d.ts.map +1 -0
- package/dist/renderers/ohlc-bar-renderer.js +73 -0
- package/dist/renderers/pie-renderer.d.ts +26 -0
- package/dist/renderers/pie-renderer.d.ts.map +1 -0
- package/dist/renderers/pie-renderer.js +71 -0
- package/dist/renderers/polar-area-renderer.d.ts +13 -0
- package/dist/renderers/polar-area-renderer.d.ts.map +1 -0
- package/dist/renderers/polar-area-renderer.js +44 -0
- package/dist/renderers/radar-renderer.d.ts +16 -0
- package/dist/renderers/radar-renderer.d.ts.map +1 -0
- package/dist/renderers/radar-renderer.js +99 -0
- package/dist/renderers/scatter-renderer.d.ts +14 -0
- package/dist/renderers/scatter-renderer.d.ts.map +1 -0
- package/dist/renderers/scatter-renderer.js +42 -0
- package/dist/renderers/stacked-bar-renderer.d.ts +14 -0
- package/dist/renderers/stacked-bar-renderer.d.ts.map +1 -0
- package/dist/renderers/stacked-bar-renderer.js +52 -0
- package/dist/renderers/step-line-renderer.d.ts +19 -0
- package/dist/renderers/step-line-renderer.d.ts.map +1 -0
- package/dist/renderers/step-line-renderer.js +52 -0
- package/dist/renderers/supertrend-renderer.d.ts +18 -0
- package/dist/renderers/supertrend-renderer.d.ts.map +1 -0
- package/dist/renderers/supertrend-renderer.js +80 -0
- package/dist/renderers/volume-profile-renderer.d.ts +25 -0
- package/dist/renderers/volume-profile-renderer.d.ts.map +1 -0
- package/dist/renderers/volume-profile-renderer.js +83 -0
- package/dist/renderers/watermark-renderer.d.ts +15 -0
- package/dist/renderers/watermark-renderer.d.ts.map +1 -0
- package/dist/renderers/watermark-renderer.js +26 -0
- package/dist/scheduler.d.ts +25 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +77 -0
- package/package.json +33 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/grid-renderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAGpG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;IAChE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;IAChE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IAC/D,mDAAmD;IACnD,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CAC1E;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CA+HlE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/grid-renderer.ts
|
|
3
|
+
* Crisp gridlines, axes, and tick text labels.
|
|
4
|
+
*/
|
|
5
|
+
import { snapLine } from '../dpi.js';
|
|
6
|
+
export function renderGridAndAxes(options) {
|
|
7
|
+
const { ctx, bounds, padding, xScale, yScale, theme, dpr, xTickFormatter = (v) => v.toString(), yTickFormatter = (v) => v.toFixed(2), showXGrid = true, showYGrid = true, gridStyle = 'dashed', yAxisPosition = 'right', secondaryYScale, secondaryYTickFormatter = (v) => `${v.toFixed(1)}%`, } = options;
|
|
8
|
+
const left = padding.left;
|
|
9
|
+
const right = bounds.width - padding.right;
|
|
10
|
+
const top = padding.top;
|
|
11
|
+
const bottom = bounds.height - padding.bottom;
|
|
12
|
+
const getLineDash = (style) => {
|
|
13
|
+
switch (style) {
|
|
14
|
+
case 'solid':
|
|
15
|
+
return [];
|
|
16
|
+
case 'dotted':
|
|
17
|
+
return [1, 3];
|
|
18
|
+
case 'dashed':
|
|
19
|
+
default:
|
|
20
|
+
return [3, 3];
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const lineDashPattern = getLineDash(gridStyle);
|
|
24
|
+
ctx.save();
|
|
25
|
+
// Primary Horizontal Grid Lines & Y-Axis Labels
|
|
26
|
+
const yTicks = yScale.ticks(6);
|
|
27
|
+
ctx.strokeStyle = theme.gridLine;
|
|
28
|
+
ctx.lineWidth = 1;
|
|
29
|
+
ctx.font = '10px monospace';
|
|
30
|
+
ctx.fillStyle = theme.textSecondary;
|
|
31
|
+
ctx.textBaseline = 'middle';
|
|
32
|
+
for (const tickVal of yTicks) {
|
|
33
|
+
const rawY = yScale.scale(tickVal);
|
|
34
|
+
if (rawY < top || rawY > bottom)
|
|
35
|
+
continue;
|
|
36
|
+
const y = snapLine(rawY, dpr);
|
|
37
|
+
if (showYGrid) {
|
|
38
|
+
ctx.beginPath();
|
|
39
|
+
ctx.setLineDash(lineDashPattern);
|
|
40
|
+
ctx.moveTo(left, y);
|
|
41
|
+
ctx.lineTo(right, y);
|
|
42
|
+
ctx.stroke();
|
|
43
|
+
}
|
|
44
|
+
const label = yTickFormatter(tickVal);
|
|
45
|
+
ctx.setLineDash([]);
|
|
46
|
+
if (yAxisPosition === 'left' || yAxisPosition === 'both') {
|
|
47
|
+
ctx.textAlign = 'right';
|
|
48
|
+
ctx.fillText(label, left - 6, y);
|
|
49
|
+
}
|
|
50
|
+
if (yAxisPosition === 'right' || (yAxisPosition === 'both' && !secondaryYScale)) {
|
|
51
|
+
ctx.textAlign = 'left';
|
|
52
|
+
ctx.fillText(label, right + 6, y);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Secondary Y-Axis Labels (for Dual-Axis / Mixed Charts)
|
|
56
|
+
if (secondaryYScale && (yAxisPosition === 'both' || yAxisPosition === 'right')) {
|
|
57
|
+
const secTicks = secondaryYScale.ticks(6);
|
|
58
|
+
ctx.font = '10px monospace';
|
|
59
|
+
ctx.fillStyle = '#f59e0b'; // Distinct secondary indicator color
|
|
60
|
+
ctx.textAlign = 'left';
|
|
61
|
+
for (const tickVal of secTicks) {
|
|
62
|
+
const rawY = secondaryYScale.scale(tickVal);
|
|
63
|
+
if (rawY < top || rawY > bottom)
|
|
64
|
+
continue;
|
|
65
|
+
const y = snapLine(rawY, dpr);
|
|
66
|
+
const label = secondaryYTickFormatter(tickVal);
|
|
67
|
+
ctx.fillText(label, right + 6, y);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Vertical Grid Lines & X-Axis Labels
|
|
71
|
+
const xTicks = xScale.ticks(6);
|
|
72
|
+
ctx.textAlign = 'center';
|
|
73
|
+
ctx.textBaseline = 'top';
|
|
74
|
+
for (const tickVal of xTicks) {
|
|
75
|
+
const rawX = xScale.scale(tickVal);
|
|
76
|
+
if (rawX < left || rawX > right)
|
|
77
|
+
continue;
|
|
78
|
+
const x = snapLine(rawX, dpr);
|
|
79
|
+
if (showXGrid) {
|
|
80
|
+
ctx.beginPath();
|
|
81
|
+
ctx.setLineDash(lineDashPattern);
|
|
82
|
+
ctx.moveTo(x, top);
|
|
83
|
+
ctx.lineTo(x, bottom);
|
|
84
|
+
ctx.stroke();
|
|
85
|
+
}
|
|
86
|
+
// X Axis Tick Label below the bottom axis
|
|
87
|
+
const label = xTickFormatter(tickVal);
|
|
88
|
+
ctx.setLineDash([]);
|
|
89
|
+
ctx.fillText(label, x, bottom + 6);
|
|
90
|
+
}
|
|
91
|
+
// Outer Plot Area Boundary
|
|
92
|
+
ctx.strokeStyle = theme.axisLine;
|
|
93
|
+
ctx.setLineDash([]);
|
|
94
|
+
ctx.beginPath();
|
|
95
|
+
// Bottom axis line
|
|
96
|
+
ctx.moveTo(left, snapLine(bottom, dpr));
|
|
97
|
+
ctx.lineTo(right, snapLine(bottom, dpr));
|
|
98
|
+
// Right axis line
|
|
99
|
+
ctx.moveTo(snapLine(right, dpr), top);
|
|
100
|
+
ctx.lineTo(snapLine(right, dpr), bottom);
|
|
101
|
+
ctx.stroke();
|
|
102
|
+
ctx.restore();
|
|
103
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/heatmap-renderer.ts
|
|
3
|
+
* High-performance 2D Matrix Heatmap renderer for correlation matrices, activity grids, and telemetry.
|
|
4
|
+
*/
|
|
5
|
+
import type { Dimension, HeatmapDatum, Padding } from '@tranding-custom-chart/core';
|
|
6
|
+
export interface HeatmapRenderOptions {
|
|
7
|
+
readonly cellGap?: number | undefined;
|
|
8
|
+
readonly cellRadius?: number | undefined;
|
|
9
|
+
readonly colorLow?: string | undefined;
|
|
10
|
+
readonly colorHigh?: string | undefined;
|
|
11
|
+
readonly showLabels?: boolean | undefined;
|
|
12
|
+
readonly labelColor?: string | undefined;
|
|
13
|
+
readonly fontSize?: number | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare function renderHeatmap(ctx: CanvasRenderingContext2D, data: readonly HeatmapDatum[], bounds: Dimension, padding: Padding, options?: HeatmapRenderOptions): void;
|
|
16
|
+
//# sourceMappingURL=heatmap-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heatmap-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/heatmap-renderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAEpF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAqBD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,SAAS,YAAY,EAAE,EAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,oBAAyB,GACjC,IAAI,CAyEN"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/heatmap-renderer.ts
|
|
3
|
+
* High-performance 2D Matrix Heatmap renderer for correlation matrices, activity grids, and telemetry.
|
|
4
|
+
*/
|
|
5
|
+
function parseRgba(colorStr) {
|
|
6
|
+
if (colorStr.startsWith('#')) {
|
|
7
|
+
let hex = colorStr.slice(1);
|
|
8
|
+
if (hex.length === 3)
|
|
9
|
+
hex = hex.split('').map((c) => c + c).join('');
|
|
10
|
+
const num = parseInt(hex, 16);
|
|
11
|
+
return [(num >> 16) & 255, (num >> 8) & 255, num & 255, 1];
|
|
12
|
+
}
|
|
13
|
+
const match = colorStr.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
14
|
+
if (match) {
|
|
15
|
+
return [
|
|
16
|
+
parseInt(match[1], 10),
|
|
17
|
+
parseInt(match[2], 10),
|
|
18
|
+
parseInt(match[3], 10),
|
|
19
|
+
match[4] !== undefined ? parseFloat(match[4]) : 1,
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
return [59, 130, 246, 1];
|
|
23
|
+
}
|
|
24
|
+
export function renderHeatmap(ctx, data, bounds, padding, options = {}) {
|
|
25
|
+
if (data.length === 0)
|
|
26
|
+
return;
|
|
27
|
+
const { cellGap = 2, cellRadius = 2, colorLow = 'rgba(30, 41, 59, 0.6)', colorHigh = 'rgba(16, 185, 129, 0.9)', showLabels = false, labelColor = '#ffffff', fontSize = 10, } = options;
|
|
28
|
+
// Extract unique X categories (columns) and Y categories (rows)
|
|
29
|
+
const cols = Array.from(new Set(data.map((d) => String(d.x))));
|
|
30
|
+
const rows = Array.from(new Set(data.map((d) => String(d.y))));
|
|
31
|
+
if (cols.length === 0 || rows.length === 0)
|
|
32
|
+
return;
|
|
33
|
+
let minVal = Infinity;
|
|
34
|
+
let maxVal = -Infinity;
|
|
35
|
+
for (const d of data) {
|
|
36
|
+
if (d.value < minVal)
|
|
37
|
+
minVal = d.value;
|
|
38
|
+
if (d.value > maxVal)
|
|
39
|
+
maxVal = d.value;
|
|
40
|
+
}
|
|
41
|
+
const valRange = maxVal > minVal ? maxVal - minVal : 1;
|
|
42
|
+
const plotWidth = bounds.width - padding.left - padding.right;
|
|
43
|
+
const plotHeight = bounds.height - padding.top - padding.bottom;
|
|
44
|
+
const colWidth = (plotWidth - (cols.length - 1) * cellGap) / cols.length;
|
|
45
|
+
const rowHeight = (plotHeight - (rows.length - 1) * cellGap) / rows.length;
|
|
46
|
+
const [r1, g1, b1, a1] = parseRgba(colorLow);
|
|
47
|
+
const [r2, g2, b2, a2] = parseRgba(colorHigh);
|
|
48
|
+
ctx.save();
|
|
49
|
+
ctx.font = `${fontSize}px system-ui, sans-serif`;
|
|
50
|
+
ctx.textAlign = 'center';
|
|
51
|
+
ctx.textBaseline = 'middle';
|
|
52
|
+
for (const datum of data) {
|
|
53
|
+
const colIdx = cols.indexOf(String(datum.x));
|
|
54
|
+
const rowIdx = rows.indexOf(String(datum.y));
|
|
55
|
+
if (colIdx === -1 || rowIdx === -1)
|
|
56
|
+
continue;
|
|
57
|
+
const x = padding.left + colIdx * (colWidth + cellGap);
|
|
58
|
+
const y = padding.top + rowIdx * (rowHeight + cellGap);
|
|
59
|
+
const t = Math.max(0, Math.min(1, (datum.value - minVal) / valRange));
|
|
60
|
+
const r = Math.round(r1 + t * (r2 - r1));
|
|
61
|
+
const g = Math.round(g1 + t * (g2 - g1));
|
|
62
|
+
const b = Math.round(b1 + t * (b2 - b1));
|
|
63
|
+
const a = a1 + t * (a2 - a1);
|
|
64
|
+
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
65
|
+
if (cellRadius > 0 && typeof ctx.roundRect === 'function') {
|
|
66
|
+
ctx.beginPath();
|
|
67
|
+
ctx.roundRect(x, y, colWidth, rowHeight, cellRadius);
|
|
68
|
+
ctx.fill();
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
ctx.fillRect(x, y, colWidth, rowHeight);
|
|
72
|
+
}
|
|
73
|
+
if (showLabels && colWidth > 20 && rowHeight > 14) {
|
|
74
|
+
ctx.fillStyle = labelColor;
|
|
75
|
+
const text = datum.label ?? (Number.isInteger(datum.value) ? String(datum.value) : datum.value.toFixed(1));
|
|
76
|
+
ctx.fillText(text, x + colWidth / 2, y + rowHeight / 2);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
ctx.restore();
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/high-low-renderer.ts
|
|
3
|
+
* High-Low range bars renderer showing volatility and trading spread without open/close ticks.
|
|
4
|
+
*/
|
|
5
|
+
import type { ChartTheme, OHLCVBar, Scale } from '@tranding-custom-chart/core';
|
|
6
|
+
export interface HighLowRenderOptions {
|
|
7
|
+
readonly ctx: CanvasRenderingContext2D;
|
|
8
|
+
readonly bars: readonly OHLCVBar[];
|
|
9
|
+
readonly xScale: Scale<number>;
|
|
10
|
+
readonly yScale: Scale<number>;
|
|
11
|
+
readonly theme: ChartTheme;
|
|
12
|
+
readonly dpr: number;
|
|
13
|
+
readonly startIndex?: number | undefined;
|
|
14
|
+
readonly endIndex?: number | undefined;
|
|
15
|
+
readonly customColor?: string | undefined;
|
|
16
|
+
readonly lineWidth?: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
export declare function renderHighLowBars(options: HighLowRenderOptions): void;
|
|
19
|
+
//# sourceMappingURL=high-low-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"high-low-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/high-low-renderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAG/E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAsCrE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/high-low-renderer.ts
|
|
3
|
+
* High-Low range bars renderer showing volatility and trading spread without open/close ticks.
|
|
4
|
+
*/
|
|
5
|
+
import { snapLine } from '../dpi.js';
|
|
6
|
+
export function renderHighLowBars(options) {
|
|
7
|
+
const { ctx, bars, xScale, yScale, theme, dpr, startIndex = 0, endIndex = bars.length - 1, customColor, lineWidth = 2, } = options;
|
|
8
|
+
if (bars.length === 0 || startIndex > endIndex)
|
|
9
|
+
return;
|
|
10
|
+
const start = Math.max(0, startIndex);
|
|
11
|
+
const end = Math.min(bars.length - 1, endIndex);
|
|
12
|
+
const color = customColor ?? theme.accent;
|
|
13
|
+
const slotWidth = bars.length > 1 ? Math.abs(xScale.scale(1) - xScale.scale(0)) : 10;
|
|
14
|
+
ctx.save();
|
|
15
|
+
ctx.strokeStyle = color;
|
|
16
|
+
ctx.lineWidth = slotWidth < 2 ? 0.75 : lineWidth;
|
|
17
|
+
ctx.beginPath();
|
|
18
|
+
for (let i = start; i <= end; i++) {
|
|
19
|
+
const b = bars[i];
|
|
20
|
+
const x = snapLine(xScale.scale(i), dpr);
|
|
21
|
+
const yHigh = snapLine(yScale.scale(b.high), dpr);
|
|
22
|
+
const yLow = snapLine(yScale.scale(b.low), dpr);
|
|
23
|
+
ctx.moveTo(x, yHigh);
|
|
24
|
+
ctx.lineTo(x, yLow);
|
|
25
|
+
}
|
|
26
|
+
ctx.stroke();
|
|
27
|
+
ctx.restore();
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/hlc-area-renderer.ts
|
|
3
|
+
* Canvas 2D renderer for HLC Area charts.
|
|
4
|
+
* Fills the shaded vertical band between High and Low prices with a crisp Close line overlay.
|
|
5
|
+
*/
|
|
6
|
+
import type { ChartTheme, OHLCVBar, Scale } from '@tranding-custom-chart/core';
|
|
7
|
+
export interface HlcAreaRenderOptions {
|
|
8
|
+
readonly ctx: CanvasRenderingContext2D;
|
|
9
|
+
readonly bars: readonly OHLCVBar[];
|
|
10
|
+
readonly xScale: Scale<number>;
|
|
11
|
+
readonly yScale: Scale<number>;
|
|
12
|
+
readonly theme?: ChartTheme | undefined;
|
|
13
|
+
readonly bandFillColor?: string | undefined;
|
|
14
|
+
readonly closeLineColor?: string | undefined;
|
|
15
|
+
readonly closeLineWidth?: number | undefined;
|
|
16
|
+
readonly highLineColor?: string | undefined;
|
|
17
|
+
readonly lowLineColor?: string | undefined;
|
|
18
|
+
readonly startIndex?: number | undefined;
|
|
19
|
+
readonly endIndex?: number | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare function renderHlcArea(options: HlcAreaRenderOptions): void;
|
|
22
|
+
//# sourceMappingURL=hlc-area-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hlc-area-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/hlc-area-renderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAE/E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CA6EjE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/hlc-area-renderer.ts
|
|
3
|
+
* Canvas 2D renderer for HLC Area charts.
|
|
4
|
+
* Fills the shaded vertical band between High and Low prices with a crisp Close line overlay.
|
|
5
|
+
*/
|
|
6
|
+
export function renderHlcArea(options) {
|
|
7
|
+
const { ctx, bars, xScale, yScale, bandFillColor = 'rgba(37, 99, 235, 0.18)', closeLineColor = '#2563eb', closeLineWidth = 2, highLineColor = 'rgba(37, 99, 235, 0.4)', lowLineColor = 'rgba(37, 99, 235, 0.4)', startIndex = 0, endIndex = bars.length - 1, } = options;
|
|
8
|
+
if (bars.length < 2 || startIndex > endIndex)
|
|
9
|
+
return;
|
|
10
|
+
const start = Math.max(0, startIndex);
|
|
11
|
+
const end = Math.min(bars.length - 1, endIndex);
|
|
12
|
+
ctx.save();
|
|
13
|
+
// 1. Draw High-Low Shaded Band
|
|
14
|
+
ctx.beginPath();
|
|
15
|
+
// Trace high forward
|
|
16
|
+
for (let i = start; i <= end; i++) {
|
|
17
|
+
const x = xScale.scale(i);
|
|
18
|
+
const yHigh = yScale.scale(bars[i].high);
|
|
19
|
+
if (i === start)
|
|
20
|
+
ctx.moveTo(x, yHigh);
|
|
21
|
+
else
|
|
22
|
+
ctx.lineTo(x, yHigh);
|
|
23
|
+
}
|
|
24
|
+
// Trace low backward
|
|
25
|
+
for (let i = end; i >= start; i--) {
|
|
26
|
+
const x = xScale.scale(i);
|
|
27
|
+
const yLow = yScale.scale(bars[i].low);
|
|
28
|
+
ctx.lineTo(x, yLow);
|
|
29
|
+
}
|
|
30
|
+
ctx.closePath();
|
|
31
|
+
ctx.fillStyle = bandFillColor;
|
|
32
|
+
ctx.fill();
|
|
33
|
+
// 2. High & Low Boundary Lines (subtle)
|
|
34
|
+
ctx.beginPath();
|
|
35
|
+
for (let i = start; i <= end; i++) {
|
|
36
|
+
const x = xScale.scale(i);
|
|
37
|
+
const y = yScale.scale(bars[i].high);
|
|
38
|
+
if (i === start)
|
|
39
|
+
ctx.moveTo(x, y);
|
|
40
|
+
else
|
|
41
|
+
ctx.lineTo(x, y);
|
|
42
|
+
}
|
|
43
|
+
ctx.strokeStyle = highLineColor;
|
|
44
|
+
ctx.lineWidth = 1;
|
|
45
|
+
ctx.stroke();
|
|
46
|
+
ctx.beginPath();
|
|
47
|
+
for (let i = start; i <= end; i++) {
|
|
48
|
+
const x = xScale.scale(i);
|
|
49
|
+
const y = yScale.scale(bars[i].low);
|
|
50
|
+
if (i === start)
|
|
51
|
+
ctx.moveTo(x, y);
|
|
52
|
+
else
|
|
53
|
+
ctx.lineTo(x, y);
|
|
54
|
+
}
|
|
55
|
+
ctx.strokeStyle = lowLineColor;
|
|
56
|
+
ctx.lineWidth = 1;
|
|
57
|
+
ctx.stroke();
|
|
58
|
+
// 3. Close Price Line
|
|
59
|
+
ctx.beginPath();
|
|
60
|
+
for (let i = start; i <= end; i++) {
|
|
61
|
+
const x = xScale.scale(i);
|
|
62
|
+
const y = yScale.scale(bars[i].close);
|
|
63
|
+
if (i === start)
|
|
64
|
+
ctx.moveTo(x, y);
|
|
65
|
+
else
|
|
66
|
+
ctx.lineTo(x, y);
|
|
67
|
+
}
|
|
68
|
+
ctx.strokeStyle = closeLineColor;
|
|
69
|
+
ctx.lineWidth = closeLineWidth;
|
|
70
|
+
ctx.stroke();
|
|
71
|
+
ctx.restore();
|
|
72
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/hollow-candlestick-renderer.ts
|
|
3
|
+
* Hollow candlestick renderer representing both intraday and bar-to-bar price direction.
|
|
4
|
+
*
|
|
5
|
+
* Rules:
|
|
6
|
+
* - Close >= Open: Hollow body (stroked outline)
|
|
7
|
+
* - Close < Open: Solid body (filled interior)
|
|
8
|
+
* - Close >= PrevClose: Bullish Green color
|
|
9
|
+
* - Close < PrevClose: Bearish Red color
|
|
10
|
+
*/
|
|
11
|
+
import type { ChartTheme, OHLCVBar, Scale } from '@tranding-custom-chart/core';
|
|
12
|
+
export interface HollowCandlestickRenderOptions {
|
|
13
|
+
readonly ctx: CanvasRenderingContext2D;
|
|
14
|
+
readonly bars: readonly OHLCVBar[];
|
|
15
|
+
readonly xScale: Scale<number>;
|
|
16
|
+
readonly yScale: Scale<number>;
|
|
17
|
+
readonly theme: ChartTheme;
|
|
18
|
+
readonly dpr: number;
|
|
19
|
+
readonly startIndex?: number | undefined;
|
|
20
|
+
readonly endIndex?: number | undefined;
|
|
21
|
+
readonly candleWidthRatio?: number | undefined;
|
|
22
|
+
readonly customUpColor?: string | undefined;
|
|
23
|
+
readonly customDownColor?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
export declare function renderHollowCandlesticks(options: HollowCandlestickRenderOptions): void;
|
|
26
|
+
//# sourceMappingURL=hollow-candlestick-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hollow-candlestick-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/hollow-candlestick-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAG/E,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,8BAA8B,GAAG,IAAI,CAiFtF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/hollow-candlestick-renderer.ts
|
|
3
|
+
* Hollow candlestick renderer representing both intraday and bar-to-bar price direction.
|
|
4
|
+
*
|
|
5
|
+
* Rules:
|
|
6
|
+
* - Close >= Open: Hollow body (stroked outline)
|
|
7
|
+
* - Close < Open: Solid body (filled interior)
|
|
8
|
+
* - Close >= PrevClose: Bullish Green color
|
|
9
|
+
* - Close < PrevClose: Bearish Red color
|
|
10
|
+
*/
|
|
11
|
+
import { snapLine, snapRect } from '../dpi.js';
|
|
12
|
+
export function renderHollowCandlesticks(options) {
|
|
13
|
+
const { ctx, bars, xScale, yScale, theme, dpr, startIndex = 0, endIndex = bars.length - 1, candleWidthRatio = 0.7, customUpColor, customDownColor, } = options;
|
|
14
|
+
if (bars.length === 0 || startIndex > endIndex)
|
|
15
|
+
return;
|
|
16
|
+
const start = Math.max(0, startIndex);
|
|
17
|
+
const end = Math.min(bars.length - 1, endIndex);
|
|
18
|
+
let slotWidth = 8;
|
|
19
|
+
if ('stepWidth' in xScale && typeof xScale.stepWidth === 'number') {
|
|
20
|
+
slotWidth = xScale.stepWidth;
|
|
21
|
+
}
|
|
22
|
+
else if (bars.length > 1) {
|
|
23
|
+
const x0 = xScale.scale(0);
|
|
24
|
+
const x1 = xScale.scale(1);
|
|
25
|
+
slotWidth = Math.abs(x1 - x0) || 8;
|
|
26
|
+
}
|
|
27
|
+
const candleWidth = slotWidth < 2 ? Math.max(0.5, slotWidth * 0.8) : Math.max(1, slotWidth * candleWidthRatio);
|
|
28
|
+
const halfWidth = candleWidth / 2;
|
|
29
|
+
const upColor = customUpColor ?? theme.upCandle;
|
|
30
|
+
const downColor = customDownColor ?? theme.downCandle;
|
|
31
|
+
ctx.save();
|
|
32
|
+
ctx.lineWidth = slotWidth < 2 ? 0.75 : 1;
|
|
33
|
+
for (let i = start; i <= end; i++) {
|
|
34
|
+
const b = bars[i];
|
|
35
|
+
const prevClose = i > 0 ? bars[i - 1].close : b.open;
|
|
36
|
+
// Color determined by comparison with previous close
|
|
37
|
+
const isBullishColor = b.close >= prevClose;
|
|
38
|
+
const color = isBullishColor ? upColor : downColor;
|
|
39
|
+
const isHollow = b.close >= b.open;
|
|
40
|
+
const xCenter = snapLine(xScale.scale(i), dpr);
|
|
41
|
+
const yHigh = snapLine(yScale.scale(b.high), dpr);
|
|
42
|
+
const yLow = snapLine(yScale.scale(b.low), dpr);
|
|
43
|
+
const yOpen = yScale.scale(b.open);
|
|
44
|
+
const yClose = yScale.scale(b.close);
|
|
45
|
+
const bodyTop = snapRect(Math.min(yOpen, yClose), dpr);
|
|
46
|
+
const bodyBottom = snapRect(Math.max(yOpen, yClose), dpr);
|
|
47
|
+
const bodyHeight = Math.max(1, bodyBottom - bodyTop);
|
|
48
|
+
const bodyLeft = snapRect(xCenter - halfWidth, dpr);
|
|
49
|
+
ctx.strokeStyle = color;
|
|
50
|
+
ctx.fillStyle = color;
|
|
51
|
+
// Draw wicks
|
|
52
|
+
ctx.beginPath();
|
|
53
|
+
// Top wick: High to top of body
|
|
54
|
+
ctx.moveTo(xCenter, yHigh);
|
|
55
|
+
ctx.lineTo(xCenter, bodyTop);
|
|
56
|
+
// Bottom wick: Bottom of body to Low
|
|
57
|
+
ctx.moveTo(xCenter, bodyBottom);
|
|
58
|
+
ctx.lineTo(xCenter, yLow);
|
|
59
|
+
ctx.stroke();
|
|
60
|
+
// Draw body
|
|
61
|
+
if (isHollow) {
|
|
62
|
+
// Hollow body: clear interior to canvas background or fill with surface, stroke border
|
|
63
|
+
ctx.strokeRect(bodyLeft, bodyTop, candleWidth, bodyHeight);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Solid filled body
|
|
67
|
+
ctx.fillRect(bodyLeft, bodyTop, candleWidth, bodyHeight);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
ctx.restore();
|
|
71
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/horizontal-bar-renderer.ts
|
|
3
|
+
* Canvas 2D renderer for Horizontal Bar charts with rounded corners and value badges.
|
|
4
|
+
*/
|
|
5
|
+
import type { HorizontalBarDatum, Scale } from '@tranding-custom-chart/core';
|
|
6
|
+
export interface HorizontalBarOptions {
|
|
7
|
+
readonly bounds: {
|
|
8
|
+
readonly width: number;
|
|
9
|
+
readonly height: number;
|
|
10
|
+
};
|
|
11
|
+
readonly padding: {
|
|
12
|
+
readonly top: number;
|
|
13
|
+
readonly right: number;
|
|
14
|
+
readonly bottom: number;
|
|
15
|
+
readonly left: number;
|
|
16
|
+
};
|
|
17
|
+
readonly xScale: Scale<number>;
|
|
18
|
+
readonly barHeightRatio?: number | undefined;
|
|
19
|
+
readonly borderRadius?: number | undefined;
|
|
20
|
+
readonly defaultColor?: string | undefined;
|
|
21
|
+
readonly showLabels?: boolean | undefined;
|
|
22
|
+
readonly labelColor?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
export declare function renderHorizontalBars(ctx: CanvasRenderingContext2D, data: readonly HorizontalBarDatum[], options: HorizontalBarOptions): void;
|
|
25
|
+
//# sourceMappingURL=horizontal-bar-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"horizontal-bar-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/horizontal-bar-renderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAG7E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnH,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,SAAS,kBAAkB,EAAE,EACnC,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CA6DN"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/horizontal-bar-renderer.ts
|
|
3
|
+
* Canvas 2D renderer for Horizontal Bar charts with rounded corners and value badges.
|
|
4
|
+
*/
|
|
5
|
+
import { snapRect } from '../dpi.js';
|
|
6
|
+
export function renderHorizontalBars(ctx, data, options) {
|
|
7
|
+
if (data.length === 0)
|
|
8
|
+
return;
|
|
9
|
+
const { bounds, padding, xScale, barHeightRatio = 0.6, borderRadius = 4, defaultColor = '#3b82f6', showLabels = true, labelColor = '#e8ebe6', } = options;
|
|
10
|
+
const innerHeight = bounds.height - padding.top - padding.bottom;
|
|
11
|
+
const slotHeight = innerHeight / data.length;
|
|
12
|
+
const barHeight = Math.max(4, Math.round(slotHeight * barHeightRatio));
|
|
13
|
+
const zeroX = xScale.scale(0);
|
|
14
|
+
ctx.save();
|
|
15
|
+
for (let i = 0; i < data.length; i++) {
|
|
16
|
+
const item = data[i];
|
|
17
|
+
const slotY = padding.top + i * slotHeight;
|
|
18
|
+
const centerY = slotY + slotHeight / 2;
|
|
19
|
+
const top = centerY - barHeight / 2;
|
|
20
|
+
const valX = xScale.scale(item.value);
|
|
21
|
+
const left = Math.min(zeroX, valX);
|
|
22
|
+
const width = Math.max(2, Math.abs(valX - zeroX));
|
|
23
|
+
const rx = snapRect(left);
|
|
24
|
+
const ry = snapRect(top);
|
|
25
|
+
const rw = snapRect(width);
|
|
26
|
+
const rh = snapRect(barHeight);
|
|
27
|
+
ctx.fillStyle = item.color ?? defaultColor;
|
|
28
|
+
ctx.beginPath();
|
|
29
|
+
if (typeof ctx.roundRect === 'function' && borderRadius > 0) {
|
|
30
|
+
ctx.roundRect(rx, ry, rw, rh, Math.min(borderRadius, rh / 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
ctx.rect(rx, ry, rw, rh);
|
|
34
|
+
}
|
|
35
|
+
ctx.fill();
|
|
36
|
+
if (showLabels) {
|
|
37
|
+
// Category label on the left margin
|
|
38
|
+
ctx.fillStyle = labelColor;
|
|
39
|
+
const fontSize = slotHeight < 22 ? 10 : slotHeight < 28 ? 11 : 12;
|
|
40
|
+
ctx.font = `500 ${fontSize}px Inter, system-ui, sans-serif`;
|
|
41
|
+
ctx.textAlign = 'right';
|
|
42
|
+
ctx.textBaseline = 'middle';
|
|
43
|
+
ctx.fillText(item.category, rx - 8, centerY);
|
|
44
|
+
// Value label to the right of the bar
|
|
45
|
+
ctx.textAlign = 'left';
|
|
46
|
+
ctx.fillStyle = labelColor;
|
|
47
|
+
const displayVal = item.label ?? item.value.toLocaleString();
|
|
48
|
+
ctx.fillText(displayVal, rx + rw + 8, centerY);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
ctx.restore();
|
|
52
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/last-price-renderer.ts
|
|
3
|
+
* Real-time last price line and dynamic Y-axis price badge.
|
|
4
|
+
*/
|
|
5
|
+
import type { Dimension, Padding, Scale } from '@tranding-custom-chart/core';
|
|
6
|
+
export interface LastPriceRenderOptions {
|
|
7
|
+
readonly ctx: CanvasRenderingContext2D;
|
|
8
|
+
readonly lastPrice: number;
|
|
9
|
+
readonly yScale: Scale<number>;
|
|
10
|
+
readonly bounds: Dimension;
|
|
11
|
+
readonly padding: Padding;
|
|
12
|
+
readonly dpr: number;
|
|
13
|
+
readonly color?: string | undefined;
|
|
14
|
+
readonly formatter?: ((price: number) => string) | undefined;
|
|
15
|
+
}
|
|
16
|
+
export declare function renderLastPriceLine(options: LastPriceRenderOptions): void;
|
|
17
|
+
//# sourceMappingURL=last-price-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"last-price-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/last-price-renderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAG7E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CAC9D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAyDzE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/last-price-renderer.ts
|
|
3
|
+
* Real-time last price line and dynamic Y-axis price badge.
|
|
4
|
+
*/
|
|
5
|
+
import { snapLine } from '../dpi.js';
|
|
6
|
+
export function renderLastPriceLine(options) {
|
|
7
|
+
const { ctx, lastPrice, yScale, bounds, padding, dpr, color = '#10b981', formatter = (p) => `₹${p.toFixed(2)}`, } = options;
|
|
8
|
+
if (!Number.isFinite(lastPrice))
|
|
9
|
+
return;
|
|
10
|
+
const yRaw = yScale.scale(lastPrice);
|
|
11
|
+
const chartTop = padding.top;
|
|
12
|
+
const chartBottom = bounds.height - padding.bottom;
|
|
13
|
+
// Do not draw if outside visible Y range
|
|
14
|
+
if (yRaw < chartTop || yRaw > chartBottom)
|
|
15
|
+
return;
|
|
16
|
+
const y = snapLine(yRaw, dpr);
|
|
17
|
+
const chartLeft = padding.left;
|
|
18
|
+
const chartRight = bounds.width - padding.right;
|
|
19
|
+
ctx.save();
|
|
20
|
+
// 1. Horizontal dashed line across chart
|
|
21
|
+
ctx.strokeStyle = color;
|
|
22
|
+
ctx.lineWidth = 1;
|
|
23
|
+
ctx.setLineDash([3, 3]);
|
|
24
|
+
ctx.beginPath();
|
|
25
|
+
ctx.moveTo(chartLeft, y);
|
|
26
|
+
ctx.lineTo(chartRight, y);
|
|
27
|
+
ctx.stroke();
|
|
28
|
+
// 2. Solid Price Badge on Right Y-Axis
|
|
29
|
+
ctx.setLineDash([]);
|
|
30
|
+
const text = formatter(lastPrice);
|
|
31
|
+
ctx.font = '10px monospace';
|
|
32
|
+
const textMetrics = ctx.measureText(text);
|
|
33
|
+
const badgeWidth = textMetrics.width + 10;
|
|
34
|
+
const badgeHeight = 16;
|
|
35
|
+
const badgeX = chartRight + 2;
|
|
36
|
+
const badgeY = y - badgeHeight / 2;
|
|
37
|
+
// Background rect
|
|
38
|
+
ctx.fillStyle = color;
|
|
39
|
+
ctx.fillRect(badgeX, badgeY, badgeWidth, badgeHeight);
|
|
40
|
+
// Text
|
|
41
|
+
ctx.fillStyle = '#ffffff';
|
|
42
|
+
ctx.textAlign = 'left';
|
|
43
|
+
ctx.textBaseline = 'middle';
|
|
44
|
+
ctx.fillText(text, badgeX + 5, y);
|
|
45
|
+
ctx.restore();
|
|
46
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-renderer-canvas/src/renderers/line-renderer.ts
|
|
3
|
+
* Stroked line and gradient area fill renderer.
|
|
4
|
+
*/
|
|
5
|
+
import type { Scale } from '@tranding-custom-chart/core';
|
|
6
|
+
export interface LineRenderOptions {
|
|
7
|
+
readonly ctx: CanvasRenderingContext2D;
|
|
8
|
+
readonly points: readonly (number | null | undefined)[];
|
|
9
|
+
readonly xScale: Scale<number>;
|
|
10
|
+
readonly yScale: Scale<number>;
|
|
11
|
+
readonly color: string;
|
|
12
|
+
readonly strokeWidth?: number | undefined;
|
|
13
|
+
readonly lineDash?: readonly number[] | undefined;
|
|
14
|
+
readonly areaFill?: {
|
|
15
|
+
readonly gradientTopColor: string;
|
|
16
|
+
readonly gradientBottomColor: string;
|
|
17
|
+
readonly baselineY: number;
|
|
18
|
+
} | undefined;
|
|
19
|
+
readonly startIndex?: number | undefined;
|
|
20
|
+
readonly endIndex?: number | undefined;
|
|
21
|
+
}
|
|
22
|
+
export declare function renderLineSeries(options: LineRenderOptions): void;
|
|
23
|
+
//# sourceMappingURL=line-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-renderer.d.ts","sourceRoot":"","sources":["../../src/renderers/line-renderer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAEzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IACxD,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;QAClC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;QACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,GAAG,SAAS,CAAC;IACd,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAiGjE"}
|