GrDashboardPalette
Overview
When to take it
- the set of widgets is chosen by the user — the dashboard is assembled from a catalogue rather than arriving ready;
- what is available depends on the role or on the plan — the list arrives as a prop, and what is unavailable simply does not enter it;
- the card of the catalogue is non-standard — the
itemslot draws an icon, a description, a preview; - there is nothing to add yet — the
emptyslot explains why, instead of an empty rectangle.
When to take something else
| Need | Take |
|---|---|
| Choose one option from a list | GrSelect |
| Find a command or an object by searching | GrCommandPalette |
| Show a list without adding | GrList |
| Explain that the dashboard is empty | GrEmptyState |
| The grid itself | GrDashboard |
Despite the name, this is not GrCommandPalette: there a command is searched for across the
whole application by typing, here a widget is chosen from a short known set.
Examples 2 each with a live demo and its code
Building a board from the catalogue
Add a widget with its button, or drag a tile onto the grid:
droppablepreviews where it will land. The palette only says what to add —addfor the button,item-dropon the grid with the cell and the options — and the page places it withaddItem. What is already on the board stays in the list as «Added», andminWtravels with the tile.Widgets- Sales today3×2Orders and revenue so far
- Average basket3×2Compared with last week
- Returns3×2Open and processed
- Low stock6×3Needs at least half the width
My store boardSales today€4,812 · 63 ordersTiles with icons and plans
The
itemslot draws a tile of its own — icon, description, a plan badge — andtransferPropsgoes on its root, without which the tile could not be dragged. The «Add» button stays in the tile: it is the keyboard path, and dragging is only an enhancement. It calls the slot’sadd, so the palette emitsaddand announces the widget, as with its own tile.ghostdecides what follows the pointer.Add widgetsTeam plan - MRR Recurring revenue and its change
- Overdue invoices Who owes what, oldest first
- Cash forecast BusinessNext 90 days from open invoices
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
draggable | boolean | undefined | undefined | The tile can be dragged onto the grid. The "Add" button stays at any value: it is the contract and the only keyboard path. A grid listening to `itemDrop` will place what is dropped — one that does not will show the backdrop and do nothing. |
size | "lg" | "xs" | "sm" | "md" | undefined | undefined | The size step: the type size of the tiles and the add buttons. `md` by default; taken from `GrConfigProvider`. |
disabled | boolean | undefined | false | The catalogue is unavailable: the "Add" buttons are disabled, the tiles cannot be dragged. |
ariaLabel | string | undefined | — | The name of the tile list. Not set — from the locale ("Widget catalog"). |
itemsrequired | GrDashboardPaletteItem[] | — | Catalog of available widgets. |
Slots
| Slot | Type | Description |
|---|---|---|
item |
| A custom tile. `transferProps` is bound to its root (`v-bind`) — without it there is nothing to drag custom markup by, and the slot exists precisely for that markup. |
ghost | { item: GrDashboardPaletteItem; } | What is drawn under the cursor during a drag. |
empty | any | What to show when the catalogue is empty. |
Events
| Event | Signature | Description |
|---|---|---|
add | (event: "add", item: GrDashboardPaletteItem): 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 { GrDashboardPaletteConfigurableProps, GrDashboardPaletteSize, GrDashboardPaletteItem } from '@feugene/granularity-dashboard/components/GrDashboardPalette'Filedist/types/components/GrDashboardPalette/config.d.ts
const grDashboardPaletteConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDashboardPalette">;Filedist/types/components/GrDashboardPalette/defaults.d.ts
interface GrDashboardPaletteConfigurableProps {
size: GrDashboardPaletteSize;
draggable: boolean;
}Filedist/types/components/GrDashboardPalette/grDashboardPaletteStyles.d.ts
type GrDashboardPaletteSize = GrComponentSize;interface GrDashboardPaletteItem {
id: string;
title: string;
description?: string;
defaultSize?: {
w: number;
h: number;
};
minW?: number;
minH?: number;
maxW?: number;
maxH?: number;
disabled?: boolean;
}Filedist/types/components/GrDashboardPalette/safelist.d.ts
const grDashboardPaletteSafelist: string[];Filedist/types/components/GrDashboardPalette/GrDashboardPalette.vue.d.ts
interface GrDashboardPaletteProps {
items: GrDashboardPaletteItem[];
size?: GrDashboardPaletteSize;
draggable?: boolean;
disabled?: boolean;
ariaLabel?: string;
}interface GrDashboardPaletteEmits {
(e: 'add', item: GrDashboardPaletteItem): void;
}Usage from the library prose
Where an added widget goes is decided by the application
The component emits an intent with a description of the widget, and the coordinates are assigned by the consumer: only they know whether to add a new widget at the bottom, in the first free place or at the position the user started from. Putting it “somewhere” would mean spoiling the layout the person has just assembled.
Dragging is a reinforcement rather than a contract
A tile can be dragged onto the grid (draggable, on by default), but the “Add” button stays and
remains the only keyboard path. Make dragging the contract and the catalogue cannot be used from the
keyboard, and “accessibility later” never comes here.
Where it was dropped is reported by the grid with an itemDrop emit, and the putting is still done
by the application — exactly as with the button. A grid that does not listen to itemDrop will
show the underlay and will do nothing on release: either handle the event or switch draggable
off.
A press on the button itself does not become a drag: otherwise a hand that wavered by five pixels would give a drag instead of an addition.
A tile of your own — the #item slot
The slot receives item, dragging, transferProps and add. transferProps is bound to the root
of the tile — without it your own markup cannot be dragged. add is the same path as the built-in
button’s: an add emit and an “added” announcement to the screen reader, and nothing at all for a
disabled tile. Your own “Add” button is obliged to call exactly it: it remains the only keyboard
path.
<template>
<GrDashboardPalette :items="catalog" @add="place">
<template #item="{ item, transferProps, add }">
<div v-bind="transferProps" class="flex items-center justify-between gap-2">
<span>{{ item.title }}</span>
<GrButton size="sm" :disabled="item.disabled" @click="add">
Add
</GrButton>
</div>
</template>
</GrDashboardPalette>
</template>Limits
A tile is not dragged with a finger. Moving by touch requires a touch-action: none on the tile,
and the catalogue is a vertical list, which on a phone is obliged to scroll. Taking the vertical axis
away from a scroller for the sake of a gesture that already has a working equivalent is a bad trade;
on touch input the button remains.
Props in this example