wenay-react2 1.0.20 → 1.0.22

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 (74) 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 +7 -6
  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 -105
  11. package/lib/common/src/components/Dnd/Resizable.js +3 -3
  12. package/lib/common/src/components/Menu/RightMenu.js +11 -17
  13. package/lib/common/src/components/Menu/StickerMenu.js +42 -28
  14. package/lib/common/src/components/Menu/index.d.ts +1 -1
  15. package/lib/common/src/components/Menu/index.js +1 -1
  16. package/lib/common/src/components/Modal/LeftModal.d.ts +14 -12
  17. package/lib/common/src/components/Modal/LeftModal.js +30 -16
  18. package/lib/common/src/components/Modal/Modal.d.ts +26 -12
  19. package/lib/common/src/components/Modal/Modal.js +66 -89
  20. package/lib/common/src/components/Modal/ModalContextProvider.d.ts +20 -3
  21. package/lib/common/src/components/Modal/ModalContextProvider.js +35 -8
  22. package/lib/common/src/components/MyResizeObserver.js +15 -18
  23. package/lib/common/src/components/Other.js +3 -2
  24. package/lib/common/src/components/Parameters.js +7 -6
  25. package/lib/common/src/components/ParametersEngine.js +25 -54
  26. package/lib/common/src/grid/agGrid4/agGrid4.d.ts +53 -0
  27. package/lib/common/src/grid/agGrid4/agGrid4.js +132 -0
  28. package/lib/common/src/grid/agGrid4/core.d.ts +56 -0
  29. package/lib/common/src/grid/agGrid4/core.js +127 -0
  30. package/lib/common/src/grid/agGrid4/index.d.ts +6 -0
  31. package/lib/common/src/grid/agGrid4/index.js +3 -0
  32. package/lib/common/src/grid/agGrid4/theme.d.ts +7 -0
  33. package/lib/common/src/grid/agGrid4/theme.js +18 -0
  34. package/lib/common/src/hooks/useAddDownAnyKey.d.ts +21 -2
  35. package/lib/common/src/hooks/useAddDownAnyKey.js +46 -8
  36. package/lib/common/src/hooks/useDraggable.d.ts +20 -2
  37. package/lib/common/src/hooks/useDraggable.js +146 -115
  38. package/lib/common/src/hooks/useOutside.d.ts +24 -4
  39. package/lib/common/src/hooks/useOutside.js +76 -27
  40. package/lib/common/src/logs/logs.d.ts +5 -2
  41. package/lib/common/src/logs/logs.js +15 -31
  42. package/lib/common/src/logs/logs3.d.ts +9 -9
  43. package/lib/common/src/logs/logs3.js +47 -38
  44. package/lib/common/src/logs/miniLogs.d.ts +3 -3
  45. package/lib/common/src/logs/miniLogs.js +28 -36
  46. package/lib/common/src/menu/menu.d.ts +18 -18
  47. package/lib/common/src/menu/menu.js +48 -24
  48. package/lib/common/src/menu/menuMouse.js +4 -1
  49. package/lib/common/src/menu/menuR.d.ts +2 -2
  50. package/lib/common/src/menu/menuR.js +34 -34
  51. package/lib/common/src/myChart/1/myChart.d.ts +5 -5
  52. package/lib/common/src/myChart/1/myChart.js +58 -47
  53. package/lib/common/src/myChart/1/myChartTest.js +8 -4
  54. package/lib/common/src/myChart/chartEngine/chartEngineReact.d.ts +24 -27
  55. package/lib/common/src/myChart/chartEngine/chartEngineReact.js +166 -117
  56. package/lib/common/src/styles/styleGrid.d.ts +2 -1
  57. package/lib/common/src/styles/styleGrid.js +10 -12
  58. package/lib/common/src/styles/tokens.d.ts +39 -0
  59. package/lib/common/src/styles/tokens.js +40 -0
  60. package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +7 -0
  61. package/lib/common/src/utils/applyTransactionAsyncUpdate.js +19 -12
  62. package/lib/common/src/utils/arrayPromise.d.ts +6 -0
  63. package/lib/common/src/utils/arrayPromise.js +7 -1
  64. package/lib/common/src/utils/cache.js +24 -21
  65. package/lib/common/src/utils/inputAutoStep.d.ts +1 -1
  66. package/lib/common/src/utils/inputAutoStep.js +30 -19
  67. package/lib/common/src/utils/mapMemory.js +9 -10
  68. package/lib/common/updateBy.d.ts +21 -2
  69. package/lib/common/updateBy.js +66 -31
  70. package/lib/index.js +0 -2
  71. package/lib/style/menuRight.css +13 -26
  72. package/lib/style/style.css +51 -39
  73. package/lib/style/tokens.css +34 -0
  74. 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,11 +317,9 @@ 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;
@@ -325,7 +334,8 @@ export function createRenderer() {
325
334
  if (b !== a)
326
335
  result.push(b);
327
336
  };
328
- for (const pt of visible) {
337
+ for (let i = start; i < end; i++) {
338
+ const pt = data[i];
329
339
  const px = Math.round(xToPixX(pt.x, transform, panel));
330
340
  if (px !== lastPixX) {
331
341
  flush();
@@ -382,19 +392,18 @@ export function createRenderer() {
382
392
  }
383
393
  function drawBarChart(ctx, data, panel, transform, style) {
384
394
  const barColor = style.barColor ?? '#66cc66';
385
- const AXIS_THICKNESS = 40;
386
- const xMinVisible = transform.offsetX;
387
- const xMaxVisible = transform.offsetX + (panel.width - AXIS_THICKNESS) / transform.scaleX;
388
- const visible = data.filter((pt) => pt.x >= xMinVisible && pt.x <= xMaxVisible);
389
- if (!visible.length)
395
+ const [xMinVisible, xMaxVisible] = visibleXRange(panel.width, transform);
396
+ const [start, end] = visibleRange(data, xMinVisible, xMaxVisible);
397
+ if (end === start)
390
398
  return;
391
399
  ctx.save();
392
400
  ctx.fillStyle = barColor;
393
401
  const barWidth = 5;
394
- 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];
395
405
  const px = xToPixX(pt.x, transform, panel);
396
406
  const py = yToPixY(pt.y, panel, transform);
397
- const pyBase = yToPixY(panel.verticalRange.minY, panel, transform);
398
407
  ctx.fillRect(px - barWidth / 2, py, barWidth, pyBase - py);
399
408
  }
400
409
  ctx.restore();
@@ -489,11 +498,14 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
489
498
  const localX = e.clientX - rect.left;
490
499
  const localY = e.clientY - rect.top;
491
500
  crosshairPos = { x: localX, y: localY };
492
- // Проверка границы между панелями (для ресайза)
501
+ // Check the boundary between panels (for resize)
493
502
  const panels = getPanels();
494
503
  for (const p of panels) {
495
504
  if (!p.resizable)
496
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;
497
509
  const bottom = p.top + p.height;
498
510
  if (Math.abs(localY - bottom) < 5) {
499
511
  dragMode = DragMode.ResizePanel;
@@ -502,15 +514,15 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
502
514
  return;
503
515
  }
504
516
  }
505
- // Ищем панель
517
+ // Find panel
506
518
  activePanel = findPanel(localX, localY, panels);
507
519
  if (!activePanel) {
508
520
  dragMode = DragMode.None;
509
521
  return;
510
522
  }
511
- const axisRightX = activePanel.left + activePanel.width - 40;
523
+ const axisRightX = activePanel.left + activePanel.width - AXIS_THICKNESS;
512
524
  if (localX >= axisRightX) {
513
- // масштаб по Y, если autoFocusY=false
525
+ // Scale by Y if autoFocusY=false
514
526
  dragMode = DragMode.ScaleY;
515
527
  }
516
528
  else {
@@ -533,11 +545,13 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
533
545
  lastX = e.clientX;
534
546
  lastY = e.clientY;
535
547
  const t = getTransform();
536
- // Режим ресайза панели
548
+ // Panel resize mode
537
549
  if (dragMode === DragMode.ResizePanel && resizingPanelId) {
538
550
  // deltaPx = dy
539
- const { height: cH } = getContainerSize();
551
+ const { width: cW, height: cH } = getContainerSize();
540
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);
541
555
  onTransformChanged();
542
556
  return;
543
557
  }
@@ -560,12 +574,6 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
560
574
  }
561
575
  onTransformChanged();
562
576
  }
563
- function onMouseUp(e) {
564
- isMouseDown = false;
565
- dragMode = DragMode.None;
566
- activePanel = null;
567
- resizingPanelId = null;
568
- }
569
577
  function onGlobalMouseUp(e) {
570
578
  if (isMouseDown) {
571
579
  isMouseDown = false;
@@ -594,7 +602,7 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
594
602
  const p = findPanel(localX, localY, getPanels());
595
603
  if (!p)
596
604
  return;
597
- const axisRightX = p.left + p.width - 40;
605
+ const axisRightX = p.left + p.width - AXIS_THICKNESS;
598
606
  if (localX >= axisRightX) {
599
607
  onToggleAutoFocusY(p);
600
608
  onTransformChanged();
@@ -638,7 +646,7 @@ export function createInteraction(canvas, getTransform, setTransform, getPanels,
638
646
  };
639
647
  }
640
648
  /**
641
- * Фабричная функция движка
649
+ * Engine factory function
642
650
  */
643
651
  export function createChartEngine(canvas) {
644
652
  const ctx = canvas.getContext('2d');
@@ -657,7 +665,22 @@ export function createChartEngine(canvas) {
657
665
  let resizeObserver = null;
658
666
  let containerWidth = 0;
659
667
  let containerHeight = 0;
660
- 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
+ }
661
684
  function setTransform(t) {
662
685
  transform = t;
663
686
  }
@@ -673,13 +696,27 @@ export function createChartEngine(canvas) {
673
696
  function getContainerSize() {
674
697
  return { width: containerWidth, height: containerHeight };
675
698
  }
676
- 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);
677
703
  function updatePanels() {
678
- const x1 = transform.offsetX;
679
- const x2 = transform.offsetX + (canvas.width - 40) / transform.scaleX;
704
+ const [x1, x2] = visibleXRange(canvas.width, transform);
680
705
  for (const p of panelManager.panels) {
681
706
  if (p.autoFocusY) {
682
- 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
+ }
683
720
  p.verticalRange.minY = minY;
684
721
  p.verticalRange.maxY = maxY;
685
722
  }
@@ -695,12 +732,19 @@ export function createChartEngine(canvas) {
695
732
  function renderLoop() {
696
733
  if (destroyed)
697
734
  return;
698
- updatePanels();
699
- const xMin = transform.offsetX;
700
- const xMax = transform.offsetX + (canvas.width - 40) / transform.scaleX;
701
- const crosshair = interaction.getCrosshairPos();
702
- for (const p of panelManager.panels) {
703
- 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
+ }
704
748
  }
705
749
  animationFrameId = requestAnimationFrame(renderLoop);
706
750
  }
@@ -712,8 +756,7 @@ export function createChartEngine(canvas) {
712
756
  destroyed = true;
713
757
  cancelAnimationFrame(animationFrameId);
714
758
  interaction.destroy();
715
- if (resizeObserver && containerEl) {
716
- resizeObserver.unobserve(containerEl);
759
+ if (resizeObserver) {
717
760
  resizeObserver.disconnect();
718
761
  }
719
762
  containerEl = null;
@@ -731,6 +774,7 @@ export function createChartEngine(canvas) {
731
774
  if (canvas.width !== w || canvas.height !== h) {
732
775
  canvas.width = w;
733
776
  canvas.height = h;
777
+ invalidate(); // changing canvas size clears the bitmap, so redraw is required
734
778
  }
735
779
  containerWidth = w;
736
780
  containerHeight = h;
@@ -742,10 +786,14 @@ export function createChartEngine(canvas) {
742
786
  resizeObserver.observe(containerEl);
743
787
  }
744
788
  function createDataSetFn(params) {
745
- 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;
746
793
  }
747
794
  function addPanelFn(config) {
748
795
  panelManager.addPanel(config);
796
+ invalidate();
749
797
  }
750
798
  return {
751
799
  init,
@@ -760,7 +808,7 @@ export function createChartEngine(canvas) {
760
808
  };
761
809
  }
762
810
  /**
763
- * Пример генерации данных
811
+ * Data generation example
764
812
  */
765
813
  export function generateIncrementalData(startX, count, startY, maxDelta) {
766
814
  const arr = [];
@@ -777,9 +825,9 @@ export function generateIncrementalData(startX, count, startY, maxDelta) {
777
825
  return arr;
778
826
  }
779
827
  /**
780
- * Пример React-компонента MyChart
828
+ * MyChart React component example
781
829
  */
782
- export const MyChartEngine = () => {
830
+ export const MyChartEngine = ({ style }) => {
783
831
  const containerRef = useRef(null);
784
832
  const canvasRef = useRef(null);
785
833
  const engineRef = useRef(null);
@@ -792,7 +840,7 @@ export const MyChartEngine = () => {
792
840
  engineRef.current = engine;
793
841
  engine.attachToContainer(container);
794
842
  engine.init();
795
- // Создаём DataSets
843
+ // Create DataSets
796
844
  const lineData = generateIncrementalData(0, 50, 100, 10);
797
845
  const lineDS = engine.createDataSet({
798
846
  id: 'line1',
@@ -807,8 +855,8 @@ export const MyChartEngine = () => {
807
855
  data: barData,
808
856
  style: { barColor: '#66aa66' }
809
857
  });
810
- // Добавляем панели указываем heightPct
811
- // Последняя панель автоматически заберёт остаток до 100%.
858
+ // Add panels and specify heightPct
859
+ // The last panel automatically takes the remaining space up to 100%.
812
860
  engine.addPanel({
813
861
  id: 'mainPanel',
814
862
  dataSets: [lineDS],
@@ -823,7 +871,7 @@ export const MyChartEngine = () => {
823
871
  autoFocusY: true,
824
872
  resizable: true
825
873
  });
826
- // Имитация добавления
874
+ // Simulate adding data
827
875
  let lineX = lineData[lineData.length - 1].x;
828
876
  let lineY = lineData[lineData.length - 1].y;
829
877
  let barX = barData[barData.length - 1].x;
@@ -841,7 +889,7 @@ export const MyChartEngine = () => {
841
889
  barY = 0;
842
890
  barX++;
843
891
  barDS.addData({ x: barX, y: barY });
844
- }, 1);
892
+ }, 50); // demo stream; 1ms flooded the dataset at max timer rate
845
893
  return () => {
846
894
  clearInterval(intervalId);
847
895
  engine.destroy();
@@ -851,6 +899,7 @@ export const MyChartEngine = () => {
851
899
  width: '100%',
852
900
  height: '600px',
853
901
  border: '1px solid #ccc',
854
- position: 'relative'
902
+ position: 'relative',
903
+ ...style
855
904
  }, children: _jsx("canvas", { ref: canvasRef }) }));
856
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
  }
@@ -0,0 +1,39 @@
1
+ export declare const tokens: {
2
+ readonly color: {
3
+ readonly bgDark: "#131821";
4
+ readonly bgLight: "#17202e";
5
+ readonly scrollbarTrack: "rgba(255, 255, 255, 0)";
6
+ readonly scrollbarThumb: "#1d262c";
7
+ readonly scrollbarThumbAlt: "#4c4562";
8
+ readonly textBase: "#c4c4c4";
9
+ readonly textTheme: "#5D9FFA";
10
+ readonly borderCommon: "rgb(50, 62, 71)";
11
+ };
12
+ readonly menu: {
13
+ readonly bgColor: "rgba(12, 12, 12, 0.91)";
14
+ readonly blur: "10px";
15
+ readonly border: "1px solid rgba(255, 255, 255, 0.41)";
16
+ readonly activeBackground: "rgba(255, 255, 255, 0.23)";
17
+ readonly itemColor: "#fff";
18
+ readonly itemHoverColor: "#101010";
19
+ readonly itemHoverBgColor: "#fff";
20
+ };
21
+ readonly font: {
22
+ readonly family: "Roboto";
23
+ readonly sizeBase: "12px";
24
+ };
25
+ readonly zIndex: {
26
+ /** CSS variable --wenay-z-modal; fallback in ModalProvider */
27
+ readonly modal: 9999;
28
+ };
29
+ /** ag-grid theme params shared by GridStyleDefault (legacy) and agGrid4 buildAgTheme */
30
+ readonly grid: {
31
+ readonly fontFamily: "Roboto";
32
+ readonly textColor: "rgba(214,214,214,0.9)";
33
+ readonly tabTextColor: "rgba(227,227,227,0.9)";
34
+ readonly fontSize: "10px";
35
+ readonly spacing: "3px";
36
+ readonly backgroundColor: "rgb(19,24,33)";
37
+ };
38
+ };
39
+ export type Tokens = typeof tokens;