import React, { useState, useMemo } from "react";
import { columns } from "./utils/columns.js";
import HeaderTable from "./components/HeaderTable.jsx";
import LinesTable from "./components/LinesTable.jsx";
import "./style/coolTable.css";
import { columnSelected } from "./utils/columnSelected.js";
import NumberRowSelector from "./components/NumberRowSelector.jsx";
import { flattenObject } from "./utils/flattenObject.js";
/**
* @fileoverview This module need Node.js v18
*/
/**
* @namespace Table
*
**/
/**
* @name CoolTable
* @function
* @description
* A customizable and sortable table component. It allows excluding certain columns,
* provides search functionality, and supports sorting. The table renders data passed to it,
* applying column formatting and sorting based on user interactions.
*
* @param {Array<Object>} data - The data to be displayed in the table.
* @param {Array<string>} excludedColumns - The columns to be excluded from the table.
* @returns {React.Component} A React component representing a dynamically generated table.
*/
function CoolTable({ data, excludedColumns }) {
const [searchQuery, setSearchQuery] = useState("");
const [rowsPerPage, setRowsPerPage] = useState(10);
const [sortConfig, setSortConfig] = useState({ key: null, direction: null });
const formatData = columnSelected(data, excludedColumns);
const columnsName = columns(formatData, true);
const columnsRef = columns(formatData, false);
const [resetPaginationKey, setResetPaginationKey] = useState(0);
const getDataType = (value) => {
if (!isNaN(Date.parse(value)) && !isNaN(new Date(value).getDate())) {
return "date";
} else if (!isNaN(value) && typeof value !== "string") {
return "number";
}
return "string";
};
const filteredLines = useMemo(() => {
return searchQuery
? formatData.filter(line => {
const flatLine = flattenObject(line);
return Object.values(flatLine).some(value =>
String(value).toLowerCase().includes(searchQuery.toLowerCase())
);
})
: formatData;
}, [formatData, searchQuery]);
const sortedLines = useMemo(() => {
let sortableItems = [...filteredLines];
if (sortConfig.key && sortConfig.direction !== null) {
sortableItems.sort((a, b) => {
const keyParts = sortConfig.key.split(".");
const aValue = keyParts.length > 1 ? a[keyParts[0]][keyParts[1]] : a[sortConfig.key];
const bValue = keyParts.length > 1 ? b[keyParts[0]][keyParts[1]] : b[sortConfig.key];
const type = getDataType(aValue);
if (aValue == null && bValue == null) return 0;
if (aValue == null) return -1;
if (bValue == null) return 1;
switch (type) {
case "date":
return (new Date(aValue) - new Date(bValue)) * (sortConfig.direction === "ascending" ? 1 : -1);
case "number":
return (aValue - bValue) * (sortConfig.direction === "ascending" ? 1 : -1);
default:
return aValue.localeCompare(bValue) * (sortConfig.direction === "ascending" ? 1 : -1);
}
});
}
return sortableItems;
}, [filteredLines, sortConfig]);
const handleSortChange = (key) => {
let direction = "ascending";
if (sortConfig.key === key && sortConfig.direction !== null) {
direction = sortConfig.direction === "ascending" ? "descending" : "ascending";
}
setSortConfig({ key, direction });
};
const handleSearchChange = (e) => {
const value = e.target.value;
setSearchQuery(value);
setResetPaginationKey(prevKey => prevKey + 1);
};
if (!data) return null;
return (
<div className="cool-table">
<div className="cool-head-table">
<NumberRowSelector rowsPerPage={rowsPerPage} setRowsPerPage={setRowsPerPage} />
<div className="cool-search-input-container">
<label htmlFor="search">Search:</label>
<input
name="search"
id="search"
type="text"
value={searchQuery}
onChange={(e) => handleSearchChange(e)}
/>
</div>
</div>
<HeaderTable columnsName={columnsName} columnsRef={columnsRef} onSortChange={handleSortChange} sortConfig={sortConfig} />
<LinesTable linesValues={sortedLines} columnsName={columnsRef} rowsPerPage={rowsPerPage} resetPaginationKey={resetPaginationKey} />
</div>
);
}
export default React.memo(CoolTable);