This commit is contained in:
@@ -43,6 +43,17 @@ const Export = ({ columns, data, filename = "export", customActions = [], show,
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSelectAll = () => {
|
||||||
|
scrollTopRef.current = scrollContainerRef.current?.scrollTop || 0;
|
||||||
|
const updatedColumns = exportColumns.map((col) => ({ ...col, show: true }));
|
||||||
|
setExportColumns(updatedColumns);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (scrollContainerRef.current) {
|
||||||
|
scrollContainerRef.current.scrollTop = scrollTopRef.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const generateCSV = () => {
|
const generateCSV = () => {
|
||||||
const visibleColumns = exportColumns.filter(col => col.show);
|
const visibleColumns = exportColumns.filter(col => col.show);
|
||||||
const headers = visibleColumns.map(col => col.label).join(',');
|
const headers = visibleColumns.map(col => col.label).join(',');
|
||||||
@@ -158,13 +169,22 @@ const Export = ({ columns, data, filename = "export", customActions = [], show,
|
|||||||
<div style={{ maxHeight: '200px', overflowY: 'auto', marginBottom: '15px' }} ref={scrollContainerRef}>
|
<div style={{ maxHeight: '200px', overflowY: 'auto', marginBottom: '15px' }} ref={scrollContainerRef}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
|
||||||
<h6 style={{ fontSize: '14px', marginBottom: 0 }}>Select Columns:</h6>
|
<h6 style={{ fontSize: '14px', marginBottom: 0 }}>Select Columns:</h6>
|
||||||
<button
|
<div style={{ display: 'flex', gap: '10px' }}>
|
||||||
type="button"
|
<button
|
||||||
className="btn btn-link btn-sm p-0"
|
type="button"
|
||||||
onClick={handleDeselectAll}
|
className="btn btn-link btn-sm p-0"
|
||||||
>
|
onClick={handleSelectAll}
|
||||||
Deselect All
|
>
|
||||||
</button>
|
Select All
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-link btn-sm p-0"
|
||||||
|
onClick={handleDeselectAll}
|
||||||
|
>
|
||||||
|
Deselect All
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{exportColumns.map((column) => (
|
{exportColumns.map((column) => (
|
||||||
<div key={column.key} style={{ marginBottom: '8px' }}>
|
<div key={column.key} style={{ marginBottom: '8px' }}>
|
||||||
|
|||||||
@@ -2,7 +2,59 @@ import React, { useRef, useState } from "react";
|
|||||||
import { Dropdown } from "react-bootstrap";
|
import { Dropdown } from "react-bootstrap";
|
||||||
import { Columns } from "react-bootstrap-icons";
|
import { Columns } from "react-bootstrap-icons";
|
||||||
|
|
||||||
const ManageTable = ({ columns, onColumnsChange, show, onToggle }) => {
|
const getSessionStorageValue = (key) => {
|
||||||
|
try {
|
||||||
|
return window.sessionStorage.getItem(key);
|
||||||
|
} catch (_e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setSessionStorageValue = (key, value) => {
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem(key, value);
|
||||||
|
return true;
|
||||||
|
} catch (_e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCookieNameFromKey = (key) => `manage_table_${`${key}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
||||||
|
|
||||||
|
const getCookieValue = (name) => {
|
||||||
|
if (typeof document === "undefined") return null;
|
||||||
|
const cookie = document.cookie
|
||||||
|
.split("; ")
|
||||||
|
.find((row) => row.startsWith(`${name}=`));
|
||||||
|
if (!cookie) return null;
|
||||||
|
return decodeURIComponent(cookie.split("=")[1] || "");
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCookieValue = (name, value) => {
|
||||||
|
if (typeof document === "undefined") return;
|
||||||
|
document.cookie = `${name}=${encodeURIComponent(value)}; path=/; SameSite=Lax`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toVisibilityMap = (columnList = []) => (
|
||||||
|
columnList.reduce((acc, col) => {
|
||||||
|
acc[col.key] = !!col.show;
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
|
|
||||||
|
const applyVisibilityMap = (columnList = [], visibilityMap = {}) => (
|
||||||
|
columnList.map((col) => (
|
||||||
|
Object.prototype.hasOwnProperty.call(visibilityMap, col.key)
|
||||||
|
? { ...col, show: !!visibilityMap[col.key] }
|
||||||
|
: col
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasVisibilityDiff = (left = [], right = []) => (
|
||||||
|
left.length !== right.length || left.some((col, idx) => col.key !== right[idx]?.key || !!col.show !== !!right[idx]?.show)
|
||||||
|
);
|
||||||
|
|
||||||
|
const ManageTable = ({ columns, onColumnsChange, show, onToggle, storageKey }) => {
|
||||||
const [internalShow, setInternalShow] = useState(false);
|
const [internalShow, setInternalShow] = useState(false);
|
||||||
const [tempColumns, setTempColumns] = useState(columns);
|
const [tempColumns, setTempColumns] = useState(columns);
|
||||||
const scrollContainerRef = useRef(null);
|
const scrollContainerRef = useRef(null);
|
||||||
@@ -11,10 +63,41 @@ const ManageTable = ({ columns, onColumnsChange, show, onToggle }) => {
|
|||||||
// Use external control if provided, otherwise use internal state
|
// Use external control if provided, otherwise use internal state
|
||||||
const showManageTableDropdown = show !== undefined ? show : internalShow;
|
const showManageTableDropdown = show !== undefined ? show : internalShow;
|
||||||
const handleToggle = onToggle || (() => setInternalShow(!internalShow));
|
const handleToggle = onToggle || (() => setInternalShow(!internalShow));
|
||||||
|
const resolvedStorageKey = React.useMemo(() => {
|
||||||
|
if (storageKey) return storageKey;
|
||||||
|
const path = window?.location?.pathname || "default";
|
||||||
|
let userKey = "guest";
|
||||||
|
try {
|
||||||
|
const user = JSON.parse(localStorage.getItem("user") || "{}");
|
||||||
|
userKey = user?.id || user?._id || user?.name || "guest";
|
||||||
|
} catch (_e) {
|
||||||
|
userKey = "guest";
|
||||||
|
}
|
||||||
|
return `manage-table:${path}:${userKey}`;
|
||||||
|
}, [storageKey]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setTempColumns(columns);
|
const rawSessionValue = getSessionStorageValue(resolvedStorageKey);
|
||||||
}, [columns]);
|
const cookieName = getCookieNameFromKey(resolvedStorageKey);
|
||||||
|
const rawCookieValue = getCookieValue(cookieName);
|
||||||
|
const rawValue = rawSessionValue || rawCookieValue;
|
||||||
|
|
||||||
|
if (!rawValue) {
|
||||||
|
setTempColumns(columns);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsedVisibilityMap = JSON.parse(rawValue);
|
||||||
|
const mergedColumns = applyVisibilityMap(columns, parsedVisibilityMap);
|
||||||
|
setTempColumns(mergedColumns);
|
||||||
|
if (hasVisibilityDiff(columns, mergedColumns)) {
|
||||||
|
onColumnsChange(mergedColumns);
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
setTempColumns(columns);
|
||||||
|
}
|
||||||
|
}, [columns, onColumnsChange, resolvedStorageKey]);
|
||||||
|
|
||||||
const handleColumnToggle = (columnKey) => {
|
const handleColumnToggle = (columnKey) => {
|
||||||
scrollTopRef.current = scrollContainerRef.current?.scrollTop || 0;
|
scrollTopRef.current = scrollContainerRef.current?.scrollTop || 0;
|
||||||
@@ -30,6 +113,13 @@ const ManageTable = ({ columns, onColumnsChange, show, onToggle }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDone = () => {
|
const handleDone = () => {
|
||||||
|
const visibilityMapString = JSON.stringify(toVisibilityMap(tempColumns));
|
||||||
|
const wroteToSession = setSessionStorageValue(resolvedStorageKey, visibilityMapString);
|
||||||
|
if (!wroteToSession) {
|
||||||
|
setCookieValue(getCookieNameFromKey(resolvedStorageKey), visibilityMapString);
|
||||||
|
} else {
|
||||||
|
setCookieValue(getCookieNameFromKey(resolvedStorageKey), visibilityMapString);
|
||||||
|
}
|
||||||
onColumnsChange(tempColumns);
|
onColumnsChange(tempColumns);
|
||||||
if (onToggle) {
|
if (onToggle) {
|
||||||
onToggle(false);
|
onToggle(false);
|
||||||
@@ -58,6 +148,17 @@ const ManageTable = ({ columns, onColumnsChange, show, onToggle }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSelectAll = () => {
|
||||||
|
scrollTopRef.current = scrollContainerRef.current?.scrollTop || 0;
|
||||||
|
const updatedColumns = tempColumns.map((col) => ({ ...col, show: true }));
|
||||||
|
setTempColumns(updatedColumns);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (scrollContainerRef.current) {
|
||||||
|
scrollContainerRef.current.scrollTop = scrollTopRef.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const customManageTableMenu = React.forwardRef(
|
const customManageTableMenu = React.forwardRef(
|
||||||
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
|
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
|
||||||
return (
|
return (
|
||||||
@@ -68,7 +169,14 @@ const ManageTable = ({ columns, onColumnsChange, show, onToggle }) => {
|
|||||||
aria-labelledby={labeledBy}
|
aria-labelledby={labeledBy}
|
||||||
>
|
>
|
||||||
<h6>Manage Table Columns</h6>
|
<h6>Manage Table Columns</h6>
|
||||||
<div style={{ marginBottom: '8px' }}>
|
<div style={{ marginBottom: '8px', display: 'flex', gap: '10px' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-link btn-sm p-0"
|
||||||
|
onClick={handleSelectAll}
|
||||||
|
>
|
||||||
|
Select All
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-link btn-sm p-0"
|
className="btn btn-link btn-sm p-0"
|
||||||
|
|||||||
Reference in New Issue
Block a user