Skip to content

Latest commit

 

History

History
351 lines (285 loc) · 7.96 KB

File metadata and controls

351 lines (285 loc) · 7.96 KB
title Plugin Grid

import { InteractiveDemo } from '@/app/components/InteractiveDemo'; import { PluginLoader } from '@/app/components/PluginLoader';

Advanced data grid with sorting, filtering, pagination, and row selection capabilities.

Installation

npm install @object-ui/plugin-grid

<PluginLoader plugins={['grid']}>

Interactive Examples

Features

  • Sorting - Multi-column sorting support
  • Filtering - Column-level filtering
  • Pagination - Built-in pagination controls
  • Row Selection - Single and multi-row selection
  • Custom Cells - Custom cell renderers
  • Responsive - Mobile-friendly layouts

Schema API

Grid

{
  type: 'grid',
  columns: GridColumn[],
  data?: any[],
  sortable?: boolean,
  filterable?: boolean,
  pagination?: boolean | PaginationConfig,
  selectable?: boolean,
  onRowClick?: (row) => void,
  className?: string
}

Column Definition

interface GridColumn {
  header: string;
  accessorKey: string;
  sortable?: boolean;
  filterable?: boolean;
  width?: number | string;
  align?: 'left' | 'center' | 'right';
  cell?: (value, row) => ReactNode;
}

Column Summaries

A column can declare a footer aggregation with summary, either as a shorthand string or as an object that aggregates a different field than the one displayed:

{
  "columns": [
    { "field": "name", "summary": "count_filled" },
    { "field": "amount", "type": "currency", "summary": "sum" },
    { "field": "owner", "summary": { "type": "count_unique", "field": "owner_id" } }
  ]
}

The accepted values are ColumnSummarySchema from @objectstack/spec:

summary Footer shows Reads
none nothing — the column opts out
count number of rows every row
count_filled rows whose cell is non-empty raw values
count_empty rows whose cell is empty raw values
count_unique distinct non-empty values raw values
percent_filled share of rows that are non-empty raw values
percent_empty share of rows that are empty raw values
sum total numeric values
avg mean numeric values
min smallest numeric values
max largest numeric values

A cell counts as empty when it is null, undefined, "" or an empty array, so an unset multi-select or lookup reads as empty rather than as a filled [].

The count and percent families read raw cell values, so they work on text, select and lookup columns. sum/avg/min/max need numeric values (numeric strings are parsed) and render nothing when the column has none.

A currency or percent column formats its sum/avg/min/max in that unit. Counts stay plain cardinalities and percentages carry their own %, so count_unique on a currency column reads Unique: 3, not $3.00.

The footer row renders only when at least one column resolves to a summary — a view whose columns are all none (or carry no summary) has no footer.

Usage

Auto-registration (Side-effect Import)

import '@object-ui/plugin-grid';

Manual Registration

import { gridComponents } from '@object-ui/plugin-grid';
import { ComponentRegistry } from '@object-ui/core';

Object.entries(gridComponents).forEach(([type, component]) => {
  ComponentRegistry.register(type, component);
});

Examples

Grid with Custom Cells

{
  "type": "grid",
  "columns": [
    { "header": "Name", "accessorKey": "name" },
    { 
      "header": "Status", 
      "accessorKey": "status",
      "cell": {
        "type": "badge",
        "label": "${value}",
        "variant": "${value === 'Active' ? 'success' : 'default'}"
      }
    },
    {
      "header": "Actions",
      "accessorKey": "id",
      "cell": {
        "type": "button-group",
        "buttons": [
          { "label": "Edit" },
          { "label": "Delete", "variant": "destructive" }
        ]
      }
    }
  ]
}

Selectable Grid

{
  "type": "grid",
  "columns": [...],
  "data": [...],
  "selectable": true,
  "onSelectionChange": "handleSelection"
}

Grid with Pagination

{
  "type": "grid",
  "columns": [...],
  "data": [...],
  "pagination": {
    "pageSize": 10,
    "showSizeChanger": true,
    "pageSizeOptions": [10, 20, 50, 100]
  }
}

ObjectQL Integration

{
  "type": "object-grid",
  "object": "users",
  "columns": [
    { "header": "Name", "accessorKey": "name" },
    { "header": "Email", "accessorKey": "email" },
    { "header": "Created", "accessorKey": "created_at" }
  ],
  "pagination": true,
  "sortable": true,
  "filterable": true
}

Features

Sorting

Enable sorting on all or specific columns:

{
  "type": "grid",
  "sortable": true,
  "columns": [
    { "header": "Name", "accessorKey": "name", "sortable": true },
    { "header": "Email", "accessorKey": "email", "sortable": false }
  ]
}

Filtering

Enable column-level filtering:

{
  "type": "grid",
  "filterable": true,
  "columns": [
    { "header": "Status", "accessorKey": "status", "filterable": true }
  ]
}

Row Actions

Add action buttons to each row:

{
  "type": "grid",
  "rowActions": [
    { "label": "View", "onClick": "viewRow" },
    { "label": "Edit", "onClick": "editRow" },
    { "label": "Delete", "onClick": "deleteRow" }
  ]
}

Inline Editing

Enable inline cell editing for quick data updates:

{
  "type": "object-grid",
  "objectName": "users",
  "columns": [
    { "header": "ID", "accessorKey": "id", "editable": false },
    { "header": "Name", "accessorKey": "name" },
    { "header": "Email", "accessorKey": "email" },
    { "header": "Status", "accessorKey": "status" }
  ],
  "editable": true,
  "onCellChange": "handleCellChange"
}

Features:

  • Double-click or press Enter on a cell to start editing
  • Press Enter to save changes
  • Press Escape to cancel editing
  • Column-level control: Set editable: false on specific columns to prevent editing
  • Callback support: Use onCellChange to handle cell value updates

Example Handler:

function handleCellChange(rowIndex, columnKey, newValue, row) {
  console.log(`Cell at row ${rowIndex}, column ${columnKey} changed to:`, newValue);
  console.log('Full row data:', row);
  // Update your data source here
}

Batch Editing & Multi-Row Save

Edit multiple cells across multiple rows and save them individually or all at once:

{
  "type": "object-grid",
  "objectName": "products",
  "columns": [
    { "header": "SKU", "accessorKey": "sku", "editable": false },
    { "header": "Name", "accessorKey": "name" },
    { "header": "Price", "accessorKey": "price" }
  ],
  "editable": true,
  "rowActions": true,
  "onRowSave": "handleRowSave",
  "onBatchSave": "handleBatchSave"
}

Features:

  • Pending changes tracking: Edit multiple cells across rows before saving
  • Visual indicators: Modified rows highlighted in amber, modified cells in bold
  • Row-level save/cancel: Individual row save and cancel buttons
  • Batch operations: Save All and Cancel All buttons for bulk actions
  • Flexible callbacks: onRowSave for single row, onBatchSave for multiple rows

Example Handlers:

function handleRowSave(rowIndex, changes, row) {
  console.log('Saving row:', rowIndex, changes);
  // Save single row to backend
  await dataSource.update(row.id, changes);
}

function handleBatchSave(allChanges) {
  console.log('Batch saving:', allChanges.length, 'rows');
  // Save all changes at once
  await Promise.all(
    allChanges.map(({ row, changes }) => 
      dataSource.update(row.id, changes)
    )
  );
}

TypeScript Support

import type { GridSchema, GridColumn } from '@object-ui/plugin-grid';

const grid: GridSchema = {
  type: 'grid',
  columns: [...],
  data: [],
  sortable: true,
  pagination: true
};

License

MIT