forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			158 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
		
		
			
		
	
	
			158 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
|  | <!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> |