wenay-react2 1.0.20 → 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 (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,4 +1,4 @@
1
- // Класс для отслеживания изменения размеров элементов
1
+ // Class for tracking element size changes
2
2
  export class CResizeObserver {
3
3
  #idMap = new WeakMap();
4
4
  #funcMap = new WeakMap();
@@ -19,9 +19,7 @@ export class CResizeObserver {
19
19
  this.#observer?.observe(element);
20
20
  }
21
21
  functions.push(onResize);
22
- let id = new class {
23
- [Symbol.species] = this; //{} as ObserveID
24
- }();
22
+ const id = {}; // unique WeakMap key; the branded type only exists at compile time
25
23
  this.#idMap.set(id, { element, func: onResize });
26
24
  return id;
27
25
  }
@@ -43,13 +41,13 @@ export class CResizeObserver {
43
41
  }
44
42
  const global_resizeObserver = new CResizeObserver();
45
43
  const resizeableElementMap = new WeakMap();
46
- // Задаём автоматическое изменение размеров элемента в зависимости от размеров родительского элемента
44
+ // Set automatic element resizing based on the parent element size
47
45
  //
48
46
  export function setResizeableElement(el) {
49
47
  const parent = el.parentElement;
50
48
  if (!parent)
51
49
  return;
52
- const parentParent = parent.parentElement; // на один уровень выше
50
+ const parentParent = parent.parentElement; // one level higher
53
51
  if (!parentParent)
54
52
  return;
55
53
  const lastEl = parent.lastElementChild;
@@ -60,26 +58,25 @@ export function setResizeableElement(el) {
60
58
  if (existing)
61
59
  global_resizeObserver.delete(existing);
62
60
  const observerId = global_resizeObserver.add(parentParent, () => {
61
+ // Jump directly by the mismatch amount (previously 1px with reflow on each step);
62
+ // a few iterations are only for final adjustment, the upper bound prevents an infinite loop
63
63
  let lastRangeDelta = 0;
64
- let i = 0;
65
- //console.log("###",el.style.width, el.clientWidth);
66
- for (let width = el.clientWidth;;) { //} width>=20; width--) {
67
- let rangeDelta = Math.floor(lastEl.getBoundingClientRect().right - parentParent.getBoundingClientRect().right - 0);
64
+ for (let width = el.clientWidth, i = 0; i < 8; i++) {
65
+ const rangeDelta = Math.floor(lastEl.getBoundingClientRect().right - parentParent.getBoundingClientRect().right);
68
66
  if (rangeDelta == 0)
69
67
  break;
70
68
  if (lastRangeDelta && rangeDelta * lastRangeDelta < 0)
71
69
  break;
72
70
  lastRangeDelta = rangeDelta;
73
- if (rangeDelta > 0)
74
- width--;
75
- else
76
- width++;
77
71
  defaultWidth ??= el.clientWidth;
78
- if (width < 10 || width > defaultWidth)
79
- break;
72
+ width -= rangeDelta;
73
+ if (width < 10)
74
+ width = 10;
75
+ if (width > defaultWidth)
76
+ width = defaultWidth;
80
77
  el.style.width = width + "px";
81
- //console.log(i+": "+numEl.getBoundingClientRect().right,"<", parentParent.getBoundingClientRect().right, rangeEl.clientWidth); //numEl.clientLeft, numEl.clientWidth);
82
- i++;
78
+ if (width == 10 || width == defaultWidth)
79
+ break;
83
80
  }
84
81
  });
85
82
  resizeableElementMap.set(el, observerId);
@@ -12,7 +12,7 @@ export function EditParams2({ onSave, params: paramsDef }) {
12
12
  return _jsxs("div", { className: "maxSize", children: [paramsD && _jsx(ParametersReact, { params: paramsD, onChange: e => params.current = e }), onSave && _jsx("div", { className: "msTradeActive msTradeAlt", onClick: async () => {
13
13
  const t = params.current || paramsD;
14
14
  if (t)
15
- onSave?.(t);
15
+ onSave(t);
16
16
  }, children: "save" })] });
17
17
  }
18
18
  export function EditParams3({ onSave, params: paramsDef }) {
@@ -26,6 +26,7 @@ export function EditParams3({ onSave, params: paramsDef }) {
26
26
  params[i] = e;
27
27
  setParams(params);
28
28
  } }, i)), onSave && _jsx("div", { className: "msTradeActive msTradeAlt", onClick: async () => {
29
- params && onSave?.(params);
29
+ if (params)
30
+ onSave(params);
30
31
  }, children: "save" })] });
31
32
  }
@@ -1,24 +1,25 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
- //вид кнопки
3
+ // Button view
4
4
  export function FButton(name) {
5
5
  return _jsxs("div", { className: "", style: { width: "100%" }, children: [" ", name] });
6
6
  }
7
- //стрелка для кнопки
7
+ // Arrow for the button
8
8
  export function FNameButton(type, name) { return FButton(_jsx("p", { className: "toPTextIndicator", children: (type ? "▼ " : "▶ ") + name })); }
9
9
  export function CParameter(props) {
10
- const [hovered, setHovered] = React.useState(false); // Состояние для отслеживания наведения мышки
10
+ const [hovered, setHovered] = React.useState(false); // Tracks mouse hover state
11
11
  return (_jsxs("div", { className: "toLine LeftMenuParameters toIndicatorMenuButton", style: { position: "relative" }, children: [_jsx("div", { className: "toLine", style: { width: "auto", ...props.style }, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: props.name }), _jsx("div", { className: "toLine toRight", style: props.enabled === false ? { opacity: 0.5 } : {}, children: props.children }), hovered && props.commentary && props.commentary.length > 0 && (_jsx("div", { className: "commentary", style: {
12
12
  marginTop: "5px",
13
13
  fontSize: "12px",
14
14
  color: "gray",
15
- position: "absolute", // Можно сделать абсолютно спозиционированным
16
- bottom: "-20px", // Сдвигаем вниз, чтобы не перекрывать основной контент
15
+ position: "absolute", // Can be absolutely positioned
16
+ bottom: "-20px", // Shift down to avoid covering the main content
17
17
  left: "0",
18
- backgroundColor: "white", // Чтобы выделялось на фоне
18
+ backgroundColor: "white", // Make it stand out from the background
19
19
  padding: "2px 4px",
20
20
  border: "1px solid lightgray",
21
21
  borderRadius: "4px",
22
22
  zIndex: 10,
23
+ whiteSpace: "pre-line", // otherwise "\n" collapses into spaces
23
24
  }, children: props.commentary.join("\n") }))] }));
24
25
  }
@@ -20,7 +20,7 @@ function CButton({ name, className, status: statusDef, header, onExpand, childre
20
20
  onExpand?.(status2);
21
21
  }, children: name(status) }), header] }), status && children] });
22
22
  }
23
- //класс наведение на объект, если на вели на оболочку над children то появиться onFocusUp/focusDw
23
+ // Hover wrapper: when the wrapper around children is hovered, focus state is exposed.
24
24
  function DivHover({ children, className, style }) {
25
25
  const [hover, setHover] = useState(false);
26
26
  return _jsx("div", { className: className, style: style, onMouseLeave: () => { setHover(false); }, onMouseEnter: () => { setHover(true); }, children: children(hover) });
@@ -36,14 +36,7 @@ function InputString(set, val, style) {
36
36
  } });
37
37
  }
38
38
  function InputTime(set, value, range) {
39
- //const data= this._inputNumStrMap.get(range);
40
- //let val= value;
41
- //if (1) return null;
42
39
  let { min, max } = range ?? {};
43
- // (data?.value==value)
44
- // ? data
45
- // : { min: range.min, max: range.max, step: range.defaultStep??range.step, val: value };
46
- //this._inputNumStrMap.delete(range);
47
40
  const timeSplits = typeof value == "string" ? value.split(":").length : 2;
48
41
  const hasDot = typeof value == "string" ? value.includes(".") : false;
49
42
  const step = range?.step ?? (timeSplits <= 1 ? TF.D1.msec : timeSplits == 2 ? TF.M1.msec : hasDot ? 1 : TF.S1.msec);
@@ -64,10 +57,8 @@ function InputTime(set, value, range) {
64
57
  step % TF.S1.msec == 0 ? timeLocalToStr_yyyymmdd_hhmmss(new Date(val_)) : timeLocalToStr_yyyymmdd_hhmmss_ms(new Date(val_));
65
58
  return timeStr.replace(" ", "T");
66
59
  }
67
- //function toInputVal(val: string | const_Date | undefined) { return val ? new Date(val).valueOf() : undefined; }
68
- //const inputType : React.InputHTMLAttributes.type = ;
69
60
  let _ref = null;
70
- return _jsxs(_Fragment, { children: [_jsx("input", { type: "range", min: toNum(range?.defaultMin ?? range?.min ?? "2015.01.01"), max: toNum(range?.defaultMax ?? range?.max ?? new Date().toString()), step: range?.defaultStep ?? step, value: toNum(value), onInput: (e) => {
61
+ return _jsxs(_Fragment, { children: [_jsx("input", { type: "range", min: toNum(range?.defaultMin ?? range?.min ?? "2015-01-01"), max: toNum(range?.defaultMax ?? range?.max ?? new Date()), step: range?.defaultStep ?? step, value: toNum(value) ?? "", onInput: (e) => {
71
62
  setVal(Number(e.currentTarget.value));
72
63
  }, ref: (ref) => {
73
64
  if (ref) {
@@ -76,18 +67,13 @@ function InputTime(set, value, range) {
76
67
  }
77
68
  } }), _jsx("div", { children: _jsx("input", { type: step % TF.D1.msec == 0 ? "date" : "datetime-local", style: { width: "calc(100% - 13px)", marginTop: 5 }, onInput: (e) => {
78
69
  set(e.currentTarget.value);
79
- }, min: toInputStr(min), max: toInputStr(max), step: step % TF.D1.msec == 0 ? step / TF.D1.msec : step / TF.S1.msec, value: toInputStr(normalizedValue),
80
- //onMouseOver={()=>{ console.log("over"); _ref?.dispatchEvent(new MouseEvent("mouseenter", { 'bubbles': true })); _ref?.dispatchEvent(new MouseEvent("mouseover", { 'bubbles': true }))}}
81
- onMouseEnter: () => {
70
+ }, min: toInputStr(min), max: toInputStr(max), step: step % TF.D1.msec == 0 ? step / TF.D1.msec : step / TF.S1.msec, value: toInputStr(normalizedValue), onMouseEnter: () => {
82
71
  _ref?.focus();
83
72
  }, onMouseLeave: () => {
84
73
  _ref?.blur();
85
- } }) }), _jsx("input", { required: true, className: "toNumberInput inputCan", type: "number", style: { width: 18, marginLeft: -13, marginRight: 0 }, min: toNum(min), max: toNum(max), step: step, value: toNum(value), onInput: (e) => {
86
- const target = e.currentTarget;
87
- setVal(Number(target.value));
88
- },
89
- //onMouseEnter={()=>console.log("!!!", deepClone(_ref?.classList))}
90
- ref: (ref) => {
74
+ } }) }), _jsx("input", { required: true, className: "toNumberInput inputCan", type: "number", style: { width: 18, marginLeft: -13, marginRight: 0 }, min: toNum(min), max: toNum(max), step: step, value: toNum(value) ?? "", onInput: (e) => {
75
+ setVal(Number(e.currentTarget.value));
76
+ }, ref: (ref) => {
91
77
  if (ref) {
92
78
  _ref = ref;
93
79
  }
@@ -117,19 +103,16 @@ function InputList(set, value, range, rangeLabels) {
117
103
  }
118
104
  const _inputNumStrMap = new WeakMap();
119
105
  function InputNumber(set, value, range) {
120
- //if (1) return null;
121
106
  const data = _inputNumStrMap.get(range);
122
- //let val= value;
123
107
  let { min, max, step, val } = (data?.value == value)
124
108
  ? data
125
109
  : { min: range.min, max: range.max, step: range.defaultStep ?? range.step, val: value };
126
110
  _inputNumStrMap.delete(range);
127
111
  let _ref;
128
112
  return _jsxs(_Fragment, { children: [_jsx("input", { type: "range", min: range.defaultMin ?? range.min, max: range.defaultMax ?? range.max, step: range.defaultStep ?? range.step, value: String(value), onInput: (e) => {
129
- const current = e.currentTarget;
130
- set(Number(current.value));
113
+ set(Number(e.currentTarget.value));
131
114
  if (_ref)
132
- _ref.step = current.step;
115
+ _ref.step = e.currentTarget.step;
133
116
  }, ref: (ref) => {
134
117
  if (ref) {
135
118
  setResizeableElement(ref);
@@ -138,7 +121,6 @@ function InputNumber(set, value, range) {
138
121
  } }), _jsx("input", { required: true, className: "toNumberInput inputCan", type: "number", min: min, max: max, step: step, value: val, onInput: (e) => {
139
122
  const target = e.currentTarget;
140
123
  let value2 = target.value != "" ? Number(target.value) : value;
141
- //if (target.value=="") console.log(value, range);
142
124
  _inputNumStrMap.set(range, {
143
125
  val: target.value,
144
126
  min: target.min,
@@ -150,11 +132,8 @@ function InputNumber(set, value, range) {
150
132
  set(Number(target.value));
151
133
  else {
152
134
  target.reportValidity();
153
- // this.Refresh();
154
135
  }
155
- },
156
- //onBlur={ (a)=>{ console.log("blur"); if (! a.currentTarget.checkValidity()) set(Number(a.currentTarget.value)); } }
157
- ref: (ref) => {
136
+ }, ref: (ref) => {
158
137
  if (ref) {
159
138
  _ref = ref;
160
139
  ref.step = step + "";
@@ -185,10 +164,12 @@ function InputListArray(set, values, range, rangeLabels) {
185
164
  }) });
186
165
  }
187
166
  export function ParametersReact(data) {
188
- const params = useRef(null);
167
+ // callbacks live in a ref: useMemo below intentionally freezes the element until
168
+ // params identity changes, and would otherwise freeze a stale onChange/onExpand with it
169
+ const callbacks = useRef({ onChange: data.onChange, onExpand: data.onExpand });
170
+ callbacks.current = { onChange: data.onChange, onExpand: data.onExpand };
189
171
  const result = useMemo(() => {
190
- params.current = data.params;
191
- return _jsx(ParametersBaseReact, { params: data.params, onChange: data.onChange, onExpand: data.onExpand, expandStatus: data.expandStatus, expandStatusLvl: data.expandStatusLvl });
172
+ return _jsx(ParametersBaseReact, { params: data.params, onChange: (p) => callbacks.current.onChange(p), onExpand: (p) => callbacks.current.onExpand?.(p), expandStatus: data.expandStatus, expandStatusLvl: data.expandStatusLvl });
192
173
  }, [data.params]);
193
174
  return result;
194
175
  }
@@ -198,10 +179,9 @@ function ParametersBaseReact(data) {
198
179
  const styleColorDisable = "rgb(146,146,146)";
199
180
  const p = useMemo(() => deepCloneMutable(data.params), [data.params]);
200
181
  const myParams = useRef(p);
201
- useEffect(() => {
182
+ // sync during render: the effect+Refresh variant committed a frame of stale params first
183
+ if (myParams.current !== p)
202
184
  myParams.current = p;
203
- Refresh();
204
- }, [p]);
205
185
  const timeoutId = useRef(null);
206
186
  useEffect(() => () => { if (timeoutId.current != null)
207
187
  clearTimeout(timeoutId.current); }, []);
@@ -233,24 +213,23 @@ function ParametersBaseReact(data) {
233
213
  Refresh();
234
214
  if (enabled)
235
215
  if (typeof value == "number" || value instanceof Date || (value instanceof Array && (typeof value[0] == "number" || value[0] instanceof Date)))
236
- refreshIndicatorDelayed(); // обрабатываем с задержкой
216
+ refreshIndicatorDelayed(); // process with delay
237
217
  else
238
- refreshIndicator(); // обрабатываем без задержки
239
- //SessionSave();
218
+ refreshIndicator(); // process without delay
240
219
  }
241
220
  function onExpandA(param, flag) {
242
221
  data.onExpand?.(myParams.current);
243
222
  }
244
223
  function ListParams(obj, parentEnabled = true, expandLevel = 0) {
245
224
  return Object.entries(obj).map(([key, param]) => {
246
- //изменения параметров индикатора
225
+ // Indicator parameter changes
247
226
  const onSetValue = (value, currentEnabled = true) => {
248
227
  onSetValueA(param, value, parentEnabled && currentEnabled);
249
228
  };
250
229
  if (!Array.isArray(obj))
251
230
  if (typeof (param) == "boolean" || typeof (param) == "string") {
252
231
  const set = (data) => {
253
- obj[key] = data;
232
+ obj[key] = data; // this branch only renders boolean/string params
254
233
  onSetValue(data);
255
234
  };
256
235
  return _jsx(CParameter, { name: key, enabled: parentEnabled, children: Param(set, param) }, key + "#" + typeof (param));
@@ -265,7 +244,6 @@ function ParametersBaseReact(data) {
265
244
  const nameButton = (type) => FNameButton(type, name);
266
245
  const nameT = _jsx("p", { className: "toPTextIndicator", children: name });
267
246
  const set = (a) => {
268
- //if (typeof a=="boolean") {a=!value;}
269
247
  const aa = param.value instanceof Date ? new Date(a) : a;
270
248
  param.value = aa;
271
249
  onSetValue(aa, param.enabled ?? true);
@@ -298,7 +276,7 @@ function ParametersBaseReact(data) {
298
276
  onSetValue(value);
299
277
  }, children: param.enabled ? "ON " : "OFF" })
300
278
  : null;
301
- // если есть вложение, то делам рекурсию со сдвигом
279
+ // If there are nested parameters, recurse with an offset.
302
280
  if (!Array.isArray(value)) {
303
281
  const enabledD = enabled != false && parentEnabled != false;
304
282
  const list = ListParams(value, enabledD, expandLevel - 1);
@@ -309,7 +287,7 @@ function ParametersBaseReact(data) {
309
287
  }
310
288
  if (Array.isArray(value)) {
311
289
  let arr = value;
312
- if (Array.isArray(range)) { // значит рисуем выпадающий список
290
+ if (Array.isArray(range)) { // render a dropdown list
313
291
  if (typeof arr[0] == "boolean")
314
292
  throw "boolean range is not supported!";
315
293
  let arr2 = arr;
@@ -325,11 +303,10 @@ function ParametersBaseReact(data) {
325
303
  type = itemType;
326
304
  }
327
305
  const elements = arr.map((itemVal, index, array) => {
328
- //работа с массивом отображения и добавления
306
+ // Render and add array elements.
329
307
  const name_ = name + " #" + String(index + 1);
330
308
  const nameElement = _jsx("div", { children: name_ }, name_ + "#$3");
331
309
  const enabledElements = Array.isArray(param.elementsEnabled) ? param.elementsEnabled : undefined;
332
- //const onHoverElement = <div style={{position: "absolute", left: -30, top: 0, display: "flex"}}>
333
310
  const onHoverElement = _jsxs("div", { style: { marginLeft: -18, marginTop: -7 }, children: [_jsx("div", { className: "toButtonEasy", style: { width: "15px", fontSize: "medium", marginRight: 3 }, onClick: () => {
334
311
  array.splice(index, 0, itemVal);
335
312
  enabledElements?.splice(index, 0, enabledElements[index]);
@@ -353,11 +330,7 @@ function ParametersBaseReact(data) {
353
330
  : undefined] });
354
331
  return _jsx(DivHover, { children: (hover) => paramL(hover) }, name_ + "#$");
355
332
  });
356
- return _jsx(CButton, { className: "toIndicatorMenuButton", name: nameButton, status: expanded, header: enableHTML, onExpand: onExpand, children: _jsx("div", { className: "", style: { overflow: "hidden", paddingLeft: nestedMarginLeft }, children: _jsxs("div", { style: { animation: "0.1s ease 0s 1 normal none running moveDown" }, children: [_jsx("div", { className: "", style: {
357
- //marginLeft: widthStr,
358
- //width: "calc(100%-" + widthStr + ")",
359
- color: "inherit"
360
- }, children: elements }), !param.constLength &&
333
+ return _jsx(CButton, { className: "toIndicatorMenuButton", name: nameButton, status: expanded, header: enableHTML, onExpand: onExpand, children: _jsx("div", { className: "", style: { overflow: "hidden", paddingLeft: nestedMarginLeft }, children: _jsxs("div", { style: { animation: "0.1s ease 0s 1 normal none running moveDown" }, children: [_jsx("div", { className: "", style: { color: "inherit" }, children: elements }), !param.constLength &&
361
334
  _jsx("div", { className: "toButtonEasy", style: { paddingLeft: 10, textAlign: "left" /*width: "255px"*/ }, onClick: () => {
362
335
  let element = arr.at(-1);
363
336
  element ??= Array.isArray(param.range) ? param.range[0] : param.range ? param.range.min :
@@ -367,9 +340,7 @@ function ParametersBaseReact(data) {
367
340
  }, children: " ......add new element" })] }) }) }, key + "#$");
368
341
  }
369
342
  }
370
- {
371
- return simpleParameter(Param(set, value, undefined, range, param.labels));
372
- }
343
+ return simpleParameter(Param(set, value, undefined, range, param.labels));
373
344
  }
374
345
  return null;
375
346
  });
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { AgGridReactProps } from 'ag-grid-react';
3
+ import type { GridApi, GetRowIdParams, GridPreDestroyedEvent, GridReadyEvent } from 'ag-grid-community';
4
+ import { type BufferTable, type GetId } from './core';
5
+ export type UseAgGridOptions<T> = {
6
+ /** How to get a row id. Defaults to the `id` field. Captured once (first render). */
7
+ getId?: GetId<T>;
8
+ /** External buffer: a plain object above the component (like datum.tableArr) that survives route remounts. */
9
+ externalBuffer?: BufferTable<T>;
10
+ };
11
+ /**
12
+ * Headless table hook. All race handling lives in the core; the hook only binds it
13
+ * to the React and grid lifecycle:
14
+ *
15
+ * const grid = useAgGrid<Row>({ getId })
16
+ * <AgGridMy<Row> controller={grid} columnDefs={cols} />
17
+ * grid.update({ newData })
18
+ */
19
+ export declare function useAgGrid<T>(options?: UseAgGridOptions<T>): {
20
+ update: (args: import("./core").UpdateArgs<T>) => void;
21
+ remove(rows: Partial<T>[]): void;
22
+ fit: () => boolean;
23
+ updateData: (args: import("./core").UpdateArgs<T>) => void;
24
+ sync: () => void;
25
+ getId: GetId<T>;
26
+ buffer: BufferTable<T>;
27
+ apiRef: React.RefObject<GridApi<T> | null>;
28
+ props: {
29
+ getRowId(params: GetRowIdParams<T>): string;
30
+ onGridReady(event: GridReadyEvent<T>): void;
31
+ onGridPreDestroyed(_event: GridPreDestroyedEvent<T>): void;
32
+ };
33
+ gridProps: {
34
+ getRowId(params: GetRowIdParams<T>): string;
35
+ onGridReady(event: GridReadyEvent<T>): void;
36
+ onGridPreDestroyed(_event: GridPreDestroyedEvent<T>): void;
37
+ };
38
+ getApi(): GridApi<T> | null;
39
+ withApi<R>(fn: (api: GridApi<T>) => R): R | undefined;
40
+ sizeColumnsToFit: () => boolean;
41
+ };
42
+ export type AgGridController<T> = ReturnType<typeof useAgGrid<T>>;
43
+ export type AgGridMyProps<T> = AgGridReactProps<T> & {
44
+ /** Controller from useAgGrid: all wiring (getRowId, ready/destroy, sync) is inside. */
45
+ controller?: AgGridController<T>;
46
+ /** Declarative row upsert for simple cases without a controller. */
47
+ data?: Partial<T>[];
48
+ /** Auto-fit columns to the container. Defaults to true. */
49
+ autoSizeColumns?: boolean;
50
+ };
51
+ declare function AgGridMyInner<T>(props: AgGridMyProps<T>): import("react/jsx-runtime").JSX.Element;
52
+ export declare const AgGridMy: typeof AgGridMyInner;
53
+ export {};
@@ -0,0 +1,132 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Layer 2 (React): headless hook over the core + custom <AgGridMy> component.
3
+ // The hook does NOT render; it returns a controller. AgGridMy is our AgGridReact with
4
+ // project defaults (theme, memo, auto sizing, selection, lifecycle). Wiring is the
5
+ // controller prop; bare <AgGridReact> can still spread gridProps (as in agGrid3).
6
+ import { memo, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { AgGridReact } from 'ag-grid-react';
8
+ import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community';
9
+ import { createGridBuffer } from './core';
10
+ import { useAgGridTheme } from './theme';
11
+ // ag-grid v35 requires module registration. Do it lazily and once, on the first hook
12
+ // use, not when the package is imported (the entry stays side-effect-free).
13
+ // Re-registration by a consumer that already calls GridStyleDefault() is harmless.
14
+ let modulesRegistered = false;
15
+ function ensureAgGridModules() {
16
+ if (modulesRegistered)
17
+ return;
18
+ modulesRegistered = true;
19
+ ModuleRegistry.registerModules([AllCommunityModule]);
20
+ }
21
+ /**
22
+ * Headless table hook. All race handling lives in the core; the hook only binds it
23
+ * to the React and grid lifecycle:
24
+ *
25
+ * const grid = useAgGrid<Row>({ getId })
26
+ * <AgGridMy<Row> controller={grid} columnDefs={cols} />
27
+ * grid.update({ newData })
28
+ */
29
+ export function useAgGrid(options) {
30
+ ensureAgGridModules();
31
+ const apiRef = useRef(null);
32
+ // The core is created once; dependencies live in its closure, with no useCallback chains.
33
+ const [core] = useState(function createCore() {
34
+ const getId = options?.getId ?? (data => String(data?.id));
35
+ return createGridBuffer({ getId, externalBuffer: options?.externalBuffer });
36
+ });
37
+ const gridProps = useMemo(() => ({
38
+ getRowId(params) {
39
+ return core.api.getId(params.data);
40
+ },
41
+ onGridReady(event) {
42
+ apiRef.current = event.api;
43
+ core.control.attach(event.api); // attach runs sync and catches up with the race
44
+ },
45
+ onGridPreDestroyed(_event) {
46
+ apiRef.current = null;
47
+ core.control.detach();
48
+ },
49
+ }), [core]);
50
+ return useMemo(() => {
51
+ const fit = () => {
52
+ const api = apiRef.current;
53
+ if (!api)
54
+ return false;
55
+ api.sizeColumnsToFit();
56
+ return true;
57
+ };
58
+ return {
59
+ update: core.api.updateData,
60
+ remove(rows) {
61
+ core.api.updateData({ removeData: rows });
62
+ },
63
+ fit,
64
+ updateData: core.api.updateData,
65
+ sync: core.api.sync,
66
+ getId: core.api.getId,
67
+ buffer: core.api.buffer,
68
+ apiRef,
69
+ props: gridProps,
70
+ gridProps,
71
+ getApi() {
72
+ return apiRef.current;
73
+ },
74
+ withApi(fn) {
75
+ const api = apiRef.current;
76
+ return api ? fn(api) : undefined;
77
+ },
78
+ sizeColumnsToFit: fit,
79
+ };
80
+ }, [core, gridProps]);
81
+ }
82
+ // --- AgGridMy: our grid with project defaults --------------------------------
83
+ // v35: the canonical row-selection form is an object, not the legacy "multiple" string.
84
+ // Stable module-level reference so the grid does not re-evaluate the option on each render.
85
+ const ROW_SELECTION = { mode: 'multiRow' };
86
+ function AgGridMyInner(props) {
87
+ const { controller, data, autoSizeColumns = true, defaultColDef, theme, getRowId, onGridReady, onGridPreDestroyed, ...rest } = props;
88
+ // Own controller for declarative mode (without an external controller); the hook is unconditional.
89
+ const own = useAgGrid();
90
+ const grid = controller ?? own;
91
+ const defaultTheme = useAgGridTheme();
92
+ const containerRef = useRef(null);
93
+ useEffect(function pushData() {
94
+ if (data)
95
+ grid.update({ newData: data });
96
+ }, [data, grid]);
97
+ useEffect(function observeResize() {
98
+ if (!containerRef.current || !autoSizeColumns)
99
+ return;
100
+ let rafId = 0;
101
+ const observer = new ResizeObserver(function fitColumns(entries) {
102
+ // one observed element: the last entry is the latest size; one RAF per batch,
103
+ // cancelled on cleanup so fit() cannot hit a grid mid-destroy
104
+ const entry = entries[entries.length - 1];
105
+ if (entry.contentRect.width < 400)
106
+ return;
107
+ cancelAnimationFrame(rafId);
108
+ rafId = requestAnimationFrame(() => grid.fit());
109
+ });
110
+ observer.observe(containerRef.current);
111
+ return () => {
112
+ cancelAnimationFrame(rafId);
113
+ observer.disconnect();
114
+ };
115
+ }, [autoSizeColumns, grid]);
116
+ const mergedColDef = useMemo(() => ({ sortable: true, resizable: true, filter: true, ...defaultColDef }), [defaultColDef]);
117
+ return (_jsx("div", { ref: containerRef, style: { height: '100%', width: '100%', overflow: 'hidden' }, children: _jsx(AgGridReact, { theme: theme ?? defaultTheme, asyncTransactionWaitMillis: 50, suppressCellFocus: true, rowSelection: ROW_SELECTION, ...rest, defaultColDef: mergedColDef,
118
+ // getRowId after the spread: undefined from rest must not erase the wiring,
119
+ // otherwise the grid falls back to index-based row identity.
120
+ getRowId: getRowId ?? grid.props.getRowId, onGridReady: function ready(event) {
121
+ grid.props.onGridReady(event);
122
+ if (autoSizeColumns)
123
+ event.api.sizeColumnsToFit();
124
+ onGridReady?.(event);
125
+ }, onGridPreDestroyed: function destroyed(event) {
126
+ grid.props.onGridPreDestroyed(event);
127
+ onGridPreDestroyed?.(event);
128
+ } }) }));
129
+ }
130
+ // memo: the grid does not re-render from unrelated page state; data updates bypass
131
+ // render (controller.updateData). The cast is required because memo() erases the component generic.
132
+ export const AgGridMy = memo(AgGridMyInner);
@@ -0,0 +1,56 @@
1
+ export type RowId = string;
2
+ /** Gets a stable row id. Works with partial data too. */
3
+ export type GetId<T> = (data: Partial<T>) => RowId;
4
+ export type BufferTable<T> = Record<RowId, Partial<T>>;
5
+ export type GridTransaction<T> = {
6
+ add?: T[];
7
+ update?: T[];
8
+ remove?: T[];
9
+ };
10
+ /** Minimal grid api contract (the real GridApi is structurally compatible). */
11
+ export type GridApiLike<T> = {
12
+ forEachNode(callback: (node: {
13
+ data?: T;
14
+ }) => void): void;
15
+ applyTransaction(tx: GridTransaction<T>): unknown;
16
+ applyTransactionAsync(tx: GridTransaction<T>): void;
17
+ };
18
+ export type PushOptions = {
19
+ add?: boolean;
20
+ update?: boolean;
21
+ /** add+update in one SYNCHRONOUS transaction. Default: add is sync, update is async (batched). */
22
+ sync?: boolean;
23
+ };
24
+ export type UpdateArgs<T> = {
25
+ newData?: Partial<T>[];
26
+ removeData?: Partial<T>[];
27
+ /** Only update the buffer; do not touch the grid. */
28
+ onlyMemo?: boolean;
29
+ option?: PushOptions;
30
+ };
31
+ /**
32
+ * Table core: buffer + delivery to the grid. Lifecycle:
33
+ * 1. updateData at any time: the buffer is always updated, the grid only if attached;
34
+ * 2. attach(api) on onGridReady: sync catches up with everything that arrived earlier;
35
+ * 3. detach() on grid destroy: the core accumulates in the buffer again until the next attach.
36
+ */
37
+ export declare function createGridBuffer<T>(deps: {
38
+ getId: GetId<T>;
39
+ /** External buffer (object above the component) survives route remounts. Otherwise use an internal one. */
40
+ externalBuffer?: BufferTable<T>;
41
+ }): {
42
+ control: {
43
+ attach: (gridApi: GridApiLike<T>) => void;
44
+ detach: () => void;
45
+ };
46
+ api: {
47
+ updateData: (args: UpdateArgs<T>) => void;
48
+ sync: () => void;
49
+ getId: GetId<T>;
50
+ buffer: BufferTable<T>;
51
+ };
52
+ };
53
+ export type GridBufferCore<T> = ReturnType<typeof createGridBuffer<T>>;
54
+ export type NumberPair = [a: number, b: number];
55
+ /** Column comparator: numbers as numbers, empty/NaN values last (respecting inversion). */
56
+ export declare function numericComparator<T = any>(map?: (a: any, b: any) => NumberPair): (a1: any, b1: any, _nodeA?: T, _nodeB?: T, inv?: boolean) => number;