Give a dashboard the panel's own size, and put the dots on its grid
A dashboard now carries the canvas it is drawn for (canvas_width / canvas_height, presets plus two numbers in the settings panel). Editor and wall panel render that surface at its true pixel size and scale it to fit, so a side panel opening changes only the scale — never the arrangement being made. With the width and column count known the dot pitch is exact, (width + gap) / columns by row height + gap, so a dot sits where every widget corner snaps. The wall panel shows no dots. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
This commit is contained in:
@@ -27,7 +27,13 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import { MAX_SERIES } from "./ChartWidget"
|
||||
import { COLUMN_CHOICES, columnsOf, type Dashboard } from "./DashboardView"
|
||||
import {
|
||||
CANVAS_PRESETS,
|
||||
COLUMN_CHOICES,
|
||||
canvasOf,
|
||||
columnsOf,
|
||||
type Dashboard,
|
||||
} from "./DashboardView"
|
||||
import { messageCatalogQueryOptions } from "./queries"
|
||||
import {
|
||||
acceptsDtype,
|
||||
@@ -314,12 +320,16 @@ export function WidgetPanel({
|
||||
)
|
||||
}
|
||||
|
||||
/** Presets are matched on their size, so a custom one simply matches none. */
|
||||
const sizeKey = (size: { width: number; height: number }) =>
|
||||
`${size.width}x${size.height}`
|
||||
|
||||
/**
|
||||
* The dashboard's own settings, in the panel its widgets use.
|
||||
*
|
||||
* The grid size is the one that matters: a wall panel is a fixed width, and
|
||||
* twelve columns on a seven-inch screen is a different dashboard than twelve
|
||||
* on a television.
|
||||
* The canvas is the one that matters: a wall panel is a fixed size, and twelve
|
||||
* columns on a seven-inch screen is a different dashboard than twelve on a
|
||||
* television.
|
||||
*/
|
||||
export function DashboardPanel({
|
||||
open,
|
||||
@@ -337,6 +347,7 @@ export function DashboardPanel({
|
||||
onClose: () => void
|
||||
}) {
|
||||
const [confirmOpen, setConfirmOpen] = useState(false)
|
||||
const canvas = canvasOf(dashboard)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -390,6 +401,65 @@ export function DashboardPanel({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<span className={PANEL_SECTION}>Canvas</span>
|
||||
<Select
|
||||
value={
|
||||
CANVAS_PRESETS.some(
|
||||
(preset) => sizeKey(preset) === sizeKey(canvas),
|
||||
)
|
||||
? sizeKey(canvas)
|
||||
: ""
|
||||
}
|
||||
onValueChange={(value) => {
|
||||
const preset = CANVAS_PRESETS.find(
|
||||
(candidate) => sizeKey(candidate) === value,
|
||||
)
|
||||
if (preset)
|
||||
onChange({
|
||||
canvas_width: preset.width,
|
||||
canvas_height: preset.height,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<SelectTrigger data-testid="dashboard-canvas-size">
|
||||
<SelectValue placeholder="Custom" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{CANVAS_PRESETS.map((preset) => (
|
||||
<SelectItem key={sizeKey(preset)} value={sizeKey(preset)}>
|
||||
{preset.label} — {preset.width}×{preset.height}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
type="number"
|
||||
aria-label="Canvas width"
|
||||
data-testid="canvas-width"
|
||||
value={canvas.width}
|
||||
onChange={(event) =>
|
||||
onChange({ canvas_width: Number(event.target.value) || 0 })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
aria-label="Canvas height"
|
||||
data-testid="canvas-height"
|
||||
value={canvas.height}
|
||||
onChange={(event) =>
|
||||
onChange({ canvas_height: Number(event.target.value) || 0 })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The panel this is drawn for, in pixels. Editing and viewing both
|
||||
scale that surface to fit, so the arrangement is the same
|
||||
everywhere.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<span className={PANEL_SECTION}>Contents</span>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user