wenay-react2 1.0.1

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.
Files changed (92) hide show
  1. package/lib/common/api.d.ts +17 -0
  2. package/lib/common/api.js +24 -0
  3. package/lib/common/src/components/Buttons/MiniButton.d.ts +17 -0
  4. package/lib/common/src/components/Buttons/MiniButton.js +18 -0
  5. package/lib/common/src/components/Buttons/index.d.ts +1 -0
  6. package/lib/common/src/components/Buttons/index.js +1 -0
  7. package/lib/common/src/components/Dnd/DraggableOutlineDiv.d.ts +1 -0
  8. package/lib/common/src/components/Dnd/DraggableOutlineDiv.js +31 -0
  9. package/lib/common/src/components/Dnd/RNDFunc.d.ts +13 -0
  10. package/lib/common/src/components/Dnd/RNDFunc.js +111 -0
  11. package/lib/common/src/components/Dnd/RNDFunc3.d.ts +81 -0
  12. package/lib/common/src/components/Dnd/RNDFunc3.js +380 -0
  13. package/lib/common/src/components/Dnd/Resizable.d.ts +15 -0
  14. package/lib/common/src/components/Dnd/Resizable.js +36 -0
  15. package/lib/common/src/components/Dnd/index.d.ts +4 -0
  16. package/lib/common/src/components/Dnd/index.js +4 -0
  17. package/lib/common/src/components/Input.d.ts +28 -0
  18. package/lib/common/src/components/Input.js +30 -0
  19. package/lib/common/src/components/Menu/RightMenu.d.ts +22 -0
  20. package/lib/common/src/components/Menu/RightMenu.js +179 -0
  21. package/lib/common/src/components/Menu/StickerMenu.d.ts +5 -0
  22. package/lib/common/src/components/Menu/StickerMenu.js +110 -0
  23. package/lib/common/src/components/Menu/index.d.ts +2 -0
  24. package/lib/common/src/components/Menu/index.js +2 -0
  25. package/lib/common/src/components/Modal/LeftModal.d.ts +58 -0
  26. package/lib/common/src/components/Modal/LeftModal.js +284 -0
  27. package/lib/common/src/components/Modal/Modal.d.ts +31 -0
  28. package/lib/common/src/components/Modal/Modal.js +104 -0
  29. package/lib/common/src/components/Modal/index.d.ts +2 -0
  30. package/lib/common/src/components/Modal/index.js +2 -0
  31. package/lib/common/src/components/MyResizeObserver.d.ts +10 -0
  32. package/lib/common/src/components/MyResizeObserver.js +94 -0
  33. package/lib/common/src/components/Other.d.ts +9 -0
  34. package/lib/common/src/components/Other.js +31 -0
  35. package/lib/common/src/components/Parameters.d.ts +10 -0
  36. package/lib/common/src/components/Parameters.js +24 -0
  37. package/lib/common/src/components/ParametersEngine.d.ts +8 -0
  38. package/lib/common/src/components/ParametersEngine.js +373 -0
  39. package/lib/common/src/components/index.d.ts +9 -0
  40. package/lib/common/src/components/index.js +9 -0
  41. package/lib/common/src/hooks/index.d.ts +3 -0
  42. package/lib/common/src/hooks/index.js +3 -0
  43. package/lib/common/src/hooks/useAddDownAnyKey.d.ts +5 -0
  44. package/lib/common/src/hooks/useAddDownAnyKey.js +22 -0
  45. package/lib/common/src/hooks/useDraggable.d.ts +15 -0
  46. package/lib/common/src/hooks/useDraggable.js +134 -0
  47. package/lib/common/src/hooks/useOutside.d.ts +40 -0
  48. package/lib/common/src/hooks/useOutside.js +68 -0
  49. package/lib/common/src/logs/logs.d.ts +163 -0
  50. package/lib/common/src/logs/logs.js +249 -0
  51. package/lib/common/src/logs/logs3.d.ts +63 -0
  52. package/lib/common/src/logs/logs3.js +245 -0
  53. package/lib/common/src/logs/miniLogs.d.ts +5 -0
  54. package/lib/common/src/logs/miniLogs.js +51 -0
  55. package/lib/common/src/menu/menu.d.ts +72 -0
  56. package/lib/common/src/menu/menu.js +230 -0
  57. package/lib/common/src/menu/menuMouse.d.ts +21 -0
  58. package/lib/common/src/menu/menuMouse.js +32 -0
  59. package/lib/common/src/menu/menuR.d.ts +17 -0
  60. package/lib/common/src/menu/menuR.js +116 -0
  61. package/lib/common/src/myChart/1/myChart.d.ts +40 -0
  62. package/lib/common/src/myChart/1/myChart.js +306 -0
  63. package/lib/common/src/myChart/1/myChartTest.d.ts +1 -0
  64. package/lib/common/src/myChart/1/myChartTest.js +45 -0
  65. package/lib/common/src/myChart/chartEngine/chartEngineReact.d.ts +164 -0
  66. package/lib/common/src/myChart/chartEngine/chartEngineReact.js +834 -0
  67. package/lib/common/src/styles/index.d.ts +1 -0
  68. package/lib/common/src/styles/index.js +1 -0
  69. package/lib/common/src/styles/styleGrid.d.ts +20 -0
  70. package/lib/common/src/styles/styleGrid.js +50 -0
  71. package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +42 -0
  72. package/lib/common/src/utils/applyTransactionAsyncUpdate.js +97 -0
  73. package/lib/common/src/utils/arrayPromise.d.ts +5 -0
  74. package/lib/common/src/utils/arrayPromise.js +16 -0
  75. package/lib/common/src/utils/cache.d.ts +31 -0
  76. package/lib/common/src/utils/cache.js +119 -0
  77. package/lib/common/src/utils/index.d.ts +6 -0
  78. package/lib/common/src/utils/index.js +6 -0
  79. package/lib/common/src/utils/inputAutoStep.d.ts +4 -0
  80. package/lib/common/src/utils/inputAutoStep.js +79 -0
  81. package/lib/common/src/utils/mapMemory.d.ts +26 -0
  82. package/lib/common/src/utils/mapMemory.js +72 -0
  83. package/lib/common/src/utils/pageVisibilityContext.d.ts +5 -0
  84. package/lib/common/src/utils/pageVisibilityContext.js +18 -0
  85. package/lib/common/updateBy.d.ts +12 -0
  86. package/lib/common/updateBy.js +88 -0
  87. package/lib/index.d.ts +1 -0
  88. package/lib/index.js +3 -0
  89. package/lib/style/menuRight.css +139 -0
  90. package/lib/style/style.css +334 -0
  91. package/lib/tsconfig.json +6 -0
  92. package/package.json +50 -0
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { tMenuReact } from "./menu";
3
+ export declare function GetMenuR(): {
4
+ /**
5
+ * Управление глобальной переменной `bb`, предотвращающей множественное
6
+ * открытие меню.
7
+ */
8
+ bb(b?: boolean): boolean | undefined;
9
+ MenuR: ({ children, other, statusOn, onUnClick, zIndex, className }: {
10
+ children: React.ReactElement;
11
+ zIndex?: number;
12
+ other?: () => (tMenuReact)[];
13
+ statusOn?: boolean;
14
+ onUnClick?: (e: boolean) => void;
15
+ className?: (active?: boolean) => string;
16
+ }) => import("react/jsx-runtime").JSX.Element;
17
+ };
@@ -0,0 +1,116 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useEffect, useRef } from "react";
3
+ import { MenuBase } from "./menu";
4
+ import { DivOutsideClick } from "../hooks";
5
+ // Функция-обёртка для создания компонента MenuR и управления глобальной переменной `bb`
6
+ export function GetMenuR() {
7
+ let bb = false; // Глобальная переменная, предотвращающая множественное открытие меню (флаг активности)
8
+ // Основной компонент MenuR
9
+ function MenuR({ children, other = () => [], statusOn = true, onUnClick, zIndex, className }) {
10
+ const data = { x: 0, y: 0 }; // Текущая позиция элемента, где происходит взаимодействие
11
+ const [show, setShow] = React.useState({ status: false });
12
+ useEffect(() => {
13
+ return () => {
14
+ bb = false; // Сбрасываем флаг активности при размонтировании компонента
15
+ };
16
+ }, []);
17
+ const timeEvent = useRef(Date.now()); // Временная метка для отслеживания двойных кликов
18
+ let x = 0, y = 0; // Текущие координаты касания
19
+ const touchTime = useRef(null); // Время начала касания
20
+ return (_jsxs("div", { className: "maxSize", style: { position: "relative" },
21
+ // Для отключения стандартного контекстного меню
22
+ onContextMenu: e => {
23
+ if (statusOn) {
24
+ e.preventDefault();
25
+ e.stopPropagation();
26
+ }
27
+ },
28
+ // Определение позиции взаимодействия для текущего элемента (например, контейнера)
29
+ ref: (e) => {
30
+ if (e) {
31
+ const r = e.getBoundingClientRect();
32
+ data.x = r.x;
33
+ data.y = r.y;
34
+ }
35
+ },
36
+ // Запоминаем начальные координаты касания
37
+ onTouchStart: (e) => {
38
+ if (x == 0)
39
+ x = e.touches[0].screenX;
40
+ if (y == 0)
41
+ y = e.touches[0].screenY;
42
+ touchTime.current = Date.now();
43
+ },
44
+ // Проверка на значительное смещение (чтобы не показывать меню при скролле)
45
+ onTouchMove: (e) => {
46
+ let x2 = e.touches[0].screenX;
47
+ let y2 = e.touches[0].screenY;
48
+ let pX = e.touches[0].pageX;
49
+ let pY = e.touches[0].pageY;
50
+ if ((Math.abs(x2 - x) / pX > 0.05) || (Math.abs(y2 - y) / pY > 0.05)) {
51
+ touchTime.current = null; // Если смещение слишком большое, отключаем показ меню
52
+ }
53
+ },
54
+ // Определяем долгое касание для вызова контекстного меню
55
+ onTouchEnd: (e) => {
56
+ if (statusOn) {
57
+ if (touchTime.current && Date.now() - touchTime.current > 300) {
58
+ // Если прошло больше 300 мс — считываем как долгое нажатие
59
+ touchTime.current = null;
60
+ x = y = 0;
61
+ if (bb)
62
+ return; // Если меню уже активно, выходим
63
+ bb = true;
64
+ setShow({
65
+ status: true,
66
+ // Устанавливаем координаты меню относительно элемента
67
+ coordinate: {
68
+ x: e.changedTouches[0].clientX - data.x,
69
+ y: e.changedTouches[0].clientY - data.y
70
+ }
71
+ });
72
+ }
73
+ }
74
+ },
75
+ // Отслеживаем двойной клик
76
+ onDoubleClick: (event) => {
77
+ timeEvent.current = Date.now();
78
+ },
79
+ // Обрабатываем клик правой кнопкой мыши или двойной клик
80
+ onMouseUp: (event) => {
81
+ if (statusOn) {
82
+ if (event.button == 2 || Date.now() - timeEvent.current < 300) {
83
+ if (bb)
84
+ return; // Если меню уже активно, ничего не делаем
85
+ bb = true;
86
+ setShow({ status: true, coordinate: { x: event.clientX - data.x, y: event.clientY - data.y } });
87
+ }
88
+ }
89
+ }, children: [children /* Дочерний элемент, внутри которого отслеживаются события */, show.status && statusOn && (
90
+ // Показываем контекстное меню
91
+ _jsx(DivOutsideClick, { outsideClick: () => {
92
+ onUnClick?.(false); // Вызов обработчика при закрытии меню
93
+ if (!bb)
94
+ return;
95
+ bb = false; // Меню больше не активно
96
+ setShow({ status: false }); // Скрываем меню
97
+ }, children: _jsx(MenuBase, { className: className, data: [
98
+ ...(show.plusMenu ?? []),
99
+ ...other().filter(e => e)
100
+ ], coordinate: { ...show.coordinate }, zIndex: zIndex }) }))] }));
101
+ }
102
+ return {
103
+ /**
104
+ * Управление глобальной переменной `bb`, предотвращающей множественное
105
+ * открытие меню.
106
+ */
107
+ bb(b) {
108
+ if (b != undefined) {
109
+ bb = b;
110
+ }
111
+ else
112
+ return bb;
113
+ },
114
+ MenuR // Возвращаем созданный компонент
115
+ };
116
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Точка на графике: время (time) и цена (price).
3
+ */
4
+ export interface IChartPoint {
5
+ time: number;
6
+ price: number;
7
+ }
8
+ /**
9
+ * Настройки канваса.
10
+ */
11
+ export interface IChartConfig {
12
+ container: HTMLElement;
13
+ width?: number;
14
+ height?: number;
15
+ autoScaleY?: boolean;
16
+ showTimeAxis?: boolean;
17
+ showPriceAxis?: boolean;
18
+ }
19
+ /**
20
+ * Интерфейс объекта, который возвращает createChartCanvas:
21
+ * методы управления зумом, прокруткой и т.д.
22
+ */
23
+ export interface IChartCanvas {
24
+ appendData(points: IChartPoint | IChartPoint[]): void;
25
+ clearData(): void;
26
+ scrollX(deltaPx: number): void;
27
+ zoomX(factor: number, centerPx?: number): void;
28
+ jumpToStart(): void;
29
+ jumpToEnd(): void;
30
+ jumpToIndex(i: number): void;
31
+ setAutoScaleY(enabled: boolean): void;
32
+ setShowTimeAxis(enabled: boolean): void;
33
+ setShowPriceAxis(enabled: boolean): void;
34
+ draw(): void;
35
+ destroy(): void;
36
+ }
37
+ /**
38
+ * Создаём функциональный канвас с возможностью зума колёсиком.
39
+ */
40
+ export declare function createChartCanvas(config: IChartConfig): IChartCanvas;
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Создаём функциональный канвас с возможностью зума колёсиком.
3
+ */
4
+ export function createChartCanvas(config) {
5
+ // Создаём <canvas> и вставляем в container
6
+ const canvas = document.createElement("canvas");
7
+ config.container.appendChild(canvas);
8
+ // Состояние (state) в замыкании
9
+ let state = {
10
+ width: (config.width ?? config.container.offsetWidth) || 800,
11
+ height: (config.height ?? config.container.offsetHeight) || 400,
12
+ data: [],
13
+ offsetX: 0,
14
+ scaleX: 5, // пикселей на «шаг» (индекс точки)
15
+ autoScaleY: config.autoScaleY ?? true,
16
+ showTimeAxis: config.showTimeAxis ?? true,
17
+ showPriceAxis: config.showPriceAxis ?? true,
18
+ needsRender: true,
19
+ isDragging: false,
20
+ lastDragX: 0,
21
+ };
22
+ const ctx = canvas.getContext("2d");
23
+ function resizeCanvas() {
24
+ state.width = (config.width ?? config.container.offsetWidth) || 800;
25
+ state.height = (config.height ?? config.container.offsetHeight) || 400;
26
+ canvas.width = state.width;
27
+ canvas.height = state.height;
28
+ state.needsRender = true;
29
+ }
30
+ resizeCanvas();
31
+ // ~~~ Отрисовка ~~~
32
+ function draw() {
33
+ if (!state.needsRender)
34
+ return;
35
+ state.needsRender = false;
36
+ ctx.clearRect(0, 0, state.width, state.height);
37
+ if (state.data.length === 0) {
38
+ drawAxes();
39
+ return;
40
+ }
41
+ // Рассчитываем, какие индексы данных видны на экране
42
+ const minIndex = Math.floor(-state.offsetX / state.scaleX);
43
+ const maxIndex = Math.ceil((state.width - state.offsetX) / state.scaleX);
44
+ const startIndex = Math.max(0, minIndex);
45
+ const endIndex = Math.min(state.data.length - 1, maxIndex);
46
+ if (startIndex > endIndex) {
47
+ drawAxes();
48
+ return;
49
+ }
50
+ // Ищем minY и maxY для видимых точек (если autoScaleY=true)
51
+ let minY = Infinity;
52
+ let maxY = -Infinity;
53
+ for (let i = startIndex; i <= endIndex; i++) {
54
+ const p = state.data[i];
55
+ if (p.price < minY)
56
+ minY = p.price;
57
+ if (p.price > maxY)
58
+ maxY = p.price;
59
+ }
60
+ if (minY === Infinity) {
61
+ // Нет видимых точек
62
+ drawAxes();
63
+ return;
64
+ }
65
+ if (!state.autoScaleY) {
66
+ // Допустим, зафиксируем 0..100, если не хотим авто
67
+ minY = 0;
68
+ maxY = 100;
69
+ }
70
+ const rangeY = maxY - minY || 1;
71
+ const marginTop = 20;
72
+ const marginBottom = state.showTimeAxis ? 20 : 0;
73
+ const chartHeight = state.height - marginTop - marginBottom;
74
+ function toScreenX(i) {
75
+ return i * state.scaleX + state.offsetX;
76
+ }
77
+ function toScreenY(price) {
78
+ return marginTop + chartHeight - ((price - minY) / rangeY) * chartHeight;
79
+ }
80
+ // Рисуем линию
81
+ ctx.beginPath();
82
+ let first = true;
83
+ for (let i = startIndex; i <= endIndex; i++) {
84
+ const p = state.data[i];
85
+ const x = toScreenX(i);
86
+ const y = toScreenY(p.price);
87
+ if (first) {
88
+ ctx.moveTo(x, y);
89
+ first = false;
90
+ }
91
+ else {
92
+ ctx.lineTo(x, y);
93
+ }
94
+ }
95
+ ctx.strokeStyle = "rgb(0,180,0)";
96
+ ctx.lineWidth = 2;
97
+ ctx.stroke();
98
+ // Рисуем оси и подписи
99
+ drawAxes(minY, maxY, startIndex, endIndex, toScreenX, toScreenY);
100
+ }
101
+ function drawAxes(minY, maxY, startIndex, endIndex, toX, toY) {
102
+ ctx.save();
103
+ ctx.strokeStyle = "rgba(0,0,0,0.4)";
104
+ ctx.fillStyle = "rgba(0,0,0,0.6)";
105
+ ctx.font = "12px sans-serif";
106
+ // Ось X (внизу)
107
+ if (state.showTimeAxis) {
108
+ ctx.beginPath();
109
+ ctx.moveTo(0, state.height - 0.5);
110
+ ctx.lineTo(state.width, state.height - 0.5);
111
+ ctx.stroke();
112
+ // Подписи времени
113
+ if (startIndex != null && endIndex != null && toX) {
114
+ const step = Math.max(1, Math.floor((endIndex - startIndex) / 5));
115
+ for (let i = startIndex; i <= endIndex; i += step) {
116
+ const p = state.data[i];
117
+ const x = toX(i);
118
+ if (x < 0 || x > state.width)
119
+ continue;
120
+ const dateStr = new Date(p.time).toLocaleTimeString();
121
+ ctx.fillText(dateStr, x, state.height - 5);
122
+ ctx.beginPath();
123
+ ctx.moveTo(x, state.height - 15);
124
+ ctx.lineTo(x, state.height);
125
+ ctx.stroke();
126
+ }
127
+ }
128
+ }
129
+ // Ось Y (слева)
130
+ if (state.showPriceAxis) {
131
+ ctx.beginPath();
132
+ ctx.moveTo(0.5, 0);
133
+ ctx.lineTo(0.5, state.height);
134
+ ctx.stroke();
135
+ if (minY !== undefined && maxY !== undefined && toY) {
136
+ const yMin = toY(minY);
137
+ const yMax = toY(maxY);
138
+ ctx.fillText(String(minY.toFixed(2)), 5, yMin - 2);
139
+ ctx.beginPath();
140
+ ctx.moveTo(0, yMin);
141
+ ctx.lineTo(5, yMin);
142
+ ctx.stroke();
143
+ ctx.fillText(String(maxY.toFixed(2)), 5, yMax - 2);
144
+ ctx.beginPath();
145
+ ctx.moveTo(0, yMax);
146
+ ctx.lineTo(5, yMax);
147
+ ctx.stroke();
148
+ }
149
+ }
150
+ ctx.restore();
151
+ }
152
+ let animationFrameId = null;
153
+ let destroyed = false;
154
+ let docListenersActive = false;
155
+ function animate() {
156
+ if (destroyed)
157
+ return;
158
+ if (!canvas.isConnected) {
159
+ destroy();
160
+ return;
161
+ }
162
+ if (state.needsRender) {
163
+ draw();
164
+ }
165
+ animationFrameId = requestAnimationFrame(animate);
166
+ }
167
+ animate();
168
+ // ~~~ Методы API ~~~
169
+ function appendData(points) {
170
+ if (!Array.isArray(points))
171
+ points = [points];
172
+ state.data.push(...points);
173
+ state.needsRender = true;
174
+ }
175
+ function clearData() {
176
+ state.data = [];
177
+ state.offsetX = 0;
178
+ state.needsRender = true;
179
+ }
180
+ function scrollX(deltaPx) {
181
+ state.offsetX += deltaPx;
182
+ state.needsRender = true;
183
+ }
184
+ function zoomX(factor, centerPx = state.width / 2) {
185
+ // Определяем, какой индекс сейчас под centerPx
186
+ const i = (centerPx - state.offsetX) / state.scaleX;
187
+ state.scaleX *= factor;
188
+ // Ограничиваем масштаб
189
+ if (state.scaleX < 0.1)
190
+ state.scaleX = 0.1;
191
+ if (state.scaleX > 2000)
192
+ state.scaleX = 2000;
193
+ // Сдвигаем offsetX так, чтобы та же точка осталась под курсором
194
+ state.offsetX = centerPx - i * state.scaleX;
195
+ state.needsRender = true;
196
+ }
197
+ function jumpToStart() {
198
+ state.offsetX = 0;
199
+ state.needsRender = true;
200
+ }
201
+ function jumpToEnd() {
202
+ const i = state.data.length - 1;
203
+ if (i < 0)
204
+ return;
205
+ const desiredX = state.width * 0.9;
206
+ state.offsetX = desiredX - i * state.scaleX;
207
+ state.needsRender = true;
208
+ }
209
+ function jumpToIndex(i) {
210
+ if (i < 0 || i >= state.data.length)
211
+ return;
212
+ const centerPx = state.width / 2;
213
+ state.offsetX = centerPx - i * state.scaleX;
214
+ state.needsRender = true;
215
+ }
216
+ function setAutoScaleY(enabled) {
217
+ state.autoScaleY = enabled;
218
+ state.needsRender = true;
219
+ }
220
+ function setShowTimeAxis(enabled) {
221
+ state.showTimeAxis = enabled;
222
+ state.needsRender = true;
223
+ }
224
+ function setShowPriceAxis(enabled) {
225
+ state.showPriceAxis = enabled;
226
+ state.needsRender = true;
227
+ }
228
+ function drawManually() {
229
+ // Принудительный вызов отрисовки
230
+ state.needsRender = true;
231
+ draw();
232
+ }
233
+ // ~~~ События мыши ~~~
234
+ const addDocListeners = () => {
235
+ if (docListenersActive)
236
+ return;
237
+ document.addEventListener("mousemove", onMouseMove);
238
+ document.addEventListener("mouseup", onMouseUp);
239
+ docListenersActive = true;
240
+ };
241
+ const removeDocListeners = () => {
242
+ if (!docListenersActive)
243
+ return;
244
+ document.removeEventListener("mousemove", onMouseMove);
245
+ document.removeEventListener("mouseup", onMouseUp);
246
+ docListenersActive = false;
247
+ };
248
+ const onMouseDown = (e) => {
249
+ state.isDragging = true;
250
+ state.lastDragX = e.clientX;
251
+ addDocListeners();
252
+ };
253
+ const onMouseMove = (e) => {
254
+ if (state.isDragging) {
255
+ const dx = e.clientX - state.lastDragX;
256
+ state.lastDragX = e.clientX;
257
+ scrollX(dx); // тянем график
258
+ }
259
+ };
260
+ const onMouseUp = () => {
261
+ state.isDragging = false;
262
+ removeDocListeners();
263
+ };
264
+ // ===> Привязка зума к скроллу мышки <===
265
+ const onWheel = (e) => {
266
+ e.preventDefault();
267
+ // e.deltaY > 0 => крутим вниз => factor < 1 => отдаляем
268
+ // e.deltaY < 0 => крутим вверх => factor > 1 => приближаем
269
+ const factor = e.deltaY < 0 ? 1.1 : 0.9;
270
+ const rect = canvas.getBoundingClientRect();
271
+ const mouseX = e.clientX - rect.left;
272
+ zoomX(factor, mouseX);
273
+ };
274
+ canvas.addEventListener("mousedown", onMouseDown);
275
+ canvas.addEventListener("wheel", onWheel, { passive: false });
276
+ function destroy() {
277
+ if (destroyed)
278
+ return;
279
+ destroyed = true;
280
+ canvas.removeEventListener("mousedown", onMouseDown);
281
+ removeDocListeners();
282
+ canvas.removeEventListener("wheel", onWheel);
283
+ if (animationFrameId !== null) {
284
+ cancelAnimationFrame(animationFrameId);
285
+ animationFrameId = null;
286
+ }
287
+ if (canvas.parentElement) {
288
+ canvas.parentElement.removeChild(canvas);
289
+ }
290
+ }
291
+ // Собираем и возвращаем API
292
+ return {
293
+ appendData,
294
+ clearData,
295
+ scrollX,
296
+ zoomX,
297
+ jumpToStart,
298
+ jumpToEnd,
299
+ jumpToIndex,
300
+ setAutoScaleY,
301
+ setShowTimeAxis,
302
+ setShowPriceAxis,
303
+ draw: drawManually,
304
+ destroy,
305
+ };
306
+ }
@@ -0,0 +1 @@
1
+ export declare function ChartDemo(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import { createChartCanvas } from "./myChart";
4
+ export function ChartDemo() {
5
+ const chartRef = useRef(null);
6
+ const containerRef = useRef(null);
7
+ useEffect(() => {
8
+ if (!containerRef.current)
9
+ return;
10
+ // Создаём график
11
+ const chart = createChartCanvas({
12
+ container: containerRef.current,
13
+ width: 600,
14
+ height: 300,
15
+ autoScaleY: true,
16
+ showTimeAxis: true,
17
+ showPriceAxis: true,
18
+ });
19
+ chartRef.current = chart;
20
+ // Добавим тестовые данные
21
+ const now = Date.now();
22
+ const demoData = [];
23
+ for (let i = 0; i < 50; i++) {
24
+ demoData.push({
25
+ time: now + i * 1000,
26
+ price: 100 + Math.sin(i / 5) * 10,
27
+ });
28
+ }
29
+ chart.appendData(demoData);
30
+ chart.jumpToEnd();
31
+ // Пример «потокового» добавления новых точек
32
+ const timer = setInterval(() => {
33
+ const last = demoData[demoData.length - 1];
34
+ const newPoint = {
35
+ time: last.time + 1000,
36
+ price: last.price + (Math.random() - 0.5) * 2,
37
+ };
38
+ demoData.push(newPoint);
39
+ chart.appendData(newPoint);
40
+ // optional: chart.jumpToEnd();
41
+ }, 2000);
42
+ return () => clearInterval(timer);
43
+ }, []);
44
+ return (_jsxs("div", { style: { padding: 20 }, children: [_jsx("h2", { children: "Chart with Wheel Zoom" }), _jsx("div", { ref: containerRef, style: { border: "1px solid black", width: 620, height: 320 } }), _jsxs("div", { style: { marginTop: 10 }, children: [_jsx("button", { onClick: () => chartRef.current?.zoomX(1.1), children: "Zoom In" }), _jsx("button", { onClick: () => chartRef.current?.zoomX(0.9), children: "Zoom Out" }), _jsx("button", { onClick: () => chartRef.current?.scrollX(50), children: "Scroll Left" }), _jsx("button", { onClick: () => chartRef.current?.scrollX(-50), children: "Scroll Right" }), _jsx("button", { onClick: () => chartRef.current?.jumpToStart(), children: "Start" }), _jsx("button", { onClick: () => chartRef.current?.jumpToEnd(), children: "End" })] })] }));
45
+ }
@@ -0,0 +1,164 @@
1
+ /*************************************************************
2
+ * chartEngine.tsx
3
+ * Обновлённый пример, где:
4
+ * - Ширина панелей = 100% от Canvas
5
+ * - Высота панелей задаётся в процентах (heightPct)
6
+ * - Последняя панель всегда "дотягивает" до 100%
7
+ *************************************************************/
8
+ import React from 'react';
9
+ /**
10
+ * Базовые типы данных
11
+ */
12
+ export interface DataPoint {
13
+ x: number;
14
+ y: number;
15
+ }
16
+ export type ChartType = 'line' | 'bar';
17
+ export interface DataSetStyle {
18
+ strokeColor?: string;
19
+ fillColor?: string;
20
+ barColor?: string;
21
+ lineWidth?: number;
22
+ gradientFill?: boolean;
23
+ }
24
+ export interface MinMaxChunk {
25
+ xStart: number;
26
+ xEnd: number;
27
+ minY: number;
28
+ maxY: number;
29
+ }
30
+ export interface DataSet {
31
+ id: string;
32
+ type: ChartType;
33
+ data: DataPoint[];
34
+ style: DataSetStyle;
35
+ chunkSize: number;
36
+ minMaxChunks: MinMaxChunk[];
37
+ getMinMaxInRange(rangeX1: number, rangeX2: number): {
38
+ minY: number;
39
+ maxY: number;
40
+ };
41
+ addData(newPoints: DataPoint | DataPoint[]): void;
42
+ }
43
+ export interface CreateDataSetParams {
44
+ id: string;
45
+ type?: ChartType;
46
+ data?: DataPoint[];
47
+ style?: DataSetStyle;
48
+ chunkSize?: number;
49
+ }
50
+ /**
51
+ * Фабрика DataSet
52
+ */
53
+ export declare function createDataSet(params: CreateDataSetParams): DataSet;
54
+ /**
55
+ * DataModel
56
+ */
57
+ export interface DataModel {
58
+ addDataSet(params: CreateDataSetParams): DataSet;
59
+ getAllDataSets(): DataSet[];
60
+ getGlobalMinMaxY(x1: number, x2: number, filterFn?: (ds: DataSet) => boolean): {
61
+ minY: number;
62
+ maxY: number;
63
+ };
64
+ }
65
+ export declare function createDataModel(): DataModel;
66
+ /**
67
+ * PanelManager
68
+ * Вместо pixel-высоты, используем heightPct (процент).
69
+ */
70
+ export interface Panel {
71
+ id: string;
72
+ left: number;
73
+ top: number;
74
+ width: number;
75
+ height: number;
76
+ heightPct: number;
77
+ dataSets: DataSet[];
78
+ verticalRange: {
79
+ minY: number;
80
+ maxY: number;
81
+ };
82
+ autoFocusY: boolean;
83
+ resizable?: boolean;
84
+ }
85
+ export interface PanelManager {
86
+ panels: Panel[];
87
+ addPanel(config: {
88
+ id: string;
89
+ /** Процент высоты: 0..100, последняя панель добирает остаток */
90
+ heightPct?: number;
91
+ dataSets: DataSet[];
92
+ autoFocusY?: boolean;
93
+ resizable?: boolean;
94
+ }): void;
95
+ layoutPanels(containerWidth: number, containerHeight: number): void;
96
+ resizePanel(panelId: string, deltaPx: number, containerHeight: number): void;
97
+ }
98
+ export declare function createPanelManager(): PanelManager;
99
+ /**
100
+ * Renderer (ось Y справа, LOD для линий)
101
+ */
102
+ export interface Renderer {
103
+ drawPanel(ctx: CanvasRenderingContext2D, panel: Panel, transform: Transform, globalTimeRange: {
104
+ xMin: number;
105
+ xMax: number;
106
+ }, crosshair: {
107
+ x: number;
108
+ y: number;
109
+ } | null, isYRightAxis: boolean): void;
110
+ }
111
+ export interface Transform {
112
+ offsetX: number;
113
+ scaleX: number;
114
+ offsetY?: number;
115
+ scaleY?: number;
116
+ }
117
+ export declare function createRenderer(): Renderer;
118
+ /**
119
+ * Interaction
120
+ */
121
+ export interface Interaction {
122
+ initEvents(canvas: HTMLCanvasElement): void;
123
+ getCrosshairPos(): {
124
+ x: number;
125
+ y: number;
126
+ } | null;
127
+ destroy(): void;
128
+ }
129
+ export declare function createInteraction(canvas: HTMLCanvasElement, getTransform: () => Transform, setTransform: (t: Transform) => void, getPanels: () => Panel[], onTransformChanged: () => void, onToggleAutoFocusY: (panel: Panel) => void, panelManager: PanelManager, getContainerSize: () => {
130
+ width: number;
131
+ height: number;
132
+ }): Interaction;
133
+ /**
134
+ * ChartEngine
135
+ */
136
+ export interface ChartEngine {
137
+ init(): void;
138
+ destroy(): void;
139
+ attachToContainer(container: HTMLElement): void;
140
+ createDataSet(params: CreateDataSetParams): DataSet;
141
+ addPanel(config: {
142
+ id: string;
143
+ dataSets: DataSet[];
144
+ heightPct?: number;
145
+ autoFocusY?: boolean;
146
+ resizable?: boolean;
147
+ }): void;
148
+ canvas: HTMLCanvasElement;
149
+ dataModel: DataModel;
150
+ panelManager: PanelManager;
151
+ renderer: Renderer;
152
+ }
153
+ /**
154
+ * Фабричная функция движка
155
+ */
156
+ export declare function createChartEngine(canvas: HTMLCanvasElement): ChartEngine;
157
+ /**
158
+ * Пример генерации данных
159
+ */
160
+ export declare function generateIncrementalData(startX: number, count: number, startY: number, maxDelta: number): DataPoint[];
161
+ /**
162
+ * Пример React-компонента MyChart
163
+ */
164
+ export declare const MyChartEngine: React.FC;