Add more labels for the day tabs
This commit is contained in:
Fraxle
2026-07-29 18:35:45 +01:00
parent 1acadb7519
commit f356954f40
6 changed files with 220 additions and 29 deletions
+71
View File
@@ -231,6 +231,77 @@ export function useAppState() {
el.scrollBy({ left: dir * 200, behavior: 'smooth' });
};
// Drag-to-scroll for the day-tab strip, mirroring the hourly table's body
// scroller (see useTableScroll). Unlike the table scroller, the draggable
// surface here IS the clickable element (each day is a <button>), so a
// plain mousedown/mouseup pair must still open that day - only a real drag
// (movement past a small threshold) should scroll instead of select. We
// track that with `dragged` and swallow the resulting click in capture
// phase so a drag never also fires the tab's onClick.
useEffect(() => {
const el = dayTabsRef.current;
if (!el) return;
let isDown = false;
let dragged = false;
let startX = 0;
let startScroll = 0;
const suppressClick = (e) => {
e.preventDefault();
e.stopPropagation();
el.removeEventListener('click', suppressClick, true);
};
const onMouseDown = (e) => {
if (!el.contains(e.target)) return;
if (e.button !== 0) return;
isDown = true;
dragged = false;
startX = e.clientX;
startScroll = el.scrollLeft;
};
const onMouseMove = (e) => {
if (!isDown) return;
const dx = e.clientX - startX;
if (!dragged && Math.abs(dx) > 4) {
dragged = true;
// .utci-day-tabs has scroll-behavior:smooth for the chevron/snap
// scrolls elsewhere - suspend it during drag so scrollLeft tracks
// the pointer 1:1 instead of easing behind it.
el.style.scrollBehavior = 'auto';
el.style.cursor = 'grabbing';
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
el.addEventListener('click', suppressClick, true);
}
if (dragged) el.scrollLeft = startScroll - dx;
};
const onMouseUp = () => {
if (!isDown) return;
isDown = false;
el.style.scrollBehavior = '';
el.style.cursor = '';
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
// Safety net: normally the browser's own click (fired right after
// mouseup, synchronously ahead of this timeout) hits suppressClick
// first and removes it. But if mouseup lands outside every tab or a
// click never follows for any other reason, this stops the listener
// leaking and silently swallowing the *next* real click.
if (dragged) setTimeout(() => el.removeEventListener('click', suppressClick, true), 0);
};
document.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
return () => {
document.removeEventListener('mousedown', onMouseDown);
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
el.removeEventListener('click', suppressClick, true);
};
}, [forecast]);
// ── 3. PRO TIER ──────────────────────────────────────────────────────
// (isPro is declared near the top so useForecast can read it; see above.)