react-klinecharts-ui 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +284 -24
- package/dist/chunk-47JXSAIT.cjs +2467 -0
- package/dist/chunk-47JXSAIT.cjs.map +1 -0
- package/dist/chunk-B3NZ66YA.js +2424 -0
- package/dist/chunk-B3NZ66YA.js.map +1 -0
- package/dist/extensions.cjs +8 -4
- package/dist/extensions.d.cts +5 -1
- package/dist/extensions.d.ts +5 -1
- package/dist/extensions.js +1 -1
- package/dist/index.cjs +1058 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +253 -3
- package/dist/index.d.ts +253 -3
- package/dist/index.js +949 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-PUULIBFN.js +0 -911
- package/dist/chunk-PUULIBFN.js.map +0 -1
- package/dist/chunk-SLTXNLKN.cjs +0 -932
- package/dist/chunk-SLTXNLKN.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk47JXSAIT_cjs = require('./chunk-47JXSAIT.cjs');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var reactKlinecharts = require('react-klinecharts');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -125,6 +125,7 @@ function KlinechartsUIProvider({
|
|
|
125
125
|
})
|
|
126
126
|
);
|
|
127
127
|
const fullscreenContainerRef = react.useRef(null);
|
|
128
|
+
const undoRedoListenerRef = react.useRef(null);
|
|
128
129
|
const stateRef = react.useRef(state);
|
|
129
130
|
stateRef.current = state;
|
|
130
131
|
const callbacksRef = react.useRef({
|
|
@@ -176,7 +177,7 @@ function KlinechartsUIProvider({
|
|
|
176
177
|
const extraOverlaysRef = react.useRef(extraOverlays);
|
|
177
178
|
react.useEffect(() => {
|
|
178
179
|
if (shouldRegister) {
|
|
179
|
-
|
|
180
|
+
chunk47JXSAIT_cjs.registerExtensions();
|
|
180
181
|
}
|
|
181
182
|
extraOverlaysRef.current?.forEach((overlay) => reactKlinecharts.registerOverlay(overlay));
|
|
182
183
|
}, [shouldRegister]);
|
|
@@ -190,7 +191,8 @@ function KlinechartsUIProvider({
|
|
|
190
191
|
dispatch: enhancedDispatch,
|
|
191
192
|
datafeed,
|
|
192
193
|
onSettingsChange,
|
|
193
|
-
fullscreenContainerRef
|
|
194
|
+
fullscreenContainerRef,
|
|
195
|
+
undoRedoListenerRef
|
|
194
196
|
}),
|
|
195
197
|
[enhancedDispatch, datafeed, onSettingsChange]
|
|
196
198
|
);
|
|
@@ -358,7 +360,14 @@ var MAIN_INDICATORS = [
|
|
|
358
360
|
"SMA",
|
|
359
361
|
"BOLL",
|
|
360
362
|
"SAR",
|
|
361
|
-
"BBI"
|
|
363
|
+
"BBI",
|
|
364
|
+
"BOLL_TV",
|
|
365
|
+
"Ichimoku",
|
|
366
|
+
"SuperTrend",
|
|
367
|
+
"HMA",
|
|
368
|
+
"VWAP",
|
|
369
|
+
"PivotPoints",
|
|
370
|
+
"MA_Ribbon"
|
|
362
371
|
];
|
|
363
372
|
var SUB_INDICATORS = [
|
|
364
373
|
"MA",
|
|
@@ -386,7 +395,11 @@ var SUB_INDICATORS = [
|
|
|
386
395
|
"ROC",
|
|
387
396
|
"PVT",
|
|
388
397
|
"BBI",
|
|
389
|
-
"AO"
|
|
398
|
+
"AO",
|
|
399
|
+
"MACD_TV",
|
|
400
|
+
"RSI_TV",
|
|
401
|
+
"CCI_TV",
|
|
402
|
+
"Stochastic"
|
|
390
403
|
];
|
|
391
404
|
var INDICATOR_PARAMS = {
|
|
392
405
|
SMA: {
|
|
@@ -545,12 +558,95 @@ var INDICATOR_PARAMS = {
|
|
|
545
558
|
{ label: "params_1", defaultValue: 5 },
|
|
546
559
|
{ label: "params_2", defaultValue: 34 }
|
|
547
560
|
]
|
|
561
|
+
},
|
|
562
|
+
BOLL_TV: {
|
|
563
|
+
name: "BOLL_TV",
|
|
564
|
+
localeKey: "boll_tv",
|
|
565
|
+
params: [
|
|
566
|
+
{ label: "period", defaultValue: 20 },
|
|
567
|
+
{ label: "multiplier", defaultValue: 2 }
|
|
568
|
+
]
|
|
569
|
+
},
|
|
570
|
+
Ichimoku: {
|
|
571
|
+
name: "Ichimoku",
|
|
572
|
+
localeKey: "ichimoku",
|
|
573
|
+
params: [
|
|
574
|
+
{ label: "tenkan", defaultValue: 9 },
|
|
575
|
+
{ label: "kijun", defaultValue: 26 },
|
|
576
|
+
{ label: "senkou_b", defaultValue: 52 },
|
|
577
|
+
{ label: "offset", defaultValue: 26 }
|
|
578
|
+
]
|
|
579
|
+
},
|
|
580
|
+
SuperTrend: {
|
|
581
|
+
name: "SuperTrend",
|
|
582
|
+
localeKey: "super_trend",
|
|
583
|
+
params: [
|
|
584
|
+
{ label: "period", defaultValue: 10 },
|
|
585
|
+
{ label: "factor", defaultValue: 3 }
|
|
586
|
+
]
|
|
587
|
+
},
|
|
588
|
+
HMA: {
|
|
589
|
+
name: "HMA",
|
|
590
|
+
localeKey: "hma",
|
|
591
|
+
params: [{ label: "period", defaultValue: 9 }]
|
|
592
|
+
},
|
|
593
|
+
VWAP: {
|
|
594
|
+
name: "VWAP",
|
|
595
|
+
localeKey: "vwap",
|
|
596
|
+
params: []
|
|
597
|
+
},
|
|
598
|
+
PivotPoints: {
|
|
599
|
+
name: "PivotPoints",
|
|
600
|
+
localeKey: "pivot_points",
|
|
601
|
+
params: []
|
|
602
|
+
},
|
|
603
|
+
MA_Ribbon: {
|
|
604
|
+
name: "MA_Ribbon",
|
|
605
|
+
localeKey: "ma_ribbon",
|
|
606
|
+
params: [
|
|
607
|
+
{ label: "period_1", defaultValue: 10 },
|
|
608
|
+
{ label: "period_2", defaultValue: 20 },
|
|
609
|
+
{ label: "period_3", defaultValue: 30 },
|
|
610
|
+
{ label: "period_4", defaultValue: 40 }
|
|
611
|
+
]
|
|
612
|
+
},
|
|
613
|
+
MACD_TV: {
|
|
614
|
+
name: "MACD_TV",
|
|
615
|
+
localeKey: "macd_tv",
|
|
616
|
+
params: [
|
|
617
|
+
{ label: "fast", defaultValue: 12 },
|
|
618
|
+
{ label: "slow", defaultValue: 26 },
|
|
619
|
+
{ label: "signal", defaultValue: 9 }
|
|
620
|
+
]
|
|
621
|
+
},
|
|
622
|
+
RSI_TV: {
|
|
623
|
+
name: "RSI_TV",
|
|
624
|
+
localeKey: "rsi_tv",
|
|
625
|
+
params: [
|
|
626
|
+
{ label: "rsi_period", defaultValue: 14 },
|
|
627
|
+
{ label: "ma_period", defaultValue: 14 }
|
|
628
|
+
]
|
|
629
|
+
},
|
|
630
|
+
CCI_TV: {
|
|
631
|
+
name: "CCI_TV",
|
|
632
|
+
localeKey: "cci_tv",
|
|
633
|
+
params: [{ label: "period", defaultValue: 20 }]
|
|
634
|
+
},
|
|
635
|
+
Stochastic: {
|
|
636
|
+
name: "Stochastic",
|
|
637
|
+
localeKey: "stochastic",
|
|
638
|
+
params: [
|
|
639
|
+
{ label: "period", defaultValue: 14 },
|
|
640
|
+
{ label: "smooth_k", defaultValue: 3 },
|
|
641
|
+
{ label: "smooth_d", defaultValue: 3 }
|
|
642
|
+
]
|
|
548
643
|
}
|
|
549
644
|
};
|
|
550
645
|
|
|
551
646
|
// src/hooks/useIndicators.ts
|
|
552
647
|
function useIndicators() {
|
|
553
648
|
const { state, dispatch } = useKlinechartsUI();
|
|
649
|
+
const { undoRedoListenerRef } = useKlinechartsUIDispatch();
|
|
554
650
|
const mainIndicators = react.useMemo(() => {
|
|
555
651
|
const activeNames = state.mainIndicators;
|
|
556
652
|
const inactive = MAIN_INDICATORS.filter((n) => !activeNames.includes(n));
|
|
@@ -577,16 +673,24 @@ function useIndicators() {
|
|
|
577
673
|
);
|
|
578
674
|
const newIndicators = [...state.mainIndicators, name];
|
|
579
675
|
dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
|
|
676
|
+
undoRedoListenerRef.current?.({
|
|
677
|
+
type: "indicator_toggled",
|
|
678
|
+
data: { name, wasActive: false, isMain: true, paneId: "candle_pane" }
|
|
679
|
+
});
|
|
580
680
|
},
|
|
581
|
-
[state.chart, state.mainIndicators, dispatch]
|
|
681
|
+
[state.chart, state.mainIndicators, dispatch, undoRedoListenerRef]
|
|
582
682
|
);
|
|
583
683
|
const removeMainIndicator = react.useCallback(
|
|
584
684
|
(name) => {
|
|
585
685
|
state.chart?.removeIndicator({ id: `main_${name}` });
|
|
586
686
|
const newIndicators = state.mainIndicators.filter((n) => n !== name);
|
|
587
687
|
dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
|
|
688
|
+
undoRedoListenerRef.current?.({
|
|
689
|
+
type: "indicator_toggled",
|
|
690
|
+
data: { name, wasActive: true, isMain: true, paneId: "candle_pane" }
|
|
691
|
+
});
|
|
588
692
|
},
|
|
589
|
-
[state.chart, state.mainIndicators, dispatch]
|
|
693
|
+
[state.chart, state.mainIndicators, dispatch, undoRedoListenerRef]
|
|
590
694
|
);
|
|
591
695
|
const addSubIndicator = react.useCallback(
|
|
592
696
|
(name) => {
|
|
@@ -596,17 +700,26 @@ function useIndicators() {
|
|
|
596
700
|
const paneId = state.chart?.getIndicators({ id })?.[0]?.paneId ?? "";
|
|
597
701
|
const newIndicators = { ...state.subIndicators, [name]: paneId };
|
|
598
702
|
dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
|
|
703
|
+
undoRedoListenerRef.current?.({
|
|
704
|
+
type: "indicator_toggled",
|
|
705
|
+
data: { name, wasActive: false, isMain: false, paneId }
|
|
706
|
+
});
|
|
599
707
|
},
|
|
600
|
-
[state.chart, state.subIndicators, dispatch]
|
|
708
|
+
[state.chart, state.subIndicators, dispatch, undoRedoListenerRef]
|
|
601
709
|
);
|
|
602
710
|
const removeSubIndicator = react.useCallback(
|
|
603
711
|
(name) => {
|
|
712
|
+
const paneId = state.subIndicators[name] ?? "";
|
|
604
713
|
state.chart?.removeIndicator({ id: `sub_${name}` });
|
|
605
714
|
const newIndicators = { ...state.subIndicators };
|
|
606
715
|
delete newIndicators[name];
|
|
607
716
|
dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
|
|
717
|
+
undoRedoListenerRef.current?.({
|
|
718
|
+
type: "indicator_toggled",
|
|
719
|
+
data: { name, wasActive: true, isMain: false, paneId }
|
|
720
|
+
});
|
|
608
721
|
},
|
|
609
|
-
[state.chart, state.subIndicators, dispatch]
|
|
722
|
+
[state.chart, state.subIndicators, dispatch, undoRedoListenerRef]
|
|
610
723
|
);
|
|
611
724
|
const toggleMainIndicator = react.useCallback(
|
|
612
725
|
(name) => {
|
|
@@ -652,9 +765,15 @@ function useIndicators() {
|
|
|
652
765
|
const moveToMain = react.useCallback(
|
|
653
766
|
(name) => {
|
|
654
767
|
if (!state.chart) return;
|
|
768
|
+
const prevCalcParams = state.chart.getIndicators({ id: `sub_${name}` })?.[0]?.calcParams;
|
|
655
769
|
state.chart.removeIndicator({ id: `sub_${name}` });
|
|
656
770
|
state.chart.createIndicator(
|
|
657
|
-
{
|
|
771
|
+
{
|
|
772
|
+
name,
|
|
773
|
+
id: `main_${name}`,
|
|
774
|
+
paneId: "candle_pane",
|
|
775
|
+
...prevCalcParams ? { calcParams: prevCalcParams } : {}
|
|
776
|
+
},
|
|
658
777
|
true,
|
|
659
778
|
{ id: "candle_pane" }
|
|
660
779
|
);
|
|
@@ -671,9 +790,14 @@ function useIndicators() {
|
|
|
671
790
|
const moveToSub = react.useCallback(
|
|
672
791
|
(name) => {
|
|
673
792
|
if (!state.chart) return;
|
|
793
|
+
const prevCalcParams = state.chart.getIndicators({ id: `main_${name}` })?.[0]?.calcParams;
|
|
674
794
|
state.chart.removeIndicator({ id: `main_${name}` });
|
|
675
795
|
const subId = `sub_${name}`;
|
|
676
|
-
state.chart.createIndicator({
|
|
796
|
+
state.chart.createIndicator({
|
|
797
|
+
name,
|
|
798
|
+
id: subId,
|
|
799
|
+
...prevCalcParams ? { calcParams: prevCalcParams } : {}
|
|
800
|
+
});
|
|
677
801
|
const paneId = state.chart.getIndicators({ id: subId })?.[0]?.paneId ?? "";
|
|
678
802
|
const newMain = state.mainIndicators.filter((n) => n !== name);
|
|
679
803
|
dispatch({ type: "SET_MAIN_INDICATORS", indicators: newMain });
|
|
@@ -737,7 +861,9 @@ var DRAWING_CATEGORIES = [
|
|
|
737
861
|
key: "moreLine",
|
|
738
862
|
tools: [
|
|
739
863
|
{ name: "priceChannelLine", localeKey: "price_channel_line" },
|
|
740
|
-
{ name: "parallelStraightLine", localeKey: "parallel_straight_line" }
|
|
864
|
+
{ name: "parallelStraightLine", localeKey: "parallel_straight_line" },
|
|
865
|
+
{ name: "ray", localeKey: "ray" },
|
|
866
|
+
{ name: "parallelChannel", localeKey: "parallel_channel" }
|
|
741
867
|
]
|
|
742
868
|
},
|
|
743
869
|
{
|
|
@@ -761,7 +887,9 @@ var DRAWING_CATEGORIES = [
|
|
|
761
887
|
localeKey: "fibonacci_speed_resistance_fan"
|
|
762
888
|
},
|
|
763
889
|
{ name: "fibonacciExtension", localeKey: "fibonacci_extension" },
|
|
764
|
-
{ name: "
|
|
890
|
+
{ name: "fibRetracement", localeKey: "fib_retracement" },
|
|
891
|
+
{ name: "gannBox", localeKey: "gann_box" },
|
|
892
|
+
{ name: "gannFan", localeKey: "gann_fan" }
|
|
765
893
|
]
|
|
766
894
|
},
|
|
767
895
|
{
|
|
@@ -772,7 +900,27 @@ var DRAWING_CATEGORIES = [
|
|
|
772
900
|
{ name: "threeWaves", localeKey: "three_waves" },
|
|
773
901
|
{ name: "fiveWaves", localeKey: "five_waves" },
|
|
774
902
|
{ name: "eightWaves", localeKey: "eight_waves" },
|
|
775
|
-
{ name: "anyWaves", localeKey: "any_waves" }
|
|
903
|
+
{ name: "anyWaves", localeKey: "any_waves" },
|
|
904
|
+
{ name: "elliottWave", localeKey: "elliott_wave" }
|
|
905
|
+
]
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
key: "measure",
|
|
909
|
+
tools: [
|
|
910
|
+
{ name: "measure", localeKey: "measure" }
|
|
911
|
+
]
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
key: "position",
|
|
915
|
+
tools: [
|
|
916
|
+
{ name: "longPosition", localeKey: "long_position" },
|
|
917
|
+
{ name: "shortPosition", localeKey: "short_position" }
|
|
918
|
+
]
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
key: "annotation",
|
|
922
|
+
tools: [
|
|
923
|
+
{ name: "brush", localeKey: "brush" }
|
|
776
924
|
]
|
|
777
925
|
}
|
|
778
926
|
];
|
|
@@ -781,6 +929,7 @@ var DRAWING_CATEGORIES = [
|
|
|
781
929
|
var DRAWING_GROUP_ID = "drawing_tools";
|
|
782
930
|
function useDrawingTools() {
|
|
783
931
|
const { state } = useKlinechartsUI();
|
|
932
|
+
const { undoRedoListenerRef } = useKlinechartsUIDispatch();
|
|
784
933
|
const [activeTool, setActiveTool] = react.useState(null);
|
|
785
934
|
const [magnetMode, setMagnetModeState] = react.useState("normal");
|
|
786
935
|
const [isLocked, setIsLocked] = react.useState(false);
|
|
@@ -803,10 +952,25 @@ function useDrawingTools() {
|
|
|
803
952
|
groupId: DRAWING_GROUP_ID,
|
|
804
953
|
lock: isLocked,
|
|
805
954
|
visible: isVisible,
|
|
806
|
-
mode: magnetMode === "strong" ? "strong_magnet" : magnetMode === "weak" ? "weak_magnet" : "normal"
|
|
955
|
+
mode: magnetMode === "strong" ? "strong_magnet" : magnetMode === "weak" ? "weak_magnet" : "normal",
|
|
956
|
+
onDrawEnd: (event) => {
|
|
957
|
+
const o = event.overlay;
|
|
958
|
+
undoRedoListenerRef.current?.({
|
|
959
|
+
type: "overlay_added",
|
|
960
|
+
data: {
|
|
961
|
+
id: o.id,
|
|
962
|
+
overlayData: {
|
|
963
|
+
name: o.name,
|
|
964
|
+
points: o.points,
|
|
965
|
+
styles: o.styles,
|
|
966
|
+
extendData: o.extendData
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
807
971
|
});
|
|
808
972
|
},
|
|
809
|
-
[state.chart, isLocked, isVisible, magnetMode]
|
|
973
|
+
[state.chart, isLocked, isVisible, magnetMode, undoRedoListenerRef]
|
|
810
974
|
);
|
|
811
975
|
const clearActiveTool = react.useCallback(() => {
|
|
812
976
|
setActiveTool(null);
|
|
@@ -854,9 +1018,23 @@ function useDrawingTools() {
|
|
|
854
1018
|
}
|
|
855
1019
|
}, [state.chart, isVisible]);
|
|
856
1020
|
const removeAllDrawings = react.useCallback(() => {
|
|
1021
|
+
const overlays2 = state.chart?.getOverlays({ groupId: DRAWING_GROUP_ID });
|
|
1022
|
+
if (overlays2 && overlays2.length > 0) {
|
|
1023
|
+
const snapshot = overlays2.map((o) => ({
|
|
1024
|
+
name: o.name,
|
|
1025
|
+
id: o.id,
|
|
1026
|
+
points: o.points,
|
|
1027
|
+
styles: o.styles,
|
|
1028
|
+
extendData: o.extendData
|
|
1029
|
+
}));
|
|
1030
|
+
undoRedoListenerRef.current?.({
|
|
1031
|
+
type: "overlays_removed",
|
|
1032
|
+
data: { overlays: snapshot }
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
857
1035
|
state.chart?.removeOverlay({ groupId: DRAWING_GROUP_ID });
|
|
858
1036
|
setActiveTool(null);
|
|
859
|
-
}, [state.chart]);
|
|
1037
|
+
}, [state.chart, undoRedoListenerRef]);
|
|
860
1038
|
return {
|
|
861
1039
|
categories,
|
|
862
1040
|
activeTool,
|
|
@@ -1260,12 +1438,12 @@ function useOrderLines() {
|
|
|
1260
1438
|
if (onPriceChange) {
|
|
1261
1439
|
callbacksRef.current.set(id, onPriceChange);
|
|
1262
1440
|
}
|
|
1263
|
-
const
|
|
1264
|
-
const
|
|
1441
|
+
const dataList = state.chart.getDataList();
|
|
1442
|
+
const anchorTimestamp = dataList.length > 0 ? dataList[dataList.length - 1].timestamp : Date.now();
|
|
1265
1443
|
state.chart.createOverlay({
|
|
1266
1444
|
name: "orderLine",
|
|
1267
1445
|
id,
|
|
1268
|
-
points: [{
|
|
1446
|
+
points: [{ timestamp: anchorTimestamp, value: price }],
|
|
1269
1447
|
extendData,
|
|
1270
1448
|
lock: !draggable,
|
|
1271
1449
|
mode: "normal",
|
|
@@ -1289,11 +1467,11 @@ function useOrderLines() {
|
|
|
1289
1467
|
if (onPriceChange !== void 0) {
|
|
1290
1468
|
callbacksRef.current.set(id, onPriceChange);
|
|
1291
1469
|
}
|
|
1292
|
-
const
|
|
1293
|
-
const
|
|
1470
|
+
const dataList = state.chart.getDataList();
|
|
1471
|
+
const anchorTimestamp = dataList.length > 0 ? dataList[dataList.length - 1].timestamp : Date.now();
|
|
1294
1472
|
state.chart.overrideOverlay({
|
|
1295
1473
|
id,
|
|
1296
|
-
...price != null ? { points: [{
|
|
1474
|
+
...price != null ? { points: [{ timestamp: anchorTimestamp, value: price }] } : {},
|
|
1297
1475
|
...draggable != null ? { lock: !draggable } : {},
|
|
1298
1476
|
extendData: {
|
|
1299
1477
|
...existing.extendData,
|
|
@@ -1321,6 +1499,752 @@ function useOrderLines() {
|
|
|
1321
1499
|
removeAllOrderLines
|
|
1322
1500
|
};
|
|
1323
1501
|
}
|
|
1502
|
+
var DRAWING_GROUP_ID2 = "drawing_tools";
|
|
1503
|
+
function useUndoRedo() {
|
|
1504
|
+
const { state, dispatch } = useKlinechartsUI();
|
|
1505
|
+
const { undoRedoListenerRef } = useKlinechartsUIDispatch();
|
|
1506
|
+
const [undoStack, setUndoStack] = react.useState([]);
|
|
1507
|
+
const [redoStack, setRedoStack] = react.useState([]);
|
|
1508
|
+
const isProcessingRef = react.useRef(false);
|
|
1509
|
+
const canUndo = undoStack.length > 0;
|
|
1510
|
+
const canRedo = redoStack.length > 0;
|
|
1511
|
+
const pushAction = react.useCallback((action) => {
|
|
1512
|
+
if (isProcessingRef.current) return;
|
|
1513
|
+
setUndoStack((prev) => [...prev, action]);
|
|
1514
|
+
setRedoStack([]);
|
|
1515
|
+
}, []);
|
|
1516
|
+
react.useEffect(() => {
|
|
1517
|
+
undoRedoListenerRef.current = pushAction;
|
|
1518
|
+
return () => {
|
|
1519
|
+
undoRedoListenerRef.current = null;
|
|
1520
|
+
};
|
|
1521
|
+
}, [pushAction, undoRedoListenerRef]);
|
|
1522
|
+
const clear = react.useCallback(() => {
|
|
1523
|
+
setUndoStack([]);
|
|
1524
|
+
setRedoStack([]);
|
|
1525
|
+
}, []);
|
|
1526
|
+
const undo = react.useCallback(() => {
|
|
1527
|
+
if (undoStack.length === 0 || !state.chart || isProcessingRef.current)
|
|
1528
|
+
return;
|
|
1529
|
+
isProcessingRef.current = true;
|
|
1530
|
+
const action = undoStack[undoStack.length - 1];
|
|
1531
|
+
setUndoStack((prev) => prev.slice(0, -1));
|
|
1532
|
+
try {
|
|
1533
|
+
switch (action.type) {
|
|
1534
|
+
case "overlay_added": {
|
|
1535
|
+
const { id, overlayData } = action.data;
|
|
1536
|
+
state.chart.removeOverlay({ id });
|
|
1537
|
+
setRedoStack((prev) => [
|
|
1538
|
+
...prev,
|
|
1539
|
+
{
|
|
1540
|
+
type: "overlay_added",
|
|
1541
|
+
data: { id, overlayData }
|
|
1542
|
+
}
|
|
1543
|
+
]);
|
|
1544
|
+
break;
|
|
1545
|
+
}
|
|
1546
|
+
case "overlays_removed": {
|
|
1547
|
+
const { overlays: overlays2 } = action.data;
|
|
1548
|
+
const restoredIds = [];
|
|
1549
|
+
for (const overlay of overlays2) {
|
|
1550
|
+
const newId = state.chart.createOverlay({
|
|
1551
|
+
...overlay,
|
|
1552
|
+
groupId: DRAWING_GROUP_ID2
|
|
1553
|
+
});
|
|
1554
|
+
if (typeof newId === "string") {
|
|
1555
|
+
restoredIds.push(newId);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
setRedoStack((prev) => [
|
|
1559
|
+
...prev,
|
|
1560
|
+
{
|
|
1561
|
+
type: "overlays_removed",
|
|
1562
|
+
data: {
|
|
1563
|
+
overlays: overlays2.map((o, i) => ({
|
|
1564
|
+
...o,
|
|
1565
|
+
id: restoredIds[i]
|
|
1566
|
+
}))
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
]);
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
case "indicator_toggled": {
|
|
1573
|
+
const { name, wasActive, isMain, paneId } = action.data;
|
|
1574
|
+
if (wasActive) {
|
|
1575
|
+
if (isMain) {
|
|
1576
|
+
state.chart.createIndicator(
|
|
1577
|
+
{ name, id: `main_${name}`, paneId: "candle_pane" },
|
|
1578
|
+
true,
|
|
1579
|
+
{ id: "candle_pane" }
|
|
1580
|
+
);
|
|
1581
|
+
dispatch({
|
|
1582
|
+
type: "SET_MAIN_INDICATORS",
|
|
1583
|
+
indicators: [...state.mainIndicators, name]
|
|
1584
|
+
});
|
|
1585
|
+
} else {
|
|
1586
|
+
state.chart.createIndicator({ name, id: `sub_${name}` });
|
|
1587
|
+
const newPaneId = state.chart.getIndicators({ id: `sub_${name}` })?.[0]?.paneId ?? "";
|
|
1588
|
+
dispatch({
|
|
1589
|
+
type: "SET_SUB_INDICATORS",
|
|
1590
|
+
indicators: {
|
|
1591
|
+
...state.subIndicators,
|
|
1592
|
+
[name]: newPaneId
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
} else {
|
|
1597
|
+
if (isMain) {
|
|
1598
|
+
state.chart.removeIndicator({ id: `main_${name}` });
|
|
1599
|
+
dispatch({
|
|
1600
|
+
type: "SET_MAIN_INDICATORS",
|
|
1601
|
+
indicators: state.mainIndicators.filter(
|
|
1602
|
+
(n) => n !== name
|
|
1603
|
+
)
|
|
1604
|
+
});
|
|
1605
|
+
} else {
|
|
1606
|
+
state.chart.removeIndicator({ id: `sub_${name}` });
|
|
1607
|
+
const newSub = { ...state.subIndicators };
|
|
1608
|
+
delete newSub[name];
|
|
1609
|
+
dispatch({
|
|
1610
|
+
type: "SET_SUB_INDICATORS",
|
|
1611
|
+
indicators: newSub
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
setRedoStack((prev) => [
|
|
1616
|
+
...prev,
|
|
1617
|
+
{
|
|
1618
|
+
type: "indicator_toggled",
|
|
1619
|
+
data: {
|
|
1620
|
+
name,
|
|
1621
|
+
wasActive: !wasActive,
|
|
1622
|
+
isMain,
|
|
1623
|
+
paneId
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
]);
|
|
1627
|
+
break;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
} finally {
|
|
1631
|
+
isProcessingRef.current = false;
|
|
1632
|
+
}
|
|
1633
|
+
}, [undoStack, state.chart, state.mainIndicators, state.subIndicators, dispatch]);
|
|
1634
|
+
const redo = react.useCallback(() => {
|
|
1635
|
+
if (redoStack.length === 0 || !state.chart || isProcessingRef.current)
|
|
1636
|
+
return;
|
|
1637
|
+
isProcessingRef.current = true;
|
|
1638
|
+
const action = redoStack[redoStack.length - 1];
|
|
1639
|
+
setRedoStack((prev) => prev.slice(0, -1));
|
|
1640
|
+
try {
|
|
1641
|
+
switch (action.type) {
|
|
1642
|
+
case "overlay_added": {
|
|
1643
|
+
const { overlayData } = action.data;
|
|
1644
|
+
const newId = state.chart.createOverlay({
|
|
1645
|
+
...overlayData,
|
|
1646
|
+
groupId: DRAWING_GROUP_ID2
|
|
1647
|
+
});
|
|
1648
|
+
setUndoStack((prev) => [
|
|
1649
|
+
...prev,
|
|
1650
|
+
{
|
|
1651
|
+
type: "overlay_added",
|
|
1652
|
+
data: {
|
|
1653
|
+
id: typeof newId === "string" ? newId : action.data.id,
|
|
1654
|
+
overlayData
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
]);
|
|
1658
|
+
break;
|
|
1659
|
+
}
|
|
1660
|
+
case "overlays_removed": {
|
|
1661
|
+
const { overlays: overlays2 } = action.data;
|
|
1662
|
+
const snapshotOverlays = overlays2.map((o) => {
|
|
1663
|
+
const allOverlays = state.chart?.getOverlays({ id: o.id });
|
|
1664
|
+
const actual = allOverlays?.[0];
|
|
1665
|
+
return actual ? {
|
|
1666
|
+
name: actual.name,
|
|
1667
|
+
points: actual.points,
|
|
1668
|
+
styles: actual.styles,
|
|
1669
|
+
extendData: actual.extendData
|
|
1670
|
+
} : o;
|
|
1671
|
+
});
|
|
1672
|
+
for (const overlay of overlays2) {
|
|
1673
|
+
state.chart.removeOverlay({ id: overlay.id });
|
|
1674
|
+
}
|
|
1675
|
+
setUndoStack((prev) => [
|
|
1676
|
+
...prev,
|
|
1677
|
+
{
|
|
1678
|
+
type: "overlays_removed",
|
|
1679
|
+
data: { overlays: snapshotOverlays }
|
|
1680
|
+
}
|
|
1681
|
+
]);
|
|
1682
|
+
break;
|
|
1683
|
+
}
|
|
1684
|
+
case "indicator_toggled": {
|
|
1685
|
+
const { name, wasActive, isMain } = action.data;
|
|
1686
|
+
if (wasActive) {
|
|
1687
|
+
if (isMain) {
|
|
1688
|
+
state.chart.createIndicator(
|
|
1689
|
+
{ name, id: `main_${name}`, paneId: "candle_pane" },
|
|
1690
|
+
true,
|
|
1691
|
+
{ id: "candle_pane" }
|
|
1692
|
+
);
|
|
1693
|
+
dispatch({
|
|
1694
|
+
type: "SET_MAIN_INDICATORS",
|
|
1695
|
+
indicators: [...state.mainIndicators, name]
|
|
1696
|
+
});
|
|
1697
|
+
} else {
|
|
1698
|
+
state.chart.createIndicator({ name, id: `sub_${name}` });
|
|
1699
|
+
const newPaneId = state.chart.getIndicators({ id: `sub_${name}` })?.[0]?.paneId ?? "";
|
|
1700
|
+
dispatch({
|
|
1701
|
+
type: "SET_SUB_INDICATORS",
|
|
1702
|
+
indicators: {
|
|
1703
|
+
...state.subIndicators,
|
|
1704
|
+
[name]: newPaneId
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
} else {
|
|
1709
|
+
if (isMain) {
|
|
1710
|
+
state.chart.removeIndicator({ id: `main_${name}` });
|
|
1711
|
+
dispatch({
|
|
1712
|
+
type: "SET_MAIN_INDICATORS",
|
|
1713
|
+
indicators: state.mainIndicators.filter(
|
|
1714
|
+
(n) => n !== name
|
|
1715
|
+
)
|
|
1716
|
+
});
|
|
1717
|
+
} else {
|
|
1718
|
+
state.chart.removeIndicator({ id: `sub_${name}` });
|
|
1719
|
+
const newSub = { ...state.subIndicators };
|
|
1720
|
+
delete newSub[name];
|
|
1721
|
+
dispatch({
|
|
1722
|
+
type: "SET_SUB_INDICATORS",
|
|
1723
|
+
indicators: newSub
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
setUndoStack((prev) => [
|
|
1728
|
+
...prev,
|
|
1729
|
+
{
|
|
1730
|
+
type: "indicator_toggled",
|
|
1731
|
+
data: {
|
|
1732
|
+
name,
|
|
1733
|
+
wasActive: !wasActive,
|
|
1734
|
+
isMain
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
]);
|
|
1738
|
+
break;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
} finally {
|
|
1742
|
+
isProcessingRef.current = false;
|
|
1743
|
+
}
|
|
1744
|
+
}, [redoStack, state.chart, state.mainIndicators, state.subIndicators, dispatch]);
|
|
1745
|
+
react.useEffect(() => {
|
|
1746
|
+
const handleKeyDown = (e) => {
|
|
1747
|
+
const isCtrlOrMeta = e.ctrlKey || e.metaKey;
|
|
1748
|
+
if (!isCtrlOrMeta) return;
|
|
1749
|
+
if (e.key === "z" && !e.shiftKey) {
|
|
1750
|
+
e.preventDefault();
|
|
1751
|
+
undo();
|
|
1752
|
+
} else if (e.key === "y" || e.key === "z" && e.shiftKey) {
|
|
1753
|
+
e.preventDefault();
|
|
1754
|
+
redo();
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1758
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1759
|
+
}, [undo, redo]);
|
|
1760
|
+
return {
|
|
1761
|
+
canUndo,
|
|
1762
|
+
canRedo,
|
|
1763
|
+
undo,
|
|
1764
|
+
redo,
|
|
1765
|
+
pushAction,
|
|
1766
|
+
clear
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
var STORAGE_KEY_PREFIX = "klinecharts_layout:";
|
|
1770
|
+
var INDEX_KEY = "klinecharts_layout_index";
|
|
1771
|
+
var STATE_VERSION = "1.0";
|
|
1772
|
+
function generateId() {
|
|
1773
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
|
1774
|
+
/[xy]/g,
|
|
1775
|
+
(c) => {
|
|
1776
|
+
const r = Math.random() * 16 | 0;
|
|
1777
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
1778
|
+
}
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
function getLayoutIds() {
|
|
1782
|
+
try {
|
|
1783
|
+
const raw = localStorage.getItem(INDEX_KEY);
|
|
1784
|
+
return raw ? JSON.parse(raw) : [];
|
|
1785
|
+
} catch {
|
|
1786
|
+
return [];
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
function getEntry(id) {
|
|
1790
|
+
try {
|
|
1791
|
+
const raw = localStorage.getItem(STORAGE_KEY_PREFIX + id);
|
|
1792
|
+
return raw ? JSON.parse(raw) : null;
|
|
1793
|
+
} catch {
|
|
1794
|
+
return null;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
function useLayoutManager() {
|
|
1798
|
+
const { state, dispatch } = useKlinechartsUI();
|
|
1799
|
+
const [layouts, setLayouts] = react.useState([]);
|
|
1800
|
+
const [autoSaveEnabled, setAutoSaveEnabled] = react.useState(false);
|
|
1801
|
+
const autoSaveTimerRef = react.useRef(null);
|
|
1802
|
+
const autoSaveIdRef = react.useRef(null);
|
|
1803
|
+
const refreshLayouts = react.useCallback(() => {
|
|
1804
|
+
const entries = getLayoutIds().map((id) => getEntry(id)).filter((e) => e !== null);
|
|
1805
|
+
setLayouts(entries);
|
|
1806
|
+
}, []);
|
|
1807
|
+
react.useEffect(() => {
|
|
1808
|
+
refreshLayouts();
|
|
1809
|
+
}, [refreshLayouts]);
|
|
1810
|
+
const serializeState = react.useCallback(() => {
|
|
1811
|
+
const chart = state.chart;
|
|
1812
|
+
if (!chart) return null;
|
|
1813
|
+
const indicators2 = [];
|
|
1814
|
+
const indicatorMap = chart.getIndicatorByPaneId?.();
|
|
1815
|
+
if (indicatorMap) {
|
|
1816
|
+
indicatorMap.forEach((paneIndicators, paneId) => {
|
|
1817
|
+
paneIndicators.forEach((indicator) => {
|
|
1818
|
+
indicators2.push({
|
|
1819
|
+
paneId,
|
|
1820
|
+
name: indicator.name,
|
|
1821
|
+
calcParams: indicator.calcParams,
|
|
1822
|
+
visible: indicator.visible,
|
|
1823
|
+
...indicator.styles ? { styles: indicator.styles } : {}
|
|
1824
|
+
});
|
|
1825
|
+
});
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
const drawings = [];
|
|
1829
|
+
const allOverlays = chart.getOverlays({});
|
|
1830
|
+
if (allOverlays) {
|
|
1831
|
+
for (const overlay of allOverlays) {
|
|
1832
|
+
drawings.push({
|
|
1833
|
+
name: overlay.name,
|
|
1834
|
+
points: overlay.points,
|
|
1835
|
+
styles: overlay.styles,
|
|
1836
|
+
extendData: overlay.extendData
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
return {
|
|
1841
|
+
version: STATE_VERSION,
|
|
1842
|
+
meta: {
|
|
1843
|
+
symbol: state.symbol?.ticker ?? "",
|
|
1844
|
+
period: state.period?.label ?? "",
|
|
1845
|
+
timestamp: Date.now(),
|
|
1846
|
+
lastModified: Date.now()
|
|
1847
|
+
},
|
|
1848
|
+
indicators: indicators2,
|
|
1849
|
+
drawings
|
|
1850
|
+
};
|
|
1851
|
+
}, [state.chart, state.symbol, state.period]);
|
|
1852
|
+
const saveLayout = react.useCallback(
|
|
1853
|
+
(name) => {
|
|
1854
|
+
const chartState = serializeState();
|
|
1855
|
+
if (!chartState) return null;
|
|
1856
|
+
const id = generateId();
|
|
1857
|
+
const now = Date.now();
|
|
1858
|
+
const entry = {
|
|
1859
|
+
id,
|
|
1860
|
+
name: name.trim() || `Layout ${new Date(now).toLocaleString()}`,
|
|
1861
|
+
symbol: chartState.meta.symbol,
|
|
1862
|
+
period: chartState.meta.period,
|
|
1863
|
+
timestamp: now,
|
|
1864
|
+
lastModified: now,
|
|
1865
|
+
state: chartState
|
|
1866
|
+
};
|
|
1867
|
+
localStorage.setItem(
|
|
1868
|
+
STORAGE_KEY_PREFIX + id,
|
|
1869
|
+
JSON.stringify(entry)
|
|
1870
|
+
);
|
|
1871
|
+
const ids = getLayoutIds();
|
|
1872
|
+
if (!ids.includes(id)) {
|
|
1873
|
+
ids.push(id);
|
|
1874
|
+
localStorage.setItem(INDEX_KEY, JSON.stringify(ids));
|
|
1875
|
+
}
|
|
1876
|
+
refreshLayouts();
|
|
1877
|
+
return id;
|
|
1878
|
+
},
|
|
1879
|
+
[serializeState, refreshLayouts]
|
|
1880
|
+
);
|
|
1881
|
+
const loadLayout = react.useCallback(
|
|
1882
|
+
(id) => {
|
|
1883
|
+
const entry = getEntry(id);
|
|
1884
|
+
if (!entry || !state.chart) return false;
|
|
1885
|
+
const chartState = entry.state;
|
|
1886
|
+
if (chartState.version !== STATE_VERSION) return false;
|
|
1887
|
+
const chart = state.chart;
|
|
1888
|
+
chart.removeOverlay();
|
|
1889
|
+
const indicatorMap = chart.getIndicatorByPaneId?.();
|
|
1890
|
+
if (indicatorMap) {
|
|
1891
|
+
indicatorMap.forEach((_paneIndicators, paneId) => {
|
|
1892
|
+
_paneIndicators.forEach((_, indicatorName) => {
|
|
1893
|
+
chart.removeIndicator({ paneId, name: indicatorName });
|
|
1894
|
+
});
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
const newMainIndicators = [];
|
|
1898
|
+
const newSubIndicators = {};
|
|
1899
|
+
if (chartState.indicators) {
|
|
1900
|
+
for (const ind of chartState.indicators) {
|
|
1901
|
+
chart.createIndicator(
|
|
1902
|
+
{
|
|
1903
|
+
name: ind.name,
|
|
1904
|
+
calcParams: ind.calcParams,
|
|
1905
|
+
visible: ind.visible
|
|
1906
|
+
},
|
|
1907
|
+
ind.paneId !== "candle_pane",
|
|
1908
|
+
{ id: ind.paneId }
|
|
1909
|
+
);
|
|
1910
|
+
if (ind.styles) {
|
|
1911
|
+
chart.overrideIndicator({
|
|
1912
|
+
name: ind.name,
|
|
1913
|
+
paneId: ind.paneId,
|
|
1914
|
+
styles: ind.styles
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
if (ind.paneId === "candle_pane") {
|
|
1918
|
+
newMainIndicators.push(ind.name);
|
|
1919
|
+
} else {
|
|
1920
|
+
newSubIndicators[ind.name] = ind.paneId;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
dispatch({
|
|
1925
|
+
type: "SET_MAIN_INDICATORS",
|
|
1926
|
+
indicators: newMainIndicators
|
|
1927
|
+
});
|
|
1928
|
+
dispatch({
|
|
1929
|
+
type: "SET_SUB_INDICATORS",
|
|
1930
|
+
indicators: newSubIndicators
|
|
1931
|
+
});
|
|
1932
|
+
if (chartState.drawings) {
|
|
1933
|
+
for (const drawing of chartState.drawings) {
|
|
1934
|
+
chart.createOverlay({
|
|
1935
|
+
name: drawing.name,
|
|
1936
|
+
points: drawing.points,
|
|
1937
|
+
styles: drawing.styles,
|
|
1938
|
+
extendData: drawing.extendData
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
return true;
|
|
1943
|
+
},
|
|
1944
|
+
[state.chart, dispatch]
|
|
1945
|
+
);
|
|
1946
|
+
const deleteLayout = react.useCallback(
|
|
1947
|
+
(id) => {
|
|
1948
|
+
localStorage.removeItem(STORAGE_KEY_PREFIX + id);
|
|
1949
|
+
const ids = getLayoutIds().filter((i) => i !== id);
|
|
1950
|
+
localStorage.setItem(INDEX_KEY, JSON.stringify(ids));
|
|
1951
|
+
refreshLayouts();
|
|
1952
|
+
},
|
|
1953
|
+
[refreshLayouts]
|
|
1954
|
+
);
|
|
1955
|
+
const renameLayout = react.useCallback(
|
|
1956
|
+
(id, name) => {
|
|
1957
|
+
const entry = getEntry(id);
|
|
1958
|
+
if (!entry) return false;
|
|
1959
|
+
const updated = {
|
|
1960
|
+
...entry,
|
|
1961
|
+
name: name.trim(),
|
|
1962
|
+
lastModified: Date.now()
|
|
1963
|
+
};
|
|
1964
|
+
localStorage.setItem(
|
|
1965
|
+
STORAGE_KEY_PREFIX + id,
|
|
1966
|
+
JSON.stringify(updated)
|
|
1967
|
+
);
|
|
1968
|
+
refreshLayouts();
|
|
1969
|
+
return true;
|
|
1970
|
+
},
|
|
1971
|
+
[refreshLayouts]
|
|
1972
|
+
);
|
|
1973
|
+
react.useEffect(() => {
|
|
1974
|
+
if (!autoSaveEnabled || !state.chart) return;
|
|
1975
|
+
if (autoSaveTimerRef.current) {
|
|
1976
|
+
clearTimeout(autoSaveTimerRef.current);
|
|
1977
|
+
}
|
|
1978
|
+
autoSaveTimerRef.current = setTimeout(() => {
|
|
1979
|
+
const chartState = serializeState();
|
|
1980
|
+
if (!chartState) return;
|
|
1981
|
+
if (autoSaveIdRef.current) {
|
|
1982
|
+
const entry = getEntry(autoSaveIdRef.current);
|
|
1983
|
+
if (entry) {
|
|
1984
|
+
const updated = {
|
|
1985
|
+
...entry,
|
|
1986
|
+
lastModified: Date.now(),
|
|
1987
|
+
state: chartState
|
|
1988
|
+
};
|
|
1989
|
+
localStorage.setItem(
|
|
1990
|
+
STORAGE_KEY_PREFIX + autoSaveIdRef.current,
|
|
1991
|
+
JSON.stringify(updated)
|
|
1992
|
+
);
|
|
1993
|
+
refreshLayouts();
|
|
1994
|
+
}
|
|
1995
|
+
} else {
|
|
1996
|
+
const id = generateId();
|
|
1997
|
+
const now = Date.now();
|
|
1998
|
+
const entry = {
|
|
1999
|
+
id,
|
|
2000
|
+
name: "Auto-save",
|
|
2001
|
+
symbol: chartState.meta.symbol,
|
|
2002
|
+
period: chartState.meta.period,
|
|
2003
|
+
timestamp: now,
|
|
2004
|
+
lastModified: now,
|
|
2005
|
+
state: chartState
|
|
2006
|
+
};
|
|
2007
|
+
localStorage.setItem(
|
|
2008
|
+
STORAGE_KEY_PREFIX + id,
|
|
2009
|
+
JSON.stringify(entry)
|
|
2010
|
+
);
|
|
2011
|
+
const ids = getLayoutIds();
|
|
2012
|
+
ids.push(id);
|
|
2013
|
+
localStorage.setItem(INDEX_KEY, JSON.stringify(ids));
|
|
2014
|
+
autoSaveIdRef.current = id;
|
|
2015
|
+
refreshLayouts();
|
|
2016
|
+
}
|
|
2017
|
+
}, 5e3);
|
|
2018
|
+
return () => {
|
|
2019
|
+
if (autoSaveTimerRef.current) {
|
|
2020
|
+
clearTimeout(autoSaveTimerRef.current);
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
}, [
|
|
2024
|
+
autoSaveEnabled,
|
|
2025
|
+
state.chart,
|
|
2026
|
+
state.mainIndicators,
|
|
2027
|
+
state.subIndicators,
|
|
2028
|
+
serializeState,
|
|
2029
|
+
refreshLayouts
|
|
2030
|
+
]);
|
|
2031
|
+
return {
|
|
2032
|
+
layouts,
|
|
2033
|
+
saveLayout,
|
|
2034
|
+
loadLayout,
|
|
2035
|
+
deleteLayout,
|
|
2036
|
+
renameLayout,
|
|
2037
|
+
refreshLayouts,
|
|
2038
|
+
autoSaveEnabled,
|
|
2039
|
+
setAutoSaveEnabled
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
var DEFAULT_SCRIPT = `// Custom Indicator \u2014 Pine Script Style
|
|
2043
|
+
// Available: TA, dataList, params
|
|
2044
|
+
|
|
2045
|
+
const period = params[0] ?? 14
|
|
2046
|
+
const closes = dataList.map(d => d.close)
|
|
2047
|
+
const highs = dataList.map(d => d.high)
|
|
2048
|
+
const lows = dataList.map(d => d.low)
|
|
2049
|
+
|
|
2050
|
+
// Example: RSI + Bollinger Bands
|
|
2051
|
+
const rsi = TA.rsi(closes, period)
|
|
2052
|
+
const boll = TA.bollinger(closes, period, 2)
|
|
2053
|
+
|
|
2054
|
+
// Return one object per candle \u2014 each key = one line on chart
|
|
2055
|
+
return rsi.map((v, i) => ({
|
|
2056
|
+
rsi: v,
|
|
2057
|
+
upper: boll.upper[i],
|
|
2058
|
+
mid: boll.mid[i],
|
|
2059
|
+
lower: boll.lower[i],
|
|
2060
|
+
}))`;
|
|
2061
|
+
var SERIES_COLORS = [
|
|
2062
|
+
"#2962FF",
|
|
2063
|
+
"#FF6D00",
|
|
2064
|
+
"#00BCD4",
|
|
2065
|
+
"#E91E63",
|
|
2066
|
+
"#76FF03",
|
|
2067
|
+
"#FFD600"
|
|
2068
|
+
];
|
|
2069
|
+
var SHADOW_KEYS = [
|
|
2070
|
+
"fetch",
|
|
2071
|
+
"XMLHttpRequest",
|
|
2072
|
+
"WebSocket",
|
|
2073
|
+
"Worker",
|
|
2074
|
+
"SharedWorker",
|
|
2075
|
+
"importScripts",
|
|
2076
|
+
"self",
|
|
2077
|
+
"caches",
|
|
2078
|
+
"indexedDB"
|
|
2079
|
+
];
|
|
2080
|
+
var scriptCounter = 0;
|
|
2081
|
+
function useScriptEditor() {
|
|
2082
|
+
const { state } = useKlinechartsUI();
|
|
2083
|
+
const [code, setCode] = react.useState(DEFAULT_SCRIPT);
|
|
2084
|
+
const [scriptName, setScriptName] = react.useState("");
|
|
2085
|
+
const [params, setParams] = react.useState("14");
|
|
2086
|
+
const [placement, setPlacement] = react.useState("sub");
|
|
2087
|
+
const [error, setError] = react.useState("");
|
|
2088
|
+
const [status, setStatus] = react.useState("");
|
|
2089
|
+
const [isRunning, setIsRunning] = react.useState(false);
|
|
2090
|
+
const activeIdRef = react.useRef(null);
|
|
2091
|
+
const activeNameRef = react.useRef(null);
|
|
2092
|
+
const hasActiveScript = activeNameRef.current !== null;
|
|
2093
|
+
const runScript = react.useCallback(() => {
|
|
2094
|
+
const chart = state.chart;
|
|
2095
|
+
if (!chart) {
|
|
2096
|
+
setError("Chart not initialized");
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
setError("");
|
|
2100
|
+
setStatus("Running...");
|
|
2101
|
+
setIsRunning(true);
|
|
2102
|
+
try {
|
|
2103
|
+
const parsedParams = params.split(",").map((s) => parseFloat(s.trim())).filter((n) => !isNaN(n));
|
|
2104
|
+
const dataList = chart.getDataList?.() ?? [];
|
|
2105
|
+
const allArgs = [
|
|
2106
|
+
...SHADOW_KEYS,
|
|
2107
|
+
"TA",
|
|
2108
|
+
"dataList",
|
|
2109
|
+
"params",
|
|
2110
|
+
`"use strict";
|
|
2111
|
+
${code}`
|
|
2112
|
+
];
|
|
2113
|
+
const fn = new Function(...allArgs);
|
|
2114
|
+
const shadowValues = SHADOW_KEYS.map(() => void 0);
|
|
2115
|
+
const result = fn(...shadowValues, chunk47JXSAIT_cjs.TA_default, dataList, parsedParams);
|
|
2116
|
+
if (!Array.isArray(result)) {
|
|
2117
|
+
throw new Error("Script must return an Array.");
|
|
2118
|
+
}
|
|
2119
|
+
const sample = result.find((v) => v !== null && v !== void 0);
|
|
2120
|
+
const seriesKeys = sample ? Object.keys(sample) : ["value"];
|
|
2121
|
+
if (seriesKeys.length === 0) {
|
|
2122
|
+
throw new Error("Returned objects have no keys.");
|
|
2123
|
+
}
|
|
2124
|
+
scriptCounter++;
|
|
2125
|
+
const indicatorName = `_custom_script_${scriptCounter}`;
|
|
2126
|
+
const figures = seriesKeys.map((key, i) => ({
|
|
2127
|
+
key,
|
|
2128
|
+
title: `${key}: `,
|
|
2129
|
+
type: "line",
|
|
2130
|
+
styles: () => ({ color: SERIES_COLORS[i % SERIES_COLORS.length] })
|
|
2131
|
+
}));
|
|
2132
|
+
reactKlinecharts.registerIndicator({
|
|
2133
|
+
name: indicatorName,
|
|
2134
|
+
shortName: (scriptName.trim() || `Script #${scriptCounter}`) + (placement === "main" ? " (Main)" : " (Sub)"),
|
|
2135
|
+
calcParams: parsedParams,
|
|
2136
|
+
figures,
|
|
2137
|
+
calc: () => result,
|
|
2138
|
+
extendData: {
|
|
2139
|
+
isCustomScript: true,
|
|
2140
|
+
code,
|
|
2141
|
+
placement
|
|
2142
|
+
}
|
|
2143
|
+
});
|
|
2144
|
+
if (activeNameRef.current) {
|
|
2145
|
+
try {
|
|
2146
|
+
chart.removeIndicator({
|
|
2147
|
+
id: activeIdRef.current === "candle_pane" ? "candle_pane" : activeIdRef.current,
|
|
2148
|
+
name: activeNameRef.current
|
|
2149
|
+
});
|
|
2150
|
+
} catch {
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
let paneId = null;
|
|
2154
|
+
if (placement === "main") {
|
|
2155
|
+
paneId = chart.createIndicator(
|
|
2156
|
+
{ name: indicatorName },
|
|
2157
|
+
true,
|
|
2158
|
+
{ id: "candle_pane" }
|
|
2159
|
+
);
|
|
2160
|
+
} else {
|
|
2161
|
+
paneId = chart.createIndicator({ name: indicatorName });
|
|
2162
|
+
}
|
|
2163
|
+
activeIdRef.current = typeof paneId === "string" ? paneId : null;
|
|
2164
|
+
activeNameRef.current = indicatorName;
|
|
2165
|
+
const title = scriptName.trim() || `Script #${scriptCounter}`;
|
|
2166
|
+
setStatus(
|
|
2167
|
+
`${title} applied to ${placement === "main" ? "Main Chart" : "Sub Pane"} \u2014 ${seriesKeys.length} series: ${seriesKeys.join(", ")}`
|
|
2168
|
+
);
|
|
2169
|
+
} catch (e) {
|
|
2170
|
+
setError(e?.message ?? String(e));
|
|
2171
|
+
} finally {
|
|
2172
|
+
setIsRunning(false);
|
|
2173
|
+
}
|
|
2174
|
+
}, [state.chart, code, scriptName, params, placement]);
|
|
2175
|
+
const removeScript = react.useCallback(() => {
|
|
2176
|
+
const chart = state.chart;
|
|
2177
|
+
if (chart && activeNameRef.current && activeIdRef.current) {
|
|
2178
|
+
try {
|
|
2179
|
+
chart.removeIndicator({
|
|
2180
|
+
id: activeIdRef.current,
|
|
2181
|
+
name: activeNameRef.current
|
|
2182
|
+
});
|
|
2183
|
+
} catch {
|
|
2184
|
+
}
|
|
2185
|
+
activeIdRef.current = null;
|
|
2186
|
+
activeNameRef.current = null;
|
|
2187
|
+
setStatus("Indicator removed.");
|
|
2188
|
+
}
|
|
2189
|
+
}, [state.chart]);
|
|
2190
|
+
const resetCode = react.useCallback(() => {
|
|
2191
|
+
setCode(DEFAULT_SCRIPT);
|
|
2192
|
+
setError("");
|
|
2193
|
+
setStatus("");
|
|
2194
|
+
}, []);
|
|
2195
|
+
const exportScript = react.useCallback(() => {
|
|
2196
|
+
const blob = new Blob([code], {
|
|
2197
|
+
type: "text/javascript;charset=utf-8"
|
|
2198
|
+
});
|
|
2199
|
+
const url = URL.createObjectURL(blob);
|
|
2200
|
+
const a = document.createElement("a");
|
|
2201
|
+
a.href = url;
|
|
2202
|
+
a.download = `${scriptName.trim() || "custom_indicator"}.js`;
|
|
2203
|
+
document.body.appendChild(a);
|
|
2204
|
+
a.click();
|
|
2205
|
+
document.body.removeChild(a);
|
|
2206
|
+
URL.revokeObjectURL(url);
|
|
2207
|
+
setStatus("Script exported.");
|
|
2208
|
+
}, [code, scriptName]);
|
|
2209
|
+
const importScript = react.useCallback(
|
|
2210
|
+
(file) => {
|
|
2211
|
+
const reader = new FileReader();
|
|
2212
|
+
reader.onload = (e) => {
|
|
2213
|
+
const content = e.target?.result;
|
|
2214
|
+
if (content) {
|
|
2215
|
+
setCode(content);
|
|
2216
|
+
if (!scriptName) {
|
|
2217
|
+
setScriptName(file.name.replace(/\.(js|ts|txt)$/i, ""));
|
|
2218
|
+
}
|
|
2219
|
+
setError("");
|
|
2220
|
+
setStatus(`Loaded ${file.name}`);
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
reader.readAsText(file);
|
|
2224
|
+
},
|
|
2225
|
+
[scriptName]
|
|
2226
|
+
);
|
|
2227
|
+
return {
|
|
2228
|
+
code,
|
|
2229
|
+
setCode,
|
|
2230
|
+
scriptName,
|
|
2231
|
+
setScriptName,
|
|
2232
|
+
params,
|
|
2233
|
+
setParams,
|
|
2234
|
+
placement,
|
|
2235
|
+
setPlacement,
|
|
2236
|
+
error,
|
|
2237
|
+
status,
|
|
2238
|
+
isRunning,
|
|
2239
|
+
hasActiveScript,
|
|
2240
|
+
runScript,
|
|
2241
|
+
removeScript,
|
|
2242
|
+
resetCode,
|
|
2243
|
+
exportScript,
|
|
2244
|
+
importScript,
|
|
2245
|
+
defaultScript: DEFAULT_SCRIPT
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
1324
2248
|
|
|
1325
2249
|
// src/utils/createDataLoader.ts
|
|
1326
2250
|
function createDataLoader(datafeed, dispatch) {
|
|
@@ -1386,85 +2310,173 @@ function createDataLoader(datafeed, dispatch) {
|
|
|
1386
2310
|
};
|
|
1387
2311
|
}
|
|
1388
2312
|
|
|
2313
|
+
Object.defineProperty(exports, "TA", {
|
|
2314
|
+
enumerable: true,
|
|
2315
|
+
get: function () { return chunk47JXSAIT_cjs.TA_default; }
|
|
2316
|
+
});
|
|
1389
2317
|
Object.defineProperty(exports, "abcd", {
|
|
1390
2318
|
enumerable: true,
|
|
1391
|
-
get: function () { return
|
|
2319
|
+
get: function () { return chunk47JXSAIT_cjs.abcd_default; }
|
|
1392
2320
|
});
|
|
1393
2321
|
Object.defineProperty(exports, "anyWaves", {
|
|
1394
2322
|
enumerable: true,
|
|
1395
|
-
get: function () { return
|
|
2323
|
+
get: function () { return chunk47JXSAIT_cjs.anyWaves_default; }
|
|
1396
2324
|
});
|
|
1397
2325
|
Object.defineProperty(exports, "arrow", {
|
|
1398
2326
|
enumerable: true,
|
|
1399
|
-
get: function () { return
|
|
2327
|
+
get: function () { return chunk47JXSAIT_cjs.arrow_default; }
|
|
2328
|
+
});
|
|
2329
|
+
Object.defineProperty(exports, "bollTv", {
|
|
2330
|
+
enumerable: true,
|
|
2331
|
+
get: function () { return chunk47JXSAIT_cjs.bollTv_default; }
|
|
2332
|
+
});
|
|
2333
|
+
Object.defineProperty(exports, "brush", {
|
|
2334
|
+
enumerable: true,
|
|
2335
|
+
get: function () { return chunk47JXSAIT_cjs.brush_default; }
|
|
2336
|
+
});
|
|
2337
|
+
Object.defineProperty(exports, "cci", {
|
|
2338
|
+
enumerable: true,
|
|
2339
|
+
get: function () { return chunk47JXSAIT_cjs.cci_default; }
|
|
1400
2340
|
});
|
|
1401
2341
|
Object.defineProperty(exports, "circle", {
|
|
1402
2342
|
enumerable: true,
|
|
1403
|
-
get: function () { return
|
|
2343
|
+
get: function () { return chunk47JXSAIT_cjs.circle_default; }
|
|
1404
2344
|
});
|
|
1405
2345
|
Object.defineProperty(exports, "eightWaves", {
|
|
1406
2346
|
enumerable: true,
|
|
1407
|
-
get: function () { return
|
|
2347
|
+
get: function () { return chunk47JXSAIT_cjs.eightWaves_default; }
|
|
2348
|
+
});
|
|
2349
|
+
Object.defineProperty(exports, "elliottWave", {
|
|
2350
|
+
enumerable: true,
|
|
2351
|
+
get: function () { return chunk47JXSAIT_cjs.elliottWave_default; }
|
|
2352
|
+
});
|
|
2353
|
+
Object.defineProperty(exports, "fibRetracement", {
|
|
2354
|
+
enumerable: true,
|
|
2355
|
+
get: function () { return chunk47JXSAIT_cjs.fibRetracement_default; }
|
|
1408
2356
|
});
|
|
1409
2357
|
Object.defineProperty(exports, "fibonacciCircle", {
|
|
1410
2358
|
enumerable: true,
|
|
1411
|
-
get: function () { return
|
|
2359
|
+
get: function () { return chunk47JXSAIT_cjs.fibonacciCircle_default; }
|
|
1412
2360
|
});
|
|
1413
2361
|
Object.defineProperty(exports, "fibonacciExtension", {
|
|
1414
2362
|
enumerable: true,
|
|
1415
|
-
get: function () { return
|
|
2363
|
+
get: function () { return chunk47JXSAIT_cjs.fibonacciExtension_default; }
|
|
1416
2364
|
});
|
|
1417
2365
|
Object.defineProperty(exports, "fibonacciSegment", {
|
|
1418
2366
|
enumerable: true,
|
|
1419
|
-
get: function () { return
|
|
2367
|
+
get: function () { return chunk47JXSAIT_cjs.fibonacciSegment_default; }
|
|
1420
2368
|
});
|
|
1421
2369
|
Object.defineProperty(exports, "fibonacciSpeedResistanceFan", {
|
|
1422
2370
|
enumerable: true,
|
|
1423
|
-
get: function () { return
|
|
2371
|
+
get: function () { return chunk47JXSAIT_cjs.fibonacciSpeedResistanceFan_default; }
|
|
1424
2372
|
});
|
|
1425
2373
|
Object.defineProperty(exports, "fibonacciSpiral", {
|
|
1426
2374
|
enumerable: true,
|
|
1427
|
-
get: function () { return
|
|
2375
|
+
get: function () { return chunk47JXSAIT_cjs.fibonacciSpiral_default; }
|
|
1428
2376
|
});
|
|
1429
2377
|
Object.defineProperty(exports, "fiveWaves", {
|
|
1430
2378
|
enumerable: true,
|
|
1431
|
-
get: function () { return
|
|
2379
|
+
get: function () { return chunk47JXSAIT_cjs.fiveWaves_default; }
|
|
1432
2380
|
});
|
|
1433
2381
|
Object.defineProperty(exports, "gannBox", {
|
|
1434
2382
|
enumerable: true,
|
|
1435
|
-
get: function () { return
|
|
2383
|
+
get: function () { return chunk47JXSAIT_cjs.gannBox_default; }
|
|
2384
|
+
});
|
|
2385
|
+
Object.defineProperty(exports, "gannFan", {
|
|
2386
|
+
enumerable: true,
|
|
2387
|
+
get: function () { return chunk47JXSAIT_cjs.gannFan_default; }
|
|
2388
|
+
});
|
|
2389
|
+
Object.defineProperty(exports, "hma", {
|
|
2390
|
+
enumerable: true,
|
|
2391
|
+
get: function () { return chunk47JXSAIT_cjs.hma_default; }
|
|
2392
|
+
});
|
|
2393
|
+
Object.defineProperty(exports, "ichimoku", {
|
|
2394
|
+
enumerable: true,
|
|
2395
|
+
get: function () { return chunk47JXSAIT_cjs.ichimoku_default; }
|
|
2396
|
+
});
|
|
2397
|
+
Object.defineProperty(exports, "indicators", {
|
|
2398
|
+
enumerable: true,
|
|
2399
|
+
get: function () { return chunk47JXSAIT_cjs.indicators; }
|
|
2400
|
+
});
|
|
2401
|
+
Object.defineProperty(exports, "longPosition", {
|
|
2402
|
+
enumerable: true,
|
|
2403
|
+
get: function () { return chunk47JXSAIT_cjs.longPosition_default; }
|
|
2404
|
+
});
|
|
2405
|
+
Object.defineProperty(exports, "maRibbon", {
|
|
2406
|
+
enumerable: true,
|
|
2407
|
+
get: function () { return chunk47JXSAIT_cjs.maRibbon_default; }
|
|
2408
|
+
});
|
|
2409
|
+
Object.defineProperty(exports, "macdTv", {
|
|
2410
|
+
enumerable: true,
|
|
2411
|
+
get: function () { return chunk47JXSAIT_cjs.macdTv_default; }
|
|
2412
|
+
});
|
|
2413
|
+
Object.defineProperty(exports, "measure", {
|
|
2414
|
+
enumerable: true,
|
|
2415
|
+
get: function () { return chunk47JXSAIT_cjs.measure_default; }
|
|
1436
2416
|
});
|
|
1437
2417
|
Object.defineProperty(exports, "orderLine", {
|
|
1438
2418
|
enumerable: true,
|
|
1439
|
-
get: function () { return
|
|
2419
|
+
get: function () { return chunk47JXSAIT_cjs.orderLine_default; }
|
|
1440
2420
|
});
|
|
1441
2421
|
Object.defineProperty(exports, "overlays", {
|
|
1442
2422
|
enumerable: true,
|
|
1443
|
-
get: function () { return
|
|
2423
|
+
get: function () { return chunk47JXSAIT_cjs.overlays; }
|
|
2424
|
+
});
|
|
2425
|
+
Object.defineProperty(exports, "parallelChannel", {
|
|
2426
|
+
enumerable: true,
|
|
2427
|
+
get: function () { return chunk47JXSAIT_cjs.parallelChannel_default; }
|
|
1444
2428
|
});
|
|
1445
2429
|
Object.defineProperty(exports, "parallelogram", {
|
|
1446
2430
|
enumerable: true,
|
|
1447
|
-
get: function () { return
|
|
2431
|
+
get: function () { return chunk47JXSAIT_cjs.parallelogram_default; }
|
|
2432
|
+
});
|
|
2433
|
+
Object.defineProperty(exports, "pivotPoints", {
|
|
2434
|
+
enumerable: true,
|
|
2435
|
+
get: function () { return chunk47JXSAIT_cjs.pivotPoints_default; }
|
|
2436
|
+
});
|
|
2437
|
+
Object.defineProperty(exports, "ray", {
|
|
2438
|
+
enumerable: true,
|
|
2439
|
+
get: function () { return chunk47JXSAIT_cjs.ray_default; }
|
|
1448
2440
|
});
|
|
1449
2441
|
Object.defineProperty(exports, "rect", {
|
|
1450
2442
|
enumerable: true,
|
|
1451
|
-
get: function () { return
|
|
2443
|
+
get: function () { return chunk47JXSAIT_cjs.rect_default; }
|
|
1452
2444
|
});
|
|
1453
2445
|
Object.defineProperty(exports, "registerExtensions", {
|
|
1454
2446
|
enumerable: true,
|
|
1455
|
-
get: function () { return
|
|
2447
|
+
get: function () { return chunk47JXSAIT_cjs.registerExtensions; }
|
|
2448
|
+
});
|
|
2449
|
+
Object.defineProperty(exports, "rsiTv", {
|
|
2450
|
+
enumerable: true,
|
|
2451
|
+
get: function () { return chunk47JXSAIT_cjs.rsiTv_default; }
|
|
2452
|
+
});
|
|
2453
|
+
Object.defineProperty(exports, "shortPosition", {
|
|
2454
|
+
enumerable: true,
|
|
2455
|
+
get: function () { return chunk47JXSAIT_cjs.shortPosition_default; }
|
|
2456
|
+
});
|
|
2457
|
+
Object.defineProperty(exports, "stochastic", {
|
|
2458
|
+
enumerable: true,
|
|
2459
|
+
get: function () { return chunk47JXSAIT_cjs.stochastic_default; }
|
|
2460
|
+
});
|
|
2461
|
+
Object.defineProperty(exports, "superTrend", {
|
|
2462
|
+
enumerable: true,
|
|
2463
|
+
get: function () { return chunk47JXSAIT_cjs.superTrend_default; }
|
|
1456
2464
|
});
|
|
1457
2465
|
Object.defineProperty(exports, "threeWaves", {
|
|
1458
2466
|
enumerable: true,
|
|
1459
|
-
get: function () { return
|
|
2467
|
+
get: function () { return chunk47JXSAIT_cjs.threeWaves_default; }
|
|
1460
2468
|
});
|
|
1461
2469
|
Object.defineProperty(exports, "triangle", {
|
|
1462
2470
|
enumerable: true,
|
|
1463
|
-
get: function () { return
|
|
2471
|
+
get: function () { return chunk47JXSAIT_cjs.triangle_default; }
|
|
2472
|
+
});
|
|
2473
|
+
Object.defineProperty(exports, "vwap", {
|
|
2474
|
+
enumerable: true,
|
|
2475
|
+
get: function () { return chunk47JXSAIT_cjs.vwap_default; }
|
|
1464
2476
|
});
|
|
1465
2477
|
Object.defineProperty(exports, "xabcd", {
|
|
1466
2478
|
enumerable: true,
|
|
1467
|
-
get: function () { return
|
|
2479
|
+
get: function () { return chunk47JXSAIT_cjs.xabcd_default; }
|
|
1468
2480
|
});
|
|
1469
2481
|
exports.CANDLE_TYPES = CANDLE_TYPES;
|
|
1470
2482
|
exports.COMPARE_RULES = COMPARE_RULES;
|
|
@@ -1488,10 +2500,13 @@ exports.useKlinechartsUI = useKlinechartsUI;
|
|
|
1488
2500
|
exports.useKlinechartsUILoading = useKlinechartsUILoading;
|
|
1489
2501
|
exports.useKlinechartsUISettings = useKlinechartsUISettings;
|
|
1490
2502
|
exports.useKlinechartsUITheme = useKlinechartsUITheme;
|
|
2503
|
+
exports.useLayoutManager = useLayoutManager;
|
|
1491
2504
|
exports.useOrderLines = useOrderLines;
|
|
1492
2505
|
exports.usePeriods = usePeriods;
|
|
1493
2506
|
exports.useScreenshot = useScreenshot;
|
|
2507
|
+
exports.useScriptEditor = useScriptEditor;
|
|
1494
2508
|
exports.useSymbolSearch = useSymbolSearch;
|
|
1495
2509
|
exports.useTimezone = useTimezone;
|
|
2510
|
+
exports.useUndoRedo = useUndoRedo;
|
|
1496
2511
|
//# sourceMappingURL=index.cjs.map
|
|
1497
2512
|
//# sourceMappingURL=index.cjs.map
|