react-klinecharts-ui 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkPIFLJKYF_cjs = require('./chunk-PIFLJKYF.cjs');
3
+ var chunkFSHI2ZDB_cjs = require('./chunk-FSHI2ZDB.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
+ chunkFSHI2ZDB_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
  }
@@ -1559,6 +1678,53 @@ function useScreenshot() {
1559
1678
  clear
1560
1679
  };
1561
1680
  }
1681
+ function useHotkeys() {
1682
+ const { state } = useKlinechartsUI();
1683
+ const registerHotkey = react.useCallback((template) => {
1684
+ reactKlinecharts.registerHotkey(template);
1685
+ }, []);
1686
+ const getHotkey = react.useCallback(
1687
+ (name) => reactKlinecharts.getHotkey(name) ?? null,
1688
+ []
1689
+ );
1690
+ const setHotkeysEnabled = react.useCallback(
1691
+ (enabled, exclude) => {
1692
+ state.chart?.setHotkey(
1693
+ exclude ? { enabled, exclude } : { enabled }
1694
+ );
1695
+ },
1696
+ [state.chart]
1697
+ );
1698
+ const getHotkeysConfig = react.useCallback(
1699
+ () => state.chart?.getHotkey() ?? null,
1700
+ [state.chart]
1701
+ );
1702
+ return {
1703
+ registerHotkey,
1704
+ getHotkey,
1705
+ supportedHotkeys: reactKlinecharts.getSupportedHotkeys(),
1706
+ setHotkeysEnabled,
1707
+ getHotkeysConfig
1708
+ };
1709
+ }
1710
+ function useChartAxes() {
1711
+ const { state } = useKlinechartsUI();
1712
+ const overrideXAxis = react.useCallback(
1713
+ (override) => {
1714
+ if (!state.chart) return;
1715
+ state.chart.overrideXAxis(override);
1716
+ },
1717
+ [state.chart]
1718
+ );
1719
+ const overrideYAxis = react.useCallback(
1720
+ (override) => {
1721
+ if (!state.chart) return;
1722
+ state.chart.overrideYAxis(override);
1723
+ },
1724
+ [state.chart]
1725
+ );
1726
+ return { overrideXAxis, overrideYAxis };
1727
+ }
1562
1728
  function useFullscreen() {
1563
1729
  const { fullscreenContainerRef } = useKlinechartsUIDispatch();
1564
1730
  const [isFullscreen, setIsFullscreen] = react.useState(false);
@@ -2123,6 +2289,7 @@ function useLayoutManager() {
2123
2289
  const newMainIndicators = [];
2124
2290
  const newSubIndicators = {};
2125
2291
  const restoredAxes = {};
2292
+ const restoredVisibility = {};
2126
2293
  if (chartState.indicators) {
2127
2294
  for (const ind of chartState.indicators) {
2128
2295
  const isMain = ind.paneId === "candle_pane";
@@ -2150,6 +2317,9 @@ function useLayoutManager() {
2150
2317
  if (ind.yAxisId) {
2151
2318
  restoredAxes[id2] = ind.yAxisId;
2152
2319
  }
2320
+ if (ind.visible === false) {
2321
+ restoredVisibility[id2] = false;
2322
+ }
2153
2323
  if (isMain) {
2154
2324
  newMainIndicators.push(ind.name);
2155
2325
  } else {
@@ -2169,6 +2339,10 @@ function useLayoutManager() {
2169
2339
  type: "SET_INDICATOR_AXES",
2170
2340
  axes: restoredAxes
2171
2341
  });
2342
+ dispatch({
2343
+ type: "SET_INDICATOR_VISIBILITY",
2344
+ visibility: restoredVisibility
2345
+ });
2172
2346
  if (chartState.drawings) {
2173
2347
  for (const drawing of chartState.drawings) {
2174
2348
  chart.createOverlay({
@@ -2352,7 +2526,7 @@ ${code}`
2352
2526
  ];
2353
2527
  const fn = new Function(...allArgs);
2354
2528
  const shadowValues = SHADOW_KEYS.map(() => void 0);
2355
- const result = fn(...shadowValues, chunkPIFLJKYF_cjs.TA_default, dataList, parsedParams);
2529
+ const result = fn(...shadowValues, chunkFSHI2ZDB_cjs.TA_default, dataList, parsedParams);
2356
2530
  if (!Array.isArray(result)) {
2357
2531
  throw new Error("Script must return an Array.");
2358
2532
  }
@@ -2535,100 +2709,63 @@ function useCrosshair() {
2535
2709
  }
2536
2710
  var alertCounter = 0;
2537
2711
  function useAlerts() {
2538
- const { state } = useKlinechartsUI();
2539
- const [alerts, setAlerts] = react.useState([]);
2540
- const callbackRef = react.useRef(null);
2541
- const prevCloseRef = react.useRef(null);
2712
+ const { state, dispatch } = useKlinechartsUI();
2713
+ const { alertTriggeredListenerRef } = useKlinechartsUIDispatch();
2714
+ const alerts = state.alerts;
2542
2715
  const addAlert = react.useCallback(
2543
- (price, condition, message) => {
2716
+ (price, condition, message, extendData) => {
2544
2717
  const id = `alert_${++alertCounter}`;
2718
+ const precision = state.chart?.getSymbol()?.pricePrecision ?? 2;
2719
+ const resolvedExtendData = {
2720
+ ...extendData,
2721
+ text: extendData?.text ?? message ?? price.toFixed(precision)
2722
+ };
2545
2723
  const alert = {
2546
2724
  id,
2547
2725
  price,
2548
2726
  condition,
2549
2727
  message,
2550
- triggered: false
2728
+ triggered: false,
2729
+ extendData: resolvedExtendData
2551
2730
  };
2552
- setAlerts((prev) => [...prev, alert]);
2731
+ dispatch({ type: "SET_ALERTS", alerts: [...state.alerts, alert] });
2553
2732
  if (state.chart) {
2733
+ chunkFSHI2ZDB_cjs.ensureAlertLineRegistered();
2554
2734
  state.chart.createOverlay({
2555
- name: "horizontalStraightLine",
2735
+ name: "alertLine",
2556
2736
  id,
2557
2737
  groupId: "price_alerts",
2558
2738
  points: [{ value: price }],
2559
- styles: {
2560
- line: {
2561
- style: "dashed",
2562
- color: "#ff9800",
2563
- size: 1
2564
- }
2565
- },
2739
+ extendData: resolvedExtendData,
2566
2740
  lock: true
2567
2741
  });
2568
2742
  }
2569
2743
  return id;
2570
2744
  },
2571
- [state.chart]
2745
+ [state.chart, state.alerts, dispatch]
2572
2746
  );
2573
2747
  const removeAlert = react.useCallback(
2574
2748
  (id) => {
2575
- setAlerts((prev) => prev.filter((a) => a.id !== id));
2749
+ dispatch({
2750
+ type: "SET_ALERTS",
2751
+ alerts: state.alerts.filter((a) => a.id !== id)
2752
+ });
2576
2753
  state.chart?.removeOverlay({ id });
2577
2754
  },
2578
- [state.chart]
2755
+ [state.chart, state.alerts, dispatch]
2579
2756
  );
2580
2757
  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]);
2758
+ for (const alert of state.alerts) {
2759
+ state.chart?.removeOverlay({ id: alert.id });
2760
+ }
2761
+ dispatch({ type: "SET_ALERTS", alerts: [] });
2762
+ }, [state.chart, state.alerts, dispatch]);
2588
2763
  const onAlertTriggered = react.useCallback(
2589
2764
  (callback) => {
2590
- callbackRef.current = callback;
2765
+ alertTriggeredListenerRef.current = callback;
2591
2766
  },
2592
- []
2767
+ [alertTriggeredListenerRef]
2593
2768
  );
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
2769
  return {
2633
2770
  alerts,
2634
2771
  addAlert,
@@ -2774,128 +2911,124 @@ function useWatchlist() {
2774
2911
  };
2775
2912
  }
2776
2913
  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);
2914
+ const { state, dispatch } = useKlinechartsUI();
2915
+ const { replayIntervalRef, replaySavedDataRef, replayIndexRef } = useKlinechartsUIDispatch();
2916
+ const { isReplaying, isPaused, speed, barIndex, totalBars } = state.replay;
2786
2917
  const clearInterval_ = react.useCallback(() => {
2787
- if (intervalRef.current !== null) {
2788
- clearInterval(intervalRef.current);
2789
- intervalRef.current = null;
2918
+ if (replayIntervalRef.current !== null) {
2919
+ clearInterval(replayIntervalRef.current);
2920
+ replayIntervalRef.current = null;
2790
2921
  }
2791
- }, []);
2922
+ }, [replayIntervalRef]);
2792
2923
  const addNextBar = react.useCallback(() => {
2793
2924
  if (!state.chart) return;
2794
- const data = savedDataRef.current;
2795
- const idx = currentIndexRef.current;
2925
+ const data = replaySavedDataRef.current;
2926
+ const idx = replayIndexRef.current;
2796
2927
  if (idx >= data.length) {
2797
2928
  clearInterval_();
2798
- setIsPaused(true);
2929
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
2799
2930
  return;
2800
2931
  }
2801
2932
  const bar = data[idx];
2802
2933
  state.chart.updateData?.(bar);
2803
- currentIndexRef.current = idx + 1;
2804
- setBarIndex(idx + 1);
2805
- }, [state.chart, clearInterval_]);
2934
+ replayIndexRef.current = idx + 1;
2935
+ dispatch({ type: "SET_REPLAY", replay: { barIndex: idx + 1 } });
2936
+ }, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
2806
2937
  const startInterval = react.useCallback(
2807
2938
  (currentSpeed) => {
2808
2939
  clearInterval_();
2809
- intervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
2940
+ replayIntervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
2810
2941
  },
2811
- [addNextBar, clearInterval_]
2942
+ [addNextBar, clearInterval_, replayIntervalRef]
2812
2943
  );
2813
2944
  const startReplay = react.useCallback(() => {
2814
2945
  if (!state.chart) return;
2815
2946
  const dataList = state.chart.getDataList();
2816
2947
  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);
2948
+ replaySavedDataRef.current = [...dataList];
2949
+ replayIndexRef.current = 0;
2950
+ dispatch({
2951
+ type: "SET_REPLAY",
2952
+ replay: {
2953
+ totalBars: dataList.length,
2954
+ barIndex: 0,
2955
+ isReplaying: true,
2956
+ isPaused: false
2957
+ }
2958
+ });
2823
2959
  state.chart.clearData?.();
2824
2960
  startInterval(speed);
2825
- }, [state.chart, speed, startInterval]);
2961
+ }, [state.chart, speed, startInterval, replaySavedDataRef, replayIndexRef, dispatch]);
2826
2962
  const stopReplay = react.useCallback(() => {
2827
2963
  if (!state.chart) return;
2828
2964
  clearInterval_();
2829
- const data = savedDataRef.current;
2965
+ const data = replaySavedDataRef.current;
2830
2966
  if (data.length > 0) {
2831
2967
  state.chart.clearData?.();
2832
2968
  for (const bar of data) {
2833
2969
  state.chart.updateData?.(bar);
2834
2970
  }
2835
2971
  }
2836
- savedDataRef.current = [];
2837
- currentIndexRef.current = 0;
2838
- setIsReplaying(false);
2839
- setIsPaused(false);
2840
- setBarIndex(0);
2841
- setTotalBars(0);
2842
- }, [state.chart, clearInterval_]);
2972
+ replaySavedDataRef.current = [];
2973
+ replayIndexRef.current = 0;
2974
+ dispatch({
2975
+ type: "SET_REPLAY",
2976
+ replay: { isReplaying: false, isPaused: false, barIndex: 0, totalBars: 0 }
2977
+ });
2978
+ }, [state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]);
2843
2979
  const togglePause = react.useCallback(() => {
2844
2980
  if (!isReplaying) return;
2845
2981
  if (isPaused) {
2846
2982
  startInterval(speed);
2847
- setIsPaused(false);
2983
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: false } });
2848
2984
  } else {
2849
2985
  clearInterval_();
2850
- setIsPaused(true);
2986
+ dispatch({ type: "SET_REPLAY", replay: { isPaused: true } });
2851
2987
  }
2852
- }, [isReplaying, isPaused, speed, startInterval, clearInterval_]);
2988
+ }, [isReplaying, isPaused, speed, startInterval, clearInterval_, dispatch]);
2853
2989
  const stepForward = react.useCallback(() => {
2854
2990
  if (!isReplaying || !isPaused) return;
2855
2991
  addNextBar();
2856
2992
  }, [isReplaying, isPaused, addNextBar]);
2857
2993
  const stepBackward = react.useCallback(() => {
2858
2994
  if (!isReplaying || !isPaused || !state.chart) return;
2859
- const idx = currentIndexRef.current;
2995
+ const idx = replayIndexRef.current;
2860
2996
  if (idx <= 1) return;
2861
- const data = savedDataRef.current;
2997
+ const data = replaySavedDataRef.current;
2862
2998
  state.chart.clearData?.();
2863
2999
  for (let i = 0; i < idx - 1; i++) {
2864
3000
  state.chart.updateData?.(data[i]);
2865
3001
  }
2866
- currentIndexRef.current = idx - 1;
2867
- setBarIndex(idx - 1);
2868
- }, [isReplaying, isPaused, state.chart]);
3002
+ replayIndexRef.current = idx - 1;
3003
+ dispatch({ type: "SET_REPLAY", replay: { barIndex: idx - 1 } });
3004
+ }, [isReplaying, isPaused, state.chart, replaySavedDataRef, replayIndexRef, dispatch]);
2869
3005
  const seekTo = react.useCallback(
2870
3006
  (targetIndex) => {
2871
3007
  if (!isReplaying || !state.chart) return;
2872
- const data = savedDataRef.current;
3008
+ const data = replaySavedDataRef.current;
2873
3009
  const clamped = Math.max(0, Math.min(targetIndex, data.length));
2874
3010
  clearInterval_();
2875
- setIsPaused(true);
2876
3011
  state.chart.clearData?.();
2877
3012
  for (let i = 0; i < clamped; i++) {
2878
3013
  state.chart.updateData?.(data[i]);
2879
3014
  }
2880
- currentIndexRef.current = clamped;
2881
- setBarIndex(clamped);
3015
+ replayIndexRef.current = clamped;
3016
+ dispatch({
3017
+ type: "SET_REPLAY",
3018
+ replay: { isPaused: true, barIndex: clamped }
3019
+ });
2882
3020
  },
2883
- [isReplaying, state.chart, clearInterval_]
3021
+ [isReplaying, state.chart, clearInterval_, replaySavedDataRef, replayIndexRef, dispatch]
2884
3022
  );
2885
3023
  const setSpeed = react.useCallback(
2886
3024
  (newSpeed) => {
2887
- setSpeedState(newSpeed);
3025
+ dispatch({ type: "SET_REPLAY", replay: { speed: newSpeed } });
2888
3026
  if (isReplaying && !isPaused) {
2889
3027
  startInterval(newSpeed);
2890
3028
  }
2891
3029
  },
2892
- [isReplaying, isPaused, startInterval]
3030
+ [isReplaying, isPaused, startInterval, dispatch]
2893
3031
  );
2894
- react.useEffect(() => {
2895
- return () => {
2896
- clearInterval_();
2897
- };
2898
- }, [clearInterval_]);
2899
3032
  return {
2900
3033
  isReplaying,
2901
3034
  isPaused,
@@ -3052,31 +3185,25 @@ function useCompare() {
3052
3185
  return { symbols, addSymbol, removeSymbol, toggleSymbol, clearAll };
3053
3186
  }
3054
3187
  var MEASURE_OVERLAY_ID = "__measure_line__";
3188
+ function computeResult(from, to) {
3189
+ const priceDiff = to.price - from.price;
3190
+ const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
3191
+ const bars = Math.abs(to.barIndex - from.barIndex);
3192
+ const timeDiff = Math.abs(to.timestamp - from.timestamp);
3193
+ return { from, to, priceDiff, pricePercent, bars, timeDiff };
3194
+ }
3055
3195
  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
- );
3196
+ const { state, dispatch } = useKlinechartsUI();
3197
+ const { isActive, fromPoint, result } = state.measure;
3071
3198
  const cleanup = react.useCallback(() => {
3072
3199
  state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
3073
3200
  }, [state.chart]);
3074
3201
  const startMeasure = react.useCallback(() => {
3075
3202
  cleanup();
3076
- setIsActive(true);
3077
- setFromPoint(null);
3078
- setResult(null);
3079
- clickCountRef.current = 0;
3203
+ dispatch({
3204
+ type: "SET_MEASURE",
3205
+ measure: { isActive: true, fromPoint: null, result: null }
3206
+ });
3080
3207
  if (!state.chart) return;
3081
3208
  state.chart.createOverlay({
3082
3209
  name: "segment",
@@ -3112,23 +3239,25 @@ function useMeasure() {
3112
3239
  };
3113
3240
  const from = makePoint(points[0]);
3114
3241
  const to = makePoint(points[1]);
3115
- setFromPoint(from);
3116
- setResult(computeResult(from, to));
3117
- setIsActive(false);
3242
+ dispatch({
3243
+ type: "SET_MEASURE",
3244
+ measure: {
3245
+ isActive: false,
3246
+ fromPoint: from,
3247
+ result: computeResult(from, to)
3248
+ }
3249
+ });
3118
3250
  }
3119
3251
  });
3120
- }, [state.chart, cleanup, computeResult]);
3252
+ }, [state.chart, cleanup, dispatch]);
3121
3253
  const cancelMeasure = react.useCallback(() => {
3122
3254
  cleanup();
3123
- setIsActive(false);
3124
- setFromPoint(null);
3125
- clickCountRef.current = 0;
3126
- }, [cleanup]);
3255
+ dispatch({ type: "SET_MEASURE", measure: { isActive: false, fromPoint: null } });
3256
+ }, [cleanup, dispatch]);
3127
3257
  const clearResult = react.useCallback(() => {
3128
3258
  cleanup();
3129
- setResult(null);
3130
- setFromPoint(null);
3131
- }, [cleanup]);
3259
+ dispatch({ type: "SET_MEASURE", measure: { result: null, fromPoint: null } });
3260
+ }, [cleanup, dispatch]);
3132
3261
  react.useEffect(() => {
3133
3262
  return () => {
3134
3263
  state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
@@ -3294,175 +3423,179 @@ function createDataLoader(datafeed, dispatch) {
3294
3423
 
3295
3424
  Object.defineProperty(exports, "TA", {
3296
3425
  enumerable: true,
3297
- get: function () { return chunkPIFLJKYF_cjs.TA_default; }
3426
+ get: function () { return chunkFSHI2ZDB_cjs.TA_default; }
3298
3427
  });
3299
3428
  Object.defineProperty(exports, "abcd", {
3300
3429
  enumerable: true,
3301
- get: function () { return chunkPIFLJKYF_cjs.abcd_default; }
3430
+ get: function () { return chunkFSHI2ZDB_cjs.abcd_default; }
3431
+ });
3432
+ Object.defineProperty(exports, "alertLine", {
3433
+ enumerable: true,
3434
+ get: function () { return chunkFSHI2ZDB_cjs.alertLine_default; }
3302
3435
  });
3303
3436
  Object.defineProperty(exports, "anyWaves", {
3304
3437
  enumerable: true,
3305
- get: function () { return chunkPIFLJKYF_cjs.anyWaves_default; }
3438
+ get: function () { return chunkFSHI2ZDB_cjs.anyWaves_default; }
3306
3439
  });
3307
3440
  Object.defineProperty(exports, "arrow", {
3308
3441
  enumerable: true,
3309
- get: function () { return chunkPIFLJKYF_cjs.arrow_default; }
3442
+ get: function () { return chunkFSHI2ZDB_cjs.arrow_default; }
3310
3443
  });
3311
3444
  Object.defineProperty(exports, "bollTv", {
3312
3445
  enumerable: true,
3313
- get: function () { return chunkPIFLJKYF_cjs.bollTv_default; }
3446
+ get: function () { return chunkFSHI2ZDB_cjs.bollTv_default; }
3314
3447
  });
3315
3448
  Object.defineProperty(exports, "brush", {
3316
3449
  enumerable: true,
3317
- get: function () { return chunkPIFLJKYF_cjs.brush_default; }
3450
+ get: function () { return chunkFSHI2ZDB_cjs.brush_default; }
3318
3451
  });
3319
3452
  Object.defineProperty(exports, "cci", {
3320
3453
  enumerable: true,
3321
- get: function () { return chunkPIFLJKYF_cjs.cci_default; }
3454
+ get: function () { return chunkFSHI2ZDB_cjs.cci_default; }
3322
3455
  });
3323
3456
  Object.defineProperty(exports, "circle", {
3324
3457
  enumerable: true,
3325
- get: function () { return chunkPIFLJKYF_cjs.circle_default; }
3458
+ get: function () { return chunkFSHI2ZDB_cjs.circle_default; }
3326
3459
  });
3327
3460
  Object.defineProperty(exports, "depthOverlay", {
3328
3461
  enumerable: true,
3329
- get: function () { return chunkPIFLJKYF_cjs.depthOverlay_default; }
3462
+ get: function () { return chunkFSHI2ZDB_cjs.depthOverlay_default; }
3330
3463
  });
3331
3464
  Object.defineProperty(exports, "eightWaves", {
3332
3465
  enumerable: true,
3333
- get: function () { return chunkPIFLJKYF_cjs.eightWaves_default; }
3466
+ get: function () { return chunkFSHI2ZDB_cjs.eightWaves_default; }
3334
3467
  });
3335
3468
  Object.defineProperty(exports, "elliottWave", {
3336
3469
  enumerable: true,
3337
- get: function () { return chunkPIFLJKYF_cjs.elliottWave_default; }
3470
+ get: function () { return chunkFSHI2ZDB_cjs.elliottWave_default; }
3338
3471
  });
3339
3472
  Object.defineProperty(exports, "fibRetracement", {
3340
3473
  enumerable: true,
3341
- get: function () { return chunkPIFLJKYF_cjs.fibRetracement_default; }
3474
+ get: function () { return chunkFSHI2ZDB_cjs.fibRetracement_default; }
3342
3475
  });
3343
3476
  Object.defineProperty(exports, "fibonacciCircle", {
3344
3477
  enumerable: true,
3345
- get: function () { return chunkPIFLJKYF_cjs.fibonacciCircle_default; }
3478
+ get: function () { return chunkFSHI2ZDB_cjs.fibonacciCircle_default; }
3346
3479
  });
3347
3480
  Object.defineProperty(exports, "fibonacciExtension", {
3348
3481
  enumerable: true,
3349
- get: function () { return chunkPIFLJKYF_cjs.fibonacciExtension_default; }
3482
+ get: function () { return chunkFSHI2ZDB_cjs.fibonacciExtension_default; }
3350
3483
  });
3351
3484
  Object.defineProperty(exports, "fibonacciSegment", {
3352
3485
  enumerable: true,
3353
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSegment_default; }
3486
+ get: function () { return chunkFSHI2ZDB_cjs.fibonacciSegment_default; }
3354
3487
  });
3355
3488
  Object.defineProperty(exports, "fibonacciSpeedResistanceFan", {
3356
3489
  enumerable: true,
3357
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSpeedResistanceFan_default; }
3490
+ get: function () { return chunkFSHI2ZDB_cjs.fibonacciSpeedResistanceFan_default; }
3358
3491
  });
3359
3492
  Object.defineProperty(exports, "fibonacciSpiral", {
3360
3493
  enumerable: true,
3361
- get: function () { return chunkPIFLJKYF_cjs.fibonacciSpiral_default; }
3494
+ get: function () { return chunkFSHI2ZDB_cjs.fibonacciSpiral_default; }
3362
3495
  });
3363
3496
  Object.defineProperty(exports, "fiveWaves", {
3364
3497
  enumerable: true,
3365
- get: function () { return chunkPIFLJKYF_cjs.fiveWaves_default; }
3498
+ get: function () { return chunkFSHI2ZDB_cjs.fiveWaves_default; }
3366
3499
  });
3367
3500
  Object.defineProperty(exports, "gannBox", {
3368
3501
  enumerable: true,
3369
- get: function () { return chunkPIFLJKYF_cjs.gannBox_default; }
3502
+ get: function () { return chunkFSHI2ZDB_cjs.gannBox_default; }
3370
3503
  });
3371
3504
  Object.defineProperty(exports, "gannFan", {
3372
3505
  enumerable: true,
3373
- get: function () { return chunkPIFLJKYF_cjs.gannFan_default; }
3506
+ get: function () { return chunkFSHI2ZDB_cjs.gannFan_default; }
3374
3507
  });
3375
3508
  Object.defineProperty(exports, "hma", {
3376
3509
  enumerable: true,
3377
- get: function () { return chunkPIFLJKYF_cjs.hma_default; }
3510
+ get: function () { return chunkFSHI2ZDB_cjs.hma_default; }
3378
3511
  });
3379
3512
  Object.defineProperty(exports, "ichimoku", {
3380
3513
  enumerable: true,
3381
- get: function () { return chunkPIFLJKYF_cjs.ichimoku_default; }
3514
+ get: function () { return chunkFSHI2ZDB_cjs.ichimoku_default; }
3382
3515
  });
3383
3516
  Object.defineProperty(exports, "indicators", {
3384
3517
  enumerable: true,
3385
- get: function () { return chunkPIFLJKYF_cjs.indicators; }
3518
+ get: function () { return chunkFSHI2ZDB_cjs.indicators; }
3386
3519
  });
3387
3520
  Object.defineProperty(exports, "longPosition", {
3388
3521
  enumerable: true,
3389
- get: function () { return chunkPIFLJKYF_cjs.longPosition_default; }
3522
+ get: function () { return chunkFSHI2ZDB_cjs.longPosition_default; }
3390
3523
  });
3391
3524
  Object.defineProperty(exports, "maRibbon", {
3392
3525
  enumerable: true,
3393
- get: function () { return chunkPIFLJKYF_cjs.maRibbon_default; }
3526
+ get: function () { return chunkFSHI2ZDB_cjs.maRibbon_default; }
3394
3527
  });
3395
3528
  Object.defineProperty(exports, "macdTv", {
3396
3529
  enumerable: true,
3397
- get: function () { return chunkPIFLJKYF_cjs.macdTv_default; }
3530
+ get: function () { return chunkFSHI2ZDB_cjs.macdTv_default; }
3398
3531
  });
3399
3532
  Object.defineProperty(exports, "measure", {
3400
3533
  enumerable: true,
3401
- get: function () { return chunkPIFLJKYF_cjs.measure_default; }
3534
+ get: function () { return chunkFSHI2ZDB_cjs.measure_default; }
3402
3535
  });
3403
3536
  Object.defineProperty(exports, "orderLine", {
3404
3537
  enumerable: true,
3405
- get: function () { return chunkPIFLJKYF_cjs.orderLine_default; }
3538
+ get: function () { return chunkFSHI2ZDB_cjs.orderLine_default; }
3406
3539
  });
3407
3540
  Object.defineProperty(exports, "overlays", {
3408
3541
  enumerable: true,
3409
- get: function () { return chunkPIFLJKYF_cjs.overlays; }
3542
+ get: function () { return chunkFSHI2ZDB_cjs.overlays; }
3410
3543
  });
3411
3544
  Object.defineProperty(exports, "parallelChannel", {
3412
3545
  enumerable: true,
3413
- get: function () { return chunkPIFLJKYF_cjs.parallelChannel_default; }
3546
+ get: function () { return chunkFSHI2ZDB_cjs.parallelChannel_default; }
3414
3547
  });
3415
3548
  Object.defineProperty(exports, "parallelogram", {
3416
3549
  enumerable: true,
3417
- get: function () { return chunkPIFLJKYF_cjs.parallelogram_default; }
3550
+ get: function () { return chunkFSHI2ZDB_cjs.parallelogram_default; }
3418
3551
  });
3419
3552
  Object.defineProperty(exports, "pivotPoints", {
3420
3553
  enumerable: true,
3421
- get: function () { return chunkPIFLJKYF_cjs.pivotPoints_default; }
3554
+ get: function () { return chunkFSHI2ZDB_cjs.pivotPoints_default; }
3422
3555
  });
3423
3556
  Object.defineProperty(exports, "ray", {
3424
3557
  enumerable: true,
3425
- get: function () { return chunkPIFLJKYF_cjs.ray_default; }
3558
+ get: function () { return chunkFSHI2ZDB_cjs.ray_default; }
3426
3559
  });
3427
3560
  Object.defineProperty(exports, "rect", {
3428
3561
  enumerable: true,
3429
- get: function () { return chunkPIFLJKYF_cjs.rect_default; }
3562
+ get: function () { return chunkFSHI2ZDB_cjs.rect_default; }
3430
3563
  });
3431
3564
  Object.defineProperty(exports, "registerExtensions", {
3432
3565
  enumerable: true,
3433
- get: function () { return chunkPIFLJKYF_cjs.registerExtensions; }
3566
+ get: function () { return chunkFSHI2ZDB_cjs.registerExtensions; }
3434
3567
  });
3435
3568
  Object.defineProperty(exports, "rsiTv", {
3436
3569
  enumerable: true,
3437
- get: function () { return chunkPIFLJKYF_cjs.rsiTv_default; }
3570
+ get: function () { return chunkFSHI2ZDB_cjs.rsiTv_default; }
3438
3571
  });
3439
3572
  Object.defineProperty(exports, "shortPosition", {
3440
3573
  enumerable: true,
3441
- get: function () { return chunkPIFLJKYF_cjs.shortPosition_default; }
3574
+ get: function () { return chunkFSHI2ZDB_cjs.shortPosition_default; }
3442
3575
  });
3443
3576
  Object.defineProperty(exports, "stochastic", {
3444
3577
  enumerable: true,
3445
- get: function () { return chunkPIFLJKYF_cjs.stochastic_default; }
3578
+ get: function () { return chunkFSHI2ZDB_cjs.stochastic_default; }
3446
3579
  });
3447
3580
  Object.defineProperty(exports, "superTrend", {
3448
3581
  enumerable: true,
3449
- get: function () { return chunkPIFLJKYF_cjs.superTrend_default; }
3582
+ get: function () { return chunkFSHI2ZDB_cjs.superTrend_default; }
3450
3583
  });
3451
3584
  Object.defineProperty(exports, "threeWaves", {
3452
3585
  enumerable: true,
3453
- get: function () { return chunkPIFLJKYF_cjs.threeWaves_default; }
3586
+ get: function () { return chunkFSHI2ZDB_cjs.threeWaves_default; }
3454
3587
  });
3455
3588
  Object.defineProperty(exports, "triangle", {
3456
3589
  enumerable: true,
3457
- get: function () { return chunkPIFLJKYF_cjs.triangle_default; }
3590
+ get: function () { return chunkFSHI2ZDB_cjs.triangle_default; }
3458
3591
  });
3459
3592
  Object.defineProperty(exports, "vwap", {
3460
3593
  enumerable: true,
3461
- get: function () { return chunkPIFLJKYF_cjs.vwap_default; }
3594
+ get: function () { return chunkFSHI2ZDB_cjs.vwap_default; }
3462
3595
  });
3463
3596
  Object.defineProperty(exports, "xabcd", {
3464
3597
  enumerable: true,
3465
- get: function () { return chunkPIFLJKYF_cjs.xabcd_default; }
3598
+ get: function () { return chunkFSHI2ZDB_cjs.xabcd_default; }
3466
3599
  });
3467
3600
  exports.CANDLE_TYPES = CANDLE_TYPES;
3468
3601
  exports.COMPARE_RULES = COMPARE_RULES;
@@ -3481,11 +3614,13 @@ exports.YAXIS_POSITIONS = YAXIS_POSITIONS;
3481
3614
  exports.createDataLoader = createDataLoader;
3482
3615
  exports.useAlerts = useAlerts;
3483
3616
  exports.useAnnotations = useAnnotations;
3617
+ exports.useChartAxes = useChartAxes;
3484
3618
  exports.useCompare = useCompare;
3485
3619
  exports.useCrosshair = useCrosshair;
3486
3620
  exports.useDataExport = useDataExport;
3487
3621
  exports.useDrawingTools = useDrawingTools;
3488
3622
  exports.useFullscreen = useFullscreen;
3623
+ exports.useHotkeys = useHotkeys;
3489
3624
  exports.useIndicators = useIndicators;
3490
3625
  exports.useKlinechartsUI = useKlinechartsUI;
3491
3626
  exports.useKlinechartsUILoading = useKlinechartsUILoading;