Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ auto-generated per-PR notes; this file is the curated, human-readable history.
reaches incompatible dependencies (#383).

### Changed
- KPI queries now have one frameless Dashboard presentation in Grid Tiles,
Full view, Report, 2-column, and 3-column styles. Flow KPI-band members and
transparent grid wrappers move with Command/Ctrl-drag while plain dragging
continues to select text; movement reuses the shared floating preview,
destination resolution, auto-scroll, cancellation, and exact-once commit
path. Grid KPI title/delete/resize chrome appears transiently on hover or
keyboard focus without shifting content, and resize is keyboard-operable
with arrow keys (#340).
- Consolidated the non-Iceberg examples into three responsive flagship
dashboards: On-time flights, Shop analytics, and ClickHouse Operations.
Each uses an authored `grafana-grid@1` layout with a complete `flow@1`
Expand Down
70 changes: 57 additions & 13 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2720,6 +2720,15 @@ table.res-table tbody tr:hover td.idx { background: var(--bg-hover); }
/* A member's stable host contributes its children (cards, or a state card)
directly to the stream's flex-wrap — it is never itself a visible box. */
.dash-kpi-member { display: contents; }
.dash-grid.modkey .dash-kpi-member:hover > * { cursor: grab; }
.dash-kpi-member.dash-drop-target > * {
outline: 2px dashed var(--accent); outline-offset: -2px;
}
.dash-kpi-member.dash-floating {
display: flex; flex-wrap: wrap; align-items: stretch; gap: 10px;
box-sizing: border-box; background: var(--bg-side); border-radius: 10px;
outline: 2px solid var(--accent); outline-offset: -2px;
}
.dash-kpi-state-card {
--kpi-accent: var(--accent);
min-width: 160px; max-width: 320px; padding: 14px 16px;
Expand Down Expand Up @@ -2829,7 +2838,8 @@ table.res-table tbody tr:hover td.idx { background: var(--bg-hover); }
.dash-gg-del, .dash-gg-resize { user-select: none; } /* #332: edit chrome stays non-selectable */
.dash-gg-resize { display: none; }
.dash-gg-grid .dash-gg-resize {
display: block; position: absolute; right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize;
display: block; position: absolute; right: 0; bottom: 0; width: 14px; height: 14px; padding: 0;
border: 0; background: transparent; cursor: nwse-resize;
}
.dash-gg-resize::after {
content: ''; position: absolute; right: 3px; bottom: 3px; width: 6px; height: 6px;
Expand Down Expand Up @@ -2890,13 +2900,8 @@ table.res-table tbody tr:hover td.idx { background: var(--bg-hover); }
host was forced to `1fr` still tries to occupy N implicit tracks, rendering
side-by-side/overflowing instead of the model's actual N-column layout. */

/* ── #316: view-mode KPI tiles are frameless ───────────────────────────────
`.is-view` is a static per-load class (ui/dashboard.ts sets it once per
tile card, from the same `readOnly` that already governs drag/remove/
resize — it never toggles mid-session; `.is-kpi` is likewise fixed per
session today but is still re-toggled defensively on every reconcile,
ui/dashboard.ts `reconcileGridTile`). Scoped to
`.dash-gg-grid .dash-gg-tile.is-kpi` so:
/* ── #340: KPI tiles are frameless in edit and view modes ──────────────────
Scoped to `.dash-gg-grid .dash-gg-tile.is-kpi` so:
- a non-KPI grid tile (`.is-view` with no `.is-kpi`) keeps its full frame
— only KPI content gets this treatment;
- a flow-rendered card is never matched — flow never applies
Expand All @@ -2907,10 +2912,49 @@ table.res-table tbody tr:hover td.idx { background: var(--bg-hover); }
"invisible structural wrapper" the issue asks for, not a new DOM node, and
never `display: contents` (that would drop it out of grid placement). The
accessible name comes from `role="group"`/`aria-label` set directly on this
same element (ui/dashboard.ts) — hiding the header visually here does not
remove it from the accessibility tree. */
.dash-gg-grid .dash-gg-tile.is-kpi.is-view {
same element. Edit controls are overlay chrome: revealing them never changes
the KPI body's size or the tile's authored height. */
.dash-gg-grid .dash-gg-tile.is-kpi {
background: transparent; border-color: transparent; border-radius: 0; box-shadow: none;
}
.dash-gg-grid .dash-gg-tile.is-kpi.is-view > .dash-tile-head { display: none; }
.dash-gg-grid .dash-gg-tile.is-kpi.is-view > .dash-tile-body { padding: 0; }
.dash-gg-grid .dash-gg-tile.is-kpi > .dash-tile-head {
position: absolute; z-index: 2; inset: 0 0 auto 0; min-height: 0; padding: 4px 5px;
border: 0; opacity: 0; pointer-events: none;
}
.dash-gg-grid .dash-gg-tile.is-kpi > .dash-tile-head > .dash-tile-name {
width: fit-content; max-width: calc(100% - 30px); padding: 2px 6px;
border-radius: 4px; background: color-mix(in srgb, var(--bg-modal) 92%, transparent);
box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.dash-gg-grid .dash-gg-tile.is-kpi > .dash-tile-head > .dash-gg-del { pointer-events: auto; }
.dash-gg-grid .dash-gg-tile.is-kpi > .dash-tile-body { padding: 0; }
.dash-gg-grid .dash-gg-tile.is-kpi .dash-gg-grip { display: none; }
.dash-gg-grid .dash-gg-tile.is-kpi::before {
content: ''; position: absolute; z-index: 1; inset: 0; border-radius: 8px;
outline: 1px solid transparent; outline-offset: -1px; pointer-events: none;
}
.dash-gg-grid .dash-gg-tile.is-kpi:not(.is-view):hover::before,
.dash-gg-grid .dash-gg-tile.is-kpi:not(.is-view):focus-within::before,
.dash-gg-grid.modkey .dash-gg-tile.is-kpi:not(.is-view):hover::before {
outline-color: var(--border);
}
.dash-gg-grid .dash-gg-tile.is-kpi:not(.is-view):hover > .dash-tile-head,
.dash-gg-grid .dash-gg-tile.is-kpi:not(.is-view):focus-within > .dash-tile-head,
.dash-gg-grid.modkey .dash-gg-tile.is-kpi:not(.is-view):hover > .dash-tile-head {
opacity: 1;
}
.dash-gg-grid .dash-gg-tile.is-kpi:hover .dash-gg-resize::after,
.dash-gg-grid .dash-gg-tile.is-kpi:focus-within .dash-gg-resize::after,
.dash-gg-grid.modkey .dash-gg-tile.is-kpi:hover .dash-gg-resize::after,
.dash-gg-grid .dash-gg-tile.is-kpi .dash-gg-resize:focus-visible::after { opacity: 1; }
.dash-gg-grid .dash-gg-tile.is-kpi:focus-within .dash-gg-del { opacity: 1; }
.dash-gg-grid .dash-gg-tile.is-kpi.dash-floating,
.dash-gg-grid .dash-gg-tile.is-kpi.dash-gg-resizing {
background: var(--bg-side); border-color: transparent; border-radius: 8px;
}
.dash-gg-grid .dash-gg-tile.is-kpi.dash-floating::before,
.dash-gg-grid .dash-gg-tile.is-kpi.dash-gg-resizing::before {
outline: 2px solid var(--accent); outline-offset: -2px;
}
.dash-gg-grid .dash-gg-tile.is-kpi.dash-floating > .dash-tile-head,
.dash-gg-grid .dash-gg-tile.is-kpi.dash-gg-resizing > .dash-tile-head { opacity: 1; }
Loading
Loading