import { useRef, useState } from "react"; import { loadFromStorage, saveToStorage } from "../utils/storage.js"; // Smaller dan dit wordt een kolom onleesbaar; de greep blijft wel pakbaar. const MIN_BREEDTE = 60; /** * Versleepbare kolombreedtes voor een tabel. * * `kolommen` is een array van { id, breedte } in weergave-volgorde, waarbij * `breedte` de standaard in pixels is. De aanpassingen van de gebruiker gaan * per tabel naar localStorage, zodat ze een tabwissel of herlaad overleven. * Kolommen die later verschijnen (een categorie die de gebruiker toevoegt) * krijgen hun standaardbreedte; opgeslagen breedtes van verdwenen kolommen * blijven ongebruikt staan en storen niets. * * De tabel moet `tableLayout: "fixed"` krijgen met een , en als * `width` de teruggegeven `totaal`. Met `width: "100%"` verdeelt de browser de * ruimte namelijk proportioneel en levert slepen niet exact de gesleepte * pixels op. * * Geeft terug: * breedtes px per kolom-id, standaard plus aanpassing * totaal som van de breedtes, voor de `width` van de tabel * grip props voor het sleepvlak in de van die kolom * herstel alle kolommen terug naar hun standaardbreedte */ export function useKolomBreedtes(opslagKey, kolommen) { const [aangepast, setAangepast] = useState(() => loadFromStorage(opslagKey) ?? {}); // Spiegelt `aangepast` zodat het slepen en het opslaan bij het loslaten niet // van de state-updater afhangen (die mag geen bijwerkingen hebben). const laatste = useRef(aangepast); const sleep = useRef(null); const breedtes = {}; for (const k of kolommen) breedtes[k.id] = aangepast[k.id] ?? k.breedte; const totaal = kolommen.reduce((som, k) => som + breedtes[k.id], 0); const zet = (volgende) => { laatste.current = volgende; setAangepast(volgende); }; const grip = (id) => ({ className: "kolom-grip", role: "separator", title: "Sleep om de kolom breder of smaller te maken — dubbelklik voor de standaardbreedte", onPointerDown: (e) => { // Pointer capture: het slepen loopt door buiten de greep, ook boven de // rijen of naast de tabel, zonder losse listeners op window. e.preventDefault(); e.currentTarget.setPointerCapture(e.pointerId); sleep.current = { id, startX: e.clientX, startBreedte: breedtes[id] }; }, onPointerMove: (e) => { if (!sleep.current) return; const { id: kolomId, startX, startBreedte } = sleep.current; const nieuw = Math.max(MIN_BREEDTE, Math.round(startBreedte + e.clientX - startX)); if (laatste.current[kolomId] === nieuw) return; zet({ ...laatste.current, [kolomId]: nieuw }); }, onPointerUp: () => { if (!sleep.current) return; sleep.current = null; // Pas bij het loslaten opslaan; tijdens het slepen zou elke pixel een // localStorage-write kosten. saveToStorage(opslagKey, laatste.current); }, onDoubleClick: () => { // Zonder destructuring-omit: die laat een ongebruikte variabele achter. const rest = { ...laatste.current }; delete rest[id]; zet(rest); saveToStorage(opslagKey, rest); }, }); const herstel = () => { zet({}); saveToStorage(opslagKey, {}); }; return { breedtes, totaal, grip, herstel }; }