wenay-react2 1.0.41 → 1.0.42
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 +58 -2
- package/doc/PROJECT_FUNCTIONALITY.md +5 -1
- package/doc/changes/v1.0.41.md +23 -0
- package/doc/changes/v1.0.42.md +26 -0
- package/doc/progress/architecture-fix-queue.md +74 -0
- package/doc/progress/style-system-normalization.md +5 -5
- package/doc/target/my.md +39 -67
- package/doc/wenay-react2-rare.md +118 -13
- package/doc/wenay-react2.md +75 -11
- package/lib/common/api.d.ts +1 -0
- package/lib/common/api.js +8 -4
- package/lib/common/src/components/Dnd/DragArea.d.ts +5 -0
- package/lib/common/src/components/Dnd/DragArea.js +5 -1
- package/lib/common/src/components/Dnd/FloatingWindow.d.ts +9 -5
- package/lib/common/src/components/Dnd/FloatingWindow.js +39 -97
- package/lib/common/src/components/Dnd/Resizable.d.ts +3 -7
- package/lib/common/src/components/Dnd/Resizable.js +4 -3
- package/lib/common/src/components/Menu/RightMenu.js +8 -4
- package/lib/common/src/components/Menu/RightMenuStore.d.ts +2 -2
- package/lib/common/src/components/Menu/RightMenuStore.js +5 -3
- package/lib/common/src/components/Modal/LeftModal.js +4 -2
- package/lib/common/src/components/Modal/ModalContextProvider.js +5 -16
- package/lib/common/src/components/MyResizeObserver.d.ts +24 -0
- package/lib/common/src/components/MyResizeObserver.js +49 -0
- package/lib/common/src/components/Overlay.d.ts +24 -0
- package/lib/common/src/components/Overlay.js +28 -0
- package/lib/common/src/components/Settings/SettingsDialog.js +21 -22
- package/lib/common/src/components/Toolbar/Toolbar.d.ts +1 -0
- package/lib/common/src/components/Toolbar/Toolbar.js +16 -23
- package/lib/common/src/grid/columnState/ColumnsMenu.js +4 -13
- package/lib/common/src/grid/columnState/columnGrid.d.ts +103 -0
- package/lib/common/src/grid/columnState/columnGrid.js +231 -0
- package/lib/common/src/grid/columnState/columnState.js +10 -9
- package/lib/common/src/grid/columnState/index.js +3 -0
- package/lib/common/src/hooks/useDraggable.d.ts +8 -0
- package/lib/common/src/hooks/useDraggable.js +13 -4
- package/lib/common/src/hooks/useOutside.d.ts +4 -1
- package/lib/common/src/hooks/useOutside.js +2 -1
- package/lib/common/src/logs/logs.d.ts +96 -5
- package/lib/common/src/logs/logs.js +147 -108
- package/lib/common/src/logs/logsController.d.ts +3 -0
- package/lib/common/src/utils/cache.d.ts +12 -0
- package/lib/common/src/utils/cache.js +0 -0
- package/lib/common/src/utils/fixedOrder.d.ts +15 -0
- package/lib/common/src/utils/fixedOrder.js +30 -0
- package/lib/common/src/utils/index.d.ts +2 -0
- package/lib/common/src/utils/index.js +2 -0
- package/lib/common/src/utils/memoryStore.d.ts +3 -6
- package/lib/common/src/utils/memoryStore.js +1 -3
- package/lib/common/src/utils/persistedMaps.d.ts +16 -0
- package/lib/common/src/utils/persistedMaps.js +5 -0
- package/lib/common/src/utils/searchHistory.js +7 -6
- package/lib/common/src/utils/structEqual.d.ts +12 -0
- package/lib/common/src/utils/structEqual.js +40 -0
- package/lib/common/updateBy.d.ts +3 -0
- package/lib/common/updateBy.js +57 -22
- package/lib/style/style.css +58 -1
- package/package.json +2 -2
package/doc/wenay-react2-rare.md
CHANGED
|
@@ -12,7 +12,7 @@ New code teaches and imports the short canonical surface:
|
|
|
12
12
|
|
|
13
13
|
Old names are recorded only in `WENAY_REACT2_RENAMES.md`; do not export new compatibility aliases casually. Existing public APIs should usually remain supported during internal refactors; removals require an explicit migration cut with a separate task, migration notes, changelog, and usage signal. Default style contracts are stricter: do not remove old default classes/styles until the replacement class/token path is shipped and documented.
|
|
14
14
|
Do not add new examples with Get*, FuncJSX, *2/*3, or business-specific helper names.
|
|
15
|
-
If a primitive needs app policy, build a small app wrapper above it.
|
|
15
|
+
If a primitive needs app policy, build a small app wrapper above it.
|
|
16
16
|
If behavior is reusable, expose it first as a headless use*/create* API. The hook/controller may return methods, getters, props/bind helpers, or a small API object; visual components and QA cards should consume that surface instead of becoming the only implementation.
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -53,6 +53,14 @@ confirmModal({modal, func, password?})
|
|
|
53
53
|
|
|
54
54
|
`inputModal` and `confirmModal` accept either a setter function or the `useModal()` controller.
|
|
55
55
|
|
|
56
|
+
Internal Overlay (A9, 2026-07-10): `components/Overlay.tsx` is the ONE portal+scrim+outside-click+
|
|
57
|
+
Escape composition; `ModalProvider` and `SettingsDialog` are adapters over it (behavior 1:1,
|
|
58
|
+
SettingsDialog keeps its own two-stage Escape by NOT passing `onEscape`). It is deliberately not
|
|
59
|
+
exported: apps use ModalProvider/useModal/SettingsDialog. The render-slot stores above, the
|
|
60
|
+
LeftModal drawer (gesture-driven, scrim-less), and `ModalWrapper`/Input helpers (backdrop-less,
|
|
61
|
+
hosted inside another modal slot) are NOT overlays and stay separate. This leaf is also where all
|
|
62
|
+
scrim/portal DOM lives - the seam for a future react-native view layer.
|
|
63
|
+
|
|
56
64
|
Left-side modal/menu helpers:
|
|
57
65
|
```
|
|
58
66
|
LeftModal({arr, zIndex})
|
|
@@ -229,7 +237,7 @@ Semantics that are easy to get wrong:
|
|
|
229
237
|
(setting columnDefs resets grid order). `setPresentGate(keys|null)` is for stable-schema app modes:
|
|
230
238
|
it marks gated-out buttons disabled and hides those grid columns through applyColumnState without
|
|
231
239
|
mutating persisted `visible`. No loop: applyColumnState never adds/removes columns. Null actual presence
|
|
232
|
-
and null gate together mean all present; if a gate is set, grid-less consumers still see that gate.
|
|
240
|
+
and null gate together mean all present; if a gate is set, grid-less consumers still see that gate.
|
|
233
241
|
- `listSource` = the `{order, visible}` slice exposed as a `UiListSource` (Toolbar's
|
|
234
242
|
external-config contract): plug it into `createToolbar({source})` and the toolbar, the icon menu
|
|
235
243
|
and the grid all mirror one config. Its setConfig MERGES visible and re-appends via normalize, so
|
|
@@ -244,6 +252,29 @@ Semantics that are easy to get wrong:
|
|
|
244
252
|
stand: hot-reload recreates a module-level controller while the live grid stays attached to the
|
|
245
253
|
OLD instance, so store->grid needs an F5; in a real app the module runs once.
|
|
246
254
|
|
|
255
|
+
High-level kit (`createColumnGrid`, `grid/columnState/columnGrid.tsx`):
|
|
256
|
+
```
|
|
257
|
+
createColumnGrid({key, columnDefs?, columns?, data?, getId?, def?, saveMs?, toolbar?, autoSizeOnColumnCountChange?})
|
|
258
|
+
-> {state, toolbar, api, grid, tableProps, Table, Menu, Dots, Cards, Toolbar, Settings, View}
|
|
259
|
+
useColumnGrid(opts) -> same controller, captured once for component-local setups
|
|
260
|
+
```
|
|
261
|
+
- This is the default convenience layer for ordinary app grids that all need the same column menu.
|
|
262
|
+
It infers `ColumnMeta` from leaf `ColDef`s (`colId` -> `field`, title from `headerName` -> key)
|
|
263
|
+
and merges optional `columns` overrides for `fixed`, `short`, `icon`, `defaultVisible`, `cardRole`,
|
|
264
|
+
and group semantics. Unknown explicit overrides are appended, so grid-less/card-only fields still work.
|
|
265
|
+
- `Table` and `tableProps()` wrap `AgGridTable` and wire `state.grid.attach/detach` automatically.
|
|
266
|
+
Their default `autoSizeColumns=false` is deliberate: the wrapper is for persisted column layout, so
|
|
267
|
+
auto-fit must be opt-in. `autoSizeOnColumnCountChange` is a narrower opt-in: it calls
|
|
268
|
+
`sizeColumnsToFit()` only when `visibleKeys().length` changes after a columnState edit.
|
|
269
|
+
- `View` is a small representation switch (`mode:'table'|'cards'`) with controls
|
|
270
|
+
(`'menu'|'dots'|'toolbar'|false|'auto'`). `auto` resolves to the built-in dots overlay for both
|
|
271
|
+
table and card mode; the wrapper sets dot max to the full column count unless overridden. `Dots` is
|
|
272
|
+
not card-specific; on table mode it edits the same config and the attached grid applies visibility,
|
|
273
|
+
order, and sort through columnState. `data`/`getId` can live on the controller defaults or be
|
|
274
|
+
passed per `View` render.
|
|
275
|
+
- Use raw `createColumnState` plus `createToolbar` when the app needs a custom skin like QA card 30,
|
|
276
|
+
a runtime `presentGate`, special group mode buttons, or nonstandard persistence timing.
|
|
277
|
+
|
|
247
278
|
Icon menu (ColumnsMenu / MenuStrip, `grid/columnState/ColumnsMenu.tsx`):
|
|
248
279
|
```
|
|
249
280
|
<MenuStrip items={MenuStripItem[]} onItem? onMove? move? tail? holdMs?=150 compact? />
|
|
@@ -284,7 +315,7 @@ Mobile (no ag-grid, no storage - the config alone):
|
|
|
284
315
|
as dots move), `cardRole:'title'` = the header (else the first visible key), `cardRole:'accent'`
|
|
285
316
|
= a badge, the rest are label+value. It sorts by the sticky `config.sort` itself
|
|
286
317
|
(numeric/locale comparator) even when the sorted column is hidden.
|
|
287
|
-
Styling uses shared classes `.wenayColDots*` / `.wenayCardList*`; runtime geometry remains inline because dot positions and drag transforms are state-derived. QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over columnState.listSource + pseudo-icons).
|
|
318
|
+
Styling uses shared classes `.wenayColDots*` / `.wenayCardList*`; runtime geometry remains inline because dot positions and drag transforms are state-derived. QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over columnState.listSource + pseudo-icons), 32 (createColumnGrid wrapper + dots driving table/cards).
|
|
288
319
|
|
|
289
320
|
## Outside / Buttons Compatibility
|
|
290
321
|
```
|
|
@@ -297,6 +328,10 @@ StyleOtherColumn
|
|
|
297
328
|
|
|
298
329
|
`Button`, `OutsideButton`, `HoverButton`, and `AbsoluteButton` are still direct components rather than hook controllers.
|
|
299
330
|
|
|
331
|
+
`Button` persists its open/closed status under `keyForSave` (module-lifetime; same naming as
|
|
332
|
+
FloatingWindow/Resizable/RightMenu); the old `keySave` prop is a deprecated alias (`keyForSave`
|
|
333
|
+
wins when both are set).
|
|
334
|
+
|
|
300
335
|
## Drag / Resize Low Level
|
|
301
336
|
```
|
|
302
337
|
FloatingWindowBase(props) // lower-level react-rnd wrapper
|
|
@@ -306,8 +341,8 @@ floatingWindowMap // persisted RND map (ObservableMap)
|
|
|
306
341
|
FloatingWindowUpdate
|
|
307
342
|
FloatingWindowProps / FloatingWindowController / FloatingWindowSavedGeometry
|
|
308
343
|
|
|
309
|
-
DragBox(props) //
|
|
310
|
-
DragArea(props) //
|
|
344
|
+
DragBox(props) // delta-drag component; thin adapter over useDraggableApi (A7)
|
|
345
|
+
DragArea(props) // @deprecated: unique semantics kept as-is; prefer useDraggableApi/DragBox
|
|
311
346
|
FResizableReact(props)
|
|
312
347
|
mapResiReact // persisted resize map (ObservableMap)
|
|
313
348
|
OutlineDragDemo()
|
|
@@ -321,6 +356,18 @@ const drag = useDraggableApi(...)
|
|
|
321
356
|
|
|
322
357
|
`useDraggable(...)` is kept as the simple hook wrapper; `useDraggableApi(...)` is the controller-style shape.
|
|
323
358
|
|
|
359
|
+
Drag primitives after the A7 pass (2026-07-10): `DragBox` is a thin adapter over
|
|
360
|
+
`useDraggableApi({holdMs: 0, trackState: false, onMove})` - same observable contract as its old
|
|
361
|
+
bespoke loop (immediate start, per-tick imperative `onX`/`onY` with the delta from the press
|
|
362
|
+
point, no re-render per move tick, `onStop` only after a real gesture, `last` ref shares the live
|
|
363
|
+
position object; pinned by `__test/dragBox.test.tsx`, QA card 35). `DragArea` stays untouched and
|
|
364
|
+
`@deprecated`: no consumers, and its semantics are unique (document.body listeners,
|
|
365
|
+
`stopPropagation` per move tick, ABSOLUTE coords, no preventDefault) - re-basing it would change
|
|
366
|
+
observable behavior; removal only in a breaking version. `useFloatingWindowController`'s header
|
|
367
|
+
loop and `StickerMenu` deliberately keep their own loops: viewport clamp + `e.buttons===1` release
|
|
368
|
+
recovery + persistence/z-stack, and mount-lifetime listeners + click-vs-drag suppression
|
|
369
|
+
respectively, are load-bearing behavior `useDraggableApi` does not model.
|
|
370
|
+
|
|
324
371
|
Reorder-by-drag (useReorder, `hooks/useReorder.tsx` - extracted from the Toolbar editor, which is
|
|
325
372
|
its first consumer):
|
|
326
373
|
```
|
|
@@ -519,6 +566,10 @@ Semantics that are easy to get wrong:
|
|
|
519
566
|
- `useReplayHistory.seek` kills the live subscription imperatively (guarded off, double-call safe), folds `history.at(where)` through `apply` (keyframe + tail; a keyframe fully redefines consumer state, so `reset` is rarely needed), and freezes. `play()` resubscribes `{since: pos}` -> archive tail -> live handover.
|
|
520
567
|
- `useStoreReplayEach` composes existing pieces instead of calling `Observe.syncStoreReplayEach` (which builds a FRESH store per call): the mirror lives in a ref (like `useStoreReplayMirror`), `store.each()` is subscribed in an effect declared BEFORE the sync effect (hook-call order guarantees the per-key subscriber exists when the keyframe applies — the expansion is never missed), then `useStoreReplaySync` drives the wire. Consequences: within one mount every resubscribe (StrictMode, `restart()`, `enabled` toggle, `staleMs`/`policy` change) reconnects by tail ON TOP of kept state and the consumer sees only the diff — no snapshot/`initial` dance (the library one-call needs `{since, initial: snapshot}` for that). After a FULL unmount pass `{since: prev.seq(), initial: prev.store.snapshot()}` like the library contract. `drain` is creation-time (it goes to `createStore`); changing it later does nothing. The fold target must live outside React state (ref/Map/grid api) — the hook renders nothing; `controller.store` is a normal store for `useStoreNode`/`useStoreKeys` extras.
|
|
521
568
|
- Server primitives (`conflateReplay` per connection, `archiveReplay`) intentionally have no hooks: they live where the RPC server is built.
|
|
569
|
+
- Route coordinator (common2 1.0.67 `Replay.createRouteCoordinator`): the coordinator owns promotion/fallback/shadow via policy hooks; consumers subscribe on a pair `link`, whose handle matches our route hooks (`ready, seq(), label(), active()`) minus `switchRoute` — the transport switch is invisible by the seq contract. Our route hooks (`useReplayRouteSubscribe` etc.) remain the manual-`switchRoute` wrapper over `replayRouteSubscribe`; they do NOT sit on top of a coordinator. React surface for coordinator links (subscribe hook + route-state/`promoteDirect` controls for ops UI) is a target-backlog item — do not bend the existing route hooks to accept links implicitly.
|
|
570
|
+
- WebRTC direct path (common2 1.0.68): `createWebRtcConnector({port, rtc, ...})` is the coordinator's direct `RouteConnector`; `rtc` is an injected factory and in the browser that injection (`() => new RTCPeerConnection(cfg)`) belongs to the app/React side — common2 stays lib.dom-free. Signaling rides the EXISTING rpc socket (`createSignalHub` port exposed by `createRpcServerAuto`), so no new connection management appears in React; channel death / revoke surfaces as loud `onError` on the line (never silence), which the existing hooks already map to `error`. Nothing here needs a hook by itself — it matters as the direct transport under a future adapter (target backlog).
|
|
571
|
+
- Peer SDK (common2 1.0.69 `Peer` / `wenay-common2/peer`): the one-call layer over rpc + store + replay + route coordinator — `createPeerClient({remote, account, initial, rtc?})` publishes the own store as a patch line and `peer(account)` returns a live mirrored store + route control; relay and direct share the OWNER's seq space (`createPatchRelayJournal`), so hand-offs are plain seq resumes, no keyframe reset. This is the intended React adoption surface (a `usePeer` adapter is the target-backlog item, superseding the raw coordinator-link hook idea): the mirrored store already plugs into `useStoreNode`/`useStoreKeys` as-is. 1.0.70 ships the oracle suites + `demo/` stand in the package — living examples per subsystem (`replay/peer-sdk.test.ts` is the Peer contract).
|
|
572
|
+
- Media capture (common2 1.0.66 `Media` namespace / `wenay-common2/media`): CONSUMPTION needs no new hooks — `Media.createAudioSource` / `Media.createVideoSource` return `[emit, listen] & control`, with `replay:true` the `listen` is a normal replay line, so `useReplaySubscribe` / `useReplayFrame` cover it; frames are one `Uint8Array` (40-byte fixed header + raw payload, `Media.decodeMediaFrame`) and follow the existing fold-outside-React rule (canvas/AudioContext via ref). The capture `control` side (`start()` resolving to `'idle'|'requesting'|'live'|'denied'|'no-device'|'error'`, `stop()`, `setDevice`, `listDevices`, stats) is a permission/device/lifecycle state machine — exactly hook-shaped; a `useMediaSource` capture hook + QA card is a recorded target goal (supersedes the earlier "wrap it app-locally" stance). Backpressure defaults live in common2 (audio = lossless queue, video `replay:true` = keep-latest frame recovery); the client-side knobs are the same `policy`/`hint` as any replay line.
|
|
522
573
|
|
|
523
574
|
QA cards 23/24/25/26 (`testUseReact/replayVideo.tsx`, all in-proc): synthetic 10fps jpeg-frame producer on `Replay.replayListen({history, current})`; client A = direct `exposeReplay` remote; client B = simulated slow wire (1 envelope per rateMs) behind `conflateReplay({pending: () => buf.length, highWater: 4, lowWater: 1, keyOf: () => "frame"})`; client C = `archiveReplay` + `openHistory` scrubber; client D = freshness (`staleMs: 2000`, `React.memo` + no tick, mounted inside a local `<StrictMode>`; the flat renders counter under growing frames is the no-per-event-render proof; "stall producer" toggles the emit interval, "new client" remounts by key for the stalled-mount case; card 24 has the same via `staleMs: 2500` on the mirror); client E = pull path (`useReplayFrame` over the direct remote with a wrapped counting `frame()`, pace switch 250ms/1s/3s keeps seq). `window.__replayVideoDemo` is exposed for debugging (wire.setRateMs, stats). Node-verified: slow wire delivered 12/36 envelopes yet converged to the last frame with bounded buffer (coalesced tail recovery); `syncStoreReplay` off() freezes the mirror and `{since}` resubscribe catches up by tail. Card 25 = per-key feed (`useStoreReplayEach` over `exposeStoreReplay`): a dict-of-rows store, producer touches ONE random row per tick, the fold target is a plain Map with per-row cb counters — only the mutated row's counter grows, keyframe/`replace` are the only whole-table expansions, delete arrives as `(key, undefined)`. Card 26 = route hand-off (`useReplayRouteSubscribe` over the same video line): one canvas starts on relay, switches direct/relay by `switchRoute`, and failed replacement keeps the previous route alive. Browser QA of throttling-sensitive behavior needs a VISIBLE tab: hidden-tab timer/effect throttling stalls the producer and delays passive effects (known stand caveat).
|
|
524
575
|
|
|
@@ -555,6 +606,18 @@ MainPage()
|
|
|
555
606
|
|
|
556
607
|
The context logger is a larger UI surface; the global `logsApi` is still the shorter integration point. `createLogsController` is the headless layer for append/limit/settings state; `useMessageEventLogsController` owns the global notification queue/timers/settings, while `MessageEventLogsView` and `MessageEventLogCard` own rendering. `PageLogs`, `MessageEventLogs`, and `LogsPage` remain compatibility UI wrappers. `useLogsTableController` and `useLogsNotificationsController` expose the provider-local table/notification state while `LogsTable` and `LogsNotifications` keep the visual wrappers. Shared logger chrome lives in `src/common/src/logs/logStyles.ts` and is themed through `--logs-*` tokens.
|
|
557
608
|
|
|
609
|
+
Full-page table controller (`useLogsPageTable` -> `LogsPageTableController`): the last logs table
|
|
610
|
+
moved to the controller pattern. Semantics that matter: the grid receives a MOUNT-TIME snapshot of
|
|
611
|
+
the accumulated log map once (`useState` initializer) and afterwards lives on
|
|
612
|
+
`applyTransactionAsync` appends from the mini feed - re-renders do not re-feed `rowData`, so row
|
|
613
|
+
identity is per-append copy exactly as before. The importance filter is ONE method
|
|
614
|
+
(`applyImportanceFilter(min?)`: set -> `setFilterModel` greaterThanOrEqual on `var`, unset ->
|
|
615
|
+
`destroyFilter`), used by both the settings subscription and `onGridReady` (which skips the
|
|
616
|
+
destroy branch - a fresh grid has no filter). Both data subscriptions are `updateBy(obj, cb)`
|
|
617
|
+
WITH a callback: they run imperatively on `renderBy` and never re-render the component.
|
|
618
|
+
`gridProps` is one stable memo - spread it into `AgGridTable`; `fit()` is exposed for the legacy
|
|
619
|
+
`update` prop, which `PageLogs` still honors. QA card 9.
|
|
620
|
+
|
|
558
621
|
## Cache / Memory / Browser Utilities
|
|
559
622
|
```
|
|
560
623
|
browserCacheStorage
|
|
@@ -577,6 +640,9 @@ memoryUpdate(key, mutate)
|
|
|
577
640
|
memoryMarkDirty(key)
|
|
578
641
|
createSearchHistory({key, max?})
|
|
579
642
|
deepMergeWithMap(target, source)
|
|
643
|
+
structEqual(a, b) // deep equality for plain data trees; JSON.stringify-idiom
|
|
644
|
+
// tolerances (undefined props absent, NaN==NaN) WITHOUT
|
|
645
|
+
// its key-order sensitivity; not for Maps/Sets/cycles
|
|
580
646
|
memoryCache
|
|
581
647
|
memoryMaps
|
|
582
648
|
|
|
@@ -584,9 +650,27 @@ ArrayPromise({arr, catchF?, thenF?})
|
|
|
584
650
|
PageVisibilityProvider
|
|
585
651
|
PageVisibilityContext
|
|
586
652
|
setAutoStepForElement(input, {minStep?, maxStep?})
|
|
653
|
+
|
|
654
|
+
useCacheMapPersistence(cache: CacheMap, delay?=300) -> {isDirty(), flush(), save(), reload()}
|
|
587
655
|
```
|
|
588
656
|
|
|
589
|
-
|
|
657
|
+
Layering (2026-07 architecture pass): the persisted maps (`floatingWindowMap`, `mapResiReact`,
|
|
658
|
+
`mapRightMenu`) are DECLARED in the utils leaf `utils/persistedMaps.ts` and re-exported by their
|
|
659
|
+
owning components (FloatingWindow/Resizable/RightMenuStore) - importing `memoryStore`/`memoryCache`
|
|
660
|
+
no longer pulls react-rnd or the Menu tree; only `import type` lines point upward (erased at build).
|
|
661
|
+
Shared order helper `utils/fixedOrder.ts` (`pinFixedOrder`, `movedOrderWithFixed`) is the single
|
|
662
|
+
implementation of the "fixed entries pin to descriptor index" invariant used by columnState,
|
|
663
|
+
ColumnsMenu, and Toolbar (it used to be four inline copies that had to stay byte-identical).
|
|
664
|
+
|
|
665
|
+
Cache helpers are process/browser storage utilities, not React state management. The one React
|
|
666
|
+
entry here is `useCacheMapPersistence` - the documented app contract (`load()` on mount +
|
|
667
|
+
`onDirty -> saveDebounced(delay)`) as a hook. Scope is deliberately exactly that: the
|
|
668
|
+
pagehide/visibilitychange flush backstops stay app-side (they are page-level policy, not
|
|
669
|
+
per-component). Effect identity is `[cache, delay]`; the returned methods are pass-throughs and
|
|
670
|
+
`reload()` is an alias of `load()` - a repeated load MERGES storage on top of the current maps
|
|
671
|
+
(addDataToMap semantics), it is not a reset. QA cards 20/21/25 share one `memoryCache`, so
|
|
672
|
+
several mounted hooks coexist: load() is idempotent-enough (merge) and extra onDirty
|
|
673
|
+
subscribers just debounce the same save.
|
|
590
674
|
|
|
591
675
|
Dirty/save contract: the dirty signal originates in the data layer. The persisted maps are
|
|
592
676
|
`ObservableMap`s, so `set/delete/clear` announce themselves; in-place mutations of stored
|
|
@@ -612,9 +696,20 @@ CResizeObserver
|
|
|
612
696
|
setResizeableElement(el)
|
|
613
697
|
removeResizeableElement(el)
|
|
614
698
|
ObserveID
|
|
699
|
+
|
|
700
|
+
useResizeObserver<T>(onResize) -> {ref, element()}
|
|
701
|
+
useElementSize<T>() -> {ref, element(), width, height, getSize()}
|
|
615
702
|
```
|
|
616
703
|
|
|
617
|
-
Use
|
|
704
|
+
Use the class/imperative pair only when a component must participate in the shared resize
|
|
705
|
+
observer map. The hooks are the React-shaped entry over the SAME module singleton (one native
|
|
706
|
+
`ResizeObserver` per app): `useResizeObserver` returns a callback ref (attach/detach re-observes;
|
|
707
|
+
React null-call on unmount unsubscribes) and takes `onResize` through a ref - new identity
|
|
708
|
+
neither resubscribes nor is missed. The native observer fires once right after `observe`, so the
|
|
709
|
+
first measurement arrives without an explicit initial read. `useElementSize` folds that into
|
|
710
|
+
rounded, equality-guarded `{width, height}` state (a no-op resize does not re-render) plus a live
|
|
711
|
+
`getSize()` getter (exact floats, no render wait). `setResizeableElement`'s auto-shrink path is
|
|
712
|
+
untouched and still class-driven (ParamsEditor inputs). QA card 19 drives both hooks.
|
|
618
713
|
|
|
619
714
|
## Styles
|
|
620
715
|
```
|
|
@@ -640,9 +735,9 @@ Current tokenized prefixes:
|
|
|
640
735
|
- `--wnd-*` for `FloatingWindow` chrome.
|
|
641
736
|
- `--dlg-*` for `SettingsDialog` chrome.
|
|
642
737
|
- `--tb-*` for toolbar chrome.
|
|
643
|
-
- `--logs-*` for global/context logger chrome.
|
|
644
|
-
- `--cols-menu-*` for compact `ColumnsMenu/MenuStrip` chrome.
|
|
645
|
-
- `--cols-dots-*` for `ColumnDots` chrome.
|
|
738
|
+
- `--logs-*` for global/context logger chrome.
|
|
739
|
+
- `--cols-menu-*` for compact `ColumnsMenu/MenuStrip` chrome.
|
|
740
|
+
- `--cols-dots-*` for `ColumnDots` chrome.
|
|
646
741
|
- `--cols-card-*` for `CardList` chrome.
|
|
647
742
|
- `--wenay-z-modal` for modal/overlay stacking.
|
|
648
743
|
|
|
@@ -650,11 +745,11 @@ Normalization rule: new shared CSS should first try an existing token. Add a new
|
|
|
650
745
|
|
|
651
746
|
Open normalization candidates:
|
|
652
747
|
- `src/style/style.css`: `.msTradeAlt`, `.msTradeActive`, `.newButtonSimple`, `.toIndicatorMenuButton:hover`, submit-button green, and several toolbar row hover/drag literals still use raw colors.
|
|
653
|
-
- `src/common/src/grid/columnState/*`: compact menu/dots/card visuals now use `.wenayColsMenu*`, `.wenayColDots*`, `.wenayCardList*` plus `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`; further changes here should be visual QA only, not a new default palette.
|
|
748
|
+
- `src/common/src/grid/columnState/*`: compact menu/dots/card visuals now use `.wenayColumnGrid*`, `.wenayColsMenu*`, `.wenayColDots*`, `.wenayCardList*` plus `--cols-grid-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`; further changes here should be visual QA only, not a new default palette.
|
|
654
749
|
- `src/common/src/components/ParamsEditor.tsx` and `src/common/src/components/Input.tsx`: if these stay public primitives, define default class/token contracts instead of component-owned visual styling.
|
|
655
750
|
- `src/common/src/styles/commentaryStyles.css`: standalone `.commentary` CSS is not imported by the root style bundle; either import/tokenize it if still used, or mark it as a local component concern.
|
|
656
751
|
|
|
657
|
-
Recently normalized: mouse context-menu item colors through `--menu-*`, `--menu-outline-color` for `OutlineDragDemo`, `--logs-*` for logger chrome, `--dlg-scrim` in `ModalProvider`, compact `ColumnsMenu/MenuStrip` visuals through `.wenayColsMenu*` / `--cols-menu-*`,
|
|
752
|
+
Recently normalized: mouse context-menu item colors through `--menu-*`, `--menu-outline-color` for `OutlineDragDemo`, `--logs-*` for logger chrome, `--dlg-scrim` in `ModalProvider`, compact `ColumnsMenu/MenuStrip` visuals through `.wenayColsMenu*` / `--cols-menu-*`, card-29 mobile primitives through `--cols-dots-*` / `--cols-card-*`, and createColumnGrid overlay through `.wenayColumnGrid*` / `--cols-grid-*`.
|
|
658
753
|
|
|
659
754
|
## Cleanup Inventory
|
|
660
755
|
|
|
@@ -668,6 +763,16 @@ Suspicious but still public:
|
|
|
668
763
|
- `src/common/src/myChart/chartEngine/chartEngine.ts` is a reference copy next to the public React engine. It is not exported from root; treat it as suspicious maintenance debt, not as public API.
|
|
669
764
|
- `StyleCSSHeadGridEdit` and `StyleCSSHeadGrid` mutate `<head>` directly. They are exported and can have consumers, but new grid styling should prefer ag-grid theme params and tokens.
|
|
670
765
|
|
|
766
|
+
Added by the 2026-07-09 architecture audit (evidence in the audit report / `doc/target/my.md`):
|
|
767
|
+
- `menuR.tsx` (`createRightClickMenu` / `MenuR`) - CONFIRMED dead: no runtime consumer anywhere (the test that supposedly covers it imports `RightMenu.tsx`); re-exported twice from `api.tsx` (`export *` + `kit.menu.rightClick`). Candidate for removal in a deliberate breaking version; its gesture code duplicates `menuMouse.tsx:342-380`.
|
|
768
|
+
- `logsContext.tsx` - a full PARALLEL logs stack (own `LogEntry` shape divergent from `logsController`, raw unguarded `localStorage`, own settings/notifications/table) exported from the barrel next to `logs.tsx`. Decide: deprecate or converge; new code should use `logsApi`/`createLogsController`.
|
|
769
|
+
- `MyChartEngine` - a hardcoded DEMO (random data + setInterval inside `useEffect`, no props but `style`) exported as public API from `chartEngineReact.tsx`; `generateIncrementalData` (Math.random demo util) is public too. Should move to demo namespace or gain a real props contract.
|
|
770
|
+
- ~~`getLogsApi<T>()` shared module state~~ FIXED (A2, v1.0.41): every call now builds its own map/mini/settings (optional `settingsKey` persists per instance); the global `logsApi` explicitly injects the legacy shared state.
|
|
771
|
+
- ~~`FloatingWindow` `onCLickClose` typo~~ ALIASED (A10, v1.0.42): `onClickClose` added, typo prop is a deprecated alias; REMOVAL of the alias still needs a breaking pass.
|
|
772
|
+
- `DragArea` - `@deprecated` (A7, v1.0.42): no consumers; unique semantics kept as-is (see Drag / Resize Low Level). Removal in a deliberate breaking version.
|
|
773
|
+
- `Button` `keySave` prop - deprecated alias of `keyForSave` (A10, v1.0.42); removal in a breaking pass.
|
|
774
|
+
- `contextMenu` raw `map`/legacy-queue path is test-only-live (populated only by `contextMenuStats.test.tsx`); the `bb`/`map.clear()` reopening invariant itself is intentional and stays.
|
|
775
|
+
|
|
671
776
|
## Charts
|
|
672
777
|
Canvas chart:
|
|
673
778
|
```
|
|
@@ -699,4 +804,4 @@ removed old grid update names -> applyGridRows / agGrid4
|
|
|
699
804
|
```
|
|
700
805
|
|
|
701
806
|
Do not add new generic utilities with app words in their signature. For example, a column primitive should accept
|
|
702
|
-
`names` and `apply`, while a product wrapper decides group ids, column ids, and labels.
|
|
807
|
+
`names` and `apply`, while a product wrapper decides group ids, column ids, and labels.
|
package/doc/wenay-react2.md
CHANGED
|
@@ -21,9 +21,11 @@ Put app-specific layout/build rules in an app wrapper above the primitive.
|
|
|
21
21
|
## Render Memory
|
|
22
22
|
```
|
|
23
23
|
updateBy(obj) / useUpdateBy(obj) // subscribe current component to renderBy(obj)
|
|
24
|
+
updateBy(obj, cb) // imperative callback instead of a re-render;
|
|
25
|
+
// cb goes through a ref - inline identity is fine
|
|
24
26
|
renderBy(obj, ms?) // emit render for subscribers
|
|
25
|
-
renderByRevers(obj, ms?, reverse?=true)
|
|
26
|
-
renderByLast(obj, ms?)
|
|
27
|
+
renderByRevers(obj, ms?, reverse?=true) // reverse/last affect ONLY React subscribers;
|
|
28
|
+
renderByLast(obj, ms?) // updateBy(obj, cb) callbacks always all run first
|
|
27
29
|
|
|
28
30
|
const state = { count: 0 }
|
|
29
31
|
const api = createUpdateApi(state)
|
|
@@ -63,6 +65,13 @@ document.addEventListener("visibilitychange",
|
|
|
63
65
|
window.addEventListener("pagehide", () => { void memoryCache.flush() }) // backstop: flush is async
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
The load + dirty->saveDebounced part of that contract as one hook (React components):
|
|
69
|
+
```
|
|
70
|
+
const persistence = useCacheMapPersistence(memoryCache, delayMs?=300)
|
|
71
|
+
persistence.isDirty(); persistence.flush(); persistence.save(); persistence.reload() // reload = load() alias
|
|
72
|
+
```
|
|
73
|
+
The pagehide/visibility flush backstops stay app-side (see above) - the hook deliberately does not own them.
|
|
74
|
+
|
|
66
75
|
## Outside Click / Buttons
|
|
67
76
|
```
|
|
68
77
|
const outside = useOutside({onOutside, enabled?})
|
|
@@ -73,19 +82,35 @@ outside.contains(event.target)
|
|
|
73
82
|
|
|
74
83
|
<OutsideClickArea outsideClick={close} status={open}>...</OutsideClickArea>
|
|
75
84
|
|
|
76
|
-
<Button button={<button>Open</button>} outClick
|
|
85
|
+
<Button button={<button>Open</button>} outClick keyForSave?>{...}</Button> // keySave = deprecated alias
|
|
77
86
|
<OutsideButton button={...}>{...}</OutsideButton>
|
|
78
87
|
<HoverButton button={...}>{...}</HoverButton>
|
|
79
88
|
<AbsoluteButton button={...}>{...}</AbsoluteButton>
|
|
80
89
|
```
|
|
81
90
|
|
|
91
|
+
## Element Size / Resize Observer
|
|
92
|
+
```
|
|
93
|
+
const box = useResizeObserver<HTMLDivElement>(() => onResize()) // shared singleton observer
|
|
94
|
+
<div ref={box.ref} />; box.element()
|
|
95
|
+
|
|
96
|
+
const size = useElementSize<HTMLDivElement>() // "want the size -> get the value/method"
|
|
97
|
+
<div ref={size.ref} />
|
|
98
|
+
size.width; size.height // state, rounded, no-op resize does not re-render
|
|
99
|
+
size.getSize() // live getter (exact, no render wait)
|
|
100
|
+
|
|
101
|
+
setResizeableElement(el) / removeResizeableElement(el) // legacy imperative auto-shrink path, unchanged
|
|
102
|
+
```
|
|
103
|
+
Both hooks ride the same module-level `CResizeObserver` singleton (one native `ResizeObserver` for the whole app). QA card 19.
|
|
104
|
+
|
|
82
105
|
## Drag / Floating Windows
|
|
83
106
|
```
|
|
84
|
-
const drag = useDraggableApi({initialPosition, holdMs?, onDragStart?, onDragEnd?})
|
|
107
|
+
const drag = useDraggableApi({initialPosition, holdMs?, onDragStart?, onDragEnd?, onMove?, trackState?})
|
|
85
108
|
<div {...drag.bind} style={{transform: `translate(${drag.position.x}px, ${drag.position.y}px)`}} />
|
|
86
109
|
drag.setPosition({x, y})
|
|
87
110
|
drag.resetPosition()
|
|
88
111
|
drag.cancelDrag()
|
|
112
|
+
// imperative path: onMove(p) fires per move tick (through a ref); trackState:false stops
|
|
113
|
+
// per-tick re-renders - position lives only in drag.positionRef/onMove (DragBox is this shape)
|
|
89
114
|
|
|
90
115
|
const r = useReorder({order, commit, move?, canDrag?, preview?, holdMs?}) // mini reorder-by-drag
|
|
91
116
|
<div ref={r.listRef}>{order.map(k => { // children 1:1 with order
|
|
@@ -99,7 +124,8 @@ const b = useReorderBoard({columns: [{key, items}], commit, // d
|
|
|
99
124
|
b.over // {col, index} | null - live target
|
|
100
125
|
// column gravity is YOUR CSS: justify-content flex-start packs up, flex-end packs down
|
|
101
126
|
|
|
102
|
-
<FloatingWindow keyForSave="tool" size={{width: 320, height: 240}} header={<div>Tool</div>}
|
|
127
|
+
<FloatingWindow keyForSave="tool" size={{width: 320, height: 240}} header={<div>Tool</div>}
|
|
128
|
+
onClickClose={() => setOpen(false)}> {/* onCLickClose (typo) = deprecated alias */}
|
|
103
129
|
<Panel />
|
|
104
130
|
</FloatingWindow>
|
|
105
131
|
|
|
@@ -156,6 +182,7 @@ tb.api.useConfig() / getConfig() / setConfig(next) / setOrder(order) / show(key,
|
|
|
156
182
|
tb.api.showSettings(on) / showReset(on) // pseudo-controls visibility
|
|
157
183
|
tb.api.useItems() // headless bar: ordered visible [{item, density, content}] - custom markup
|
|
158
184
|
tb.api.onChange.on(cfg => ...) -> off // fires on every edit
|
|
185
|
+
tb.api.dispose() // release the external-source subscription (HMR/remounts); persisted config stays
|
|
159
186
|
|
|
160
187
|
registerToolbarDensity({key, name, renderItem?}) -> unregister // built-ins: 'icon', 'label'
|
|
161
188
|
toolbarItemIcon(item) // the item's icon, or its first letters as a text pseudo-icon
|
|
@@ -282,7 +309,7 @@ cs.api.show(key, on) / move(order) / setSort({key, dir} | null) / toggleSort(key
|
|
|
282
309
|
cs.api.visibleKeys() // keys to render, in order (group-gated)
|
|
283
310
|
cs.api.onChange.on(cfg => ...) -> off
|
|
284
311
|
cs.api.usePresent() / isPresent(key) / setPresent(keys | null) // live-grid presence
|
|
285
|
-
cs.api.getPresentGate() / setPresentGate(keys | null) // app runtime availability gate, not persisted
|
|
312
|
+
cs.api.getPresentGate() / setPresentGate(keys | null) // app runtime availability gate, not persisted
|
|
286
313
|
cs.api.listSource // {order, visible} slice as a Toolbar `source`
|
|
287
314
|
```
|
|
288
315
|
Config `{v, order, visible, width, sort, filter, groups}` is serializable and persisted like
|
|
@@ -317,7 +344,34 @@ Mobile (no ag-grid): dots selector + card rows over the same config:
|
|
|
317
344
|
// drag=replace, swipe up=remove, tap dot=select, sort button cycles
|
|
318
345
|
<CardList<Row> state={cs} data={rows} getId? renderValue? /> // visible cols -> card fields; cardRole:'title'/'accent'
|
|
319
346
|
```
|
|
320
|
-
|
|
347
|
+
|
|
348
|
+
Default wrapper for repeated grid/menu/mobile use:
|
|
349
|
+
```
|
|
350
|
+
const cg = createColumnGrid<Row>({
|
|
351
|
+
key: "orders.columns",
|
|
352
|
+
columnDefs: cols, // ColumnMeta inferred from colId/field/headerName
|
|
353
|
+
data: rows, // optional default; View props can override
|
|
354
|
+
getId: r => r.id,
|
|
355
|
+
autoSizeOnColumnCountChange: true, // optional fit when visible column count changes
|
|
356
|
+
columns: [{key: "name", fixed: true, cardRole: "title"}], // optional overrides
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
<cg.View mode="table" /> // default controls="auto" -> dots overlay
|
|
360
|
+
<cg.View mode="cards" data={otherRows} />
|
|
361
|
+
<cg.Table rowData={rows} getRowId={p => p.data.id} />
|
|
362
|
+
<cg.Menu compact />
|
|
363
|
+
<cg.Dots /> // no wrapper max; defaults to all columns
|
|
364
|
+
<cg.Toolbar settings />
|
|
365
|
+
<cg.Settings />
|
|
366
|
+
```
|
|
367
|
+
`createColumnGrid` returns `{state, toolbar, api, grid, tableProps, Table, Menu, Dots, Cards, Toolbar, Settings, View, dispose}`; `dispose()` releases factory-lifetime subscriptions (config onChange, toolbar source, pending fit) without touching the persisted config. The columnState BARREL stays ag-grid-free; `createColumnGrid` ships from its own module (both are exported from the package root).
|
|
368
|
+
Its `Table`/`tableProps()` attach/detach the columnState grid adapter automatically and default
|
|
369
|
+
`autoSizeColumns=false` so restored widths are not overwritten. `autoSizeOnColumnCountChange` is
|
|
370
|
+
separate and only calls `sizeColumnsToFit()` when the visible column count changes. `useColumnGrid(opts)`
|
|
371
|
+
is the same controller captured once for component-local setups; keep module-level `createColumnGrid`
|
|
372
|
+
when the controller must survive route/component remounts.
|
|
373
|
+
|
|
374
|
+
QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over the same config), 32 (createColumnGrid wrapper + dots driving table/cards).
|
|
321
375
|
|
|
322
376
|
|
|
323
377
|
## Observe React Adapter
|
|
@@ -416,6 +470,10 @@ const tt = useReplayHistory(history, (frame) => draw(frame), {head: () => replay
|
|
|
416
470
|
tt.live; tt.seq; tt.head; tt.pause(); tt.play(); tt.seek({seq})
|
|
417
471
|
```
|
|
418
472
|
|
|
473
|
+
Route hand-off here is the MANUAL surface (`switchRoute`). common2 1.0.67 `Replay.createRouteCoordinator` moves route decisions (policy, promote/fallback/shadow) out of the consumer; a coordinator `link.subscribe(cb)` handle has the same shape (`ready, seq(), label(), active()`) and survives every route change, so components consuming a coordinator link do not need `switchRoute` at all. common2 1.0.68 supplies the direct transport for it: `createWebRtcConnector` with an app-injected `rtc: () => new RTCPeerConnection(cfg)` factory (the browser — i.e. the React app — owns that injection) and signaling over the existing RPC socket (`createSignalHub`). common2 1.0.69 wraps the whole stack into the `Peer` SDK (`wenay-common2/peer`): `createPeerClient(...).peer(account)` returns a live mirrored store (works with `useStoreNode`/`useStoreKeys` as-is) + route control, surviving relay<->direct hand-offs in one seq space. A dedicated `usePeer` adapter is a target-backlog item, not yet a library surface.
|
|
474
|
+
|
|
475
|
+
Media lines (common2 1.0.66 `Media.createAudioSource` / `Media.createVideoSource`) are ordinary binary Listen sources; with `replay:true` their `listen` is a replay line, so `useReplaySubscribe` / `useReplayFrame` consume mic/camera frames with no media-specific hook. Each frame is one `Uint8Array` (`Media.decodeMediaFrame`); draw/play it via ref (canvas, AudioContext), never useState — the same rule as any high-frequency line. Without `replay`, the plain `listen` works with the listen hooks above.
|
|
476
|
+
|
|
419
477
|
Contract: `off()` on unmount, StrictMode-safe; seq survives resubscribes inside one mount (keepSeq, default on) — a resubscribe reconnects with `{since}` and gets the journal tail, not a keyframe. Across a FULL unmount/remount keep the position outside via `onSeq` and pass it back as `since`. `seq()` is a getter — high-frequency lines (video frames, ticks) do not re-render per event; draw to canvas via ref, bypassing VDOM. Freshness: detection lives in wenay-common2 (`staleMs` watchdog); the non-route hooks mirror its edge-triggered `onStale` into `stale`, so a fresh 100 ev/s line causes zero extra renders. Route hand-off is explicit through `switchRoute(nextRemote, {label?, since?, reset?, policy?, hint?})`: old route stays live while the replacement catches up by `seq`, then closes. `useReplayHistory` is archive playback — staleness does not apply. QA cards 23 (video line + conflation + time travel + freshness), 24 (store sync), 25 (per-key feed), and 26 (route hand-off) are the live examples.
|
|
420
478
|
|
|
421
479
|
## Logs
|
|
@@ -425,7 +483,9 @@ logsApi.addLogs({id: "api", time: new Date(), txt: "done", var: 1})
|
|
|
425
483
|
<logsApi.React.PageLogs />
|
|
426
484
|
<logsApi.React.Setting />
|
|
427
485
|
|
|
428
|
-
const customLogs = getLogsApi<MyFields>({limitPer: 500, limit?: 50, varMin?: 0})
|
|
486
|
+
const customLogs = getLogsApi<MyFields>({limitPer: 500, limit?: 50, varMin?: 0, settingsKey?: "myLogs"})
|
|
487
|
+
// each getLogsApi call now owns ISOLATED full/mini/settings state (settingsKey persists the
|
|
488
|
+
// instance settings; omitted = fresh non-persisted). The global logsApi keeps the legacy shared state.
|
|
429
489
|
const headless = createLogsController<MyFields>({options: {limitPer: 500, limit: 50}})
|
|
430
490
|
|
|
431
491
|
const messageNotifications = useMessageEventLogsController({maxVisible: 4})
|
|
@@ -437,9 +497,13 @@ const contextNotifications = useLogsNotificationsController()
|
|
|
437
497
|
const mini = useMiniLogsTable({data: rows, onClick: e => console.log(e.data)})
|
|
438
498
|
<AgGridTable {...mini.props} />
|
|
439
499
|
<MiniLogsTable data={rows} />
|
|
500
|
+
|
|
501
|
+
const table = useLogsPageTable() // full-page logs grid controller
|
|
502
|
+
<AgGridTable {...table.gridProps} />
|
|
503
|
+
table.fit(); table.applyImportanceFilter(min?); table.appendRow(row); table.getApi()
|
|
440
504
|
```
|
|
441
505
|
|
|
442
|
-
Logger notification/tabs chrome uses `--logs-*` CSS variables; apps can theme it without forking logger components. `createLogsController` owns the headless append/limit/settings state for custom integrations. `useMessageEventLogsController` owns the global notification queue/timers/settings, `MessageEventLogsView` draws it, and `MessageEventLogs` / `logsApi.React.Message` remain compatibility wrappers. `MiniLogs` remains the compatibility wrapper; new compact-table code can use `useMiniLogsTable` or `MiniLogsTable`.
|
|
506
|
+
Logger notification/tabs chrome uses `--logs-*` CSS variables; apps can theme it without forking logger components. `createLogsController` owns the headless append/limit/settings state for custom integrations. `useMessageEventLogsController` owns the global notification queue/timers/settings, `MessageEventLogsView` draws it, and `MessageEventLogs` / `logsApi.React.Message` remain compatibility wrappers. `MiniLogs` remains the compatibility wrapper; new compact-table code can use `useMiniLogsTable` or `MiniLogsTable`. `useLogsPageTable` owns the full-page table (mount-time row snapshot, transaction appends from the mini feed, one importance-filter method); `PageLogs` / `logsApi.React.PageLogs` remain the thin compatibility wrappers over it.
|
|
443
507
|
|
|
444
508
|
## Styles / Theme
|
|
445
509
|
```
|
|
@@ -455,7 +519,7 @@ useAgGridTheme("dark" | "light")
|
|
|
455
519
|
colDefCentered / colDefWrap / numericComparator
|
|
456
520
|
```
|
|
457
521
|
|
|
458
|
-
Shared CSS variables include `--menu-outline-color`, `--logs-*`, `--dlg-*`, `--wnd-*`, `--tb-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
|
|
522
|
+
Shared CSS variables include `--menu-outline-color`, `--logs-*`, `--dlg-*`, `--wnd-*`, `--tb-*`, `--cols-grid-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
|
|
459
523
|
|
|
460
524
|
## Charts
|
|
461
525
|
```
|
|
@@ -472,4 +536,4 @@ npm run testReact // http://localhost:3010/
|
|
|
472
536
|
|
|
473
537
|
The stand lives in `src/common/testUseReact/qa.tsx`. Use it for visual checks; agGrid4 overlay/dynamic-column demos are dedicated QA cards.
|
|
474
538
|
Two tabs: Active checks (current work) and Verified archive (`#archive`) - verified/fixed cards move
|
|
475
|
-
there and stay runnable for regression re-checks.
|
|
539
|
+
there and stay runnable for regression re-checks.
|
package/lib/common/api.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./src/components/Dnd";
|
|
|
18
18
|
export * from "./src/utils";
|
|
19
19
|
export * from "./src/grid/agGrid4";
|
|
20
20
|
export * from "./src/grid/columnState";
|
|
21
|
+
export * from "./src/grid/columnState/columnGrid";
|
|
21
22
|
export * from "./src/components/Buttons";
|
|
22
23
|
export * from "./src/components/MyResizeObserver";
|
|
23
24
|
export * from "./src/components/Other";
|
package/lib/common/api.js
CHANGED
|
@@ -15,17 +15,21 @@ export * from "./src/styles/styleGrid";
|
|
|
15
15
|
export * from "./src/styles/tokens";
|
|
16
16
|
// 1. BASE LAYER - no internal project dependencies
|
|
17
17
|
export * from "./updateBy";
|
|
18
|
-
// 2. HOOKS - depend
|
|
18
|
+
// 2. HOOKS - depend on updateBy and wenay-common2
|
|
19
19
|
export * from "./src/hooks";
|
|
20
|
-
// 4. DND components - depend on updateBy
|
|
20
|
+
// 4. DND components - depend on updateBy and utils (persistedMaps)
|
|
21
21
|
export * from "./src/components/Dnd";
|
|
22
|
-
// 5. UTILS -
|
|
22
|
+
// 5. UTILS - self-contained (persisted maps live in utils/persistedMaps; components import
|
|
23
|
+
// their map from there, so utils no longer reaches into the component layer)
|
|
23
24
|
export * from "./src/utils";
|
|
24
25
|
// 5b. GRID (agGrid4): core buffer + headless hook + AgGridTable.
|
|
25
26
|
export * from "./src/grid/agGrid4";
|
|
26
27
|
// 5c. GRID column state (createColumnState): persisted order/visibility/width/sort/filter,
|
|
27
|
-
// standalone config store + optional two-way ag-grid adapter (attach via onGridReady)
|
|
28
|
+
// standalone config store + optional two-way ag-grid adapter (attach via onGridReady).
|
|
29
|
+
// The columnState barrel is deliberately ag-grid-free; createColumnGrid (ag-grid runtime +
|
|
30
|
+
// Toolbar) ships from its own module so grid-less consumers can import the barrel directly.
|
|
28
31
|
export * from "./src/grid/columnState";
|
|
32
|
+
export * from "./src/grid/columnState/columnGrid";
|
|
29
33
|
// 6. BASE COMPONENTS - depend on hooks
|
|
30
34
|
export * from "./src/components/Buttons";
|
|
31
35
|
export * from "./src/components/MyResizeObserver";
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** @deprecated No consumers in the library; prefer `useDraggableApi` (delta-based, mouse+touch)
|
|
2
|
+
* or `DragBox` for the imperative onX/onY shape. Kept as-is because its semantics are unique
|
|
3
|
+
* (document.body listeners, stopPropagation on every move tick, ABSOLUTE x/y coordinates,
|
|
4
|
+
* no preventDefault) - re-basing it on useDraggableApi would change observable behavior.
|
|
5
|
+
* Candidate for removal in a deliberate breaking version (A7/A3 cleanup inventory). */
|
|
1
6
|
export declare function DragArea({ children, onY, onX, x, y, last, onStart, onStop, }: {
|
|
2
7
|
children: React.JSX.Element;
|
|
3
8
|
onX?: (x: number) => void;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
|
|
3
|
+
/** @deprecated No consumers in the library; prefer `useDraggableApi` (delta-based, mouse+touch)
|
|
4
|
+
* or `DragBox` for the imperative onX/onY shape. Kept as-is because its semantics are unique
|
|
5
|
+
* (document.body listeners, stopPropagation on every move tick, ABSOLUTE x/y coordinates,
|
|
6
|
+
* no preventDefault) - re-basing it on useDraggableApi would change observable behavior.
|
|
7
|
+
* Candidate for removal in a deliberate breaking version (A7/A3 cleanup inventory). */
|
|
4
8
|
export function DragArea({ children, onY, onX, x = 0, y = 0, last, onStart, onStop, }) {
|
|
5
9
|
const lastC = useRef(null);
|
|
6
10
|
const lastT = useRef(null);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode } from "react";
|
|
2
2
|
import { type RndResizeCallback } from "react-rnd";
|
|
3
|
-
import {
|
|
3
|
+
import { floatingWindowMap } from "../../utils/persistedMaps";
|
|
4
4
|
export type FloatingWindowPosition = {
|
|
5
5
|
x: number;
|
|
6
6
|
y: number;
|
|
@@ -31,6 +31,9 @@ export type FloatingWindowProps = {
|
|
|
31
31
|
position?: FloatingWindowPosition;
|
|
32
32
|
size?: FloatingWindowSize;
|
|
33
33
|
moveOnlyHeader?: boolean;
|
|
34
|
+
/** Show the close button and handle its click. */
|
|
35
|
+
onClickClose?: () => void;
|
|
36
|
+
/** @deprecated typo alias of {@link onClickClose}; kept for compatibility, `onClickClose` wins when both are set. */
|
|
34
37
|
onCLickClose?: () => void;
|
|
35
38
|
header?: React.ReactElement | boolean;
|
|
36
39
|
overflow?: boolean;
|
|
@@ -48,7 +51,7 @@ export type FloatingWindowProps = {
|
|
|
48
51
|
children: React.ReactElement | ((update: number) => React.ReactElement);
|
|
49
52
|
className?: string;
|
|
50
53
|
};
|
|
51
|
-
export type FloatingWindowControllerOptions = Omit<FloatingWindowProps, "children" | "className" | "header" | "moveOnlyHeader" | "overflow" | "onCLickClose">;
|
|
54
|
+
export type FloatingWindowControllerOptions = Omit<FloatingWindowProps, "children" | "className" | "header" | "moveOnlyHeader" | "overflow" | "onCLickClose" | "onClickClose">;
|
|
52
55
|
export type FloatingWindowController = {
|
|
53
56
|
position: FloatingWindowPosition;
|
|
54
57
|
size: FloatingWindowSize;
|
|
@@ -64,14 +67,14 @@ export type FloatingWindowController = {
|
|
|
64
67
|
onResize: RndResizeCallback;
|
|
65
68
|
onResizeStop: RndResizeCallback;
|
|
66
69
|
};
|
|
67
|
-
export
|
|
70
|
+
export { floatingWindowMap };
|
|
68
71
|
export declare const FloatingWindow: typeof FloatingWindowBase;
|
|
69
72
|
export declare function useFloatingWindowController({ keyForSave: ks, position, size, zIndex, onUpdate, limit, sizeByWindow }?: FloatingWindowControllerOptions): FloatingWindowController;
|
|
70
73
|
/**
|
|
71
74
|
* Wrapper component around react-rnd.
|
|
72
75
|
* Provides dragging and resizing, an optional header, and a close button.
|
|
73
76
|
*/
|
|
74
|
-
export declare function FloatingWindowBase({ children, keyForSave: ks, position, size, overflow, zIndex, onUpdate, disableDragging, className, header, moveOnlyHeader, limit, onCLickClose, sizeByWindow }: FloatingWindowProps): import("react/jsx-runtime").JSX.Element;
|
|
77
|
+
export declare function FloatingWindowBase({ children, keyForSave: ks, position, size, overflow, zIndex, onUpdate, disableDragging, className, header, moveOnlyHeader, limit, onCLickClose, onClickClose, sizeByWindow }: FloatingWindowProps): import("react/jsx-runtime").JSX.Element;
|
|
75
78
|
export type DragBoxProps = {
|
|
76
79
|
/** Child element that should be draggable */
|
|
77
80
|
children: ReactNode;
|
|
@@ -106,4 +109,5 @@ export type DragBoxProps = {
|
|
|
106
109
|
* Function only as a hook for parameter changes during movement, although it has its own component (for offset counting).
|
|
107
110
|
* Returns the distance traveled when moving the child element.
|
|
108
111
|
*/
|
|
109
|
-
export declare function DragBox({ children, onX, onY, x, y, right, last, dragging,
|
|
112
|
+
export declare function DragBox({ children, onX, onY, x, y, right, last, dragging: _dragging, // accepted for compatibility, was never read
|
|
113
|
+
onStart, onStop }: DragBoxProps): import("react/jsx-runtime").JSX.Element;
|