velo-plot 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/axisFormat-B7o_uIEA.js +127 -0
- package/dist/axisFormat-B7o_uIEA.js.map +1 -0
- package/dist/core/OverlayRenderer.d.ts +1 -2
- package/dist/core/OverlayRenderer.test.d.ts +1 -0
- package/dist/core/chart/ChartCore.d.ts +17 -1
- package/dist/core/chart/ChartRenderLoop.d.ts +4 -0
- package/dist/core/chart/ChartScaling.d.ts +11 -0
- package/dist/core/chart/ChartScaling.test.d.ts +1 -0
- package/dist/core/chart/NavigationUtils.d.ts +12 -0
- package/dist/core/chart/NavigationUtils.test.d.ts +1 -0
- package/dist/core/chart/types.d.ts +11 -1
- package/dist/core/format/axisFormat.d.ts +16 -0
- package/dist/core/format/axisFormat.test.d.ts +1 -0
- package/dist/core/indicator/buildIndicatorPane.d.ts +22 -0
- package/dist/core/indicator/buildIndicatorSeries.d.ts +14 -0
- package/dist/core/indicator/buildIndicatorSeries.test.d.ts +1 -0
- package/dist/core/indicator/index.d.ts +3 -0
- package/dist/core/indicator/types.d.ts +74 -0
- package/dist/core/series/SeriesBounds.d.ts +1 -1
- package/dist/core/series/SeriesBounds.test.d.ts +1 -0
- package/dist/core/stacked/createStackedChart.d.ts +3 -0
- package/dist/core/stacked/createStackedChart.test.d.ts +1 -0
- package/dist/core/stacked/index.d.ts +4 -0
- package/dist/core/stacked/paneAxis.d.ts +20 -0
- package/dist/core/stacked/paneAxis.test.d.ts +1 -0
- package/dist/core/stacked/paneResize.d.ts +32 -0
- package/dist/core/stacked/paneResize.test.d.ts +1 -0
- package/dist/core/stacked/types.d.ts +91 -0
- package/dist/core/sync/index.d.ts +25 -70
- package/dist/core/sync/index.test.d.ts +1 -0
- package/dist/{index-D8ifudJH.js → index-DjeWClO9.js} +601 -561
- package/dist/index-DjeWClO9.js.map +1 -0
- package/dist/{index.core-CYu3tydL.js → index.core-xiNcuFfJ.js} +1760 -1405
- package/dist/index.core-xiNcuFfJ.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/plugins/sync.js +169 -145
- package/dist/plugins/sync.js.map +1 -1
- package/dist/plugins/tools/tooltip/TooltipManager.d.ts +9 -1
- package/dist/plugins/tools/tooltip/format.d.ts +7 -0
- package/dist/plugins/tools/tooltip/format.test.d.ts +1 -0
- package/dist/plugins/tools/tooltip/types.d.ts +6 -8
- package/dist/plugins/tools.js +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/useStackedPlot.d.ts +17 -0
- package/dist/types.d.ts +21 -1
- package/dist/velo-plot.full.js +2906 -2399
- package/dist/velo-plot.full.js.map +1 -1
- package/dist/velo-plot.js +11 -11
- package/package.json +9 -2
- package/dist/EventEmitter-DAbs2K1C.js +0 -60
- package/dist/EventEmitter-DAbs2K1C.js.map +0 -1
- package/dist/index-D8ifudJH.js.map +0 -1
- package/dist/index.core-CYu3tydL.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export { DebugOverlay, createDebugOverlay, enableDebugMode, type DebugStats, typ
|
|
|
44
44
|
export { LoadingIndicator, createLoadingIndicator, showLoading, showProgress, type LoadingIndicatorType, type LoadingIndicatorOptions, type LoadingState, } from './core/loading';
|
|
45
45
|
export { generateSineWave, generateSquareWave, generateSawtoothWave, generateTriangleWave, generateRandomData, generateCVData, generateNyquistData, benchmarkRender, assertPerformance, createSnapshot, compareSnapshots, waitForFrames, waitFor, type MockChartOptions, type WaveformOptions, type RandomDataOptions, type BenchmarkResult as TestBenchmarkResult, type BenchmarkOptions as TestBenchmarkOptions, type SnapshotOptions, type ChartSnapshot, } from './testing';
|
|
46
46
|
export { ChartGroup, createChartGroup, linkCharts, createMasterSlave, type SyncAxis, type SyncOptions, type ChartLike, type SyncEvent, } from './core/sync';
|
|
47
|
+
export { createStackedChart, STACKED_MAX_PANES, STACKED_DEFAULT_MIN_PANE_RATIO, type StackedChart, type StackedChartOptions, type StackedPaneConfig, type StackedSyncOptions, } from './core/stacked';
|
|
48
|
+
export { buildIndicatorSeries, createIndicatorSeries, buildIndicatorPane, detectIndicatorMarkers, type IndicatorData, type IndicatorSeriesOptions, type IndicatorStyle, type IndicatorLineLayer, type IndicatorLineColorZones, type BuildIndicatorPaneOptions, } from './core/indicator';
|
|
47
49
|
export { ThemeEditor, createThemeEditor, getPresetTheme, getPresetNames, THEME_PRESETS, type EditorTheme, type ThemeEditorOptions, type ThemePreset, type ColorGroup, type ColorProperty, } from './core/theme-editor';
|
|
48
50
|
export { BackpressureManager, CircularBuffer, createBackpressureManager, createRealtimeBackpressure, createLosslessBackpressure, type OverflowStrategy, type BackpressureConfig, type PressureStats, type BufferHealth, } from './streaming/backpressure';
|
|
49
51
|
export { sma, ema, wma, dema, tema, rsi, macd, stochastic, roc, momentum, bollingerBands, atr, standardDeviation, vwap, obv, adx, aroon, percentChange, cumsum, normalize, type IndicatorResult, type OHLCData, } from './plugins/analysis';
|
package/dist/plugins/sync.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
var
|
|
2
|
-
var l = (
|
|
3
|
-
var
|
|
4
|
-
class
|
|
1
|
+
var u = Object.defineProperty;
|
|
2
|
+
var l = (r, t, i) => t in r ? u(r, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : r[t] = i;
|
|
3
|
+
var o = (r, t, i) => l(r, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
class f {
|
|
5
5
|
constructor(t) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
o(this, "charts", /* @__PURE__ */ new Map());
|
|
7
|
+
o(this, "options");
|
|
8
|
+
o(this, "eventHandlers", /* @__PURE__ */ new Map());
|
|
9
|
+
o(this, "isUpdating", !1);
|
|
10
|
+
o(this, "debounceTimers", /* @__PURE__ */ new Map());
|
|
11
|
+
o(this, "rafTimers", /* @__PURE__ */ new Map());
|
|
11
12
|
this.options = {
|
|
12
13
|
axis: "x",
|
|
13
14
|
syncCursor: !0,
|
|
@@ -16,246 +17,269 @@ class c {
|
|
|
16
17
|
syncPan: !0,
|
|
17
18
|
debounce: 0,
|
|
18
19
|
bidirectional: !0,
|
|
20
|
+
masterId: void 0,
|
|
19
21
|
...t
|
|
20
22
|
};
|
|
21
23
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Add a chart to the group
|
|
24
|
-
*/
|
|
25
24
|
add(t) {
|
|
26
|
-
const
|
|
27
|
-
return this.charts.has(
|
|
25
|
+
const i = t.getId();
|
|
26
|
+
return this.charts.has(i) ? (console.warn(`[ChartGroup] Chart ${i} is already in the group`), this) : (this.charts.set(i, t), this.attachEventHandlers(t), this);
|
|
28
27
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Add multiple charts at once
|
|
31
|
-
*/
|
|
32
28
|
addAll(...t) {
|
|
33
|
-
for (const
|
|
34
|
-
this.add(s);
|
|
29
|
+
for (const i of t) this.add(i);
|
|
35
30
|
return this;
|
|
36
31
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Remove a chart from the group
|
|
39
|
-
*/
|
|
40
32
|
remove(t) {
|
|
41
|
-
const
|
|
42
|
-
return this.charts.has(
|
|
33
|
+
const i = t.getId();
|
|
34
|
+
return this.charts.has(i) ? (this.detachEventHandlers(t), this.charts.delete(i), this) : this;
|
|
43
35
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Get all charts in the group
|
|
46
|
-
*/
|
|
47
36
|
getCharts() {
|
|
48
37
|
return Array.from(this.charts.values());
|
|
49
38
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Get chart count
|
|
52
|
-
*/
|
|
53
39
|
size() {
|
|
54
40
|
return this.charts.size;
|
|
55
41
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Check if a chart is in the group
|
|
58
|
-
*/
|
|
59
42
|
has(t) {
|
|
60
43
|
return this.charts.has(t.getId());
|
|
61
44
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Set synchronization axis
|
|
64
|
-
*/
|
|
65
45
|
syncAxis(t) {
|
|
66
46
|
return this.options.axis = t, this;
|
|
67
47
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
48
|
+
syncZoom(t) {
|
|
49
|
+
return this.options.syncZoom === t ? this : this.updateOptions({ syncZoom: t });
|
|
50
|
+
}
|
|
51
|
+
syncPan(t) {
|
|
52
|
+
return this.options.syncPan === t ? this : this.updateOptions({ syncPan: t });
|
|
53
|
+
}
|
|
71
54
|
syncCursor(t) {
|
|
72
55
|
return this.options.syncCursor = t, this;
|
|
73
56
|
}
|
|
74
|
-
/**
|
|
75
|
-
* Enable/disable selection synchronization
|
|
76
|
-
*/
|
|
77
57
|
syncSelection(t) {
|
|
78
58
|
return this.options.syncSelection = t, this;
|
|
79
59
|
}
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
60
|
+
/** Update sync options and re-bind event handlers on all charts */
|
|
61
|
+
updateOptions(t) {
|
|
62
|
+
Object.assign(this.options, t);
|
|
63
|
+
for (const i of this.charts.values())
|
|
64
|
+
this.detachEventHandlers(i), this.attachEventHandlers(i);
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
getOptions() {
|
|
68
|
+
return { ...this.options };
|
|
69
|
+
}
|
|
70
|
+
syncTo(t, i) {
|
|
71
|
+
this.propagateZoom(i || "", t);
|
|
85
72
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Reset all charts to auto-scale
|
|
88
|
-
*/
|
|
89
73
|
resetAll() {
|
|
90
|
-
|
|
91
|
-
|
|
74
|
+
this.batch(() => {
|
|
75
|
+
for (const t of this.charts.values())
|
|
76
|
+
t.fit ? t.fit() : t.zoom({ x: void 0, y: void 0 });
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
fitAll(t) {
|
|
80
|
+
this.batch(() => {
|
|
81
|
+
let i = t == null ? void 0 : t.x;
|
|
82
|
+
const n = this.options.masterId, s = n ? this.charts.get(n) : this.charts.values().next().value;
|
|
83
|
+
if (!i && (s != null && s.fit)) {
|
|
84
|
+
s.fit({ padding: t == null ? void 0 : t.padding });
|
|
85
|
+
const e = s.getViewBounds();
|
|
86
|
+
this.hasValidViewBounds(e) && (i = [e.xMin, e.xMax]);
|
|
87
|
+
}
|
|
88
|
+
for (const [e, a] of this.charts.entries())
|
|
89
|
+
a.fit && e !== n && a.fit(
|
|
90
|
+
i ? { x: i, padding: t == null ? void 0 : t.padding } : { padding: t == null ? void 0 : t.padding }
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
batch(t) {
|
|
95
|
+
const i = this.isUpdating;
|
|
96
|
+
this.isUpdating = !0;
|
|
97
|
+
try {
|
|
98
|
+
return t();
|
|
99
|
+
} finally {
|
|
100
|
+
this.isUpdating = i;
|
|
101
|
+
}
|
|
92
102
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Clear all selections in the group
|
|
95
|
-
*/
|
|
96
103
|
clearAllSelections() {
|
|
97
104
|
var t;
|
|
98
|
-
for (const
|
|
99
|
-
(t =
|
|
105
|
+
for (const i of this.charts.values())
|
|
106
|
+
(t = i.clearSelection) == null || t.call(i);
|
|
100
107
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Destroy the group and cleanup
|
|
103
|
-
*/
|
|
104
108
|
destroy() {
|
|
105
109
|
for (const t of this.charts.values())
|
|
106
110
|
this.detachEventHandlers(t);
|
|
107
111
|
this.charts.clear(), this.eventHandlers.clear();
|
|
108
|
-
for (const t of this.debounceTimers.values())
|
|
109
|
-
clearTimeout(t);
|
|
112
|
+
for (const t of this.debounceTimers.values()) clearTimeout(t);
|
|
110
113
|
this.debounceTimers.clear();
|
|
114
|
+
for (const t of this.rafTimers.values()) cancelAnimationFrame(t);
|
|
115
|
+
this.rafTimers.clear();
|
|
116
|
+
}
|
|
117
|
+
isSyncSource(t) {
|
|
118
|
+
return this.options.bidirectional ? !0 : this.options.masterId ? t === this.options.masterId : !0;
|
|
119
|
+
}
|
|
120
|
+
canPropagateFrom(t) {
|
|
121
|
+
return this.options.bidirectional ? !0 : this.options.masterId ? t === this.options.masterId : !0;
|
|
122
|
+
}
|
|
123
|
+
hasValidViewBounds(t) {
|
|
124
|
+
const i = t.xMax - t.xMin, n = t.yMax - t.yMin;
|
|
125
|
+
return !(!Number.isFinite(i) || !Number.isFinite(n) || i <= 0 && n <= 0);
|
|
111
126
|
}
|
|
112
|
-
// ============================================
|
|
113
|
-
// Private Methods
|
|
114
|
-
// ============================================
|
|
115
127
|
attachEventHandlers(t) {
|
|
116
|
-
const
|
|
117
|
-
if (this.options.syncZoom) {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
this.handleZoom(s, r);
|
|
128
|
+
const i = t.getId(), n = /* @__PURE__ */ new Map();
|
|
129
|
+
if (this.options.syncZoom && this.isSyncSource(i)) {
|
|
130
|
+
const s = (...e) => {
|
|
131
|
+
this.handleZoom(i, e[0]);
|
|
121
132
|
};
|
|
122
|
-
t.on("zoom",
|
|
133
|
+
t.on("zoom", s), n.set("zoom", s);
|
|
123
134
|
}
|
|
124
|
-
if (this.options.syncPan) {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
this.handlePan(s, r);
|
|
135
|
+
if (this.options.syncPan && this.isSyncSource(i)) {
|
|
136
|
+
const s = (...e) => {
|
|
137
|
+
this.handlePan(i, e[0]);
|
|
128
138
|
};
|
|
129
|
-
t.on("pan",
|
|
139
|
+
t.on("pan", s), n.set("pan", s);
|
|
130
140
|
}
|
|
131
141
|
if (this.options.syncCursor) {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
this.handleCursor(s, r);
|
|
142
|
+
const s = (...e) => {
|
|
143
|
+
this.handleCursor(i, e[0]);
|
|
135
144
|
};
|
|
136
|
-
t.on("hover",
|
|
145
|
+
t.on("hover", s), n.set("hover", s);
|
|
137
146
|
}
|
|
138
147
|
if (this.options.syncSelection) {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
this.handleSelection(s, r);
|
|
148
|
+
const s = (...e) => {
|
|
149
|
+
this.handleSelection(i, e[0]);
|
|
142
150
|
};
|
|
143
|
-
t.on("selectionChange",
|
|
151
|
+
t.on("selectionChange", s), n.set("selectionChange", s);
|
|
144
152
|
}
|
|
145
|
-
this.eventHandlers.set(
|
|
153
|
+
this.eventHandlers.set(i, n);
|
|
146
154
|
}
|
|
147
155
|
detachEventHandlers(t) {
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
for (const [
|
|
151
|
-
t.off(
|
|
152
|
-
this.eventHandlers.delete(
|
|
156
|
+
const i = t.getId(), n = this.eventHandlers.get(i);
|
|
157
|
+
if (n) {
|
|
158
|
+
for (const [s, e] of n.entries())
|
|
159
|
+
t.off(s, e);
|
|
160
|
+
this.eventHandlers.delete(i);
|
|
153
161
|
}
|
|
154
162
|
}
|
|
155
|
-
handleZoom(t,
|
|
156
|
-
if (this.isUpdating) return;
|
|
157
|
-
const
|
|
158
|
-
(
|
|
159
|
-
|
|
163
|
+
handleZoom(t, i) {
|
|
164
|
+
if (this.isUpdating || !this.canPropagateFrom(t)) return;
|
|
165
|
+
const n = this.charts.get(t);
|
|
166
|
+
if (n && !this.hasValidViewBounds(n.getViewBounds())) return;
|
|
167
|
+
const s = {};
|
|
168
|
+
if ((this.options.axis === "x" || this.options.axis === "xy") && (s.xMin = i.x[0], s.xMax = i.x[1]), (this.options.axis === "y" || this.options.axis === "xy") && (s.yMin = i.y[0], s.yMax = i.y[1]), s.xMin !== void 0 && s.xMax !== void 0) {
|
|
169
|
+
const e = s.xMax - s.xMin;
|
|
170
|
+
if (!Number.isFinite(e) || e <= 0) return;
|
|
171
|
+
}
|
|
172
|
+
this.scheduleSyncAction(`zoom-${t}`, () => {
|
|
173
|
+
this.propagateZoom(t, s);
|
|
160
174
|
});
|
|
161
175
|
}
|
|
162
|
-
handlePan(t,
|
|
163
|
-
if (this.isUpdating) return;
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
this.propagatePan(t,
|
|
176
|
+
handlePan(t, i) {
|
|
177
|
+
if (this.isUpdating || !this.canPropagateFrom(t)) return;
|
|
178
|
+
const n = this.options.axis === "x" || this.options.axis === "xy" ? i.deltaX : 0, s = this.options.axis === "y" || this.options.axis === "xy" ? i.deltaY : 0;
|
|
179
|
+
n === 0 && s === 0 || this.scheduleSyncAction(`pan-${t}`, () => {
|
|
180
|
+
this.propagatePan(t, n, s);
|
|
167
181
|
});
|
|
168
182
|
}
|
|
169
|
-
handleCursor(t,
|
|
170
|
-
var
|
|
183
|
+
handleCursor(t, i) {
|
|
184
|
+
var n, s;
|
|
171
185
|
if (!this.isUpdating)
|
|
172
|
-
for (const [e,
|
|
173
|
-
e !== t && (
|
|
186
|
+
for (const [e, a] of this.charts.entries())
|
|
187
|
+
e !== t && (i != null && i.point ? (n = a.setExternalCursor) == null || n.call(a, i.point.x, i.point.y) : (s = a.clearExternalCursor) == null || s.call(a));
|
|
174
188
|
}
|
|
175
|
-
handleSelection(t,
|
|
176
|
-
|
|
177
|
-
for (const [i, n] of this.charts.entries())
|
|
178
|
-
;
|
|
189
|
+
handleSelection(t, i) {
|
|
190
|
+
this.isUpdating || this.options.syncSelection;
|
|
179
191
|
}
|
|
180
|
-
propagateZoom(t,
|
|
192
|
+
propagateZoom(t, i) {
|
|
181
193
|
if (!this.isUpdating) {
|
|
182
194
|
this.isUpdating = !0;
|
|
183
195
|
try {
|
|
184
|
-
for (const [
|
|
185
|
-
if (
|
|
186
|
-
const e = {
|
|
187
|
-
|
|
188
|
-
};
|
|
189
|
-
s.xMin !== void 0 && s.xMax !== void 0 && (e.x = [s.xMin, s.xMax]), s.yMin !== void 0 && s.yMax !== void 0 && (e.y = [s.yMin, s.yMax]), n.zoom(e);
|
|
196
|
+
for (const [n, s] of this.charts.entries()) {
|
|
197
|
+
if (n === t) continue;
|
|
198
|
+
const e = { animate: !1 };
|
|
199
|
+
i.xMin !== void 0 && i.xMax !== void 0 && (e.x = [i.xMin, i.xMax]), i.yMin !== void 0 && i.yMax !== void 0 && (e.y = [i.yMin, i.yMax]), s.zoom(e);
|
|
190
200
|
}
|
|
191
201
|
} finally {
|
|
192
202
|
this.isUpdating = !1;
|
|
193
203
|
}
|
|
194
204
|
}
|
|
195
205
|
}
|
|
196
|
-
propagatePan(t,
|
|
206
|
+
propagatePan(t, i, n) {
|
|
197
207
|
if (!this.isUpdating) {
|
|
198
208
|
this.isUpdating = !0;
|
|
199
209
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
210
|
+
const s = this.charts.get(t);
|
|
211
|
+
if (!s || !this.hasValidViewBounds(s.getViewBounds())) return;
|
|
212
|
+
const e = s.getViewBounds();
|
|
213
|
+
for (const [a, d] of this.charts.entries()) {
|
|
214
|
+
if (a === t) continue;
|
|
215
|
+
const h = { animate: !1 };
|
|
216
|
+
(this.options.axis === "x" || this.options.axis === "xy") && (h.x = [e.xMin, e.xMax]), (this.options.axis === "y" || this.options.axis === "xy") && (h.y = [e.yMin, e.yMax]), (h.x || h.y) && d.zoom(h);
|
|
217
|
+
}
|
|
202
218
|
} finally {
|
|
203
219
|
this.isUpdating = !1;
|
|
204
220
|
}
|
|
205
221
|
}
|
|
206
222
|
}
|
|
207
|
-
|
|
208
|
-
if (this.options.debounce
|
|
209
|
-
|
|
223
|
+
scheduleSyncAction(t, i) {
|
|
224
|
+
if (this.options.debounce > 0) {
|
|
225
|
+
this.debounceAction(t, i);
|
|
210
226
|
return;
|
|
211
227
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
228
|
+
if (this.rafTimers.has(t)) return;
|
|
229
|
+
const n = requestAnimationFrame(() => {
|
|
230
|
+
this.rafTimers.delete(t), i();
|
|
231
|
+
});
|
|
232
|
+
this.rafTimers.set(t, n);
|
|
233
|
+
}
|
|
234
|
+
debounceAction(t, i) {
|
|
235
|
+
const n = this.debounceTimers.get(t);
|
|
236
|
+
n && clearTimeout(n);
|
|
237
|
+
const s = window.setTimeout(() => {
|
|
238
|
+
this.debounceTimers.delete(t), i();
|
|
216
239
|
}, this.options.debounce);
|
|
217
|
-
this.debounceTimers.set(t,
|
|
240
|
+
this.debounceTimers.set(t, s);
|
|
218
241
|
}
|
|
219
242
|
}
|
|
220
|
-
function
|
|
221
|
-
const
|
|
222
|
-
return
|
|
243
|
+
function c(r, t) {
|
|
244
|
+
const i = new f(t);
|
|
245
|
+
return i.addAll(...r), i;
|
|
223
246
|
}
|
|
224
|
-
function
|
|
225
|
-
return
|
|
247
|
+
function y(r, t, i) {
|
|
248
|
+
return c([r, t], i);
|
|
226
249
|
}
|
|
227
|
-
function
|
|
228
|
-
return
|
|
229
|
-
axis:
|
|
250
|
+
function m(r, t, i = "x") {
|
|
251
|
+
return c([r, t], {
|
|
252
|
+
axis: i,
|
|
230
253
|
bidirectional: !1,
|
|
254
|
+
masterId: r.getId(),
|
|
231
255
|
syncCursor: !0,
|
|
232
256
|
syncZoom: !0,
|
|
233
257
|
syncPan: !0
|
|
234
|
-
})
|
|
258
|
+
});
|
|
235
259
|
}
|
|
236
|
-
const
|
|
260
|
+
const p = {
|
|
237
261
|
name: "velo-plot-sync",
|
|
238
262
|
version: "1.0.0",
|
|
239
263
|
description: "Multi-chart synchronization for velo-plot",
|
|
240
264
|
provides: ["interaction"],
|
|
241
265
|
tags: ["sync", "multi-chart", "coordination"]
|
|
242
266
|
};
|
|
243
|
-
function
|
|
267
|
+
function g(r = {}) {
|
|
244
268
|
return {
|
|
245
|
-
manifest:
|
|
269
|
+
manifest: p,
|
|
246
270
|
onInit(t) {
|
|
247
|
-
|
|
271
|
+
r.groupId;
|
|
248
272
|
},
|
|
249
273
|
onDestroy(t) {
|
|
250
274
|
}
|
|
251
275
|
};
|
|
252
276
|
}
|
|
253
277
|
export {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
278
|
+
f as ChartGroup,
|
|
279
|
+
g as PluginSync,
|
|
280
|
+
c as createChartGroup,
|
|
281
|
+
m as createMasterSlave,
|
|
282
|
+
g as default,
|
|
283
|
+
y as linkCharts
|
|
260
284
|
};
|
|
261
285
|
//# sourceMappingURL=sync.js.map
|
package/dist/plugins/sync.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.js","sources":["../../src/core/sync/index.ts","../../src/plugins/sync/index.ts"],"sourcesContent":["/**\n * Sci Plot - Chart Synchronization Module\n * \n * Provides synchronization between multiple charts:\n * - Synchronized zoom/pan (X, Y, or both)\n * - Shared crosshair cursor\n * - Coordinated selection\n * - Event propagation between linked charts\n * \n * @module sync\n */\n\nimport type { Bounds, Range } from \"../../types\";\n\n// ============================================\n// Types\n// ============================================\n\nexport type SyncAxis = 'x' | 'y' | 'xy' | 'none';\n\nexport interface SyncOptions {\n /** Synchronize axis (default: 'x') */\n axis?: SyncAxis;\n /** Synchronize cursor position (default: true) */\n syncCursor?: boolean;\n /** Synchronize selection state (default: false) */\n syncSelection?: boolean;\n /** Synchronize zoom level (default: true) */\n syncZoom?: boolean;\n /** Synchronize pan (default: true) */\n syncPan?: boolean;\n /** Debounce time for sync events in ms (default: 0) */\n debounce?: number;\n /** Enable bidirectional sync (default: true) */\n bidirectional?: boolean;\n}\n\nexport interface ChartLike {\n /** Unique identifier for the chart */\n getId(): string;\n /** Get current view bounds */\n getViewBounds(): Bounds;\n /** Set view bounds (zoom) */\n zoom(options: { x?: Range; y?: Range; animate?: boolean }): void;\n /** Pan the chart */\n pan(dx: number, dy: number): void;\n /** Get cursor position */\n getCursorPosition?(): { x: number; y: number } | null;\n /** Set external cursor position */\n setExternalCursor?(x: number, y: number): void;\n /** Clear external cursor */\n clearExternalCursor?(): void;\n /** Get selected points */\n getSelectedPoints?(): { seriesId: string; indices: number[] }[];\n /** Set selection */\n selectPoints?(points: { seriesId: string; indices: number[] }[]): void;\n /** Clear selection */\n clearSelection?(): void;\n /** Subscribe to events */\n on(event: string, callback: (...args: unknown[]) => void): void;\n /** Unsubscribe from events */\n off(event: string, callback: (...args: unknown[]) => void): void;\n}\n\nexport interface SyncEvent {\n /** Source chart ID */\n sourceId: string;\n /** Event type */\n type: 'zoom' | 'pan' | 'cursor' | 'selection' | 'bounds';\n /** Event data */\n data: unknown;\n}\n\n// ============================================\n// Chart Group Implementation\n// ============================================\n\nexport class ChartGroup {\n private charts: Map<string, ChartLike> = new Map();\n private options: Required<SyncOptions>;\n private eventHandlers: Map<string, Map<string, (...args: unknown[]) => void>> = new Map();\n private isUpdating: boolean = false;\n private debounceTimers: Map<string, number> = new Map();\n\n constructor(options?: SyncOptions) {\n this.options = {\n axis: 'x',\n syncCursor: true,\n syncSelection: false,\n syncZoom: true,\n syncPan: true,\n debounce: 0,\n bidirectional: true,\n ...options,\n };\n }\n\n /**\n * Add a chart to the group\n */\n add(chart: ChartLike): this {\n const chartId = chart.getId();\n \n if (this.charts.has(chartId)) {\n console.warn(`[ChartGroup] Chart ${chartId} is already in the group`);\n return this;\n }\n\n this.charts.set(chartId, chart);\n this.attachEventHandlers(chart);\n \n return this;\n }\n\n /**\n * Add multiple charts at once\n */\n addAll(...charts: ChartLike[]): this {\n for (const chart of charts) {\n this.add(chart);\n }\n return this;\n }\n\n /**\n * Remove a chart from the group\n */\n remove(chart: ChartLike): this {\n const chartId = chart.getId();\n \n if (!this.charts.has(chartId)) {\n return this;\n }\n\n this.detachEventHandlers(chart);\n this.charts.delete(chartId);\n \n return this;\n }\n\n /**\n * Get all charts in the group\n */\n getCharts(): ChartLike[] {\n return Array.from(this.charts.values());\n }\n\n /**\n * Get chart count\n */\n size(): number {\n return this.charts.size;\n }\n\n /**\n * Check if a chart is in the group\n */\n has(chart: ChartLike): boolean {\n return this.charts.has(chart.getId());\n }\n\n /**\n * Set synchronization axis\n */\n syncAxis(axis: SyncAxis): this {\n this.options.axis = axis;\n return this;\n }\n\n /**\n * Enable/disable cursor synchronization\n */\n syncCursor(enabled: boolean): this {\n this.options.syncCursor = enabled;\n return this;\n }\n\n /**\n * Enable/disable selection synchronization\n */\n syncSelection(enabled: boolean): this {\n this.options.syncSelection = enabled;\n return this;\n }\n\n /**\n * Synchronize all charts to a specific view\n */\n syncTo(bounds: Partial<Bounds>, excludeChartId?: string): void {\n this.propagateZoom(excludeChartId || '', bounds);\n }\n\n /**\n * Reset all charts to auto-scale\n */\n resetAll(): void {\n for (const chart of this.charts.values()) {\n chart.zoom({ x: undefined, y: undefined });\n }\n }\n\n /**\n * Clear all selections in the group\n */\n clearAllSelections(): void {\n for (const chart of this.charts.values()) {\n chart.clearSelection?.();\n }\n }\n\n /**\n * Destroy the group and cleanup\n */\n destroy(): void {\n for (const chart of this.charts.values()) {\n this.detachEventHandlers(chart);\n }\n this.charts.clear();\n this.eventHandlers.clear();\n \n // Clear any pending debounce timers\n for (const timerId of this.debounceTimers.values()) {\n clearTimeout(timerId);\n }\n this.debounceTimers.clear();\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private attachEventHandlers(chart: ChartLike): void {\n const chartId = chart.getId();\n const handlers = new Map<string, (...args: unknown[]) => void>();\n\n // Zoom handler\n if (this.options.syncZoom) {\n const zoomHandler = (...args: unknown[]) => {\n const e = args[0] as { x: Range; y: Range };\n this.handleZoom(chartId, e);\n };\n chart.on('zoom', zoomHandler);\n handlers.set('zoom', zoomHandler);\n }\n\n // Pan handler\n if (this.options.syncPan) {\n const panHandler = (...args: unknown[]) => {\n const e = args[0] as { deltaX: number; deltaY: number };\n this.handlePan(chartId, e);\n };\n chart.on('pan', panHandler);\n handlers.set('pan', panHandler);\n }\n\n // Cursor handler\n if (this.options.syncCursor) {\n const hoverHandler = (...args: unknown[]) => {\n const e = args[0] as { point?: { x: number; y: number } } | null;\n this.handleCursor(chartId, e);\n };\n chart.on('hover', hoverHandler);\n handlers.set('hover', hoverHandler);\n }\n\n // Selection handler\n if (this.options.syncSelection) {\n const selectionHandler = (...args: unknown[]) => {\n const e = args[0] as { selected: unknown[] };\n this.handleSelection(chartId, e);\n };\n chart.on('selectionChange', selectionHandler);\n handlers.set('selectionChange', selectionHandler);\n }\n\n this.eventHandlers.set(chartId, handlers);\n }\n\n private detachEventHandlers(chart: ChartLike): void {\n const chartId = chart.getId();\n const handlers = this.eventHandlers.get(chartId);\n \n if (!handlers) return;\n\n for (const [event, handler] of handlers.entries()) {\n chart.off(event, handler);\n }\n\n this.eventHandlers.delete(chartId);\n }\n\n private handleZoom(sourceId: string, event: { x: Range; y: Range }): void {\n if (this.isUpdating) return;\n \n const bounds: Partial<Bounds> = {};\n \n if (this.options.axis === 'x' || this.options.axis === 'xy') {\n bounds.xMin = event.x[0];\n bounds.xMax = event.x[1];\n }\n \n if (this.options.axis === 'y' || this.options.axis === 'xy') {\n bounds.yMin = event.y[0];\n bounds.yMax = event.y[1];\n }\n\n this.debounceAction(`zoom-${sourceId}`, () => {\n this.propagateZoom(sourceId, bounds);\n });\n }\n\n private handlePan(sourceId: string, event: { deltaX: number; deltaY: number }): void {\n if (this.isUpdating) return;\n\n const dx = (this.options.axis === 'x' || this.options.axis === 'xy') ? event.deltaX : 0;\n const dy = (this.options.axis === 'y' || this.options.axis === 'xy') ? event.deltaY : 0;\n\n if (dx === 0 && dy === 0) return;\n\n this.debounceAction(`pan-${sourceId}`, () => {\n this.propagatePan(sourceId, dx, dy);\n });\n }\n\n private handleCursor(sourceId: string, event: { point?: { x: number; y: number } } | null): void {\n if (this.isUpdating) return;\n\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n \n if (event?.point) {\n chart.setExternalCursor?.(event.point.x, event.point.y);\n } else {\n chart.clearExternalCursor?.();\n }\n }\n }\n\n private handleSelection(sourceId: string, _event: { selected: unknown[] }): void {\n if (this.isUpdating || !this.options.syncSelection) return;\n\n // Selection sync is more complex - we need to map indices across charts\n // For now, just clear selection on other charts when one changes\n for (const [chartId, _chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n // In a full implementation, you'd map selection by data values\n // For basic sync, we just notify that selection changed\n }\n }\n\n private propagateZoom(sourceId: string, bounds: Partial<Bounds>): void {\n if (this.isUpdating) return;\n \n this.isUpdating = true;\n\n try {\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId && !this.options.bidirectional) continue;\n if (chartId === sourceId) continue;\n\n const zoomOptions: { x?: Range; y?: Range; animate?: boolean } = {\n animate: false,\n };\n\n if (bounds.xMin !== undefined && bounds.xMax !== undefined) {\n zoomOptions.x = [bounds.xMin, bounds.xMax];\n }\n\n if (bounds.yMin !== undefined && bounds.yMax !== undefined) {\n zoomOptions.y = [bounds.yMin, bounds.yMax];\n }\n\n chart.zoom(zoomOptions);\n }\n } finally {\n this.isUpdating = false;\n }\n }\n\n private propagatePan(sourceId: string, dx: number, dy: number): void {\n if (this.isUpdating) return;\n \n this.isUpdating = true;\n\n try {\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n chart.pan(dx, dy);\n }\n } finally {\n this.isUpdating = false;\n }\n }\n\n private debounceAction(key: string, action: () => void): void {\n if (this.options.debounce <= 0) {\n action();\n return;\n }\n\n // Clear existing timer\n const existingTimer = this.debounceTimers.get(key);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Set new timer\n const timerId = window.setTimeout(() => {\n this.debounceTimers.delete(key);\n action();\n }, this.options.debounce);\n\n this.debounceTimers.set(key, timerId);\n }\n}\n\n// ============================================\n// Convenience Functions\n// ============================================\n\n/**\n * Create a chart group with specified charts\n */\nexport function createChartGroup(\n charts: ChartLike[],\n options?: SyncOptions\n): ChartGroup {\n const group = new ChartGroup(options);\n group.addAll(...charts);\n return group;\n}\n\n/**\n * Link two charts for synchronized viewing\n */\nexport function linkCharts(\n chart1: ChartLike,\n chart2: ChartLike,\n options?: SyncOptions\n): ChartGroup {\n return createChartGroup([chart1, chart2], options);\n}\n\n/**\n * Create a master-slave relationship (master controls slave)\n */\nexport function createMasterSlave(\n master: ChartLike,\n slave: ChartLike,\n axis: SyncAxis = 'x'\n): ChartGroup {\n return new ChartGroup({\n axis,\n bidirectional: false,\n syncCursor: true,\n syncZoom: true,\n syncPan: true,\n }).addAll(master, slave);\n}\n","/**\n * SciPlot Engine - Chart Sync Plugin\n * \n * Provides synchronization between multiple chart instances.\n * \n * @module plugins/sync\n */\n\nexport * from \"../../core/sync\";\n\nimport type { PluginManifest, ChartPlugin, PluginContext } from \"../types\";\n\nexport interface PluginSyncConfig {\n /** Group ID to join */\n groupId?: string;\n /** Sync axes: 'x' | 'y' | 'both' */\n syncAxes?: \"x\" | \"y\" | \"both\";\n}\n\nconst manifestSync: PluginManifest = {\n name: \"velo-plot-sync\",\n version: \"1.0.0\",\n description: \"Multi-chart synchronization for velo-plot\",\n provides: [\"interaction\"],\n tags: [\"sync\", \"multi-chart\", \"coordination\"],\n};\n\n/**\n * SciPlot Sync Plugin\n * \n * Enables smooth coordination and synchronization between separate chart instances.\n */\nexport function PluginSync(config: PluginSyncConfig = {}): ChartPlugin<PluginSyncConfig> {\n void config;\n return {\n manifest: manifestSync,\n\n onInit(_ctx: PluginContext) { \n if (config.groupId) {\n // Join the specified group\n }\n },\n\n onDestroy(_ctx: PluginContext) {\n }\n };\n}\n\nexport default PluginSync;\n"],"names":["ChartGroup","options","__publicField","chart","chartId","charts","axis","enabled","bounds","excludeChartId","_a","timerId","handlers","zoomHandler","args","e","panHandler","hoverHandler","selectionHandler","event","handler","sourceId","dx","dy","_b","_event","_chart","zoomOptions","key","action","existingTimer","createChartGroup","group","linkCharts","chart1","chart2","createMasterSlave","master","slave","manifestSync","PluginSync","config","_ctx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sync.js","sources":["../../src/core/sync/index.ts","../../src/plugins/sync/index.ts"],"sourcesContent":["/**\n * Sci Plot - Chart Synchronization Module\n *\n * @module sync\n */\n\nimport type { Bounds, Range } from \"../../types\";\n\nexport type SyncAxis = 'x' | 'y' | 'xy' | 'none';\n\nexport interface SyncOptions {\n axis?: SyncAxis;\n syncCursor?: boolean;\n syncSelection?: boolean;\n syncZoom?: boolean;\n syncPan?: boolean;\n debounce?: number;\n bidirectional?: boolean;\n /** When set, used for fitAll; with bidirectional sync any pane can drive pan/zoom */\n masterId?: string;\n}\n\nexport interface ChartLike {\n getId(): string;\n getViewBounds(): Bounds;\n zoom(options: { x?: Range; y?: Range; animate?: boolean }): void;\n pan(dx: number, dy: number): void;\n fit?(options?: { x?: Range; y?: Range; padding?: number | { x?: number; y?: number } }): void;\n getCursorPosition?(): { x: number; y: number } | null;\n setExternalCursor?(x: number, y: number): void;\n clearExternalCursor?(): void;\n getSelectedPoints?(): { seriesId: string; indices: number[] }[];\n selectPoints?(points: { seriesId: string; indices: number[] }[]): void;\n clearSelection?(): void;\n on(event: string, callback: (...args: unknown[]) => void): void;\n off(event: string, callback: (...args: unknown[]) => void): void;\n}\n\nexport interface SyncEvent {\n sourceId: string;\n type: 'zoom' | 'pan' | 'cursor' | 'selection' | 'bounds';\n data: unknown;\n}\n\ntype RequiredSyncOptions = Required<Omit<SyncOptions, 'masterId'>> & Pick<SyncOptions, 'masterId'>;\n\nexport class ChartGroup {\n private charts: Map<string, ChartLike> = new Map();\n private options: RequiredSyncOptions;\n private eventHandlers: Map<string, Map<string, (...args: unknown[]) => void>> = new Map();\n private isUpdating: boolean = false;\n private debounceTimers: Map<string, number> = new Map();\n private rafTimers: Map<string, number> = new Map();\n\n constructor(options?: SyncOptions) {\n this.options = {\n axis: 'x',\n syncCursor: true,\n syncSelection: false,\n syncZoom: true,\n syncPan: true,\n debounce: 0,\n bidirectional: true,\n masterId: undefined,\n ...options,\n };\n }\n\n add(chart: ChartLike): this {\n const chartId = chart.getId();\n if (this.charts.has(chartId)) {\n console.warn(`[ChartGroup] Chart ${chartId} is already in the group`);\n return this;\n }\n this.charts.set(chartId, chart);\n this.attachEventHandlers(chart);\n return this;\n }\n\n addAll(...charts: ChartLike[]): this {\n for (const chart of charts) this.add(chart);\n return this;\n }\n\n remove(chart: ChartLike): this {\n const chartId = chart.getId();\n if (!this.charts.has(chartId)) return this;\n this.detachEventHandlers(chart);\n this.charts.delete(chartId);\n return this;\n }\n\n getCharts(): ChartLike[] {\n return Array.from(this.charts.values());\n }\n\n size(): number {\n return this.charts.size;\n }\n\n has(chart: ChartLike): boolean {\n return this.charts.has(chart.getId());\n }\n\n syncAxis(axis: SyncAxis): this {\n this.options.axis = axis;\n return this;\n }\n\n syncZoom(enabled: boolean): this {\n if (this.options.syncZoom === enabled) return this;\n return this.updateOptions({ syncZoom: enabled });\n }\n\n syncPan(enabled: boolean): this {\n if (this.options.syncPan === enabled) return this;\n return this.updateOptions({ syncPan: enabled });\n }\n\n syncCursor(enabled: boolean): this {\n this.options.syncCursor = enabled;\n return this;\n }\n\n syncSelection(enabled: boolean): this {\n this.options.syncSelection = enabled;\n return this;\n }\n\n /** Update sync options and re-bind event handlers on all charts */\n updateOptions(partial: Partial<SyncOptions>): this {\n Object.assign(this.options, partial);\n for (const chart of this.charts.values()) {\n this.detachEventHandlers(chart);\n this.attachEventHandlers(chart);\n }\n return this;\n }\n\n getOptions(): Readonly<SyncOptions> {\n return { ...this.options };\n }\n\n syncTo(bounds: Partial<Bounds>, excludeChartId?: string): void {\n this.propagateZoom(excludeChartId || '', bounds);\n }\n\n resetAll(): void {\n this.batch(() => {\n for (const chart of this.charts.values()) {\n if (chart.fit) chart.fit();\n else chart.zoom({ x: undefined, y: undefined });\n }\n });\n }\n\n fitAll(options?: { x?: Range; padding?: number }): void {\n this.batch(() => {\n let sharedX: Range | undefined = options?.x;\n const masterId = this.options.masterId;\n const master = masterId ? this.charts.get(masterId) : this.charts.values().next().value;\n\n if (!sharedX && master?.fit) {\n master.fit({ padding: options?.padding });\n const b = master.getViewBounds();\n if (this.hasValidViewBounds(b)) sharedX = [b.xMin, b.xMax];\n }\n\n for (const [chartId, chart] of this.charts.entries()) {\n if (!chart.fit) continue;\n if (chartId === masterId) continue;\n chart.fit(\n sharedX\n ? { x: sharedX, padding: options?.padding }\n : { padding: options?.padding },\n );\n }\n });\n }\n\n batch<T>(fn: () => T): T {\n const prev = this.isUpdating;\n this.isUpdating = true;\n try {\n return fn();\n } finally {\n this.isUpdating = prev;\n }\n }\n\n clearAllSelections(): void {\n for (const chart of this.charts.values()) {\n chart.clearSelection?.();\n }\n }\n\n destroy(): void {\n for (const chart of this.charts.values()) {\n this.detachEventHandlers(chart);\n }\n this.charts.clear();\n this.eventHandlers.clear();\n for (const timerId of this.debounceTimers.values()) clearTimeout(timerId);\n this.debounceTimers.clear();\n for (const rafId of this.rafTimers.values()) cancelAnimationFrame(rafId);\n this.rafTimers.clear();\n }\n\n private isSyncSource(chartId: string): boolean {\n if (this.options.bidirectional) return true;\n if (this.options.masterId) return chartId === this.options.masterId;\n return true;\n }\n\n private canPropagateFrom(sourceId: string): boolean {\n if (this.options.bidirectional) return true;\n if (this.options.masterId) return sourceId === this.options.masterId;\n return true;\n }\n\n private hasValidViewBounds(bounds: Bounds): boolean {\n const xSpan = bounds.xMax - bounds.xMin;\n const ySpan = bounds.yMax - bounds.yMin;\n if (!Number.isFinite(xSpan) || !Number.isFinite(ySpan)) return false;\n if (xSpan <= 0 && ySpan <= 0) return false;\n return true;\n }\n\n private attachEventHandlers(chart: ChartLike): void {\n const chartId = chart.getId();\n const handlers = new Map<string, (...args: unknown[]) => void>();\n\n if (this.options.syncZoom && this.isSyncSource(chartId)) {\n const zoomHandler = (...args: unknown[]) => {\n this.handleZoom(chartId, args[0] as { x: Range; y: Range });\n };\n chart.on('zoom', zoomHandler);\n handlers.set('zoom', zoomHandler);\n }\n\n if (this.options.syncPan && this.isSyncSource(chartId)) {\n const panHandler = (...args: unknown[]) => {\n this.handlePan(chartId, args[0] as { deltaX: number; deltaY: number });\n };\n chart.on('pan', panHandler);\n handlers.set('pan', panHandler);\n }\n\n if (this.options.syncCursor) {\n const hoverHandler = (...args: unknown[]) => {\n this.handleCursor(chartId, args[0] as { point?: { x: number; y: number } } | null);\n };\n chart.on('hover', hoverHandler);\n handlers.set('hover', hoverHandler);\n }\n\n if (this.options.syncSelection) {\n const selectionHandler = (...args: unknown[]) => {\n this.handleSelection(chartId, args[0] as { selected: unknown[] });\n };\n chart.on('selectionChange', selectionHandler);\n handlers.set('selectionChange', selectionHandler);\n }\n\n this.eventHandlers.set(chartId, handlers);\n }\n\n private detachEventHandlers(chart: ChartLike): void {\n const chartId = chart.getId();\n const handlers = this.eventHandlers.get(chartId);\n if (!handlers) return;\n for (const [event, handler] of handlers.entries()) {\n chart.off(event, handler);\n }\n this.eventHandlers.delete(chartId);\n }\n\n private handleZoom(sourceId: string, event: { x: Range; y: Range }): void {\n if (this.isUpdating) return;\n if (!this.canPropagateFrom(sourceId)) return;\n\n const sourceChart = this.charts.get(sourceId);\n if (sourceChart && !this.hasValidViewBounds(sourceChart.getViewBounds())) return;\n\n const bounds: Partial<Bounds> = {};\n if (this.options.axis === 'x' || this.options.axis === 'xy') {\n bounds.xMin = event.x[0];\n bounds.xMax = event.x[1];\n }\n if (this.options.axis === 'y' || this.options.axis === 'xy') {\n bounds.yMin = event.y[0];\n bounds.yMax = event.y[1];\n }\n\n if (bounds.xMin !== undefined && bounds.xMax !== undefined) {\n const xSpan = bounds.xMax - bounds.xMin;\n if (!Number.isFinite(xSpan) || xSpan <= 0) return;\n }\n\n this.scheduleSyncAction(`zoom-${sourceId}`, () => {\n this.propagateZoom(sourceId, bounds);\n });\n }\n\n private handlePan(sourceId: string, event: { deltaX: number; deltaY: number }): void {\n if (this.isUpdating) return;\n if (!this.canPropagateFrom(sourceId)) return;\n\n const dx = (this.options.axis === 'x' || this.options.axis === 'xy') ? event.deltaX : 0;\n const dy = (this.options.axis === 'y' || this.options.axis === 'xy') ? event.deltaY : 0;\n if (dx === 0 && dy === 0) return;\n\n this.scheduleSyncAction(`pan-${sourceId}`, () => {\n this.propagatePan(sourceId, dx, dy);\n });\n }\n\n private handleCursor(sourceId: string, event: { point?: { x: number; y: number } } | null): void {\n if (this.isUpdating) return;\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n if (event?.point) chart.setExternalCursor?.(event.point.x, event.point.y);\n else chart.clearExternalCursor?.();\n }\n }\n\n private handleSelection(_sourceId: string, _event: { selected: unknown[] }): void {\n if (this.isUpdating || !this.options.syncSelection) return;\n }\n\n private propagateZoom(sourceId: string, bounds: Partial<Bounds>): void {\n if (this.isUpdating) return;\n this.isUpdating = true;\n try {\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n const zoomOptions: { x?: Range; y?: Range; animate?: boolean } = { animate: false };\n if (bounds.xMin !== undefined && bounds.xMax !== undefined) {\n zoomOptions.x = [bounds.xMin, bounds.xMax];\n }\n if (bounds.yMin !== undefined && bounds.yMax !== undefined) {\n zoomOptions.y = [bounds.yMin, bounds.yMax];\n }\n chart.zoom(zoomOptions);\n }\n } finally {\n this.isUpdating = false;\n }\n }\n\n private propagatePan(sourceId: string, _dx: number, _dy: number): void {\n if (this.isUpdating) return;\n this.isUpdating = true;\n try {\n const sourceChart = this.charts.get(sourceId);\n if (!sourceChart || !this.hasValidViewBounds(sourceChart.getViewBounds())) return;\n\n const masterBounds = sourceChart.getViewBounds();\n\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n\n const zoomOptions: { x?: Range; y?: Range; animate?: boolean } = { animate: false };\n\n if (this.options.axis === 'x' || this.options.axis === 'xy') {\n zoomOptions.x = [masterBounds.xMin, masterBounds.xMax];\n }\n\n if (this.options.axis === 'y' || this.options.axis === 'xy') {\n zoomOptions.y = [masterBounds.yMin, masterBounds.yMax];\n }\n\n if (zoomOptions.x || zoomOptions.y) {\n chart.zoom(zoomOptions);\n }\n }\n } finally {\n this.isUpdating = false;\n }\n }\n\n private scheduleSyncAction(key: string, action: () => void): void {\n if (this.options.debounce > 0) {\n this.debounceAction(key, action);\n return;\n }\n if (this.rafTimers.has(key)) return;\n const rafId = requestAnimationFrame(() => {\n this.rafTimers.delete(key);\n action();\n });\n this.rafTimers.set(key, rafId);\n }\n\n private debounceAction(key: string, action: () => void): void {\n const existingTimer = this.debounceTimers.get(key);\n if (existingTimer) clearTimeout(existingTimer);\n const timerId = window.setTimeout(() => {\n this.debounceTimers.delete(key);\n action();\n }, this.options.debounce);\n this.debounceTimers.set(key, timerId);\n }\n}\n\nexport function createChartGroup(charts: ChartLike[], options?: SyncOptions): ChartGroup {\n const group = new ChartGroup(options);\n group.addAll(...charts);\n return group;\n}\n\nexport function linkCharts(chart1: ChartLike, chart2: ChartLike, options?: SyncOptions): ChartGroup {\n return createChartGroup([chart1, chart2], options);\n}\n\nexport function createMasterSlave(\n master: ChartLike,\n slave: ChartLike,\n axis: SyncAxis = 'x',\n): ChartGroup {\n return createChartGroup([master, slave], {\n axis,\n bidirectional: false,\n masterId: master.getId(),\n syncCursor: true,\n syncZoom: true,\n syncPan: true,\n });\n}\n","/**\n * SciPlot Engine - Chart Sync Plugin\n * \n * Provides synchronization between multiple chart instances.\n * \n * @module plugins/sync\n */\n\nexport * from \"../../core/sync\";\n\nimport type { PluginManifest, ChartPlugin, PluginContext } from \"../types\";\n\nexport interface PluginSyncConfig {\n /** Group ID to join */\n groupId?: string;\n /** Sync axes: 'x' | 'y' | 'both' */\n syncAxes?: \"x\" | \"y\" | \"both\";\n}\n\nconst manifestSync: PluginManifest = {\n name: \"velo-plot-sync\",\n version: \"1.0.0\",\n description: \"Multi-chart synchronization for velo-plot\",\n provides: [\"interaction\"],\n tags: [\"sync\", \"multi-chart\", \"coordination\"],\n};\n\n/**\n * SciPlot Sync Plugin\n * \n * Enables smooth coordination and synchronization between separate chart instances.\n */\nexport function PluginSync(config: PluginSyncConfig = {}): ChartPlugin<PluginSyncConfig> {\n void config;\n return {\n manifest: manifestSync,\n\n onInit(_ctx: PluginContext) { \n if (config.groupId) {\n // Join the specified group\n }\n },\n\n onDestroy(_ctx: PluginContext) {\n }\n };\n}\n\nexport default PluginSync;\n"],"names":["ChartGroup","options","__publicField","chart","chartId","charts","axis","enabled","partial","bounds","excludeChartId","sharedX","masterId","master","b","fn","prev","_a","timerId","rafId","sourceId","xSpan","ySpan","handlers","zoomHandler","args","panHandler","hoverHandler","selectionHandler","event","handler","sourceChart","dx","dy","_b","_sourceId","_event","zoomOptions","_dx","_dy","masterBounds","key","action","existingTimer","createChartGroup","group","linkCharts","chart1","chart2","createMasterSlave","slave","manifestSync","PluginSync","config","_ctx"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PlotArea, Bounds } from '../../../types';
|
|
1
|
+
import { PlotArea, Bounds, AxisOptions } from '../../../types';
|
|
2
2
|
import { Scale } from '../../../scales';
|
|
3
3
|
import { Series } from '../../../core/Series';
|
|
4
4
|
import { TooltipData, TooltipOptions, TooltipTheme, TooltipTemplate, ShowTooltipOptions, TooltipEventMap } from './types';
|
|
@@ -23,6 +23,10 @@ export interface TooltipManagerConfig {
|
|
|
23
23
|
getYScales: () => Map<string, Scale>;
|
|
24
24
|
/** Function to get view bounds */
|
|
25
25
|
getViewBounds: () => Bounds;
|
|
26
|
+
/** Function to get X axis options */
|
|
27
|
+
getXAxisOptions?: () => AxisOptions;
|
|
28
|
+
/** Function to get Y axis options for a series axis id */
|
|
29
|
+
getYAxisOptions?: (axisId?: string) => AxisOptions | undefined;
|
|
26
30
|
/** Initial options */
|
|
27
31
|
options?: TooltipOptions;
|
|
28
32
|
}
|
|
@@ -34,6 +38,9 @@ export declare class TooltipManager {
|
|
|
34
38
|
private pixelToDataY;
|
|
35
39
|
private getXScale;
|
|
36
40
|
private getYScales;
|
|
41
|
+
private getViewBounds;
|
|
42
|
+
private getXAxisOptions?;
|
|
43
|
+
private getYAxisOptions?;
|
|
37
44
|
private options;
|
|
38
45
|
private theme;
|
|
39
46
|
private positioner;
|
|
@@ -131,6 +138,7 @@ export declare class TooltipManager {
|
|
|
131
138
|
* Handle heatmap and crosshair tooltip types (fallback)
|
|
132
139
|
*/
|
|
133
140
|
private performFallbackTooltipUpdate;
|
|
141
|
+
private buildAxisFormat;
|
|
134
142
|
/**
|
|
135
143
|
* Find data point by X coordinate only (O(log n) - fastest method)
|
|
136
144
|
* Best for very large datasets where precision is less important than speed
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DataPointTooltip, CrosshairTooltip } from './types';
|
|
2
|
+
|
|
3
|
+
export declare function formatDataPointX(data: DataPointTooltip): string;
|
|
4
|
+
export declare function formatDataPointY(data: DataPointTooltip): string;
|
|
5
|
+
export declare function formatCrosshairX(data: CrosshairTooltip): string;
|
|
6
|
+
export declare function formatCrosshairY(value: number, data: CrosshairTooltip): string;
|
|
7
|
+
export declare function formatCompactValue(data: DataPointTooltip, axis: "x" | "y"): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* This module defines all types and interfaces for the high-performance
|
|
5
|
-
* tooltip system in Sci Plot.
|
|
6
|
-
*
|
|
7
|
-
* @module tooltip/types
|
|
8
|
-
*/
|
|
1
|
+
import { TooltipAxisFormat } from '../../../core/format/axisFormat';
|
|
2
|
+
|
|
9
3
|
/** Base type for all tooltip data */
|
|
10
4
|
export type TooltipType = 'datapoint' | 'crosshair' | 'range' | 'annotation' | 'heatmap' | 'axis';
|
|
11
5
|
/** Union type for all tooltip data structures */
|
|
@@ -37,6 +31,8 @@ export interface DataPointTooltip {
|
|
|
37
31
|
xError?: [number, number];
|
|
38
32
|
/** Cycle number (for CV data) */
|
|
39
33
|
cycle?: number;
|
|
34
|
+
/** Axis formatting context aligned with chart tick formatters */
|
|
35
|
+
axisFormat?: TooltipAxisFormat;
|
|
40
36
|
/** Custom metadata from series */
|
|
41
37
|
metadata?: Record<string, unknown>;
|
|
42
38
|
}
|
|
@@ -53,6 +49,8 @@ export interface CrosshairTooltip {
|
|
|
53
49
|
dataX: number;
|
|
54
50
|
/** Interpolated values for each visible series */
|
|
55
51
|
interpolatedValues: CrosshairSeriesValue[];
|
|
52
|
+
/** Axis formatting context aligned with chart tick formatters */
|
|
53
|
+
axisFormat?: TooltipAxisFormat;
|
|
56
54
|
}
|
|
57
55
|
export interface CrosshairSeriesValue {
|
|
58
56
|
/** Series ID */
|
package/dist/plugins/tools.js
CHANGED
package/dist/react/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* React bindings for
|
|
2
|
+
* React bindings for Velo Plot
|
|
3
3
|
*/
|
|
4
4
|
export { SciPlot, type SciPlotProps, type SciPlotRef, type SciPlotSeries } from './SciPlot';
|
|
5
5
|
export { useSciPlot, type UseSciPlotOptions, type UseSciPlotReturn } from './useSciPlot';
|
|
6
|
+
export { useStackedPlot, type UseStackedPlotOptions, type UseStackedPlotReturn } from './useStackedPlot';
|