react-klinecharts-ui 0.4.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');
@@ -60,6 +60,14 @@ function reducer(state, action) {
60
60
  return { ...state, subIndicators: action.indicators };
61
61
  case "SET_INDICATOR_AXES":
62
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 } };
63
71
  case "SET_STYLES":
64
72
  return { ...state, styles: action.styles };
65
73
  case "SET_LOCALE":
@@ -123,12 +131,27 @@ function KlinechartsUIProvider({
123
131
  {}
124
132
  ),
125
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
+ },
126
144
  styles: opts.styles,
127
145
  screenshotUrl: null
128
146
  })
129
147
  );
130
148
  const fullscreenContainerRef = react.useRef(null);
131
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);
132
155
  const stateRef = react.useRef(state);
133
156
  stateRef.current = state;
134
157
  const callbacksRef = react.useRef({
@@ -180,7 +203,7 @@ function KlinechartsUIProvider({
180
203
  const extraOverlaysRef = react.useRef(extraOverlays);
181
204
  react.useEffect(() => {
182
205
  if (shouldRegister) {
183
- chunkPIFLJKYF_cjs.registerExtensions();
206
+ chunkUJNJH3BS_cjs.registerExtensions();
184
207
  }
185
208
  extraOverlaysRef.current?.forEach((overlay) => reactKlinecharts.registerOverlay(overlay));
186
209
  }, [shouldRegister]);
@@ -189,13 +212,58 @@ function KlinechartsUIProvider({
189
212
  state.chart.setStyles(state.styles);
190
213
  }
191
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
+ }, []);
192
256
  const dispatchValue = react.useMemo(
193
257
  () => ({
194
258
  dispatch: enhancedDispatch,
195
259
  datafeed,
196
260
  onSettingsChange,
197
261
  fullscreenContainerRef,
198
- undoRedoListenerRef
262
+ undoRedoListenerRef,
263
+ alertTriggeredListenerRef,
264
+ replayIntervalRef,
265
+ replaySavedDataRef,
266
+ replayIndexRef
199
267
  }),
200
268
  [enhancedDispatch, datafeed, onSettingsChange]
201
269
  );
@@ -657,18 +725,26 @@ function useIndicators() {
657
725
  const activeNames = state.mainIndicators;
658
726
  const inactive = MAIN_INDICATORS.filter((n) => !activeNames.includes(n));
659
727
  return [
660
- ...activeNames.map((name) => ({ name, isActive: true })),
661
- ...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 }))
662
734
  ];
663
- }, [state.mainIndicators]);
735
+ }, [state.mainIndicators, state.indicatorVisibility]);
664
736
  const subIndicators = react.useMemo(() => {
665
737
  const activeNames = Object.keys(state.subIndicators);
666
738
  const inactive = SUB_INDICATORS.filter((n) => !activeNames.includes(n));
667
739
  return [
668
- ...activeNames.map((name) => ({ name, isActive: true })),
669
- ...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 }))
670
746
  ];
671
- }, [state.subIndicators]);
747
+ }, [state.subIndicators, state.indicatorVisibility]);
672
748
  const addMainIndicator = react.useCallback(
673
749
  (name, options) => {
674
750
  if (state.mainIndicators.includes(name)) return;
@@ -710,12 +786,24 @@ function useIndicators() {
710
786
  delete nextAxes[id];
711
787
  dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
712
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
+ }
713
794
  undoRedoListenerRef.current?.({
714
795
  type: "indicator_toggled",
715
796
  data: { name, wasActive: true, isMain: true, paneId: "candle_pane", yAxisId }
716
797
  });
717
798
  },
718
- [state.chart, state.mainIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
799
+ [
800
+ state.chart,
801
+ state.mainIndicators,
802
+ state.indicatorAxes,
803
+ state.indicatorVisibility,
804
+ dispatch,
805
+ undoRedoListenerRef
806
+ ]
719
807
  );
720
808
  const addSubIndicator = react.useCallback(
721
809
  (name, options) => {
@@ -753,12 +841,24 @@ function useIndicators() {
753
841
  delete nextAxes[id];
754
842
  dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
755
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
+ }
756
849
  undoRedoListenerRef.current?.({
757
850
  type: "indicator_toggled",
758
851
  data: { name, wasActive: true, isMain: false, paneId, yAxisId }
759
852
  });
760
853
  },
761
- [state.chart, state.subIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
854
+ [
855
+ state.chart,
856
+ state.subIndicators,
857
+ state.indicatorAxes,
858
+ state.indicatorVisibility,
859
+ dispatch,
860
+ undoRedoListenerRef
861
+ ]
762
862
  );
763
863
  const toggleMainIndicator = react.useCallback(
764
864
  (name) => {
@@ -784,8 +884,16 @@ function useIndicators() {
784
884
  (name, isMain, visible) => {
785
885
  const id = isMain ? `main_${name}` : `sub_${name}`;
786
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 });
787
891
  },
788
- [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]
789
897
  );
790
898
  const updateIndicatorParams = react.useCallback(
791
899
  // `paneId` is kept in the signature for API stability; klinecharts v10
@@ -868,9 +976,14 @@ function useIndicators() {
868
976
  id: paneId,
869
977
  height: COLLAPSED_HEIGHT
870
978
  });
871
- 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
+ });
872
985
  },
873
- [state.chart, state.subIndicators]
986
+ [state.chart, state.subIndicators, state.indicatorVisibility, dispatch]
874
987
  );
875
988
  const expandSubIndicator = react.useCallback(
876
989
  (name) => {
@@ -882,9 +995,13 @@ function useIndicators() {
882
995
  id: paneId,
883
996
  height: savedHeight
884
997
  });
885
- 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 });
886
1003
  },
887
- [state.chart, state.subIndicators]
1004
+ [state.chart, state.subIndicators, state.indicatorVisibility, dispatch]
888
1005
  );
889
1006
  const isSubIndicatorCollapsed = react.useCallback(
890
1007
  (name) => {
@@ -993,6 +1110,7 @@ function useIndicators() {
993
1110
  moveToMain,
994
1111
  moveToSub,
995
1112
  setIndicatorVisible,
1113
+ isIndicatorVisible,
996
1114
  updateIndicatorParams,
997
1115
  getIndicatorParams,
998
1116
  isMainIndicatorActive,
@@ -1003,6 +1121,7 @@ function useIndicators() {
1003
1121
  reorderSubIndicator,
1004
1122
  indicatorAxes: state.indicatorAxes,
1005
1123
  getIndicatorAxis,
1124
+ indicatorVisibility: state.indicatorVisibility,
1006
1125
  bindIndicatorToNewAxis
1007
1126
  };
1008
1127
  }
@@ -2123,6 +2242,7 @@ function useLayoutManager() {
2123
2242
  const newMainIndicators = [];
2124
2243
  const newSubIndicators = {};
2125
2244
  const restoredAxes = {};
2245
+ const restoredVisibility = {};
2126
2246
  if (chartState.indicators) {
2127
2247
  for (const ind of chartState.indicators) {
2128
2248
  const isMain = ind.paneId === "candle_pane";
@@ -2150,6 +2270,9 @@ function useLayoutManager() {
2150
2270
  if (ind.yAxisId) {
2151
2271
  restoredAxes[id2] = ind.yAxisId;
2152
2272
  }
2273
+ if (ind.visible === false) {
2274
+ restoredVisibility[id2] = false;
2275
+ }
2153
2276
  if (isMain) {
2154
2277
  newMainIndicators.push(ind.name);
2155
2278
  } else {
@@ -2169,6 +2292,10 @@ function useLayoutManager() {
2169
2292
  type: "SET_INDICATOR_AXES",
2170
2293
  axes: restoredAxes
2171
2294
  });
2295
+ dispatch({
2296
+ type: "SET_INDICATOR_VISIBILITY",
2297
+ visibility: restoredVisibility
2298
+ });
2172
2299
  if (chartState.drawings) {
2173
2300
  for (const drawing of chartState.drawings) {
2174
2301
  chart.createOverlay({
@@ -2352,7 +2479,7 @@ ${code}`
2352
2479
  ];
2353
2480
  const fn = new Function(...allArgs);
2354
2481
  const shadowValues = SHADOW_KEYS.map(() => void 0);
2355
- const result = fn(...shadowValues, chunkPIFLJKYF_cjs.TA_default, dataList, parsedParams);
2482
+ const result = fn(...shadowValues, chunkUJNJH3BS_cjs.TA_default, dataList, parsedParams);
2356
2483
  if (!Array.isArray(result)) {
2357
2484
  throw new Error("Script must return an Array.");
2358
2485
  }
@@ -2535,10 +2662,9 @@ function useCrosshair() {
2535
2662
  }
2536
2663
  var alertCounter = 0;
2537
2664
  function useAlerts() {
2538
- const { state } = useKlinechartsUI();
2539
- const [alerts, setAlerts] = react.useState([]);
2540
- const callbackRef = react.useRef(null);
2541
- const prevCloseRef = react.useRef(null);
2665
+ const { state, dispatch } = useKlinechartsUI();
2666
+ const { alertTriggeredListenerRef } = useKlinechartsUIDispatch();
2667
+ const alerts = state.alerts;
2542
2668
  const addAlert = react.useCallback(
2543
2669
  (price, condition, message) => {
2544
2670
  const id = `alert_${++alertCounter}`;
@@ -2549,7 +2675,7 @@ function useAlerts() {
2549
2675
  message,
2550
2676
  triggered: false
2551
2677
  };
2552
- setAlerts((prev) => [...prev, alert]);
2678
+ dispatch({ type: "SET_ALERTS", alerts: [...state.alerts, alert] });
2553
2679
  if (state.chart) {
2554
2680
  state.chart.createOverlay({
2555
2681
  name: "horizontalStraightLine",
@@ -2568,67 +2694,30 @@ function useAlerts() {
2568
2694
  }
2569
2695
  return id;
2570
2696
  },
2571
- [state.chart]
2697
+ [state.chart, state.alerts, dispatch]
2572
2698
  );
2573
2699
  const removeAlert = react.useCallback(
2574
2700
  (id) => {
2575
- setAlerts((prev) => prev.filter((a) => a.id !== id));
2701
+ dispatch({
2702
+ type: "SET_ALERTS",
2703
+ alerts: state.alerts.filter((a) => a.id !== id)
2704
+ });
2576
2705
  state.chart?.removeOverlay({ id });
2577
2706
  },
2578
- [state.chart]
2707
+ [state.chart, state.alerts, dispatch]
2579
2708
  );
2580
2709
  const clearAlerts = react.useCallback(() => {
2581
- setAlerts((prev) => {
2582
- for (const alert of prev) {
2583
- state.chart?.removeOverlay({ id: alert.id });
2584
- }
2585
- return [];
2586
- });
2587
- }, [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]);
2588
2715
  const onAlertTriggered = react.useCallback(
2589
2716
  (callback) => {
2590
- callbackRef.current = callback;
2717
+ alertTriggeredListenerRef.current = callback;
2591
2718
  },
2592
- []
2719
+ [alertTriggeredListenerRef]
2593
2720
  );
2594
- react.useEffect(() => {
2595
- if (!state.chart) return;
2596
- const interval = setInterval(() => {
2597
- const dataList = state.chart?.getDataList();
2598
- if (!dataList || dataList.length === 0) return;
2599
- const lastBar = dataList[dataList.length - 1];
2600
- const currentClose = lastBar.close;
2601
- const prevClose = prevCloseRef.current;
2602
- if (prevClose === null) {
2603
- prevCloseRef.current = currentClose;
2604
- return;
2605
- }
2606
- prevCloseRef.current = currentClose;
2607
- setAlerts((prev) => {
2608
- let changed = false;
2609
- const next = prev.map((alert) => {
2610
- if (alert.triggered) return alert;
2611
- let shouldTrigger = false;
2612
- if (alert.condition === "crossing_up") {
2613
- shouldTrigger = prevClose < alert.price && currentClose >= alert.price;
2614
- } else if (alert.condition === "crossing_down") {
2615
- shouldTrigger = prevClose > alert.price && currentClose <= alert.price;
2616
- } else if (alert.condition === "crossing") {
2617
- shouldTrigger = prevClose < alert.price && currentClose >= alert.price || prevClose > alert.price && currentClose <= alert.price;
2618
- }
2619
- if (shouldTrigger) {
2620
- changed = true;
2621
- const triggered = { ...alert, triggered: true };
2622
- callbackRef.current?.(triggered);
2623
- return triggered;
2624
- }
2625
- return alert;
2626
- });
2627
- return changed ? next : prev;
2628
- });
2629
- }, 1e3);
2630
- return () => clearInterval(interval);
2631
- }, [state.chart]);
2632
2721
  return {
2633
2722
  alerts,
2634
2723
  addAlert,
@@ -2774,128 +2863,124 @@ function useWatchlist() {
2774
2863
  };
2775
2864
  }
2776
2865
  function useReplay() {
2777
- const { state } = useKlinechartsUI();
2778
- const [isReplaying, setIsReplaying] = react.useState(false);
2779
- const [isPaused, setIsPaused] = react.useState(false);
2780
- const [speed, setSpeedState] = react.useState(1);
2781
- const [barIndex, setBarIndex] = react.useState(0);
2782
- const [totalBars, setTotalBars] = react.useState(0);
2783
- const intervalRef = react.useRef(null);
2784
- const savedDataRef = react.useRef([]);
2785
- 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;
2786
2869
  const clearInterval_ = react.useCallback(() => {
2787
- if (intervalRef.current !== null) {
2788
- clearInterval(intervalRef.current);
2789
- intervalRef.current = null;
2870
+ if (replayIntervalRef.current !== null) {
2871
+ clearInterval(replayIntervalRef.current);
2872
+ replayIntervalRef.current = null;
2790
2873
  }
2791
- }, []);
2874
+ }, [replayIntervalRef]);
2792
2875
  const addNextBar = react.useCallback(() => {
2793
2876
  if (!state.chart) return;
2794
- const data = savedDataRef.current;
2795
- const idx = currentIndexRef.current;
2877
+ const data = replaySavedDataRef.current;
2878
+ const idx = replayIndexRef.current;
2796
2879
  if (idx >= data.length) {
2797
2880
  clearInterval_();
2798
- setIsPaused(true);
2881
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
2799
2882
  return;
2800
2883
  }
2801
2884
  const bar = data[idx];
2802
2885
  state.chart.updateData?.(bar);
2803
- currentIndexRef.current = idx + 1;
2804
- setBarIndex(idx + 1);
2805
- }, [state.chart, clearInterval_]);
2886
+ replayIndexRef.current = idx + 1;
2887
+ dispatch({ type: "SET_REPLAY", replay: { barIndex: idx + 1 } });
2888
+ }, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
2806
2889
  const startInterval = react.useCallback(
2807
2890
  (currentSpeed) => {
2808
2891
  clearInterval_();
2809
- intervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
2892
+ replayIntervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
2810
2893
  },
2811
- [addNextBar, clearInterval_]
2894
+ [addNextBar, clearInterval_, replayIntervalRef]
2812
2895
  );
2813
2896
  const startReplay = react.useCallback(() => {
2814
2897
  if (!state.chart) return;
2815
2898
  const dataList = state.chart.getDataList();
2816
2899
  if (!dataList || dataList.length === 0) return;
2817
- savedDataRef.current = [...dataList];
2818
- currentIndexRef.current = 0;
2819
- setTotalBars(dataList.length);
2820
- setBarIndex(0);
2821
- setIsReplaying(true);
2822
- 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
+ });
2823
2911
  state.chart.clearData?.();
2824
2912
  startInterval(speed);
2825
- }, [state.chart, speed, startInterval]);
2913
+ }, [state.chart, speed, startInterval, replaySavedDataRef, replayIndexRef, dispatch]);
2826
2914
  const stopReplay = react.useCallback(() => {
2827
2915
  if (!state.chart) return;
2828
2916
  clearInterval_();
2829
- const data = savedDataRef.current;
2917
+ const data = replaySavedDataRef.current;
2830
2918
  if (data.length > 0) {
2831
2919
  state.chart.clearData?.();
2832
2920
  for (const bar of data) {
2833
2921
  state.chart.updateData?.(bar);
2834
2922
  }
2835
2923
  }
2836
- savedDataRef.current = [];
2837
- currentIndexRef.current = 0;
2838
- setIsReplaying(false);
2839
- setIsPaused(false);
2840
- setBarIndex(0);
2841
- setTotalBars(0);
2842
- }, [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]);
2843
2931
  const togglePause = react.useCallback(() => {
2844
2932
  if (!isReplaying) return;
2845
2933
  if (isPaused) {
2846
2934
  startInterval(speed);
2847
- setIsPaused(false);
2935
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: false } });
2848
2936
  } else {
2849
2937
  clearInterval_();
2850
- setIsPaused(true);
2938
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
2851
2939
  }
2852
- }, [isReplaying, isPaused, speed, startInterval, clearInterval_]);
2940
+ }, [isReplaying, isPaused, speed, startInterval, clearInterval_, dispatch]);
2853
2941
  const stepForward = react.useCallback(() => {
2854
2942
  if (!isReplaying || !isPaused) return;
2855
2943
  addNextBar();
2856
2944
  }, [isReplaying, isPaused, addNextBar]);
2857
2945
  const stepBackward = react.useCallback(() => {
2858
2946
  if (!isReplaying || !isPaused || !state.chart) return;
2859
- const idx = currentIndexRef.current;
2947
+ const idx = replayIndexRef.current;
2860
2948
  if (idx <= 1) return;
2861
- const data = savedDataRef.current;
2949
+ const data = replaySavedDataRef.current;
2862
2950
  state.chart.clearData?.();
2863
2951
  for (let i = 0; i < idx - 1; i++) {
2864
2952
  state.chart.updateData?.(data[i]);
2865
2953
  }
2866
- currentIndexRef.current = idx - 1;
2867
- setBarIndex(idx - 1);
2868
- }, [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]);
2869
2957
  const seekTo = react.useCallback(
2870
2958
  (targetIndex) => {
2871
2959
  if (!isReplaying || !state.chart) return;
2872
- const data = savedDataRef.current;
2960
+ const data = replaySavedDataRef.current;
2873
2961
  const clamped = Math.max(0, Math.min(targetIndex, data.length));
2874
2962
  clearInterval_();
2875
- setIsPaused(true);
2876
2963
  state.chart.clearData?.();
2877
2964
  for (let i = 0; i < clamped; i++) {
2878
2965
  state.chart.updateData?.(data[i]);
2879
2966
  }
2880
- currentIndexRef.current = clamped;
2881
- setBarIndex(clamped);
2967
+ replayIndexRef.current = clamped;
2968
+ dispatch({
2969
+ type: "SET_REPLAY",
2970
+ replay: { isPaused: true, barIndex: clamped }
2971
+ });
2882
2972
  },
2883
- [isReplaying, state.chart, clearInterval_]
2973
+ [isReplaying, state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]
2884
2974
  );
2885
2975
  const setSpeed = react.useCallback(
2886
2976
  (newSpeed) => {
2887
- setSpeedState(newSpeed);
2977
+ dispatch({ type: "SET_REPLAY", replay: { speed: newSpeed } });
2888
2978
  if (isReplaying && !isPaused) {
2889
2979
  startInterval(newSpeed);
2890
2980
  }
2891
2981
  },
2892
- [isReplaying, isPaused, startInterval]
2982
+ [isReplaying, isPaused, startInterval, dispatch]
2893
2983
  );
2894
- react.useEffect(() => {
2895
- return () => {
2896
- clearInterval_();
2897
- };
2898
- }, [clearInterval_]);
2899
2984
  return {
2900
2985
  isReplaying,
2901
2986
  isPaused,
@@ -3052,31 +3137,25 @@ function useCompare() {
3052
3137
  return { symbols, addSymbol, removeSymbol, toggleSymbol, clearAll };
3053
3138
  }
3054
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
+ }
3055
3147
  function useMeasure() {
3056
- const { state } = useKlinechartsUI();
3057
- const [isActive, setIsActive] = react.useState(false);
3058
- const [fromPoint, setFromPoint] = react.useState(null);
3059
- const [result, setResult] = react.useState(null);
3060
- const clickCountRef = react.useRef(0);
3061
- const computeResult = react.useCallback(
3062
- (from, to) => {
3063
- const priceDiff = to.price - from.price;
3064
- const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
3065
- const bars = Math.abs(to.barIndex - from.barIndex);
3066
- const timeDiff = Math.abs(to.timestamp - from.timestamp);
3067
- return { from, to, priceDiff, pricePercent, bars, timeDiff };
3068
- },
3069
- []
3070
- );
3148
+ const { state, dispatch } = useKlinechartsUI();
3149
+ const { isActive, fromPoint, result } = state.measure;
3071
3150
  const cleanup = react.useCallback(() => {
3072
3151
  state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
3073
3152
  }, [state.chart]);
3074
3153
  const startMeasure = react.useCallback(() => {
3075
3154
  cleanup();
3076
- setIsActive(true);
3077
- setFromPoint(null);
3078
- setResult(null);
3079
- clickCountRef.current = 0;
3155
+ dispatch({
3156
+ type: "SET_MEASURE",
3157
+ measure: { isActive: true, fromPoint: null, result: null }
3158
+ });
3080
3159
  if (!state.chart) return;
3081
3160
  state.chart.createOverlay({
3082
3161
  name: "segment",
@@ -3112,23 +3191,25 @@ function useMeasure() {
3112
3191
  };
3113
3192
  const from = makePoint(points[0]);
3114
3193
  const to = makePoint(points[1]);
3115
- setFromPoint(from);
3116
- setResult(computeResult(from, to));
3117
- setIsActive(false);
3194
+ dispatch({
3195
+ type: "SET_MEASURE",
3196
+ measure: {
3197
+ isActive: false,
3198
+ fromPoint: from,
3199
+ result: computeResult(from, to)
3200
+ }
3201
+ });
3118
3202
  }
3119
3203
  });
3120
- }, [state.chart, cleanup, computeResult]);
3204
+ }, [state.chart, cleanup, dispatch]);
3121
3205
  const cancelMeasure = react.useCallback(() => {
3122
3206
  cleanup();
3123
- setIsActive(false);
3124
- setFromPoint(null);
3125
- clickCountRef.current = 0;
3126
- }, [cleanup]);
3207
+ dispatch({ type: "SET_MEASURE", measure: { isActive: false, fromPoint: null } });
3208
+ }, [cleanup, dispatch]);
3127
3209
  const clearResult = react.useCallback(() => {
3128
3210
  cleanup();
3129
- setResult(null);
3130
- setFromPoint(null);
3131
- }, [cleanup]);
3211
+ dispatch({ type: "SET_MEASURE", measure: { result: null, fromPoint: null } });
3212
+ }, [cleanup, dispatch]);
3132
3213
  react.useEffect(() => {
3133
3214
  return () => {
3134
3215
  state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
@@ -3294,175 +3375,175 @@ function createDataLoader(datafeed, dispatch) {
3294
3375
 
3295
3376
  Object.defineProperty(exports, "TA", {
3296
3377
  enumerable: true,
3297
- get: function () { return chunkPIFLJKYF_cjs.TA_default; }
3378
+ get: function () { return chunkUJNJH3BS_cjs.TA_default; }
3298
3379
  });
3299
3380
  Object.defineProperty(exports, "abcd", {
3300
3381
  enumerable: true,
3301
- get: function () { return chunkPIFLJKYF_cjs.abcd_default; }
3382
+ get: function () { return chunkUJNJH3BS_cjs.abcd_default; }
3302
3383
  });
3303
3384
  Object.defineProperty(exports, "anyWaves", {
3304
3385
  enumerable: true,
3305
- get: function () { return chunkPIFLJKYF_cjs.anyWaves_default; }
3386
+ get: function () { return chunkUJNJH3BS_cjs.anyWaves_default; }
3306
3387
  });
3307
3388
  Object.defineProperty(exports, "arrow", {
3308
3389
  enumerable: true,
3309
- get: function () { return chunkPIFLJKYF_cjs.arrow_default; }
3390
+ get: function () { return chunkUJNJH3BS_cjs.arrow_default; }
3310
3391
  });
3311
3392
  Object.defineProperty(exports, "bollTv", {
3312
3393
  enumerable: true,
3313
- get: function () { return chunkPIFLJKYF_cjs.bollTv_default; }
3394
+ get: function () { return chunkUJNJH3BS_cjs.bollTv_default; }
3314
3395
  });
3315
3396
  Object.defineProperty(exports, "brush", {
3316
3397
  enumerable: true,
3317
- get: function () { return chunkPIFLJKYF_cjs.brush_default; }
3398
+ get: function () { return chunkUJNJH3BS_cjs.brush_default; }
3318
3399
  });
3319
3400
  Object.defineProperty(exports, "cci", {
3320
3401
  enumerable: true,
3321
- get: function () { return chunkPIFLJKYF_cjs.cci_default; }
3402
+ get: function () { return chunkUJNJH3BS_cjs.cci_default; }
3322
3403
  });
3323
3404
  Object.defineProperty(exports, "circle", {
3324
3405
  enumerable: true,
3325
- get: function () { return chunkPIFLJKYF_cjs.circle_default; }
3406
+ get: function () { return chunkUJNJH3BS_cjs.circle_default; }
3326
3407
  });
3327
3408
  Object.defineProperty(exports, "depthOverlay", {
3328
3409
  enumerable: true,
3329
- get: function () { return chunkPIFLJKYF_cjs.depthOverlay_default; }
3410
+ get: function () { return chunkUJNJH3BS_cjs.depthOverlay_default; }
3330
3411
  });
3331
3412
  Object.defineProperty(exports, "eightWaves", {
3332
3413
  enumerable: true,
3333
- get: function () { return chunkPIFLJKYF_cjs.eightWaves_default; }
3414
+ get: function () { return chunkUJNJH3BS_cjs.eightWaves_default; }
3334
3415
  });
3335
3416
  Object.defineProperty(exports, "elliottWave", {
3336
3417
  enumerable: true,
3337
- get: function () { return chunkPIFLJKYF_cjs.elliottWave_default; }
3418
+ get: function () { return chunkUJNJH3BS_cjs.elliottWave_default; }
3338
3419
  });
3339
3420
  Object.defineProperty(exports, "fibRetracement", {
3340
3421
  enumerable: true,
3341
- get: function () { return chunkPIFLJKYF_cjs.fibRetracement_default; }
3422
+ get: function () { return chunkUJNJH3BS_cjs.fibRetracement_default; }
3342
3423
  });
3343
3424
  Object.defineProperty(exports, "fibonacciCircle", {
3344
3425
  enumerable: true,
3345
- get: function () { return chunkPIFLJKYF_cjs.fibonacciCircle_default; }
3426
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciCircle_default; }
3346
3427
  });
3347
3428
  Object.defineProperty(exports, "fibonacciExtension", {
3348
3429
  enumerable: true,
3349
- get: function () { return chunkPIFLJKYF_cjs.fibonacciExtension_default; }
3430
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciExtension_default; }
3350
3431
  });
3351
3432
  Object.defineProperty(exports, "fibonacciSegment", {
3352
3433
  enumerable: true,
3353
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSegment_default; }
3434
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciSegment_default; }
3354
3435
  });
3355
3436
  Object.defineProperty(exports, "fibonacciSpeedResistanceFan", {
3356
3437
  enumerable: true,
3357
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSpeedResistanceFan_default; }
3438
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciSpeedResistanceFan_default; }
3358
3439
  });
3359
3440
  Object.defineProperty(exports, "fibonacciSpiral", {
3360
3441
  enumerable: true,
3361
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSpiral_default; }
3442
+ get: function () { return chunkUJNJH3BS_cjs.fibonacciSpiral_default; }
3362
3443
  });
3363
3444
  Object.defineProperty(exports, "fiveWaves", {
3364
3445
  enumerable: true,
3365
- get: function () { return chunkPIFLJKYF_cjs.fiveWaves_default; }
3446
+ get: function () { return chunkUJNJH3BS_cjs.fiveWaves_default; }
3366
3447
  });
3367
3448
  Object.defineProperty(exports, "gannBox", {
3368
3449
  enumerable: true,
3369
- get: function () { return chunkPIFLJKYF_cjs.gannBox_default; }
3450
+ get: function () { return chunkUJNJH3BS_cjs.gannBox_default; }
3370
3451
  });
3371
3452
  Object.defineProperty(exports, "gannFan", {
3372
3453
  enumerable: true,
3373
- get: function () { return chunkPIFLJKYF_cjs.gannFan_default; }
3454
+ get: function () { return chunkUJNJH3BS_cjs.gannFan_default; }
3374
3455
  });
3375
3456
  Object.defineProperty(exports, "hma", {
3376
3457
  enumerable: true,
3377
- get: function () { return chunkPIFLJKYF_cjs.hma_default; }
3458
+ get: function () { return chunkUJNJH3BS_cjs.hma_default; }
3378
3459
  });
3379
3460
  Object.defineProperty(exports, "ichimoku", {
3380
3461
  enumerable: true,
3381
- get: function () { return chunkPIFLJKYF_cjs.ichimoku_default; }
3462
+ get: function () { return chunkUJNJH3BS_cjs.ichimoku_default; }
3382
3463
  });
3383
3464
  Object.defineProperty(exports, "indicators", {
3384
3465
  enumerable: true,
3385
- get: function () { return chunkPIFLJKYF_cjs.indicators; }
3466
+ get: function () { return chunkUJNJH3BS_cjs.indicators; }
3386
3467
  });
3387
3468
  Object.defineProperty(exports, "longPosition", {
3388
3469
  enumerable: true,
3389
- get: function () { return chunkPIFLJKYF_cjs.longPosition_default; }
3470
+ get: function () { return chunkUJNJH3BS_cjs.longPosition_default; }
3390
3471
  });
3391
3472
  Object.defineProperty(exports, "maRibbon", {
3392
3473
  enumerable: true,
3393
- get: function () { return chunkPIFLJKYF_cjs.maRibbon_default; }
3474
+ get: function () { return chunkUJNJH3BS_cjs.maRibbon_default; }
3394
3475
  });
3395
3476
  Object.defineProperty(exports, "macdTv", {
3396
3477
  enumerable: true,
3397
- get: function () { return chunkPIFLJKYF_cjs.macdTv_default; }
3478
+ get: function () { return chunkUJNJH3BS_cjs.macdTv_default; }
3398
3479
  });
3399
3480
  Object.defineProperty(exports, "measure", {
3400
3481
  enumerable: true,
3401
- get: function () { return chunkPIFLJKYF_cjs.measure_default; }
3482
+ get: function () { return chunkUJNJH3BS_cjs.measure_default; }
3402
3483
  });
3403
3484
  Object.defineProperty(exports, "orderLine", {
3404
3485
  enumerable: true,
3405
- get: function () { return chunkPIFLJKYF_cjs.orderLine_default; }
3486
+ get: function () { return chunkUJNJH3BS_cjs.orderLine_default; }
3406
3487
  });
3407
3488
  Object.defineProperty(exports, "overlays", {
3408
3489
  enumerable: true,
3409
- get: function () { return chunkPIFLJKYF_cjs.overlays; }
3490
+ get: function () { return chunkUJNJH3BS_cjs.overlays; }
3410
3491
  });
3411
3492
  Object.defineProperty(exports, "parallelChannel", {
3412
3493
  enumerable: true,
3413
- get: function () { return chunkPIFLJKYF_cjs.parallelChannel_default; }
3494
+ get: function () { return chunkUJNJH3BS_cjs.parallelChannel_default; }
3414
3495
  });
3415
3496
  Object.defineProperty(exports, "parallelogram", {
3416
3497
  enumerable: true,
3417
- get: function () { return chunkPIFLJKYF_cjs.parallelogram_default; }
3498
+ get: function () { return chunkUJNJH3BS_cjs.parallelogram_default; }
3418
3499
  });
3419
3500
  Object.defineProperty(exports, "pivotPoints", {
3420
3501
  enumerable: true,
3421
- get: function () { return chunkPIFLJKYF_cjs.pivotPoints_default; }
3502
+ get: function () { return chunkUJNJH3BS_cjs.pivotPoints_default; }
3422
3503
  });
3423
3504
  Object.defineProperty(exports, "ray", {
3424
3505
  enumerable: true,
3425
- get: function () { return chunkPIFLJKYF_cjs.ray_default; }
3506
+ get: function () { return chunkUJNJH3BS_cjs.ray_default; }
3426
3507
  });
3427
3508
  Object.defineProperty(exports, "rect", {
3428
3509
  enumerable: true,
3429
- get: function () { return chunkPIFLJKYF_cjs.rect_default; }
3510
+ get: function () { return chunkUJNJH3BS_cjs.rect_default; }
3430
3511
  });
3431
3512
  Object.defineProperty(exports, "registerExtensions", {
3432
3513
  enumerable: true,
3433
- get: function () { return chunkPIFLJKYF_cjs.registerExtensions; }
3514
+ get: function () { return chunkUJNJH3BS_cjs.registerExtensions; }
3434
3515
  });
3435
3516
  Object.defineProperty(exports, "rsiTv", {
3436
3517
  enumerable: true,
3437
- get: function () { return chunkPIFLJKYF_cjs.rsiTv_default; }
3518
+ get: function () { return chunkUJNJH3BS_cjs.rsiTv_default; }
3438
3519
  });
3439
3520
  Object.defineProperty(exports, "shortPosition", {
3440
3521
  enumerable: true,
3441
- get: function () { return chunkPIFLJKYF_cjs.shortPosition_default; }
3522
+ get: function () { return chunkUJNJH3BS_cjs.shortPosition_default; }
3442
3523
  });
3443
3524
  Object.defineProperty(exports, "stochastic", {
3444
3525
  enumerable: true,
3445
- get: function () { return chunkPIFLJKYF_cjs.stochastic_default; }
3526
+ get: function () { return chunkUJNJH3BS_cjs.stochastic_default; }
3446
3527
  });
3447
3528
  Object.defineProperty(exports, "superTrend", {
3448
3529
  enumerable: true,
3449
- get: function () { return chunkPIFLJKYF_cjs.superTrend_default; }
3530
+ get: function () { return chunkUJNJH3BS_cjs.superTrend_default; }
3450
3531
  });
3451
3532
  Object.defineProperty(exports, "threeWaves", {
3452
3533
  enumerable: true,
3453
- get: function () { return chunkPIFLJKYF_cjs.threeWaves_default; }
3534
+ get: function () { return chunkUJNJH3BS_cjs.threeWaves_default; }
3454
3535
  });
3455
3536
  Object.defineProperty(exports, "triangle", {
3456
3537
  enumerable: true,
3457
- get: function () { return chunkPIFLJKYF_cjs.triangle_default; }
3538
+ get: function () { return chunkUJNJH3BS_cjs.triangle_default; }
3458
3539
  });
3459
3540
  Object.defineProperty(exports, "vwap", {
3460
3541
  enumerable: true,
3461
- get: function () { return chunkPIFLJKYF_cjs.vwap_default; }
3542
+ get: function () { return chunkUJNJH3BS_cjs.vwap_default; }
3462
3543
  });
3463
3544
  Object.defineProperty(exports, "xabcd", {
3464
3545
  enumerable: true,
3465
- get: function () { return chunkPIFLJKYF_cjs.xabcd_default; }
3546
+ get: function () { return chunkUJNJH3BS_cjs.xabcd_default; }
3466
3547
  });
3467
3548
  exports.CANDLE_TYPES = CANDLE_TYPES;
3468
3549
  exports.COMPARE_RULES = COMPARE_RULES;