demo D280
Bring rows in from a CSV in ESM
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.
This is the ES module version. The grid is imported straight from a CDN as a module, the same configuration as the vanilla tab without a global script tag, so it drops into any bundler or a bare module script.
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">
<textarea id="csv" rows="5" style="width: 100%; font: 12px ui-monospace, monospace">sku,name,category,price,stock
B-201,Floor lamp,Lighting,72,24
C-300,Bookshelf,Furniture,180,9</textarea>
<div style="margin: 8px 0">
<button id="preview">Preview</button>
<button id="confirm" disabled>Confirm and add</button>
<!-- Only preview() writes here, so it carries its own placeholder until
the button is clicked. -->
<span id="note">Click "Preview" to check the text above before it lands.</span>
</div>
<div id="grid" style="height: 460px"></div>
<script type="module">
import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.esm.min.js';
const grid = createGrid(document.getElementById('grid'), {
rowKey: 'id',
// 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 below. The grid.import API is present either way.
import: true,
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' },
],
rows, // the rows already in the grid: sku, name, category, price, stock
});
let pending = null;
// 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.
document.getElementById('preview').addEventListener('click', () => {
pending = grid.import.preview(document.getElementById('csv').value);
document.getElementById('note').textContent =
'Ready to add ' + pending.rowCount + ' rows, ' + (pending.warnings?.length ?? 0) + ' warnings.';
document.getElementById('confirm').disabled = pending.rowCount === 0;
});
// Nothing lands until apply(), so a ragged or mismatched file is caught first.
// mode: 'append' adds the rows; mode: 'replace' would swap the set.
document.getElementById('confirm').addEventListener('click', () => {
grid.import.apply(pending, { mode: 'append' });
pending = null;
document.getElementById('confirm').disabled = true;
});
</script>