It described the wrong object. A dashboard is a document that may hang on a
hallway tablet and in a desk browser at the same time, and only one of those
has fingers on it — so the flag moves off `DashboardDef.settings` and onto
`PanelDef` as a plain bool, ticked in the Panels dialog. `useCanvasRoot` takes
it as an argument rather than reading the document, and `/panel/{id}` is the
only surface with a panel to ask.
Dropping the message binding with it is deliberate: nothing drove it, and a
flow deciding whether a screen has fingers on it was never the point. A stored
`settings.touch` is inert rather than migrated, which `_check_settings`
skipping unknown names already guaranteed.
The rail was the other half. It had no touch behaviour at all and its 40px
buttons met neither branch of the 44/32 rule. `[data-touch] .dui-rail{-item}`
in `ui/core/core.css` spends the padding and the gap on the buttons instead,
so they reach the 44px target and the rail comes out taller at exactly the
same width — `RAIL_INSET` never moves, and the arrangement under it does not
either.
Also closes the panels-dialog icon gap: `DashboardSummary` carries the `icon`
now, so the dialog draws each assigned dashboard's rail glyph beside its
checkbox. `initials()` went from three identical copies in the looks to one in
`Dashboard/icons.ts`, so the dialog and the rail fall back the same way.
77 lines
2.8 KiB
TypeScript
77 lines
2.8 KiB
TypeScript
import type { Dashboard } from "@/components/Dashboard/DashboardView"
|
|
import {
|
|
CanvasSurface,
|
|
DashboardView,
|
|
} from "@/components/Dashboard/DashboardView"
|
|
import { useDashboardLocked } from "@/components/Dashboard/settings"
|
|
import { useUi } from "@/components/Dashboard/ui"
|
|
import { LookProvider } from "@/components/Dashboard/ui/core/look"
|
|
|
|
/**
|
|
* One dashboard filling whatever screen it landed on.
|
|
*
|
|
* The whole of what a wall panel draws, shared by the single-dashboard route
|
|
* (`/view/{name}`) and the paired-panel one (`/panel/{id}`) so a device shows
|
|
* the same thing either way — the second merely has a rail beside it.
|
|
*
|
|
* The dashboard's theme is stated by the route rather than here: on these two
|
|
* the screen *is* the dashboard, so it has to cover the letterbox and the rail
|
|
* as well as the canvas.
|
|
*/
|
|
export function PanelSurface({
|
|
dashboard,
|
|
stacked,
|
|
rail,
|
|
touch,
|
|
}: {
|
|
dashboard: Dashboard
|
|
/** A landscape arrangement scaled onto a phone comes out at about a fifth of
|
|
* its size, which reads as nothing at all. Stack it instead. */
|
|
stacked?: boolean
|
|
/** The way between this panel's dashboards, drawn on the canvas itself. */
|
|
rail?: React.ReactNode
|
|
/** Whether the screen this hangs on is touched. Stated by the panel route. */
|
|
touch?: boolean
|
|
}) {
|
|
return (
|
|
<div className="relative size-full">
|
|
{stacked ? (
|
|
<DashboardView dashboard={dashboard} stacked />
|
|
) : (
|
|
// The panel's own surface, scaled to fit. No dots: nothing is being
|
|
// arranged here.
|
|
<CanvasSurface dashboard={dashboard} rail={rail} touch={touch}>
|
|
{() => <DashboardView dashboard={dashboard} rail={Boolean(rail)} />}
|
|
</CanvasSurface>
|
|
)}
|
|
{/* Outside the canvas, so it needs the look stated for it. */}
|
|
<LookProvider dashboard={dashboard} touch={touch}>
|
|
<LockNotice dashboard={dashboard} />
|
|
</LookProvider>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* What a locked dashboard looks like, beyond controls that read as disabled.
|
|
*
|
|
* Shared with the editor's view mode, which shows the same locked dashboard
|
|
* from inside the app.
|
|
*
|
|
* Without it a read-only panel is a panel whose buttons do nothing, which
|
|
* reads as broken rather than as locked. Frosted chrome over content, like
|
|
* every other floating surface, and it says the state in words — a glyph on
|
|
* its own is not a label.
|
|
*
|
|
* Live, because `locked` may be driven by a flow: the notice appears and goes
|
|
* with the lock rather than with the page load.
|
|
*/
|
|
export function LockNotice({ dashboard }: { dashboard: Dashboard }) {
|
|
const { Notice } = useUi()
|
|
// Read straight from the document rather than through `useLocked`: that
|
|
// provider sits inside the grid, and this notice is beside it.
|
|
const locked = useDashboardLocked(dashboard)
|
|
if (!locked) return null
|
|
return <Notice>Read-only</Notice>
|
|
}
|