79 lines
3.2 KiB
JavaScript
79 lines
3.2 KiB
JavaScript
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 <colgroup>, 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 <th> 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 };
|
||
|
|
}
|