hyperprop-charting-library 0.1.32 → 0.1.34
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/hyperprop-charting-library.cjs +82 -0
- package/dist/hyperprop-charting-library.d.ts +16 -1
- package/dist/hyperprop-charting-library.js +82 -0
- package/dist/index.cjs +82 -0
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +82 -0
- package/package.json +1 -1
|
@@ -821,10 +821,21 @@ function createChart(element, options = {}) {
|
|
|
821
821
|
let xSpan = 60;
|
|
822
822
|
let followLatest = true;
|
|
823
823
|
let followingLatestChangeHandler = null;
|
|
824
|
+
let viewportChangeHandler = null;
|
|
825
|
+
let viewportEmitScheduled = false;
|
|
826
|
+
const emitViewportChange = () => {
|
|
827
|
+
if (!viewportChangeHandler || viewportEmitScheduled) return;
|
|
828
|
+
viewportEmitScheduled = true;
|
|
829
|
+
queueMicrotask(() => {
|
|
830
|
+
viewportEmitScheduled = false;
|
|
831
|
+
viewportChangeHandler?.(getViewport());
|
|
832
|
+
});
|
|
833
|
+
};
|
|
824
834
|
const updateFollowLatest = (next) => {
|
|
825
835
|
if (followLatest === next) return;
|
|
826
836
|
followLatest = next;
|
|
827
837
|
followingLatestChangeHandler?.(next);
|
|
838
|
+
emitViewportChange();
|
|
828
839
|
};
|
|
829
840
|
let yMinOverride = null;
|
|
830
841
|
let yMaxOverride = null;
|
|
@@ -2158,6 +2169,7 @@ function createChart(element, options = {}) {
|
|
|
2158
2169
|
xCenter = nextStart + nextSpan / 2;
|
|
2159
2170
|
clampXViewport();
|
|
2160
2171
|
updateFollowLatest(false);
|
|
2172
|
+
emitViewportChange();
|
|
2161
2173
|
draw();
|
|
2162
2174
|
};
|
|
2163
2175
|
const zoomXToLatest = (factor) => {
|
|
@@ -2172,6 +2184,7 @@ function createChart(element, options = {}) {
|
|
|
2172
2184
|
xSpan = nextSpan;
|
|
2173
2185
|
xCenter = nextStart + nextSpan / 2;
|
|
2174
2186
|
clampXViewport();
|
|
2187
|
+
emitViewportChange();
|
|
2175
2188
|
draw();
|
|
2176
2189
|
};
|
|
2177
2190
|
const zoomY = (factor, anchorY) => {
|
|
@@ -2192,6 +2205,7 @@ function createChart(element, options = {}) {
|
|
|
2192
2205
|
const clamped = clampYRange(nextMin, nextMax);
|
|
2193
2206
|
yMinOverride = clamped.min;
|
|
2194
2207
|
yMaxOverride = clamped.max;
|
|
2208
|
+
emitViewportChange();
|
|
2195
2209
|
draw();
|
|
2196
2210
|
};
|
|
2197
2211
|
const pan = (deltaX, deltaY, allowX, allowY) => {
|
|
@@ -2215,6 +2229,9 @@ function createChart(element, options = {}) {
|
|
|
2215
2229
|
yMinOverride = clamped.min;
|
|
2216
2230
|
yMaxOverride = clamped.max;
|
|
2217
2231
|
}
|
|
2232
|
+
if (allowX || allowY) {
|
|
2233
|
+
emitViewportChange();
|
|
2234
|
+
}
|
|
2218
2235
|
draw();
|
|
2219
2236
|
};
|
|
2220
2237
|
const resetYViewport = () => {
|
|
@@ -2254,6 +2271,7 @@ function createChart(element, options = {}) {
|
|
|
2254
2271
|
xCenter += bars;
|
|
2255
2272
|
clampXViewport();
|
|
2256
2273
|
updateFollowLatest(false);
|
|
2274
|
+
emitViewportChange();
|
|
2257
2275
|
draw();
|
|
2258
2276
|
};
|
|
2259
2277
|
const panY = (priceDelta) => {
|
|
@@ -2265,17 +2283,20 @@ function createChart(element, options = {}) {
|
|
|
2265
2283
|
const clamped = clampYRange(currentMin + priceDelta, currentMax + priceDelta);
|
|
2266
2284
|
yMinOverride = clamped.min;
|
|
2267
2285
|
yMaxOverride = clamped.max;
|
|
2286
|
+
emitViewportChange();
|
|
2268
2287
|
draw();
|
|
2269
2288
|
};
|
|
2270
2289
|
const fitContent = () => {
|
|
2271
2290
|
fitXViewport();
|
|
2272
2291
|
updateFollowLatest(true);
|
|
2292
|
+
emitViewportChange();
|
|
2273
2293
|
draw();
|
|
2274
2294
|
};
|
|
2275
2295
|
const resetViewport = () => {
|
|
2276
2296
|
fitXViewport();
|
|
2277
2297
|
resetYViewport();
|
|
2278
2298
|
updateFollowLatest(true);
|
|
2299
|
+
emitViewportChange();
|
|
2279
2300
|
draw();
|
|
2280
2301
|
};
|
|
2281
2302
|
const isFollowingLatest = () => followLatest;
|
|
@@ -2292,6 +2313,64 @@ function createChart(element, options = {}) {
|
|
|
2292
2313
|
const onFollowingLatestChange = (handler) => {
|
|
2293
2314
|
followingLatestChangeHandler = handler;
|
|
2294
2315
|
};
|
|
2316
|
+
const getViewport = () => {
|
|
2317
|
+
let centerTimeMs = null;
|
|
2318
|
+
if (data.length > 0) {
|
|
2319
|
+
const centerRounded = Math.round(xCenter);
|
|
2320
|
+
if (centerRounded >= 0 && centerRounded < data.length) {
|
|
2321
|
+
centerTimeMs = data[centerRounded]?.time.getTime() ?? null;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return {
|
|
2325
|
+
xSpan,
|
|
2326
|
+
followingLatest: followLatest,
|
|
2327
|
+
centerTimeMs,
|
|
2328
|
+
yMin: yMinOverride,
|
|
2329
|
+
yMax: yMaxOverride
|
|
2330
|
+
};
|
|
2331
|
+
};
|
|
2332
|
+
const setViewport = (viewport) => {
|
|
2333
|
+
let changed = false;
|
|
2334
|
+
if (typeof viewport.xSpan === "number" && Number.isFinite(viewport.xSpan)) {
|
|
2335
|
+
const minSpan = minVisibleBars;
|
|
2336
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2337
|
+
const nextSpan = clamp(viewport.xSpan, minSpan, maxSpan);
|
|
2338
|
+
if (nextSpan !== xSpan) {
|
|
2339
|
+
xSpan = nextSpan;
|
|
2340
|
+
changed = true;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
if (typeof viewport.centerTimeMs === "number" && data.length > 0) {
|
|
2344
|
+
const nextCenter = findNearestIndexForTimeMs(viewport.centerTimeMs);
|
|
2345
|
+
if (nextCenter !== null) {
|
|
2346
|
+
xCenter = nextCenter;
|
|
2347
|
+
changed = true;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
if (viewport.yMin !== void 0) {
|
|
2351
|
+
yMinOverride = viewport.yMin;
|
|
2352
|
+
changed = true;
|
|
2353
|
+
}
|
|
2354
|
+
if (viewport.yMax !== void 0) {
|
|
2355
|
+
yMaxOverride = viewport.yMax;
|
|
2356
|
+
changed = true;
|
|
2357
|
+
}
|
|
2358
|
+
if (typeof viewport.followingLatest === "boolean") {
|
|
2359
|
+
if (followLatest !== viewport.followingLatest) {
|
|
2360
|
+
followLatest = viewport.followingLatest;
|
|
2361
|
+
followingLatestChangeHandler?.(followLatest);
|
|
2362
|
+
changed = true;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
if (changed) {
|
|
2366
|
+
clampXViewport();
|
|
2367
|
+
draw();
|
|
2368
|
+
emitViewportChange();
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
const onViewportChange = (handler) => {
|
|
2372
|
+
viewportChangeHandler = handler;
|
|
2373
|
+
};
|
|
2295
2374
|
const getCanvasPoint = (event) => {
|
|
2296
2375
|
const rect = canvas.getBoundingClientRect();
|
|
2297
2376
|
return {
|
|
@@ -2907,6 +2986,9 @@ function createChart(element, options = {}) {
|
|
|
2907
2986
|
isFollowingLatest,
|
|
2908
2987
|
setFollowingLatest,
|
|
2909
2988
|
onFollowingLatestChange,
|
|
2989
|
+
getViewport,
|
|
2990
|
+
setViewport,
|
|
2991
|
+
onViewportChange,
|
|
2910
2992
|
setDoubleClickEnabled,
|
|
2911
2993
|
setDoubleClickAction,
|
|
2912
2994
|
registerIndicator,
|
|
@@ -284,6 +284,9 @@ interface ChartInstance {
|
|
|
284
284
|
isFollowingLatest: () => boolean;
|
|
285
285
|
setFollowingLatest: (follow: boolean) => void;
|
|
286
286
|
onFollowingLatestChange: (handler: ((following: boolean) => void) | null) => void;
|
|
287
|
+
getViewport: () => ViewportState;
|
|
288
|
+
setViewport: (viewport: Partial<ViewportState>) => void;
|
|
289
|
+
onViewportChange: (handler: ((viewport: ViewportState) => void) | null) => void;
|
|
287
290
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
288
291
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
289
292
|
registerIndicator: (plugin: IndicatorPlugin<any>) => void;
|
|
@@ -305,6 +308,18 @@ interface OhlcDataPoint {
|
|
|
305
308
|
c: number;
|
|
306
309
|
v?: number;
|
|
307
310
|
}
|
|
311
|
+
interface ViewportState {
|
|
312
|
+
/** Number of bars visible horizontally. */
|
|
313
|
+
xSpan: number;
|
|
314
|
+
/** Whether the chart is auto-following the latest candle. */
|
|
315
|
+
followingLatest: boolean;
|
|
316
|
+
/** Timestamp (ms) at the horizontal center of the viewport. null when data is empty. */
|
|
317
|
+
centerTimeMs: number | null;
|
|
318
|
+
/** Manual Y-axis min override (null = auto-scale). */
|
|
319
|
+
yMin: number | null;
|
|
320
|
+
/** Manual Y-axis max override (null = auto-scale). */
|
|
321
|
+
yMax: number | null;
|
|
322
|
+
}
|
|
308
323
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
309
324
|
|
|
310
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPlugin, type IndicatorRenderContext, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
325
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPlugin, type IndicatorRenderContext, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|
|
@@ -797,10 +797,21 @@ function createChart(element, options = {}) {
|
|
|
797
797
|
let xSpan = 60;
|
|
798
798
|
let followLatest = true;
|
|
799
799
|
let followingLatestChangeHandler = null;
|
|
800
|
+
let viewportChangeHandler = null;
|
|
801
|
+
let viewportEmitScheduled = false;
|
|
802
|
+
const emitViewportChange = () => {
|
|
803
|
+
if (!viewportChangeHandler || viewportEmitScheduled) return;
|
|
804
|
+
viewportEmitScheduled = true;
|
|
805
|
+
queueMicrotask(() => {
|
|
806
|
+
viewportEmitScheduled = false;
|
|
807
|
+
viewportChangeHandler?.(getViewport());
|
|
808
|
+
});
|
|
809
|
+
};
|
|
800
810
|
const updateFollowLatest = (next) => {
|
|
801
811
|
if (followLatest === next) return;
|
|
802
812
|
followLatest = next;
|
|
803
813
|
followingLatestChangeHandler?.(next);
|
|
814
|
+
emitViewportChange();
|
|
804
815
|
};
|
|
805
816
|
let yMinOverride = null;
|
|
806
817
|
let yMaxOverride = null;
|
|
@@ -2134,6 +2145,7 @@ function createChart(element, options = {}) {
|
|
|
2134
2145
|
xCenter = nextStart + nextSpan / 2;
|
|
2135
2146
|
clampXViewport();
|
|
2136
2147
|
updateFollowLatest(false);
|
|
2148
|
+
emitViewportChange();
|
|
2137
2149
|
draw();
|
|
2138
2150
|
};
|
|
2139
2151
|
const zoomXToLatest = (factor) => {
|
|
@@ -2148,6 +2160,7 @@ function createChart(element, options = {}) {
|
|
|
2148
2160
|
xSpan = nextSpan;
|
|
2149
2161
|
xCenter = nextStart + nextSpan / 2;
|
|
2150
2162
|
clampXViewport();
|
|
2163
|
+
emitViewportChange();
|
|
2151
2164
|
draw();
|
|
2152
2165
|
};
|
|
2153
2166
|
const zoomY = (factor, anchorY) => {
|
|
@@ -2168,6 +2181,7 @@ function createChart(element, options = {}) {
|
|
|
2168
2181
|
const clamped = clampYRange(nextMin, nextMax);
|
|
2169
2182
|
yMinOverride = clamped.min;
|
|
2170
2183
|
yMaxOverride = clamped.max;
|
|
2184
|
+
emitViewportChange();
|
|
2171
2185
|
draw();
|
|
2172
2186
|
};
|
|
2173
2187
|
const pan = (deltaX, deltaY, allowX, allowY) => {
|
|
@@ -2191,6 +2205,9 @@ function createChart(element, options = {}) {
|
|
|
2191
2205
|
yMinOverride = clamped.min;
|
|
2192
2206
|
yMaxOverride = clamped.max;
|
|
2193
2207
|
}
|
|
2208
|
+
if (allowX || allowY) {
|
|
2209
|
+
emitViewportChange();
|
|
2210
|
+
}
|
|
2194
2211
|
draw();
|
|
2195
2212
|
};
|
|
2196
2213
|
const resetYViewport = () => {
|
|
@@ -2230,6 +2247,7 @@ function createChart(element, options = {}) {
|
|
|
2230
2247
|
xCenter += bars;
|
|
2231
2248
|
clampXViewport();
|
|
2232
2249
|
updateFollowLatest(false);
|
|
2250
|
+
emitViewportChange();
|
|
2233
2251
|
draw();
|
|
2234
2252
|
};
|
|
2235
2253
|
const panY = (priceDelta) => {
|
|
@@ -2241,17 +2259,20 @@ function createChart(element, options = {}) {
|
|
|
2241
2259
|
const clamped = clampYRange(currentMin + priceDelta, currentMax + priceDelta);
|
|
2242
2260
|
yMinOverride = clamped.min;
|
|
2243
2261
|
yMaxOverride = clamped.max;
|
|
2262
|
+
emitViewportChange();
|
|
2244
2263
|
draw();
|
|
2245
2264
|
};
|
|
2246
2265
|
const fitContent = () => {
|
|
2247
2266
|
fitXViewport();
|
|
2248
2267
|
updateFollowLatest(true);
|
|
2268
|
+
emitViewportChange();
|
|
2249
2269
|
draw();
|
|
2250
2270
|
};
|
|
2251
2271
|
const resetViewport = () => {
|
|
2252
2272
|
fitXViewport();
|
|
2253
2273
|
resetYViewport();
|
|
2254
2274
|
updateFollowLatest(true);
|
|
2275
|
+
emitViewportChange();
|
|
2255
2276
|
draw();
|
|
2256
2277
|
};
|
|
2257
2278
|
const isFollowingLatest = () => followLatest;
|
|
@@ -2268,6 +2289,64 @@ function createChart(element, options = {}) {
|
|
|
2268
2289
|
const onFollowingLatestChange = (handler) => {
|
|
2269
2290
|
followingLatestChangeHandler = handler;
|
|
2270
2291
|
};
|
|
2292
|
+
const getViewport = () => {
|
|
2293
|
+
let centerTimeMs = null;
|
|
2294
|
+
if (data.length > 0) {
|
|
2295
|
+
const centerRounded = Math.round(xCenter);
|
|
2296
|
+
if (centerRounded >= 0 && centerRounded < data.length) {
|
|
2297
|
+
centerTimeMs = data[centerRounded]?.time.getTime() ?? null;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return {
|
|
2301
|
+
xSpan,
|
|
2302
|
+
followingLatest: followLatest,
|
|
2303
|
+
centerTimeMs,
|
|
2304
|
+
yMin: yMinOverride,
|
|
2305
|
+
yMax: yMaxOverride
|
|
2306
|
+
};
|
|
2307
|
+
};
|
|
2308
|
+
const setViewport = (viewport) => {
|
|
2309
|
+
let changed = false;
|
|
2310
|
+
if (typeof viewport.xSpan === "number" && Number.isFinite(viewport.xSpan)) {
|
|
2311
|
+
const minSpan = minVisibleBars;
|
|
2312
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2313
|
+
const nextSpan = clamp(viewport.xSpan, minSpan, maxSpan);
|
|
2314
|
+
if (nextSpan !== xSpan) {
|
|
2315
|
+
xSpan = nextSpan;
|
|
2316
|
+
changed = true;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
if (typeof viewport.centerTimeMs === "number" && data.length > 0) {
|
|
2320
|
+
const nextCenter = findNearestIndexForTimeMs(viewport.centerTimeMs);
|
|
2321
|
+
if (nextCenter !== null) {
|
|
2322
|
+
xCenter = nextCenter;
|
|
2323
|
+
changed = true;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
if (viewport.yMin !== void 0) {
|
|
2327
|
+
yMinOverride = viewport.yMin;
|
|
2328
|
+
changed = true;
|
|
2329
|
+
}
|
|
2330
|
+
if (viewport.yMax !== void 0) {
|
|
2331
|
+
yMaxOverride = viewport.yMax;
|
|
2332
|
+
changed = true;
|
|
2333
|
+
}
|
|
2334
|
+
if (typeof viewport.followingLatest === "boolean") {
|
|
2335
|
+
if (followLatest !== viewport.followingLatest) {
|
|
2336
|
+
followLatest = viewport.followingLatest;
|
|
2337
|
+
followingLatestChangeHandler?.(followLatest);
|
|
2338
|
+
changed = true;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
if (changed) {
|
|
2342
|
+
clampXViewport();
|
|
2343
|
+
draw();
|
|
2344
|
+
emitViewportChange();
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
const onViewportChange = (handler) => {
|
|
2348
|
+
viewportChangeHandler = handler;
|
|
2349
|
+
};
|
|
2271
2350
|
const getCanvasPoint = (event) => {
|
|
2272
2351
|
const rect = canvas.getBoundingClientRect();
|
|
2273
2352
|
return {
|
|
@@ -2883,6 +2962,9 @@ function createChart(element, options = {}) {
|
|
|
2883
2962
|
isFollowingLatest,
|
|
2884
2963
|
setFollowingLatest,
|
|
2885
2964
|
onFollowingLatestChange,
|
|
2965
|
+
getViewport,
|
|
2966
|
+
setViewport,
|
|
2967
|
+
onViewportChange,
|
|
2886
2968
|
setDoubleClickEnabled,
|
|
2887
2969
|
setDoubleClickAction,
|
|
2888
2970
|
registerIndicator,
|
package/dist/index.cjs
CHANGED
|
@@ -821,10 +821,21 @@ function createChart(element, options = {}) {
|
|
|
821
821
|
let xSpan = 60;
|
|
822
822
|
let followLatest = true;
|
|
823
823
|
let followingLatestChangeHandler = null;
|
|
824
|
+
let viewportChangeHandler = null;
|
|
825
|
+
let viewportEmitScheduled = false;
|
|
826
|
+
const emitViewportChange = () => {
|
|
827
|
+
if (!viewportChangeHandler || viewportEmitScheduled) return;
|
|
828
|
+
viewportEmitScheduled = true;
|
|
829
|
+
queueMicrotask(() => {
|
|
830
|
+
viewportEmitScheduled = false;
|
|
831
|
+
viewportChangeHandler?.(getViewport());
|
|
832
|
+
});
|
|
833
|
+
};
|
|
824
834
|
const updateFollowLatest = (next) => {
|
|
825
835
|
if (followLatest === next) return;
|
|
826
836
|
followLatest = next;
|
|
827
837
|
followingLatestChangeHandler?.(next);
|
|
838
|
+
emitViewportChange();
|
|
828
839
|
};
|
|
829
840
|
let yMinOverride = null;
|
|
830
841
|
let yMaxOverride = null;
|
|
@@ -2158,6 +2169,7 @@ function createChart(element, options = {}) {
|
|
|
2158
2169
|
xCenter = nextStart + nextSpan / 2;
|
|
2159
2170
|
clampXViewport();
|
|
2160
2171
|
updateFollowLatest(false);
|
|
2172
|
+
emitViewportChange();
|
|
2161
2173
|
draw();
|
|
2162
2174
|
};
|
|
2163
2175
|
const zoomXToLatest = (factor) => {
|
|
@@ -2172,6 +2184,7 @@ function createChart(element, options = {}) {
|
|
|
2172
2184
|
xSpan = nextSpan;
|
|
2173
2185
|
xCenter = nextStart + nextSpan / 2;
|
|
2174
2186
|
clampXViewport();
|
|
2187
|
+
emitViewportChange();
|
|
2175
2188
|
draw();
|
|
2176
2189
|
};
|
|
2177
2190
|
const zoomY = (factor, anchorY) => {
|
|
@@ -2192,6 +2205,7 @@ function createChart(element, options = {}) {
|
|
|
2192
2205
|
const clamped = clampYRange(nextMin, nextMax);
|
|
2193
2206
|
yMinOverride = clamped.min;
|
|
2194
2207
|
yMaxOverride = clamped.max;
|
|
2208
|
+
emitViewportChange();
|
|
2195
2209
|
draw();
|
|
2196
2210
|
};
|
|
2197
2211
|
const pan = (deltaX, deltaY, allowX, allowY) => {
|
|
@@ -2215,6 +2229,9 @@ function createChart(element, options = {}) {
|
|
|
2215
2229
|
yMinOverride = clamped.min;
|
|
2216
2230
|
yMaxOverride = clamped.max;
|
|
2217
2231
|
}
|
|
2232
|
+
if (allowX || allowY) {
|
|
2233
|
+
emitViewportChange();
|
|
2234
|
+
}
|
|
2218
2235
|
draw();
|
|
2219
2236
|
};
|
|
2220
2237
|
const resetYViewport = () => {
|
|
@@ -2254,6 +2271,7 @@ function createChart(element, options = {}) {
|
|
|
2254
2271
|
xCenter += bars;
|
|
2255
2272
|
clampXViewport();
|
|
2256
2273
|
updateFollowLatest(false);
|
|
2274
|
+
emitViewportChange();
|
|
2257
2275
|
draw();
|
|
2258
2276
|
};
|
|
2259
2277
|
const panY = (priceDelta) => {
|
|
@@ -2265,17 +2283,20 @@ function createChart(element, options = {}) {
|
|
|
2265
2283
|
const clamped = clampYRange(currentMin + priceDelta, currentMax + priceDelta);
|
|
2266
2284
|
yMinOverride = clamped.min;
|
|
2267
2285
|
yMaxOverride = clamped.max;
|
|
2286
|
+
emitViewportChange();
|
|
2268
2287
|
draw();
|
|
2269
2288
|
};
|
|
2270
2289
|
const fitContent = () => {
|
|
2271
2290
|
fitXViewport();
|
|
2272
2291
|
updateFollowLatest(true);
|
|
2292
|
+
emitViewportChange();
|
|
2273
2293
|
draw();
|
|
2274
2294
|
};
|
|
2275
2295
|
const resetViewport = () => {
|
|
2276
2296
|
fitXViewport();
|
|
2277
2297
|
resetYViewport();
|
|
2278
2298
|
updateFollowLatest(true);
|
|
2299
|
+
emitViewportChange();
|
|
2279
2300
|
draw();
|
|
2280
2301
|
};
|
|
2281
2302
|
const isFollowingLatest = () => followLatest;
|
|
@@ -2292,6 +2313,64 @@ function createChart(element, options = {}) {
|
|
|
2292
2313
|
const onFollowingLatestChange = (handler) => {
|
|
2293
2314
|
followingLatestChangeHandler = handler;
|
|
2294
2315
|
};
|
|
2316
|
+
const getViewport = () => {
|
|
2317
|
+
let centerTimeMs = null;
|
|
2318
|
+
if (data.length > 0) {
|
|
2319
|
+
const centerRounded = Math.round(xCenter);
|
|
2320
|
+
if (centerRounded >= 0 && centerRounded < data.length) {
|
|
2321
|
+
centerTimeMs = data[centerRounded]?.time.getTime() ?? null;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return {
|
|
2325
|
+
xSpan,
|
|
2326
|
+
followingLatest: followLatest,
|
|
2327
|
+
centerTimeMs,
|
|
2328
|
+
yMin: yMinOverride,
|
|
2329
|
+
yMax: yMaxOverride
|
|
2330
|
+
};
|
|
2331
|
+
};
|
|
2332
|
+
const setViewport = (viewport) => {
|
|
2333
|
+
let changed = false;
|
|
2334
|
+
if (typeof viewport.xSpan === "number" && Number.isFinite(viewport.xSpan)) {
|
|
2335
|
+
const minSpan = minVisibleBars;
|
|
2336
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2337
|
+
const nextSpan = clamp(viewport.xSpan, minSpan, maxSpan);
|
|
2338
|
+
if (nextSpan !== xSpan) {
|
|
2339
|
+
xSpan = nextSpan;
|
|
2340
|
+
changed = true;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
if (typeof viewport.centerTimeMs === "number" && data.length > 0) {
|
|
2344
|
+
const nextCenter = findNearestIndexForTimeMs(viewport.centerTimeMs);
|
|
2345
|
+
if (nextCenter !== null) {
|
|
2346
|
+
xCenter = nextCenter;
|
|
2347
|
+
changed = true;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
if (viewport.yMin !== void 0) {
|
|
2351
|
+
yMinOverride = viewport.yMin;
|
|
2352
|
+
changed = true;
|
|
2353
|
+
}
|
|
2354
|
+
if (viewport.yMax !== void 0) {
|
|
2355
|
+
yMaxOverride = viewport.yMax;
|
|
2356
|
+
changed = true;
|
|
2357
|
+
}
|
|
2358
|
+
if (typeof viewport.followingLatest === "boolean") {
|
|
2359
|
+
if (followLatest !== viewport.followingLatest) {
|
|
2360
|
+
followLatest = viewport.followingLatest;
|
|
2361
|
+
followingLatestChangeHandler?.(followLatest);
|
|
2362
|
+
changed = true;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
if (changed) {
|
|
2366
|
+
clampXViewport();
|
|
2367
|
+
draw();
|
|
2368
|
+
emitViewportChange();
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
const onViewportChange = (handler) => {
|
|
2372
|
+
viewportChangeHandler = handler;
|
|
2373
|
+
};
|
|
2295
2374
|
const getCanvasPoint = (event) => {
|
|
2296
2375
|
const rect = canvas.getBoundingClientRect();
|
|
2297
2376
|
return {
|
|
@@ -2907,6 +2986,9 @@ function createChart(element, options = {}) {
|
|
|
2907
2986
|
isFollowingLatest,
|
|
2908
2987
|
setFollowingLatest,
|
|
2909
2988
|
onFollowingLatestChange,
|
|
2989
|
+
getViewport,
|
|
2990
|
+
setViewport,
|
|
2991
|
+
onViewportChange,
|
|
2910
2992
|
setDoubleClickEnabled,
|
|
2911
2993
|
setDoubleClickAction,
|
|
2912
2994
|
registerIndicator,
|
package/dist/index.d.cts
CHANGED
|
@@ -284,6 +284,9 @@ interface ChartInstance {
|
|
|
284
284
|
isFollowingLatest: () => boolean;
|
|
285
285
|
setFollowingLatest: (follow: boolean) => void;
|
|
286
286
|
onFollowingLatestChange: (handler: ((following: boolean) => void) | null) => void;
|
|
287
|
+
getViewport: () => ViewportState;
|
|
288
|
+
setViewport: (viewport: Partial<ViewportState>) => void;
|
|
289
|
+
onViewportChange: (handler: ((viewport: ViewportState) => void) | null) => void;
|
|
287
290
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
288
291
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
289
292
|
registerIndicator: (plugin: IndicatorPlugin<any>) => void;
|
|
@@ -305,6 +308,18 @@ interface OhlcDataPoint {
|
|
|
305
308
|
c: number;
|
|
306
309
|
v?: number;
|
|
307
310
|
}
|
|
311
|
+
interface ViewportState {
|
|
312
|
+
/** Number of bars visible horizontally. */
|
|
313
|
+
xSpan: number;
|
|
314
|
+
/** Whether the chart is auto-following the latest candle. */
|
|
315
|
+
followingLatest: boolean;
|
|
316
|
+
/** Timestamp (ms) at the horizontal center of the viewport. null when data is empty. */
|
|
317
|
+
centerTimeMs: number | null;
|
|
318
|
+
/** Manual Y-axis min override (null = auto-scale). */
|
|
319
|
+
yMin: number | null;
|
|
320
|
+
/** Manual Y-axis max override (null = auto-scale). */
|
|
321
|
+
yMax: number | null;
|
|
322
|
+
}
|
|
308
323
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
309
324
|
|
|
310
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPlugin, type IndicatorRenderContext, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
325
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPlugin, type IndicatorRenderContext, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|
package/dist/index.d.ts
CHANGED
|
@@ -284,6 +284,9 @@ interface ChartInstance {
|
|
|
284
284
|
isFollowingLatest: () => boolean;
|
|
285
285
|
setFollowingLatest: (follow: boolean) => void;
|
|
286
286
|
onFollowingLatestChange: (handler: ((following: boolean) => void) | null) => void;
|
|
287
|
+
getViewport: () => ViewportState;
|
|
288
|
+
setViewport: (viewport: Partial<ViewportState>) => void;
|
|
289
|
+
onViewportChange: (handler: ((viewport: ViewportState) => void) | null) => void;
|
|
287
290
|
setDoubleClickEnabled: (enabled: boolean) => void;
|
|
288
291
|
setDoubleClickAction: (action: "reset" | "placeLimitOrder") => void;
|
|
289
292
|
registerIndicator: (plugin: IndicatorPlugin<any>) => void;
|
|
@@ -305,6 +308,18 @@ interface OhlcDataPoint {
|
|
|
305
308
|
c: number;
|
|
306
309
|
v?: number;
|
|
307
310
|
}
|
|
311
|
+
interface ViewportState {
|
|
312
|
+
/** Number of bars visible horizontally. */
|
|
313
|
+
xSpan: number;
|
|
314
|
+
/** Whether the chart is auto-following the latest candle. */
|
|
315
|
+
followingLatest: boolean;
|
|
316
|
+
/** Timestamp (ms) at the horizontal center of the viewport. null when data is empty. */
|
|
317
|
+
centerTimeMs: number | null;
|
|
318
|
+
/** Manual Y-axis min override (null = auto-scale). */
|
|
319
|
+
yMin: number | null;
|
|
320
|
+
/** Manual Y-axis max override (null = auto-scale). */
|
|
321
|
+
yMax: number | null;
|
|
322
|
+
}
|
|
308
323
|
declare function createChart(element: HTMLElement, options?: ChartOptions): ChartInstance;
|
|
309
324
|
|
|
310
|
-
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPlugin, type IndicatorRenderContext, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type WatermarkOptions, createChart };
|
|
325
|
+
export { type AxisOptions, type BuiltInIndicatorInfo, type ChartClickEvent, type ChartInstance, type ChartOptions, type CrosshairMoveEvent, type CrosshairOptions, type CrosshairPriceActionEvent, type DashPatternOptions, type GridOptions, type IndicatorInstanceOptions, type IndicatorPane, type IndicatorPlugin, type IndicatorRenderContext, type OhlcDataPoint, type OrderActionButton, type OrderActionEvent, type OrderLineOptions, type PriceLineOptions, type TickerLineOptions, type ViewportState, type WatermarkOptions, createChart };
|
package/dist/index.js
CHANGED
|
@@ -797,10 +797,21 @@ function createChart(element, options = {}) {
|
|
|
797
797
|
let xSpan = 60;
|
|
798
798
|
let followLatest = true;
|
|
799
799
|
let followingLatestChangeHandler = null;
|
|
800
|
+
let viewportChangeHandler = null;
|
|
801
|
+
let viewportEmitScheduled = false;
|
|
802
|
+
const emitViewportChange = () => {
|
|
803
|
+
if (!viewportChangeHandler || viewportEmitScheduled) return;
|
|
804
|
+
viewportEmitScheduled = true;
|
|
805
|
+
queueMicrotask(() => {
|
|
806
|
+
viewportEmitScheduled = false;
|
|
807
|
+
viewportChangeHandler?.(getViewport());
|
|
808
|
+
});
|
|
809
|
+
};
|
|
800
810
|
const updateFollowLatest = (next) => {
|
|
801
811
|
if (followLatest === next) return;
|
|
802
812
|
followLatest = next;
|
|
803
813
|
followingLatestChangeHandler?.(next);
|
|
814
|
+
emitViewportChange();
|
|
804
815
|
};
|
|
805
816
|
let yMinOverride = null;
|
|
806
817
|
let yMaxOverride = null;
|
|
@@ -2134,6 +2145,7 @@ function createChart(element, options = {}) {
|
|
|
2134
2145
|
xCenter = nextStart + nextSpan / 2;
|
|
2135
2146
|
clampXViewport();
|
|
2136
2147
|
updateFollowLatest(false);
|
|
2148
|
+
emitViewportChange();
|
|
2137
2149
|
draw();
|
|
2138
2150
|
};
|
|
2139
2151
|
const zoomXToLatest = (factor) => {
|
|
@@ -2148,6 +2160,7 @@ function createChart(element, options = {}) {
|
|
|
2148
2160
|
xSpan = nextSpan;
|
|
2149
2161
|
xCenter = nextStart + nextSpan / 2;
|
|
2150
2162
|
clampXViewport();
|
|
2163
|
+
emitViewportChange();
|
|
2151
2164
|
draw();
|
|
2152
2165
|
};
|
|
2153
2166
|
const zoomY = (factor, anchorY) => {
|
|
@@ -2168,6 +2181,7 @@ function createChart(element, options = {}) {
|
|
|
2168
2181
|
const clamped = clampYRange(nextMin, nextMax);
|
|
2169
2182
|
yMinOverride = clamped.min;
|
|
2170
2183
|
yMaxOverride = clamped.max;
|
|
2184
|
+
emitViewportChange();
|
|
2171
2185
|
draw();
|
|
2172
2186
|
};
|
|
2173
2187
|
const pan = (deltaX, deltaY, allowX, allowY) => {
|
|
@@ -2191,6 +2205,9 @@ function createChart(element, options = {}) {
|
|
|
2191
2205
|
yMinOverride = clamped.min;
|
|
2192
2206
|
yMaxOverride = clamped.max;
|
|
2193
2207
|
}
|
|
2208
|
+
if (allowX || allowY) {
|
|
2209
|
+
emitViewportChange();
|
|
2210
|
+
}
|
|
2194
2211
|
draw();
|
|
2195
2212
|
};
|
|
2196
2213
|
const resetYViewport = () => {
|
|
@@ -2230,6 +2247,7 @@ function createChart(element, options = {}) {
|
|
|
2230
2247
|
xCenter += bars;
|
|
2231
2248
|
clampXViewport();
|
|
2232
2249
|
updateFollowLatest(false);
|
|
2250
|
+
emitViewportChange();
|
|
2233
2251
|
draw();
|
|
2234
2252
|
};
|
|
2235
2253
|
const panY = (priceDelta) => {
|
|
@@ -2241,17 +2259,20 @@ function createChart(element, options = {}) {
|
|
|
2241
2259
|
const clamped = clampYRange(currentMin + priceDelta, currentMax + priceDelta);
|
|
2242
2260
|
yMinOverride = clamped.min;
|
|
2243
2261
|
yMaxOverride = clamped.max;
|
|
2262
|
+
emitViewportChange();
|
|
2244
2263
|
draw();
|
|
2245
2264
|
};
|
|
2246
2265
|
const fitContent = () => {
|
|
2247
2266
|
fitXViewport();
|
|
2248
2267
|
updateFollowLatest(true);
|
|
2268
|
+
emitViewportChange();
|
|
2249
2269
|
draw();
|
|
2250
2270
|
};
|
|
2251
2271
|
const resetViewport = () => {
|
|
2252
2272
|
fitXViewport();
|
|
2253
2273
|
resetYViewport();
|
|
2254
2274
|
updateFollowLatest(true);
|
|
2275
|
+
emitViewportChange();
|
|
2255
2276
|
draw();
|
|
2256
2277
|
};
|
|
2257
2278
|
const isFollowingLatest = () => followLatest;
|
|
@@ -2268,6 +2289,64 @@ function createChart(element, options = {}) {
|
|
|
2268
2289
|
const onFollowingLatestChange = (handler) => {
|
|
2269
2290
|
followingLatestChangeHandler = handler;
|
|
2270
2291
|
};
|
|
2292
|
+
const getViewport = () => {
|
|
2293
|
+
let centerTimeMs = null;
|
|
2294
|
+
if (data.length > 0) {
|
|
2295
|
+
const centerRounded = Math.round(xCenter);
|
|
2296
|
+
if (centerRounded >= 0 && centerRounded < data.length) {
|
|
2297
|
+
centerTimeMs = data[centerRounded]?.time.getTime() ?? null;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return {
|
|
2301
|
+
xSpan,
|
|
2302
|
+
followingLatest: followLatest,
|
|
2303
|
+
centerTimeMs,
|
|
2304
|
+
yMin: yMinOverride,
|
|
2305
|
+
yMax: yMaxOverride
|
|
2306
|
+
};
|
|
2307
|
+
};
|
|
2308
|
+
const setViewport = (viewport) => {
|
|
2309
|
+
let changed = false;
|
|
2310
|
+
if (typeof viewport.xSpan === "number" && Number.isFinite(viewport.xSpan)) {
|
|
2311
|
+
const minSpan = minVisibleBars;
|
|
2312
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2313
|
+
const nextSpan = clamp(viewport.xSpan, minSpan, maxSpan);
|
|
2314
|
+
if (nextSpan !== xSpan) {
|
|
2315
|
+
xSpan = nextSpan;
|
|
2316
|
+
changed = true;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
if (typeof viewport.centerTimeMs === "number" && data.length > 0) {
|
|
2320
|
+
const nextCenter = findNearestIndexForTimeMs(viewport.centerTimeMs);
|
|
2321
|
+
if (nextCenter !== null) {
|
|
2322
|
+
xCenter = nextCenter;
|
|
2323
|
+
changed = true;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
if (viewport.yMin !== void 0) {
|
|
2327
|
+
yMinOverride = viewport.yMin;
|
|
2328
|
+
changed = true;
|
|
2329
|
+
}
|
|
2330
|
+
if (viewport.yMax !== void 0) {
|
|
2331
|
+
yMaxOverride = viewport.yMax;
|
|
2332
|
+
changed = true;
|
|
2333
|
+
}
|
|
2334
|
+
if (typeof viewport.followingLatest === "boolean") {
|
|
2335
|
+
if (followLatest !== viewport.followingLatest) {
|
|
2336
|
+
followLatest = viewport.followingLatest;
|
|
2337
|
+
followingLatestChangeHandler?.(followLatest);
|
|
2338
|
+
changed = true;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
if (changed) {
|
|
2342
|
+
clampXViewport();
|
|
2343
|
+
draw();
|
|
2344
|
+
emitViewportChange();
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
const onViewportChange = (handler) => {
|
|
2348
|
+
viewportChangeHandler = handler;
|
|
2349
|
+
};
|
|
2271
2350
|
const getCanvasPoint = (event) => {
|
|
2272
2351
|
const rect = canvas.getBoundingClientRect();
|
|
2273
2352
|
return {
|
|
@@ -2883,6 +2962,9 @@ function createChart(element, options = {}) {
|
|
|
2883
2962
|
isFollowingLatest,
|
|
2884
2963
|
setFollowingLatest,
|
|
2885
2964
|
onFollowingLatestChange,
|
|
2965
|
+
getViewport,
|
|
2966
|
+
setViewport,
|
|
2967
|
+
onViewportChange,
|
|
2886
2968
|
setDoubleClickEnabled,
|
|
2887
2969
|
setDoubleClickAction,
|
|
2888
2970
|
registerIndicator,
|