--- title: Glide Datagrid pagination_prev: demos/frontend/index pagination_next: demos/net/index --- import current from '/version.js'; import CodeBlock from '@theme/CodeBlock'; :::note This demo was last tested on 2023 February 07 with the ViteJS+React+TypeScript starter (Vite `4.1.1`, React `18.2.0`) and `@glideapps/glide-data-grid@5.2.1`. ::: ## Integration Details #### Backing Store Under the hood, the `DataEditor` component is designed to call methods and request data to display in the grid. It is typical to store data *outside* of component state. A `getCellContent` callback will pull data from the external backing store, while SheetJS operations will directly act on the store: ```js // !! THESE OBJECTS ARE DEFINED OUTSIDE OF THE COMPONENT FUNCTION !! // this will store the raw data objects let data: any[] = []; // this will store the header names let header: string[] = []; ``` #### Props :::note This is a high-level overview. The official documentation should be consulted. ::: _Columns_ `DataEditor` expects column metadata to be passed through a `columns` prop. This should be managed in the component state: ```js import { useState } from 'react'; import { DataEditor, GridColumn } from '@glideapps/glide-data-grid'; function App() { // highlight-next-line const [cols, setCols] = useState([]); // gdg column objects // ... return ( <> // ... // ... ); } export default App; ``` Each `GridColumn` object expects a `title` representing the display name and an `id` representing the key to index within the data object. _Data_ The `DataEditor` component expects a `getCellContent` callback for supplying data. The callback accepts column and row indices. The column index should be used to find the header key: ```js import { useCallback } from 'react'; import { DataEditor, GridCellKind, GridCell, Item } from '@glideapps/glide-data-grid'; // ... function App() { // ... // backing data store -> gdg // highlight-start const getContent = useCallback((cell: Item): GridCell => { const [col, row] = cell; return { kind: GridCellKind.Text, // header[col] is the name of the field displayData: String(data[row]?.[header[col]]??""), data: data[row]?.[header[col]], }; }, []); // highlight-end // ... return ( <> // ... // ... ); } ``` _Row Count_ `DataEditor` also accepts a `rows` property indicating the number of rows. This is best managed in state: ```js import { useState } from 'react'; import { DataEditor } from '@glideapps/glide-data-grid'; function App() { // highlight-next-line const [rows, setRows] = useState(0); // number of rows // ... return ( <> // ... // ... ); } export default App; ``` _Editing Data_ The demo uses the `onCellEdited` callback to write back to the data store. ### Parsing Data _SheetJS to Data Store_ The raw data objects are readily generated with `sheet_to_json`. The headers can be pulled by extracting the first row of the worksheet: ```js import { utils, WorkBook } from 'xlsx'; // ... const update_backing_store = (wb: WorkBook) => { // get first worksheet const sheet = wb.Sheets[wb.SheetNames[0]]; // set data // highlight-next-line data = utils.sheet_to_json(sheet); // create a range consisting of the first row const range = utils.decode_range(sheet["!ref"]??"A1"); // original range range.e.r = range.s.r; // set ending row to starting row (select first row) // pull headers // highlight-next-line header = utils.sheet_to_json(sheet, {header: 1, range})[0]; }; // ... ``` _Importing from Data Store_ Scheduling a refresh for the `DataEditor` involves updating the grid column metadata and row count through the standard state. It also requires a special `updateCells` call to instruct the grid to mark the cached data as stale: ```js import { useRef } from 'react' import { WorkBook } from 'xlsx' import { DataEditor, GridColumn, Item, DataEditorRef } from '@glideapps/glide-data-grid' function App() { const ref = useRef(null); // gdg ref // ... const parse_wb = (wb: WorkBook) => { update_backing_store(wb); // highlight-start // update column metadata by pulling from external header keys setCols(header.map(h => ({title: h, id: h} as GridColumn))); // update number of rows setRows(data.length); if(data.length > 0) { // create an array of the cells that must be updated let cells = data.map( (_,R) => Array.from({length:header.length}, (_,C) => ({cell: ([C,R] as Item)})) ).flat(); // initiate update using the `ref` attached to the DataEditor ref.current?.updateCells(cells) } // highlight-end }; // ... return ( <> // ... // ... ); } export default App; ``` ### Writing Data `json_to_sheet` works directly on the `data` array: ```js const ws = utils.json_to_sheet(data); // easy :) ``` Since the editor can change the header titles, it is strongly recommended to pull column data from the state and rewrite the header row: ```js import { utils, writeFileXLSX } from 'xlsx'; function App() { // ... const exportXLSX = useCallback(() => { // highlight-start // 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"}); // highlight-end // 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 ( <> // ... // highlight-next-line // ... ); } export default App; ``` ## Demo 1) Create a new project: ```bash npm create vite@latest -- sheetjs-gdg --template react-ts cd sheetjs-gdg npm i ``` Install SheetJS and Glide Data Grid required dependencies: {`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.2.1`} Start dev server: ```bash npm run dev ``` The terminal window will display a URL (typically `http://localhost:5173`). Open the URL with a web browser. 2) Download [`App.tsx`](pathname:///gdg/App.tsx) and replace `src/App.tsx`: ```bash curl -L -o src/App.tsx https://docs.sheetjs.com/gdg/App.tsx ``` Refresh the browser window and a grid should be displayed: ![glide-data-grid initial view](pathname:///gdg/pre.png) 3) To test the export functionality, make some changes to the grid data. Suppose you believe that President Grover Cleveland should be counted once. That would imply President Clinton should be index 41 and the indices of the other presidents should be decremented. By double-clicking on each cell in the Index column, a cell editor should appear. Decrement each index: ![glide-data-grid after edits](pathname:///gdg/post.png) Click on the "Export" button to create a file! Open the file and verify.