Appearance
Headless usage
The core of the grid does not depend on a UI framework or on the DOM. It can be used to process data (sorting, filtering, grouping, pagination, selection) for your own user interface.
useDatagrid
useDatagrid creates a grid instance without user interface and provides its state reactively:
vue
<template>
<div class="demo">
<div class="demo-toolbar">
<input v-model="search" type="search" placeholder="Search…" />
<button @click="api.sortColumn('salary', 'desc')">Highest salary</button>
<button @click="api.sortColumn('lastName', 'asc')">By name</button>
<button :disabled="!state.pagination?.hasPreviousPage" @click="api.prevPage()">‹</button>
<span>page {{ (state.pagination?.pageIndex ?? 0) + 1 }} / {{ state.pagination?.pageCount }}</span>
<button :disabled="!state.pagination?.hasNextPage" @click="api.nextPage()">›</button>
</div>
<div class="cards">
<div v-for="employee in employees" :key="employee.id" class="card">
<strong>{{ employee.firstName }} {{ employee.lastName }}</strong>
<span>{{ employee.department }}</span>
<span>{{ employee.salary.toLocaleString() }} €</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { useDatagrid, type ColumnConfig, type Row } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";
const columns: Array<ColumnConfig<Employee>> = [
{ field: "firstName" },
{ field: "lastName", sortable: true },
{ field: "department" },
{ field: "salary", type: "number", sortable: true },
];
// sorting, filtering and pagination without the grid ui
const { state, api } = useDatagrid(columns, createEmployees(200), {
pagination: { enabled: true, pageSize: 12 },
});
// state.items contains the rows of the current page (sorted and filtered)
const employees = computed(() =>
state.value.items.filter((item) => item.isRow).map((item) => (item as Row<Employee>).data)
);
const search = ref("");
watch(search, (value) => api.setGlobalFilter(value));
</script>
<style>
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 8px;
overflow: auto;
}
.card {
display: flex;
flex-direction: column;
gap: 2px;
padding: 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
font-size: 14px;
}
</style>tsx
import { useDatagrid, type ColumnConfig, type GridOptions, type Row } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";
const rows = createEmployees(200);
const columns: Array<ColumnConfig<Employee>> = [
{ field: "firstName" },
{ field: "lastName", sortable: true },
{ field: "department" },
{ field: "salary", type: "number", sortable: true },
];
const options: GridOptions<Employee> = {
pagination: { enabled: true, pageSize: 12 },
};
export default function Headless() {
// sorting, filtering and pagination without the grid ui
const { state, api } = useDatagrid(columns, rows, options);
const { items, pagination } = state;
const employees = items.filter((item) => item.isRow).map((item) => (item as Row<Employee>).data);
return (
<div className="demo">
<div className="demo-toolbar">
<input onChange={(e) => api?.setGlobalFilter(e.target.value)} type="search" placeholder="Search…" />
<button onClick={() => api?.sortColumn("salary", "desc")}>Highest salary</button>
<button onClick={() => api?.sortColumn("lastName", "asc")}>By name</button>
<button disabled={!pagination?.hasPreviousPage} onClick={() => api?.prevPage()}>
‹
</button>
<span>
page {(pagination?.pageIndex ?? 0) + 1} / {pagination?.pageCount}
</span>
<button disabled={!pagination?.hasNextPage} onClick={() => api?.nextPage()}>
›
</button>
</div>
<div className="cards">
{employees.map((employee) => (
<div key={employee.id} className="card">
<strong>
{employee.firstName} {employee.lastName}
</strong>
<span>{employee.department}</span>
<span>{employee.salary.toLocaleString()} €</span>
</div>
))}
</div>
<style>{`
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 8px;
overflow: auto;
}
.card {
display: flex;
flex-direction: column;
gap: 2px;
padding: 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
font-size: 14px;
}
`}</style>
</div>
);
}ts
import { useDatagrid } from "@datagrid/vue-ui";
const { state, api } = useDatagrid(columns, rows, options);columns,rowsandoptionscan be refs, changes are applied automatically.state.valuecontainsitems(rows and group rows of the current view),columns,rowCount,selectedKeys,expandedKeys,paginationand the complete gridstate.apiis the complete grid API.
ts
import { useDatagrid } from "@datagrid/react-ui";
const { state, api } = useDatagrid(columns, rows, options);- New values of
columns,rowsandoptionsare applied when their reference changes (keep them stable withuseMemoor module constants). statecontainsitems(rows and group rows of the current view),columns,rowCount,selectedKeys,expandedKeys,paginationand the complete gridstate. The component re-renders when it changes.apiis the complete grid API. It isundefinedduring the first render.
Core without a framework
ts
import { Core } from "@datagrid/core";
const core = new Core(undefined, { pagination: { enabled: true, pageSize: 20 } }, {
onRowsChanged: () => render(core.getAllVisibleItems()),
});
core.initialize(undefined, columns, rows);
core.sortColumn("name", "asc");
core.setColumnFilter("age", ">30");The first parameter is an optional function to measure column widths for autosizing. Passing a DOM element to initialize (with .ut-header and .ut-body children) enables the virtualization and the mouse and keyboard handling, which is what the Vue and React components do. They are a good reference for building adapters for other frameworks.