File size: 3,862 Bytes
867b17d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
'use client';
import React, { memo, useEffect, useMemo, useState } from 'react';
import DataGrid, { textEditor } from 'react-data-grid';
import { parse, unparse } from 'papaparse';
import { useTheme } from 'next-themes';
import { cn } from '@/lib/utils';
import 'react-data-grid/lib/styles.css';
type SheetEditorProps = {
content: string;
saveContent: (content: string, isCurrentVersion: boolean) => void;
status: string;
isCurrentVersion: boolean;
currentVersionIndex: number;
};
const MIN_ROWS = 50;
const MIN_COLS = 26;
const PureSpreadsheetEditor = ({
content,
saveContent,
status,
isCurrentVersion,
}: SheetEditorProps) => {
const { resolvedTheme } = useTheme();
const parseData = useMemo(() => {
if (!content) return Array(MIN_ROWS).fill(Array(MIN_COLS).fill(''));
const result = parse<string[]>(content, { skipEmptyLines: true });
const paddedData = result.data.map((row) => {
const paddedRow = [...row];
while (paddedRow.length < MIN_COLS) {
paddedRow.push('');
}
return paddedRow;
});
while (paddedData.length < MIN_ROWS) {
paddedData.push(Array(MIN_COLS).fill(''));
}
return paddedData;
}, [content]);
const columns = useMemo(() => {
const rowNumberColumn = {
key: 'rowNumber',
name: '',
frozen: true,
width: 50,
renderCell: ({ rowIdx }: { rowIdx: number }) => rowIdx + 1,
cellClass: 'border-t border-r dark:bg-zinc-950 dark:text-zinc-50',
headerCellClass: 'border-t border-r dark:bg-zinc-900 dark:text-zinc-50',
};
const dataColumns = Array.from({ length: MIN_COLS }, (_, i) => ({
key: i.toString(),
name: String.fromCharCode(65 + i),
renderEditCell: textEditor,
width: 120,
cellClass: cn(`border-t dark:bg-zinc-950 dark:text-zinc-50`, {
'border-l': i !== 0,
}),
headerCellClass: cn(`border-t dark:bg-zinc-900 dark:text-zinc-50`, {
'border-l': i !== 0,
}),
}));
return [rowNumberColumn, ...dataColumns];
}, []);
const initialRows = useMemo(() => {
return parseData.map((row, rowIndex) => {
const rowData: any = {
id: rowIndex,
rowNumber: rowIndex + 1,
};
columns.slice(1).forEach((col, colIndex) => {
rowData[col.key] = row[colIndex] || '';
});
return rowData;
});
}, [parseData, columns]);
const [localRows, setLocalRows] = useState(initialRows);
useEffect(() => {
setLocalRows(initialRows);
}, [initialRows]);
const generateCsv = (data: any[][]) => {
return unparse(data);
};
const handleRowsChange = (newRows: any[]) => {
setLocalRows(newRows);
const updatedData = newRows.map((row) => {
return columns.slice(1).map((col) => row[col.key] || '');
});
const newCsvContent = generateCsv(updatedData);
saveContent(newCsvContent, true);
};
return (
<DataGrid
className={resolvedTheme === 'dark' ? 'rdg-dark' : 'rdg-light'}
columns={columns}
rows={localRows}
enableVirtualization
onRowsChange={handleRowsChange}
onCellClick={(args) => {
if (args.column.key !== 'rowNumber') {
args.selectCell(true);
}
}}
style={{ height: '100%' }}
defaultColumnOptions={{
resizable: true,
sortable: true,
}}
/>
);
};
function areEqual(prevProps: SheetEditorProps, nextProps: SheetEditorProps) {
return (
prevProps.currentVersionIndex === nextProps.currentVersionIndex &&
prevProps.isCurrentVersion === nextProps.isCurrentVersion &&
!(prevProps.status === 'streaming' && nextProps.status === 'streaming') &&
prevProps.content === nextProps.content &&
prevProps.saveContent === nextProps.saveContent
);
}
export const SpreadsheetEditor = memo(PureSpreadsheetEditor, areEqual);
|