From 61857c3ce1d23e3579189b0f006160b299161453 Mon Sep 17 00:00:00 2001 From: govin Date: Fri, 18 Sep 2026 18:26:52 +0800 Subject: [PATCH] frontend: let the list tables fill the window instead of their rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A list page is a table with controls around it, and the table was as tall as its rows: on a large screen most of the window sat empty below the last row while the page stayed the thing that scrolled. The three list pages now stretch the table to the height the window offers — the toolbar and the pagination stay put and the rows scroll inside the table — and give the height back on a small screen. Pure CSS, no resize listener and no measured pixel height, so nothing goes stale when the rail collapses or the window is resized. Four links carry it, each load-bearing: `.page--fill` is `min-height: 100%` (a floor, not a cap), `.card--fill` makes the card a column, its body gets `min-height: 0` so a flex child may shrink below its content, and `.table-fill` is `flex: 1 1 0` so the table takes the leftover height rather than claiming its content height as its basis. Element Plus then pins the header and scrolls the rows. Verified by rendering the pages in a headless browser at 1920x1080, 1440x900 and 1280x620: the table measures 781/601/321px, the page never scrolls, the rows scroll inside it, header and body columns stay aligned to 0px through a 400px horizontal scroll, the pinned action column stays pinned, and the empty state is centred in the full-height body rather than stranded at the top. A 1280x420 window hits the 240px floor and falls back to the page scrolling, and the paper document page is untouched. --- docs/OVERVIEW.md | 35 ++++++++++++ frontend/src/styles/base.css | 57 +++++++++++++++++++ frontend/src/views/PapersView.vue | 7 ++- .../src/views/templates/FieldManageView.vue | 7 ++- .../src/views/templates/TemplateListView.vue | 7 ++- 5 files changed, 104 insertions(+), 9 deletions(-) diff --git a/docs/OVERVIEW.md b/docs/OVERVIEW.md index 19f2103..e602773 100644 --- a/docs/OVERVIEW.md +++ b/docs/OVERVIEW.md @@ -298,6 +298,41 @@ module rather than CSS custom properties because `el-aside` takes its width through a prop, which would otherwise fight an inline style coming from the stylesheet. +### List pages fill the window + +A table is the page. Left alone it is as tall as its rows, so on a large screen +most of the window sits empty below the last row while the *page* is still the +thing that scrolls — the wrong half of the screen moving for a list that should +simply show more. The three list pages (`/papers`, `/templates/list`, +`/templates/fields`) therefore stretch the table to the height the window +offers, with the toolbar above it and the pagination below it staying put while +the rows scroll inside the table. + +It is CSS only — no resize listener, no measured pixel height, nothing to go +stale when the rail collapses or the window changes. Four links carry it, and +each one is load-bearing: + +| Piece | Why | +|---|---| +| `.page--fill` | `min-height: 100%` of the content area — a floor that still grows | +| `.card--fill` | makes the card a column so its body can pass the height down | +| `.card--fill > .el-card__body` | a column with `min-height: 0`, without which a flex child refuses to shrink below its content | +| `.table-fill` + `height="100%"` | `flex: 1 1 0` so the table takes the leftover height instead of claiming its content height as its basis; Element Plus then pins the header and scrolls the rows | + +Measured in a real browser at three window sizes, with rows to scroll and with +none: + +| Window | Table | Page scroll | Rows scroll | Empty state | +|---|---|---|---|---| +| 1920x1080 | 781px | none | 519px inside | centred, 741px | +| 1440x900 | 601px | none | 699px inside | centred, 561px | +| 1280x620 | 321px | none | 979px inside | centred, 281px | + +`.table-fill` keeps a 240px floor. Below it — a 1280x420 window — the table +stops shrinking, the page grows past the viewport and the content area scrolls, +which is the pre-existing behaviour rather than a squashed table. Pages that +are not lists (the paper document) keep scrolling normally. + ### Routes | Path | View | Section | diff --git a/frontend/src/styles/base.css b/frontend/src/styles/base.css index 16bcaf6..a2e07d7 100644 --- a/frontend/src/styles/base.css +++ b/frontend/src/styles/base.css @@ -202,3 +202,60 @@ samp { justify-content: flex-end; margin-top: 16px; } + +/* --- list pages that fill the viewport -------------------------------------- + A list page is a table with controls around it. Left alone, the table is as + tall as its rows, so on a large screen most of the window sits empty under + the last row while the page is still the thing that scrolls. These three + classes make the table take the height the window offers instead: more rows + visible at once on a big screen, and the height given back on a small one. + + Every link in the chain is load-bearing: + + .page--fill `min-height`, never `height` + .card--fill a column, so its body can pass the height on + .card--fill > .el-card__body + a column too, and `min-height: 0` — without it + a flex child refuses to shrink below its + content and the table would never get a height + .table-fill `flex: 1 1 0`, i.e. basis 0. With `auto` the + table would claim its content height as its + basis and push the pagination out of the card + el-table height="100%" fills .table-fill; Element Plus then keeps the + header fixed and scrolls the rows internally + + `.page--fill` is a *minimum*, not a fixed height: on a short window the + table keeps its 240px floor, the page grows past the viewport and the + content area scrolls, which is the behaviour a squeezed table would not + survive. */ + +.page--fill { + min-height: 100%; +} + +.card--fill { + /* Basis auto, so the card fills the page's minimum height exactly and still + grows if its content needs more. */ + flex: 1 1 auto; + display: flex; + flex-direction: column; + min-height: 0; +} + +.card--fill > .el-card__header { + flex: 0 0 auto; +} + +.card--fill > .el-card__body { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; +} + +.table-fill { + /* `1 1 0` rather than `1 1 auto`: the table takes the leftover height rather + than adding to it. The min-height is the floor a short window lands on. */ + flex: 1 1 0; + min-height: 240px; +} diff --git a/frontend/src/views/PapersView.vue b/frontend/src/views/PapersView.vue index bc113ab..edcfa06 100644 --- a/frontend/src/views/PapersView.vue +++ b/frontend/src/views/PapersView.vue @@ -168,8 +168,8 @@ onMounted(async () => {