GrDashboardItem
Overview
When to take it
- any widget inside
GrDashboard— outside the grid the component makes no sense: moving, stretching and the keyboard go through its context; - the content has a minimum —
minW/minHare declared here rather than in the layout: the layout knows the coordinates, and “a chart below three rows is unreadable” is known by the widget; - a header is not always needed — it appears only if there is a
title, a#headeror#actions; a map and a large figure have no use for a header; - one widget behaves differently —
:draggable="false"or:resizable="false"narrow the rule of the grid for it alone.
When to take something else
| Need | Take |
|---|---|
| A card outside a grid of widgets | GrCard |
| A metric as a number, without the frame of a widget | GrStatistic |
| The grid itself | GrDashboard |
| A catalogue of widgets to add from | GrDashboardPalette |
Examples 3 each with a live demo and its code
Every part of a widget
titlenames the widget in the header, in the handle names and in the grid’s announcements.#actionsare product buttons and are always there;#editActionsandshow-settingsappear only in edit mode, without adding a header;#footerstays at the bottom while the body scrolls;#skeletonreplaces the body while «Refresh» loads.Support todaySwitch to Edit to see the handle, Remove and the gear.Waiting longest- Invoice shows the old VAT numberT-229142 min
- Cannot add a second adminT-22941 h 10 min
- Card declined on renewalT-22972 h 05 min
- Export to DATEV is emptyT-23013 h 40 min
Updated 2 min agoAgents online7 / 9Two on a break until 14:30A chart edge to edge, a list that scrolls, a number without a header
padding="none"withoverflow="hidden"gives the body to the chart edge to edge and removes both the scroll bar and the Tab stop. The default body scrolls, and its scroll is a Tab stop, so a list that does not fit stays reachable. A widget withouttitle,#headeror#actionshas no header at all — a big number needs no frame.Traffic, todayChart data X Requests per second 00:00 180 01:00 180 02:00 180 03:00 180 04:00 180 05:00 180 06:00 180 07:00 207 08:00 234 09:00 258 10:00 279 11:00 296 12:00 309 13:00 317 14:00 320 15:00 317 16:00 309 17:00 296 18:00 279 19:00 258 20:00 234 21:00 207 22:00 180 23:00 180 99.982%Uptime this monthLast errors- 502 on /v1/invoices · 14:02
- Timeout to the bank API · 13:47
- 429 from the mail provider · 13:15
- 502 on /v1/payments · 12:58
- Timeout to the bank API · 12:30
A pinned banner and a timeline that grows
staticis about the layout, so the status banner stays put in edit mode too, and nothing pushes it. «Next event» adds to theauto-heighttimeline: it asks the grid for rows by itself, and the grid reports that asitem-auto-resize— separate fromitem-resize, so data arriving does not look like the user editing the layout.min-wandmax-hare the widget’s own limits.Resized by hand: 0 · by content: 0INC-0912 · Invoices API returning 502 · started 14:02Timeline- 14:02 · Alert: 502 rate above 2 %
- 14:05 · Lena acknowledged
OwnerLena Brandt · on call until 18:00
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
draggable | boolean | undefined | undefined | Whether this widget can be dragged and resized. Unset — the grid rule applies; `static` beats both, because it is about the layout, not about the interface. |
resizable | boolean | undefined | undefined | Whether this widget can be resized by the corner. Not set — the rule of the grid applies. |
size | "lg" | "xs" | "sm" | "md" | undefined | undefined | The size step: the type size of the header and the paddings of the header, the body and the footer. `md` by default; taken from `GrConfigProvider`. |
padding | GrDashboardItemPadding | undefined | undefined | The content padding. Unset — a step derived from `size`; `none` gives the widget to its content entirely, edge to edge. |
overflow | GrDashboardItemOverflow | undefined | undefined | `hidden` removes both the scrollbar and the `Tab` stop on the body. |
autoHeight | boolean | undefined | undefined | The widget height is set by its content, not by the layout record. Rounded up to a whole row: the grid is integral, and a few pixels of emptiness at the bottom beat a cut-off last line. `minH`/`maxH` still apply, and the corner of such a widget changes the width only. |
static | boolean | undefined | undefined | Does not move by itself and is not moved by its neighbours. |
showSettings | boolean | undefined | false | A settings button among the edit mode actions. It does not turn the header on. |
ariaLabel | string | undefined | — | The accessible name of the widget instead of `title`. Without either, the widget stays an ordinary block, with no `group` role. |
itemIdrequired | string | — | The widget identifier. Links the markup to the layout record. |
title | string | undefined | — | The widget name: the heading, the name of the handles, the name in announcements. |
minW | number | undefined | — | Size bounds. The layout may not contain them — the widget knows them. |
minH | number | undefined | — | The minimum height of the widget in grid rows. |
maxW | number | undefined | — | The maximum width of the widget in grid columns. |
maxH | number | undefined | — | The maximum height of the widget in grid rows; content that hits it scrolls. |
Slots
| Slot | Type | Description |
|---|---|---|
default | any | The content of the widget; with `lazy` on the grid it is mounted once it enters the viewport. |
header | any | A custom header instead of the `title` heading; it switches the header on. |
actions | any | Product buttons in the header — always visible, and they switch the header on. |
editActions | any | Edit mode actions: remove the widget, open the settings. |
footer | any | The footer of the widget card. |
skeleton | any | A placeholder in place of the content until a lazy widget enters the viewport. |
Events
| Event | Signature | Description |
|---|---|---|
settings | (event: "settings", id: string): void | — |
Types
Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.
import type { GrDashboardItemConfigurableProps, GrDashboardItemSize, GrDashboardItemPadding } from '@feugene/granularity-dashboard/components/GrDashboardItem'Filedist/types/components/GrDashboardItem/config.d.ts
const grDashboardItemConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDashboardItem">;Filedist/types/components/GrDashboardItem/defaults.d.ts
interface GrDashboardItemConfigurableProps {
size: GrDashboardItemSize;
padding: GrDashboardItemPadding;
overflow: GrDashboardItemOverflow;
autoHeight: boolean;
}Filedist/types/components/GrDashboardItem/grDashboardItemStyles.d.ts
type GrDashboardItemSize = GrComponentSize;type GrDashboardItemPadding = 'none' | GrDashboardItemSize;type GrDashboardItemOverflow = 'auto' | 'hidden';Filedist/types/components/GrDashboardItem/safelist.d.ts
const grDashboardItemSafelist: string[];Filedist/types/components/GrDashboardItem/GrDashboardItem.vue.d.ts
interface GrDashboardItemProps {
itemId: string;
title?: string;
size?: GrDashboardItemSize;
padding?: GrDashboardItemPadding;
overflow?: GrDashboardItemOverflow;
autoHeight?: boolean;
draggable?: boolean;
resizable?: boolean;
static?: boolean;
showSettings?: boolean;
minW?: number;
minH?: number;
maxW?: number;
maxH?: number;
ariaLabel?: string;
}interface GrDashboardItemEmits {
(e: 'settings', id: string): void;
}Usage from the library prose
static and draggable are about different things
static is about the layout: the widget does not move by itself and does not let its neighbours
move it; a collision with it cancels someone else’s move.
draggable and resizable are about the interface, and they are removed separately:
:resizable="false" removes the resize corner while leaving the moving.
The prohibition is checked by the grid itself rather than merely hiding the handle. A protection that rests on “the button was not rendered” would be removed by the very first direct call from the keyboard context.
The handle takes up no room in the flow
In the edit mode the header does not appear for the sake of the handle — the panel lies over the top of the content. Switching the mode therefore shifts nothing: the widget stays exactly in its place rather than jumping by the height of a header.
The hidden panel stays in the DOM and in the tab order: removing it would mean removing the handle from the keyboard walk. Where there is no hovering (a finger), it is visible for the whole of the editing.
Auto height: the content decides how many rows to take
auto-height relieves the application of the task of guessing h on the user’s behalf. The widget
measures its content and asks the grid for as many rows as it needs:
<GrDashboardItem item-id="log" title="Events" auto-height>
<ul><li v-for="e in events" :key="e.id">{{ e.text }}</li></ul>
</GrDashboardItem>What is measured is the wrapper around the content rather than the body of the widget. The height
of the body is set by the cell of the grid, and its scrollHeight equals max(content, container):
the widget would be able to grow to fit its content, but having become taller than it, it would
report its own height — and would never shrink back. The wrapper appears in the markup only with
the prop switched on: widgets without it keep their previous DOM.
Rounding upwards. A row of the grid is a whole one (model.md, invariant 1), and content of 131
pixels with a row of 60 takes three rows rather than two. An emptiness of a few pixels at the bottom
is noticeable and harmless — a truncated last row of a table is not. That is how auto height differs
from stretching by a corner: there the user drags and expects snapping to the nearest cell, so there
the rounding is to the nearest.
The corner of such a widget changes only the width. A height set by hand would be wiped out by the very first measurement, so the vertical is taken away from the gesture and from the arrows at once — rather than promising a movement and cancelling it.
minH and maxH keep applying. If maxH has been reached, the content scrolls by the ordinary
rules of overflow, which is what it exists for. static does not yield to auto height at all: it
is about the widget not moving either by itself or under pressure from its neighbours.
The grid reports that with a separate event. A change of height travels into update:layout like
any other — the layout remains the only truth — but the grid additionally emits itemAutoResize. An
application that judges the layout dirty by the edits would otherwise ask “save the changes?” after
data has loaded into a widget.
On the server there is no measurement: the h from the layout applies, and the refinement arrives
after mounting — as does the breakpoint (see ../ssr.md).
The actions are separated by their lifetime
#actions are product buttons, always visible, and they switch the header on. #editActions are the
actions of the edit mode (delete the widget, open the settings): they appear only in mode="edit"
and travel either into the header or into the panel if there is no header.
The settings in that row are not a slot but a prop: showSettings draws a gear button in the same
place as #editActions and emits settings; the grid forwards that outside as itemSettings. The
place, the icon and the accessible name are the same on every dashboard, and what to show on a press
is the business of the application: there is a ready window,
GrDashboardItemSettings.
The button does not switch the header on, unlike #actions. Otherwise switching the mode would
shift the content of a headless widget by its height — exactly what the drag handle moved into the
panel over the content for.
Props in this example