diff --git a/.spelling b/.spelling index 14d7ffe..b578e6f 100644 --- a/.spelling +++ b/.spelling @@ -197,6 +197,7 @@ PPI PhantomJS Photoshop PostgreSQL +PouchDB PowerShell Preact QuickJS diff --git a/Makefile b/Makefile index cd0407e..9dee755 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ build: .PHONY: init init: - cd docz; npm i; cd .. + cd docz; npm i || npm i --legacy-peer-deps; cd .. .PHONY: dev dev: diff --git a/docz/docs/03-demos/04-grid.md b/docz/docs/03-demos/04-grid.md index 443acec..470030c 100644 --- a/docz/docs/03-demos/04-grid.md +++ b/docz/docs/03-demos/04-grid.md @@ -303,6 +303,35 @@ function ws_to_rdg(ws: WorkSheet): RowCol { In the other direction, a worksheet can be generated with `aoa_to_sheet`: +:::caution + +When the demo was last refreshed, row array objects were preserved. This was +not the case in a later release. The row arrays must be re-created. + +The snippet defines a `arrayify` function that creates arrays if necessary. + +::: + +```ts +import { WorkSheet, utils } from 'xlsx'; + +type Row = any[]; + +// highlight-start +function arrayify(rows: any[]): Row[] { + return rows.map(row => { + var length = Object.keys(row).length; + for(; length > 0; --length) if(row[length-1] != null) break; + return Array.from({length, ...row}); + }); +} +// highlight-end + +function rdg_to_ws(rows: Row[]): WorkSheet { + return utils.aoa_to_sheet(arrayify(rows)); +} +``` + ```ts import { WorkSheet, utils } from 'xlsx'; @@ -334,125 +363,128 @@ cd sheetjs-cra npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz react-data-grid ``` -3) Replace the contents of `src/App.tsx` with the following code. Note: a copy -to clipboard button will show up if you move your mouse over the code. The -notable SheetJS integration code is highlighted below: - -```tsx title="src/App.tsx" -import React, { useEffect, useState, ChangeEvent } from "react"; -import DataGrid, { textEditor, Column } from "react-data-grid"; -import { read, utils, WorkSheet, writeFile } from "xlsx"; - -import './App.css'; - -type DataSet = { [index: string]: WorkSheet; }; -type Row = any[]; -type AOAColumn = Column; -type RowCol = { rows: Row[]; columns: AOAColumn[]; }; - -/* this method returns `rows` and `columns` data for sheet change */ -const getRowsCols = ( data: DataSet, sheetName: string ): RowCol => ({ - rows: utils.sheet_to_json(data[sheetName], {header:1}), - columns: Array.from({ - length: utils.decode_range(data[sheetName]["!ref"]||"A1").e.c + 1 - }, (_, i) => ({ key: String(i), name: utils.encode_col(i), editor: textEditor })) -}); - -export default function App() { - const [rows, setRows] = useState([]); // data rows - const [columns, setColumns] = useState([]); // columns - const [workBook, setWorkBook] = useState({} as DataSet); // workbook - const [sheets, setSheets] = useState([]); // list of sheet names - const [current, setCurrent] = useState(""); // selected sheet - - /* called when sheet dropdown is changed */ - function selectSheet(name: string) { - // highlight-start - /* update workbook cache in case the current worksheet was changed */ - workBook[current] = utils.aoa_to_sheet(rows); - // highlight-end - - /* get data for desired sheet and update state */ - const { rows: new_rows, columns: new_columns } = getRowsCols(workBook, name); - setRows(new_rows); - setColumns(new_columns); - setCurrent(name); - } - - /* this method handles refreshing the state with new workbook data */ - async function handleAB(file: ArrayBuffer): Promise { - // highlight-start - /* read file data */ - const data = read(file); - // highlight-end - - /* update workbook state */ - setWorkBook(data.Sheets); - setSheets(data.SheetNames); - - /* select the first worksheet */ - const name = data.SheetNames[0]; - const { rows: new_rows, columns: new_columns } = getRowsCols(data.Sheets, name); - setRows(new_rows); - setColumns(new_columns); - setCurrent(name); - } - - /* called when file input element is used to select a new file */ - async function handleFile(ev: ChangeEvent): Promise { - const file = await ev.target.files?.[0]?.arrayBuffer(); - if(file) await handleAB(file); - } - - /* when page is loaded, fetch and processs worksheet */ - useEffect(() => { (async () => { - const f = await fetch("https://sheetjs.com/pres.numbers"); - await handleAB(await f.arrayBuffer()); - })(); }, []); - - /* method is called when one of the save buttons is clicked */ - function saveFile(ext: string): void { - /* update current worksheet in case changes were made */ - workBook[current] = utils.aoa_to_sheet(rows); - - // highlight-start - /* construct workbook and loop through worksheets */ - const wb = utils.book_new(); - sheets.forEach((n) => { utils.book_append_sheet(wb, workBook[n], n); }); - // highlight-end - - /* generate a file and download it */ - writeFile(wb, "sheet." + ext); - } - - return ( - <> -

SheetJS × React-Data-Grid Demo

- - {sheets.length > 0 && ( <> -

Use the dropdown to switch to a worksheet:  - -

-
Current Sheet: {current}
- -

Click one of the buttons to create a new file with the modified data

-
{["xlsx", "xlsb", "xls"].map((ext) => ( - - ))}
- )} - - ); -} -``` +3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`. 4) run `npm start`. When you load the page in the browser, it will attempt to fetch and load the data. The following screenshot was taken from the demo: -![react-data-grid screenshot](pathname:///react/rdg1.png) +![react-data-grid screenshot](pathname:///rdg/rdg1.png) + + + +### Material UI Data Grid + +Material UI Data Grid and React Data Grid share many state patterns and idioms. +Differences from ["React Data Grid"](#react-data-grid) will be highlighted. + +[A complete example is included below.](#muidg-demo) + +:::warning + +Despite presenting an editable UI, Material UI Data Grid version `5.17.0` does +not update the state when values are changed. The demo uses the React Data Grid +editable structure in the hopes that a future version does support state. + +Until the issues are resolved, "React Data Grid" is an excellent choice. + +::: + +**Rows and Columns State** + +The analogue of `Column` is `GridColDef`. The simple structure looks like: + +```js +// highlight-next-line +import { DataGrid, GridColDef } from "@mui/x-data-grid"; + +export default function App() { + const [rows, setRows] = useState([]); + const [columns, setColumns] = useState([]); + + return ( ); +} +``` + +The most generic data representation is an array of arrays. To sate the grid, +columns must be objects whose `field` property is the index converted to string: + +```ts +import { WorkSheet, utils } from 'xlsx'; +// highlight-next-line +import { GridColDef } from "@mui/x-data-grid"; + +type Row = any[]; +type RowCol = { rows: Row[]; columns: GridColDef[]; }; + +function ws_to_muidg(ws: WorkSheet): RowCol { + /* create an array of arrays */ + const rows = utils.sheet_to_json(ws, { header: 1 }); + + /* create column array */ + const range = utils.decode_range(ws["!ref"]||"A1"); + const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({ + // highlight-start + field: String(i), // MUIDG will access row["0"], row["1"], etc + headerName: utils.encode_col(i), // the column labels will be A, B, etc + editable: true // enable cell editing + // highlight-end + })); + + return { rows, columns }; // these can be fed to setRows / setColumns +} +``` + +In the other direction, a worksheet can be generated with `aoa_to_sheet`: + +:::caution + +`x-data-grid` does not properly preserve row array objects, so the row arrays +must be re-created. The snippet defines a `arrayify` function. + +::: + +```ts +import { WorkSheet, utils } from 'xlsx'; + +type Row = any[]; + +// highlight-start +function arrayify(rows: any[]): Row[] { + return rows.map(row => { + var length = Object.keys(row).length; + for(; length > 0; --length) if(row[length-1] != null) break; + return Array.from({length, ...row}); + }); +} +// highlight-end + +function muidg_to_ws(rows: Row[]): WorkSheet { + return utils.aoa_to_sheet(arrayify(rows)); +} +``` + + + +#### MUIDG Demo + + + +
Complete Example (click to show) + +0) [Follow the React Data Grid demo](#rdg-demo) and generate the sample app. + +1) Install dependencies: + +```bash +npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @mui/x-data-grid @emotion/react @emotion/styled +``` + +2) Download [`App.tsx`](pathname:///muidg/App.tsx) and replace `src/App.tsx`. + +3) run `npm start`. When you load the page in the browser, it will attempt to + fetch and load the data.
@@ -585,3 +617,205 @@ cd .. fetch and load the data. + +## Standard HTML Tables + +Many UI components present styled HTML tables. Data can be extracted from the +tables given a reference to the underlying TABLE element: + +```js +function export_html_table(table) { + const wb = XLSX.utils.table_to_book(table); + XLSX.writeFile(wb, "HTMLTable.xlsx"); +} // yes, it's that easy! +``` + +:::info + +SheetJS CE is focused on data preservation and will extract values from tables. + +[SheetJS Pro](https://sheetjs.com/pro) offers styling support when reading from +TABLE elements and when writing to XLSX and other spreadsheet formats. + +::: + +### Fixed Tables + +When the page has a raw HTML table, the easiest solution is to attach an `id`: + +```html +
SheetJS
+ + + +``` + +When programmatically constructing the table in the browser, retain a reference: + +```js +var tbl = document.createElement("TABLE"); +tbl.insertRow(0).insertCell(0).innerHTML = "SheetJS"; +document.body.appendChild(tbl); +const wb = XLSX.utils.table_to_book(tbl); +XLSX.writeFile(wb, "HTMLFlicker.xlsx"); +document.body.removeChild(tbl); +``` + +### React + +The typical solution is to attach a Ref to the table element. The `current` +property will be a live reference which plays nice with `table_to_book`: + +```jsx +// highlight-next-line +import { useRef } from "react"; + +export default function ReactTable() { +// highlight-next-line + const tbl = useRef(null); + + return ( <> + + // highlight-next-line + + {/* ... TR and TD/TH elements ... */} +
+ ); +} +``` + +### Material UI Table + +The `Table` component abstracts the `` element in HTML. + +```tsx +import TableContainer from '@mui/material/TableContainer'; +import Table from '@mui/material/Table'; +// ... +// highlight-next-line +import { useRef } from "react"; + +// ... +export default function BasicTable() { +// highlight-next-line + const tbl = useRef(null); + return (<> + + +// highlight-next-line +
+ {/* ... material ui table machinations ... */} +
+ + <>); +} +``` + +
Complete Example (click to show) + +1) Create a new TypeScript `create-react-app` app: + +```bash +npx create-react-app sheetjs-mui --template typescript +cd sheetjs-mui +``` + +2) Install dependencies: + +```bash +npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz @mui/material +``` + +3) Replace `src/App.tsx` with the following code. This is based on the official +Material UI Table example. Differences are highlighted. + +```tsx title="src/App.tsx" +// highlight-start +import React, { useEffect, useState, useRef, ChangeEvent } from "react"; +import { utils, writeFileXLSX } from 'xlsx'; +// highlight-end +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Paper from '@mui/material/Paper'; + +function createData( + name: string, + calories: number, + fat: number, + carbs: number, + protein: number, +) { + return { name, calories, fat, carbs, protein }; +} + +const rows = [ + createData('Frozen yoghurt', 159, 6.0, 24, 4.0), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3), + createData('Eclair', 262, 16.0, 24, 6.0), + createData('Cupcake', 305, 3.7, 67, 4.3), + createData('Gingerbread', 356, 16.0, 49, 3.9), +]; + +export default function BasicTable() { + // highlight-start + const tbl = useRef(null); + return ( <> + + // highlight-end + + // highlight-next-line + + + + Dessert (100g serving) + Calories + Fat (g) + Carbs (g) + Protein (g) + + + + {rows.map((row) => ( + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + ))} + +
+
+ // highlight-next-line + ); +} +``` + +4) run `npm start`. Click the "Export" button and inspect the generated file. + +
diff --git a/docz/docs/03-demos/11-angular.md b/docz/docs/03-demos/11-angular.md index 352aa5f..a7668ba 100644 --- a/docz/docs/03-demos/11-angular.md +++ b/docz/docs/03-demos/11-angular.md @@ -15,7 +15,7 @@ and TypeScript familiarity is assumed. Other demos cover general Angular deployments, including: - [iOS and Android applications powered by NativeScript](./mobile#nativescript) -- [iOS and Android applications powered by ionic](./mobile#ionic) +- [iOS and Android applications powered by Ionic](./mobile#ionic) :::warning diff --git a/docz/docs/03-demos/41-nosql.md b/docz/docs/03-demos/41-nosql.md index 30ecf52..f47bddb 100644 --- a/docz/docs/03-demos/41-nosql.md +++ b/docz/docs/03-demos/41-nosql.md @@ -205,3 +205,104 @@ Inspect the output and compare with the data in `SheetJSRedisTest.mjs`. Open `SheetJSRedis.xlsx` and verify the columns have the correct data + + +### PouchDB + +`Database#allDocs` is the standard approach for bulk data export. The generated +row objects have an additional `_id` and `_rev` keys that should be removed. + +Nested objects must be flattened. The ["Tutorial"](../getting-started/example) +includes an example of constructing a simple array. + +```js +function export_pouchdb_to_xlsx(db) { + /* fetch all rows, including the underlying data */ + db.allDocs({include_docs: true}, function(err, doc) { + + /* pull the individual data rows */ + const aoo = doc.rows.map(r => { + /* `rest` will include every field from `r` except for _id and _rev */ + const { _id, _rev, ...rest } = r; + return rest; + }); + + /* generate worksheet */ + const ws = XLSX.utils.json_to_sheet(aoo); + + /* generate workbook and export */ + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); + XLSX.writeFile(wb, "SheetJSPouch.xlsx"); + }); +} +``` + +
Complete Example (click to show) + +0) Download the "Working Version" from the Getting Started guide. + +[ZIP](https://github.com/nickcolley/getting-started-todo/archive/master.zip) + +The ZIP file should have `MD5` checksum `ac4da7cb0cade1be293ba222462f109c`: + +```bash +curl -LO https://github.com/nickcolley/getting-started-todo/archive/master.zip +md5sum master.zip || md5 master.zip +### the checksum will be printed +``` + +If the download is unavailable, a mirror is available at + + +1) Unzip the `master.zip` file and enter the folder: + +```bash +unzip master.zip +cd getting-started-todo-master +``` + +2) Edit `index.html` to reference the SheetJS library and add a button: + +```html title="index.html" + + + + + +
+``` + +3) Just before the end of `app.js`, add a `click` event listener: + +```js title="app.js" + if (remoteCouch) { + sync(); + } + + // highlight-start + document.getElementById("xport").addEventListener("click", function() { + db.allDocs({include_docs: true}, function(err, doc) { + const aoo = doc.rows.map(r => { + const { _id, _rev, ... rest } = r.doc; + return rest; + }); + const ws = XLSX.utils.json_to_sheet(aoo); + const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); + XLSX.writeFile(wb, "SheetJSPouch.xlsx"); + }); + }); + // highlight-end +})(); +``` + +4) Start a local web server: + +```bash +npx http-server . +``` + +Access `http://localhost:8080` from your browser. Add a few items and click +the "Export!" button to generate a new file. + +
diff --git a/docz/docs/03-demos/index.md b/docz/docs/03-demos/index.md index d34e837..1576354 100644 --- a/docz/docs/03-demos/index.md +++ b/docz/docs/03-demos/index.md @@ -35,7 +35,7 @@ run in the web browser, demos will include interactive examples. - [`react-data-grid`](./grid#react-data-grid) - [`vue3-table-lite`](./grid#vue3-table-lite) - [`angular-ui-grid`](./grid#angular-ui-grid) - +- [`material ui`](./grid#material-ui-table) ### Platforms and Integrations - [`Command-Line Tools`](./cli) diff --git a/docz/package.json b/docz/package.json index 288efe6..8a23db2 100644 --- a/docz/package.json +++ b/docz/package.json @@ -15,11 +15,11 @@ }, "dependencies": { "@cmfcmf/docusaurus-search-local": "0.11.0", - "@docusaurus/core": "2.0.1", - "@docusaurus/plugin-client-redirects": "2.0.1", - "@docusaurus/preset-classic": "2.0.1", - "@docusaurus/theme-common": "2.0.1", - "@docusaurus/theme-live-codeblock": "2.0.1", + "@docusaurus/core": "2.1.0", + "@docusaurus/plugin-client-redirects": "2.1.0", + "@docusaurus/preset-classic": "2.1.0", + "@docusaurus/theme-common": "2.1.0", + "@docusaurus/theme-live-codeblock": "2.1.0", "@mdx-js/react": "1.6.22", "clsx": "1.2.1", "prism-react-renderer": "1.3.5", @@ -28,7 +28,7 @@ "xlsx": "https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.0.1" + "@docusaurus/module-type-aliases": "2.1.0" }, "browserslist": { "production": [ diff --git a/docz/static/muidg/App.tsx b/docz/static/muidg/App.tsx new file mode 100644 index 0000000..dbc388c --- /dev/null +++ b/docz/static/muidg/App.tsx @@ -0,0 +1,111 @@ +import React, { useEffect, useState, ChangeEvent } from "react"; +import { DataGrid, GridColDef } from "@mui/x-data-grid"; +import { read, utils, WorkSheet, writeFile } from "xlsx"; + +import './App.css'; + +type DataSet = { [index: string]: WorkSheet; }; +type Row = any[]; +type RowCol = { rows: Row[]; columns: GridColDef[]; }; + +function arrayify(rows: any[]): Row[] { + return rows.map(row => { + if(Array.isArray(row)) return row; + var length = Object.keys(row).length; + for(; length > 0; --length) if(row[length-1] != null) break; + return Array.from({length, ...row}); + }); +} + +/* this method returns `rows` and `columns` data for sheet change */ +const getRowsCols = ( data: DataSet, sheetName: string ): RowCol => ({ + rows: utils.sheet_to_json(data[sheetName], {header:1}).map((r,id) => ({...r, id})), + columns: Array.from({ + length: utils.decode_range(data[sheetName]["!ref"]||"A1").e.c + 1 + }, (_, i) => ({ field: String(i), headerName: utils.encode_col(i), editable: true })) +}); + +export default function App() { + const [rows, setRows] = useState([]); // data rows + const [columns, setColumns] = useState([]); // columns + const [workBook, setWorkBook] = useState({} as DataSet); // workbook + const [sheets, setSheets] = useState([]); // list of sheet names + const [current, setCurrent] = useState(""); // selected sheet + + /* called when sheet dropdown is changed */ + function selectSheet(name: string) { + /* update workbook cache in case the current worksheet was changed */ + workBook[current] = utils.aoa_to_sheet(arrayify(rows)); + + /* get data for desired sheet and update state */ + const { rows: new_rows, columns: new_columns } = getRowsCols(workBook, name); + setRows(new_rows); + setColumns(new_columns); + setCurrent(name); + } + + /* this method handles refreshing the state with new workbook data */ + async function handleAB(file: ArrayBuffer): Promise { + /* read file data */ + const data = read(file); + + /* update workbook state */ + setWorkBook(data.Sheets); + setSheets(data.SheetNames); + + /* select the first worksheet */ + const name = data.SheetNames[0]; + const { rows: new_rows, columns: new_columns } = getRowsCols(data.Sheets, name); + setRows(new_rows); + setColumns(new_columns); + setCurrent(name); + } + + /* called when file input element is used to select a new file */ + async function handleFile(ev: ChangeEvent): Promise { + const file = await ev.target.files?.[0]?.arrayBuffer(); + if(file) await handleAB(file); + } + + /* when page is loaded, fetch and processs worksheet */ + useEffect(() => { (async () => { + const f = await fetch("https://sheetjs.com/pres.numbers"); + await handleAB(await f.arrayBuffer()); + })(); }, []); + + /* method is called when one of the save buttons is clicked */ + function saveFile(ext: string): void { + console.log(rows); + /* update current worksheet in case changes were made */ + workBook[current] = utils.aoa_to_sheet(arrayify(rows)); + + /* construct workbook and loop through worksheets */ + const wb = utils.book_new(); + sheets.forEach((n) => { utils.book_append_sheet(wb, workBook[n], n); }); + + /* generate a file and download it */ + writeFile(wb, "SheetJSMUIDG." + ext); + } + + return ( + <> +

SheetJS × MUI Data Grid Demo

+ + {sheets.length > 0 && ( <> +

Use the dropdown to switch to a worksheet:  + +

+
Current Sheet: {current}
+
+ +
+

Click one of the buttons to create a new file with the modified data

+
{["xlsx", "xlsb", "xls"].map((ext) => ( + + ))}
+ )} + + ); +} diff --git a/docz/static/pouchdb/master.zip b/docz/static/pouchdb/master.zip new file mode 100644 index 0000000..fc7b055 Binary files /dev/null and b/docz/static/pouchdb/master.zip differ diff --git a/docz/static/rdg/App.tsx b/docz/static/rdg/App.tsx new file mode 100644 index 0000000..80616d7 --- /dev/null +++ b/docz/static/rdg/App.tsx @@ -0,0 +1,110 @@ +import React, { useEffect, useState, ChangeEvent } from "react"; +import DataGrid, { textEditor, Column } from "react-data-grid"; +import { read, utils, WorkSheet, writeFile } from "xlsx"; + +import './App.css'; + +type DataSet = { [index: string]: WorkSheet; }; +type Row = any[]; +type AOAColumn = Column; +type RowCol = { rows: Row[]; columns: AOAColumn[]; }; + +function arrayify(rows: any[]): Row[] { + return rows.map(row => { + if(Array.isArray(row)) return row; + var length = Object.keys(row).length; + for(; length > 0; --length) if(row[length-1] != null) break; + return Array.from({length, ...row}); + }); +} + +/* this method returns `rows` and `columns` data for sheet change */ +const getRowsCols = ( data: DataSet, sheetName: string ): RowCol => ({ + rows: utils.sheet_to_json(data[sheetName], {header:1}), + columns: Array.from({ + length: utils.decode_range(data[sheetName]["!ref"]||"A1").e.c + 1 + }, (_, i) => ({ key: String(i), name: utils.encode_col(i), editor: textEditor })) +}); + +export default function App() { + const [rows, setRows] = useState([]); // data rows + const [columns, setColumns] = useState([]); // columns + const [workBook, setWorkBook] = useState({} as DataSet); // workbook + const [sheets, setSheets] = useState([]); // list of sheet names + const [current, setCurrent] = useState(""); // selected sheet + + /* called when sheet dropdown is changed */ + function selectSheet(name: string) { + /* update workbook cache in case the current worksheet was changed */ + workBook[current] = utils.aoa_to_sheet(arrayify(rows)); + + /* get data for desired sheet and update state */ + const { rows: new_rows, columns: new_columns } = getRowsCols(workBook, name); + setRows(new_rows); + setColumns(new_columns); + setCurrent(name); + } + + /* this method handles refreshing the state with new workbook data */ + async function handleAB(file: ArrayBuffer): Promise { + /* read file data */ + const data = read(file); + + /* update workbook state */ + setWorkBook(data.Sheets); + setSheets(data.SheetNames); + + /* select the first worksheet */ + const name = data.SheetNames[0]; + const { rows: new_rows, columns: new_columns } = getRowsCols(data.Sheets, name); + setRows(new_rows); + setColumns(new_columns); + setCurrent(name); + } + + /* called when file input element is used to select a new file */ + async function handleFile(ev: ChangeEvent): Promise { + const file = await ev.target.files?.[0]?.arrayBuffer(); + if(file) await handleAB(file); + } + + /* when page is loaded, fetch and processs worksheet */ + useEffect(() => { (async () => { + const f = await fetch("https://sheetjs.com/pres.numbers"); + await handleAB(await f.arrayBuffer()); + })(); }, []); + + /* method is called when one of the save buttons is clicked */ + function saveFile(ext: string): void { + console.log(rows); + /* update current worksheet in case changes were made */ + workBook[current] = utils.aoa_to_sheet(arrayify(rows)); + + /* construct workbook and loop through worksheets */ + const wb = utils.book_new(); + sheets.forEach((n) => { utils.book_append_sheet(wb, workBook[n], n); }); + + /* generate a file and download it */ + writeFile(wb, "SheetJSRDG." + ext); + } + + return ( + <> +

SheetJS × React-Data-Grid Demo

+ + {sheets.length > 0 && ( <> +

Use the dropdown to switch to a worksheet:  + +

+
Current Sheet: {current}
+ +

Click one of the buttons to create a new file with the modified data

+
{["xlsx", "xlsb", "xls"].map((ext) => ( + + ))}
+ )} + + ); +} diff --git a/docz/static/react/rdg1.png b/docz/static/rdg/rdg1.png similarity index 100% rename from docz/static/react/rdg1.png rename to docz/static/rdg/rdg1.png