wenay-react2 1.0.19 → 1.0.21

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 (77) hide show
  1. package/README.md +383 -382
  2. package/lib/common/api.d.ts +28 -0
  3. package/lib/common/api.js +41 -16
  4. package/lib/common/src/components/Buttons/MiniButton.d.ts +1 -0
  5. package/lib/common/src/components/Buttons/MiniButton.js +2 -2
  6. package/lib/common/src/components/Dnd/DraggableOutlineDiv.d.ts +1 -0
  7. package/lib/common/src/components/Dnd/DraggableOutlineDiv.js +10 -7
  8. package/lib/common/src/components/Dnd/RNDFunc.js +28 -17
  9. package/lib/common/src/components/Dnd/RNDFunc3.d.ts +27 -17
  10. package/lib/common/src/components/Dnd/RNDFunc3.js +107 -107
  11. package/lib/common/src/components/Dnd/Resizable.js +3 -3
  12. package/lib/common/src/components/Input.js +3 -3
  13. package/lib/common/src/components/Menu/RightMenu.js +17 -18
  14. package/lib/common/src/components/Menu/StickerMenu.d.ts +1 -3
  15. package/lib/common/src/components/Menu/StickerMenu.js +42 -28
  16. package/lib/common/src/components/Menu/index.d.ts +1 -1
  17. package/lib/common/src/components/Menu/index.js +1 -1
  18. package/lib/common/src/components/Modal/LeftModal.d.ts +14 -12
  19. package/lib/common/src/components/Modal/LeftModal.js +34 -52
  20. package/lib/common/src/components/Modal/Modal.d.ts +26 -12
  21. package/lib/common/src/components/Modal/Modal.js +66 -89
  22. package/lib/common/src/components/Modal/ModalContextProvider.d.ts +20 -3
  23. package/lib/common/src/components/Modal/ModalContextProvider.js +35 -8
  24. package/lib/common/src/components/MyResizeObserver.js +15 -18
  25. package/lib/common/src/components/Other.js +6 -5
  26. package/lib/common/src/components/Parameters.js +8 -7
  27. package/lib/common/src/components/ParametersEngine.js +36 -59
  28. package/lib/common/src/grid/agGrid4/agGrid4.d.ts +53 -0
  29. package/lib/common/src/grid/agGrid4/agGrid4.js +132 -0
  30. package/lib/common/src/grid/agGrid4/core.d.ts +56 -0
  31. package/lib/common/src/grid/agGrid4/core.js +127 -0
  32. package/lib/common/src/grid/agGrid4/index.d.ts +6 -0
  33. package/lib/common/src/grid/agGrid4/index.js +3 -0
  34. package/lib/common/src/grid/agGrid4/theme.d.ts +7 -0
  35. package/lib/common/src/grid/agGrid4/theme.js +18 -0
  36. package/lib/common/src/hooks/useAddDownAnyKey.d.ts +21 -2
  37. package/lib/common/src/hooks/useAddDownAnyKey.js +46 -9
  38. package/lib/common/src/hooks/useDraggable.d.ts +20 -2
  39. package/lib/common/src/hooks/useDraggable.js +146 -115
  40. package/lib/common/src/hooks/useOutside.d.ts +24 -4
  41. package/lib/common/src/hooks/useOutside.js +77 -26
  42. package/lib/common/src/logs/logs.d.ts +5 -2
  43. package/lib/common/src/logs/logs.js +18 -34
  44. package/lib/common/src/logs/logs3.d.ts +9 -9
  45. package/lib/common/src/logs/logs3.js +48 -39
  46. package/lib/common/src/logs/miniLogs.d.ts +3 -3
  47. package/lib/common/src/logs/miniLogs.js +28 -36
  48. package/lib/common/src/menu/menu.d.ts +18 -18
  49. package/lib/common/src/menu/menu.js +49 -25
  50. package/lib/common/src/menu/menuMouse.d.ts +2 -1
  51. package/lib/common/src/menu/menuMouse.js +5 -4
  52. package/lib/common/src/menu/menuR.d.ts +4 -3
  53. package/lib/common/src/menu/menuR.js +42 -38
  54. package/lib/common/src/myChart/1/myChart.d.ts +5 -5
  55. package/lib/common/src/myChart/1/myChart.js +58 -47
  56. package/lib/common/src/myChart/1/myChartTest.js +8 -4
  57. package/lib/common/src/myChart/chartEngine/chartEngineReact.d.ts +24 -27
  58. package/lib/common/src/myChart/chartEngine/chartEngineReact.js +189 -118
  59. package/lib/common/src/styles/styleGrid.d.ts +2 -1
  60. package/lib/common/src/styles/styleGrid.js +10 -12
  61. package/lib/common/src/styles/tokens.d.ts +39 -0
  62. package/lib/common/src/styles/tokens.js +40 -0
  63. package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +7 -0
  64. package/lib/common/src/utils/applyTransactionAsyncUpdate.js +25 -19
  65. package/lib/common/src/utils/arrayPromise.d.ts +6 -0
  66. package/lib/common/src/utils/arrayPromise.js +7 -1
  67. package/lib/common/src/utils/cache.js +32 -24
  68. package/lib/common/src/utils/inputAutoStep.d.ts +1 -1
  69. package/lib/common/src/utils/inputAutoStep.js +30 -19
  70. package/lib/common/src/utils/mapMemory.js +15 -15
  71. package/lib/common/updateBy.d.ts +21 -2
  72. package/lib/common/updateBy.js +66 -131
  73. package/lib/index.js +0 -2
  74. package/lib/style/menuRight.css +13 -26
  75. package/lib/style/style.css +51 -39
  76. package/lib/style/tokens.css +34 -0
  77. package/package.json +2 -2
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /*************************************************************
3
3
  * chartEngine.tsx
4
- * Обновлённый пример, где:
5
- * - Ширина панелей = 100% от Canvas
6
- * - Высота панелей задаётся в процентах (heightPct)
7
- * - Последняя панель всегда "дотягивает" до 100%
4
+ * Updated example where:
5
+ * - Panel width = 100% of Canvas
6
+ * - Panel height is defined in percent (heightPct)
7
+ * - The last panel always fills up to 100%
8
8
  *************************************************************/
9
9
  import { useRef, useEffect } from 'react';
10
10
  /**
11
- * Фабрика DataSet
11
+ * DataSet factory
12
12
  */
13
13
  export function createDataSet(params) {
14
14
  const { id, type = 'line', data = [], style = {}, chunkSize = 100 } = params;
@@ -22,23 +22,24 @@ export function createDataSet(params) {
22
22
  const mergedStyle = { ...defaultStyle, ...style };
23
23
  let internalData = data.slice();
24
24
  let minMaxChunks = [];
25
+ function computeChunk(chunkIndex) {
26
+ const start = chunkIndex * chunkSize;
27
+ const end = Math.min(start + chunkSize, internalData.length);
28
+ let minY = Infinity;
29
+ let maxY = -Infinity;
30
+ for (let i = start; i < end; i++) {
31
+ const y = internalData[i].y;
32
+ if (y < minY)
33
+ minY = y;
34
+ if (y > maxY)
35
+ maxY = y;
36
+ }
37
+ return { xStart: internalData[start].x, xEnd: internalData[end - 1].x, minY, maxY };
38
+ }
25
39
  function buildMinMaxChunks() {
26
40
  minMaxChunks = [];
27
- if (internalData.length === 0)
28
- return;
29
- for (let i = 0; i < internalData.length; i += chunkSize) {
30
- const chunkData = internalData.slice(i, i + chunkSize);
31
- let minY = Infinity;
32
- let maxY = -Infinity;
33
- const xStart = chunkData[0].x;
34
- const xEnd = chunkData[chunkData.length - 1].x;
35
- for (const dp of chunkData) {
36
- if (dp.y < minY)
37
- minY = dp.y;
38
- if (dp.y > maxY)
39
- maxY = dp.y;
40
- }
41
- minMaxChunks.push({ xStart, xEnd, minY, maxY });
41
+ for (let c = 0; c * chunkSize < internalData.length; c++) {
42
+ minMaxChunks.push(computeChunk(c));
42
43
  }
43
44
  }
44
45
  buildMinMaxChunks();
@@ -64,26 +65,15 @@ export function createDataSet(params) {
64
65
  }
65
66
  function addData(newPoints) {
66
67
  const arr = Array.isArray(newPoints) ? newPoints : [newPoints];
68
+ if (arr.length === 0)
69
+ return;
70
+ const oldLength = internalData.length;
67
71
  internalData.push(...arr);
68
- const remainder = internalData.length % chunkSize;
69
- if (remainder <= arr.length) {
70
- buildMinMaxChunks();
71
- }
72
- else {
73
- const lastChunkIndex = Math.floor((internalData.length - 1) / chunkSize);
74
- const startIndex = lastChunkIndex * chunkSize;
75
- const chunkData = internalData.slice(startIndex, startIndex + chunkSize);
76
- let minY = Infinity;
77
- let maxY = -Infinity;
78
- for (const d of chunkData) {
79
- if (d.y < minY)
80
- minY = d.y;
81
- if (d.y > maxY)
82
- maxY = d.y;
83
- }
84
- const xStart = chunkData[0].x;
85
- const xEnd = chunkData[chunkData.length - 1].x;
86
- minMaxChunks[lastChunkIndex] = { xStart, xEnd, minY, maxY };
72
+ // recompute only the tail: previously crossing a chunk boundary rebuilt ALL chunks (O(n) per boundary)
73
+ const firstChunk = Math.floor(oldLength / chunkSize);
74
+ const lastChunk = Math.floor((internalData.length - 1) / chunkSize);
75
+ for (let c = firstChunk; c <= lastChunk; c++) {
76
+ minMaxChunks[c] = computeChunk(c);
87
77
  }
88
78
  }
89
79
  return {
@@ -92,7 +82,8 @@ export function createDataSet(params) {
92
82
  data: internalData,
93
83
  style: mergedStyle,
94
84
  chunkSize,
95
- minMaxChunks,
85
+ // buildMinMaxChunks reassigns the local array, so use a getter instead of a stale snapshot
86
+ get minMaxChunks() { return minMaxChunks; },
96
87
  getMinMaxInRange,
97
88
  addData
98
89
  };
@@ -140,7 +131,7 @@ export function createPanelManager() {
140
131
  top: 0,
141
132
  width: 0,
142
133
  height: 0,
143
- heightPct: config.heightPct ?? 20, // по умолчанию 20% (или любой другой)
134
+ heightPct: config.heightPct ?? 20, // default 20% (or any other value)
144
135
  dataSets: config.dataSets,
145
136
  verticalRange: { minY: 0, maxY: 1 },
146
137
  autoFocusY: config.autoFocusY !== false,
@@ -150,28 +141,28 @@ export function createPanelManager() {
150
141
  }
151
142
  /**
152
143
  * layoutPanels:
153
- * - Суммируем heightPct всех панелей (кроме последней).
154
- * - Последняя панель = 100% - сумма предыдущих (если вдруг сумма < 100).
155
- * - Переводим проценты в px, рассчитываем top/height.
144
+ * - Sum heightPct for all panels except the last one.
145
+ * - Last panel = 100% - sum of previous panels (if the sum is below 100).
146
+ * - Convert percents to px and calculate top/height.
156
147
  */
157
148
  function layoutPanels(containerWidth, containerHeight) {
158
- // Сумма процентов у всех панелей, кроме последней
149
+ // Percent sum for all panels except the last one
159
150
  if (panels.length === 0)
160
151
  return;
161
- // Рассчитываем сумму “заданных” процентов (кроме последней)
152
+ // Calculate the sum of assigned percents (except the last one)
162
153
  let totalAssignedPct = 0;
163
154
  for (let i = 0; i < panels.length - 1; i++) {
164
155
  totalAssignedPct += panels[i].heightPct;
165
156
  }
166
157
  if (totalAssignedPct > 100)
167
- totalAssignedPct = 100; // ограничим
168
- // Последней панели даём остаток
158
+ totalAssignedPct = 100; // clamp
159
+ // Give the remaining space to the last panel
169
160
  const lastPanel = panels[panels.length - 1];
170
161
  lastPanel.heightPct = 100 - totalAssignedPct;
171
- // Теперь вычисляем top/height
162
+ // Now calculate top/height
172
163
  let currentTopPx = 0;
173
164
  for (const p of panels) {
174
- // Преобразуем процент в px
165
+ // Convert percent to px
175
166
  const hPx = (p.heightPct / 100) * containerHeight;
176
167
  p.left = 0;
177
168
  p.top = currentTopPx;
@@ -181,21 +172,21 @@ export function createPanelManager() {
181
172
  }
182
173
  }
183
174
  /**
184
- * resizePanel: меняем процент высоты одной панели при “перетаскивании”.
185
- * deltaPx изменение высоты в px, превращаем в проценты.
175
+ * resizePanel: change one panel height percent while dragging.
176
+ * deltaPx is the height change in px, converted to percent.
186
177
  */
187
178
  function resizePanel(panelId, deltaPx, containerHeight) {
188
179
  const idx = panels.findIndex((p) => p.id === panelId);
189
- if (idx < 0 || idx >= panels.length)
180
+ if (idx < 0)
190
181
  return;
191
182
  const p = panels[idx];
192
- // Текущий процент
183
+ // Current percent
193
184
  const oldPct = p.heightPct;
194
185
  // px -> pct
195
186
  const deltaPct = (deltaPx / containerHeight) * 100;
196
187
  const newPct = p.heightPct + deltaPct;
197
- // Ограничим чтобы не уходило в отрицательные значения
198
- if (newPct < 5) { // Минимум 5% (условно)
188
+ // Clamp to avoid negative values
189
+ if (newPct < 5) { // Minimum 5% (arbitrary)
199
190
  p.heightPct = 5;
200
191
  }
201
192
  else if (newPct > 95) {
@@ -212,21 +203,41 @@ export function createPanelManager() {
212
203
  resizePanel
213
204
  };
214
205
  }
206
+ // Right Y-axis width, a single file-level constant
207
+ const AXIS_THICKNESS = 40;
208
+ // Visible world-X range for a drawing area of the given pixel width
209
+ function visibleXRange(width, transform) {
210
+ return [transform.offsetX, transform.offsetX + (width - AXIS_THICKNESS) / transform.scaleX];
211
+ }
212
+ // Data is sorted by x: find visible-range boundaries with binary search,
213
+ // without filter allocations on each frame. Returns [start, end) by indexes.
214
+ function visibleRange(data, xMin, xMax) {
215
+ let lo = 0, hi = data.length;
216
+ while (lo < hi) {
217
+ const mid = (lo + hi) >> 1;
218
+ if (data[mid].x < xMin)
219
+ lo = mid + 1;
220
+ else
221
+ hi = mid;
222
+ }
223
+ const start = lo;
224
+ hi = data.length;
225
+ while (lo < hi) {
226
+ const mid = (lo + hi) >> 1;
227
+ if (data[mid].x <= xMax)
228
+ lo = mid + 1;
229
+ else
230
+ hi = mid;
231
+ }
232
+ return [start, lo];
233
+ }
215
234
  export function createRenderer() {
216
- const AXIS_THICKNESS = 40;
217
235
  function getNiceTicks(minVal, maxVal, count) {
218
236
  const range = maxVal - minVal || 1;
219
237
  const roughStep = range / count;
220
238
  const mag = Math.pow(10, Math.floor(Math.log10(roughStep)));
221
- let norm = roughStep / mag;
222
- let step = 1;
223
- if (norm < 2)
224
- step = 1;
225
- else if (norm < 5)
226
- step = 2;
227
- else
228
- step = 5;
229
- step *= mag;
239
+ const norm = roughStep / mag;
240
+ const step = (norm < 2 ? 1 : norm < 5 ? 2 : 5) * mag;
230
241
  const niceMin = Math.floor(minVal / step) * step;
231
242
  const niceMax = Math.ceil(maxVal / step) * step;
232
243
  const ticks = [];
@@ -240,7 +251,7 @@ export function createRenderer() {
240
251
  }
241
252
  function yToPixY(yVal, panel, transform) {
242
253
  // autoFocusY => [minY, maxY]
243
- // иначе offsetY/scaleY
254
+ // otherwise offsetY/scaleY
244
255
  if (panel.autoFocusY) {
245
256
  const { minY, maxY } = panel.verticalRange;
246
257
  const range = maxY - minY || 1;
@@ -306,21 +317,40 @@ export function createRenderer() {
306
317
  const fillColor = style.fillColor ?? 'rgba(34,153,221,0.2)';
307
318
  const gradientFill = style.gradientFill ?? true;
308
319
  const lineWidth = style.lineWidth ?? 2;
309
- const AXIS_THICKNESS = 40;
310
- const xMinVisible = transform.offsetX;
311
- const xMaxVisible = transform.offsetX + (panel.width - AXIS_THICKNESS) / transform.scaleX;
312
- const visible = data.filter((pt) => pt.x >= xMinVisible && pt.x <= xMaxVisible);
313
- if (visible.length < 2)
320
+ const [xMinVisible, xMaxVisible] = visibleXRange(panel.width, transform);
321
+ const [start, end] = visibleRange(data, xMinVisible, xMaxVisible);
322
+ if (end - start < 2)
314
323
  return;
315
324
  const result = [];
316
325
  let lastPixX = -1;
317
- for (const pt of visible) {
326
+ let minPt = null;
327
+ let maxPt = null;
328
+ const flush = () => {
329
+ if (!minPt || !maxPt)
330
+ return;
331
+ const a = minPt.x <= maxPt.x ? minPt : maxPt;
332
+ const b = a === minPt ? maxPt : minPt;
333
+ result.push(a);
334
+ if (b !== a)
335
+ result.push(b);
336
+ };
337
+ for (let i = start; i < end; i++) {
338
+ const pt = data[i];
318
339
  const px = Math.round(xToPixX(pt.x, transform, panel));
319
340
  if (px !== lastPixX) {
320
- result.push(pt);
341
+ flush();
342
+ minPt = pt;
343
+ maxPt = pt;
321
344
  lastPixX = px;
322
345
  }
346
+ else {
347
+ if (pt.y < minPt.y)
348
+ minPt = pt;
349
+ if (pt.y > maxPt.y)
350
+ maxPt = pt;
351
+ }
323
352
  }
353
+ flush();
324
354
  if (result.length < 2)
325
355
  return;
326
356
  ctx.save();
@@ -362,19 +392,18 @@ export function createRenderer() {
362
392
  }
363
393
  function drawBarChart(ctx, data, panel, transform, style) {
364
394
  const barColor = style.barColor ?? '#66cc66';
365
- const AXIS_THICKNESS = 40;
366
- const xMinVisible = transform.offsetX;
367
- const xMaxVisible = transform.offsetX + (panel.width - AXIS_THICKNESS) / transform.scaleX;
368
- const visible = data.filter((pt) => pt.x >= xMinVisible && pt.x <= xMaxVisible);
369
- if (!visible.length)
395
+ const [xMinVisible, xMaxVisible] = visibleXRange(panel.width, transform);
396
+ const [start, end] = visibleRange(data, xMinVisible, xMaxVisible);
397
+ if (end === start)
370
398
  return;
371
399
  ctx.save();
372
400
  ctx.fillStyle = barColor;
373
401
  const barWidth = 5;
374
- for (const pt of visible) {
402
+ const pyBase = yToPixY(panel.verticalRange.minY, panel, transform);
403
+ for (let i = start; i < end; i++) {
404
+ const pt = data[i];
375
405
  const px = xToPixX(pt.x, transform, panel);
376
406
  const py = yToPixY(pt.y, panel, transform);
377
- const pyBase = yToPixY(panel.verticalRange.minY, panel, transform);
378
407
  ctx.fillRect(px - barWidth / 2, py, barWidth, pyBase - py);
379
408
  }
380
409
  ctx.restore();
@@ -469,11 +498,14 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
469
498
  const localX = e.clientX - rect.left;
470
499
  const localY = e.clientY - rect.top;
471
500
  crosshairPos = { x: localX, y: localY };
472
- // Проверка границы между панелями (для ресайза)
501
+ // Check the boundary between panels (for resize)
473
502
  const panels = getPanels();
474
503
  for (const p of panels) {
475
504
  if (!p.resizable)
476
505
  continue;
506
+ // last panel takes the leftover height in layoutPanels - its bottom border is not draggable
507
+ if (p === panels[panels.length - 1])
508
+ continue;
477
509
  const bottom = p.top + p.height;
478
510
  if (Math.abs(localY - bottom) < 5) {
479
511
  dragMode = DragMode.ResizePanel;
@@ -482,15 +514,15 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
482
514
  return;
483
515
  }
484
516
  }
485
- // Ищем панель
517
+ // Find panel
486
518
  activePanel = findPanel(localX, localY, panels);
487
519
  if (!activePanel) {
488
520
  dragMode = DragMode.None;
489
521
  return;
490
522
  }
491
- const axisRightX = activePanel.left + activePanel.width - 40;
523
+ const axisRightX = activePanel.left + activePanel.width - AXIS_THICKNESS;
492
524
  if (localX >= axisRightX) {
493
- // масштаб по Y, если autoFocusY=false
525
+ // Scale by Y if autoFocusY=false
494
526
  dragMode = DragMode.ScaleY;
495
527
  }
496
528
  else {
@@ -513,11 +545,13 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
513
545
  lastX = e.clientX;
514
546
  lastY = e.clientY;
515
547
  const t = getTransform();
516
- // Режим ресайза панели
548
+ // Panel resize mode
517
549
  if (dragMode === DragMode.ResizePanel && resizingPanelId) {
518
550
  // deltaPx = dy
519
- const { height: cH } = getContainerSize();
551
+ const { width: cW, height: cH } = getContainerSize();
520
552
  panelManager.resizePanel(resizingPanelId, dy, cH);
553
+ // recalc px top/height right away - previously it only happened on container resize
554
+ panelManager.layoutPanels(cW, cH);
521
555
  onTransformChanged();
522
556
  return;
523
557
  }
@@ -540,12 +574,6 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
540
574
  }
541
575
  onTransformChanged();
542
576
  }
543
- function onMouseUp(e) {
544
- isMouseDown = false;
545
- dragMode = DragMode.None;
546
- activePanel = null;
547
- resizingPanelId = null;
548
- }
549
577
  function onGlobalMouseUp(e) {
550
578
  if (isMouseDown) {
551
579
  isMouseDown = false;
@@ -574,7 +602,7 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
574
602
  const p = findPanel(localX, localY, getPanels());
575
603
  if (!p)
576
604
  return;
577
- const axisRightX = p.left + p.width - 40;
605
+ const axisRightX = p.left + p.width - AXIS_THICKNESS;
578
606
  if (localX >= axisRightX) {
579
607
  onToggleAutoFocusY(p);
580
608
  onTransformChanged();
@@ -618,7 +646,7 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
618
646
  };
619
647
  }
620
648
  /**
621
- * Фабричная функция движка
649
+ * Engine factory function
622
650
  */
623
651
  export function createChartEngine(canvas) {
624
652
  const ctx = canvas.getContext('2d');
@@ -637,7 +665,22 @@ export function createChartEngine(canvas) {
637
665
  let resizeObserver = null;
638
666
  let containerWidth = 0;
639
667
  let containerHeight = 0;
640
- const isYRightAxis = true;
668
+ // Dirty flag: render only when something changes. Invalidation hooks cover interaction
669
+ // (including crosshair), addData/addPanel/resize; stamp in renderLoop catches direct
670
+ // mutations through dataModel/panelManager (data lengths, panel geometry, canvas size).
671
+ let needsRender = true;
672
+ let lastStamp = NaN;
673
+ function invalidate() { needsRender = true; }
674
+ function frameStamp() {
675
+ const panels = panelManager.panels;
676
+ let s = panels.length + canvas.width * 3 + canvas.height * 5;
677
+ for (const p of panels) {
678
+ s += p.top * 7 + p.height * 13 + (p.autoFocusY ? 1 : 0);
679
+ for (const ds of p.dataSets)
680
+ s += ds.data.length * 31;
681
+ }
682
+ return s;
683
+ }
641
684
  function setTransform(t) {
642
685
  transform = t;
643
686
  }
@@ -653,13 +696,27 @@ export function createChartEngine(canvas) {
653
696
  function getContainerSize() {
654
697
  return { width: containerWidth, height: containerHeight };
655
698
  }
656
- const interaction = createInteraction(canvas, getTransform, setTransform, getPanels, () => updatePanels(), (p) => toggleAutoFocusY(p), panelManager, getContainerSize);
699
+ const interaction = createInteraction(canvas, getTransform, setTransform, getPanels,
700
+ // invalidate is enough: renderLoop runs updatePanels once per dirty frame,
701
+ // calling it here doubled the min/max scan on every mouse event
702
+ invalidate, (p) => toggleAutoFocusY(p), panelManager, getContainerSize);
657
703
  function updatePanels() {
658
- const x1 = transform.offsetX;
659
- const x2 = transform.offsetX + (canvas.width - 40) / transform.scaleX;
704
+ const [x1, x2] = visibleXRange(canvas.width, transform);
660
705
  for (const p of panelManager.panels) {
661
706
  if (p.autoFocusY) {
662
- const { minY, maxY } = dataModel.getGlobalMinMaxY(x1, x2, (ds) => p.dataSets.includes(ds));
707
+ let minY = Infinity;
708
+ let maxY = -Infinity;
709
+ for (const ds of p.dataSets) {
710
+ const r = ds.getMinMaxInRange(x1, x2);
711
+ if (r.minY < minY)
712
+ minY = r.minY;
713
+ if (r.maxY > maxY)
714
+ maxY = r.maxY;
715
+ }
716
+ if (minY === Infinity || maxY === -Infinity) {
717
+ minY = 0;
718
+ maxY = 1;
719
+ }
663
720
  p.verticalRange.minY = minY;
664
721
  p.verticalRange.maxY = maxY;
665
722
  }
@@ -675,12 +732,19 @@ export function createChartEngine(canvas) {
675
732
  function renderLoop() {
676
733
  if (destroyed)
677
734
  return;
678
- updatePanels();
679
- const xMin = transform.offsetX;
680
- const xMax = transform.offsetX + (canvas.width - 40) / transform.scaleX;
681
- const crosshair = interaction.getCrosshairPos();
682
- for (const p of panelManager.panels) {
683
- renderer.drawPanel(ctx, p, transform, { xMin, xMax }, crosshair, isYRightAxis);
735
+ const stamp = frameStamp();
736
+ if (stamp !== lastStamp) {
737
+ lastStamp = stamp;
738
+ needsRender = true;
739
+ }
740
+ if (needsRender) {
741
+ needsRender = false;
742
+ updatePanels();
743
+ const [xMin, xMax] = visibleXRange(canvas.width, transform);
744
+ const crosshair = interaction.getCrosshairPos();
745
+ for (const p of panelManager.panels) {
746
+ renderer.drawPanel(ctx, p, transform, { xMin, xMax }, crosshair, true);
747
+ }
684
748
  }
685
749
  animationFrameId = requestAnimationFrame(renderLoop);
686
750
  }
@@ -692,14 +756,15 @@ export function createChartEngine(canvas) {
692
756
  destroyed = true;
693
757
  cancelAnimationFrame(animationFrameId);
694
758
  interaction.destroy();
695
- if (resizeObserver && containerEl) {
696
- resizeObserver.unobserve(containerEl);
759
+ if (resizeObserver) {
697
760
  resizeObserver.disconnect();
698
761
  }
699
762
  containerEl = null;
700
763
  resizeObserver = null;
701
764
  }
702
765
  function attachToContainer(container) {
766
+ if (resizeObserver)
767
+ resizeObserver.disconnect();
703
768
  containerEl = container;
704
769
  resizeObserver = new ResizeObserver((entries) => {
705
770
  for (const entry of entries) {
@@ -709,6 +774,7 @@ export function createChartEngine(canvas) {
709
774
  if (canvas.width !== w || canvas.height !== h) {
710
775
  canvas.width = w;
711
776
  canvas.height = h;
777
+ invalidate(); // changing canvas size clears the bitmap, so redraw is required
712
778
  }
713
779
  containerWidth = w;
714
780
  containerHeight = h;
@@ -720,10 +786,14 @@ export function createChartEngine(canvas) {
720
786
  resizeObserver.observe(containerEl);
721
787
  }
722
788
  function createDataSetFn(params) {
723
- return dataModel.addDataSet(params);
789
+ const ds = dataModel.addDataSet(params);
790
+ const addDataOrig = ds.addData;
791
+ ds.addData = (p) => { addDataOrig(p); invalidate(); };
792
+ return ds;
724
793
  }
725
794
  function addPanelFn(config) {
726
795
  panelManager.addPanel(config);
796
+ invalidate();
727
797
  }
728
798
  return {
729
799
  init,
@@ -738,7 +808,7 @@ export function createChartEngine(canvas) {
738
808
  };
739
809
  }
740
810
  /**
741
- * Пример генерации данных
811
+ * Data generation example
742
812
  */
743
813
  export function generateIncrementalData(startX, count, startY, maxDelta) {
744
814
  const arr = [];
@@ -755,9 +825,9 @@ export function generateIncrementalData(startX, count, startY, maxDelta) {
755
825
  return arr;
756
826
  }
757
827
  /**
758
- * Пример React-компонента MyChart
828
+ * MyChart React component example
759
829
  */
760
- export const MyChartEngine = () => {
830
+ export const MyChartEngine = ({ style }) => {
761
831
  const containerRef = useRef(null);
762
832
  const canvasRef = useRef(null);
763
833
  const engineRef = useRef(null);
@@ -770,7 +840,7 @@ export const MyChartEngine = () => {
770
840
  engineRef.current = engine;
771
841
  engine.attachToContainer(container);
772
842
  engine.init();
773
- // Создаём DataSets
843
+ // Create DataSets
774
844
  const lineData = generateIncrementalData(0, 50, 100, 10);
775
845
  const lineDS = engine.createDataSet({
776
846
  id: 'line1',
@@ -785,8 +855,8 @@ export const MyChartEngine = () => {
785
855
  data: barData,
786
856
  style: { barColor: '#66aa66' }
787
857
  });
788
- // Добавляем панели указываем heightPct
789
- // Последняя панель автоматически заберёт остаток до 100%.
858
+ // Add panels and specify heightPct
859
+ // The last panel automatically takes the remaining space up to 100%.
790
860
  engine.addPanel({
791
861
  id: 'mainPanel',
792
862
  dataSets: [lineDS],
@@ -801,7 +871,7 @@ export const MyChartEngine = () => {
801
871
  autoFocusY: true,
802
872
  resizable: true
803
873
  });
804
- // Имитация добавления
874
+ // Simulate adding data
805
875
  let lineX = lineData[lineData.length - 1].x;
806
876
  let lineY = lineData[lineData.length - 1].y;
807
877
  let barX = barData[barData.length - 1].x;
@@ -819,7 +889,7 @@ export const MyChartEngine = () => {
819
889
  barY = 0;
820
890
  barX++;
821
891
  barDS.addData({ x: barX, y: barY });
822
- }, 1);
892
+ }, 50); // demo stream; 1ms flooded the dataset at max timer rate
823
893
  return () => {
824
894
  clearInterval(intervalId);
825
895
  engine.destroy();
@@ -829,6 +899,7 @@ export const MyChartEngine = () => {
829
899
  width: '100%',
830
900
  height: '600px',
831
901
  border: '1px solid #ccc',
832
- position: 'relative'
902
+ position: 'relative',
903
+ ...style
833
904
  }, children: _jsx("canvas", { ref: canvasRef }) }));
834
905
  };
@@ -1,3 +1,4 @@
1
+ import type { CSSProperties } from "react";
1
2
  import { CellClassParams } from "ag-grid-community";
2
3
  import type { Theme, ThemeDefaultParams } from "ag-grid-community";
3
4
  import { provideGlobalGridOptions } from "ag-grid-community";
@@ -17,4 +18,4 @@ export declare function StyleCSSHeadGridEdit(name: string, rules: string): void;
17
18
  export declare function StyleCSSHeadGrid(): void;
18
19
  export type tCallFuncAgGrid<T> = (params: CellClassParams & {
19
20
  data: T;
20
- }) => {};
21
+ }) => CSSProperties | Record<string, any>;
@@ -1,6 +1,7 @@
1
1
  import { colorSchemeDarkBlue, iconSetMaterial, provideGlobalGridOptions, themeAlpine } from "ag-grid-community";
2
2
  import { AllCommunityModule, ModuleRegistry } from "ag-grid-community";
3
3
  export { AllCommunityModule, ModuleRegistry } from "ag-grid-community";
4
+ import { tokens } from "./tokens";
4
5
  // import 'ag-grid-community/styles/ag-grid.css';
5
6
  // import 'ag-grid-community/styles/ag-theme-alpine.css';
6
7
  // Register all community features
@@ -9,14 +10,7 @@ export function GridStyleDefault() {
9
10
  const theme = themeAlpine
10
11
  .withPart(colorSchemeDarkBlue)
11
12
  .withPart(iconSetMaterial)
12
- .withParams({
13
- 'fontFamily': 'Roboto',
14
- 'textColor': 'rgba(214,214,214,0.9)',
15
- 'tabTextColor': 'rgba(227,227,227,0.9)',
16
- 'fontSize': '10px',
17
- 'spacing': '3px',
18
- 'backgroundColor': 'rgb(19,24,33)'
19
- });
13
+ .withParams({ ...tokens.grid });
20
14
  // Mark all grids as using legacy themes
21
15
  provideGlobalGridOptions({ theme: theme });
22
16
  return { theme, provideGlobalGridOptions };
@@ -38,13 +32,17 @@ export const StyleGridDefault = {
38
32
  };
39
33
  export function StyleCSSHeadGridEdit(name, rules) {
40
34
  let style = document.createElement('style');
41
- style.type = 'text/css';
42
- document.getElementsByTagName('head')[0].appendChild(style);
35
+ document.head.appendChild(style);
43
36
  style.sheet?.insertRule(name + "{" + rules + "}", 0);
44
37
  }
38
+ let headGridStylesApplied = false;
45
39
  export function StyleCSSHeadGrid() {
46
- // уменьшаем отступы с боков для заголовков
40
+ // idempotent: repeated calls used to append a new <style> to <head> every time
41
+ if (headGridStylesApplied)
42
+ return;
43
+ headGridStylesApplied = true;
44
+ // Reduce side padding for headers
47
45
  StyleCSSHeadGridEdit('.ag-theme-alpine-dark .ag-theme-alpine .ag-header-cell, .ag-theme-alpine-dark .ag-header-group-cell', "padding-left: 3px; padding-right: 3px;");
48
- // выравнивание в заголовке по центру
46
+ // Center header content
49
47
  StyleCSSHeadGridEdit('.ag-header-cell-label', 'justify-content: center');
50
48
  }