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
@@ -0,0 +1,40 @@
1
+ // TS mirror of design tokens (src/style/tokens.css). Values must match CSS.
2
+ // For inline styles, ag-grid themes (styleGrid.ts, grid/agGrid4/theme.ts), and layers (z-index).
3
+ export const tokens = {
4
+ color: {
5
+ bgDark: '#131821',
6
+ bgLight: '#17202e',
7
+ scrollbarTrack: 'rgba(255, 255, 255, 0)',
8
+ scrollbarThumb: '#1d262c',
9
+ scrollbarThumbAlt: '#4c4562',
10
+ textBase: '#c4c4c4',
11
+ textTheme: '#5D9FFA',
12
+ borderCommon: 'rgb(50, 62, 71)',
13
+ },
14
+ menu: {
15
+ bgColor: 'rgba(12, 12, 12, 0.91)',
16
+ blur: '10px',
17
+ border: '1px solid rgba(255, 255, 255, 0.41)',
18
+ activeBackground: 'rgba(255, 255, 255, 0.23)',
19
+ itemColor: '#fff',
20
+ itemHoverColor: '#101010',
21
+ itemHoverBgColor: '#fff',
22
+ },
23
+ font: {
24
+ family: 'Roboto',
25
+ sizeBase: '12px',
26
+ },
27
+ zIndex: {
28
+ /** CSS variable --wenay-z-modal; fallback in ModalProvider */
29
+ modal: 9999,
30
+ },
31
+ /** ag-grid theme params shared by GridStyleDefault (legacy) and agGrid4 buildAgTheme */
32
+ grid: {
33
+ fontFamily: 'Roboto',
34
+ textColor: 'rgba(214,214,214,0.9)',
35
+ tabTextColor: 'rgba(227,227,227,0.9)',
36
+ fontSize: '10px',
37
+ spacing: '3px',
38
+ backgroundColor: 'rgb(19,24,33)',
39
+ },
40
+ };
@@ -6,9 +6,15 @@ declare const optionsDef: {
6
6
  sync: boolean;
7
7
  };
8
8
  type options = Partial<typeof optionsDef>;
9
+ /**
10
+ * @deprecated v1: does not support row removal and silently loses data while the grid is not ready.
11
+ * Use `useAgGrid`/`createGridBuffer` (grid/agGrid4) - buffer + attach/sync,
12
+ * or at least `applyTransactionAsyncUpdate2`.
13
+ */
9
14
  export declare function applyTransactionAsyncUpdate<T>(grid: GridReadyEvent<T, any> | null | undefined, newData: (Partial<T>)[], getId: (...a: any[]) => string, bufTable: {
10
15
  [id: string]: Partial<T>;
11
16
  }, option?: options): void;
17
+ /** @deprecated No-op stub: always returns {}. Never implemented; will be removed in a major version. */
12
18
  export declare function getUpdateTable<T>(grid: GridReadyEvent<T, any> | null | undefined, newData: (Partial<T>)[], getId: (...a: any[]) => string, bufTable: {
13
19
  [id: string]: Partial<T>;
14
20
  }, option?: options): {};
@@ -42,5 +48,6 @@ export declare function applyTransactionAsyncUpdate2<T>(params: params<T>): void
42
48
  type UnUndefined<T extends (any | undefined)> = T extends undefined ? never : T;
43
49
  type t1<T = any> = ColDef<T>["comparator"];
44
50
  type paramsCompare<TData = any> = Parameters<Extract<UnUndefined<t1>, (...args: any) => any>>;
51
+ /** @deprecated Duplicate of `numericComparator` from grid/agGrid4 (core.ts) - use that instead. */
45
52
  export declare function getComparatorGrid<T = any>(func?: (...param: paramsCompare<T>) => [a: number, b: number]): t1;
46
53
  export {};
@@ -1,4 +1,4 @@
1
- // обертка для упрощения работы с гридом через точку памяти
1
+ // Wrapper to simplify working with the grid through a memory point
2
2
  const optionsDef = {
3
3
  update: true,
4
4
  add: true,
@@ -9,7 +9,7 @@ function applyTransactionAsyncUpdate3({ getId, bufTable, option, newData, grid,
9
9
  const op = { ...optionsDef, ...(option ?? {}) };
10
10
  if (!grid?.api.getRowNode)
11
11
  return;
12
- // определяем какие строки надо добавить, а какие только обновить
12
+ // Determine which rows must be added and which only need updates
13
13
  const arrNew = [];
14
14
  const arr = newData.map(e => {
15
15
  const id = getId(e);
@@ -31,16 +31,22 @@ function applyTransactionAsyncUpdate3({ getId, bufTable, option, newData, grid,
31
31
  });
32
32
  return;
33
33
  }
34
- // remove применяем один раз и независимо: иначе при пустых add/update удаление терялось, а при обоих — дублировалось
34
+ // Apply remove once and independently: otherwise removal was lost with empty add/update and duplicated with both
35
35
  if ((arrNew.length && op.add) || remove?.length)
36
36
  grid.api.applyTransaction({ add: (arrNew.length && op.add) ? arrNew : [], remove: remove });
37
37
  if (arr.length && op.update)
38
38
  grid.api.applyTransactionAsync({ update: arr });
39
39
  }
40
- // тут нет удаления но эта версия может использовать где то
40
+ // There is no removal here, but this version may still be used somewhere
41
+ /**
42
+ * @deprecated v1: does not support row removal and silently loses data while the grid is not ready.
43
+ * Use `useAgGrid`/`createGridBuffer` (grid/agGrid4) - buffer + attach/sync,
44
+ * or at least `applyTransactionAsyncUpdate2`.
45
+ */
41
46
  export function applyTransactionAsyncUpdate(grid, newData, getId, bufTable, option) {
42
47
  return applyTransactionAsyncUpdate3({ getId, option, newData, grid, bufTable });
43
48
  }
49
+ /** @deprecated No-op stub: always returns {}. Never implemented; will be removed in a major version. */
44
50
  export function getUpdateTable(grid, newData, getId, bufTable, option) {
45
51
  return {};
46
52
  }
@@ -57,7 +63,7 @@ export function applyTransactionAsyncUpdate2(params) {
57
63
  const g = resolveGrid(params);
58
64
  if (g && params.onlyMemo != true) {
59
65
  if (params.synchronization) {
60
- // === СИНХРОНИЗАЦИЯ: bufTable истина ===
66
+ // === SYNCHRONIZATION: bufTable is the source of truth ===
61
67
  const bufIds = new Set(Object.keys(bufTable));
62
68
  const gridIds = new Set();
63
69
  const arrAdd = [];
@@ -106,17 +112,18 @@ export function applyTransactionAsyncUpdate2(params) {
106
112
  });
107
113
  }
108
114
  }
115
+ /** @deprecated Duplicate of `numericComparator` from grid/agGrid4 (core.ts) - use that instead. */
109
116
  export function getComparatorGrid(func) {
110
117
  return (...param) => {
111
- const [a1, b1, modeA, modeB, inv] = param; // Распаковка параметров в функцию
112
- const [a, b] = func ? func(...param) : [a1, b1]; // Использование преобразующей функции func, если передана
118
+ const [a1, b1, modeA, modeB, inv] = param; // Unpack parameters into the function
119
+ const [a, b] = func ? func(...param) : [a1, b1]; // Use the transforming func if provided
113
120
  return ((typeof a == "number" && !Number.isNaN(a)) &&
114
- (typeof b == "number" && !Number.isNaN(b))) // Если оба a и b - валидные числа
115
- ? a - b // Разница между числами
121
+ (typeof b == "number" && !Number.isNaN(b))) // If both a and b are valid numbers
122
+ ? a - b // Difference between numbers
116
123
  : a == b
117
- ? 0 // Если значения равны, возвращаем 0
124
+ ? 0 // If values are equal, return 0
118
125
  : (!Number.isNaN(b) && b != undefined)
119
- ? (inv ? -1 : 1) // Если b существует: порядок определяется inv
120
- : (inv ? 1 : -1); // Если b отсутствует: порядок определяется inv
126
+ ? (inv ? -1 : 1) // If b exists: order is determined by inv
127
+ : (inv ? 1 : -1); // If b is missing: order is determined by inv
121
128
  };
122
129
  }
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Wraps an array of promise thunks with progress callbacks.
3
+ * CONTRACT: countOk/countError counters are correct only when thunks are run
4
+ * STRICTLY SEQUENTIALLY (one after another). During parallel execution (Promise.all),
5
+ * counter values in callbacks are a race and must not be relied on.
6
+ */
1
7
  export declare function ArrayPromise<T = unknown>({ arr, catchF, thenF }: {
2
8
  arr: (() => Promise<T>)[];
3
9
  thenF?: (data: T, i: number, countOk: number, countError: number, count: number) => unknown;
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Wraps an array of promise thunks with progress callbacks.
3
+ * CONTRACT: countOk/countError counters are correct only when thunks are run
4
+ * STRICTLY SEQUENTIALLY (one after another). During parallel execution (Promise.all),
5
+ * counter values in callbacks are a race and must not be relied on.
6
+ */
1
7
  export function ArrayPromise({ arr, catchF, thenF }) {
2
8
  let ok = 0, countError = 0;
3
9
  const count = arr.length;
@@ -8,7 +14,7 @@ export function ArrayPromise({ arr, catchF, thenF }) {
8
14
  const b = (error, i) => {
9
15
  ++countError;
10
16
  if (catchF)
11
- return catchF?.(error, i, ok, countError, count);
17
+ return catchF(error, i, ok, countError, count);
12
18
  else
13
19
  throw error;
14
20
  };
@@ -1,19 +1,21 @@
1
1
  import { renderBy } from "../../updateBy";
2
- const HostName = location.toString();
2
+ function getHostName() {
3
+ return typeof location != "undefined" ? location.toString() : "wenay-react2";
4
+ }
3
5
  export class CSaveToCache {
4
6
  async set(key, value) {
5
7
  const t = new Response(JSON.stringify(value));
6
- if (window.caches) {
8
+ if (typeof caches != "undefined") {
7
9
  const Cache = await caches.open(key);
8
- await Cache.put(HostName, t);
10
+ await Cache.put(getHostName(), t);
9
11
  return true;
10
12
  }
11
13
  return false;
12
14
  }
13
15
  async get(key) {
14
- if (window.caches) {
16
+ if (typeof caches != "undefined") {
15
17
  const Cache = await caches.open(key);
16
- const cachedResponse = await Cache.match(HostName);
18
+ const cachedResponse = await Cache.match(getHostName());
17
19
  if (cachedResponse) {
18
20
  return (await cachedResponse.json());
19
21
  }
@@ -21,7 +23,7 @@ export class CSaveToCache {
21
23
  return null;
22
24
  }
23
25
  async delete(key) {
24
- if (window.caches) {
26
+ if (typeof caches != "undefined") {
25
27
  return await caches.delete(key);
26
28
  }
27
29
  return false;
@@ -29,14 +31,14 @@ export class CSaveToCache {
29
31
  }
30
32
  export class CSaveToLocalStorage {
31
33
  async set(key, value) {
32
- if (window.localStorage) {
34
+ if (typeof localStorage != "undefined") {
33
35
  await localStorage.setItem(key, JSON.stringify(value));
34
36
  return true;
35
37
  }
36
38
  return false;
37
39
  }
38
40
  async get(key) {
39
- if (window.localStorage) {
41
+ if (typeof localStorage != "undefined") {
40
42
  const st = await localStorage.getItem(key);
41
43
  if (st) {
42
44
  try {
@@ -50,14 +52,14 @@ export class CSaveToLocalStorage {
50
52
  return null;
51
53
  }
52
54
  async delete(key) {
53
- if (window.localStorage) {
55
+ if (typeof localStorage != "undefined") {
54
56
  localStorage.removeItem(key);
55
57
  return true;
56
58
  }
57
59
  return false;
58
60
  }
59
61
  async deleteAll() {
60
- if (window.localStorage) {
62
+ if (typeof localStorage != "undefined") {
61
63
  await localStorage.clear();
62
64
  return true;
63
65
  }
@@ -66,14 +68,12 @@ export class CSaveToLocalStorage {
66
68
  }
67
69
  export const CacheG = new CSaveToCache();
68
70
  export const CacheLocal = new CSaveToLocalStorage();
69
- async function addDataToMap(data, map) {
70
- if (data) {
71
- for (let [k, v] of data) {
72
- const tr = (map.get(k) || map.set(k, v).get(k));
73
- if (tr && typeof tr === 'object') {
74
- Object.assign(tr, v);
75
- renderBy(tr);
76
- }
71
+ function addDataToMap(data, map) {
72
+ for (let [k, v] of data) {
73
+ const tr = map.has(k) ? map.get(k) : map.set(k, v).get(k);
74
+ if (tr && typeof tr === 'object') {
75
+ Object.assign(tr, v);
76
+ renderBy(tr);
77
77
  }
78
78
  }
79
79
  }
@@ -93,17 +93,20 @@ export const ObjectStringToDate = (obj) => {
93
93
  });
94
94
  }
95
95
  };
96
+ // module-level constant: this runs for every string of every cached object on load
97
+ const ISO_DATE_RE = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z)?$/;
96
98
  function isDate(_date) {
97
- const _regExp = new RegExp('^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$');
98
- return _regExp.test(_date);
99
+ return ISO_DATE_RE.test(_date);
99
100
  }
100
101
  export function CacheFuncMapBase(arr, Save) {
101
102
  return {
102
103
  async load() {
103
104
  for (let [k, v] of arr) {
104
105
  const t = await Save.get(k);
106
+ if (!t)
107
+ continue;
105
108
  ObjectStringToDate(t);
106
- await addDataToMap(t, v);
109
+ addDataToMap(t, v);
107
110
  }
108
111
  },
109
112
  async save() {
@@ -7,5 +7,5 @@ interface IStepInputElement extends HTMLElement {
7
7
  export declare function SetAutoStepForElement(element: IStepInputElement, params?: {
8
8
  minStep?: number | undefined;
9
9
  maxStep?: number;
10
- }): void;
10
+ }): () => void;
11
11
  export {};
@@ -1,7 +1,18 @@
1
1
  /// <reference lib="dom" />
2
- // задать автоматическое управление шагом для элемента input
2
+ // Set automatic step control for an input element
3
3
  import { GetDblPrecision, GetDblPrecision2, NormalizeDouble } from "wenay-common2";
4
+ // step is a power of 0.1 (0.1, 0.01, ...) - resilient to float noise,
5
+ // unlike the previous Math.log10(step)%1==0
6
+ function isPowerOfTenth(step) {
7
+ if (!(step > 0 && step < 1))
8
+ return false;
9
+ const digits = Math.round(-Math.log10(step));
10
+ return Math.abs(Math.pow(0.1, digits) - step) < step * 1e-9;
11
+ }
12
+ // Own handlers on the element: repeated calls remove previous ones (like the old on* overwrite)
13
+ const appliedHandlers = new WeakMap();
4
14
  export function SetAutoStepForElement(element, params = { maxStep: 1 }) {
15
+ appliedHandlers.get(element)?.();
5
16
  function parse(valueStr) { let val = parseFloat(valueStr); return isNaN(val) ? null : val; }
6
17
  const { minStep, maxStep = 1 } = params;
7
18
  const maxDigits = minStep ? GetDblPrecision(minStep) : undefined;
@@ -16,7 +27,7 @@ export function SetAutoStepForElement(element, params = { maxStep: 1 }) {
16
27
  //function NormalizeDouble(value :number, digits :number) { let factor=Math.pow(10, digits); return Math.round(value * factor)/factor; }
17
28
  //if (dotPos===valueStr.length-1) dotPos--;
18
29
  //let digits= GetDblPrecision2(parseFloat(valueStr), minDigits, 10);
19
- let dotPos = valueStr.search(/\.|,/); // Находим точку или запятую
30
+ let dotPos = valueStr.search(/\.|,/); // Find a dot or comma
20
31
  let digits = (dotPos >= 0) ? valueStr.length - dotPos - 1 : 0;
21
32
  digits = Math.max(digits, minDigits);
22
33
  if (digits > 10)
@@ -34,20 +45,15 @@ export function SetAutoStepForElement(element, params = { maxStep: 1 }) {
34
45
  //if (Math.abs(min) < step) min= Math.floor(Math.abs(min)/step)*step * Math.sign(min);
35
46
  if (_min != null) {
36
47
  if (Math.abs(_min) % step != 0)
37
- _min = Math.floor(Math.abs(_min) / step) * step * Math.sign(minDefault);
48
+ _min = Math.floor(Math.abs(_min) / step) * step * Math.sign(_min);
38
49
  element.min = _min + "";
39
50
  }
40
- //console.log("step:",element.step+"->"+step," min:",element.min+"->"+min);
41
- //console.log("minDefault:",_minDefault,parseFloat(_minDefault));
42
51
  element.step = step + "";
43
- //console.log("dotPos:",dotPos," _digits:",_digits, "minDigits:",minDigits);
44
- return step; ////lib.GetDblPrecision($fee[0].value)); }
52
+ return step;
45
53
  }
46
- let modeAuto = !_step || (_step < 1 && Math.log10(_step - Math.trunc(_step)) % 1 == 0); // является степенью 0.1
47
- const modeAuto0 = modeAuto;
54
+ let modeAuto = !_step || isPowerOfTenth(_step);
48
55
  if (modeAuto) {
49
56
  calculateStep((_step ? (Math.round(parseFloat(element.value) / _step) * _step) : element.value) + "");
50
- //console.log(element.value, step);
51
57
  }
52
58
  if (_step && minDefault && Math.abs(minDefault % _step) > 1e-10)
53
59
  modeAuto = false;
@@ -55,16 +61,13 @@ export function SetAutoStepForElement(element, params = { maxStep: 1 }) {
55
61
  modeAuto = false;
56
62
  else
57
63
  modeAuto ||= (_step != null && (minStep == null || _step > minStep));
58
- //console.log("###",{value: element.value, modeAuto0, modeAuto, _step, _digits});
59
- //element.onkeydown= (e)=> { if (_step) element.step = "0.00000001"; /*String(_step/10);*/ console.log("keydown", {value: element.value}); }
60
- //element.onkeyup= ()=>{ if (_step) element.step= _step+""; console.log("keyup", {modeAuto, value: element.value, step: calculateStep(element.value), _digits}); if (modeAuto) calculateStep(element.value); }// console.log(element.step); }// console.log(element.value, element.step); }
61
- element.onkeyup = () => { if (modeAuto)
62
- calculateStep(element.value); }; // console.log(element.step); }// console.log(element.value, element.step); }
63
- element.onchange = () => {
64
+ // addEventListener instead of on*=: do not overwrite other handlers; return a disposer
65
+ const onKeyUp = () => { if (modeAuto)
66
+ calculateStep(element.value); };
67
+ const onChange = () => {
64
68
  let digits = _digits;
65
69
  if (digits != null)
66
70
  element.value = parseFloat(element.value).toFixed(digits);
67
- //console.log("change", {value: element.value, digits, minDigits});
68
71
  if (minDefault != null && parseFloat(element.value) < minDefault) {
69
72
  element.step = stepDefault + "";
70
73
  element.value = minDefault + "";
@@ -72,8 +75,16 @@ export function SetAutoStepForElement(element, params = { maxStep: 1 }) {
72
75
  _digits = null;
73
76
  }
74
77
  element.setAttribute("value", element.value);
75
- }; //Math.round($fee[0].value/$fee[0].step)*$fee[0].step; }
76
- //element.onkeyup();
78
+ };
79
+ element.addEventListener("keyup", onKeyUp);
80
+ element.addEventListener("change", onChange);
81
+ const dispose = () => {
82
+ element.removeEventListener("keyup", onKeyUp);
83
+ element.removeEventListener("change", onChange);
84
+ appliedHandlers.delete(element);
85
+ };
86
+ appliedHandlers.set(element, dispose);
87
+ return dispose;
77
88
  }
78
89
  //import * as Time from "./Time"
79
90
  //import {const_Date} from "./Time";
@@ -36,20 +36,19 @@ export function deepMergeWithMap(target, source, visited = new Map()) {
36
36
  }
37
37
  return target;
38
38
  }
39
- const map = new Map();
39
+ // WeakSet: a strict Map kept every def object alive forever
40
+ const merged = new WeakSet();
40
41
  export function staticGetAdd(key, def, options = { reversDeep: false }) {
41
- if (options.deepAutoMerge && !map.get(def)) {
42
- map.set(def, true);
43
- if (options.deepAutoMerge) {
44
- if (!options.reversDeep)
45
- staticProps.set(key, deepMergeWithMap(staticProps.get(key) ?? {}, def));
46
- else
47
- staticProps.set(key, deepMergeWithMap(deepClone(def), staticProps.get(key) ?? {}));
48
- }
42
+ if (options.deepAutoMerge && !merged.has(def)) {
43
+ merged.add(def);
44
+ if (!options.reversDeep)
45
+ staticProps.set(key, deepMergeWithMap(staticProps.get(key) ?? {}, def));
46
+ else
47
+ staticProps.set(key, deepMergeWithMap(deepClone(def), staticProps.get(key) ?? {}));
49
48
  }
50
49
  if (options.abs)
51
50
  staticProps.set(key, def);
52
- const t = (staticProps.get(key) || staticProps.set(key, def).get(key));
51
+ const t = (staticProps.has(key) ? staticProps.get(key) : staticProps.set(key, def).get(key));
53
52
  return t; // Object.assign(def, t) // t //
54
53
  }
55
54
  export function staticGetById(key, def, id) {
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  type Listener = (a?: any) => void;
2
3
  interface ObserverState {
3
4
  listeners: Set<Listener>;
@@ -8,9 +9,27 @@ export declare const mapWait: Map<object, {
8
9
  refreshAsync: (ms: number, func: () => any) => void;
9
10
  refreshAsync2: (ms: number, func: () => any) => Promise<void>;
10
11
  }>;
12
+ /**
13
+ * Ветка Dispatch<SetStateAction<T>>: listener получает тот же мутированный объект,
14
+ * setState(тот же ref) React бэйлаутит без ререндера — используйте (a: T) => void.
15
+ */
16
+ export type UpdateCallback<T extends object> = React.Dispatch<React.SetStateAction<T>> | ((a: T) => void);
11
17
  export declare function renderBy(a: object, ms?: number): void;
12
18
  export declare function renderByRevers(a: object, ms?: number, reverse?: boolean): void;
13
19
  export declare function renderByLast(a: object, ms?: number): void;
14
- export declare function useUpdateBy<T extends object>(a: T, f?: React.Dispatch<React.SetStateAction<T>> | ((a: T) => void)): void;
15
- export declare function updateBy<T extends object>(a: T, f?: React.Dispatch<React.SetStateAction<T>> | ((a: T) => void)): void;
20
+ export declare function useUpdateBy<T extends object>(a: T, f?: UpdateCallback<T>): void;
21
+ export declare function updateBy<T extends object>(a: T, f?: UpdateCallback<T>): void;
22
+ export type UpdateApi<T extends object> = {
23
+ object: T;
24
+ emit(ms?: number): void;
25
+ render(ms?: number): void;
26
+ renderReverse(ms?: number, reverse?: boolean): void;
27
+ renderLast(ms?: number): void;
28
+ on(listener: (obj: T) => void): () => void;
29
+ subscribe(listener: (obj: T) => void): () => void;
30
+ use(f?: UpdateCallback<T>): void;
31
+ useSubscribe(f?: UpdateCallback<T>): void;
32
+ };
33
+ export declare function createUpdateApi<T extends object>(obj: T): UpdateApi<T>;
34
+ export declare function useUpdateByApi<T extends object>(obj: T, f?: UpdateCallback<T>): UpdateApi<T>;
16
35
  export {};
@@ -1,7 +1,16 @@
1
- import { useLayoutEffect, useSyncExternalStore } from "react";
2
- import { waitRun } from "wenay-common2";
1
+ import { useCallback, useLayoutEffect, useSyncExternalStore } from "react";
2
+ import { UseListen, waitRun } from "wenay-common2";
3
3
  export const map3 = new WeakMap();
4
4
  export const mapWait = new Map();
5
+ const updateListens = new WeakMap();
6
+ function getUpdateListen(obj) {
7
+ let listen = updateListens.get(obj);
8
+ if (!listen) {
9
+ listen = UseListen();
10
+ updateListens.set(obj, listen);
11
+ }
12
+ return listen;
13
+ }
5
14
  function getObserverState(obj) {
6
15
  let state = map3.get(obj);
7
16
  if (!state) {
@@ -10,68 +19,64 @@ function getObserverState(obj) {
10
19
  }
11
20
  return state;
12
21
  }
22
+ // reverse/lastOnly действуют только на React-подписчиков (useUpdateBy);
23
+ // императивные on-подписчики вызываются всегда все, в порядке подписки
13
24
  function triggerUpdate(obj, reverse = false, lastOnly = false) {
14
- const state = map3.get(obj);
15
- if (!state || state.listeners.size === 0)
25
+ const listen = updateListens.get(obj);
26
+ let state = map3.get(obj);
27
+ if ((!state || state.listeners.size === 0) && !listen?.[1].count())
16
28
  return;
29
+ state ??= getObserverState(obj);
17
30
  state.version += 1;
18
31
  let listenersArray = Array.from(state.listeners);
19
32
  if (lastOnly) {
20
33
  const last = listenersArray.at(-1);
21
34
  if (last)
22
35
  last(obj);
36
+ listen?.[0](obj);
23
37
  return;
24
38
  }
25
39
  if (reverse) {
26
40
  listenersArray.reverse();
27
41
  }
28
42
  listenersArray.forEach(listener => listener(obj));
43
+ listen?.[0](obj);
29
44
  }
30
- export function renderBy(a, ms) {
45
+ function schedule(a, ms, reverse = false, lastOnly = false) {
31
46
  if (ms) {
32
47
  (mapWait.get(a) || mapWait.set(a, waitRun()).get(a))
33
48
  .refreshAsync(ms, () => {
34
49
  mapWait.delete(a);
35
- triggerUpdate(a);
50
+ triggerUpdate(a, reverse, lastOnly);
36
51
  });
37
52
  }
38
53
  else
39
- triggerUpdate(a);
54
+ triggerUpdate(a, reverse, lastOnly);
55
+ }
56
+ export function renderBy(a, ms) {
57
+ schedule(a, ms);
40
58
  }
41
59
  export function renderByRevers(a, ms, reverse = true) {
42
- if (ms) {
43
- (mapWait.get(a) || mapWait.set(a, waitRun()).get(a))
44
- .refreshAsync(ms, () => {
45
- mapWait.delete(a);
46
- triggerUpdate(a, reverse);
47
- });
48
- }
49
- else
50
- triggerUpdate(a, reverse);
60
+ schedule(a, ms, reverse);
51
61
  }
52
62
  export function renderByLast(a, ms) {
53
- if (ms) {
54
- (mapWait.get(a) || mapWait.set(a, waitRun()).get(a))
55
- .refreshAsync(ms, () => {
56
- mapWait.delete(a);
57
- triggerUpdate(a, false, true);
58
- });
59
- }
60
- else
61
- triggerUpdate(a, false, true);
63
+ schedule(a, ms, false, true);
62
64
  }
63
65
  export function useUpdateBy(a, f) {
64
- useSyncExternalStore((listener) => {
66
+ // стабильный subscribe: без useCallback React переподписывается на каждый рендер,
67
+ // а удаление state из map3 при этом сбрасывало бы version → лишний форс-ререндер
68
+ const subscribe = useCallback((listener) => {
65
69
  if (f)
66
70
  return () => { };
67
71
  const state = getObserverState(a);
68
72
  state.listeners.add(listener);
69
73
  return () => {
70
74
  state.listeners.delete(listener);
71
- if (state.listeners.size === 0)
72
- map3.delete(a);
73
75
  };
74
- }, () => (f ? 0 : getObserverState(a).version));
76
+ }, [a, f]);
77
+ useSyncExternalStore(subscribe,
78
+ // getSnapshot must be pure: only read the version, subscribe creates the state
79
+ () => (f ? 0 : (map3.get(a)?.version ?? 0)));
75
80
  useLayoutEffect(() => {
76
81
  if (!f)
77
82
  return;
@@ -79,11 +84,41 @@ export function useUpdateBy(a, f) {
79
84
  state.listeners.add(f);
80
85
  return () => {
81
86
  state.listeners.delete(f);
82
- if (state.listeners.size === 0)
83
- map3.delete(a);
84
87
  };
85
88
  }, [a, f]);
86
89
  }
87
90
  export function updateBy(a, f) {
88
91
  useUpdateBy(a, f);
89
92
  }
93
+ const apiCache = new WeakMap();
94
+ export function createUpdateApi(obj) {
95
+ const cached = apiCache.get(obj);
96
+ if (cached)
97
+ return cached;
98
+ const [, listen] = getUpdateListen(obj);
99
+ const api = {
100
+ object: obj,
101
+ emit(ms) { renderBy(obj, ms); },
102
+ render(ms) { renderBy(obj, ms); },
103
+ renderReverse(ms, reverse = true) { renderByRevers(obj, ms, reverse); },
104
+ renderLast(ms) { renderByLast(obj, ms); },
105
+ on(listener) {
106
+ return listen.on((value) => listener(value));
107
+ },
108
+ subscribe(listener) {
109
+ return api.on(listener);
110
+ },
111
+ use(f) {
112
+ useUpdateBy(obj, f);
113
+ },
114
+ useSubscribe(f) {
115
+ useUpdateBy(obj, f);
116
+ },
117
+ };
118
+ apiCache.set(obj, api);
119
+ return api;
120
+ }
121
+ export function useUpdateByApi(obj, f) {
122
+ useUpdateBy(obj, f);
123
+ return createUpdateApi(obj);
124
+ }
package/lib/index.js CHANGED
@@ -1,3 +1 @@
1
1
  export * from "./common/api";
2
- import { test } from "./common/api";
3
- test();