@etsoo/react
Version:
TypeScript ReactJs UI Independent Framework
229 lines (228 loc) • 8.74 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScrollerGrid = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importDefault(require("react"));
const react_window_1 = require("react-window");
const useCombinedRefs_1 = require("../uses/useCombinedRefs");
/**
* Scroller vertical grid
* @param props Props
* @returns Component
*/
const ScrollerGrid = (props) => {
// Destruct
const { autoLoad = true, defaultOrderBy, footerRenderer, headerRenderer, height = "100%", gridRef, width = "100%", style = {}, idField = "id", loadBatchSize, loadData, mRef, onCellsRendered, onSelectChange, rowHeight, threshold = 3, onInitLoad, onUpdateRows, ...rest } = props;
// Style
Object.assign(style, {
width,
height,
overflowX: "hidden"
});
// Refs
const localRef = (0, react_window_1.useGridRef)(null);
const refs = (0, useCombinedRefs_1.useCombinedRefs)(gridRef, localRef);
// Rows
const [rows, updateRows] = react_1.default.useState([]);
const setRows = (rows, reset = false) => {
stateRefs.current.loadedItems = rows.length;
updateRows(rows);
onUpdateRows?.(rows, stateRefs.current, reset);
};
// State Refs
const stateRefs = react_1.default.useRef({
queryPaging: {
currentPage: 0,
orderBy: defaultOrderBy,
batchSize: 10
},
autoLoad,
hasNextPage: true,
isNextPageLoading: false,
loadedItems: 0,
selectedItems: [],
idCache: {}
});
// Load data
const loadDataLocal = (pageAdd = 1) => {
// Prevent multiple loadings
if (!stateRefs.current.hasNextPage ||
stateRefs.current.isNextPageLoading ||
stateRefs.current.isMounted === false)
return;
// Update state
stateRefs.current.isNextPageLoading = true;
// Parameters
const { queryPaging, data } = stateRefs.current;
const loadProps = {
queryPaging,
data
};
loadData(loadProps, stateRefs.current.lastItem).then((result) => {
if (result == null || stateRefs.current.isMounted === false) {
return;
}
stateRefs.current.isMounted = true;
const newItems = result.length;
stateRefs.current.lastLoadedItems = newItems;
stateRefs.current.lastItem = result.at(-1);
stateRefs.current.isNextPageLoading = false;
stateRefs.current.hasNextPage =
newItems >= stateRefs.current.queryPaging.batchSize;
if (pageAdd === 0) {
// New items
const newRows = stateRefs.current.lastLoadedItems
? [...rows]
.splice(rows.length - stateRefs.current.lastLoadedItems, stateRefs.current.lastLoadedItems)
.concat(result)
: result;
stateRefs.current.idCache = {};
for (const row of newRows) {
const id = row[idField];
stateRefs.current.idCache[id] = null;
}
// Update rows
setRows(newRows);
}
else {
// Set current page
if (stateRefs.current.queryPaging.currentPage == null)
stateRefs.current.queryPaging.currentPage = pageAdd;
else
stateRefs.current.queryPaging.currentPage += pageAdd;
// Update rows, avoid duplicate items
const newRows = [...rows];
for (const item of result) {
const id = item[idField];
if (stateRefs.current.idCache[id] === undefined) {
newRows.push(item);
}
}
setRows(newRows);
}
});
};
// Reset the state and load again
const reset = (add, items = []) => {
const { queryPaging, ...rest } = add ?? {};
const resetState = {
autoLoad: true,
loadedItems: 0,
hasNextPage: true,
isNextPageLoading: false,
lastLoadedItems: undefined,
lastItem: undefined,
...rest
};
Object.assign(stateRefs.current, resetState);
Object.assign(stateRefs.current.queryPaging, {
currentPage: 0,
...queryPaging
});
// Reset items
if (stateRefs.current.isMounted !== false)
setRows(items, true);
};
react_1.default.useImperativeHandle(mRef, () => ({
get element() {
return localRef.current?.element;
},
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() {
loadDataLocal(0);
},
reset,
scrollToCell(param) {
localRef.current?.scrollToCell(param);
},
scrollToColumn(param) {
localRef.current?.scrollToColumn(param);
},
scrollToRow(param) {
localRef.current?.scrollToRow(param);
},
select(rowIndex) {
// Select only one item
const selectedItems = stateRefs.current.selectedItems;
selectedItems[0] = rows[rowIndex];
if (onSelectChange)
onSelectChange(selectedItems);
},
selectAll(checked) {
const selectedItems = stateRefs.current.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)
onSelectChange(selectedItems);
},
selectItem(item, checked) {
const selectedItems = stateRefs.current.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)
onSelectChange(selectedItems);
}
}), [rows]);
// Rows
const rowCount = rows.length;
react_1.default.useEffect(() => {
// Auto load data when current page is 0
if (stateRefs.current.queryPaging.currentPage === 0 &&
stateRefs.current.autoLoad) {
const initItems = onInitLoad == null || localRef.current == null
? undefined
: onInitLoad(localRef.current);
if (initItems)
reset(initItems[1], initItems[0]);
else
loadDataLocal();
}
}, [onInitLoad, loadDataLocal]);
react_1.default.useEffect(() => {
return () => {
stateRefs.current.isMounted = false;
};
}, []);
// Layout
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [headerRenderer && headerRenderer(stateRefs.current), (0, jsx_runtime_1.jsx)(react_window_1.Grid, { cellProps: { rows, states: stateRefs.current }, gridRef: refs, onCellsRendered: (visibleCells, allCells) => {
// No items, means no necessary to load more data during reset
if (rowCount > 0 &&
visibleCells.rowStopIndex + threshold > rowCount) {
// Auto load next page
loadDataLocal();
}
onCellsRendered?.(visibleCells, allCells);
}, overscanCount: threshold, rowHeight: rowHeight, rowCount: rowCount, style: style, ...rest }), footerRenderer && footerRenderer(rows, stateRefs.current)] }));
};
exports.ScrollerGrid = ScrollerGrid;