[components,excel][m]: initial version of the <Excel /> component

This commit is contained in:
João Demenech 2023-07-10 17:44:11 -03:00
parent df000b9e8f
commit cbe693825c
4 changed files with 265 additions and 4 deletions

185
package-lock.json generated
View File

@ -13812,6 +13812,14 @@
"node": ">= 10.0.0"
}
},
"node_modules/adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -15480,6 +15488,18 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"dependencies": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/chai": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
@ -15803,6 +15823,14 @@
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==",
"dev": true
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/collect-v8-coverage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@ -16291,6 +16319,17 @@
"node": ">=10"
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@ -20129,6 +20168,14 @@
"node": ">= 0.6"
}
},
"node_modules/frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@ -32795,6 +32842,18 @@
"react-dom": ">=16.8.0"
}
},
"node_modules/react-data-grid": {
"version": "7.0.0-beta.34",
"resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-7.0.0-beta.34.tgz",
"integrity": "sha512-OwOP77vabb47xhbvHCXJ2QwFHsfduoLJhgXncGe0gIjrrwJ9xr2SdfEe/U6KAfHnLuBDxCDoY0SOgfJiOQtd+Q==",
"dependencies": {
"clsx": "^1.1.1"
},
"peerDependencies": {
"react": "^18.0",
"react-dom": "^18.0"
}
},
"node_modules/react-docgen": {
"version": "6.0.0-alpha.3",
"resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-6.0.0-alpha.3.tgz",
@ -34962,6 +35021,17 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
"node_modules/ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"dependencies": {
"frac": "~1.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
@ -38957,6 +39027,22 @@
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
"dev": true
},
"node_modules/wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@ -39063,6 +39149,26 @@
}
}
},
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"dependencies": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/xml-name-validator": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
@ -39635,7 +39741,7 @@
},
"packages/components": {
"name": "@portaljs/components",
"version": "0.1.12",
"version": "0.2.0",
"dependencies": {
"@githubocto/flat-ui": "^0.14.1",
"@heroicons/react": "^2.0.17",
@ -39648,6 +39754,7 @@
"ol": "^7.4.0",
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-data-grid": "^7.0.0-beta.34",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-leaflet": "^4.2.1",
@ -39655,7 +39762,8 @@
"react-vega": "^7.6.0",
"vega": "5.25.0",
"vega-lite": "5.1.0",
"vitest": "^0.31.4"
"vitest": "^0.31.4",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.7",
@ -46884,6 +46992,7 @@
"postcss-import-url": "^7.2.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-data-grid": "*",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-leaflet": "^4.2.1",
@ -46897,7 +47006,8 @@
"vite": "^4.3.2",
"vite-plugin-dts": "^2.3.0",
"vite-plugin-swc-only": "^0.1.18",
"vitest": "^0.31.4"
"vitest": "^0.31.4",
"xlsx": "^0.18.5"
},
"dependencies": {
"@eslint/eslintrc": {
@ -50929,6 +51039,11 @@
"integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==",
"dev": true
},
"adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="
},
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -52180,6 +52295,15 @@
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
},
"cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"requires": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
}
},
"chai": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
@ -52403,6 +52527,11 @@
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==",
"dev": true
},
"codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="
},
"collect-v8-coverage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@ -52804,6 +52933,11 @@
"yaml": "^1.10.0"
}
},
"crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@ -55765,6 +55899,11 @@
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true
},
"frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
},
"fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@ -65187,6 +65326,14 @@
"dev": true,
"requires": {}
},
"react-data-grid": {
"version": "7.0.0-beta.34",
"resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-7.0.0-beta.34.tgz",
"integrity": "sha512-OwOP77vabb47xhbvHCXJ2QwFHsfduoLJhgXncGe0gIjrrwJ9xr2SdfEe/U6KAfHnLuBDxCDoY0SOgfJiOQtd+Q==",
"requires": {
"clsx": "^1.1.1"
}
},
"react-docgen": {
"version": "6.0.0-alpha.3",
"resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-6.0.0-alpha.3.tgz",
@ -66857,6 +67004,14 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
"ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"requires": {
"frac": "~1.1.2"
}
},
"sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
@ -69873,6 +70028,16 @@
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
"dev": true
},
"wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="
},
"word": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
},
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@ -69946,6 +70111,20 @@
"devOptional": true,
"requires": {}
},
"xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"requires": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
}
},
"xml-name-validator": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",

View File

@ -37,6 +37,7 @@
"ol": "^7.4.0",
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-data-grid": "^7.0.0-beta.34",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-leaflet": "^4.2.1",
@ -44,7 +45,8 @@
"react-vega": "^7.6.0",
"vega": "5.25.0",
"vega-lite": "5.1.0",
"vitest": "^0.31.4"
"vitest": "^0.31.4",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.7",

View File

@ -0,0 +1,53 @@
import { useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';
import { read, utils } from 'xlsx';
import DataGrid, { Column, textEditor } from 'react-data-grid';
import 'react-data-grid/lib/styles.css';
export type ExcelProps = {
url: string;
};
export function Excel({ url }: ExcelProps) {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [rows, setRows] = useState<any>();
const [cols, setCols] = useState<any>();
useEffect(() => {
setIsLoading(true);
fetch(url)
.then((res) => res.arrayBuffer())
.then((f) => {
const wb = read(f);
const ws = wb.Sheets[wb.SheetNames[0]];
const rows = utils.sheet_to_json(ws, { header: 1 });
const range = utils.decode_range(ws['!ref'] || 'A1');
const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
key: String(i),
name: utils.encode_col(i),
editor: textEditor,
}));
setRows(rows);
setCols(columns);
setIsLoading(false);
});
}, []);
return isLoading ? (
<div className="w-full flex items-center justify-center w-[600px] h-[300px]">
<LoadingSpinner />
</div>
) : (
<>
{cols && rows && (
<DataGrid columns={cols} rows={rows} onRowsChange={setRows} />
)}
</>
);
}

View File

@ -0,0 +1,27 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Excel, ExcelProps } from '../src/components/Excel';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta = {
title: 'Components/Excel',
component: Excel,
tags: ['autodocs'],
argTypes: {
url: {
description:
'Data to be displayed.\n\n E.g.: [["1990", 1], ["1991", 2]] \n\nOR\n\n "https://url.to/data.csv"',
},
},
};
export default meta;
type Story = StoryObj<ExcelProps>;
export const FromURL: Story = {
name: 'Excel spreadsheet from URL',
args: {
url: 'https://sheetjs.com/pres.xlsx',
},
};