Files
app/frontend/src/components/Dashboard/PanelRail.tsx
T
stroblme 6238728dce Draw the panel as a panel
Five things a wall panel showed that a wall panel should not.

**A tile's body no longer clips.** It scrolled, and a box that scrolls
also cuts whatever crosses its edge — which took the glow off a lit
button at exactly the width where the button filled its tile, and off a
gauge's arc at exactly the height where the dial filled its own. Only
what is written or listed asks for a scroller now; everything else is a
picture drawn to fit, and what overflows is left to the frame, which
clips at the tile's edge where a shadow has already faded out. The
slider's phantom scrollbar goes with it.

**The selector is a selector.** Named for what it does rather than what
it is, and the choice it is holding is held in the dashboard's own
primary — a pill that slides between the options rather than a grey one
that had to be looked for. The stored type is untouched, so no document
changes meaning.

**The arrangement is held off the panel's edges**, by the same distance
it holds between two widgets. The ground is not held off with it: a
background covers the whole panel, and only what is arranged on it has
a margin. No stored panel loses a row to it.

**The rail is drawn on the panel.** It was chrome bolted to the edge of
the screen beside the canvas — in the app's own design rather than the
dashboard's, and on a scaled canvas not even lined up with it. It now
takes a column out of the canvas the way the margin does, scaled with
it and wearing its look. Which cell each widget sits in is unchanged;
only how big a cell is.

Two of these were the same mistake twice: an unlayered rule stating
`position` for everything wearing a class — `.gl-surface` on a rail
placed by a utility, and a blanket lift over every child of a pressable
on a pill placed by `layoutId`. Both now say it one element at a time.
2026-08-24 10:44:53 +02:00

46 lines
1.5 KiB
TypeScript

import { useQueries } from "@tanstack/react-query"
import type { LinkProps } from "@tanstack/react-router"
import { dashboardQueryOptions } from "@/components/Dashboard/queries"
import { useUi } from "./ui"
/**
* Switching between the dashboards one panel was assigned.
*
* Permanent rather than a menu behind a button: a wall panel is glanced at,
* and something you have to open first is something nobody opens. It costs
* about a fortieth of the canvas, which `CanvasSurface` absorbs by scaling —
* the grid itself never changes, so an arrangement made without the rail still
* fits with it.
*
* Drawn in the dashboard's own look, because it hangs on the same screen: a
* rail wearing the app's chrome beside a glass panel is two designs at once.
*
* Reading the dashboards it links to is also what fills the labels, and it
* warms the cache for the neighbours so a switch draws immediately.
*/
export function PanelRail({
dashboards,
current,
linkFor,
}: {
dashboards: string[]
current: string
linkFor: (name: string) => LinkProps
}) {
const { Rail } = useUi()
const entries = useQueries({
queries: dashboards.map((name) => dashboardQueryOptions(name)),
combine: (results) =>
results.map((result, index) => ({
name: dashboards[index],
label: result.data?.title || dashboards[index],
icon: result.data?.icon ?? "",
active: dashboards[index] === current,
link: linkFor(dashboards[index]),
})),
})
return <Rail entries={entries} />
}