Add column resizing in Pricing table

Drag column borders to resize width like Excel
This commit is contained in:
Christian Vidal Wolf
2026-04-21 08:40:54 +02:00
parent d8c65a22f9
commit b4cb7cbe57
3 changed files with 45 additions and 21 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ export default function App() {
if (isDev) {
const cacheBuster = `t_${Date.now()}`;
const fileUrl = `/dropbox-file/scl/fi/nkyabkq2jdwfudof2ovrl/Data-Matrix.xlsx?rlkey=1cz5fuabwipqqivihii7sybw0&st=io0g4wvb&dl=1&${cacheBuster}=${Date.now()}`;
const fileUrl = `/dropbox-file/scl/fi/ulcgt5iyyvrkeynprhali/Data-Matrix.xlsx?rlkey=i9p9xgwzm0fr7jip91vxdeal5&st=k8jmibh4&dl=1&${cacheBuster}=${Date.now()}`;
console.log('Fetching Data-Matrix.xlsx from Dropbox (hard refresh)...');
const response = await fetch(fileUrl, { cache: 'no-store' });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
+36 -5
View File
@@ -114,6 +114,37 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
setSearchMode('all');
};
const handleResizeStart = (e: React.MouseEvent, colKey: string) => {
e.preventDefault();
setResizingColumn(colKey);
};
React.useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (!resizingColumn || !tableRef.current) return;
const th = tableRef.current.querySelector(`th[data-col-key="${resizingColumn}"]`) as HTMLElement;
if (!th) return;
const rect = th.getBoundingClientRect();
const newWidth = e.clientX - rect.left;
if (newWidth >= 50 && newWidth <= 400) {
setColumnWidths(prev => ({ ...prev, [resizingColumn]: newWidth }));
}
};
const handleMouseUp = () => {
setResizingColumn(null);
};
if (resizingColumn) {
document.addEventListener('mousemove', handleMouseMove as any);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove as any);
document.removeEventListener('mouseup', handleMouseUp);
};
}
}, [resizingColumn]);
// ── Dynamic column detection ──────────────────────────────────────────────
const { uvpIdx, srpCols, containerCols } = useMemo(() => {
const uvpIdx = findCol(headers, 'uvp');
@@ -620,15 +651,15 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
<p className="text-sm mt-1">All products are correctly configured.</p>
</div>
) : (
<table className="w-full text-sm border-collapse">
<table ref={tableRef} className="w-full text-sm border-collapse">
<thead className="sticky top-0 z-10 bg-slate-900">
<tr>
<th className="text-left px-3 py-3 text-xs font-semibold text-slate-400 uppercase tracking-wider border-b border-slate-700 whitespace-nowrap">
<th data-col-key="articleNo" style={{ width: columnWidths.articleNo || 100 }} className="text-left px-3 py-3 text-xs font-semibold text-slate-400 uppercase tracking-wider border-b border-slate-700 whitespace-nowrap relative">
Art. No.
<div onMouseDown={(e) => handleResizeStart(e, 'articleNo')} className="absolute right-0 top-0 bottom-0 w-2 cursor-col-resize hover:bg-blue-500/30" />
</th>
<th className="text-left px-3 py-3 text-xs font-semibold text-slate-400 uppercase tracking-wider border-b border-slate-700 group relative">
<div className="flex items-center gap-1">
<span>Article Name</span>
<th data-col-key="articleName" style={{ width: columnWidths.articleName || 220 }} className="text-left px-3 py-3 text-xs font-semibold text-slate-400 uppercase tracking-wider border-b border-slate-700 group relative">
<div className="flex items-center gap-1"><span>Article Name</span>
<button
onClick={() => setOpenFilter(openFilter === 'name' ? null : 'name')}
className={cn(