forked from sheetjs/docs.sheetjs.com
react
This commit is contained in:
parent
99dd5c8834
commit
d4a38231dd
@ -151,3 +151,218 @@ a download. By setting the `filename` and `sheetname` options in the `ui-grid`
|
||||
options, the output can be controlled.
|
||||
|
||||
</details>
|
||||
|
||||
## 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[];
|
||||
|
||||
function ws_to_rdg(rows: Row[]): WorkSheet {
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
||||
The following screenshot was taken from the demo:
|
||||
|
||||
![react-data-grid screenshot](pathname:///react/rdg1.png)
|
||||
|
179
docz/docs/03-demos/21-react.md
Normal file
179
docz/docs/03-demos/21-react.md
Normal file
@ -0,0 +1,179 @@
|
||||
---
|
||||
sidebar_position: 20
|
||||
title: ReactJS
|
||||
---
|
||||
|
||||
[ReactJS](https://reactjs.org/) is a JS library for building user interfaces.
|
||||
|
||||
This demo tries to cover common React data flow ideas and strategies. React
|
||||
familiarity is assumed.
|
||||
|
||||
Other demos cover general React deployments, including:
|
||||
|
||||
- [Static Site Generation powered by NextJS](./content#nextjs)
|
||||
- [iOS and Android applications powered by React Native](./mobile#react-native)
|
||||
- [React Data Grid UI component](./grid#react-data-grid)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
[The "Frameworks" section](../getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
The library can be imported directly from JS or JSX code with:
|
||||
|
||||
```js
|
||||
import { read, utils, writeFile } from 'xlsx';
|
||||
```
|
||||
|
||||
|
||||
## Internal State
|
||||
|
||||
The various SheetJS APIs work with various data shapes. The preferred state
|
||||
depends on the application.
|
||||
|
||||
### Array of Objects
|
||||
|
||||
Typically, some users will create a spreadsheet with source data that should be
|
||||
loaded into the site. This sheet will have known columns. For example, our
|
||||
[presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" / "Index" columns:
|
||||
|
||||
![`pres.xlsx` data](pathname:///react/pres.png)
|
||||
|
||||
This naturally maps to an array of typed objects, as in the TS example below:
|
||||
|
||||
```ts
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
interface President {
|
||||
Name: string;
|
||||
Index: number;
|
||||
}
|
||||
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f);
|
||||
const data = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
`data` will be an array of objects:
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 },
|
||||
{ Name: "Joseph Biden", Index: 46 }
|
||||
]
|
||||
```
|
||||
|
||||
A component will typically map over the data. The following example generates
|
||||
a TABLE with a row for each President:
|
||||
|
||||
```tsx title="src/SheetJSReactAoO.tsx"
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
interface President { Name: string; Index: number; }
|
||||
|
||||
export default function SheetJSReactAoO() {
|
||||
/* the component state is an array of presidents */
|
||||
const [pres, setPres] = useState<President[]>([]);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
// highlight-start
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_json<President>(ws); // generate objects
|
||||
setPres(data); // update state
|
||||
// highlight-end
|
||||
})(); }, []);
|
||||
|
||||
return (<table><thead><th>Name</th><th>Index</th></thead><tbody>
|
||||
{ /* generate row for each president */
|
||||
// highlight-start
|
||||
pres.map(pres => (<tr>
|
||||
<td>{pres.Name}</td>
|
||||
<td>{pres.Index}</td>
|
||||
</tr>))
|
||||
// highlight-end
|
||||
}
|
||||
</tbody></table>);
|
||||
}
|
||||
```
|
||||
|
||||
### HTML
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The `sheet_to_html` function generates HTML that is aware of merges and other
|
||||
worksheet features. React `dangerouslySetInnerHTML` attribute allows code to
|
||||
set the `innerHTML` attribute, effectively inserting the code into the page:
|
||||
|
||||
```tsx title="src/SheetJSReactHTML.tsx"
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactHTML() {
|
||||
/* the component state is an HTML string */
|
||||
const [html, setHtml] = useState<string>("");
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
// highlight-start
|
||||
const data = utils.sheet_to_html(ws); // generate HTML
|
||||
setHtml(data); // update state
|
||||
// highlight-end
|
||||
})(); }, []);
|
||||
|
||||
// highlight-next-line
|
||||
return ( <div dangerouslySetInnerHTML={{ __html: html }} />);
|
||||
}
|
||||
```
|
||||
|
||||
### Rows and Columns
|
||||
|
||||
Some data grids and UI components split worksheet state in two parts: an array
|
||||
of column attribute objects and an array of row objects. The former is used to
|
||||
generate column headings and for indexing into the row objects.
|
||||
|
||||
The safest approach is to use an array of arrays for state and to generate
|
||||
column objects that map to A1-style column headers.
|
||||
|
||||
The [React Data Grid demo](./grid#rows-and-columns-state) uses this approach
|
||||
with the following column and row structure:
|
||||
|
||||
```js
|
||||
/* rows are generated with a simple array of arrays */
|
||||
const rows = utils.sheet_to_json(worksheet, { header: 1 });
|
||||
|
||||
/* column objects are generated based on the worksheet range */
|
||||
const range = utils.decode_range(ws["!ref"]||"A1");
|
||||
const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
|
||||
/* for an array of arrays, the keys are "0", "1", "2", ... */
|
||||
key: String(i),
|
||||
/* column labels: encode_col translates 0 -> "A", 1 -> "B", 2 -> "C", ... */
|
||||
name: XLSX.utils.encode_col(i)
|
||||
}));
|
||||
|
||||
```
|
||||
|
||||
![Column labels for headers](pathname:///react/cols.png)
|
||||
|
||||
|
||||
|
||||
## Legacy Deployments
|
||||
|
||||
[The Standalone Scripts](../getting-started/installation/standalone) play nice
|
||||
with legacy deployments that do not use a bundler.
|
||||
|
||||
[The legacy demo](pathname:///react/index.html) shows a simple React component
|
||||
transpiled in the browser using Babel standalone library.
|
@ -21,7 +21,7 @@ The demo projects include small runnable examples and short explainers.
|
||||
- [`Angular.JS`](./legacy#angularjs)
|
||||
- [`Angular 2+ and Ionic`](https://github.com/SheetJS/SheetJS/tree/master/demos/angular2/)
|
||||
- [`Knockout`](./legacy#knockout)
|
||||
- [`React and NextJS`](https://github.com/SheetJS/SheetJS/tree/master/demos/react/)
|
||||
- [`React`](./react)
|
||||
- [`VueJS`](https://github.com/SheetJS/SheetJS/tree/master/demos/vue/)
|
||||
|
||||
### Front-End UI Components
|
||||
@ -68,3 +68,9 @@ The demo projects include small runnable examples and short explainers.
|
||||
- [`vite`](./bundler#vite)
|
||||
- [`webpack`](./bundler#webpack)
|
||||
- [`wmr`](./bundler#wmr)
|
||||
|
||||
:::note
|
||||
|
||||
If a demo for a library or framework is not included here, please leave a note.
|
||||
|
||||
:::
|
||||
|
BIN
docz/static/react/cols.png
Normal file
BIN
docz/static/react/cols.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
158
docz/static/react/index.html
Normal file
158
docz/static/react/index.html
Normal file
@ -0,0 +1,158 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- sheetjs (C) 2013-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html lang="en" style="height: 100%">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>SheetJS React Demo</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
|
||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
||||
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
|
||||
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
|
||||
<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>
|
||||
<style>body, #app { height: 100%; }</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid"><h1><a href="http://sheetjs.com">SheetJS × React Demo</a></h1><br /></div>
|
||||
<div id="app" class="container-fluid"></div>
|
||||
<script type="text/babel">
|
||||
/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* Notes:
|
||||
- usage: `ReactDOM.render( <SheetJSApp />, document.getElementById('app') );`
|
||||
- xlsx.full.min.js is loaded in the head of the HTML page
|
||||
- this script should be referenced with type="text/babel"
|
||||
- babel.js in-browser transpiler should be loaded before this script
|
||||
*/
|
||||
const { read, writeFile } = XLSX;
|
||||
const { decode_range, encode_col, sheet_to_json, aoa_to_sheet, book_new, book_append_sheet } = XLSX.utils;
|
||||
|
||||
/* generate an array of column objects */
|
||||
const make_cols = refstr => Array.from({length: decode_range(refstr).e.c + 1}, (_, i) => ({ name: encode_col(i), key: i}));
|
||||
|
||||
/* main component */
|
||||
function SheetJSApp() {
|
||||
const [data, setData] = React.useState([]);
|
||||
const [cols, setCols] = React.useState([]);
|
||||
|
||||
const handleFile = (file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
/* Parse data */
|
||||
const ab = e.target.result;
|
||||
const wb = read(ab, { type: 'array' });
|
||||
/* Get first worksheet */
|
||||
const wsname = wb.SheetNames[0];
|
||||
const ws = wb.Sheets[wsname];
|
||||
/* Convert array of arrays */
|
||||
const data = sheet_to_json(ws, { header: 1 });
|
||||
/* Update state */
|
||||
setData(data);
|
||||
setCols(make_cols(ws['!ref']))
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
const exportFile = () => {
|
||||
/* convert state to workbook */
|
||||
const ws = aoa_to_sheet(data);
|
||||
const wb = book_new();
|
||||
book_append_sheet(wb, ws, "SheetJS");
|
||||
/* generate XLSX file and send to client */
|
||||
writeFile(wb, "sheetjs.xlsx")
|
||||
};
|
||||
|
||||
return (
|
||||
<DragDropFile handleFile={handleFile}>
|
||||
<div className="row"><div className="col-xs-12">
|
||||
<DataInput handleFile={handleFile} />
|
||||
</div></div>
|
||||
<div className="row"><div className="col-xs-12">
|
||||
{data.length ? <button className="btn btn-success" onClick={exportFile}>Export</button> : ""}
|
||||
</div></div>
|
||||
<div className="row"><div className="col-xs-12">
|
||||
<OutTable data={data} cols={cols} />
|
||||
</div></div>
|
||||
</DragDropFile>
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
Simple HTML5 file drag-and-drop wrapper
|
||||
usage: <DragDropFile handleFile={handleFile}>...</DragDropFile>
|
||||
handleFile(file:File):void;
|
||||
*/
|
||||
|
||||
function DragDropFile({ handleFile, children }) {
|
||||
const suppress = (e) => { e.stopPropagation(); e.preventDefault(); };
|
||||
const handleDrop = (e) => {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
const files = e.dataTransfer.files;
|
||||
if (files && files[0]) handleFile(files[0]);
|
||||
};
|
||||
|
||||
return ( <div onDrop={handleDrop} onDragEnter={suppress} onDragOver={suppress}>{children}</div> );
|
||||
}
|
||||
|
||||
/*
|
||||
Simple HTML5 file input wrapper
|
||||
usage: <DataInput handleFile={callback} />
|
||||
handleFile(file:File):void;
|
||||
*/
|
||||
|
||||
function DataInput({ handleFile }) {
|
||||
const handleChange = (e) => {
|
||||
const files = e.target.files;
|
||||
if (files && files[0]) handleFile(files[0]);
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="form-inline">
|
||||
<div className="form-group">
|
||||
<label htmlFor="file">Drag or choose a spreadsheet file</label><br />
|
||||
<input type="file" className="form-control" id="file" accept={SheetJSFT} onChange={handleChange} />
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
/* list of supported file types */
|
||||
const SheetJSFT = [
|
||||
"xlsx", "xlsb", "xlsm", "xls", "xml", "csv", "txt", "ods", "fods", "uos", "sylk", "dif", "dbf", "prn", "qpw", "123", "wb*", "wq*", "html", "htm"
|
||||
].map(x => `.${x}`).join(",");
|
||||
|
||||
/*
|
||||
Simple HTML Table
|
||||
usage: <OutTable data={data} cols={cols} />
|
||||
data:Array<Array<any> >;
|
||||
cols:Array<{name:string, key:number|string}>;
|
||||
*/
|
||||
function OutTable({ data, cols }) {
|
||||
return (
|
||||
<div className="table-responsive">
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>{cols.map((c) => <th key={c.key}>{c.name}</th>)}</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((r, i) => <tr key={i}>
|
||||
{cols.map(c => <td key={c.key}>{r[c.key]}</td>)}
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* React 18 uses ReactDOM.createRoot; < 18 should use ReactDOM.render */
|
||||
const root_elt = document.getElementById('app');
|
||||
if(typeof ReactDOM.createRoot !== "undefined") {
|
||||
const root = ReactDOM.createRoot(root_elt);
|
||||
root.render(<SheetJSApp/>);
|
||||
} else {
|
||||
ReactDOM.render(<SheetJSApp />, root_elt);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
BIN
docz/static/react/pres.png
Normal file
BIN
docz/static/react/pres.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
docz/static/react/rdg1.png
Normal file
BIN
docz/static/react/rdg1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
Loading…
Reference in New Issue
Block a user