fix(managers): address the E4 S3 code-review findings
- DataTable: keep `pagerNode` NULLABLE. Extracting Pager made it an always-truthy element, so the `controls` / `bottomControls` guards always fired and a table with nothing to page rendered empty `my-3` bands (plus an empty in-band PageToolbar). - ChannelsProvider: `setView` bails when the tab is unchanged (re-selecting the active tab, or goToFullHistory forcing "subscribed", stacked dead Back entries), and pushes a FRESH state like setPage does instead of spreading `_chan`/`_yt` markers into the tab entry. - Lift the layout into its own narrow context (`useChannelLayout`): App read the whole ChannelsState for the fade, so every keystroke in the manager's search box re-rendered the app root. - Reset the card page when the row set or sort changes (both tabs) — the clamp only hid a stale page, which came back when the filter was cleared. - Sort + slice the card rows only in cards mode; table mode was copy-sorting ~300 rows per render for a result nothing read. - Validate persisted `cardSize` / `sort` (`parseCardSize`, `parseSortState`), matching the clamp-on-read convention the module's other persisted values already follow; a corrupt size made the page count NaN and emptied the grid. - Pager: page-size changes scroll to the top too (they re-slice from a new first item). - ColumnSortControl: a stored sort naming a column this table no longer offers is treated as no sort, instead of showing a direction arrow for a sort that isn't applied. - Drop the stale "rows" layout comments (that variant was retired this sprint).
This commit is contained in:
@@ -35,12 +35,16 @@ export default function Pager({
|
||||
const [pageInput, setPageInput] = useState(String(safePage + 1));
|
||||
useEffect(() => setPageInput(String(safePage + 1)), [safePage]);
|
||||
|
||||
// Turning the page swaps the whole list, so put the reader at its top — landing mid-list in a page
|
||||
// you haven't seen is disorienting. The page scroller is the app's <main> (the same implicit
|
||||
// contract BackToTop and VirtualGrid's getScrollParent rely on).
|
||||
// Turning the page — or resizing it, which re-slices from a different first item — swaps the whole
|
||||
// list, so put the reader at its top; landing mid-list in a page you haven't seen is disorienting.
|
||||
// The page scroller is the app's <main> (PageScroller documents itself as the ONE scroller; the
|
||||
// same implicit contract BackToTop and VirtualGrid's getScrollParent already rely on).
|
||||
function scrollListToTop() {
|
||||
document.querySelector("main")?.scrollTo({ top: 0 });
|
||||
}
|
||||
function goToPage(p: number) {
|
||||
onPageChange(p);
|
||||
document.querySelector("main")?.scrollTo({ top: 0 });
|
||||
scrollListToTop();
|
||||
}
|
||||
function commitPageInput() {
|
||||
const n = parseInt(pageInput, 10);
|
||||
@@ -91,7 +95,10 @@ export default function Pager({
|
||||
{sizeLabel}
|
||||
<select
|
||||
value={size}
|
||||
onChange={(e) => onSizeChange(Number(e.target.value))}
|
||||
onChange={(e) => {
|
||||
onSizeChange(Number(e.target.value));
|
||||
scrollListToTop();
|
||||
}}
|
||||
className="bg-card border border-border rounded-md px-1.5 py-1 text-xs outline-none focus:border-accent"
|
||||
>
|
||||
{pageSizeOptions.map((n) => (
|
||||
|
||||
Reference in New Issue
Block a user