Skip to content

Pinning, moving & sizing ​

The demo has pinned columns on both sides. Drag the header border to resize a column (double click to autosize), drag a header to move a column, open the column menu to pin, hide or autosize columns and toggle columns with the checkboxes.

Source
vue
<template>
  <div class="demo">
    <div class="demo-toolbar">
      <label v-for="col in toggleColumns" :key="col.id">
        <input
          type="checkbox"
          :checked="visible[col.id] !== false"
          @change="api?.setColumnVisible(col.id, ($event.target as HTMLInputElement).checked)"
        />
        {{ col.text }}
      </label>
      <button @click="api?.autosizeColumns()">Autosize</button>
      <button @click="api?.resetColumnState()">Reset</button>
    </div>
    <Datagrid
      :columns="columns"
      :rows="rows"
      :options="options"
      @ready="api = $event"
      @state-change="onStateChange"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef } from "vue";
import { Datagrid, type ColumnConfig, type Core, type GridOptions, type GridState } from "@datagrid/vue-ui";
import { createEmployees, type Employee } from "./data";

const rows = createEmployees(200);
const api = shallowRef<Core>();

const columns: Array<ColumnConfig<Employee>> = [
  { field: "id", text: "ID", type: "number", width: 70, pinned: "left" },
  { field: "firstName", text: "First name", pinned: "left" },
  { field: "lastName", text: "Last name" },
  { field: "email", text: "Email", width: 260 },
  { field: "department", text: "Department" },
  { field: "role", text: "Role" },
  { field: "city", text: "City" },
  { field: "country", text: "Country", visible: false },
  { field: "startDate", text: "Start date", type: "date" },
  { field: "salary", text: "Salary", type: "number", pinned: "right" },
];

// columns which can be toggled with the checkboxes
const toggleColumns = [
  { id: "email", text: "Email" },
  { id: "role", text: "Role" },
  { id: "city", text: "City" },
  { id: "country", text: "Country" },
  { id: "startDate", text: "Start date" },
];

const options: GridOptions<Employee> = {
  columnMenu: true,
  defaultColumn: { sortable: true, resizeable: true, moveable: true, width: 130 },
};

// the visibility is read from the column state, so changes in the column menu are reflected too
const visible = ref<Record<string, boolean>>({ country: false });

function onStateChange(state: GridState) {
  visible.value = Object.fromEntries(state.columns.map((col) => [col.id, col.visible]));
}
</script>
tsx
import { useState } from "react";
import { Datagrid, type ColumnConfig, type Core, type GridOptions, type GridState } from "@datagrid/react-ui";
import { createEmployees, type Employee } from "../data";

const rows = createEmployees(200);

const columns: Array<ColumnConfig<Employee>> = [
  { field: "id", text: "ID", type: "number", width: 70, pinned: "left" },
  { field: "firstName", text: "First name", pinned: "left" },
  { field: "lastName", text: "Last name" },
  { field: "email", text: "Email", width: 260 },
  { field: "department", text: "Department" },
  { field: "role", text: "Role" },
  { field: "city", text: "City" },
  { field: "country", text: "Country", visible: false },
  { field: "startDate", text: "Start date", type: "date" },
  { field: "salary", text: "Salary", type: "number", pinned: "right" },
];

// columns which can be toggled with the checkboxes
const toggleColumns = [
  { id: "email", text: "Email" },
  { id: "role", text: "Role" },
  { id: "city", text: "City" },
  { id: "country", text: "Country" },
  { id: "startDate", text: "Start date" },
];

const options: GridOptions<Employee> = {
  columnMenu: true,
  defaultColumn: { sortable: true, resizeable: true, moveable: true, width: 130 },
};

export default function ColumnLayout() {
  const [api, setApi] = useState<Core>();
  // the visibility is read from the column state, so changes in the column menu are reflected too
  const [visible, setVisible] = useState<Record<string, boolean>>({ country: false });

  function onStateChange(state: GridState) {
    setVisible(Object.fromEntries(state.columns.map((col) => [col.id, col.visible])));
  }

  return (
    <div className="demo">
      <div className="demo-toolbar">
        {toggleColumns.map((col) => (
          <label key={col.id}>
            <input
              type="checkbox"
              checked={visible[col.id] !== false}
              onChange={(e) => api?.setColumnVisible(col.id, e.target.checked)}
            />{" "}
            {col.text}
          </label>
        ))}
        <button onClick={() => api?.autosizeColumns()}>Autosize</button>
        <button onClick={() => api?.resetColumnState()}>Reset</button>
      </div>
      <Datagrid columns={columns} rows={rows} options={options} onReady={setApi} onStateChange={onStateChange} />
    </div>
  );
}

Width ​

PropertyDescription
widthWidth in pixels (default 100).
minWidth, maxWidthLimits for resizing and autosizing.
flexThe column takes a share of the free space. Two columns with flex: 1 and flex: 2 get one and two thirds of the remaining width.
autosizeThe width is calculated from the content of the first rows and the header when the grid is initialized.
autosizeAdjust(column, { headerWidth, contentWidth }) => width to adjust the calculated width.

Users can resize columns with resizeable: true by dragging the right border of the header. A double click on the border autosizes the column.

ts
// API
api.setColumnWidth("name", 200);
await api.autosizeColumns(["name", "city"]); // or api.autosizeColumns() for all columns
api.sizeColumnsToFit(); // recalculates the flex columns

Pinning ​

Pinned columns stay visible while scrolling horizontally:

ts
{ field: "id", pinned: "left" },
{ field: "actions", pinned: "right" },

The pinning can be changed with the column menu or the API:

ts
api.setColumnPinned("salary", "left");
api.setColumnPinned("salary", undefined); // unpin

Children of a column group always have the pinning of the group.

Moving ​

Columns with moveable: true can be dragged to another position within their pinned area. Press Escape while dragging to cancel.

ts
api.moveColumn("salary", 0); // index relative to the siblings

Visibility ​

ts
{ field: "internalId", visible: false }

api.hideColumn("city");
api.showColumn("city");
api.setColumnVisible("city", false);
api.toggleColumnVisibility("city");

The column menu contains a column chooser to show and hide columns.

Column menu ​

The column menu is opened with the button in the header. It offers sorting, pinning, autosizing, grouping, hiding and the column chooser. It is enabled for all columns with the grid option columnMenu: true or per column with menu: true.

Saving the column layout ​

The column layout (order, width, visibility, pinning) is part of the grid state:

ts
const columnState = api.getColumnState();
localStorage.setItem("columns", JSON.stringify(columnState));

api.setColumnState(JSON.parse(localStorage.getItem("columns")));
api.resetColumnState(); // back to the column definitions

Released under the ISC License.