wenay-react2 1.0.39 → 1.0.41
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/doc/EXAMPLE_USAGE.md +915 -0
- package/doc/PROJECT_FUNCTIONALITY.md +401 -0
- package/doc/PROJECT_RULES.md +80 -0
- package/doc/WENAY_REACT2_RENAMES.md +177 -0
- package/doc/changes/v1.0.35.md +26 -0
- package/doc/changes/v1.0.37.md +13 -0
- package/doc/changes/v1.0.38.md +48 -0
- package/doc/changes/v1.0.39.md +35 -0
- package/doc/changes/v1.0.40.md +15 -0
- package/doc/changes/v1.0.41.md +13 -0
- package/doc/progress/README.md +14 -0
- package/doc/progress/hook-controller-opportunities.md +367 -0
- package/doc/progress/hook-extraction-audit.md +195 -0
- package/doc/progress/public-surface-normalization.md +368 -0
- package/doc/progress/style-system-normalization.md +121 -0
- package/doc/target/README.md +33 -0
- package/doc/target/my.md +109 -0
- package/doc/wenay-react2-1.0.8.tgz +0 -0
- package/doc/wenay-react2-rare.md +702 -0
- package/doc/wenay-react2.md +475 -0
- package/lib/common/src/components/Dnd/FloatingWindow.d.ts +29 -12
- package/lib/common/src/components/Dnd/FloatingWindow.js +100 -91
- package/lib/common/src/components/Menu/RightMenu.d.ts +28 -1
- package/lib/common/src/components/Menu/RightMenu.js +62 -35
- package/lib/common/src/components/Modal/LeftModal.js +5 -4
- package/lib/common/src/components/Modal/Modal.js +8 -7
- package/lib/common/src/components/ParamsEditor.d.ts +15 -0
- package/lib/common/src/components/ParamsEditor.js +45 -18
- package/lib/common/src/components/Settings/SettingsDialog.d.ts +68 -6
- package/lib/common/src/components/Settings/SettingsDialog.js +61 -12
- package/lib/common/src/components/Toolbar/Toolbar.js +64 -19
- package/lib/common/src/components/UiSlot/UiSlot.js +5 -4
- package/lib/common/src/grid/columnState/columnState.js +8 -6
- package/lib/common/src/hooks/useKeyboard.js +4 -3
- package/lib/common/src/logs/logs.d.ts +40 -133
- package/lib/common/src/logs/logs.js +96 -66
- package/lib/common/src/logs/logsContext.d.ts +26 -0
- package/lib/common/src/logs/logsContext.js +25 -20
- package/lib/common/src/logs/logsController.d.ts +88 -0
- package/lib/common/src/logs/logsController.js +54 -0
- package/lib/common/src/logs/miniLogs.d.ts +69 -3
- package/lib/common/src/logs/miniLogs.js +70 -15
- package/lib/common/src/menu/menu.d.ts +15 -3
- package/lib/common/src/menu/menu.js +81 -17
- package/lib/common/src/menu/menuMouse.d.ts +18 -0
- package/lib/common/src/menu/menuMouse.js +43 -1
- package/lib/common/src/styles/tokens.d.ts +7 -6
- package/lib/common/src/styles/tokens.js +8 -5
- package/lib/common/src/utils/memoryStore.d.ts +1 -10
- package/lib/common/src/utils/searchHistory.js +4 -3
- package/lib/style/menuRight.css +29 -23
- package/lib/style/style.css +15 -9
- package/lib/style/tokens.css +8 -4
- package/package.json +50 -49
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Hook extraction audit (API-возвращающие хуки)
|
|
2
|
+
|
|
3
|
+
Дата: 2026-07-09.
|
|
4
|
+
|
|
5
|
+
Задача (надиктовка): пройти по всему коду фронт-библиотеки и осмысленно найти места,
|
|
6
|
+
которые стоит вынести в `use*`/`create*` хуки, где хук **возвращает API** — методы,
|
|
7
|
+
которые отдают X, Y, размеры, «сколько», snapshot и т.п. Не «работа ради работы»:
|
|
8
|
+
каждый кандидат обязан устранять реальный императивный код/дублирование и иметь
|
|
9
|
+
реального consumer (QA card / test / app), иначе он отклоняется.
|
|
10
|
+
|
|
11
|
+
Метод: multi-agent проход. 7 finder-агентов по подсистемам (chart, menu, grid,
|
|
12
|
+
dnd-geometry, replay-logs, settings/params/input, utils-core) → на каждый найденный
|
|
13
|
+
кандидат отдельный adversarial critic (проверка фактов grep'ом, сверка с уже
|
|
14
|
+
сделанным и запрещённым списком). Всего 27 агентов, 0 ошибок. Итог: 7 кандидатов
|
|
15
|
+
прошли verify (3 do-now, 4 parked), 13 отклонено.
|
|
16
|
+
|
|
17
|
+
Критерий осмысленности (из надиктовки): хочешь получить X,Y/размер/счётчик →
|
|
18
|
+
правильный хук отдаёт **метод**, который это возвращает (`getSize()`, `getXY()`,
|
|
19
|
+
`toPixel(v)`, `count()`, `isDirty()`). Не рефактор ради стиля.
|
|
20
|
+
|
|
21
|
+
## Сверка с уже сделанным / запрещённым
|
|
22
|
+
|
|
23
|
+
Проход НЕ переоткрывает уже сделанное: `useKeyboard`, `useMiniLogsTable`,
|
|
24
|
+
`useParamsEditorController`, `useSettingsDialogController`, `useFloatingWindowController`,
|
|
25
|
+
`useRightMenuController`, `createLogsController`/`useLogsTableController`/
|
|
26
|
+
`useLogsNotificationsController`, `useTextInputPanel`/`useFileInputPanel`, section/density
|
|
27
|
+
registries, `createSearchHistory`, `createUiSlot`, `columnState` `createUpdateApi` и др.
|
|
28
|
+
И НЕ предлагает запрещённое без consumer: `FResizableReact` hook, `memoryStore` core
|
|
29
|
+
rewrite, `PageVisibilityProvider` в обычный UI, `StickerMenu` shared primitive, `DragBox`
|
|
30
|
+
broad rewrite. QA card 25 (эталонный toolbar, ~qa.tsx:603) — не трогать.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Прошли verify — DO NOW (3)
|
|
35
|
+
|
|
36
|
+
### 1. `useResizeObserver` / `useElementSize` — `MyResizeObserver.tsx`
|
|
37
|
+
|
|
38
|
+
- Эффективность: medium; простота: simple; риск: low.
|
|
39
|
+
- Императив сейчас: `CResizeObserver` — это класс + модульный синглтон
|
|
40
|
+
`global_resizeObserver`, чисто side-effect (`add()`/`delete()`), **без React-хука**,
|
|
41
|
+
который бы отдавал измерения.
|
|
42
|
+
- Реальный consumer: `qa.tsx` `ResizeBugRepro` (объявлен ~1076, рендерится ~1723)
|
|
43
|
+
вручную городит `new ResizeObserver` + `rAF` + `getBoundingClientRect` +
|
|
44
|
+
`disconnect` в `useState`. Это **не** эталонная card 25.
|
|
45
|
+
- Также `REFACTOR_PLAN.md` (строки 187/209/221/309) явно просит `useElementSize`
|
|
46
|
+
вместо ручного `MyResizeObserver` цикла.
|
|
47
|
+
- API, который вернёт хук: `{ width, height, getSize(), ref/bind }` — ровно «хочу
|
|
48
|
+
размер → получаю метод/значение».
|
|
49
|
+
- Подход: хук оборачивает существующий синглтон `CResizeObserver`, поэтому
|
|
50
|
+
`setResizeableElement`/вызовы из `ParamsEditor` остаются нетронутыми → малый surface.
|
|
51
|
+
- Оговорки verify: только один чистый consumer (QA card); упомянутые «места
|
|
52
|
+
переиспользования» (FloatingWindow читает x/y для clamp, не размер; chart/agGrid
|
|
53
|
+
имеют свои ResizeObserver, но сложные) чисто не переходят на width/height API —
|
|
54
|
+
не завышать охват.
|
|
55
|
+
- Проверка: `tsc` + `jest`; `ResizeBugRepro` переведён на хук, width/height
|
|
56
|
+
обновляются при ресайзе на стенде; старый `setResizeableElement` путь не сломан.
|
|
57
|
+
|
|
58
|
+
### 2. `useLogsPageTable` / `useLogsFullTableController` — `logs.tsx`
|
|
59
|
+
|
|
60
|
+
- Эффективность: medium; простота: medium; риск: medium.
|
|
61
|
+
- Императив сейчас: `PageLogs` гоняет ag-grid императивно через `apiGrid` ref;
|
|
62
|
+
блок importance-filter `setFilterModel` **написан дважды** (эффект `updateBy(setting)`
|
|
63
|
+
~92-103 И `onGridReady` ~160-167); `sizeColumnsToFit()` на update; `applyTransactionAsync`
|
|
64
|
+
append (~105-112).
|
|
65
|
+
- Реальный live consumer: `qa.tsx` card 9 (монтирует `<PageLogs />` ~94; assertion ~1765).
|
|
66
|
+
- Это последняя logs-таблица, не переведённая на controller-паттерн (MiniLogs и
|
|
67
|
+
`logsContext.LogsTable` — отдельные поверхности, уже сделаны).
|
|
68
|
+
- API: `getApi()`, `fit()`, `applyImportanceFilter(min)`, `appendRow(row)`,
|
|
69
|
+
`onGridReady`, `columnDefs`, `gridProps`. Дедуп filter-логики в один метод.
|
|
70
|
+
- Подход: `PageLogs` остаётся тонким wrapper; row identity сохранить.
|
|
71
|
+
- Проверка: card 9 + стенд, особое внимание filter-timing и `applyTransactionAsync` append.
|
|
72
|
+
|
|
73
|
+
### 3. `useCacheMapPersistence` — `cache.ts`
|
|
74
|
+
|
|
75
|
+
- Эффективность: medium; простота: simple; риск: low.
|
|
76
|
+
- Императив сейчас: тройное **точное** дублирование
|
|
77
|
+
`useEffect(() => { void cache.load(); return cache.onDirty(() => cache.saveDebounced(300)); }, [])`
|
|
78
|
+
в `qa.tsx`: `SettingsDialogDemo` (1221-1224), `UiSlotDemo` (1247-1251),
|
|
79
|
+
`ToolbarDemo` (1329-1332). `ToolbarDemo` (~1320) — отдельная демка, **не** эталонная
|
|
80
|
+
card 25 (та ~603).
|
|
81
|
+
- Совпадает с задокументированным app-контрактом `doc/EXAMPLE_USAGE.md:85-99`,
|
|
82
|
+
`doc/wenay-react2.md:54-63`.
|
|
83
|
+
- API: `{ isDirty(), flush(), save(), reload() }`. Внимание: cache отдаёт `load()`,
|
|
84
|
+
а не `reload()` — `reload()` сделать явным alias/ре-экспортом `load()`, не выдумывать
|
|
85
|
+
несуществующий метод.
|
|
86
|
+
- Границы: аддитивная обёртка, **НЕ** `memoryStore` rewrite; scope строго
|
|
87
|
+
`load` + `onDirty`→`saveDebounced`, без pagehide/visibility flush.
|
|
88
|
+
- Каждый сайт схлопывает ~2-3 строки — польза реальная, но скромная (поэтому
|
|
89
|
+
medium, не high).
|
|
90
|
+
- Проверка: card 20/21 и toolbar demo — persistence переживает reload; card 25 не трогать.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Прошли verify — PARKED (4)
|
|
95
|
+
|
|
96
|
+
Реальны, но сейчас не do-now; причина зафиксирована, вернуть при условии.
|
|
97
|
+
|
|
98
|
+
### `useChartCanvas` — `myChart/1/myChartTest.tsx`
|
|
99
|
+
- Boilerplate реален (`ChartDemo`: `chartRef`+`containerRef`, `useEffect` с
|
|
100
|
+
`createChartCanvas`/`appendData`/`jumpToEnd`/`setInterval`/`destroy`, кнопки через
|
|
101
|
+
`chartRef.current?.method` null-check). `createChartCanvas` уже сам возвращает
|
|
102
|
+
API-объект — хук лишь добавил бы lifecycle и стабильные non-null методы (паттерн
|
|
103
|
+
как `useFloatingWindowController`). API: `containerRef, appendData, clearData,
|
|
104
|
+
scrollX, zoomX, jumpToStart/End/Index, setAutoScaleY, setShowTimeAxis/PriceAxis, draw()`.
|
|
105
|
+
- **Почему parked**: единственный consumer — сам example-компонент `ChartDemo`, который
|
|
106
|
+
**не отрендерен ни в одной карточке/тесте** → нельзя проверить на стенде (working-style
|
|
107
|
+
требует tsc+стенд). Польза чисто косметическая/демонстрационная.
|
|
108
|
+
- Поправки к автору кандидата: `redraw()` не существует (в `IChartCanvas` это `draw()`);
|
|
109
|
+
`ready` в интерфейсе нет — не тащить в API.
|
|
110
|
+
- Вернуть как do-now, когда `ChartDemo` реально появится в карточке/тесте.
|
|
111
|
+
|
|
112
|
+
### `useContextMenuGesture` — `menuMouse.tsx`
|
|
113
|
+
- Дублирование реально: gesture-распознавание (порог 0.05, long-press 300ms,
|
|
114
|
+
right/dbl-click) в `menuMouse` `Layer` (~348-380) и в `menuR` `MenuR` (~55-107).
|
|
115
|
+
- **Почему parked**: `menuR.MenuR` — **мёртвый код** (нет app-consumer; заявленный тест
|
|
116
|
+
на самом деле импортит `RightMenu.tsx`, не `menuR`). Единственный live consumer —
|
|
117
|
+
`Layer`. Делить хук с мёртвым кодом — мнимый выигрыш; честный фикс той половины —
|
|
118
|
+
удалить `menuR`. Touch-семантика тонкая, card 4 гоняет только mouse-путь → риск
|
|
119
|
+
touch-регрессии без нового consumer. Это часть уже-parked context-menu controller split.
|
|
120
|
+
- Вернуть при: оживлении `menuR` с touch-тестами ИЛИ новом live touch-consumer.
|
|
121
|
+
|
|
122
|
+
### `columnState.api.reorderPreview(order, key, to)` — `columnState.ts`
|
|
123
|
+
- Метод на существующем `createColumnState` контроллере (не новый хук): экспонировать
|
|
124
|
+
move+fixed-pinning preview (+опц. `applyFixedPinning(order)`). Тройное дублирование
|
|
125
|
+
идиомы `filter(!fixed)+pinFixed-splice`: `columnState.ts:140-145`,
|
|
126
|
+
`ColumnsMenu.tsx:187-198`, `Toolbar.tsx:448-459`. Реальный consumer — `ColumnsMenu`
|
|
127
|
+
(qa cards 30/31).
|
|
128
|
+
- **Почему parked**: метод дедупит только 1 из 3 копий (копия `Toolbar` — другой
|
|
129
|
+
контроллер над своими `opts.items`, честно вне охвата), и пересекается лишь pin-половина
|
|
130
|
+
→ польза low. Брать, если появится общий reorder-контракт для обоих контроллеров.
|
|
131
|
+
|
|
132
|
+
### `useResizeableFit` — `ParamsEditor.tsx`
|
|
133
|
+
- 4x **байт-идентичный** inline ref-callback (`InputTime` 113-117, `InputList` 175-179,
|
|
134
|
+
`InputNumber` 212-216, `InputListArray` 269-273): `setResizeableElement(el)` +
|
|
135
|
+
`removeResizeableElement` cleanup. Consumer — `ParamsEditor` (qa cards 7 и 19).
|
|
136
|
+
Также чинит minor unmount-dispose leak.
|
|
137
|
+
- **Почему parked / поправка**: это **не** `use*`-хук — все 4 сайта внутри обычных
|
|
138
|
+
функций-хелперов (`InputTime`/`InputList`/`InputNumber`/`InputListArray`), а не
|
|
139
|
+
компонентов, там нельзя вызывать хуки. Правильная форма — общий **module-level
|
|
140
|
+
стабильный bind callback-ref** (`bind(el)` ставит `setResizeableElement`, возвращает
|
|
141
|
+
`()=>removeResizeableElement`). Не пересекается с done (`useParamsEditorController`
|
|
142
|
+
покрывает только draft/notify) и forbidden (`FResizableReact` — другое). low risk,
|
|
143
|
+
узкая польза — можно сделать как маленький shared primitive отдельным микро-passом.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Отклонено (13) — кратко, для покрытия
|
|
148
|
+
|
|
149
|
+
- `useChartEngine` (`chartEngineReact.tsx`) — **parked/не conscious**: императивная
|
|
150
|
+
проводка реальна, но НИ ОДИН consumer не читает обратно координатный API
|
|
151
|
+
(`xToPixel/pixelToValue/getViewport/getCrosshair/getPanelCount`); обе точки
|
|
152
|
+
(`qa.tsx:1707` card 6, `use.tsx:187`) рендерят `<MyChartEngine/>` без пропов и ничего
|
|
153
|
+
не считывают. Координатный хук был бы спекулятивным. Вернуть, если появится стенд/тест
|
|
154
|
+
на chart canvas lifecycle или реальный consumer координат.
|
|
155
|
+
- `useMenuPlacement` (`menu.tsx`) — **skip**: реальный `useLayoutEffect` + `getBoundingClientRect`,
|
|
156
|
+
но ровно один in-file consumer и ноль дублирования; `RightMenu`/`LeftModal` клампят
|
|
157
|
+
по другой семантике (parent rect / другая ось). Извлекать нечего дедуплицировать.
|
|
158
|
+
- `useSidebarMenuController` (`LeftModal.tsx`) — **parked**: движок реален, но заявленные
|
|
159
|
+
consumers ложны (в `use.tsx` — закомментированный блок; `Parameters.tsx` не импортит
|
|
160
|
+
`LeftModal`). Нет live consumer. Совпадает с уже-parked LeftModal split.
|
|
161
|
+
- `useStickerMenuDrag` (`StickerMenu.tsx`) — **skip** (overlap forbidden): drag реален,
|
|
162
|
+
но consumer'ов нет (только barrel export + docs). `StickerMenu` в запрещённом списке.
|
|
163
|
+
- `useColumnDotsGestures` (`ColumnDots.tsx`) — **skip**: жестовая логика реальна, но
|
|
164
|
+
единственный consumer — card 29, второго нет. Извлечение = вью-модель без внешнего
|
|
165
|
+
X/Y/size/count consumer.
|
|
166
|
+
- `useCardModel` (`CardList.tsx`) — **skip**: single-use presentation-деривация (~15 строк),
|
|
167
|
+
без дублирования, consumer только сам `CardList` (card 29). Спекулятивно.
|
|
168
|
+
- `useDragArea` / `getXY on DragArea` (`DragArea.tsx`) — **skip** (overlap): у `DragArea`
|
|
169
|
+
нет render-consumers (только export + docs); дублирует `useDraggable`. Мёртвая поверхность.
|
|
170
|
+
- `createLogsStore` / `useLogsStore` (`logsContext.tsx`) — **parked** (overlap): store
|
|
171
|
+
реален и дублирует `logsController`, но новых consumer'ов нет (только same-file демо +
|
|
172
|
+
1 unit-test). Это migration/deprecation-решение по `logsContext`, а не быстрый split.
|
|
173
|
+
- `useInputAutoStep` (`ParamsEditor.tsx`) — **parked** (overlap): apply-only DOM behavior,
|
|
174
|
+
1 consumer (`InputNumber`), геттеров не читает; есть minor unmount-dispose leak (стоит
|
|
175
|
+
починить точечно). Не тянет на API-хук.
|
|
176
|
+
- `useNumericInputDraft` (`ParamsEditor.tsx`) — **parked**: WeakMap-черновик реален, но
|
|
177
|
+
`InputNumber` вызывается как обычная функция (`InputNumber(set,val,range)`, не JSX) →
|
|
178
|
+
хук внутри вызвать нельзя. Блокер до превращения `InputNumber` в компонент.
|
|
179
|
+
- `useCallbackHubEffect` (`callbackHub.ts`) — **skip**: `createCallbackHub.on` уже
|
|
180
|
+
возвращает unsubscribe, годный прямо в `useEffect`; обёртка не нужна; 1 demo-consumer.
|
|
181
|
+
- `useObservableMap` (`observableMap.ts`) — **skip**: чистый 45-строчный data-layer,
|
|
182
|
+
ноль UI-императива/дублирования; подписчики только внутренние + тесты.
|
|
183
|
+
- `usePageVisibility` (`pageVisibilityContext.tsx`) — **parked** (overlap forbidden):
|
|
184
|
+
дублирования нет (`PageVisibilityProvider` уже инкапсулирует listener); хук был бы просто
|
|
185
|
+
`useContext`; consumer'ов ноль. В запрещённом «без consumer» списке.
|
|
186
|
+
|
|
187
|
+
## Рекомендованный порядок
|
|
188
|
+
|
|
189
|
+
1. `useCacheMapPersistence` — самый безопасный (simple/low), аддитивный, 3 реальных consumer.
|
|
190
|
+
2. `useResizeObserver`/`useElementSize` — simple/low, явный запрос в REFACTOR_PLAN + живой consumer.
|
|
191
|
+
3. `useLogsPageTable` — medium/medium, последняя logs-таблица вне controller-паттерна; делать аккуратно (filter-timing).
|
|
192
|
+
|
|
193
|
+
Каждую задачу — по правилам `doc/target/README.md`: старое имя оставить compatibility
|
|
194
|
+
wrapper, публичный API не менять, проверять `tsc -p tsconfig.qa-check.json --noEmit` +
|
|
195
|
+
`jest` + сборку + стенд. Версию не бампать, не публиковать, не пушить.
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# Public surface normalization
|
|
2
|
+
|
|
3
|
+
Задача из target: пройти второй слой публичной поверхности `wenay-react2` и внедрять только те primitives, которые реально заменяют ручной код.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
In progress.
|
|
8
|
+
|
|
9
|
+
## Usage/example sources
|
|
10
|
+
|
|
11
|
+
Обновляем вместе с кодом, если меняется canonical usage:
|
|
12
|
+
- `doc/EXAMPLE_USAGE.md` — главный standards/example файл;
|
|
13
|
+
- `src/common/testUseReact/qa.tsx` — QA cards как живые примеры использования, особенно cards 20-31;
|
|
14
|
+
- `src/common/testUseReact/replayVideo.tsx` — Replay usage examples для cards 23-26;
|
|
15
|
+
- `src/common/src/grid/agGrid4/example.tsx`, `README.md`, `WRAPPER.md`, `MIGRATION.md` — grid wrapper examples/docs.
|
|
16
|
+
|
|
17
|
+
Legacy/compat examples (`src/common/testUseReact/use.tsx`, `useGrid.tsx`, отдельные старые cards) не переписывать автоматически; если они показывают low-level path, помечать как regression/compat, а не canonical pattern.
|
|
18
|
+
|
|
19
|
+
Правило прохода: для каждого изменения проверять, есть ли affected card/example. Если публичный API не менялся и пример уже показывает правильный usage, фиксировать "пример проверен, менять не нужно".
|
|
20
|
+
|
|
21
|
+
## Task scoring
|
|
22
|
+
|
|
23
|
+
Перед выполнением следующего кандидата ставим оценку:
|
|
24
|
+
- эффективность: high / medium / low;
|
|
25
|
+
- простота: simple / medium / hard;
|
|
26
|
+
- риск: low / medium / high.
|
|
27
|
+
|
|
28
|
+
Делаем сразу только high/medium эффективность при simple/medium сложности и понятном риске. Low-effectiveness или hard/high-risk без явного выигрыша — пропускать и записывать причину.
|
|
29
|
+
|
|
30
|
+
## Pass 1 — low-risk controller reuse
|
|
31
|
+
|
|
32
|
+
### `createUpdateApi` / `useUpdateByApi`
|
|
33
|
+
|
|
34
|
+
Проверено:
|
|
35
|
+
- `src/common/src/hooks/useKeyboard.ts` содержит простой mutable singleton `keyboardState` и прямые вызовы `renderBy(keyboardState)`.
|
|
36
|
+
|
|
37
|
+
Сделано:
|
|
38
|
+
- `useKeyboard.ts` переведён на `createUpdateApi(keyboardState)` для локального render-controller.
|
|
39
|
+
- Публичный API `keyboard`, `keyboardState`, `useKeyboard`, `keyboard.on/subscribe` не менялся.
|
|
40
|
+
|
|
41
|
+
Почему это не высосано из пальца:
|
|
42
|
+
- это ровно contract `createUpdateApi`: mutable object + render/subscribe controller;
|
|
43
|
+
- изменение маленькое и не тянет UI-поведение.
|
|
44
|
+
|
|
45
|
+
Ограничение:
|
|
46
|
+
- широкая замена `updateBy/renderBy` в `Toolbar`, `SettingsDialog`, `columnState` пока не делалась. Там controller может быть полезен, но риск выше, потому что эти объекты имеют persistence, registry или grid side effects.
|
|
47
|
+
|
|
48
|
+
## Pass 1 — tasks intentionally not implemented yet
|
|
49
|
+
|
|
50
|
+
### `ArrayPromise`
|
|
51
|
+
|
|
52
|
+
Статус: пока не внедрять.
|
|
53
|
+
|
|
54
|
+
Причина:
|
|
55
|
+
- в текущей библиотеке нет потребителя, где явно нужен именно последовательный набор promise-thunks с progress counters;
|
|
56
|
+
- для параллельных batch operations counters у `ArrayPromise` не являются стабильным контрактом;
|
|
57
|
+
- внедрение без конкретной batch-команды будет искусственным.
|
|
58
|
+
|
|
59
|
+
Что считать подходящим местом позже:
|
|
60
|
+
- реальные последовательные операции: массовое закрытие позиций, очистка лимиток, transfer step-by-step;
|
|
61
|
+
- если операции параллельные, нужен другой progress contract, не `ArrayPromise` counters.
|
|
62
|
+
|
|
63
|
+
### `DragBox` / `DragArea`
|
|
64
|
+
|
|
65
|
+
Статус: не использовать в новом коде как high-level стандарт.
|
|
66
|
+
|
|
67
|
+
Причина:
|
|
68
|
+
- это low-level/legacy movement components;
|
|
69
|
+
- для нового кода уже есть hook-first путь `useDraggableApi`, а для окон — `FloatingWindow`.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Pass 2 — grid helpers without grid lifecycle change
|
|
73
|
+
|
|
74
|
+
### Log tables default column definition
|
|
75
|
+
|
|
76
|
+
Проверено:
|
|
77
|
+
- `src/common/src/logs/miniLogs.tsx` и `src/common/src/logs/logs.tsx` дублировали один и тот же `defaultColDef`: `headerClass`, `resizable`, centered `cellStyle`, `sortable`, `filter`, `wrapText`.
|
|
78
|
+
|
|
79
|
+
Сделано:
|
|
80
|
+
- оба места переведены на общий `colDefCentered` + локальный `wrapText: true`.
|
|
81
|
+
- `AgGridReact` намеренно оставлен на месте: перевод на `AgGridTable` может изменить тему, row selection и lifecycle, поэтому это отдельная задача после визуальной проверки.
|
|
82
|
+
|
|
83
|
+
Почему это не высосано из пальца:
|
|
84
|
+
- helper `colDefCentered` уже существует именно для такого dense table default;
|
|
85
|
+
- замена убирает ручное дублирование, но не трогает grid lifecycle.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Pass 3 — compatibility modal store controller
|
|
89
|
+
|
|
90
|
+
### `createModalElementStore` / `createModalRenderStore`
|
|
91
|
+
|
|
92
|
+
Проверено:
|
|
93
|
+
- `src/common/src/components/Modal/Modal.tsx` содержит общий `createJsxStore`, где mutable object `data` одновременно используется как render subscription target.
|
|
94
|
+
|
|
95
|
+
Сделано:
|
|
96
|
+
- прямые `renderBy(data)` / `updateBy(data)` заменены на `createUpdateApi(data)` (`dataApi.render()` / `dataApi.use()`).
|
|
97
|
+
- публичные compatibility APIs `createModalElementStore()` и `createModalRenderStore()` не менялись.
|
|
98
|
+
|
|
99
|
+
Почему это не высосано из пальца:
|
|
100
|
+
- store уже был построен на mutable object + render subscription;
|
|
101
|
+
- `createUpdateApi` делает этот контракт явным без изменения поведения.
|
|
102
|
+
## Pass 4 — persisted slot controller
|
|
103
|
+
|
|
104
|
+
### `createUiSlot`
|
|
105
|
+
|
|
106
|
+
Проверено:
|
|
107
|
+
- `src/common/src/components/UiSlot/UiSlot.tsx` содержит persisted mutable store `st` из `memoryGetOrCreate` и прямые `renderBy(st)` / `updateBy(st)`.
|
|
108
|
+
|
|
109
|
+
Сделано:
|
|
110
|
+
- `createUiSlot` переведён на `createUpdateApi(st)` (`stApi.render()` / `stApi.use()`).
|
|
111
|
+
- публичный API `Slot`, `PlacementSetting`, `getPlace`, `setPlace` не менялся.
|
|
112
|
+
- usage example проверен: card 21 (createUiSlot - configurable block placement) уже использует публичный API; менять card не нужно, потому что refactor внутренний.
|
|
113
|
+
|
|
114
|
+
Почему это не высосано из пальца:
|
|
115
|
+
- это тот же contract, что в `useKeyboard` и compatibility modal store: mutable object + render subscription;
|
|
116
|
+
- persistence через `memoryMarkDirty` оставлен без изменений.
|
|
117
|
+
|
|
118
|
+
## Pass 5 — toolbar density registry controller
|
|
119
|
+
|
|
120
|
+
### `registerToolbarDensity` / toolbar density consumers
|
|
121
|
+
|
|
122
|
+
Оценка перед выполнением:
|
|
123
|
+
- эффективность: medium;
|
|
124
|
+
- простота: simple;
|
|
125
|
+
- риск: low.
|
|
126
|
+
|
|
127
|
+
Проверено:
|
|
128
|
+
- `src/common/src/components/Toolbar/Toolbar.tsx` содержит module-level mutable registry `densities` и прямые `renderBy(densities)` / `updateBy(densities)`.
|
|
129
|
+
- card 25 использует `registerToolbarDensity` как usage example.
|
|
130
|
+
|
|
131
|
+
Сделано:
|
|
132
|
+
- только registry `densities` переведён на `createUpdateApi(densities)` (`densitiesApi.render()` / `densitiesApi.use()`).
|
|
133
|
+
- persisted toolbar config `st`, `sourceMode`, external `listSource`, `memoryMarkDirty` не трогались.
|
|
134
|
+
- usage example проверен: card 25 (`createToolbar - customizable toolbar`) уже использует публичный `registerToolbarDensity`; менять card не нужно, потому что refactor внутренний.
|
|
135
|
+
|
|
136
|
+
Почему это не высосано из пальца:
|
|
137
|
+
- `densities` ровно module singleton + subscription target;
|
|
138
|
+
- изменение уменьшает прямой `renderBy/updateBy` там, где нет persistence/grid side effects.
|
|
139
|
+
|
|
140
|
+
## Pass 6 — settings section registry controller
|
|
141
|
+
|
|
142
|
+
### `registerSettingsSection` / `SettingsDialog`
|
|
143
|
+
|
|
144
|
+
Оценка перед выполнением:
|
|
145
|
+
- эффективность: medium;
|
|
146
|
+
- простота: medium;
|
|
147
|
+
- риск: medium.
|
|
148
|
+
|
|
149
|
+
Проверено:
|
|
150
|
+
- `src/common/src/components/Settings/SettingsDialog.tsx` содержит module-level mutable registry `registry` и прямые `renderBy(registry)` / `updateBy(registry)`.
|
|
151
|
+
- card 20 использует `registerSettingsSection` как usage example.
|
|
152
|
+
|
|
153
|
+
Сделано:
|
|
154
|
+
- только section registry переведён на `createUpdateApi(registry)` (`registryApi.render()` / `registryApi.use()`).
|
|
155
|
+
- search history, tree UX, resize behavior и `settingsDialogLayout` не менялись.
|
|
156
|
+
- usage example проверен: card 20 (`SettingsDialog - searchable settings tree + registry`) уже использует публичный `registerSettingsSection`; менять card не нужно, потому что refactor внутренний.
|
|
157
|
+
|
|
158
|
+
Low-effectiveness skip:
|
|
159
|
+
- `settingsDialogLayout` пока не переводился: сейчас нет явного `updateBy(settingsDialogLayout)` consumer, а nav width живёт в React state и сохраняется через `memoryMarkDirty`. Отдельный controller здесь мало что даст без изменения layout contract.
|
|
160
|
+
|
|
161
|
+
## Pass 7 — columnState controller split
|
|
162
|
+
|
|
163
|
+
### `createColumnState` runtime `rt` and persisted `st`
|
|
164
|
+
|
|
165
|
+
Оценка перед выполнением:
|
|
166
|
+
- `rt` present/presentGate: эффективность medium, простота simple, риск low-medium;
|
|
167
|
+
- `st` persisted config: эффективность medium, простота medium, риск medium.
|
|
168
|
+
|
|
169
|
+
Проверено:
|
|
170
|
+
- `src/common/src/grid/columnState/columnState.ts` содержит runtime store `rt` для `present` / `presentGate` и persisted store `st` для column config.
|
|
171
|
+
- cards 29/30 используют `usePresent` / `setPresentGate`; cards 28-31 используют persisted config через `useConfig`, grid adapter, menu, toolbar source.
|
|
172
|
+
|
|
173
|
+
Сделано:
|
|
174
|
+
- `rt` переведён на `createUpdateApi(rt)` (`rtApi.render()` / `rtApi.use()`).
|
|
175
|
+
- `st` переведён на `createUpdateApi(st)` (`stApi.render()` / `stApi.use()`).
|
|
176
|
+
- `commit`, `memoryMarkDirty`, `applyToGrid`, `emitChange`, `listSource` и grid adapter flow не менялись.
|
|
177
|
+
- usage examples проверены: cards 28-31 уже используют публичный `createColumnState` API; менять cards не нужно, потому что refactor внутренний.
|
|
178
|
+
|
|
179
|
+
## Pass 8 — search history controller
|
|
180
|
+
|
|
181
|
+
### `createSearchHistory`
|
|
182
|
+
|
|
183
|
+
Оценка перед выполнением:
|
|
184
|
+
- эффективность: high;
|
|
185
|
+
- простота: simple;
|
|
186
|
+
- риск: low.
|
|
187
|
+
|
|
188
|
+
Проверено:
|
|
189
|
+
- `src/common/src/utils/searchHistory.ts` содержит persisted mutable store `st` и прямые `renderBy(st)` / `updateBy(st)`.
|
|
190
|
+
- `SettingsDialog` и card 20 используют этот primitive для истории поиска.
|
|
191
|
+
|
|
192
|
+
Сделано:
|
|
193
|
+
- `createSearchHistory` переведён на `createUpdateApi(st)` (`stApi.render()` / `stApi.use()`).
|
|
194
|
+
- публичный API `items`, `use`, `add`, `remove`, `clear` не менялся.
|
|
195
|
+
- usage docs уже рекомендуют `createSearchHistory({key})`; менять пример не нужно.
|
|
196
|
+
|
|
197
|
+
## Pass 9 — toolbar persisted config controller
|
|
198
|
+
|
|
199
|
+
### `createToolbar` local `st`
|
|
200
|
+
|
|
201
|
+
Оценка перед выполнением:
|
|
202
|
+
- эффективность: medium;
|
|
203
|
+
- простота: medium;
|
|
204
|
+
- риск: medium.
|
|
205
|
+
|
|
206
|
+
Проверено:
|
|
207
|
+
- `src/common/src/components/Toolbar/Toolbar.tsx` после density-registry pass всё ещё содержал persisted config store `st` с прямыми `renderBy(st)` / `updateBy(st)`.
|
|
208
|
+
- cards 25, 30, 31 используют `createToolbar` local/external source flows.
|
|
209
|
+
|
|
210
|
+
Сделано:
|
|
211
|
+
- local toolbar store `st` переведён на `createUpdateApi(st)` (`stApi.render()` / `stApi.use()`).
|
|
212
|
+
- external source hook `ext?.useConfig()`, `sourceMode`, `memoryMarkDirty`, `emitChange` не менялись.
|
|
213
|
+
- usage examples проверены: cards 25/30/31 уже используют публичный API; менять cards не нужно, потому что refactor внутренний.
|
|
214
|
+
|
|
215
|
+
## Pass 10 — window/menu controller normalization
|
|
216
|
+
|
|
217
|
+
### `FloatingWindow` open-window registry
|
|
218
|
+
|
|
219
|
+
Оценка перед выполнением:
|
|
220
|
+
- эффективность: medium;
|
|
221
|
+
- простота: medium;
|
|
222
|
+
- риск: medium.
|
|
223
|
+
|
|
224
|
+
Проверено:
|
|
225
|
+
- `src/common/src/components/Dnd/FloatingWindow.tsx` содержит module-level `openWindows` registry для z-index order и прямые `renderBy(openWindows)` / `updateBy(openWindows, cb)`.
|
|
226
|
+
- card 2 и Settings/Dialog flows используют `FloatingWindow` как usage examples.
|
|
227
|
+
|
|
228
|
+
Сделано:
|
|
229
|
+
- `openWindows` переведён на `createUpdateApi(openWindows)` (`openWindowsApi.render()` / `openWindowsApi.use(cb)`).
|
|
230
|
+
- drag/resize geometry, document listeners, `floatingWindowMap` persistence и `DragBox` не менялись.
|
|
231
|
+
|
|
232
|
+
### `LeftModal` menu store
|
|
233
|
+
|
|
234
|
+
Оценка перед выполнением:
|
|
235
|
+
- эффективность: medium;
|
|
236
|
+
- простота: simple;
|
|
237
|
+
- риск: medium.
|
|
238
|
+
|
|
239
|
+
Проверено:
|
|
240
|
+
- `src/common/src/components/Modal/LeftModal.tsx` содержит local `menuStore` Map внутри `getApiLeftMenu()` и прямые `renderBy(menuStore)` / `updateBy(menuStore)`.
|
|
241
|
+
|
|
242
|
+
Сделано:
|
|
243
|
+
- `menuStore` переведён на `createUpdateApi(menuStore)` (`menuStoreApi.render()` / `menuStoreApi.use()`).
|
|
244
|
+
- compatibility method `ApiLeftMenu.renderBy()` оставлен, но внутри вызывает `menuStoreApi.render()`.
|
|
245
|
+
- `createModalElementStore()` path уже был переведён раньше; меню/модалка публично не менялись.
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
## Pass 11 — log tables wrapper transition
|
|
249
|
+
|
|
250
|
+
### `PageLogs` / `MiniLogs`
|
|
251
|
+
|
|
252
|
+
Оценка перед выполнением:
|
|
253
|
+
- эффективность: medium/high;
|
|
254
|
+
- простота: medium;
|
|
255
|
+
- риск: medium, потому что внешний вид/theme/resize могут измениться, но QA stand это покрывает вручную.
|
|
256
|
+
|
|
257
|
+
Проверено:
|
|
258
|
+
- `src/common/src/logs/logs.tsx` и `src/common/src/logs/miniLogs.tsx` после defaultColDef pass всё ещё использовали raw `AgGridReact`.
|
|
259
|
+
- card 9 (`Logs - time format`) использует `logsApi.React.PageLogs` и подходит для ручной проверки основного log table path.
|
|
260
|
+
|
|
261
|
+
Сделано:
|
|
262
|
+
- `PageLogs` и `MiniLogs` переведены на `AgGridTable`.
|
|
263
|
+
- logger state/timing, `logsApi`, `MessageEventLogs`, `contextMenu` copy action, filter update path и row transaction path не менялись.
|
|
264
|
+
- `logsContext.tsx` намеренно не трогался: это отдельный older/context logger implementation with localStorage + raw grid.
|
|
265
|
+
|
|
266
|
+
Ручная проверка:
|
|
267
|
+
- card 9: нажать `add log`, время форматируется как `HH:mm:ss`, строка появляется в таблице, правый клик по ячейке даёт copy menu.
|
|
268
|
+
- если `MiniLogs` есть в app/stand consumer, проверить: таблица растягивается по контейнеру, click callback на ячейке вызывается.
|
|
269
|
+
|
|
270
|
+
## Pass 12 — MiniLogs hook/controller layering
|
|
271
|
+
|
|
272
|
+
### `useMiniLogsTable` / `MiniLogsView` / `MiniLogsTable`
|
|
273
|
+
|
|
274
|
+
Оценка перед выполнением:
|
|
275
|
+
- эффективность: medium/high;
|
|
276
|
+
- простота: medium;
|
|
277
|
+
- риск: medium.
|
|
278
|
+
|
|
279
|
+
Проверено:
|
|
280
|
+
- прямых runtime-использований `MiniLogs` в repo не найдено, но public barrel уже экспортирует `miniLogs.tsx` через `src/common/api.tsx`;
|
|
281
|
+
- card 9 покрывал `PageLogs`, но не `MiniLogs`, поэтому QA example требовал обновления;
|
|
282
|
+
- row identity нельзя менять на `AgGridTable data={...}` / default `id`, потому логовый `id` может повторяться по source area.
|
|
283
|
+
|
|
284
|
+
Сделано:
|
|
285
|
+
- `MiniLogs` разделён на hook/controller `useMiniLogsTable`, pure visual `MiniLogsView`, convenience `MiniLogsTable` и compatibility wrapper `MiniLogs`;
|
|
286
|
+
- hook отдаёт `props/tableProps/gridProps`, `apiRef`, `fit`, `getApi`, `withApi` и callbacks, но не создаёт внутренний `useAgGrid` controller;
|
|
287
|
+
- сохранён `rowData` path и старый `CellMouseDownEvent<T>` click contract;
|
|
288
|
+
- QA card 9 получил прямой `MiniLogsTable` block;
|
|
289
|
+
- docs обновлены в `doc/EXAMPLE_USAGE.md`, `doc/wenay-react2.md`, `doc/wenay-react2-rare.md`;
|
|
290
|
+
- добавлен `__test/miniLogs.test.tsx` на hook props/click/grid helpers.
|
|
291
|
+
|
|
292
|
+
Почему store не добавлен:
|
|
293
|
+
- MiniLogs сейчас table-view над внешним `data`; нет реального consumer для append/limit/history store;
|
|
294
|
+
- MiniLogs-owned state лучше добавлять позже только вместе с `createLogsController` или конкретным app consumer.
|
|
295
|
+
|
|
296
|
+
## Next candidates
|
|
297
|
+
|
|
298
|
+
1. `logs.tsx` logger internals — эффективность medium, простота hard, риск medium-high. Там несколько stores, callback-subscriptions, notification timing и table rendering; не делать как small pass без отдельного logger plan.
|
|
299
|
+
2. `logsContext.tsx` — эффективность medium, простота hard, риск medium-high. Есть прямой `localStorage` и голый `AgGridReact`, но split нужен отдельно: persistence contract + grid lifecycle/visual check.
|
|
300
|
+
3. `memoryStore.tsx` direct `renderBy(cur)` — эффективность low/medium, простота medium, риск medium. Подробно:
|
|
301
|
+
- прямой вызов есть только в `memoryUpdate(key, mutate)`: он ререндерит подписчиков именно на объект `cur`, который лежит внутри `memoryProps`;
|
|
302
|
+
- `memoryProps` сам является `ObservableMap` и уже наблюдается `memoryCache`, поэтому `memorySet`, `memoryGetOrCreate`, `memoryMarkDirty` идут по map/cache dirty channel, а не по React-subscription API;
|
|
303
|
+
- перевод `memoryUpdate` на `createUpdateApi(cur)` технически возможен, но почти ничего не меняет для публичного contract, потому что API создаётся на лету для произвольного stored object;
|
|
304
|
+
- риск в том, что это core persistence helper: им пользуются toolbar/columnState/UiSlot/search history/settings/window/right menu flows, и ошибка ударит не по одному widget, а по всему сохранению UI-конфига;
|
|
305
|
+
- делать стоит только если появится конкретный consumer/test на `memoryUpdate` или если мы явно вводим стандарт “memory object update controller” и покрываем это тестом: subscribe to stored object -> `memoryUpdate` -> rerender + `memoryCache.onDirty`.
|
|
306
|
+
4. `SettingsDialog.layout` controller — эффективность low, простота simple, риск low. Сейчас нет `updateBy(settingsDialogLayout)` consumer, поэтому пропущено как малоэффективное.
|
|
307
|
+
5. `PageVisibilityProvider` candidates — эффективность low до появления реального polling/replay/canvas consumer. Сейчас не внедрять в обычный UI ради helper.
|
|
308
|
+
6. Store/listen hooks — эффективность low внутри shared library без настоящего `Observe.Store` / `listen` consumer. Больше смысла искать в QA/app examples.
|
|
309
|
+
|
|
310
|
+
## Verification
|
|
311
|
+
|
|
312
|
+
- `npx tsc -p tsconfig.qa-check.json --noEmit` — passed after Pass 12.
|
|
313
|
+
- `npm run testjest -- --runInBand miniLogs.test.tsx` — passed after Pass 12: 1 suite, 2 tests.
|
|
314
|
+
- `npm run testjest -- --runInBand` — passed after Pass 12: 10 suites, 27 tests.
|
|
315
|
+
- `npm run build` — passed after Pass 12.
|
|
316
|
+
- `git diff --check` — passed after Pass 12; only LF/CRLF normalization warnings.
|
|
317
|
+
|
|
318
|
+
## Pass 13 — final inventory of remaining primitive-reuse candidates
|
|
319
|
+
|
|
320
|
+
Дата: 2026-07-09.
|
|
321
|
+
|
|
322
|
+
Цель: после hook/controller-first очереди повторно проверить широкий пункт `Недоиспользуемые методы wenay-react2` и не делать задачи ради задач.
|
|
323
|
+
|
|
324
|
+
Проверено:
|
|
325
|
+
- raw `AgGridReact`: остался только внутри самого wrapper `agGrid4.tsx` и в `src/common/testUseReact/useGrid.tsx`;
|
|
326
|
+
- `src/common/testUseReact/useGrid.tsx` уже документирован в `doc/EXAMPLE_USAGE.md` / `doc/PROJECT_FUNCTIONALITY.md` как legacy/regression example для низкоуровневого `applyGridRows`, а не canonical grid pattern;
|
|
327
|
+
- `logsContext.tsx` всё ещё имеет прямой `localStorage`, но это публичное legacy/compat поведение provider-а; перевод на `memoryCache` изменит contract, потому что app должен будет сам вызывать `memoryCache.load/save`;
|
|
328
|
+
- `PageVisibilityProvider` candidates есть только в тяжёлых timer/canvas/replay/chart местах (`useReplay`, chart engines, QA replay demos), где нужно отдельное policy-решение: продолжать catch-up в hidden tab или паузить;
|
|
329
|
+
- ручные `listen.on` / `useEffect` в QA в основном демонстрируют compatibility APIs или сами hook implementations; единственный useful cleanup — card 25 toolbar `tb.api.onChange` — безопасно переведён на `useListenEffect`;
|
|
330
|
+
- Settings/Toolbar registries уже показаны активными QA cards 20/25/30/31; новых low-risk registry migrations без app consumer не найдено;
|
|
331
|
+
- ModalProvider/useModal уже есть active QA card 13; imperative modal stores оставлены documented compatibility path.
|
|
332
|
+
|
|
333
|
+
Решение:
|
|
334
|
+
- `qa.tsx` card 25: `tb.api.onChange.on(...)` заменён на `useListenEffect(tb.api.onChange, ...)`;
|
|
335
|
+
- `useGrid.tsx` не переводить на `AgGridTable`, пока он нужен как regression для `applyGridRows`;
|
|
336
|
+
- `logsContext` storage не менять без отдельной задачи “context logger persistence contract”;
|
|
337
|
+
- PageVisibility/replay/chart timers не менять без явного UX-policy;
|
|
338
|
+
- broad primitive-reuse target считать выполненным на текущем repository surface: оставшиеся пункты low-effectiveness или high-risk без нового consumer/test.
|
|
339
|
+
|
|
340
|
+
## Verification update
|
|
341
|
+
|
|
342
|
+
- `npx tsc -p tsconfig.qa-check.json --noEmit` — passed after FloatingWindow / final inventory / QA listen cleanup.
|
|
343
|
+
- `npm run testjest -- --runInBand createToolbar.test.ts` — passed after QA listen cleanup.
|
|
344
|
+
- `npm run testjest -- --runInBand floatingWindowController.test.tsx settingsDialog.test.tsx` — passed: 2 suites, 6 tests.
|
|
345
|
+
- `npm run testjest -- --runInBand` — passed: 14 suites, 44 tests.
|
|
346
|
+
- `npm run build` — passed.
|
|
347
|
+
- `git diff --check` — passed; only LF/CRLF normalization warnings.
|
|
348
|
+
## Pass 14 — Toolbar.Bar movement animation
|
|
349
|
+
|
|
350
|
+
Дата: 2026-07-09.
|
|
351
|
+
|
|
352
|
+
Задача: пользователь подтвердил card 30 как эталон и попросил такую же анимацию горизонтального меню в cards 25 и 31.
|
|
353
|
+
|
|
354
|
+
Сделано:
|
|
355
|
+
- `createToolbar().Bar` получил общий FLIP-pass (`useToolbarFlip`): при изменении order/visibility/density элементы получают временный `translate(dx,dy)` и возвращаются через `transform 180ms ease`;
|
|
356
|
+
- card 25 and card 31 используют стандартный `Toolbar.Bar`, поэтому получили анимацию без копирования stand-only `Qa30AnimatedMenuBar`;
|
|
357
|
+
- card 30 оставлен эталоном и не переписывался;
|
|
358
|
+
- публичный API `createToolbar`, `ToolbarItem`, `ToolbarConfig` не менялся.
|
|
359
|
+
|
|
360
|
+
Проверка:
|
|
361
|
+
- `npx tsc -p tsconfig.qa-check.json --noEmit`;
|
|
362
|
+
- `npm run testjest -- --runInBand createToolbar.test.ts`;
|
|
363
|
+
- full test/build перед публикацией v1.0.40.
|
|
364
|
+
|
|
365
|
+
Оценка пользы:
|
|
366
|
+
- UI-польза: high для cards 25/31, потому order/density changes теперь визуально читаются так же, как в card 30;
|
|
367
|
+
- архитектурная польза: medium/high, потому поведение находится в shared `Toolbar.Bar`, а не в каждом stand/example отдельно;
|
|
368
|
+
- performance-польза: low/medium, это не ускорение вычислений, а FLIP-анимация с одним layout read pass на изменение меню.
|