forked from sheetjs/docs.sheetjs.com
docusaurus
This commit is contained in:
parent
ea501889ac
commit
9ae03ac35a
@ -197,6 +197,7 @@ PPI
|
||||
PhantomJS
|
||||
Photoshop
|
||||
PostgreSQL
|
||||
PouchDB
|
||||
PowerShell
|
||||
Preact
|
||||
QuickJS
|
||||
|
2
Makefile
2
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:
|
||||
|
@ -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<Row>;
|
||||
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<Row>(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<Row[]>([]); // data rows
|
||||
const [columns, setColumns] = useState<AOAColumn[]>([]); // columns
|
||||
const [workBook, setWorkBook] = useState<DataSet>({} as DataSet); // workbook
|
||||
const [sheets, setSheets] = useState<string[]>([]); // list of sheet names
|
||||
const [current, setCurrent] = useState<string>(""); // 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<void> {
|
||||
// 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<HTMLInputElement>): Promise<void> {
|
||||
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 (
|
||||
<>
|
||||
<h3>SheetJS × React-Data-Grid Demo</h3>
|
||||
<input type="file" onChange={handleFile} />
|
||||
{sheets.length > 0 && ( <>
|
||||
<p>Use the dropdown to switch to a worksheet:
|
||||
<select onChange={async (e) => selectSheet(sheets[+(e.target.value)])}>
|
||||
{sheets.map((sheet, idx) => (<option key={sheet} value={idx}>{sheet}</option>))}
|
||||
</select>
|
||||
</p>
|
||||
<div className="flex-cont"><b>Current Sheet: {current}</b></div>
|
||||
<DataGrid columns={columns} rows={rows} onRowsChange={setRows} />
|
||||
<p>Click one of the buttons to create a new file with the modified data</p>
|
||||
<div className="flex-cont">{["xlsx", "xlsb", "xls"].map((ext) => (
|
||||
<button key={ext} onClick={() => saveFile(ext)}>export [.{ext}]</button>
|
||||
))}</div>
|
||||
</>)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
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 <https://sheetjs.com/pres.numbers> 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)
|
||||
|
||||
</details>
|
||||
|
||||
### 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 ( <DataGrid columns={columns} rows={rows} onRowsChange={setRows} /> );
|
||||
}
|
||||
```
|
||||
|
||||
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));
|
||||
}
|
||||
```
|
||||
|
||||
<!-- spellchecker-disable -->
|
||||
|
||||
#### MUIDG Demo
|
||||
|
||||
<!-- spellchecker-enable -->
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
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 <https://sheetjs.com/pres.numbers> and load the data.
|
||||
|
||||
</details>
|
||||
|
||||
@ -585,3 +617,205 @@ cd ..
|
||||
fetch <https://sheetjs.com/pres.numbers> and load the data.
|
||||
|
||||
</details>
|
||||
|
||||
## 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
|
||||
<table id="xport"><tr><td>SheetJS</td></tr></table>
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
|
||||
<script>
|
||||
/* as long as this script appears after the table, it will be visible */
|
||||
var tbl = document.getElementById("xport");
|
||||
const wb = XLSX.utils.table_to_book(tbl);
|
||||
XLSX.writeFile(wb, "HTMLTable.xlsx");
|
||||
</script>
|
||||
```
|
||||
|
||||
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 ( <>
|
||||
<button onClick={() => {
|
||||
// highlight-next-line
|
||||
const wb = XLSX.utils.table_to_book(tbl.current);
|
||||
XLSX.writeFile(wb, "ReactTable.xlsx");
|
||||
}}>Export</button>
|
||||
// highlight-next-line
|
||||
<table ref={tbl}>
|
||||
{/* ... TR and TD/TH elements ... */}
|
||||
</table>
|
||||
</>);
|
||||
}
|
||||
```
|
||||
|
||||
### Material UI Table
|
||||
|
||||
The `Table` component abstracts the `<table>` 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<HTMLTableElement>(null);
|
||||
return (<>
|
||||
<button onClick={() => {
|
||||
const wb = utils.table_to_book(tbl.current);
|
||||
writeFileXLSX(wb, "SheetJSMaterialUI.xlsx");
|
||||
}}>Export</button>
|
||||
<TableContainer {...}>
|
||||
// highlight-next-line
|
||||
<Table {...} ref={tbl}>
|
||||
{/* ... material ui table machinations ... */}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<>);
|
||||
}
|
||||
```
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
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<HTMLTableElement>(null);
|
||||
return ( <>
|
||||
<button onClick={() => {
|
||||
const wb = utils.table_to_book(tbl.current);
|
||||
writeFileXLSX(wb, "SheetJSMaterialUI.xlsx");
|
||||
}}>Export</button>
|
||||
// highlight-end
|
||||
<TableContainer component={Paper}>
|
||||
// highlight-next-line
|
||||
<Table sx={{ minWidth: 650 }} aria-label="simple table" ref={tbl}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Dessert (100g serving)</TableCell>
|
||||
<TableCell align="right">Calories</TableCell>
|
||||
<TableCell align="right">Fat (g)</TableCell>
|
||||
<TableCell align="right">Carbs (g)</TableCell>
|
||||
<TableCell align="right">Protein (g)</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.name}
|
||||
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||
>
|
||||
<TableCell component="th" scope="row">
|
||||
{row.name}
|
||||
</TableCell>
|
||||
<TableCell align="right">{row.calories}</TableCell>
|
||||
<TableCell align="right">{row.fat}</TableCell>
|
||||
<TableCell align="right">{row.carbs}</TableCell>
|
||||
<TableCell align="right">{row.protein}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
// highlight-next-line
|
||||
</> );
|
||||
}
|
||||
```
|
||||
|
||||
4) run `npm start`. Click the "Export" button and inspect the generated file.
|
||||
|
||||
</details>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### 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");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
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
|
||||
<https://docs.sheetjs.com/pouchdb/master.zip>
|
||||
|
||||
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"
|
||||
<body>
|
||||
<!-- highlight-start -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
|
||||
<button id="xport">Export!</button>
|
||||
<!-- highlight-end -->
|
||||
<section id="todoapp">
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
@ -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)
|
||||
|
@ -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": [
|
||||
|
111
docz/static/muidg/App.tsx
Normal file
111
docz/static/muidg/App.tsx
Normal file
@ -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<Row>(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<Row[]>([]); // data rows
|
||||
const [columns, setColumns] = useState<GridColDef[]>([]); // columns
|
||||
const [workBook, setWorkBook] = useState<DataSet>({} as DataSet); // workbook
|
||||
const [sheets, setSheets] = useState<string[]>([]); // list of sheet names
|
||||
const [current, setCurrent] = useState<string>(""); // 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<void> {
|
||||
/* 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<HTMLInputElement>): Promise<void> {
|
||||
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 (
|
||||
<>
|
||||
<h3>SheetJS × MUI Data Grid Demo</h3>
|
||||
<input type="file" onChange={handleFile} />
|
||||
{sheets.length > 0 && ( <>
|
||||
<p>Use the dropdown to switch to a worksheet:
|
||||
<select onChange={async (e) => selectSheet(sheets[+(e.target.value)])}>
|
||||
{sheets.map((sheet, idx) => (<option key={sheet} value={idx}>{sheet}</option>))}
|
||||
</select>
|
||||
</p>
|
||||
<div className="flex-cont"><b>Current Sheet: {current}</b></div>
|
||||
<div style={{width:"100%", height:400}}>
|
||||
<DataGrid columns={columns} rows={rows} experimentalFeatures={{ newEditingApi: true }} />
|
||||
</div>
|
||||
<p>Click one of the buttons to create a new file with the modified data</p>
|
||||
<div className="flex-cont">{["xlsx", "xlsb", "xls"].map((ext) => (
|
||||
<button key={ext} onClick={() => saveFile(ext)}>export [.{ext}]</button>
|
||||
))}</div>
|
||||
</>)}
|
||||
</>
|
||||
);
|
||||
}
|
BIN
docz/static/pouchdb/master.zip
Normal file
BIN
docz/static/pouchdb/master.zip
Normal file
Binary file not shown.
110
docz/static/rdg/App.tsx
Normal file
110
docz/static/rdg/App.tsx
Normal file
@ -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<Row>;
|
||||
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<Row>(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<Row[]>([]); // data rows
|
||||
const [columns, setColumns] = useState<AOAColumn[]>([]); // columns
|
||||
const [workBook, setWorkBook] = useState<DataSet>({} as DataSet); // workbook
|
||||
const [sheets, setSheets] = useState<string[]>([]); // list of sheet names
|
||||
const [current, setCurrent] = useState<string>(""); // 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<void> {
|
||||
/* 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<HTMLInputElement>): Promise<void> {
|
||||
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 (
|
||||
<>
|
||||
<h3>SheetJS × React-Data-Grid Demo</h3>
|
||||
<input type="file" onChange={handleFile} />
|
||||
{sheets.length > 0 && ( <>
|
||||
<p>Use the dropdown to switch to a worksheet:
|
||||
<select onChange={async (e) => selectSheet(sheets[+(e.target.value)])}>
|
||||
{sheets.map((sheet, idx) => (<option key={sheet} value={idx}>{sheet}</option>))}
|
||||
</select>
|
||||
</p>
|
||||
<div className="flex-cont"><b>Current Sheet: {current}</b></div>
|
||||
<DataGrid columns={columns} rows={rows} onRowsChange={setRows} />
|
||||
<p>Click one of the buttons to create a new file with the modified data</p>
|
||||
<div className="flex-cont">{["xlsx", "xlsb", "xls"].map((ext) => (
|
||||
<button key={ext} onClick={() => saveFile(ext)}>export [.{ext}]</button>
|
||||
))}</div>
|
||||
</>)}
|
||||
</>
|
||||
);
|
||||
}
|
Before (image error) Size: 107 KiB After (image error) Size: 107 KiB |
Loading…
Reference in New Issue
Block a user