Data grid
The database layer, Notion-style — a schema-driven, editable grid over a typed collection.
The data grid is the first piece of Ramson's database layer: the part of Notion that is just data. A collection is a schema (an ordered list of typed properties) plus rows (cell values keyed by property id), and the grid renders and edits that shape directly. Any internal tool that has records behind an API can hand them to this grid and get a Notion-style view with no bespoke table code.
This is front end only. There is no Postgres and no server yet. The collection below is mock data held in React state, and every edit rewrites that state, which is the same read/write shape a real backend would serve later.
| Project | Status | Priority | Tags | Owner | Due | Effort | On track | Brief | ||
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Ramson UI kit — data layer | In progress | JSJoe | Sep 5, 2026 | 8 | ramson.tech/kit | ||||
| 2 | Washi database GUI | Not started | JSJoe | Sep 20, 2026 | 13 | Empty | ||||
| 3 | Winter Shore-Up 2026 | In progress | BQBasquiat | Oct 15, 2026 | 5 | ramson.tech/winter | ||||
| 4 | iCreditScore redemption | Blocked | DKDickinson | Sep 1, 2026 | 5 | Empty | ||||
| 5 | Outdoor Revival 2026 | Done | BQBasquiat | Aug 10, 2026 | 3 | Empty | ||||
| 6 | Subscription audit | In progress | WEWASH-E | Aug 30, 2026 | 2 | ramson.tech/subs |
Choice cells (select, multiSelect) render PropertyTag, not
role-colored Chip. The hue lives on the option, mixed in cat-colors.ts.
Click a cell to edit it. Pick a status or priority from its menu, toggle On track, edit the tags, and then use the + in the header to add a property or New at the bottom to add a row. Everything updates the one collection object.
The field registry — how it extends
The grid knows nothing about any specific property type. It looks each one up in a field
registry, and every type owns three things there: how it draws in a cell, how it is
edited, and its icon. Adding a whole new kind of property to the system (the CMS move) is
adding one case to PropertyType and one entry to the registry, and nothing else changes.
// registry/data/lib/field-types.tsx
import type { Icon } from '@phosphor-icons/react';
export interface FieldType {
type: PropertyType;
label: string;
icon: Icon;
emptyValue: CellValue;
Display: React.FC<FieldDisplayProps>; // read mode
Editor?: React.FC<FieldEditorProps>; // edit mode
}
export const FIELD_TYPES: Record<PropertyType, FieldType> = {
text: { /* ... */ },
number: { /* ... */ },
// add a new type here and the grid can render and edit it
};The nine types shipped so far: text, number, select, multi-select,
status, date, checkbox, url, and person. A person cell picks from a
directory the host app supplies, which is the seam a relation or a linked collection grows
into.
The data shape
interface PropertyDef {
id: string;
name: string;
type: PropertyType;
options?: SelectOption[]; // select / multiSelect / status
numberFormat?: 'plain' | 'integer' | 'currency' | 'percent';
}
interface Row {
id: string;
cells: Record<string, CellValue>; // propertyId -> value
}
interface Collection {
name: string;
properties: PropertyDef[];
rows: Row[];
}CellValue is a loose union (string | number | boolean | string[] | Person | null).
Each field type reads only the shape it owns, and null is the universal empty.
Usage
The grid is controlled. It never mutates in place; it produces the next collection and
calls onChange, so the host owns the state (and, later, the persistence).
import { DataGrid } from '@/components/data/data-grid';
import type { Collection } from '@/components/data/lib/types';
function ProjectsView() {
const [collection, setCollection] = useState<Collection>(initial);
return (
<DataGrid
collection={collection}
onChange={setCollection}
people={team}
/>
);
}Accessibility and the honest edges
- The grid uses
role="grid"with row and cell roles, cells are focusable, and Enter opens the editor. - Focus-visible rings, forced-colors-safe outlines, and a real
role="checkbox"on the boolean cell. - Choice editors use a small dependency-free popover that closes on outside click or Escape. When the kit gains a Radix Popover primitive these move onto it and gain full focus management. Columns do not resize or stick yet, and property config (rename, retype, recolor options) is not built.
- Tag color is the one categorical palette the data layer owns, mixed off an oklch anchor
against the live surfaces so it stays legible in light and dark with no
.darkvariant.
Roadmap
- Views on the same collection: board (group by a select), gallery, list.
- Property config: rename, change type, add and recolor options, reorder columns.
- Filter, sort, and search over the collection.
- Relations and rollups (the
persondirectory generalizes into linked collections). - The Postgres schema and the API that serializes to and from
Collection.
Install
shadcn add @gesso/data-grid