@etsoo/materialui
Version:
TypeScript Material-UI Implementation
305 lines (304 loc) • 14.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TableExMinWidth = void 0;
exports.TableEx = TableEx;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("@etsoo/react");
const shared_1 = require("@etsoo/shared");
const react_2 = __importDefault(require("react"));
const DataGridRenderers_1 = require("./DataGridRenderers");
const Table_1 = __importDefault(require("@mui/material/Table"));
const styles_1 = require("@mui/material/styles");
const Paper_1 = __importDefault(require("@mui/material/Paper"));
const TableContainer_1 = __importDefault(require("@mui/material/TableContainer"));
const TableHead_1 = __importDefault(require("@mui/material/TableHead"));
const TableRow_1 = __importDefault(require("@mui/material/TableRow"));
const TableCell_1 = __importDefault(require("@mui/material/TableCell"));
const Checkbox_1 = __importDefault(require("@mui/material/Checkbox"));
const TableSortLabel_1 = __importDefault(require("@mui/material/TableSortLabel"));
const TableBody_1 = __importDefault(require("@mui/material/TableBody"));
const TablePagination_1 = __importDefault(require("@mui/material/TablePagination"));
/**
* Extended table min width for width-unset column
*/
exports.TableExMinWidth = 180;
/**
* Extended Table
* @param props Props
* @returns Component
*/
function TableEx(props) {
// Theme
const theme = (0, styles_1.useTheme)();
// Destruct
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;
const selectable = onSelectChange != null;
// Rows per page
let rowsPerPageLocal;
if (maxHeight != null) {
if (loadBatchSize != null)
rowsPerPageLocal = (0, react_1.GridSizeGet)(loadBatchSize, maxHeight);
else
rowsPerPageLocal = Math.floor((maxHeight - otherHeight) / rowHeight);
}
else if (typeof loadBatchSize === "number") {
rowsPerPageLocal = loadBatchSize;
}
else {
rowsPerPageLocal = 10;
}
// Rows
const [rows, updateRows] = react_2.default.useState([]);
const setRows = (rows) => {
state.loadedItems = rows.length;
updateRows(rows);
};
// States
const stateRefs = react_2.default.useRef({
queryPaging: {
currentPage: 0,
orderBy: defaultOrderBy,
batchSize: rowsPerPageLocal
},
autoLoad,
loadedItems: 0,
hasNextPage: true,
isNextPageLoading: false,
selectedItems: [],
idCache: {}
});
const state = stateRefs.current;
// Reset the state and load again
const reset = (add) => {
const { queryPaging, ...rest } = add ?? {};
const resetState = {
autoLoad: true,
loadedItems: 0,
hasNextPage: true,
isNextPageLoading: false,
lastLoadedItems: undefined,
lastItem: undefined,
...rest
};
Object.assign(state, resetState);
Object.assign(state.queryPaging, {
currentPage: 0,
...queryPaging
});
};
react_2.default.useImperativeHandle(mRef, () => ({
delete(index) {
const item = rows.at(index);
if (item) {
const newRows = [...rows];
newRows.splice(index, 1);
setRows(newRows);
}
return item;
},
insert(item, start) {
const newRows = [...rows];
newRows.splice(start, 0, item);
setRows(newRows);
},
/**
* Refresh data
*/
refresh() {
loadDataLocal();
},
/**
* Reset
*/
reset,
scrollToRef(scrollOffset) {
// Not implemented
},
scrollToItemRef(index) {
// Not implemented
}
}), []);
// Load data
const loadDataLocal = () => {
// Prevent multiple loadings
if (!state.hasNextPage || state.isNextPageLoading)
return;
// Update state
state.isNextPageLoading = true;
// Parameters
const { queryPaging, data, isMounted } = state;
const loadProps = {
queryPaging,
data
};
loadData(loadProps).then((result) => {
state.isMounted = true;
if (result == null || isMounted === false) {
return;
}
const newItems = result.length;
state.lastLoadedItems = newItems;
state.hasNextPage = newItems >= queryPaging.batchSize;
state.isNextPageLoading = false;
// Update rows
setRows(result);
});
};
const handleChangePage = (_event, newPage) => {
state.hasNextPage = true;
state.queryPaging.currentPage = newPage;
loadDataLocal();
};
const handleChangeRowsPerPage = (event) => {
const batchSize = parseInt(event.target.value);
reset({ queryPaging: { batchSize } });
};
const handleSelect = (item, checked) => {
const selectedItems = state.selectedItems;
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === item[idField]);
if (checked) {
if (index === -1)
selectedItems.push(item);
}
else {
if (index !== -1)
selectedItems.splice(index, 1);
}
if (onSelectChange != null) {
onSelectChange(selectedItems);
}
};
const handleSelectAll = (checked) => {
const selectedItems = state.selectedItems;
rows.forEach((row) => {
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === row[idField]);
if (checked) {
if (index === -1)
selectedItems.push(row);
}
else if (index !== -1) {
selectedItems.splice(index, 1);
}
});
if (onSelectChange != null) {
onSelectChange(selectedItems);
}
};
// New sort
const handleSort = (field, asc) => {
reset({ queryPaging: { orderBy: [{ field, desc: !(asc ?? true) }] } });
};
// Set items for rerenderer
const setItems = (callback) => {
const result = callback(rows);
if (result == null)
return;
setRows(result);
};
// Destruct states
const { queryPaging, autoLoad: stateAutoLoad, hasNextPage, lastLoadedItems, selectedItems } = state;
const currentPage = queryPaging.currentPage ?? 0;
const batchSize = queryPaging.batchSize;
// Current page selected items
const pageSelectedItems = selectable
? rows.reduce((previousValue, currentItem) => {
if (selectedItems.some((item) => item[idField] === currentItem[idField]))
return previousValue + 1;
return previousValue;
}, 0)
: 0;
// Total rows
const totalRows = hasNextPage
? -1
: currentPage * batchSize + (lastLoadedItems ?? 0);
// Auto load data when current page is 0
if (currentPage === 0 && stateAutoLoad && lastLoadedItems == null)
loadDataLocal();
react_2.default.useEffect(() => {
return () => {
state.isMounted = false;
};
}, []);
// Layout
return ((0, jsx_runtime_1.jsxs)(Paper_1.default, { children: [(0, jsx_runtime_1.jsx)(TableContainer_1.default, { sx: { maxHeight }, children: (0, jsx_runtime_1.jsxs)(Table_1.default, { ...rest, children: [(0, jsx_runtime_1.jsx)(TableHead_1.default, { children: (0, jsx_runtime_1.jsxs)(TableRow_1.default, { sx: {
"& th": {
backgroundColor: headerColors[0],
color: headerColors[1]
}
}, children: [selectable && ((0, jsx_runtime_1.jsx)(TableCell_1.default, { padding: "checkbox", children: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { color: "primary", indeterminate: pageSelectedItems > 0 && pageSelectedItems < rows.length, checked: pageSelectedItems > 0, onChange: (_event, checked) => handleSelectAll(checked) }) })), columns.map((column, index) => {
// Destruct
const { align, field, header, minWidth, sortable, sortAsc = true, type, width } = column;
// Header text
const headerText = header ?? field;
// Sortable
let sortLabel;
if (sortable && field != null) {
const active = queryPaging.orderBy?.some((o) => o.field.toLowerCase() === field.toLowerCase());
sortLabel = ((0, jsx_runtime_1.jsx)(TableSortLabel_1.default, { active: active, direction: sortAsc ? "asc" : "desc", onClick: (_event) => {
if (active)
column.sortAsc = !sortAsc;
handleSort(field, column.sortAsc);
}, children: headerText }));
}
else {
sortLabel = headerText;
}
return ((0, jsx_runtime_1.jsx)(TableCell_1.default, { align: (0, react_1.GridAlignGet)(align, type), width: width, sx: {
minWidth: minWidth == null
? width == null
? exports.TableExMinWidth
: undefined
: minWidth
}, children: sortLabel }, field ?? index.toString()));
})] }) }), (0, jsx_runtime_1.jsx)(TableBody_1.default, { sx: {
"& tr:nth-of-type(odd):not(.Mui-selected)": {
backgroundColor: alternatingColors[0]
},
"& tr:nth-of-type(even):not(.Mui-selected)": {
backgroundColor: alternatingColors[1]
}
}, children: [...Array(queryPaging.batchSize)].map((_item, rowIndex) => {
// Row
const row = rowIndex < rows.length ? rows[rowIndex] : undefined;
// Row id field value
const rowId = shared_1.DataTypes.getValue(row, idField) ?? rowIndex;
// Selected or not
const isItemSelected = selectable
? selectedItems.some((item) => item[idField] === rowId)
: false;
return ((0, jsx_runtime_1.jsxs)(TableRow_1.default, { selected: isItemSelected, children: [selectable && ((0, jsx_runtime_1.jsx)(TableCell_1.default, { padding: "checkbox", children: row && ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { color: "primary", checked: isItemSelected, onChange: (_event, checked) => handleSelect(row, checked) })) })), columns.map(({ align, cellRenderer = DataGridRenderers_1.DataGridRenderers.defaultCellRenderer, field, type, valueFormatter }, columnIndex) => {
const formatProps = {
data: row,
field,
rowIndex,
columnIndex
};
const cellProps = {
align: (0, react_1.GridAlignGet)(align, type),
valign: "middle"
};
const child = row ? (cellRenderer({
data: row,
field,
formattedValue: valueFormatter
? valueFormatter(formatProps)
: undefined,
selected: isItemSelected,
type,
rowIndex,
columnIndex,
cellProps,
setItems
})) : ((0, jsx_runtime_1.jsx)(react_2.default.Fragment, { children: "\u00A0" }));
return ((0, jsx_runtime_1.jsx)(TableCell_1.default, { ...cellProps, children: child }, `${rowId}${columnIndex}`));
})] }, rowId));
}) })] }) }), (0, jsx_runtime_1.jsx)(TablePagination_1.default, { component: "div", showFirstButton: true, count: totalRows, rowsPerPage: batchSize, page: currentPage, onPageChange: handleChangePage, onRowsPerPageChange: handleChangeRowsPerPage, rowsPerPageOptions: [
batchSize,
2 * batchSize,
5 * batchSize,
10 * batchSize
] })] }));
}