@baron1996/klinecharts-adapter 0.9.4 → 0.9.7
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 +4 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +488 -67
- package/dist/conversion/id-map.d.ts.map +1 -1
- package/dist/conversion/id-map.js +6 -1
- package/dist/drawing/engine-port.d.ts +15 -0
- package/dist/drawing/engine-port.d.ts.map +1 -1
- package/dist/engine.d.ts +10 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +26 -1
- package/dist/gap-aware-series.d.ts +44 -0
- package/dist/gap-aware-series.d.ts.map +1 -0
- package/dist/gap-aware-series.js +221 -0
- package/dist/interaction/dragging.d.ts.map +1 -1
- package/dist/interaction/dragging.js +149 -44
- package/dist/interaction/hit-testing.d.ts +16 -1
- package/dist/interaction/hit-testing.d.ts.map +1 -1
- package/dist/interaction/hit-testing.js +25 -4
- package/dist/interaction/overlay-geometry.d.ts +15 -0
- package/dist/interaction/overlay-geometry.d.ts.map +1 -0
- package/dist/interaction/overlay-geometry.js +228 -0
- package/dist/interaction/touch-precision-drawing.d.ts +29 -0
- package/dist/interaction/touch-precision-drawing.d.ts.map +1 -0
- package/dist/interaction/touch-precision-drawing.js +273 -0
- package/dist/static-data-loader.d.ts +2 -3
- package/dist/static-data-loader.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/adapter.js
CHANGED
|
@@ -8,12 +8,15 @@ import { createSceneOverlays, fromEngineOverlay, normalizeSceneOverlayPrices, to
|
|
|
8
8
|
import { normalizePriceValue } from './conversion/price.js';
|
|
9
9
|
import { applyViewport } from './conversion/viewport.js';
|
|
10
10
|
import { createStaticDataLoader } from './static-data-loader.js';
|
|
11
|
+
import { assertGapAwareSceneSupported, engineDataForScene, GAP_AWARE_CANDLE_INDICATOR_ID, gapCount, installGapAwareMainSeries, isGapAwareScene, timelineItemOf, timelineSlotCount, } from './gap-aware-series.js';
|
|
11
12
|
import { toOverlayStyles } from './conversion/overlays.js';
|
|
12
13
|
import { drawingToSceneOverlay, sceneOverlayToDrawing, } from './drawing/overlay-conversion.js';
|
|
13
14
|
import { MainSeriesPresentationError, presentationToSceneCandle, STANDARD_CLOSE_LINE_PRESENTATION, } from './main-series-presentation.js';
|
|
14
15
|
import { createDragCandidate, } from './interaction/dragging.js';
|
|
15
|
-
import { hitTestOverlayGeometries, } from './interaction/hit-testing.js';
|
|
16
|
+
import { DEFAULT_OVERLAY_MOUSE_HIT_TOLERANCE, DEFAULT_OVERLAY_TOUCH_HIT_TOLERANCE, hitTestOverlayGeometries, } from './interaction/hit-testing.js';
|
|
17
|
+
import { projectOverlayGeometry } from './interaction/overlay-geometry.js';
|
|
16
18
|
import { shouldIgnoreStaleOverlayDeselection } from './interaction/selection-arbitration.js';
|
|
19
|
+
import { isTouchPrecisionTap, resolveTouchPrecisionCursor, TouchPrecisionDrawingGuide, } from './interaction/touch-precision-drawing.js';
|
|
17
20
|
function promoteSceneToM2(scene, scale) {
|
|
18
21
|
const candidate = structuredClone(scene);
|
|
19
22
|
candidate.runtime.runtimeVersion = '0.2.0';
|
|
@@ -41,12 +44,19 @@ export class KLineChartsSceneAdapter {
|
|
|
41
44
|
#engine;
|
|
42
45
|
/** 引擎点击仲裁显式复位;每次绘制开始前调用,保证新绘制首击独立成单击。 */
|
|
43
46
|
#resetClickArbitration;
|
|
47
|
+
/** 向引擎派发鼠标移动语义,供触摸虚拟光标复用原生绘制预览。 */
|
|
48
|
+
#dispatchEngineMouseMove;
|
|
49
|
+
/** 向引擎派发鼠标单击语义,供触摸轻点复用原生绘制落点。 */
|
|
50
|
+
#dispatchEngineMouseClick;
|
|
44
51
|
/** 引擎十字线动作监听器集合。 */
|
|
45
52
|
#crosshairListeners = new Set();
|
|
46
53
|
/** 引擎十字线动作退订函数。 */
|
|
47
54
|
#unsubscribeCrosshair = null;
|
|
48
55
|
/** 引擎十字线动作的绑定处理函数。 */
|
|
49
56
|
#handleCrosshairChange = (payload) => {
|
|
57
|
+
if (this.#chartInteractionSnapshot !== undefined) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
50
60
|
const event = (typeof payload === 'object' && payload !== null
|
|
51
61
|
? payload
|
|
52
62
|
: {});
|
|
@@ -74,14 +84,16 @@ export class KLineChartsSceneAdapter {
|
|
|
74
84
|
}
|
|
75
85
|
const match = barIndex === null ? undefined : data[barIndex];
|
|
76
86
|
const timestamp = match?.timestamp ?? null;
|
|
77
|
-
const
|
|
87
|
+
const timelineItem = timelineItemOf(match);
|
|
88
|
+
const source = timelineItem?.kind === 'bar' ? timelineItem.bar : match;
|
|
89
|
+
const bar = source === undefined || timelineItem?.kind === 'gap'
|
|
78
90
|
? null
|
|
79
91
|
: {
|
|
80
|
-
open:
|
|
81
|
-
high:
|
|
82
|
-
low:
|
|
83
|
-
close:
|
|
84
|
-
volume:
|
|
92
|
+
open: source.open,
|
|
93
|
+
high: source.high,
|
|
94
|
+
low: source.low,
|
|
95
|
+
close: source.close,
|
|
96
|
+
volume: source.volume ?? null,
|
|
85
97
|
};
|
|
86
98
|
const snapshot = { timestamp, bar };
|
|
87
99
|
for (const listener of this.#crosshairListeners) {
|
|
@@ -102,12 +114,26 @@ export class KLineChartsSceneAdapter {
|
|
|
102
114
|
#listeners = new Set();
|
|
103
115
|
/** 当前选择状态,null 表示明确未选择。 */
|
|
104
116
|
#selectedOverlayId = null;
|
|
117
|
+
/** 独占选择前的图表交互状态,用于无损恢复宿主原配置。 */
|
|
118
|
+
#chartInteractionSnapshot;
|
|
119
|
+
/** 点击空白退出编辑时被完整消费的 Pointer ID。 */
|
|
120
|
+
#deselectingPointerId;
|
|
105
121
|
/** 当前受控拖动事务;progress 永不写入 #scene。 */
|
|
106
122
|
#pointerInteraction;
|
|
107
123
|
/** 当前交互式量度的首锚点;只用于补偿引擎丢弃快速第二击,不进入 Scene。 */
|
|
108
124
|
#interactivePriceMeasurement;
|
|
109
125
|
/** 当前引擎进行中的绘制 Overlay ID;非 null 时 pointerdown 只路由给新绘制,不做命中测试。 */
|
|
110
126
|
#drawingInProgressId = null;
|
|
127
|
+
/** 宿主显式开启的 Drawing 输入策略;未配置时保持引擎原生行为。 */
|
|
128
|
+
#drawingInteraction;
|
|
129
|
+
/** 触摸精确绘制的通用提示与虚拟光标层。 */
|
|
130
|
+
#touchPrecisionGuide;
|
|
131
|
+
/** 当前允许切换到触摸精确交互的线段绘制。 */
|
|
132
|
+
#touchPrecisionDrawing;
|
|
133
|
+
/** 当前单指定位手势;拖动只更新光标,轻点才确认。 */
|
|
134
|
+
#touchPrecisionPointer;
|
|
135
|
+
/** 阻断触摸结束后浏览器补发的兼容鼠标事件。 */
|
|
136
|
+
#suppressCompatibilityMouseUntil = 0;
|
|
111
137
|
/** 确定性 opaque 交互 ID 序号。 */
|
|
112
138
|
#interactionSequence = 0;
|
|
113
139
|
/** 显式 Workspace 模式;Legacy 与 Workspace 状态严格隔离。 */
|
|
@@ -135,14 +161,19 @@ export class KLineChartsSceneAdapter {
|
|
|
135
161
|
horizontal: false,
|
|
136
162
|
vertical: false,
|
|
137
163
|
};
|
|
138
|
-
constructor(container, scene, handle, idMap, originalBackground) {
|
|
164
|
+
constructor(container, scene, handle, idMap, originalBackground, drawingInteraction = {}) {
|
|
139
165
|
this.#container = container;
|
|
140
166
|
this.#scene = scene;
|
|
141
167
|
this.#chart = handle.chart;
|
|
142
168
|
this.#engine = handle.module;
|
|
143
169
|
this.#resetClickArbitration = handle.resetClickArbitration;
|
|
170
|
+
this.#dispatchEngineMouseMove = handle.dispatchMouseMove;
|
|
171
|
+
this.#dispatchEngineMouseClick = handle.dispatchMouseClick;
|
|
144
172
|
this.#idMap = idMap;
|
|
145
173
|
this.#originalBackground = originalBackground;
|
|
174
|
+
this.#drawingInteraction = structuredClone(drawingInteraction);
|
|
175
|
+
this.#hitTolerance('mouse');
|
|
176
|
+
this.#hitTolerance('touch');
|
|
146
177
|
this.#installInteractionListeners();
|
|
147
178
|
this.#chart.subscribeAction('onCrosshairChange', this.#handleCrosshairChange);
|
|
148
179
|
this.#unsubscribeCrosshair = () => {
|
|
@@ -156,6 +187,7 @@ export class KLineChartsSceneAdapter {
|
|
|
156
187
|
let adapter;
|
|
157
188
|
try {
|
|
158
189
|
handle = await createEngine(container, scene);
|
|
190
|
+
installGapAwareMainSeries(scene, handle.chart, handle.module.registerIndicator);
|
|
159
191
|
registerProjectOverlays(handle.module.registerOverlay);
|
|
160
192
|
const idMap = createEngineIdMap(scene, handle.chart);
|
|
161
193
|
applyPanes(scene, handle.chart, idMap);
|
|
@@ -193,10 +225,11 @@ export class KLineChartsSceneAdapter {
|
|
|
193
225
|
? {}
|
|
194
226
|
: { displayTimezone: options.displayTimezone }),
|
|
195
227
|
});
|
|
228
|
+
installGapAwareMainSeries(scene, handle.chart, handle.module.registerIndicator);
|
|
196
229
|
registerProjectOverlays(handle.module.registerOverlay);
|
|
197
230
|
const idMap = createEngineIdMap(scene, handle.chart);
|
|
198
231
|
applyPanes(scene, handle.chart, idMap);
|
|
199
|
-
adapter = new KLineChartsSceneAdapter(container, scene, handle, idMap, originalBackground);
|
|
232
|
+
adapter = new KLineChartsSceneAdapter(container, scene, handle, idMap, originalBackground, options?.drawingInteraction);
|
|
200
233
|
applyViewport(handle.chart, scene.viewport);
|
|
201
234
|
container.style.backgroundColor = scene.chart.layout.backgroundColor;
|
|
202
235
|
adapter.#workspaceMode = true;
|
|
@@ -279,7 +312,7 @@ export class KLineChartsSceneAdapter {
|
|
|
279
312
|
}
|
|
280
313
|
}
|
|
281
314
|
#historicalDataLoader(scene) {
|
|
282
|
-
const snapshot = structuredClone(scene
|
|
315
|
+
const snapshot = structuredClone(engineDataForScene(scene));
|
|
283
316
|
return {
|
|
284
317
|
getBars: ({ type, timestamp, callback }) => {
|
|
285
318
|
if (type === 'init') {
|
|
@@ -359,6 +392,12 @@ export class KLineChartsSceneAdapter {
|
|
|
359
392
|
if (previousId === id) {
|
|
360
393
|
return;
|
|
361
394
|
}
|
|
395
|
+
if (previousId === null && id !== null) {
|
|
396
|
+
this.#enterExclusiveSelection();
|
|
397
|
+
}
|
|
398
|
+
else if (previousId !== null && id === null) {
|
|
399
|
+
this.#leaveExclusiveSelection();
|
|
400
|
+
}
|
|
362
401
|
this.#selectedOverlayId = id;
|
|
363
402
|
if (this.#workspaceMode) {
|
|
364
403
|
this.#emitPort({
|
|
@@ -372,7 +411,63 @@ export class KLineChartsSceneAdapter {
|
|
|
372
411
|
this.#emit({ type: 'overlay-selected', id });
|
|
373
412
|
}
|
|
374
413
|
}
|
|
414
|
+
#enterExclusiveSelection() {
|
|
415
|
+
if (this.#drawingInteraction.exclusiveSelection !== true ||
|
|
416
|
+
this.#chartInteractionSnapshot !== undefined) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
this.#chartInteractionSnapshot = {
|
|
420
|
+
scrollEnabled: this.#chart.isScrollEnabled(),
|
|
421
|
+
zoomEnabled: this.#chart.isZoomEnabled(),
|
|
422
|
+
crosshairVisible: this.#chart.getStyles().crosshair.show,
|
|
423
|
+
};
|
|
424
|
+
this.#chart.setScrollEnabled(false);
|
|
425
|
+
this.#chart.setZoomEnabled(false);
|
|
426
|
+
this.#chart.setStyles({ crosshair: { show: false } });
|
|
427
|
+
}
|
|
428
|
+
#leaveExclusiveSelection() {
|
|
429
|
+
const snapshot = this.#chartInteractionSnapshot;
|
|
430
|
+
if (snapshot === undefined) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
this.#chartInteractionSnapshot = undefined;
|
|
434
|
+
this.#chart.setScrollEnabled(snapshot.scrollEnabled);
|
|
435
|
+
this.#chart.setZoomEnabled(snapshot.zoomEnabled);
|
|
436
|
+
this.#chart.setStyles({ crosshair: { show: snapshot.crosshairVisible } });
|
|
437
|
+
}
|
|
438
|
+
#hitTolerance(pointerType) {
|
|
439
|
+
const touch = pointerType === 'touch';
|
|
440
|
+
const defaults = touch
|
|
441
|
+
? DEFAULT_OVERLAY_TOUCH_HIT_TOLERANCE
|
|
442
|
+
: DEFAULT_OVERLAY_MOUSE_HIT_TOLERANCE;
|
|
443
|
+
const configured = touch
|
|
444
|
+
? this.#drawingInteraction.hitTolerance?.touch
|
|
445
|
+
: this.#drawingInteraction.hitTolerance?.mouse;
|
|
446
|
+
const body = configured?.body ?? defaults.body;
|
|
447
|
+
const anchor = configured?.anchor ?? defaults.anchor;
|
|
448
|
+
if (!Number.isFinite(body) || body < 0 || !Number.isFinite(anchor) || anchor < 0) {
|
|
449
|
+
throw new SceneError('RUNTIME_INIT_FAILED', '/drawingInteraction/hitTolerance', 'Drawing hit tolerance must contain finite non-negative CSS pixel values.');
|
|
450
|
+
}
|
|
451
|
+
return { body, anchor };
|
|
452
|
+
}
|
|
375
453
|
#dimensionsForHit(overlay, hit) {
|
|
454
|
+
const constrainedType = overlay.type === 'horizontalStraightLine'
|
|
455
|
+
|| overlay.type === 'priceLine'
|
|
456
|
+
|| overlay.type === 'simpleTag'
|
|
457
|
+
|| overlay.type === 'horizontalRayLine'
|
|
458
|
+
|| overlay.type === 'horizontalSegment'
|
|
459
|
+
? 'vertical'
|
|
460
|
+
: overlay.type === 'verticalStraightLine'
|
|
461
|
+
|| overlay.type === 'verticalRayLine'
|
|
462
|
+
|| overlay.type === 'verticalSegment'
|
|
463
|
+
? 'horizontal'
|
|
464
|
+
: null;
|
|
465
|
+
if (constrainedType === 'vertical') {
|
|
466
|
+
return { horizontal: false, vertical: true };
|
|
467
|
+
}
|
|
468
|
+
if (constrainedType === 'horizontal') {
|
|
469
|
+
return { horizontal: true, vertical: false };
|
|
470
|
+
}
|
|
376
471
|
if (hit.target !== 'anchor') {
|
|
377
472
|
return { horizontal: true, vertical: true };
|
|
378
473
|
}
|
|
@@ -430,6 +525,7 @@ export class KLineChartsSceneAdapter {
|
|
|
430
525
|
preventDefault?.();
|
|
431
526
|
},
|
|
432
527
|
onDrawEnd: ({ overlay }) => {
|
|
528
|
+
this.#finishTouchPrecisionDrawing(overlay.id);
|
|
433
529
|
if (drawing &&
|
|
434
530
|
this.#interactivePriceMeasurement?.source.id === source.id) {
|
|
435
531
|
this.#interactivePriceMeasurement = undefined;
|
|
@@ -463,6 +559,7 @@ export class KLineChartsSceneAdapter {
|
|
|
463
559
|
}
|
|
464
560
|
},
|
|
465
561
|
onRemoved: ({ overlay }) => {
|
|
562
|
+
this.#finishTouchPrecisionDrawing(overlay.id);
|
|
466
563
|
if (this.#interactivePriceMeasurement?.source.id === overlay.id) {
|
|
467
564
|
this.#interactivePriceMeasurement = undefined;
|
|
468
565
|
}
|
|
@@ -496,32 +593,191 @@ export class KLineChartsSceneAdapter {
|
|
|
496
593
|
throw error;
|
|
497
594
|
}
|
|
498
595
|
}
|
|
596
|
+
#prepareTouchPrecisionDrawing(id, paneId) {
|
|
597
|
+
if (this.#drawingInteraction.touch !== 'precision-cursor') {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
this.#finishTouchPrecisionDrawing();
|
|
601
|
+
this.#touchPrecisionDrawing = {
|
|
602
|
+
id,
|
|
603
|
+
paneId,
|
|
604
|
+
phase: 'move-start',
|
|
605
|
+
};
|
|
606
|
+
const view = this.#container.ownerDocument.defaultView;
|
|
607
|
+
if ((view?.navigator.maxTouchPoints ?? 0) > 0 ||
|
|
608
|
+
view?.matchMedia('(any-pointer: coarse)').matches === true) {
|
|
609
|
+
const guide = this.#ensureTouchPrecisionGuide();
|
|
610
|
+
guide.setPhase('move-start');
|
|
611
|
+
guide.show();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
#ensureTouchPrecisionGuide() {
|
|
615
|
+
this.#touchPrecisionGuide ??= new TouchPrecisionDrawingGuide(this.#container, () => this.#cancelTouchPrecisionDrawing(true));
|
|
616
|
+
return this.#touchPrecisionGuide;
|
|
617
|
+
}
|
|
618
|
+
#finishTouchPrecisionDrawing(id) {
|
|
619
|
+
const drawing = this.#touchPrecisionDrawing;
|
|
620
|
+
if (drawing === undefined || (id !== undefined && drawing.id !== id)) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
this.#stopTouchPrecisionPointerCapture();
|
|
624
|
+
this.#touchPrecisionPointer = undefined;
|
|
625
|
+
this.#touchPrecisionDrawing = undefined;
|
|
626
|
+
this.#touchPrecisionGuide?.hide();
|
|
627
|
+
}
|
|
628
|
+
#cancelTouchPrecisionDrawing(emitRemoval) {
|
|
629
|
+
const drawing = this.#touchPrecisionDrawing;
|
|
630
|
+
if (drawing === undefined) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const id = drawing.id;
|
|
634
|
+
this.#finishTouchPrecisionDrawing(id);
|
|
635
|
+
this.#suppressCompatibilityMouseUntil = performance.now() + 800;
|
|
636
|
+
this.#chart.removeOverlay({ id });
|
|
637
|
+
if (this.#drawingInProgressId === id) {
|
|
638
|
+
this.#drawingInProgressId = null;
|
|
639
|
+
}
|
|
640
|
+
if (this.#workspaceMode) {
|
|
641
|
+
const removed = this.#workspaceSources.delete(id);
|
|
642
|
+
if (emitRemoval && removed) {
|
|
643
|
+
this.#emitPort({ type: 'removed', id });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
#stopTouchPrecisionPointerCapture() {
|
|
648
|
+
const pointer = this.#touchPrecisionPointer;
|
|
649
|
+
if (pointer === undefined) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
if (this.#container.hasPointerCapture(pointer.pointerId)) {
|
|
654
|
+
this.#container.releasePointerCapture(pointer.pointerId);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
// Synthetic browser tests and interrupted system gestures may not own capture.
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
#touchPrecisionMainElement(paneId) {
|
|
662
|
+
const filter = this.#primaryAxisFilter(paneId);
|
|
663
|
+
const main = this.#chart.getDom(filter.paneId, 'main');
|
|
664
|
+
if (!(main instanceof HTMLElement)) {
|
|
665
|
+
throw new SceneError('INVALID_REFERENCE', '/drawings/target', 'Touch Drawing target Pane has no interactive main element.');
|
|
666
|
+
}
|
|
667
|
+
return main;
|
|
668
|
+
}
|
|
669
|
+
#touchPrecisionBounds(paneId) {
|
|
670
|
+
const containerRect = this.#container.getBoundingClientRect();
|
|
671
|
+
const mainRect = this.#touchPrecisionMainElement(paneId).getBoundingClientRect();
|
|
672
|
+
return {
|
|
673
|
+
left: Math.max(0, mainRect.left - containerRect.left),
|
|
674
|
+
top: Math.max(0, mainRect.top - containerRect.top),
|
|
675
|
+
right: Math.min(containerRect.width, mainRect.right - containerRect.left),
|
|
676
|
+
bottom: Math.min(containerRect.height, mainRect.bottom - containerRect.top),
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
#dispatchTouchPrecisionMouseMove(point) {
|
|
680
|
+
const rect = this.#container.getBoundingClientRect();
|
|
681
|
+
this.#dispatchEngineMouseMove({
|
|
682
|
+
x: point.x,
|
|
683
|
+
y: point.y,
|
|
684
|
+
pageX: rect.left + point.x + window.scrollX,
|
|
685
|
+
pageY: rect.top + point.y + window.scrollY,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
#dispatchTouchPrecisionClick(point) {
|
|
689
|
+
const rect = this.#container.getBoundingClientRect();
|
|
690
|
+
this.#resetClickArbitration();
|
|
691
|
+
this.#dispatchEngineMouseClick({
|
|
692
|
+
x: point.x,
|
|
693
|
+
y: point.y,
|
|
694
|
+
pageX: rect.left + point.x + window.scrollX,
|
|
695
|
+
pageY: rect.top + point.y + window.scrollY,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
#updateTouchPrecisionCursor(drawing, pointer) {
|
|
699
|
+
const cursor = resolveTouchPrecisionCursor(pointer, this.#touchPrecisionBounds(drawing.paneId));
|
|
700
|
+
this.#touchPrecisionGuide?.updateCursor(cursor);
|
|
701
|
+
this.#dispatchTouchPrecisionMouseMove(cursor);
|
|
702
|
+
return cursor;
|
|
703
|
+
}
|
|
704
|
+
#isTouchPrecisionCancelTarget(target) {
|
|
705
|
+
return this.#touchPrecisionGuide?.ownsCancelTarget(target) ?? false;
|
|
706
|
+
}
|
|
499
707
|
#installInteractionListeners() {
|
|
500
708
|
this.#container.addEventListener('mousedown', this.#handleRightMouseDown, true);
|
|
709
|
+
this.#container.addEventListener('click', this.#handleCompatibilityClick, true);
|
|
501
710
|
this.#container.addEventListener('contextmenu', this.#handleContextMenu, true);
|
|
502
711
|
this.#container.addEventListener('pointerdown', this.#handlePointerDown, true);
|
|
503
712
|
this.#container.addEventListener('pointermove', this.#handlePointerMove, true);
|
|
504
713
|
this.#container.addEventListener('pointerup', this.#handlePointerUp, true);
|
|
505
714
|
this.#container.addEventListener('pointercancel', this.#handlePointerCancel, true);
|
|
715
|
+
this.#container.addEventListener('touchstart', this.#handleCompatibilityTouch, {
|
|
716
|
+
capture: true,
|
|
717
|
+
passive: false,
|
|
718
|
+
});
|
|
719
|
+
this.#container.addEventListener('touchmove', this.#handleCompatibilityTouch, {
|
|
720
|
+
capture: true,
|
|
721
|
+
passive: false,
|
|
722
|
+
});
|
|
723
|
+
this.#container.addEventListener('touchend', this.#handleCompatibilityTouch, {
|
|
724
|
+
capture: true,
|
|
725
|
+
passive: false,
|
|
726
|
+
});
|
|
727
|
+
this.#container.addEventListener('touchcancel', this.#handleCompatibilityTouch, {
|
|
728
|
+
capture: true,
|
|
729
|
+
passive: false,
|
|
730
|
+
});
|
|
506
731
|
window.addEventListener('keydown', this.#handleKeyDown);
|
|
507
732
|
window.addEventListener('blur', this.#handleWindowBlur);
|
|
508
733
|
}
|
|
509
734
|
#removeInteractionListeners() {
|
|
510
735
|
this.#container.removeEventListener('mousedown', this.#handleRightMouseDown, true);
|
|
736
|
+
this.#container.removeEventListener('click', this.#handleCompatibilityClick, true);
|
|
511
737
|
this.#container.removeEventListener('contextmenu', this.#handleContextMenu, true);
|
|
512
738
|
this.#container.removeEventListener('pointerdown', this.#handlePointerDown, true);
|
|
513
739
|
this.#container.removeEventListener('pointermove', this.#handlePointerMove, true);
|
|
514
740
|
this.#container.removeEventListener('pointerup', this.#handlePointerUp, true);
|
|
515
741
|
this.#container.removeEventListener('pointercancel', this.#handlePointerCancel, true);
|
|
742
|
+
this.#container.removeEventListener('touchstart', this.#handleCompatibilityTouch, true);
|
|
743
|
+
this.#container.removeEventListener('touchmove', this.#handleCompatibilityTouch, true);
|
|
744
|
+
this.#container.removeEventListener('touchend', this.#handleCompatibilityTouch, true);
|
|
745
|
+
this.#container.removeEventListener('touchcancel', this.#handleCompatibilityTouch, true);
|
|
516
746
|
window.removeEventListener('keydown', this.#handleKeyDown);
|
|
517
747
|
window.removeEventListener('blur', this.#handleWindowBlur);
|
|
518
748
|
}
|
|
519
749
|
#handleRightMouseDown = (event) => {
|
|
750
|
+
if (event.button === 0 &&
|
|
751
|
+
event.isTrusted &&
|
|
752
|
+
performance.now() < this.#suppressCompatibilityMouseUntil &&
|
|
753
|
+
!this.#isTouchPrecisionCancelTarget(event.target)) {
|
|
754
|
+
event.preventDefault();
|
|
755
|
+
event.stopImmediatePropagation();
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
520
758
|
if (event.button === 2) {
|
|
521
759
|
// 阻止事件到达 KLineCharts;不 preventDefault,保留浏览器原生右键菜单。
|
|
522
760
|
event.stopPropagation();
|
|
523
761
|
}
|
|
524
762
|
};
|
|
763
|
+
#handleCompatibilityClick = (event) => {
|
|
764
|
+
if (event.isTrusted &&
|
|
765
|
+
performance.now() < this.#suppressCompatibilityMouseUntil &&
|
|
766
|
+
!this.#isTouchPrecisionCancelTarget(event.target)) {
|
|
767
|
+
event.preventDefault();
|
|
768
|
+
event.stopImmediatePropagation();
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
#handleCompatibilityTouch = (event) => {
|
|
772
|
+
if ((this.#touchPrecisionDrawing !== undefined ||
|
|
773
|
+
performance.now() < this.#suppressCompatibilityMouseUntil) &&
|
|
774
|
+
!this.#isTouchPrecisionCancelTarget(event.target)) {
|
|
775
|
+
if (event.cancelable) {
|
|
776
|
+
event.preventDefault();
|
|
777
|
+
}
|
|
778
|
+
event.stopImmediatePropagation();
|
|
779
|
+
}
|
|
780
|
+
};
|
|
525
781
|
#handleContextMenu = (event) => {
|
|
526
782
|
// KLineCharts 会 preventDefault。捕获阶段截断传播,但 Baron 本身不拦截菜单。
|
|
527
783
|
event.stopPropagation();
|
|
@@ -598,60 +854,40 @@ export class KLineChartsSceneAdapter {
|
|
|
598
854
|
#overlayGeometries() {
|
|
599
855
|
const geometries = [];
|
|
600
856
|
const active = this.#activeOverlays();
|
|
857
|
+
const containerRect = this.#container.getBoundingClientRect();
|
|
858
|
+
const canvas = this.#container.ownerDocument.createElement('canvas');
|
|
859
|
+
const textContext = canvas.getContext('2d');
|
|
601
860
|
for (let sceneIndex = 0; sceneIndex < active.length; sceneIndex++) {
|
|
602
861
|
const overlay = active[sceneIndex];
|
|
603
862
|
if (overlay === undefined || !overlay.visible) {
|
|
604
863
|
continue;
|
|
605
864
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
geometries.push(
|
|
632
|
-
overlayId: overlay.id,
|
|
633
|
-
sceneIndex,
|
|
634
|
-
zLevel: overlay.zLevel,
|
|
635
|
-
locked: overlay.locked,
|
|
636
|
-
anchors: [start, end],
|
|
637
|
-
bodySegments: [[start, end]],
|
|
638
|
-
});
|
|
639
|
-
continue;
|
|
640
|
-
}
|
|
641
|
-
if (overlay.points !== undefined && overlay.points.length >= 2) {
|
|
642
|
-
const anchors = overlay.points.map((point) => this.#toPixel(point, overlay.paneId));
|
|
643
|
-
const bodySegments = [];
|
|
644
|
-
for (let pointIndex = 1; pointIndex < anchors.length; pointIndex++) {
|
|
645
|
-
bodySegments.push([anchors[pointIndex - 1], anchors[pointIndex]]);
|
|
646
|
-
}
|
|
647
|
-
geometries.push({
|
|
648
|
-
overlayId: overlay.id,
|
|
649
|
-
sceneIndex,
|
|
650
|
-
zLevel: overlay.zLevel,
|
|
651
|
-
locked: overlay.locked,
|
|
652
|
-
anchors,
|
|
653
|
-
bodySegments,
|
|
654
|
-
});
|
|
865
|
+
const paneFilter = this.#primaryAxisFilter(overlay.paneId);
|
|
866
|
+
const paneMain = this.#chart.getDom(paneFilter.paneId, 'main');
|
|
867
|
+
const mainRect = paneMain?.getBoundingClientRect() ?? containerRect;
|
|
868
|
+
const geometry = projectOverlayGeometry(overlay, sceneIndex, {
|
|
869
|
+
bounds: {
|
|
870
|
+
left: mainRect.left - containerRect.left,
|
|
871
|
+
top: mainRect.top - containerRect.top,
|
|
872
|
+
right: mainRect.right - containerRect.left,
|
|
873
|
+
bottom: mainRect.bottom - containerRect.top,
|
|
874
|
+
},
|
|
875
|
+
referenceTimestamp: this.#scene.data[0].timestamp,
|
|
876
|
+
referenceValue: this.#scene.data[0].close,
|
|
877
|
+
project: (point) => this.#toPixel(point, overlay.paneId),
|
|
878
|
+
measureText: (text, source) => {
|
|
879
|
+
const textStyle = source.styles.text;
|
|
880
|
+
if (textContext !== null) {
|
|
881
|
+
textContext.font = `${textStyle.weight} ${textStyle.size}px ${textStyle.family}`;
|
|
882
|
+
}
|
|
883
|
+
return {
|
|
884
|
+
width: Math.max(textStyle.size, textContext?.measureText(text).width ?? [...text].length * textStyle.size),
|
|
885
|
+
height: textStyle.size * 1.4,
|
|
886
|
+
};
|
|
887
|
+
},
|
|
888
|
+
});
|
|
889
|
+
if (geometry !== null) {
|
|
890
|
+
geometries.push(geometry);
|
|
655
891
|
}
|
|
656
892
|
}
|
|
657
893
|
return geometries;
|
|
@@ -695,13 +931,102 @@ export class KLineChartsSceneAdapter {
|
|
|
695
931
|
this.#emit({ type: 'scene-error', issues: structuredClone(error.issues) });
|
|
696
932
|
}
|
|
697
933
|
}
|
|
934
|
+
#routeTouchPrecisionPointerDown(event) {
|
|
935
|
+
const drawing = this.#touchPrecisionDrawing;
|
|
936
|
+
if (drawing === undefined || this.#isTouchPrecisionCancelTarget(event.target)) {
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
if (event.pointerType !== 'touch') {
|
|
940
|
+
this.#finishTouchPrecisionDrawing(drawing.id);
|
|
941
|
+
return false;
|
|
942
|
+
}
|
|
943
|
+
event.preventDefault();
|
|
944
|
+
event.stopImmediatePropagation();
|
|
945
|
+
if (this.#touchPrecisionPointer !== undefined) {
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
948
|
+
const guide = this.#ensureTouchPrecisionGuide();
|
|
949
|
+
guide.setPhase(drawing.phase);
|
|
950
|
+
guide.show();
|
|
951
|
+
const point = this.#pointerCoordinate(event);
|
|
952
|
+
this.#touchPrecisionPointer = {
|
|
953
|
+
pointerId: event.pointerId,
|
|
954
|
+
origin: point,
|
|
955
|
+
current: point,
|
|
956
|
+
};
|
|
957
|
+
try {
|
|
958
|
+
this.#container.setPointerCapture(event.pointerId);
|
|
959
|
+
}
|
|
960
|
+
catch {
|
|
961
|
+
// Synthetic PointerEvents do not own browser pointer capture.
|
|
962
|
+
}
|
|
963
|
+
this.#suppressCompatibilityMouseUntil = performance.now() + 800;
|
|
964
|
+
this.#updateTouchPrecisionCursor(drawing, point);
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
#routeTouchPrecisionPointerMove(event) {
|
|
968
|
+
const drawing = this.#touchPrecisionDrawing;
|
|
969
|
+
const pointer = this.#touchPrecisionPointer;
|
|
970
|
+
if (drawing === undefined ||
|
|
971
|
+
pointer === undefined ||
|
|
972
|
+
pointer.pointerId !== event.pointerId) {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
event.preventDefault();
|
|
976
|
+
event.stopImmediatePropagation();
|
|
977
|
+
const point = this.#pointerCoordinate(event);
|
|
978
|
+
pointer.current = point;
|
|
979
|
+
this.#suppressCompatibilityMouseUntil = performance.now() + 800;
|
|
980
|
+
this.#updateTouchPrecisionCursor(drawing, point);
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
983
|
+
#routeTouchPrecisionPointerUp(event) {
|
|
984
|
+
const drawing = this.#touchPrecisionDrawing;
|
|
985
|
+
const pointer = this.#touchPrecisionPointer;
|
|
986
|
+
if (drawing === undefined ||
|
|
987
|
+
pointer === undefined ||
|
|
988
|
+
pointer.pointerId !== event.pointerId) {
|
|
989
|
+
return false;
|
|
990
|
+
}
|
|
991
|
+
event.preventDefault();
|
|
992
|
+
event.stopImmediatePropagation();
|
|
993
|
+
const point = this.#pointerCoordinate(event);
|
|
994
|
+
pointer.current = point;
|
|
995
|
+
const cursor = this.#updateTouchPrecisionCursor(drawing, point);
|
|
996
|
+
const tap = isTouchPrecisionTap(pointer.origin, pointer.current);
|
|
997
|
+
this.#stopTouchPrecisionPointerCapture();
|
|
998
|
+
this.#touchPrecisionPointer = undefined;
|
|
999
|
+
this.#suppressCompatibilityMouseUntil = performance.now() + 800;
|
|
1000
|
+
if (drawing.phase === 'move-start') {
|
|
1001
|
+
drawing.phase = 'confirm-start';
|
|
1002
|
+
this.#touchPrecisionGuide?.setPhase('confirm-start');
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
if (!tap) {
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
const phase = drawing.phase;
|
|
1009
|
+
this.#dispatchTouchPrecisionClick(cursor);
|
|
1010
|
+
const active = this.#touchPrecisionDrawing;
|
|
1011
|
+
if (phase === 'confirm-start' &&
|
|
1012
|
+
active !== undefined &&
|
|
1013
|
+
active.id === drawing.id) {
|
|
1014
|
+
active.phase = 'confirm-end';
|
|
1015
|
+
this.#touchPrecisionGuide?.setPhase('confirm-end');
|
|
1016
|
+
}
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
698
1019
|
#handlePointerDown = (event) => {
|
|
699
1020
|
if (this.#disposed ||
|
|
700
1021
|
event.button !== 0 ||
|
|
701
1022
|
this.#pointerInteraction !== undefined ||
|
|
1023
|
+
this.#deselectingPointerId !== undefined ||
|
|
702
1024
|
!this.#mutationsEnabled) {
|
|
703
1025
|
return;
|
|
704
1026
|
}
|
|
1027
|
+
if (this.#routeTouchPrecisionPointerDown(event)) {
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
705
1030
|
const coordinate = this.#pointerCoordinate(event);
|
|
706
1031
|
const measurement = this.#interactivePriceMeasurement;
|
|
707
1032
|
if (measurement !== undefined) {
|
|
@@ -732,9 +1057,22 @@ export class KLineChartsSceneAdapter {
|
|
|
732
1057
|
if (this.#drawingInProgressId !== null) {
|
|
733
1058
|
return;
|
|
734
1059
|
}
|
|
735
|
-
const hit = hitTestOverlayGeometries(coordinate, this.#overlayGeometries());
|
|
1060
|
+
const hit = hitTestOverlayGeometries(coordinate, this.#overlayGeometries(), this.#hitTolerance(event.pointerType));
|
|
736
1061
|
if (hit === null) {
|
|
737
1062
|
const selected = this.#activeOverlays().find((overlay) => overlay.id === this.#selectedOverlayId);
|
|
1063
|
+
if (selected !== undefined &&
|
|
1064
|
+
this.#drawingInteraction.exclusiveSelection === true) {
|
|
1065
|
+
event.preventDefault();
|
|
1066
|
+
event.stopImmediatePropagation();
|
|
1067
|
+
this.#deselectingPointerId = event.pointerId;
|
|
1068
|
+
try {
|
|
1069
|
+
this.#container.setPointerCapture(event.pointerId);
|
|
1070
|
+
}
|
|
1071
|
+
catch {
|
|
1072
|
+
// Synthetic PointerEvents do not own browser pointer capture.
|
|
1073
|
+
}
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
738
1076
|
if (selected !== undefined && isControlledInteractionOverlay(selected)) {
|
|
739
1077
|
this.#selectOverlay(null);
|
|
740
1078
|
}
|
|
@@ -745,7 +1083,8 @@ export class KLineChartsSceneAdapter {
|
|
|
745
1083
|
return;
|
|
746
1084
|
}
|
|
747
1085
|
this.#selectOverlay(before.id);
|
|
748
|
-
if (!isControlledInteractionOverlay(before)
|
|
1086
|
+
if (!isControlledInteractionOverlay(before) &&
|
|
1087
|
+
this.#drawingInteraction.exclusiveSelection !== true) {
|
|
749
1088
|
return;
|
|
750
1089
|
}
|
|
751
1090
|
event.preventDefault();
|
|
@@ -753,7 +1092,12 @@ export class KLineChartsSceneAdapter {
|
|
|
753
1092
|
if (hit.locked) {
|
|
754
1093
|
return;
|
|
755
1094
|
}
|
|
756
|
-
|
|
1095
|
+
try {
|
|
1096
|
+
this.#container.setPointerCapture(event.pointerId);
|
|
1097
|
+
}
|
|
1098
|
+
catch {
|
|
1099
|
+
// Synthetic PointerEvents do not own browser pointer capture.
|
|
1100
|
+
}
|
|
757
1101
|
this.#interactionDimensions = this.#dimensionsForHit(before, hit);
|
|
758
1102
|
this.#pointerInteraction = {
|
|
759
1103
|
pointerId: event.pointerId,
|
|
@@ -766,6 +1110,14 @@ export class KLineChartsSceneAdapter {
|
|
|
766
1110
|
};
|
|
767
1111
|
};
|
|
768
1112
|
#handlePointerMove = (event) => {
|
|
1113
|
+
if (this.#routeTouchPrecisionPointerMove(event)) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (this.#deselectingPointerId === event.pointerId) {
|
|
1117
|
+
event.preventDefault();
|
|
1118
|
+
event.stopImmediatePropagation();
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
769
1121
|
const interaction = this.#pointerInteraction;
|
|
770
1122
|
if (interaction === undefined || interaction.pointerId !== event.pointerId) {
|
|
771
1123
|
return;
|
|
@@ -797,6 +1149,7 @@ export class KLineChartsSceneAdapter {
|
|
|
797
1149
|
throw new SceneError('RUNTIME_INIT_FAILED', `/overlays/${index}`, `KLineCharts failed to preview Overlay ${normalized.id}.`);
|
|
798
1150
|
}
|
|
799
1151
|
if (this.#workspaceMode) {
|
|
1152
|
+
interaction.candidate = candidate;
|
|
800
1153
|
this.#setActiveOverlays(overlays);
|
|
801
1154
|
return;
|
|
802
1155
|
}
|
|
@@ -816,6 +1169,19 @@ export class KLineChartsSceneAdapter {
|
|
|
816
1169
|
}
|
|
817
1170
|
};
|
|
818
1171
|
#handlePointerUp = (event) => {
|
|
1172
|
+
if (this.#routeTouchPrecisionPointerUp(event)) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (this.#deselectingPointerId === event.pointerId) {
|
|
1176
|
+
event.preventDefault();
|
|
1177
|
+
event.stopImmediatePropagation();
|
|
1178
|
+
if (this.#container.hasPointerCapture(event.pointerId)) {
|
|
1179
|
+
this.#container.releasePointerCapture(event.pointerId);
|
|
1180
|
+
}
|
|
1181
|
+
this.#deselectingPointerId = undefined;
|
|
1182
|
+
this.#selectOverlay(null);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
819
1185
|
const interaction = this.#pointerInteraction;
|
|
820
1186
|
if (interaction === undefined || interaction.pointerId !== event.pointerId) {
|
|
821
1187
|
return;
|
|
@@ -823,6 +1189,7 @@ export class KLineChartsSceneAdapter {
|
|
|
823
1189
|
event.preventDefault();
|
|
824
1190
|
event.stopImmediatePropagation();
|
|
825
1191
|
this.#pointerInteraction = undefined;
|
|
1192
|
+
const interactionDimensions = this.#interactionDimensions;
|
|
826
1193
|
this.#interactionDimensions = { horizontal: false, vertical: false };
|
|
827
1194
|
this.#stopPointerCapture(interaction);
|
|
828
1195
|
if (!interaction.started) {
|
|
@@ -841,7 +1208,7 @@ export class KLineChartsSceneAdapter {
|
|
|
841
1208
|
type: 'updated',
|
|
842
1209
|
id: overlay.id,
|
|
843
1210
|
drawing: snapshotOfDrawing(sceneOverlayToDrawing(overlay, drawing)),
|
|
844
|
-
editDimensions: structuredClone(
|
|
1211
|
+
editDimensions: structuredClone(interactionDimensions),
|
|
845
1212
|
});
|
|
846
1213
|
}
|
|
847
1214
|
return;
|
|
@@ -856,21 +1223,56 @@ export class KLineChartsSceneAdapter {
|
|
|
856
1223
|
this.#emit({ type: 'overlay-updated', overlay: committed });
|
|
857
1224
|
};
|
|
858
1225
|
#handlePointerCancel = (event) => {
|
|
1226
|
+
if (this.#touchPrecisionPointer?.pointerId === event.pointerId) {
|
|
1227
|
+
event.preventDefault();
|
|
1228
|
+
event.stopImmediatePropagation();
|
|
1229
|
+
this.#cancelTouchPrecisionDrawing(true);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
859
1232
|
if (this.#pointerInteraction?.pointerId === event.pointerId) {
|
|
860
1233
|
event.preventDefault();
|
|
861
1234
|
event.stopImmediatePropagation();
|
|
862
1235
|
this.#cancelInteraction('pointer-cancel');
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (this.#deselectingPointerId === event.pointerId) {
|
|
1239
|
+
event.preventDefault();
|
|
1240
|
+
event.stopImmediatePropagation();
|
|
1241
|
+
if (this.#container.hasPointerCapture(event.pointerId)) {
|
|
1242
|
+
this.#container.releasePointerCapture(event.pointerId);
|
|
1243
|
+
}
|
|
1244
|
+
this.#deselectingPointerId = undefined;
|
|
1245
|
+
this.#selectOverlay(null);
|
|
863
1246
|
}
|
|
864
1247
|
};
|
|
865
1248
|
#handleKeyDown = (event) => {
|
|
1249
|
+
if (event.key === 'Escape' && this.#touchPrecisionDrawing !== undefined) {
|
|
1250
|
+
this.#cancelTouchPrecisionDrawing(true);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
866
1253
|
if (event.key === 'Escape' && this.#pointerInteraction !== undefined) {
|
|
867
1254
|
this.#cancelInteraction('escape');
|
|
868
1255
|
}
|
|
1256
|
+
if (event.key === 'Escape' &&
|
|
1257
|
+
this.#drawingInteraction.exclusiveSelection === true &&
|
|
1258
|
+
this.#selectedOverlayId !== null) {
|
|
1259
|
+
this.#selectOverlay(null);
|
|
1260
|
+
}
|
|
869
1261
|
};
|
|
870
1262
|
#handleWindowBlur = () => {
|
|
1263
|
+
if (this.#touchPrecisionDrawing !== undefined) {
|
|
1264
|
+
this.#cancelTouchPrecisionDrawing(true);
|
|
1265
|
+
}
|
|
871
1266
|
if (this.#pointerInteraction !== undefined) {
|
|
872
1267
|
this.#cancelInteraction('window-blur');
|
|
873
1268
|
}
|
|
1269
|
+
if (this.#deselectingPointerId !== undefined) {
|
|
1270
|
+
if (this.#container.hasPointerCapture(this.#deselectingPointerId)) {
|
|
1271
|
+
this.#container.releasePointerCapture(this.#deselectingPointerId);
|
|
1272
|
+
}
|
|
1273
|
+
this.#deselectingPointerId = undefined;
|
|
1274
|
+
this.#selectOverlay(null);
|
|
1275
|
+
}
|
|
874
1276
|
};
|
|
875
1277
|
subscribe(listener) {
|
|
876
1278
|
this.#assertActive();
|
|
@@ -922,6 +1324,10 @@ export class KLineChartsSceneAdapter {
|
|
|
922
1324
|
replaceScene(value) {
|
|
923
1325
|
this.#assertActive();
|
|
924
1326
|
const candidate = parseChartScene(value);
|
|
1327
|
+
if (candidate.version !== this.#scene.version) {
|
|
1328
|
+
throw new SceneError('INVALID_REFERENCE', '/version', 'Scene replacement requires the same ChartScene contract version.');
|
|
1329
|
+
}
|
|
1330
|
+
assertGapAwareSceneSupported(candidate);
|
|
925
1331
|
const paneShape = (scene) => JSON.stringify(scene.panes.map((pane) => ({
|
|
926
1332
|
id: pane.id,
|
|
927
1333
|
kind: pane.kind,
|
|
@@ -946,7 +1352,7 @@ export class KLineChartsSceneAdapter {
|
|
|
946
1352
|
this.#historicalDataLoading = { hasMore: true };
|
|
947
1353
|
}
|
|
948
1354
|
this.#chart.setDataLoader(this.#historicalDataLoading === undefined
|
|
949
|
-
? createStaticDataLoader(candidate
|
|
1355
|
+
? createStaticDataLoader(engineDataForScene(candidate))
|
|
950
1356
|
: this.#historicalDataLoader(candidate));
|
|
951
1357
|
this.#chart.resetData();
|
|
952
1358
|
applyViewport(this.#chart, candidate.viewport);
|
|
@@ -969,7 +1375,7 @@ export class KLineChartsSceneAdapter {
|
|
|
969
1375
|
this.#historicalDataLoading = { hasMore: previousHistoricalHasMore };
|
|
970
1376
|
}
|
|
971
1377
|
this.#chart.setDataLoader(this.#historicalDataLoading === undefined
|
|
972
|
-
? createStaticDataLoader(previous
|
|
1378
|
+
? createStaticDataLoader(engineDataForScene(previous))
|
|
973
1379
|
: this.#historicalDataLoader(previous));
|
|
974
1380
|
this.#chart.resetData();
|
|
975
1381
|
applyViewport(this.#chart, previous.viewport);
|
|
@@ -1080,6 +1486,9 @@ export class KLineChartsSceneAdapter {
|
|
|
1080
1486
|
throw new SceneError('RUNTIME_INIT_FAILED', '/drawings', `KLineCharts failed to start Drawing ${request.id}.`);
|
|
1081
1487
|
}
|
|
1082
1488
|
this.#drawingInProgressId = request.id;
|
|
1489
|
+
if (request.type === 'segment') {
|
|
1490
|
+
this.#prepareTouchPrecisionDrawing(request.id, overlay.paneId);
|
|
1491
|
+
}
|
|
1083
1492
|
return request.id;
|
|
1084
1493
|
}
|
|
1085
1494
|
listDrawings() {
|
|
@@ -1224,6 +1633,9 @@ export class KLineChartsSceneAdapter {
|
|
|
1224
1633
|
applyMainSeriesPresentation(presentation) {
|
|
1225
1634
|
this.#assertActive();
|
|
1226
1635
|
this.#assertNotTerminated();
|
|
1636
|
+
if (isGapAwareScene(this.#scene)) {
|
|
1637
|
+
throw new MainSeriesPresentationError('MAIN_SERIES_PRESENTATION_UNSUPPORTED', '/chart/candle/type', 'ChartScene v2 main-series switching is disabled until every presentation is Gap-aware.');
|
|
1638
|
+
}
|
|
1227
1639
|
if (presentation.type === 'area') {
|
|
1228
1640
|
const expected = STANDARD_CLOSE_LINE_PRESENTATION;
|
|
1229
1641
|
if (presentation.value !== expected.value ||
|
|
@@ -1513,7 +1925,9 @@ export class KLineChartsSceneAdapter {
|
|
|
1513
1925
|
}
|
|
1514
1926
|
inspect() {
|
|
1515
1927
|
this.#assertActive();
|
|
1516
|
-
const indicators = this.#chart.getIndicators()
|
|
1928
|
+
const indicators = this.#chart.getIndicators()
|
|
1929
|
+
.filter((indicator) => indicator.id !== GAP_AWARE_CANDLE_INDICATOR_ID)
|
|
1930
|
+
.map((indicator) => {
|
|
1517
1931
|
const paneId = this.#idMap.paneFromEngine.get(indicator.paneId);
|
|
1518
1932
|
const yAxisId = this.#idMap.yAxisFromEngine.get(indicator.yAxisId);
|
|
1519
1933
|
if (paneId === undefined || yAxisId === undefined) {
|
|
@@ -1529,7 +1943,9 @@ export class KLineChartsSceneAdapter {
|
|
|
1529
1943
|
return {
|
|
1530
1944
|
engineVersion: this.#engine.version(),
|
|
1531
1945
|
runtimeVersion: this.#scene.runtime.runtimeVersion,
|
|
1532
|
-
dataCount: this.#
|
|
1946
|
+
dataCount: this.#scene.data.length,
|
|
1947
|
+
timelineSlotCount: timelineSlotCount(this.#scene),
|
|
1948
|
+
gapCount: gapCount(this.#scene),
|
|
1533
1949
|
paneIds: this.#scene.panes.map((pane) => pane.id),
|
|
1534
1950
|
indicators,
|
|
1535
1951
|
overlays: this.exportScene().overlays,
|
|
@@ -1542,6 +1958,11 @@ export class KLineChartsSceneAdapter {
|
|
|
1542
1958
|
return;
|
|
1543
1959
|
}
|
|
1544
1960
|
this.#cancelInteraction('destroy');
|
|
1961
|
+
this.#deselectingPointerId = undefined;
|
|
1962
|
+
this.#leaveExclusiveSelection();
|
|
1963
|
+
this.#cancelTouchPrecisionDrawing(false);
|
|
1964
|
+
this.#touchPrecisionGuide?.destroy();
|
|
1965
|
+
this.#touchPrecisionGuide = undefined;
|
|
1545
1966
|
this.#interactivePriceMeasurement = undefined;
|
|
1546
1967
|
this.#drawingInProgressId = null;
|
|
1547
1968
|
this.#disposed = true;
|