| 
									
										
										
										
											2017-09-12 20:02:06 +00:00
										 |  |  | /* xlsx.js (C) 2013-present  SheetJS -- http://sheetjs.com */ | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | /* 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 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | class SheetJSApp extends React.Component { | 
					
						
							|  |  |  | 	constructor(props) { | 
					
						
							|  |  |  | 		super(props); | 
					
						
							|  |  |  | 		this.state = { | 
					
						
							|  |  |  | 			data: [], /* Array of Arrays e.g. [["a","b"],[1,2]] */ | 
					
						
							|  |  |  | 			cols: []  /* Array of column objects e.g. { name: "C", K: 2 } */ | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		this.handleFile = this.handleFile.bind(this); | 
					
						
							|  |  |  | 		this.exportFile = this.exportFile.bind(this); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	handleFile(file/*:File*/) { | 
					
						
							|  |  |  | 		/* Boilerplate to set up FileReader */ | 
					
						
							|  |  |  | 		const reader = new FileReader(); | 
					
						
							| 
									
										
										
										
											2018-02-03 20:46:32 +00:00
										 |  |  | 		const rABS = !!reader.readAsBinaryString; | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | 		reader.onload = (e) => { | 
					
						
							|  |  |  | 			/* Parse data */ | 
					
						
							|  |  |  | 			const bstr = e.target.result; | 
					
						
							| 
									
										
										
										
											2018-02-03 20:46:32 +00:00
										 |  |  | 			const wb = XLSX.read(bstr, {type:rABS ? 'binary' : 'array'}); | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | 			/* Get first worksheet */ | 
					
						
							|  |  |  | 			const wsname = wb.SheetNames[0]; | 
					
						
							|  |  |  | 			const ws = wb.Sheets[wsname]; | 
					
						
							|  |  |  | 			/* Convert array of arrays */ | 
					
						
							|  |  |  | 			const data = XLSX.utils.sheet_to_json(ws, {header:1}); | 
					
						
							|  |  |  | 			/* Update state */ | 
					
						
							|  |  |  | 			this.setState({ data: data, cols: make_cols(ws['!ref']) }); | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2018-02-03 20:46:32 +00:00
										 |  |  | 		if(rABS) reader.readAsBinaryString(file); else reader.readAsArrayBuffer(file); | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 	exportFile() { | 
					
						
							|  |  |  | 		/* convert state to workbook */ | 
					
						
							|  |  |  | 		const ws = XLSX.utils.aoa_to_sheet(this.state.data); | 
					
						
							|  |  |  | 		const wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  | 		XLSX.utils.book_append_sheet(wb, ws, "SheetJS"); | 
					
						
							| 
									
										
										
										
											2018-02-03 20:46:32 +00:00
										 |  |  | 		/* generate XLSX file and send to client */ | 
					
						
							|  |  |  | 		XLSX.writeFile(wb, "sheetjs.xlsx") | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 	render() { return ( | 
					
						
							|  |  |  | <DragDropFile handleFile={this.handleFile}> | 
					
						
							|  |  |  | 	<div className="row"><div className="col-xs-12"> | 
					
						
							|  |  |  | 		<DataInput handleFile={this.handleFile} /> | 
					
						
							|  |  |  | 	</div></div> | 
					
						
							|  |  |  | 	<div className="row"><div className="col-xs-12"> | 
					
						
							|  |  |  | 		<button disabled={!this.state.data.length} className="btn btn-success" onClick={this.exportFile}>Export</button> | 
					
						
							|  |  |  | 	</div></div> | 
					
						
							|  |  |  | 	<div className="row"><div className="col-xs-12"> | 
					
						
							|  |  |  | 		<OutTable data={this.state.data} cols={this.state.cols} /> | 
					
						
							|  |  |  | 	</div></div> | 
					
						
							|  |  |  | </DragDropFile> | 
					
						
							|  |  |  | ); }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if(typeof module !== 'undefined') module.exports = SheetJSApp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							| 
									
										
										
										
											2017-09-12 20:02:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |   Simple HTML5 file drag-and-drop wrapper | 
					
						
							|  |  |  |   usage: <DragDropFile handleFile={handleFile}>...</DragDropFile> | 
					
						
							|  |  |  |     handleFile(file:File):void; | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | class DragDropFile extends React.Component { | 
					
						
							|  |  |  | 	constructor(props) { | 
					
						
							|  |  |  | 		super(props); | 
					
						
							|  |  |  | 		this.onDrop = this.onDrop.bind(this); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	suppress(evt) { evt.stopPropagation(); evt.preventDefault(); }; | 
					
						
							|  |  |  | 	onDrop(evt) { evt.stopPropagation(); evt.preventDefault(); | 
					
						
							|  |  |  | 		const files = evt.dataTransfer.files; | 
					
						
							|  |  |  | 		if(files && files[0]) this.props.handleFile(files[0]); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	render() { return ( | 
					
						
							|  |  |  | <div onDrop={this.onDrop} onDragEnter={this.suppress} onDragOver={this.suppress}> | 
					
						
							|  |  |  | 	{this.props.children} | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  | 	); }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |   Simple HTML5 file input wrapper | 
					
						
							|  |  |  |   usage: <DataInput handleFile={callback} /> | 
					
						
							|  |  |  |     handleFile(file:File):void; | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | class DataInput extends React.Component { | 
					
						
							|  |  |  | 	constructor(props) { | 
					
						
							|  |  |  | 		super(props); | 
					
						
							|  |  |  | 		this.handleChange = this.handleChange.bind(this); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	handleChange(e) { | 
					
						
							|  |  |  | 		const files = e.target.files; | 
					
						
							|  |  |  | 		if(files && files[0]) this.props.handleFile(files[0]); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	render() { return ( | 
					
						
							|  |  |  | <form className="form-inline"> | 
					
						
							|  |  |  | 	<div className="form-group"> | 
					
						
							|  |  |  | 		<label htmlFor="file">Spreadsheet</label> | 
					
						
							|  |  |  | 		<input type="file" className="form-control" id="file" accept={SheetJSFT} onChange={this.handleChange} /> | 
					
						
							|  |  |  | 	</div> | 
					
						
							|  |  |  | </form> | 
					
						
							|  |  |  | 	); }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |   Simple HTML Table | 
					
						
							|  |  |  |   usage: <OutTable data={data} cols={cols} /> | 
					
						
							|  |  |  |     data:Array<Array<any> >; | 
					
						
							|  |  |  |     cols:Array<{name:string, key:number|string}>; | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | class OutTable extends React.Component { | 
					
						
							|  |  |  | 	constructor(props) { super(props); }; | 
					
						
							|  |  |  | 	render() { return ( | 
					
						
							|  |  |  | <div className="table-responsive"> | 
					
						
							|  |  |  | 	<table className="table table-striped"> | 
					
						
							|  |  |  | 		<thead> | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | 			<tr>{this.props.cols.map((c) => <th key={c.key}>{c.name}</th>)}</tr> | 
					
						
							| 
									
										
										
										
											2017-09-12 20:02:06 +00:00
										 |  |  | 		</thead> | 
					
						
							|  |  |  | 		<tbody> | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | 			{this.props.data.map((r,i) => <tr key={i}> | 
					
						
							| 
									
										
										
										
											2017-09-12 20:02:06 +00:00
										 |  |  | 				{this.props.cols.map(c => <td key={c.key}>{ r[c.key] }</td>)} | 
					
						
							|  |  |  | 			</tr>)} | 
					
						
							|  |  |  | 		</tbody> | 
					
						
							|  |  |  | 	</table> | 
					
						
							|  |  |  | </div> | 
					
						
							|  |  |  | 	); }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-24 23:40:09 +00:00
										 |  |  | /* 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(function(x) { return "." + x; }).join(","); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* generate an array of column objects */ | 
					
						
							| 
									
										
										
										
											2018-02-03 20:46:32 +00:00
										 |  |  | const make_cols = refstr => { | 
					
						
							|  |  |  | 	let o = [], C = XLSX.utils.decode_range(refstr).e.c + 1; | 
					
						
							|  |  |  | 	for(var i = 0; i < C; ++i) o[i] = {name:XLSX.utils.encode_col(i), key:i} | 
					
						
							|  |  |  | 	return o; | 
					
						
							|  |  |  | }; |