wenay-react2 1.0.1 → 1.0.3
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.
- package/README.md +383 -0
- package/package.json +1 -2
package/README.md
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Вот расширенное руководство по API (с типизацией и примерами использования). Этот формат отлично подойдет для документации, чтобы другие разработчики (или AI-ассистенты) могли сразу понимать, какие пропсы принимает компонент и как его внедрять.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## 🛠 Справочник API (Components & Hooks)
|
|
6
|
+
|
|
7
|
+
### 🪟 Модальные и плавающие окна (MDI)
|
|
8
|
+
|
|
9
|
+
#### `DivRnd3`
|
|
10
|
+
Обертка для создания окон со свободным перемещением, изменением размеров (resize) и сохранением состояния.
|
|
11
|
+
|
|
12
|
+
**Типизация:**
|
|
13
|
+
```typescript
|
|
14
|
+
type tDivRndBase = {
|
|
15
|
+
children: React.ReactElement | ((update: number) => React.ReactElement);
|
|
16
|
+
position?: { x: number; y: number };
|
|
17
|
+
size?: { height: number | string; width: number | string };
|
|
18
|
+
keyForSave?: string; // Ключ для кэширования позиции/размера в памяти
|
|
19
|
+
limit?: { x?: { max?: number; min?: number }, y?: { max?: number; min?: number } };
|
|
20
|
+
onCLickClose?: () => void; // Добавляет крестик закрытия окна
|
|
21
|
+
header?: React.ReactElement | boolean; // Шапка для перетаскивания (иначе тянется за весь блок)
|
|
22
|
+
zIndex?: number;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Пример:**
|
|
27
|
+
```textmate
|
|
28
|
+
<DivRnd3
|
|
29
|
+
keyForSave="my_tool_window"
|
|
30
|
+
size={{ width: 300, height: 200 }}
|
|
31
|
+
onCLickClose={() => setOpen(false)}
|
|
32
|
+
header={<div>Мой инструмент</div>}
|
|
33
|
+
>
|
|
34
|
+
<div>Контент окна</div>
|
|
35
|
+
</DivRnd3>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
#### `GetModalJSX()`
|
|
40
|
+
Фабрика глобальных модальных окон. Позволяет вызывать окна из бизнес-логики без привязки к локальному стейту компонента.
|
|
41
|
+
|
|
42
|
+
**Пример:**
|
|
43
|
+
```textmate
|
|
44
|
+
const myModal = GetModalJSX();
|
|
45
|
+
|
|
46
|
+
// 1. В корневом файле приложения (App.tsx):
|
|
47
|
+
<myModal.Render />
|
|
48
|
+
|
|
49
|
+
// 2. В любом месте логики:
|
|
50
|
+
myModal.set(<div onClick={() => myModal.set(null)}>Закрыть меня</div>);
|
|
51
|
+
// Для множественных окон: myModal.addJSX( <Component/> )
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### 🖱 Drag-and-Drop и Взаимодействие (Hooks & Wrappers)
|
|
58
|
+
|
|
59
|
+
#### `useOutside` / `DivOutsideClick`
|
|
60
|
+
Отслеживание клика вне элемента (полезно для закрытия дропдаунов и меню).
|
|
61
|
+
|
|
62
|
+
**Типизация:**
|
|
63
|
+
```typescript
|
|
64
|
+
// Hook
|
|
65
|
+
function useOutside(params: { outsideClick: () => void, status?: boolean, ref?: RefObject }): RefObject;
|
|
66
|
+
|
|
67
|
+
// Component
|
|
68
|
+
type Props = { outsideClick: () => void, status?: boolean, zIndex?: number } & HTMLAttributes;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Пример:**
|
|
72
|
+
```textmate
|
|
73
|
+
<DivOutsideClick outsideClick={() => setDropdownOpen(false)} status={isOpen}>
|
|
74
|
+
<div className="dropdown-menu">...</div>
|
|
75
|
+
</DivOutsideClick>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
#### `Drag22`
|
|
80
|
+
Отслеживает сдвиг мыши/пальца и возвращает новые координаты, не вмешиваясь в CSS напрямую.
|
|
81
|
+
|
|
82
|
+
**Типизация:**
|
|
83
|
+
```typescript
|
|
84
|
+
type Drag2Props = {
|
|
85
|
+
x?: number; y?: number; // Стартовые/текущие координаты
|
|
86
|
+
onX?: (val: number) => void; // Коллбек изменения X
|
|
87
|
+
onY?: (val: number) => void; // Коллбек изменения Y
|
|
88
|
+
onStart?: () => void; onStop?: () => void;
|
|
89
|
+
children: ReactNode;
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### 📋 Контекстные меню (`MenuR` / `mouseMenuApi`)
|
|
97
|
+
|
|
98
|
+
Умные контекстные меню. Вызываются на ПК (ПКМ) и мобилках (долгий тап / дабл-тап). Поддерживают асинхронную генерацию элементов.
|
|
99
|
+
|
|
100
|
+
**Типизация элемента меню (`tMenuReactStrictly`):**
|
|
101
|
+
```typescript
|
|
102
|
+
type tMenuReactStrictly = {
|
|
103
|
+
name: string | ((status?: any) => string);
|
|
104
|
+
onClick?: (e: any) => void | Promise<any> | Promise<any>[]; // Поддержка промисов со счетчиком ok/error
|
|
105
|
+
status?: boolean; // Раскрыто ли подменю
|
|
106
|
+
next?: () => tMenuReact[] | Promise<tMenuReact[]>; // Вложенное меню
|
|
107
|
+
func?: () => React.ReactElement | Promise<React.ReactElement>; // Кастомный рендер при наведении
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Примеры:**
|
|
112
|
+
```textmate
|
|
113
|
+
// 1. Использование компонента-обертки
|
|
114
|
+
const menuItems = () => [
|
|
115
|
+
{ name: "Копировать", onClick: () => copy() },
|
|
116
|
+
{ name: "Опции", next: () => [{ name: "Опция 1", onClick: () => {} }] }
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
<MenuR other={menuItems}>
|
|
120
|
+
<div className="item">Кликни меня правой кнопкой</div>
|
|
121
|
+
</MenuR>
|
|
122
|
+
|
|
123
|
+
// 2. Глобальный вызов через API
|
|
124
|
+
mouseMenuApi.map.set("global_action", [{ name: "Глобальное действие", onClick: () => {} }]);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### 📊 Обновление таблиц Ag-Grid (`applyTransactionAsyncUpdate2`)
|
|
131
|
+
|
|
132
|
+
Функция для безопасного, пакетного и асинхронного обновления данных таблицы, синхронизированного с локальным буфером (кэшем).
|
|
133
|
+
|
|
134
|
+
**Типизация:**
|
|
135
|
+
```typescript
|
|
136
|
+
type Params<T> = {
|
|
137
|
+
gridRef?: React.RefObject<GridReadyEvent<T, any>>;
|
|
138
|
+
newData: Partial<T>[]; // Массив новых данных
|
|
139
|
+
getId: (row: Partial<T>) => string; // Функция получения ID
|
|
140
|
+
bufTable: { [id: string]: Partial<T> }; // Локальный кэш-словарь
|
|
141
|
+
option?: { update?: boolean, add?: boolean, updateBuffer?: boolean, sync?: boolean };
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Пример:**
|
|
146
|
+
```textmate
|
|
147
|
+
// Мгновенно обновляет кэш и отправляет транзакцию в Ag-Grid
|
|
148
|
+
applyTransactionAsyncUpdate2({
|
|
149
|
+
gridRef: apiGrid,
|
|
150
|
+
newData: [{ id: "user_1", balance: 500 }],
|
|
151
|
+
getId: (e) => e.id,
|
|
152
|
+
bufTable: myLocalBuffer,
|
|
153
|
+
option: { update: true, add: true }
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### ⚙️ Автогенерация UI настроек (`ParametersReact`)
|
|
161
|
+
|
|
162
|
+
Движок, который принимает объект со схемой данных и рендерит готовую форму управления (инпуты, слайдеры, селекты).
|
|
163
|
+
|
|
164
|
+
**Типизация:**
|
|
165
|
+
Схема строится на основе типов `Params.IParamsExpandableReadonly`.
|
|
166
|
+
Поддерживаются типы: `number`, `string`, `boolean`, `Date`, массивы, вложенные объекты.
|
|
167
|
+
|
|
168
|
+
**Пример:**
|
|
169
|
+
```textmate
|
|
170
|
+
const mySettings = {
|
|
171
|
+
showGrid: true, // Сгенерирует Checkbox
|
|
172
|
+
opacity: {
|
|
173
|
+
value: 0.5,
|
|
174
|
+
range: { min: 0, max: 1, step: 0.1 },
|
|
175
|
+
name: "Прозрачность"
|
|
176
|
+
}, // Сгенерирует Range Slider + Number Input
|
|
177
|
+
theme: {
|
|
178
|
+
value: "dark",
|
|
179
|
+
range: ["dark", "light", "system"]
|
|
180
|
+
} // Сгенерирует Select
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
<ParametersReact
|
|
184
|
+
params={mySettings}
|
|
185
|
+
onChange={(newParams) => {
|
|
186
|
+
console.log("Новое значение:", newParams.opacity.value);
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### 📜 Система логирования (`logsApi`)
|
|
195
|
+
|
|
196
|
+
Регистрация логов, таблица их просмотра и система всплывающих уведомлений (toasts).
|
|
197
|
+
|
|
198
|
+
**API:**
|
|
199
|
+
```typescript
|
|
200
|
+
// 1. Добавление лога
|
|
201
|
+
logsApi.addLogs({
|
|
202
|
+
id: "system",
|
|
203
|
+
var: 10, // Важность (важно для фильтрации уведомлений)
|
|
204
|
+
time: new Date(),
|
|
205
|
+
txt: "Соединение разорвано"
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// 2. Рендер компонентов (обычно где-то в корне):
|
|
209
|
+
<logsApi.React.Message zIndex={9999} /> // Всплывающие уведомления справа сверху
|
|
210
|
+
<logsApi.React.PageLogs /> // Таблица всех логов Ag-Grid
|
|
211
|
+
```
|
|
212
|
+
Да, в кодовой базе есть еще несколько очень важных архитектурных паттернов и UI-компонентов, которые активно используются и обязательно должны быть в документации, чтобы другой разработчик (или ИИ) понимал, как строить интерфейсы в этом проекте.
|
|
213
|
+
|
|
214
|
+
Вот вторая часть дополнений для `README.md`:
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### 🗂 Боковая навигация (Sidebar)
|
|
219
|
+
|
|
220
|
+
#### `ApiLeftMenu`
|
|
221
|
+
Готовое API для управления левым выдвижным меню (Sidebar). Поддерживает свайпы, плавную доводку (snap scrolling) и императивное управление вкладками.
|
|
222
|
+
|
|
223
|
+
**API и Типизация:**
|
|
224
|
+
```typescript
|
|
225
|
+
type MenuItem = {
|
|
226
|
+
el: () => React.JSX.Element; // Компонент вкладки
|
|
227
|
+
button?: React.JSX.Element; // Кастомная кнопка (опционально)
|
|
228
|
+
color?: ColorString; // Цвет фона
|
|
229
|
+
textB?: string; // Текст по умолчанию для кнопки
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Регистрация пунктов меню:
|
|
233
|
+
ApiLeftMenu.setMenu(items: MenuItem[], key?: string);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Пример использования:**
|
|
237
|
+
```textmate
|
|
238
|
+
// 1. Регистрация вкладок (можно делать где угодно в логике):
|
|
239
|
+
ApiLeftMenu.setMenu([
|
|
240
|
+
{ textB: "Дашборд", el: () => <Dashboard />, color: "rgb(92,50,213)" },
|
|
241
|
+
{ textB: "Настройки", el: () => <Settings /> }
|
|
242
|
+
], "main_menu");
|
|
243
|
+
|
|
244
|
+
// 2. Рендер самого меню в корневом Layout:
|
|
245
|
+
export function AppLayout() {
|
|
246
|
+
return <ApiLeftMenu.Modal2 zIndex={20} />;
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### 🎛 Интерактивные элементы и Кнопки
|
|
254
|
+
|
|
255
|
+
#### `Button` (из `useOutside.tsx`) / `MiniButton`
|
|
256
|
+
Продвинутые компоненты кнопок, которые инкапсулируют логику раскрывающихся списков (dropdowns), всплывающих панелей и отслеживают клик снаружи для автоматического закрытия.
|
|
257
|
+
|
|
258
|
+
**Типизация `Button`:**
|
|
259
|
+
```typescript
|
|
260
|
+
type tButton = {
|
|
261
|
+
button: ReactElement | ((status: boolean) => ReactElement); // Сама кнопка (может менять вид от статуса)
|
|
262
|
+
children: ReactNode | ((api: {onClose: () => void}) => ReactNode); // Выпадающий контент
|
|
263
|
+
outClick?: boolean | (() => void); // Закрывать ли по клику вне области (true по умолчанию)
|
|
264
|
+
statusDef?: boolean; // Начальное состояние (открыто/закрыто)
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Пример:**
|
|
269
|
+
```textmate
|
|
270
|
+
<Button
|
|
271
|
+
outClick={true}
|
|
272
|
+
button={(isOpen) => <div className={isOpen ? "active" : ""}>Опции</div>}
|
|
273
|
+
>
|
|
274
|
+
{({ onClose }) => (
|
|
275
|
+
<div className="dropdown-panel">
|
|
276
|
+
<div onClick={() => { doSomething(); onClose(); }}>Действие 1</div>
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
</Button>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
#### `FResizableReact`
|
|
284
|
+
Обертка над `re-resizable`. Позволяет делать панели с изменяемым размером (например, колонки или нижние логи), сохраняя их размер в кэш.
|
|
285
|
+
|
|
286
|
+
**Пример:**
|
|
287
|
+
```textmate
|
|
288
|
+
<FResizableReact
|
|
289
|
+
keyForSave="bottom_panel_size"
|
|
290
|
+
size={{ height: 200, width: "100%" }}
|
|
291
|
+
moveWith={false} // Разрешить ресайз только по высоте
|
|
292
|
+
>
|
|
293
|
+
<LogsTable />
|
|
294
|
+
</FResizableReact>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### 🔄 Глобальная реактивность (Паттерн `renderBy` / `updateBy`)
|
|
301
|
+
|
|
302
|
+
*(Примечание: это важнейший концепт проекта, используемый вместо классического `useState` / `Redux` для сложной бизнес-логики).*
|
|
303
|
+
|
|
304
|
+
Во многих местах проекта используется мутабельный подход к состоянию: вы меняете свойства обычного JS-объекта и вызываете `renderBy(obj)`, чтобы принудительно обновить все компоненты, которые подписаны на этот объект через `updateBy(obj)`.
|
|
305
|
+
|
|
306
|
+
**Пример паттерна:**
|
|
307
|
+
```textmate
|
|
308
|
+
import { renderBy, updateBy } from "./updateBy";
|
|
309
|
+
|
|
310
|
+
// 1. Глобальное или локальное состояние (обычный объект)
|
|
311
|
+
const myState = {
|
|
312
|
+
count: 0,
|
|
313
|
+
text: "hello"
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// 2. Компонент-потребитель
|
|
317
|
+
function CounterViewer() {
|
|
318
|
+
// Компонент подписывается на изменения myState
|
|
319
|
+
updateBy(myState);
|
|
320
|
+
|
|
321
|
+
return <div>{myState.count}</div>;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 3. Изменение состояния (где угодно, даже вне React)
|
|
325
|
+
function increment() {
|
|
326
|
+
myState.count += 1;
|
|
327
|
+
renderBy(myState); // Триггерит ререндер CounterViewer
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
### 📊 Полезные утилиты для Ag-Grid
|
|
335
|
+
|
|
336
|
+
#### `GridStyleDefault` и `StyleCSSHeadGrid`
|
|
337
|
+
Быстрое применение единого корпоративного стиля ко всем таблицам приложения.
|
|
338
|
+
|
|
339
|
+
**Пример внедрения в корень проекта:**
|
|
340
|
+
```typescript
|
|
341
|
+
import { GridStyleDefault, StyleCSSHeadGrid } from "./styleGrid";
|
|
342
|
+
|
|
343
|
+
// Применяет темную тему, сжимает отступы, центрирует заголовки
|
|
344
|
+
GridStyleDefault();
|
|
345
|
+
StyleCSSHeadGrid();
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
#### `getComparatorGrid`
|
|
350
|
+
Фабрика для создания кастомных функций сортировки колонок Ag-Grid, корректно обрабатывающая `undefined`, `NaN` и инверсию.
|
|
351
|
+
**Пример:**
|
|
352
|
+
```textmate
|
|
353
|
+
const columnDefs = [
|
|
354
|
+
{
|
|
355
|
+
field: "price",
|
|
356
|
+
comparator: getComparatorGrid() // Безопасная сортировка чисел
|
|
357
|
+
}
|
|
358
|
+
];
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
### ⌨️ Глобальные хуки
|
|
365
|
+
|
|
366
|
+
#### `useAddDownAnyKey`
|
|
367
|
+
Регистрирует глобальный слушатель нажатий клавиатуры и сохраняет последнюю нажатую клавишу в экспортируемый реактивный объект `KeyDown`.
|
|
368
|
+
|
|
369
|
+
**Пример использования:**
|
|
370
|
+
```textmate
|
|
371
|
+
import { KeyDown, useAddDownAnyKey } from "./useAddDownAnyKey";
|
|
372
|
+
import { updateBy } from "./updateBy";
|
|
373
|
+
|
|
374
|
+
function HotkeyListener() {
|
|
375
|
+
useAddDownAnyKey(); // Инициализация хука
|
|
376
|
+
updateBy(KeyDown); // Подписка на нажатия
|
|
377
|
+
|
|
378
|
+
if (KeyDown.key === "Escape") {
|
|
379
|
+
return <div>Нажат Escape!</div>;
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
```
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wenay-react2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Common react",
|
|
5
5
|
"strict": true,
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"lib/**/*",
|
|
10
|
-
"README.md",
|
|
11
10
|
"!**/*.tsbuildinfo"
|
|
12
11
|
],
|
|
13
12
|
"author": "wenay",
|