chain table round 4: double the indentation (owner refinement)

Each staircase step is now TWO columns (36px): chains pl-12 — the
checkbox aligned with the cluster's FOLDER icon — and statements pl-21
— the checkbox where a chain icon WOULD sit (the chains lost theirs;
the pattern continues anyway). Live-measured: chain checkbox = folder
icon (336=336); statement checkbox = chain checkbox + 36 (372).
This commit is contained in:
amosblomqvist
2026-09-30 00:23:15 +03:00
parent d6a7228b15
commit b85aab6eb0
4 changed files with 25 additions and 23 deletions
+13 -11
View File
@@ -33,12 +33,14 @@
* conclusion marker down there). * conclusion marker down there).
* *
* THE ALIGNMENT STAIRCASE (owner ruling, round 3): every level's * THE ALIGNMENT STAIRCASE (owner ruling, round 3): every level's
* checkbox sits DIRECTLY UNDER its parent's expander arrow — open a * checkbox sits ONE COLUMN under its parent's expander — aligned with
* folder and the chain checkboxes line up with the arrow, same one * the parent's ICON slot: open a folder and the chain checkboxes line
* level down. Geometry: the expander shares the checkbox's 14px width * up with the FOLDER icon; the statements step one more unit out (the
* (h-5 w-3.5), so each nesting step is exactly checkbox(14) + gap(4) = * would-be chain-icon column — the chains lost their icons but the
* 18px — clusters pl-3 (12px, checkbox at 12), chains pl-7.5 (30px = * pattern continues, owner: "double the indentation"). Geometry: every
* the cluster expander), statements pl-12 (48px = the chain expander). * column is 14px + 4px gap = 18px and each step is TWO columns (36px) —
* clusters pl-3 (12px), chains pl-12 (48px = the cluster's folder
* icon), statements pl-21 (84px = where a chain icon WOULD sit).
* *
* SORTING: all three columns sortable (owner ruling — "consistency is * SORTING: all three columns sortable (owner ruling — "consistency is
* key"): Name (A→Z), Props (largest-first), Created (newest-first); * key"): Name (A→Z), Props (largest-first), Created (newest-first);
@@ -265,9 +267,9 @@ export function ChainTableBody({
{/* The list: NO per-row lines (owner ruling) — the ONLY horizontal {/* The list: NO per-row lines (owner ruling) — the ONLY horizontal
separators sit between cluster groups; the ALIGNMENT STAIRCASE separators sit between cluster groups; the ALIGNMENT STAIRCASE
carries the hierarchy (owner round 3: each level's checkbox sits carries the hierarchy (owner round 4, "double the indentation":
under its parent's expander arrow — clusters pl-3, chains pl-7.5 two-column steps — chains pl-12 aligned with the folder icon,
under the folder arrow, statements pl-12 under the chain arrow). statements pl-21 where a chain icon would sit).
All three columns sort, small chevron on active. */} All three columns sort, small chevron on active. */}
<div className="min-h-0 flex-1 overflow-y-auto"> <div className="min-h-0 flex-1 overflow-y-auto">
<div className="sticky top-0 z-10 flex items-center gap-1 px-3 py-1.5 text-[11px] uppercase tracking-widest text-muted-foreground/70 backdrop-blur"> <div className="sticky top-0 z-10 flex items-center gap-1 px-3 py-1.5 text-[11px] uppercase tracking-widest text-muted-foreground/70 backdrop-blur">
@@ -355,7 +357,7 @@ export function ChainTableBody({
<div <div
data-testid={`chain-row-${chain.id}`} data-testid={`chain-row-${chain.id}`}
aria-label={formatChainLine(chain, Object.fromEntries([...byId.entries()].map(([id, n]) => [id, { name: n.name }])))} aria-label={formatChainLine(chain, Object.fromEntries([...byId.entries()].map(([id, n]) => [id, { name: n.name }])))}
className="flex items-center gap-1 py-1 pl-7.5 pr-3" className="flex items-center gap-1 py-1 pl-12 pr-3"
> >
<RowCheckbox <RowCheckbox
checked={selectedInChain === memberIds.length && memberIds.length > 0} checked={selectedInChain === memberIds.length && memberIds.length > 0}
@@ -402,7 +404,7 @@ export function ChainTableBody({
<div <div
key={id} key={id}
data-testid={`prop-row-${id}`} data-testid={`prop-row-${id}`}
className="flex items-start gap-2 py-1 pl-12 pr-3" className="flex items-start gap-2 py-1 pl-21 pr-3"
> >
<RowCheckbox <RowCheckbox
checked={selection.has(id)} checked={selection.has(id)}
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,7 +1,7 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts"; import "./.next/types/routes.d.ts";
import "./.next/dev/types/root-params.d.ts"; import "./.next/types/root-params.d.ts";
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+9 -9
View File
@@ -139,9 +139,9 @@ describe("ChainTableBody — THE shared TopBar + the lines law (owner, round 2)"
const sep = html.split("border-b border-border/60").length - 1; const sep = html.split("border-b border-border/60").length - 1;
assert.ok(sep >= 2, "separators between groups"); assert.ok(sep >= 2, "separators between groups");
// chain rows themselves carry NO border — their entire class list is // chain rows themselves carry NO border — their entire class list is
// the staircase law (pl-7.5 = under the cluster's expander arrow); // the staircase law (pl-12 = aligned with the cluster's FOLDER icon);
// the chip outlines' border-border/70 is an outline, not a row line // the chip outlines' border-border/70 is an outline, not a row line
assert.ok(html.includes('class="flex items-center gap-1 py-1 pl-7.5 pr-3"'), "the chain row: staircase indent, no border"); assert.ok(html.includes('class="flex items-center gap-1 py-1 pl-12 pr-3"'), "the chain row: staircase indent, no border");
}); });
}); });
@@ -186,13 +186,13 @@ describe("ChainTableBody — chain rows (chips + connectors)", () => {
// owner ruling: the derivation is already encoded in the chain line — // owner ruling: the derivation is already encoded in the chain line —
// no conclusion marker down here // no conclusion marker down here
assert.ok(!html.includes("prop-conclusion"), "no conclusion marker anywhere"); assert.ok(!html.includes("prop-conclusion"), "no conclusion marker anywhere");
// THE ALIGNMENT STAIRCASE (owner ruling, round 3): each level's // THE ALIGNMENT STAIRCASE (owner ruling, round 4 — "double the
// checkbox sits under its parent's expander arrow. The expander // indentation"): each level's checkbox sits under its parent's ICON
// shares the checkbox's 14px width, so each step is 18px: clusters // slot, TWO columns (36px) past its own. Every column is 18px:
// pl-3 (12) → chains pl-7.5 (30 = the cluster expander) → // clusters pl-3 (12) → chains pl-12 (48 = the folder icon) →
// statements pl-12 (48 = the chain expander) // statements pl-21 (84 = where a chain icon WOULD sit)
assert.ok(html.includes("pl-12"), "statements under the chain's expander"); assert.ok(html.includes("pl-21"), "statements at the would-be chain-icon column");
assert.ok(html.includes("pl-7.5"), "chains under the cluster's expander"); assert.ok(html.includes("pl-12"), "chains aligned with the folder icon");
assert.ok(html.includes("h-5 w-3.5"), "the expander shares the checkbox's 14px width"); assert.ok(html.includes("h-5 w-3.5"), "the expander shares the checkbox's 14px width");
}); });
}); });