UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

305 lines (304 loc) 14.5 kB
"use strict"; 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 ] })] })); }