@funnest/charts 0.6.9 → 0.6.11
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/chart.d.ts +10 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +77 -13
- package/dist/objects/drawings.d.ts +34 -1
- package/dist/objects/types.d.ts +8 -0
- package/dist/render/renderer.d.ts +2 -1
- package/package.json +1 -1
package/dist/chart.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { type Drawing, type DrawingDefinition, type DrawingType } from './object
|
|
|
6
6
|
import { type AlertLineOptions, type ExecutionMarker, type LineHandle, type OrderLineOptions, type PositionLineOptions, type TradingLabelOptions } from './objects/trading';
|
|
7
7
|
import { type ChartSettings, type ChartState } from './settings';
|
|
8
8
|
import { type TradingAdapter } from './objects/trading-sync';
|
|
9
|
+
import type { ChartOverlay } from './objects/types';
|
|
9
10
|
import { type ChartType, type CrosshairInfo, type PaneId } from './render/renderer';
|
|
10
11
|
import { type ThemeTokens } from './theme';
|
|
11
12
|
import type { Bar, ConnectionState, Datafeed, Quote, Resolution, SymbolInfo, TickSize } from './types';
|
|
@@ -166,6 +167,7 @@ export declare class Chart {
|
|
|
166
167
|
private readonly interaction;
|
|
167
168
|
private readonly cleanups;
|
|
168
169
|
private readonly drawings;
|
|
170
|
+
private readonly overlays;
|
|
169
171
|
private readonly trading;
|
|
170
172
|
private readonly tradingSync;
|
|
171
173
|
private tooltipKey;
|
|
@@ -411,6 +413,14 @@ export declare class Chart {
|
|
|
411
413
|
addOrderLine(options: OrderLineOptions): LineHandle<OrderLineOptions>;
|
|
412
414
|
/** Price alert line (dotted, gray, "알림" label). Pass `onMove` to make it draggable and `onCancel` for an ×. */
|
|
413
415
|
addAlertLine(options: AlertLineOptions): LineHandle<AlertLineOptions>;
|
|
416
|
+
/**
|
|
417
|
+
* Paints `overlay` on the price pane every frame (see ChartOverlay). `redraw()` asks for a
|
|
418
|
+
* new frame after its data changed; `remove()` takes it off.
|
|
419
|
+
*/
|
|
420
|
+
addOverlay(overlay: ChartOverlay): {
|
|
421
|
+
redraw(): void;
|
|
422
|
+
remove(): void;
|
|
423
|
+
};
|
|
414
424
|
/** Fill markers (▲ buy under the bar, ▼ sell above; ⚡ at the price for liquidations). Replaces the previous set. */
|
|
415
425
|
setExecutions(markers: readonly ExecutionMarker[]): void;
|
|
416
426
|
/** What position/order/alert line labels show and where (direction, price, size, P&L, ×, alignment, line width). */
|
package/dist/index.d.ts
CHANGED
|
@@ -13,9 +13,9 @@ export type { FormatPriceOptions } from './price';
|
|
|
13
13
|
export { decimalsOf, formatCompact, formatPrice, krxTickSize, resolveTickSize, roundToTick } from './price';
|
|
14
14
|
export { isIntradayResolution, resolutionToMs } from './resolution';
|
|
15
15
|
export type { ChartType } from './render/renderer';
|
|
16
|
-
export type { BuiltinDrawingType, Drawing, DrawingDefinition, DrawingGeometry, DrawingPoint, DrawingRenderContext, DrawingType, } from './objects/drawings';
|
|
16
|
+
export type { BuiltinDrawingType, Drawing, DrawingDefinition, DrawingGeometry, DrawingPoint, DrawingRenderContext, DrawingStyle, DrawingType, } from './objects/drawings';
|
|
17
17
|
export { BUILTIN_DRAWINGS, distToSegment, FIB_LEVELS } from './objects/drawings';
|
|
18
|
-
export type { PaneCoords } from './objects/types';
|
|
18
|
+
export type { ChartOverlay, ObjectRenderContext, PaneCoords } from './objects/types';
|
|
19
19
|
export type { Painter } from './render/painter';
|
|
20
20
|
export type { Palette } from './render/renderer';
|
|
21
21
|
export { snapToOhlc } from './objects/magnet';
|
package/dist/index.js
CHANGED
|
@@ -1221,6 +1221,7 @@ var IndicatorManager = class {
|
|
|
1221
1221
|
};
|
|
1222
1222
|
|
|
1223
1223
|
// src/objects/drawings.ts
|
|
1224
|
+
var DASHES = { solid: [], dashed: [6, 4], dotted: [1.5, 3] };
|
|
1224
1225
|
var FIB_LEVELS = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1];
|
|
1225
1226
|
var FIB_COLORS = ["#8B95A1", "#F04452", "#FF9F0A", "#12B886", "#3182F6", "#7048E8", "#8B95A1"];
|
|
1226
1227
|
var HANDLE_RADIUS = 5;
|
|
@@ -1240,7 +1241,7 @@ var BUILTIN_DRAWINGS = [
|
|
|
1240
1241
|
group: "line",
|
|
1241
1242
|
clicks: 2,
|
|
1242
1243
|
distance: (g, x, y) => distToSegment(x, y, ...p0(g), ...p1(g)),
|
|
1243
|
-
render: (_d, { painter, color, ...g }) => painter.line(...p0(g), ...p1(g), color, 2)
|
|
1244
|
+
render: (_d, { painter, color, ...g }) => painter.line(...p0(g), ...p1(g), color, g.width(2), g.dash())
|
|
1244
1245
|
},
|
|
1245
1246
|
{
|
|
1246
1247
|
type: "horizontal",
|
|
@@ -1252,7 +1253,9 @@ var BUILTIN_DRAWINGS = [
|
|
|
1252
1253
|
distance: (g, _x, y) => Math.abs(y - p0(g)[1]),
|
|
1253
1254
|
render: (d, { painter, color, coords, badge, ...g }) => {
|
|
1254
1255
|
const y = p0(g)[1];
|
|
1255
|
-
|
|
1256
|
+
const dash = g.dash();
|
|
1257
|
+
if (g.width(1) === 1 && !dash.length) painter.hLine(y, 0, coords.plotWidth, color);
|
|
1258
|
+
else painter.line(0, y, coords.plotWidth, y, color, g.width(1), dash);
|
|
1256
1259
|
badge(y, coords.formatPrice(d.points[0].price), color);
|
|
1257
1260
|
}
|
|
1258
1261
|
},
|
|
@@ -1314,6 +1317,7 @@ var DrawingManager = class {
|
|
|
1314
1317
|
tool = null;
|
|
1315
1318
|
selectedId = null;
|
|
1316
1319
|
draft = null;
|
|
1320
|
+
freehandLast = null;
|
|
1317
1321
|
drag = null;
|
|
1318
1322
|
get selected() {
|
|
1319
1323
|
return this.drawings.find((d) => d.id === this.selectedId) ?? null;
|
|
@@ -1368,6 +1372,13 @@ var DrawingManager = class {
|
|
|
1368
1372
|
const def = this.tool ? this.registry.get(this.tool) : void 0;
|
|
1369
1373
|
if (!def) return;
|
|
1370
1374
|
const point = c.pointAt(x, y);
|
|
1375
|
+
if (def.freehand) {
|
|
1376
|
+
if (this.draft) return this.finish();
|
|
1377
|
+
this.draft = { drawing: { id: newId("d"), type: def.type, points: [point] }, placed: [point] };
|
|
1378
|
+
this.freehandLast = [x, y];
|
|
1379
|
+
this.onChange("preview");
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1371
1382
|
if (!this.draft) {
|
|
1372
1383
|
const placed = [point];
|
|
1373
1384
|
this.draft = { drawing: { id: newId("d"), type: def.type, points: this.expand(def, placed, c) }, placed };
|
|
@@ -1384,11 +1395,25 @@ var DrawingManager = class {
|
|
|
1384
1395
|
const draft = this.draft;
|
|
1385
1396
|
const def = draft && this.registry.get(draft.drawing.type);
|
|
1386
1397
|
if (!draft || !def) return;
|
|
1398
|
+
if (def.freehand) {
|
|
1399
|
+
const last = this.freehandLast;
|
|
1400
|
+
if (last && Math.hypot(x - last[0], y - last[1]) < 3) return;
|
|
1401
|
+
if (draft.drawing.points.length >= 4e3) return;
|
|
1402
|
+
this.freehandLast = [x, y];
|
|
1403
|
+
draft.drawing.points.push(c.pointAt(x, y));
|
|
1404
|
+
this.onChange("preview");
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1387
1407
|
draft.drawing.points = this.expand(def, [...draft.placed, c.pointAt(x, y)], c);
|
|
1388
1408
|
this.onChange("preview");
|
|
1389
1409
|
}
|
|
1390
1410
|
/** Pointer released: a drag since the first point places the next one (drag-to-draw). */
|
|
1391
1411
|
releaseAt(c, x, y, moved) {
|
|
1412
|
+
const def = this.draft && this.registry.get(this.draft.drawing.type);
|
|
1413
|
+
if (def?.freehand) {
|
|
1414
|
+
if (moved && this.draft.drawing.points.length > 1) this.finish();
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1392
1417
|
if (this.draft && moved) this.createAt(c, x, y);
|
|
1393
1418
|
}
|
|
1394
1419
|
finish() {
|
|
@@ -1407,7 +1432,7 @@ var DrawingManager = class {
|
|
|
1407
1432
|
handles(c, d) {
|
|
1408
1433
|
const def = this.registry.get(d.type);
|
|
1409
1434
|
const g = this.geometry(c, d);
|
|
1410
|
-
return def?.handles ? def.handles(g) : g.points;
|
|
1435
|
+
return def?.handles ? def.handles(g) : def?.freehand ? [] : g.points;
|
|
1411
1436
|
}
|
|
1412
1437
|
hitTest(c, x, y, tolerance) {
|
|
1413
1438
|
const selected = this.selected;
|
|
@@ -1426,6 +1451,7 @@ var DrawingManager = class {
|
|
|
1426
1451
|
const d = this.drawings.find((x2) => x2.id === id);
|
|
1427
1452
|
if (!d) return;
|
|
1428
1453
|
this.select(id);
|
|
1454
|
+
if (d.style?.locked) return;
|
|
1429
1455
|
this.drag = { id, part, x, y, origin: this.geometry(c, d).points };
|
|
1430
1456
|
}
|
|
1431
1457
|
dragTo(c, x, y) {
|
|
@@ -1448,21 +1474,32 @@ var DrawingManager = class {
|
|
|
1448
1474
|
this.drag = null;
|
|
1449
1475
|
}
|
|
1450
1476
|
// ── Rendering ─────────────────────────────────────────────────────
|
|
1451
|
-
render(rc, badges) {
|
|
1477
|
+
render(rc, badges, bars = [], barIndexAt = () => -1) {
|
|
1452
1478
|
const all = this.draft ? [...this.drawings, this.draft.drawing] : this.drawings;
|
|
1453
1479
|
const c = rc.coords;
|
|
1454
1480
|
for (const d of all) {
|
|
1455
1481
|
const def = this.registry.get(d.type);
|
|
1456
1482
|
if (!def) continue;
|
|
1457
1483
|
const color = d.color ?? rc.palette.accent;
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1484
|
+
const style = d.style ?? {};
|
|
1485
|
+
rc.painter.ctx.save();
|
|
1486
|
+
try {
|
|
1487
|
+
def.render(d, {
|
|
1488
|
+
...this.geometry(c, d),
|
|
1489
|
+
painter: rc.painter,
|
|
1490
|
+
palette: rc.palette,
|
|
1491
|
+
color,
|
|
1492
|
+
style,
|
|
1493
|
+
width: (fallback) => Math.min(8, Math.max(0.5, style.width ?? fallback)),
|
|
1494
|
+
dash: (fallback = []) => style.dash ? DASHES[style.dash] : [...fallback],
|
|
1495
|
+
bars,
|
|
1496
|
+
barIndexAt,
|
|
1497
|
+
badge: (y, text, bg, fg = "#FFFFFF") => badges.push({ y, text, bg, fg })
|
|
1498
|
+
});
|
|
1499
|
+
} finally {
|
|
1500
|
+
rc.painter.ctx.restore();
|
|
1501
|
+
}
|
|
1502
|
+
if (d.id === this.selectedId && !style.locked || d === this.draft?.drawing) {
|
|
1466
1503
|
for (const [px, py] of this.handles(c, d)) rc.painter.circle(px, py, HANDLE_RADIUS, rc.palette.background, color);
|
|
1467
1504
|
}
|
|
1468
1505
|
}
|
|
@@ -2409,8 +2446,17 @@ function drawMain(layer, m) {
|
|
|
2409
2446
|
ctx.clip();
|
|
2410
2447
|
const o = m.objects;
|
|
2411
2448
|
const rc = { painter: p, coords: o.coords, palette: pal };
|
|
2449
|
+
for (const overlay of o.overlays) {
|
|
2450
|
+
ctx.save();
|
|
2451
|
+
try {
|
|
2452
|
+
overlay.render(rc);
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
console.error("[fnc] overlay", error);
|
|
2455
|
+
}
|
|
2456
|
+
ctx.restore();
|
|
2457
|
+
}
|
|
2412
2458
|
o.trading.render(rc, m.bars, o.barIndexAt, (i) => m.timeScale.indexToX(i), o.tradingLabels, badges);
|
|
2413
|
-
o.drawings.render(rc, badges);
|
|
2459
|
+
o.drawings.render(rc, badges, m.bars, o.barIndexAt);
|
|
2414
2460
|
ctx.restore();
|
|
2415
2461
|
const last = f.settings.lastPriceLine ? m.bars[m.bars.length - 1] : void 0;
|
|
2416
2462
|
const lastColor = last && last.close >= last.open ? pal.up : pal.down;
|
|
@@ -2747,6 +2793,7 @@ var Chart = class {
|
|
|
2747
2793
|
interaction;
|
|
2748
2794
|
cleanups = [];
|
|
2749
2795
|
drawings;
|
|
2796
|
+
overlays = [];
|
|
2750
2797
|
trading;
|
|
2751
2798
|
tradingSync;
|
|
2752
2799
|
tooltipKey = null;
|
|
@@ -3723,6 +3770,22 @@ var Chart = class {
|
|
|
3723
3770
|
addAlertLine(options) {
|
|
3724
3771
|
return this.trading.addAlert(options);
|
|
3725
3772
|
}
|
|
3773
|
+
/**
|
|
3774
|
+
* Paints `overlay` on the price pane every frame (see ChartOverlay). `redraw()` asks for a
|
|
3775
|
+
* new frame after its data changed; `remove()` takes it off.
|
|
3776
|
+
*/
|
|
3777
|
+
addOverlay(overlay) {
|
|
3778
|
+
this.overlays.push(overlay);
|
|
3779
|
+
this.invalidate();
|
|
3780
|
+
return {
|
|
3781
|
+
redraw: () => this.invalidate(),
|
|
3782
|
+
remove: () => {
|
|
3783
|
+
const i = this.overlays.indexOf(overlay);
|
|
3784
|
+
if (i >= 0) this.overlays.splice(i, 1);
|
|
3785
|
+
this.invalidate();
|
|
3786
|
+
}
|
|
3787
|
+
};
|
|
3788
|
+
}
|
|
3726
3789
|
/** Fill markers (▲ buy under the bar, ▼ sell above; ⚡ at the price for liquidations). Replaces the previous set. */
|
|
3727
3790
|
setExecutions(markers) {
|
|
3728
3791
|
this.trading.setExecutions(markers);
|
|
@@ -3829,6 +3892,7 @@ var Chart = class {
|
|
|
3829
3892
|
replaySelecting: this.replaySelecting,
|
|
3830
3893
|
objects: {
|
|
3831
3894
|
coords: this.paneCoords(),
|
|
3895
|
+
overlays: this.overlays,
|
|
3832
3896
|
drawings: this.drawings,
|
|
3833
3897
|
trading: this.trading,
|
|
3834
3898
|
tradingLabels: TRADING_LABELS[this.locale],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Bar } from '../types';
|
|
1
2
|
import type { Locale } from '../model/time-format';
|
|
2
3
|
import type { Painter } from '../render/painter';
|
|
3
4
|
import type { Palette } from '../render/renderer';
|
|
@@ -11,11 +12,25 @@ export interface DrawingPoint {
|
|
|
11
12
|
time: number;
|
|
12
13
|
price: number;
|
|
13
14
|
}
|
|
15
|
+
/** How a drawing looks beyond its color (all optional; tools read them through the render context). */
|
|
16
|
+
export interface DrawingStyle {
|
|
17
|
+
/** Main line width in CSS px (1–4). */
|
|
18
|
+
width?: number;
|
|
19
|
+
/** Main line pattern. */
|
|
20
|
+
dash?: 'solid' | 'dashed' | 'dotted';
|
|
21
|
+
/** Text tools (and notes on others): what it says. */
|
|
22
|
+
text?: string;
|
|
23
|
+
/** Text size in CSS px. */
|
|
24
|
+
fontSize?: number;
|
|
25
|
+
/** Can't be dragged or edited until unlocked (still selectable). */
|
|
26
|
+
locked?: boolean;
|
|
27
|
+
}
|
|
14
28
|
export interface Drawing {
|
|
15
29
|
id: string;
|
|
16
30
|
type: DrawingType;
|
|
17
31
|
points: DrawingPoint[];
|
|
18
32
|
color?: string;
|
|
33
|
+
style?: DrawingStyle;
|
|
19
34
|
}
|
|
20
35
|
/** What a drawing definition gets to measure and paint itself. Pixels are relative to the price pane. */
|
|
21
36
|
export interface DrawingGeometry {
|
|
@@ -28,6 +43,16 @@ export interface DrawingRenderContext extends DrawingGeometry {
|
|
|
28
43
|
palette: Palette;
|
|
29
44
|
/** `drawing.color`, or the theme accent. */
|
|
30
45
|
color: string;
|
|
46
|
+
/** `drawing.style` (empty object when none). */
|
|
47
|
+
style: DrawingStyle;
|
|
48
|
+
/** The main line width: the drawing's style, else the tool's default. */
|
|
49
|
+
width(fallback: number): number;
|
|
50
|
+
/** The main line dash pattern for canvas: the drawing's style, else the tool's default. */
|
|
51
|
+
dash(fallback?: readonly number[]): number[];
|
|
52
|
+
/** The chart's bars (oldest first), for tools that measure them (VWAP, volume profile…). */
|
|
53
|
+
bars: readonly Bar[];
|
|
54
|
+
/** Index of the last bar at or before `time` (-1 before the first). */
|
|
55
|
+
barIndexAt(time: number): number;
|
|
31
56
|
/** Adds a price-axis badge (drawn above the axis labels, clamped to the pane). */
|
|
32
57
|
badge(y: number, text: string, bg: string, fg?: string): void;
|
|
33
58
|
}
|
|
@@ -61,6 +86,13 @@ export interface DrawingDefinition {
|
|
|
61
86
|
constrain?(points: DrawingPoint[], index: number): DrawingPoint[];
|
|
62
87
|
/** Drag handle positions; dragging handle i moves point i. Default: the points. */
|
|
63
88
|
handles?(geometry: DrawingGeometry): [number, number][];
|
|
89
|
+
/**
|
|
90
|
+
* Drawn by dragging: every pointer move adds a point (a brush); releasing (or clicking again)
|
|
91
|
+
* finishes it. `clicks` is ignored.
|
|
92
|
+
*/
|
|
93
|
+
freehand?: boolean;
|
|
94
|
+
/** Has text (`style.text`): the widget offers a text field for it when selected. */
|
|
95
|
+
text?: boolean;
|
|
64
96
|
/** Distance in px from (x, y) to the shape, for hit testing. */
|
|
65
97
|
distance(geometry: DrawingGeometry, x: number, y: number): number;
|
|
66
98
|
render(drawing: Drawing, rc: DrawingRenderContext): void;
|
|
@@ -84,6 +116,7 @@ export declare class DrawingManager {
|
|
|
84
116
|
tool: DrawingType | null;
|
|
85
117
|
selectedId: string | null;
|
|
86
118
|
private draft;
|
|
119
|
+
private freehandLast;
|
|
87
120
|
private drag;
|
|
88
121
|
constructor(onChange: (change: DrawingsChange) => void, registry?: Map<string, DrawingDefinition>);
|
|
89
122
|
get selected(): Drawing | null;
|
|
@@ -109,5 +142,5 @@ export declare class DrawingManager {
|
|
|
109
142
|
beginDrag(c: PaneCoords, id: string, part: 'body' | number, x: number, y: number): void;
|
|
110
143
|
dragTo(c: PaneCoords, x: number, y: number): void;
|
|
111
144
|
endDrag(moved: boolean): void;
|
|
112
|
-
render(rc: ObjectRenderContext, badges: AxisBadge[]): void;
|
|
145
|
+
render(rc: ObjectRenderContext, badges: AxisBadge[], bars?: readonly Bar[], barIndexAt?: (time: number) => number): void;
|
|
113
146
|
}
|
package/dist/objects/types.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ export interface ObjectRenderContext {
|
|
|
22
22
|
coords: PaneCoords;
|
|
23
23
|
palette: Palette;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Something an app paints on the price pane every frame (a liquidation map, volume profile…),
|
|
27
|
+
* clipped to the pane, above the candles and indicators, under trading lines and drawings.
|
|
28
|
+
* Added with chart.addOverlay(); it reads its own data and the pane's coordinates.
|
|
29
|
+
*/
|
|
30
|
+
export interface ChartOverlay {
|
|
31
|
+
render(rc: ObjectRenderContext): void;
|
|
32
|
+
}
|
|
25
33
|
export type ObjectHit = {
|
|
26
34
|
kind: 'drawing';
|
|
27
35
|
id: string;
|
|
@@ -5,7 +5,7 @@ import type { Bar } from '../types';
|
|
|
5
5
|
import type { CanvasLayer } from './canvas-layer';
|
|
6
6
|
import type { DrawingManager } from '../objects/drawings';
|
|
7
7
|
import type { TradingLabels, TradingLayer } from '../objects/trading';
|
|
8
|
-
import type { PaneCoords } from '../objects/types';
|
|
8
|
+
import type { ChartOverlay, PaneCoords } from '../objects/types';
|
|
9
9
|
import type { IndicatorOutput } from '../indicators/definitions';
|
|
10
10
|
import type { ChartSettings } from '../settings';
|
|
11
11
|
export type ChartType = 'candles' | 'line' | 'area';
|
|
@@ -95,6 +95,7 @@ export interface RenderModel {
|
|
|
95
95
|
replaySelecting?: boolean;
|
|
96
96
|
objects: {
|
|
97
97
|
coords: PaneCoords;
|
|
98
|
+
overlays: readonly ChartOverlay[];
|
|
98
99
|
drawings: DrawingManager;
|
|
99
100
|
trading: TradingLayer;
|
|
100
101
|
tradingLabels: TradingLabels;
|