demo D217
A type of your own in React
A part number, AB-1234-X, in five functions: matches, parse, format, compare
dataTypes: { partNumber: { base, matches, parse, format, compare } }
This grid teaches the grid a part-number format of your own through five small functions: match, parse, format and compare. It shows how any domain value can become a first-class type that sorts, filters and edits correctly, not just a string that happens to look right.
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.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">
<div id="grid"></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.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.esm.min.js';
import createLatticeGrid from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/react.esm.min.js';
const LatticeGrid = createLatticeGrid({ React, createGrid });
// A part number, AB-1234-X: a family, a serial and a revision. A type is a
// few functions, registered under one name.
const PART = /^\s*([A-Za-z]{2})[-\s]?(\d{1,4})[-\s]?([A-Za-z])\s*$/;
const read = (v) => {
const m = PART.exec(String(v ?? ''));
return m ? { family: m[1].toUpperCase(), serial: +m[2], rev: m[3].toUpperCase() } : null;
};
const canonical = (v) => {
const p = read(v);
return p ? `${p.family}-${String(p.serial).padStart(4, '0')}-${p.rev}` : null;
};
const partNumber = {
base: 'text',
matches: (v) => read(v) != null, // claim the value when inferring
parse: ({ text }) => canonical(text) ?? text, // ab12x -> AB-0012-X
format: ({ value }) => canonical(value) ?? String(value ?? ''),
compare: (a, b) => { // sort on the serial as a number
const x = read(a), y = read(b);
if (!x || !y) return x ? -1 : y ? 1 : 0;
return x.family.localeCompare(y.family) || x.serial - y.serial || x.rev.localeCompare(y.rev);
},
};
const columns = [
{ field: 'part', title: 'Part number', type: 'partNumber', editor: 'text', layout: { width: 160, pin: 'start' } },
{ field: 'description', title: 'Description', layout: { width: 240 } },
{ field: 'qty', title: 'On hand', type: 'number', total: 'sum' },
{ field: 'price', title: 'Unit price', type: 'number',
format: { style: 'currency', currency: 'GBP' }, total: 'mean' },
];
const rows = [/* e.g. [{ id: 1, part: 'AB-1000-A', description: 'Bracket', qty: 240, price: 3.75 }, ...] */];
function App() {
return (
<LatticeGrid
rowKey="id"
dataTypes={{ partNumber }}
columns={columns}
rows={rows}
state={{ sort: [{ col: 'part', dir: 'asc' }] }}
style={{ height: '480px' }}
/>
);
}
createRoot(document.getElementById('grid')).render(<App />);
</script>