3.5.8.1
Add more labels for the day tabs
This commit is contained in:
@@ -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.)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user