admin: tables fit a phone, their first cell carrying the hidden columns, and no positioned label widens the page

This commit is contained in:
2026-10-21 10:21:17 +02:00
parent 9a99890aea
commit f26ee523c2
29 changed files with 765 additions and 156 deletions
@@ -5,6 +5,7 @@ import InlineError from "@/lib/InlineError";
import { upstreamCreateMutation, upstreamDeleteMutation, upstreamUpdateMutation, upstreamsQuery } from "@/lib/queries";
import type { Upstream, UpstreamInput } from "@/lib/types";
import ConfirmDialog from "@/ui/ConfirmDialog";
import FactLine from "@/ui/FactLine";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import Switch from "@/ui/Switch";
@@ -16,18 +17,41 @@ import { styles as config } from "./styles";
const INTRO = "The pool is rebuilt as you save, so an edit here applies to the next query.";
/**
* Narrower than the four columns side by side: below it the table keeps URL,
* Enabled and the actions, and the URL cell carries the rest.
*/
const COMPACT = "@container (max-width: 42rem)";
const styles = stylex.create({
/** Cut to one line beside the other columns; whole, wrapping, where it is the row's only text column. */
url: {
display: "block",
maxWidth: "18rem",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
whiteSpace: { default: "nowrap", [COMPACT]: "normal" },
overflowWrap: "anywhere",
fontWeight: 500,
},
unbroken: {
overflowWrap: "anywhere",
},
wide: {
display: { default: null, [COMPACT]: "none" },
},
compact: {
display: { default: "none", [COMPACT]: "block" },
marginTop: "0.125rem",
color: colors.textMuted,
overflowWrap: "anywhere",
},
/** Stacked in the narrow form, which gives the first cell the width; the row is several lines tall there already. */
actions: {
display: "flex",
gap: "0.75rem",
flexDirection: { default: null, [COMPACT]: "column" },
alignItems: { default: null, [COMPACT]: "flex-start" },
gap: { default: "0.75rem", [COMPACT]: "0.5rem" },
},
dimWhenDisabled: {
opacity: { default: 1, ":disabled": 0.5 },
@@ -37,6 +61,17 @@ const styles = stylex.create({
},
});
/** The hidden columns' facts for the compact URL cell; an empty TLS name has nothing to say there. */
function UpstreamFacts({ upstream }: { upstream: Upstream }) {
const facts = [`Priority ${upstream.priority}`];
if (upstream.tls_name !== "") facts.push(`TLS name ${upstream.tls_name}`);
return (
<span {...stylex.props(styles.compact, shared.tabularNums)}>
<FactLine facts={facts} />
</span>
);
}
export default function UpstreamsTab() {
const upstreams = useQuery(upstreamsQuery());
return (
@@ -78,18 +113,21 @@ function UpstreamsReadOnly({ upstreams }: { upstreams: Upstream[] }) {
<thead>
<tr>
<th {...stylex.props(shared.th)}>URL</th>
<th {...stylex.props(shared.th)}>Priority</th>
<th {...stylex.props(shared.th, styles.wide)}>Priority</th>
<th {...stylex.props(shared.th)}>Enabled</th>
<th {...stylex.props(shared.th)}>TLS name</th>
<th {...stylex.props(shared.th, styles.wide)}>TLS name</th>
</tr>
</thead>
<tbody>
{upstreams.map((u) => (
<tr key={u.id}>
<td {...stylex.props(shared.td, shared.mono)}>{u.url}</td>
<td {...stylex.props(shared.td, shared.tabularNums)}>{u.priority}</td>
<td {...stylex.props(shared.td, styles.unbroken)}>
<span {...stylex.props(shared.mono)}>{u.url}</span>
<UpstreamFacts upstream={u} />
</td>
<td {...stylex.props(shared.td, shared.tabularNums, styles.wide)}>{u.priority}</td>
<td {...stylex.props(shared.td)}>{String(u.enabled)}</td>
<td {...stylex.props(shared.td)}>
<td {...stylex.props(shared.td, styles.unbroken, styles.wide)}>
{u.tls_name === "" ? (
<span {...stylex.props(styles.absent)}>empty</span>
) : (
@@ -151,9 +189,9 @@ function UpstreamsEditor({ upstreams }: { upstreams: Upstream[] }) {
<thead>
<tr>
<th {...stylex.props(shared.th)}>URL</th>
<th {...stylex.props(shared.th)}>Priority</th>
<th {...stylex.props(shared.th, styles.wide)}>Priority</th>
<th {...stylex.props(shared.th)}>Enabled</th>
<th {...stylex.props(shared.th)}>TLS name</th>
<th {...stylex.props(shared.th, styles.wide)}>TLS name</th>
<th {...stylex.props(shared.th)}>
<span {...stylex.props(shared.srOnly)}>Actions</span>
</th>
@@ -166,8 +204,9 @@ function UpstreamsEditor({ upstreams }: { upstreams: Upstream[] }) {
<span {...stylex.props(styles.url)} title={u.url}>
{u.url}
</span>
<UpstreamFacts upstream={u} />
</td>
<td {...stylex.props(shared.td, shared.tabularNums)}>{u.priority}</td>
<td {...stylex.props(shared.td, shared.tabularNums, styles.wide)}>{u.priority}</td>
<td {...stylex.props(shared.td)}>
<Switch
aria-label={`${u.url} enabled`}
@@ -176,7 +215,9 @@ function UpstreamsEditor({ upstreams }: { upstreams: Upstream[] }) {
onChange={() => toggleEnabled(u)}
/>
</td>
<td {...stylex.props(shared.td)}>{u.tls_name === "" ? "—" : u.tls_name}</td>
<td {...stylex.props(shared.td, styles.unbroken, styles.wide)}>
{u.tls_name === "" ? "—" : u.tls_name}
</td>
<td {...stylex.props(shared.td)}>
<div {...stylex.props(styles.actions)}>
<button