@funnest/charts 0.6.26 → 0.6.27
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 +18 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +88 -5
- package/dist/objects/drawings.d.ts +23 -3
- package/package.json +1 -1
package/dist/chart.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Locale } from './model/time-format';
|
|
|
2
2
|
import { TimeScale, type IndexRange, type TimeScaleOptions } from './model/time-scale';
|
|
3
3
|
import type { IndicatorDefinition, IndicatorParams, IndicatorType } from './indicators/definitions';
|
|
4
4
|
import { type IndicatorConfig, type IndicatorPatch } from './indicators/manager';
|
|
5
|
-
import { type Drawing, type DrawingDefinition, type DrawingType } from './objects/drawings';
|
|
5
|
+
import { type Drawing, type DrawingDefaults, type DrawingDefinition, type DrawingType } from './objects/drawings';
|
|
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';
|
|
@@ -77,8 +77,14 @@ export interface ChartEvents {
|
|
|
77
77
|
loadingChange: boolean;
|
|
78
78
|
/** Drawings were created, edited or removed (not fired during a live drag). */
|
|
79
79
|
drawingsChange: Drawing[];
|
|
80
|
-
/** The armed drawing tool changed (null = cursor). Tools disarm after one drawing. */
|
|
80
|
+
/** The armed drawing tool changed (null = cursor). Tools disarm after one drawing (unless `keepDrawingTool`). */
|
|
81
81
|
drawingToolChange: DrawingType | null;
|
|
82
|
+
/** All drawings hidden (true) or shown again. */
|
|
83
|
+
drawingsHiddenChange: boolean;
|
|
84
|
+
/** 연속 그리기 turned on or off. */
|
|
85
|
+
keepDrawingToolChange: boolean;
|
|
86
|
+
/** A tool's last color / style changed (save it to restore with `setDrawingDefaults`). */
|
|
87
|
+
drawingDefaultsChange: DrawingDefaults;
|
|
82
88
|
selectionChange: Drawing | null;
|
|
83
89
|
magnetChange: boolean;
|
|
84
90
|
chartTypeChange: ChartType;
|
|
@@ -358,6 +364,16 @@ export declare class Chart {
|
|
|
358
364
|
/** Arms a drawing tool (null = cursor). It disarms itself after one drawing is placed. */
|
|
359
365
|
setDrawingTool(tool: DrawingType | null): void;
|
|
360
366
|
get drawingTool(): DrawingType | null;
|
|
367
|
+
/** Hides every drawing (not drawn, not clickable) or shows them again. Arming a tool shows them. */
|
|
368
|
+
setDrawingsHidden(hidden: boolean): void;
|
|
369
|
+
get drawingsHidden(): boolean;
|
|
370
|
+
/** 연속 그리기: the armed tool stays armed after each drawing (Esc or the cursor drops it). */
|
|
371
|
+
setKeepDrawingTool(on: boolean): void;
|
|
372
|
+
get keepDrawingTool(): boolean;
|
|
373
|
+
/** Each tool's last color / line / text style; new drawings of a tool start with it. */
|
|
374
|
+
getDrawingDefaults(): DrawingDefaults;
|
|
375
|
+
/** Restores them (e.g. from storage). Doesn't fire drawingDefaultsChange. */
|
|
376
|
+
setDrawingDefaults(defaults: DrawingDefaults): void;
|
|
361
377
|
/** Magnet mode: drawing points snap to the nearest open/high/low/close of the bar. */
|
|
362
378
|
setMagnet(on: boolean): void;
|
|
363
379
|
get magnetEnabled(): boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type { FormatPriceOptions } from './price';
|
|
|
14
14
|
export { decimalsOf, formatCompact, formatPrice, krxTickSize, resolveTickSize, roundToTick } from './price';
|
|
15
15
|
export { isIntradayResolution, resolutionLabel, resolutionToMs } from './resolution';
|
|
16
16
|
export type { ChartType } from './render/renderer';
|
|
17
|
-
export type { BuiltinDrawingType, Drawing, DrawingDefinition, DrawingGeometry, DrawingPoint, DrawingRenderContext, DrawingStyle, DrawingColorSlot, DrawingType, } from './objects/drawings';
|
|
17
|
+
export type { BuiltinDrawingType, Drawing, DrawingDefaults, DrawingDefinition, DrawingGeometry, DrawingPoint, DrawingRenderContext, DrawingStyle, DrawingColorSlot, DrawingType, } from './objects/drawings';
|
|
18
18
|
export { BUILTIN_DRAWINGS, distToSegment, FIB_LEVELS } from './objects/drawings';
|
|
19
19
|
export type { ChartOverlay, ObjectRenderContext, PaneCoords } from './objects/types';
|
|
20
20
|
export type { Painter } from './render/painter';
|
package/dist/index.js
CHANGED
|
@@ -1312,6 +1312,7 @@ function drawingRegistry(extra = []) {
|
|
|
1312
1312
|
for (const def of extra) registry.set(def.type, def);
|
|
1313
1313
|
return registry;
|
|
1314
1314
|
}
|
|
1315
|
+
var CLOSE_PX = 7;
|
|
1315
1316
|
var DrawingManager = class {
|
|
1316
1317
|
constructor(onChange, registry = drawingRegistry()) {
|
|
1317
1318
|
this.onChange = onChange;
|
|
@@ -1322,6 +1323,11 @@ var DrawingManager = class {
|
|
|
1322
1323
|
drawings = [];
|
|
1323
1324
|
tool = null;
|
|
1324
1325
|
selectedId = null;
|
|
1326
|
+
/** Every drawing hidden at once (arming a tool shows them again). */
|
|
1327
|
+
hiddenAll = false;
|
|
1328
|
+
/** The tool stays armed after a drawing is placed (연속 그리기). */
|
|
1329
|
+
keepTool = false;
|
|
1330
|
+
defaults = {};
|
|
1325
1331
|
draft = null;
|
|
1326
1332
|
freehandLast = null;
|
|
1327
1333
|
drag = null;
|
|
@@ -1333,8 +1339,15 @@ var DrawingManager = class {
|
|
|
1333
1339
|
this.tool = tool;
|
|
1334
1340
|
this.draft = null;
|
|
1335
1341
|
if (tool) this.selectedId = null;
|
|
1342
|
+
if (tool && this.hiddenAll) this.setHiddenAll(false);
|
|
1336
1343
|
this.onChange("tool");
|
|
1337
1344
|
}
|
|
1345
|
+
setHiddenAll(hidden) {
|
|
1346
|
+
if (hidden === this.hiddenAll) return;
|
|
1347
|
+
this.hiddenAll = hidden;
|
|
1348
|
+
if (hidden) this.selectedId = null;
|
|
1349
|
+
this.onChange("hidden");
|
|
1350
|
+
}
|
|
1338
1351
|
select(id) {
|
|
1339
1352
|
if (id === this.selectedId) return;
|
|
1340
1353
|
this.selectedId = id;
|
|
@@ -1350,6 +1363,12 @@ var DrawingManager = class {
|
|
|
1350
1363
|
const d = this.drawings.find((x) => x.id === id);
|
|
1351
1364
|
if (!d) return;
|
|
1352
1365
|
Object.assign(d, patch);
|
|
1366
|
+
if ("color" in patch || patch.style) {
|
|
1367
|
+
const { width, dash, fontSize } = d.style ?? {};
|
|
1368
|
+
const style = Object.fromEntries(Object.entries({ width, dash, fontSize }).filter(([, v]) => v !== void 0));
|
|
1369
|
+
this.defaults = { ...this.defaults, [d.type]: { ...d.color ? { color: d.color } : {}, ...Object.keys(style).length ? { style } : {} } };
|
|
1370
|
+
this.onChange("defaults");
|
|
1371
|
+
}
|
|
1353
1372
|
this.onChange("commit");
|
|
1354
1373
|
}
|
|
1355
1374
|
remove(id) {
|
|
@@ -1364,13 +1383,24 @@ var DrawingManager = class {
|
|
|
1364
1383
|
this.draft = null;
|
|
1365
1384
|
this.onChange("commit");
|
|
1366
1385
|
}
|
|
1367
|
-
/** Esc: abort an in-progress drawing, else drop the tool, else deselect. */
|
|
1386
|
+
/** Esc: finish a path that has enough points, else abort an in-progress drawing, else drop the tool, else deselect. */
|
|
1368
1387
|
cancel() {
|
|
1388
|
+
if (this.finishOpen()) return;
|
|
1369
1389
|
if (this.draft || this.tool) this.setTool(null);
|
|
1370
1390
|
else this.select(null);
|
|
1371
1391
|
}
|
|
1392
|
+
/** Finishes an `open` tool's drawing in progress (Enter); false when there is none or too few points. */
|
|
1393
|
+
finishOpen() {
|
|
1394
|
+
const draft = this.draft;
|
|
1395
|
+
const def = draft && this.registry.get(draft.drawing.type);
|
|
1396
|
+
if (!draft || !def?.open || draft.placed.length < Math.max(2, def.clicks)) return false;
|
|
1397
|
+
draft.drawing.points = draft.placed.map((p) => ({ ...p }));
|
|
1398
|
+
this.finish();
|
|
1399
|
+
return true;
|
|
1400
|
+
}
|
|
1372
1401
|
// ── Creation ──────────────────────────────────────────────────────
|
|
1373
1402
|
expand(def, placed, c) {
|
|
1403
|
+
if (def.open) return placed.map((p) => ({ ...p }));
|
|
1374
1404
|
const full = Array.from({ length: def.clicks }, (_, i) => ({ ...placed[i] ?? placed[placed.length - 1] }));
|
|
1375
1405
|
return def.create ? def.create(full, c) : full;
|
|
1376
1406
|
}
|
|
@@ -1387,11 +1417,28 @@ var DrawingManager = class {
|
|
|
1387
1417
|
}
|
|
1388
1418
|
if (!this.draft) {
|
|
1389
1419
|
const placed = [point];
|
|
1390
|
-
|
|
1391
|
-
|
|
1420
|
+
const last = this.defaults[def.type];
|
|
1421
|
+
this.draft = {
|
|
1422
|
+
drawing: { id: newId("d"), type: def.type, points: this.expand(def, placed, c), ...last?.color ? { color: last.color } : {}, ...last?.style ? { style: { ...last.style } } : {} },
|
|
1423
|
+
placed
|
|
1424
|
+
};
|
|
1425
|
+
if (def.clicks <= 1 && !def.open) this.finish();
|
|
1392
1426
|
else this.onChange("preview");
|
|
1393
1427
|
return;
|
|
1394
1428
|
}
|
|
1429
|
+
if (def.open) {
|
|
1430
|
+
const near = (p) => Math.hypot(c.timeToX(p.time) - x, c.priceToY(p.price) - y) <= CLOSE_PX;
|
|
1431
|
+
const placed = this.draft.placed;
|
|
1432
|
+
if (placed.length >= Math.max(2, def.clicks) && (near(placed[placed.length - 1]) || near(placed[0]))) {
|
|
1433
|
+
this.finishOpen();
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (near(placed[placed.length - 1])) return;
|
|
1437
|
+
placed.push(point);
|
|
1438
|
+
this.draft.drawing.points = [...placed.map((p) => ({ ...p })), { ...point }];
|
|
1439
|
+
this.onChange("preview");
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1395
1442
|
this.draft.placed.push(point);
|
|
1396
1443
|
this.draft.drawing.points = this.expand(def, this.draft.placed, c);
|
|
1397
1444
|
if (this.draft.placed.length >= def.clicks) this.finish();
|
|
@@ -1410,7 +1457,7 @@ var DrawingManager = class {
|
|
|
1410
1457
|
this.onChange("preview");
|
|
1411
1458
|
return;
|
|
1412
1459
|
}
|
|
1413
|
-
draft.drawing.points = this.expand(def, [...draft.placed, c.pointAt(x, y)], c);
|
|
1460
|
+
draft.drawing.points = def.open ? [...draft.placed.map((p) => ({ ...p })), c.pointAt(x, y)] : this.expand(def, [...draft.placed, c.pointAt(x, y)], c);
|
|
1414
1461
|
this.onChange("preview");
|
|
1415
1462
|
}
|
|
1416
1463
|
/** Pointer released: a drag since the first point places the next one (drag-to-draw). */
|
|
@@ -1426,6 +1473,10 @@ var DrawingManager = class {
|
|
|
1426
1473
|
const drawing = this.draft.drawing;
|
|
1427
1474
|
this.draft = null;
|
|
1428
1475
|
this.drawings.push(drawing);
|
|
1476
|
+
if (this.keepTool) {
|
|
1477
|
+
this.onChange("commit");
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1429
1480
|
this.selectedId = drawing.id;
|
|
1430
1481
|
this.tool = null;
|
|
1431
1482
|
this.onChange("tool");
|
|
@@ -1446,8 +1497,10 @@ var DrawingManager = class {
|
|
|
1446
1497
|
const handle = this.handles(c, selected).findIndex(([px, py]) => Math.hypot(px - x, py - y) <= HANDLE_RADIUS + tolerance);
|
|
1447
1498
|
if (handle >= 0) return { kind: "drawing", id: selected.id, part: handle };
|
|
1448
1499
|
}
|
|
1500
|
+
if (this.hiddenAll) return null;
|
|
1449
1501
|
for (let i = this.drawings.length - 1; i >= 0; i--) {
|
|
1450
1502
|
const d = this.drawings[i];
|
|
1503
|
+
if (d.style?.hidden) continue;
|
|
1451
1504
|
const def = this.registry.get(d.type);
|
|
1452
1505
|
if (def && def.distance(this.geometry(c, d), x, y) <= tolerance) return { kind: "drawing", id: d.id, part: "body" };
|
|
1453
1506
|
}
|
|
@@ -1481,7 +1534,8 @@ var DrawingManager = class {
|
|
|
1481
1534
|
}
|
|
1482
1535
|
// ── Rendering ─────────────────────────────────────────────────────
|
|
1483
1536
|
render(rc, badges, bars = [], barIndexAt = () => -1) {
|
|
1484
|
-
const
|
|
1537
|
+
const shown = this.hiddenAll ? [] : this.drawings.filter((d) => !d.style?.hidden);
|
|
1538
|
+
const all = this.draft ? [...shown, this.draft.drawing] : shown;
|
|
1485
1539
|
const c = rc.coords;
|
|
1486
1540
|
for (const d of all) {
|
|
1487
1541
|
const def = this.registry.get(d.type);
|
|
@@ -3030,6 +3084,9 @@ var Chart = class {
|
|
|
3030
3084
|
this.recordHistory();
|
|
3031
3085
|
}
|
|
3032
3086
|
if (change === "tool") this.emitter.emit("drawingToolChange", this.drawings.tool);
|
|
3087
|
+
if (change === "hidden") this.emitter.emit("drawingsHiddenChange", this.drawings.hiddenAll);
|
|
3088
|
+
if (change === "defaults") this.emitter.emit("drawingDefaultsChange", { ...this.drawings.defaults });
|
|
3089
|
+
if (change === "defaults") return;
|
|
3033
3090
|
if (change !== "preview") {
|
|
3034
3091
|
const d = this.drawings.selected;
|
|
3035
3092
|
this.emitter.emit("selectionChange", d ? { ...d, points: d.points.map((p) => ({ ...p })) } : null);
|
|
@@ -3487,6 +3544,30 @@ var Chart = class {
|
|
|
3487
3544
|
get drawingTool() {
|
|
3488
3545
|
return this.drawings.tool;
|
|
3489
3546
|
}
|
|
3547
|
+
/** Hides every drawing (not drawn, not clickable) or shows them again. Arming a tool shows them. */
|
|
3548
|
+
setDrawingsHidden(hidden) {
|
|
3549
|
+
this.drawings.setHiddenAll(hidden);
|
|
3550
|
+
}
|
|
3551
|
+
get drawingsHidden() {
|
|
3552
|
+
return this.drawings.hiddenAll;
|
|
3553
|
+
}
|
|
3554
|
+
/** 연속 그리기: the armed tool stays armed after each drawing (Esc or the cursor drops it). */
|
|
3555
|
+
setKeepDrawingTool(on) {
|
|
3556
|
+
if (on === this.drawings.keepTool) return;
|
|
3557
|
+
this.drawings.keepTool = on;
|
|
3558
|
+
this.emitter.emit("keepDrawingToolChange", on);
|
|
3559
|
+
}
|
|
3560
|
+
get keepDrawingTool() {
|
|
3561
|
+
return this.drawings.keepTool;
|
|
3562
|
+
}
|
|
3563
|
+
/** Each tool's last color / line / text style; new drawings of a tool start with it. */
|
|
3564
|
+
getDrawingDefaults() {
|
|
3565
|
+
return JSON.parse(JSON.stringify(this.drawings.defaults));
|
|
3566
|
+
}
|
|
3567
|
+
/** Restores them (e.g. from storage). Doesn't fire drawingDefaultsChange. */
|
|
3568
|
+
setDrawingDefaults(defaults) {
|
|
3569
|
+
this.drawings.defaults = JSON.parse(JSON.stringify(defaults ?? {}));
|
|
3570
|
+
}
|
|
3490
3571
|
/** Magnet mode: drawing points snap to the nearest open/high/low/close of the bar. */
|
|
3491
3572
|
setMagnet(on) {
|
|
3492
3573
|
if (on === this.magnet) return;
|
|
@@ -4673,6 +4754,8 @@ var Chart = class {
|
|
|
4673
4754
|
this.setReplaySelecting(false);
|
|
4674
4755
|
} else if (e.key === "Escape" && (this.drawings.tool || this.drawings.selectedId)) {
|
|
4675
4756
|
this.drawings.cancel();
|
|
4757
|
+
} else if (e.key === "Enter" && this.drawings.finishOpen()) {
|
|
4758
|
+
e.preventDefault();
|
|
4676
4759
|
} else if ((e.key === "Delete" || e.key === "Backspace") && this.drawings.selectedId) {
|
|
4677
4760
|
e.preventDefault();
|
|
4678
4761
|
this.drawings.remove(this.drawings.selectedId);
|
|
@@ -24,6 +24,8 @@ export interface DrawingStyle {
|
|
|
24
24
|
fontSize?: number;
|
|
25
25
|
/** Can't be dragged or edited until unlocked (still selectable). */
|
|
26
26
|
locked?: boolean;
|
|
27
|
+
/** Not drawn and can't be hit (a drawings list can show it again). */
|
|
28
|
+
hidden?: boolean;
|
|
27
29
|
/** Colors of the tool's parts (`DrawingDefinition.colors` keys), where set apart from the rest. */
|
|
28
30
|
colors?: Record<string, string>;
|
|
29
31
|
}
|
|
@@ -89,8 +91,13 @@ export interface DrawingDefinition {
|
|
|
89
91
|
* are listed under it, with a divider between sections. Default: no heading.
|
|
90
92
|
*/
|
|
91
93
|
section?: Record<Locale, string>;
|
|
92
|
-
/** Clicks (or drag ends) needed to place it. */
|
|
94
|
+
/** Clicks (or drag ends) needed to place it; with `open`, the fewest it takes. */
|
|
93
95
|
clicks: number;
|
|
96
|
+
/**
|
|
97
|
+
* Placed point by point (a path, a polygon): every click adds one, until a double-click (a
|
|
98
|
+
* click on the last point), a click on the first point, Enter or Esc finishes it.
|
|
99
|
+
*/
|
|
100
|
+
open?: boolean;
|
|
94
101
|
/**
|
|
95
102
|
* Turns the placed click points into the stored points, e.g. a position tool that creates
|
|
96
103
|
* entry/target/stop from one click. Called for the live preview too. Default: as placed.
|
|
@@ -119,7 +126,12 @@ export declare function distToSegment(px: number, py: number, x1: number, y1: nu
|
|
|
119
126
|
export declare const BUILTIN_DRAWINGS: readonly DrawingDefinition[];
|
|
120
127
|
/** Built-ins plus definitions passed to the chart; later ones replace earlier ones of the same type. */
|
|
121
128
|
export declare function drawingRegistry(extra?: readonly DrawingDefinition[]): Map<string, DrawingDefinition>;
|
|
122
|
-
export type DrawingsChange = 'preview' | 'commit' | 'tool' | 'selection';
|
|
129
|
+
export type DrawingsChange = 'preview' | 'commit' | 'tool' | 'selection' | 'defaults' | 'hidden';
|
|
130
|
+
/** The last color and line / text style used per tool type: new drawings of that type start with it. */
|
|
131
|
+
export type DrawingDefaults = Record<string, {
|
|
132
|
+
color?: string;
|
|
133
|
+
style?: Pick<DrawingStyle, 'width' | 'dash' | 'fontSize'>;
|
|
134
|
+
}>;
|
|
123
135
|
/**
|
|
124
136
|
* Owns drawings: creation (click-click or drag), selection, handle/body dragging,
|
|
125
137
|
* hit testing and rendering. Pure state + canvas; pointer routing lives in Interaction.
|
|
@@ -131,20 +143,28 @@ export declare class DrawingManager {
|
|
|
131
143
|
drawings: Drawing[];
|
|
132
144
|
tool: DrawingType | null;
|
|
133
145
|
selectedId: string | null;
|
|
146
|
+
/** Every drawing hidden at once (arming a tool shows them again). */
|
|
147
|
+
hiddenAll: boolean;
|
|
148
|
+
/** The tool stays armed after a drawing is placed (연속 그리기). */
|
|
149
|
+
keepTool: boolean;
|
|
150
|
+
defaults: DrawingDefaults;
|
|
134
151
|
private draft;
|
|
135
152
|
private freehandLast;
|
|
136
153
|
private drag;
|
|
137
154
|
constructor(onChange: (change: DrawingsChange) => void, registry?: Map<string, DrawingDefinition>);
|
|
138
155
|
get selected(): Drawing | null;
|
|
139
156
|
setTool(tool: DrawingType | null): void;
|
|
157
|
+
setHiddenAll(hidden: boolean): void;
|
|
140
158
|
select(id: string | null): void;
|
|
141
159
|
/** Drawings of unknown types are kept (and saved) but not shown. */
|
|
142
160
|
set(drawings: readonly Drawing[]): void;
|
|
143
161
|
update(id: string, patch: Partial<Omit<Drawing, 'id'>>): void;
|
|
144
162
|
remove(id: string): void;
|
|
145
163
|
clear(): void;
|
|
146
|
-
/** Esc: abort an in-progress drawing, else drop the tool, else deselect. */
|
|
164
|
+
/** Esc: finish a path that has enough points, else abort an in-progress drawing, else drop the tool, else deselect. */
|
|
147
165
|
cancel(): void;
|
|
166
|
+
/** Finishes an `open` tool's drawing in progress (Enter); false when there is none or too few points. */
|
|
167
|
+
finishOpen(): boolean;
|
|
148
168
|
private expand;
|
|
149
169
|
createAt(c: PaneCoords, x: number, y: number): void;
|
|
150
170
|
/** Moves the not-yet-placed points (live preview while hovering or dragging). */
|