|
| 1 | +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ |
| 2 | +/* Notes: |
| 3 | + - usage: `ReactDOM.render( <SheetJSApp />, document.getElementById('app') );` |
| 4 | + - xlsx.full.min.js is loaded in the head of the HTML page |
| 5 | + - this script should be referenced with type="text/babel" |
| 6 | + - babel.js in-browser transpiler should be loaded before this script |
| 7 | +*/ |
| 8 | +function SheetJSApp() { |
| 9 | + const [data, setData] = React.useState([]); |
| 10 | + const [cols, setCols] = React.useState([]); |
| 11 | + |
| 12 | + const handleFile = (file) => { |
| 13 | + const reader = new FileReader(); |
| 14 | + const rABS = !!reader.readAsBinaryString; |
| 15 | + reader.onload = (e) => { |
| 16 | + /* Parse data */ |
| 17 | + const bstr = e.target.result; |
| 18 | + const wb = XLSX.read(bstr, {type:rABS ? 'binary' : 'array'}); |
| 19 | + /* Get first worksheet */ |
| 20 | + const wsname = wb.SheetNames[0]; |
| 21 | + const ws = wb.Sheets[wsname]; |
| 22 | + /* Convert array of arrays */ |
| 23 | + const data = XLSX.utils.sheet_to_json(ws, {header:1}); |
| 24 | + /* Update state */ |
| 25 | + setData(data); |
| 26 | + setCols(make_cols(ws['!ref'])) |
| 27 | + }; |
| 28 | + if(rABS) reader.readAsBinaryString(file); else reader.readAsArrayBuffer(file); |
| 29 | + } |
| 30 | + |
| 31 | + const exportFile = () => { |
| 32 | + /* convert state to workbook */ |
| 33 | + const ws = XLSX.utils.aoa_to_sheet(data); |
| 34 | + const wb = XLSX.utils.book_new(); |
| 35 | + XLSX.utils.book_append_sheet(wb, ws, "SheetJS"); |
| 36 | + /* generate XLSX file and send to client */ |
| 37 | + XLSX.writeFile(wb, "sheetjs.xlsx") |
| 38 | + }; |
| 39 | + |
| 40 | + return ( |
| 41 | + <DragDropFile handleFile={handleFile}> |
| 42 | + <div className="row"><div className="col-xs-12"> |
| 43 | + <DataInput handleFile={handleFile} /> |
| 44 | + </div></div> |
| 45 | + <div className="row"><div className="col-xs-12"> |
| 46 | + <button disabled={!data.length} className="btn btn-success" onClick={exportFile}>Export</button> |
| 47 | + </div></div> |
| 48 | + <div className="row"><div className="col-xs-12"> |
| 49 | + <OutTable data={data} cols={cols} /> |
| 50 | + </div></div> |
| 51 | + </DragDropFile> |
| 52 | + ); |
| 53 | +} |
| 54 | + |
| 55 | +if(typeof module !== 'undefined') module.exports = SheetJSApp |
| 56 | + |
| 57 | +/* -------------------------------------------------------------------------- */ |
| 58 | + |
| 59 | +/* |
| 60 | + Simple HTML5 file drag-and-drop wrapper |
| 61 | + usage: <DragDropFile handleFile={handleFile}>...</DragDropFile> |
| 62 | + handleFile(file:File):void; |
| 63 | +*/ |
| 64 | + |
| 65 | +function DragDropFile({ handleFile, children }) { |
| 66 | + const suppress = (e) => { e.stopPropagation(); e.preventDefault(); }; |
| 67 | + const handleDrop = (e) => { e.stopPropagation(); e.preventDefault(); |
| 68 | + const files = e.dataTransfer.files; |
| 69 | + if(files && files[0]) handleFile(files[0]); |
| 70 | + }; |
| 71 | + |
| 72 | + return ( |
| 73 | + <div |
| 74 | + onDrop={handleDrop} |
| 75 | + onDragEnter={suppress} |
| 76 | + onDragOver={suppress} |
| 77 | + > |
| 78 | + {children} |
| 79 | + </div> |
| 80 | + ); |
| 81 | +} |
| 82 | + |
| 83 | +/* |
| 84 | + Simple HTML5 file input wrapper |
| 85 | + usage: <DataInput handleFile={callback} /> |
| 86 | + handleFile(file:File):void; |
| 87 | +*/ |
| 88 | + |
| 89 | +function DataInput({ handleFile }) { |
| 90 | + const handleChange = (e) => { |
| 91 | + const files = e.target.files; |
| 92 | + if(files && files[0]) handleFile(files[0]); |
| 93 | + }; |
| 94 | + |
| 95 | + return ( |
| 96 | + <form className="form-inline"> |
| 97 | + <div className="form-group"> |
| 98 | + <label htmlFor="file">Drag or choose a spreadsheet file</label> |
| 99 | + <br /> |
| 100 | + <input |
| 101 | + type="file" |
| 102 | + className="form-control" |
| 103 | + id="file" |
| 104 | + accept={SheetJSFT} |
| 105 | + onChange={handleChange} |
| 106 | + /> |
| 107 | + </div> |
| 108 | + </form> |
| 109 | + ) |
| 110 | +} |
| 111 | + |
| 112 | +/* |
| 113 | + Simple HTML Table |
| 114 | + usage: <OutTable data={data} cols={cols} /> |
| 115 | + data:Array<Array<any> >; |
| 116 | + cols:Array<{name:string, key:number|string}>; |
| 117 | +*/ |
| 118 | +function OutTable({ data, cols }) { |
| 119 | + return ( |
| 120 | + <div className="table-responsive"> |
| 121 | + <table className="table table-striped"> |
| 122 | + <thead> |
| 123 | + <tr>{cols.map((c) => <th key={c.key}>{c.name}</th>)}</tr> |
| 124 | + </thead> |
| 125 | + <tbody> |
| 126 | + {data.map((r,i) => <tr key={i}> |
| 127 | + {cols.map(c => <td key={c.key}>{ r[c.key] }</td>)} |
| 128 | + </tr>)} |
| 129 | + </tbody> |
| 130 | + </table> |
| 131 | + </div> |
| 132 | + ); |
| 133 | +} |
| 134 | + |
| 135 | +/* list of supported file types */ |
| 136 | +const SheetJSFT = [ |
| 137 | + "xlsx", "xlsb", "xlsm", "xls", "xml", "csv", "txt", "ods", "fods", "uos", "sylk", "dif", "dbf", "prn", "qpw", "123", "wb*", "wq*", "html", "htm" |
| 138 | +].map(x => `.${x}`).join(","); |
| 139 | + |
| 140 | +/* generate an array of column objects */ |
| 141 | +const make_cols = refstr => { |
| 142 | + let o = [], C = XLSX.utils.decode_range(refstr).e.c + 1; |
| 143 | + for(var i = 0; i < C; ++i) o[i] = {name:XLSX.utils.encode_col(i), key:i} |
| 144 | + return o; |
| 145 | +}; |
0 commit comments