docs.sheetjs.com/docz/docs/03-demos/02-grid/index.md

436 lines
13 KiB
Markdown
Raw Normal View History

2022-08-01 05:34:23 +00:00
---
2022-08-26 19:21:53 +00:00
title: Data Grids and Tables
2023-02-28 11:40:44 +00:00
pagination_prev: demos/frontend/index
pagination_next: demos/net/index
2022-08-01 05:34:23 +00:00
---
2023-04-27 09:12:19 +00:00
import current from '/version.js';
2023-05-03 03:40:40 +00:00
import CodeBlock from '@theme/CodeBlock';
2023-04-27 09:12:19 +00:00
2022-08-01 05:34:23 +00:00
Various JavaScript UI components provide a more interactive editing experience.
Most are able to interchange with arrays of arrays or arrays of data objects.
This demo focuses on a few open source data grids.
:::note
[SheetJS Pro](https://sheetjs.com/pro) offers additional features like styling
and images. The UI tools typically support many of these advanced features.
To eliminate any confusion, the live examples linked from this page demonstrate
SheetJS Community Edition data interchange.
:::
## Managed Lifecycle
Many UI components tend to manage the entire lifecycle, providing methods to
import and export data.
The `sheet_to_json` utility function generates arrays of objects, which is
suitable for a number of libraries. When more advanced shapes are needed,
2022-08-25 08:22:28 +00:00
it is easier to process an array of arrays.
2022-08-01 05:34:23 +00:00
2022-08-18 08:41:34 +00:00
### x-spreadsheet
2023-02-28 11:40:44 +00:00
With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
2022-08-18 08:41:34 +00:00
[Click here for a live integration demo.](pathname:///xspreadsheet/)
2023-03-12 06:25:57 +00:00
**[The exposition has been moved to a separate page.](/docs/demos/grid/xs)**
2022-08-18 08:41:34 +00:00
2023-03-12 06:25:57 +00:00
### Canvas Datagrid
2022-08-01 05:34:23 +00:00
2023-03-12 06:25:57 +00:00
After extensive testing, `canvas-datagrid` stood out as a high-performance grid
with a straightforward API.
2022-08-01 05:34:23 +00:00
[Click here for a live integration demo.](pathname:///cdg/index.html)
2023-03-12 06:25:57 +00:00
**[The exposition has been moved to a separate page.](/docs/demos/grid/cdg)**
2022-08-24 23:48:22 +00:00
### Tabulator
2022-10-19 21:12:12 +00:00
[Tabulator](https://tabulator.info/docs/5.4/download#xlsx) includes deep support
2022-08-25 08:22:28 +00:00
through a special Export button. It handles the SheetJS operations internally.
2022-08-24 23:48:22 +00:00
2022-08-01 05:34:23 +00:00
### Angular UI Grid
:::warning
This UI Grid is for AngularJS, not the modern Angular. New projects should not
use AngularJS. This demo is included for legacy applications.
2023-04-09 00:20:50 +00:00
The [AngularJS demo](/docs/demos/frontend/angularjs) covers more general strategies.
2022-08-01 05:34:23 +00:00
:::
[Click here for a live integration demo.](pathname:///angularjs/ui-grid.html)
<details><summary><b>Notes</b> (click to show)</summary>
The library does not provide any way to modify the import button, so the demo
2022-08-25 08:22:28 +00:00
includes a simple directive for a File Input HTML element. It also includes a
2022-08-01 05:34:23 +00:00
sample service for export which adds an item to the export menu.
The demo `SheetJSImportDirective` follows the prescription from the README for
File input controls using `readAsArrayBuffer`, converting to a suitable
representation and updating the scope.
`SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other
file formats can be exported by changing the `bookType` variable. It grabs
values from the grid, builds an array of arrays, generates a workbook and forces
a download. By setting the `filename` and `sheetname` options in the `ui-grid`
options, the output can be controlled.
</details>
2022-08-17 07:10:01 +00:00
## Framework Lifecycle
For modern frameworks like React, data grids tend to follow the framework state
and idioms. The same `sheet_to_json` and `json_to_sheet` / `aoa_to_sheet`
methods are used, but they pull from a shared state object that can be mutated
with other buttons and components on the page.
### React Data Grid
2023-04-19 08:50:07 +00:00
**[The exposition has been moved to a separate page.](/docs/demos/grid/rdg)**
2022-08-17 07:10:01 +00:00
2023-02-07 09:24:49 +00:00
### Glide Data Grid
2023-04-07 08:30:20 +00:00
**[The exposition has been moved to a separate page.](/docs/demos/grid/gdg)**
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
### Material UI Data Grid
Material UI Data Grid and React Data Grid share many state patterns and idioms.
2023-04-19 08:50:07 +00:00
Differences from ["React Data Grid"](/docs/demos/grid/rdg) will be highlighted.
2022-09-03 10:02:45 +00:00
[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";
2022-08-17 07:10:01 +00:00
export default function App() {
2022-09-03 10:02:45 +00:00
const [rows, setRows] = useState([]);
const [columns, setColumns] = useState([]);
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
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[]; };
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
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) => ({
2022-08-17 07:10:01 +00:00
// highlight-start
2022-09-03 10:02:45 +00:00
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
2022-08-17 07:10:01 +00:00
// highlight-end
2022-09-03 10:02:45 +00:00
}));
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
return { rows, columns }; // these can be fed to setRows / setColumns
}
```
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
In the other direction, a worksheet can be generated with `aoa_to_sheet`:
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
:::caution
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
`x-data-grid` does not properly preserve row array objects, so the row arrays
must be re-created. The snippet defines a `arrayify` function.
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
:::
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
```ts
import { WorkSheet, utils } from 'xlsx';
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
type Row = any[];
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
// 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));
2022-08-17 07:10:01 +00:00
}
```
2022-09-03 10:02:45 +00:00
<!-- spellchecker-disable -->
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
#### 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:
2023-05-03 03:40:40 +00:00
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid @emotion/react @emotion/styled`}
</CodeBlock>
2022-09-03 10:02:45 +00:00
2) Download [`App.tsx`](pathname:///muidg/App.tsx) and replace `src/App.tsx`.
2022-08-17 07:10:01 +00:00
2022-09-03 10:02:45 +00:00
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.
2022-08-18 08:41:34 +00:00
</details>
2022-08-25 08:22:28 +00:00
<!-- spellchecker-disable -->
2022-08-18 08:41:34 +00:00
### vue3-table-lite
2022-08-25 08:22:28 +00:00
<!-- spellchecker-enable -->
2023-04-24 08:50:42 +00:00
**[The exposition has been moved to a separate page.](/docs/demos/grid/vtl)**
2022-08-18 08:41:34 +00:00
2022-09-03 10:02:45 +00:00
## 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`:
2023-05-03 03:40:40 +00:00
<CodeBlock language="html">{`\
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
\n\
<!-- table with id \`xport\` -->
2022-09-03 10:02:45 +00:00
<table id="xport"><tr><td>SheetJS</td></tr></table>
2023-05-03 03:40:40 +00:00
\n\
2022-09-03 10:02:45 +00:00
<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");
2023-05-03 03:40:40 +00:00
</script>`}
</CodeBlock>
2022-09-03 10:02:45 +00:00
When programmatically constructing the table in the browser, retain a reference:
```js
2023-05-03 03:40:40 +00:00
/* assemble table */
2022-09-03 10:02:45 +00:00
var tbl = document.createElement("TABLE");
tbl.insertRow(0).insertCell(0).innerHTML = "SheetJS";
2023-05-03 03:40:40 +00:00
/* add to document body */
2022-09-03 10:02:45 +00:00
document.body.appendChild(tbl);
2023-05-03 03:40:40 +00:00
/* generate workbook and export */
2022-09-03 10:02:45 +00:00
const wb = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(wb, "HTMLFlicker.xlsx");
2023-05-03 03:40:40 +00:00
/* remove from document body */
2022-09-03 10:02:45 +00:00
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>
2023-02-28 11:40:44 +00:00
</> );
2022-09-03 10:02:45 +00:00
}
```
### 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);
2023-02-28 11:40:44 +00:00
return ( <>
2022-09-03 10:02:45 +00:00
<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:
2023-05-03 03:40:40 +00:00
<CodeBlock language="bash">{`\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material`}
</CodeBlock>
2022-09-03 10:02:45 +00:00
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&nbsp;(g)</TableCell>
<TableCell align="right">Carbs&nbsp;(g)</TableCell>
<TableCell align="right">Protein&nbsp;(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>