wenay-react2 1.0.38 → 1.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/common/src/components/Dnd/FloatingWindow.d.ts +29 -12
  2. package/lib/common/src/components/Dnd/FloatingWindow.js +100 -91
  3. package/lib/common/src/components/Input.d.ts +25 -6
  4. package/lib/common/src/components/Input.js +27 -8
  5. package/lib/common/src/components/Menu/RightMenu.d.ts +28 -1
  6. package/lib/common/src/components/Menu/RightMenu.js +62 -35
  7. package/lib/common/src/components/Modal/LeftModal.js +5 -4
  8. package/lib/common/src/components/Modal/Modal.js +8 -7
  9. package/lib/common/src/components/ParamsEditor.d.ts +15 -0
  10. package/lib/common/src/components/ParamsEditor.js +45 -18
  11. package/lib/common/src/components/Settings/SettingsDialog.d.ts +68 -6
  12. package/lib/common/src/components/Settings/SettingsDialog.js +73 -13
  13. package/lib/common/src/components/Toolbar/Toolbar.js +64 -19
  14. package/lib/common/src/components/UiSlot/UiSlot.js +5 -4
  15. package/lib/common/src/grid/columnState/ColumnDots.d.ts +1 -1
  16. package/lib/common/src/grid/columnState/ColumnDots.js +1 -1
  17. package/lib/common/src/grid/columnState/ColumnsMenu.js +10 -18
  18. package/lib/common/src/grid/columnState/columnState.js +8 -6
  19. package/lib/common/src/hooks/useKeyboard.js +4 -3
  20. package/lib/common/src/logs/logs.d.ts +13 -133
  21. package/lib/common/src/logs/logs.js +20 -39
  22. package/lib/common/src/logs/logsContext.d.ts +26 -0
  23. package/lib/common/src/logs/logsContext.js +25 -20
  24. package/lib/common/src/logs/logsController.d.ts +88 -0
  25. package/lib/common/src/logs/logsController.js +54 -0
  26. package/lib/common/src/logs/miniLogs.d.ts +69 -3
  27. package/lib/common/src/logs/miniLogs.js +70 -15
  28. package/lib/common/src/menu/menu.d.ts +15 -3
  29. package/lib/common/src/menu/menu.js +81 -17
  30. package/lib/common/src/menu/menuMouse.d.ts +35 -0
  31. package/lib/common/src/menu/menuMouse.js +113 -4
  32. package/lib/common/src/styles/tokens.d.ts +79 -1
  33. package/lib/common/src/styles/tokens.js +79 -1
  34. package/lib/common/src/utils/memoryStore.d.ts +1 -10
  35. package/lib/common/src/utils/searchHistory.js +4 -3
  36. package/lib/style/style.css +122 -50
  37. package/lib/style/tokens.css +76 -2
  38. package/package.json +49 -49
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useLayoutEffect, useRef, useState } from 'react';
3
3
  import { listen as createListen } from 'wenay-common2';
4
- import { renderBy, updateBy } from '../../../updateBy';
4
+ import { createUpdateApi } from '../../../updateBy';
5
5
  import { memoryGetOrCreate, memoryMarkDirty } from '../../utils/memoryStore';
6
6
  import { OutsideClickArea } from '../../hooks/useOutside';
7
7
  import { useReorder } from '../../hooks/useReorder';
@@ -12,6 +12,7 @@ const densities = { list: [
12
12
  { key: 'icon', name: 'Icons', renderItem: itemIconOnly },
13
13
  { key: 'label', name: 'Icons + labels' },
14
14
  ] };
15
+ const densitiesApi = createUpdateApi(densities);
15
16
  /** Icon with a text fallback: no glyph = the first letters of short/title
16
17
  * (an "PR"-style pseudo-icon), so icon densities work for icon-less items. */
17
18
  export function toolbarItemIcon(item) {
@@ -30,12 +31,12 @@ export function registerToolbarDensity(d) {
30
31
  densities.list.push(d);
31
32
  else
32
33
  densities.list.splice(i, 1, d);
33
- renderBy(densities);
34
+ densitiesApi.render();
34
35
  return function offToolbarDensity() {
35
36
  const j = densities.list.indexOf(d);
36
37
  if (j != -1) {
37
38
  densities.list.splice(j, 1);
38
- renderBy(densities);
39
+ densitiesApi.render();
39
40
  }
40
41
  };
41
42
  }
@@ -55,6 +56,49 @@ function itemContent(item, densityKey) {
55
56
  * order (the gear always sits at the bar edge), but toggleable like an item. */
56
57
  const SETTINGS_KEY = '__settings';
57
58
  const RESET_KEY = '__reset';
59
+ function useToolbarFlip(layoutKey) {
60
+ const itemRefs = useRef(new Map());
61
+ const prevRects = useRef(new Map());
62
+ const rafs = useRef([]);
63
+ useLayoutEffect(() => {
64
+ const prev = prevRects.current;
65
+ const next = new Map();
66
+ rafs.current.forEach(cancelAnimationFrame);
67
+ rafs.current = [];
68
+ itemRefs.current.forEach((node, key) => {
69
+ const rect = node.getBoundingClientRect();
70
+ next.set(key, { left: rect.left, top: rect.top });
71
+ const old = prev.get(key);
72
+ if (old == null)
73
+ return;
74
+ const dx = old.left - rect.left;
75
+ const dy = old.top - rect.top;
76
+ if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5)
77
+ return;
78
+ node.style.transition = 'none';
79
+ node.style.transform = `translate(${dx}px, ${dy}px)`;
80
+ node.getBoundingClientRect();
81
+ const raf = requestAnimationFrame(() => {
82
+ node.style.transition = 'transform 180ms ease';
83
+ node.style.transform = '';
84
+ });
85
+ rafs.current.push(raf);
86
+ });
87
+ prevRects.current = next;
88
+ return () => {
89
+ rafs.current.forEach(cancelAnimationFrame);
90
+ rafs.current = [];
91
+ };
92
+ }, [layoutKey]);
93
+ return function bind(key) {
94
+ return (node) => {
95
+ if (node)
96
+ itemRefs.current.set(key, node);
97
+ else
98
+ itemRefs.current.delete(key);
99
+ };
100
+ };
101
+ }
58
102
  export function createToolbar(opts) {
59
103
  const resetDefaultVisible = () => opts.resetItem !== false && opts.resetItem?.defaultVisible === true;
60
104
  const defConfig = () => ({
@@ -63,6 +107,7 @@ export function createToolbar(opts) {
63
107
  density: opts.def?.density ?? densities.list[0].key,
64
108
  });
65
109
  const st = memoryGetOrCreate(opts.key, defConfig());
110
+ const stApi = createUpdateApi(st);
66
111
  const [emitChange, onChange] = createListen();
67
112
  // ext is fixed for the controller's lifetime, so hook call order inside
68
113
  // useConfig/Bar/Settings never changes for a given toolbar instance
@@ -137,7 +182,7 @@ export function createToolbar(opts) {
137
182
  [RESET_KEY]: metaVisible(next, RESET_KEY, resetDefaultVisible()),
138
183
  };
139
184
  st.density = next.density;
140
- renderBy(st);
185
+ stApi.render();
141
186
  memoryMarkDirty(opts.key);
142
187
  ext.setConfig({ order: next.order.slice(), visible });
143
188
  // the source's own onChange already re-emitted; emit only when it can't
@@ -153,7 +198,7 @@ export function createToolbar(opts) {
153
198
  st.order = next.order.slice();
154
199
  st.visible = { ...next.visible };
155
200
  st.density = next.density;
156
- renderBy(st);
201
+ stApi.render();
157
202
  memoryMarkDirty(opts.key);
158
203
  const orderChanged = !sameOrder(curSourceOrder, nextSourceOrder);
159
204
  if (orderChanged)
@@ -168,7 +213,7 @@ export function createToolbar(opts) {
168
213
  st.order = next.order.slice();
169
214
  st.visible = { ...next.visible };
170
215
  st.density = next.density;
171
- renderBy(st);
216
+ stApi.render();
172
217
  memoryMarkDirty(opts.key);
173
218
  emitChange(normalize());
174
219
  }
@@ -176,7 +221,7 @@ export function createToolbar(opts) {
176
221
  const getConfig = () => normalize();
177
222
  /** subscription to everything the toolbar renders from (hook) */
178
223
  function useSubscribe() {
179
- updateBy(st);
224
+ stApi.use();
180
225
  ext?.useConfig(); // ext is per-instance constant - hook order is stable
181
226
  }
182
227
  function useConfig() {
@@ -191,7 +236,7 @@ export function createToolbar(opts) {
191
236
  * someone else's nodes.) */
192
237
  function useItems() {
193
238
  useSubscribe();
194
- updateBy(densities);
239
+ densitiesApi.use();
195
240
  const cfg = normalize();
196
241
  const byKey = new Map(opts.items.map(i => [i.key, i]));
197
242
  return cfg.order
@@ -238,18 +283,18 @@ export function createToolbar(opts) {
238
283
  * the popover sticks to - 'right' (default, for bars in a top-right corner)
239
284
  * or 'left'. */
240
285
  function Bar(p = {}) {
241
- useSubscribe();
242
- updateBy(densities);
286
+ const items = useItems();
243
287
  const cfg = normalize();
244
288
  const [open, setOpen] = useState(false);
245
- const byKey = new Map(opts.items.map(i => [i.key, i]));
246
289
  const resetOn = opts.resetItem !== false && (p.reset ?? p.settings ?? false) && cfg.visible[RESET_KEY] != false;
247
- return _jsxs("div", { className: p.className ?? 'wenayTb', children: [cfg.order.map(k => {
248
- const it = byKey.get(k);
249
- if (!it || cfg.visible[k] == false)
250
- return null;
251
- return _jsx("div", { className: 'wenayTbItem', title: cfg.density == 'icon' ? it.title : undefined, onClick: it.onClick, children: itemContent(it, cfg.density) }, k);
252
- }), resetOn && _jsx("div", { className: 'wenayTbItem', title: resetTitle(), onClick: () => reset(), children: resetIcon() }), p.settings && cfg.visible[SETTINGS_KEY] != false && _jsxs(OutsideClickArea, { className: 'wenayTbGear', status: open, outsideClick: () => setOpen(false), children: [_jsx("div", { className: 'wenayTbItem', title: settingsTitle(), onClick: () => setOpen(v => !v), children: settingsIcon() }), open && _jsx("div", { className: p.popAlign == 'left' ? 'wenayTbPop wenayTbPopLeft' : 'wenayTbPop', children: _jsx(Settings, {}) })] })] });
290
+ const settingsOn = !!p.settings && cfg.visible[SETTINGS_KEY] != false;
291
+ const layoutKey = [
292
+ ...items.map(x => `${x.item.key}:${x.density}`),
293
+ resetOn ? `${RESET_KEY}:on` : '',
294
+ settingsOn ? `${SETTINGS_KEY}:on` : '',
295
+ ].join('|');
296
+ const bindFlip = useToolbarFlip(layoutKey);
297
+ return _jsxs("div", { className: p.className ?? 'wenayTb', children: [items.map(x => _jsx("div", { ref: bindFlip(x.item.key), className: 'wenayTbItem', title: x.density == 'icon' ? x.item.title : undefined, onClick: x.item.onClick, children: x.content }, x.item.key)), resetOn && _jsx("div", { ref: bindFlip(RESET_KEY), className: 'wenayTbItem', title: resetTitle(), onClick: () => reset(), children: resetIcon() }), settingsOn && _jsxs(OutsideClickArea, { className: 'wenayTbGear', status: open, outsideClick: () => setOpen(false), children: [_jsx("div", { ref: bindFlip(SETTINGS_KEY), className: 'wenayTbItem', title: settingsTitle(), onClick: () => setOpen(v => !v), children: settingsIcon() }), open && _jsx("div", { className: p.popAlign == 'left' ? 'wenayTbPop wenayTbPopLeft' : 'wenayTbPop', children: _jsx(Settings, {}) })] })] });
253
298
  }
254
299
  /** The pure editor over config: density segments + one row per item
255
300
  * (visibility checkbox, icon preview, title, drag handle). Reorder rides the
@@ -260,7 +305,7 @@ export function createToolbar(opts) {
260
305
  * Plus arrow keys on the focused handle; fixed rows have neither. */
261
306
  function Settings(p = {}) {
262
307
  useSubscribe();
263
- updateBy(densities);
308
+ densitiesApi.use();
264
309
  const cfg = normalize();
265
310
  const base = p.className ?? 'wenaySegBtn';
266
311
  const activeCls = p.activeClassName ?? 'wenaySegBtnActive';
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- import { renderBy, updateBy } from "../../../updateBy";
2
+ import { createUpdateApi } from "../../../updateBy";
3
3
  import { memoryGetOrCreate, memoryMarkDirty } from "../../utils/memoryStore";
4
4
  /** One UI block shown in exactly one of several mount points; the point is a persisted setting.
5
5
  * Persistence rides the existing memoryProps -> memoryCache mechanics: memoryProps is observable,
@@ -9,6 +9,7 @@ import { memoryGetOrCreate, memoryMarkDirty } from "../../utils/memoryStore";
9
9
  * the persisted place with its own and renders children only on a match. */
10
10
  export function createUiSlot(opts) {
11
11
  const st = memoryGetOrCreate(opts.key, { place: opts.def });
12
+ const stApi = createUpdateApi(st);
12
13
  // a stored place may no longer exist after an app update - fall back to def
13
14
  const isPlace = (p) => typeof p == "string" && p in opts.places;
14
15
  const getPlace = () => isPlace(st.place) ? st.place : opts.def;
@@ -16,16 +17,16 @@ export function createUiSlot(opts) {
16
17
  if (st.place == p)
17
18
  return;
18
19
  st.place = p;
19
- renderBy(st);
20
+ stApi.render();
20
21
  memoryMarkDirty(opts.key);
21
22
  };
22
23
  function Slot(p) {
23
- updateBy(st);
24
+ stApi.use();
24
25
  return getPlace() == p.place ? _jsx(_Fragment, { children: p.children }) : null;
25
26
  }
26
27
  /** Segmented row over opts.places; apps pass their own .chip / .chipActive classes */
27
28
  function PlacementSetting(p = {}) {
28
- updateBy(st);
29
+ stApi.use();
29
30
  const cur = getPlace();
30
31
  const base = p.className ?? "wenaySegBtn";
31
32
  const activeCls = p.activeClassName ?? "wenaySegBtnActive";
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import type { ColumnStateController } from './columnState';
3
3
  export declare function ColumnDots(p: {
4
4
  state: ColumnStateController;
5
- /** max simultaneous dots (default 4) */
5
+ /** max simultaneous dots (default 8) */
6
6
  max?: number;
7
7
  className?: string;
8
8
  style?: React.CSSProperties;
@@ -25,7 +25,7 @@ export function ColumnDots(p) {
25
25
  const cfg = p.state.api.useConfig();
26
26
  const cols = p.state.columns;
27
27
  const byKey = new Map(cols.map(c => [c.key, c]));
28
- const max = p.max ?? 4;
28
+ const max = p.max ?? 8;
29
29
  const order = cfg.order;
30
30
  const n = order.length;
31
31
  const visibleKeys = order.filter(k => cfg.visible[k] != false);
@@ -22,22 +22,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
22
22
  // comes back to life when the column returns. No ag-grid, no storage here.
23
23
  import { useRef } from 'react';
24
24
  import { useReorder } from '../../hooks/useReorder';
25
+ function colsMenuClass(parts) {
26
+ return parts.filter(Boolean).join(' ');
27
+ }
25
28
  function MenuButton(p) {
26
29
  const it = p.item;
27
30
  const disabled = it.state == 'disabled';
28
31
  const on = it.state == 'on';
32
+ const stateClass = disabled ? 'wenayColsMenuBtn_disabled' : on ? 'wenayColsMenuBtn_on' : 'wenayColsMenuBtn_off';
29
33
  const abbr = p.compact && it.icon == null
30
- ? _jsx("span", { style: { fontSize: 10, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase' }, children: (it.short ?? it.title).slice(0, 3) }) : null;
31
- return _jsxs("div", { title: it.title, onClick: disabled ? undefined : e => p.onItem?.(it.key, e), onMouseDown: p.drag?.onMouseDown, onTouchStart: p.drag?.onTouchStart, style: {
32
- position: 'relative', display: 'flex', alignItems: 'center', gap: 4,
33
- padding: '3px 9px', borderRadius: 6, fontSize: 12, lineHeight: '16px',
34
- whiteSpace: 'nowrap', cursor: disabled ? 'default' : 'pointer',
35
- ...(disabled ? { border: '1px dashed #d0d7de', background: '#f6f8fa', color: '#c4ccd4' }
36
- : on ? { border: '1px solid #24292f', background: '#24292f', color: '#fff' }
37
- : { border: '1px solid #d0d7de', background: '#fff', color: '#8c959f', textDecoration: 'line-through' }),
38
- ...(it.fixed ? { boxShadow: '0 0 0 2px #eaeef2' } : null),
39
- ...p.style,
40
- }, children: [it.icon != null && _jsx("span", { style: { display: 'inline-flex', alignItems: 'center' }, children: it.icon }), abbr, !p.compact && _jsx("span", { children: it.short ?? it.title }), it.marks != null && _jsx("span", { style: { fontSize: 9, opacity: 0.9 }, children: it.marks })] });
34
+ ? _jsx("span", { className: 'wenayColsMenuAbbr', children: (it.short ?? it.title).slice(0, 3) }) : null;
35
+ return _jsxs("div", { title: it.title, onClick: disabled ? undefined : e => p.onItem?.(it.key, e), onMouseDown: p.drag?.onMouseDown, onTouchStart: p.drag?.onTouchStart, className: colsMenuClass(['wenayColsMenuBtn', stateClass, it.fixed && 'wenayColsMenuBtn_fixed', p.className]), style: p.style, children: [it.icon != null && _jsx("span", { className: 'wenayColsMenuIcon', children: it.icon }), abbr, !p.compact && _jsx("span", { className: 'wenayColsMenuLabel', children: it.short ?? it.title }), it.marks != null && _jsx("span", { className: 'wenayColsMenuMarks', children: it.marks })] });
41
36
  }
42
37
  /** The presentation layer: renders the buttons in the given order, reports
43
38
  * clicks (onItem) and drag-reorders (onMove) - never interprets either.
@@ -61,7 +56,7 @@ export function MenuStrip(p) {
61
56
  return;
62
57
  p.onItem?.(key, e);
63
58
  }
64
- return _jsxs("div", { className: p.className, style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap', userSelect: 'none', ...p.style }, children: [_jsx("div", { ref: reorder.listRef, style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }, children: p.items.map(it => {
59
+ return _jsxs("div", { className: colsMenuClass(['wenayColsMenu', p.className]), style: p.style, children: [_jsx("div", { ref: reorder.listRef, className: 'wenayColsMenuList', children: p.items.map(it => {
65
60
  const r = reorder.item(it.key);
66
61
  const drag = {
67
62
  onMouseDown: (e) => {
@@ -70,12 +65,9 @@ export function MenuStrip(p) {
70
65
  },
71
66
  onTouchStart: r.props.onTouchStart,
72
67
  };
73
- return _jsx(MenuButton, { item: it, onItem: onItem, drag: drag, compact: p.compact, style: {
74
- ...r.style,
75
- ...(r.dragging ? { zIndex: 3, boxShadow: '0 3px 10px rgba(0,0,0,0.35)' }
76
- : r.active ? { transition: 'transform 0.15s ease' } : null),
77
- } }, it.key);
78
- }) }), !!p.tail?.length && _jsxs(_Fragment, { children: [_jsx("div", { style: { width: 1, alignSelf: 'stretch', margin: '1px 2px', background: '#d0d7de' } }), p.tail.map(it => _jsx(MenuButton, { item: it, onItem: p.onItem, compact: p.compact }, it.key))] })] });
68
+ const dragClass = r.dragging ? 'wenayColsMenuBtn_dragging' : r.active ? 'wenayColsMenuBtn_shift' : undefined;
69
+ return _jsx(MenuButton, { item: it, onItem: onItem, drag: drag, compact: p.compact, className: dragClass, style: r.style }, it.key);
70
+ }) }), !!p.tail?.length && _jsxs(_Fragment, { children: [_jsx("div", { className: 'wenayColsMenuDivider' }), p.tail.map(it => _jsx(MenuButton, { item: it, onItem: p.onItem, compact: p.compact }, it.key))] })] });
79
71
  }
80
72
  /** The binding layer: MenuStrip fed by a ColumnStateController. Buttons follow
81
73
  * config.order (the grid mirror is free: both sides subscribe to the same
@@ -1,5 +1,5 @@
1
1
  import { listen as createListen } from 'wenay-common2';
2
- import { renderBy, updateBy } from '../../../updateBy';
2
+ import { createUpdateApi } from '../../../updateBy';
3
3
  import { memoryGetOrCreate, memoryMarkDirty } from '../../utils/memoryStore';
4
4
  const SCHEMA_V = 1;
5
5
  /** Grid events that mean "the user changed the column layout / sort / filter". */
@@ -17,6 +17,7 @@ export function createColumnState(opts) {
17
17
  groups: opts.def?.groups ? { ...opts.def.groups } : Object.fromEntries(groupKeys.map(g => [g, groupMembers(g)])),
18
18
  });
19
19
  const st = memoryGetOrCreate(opts.key, defConfig());
20
+ const stApi = createUpdateApi(st);
20
21
  const [emitChange, onChange] = createListen();
21
22
  /** Runtime-only, never persisted: actual = which keys the attached grid has;
22
23
  * gate = optional app-level availability over a stable grid schema (standards,
@@ -25,6 +26,7 @@ export function createColumnState(opts) {
25
26
  present: null,
26
27
  presentGate: null,
27
28
  };
29
+ const rtApi = createUpdateApi(rt);
28
30
  const keyMap = (keys) => keys ? Object.fromEntries(keys.map(k => [k, true])) : null;
29
31
  const sameMap = (a, b) => JSON.stringify(a) == JSON.stringify(b);
30
32
  function combinedPresent() {
@@ -42,14 +44,14 @@ export function createColumnState(opts) {
42
44
  if (sameMap(next, rt.present))
43
45
  return;
44
46
  rt.present = next;
45
- renderBy(rt);
47
+ rtApi.render();
46
48
  }
47
49
  function setPresentGate(keys) {
48
50
  const next = keyMap(keys);
49
51
  if (sameMap(next, rt.presentGate))
50
52
  return;
51
53
  rt.presentGate = next;
52
- renderBy(rt);
54
+ rtApi.render();
53
55
  applyToGrid();
54
56
  }
55
57
  const getPresent = combinedPresent;
@@ -60,7 +62,7 @@ export function createColumnState(opts) {
60
62
  };
61
63
  const passesPresentGate = (key) => !rt.presentGate || rt.presentGate[key] == true;
62
64
  function usePresent() {
63
- updateBy(rt);
65
+ rtApi.use();
64
66
  return combinedPresent();
65
67
  }
66
68
  /** The persisted state may be stale or partial (older app version, columns
@@ -116,7 +118,7 @@ export function createColumnState(opts) {
116
118
  st.sort = next.sort ? { ...next.sort } : null;
117
119
  st.filter = { ...next.filter };
118
120
  st.groups = Object.fromEntries(Object.entries(next.groups).map(([g, keys]) => [g, keys.slice()]));
119
- renderBy(st);
121
+ stApi.render();
120
122
  memoryMarkDirty(opts.key);
121
123
  if (!fromGrid)
122
124
  applyToGrid();
@@ -126,7 +128,7 @@ export function createColumnState(opts) {
126
128
  const setConfig = (next) => commit(next, false);
127
129
  const reset = () => commit(defConfig(), false);
128
130
  function useConfig() {
129
- updateBy(st);
131
+ stApi.use();
130
132
  return normalize();
131
133
  }
132
134
  function show(key, on) {
@@ -1,9 +1,10 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { renderBy } from "../../updateBy";
2
+ import { createUpdateApi } from "../../updateBy";
3
3
  import { listen as createListen } from "wenay-common2";
4
4
  export const keyboardState = {
5
5
  key: ""
6
6
  };
7
+ const keyboardStateApi = createUpdateApi(keyboardState);
7
8
  const [emitKeyDown, keyboardListen] = createListen();
8
9
  export const keyboard = {
9
10
  get key() { return keyboardState.key; },
@@ -11,7 +12,7 @@ export const keyboard = {
11
12
  get() { return keyboardState.key; },
12
13
  clear() {
13
14
  keyboardState.key = "";
14
- renderBy(keyboardState);
15
+ keyboardStateApi.render();
15
16
  emitKeyDown("", undefined);
16
17
  },
17
18
  reset() {
@@ -38,7 +39,7 @@ export function useKeyboard(options = {}) {
38
39
  if (!(event instanceof KeyboardEvent))
39
40
  return;
40
41
  keyboardState.key = event.key;
41
- renderBy(keyboardState);
42
+ keyboardStateApi.render();
42
43
  emitKeyDown(event.key, event);
43
44
  onKeyDownRef.current?.(event.key, event);
44
45
  };
@@ -1,92 +1,12 @@
1
- import { Params } from "wenay-common2";
2
- type LogInput<T extends object> = T & {
3
- id: string;
4
- var?: number;
5
- time: Date;
6
- txt: string;
7
- };
8
- declare const getSettingLogs: () => {
9
- minVarLogs: {
10
- name: string;
11
- range: {
12
- min: number;
13
- max: number;
14
- step: number;
15
- };
16
- value: number;
17
- };
18
- minVarMessage: {
19
- name: string;
20
- range: {
21
- min: number;
22
- max: number;
23
- step: number;
24
- };
25
- value: number;
26
- };
27
- timeShow: {
28
- name: string;
29
- range: {
30
- min: number;
31
- max: number;
32
- step: number;
33
- };
34
- value: number;
35
- };
36
- show: {
37
- name: string;
38
- value: boolean;
39
- };
40
- };
41
- export declare function getLogsApi<T extends object = {}>(setting: {
42
- limit?: number;
43
- limitPer: number;
44
- varMin?: number;
45
- }): {
46
- addLogs(a: LogInput<T>): void;
1
+ import { getSettingLogs, type LogEntry, type LogsApiOptions } from "./logsController";
2
+ export { createLogsController, createLogsControllerState, getSettingLogs, } from "./logsController";
3
+ export type { CreateLogsControllerOptions, LogsApiOptions, LogsController, LogsControllerState, LogsFullState, LogsMiniState, } from "./logsController";
4
+ export declare function getLogsApi<T extends object = {}>(setting: LogsApiOptions): {
5
+ addLogs: (input: import("./logsController").LogInput<T>) => LogEntry<T>;
47
6
  params: {
48
- def: () => {
49
- minVarLogs: {
50
- name: string;
51
- range: {
52
- min: number;
53
- max: number;
54
- step: number;
55
- };
56
- value: number;
57
- };
58
- minVarMessage: {
59
- name: string;
60
- range: {
61
- min: number;
62
- max: number;
63
- step: number;
64
- };
65
- value: number;
66
- };
67
- timeShow: {
68
- name: string;
69
- range: {
70
- min: number;
71
- max: number;
72
- step: number;
73
- };
74
- value: number;
75
- };
76
- show: {
77
- name: string;
78
- value: boolean;
79
- };
80
- };
81
- get(): {
82
- minVarLogs: number;
83
- minVarMessage: number;
84
- timeShow: number;
85
- show: boolean;
86
- } & {
87
- readonly [key: number]: void;
88
- };
89
- set(a: Params.SimpleParams<ReturnType<typeof getSettingLogs>>): void;
7
+ def: typeof getSettingLogs;
8
+ get(): import("./logsController").LogsSettings;
9
+ set(settings: import("./logsController").LogsSettings): void;
90
10
  };
91
11
  React: {
92
12
  Setting: typeof InputSettingLogs;
@@ -95,55 +15,16 @@ export declare function getLogsApi<T extends object = {}>(setting: {
95
15
  };
96
16
  };
97
17
  export declare const logsApi: {
98
- addLogs(a: {
18
+ addLogs: (input: {
99
19
  id: string;
100
20
  var?: number;
101
21
  time: Date;
102
22
  txt: string;
103
- }): void;
23
+ }) => LogEntry<{}>;
104
24
  params: {
105
- def: () => {
106
- minVarLogs: {
107
- name: string;
108
- range: {
109
- min: number;
110
- max: number;
111
- step: number;
112
- };
113
- value: number;
114
- };
115
- minVarMessage: {
116
- name: string;
117
- range: {
118
- min: number;
119
- max: number;
120
- step: number;
121
- };
122
- value: number;
123
- };
124
- timeShow: {
125
- name: string;
126
- range: {
127
- min: number;
128
- max: number;
129
- step: number;
130
- };
131
- value: number;
132
- };
133
- show: {
134
- name: string;
135
- value: boolean;
136
- };
137
- };
138
- get(): {
139
- minVarLogs: number;
140
- minVarMessage: number;
141
- timeShow: number;
142
- show: boolean;
143
- } & {
144
- readonly [key: number]: void;
145
- };
146
- set(a: Params.SimpleParams<ReturnType<typeof getSettingLogs>>): void;
25
+ def: typeof getSettingLogs;
26
+ get(): import("./logsController").LogsSettings;
27
+ set(settings: import("./logsController").LogsSettings): void;
147
28
  };
148
29
  React: {
149
30
  Setting: typeof InputSettingLogs;
@@ -163,4 +44,3 @@ export declare function MessageEventLogs({ zIndex }: {
163
44
  export declare function LogsPage({ update }: {
164
45
  update?: number;
165
46
  }): import("react/jsx-runtime").JSX.Element;
166
- export {};
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { AgGridReact } from "ag-grid-react";
3
2
  import { useCallback, useEffect, useRef } from "react";
4
3
  import { copyToClipboard, Params, timeLocalToStr_hhmmss } from "wenay-common2";
5
4
  import { renderBy, updateBy } from "../../updateBy";
@@ -7,6 +6,9 @@ import { contextMenu } from "../menu/menuMouse";
7
6
  import { memoryGetOrCreate } from "../utils/memoryStore";
8
7
  import { ParamsEditor } from "../components/ParamsEditor";
9
8
  import { logDividerGradient, logSeverityBackground, logStyleTokens } from "./logStyles";
9
+ import { AgGridTable, colDefCentered } from "../grid/agGrid4";
10
+ import { createLogsController, createLogsControllerState, getSettingLogs, } from "./logsController";
11
+ export { createLogsController, createLogsControllerState, getSettingLogs, } from "./logsController";
10
12
  const cashLogs = new Map();
11
13
  const datumConst = {
12
14
  map: cashLogs,
@@ -14,39 +16,25 @@ const datumConst = {
14
16
  const datumMiniConst = {
15
17
  last: []
16
18
  };
17
- const getSettingLogs = () => ({
18
- minVarLogs: { name: "min. importance for notifications", range: { min: 0, max: 25, step: 1 }, value: 0 },
19
- minVarMessage: { name: "min. importance for log table", range: { min: 0, max: 25, step: 1 }, value: 0 },
20
- timeShow: { name: "screen display time", range: { min: 1, max: 20, step: 1 }, value: 2 },
21
- show: { name: "show", value: true }
22
- });
23
19
  const settingLogs = { params: Params.GetSimpleParams(getSettingLogs()) };
20
+ const logGridDefaultColDef = { ...colDefCentered, wrapText: true };
24
21
  // varMin - minimum importance
25
22
  export function getLogsApi(setting) {
26
23
  const datum = memoryGetOrCreate("settingLogs", settingLogs);
27
- function addToArr(arr, data, limit) {
28
- arr.unshift(data);
29
- if (arr.length > limit)
30
- arr.length = limit;
31
- }
32
- let num = 0;
24
+ const controller = createLogsController({
25
+ options: setting,
26
+ state: createLogsControllerState({
27
+ full: datumConst,
28
+ mini: datumMiniConst,
29
+ settings: datum,
30
+ }),
31
+ onFullChange: () => renderBy(datumConst),
32
+ onMiniChange: () => renderBy(datumMiniConst),
33
+ onSettingsChange: () => renderBy(datum),
34
+ });
33
35
  return {
34
- addLogs(a) {
35
- const item = { ...a, num: num++ };
36
- addToArr(datumMiniConst.last, item, setting.limit ?? 50);
37
- addToArr(datumConst.map.get(a.id) ?? datumConst.map.set(a.id, []).get(a.id), item, setting.limitPer);
38
- renderBy(datumConst);
39
- renderBy(datumMiniConst);
40
- },
41
- params: {
42
- def: getSettingLogs,
43
- get() { return datum.params; },
44
- set(a) {
45
- datum.params = a;
46
- renderBy(datumMiniConst);
47
- renderBy(datumConst);
48
- },
49
- },
36
+ addLogs: controller.addLogs,
37
+ params: controller.params,
50
38
  React: {
51
39
  Setting: InputSettingLogs,
52
40
  Message: MessageEventLogs,
@@ -136,7 +124,7 @@ export function PageLogs({ update }) {
136
124
  width: 150,
137
125
  },
138
126
  ];
139
- return _jsx("div", { className: "maxSize", children: _jsx(AgGridReact
127
+ return _jsx("div", { className: "maxSize", children: _jsx(AgGridTable
140
128
  // className = "ag-theme-alpine-dark ag-theme-alpine2" // ag-theme-alpine-dark3
141
129
  , {
142
130
  // className = "ag-theme-alpine-dark ag-theme-alpine2" // ag-theme-alpine-dark3
@@ -153,18 +141,11 @@ export function PageLogs({ update }) {
153
141
  });
154
142
  }
155
143
  }, onSortChanged: (e) => {
156
- }, defaultColDef: {
157
- headerClass: () => ("gridTable-header"),
158
- resizable: true,
159
- cellStyle: { textAlign: "center" },
160
- sortable: true,
161
- filter: true,
162
- wrapText: true,
163
- }, headerHeight: 30, rowHeight: 26, autoSizePadding: 1, rowData: rowData, columnDefs: columns, onCellMouseDown: (e) => {
144
+ }, defaultColDef: logGridDefaultColDef, headerHeight: 30, rowHeight: 26, autoSizePadding: 1, rowData: rowData, columnDefs: columns, onCellMouseDown: (e) => {
164
145
  if (e.event instanceof MouseEvent && e.event.button == 2) {
165
146
  contextMenu.openAt(e.event, [
166
147
  {
167
- name: "copy", onClick: () => { copyToClipboard(e.value); }
148
+ name: "copy", actionKey: "logs.copyCell", onClick: () => { copyToClipboard(e.value); }
168
149
  }
169
150
  ]);
170
151
  }