Spreadsheet-UX op een echte relationele engine

main

Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.

2.1 kB Ruw bestand
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 };
}