Draw a round dashboard in the mosaic as its circle with a dot per face

DashboardSummary carries the dashboard's shape, so the list no longer lays
a round dashboard's faces out at grid places they never use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgBfkm4rCmtd1T4vHWjEPx
This commit is contained in:
2026-10-15 15:54:47 +02:00
co-authored by Claude Opus 5.5
parent 9586a9fbc3
commit cea442d125
5 changed files with 46 additions and 0 deletions
+6
View File
@@ -828,6 +828,12 @@ export const DashboardSummarySchema = {
title: 'Columns',
default: 12
},
shape: {
type: 'string',
enum: ['rect', 'round'],
title: 'Shape',
default: 'rect'
},
footprint: {
items: {
'$ref': '#/components/schemas/WidgetFootprint'
+1
View File
@@ -215,6 +215,7 @@ export type DashboardSummary = {
has_draft?: boolean;
version?: number;
columns?: number;
shape?: 'rect' | 'round';
footprint?: Array<WidgetFootprint>;
updated_at?: (string | null);
};
@@ -65,6 +65,9 @@ function blocksOf(footprint: WidgetFootprint[]): {
* dashboard. The summary carries the shapes, so it costs no request either.
*/
function Footprint({ dashboard }: { dashboard: DashboardSummary }) {
if (dashboard.shape === "round") {
return <RoundFootprint faces={dashboard.footprint ?? []} />
}
const { blocks, rows } = blocksOf(dashboard.footprint ?? [])
const columns = dashboard.columns || DEFAULT_COLUMNS
// Before the editor could place things, every widget was written at 0,0;
@@ -112,6 +115,31 @@ function Footprint({ dashboard }: { dashboard: DashboardSummary }) {
)
}
/**
* A round dashboard shows one face at a time, so its grid places mean
* nothing: it is drawn as the circle, with a dot per face where a panel puts
* its page dots.
*/
function RoundFootprint({ faces }: { faces: WidgetFootprint[] }) {
return (
<div
aria-hidden
className="flex aspect-video items-center justify-center rounded-sm bg-muted p-1"
>
<div className="relative aspect-square h-full rounded-full border-2 border-primary/45">
<div className="absolute inset-x-[15%] bottom-[15%] flex flex-wrap justify-center gap-1">
{faces.map((face, index) => (
<span
key={index}
className={cn("size-2 rounded-full", shade(face.type))}
/>
))}
</div>
</div>
</div>
)
}
/** One dashboard in the mosaic. */
function Tile({
dashboard,