wenay-react2 1.0.40 → 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 +971 -0
- package/doc/PROJECT_FUNCTIONALITY.md +405 -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 +36 -0
- package/doc/changes/v1.0.42.md +26 -0
- package/doc/progress/README.md +14 -0
- package/doc/progress/architecture-fix-queue.md +74 -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 +81 -0
- package/doc/wenay-react2-1.0.8.tgz +0 -0
- package/doc/wenay-react2-rare.md +807 -0
- package/doc/wenay-react2.md +539 -0
- 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 +123 -5
- package/lib/common/src/logs/logs.js +218 -130
- package/lib/common/src/logs/logsController.d.ts +3 -0
- package/lib/common/src/styles/tokens.d.ts +7 -6
- package/lib/common/src/styles/tokens.js +8 -5
- 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/menuRight.css +29 -23
- package/lib/style/style.css +73 -10
- package/lib/style/tokens.css +8 -4
- package/package.json +3 -2
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
# wenay-react2 - BRIEF cheat sheet (canonical UI/controller API)
|
|
2
|
+
|
|
3
|
+
> Root import: `import { ... } from "wenay-react2"`.
|
|
4
|
+
> Notation: `name(args) -> ret`. JSX examples show the intended public path, not every prop.
|
|
5
|
+
> Short controller-style names are canonical. Removed names are recorded in
|
|
6
|
+
> **WENAY_REACT2_RENAMES.md** for migration only; old aliases are not exported.
|
|
7
|
+
|
|
8
|
+
## Standard
|
|
9
|
+
```
|
|
10
|
+
Hooks that own lifecycle/state return a small controller:
|
|
11
|
+
api.props / api.bind // props to spread on an element
|
|
12
|
+
api.open() / close() / set() // modal/menu-like state
|
|
13
|
+
api.update() / remove() / clean() // data buffers
|
|
14
|
+
api.fit() / flush() / sync() // ag-grid lifecycle
|
|
15
|
+
|
|
16
|
+
Components receive controllers when state must outlive the component.
|
|
17
|
+
Library primitives never know app-specific terms, domain objects, or group policy.
|
|
18
|
+
Put app-specific layout/build rules in an app wrapper above the primitive.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Render Memory
|
|
22
|
+
```
|
|
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
|
|
26
|
+
renderBy(obj, ms?) // emit render for subscribers
|
|
27
|
+
renderByRevers(obj, ms?, reverse?=true) // reverse/last affect ONLY React subscribers;
|
|
28
|
+
renderByLast(obj, ms?) // updateBy(obj, cb) callbacks always all run first
|
|
29
|
+
|
|
30
|
+
const state = { count: 0 }
|
|
31
|
+
const api = createUpdateApi(state)
|
|
32
|
+
api.use() // hook subscription
|
|
33
|
+
api.emit(ms?) // renderBy(state)
|
|
34
|
+
api.on(obj => {}) -> off
|
|
35
|
+
|
|
36
|
+
const api2 = useUpdateByApi(state) // hook + controller in one call
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Persistent process memory:
|
|
40
|
+
```
|
|
41
|
+
memoryGetOrCreate(key, def, {abs?, deepAutoMerge?, reversDeep?}) -> def-or-stored
|
|
42
|
+
memoryGetById(key, def, id) -> stored only while id is the same
|
|
43
|
+
memorySet(key, data)
|
|
44
|
+
memoryGet(key)
|
|
45
|
+
memoryUpdate(key, mutate) -> cur? // mutate + rerender + announce in one call
|
|
46
|
+
memoryMarkDirty(key) // announce an in-place mutation of a memoryGetOrCreate object
|
|
47
|
+
createSearchHistory({key, max?}) // reusable small persisted search history controller
|
|
48
|
+
memoryMaps // rnd / resize / other maps
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Persistence contract (memoryCache): the library NEVER writes storage by itself. The persisted maps
|
|
52
|
+
(floatingWindowMap, mapResiReact, mapRightMenu, memoryProps) are `ObservableMap`s - set/delete/clear
|
|
53
|
+
announce themselves, in-place mutations are announced at the commit points (drag/resize stop,
|
|
54
|
+
menu drag end, setPlace). memoryCache observes the maps it owns; the app owns the write policy:
|
|
55
|
+
```
|
|
56
|
+
memoryCache.load() // once on start; remembers the saved snapshot
|
|
57
|
+
memoryCache.onDirty((scope?, key?) => ...) -> off // dirty channel (coalesced, async)
|
|
58
|
+
memoryCache.saveDebounced(ms?) / save() / flush() // write only payloads that differ from the snapshot
|
|
59
|
+
memoryCache.isDirty() // cheap hint, e.g. a beforeunload guard
|
|
60
|
+
|
|
61
|
+
memoryCache.load()
|
|
62
|
+
const off = memoryCache.onDirty(() => memoryCache.saveDebounced(800))
|
|
63
|
+
document.addEventListener("visibilitychange",
|
|
64
|
+
() => { if (document.visibilityState == "hidden") void memoryCache.flush() })
|
|
65
|
+
window.addEventListener("pagehide", () => { void memoryCache.flush() }) // backstop: flush is async
|
|
66
|
+
```
|
|
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
|
+
|
|
75
|
+
## Outside Click / Buttons
|
|
76
|
+
```
|
|
77
|
+
const outside = useOutside({onOutside, enabled?})
|
|
78
|
+
<div {...outside.props} />
|
|
79
|
+
outside.enable()
|
|
80
|
+
outside.disable()
|
|
81
|
+
outside.contains(event.target)
|
|
82
|
+
|
|
83
|
+
<OutsideClickArea outsideClick={close} status={open}>...</OutsideClickArea>
|
|
84
|
+
|
|
85
|
+
<Button button={<button>Open</button>} outClick keyForSave?>{...}</Button> // keySave = deprecated alias
|
|
86
|
+
<OutsideButton button={...}>{...}</OutsideButton>
|
|
87
|
+
<HoverButton button={...}>{...}</HoverButton>
|
|
88
|
+
<AbsoluteButton button={...}>{...}</AbsoluteButton>
|
|
89
|
+
```
|
|
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
|
+
|
|
105
|
+
## Drag / Floating Windows
|
|
106
|
+
```
|
|
107
|
+
const drag = useDraggableApi({initialPosition, holdMs?, onDragStart?, onDragEnd?, onMove?, trackState?})
|
|
108
|
+
<div {...drag.bind} style={{transform: `translate(${drag.position.x}px, ${drag.position.y}px)`}} />
|
|
109
|
+
drag.setPosition({x, y})
|
|
110
|
+
drag.resetPosition()
|
|
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)
|
|
114
|
+
|
|
115
|
+
const r = useReorder({order, commit, move?, canDrag?, preview?, holdMs?}) // mini reorder-by-drag
|
|
116
|
+
<div ref={r.listRef}>{order.map(k => { // children 1:1 with order
|
|
117
|
+
const it = r.item(k) // it.dragging / it.active
|
|
118
|
+
return <div key={k} {...it.props} style={{...own, ...it.style}} /> // ONE commit(next) on drop
|
|
119
|
+
})}</div>
|
|
120
|
+
|
|
121
|
+
const b = useReorderBoard({columns: [{key, items}], commit, // drag between vertical columns
|
|
122
|
+
canDrag?, holdMs?, onDragStart?, onDragMove?, onOverChange?, onDragEnd?})
|
|
123
|
+
<div ref={b.columnRef('todo')}>{items.map(k => ...b.item(k)...)}</div> // one div per column, any count
|
|
124
|
+
b.over // {col, index} | null - live target
|
|
125
|
+
// column gravity is YOUR CSS: justify-content flex-start packs up, flex-end packs down
|
|
126
|
+
|
|
127
|
+
<FloatingWindow keyForSave="tool" size={{width: 320, height: 240}} header={<div>Tool</div>}
|
|
128
|
+
onClickClose={() => setOpen(false)}> {/* onCLickClose (typo) = deprecated alias */}
|
|
129
|
+
<Panel />
|
|
130
|
+
</FloatingWindow>
|
|
131
|
+
|
|
132
|
+
const wnd = useFloatingWindowController({keyForSave: "custom-tool", size: {width: 320, height: 240}})
|
|
133
|
+
wnd.position // {x,y}; bind wnd.onHeaderMouseDown/onHeaderTouchStart for custom chrome
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Modal / Input
|
|
137
|
+
```
|
|
138
|
+
<ModalProvider>
|
|
139
|
+
<App />
|
|
140
|
+
</ModalProvider>
|
|
141
|
+
|
|
142
|
+
const modal = useModal()
|
|
143
|
+
modal.open(<Dialog />)
|
|
144
|
+
modal.set(<Dialog />)
|
|
145
|
+
modal.close()
|
|
146
|
+
modal(null) // callable shortcut for clearing
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Input helpers:
|
|
150
|
+
```
|
|
151
|
+
const text = useTextInputPanel({callback: txt => {}, txt: ""}) // headless value + submit API
|
|
152
|
+
const file = useFileInputPanel({callback: file => {}}) // headless file + submit API
|
|
153
|
+
<TextInputPanel callback={txt => {}} name="Name" txt="" /> // thin visual wrapper over hook
|
|
154
|
+
<FileInputPanel callback={file => {}} name="File" /> // thin visual wrapper over hook
|
|
155
|
+
<TextInputModal callback={...} outClick={modal.close} />
|
|
156
|
+
<FileInputModal callback={...} outClick={modal.close} />
|
|
157
|
+
<FreeModal outClick={modal.close} size={{width: 400, height: 260}}>...</FreeModal>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Settings Dialog
|
|
161
|
+
```
|
|
162
|
+
<SettingsDialog trigger={<span>settings</span>} sections={[{key, name, render, children?, parentKey?, searchText?, keywords?}]} defaultSection? /> // searchable tree + persisted search history; history closes when search focus leaves
|
|
163
|
+
const settings = useSettingsDialogController({sections, defaultSection?}) // open/search/tree/history/resize actions for custom chrome
|
|
164
|
+
registerSettingsSection({key, name, render, parentKey?, searchText?, keywords?}) -> unregister
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## UI Slot
|
|
168
|
+
```
|
|
169
|
+
const slot = createUiSlot({key, places: {top: "Top bar", side: "Sidebar"}, def: "top"})
|
|
170
|
+
|
|
171
|
+
<slot.Slot place="top">{content}</slot.Slot> // each mount point decides by itself
|
|
172
|
+
<slot.PlacementSetting /> // segmented place switcher; setPlace marks memoryCache dirty
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Toolbar
|
|
176
|
+
```
|
|
177
|
+
const tb = createToolbar({key, items: [{key, title, icon?, short?, render?, onClick?, defaultVisible?, fixed?}], def?, settingsItem?, resetItem?, source?, sourceMode?})
|
|
178
|
+
|
|
179
|
+
<tb.Bar settings reset? /> // the live bar; settings adds a gear; reset is hidden by default; item moves animate
|
|
180
|
+
<tb.Settings /> // the PURE config editor - same element drops into a settings section
|
|
181
|
+
tb.api.useConfig() / getConfig() / setConfig(next) / setOrder(order) / show(key,on) / setDensity(key) / reset()
|
|
182
|
+
tb.api.showSettings(on) / showReset(on) // pseudo-controls visibility
|
|
183
|
+
tb.api.useItems() // headless bar: ordered visible [{item, density, content}] - custom markup
|
|
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
|
|
186
|
+
|
|
187
|
+
registerToolbarDensity({key, name, renderItem?}) -> unregister // built-ins: 'icon', 'label'
|
|
188
|
+
toolbarItemIcon(item) // the item's icon, or its first letters as a text pseudo-icon
|
|
189
|
+
```
|
|
190
|
+
Config `{order, visible, density}` is serializable and persisted like createUiSlot
|
|
191
|
+
(memoryGetOrCreate -> memoryCache, the app owns the write policy). Items added in an app update are merged
|
|
192
|
+
into a stale persisted config (appended, default-visible), removed ones are ignored. `icon` is
|
|
193
|
+
optional: in icon density an icon-less item renders the first letters of its short/title.
|
|
194
|
+
The gear and reset button are pseudo-items: `settingsItem: {title?, icon?}` and `resetItem` re-skin them,
|
|
195
|
+
and the editor has separated toggle rows for them (`visible['__settings']` / `visible['__reset']`, no order slots - they always sit at the bar edge). Settings is visible by default; reset is hidden by default unless `resetItem.defaultVisible: true` or `api.showReset(true)` enables it. `resetItem: false` removes the reset feature.
|
|
196
|
+
|
|
197
|
+
`source?` makes the toolbar a VIEW over an external `UiListSource`. Default `sourceMode:"orderVisible"`
|
|
198
|
+
means the source owns item order and item visibility, so one config can drive a grid, its icon menu
|
|
199
|
+
AND the toolbar together (`columnState.api.listSource`, QA card 31). `sourceMode:"order"` shares only
|
|
200
|
+
the source-key order; item membership, density, pseudo-controls, and extra non-source item positions
|
|
201
|
+
stay in the toolbar store (QA card 30). Without `source` the toolbar keeps its own store (standalone mode).
|
|
202
|
+
|
|
203
|
+
## Callback Hub
|
|
204
|
+
```
|
|
205
|
+
const hub = createCallbackHub<[Args]>(emit => api.onX(emit)) // one slot -> many subscribers
|
|
206
|
+
hub.on(cb) -> off
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Params
|
|
210
|
+
```
|
|
211
|
+
<ParamsEditor
|
|
212
|
+
params={params}
|
|
213
|
+
onChange={next => setParams(next)}
|
|
214
|
+
onExpand={next => setParams(next)}
|
|
215
|
+
expandStatus?
|
|
216
|
+
expandStatusLvl?
|
|
217
|
+
/>
|
|
218
|
+
|
|
219
|
+
const controller = useParamsEditorController({params, onChange, onExpand?})
|
|
220
|
+
|
|
221
|
+
<ParamsEdit params={params} onSave={next => save(next)} />
|
|
222
|
+
<ParamRow name="Name">...</ParamRow>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Menu
|
|
226
|
+
```
|
|
227
|
+
type MenuItem = { name, actionKey?, onClick?, next?, func?, status? } | false | null | undefined
|
|
228
|
+
|
|
229
|
+
<Menu data={items} coordinate={{x: 0, y: 0}} />
|
|
230
|
+
|
|
231
|
+
<contextMenu.Layer>{children}</contextMenu.Layer>
|
|
232
|
+
contextMenu.openAt(event, [{name: "Copy", actionKey: "grid.copy", onClick: copy}], {source: "grid"})
|
|
233
|
+
contextMenu.stats.getSnapshot() // local counters: opens, source/layer usage, actionTotals, keyed actions
|
|
234
|
+
contextMenu.close()
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Use `contextMenu.openAt(e, items, {source?})` for new right-click integrations. `contextMenu.map` still exists as a legacy Layer queue, but it is not the recommended path for new code. `contextMenu.stats` is an in-memory diagnostics API; it never persists data, never reports over the network, and records per-action counters only for explicit `actionKey` values. Unkeyed items are counted in aggregate totals without storing labels.
|
|
238
|
+
|
|
239
|
+
`Menu` keeps the hovered/open item in internal React state. `status` is only an initial open hint and a compatibility field passed to custom renderers; menu objects are not mutated.
|
|
240
|
+
|
|
241
|
+
Floating right menu:
|
|
242
|
+
```
|
|
243
|
+
<DropdownMenu elements={items} trigger={iconOrRender} classNames={...} styles={...} />
|
|
244
|
+
const menu = useRightMenuController({elements, keyForSave?})
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`DropdownMenu` is a floating action menu with caller-owned trigger/content styling, not the main context-menu primitive. `useRightMenuController` exposes the same open/fixed/select/submenu/drag state for custom views; `DropdownMenu` remains the compatible visual wrapper.
|
|
248
|
+
|
|
249
|
+
## agGrid4
|
|
250
|
+
Plain declarative rows:
|
|
251
|
+
```
|
|
252
|
+
<AgGridTable<Row> rowData={rows} columnDefs={cols} />
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Buffered mirror table:
|
|
256
|
+
```
|
|
257
|
+
const grid = useAgGrid<Row>({getId: row => row.id})
|
|
258
|
+
<AgGridTable<Row> controller={grid} columnDefs={cols} />
|
|
259
|
+
|
|
260
|
+
grid.update({newData: rows})
|
|
261
|
+
grid.remove([{id}])
|
|
262
|
+
grid.clean()
|
|
263
|
+
grid.fit()
|
|
264
|
+
grid.flush()
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Shared module store:
|
|
268
|
+
```
|
|
269
|
+
export const mainTable = createGridBuffer<Row>({getId, externalBuffer})
|
|
270
|
+
|
|
271
|
+
const grid = useAgGrid({core: mainTable})
|
|
272
|
+
<AgGridTable<Row> controller={grid} columnDefs={cols} />
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Overlay patches over React-owned `rowData`:
|
|
276
|
+
```
|
|
277
|
+
const core = createGridBuffer<Row>({getId, mode: "overlay", pushDefaults: {add: false}})
|
|
278
|
+
const grid = useAgGrid({core})
|
|
279
|
+
|
|
280
|
+
<AgGridTable<Row> controller={grid} rowData={rows} columnDefs={cols} getRowId={p => getId(p.data)} />
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Dynamic columns are pure names + lifecycle replay:
|
|
284
|
+
```
|
|
285
|
+
const columns = createColumnBuffer<Row>()
|
|
286
|
+
|
|
287
|
+
columns.api.setNames(["a", "b"])
|
|
288
|
+
columns.control.attach(api, {
|
|
289
|
+
apply: ({api, names}) => api.setGridOption("columnDefs", buildColumnDefs(names)),
|
|
290
|
+
})
|
|
291
|
+
columns.api.apply()
|
|
292
|
+
columns.control.detach()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`createColumnBuffer()` knows nothing about groups, `columnDefs`, business names, or where columns live.
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
## columnState
|
|
299
|
+
A persisted column layer over a keyed column set - order / visibility / width / sort / filter in a
|
|
300
|
+
standalone config store, with an OPTIONAL two-way ag-grid adapter. Mobile card views consume the
|
|
301
|
+
SAME config with no ag-grid at all. agGrid4 wrappers are never modified: this is the app-level
|
|
302
|
+
column wrapper `WRAPPER.md` postulates, packaged as a primitive.
|
|
303
|
+
```
|
|
304
|
+
const cs = createColumnState({key, columns: [{key, title, short?, icon?, group?, fixed?, defaultVisible?, cardRole?}], def?, saveMs?})
|
|
305
|
+
|
|
306
|
+
cs.columns // the descriptors (UI renders from these + config)
|
|
307
|
+
cs.api.useConfig() / getConfig() / setConfig(next) / reset()
|
|
308
|
+
cs.api.show(key, on) / move(order) / setSort({key, dir} | null) / toggleSort(key) // asc->desc->off
|
|
309
|
+
cs.api.visibleKeys() // keys to render, in order (group-gated)
|
|
310
|
+
cs.api.onChange.on(cfg => ...) -> off
|
|
311
|
+
cs.api.usePresent() / isPresent(key) / setPresent(keys | null) // live-grid presence
|
|
312
|
+
cs.api.getPresentGate() / setPresentGate(keys | null) // app runtime availability gate, not persisted
|
|
313
|
+
cs.api.listSource // {order, visible} slice as a Toolbar `source`
|
|
314
|
+
```
|
|
315
|
+
Config `{v, order, visible, width, sort, filter, groups}` is serializable and persisted like
|
|
316
|
+
createToolbar (memoryGetOrCreate(key) -> memoryCache, the app owns the write policy); a stale config migrates
|
|
317
|
+
softly (unknown keys dropped, new columns appended default-visible, `fixed` pinned). `sort` is
|
|
318
|
+
STICKY: independent of visibility/selection, may point at a hidden or grid-absent column, changes
|
|
319
|
+
only by an explicit toggle/header click.
|
|
320
|
+
|
|
321
|
+
Two-way ag-grid adapter (opt-in, agGrid4 untouched):
|
|
322
|
+
```
|
|
323
|
+
<AgGridTable columnDefs={cols} autoSizeColumns={false}
|
|
324
|
+
onGridReady={e => cs.grid.attach(e.api)} // restore saved layout, then watch the grid
|
|
325
|
+
onGridPreDestroyed={() => cs.grid.detach()} /> // config survives remounts
|
|
326
|
+
```
|
|
327
|
+
Grid drag/resize/hide/sort/filter fold back into the config; UI edits apply to the grid; a column
|
|
328
|
+
removed from the live grid (dynamic defs) keeps its config entry and reads `isPresent==false`.
|
|
329
|
+
|
|
330
|
+
Icon menu - a 1:1 grid mirror (reorder in the grid <-> reorder the buttons):
|
|
331
|
+
```
|
|
332
|
+
<ColumnsMenu state={cs} compact? marks? tail? onItem? onTail? reorder? /> // default click = toggle visibility
|
|
333
|
+
<MenuStrip items={[{key, title, state:'on'|'off'|'disabled', marks?, fixed?}]} onItem? onMove? tail? compact? />
|
|
334
|
+
```
|
|
335
|
+
`ColumnsMenu` binds `MenuStrip` - a presentation-only layer that reports clicks/drags but never
|
|
336
|
+
interprets them - to the config. `compact` = icon-only buttons; an item with no icon shows its
|
|
337
|
+
first letters. `tail` = trailing non-column buttons (a table-standards cycler etc.). For new
|
|
338
|
+
settings-integrated toolbar/grid surfaces prefer `createToolbar({source: cs.api.listSource})`;
|
|
339
|
+
keep `ColumnsMenu` for compact button strips or custom presentation-only menus.
|
|
340
|
+
|
|
341
|
+
Mobile (no ag-grid): dots selector + card rows over the same config:
|
|
342
|
+
```
|
|
343
|
+
<ColumnDots state={cs} max?=8 /> // track of marks; dots = shown columns; tap empty=add,
|
|
344
|
+
// drag=replace, swipe up=remove, tap dot=select, sort button cycles
|
|
345
|
+
<CardList<Row> state={cs} data={rows} getId? renderValue? /> // visible cols -> card fields; cardRole:'title'/'accent'
|
|
346
|
+
```
|
|
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).
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
## Observe React Adapter
|
|
378
|
+
`wenay-common2` owns the store/listen/RPC primitives. `wenay-react2` owns React lifecycle hooks around them.
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
import { Observe } from "wenay-common2"
|
|
382
|
+
import { useStoreMirror, useStoreNode, useStoreKeys, useStoreSelect, useStoreChangedPaths, useListenEffect, useListenValue } from "wenay-react2"
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Node subscription:
|
|
386
|
+
```
|
|
387
|
+
const price = useStoreNode(store.node.data.BTC)
|
|
388
|
+
price.value
|
|
389
|
+
price.exists
|
|
390
|
+
price.replace(123)
|
|
391
|
+
price.refresh()
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Selection subscription:
|
|
395
|
+
```
|
|
396
|
+
const sel = useStoreSelect(store.update({data: {BTC: true}, meta: {status: true}}))
|
|
397
|
+
sel.value
|
|
398
|
+
sel.get()
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Network mirror is explicit:
|
|
402
|
+
```
|
|
403
|
+
const mirror = useStoreMirror(remoteStore, {data: {}, meta: {}}, {
|
|
404
|
+
mask: {data: {BTC: true}, meta: {status: true}},
|
|
405
|
+
current: true,
|
|
406
|
+
drain: 250,
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
mirror.value
|
|
410
|
+
mirror.ready
|
|
411
|
+
mirror.sync()
|
|
412
|
+
mirror.stop()
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Per-key feed (`store.each()` of common2 — one cb per CHANGED top-level key):
|
|
416
|
+
```
|
|
417
|
+
useStoreEach(store, (key, value, ctx) => {
|
|
418
|
+
value === undefined ? removeRow(key) : upsertRow(key, value)
|
|
419
|
+
}, {enabled?})
|
|
420
|
+
```
|
|
421
|
+
`undefined` = key deleted; a root replace (store.replace / mirror keyframe) expands into one call per key. The fold target should live outside React state (ref/Map/grid api) — the hook renders nothing itself. The wire counterpart is `useStoreReplayEach` below.
|
|
422
|
+
|
|
423
|
+
Listen helpers:
|
|
424
|
+
```
|
|
425
|
+
useListenEffect(listen, (...args) => {})
|
|
426
|
+
const value = useListenValue(listen, {initial})
|
|
427
|
+
const args = useListenArgs(listen)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The hook does not choose transport. `remoteStore` needs `{ get(mask?), changed }` and may also provide `changedPaths`; apps may implement it with RPC, WebSocket, SSE, or test HTTP. `changedPaths` is used as a transport optimization: mirror pulls `mask ∩ paths` instead of the whole mask. `initial` is only the local mirror seed; changing it does not reset an existing mirror. `mask` is treated as a small declarative StoreMask, so structurally equal inline masks do not resubscribe. For add/delete keys, subscribe to the parent object node with `useStoreKeys(node)`; this also covers deep objects. If a state key conflicts with StoreNode methods such as `count`, use `store.node.at("count")`.
|
|
431
|
+
|
|
432
|
+
## Replay React Adapter
|
|
433
|
+
Client-side hooks over the wenay-common2 Replay stack (snapshot + sequenced delta line). Server parts (`conflateReplay`, `archiveReplay`, `createRpcServerAuto` replayOpts) are per-connection and stay hook-free by design.
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { useReplaySubscribe, useReplayRouteSubscribe, useStoreReplaySync, useStoreReplayMirror, useStoreReplayRouteSync, useStoreReplayRouteMirror, useStoreReplayEach, useReplayFrame, useReplayHistory } from "wenay-react2"
|
|
437
|
+
|
|
438
|
+
// any replay line ({line, since, keyframe, frame?, frameLine?} remote)
|
|
439
|
+
const sub = useReplaySubscribe(remote, (frame) => draw(frame), {onSeq?, onError?, since?, enabled?, staleMs?, onStale?, policy?, hint?})
|
|
440
|
+
sub.ready; sub.error; sub.seq(); sub.restart()
|
|
441
|
+
sub.stale; sub.lastTs() // freshness (needs staleMs): stale re-renders on fresh<->stale transitions ONLY; lastTs() is a getter like seq()
|
|
442
|
+
// policy: 'queue' (default, nothing skipped) | 'frame' (rides the server frameLine when present: on lag the
|
|
443
|
+
// server drops and recovers with a mini-frame; old servers/in-proc degrade to 'queue'). hint -> the line's frame condenser.
|
|
444
|
+
|
|
445
|
+
// explicit route hand-off over the same logical replay line (relay <-> direct, etc.)
|
|
446
|
+
const routed = useReplayRouteSubscribe(relayRemote, (frame) => draw(frame), {label: "relay", onRoute?})
|
|
447
|
+
await routed.switchRoute(directRemote, {label: "direct"})
|
|
448
|
+
routed.ready; routed.switching; routed.route; routed.seq(); routed.label(); routed.active()
|
|
449
|
+
// route hooks do not expose stale/lastTs yet; use non-route hooks when freshness is required.
|
|
450
|
+
// store patch line (Observe.exposeStoreReplay(...).api.replay)
|
|
451
|
+
const mirror = useStoreReplayMirror(remote, initial, {enabled?}) // creates the mirror store; same staleMs/stale/policy surface
|
|
452
|
+
mirror.store; mirror.ready; mirror.seq(); mirror.restart()
|
|
453
|
+
const sync = useStoreReplaySync(existingStore, remote) // same, store supplied by the app
|
|
454
|
+
const routedSync = useStoreReplayRouteSync(existingStore, relayRemote, {label: "relay"}) // same store, switchRoute() replaces the route after catch-up
|
|
455
|
+
const routedMirror = useStoreReplayRouteMirror(relayRemote, initial, {label: "relay"}) // mirror store + route hand-off controller
|
|
456
|
+
|
|
457
|
+
// per-key fold over the same line (Observe.syncStoreReplayEach counterpart): cb per CHANGED top-level key,
|
|
458
|
+
// first delivery = keyframe expanded per key, (key, undefined) = deleted — grid rows without special cases
|
|
459
|
+
const feed = useStoreReplayEach<Rows>(remote, (key, row) => row === undefined ? removeRow(key) : upsertRow(key, row), {drain?, initial?})
|
|
460
|
+
feed.store; feed.ready; feed.stale; feed.seq(); feed.restart() // same controller surface as the mirror
|
|
461
|
+
// the mirror store lives in a ref: in-mount resubscribes (StrictMode/restart/enabled) reconnect by tail ON TOP of
|
|
462
|
+
// kept state — the consumer sees only the diff. After a full unmount: {since: prev.seq(), initial: prev.store.snapshot()}
|
|
463
|
+
|
|
464
|
+
// pull at YOUR pace (frame model): timer around remote.frame(seq, hint) — server condenses, no backlog, no live socket sub
|
|
465
|
+
const pf = useReplayFrame(remote, (quote) => apply(quote), {intervalMs: 500, hint?})
|
|
466
|
+
pf.ready; pf.error; pf.seq(); pf.pull(); pf.restart() // error (e.g. sacred line evicted) STOPS pulling until restart()
|
|
467
|
+
|
|
468
|
+
// time machine over Replay.openHistory(storage, live?)
|
|
469
|
+
const tt = useReplayHistory(history, (frame) => draw(frame), {head: () => replay.head()})
|
|
470
|
+
tt.live; tt.seq; tt.head; tt.pause(); tt.play(); tt.seek({seq})
|
|
471
|
+
```
|
|
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
|
+
|
|
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.
|
|
478
|
+
|
|
479
|
+
## Logs
|
|
480
|
+
```
|
|
481
|
+
logsApi.addLogs({id: "api", time: new Date(), txt: "done", var: 1})
|
|
482
|
+
<logsApi.React.Message />
|
|
483
|
+
<logsApi.React.PageLogs />
|
|
484
|
+
<logsApi.React.Setting />
|
|
485
|
+
|
|
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.
|
|
489
|
+
const headless = createLogsController<MyFields>({options: {limitPer: 500, limit: 50}})
|
|
490
|
+
|
|
491
|
+
const messageNotifications = useMessageEventLogsController({maxVisible: 4})
|
|
492
|
+
<MessageEventLogsView controller={messageNotifications} zIndex={80} />
|
|
493
|
+
|
|
494
|
+
const contextTable = useLogsTableController()
|
|
495
|
+
const contextNotifications = useLogsNotificationsController()
|
|
496
|
+
|
|
497
|
+
const mini = useMiniLogsTable({data: rows, onClick: e => console.log(e.data)})
|
|
498
|
+
<AgGridTable {...mini.props} />
|
|
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()
|
|
504
|
+
```
|
|
505
|
+
|
|
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.
|
|
507
|
+
|
|
508
|
+
## Styles / Theme
|
|
509
|
+
```
|
|
510
|
+
tokens.color.bgDark
|
|
511
|
+
tokens.menu.outlineColor
|
|
512
|
+
tokens.logs.notificationAccent
|
|
513
|
+
tokens.zIndex.modal
|
|
514
|
+
|
|
515
|
+
GridStyleDefault() // inject legacy grid CSS vars/classes
|
|
516
|
+
StyleGridDefault // common ag-grid style object
|
|
517
|
+
buildAgTheme("dark" | "light")
|
|
518
|
+
useAgGridTheme("dark" | "light")
|
|
519
|
+
colDefCentered / colDefWrap / numericComparator
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Shared CSS variables include `--menu-outline-color`, `--logs-*`, `--dlg-*`, `--wnd-*`, `--tb-*`, `--cols-grid-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
|
|
523
|
+
|
|
524
|
+
## Charts
|
|
525
|
+
```
|
|
526
|
+
<MyChartEngine style={{height: 400}} />
|
|
527
|
+
createChartCanvas(config) -> canvas controller
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
The chart engine surface is still mostly low-level. Keep product-specific chart wrappers in the app.
|
|
531
|
+
|
|
532
|
+
## QA Stand
|
|
533
|
+
```
|
|
534
|
+
npm run testReact // http://localhost:3010/
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
The stand lives in `src/common/testUseReact/qa.tsx`. Use it for visual checks; agGrid4 overlay/dynamic-column demos are dedicated QA cards.
|
|
538
|
+
Two tabs: Active checks (current work) and Verified archive (`#archive`) - verified/fixed cards move
|
|
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;
|