react-klinecharts-ui 0.3.0 → 0.5.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/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkPIFLJKYF_cjs = require('./chunk-PIFLJKYF.cjs');
3
+ var chunkUJNJH3BS_cjs = require('./chunk-UJNJH3BS.cjs');
4
4
  var react = require('react');
5
5
  var reactKlinecharts = require('react-klinecharts');
6
6
  var jsxRuntime = require('react/jsx-runtime');
@@ -58,6 +58,16 @@ function reducer(state, action) {
58
58
  return { ...state, mainIndicators: action.indicators };
59
59
  case "SET_SUB_INDICATORS":
60
60
  return { ...state, subIndicators: action.indicators };
61
+ case "SET_INDICATOR_AXES":
62
+ return { ...state, indicatorAxes: action.axes };
63
+ case "SET_INDICATOR_VISIBILITY":
64
+ return { ...state, indicatorVisibility: action.visibility };
65
+ case "SET_ALERTS":
66
+ return { ...state, alerts: action.alerts };
67
+ case "SET_MEASURE":
68
+ return { ...state, measure: { ...state.measure, ...action.measure } };
69
+ case "SET_REPLAY":
70
+ return { ...state, replay: { ...state.replay, ...action.replay } };
61
71
  case "SET_STYLES":
62
72
  return { ...state, styles: action.styles };
63
73
  case "SET_LOCALE":
@@ -120,12 +130,28 @@ function KlinechartsUIProvider({
120
130
  (acc, name) => ({ ...acc, [name]: "" }),
121
131
  {}
122
132
  ),
133
+ indicatorAxes: {},
134
+ indicatorVisibility: {},
135
+ alerts: [],
136
+ measure: { isActive: false, fromPoint: null, result: null },
137
+ replay: {
138
+ isReplaying: false,
139
+ isPaused: false,
140
+ speed: 1,
141
+ barIndex: 0,
142
+ totalBars: 0
143
+ },
123
144
  styles: opts.styles,
124
145
  screenshotUrl: null
125
146
  })
126
147
  );
127
148
  const fullscreenContainerRef = react.useRef(null);
128
149
  const undoRedoListenerRef = react.useRef(null);
150
+ const alertTriggeredListenerRef = react.useRef(null);
151
+ const replayIntervalRef = react.useRef(null);
152
+ const replaySavedDataRef = react.useRef([]);
153
+ const replayIndexRef = react.useRef(0);
154
+ const alertPrevCloseRef = react.useRef(null);
129
155
  const stateRef = react.useRef(state);
130
156
  stateRef.current = state;
131
157
  const callbacksRef = react.useRef({
@@ -177,7 +203,7 @@ function KlinechartsUIProvider({
177
203
  const extraOverlaysRef = react.useRef(extraOverlays);
178
204
  react.useEffect(() => {
179
205
  if (shouldRegister) {
180
- chunkPIFLJKYF_cjs.registerExtensions();
206
+ chunkUJNJH3BS_cjs.registerExtensions();
181
207
  }
182
208
  extraOverlaysRef.current?.forEach((overlay) => reactKlinecharts.registerOverlay(overlay));
183
209
  }, [shouldRegister]);
@@ -186,13 +212,58 @@ function KlinechartsUIProvider({
186
212
  state.chart.setStyles(state.styles);
187
213
  }
188
214
  }, [state.chart, state.styles]);
215
+ const hasAlerts = state.alerts.length > 0;
216
+ react.useEffect(() => {
217
+ const chart = state.chart;
218
+ if (!chart || !hasAlerts) return;
219
+ alertPrevCloseRef.current = null;
220
+ const interval = setInterval(() => {
221
+ const dataList = chart.getDataList();
222
+ if (!dataList || dataList.length === 0) return;
223
+ const currentClose = dataList[dataList.length - 1].close;
224
+ const prevClose = alertPrevCloseRef.current;
225
+ alertPrevCloseRef.current = currentClose;
226
+ if (prevClose === null) return;
227
+ const alerts = stateRef.current.alerts;
228
+ let changed = false;
229
+ const next = alerts.map((alert) => {
230
+ if (alert.triggered) return alert;
231
+ const crossedUp = prevClose < alert.price && currentClose >= alert.price;
232
+ const crossedDown = prevClose > alert.price && currentClose <= alert.price;
233
+ const shouldTrigger = alert.condition === "crossing_up" ? crossedUp : alert.condition === "crossing_down" ? crossedDown : crossedUp || crossedDown;
234
+ if (shouldTrigger) {
235
+ changed = true;
236
+ const triggered = { ...alert, triggered: true };
237
+ alertTriggeredListenerRef.current?.(triggered);
238
+ return triggered;
239
+ }
240
+ return alert;
241
+ });
242
+ if (changed) {
243
+ enhancedDispatch({ type: "SET_ALERTS", alerts: next });
244
+ }
245
+ }, 1e3);
246
+ return () => clearInterval(interval);
247
+ }, [state.chart, hasAlerts, enhancedDispatch]);
248
+ react.useEffect(() => {
249
+ return () => {
250
+ if (replayIntervalRef.current !== null) {
251
+ clearInterval(replayIntervalRef.current);
252
+ replayIntervalRef.current = null;
253
+ }
254
+ };
255
+ }, []);
189
256
  const dispatchValue = react.useMemo(
190
257
  () => ({
191
258
  dispatch: enhancedDispatch,
192
259
  datafeed,
193
260
  onSettingsChange,
194
261
  fullscreenContainerRef,
195
- undoRedoListenerRef
262
+ undoRedoListenerRef,
263
+ alertTriggeredListenerRef,
264
+ replayIntervalRef,
265
+ replaySavedDataRef,
266
+ replayIndexRef
196
267
  }),
197
268
  [enhancedDispatch, datafeed, onSettingsChange]
198
269
  );
@@ -654,75 +725,140 @@ function useIndicators() {
654
725
  const activeNames = state.mainIndicators;
655
726
  const inactive = MAIN_INDICATORS.filter((n) => !activeNames.includes(n));
656
727
  return [
657
- ...activeNames.map((name) => ({ name, isActive: true })),
658
- ...inactive.map((name) => ({ name, isActive: false }))
728
+ ...activeNames.map((name) => ({
729
+ name,
730
+ isActive: true,
731
+ visible: state.indicatorVisibility[`main_${name}`] ?? true
732
+ })),
733
+ ...inactive.map((name) => ({ name, isActive: false, visible: true }))
659
734
  ];
660
- }, [state.mainIndicators]);
735
+ }, [state.mainIndicators, state.indicatorVisibility]);
661
736
  const subIndicators = react.useMemo(() => {
662
737
  const activeNames = Object.keys(state.subIndicators);
663
738
  const inactive = SUB_INDICATORS.filter((n) => !activeNames.includes(n));
664
739
  return [
665
- ...activeNames.map((name) => ({ name, isActive: true })),
666
- ...inactive.map((name) => ({ name, isActive: false }))
740
+ ...activeNames.map((name) => ({
741
+ name,
742
+ isActive: true,
743
+ visible: state.indicatorVisibility[`sub_${name}`] ?? true
744
+ })),
745
+ ...inactive.map((name) => ({ name, isActive: false, visible: true }))
667
746
  ];
668
- }, [state.subIndicators]);
747
+ }, [state.subIndicators, state.indicatorVisibility]);
669
748
  const addMainIndicator = react.useCallback(
670
- (name) => {
749
+ (name, options) => {
671
750
  if (state.mainIndicators.includes(name)) return;
751
+ const yAxis = options?.yAxis;
672
752
  state.chart?.createIndicator(
673
- { name, id: `main_${name}`, paneId: "candle_pane" },
674
- true,
675
- { id: "candle_pane" }
753
+ { name, id: `main_${name}` },
754
+ { isStack: true, pane: { id: "candle_pane" }, yAxis }
676
755
  );
677
756
  const newIndicators = [...state.mainIndicators, name];
678
757
  dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
758
+ if (yAxis?.id) {
759
+ dispatch({
760
+ type: "SET_INDICATOR_AXES",
761
+ axes: { ...state.indicatorAxes, [`main_${name}`]: yAxis.id }
762
+ });
763
+ }
679
764
  undoRedoListenerRef.current?.({
680
765
  type: "indicator_toggled",
681
- data: { name, wasActive: false, isMain: true, paneId: "candle_pane" }
766
+ data: {
767
+ name,
768
+ wasActive: false,
769
+ isMain: true,
770
+ paneId: "candle_pane",
771
+ yAxisId: yAxis?.id
772
+ }
682
773
  });
683
774
  },
684
- [state.chart, state.mainIndicators, dispatch, undoRedoListenerRef]
775
+ [state.chart, state.mainIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
685
776
  );
686
777
  const removeMainIndicator = react.useCallback(
687
778
  (name) => {
688
- state.chart?.removeIndicator({ id: `main_${name}` });
779
+ const id = `main_${name}`;
780
+ const yAxisId = state.indicatorAxes[id];
781
+ state.chart?.removeIndicator({ id });
689
782
  const newIndicators = state.mainIndicators.filter((n) => n !== name);
690
783
  dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
784
+ if (yAxisId) {
785
+ const nextAxes = { ...state.indicatorAxes };
786
+ delete nextAxes[id];
787
+ dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
788
+ }
789
+ if (id in state.indicatorVisibility) {
790
+ const nextVisibility = { ...state.indicatorVisibility };
791
+ delete nextVisibility[id];
792
+ dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
793
+ }
691
794
  undoRedoListenerRef.current?.({
692
795
  type: "indicator_toggled",
693
- data: { name, wasActive: true, isMain: true, paneId: "candle_pane" }
796
+ data: { name, wasActive: true, isMain: true, paneId: "candle_pane", yAxisId }
694
797
  });
695
798
  },
696
- [state.chart, state.mainIndicators, dispatch, undoRedoListenerRef]
799
+ [
800
+ state.chart,
801
+ state.mainIndicators,
802
+ state.indicatorAxes,
803
+ state.indicatorVisibility,
804
+ dispatch,
805
+ undoRedoListenerRef
806
+ ]
697
807
  );
698
808
  const addSubIndicator = react.useCallback(
699
- (name) => {
809
+ (name, options) => {
700
810
  if (name in state.subIndicators) return;
701
811
  const id = `sub_${name}`;
702
- state.chart?.createIndicator({ name, id });
812
+ const yAxis = options?.yAxis;
813
+ state.chart?.createIndicator({ name, id }, { yAxis });
703
814
  const paneId = state.chart?.getIndicators({ id })?.[0]?.paneId ?? "";
704
815
  const newIndicators = { ...state.subIndicators, [name]: paneId };
705
816
  dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
817
+ if (yAxis?.id) {
818
+ dispatch({
819
+ type: "SET_INDICATOR_AXES",
820
+ axes: { ...state.indicatorAxes, [id]: yAxis.id }
821
+ });
822
+ }
706
823
  undoRedoListenerRef.current?.({
707
824
  type: "indicator_toggled",
708
- data: { name, wasActive: false, isMain: false, paneId }
825
+ data: { name, wasActive: false, isMain: false, paneId, yAxisId: yAxis?.id }
709
826
  });
710
827
  },
711
- [state.chart, state.subIndicators, dispatch, undoRedoListenerRef]
828
+ [state.chart, state.subIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
712
829
  );
713
830
  const removeSubIndicator = react.useCallback(
714
831
  (name) => {
832
+ const id = `sub_${name}`;
715
833
  const paneId = state.subIndicators[name] ?? "";
716
- state.chart?.removeIndicator({ id: `sub_${name}` });
834
+ const yAxisId = state.indicatorAxes[id];
835
+ state.chart?.removeIndicator({ id });
717
836
  const newIndicators = { ...state.subIndicators };
718
837
  delete newIndicators[name];
719
838
  dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
839
+ if (yAxisId) {
840
+ const nextAxes = { ...state.indicatorAxes };
841
+ delete nextAxes[id];
842
+ dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
843
+ }
844
+ if (id in state.indicatorVisibility) {
845
+ const nextVisibility = { ...state.indicatorVisibility };
846
+ delete nextVisibility[id];
847
+ dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
848
+ }
720
849
  undoRedoListenerRef.current?.({
721
850
  type: "indicator_toggled",
722
- data: { name, wasActive: true, isMain: false, paneId }
851
+ data: { name, wasActive: true, isMain: false, paneId, yAxisId }
723
852
  });
724
853
  },
725
- [state.chart, state.subIndicators, dispatch, undoRedoListenerRef]
854
+ [
855
+ state.chart,
856
+ state.subIndicators,
857
+ state.indicatorAxes,
858
+ state.indicatorVisibility,
859
+ dispatch,
860
+ undoRedoListenerRef
861
+ ]
726
862
  );
727
863
  const toggleMainIndicator = react.useCallback(
728
864
  (name) => {
@@ -748,12 +884,22 @@ function useIndicators() {
748
884
  (name, isMain, visible) => {
749
885
  const id = isMain ? `main_${name}` : `sub_${name}`;
750
886
  state.chart?.overrideIndicator({ name, id, visible });
887
+ const nextVisibility = { ...state.indicatorVisibility };
888
+ if (visible) delete nextVisibility[id];
889
+ else nextVisibility[id] = false;
890
+ dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
751
891
  },
752
- [state.chart]
892
+ [state.chart, state.indicatorVisibility, dispatch]
893
+ );
894
+ const isIndicatorVisible = react.useCallback(
895
+ (name, isMain) => state.indicatorVisibility[isMain ? `main_${name}` : `sub_${name}`] ?? true,
896
+ [state.indicatorVisibility]
753
897
  );
754
898
  const updateIndicatorParams = react.useCallback(
755
- (name, paneId, params) => {
756
- state.chart?.overrideIndicator({ name, paneId, calcParams: params });
899
+ // `paneId` is kept in the signature for API stability; klinecharts v10
900
+ // `overrideIndicator` no longer accepts it and targets by name/id instead.
901
+ (name, _paneId, params) => {
902
+ state.chart?.overrideIndicator({ name, calcParams: params });
757
903
  },
758
904
  [state.chart]
759
905
  );
@@ -774,11 +920,9 @@ function useIndicators() {
774
920
  {
775
921
  name,
776
922
  id: `main_${name}`,
777
- paneId: "candle_pane",
778
923
  ...prevCalcParams ? { calcParams: prevCalcParams } : {}
779
924
  },
780
- true,
781
- { id: "candle_pane" }
925
+ { isStack: true, pane: { id: "candle_pane" } }
782
926
  );
783
927
  const newSub = { ...state.subIndicators };
784
928
  delete newSub[name];
@@ -832,9 +976,14 @@ function useIndicators() {
832
976
  id: paneId,
833
977
  height: COLLAPSED_HEIGHT
834
978
  });
835
- state.chart.overrideIndicator({ name, id: `sub_${name}`, visible: false });
979
+ const id = `sub_${name}`;
980
+ state.chart.overrideIndicator({ name, id, visible: false });
981
+ dispatch({
982
+ type: "SET_INDICATOR_VISIBILITY",
983
+ visibility: { ...state.indicatorVisibility, [id]: false }
984
+ });
836
985
  },
837
- [state.chart, state.subIndicators]
986
+ [state.chart, state.subIndicators, state.indicatorVisibility, dispatch]
838
987
  );
839
988
  const expandSubIndicator = react.useCallback(
840
989
  (name) => {
@@ -846,9 +995,13 @@ function useIndicators() {
846
995
  id: paneId,
847
996
  height: savedHeight
848
997
  });
849
- state.chart.overrideIndicator({ name, id: `sub_${name}`, visible: true });
998
+ const id = `sub_${name}`;
999
+ state.chart.overrideIndicator({ name, id, visible: true });
1000
+ const nextVisibility = { ...state.indicatorVisibility };
1001
+ delete nextVisibility[id];
1002
+ dispatch({ type: "SET_INDICATOR_VISIBILITY", visibility: nextVisibility });
850
1003
  },
851
- [state.chart, state.subIndicators]
1004
+ [state.chart, state.subIndicators, state.indicatorVisibility, dispatch]
852
1005
  );
853
1006
  const isSubIndicatorCollapsed = react.useCallback(
854
1007
  (name) => {
@@ -894,7 +1047,7 @@ function useIndicators() {
894
1047
  if (sub.styles) {
895
1048
  state.chart.overrideIndicator({
896
1049
  name: sub.name,
897
- paneId,
1050
+ id,
898
1051
  styles: sub.styles
899
1052
  });
900
1053
  }
@@ -903,6 +1056,44 @@ function useIndicators() {
903
1056
  },
904
1057
  [state.chart, state.subIndicators, dispatch]
905
1058
  );
1059
+ const getIndicatorAxis = react.useCallback(
1060
+ (name, isMain) => state.indicatorAxes[isMain ? `main_${name}` : `sub_${name}`],
1061
+ [state.indicatorAxes]
1062
+ );
1063
+ const bindIndicatorToNewAxis = react.useCallback(
1064
+ (name, isMain, yAxis) => {
1065
+ if (!state.chart) return;
1066
+ const id = isMain ? `main_${name}` : `sub_${name}`;
1067
+ const current = state.chart.getIndicators({ id })?.[0];
1068
+ if (!current) return;
1069
+ const calcParams = current.calcParams;
1070
+ const styles = current.styles;
1071
+ const visible = current.visible ?? true;
1072
+ const paneId = isMain ? "candle_pane" : current.paneId ?? state.subIndicators[name] ?? "";
1073
+ state.chart.removeIndicator({ id });
1074
+ state.chart.createIndicator(
1075
+ {
1076
+ name,
1077
+ id,
1078
+ ...calcParams ? { calcParams } : {},
1079
+ visible
1080
+ },
1081
+ {
1082
+ isStack: isMain,
1083
+ ...paneId ? { pane: { id: paneId } } : {},
1084
+ yAxis
1085
+ }
1086
+ );
1087
+ if (styles) {
1088
+ state.chart.overrideIndicator({ name, id, styles });
1089
+ }
1090
+ const nextAxes = { ...state.indicatorAxes };
1091
+ if (yAxis?.id) nextAxes[id] = yAxis.id;
1092
+ else delete nextAxes[id];
1093
+ dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
1094
+ },
1095
+ [state.chart, state.indicatorAxes, state.subIndicators, dispatch]
1096
+ );
906
1097
  return {
907
1098
  mainIndicators,
908
1099
  subIndicators,
@@ -919,6 +1110,7 @@ function useIndicators() {
919
1110
  moveToMain,
920
1111
  moveToSub,
921
1112
  setIndicatorVisible,
1113
+ isIndicatorVisible,
922
1114
  updateIndicatorParams,
923
1115
  getIndicatorParams,
924
1116
  isMainIndicatorActive,
@@ -926,7 +1118,11 @@ function useIndicators() {
926
1118
  collapseSubIndicator,
927
1119
  expandSubIndicator,
928
1120
  isSubIndicatorCollapsed,
929
- reorderSubIndicator
1121
+ reorderSubIndicator,
1122
+ indicatorAxes: state.indicatorAxes,
1123
+ getIndicatorAxis,
1124
+ indicatorVisibility: state.indicatorVisibility,
1125
+ bindIndicatorToNewAxis
930
1126
  };
931
1127
  }
932
1128
 
@@ -1236,20 +1432,23 @@ function useKlinechartsUISettings() {
1236
1432
  () => TOOLTIP_SHOW_RULES.map((r) => ({ key: r, localeKey: r })),
1237
1433
  []
1238
1434
  );
1435
+ const applyPaneAxis = react.useCallback(
1436
+ (axis) => {
1437
+ state.chart?.overrideYAxis?.({ paneId: "candle_pane", ...axis });
1438
+ },
1439
+ [state.chart]
1440
+ );
1239
1441
  const hasAppliedInitial = react.useRef(false);
1240
1442
  react.useEffect(() => {
1241
1443
  if (!state.chart || hasAppliedInitial.current) return;
1242
1444
  hasAppliedInitial.current = true;
1243
- const needsPaneOptions = settings.reverseCoordinate || settings.priceAxisType !== "normal" || settings.yAxisPosition !== "right" || settings.yAxisInside;
1244
- if (needsPaneOptions) {
1245
- state.chart.setPaneOptions({
1246
- id: "candle_pane",
1247
- axis: {
1248
- ...settings.priceAxisType !== "normal" && { name: settings.priceAxisType },
1249
- ...settings.reverseCoordinate && { reverse: true },
1250
- ...settings.yAxisPosition !== "right" && { position: settings.yAxisPosition },
1251
- ...settings.yAxisInside && { inside: true }
1252
- }
1445
+ const needsAxisOverride = settings.reverseCoordinate || settings.priceAxisType !== "normal" || settings.yAxisPosition !== "right" || settings.yAxisInside;
1446
+ if (needsAxisOverride) {
1447
+ applyPaneAxis({
1448
+ ...settings.priceAxisType !== "normal" && { name: settings.priceAxisType },
1449
+ ...settings.reverseCoordinate && { reverse: true },
1450
+ ...settings.yAxisPosition !== "right" && { position: settings.yAxisPosition },
1451
+ ...settings.yAxisInside && { inside: true }
1253
1452
  });
1254
1453
  }
1255
1454
  if (settings.showIndicatorLastValue) {
@@ -1272,12 +1471,6 @@ function useKlinechartsUISettings() {
1272
1471
  },
1273
1472
  [state.chart]
1274
1473
  );
1275
- const applyPaneAxis = react.useCallback(
1276
- (axis) => {
1277
- state.chart?.setPaneOptions({ id: "candle_pane", axis });
1278
- },
1279
- [state.chart]
1280
- );
1281
1474
  const setCandleType = react.useCallback(
1282
1475
  (type) => {
1283
1476
  applyStyle("candle.type", type);
@@ -1425,11 +1618,8 @@ function useKlinechartsUISettings() {
1425
1618
  const resetToDefaults = react.useCallback(() => {
1426
1619
  setSettings(defaultSettings);
1427
1620
  state.chart?.setStyles(state.theme);
1428
- state.chart?.setPaneOptions({
1429
- id: "candle_pane",
1430
- axis: { name: "normal", reverse: false, position: "right", inside: false }
1431
- });
1432
- }, [state.chart, state.theme]);
1621
+ applyPaneAxis({ name: "normal", reverse: false, position: "right", inside: false });
1622
+ }, [state.chart, state.theme, applyPaneAxis]);
1433
1623
  return {
1434
1624
  ...settings,
1435
1625
  candleTypes,
@@ -1686,21 +1876,28 @@ function useUndoRedo() {
1686
1876
  break;
1687
1877
  }
1688
1878
  case "indicator_toggled": {
1689
- const { name, wasActive, isMain, paneId } = action.data;
1879
+ const { name, wasActive, isMain, paneId, yAxisId } = action.data;
1880
+ const id = isMain ? `main_${name}` : `sub_${name}`;
1690
1881
  if (wasActive) {
1691
1882
  if (isMain) {
1692
1883
  state.chart.createIndicator(
1693
- { name, id: `main_${name}`, paneId: "candle_pane" },
1694
- true,
1695
- { id: "candle_pane" }
1884
+ { name, id },
1885
+ {
1886
+ isStack: true,
1887
+ pane: { id: "candle_pane" },
1888
+ ...yAxisId ? { yAxis: { id: yAxisId } } : {}
1889
+ }
1696
1890
  );
1697
1891
  dispatch({
1698
1892
  type: "SET_MAIN_INDICATORS",
1699
1893
  indicators: [...state.mainIndicators, name]
1700
1894
  });
1701
1895
  } else {
1702
- state.chart.createIndicator({ name, id: `sub_${name}` });
1703
- const newPaneId = state.chart.getIndicators({ id: `sub_${name}` })?.[0]?.paneId ?? "";
1896
+ state.chart.createIndicator(
1897
+ { name, id },
1898
+ yAxisId ? { yAxis: { id: yAxisId } } : void 0
1899
+ );
1900
+ const newPaneId = state.chart.getIndicators({ id })?.[0]?.paneId ?? "";
1704
1901
  dispatch({
1705
1902
  type: "SET_SUB_INDICATORS",
1706
1903
  indicators: {
@@ -1709,9 +1906,15 @@ function useUndoRedo() {
1709
1906
  }
1710
1907
  });
1711
1908
  }
1909
+ if (yAxisId) {
1910
+ dispatch({
1911
+ type: "SET_INDICATOR_AXES",
1912
+ axes: { ...state.indicatorAxes, [id]: yAxisId }
1913
+ });
1914
+ }
1712
1915
  } else {
1916
+ state.chart.removeIndicator({ id });
1713
1917
  if (isMain) {
1714
- state.chart.removeIndicator({ id: `main_${name}` });
1715
1918
  dispatch({
1716
1919
  type: "SET_MAIN_INDICATORS",
1717
1920
  indicators: state.mainIndicators.filter(
@@ -1719,7 +1922,6 @@ function useUndoRedo() {
1719
1922
  )
1720
1923
  });
1721
1924
  } else {
1722
- state.chart.removeIndicator({ id: `sub_${name}` });
1723
1925
  const newSub = { ...state.subIndicators };
1724
1926
  delete newSub[name];
1725
1927
  dispatch({
@@ -1727,6 +1929,11 @@ function useUndoRedo() {
1727
1929
  indicators: newSub
1728
1930
  });
1729
1931
  }
1932
+ if (state.indicatorAxes[id]) {
1933
+ const nextAxes = { ...state.indicatorAxes };
1934
+ delete nextAxes[id];
1935
+ dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
1936
+ }
1730
1937
  }
1731
1938
  setRedoStack((prev) => [
1732
1939
  ...prev,
@@ -1736,7 +1943,8 @@ function useUndoRedo() {
1736
1943
  name,
1737
1944
  wasActive: !wasActive,
1738
1945
  isMain,
1739
- paneId
1946
+ paneId,
1947
+ yAxisId
1740
1948
  }
1741
1949
  }
1742
1950
  ]);
@@ -1746,7 +1954,7 @@ function useUndoRedo() {
1746
1954
  } finally {
1747
1955
  isProcessingRef.current = false;
1748
1956
  }
1749
- }, [undoStack, state.chart, state.mainIndicators, state.subIndicators, dispatch]);
1957
+ }, [undoStack, state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
1750
1958
  const redo = react.useCallback(() => {
1751
1959
  if (redoStack.length === 0 || !state.chart || isProcessingRef.current)
1752
1960
  return;
@@ -1798,21 +2006,28 @@ function useUndoRedo() {
1798
2006
  break;
1799
2007
  }
1800
2008
  case "indicator_toggled": {
1801
- const { name, wasActive, isMain } = action.data;
2009
+ const { name, wasActive, isMain, paneId, yAxisId } = action.data;
2010
+ const id = isMain ? `main_${name}` : `sub_${name}`;
1802
2011
  if (wasActive) {
1803
2012
  if (isMain) {
1804
2013
  state.chart.createIndicator(
1805
- { name, id: `main_${name}`, paneId: "candle_pane" },
1806
- true,
1807
- { id: "candle_pane" }
2014
+ { name, id },
2015
+ {
2016
+ isStack: true,
2017
+ pane: { id: "candle_pane" },
2018
+ ...yAxisId ? { yAxis: { id: yAxisId } } : {}
2019
+ }
1808
2020
  );
1809
2021
  dispatch({
1810
2022
  type: "SET_MAIN_INDICATORS",
1811
2023
  indicators: [...state.mainIndicators, name]
1812
2024
  });
1813
2025
  } else {
1814
- state.chart.createIndicator({ name, id: `sub_${name}` });
1815
- const newPaneId = state.chart.getIndicators({ id: `sub_${name}` })?.[0]?.paneId ?? "";
2026
+ state.chart.createIndicator(
2027
+ { name, id },
2028
+ yAxisId ? { yAxis: { id: yAxisId } } : void 0
2029
+ );
2030
+ const newPaneId = state.chart.getIndicators({ id })?.[0]?.paneId ?? "";
1816
2031
  dispatch({
1817
2032
  type: "SET_SUB_INDICATORS",
1818
2033
  indicators: {
@@ -1821,9 +2036,15 @@ function useUndoRedo() {
1821
2036
  }
1822
2037
  });
1823
2038
  }
2039
+ if (yAxisId) {
2040
+ dispatch({
2041
+ type: "SET_INDICATOR_AXES",
2042
+ axes: { ...state.indicatorAxes, [id]: yAxisId }
2043
+ });
2044
+ }
1824
2045
  } else {
2046
+ state.chart.removeIndicator({ id });
1825
2047
  if (isMain) {
1826
- state.chart.removeIndicator({ id: `main_${name}` });
1827
2048
  dispatch({
1828
2049
  type: "SET_MAIN_INDICATORS",
1829
2050
  indicators: state.mainIndicators.filter(
@@ -1831,7 +2052,6 @@ function useUndoRedo() {
1831
2052
  )
1832
2053
  });
1833
2054
  } else {
1834
- state.chart.removeIndicator({ id: `sub_${name}` });
1835
2055
  const newSub = { ...state.subIndicators };
1836
2056
  delete newSub[name];
1837
2057
  dispatch({
@@ -1839,6 +2059,11 @@ function useUndoRedo() {
1839
2059
  indicators: newSub
1840
2060
  });
1841
2061
  }
2062
+ if (state.indicatorAxes[id]) {
2063
+ const nextAxes = { ...state.indicatorAxes };
2064
+ delete nextAxes[id];
2065
+ dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
2066
+ }
1842
2067
  }
1843
2068
  setUndoStack((prev) => [
1844
2069
  ...prev,
@@ -1847,7 +2072,9 @@ function useUndoRedo() {
1847
2072
  data: {
1848
2073
  name,
1849
2074
  wasActive: !wasActive,
1850
- isMain
2075
+ isMain,
2076
+ paneId,
2077
+ yAxisId
1851
2078
  }
1852
2079
  }
1853
2080
  ]);
@@ -1857,7 +2084,7 @@ function useUndoRedo() {
1857
2084
  } finally {
1858
2085
  isProcessingRef.current = false;
1859
2086
  }
1860
- }, [redoStack, state.chart, state.mainIndicators, state.subIndicators, dispatch]);
2087
+ }, [redoStack, state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
1861
2088
  react.useEffect(() => {
1862
2089
  const handleKeyDown = (e) => {
1863
2090
  const isCtrlOrMeta = e.ctrlKey || e.metaKey;
@@ -1931,12 +2158,14 @@ function useLayoutManager() {
1931
2158
  if (indicatorMap) {
1932
2159
  indicatorMap.forEach((paneIndicators, paneId) => {
1933
2160
  paneIndicators.forEach((indicator) => {
2161
+ const yAxisId = state.indicatorAxes[indicator.id];
1934
2162
  indicators2.push({
1935
2163
  paneId,
1936
2164
  name: indicator.name,
1937
2165
  calcParams: indicator.calcParams,
1938
2166
  visible: indicator.visible,
1939
- ...indicator.styles ? { styles: indicator.styles } : {}
2167
+ ...indicator.styles ? { styles: indicator.styles } : {},
2168
+ ...yAxisId ? { yAxisId } : {}
1940
2169
  });
1941
2170
  });
1942
2171
  });
@@ -1964,7 +2193,7 @@ function useLayoutManager() {
1964
2193
  indicators: indicators2,
1965
2194
  drawings
1966
2195
  };
1967
- }, [state.chart, state.symbol, state.period]);
2196
+ }, [state.chart, state.symbol, state.period, state.indicatorAxes]);
1968
2197
  const saveLayout = react.useCallback(
1969
2198
  (name) => {
1970
2199
  const chartState = serializeState();
@@ -2012,25 +2241,39 @@ function useLayoutManager() {
2012
2241
  }
2013
2242
  const newMainIndicators = [];
2014
2243
  const newSubIndicators = {};
2244
+ const restoredAxes = {};
2245
+ const restoredVisibility = {};
2015
2246
  if (chartState.indicators) {
2016
2247
  for (const ind of chartState.indicators) {
2248
+ const isMain = ind.paneId === "candle_pane";
2249
+ const id2 = isMain ? `main_${ind.name}` : `sub_${ind.name}`;
2017
2250
  chart.createIndicator(
2018
2251
  {
2019
2252
  name: ind.name,
2253
+ id: id2,
2020
2254
  calcParams: ind.calcParams,
2021
2255
  visible: ind.visible
2022
2256
  },
2023
- ind.paneId !== "candle_pane",
2024
- { id: ind.paneId }
2257
+ {
2258
+ isStack: ind.paneId !== "candle_pane",
2259
+ pane: { id: ind.paneId },
2260
+ ...ind.yAxisId ? { yAxis: { id: ind.yAxisId } } : {}
2261
+ }
2025
2262
  );
2026
2263
  if (ind.styles) {
2027
2264
  chart.overrideIndicator({
2028
2265
  name: ind.name,
2029
- paneId: ind.paneId,
2266
+ id: id2,
2030
2267
  styles: ind.styles
2031
2268
  });
2032
2269
  }
2033
- if (ind.paneId === "candle_pane") {
2270
+ if (ind.yAxisId) {
2271
+ restoredAxes[id2] = ind.yAxisId;
2272
+ }
2273
+ if (ind.visible === false) {
2274
+ restoredVisibility[id2] = false;
2275
+ }
2276
+ if (isMain) {
2034
2277
  newMainIndicators.push(ind.name);
2035
2278
  } else {
2036
2279
  newSubIndicators[ind.name] = ind.paneId;
@@ -2045,6 +2288,14 @@ function useLayoutManager() {
2045
2288
  type: "SET_SUB_INDICATORS",
2046
2289
  indicators: newSubIndicators
2047
2290
  });
2291
+ dispatch({
2292
+ type: "SET_INDICATOR_AXES",
2293
+ axes: restoredAxes
2294
+ });
2295
+ dispatch({
2296
+ type: "SET_INDICATOR_VISIBILITY",
2297
+ visibility: restoredVisibility
2298
+ });
2048
2299
  if (chartState.drawings) {
2049
2300
  for (const drawing of chartState.drawings) {
2050
2301
  chart.createOverlay({
@@ -2228,7 +2479,7 @@ ${code}`
2228
2479
  ];
2229
2480
  const fn = new Function(...allArgs);
2230
2481
  const shadowValues = SHADOW_KEYS.map(() => void 0);
2231
- const result = fn(...shadowValues, chunkPIFLJKYF_cjs.TA_default, dataList, parsedParams);
2482
+ const result = fn(...shadowValues, chunkUJNJH3BS_cjs.TA_default, dataList, parsedParams);
2232
2483
  if (!Array.isArray(result)) {
2233
2484
  throw new Error("Script must return an Array.");
2234
2485
  }
@@ -2270,8 +2521,7 @@ ${code}`
2270
2521
  if (placement === "main") {
2271
2522
  paneId = chart.createIndicator(
2272
2523
  { name: indicatorName },
2273
- true,
2274
- { id: "candle_pane" }
2524
+ { isStack: true, pane: { id: "candle_pane" } }
2275
2525
  );
2276
2526
  } else {
2277
2527
  paneId = chart.createIndicator({ name: indicatorName });
@@ -2412,10 +2662,9 @@ function useCrosshair() {
2412
2662
  }
2413
2663
  var alertCounter = 0;
2414
2664
  function useAlerts() {
2415
- const { state } = useKlinechartsUI();
2416
- const [alerts, setAlerts] = react.useState([]);
2417
- const callbackRef = react.useRef(null);
2418
- const prevCloseRef = react.useRef(null);
2665
+ const { state, dispatch } = useKlinechartsUI();
2666
+ const { alertTriggeredListenerRef } = useKlinechartsUIDispatch();
2667
+ const alerts = state.alerts;
2419
2668
  const addAlert = react.useCallback(
2420
2669
  (price, condition, message) => {
2421
2670
  const id = `alert_${++alertCounter}`;
@@ -2426,7 +2675,7 @@ function useAlerts() {
2426
2675
  message,
2427
2676
  triggered: false
2428
2677
  };
2429
- setAlerts((prev) => [...prev, alert]);
2678
+ dispatch({ type: "SET_ALERTS", alerts: [...state.alerts, alert] });
2430
2679
  if (state.chart) {
2431
2680
  state.chart.createOverlay({
2432
2681
  name: "horizontalStraightLine",
@@ -2445,67 +2694,30 @@ function useAlerts() {
2445
2694
  }
2446
2695
  return id;
2447
2696
  },
2448
- [state.chart]
2697
+ [state.chart, state.alerts, dispatch]
2449
2698
  );
2450
2699
  const removeAlert = react.useCallback(
2451
2700
  (id) => {
2452
- setAlerts((prev) => prev.filter((a) => a.id !== id));
2701
+ dispatch({
2702
+ type: "SET_ALERTS",
2703
+ alerts: state.alerts.filter((a) => a.id !== id)
2704
+ });
2453
2705
  state.chart?.removeOverlay({ id });
2454
2706
  },
2455
- [state.chart]
2707
+ [state.chart, state.alerts, dispatch]
2456
2708
  );
2457
2709
  const clearAlerts = react.useCallback(() => {
2458
- setAlerts((prev) => {
2459
- for (const alert of prev) {
2460
- state.chart?.removeOverlay({ id: alert.id });
2461
- }
2462
- return [];
2463
- });
2464
- }, [state.chart]);
2710
+ for (const alert of state.alerts) {
2711
+ state.chart?.removeOverlay({ id: alert.id });
2712
+ }
2713
+ dispatch({ type: "SET_ALERTS", alerts: [] });
2714
+ }, [state.chart, state.alerts, dispatch]);
2465
2715
  const onAlertTriggered = react.useCallback(
2466
2716
  (callback) => {
2467
- callbackRef.current = callback;
2717
+ alertTriggeredListenerRef.current = callback;
2468
2718
  },
2469
- []
2719
+ [alertTriggeredListenerRef]
2470
2720
  );
2471
- react.useEffect(() => {
2472
- if (!state.chart) return;
2473
- const interval = setInterval(() => {
2474
- const dataList = state.chart?.getDataList();
2475
- if (!dataList || dataList.length === 0) return;
2476
- const lastBar = dataList[dataList.length - 1];
2477
- const currentClose = lastBar.close;
2478
- const prevClose = prevCloseRef.current;
2479
- if (prevClose === null) {
2480
- prevCloseRef.current = currentClose;
2481
- return;
2482
- }
2483
- prevCloseRef.current = currentClose;
2484
- setAlerts((prev) => {
2485
- let changed = false;
2486
- const next = prev.map((alert) => {
2487
- if (alert.triggered) return alert;
2488
- let shouldTrigger = false;
2489
- if (alert.condition === "crossing_up") {
2490
- shouldTrigger = prevClose < alert.price && currentClose >= alert.price;
2491
- } else if (alert.condition === "crossing_down") {
2492
- shouldTrigger = prevClose > alert.price && currentClose <= alert.price;
2493
- } else if (alert.condition === "crossing") {
2494
- shouldTrigger = prevClose < alert.price && currentClose >= alert.price || prevClose > alert.price && currentClose <= alert.price;
2495
- }
2496
- if (shouldTrigger) {
2497
- changed = true;
2498
- const triggered = { ...alert, triggered: true };
2499
- callbackRef.current?.(triggered);
2500
- return triggered;
2501
- }
2502
- return alert;
2503
- });
2504
- return changed ? next : prev;
2505
- });
2506
- }, 1e3);
2507
- return () => clearInterval(interval);
2508
- }, [state.chart]);
2509
2721
  return {
2510
2722
  alerts,
2511
2723
  addAlert,
@@ -2651,128 +2863,124 @@ function useWatchlist() {
2651
2863
  };
2652
2864
  }
2653
2865
  function useReplay() {
2654
- const { state } = useKlinechartsUI();
2655
- const [isReplaying, setIsReplaying] = react.useState(false);
2656
- const [isPaused, setIsPaused] = react.useState(false);
2657
- const [speed, setSpeedState] = react.useState(1);
2658
- const [barIndex, setBarIndex] = react.useState(0);
2659
- const [totalBars, setTotalBars] = react.useState(0);
2660
- const intervalRef = react.useRef(null);
2661
- const savedDataRef = react.useRef([]);
2662
- const currentIndexRef = react.useRef(0);
2866
+ const { state, dispatch } = useKlinechartsUI();
2867
+ const { replayIntervalRef, replaySavedDataRef, replayIndexRef } = useKlinechartsUIDispatch();
2868
+ const { isReplaying, isPaused, speed, barIndex, totalBars } = state.replay;
2663
2869
  const clearInterval_ = react.useCallback(() => {
2664
- if (intervalRef.current !== null) {
2665
- clearInterval(intervalRef.current);
2666
- intervalRef.current = null;
2870
+ if (replayIntervalRef.current !== null) {
2871
+ clearInterval(replayIntervalRef.current);
2872
+ replayIntervalRef.current = null;
2667
2873
  }
2668
- }, []);
2874
+ }, [replayIntervalRef]);
2669
2875
  const addNextBar = react.useCallback(() => {
2670
2876
  if (!state.chart) return;
2671
- const data = savedDataRef.current;
2672
- const idx = currentIndexRef.current;
2877
+ const data = replaySavedDataRef.current;
2878
+ const idx = replayIndexRef.current;
2673
2879
  if (idx >= data.length) {
2674
2880
  clearInterval_();
2675
- setIsPaused(true);
2881
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
2676
2882
  return;
2677
2883
  }
2678
2884
  const bar = data[idx];
2679
2885
  state.chart.updateData?.(bar);
2680
- currentIndexRef.current = idx + 1;
2681
- setBarIndex(idx + 1);
2682
- }, [state.chart, clearInterval_]);
2886
+ replayIndexRef.current = idx + 1;
2887
+ dispatch({ type: "SET_REPLAY", replay: { barIndex: idx + 1 } });
2888
+ }, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
2683
2889
  const startInterval = react.useCallback(
2684
2890
  (currentSpeed) => {
2685
2891
  clearInterval_();
2686
- intervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
2892
+ replayIntervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
2687
2893
  },
2688
- [addNextBar, clearInterval_]
2894
+ [addNextBar, clearInterval_, replayIntervalRef]
2689
2895
  );
2690
2896
  const startReplay = react.useCallback(() => {
2691
2897
  if (!state.chart) return;
2692
2898
  const dataList = state.chart.getDataList();
2693
2899
  if (!dataList || dataList.length === 0) return;
2694
- savedDataRef.current = [...dataList];
2695
- currentIndexRef.current = 0;
2696
- setTotalBars(dataList.length);
2697
- setBarIndex(0);
2698
- setIsReplaying(true);
2699
- setIsPaused(false);
2900
+ replaySavedDataRef.current = [...dataList];
2901
+ replayIndexRef.current = 0;
2902
+ dispatch({
2903
+ type: "SET_REPLAY",
2904
+ replay: {
2905
+ totalBars: dataList.length,
2906
+ barIndex: 0,
2907
+ isReplaying: true,
2908
+ isPaused: false
2909
+ }
2910
+ });
2700
2911
  state.chart.clearData?.();
2701
2912
  startInterval(speed);
2702
- }, [state.chart, speed, startInterval]);
2913
+ }, [state.chart, speed, startInterval, replaySavedDataRef, replayIndexRef, dispatch]);
2703
2914
  const stopReplay = react.useCallback(() => {
2704
2915
  if (!state.chart) return;
2705
2916
  clearInterval_();
2706
- const data = savedDataRef.current;
2917
+ const data = replaySavedDataRef.current;
2707
2918
  if (data.length > 0) {
2708
2919
  state.chart.clearData?.();
2709
2920
  for (const bar of data) {
2710
2921
  state.chart.updateData?.(bar);
2711
2922
  }
2712
2923
  }
2713
- savedDataRef.current = [];
2714
- currentIndexRef.current = 0;
2715
- setIsReplaying(false);
2716
- setIsPaused(false);
2717
- setBarIndex(0);
2718
- setTotalBars(0);
2719
- }, [state.chart, clearInterval_]);
2924
+ replaySavedDataRef.current = [];
2925
+ replayIndexRef.current = 0;
2926
+ dispatch({
2927
+ type: "SET_REPLAY",
2928
+ replay: { isReplaying: false, isPaused: false, barIndex: 0, totalBars: 0 }
2929
+ });
2930
+ }, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
2720
2931
  const togglePause = react.useCallback(() => {
2721
2932
  if (!isReplaying) return;
2722
2933
  if (isPaused) {
2723
2934
  startInterval(speed);
2724
- setIsPaused(false);
2935
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: false } });
2725
2936
  } else {
2726
2937
  clearInterval_();
2727
- setIsPaused(true);
2938
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
2728
2939
  }
2729
- }, [isReplaying, isPaused, speed, startInterval, clearInterval_]);
2940
+ }, [isReplaying, isPaused, speed, startInterval, clearInterval_, dispatch]);
2730
2941
  const stepForward = react.useCallback(() => {
2731
2942
  if (!isReplaying || !isPaused) return;
2732
2943
  addNextBar();
2733
2944
  }, [isReplaying, isPaused, addNextBar]);
2734
2945
  const stepBackward = react.useCallback(() => {
2735
2946
  if (!isReplaying || !isPaused || !state.chart) return;
2736
- const idx = currentIndexRef.current;
2947
+ const idx = replayIndexRef.current;
2737
2948
  if (idx <= 1) return;
2738
- const data = savedDataRef.current;
2949
+ const data = replaySavedDataRef.current;
2739
2950
  state.chart.clearData?.();
2740
2951
  for (let i = 0; i < idx - 1; i++) {
2741
2952
  state.chart.updateData?.(data[i]);
2742
2953
  }
2743
- currentIndexRef.current = idx - 1;
2744
- setBarIndex(idx - 1);
2745
- }, [isReplaying, isPaused, state.chart]);
2954
+ replayIndexRef.current = idx - 1;
2955
+ dispatch({ type: "SET_REPLAY", replay: { barIndex: idx - 1 } });
2956
+ }, [isReplaying, isPaused, state.chart, replaySavedDataRef, replayIndexRef, dispatch]);
2746
2957
  const seekTo = react.useCallback(
2747
2958
  (targetIndex) => {
2748
2959
  if (!isReplaying || !state.chart) return;
2749
- const data = savedDataRef.current;
2960
+ const data = replaySavedDataRef.current;
2750
2961
  const clamped = Math.max(0, Math.min(targetIndex, data.length));
2751
2962
  clearInterval_();
2752
- setIsPaused(true);
2753
2963
  state.chart.clearData?.();
2754
2964
  for (let i = 0; i < clamped; i++) {
2755
2965
  state.chart.updateData?.(data[i]);
2756
2966
  }
2757
- currentIndexRef.current = clamped;
2758
- setBarIndex(clamped);
2967
+ replayIndexRef.current = clamped;
2968
+ dispatch({
2969
+ type: "SET_REPLAY",
2970
+ replay: { isPaused: true, barIndex: clamped }
2971
+ });
2759
2972
  },
2760
- [isReplaying, state.chart, clearInterval_]
2973
+ [isReplaying, state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]
2761
2974
  );
2762
2975
  const setSpeed = react.useCallback(
2763
2976
  (newSpeed) => {
2764
- setSpeedState(newSpeed);
2977
+ dispatch({ type: "SET_REPLAY", replay: { speed: newSpeed } });
2765
2978
  if (isReplaying && !isPaused) {
2766
2979
  startInterval(newSpeed);
2767
2980
  }
2768
2981
  },
2769
- [isReplaying, isPaused, startInterval]
2982
+ [isReplaying, isPaused, startInterval, dispatch]
2770
2983
  );
2771
- react.useEffect(() => {
2772
- return () => {
2773
- clearInterval_();
2774
- };
2775
- }, [clearInterval_]);
2776
2984
  return {
2777
2985
  isReplaying,
2778
2986
  isPaused,
@@ -2856,8 +3064,7 @@ function useCompare() {
2856
3064
  });
2857
3065
  const paneId = state.chart.createIndicator(
2858
3066
  { name: indicatorName },
2859
- true,
2860
- { id: "candle_pane" }
3067
+ { isStack: true, pane: { id: "candle_pane" } }
2861
3068
  );
2862
3069
  indicatorsRef.current.set(ticker, {
2863
3070
  name: indicatorName,
@@ -2930,31 +3137,25 @@ function useCompare() {
2930
3137
  return { symbols, addSymbol, removeSymbol, toggleSymbol, clearAll };
2931
3138
  }
2932
3139
  var MEASURE_OVERLAY_ID = "__measure_line__";
3140
+ function computeResult(from, to) {
3141
+ const priceDiff = to.price - from.price;
3142
+ const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
3143
+ const bars = Math.abs(to.barIndex - from.barIndex);
3144
+ const timeDiff = Math.abs(to.timestamp - from.timestamp);
3145
+ return { from, to, priceDiff, pricePercent, bars, timeDiff };
3146
+ }
2933
3147
  function useMeasure() {
2934
- const { state } = useKlinechartsUI();
2935
- const [isActive, setIsActive] = react.useState(false);
2936
- const [fromPoint, setFromPoint] = react.useState(null);
2937
- const [result, setResult] = react.useState(null);
2938
- const clickCountRef = react.useRef(0);
2939
- const computeResult = react.useCallback(
2940
- (from, to) => {
2941
- const priceDiff = to.price - from.price;
2942
- const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
2943
- const bars = Math.abs(to.barIndex - from.barIndex);
2944
- const timeDiff = Math.abs(to.timestamp - from.timestamp);
2945
- return { from, to, priceDiff, pricePercent, bars, timeDiff };
2946
- },
2947
- []
2948
- );
3148
+ const { state, dispatch } = useKlinechartsUI();
3149
+ const { isActive, fromPoint, result } = state.measure;
2949
3150
  const cleanup = react.useCallback(() => {
2950
3151
  state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
2951
3152
  }, [state.chart]);
2952
3153
  const startMeasure = react.useCallback(() => {
2953
3154
  cleanup();
2954
- setIsActive(true);
2955
- setFromPoint(null);
2956
- setResult(null);
2957
- clickCountRef.current = 0;
3155
+ dispatch({
3156
+ type: "SET_MEASURE",
3157
+ measure: { isActive: true, fromPoint: null, result: null }
3158
+ });
2958
3159
  if (!state.chart) return;
2959
3160
  state.chart.createOverlay({
2960
3161
  name: "segment",
@@ -2990,23 +3191,25 @@ function useMeasure() {
2990
3191
  };
2991
3192
  const from = makePoint(points[0]);
2992
3193
  const to = makePoint(points[1]);
2993
- setFromPoint(from);
2994
- setResult(computeResult(from, to));
2995
- setIsActive(false);
3194
+ dispatch({
3195
+ type: "SET_MEASURE",
3196
+ measure: {
3197
+ isActive: false,
3198
+ fromPoint: from,
3199
+ result: computeResult(from, to)
3200
+ }
3201
+ });
2996
3202
  }
2997
3203
  });
2998
- }, [state.chart, cleanup, computeResult]);
3204
+ }, [state.chart, cleanup, dispatch]);
2999
3205
  const cancelMeasure = react.useCallback(() => {
3000
3206
  cleanup();
3001
- setIsActive(false);
3002
- setFromPoint(null);
3003
- clickCountRef.current = 0;
3004
- }, [cleanup]);
3207
+ dispatch({ type: "SET_MEASURE", measure: { isActive: false, fromPoint: null } });
3208
+ }, [cleanup, dispatch]);
3005
3209
  const clearResult = react.useCallback(() => {
3006
3210
  cleanup();
3007
- setResult(null);
3008
- setFromPoint(null);
3009
- }, [cleanup]);
3211
+ dispatch({ type: "SET_MEASURE", measure: { result: null, fromPoint: null } });
3212
+ }, [cleanup, dispatch]);
3010
3213
  react.useEffect(() => {
3011
3214
  return () => {
3012
3215
  state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
@@ -3172,175 +3375,175 @@ function createDataLoader(datafeed, dispatch) {
3172
3375
 
3173
3376
  Object.defineProperty(exports, "TA", {
3174
3377
  enumerable: true,
3175
- get: function () { return chunkPIFLJKYF_cjs.TA_default; }
3378
+ get: function () { return chunkUJNJH3BS_cjs.TA_default; }
3176
3379
  });
3177
3380
  Object.defineProperty(exports, "abcd", {
3178
3381
  enumerable: true,
3179
- get: function () { return chunkPIFLJKYF_cjs.abcd_default; }
3382
+ get: function () { return chunkUJNJH3BS_cjs.abcd_default; }
3180
3383
  });
3181
3384
  Object.defineProperty(exports, "anyWaves", {
3182
3385
  enumerable: true,
3183
- get: function () { return chunkPIFLJKYF_cjs.anyWaves_default; }
3386
+ get: function () { return chunkUJNJH3BS_cjs.anyWaves_default; }
3184
3387
  });
3185
3388
  Object.defineProperty(exports, "arrow", {
3186
3389
  enumerable: true,
3187
- get: function () { return chunkPIFLJKYF_cjs.arrow_default; }
3390
+ get: function () { return chunkUJNJH3BS_cjs.arrow_default; }
3188
3391
  });
3189
3392
  Object.defineProperty(exports, "bollTv", {
3190
3393
  enumerable: true,
3191
- get: function () { return chunkPIFLJKYF_cjs.bollTv_default; }
3394
+ get: function () { return chunkUJNJH3BS_cjs.bollTv_default; }
3192
3395
  });
3193
3396
  Object.defineProperty(exports, "brush", {
3194
3397
  enumerable: true,
3195
- get: function () { return chunkPIFLJKYF_cjs.brush_default; }
3398
+ get: function () { return chunkUJNJH3BS_cjs.brush_default; }
3196
3399
  });
3197
3400
  Object.defineProperty(exports, "cci", {
3198
3401
  enumerable: true,
3199
- get: function () { return chunkPIFLJKYF_cjs.cci_default; }
3402
+ get: function () { return chunkUJNJH3BS_cjs.cci_default; }
3200
3403
  });
3201
3404
  Object.defineProperty(exports, "circle", {
3202
3405
  enumerable: true,
3203
- get: function () { return chunkPIFLJKYF_cjs.circle_default; }
3406
+ get: function () { return chunkUJNJH3BS_cjs.circle_default; }
3204
3407
  });
3205
3408
  Object.defineProperty(exports, "depthOverlay", {
3206
3409
  enumerable: true,
3207
- get: function () { return chunkPIFLJKYF_cjs.depthOverlay_default; }
3410
+ get: function () { return chunkUJNJH3BS_cjs.depthOverlay_default; }
3208
3411
  });
3209
3412
  Object.defineProperty(exports, "eightWaves", {
3210
3413
  enumerable: true,
3211
- get: function () { return chunkPIFLJKYF_cjs.eightWaves_default; }
3414
+ get: function () { return chunkUJNJH3BS_cjs.eightWaves_default; }
3212
3415
  });
3213
3416
  Object.defineProperty(exports, "elliottWave", {
3214
3417
  enumerable: true,
3215
- get: function () { return chunkPIFLJKYF_cjs.elliottWave_default; }
3418
+ get: function () { return chunkUJNJH3BS_cjs.elliottWave_default; }
3216
3419
  });
3217
3420
  Object.defineProperty(exports, "fibRetracement", {
3218
3421
  enumerable: true,
3219
- get: function () { return chunkPIFLJKYF_cjs.fibRetracement_default; }
3422
+ get: function () { return chunkUJNJH3BS_cjs.fibRetracement_default; }
3220
3423
  });
3221
3424
  Object.defineProperty(exports, "fibonacciCircle", {
3222
3425
  enumerable: true,
3223
- get: function () { return chunkPIFLJKYF_cjs.fibonacciCircle_default; }
3426
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciCircle_default; }
3224
3427
  });
3225
3428
  Object.defineProperty(exports, "fibonacciExtension", {
3226
3429
  enumerable: true,
3227
- get: function () { return chunkPIFLJKYF_cjs.fibonacciExtension_default; }
3430
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciExtension_default; }
3228
3431
  });
3229
3432
  Object.defineProperty(exports, "fibonacciSegment", {
3230
3433
  enumerable: true,
3231
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSegment_default; }
3434
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciSegment_default; }
3232
3435
  });
3233
3436
  Object.defineProperty(exports, "fibonacciSpeedResistanceFan", {
3234
3437
  enumerable: true,
3235
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSpeedResistanceFan_default; }
3438
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciSpeedResistanceFan_default; }
3236
3439
  });
3237
3440
  Object.defineProperty(exports, "fibonacciSpiral", {
3238
3441
  enumerable: true,
3239
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSpiral_default; }
3442
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciSpiral_default; }
3240
3443
  });
3241
3444
  Object.defineProperty(exports, "fiveWaves", {
3242
3445
  enumerable: true,
3243
- get: function () { return chunkPIFLJKYF_cjs.fiveWaves_default; }
3446
+ get: function () { return chunkUJNJH3BS_cjs.fiveWaves_default; }
3244
3447
  });
3245
3448
  Object.defineProperty(exports, "gannBox", {
3246
3449
  enumerable: true,
3247
- get: function () { return chunkPIFLJKYF_cjs.gannBox_default; }
3450
+ get: function () { return chunkUJNJH3BS_cjs.gannBox_default; }
3248
3451
  });
3249
3452
  Object.defineProperty(exports, "gannFan", {
3250
3453
  enumerable: true,
3251
- get: function () { return chunkPIFLJKYF_cjs.gannFan_default; }
3454
+ get: function () { return chunkUJNJH3BS_cjs.gannFan_default; }
3252
3455
  });
3253
3456
  Object.defineProperty(exports, "hma", {
3254
3457
  enumerable: true,
3255
- get: function () { return chunkPIFLJKYF_cjs.hma_default; }
3458
+ get: function () { return chunkUJNJH3BS_cjs.hma_default; }
3256
3459
  });
3257
3460
  Object.defineProperty(exports, "ichimoku", {
3258
3461
  enumerable: true,
3259
- get: function () { return chunkPIFLJKYF_cjs.ichimoku_default; }
3462
+ get: function () { return chunkUJNJH3BS_cjs.ichimoku_default; }
3260
3463
  });
3261
3464
  Object.defineProperty(exports, "indicators", {
3262
3465
  enumerable: true,
3263
- get: function () { return chunkPIFLJKYF_cjs.indicators; }
3466
+ get: function () { return chunkUJNJH3BS_cjs.indicators; }
3264
3467
  });
3265
3468
  Object.defineProperty(exports, "longPosition", {
3266
3469
  enumerable: true,
3267
- get: function () { return chunkPIFLJKYF_cjs.longPosition_default; }
3470
+ get: function () { return chunkUJNJH3BS_cjs.longPosition_default; }
3268
3471
  });
3269
3472
  Object.defineProperty(exports, "maRibbon", {
3270
3473
  enumerable: true,
3271
- get: function () { return chunkPIFLJKYF_cjs.maRibbon_default; }
3474
+ get: function () { return chunkUJNJH3BS_cjs.maRibbon_default; }
3272
3475
  });
3273
3476
  Object.defineProperty(exports, "macdTv", {
3274
3477
  enumerable: true,
3275
- get: function () { return chunkPIFLJKYF_cjs.macdTv_default; }
3478
+ get: function () { return chunkUJNJH3BS_cjs.macdTv_default; }
3276
3479
  });
3277
3480
  Object.defineProperty(exports, "measure", {
3278
3481
  enumerable: true,
3279
- get: function () { return chunkPIFLJKYF_cjs.measure_default; }
3482
+ get: function () { return chunkUJNJH3BS_cjs.measure_default; }
3280
3483
  });
3281
3484
  Object.defineProperty(exports, "orderLine", {
3282
3485
  enumerable: true,
3283
- get: function () { return chunkPIFLJKYF_cjs.orderLine_default; }
3486
+ get: function () { return chunkUJNJH3BS_cjs.orderLine_default; }
3284
3487
  });
3285
3488
  Object.defineProperty(exports, "overlays", {
3286
3489
  enumerable: true,
3287
- get: function () { return chunkPIFLJKYF_cjs.overlays; }
3490
+ get: function () { return chunkUJNJH3BS_cjs.overlays; }
3288
3491
  });
3289
3492
  Object.defineProperty(exports, "parallelChannel", {
3290
3493
  enumerable: true,
3291
- get: function () { return chunkPIFLJKYF_cjs.parallelChannel_default; }
3494
+ get: function () { return chunkUJNJH3BS_cjs.parallelChannel_default; }
3292
3495
  });
3293
3496
  Object.defineProperty(exports, "parallelogram", {
3294
3497
  enumerable: true,
3295
- get: function () { return chunkPIFLJKYF_cjs.parallelogram_default; }
3498
+ get: function () { return chunkUJNJH3BS_cjs.parallelogram_default; }
3296
3499
  });
3297
3500
  Object.defineProperty(exports, "pivotPoints", {
3298
3501
  enumerable: true,
3299
- get: function () { return chunkPIFLJKYF_cjs.pivotPoints_default; }
3502
+ get: function () { return chunkUJNJH3BS_cjs.pivotPoints_default; }
3300
3503
  });
3301
3504
  Object.defineProperty(exports, "ray", {
3302
3505
  enumerable: true,
3303
- get: function () { return chunkPIFLJKYF_cjs.ray_default; }
3506
+ get: function () { return chunkUJNJH3BS_cjs.ray_default; }
3304
3507
  });
3305
3508
  Object.defineProperty(exports, "rect", {
3306
3509
  enumerable: true,
3307
- get: function () { return chunkPIFLJKYF_cjs.rect_default; }
3510
+ get: function () { return chunkUJNJH3BS_cjs.rect_default; }
3308
3511
  });
3309
3512
  Object.defineProperty(exports, "registerExtensions", {
3310
3513
  enumerable: true,
3311
- get: function () { return chunkPIFLJKYF_cjs.registerExtensions; }
3514
+ get: function () { return chunkUJNJH3BS_cjs.registerExtensions; }
3312
3515
  });
3313
3516
  Object.defineProperty(exports, "rsiTv", {
3314
3517
  enumerable: true,
3315
- get: function () { return chunkPIFLJKYF_cjs.rsiTv_default; }
3518
+ get: function () { return chunkUJNJH3BS_cjs.rsiTv_default; }
3316
3519
  });
3317
3520
  Object.defineProperty(exports, "shortPosition", {
3318
3521
  enumerable: true,
3319
- get: function () { return chunkPIFLJKYF_cjs.shortPosition_default; }
3522
+ get: function () { return chunkUJNJH3BS_cjs.shortPosition_default; }
3320
3523
  });
3321
3524
  Object.defineProperty(exports, "stochastic", {
3322
3525
  enumerable: true,
3323
- get: function () { return chunkPIFLJKYF_cjs.stochastic_default; }
3526
+ get: function () { return chunkUJNJH3BS_cjs.stochastic_default; }
3324
3527
  });
3325
3528
  Object.defineProperty(exports, "superTrend", {
3326
3529
  enumerable: true,
3327
- get: function () { return chunkPIFLJKYF_cjs.superTrend_default; }
3530
+ get: function () { return chunkUJNJH3BS_cjs.superTrend_default; }
3328
3531
  });
3329
3532
  Object.defineProperty(exports, "threeWaves", {
3330
3533
  enumerable: true,
3331
- get: function () { return chunkPIFLJKYF_cjs.threeWaves_default; }
3534
+ get: function () { return chunkUJNJH3BS_cjs.threeWaves_default; }
3332
3535
  });
3333
3536
  Object.defineProperty(exports, "triangle", {
3334
3537
  enumerable: true,
3335
- get: function () { return chunkPIFLJKYF_cjs.triangle_default; }
3538
+ get: function () { return chunkUJNJH3BS_cjs.triangle_default; }
3336
3539
  });
3337
3540
  Object.defineProperty(exports, "vwap", {
3338
3541
  enumerable: true,
3339
- get: function () { return chunkPIFLJKYF_cjs.vwap_default; }
3542
+ get: function () { return chunkUJNJH3BS_cjs.vwap_default; }
3340
3543
  });
3341
3544
  Object.defineProperty(exports, "xabcd", {
3342
3545
  enumerable: true,
3343
- get: function () { return chunkPIFLJKYF_cjs.xabcd_default; }
3546
+ get: function () { return chunkUJNJH3BS_cjs.xabcd_default; }
3344
3547
  });
3345
3548
  exports.CANDLE_TYPES = CANDLE_TYPES;
3346
3549
  exports.COMPARE_RULES = COMPARE_RULES;