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: | ||||
| 
 | ||||
|  | ||||
|  | ||||
							
								
								
									
										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: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 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) | ||||
| })); | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## 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