2022-08-01 05:34:23 +00:00
|
|
|
---
|
2022-08-26 19:21:53 +00:00
|
|
|
title: Data Grids and Tables
|
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
|
|
|
|
|
|
|
|
With a familiar UI, [`x-spreadsheet`](https://myliang.github.io/x-spreadsheet/)
|
|
|
|
is an excellent choice for developers looking for a modern editor.
|
|
|
|
|
|
|
|
[Click here for a live integration demo.](pathname:///xspreadsheet/)
|
|
|
|
|
|
|
|
<details><summary><b>Full Exposition</b> (click to show)</summary>
|
|
|
|
|
|
|
|
**Obtaining the Library**
|
|
|
|
|
|
|
|
The `x-data-spreadsheet` NodeJS packages include a minified script that can be
|
|
|
|
directly inserted as a script tag. The unpkg CDN also serves this script:
|
|
|
|
|
|
|
|
```html
|
|
|
|
<!-- x-spreadsheet stylesheet -->
|
|
|
|
<link rel="stylesheet" href="https://unpkg.com/x-data-spreadsheet/dist/xspreadsheet.css"/>
|
|
|
|
<!-- x-spreadsheet library -->
|
|
|
|
<script src="https://unpkg.com/x-data-spreadsheet/dist/xspreadsheet.js"></script>
|
|
|
|
```
|
|
|
|
|
|
|
|
**Previewing and Editing Data**
|
|
|
|
|
|
|
|
The HTML document needs a container element:
|
|
|
|
|
|
|
|
```html
|
|
|
|
<div id="gridctr"></div>
|
|
|
|
```
|
|
|
|
|
|
|
|
Grid initialization is a one-liner:
|
|
|
|
|
|
|
|
```js
|
|
|
|
var grid = x_spreadsheet(document.getElementById("gridctr"));
|
|
|
|
```
|
|
|
|
|
|
|
|
`x-spreadsheet` handles the entire edit cycle. No intervention is necessary.
|
|
|
|
|
|
|
|
**SheetJS and x-spreadsheet**
|
|
|
|
|
|
|
|
The integration library can be downloaded from the SheetJS CDN:
|
|
|
|
|
|
|
|
[Development Use](https://cdn.sheetjs.com/xspreadsheet/xlsxspread.js)
|
|
|
|
|
|
|
|
[Production Use](https://cdn.sheetjs.com/xspreadsheet/xlsxspread.min.js)
|
|
|
|
|
|
|
|
|
|
|
|
When used in a browser tag, it exposes two functions: `xtos` and `stox`.
|
|
|
|
|
|
|
|
- `stox(worksheet)` returns a data structure suitable for `grid.loadData`
|
|
|
|
- `xtos(data)` accepts the result of `grid.getData` and generates a workbook
|
|
|
|
|
|
|
|
_Reading Data_
|
|
|
|
|
|
|
|
The following snippet fetches a spreadsheet and loads the grid:
|
|
|
|
|
|
|
|
```js
|
|
|
|
(async() => {
|
|
|
|
const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
|
|
|
|
grid.loadData(stox(XLSX.read(ab)));
|
|
|
|
})();
|
|
|
|
```
|
|
|
|
|
|
|
|
The same pattern can be used in file input elements and other data sources.
|
|
|
|
|
|
|
|
_Writing Data_
|
|
|
|
|
|
|
|
The following snippet exports the grid data to a file:
|
|
|
|
|
|
|
|
```js
|
|
|
|
/* build workbook from the grid data */
|
|
|
|
XLSX.writeFile(xtos(grid.getData()), "SheetJS.xlsx");
|
|
|
|
```
|
|
|
|
|
|
|
|
**Additional Features**
|
|
|
|
|
|
|
|
This demo barely scratches the surface. The underlying grid component includes
|
|
|
|
many additional features that work with [SheetJS Pro](https://sheetjs.com/pro).
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
2022-08-01 05:34:23 +00:00
|
|
|
### Canvas DataGrid
|
|
|
|
|
|
|
|
After extensive testing, [`canvas-datagrid`](https://canvas-datagrid.js.org/demo.html)
|
|
|
|
stood out as a very high-performance grid with an incredibly simple API.
|
|
|
|
|
|
|
|
[Click here for a live integration demo.](pathname:///cdg/index.html)
|
|
|
|
|
|
|
|
<details><summary><b>Full Exposition</b> (click to show)</summary>
|
|
|
|
|
|
|
|
**Obtaining the Library**
|
|
|
|
|
|
|
|
The `canvas-datagrid` NodeJS packages include a minified script that can be
|
|
|
|
directly inserted as a script tag. The unpkg CDN also serves this script:
|
|
|
|
|
|
|
|
```html
|
|
|
|
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
|
|
|
|
```
|
|
|
|
|
|
|
|
**Previewing Data**
|
|
|
|
|
|
|
|
The HTML document needs a container element:
|
|
|
|
|
|
|
|
```html
|
|
|
|
<div id="gridctr"></div>
|
|
|
|
```
|
|
|
|
|
|
|
|
Grid initialization is a one-liner:
|
|
|
|
|
|
|
|
```js
|
|
|
|
var grid = canvasDatagrid({
|
|
|
|
parentNode: document.getElementById('gridctr'),
|
|
|
|
data: []
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
For large data sets, it's necessary to constrain the size of the grid.
|
|
|
|
|
|
|
|
```js
|
|
|
|
grid.style.height = '100%';
|
|
|
|
grid.style.width = '100%';
|
|
|
|
```
|
|
|
|
|
|
|
|
Once the workbook is read and the worksheet is selected, assigning the data
|
|
|
|
variable automatically updates the view:
|
|
|
|
|
|
|
|
```js
|
|
|
|
grid.data = XLSX.utils.sheet_to_json(ws, {header:1});
|
|
|
|
```
|
|
|
|
|
|
|
|
This demo previews the first worksheet.
|
|
|
|
|
|
|
|
**Editing**
|
|
|
|
|
|
|
|
`canvas-datagrid` handles the entire edit cycle. No intervention is necessary.
|
|
|
|
|
|
|
|
**Saving Data**
|
|
|
|
|
|
|
|
`grid.data` is immediately readable and can be converted back to a worksheet.
|
|
|
|
Some versions return an array-like object without the length, so a little bit of
|
|
|
|
preparation may be needed:
|
|
|
|
|
|
|
|
```js
|
|
|
|
/* converts an array of array-like objects into an array of arrays */
|
|
|
|
function prep(arr) {
|
|
|
|
var out = [];
|
|
|
|
for(var i = 0; i < arr.length; ++i) {
|
|
|
|
if(!arr[i]) continue;
|
|
|
|
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
|
|
|
|
var o = new Array();
|
|
|
|
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
|
|
|
|
out[i] = o;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* build worksheet from the grid data */
|
|
|
|
var ws = XLSX.utils.aoa_to_sheet(prep(grid.data));
|
|
|
|
|
|
|
|
/* build up workbook */
|
|
|
|
var wb = XLSX.utils.book_new();
|
|
|
|
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
|
|
|
|
|
|
|
|
/* generate download */
|
|
|
|
XLSX.writeFile(wb, "SheetJS.xlsx");
|
|
|
|
```
|
|
|
|
|
|
|
|
**Additional Features**
|
|
|
|
|
|
|
|
This demo barely scratches the surface. The underlying grid component includes
|
|
|
|
many additional features including massive data streaming, sorting and styling.
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
2022-08-24 23:48:22 +00:00
|
|
|
|
|
|
|
### Tabulator
|
|
|
|
|
|
|
|
[Tabulator](http://tabulator.info/docs/5.3/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.
|
|
|
|
|
|
|
|
The [AngularJS demo](./legacy#angularjs) covers more general strategies.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
[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
|
|
|
|
|
|
|
|
:::note
|
|
|
|
|
|
|
|
This demo was tested against `react-data-grid 7.0.0-beta.15`, React 18.2.0,
|
|
|
|
and `create-react-app` 5.0.1 on 2022 August 16.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
[`react-data-grid`](https://github.com/adazzle/react-data-grid) is a data grid
|
|
|
|
built for React. `react-data-grid` powers <https://sheet.js.org/>
|
|
|
|
|
|
|
|
[A complete example is included below.](#rdg-demo)
|
|
|
|
|
|
|
|
#### Rows and Columns state
|
|
|
|
|
|
|
|
`react-data-grid` state consists of an Array of column metadata and an Array of
|
|
|
|
row objects. Typically both are defined in state:
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
import DataGrid, { Column } from "react-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,
|
2022-08-25 08:22:28 +00:00
|
|
|
columns must be objects whose `key` property is the index converted to string:
|
2022-08-17 07:10:01 +00:00
|
|
|
|
|
|
|
```ts
|
|
|
|
import { WorkSheet, utils } from 'xlsx';
|
|
|
|
import { textEditor, Column } from "react-data-grid";
|
|
|
|
|
|
|
|
type Row = any[];
|
|
|
|
type AOAColumn = Column<Row>;
|
|
|
|
type RowCol = { rows: Row[]; columns: AOAColumn[]; };
|
|
|
|
|
|
|
|
function ws_to_rdg(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) => ({
|
|
|
|
key: String(i), // RDG will access row["0"], row["1"], etc
|
|
|
|
name: utils.encode_col(i), // the column labels will be A, B, etc
|
|
|
|
editor: textEditor // enable cell editing
|
|
|
|
}));
|
|
|
|
|
|
|
|
return { rows, columns }; // these can be fed to setRows / setColumns
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
In the other direction, a worksheet can be generated with `aoa_to_sheet`:
|
|
|
|
|
2022-09-03 10:02:45 +00:00
|
|
|
:::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));
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-08-17 07:10:01 +00:00
|
|
|
```ts
|
|
|
|
import { WorkSheet, utils } from 'xlsx';
|
|
|
|
|
|
|
|
type Row = any[];
|
|
|
|
|
2022-08-18 08:41:34 +00:00
|
|
|
function rdg_to_ws(rows: Row[]): WorkSheet {
|
2022-08-17 07:10:01 +00:00
|
|
|
return utils.aoa_to_sheet(rows);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
<!-- spellchecker-disable -->
|
|
|
|
|
2022-08-17 07:10:01 +00:00
|
|
|
#### RDG Demo
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
<!-- spellchecker-enable -->
|
|
|
|
|
2022-08-17 07:10:01 +00:00
|
|
|
<details><summary><b>Complete Example</b> (click to show)</summary>
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
1) Create a new TypeScript `create-react-app` app:
|
2022-08-17 07:10:01 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
npx create-react-app sheetjs-cra --template typescript
|
|
|
|
cd sheetjs-cra
|
|
|
|
```
|
|
|
|
|
|
|
|
2) Install dependencies:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz react-data-grid
|
|
|
|
```
|
|
|
|
|
2022-09-03 10:02:45 +00:00
|
|
|
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.
|
2022-08-17 07:10:01 +00:00
|
|
|
|
2022-09-03 10:02:45 +00:00
|
|
|
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.
|
2022-08-17 07:10:01 +00:00
|
|
|
|
2022-09-03 10:02:45 +00:00
|
|
|
The following screenshot was taken from the demo:
|
2022-08-17 07:10:01 +00:00
|
|
|
|
2022-09-03 10:02:45 +00:00
|
|
|
![react-data-grid screenshot](pathname:///rdg/rdg1.png)
|
2022-08-17 07:10:01 +00:00
|
|
|
|
2022-09-03 10:02:45 +00:00
|
|
|
</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";
|
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:
|
|
|
|
|
|
|
|
```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`.
|
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 -->
|
|
|
|
|
2022-08-18 08:41:34 +00:00
|
|
|
:::note
|
|
|
|
|
|
|
|
This demo was tested against `vue3-table-lite 1.2.4`, VueJS `3.2.37`, ViteJS
|
|
|
|
3.0.7, and `@vitejs/plugin-vue` 3.0.3 on 2022 August 18
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
[`vue3-table-lite`](https://vue3-lite-table.vercel.app/) is a VueJS data grid.
|
2022-08-18 08:41:34 +00:00
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
[A complete example is included below.](#vuejs-demo)
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
#### Rows and Columns Bindings
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
`vue3-table-lite` presents two attribute bindings: an array of column metadata
|
2022-08-18 08:41:34 +00:00
|
|
|
(`columns`) and an array of objects representing the displayed data (`rows`).
|
|
|
|
Typically both are `ref` objects:
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref } from "vue";
|
|
|
|
import VueTableLite from "vue3-table-lite/ts";
|
|
|
|
|
|
|
|
/* rows */
|
|
|
|
type Row = any[];
|
|
|
|
const rows = ref<Row[]>([]);
|
|
|
|
|
|
|
|
/* columns */
|
|
|
|
type Column = { field: string; label: string; };
|
|
|
|
const columns = ref<Column[]>([]);
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<vue-table-lite :columns="columns" :rows="rows"></vue-table-lite>
|
|
|
|
</template>
|
|
|
|
```
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
These can be mutated through the `value` property in VueJS lifecycle methods:
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
```ts
|
|
|
|
import { onMounted } from "vue";
|
|
|
|
onMounted(() => {
|
|
|
|
columns.value = [ { field: "name", label: "Names" }];
|
|
|
|
rows.value = [ { name: "SheetJS" }, { name: "VueJS" } ];
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
The most generic data representation is an array of arrays. To sate the grid,
|
2022-08-25 08:22:28 +00:00
|
|
|
columns must be objects whose `field` property is the index converted to string:
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
import { ref } from "vue";
|
|
|
|
import { utils } from 'xlsx';
|
|
|
|
|
|
|
|
/* generate row and column data */
|
|
|
|
function ws_to_vte(ws) {
|
|
|
|
/* 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) => ({
|
|
|
|
field: String(i), // VTE will access row["0"], row["1"], etc
|
|
|
|
label: utils.encode_col(i), // the column labels will be A, B, etc
|
|
|
|
}));
|
|
|
|
|
|
|
|
return { rows, columns };
|
|
|
|
}
|
|
|
|
|
|
|
|
const rows = ref([]);
|
|
|
|
const columns = ref([]);
|
|
|
|
|
|
|
|
/* update refs */
|
|
|
|
function update_refs(ws) {
|
|
|
|
const data = ws_to_vte(ws);
|
|
|
|
rows.value = data.rows;
|
|
|
|
columns.value = data.columns;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
In the other direction, a worksheet can be generated with `aoa_to_sheet`:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { utils } from 'xlsx';
|
|
|
|
|
|
|
|
const rows = ref([]);
|
|
|
|
|
|
|
|
function vte_to_ws(rows) {
|
|
|
|
return utils.aoa_to_sheet(rows.value);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
#### VueJS Demo
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
<details><summary><b>Complete Example</b> (click to show)</summary>
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
1) Create a new ViteJS App using the VueJS + TypeScript template:
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
npm create vite@latest sheetjs-vue -- --template vue-ts
|
|
|
|
cd sheetjs-vue
|
|
|
|
```
|
|
|
|
|
|
|
|
2) Install dependencies:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npm i
|
|
|
|
npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz vue3-table-lite
|
|
|
|
```
|
|
|
|
|
|
|
|
3) Download [`src/App.vue`](pathname:///vtl/App.vue) and replace the contents:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd src
|
|
|
|
rm -f App.vue
|
|
|
|
curl -LO https://docs.sheetjs.com/vtl/App.vue
|
|
|
|
cd ..
|
|
|
|
```
|
|
|
|
|
2022-08-25 08:22:28 +00:00
|
|
|
4) run `npm run dev`. When you load the page in the browser, it will try to
|
|
|
|
fetch <https://sheetjs.com/pres.numbers> and load the data.
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
</details>
|
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`:
|
|
|
|
|
|
|
|
```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>
|