{
- if (changes && isCurrentVersion) {
- const newData: string[][] = data.map((row) => [...row]);
-
- changes.forEach(([row, prop, _, newValue]) => {
- if (typeof row === 'number' && typeof prop === 'number') {
- newData[row][prop] = String(newValue);
- }
- });
-
- const updatedCsv = generateCsv(newData);
- saveContent(updatedCsv, true);
- }
- }}
- />
+ const rawData = parseData(content);
+
+ const columns = useMemo(() => {
+ if (!rawData || rawData.length === 0) return [];
+
+ const columnCount = Math.max(...rawData.map((row) => row.length));
+ return Array.from({ length: columnCount }, (_, i) => ({
+ key: i.toString(),
+ name: String.fromCharCode(65 + i),
+ editor: 'textEditor',
+ editable: true,
+ }));
+ }, [rawData]);
+
+ const rows = useMemo(() => {
+ if (!rawData) return [];
+
+ return rawData.map((row, rowIndex) => {
+ const rowData: any = { id: rowIndex };
+
+ columns.forEach((col, colIndex) => {
+ rowData[col.key] = row[colIndex] || '';
+ });
+
+ return rowData;
+ });
+ }, [rawData, columns]);
+
+ function onCellEdit(rowIndex: number, columnKey: string, newValue: string) {
+ if (!isCurrentVersion) return;
+
+ const newRows = [...rows];
+ newRows[rowIndex] = { ...newRows[rowIndex], [columnKey]: newValue };
+
+ // Convert the rows back to 2D array format
+ const newData = newRows.map((row) =>
+ columns.map((col) => row[col.key] || ''),
+ );
+
+ const updatedCsv = generateCsv(newData);
+ saveContent(updatedCsv, true);
+ }
+
+ return rawData ? (
+
+ {
+ args.selectCell();
+ }}
+ onCellKeyDown={(args) => {
+ if (args.mode !== 'EDIT' && args.row.id !== undefined) {
+ args.enableEditMode();
+ }
+ }}
+ onRowsChange={(newRows, { indexes, column }) => {
+ if (indexes.length === 1) {
+ const rowIndex = indexes[0];
+ const newValue = newRows[rowIndex][column.key];
+ onCellEdit(rowIndex, column.key, newValue);
+ }
+ }}
+ style={{ height: '100%' }}
+ defaultColumnOptions={{
+ resizable: true,
+ sortable: true,
+ }}
+ />
+
) : null;
};
-function areEqual(
- prevProps: SpreadsheetEditorProps,
- nextProps: SpreadsheetEditorProps,
-) {
+function areEqual(prevProps: SheetEditorProps, nextProps: SheetEditorProps) {
return (
prevProps.currentVersionIndex === nextProps.currentVersionIndex &&
prevProps.isCurrentVersion === nextProps.isCurrentVersion &&
diff --git a/package.json b/package.json
index 967a5cc1c..dece591e9 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,6 @@
"@codemirror/state": "^6.5.0",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.35.3",
- "@handsontable/react-wrapper": "^15.0.0",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
@@ -51,7 +50,6 @@
"fast-deep-equal": "^3.1.3",
"framer-motion": "^11.3.19",
"geist": "^1.3.1",
- "handsontable": "^15.0.0",
"lucide-react": "^0.446.0",
"nanoid": "^5.0.8",
"next": "15.0.3-canary.2",
@@ -69,6 +67,7 @@
"prosemirror-state": "^1.4.3",
"prosemirror-view": "^1.34.3",
"react": "19.0.0-rc-45804af1-20241021",
+ "react-data-grid": "7.0.0-beta.47",
"react-dom": "19.0.0-rc-45804af1-20241021",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^2.1.7",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0932612fd..11d5d0482 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,9 +26,6 @@ importers:
'@codemirror/view':
specifier: ^6.35.3
version: 6.35.3
- '@handsontable/react-wrapper':
- specifier: ^15.0.0
- version: 15.0.0(handsontable@15.0.0)
'@radix-ui/react-alert-dialog':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
@@ -107,9 +104,6 @@ importers:
geist:
specifier: ^1.3.1
version: 1.3.1(next@15.0.3-canary.2(@opentelemetry/api@1.9.0)(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021))
- handsontable:
- specifier: ^15.0.0
- version: 15.0.0
lucide-react:
specifier: ^0.446.0
version: 0.446.0(react@19.0.0-rc-45804af1-20241021)
@@ -161,6 +155,9 @@ importers:
react:
specifier: 19.0.0-rc-45804af1-20241021
version: 19.0.0-rc-45804af1-20241021
+ react-data-grid:
+ specifier: 7.0.0-beta.47
+ version: 7.0.0-beta.47(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
react-dom:
specifier: 19.0.0-rc-45804af1-20241021
version: 19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021)
@@ -876,14 +873,6 @@ packages:
'@floating-ui/utils@0.2.8':
resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
- '@handsontable/pikaday@1.0.0':
- resolution: {integrity: sha512-1VN6N38t5/DcjJ7y7XUYrDx1LuzvvzlrFdBdMG90Qo1xc8+LXHqbWbsTEm5Ec5gXTEbDEO53vUT35R+2COmOyg==}
-
- '@handsontable/react-wrapper@15.0.0':
- resolution: {integrity: sha512-78uCgcVBM5JjIVYb9stShbKQ126RCEnpjojYMNVF+EqIhc0KzvlKoVKonDm6k6PoQz2b1aMkHL17BEBxumsU/w==}
- peerDependencies:
- handsontable: '>=15.0.0'
-
'@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=10.10.0'}
@@ -1580,9 +1569,6 @@ packages:
'@types/react@18.3.12':
resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
- '@types/trusted-types@2.0.7':
- resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
-
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
@@ -1768,9 +1754,6 @@ packages:
resolution: {integrity: sha512-gDwQ5784AkkfhHACh3jGcg1hUubyZyeq9AtVd5gXkcyHGVOC+mORjRIHSj+fHfqwY5vxwyBLXQpcfk8MpK0ROg==}
engines: {node: '>=18'}
- bignumber.js@9.1.2:
- resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==}
-
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
@@ -1838,9 +1821,6 @@ packages:
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
- chevrotain@6.5.0:
- resolution: {integrity: sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==}
-
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
@@ -1893,9 +1873,6 @@ packages:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
- core-js@3.40.0:
- resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==}
-
crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
@@ -1995,9 +1972,6 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
- dompurify@3.2.3:
- resolution: {integrity: sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==}
-
dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
@@ -2437,9 +2411,6 @@ packages:
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
- handsontable@15.0.0:
- resolution: {integrity: sha512-vv3d8m9tmXZDE/+zzyMyMmFu93LxcYCsm0DN+nPaEPzNMk9+5B29o6K6vc6nPJhzB1k/sPu3J393UMQYf3/Rgw==}
-
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -2475,9 +2446,6 @@ packages:
html-url-attributes@3.0.1:
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
- hyperformula@2.7.1:
- resolution: {integrity: sha512-mpVF5zOyNpksZzgTaCQyRAzdC/X43+taz5x1n7zNbs/PUUv0AuLmsy2yfihCr+vihUzN/pk+gXBbOfNpXKOpgA==}
-
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -2922,9 +2890,6 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
- moment@2.30.1:
- resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
-
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -3000,9 +2965,6 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
- numbro@2.5.0:
- resolution: {integrity: sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==}
-
oauth4webapi@3.1.2:
resolution: {integrity: sha512-KQZkNU+xn02lWrFu5Vjqg9E81yPtDSxUZorRHlLWVoojD+H/0GFbH59kcnz5Thdjj7c4/mYMBPj/mhvGe/kKXA==}
@@ -3282,6 +3244,12 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ react-data-grid@7.0.0-beta.47:
+ resolution: {integrity: sha512-28kjsmwQGD/9RXYC50zn5Zv/SQMhBBoSvG5seq0fM8XXi9TZ0zr9Z5T3YJqLwcEtoNzTOq3y0njkmdujGkIwQQ==}
+ peerDependencies:
+ react: ^18.0 || ^19.0
+ react-dom: ^18.0 || ^19.0
+
react-dom@19.0.0-rc-45804af1-20241021:
resolution: {integrity: sha512-8hOckEFO7Vxo+nH/EEddIGdencOFT0/3iJqF3mKrqv71n1xxhYcp0595JbT/DP31G8bHfDcBSMWVhIvyCGWy/A==}
peerDependencies:
@@ -3347,9 +3315,6 @@ packages:
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
engines: {node: '>= 0.4'}
- regexp-to-ast@0.4.0:
- resolution: {integrity: sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==}
-
regexp.prototype.flags@1.5.3:
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
engines: {node: '>= 0.4'}
@@ -3602,9 +3567,6 @@ packages:
resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
engines: {node: '>=18'}
- tiny-emitter@2.1.0:
- resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
-
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -4245,12 +4207,6 @@ snapshots:
'@floating-ui/utils@0.2.8': {}
- '@handsontable/pikaday@1.0.0': {}
-
- '@handsontable/react-wrapper@15.0.0(handsontable@15.0.0)':
- dependencies:
- handsontable: 15.0.0
-
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
@@ -4891,9 +4847,6 @@ snapshots:
'@types/prop-types': 15.7.13
csstype: 3.1.3
- '@types/trusted-types@2.0.7':
- optional: true
-
'@types/unist@2.0.11': {}
'@types/unist@3.0.3': {}
@@ -5101,8 +5054,6 @@ snapshots:
bcrypt-ts@5.0.2: {}
- bignumber.js@9.1.2: {}
-
binary-extensions@2.3.0: {}
brace-expansion@1.1.11:
@@ -5161,11 +5112,6 @@ snapshots:
character-reference-invalid@2.0.1: {}
- chevrotain@6.5.0:
- dependencies:
- regexp-to-ast: 0.4.0
- optional: true
-
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -5228,8 +5174,6 @@ snapshots:
cookie@0.7.1: {}
- core-js@3.40.0: {}
-
crelt@1.0.6: {}
cross-spawn@7.0.3:
@@ -5319,10 +5263,6 @@ snapshots:
dependencies:
esutils: 2.0.3
- dompurify@3.2.3:
- optionalDependencies:
- '@types/trusted-types': 2.0.7
-
dotenv@16.4.5: {}
drizzle-kit@0.25.0:
@@ -5908,16 +5848,6 @@ snapshots:
graphemer@1.4.0: {}
- handsontable@15.0.0:
- dependencies:
- '@handsontable/pikaday': 1.0.0
- core-js: 3.40.0
- dompurify: 3.2.3
- moment: 2.30.1
- numbro: 2.5.0
- optionalDependencies:
- hyperformula: 2.7.1
-
has-bigints@1.0.2: {}
has-flag@4.0.0: {}
@@ -5964,12 +5894,6 @@ snapshots:
html-url-attributes@3.0.1: {}
- hyperformula@2.7.1:
- dependencies:
- chevrotain: 6.5.0
- tiny-emitter: 2.1.0
- optional: true
-
ignore@5.3.2: {}
import-fresh@3.3.0:
@@ -6605,8 +6529,6 @@ snapshots:
minipass@7.1.2: {}
- moment@2.30.1: {}
-
ms@2.1.3: {}
mz@2.7.0:
@@ -6664,10 +6586,6 @@ snapshots:
normalize-path@3.0.0: {}
- numbro@2.5.0:
- dependencies:
- bignumber.js: 9.1.2
-
oauth4webapi@3.1.2: {}
object-assign@4.1.1: {}
@@ -6973,6 +6891,12 @@ snapshots:
queue-microtask@1.2.3: {}
+ react-data-grid@7.0.0-beta.47(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021):
+ dependencies:
+ clsx: 2.1.1
+ react: 19.0.0-rc-45804af1-20241021
+ react-dom: 19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021)
+
react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021):
dependencies:
react: 19.0.0-rc-45804af1-20241021
@@ -7050,9 +6974,6 @@ snapshots:
globalthis: 1.0.4
which-builtin-type: 1.1.4
- regexp-to-ast@0.4.0:
- optional: true
-
regexp.prototype.flags@1.5.3:
dependencies:
call-bind: 1.0.7
@@ -7384,9 +7305,6 @@ snapshots:
throttleit@2.1.0: {}
- tiny-emitter@2.1.0:
- optional: true
-
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0