@etsoo/materialui 1.5.71 → 1.5.73

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 (61) hide show
  1. package/__tests__/ReactAppTests.tsx +12 -7
  2. package/__tests__/{ResponsePage.tsx → ResponsivePage.tsx} +11 -5
  3. package/__tests__/SelectEx.tsx +1 -1
  4. package/lib/cjs/DataGridEx.d.ts +8 -1
  5. package/lib/cjs/DataGridEx.js +71 -56
  6. package/lib/cjs/DataGridRenderers.d.ts +1 -1
  7. package/lib/cjs/DataGridRenderers.js +1 -1
  8. package/lib/cjs/MUUtils.d.ts +0 -9
  9. package/lib/cjs/MUUtils.js +0 -26
  10. package/lib/cjs/MobileListItemRenderer.d.ts +2 -2
  11. package/lib/cjs/MobileListItemRenderer.js +3 -4
  12. package/lib/cjs/ResponsibleContainer.d.ts +9 -13
  13. package/lib/cjs/ResponsibleContainer.js +19 -58
  14. package/lib/cjs/ScrollerListEx.d.ts +23 -23
  15. package/lib/cjs/ScrollerListEx.js +32 -84
  16. package/lib/cjs/TableEx.d.ts +7 -0
  17. package/lib/cjs/TableEx.js +6 -12
  18. package/lib/cjs/pages/DataGridPage.js +3 -32
  19. package/lib/cjs/pages/FixedListPage.js +5 -34
  20. package/lib/cjs/pages/ListPage.js +1 -29
  21. package/lib/cjs/pages/ResponsivePage.d.ts +9 -13
  22. package/lib/cjs/uses/useGridCacheInitLoad.d.ts +2 -0
  23. package/lib/cjs/uses/useGridCacheInitLoad.js +41 -0
  24. package/lib/cjs/uses/useListCacheInitLoad.d.ts +2 -0
  25. package/lib/cjs/uses/useListCacheInitLoad.js +38 -0
  26. package/lib/mjs/DataGridEx.d.ts +8 -1
  27. package/lib/mjs/DataGridEx.js +71 -56
  28. package/lib/mjs/DataGridRenderers.d.ts +1 -1
  29. package/lib/mjs/DataGridRenderers.js +1 -1
  30. package/lib/mjs/MUUtils.d.ts +0 -9
  31. package/lib/mjs/MUUtils.js +0 -26
  32. package/lib/mjs/MobileListItemRenderer.d.ts +2 -2
  33. package/lib/mjs/MobileListItemRenderer.js +3 -4
  34. package/lib/mjs/ResponsibleContainer.d.ts +9 -13
  35. package/lib/mjs/ResponsibleContainer.js +19 -58
  36. package/lib/mjs/ScrollerListEx.d.ts +23 -23
  37. package/lib/mjs/ScrollerListEx.js +32 -84
  38. package/lib/mjs/TableEx.d.ts +7 -0
  39. package/lib/mjs/TableEx.js +6 -12
  40. package/lib/mjs/pages/DataGridPage.js +3 -32
  41. package/lib/mjs/pages/FixedListPage.js +5 -34
  42. package/lib/mjs/pages/ListPage.js +1 -29
  43. package/lib/mjs/pages/ResponsivePage.d.ts +9 -13
  44. package/lib/mjs/uses/useGridCacheInitLoad.d.ts +2 -0
  45. package/lib/mjs/uses/useGridCacheInitLoad.js +35 -0
  46. package/lib/mjs/uses/useListCacheInitLoad.d.ts +2 -0
  47. package/lib/mjs/uses/useListCacheInitLoad.js +32 -0
  48. package/package.json +18 -19
  49. package/src/DataGridEx.tsx +155 -109
  50. package/src/DataGridRenderers.tsx +2 -1
  51. package/src/MUUtils.ts +0 -33
  52. package/src/MobileListItemRenderer.tsx +4 -4
  53. package/src/ResponsibleContainer.tsx +50 -111
  54. package/src/ScrollerListEx.tsx +141 -229
  55. package/src/TableEx.tsx +20 -12
  56. package/src/pages/DataGridPage.tsx +3 -49
  57. package/src/pages/FixedListPage.tsx +5 -49
  58. package/src/pages/ListPage.tsx +0 -43
  59. package/src/pages/ResponsivePage.tsx +16 -21
  60. package/src/uses/useGridCacheInitLoad.ts +55 -0
  61. 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,12 +1,12 @@
1
- import { act, render } from "@testing-library/react";
1
+ import { act, render, renderHook } from "@testing-library/react";
2
2
  import {
3
- MUGlobal,
4
3
  MobileListItemRenderer,
5
4
  ReactAppContext,
6
5
  ResponsivePage,
7
6
  SearchField
8
7
  } from "../src";
9
8
  import React from "react";
9
+ import { GridMethodRef } from "@etsoo/react";
10
10
 
11
11
  globalThis.ResizeObserver = vi.fn().mockImplementation(() => ({
12
12
  observe: vi.fn(),
@@ -32,15 +32,21 @@ const fieldTemplate = {
32
32
  name: "string"
33
33
  } as const;
34
34
 
35
- it("Render ResponsePage", async () => {
35
+ it("Render ResponsivePage", async () => {
36
36
  act(() => {
37
+ // Hook
38
+ const { result: ref } = renderHook(() =>
39
+ React.useRef<GridMethodRef<Data>>(undefined)
40
+ );
41
+
37
42
  // Act
38
43
  render(
39
44
  <ReactAppContext.Provider value={null}>
40
45
  <ResponsivePage<Data, typeof fieldTemplate>
41
46
  fields={[<SearchField label="Keyword" name="keyword" minChars={2} />]}
42
47
  height={200}
43
- itemSize={[118, MUGlobal.pagePaddings]}
48
+ rowHeight={[53, 116]}
49
+ mRef={ref.current}
44
50
  fieldTemplate={fieldTemplate}
45
51
  loadData={({ id }) =>
46
52
  Promise.resolve([
@@ -58,7 +64,7 @@ it("Render ResponsePage", async () => {
58
64
  valueFormatter: ({ data }) => data?.deviceName ?? data?.name
59
65
  }
60
66
  ]}
61
- innerItemRenderer={(props) =>
67
+ itemRenderer={(props) =>
62
68
  MobileListItemRenderer(props, (data) => {
63
69
  return [
64
70
  data.name,
@@ -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" | "rowHeight" | "width"> & Partial<Pick<ScrollerGridProps<T, P>, "rowHeight">> & {
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, rowHeight = 53, 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, rowHeight: rowHeight, 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, margins, style }: ScrollerListExItemRendererProps<T>, renderer: (data: T) => [
12
12
  string,
13
13
  string | undefined,
14
14
  React.ReactNode | (ListItemReact | boolean)[],
@@ -17,16 +17,15 @@ const CardContent_1 = __importDefault(require("@mui/material/CardContent"));
17
17
  * @param renderer Renderer for card content
18
18
  * @returns Component
19
19
  */
20
- function MobileListItemRenderer({ data, itemHeight, margins }, renderer) {
20
+ function MobileListItemRenderer({ data, margins, style }, renderer) {
21
21
  // Loading
22
22
  if (data == null)
23
23
  return (0, jsx_runtime_1.jsx)(LinearProgress_1.default, {});
24
24
  // Elements
25
25
  const [title, subheader, actions, children, cardActions] = renderer(data);
26
26
  return ((0, jsx_runtime_1.jsxs)(Card_1.default, { sx: {
27
- height: itemHeight,
28
27
  ...margins
29
- }, children: [(0, jsx_runtime_1.jsx)(CardHeader_1.default, { sx: { paddingBottom: 0.5 }, action: Array.isArray(actions) ? ((0, jsx_runtime_1.jsx)(MoreFab_1.MoreFab, { iconButton: true, size: "small", anchorOrigin: {
28
+ }, style: style, children: [(0, jsx_runtime_1.jsx)(CardHeader_1.default, { sx: { paddingBottom: 0.5 }, action: Array.isArray(actions) ? ((0, jsx_runtime_1.jsx)(MoreFab_1.MoreFab, { iconButton: true, size: "small", anchorOrigin: {
30
29
  vertical: "bottom",
31
30
  horizontal: "right"
32
31
  }, transformOrigin: {
@@ -37,6 +36,6 @@ function MobileListItemRenderer({ data, itemHeight, margins }, renderer) {
37
36
  subheader: { variant: "caption" }
38
37
  } }), (0, jsx_runtime_1.jsx)(CardContent_1.default, { sx: {
39
38
  paddingTop: 0,
40
- paddingBottom: cardActions == null ? Reflect.get(margins, "marginBottom") : 0
39
+ paddingBottom: cardActions == null ? Reflect.get(margins, "marginBottom") ?? 0 : 0
41
40
  }, children: children }), cardActions] }));
42
41
  }
@@ -1,13 +1,12 @@
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 { ScrollerListExProps } from "./ScrollerListEx";
6
5
  import { SxProps, Theme } from "@mui/material/styles";
7
6
  /**
8
7
  * ResponsibleContainer props
9
8
  */
10
- export type ResponsibleContainerProps<T extends object, F> = Omit<DataGridExProps<T>, "height" | "itemKey" | "loadData" | "mRef" | "onScroll" | "onItemsRendered" | "onInitLoad" | "onUpdateRows"> & {
9
+ export type ResponsibleContainerProps<T extends object, F> = Omit<DataGridExProps<T>, "height" | "loadData" | "mRef" | "onInitLoad" | "onUpdateRows" | "rowHeight"> & {
11
10
  /**
12
11
  * Height will be deducted
13
12
  * @param height Current calcuated height
@@ -41,18 +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;
52
- /**
53
- * Item size, a function indicates its a variable size list
54
- */
55
- itemSize: ScrollerListExItemSize;
46
+ itemRenderer?: ScrollerListExProps<T>["itemRenderer"];
56
47
  /**
57
48
  * Load data callback
58
49
  */
@@ -60,7 +51,7 @@ export type ResponsibleContainerProps<T extends object, F> = Omit<DataGridExProp
60
51
  /**
61
52
  * Methods
62
53
  */
63
- mRef?: React.MutableRefObject<GridMethodRef<T> | undefined>;
54
+ mRef?: React.RefObject<GridMethodRef<T> | undefined>;
64
55
  /**
65
56
  * Element ready callback
66
57
  */
@@ -77,6 +68,11 @@ export type ResponsibleContainerProps<T extends object, F> = Omit<DataGridExProp
77
68
  * Quick action for double click or click under mobile
78
69
  */
79
70
  quickAction?: (data: T) => void;
71
+ /**
72
+ * Row height
73
+ * @param isGrid Is displaying as DataGrid
74
+ */
75
+ rowHeight?: number | [number, number] | (<B extends boolean>(isGrid: B) => B extends true ? DataGridExProps<T>["rowHeight"] : ScrollerListExProps<T>["rowHeight"]);
80
76
  /**
81
77
  * Size ready to read miliseconds span
82
78
  */
@@ -32,7 +32,7 @@ function defaultContainerBoxSx(paddings, hasField, _dataGrid) {
32
32
  */
33
33
  function ResponsibleContainer(props) {
34
34
  // Destruct
35
- const { adjustHeight, adjustFabHeight, cacheKey, cacheMinutes = 15, columns, containerBoxSx = defaultContainerBoxSx, elementReady, fields, fieldTemplate, height, loadData, mRef, paddings = MUGlobal_1.MUGlobal.pagePaddings, pullToRefresh = true, quickAction, sizeReadyMiliseconds = 0, searchBarHeight = 45.6, searchBarBottom = 8, searchBarTop, ...rest } = props;
35
+ const { adjustHeight, adjustFabHeight, cacheKey, cacheMinutes = 15, columns, containerBoxSx = defaultContainerBoxSx, elementReady, fields, fieldTemplate, height, loadData, mRef, paddings = MUGlobal_1.MUGlobal.pagePaddings, pullToRefresh = true, quickAction, rowHeight, sizeReadyMiliseconds = 0, searchBarHeight = 45.6, searchBarBottom = 8, searchBarTop, ...rest } = props;
36
36
  // Labels
37
37
  const labels = Labels_1.Labels.CommonPage;
38
38
  // Refs
@@ -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)");
@@ -58,6 +60,16 @@ function ResponsibleContainer(props) {
58
60
  state.mounted = true;
59
61
  return loadData(GridUtils_1.GridUtils.createLoader(props, fieldTemplate, cacheKey, false), lastItem);
60
62
  };
63
+ const getRowHeight = react_1.default.useCallback((isGrid) => {
64
+ if (rowHeight == null)
65
+ return undefined;
66
+ else if (typeof rowHeight === "number")
67
+ return isGrid ? undefined : rowHeight;
68
+ else if (Array.isArray(rowHeight))
69
+ return rowHeight[isGrid ? 0 : 1];
70
+ else
71
+ return rowHeight(isGrid);
72
+ }, [rowHeight]);
61
73
  // Search data
62
74
  const searchData = (0, react_2.useSearchParamsWithCache)(cacheKey);
63
75
  // On submit callback
@@ -83,44 +95,6 @@ function ResponsibleContainer(props) {
83
95
  state.rect = rect;
84
96
  return false;
85
97
  });
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
98
  // Rect
125
99
  const rect = dimensions[0][2];
126
100
  // Create list
@@ -150,26 +124,13 @@ function ResponsibleContainer(props) {
150
124
  if (adjustFabHeight)
151
125
  heightLocal = adjustFabHeight(heightLocal, showDataGrid);
152
126
  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 }) }));
127
+ // Remove useless props
128
+ const { itemRenderer, ...gridProps } = rest;
129
+ 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, rowHeight: getRowHeight(true), ...gridProps }) }));
159
130
  }
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 }) }));
131
+ // Remove useless props
132
+ const { checkable, borderRowsCount, bottomHeight, footerItemRenderer, headerHeight, hideFooter, hoverColor, selectable, onCellsRendered, ...listProps } = rest;
133
+ 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), rowHeight: getRowHeight(false), ...listProps }) }));
173
134
  })();
174
135
  const searchBar = react_1.default.useMemo(() => {
175
136
  if (!hasFields ||
@@ -1,55 +1,55 @@
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;
13
12
  /**
14
- * Item height
13
+ * Row data
15
14
  */
16
- itemHeight: number;
15
+ data: T;
17
16
  /**
18
- * Item space
17
+ * Style
19
18
  */
20
- space: number;
19
+ style: React.CSSProperties;
21
20
  /**
22
21
  * Default margins
23
22
  */
24
23
  margins: object;
25
- }
26
- /**
27
- * Extended ScrollerList ItemSize type
28
- * 1. Callback function
29
- * 2. Static sets
30
- * 3. Dynamic left & right margin calculation
31
- */
32
- export type ScrollerListExItemSize = ((index: number) => [number, number] | [number, number, object]) | [number, number] | [number, object, (number | string)?];
24
+ /**
25
+ * Item selected
26
+ */
27
+ selected: boolean;
28
+ };
33
29
  /**
34
30
  * Extended ScrollerList Props
35
31
  */
36
- export type ScrollerListExProps<T extends object> = Omit<ScrollerListProps<T>, "itemRenderer" | "itemSize"> & {
32
+ export type ScrollerListExProps<T extends object> = Omit<ScrollerListProps<T>, "rowComponent" | "rowHeight" | "onClick" | "onDoubleClick" | "onInitLoad"> & Partial<Pick<ScrollerListProps<T>, "rowHeight">> & {
37
33
  /**
38
34
  * Alternating colors for odd/even rows
39
35
  */
40
36
  alternatingColors?: [string?, string?];
41
37
  /**
42
- * Inner item renderer
38
+ * Cache key
43
39
  */
44
- innerItemRenderer: (props: ScrollerListExInnerItemRendererProps<T>) => React.ReactNode;
40
+ cacheKey?: string;
45
41
  /**
46
- * Item renderer
42
+ * Cache minutes
47
43
  */
48
- itemRenderer?: (props: ListChildComponentProps<T>) => React.ReactElement;
44
+ cacheMinutes?: number;
49
45
  /**
50
- * Item size, a function indicates its a variable size list
46
+ * Cell margins, default to half of MUGlobal.pagePaddings
47
+ */
48
+ cellMargins?: object;
49
+ /**
50
+ * Item renderer
51
51
  */
52
- itemSize: ScrollerListExItemSize;
52
+ itemRenderer?: (props: ScrollerListExItemRendererProps<T>) => React.ReactNode;
53
53
  /**
54
54
  * Double click handler
55
55
  */