react-klinecharts-ui 0.1.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.js ADDED
@@ -0,0 +1,1390 @@
1
+ import { registerExtensions } from './chunk-PUULIBFN.js';
2
+ export { abcd_default as abcd, anyWaves_default as anyWaves, arrow_default as arrow, circle_default as circle, eightWaves_default as eightWaves, fibonacciCircle_default as fibonacciCircle, fibonacciExtension_default as fibonacciExtension, fibonacciSegment_default as fibonacciSegment, fibonacciSpeedResistanceFan_default as fibonacciSpeedResistanceFan, fibonacciSpiral_default as fibonacciSpiral, fiveWaves_default as fiveWaves, gannBox_default as gannBox, orderLine_default as orderLine, overlays, parallelogram_default as parallelogram, rect_default as rect, registerExtensions, threeWaves_default as threeWaves, triangle_default as triangle, xabcd_default as xabcd } from './chunk-PUULIBFN.js';
3
+ import { createContext, useContext, useReducer, useRef, useCallback, useEffect, useMemo, useState } from 'react';
4
+ import { registerOverlay } from 'react-klinecharts';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ var KlinechartsUIStateContext = createContext(null);
8
+ var KlinechartsUIDispatchContext = createContext(null);
9
+ function useKlinechartsUIDispatch() {
10
+ const ctx = useContext(KlinechartsUIDispatchContext);
11
+ if (!ctx) {
12
+ throw new Error(
13
+ "useKlinechartsUI must be used within a <KlinechartsUIProvider>"
14
+ );
15
+ }
16
+ return ctx;
17
+ }
18
+ function useKlinechartsUI() {
19
+ const state = useContext(KlinechartsUIStateContext);
20
+ const dispatch = useContext(KlinechartsUIDispatchContext);
21
+ if (!state || !dispatch) {
22
+ throw new Error(
23
+ "useKlinechartsUI must be used within a <KlinechartsUIProvider>"
24
+ );
25
+ }
26
+ return { state, ...dispatch };
27
+ }
28
+
29
+ // src/data/periods.ts
30
+ var DEFAULT_PERIODS = [
31
+ { span: 1, type: "minute", label: "1m" },
32
+ { span: 5, type: "minute", label: "5m" },
33
+ { span: 15, type: "minute", label: "15m" },
34
+ { span: 1, type: "hour", label: "1H" },
35
+ { span: 2, type: "hour", label: "2H" },
36
+ { span: 4, type: "hour", label: "4H" },
37
+ { span: 1, type: "day", label: "D" },
38
+ { span: 1, type: "week", label: "W" },
39
+ { span: 1, type: "month", label: "M" },
40
+ { span: 1, type: "year", label: "Y" }
41
+ ];
42
+ function reducer(state, action) {
43
+ switch (action.type) {
44
+ case "SET_CHART":
45
+ return { ...state, chart: action.chart };
46
+ case "SET_SYMBOL":
47
+ return { ...state, symbol: action.symbol };
48
+ case "SET_PERIOD":
49
+ return { ...state, period: action.period };
50
+ case "SET_THEME":
51
+ return { ...state, theme: action.theme };
52
+ case "SET_TIMEZONE":
53
+ return { ...state, timezone: action.timezone };
54
+ case "SET_LOADING":
55
+ return { ...state, isLoading: action.isLoading };
56
+ case "SET_MAIN_INDICATORS":
57
+ return { ...state, mainIndicators: action.indicators };
58
+ case "SET_SUB_INDICATORS":
59
+ return { ...state, subIndicators: action.indicators };
60
+ case "SET_STYLES":
61
+ return { ...state, styles: action.styles };
62
+ case "SET_LOCALE":
63
+ return { ...state, locale: action.locale };
64
+ case "SET_SCREENSHOT_URL":
65
+ return { ...state, screenshotUrl: action.url };
66
+ default:
67
+ return state;
68
+ }
69
+ }
70
+ function KlinechartsUIProvider({
71
+ datafeed,
72
+ defaultSymbol,
73
+ defaultPeriod,
74
+ defaultTheme = "light",
75
+ defaultTimezone = "Asia/Shanghai",
76
+ defaultMainIndicators = ["MA"],
77
+ defaultSubIndicators = ["VOL"],
78
+ defaultLocale = "en-US",
79
+ periods,
80
+ styles,
81
+ registerExtensions: shouldRegister = true,
82
+ overlays: extraOverlays,
83
+ children,
84
+ onStateChange,
85
+ onSymbolChange,
86
+ onPeriodChange,
87
+ onThemeChange,
88
+ onTimezoneChange,
89
+ onMainIndicatorsChange,
90
+ onSubIndicatorsChange,
91
+ onSettingsChange
92
+ }) {
93
+ const [state, dispatch] = useReducer(
94
+ reducer,
95
+ {
96
+ datafeed,
97
+ defaultSymbol,
98
+ defaultPeriod,
99
+ defaultTheme,
100
+ defaultTimezone,
101
+ defaultMainIndicators,
102
+ defaultSubIndicators,
103
+ defaultLocale,
104
+ periods,
105
+ styles
106
+ },
107
+ (opts) => ({
108
+ chart: null,
109
+ datafeed: opts.datafeed,
110
+ symbol: opts.defaultSymbol ?? null,
111
+ period: opts.defaultPeriod ?? (opts.periods ?? DEFAULT_PERIODS)[0],
112
+ theme: opts.defaultTheme ?? "light",
113
+ timezone: opts.defaultTimezone ?? "Asia/Shanghai",
114
+ isLoading: false,
115
+ locale: opts.defaultLocale ?? "en-US",
116
+ periods: opts.periods ?? DEFAULT_PERIODS,
117
+ mainIndicators: opts.defaultMainIndicators ?? ["MA"],
118
+ subIndicators: (opts.defaultSubIndicators ?? ["VOL"]).reduce(
119
+ (acc, name) => ({ ...acc, [name]: "" }),
120
+ {}
121
+ ),
122
+ styles: opts.styles,
123
+ screenshotUrl: null
124
+ })
125
+ );
126
+ const fullscreenContainerRef = useRef(null);
127
+ const stateRef = useRef(state);
128
+ stateRef.current = state;
129
+ const callbacksRef = useRef({
130
+ onStateChange,
131
+ onSymbolChange,
132
+ onPeriodChange,
133
+ onThemeChange,
134
+ onTimezoneChange,
135
+ onMainIndicatorsChange,
136
+ onSubIndicatorsChange
137
+ });
138
+ callbacksRef.current = {
139
+ onStateChange,
140
+ onSymbolChange,
141
+ onPeriodChange,
142
+ onThemeChange,
143
+ onTimezoneChange,
144
+ onMainIndicatorsChange,
145
+ onSubIndicatorsChange
146
+ };
147
+ const enhancedDispatch = useCallback((action) => {
148
+ const prevState = stateRef.current;
149
+ const newState = reducer(prevState, action);
150
+ stateRef.current = newState;
151
+ dispatch(action);
152
+ const cbs = callbacksRef.current;
153
+ cbs.onStateChange?.(action, newState, prevState);
154
+ switch (action.type) {
155
+ case "SET_SYMBOL":
156
+ cbs.onSymbolChange?.(action.symbol);
157
+ break;
158
+ case "SET_PERIOD":
159
+ cbs.onPeriodChange?.(action.period);
160
+ break;
161
+ case "SET_THEME":
162
+ cbs.onThemeChange?.(action.theme);
163
+ break;
164
+ case "SET_TIMEZONE":
165
+ cbs.onTimezoneChange?.(action.timezone);
166
+ break;
167
+ case "SET_MAIN_INDICATORS":
168
+ cbs.onMainIndicatorsChange?.(action.indicators);
169
+ break;
170
+ case "SET_SUB_INDICATORS":
171
+ cbs.onSubIndicatorsChange?.(action.indicators);
172
+ break;
173
+ }
174
+ }, []);
175
+ const extraOverlaysRef = useRef(extraOverlays);
176
+ useEffect(() => {
177
+ if (shouldRegister) {
178
+ registerExtensions();
179
+ }
180
+ extraOverlaysRef.current?.forEach((overlay) => registerOverlay(overlay));
181
+ }, [shouldRegister]);
182
+ useEffect(() => {
183
+ if (state.chart && state.styles) {
184
+ state.chart.setStyles(state.styles);
185
+ }
186
+ }, [state.chart, state.styles]);
187
+ const dispatchValue = useMemo(
188
+ () => ({
189
+ dispatch: enhancedDispatch,
190
+ datafeed,
191
+ onSettingsChange,
192
+ fullscreenContainerRef
193
+ }),
194
+ [enhancedDispatch, datafeed, onSettingsChange]
195
+ );
196
+ return /* @__PURE__ */ jsx(KlinechartsUIStateContext.Provider, { value: state, children: /* @__PURE__ */ jsx(KlinechartsUIDispatchContext.Provider, { value: dispatchValue, children }) });
197
+ }
198
+ function useKlinechartsUITheme() {
199
+ const { state, dispatch } = useKlinechartsUI();
200
+ const setTheme = useCallback(
201
+ (theme) => {
202
+ state.chart?.setStyles(theme);
203
+ dispatch({ type: "SET_THEME", theme });
204
+ },
205
+ [state.chart, dispatch]
206
+ );
207
+ const toggleTheme = useCallback(() => {
208
+ const newTheme = state.theme === "light" ? "dark" : "light";
209
+ setTheme(newTheme);
210
+ }, [state.theme, setTheme]);
211
+ return {
212
+ theme: state.theme,
213
+ setTheme,
214
+ toggleTheme
215
+ };
216
+ }
217
+
218
+ // src/hooks/useKlinechartsUILoading.ts
219
+ function useKlinechartsUILoading() {
220
+ const { state } = useKlinechartsUI();
221
+ return { isLoading: state.isLoading };
222
+ }
223
+ function usePeriods() {
224
+ const { state, dispatch } = useKlinechartsUI();
225
+ const setPeriod = useCallback(
226
+ (period) => {
227
+ dispatch({ type: "SET_PERIOD", period });
228
+ },
229
+ [dispatch]
230
+ );
231
+ return {
232
+ periods: state.periods,
233
+ activePeriod: state.period,
234
+ setPeriod
235
+ };
236
+ }
237
+
238
+ // src/data/timezones.ts
239
+ var TIMEZONES = [
240
+ { key: "Etc/UTC", localeKey: "utc" },
241
+ { key: "Pacific/Honolulu", localeKey: "honolulu" },
242
+ { key: "America/Juneau", localeKey: "juneau" },
243
+ { key: "America/Los_Angeles", localeKey: "los_angeles" },
244
+ { key: "America/Chicago", localeKey: "chicago" },
245
+ { key: "America/Toronto", localeKey: "toronto" },
246
+ { key: "America/Sao_Paulo", localeKey: "sao_paulo" },
247
+ { key: "Europe/London", localeKey: "london" },
248
+ { key: "Europe/Berlin", localeKey: "berlin" },
249
+ { key: "Asia/Bahrain", localeKey: "bahrain" },
250
+ { key: "Asia/Dubai", localeKey: "dubai" },
251
+ { key: "Asia/Ashkhabad", localeKey: "ashkhabad" },
252
+ { key: "Asia/Almaty", localeKey: "almaty" },
253
+ { key: "Asia/Bangkok", localeKey: "bangkok" },
254
+ { key: "Asia/Shanghai", localeKey: "shanghai" },
255
+ { key: "Asia/Tokyo", localeKey: "tokyo" },
256
+ { key: "Australia/Sydney", localeKey: "sydney" },
257
+ { key: "Pacific/Norfolk", localeKey: "norfolk" }
258
+ ];
259
+
260
+ // src/hooks/useTimezone.ts
261
+ function useTimezone() {
262
+ const { state, dispatch } = useKlinechartsUI();
263
+ const timezones = useMemo(
264
+ () => TIMEZONES.map((tz) => ({
265
+ key: tz.key,
266
+ localeKey: tz.localeKey
267
+ })),
268
+ []
269
+ );
270
+ const setTimezone = useCallback(
271
+ (timezone) => {
272
+ state.chart?.setTimezone(timezone);
273
+ dispatch({ type: "SET_TIMEZONE", timezone });
274
+ },
275
+ [state.chart, dispatch]
276
+ );
277
+ return {
278
+ timezones,
279
+ activeTimezone: state.timezone,
280
+ setTimezone
281
+ };
282
+ }
283
+ function useSymbolSearch(debounceMs = 300) {
284
+ const { state, dispatch, datafeed } = useKlinechartsUI();
285
+ const [query, setQueryState] = useState("");
286
+ const [results, setResults] = useState([]);
287
+ const [isSearching, setIsSearching] = useState(false);
288
+ const timerRef = useRef(null);
289
+ const abortRef = useRef(null);
290
+ const setQuery = useCallback(
291
+ (q) => {
292
+ setQueryState(q);
293
+ if (timerRef.current) clearTimeout(timerRef.current);
294
+ abortRef.current?.abort();
295
+ abortRef.current = null;
296
+ if (!q.trim()) {
297
+ setResults([]);
298
+ setIsSearching(false);
299
+ return;
300
+ }
301
+ setIsSearching(true);
302
+ timerRef.current = setTimeout(async () => {
303
+ const controller = new AbortController();
304
+ abortRef.current = controller;
305
+ try {
306
+ const data = await datafeed.searchSymbols(q, controller.signal);
307
+ if (!controller.signal.aborted) {
308
+ setResults(data);
309
+ }
310
+ } catch {
311
+ if (!controller.signal.aborted) {
312
+ setResults([]);
313
+ }
314
+ } finally {
315
+ if (!controller.signal.aborted) {
316
+ setIsSearching(false);
317
+ }
318
+ }
319
+ }, debounceMs);
320
+ },
321
+ [datafeed, debounceMs]
322
+ );
323
+ const selectSymbol = useCallback(
324
+ (symbol) => {
325
+ dispatch({ type: "SET_SYMBOL", symbol });
326
+ setQueryState("");
327
+ setResults([]);
328
+ },
329
+ [dispatch]
330
+ );
331
+ const clearResults = useCallback(() => {
332
+ setQueryState("");
333
+ setResults([]);
334
+ setIsSearching(false);
335
+ }, []);
336
+ useEffect(() => {
337
+ return () => {
338
+ if (timerRef.current) clearTimeout(timerRef.current);
339
+ abortRef.current?.abort();
340
+ };
341
+ }, []);
342
+ return {
343
+ query,
344
+ results,
345
+ isSearching,
346
+ activeSymbol: state.symbol,
347
+ setQuery,
348
+ selectSymbol,
349
+ clearResults
350
+ };
351
+ }
352
+
353
+ // src/data/indicators.ts
354
+ var MAIN_INDICATORS = [
355
+ "MA",
356
+ "EMA",
357
+ "SMA",
358
+ "BOLL",
359
+ "SAR",
360
+ "BBI"
361
+ ];
362
+ var SUB_INDICATORS = [
363
+ "MA",
364
+ "EMA",
365
+ "VOL",
366
+ "MACD",
367
+ "BOLL",
368
+ "KDJ",
369
+ "RSI",
370
+ "BIAS",
371
+ "BRAR",
372
+ "CCI",
373
+ "DMI",
374
+ "CR",
375
+ "PSY",
376
+ "DMA",
377
+ "TRIX",
378
+ "OBV",
379
+ "VR",
380
+ "WR",
381
+ "MTM",
382
+ "EMV",
383
+ "SAR",
384
+ "SMA",
385
+ "ROC",
386
+ "PVT",
387
+ "BBI",
388
+ "AO"
389
+ ];
390
+ var INDICATOR_PARAMS = {
391
+ SMA: {
392
+ name: "SMA",
393
+ localeKey: "sma",
394
+ params: [
395
+ { label: "params_1", defaultValue: 12 },
396
+ { label: "params_2", defaultValue: 2 }
397
+ ]
398
+ },
399
+ BOLL: {
400
+ name: "BOLL",
401
+ localeKey: "boll",
402
+ params: [
403
+ { label: "period", defaultValue: 20 },
404
+ { label: "standard_deviation", defaultValue: 2 }
405
+ ]
406
+ },
407
+ SAR: {
408
+ name: "SAR",
409
+ localeKey: "sar",
410
+ params: [
411
+ { label: "params_1", defaultValue: 2 },
412
+ { label: "params_2", defaultValue: 2 },
413
+ { label: "params_3", defaultValue: 20 }
414
+ ]
415
+ },
416
+ BBI: {
417
+ name: "BBI",
418
+ localeKey: "bbi",
419
+ params: [
420
+ { label: "params_1", defaultValue: 3 },
421
+ { label: "params_2", defaultValue: 6 },
422
+ { label: "params_3", defaultValue: 12 },
423
+ { label: "params_4", defaultValue: 24 }
424
+ ]
425
+ },
426
+ MACD: {
427
+ name: "MACD",
428
+ localeKey: "macd",
429
+ params: [
430
+ { label: "params_1", defaultValue: 12 },
431
+ { label: "params_2", defaultValue: 26 },
432
+ { label: "params_3", defaultValue: 9 }
433
+ ]
434
+ },
435
+ KDJ: {
436
+ name: "KDJ",
437
+ localeKey: "kdj",
438
+ params: [
439
+ { label: "params_1", defaultValue: 9 },
440
+ { label: "params_2", defaultValue: 3 },
441
+ { label: "params_3", defaultValue: 3 }
442
+ ]
443
+ },
444
+ BRAR: {
445
+ name: "BRAR",
446
+ localeKey: "brar",
447
+ params: [{ label: "params_1", defaultValue: 26 }]
448
+ },
449
+ CCI: {
450
+ name: "CCI",
451
+ localeKey: "cci",
452
+ params: [{ label: "params_1", defaultValue: 20 }]
453
+ },
454
+ DMI: {
455
+ name: "DMI",
456
+ localeKey: "dmi",
457
+ params: [
458
+ { label: "params_1", defaultValue: 14 },
459
+ { label: "params_2", defaultValue: 6 }
460
+ ]
461
+ },
462
+ CR: {
463
+ name: "CR",
464
+ localeKey: "cr",
465
+ params: [
466
+ { label: "params_1", defaultValue: 26 },
467
+ { label: "params_2", defaultValue: 10 },
468
+ { label: "params_3", defaultValue: 20 },
469
+ { label: "params_4", defaultValue: 40 },
470
+ { label: "params_5", defaultValue: 60 }
471
+ ]
472
+ },
473
+ PSY: {
474
+ name: "PSY",
475
+ localeKey: "psy",
476
+ params: [
477
+ { label: "params_1", defaultValue: 12 },
478
+ { label: "params_2", defaultValue: 6 }
479
+ ]
480
+ },
481
+ DMA: {
482
+ name: "DMA",
483
+ localeKey: "dma",
484
+ params: [
485
+ { label: "params_1", defaultValue: 10 },
486
+ { label: "params_2", defaultValue: 50 },
487
+ { label: "params_3", defaultValue: 10 }
488
+ ]
489
+ },
490
+ TRIX: {
491
+ name: "TRIX",
492
+ localeKey: "trix",
493
+ params: [
494
+ { label: "params_1", defaultValue: 12 },
495
+ { label: "params_2", defaultValue: 9 }
496
+ ]
497
+ },
498
+ OBV: {
499
+ name: "OBV",
500
+ localeKey: "obv",
501
+ params: [{ label: "params_1", defaultValue: 30 }]
502
+ },
503
+ VR: {
504
+ name: "VR",
505
+ localeKey: "vr",
506
+ params: [
507
+ { label: "params_1", defaultValue: 26 },
508
+ { label: "params_2", defaultValue: 6 }
509
+ ]
510
+ },
511
+ MTM: {
512
+ name: "MTM",
513
+ localeKey: "mtm",
514
+ params: [
515
+ { label: "params_1", defaultValue: 12 },
516
+ { label: "params_2", defaultValue: 6 }
517
+ ]
518
+ },
519
+ EMV: {
520
+ name: "EMV",
521
+ localeKey: "emv",
522
+ params: [
523
+ { label: "params_1", defaultValue: 14 },
524
+ { label: "params_2", defaultValue: 9 }
525
+ ]
526
+ },
527
+ ROC: {
528
+ name: "ROC",
529
+ localeKey: "roc",
530
+ params: [
531
+ { label: "params_1", defaultValue: 12 },
532
+ { label: "params_2", defaultValue: 6 }
533
+ ]
534
+ },
535
+ PVT: {
536
+ name: "PVT",
537
+ localeKey: "pvt",
538
+ params: []
539
+ },
540
+ AO: {
541
+ name: "AO",
542
+ localeKey: "ao",
543
+ params: [
544
+ { label: "params_1", defaultValue: 5 },
545
+ { label: "params_2", defaultValue: 34 }
546
+ ]
547
+ }
548
+ };
549
+
550
+ // src/hooks/useIndicators.ts
551
+ function useIndicators() {
552
+ const { state, dispatch } = useKlinechartsUI();
553
+ const mainIndicators = useMemo(() => {
554
+ const activeNames = state.mainIndicators;
555
+ const inactive = MAIN_INDICATORS.filter((n) => !activeNames.includes(n));
556
+ return [
557
+ ...activeNames.map((name) => ({ name, isActive: true })),
558
+ ...inactive.map((name) => ({ name, isActive: false }))
559
+ ];
560
+ }, [state.mainIndicators]);
561
+ const subIndicators = useMemo(() => {
562
+ const activeNames = Object.keys(state.subIndicators);
563
+ const inactive = SUB_INDICATORS.filter((n) => !activeNames.includes(n));
564
+ return [
565
+ ...activeNames.map((name) => ({ name, isActive: true })),
566
+ ...inactive.map((name) => ({ name, isActive: false }))
567
+ ];
568
+ }, [state.subIndicators]);
569
+ const addMainIndicator = useCallback(
570
+ (name) => {
571
+ if (state.mainIndicators.includes(name)) return;
572
+ state.chart?.createIndicator(
573
+ { name, id: `main_${name}`, paneId: "candle_pane" },
574
+ true,
575
+ { id: "candle_pane" }
576
+ );
577
+ const newIndicators = [...state.mainIndicators, name];
578
+ dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
579
+ },
580
+ [state.chart, state.mainIndicators, dispatch]
581
+ );
582
+ const removeMainIndicator = useCallback(
583
+ (name) => {
584
+ state.chart?.removeIndicator({ id: `main_${name}` });
585
+ const newIndicators = state.mainIndicators.filter((n) => n !== name);
586
+ dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
587
+ },
588
+ [state.chart, state.mainIndicators, dispatch]
589
+ );
590
+ const addSubIndicator = useCallback(
591
+ (name) => {
592
+ if (name in state.subIndicators) return;
593
+ const id = `sub_${name}`;
594
+ state.chart?.createIndicator({ name, id });
595
+ const paneId = state.chart?.getIndicators({ id })?.[0]?.paneId ?? "";
596
+ const newIndicators = { ...state.subIndicators, [name]: paneId };
597
+ dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
598
+ },
599
+ [state.chart, state.subIndicators, dispatch]
600
+ );
601
+ const removeSubIndicator = useCallback(
602
+ (name) => {
603
+ state.chart?.removeIndicator({ id: `sub_${name}` });
604
+ const newIndicators = { ...state.subIndicators };
605
+ delete newIndicators[name];
606
+ dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
607
+ },
608
+ [state.chart, state.subIndicators, dispatch]
609
+ );
610
+ const toggleMainIndicator = useCallback(
611
+ (name) => {
612
+ if (state.mainIndicators.includes(name)) {
613
+ removeMainIndicator(name);
614
+ } else {
615
+ addMainIndicator(name);
616
+ }
617
+ },
618
+ [state.mainIndicators, addMainIndicator, removeMainIndicator]
619
+ );
620
+ const toggleSubIndicator = useCallback(
621
+ (name) => {
622
+ if (name in state.subIndicators) {
623
+ removeSubIndicator(name);
624
+ } else {
625
+ addSubIndicator(name);
626
+ }
627
+ },
628
+ [state.subIndicators, addSubIndicator, removeSubIndicator]
629
+ );
630
+ const setIndicatorVisible = useCallback(
631
+ (name, isMain, visible) => {
632
+ const id = isMain ? `main_${name}` : `sub_${name}`;
633
+ state.chart?.overrideIndicator({ name, id, visible });
634
+ },
635
+ [state.chart]
636
+ );
637
+ const updateIndicatorParams = useCallback(
638
+ (name, paneId, params) => {
639
+ state.chart?.overrideIndicator({ name, paneId, calcParams: params });
640
+ },
641
+ [state.chart]
642
+ );
643
+ const getIndicatorParams = useCallback((name) => {
644
+ const config = INDICATOR_PARAMS[name];
645
+ if (!config) return [];
646
+ return config.params.map((p) => ({
647
+ label: p.label,
648
+ defaultValue: p.defaultValue
649
+ }));
650
+ }, []);
651
+ const moveToMain = useCallback(
652
+ (name) => {
653
+ if (!state.chart) return;
654
+ state.chart.removeIndicator({ id: `sub_${name}` });
655
+ state.chart.createIndicator(
656
+ { name, id: `main_${name}`, paneId: "candle_pane" },
657
+ true,
658
+ { id: "candle_pane" }
659
+ );
660
+ const newSub = { ...state.subIndicators };
661
+ delete newSub[name];
662
+ dispatch({ type: "SET_SUB_INDICATORS", indicators: newSub });
663
+ dispatch({
664
+ type: "SET_MAIN_INDICATORS",
665
+ indicators: [...state.mainIndicators, name]
666
+ });
667
+ },
668
+ [state.chart, state.mainIndicators, state.subIndicators, dispatch]
669
+ );
670
+ const moveToSub = useCallback(
671
+ (name) => {
672
+ if (!state.chart) return;
673
+ state.chart.removeIndicator({ id: `main_${name}` });
674
+ const subId = `sub_${name}`;
675
+ state.chart.createIndicator({ name, id: subId });
676
+ const paneId = state.chart.getIndicators({ id: subId })?.[0]?.paneId ?? "";
677
+ const newMain = state.mainIndicators.filter((n) => n !== name);
678
+ dispatch({ type: "SET_MAIN_INDICATORS", indicators: newMain });
679
+ dispatch({
680
+ type: "SET_SUB_INDICATORS",
681
+ indicators: { ...state.subIndicators, [name]: paneId }
682
+ });
683
+ },
684
+ [state.chart, state.mainIndicators, state.subIndicators, dispatch]
685
+ );
686
+ const isMainIndicatorActive = useCallback(
687
+ (name) => state.mainIndicators.includes(name),
688
+ [state.mainIndicators]
689
+ );
690
+ const isSubIndicatorActive = useCallback(
691
+ (name) => name in state.subIndicators,
692
+ [state.subIndicators]
693
+ );
694
+ return {
695
+ mainIndicators,
696
+ subIndicators,
697
+ activeMainIndicators: state.mainIndicators,
698
+ activeSubIndicators: state.subIndicators,
699
+ availableMainIndicators: MAIN_INDICATORS,
700
+ availableSubIndicators: SUB_INDICATORS,
701
+ addMainIndicator,
702
+ removeMainIndicator,
703
+ addSubIndicator,
704
+ removeSubIndicator,
705
+ toggleMainIndicator,
706
+ toggleSubIndicator,
707
+ moveToMain,
708
+ moveToSub,
709
+ setIndicatorVisible,
710
+ updateIndicatorParams,
711
+ getIndicatorParams,
712
+ isMainIndicatorActive,
713
+ isSubIndicatorActive
714
+ };
715
+ }
716
+
717
+ // src/data/drawings.ts
718
+ var DRAWING_CATEGORIES = [
719
+ {
720
+ key: "singleLine",
721
+ tools: [
722
+ { name: "horizontalStraightLine", localeKey: "horizontal_straight_line" },
723
+ { name: "horizontalRayLine", localeKey: "horizontal_ray_line" },
724
+ { name: "horizontalSegment", localeKey: "horizontal_segment" },
725
+ { name: "verticalStraightLine", localeKey: "vertical_straight_line" },
726
+ { name: "verticalRayLine", localeKey: "vertical_ray_line" },
727
+ { name: "verticalSegment", localeKey: "vertical_segment" },
728
+ { name: "straightLine", localeKey: "straight_line" },
729
+ { name: "rayLine", localeKey: "ray_line" },
730
+ { name: "segment", localeKey: "segment" },
731
+ { name: "arrow", localeKey: "arrow" },
732
+ { name: "priceLine", localeKey: "price_line" }
733
+ ]
734
+ },
735
+ {
736
+ key: "moreLine",
737
+ tools: [
738
+ { name: "priceChannelLine", localeKey: "price_channel_line" },
739
+ { name: "parallelStraightLine", localeKey: "parallel_straight_line" }
740
+ ]
741
+ },
742
+ {
743
+ key: "polygon",
744
+ tools: [
745
+ { name: "circle", localeKey: "circle" },
746
+ { name: "rect", localeKey: "rect" },
747
+ { name: "parallelogram", localeKey: "parallelogram" },
748
+ { name: "triangle", localeKey: "triangle" }
749
+ ]
750
+ },
751
+ {
752
+ key: "fibonacci",
753
+ tools: [
754
+ { name: "fibonacciLine", localeKey: "fibonacci_line" },
755
+ { name: "fibonacciSegment", localeKey: "fibonacci_segment" },
756
+ { name: "fibonacciCircle", localeKey: "fibonacci_circle" },
757
+ { name: "fibonacciSpiral", localeKey: "fibonacci_spiral" },
758
+ {
759
+ name: "fibonacciSpeedResistanceFan",
760
+ localeKey: "fibonacci_speed_resistance_fan"
761
+ },
762
+ { name: "fibonacciExtension", localeKey: "fibonacci_extension" },
763
+ { name: "gannBox", localeKey: "gann_box" }
764
+ ]
765
+ },
766
+ {
767
+ key: "wave",
768
+ tools: [
769
+ { name: "xabcd", localeKey: "xabcd" },
770
+ { name: "abcd", localeKey: "abcd" },
771
+ { name: "threeWaves", localeKey: "three_waves" },
772
+ { name: "fiveWaves", localeKey: "five_waves" },
773
+ { name: "eightWaves", localeKey: "eight_waves" },
774
+ { name: "anyWaves", localeKey: "any_waves" }
775
+ ]
776
+ }
777
+ ];
778
+
779
+ // src/hooks/useDrawingTools.ts
780
+ var DRAWING_GROUP_ID = "drawing_tools";
781
+ function useDrawingTools() {
782
+ const { state } = useKlinechartsUI();
783
+ const [activeTool, setActiveTool] = useState(null);
784
+ const [magnetMode, setMagnetModeState] = useState("normal");
785
+ const [isLocked, setIsLocked] = useState(false);
786
+ const [isVisible, setIsVisible] = useState(true);
787
+ const categories = useMemo(
788
+ () => DRAWING_CATEGORIES.map((cat) => ({
789
+ key: cat.key,
790
+ tools: cat.tools.map((tool) => ({
791
+ name: tool.name,
792
+ localeKey: tool.localeKey
793
+ }))
794
+ })),
795
+ []
796
+ );
797
+ const selectTool = useCallback(
798
+ (name) => {
799
+ setActiveTool(name);
800
+ state.chart?.createOverlay({
801
+ name,
802
+ groupId: DRAWING_GROUP_ID,
803
+ lock: isLocked,
804
+ visible: isVisible,
805
+ mode: magnetMode === "strong" ? "strong_magnet" : magnetMode === "weak" ? "weak_magnet" : "normal"
806
+ });
807
+ },
808
+ [state.chart, isLocked, isVisible, magnetMode]
809
+ );
810
+ const clearActiveTool = useCallback(() => {
811
+ setActiveTool(null);
812
+ }, []);
813
+ const setMagnetMode = useCallback(
814
+ (mode) => {
815
+ setMagnetModeState(mode);
816
+ const overlays2 = state.chart?.getOverlays({ groupId: DRAWING_GROUP_ID });
817
+ if (overlays2) {
818
+ const overlayMode = mode === "strong" ? "strong_magnet" : mode === "weak" ? "weak_magnet" : "normal";
819
+ overlays2.forEach((overlay) => {
820
+ state.chart?.overrideOverlay({
821
+ id: overlay.id,
822
+ mode: overlayMode
823
+ });
824
+ });
825
+ }
826
+ },
827
+ [state.chart]
828
+ );
829
+ const toggleLock = useCallback(() => {
830
+ const newLocked = !isLocked;
831
+ setIsLocked(newLocked);
832
+ const overlays2 = state.chart?.getOverlays({ groupId: DRAWING_GROUP_ID });
833
+ if (overlays2) {
834
+ overlays2.forEach((overlay) => {
835
+ state.chart?.overrideOverlay({
836
+ id: overlay.id,
837
+ lock: newLocked
838
+ });
839
+ });
840
+ }
841
+ }, [state.chart, isLocked]);
842
+ const toggleVisibility = useCallback(() => {
843
+ const newVisible = !isVisible;
844
+ setIsVisible(newVisible);
845
+ const overlays2 = state.chart?.getOverlays({ groupId: DRAWING_GROUP_ID });
846
+ if (overlays2) {
847
+ overlays2.forEach((overlay) => {
848
+ state.chart?.overrideOverlay({
849
+ id: overlay.id,
850
+ visible: newVisible
851
+ });
852
+ });
853
+ }
854
+ }, [state.chart, isVisible]);
855
+ const removeAllDrawings = useCallback(() => {
856
+ state.chart?.removeOverlay({ groupId: DRAWING_GROUP_ID });
857
+ setActiveTool(null);
858
+ }, [state.chart]);
859
+ return {
860
+ categories,
861
+ activeTool,
862
+ magnetMode,
863
+ isLocked,
864
+ isVisible,
865
+ selectTool,
866
+ clearActiveTool,
867
+ setMagnetMode,
868
+ toggleLock,
869
+ toggleVisibility,
870
+ removeAllDrawings
871
+ };
872
+ }
873
+
874
+ // src/data/candle-types.ts
875
+ var CANDLE_TYPES = [
876
+ { key: "candle_solid", localeKey: "candle_solid" },
877
+ { key: "candle_stroke", localeKey: "candle_stroke" },
878
+ { key: "candle_up_stroke", localeKey: "candle_up_stroke" },
879
+ { key: "candle_down_stroke", localeKey: "candle_down_stroke" },
880
+ { key: "ohlc", localeKey: "ohlc" },
881
+ { key: "area", localeKey: "area" }
882
+ ];
883
+ var PRICE_AXIS_TYPES = ["normal", "percentage", "logarithm"];
884
+ var YAXIS_POSITIONS = ["left", "right"];
885
+ var COMPARE_RULES = ["current_open", "prev_close"];
886
+ var TOOLTIP_SHOW_RULES = ["always", "follow_cross", "none"];
887
+
888
+ // src/hooks/useKlinechartsUISettings.ts
889
+ var defaultSettings = {
890
+ candleType: "candle_solid",
891
+ candleUpColor: "#2DC08E",
892
+ candleDownColor: "#F92855",
893
+ compareRule: "current_open",
894
+ showLastPrice: true,
895
+ showLastPriceLine: true,
896
+ showHighPrice: true,
897
+ showLowPrice: true,
898
+ showIndicatorLastValue: true,
899
+ priceAxisType: "normal",
900
+ yAxisPosition: "right",
901
+ yAxisInside: false,
902
+ reverseCoordinate: false,
903
+ showTimeAxis: true,
904
+ showGrid: true,
905
+ showCrosshair: true,
906
+ showCandleTooltip: true,
907
+ showIndicatorTooltip: true,
908
+ tooltipShowRule: "always"
909
+ };
910
+ function useKlinechartsUISettings() {
911
+ const { state, onSettingsChange } = useKlinechartsUI();
912
+ const [settings, setSettings] = useState(defaultSettings);
913
+ const isInitialMount = useRef(true);
914
+ useEffect(() => {
915
+ if (isInitialMount.current) {
916
+ isInitialMount.current = false;
917
+ return;
918
+ }
919
+ onSettingsChange?.({ ...settings });
920
+ }, [settings, onSettingsChange]);
921
+ const candleTypes = useMemo(
922
+ () => CANDLE_TYPES.map((ct) => ({
923
+ key: ct.key,
924
+ localeKey: ct.localeKey
925
+ })),
926
+ []
927
+ );
928
+ const priceAxisTypes = useMemo(
929
+ () => PRICE_AXIS_TYPES.map((pat) => ({ key: pat, localeKey: pat })),
930
+ []
931
+ );
932
+ const yAxisPositions = useMemo(
933
+ () => YAXIS_POSITIONS.map((p) => ({ key: p, localeKey: p })),
934
+ []
935
+ );
936
+ const compareRules = useMemo(
937
+ () => COMPARE_RULES.map((r) => ({ key: r, localeKey: r })),
938
+ []
939
+ );
940
+ const tooltipShowRules = useMemo(
941
+ () => TOOLTIP_SHOW_RULES.map((r) => ({ key: r, localeKey: r })),
942
+ []
943
+ );
944
+ const hasAppliedInitial = useRef(false);
945
+ useEffect(() => {
946
+ if (!state.chart || hasAppliedInitial.current) return;
947
+ hasAppliedInitial.current = true;
948
+ const needsPaneOptions = settings.reverseCoordinate || settings.priceAxisType !== "normal" || settings.yAxisPosition !== "right" || settings.yAxisInside;
949
+ if (needsPaneOptions) {
950
+ state.chart.setPaneOptions({
951
+ id: "candle_pane",
952
+ axis: {
953
+ ...settings.priceAxisType !== "normal" && { name: settings.priceAxisType },
954
+ ...settings.reverseCoordinate && { reverse: true },
955
+ ...settings.yAxisPosition !== "right" && { position: settings.yAxisPosition },
956
+ ...settings.yAxisInside && { inside: true }
957
+ }
958
+ });
959
+ }
960
+ if (settings.showIndicatorLastValue) {
961
+ state.chart.setStyles({
962
+ indicator: { lastValueMark: { show: true } }
963
+ });
964
+ }
965
+ }, [state.chart]);
966
+ const applyStyle = useCallback(
967
+ (path, value) => {
968
+ const parts = path.split(".");
969
+ const styleObj = {};
970
+ let current = styleObj;
971
+ for (let i = 0; i < parts.length - 1; i++) {
972
+ current[parts[i]] = {};
973
+ current = current[parts[i]];
974
+ }
975
+ current[parts[parts.length - 1]] = value;
976
+ state.chart?.setStyles(styleObj);
977
+ },
978
+ [state.chart]
979
+ );
980
+ const applyPaneAxis = useCallback(
981
+ (axis) => {
982
+ state.chart?.setPaneOptions({ id: "candle_pane", axis });
983
+ },
984
+ [state.chart]
985
+ );
986
+ const setCandleType = useCallback(
987
+ (type) => {
988
+ applyStyle("candle.type", type);
989
+ setSettings((s) => ({ ...s, candleType: type }));
990
+ },
991
+ [applyStyle]
992
+ );
993
+ const setCandleUpColor = useCallback(
994
+ (color) => {
995
+ state.chart?.setStyles({
996
+ candle: {
997
+ bar: { upColor: color, upBorderColor: color, upWickColor: color }
998
+ }
999
+ });
1000
+ setSettings((s) => ({ ...s, candleUpColor: color }));
1001
+ },
1002
+ [state.chart]
1003
+ );
1004
+ const setCandleDownColor = useCallback(
1005
+ (color) => {
1006
+ state.chart?.setStyles({
1007
+ candle: {
1008
+ bar: { downColor: color, downBorderColor: color, downWickColor: color }
1009
+ }
1010
+ });
1011
+ setSettings((s) => ({ ...s, candleDownColor: color }));
1012
+ },
1013
+ [state.chart]
1014
+ );
1015
+ const setCompareRule = useCallback(
1016
+ (rule) => {
1017
+ applyStyle("candle.bar.compareRule", rule);
1018
+ setSettings((s) => ({ ...s, compareRule: rule }));
1019
+ },
1020
+ [applyStyle]
1021
+ );
1022
+ const setShowLastPrice = useCallback(
1023
+ (show) => {
1024
+ applyStyle("candle.priceMark.last.show", show);
1025
+ setSettings((s) => ({ ...s, showLastPrice: show }));
1026
+ },
1027
+ [applyStyle]
1028
+ );
1029
+ const setShowLastPriceLine = useCallback(
1030
+ (show) => {
1031
+ applyStyle("candle.priceMark.last.line.show", show);
1032
+ setSettings((s) => ({ ...s, showLastPriceLine: show }));
1033
+ },
1034
+ [applyStyle]
1035
+ );
1036
+ const setShowHighPrice = useCallback(
1037
+ (show) => {
1038
+ applyStyle("candle.priceMark.high.show", show);
1039
+ setSettings((s) => ({ ...s, showHighPrice: show }));
1040
+ },
1041
+ [applyStyle]
1042
+ );
1043
+ const setShowLowPrice = useCallback(
1044
+ (show) => {
1045
+ applyStyle("candle.priceMark.low.show", show);
1046
+ setSettings((s) => ({ ...s, showLowPrice: show }));
1047
+ },
1048
+ [applyStyle]
1049
+ );
1050
+ const setShowIndicatorLastValue = useCallback(
1051
+ (show) => {
1052
+ applyStyle("indicator.lastValueMark.show", show);
1053
+ setSettings((s) => ({ ...s, showIndicatorLastValue: show }));
1054
+ },
1055
+ [applyStyle]
1056
+ );
1057
+ const setPriceAxisType = useCallback(
1058
+ (type) => {
1059
+ applyPaneAxis({ name: type });
1060
+ setSettings((s) => ({ ...s, priceAxisType: type }));
1061
+ },
1062
+ [applyPaneAxis]
1063
+ );
1064
+ const setYAxisPosition = useCallback(
1065
+ (position) => {
1066
+ applyPaneAxis({ position });
1067
+ setSettings((s) => ({ ...s, yAxisPosition: position }));
1068
+ },
1069
+ [applyPaneAxis]
1070
+ );
1071
+ const setYAxisInside = useCallback(
1072
+ (inside) => {
1073
+ applyPaneAxis({ inside });
1074
+ setSettings((s) => ({ ...s, yAxisInside: inside }));
1075
+ },
1076
+ [applyPaneAxis]
1077
+ );
1078
+ const setReverseCoordinate = useCallback(
1079
+ (reverse) => {
1080
+ applyPaneAxis({ reverse });
1081
+ setSettings((s) => ({ ...s, reverseCoordinate: reverse }));
1082
+ },
1083
+ [applyPaneAxis]
1084
+ );
1085
+ const setShowGrid = useCallback(
1086
+ (show) => {
1087
+ applyStyle("grid.show", show);
1088
+ setSettings((s) => ({ ...s, showGrid: show }));
1089
+ },
1090
+ [applyStyle]
1091
+ );
1092
+ const setShowTimeAxis = useCallback(
1093
+ (show) => {
1094
+ applyStyle("xAxis.show", show);
1095
+ setSettings((s) => ({ ...s, showTimeAxis: show }));
1096
+ },
1097
+ [applyStyle]
1098
+ );
1099
+ const setShowCrosshair = useCallback(
1100
+ (show) => {
1101
+ applyStyle("crosshair.show", show);
1102
+ setSettings((s) => ({ ...s, showCrosshair: show }));
1103
+ },
1104
+ [applyStyle]
1105
+ );
1106
+ const setShowCandleTooltip = useCallback(
1107
+ (show) => {
1108
+ applyStyle("candle.tooltip.show", show);
1109
+ setSettings((s) => ({ ...s, showCandleTooltip: show }));
1110
+ },
1111
+ [applyStyle]
1112
+ );
1113
+ const setShowIndicatorTooltip = useCallback(
1114
+ (show) => {
1115
+ applyStyle("indicator.tooltip.show", show);
1116
+ setSettings((s) => ({ ...s, showIndicatorTooltip: show }));
1117
+ },
1118
+ [applyStyle]
1119
+ );
1120
+ const setTooltipShowRule = useCallback(
1121
+ (rule) => {
1122
+ state.chart?.setStyles({
1123
+ candle: { tooltip: { showRule: rule } },
1124
+ indicator: { tooltip: { showRule: rule } }
1125
+ });
1126
+ setSettings((s) => ({ ...s, tooltipShowRule: rule }));
1127
+ },
1128
+ [state.chart]
1129
+ );
1130
+ const resetToDefaults = useCallback(() => {
1131
+ setSettings(defaultSettings);
1132
+ state.chart?.setStyles(state.theme);
1133
+ state.chart?.setPaneOptions({
1134
+ id: "candle_pane",
1135
+ axis: { name: "normal", reverse: false, position: "right", inside: false }
1136
+ });
1137
+ }, [state.chart, state.theme]);
1138
+ return {
1139
+ ...settings,
1140
+ candleTypes,
1141
+ priceAxisTypes,
1142
+ yAxisPositions,
1143
+ compareRules,
1144
+ tooltipShowRules,
1145
+ setCandleType,
1146
+ setCandleUpColor,
1147
+ setCandleDownColor,
1148
+ setCompareRule,
1149
+ setShowLastPrice,
1150
+ setShowLastPriceLine,
1151
+ setShowHighPrice,
1152
+ setShowLowPrice,
1153
+ setShowIndicatorLastValue,
1154
+ setPriceAxisType,
1155
+ setYAxisPosition,
1156
+ setYAxisInside,
1157
+ setReverseCoordinate,
1158
+ setShowTimeAxis,
1159
+ setShowGrid,
1160
+ setShowCrosshair,
1161
+ setShowCandleTooltip,
1162
+ setShowIndicatorTooltip,
1163
+ setTooltipShowRule,
1164
+ resetToDefaults
1165
+ };
1166
+ }
1167
+ function useScreenshot() {
1168
+ const { state, dispatch } = useKlinechartsUI();
1169
+ const capture = useCallback(() => {
1170
+ if (!state.chart) return;
1171
+ const url = state.chart.getConvertPictureUrl(true, "jpeg", state.theme === "dark" ? "#151517" : "#ffffff");
1172
+ dispatch({ type: "SET_SCREENSHOT_URL", url });
1173
+ }, [state.chart, state.theme, dispatch]);
1174
+ const download = useCallback(
1175
+ (filename = "chart-screenshot.jpg") => {
1176
+ if (!state.screenshotUrl) return;
1177
+ const link = document.createElement("a");
1178
+ link.href = state.screenshotUrl;
1179
+ link.download = filename;
1180
+ document.body.appendChild(link);
1181
+ link.click();
1182
+ document.body.removeChild(link);
1183
+ },
1184
+ [state.screenshotUrl]
1185
+ );
1186
+ const clear = useCallback(() => {
1187
+ dispatch({ type: "SET_SCREENSHOT_URL", url: null });
1188
+ }, [dispatch]);
1189
+ return {
1190
+ screenshotUrl: state.screenshotUrl,
1191
+ capture,
1192
+ download,
1193
+ clear
1194
+ };
1195
+ }
1196
+ function useFullscreen() {
1197
+ const { fullscreenContainerRef } = useKlinechartsUIDispatch();
1198
+ const [isFullscreen, setIsFullscreen] = useState(false);
1199
+ const enter = useCallback(() => {
1200
+ const el = fullscreenContainerRef.current;
1201
+ if (!el) return;
1202
+ if (el.requestFullscreen) {
1203
+ el.requestFullscreen();
1204
+ } else if ("webkitRequestFullscreen" in el) {
1205
+ el.webkitRequestFullscreen();
1206
+ } else if ("msRequestFullscreen" in el) {
1207
+ el.msRequestFullscreen();
1208
+ }
1209
+ }, [fullscreenContainerRef]);
1210
+ const exit = useCallback(() => {
1211
+ if (document.exitFullscreen) {
1212
+ document.exitFullscreen();
1213
+ } else if ("webkitExitFullscreen" in document) {
1214
+ document.webkitExitFullscreen();
1215
+ } else if ("msExitFullscreen" in document) {
1216
+ document.msExitFullscreen();
1217
+ }
1218
+ }, []);
1219
+ const toggle = useCallback(() => {
1220
+ if (isFullscreen) {
1221
+ exit();
1222
+ } else {
1223
+ enter();
1224
+ }
1225
+ }, [isFullscreen, enter, exit]);
1226
+ useEffect(() => {
1227
+ const handler = () => {
1228
+ setIsFullscreen(!!document.fullscreenElement);
1229
+ };
1230
+ document.addEventListener("fullscreenchange", handler);
1231
+ document.addEventListener("webkitfullscreenchange", handler);
1232
+ return () => {
1233
+ document.removeEventListener("fullscreenchange", handler);
1234
+ document.removeEventListener("webkitfullscreenchange", handler);
1235
+ };
1236
+ }, []);
1237
+ return {
1238
+ isFullscreen,
1239
+ toggle,
1240
+ enter,
1241
+ exit,
1242
+ containerRef: fullscreenContainerRef
1243
+ };
1244
+ }
1245
+ function useOrderLines() {
1246
+ const { state } = useKlinechartsUI();
1247
+ const callbacksRef = useRef(/* @__PURE__ */ new Map());
1248
+ const createOrderLine = useCallback(
1249
+ (options) => {
1250
+ if (!state.chart) return null;
1251
+ const {
1252
+ id: optId,
1253
+ price,
1254
+ draggable = false,
1255
+ onPriceChange,
1256
+ ...extendData
1257
+ } = options;
1258
+ const id = optId ?? `order_line_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
1259
+ if (onPriceChange) {
1260
+ callbacksRef.current.set(id, onPriceChange);
1261
+ }
1262
+ const dataCount = state.chart.getDataList().length;
1263
+ const anchorIndex = dataCount > 0 ? dataCount - 1 : 0;
1264
+ state.chart.createOverlay({
1265
+ name: "orderLine",
1266
+ id,
1267
+ points: [{ dataIndex: anchorIndex, value: price }],
1268
+ extendData,
1269
+ lock: !draggable,
1270
+ mode: "normal",
1271
+ onPressedMoveEnd: (event) => {
1272
+ const newPrice = event.overlay.points[0]?.value;
1273
+ if (newPrice != null) {
1274
+ callbacksRef.current.get(id)?.(newPrice);
1275
+ }
1276
+ }
1277
+ });
1278
+ return id;
1279
+ },
1280
+ [state.chart]
1281
+ );
1282
+ const updateOrderLine = useCallback(
1283
+ (id, options) => {
1284
+ if (!state.chart) return;
1285
+ const { price, draggable, onPriceChange, ...extendData } = options;
1286
+ const existing = state.chart.getOverlays({ id })?.[0];
1287
+ if (!existing) return;
1288
+ if (onPriceChange !== void 0) {
1289
+ callbacksRef.current.set(id, onPriceChange);
1290
+ }
1291
+ const dataCount = state.chart.getDataList().length;
1292
+ const anchorIndex = dataCount > 0 ? dataCount - 1 : 0;
1293
+ state.chart.overrideOverlay({
1294
+ id,
1295
+ ...price != null ? { points: [{ dataIndex: anchorIndex, value: price }] } : {},
1296
+ ...draggable != null ? { lock: !draggable } : {},
1297
+ extendData: {
1298
+ ...existing.extendData,
1299
+ ...extendData
1300
+ }
1301
+ });
1302
+ },
1303
+ [state.chart]
1304
+ );
1305
+ const removeOrderLine = useCallback(
1306
+ (id) => {
1307
+ state.chart?.removeOverlay({ id });
1308
+ callbacksRef.current.delete(id);
1309
+ },
1310
+ [state.chart]
1311
+ );
1312
+ const removeAllOrderLines = useCallback(() => {
1313
+ state.chart?.removeOverlay({ name: "orderLine" });
1314
+ callbacksRef.current.clear();
1315
+ }, [state.chart]);
1316
+ return {
1317
+ createOrderLine,
1318
+ updateOrderLine,
1319
+ removeOrderLine,
1320
+ removeAllOrderLines
1321
+ };
1322
+ }
1323
+
1324
+ // src/utils/createDataLoader.ts
1325
+ function createDataLoader(datafeed, dispatch) {
1326
+ let oldestTimestamp = null;
1327
+ let currentGen = 0;
1328
+ return {
1329
+ getBars: async (params) => {
1330
+ try {
1331
+ dispatch({ type: "SET_LOADING", isLoading: true });
1332
+ if (params.type === "init") {
1333
+ oldestTimestamp = null;
1334
+ const gen = ++currentGen;
1335
+ const data = await datafeed.getHistoryKLineData(
1336
+ params.symbol,
1337
+ { ...params.period, label: "" },
1338
+ 0,
1339
+ Date.now()
1340
+ );
1341
+ if (gen !== currentGen) return;
1342
+ if (data.length > 0) {
1343
+ oldestTimestamp = data[0].timestamp;
1344
+ }
1345
+ params.callback(data, {
1346
+ forward: data.length > 0,
1347
+ backward: false
1348
+ });
1349
+ } else if (params.type === "forward" && oldestTimestamp !== null) {
1350
+ const gen = currentGen;
1351
+ const data = await datafeed.getHistoryKLineData(
1352
+ params.symbol,
1353
+ { ...params.period, label: "" },
1354
+ 0,
1355
+ oldestTimestamp - 1
1356
+ );
1357
+ if (gen !== currentGen) return;
1358
+ if (data.length > 0) {
1359
+ oldestTimestamp = data[0].timestamp;
1360
+ }
1361
+ params.callback(data, {
1362
+ forward: data.length > 0,
1363
+ backward: false
1364
+ });
1365
+ } else if (params.type === "backward") {
1366
+ params.callback([], { forward: false, backward: false });
1367
+ }
1368
+ } catch (error) {
1369
+ console.error("Failed to load chart data:", error);
1370
+ params.callback([], { forward: false, backward: false });
1371
+ } finally {
1372
+ dispatch({ type: "SET_LOADING", isLoading: false });
1373
+ }
1374
+ },
1375
+ subscribeBar: (params) => {
1376
+ datafeed.subscribe(
1377
+ params.symbol,
1378
+ { ...params.period, label: "" },
1379
+ (klineData) => params.callback(klineData)
1380
+ );
1381
+ },
1382
+ unsubscribeBar: (params) => {
1383
+ datafeed.unsubscribe(params.symbol, { ...params.period, label: "" });
1384
+ }
1385
+ };
1386
+ }
1387
+
1388
+ export { CANDLE_TYPES, COMPARE_RULES, DEFAULT_PERIODS, DRAWING_CATEGORIES, INDICATOR_PARAMS, KlinechartsUIDispatchContext, KlinechartsUIProvider, KlinechartsUIStateContext, MAIN_INDICATORS, PRICE_AXIS_TYPES, SUB_INDICATORS, TIMEZONES, TOOLTIP_SHOW_RULES, YAXIS_POSITIONS, createDataLoader, useDrawingTools, useFullscreen, useIndicators, useKlinechartsUI, useKlinechartsUILoading, useKlinechartsUISettings, useKlinechartsUITheme, useOrderLines, usePeriods, useScreenshot, useSymbolSearch, useTimezone };
1389
+ //# sourceMappingURL=index.js.map
1390
+ //# sourceMappingURL=index.js.map