UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

338 lines (337 loc) 15.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataGridExCalColumns = DataGridExCalColumns; exports.DataGridEx = DataGridEx; const jsx_runtime_1 = require("react/jsx-runtime"); const css_1 = require("@emotion/css"); const react_1 = require("@etsoo/react"); const shared_1 = require("@etsoo/shared"); const react_2 = __importDefault(require("react")); const DataGridRenderers_1 = require("./DataGridRenderers"); const styles_1 = require("@mui/material/styles"); const Box_1 = __importDefault(require("@mui/material/Box")); const TableSortLabel_1 = __importDefault(require("@mui/material/TableSortLabel")); const Checkbox_1 = __importDefault(require("@mui/material/Checkbox")); const Paper_1 = __importDefault(require("@mui/material/Paper")); // Borders const boldBorder = "2px solid rgba(224, 224, 224, 1)"; const thinBorder = "1px solid rgba(224, 224, 224, 1)"; // Scroll bar size const scrollbarSize = 16; // Minimum width const minWidth = 120; const createGridStyle = (alternatingColors, selectedColor, hoverColor) => { return (0, css_1.css)({ ".DataGridEx-Selected": { backgroundColor: selectedColor }, ".DataGridEx-Hover:not(.DataGridEx-Selected)": { backgroundColor: hoverColor }, "& .DataGridEx-Cell0:not(.DataGridEx-Hover):not(.DataGridEx-Selected)": { backgroundColor: alternatingColors[0] }, "& .DataGridEx-Cell1:not(.DataGridEx-Hover):not(.DataGridEx-Selected)": { backgroundColor: alternatingColors[1] }, "& .DataGridEx-Cell-Border": { borderBottom: thinBorder } }); }; const rowItems = (div, callback) => { const row = div.dataset["row"]; if (div.parentElement == null || row == null) return; doRowItems(div.parentElement, parseFloat(row), callback); }; const doRowItems = (parent, rowIndex, callback) => { if (parent == null || rowIndex == null) return; parent ?.querySelectorAll(`div[data-row="${rowIndex}"]`) .forEach((rowItem) => { callback(rowItem); }); }; /** * Extended datagrid columns calculation * @param columns * @returns Total width and unset items */ function DataGridExCalColumns(columns) { return columns.reduce((previousValue, currentItem) => { previousValue.total += currentItem.width ?? currentItem.minWidth ?? minWidth; if (currentItem.width == null) previousValue.unset++; return previousValue; }, { total: 0, unset: 0 }); } /** * Extended DataGrid with VariableSizeGrid * @param props Props * @returns Component */ function DataGridEx(props) { // Theme const theme = (0, styles_1.useTheme)(); const defaultHeaderRenderer = (states) => { const { orderBy } = states.queryPaging; return ((0, jsx_runtime_1.jsx)(Box_1.default, { className: "DataGridEx-Header", display: "flex", alignItems: "center", borderBottom: boldBorder, fontWeight: 500, minWidth: widthCalculator.total, height: headerHeight, children: columns.map((column, index) => { // Destruct const { align, field, header, headerCellRenderer, sortable, sortAsc = true, type } = column; // Header text const headerText = header ?? field; // Cell props const cellProps = {}; // Sortable let sortLabel; if (headerCellRenderer) { sortLabel = headerCellRenderer({ cellProps, column, columnIndex: checkable ? index - 1 : index, // Ignore the checkbox case, states }); } else if (sortable && field != null) { const active = orderBy?.some((o) => o.field.toUpperCase() === field.toUpperCase()); 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)(Box_1.default, { textAlign: (0, react_1.GridAlignGet)(align, type), width: columnWidth(index), children: (0, jsx_runtime_1.jsx)(Box_1.default, { className: "DataGridEx-Cell", onMouseEnter: handleMouseEnter, ...cellProps, children: sortLabel }) }, field ?? index.toString())); }) })); }; function defaultFooterRenderer(rows, states) { return ((0, jsx_runtime_1.jsx)(Box_1.default, { className: "DataGridEx-Footer", display: "flex", alignItems: "center", borderTop: thinBorder, marginTop: "1px", minWidth: widthCalculator.total, height: bottomHeight - 1, children: columns.map((column, index) => { // Destruct const { align, field, type } = column; // Cell props const cellProps = {}; // Cell const cell = footerItemRenderer ? footerItemRenderer(rows, { column, index: checkable ? index - 1 : index, // Ignore the checkbox case states, cellProps, checkable }) : undefined; return ((0, jsx_runtime_1.jsx)(Box_1.default, { textAlign: (0, react_1.GridAlignGet)(align, type), width: columnWidth(index), children: (0, jsx_runtime_1.jsx)(Box_1.default, { className: "DataGridEx-Cell", onMouseEnter: handleMouseEnter, ...cellProps, children: cell }) }, "bottom-" + (field ?? index.toString()))); }) })); } // Destruct 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; if (checkable) { const cbColumn = { field: "selected", // Avoid validation from data model header: "", sortable: false, width: 50, cellRenderer: ({ cellProps, data, selected }) => { cellProps.sx = { padding: "4px!important" }; return ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { color: "primary", checked: selected, onChange: (_event, checked) => { refs.current.ref?.selectItem(data, checked); } })); }, headerCellRenderer: ({ cellProps, states }) => { // 2 = border height const hpad = (headerHeight - 42) / 2; cellProps.sx = { padding: `${hpad}px 4px ${hpad - 1}px 4px!important` }; return ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { color: "primary", indeterminate: states.selectedItems.length > 0 && states.selectedItems.length < states.loadedItems, checked: states.selectedItems.length > 0, onChange: (_event, checked) => refs.current.ref?.selectAll(checked) })); } }; // Update to the latest version if (columns[0].field === "selected") { columns[0] = cbColumn; } else { columns.unshift(cbColumn); } } const refs = react_2.default.useRef({}); const mRefLocal = (0, react_1.useCombinedRefs)(mRef, (ref) => { if (ref == null) return; refs.current.ref = ref; }); // New sort const handleSort = (field, asc) => { reset({ queryPaging: { orderBy: [{ field, desc: !(asc ?? true) }] } }); }; // Reset const reset = (add) => { refs.current.ref?.reset(add); }; // Show hover tooltip for trucated text const handleMouseEnter = (event) => { const div = event.currentTarget; const { innerText, offsetWidth, scrollWidth } = div; if (offsetWidth < scrollWidth) { div.title = innerText; } else { div.title = ""; } }; // selectedRowIndex state const selectedRowIndex = react_2.default.useRef(-1); const handleMouseDown = (event) => { const div = event.currentTarget; const row = div.dataset["row"]; if (div.parentElement == null || row == null) return; const rowIndex = parseFloat(row); // No change if (isNaN(rowIndex) || rowIndex === selectedRowIndex.current) return; if (selectedRowIndex.current != -1) { doRowItems(div.parentElement, selectedRowIndex.current, (preDiv) => { preDiv.classList.remove("DataGridEx-Selected"); }); } rowItems(div, (currentDiv) => { currentDiv.classList.add("DataGridEx-Selected"); }); selectedRowIndex.current = rowIndex; }; const handleMouseOver = (event) => { rowItems(event.currentTarget, (div) => { div.classList.add("DataGridEx-Hover"); }); }; const handleMouseOut = (event) => { rowItems(event.currentTarget, (div) => { div.classList.remove("DataGridEx-Hover"); }); }; /** * Item renderer */ const itemRenderer = ({ columnIndex, rowIndex, style, data, selectedItems, setItems }) => { // Column const { align, cellRenderer = DataGridRenderers_1.DataGridRenderers.defaultCellRenderer, cellBoxStyle, field, type, valueFormatter, renderProps } = columns[columnIndex]; // Props const formatProps = { data, field, rowIndex, columnIndex }; let rowClass = `DataGridEx-Cell${rowIndex % 2}`; if (borderRowsCount != null && borderRowsCount > 0 && (rowIndex + 1) % borderRowsCount === 0) { rowClass += ` DataGridEx-Cell-Border`; } // Selected const selected = data != null && (selectedRowIndex.current === rowIndex || selectedItems.some((selectedItem) => selectedItem != null && selectedItem[idField] === data[idField])); if (selected) { rowClass += ` DataGridEx-Selected`; } // Box style const boxStyle = data == null || cellBoxStyle == null ? undefined : typeof cellBoxStyle === "function" ? cellBoxStyle(data) : cellBoxStyle; const cellProps = { className: "DataGridEx-Cell", textAlign: (0, react_1.GridAlignGet)(align, type), sx: { ...boxStyle } }; const child = cellRenderer({ data, field, formattedValue: valueFormatter ? valueFormatter(formatProps) : undefined, selected, type, rowIndex, columnIndex, cellProps, renderProps: typeof renderProps === "function" ? renderProps(data) : renderProps, setItems }); 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 }) })); }; // Column width calculator const widthCalculator = react_2.default.useMemo(() => DataGridExCalColumns(columns), [columns]); // Column width const columnWidth = react_2.default.useCallback((index) => { // Ignore null case if (width == null) return 0; // Column const column = columns[index]; if (column.width != null) return column.width; // More space const leftWidth = width - widthCalculator.total - (width < 800 ? 0 : scrollbarSize); // Shared width const sharedWidth = leftWidth > 0 ? leftWidth / widthCalculator.unset : 0; return (column.minWidth || minWidth) + sharedWidth; }, [columns, width]); // Table const table = react_2.default.useMemo(() => { 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 - headerHeight - (hideFooter ? 0 : bottomHeight + 1) - scrollbarSize, headerRenderer: headerRenderer, idField: idField, itemRenderer: itemRenderer, footerRenderer: hideFooter ? undefined : footerRenderer, width: Math.max(width ?? 0, widthCalculator.total), mRef: mRefLocal, ...rest })); }, [width]); return ((0, jsx_runtime_1.jsx)(Paper_1.default, { sx: { fontSize: "0.875rem", height, "& .DataGridEx-Cell": { padding: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, "& .DataGridEx-CustomBar": { "@media (min-width: 800px)": { "::-webkit-scrollbar": { width: scrollbarSize, height: scrollbarSize, backgroundColor: "#f6f6f6" }, "::-webkit-scrollbar-thumb": { backgroundColor: "rgba(0,0,0,0.4)", borderRadius: "2px" }, "::-webkit-scrollbar-track-piece:start": { background: "transparent" }, "::-webkit-scrollbar-track-piece:end": { background: "transparent" } } } }, children: (0, jsx_runtime_1.jsx)("div", { className: "DataGridEx-CustomBar", style: { width, overflowX: "auto", overflowY: "hidden" }, children: table }) })); }