@etsoo/materialui 1.5.70 → 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.
- package/__tests__/ReactAppTests.tsx +12 -7
- package/__tests__/SelectEx.tsx +1 -1
- package/__tests__/tsconfig.json +1 -1
- package/lib/cjs/ButtonPopupCheckbox.js +1 -1
- package/lib/cjs/ButtonPopupRadio.js +1 -1
- package/lib/cjs/DataGridEx.d.ts +8 -1
- package/lib/cjs/DataGridEx.js +71 -56
- package/lib/cjs/DataGridRenderers.d.ts +1 -1
- package/lib/cjs/DataGridRenderers.js +1 -1
- package/lib/cjs/DnDList.js +1 -1
- package/lib/cjs/MUUtils.d.ts +0 -9
- package/lib/cjs/MUUtils.js +0 -26
- package/lib/cjs/MobileListItemRenderer.d.ts +2 -2
- package/lib/cjs/ResponsibleContainer.d.ts +2 -7
- package/lib/cjs/ResponsibleContainer.js +8 -57
- package/lib/cjs/ScrollerListEx.d.ts +24 -9
- package/lib/cjs/ScrollerListEx.js +36 -39
- package/lib/cjs/SelectEx.js +2 -2
- package/lib/cjs/TableEx.d.ts +7 -0
- package/lib/cjs/TableEx.js +6 -12
- package/lib/cjs/app/ReactApp.d.ts +1 -3
- package/lib/cjs/custom/FieldDateInput.js +1 -1
- package/lib/cjs/custom/FieldInput.js +1 -1
- package/lib/cjs/custom/FieldJson.js +1 -1
- package/lib/cjs/custom/FieldNumberInput.js +1 -1
- package/lib/cjs/custom/FieldTexarea.js +1 -1
- package/lib/cjs/html/HtmlDiv.d.ts +24 -7
- package/lib/cjs/html/HtmlDiv.js +5 -1
- package/lib/cjs/pages/DataGridPage.js +3 -32
- package/lib/cjs/pages/FixedListPage.js +5 -34
- package/lib/cjs/pages/ListPage.js +1 -29
- package/lib/cjs/pages/ResponsivePage.d.ts +2 -7
- package/lib/cjs/uses/useGridCacheInitLoad.d.ts +2 -0
- package/lib/cjs/uses/useGridCacheInitLoad.js +41 -0
- package/lib/cjs/uses/useListCacheInitLoad.d.ts +2 -0
- package/lib/cjs/uses/useListCacheInitLoad.js +38 -0
- package/lib/mjs/ButtonPopupCheckbox.js +1 -1
- package/lib/mjs/ButtonPopupRadio.js +1 -1
- package/lib/mjs/DataGridEx.d.ts +8 -1
- package/lib/mjs/DataGridEx.js +71 -56
- package/lib/mjs/DataGridRenderers.d.ts +1 -1
- package/lib/mjs/DataGridRenderers.js +1 -1
- package/lib/mjs/DnDList.js +1 -1
- package/lib/mjs/MUUtils.d.ts +0 -9
- package/lib/mjs/MUUtils.js +0 -26
- package/lib/mjs/MobileListItemRenderer.d.ts +2 -2
- package/lib/mjs/ResponsibleContainer.d.ts +2 -7
- package/lib/mjs/ResponsibleContainer.js +8 -57
- package/lib/mjs/ScrollerListEx.d.ts +24 -9
- package/lib/mjs/ScrollerListEx.js +36 -39
- package/lib/mjs/SelectEx.js +2 -2
- package/lib/mjs/TableEx.d.ts +7 -0
- package/lib/mjs/TableEx.js +6 -12
- package/lib/mjs/app/ReactApp.d.ts +1 -3
- package/lib/mjs/custom/FieldDateInput.js +1 -1
- package/lib/mjs/custom/FieldInput.js +1 -1
- package/lib/mjs/custom/FieldJson.js +1 -1
- package/lib/mjs/custom/FieldNumberInput.js +1 -1
- package/lib/mjs/custom/FieldTexarea.js +1 -1
- package/lib/mjs/html/HtmlDiv.d.ts +24 -7
- package/lib/mjs/html/HtmlDiv.js +2 -1
- package/lib/mjs/pages/DataGridPage.js +3 -32
- package/lib/mjs/pages/FixedListPage.js +5 -34
- package/lib/mjs/pages/ListPage.js +1 -29
- package/lib/mjs/pages/ResponsivePage.d.ts +2 -7
- package/lib/mjs/uses/useGridCacheInitLoad.d.ts +2 -0
- package/lib/mjs/uses/useGridCacheInitLoad.js +35 -0
- package/lib/mjs/uses/useListCacheInitLoad.d.ts +2 -0
- package/lib/mjs/uses/useListCacheInitLoad.js +32 -0
- package/package.json +18 -19
- package/setupTests.ts +2 -0
- package/src/ButtonPopupCheckbox.tsx +1 -1
- package/src/ButtonPopupRadio.tsx +1 -1
- package/src/DataGridEx.tsx +151 -108
- package/src/DataGridRenderers.tsx +2 -1
- package/src/DnDList.tsx +1 -1
- package/src/MUUtils.ts +0 -33
- package/src/MobileListItemRenderer.tsx +2 -2
- package/src/ResponsibleContainer.tsx +21 -94
- package/src/ScrollerListEx.tsx +110 -122
- package/src/SelectEx.tsx +2 -2
- package/src/TableEx.tsx +20 -12
- package/src/custom/CustomFieldUtils.tsx +1 -1
- package/src/custom/FieldDateInput.tsx +1 -1
- package/src/custom/FieldInput.tsx +1 -1
- package/src/custom/FieldJson.tsx +1 -1
- package/src/custom/FieldNumberInput.tsx +1 -1
- package/src/custom/FieldTexarea.tsx +1 -1
- package/src/html/HtmlDiv.tsx +13 -9
- package/src/pages/DataGridPage.tsx +3 -49
- package/src/pages/FixedListPage.tsx +5 -49
- package/src/pages/ListPage.tsx +0 -43
- package/src/pages/ResponsivePage.tsx +3 -11
- package/src/uses/useGridCacheInitLoad.ts +55 -0
- package/src/uses/useListCacheInitLoad.ts +51 -0
package/lib/mjs/DataGridEx.js
CHANGED
|
@@ -9,6 +9,8 @@ import Box from "@mui/material/Box";
|
|
|
9
9
|
import TableSortLabel from "@mui/material/TableSortLabel";
|
|
10
10
|
import Checkbox from "@mui/material/Checkbox";
|
|
11
11
|
import Paper from "@mui/material/Paper";
|
|
12
|
+
import { GridUtils } from "./GridUtils";
|
|
13
|
+
import { useGridCacheInitLoad } from "./uses/useGridCacheInitLoad";
|
|
12
14
|
// Borders
|
|
13
15
|
const boldBorder = "2px solid rgba(224, 224, 224, 1)";
|
|
14
16
|
const thinBorder = "1px solid rgba(224, 224, 224, 1)";
|
|
@@ -128,7 +130,7 @@ export function DataGridEx(props) {
|
|
|
128
130
|
}) }));
|
|
129
131
|
}
|
|
130
132
|
// Destruct
|
|
131
|
-
const { alternatingColors = [theme.palette.grey[100], undefined], borderRowsCount, bottomHeight = 53, checkable = false, className, columns, defaultOrderBy, height, headerHeight = 56, headerRenderer = defaultHeaderRenderer, footerRenderer = defaultFooterRenderer, footerItemRenderer = DataGridRenderers.defaultFooterItemRenderer, hideFooter = false, hoverColor = "#f6f9fb", idField = "id", mRef = React.createRef(), onClick, onDoubleClick, selectable = true, selectedColor = "#edf4fb", width, ...rest } = props;
|
|
133
|
+
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.defaultFooterItemRenderer, hideFooter = false, hoverColor = "#f6f9fb", idField = "id", mRef = React.createRef(), onClick, onDataChange, onDoubleClick, onUpdateRows, selectable = true, selectedColor = "#edf4fb", width, ...rest } = props;
|
|
132
134
|
if (checkable) {
|
|
133
135
|
const cbColumn = {
|
|
134
136
|
field: "selected", // Avoid validation from data model
|
|
@@ -161,6 +163,8 @@ export function DataGridEx(props) {
|
|
|
161
163
|
columns.unshift(cbColumn);
|
|
162
164
|
}
|
|
163
165
|
}
|
|
166
|
+
// Init handler
|
|
167
|
+
const initHandler = useGridCacheInitLoad(cacheKey, cacheMinutes);
|
|
164
168
|
const refs = React.useRef({});
|
|
165
169
|
const mRefLocal = useCombinedRefs(mRef, (ref) => {
|
|
166
170
|
if (ref == null)
|
|
@@ -219,57 +223,6 @@ export function DataGridEx(props) {
|
|
|
219
223
|
div.classList.remove("DataGridEx-Hover");
|
|
220
224
|
});
|
|
221
225
|
};
|
|
222
|
-
/**
|
|
223
|
-
* Item renderer
|
|
224
|
-
*/
|
|
225
|
-
const itemRenderer = ({ columnIndex, rowIndex, style, data, selectedItems, setItems }) => {
|
|
226
|
-
// Column
|
|
227
|
-
const { align, cellRenderer = DataGridRenderers.defaultCellRenderer, cellBoxStyle, field, type, valueFormatter, renderProps } = columns[columnIndex];
|
|
228
|
-
// Props
|
|
229
|
-
const formatProps = {
|
|
230
|
-
data,
|
|
231
|
-
field,
|
|
232
|
-
rowIndex,
|
|
233
|
-
columnIndex
|
|
234
|
-
};
|
|
235
|
-
let rowClass = `DataGridEx-Cell${rowIndex % 2}`;
|
|
236
|
-
if (borderRowsCount != null &&
|
|
237
|
-
borderRowsCount > 0 &&
|
|
238
|
-
(rowIndex + 1) % borderRowsCount === 0) {
|
|
239
|
-
rowClass += ` DataGridEx-Cell-Border`;
|
|
240
|
-
}
|
|
241
|
-
// Selected
|
|
242
|
-
const selected = data != null &&
|
|
243
|
-
(selectedRowIndex.current === rowIndex ||
|
|
244
|
-
selectedItems.some((selectedItem) => selectedItem != null && selectedItem[idField] === data[idField]));
|
|
245
|
-
if (selected) {
|
|
246
|
-
rowClass += ` DataGridEx-Selected`;
|
|
247
|
-
}
|
|
248
|
-
// Box style
|
|
249
|
-
const boxStyle = data == null || cellBoxStyle == null
|
|
250
|
-
? undefined
|
|
251
|
-
: typeof cellBoxStyle === "function"
|
|
252
|
-
? cellBoxStyle(data)
|
|
253
|
-
: cellBoxStyle;
|
|
254
|
-
const cellProps = {
|
|
255
|
-
className: "DataGridEx-Cell",
|
|
256
|
-
textAlign: GridAlignGet(align, type),
|
|
257
|
-
sx: { ...boxStyle }
|
|
258
|
-
};
|
|
259
|
-
const child = cellRenderer({
|
|
260
|
-
data,
|
|
261
|
-
field,
|
|
262
|
-
formattedValue: valueFormatter ? valueFormatter(formatProps) : undefined,
|
|
263
|
-
selected,
|
|
264
|
-
type,
|
|
265
|
-
rowIndex,
|
|
266
|
-
columnIndex,
|
|
267
|
-
cellProps,
|
|
268
|
-
renderProps: typeof renderProps === "function" ? renderProps(data) : renderProps,
|
|
269
|
-
setItems
|
|
270
|
-
});
|
|
271
|
-
return (_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: _jsx(Box, { ...cellProps, onMouseEnter: handleMouseEnter, children: child }) }));
|
|
272
|
-
};
|
|
273
226
|
// Column width calculator
|
|
274
227
|
const widthCalculator = React.useMemo(() => DataGridExCalColumns(columns), [columns]);
|
|
275
228
|
// Column width
|
|
@@ -287,12 +240,74 @@ export function DataGridEx(props) {
|
|
|
287
240
|
const sharedWidth = leftWidth > 0 ? leftWidth / widthCalculator.unset : 0;
|
|
288
241
|
return (column.minWidth || minWidth) + sharedWidth;
|
|
289
242
|
}, [columns, width]);
|
|
243
|
+
const onUpdateRowsHandler = React.useCallback((rows, state) => {
|
|
244
|
+
GridUtils.getUpdateRowsHandler(cacheKey)?.(rows, state);
|
|
245
|
+
onUpdateRows?.(rows, state);
|
|
246
|
+
}, [onUpdateRows, cacheKey]);
|
|
290
247
|
// Table
|
|
291
248
|
const table = React.useMemo(() => {
|
|
292
|
-
return (_jsx(ScrollerGrid, { className: Utils.mergeClasses("DataGridEx-Body", "DataGridEx-CustomBar", className, createGridStyle(alternatingColors, selectedColor, hoverColor)),
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
249
|
+
return (_jsx(ScrollerGrid, { className: Utils.mergeClasses("DataGridEx-Body", "DataGridEx-CustomBar", className, createGridStyle(alternatingColors, selectedColor, hoverColor)), onCellsRendered: cacheKey
|
|
250
|
+
? (visibleCells) => sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(visibleCells))
|
|
251
|
+
: undefined, onInitLoad: initHandler, onUpdateRows: onUpdateRowsHandler, cellComponent: ({ rowIndex, columnIndex, style, rows, states }) => {
|
|
252
|
+
// Column
|
|
253
|
+
const { align, cellRenderer = DataGridRenderers.defaultCellRenderer, cellBoxStyle, field, type, valueFormatter, renderProps } = columns[columnIndex];
|
|
254
|
+
// Data
|
|
255
|
+
const data = rows[rowIndex];
|
|
256
|
+
// Props
|
|
257
|
+
const formatProps = {
|
|
258
|
+
data,
|
|
259
|
+
field,
|
|
260
|
+
rowIndex,
|
|
261
|
+
columnIndex
|
|
262
|
+
};
|
|
263
|
+
let rowClass = `DataGridEx-Cell${rowIndex % 2}`;
|
|
264
|
+
if (borderRowsCount != null &&
|
|
265
|
+
borderRowsCount > 0 &&
|
|
266
|
+
(rowIndex + 1) % borderRowsCount === 0) {
|
|
267
|
+
rowClass += ` DataGridEx-Cell-Border`;
|
|
268
|
+
}
|
|
269
|
+
// Selected
|
|
270
|
+
const selected = data != null &&
|
|
271
|
+
(selectedRowIndex.current === rowIndex ||
|
|
272
|
+
states.selectedItems.some((selectedItem) => selectedItem != null &&
|
|
273
|
+
selectedItem[idField] === data[idField]));
|
|
274
|
+
if (selected) {
|
|
275
|
+
rowClass += ` DataGridEx-Selected`;
|
|
276
|
+
}
|
|
277
|
+
// Box style
|
|
278
|
+
const boxStyle = data == null || cellBoxStyle == null
|
|
279
|
+
? undefined
|
|
280
|
+
: typeof cellBoxStyle === "function"
|
|
281
|
+
? cellBoxStyle(data)
|
|
282
|
+
: cellBoxStyle;
|
|
283
|
+
const cellProps = {
|
|
284
|
+
className: "DataGridEx-Cell",
|
|
285
|
+
textAlign: GridAlignGet(align, type),
|
|
286
|
+
sx: { ...boxStyle }
|
|
287
|
+
};
|
|
288
|
+
const child = cellRenderer({
|
|
289
|
+
data,
|
|
290
|
+
field,
|
|
291
|
+
formattedValue: valueFormatter
|
|
292
|
+
? valueFormatter(formatProps)
|
|
293
|
+
: undefined,
|
|
294
|
+
selected,
|
|
295
|
+
type,
|
|
296
|
+
rowIndex,
|
|
297
|
+
columnIndex,
|
|
298
|
+
cellProps,
|
|
299
|
+
renderProps: typeof renderProps === "function"
|
|
300
|
+
? renderProps(data)
|
|
301
|
+
: renderProps,
|
|
302
|
+
triggerChange: () => onDataChange?.(rows, rowIndex, columnIndex)
|
|
303
|
+
});
|
|
304
|
+
return (_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: _jsx(Box, { ...cellProps, onMouseEnter: handleMouseEnter, children: child }) }));
|
|
305
|
+
}, columnCount: columns.length, columnWidth: columnWidth, defaultOrderBy: defaultOrderBy, height: typeof height === "number"
|
|
306
|
+
? height -
|
|
307
|
+
headerHeight -
|
|
308
|
+
(hideFooter ? 0 : bottomHeight + 1) -
|
|
309
|
+
scrollbarSize
|
|
310
|
+
: height, headerRenderer: headerRenderer, idField: idField, footerRenderer: hideFooter ? undefined : footerRenderer, width: Math.max(width ?? 0, widthCalculator.total), mRef: mRefLocal, ...rest }));
|
|
296
311
|
}, [width]);
|
|
297
312
|
return (_jsx(Paper, { sx: {
|
|
298
313
|
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
|
|
@@ -15,7 +15,7 @@ export var DataGridRenderers;
|
|
|
15
15
|
* @param param Props
|
|
16
16
|
* @returns Component
|
|
17
17
|
*/
|
|
18
|
-
function defaultCellRenderer({ cellProps, data, field, formattedValue, columnIndex, type, renderProps }) {
|
|
18
|
+
function defaultCellRenderer({ cellProps, data, field, formattedValue, columnIndex, type, renderProps, triggerChange }) {
|
|
19
19
|
// Is loading
|
|
20
20
|
if (data == null) {
|
|
21
21
|
// First column, show loading indicator
|
package/lib/mjs/DnDList.js
CHANGED
|
@@ -167,7 +167,7 @@ export function DnDList(props) {
|
|
|
167
167
|
? input.removeEventListener("change", doFormChange)
|
|
168
168
|
: input.addEventListener("change", doFormChange));
|
|
169
169
|
};
|
|
170
|
-
const divRef = React.useRef();
|
|
170
|
+
const divRef = React.useRef(null);
|
|
171
171
|
if (dnd == null) {
|
|
172
172
|
return _jsx(Skeleton, { variant: "rectangular", width: "100%", height: height });
|
|
173
173
|
}
|
package/lib/mjs/MUUtils.d.ts
CHANGED
|
@@ -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
|
}
|
package/lib/mjs/MUUtils.js
CHANGED
|
@@ -26,30 +26,4 @@ export var MUUtils;
|
|
|
26
26
|
return items;
|
|
27
27
|
}
|
|
28
28
|
MUUtils.getGridData = getGridData;
|
|
29
|
-
/**
|
|
30
|
-
* Setup paging keysets
|
|
31
|
-
* @param data Paging data
|
|
32
|
-
* @param lastItem Last item of the query
|
|
33
|
-
* @param idField Id field
|
|
34
|
-
*/
|
|
35
|
-
function setupPagingKeysets(data, lastItem, idField) {
|
|
36
|
-
// If the id field is not set for ordering, add it with descending
|
|
37
|
-
if (typeof data.queryPaging === "object") {
|
|
38
|
-
const orderBy = (data.queryPaging.orderBy ??= []);
|
|
39
|
-
const idUpper = idField.toUpperCase();
|
|
40
|
-
if (!orderBy.find((o) => o.field.toUpperCase() === idUpper)) {
|
|
41
|
-
orderBy.push({ field: idField, desc: true, unique: true });
|
|
42
|
-
}
|
|
43
|
-
// Set the paging keysets
|
|
44
|
-
if (lastItem) {
|
|
45
|
-
const keysets = orderBy.map((o) => Reflect.get(lastItem, o.field));
|
|
46
|
-
data.queryPaging.keysets = keysets;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
data.queryPaging.keysets = undefined;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return data;
|
|
53
|
-
}
|
|
54
|
-
MUUtils.setupPagingKeysets = setupPagingKeysets;
|
|
55
29
|
})(MUUtils || (MUUtils = {}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ListItemReact } from "@etsoo/react";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
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 }:
|
|
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 {
|
|
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?:
|
|
46
|
+
itemRenderer?: ScrollerListExProps<T>["itemRenderer"];
|
|
52
47
|
/**
|
|
53
48
|
* Item size, a function indicates its a variable size list
|
|
54
49
|
*/
|
|
@@ -36,6 +36,8 @@ export function ResponsibleContainer(props) {
|
|
|
36
36
|
if (ref == null)
|
|
37
37
|
return;
|
|
38
38
|
state.ref = ref;
|
|
39
|
+
if (ref.element && elementReady)
|
|
40
|
+
elementReady(ref.element, true);
|
|
39
41
|
});
|
|
40
42
|
// Screen size detection
|
|
41
43
|
const showDataGrid = useMediaQuery("(min-width:600px)");
|
|
@@ -77,44 +79,6 @@ export function ResponsibleContainer(props) {
|
|
|
77
79
|
state.rect = rect;
|
|
78
80
|
return false;
|
|
79
81
|
});
|
|
80
|
-
const onInitLoad = (ref) => {
|
|
81
|
-
// Avoid repeatedly load from cache
|
|
82
|
-
if (refs.current.initLoaded || !cacheKey)
|
|
83
|
-
return undefined;
|
|
84
|
-
// Cache data
|
|
85
|
-
const cacheData = GridUtils.getCacheData(cacheKey, cacheMinutes);
|
|
86
|
-
if (cacheData) {
|
|
87
|
-
const { rows, state } = cacheData;
|
|
88
|
-
GridUtils.mergeSearchData(state, searchData);
|
|
89
|
-
// Scroll position
|
|
90
|
-
const scrollData = sessionStorage.getItem(`${cacheKey}-scroll`);
|
|
91
|
-
if (scrollData) {
|
|
92
|
-
if ("resetAfterColumnIndex" in ref) {
|
|
93
|
-
const { scrollLeft, scrollTop } = JSON.parse(scrollData);
|
|
94
|
-
globalThis.setTimeout(() => ref.scrollTo({ scrollLeft, scrollTop }), 0);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
const { scrollOffset } = JSON.parse(scrollData);
|
|
98
|
-
globalThis.setTimeout(() => ref.scrollTo(scrollOffset), 0);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Update flag value
|
|
102
|
-
refs.current.initLoaded = true;
|
|
103
|
-
// Return cached rows and state
|
|
104
|
-
return [rows, state];
|
|
105
|
-
}
|
|
106
|
-
return undefined;
|
|
107
|
-
};
|
|
108
|
-
const onListScroll = (props) => {
|
|
109
|
-
if (!cacheKey || !refs.current.initLoaded)
|
|
110
|
-
return;
|
|
111
|
-
sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
|
|
112
|
-
};
|
|
113
|
-
const onGridScroll = (props) => {
|
|
114
|
-
if (!cacheKey || !refs.current.initLoaded)
|
|
115
|
-
return;
|
|
116
|
-
sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(props));
|
|
117
|
-
};
|
|
118
82
|
// Rect
|
|
119
83
|
const rect = dimensions[0][2];
|
|
120
84
|
// Create list
|
|
@@ -144,26 +108,13 @@ export function ResponsibleContainer(props) {
|
|
|
144
108
|
if (adjustFabHeight)
|
|
145
109
|
heightLocal = adjustFabHeight(heightLocal, showDataGrid);
|
|
146
110
|
if (showDataGrid) {
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
return (_jsx(Box, { className: "DataGridBox", children: _jsx(DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, onDoubleClick: (_, data) => quickAction && quickAction(data),
|
|
150
|
-
if (element != null && elementReady)
|
|
151
|
-
elementReady(element, true);
|
|
152
|
-
}, onScroll: onGridScroll, columns: columns, onUpdateRows: GridUtils.getUpdateRowsHandler(cacheKey), onInitLoad: onInitLoad, ...rest }) }));
|
|
111
|
+
// Remove useless props
|
|
112
|
+
const { itemRenderer, ...gridProps } = rest;
|
|
113
|
+
return (_jsx(Box, { className: "DataGridBox", children: _jsx(DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, onDoubleClick: (_, data) => quickAction && quickAction(data), columns: columns, ...gridProps }) }));
|
|
153
114
|
}
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
delete rest.bottomHeight;
|
|
158
|
-
delete rest.footerItemRenderer;
|
|
159
|
-
delete rest.headerHeight;
|
|
160
|
-
delete rest.hideFooter;
|
|
161
|
-
delete rest.hoverColor;
|
|
162
|
-
delete rest.selectable;
|
|
163
|
-
return (_jsx(Box, { className: "ListBox", sx: { height: heightLocal }, children: _jsx(ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, onUpdateRows: GridUtils.getUpdateRowsHandler(cacheKey), onInitLoad: onInitLoad, mRef: mRefs, onClick: (event, data) => quickAction && ReactUtils.isSafeClick(event) && quickAction(data), oRef: (element) => {
|
|
164
|
-
if (element != null && elementReady)
|
|
165
|
-
elementReady(element, false);
|
|
166
|
-
}, onScroll: onListScroll, ...rest }) }));
|
|
115
|
+
// Remove useless props
|
|
116
|
+
const { checkable, borderRowsCount, bottomHeight, footerItemRenderer, headerHeight, hideFooter, hoverColor, selectable, ...listProps } = rest;
|
|
117
|
+
return (_jsx(Box, { className: "ListBox", sx: { height: heightLocal }, children: _jsx(ScrollerListEx, { autoLoad: !hasFields, height: heightLocal, loadData: localLoadData, mRef: mRefs, onClick: (event, data) => quickAction && ReactUtils.isSafeClick(event) && quickAction(data), ...listProps }) }));
|
|
167
118
|
})();
|
|
168
119
|
const searchBar = React.useMemo(() => {
|
|
169
120
|
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
|
|
7
|
+
export type ScrollerListExItemRendererProps<T> = {
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
9
|
+
* Row index
|
|
11
10
|
*/
|
|
12
|
-
|
|
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>, "
|
|
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
|
-
*
|
|
53
|
+
* Cache key
|
|
54
|
+
*/
|
|
55
|
+
cacheKey?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Cache minutes
|
|
43
58
|
*/
|
|
44
|
-
|
|
59
|
+
cacheMinutes?: number;
|
|
45
60
|
/**
|
|
46
61
|
* Item renderer
|
|
47
62
|
*/
|
|
48
|
-
itemRenderer?: (props:
|
|
63
|
+
itemRenderer?: (props: ScrollerListExItemRendererProps<T>) => React.ReactNode;
|
|
49
64
|
/**
|
|
50
65
|
* Item size, a function indicates its a variable size list
|
|
51
66
|
*/
|
|
@@ -5,6 +5,9 @@ import { Utils } from "@etsoo/shared";
|
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { MUGlobal } from "./MUGlobal";
|
|
7
7
|
import { useTheme } from "@mui/material/styles";
|
|
8
|
+
import { GridUtils } from "./GridUtils";
|
|
9
|
+
import { useListCacheInitLoad } from "./uses/useListCacheInitLoad";
|
|
10
|
+
import Box from "@mui/material/Box";
|
|
8
11
|
// Scroll bar size
|
|
9
12
|
const scrollbarSize = 16;
|
|
10
13
|
// Selected class name
|
|
@@ -68,24 +71,6 @@ const defaultMargin = (margin, horizon) => {
|
|
|
68
71
|
marginBottom: half
|
|
69
72
|
};
|
|
70
73
|
};
|
|
71
|
-
// Default itemRenderer
|
|
72
|
-
function defaultItemRenderer({ index, innerItemRenderer, data, onMouseDown, selected, style, itemHeight, onClick, onDoubleClick, space, margins }) {
|
|
73
|
-
// Child
|
|
74
|
-
const child = innerItemRenderer({
|
|
75
|
-
index,
|
|
76
|
-
data,
|
|
77
|
-
style,
|
|
78
|
-
selected,
|
|
79
|
-
itemHeight,
|
|
80
|
-
space,
|
|
81
|
-
margins
|
|
82
|
-
});
|
|
83
|
-
let rowClass = `ScrollerListEx-Row${index % 2}`;
|
|
84
|
-
if (selected)
|
|
85
|
-
rowClass += ` ${selectedClassName}`;
|
|
86
|
-
// Layout
|
|
87
|
-
return (_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 }));
|
|
88
|
-
}
|
|
89
74
|
/**
|
|
90
75
|
* Extended ScrollerList
|
|
91
76
|
* @param props Props
|
|
@@ -93,7 +78,7 @@ function defaultItemRenderer({ index, innerItemRenderer, data, onMouseDown, sele
|
|
|
93
78
|
*/
|
|
94
79
|
export function ScrollerListEx(props) {
|
|
95
80
|
// Selected item ref
|
|
96
|
-
const selectedItem = React.useRef();
|
|
81
|
+
const selectedItem = React.useRef(null);
|
|
97
82
|
const onMouseDown = (div, data) => {
|
|
98
83
|
// Destruct
|
|
99
84
|
const [selectedDiv, selectedData] = selectedItem.current ?? [];
|
|
@@ -113,20 +98,12 @@ export function ScrollerListEx(props) {
|
|
|
113
98
|
return selected;
|
|
114
99
|
};
|
|
115
100
|
// Destruct
|
|
116
|
-
const { alternatingColors = [undefined, undefined], className, idField = "id",
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
onClick,
|
|
123
|
-
onDoubleClick,
|
|
124
|
-
space,
|
|
125
|
-
margins,
|
|
126
|
-
selected: isSelected(itemProps.data),
|
|
127
|
-
...itemProps
|
|
128
|
-
});
|
|
129
|
-
}, onClick, onDoubleClick, onSelectChange, selectedColor = "#edf4fb", ...rest } = props;
|
|
101
|
+
const { alternatingColors = [undefined, undefined], className, cacheKey, cacheMinutes = 15, idField = "id", itemSize, itemRenderer = ({ data, itemHeight, margins }) => (_jsx(Box, { component: "pre", sx: {
|
|
102
|
+
height: itemHeight,
|
|
103
|
+
...margins
|
|
104
|
+
}, children: JSON.stringify(data) })), onClick, onDoubleClick, onUpdateRows, onSelectChange, selectedColor = "#edf4fb", ...rest } = props;
|
|
105
|
+
// Init handler
|
|
106
|
+
const initHandler = useListCacheInitLoad(cacheKey, cacheMinutes);
|
|
130
107
|
// Theme
|
|
131
108
|
const theme = useTheme();
|
|
132
109
|
// Cache calculation
|
|
@@ -150,11 +127,31 @@ export function ScrollerListEx(props) {
|
|
|
150
127
|
// Calculation
|
|
151
128
|
return itemSizeResult;
|
|
152
129
|
};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
130
|
+
const onUpdateRowsHandler = React.useCallback((rows, state) => {
|
|
131
|
+
GridUtils.getUpdateRowsHandler(cacheKey)?.(rows, state);
|
|
132
|
+
onUpdateRows?.(rows, state);
|
|
133
|
+
}, [onUpdateRows, cacheKey]);
|
|
158
134
|
// Layout
|
|
159
|
-
return (_jsx(ScrollerList, { className: Utils.mergeClasses("ScrollerListEx-Body", className, createGridStyle(alternatingColors, selectedColor)), idField: idField,
|
|
135
|
+
return (_jsx(ScrollerList, { className: Utils.mergeClasses("ScrollerListEx-Body", className, createGridStyle(alternatingColors, selectedColor)), idField: idField, onRowsRendered: cacheKey
|
|
136
|
+
? (visibleRows) => sessionStorage.setItem(`${cacheKey}-scroll`, JSON.stringify(visibleRows))
|
|
137
|
+
: undefined, onInitLoad: initHandler, onUpdateRows: onUpdateRowsHandler, rowComponent: ({ index, items, style }) => {
|
|
138
|
+
const data = items[index];
|
|
139
|
+
const selected = isSelected(data);
|
|
140
|
+
const rowClass = `ScrollerListEx-Row${index % 2}${selected ? ` ${selectedClassName}` : ""}`;
|
|
141
|
+
const [itemHeight, space, margins] = calculateItemSize(index);
|
|
142
|
+
// Child
|
|
143
|
+
const child = itemRenderer({
|
|
144
|
+
index,
|
|
145
|
+
data,
|
|
146
|
+
style,
|
|
147
|
+
selected,
|
|
148
|
+
itemHeight,
|
|
149
|
+
space,
|
|
150
|
+
margins
|
|
151
|
+
});
|
|
152
|
+
return (_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 }));
|
|
153
|
+
}, rowHeight: (index) => {
|
|
154
|
+
const [size, space] = calculateItemSize(index);
|
|
155
|
+
return size + space;
|
|
156
|
+
}, ...rest }));
|
|
160
157
|
}
|
package/lib/mjs/SelectEx.js
CHANGED
|
@@ -63,7 +63,7 @@ export function SelectEx(props) {
|
|
|
63
63
|
}, [options, propertyWay, setOptionsAdd]);
|
|
64
64
|
// Value state
|
|
65
65
|
const [valueState, setValueStateBase] = React.useState(valueSource);
|
|
66
|
-
const valueRef = React.useRef();
|
|
66
|
+
const valueRef = React.useRef(null);
|
|
67
67
|
const setValueState = (newValue) => {
|
|
68
68
|
valueRef.current = newValue;
|
|
69
69
|
setValueStateBase(newValue);
|
|
@@ -108,7 +108,7 @@ export function SelectEx(props) {
|
|
|
108
108
|
: option[labelField];
|
|
109
109
|
};
|
|
110
110
|
// Refs
|
|
111
|
-
const divRef = React.useRef();
|
|
111
|
+
const divRef = React.useRef(null);
|
|
112
112
|
// Refresh list data
|
|
113
113
|
const refreshData = () => {
|
|
114
114
|
if (loadData == null)
|
package/lib/mjs/TableEx.d.ts
CHANGED
|
@@ -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
|
*/
|
package/lib/mjs/TableEx.js
CHANGED
|
@@ -27,7 +27,7 @@ export function TableEx(props) {
|
|
|
27
27
|
// Theme
|
|
28
28
|
const theme = useTheme();
|
|
29
29
|
// Destruct
|
|
30
|
-
const { alternatingColors = [theme.palette.action.hover, undefined], autoLoad = true, columns, defaultOrderBy, headerColors = [undefined, undefined], idField = "id", loadBatchSize, loadData, maxHeight, mRef, onSelectChange, rowHeight = 53, otherHeight = 110, threshold, ...rest } = props;
|
|
30
|
+
const { alternatingColors = [theme.palette.action.hover, undefined], autoLoad = true, columns, defaultOrderBy, headerColors = [undefined, undefined], idField = "id", loadBatchSize, loadData, maxHeight, mRef, onDataChange, onSelectChange, rowHeight = 53, otherHeight = 110, threshold, ...rest } = props;
|
|
31
31
|
const selectable = onSelectChange != null;
|
|
32
32
|
// Rows per page
|
|
33
33
|
let rowsPerPageLocal;
|
|
@@ -83,6 +83,9 @@ export function TableEx(props) {
|
|
|
83
83
|
});
|
|
84
84
|
};
|
|
85
85
|
React.useImperativeHandle(mRef, () => ({
|
|
86
|
+
get element() {
|
|
87
|
+
return null;
|
|
88
|
+
},
|
|
86
89
|
delete(index) {
|
|
87
90
|
const item = rows.at(index);
|
|
88
91
|
if (item) {
|
|
@@ -97,20 +100,11 @@ export function TableEx(props) {
|
|
|
97
100
|
newRows.splice(start, 0, item);
|
|
98
101
|
setRows(newRows);
|
|
99
102
|
},
|
|
100
|
-
/**
|
|
101
|
-
* Refresh data
|
|
102
|
-
*/
|
|
103
103
|
refresh() {
|
|
104
104
|
loadDataLocal();
|
|
105
105
|
},
|
|
106
|
-
/**
|
|
107
|
-
* Reset
|
|
108
|
-
*/
|
|
109
106
|
reset,
|
|
110
|
-
|
|
111
|
-
// Not implemented
|
|
112
|
-
},
|
|
113
|
-
scrollToItemRef(index) {
|
|
107
|
+
scrollToRow(param) {
|
|
114
108
|
// Not implemented
|
|
115
109
|
}
|
|
116
110
|
}), []);
|
|
@@ -284,7 +278,7 @@ export function TableEx(props) {
|
|
|
284
278
|
rowIndex,
|
|
285
279
|
columnIndex,
|
|
286
280
|
cellProps,
|
|
287
|
-
|
|
281
|
+
triggerChange: () => onDataChange?.(rows, rowIndex, columnIndex)
|
|
288
282
|
})) : (_jsx(React.Fragment, { children: "\u00A0" }));
|
|
289
283
|
return (_jsx(TableCell, { ...cellProps, children: child }, `${rowId}${columnIndex}`));
|
|
290
284
|
})] }, rowId));
|
|
@@ -173,9 +173,7 @@ export declare class ReactApp<S extends IAppSettings, D extends IUser> extends C
|
|
|
173
173
|
* @param props Props
|
|
174
174
|
*/
|
|
175
175
|
showInputDialog({ title, message, callback, ...rest }: InputDialogProps): INotificationReact;
|
|
176
|
-
stateDetector(props: IStateProps): React.FunctionComponentElement<
|
|
177
|
-
children?: React.ReactNode | undefined;
|
|
178
|
-
}>;
|
|
176
|
+
stateDetector(props: IStateProps): React.FunctionComponentElement<React.FragmentProps>;
|
|
179
177
|
/**
|
|
180
178
|
* User login extended
|
|
181
179
|
* @param user New user
|
|
@@ -10,7 +10,7 @@ import Typography from "@mui/material/Typography";
|
|
|
10
10
|
*/
|
|
11
11
|
export const FieldDateInput = ({ field, mref, onChange, defaultValue }) => {
|
|
12
12
|
// Ref
|
|
13
|
-
const inputRef = React.useRef();
|
|
13
|
+
const inputRef = React.useRef(null);
|
|
14
14
|
const getValue = () => inputRef.current == null
|
|
15
15
|
? undefined
|
|
16
16
|
: DateUtils.parse(inputRef.current.value);
|