A draggable, resizable dashboard canvas: gravity packing, responsive column squeezing, optional size snapping, and a serializable layout you own.
EditableStaticResponsiveSnapping
A
B
C
import { DashboardGrid, DashboardGridItem } from '@chanrg/4ui/grid';<DashboardGrid bind:layout editable> {#each layout as item (item.id)} <DashboardGridItem id={item.id} x={item.x} y={item.y} w={item.w} h={item.h}> <KpiCard {...cards[item.id]} /> </DashboardGridItem> {/each}</DashboardGrid>Installation
pnpm add @chanrg/4uiImport the stylesheet once, at the root of your app:
import '@chanrg/4ui/styles.css';Usage
import { DashboardGrid } from '@chanrg/4ui';import { DashboardGrid, DashboardGridItem } from '@chanrg/4ui/grid';<DashboardGrid bind:layout editable> {#each layout as item (item.id)} <DashboardGridItem id={item.id} x={item.x} y={item.y} w={item.w} h={item.h}> <KpiCard {...cards[item.id]} /> </DashboardGridItem> {/each}</DashboardGrid>API reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
layout | GridItemLayout[] ($bindable) | [] | Widget positions; updates as the user drags and resizes. |
columns | number | 12 | Grid columns. |
cellHeight | number | 54 | Row height in px, margins added on top. |
editable | boolean | true | When false the board is frozen for viewing. |
breakpoints | GridBreakpoint[] | 1 col < 540px, 2 < 900px | Responsive column squeezing; [] disables it. |
snapPresets | GridPreset[] | — | Snap resizes to the nearest preset from makePresets(). |
onlayoutchange | (layout) => void | — | Fires after a drag/resize settles — persist it where you like. |
CSS variables
| Variable | Description |
|---|---|
--4ui-surface | Primary component surface. |
--4ui-text | Primary foreground color. |
--4ui-border | Default control and panel border. |
Accessibility
- Layout edits are pointer-driven; keep an alternative path (form or API) for keyboard users.
- Squeezed responsive layouts are a projection and never overwrite the saved layout.