Lattice Grid Buy a licence

demo D280

Bring rows in from a CSV in React

Paste a block or drop a file, each column typed and mapped onto your fields, shown as a preview so nothing lands until you confirm; a spreadsheet block copied from Excel pastes the same way

grid.import.preview() · apply()

This grid brings rows in from a pasted block or a dropped CSV file, typing each column and mapping it onto your fields, shown as a preview so nothing lands until you confirm. It turns a spreadsheet export into grid rows without a separate import tool, and a block copied from Excel pastes the same way.

Building…
Loading a live grid…

This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">

<div id="app"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.esm.min.js';
  import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/react.esm.min.js';

  const LatticeGrid = createLatticeGrid({ React, createGrid });

  const columns = [
    { field: 'sku', title: 'SKU', layout: { width: 110, pin: 'start' } },
    { field: 'name', title: 'Product', layout: { flex: 1, min: 160 } },
    { field: 'category', title: 'Category', filter: { type: 'set' } },
    { field: 'price', title: 'Price', type: 'number', format: { style: 'currency', currency: 'GBP' }, total: 'sum' },
    { field: 'stock', title: 'In stock', type: 'number', total: 'sum' },
  ];

  const rows = [/* the rows already in the grid: sku, name, category, price, stock */];

  const CSV = `sku,name,category,price,stock
B-201,Floor lamp,Lighting,72,24
C-300,Bookshelf,Furniture,180,9`;

  function App() {
    const [grid, setGrid] = React.useState(null);
    const [csv, setCsv] = React.useState(CSV);
    const [pending, setPending] = React.useState(null);
    const [note, setNote] = React.useState('');

    // preview() parses the text, infers each column's type and maps it onto
    // the grid's own fields, and reports the row count and any warnings, all
    // without changing the grid. A spreadsheet block copied from Excel reads
    // the same way.
    const preview = () => {
      const result = grid.import.preview(csv);
      setPending(result);
      setNote('Ready to add ' + result.rowCount + ' rows, ' + (result.warnings?.length ?? 0) + ' warnings.');
    };

    // Nothing lands until apply(), so a ragged or mismatched file is caught
    // first. mode: 'append' adds the rows; mode: 'replace' would swap the set.
    const confirm = () => {
      grid.import.apply(pending, { mode: 'append' });
      setPending(null);
    };

    return (
      <>
        <textarea rows={5} style={{ width: '100%', font: '12px ui-monospace, monospace' }}
          value={csv} onChange={(e) => setCsv(e.target.value)} />
        <div style={{ margin: '8px 0' }}>
          <button onClick={preview} disabled={!grid}>Preview</button>{' '}
          <button onClick={confirm} disabled={!pending || pending.rowCount === 0}>Confirm and add</button>{' '}
          <span>{note}</span>
        </div>
        {/* import: true turns on the DOM affordances too: a drop target on the
            grid, paste, and a cell-menu item, all running the same
            preview-then-confirm pipeline as the buttons above. The
            grid.import API is present either way. */}
        <LatticeGrid rowKey="id" import columns={columns} rows={rows} onGridReady={setGrid} style={{ height: '460px' }} />
      </>
    );
  }

  createRoot(document.getElementById('app')).render(<App />);
</script>