forked from sheetjs/docs.sheetjs.com
		
	
		
			
				
	
	
		
			112 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { useEffect, useState, ChangeEvent } from "react";
 | ||
| import { DataGrid, GridColDef } from "@mui/x-data-grid";
 | ||
| import { read, utils, WorkSheet, writeFile } from "xlsx";
 | ||
| 
 | ||
| import './App.css';
 | ||
| 
 | ||
| type DataSet = { [index: string]: WorkSheet; };
 | ||
| type Row = any[];
 | ||
| type RowCol = { rows: Row[]; columns: GridColDef[]; };
 | ||
| 
 | ||
| function arrayify(rows: any[]): Row[] {
 | ||
|   return rows.map(row => {
 | ||
|     if(Array.isArray(row)) return row;
 | ||
|     var length = Object.keys(row).length;
 | ||
|     for(; length > 0; --length) if(row[length-1] != null) break;
 | ||
|     return Array.from({length, ...row});
 | ||
|   });
 | ||
| }
 | ||
| 
 | ||
| /* 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}).map((r,id) => ({...r, id})),
 | ||
|   columns: Array.from({
 | ||
|     length: utils.decode_range(data[sheetName]["!ref"]||"A1").e.c + 1
 | ||
|   }, (_, i) => ({ field: String(i), headerName: utils.encode_col(i), editable: true }))
 | ||
| });
 | ||
| 
 | ||
| export default function App() {
 | ||
|   const [rows, setRows] = useState<Row[]>([]); // data rows
 | ||
|   const [columns, setColumns] = useState<GridColDef[]>([]); // 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) {
 | ||
|     /* update workbook cache in case the current worksheet was changed */
 | ||
|     workBook[current] = utils.aoa_to_sheet(arrayify(rows));
 | ||
| 
 | ||
|     /* 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> {
 | ||
|     /* read file data */
 | ||
|     const data = read(file);
 | ||
| 
 | ||
|     /* 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 {
 | ||
|     console.log(rows);
 | ||
|     /* update current worksheet in case changes were made */
 | ||
|     workBook[current] = utils.aoa_to_sheet(arrayify(rows));
 | ||
| 
 | ||
|     /* construct workbook and loop through worksheets */
 | ||
|     const wb = utils.book_new();
 | ||
|     sheets.forEach((n) => { utils.book_append_sheet(wb, workBook[n], n); });
 | ||
| 
 | ||
|     /* generate a file and download it */
 | ||
|     writeFile(wb, "SheetJSMUIDG." + ext);
 | ||
|   }
 | ||
| 
 | ||
|   return (
 | ||
|     <>
 | ||
|       <h3>SheetJS × MUI 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>
 | ||
|         <div style={{width:"100%", height:400}}>
 | ||
|           <DataGrid columns={columns} rows={rows} experimentalFeatures={{ newEditingApi: true }} />
 | ||
|         </div>
 | ||
|         <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>
 | ||
|       </> )}
 | ||
|     </>
 | ||
|   );
 | ||
| }
 |