1.5.7
Profile memory Comment cleanup
This commit is contained in:
@@ -1,33 +1,33 @@
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// useColumnPopup — owns the column-header popup AND the event-tag popup.
|
||||
// ------------------------------------------------------------------------
|
||||
// useColumnPopup - owns the column-header popup AND the event-tag popup.
|
||||
//
|
||||
// Both popups behave identically:
|
||||
// • click an anchor → toggle the popup open
|
||||
// • hover an anchor for N ms → open
|
||||
// • move into the popup → keep it open
|
||||
// • leave the popup → close after 200 ms
|
||||
// • click outside / scroll / resize → close immediately
|
||||
// - click an anchor - toggle the popup open
|
||||
// - hover an anchor for N ms - open
|
||||
// - move into the popup - keep it open
|
||||
// - leave the popup - close after 200 ms
|
||||
// - click outside / scroll / resize - close immediately
|
||||
//
|
||||
// Returns everything app.js needs to wire up the th cells and event-tag
|
||||
// spans, plus the popup state objects for rendering the floating panels.
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
import { useState, useEffect, useRef, useCallback } from '../../vendor/preact-hooks.js';
|
||||
|
||||
export function useColumnPopup() {
|
||||
// ─── Column header popup ────────────────────────────────────────────
|
||||
// --- Column header popup --------------------------------------------
|
||||
const [colPopup, setColPopup] = useState(null);
|
||||
const colPopupRef = useRef(null);
|
||||
const colPopupThRef = useRef(null);
|
||||
const hoverTimerRef = useRef(null);
|
||||
const closeTimerRef = useRef(null);
|
||||
|
||||
// ─── Event tag popup ────────────────────────────────────────────────
|
||||
// --- Event tag popup ------------------------------------------------
|
||||
// Holds { events[], slideIndex, x, y, arrowLeft, below }
|
||||
// When multiple events are in the popup they auto-cycle with a crossfade.
|
||||
const [eventTagPopup, setEventTagPopup] = useState(null);
|
||||
const [evSlideIndex, setEvSlideIndex] = useState(0);
|
||||
// 'entering' | 'exiting' | null — drives CSS crossfade classes
|
||||
// 'entering' | 'exiting' | null - drives CSS crossfade classes
|
||||
const [evTransition, setEvTransition] = useState(null);
|
||||
const prevSlideIndexRef = useRef(0);
|
||||
const eventTagPopupRef = useRef(null);
|
||||
@@ -76,7 +76,7 @@ export function useColumnPopup() {
|
||||
hoverTimerRef.current = setTimeout(() => openPopup(key, thEl), 1000);
|
||||
};
|
||||
|
||||
// Leaving a th: just cancel the pending open. Don't auto-close —
|
||||
// Leaving a th: just cancel the pending open. Don't auto-close -
|
||||
// the user might be moving into the popup, or just passing through.
|
||||
const handleThLeave = () => {
|
||||
clearTimeout(hoverTimerRef.current);
|
||||
@@ -103,7 +103,7 @@ export function useColumnPopup() {
|
||||
};
|
||||
}, [colPopup]);
|
||||
|
||||
// ─── Event tag popup helpers ─────────────────────────────────────────
|
||||
// --- Event tag popup helpers -----------------------------------------
|
||||
const calcEventPopupPos = (spanEl) => {
|
||||
const rect = spanEl.getBoundingClientRect();
|
||||
const popupW = 260, popupH = 80, margin = 8, gap = 6;
|
||||
@@ -116,7 +116,7 @@ export function useColumnPopup() {
|
||||
return { x, y, arrowLeft, below };
|
||||
};
|
||||
|
||||
// ─── Slideshow advance ───────────────────────────────────────────────
|
||||
// --- Slideshow advance -----------------------------------------------
|
||||
// evSlideTo triggers a crossfade to a new slide index.
|
||||
const evSlideTo = useCallback((nextIndex) => {
|
||||
setEvTransition('exiting');
|
||||
@@ -148,7 +148,7 @@ export function useColumnPopup() {
|
||||
return () => clearInterval(evSlideTimerRef.current);
|
||||
}, [eventTagPopup, evSlideTo]);
|
||||
|
||||
// ─── Event tag popup handlers ────────────────────────────────────────
|
||||
// --- Event tag popup handlers ----------------------------------------
|
||||
const openEventTagPopup = (events, spanEl) => {
|
||||
clearInterval(evSlideTimerRef.current);
|
||||
setEvSlideIndex(0);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// useForecast — fetches the weather forecast and air quality for the
|
||||
// ------------------------------------------------------------------------
|
||||
// useForecast - fetches the weather forecast and air quality for the
|
||||
// given location and keeps them fresh.
|
||||
//
|
||||
// Responsibilities:
|
||||
@@ -11,15 +11,15 @@
|
||||
// stale.
|
||||
//
|
||||
// Inputs:
|
||||
// location — { lat, lon, name, country }
|
||||
// location - { lat, lon, name, country }
|
||||
//
|
||||
// Outputs:
|
||||
// forecast — raw /v1/forecast response, or null
|
||||
// airQuality — raw /v1/air-quality response, or null
|
||||
// loading — true while the forecast fetch is in flight
|
||||
// error — fetch error message, or null
|
||||
// now — Date that ticks every 5 minutes (drives the "current row")
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// forecast - raw /v1/forecast response, or null
|
||||
// airQuality - raw /v1/air-quality response, or null
|
||||
// loading - true while the forecast fetch is in flight
|
||||
// error - fetch error message, or null
|
||||
// now - Date that ticks every 5 minutes (drives the "current row")
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
import { useState, useEffect } from '../../vendor/preact-hooks.js';
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// useTableScroll — owns the horizontal scroll behaviour of the hourly
|
||||
// ------------------------------------------------------------------------
|
||||
// useTableScroll - owns the horizontal scroll behaviour of the hourly
|
||||
// table (sticky header + body scroller layout).
|
||||
//
|
||||
// Responsibilities:
|
||||
// 1. SCROLL INDICATORS — track whether the body can scroll left/right so
|
||||
// 1. SCROLL INDICATORS - track whether the body can scroll left/right so
|
||||
// the table edges can show fade + chevron indicators.
|
||||
// 2. DRAG-TO-SCROLL — pointer-event drag on the body scroller for
|
||||
// 2. DRAG-TO-SCROLL - pointer-event drag on the body scroller for
|
||||
// desktop users.
|
||||
// 3. SCROLL SYNC — keep the sticky header track shifted horizontally to
|
||||
// 3. SCROLL SYNC - keep the sticky header track shifted horizontally to
|
||||
// match the body's scrollLeft, and keep header cell widths in lock-
|
||||
// step with body cell widths even as columns toggle / window resizes.
|
||||
//
|
||||
@@ -15,12 +15,12 @@
|
||||
// refs: { headTableRef, bodyTableRef, bodyScrollRef, headTrackRef }
|
||||
// deps: { forecast, visibleCols, selectedDay, skinType, vehicleType,
|
||||
// indoorMode, indoorManaged }
|
||||
// — anything that should cause a re-sync when it changes.
|
||||
// - anything that should cause a re-sync when it changes.
|
||||
//
|
||||
// Outputs:
|
||||
// tableCanScrollLeft, tableCanScrollRight → drive the fade/chevron CSS
|
||||
// handleBodyScroll → attach to body onScroll
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// tableCanScrollLeft, tableCanScrollRight - drive the fade/chevron CSS
|
||||
// handleBodyScroll - attach to body onScroll
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
import { useState, useEffect, useLayoutEffect } from '../../vendor/preact-hooks.js';
|
||||
|
||||
@@ -37,7 +37,7 @@ export function useTableScroll({
|
||||
indoorMode,
|
||||
indoorManaged,
|
||||
}) {
|
||||
// ─── SCROLL INDICATORS ─────────────────────────────────────────────
|
||||
// --- SCROLL INDICATORS ---------------------------------------------
|
||||
const [tableCanScrollLeft, setTableCanScrollLeft] = useState(false);
|
||||
const [tableCanScrollRight, setTableCanScrollRight] = useState(false);
|
||||
|
||||
@@ -48,7 +48,7 @@ export function useTableScroll({
|
||||
setTableCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
||||
};
|
||||
|
||||
// ─── DRAG-TO-SCROLL ────────────────────────────────────────────────
|
||||
// --- DRAG-TO-SCROLL ------------------------------------------------
|
||||
useEffect(() => {
|
||||
const el = bodyScrollRef.current;
|
||||
if (!el) return;
|
||||
@@ -103,7 +103,7 @@ export function useTableScroll({
|
||||
updateTableScrollIndicators();
|
||||
}, [forecast, visibleCols, selectedDay]);
|
||||
|
||||
// ─── BODY SCROLL HANDLER (called from JSX onScroll) ───────────────
|
||||
// --- BODY SCROLL HANDLER (called from JSX onScroll) ---------------
|
||||
const handleBodyScroll = () => {
|
||||
const track = headTrackRef.current;
|
||||
const body = bodyScrollRef.current;
|
||||
@@ -112,7 +112,7 @@ export function useTableScroll({
|
||||
updateTableScrollIndicators();
|
||||
};
|
||||
|
||||
// ─── COLUMN-WIDTH SCROLL SYNC (layout effect) ─────────────────────
|
||||
// --- COLUMN-WIDTH SCROLL SYNC (layout effect) ---------------------
|
||||
// Synchronise the head and body table column widths with a
|
||||
// "shrink-to-fit then distribute" strategy. See the comments inside
|
||||
// sync() for the algorithm.
|
||||
@@ -132,7 +132,7 @@ export function useTableScroll({
|
||||
|
||||
// Step 1: clear any previously-forced cell widths and switch the
|
||||
// tables to natural sizing so the measurement reflects the true
|
||||
// content-fit width — independent of how wide the container is.
|
||||
// content-fit width - independent of how wide the container is.
|
||||
headCells.forEach(c => { c.style.width = ''; c.style.minWidth = ''; c.style.maxWidth = ''; });
|
||||
bodyCells.forEach(c => { c.style.width = ''; c.style.minWidth = ''; c.style.maxWidth = ''; });
|
||||
headTable.style.width = 'max-content';
|
||||
@@ -141,7 +141,7 @@ export function useTableScroll({
|
||||
bodyTable.style.tableLayout = 'auto';
|
||||
|
||||
// Step 2: read each cell's natural width. getBoundingClientRect
|
||||
// forces synchronous layout — that's what we want.
|
||||
// forces synchronous layout - that's what we want.
|
||||
const naturalW = new Array(n);
|
||||
let naturalTotal = 0;
|
||||
for (let i = 0; i < n; i++) {
|
||||
@@ -157,7 +157,7 @@ export function useTableScroll({
|
||||
const finalW = new Array(n);
|
||||
let totalWidth;
|
||||
if (naturalTotal > 0 && naturalTotal < containerW) {
|
||||
// Spare space — distribute proportionally across columns so they
|
||||
// Spare space - distribute proportionally across columns so they
|
||||
// fan out to fill the scroller (no awkward right-hand gap).
|
||||
const scale = containerW / naturalTotal;
|
||||
let running = 0;
|
||||
@@ -170,7 +170,7 @@ export function useTableScroll({
|
||||
finalW[n - 1] = containerW - running;
|
||||
totalWidth = containerW;
|
||||
} else {
|
||||
// Naturals don't fit — use them as-is and let the body scroll.
|
||||
// Naturals don't fit - use them as-is and let the body scroll.
|
||||
for (let i = 0; i < n; i++) finalW[i] = naturalW[i];
|
||||
totalWidth = naturalTotal;
|
||||
}
|
||||
@@ -192,7 +192,7 @@ export function useTableScroll({
|
||||
bodyCells[i].style.maxWidth = px;
|
||||
}
|
||||
// Make both tables exactly totalWidth wide so they share the same
|
||||
// horizontal extent — column N in the header sits directly above
|
||||
// horizontal extent - column N in the header sits directly above
|
||||
// column N in the body, no drift as you scroll right.
|
||||
headTable.style.width = `${totalWidth}px`;
|
||||
bodyTable.style.width = `${totalWidth}px`;
|
||||
@@ -202,7 +202,7 @@ export function useTableScroll({
|
||||
// Run once after layout
|
||||
sync();
|
||||
// Re-sync when the scroll container's width changes (window resize,
|
||||
// sidebar opens, etc). We observe the scroller — not the body table —
|
||||
// sidebar opens, etc). We observe the scroller - not the body table -
|
||||
// because the body table's width is now driven by sync itself, which
|
||||
// would otherwise create a feedback loop.
|
||||
let ro = null;
|
||||
|
||||
Reference in New Issue
Block a user