@etsoo/materialui 1.5.71 → 1.5.72

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 (58) hide show
  1. package/__tests__/ReactAppTests.tsx +12 -7
  2. package/__tests__/SelectEx.tsx +1 -1
  3. package/lib/cjs/DataGridEx.d.ts +8 -1
  4. package/lib/cjs/DataGridEx.js +71 -56
  5. package/lib/cjs/DataGridRenderers.d.ts +1 -1
  6. package/lib/cjs/DataGridRenderers.js +1 -1
  7. package/lib/cjs/MUUtils.d.ts +0 -9
  8. package/lib/cjs/MUUtils.js +0 -26
  9. package/lib/cjs/MobileListItemRenderer.d.ts +2 -2
  10. package/lib/cjs/ResponsibleContainer.d.ts +2 -7
  11. package/lib/cjs/ResponsibleContainer.js +8 -57
  12. package/lib/cjs/ScrollerListEx.d.ts +24 -9
  13. package/lib/cjs/ScrollerListEx.js +35 -38
  14. package/lib/cjs/TableEx.d.ts +7 -0
  15. package/lib/cjs/TableEx.js +6 -12
  16. package/lib/cjs/pages/DataGridPage.js +3 -32
  17. package/lib/cjs/pages/FixedListPage.js +5 -34
  18. package/lib/cjs/pages/ListPage.js +1 -29
  19. package/lib/cjs/pages/ResponsivePage.d.ts +2 -7
  20. package/lib/cjs/uses/useGridCacheInitLoad.d.ts +2 -0
  21. package/lib/cjs/uses/useGridCacheInitLoad.js +41 -0
  22. package/lib/cjs/uses/useListCacheInitLoad.d.ts +2 -0
  23. package/lib/cjs/uses/useListCacheInitLoad.js +38 -0
  24. package/lib/mjs/DataGridEx.d.ts +8 -1
  25. package/lib/mjs/DataGridEx.js +71 -56
  26. package/lib/mjs/DataGridRenderers.d.ts +1 -1
  27. package/lib/mjs/DataGridRenderers.js +1 -1
  28. package/lib/mjs/MUUtils.d.ts +0 -9
  29. package/lib/mjs/MUUtils.js +0 -26
  30. package/lib/mjs/MobileListItemRenderer.d.ts +2 -2
  31. package/lib/mjs/ResponsibleContainer.d.ts +2 -7
  32. package/lib/mjs/ResponsibleContainer.js +8 -57
  33. package/lib/mjs/ScrollerListEx.d.ts +24 -9
  34. package/lib/mjs/ScrollerListEx.js +35 -38
  35. package/lib/mjs/TableEx.d.ts +7 -0
  36. package/lib/mjs/TableEx.js +6 -12
  37. package/lib/mjs/pages/DataGridPage.js +3 -32
  38. package/lib/mjs/pages/FixedListPage.js +5 -34
  39. package/lib/mjs/pages/ListPage.js +1 -29
  40. package/lib/mjs/pages/ResponsivePage.d.ts +2 -7
  41. package/lib/mjs/uses/useGridCacheInitLoad.d.ts +2 -0
  42. package/lib/mjs/uses/useGridCacheInitLoad.js +35 -0
  43. package/lib/mjs/uses/useListCacheInitLoad.d.ts +2 -0
  44. package/lib/mjs/uses/useListCacheInitLoad.js +32 -0
  45. package/package.json +18 -19
  46. package/src/DataGridEx.tsx +151 -108
  47. package/src/DataGridRenderers.tsx +2 -1
  48. package/src/MUUtils.ts +0 -33
  49. package/src/MobileListItemRenderer.tsx +2 -2
  50. package/src/ResponsibleContainer.tsx +21 -94
  51. package/src/ScrollerListEx.tsx +109 -121
  52. package/src/TableEx.tsx +20 -12
  53. package/src/pages/DataGridPage.tsx +3 -49
  54. package/src/pages/FixedListPage.tsx +5 -49
  55. package/src/pages/ListPage.tsx +0 -43
  56. package/src/pages/ResponsivePage.tsx +3 -11
  57. package/src/uses/useGridCacheInitLoad.ts +55 -0
  58. package/src/uses/useListCacheInitLoad.ts +51 -0
@@ -1,8 +1,9 @@
1
1
  import { AddressUtils, Culture } from "@etsoo/appscript";
2
2
  import { ReactApp } from "../src";
3
3
  import { DataTypes, DomUtils, IActionResult, Utils } from "@etsoo/shared";
4
- import React, { act } from "react";
4
+ import { act } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
+ import { screen, waitFor } from "@testing-library/react";
6
7
 
7
8
  // Detected country or region
8
9
  const { detectedCountry } = DomUtils;
@@ -77,7 +78,7 @@ act(() => {
77
78
  reactRoot.render(<Provider />);
78
79
  });
79
80
 
80
- test("Test for properties", () => {
81
+ test("Test for properties", async () => {
81
82
  const result: IActionResult = {
82
83
  ok: false,
83
84
  type: "https://tools.ietf.org/html/rfc9110#section-15.5.1",
@@ -96,10 +97,13 @@ test("Test for properties", () => {
96
97
  app.alertResult(result);
97
98
  });
98
99
 
99
- expect(root.innerHTML).toContain(result.title);
100
+ await waitFor(() => {
101
+ const item = screen.getByText(result.title!);
102
+ expect(item.classList.contains("MuiDialogContent-root")).toBeTruthy();
103
+ });
100
104
  });
101
105
 
102
- test("Test for alertResult", () => {
106
+ test("Test for alertResult", async () => {
103
107
  const result: IActionResult = {
104
108
  ok: false,
105
109
  type: "TokenExpired",
@@ -111,7 +115,8 @@ test("Test for alertResult", () => {
111
115
  app.alertResult(result);
112
116
  });
113
117
 
114
- expect(root.innerHTML).toContain(
115
- '<span style="font-size: 9px;">(TokenExpired)</span>'
116
- );
118
+ await waitFor(() => {
119
+ const item = screen.getByText("(TokenExpired)");
120
+ expect(item.nodeName).toBe("SPAN");
121
+ });
117
122
  });
@@ -1,4 +1,4 @@
1
- import React, { act } from "react";
1
+ import { act } from "react";
2
2
  import { SelectEx } from "../src";
3
3
  import { fireEvent, render, screen } from "@testing-library/react";
4
4
  import { ListType1, Utils } from "@etsoo/shared";
@@ -14,7 +14,7 @@ export type DataGridExFooterItemRendererProps<T extends object> = {
14
14
  /**
15
15
  * Extended DataGrid with VariableSizeGrid props
16
16
  */
17
- export type DataGridExProps<T extends object, P extends GridJsonData = GridLoadDataProps> = Omit<ScrollerGridProps<T, P>, "itemRenderer" | "columnCount" | "columnWidth" | "width"> & {
17
+ export type DataGridExProps<T extends object, P extends GridJsonData = GridLoadDataProps> = Omit<ScrollerGridProps<T, P>, "cellComponent" | "columnCount" | "columnWidth" | "onClick" | "onDoubleClick" | "onInitLoad" | "width"> & {
18
18
  /**
19
19
  * Alternating colors for odd/even rows
20
20
  */
@@ -70,6 +70,13 @@ export type DataGridExProps<T extends object, P extends GridJsonData = GridLoadD
70
70
  * Click handler
71
71
  */
72
72
  onClick?: MouseEventWithDataHandler<T>;
73
+ /**
74
+ * Data change handler
75
+ * @param rows Rows
76
+ * @param rowIndex Row index
77
+ * @param columnIndex Column index
78
+ */
79
+ onDataChange?: (rows: T[], rowIndex: number, columnIndex: number) => void;
73
80
  /**
74
81
  * Selectable to support hover over and out effect and row clickable
75
82
  * @default true
@@ -16,6 +16,8 @@ const Box_1 = __importDefault(require("@mui/material/Box"));
16
16
  const TableSortLabel_1 = __importDefault(require("@mui/material/TableSortLabel"));
17
17
  const Checkbox_1 = __importDefault(require("@mui/material/Checkbox"));
18
18
  const Paper_1 = __importDefault(require("@mui/material/Paper"));
19
+ const GridUtils_1 = require("./GridUtils");
20
+ const useGridCacheInitLoad_1 = require("./uses/useGridCacheInitLoad");
19
21
  // Borders
20
22
  const boldBorder = "2px solid rgba(224, 224, 224, 1)";
21
23
  const thinBorder = "1px solid rgba(224, 224, 224, 1)";
@@ -135,7 +137,7 @@ function DataGridEx(props) {
135
137
  }) }));
136
138
  }
137
139
  // Destruct
138
- const { alternatingColors = [theme.palette.grey[100], undefined], borderRowsCount, bottomHeight = 53, checkable = false, className, columns, defaultOrderBy, height, headerHeight = 56, headerRenderer = defaultHeaderRenderer, footerRenderer = defaultFooterRenderer, footerItemRenderer = DataGridRenderers_1.DataGridRenderers.defaultFooterItemRenderer, hideFooter = false, hoverColor = "#f6f9fb", idField = "id", mRef = react_2.default.createRef(), onClick, onDoubleClick, selectable = true, selectedColor = "#edf4fb", width, ...rest } = props;
140
+ const { alternatingColors = [theme.palette.grey[100], undefined], borderRowsCount, bottomHeight = 53, cacheKey, cacheMinutes = 15, checkable = false, className, columns, defaultOrderBy, height, headerHeight = 56, headerRenderer = defaultHeaderRenderer, footerRenderer = defaultFooterRenderer, footerItemRenderer = DataGridRenderers_1.DataGridRenderers.defaultFooterItemRenderer, hideFooter = false, hoverColor = "#f6f9fb", idField = "id", mRef = react_2.default.createRef(), onClick, onDataChange, onDoubleClick, onUpdateRows, selectable = true, selectedColor = "#edf4fb", width, ...rest } = props;
139
141
  if (checkable) {
140
142
  const cbColumn = {
141
143
  field: "selected", // Avoid validation from data model
@@ -168,6 +170,8 @@ function DataGridEx(props) {
168
170
  columns.unshift(cbColumn);
169
171
  }
170
172
  }
173
+ // Init handler
174
+ const initHandler = (0, useGridCacheInitLoad_1.useGridCacheInitLoad)(cacheKey, cacheMinutes);
171
175
  const refs = react_2.default.useRef({});
172
176
  const mRefLocal = (0, react_1.useCombinedRefs)(mRef, (ref) => {
173
177
  if (ref == null)
@@ -226,57 +230,6 @@ function DataGridEx(props) {
226
230
  div.classList.remove("DataGridEx-Hover");
227
231
  });
228
232
  };
229
- /**
230
- * Item renderer
231
- */
232
- const itemRenderer = ({ columnIndex, rowIndex, style, data, selectedItems, setItems }) => {
233
- // Column
234
- const { align, cellRenderer = DataGridRenderers_1.DataGridRenderers.defaultCellRenderer, cellBoxStyle, field, type, valueFormatter, renderProps } = columns[columnIndex];
235
- // Props
236
- const formatProps = {
237
- data,
238
- field,
239
- rowIndex,
240
- columnIndex
241
- };
242
- let rowClass = `DataGridEx-Cell${rowIndex % 2}`;
243
- if (borderRowsCount != null &&
244
- borderRowsCount > 0 &&
245
- (rowIndex + 1) % borderRowsCount === 0) {
246
- rowClass += ` DataGridEx-Cell-Border`;
247
- }
248
- // Selected
249
- const selected = data != null &&
250
- (selectedRowIndex.current === rowIndex ||
251
- selectedItems.some((selectedItem) => selectedItem != null && selectedItem[idField] === data[idField]));
252
- if (selected) {
253
- rowClass += ` DataGridEx-Selected`;
254
- }
255
- // Box style
256
- const boxStyle = data == null || cellBoxStyle == null
257
- ? undefined
258
- : typeof cellBoxStyle === "function"
259
- ? cellBoxStyle(data)
260
- : cellBoxStyle;
261
- const cellProps = {
262
- className: "DataGridEx-Cell",
263
- textAlign: (0, react_1.GridAlignGet)(align, type),
264
- sx: { ...boxStyle }
265
- };
266
- const child = cellRenderer({
267
- data,
268
- field,
269
- formattedValue: valueFormatter ? valueFormatter(formatProps) : undefined,
270
- selected,
271
- type,
272
- rowIndex,
273
- columnIndex,
274
- cellProps,
275
- renderProps: typeof renderProps === "function" ? renderProps(data) : renderProps,
276
- setItems
277
- });
278
- return ((0, jsx_runtime_1.jsx)("div", { className: rowClass, style: style, "data-row": rowIndex, "data-column": columnIndex, onMouseDown: selectable && !checkable ? handleMouseDown : undefined, onMouseOver: selectable ? handleMouseOver : undefined, onMouseOut: selectable ? handleMouseOut : undefined, onClick: (event) => onClick && data != null && onClick(event, data), onDoubleClick: (event) => onDoubleClick && data != null && onDoubleClick(event, data), children: (0, jsx_runtime_1.jsx)(Box_1.default, { ...cellProps, onMouseEnter: handleMouseEnter, children: child }) }));
279
- };
280
233
  // Column width calculator
281
234
  const widthCalculator = react_2.default.useMemo(() => DataGridExCalColumns(columns), [columns]);
282
235
  // Column width
@@ -294,12 +247,74 @@ function DataGridEx(props) {
294
247
  const sharedWidth = leftWidth > 0 ? leftWidth / widthCalculator.unset : 0;
295
248
  return (column.minWidth || minWidth) + sharedWidth;
296
249
  }, [columns, width]);
250
+ const onUpdateRowsHandler = react_2.default.useCallback((rows, state) => {
251
+ GridUtils_1.GridUtils.getUpdateRowsHandler(cacheKey)?.(rows, state);
252
+ onUpdateRows?.(rows, state);
253
+ }, [onUpdateRows, cacheKey]);
297
254
  // Table
298
255
  const table = react_2.default.useMemo(() => {
299
- return ((0, jsx_runtime_1.jsx)(react_1.ScrollerGrid, { className: shared_1.Utils.mergeClasses("DataGridEx-Body", "DataGridEx-CustomBar", className, createGridStyle(alternatingColors, selectedColor, hoverColor)), columnCount: columns.length, columnWidth: columnWidth, defaultOrderBy: defaultOrderBy, height: height -
300
- headerHeight -
301
- (hideFooter ? 0 : bottomHeight + 1) -
302
- scrollbarSize, headerRenderer: headerRenderer, idField: idField, itemRenderer: itemRenderer, footerRenderer: hideFooter ? undefined : footerRenderer, width: Math.max(width ?? 0, widthCalculator.total), mRef: mRefLocal, ...rest }));
256
+ return ((0, jsx_runtime_1.jsx)(react_1.ScrollerGrid, { className: shared_1.Utils.mergeClasses("DataGridEx-Body", "DataGridEx-CustomBar", className, createGridStyle(alternatingColors, selectedColor, hoverColor)), onCellsRendered: cacheKey
257
+ ? (visibleCells) => sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(visibleCells))
258
+ : undefined, onInitLoad: initHandler, onUpdateRows: onUpdateRowsHandler, cellComponent: ({ rowIndex, columnIndex, style, rows, states }) => {
259
+ // Column
260
+ const { align, cellRenderer = DataGridRenderers_1.DataGridRenderers.defaultCellRenderer, cellBoxStyle, field, type, valueFormatter, renderProps } = columns[columnIndex];
261
+ // Data
262
+ const data = rows[rowIndex];
263
+ // Props
264
+ const formatProps = {
265
+ data,
266
+ field,
267
+ rowIndex,
268
+ columnIndex
269
+ };
270
+ let rowClass = `DataGridEx-Cell${rowIndex % 2}`;
271
+ if (borderRowsCount != null &&
272
+ borderRowsCount > 0 &&
273
+ (rowIndex + 1) % borderRowsCount === 0) {
274
+ rowClass += ` DataGridEx-Cell-Border`;
275
+ }
276
+ // Selected
277
+ const selected = data != null &&
278
+ (selectedRowIndex.current === rowIndex ||
279
+ states.selectedItems.some((selectedItem) => selectedItem != null &&
280
+ selectedItem[idField] === data[idField]));
281
+ if (selected) {
282
+ rowClass += ` DataGridEx-Selected`;
283
+ }
284
+ // Box style
285
+ const boxStyle = data == null || cellBoxStyle == null
286
+ ? undefined
287
+ : typeof cellBoxStyle === "function"
288
+ ? cellBoxStyle(data)
289
+ : cellBoxStyle;
290
+ const cellProps = {
291
+ className: "DataGridEx-Cell",
292
+ textAlign: (0, react_1.GridAlignGet)(align, type),
293
+ sx: { ...boxStyle }
294
+ };
295
+ const child = cellRenderer({
296
+ data,
297
+ field,
298
+ formattedValue: valueFormatter
299
+ ? valueFormatter(formatProps)
300
+ : undefined,
301
+ selected,
302
+ type,
303
+ rowIndex,
304
+ columnIndex,
305
+ cellProps,
306
+ renderProps: typeof renderProps === "function"
307
+ ? renderProps(data)
308
+ : renderProps,
309
+ triggerChange: () => onDataChange?.(rows, rowIndex, columnIndex)
310
+ });
311
+ return ((0, jsx_runtime_1.jsx)("div", { className: rowClass, style: style, "data-row": rowIndex, "data-column": columnIndex, onMouseDown: selectable && !checkable ? handleMouseDown : undefined, onMouseOver: selectable ? handleMouseOver : undefined, onMouseOut: selectable ? handleMouseOut : undefined, onClick: (event) => onClick && data != null && onClick(event, data), onDoubleClick: (event) => onDoubleClick && data != null && onDoubleClick(event, data), children: (0, jsx_runtime_1.jsx)(Box_1.default, { ...cellProps, onMouseEnter: handleMouseEnter, children: child }) }));
312
+ }, columnCount: columns.length, columnWidth: columnWidth, defaultOrderBy: defaultOrderBy, height: typeof height === "number"
313
+ ? height -
314
+ headerHeight -
315
+ (hideFooter ? 0 : bottomHeight + 1) -
316
+ scrollbarSize
317
+ : height, headerRenderer: headerRenderer, idField: idField, footerRenderer: hideFooter ? undefined : footerRenderer, width: Math.max(width ?? 0, widthCalculator.total), mRef: mRefLocal, ...rest }));
303
318
  }, [width]);
304
319
  return ((0, jsx_runtime_1.jsx)(Paper_1.default, { sx: {
305
320
  fontSize: "0.875rem",
@@ -10,7 +10,7 @@ export declare namespace DataGridRenderers {
10
10
  * @param param Props
11
11
  * @returns Component
12
12
  */
13
- function defaultCellRenderer<T extends Record<string, any>>({ cellProps, data, field, formattedValue, columnIndex, type, renderProps }: GridCellRendererProps<T>): React.ReactNode;
13
+ function defaultCellRenderer<T extends Record<string, any>>({ cellProps, data, field, formattedValue, columnIndex, type, renderProps, triggerChange }: GridCellRendererProps<T>): React.ReactNode;
14
14
  /**
15
15
  * Default footer item renderer
16
16
  * @param rows Rows
@@ -21,7 +21,7 @@ var DataGridRenderers;
21
21
  * @param param Props
22
22
  * @returns Component
23
23
  */
24
- function defaultCellRenderer({ cellProps, data, field, formattedValue, columnIndex, type, renderProps }) {
24
+ function defaultCellRenderer({ cellProps, data, field, formattedValue, columnIndex, type, renderProps, triggerChange }) {
25
25
  // Is loading
26
26
  if (data == null) {
27
27
  // First column, show loading indicator
@@ -1,5 +1,3 @@
1
- import { QueryRQ } from "@etsoo/appscript";
2
- import { IdType } from "@etsoo/shared";
3
1
  import { GridApiCommunity } from "@mui/x-data-grid/internals";
4
2
  /**
5
3
  * MU utilities
@@ -12,11 +10,4 @@ export declare namespace MUUtils {
12
10
  * @returns Results
13
11
  */
14
12
  function getGridData<T>(grid: GridApiCommunity, checkField: keyof T | ((item: T) => boolean)): T[];
15
- /**
16
- * Setup paging keysets
17
- * @param data Paging data
18
- * @param lastItem Last item of the query
19
- * @param idField Id field
20
- */
21
- function setupPagingKeysets<T, K extends IdType = number>(data: QueryRQ<K>, lastItem: T | undefined, idField: keyof T & string): QueryRQ<K>;
22
13
  }
@@ -29,30 +29,4 @@ var MUUtils;
29
29
  return items;
30
30
  }
31
31
  MUUtils.getGridData = getGridData;
32
- /**
33
- * Setup paging keysets
34
- * @param data Paging data
35
- * @param lastItem Last item of the query
36
- * @param idField Id field
37
- */
38
- function setupPagingKeysets(data, lastItem, idField) {
39
- // If the id field is not set for ordering, add it with descending
40
- if (typeof data.queryPaging === "object") {
41
- const orderBy = (data.queryPaging.orderBy ??= []);
42
- const idUpper = idField.toUpperCase();
43
- if (!orderBy.find((o) => o.field.toUpperCase() === idUpper)) {
44
- orderBy.push({ field: idField, desc: true, unique: true });
45
- }
46
- // Set the paging keysets
47
- if (lastItem) {
48
- const keysets = orderBy.map((o) => Reflect.get(lastItem, o.field));
49
- data.queryPaging.keysets = keysets;
50
- }
51
- else {
52
- data.queryPaging.keysets = undefined;
53
- }
54
- }
55
- return data;
56
- }
57
- MUUtils.setupPagingKeysets = setupPagingKeysets;
58
32
  })(MUUtils || (exports.MUUtils = MUUtils = {}));
@@ -1,6 +1,6 @@
1
1
  import { ListItemReact } from "@etsoo/react";
2
2
  import React from "react";
3
- import { ScrollerListExInnerItemRendererProps } from "./ScrollerListEx";
3
+ import { ScrollerListExItemRendererProps } from "./ScrollerListEx";
4
4
  /**
5
5
  * Default mobile list item renderer
6
6
  * @param param0 List renderer props
@@ -8,7 +8,7 @@ import { ScrollerListExInnerItemRendererProps } from "./ScrollerListEx";
8
8
  * @param renderer Renderer for card content
9
9
  * @returns Component
10
10
  */
11
- export declare function MobileListItemRenderer<T>({ data, itemHeight, margins }: ScrollerListExInnerItemRendererProps<T>, renderer: (data: T) => [
11
+ export declare function MobileListItemRenderer<T>({ data, itemHeight, margins }: ScrollerListExItemRendererProps<T>, renderer: (data: T) => [
12
12
  string,
13
13
  string | undefined,
14
14
  React.ReactNode | (ListItemReact | boolean)[],
@@ -1,8 +1,7 @@
1
1
  import React from "react";
2
- import { ListChildComponentProps } from "react-window";
3
2
  import { GridColumn, GridJsonData, GridMethodRef, GridTemplateType } from "@etsoo/react";
4
3
  import { DataGridExProps } from "./DataGridEx";
5
- import { ScrollerListExInnerItemRendererProps, ScrollerListExItemSize } from "./ScrollerListEx";
4
+ import { ScrollerListExItemSize, ScrollerListExProps } from "./ScrollerListEx";
6
5
  import { SxProps, Theme } from "@mui/material/styles";
7
6
  /**
8
7
  * ResponsibleContainer props
@@ -41,14 +40,10 @@ export type ResponsibleContainerProps<T extends object, F> = Omit<DataGridExProp
41
40
  * Grid height
42
41
  */
43
42
  height?: number;
44
- /**
45
- * Inner item renderer
46
- */
47
- innerItemRenderer: (props: ScrollerListExInnerItemRendererProps<T>) => React.ReactNode;
48
43
  /**
49
44
  * Item renderer
50
45
  */
51
- itemRenderer?: (props: ListChildComponentProps<T>) => React.ReactElement;
46
+ itemRenderer?: ScrollerListExProps<T>["itemRenderer"];
52
47
  /**
53
48
  * Item size, a function indicates its a variable size list
54
49
  */
@@ -42,6 +42,8 @@ function ResponsibleContainer(props) {
42
42
  if (ref == null)
43
43
  return;
44
44
  state.ref = ref;
45
+ if (ref.element && elementReady)
46
+ elementReady(ref.element, true);
45
47
  });
46
48
  // Screen size detection
47
49
  const showDataGrid = (0, useMediaQuery_1.default)("(min-width:600px)");
@@ -83,44 +85,6 @@ function ResponsibleContainer(props) {
83
85
  state.rect = rect;
84
86
  return false;
85
87
  });
86
- const onInitLoad = (ref) => {
87
- // Avoid repeatedly load from cache
88
- if (refs.current.initLoaded || !cacheKey)
89
- return undefined;
90
- // Cache data
91
- const cacheData = GridUtils_1.GridUtils.getCacheData(cacheKey, cacheMinutes);
92
- if (cacheData) {
93
- const { rows, state } = cacheData;
94
- GridUtils_1.GridUtils.mergeSearchData(state, searchData);
95
- // Scroll position
96
- const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
97
- if (scrollData) {
98
- if ("resetAfterColumnIndex" in ref) {
99
- const { scrollLeft, scrollTop } = JSON.parse(scrollData);
100
- globalThis.setTimeout(() => ref.scrollTo({ scrollLeft, scrollTop }), 0);
101
- }
102
- else {
103
- const { scrollOffset } = JSON.parse(scrollData);
104
- globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
105
- }
106
- }
107
- // Update flag value
108
- refs.current.initLoaded = true;
109
- // Return cached rows and state
110
- return [rows, state];
111
- }
112
- return undefined;
113
- };
114
- const onListScroll = (props) => {
115
- if (!cacheKey || !refs.current.initLoaded)
116
- return;
117
- sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
118
- };
119
- const onGridScroll = (props) => {
120
- if (!cacheKey || !refs.current.initLoaded)
121
- return;
122
- sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
123
- };
124
88
  // Rect
125
89
  const rect = dimensions[0][2];
126
90
  // Create list
@@ -150,26 +114,13 @@ function ResponsibleContainer(props) {
150
114
  if (adjustFabHeight)
151
115
  heightLocal = adjustFabHeight(heightLocal, showDataGrid);
152
116
  if (showDataGrid) {
153
- // Delete
154
- delete rest.itemRenderer;
155
- return ((0, jsx_runtime_1.jsx)(Box_1.default, { className: "DataGridBox", children: (0, jsx_runtime_1.jsx)(DataGridEx_1.DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, onDoubleClick: (_, data) => quickAction && quickAction(data), outerRef: (element) => {
156
- if (element != null && elementReady)
157
- elementReady(element, true);
158
- }, onScroll: onGridScroll, columns: columns, onUpdateRows: GridUtils_1.GridUtils.getUpdateRowsHandler(cacheKey), onInitLoad: onInitLoad, ...rest }) }));
117
+ // Remove useless props
118
+ const { itemRenderer, ...gridProps } = rest;
119
+ return ((0, jsx_runtime_1.jsx)(Box_1.default, { className: "DataGridBox", children: (0, jsx_runtime_1.jsx)(DataGridEx_1.DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, onDoubleClick: (_, data) => quickAction && quickAction(data), columns: columns, ...gridProps }) }));
159
120
  }
160
- // Delete
161
- delete rest.checkable;
162
- delete rest.borderRowsCount;
163
- delete rest.bottomHeight;
164
- delete rest.footerItemRenderer;
165
- delete rest.headerHeight;
166
- delete rest.hideFooter;
167
- delete rest.hoverColor;
168
- delete rest.selectable;
169
- return ((0, jsx_runtime_1.jsx)(Box_1.default, { className: "ListBox", sx: { height: heightLocal }, children: (0, jsx_runtime_1.jsx)(ScrollerListEx_1.ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, onUpdateRows: GridUtils_1.GridUtils.getUpdateRowsHandler(cacheKey), onInitLoad: onInitLoad, mRef: mRefs, onClick: (event, data) => quickAction && react_2.ReactUtils.isSafeClick(event) && quickAction(data), oRef: (element) => {
170
- if (element != null && elementReady)
171
- elementReady(element, false);
172
- }, onScroll: onListScroll, ...rest }) }));
121
+ // Remove useless props
122
+ const { checkable, borderRowsCount, bottomHeight, footerItemRenderer, headerHeight, hideFooter, hoverColor, selectable, ...listProps } = rest;
123
+ return ((0, jsx_runtime_1.jsx)(Box_1.default, { className: "ListBox", sx: { height: heightLocal }, children: (0, jsx_runtime_1.jsx)(ScrollerListEx_1.ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, mRef: mRefs, onClick: (event, data) => quickAction && react_2.ReactUtils.isSafeClick(event) && quickAction(data), ...listProps }) }));
173
124
  })();
174
125
  const searchBar = react_1.default.useMemo(() => {
175
126
  if (!hasFields ||
@@ -1,15 +1,22 @@
1
1
  import { ScrollerListProps } from "@etsoo/react";
2
2
  import React from "react";
3
- import { ListChildComponentProps } from "react-window";
4
3
  import { MouseEventWithDataHandler } from "./MUGlobal";
5
4
  /**
6
5
  * Extended ScrollerList inner item renderer props
7
6
  */
8
- export interface ScrollerListExInnerItemRendererProps<T> extends ListChildComponentProps<T> {
7
+ export type ScrollerListExItemRendererProps<T> = {
9
8
  /**
10
- * Item selected
9
+ * Row index
11
10
  */
12
- selected: boolean;
11
+ index: number;
12
+ /**
13
+ * Row data
14
+ */
15
+ data: T;
16
+ /**
17
+ * Style
18
+ */
19
+ style: React.CSSProperties;
13
20
  /**
14
21
  * Item height
15
22
  */
@@ -22,7 +29,11 @@ export interface ScrollerListExInnerItemRendererProps<T> extends ListChildCompon
22
29
  * Default margins
23
30
  */
24
31
  margins: object;
25
- }
32
+ /**
33
+ * Item selected
34
+ */
35
+ selected: boolean;
36
+ };
26
37
  /**
27
38
  * Extended ScrollerList ItemSize type
28
39
  * 1. Callback function
@@ -33,19 +44,23 @@ export type ScrollerListExItemSize = ((index: number) => [number, number] | [num
33
44
  /**
34
45
  * Extended ScrollerList Props
35
46
  */
36
- export type ScrollerListExProps<T extends object> = Omit<ScrollerListProps<T>, "itemRenderer" | "itemSize"> & {
47
+ export type ScrollerListExProps<T extends object> = Omit<ScrollerListProps<T>, "rowComponent" | "rowHeight" | "onClick" | "onDoubleClick" | "onInitLoad"> & {
37
48
  /**
38
49
  * Alternating colors for odd/even rows
39
50
  */
40
51
  alternatingColors?: [string?, string?];
41
52
  /**
42
- * Inner item renderer
53
+ * Cache key
54
+ */
55
+ cacheKey?: string;
56
+ /**
57
+ * Cache minutes
43
58
  */
44
- innerItemRenderer: (props: ScrollerListExInnerItemRendererProps<T>) => React.ReactNode;
59
+ cacheMinutes?: number;
45
60
  /**
46
61
  * Item renderer
47
62
  */
48
- itemRenderer?: (props: ListChildComponentProps<T>) => React.ReactElement;
63
+ itemRenderer?: (props: ScrollerListExItemRendererProps<T>) => React.ReactNode;
49
64
  /**
50
65
  * Item size, a function indicates its a variable size list
51
66
  */
@@ -11,6 +11,9 @@ const shared_1 = require("@etsoo/shared");
11
11
  const react_2 = __importDefault(require("react"));
12
12
  const MUGlobal_1 = require("./MUGlobal");
13
13
  const styles_1 = require("@mui/material/styles");
14
+ const GridUtils_1 = require("./GridUtils");
15
+ const useListCacheInitLoad_1 = require("./uses/useListCacheInitLoad");
16
+ const Box_1 = __importDefault(require("@mui/material/Box"));
14
17
  // Scroll bar size
15
18
  const scrollbarSize = 16;
16
19
  // Selected class name
@@ -74,24 +77,6 @@ const defaultMargin = (margin, horizon) => {
74
77
  marginBottom: half
75
78
  };
76
79
  };
77
- // Default itemRenderer
78
- function defaultItemRenderer({ index, innerItemRenderer, data, onMouseDown, selected, style, itemHeight, onClick, onDoubleClick, space, margins }) {
79
- // Child
80
- const child = innerItemRenderer({
81
- index,
82
- data,
83
- style,
84
- selected,
85
- itemHeight,
86
- space,
87
- margins
88
- });
89
- let rowClass = `ScrollerListEx-Row${index % 2}`;
90
- if (selected)
91
- rowClass += ` ${selectedClassName}`;
92
- // Layout
93
- return ((0, jsx_runtime_1.jsx)("div", { className: rowClass, style: style, onMouseDown: (event) => onMouseDown(event.currentTarget, data), onClick: (event) => onClick && onClick(event, data), onDoubleClick: (event) => onDoubleClick && onDoubleClick(event, data), children: child }));
94
- }
95
80
  /**
96
81
  * Extended ScrollerList
97
82
  * @param props Props
@@ -119,20 +104,12 @@ function ScrollerListEx(props) {
119
104
  return selected;
120
105
  };
121
106
  // Destruct
122
- const { alternatingColors = [undefined, undefined], className, idField = "id", innerItemRenderer, itemSize, itemRenderer = (itemProps) => {
123
- const [itemHeight, space, margins] = calculateItemSize(itemProps.index);
124
- return defaultItemRenderer({
125
- itemHeight,
126
- innerItemRenderer,
127
- onMouseDown,
128
- onClick,
129
- onDoubleClick,
130
- space,
131
- margins,
132
- selected: isSelected(itemProps.data),
133
- ...itemProps
134
- });
135
- }, onClick, onDoubleClick, onSelectChange, selectedColor = "#edf4fb", ...rest } = props;
107
+ const { alternatingColors = [undefined, undefined], className, cacheKey, cacheMinutes = 15, idField = "id", itemSize, itemRenderer = ({ data, itemHeight, margins }) => ((0, jsx_runtime_1.jsx)(Box_1.default, { component: "pre", sx: {
108
+ height: itemHeight,
109
+ ...margins
110
+ }, children: JSON.stringify(data) })), onClick, onDoubleClick, onUpdateRows, onSelectChange, selectedColor = "#edf4fb", ...rest } = props;
111
+ // Init handler
112
+ const initHandler = (0, useListCacheInitLoad_1.useListCacheInitLoad)(cacheKey, cacheMinutes);
136
113
  // Theme
137
114
  const theme = (0, styles_1.useTheme)();
138
115
  // Cache calculation
@@ -156,11 +133,31 @@ function ScrollerListEx(props) {
156
133
  // Calculation
157
134
  return itemSizeResult;
158
135
  };
159
- // Local item size
160
- const itemSizeLocal = (index) => {
161
- const [size, space] = calculateItemSize(index);
162
- return size + space;
163
- };
136
+ const onUpdateRowsHandler = react_2.default.useCallback((rows, state) => {
137
+ GridUtils_1.GridUtils.getUpdateRowsHandler(cacheKey)?.(rows, state);
138
+ onUpdateRows?.(rows, state);
139
+ }, [onUpdateRows, cacheKey]);
164
140
  // Layout
165
- return ((0, jsx_runtime_1.jsx)(react_1.ScrollerList, { className: shared_1.Utils.mergeClasses("ScrollerListEx-Body", className, createGridStyle(alternatingColors, selectedColor)), idField: idField, itemRenderer: itemRenderer, itemSize: itemSizeLocal, ...rest }));
141
+ return ((0, jsx_runtime_1.jsx)(react_1.ScrollerList, { className: shared_1.Utils.mergeClasses("ScrollerListEx-Body", className, createGridStyle(alternatingColors, selectedColor)), idField: idField, onRowsRendered: cacheKey
142
+ ? (visibleRows) => sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(visibleRows))
143
+ : undefined, onInitLoad: initHandler, onUpdateRows: onUpdateRowsHandler, rowComponent: ({ index, items, style }) => {
144
+ const data = items[index];
145
+ const selected = isSelected(data);
146
+ const rowClass = `ScrollerListEx-Row${index % 2}${selected ? ` ${selectedClassName}` : ""}`;
147
+ const [itemHeight, space, margins] = calculateItemSize(index);
148
+ // Child
149
+ const child = itemRenderer({
150
+ index,
151
+ data,
152
+ style,
153
+ selected,
154
+ itemHeight,
155
+ space,
156
+ margins
157
+ });
158
+ return ((0, jsx_runtime_1.jsx)("div", { className: rowClass, style: style, onMouseDown: (event) => onMouseDown(event.currentTarget, data), onClick: (event) => onClick && onClick(event, data), onDoubleClick: (event) => onDoubleClick && onDoubleClick(event, data), children: child }));
159
+ }, rowHeight: (index) => {
160
+ const [size, space] = calculateItemSize(index);
161
+ return size + space;
162
+ }, ...rest }));
166
163
  }
@@ -43,6 +43,13 @@ export type TableExProps<T extends object, D extends DataTypes.Keys<T>> = TableP
43
43
  * Methods
44
44
  */
45
45
  mRef?: React.Ref<TableExMethodRef<T>>;
46
+ /**
47
+ * Data change handler
48
+ * @param rows Rows
49
+ * @param rowIndex Row index
50
+ * @param columnIndex Column index
51
+ */
52
+ onDataChange?: (rows: T[], rowIndex: number, columnIndex: number) => void;
46
53
  /**
47
54
  * On items select change
48
55
  */