2022-08-01 05:34:23 +00:00
|
|
|
|
---
|
|
|
|
|
sidebar_position: 14
|
|
|
|
|
title: Data Grids and UI
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
it is easier to munge the output of an array of arrays.
|
|
|
|
|
|
|
|
|
|
|
2022-08-19 02:26:17 +00:00
|
|
|
|
### Tabulator
|
|
|
|
|
|
|
|
|
|
[Tabulator](http://tabulator.info/docs/5.3/download#xlsx) includes deep support
|
|
|
|
|
through a special Export button. It handles the SheetJS-related operations.
|
|
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
|
|
|
|
### 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
|
|
|
|
|
includes a simple directive for a HTML File Input control. It also includes a
|
|
|
|
|
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,
|
|
|
|
|
the columns must be objects whose `key` property is the stringified number:
|
|
|
|
|
|
|
|
|
|
```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`:
|
|
|
|
|
|
|
|
|
|
```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);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### RDG Demo
|
|
|
|
|
|
|
|
|
|
<details><summary><b>Complete Example</b> (click to show)</summary>
|
|
|
|
|
|
|
|
|
|
1) Create a new TypeScript CRA app:
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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-specific 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>
|
|
|
|
|
</>)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
4) run `npm start`. When you load the dev 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)
|
2022-08-18 08:41:34 +00:00
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
|
|
### vue3-table-lite
|
|
|
|
|
|
|
|
|
|
:::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
|
|
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
[`vue3-table-lite`](https://vue3-lite-table.vercel.app/) is a data grid built
|
|
|
|
|
for Vue
|
|
|
|
|
|
|
|
|
|
[A complete example is included below.](#vte-demo)
|
|
|
|
|
|
|
|
|
|
#### Rows and Columns Bindings
|
|
|
|
|
|
|
|
|
|
`vue3-table-lite` presents two bindable attributes: an array of column metadata
|
|
|
|
|
(`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>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
These can be mutated through the `value` property in Vue lifecycle methods:
|
|
|
|
|
|
|
|
|
|
```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,
|
|
|
|
|
the columns must be objects whose `field` property is the stringified number:
|
|
|
|
|
|
|
|
|
|
```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);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### VTE Demo
|
|
|
|
|
|
|
|
|
|
<details><summary><b>Complete Example</b> (click to show)</summary>
|
|
|
|
|
|
|
|
|
|
1) Create a new ViteJS App using the Vue + TypeScript template:
|
|
|
|
|
|
|
|
|
|
```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 ..
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
4) run `npm run dev`. When you load the dev page in the browser, it will try
|
|
|
|
|
to fetch <https://sheetjs.com/pres.numbers> and load the data.
|
|
|
|
|
|
|
|
|
|
</details>
|