react-klinecharts-ui 0.4.0 → 0.6.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 +475 -51
- package/dist/{chunk-PIFLJKYF.cjs → chunk-FSHI2ZDB.cjs} +125 -19
- package/dist/chunk-FSHI2ZDB.cjs.map +1 -0
- package/dist/{chunk-U325AHHX.js → chunk-MVD3ILJT.js} +123 -20
- package/dist/chunk-MVD3ILJT.js.map +1 -0
- package/dist/extensions.cjs +18 -6
- package/dist/extensions.d.cts +37 -4
- package/dist/extensions.d.ts +37 -4
- package/dist/extensions.js +1 -1
- package/dist/index.cjs +346 -211
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +220 -34
- package/dist/index.d.ts +220 -34
- package/dist/index.js +298 -169
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-PIFLJKYF.cjs.map +0 -1
- package/dist/chunk-U325AHHX.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { registerExtensions, TA_default } from './chunk-
|
|
2
|
-
export { TA_default as TA, abcd_default as abcd, anyWaves_default as anyWaves, arrow_default as arrow, bollTv_default as bollTv, brush_default as brush, cci_default as cci, circle_default as circle, depthOverlay_default as depthOverlay, eightWaves_default as eightWaves, elliottWave_default as elliottWave, fibRetracement_default as fibRetracement, fibonacciCircle_default as fibonacciCircle, fibonacciExtension_default as fibonacciExtension, fibonacciSegment_default as fibonacciSegment, fibonacciSpeedResistanceFan_default as fibonacciSpeedResistanceFan, fibonacciSpiral_default as fibonacciSpiral, fiveWaves_default as fiveWaves, gannBox_default as gannBox, gannFan_default as gannFan, hma_default as hma, ichimoku_default as ichimoku, indicators, longPosition_default as longPosition, maRibbon_default as maRibbon, macdTv_default as macdTv, measure_default as measure, orderLine_default as orderLine, overlays, parallelChannel_default as parallelChannel, parallelogram_default as parallelogram, pivotPoints_default as pivotPoints, ray_default as ray, rect_default as rect, registerExtensions, rsiTv_default as rsiTv, shortPosition_default as shortPosition, stochastic_default as stochastic, superTrend_default as superTrend, threeWaves_default as threeWaves, triangle_default as triangle, vwap_default as vwap, xabcd_default as xabcd } from './chunk-
|
|
1
|
+
import { registerExtensions, TA_default, ensureAlertLineRegistered } from './chunk-MVD3ILJT.js';
|
|
2
|
+
export { TA_default as TA, abcd_default as abcd, alertLine_default as alertLine, anyWaves_default as anyWaves, arrow_default as arrow, bollTv_default as bollTv, brush_default as brush, cci_default as cci, circle_default as circle, depthOverlay_default as depthOverlay, eightWaves_default as eightWaves, elliottWave_default as elliottWave, fibRetracement_default as fibRetracement, fibonacciCircle_default as fibonacciCircle, fibonacciExtension_default as fibonacciExtension, fibonacciSegment_default as fibonacciSegment, fibonacciSpeedResistanceFan_default as fibonacciSpeedResistanceFan, fibonacciSpiral_default as fibonacciSpiral, fiveWaves_default as fiveWaves, gannBox_default as gannBox, gannFan_default as gannFan, hma_default as hma, ichimoku_default as ichimoku, indicators, longPosition_default as longPosition, maRibbon_default as maRibbon, macdTv_default as macdTv, measure_default as measure, orderLine_default as orderLine, overlays, parallelChannel_default as parallelChannel, parallelogram_default as parallelogram, pivotPoints_default as pivotPoints, ray_default as ray, rect_default as rect, registerExtensions, rsiTv_default as rsiTv, shortPosition_default as shortPosition, stochastic_default as stochastic, superTrend_default as superTrend, threeWaves_default as threeWaves, triangle_default as triangle, vwap_default as vwap, xabcd_default as xabcd } from './chunk-MVD3ILJT.js';
|
|
3
3
|
import { createContext, useContext, useReducer, useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
-
import { registerOverlay, registerIndicator } from 'react-klinecharts';
|
|
4
|
+
import { registerOverlay, registerHotkey, getHotkey, getSupportedHotkeys, registerIndicator } from 'react-klinecharts';
|
|
5
5
|
import { jsx } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
var KlinechartsUIStateContext = createContext(null);
|
|
@@ -59,6 +59,14 @@ function reducer(state, action) {
|
|
|
59
59
|
return { ...state, subIndicators: action.indicators };
|
|
60
60
|
case "SET_INDICATOR_AXES":
|
|
61
61
|
return { ...state, indicatorAxes: action.axes };
|
|
62
|
+
case "SET_INDICATOR_VISIBILITY":
|
|
63
|
+
return { ...state, indicatorVisibility: action.visibility };
|
|
64
|
+
case "SET_ALERTS":
|
|
65
|
+
return { ...state, alerts: action.alerts };
|
|
66
|
+
case "SET_MEASURE":
|
|
67
|
+
return { ...state, measure: { ...state.measure, ...action.measure } };
|
|
68
|
+
case "SET_REPLAY":
|
|
69
|
+
return { ...state, replay: { ...state.replay, ...action.replay } };
|
|
62
70
|
case "SET_STYLES":
|
|
63
71
|
return { ...state, styles: action.styles };
|
|
64
72
|
case "SET_LOCALE":
|
|
@@ -122,12 +130,27 @@ function KlinechartsUIProvider({
|
|
|
122
130
|
{}
|
|
123
131
|
),
|
|
124
132
|
indicatorAxes: {},
|
|
133
|
+
indicatorVisibility: {},
|
|
134
|
+
alerts: [],
|
|
135
|
+
measure: { isActive: false, fromPoint: null, result: null },
|
|
136
|
+
replay: {
|
|
137
|
+
isReplaying: false,
|
|
138
|
+
isPaused: false,
|
|
139
|
+
speed: 1,
|
|
140
|
+
barIndex: 0,
|
|
141
|
+
totalBars: 0
|
|
142
|
+
},
|
|
125
143
|
styles: opts.styles,
|
|
126
144
|
screenshotUrl: null
|
|
127
145
|
})
|
|
128
146
|
);
|
|
129
147
|
const fullscreenContainerRef = useRef(null);
|
|
130
148
|
const undoRedoListenerRef = useRef(null);
|
|
149
|
+
const alertTriggeredListenerRef = useRef(null);
|
|
150
|
+
const replayIntervalRef = useRef(null);
|
|
151
|
+
const replaySavedDataRef = useRef([]);
|
|
152
|
+
const replayIndexRef = useRef(0);
|
|
153
|
+
const alertPrevCloseRef = useRef(null);
|
|
131
154
|
const stateRef = useRef(state);
|
|
132
155
|
stateRef.current = state;
|
|
133
156
|
const callbacksRef = useRef({
|
|
@@ -188,13 +211,58 @@ function KlinechartsUIProvider({
|
|
|
188
211
|
state.chart.setStyles(state.styles);
|
|
189
212
|
}
|
|
190
213
|
}, [state.chart, state.styles]);
|
|
214
|
+
const hasAlerts = state.alerts.length > 0;
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
const chart = state.chart;
|
|
217
|
+
if (!chart || !hasAlerts) return;
|
|
218
|
+
alertPrevCloseRef.current = null;
|
|
219
|
+
const interval = setInterval(() => {
|
|
220
|
+
const dataList = chart.getDataList();
|
|
221
|
+
if (!dataList || dataList.length === 0) return;
|
|
222
|
+
const currentClose = dataList[dataList.length - 1].close;
|
|
223
|
+
const prevClose = alertPrevCloseRef.current;
|
|
224
|
+
alertPrevCloseRef.current = currentClose;
|
|
225
|
+
if (prevClose === null) return;
|
|
226
|
+
const alerts = stateRef.current.alerts;
|
|
227
|
+
let changed = false;
|
|
228
|
+
const next = alerts.map((alert) => {
|
|
229
|
+
if (alert.triggered) return alert;
|
|
230
|
+
const crossedUp = prevClose < alert.price && currentClose >= alert.price;
|
|
231
|
+
const crossedDown = prevClose > alert.price && currentClose <= alert.price;
|
|
232
|
+
const shouldTrigger = alert.condition === "crossing_up" ? crossedUp : alert.condition === "crossing_down" ? crossedDown : crossedUp || crossedDown;
|
|
233
|
+
if (shouldTrigger) {
|
|
234
|
+
changed = true;
|
|
235
|
+
const triggered = { ...alert, triggered: true };
|
|
236
|
+
alertTriggeredListenerRef.current?.(triggered);
|
|
237
|
+
return triggered;
|
|
238
|
+
}
|
|
239
|
+
return alert;
|
|
240
|
+
});
|
|
241
|
+
if (changed) {
|
|
242
|
+
enhancedDispatch({ type: "SET_ALERTS", alerts: next });
|
|
243
|
+
}
|
|
244
|
+
}, 1e3);
|
|
245
|
+
return () => clearInterval(interval);
|
|
246
|
+
}, [state.chart, hasAlerts, enhancedDispatch]);
|
|
247
|
+
useEffect(() => {
|
|
248
|
+
return () => {
|
|
249
|
+
if (replayIntervalRef.current !== null) {
|
|
250
|
+
clearInterval(replayIntervalRef.current);
|
|
251
|
+
replayIntervalRef.current = null;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}, []);
|
|
191
255
|
const dispatchValue = useMemo(
|
|
192
256
|
() => ({
|
|
193
257
|
dispatch: enhancedDispatch,
|
|
194
258
|
datafeed,
|
|
195
259
|
onSettingsChange,
|
|
196
260
|
fullscreenContainerRef,
|
|
197
|
-
undoRedoListenerRef
|
|
261
|
+
undoRedoListenerRef,
|
|
262
|
+
alertTriggeredListenerRef,
|
|
263
|
+
replayIntervalRef,
|
|
264
|
+
replaySavedDataRef,
|
|
265
|
+
replayIndexRef
|
|
198
266
|
}),
|
|
199
267
|
[enhancedDispatch, datafeed, onSettingsChange]
|
|
200
268
|
);
|
|
@@ -656,18 +724,26 @@ function useIndicators() {
|
|
|
656
724
|
const activeNames = state.mainIndicators;
|
|
657
725
|
const inactive = MAIN_INDICATORS.filter((n) => !activeNames.includes(n));
|
|
658
726
|
return [
|
|
659
|
-
...activeNames.map((name) => ({
|
|
660
|
-
|
|
727
|
+
...activeNames.map((name) => ({
|
|
728
|
+
name,
|
|
729
|
+
isActive: true,
|
|
730
|
+
visible: state.indicatorVisibility[`main_${name}`] ?? true
|
|
731
|
+
})),
|
|
732
|
+
...inactive.map((name) => ({ name, isActive: false, visible: true }))
|
|
661
733
|
];
|
|
662
|
-
}, [state.mainIndicators]);
|
|
734
|
+
}, [state.mainIndicators, state.indicatorVisibility]);
|
|
663
735
|
const subIndicators = useMemo(() => {
|
|
664
736
|
const activeNames = Object.keys(state.subIndicators);
|
|
665
737
|
const inactive = SUB_INDICATORS.filter((n) => !activeNames.includes(n));
|
|
666
738
|
return [
|
|
667
|
-
...activeNames.map((name) => ({
|
|
668
|
-
|
|
739
|
+
...activeNames.map((name) => ({
|
|
740
|
+
name,
|
|
741
|
+
isActive: true,
|
|
742
|
+
visible: state.indicatorVisibility[`sub_${name}`] ?? true
|
|
743
|
+
})),
|
|
744
|
+
...inactive.map((name) => ({ name, isActive: false, visible: true }))
|
|
669
745
|
];
|
|
670
|
-
}, [state.subIndicators]);
|
|
746
|
+
}, [state.subIndicators, state.indicatorVisibility]);
|
|
671
747
|
const addMainIndicator = useCallback(
|
|
672
748
|
(name, options) => {
|
|
673
749
|
if (state.mainIndicators.includes(name)) return;
|
|
@@ -709,12 +785,24 @@ function useIndicators() {
|
|
|
709
785
|
delete nextAxes[id];
|
|
710
786
|
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
711
787
|
}
|
|
788
|
+
if (id in state.indicatorVisibility) {
|
|
789
|
+
const nextVisibility = { ...state.indicatorVisibility };
|
|
790
|
+
delete nextVisibility[id];
|
|
791
|
+
dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
|
|
792
|
+
}
|
|
712
793
|
undoRedoListenerRef.current?.({
|
|
713
794
|
type: "indicator_toggled",
|
|
714
795
|
data: { name, wasActive: true, isMain: true, paneId: "candle_pane", yAxisId }
|
|
715
796
|
});
|
|
716
797
|
},
|
|
717
|
-
[
|
|
798
|
+
[
|
|
799
|
+
state.chart,
|
|
800
|
+
state.mainIndicators,
|
|
801
|
+
state.indicatorAxes,
|
|
802
|
+
state.indicatorVisibility,
|
|
803
|
+
dispatch,
|
|
804
|
+
undoRedoListenerRef
|
|
805
|
+
]
|
|
718
806
|
);
|
|
719
807
|
const addSubIndicator = useCallback(
|
|
720
808
|
(name, options) => {
|
|
@@ -752,12 +840,24 @@ function useIndicators() {
|
|
|
752
840
|
delete nextAxes[id];
|
|
753
841
|
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
754
842
|
}
|
|
843
|
+
if (id in state.indicatorVisibility) {
|
|
844
|
+
const nextVisibility = { ...state.indicatorVisibility };
|
|
845
|
+
delete nextVisibility[id];
|
|
846
|
+
dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
|
|
847
|
+
}
|
|
755
848
|
undoRedoListenerRef.current?.({
|
|
756
849
|
type: "indicator_toggled",
|
|
757
850
|
data: { name, wasActive: true, isMain: false, paneId, yAxisId }
|
|
758
851
|
});
|
|
759
852
|
},
|
|
760
|
-
[
|
|
853
|
+
[
|
|
854
|
+
state.chart,
|
|
855
|
+
state.subIndicators,
|
|
856
|
+
state.indicatorAxes,
|
|
857
|
+
state.indicatorVisibility,
|
|
858
|
+
dispatch,
|
|
859
|
+
undoRedoListenerRef
|
|
860
|
+
]
|
|
761
861
|
);
|
|
762
862
|
const toggleMainIndicator = useCallback(
|
|
763
863
|
(name) => {
|
|
@@ -783,8 +883,16 @@ function useIndicators() {
|
|
|
783
883
|
(name, isMain, visible) => {
|
|
784
884
|
const id = isMain ? `main_${name}` : `sub_${name}`;
|
|
785
885
|
state.chart?.overrideIndicator({ name, id, visible });
|
|
886
|
+
const nextVisibility = { ...state.indicatorVisibility };
|
|
887
|
+
if (visible) delete nextVisibility[id];
|
|
888
|
+
else nextVisibility[id] = false;
|
|
889
|
+
dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
|
|
786
890
|
},
|
|
787
|
-
[state.chart]
|
|
891
|
+
[state.chart, state.indicatorVisibility, dispatch]
|
|
892
|
+
);
|
|
893
|
+
const isIndicatorVisible = useCallback(
|
|
894
|
+
(name, isMain) => state.indicatorVisibility[isMain ? `main_${name}` : `sub_${name}`] ?? true,
|
|
895
|
+
[state.indicatorVisibility]
|
|
788
896
|
);
|
|
789
897
|
const updateIndicatorParams = useCallback(
|
|
790
898
|
// `paneId` is kept in the signature for API stability; klinecharts v10
|
|
@@ -867,9 +975,14 @@ function useIndicators() {
|
|
|
867
975
|
id: paneId,
|
|
868
976
|
height: COLLAPSED_HEIGHT
|
|
869
977
|
});
|
|
870
|
-
|
|
978
|
+
const id = `sub_${name}`;
|
|
979
|
+
state.chart.overrideIndicator({ name, id, visible: false });
|
|
980
|
+
dispatch({
|
|
981
|
+
type: "SET_INDICATOR_VISIBILITY",
|
|
982
|
+
visibility: { ...state.indicatorVisibility, [id]: false }
|
|
983
|
+
});
|
|
871
984
|
},
|
|
872
|
-
[state.chart, state.subIndicators]
|
|
985
|
+
[state.chart, state.subIndicators, state.indicatorVisibility, dispatch]
|
|
873
986
|
);
|
|
874
987
|
const expandSubIndicator = useCallback(
|
|
875
988
|
(name) => {
|
|
@@ -881,9 +994,13 @@ function useIndicators() {
|
|
|
881
994
|
id: paneId,
|
|
882
995
|
height: savedHeight
|
|
883
996
|
});
|
|
884
|
-
|
|
997
|
+
const id = `sub_${name}`;
|
|
998
|
+
state.chart.overrideIndicator({ name, id, visible: true });
|
|
999
|
+
const nextVisibility = { ...state.indicatorVisibility };
|
|
1000
|
+
delete nextVisibility[id];
|
|
1001
|
+
dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
|
|
885
1002
|
},
|
|
886
|
-
[state.chart, state.subIndicators]
|
|
1003
|
+
[state.chart, state.subIndicators, state.indicatorVisibility, dispatch]
|
|
887
1004
|
);
|
|
888
1005
|
const isSubIndicatorCollapsed = useCallback(
|
|
889
1006
|
(name) => {
|
|
@@ -992,6 +1109,7 @@ function useIndicators() {
|
|
|
992
1109
|
moveToMain,
|
|
993
1110
|
moveToSub,
|
|
994
1111
|
setIndicatorVisible,
|
|
1112
|
+
isIndicatorVisible,
|
|
995
1113
|
updateIndicatorParams,
|
|
996
1114
|
getIndicatorParams,
|
|
997
1115
|
isMainIndicatorActive,
|
|
@@ -1002,6 +1120,7 @@ function useIndicators() {
|
|
|
1002
1120
|
reorderSubIndicator,
|
|
1003
1121
|
indicatorAxes: state.indicatorAxes,
|
|
1004
1122
|
getIndicatorAxis,
|
|
1123
|
+
indicatorVisibility: state.indicatorVisibility,
|
|
1005
1124
|
bindIndicatorToNewAxis
|
|
1006
1125
|
};
|
|
1007
1126
|
}
|
|
@@ -1558,6 +1677,53 @@ function useScreenshot() {
|
|
|
1558
1677
|
clear
|
|
1559
1678
|
};
|
|
1560
1679
|
}
|
|
1680
|
+
function useHotkeys() {
|
|
1681
|
+
const { state } = useKlinechartsUI();
|
|
1682
|
+
const registerHotkey$1 = useCallback((template) => {
|
|
1683
|
+
registerHotkey(template);
|
|
1684
|
+
}, []);
|
|
1685
|
+
const getHotkey$1 = useCallback(
|
|
1686
|
+
(name) => getHotkey(name) ?? null,
|
|
1687
|
+
[]
|
|
1688
|
+
);
|
|
1689
|
+
const setHotkeysEnabled = useCallback(
|
|
1690
|
+
(enabled, exclude) => {
|
|
1691
|
+
state.chart?.setHotkey(
|
|
1692
|
+
exclude ? { enabled, exclude } : { enabled }
|
|
1693
|
+
);
|
|
1694
|
+
},
|
|
1695
|
+
[state.chart]
|
|
1696
|
+
);
|
|
1697
|
+
const getHotkeysConfig = useCallback(
|
|
1698
|
+
() => state.chart?.getHotkey() ?? null,
|
|
1699
|
+
[state.chart]
|
|
1700
|
+
);
|
|
1701
|
+
return {
|
|
1702
|
+
registerHotkey: registerHotkey$1,
|
|
1703
|
+
getHotkey: getHotkey$1,
|
|
1704
|
+
supportedHotkeys: getSupportedHotkeys(),
|
|
1705
|
+
setHotkeysEnabled,
|
|
1706
|
+
getHotkeysConfig
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function useChartAxes() {
|
|
1710
|
+
const { state } = useKlinechartsUI();
|
|
1711
|
+
const overrideXAxis = useCallback(
|
|
1712
|
+
(override) => {
|
|
1713
|
+
if (!state.chart) return;
|
|
1714
|
+
state.chart.overrideXAxis(override);
|
|
1715
|
+
},
|
|
1716
|
+
[state.chart]
|
|
1717
|
+
);
|
|
1718
|
+
const overrideYAxis = useCallback(
|
|
1719
|
+
(override) => {
|
|
1720
|
+
if (!state.chart) return;
|
|
1721
|
+
state.chart.overrideYAxis(override);
|
|
1722
|
+
},
|
|
1723
|
+
[state.chart]
|
|
1724
|
+
);
|
|
1725
|
+
return { overrideXAxis, overrideYAxis };
|
|
1726
|
+
}
|
|
1561
1727
|
function useFullscreen() {
|
|
1562
1728
|
const { fullscreenContainerRef } = useKlinechartsUIDispatch();
|
|
1563
1729
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
@@ -2122,6 +2288,7 @@ function useLayoutManager() {
|
|
|
2122
2288
|
const newMainIndicators = [];
|
|
2123
2289
|
const newSubIndicators = {};
|
|
2124
2290
|
const restoredAxes = {};
|
|
2291
|
+
const restoredVisibility = {};
|
|
2125
2292
|
if (chartState.indicators) {
|
|
2126
2293
|
for (const ind of chartState.indicators) {
|
|
2127
2294
|
const isMain = ind.paneId === "candle_pane";
|
|
@@ -2149,6 +2316,9 @@ function useLayoutManager() {
|
|
|
2149
2316
|
if (ind.yAxisId) {
|
|
2150
2317
|
restoredAxes[id2] = ind.yAxisId;
|
|
2151
2318
|
}
|
|
2319
|
+
if (ind.visible === false) {
|
|
2320
|
+
restoredVisibility[id2] = false;
|
|
2321
|
+
}
|
|
2152
2322
|
if (isMain) {
|
|
2153
2323
|
newMainIndicators.push(ind.name);
|
|
2154
2324
|
} else {
|
|
@@ -2168,6 +2338,10 @@ function useLayoutManager() {
|
|
|
2168
2338
|
type: "SET_INDICATOR_AXES",
|
|
2169
2339
|
axes: restoredAxes
|
|
2170
2340
|
});
|
|
2341
|
+
dispatch({
|
|
2342
|
+
type: "SET_INDICATOR_VISIBILITY",
|
|
2343
|
+
visibility: restoredVisibility
|
|
2344
|
+
});
|
|
2171
2345
|
if (chartState.drawings) {
|
|
2172
2346
|
for (const drawing of chartState.drawings) {
|
|
2173
2347
|
chart.createOverlay({
|
|
@@ -2534,100 +2708,63 @@ function useCrosshair() {
|
|
|
2534
2708
|
}
|
|
2535
2709
|
var alertCounter = 0;
|
|
2536
2710
|
function useAlerts() {
|
|
2537
|
-
const { state } = useKlinechartsUI();
|
|
2538
|
-
const
|
|
2539
|
-
const
|
|
2540
|
-
const prevCloseRef = useRef(null);
|
|
2711
|
+
const { state, dispatch } = useKlinechartsUI();
|
|
2712
|
+
const { alertTriggeredListenerRef } = useKlinechartsUIDispatch();
|
|
2713
|
+
const alerts = state.alerts;
|
|
2541
2714
|
const addAlert = useCallback(
|
|
2542
|
-
(price, condition, message) => {
|
|
2715
|
+
(price, condition, message, extendData) => {
|
|
2543
2716
|
const id = `alert_${++alertCounter}`;
|
|
2717
|
+
const precision = state.chart?.getSymbol()?.pricePrecision ?? 2;
|
|
2718
|
+
const resolvedExtendData = {
|
|
2719
|
+
...extendData,
|
|
2720
|
+
text: extendData?.text ?? message ?? price.toFixed(precision)
|
|
2721
|
+
};
|
|
2544
2722
|
const alert = {
|
|
2545
2723
|
id,
|
|
2546
2724
|
price,
|
|
2547
2725
|
condition,
|
|
2548
2726
|
message,
|
|
2549
|
-
triggered: false
|
|
2727
|
+
triggered: false,
|
|
2728
|
+
extendData: resolvedExtendData
|
|
2550
2729
|
};
|
|
2551
|
-
|
|
2730
|
+
dispatch({ type: "SET_ALERTS", alerts: [...state.alerts, alert] });
|
|
2552
2731
|
if (state.chart) {
|
|
2732
|
+
ensureAlertLineRegistered();
|
|
2553
2733
|
state.chart.createOverlay({
|
|
2554
|
-
name: "
|
|
2734
|
+
name: "alertLine",
|
|
2555
2735
|
id,
|
|
2556
2736
|
groupId: "price_alerts",
|
|
2557
2737
|
points: [{ value: price }],
|
|
2558
|
-
|
|
2559
|
-
line: {
|
|
2560
|
-
style: "dashed",
|
|
2561
|
-
color: "#ff9800",
|
|
2562
|
-
size: 1
|
|
2563
|
-
}
|
|
2564
|
-
},
|
|
2738
|
+
extendData: resolvedExtendData,
|
|
2565
2739
|
lock: true
|
|
2566
2740
|
});
|
|
2567
2741
|
}
|
|
2568
2742
|
return id;
|
|
2569
2743
|
},
|
|
2570
|
-
[state.chart]
|
|
2744
|
+
[state.chart, state.alerts, dispatch]
|
|
2571
2745
|
);
|
|
2572
2746
|
const removeAlert = useCallback(
|
|
2573
2747
|
(id) => {
|
|
2574
|
-
|
|
2748
|
+
dispatch({
|
|
2749
|
+
type: "SET_ALERTS",
|
|
2750
|
+
alerts: state.alerts.filter((a) => a.id !== id)
|
|
2751
|
+
});
|
|
2575
2752
|
state.chart?.removeOverlay({ id });
|
|
2576
2753
|
},
|
|
2577
|
-
[state.chart]
|
|
2754
|
+
[state.chart, state.alerts, dispatch]
|
|
2578
2755
|
);
|
|
2579
2756
|
const clearAlerts = useCallback(() => {
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
});
|
|
2586
|
-
}, [state.chart]);
|
|
2757
|
+
for (const alert of state.alerts) {
|
|
2758
|
+
state.chart?.removeOverlay({ id: alert.id });
|
|
2759
|
+
}
|
|
2760
|
+
dispatch({ type: "SET_ALERTS", alerts: [] });
|
|
2761
|
+
}, [state.chart, state.alerts, dispatch]);
|
|
2587
2762
|
const onAlertTriggered = useCallback(
|
|
2588
2763
|
(callback) => {
|
|
2589
|
-
|
|
2764
|
+
alertTriggeredListenerRef.current = callback;
|
|
2590
2765
|
},
|
|
2591
|
-
[]
|
|
2766
|
+
[alertTriggeredListenerRef]
|
|
2592
2767
|
);
|
|
2593
|
-
useEffect(() => {
|
|
2594
|
-
if (!state.chart) return;
|
|
2595
|
-
const interval = setInterval(() => {
|
|
2596
|
-
const dataList = state.chart?.getDataList();
|
|
2597
|
-
if (!dataList || dataList.length === 0) return;
|
|
2598
|
-
const lastBar = dataList[dataList.length - 1];
|
|
2599
|
-
const currentClose = lastBar.close;
|
|
2600
|
-
const prevClose = prevCloseRef.current;
|
|
2601
|
-
if (prevClose === null) {
|
|
2602
|
-
prevCloseRef.current = currentClose;
|
|
2603
|
-
return;
|
|
2604
|
-
}
|
|
2605
|
-
prevCloseRef.current = currentClose;
|
|
2606
|
-
setAlerts((prev) => {
|
|
2607
|
-
let changed = false;
|
|
2608
|
-
const next = prev.map((alert) => {
|
|
2609
|
-
if (alert.triggered) return alert;
|
|
2610
|
-
let shouldTrigger = false;
|
|
2611
|
-
if (alert.condition === "crossing_up") {
|
|
2612
|
-
shouldTrigger = prevClose < alert.price && currentClose >= alert.price;
|
|
2613
|
-
} else if (alert.condition === "crossing_down") {
|
|
2614
|
-
shouldTrigger = prevClose > alert.price && currentClose <= alert.price;
|
|
2615
|
-
} else if (alert.condition === "crossing") {
|
|
2616
|
-
shouldTrigger = prevClose < alert.price && currentClose >= alert.price || prevClose > alert.price && currentClose <= alert.price;
|
|
2617
|
-
}
|
|
2618
|
-
if (shouldTrigger) {
|
|
2619
|
-
changed = true;
|
|
2620
|
-
const triggered = { ...alert, triggered: true };
|
|
2621
|
-
callbackRef.current?.(triggered);
|
|
2622
|
-
return triggered;
|
|
2623
|
-
}
|
|
2624
|
-
return alert;
|
|
2625
|
-
});
|
|
2626
|
-
return changed ? next : prev;
|
|
2627
|
-
});
|
|
2628
|
-
}, 1e3);
|
|
2629
|
-
return () => clearInterval(interval);
|
|
2630
|
-
}, [state.chart]);
|
|
2631
2768
|
return {
|
|
2632
2769
|
alerts,
|
|
2633
2770
|
addAlert,
|
|
@@ -2773,128 +2910,124 @@ function useWatchlist() {
|
|
|
2773
2910
|
};
|
|
2774
2911
|
}
|
|
2775
2912
|
function useReplay() {
|
|
2776
|
-
const { state } = useKlinechartsUI();
|
|
2777
|
-
const
|
|
2778
|
-
const
|
|
2779
|
-
const [speed, setSpeedState] = useState(1);
|
|
2780
|
-
const [barIndex, setBarIndex] = useState(0);
|
|
2781
|
-
const [totalBars, setTotalBars] = useState(0);
|
|
2782
|
-
const intervalRef = useRef(null);
|
|
2783
|
-
const savedDataRef = useRef([]);
|
|
2784
|
-
const currentIndexRef = useRef(0);
|
|
2913
|
+
const { state, dispatch } = useKlinechartsUI();
|
|
2914
|
+
const { replayIntervalRef, replaySavedDataRef, replayIndexRef } = useKlinechartsUIDispatch();
|
|
2915
|
+
const { isReplaying, isPaused, speed, barIndex, totalBars } = state.replay;
|
|
2785
2916
|
const clearInterval_ = useCallback(() => {
|
|
2786
|
-
if (
|
|
2787
|
-
clearInterval(
|
|
2788
|
-
|
|
2917
|
+
if (replayIntervalRef.current !== null) {
|
|
2918
|
+
clearInterval(replayIntervalRef.current);
|
|
2919
|
+
replayIntervalRef.current = null;
|
|
2789
2920
|
}
|
|
2790
|
-
}, []);
|
|
2921
|
+
}, [replayIntervalRef]);
|
|
2791
2922
|
const addNextBar = useCallback(() => {
|
|
2792
2923
|
if (!state.chart) return;
|
|
2793
|
-
const data =
|
|
2794
|
-
const idx =
|
|
2924
|
+
const data = replaySavedDataRef.current;
|
|
2925
|
+
const idx = replayIndexRef.current;
|
|
2795
2926
|
if (idx >= data.length) {
|
|
2796
2927
|
clearInterval_();
|
|
2797
|
-
|
|
2928
|
+
dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
|
|
2798
2929
|
return;
|
|
2799
2930
|
}
|
|
2800
2931
|
const bar = data[idx];
|
|
2801
2932
|
state.chart.updateData?.(bar);
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
}, [state.chart, clearInterval_]);
|
|
2933
|
+
replayIndexRef.current = idx + 1;
|
|
2934
|
+
dispatch({ type: "SET_REPLAY", replay: { barIndex: idx + 1 } });
|
|
2935
|
+
}, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
|
|
2805
2936
|
const startInterval = useCallback(
|
|
2806
2937
|
(currentSpeed) => {
|
|
2807
2938
|
clearInterval_();
|
|
2808
|
-
|
|
2939
|
+
replayIntervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
|
|
2809
2940
|
},
|
|
2810
|
-
[addNextBar, clearInterval_]
|
|
2941
|
+
[addNextBar, clearInterval_, replayIntervalRef]
|
|
2811
2942
|
);
|
|
2812
2943
|
const startReplay = useCallback(() => {
|
|
2813
2944
|
if (!state.chart) return;
|
|
2814
2945
|
const dataList = state.chart.getDataList();
|
|
2815
2946
|
if (!dataList || dataList.length === 0) return;
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2947
|
+
replaySavedDataRef.current = [...dataList];
|
|
2948
|
+
replayIndexRef.current = 0;
|
|
2949
|
+
dispatch({
|
|
2950
|
+
type: "SET_REPLAY",
|
|
2951
|
+
replay: {
|
|
2952
|
+
totalBars: dataList.length,
|
|
2953
|
+
barIndex: 0,
|
|
2954
|
+
isReplaying: true,
|
|
2955
|
+
isPaused: false
|
|
2956
|
+
}
|
|
2957
|
+
});
|
|
2822
2958
|
state.chart.clearData?.();
|
|
2823
2959
|
startInterval(speed);
|
|
2824
|
-
}, [state.chart, speed, startInterval]);
|
|
2960
|
+
}, [state.chart, speed, startInterval, replaySavedDataRef, replayIndexRef, dispatch]);
|
|
2825
2961
|
const stopReplay = useCallback(() => {
|
|
2826
2962
|
if (!state.chart) return;
|
|
2827
2963
|
clearInterval_();
|
|
2828
|
-
const data =
|
|
2964
|
+
const data = replaySavedDataRef.current;
|
|
2829
2965
|
if (data.length > 0) {
|
|
2830
2966
|
state.chart.clearData?.();
|
|
2831
2967
|
for (const bar of data) {
|
|
2832
2968
|
state.chart.updateData?.(bar);
|
|
2833
2969
|
}
|
|
2834
2970
|
}
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
}, [state.chart, clearInterval_]);
|
|
2971
|
+
replaySavedDataRef.current = [];
|
|
2972
|
+
replayIndexRef.current = 0;
|
|
2973
|
+
dispatch({
|
|
2974
|
+
type: "SET_REPLAY",
|
|
2975
|
+
replay: { isReplaying: false, isPaused: false, barIndex: 0, totalBars: 0 }
|
|
2976
|
+
});
|
|
2977
|
+
}, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
|
|
2842
2978
|
const togglePause = useCallback(() => {
|
|
2843
2979
|
if (!isReplaying) return;
|
|
2844
2980
|
if (isPaused) {
|
|
2845
2981
|
startInterval(speed);
|
|
2846
|
-
|
|
2982
|
+
dispatch({ type: "SET_REPLAY", replay: { isPaused: false } });
|
|
2847
2983
|
} else {
|
|
2848
2984
|
clearInterval_();
|
|
2849
|
-
|
|
2985
|
+
dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
|
|
2850
2986
|
}
|
|
2851
|
-
}, [isReplaying, isPaused, speed, startInterval, clearInterval_]);
|
|
2987
|
+
}, [isReplaying, isPaused, speed, startInterval, clearInterval_, dispatch]);
|
|
2852
2988
|
const stepForward = useCallback(() => {
|
|
2853
2989
|
if (!isReplaying || !isPaused) return;
|
|
2854
2990
|
addNextBar();
|
|
2855
2991
|
}, [isReplaying, isPaused, addNextBar]);
|
|
2856
2992
|
const stepBackward = useCallback(() => {
|
|
2857
2993
|
if (!isReplaying || !isPaused || !state.chart) return;
|
|
2858
|
-
const idx =
|
|
2994
|
+
const idx = replayIndexRef.current;
|
|
2859
2995
|
if (idx <= 1) return;
|
|
2860
|
-
const data =
|
|
2996
|
+
const data = replaySavedDataRef.current;
|
|
2861
2997
|
state.chart.clearData?.();
|
|
2862
2998
|
for (let i = 0; i < idx - 1; i++) {
|
|
2863
2999
|
state.chart.updateData?.(data[i]);
|
|
2864
3000
|
}
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
}, [isReplaying, isPaused, state.chart]);
|
|
3001
|
+
replayIndexRef.current = idx - 1;
|
|
3002
|
+
dispatch({ type: "SET_REPLAY", replay: { barIndex: idx - 1 } });
|
|
3003
|
+
}, [isReplaying, isPaused, state.chart, replaySavedDataRef, replayIndexRef, dispatch]);
|
|
2868
3004
|
const seekTo = useCallback(
|
|
2869
3005
|
(targetIndex) => {
|
|
2870
3006
|
if (!isReplaying || !state.chart) return;
|
|
2871
|
-
const data =
|
|
3007
|
+
const data = replaySavedDataRef.current;
|
|
2872
3008
|
const clamped = Math.max(0, Math.min(targetIndex, data.length));
|
|
2873
3009
|
clearInterval_();
|
|
2874
|
-
setIsPaused(true);
|
|
2875
3010
|
state.chart.clearData?.();
|
|
2876
3011
|
for (let i = 0; i < clamped; i++) {
|
|
2877
3012
|
state.chart.updateData?.(data[i]);
|
|
2878
3013
|
}
|
|
2879
|
-
|
|
2880
|
-
|
|
3014
|
+
replayIndexRef.current = clamped;
|
|
3015
|
+
dispatch({
|
|
3016
|
+
type: "SET_REPLAY",
|
|
3017
|
+
replay: { isPaused: true, barIndex: clamped }
|
|
3018
|
+
});
|
|
2881
3019
|
},
|
|
2882
|
-
[isReplaying, state.chart, clearInterval_]
|
|
3020
|
+
[isReplaying, state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]
|
|
2883
3021
|
);
|
|
2884
3022
|
const setSpeed = useCallback(
|
|
2885
3023
|
(newSpeed) => {
|
|
2886
|
-
|
|
3024
|
+
dispatch({ type: "SET_REPLAY", replay: { speed: newSpeed } });
|
|
2887
3025
|
if (isReplaying && !isPaused) {
|
|
2888
3026
|
startInterval(newSpeed);
|
|
2889
3027
|
}
|
|
2890
3028
|
},
|
|
2891
|
-
[isReplaying, isPaused, startInterval]
|
|
3029
|
+
[isReplaying, isPaused, startInterval, dispatch]
|
|
2892
3030
|
);
|
|
2893
|
-
useEffect(() => {
|
|
2894
|
-
return () => {
|
|
2895
|
-
clearInterval_();
|
|
2896
|
-
};
|
|
2897
|
-
}, [clearInterval_]);
|
|
2898
3031
|
return {
|
|
2899
3032
|
isReplaying,
|
|
2900
3033
|
isPaused,
|
|
@@ -3051,31 +3184,25 @@ function useCompare() {
|
|
|
3051
3184
|
return { symbols, addSymbol, removeSymbol, toggleSymbol, clearAll };
|
|
3052
3185
|
}
|
|
3053
3186
|
var MEASURE_OVERLAY_ID = "__measure_line__";
|
|
3187
|
+
function computeResult(from, to) {
|
|
3188
|
+
const priceDiff = to.price - from.price;
|
|
3189
|
+
const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
|
|
3190
|
+
const bars = Math.abs(to.barIndex - from.barIndex);
|
|
3191
|
+
const timeDiff = Math.abs(to.timestamp - from.timestamp);
|
|
3192
|
+
return { from, to, priceDiff, pricePercent, bars, timeDiff };
|
|
3193
|
+
}
|
|
3054
3194
|
function useMeasure() {
|
|
3055
|
-
const { state } = useKlinechartsUI();
|
|
3056
|
-
const
|
|
3057
|
-
const [fromPoint, setFromPoint] = useState(null);
|
|
3058
|
-
const [result, setResult] = useState(null);
|
|
3059
|
-
const clickCountRef = useRef(0);
|
|
3060
|
-
const computeResult = useCallback(
|
|
3061
|
-
(from, to) => {
|
|
3062
|
-
const priceDiff = to.price - from.price;
|
|
3063
|
-
const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
|
|
3064
|
-
const bars = Math.abs(to.barIndex - from.barIndex);
|
|
3065
|
-
const timeDiff = Math.abs(to.timestamp - from.timestamp);
|
|
3066
|
-
return { from, to, priceDiff, pricePercent, bars, timeDiff };
|
|
3067
|
-
},
|
|
3068
|
-
[]
|
|
3069
|
-
);
|
|
3195
|
+
const { state, dispatch } = useKlinechartsUI();
|
|
3196
|
+
const { isActive, fromPoint, result } = state.measure;
|
|
3070
3197
|
const cleanup = useCallback(() => {
|
|
3071
3198
|
state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
|
|
3072
3199
|
}, [state.chart]);
|
|
3073
3200
|
const startMeasure = useCallback(() => {
|
|
3074
3201
|
cleanup();
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3202
|
+
dispatch({
|
|
3203
|
+
type: "SET_MEASURE",
|
|
3204
|
+
measure: { isActive: true, fromPoint: null, result: null }
|
|
3205
|
+
});
|
|
3079
3206
|
if (!state.chart) return;
|
|
3080
3207
|
state.chart.createOverlay({
|
|
3081
3208
|
name: "segment",
|
|
@@ -3111,23 +3238,25 @@ function useMeasure() {
|
|
|
3111
3238
|
};
|
|
3112
3239
|
const from = makePoint(points[0]);
|
|
3113
3240
|
const to = makePoint(points[1]);
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3241
|
+
dispatch({
|
|
3242
|
+
type: "SET_MEASURE",
|
|
3243
|
+
measure: {
|
|
3244
|
+
isActive: false,
|
|
3245
|
+
fromPoint: from,
|
|
3246
|
+
result: computeResult(from, to)
|
|
3247
|
+
}
|
|
3248
|
+
});
|
|
3117
3249
|
}
|
|
3118
3250
|
});
|
|
3119
|
-
}, [state.chart, cleanup,
|
|
3251
|
+
}, [state.chart, cleanup, dispatch]);
|
|
3120
3252
|
const cancelMeasure = useCallback(() => {
|
|
3121
3253
|
cleanup();
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
clickCountRef.current = 0;
|
|
3125
|
-
}, [cleanup]);
|
|
3254
|
+
dispatch({ type: "SET_MEASURE", measure: { isActive: false, fromPoint: null } });
|
|
3255
|
+
}, [cleanup, dispatch]);
|
|
3126
3256
|
const clearResult = useCallback(() => {
|
|
3127
3257
|
cleanup();
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
}, [cleanup]);
|
|
3258
|
+
dispatch({ type: "SET_MEASURE", measure: { result: null, fromPoint: null } });
|
|
3259
|
+
}, [cleanup, dispatch]);
|
|
3131
3260
|
useEffect(() => {
|
|
3132
3261
|
return () => {
|
|
3133
3262
|
state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
|
|
@@ -3291,6 +3420,6 @@ function createDataLoader(datafeed, dispatch) {
|
|
|
3291
3420
|
};
|
|
3292
3421
|
}
|
|
3293
3422
|
|
|
3294
|
-
export { CANDLE_TYPES, COMPARE_RULES, DEFAULT_PERIODS, DRAWING_CATEGORIES, INDICATOR_PARAMS, KlinechartsUIDispatchContext, KlinechartsUIProvider, KlinechartsUIStateContext, MAIN_INDICATORS, PRICE_AXIS_TYPES, SUB_INDICATORS, TIMEZONES, TOOLTIP_SHOW_RULES, YAXIS_POSITIONS, createDataLoader, useAlerts, useAnnotations, useCompare, useCrosshair, useDataExport, useDrawingTools, useFullscreen, useIndicators, useKlinechartsUI, useKlinechartsUILoading, useKlinechartsUISettings, useKlinechartsUITheme, useLayoutManager, useMeasure, useOrderLines, usePeriods, useReplay, useScreenshot, useScriptEditor, useSymbolSearch, useTimezone, useUndoRedo, useWatchlist };
|
|
3423
|
+
export { CANDLE_TYPES, COMPARE_RULES, DEFAULT_PERIODS, DRAWING_CATEGORIES, INDICATOR_PARAMS, KlinechartsUIDispatchContext, KlinechartsUIProvider, KlinechartsUIStateContext, MAIN_INDICATORS, PRICE_AXIS_TYPES, SUB_INDICATORS, TIMEZONES, TOOLTIP_SHOW_RULES, YAXIS_POSITIONS, createDataLoader, useAlerts, useAnnotations, useChartAxes, useCompare, useCrosshair, useDataExport, useDrawingTools, useFullscreen, useHotkeys, useIndicators, useKlinechartsUI, useKlinechartsUILoading, useKlinechartsUISettings, useKlinechartsUITheme, useLayoutManager, useMeasure, useOrderLines, usePeriods, useReplay, useScreenshot, useScriptEditor, useSymbolSearch, useTimezone, useUndoRedo, useWatchlist };
|
|
3295
3424
|
//# sourceMappingURL=index.js.map
|
|
3296
3425
|
//# sourceMappingURL=index.js.map
|