fixed static data sorting (client side)

This commit is contained in:
pramod.mahajan 2025-12-28 12:53:33 +05:30
parent 2ceb38b496
commit 5a182c5027
5 changed files with 128 additions and 122 deletions

View File

@ -12,6 +12,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
title: "Invoice Number",
className: "text-start",
groupable: true,
sortable: true,
enableAdvancedFilter: false,
onCellClick: (row, column) => {
console.log("Clicked cell:", row, column);
@ -35,6 +36,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
key: "exceptedPaymentDate",
title: "Expected Payment Date",
className: "text-start",
sortable: true,
enableAdvancedFilter: { type: "date" },
},
{
@ -148,6 +150,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
});
}, [grid.sortBy, grid.advanceFilters, grid.groupBy]);
const { data, isLoading, error } = useGridCollectionQuery({
projectId: selectedProject,
page: grid.page,
@ -161,9 +164,10 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
});
useEffect(() => {
if(!data) return
if (data) {
grid.setRows(data.rows);
grid.setTotalRows(data.total);
grid.setServerRows(data.rows);
grid.setServerTotal(data.total);
}
}, [data]);
@ -174,6 +178,7 @@ const PmGridCollection = ({ selectedProject, fromDate, toDate, isPending }) => {
loading={isLoading}
features={{
search: true,
selection:true,
pagination: true,
pinning: true,
resizing: true,

View File

@ -621,6 +621,9 @@ const boqColumns = [
sortable: true,
pinned: "left",
className: "text-start",
onCellClick: (row, column) => {
console.log("Clicked cell:", row, column);
},
},
{
key: "description",
@ -714,28 +717,15 @@ export default function DemoBOQGrid() {
amount: r.quantity * r.rate,
}))
});
useEffect(() => {
if (!boqData || boqData.length === 0) return;
grid.setRows(
boqData.map((r) => ({
...r,
amount: r.quantity * r.rate,
}))
);
grid.setTotalRows(boqData.length);
}, [boqData]);
return (
<div className="container-fluid py-3">
<div className="card p-3">
<div id="mainContainer" class="container-box">
<button id="dropdownBtn" class="btn">
<div id="mainContainer" className="container-box">
<button id="dropdownBtn" className="btn">
Open Dropdown
</button>
<div id="dropdownMenu" class="dropdown-menu">
<div id="dropdownMenu" className="dropdown-menu">
<div>Option 1</div>
<div>Option 2</div>
<div>Option 3</div>

View File

@ -357,7 +357,7 @@ export default function PmsGrid({
<tbody>
{loading || totalRows === 0
? Array.from({ length: 1 }).map((_, index) => (
<tr className="">
<tr className="" key={`row-${index}`}>
<td
key={index}
colSpan={
@ -390,7 +390,7 @@ export default function PmsGrid({
colSpan={
visibleColumns.length +
(features.selection ? 1 : 0) +
(features.actions ? 1 : 0)
(features.actions ? columns.length : 0)
}
className="text-start pinned pinned-left tr-group"
>
@ -464,7 +464,7 @@ export default function PmsGrid({
return (
<React.Fragment key={row[rowKey]}>
<tr>
<tr >
{features.IsNumbering && (
<td className="text-center align-middle p-2">
<small className="text-secondry">{ind + 1}</small>

View File

@ -64,7 +64,7 @@ const PmsHeaderOption = ({
<div ref={rootRef} style={{ position: "relative", zIndex: 9999 }}>
<button
ref={btnRef}
class="btn btn-icon btn-text-secondary rounded-pill d-inline-block"
className="btn btn-icon btn-text-secondary rounded-pill d-inline-block"
onClick={() => setOpen((p) => !p)}
>
<span className="icon-base bx bx-dots-vertical-rounded bx-sm"></span>

View File

@ -13,6 +13,7 @@ import {
- rowKey
- initialPageSize
*/
export function useGridCore({
data = [],
serverMode = false,
@ -20,6 +21,7 @@ export function useGridCore({
initialPageSize = 20,
columns = [],
}) {
/* ---------------- BASIC STATE ---------------- */
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(initialPageSize);
const [search, setSearch] = useState("");
@ -32,9 +34,11 @@ export function useGridCore({
const [selected, setSelected] = useState(new Set());
const [expanded, setExpanded] = useState(new Set());
const [rows, setRows] = useState([]);
const [totalRows, setTotalRows] = useState(0);
/* ---------------- SERVER MODE STATE ---------------- */
const [serverRows, setServerRows] = useState([]);
const [serverTotal, setServerTotal] = useState(0);
/* ---------------- COLUMN STATE ---------------- */
const [colState, setColState] = useState(() =>
columns.map((c, i) => ({
...c,
@ -43,35 +47,6 @@ export function useGridCore({
}))
);
/* ---------------- SELECTION ---------------- */
const toggleSelect = useCallback((id) => {
setSelected((prev) => {
const s = new Set(prev);
s.has(id) ? s.delete(id) : s.add(id);
return s;
});
}, []);
const selectAllOnPage = useCallback(
(rows) => {
setSelected((prev) => {
const s = new Set(prev);
rows.forEach((r) => s.add(r[rowKey]));
return s;
});
},
[rowKey]
);
const deselectAllOnPage = useCallback(
(rows) => {
setSelected((prev) => {
const s = new Set(prev);
rows.forEach((r) => s.delete(r[rowKey]));
return s;
});
},
[rowKey]
);
/* ---------------- SEARCH (DEBOUNCE) ---------------- */
useEffect(() => {
const t = setTimeout(() => {
@ -81,47 +56,93 @@ export function useGridCore({
return () => clearTimeout(t);
}, [search]);
/* ---------------- CLIENT MODE ---------------- */
const filteredData = useMemo(() => {
if (serverMode) return [];
let filtered = data;
/* ---------------- CLIENT MODE (PURE DERIVED DATA) ---------------- */
const filteredData = useMemo(() => {
if (serverMode) return [];
if (search) {
const q = search.toLowerCase();
filtered = filtered.filter((r) =>
Object.values(r).some((v) =>
String(v ?? "").toLowerCase().includes(q)
)
let filtered = [...data]; // IMPORTANT: clone
// SEARCH
if (debouncedSearch) {
const q = debouncedSearch.toLowerCase();
filtered = filtered.filter((r) =>
Object.values(r).some((v) =>
String(v ?? "")
.toLowerCase()
.includes(q)
)
);
}
// SORT
if (sortBy.key) {
const dir = sortBy.dir === "asc" ? 1 : -1;
filtered.sort(
(a, b) =>
String(a?.[sortBy.key] ?? "").localeCompare(
String(b?.[sortBy.key] ?? ""),
undefined,
{ numeric: true, sensitivity: "base" }
) * dir
);
}
return filtered;
}, [data, debouncedSearch, sortBy, serverMode]);
const pagedClientRows = useMemo(() => {
if (serverMode) return [];
const start = (page - 1) * pageSize;
return filteredData.slice(start, start + pageSize);
}, [filteredData, page, pageSize, serverMode]);
/* ---------------- FINAL ROW SOURCE ---------------- */
const rows = serverMode ? serverRows : pagedClientRows;
const totalRows = serverMode ? serverTotal : filteredData.length;
const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
/* ---------------- SELECTION ---------------- */
const toggleSelect = useCallback((id) => {
setSelected((prev) => {
const s = new Set(prev);
s.has(id) ? s.delete(id) : s.add(id);
return s;
});
}, []);
const selectAllOnPage = useCallback(
(rows) => {
setSelected((prev) => {
const s = new Set(prev);
rows.forEach((r) => s.add(r[rowKey]));
return s;
});
},
[rowKey]
);
const deselectAllOnPage = useCallback(
(rows) => {
setSelected((prev) => {
const s = new Set(prev);
rows.forEach((r) => s.delete(r[rowKey]));
return s;
});
},
[rowKey]
);
/* ---------------- SORT ---------------- */
const changeSort = useCallback((key) => {
setSortBy((p) =>
p.key !== key
? { key, dir: "asc" }
: p.dir === "asc"
? { key, dir: "desc" }
: { key: null, dir: "asc" }
);
}
if (sortBy.key) {
const dir = sortBy.dir === "asc" ? 1 : -1;
filtered = [...filtered].sort(
(a, b) =>
String(a[sortBy.key] ?? "").localeCompare(
String(b[sortBy.key] ?? "")
) * dir
);
}
return filtered;
}, [data, search, sortBy, serverMode]);
const pagedRows = useMemo(() => {
const start = (page - 1) * pageSize;
return filteredData.slice(start, start + pageSize);
}, [filteredData, page, pageSize]);
useEffect(() => {
if (!serverMode) {
setRows(pagedRows);
setTotalRows(filteredData.length);
}
}, [serverMode, page, pageSize, filteredData.length]);
setPage(1);
}, []);
/* ---------------- ADVANCED FILTER ---------------- */
const setColumnAdvanceFilter = useCallback((column, filter) => {
@ -136,18 +157,6 @@ useEffect(() => {
setPage(1);
}, []);
/* ---------------- SORT ---------------- */
const changeSort = useCallback((key) => {
setSortBy((p) =>
p.key !== key
? { key, dir: "asc" }
: p.dir === "asc"
? { key, dir: "desc" }
: { key: null, dir: "asc" }
);
setPage(1);
}, []);
/* ---------------- COLUMNS ---------------- */
const visibleColumns = useMemo(
() => colState.filter((c) => c.visible).sort((a, b) => a.order - b.order),
@ -160,8 +169,7 @@ useEffect(() => {
);
}, []);
// *---------------- ROW EXPAND -----------------------*/
/* ---------------- EXPAND ---------------- */
const toggleExpand = useCallback((id) => {
setExpanded((prev) => {
const s = new Set(prev);
@ -169,55 +177,58 @@ useEffect(() => {
return s;
});
}, []);
/* ---------------- RETURN API ---------------- */
return {
/* paging */
// paging
page,
setPage,
pageSize,
setPageSize,
totalRows,
totalPages: Math.max(1, Math.ceil(totalRows / pageSize)),
totalPages,
/* search */
// search
search,
setSearch,
debouncedSearch,
/* ----------sorting------ */
// sorting
sortBy,
changeSort,
/* --------------grouping---------------- */
// grouping
groupBy,
setGroupBy,
/* --------------advanced filter----------------- */
// advanced filters
advanceFilters,
setColumnAdvanceFilter,
/*--------- selection ---------------------- */
// selection
selected,
setSelected,
toggleSelect,
selectAllOnPage,
deselectAllOnPage,
// *------------- Expanding Row --------------*/
// expand
expanded,
setExpanded,
toggleExpand,
/* --------------columns--------------------- */
// columns
colState,
visibleColumns,
updateColumn,
setColState,
toggleExpand,
/* ---------------data---------------------- */
// data
rows,
setRows,
setTotalRows,
/*------------Key and grid-mode-------------*/
// server setters (ONLY used in serverMode)
setServerRows,
setServerTotal,
// flags
serverMode,
rowKey,
};