forked from sheetjs/docs.sheetjs.com
		
	
		
			
				
	
	
		
			83 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			83 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { useState, useCallback, useEffect, useRef, ChangeEvent } from 'react';
 | |
| import { utils, read, writeFileXLSX, WorkBook } from 'xlsx';
 | |
| import { DataEditor, GridCellKind, GridCell, GridColumn, Item, DataEditorRef, EditableGridCell } from '@glideapps/glide-data-grid';
 | |
| import "@glideapps/glide-data-grid/dist/index.css";
 | |
| 
 | |
| // this will store the raw data objects
 | |
| let data: any[] = [];
 | |
| // this will store the header names
 | |
| let header: string[] = [];
 | |
| 
 | |
| function App() {
 | |
|   const [cols, setCols] = useState<GridColumn[]>([]); // gdg column objects
 | |
|   const [rows, setRows] = useState<number>(0); // number of rows
 | |
|   const ref = useRef<DataEditorRef>(null); // gdg ref
 | |
| 
 | |
|   // read/write between gdg and the backing data store
 | |
|   const getContent = useCallback((cell: Item): GridCell => {
 | |
|     const [col, row] = cell;
 | |
|     return {
 | |
|       kind: GridCellKind.Text,
 | |
|       allowOverlay: true,
 | |
|       readonly: false,
 | |
|       displayData: String(data[row]?.[header[col]]??""),
 | |
|       data: data[row]?.[header[col]],
 | |
|     };
 | |
|   }, []);
 | |
|   const onCellEdited = useCallback((cell: Item, newValue: EditableGridCell) => {
 | |
|     const [ col, row ] = cell;
 | |
|     data[row][header[col]] = newValue.data;
 | |
|   }, []);
 | |
| 
 | |
|   // update the data store from a workbook object
 | |
|   const parse_wb = (wb: WorkBook) => {
 | |
|     const sheet = wb.Sheets[wb.SheetNames[0]];
 | |
|     data = utils.sheet_to_json<any>(sheet);
 | |
|     const range = utils.decode_range(sheet["!ref"]??"A1"); range.e.r = range.s.r;
 | |
|     header = utils.sheet_to_json<string[]>(sheet, {header: 1, range})[0];
 | |
|     setCols(header.map(h => ({title: h, id: h} as GridColumn)));
 | |
|     setRows(data.length);
 | |
|     if(data.length > 0) {
 | |
|       let cells = data.map(
 | |
|         (_,R) => Array.from({length:header.length}, (_,C) => ({cell: ([C,R] as Item)}))
 | |
|       ).flat();
 | |
|       ref.current?.updateCells(cells);
 | |
|     }
 | |
|   };
 | |
|   // file input element onchange event handler
 | |
|   const onChange = useCallback(async (e: ChangeEvent<HTMLInputElement>) => {
 | |
|     if(!e.target?.files) return;
 | |
|     parse_wb(read(await e.target.files[0].arrayBuffer()));
 | |
|   }, []);
 | |
|   // when the component loads, fetch and display a sample workbook
 | |
|   useEffect(() => {
 | |
|     (async() => {
 | |
|       parse_wb(read(await (await fetch("https://docs.sheetjs.com/pres.numbers")).arrayBuffer()));
 | |
|     })();
 | |
|   }, []);
 | |
| 
 | |
|   // export data
 | |
|   const exportXLSX = useCallback(() => {
 | |
|     // generate worksheet using data with the order specified in the columns array
 | |
|     const ws = utils.json_to_sheet(data, {header: cols.map(c => c.id ?? c.title)});
 | |
|     // rewrite header row with titles
 | |
|     utils.sheet_add_aoa(ws, [cols.map(c => c.title ?? c.id)], {origin: "A1"});
 | |
|     // create workbook
 | |
|     const wb = utils.book_new();
 | |
|     utils.book_append_sheet(wb, ws, "Export"); // replace with sheet name
 | |
|     // download file
 | |
|     writeFileXLSX(wb, "sheetjs-gdg.xlsx");
 | |
|   }, []);
 | |
| 
 | |
|   return ( <>
 | |
|     <input type="file" onChange={onChange} />
 | |
|     <button onClick={exportXLSX}><b>Export XLSX!</b></button>
 | |
|     <div className="App">
 | |
|       <DataEditor getCellContent={getContent} columns={cols} rows={rows} onCellEdited={onCellEdited} ref={ref}/>
 | |
|     </div>
 | |
|     <div id="portal"></div>
 | |
|     </>
 | |
|   )
 | |
| }
 | |
| export default App;
 |