fix(managers): address the E4 S3 code-review round 2 findings

- The portalled tag picker now FOLLOWS its trigger on scroll (rAF-coalesced)
  instead of closing. Closing on every wheel tick was a regression for the table,
  where the menu used to travel with its row; it still closes once the trigger
  leaves the viewport (nothing left to anchor to) or on resize.
- Place the menu by MEASUREMENT, not a px estimate: it opens below, then the
  layout effect flips it above only if the real box overflows. The old
  `rows * 30px` guess read short at font-scale 1.3 (the rows are rem), which
  could leave a fixed menu hanging off the bottom with no way to reach it.
- `toggle` only opens once a position exists, so the `open && coords` gate can
  never show a stale anchor.
- One owner for "is there anything to page": `hasPagerContent()` lives in Pager
  and DataTable calls it, instead of both re-deriving the rule — that drift is
  what left empty control bands behind in the first place.
- Reset the card page on the row COUNT too, not just the filter inputs, so an
  unsubscribe or a shrinking refetch can't leave a stale page to spring back.
- `parseCardSize` clamps to the sizes the pager actually offers (like clampView /
  clampStatus), so the select can't display one value while the grid pages by another.
- VirtualGrid's docblock now carries the stacking-context constraint: its rows are
  transformed, so anything floating an item renders must portal out.
This commit is contained in:
2026-07-20 00:56:47 +02:00
parent 260de48f41
commit c630d29ff6
6 changed files with 78 additions and 43 deletions
+5
View File
@@ -6,6 +6,11 @@ import { useVirtualizer } from "@tanstack/react-virtual";
// mechanics — measure the container, derive the column count, chunk into rows, virtualize, and place
// each row absolutely — and knows nothing about what an item is. The caller supplies `renderItem`.
//
// ⚠️ Each row is placed with `position:absolute` + `transform` (below), which makes EVERY ROW ITS OWN
// STACKING CONTEXT. A z-indexed child therefore cannot escape its row: a menu or popover rendered
// inside an item draws UNDER the next row, whatever z-index it carries — the channel manager's tag
// picker hit exactly this. Anything floating that an item renders must portal out (see Overlay).
//
// Column sizing mirrors the CSS the feed used before virtualization
// (grid-cols-[repeat(auto-fill,minmax(260px,1fr))] with a gap-4 / 1rem gutter).
const GRID_GAP = 16;