@baron1996/klinecharts-adapter 0.1.1 → 0.2.1
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/adapter.d.ts +40 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +407 -21
- package/dist/conversion/overlays.d.ts +3 -0
- package/dist/conversion/overlays.d.ts.map +1 -1
- package/dist/conversion/overlays.js +2 -0
- package/dist/conversion/panes.d.ts +3 -1
- package/dist/conversion/panes.d.ts.map +1 -1
- package/dist/conversion/panes.js +7 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -3
- package/dist/extensions/price-measurement.d.ts +13 -0
- package/dist/extensions/price-measurement.d.ts.map +1 -0
- package/dist/extensions/price-measurement.js +64 -0
- package/dist/extensions/register.d.ts.map +1 -1
- package/dist/extensions/register.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/interaction/dragging.d.ts +12 -0
- package/dist/interaction/dragging.d.ts.map +1 -0
- package/dist/interaction/dragging.js +71 -0
- package/dist/interaction/hit-testing.d.ts +26 -0
- package/dist/interaction/hit-testing.d.ts.map +1 -0
- package/dist/interaction/hit-testing.js +86 -0
- package/dist/interaction/selection-arbitration.d.ts +6 -0
- package/dist/interaction/selection-arbitration.d.ts.map +1 -0
- package/dist/interaction/selection-arbitration.js +10 -0
- package/dist/registry/overlays.d.ts +2 -2
- package/dist/registry/overlays.d.ts.map +1 -1
- package/dist/registry/overlays.js +1 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +3 -2
- package/package.json +2 -2
package/dist/adapter.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ChartScene, SceneIndicator, SceneOverlay } from '@baron1996/kline-scene-schema';
|
|
2
2
|
import { SceneError } from '@baron1996/kline-scene-schema';
|
|
3
3
|
import { type OverlayDrawingSource } from './conversion/overlays.js';
|
|
4
|
+
import { type OverlayHitResult, type PixelCoordinate } from './interaction/hit-testing.js';
|
|
5
|
+
export type PriceScale = 'linear' | 'logarithmic';
|
|
6
|
+
export type AdapterDragTarget = 'body' | 'anchor';
|
|
7
|
+
export type AdapterDragCancelReason = 'escape' | 'pointer-cancel' | 'window-blur' | 'destroy' | 'validation-error';
|
|
4
8
|
export interface AdapterIndicatorSnapshot {
|
|
5
9
|
readonly id: string;
|
|
6
10
|
readonly name: SceneIndicator['name'];
|
|
@@ -9,6 +13,7 @@ export interface AdapterIndicatorSnapshot {
|
|
|
9
13
|
}
|
|
10
14
|
export interface AdapterSnapshot {
|
|
11
15
|
readonly engineVersion: string;
|
|
16
|
+
readonly runtimeVersion: ChartScene['runtime']['runtimeVersion'];
|
|
12
17
|
readonly dataCount: number;
|
|
13
18
|
readonly paneIds: readonly string[];
|
|
14
19
|
readonly indicators: readonly AdapterIndicatorSnapshot[];
|
|
@@ -16,19 +21,45 @@ export interface AdapterSnapshot {
|
|
|
16
21
|
readonly barSpace: number;
|
|
17
22
|
readonly rightOffsetDistance: number;
|
|
18
23
|
}
|
|
24
|
+
interface AdapterDragEventIdentity {
|
|
25
|
+
readonly interactionId: string;
|
|
26
|
+
readonly overlayId: string;
|
|
27
|
+
readonly target: AdapterDragTarget;
|
|
28
|
+
readonly anchorIndex: number | null;
|
|
29
|
+
readonly before: SceneOverlay;
|
|
30
|
+
}
|
|
19
31
|
export type AdapterSceneEvent = {
|
|
20
32
|
readonly type: 'overlay-created';
|
|
21
33
|
readonly overlay: SceneOverlay;
|
|
22
34
|
} | {
|
|
23
35
|
readonly type: 'overlay-updated';
|
|
24
36
|
readonly overlay: SceneOverlay;
|
|
37
|
+
} | {
|
|
38
|
+
readonly type: 'overlay-style-changed';
|
|
39
|
+
readonly before: SceneOverlay;
|
|
40
|
+
readonly overlay: SceneOverlay;
|
|
25
41
|
} | {
|
|
26
42
|
readonly type: 'overlay-removed';
|
|
27
43
|
readonly id: string;
|
|
44
|
+
} | {
|
|
45
|
+
readonly type: 'overlay-selection-changed';
|
|
46
|
+
readonly previousId: string | null;
|
|
47
|
+
readonly id: string | null;
|
|
28
48
|
} | {
|
|
29
49
|
readonly type: 'overlay-selected';
|
|
30
50
|
readonly id: string;
|
|
31
|
-
} | {
|
|
51
|
+
} | ({
|
|
52
|
+
readonly type: 'overlay-drag-started';
|
|
53
|
+
} & AdapterDragEventIdentity) | ({
|
|
54
|
+
readonly type: 'overlay-dragging';
|
|
55
|
+
readonly candidate: SceneOverlay;
|
|
56
|
+
} & AdapterDragEventIdentity) | ({
|
|
57
|
+
readonly type: 'overlay-drag-committed';
|
|
58
|
+
readonly overlay: SceneOverlay;
|
|
59
|
+
} & AdapterDragEventIdentity) | ({
|
|
60
|
+
readonly type: 'overlay-drag-cancelled';
|
|
61
|
+
readonly reason: AdapterDragCancelReason;
|
|
62
|
+
} & AdapterDragEventIdentity) | {
|
|
32
63
|
readonly type: 'scene-error';
|
|
33
64
|
readonly issues: readonly SceneError['issues'][number][];
|
|
34
65
|
};
|
|
@@ -45,8 +76,15 @@ export declare class KLineChartsSceneAdapter {
|
|
|
45
76
|
static create(container: HTMLElement, value: unknown): Promise<KLineChartsSceneAdapter>;
|
|
46
77
|
subscribe(listener: AdapterSceneEventListener): () => void;
|
|
47
78
|
exportScene(): ChartScene;
|
|
79
|
+
setPriceScale(scale: PriceScale): Promise<ChartScene>;
|
|
80
|
+
projectPoint(point: {
|
|
81
|
+
readonly timestamp: number;
|
|
82
|
+
readonly value: number;
|
|
83
|
+
}, paneId?: string): PixelCoordinate;
|
|
84
|
+
hitTestOverlay(point: PixelCoordinate): OverlayHitResult | null;
|
|
48
85
|
addOverlay(value: SceneOverlay): SceneOverlay;
|
|
49
86
|
updateOverlay(value: SceneOverlay): SceneOverlay;
|
|
87
|
+
updateOverlayStyles(id: string, styles: SceneOverlay['styles']): SceneOverlay;
|
|
50
88
|
removeOverlay(id: string): boolean;
|
|
51
89
|
startOverlayDrawing(request: OverlayDrawingRequest): string;
|
|
52
90
|
getOverlay(id: string): SceneOverlay | undefined;
|
|
@@ -54,4 +92,5 @@ export declare class KLineChartsSceneAdapter {
|
|
|
54
92
|
inspect(): AdapterSnapshot;
|
|
55
93
|
dispose(): void;
|
|
56
94
|
}
|
|
95
|
+
export {};
|
|
57
96
|
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,cAAc,EACd,YAAY,EACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEN,UAAU,EACV,MAAM,+BAA+B,CAAC;AAUvC,OAAO,EAIN,KAAK,oBAAoB,EAGzB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter.js
CHANGED
|
@@ -2,9 +2,28 @@ import { parseChartScene, SceneError, } from '@baron1996/kline-scene-schema';
|
|
|
2
2
|
import { createEngine } from './engine.js';
|
|
3
3
|
import { registerProjectOverlays } from './extensions/register.js';
|
|
4
4
|
import { createEngineIdMap, } from './conversion/id-map.js';
|
|
5
|
-
import { applyPanes } from './conversion/panes.js';
|
|
5
|
+
import { applyPanes, overrideSceneYAxis } from './conversion/panes.js';
|
|
6
6
|
import { createSceneOverlays, fromEngineOverlay, toEngineOverlay, toEngineOverlayDrawing, } from './conversion/overlays.js';
|
|
7
7
|
import { applyViewport } from './conversion/viewport.js';
|
|
8
|
+
import { createDragCandidate, } from './interaction/dragging.js';
|
|
9
|
+
import { hitTestOverlayGeometries, } from './interaction/hit-testing.js';
|
|
10
|
+
import { shouldIgnoreStaleOverlayDeselection } from './interaction/selection-arbitration.js';
|
|
11
|
+
function promoteSceneToM2(scene, scale) {
|
|
12
|
+
const candidate = structuredClone(scene);
|
|
13
|
+
candidate.runtime.runtimeVersion = '0.2.0';
|
|
14
|
+
for (const pane of candidate.panes) {
|
|
15
|
+
for (const axis of pane.yAxes) {
|
|
16
|
+
axis.scale =
|
|
17
|
+
pane.kind === 'candle' && axis.role === 'primary' && scale !== undefined
|
|
18
|
+
? scale
|
|
19
|
+
: axis.scale ?? 'linear';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
function isControlledInteractionOverlay(overlay) {
|
|
25
|
+
return overlay.type === 'horizontalStraightLine' || overlay.type === 'priceMeasurement';
|
|
26
|
+
}
|
|
8
27
|
/**
|
|
9
28
|
* ChartScene 与 KLineCharts 之间的唯一边界。
|
|
10
29
|
* 引擎对象和内部 ID 永不从该类的公共接口泄露。
|
|
@@ -16,7 +35,7 @@ export class KLineChartsSceneAdapter {
|
|
|
16
35
|
#engine;
|
|
17
36
|
/** 场景 ID 与引擎内部 ID 的双向映射。 */
|
|
18
37
|
#idMap;
|
|
19
|
-
/**
|
|
38
|
+
/** 当前最后一次成功提交、可导出的规范化场景。 */
|
|
20
39
|
#scene;
|
|
21
40
|
/** 当前引擎容器。 */
|
|
22
41
|
#container;
|
|
@@ -26,6 +45,12 @@ export class KLineChartsSceneAdapter {
|
|
|
26
45
|
#disposed = false;
|
|
27
46
|
/** 仅传递纯场景数据的事件订阅者。 */
|
|
28
47
|
#listeners = new Set();
|
|
48
|
+
/** 当前选择状态,null 表示明确未选择。 */
|
|
49
|
+
#selectedOverlayId = null;
|
|
50
|
+
/** 当前受控拖动事务;progress 永不写入 #scene。 */
|
|
51
|
+
#pointerInteraction;
|
|
52
|
+
/** 确定性 opaque 交互 ID 序号。 */
|
|
53
|
+
#interactionSequence = 0;
|
|
29
54
|
constructor(container, scene, handle, idMap, originalBackground) {
|
|
30
55
|
this.#container = container;
|
|
31
56
|
this.#scene = scene;
|
|
@@ -33,6 +58,7 @@ export class KLineChartsSceneAdapter {
|
|
|
33
58
|
this.#engine = handle.module;
|
|
34
59
|
this.#idMap = idMap;
|
|
35
60
|
this.#originalBackground = originalBackground;
|
|
61
|
+
this.#installInteractionListeners();
|
|
36
62
|
}
|
|
37
63
|
static async create(container, value) {
|
|
38
64
|
const scene = parseChartScene(value);
|
|
@@ -75,6 +101,17 @@ export class KLineChartsSceneAdapter {
|
|
|
75
101
|
listener(structuredClone(event));
|
|
76
102
|
}
|
|
77
103
|
}
|
|
104
|
+
#selectOverlay(id) {
|
|
105
|
+
const previousId = this.#selectedOverlayId;
|
|
106
|
+
if (previousId === id) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.#selectedOverlayId = id;
|
|
110
|
+
this.#emit({ type: 'overlay-selection-changed', previousId, id });
|
|
111
|
+
if (id !== null) {
|
|
112
|
+
this.#emit({ type: 'overlay-selected', id });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
78
115
|
#commitEngineOverlay(engineOverlay, source, kind) {
|
|
79
116
|
const existingIndex = this.#scene.overlays.findIndex((overlay) => overlay.id === source.id);
|
|
80
117
|
const path = existingIndex < 0 ? `/overlays/${this.#scene.overlays.length}` : `/overlays/${existingIndex}`;
|
|
@@ -101,11 +138,28 @@ export class KLineChartsSceneAdapter {
|
|
|
101
138
|
onDrawEnd: ({ overlay }) => {
|
|
102
139
|
this.#safelyCommitEngineOverlay(overlay, source, drawing ? 'created' : 'updated');
|
|
103
140
|
},
|
|
141
|
+
onPressedMoveStart: ({ overlay }) => {
|
|
142
|
+
this.#selectOverlay(overlay.id);
|
|
143
|
+
},
|
|
104
144
|
onPressedMoveEnd: ({ overlay }) => {
|
|
105
|
-
|
|
145
|
+
if (!isControlledInteractionOverlay(source)) {
|
|
146
|
+
this.#safelyCommitEngineOverlay(overlay, source, 'updated');
|
|
147
|
+
}
|
|
106
148
|
},
|
|
107
149
|
onSelected: ({ overlay }) => {
|
|
108
|
-
this.#
|
|
150
|
+
this.#selectOverlay(overlay.id);
|
|
151
|
+
},
|
|
152
|
+
onDeselected: (event) => {
|
|
153
|
+
const eventX = event.x;
|
|
154
|
+
const eventY = event.y;
|
|
155
|
+
const coordinate = typeof eventX === 'number' && Number.isFinite(eventX) &&
|
|
156
|
+
typeof eventY === 'number' && Number.isFinite(eventY)
|
|
157
|
+
? { x: eventX, y: eventY }
|
|
158
|
+
: undefined;
|
|
159
|
+
if (this.#selectedOverlayId === event.overlay.id &&
|
|
160
|
+
!shouldIgnoreStaleOverlayDeselection(this.#selectedOverlayId, event.overlay.id, coordinate, this.#overlayGeometries())) {
|
|
161
|
+
this.#selectOverlay(null);
|
|
162
|
+
}
|
|
109
163
|
},
|
|
110
164
|
onRemoved: ({ overlay }) => {
|
|
111
165
|
if (this.#scene.overlays.some((candidate) => candidate.id === overlay.id)) {
|
|
@@ -113,6 +167,9 @@ export class KLineChartsSceneAdapter {
|
|
|
113
167
|
...structuredClone(this.#scene),
|
|
114
168
|
overlays: this.#scene.overlays.filter((candidate) => candidate.id !== overlay.id),
|
|
115
169
|
});
|
|
170
|
+
if (this.#selectedOverlayId === overlay.id) {
|
|
171
|
+
this.#selectOverlay(null);
|
|
172
|
+
}
|
|
116
173
|
this.#emit({ type: 'overlay-removed', id: overlay.id });
|
|
117
174
|
}
|
|
118
175
|
},
|
|
@@ -130,6 +187,272 @@ export class KLineChartsSceneAdapter {
|
|
|
130
187
|
throw error;
|
|
131
188
|
}
|
|
132
189
|
}
|
|
190
|
+
#installInteractionListeners() {
|
|
191
|
+
this.#container.addEventListener('pointerdown', this.#handlePointerDown, true);
|
|
192
|
+
this.#container.addEventListener('pointermove', this.#handlePointerMove, true);
|
|
193
|
+
this.#container.addEventListener('pointerup', this.#handlePointerUp, true);
|
|
194
|
+
this.#container.addEventListener('pointercancel', this.#handlePointerCancel, true);
|
|
195
|
+
window.addEventListener('keydown', this.#handleKeyDown);
|
|
196
|
+
window.addEventListener('blur', this.#handleWindowBlur);
|
|
197
|
+
}
|
|
198
|
+
#removeInteractionListeners() {
|
|
199
|
+
this.#container.removeEventListener('pointerdown', this.#handlePointerDown, true);
|
|
200
|
+
this.#container.removeEventListener('pointermove', this.#handlePointerMove, true);
|
|
201
|
+
this.#container.removeEventListener('pointerup', this.#handlePointerUp, true);
|
|
202
|
+
this.#container.removeEventListener('pointercancel', this.#handlePointerCancel, true);
|
|
203
|
+
window.removeEventListener('keydown', this.#handleKeyDown);
|
|
204
|
+
window.removeEventListener('blur', this.#handleWindowBlur);
|
|
205
|
+
}
|
|
206
|
+
#pointerCoordinate(event) {
|
|
207
|
+
const rect = this.#container.getBoundingClientRect();
|
|
208
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
209
|
+
}
|
|
210
|
+
#primaryAxisFilter(paneId) {
|
|
211
|
+
const pane = this.#scene.panes.find((candidate) => candidate.id === paneId);
|
|
212
|
+
const axis = pane?.yAxes.find((candidate) => candidate.role === 'primary');
|
|
213
|
+
const enginePaneId = this.#idMap.paneToEngine.get(paneId);
|
|
214
|
+
const engineAxisId = axis === undefined ? undefined : this.#idMap.yAxisToEngine.get(axis.id);
|
|
215
|
+
if (enginePaneId === undefined || engineAxisId === undefined) {
|
|
216
|
+
throw new SceneError('INVALID_REFERENCE', '/panes', 'Overlay Pane or primary Y-axis is unmapped.');
|
|
217
|
+
}
|
|
218
|
+
return { paneId: enginePaneId, yAxisId: engineAxisId, absolute: true };
|
|
219
|
+
}
|
|
220
|
+
#toPixel(point, paneId) {
|
|
221
|
+
const converted = this.#chart.convertToPixel(point, this.#primaryAxisFilter(paneId));
|
|
222
|
+
if (!Number.isFinite(converted.x) || !Number.isFinite(converted.y)) {
|
|
223
|
+
throw new SceneError('EXPORT_INVALID', '/overlays', 'KLineCharts returned a non-finite pixel coordinate.');
|
|
224
|
+
}
|
|
225
|
+
return { x: converted.x, y: converted.y };
|
|
226
|
+
}
|
|
227
|
+
#fromPixel(point, paneId) {
|
|
228
|
+
const converted = this.#chart.convertFromPixel([point], this.#primaryAxisFilter(paneId));
|
|
229
|
+
const value = converted[0];
|
|
230
|
+
if (!Number.isFinite(value?.dataIndex) || !Number.isFinite(value?.value)) {
|
|
231
|
+
throw new SceneError('INVALID_REFERENCE', '/overlays', 'Pointer does not map to finite chart data.');
|
|
232
|
+
}
|
|
233
|
+
return { dataIndex: value.dataIndex, value: value.value };
|
|
234
|
+
}
|
|
235
|
+
#overlayGeometries() {
|
|
236
|
+
const geometries = [];
|
|
237
|
+
for (let sceneIndex = 0; sceneIndex < this.#scene.overlays.length; sceneIndex++) {
|
|
238
|
+
const overlay = this.#scene.overlays[sceneIndex];
|
|
239
|
+
if (overlay === undefined || !overlay.visible) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (overlay.type === 'horizontalStraightLine') {
|
|
243
|
+
const anchor = overlay.anchor;
|
|
244
|
+
if (anchor === undefined || !('value' in anchor)) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const paneFilter = this.#primaryAxisFilter(overlay.paneId);
|
|
248
|
+
const paneMain = this.#chart.getDom(paneFilter.paneId, 'main');
|
|
249
|
+
const containerRect = this.#container.getBoundingClientRect();
|
|
250
|
+
const mainRect = paneMain?.getBoundingClientRect() ?? containerRect;
|
|
251
|
+
const projected = this.#toPixel({ timestamp: this.#scene.data[0].timestamp, value: anchor.value }, overlay.paneId);
|
|
252
|
+
const start = { x: mainRect.left - containerRect.left, y: projected.y };
|
|
253
|
+
const end = { x: mainRect.right - containerRect.left, y: projected.y };
|
|
254
|
+
geometries.push({
|
|
255
|
+
overlayId: overlay.id,
|
|
256
|
+
sceneIndex,
|
|
257
|
+
zLevel: overlay.zLevel,
|
|
258
|
+
locked: overlay.locked,
|
|
259
|
+
anchors: [{ x: (start.x + end.x) / 2, y: projected.y }],
|
|
260
|
+
bodySegments: [[start, end]],
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (overlay.type === 'priceMeasurement' && overlay.start !== undefined && overlay.end !== undefined) {
|
|
265
|
+
const start = this.#toPixel(overlay.start, overlay.paneId);
|
|
266
|
+
const end = this.#toPixel(overlay.end, overlay.paneId);
|
|
267
|
+
geometries.push({
|
|
268
|
+
overlayId: overlay.id,
|
|
269
|
+
sceneIndex,
|
|
270
|
+
zLevel: overlay.zLevel,
|
|
271
|
+
locked: overlay.locked,
|
|
272
|
+
anchors: [start, end],
|
|
273
|
+
bodySegments: [[start, end]],
|
|
274
|
+
});
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (overlay.points !== undefined && overlay.points.length >= 2) {
|
|
278
|
+
const anchors = overlay.points.map((point) => this.#toPixel(point, overlay.paneId));
|
|
279
|
+
const bodySegments = [];
|
|
280
|
+
for (let pointIndex = 1; pointIndex < anchors.length; pointIndex++) {
|
|
281
|
+
bodySegments.push([anchors[pointIndex - 1], anchors[pointIndex]]);
|
|
282
|
+
}
|
|
283
|
+
geometries.push({
|
|
284
|
+
overlayId: overlay.id,
|
|
285
|
+
sceneIndex,
|
|
286
|
+
zLevel: overlay.zLevel,
|
|
287
|
+
locked: overlay.locked,
|
|
288
|
+
anchors,
|
|
289
|
+
bodySegments,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return geometries;
|
|
294
|
+
}
|
|
295
|
+
#interactionIdentity(interaction) {
|
|
296
|
+
return {
|
|
297
|
+
interactionId: interaction.interactionId,
|
|
298
|
+
overlayId: interaction.hit.overlayId,
|
|
299
|
+
target: interaction.hit.target,
|
|
300
|
+
anchorIndex: interaction.hit.anchorIndex,
|
|
301
|
+
before: structuredClone(interaction.before),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
#stopPointerCapture(interaction) {
|
|
305
|
+
if (this.#container.hasPointerCapture(interaction.pointerId)) {
|
|
306
|
+
this.#container.releasePointerCapture(interaction.pointerId);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
#cancelInteraction(reason, error) {
|
|
310
|
+
const interaction = this.#pointerInteraction;
|
|
311
|
+
if (interaction === undefined) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
this.#pointerInteraction = undefined;
|
|
315
|
+
this.#stopPointerCapture(interaction);
|
|
316
|
+
if (!interaction.started) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const index = this.#scene.overlays.findIndex((overlay) => overlay.id === interaction.before.id);
|
|
320
|
+
if (index < 0 ||
|
|
321
|
+
!this.#chart.overrideOverlay(toEngineOverlay(interaction.before, this.#idMap, `/overlays/${index}`, this.#overlayCallbacks(interaction.before)))) {
|
|
322
|
+
throw new SceneError('RUNTIME_INIT_FAILED', '/overlays', `KLineCharts failed to restore Overlay ${interaction.before.id}.`);
|
|
323
|
+
}
|
|
324
|
+
this.#emit({
|
|
325
|
+
type: 'overlay-drag-cancelled',
|
|
326
|
+
...this.#interactionIdentity(interaction),
|
|
327
|
+
reason,
|
|
328
|
+
});
|
|
329
|
+
if (reason === 'validation-error' && error !== undefined) {
|
|
330
|
+
this.#emit({ type: 'scene-error', issues: structuredClone(error.issues) });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
#handlePointerDown = (event) => {
|
|
334
|
+
if (this.#disposed || event.button !== 0 || this.#pointerInteraction !== undefined) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const coordinate = this.#pointerCoordinate(event);
|
|
338
|
+
const hit = hitTestOverlayGeometries(coordinate, this.#overlayGeometries());
|
|
339
|
+
if (hit === null) {
|
|
340
|
+
const selected = this.#scene.overlays.find((overlay) => overlay.id === this.#selectedOverlayId);
|
|
341
|
+
if (selected !== undefined && isControlledInteractionOverlay(selected)) {
|
|
342
|
+
this.#selectOverlay(null);
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const before = this.#scene.overlays.find((overlay) => overlay.id === hit.overlayId);
|
|
347
|
+
if (before === undefined) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
this.#selectOverlay(before.id);
|
|
351
|
+
if (!isControlledInteractionOverlay(before)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
event.preventDefault();
|
|
355
|
+
event.stopImmediatePropagation();
|
|
356
|
+
if (hit.locked) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
this.#container.setPointerCapture(event.pointerId);
|
|
360
|
+
this.#pointerInteraction = {
|
|
361
|
+
pointerId: event.pointerId,
|
|
362
|
+
originClient: coordinate,
|
|
363
|
+
originData: this.#fromPixel(coordinate, before.paneId),
|
|
364
|
+
hit,
|
|
365
|
+
before: structuredClone(before),
|
|
366
|
+
interactionId: `interaction-${this.#interactionSequence++}`,
|
|
367
|
+
started: false,
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
#handlePointerMove = (event) => {
|
|
371
|
+
const interaction = this.#pointerInteraction;
|
|
372
|
+
if (interaction === undefined || interaction.pointerId !== event.pointerId) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
event.preventDefault();
|
|
376
|
+
event.stopImmediatePropagation();
|
|
377
|
+
const coordinate = this.#pointerCoordinate(event);
|
|
378
|
+
if (!interaction.started &&
|
|
379
|
+
Math.hypot(coordinate.x - interaction.originClient.x, coordinate.y - interaction.originClient.y) < 0.5) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (!interaction.started) {
|
|
383
|
+
interaction.started = true;
|
|
384
|
+
this.#emit({
|
|
385
|
+
type: 'overlay-drag-started',
|
|
386
|
+
...this.#interactionIdentity(interaction),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
const candidate = createDragCandidate(interaction.before, interaction.hit, interaction.originData, this.#fromPixel(coordinate, interaction.before.paneId), this.#scene.data.map((bar) => bar.timestamp), this.#scene.symbol.pricePrecision);
|
|
391
|
+
const index = this.#scene.overlays.findIndex((overlay) => overlay.id === candidate.id);
|
|
392
|
+
const overlays = structuredClone(this.#scene.overlays);
|
|
393
|
+
overlays[index] = candidate;
|
|
394
|
+
const parsed = parseChartScene({ ...structuredClone(this.#scene), overlays });
|
|
395
|
+
const normalized = parsed.overlays[index];
|
|
396
|
+
if (!this.#chart.overrideOverlay(toEngineOverlay(normalized, this.#idMap, `/overlays/${index}`, this.#overlayCallbacks(normalized)))) {
|
|
397
|
+
throw new SceneError('RUNTIME_INIT_FAILED', `/overlays/${index}`, `KLineCharts failed to preview Overlay ${normalized.id}.`);
|
|
398
|
+
}
|
|
399
|
+
interaction.candidate = normalized;
|
|
400
|
+
this.#emit({
|
|
401
|
+
type: 'overlay-dragging',
|
|
402
|
+
...this.#interactionIdentity(interaction),
|
|
403
|
+
candidate: normalized,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
if (error instanceof SceneError) {
|
|
408
|
+
this.#cancelInteraction('validation-error', error);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
#handlePointerUp = (event) => {
|
|
415
|
+
const interaction = this.#pointerInteraction;
|
|
416
|
+
if (interaction === undefined || interaction.pointerId !== event.pointerId) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
event.preventDefault();
|
|
420
|
+
event.stopImmediatePropagation();
|
|
421
|
+
this.#pointerInteraction = undefined;
|
|
422
|
+
this.#stopPointerCapture(interaction);
|
|
423
|
+
if (!interaction.started) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const overlay = interaction.candidate ?? interaction.before;
|
|
427
|
+
const index = this.#scene.overlays.findIndex((candidate) => candidate.id === overlay.id);
|
|
428
|
+
const overlays = structuredClone(this.#scene.overlays);
|
|
429
|
+
overlays[index] = overlay;
|
|
430
|
+
this.#scene = parseChartScene({ ...structuredClone(this.#scene), overlays });
|
|
431
|
+
const committed = this.#scene.overlays[index];
|
|
432
|
+
this.#emit({
|
|
433
|
+
type: 'overlay-drag-committed',
|
|
434
|
+
...this.#interactionIdentity(interaction),
|
|
435
|
+
overlay: committed,
|
|
436
|
+
});
|
|
437
|
+
this.#emit({ type: 'overlay-updated', overlay: committed });
|
|
438
|
+
};
|
|
439
|
+
#handlePointerCancel = (event) => {
|
|
440
|
+
if (this.#pointerInteraction?.pointerId === event.pointerId) {
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
event.stopImmediatePropagation();
|
|
443
|
+
this.#cancelInteraction('pointer-cancel');
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
#handleKeyDown = (event) => {
|
|
447
|
+
if (event.key === 'Escape' && this.#pointerInteraction !== undefined) {
|
|
448
|
+
this.#cancelInteraction('escape');
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
#handleWindowBlur = () => {
|
|
452
|
+
if (this.#pointerInteraction !== undefined) {
|
|
453
|
+
this.#cancelInteraction('window-blur');
|
|
454
|
+
}
|
|
455
|
+
};
|
|
133
456
|
subscribe(listener) {
|
|
134
457
|
this.#assertActive();
|
|
135
458
|
this.#listeners.add(listener);
|
|
@@ -139,24 +462,60 @@ export class KLineChartsSceneAdapter {
|
|
|
139
462
|
}
|
|
140
463
|
exportScene() {
|
|
141
464
|
this.#assertActive();
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
throw new SceneError('EXPORT_INVALID', `/overlays/${index}`, `KLineCharts lost Overlay ${source.id}.`);
|
|
465
|
+
for (let index = 0; index < this.#scene.overlays.length; index++) {
|
|
466
|
+
const overlay = this.#scene.overlays[index];
|
|
467
|
+
if (!this.#engineOverlays().some((engine) => engine.id === overlay.id)) {
|
|
468
|
+
throw new SceneError('EXPORT_INVALID', `/overlays/${index}`, `KLineCharts lost Overlay ${overlay.id}.`);
|
|
147
469
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
470
|
+
}
|
|
471
|
+
return parseChartScene(structuredClone(this.#scene));
|
|
472
|
+
}
|
|
473
|
+
async setPriceScale(scale) {
|
|
474
|
+
this.#assertActive();
|
|
475
|
+
const candidate = parseChartScene(promoteSceneToM2(this.#scene, scale));
|
|
476
|
+
const paneIndex = candidate.panes.findIndex((pane) => pane.kind === 'candle');
|
|
477
|
+
const pane = candidate.panes[paneIndex];
|
|
478
|
+
const axisIndex = pane.yAxes.findIndex((axis) => axis.role === 'primary');
|
|
479
|
+
const axis = pane.yAxes[axisIndex];
|
|
480
|
+
const previousPane = this.#scene.panes[paneIndex];
|
|
481
|
+
const previousAxis = previousPane.yAxes[axisIndex];
|
|
482
|
+
const path = `/panes/${paneIndex}/yAxes/${axisIndex}`;
|
|
483
|
+
try {
|
|
484
|
+
overrideSceneYAxis(this.#chart, this.#idMap, axis, pane.id, path);
|
|
485
|
+
// KLineCharts batches Y-axis recreation in a microtask; await that formal
|
|
486
|
+
// layout boundary before making the upgraded Scene externally visible.
|
|
487
|
+
await Promise.resolve();
|
|
488
|
+
const reference = candidate.data[0];
|
|
489
|
+
this.#toPixel({ timestamp: reference.timestamp, value: reference.close }, pane.id);
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
overrideSceneYAxis(this.#chart, this.#idMap, previousAxis, previousPane.id, path);
|
|
493
|
+
await Promise.resolve();
|
|
494
|
+
if (error instanceof SceneError) {
|
|
495
|
+
throw error;
|
|
496
|
+
}
|
|
497
|
+
throw new SceneError('RUNTIME_INIT_FAILED', `${path}/scale`, 'KLineCharts failed to apply the requested price scale atomically.');
|
|
498
|
+
}
|
|
499
|
+
this.#scene = candidate;
|
|
500
|
+
return structuredClone(candidate);
|
|
501
|
+
}
|
|
502
|
+
projectPoint(point, paneId) {
|
|
503
|
+
this.#assertActive();
|
|
504
|
+
const targetPane = paneId ?? this.#scene.panes.find((pane) => pane.kind === 'candle').id;
|
|
505
|
+
return this.#toPixel(point, targetPane);
|
|
506
|
+
}
|
|
507
|
+
hitTestOverlay(point) {
|
|
508
|
+
this.#assertActive();
|
|
509
|
+
return hitTestOverlayGeometries(point, this.#overlayGeometries());
|
|
154
510
|
}
|
|
155
511
|
addOverlay(value) {
|
|
156
512
|
this.#assertActive();
|
|
513
|
+
const baseScene = value.type === 'priceMeasurement'
|
|
514
|
+
? promoteSceneToM2(this.#scene)
|
|
515
|
+
: structuredClone(this.#scene);
|
|
157
516
|
const candidate = parseChartScene({
|
|
158
|
-
...
|
|
159
|
-
overlays: [...
|
|
517
|
+
...baseScene,
|
|
518
|
+
overlays: [...baseScene.overlays, structuredClone(value)],
|
|
160
519
|
});
|
|
161
520
|
const index = candidate.overlays.length - 1;
|
|
162
521
|
const overlay = candidate.overlays[index];
|
|
@@ -168,12 +527,13 @@ export class KLineChartsSceneAdapter {
|
|
|
168
527
|
this.#emit({ type: 'overlay-created', overlay });
|
|
169
528
|
return structuredClone(overlay);
|
|
170
529
|
}
|
|
171
|
-
updateOverlay(value) {
|
|
530
|
+
#updateOverlay(value, styleChange) {
|
|
172
531
|
this.#assertActive();
|
|
173
532
|
const index = this.#scene.overlays.findIndex((overlay) => overlay.id === value.id);
|
|
174
533
|
if (index < 0) {
|
|
175
534
|
throw new SceneError('INVALID_REFERENCE', '/overlays', `Overlay ${value.id} does not exist.`);
|
|
176
535
|
}
|
|
536
|
+
const before = structuredClone(this.#scene.overlays[index]);
|
|
177
537
|
const overlays = structuredClone(this.#scene.overlays);
|
|
178
538
|
overlays[index] = structuredClone(value);
|
|
179
539
|
const candidate = parseChartScene({
|
|
@@ -181,13 +541,26 @@ export class KLineChartsSceneAdapter {
|
|
|
181
541
|
overlays,
|
|
182
542
|
});
|
|
183
543
|
const overlay = candidate.overlays[index];
|
|
184
|
-
if (!this.#chart.overrideOverlay(toEngineOverlay(overlay, this.#idMap, `/overlays/${index}
|
|
544
|
+
if (!this.#chart.overrideOverlay(toEngineOverlay(overlay, this.#idMap, `/overlays/${index}`, this.#overlayCallbacks(overlay)))) {
|
|
185
545
|
throw new SceneError('RUNTIME_INIT_FAILED', `/overlays/${index}`, `KLineCharts failed to update Overlay ${overlay.id}.`);
|
|
186
546
|
}
|
|
187
547
|
this.#scene = candidate;
|
|
548
|
+
if (styleChange) {
|
|
549
|
+
this.#emit({ type: 'overlay-style-changed', before, overlay });
|
|
550
|
+
}
|
|
188
551
|
this.#emit({ type: 'overlay-updated', overlay });
|
|
189
552
|
return structuredClone(overlay);
|
|
190
553
|
}
|
|
554
|
+
updateOverlay(value) {
|
|
555
|
+
return this.#updateOverlay(value, false);
|
|
556
|
+
}
|
|
557
|
+
updateOverlayStyles(id, styles) {
|
|
558
|
+
const overlay = this.getOverlay(id);
|
|
559
|
+
if (overlay === undefined) {
|
|
560
|
+
throw new SceneError('INVALID_REFERENCE', '/overlays', `Overlay ${id} does not exist.`);
|
|
561
|
+
}
|
|
562
|
+
return this.#updateOverlay({ ...overlay, styles: structuredClone(styles) }, true);
|
|
563
|
+
}
|
|
191
564
|
removeOverlay(id) {
|
|
192
565
|
this.#assertActive();
|
|
193
566
|
const index = this.#scene.overlays.findIndex((overlay) => overlay.id === id);
|
|
@@ -202,6 +575,9 @@ export class KLineChartsSceneAdapter {
|
|
|
202
575
|
...structuredClone(this.#scene),
|
|
203
576
|
overlays: this.#scene.overlays.filter((overlay) => overlay.id !== id),
|
|
204
577
|
});
|
|
578
|
+
if (this.#selectedOverlayId === id) {
|
|
579
|
+
this.#selectOverlay(null);
|
|
580
|
+
}
|
|
205
581
|
this.#emit({ type: 'overlay-removed', id });
|
|
206
582
|
}
|
|
207
583
|
return true;
|
|
@@ -212,17 +588,24 @@ export class KLineChartsSceneAdapter {
|
|
|
212
588
|
this.#engineOverlays().some((overlay) => overlay.id === request.id)) {
|
|
213
589
|
throw new SceneError('DUPLICATE_ID', '/overlays/id', `Overlay ${request.id} already exists.`);
|
|
214
590
|
}
|
|
591
|
+
const candidate = request.type === 'priceMeasurement'
|
|
592
|
+
? parseChartScene(promoteSceneToM2(this.#scene))
|
|
593
|
+
: this.#scene;
|
|
215
594
|
const result = this.#chart.createOverlay(toEngineOverlayDrawing(structuredClone(request), this.#idMap, this.#overlayCallbacks(structuredClone(request), true)));
|
|
216
595
|
if (result !== request.id) {
|
|
217
596
|
throw new SceneError('RUNTIME_INIT_FAILED', '/overlays', `KLineCharts failed to start drawing Overlay ${request.id}.`);
|
|
218
597
|
}
|
|
598
|
+
this.#scene = candidate;
|
|
219
599
|
return request.id;
|
|
220
600
|
}
|
|
221
601
|
getOverlay(id) {
|
|
222
|
-
|
|
602
|
+
this.#assertActive();
|
|
603
|
+
const overlay = this.#scene.overlays.find((candidate) => candidate.id === id);
|
|
604
|
+
return overlay === undefined ? undefined : structuredClone(overlay);
|
|
223
605
|
}
|
|
224
606
|
listOverlays() {
|
|
225
|
-
|
|
607
|
+
this.#assertActive();
|
|
608
|
+
return structuredClone(this.#scene.overlays);
|
|
226
609
|
}
|
|
227
610
|
inspect() {
|
|
228
611
|
this.#assertActive();
|
|
@@ -241,6 +624,7 @@ export class KLineChartsSceneAdapter {
|
|
|
241
624
|
});
|
|
242
625
|
return {
|
|
243
626
|
engineVersion: this.#engine.version(),
|
|
627
|
+
runtimeVersion: this.#scene.runtime.runtimeVersion,
|
|
244
628
|
dataCount: this.#chart.getDataList().length,
|
|
245
629
|
paneIds: this.#scene.panes.map((pane) => pane.id),
|
|
246
630
|
indicators,
|
|
@@ -253,7 +637,9 @@ export class KLineChartsSceneAdapter {
|
|
|
253
637
|
if (this.#disposed) {
|
|
254
638
|
return;
|
|
255
639
|
}
|
|
640
|
+
this.#cancelInteraction('destroy');
|
|
256
641
|
this.#disposed = true;
|
|
642
|
+
this.#removeInteractionListeners();
|
|
257
643
|
this.#listeners.clear();
|
|
258
644
|
this.#engine.dispose(this.#container);
|
|
259
645
|
this.#container.replaceChildren();
|
|
@@ -18,9 +18,12 @@ export interface OverlayDrawingSource extends OverlaySourceSnapshot {
|
|
|
18
18
|
}
|
|
19
19
|
export interface EngineOverlayCallbacks {
|
|
20
20
|
readonly onDrawEnd?: NonNullable<OverlayCreate['onDrawEnd']>;
|
|
21
|
+
readonly onPressedMoveStart?: NonNullable<OverlayCreate['onPressedMoveStart']>;
|
|
22
|
+
readonly onPressedMoving?: NonNullable<OverlayCreate['onPressedMoving']>;
|
|
21
23
|
readonly onPressedMoveEnd?: NonNullable<OverlayCreate['onPressedMoveEnd']>;
|
|
22
24
|
readonly onRemoved?: NonNullable<OverlayCreate['onRemoved']>;
|
|
23
25
|
readonly onSelected?: NonNullable<OverlayCreate['onSelected']>;
|
|
26
|
+
readonly onDeselected?: NonNullable<OverlayCreate['onDeselected']>;
|
|
24
27
|
}
|
|
25
28
|
export declare function toEngineOverlay(overlay: SceneOverlay, idMap: EngineIdMap, path: string, callbacks?: EngineOverlayCallbacks): OverlayCreate;
|
|
26
29
|
/** 创建尚无几何点的交互式 Overlay,不把临时状态写入 Scene。 */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/conversion/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,YAAY,EACZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAEX,OAAO,EACP,aAAa,EAGb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/conversion/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,YAAY,EACZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAEX,OAAO,EACP,aAAa,EAGb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC/E,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACzE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC;CACnE;AAoGD,wBAAgB,eAAe,CAC9B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,sBAA2B,GACpC,aAAa,CAwBf;AAED,0CAA0C;AAC1C,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,sBAAsB,GAC/B,aAAa,CAsBf;AAiED,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,GACpB,YAAY,CAkHd;AAED,qBAAqB;AACrB,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE;IAAE,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,EACpF,KAAK,EAAE,WAAW,EAClB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,sBAAsB,GAC3D,IAAI,CAiBN"}
|
|
@@ -81,6 +81,7 @@ function toPoints(overlay) {
|
|
|
81
81
|
return [structuredClone(overlay.point ?? {})];
|
|
82
82
|
case 'rectangle':
|
|
83
83
|
case 'arrow':
|
|
84
|
+
case 'priceMeasurement':
|
|
84
85
|
return [structuredClone(overlay.start ?? {}), structuredClone(overlay.end ?? {})];
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -278,6 +279,7 @@ export function fromEngineOverlay(engine, source, idMap, path, pricePrecision) {
|
|
|
278
279
|
};
|
|
279
280
|
case 'rectangle':
|
|
280
281
|
case 'arrow':
|
|
282
|
+
case 'priceMeasurement':
|
|
281
283
|
return {
|
|
282
284
|
...base,
|
|
283
285
|
start: readPoint(0, true, true, `${path}/start`),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { ChartScene } from '@baron1996/kline-scene-schema';
|
|
1
|
+
import type { ChartScene, YAxis as SceneYAxis } from '@baron1996/kline-scene-schema';
|
|
2
2
|
import type { Chart } from 'klinecharts';
|
|
3
3
|
import type { EngineIdMap } from './id-map.js';
|
|
4
|
+
/** 原子提交 Scene 前,将单条轴映射到引擎的正式 normal/logarithm 名称。 */
|
|
5
|
+
export declare function overrideSceneYAxis(chart: Chart, idMap: EngineIdMap, axis: SceneYAxis, paneId: string, path: string): void;
|
|
4
6
|
/** 按 Scene 顺序创建 Pane、Y 轴和指标,并核对每个映射。 */
|
|
5
7
|
export declare function applyPanes(scene: ChartScene, chart: Chart, idMap: EngineIdMap): void;
|
|
6
8
|
//# sourceMappingURL=panes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panes.d.ts","sourceRoot":"","sources":["../../src/conversion/panes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"panes.d.ts","sourceRoot":"","sources":["../../src/conversion/panes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAErF,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA8B/C,qDAAqD;AACrD,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACV,IAAI,CAIN;AAED,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAuEpF"}
|
package/dist/conversion/panes.js
CHANGED
|
@@ -5,7 +5,7 @@ function axisOverride(axis, enginePaneId, engineAxisId) {
|
|
|
5
5
|
return {
|
|
6
6
|
id: engineAxisId,
|
|
7
7
|
paneId: enginePaneId,
|
|
8
|
-
name: 'normal',
|
|
8
|
+
name: axis.scale === 'logarithmic' ? 'logarithm' : 'normal',
|
|
9
9
|
reverse: axis.reverse,
|
|
10
10
|
inside: axis.inside,
|
|
11
11
|
position: axis.position,
|
|
@@ -17,6 +17,12 @@ function axisOverride(axis, enginePaneId, engineAxisId) {
|
|
|
17
17
|
needWidget: true,
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
/** 原子提交 Scene 前,将单条轴映射到引擎的正式 normal/logarithm 名称。 */
|
|
21
|
+
export function overrideSceneYAxis(chart, idMap, axis, paneId, path) {
|
|
22
|
+
const enginePaneId = requireMappedId(idMap.paneToEngine, paneId, `${path}/paneId`, 'Pane');
|
|
23
|
+
const engineAxisId = requireMappedId(idMap.yAxisToEngine, axis.id, `${path}/id`, 'Y-axis');
|
|
24
|
+
chart.overrideYAxis(axisOverride(axis, enginePaneId, engineAxisId));
|
|
25
|
+
}
|
|
20
26
|
/** 按 Scene 顺序创建 Pane、Y 轴和指标,并核对每个映射。 */
|
|
21
27
|
export function applyPanes(scene, chart, idMap) {
|
|
22
28
|
for (let paneIndex = 0; paneIndex < scene.panes.length; paneIndex++) {
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAUzC,MAAM,MAAM,iBAAiB,GAAG,cAAc,aAAa,CAAC,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;CACnC;AAmBD,qCAAqC;AACrC,wBAAsB,YAAY,CACjC,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,UAAU,GACf,OAAO,CAAC,YAAY,CAAC,CA+BvB"}
|
package/dist/engine.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { SceneError } from '@baron1996/kline-scene-schema';
|
|
2
2
|
import { toKLineChartsOptions } from './conversion/chart-options.js';
|
|
3
3
|
import { createStaticDataLoader } from './static-data-loader.js';
|
|
4
|
-
import { KLINECHARTS_ENGINE_VERSION, KLINECHARTS_RUNTIME_VERSION, } from './version.js';
|
|
4
|
+
import { KLINECHARTS_ENGINE_VERSION, KLINECHARTS_RUNTIME_VERSION, SUPPORTED_KLINECHARTS_RUNTIME_VERSIONS, } from './version.js';
|
|
5
5
|
function assertRuntimeIdentity(scene, actualEngineVersion) {
|
|
6
6
|
if (scene.runtime.engine !== 'klinecharts' ||
|
|
7
7
|
scene.runtime.engineVersion !== KLINECHARTS_ENGINE_VERSION ||
|
|
8
8
|
actualEngineVersion !== KLINECHARTS_ENGINE_VERSION ||
|
|
9
|
-
scene.runtime.runtimeVersion
|
|
10
|
-
throw new SceneError('ENGINE_VERSION_MISMATCH', '/runtime', `Expected klinecharts ${KLINECHARTS_ENGINE_VERSION} and Runtime ${KLINECHARTS_RUNTIME_VERSION}; received engine ${actualEngineVersion}.`);
|
|
9
|
+
!SUPPORTED_KLINECHARTS_RUNTIME_VERSIONS.includes(scene.runtime.runtimeVersion)) {
|
|
10
|
+
throw new SceneError('ENGINE_VERSION_MISMATCH', '/runtime', `Expected klinecharts ${KLINECHARTS_ENGINE_VERSION} and a supported Runtime through ${KLINECHARTS_RUNTIME_VERSION}; received engine ${actualEngineVersion}.`);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
/** 按固定顺序创建并初始化唯一的 KLineCharts 引擎。 */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { registerOverlay } from 'klinecharts';
|
|
2
|
+
type KLineOverlayTemplate = Parameters<typeof registerOverlay>[0];
|
|
3
|
+
export interface PriceMeasurementDisplay {
|
|
4
|
+
readonly absoluteChange: number;
|
|
5
|
+
readonly percentageChange: number;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
}
|
|
8
|
+
/** 仅派生显示值;绝对变化、百分比和 label 永不写入 Scene。 */
|
|
9
|
+
export declare function derivePriceMeasurementDisplay(startValue: number, endValue: number, pricePrecision: number): PriceMeasurementDisplay;
|
|
10
|
+
/** 量度工具只从两个引擎数据点派生显示文字,派生值不进入 Scene。 */
|
|
11
|
+
export declare const priceMeasurementOverlay: KLineOverlayTemplate;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=price-measurement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"price-measurement.d.ts","sourceRoot":"","sources":["../../src/extensions/price-measurement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAOlE,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,yCAAyC;AACzC,wBAAgB,6BAA6B,CAC5C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACpB,uBAAuB,CAsBzB;AAED,wCAAwC;AACxC,eAAO,MAAM,uBAAuB,EAAE,oBA6CrC,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SceneError } from '@baron1996/kline-scene-schema';
|
|
2
|
+
function signedFixed(value, precision) {
|
|
3
|
+
const prefix = value > 0 ? '+' : '';
|
|
4
|
+
return `${prefix}${value.toFixed(precision)}`;
|
|
5
|
+
}
|
|
6
|
+
/** 仅派生显示值;绝对变化、百分比和 label 永不写入 Scene。 */
|
|
7
|
+
export function derivePriceMeasurementDisplay(startValue, endValue, pricePrecision) {
|
|
8
|
+
if (!Number.isFinite(startValue) ||
|
|
9
|
+
!Number.isFinite(endValue) ||
|
|
10
|
+
startValue <= 0 ||
|
|
11
|
+
!Number.isInteger(pricePrecision) ||
|
|
12
|
+
pricePrecision < 0 ||
|
|
13
|
+
pricePrecision > 16) {
|
|
14
|
+
throw new SceneError('SCENE_SCHEMA_INVALID', '/overlays/priceMeasurement', 'Price measurement display inputs must be positive finite prices and a 0..16 precision.');
|
|
15
|
+
}
|
|
16
|
+
const absoluteChange = endValue - startValue;
|
|
17
|
+
const percentageChange = absoluteChange / startValue * 100;
|
|
18
|
+
return {
|
|
19
|
+
absoluteChange,
|
|
20
|
+
percentageChange,
|
|
21
|
+
label: `${signedFixed(absoluteChange, pricePrecision)} (${signedFixed(percentageChange, 2)}%)`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** 量度工具只从两个引擎数据点派生显示文字,派生值不进入 Scene。 */
|
|
25
|
+
export const priceMeasurementOverlay = {
|
|
26
|
+
name: 'priceMeasurement',
|
|
27
|
+
totalStep: 3,
|
|
28
|
+
needDefaultPointFigure: true,
|
|
29
|
+
needDefaultXAxisFigure: true,
|
|
30
|
+
needDefaultYAxisFigure: true,
|
|
31
|
+
createPointFigures: ({ chart, coordinates, overlay }) => {
|
|
32
|
+
const startCoordinate = coordinates[0];
|
|
33
|
+
const endCoordinate = coordinates[1];
|
|
34
|
+
const startPoint = overlay.points[0];
|
|
35
|
+
const endPoint = overlay.points[1];
|
|
36
|
+
if (startCoordinate === undefined ||
|
|
37
|
+
endCoordinate === undefined ||
|
|
38
|
+
startPoint?.value === undefined ||
|
|
39
|
+
endPoint?.value === undefined) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const pricePrecision = chart.getSymbol()?.pricePrecision ?? 0;
|
|
43
|
+
const { label } = derivePriceMeasurementDisplay(startPoint.value, endPoint.value, pricePrecision);
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
key: 'measurement-body',
|
|
47
|
+
type: 'line',
|
|
48
|
+
attrs: { coordinates: [startCoordinate, endCoordinate] },
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'measurement-label',
|
|
52
|
+
type: 'text',
|
|
53
|
+
attrs: {
|
|
54
|
+
x: (startCoordinate.x + endCoordinate.x) / 2 + 8,
|
|
55
|
+
y: (startCoordinate.y + endCoordinate.y) / 2 - 8,
|
|
56
|
+
text: label,
|
|
57
|
+
align: 'left',
|
|
58
|
+
baseline: 'bottom',
|
|
59
|
+
},
|
|
60
|
+
ignoreEvent: true,
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
},
|
|
64
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/extensions/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/extensions/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AASnD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,KAAK,eAAe,GAAG,CAAC,QAAQ,EAAE,oBAAoB,KAAK,IAAI,CAAC;AAahE,kCAAkC;AAClC,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAQvE;AAED,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD"}
|
|
@@ -2,8 +2,10 @@ import { arrowOverlay } from './arrow.js';
|
|
|
2
2
|
import { calloutOverlay } from './callout.js';
|
|
3
3
|
import { crossLineOverlay } from './cross-line.js';
|
|
4
4
|
import { rectangleOverlay } from './rectangle.js';
|
|
5
|
+
import { priceMeasurementOverlay } from './price-measurement.js';
|
|
5
6
|
import { textOverlay } from './text.js';
|
|
6
7
|
const projectExtensions = [
|
|
8
|
+
priceMeasurementOverlay,
|
|
7
9
|
rectangleOverlay,
|
|
8
10
|
arrowOverlay,
|
|
9
11
|
crossLineOverlay,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './adapter.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
3
|
export * from './extensions/register.js';
|
|
4
|
+
export * from './interaction/dragging.js';
|
|
5
|
+
export * from './interaction/hit-testing.js';
|
|
4
6
|
export * from './registry/indicators.js';
|
|
5
7
|
export * from './registry/overlays.js';
|
|
6
8
|
export * from './static-data-loader.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './adapter.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
3
|
export * from './extensions/register.js';
|
|
4
|
+
export * from './interaction/dragging.js';
|
|
5
|
+
export * from './interaction/hit-testing.js';
|
|
4
6
|
export * from './registry/indicators.js';
|
|
5
7
|
export * from './registry/overlays.js';
|
|
6
8
|
export * from './static-data-loader.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SceneOverlay } from '@baron1996/kline-scene-schema';
|
|
2
|
+
export interface DragDataPoint {
|
|
3
|
+
readonly dataIndex: number;
|
|
4
|
+
readonly value: number;
|
|
5
|
+
}
|
|
6
|
+
export interface DragTarget {
|
|
7
|
+
readonly target: 'anchor' | 'body';
|
|
8
|
+
readonly anchorIndex: number | null;
|
|
9
|
+
}
|
|
10
|
+
/** 根据冻结的绝对平移语义构造未提交候选,不修改输入 Overlay。 */
|
|
11
|
+
export declare function createDragCandidate(before: SceneOverlay, dragTarget: DragTarget, origin: DragDataPoint, current: DragDataPoint, timestamps: readonly number[], pricePrecision: number): SceneOverlay;
|
|
12
|
+
//# sourceMappingURL=dragging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dragging.d.ts","sourceRoot":"","sources":["../../src/interaction/dragging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAKlE,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAyBD,wCAAwC;AACxC,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,cAAc,EAAE,MAAM,GACpB,YAAY,CAgFd"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { SceneError } from '@baron1996/kline-scene-schema';
|
|
2
|
+
import { normalizePriceValue } from '../conversion/price.js';
|
|
3
|
+
function requireDataIndex(value, path) {
|
|
4
|
+
if (!Number.isFinite(value)) {
|
|
5
|
+
throw new SceneError('INVALID_REFERENCE', path, 'Drag data index must be finite.');
|
|
6
|
+
}
|
|
7
|
+
return Math.round(value);
|
|
8
|
+
}
|
|
9
|
+
function requireTimestamp(timestamps, index, path) {
|
|
10
|
+
const timestamp = timestamps[index];
|
|
11
|
+
if (timestamp === undefined) {
|
|
12
|
+
throw new SceneError('INVALID_REFERENCE', path, 'Drag candidate must remain on an embedded market-data bar.');
|
|
13
|
+
}
|
|
14
|
+
return timestamp;
|
|
15
|
+
}
|
|
16
|
+
/** 根据冻结的绝对平移语义构造未提交候选,不修改输入 Overlay。 */
|
|
17
|
+
export function createDragCandidate(before, dragTarget, origin, current, timestamps, pricePrecision) {
|
|
18
|
+
const deltaValue = current.value - origin.value;
|
|
19
|
+
if (!Number.isFinite(deltaValue)) {
|
|
20
|
+
throw new SceneError('SCENE_SCHEMA_INVALID', '/overlays', 'Drag price delta must be finite.');
|
|
21
|
+
}
|
|
22
|
+
if (before.type === 'horizontalStraightLine') {
|
|
23
|
+
const anchor = before.anchor;
|
|
24
|
+
if (anchor === undefined || !('value' in anchor)) {
|
|
25
|
+
throw new SceneError('SCENE_SCHEMA_INVALID', '/overlays/anchor', 'Missing price anchor.');
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
...structuredClone(before),
|
|
29
|
+
anchor: {
|
|
30
|
+
value: normalizePriceValue(anchor.value + deltaValue, pricePrecision, '/overlays/anchor/value'),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (before.type !== 'priceMeasurement' || before.start === undefined || before.end === undefined) {
|
|
35
|
+
throw new SceneError('SCENE_SCHEMA_INVALID', '/overlays/type', 'Controlled M2 dragging only supports horizontalStraightLine and priceMeasurement.');
|
|
36
|
+
}
|
|
37
|
+
const candidate = structuredClone(before);
|
|
38
|
+
if (dragTarget.target === 'anchor') {
|
|
39
|
+
if (dragTarget.anchorIndex !== 0 && dragTarget.anchorIndex !== 1) {
|
|
40
|
+
throw new SceneError('INVALID_REFERENCE', '/overlays/anchorIndex', 'Invalid anchor index.');
|
|
41
|
+
}
|
|
42
|
+
const index = requireDataIndex(current.dataIndex, '/overlays/anchor/dataIndex');
|
|
43
|
+
const point = {
|
|
44
|
+
timestamp: requireTimestamp(timestamps, index, '/overlays/anchor/timestamp'),
|
|
45
|
+
value: normalizePriceValue(current.value, pricePrecision, '/overlays/anchor/value'),
|
|
46
|
+
};
|
|
47
|
+
if (dragTarget.anchorIndex === 0) {
|
|
48
|
+
candidate.start = point;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
candidate.end = point;
|
|
52
|
+
}
|
|
53
|
+
return candidate;
|
|
54
|
+
}
|
|
55
|
+
const startIndex = timestamps.indexOf(before.start.timestamp);
|
|
56
|
+
const endIndex = timestamps.indexOf(before.end.timestamp);
|
|
57
|
+
if (startIndex < 0 || endIndex < 0) {
|
|
58
|
+
throw new SceneError('INVALID_REFERENCE', '/overlays', 'priceMeasurement endpoints must reference embedded bars before dragging.');
|
|
59
|
+
}
|
|
60
|
+
const deltaIndex = requireDataIndex(current.dataIndex, '/overlays/body/dataIndex') -
|
|
61
|
+
requireDataIndex(origin.dataIndex, '/overlays/body/originDataIndex');
|
|
62
|
+
candidate.start = {
|
|
63
|
+
timestamp: requireTimestamp(timestamps, startIndex + deltaIndex, '/overlays/start/timestamp'),
|
|
64
|
+
value: normalizePriceValue(before.start.value + deltaValue, pricePrecision, '/overlays/start/value'),
|
|
65
|
+
};
|
|
66
|
+
candidate.end = {
|
|
67
|
+
timestamp: requireTimestamp(timestamps, endIndex + deltaIndex, '/overlays/end/timestamp'),
|
|
68
|
+
value: normalizePriceValue(before.end.value + deltaValue, pricePrecision, '/overlays/end/value'),
|
|
69
|
+
};
|
|
70
|
+
return candidate;
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const OVERLAY_BODY_HIT_THRESHOLD_CSS_PX = 12;
|
|
2
|
+
export declare const OVERLAY_ANCHOR_HIT_THRESHOLD_CSS_PX = 14;
|
|
3
|
+
export interface PixelCoordinate {
|
|
4
|
+
readonly x: number;
|
|
5
|
+
readonly y: number;
|
|
6
|
+
}
|
|
7
|
+
export interface OverlayPixelGeometry {
|
|
8
|
+
readonly overlayId: string;
|
|
9
|
+
readonly sceneIndex: number;
|
|
10
|
+
readonly zLevel: number;
|
|
11
|
+
readonly locked: boolean;
|
|
12
|
+
readonly anchors: readonly PixelCoordinate[];
|
|
13
|
+
readonly bodySegments: readonly (readonly [PixelCoordinate, PixelCoordinate])[];
|
|
14
|
+
}
|
|
15
|
+
export interface OverlayHitResult {
|
|
16
|
+
readonly overlayId: string;
|
|
17
|
+
readonly target: 'anchor' | 'body';
|
|
18
|
+
readonly anchorIndex: number | null;
|
|
19
|
+
readonly locked: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 按冻结契约在 CSS 像素坐标中执行命中测试。
|
|
23
|
+
* 锚点全局优先于主体;同类再按 zLevel、Scene 后序和锚点低索引排序。
|
|
24
|
+
*/
|
|
25
|
+
export declare function hitTestOverlayGeometries(point: PixelCoordinate, geometries: readonly OverlayPixelGeometry[]): OverlayHitResult | null;
|
|
26
|
+
//# sourceMappingURL=hit-testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hit-testing.d.ts","sourceRoot":"","sources":["../../src/interaction/hit-testing.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,KAAK,CAAC;AACpD,eAAO,MAAM,mCAAmC,KAAK,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;CAChF;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAmDD;;;GAGG;AACH,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,SAAS,oBAAoB,EAAE,GACzC,gBAAgB,GAAG,IAAI,CAgDzB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export const OVERLAY_BODY_HIT_THRESHOLD_CSS_PX = 12;
|
|
2
|
+
export const OVERLAY_ANCHOR_HIT_THRESHOLD_CSS_PX = 14;
|
|
3
|
+
function coordinateDistance(left, right) {
|
|
4
|
+
return Math.hypot(left.x - right.x, left.y - right.y);
|
|
5
|
+
}
|
|
6
|
+
function segmentDistance(point, start, end) {
|
|
7
|
+
const dx = end.x - start.x;
|
|
8
|
+
const dy = end.y - start.y;
|
|
9
|
+
const lengthSquared = dx * dx + dy * dy;
|
|
10
|
+
if (lengthSquared === 0) {
|
|
11
|
+
return coordinateDistance(point, start);
|
|
12
|
+
}
|
|
13
|
+
const projection = Math.max(0, Math.min(1, ((point.x - start.x) * dx + (point.y - start.y) * dy) / lengthSquared));
|
|
14
|
+
return coordinateDistance(point, {
|
|
15
|
+
x: start.x + projection * dx,
|
|
16
|
+
y: start.y + projection * dy,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function compareHits(left, right) {
|
|
20
|
+
if (left.zLevel !== right.zLevel) {
|
|
21
|
+
return right.zLevel - left.zLevel;
|
|
22
|
+
}
|
|
23
|
+
if (left.sceneIndex !== right.sceneIndex) {
|
|
24
|
+
return right.sceneIndex - left.sceneIndex;
|
|
25
|
+
}
|
|
26
|
+
if (left.overlayId === right.overlayId &&
|
|
27
|
+
left.anchorIndex !== null &&
|
|
28
|
+
right.anchorIndex !== null &&
|
|
29
|
+
left.anchorIndex !== right.anchorIndex) {
|
|
30
|
+
return left.anchorIndex - right.anchorIndex;
|
|
31
|
+
}
|
|
32
|
+
return left.distance - right.distance;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 按冻结契约在 CSS 像素坐标中执行命中测试。
|
|
36
|
+
* 锚点全局优先于主体;同类再按 zLevel、Scene 后序和锚点低索引排序。
|
|
37
|
+
*/
|
|
38
|
+
export function hitTestOverlayGeometries(point, geometries) {
|
|
39
|
+
const anchorHits = [];
|
|
40
|
+
const bodyHits = [];
|
|
41
|
+
for (const geometry of geometries) {
|
|
42
|
+
for (let anchorIndex = 0; anchorIndex < geometry.anchors.length; anchorIndex++) {
|
|
43
|
+
const anchor = geometry.anchors[anchorIndex];
|
|
44
|
+
if (anchor === undefined) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const distance = coordinateDistance(point, anchor);
|
|
48
|
+
if (distance <= OVERLAY_ANCHOR_HIT_THRESHOLD_CSS_PX) {
|
|
49
|
+
anchorHits.push({
|
|
50
|
+
overlayId: geometry.overlayId,
|
|
51
|
+
target: 'anchor',
|
|
52
|
+
anchorIndex,
|
|
53
|
+
locked: geometry.locked,
|
|
54
|
+
distance,
|
|
55
|
+
sceneIndex: geometry.sceneIndex,
|
|
56
|
+
zLevel: geometry.zLevel,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let distance = Number.POSITIVE_INFINITY;
|
|
61
|
+
for (const [start, end] of geometry.bodySegments) {
|
|
62
|
+
distance = Math.min(distance, segmentDistance(point, start, end));
|
|
63
|
+
}
|
|
64
|
+
if (distance <= OVERLAY_BODY_HIT_THRESHOLD_CSS_PX) {
|
|
65
|
+
bodyHits.push({
|
|
66
|
+
overlayId: geometry.overlayId,
|
|
67
|
+
target: 'body',
|
|
68
|
+
anchorIndex: null,
|
|
69
|
+
locked: geometry.locked,
|
|
70
|
+
distance,
|
|
71
|
+
sceneIndex: geometry.sceneIndex,
|
|
72
|
+
zLevel: geometry.zLevel,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const winner = (anchorHits.length > 0 ? anchorHits : bodyHits).sort(compareHits)[0];
|
|
77
|
+
if (winner === undefined) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
overlayId: winner.overlayId,
|
|
82
|
+
target: winner.target,
|
|
83
|
+
anchorIndex: winner.anchorIndex,
|
|
84
|
+
locked: winner.locked,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type OverlayPixelGeometry, type PixelCoordinate } from './hit-testing.js';
|
|
2
|
+
/**
|
|
3
|
+
* 仅忽略引擎旧命中区域产生的取消选择:回调坐标必须仍命中当前选中 Overlay 的规范几何。
|
|
4
|
+
*/
|
|
5
|
+
export declare function shouldIgnoreStaleOverlayDeselection(selectedOverlayId: string | null, deselectedOverlayId: string, coordinate: PixelCoordinate | undefined, currentGeometries: readonly OverlayPixelGeometry[]): boolean;
|
|
6
|
+
//# sourceMappingURL=selection-arbitration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-arbitration.d.ts","sourceRoot":"","sources":["../../src/interaction/selection-arbitration.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,MAAM,kBAAkB,CAAC;AAE1B;;GAEG;AACH,wBAAgB,mCAAmC,CAClD,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,mBAAmB,EAAE,MAAM,EAC3B,UAAU,EAAE,eAAe,GAAG,SAAS,EACvC,iBAAiB,EAAE,SAAS,oBAAoB,EAAE,GAChD,OAAO,CAKT"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { hitTestOverlayGeometries, } from './hit-testing.js';
|
|
2
|
+
/**
|
|
3
|
+
* 仅忽略引擎旧命中区域产生的取消选择:回调坐标必须仍命中当前选中 Overlay 的规范几何。
|
|
4
|
+
*/
|
|
5
|
+
export function shouldIgnoreStaleOverlayDeselection(selectedOverlayId, deselectedOverlayId, coordinate, currentGeometries) {
|
|
6
|
+
if (selectedOverlayId !== deselectedOverlayId || coordinate === undefined) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return hitTestOverlayGeometries(coordinate, currentGeometries)?.overlayId === deselectedOverlayId;
|
|
10
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SceneOverlay } from '@baron1996/kline-scene-schema';
|
|
2
2
|
export declare const BUILT_IN_OVERLAYS: readonly ["horizontalRayLine", "horizontalSegment", "horizontalStraightLine", "verticalRayLine", "verticalSegment", "verticalStraightLine", "rayLine", "segment", "straightLine", "priceLine", "priceChannelLine", "parallelStraightLine", "fibonacciLine", "brush", "simpleAnnotation", "simpleTag"];
|
|
3
|
-
export declare const PROJECT_OVERLAYS: readonly ["rectangle", "arrow", "crossLine", "callout", "text"];
|
|
4
|
-
export declare const SUPPORTED_OVERLAYS: readonly ["horizontalRayLine", "horizontalSegment", "horizontalStraightLine", "verticalRayLine", "verticalSegment", "verticalStraightLine", "rayLine", "segment", "straightLine", "priceLine", "priceChannelLine", "parallelStraightLine", "fibonacciLine", "brush", "simpleAnnotation", "simpleTag", "rectangle", "arrow", "crossLine", "callout", "text"];
|
|
3
|
+
export declare const PROJECT_OVERLAYS: readonly ["priceMeasurement", "rectangle", "arrow", "crossLine", "callout", "text"];
|
|
4
|
+
export declare const SUPPORTED_OVERLAYS: readonly ["horizontalRayLine", "horizontalSegment", "horizontalStraightLine", "verticalRayLine", "verticalSegment", "verticalStraightLine", "rayLine", "segment", "straightLine", "priceLine", "priceChannelLine", "parallelStraightLine", "fibonacciLine", "brush", "simpleAnnotation", "simpleTag", "priceMeasurement", "rectangle", "arrow", "crossLine", "callout", "text"];
|
|
5
5
|
export declare function isSupportedOverlay(name: string): name is SceneOverlay['type'];
|
|
6
6
|
//# sourceMappingURL=overlays.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/registry/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,eAAO,MAAM,iBAAiB,uSAiBsB,CAAC;AAErD,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/registry/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,eAAO,MAAM,iBAAiB,uSAiBsB,CAAC;AAErD,eAAO,MAAM,gBAAgB,qFAOuB,CAAC;AAErD,eAAO,MAAM,kBAAkB,iXAGqB,CAAC;AAIrD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,YAAY,CAAC,MAAM,CAAC,CAE7E"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Adapter 包版本。 */
|
|
2
|
-
export declare const ADAPTER_PACKAGE_VERSION: "0.
|
|
2
|
+
export declare const ADAPTER_PACKAGE_VERSION: "0.2.1";
|
|
3
3
|
/** 唯一允许加载的 KLineCharts 引擎版本。 */
|
|
4
4
|
export declare const KLINECHARTS_ENGINE_VERSION: "10.0.0";
|
|
5
5
|
/** 与 ChartScene 绑定的 Runtime 协议版本。 */
|
|
6
|
-
export declare const KLINECHARTS_RUNTIME_VERSION: "0.
|
|
6
|
+
export declare const KLINECHARTS_RUNTIME_VERSION: "0.2.0";
|
|
7
|
+
export declare const SUPPORTED_KLINECHARTS_RUNTIME_VERSIONS: readonly ["0.1.0", "0.2.0"];
|
|
7
8
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,eAAO,MAAM,uBAAuB,EAAG,OAAgB,CAAC;AAExD,gCAAgC;AAChC,eAAO,MAAM,0BAA0B,EAAG,QAAiB,CAAC;AAE5D,qCAAqC;AACrC,eAAO,MAAM,2BAA2B,EAAG,OAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,eAAO,MAAM,uBAAuB,EAAG,OAAgB,CAAC;AAExD,gCAAgC;AAChC,eAAO,MAAM,0BAA0B,EAAG,QAAiB,CAAC;AAE5D,qCAAqC;AACrC,eAAO,MAAM,2BAA2B,EAAG,OAAgB,CAAC;AAC5D,eAAO,MAAM,sCAAsC,6BAA8B,CAAC"}
|
package/dist/version.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Adapter 包版本。 */
|
|
2
|
-
export const ADAPTER_PACKAGE_VERSION = '0.
|
|
2
|
+
export const ADAPTER_PACKAGE_VERSION = '0.2.1';
|
|
3
3
|
/** 唯一允许加载的 KLineCharts 引擎版本。 */
|
|
4
4
|
export const KLINECHARTS_ENGINE_VERSION = '10.0.0';
|
|
5
5
|
/** 与 ChartScene 绑定的 Runtime 协议版本。 */
|
|
6
|
-
export const KLINECHARTS_RUNTIME_VERSION = '0.
|
|
6
|
+
export const KLINECHARTS_RUNTIME_VERSION = '0.2.0';
|
|
7
|
+
export const SUPPORTED_KLINECHARTS_RUNTIME_VERSIONS = ['0.1.0', '0.2.0'];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baron1996/klinecharts-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Controlled ChartScene adapter for KLineCharts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@baron1996/kline-scene-schema": "0.1
|
|
41
|
+
"@baron1996/kline-scene-schema": "0.2.1",
|
|
42
42
|
"klinecharts": "10.0.0"
|
|
43
43
|
}
|
|
44
44
|
}
|