diff --git a/frontend/src/features/triage/TasksView.tsx b/frontend/src/features/triage/TasksView.tsx index 28b5710..c943016 100644 --- a/frontend/src/features/triage/TasksView.tsx +++ b/frontend/src/features/triage/TasksView.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useLayoutEffect, useState, useRef, useMemo, useCallback } from 'react'; +import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { Check, @@ -82,6 +82,8 @@ const KANBAN_MIN_PHONE_COL_HEIGHT = 280; // First row of a Kanban board with several rows: the full height minus what is still visible of the bar // above the board (--kb-top at the start, shrinking with the scroll position --kb-scroll) const KANBAN_FIRST_ROW_HEIGHT = `max(${KANBAN_MIN_COL_HEIGHT}px, calc(var(--kb-full) - max(0px, calc(var(--kb-top) - var(--kb-scroll, 0px)))))`; +// Below the board: what the first row has not grown into yet, so the page keeps its height while scrolling +const KANBAN_BAR_SPACER = 'max(0px, calc(var(--kb-top) - var(--kb-scroll, 0px)))'; // Wide Kanban: a column grows to at most this width, the board stays centred const KANBAN_COL_MAX = 380; const KANBAN_GAP = 12; // gap-3 @@ -563,9 +565,8 @@ export const TasksView: React.FC = ({ // Desktop Kanban: every column, in every row, is as high as the room from the board's top to the bottom of // the scroll area, so the first row fits completely on opening and each further row fills one screen; // more cards scroll inside the column. At least room for about three cards (the page scrolls below that). - // One row: the page itself does not scroll (like the workspace). Several rows: the first row grows while - // the bar above the board scrolls away, so a row always fills the screen; the wheel moves row by row - // (below). + // The first row grows while the bar above the board scrolls away, so a row always fills the screen (with + // one row too: the first scroll step hides the bar); the wheel moves row by row (below). const kanbanBoardRef = useRef(null); const [kanbanColHeight, setKanbanColHeight] = useState(null); const [kanbanMultiRow, setKanbanMultiRow] = useState(false); @@ -610,15 +611,15 @@ export const TasksView: React.FC = ({ const areaHeight = area ? area.clientHeight : window.innerHeight; const top = el.getBoundingClientRect().top - areaTop; setKanbanColHeight(Math.max(KANBAN_MIN_COL_HEIGHT, areaHeight - top - 16)); - // Several rows: a row fills the whole area (--kb-full); the first one grows into the room the bar - // above the board frees while scrolling (--kb-top - --kb-scroll, see the scroll effect) - el.style.setProperty('--kb-full', `${Math.max(KANBAN_MIN_COL_HEIGHT, areaHeight - 24)}px`); - el.style.setProperty('--kb-top', `${Math.max(0, top - 12)}px`); + // A row fills the whole area (--kb-full); the first one grows into the room the bar above the board + // frees while scrolling (--kb-top - --kb-scroll, see the scroll effect). With one row as well, so the + // first scroll step hides the bar there too. Set on the board's wrapper: the spacer below needs them. + const vars = el.parentElement ?? el; + vars.style.setProperty('--kb-full', `${Math.max(KANBAN_MIN_COL_HEIGHT, areaHeight - 24)}px`); + vars.style.setProperty('--kb-top', `${Math.max(0, top - 12)}px`); + setKanbanMultiRow(true); requestAnimationFrame(() => { const colTops = Array.from(el.querySelectorAll('[data-kanban-col]')).map((c) => c.offsetTop); - const tops = new Set(colTops); - const multi = tops.size > 1; - setKanbanMultiRow(multi); setKanbanPerRow(colTops.filter((t) => t === Math.min(...colTops)).length); }); }; @@ -636,16 +637,6 @@ export const TasksView: React.FC = ({ }; }, [isKanban, isKanbanWide, boardColumns.length, selectedListId, loading]); - // One row on the desktop: take back what still overflows (paddings), so the page does not scroll. Measured - // only once the new height is on the page; measured right away, the old layout (e.g. two tall rows before - // stretching the board to full width) counted as overflow and squeezed the columns to their minimum. - useLayoutEffect(() => { - if (!kanbanColHeight || kanbanMultiRow || kanbanPhoneSnap) return; - const area = kanbanBoardRef.current?.closest('[data-app-scroll]'); - if (!area) return; - const over = area.scrollHeight - area.clientHeight; - if (over > 0) setKanbanColHeight((h) => (h ? Math.max(KANBAN_MIN_COL_HEIGHT, h - over) : h)); - }, [kanbanColHeight, kanbanMultiRow, kanbanPhoneSnap]); // Several rows: one wheel step moves to the next row (or back up to the previous row / the page top), // smoothly. The browser's own snapping goes back to the nearest point, so a wheel step shorter than a row @@ -694,7 +685,8 @@ export const TasksView: React.FC = ({ area.scrollTo({ top: next, behavior: 'smooth' }); }; // The first row grows while the bar above the board scrolls away - const onScroll = () => board.style.setProperty('--kb-scroll', `${area.scrollTop}px`); + const vars = board.parentElement ?? board; + const onScroll = () => vars.style.setProperty('--kb-scroll', `${area.scrollTop}px`); onScroll(); area.addEventListener('wheel', onWheel, { passive: false }); area.addEventListener('scroll', onScroll, { passive: true }); @@ -3163,6 +3155,9 @@ export const TasksView: React.FC = ({ ); })} + {/* Desktop: room as high as what is still visible of the bar above the board, so the page can + scroll the bar away even with one row while the first row grows (the page keeps its height) */} + {kanbanMultiRow &&
}
) : ( /* STANDARD LIST VIEW (CLEAN & UNCLUTTERED) */