forked from sheetjs/sheetjs
SheetJS
f277ebe140
- HTML whitespace (fixes #849 h/t @laurentauthier) - XLML number format write (fixes #904 h/t @james-boswell) - XLAM/XLA write - 'array' write type - XLSX/XLSB/XLS/XLML RTL read/write - XLS merge cells write - demo and typing refresh
141 lines
4.5 KiB
JavaScript
141 lines
4.5 KiB
JavaScript
/* xlsx.js (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
|
|
*/
|
|
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();
|
|
reader.onload = (e) => {
|
|
/* Parse data */
|
|
const bstr = e.target.result;
|
|
const wb = XLSX.read(bstr, {type:'binary'});
|
|
/* 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']) });
|
|
};
|
|
reader.readAsBinaryString(file);
|
|
};
|
|
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");
|
|
/* generate XLSX file */
|
|
const wbout = XLSX.write(wb, {type:"array", bookType:"xlsx"});
|
|
/* send to client */
|
|
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "sheetjs.xlsx");
|
|
};
|
|
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
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/*
|
|
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>
|
|
<tr>{this.props.cols.map((c) => <th key={c.key}>{c.name}</th>)}</tr>
|
|
</thead>
|
|
<tbody>
|
|
{this.props.data.map((r,i) => <tr key={i}>
|
|
{this.props.cols.map(c => <td key={c.key}>{ r[c.key] }</td>)}
|
|
</tr>)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
); };
|
|
};
|
|
|
|
/* 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 */
|
|
const make_cols = refstr => Array(XLSX.utils.decode_range(refstr).e.c + 1).fill(0).map((x,i) => ({name:XLSX.utils.encode_col(i), key:i}));
|