From 45e45bfa60183fc7ad6ad33383afa977e912a231 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Fri, 25 Sep 2026 12:59:45 -0400 Subject: [PATCH] Make the pivot toolbar read as three controls, not twelve Each axis's depth stepper is now one joined control, styled like the operation panel's Scale | Recode | Clone, so the arrows are visible and tell enabled from disabled in dark mode. The Total checkboxes become toggle chips beside their stepper. Operations shows its open state as pressed rather than relabelling to a blue "Hide panel", keeping blue for actions. The zone boxes are gone; the gap between the zones already separates them. The layout name gets more room before it truncates. color-scheme: dark on the dark palette fixes native controls app-wide. Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 26 +++++++++----- ui/src/components/DepthButtons.jsx | 58 ++++++++++++++++++++---------- ui/src/components/LayoutMenu.jsx | 2 +- ui/src/index.css | 27 ++++++++++++++ ui/src/views/Forecast.jsx | 51 +++++++++++++------------- 5 files changed, 111 insertions(+), 53 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6c0fbe8..94f14e9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -615,22 +615,32 @@ layout, since `restore()` is all-or-nothing. ## The Forecast toolbar is two zones -Left, on its own tinted ground: **view** — `Layout ▾` and the `Rows` and -`Columns` depth buttons. Nothing there reaches the server or changes the +Left: **view** — `Layout ▾` and the `Rows` and `Columns` depth steppers, each +with its `Total` toggle. Nothing there reaches the server or changes the forecast; it is all `ViewConfig` against rows already loaded. -Right, in a bordered panel of its own: **forecast** — `Refresh data`, -`Change log`, `Bridge`, `Operations`. These re-run the load, open something over -the pivot, or write to the version. +Right: **forecast** — `Refresh data`, `Change log`, `Bridge`, `Operations`. +These re-run the load, open something over the pivot, or write to the version. + +The distance between them is the separation. Each zone used to be an outlined +box as well, the view one on a `gray-50` tint — too faint to see against white, +and in dark mode the two boxes read as siblings. Within the view zone each axis +is one joined control, `◂ | level | ▸`, in the style of the operation panel's +`Scale | Recode | Clone`, followed by its `Total` chip. + +**Blue is for actions, pressed grey for state.** `Save` and `Post` are blue; +the `Total` chips and `Operations` while its panel is open use `.pf-pressed`. +`Operations` used to relabel itself `Hide panel` in a blue outline, which made +it look like a second Save. They were one undifferentiated row separated by identical hairlines, which made `Fit` and `Bridge` read as peers when one is a column width and the other is the version's history. Three weights do the separating now, and they were all once the same: `.pf-section-label` names a group (`Layout`, `Rows`, `Columns`) and is heavier and darker than the controls under it, `.pf-group-rule` separates groups -*within* a zone, and the zone borders separate the zones. A label that matched -its buttons, and a group rule that matched a zone border, is what made the row -read as one undifferentiated thing. +*within* a zone, and the gap between the zones separates them. A label that +matched its buttons, and a group rule that matched a zone border, is what made +the row read as one undifferentiated thing. **`Fit widths` is gone**, button and `fitColumns` both, for three independent reasons. Auto-size measures the leaf header over the rows *currently in the diff --git a/ui/src/components/DepthButtons.jsx b/ui/src/components/DepthButtons.jsx index b2bdb00..ab3e2f3 100644 --- a/ui/src/components/DepthButtons.jsx +++ b/ui/src/components/DepthButtons.jsx @@ -11,7 +11,7 @@ import { useEffect, useRef, useState } from 'react' // A stepper is three controls whatever the depth, and says where you are rather // than making you find it. The full list is still one click away, for jumping // rather than stepping. -export default function DepthButtons({ label, levels, depth, onPick, totalLabel = 'Total' }) { +export default function DepthButtons({ label, levels, depth, onPick, totalLabel = 'Total', children }) { const [open, setOpen] = useState(false) const boxRef = useRef(null) @@ -36,28 +36,36 @@ export default function DepthButtons({ label, levels, depth, onPick, totalLabel const current = !known ? '—' : (depth === 0 ? totalLabel : levels[depth - 1]) const step = (d) => onPick(Math.min(max, Math.max(0, d))) - const arrow = 'border border-gray-200 rounded px-1 py-0.5 text-gray-500 leading-none ' + - 'hover:border-gray-400 disabled:opacity-30 disabled:hover:border-gray-200 transition-colors' + // One joined control, in the style of the operation panel's + // Scale | Recode | Clone. The arrows used to be separate outlined boxes a + // few pixels wide, which in dark mode were barely visible and looked the + // same enabled or disabled; joined, the axis reads as one object and the + // arrows are halves of it rather than specks beside it. + const seg = 'px-1.5 py-0.5 leading-none bg-white transition-colors ' + + 'text-gray-600 hover:bg-gray-100 disabled:text-gray-300 disabled:hover:bg-white disabled:cursor-default' return ( -
- {label} +
+ {label} - +
+ - + - + +
+ + {children} {open && (
onChange(!on)} + className={`border rounded px-2 py-0.5 transition-colors ${ + on ? 'pf-pressed' : 'border-gray-300 bg-white text-gray-500 hover:bg-gray-100'}`}> + {children} + + ) +} diff --git a/ui/src/components/LayoutMenu.jsx b/ui/src/components/LayoutMenu.jsx index ea310bd..7db8102 100644 --- a/ui/src/components/LayoutMenu.jsx +++ b/ui/src/components/LayoutMenu.jsx @@ -119,7 +119,7 @@ export default function LayoutMenu({ Layout