<DfGrid> Component
The main grid component. Handles column layout, virtual scrolling, sorting, and filtering.
<df-grid
:columns="columns"
:records="records"
key-field="id"
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
columns | ResponsiveColumnDefinitions | — | Column layout definitions. Can be a flat list or a responsive array. See Column Definitions. |
records | RowValue[] | — | Array of row data objects. Each item should contain at least the keyField property. |
keyField | string | — | Name of the property used as a unique row identifier (like a primary key). |
activeColumns | string | — | Name of the currently active responsive layout (matches cssClass of a ResponsiveColumnDefinition). Supports v-model. |
sortState | SortState | — | External sort state. Use with v-model:sortState for controlled sorting. When omitted the grid sorts internally. |
filterState | FilterState | — | External filter state. Use with v-model:filterState for controlled filtering. When omitted the grid filters internally. |
showFilterRow | boolean | false | Show the filter input row below the column headers. |
showStatusBar | boolean | false | Show the status bar below the filter row (displays active filter count). |
showSummaryBar | boolean | false | Show the summary bar below the data rows. The bar also appears automatically when loading is true or when records is empty. |
loading | boolean | false | Indicates that data is being fetched. When true the default summary bar shows a loading spinner; the no-data indicator is suppressed even when records is empty. |
mainShadowCount | number | 500 | Number of rows rendered in the main shadow grid used for column width measurement. Rarely needs changing. |
secondaryShadowCount | number | 30 | Number of rows rendered in secondary shadow grids (one per responsive layout). Rarely needs changing. |
rowClass | (item: RowValue, index: number) => string | string[] | Record<string, boolean> | zebra striping ('even'/'odd') | Returns CSS classes applied to each data row card. Receives the row data object and its 0-based index. Return type matches Vue's :class binding — a string, an array, or an object. Overriding this prop replaces the default even/odd zebra striping entirely; include the logic yourself if you still want it. |
selectionMode | SelectionMode | null | Active selection mode (null, 'selection', or 'exclusion'). Use with v-model:selectionMode for controlled selection. See Selection. |
selectionKeys | Set<any> | — | Set of selected (or excluded) row keys. Use with v-model:selectionKeys. See Selection. |
excessiveScrollThreshold | number | — | Percentage (0–100) of the maximum overscroll displacement (60 px) at which the excessive-scroll event fires. When omitted the event is never emitted. See Pull to refresh. |
Emits
| Event | Payload | Description |
|---|---|---|
click | GridClickEvent | Fired on single click on a data row when selection mode is inactive. Not fired while selection is active (clicks toggle selection instead). Header clicks always fire. |
dblclick | GridClickEvent | Fired on double click. |
sort | GridSortEvent | Fired when the user interacts with a sortable column header. |
filter | GridFilterEvent | Fired when any filter value changes. |
load | 'vertical' | 'horizontal' | Fired when the user scrolls within loadDistance px of the end of the list and the grid is not in loading state. Use this to fetch and append the next page. Set :loading="true" while fetching to suppress duplicate events. Proxied from the underlying virtual-scroll load event. |
excessive-scroll | number | Fired when the user overscrolls past the excessiveScrollThreshold. Payload is the signed displacement in pixels: positive = past the bottom, negative = past the top. Re-fires only after 1 second has elapsed and the overscroll has fallen back below the threshold. Requires excessiveScrollThreshold to be set. |
update:activeColumns | string | Fired when the grid's ResizeObserver selects a different responsive layout. |
update:sortState | SortState | Fired together with sort when sort state changes internally. |
update:filterState | FilterState | Fired together with filter when filter state changes internally. |
update:selectionMode | SelectionMode | Fired when selection mode changes. Use with v-model:selectionMode. |
update:selectionKeys | Set<any>, SelectionAction, key? | Fired when the selected key set changes. action is 'add', 'remove', or 'clear'. |
GridClickEvent
interface GridClickEvent {
rowId: number | 'header'; // row index (0-based) or 'header'
key: any; // value of keyField for this row, or 'header'
rowData: RowValue | undefined;
columnClasses: string[]; // CSS classes of the clicked cell (fieldName + custom cssClass, grid internals stripped)
columnName?: string; // fieldName of the clicked column
event: MouseEvent | TouchEvent;
}GridSortEvent
GridFilterEvent
See Filtering → GridFilterEvent.
Slots
| Slot | Scope | Description |
|---|---|---|
toolbar-start | — | Rendered at the left side of the toolbar bar above the header. The toolbar wrapper (div.df-grid-toolbar) is only rendered when at least one toolbar slot is provided. |
toolbar-end | — | Rendered at the right side of the toolbar. |
header | { column, sortState, filterState, ... } | Overrides the default header cell rendering. Passed through to <DfGridHeader>. |
statusBar | { filterState } | Overrides the default status bar content (filter count). Only visible when showStatusBar is true and selection mode is inactive. |
groupActions | — | Rendered on the right side of the selection status bar. Only visible while selection mode is active. Use it for batch actions (delete, export, …). |
item | { item, index, active } | Overrides the default <GridCard> row rendering. Receives the raw row object, its index, and the virtual scroller's active flag. |
summary-bar | — | Replaces the entire summary bar content. The summary bar is visible when showSummaryBar is true, loading is true, or records is empty. |
loading | — | Replaces the default loading indicator (spinning icon + "Loading…" text) inside the summary bar. Only rendered when loading is true. |
no-data | — | Replaces the default no-data indicator (database-off icon + "No data" text) inside the summary bar. Only rendered when loading is false and records is empty. |
footer-start | — | Rendered at the left side of the footer bar below the scroller. The footer wrapper (div.df-grid-footer) is only rendered when at least one footer slot is provided. |
footer-end | — | Rendered at the right side of the footer. |
Grid structure
The grid renders the following layers from top to bottom. Each section carries a data-section attribute so you can target it from CSS or event handlers without relying on internal class names.
┌─────────────────────────────────────┐ data-section="toolbar"
│ toolbar-start │ toolbar-end │ div.df-grid-toolbar (only when a slot has content)
├─────────────────────────────────────┤ data-section="header"
│ column headers │ df-grid-header component root
├─────────────────────────────────────┤ data-section="filter" (inside header, when showFilterRow)
│ filter row │
├─────────────────────────────────────┤ data-section="status-bar" (inside header, when visible)
│ status bar │
├─────────────────────────────────────┤ data-section="body"
│ data rows │ virtual-scroll container
├─────────────────────────────────────┤ data-section="summary-bar" (when visible)
│ summary bar │ div.df-summary-bar
├─────────────────────────────────────┤ data-section="footer"
│ footer-start │ footer-end │ div.df-grid-footer (only when a slot has content)
└─────────────────────────────────────┘data-section values:
| Value | Element | Notes |
|---|---|---|
toolbar | div.df-grid-toolbar | Present only when toolbar-start or toolbar-end slot has content |
header | df-grid-header root | Always present |
filter | Filter row div inside header | Present only when showFilterRow is true |
status-bar | Status bar div inside header | Present when showStatusBar is true or selection mode is active |
body | Virtual scroll container | Always present |
summary-bar | div.df-summary-bar | Present when showSummaryBar is true, loading is true, or records is empty |
footer | div.df-grid-footer | Present only when footer-start or footer-end slot has content |
The grid's own mouse event handler (processMouse) reads data-section first and immediately ignores clicks that originate in toolbar, filter, status-bar, and footer — those sections never trigger row interactions or selection.
Both div.df-grid-toolbar and div.df-grid-footer use display: flex; justify-content: space-between and are only mounted when at least one of their two slots has content.
Row CSS classes
The rowClass prop lets you attach per-row CSS classes based on row data or position. It receives the raw row object and its 0-based index, and its return value is passed directly to Vue's :class binding.
<!-- Highlight negative amounts in red, keep zebra striping for the rest -->
<df-grid
:columns="columns"
:records="records"
key-field="id"
:row-class="(item, index) => item.amount < 0 ? 'negative' : (index % 2 === 0 ? 'even' : 'odd')"
/><!-- Data-driven classes stored in the record itself -->
<df-grid
:columns="columns"
:records="records"
key-field="id"
:row-class="(item) => item.cssClass"
/>When rowClass is omitted the default is (item, index) => index % 2 === 0 ? 'even' : 'odd', which produces the standard zebra striping. Providing your own function replaces this default entirely.
Example
<df-grid :columns="columns" :records="records" key-field="id">
<template #toolbar-start>
<h2>My Grid</h2>
</template>
<template #toolbar-end>
<button @click="addRow">Add row</button>
</template>
<template #footer-start>
<span>{{ records.length }} records</span>
</template>
<template #footer-end>
<span>Page 1 of 10</span>
</template>
</df-grid>Pull to refresh
The grid detects overscroll gestures (scrolling past the top or bottom edge) and exposes them through the excessive-scroll event. The consumer is responsible for acting on the event — the grid only detects the gesture and emits the notification.
Set excessiveScrollThreshold to the percentage of the maximum overscroll displacement (60 px) that should trigger the event. A value of 80 fires when the user has scrolled 48 px past the edge. The visual overscroll indicator (a blue gradient) is always rendered regardless of whether the event is listened to.
Firing conditions
The event fires immediately when the threshold is first crossed. After firing it is silenced until both of the following are true:
- At least 1 second has elapsed since the last firing.
- The overscroll displacement has fallen back below the threshold (the user released the gesture or stopped scrolling).
This prevents repeated rapid firings while the user holds the scroll position above the threshold.
Payload
| Value | Meaning |
|---|---|
positive (amount > 0) | Overscroll past the bottom of the list (pull-to-load-more) |
negative (amount < 0) | Overscroll past the top of the list (pull-to-refresh) |
Example — pull-to-refresh at the top
<template>
<df-grid
:columns="columns"
:records="records"
key-field="id"
:loading="loading"
:excessive-scroll-threshold="80"
@excessive-scroll="onExcessiveScroll"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const loading = ref(false);
const records = ref([]);
async function onExcessiveScroll(amount: number) {
if (amount < 0 && !loading.value) {
// Negative amount = user pulled past the top → refresh
loading.value = true;
records.value = await fetchRecords();
loading.value = false;
}
}
</script>