import { useState, useRef, useEffect } from '@wordpress/element'; import { AgGridReact } from 'ag-grid-react'; import 'ag-grid-community/styles/ag-grid.css'; import 'ag-grid-community/styles/ag-theme-alpine.css'; const LCPDataGrid = ({dataset}) => { // let gridData = [ // { Department2: 'Sheriffs Office 2', Budget: '150000', MeetAt: '12/12/2025', PreferredColor: '#e0e0e0', PostContent: '
' }, // { Department2: 'Treasurer2', Budget: '10000', MeetAt: '12-05', PreferredColor: '#232323', PostContent: '

' }, // { Department2: 'Assessor2', Budget: '40000', MeetAt: 1737718512, PreferredColor: 'red', PostContent: '

' }, // ]; const gridData = dataset; // Helper function to detect the data type of a value const getDataType = (value) => { if (typeof value === 'number' && !isNaN(value)) { return 'number'; // Identifies numerical values } return 'text'; // Defaults to 'text' for strings and others }; // Helper function to detect if a value is a valid CSS color const isValidCSSColor = (value) => { const cssColorRegex = /^(#([0-9a-fA-F]{3}){1,2}|[a-zA-Z]+|rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)|rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(0(\.\d+)?|1(\.0+)?)\)|hsl\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%\)|hsla\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%,\s*(0(\.\d+)?|1(\.0+)?)\))$/i; return cssColorRegex.test(value); }; // Helper function to detect if a value contains HTML tags const isHTML = (value) => { const htmlRegex = /<([a-z][\s\S]*)>/i; // A simple check for any HTML tag return htmlRegex.test(value); }; // Helper function to detect if a value is a valid date const isDate = (value) => { const date = new Date(value); return !isNaN(date.getTime()); // Return true if it's a valid date }; // Detect the data type of each key by checking all rows const detectDataTypes = (data) => { const keys = Object.keys(data[0]); let dataTypes = {}; keys.forEach((key) => { // Check all values for the key across all rows const allValuesAreNumbers = data.every(row => !isNaN(Number(row[key]))); const allValuesAreColors = data.every(row => isValidCSSColor(row[key])); const allValuesAreHTML = data.every(row => isHTML(row[key])); const allValuesAreDates = data.every(row => isDate(row[key])); // If all values for that key are valid numbers, mark it as 'number' if (allValuesAreNumbers) { dataTypes[key] = 'number'; } // If all values for that key are valid CSS colors, mark it as 'color' else if (allValuesAreColors) { dataTypes[key] = 'color'; } // If all values for that key contain HTML, mark it as 'html' else if (allValuesAreHTML) { dataTypes[key] = 'html'; } // If all values for that key are valid dates, mark it as 'date' else if (allValuesAreDates) { dataTypes[key] = 'date'; } // If neither, mark it as 'text' else { dataTypes[key] = 'text'; } }); return dataTypes; }; // Assuming gridData is already populated const gridDataTypes = detectDataTypes(gridData); // This will detect the types of each field console.log(gridDataTypes); // For debugging, will output the detected types for each column // Helper function to convert index to letters, accounting for 'A1', 'B1', 'C1', etc. const getColumnLabel = (index) => { const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; let label = ''; while (index >= 0) { label = alphabet[index % 26] + label; index = Math.floor(index / 26) - 1; } return label; }; // Data parser for Ag Grid based on detected data types function lcpDataTypeParser(params) { const columnName = params.colDef.field; // Get the column field name (e.g., 'Budget', 'MeetAt') const dataType = gridDataTypes[columnName]; // Get the data type for this column from gridDataTypes if (params.node.rowPinned) { // If it's the pinned top row, return the value as-is return params.newValue; } else { // Process based on the detected data type switch (dataType) { case 'number': // If it's a number, return the number or NaN return Number(params.newValue); case 'date': // If it's a date, try to parse the value as a Date const parsedDate = new Date(params.newValue); return !isNaN(parsedDate.getTime()) ? parsedDate : params.newValue; // Return parsed date or original value if invalid case 'color': // If it's a color, we don't need to change the value (it's already a valid color) return params.newValue; case 'html': // If it's HTML, we can either sanitize or leave the value as is (for now, leaving it as is) return params.newValue; default: // If it's text (or any other type), return the value as-is return params.newValue; } } } // Create columnDefs dynamically const [columnDefs, setColumnDefs] = useState([]); const [pinnedTopRowData, setPinnedTopRowData] = useState([]); useEffect(() => { if (columnDefs.length > 0) return; // Prevent rerun if columnDefs already set if (gridData.length > 0) { const keys = Object.keys(gridData[0]); // Get the keys from the first row (Department2, Budget, etc.) const columns = keys.map((key, index) => { return { headerName: getColumnLabel(index), // 'A', 'B', 'C', ... field: key, // Field will match the key (Department2, Budget, etc.) valueParser: lcpDataTypeParser, }; }); // Set the pinned top row data with the actual field names (like 'Department2', 'Budget') const topRowData = keys.reduce((acc, key) => { acc[key] = key; // Set key as the value in top row (use field names) return acc; }, {}); // Ensure pinnedTopRowData is set with correct data setPinnedTopRowData([topRowData]); // Add the row number column as the first column const rowNumberColumn = { headerName: '', valueGetter: (params) => { const rowIndex = params.node.rowPinned ? params.node.rowIndex + 1 : params.node.rowIndex + 2; return rowIndex; }, suppressMovable: true, editable: false, filter: false, sortable: false, width: 45, resizable: false, flex: 0, cellStyle: { backgroundColor: '#e0e0e0' }, }; setColumnDefs([rowNumberColumn, ...columns]); // Set columns along with row number column } }, [gridData, columnDefs]); // R const [rowData] = useState(gridData); // AG Grid instance reference const gridRef = useRef(null); const [gridApi, setGridApi] = useState(null); // Store grid API in state // Button click handler to add a new row const addRow = () => { if (gridApi) { const newRow = {}; // Empty Row // Use the grid API to add the new row gridApi.applyTransaction({ add: [newRow] }); } else { console.log('Grid is not ready yet'); } }; // Default Column Properties const defaultColDef = { flex: 1, minWidth: 45, editable: true, sortable: true, filter: true, suppressMovable: true, cellDataType: false, //By default column can be any data type cellStyle: (params) => { if (params.node.rowPinned) { return { backgroundColor: 'rgb(197, 219, 229)' }; } } }; const gridOptions = { rowDragManaged: true, animateRows: true, rowHeight: 35, getRowStyle: (params) => { if (params.node.rowPinned) { return { backgroundColor: 'rgb(197, 219, 229)' }; // Light gray for pinned rows } return null; // No special style for non-pinned rows } }; const onGridReady = (params) => { // Store the grid API when the grid is ready setGridApi(params.api); }; // Reset row number on sorted const onSortChanged = (e) => { e.api.refreshCells(); }; // Reset row number on filtered const onFilterChanged = (e) => { e.api.refreshCells(); }; return (
{/* Button outside the grid */}
); }; export default LCPDataGrid;