wenay-react2 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/lib/common/api.d.ts +17 -0
  2. package/lib/common/api.js +24 -0
  3. package/lib/common/src/components/Buttons/MiniButton.d.ts +17 -0
  4. package/lib/common/src/components/Buttons/MiniButton.js +18 -0
  5. package/lib/common/src/components/Buttons/index.d.ts +1 -0
  6. package/lib/common/src/components/Buttons/index.js +1 -0
  7. package/lib/common/src/components/Dnd/DraggableOutlineDiv.d.ts +1 -0
  8. package/lib/common/src/components/Dnd/DraggableOutlineDiv.js +31 -0
  9. package/lib/common/src/components/Dnd/RNDFunc.d.ts +13 -0
  10. package/lib/common/src/components/Dnd/RNDFunc.js +111 -0
  11. package/lib/common/src/components/Dnd/RNDFunc3.d.ts +81 -0
  12. package/lib/common/src/components/Dnd/RNDFunc3.js +380 -0
  13. package/lib/common/src/components/Dnd/Resizable.d.ts +15 -0
  14. package/lib/common/src/components/Dnd/Resizable.js +36 -0
  15. package/lib/common/src/components/Dnd/index.d.ts +4 -0
  16. package/lib/common/src/components/Dnd/index.js +4 -0
  17. package/lib/common/src/components/Input.d.ts +28 -0
  18. package/lib/common/src/components/Input.js +30 -0
  19. package/lib/common/src/components/Menu/RightMenu.d.ts +22 -0
  20. package/lib/common/src/components/Menu/RightMenu.js +179 -0
  21. package/lib/common/src/components/Menu/StickerMenu.d.ts +5 -0
  22. package/lib/common/src/components/Menu/StickerMenu.js +110 -0
  23. package/lib/common/src/components/Menu/index.d.ts +2 -0
  24. package/lib/common/src/components/Menu/index.js +2 -0
  25. package/lib/common/src/components/Modal/LeftModal.d.ts +58 -0
  26. package/lib/common/src/components/Modal/LeftModal.js +284 -0
  27. package/lib/common/src/components/Modal/Modal.d.ts +31 -0
  28. package/lib/common/src/components/Modal/Modal.js +104 -0
  29. package/lib/common/src/components/Modal/index.d.ts +2 -0
  30. package/lib/common/src/components/Modal/index.js +2 -0
  31. package/lib/common/src/components/MyResizeObserver.d.ts +10 -0
  32. package/lib/common/src/components/MyResizeObserver.js +94 -0
  33. package/lib/common/src/components/Other.d.ts +9 -0
  34. package/lib/common/src/components/Other.js +31 -0
  35. package/lib/common/src/components/Parameters.d.ts +10 -0
  36. package/lib/common/src/components/Parameters.js +24 -0
  37. package/lib/common/src/components/ParametersEngine.d.ts +8 -0
  38. package/lib/common/src/components/ParametersEngine.js +373 -0
  39. package/lib/common/src/components/index.d.ts +9 -0
  40. package/lib/common/src/components/index.js +9 -0
  41. package/lib/common/src/hooks/index.d.ts +3 -0
  42. package/lib/common/src/hooks/index.js +3 -0
  43. package/lib/common/src/hooks/useAddDownAnyKey.d.ts +5 -0
  44. package/lib/common/src/hooks/useAddDownAnyKey.js +22 -0
  45. package/lib/common/src/hooks/useDraggable.d.ts +15 -0
  46. package/lib/common/src/hooks/useDraggable.js +134 -0
  47. package/lib/common/src/hooks/useOutside.d.ts +40 -0
  48. package/lib/common/src/hooks/useOutside.js +68 -0
  49. package/lib/common/src/logs/logs.d.ts +163 -0
  50. package/lib/common/src/logs/logs.js +249 -0
  51. package/lib/common/src/logs/logs3.d.ts +63 -0
  52. package/lib/common/src/logs/logs3.js +245 -0
  53. package/lib/common/src/logs/miniLogs.d.ts +5 -0
  54. package/lib/common/src/logs/miniLogs.js +51 -0
  55. package/lib/common/src/menu/menu.d.ts +72 -0
  56. package/lib/common/src/menu/menu.js +230 -0
  57. package/lib/common/src/menu/menuMouse.d.ts +21 -0
  58. package/lib/common/src/menu/menuMouse.js +32 -0
  59. package/lib/common/src/menu/menuR.d.ts +17 -0
  60. package/lib/common/src/menu/menuR.js +116 -0
  61. package/lib/common/src/myChart/1/myChart.d.ts +40 -0
  62. package/lib/common/src/myChart/1/myChart.js +306 -0
  63. package/lib/common/src/myChart/1/myChartTest.d.ts +1 -0
  64. package/lib/common/src/myChart/1/myChartTest.js +45 -0
  65. package/lib/common/src/myChart/chartEngine/chartEngineReact.d.ts +164 -0
  66. package/lib/common/src/myChart/chartEngine/chartEngineReact.js +834 -0
  67. package/lib/common/src/styles/index.d.ts +1 -0
  68. package/lib/common/src/styles/index.js +1 -0
  69. package/lib/common/src/styles/styleGrid.d.ts +20 -0
  70. package/lib/common/src/styles/styleGrid.js +50 -0
  71. package/lib/common/src/utils/applyTransactionAsyncUpdate.d.ts +42 -0
  72. package/lib/common/src/utils/applyTransactionAsyncUpdate.js +97 -0
  73. package/lib/common/src/utils/arrayPromise.d.ts +5 -0
  74. package/lib/common/src/utils/arrayPromise.js +16 -0
  75. package/lib/common/src/utils/cache.d.ts +31 -0
  76. package/lib/common/src/utils/cache.js +119 -0
  77. package/lib/common/src/utils/index.d.ts +6 -0
  78. package/lib/common/src/utils/index.js +6 -0
  79. package/lib/common/src/utils/inputAutoStep.d.ts +4 -0
  80. package/lib/common/src/utils/inputAutoStep.js +79 -0
  81. package/lib/common/src/utils/mapMemory.d.ts +26 -0
  82. package/lib/common/src/utils/mapMemory.js +72 -0
  83. package/lib/common/src/utils/pageVisibilityContext.d.ts +5 -0
  84. package/lib/common/src/utils/pageVisibilityContext.js +18 -0
  85. package/lib/common/updateBy.d.ts +12 -0
  86. package/lib/common/updateBy.js +88 -0
  87. package/lib/index.d.ts +1 -0
  88. package/lib/index.js +3 -0
  89. package/lib/style/menuRight.css +139 -0
  90. package/lib/style/style.css +334 -0
  91. package/lib/tsconfig.json +6 -0
  92. package/package.json +50 -0
@@ -0,0 +1,245 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { AgGridReact } from 'ag-grid-react';
4
+ /** -----------------------------
5
+ * 2. Функция staticGetAdd —
6
+ * загружает/сохраняет данные в localStorage.
7
+ * -----------------------------
8
+ */
9
+ function staticGetAdd(key, defaultValue) {
10
+ try {
11
+ const stored = localStorage.getItem(key);
12
+ // Если в localStorage ничего нет, то записываем defaultValue
13
+ if (!stored) {
14
+ localStorage.setItem(key, JSON.stringify(defaultValue));
15
+ return defaultValue;
16
+ }
17
+ // Если что-то нашли, пытаемся объединить с defaultValue
18
+ // (на случай, если в defaultValue появились новые поля)
19
+ const parsed = JSON.parse(stored);
20
+ return { ...defaultValue, ...parsed };
21
+ }
22
+ catch (error) {
23
+ console.error("Ошибка чтения localStorage:", error);
24
+ return defaultValue;
25
+ }
26
+ }
27
+ /** -----------------------------
28
+ * 4. Создаём сам контекст
29
+ * и провайдер для логов + настроек
30
+ * -----------------------------
31
+ */
32
+ const LogsContext = createContext(null);
33
+ export function LogsProvider({ children }) {
34
+ // 4.1. Загружаем настройки из localStorage через staticGetAdd
35
+ const savedSettings = staticGetAdd("logSettings", {
36
+ minVarLogs: 0,
37
+ minVarMessage: 0,
38
+ timeShow: 2,
39
+ showMessages: true
40
+ });
41
+ // 4.2. Список логов в памяти (не сохраняем логи в localStorage,
42
+ // только настройки — но можно и логи, если нужно)
43
+ const [logs, setLogs] = useState([]);
44
+ const counterRef = useRef(0);
45
+ // 4.3. Сами настройки (инициализируем тем, что вернул staticGetAdd)
46
+ const [minVarLogs, setMinVarLogs] = useState(savedSettings.minVarLogs);
47
+ const [minVarMessage, setMinVarMessage] = useState(savedSettings.minVarMessage);
48
+ const [timeShow, setTimeShow] = useState(savedSettings.timeShow);
49
+ const [showMessages, setShowMessages] = useState(savedSettings.showMessages);
50
+ // 4.4. Следим за изменениями настроек и сохраняем их обратно в localStorage
51
+ useEffect(() => {
52
+ const toSave = {
53
+ minVarLogs,
54
+ minVarMessage,
55
+ timeShow,
56
+ showMessages
57
+ };
58
+ localStorage.setItem("logSettings", JSON.stringify(toSave));
59
+ }, [minVarLogs, minVarMessage, timeShow, showMessages]);
60
+ // 4.5. Функция добавления лога (генерируем поле num автоматически)
61
+ const addLog = useCallback((input) => {
62
+ counterRef.current += 1;
63
+ const num = counterRef.current;
64
+ setLogs((prevLogs) => {
65
+ const newLog = { ...input, num };
66
+ // ограничимся 500 логами (можно менять)
67
+ return [newLog, ...prevLogs].slice(0, 500);
68
+ });
69
+ }, []);
70
+ // 4.6. Возвращаем провайдер контекста
71
+ return (_jsx(LogsContext.Provider, { value: {
72
+ logs,
73
+ addLog,
74
+ minVarLogs,
75
+ setMinVarLogs,
76
+ minVarMessage,
77
+ setMinVarMessage,
78
+ timeShow,
79
+ setTimeShow,
80
+ showMessages,
81
+ setShowMessages,
82
+ }, children: children }));
83
+ }
84
+ /** -----------------------------
85
+ * 5. Хук для удобного доступа к контексту
86
+ * -----------------------------
87
+ */
88
+ export function useLogsContext() {
89
+ const ctx = useContext(LogsContext);
90
+ if (!ctx) {
91
+ throw new Error('useLogsContext must be used within LogsProvider');
92
+ }
93
+ return ctx;
94
+ }
95
+ /** -----------------------------
96
+ * 6. Компонент LogsTable
97
+ * (аналог PageLogs)
98
+ * -----------------------------
99
+ */
100
+ export function LogsTable() {
101
+ const { logs, minVarLogs } = useLogsContext();
102
+ const gridRef = useRef(null);
103
+ // Определения колонок
104
+ const columnDefs = useMemo(() => [
105
+ {
106
+ field: 'time',
107
+ headerName: 'Время',
108
+ sort: 'desc',
109
+ valueFormatter: (params) => {
110
+ const dateObj = params.value;
111
+ if (!dateObj)
112
+ return '';
113
+ return new Date(dateObj).toLocaleTimeString();
114
+ },
115
+ width: 120,
116
+ },
117
+ { field: 'id', headerName: 'ID', width: 80 },
118
+ { field: 'var', headerName: 'Важность', width: 90 },
119
+ {
120
+ field: 'txt',
121
+ headerName: 'Сообщение',
122
+ flex: 1,
123
+ wrapText: true,
124
+ autoHeight: true
125
+ }
126
+ ], []);
127
+ const defaultColDef = useMemo(() => ({
128
+ resizable: true,
129
+ sortable: true,
130
+ filter: true,
131
+ wrapText: true,
132
+ }), []);
133
+ // Следим за minVarLogs и настраиваем фильтр AG Grid
134
+ useEffect(() => {
135
+ if (gridRef.current?.api) {
136
+ if (minVarLogs > 0) {
137
+ gridRef.current.api.setFilterModel({
138
+ var: {
139
+ filterType: 'number',
140
+ type: 'greaterThanOrEqual',
141
+ filter: minVarLogs
142
+ }
143
+ });
144
+ }
145
+ else {
146
+ gridRef.current.api.setFilterModel(null);
147
+ }
148
+ }
149
+ }, [minVarLogs]);
150
+ return (
151
+ // <div className="ag-theme-alpine-dark" style={{ width: '100%', height: '100%' }}>
152
+ _jsx("div", { style: { width: '100%', height: '100%' }, children: _jsx(AgGridReact, { ref: gridRef, onGridReady: (params) => {
153
+ gridRef.current = params;
154
+ params.api.sizeColumnsToFit();
155
+ }, rowData: logs, columnDefs: columnDefs, defaultColDef: defaultColDef, headerHeight: 30, rowHeight: 26 }) }));
156
+ }
157
+ export function LogsNotifications() {
158
+ const { logs, minVarMessage, timeShow, showMessages, setShowMessages } = useLogsContext();
159
+ const [notifications, setNotifications] = useState([]);
160
+ const counterRef = useRef(0);
161
+ useEffect(() => {
162
+ if (logs.length === 0)
163
+ return;
164
+ const newestLog = logs[0];
165
+ if ((newestLog.var ?? 0) < minVarMessage)
166
+ return;
167
+ counterRef.current += 1;
168
+ const newItem = { id: counterRef.current, log: newestLog };
169
+ setNotifications((prev) => [newItem, ...prev]);
170
+ // убираем нотификацию через timeShow секунд
171
+ const timer = setTimeout(() => {
172
+ setNotifications((prev) => prev.filter((item) => item.id !== newItem.id));
173
+ }, timeShow * 1000);
174
+ return () => clearTimeout(timer);
175
+ }, [logs, minVarMessage, timeShow]);
176
+ if (!showMessages) {
177
+ // Если скрыли всплывашки, показываем только "log"
178
+ return (_jsx("div", { style: { position: 'absolute', right: 10, top: 10, zIndex: 999 }, children: _jsx("div", { style: {
179
+ background: 'rgb(144,60,60)',
180
+ padding: '6px 10px',
181
+ cursor: 'pointer'
182
+ }, onClick: () => setShowMessages(true), children: "log" }) }));
183
+ }
184
+ // Иначе выводим список текущих нотификаций
185
+ return (_jsxs("div", { style: { position: 'absolute', right: 10, top: 10, zIndex: 999 }, children: [_jsx("div", { style: {
186
+ background: 'rgb(58,58,58)',
187
+ fontSize: '20px',
188
+ padding: '6px 10px',
189
+ cursor: 'pointer'
190
+ }, onClick: () => setShowMessages(false), children: "X" }), _jsx("div", { children: notifications.slice(0, 10).map(({ id, log }) => {
191
+ let red = (log.var ?? 0) * 10;
192
+ if (red > 255)
193
+ red = 255;
194
+ return (_jsxs("div", { className: "testAnime example-exit", style: {
195
+ width: 200,
196
+ color: 'white',
197
+ marginTop: 10,
198
+ borderRight: '5px solid #5D9FFA',
199
+ backgroundColor: `rgb(${red},73,35)`,
200
+ padding: 8,
201
+ wordWrap: 'break-word'
202
+ }, children: [_jsx("p", { style: { textAlign: 'center', fontSize: 10, marginBottom: 1 }, children: "\u043E\u043F\u043E\u0432\u0435\u0449\u0435\u043D\u0438\u0435" }), _jsx("hr", { style: {
203
+ backgroundImage: 'linear-gradient(to right, transparent, rgba(255, 255, 255, 1), transparent)',
204
+ border: 0,
205
+ height: 1,
206
+ margin: 0
207
+ } }), _jsx("div", { style: { textAlign: 'right', marginRight: 10 }, children: typeof log.txt === 'object' ? JSON.stringify(log.txt) : log.txt }), _jsx("p", { style: { textAlign: 'right', marginRight: 10 }, children: log.time.toLocaleDateString() })] }, id));
208
+ }) })] }));
209
+ }
210
+ /** -----------------------------
211
+ * 8. Компонент LogsSettings
212
+ * (аналог InputSettingLogs)
213
+ * -----------------------------
214
+ */
215
+ export function LogsSettings() {
216
+ const { minVarLogs, setMinVarLogs, minVarMessage, setMinVarMessage, timeShow, setTimeShow, showMessages, setShowMessages } = useLogsContext();
217
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 10, padding: 10 }, children: [_jsxs("label", { children: ["\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0432\u0430\u0436\u043D\u043E\u0441\u0442\u044C \u0434\u043B\u044F ", _jsx("b", { children: "\u0442\u0430\u0431\u043B\u0438\u0446\u044B" }), " (minVarLogs):", _jsx("input", { type: "number", value: minVarLogs, onChange: (e) => setMinVarLogs(Number(e.target.value)), style: { marginLeft: 8 } })] }), _jsxs("label", { children: ["\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0432\u0430\u0436\u043D\u043E\u0441\u0442\u044C \u0434\u043B\u044F ", _jsx("b", { children: "\u043E\u043F\u043E\u0432\u0435\u0449\u0435\u043D\u0438\u0439" }), " (minVarMessage):", _jsx("input", { type: "number", value: minVarMessage, onChange: (e) => setMinVarMessage(Number(e.target.value)), style: { marginLeft: 8 } })] }), _jsxs("label", { children: ["\u0412\u0440\u0435\u043C\u044F \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F (\u0441\u0435\u043A) (timeShow):", _jsx("input", { type: "number", value: timeShow, onChange: (e) => setTimeShow(Number(e.target.value)), style: { marginLeft: 8 } })] }), _jsxs("label", { children: ["\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0442\u044C \u0432\u0441\u043F\u043B\u044B\u0432\u0430\u0448\u043A\u0438 (showMessages):", _jsx("input", { type: "checkbox", checked: showMessages, onChange: (e) => setShowMessages(e.target.checked), style: { marginLeft: 8 } })] })] }));
218
+ }
219
+ /** -----------------------------
220
+ * 9. MainPage — вкладки (Таблица / Настройки)
221
+ * -----------------------------
222
+ */
223
+ export function MainPage() {
224
+ const [currentTab, setCurrentTab] = useState('table');
225
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: { display: 'flex', gap: 8, padding: 10, background: '#333' }, children: [_jsx("button", { onClick: () => setCurrentTab('table'), style: {
226
+ backgroundColor: currentTab === 'table' ? '#666' : '#444',
227
+ color: 'white',
228
+ border: 'none',
229
+ padding: '8px',
230
+ cursor: 'pointer'
231
+ }, children: "\u0422\u0430\u0431\u043B\u0438\u0446\u0430 \u043B\u043E\u0433\u043E\u0432" }), _jsx("button", { onClick: () => setCurrentTab('settings'), style: {
232
+ backgroundColor: currentTab === 'settings' ? '#666' : '#444',
233
+ color: 'white',
234
+ border: 'none',
235
+ padding: '8px',
236
+ cursor: 'pointer'
237
+ }, children: "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438" })] }), _jsxs("div", { style: { flex: 1, position: 'relative' }, children: [currentTab === 'table' && _jsx(LogsTable, {}), currentTab === 'settings' && _jsx(LogsSettings, {})] })] }));
238
+ }
239
+ /** -----------------------------
240
+ * 10. Корневой компонент App
241
+ * -----------------------------
242
+ */
243
+ export default function AppLogs() {
244
+ return (_jsx(LogsProvider, { children: _jsxs("div", { style: { position: 'relative', width: '100%', height: '100vh' }, children: [_jsx(MainPage, {}), _jsx(LogsNotifications, {})] }) }));
245
+ }
@@ -0,0 +1,5 @@
1
+ import { CellMouseDownEvent } from "ag-grid-community";
2
+ export declare function MiniLogs({ data, onClick }: {
3
+ data: any[];
4
+ onClick?: (e: CellMouseDownEvent<any, any>) => any;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { timeLocalToStr_hhmmss } from "wenay-common2";
3
+ import { AgGridReact } from "ag-grid-react";
4
+ import { useRef } from "react";
5
+ export function MiniLogs({ data, onClick }) {
6
+ const apiGrid = useRef(null);
7
+ const columns = [
8
+ {
9
+ field: "time",
10
+ sort: "desc",
11
+ width: 50,
12
+ valueFormatter: (e) => e.value.time ? timeLocalToStr_hhmmss(e.value.time) : e.value.time
13
+ },
14
+ {
15
+ field: "id",
16
+ width: 20,
17
+ },
18
+ {
19
+ field: "var",
20
+ width: 50,
21
+ },
22
+ {
23
+ field: "txt",
24
+ wrapText: true,
25
+ autoHeight: true,
26
+ width: 350
27
+ },
28
+ {
29
+ field: "address",
30
+ width: 150,
31
+ },
32
+ ];
33
+ return _jsx("div", { className: "maxSize", children: _jsx(AgGridReact
34
+ // className = "ag-theme-alpine-dark ag-theme-alpine2" // ag-theme-alpine-dark3
35
+ , {
36
+ // className = "ag-theme-alpine-dark ag-theme-alpine2" // ag-theme-alpine-dark3
37
+ suppressCellFocus: true, onGridReady: (a) => {
38
+ apiGrid.current = a; //as GridReadyEvent<tColum>
39
+ apiGrid.current.api.sizeColumnsToFit();
40
+ }, defaultColDef: {
41
+ headerClass: () => ("gridTable-header"),
42
+ resizable: true,
43
+ cellStyle: { textAlign: "center" },
44
+ sortable: true,
45
+ filter: true,
46
+ wrapText: true,
47
+ }, headerHeight: 30, rowHeight: 26, autoSizePadding: 1, rowData: data, columnDefs: columns, onCellMouseDown: (e) => {
48
+ // @ts-ignore
49
+ onClick?.(e);
50
+ } }) });
51
+ }
@@ -0,0 +1,72 @@
1
+ import React, { ReactElement } from 'react';
2
+ /*******************************************************
3
+ * Типы данных для меню
4
+ *******************************************************/
5
+ export type tMenuReactStrictly<T = any> = {
6
+ name: string | ((status?: T) => string);
7
+ getStatus?: (() => T) | null;
8
+ onClick?: ((e: any) => void | undefined | null | ((void | undefined | null | Promise<any> | (() => Promise<any>))[]) | Promise<any>) | null;
9
+ active?: (() => boolean) | null;
10
+ status?: boolean;
11
+ next?: (() => (tMenuReact<any> | false)[] | Promise<(tMenuReact<any> | false)[]>) | null;
12
+ func?: (() => React.ReactElement | Promise<React.ReactElement>) | null;
13
+ onFocus?: (() => tMenuReact<any>[] | Promise<tMenuReact<any>[]>) | null;
14
+ menuElement?: typeof MenuElement;
15
+ };
16
+ export type tMenuReact<T = any> = tMenuReactStrictly<T> | false | null | undefined;
17
+ /*******************************************************
18
+ * Вспомогательный тип
19
+ *******************************************************/
20
+ type tCounters = {
21
+ ok?: number;
22
+ error?: number;
23
+ count?: number;
24
+ };
25
+ /*******************************************************
26
+ * Отображает счётчик/прогресс (анимация, кол-во ok/error)
27
+ *******************************************************/
28
+ declare function TimeNum({ data }: {
29
+ data: tCounters;
30
+ }): ReactElement;
31
+ /*******************************************************
32
+ * Основной элемент меню (пункт с onClick, счётчиками и т.д.)
33
+ *******************************************************/
34
+ declare function MenuElement({ data: item, toLeft, className, update, }: {
35
+ data: Pick<tMenuReactStrictly, "onClick" | "active" | "name" | "getStatus">;
36
+ toLeft: boolean;
37
+ className?: (active?: boolean) => string;
38
+ update: () => void;
39
+ }): ReactElement;
40
+ /*******************************************************
41
+ * Компонент MenuBase отвечает за отрисовку всплывающего меню с поддержкой
42
+ * вложенных подменю и управления их состоянием.
43
+ *
44
+ * @param {Object} props - Пропсы компонента.
45
+ * @param {Object} [props.coordinate] - Координаты и параметры отображения меню.
46
+ * @param {number} props.coordinate.x - Координата X для размещения меню.
47
+ * @param {number} props.coordinate.y - Координата Y для размещения меню.
48
+ * @param {boolean} [props.coordinate.toLeft=false] - Указывает, должно ли меню быть смещено влево.
49
+ * @param {number} [props.coordinate.left=0] - Дополнительное смещение влево, если меню отображается с вложенными элементами.
50
+ * @param {tMenuReactStrictly[]} props.data - Массив объектов, описывающих элементы меню.
51
+ * @param {number} [props.zIndex] - Z-index меню для управления видимостью при перекрытии.
52
+ * @param {Function} [props.menu] - Функция, которая генерирует кастомный React элемент для отображения всего меню.
53
+ * @param {Function} [props.menuElement] - Функция для генерации кастомного React элемента для отображения отдельного элемента меню.
54
+ * @param {Function} [props.className] - Функция для задания CSS-классов для элементов меню.
55
+ *
56
+ * @returns {ReactElement} Визуальный элемент меню.
57
+ */
58
+ type MenuBaseProps = {
59
+ menu?: (arr: tMenuReact[]) => ReactElement;
60
+ menuElement?: (item: tMenuReact) => ReactElement;
61
+ data: tMenuReact[];
62
+ zIndex?: number;
63
+ className?: (active?: boolean) => string;
64
+ coordinate?: {
65
+ x: number;
66
+ y: number;
67
+ toLeft?: boolean;
68
+ left?: number;
69
+ };
70
+ };
71
+ export declare function MenuBase({ coordinate, data, zIndex, menu, className, menuElement, }: MenuBaseProps): ReactElement;
72
+ export { TimeNum, MenuElement };
@@ -0,0 +1,230 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
+ import { PromiseArrayListen, sleepAsync } from "wenay-common2";
4
+ /*******************************************************
5
+ * Отображает счётчик/прогресс (анимация, кол-во ok/error)
6
+ *******************************************************/
7
+ function TimeNum({ data }) {
8
+ const refCounter = useRef(0);
9
+ const [count, setCount] = useState(refCounter.current);
10
+ refCounter.current = count;
11
+ const formatLabel = () => {
12
+ if (!data.ok && !data.error)
13
+ return count;
14
+ const txtOk = data.ok ? "ok " + data.ok : "";
15
+ const txtEr = data.error ? " er " + (data.error > 1 ? data.error : "") : "";
16
+ const txtCount = data.count ? "/" + data.count : "";
17
+ return txtOk + txtCount + txtEr;
18
+ };
19
+ useEffect(() => {
20
+ let local = 0;
21
+ const timer = setInterval(() => setCount(++local), 30);
22
+ return () => clearInterval(timer);
23
+ }, []);
24
+ return (_jsx("div", { style: {
25
+ float: "right",
26
+ opacity: count < 45 ? count / 45 : 1,
27
+ width: count < 25 ? count * 3 : 75,
28
+ textAlign: "right",
29
+ }, children: formatLabel() }));
30
+ }
31
+ /*******************************************************
32
+ * Основной элемент меню (пункт с onClick, счётчиками и т.д.)
33
+ *******************************************************/
34
+ function MenuElement({ data: item, toLeft, className, update, }) {
35
+ const unsubOk = useRef(null);
36
+ const unsubErr = useRef(null);
37
+ useEffect(() => {
38
+ // При размонтировании отписываемся
39
+ return () => {
40
+ unsubOk.current?.();
41
+ unsubErr.current?.();
42
+ unsubOk.current = null;
43
+ unsubErr.current = null;
44
+ };
45
+ }, []);
46
+ const [progress, setProgress] = useState(null);
47
+ return (_jsx("div", { className: className?.(item.active?.()) ||
48
+ "MenuR " + (item.active?.() ? "toButtonA" : "toButton"), style: { float: toLeft ? "left" : "right" }, onClick: () => {
49
+ if (!item.onClick)
50
+ return;
51
+ const result = item?.onClick?.(item);
52
+ if (!result) {
53
+ update();
54
+ return;
55
+ }
56
+ // Если это массив "задач" (промисов или функций)
57
+ if (Array.isArray(result)) {
58
+ const tasks = result.filter(Boolean);
59
+ const pa = PromiseArrayListen(tasks); // Допустим, внешняя функция
60
+ setProgress({});
61
+ unsubOk.current?.();
62
+ unsubErr.current?.();
63
+ unsubOk.current = pa.listenOk((data, i, countOk, countError, count) => setProgress({ ok: countOk, error: countError, count }));
64
+ unsubErr.current = pa.listenError((error, i, countOk, countError, count) => setProgress({ ok: countOk, error: countError, count }));
65
+ }
66
+ // Если это один промис
67
+ else if (result instanceof Promise) {
68
+ setProgress({});
69
+ result
70
+ .then(async (val) => {
71
+ if (Array.isArray(val) && val.length) {
72
+ // Если вернулся массив из Promise.allSettled
73
+ // Считаем кол-во ok/error
74
+ if (val[0]?.status === "fulfilled" || val[0]?.status === "rejected") {
75
+ const t = { ok: 0, error: 0 };
76
+ val.forEach((res) => {
77
+ if (res?.status === "fulfilled")
78
+ t.ok++;
79
+ if (res?.status === "rejected")
80
+ t.error++;
81
+ });
82
+ setProgress(t);
83
+ }
84
+ }
85
+ else {
86
+ setProgress({ ok: 1 });
87
+ await sleepAsync(0);
88
+ }
89
+ })
90
+ .finally(async () => {
91
+ await sleepAsync(500);
92
+ setProgress(null);
93
+ });
94
+ }
95
+ else {
96
+ update();
97
+ }
98
+ }, children: _jsxs("div", { className: "toLine", children: [typeof item.name === "string"
99
+ ? item.name
100
+ : item.name(item.getStatus?.()), progress && _jsx(TimeNum, { data: progress })] }) }));
101
+ }
102
+ const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPos, menuElement, fullArray, }) => {
103
+ const [childMenu, setChildMenu] = useState([]);
104
+ const [asyncFuncElement, setAsyncFuncElement] = useState(null);
105
+ const [onFocusMenu, setOnFocusMenu] = useState([]);
106
+ useEffect(() => {
107
+ if (item.status && item.next) {
108
+ const result = item.next();
109
+ if (result instanceof Promise) {
110
+ result.then((val) => {
111
+ setChildMenu(val.filter(Boolean));
112
+ });
113
+ }
114
+ else {
115
+ setChildMenu(result.filter(Boolean));
116
+ }
117
+ }
118
+ else {
119
+ setChildMenu([]);
120
+ }
121
+ }, [item.status, item.next]);
122
+ useEffect(() => {
123
+ if (item.status && item.func) {
124
+ const result = item.func();
125
+ if (result instanceof Promise) {
126
+ result.then((val) => {
127
+ setAsyncFuncElement(val);
128
+ });
129
+ }
130
+ else {
131
+ setAsyncFuncElement(result);
132
+ }
133
+ }
134
+ else {
135
+ setAsyncFuncElement(null);
136
+ }
137
+ }, [item.status, item.func]);
138
+ useEffect(() => {
139
+ if (item.status && item.onFocus) {
140
+ const result = item.onFocus();
141
+ if (result instanceof Promise) {
142
+ result.then((val) => {
143
+ setOnFocusMenu(val.filter(Boolean));
144
+ });
145
+ }
146
+ else {
147
+ setOnFocusMenu(result.filter(Boolean));
148
+ }
149
+ }
150
+ else {
151
+ setOnFocusMenu([]);
152
+ }
153
+ }, [item.status, item.onFocus]);
154
+ const onMouseEnter = () => {
155
+ if (item.status)
156
+ return;
157
+ fullArray.forEach((it, j) => {
158
+ it.status = j === index;
159
+ });
160
+ update();
161
+ };
162
+ return (_jsxs("div", { className: "toLine", onMouseEnter: onMouseEnter, children: [menuElement
163
+ ? menuElement(item)
164
+ : item.menuElement?.({
165
+ toLeft: isLeftAligned,
166
+ data: item,
167
+ className,
168
+ update,
169
+ }) ?? (_jsx(MenuElement, { toLeft: isLeftAligned, data: item, className: className, update: update })), _jsxs("div", { children: [item.status && childMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(MenuBase, { data: childMenu, coordinate: {
170
+ x: 3,
171
+ y: 0,
172
+ toLeft: isLeftAligned,
173
+ left: leftPos,
174
+ } }) })), item.status && asyncFuncElement && (_jsx("div", { style: { position: "relative" }, children: _jsx(MenuBase, { menu: () => asyncFuncElement, data: [], coordinate: {
175
+ x: 3,
176
+ y: 0,
177
+ toLeft: isLeftAligned,
178
+ left: leftPos,
179
+ } }) })), item.status && onFocusMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(MenuBase, { data: onFocusMenu, coordinate: {
180
+ x: 3,
181
+ y: 0,
182
+ toLeft: isLeftAligned,
183
+ left: leftPos,
184
+ } }) }))] })] }));
185
+ };
186
+ export function MenuBase({ coordinate = { x: 0, y: 0, toLeft: false, left: 0 }, data, zIndex, menu, className, menuElement, }) {
187
+ const [_, forceUpdate] = useState(false);
188
+ const update = () => forceUpdate((p) => !p);
189
+ const refMenu = useRef(null);
190
+ const dataMemo = useMemo(() => data.filter((e) => e), [data, data.length]);
191
+ const [top, setTop] = useState(coordinate.y);
192
+ const [leftPos, setLeftPos] = useState(coordinate.x);
193
+ const [menuWidth, setMenuWidth] = useState(0);
194
+ const [isLeftAligned, setIsLeftAligned] = useState(!!coordinate.toLeft);
195
+ const [xOffset, setXOffset] = useState(0);
196
+ useLayoutEffect(() => {
197
+ if (!refMenu.current)
198
+ return;
199
+ const rect = refMenu.current.getBoundingClientRect();
200
+ const w = window.innerWidth, h = window.innerHeight;
201
+ if (h - rect.bottom < 8)
202
+ setTop((prev) => prev + (h - rect.bottom));
203
+ setLeftPos(rect.x);
204
+ setMenuWidth(rect.width);
205
+ if (!coordinate.toLeft && w - rect.right < 8 && rect.width < (coordinate.left ?? 0)) {
206
+ setXOffset(rect.x - (coordinate.left ?? 0));
207
+ setIsLeftAligned(true);
208
+ }
209
+ if (coordinate.toLeft) {
210
+ setXOffset((coordinate.left ?? 0) - rect.x - 4);
211
+ }
212
+ }, [coordinate.x, coordinate.y, coordinate.toLeft, coordinate.left]);
213
+ const alignStyle = isLeftAligned
214
+ ? { display: "flex", flexDirection: "column-reverse", alignItems: "flex-end" }
215
+ : {};
216
+ return (_jsx("div", { ref: (el) => {
217
+ if (el)
218
+ refMenu.current = el;
219
+ }, style: {
220
+ position: "absolute",
221
+ zIndex,
222
+ paddingLeft: 3,
223
+ left: (isLeftAligned ? -1 * (menuWidth + 3 + xOffset) : coordinate.x) - 3,
224
+ top,
225
+ ...alignStyle,
226
+ }, children: menu
227
+ ? menu(dataMemo)
228
+ : dataMemo.map((item, i, arr) => (_jsx(MenuItemWrapper, { item: item, index: i, update: update, className: className, isLeftAligned: isLeftAligned, leftPos: leftPos, menuElement: menuElement, fullArray: dataMemo }, i))) }));
229
+ }
230
+ export { TimeNum, MenuElement };
@@ -0,0 +1,21 @@
1
+ import { MenuBase, tMenuReact } from "./menu";
2
+ export declare const mouseMenuApi: {
3
+ bb: (b?: boolean) => boolean | undefined;
4
+ readonly map: Map<string, tMenuReact[]>;
5
+ readonly menuMouse: {
6
+ name: string;
7
+ readonly value: {
8
+ status: boolean;
9
+ clicks: number;
10
+ };
11
+ };
12
+ ReactMouse: (agr: Parameters<({ children, other, statusOn, onUnClick, zIndex, className }: {
13
+ children: React.ReactElement;
14
+ zIndex?: number;
15
+ other?: () => (tMenuReact)[];
16
+ statusOn?: boolean;
17
+ onUnClick?: (e: boolean) => void;
18
+ className?: (active?: boolean) => string;
19
+ }) => import("react/jsx-runtime").JSX.Element>[0]) => import("react/jsx-runtime").JSX.Element;
20
+ ReactMenu: typeof MenuBase;
21
+ };
@@ -0,0 +1,32 @@
1
+ import { GetMenuR } from "./menuR";
2
+ import { MenuBase } from "./menu";
3
+ function GetMouseMenuApi(data) {
4
+ const { name = "mouse" } = data ?? {};
5
+ const { MenuR, bb } = GetMenuR();
6
+ const value = {
7
+ status: true,
8
+ clicks: 0
9
+ };
10
+ const menuMouse = {
11
+ name,
12
+ get value() { return value; }
13
+ };
14
+ const map = new Map;
15
+ function other() {
16
+ if (map.has("only"))
17
+ return map.get("only");
18
+ const t = [];
19
+ map.forEach(e => { t.unshift(...e); });
20
+ return t;
21
+ }
22
+ function ReactMouse(agr) {
23
+ const datum = menuMouse.value; //staticGetAdd(menuMouse.name, menuMouse.value
24
+ return MenuR({ ...(agr ?? {}), other: agr.other ? agr.other : other, statusOn: datum.status, onUnClick: () => {
25
+ map.clear();
26
+ } });
27
+ }
28
+ return {
29
+ bb, get map() { return map; }, get menuMouse() { return menuMouse; }, ReactMouse, ReactMenu: MenuBase
30
+ };
31
+ }
32
+ export const mouseMenuApi = GetMouseMenuApi();