Spreadsheet-UX op een echte relationele engine
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
import { useCallback, useEffect, useState } from 'react';
import type { ColumnMeta } from '@wellformed/shared';
const STORAGE_KEY = 'wellformed.columnWidths';
const MIN_WIDTH = 60;
const MAX_WIDTH = 640;
function defaultWidth(col: ColumnMeta): number {
switch (col.type) {
case 'checkbox':
return 70;
case 'date':
return 118;
case 'number':
return 100;
case 'currency':
return 120;
case 'select':
return 120;
case 'link':
return 170;
default:
// The label is what the header actually renders, so it sizes the column.
return col.label.length > 12 ? 190 : 150;
}
}
type WidthMap = Record<string, number>;
function readStored(): WidthMap {
try {
const raw = window.localStorage.getItem(STORAGE_KEY);
return raw ? (JSON.parse(raw) as WidthMap) : {};
} catch {
return {};
}
}
/**
* Per-column widths, remembered across sessions. Keyed by table and column so
* two tables with a `Name` column keep their own sizing.
*/
export function useColumnWidths(table: string, columns: ColumnMeta[]) {
const [stored, setStored] = useState<WidthMap>(readStored);
useEffect(() => {
try {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
} catch {
// A full or disabled localStorage must not break the grid.
}
}, [stored]);
const widthOf = useCallback(
(col: ColumnMeta) => stored[`${table}.${col.name}`] ?? defaultWidth(col),
[stored, table],
);
const setWidth = useCallback(
(col: ColumnMeta, width: number) => {
const clamped = Math.round(Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, width)));
setStored((s) => ({ ...s, [`${table}.${col.name}`]: clamped }));
},
[table],
);
const reset = useCallback(
(col: ColumnMeta) => {
setStored((s) => {
const next = { ...s };
delete next[`${table}.${col.name}`];
return next;
});
},
[table],
);
const widths = columns.map(widthOf);
const totalWidth = widths.reduce((a, b) => a + b, 0) + 44; // + row-number gutter
return { widthOf, setWidth, reset, widths, totalWidth, MIN_WIDTH };
}