wenay-react2 1.0.39 → 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 (29) 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/Menu/RightMenu.d.ts +28 -1
  4. package/lib/common/src/components/Menu/RightMenu.js +62 -35
  5. package/lib/common/src/components/Modal/LeftModal.js +5 -4
  6. package/lib/common/src/components/Modal/Modal.js +8 -7
  7. package/lib/common/src/components/ParamsEditor.d.ts +15 -0
  8. package/lib/common/src/components/ParamsEditor.js +45 -18
  9. package/lib/common/src/components/Settings/SettingsDialog.d.ts +68 -6
  10. package/lib/common/src/components/Settings/SettingsDialog.js +61 -12
  11. package/lib/common/src/components/Toolbar/Toolbar.js +64 -19
  12. package/lib/common/src/components/UiSlot/UiSlot.js +5 -4
  13. package/lib/common/src/grid/columnState/columnState.js +8 -6
  14. package/lib/common/src/hooks/useKeyboard.js +4 -3
  15. package/lib/common/src/logs/logs.d.ts +13 -133
  16. package/lib/common/src/logs/logs.js +20 -39
  17. package/lib/common/src/logs/logsContext.d.ts +26 -0
  18. package/lib/common/src/logs/logsContext.js +25 -20
  19. package/lib/common/src/logs/logsController.d.ts +88 -0
  20. package/lib/common/src/logs/logsController.js +54 -0
  21. package/lib/common/src/logs/miniLogs.d.ts +69 -3
  22. package/lib/common/src/logs/miniLogs.js +70 -15
  23. package/lib/common/src/menu/menu.d.ts +15 -3
  24. package/lib/common/src/menu/menu.js +81 -17
  25. package/lib/common/src/menu/menuMouse.d.ts +18 -0
  26. package/lib/common/src/menu/menuMouse.js +43 -1
  27. package/lib/common/src/utils/memoryStore.d.ts +1 -10
  28. package/lib/common/src/utils/searchHistory.js +4 -3
  29. package/package.json +49 -49
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { timeLocalToStr_hhmmss } from "wenay-common2";
3
- import { AgGridReact } from "ag-grid-react";
4
- const columns = [
3
+ import { useCallback, useMemo, useRef } from "react";
4
+ import { AgGridTable, colDefCentered } from "../grid/agGrid4";
5
+ export const miniLogsColumnDefs = [
5
6
  {
6
7
  field: "time",
7
8
  sort: "desc",
@@ -27,17 +28,71 @@ const columns = [
27
28
  width: 150,
28
29
  },
29
30
  ];
30
- export function MiniLogs({ data, onClick }) {
31
- return _jsx("div", { className: "maxSize", children: _jsx(AgGridReact, { suppressCellFocus: true, onGridReady: (a) => {
32
- a.api.sizeColumnsToFit();
33
- }, defaultColDef: {
34
- headerClass: () => ("gridTable-header"),
35
- resizable: true,
36
- cellStyle: { textAlign: "center" },
37
- sortable: true,
38
- filter: true,
39
- wrapText: true,
40
- }, headerHeight: 30, rowHeight: 26, autoSizePadding: 1, rowData: data, columnDefs: columns, onCellMouseDown: (e) => {
41
- onClick?.(e);
42
- } }) });
31
+ export const miniLogsDefaultColDef = { ...colDefCentered, wrapText: true };
32
+ export function useMiniLogsTable(options) {
33
+ const { data, onClick, columnDefs, defaultColDef: defaultColDefOverride } = options;
34
+ const apiRef = useRef(null);
35
+ const columns = useMemo(() => (columnDefs ?? miniLogsColumnDefs), [columnDefs]);
36
+ const defaultColDef = useMemo(() => ({ ...miniLogsDefaultColDef, ...defaultColDefOverride }), [defaultColDefOverride]);
37
+ const fit = useCallback(() => {
38
+ const api = apiRef.current;
39
+ if (!api)
40
+ return false;
41
+ api.sizeColumnsToFit();
42
+ return true;
43
+ }, []);
44
+ const getApi = useCallback(() => apiRef.current, []);
45
+ const withApi = useCallback(function withApi(fn) {
46
+ const api = apiRef.current;
47
+ return api ? fn(api) : undefined;
48
+ }, []);
49
+ const onGridReady = useCallback((e) => {
50
+ apiRef.current = e.api;
51
+ fit();
52
+ }, [fit]);
53
+ const onGridPreDestroyed = useCallback((_e) => {
54
+ apiRef.current = null;
55
+ }, []);
56
+ const onCellMouseDown = useCallback((e) => {
57
+ onClick?.(e);
58
+ }, [onClick]);
59
+ const props = useMemo(() => ({
60
+ suppressCellFocus: true,
61
+ onGridReady,
62
+ onGridPreDestroyed,
63
+ defaultColDef,
64
+ headerHeight: 30,
65
+ rowHeight: 26,
66
+ autoSizePadding: 1,
67
+ rowData: data,
68
+ columnDefs: columns,
69
+ onCellMouseDown,
70
+ }), [columns, data, defaultColDef, onCellMouseDown, onGridPreDestroyed, onGridReady]);
71
+ return useMemo(() => ({
72
+ rows: data,
73
+ columns,
74
+ columnDefs: columns,
75
+ defaultColDef,
76
+ apiRef,
77
+ fit,
78
+ getApi,
79
+ withApi,
80
+ onGridReady,
81
+ onGridPreDestroyed,
82
+ onCellMouseDown,
83
+ props,
84
+ tableProps: props,
85
+ gridProps: props,
86
+ }), [columns, data, defaultColDef, fit, getApi, onCellMouseDown, onGridPreDestroyed, onGridReady, props, withApi]);
87
+ }
88
+ export function MiniLogsView({ controller, className, style }) {
89
+ const classNames = ["maxSize", className].filter(Boolean).join(" ");
90
+ return _jsx("div", { className: classNames, style: style, children: _jsx(AgGridTable, { ...controller.props }) });
91
+ }
92
+ export function MiniLogsTable({ className, style, ...options }) {
93
+ const table = useMiniLogsTable(options);
94
+ return _jsx(MiniLogsView, { controller: table, className: className, style: style });
95
+ }
96
+ export function MiniLogs(props) {
97
+ return _jsx(MiniLogsTable, { ...props });
43
98
  }
@@ -4,6 +4,8 @@ import React, { ReactElement } from 'react';
4
4
  *******************************************************/
5
5
  export type MenuItemStrict<T = any> = {
6
6
  name: string | ((status?: T) => string);
7
+ /** Stable diagnostics key. Stats never fall back to visible labels. */
8
+ actionKey?: string | null;
7
9
  getStatus?: (() => T) | null;
8
10
  onClick?: ((e: any) => void | undefined | null | ((void | undefined | null | Promise<any> | (() => Promise<any>))[]) | Promise<any>) | null;
9
11
  active?: (() => boolean) | null;
@@ -14,6 +16,14 @@ export type MenuItemStrict<T = any> = {
14
16
  menuElement?: typeof MenuElement;
15
17
  };
16
18
  export type MenuItem<T = any> = MenuItemStrict<T> | false | null | undefined;
19
+ export type MenuActionEventType = "click" | "ok" | "error" | "taskOk" | "taskError" | "submenuOpen" | "submenuOk" | "submenuError" | "funcOpen" | "funcOk" | "funcError" | "focusOpen" | "focusOk" | "focusError";
20
+ export type MenuActionEvent = {
21
+ type: MenuActionEventType;
22
+ item: MenuItemStrict;
23
+ actionKey?: string;
24
+ error?: unknown;
25
+ };
26
+ export type MenuActionHandler = (event: MenuActionEvent) => void;
17
27
  /*******************************************************
18
28
  * Helper type
19
29
  *******************************************************/
@@ -31,12 +41,13 @@ declare function MenuProgress({ data }: {
31
41
  /*******************************************************
32
42
  * Main menu element with onClick and counters
33
43
  *******************************************************/
34
- declare function MenuElement({ data: item, toLeft, className, update, open, }: {
35
- data: Pick<MenuItemStrict, "onClick" | "active" | "name" | "getStatus">;
44
+ declare function MenuElement({ data: item, toLeft, className, update, open, onActionEvent, }: {
45
+ data: Pick<MenuItemStrict, "onClick" | "active" | "name" | "getStatus" | "actionKey">;
36
46
  toLeft: boolean;
37
47
  className?: (active?: boolean) => string;
38
48
  update: () => void;
39
49
  open?: boolean;
50
+ onActionEvent?: MenuActionHandler;
40
51
  }): ReactElement;
41
52
  /*******************************************************
42
53
  * Menu renders the popup menu with support for
@@ -62,6 +73,7 @@ type MenuProps = {
62
73
  data: MenuItem[];
63
74
  zIndex?: number;
64
75
  className?: (active?: boolean) => string;
76
+ onActionEvent?: MenuActionHandler;
65
77
  coordinate?: {
66
78
  x: number;
67
79
  y: number;
@@ -69,5 +81,5 @@ type MenuProps = {
69
81
  left?: number;
70
82
  };
71
83
  };
72
- export declare function Menu({ coordinate, data, zIndex, menu, className, menuElement, }: MenuProps): ReactElement;
84
+ export declare function Menu({ coordinate, data, zIndex, menu, className, menuElement, onActionEvent, }: MenuProps): ReactElement;
73
85
  export { MenuProgress, MenuElement };
@@ -29,7 +29,7 @@ function MenuProgress({ data }) {
29
29
  /*******************************************************
30
30
  * Main menu element with onClick and counters
31
31
  *******************************************************/
32
- function MenuElement({ data: item, toLeft, className, update, open, }) {
32
+ function MenuElement({ data: item, toLeft, className, update, open, onActionEvent, }) {
33
33
  const unsubOk = useRef(null);
34
34
  const unsubErr = useRef(null);
35
35
  useEffect(() => {
@@ -47,8 +47,18 @@ function MenuElement({ data: item, toLeft, className, update, open, }) {
47
47
  "MenuR " + (active ? "toButtonA" : "toButton"), style: { float: toLeft ? "left" : "right" }, onClick: () => {
48
48
  if (!item.onClick)
49
49
  return;
50
- const result = item?.onClick?.(item);
50
+ const actionKey = item.actionKey ?? undefined;
51
+ onActionEvent?.({ type: "click", item: item, actionKey });
52
+ let result;
53
+ try {
54
+ result = item.onClick(item);
55
+ }
56
+ catch (error) {
57
+ onActionEvent?.({ type: "error", item: item, actionKey, error });
58
+ throw error;
59
+ }
51
60
  if (!result) {
61
+ onActionEvent?.({ type: "ok", item: item, actionKey });
52
62
  update();
53
63
  return;
54
64
  }
@@ -72,8 +82,14 @@ function MenuElement({ data: item, toLeft, className, update, open, }) {
72
82
  setProgress(null);
73
83
  }
74
84
  };
75
- unsubOk.current = pa.onOk((data, i, countOk, countError, count) => onTick(countOk, countError, count));
76
- unsubErr.current = pa.onError((error, i, countOk, countError, count) => onTick(countOk, countError, count));
85
+ unsubOk.current = pa.onOk((data, i, countOk, countError, count) => {
86
+ onActionEvent?.({ type: "taskOk", item: item, actionKey });
87
+ return onTick(countOk, countError, count);
88
+ });
89
+ unsubErr.current = pa.onError((error, i, countOk, countError, count) => {
90
+ onActionEvent?.({ type: "taskError", item: item, actionKey, error });
91
+ return onTick(countOk, countError, count);
92
+ });
77
93
  void pa.allSettled();
78
94
  }
79
95
  // If this is a single promise
@@ -81,6 +97,7 @@ function MenuElement({ data: item, toLeft, className, update, open, }) {
81
97
  setProgress({});
82
98
  result
83
99
  .then(async (val) => {
100
+ onActionEvent?.({ type: "ok", item: item, actionKey });
84
101
  if (Array.isArray(val) && val.length) {
85
102
  // If an array from Promise.allSettled was returned
86
103
  // Count ok/error results
@@ -99,6 +116,10 @@ function MenuElement({ data: item, toLeft, className, update, open, }) {
99
116
  setProgress({ ok: 1 });
100
117
  await sleepAsync(0);
101
118
  }
119
+ })
120
+ .catch((error) => {
121
+ onActionEvent?.({ type: "error", item: item, actionKey, error });
122
+ throw error;
102
123
  })
103
124
  .finally(async () => {
104
125
  await sleepAsync(500);
@@ -106,73 +127,116 @@ function MenuElement({ data: item, toLeft, className, update, open, }) {
106
127
  });
107
128
  }
108
129
  else {
130
+ onActionEvent?.({ type: "ok", item: item, actionKey });
109
131
  update();
110
132
  }
111
133
  }, children: _jsxs("div", { className: "toLine", children: [typeof item.name === "string"
112
134
  ? item.name
113
135
  : item.name(item.getStatus?.()), progress && _jsx(MenuProgress, { data: progress })] }) }));
114
136
  }
115
- const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPos, menuElement, open, setOpenIndex, }) => {
137
+ const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPos, menuElement, open, setOpenIndex, onActionEvent, }) => {
116
138
  const [childMenu, setChildMenu] = useState([]);
117
139
  const [asyncFuncElement, setAsyncFuncElement] = useState(null);
118
140
  const [onFocusMenu, setOnFocusMenu] = useState([]);
119
141
  useEffect(() => {
120
142
  if (open && item.next) {
143
+ const actionKey = item.actionKey ?? undefined;
144
+ onActionEvent?.({ type: "submenuOpen", item, actionKey });
121
145
  let alive = true; // guard: do not set state after unmount or item change
122
- const result = item.next();
146
+ let result;
147
+ try {
148
+ result = item.next();
149
+ }
150
+ catch (error) {
151
+ onActionEvent?.({ type: "submenuError", item, actionKey, error });
152
+ throw error;
153
+ }
123
154
  if (result instanceof Promise) {
124
155
  result.then((val) => {
125
156
  if (alive)
126
157
  setChildMenu(val.filter(Boolean));
158
+ onActionEvent?.({ type: "submenuOk", item, actionKey });
159
+ }).catch((error) => {
160
+ onActionEvent?.({ type: "submenuError", item, actionKey, error });
161
+ throw error;
127
162
  });
128
163
  }
129
164
  else {
130
165
  setChildMenu(result.filter(Boolean));
166
+ onActionEvent?.({ type: "submenuOk", item, actionKey });
131
167
  }
132
168
  return () => { alive = false; };
133
169
  }
134
170
  else {
135
171
  setChildMenu([]);
136
172
  }
137
- }, [open, item.next]);
173
+ }, [open, item, item.next, onActionEvent]);
138
174
  useEffect(() => {
139
175
  if (open && item.func) {
176
+ const actionKey = item.actionKey ?? undefined;
177
+ onActionEvent?.({ type: "funcOpen", item, actionKey });
140
178
  let alive = true;
141
- const result = item.func();
179
+ let result;
180
+ try {
181
+ result = item.func();
182
+ }
183
+ catch (error) {
184
+ onActionEvent?.({ type: "funcError", item, actionKey, error });
185
+ throw error;
186
+ }
142
187
  if (result instanceof Promise) {
143
188
  result.then((val) => {
144
189
  if (alive)
145
190
  setAsyncFuncElement(val);
191
+ onActionEvent?.({ type: "funcOk", item, actionKey });
192
+ }).catch((error) => {
193
+ onActionEvent?.({ type: "funcError", item, actionKey, error });
194
+ throw error;
146
195
  });
147
196
  }
148
197
  else {
149
198
  setAsyncFuncElement(result);
199
+ onActionEvent?.({ type: "funcOk", item, actionKey });
150
200
  }
151
201
  return () => { alive = false; };
152
202
  }
153
203
  else {
154
204
  setAsyncFuncElement(null);
155
205
  }
156
- }, [open, item.func]);
206
+ }, [open, item, item.func, onActionEvent]);
157
207
  useEffect(() => {
158
208
  if (open && item.onFocus) {
209
+ const actionKey = item.actionKey ?? undefined;
210
+ onActionEvent?.({ type: "focusOpen", item, actionKey });
159
211
  let alive = true;
160
- const result = item.onFocus();
212
+ let result;
213
+ try {
214
+ result = item.onFocus();
215
+ }
216
+ catch (error) {
217
+ onActionEvent?.({ type: "focusError", item, actionKey, error });
218
+ throw error;
219
+ }
161
220
  if (result instanceof Promise) {
162
221
  result.then((val) => {
163
222
  if (alive)
164
223
  setOnFocusMenu(val.filter(Boolean));
224
+ onActionEvent?.({ type: "focusOk", item, actionKey });
225
+ }).catch((error) => {
226
+ onActionEvent?.({ type: "focusError", item, actionKey, error });
227
+ throw error;
165
228
  });
166
229
  }
167
230
  else {
168
231
  setOnFocusMenu(result.filter(Boolean));
232
+ onActionEvent?.({ type: "focusOk", item, actionKey });
169
233
  }
170
234
  return () => { alive = false; };
171
235
  }
172
236
  else {
173
237
  setOnFocusMenu([]);
174
238
  }
175
- }, [open, item.onFocus]);
239
+ }, [open, item, item.onFocus, onActionEvent]);
176
240
  const onMouseEnter = () => {
177
241
  if (open)
178
242
  return;
@@ -186,24 +250,24 @@ const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPo
186
250
  data: viewItem,
187
251
  className,
188
252
  update,
189
- }) ?? (_jsx(MenuElement, { toLeft: isLeftAligned, data: viewItem, className: className, update: update, open: open })), _jsxs("div", { children: [open && childMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { data: childMenu, coordinate: {
253
+ }) ?? (_jsx(MenuElement, { toLeft: isLeftAligned, data: viewItem, className: className, update: update, open: open, onActionEvent: onActionEvent })), _jsxs("div", { children: [open && childMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { data: childMenu, coordinate: {
190
254
  x: 3,
191
255
  y: 0,
192
256
  toLeft: isLeftAligned,
193
257
  left: leftPos,
194
- } }) })), open && asyncFuncElement && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { menu: () => asyncFuncElement, data: [], coordinate: {
258
+ }, onActionEvent: onActionEvent }) })), open && asyncFuncElement && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { menu: () => asyncFuncElement, data: [], coordinate: {
195
259
  x: 3,
196
260
  y: 0,
197
261
  toLeft: isLeftAligned,
198
262
  left: leftPos,
199
- } }) })), open && onFocusMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { data: onFocusMenu, coordinate: {
263
+ }, onActionEvent: onActionEvent }) })), open && onFocusMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { data: onFocusMenu, coordinate: {
200
264
  x: 3,
201
265
  y: 0,
202
266
  toLeft: isLeftAligned,
203
267
  left: leftPos,
204
- } }) }))] })] }));
268
+ }, onActionEvent: onActionEvent }) }))] })] }));
205
269
  };
206
- export function Menu({ coordinate = { x: 0, y: 0, toLeft: false, left: 0 }, data, zIndex, menu, className, menuElement, }) {
270
+ export function Menu({ coordinate = { x: 0, y: 0, toLeft: false, left: 0 }, data, zIndex, menu, className, menuElement, onActionEvent, }) {
207
271
  const [, forceUpdate] = useState(false);
208
272
  const update = () => forceUpdate((p) => !p);
209
273
  const refMenu = useRef(null);
@@ -257,6 +321,6 @@ export function Menu({ coordinate = { x: 0, y: 0, toLeft: false, left: 0 }, data
257
321
  ...alignStyle,
258
322
  }, children: menu
259
323
  ? menu(dataMemo)
260
- : dataMemo.map((item, i, arr) => (_jsx(MenuItemWrapper, { item: item, index: i, update: update, className: className, isLeftAligned: isLeftAligned, leftPos: leftPos, menuElement: menuElement, open: activeIndex === i, setOpenIndex: setActiveIndex }, typeof item.name === "string" ? item.name : i))) }));
324
+ : dataMemo.map((item, i, arr) => (_jsx(MenuItemWrapper, { item: item, index: i, update: update, className: className, isLeftAligned: isLeftAligned, leftPos: leftPos, menuElement: menuElement, open: activeIndex === i, setOpenIndex: setActiveIndex, onActionEvent: onActionEvent }, typeof item.name === "string" ? item.name : i))) }));
261
325
  }
262
326
  export { MenuProgress, MenuElement };
@@ -28,6 +28,22 @@ export type ContextMenuLayerProps = {
28
28
  onConsume?: () => void;
29
29
  className?: (active?: boolean) => string;
30
30
  };
31
+ export type ContextMenuActionCounters = {
32
+ click: number;
33
+ ok: number;
34
+ error: number;
35
+ taskOk: number;
36
+ taskError: number;
37
+ submenuOpen: number;
38
+ submenuOk: number;
39
+ submenuError: number;
40
+ funcOpen: number;
41
+ funcOk: number;
42
+ funcError: number;
43
+ focusOpen: number;
44
+ focusOk: number;
45
+ focusError: number;
46
+ };
31
47
  export type ContextMenuStatsSnapshot = {
32
48
  openAt: number;
33
49
  openAtPoint: number;
@@ -37,6 +53,8 @@ export type ContextMenuStatsSnapshot = {
37
53
  empty: number;
38
54
  sources: Record<string, number>;
39
55
  layers: Record<string, number>;
56
+ actionTotals: ContextMenuActionCounters;
57
+ actions: Record<string, ContextMenuActionCounters>;
40
58
  };
41
59
  export type ContextMenuStats = {
42
60
  getSnapshot(): ContextMenuStatsSnapshot;
@@ -2,6 +2,33 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { Menu } from "./menu";
4
4
  import { OutsideClickArea } from "../hooks/useOutside";
5
+ function createActionCounters() {
6
+ return {
7
+ click: 0,
8
+ ok: 0,
9
+ error: 0,
10
+ taskOk: 0,
11
+ taskError: 0,
12
+ submenuOpen: 0,
13
+ submenuOk: 0,
14
+ submenuError: 0,
15
+ funcOpen: 0,
16
+ funcOk: 0,
17
+ funcError: 0,
18
+ focusOpen: 0,
19
+ focusOk: 0,
20
+ focusError: 0,
21
+ };
22
+ }
23
+ function cloneActionCounters(counters) {
24
+ return { ...counters };
25
+ }
26
+ function cloneActions(actions) {
27
+ const result = {};
28
+ for (const [key, counters] of Object.entries(actions))
29
+ result[key] = cloneActionCounters(counters);
30
+ return result;
31
+ }
5
32
  function normalizeItems(items) {
6
33
  return (items ?? []).filter(Boolean);
7
34
  }
@@ -42,6 +69,8 @@ export function createContextMenu(data) {
42
69
  empty: 0,
43
70
  sources: {},
44
71
  layers: {},
72
+ actionTotals: createActionCounters(),
73
+ actions: {},
45
74
  };
46
75
  const layers = new Set();
47
76
  let layerSeq = 0;
@@ -64,6 +93,8 @@ export function createContextMenu(data) {
64
93
  empty: statsState.empty,
65
94
  sources: { ...statsState.sources },
66
95
  layers: { ...statsState.layers },
96
+ actionTotals: cloneActionCounters(statsState.actionTotals),
97
+ actions: cloneActions(statsState.actions),
67
98
  };
68
99
  }
69
100
  function emitStats() {
@@ -80,6 +111,15 @@ export function createContextMenu(data) {
80
111
  return;
81
112
  map[key] = (map[key] ?? 0) + 1;
82
113
  }
114
+ function recordMenuAction(event) {
115
+ const stat = event.type;
116
+ statsState.actionTotals[stat] += 1;
117
+ if (event.actionKey) {
118
+ const counters = statsState.actions[event.actionKey] ??= createActionCounters();
119
+ counters[stat] += 1;
120
+ }
121
+ emitStats();
122
+ }
83
123
  const stats = {
84
124
  getSnapshot: statsSnapshot,
85
125
  reset() {
@@ -91,6 +131,8 @@ export function createContextMenu(data) {
91
131
  statsState.empty = 0;
92
132
  statsState.sources = {};
93
133
  statsState.layers = {};
134
+ statsState.actionTotals = createActionCounters();
135
+ statsState.actions = {};
94
136
  emitStats();
95
137
  },
96
138
  onChange(cb) {
@@ -249,7 +291,7 @@ export function createContextMenu(data) {
249
291
  if (!state.open || hasQueuedItems(other))
250
292
  openQueued(event);
251
293
  }
252
- }, children: [children, state.open && enabled && state.layerId == layerId && _jsx(OutsideClickArea, { outsideClick: handleClose, children: _jsx(Menu, { className: className, data: state.items, coordinate: relativePoint(), zIndex: zIndex }) })] });
294
+ }, children: [children, state.open && enabled && state.layerId == layerId && _jsx(OutsideClickArea, { outsideClick: handleClose, children: _jsx(Menu, { className: className, data: state.items, coordinate: relativePoint(), zIndex: zIndex, onActionEvent: recordMenuAction }) })] });
253
295
  }
254
296
  return {
255
297
  bb,
@@ -27,16 +27,7 @@ export declare const memoryCache: {
27
27
  getArr: [k: string, v: Map<string, unknown>][];
28
28
  };
29
29
  export declare const memoryMaps: {
30
- rnd: ObservableMap<string, {
31
- position: {
32
- x: number;
33
- y: number;
34
- };
35
- size: {
36
- height: number | string;
37
- width: number | string;
38
- };
39
- }>;
30
+ rnd: ObservableMap<string, import("../components/Dnd/FloatingWindow").FloatingWindowSavedGeometry>;
40
31
  resize: ObservableMap<string, {
41
32
  height?: number | string;
42
33
  width?: number | string;
@@ -1,4 +1,4 @@
1
- import { renderBy, updateBy } from "../../updateBy";
1
+ import { createUpdateApi } from "../../updateBy";
2
2
  import { memoryGetOrCreate, memoryMarkDirty } from "./memoryStore";
3
3
  function normalizeSearchHistoryItem(value) {
4
4
  return value.replace(/\s+/g, " ").trim();
@@ -6,6 +6,7 @@ function normalizeSearchHistoryItem(value) {
6
6
  export function createSearchHistory(opts) {
7
7
  const max = Math.max(1, opts.max ?? 8);
8
8
  const st = memoryGetOrCreate(opts.key, { items: [] });
9
+ const stApi = createUpdateApi(st);
9
10
  function normalize() {
10
11
  const seen = new Set();
11
12
  st.items = st.items
@@ -23,7 +24,7 @@ export function createSearchHistory(opts) {
23
24
  }
24
25
  function emit() {
25
26
  normalize();
26
- renderBy(st);
27
+ stApi.render();
27
28
  memoryMarkDirty(opts.key);
28
29
  }
29
30
  return {
@@ -31,7 +32,7 @@ export function createSearchHistory(opts) {
31
32
  return normalize().slice();
32
33
  },
33
34
  use() {
34
- updateBy(st);
35
+ stApi.use();
35
36
  return normalize().slice();
36
37
  },
37
38
  add(value) {
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
- {
2
- "name": "wenay-react2",
3
- "version": "1.0.39",
4
- "description": "Common react",
5
- "strict": true,
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "files": [
9
- "lib/**/*",
10
- "!**/*.tsbuildinfo"
11
- ],
12
- "author": "wenay",
13
- "license": "ISC",
14
- "peerDependenciesMeta": {
15
- "react-dom": {
16
- "optional": true
17
- }
18
- },
19
- "dependencies": {
20
- "ag-grid-community": "^35.1.0",
21
- "ag-grid-react": "^35.1.0",
22
- "re-resizable": "^6.11.2",
23
- "react": "^19.2.0",
24
- "react-dom": "^19.2.0",
25
- "react-rnd": "^10.5.3",
26
- "wenay-common2": "^1.0.65"
27
- },
28
- "peerDependencies": {
29
- "react": "^19.2.0",
30
- "react-dom": "^19.2.0"
31
- },
32
- "engines": {
33
- "node": ">=18",
34
- "vscode": "^1.22.0"
35
- },
36
- "browserslist": {
37
- "production": [
38
- ">0.2%",
39
- "not dead"
40
- ],
41
- "development": [
42
- ">1%"
43
- ]
44
- },
45
- "exports": {
46
- ".": "./lib/index.js",
47
- "./package.json": "./package.json"
48
- }
49
- }
1
+ {
2
+ "name": "wenay-react2",
3
+ "version": "1.0.40",
4
+ "description": "Common react",
5
+ "strict": true,
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "lib/**/*",
10
+ "!**/*.tsbuildinfo"
11
+ ],
12
+ "author": "wenay",
13
+ "license": "ISC",
14
+ "peerDependenciesMeta": {
15
+ "react-dom": {
16
+ "optional": true
17
+ }
18
+ },
19
+ "dependencies": {
20
+ "ag-grid-community": "^35.1.0",
21
+ "ag-grid-react": "^35.1.0",
22
+ "re-resizable": "^6.11.2",
23
+ "react": "^19.2.0",
24
+ "react-dom": "^19.2.0",
25
+ "react-rnd": "^10.5.3",
26
+ "wenay-common2": "^1.0.65"
27
+ },
28
+ "peerDependencies": {
29
+ "react": "^19.2.0",
30
+ "react-dom": "^19.2.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18",
34
+ "vscode": "^1.22.0"
35
+ },
36
+ "browserslist": {
37
+ "production": [
38
+ ">0.2%",
39
+ "not dead"
40
+ ],
41
+ "development": [
42
+ ">1%"
43
+ ]
44
+ },
45
+ "exports": {
46
+ ".": "./lib/index.js",
47
+ "./package.json": "./package.json"
48
+ }
49
+ }